summaryrefslogtreecommitdiff
path: root/libs/type_index/examples/constexpr14_namespace_check.cpp
blob: 98a5e40fff9e6ba9e41b11cdaed057657286b0f4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
// Copyright 2013-2017 Antony Polukhin

// Distributed under the Boost Software License, Version 1.0.
// (See the accompanying file LICENSE_1_0.txt
// or a copy at <http://www.boost.org/LICENSE_1_0.txt>.)

#include <boost/config.hpp>

template <class T>
void do_something(const T&) {}


#if !defined(BOOST_NO_CXX14_CONSTEXPR) && !defined(BOOST_NO_CXX11_CONSTEXPR)
// Implementation of this function is not essential for the example
template <std::size_t N>
constexpr bool starts_with(const char* name, const char (&ns)[N]) noexcept {
    for (std::size_t i = 0; i < N - 1; ++i)
        if (name[i] != ns[i])
            return false;
            
    return true;
}

//[type_index_constexpr14_namespace_example
/*`
    The following example shows that `boost::typeindex::ctti_type_index` is usable at compile time on
    a C++14 compatible compilers.

    In this example we'll create and use a constexpr function that checks namespace of the provided type.
*/

#include <boost/type_index/ctti_type_index.hpp>

// Helper function that returns true if `name` starts with `substr`
template <std::size_t N>
constexpr bool starts_with(const char* name, const char (&substr)[N]) noexcept;


// Function that returns true if `T` declared in namespace `ns`
template <class T, std::size_t N>
constexpr bool in_namespace(const char (&ns)[N]) noexcept {
    const char* name = boost::typeindex::ctti_type_index::type_id<T>().raw_name();

    // Some compilers add `class ` or `struct ` before the namespace, so we need to skip those words first
    if (starts_with(name, "class ")) {
        name += sizeof("class ") - 1;
    } else if (starts_with(name, "struct ")) {
        name += sizeof("struct ") - 1;
    }

    return starts_with(name, ns) && starts_with(name + N - 1, "::");
}

/*`
    Now when we have that wonderfull function, we can do static assertions and other compile-time validations:
*/

namespace my_project {
    struct serializer {
        template <class T>
        void serialize(const T& value) {
            static_assert(
                in_namespace<T>("my_project::types") || in_namespace<T>("my_project::types_ext"),
                "Only types from namespaces `my_project::types` and `my_project::types_ext` are allowed to be serialized using `my_project::serializer`"
            );

            // Actual implementation of the serialization goes below
            // ...
            do_something(value);
        }
    };

    namespace types {
        struct foo{};
        struct bar{};
    }
} // namespace my_project

int main() {
    my_project::serializer s;
    my_project::types::foo f;
    my_project::types::bar b;

    s.serialize(f);
    s.serialize(b);

    // short sh = 0;
    // s.serialize(sh); // Fails the static_assert!
}
//] [/type_index_constexpr14_namespace_example]

#else // #if !defined(BOOST_NO_CXX14_CONSTEXPR) && !defined(BOOST_NO_CXX11_CONSTEXPR)

int main() {}

#endif