Awesome
CTTI
Compile Time Type Information for the C++ programming language.
Background
We know C++ is a statically typed compiled language, but it's disappointing that we cannot even get the name of a C++ type at compile time, having to use RTTI (Run Time Type Information) which introduces a lot of overhead. In fact, that's one of the most feared features of C++, usually disabled in performance dependent scenarios such as videogames programming.
This library aims to provide features similar to RTTI std::type_info
at compile-time, currently constexpr
type name and
a constexpr
replacement of std::type_index
for indexing maps with types.
#include "ctti/type_id.hpp"
int main()
{
static_assert(ctti::type_id<int>() != ctti::type_id("hello"), "compile-time type-id comparison");
std::unordered_map<ctti::type_id_t, std::size_t> sizeof_map;
sizeof_map[ctti::type_id<int>()] = sizeof(int);
}
Support and current status
This was tested on Visual Studio 2015, GCC 5.2.0, MinGW GCC 5.1, Clang >3.6.2, and cygwin Clang 3.5.2.
ctti does no use of generalized constexpr
and must work on C++11 mode, but it needs support of constexpr
__func__
-like variables in the compiler.
All major compilers except GCC before 5.1 support this.
Features
ctti::nameof<T>()
:constexpr
demangled full qualified type names.ctti::nameof<T, T Value>()
:constexpr
demangled value strings.ctti::static_calue<T, Value>
:std::integral_constant
on steroids, to pass values as template parameters.CTTI_STATIC_VALUE(Value)
: Handy macro to instance actti::static_value
from the given value.ctti::detailed_nameof<>()
: Alternatives to nameof with detailed name info, supporting non-qualified names, getting qualifiers, etc. Seectti::name_t
for details.ctti::nameof_v<>
andctti::detailed_nameof_v<>
: C++14 variable template alternatives to the nameof family of functions. Thanks C++ Bruja for this suggestion.ctti::type_id<T>()
:constexpr
std::type_info
-like type identifier. Seectti::type_id_t
bellow for details.ctti::id_from_name(name)
: Get the type id corresponding to the given typename.ctti::unnamed_type_id<T>()
:constexpr
std::type_info
-like hash-only type identifier. Seectti::unnamed_type_id_t
bellow for details.- Symbol based introspection: Automatic serialization (wip), object conversion, etc. See symbolic introspection bellow.
Name customization
The names ctti return for a given static value or typename can be overrided with the ctti_nameof()
function. This can be achieved in two ways:
- Intrusive override: The user defined class defines a
ctti_nameof()
function as part of its static public API:struct Foo { static constexpr const char* ctti_nameof() { return "foo"; } };
- Non-intrusive override: A
ctti_nameof(type/value tag)
customization point is defined in the type/value namespace:#include <string> namespace std { constexpr const char* ctti_nameof(ctti::type_tag<std::string>) { return "std::string"; // instead of "std::__foobar::basic_string<char>" } }
ctti::detail::cstring
cstring
is a constexpr
implementation of an string view and the core of ctti. All strings returned by the ctti API are represented by this type.
It supports slicing via operator()(begin index, end index)
, subscript, hashing, string padding, comparison operators, print to std::ostream
, etc.
constexpr ctti::detail::cstring str{"hello world"};
constexpr ctti::detail::cstring str2{" hello world "};
static_assert(str(0, 5) == "hello");
static_assert(str2.pad(2, 2) == str);
constexpr std::uint64_t hash = str.hash(); // fnv1a hash or "hello world"
ctti::name_t
name_t
contains methods to extract information of a name. Given a full qualified name (like those returned by ctti::nameof()
) a name_t
can be constructed and
queried:
struct Foo
{
int i;
};
constexpr ctti::name_t FooName = ctti::detailed_nameof<CTTI_STATIC_VALUE(&Foo::i)>();
int main()
{
std::cout << FooName.name(); // prints "i"
std::cout << FooName.full_name(); // prints "&Foo::i"
std::cout << FooName.full_homogeneous_name(); // prints "Foo::i"
std::cout << FooName.qualifier(0); // prints "Foo"
}
All methods in name_t
are constexpr
and return cstring
s.
ctti::type_id_t
A constexpr
class representing an object identifier. It has the minimal constexpr
comparable interface to be used as a key type. The id is based on
a fnv1a hash of the type name, which can be obtained by calling type_id_t::hash()
. std::hash
is specialized for this type.
std::unordered_map<ctti::type_id_t, void*(*)()> factories;
factories[ctti::type_id<int>()] = []() -> void* { return new int{}; };
factories[ctti::type_id<std::string>()] = []() -> void* { return new std::string{}; };
void* int_var = factories[ctti::id_from_name("int")]();
ctti::unnamed_type_id_t
A lightweight version of type_id_t
which only stores the type hash (ctti::type_id_t
stores the name string, which takes one pointer and a size_t
, and
computes hash on demand. unnamed_type_id_t
stores the std::uint64_t
hash only).
struct object
{
ctti::unnamed_type_id_t type_id;
void* data;
};
Symbolic introspection
ctti implements object introspection by working with abstract "symbols" defined by the user:
#include <ctti/symbol.hpp>
CTTI_DEFINE_SYMBOL(foo);
the CTTI_DEFINE_SYMBOL
macro defines an abstract identifier, a symbol, in the current namespace. After its definition,
classes can be asked for members through that symbo:
CTTI_DEFINE_SYMBOL(i);
struct my_struct
{
int i;
};
static_assert(i::is_member_of<my_struct>(), "Has my_struct a member named 'i'?");
with symbols as non-intrusive generic member identifiers, objects can be manipulated in a generic way:
#include <ctti/symbol.hpp>
#include <ctti/tie.hpp>
CTTI_DEFINE_SYMBOL(a);
CTTI_DEFINE_SYMBOL(b);
CTTI_DEFINE_SYMBOL(c);
struct foo
{
int a, b, c;
};
int var_a, var_b;
foo my_foo{1, 2, 3};
ctti::tie<a, b>(var_a, var_b) = my_foo;
assert(var_a == 1);
assert(var_a == 2);
struct bar
{
int a, b;
};
bar my_bar;
// Copy my_foo.a to my_bar.a and my_foo.b to my_bar.b:
ctti::map(my_foo, my_bar, ctti::mapping<a, a>(), ctti::mapping<b, b>());
Conditional features
ctti has a set of conditional features that depend on non-C++11 language features. This ctti features can be controlled by different macros:
ctti::nameof_v/detailed_nameof_v
are declared only ifCTTI_HAS_VARIABLE_TEMPLATES
is defined. Enabled by default if the compiler supports variable templates.ctti::nameof/detailed_nameof
could work with enum values if the compiler prints those values as part of theirPRETTY_FUNCTION
variables. ctti controls this by defining theCTTI_HAS_ENUM_AWARE_PRETTY_FUNCTION
. If this macro is not defined ctti gives no guarantee you will get the right string from an enum value.
Acknowledgments
Thanks a lot to Jonathan "foonathan" Müller, both for the constexpr
hash and the idea for the __PRETTY_FUNCTION__
trick.
License
This project is licensed under the MIT license. See LICENSE.md for more details.