Skip to content

Commit

Permalink
Implemented ID generation based on FNV1a hash of the type signature.
Browse files Browse the repository at this point in the history
  • Loading branch information
royvandam committed Feb 17, 2021
1 parent 5a9812c commit a2c1261
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 15 deletions.
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ set(CMAKE_VERBOSE_MAKEFILE OFF)
# Build options
option(ENABLE_DOCS "Enable documentation target" OFF)
option(ENABLE_CODE_COVERAGE "Enable code coverage generation" OFF)
option(ENABLE_CODE_ANALYSIS "Enable static code analysis" ON)
option(ENABLE_CPPCHECK "Enable cppcheck for static code analysis" ON)
option(ENABLE_CODE_ANALYSIS "Enable static code analysis" OFF)
option(ENABLE_CPPCHECK "Enable cppcheck for static code analysis" OFF)
option(ENABLE_CLANG_TIDY "Enable clang-tidy for static code analysis" OFF)
option(BUILD_TESTS "Build unittest binary" ON)
option(BUILD_BENCHMARK "Build benchmark binary" ON)
Expand Down
26 changes: 26 additions & 0 deletions include/hash.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#pragma once

#include <string_view>
#include <cstdint>

namespace Hash {
/**
* Calculates the 32bit FNV1a hash of a c-string literal.
* @param str String literal to be hashed
* @param n Length of the string.
* @return Calculated hash of the string
*/
static constexpr std::uint32_t FNV1a(const char* str, std::size_t n, std::uint32_t hash = UINT32_C(2166136261)) {
return n == 0 ? hash : FNV1a(str + 1, n - 1, (hash ^ str[0]) * UINT32_C(19777619));
}

/**
* Calculates the 32bit FNV1a hash of a std::string_view literal.
* note: Requires string_view to be a literal in order to be evaluated during compile time!
* @param str String literal to be hashed
* @return Calculated hash of the string
*/
static constexpr std::uint32_t FNV1a(std::string_view str) {
return FNV1a(str.data(), str.size());
}
}
69 changes: 56 additions & 13 deletions include/rtti.hh
Original file line number Diff line number Diff line change
Expand Up @@ -29,25 +29,61 @@
#include <cstdint>
#include <type_traits>

#include "hash.hh"

namespace RTTI {
/// Forward declaration of the Enable base.
struct Enable;
template <typename T>
constexpr std::string_view TypeName();

template <>
constexpr std::string_view TypeName<void>()
{ return "void"; }

namespace Detail {
template <typename T>
constexpr std::string_view WrappedTypeName() {
#ifdef __clang__
return __PRETTY_FUNCTION__;
#elif defined(__GNUC__)
return __PRETTY_FUNCTION__;
#else
#error "Unsupported compiler"
#endif
}

constexpr std::size_t WrappedTypeNamePrefixLength() {
return WrappedTypeName<void>().find(TypeName<void>());
}

constexpr std::size_t WrappedTypeNameSuffixLength() {
return WrappedTypeName<void>().length()
- WrappedTypeNamePrefixLength()
- TypeName<void>().length();
}
}

template <typename T>
constexpr std::string_view TypeName() {
constexpr auto wrappedTypeName = Detail::WrappedTypeName<T>();
constexpr auto prefixLength = Detail::WrappedTypeNamePrefixLength();
constexpr auto suffixLength = Detail::WrappedTypeNameSuffixLength();
constexpr auto typeNameLength = wrappedTypeName.length() - prefixLength - suffixLength;
return wrappedTypeName.substr(prefixLength, typeNameLength);
}

/// TypeId type definition
using TypeId = uintptr_t;
using TypeId = std::uint32_t;

namespace Detail {
template <typename T>
struct Type {
static constexpr int Id = 0;
};
} // namespace Detail
/// Forward declaration of the Enable base.
struct Enable;

/**
* Static typeinfo structure for registering types and accessing their information.
*/
template <typename This, typename... Parents>
struct TypeInfo {
using T = std::remove_const_t<std::remove_reference_t<This>>;

/// Ensure all passed parents are basses of this type.
static_assert((... && std::is_base_of<Parents, This>::value),
"One or more parents are not a base of this type.");
Expand All @@ -56,21 +92,28 @@ namespace RTTI {
static_assert((... && std::is_base_of<Enable, Parents>::value),
"One or more parent hierarchies is not based on top of RTTI::Enable.");

/**
* Returns the type string of the type T.
* @returns Type string
*/
[[nodiscard]] static constexpr std::string_view Name() noexcept {
return TypeName<T>();
}

/**
* Returns the type identifier of the type T.
* @returns Type identifier
*/
[[nodiscard]] static TypeId Id() noexcept {
return reinterpret_cast<TypeId>(
&Detail::Type<std::remove_const_t<std::remove_reference_t<This>>>::Id);
[[nodiscard]] static constexpr TypeId Id() noexcept {
return Hash::FNV1a(Name());
}

/**
* Checks whether the passed type is the same or a parent of the type.
* @tparam The type to compare the identifier with.
* @returns True in case a match was found.
*/
[[nodiscard]] static bool Is(TypeId typeId) noexcept {
[[nodiscard]] static constexpr bool Is(TypeId typeId) noexcept {
return (Id() == typeId) || (... || (Parents::TypeInfo::Is(typeId)));
}

Expand Down
25 changes: 25 additions & 0 deletions tests/hierarchy_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ struct ChildB
RTTI_DECLARE_TYPEINFO(ChildB, ParentA, ParentB);
};

template<typename T>
struct ChildT
: ParentA
, ParentB {
RTTI_DECLARE_TYPEINFO(ChildT<T>, ParentA, ParentB);
};

namespace {
TEST(HierarchyTest, TypeIdentification) {
ChildA childA;
Expand All @@ -44,6 +51,24 @@ namespace {
EXPECT_TRUE(childB.is<ParentB>());
EXPECT_TRUE(childB.is<GrandParent>());

ChildT<int> childTi;
EXPECT_EQ(childTi.typeId(), RTTI::TypeInfo<ChildT<int>>::Id());
EXPECT_FALSE(childTi.is<ChildT<float>>());
EXPECT_TRUE(childTi.is<ChildT<int>>());
EXPECT_TRUE(childTi.is<ParentA>());
EXPECT_TRUE(childTi.is<ParentB>());
EXPECT_TRUE(childTi.is<GrandParent>());

ChildT<float> childTf;
EXPECT_EQ(childTf.typeId(), RTTI::TypeInfo<ChildT<float>>::Id());
EXPECT_FALSE(childTf.is<ChildT<int>>());
EXPECT_TRUE(childTf.is<ChildT<float>>());
EXPECT_TRUE(childTf.is<ParentA>());
EXPECT_TRUE(childTf.is<ParentB>());
EXPECT_TRUE(childTf.is<GrandParent>());

EXPECT_NE(childTi.typeId(), childTf.typeId());

ParentA parentA;
EXPECT_EQ(parentA.typeId(), RTTI::TypeInfo<ParentA>::Id());
EXPECT_FALSE(parentA.is<ChildA>());
Expand Down

0 comments on commit a2c1261

Please sign in to comment.