Skip to content

Commit 89d456b

Browse files
authored
Use function address as TypeIndex (#6)
* use function address as TypeID * use std::size_t * use struct member address instead * add test for anonymous types * use decltype to get index type * use old solution for gcc * switch to minor release * change compiler test to macro * update how it works section * update version in readme * move test to TypeIndex * use static assert * allow switching the member pointer macro
1 parent 070c28f commit 89d456b

File tree

6 files changed

+64
-21
lines changed

6 files changed

+64
-21
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
44

55
# Note: update this to your new project's name and version
66
project(StaticTypeInfo
7-
VERSION 1.2
7+
VERSION 1.3
88
LANGUAGES CXX
99
)
1010

README.md

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,32 +36,24 @@ void example() {
3636
}
3737
```
3838

39-
## How it works
40-
41-
The type name is extracted from the macro `__PRETTY_FUNCTION__` (clang/gcc) or `__FUNCSIG__` (on MSVC) inside a probe function and converted to a `string_view` using the `constexpr` constructor.
42-
The type index is a 64 bit fnv1a hash of the type name.
43-
44-
## Hash collisions
45-
46-
hash collisions are theoretically possible but very unlikely, assuming that fnv1a is a sufficiently strong hash function. If the generated hashes are more or less uniformly distributed the [probability of a collision](https://en.wikipedia.org/wiki/Birthday_problem) should be approximately `T^2 / (2^65)` for any reasonable number of types `T`.
47-
So for a program with 1000 unique types, the probability of a hash collision in that program is on the order of `10^(-14)`, which is incredibly unlikely.
48-
For most real-life purpose we can therefore assume all hashes to be unique.
49-
50-
If you are paranoid or working on a security relevant project, you could bring that probability down to `0` with a simple test case checking the uniqueness of all used type indices within the project.
51-
5239
## Compatibility
5340

5441
The library has been tested with AppleClang 11, Visual Studio 16 2019, and gcc-9.
5542
Note that type names and indices are not compatible between compilers.
5643

44+
## How it works
45+
46+
The type name is extracted from the macro `__PRETTY_FUNCTION__` (clang/gcc) or `__FUNCSIG__` (on MSVC) inside a probe function and converted to a `string_view` using the `constexpr` constructor.
47+
The type index is determined from the pointer to a static member of a templated string (clang / msvc) or from a 64 bit fnv1a hash of the type name (gcc and others).
48+
5749
## How to integrate
5850

5951
Use [CPM.cmake](https://github.com/TheLartians/CPM.cmake) to easily add the headers to your CMake project.
6052

6153
```cmake
6254
CPMAddPackage(
6355
NAME StaticTypeInfo
64-
VERSION 1.1
56+
VERSION 1.3
6557
GIT_REPOSITORY https://github.com/TheLartians/StaticTypeInfo
6658
)
6759

include/static_type_info/type_id.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ namespace static_type_info {
2222

2323
namespace std {
2424
template <> struct hash<static_type_info::TypeID> {
25-
size_t operator()(const static_type_info::TypeID &id) const { return id.index; }
25+
hash<static_type_info::TypeIndex> hasher;
26+
size_t operator()(const static_type_info::TypeID &id) const { return hasher(id.index); }
2627
};
2728
} // namespace std

include/static_type_info/type_index.h

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,33 @@
11
#pragma once
22

3-
#include <static_type_info/hash.h>
4-
#include <static_type_info/type_name.h>
3+
#ifndef STATIC_TYPE_INFO_USE_MEMBER_POINTER
4+
// enable the new implementation using member pointers on tested compilers
5+
// gcc currently fails as member pointers are not considered constexpr
6+
# if defined(__clang__) || defined(_MSC_VER)
7+
# define STATIC_TYPE_INFO_USE_MEMBER_POINTER true
8+
# else
9+
# define STATIC_TYPE_INFO_USE_MEMBER_POINTER false
10+
# endif
11+
#endif
12+
13+
#if STATIC_TYPE_INFO_USE_MEMBER_POINTER
14+
15+
namespace static_type_info {
16+
17+
namespace detail {
18+
template <typename T> struct IDGenerator { static const void* const id; };
19+
template <typename T> const void* const IDGenerator<T>::id = nullptr;
20+
} // namespace detail
21+
22+
using TypeIndex = decltype(&detail::IDGenerator<void>::id);
23+
template <class T> constexpr TypeIndex getTypeIndex() { return &detail::IDGenerator<T>::id; }
24+
25+
} // namespace static_type_info
26+
27+
#else
28+
29+
# include <static_type_info/hash.h>
30+
# include <static_type_info/type_name.h>
531

632
namespace static_type_info {
733

@@ -12,3 +38,5 @@ namespace static_type_info {
1238
}
1339

1440
} // namespace static_type_info
41+
42+
#endif

test/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ endif()
3131
CPMAddPackage(
3232
NAME Format.cmake
3333
GITHUB_REPOSITORY TheLartians/Format.cmake
34-
VERSION 1.0
34+
VERSION 1.4
3535
)
3636

3737
# ---- Create binary ----

test/source/type_index.cpp

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include <doctest/doctest.h>
22
#include <static_type_info/type_index.h>
33

4+
#include <cstddef>
45
#include <type_traits>
56

67
namespace ns {
@@ -14,14 +15,16 @@ template <class A, class B> void checkType() {
1415
constexpr auto ia = getTypeIndex<A>();
1516
constexpr auto ib = getTypeIndex<B>();
1617
static_assert(ia != ib || std::is_same<A, B>::value);
17-
if (std::is_same<A, B>::value) {
18+
if constexpr (std::is_same<A, B>::value) {
1819
CHECK(ia == ib);
20+
static_assert(ia == ib);
1921
} else {
2022
CHECK(ia != ib);
23+
static_assert(ia != ib);
2124
}
2225
}
2326

24-
TEST_CASE_TEMPLATE("TypeIndex", T, char, int, unsigned, float, double, long, long long, size_t,
27+
TEST_CASE_TEMPLATE("TypeIndex", T, char, int, unsigned, float, double, long, long long, std::size_t,
2528
ns::A, ns::B, ns::C<ns::A>, ns::C<ns::B>) {
2629
checkType<char, T>();
2730
checkType<int, T>();
@@ -36,3 +39,22 @@ TEST_CASE_TEMPLATE("TypeIndex", T, char, int, unsigned, float, double, long, lon
3639
checkType<ns::C<ns::A>, T>();
3740
checkType<ns::C<ns::B>, T>();
3841
}
42+
43+
#if STATIC_TYPE_INFO_USE_MEMBER_POINTER
44+
// Anonymous functions are incompatible with the name based implementation
45+
46+
template <class T> constexpr auto getAnonymousTypeIndex() {
47+
using namespace static_type_info;
48+
struct X {};
49+
return getTypeIndex<X>();
50+
}
51+
52+
TEST_CASE("Anonymous types") {
53+
static_assert(getAnonymousTypeIndex<int>() != getAnonymousTypeIndex<unsigned>());
54+
static_assert(getAnonymousTypeIndex<int>() != getAnonymousTypeIndex<float>());
55+
static_assert(getAnonymousTypeIndex<int>() != getAnonymousTypeIndex<double>());
56+
}
57+
58+
#elif defined(__clang__) || defined(_MSC_VER)
59+
# error STATIC_TYPE_INFO_USE_MEMBER_POINTER not enabled
60+
#endif

0 commit comments

Comments
 (0)