Abstract
Reflecto is a constexpr library for C++17. Features:
-
Type / enumerator / scope name reflection.
-
Template specialization based on scope (e.g. for any type within a given namespace).
-
Traits for inspecting scope relationships between types.
-
Header-only library, no dependencies.
Support
-
Report issues on GitHub
Distribution
Reflecto is distributed under the Boost Software License, Version 1.0.
The source code is hosted on GitHub.
| Reflecto is not a Boost library. |
Canonical name format
Reflecto uses a canonical format for name objects which is stable across all supported compilers and platforms. This makes Reflecto names persistent, portable identifiers suitable for logging, serialization and other use cases where a type needs to be recognized across an ABI or storage boundary.
Tutorial
Names
All reflected strings are returned by reference as name objects. The name class derives from std::string_view and adds:
-
a kind() accessor describing what kind of name it is (type, scope, enum value, etc.), and
-
a precomputed FNV-1a
hash()used for equality comparisons.
All name operations are C++17 constexpr.
Type names
returns a reference to a type_name<T>()constexpr name with the fully qualified name of T:
#include <boost/reflecto/type_name.hpp>
#include <map>
#include <vector>
using namespace boost::reflecto;
namespace ns
{
struct point
{
int x, y;
};
}
static_assert(type_name<int>() == "int");
static_assert(type_name<ns::point>() == "ns::point");
static_assert(type_name<std::vector<int>>() == "std::vector<int>");
static_assert(type_name<std::map<int, std::vector<float>>>() == "std::map<int, std::vector<float>>");
Type aliases are resolved by the compiler, so the extracted name is that of the underlying type. For example type_name<std::string>() may yield std::__cxx11::basic_string<char> under libstdc++.
|
type_name is not supported for types enclosed in unnamed namespaces.
|
Enumerators
All examples in this section use the following definition:
#include <boost/reflecto/value_name.hpp>
using namespace boost::reflecto;
namespace graphics
{
enum class color
{
red,
green = 10,
blue = 20
};
}
| Enumerator reflection is not supported for enums enclosed in unnamed namespaces. |
Value names
For an enumerator known at compile time, value_name returns its fully qualified name (including the enum type for scoped enums), while short_value_name returns just the enumerator identifier:
static_assert(value_name<graphics::color::red>() == "graphics::color::red");
static_assert(short_value_name<graphics::color::red>() == "red");
Both functions also have overloads that take the value as a function argument:
graphics::color c = graphics::color::green;
// Run-time, from a variable:
assert(value_name(c) == "graphics::color::green");
assert(short_value_name(c) == "green");
For each enum, Reflecto generates a table mapping integer values to enumerator names. The covered interval is defined by the enum_lookup_range template, which can be specialized per enum or for an entire scope (e.g. all enums in a given namespace). See Scope-based specializations.
The overloads that take the value as a function argument use it as an index into the table. For values outside the range, the returned name has kind() == name_kind::value_out_of_lookup_range.
The overloads that take the value as a non-type template argument trigger a static_assert if the value is outside the range, to help detect inadequate lookup ranges.
Named values
For a given enum type, the following constexpr functions extract only the named values within the enum_lookup_range:
static_assert(named_value_count<graphics::color>() == 3);
static_assert(min_named_value<graphics::color>() == graphics::color::red);
static_assert(max_named_value<graphics::color>() == graphics::color::blue);
The named values (within the enum_lookup_range) can be extracted using named_values / short_named_values, which return a reference to a constexpr array of enumerator objects, sorted by integer value:
auto const & entries = named_values<graphics::color>();
static_assert(entries[0].value_name == "graphics::color::red");
static_assert(entries[0].value == 0);
static_assert(entries[1].value_name == "graphics::color::green");
static_assert(entries[1].value == 10);
static_assert(entries[2].value_name == "graphics::color::blue");
static_assert(entries[2].value == 20);
The array can be iterated:
for(auto const & e : named_values<graphics::color>())
std::cout << e.value_name << " = " << e.value << "\n";
sorted_named_values / sorted_short_named_values return the same enumerators sorted lexicographically by name.
Scopes
Scope names
returns the name of the enclosing scope (namespace or class) of a type:scope_name_of<T>()
#include <boost/reflecto/scope_name_of.hpp>
using namespace boost::reflecto;
namespace net
{
namespace http
{
struct request;
}
}
static_assert(scope_name_of<net::http::request>() == "net::http");
static_assert(scope_name_of<net::http::request>().kind() == name_kind::scope_name);
For a type at global scope, the returned name is empty:
struct widget;
static_assert(scope_name_of<widget>().empty());
static_assert(scope_name_of<widget>().kind() == name_kind::empty);
Scope traits
Reflecto provides two traits for testing scope relationships between two types: tests whether same_scope<T, U>T and U share the same enclosing scope, and tests whether in_scope_of<T, U>T is directly or transitively enclosed in U's enclosing scope. Both derive from std::true_type / std::false_type and come with the corresponding _v variable templates:
namespace lib
{
struct a;
struct b;
namespace nested
{
struct c;
struct d;
}
}
static_assert(same_scope_v<lib::a, lib::b>);
static_assert(!same_scope_v<lib::a, lib::nested::c>);
static_assert(in_scope_of_v<lib::a, lib::b>);
static_assert(in_scope_of_v<lib::nested::c, lib::a>);
static_assert(!in_scope_of_v<lib::a, lib::nested::c>);
Scope-based specializations
Reflecto provides a mechanism for specializing class templates not just for individual types, but for any type within a given scope. For example, a library may provide a customization point in the form of a class template and use Reflecto to support specializing the template for any type within the library namespace.
Single argument
An example use of the scope-based specialization system is Reflecto’s own enum_lookup_range class template (see Enumerators), defined as:
namespace boost::reflecto
{
template <class>
struct enum_lookup_range: unspecialized
{
static constexpr int min_value = BOOST_REFLECTO_DEFAULT_ENUM_MIN_VALUE;
static constexpr int max_value = BOOST_REFLECTO_DEFAULT_ENUM_MAX_VALUE;
};
}
By convention, the primary template derives from unspecialized, which lets Reflecto detect whether a given template instance is a specialization. If this is impractical, specialize the is_specialization template instead (it defaults to !std::is_base_of_v<unspecialized, T>).
Now enum_lookup_range can be specialized for all enums in a given namespace. We declare a this_namespace tag type to identify the namespace:
namespace lib_a
{
struct this_namespace;
enum class status
{
ok,
fail
};
}
namespace boost::reflecto
{
template <>
struct enum_lookup_range<within_scope_of<lib_a::this_namespace>>
{
static constexpr int min_value = 0;
static constexpr int max_value = 255;
};
}
A different library can do the same without clashing:
namespace lib_b
{
struct this_namespace;
enum class mode
{
fast,
slow
};
}
namespace boost::reflecto
{
template <>
struct enum_lookup_range<within_scope_of<lib_b::this_namespace>>
{
static constexpr int min_value = -100;
static constexpr int max_value = 100;
};
}
With these specializations in place, use bind_instance together with resolve_for to select the correct specialization for a given argument:
using ra = bind_instance<enum_lookup_range<resolve_for<lib_a::status>>>;
static_assert(ra::min_value == 0 && ra::max_value == 255);
using rb = bind_instance<enum_lookup_range<resolve_for<lib_b::mode>>>;
static_assert(rb::min_value == -100 && rb::max_value == 100);
Type-specific specializations (as in the color example from Enumerators) are supported and take precedence over scope-based specializations.
Exact scope vs. any enclosing scope
Two scope markers are provided:
matches types whose enclosing scope is exactly the scope of |
|
matches types whose enclosing scope is the scope of |
Exact-scope specializations are more specific than within-scope specializations; the compiler’s standard partial-ordering rules apply.
namespace lib
{
struct this_namespace;
struct widget;
namespace detail
{
struct this_namespace;
struct widget;
}
}
template <class T>
struct tag: unspecialized
{
static constexpr int id = 0;
};
template <>
struct tag<exact_scope_of<lib::this_namespace>>
{
static constexpr int id = 1;
};
template <>
struct tag<within_scope_of<lib::this_namespace>>
{
static constexpr int id = 2;
};
static_assert(bind_instance<tag<resolve_for<lib::widget>>>::id == 1); // exact
static_assert(bind_instance<tag<resolve_for<lib::detail::widget>>>::id == 2); // within
within_scope_of<T> cannot be used with globally-scoped types — a type at global scope matches every scope, defeating the purpose of the marker. This is enforced with a static_assert.
|
Multiple arguments
Class templates with several type parameters can mark each parameter for scope-based resolution independently. Here is an example specializing currency conversion fees:
namespace americas
{
struct this_namespace;
struct usd;
struct cad;
namespace caribbean
{
struct this_namespace;
struct jmd;
}
}
namespace europe
{
struct this_namespace;
struct eur;
struct gbp;
}
namespace asia
{
struct this_namespace;
struct jpy;
}
template <class From, class To>
struct conversion_fee: unspecialized
{
static constexpr int basis_points = 100;
};
template <>
struct conversion_fee<
within_scope_of<americas::this_namespace>,
within_scope_of<europe::this_namespace>>
{
static constexpr int basis_points = 50;
};
template <>
struct conversion_fee<
within_scope_of<americas::caribbean::this_namespace>,
within_scope_of<europe::this_namespace>>
{
static constexpr int basis_points = 25;
};
template <>
struct conversion_fee<
americas::usd,
within_scope_of<europe::this_namespace>>
{
static constexpr int basis_points = 10;
};
template <class T>
struct conversion_fee<T, T>
{
static constexpr int basis_points = 0;
};
bind_instance picks the best match across all arguments:
static_assert(bind_instance<conversion_fee<
resolve_for<americas::usd>,
resolve_for<europe::eur>>
>::basis_points == 10);
static_assert(bind_instance<conversion_fee<
resolve_for<americas::cad>,
resolve_for<europe::gbp>>
>::basis_points == 50);
static_assert(bind_instance<conversion_fee<
resolve_for<americas::caribbean::jmd>,
resolve_for<europe::eur>>
>::basis_points == 25);
static_assert(bind_instance<conversion_fee<
resolve_for<americas::usd>,
resolve_for<asia::jpy>>
>::basis_points == 100);
static_assert(bind_instance<conversion_fee<
resolve_for<europe::eur>,
resolve_for<europe::eur>>
>::basis_points == 0);
When resolving, bind_instance considers all matching specializations and selects the best one using partial-ordering rules similar to standard C++ overload resolution:
-
a type-specific match is more specific than an
exact_scope_ofmatch; -
an
exact_scope_ofmatch is more specific than awithin_scope_ofmatch; -
a more deeply nested
within_scope_ofmatch is more specific than an outer one.
If the partial ordering is ambiguous, we get a compile error. Suppose we add another specialization to the conversion_fee example above:
template <>
struct conversion_fee<
within_scope_of<americas::this_namespace>,
europe::eur>
{
static constexpr int basis_points = 5;
};
Now resolving conversion_fee<resolve_for<americas::usd>, resolve_for<europe::eur>> is ambiguous:
-
the existing
conversion_fee<americas::usd, within_scope_of<europe::this_namespace>>is more specific in the first argument (exact type vs within-scope) but less specific in the second; -
the new
conversion_fee<within_scope_of<americas::this_namespace>, europe::eur>is more specific in the second argument but less specific in the first.
The partial ordering is ambiguous, so bind_instance produces a compile error.
Synopsis
config.hpp
#ifndef BOOST_REFLECTO_ASSERT
# include <cassert>
# define BOOST_REFLECTO_ASSERT assert
#endif
#ifndef BOOST_REFLECTO_DEFAULT_ENUM_MIN_VALUE
# define BOOST_REFLECTO_DEFAULT_ENUM_MIN_VALUE -8
#endif
#ifndef BOOST_REFLECTO_DEFAULT_ENUM_MAX_VALUE
# define BOOST_REFLECTO_DEFAULT_ENUM_MAX_VALUE 63
#endif
namespace boost::reflecto
{
struct unspecialized { };
template <class>
struct enum_lookup_range: unspecialized
{
static constexpr int min_value = BOOST_REFLECTO_DEFAULT_ENUM_MIN_VALUE;
static constexpr int max_value = BOOST_REFLECTO_DEFAULT_ENUM_MAX_VALUE;
};
}
Reference: unspecialized | enum_lookup_range | Configuration
name.hpp
namespace boost::reflecto
{
enum class name_kind
{
empty,
scope_name,
type_name,
value_name,
short_value_name,
unnamed_value,
value_out_of_lookup_range
};
class name: public std::string_view
{
public:
constexpr name() noexcept;
explicit name(name const &) = default;
constexpr name_kind kind() const noexcept;
constexpr std::uint64_t hash() const noexcept;
friend constexpr bool operator==(name const &, name const &) noexcept;
friend constexpr bool operator!=(name const &, name const &) noexcept;
};
}
type_name.hpp
namespace boost::reflecto
{
template <class T>
constexpr name const & type_name() noexcept;
}
Reference: type_name
scope_name_of.hpp
namespace boost::reflecto
{
template <class T>
constexpr name const & scope_name_of() noexcept;
}
Reference: scope_name_of
scope_traits.hpp
namespace boost::reflecto
{
template <class T, class U>
struct same_scope: std::bool_constant<<<unspecified>>>
{
};
template <class T, class U>
inline constexpr bool same_scope_v = same_scope<T, U>::value;
template <class T, class U>
struct in_scope_of: std::bool_constant<<<unspecified>>>
{
};
template <class T, class U>
inline constexpr bool in_scope_of_v = in_scope_of<T, U>::value;
}
Reference: same_scope | in_scope_of
value_name.hpp
namespace boost::reflecto
{
template <class Enum>
using lookup_range = bind_instance<enum_lookup_range<resolve_for<Enum>>>;
struct enumerator
{
name const & value_name;
int value;
};
template <auto EnumValue>
constexpr name const & value_name() noexcept;
template <auto EnumValue>
constexpr name const & short_value_name() noexcept;
template <class Enum>
constexpr name const & value_name(Enum value) noexcept;
template <class Enum>
constexpr name const & short_value_name(Enum value) noexcept;
template <class Enum>
constexpr int named_value_count() noexcept;
template <class Enum>
constexpr Enum min_named_value() noexcept;
template <class Enum>
constexpr Enum max_named_value() noexcept;
template <class Enum>
constexpr enumerator const (&named_values() noexcept)[named_value_count<Enum>()];
template <class Enum>
constexpr enumerator const (&short_named_values() noexcept)[named_value_count<Enum>()];
template <class Enum>
constexpr enumerator const (&sorted_named_values() noexcept)[named_value_count<Enum>()];
template <class Enum>
constexpr enumerator const (&sorted_short_named_values() noexcept)[named_value_count<Enum>()];
}
bind_instance.hpp
namespace boost::reflecto
{
template <class>
struct resolve_for { };
template <class T>
struct is_specialization
{
static constexpr bool value = !std::is_base_of_v<unspecialized, T>;
};
template <class T>
using exact_scope_of = <<unspecified>>;
template <class T>
using within_scope_of = <<unspecified>>;
template <class Inst>
using bind_instance = <<unspecified>>;
}
Reference: resolve_for | is_specialization | exact_scope_of | within_scope_of | bind_instance
Reference: functions
| The contents of each Reference section are organized alphabetically. |
max_named_value
namespace boost::reflecto
{
template <class Enum>
constexpr Enum max_named_value() noexcept;
}
- Returns
-
The largest value among the named enumerators of
Enumwithin the effective.lookup_range<Enum>
min_named_value
namespace boost::reflecto
{
template <class Enum>
constexpr Enum min_named_value() noexcept;
}
- Returns
-
The smallest value among the named enumerators of
Enumwithin the effective.lookup_range<Enum>
named_value_count
namespace boost::reflecto
{
template <class Enum>
constexpr int named_value_count() noexcept;
}
- Returns
-
The number of named enumerators of
Enumfound within the effectivelookup_range.
enum class color
{
red,
green = 10,
blue = 20
};
static_assert(named_value_count<color>() == 3);
named_values
namespace boost::reflecto
{
template <class Enum>
constexpr enumerator const (&named_values() noexcept)[named_value_count<Enum>()];
}
- Returns
-
A reference to a
constexprarray ofenumeratorobjects, one per named value ofEnumwithin the effectivelookup_range, ordered by increasing integer value. Each element’svalue_nameis the fully qualified name (e.g."color::red").
for(auto const & e : named_values<color>())
std::cout << e.value_name << " = " << e.value << "\n";
scope_name_of
namespace boost::reflecto
{
template <class T>
constexpr name const & scope_name_of() noexcept;
}
- Returns
-
A reference to a
constexprnameobject containing the fully qualified name of the enclosing scope ofT. IfTis at global scope, the returnednameis empty and itskind()isname_kind::empty; otherwise itskind()isname_kind::scope_name.
namespace lib
{
namespace util
{
struct engine;
}
}
static_assert(scope_name_of<lib::util::engine>() == "lib::util");
short_named_values
namespace boost::reflecto
{
template <class Enum>
constexpr enumerator const (&short_named_values() noexcept)[named_value_count<Enum>()];
}
- Returns
-
Like
named_values, but for the unqualified enumerator names (e.g."red"instead of"color::red").
short_value_name
namespace boost::reflecto
{
template <auto EnumValue>
constexpr name const & short_value_name() noexcept;
template <class Enum>
constexpr name const & short_value_name(Enum value) noexcept;
}
The first overload takes the value as a non-type template argument; the second looks up value in a precomputed table for the effective enum_lookup_range.
- Returns
-
A reference to a
namecontaining the unqualified name of the given enumerator (e.g."red"). For the second overload, if(int)valuefalls outside the effectiveenum_lookup_range, the returnednamehaskind() == name_kind::value_out_of_lookup_range.
If (int)EnumValue falls outside the effective enum_lookup_range, the first overload triggers a static_assert to help detect inadequate lookup ranges.
|
sorted_named_values
namespace boost::reflecto
{
template <class Enum>
constexpr enumerator const (&sorted_named_values() noexcept)[named_value_count<Enum>()];
}
- Returns
-
Like
named_values, but the entries are sorted lexicographically byvalue_name.
sorted_short_named_values
namespace boost::reflecto
{
template <class Enum>
constexpr enumerator const (&sorted_short_named_values() noexcept)[named_value_count<Enum>()];
}
- Returns
-
Like
short_named_values, but the elements are sorted lexicographically byvalue_name.
type_name
namespace boost::reflecto
{
template <class T>
constexpr name const & type_name() noexcept;
}
- Returns
-
A reference to a
constexprnamecontaining the fully qualified name ofT, withkind() == name_kind::type_name.
static_assert(type_name<int>() == "int");
static_assert(type_name<std::vector<int>>() == "std::vector<int>");
| Not supported for types enclosed in unnamed namespaces. |
| Type aliases are resolved by the compiler; the extracted name is that of the underlying type. |
value_name
namespace boost::reflecto
{
template <auto EnumValue>
constexpr name const & value_name() noexcept;
template <class Enum>
constexpr name const & value_name(Enum value) noexcept;
}
Same as short_value_name, but returns the fully qualified name of the given enumerator (e.g. "color::red").
Reference: types
bind_instance
namespace boost::reflecto
{
template <class Inst>
using bind_instance = <<unspecified>>;
}
Given an instantiation Inst of the form S<A1, A2, …, An>, where zero or more of the Ai are of the form , resolve_for<Ti>bind_instance<Inst> names the specialization of S selected by the following process:
-
For each
resolve_for<Ti>argument, the enclosing scopes ofTiare collected, from innermost to outermost (plus the typeTiitself at the innermost position). Each scope contributes two match tokens: anexact_scope_oftoken and awithin_scope_oftoken;Tiitself contributes a type-match token. -
All combinations of these tokens across all
resolve_forpositions are considered as candidate specializations ofS. -
The C++ partial-ordering rules are applied across the candidates; the most specific one for which
is_specializationistrueis selected. -
If no candidate is a specialization, the primary template is used.
If the partial ordering is ambiguous, the compiler reports an error.
namespace lib
{
struct this_namespace;
struct widget;
}
template <class T>
struct tag: unspecialized
{
static constexpr int id = 0;
};
template <>
struct tag<within_scope_of<lib::this_namespace>>
{
static constexpr int id = 1;
};
static_assert(bind_instance<tag<resolve_for<lib::widget>>>::id == 1);
namespace lib
{
struct this_namespace;
struct widget;
struct gadget;
}
template <class T>
struct tag: unspecialized
{
static constexpr int id = 0;
};
template <>
struct tag<within_scope_of<lib::this_namespace>>
{
static constexpr int id = 1;
};
template <>
struct tag<lib::widget>
{
static constexpr int id = 2;
};
static_assert(bind_instance<tag<resolve_for<lib::widget>>>::id == 2);
static_assert(bind_instance<tag<resolve_for<lib::gadget>>>::id == 1);
namespace lib_a
{
struct this_namespace;
struct x;
}
namespace lib_b
{
struct this_namespace;
struct y;
}
template <class T, class U>
struct tag: unspecialized
{
static constexpr int bps = 0;
};
template <class U>
struct tag<within_scope_of<lib_a::this_namespace>, U>
{
static constexpr int bps = 10;
};
template <class T>
struct tag<T, within_scope_of<lib_b::this_namespace>>
{
static constexpr int bps = 20;
};
// Both partial specializations match but neither is more specific
// than the other -- the compiler reports the ambiguity.
enumerator
namespace boost::reflecto
{
struct enumerator
{
name const & value_name;
int value;
};
}
The type of the elements of the array returned by named_values and other similar functions.
enum_lookup_range
namespace boost::reflecto
{
template <class>
struct enum_lookup_range: unspecialized
{
static constexpr int min_value = BOOST_REFLECTO_DEFAULT_ENUM_MIN_VALUE;
static constexpr int max_value = BOOST_REFLECTO_DEFAULT_ENUM_MAX_VALUE;
};
}
Customization point that controls the integer range scanned at run time to discover enumerator names. Affects the following functions:
The enum_lookup_range template can be specialized for an individual enum type, or for all enums in a given scope using exact_scope_of / within_scope_of.
namespace lib
{
struct this_namespace;
enum class status
{
ok = 100,
fail = 200
};
}
namespace boost::reflecto
{
template <>
struct enum_lookup_range<within_scope_of<lib::this_namespace>>
{
static constexpr int min_value = 0;
static constexpr int max_value = 400;
};
}
static_assert(value_name<lib::status::ok>() == "lib::status::ok");
exact_scope_of
namespace boost::reflecto
{
template <class T>
using exact_scope_of = <<unspecified>>;
}
A marker used as a template argument in a specialization to match types whose enclosing scope is exactly the scope of T.
Unlike within_scope_of, exact_scope_of<T> is valid even when T is at global scope.
lookup_range
namespace boost::reflecto
{
template <class Enum>
using lookup_range = bind_instance<enum_lookup_range<resolve_for<Enum>>>;
}
The effective enum_lookup_range for Enum, after applying any type-specific or scope-based specialization via bind_instance.
name
namespace boost::reflecto
{
class name: public std::string_view
{
public:
constexpr name() noexcept;
explicit name(name const &) = default;
constexpr name_kind kind() const noexcept;
constexpr std::uint64_t hash() const noexcept;
friend constexpr bool operator==(name const &, name const &) noexcept;
friend constexpr bool operator!=(name const &, name const &) noexcept;
};
}
A constexpr string view annotated with a name_kind classifier and a precomputed FNV-1a hash. Comparison via operator== / operator!= is based on the hash, and therefore takes constant time.
name_kind
namespace boost::reflecto
{
enum class name_kind
{
empty,
scope_name,
type_name,
value_name,
short_value_name,
unnamed_value,
value_out_of_lookup_range
};
}
Classifier for a name:
|
The name is an empty string. |
|
Returned by |
|
Returned by |
|
Returned by |
|
Returned by |
|
Returned by |
|
Returned by the run-time |
resolve_for
namespace boost::reflecto
{
template <class>
struct resolve_for { };
}
Wrapper used in a bind_instance invocation to mark a type argument for scope-based resolution.
unspecialized
namespace boost::reflecto
{
struct unspecialized { };
}
To make a class template compatible with bind_instance, by convention its primary template should derive from unspecialized. When this is not practical (e.g. for third-party class templates), specialize the is_specialization template instead.
within_scope_of
namespace boost::reflecto
{
template <class T>
using within_scope_of = <<unspecified>>;
}
A marker used as a template argument in a specialization to match types whose enclosing scope is the scope of T or any scope nested within it.
within_scope_of cannot be used with globally-scoped types.
|
Reference: traits
in_scope_of
namespace boost::reflecto
{
template <class T, class U>
struct in_scope_of: std::bool_constant<<<unspecified>>>
{
};
template <class T, class U>
inline constexpr bool in_scope_of_v = in_scope_of<T, U>::value;
}
Derives from std::true_type iff T's enclosing scope equals U's enclosing scope, or is (transitively) nested within U's enclosing scope; otherwise derives from std::false_type.
When U's enclosing scope is the global scope, in_scope_of<T, U> derives from std::true_type for any T.
namespace lib
{
struct this_namespace;
struct a;
namespace nested
{
struct this_namespace;
struct c;
}
}
static_assert(in_scope_of_v<lib::a, lib::this_namespace>);
static_assert(in_scope_of_v<lib::nested::c, lib::this_namespace>);
static_assert(in_scope_of_v<lib::nested::c, lib::nested::this_namespace>);
static_assert(!in_scope_of_v<lib::a, lib::nested::this_namespace>);
is_specialization
namespace boost::reflecto
{
template <class T>
struct is_specialization
{
static constexpr bool value = !std::is_base_of_v<unspecialized, T>;
};
}
For scope-based specialization, bind_instance needs to detect whether a class template instance is a specialization or not. By convention, the primary template of the class template used with bind_instance should derive from unspecialized. Specialize is_specialization to customize the detection.
same_scope
namespace boost::reflecto
{
template <class T, class U>
struct same_scope: std::bool_constant<<<unspecified>>>
{
};
template <class T, class U>
inline constexpr bool same_scope_v = same_scope<T, U>::value;
}
Derives from std::true_type iff T and U have the same enclosing scope (including both being at global scope); otherwise derives from std::false_type.
namespace lib
{
struct a;
struct b;
namespace nested
{
struct c;
}
}
static_assert(same_scope_v<lib::a, lib::b>);
static_assert(!same_scope_v<lib::a, lib::nested::c>);
Configuration
The following configuration macros are defined iff not defined by the user.
-
BOOST_REFLECTO_ASSERT: The assertion macro used internally by Reflecto. If left undefined, Reflecto will#include <cassert>and then#define BOOST_REFLECTO_ASSERT assert. -
BOOST_REFLECTO_DEFAULT_ENUM_MIN_VALUE: The defaultmin_valueofenum_lookup_range. If left undefined, Reflecto defines it as-8. -
BOOST_REFLECTO_DEFAULT_ENUM_MAX_VALUE: The defaultmax_valueofenum_lookup_range. If left undefined, Reflecto defines it as63.
The enum_lookup_range template can be specialized for an individual enum type or for all enums within a given scope (e.g. a namespace). See Scope-based specializations.
|