#pragma once #include #include #include namespace c10 { /** * This template simplifies generation of simple classes that wrap an id * in a typesafe way. Namely, you can use it to create a very lightweight * type that only offers equality comparators and hashing. Example: * * struct MyIdType final : IdWrapper { * constexpr explicit MyIdType(uint32_t id): IdWrapper(id) {} * }; * * Then in the global top level namespace: * * C10_DEFINE_HASH_FOR_IDWRAPPER(MyIdType); * * That's it - equality operators and hash functions are automatically defined * for you, given the underlying type supports it. */ template class IdWrapper { public: using underlying_type = UnderlyingType; using concrete_type = ConcreteType; protected: constexpr explicit IdWrapper(underlying_type id) noexcept( noexcept(underlying_type(std::declval()))) : id_(id) {} constexpr underlying_type underlyingId() const noexcept(noexcept(underlying_type(std::declval()))) { return id_; } private: friend size_t hash_value(const concrete_type& v) { return std::hash()(v.id_); } // TODO Making operator== noexcept if underlying type is noexcept equality // comparable doesn't work with GCC 4.8. // Fix this once we don't need GCC 4.8 anymore. friend constexpr bool operator==( const concrete_type& lhs, const concrete_type& rhs) noexcept { return lhs.id_ == rhs.id_; } // TODO Making operator!= noexcept if operator== is noexcept doesn't work with // GCC 4.8. // Fix this once we don't need GCC 4.8 anymore. friend constexpr bool operator!=( const concrete_type& lhs, const concrete_type& rhs) noexcept { return !(lhs == rhs); } underlying_type id_; }; } // namespace c10 #define C10_DEFINE_HASH_FOR_IDWRAPPER(ClassName) \ namespace std { \ template <> \ struct hash { \ size_t operator()(ClassName x) const { \ return hash_value(x); \ } \ }; \ }