Skip to content

Commit 230733d

Browse files
committed
First commit
0 parents  commit 230733d

9 files changed

+338
-0
lines changed

.gitignore

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/build/
2+
*.idea
3+
CMakeLists.txt
4+
*.vcxproj*
5+
Debug/
6+
Release/
7+
*.exe
8+
*.s

LICENSE.md

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License
2+
3+
Copyright (c) 2015 Manuel Sánchez
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

README.md

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# CTTI
2+
3+
Compile Time Type Information for the C++ programming language.
4+
5+
## Background
6+
7+
We know C++ is a statically typed compiled language, but it's disappointing that we cannot even get
8+
the name of a C++ type at compile time, having to use RTTI (Run Time Type Information) which introduces
9+
a lot of overhead. In fact, that's one of the most feared features of C++, usually disabled in performance dependent
10+
scenarios such as videogames programming.
11+
12+
This library aims to provide features similar to RTTI `std::type_info` at compile-time, currently `constexpr` type name and
13+
a `constexpr` replacement of `std::type_index` for indexing maps with types.
14+
15+
``` cpp
16+
#include "ctti/type_id.hpp"
17+
18+
int main()
19+
{
20+
static_assert(ctti::type_id<int>() != ctti::type_id("hello"), "compile-time type-id comparison");
21+
22+
std::unordered_map<ctti::type_id_t, std::size_t> sizeof_map;
23+
24+
sizeof_map[ctti::type_id<int>()] = sizeof(int);
25+
}
26+
```
27+
28+
## Support and current status
29+
30+
This was tested on both GCC 5.2.0 and Clang 3.6.2. It's fairly unstable (I'm currently working on it), and it leads
31+
to ugly ICEs whenever the `constexpr` machinery fails.
32+
33+
### Current state: Bugs...
34+
35+
`ctti::type_id` comparison and hashing works correctly, but I'm trying to find a bug that
36+
causes `type_ide::name()` to return empty or even corrupted strings. I'm sure there's something wrong with the
37+
`ctti::detail::string::substr()` function. I'm on it :)
38+
39+
## Acknowledgments
40+
41+
Thanks a lot to Jonathan "foonathan" Müller, both for the `constexpr` hash and the idea for the `__PRETTY_FUNCTION__` trick.
42+
43+
## License
44+
45+
This project is licensed under the MIT license. See LICENSE.md for more details.

ctti/ctti

14.4 KB
Binary file not shown.

ctti/detail/hash.hpp

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#ifndef CTTI_DETAIL_HASH_HPP
2+
#define CTTI_DETAIL_HASH_HPP
3+
4+
#include <cstdint>
5+
6+
namespace ctti
7+
{
8+
namespace detail
9+
{
10+
// From https://github.com/foonathan/string_id. As usually, thanks Jonathan.
11+
12+
using hash_t = std::uint64_t;
13+
14+
constexpr hash_t fnv_basis = 14695981039346656037ull;
15+
constexpr hash_t fnv_prime = 109951162821ull;
16+
17+
// FNV-1a 64 bit hash
18+
constexpr hash_t sid_hash(std::size_t n, const char *str, hash_t hash = fnv_basis)
19+
{
20+
return n > 0 ? sid_hash(n - 1, str + 1, (hash ^ *str) * fnv_prime) : hash;
21+
}
22+
}
23+
}
24+
25+
#endif /* CTTI_DETAIL_HASH_HPP */

ctti/detail/pretty_function.hpp

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//
2+
// Created by manu343726 on 4/08/15.
3+
//
4+
5+
#ifndef CTTI_PRETTY_FUNCTION_HPP
6+
#define CTTI_PRETTY_FUNCTION_HPP
7+
8+
#if defined(__clang__)
9+
#define CTTI_PRETTY_FUNCTION __PRETTY_FUNCTION__
10+
#define CTTI_PRETTY_FUNCTION_PREFIX "ctti::type_id_t ctti::detail::type_id() [T = "
11+
#define CTTI_PRETTY_FUNCTION_SUFFIX "]"
12+
#elif defined(__GNUC__) && !defined(__clang__)
13+
#define CTTI_PRETTY_FUNCTION __PRETTY_FUNCTION__
14+
#define CTTI_PRETTY_FUNCTION_PREFIX "ctti::type_id_t ctti::detail::type_id() [T = "
15+
#define CTTI_PRETTY_FUNCTION_SUFFIX "]"
16+
#endif
17+
18+
#define CTTI_PRETTY_FUNCTION_BEGIN (sizeof(CTTI_PRETTY_FUNCTION_PREFIX) - 1)
19+
#define CTTI_PRETTY_FUNCTION_END (sizeof(CTTI_PRETTY_FUNCTION) - 1 - sizeof(CTTI_PRETTY_FUNCTION_SUFFIX) - 1)
20+
#define CTTI_PRETTY_FUNCTION_LENGTH (CTTI_PRETTY_FUNCTION_END - CTTI_PRETTY_FUNCTION_BEGIN)
21+
22+
#endif //CTTI_PRETTY_FUNCTION_HPP

ctti/detail/string.hpp

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//
2+
// Created by manu343726 on 4/08/15.
3+
//
4+
5+
#ifndef CTTI_STRING_HPP
6+
#define CTTI_STRING_HPP
7+
8+
#include <ostream>
9+
10+
namespace ctti
11+
{
12+
namespace detail
13+
{
14+
struct string
15+
{
16+
template<std::size_t N>
17+
constexpr string(const char (&str)[N]) :
18+
string{str, N}
19+
{}
20+
21+
constexpr string(const char* str, std::size_t length) :
22+
str_{str},
23+
length_{length},
24+
hash_{sid_hash(length, str)}
25+
{}
26+
27+
constexpr hash_t hash() const
28+
{
29+
return hash_;
30+
}
31+
32+
constexpr const char* c_str() const
33+
{
34+
return str_;
35+
}
36+
37+
constexpr std::size_t length() const
38+
{
39+
return length_;
40+
}
41+
42+
constexpr char operator[](std::size_t i) const
43+
{
44+
return str_[i];
45+
}
46+
47+
template<std::size_t Begin, std::size_t End>
48+
constexpr string substr() const
49+
{
50+
return substr_<Begin>(std::make_index_sequence<End - Begin>{});
51+
}
52+
53+
friend std::ostream& operator<<(std::ostream& os, const string& str)
54+
{
55+
return os << str.c_str();
56+
}
57+
58+
private:
59+
template<std::size_t Begin, std::size_t... Is>
60+
constexpr string substr_(std::index_sequence<Is...>) const
61+
{
62+
const char str[] = {str_[Begin + Is]...};
63+
return { str };
64+
}
65+
66+
const char* str_;
67+
std::size_t length_;
68+
hash_t hash_;
69+
};
70+
71+
template<std::size_t N>
72+
constexpr string make_string(const char (&str)[N])
73+
{
74+
return {str};
75+
}
76+
}
77+
}
78+
79+
#endif //CTTI_STRING_HPP

ctti/main.cpp

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//
2+
// Created by manu343726 on 4/08/15.
3+
//
4+
5+
#include "type_id.hpp"
6+
#include <unordered_map>
7+
#include <iostream>
8+
9+
int main()
10+
{
11+
std::unordered_map<ctti::type_id_t, int> map;
12+
std::string name = ctti::type_id("hello").name().c_str();
13+
14+
map[ctti::type_id(1)] = 1;
15+
map[ctti::type_id(1u)] = 2;
16+
map[ctti::type_id(1ul)] = 3;
17+
18+
static_assert(ctti::type_id<int>() == ctti::type_id(1), "???");
19+
static_assert(ctti::type_id<int>() != ctti::type_id<int*>(), "???");
20+
21+
std::cout << map[ctti::type_id<int>()] << std::endl;
22+
std::cout << map[ctti::type_id<char>()] << std::endl;
23+
std::cout << map[ctti::type_id<unsigned long int>()] << std::endl;
24+
std::cout << ctti::type_id<int>().name() << std::endl;
25+
26+
std::cin.get();
27+
}

ctti/type_id.hpp

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
//
2+
// Created by manu343726 on 4/08/15.
3+
//
4+
5+
#ifndef CTTI_HASH_HPP
6+
#define CTTI_HASH_HPP
7+
8+
#include <functional>
9+
#include "detail/hash.hpp"
10+
#include "detail/pretty_function.hpp"
11+
#include "detail/string.hpp"
12+
13+
namespace ctti
14+
{
15+
struct type_id_t
16+
{
17+
constexpr type_id_t(const detail::string& name) :
18+
name_{name}
19+
{}
20+
21+
constexpr detail::hash_t hash() const
22+
{
23+
return name_.hash();
24+
}
25+
26+
constexpr detail::string name() const
27+
{
28+
return name_;
29+
}
30+
31+
friend constexpr bool operator==(const type_id_t& lhs, const type_id_t& rhs)
32+
{
33+
return lhs.hash() == rhs.hash();
34+
}
35+
36+
friend constexpr bool operator!=(const type_id_t& lhs, const type_id_t& rhs)
37+
{
38+
return !(lhs == rhs);
39+
}
40+
41+
private:
42+
detail::string name_;
43+
};
44+
45+
namespace detail
46+
{
47+
template<char... Chars>
48+
struct ct_string
49+
{
50+
static constexpr const char value[] = {Chars..., '\0'};
51+
};
52+
53+
template<char... Chars>
54+
constexpr const char ct_string<Chars...>::value[];
55+
56+
// The trick is to be able to discard both the preffix and suffix of
57+
// __PRETTY_FUNCTION__ string, getting T name only.
58+
// This is done in two steps:
59+
//
60+
// - type_id() function bellow gets T name length through CTTI_PRETTY_PRINT_LENGTH macro.
61+
// - Then __PRETTY_FUNCTION__ is computed again, using it to fill a variadic char string
62+
// template which will hold the T name.
63+
template<typename T, std::size_t... Is, std::size_t N>
64+
constexpr const char* parse_name(const char (&pretty_function)[N], std::index_sequence<Is...>)
65+
{
66+
constexpr std::size_t fn_length = 4 + sizeof("const char* parse_name(const char (&pretty_function)[N], std::index_sequence<Is...>)");
67+
using str = ct_string<CTTI_PRETTY_FUNCTION[fn_length + Is]...>;
68+
69+
return str::value;
70+
}
71+
72+
// Since _() is a template, __PRETTY_FUNCTION__ string varies with template parameter,
73+
// pretty function is usually something like "hash_t _<T>() [with T = *type here*]".
74+
template<typename T>
75+
constexpr ctti::type_id_t type_id()
76+
{
77+
const auto str = detail::make_string(CTTI_PRETTY_FUNCTION);
78+
const auto name = str.template substr<CTTI_PRETTY_FUNCTION_BEGIN, CTTI_PRETTY_FUNCTION_END>();
79+
80+
return {name};
81+
}
82+
}
83+
84+
template<typename T>
85+
constexpr type_id_t type_id(T&&)
86+
{
87+
return detail::type_id<typename std::decay<T>::type>();
88+
}
89+
90+
template<typename T>
91+
constexpr type_id_t type_id()
92+
{
93+
return detail::type_id<typename std::decay<T>::type>();
94+
}
95+
96+
//static_assert(type_id<void>() == type_id<void>(), "ctti::type_id_t instances must be constant expressions");
97+
}
98+
99+
namespace std
100+
{
101+
template<>
102+
struct hash<ctti::type_id_t>
103+
{
104+
std::size_t operator()(const ctti::type_id_t& id) const
105+
{
106+
return id.hash();
107+
}
108+
};
109+
}
110+
111+
#endif //CTTI_HASH_HPP

0 commit comments

Comments
 (0)