Преглед изворни кода

more on hashed strings and unique identifiers

Michele Caini пре 6 година
родитељ
комит
52bcf63554

+ 17 - 0
docs/md/core.md

@@ -9,6 +9,7 @@
 * [Compile-time identifiers](#compile-time-identifiers)
 * [Runtime identifiers](#runtime-identifiers)
 * [Hashed strings](#hashed-strings)
+  * [Wide characters](wide-characters)
   * [Conflicts](#conflicts)
 * [Monostate](#monostate)
 <!--
@@ -130,6 +131,22 @@ more user-friendly:
 constexpr auto str = "text"_hs;
 ```
 
+## Wide characters
+
+The hashed string has a design that is close to that of an `std::basic_string`.
+It means that `hashed_string` is nothing more than an alias for
+`basic_hashed_string<char>`. For those who want to use the C++ type for wide
+character representation, there exists also the alias `hashed_wstring` for
+`basic_hashed_string<wchar_t>`.<br/>
+In this case, the user defined literal to use to create hashed strings on the
+fly is `_hws`:
+
+```cpp
+constexpr auto str = "text"_hws;
+```
+
+Note that the hash type of the `hashed_wstring` is the same of its counterpart.
+
 ## Conflicts
 
 The hashed string class uses internally FNV-1a to compute the numeric

+ 84 - 55
docs/md/meta.md

@@ -6,12 +6,13 @@
 # Table of Contents
 
 * [Introduction](#introduction)
+* [Names and identifiers](#names-and-identifiers)
 * [Reflection in a nutshell](#reflection-in-a-nutshell)
-* [Any as in any type](#any-as-in-any-type)
-* [Enjoy the runtime](#enjoy-the-runtime)
-* [Named constants and enums](#named-constants-and-enums)
-* [Properties and meta objects](#properties-and-meta-objects)
-* [Unregister types](#unregister-types)
+  * [Any as in any type](#any-as-in-any-type)
+  * [Enjoy the runtime](#enjoy-the-runtime)
+  * [Named constants and enums](#named-constants-and-enums)
+  * [Properties and meta objects](#properties-and-meta-objects)
+  * [Unregister types](#unregister-types)
 <!--
 @endcond TURN_OFF_DOXYGEN
 -->
@@ -28,6 +29,34 @@ reflection system for `EnTT`. Maybe I didn't do better than others or maybe yes,
 time will tell me, but at least I can model this tool around the library to
 which it belongs and not vice versa.
 
+# Names and identifiers
+
+The meta system doesn't force the user to use the tools provided by the library
+when it comes to working with names and identifiers. It does this by offering an
+API that works with opaque identifiers that may or may not be generated by means
+of a hashed string.<br/>
+This means that users can assign any type of identifier to the meta objects, as
+long as they are numeric. It doesn't matter if they are generated at runtime, at
+compile-time or with custom functions.
+
+However, the examples in the following sections are all based on the
+`hashed_string` class as provided by this library. Therefore, where an
+identifier is required, it's likely that a user defined literal is used as
+follows:
+
+```cpp
+auto factory = entt::reflect<my_type>("reflected_type"_hs);
+```
+
+For what it's worth, this is likely completely equivalent to:
+
+```cpp
+auto factory = entt::reflect<my_type>(42);
+```
+
+Obviously, human-readable identifiers are more convenient to use and highly
+recommended.
+
 # Reflection in a nutshell
 
 Reflection always starts from real types (users cannot reflect imaginary types
@@ -36,15 +65,15 @@ anymore).<br/>
 To _reflect_ a type, the library provides the `reflect` function:
 
 ```cpp
-auto factory = entt::reflect<my_type>("reflected_type");
+auto factory = entt::reflect<my_type>("reflected_type"_hs);
 ```
 
-It accepts the type to reflect as a template parameter and an optional name as
-an argument. Names are important because users can retrieve meta types at
-runtime by searching for them by name. However, there are cases in which users
-can be interested in adding features to a reflected type so that the reflection
-system can use it correctly under the hood, but they don't want to allow
-searching the type by name.<br/>
+It accepts the type to reflect as a template parameter and an optional
+identifier as an argument. Identifiers are important because users can retrieve
+meta types at runtime by searching for them by _name_. However, there are cases
+in which users can be interested in adding features to a reflected type so that
+the reflection system can use it correctly under the hood, but they don't want
+to allow searching the type by _name_.<br/>
 In both cases, the returned value is a factory object to use to continue
 building the meta type.
 
@@ -59,7 +88,7 @@ It can be used to extend the reflected type and add the following:
   Use the `ctor` member function for this purpose:
 
   ```cpp
-  entt::reflect<my_type>("reflected").ctor<int, char>().ctor<&factory>();
+  entt::reflect<my_type>("reflected"_hs).ctor<int, char>().ctor<&factory>();
   ```
 
 * _Destructors_. Free functions can be set as destructors of reflected types.
@@ -68,7 +97,7 @@ It can be used to extend the reflected type and add the following:
   Use the `dtor` member function for this purpose:
 
   ```cpp
-  entt::reflect<my_type>("reflected").dtor<&destroy>();
+  entt::reflect<my_type>("reflected"_hs).dtor<&destroy>();
   ```
 
 * _Data members_. Both real data members of the underlying type and static and
@@ -78,15 +107,15 @@ It can be used to extend the reflected type and add the following:
   Use the `data` member function for this purpose:
 
   ```cpp
-  entt::reflect<my_type>("reflected")
-      .data<&my_type::static_variable>("static")
-      .data<&my_type::data_member>("member")
-      .data<&global_variable>("global");
+  entt::reflect<my_type>("reflected"_hs)
+      .data<&my_type::static_variable>("static"_hs)
+      .data<&my_type::data_member>("member"_hs)
+      .data<&global_variable>("global"_hs);
   ```
 
-  This function requires as an argument the name to give to the meta data once
-  created. Users can then access meta data at runtime by searching for them by
-  name.<br/>
+  This function requires as an argument the identifier to give to the meta data
+  once created. Users can then access meta data at runtime by searching for them
+  by _name_.<br/>
   Data members can be set also by means of a couple of functions, namely a
   setter and a getter. Setters and getters can be either free functions, member
   functions or mixed ones, as long as they respect the required signatures.<br/>
@@ -99,15 +128,15 @@ It can be used to extend the reflected type and add the following:
   Use the `func` member function for this purpose:
 
   ```cpp
-  entt::reflect<my_type>("reflected")
-      .func<&my_type::static_function>("static")
-      .func<&my_type::member_function>("member")
-      .func<&free_function>("free");
+  entt::reflect<my_type>("reflected"_hs)
+      .func<&my_type::static_function>("static"_hs)
+      .func<&my_type::member_function>("member"_hs)
+      .func<&free_function>("free"_hs);
   ```
 
-  This function requires as an argument the name to give to the meta function
-  once created. Users can then access meta functions at runtime by searching for
-  them by name.
+  This function requires as an argument the identifier to give to the meta
+  function once created. Users can then access meta functions at runtime by
+  searching for them by _name_.
 
 * _Base classes_. A base class is such that the underlying type is actually
   derived from it. In this case, the reflection system tracks the relationship
@@ -115,7 +144,7 @@ It can be used to extend the reflected type and add the following:
   Use the `base` member function for this purpose:
 
   ```cpp
-  entt::reflect<derived_type>("derived").base<base_type>();
+  entt::reflect<derived_type>("derived"_hs).base<base_type>();
   ```
 
   From now on, wherever a `base_type` is required, an instance of `derived_type`
@@ -138,7 +167,7 @@ Also, do not forget what these few lines hide under the hood: a built-in,
 non-intrusive and macro-free system for reflection in C++. Features that are
 definitely worth the price, at least for me.
 
-# Any as in any type
+## Any as in any type
 
 The reflection system comes with its own meta any type. It may seem redundant
 since C++17 introduced `std::any`, but it is not.<br/>
@@ -174,7 +203,7 @@ used to know if the underlying object has a given type as a base or if it can be
 converted implicitly to it. Similarly, `cast` and `convert` do what they promise
 and return the expected value.
 
-# Enjoy the runtime
+## Enjoy the runtime
 
 Once the web of reflected types has been constructed, it's a matter of using it
 at runtime where required.<br/>
@@ -182,7 +211,7 @@ All this has the great merit that, unlike the vast majority of the things
 present in this library and closely linked to the compile-time, the reflection
 system stands in fact as a non-intrusive tool for the runtime.
 
-To search for a reflected type there are two options: by type or by name. In
+To search for a reflected type there are two options: by type or by _name_. In
 both cases, the search can be done by means of the `resolve` function:
 
 ```cpp
@@ -190,7 +219,7 @@ both cases, the search can be done by means of the `resolve` function:
 auto by_type = entt::resolve<my_type>();
 
 // search for a reflected type by name
-auto by_name = entt::resolve("reflected_type");
+auto by_name = entt::resolve("reflected_type"_hs);
 ```
 
 There exits also a third overload of the `resolve` function to use to iterate
@@ -203,9 +232,9 @@ resolve([](auto type) {
 ```
 
 In all cases, the returned value is an instance of `meta_type`. This type of
-objects offer an API to know the _runtime name_ of the type, to iterate all the
-meta objects associated with them and even to build or destroy instances of the
-underlying type.<br/>
+objects offer an API to know the _runtime identifier_ of the type, to iterate
+all themeta objects associated with them and even to build or destroy instances
+of the underlying type.<br/>
 Refer to the inline documentation for all the details.
 
 The meta objects that compose a meta type are accessed in the following ways:
@@ -234,26 +263,26 @@ The meta objects that compose a meta type are accessed in the following ways:
   All what a meta destructor has to offer is a way to invoke it on a given
   instance. Be aware that the result may not be what is expected.
 
-* _Meta data_. They are accessed by name:
+* _Meta data_. They are accessed by _name_:
 
   ```cpp
-  auto data = entt::resolve<my_type>().data("member");
+  auto data = entt::resolve<my_type>().data("member"_hs);
   ```
 
   The returned type is `meta_data` and may be invalid if there is no meta data
-  object associated with the given name.<br/>
+  object associated with the given identifier.<br/>
   A meta data object offers an API to query the underlying type (ie to know if
   it's a const or a static one), to get the meta type of the variable and to set
   or get the contained value.
 
-* _Meta functions_. They are accessed by name:
+* _Meta functions_. They are accessed by _name_:
 
   ```cpp
-  auto func = entt::resolve<my_type>().func("member");
+  auto func = entt::resolve<my_type>().func("member"_hs);
   ```
 
   The returned type is `meta_func` and may be invalid if there is no meta
-  function object associated with the given name.<br/>
+  function object associated with the given identifier.<br/>
   A meta function object offers an API to query the underlying type (ie to know
   if it's a const or a static function), to know the number of arguments, the
   meta return type and the meta types of the parameters. In addition, a meta
@@ -261,14 +290,14 @@ The meta objects that compose a meta type are accessed in the following ways:
   return value in the form of meta any object.
 
 
-* _Meta bases_. They are accessed through the name of the base types:
+* _Meta bases_. They are accessed through the _name_ of the base types:
 
   ```cpp
-  auto base = entt::resolve<derived_type>().base("base");
+  auto base = entt::resolve<derived_type>().base("base"_hs);
   ```
 
   The returned type is `meta_base` and may be invalid if there is no meta base
-  object associated with the given name.<br/>
+  object associated with the given identifier.<br/>
   Meta bases aren't meant to be used directly, even though they are freely
   accessible. They expose only a few methods to use to know the meta type of the
   base class and to convert a raw pointer between types.
@@ -290,7 +319,7 @@ All the objects thus obtained as well as the meta types can be explicitly
 converted to a boolean value to check if they are valid:
 
 ```cpp
-auto func = entt::resolve<my_type>().func("member");
+auto func = entt::resolve<my_type>().func("member"_hs);
 
 if(func) {
     // ...
@@ -323,7 +352,7 @@ unfortunately beyond the scope of this document.<br/>
 I invite anyone interested in the subject to look at the code, experiment and
 read the official documentation to get the best out of this powerful tool.
 
-# Named constants and enums
+## Named constants and enums
 
 A special mention should be made for constant values and enums. It wouldn't be
 necessary, but it will help distracted readers.
@@ -343,25 +372,25 @@ Exporting constant values or elements from an enum is as simple as ever:
 
 ```cpp
 entt::reflect<my_enum>()
-        .data<my_enum::a_value>("a_value")
-        .data<my_enum::another_value>("another_value");
+        .data<my_enum::a_value>("a_value"_hs)
+        .data<my_enum::another_value>("another_value"_hs);
 
-entt::reflect<int>().data<2048>("max_int");
+entt::reflect<int>().data<2048>("max_int"_hs);
 ```
 
 It goes without saying that accessing them is trivial as well. It's a matter of
 doing the following, as with any other data member of a meta type:
 
 ```cpp
-auto value = entt::resolve<my_enum>().data("a_value").get({}).cast<my_enum>();
-auto max = entt::resolve<int>().data("max_int").get({}).cast<int>();
+auto value = entt::resolve<my_enum>().data("a_value"_hs).get({}).cast<my_enum>();
+auto max = entt::resolve<int>().data("max_int"_hs).get({}).cast<int>();
 ```
 
 As a side note, remember that all this happens behind the scenes without any
 allocation because of the small object optimization performed by the meta any
 class.
 
-# Properties and meta objects
+## Properties and meta objects
 
 Sometimes (ie when it comes to creating an editor) it might be useful to be able
 to attach properties to the meta objects created. Fortunately, this is possible
@@ -373,7 +402,7 @@ properties are nothing more than key/value pairs users can put in an
 `std::pair`. As an example:
 
 ```cpp
-entt::reflect<my_type>("reflected", std::make_pair("tooltip"_hs, "message"));
+entt::reflect<my_type>("reflected"_hs, std::make_pair("tooltip"_hs, "message"));
 ```
 
 The meta objects that support properties offer then a couple of member functions
@@ -393,7 +422,7 @@ Meta properties are objects having a fairly poor interface, all in all. They
 only provide the `key` and the `value` member functions to be used to retrieve
 the key and the value contained in the form of meta any objects, respectively.
 
-# Unregister types
+## Unregister types
 
 A type registered with the reflection system can also be unregistered. This
 means unregistering all its data members, member functions, conversion functions

+ 24 - 7
src/entt/core/hashed_string.hpp

@@ -72,6 +72,8 @@ class basic_hashed_string {
     }
 
 public:
+    /*! @brief Character type. */
+    using value_type = Char;
     /*! @brief Unsigned integer type. */
     using hash_type = ENTT_ID_TYPE;
 
@@ -91,7 +93,7 @@ public:
      * @return The numeric representation of the string.
      */
     template<std::size_t N>
-    static constexpr hash_type to_value(const Char (&str)[N]) ENTT_NOEXCEPT {
+    static constexpr hash_type to_value(const value_type (&str)[N]) ENTT_NOEXCEPT {
         return helper(traits_type::offset, str);
     }
 
@@ -110,7 +112,7 @@ public:
      * @param size Length of the string to hash.
      * @return The numeric representation of the string.
      */
-    static hash_type to_value(const Char *str, std::size_t size) ENTT_NOEXCEPT {
+    static hash_type to_value(const value_type *str, std::size_t size) ENTT_NOEXCEPT {
         ENTT_ID_TYPE partial{traits_type::offset};
         while(size--) { partial = (partial^(str++)[0])*traits_type::prime; }
         return partial;
@@ -136,13 +138,13 @@ public:
      * @param curr Human-readable identifer.
      */
     template<std::size_t N>
-    constexpr basic_hashed_string(const Char (&curr)[N]) ENTT_NOEXCEPT
+    constexpr basic_hashed_string(const value_type (&curr)[N]) ENTT_NOEXCEPT
         : str{curr}, hash{helper(traits_type::offset, curr)}
     {}
 
     /**
      * @brief Explicit constructor on purpose to avoid constructing a hashed
-     * string directly from a `const Char *`.
+     * string directly from a `const value_type *`.
      * @param wrapper Helps achieving the purpose by relying on overloading.
      */
     explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT
@@ -153,7 +155,7 @@ public:
      * @brief Returns the human-readable representation of a hashed string.
      * @return The string used to initialize the instance.
      */
-    constexpr const Char * data() const ENTT_NOEXCEPT {
+    constexpr const value_type * data() const ENTT_NOEXCEPT {
         return str;
     }
 
@@ -169,7 +171,7 @@ public:
      * @brief Returns the human-readable representation of a hashed string.
      * @return The string used to initialize the instance.
      */
-    constexpr operator const Char *() const ENTT_NOEXCEPT { return str; }
+    constexpr operator const value_type *() const ENTT_NOEXCEPT { return str; }
 
     /*! @copydoc value */
     constexpr operator hash_type() const ENTT_NOEXCEPT { return hash; }
@@ -184,11 +186,26 @@ public:
     }
 
 private:
-    const Char *str;
+    const value_type *str;
     hash_type hash;
 };
 
 
+/**
+ * @brief Deduction guide.
+ *
+ * It allows to deduce the character type of the hashed string directly from a
+ * human-readable identifer provided to the constructor.
+ *
+ * @tparam Char Character type.
+ * @tparam N Number of characters of the identifier.
+ * @param str Human-readable identifer.
+ */
+template<typename Char, std::size_t N>
+basic_hashed_string(const Char (&str)[N]) ENTT_NOEXCEPT
+-> basic_hashed_string<Char>;
+
+
 /**
  * @brief Compares two hashed strings.
  * @tparam Char Character type.

+ 2 - 3
src/entt/core/monostate.hpp

@@ -4,7 +4,6 @@
 
 #include <cassert>
 #include "../config/config.h"
-#include "hashed_string.hpp"
 
 
 namespace entt {
@@ -21,7 +20,7 @@ namespace entt {
  * both during an assignment and when they try to read back their data.
  * Otherwise, they can incur in unexpected results.
  */
-template<hashed_string::hash_type>
+template<ENTT_ID_TYPE>
 struct monostate {
     /**
      * @brief Assigns a value of a specific type to a given key.
@@ -53,7 +52,7 @@ private:
  * @brief Helper variable template.
  * @tparam Value Value used to differentiate between different variables.
  */
-template<hashed_string::hash_type Value>
+template<ENTT_ID_TYPE Value>
 inline monostate<Value> monostate_v = {};
 
 

+ 2 - 1
src/entt/core/type_traits.hpp

@@ -3,6 +3,7 @@
 
 
 #include <type_traits>
+#include "../config/config.h"
 #include "../core/hashed_string.hpp"
 
 
@@ -173,7 +174,7 @@ constexpr auto is_named_type_v = is_named_type<Type>::value;
 #define ENTT_NAMED_TYPE(type)\
     template<>\
     struct entt::named_type_traits<type>\
-        : std::integral_constant<typename entt::hashed_string::hash_type, entt::hashed_string::to_value(#type)>\
+        : std::integral_constant<ENTT_ID_TYPE, entt::basic_hashed_string{#type}>\
     {\
         static_assert(std::is_same_v<std::decay_t<type>, type>);\
     };

+ 2 - 3
src/entt/entity/helper.hpp

@@ -4,7 +4,6 @@
 
 #include <type_traits>
 #include "../config/config.h"
-#include "../core/hashed_string.hpp"
 #include "../signal/sigh.hpp"
 #include "registry.hpp"
 
@@ -201,8 +200,8 @@ void disconnect(sink<void(basic_registry<Entity> &, const Entity, Component &)>
  *
  * @tparam Value The numeric representation of an instance of hashed string.
  */
-template<typename hashed_string::hash_type Value>
-using tag = std::integral_constant<typename hashed_string::hash_type, Value>;
+template<ENTT_ID_TYPE Value>
+using tag = std::integral_constant<ENTT_ID_TYPE, Value>;
 
 
 }

+ 31 - 31
src/entt/meta/factory.hpp

@@ -6,7 +6,6 @@
 #include <algorithm>
 #include <type_traits>
 #include "../config/config.h"
-#include "../core/hashed_string.hpp"
 #include "meta.hpp"
 
 
@@ -18,7 +17,7 @@ class meta_factory;
 
 
 template<typename Type, typename... Property>
-meta_factory<Type> reflect(const char *str, Property &&... property) ENTT_NOEXCEPT;
+meta_factory<Type> reflect(const ENTT_ID_TYPE identifier, Property &&... property) ENTT_NOEXCEPT;
 
 
 template<typename Type>
@@ -40,8 +39,8 @@ class meta_factory {
     static_assert(std::is_object_v<Type> && !(std::is_const_v<Type> || std::is_volatile_v<Type>));
 
     template<typename Node>
-    bool duplicate(const hashed_string &name, const Node *node) ENTT_NOEXCEPT {
-        return node ? node->name == name || duplicate(name, node->next) : false;
+    bool duplicate(const ENTT_ID_TYPE identifier, const Node *node) ENTT_NOEXCEPT {
+        return node ? node->identifier == identifier || duplicate(identifier, node->next) : false;
     }
 
     bool duplicate(const meta_any &key, const internal::meta_prop_node *node) ENTT_NOEXCEPT {
@@ -77,7 +76,7 @@ class meta_factory {
     }
 
     template<typename... Property>
-    meta_factory type(hashed_string name, Property &&... property) ENTT_NOEXCEPT {
+    meta_factory type(const ENTT_ID_TYPE identifier, Property &&... property) ENTT_NOEXCEPT {
         static internal::meta_type_node node{
             {},
             nullptr,
@@ -103,10 +102,10 @@ class meta_factory {
             }
         };
 
-        node.name = name;
+        node.identifier = identifier;
         node.next = internal::meta_info<>::type;
         node.prop = properties<Type>(std::forward<Property>(property)...);
-        ENTT_ASSERT(!duplicate(name, node.next));
+        ENTT_ASSERT(!duplicate(identifier, node.next));
         ENTT_ASSERT(!internal::meta_info<Type>::type);
         internal::meta_info<Type>::type = &node;
         internal::meta_info<>::type = &node;
@@ -175,7 +174,7 @@ class meta_factory {
             unregister_all<&internal::meta_type_node::func>(0);
             unregister_dtor();
 
-            internal::meta_info<Type>::type->name = {};
+            internal::meta_info<Type>::type->identifier = {};
             internal::meta_info<Type>::type->next = nullptr;
             internal::meta_info<Type>::type = nullptr;
         }
@@ -187,7 +186,7 @@ class meta_factory {
 
 public:
     template<typename Other, typename... Property>
-    friend meta_factory<Other> reflect(const char *str, Property &&... property) ENTT_NOEXCEPT;
+    friend meta_factory<Other> reflect(const ENTT_ID_TYPE identifier, Property &&... property) ENTT_NOEXCEPT;
 
     template<typename Other>
     friend bool unregister() ENTT_NOEXCEPT;
@@ -398,12 +397,12 @@ public:
      *
      * @tparam Data The actual variable to attach to the meta type.
      * @tparam Property Types of properties to assign to the meta data.
-     * @param str The name to assign to the meta data.
+     * @param identifier Unique identifier.
      * @param property Properties to assign to the meta data.
      * @return A meta factory for the parent type.
      */
     template<auto Data, typename... Property>
-    meta_factory data(const char *str, Property &&... property) ENTT_NOEXCEPT {
+    meta_factory data(const ENTT_ID_TYPE identifier, Property &&... property) ENTT_NOEXCEPT {
         auto * const type = internal::meta_info<Type>::resolve();
         internal::meta_data_node *curr = nullptr;
 
@@ -471,9 +470,9 @@ public:
             curr = &node;
         }
 
-        curr->name = hashed_string{str};
+        curr->identifier = identifier;
         curr->next = type->data;
-        ENTT_ASSERT(!duplicate(hashed_string{str}, curr->next));
+        ENTT_ASSERT(!duplicate(identifier, curr->next));
         ENTT_ASSERT((!internal::meta_info<Type>::template data<Data>));
         internal::meta_info<Type>::template data<Data> = curr;
         type->data = curr;
@@ -498,12 +497,12 @@ public:
      * @tparam Setter The actual function to use as a setter.
      * @tparam Getter The actual function to use as a getter.
      * @tparam Property Types of properties to assign to the meta data.
-     * @param str The name to assign to the meta data.
+     * @param identifier Unique identifier.
      * @param property Properties to assign to the meta data.
      * @return A meta factory for the parent type.
      */
     template<auto Setter, auto Getter, typename... Property>
-    meta_factory data(const char *str, Property &&... property) ENTT_NOEXCEPT {
+    meta_factory data(const ENTT_ID_TYPE identifier, Property &&... property) ENTT_NOEXCEPT {
         using owner_type = std::tuple<std::integral_constant<decltype(Setter), Setter>, std::integral_constant<decltype(Getter), Getter>>;
         using underlying_type = std::invoke_result_t<decltype(Getter), Type *>;
         static_assert(std::is_invocable_v<decltype(Setter), Type *, underlying_type>);
@@ -525,10 +524,10 @@ public:
             }
         };
 
-        node.name = hashed_string{str};
+        node.identifier = identifier;
         node.next = type->data;
         node.prop = properties<owner_type>(std::forward<Property>(property)...);
-        ENTT_ASSERT(!duplicate(hashed_string{str}, node.next));
+        ENTT_ASSERT(!duplicate(identifier, node.next));
         ENTT_ASSERT((!internal::meta_info<Type>::template data<Setter, Getter>));
         internal::meta_info<Type>::template data<Setter, Getter> = &node;
         type->data = &node;
@@ -546,12 +545,12 @@ public:
      *
      * @tparam Func The actual function to attach to the meta type.
      * @tparam Property Types of properties to assign to the meta function.
-     * @param str The name to assign to the meta function.
+     * @param identifier Unique identifier.
      * @param property Properties to assign to the meta function.
      * @return A meta factory for the parent type.
      */
     template<auto Func, typename... Property>
-    meta_factory func(const char *str, Property &&... property) ENTT_NOEXCEPT {
+    meta_factory func(const ENTT_ID_TYPE identifier, Property &&... property) ENTT_NOEXCEPT {
         using owner_type = std::integral_constant<decltype(Func), Func>;
         using func_type = internal::meta_function_helper<std::integral_constant<decltype(Func), Func>>;
         auto * const type = internal::meta_info<Type>::resolve();
@@ -575,10 +574,10 @@ public:
             }
         };
 
-        node.name = hashed_string{str};
+        node.identifier = identifier;
         node.next = type->func;
         node.prop = properties<owner_type>(std::forward<Property>(property)...);
-        ENTT_ASSERT(!duplicate(hashed_string{str}, node.next));
+        ENTT_ASSERT(!duplicate(identifier, node.next));
         ENTT_ASSERT((!internal::meta_info<Type>::template func<Func>));
         internal::meta_info<Type>::template func<Func> = &node;
         type->func = &node;
@@ -598,13 +597,13 @@ public:
  *
  * @tparam Type Type to reflect.
  * @tparam Property Types of properties to assign to the reflected type.
- * @param str The name to assign to the reflected type.
+ * @param identifier Unique identifier.
  * @param property Properties to assign to the reflected type.
  * @return A meta factory for the given type.
  */
 template<typename Type, typename... Property>
-inline meta_factory<Type> reflect(const char *str, Property &&... property) ENTT_NOEXCEPT {
-    return meta_factory<Type>{}.type(hashed_string{str}, std::forward<Property>(property)...);
+inline meta_factory<Type> reflect(const ENTT_ID_TYPE identifier, Property &&... property) ENTT_NOEXCEPT {
+    return meta_factory<Type>{}.type(identifier, std::forward<Property>(property)...);
 }
 
 
@@ -655,13 +654,13 @@ inline meta_type resolve() ENTT_NOEXCEPT {
 
 
 /**
- * @brief Returns the meta type associated with a given name.
- * @param str The name to use to search for a meta type.
- * @return The meta type associated with the given name, if any.
+ * @brief Returns the meta type associated with a given identifier.
+ * @param identifier Unique identifier.
+ * @return The meta type associated with the given identifier, if any.
  */
-inline meta_type resolve(const char *str) ENTT_NOEXCEPT {
-    const auto *curr = internal::find_if([name = hashed_string{str}](auto *node) {
-        return node->name == name;
+inline meta_type resolve(const ENTT_ID_TYPE identifier) ENTT_NOEXCEPT {
+    const auto *curr = internal::find_if([identifier](auto *node) {
+        return node->identifier == identifier;
     }, internal::meta_info<>::type);
 
     return curr ? curr->meta() : meta_type{};
@@ -674,7 +673,8 @@ inline meta_type resolve(const char *str) ENTT_NOEXCEPT {
  * @param op A valid function object.
  */
 template<typename Op>
-inline void resolve(Op op) ENTT_NOEXCEPT {
+inline std::enable_if_t<std::is_invocable_v<Op, meta_type>, void>
+resolve(Op op) ENTT_NOEXCEPT {
     internal::iterate([op = std::move(op)](auto *node) {
         op(node->meta());
     }, internal::meta_info<>::type);

+ 39 - 37
src/entt/meta/meta.hpp

@@ -11,7 +11,6 @@
 #include <functional>
 #include <type_traits>
 #include "../config/config.h"
-#include "../core/hashed_string.hpp"
 
 
 namespace entt {
@@ -92,7 +91,7 @@ struct meta_dtor_node {
 
 struct meta_data_node {
     meta_data_node ** const underlying;
-    hashed_string name;
+    ENTT_ID_TYPE identifier;
     meta_type_node * const parent;
     meta_data_node * next;
     meta_prop_node * prop;
@@ -108,7 +107,7 @@ struct meta_data_node {
 struct meta_func_node {
     using size_type = std::size_t;
     meta_func_node ** const underlying;
-    hashed_string name;
+    ENTT_ID_TYPE identifier;
     meta_type_node * const parent;
     meta_func_node * next;
     meta_prop_node * prop;
@@ -124,7 +123,7 @@ struct meta_func_node {
 
 struct meta_type_node {
     using size_type = std::size_t;
-    hashed_string name;
+    ENTT_ID_TYPE identifier;
     meta_type_node * next;
     meta_prop_node * prop;
     const bool is_void;
@@ -1176,11 +1175,11 @@ public:
     {}
 
     /**
-     * @brief Returns the name assigned to a given meta data.
-     * @return The name assigned to the meta data.
+     * @brief Returns the identifier assigned to a given meta data.
+     * @return The identifier assigned to the meta data.
      */
-    const char * name() const ENTT_NOEXCEPT {
-        return node->name;
+    ENTT_ID_TYPE identifier() const ENTT_NOEXCEPT {
+        return node->identifier;
     }
 
     /**
@@ -1370,11 +1369,11 @@ public:
     {}
 
     /**
-     * @brief Returns the name assigned to a given meta function.
-     * @return The name assigned to the meta function.
+     * @brief Returns the identifier assigned to a given meta function.
+     * @return The identifier assigned to the meta function.
      */
-    const char * name() const ENTT_NOEXCEPT {
-        return node->name;
+    ENTT_ID_TYPE identifier() const ENTT_NOEXCEPT {
+        return node->identifier;
     }
 
     /**
@@ -1541,11 +1540,11 @@ public:
     {}
 
     /**
-     * @brief Returns the name assigned to a given meta type.
-     * @return The name assigned to the meta type.
+     * @brief Returns the identifier assigned to a given meta type.
+     * @return The identifier assigned to the meta type.
      */
-    const char * name() const ENTT_NOEXCEPT {
-        return node->name;
+    ENTT_ID_TYPE identifier() const ENTT_NOEXCEPT {
+        return node->identifier;
     }
 
     /**
@@ -1673,23 +1672,24 @@ public:
      * @param op A valid function object.
      */
     template<typename Op>
-    void base(Op op) const ENTT_NOEXCEPT {
+    std::enable_if_t<std::is_invocable_v<Op, meta_base>, void>
+    base(Op op) const ENTT_NOEXCEPT {
         internal::iterate<&internal::meta_type_node::base>([op = std::move(op)](auto *curr) {
             op(curr->meta());
         }, node);
     }
 
     /**
-     * @brief Returns the meta base associated with a given name.
+     * @brief Returns the meta base associated with a given identifier.
      *
      * Searches recursively among **all** the base classes of the given type.
      *
-     * @param str The name to use to search for a meta base.
-     * @return The meta base associated with the given name, if any.
+     * @param identifier Unique identifier.
+     * @return The meta base associated with the given identifier, if any.
      */
-    meta_base base(const char *str) const ENTT_NOEXCEPT {
-        const auto *curr = internal::find_if<&internal::meta_type_node::base>([name = hashed_string{str}](auto *candidate) {
-            return candidate->type()->name == name;
+    meta_base base(const ENTT_ID_TYPE identifier) const ENTT_NOEXCEPT {
+        const auto *curr = internal::find_if<&internal::meta_type_node::base>([identifier](auto *candidate) {
+            return candidate->type()->identifier == identifier;
         }, node);
 
         return curr ? curr->meta() : meta_base{};
@@ -1771,25 +1771,26 @@ public:
      * @param op A valid function object.
      */
     template<typename Op>
-    void data(Op op) const ENTT_NOEXCEPT {
+    std::enable_if_t<std::is_invocable_v<Op, meta_data>, void>
+    data(Op op) const ENTT_NOEXCEPT {
         internal::iterate<&internal::meta_type_node::data>([op = std::move(op)](auto *curr) {
             op(curr->meta());
         }, node);
     }
 
     /**
-     * @brief Returns the meta data associated with a given name.
+     * @brief Returns the meta data associated with a given identifier.
      *
      * Searches recursively among **all** the meta data of the given type. This
      * means that the meta data of the base classes will also be inspected, if
      * any.
      *
-     * @param str The name to use to search for a meta data.
-     * @return The meta data associated with the given name, if any.
+     * @param identifier Unique identifier.
+     * @return The meta data associated with the given identifier, if any.
      */
-    meta_data data(const char *str) const ENTT_NOEXCEPT {
-        const auto *curr = internal::find_if<&internal::meta_type_node::data>([name = hashed_string{str}](auto *candidate) {
-            return candidate->name == name;
+    meta_data data(const ENTT_ID_TYPE identifier) const ENTT_NOEXCEPT {
+        const auto *curr = internal::find_if<&internal::meta_type_node::data>([identifier](auto *candidate) {
+            return candidate->identifier == identifier;
         }, node);
 
         return curr ? curr->meta() : meta_data{};
@@ -1806,25 +1807,26 @@ public:
      * @param op A valid function object.
      */
     template<typename Op>
-    void func(Op op) const ENTT_NOEXCEPT {
+    std::enable_if_t<std::is_invocable_v<Op, meta_func>, void>
+    func(Op op) const ENTT_NOEXCEPT {
         internal::iterate<&internal::meta_type_node::func>([op = std::move(op)](auto *curr) {
             op(curr->meta());
         }, node);
     }
 
     /**
-     * @brief Returns the meta function associated with a given name.
+     * @brief Returns the meta function associated with a given identifier.
      *
      * Searches recursively among **all** the meta functions of the given type.
      * This means that the meta functions of the base classes will also be
      * inspected, if any.
      *
-     * @param str The name to use to search for a meta function.
-     * @return The meta function associated with the given name, if any.
+     * @param identifier Unique identifier.
+     * @return The meta function associated with the given identifier, if any.
      */
-    meta_func func(const char *str) const ENTT_NOEXCEPT {
-        const auto *curr = internal::find_if<&internal::meta_type_node::func>([name = hashed_string{str}](auto *candidate) {
-            return candidate->name == name;
+    meta_func func(const ENTT_ID_TYPE identifier) const ENTT_NOEXCEPT {
+        const auto *curr = internal::find_if<&internal::meta_type_node::func>([identifier](auto *candidate) {
+            return candidate->identifier == identifier;
         }, node);
 
         return curr ? curr->meta() : meta_func{};

+ 2 - 3
src/entt/resource/cache.hpp

@@ -7,7 +7,6 @@
 #include <type_traits>
 #include <unordered_map>
 #include "../config/config.h"
-#include "../core/hashed_string.hpp"
 #include "handle.hpp"
 #include "loader.hpp"
 #include "fwd.hpp"
@@ -28,13 +27,13 @@ namespace entt {
  */
 template<typename Resource>
 class resource_cache {
-    using container_type = std::unordered_map<hashed_string::hash_type, std::shared_ptr<Resource>>;
+    using container_type = std::unordered_map<ENTT_ID_TYPE, std::shared_ptr<Resource>>;
 
 public:
     /*! @brief Unsigned integer type. */
     using size_type = typename container_type::size_type;
     /*! @brief Type of resources managed by a cache. */
-    using resource_type = typename hashed_string::hash_type;
+    using resource_type = ENTT_ID_TYPE;
 
     /*! @brief Default constructor. */
     resource_cache() = default;

+ 5 - 0
test/entt/core/hashed_string.cpp

@@ -121,3 +121,8 @@ TEST(HashedWString, StringView) {
     std::wstring_view view{str.data()+2, 6};
     ASSERT_EQ(entt::hashed_wstring::to_value(view.data(), view.size()), 0xbf9cf968);
 }
+
+TEST(BasicHashedString, DeductionGuide) {
+    static_assert(std::is_same_v<decltype(entt::basic_hashed_string{"foo"}), entt::hashed_string>);
+    static_assert(std::is_same_v<decltype(entt::basic_hashed_string{L"foo"}), entt::hashed_wstring>);
+}

+ 160 - 160
test/entt/meta/meta.cpp

@@ -130,77 +130,77 @@ struct Meta: public ::testing::Test {
     static void SetUpTestCase() {
         entt::reflect<double>().conv<int>();
 
-        entt::reflect<char>("char", std::make_pair(properties::prop_int, 42));
+        entt::reflect<char>("char"_hs, std::make_pair(properties::prop_int, 42));
 
         entt::reflect<properties>()
-                .data<properties::prop_bool>("prop_bool")
-                .data<properties::prop_int>("prop_int");
+                .data<properties::prop_bool>("prop_bool"_hs)
+                .data<properties::prop_int>("prop_int"_hs);
 
-        entt::reflect<unsigned int>().data<0u>("min").data<100u>("max");
+        entt::reflect<unsigned int>().data<0u>("min"_hs).data<100u>("max"_hs);
 
-        entt::reflect<base_type>("base");
+        entt::reflect<base_type>("base"_hs);
 
-        entt::reflect<derived_type>("derived", std::make_pair(properties::prop_int, 99))
+        entt::reflect<derived_type>("derived"_hs, std::make_pair(properties::prop_int, 99))
                 .base<base_type>()
                 .ctor<const base_type &, int, char>(std::make_pair(properties::prop_bool, false))
                 .ctor<&derived_factory>(std::make_pair(properties::prop_int, 42));
 
-        entt::reflect<empty_type>("empty")
+        entt::reflect<empty_type>("empty"_hs)
                 .dtor<&empty_type::destroy>();
 
-        entt::reflect<fat_type>("fat")
+        entt::reflect<fat_type>("fat"_hs)
                 .base<empty_type>()
                 .dtor<&fat_type::destroy>();
 
-        entt::reflect<data_type>("data")
-                .data<&data_type::i>("i", std::make_pair(properties::prop_int, 0))
-                .data<&data_type::j>("j", std::make_pair(properties::prop_int, 1))
-                .data<&data_type::h>("h", std::make_pair(properties::prop_int, 2))
-                .data<&data_type::k>("k", std::make_pair(properties::prop_int, 3))
-                .data<&data_type::empty>("empty");
-
-        entt::reflect<array_type>("array")
-                .data<&array_type::global>("global")
-                .data<&array_type::local>("local");
-
-        entt::reflect<func_type>("func")
-                .func<entt::overload<int(const base_type &, int, int)>(&func_type::f)>("f3")
-                .func<entt::overload<int(int, int)>(&func_type::f)>("f2", std::make_pair(properties::prop_bool, false))
-                .func<entt::overload<int(int) const>(&func_type::f)>("f1", std::make_pair(properties::prop_bool, false))
-                .func<&func_type::g>("g", std::make_pair(properties::prop_bool, false))
-                .func<&func_type::h>("h", std::make_pair(properties::prop_bool, false))
-                .func<&func_type::k>("k", std::make_pair(properties::prop_bool, false));
-
-        entt::reflect<setter_getter_type>("setter_getter")
-                .data<&setter_getter_type::static_setter, &setter_getter_type::static_getter>("x")
-                .data<&setter_getter_type::setter, &setter_getter_type::getter>("y")
-                .data<&setter_getter_type::static_setter, &setter_getter_type::getter>("z")
-                .data<&setter_getter_type::setter_with_ref, &setter_getter_type::getter_with_ref>("w");
-
-        entt::reflect<an_abstract_type>("an_abstract_type", std::make_pair(properties::prop_bool, false))
-                .data<&an_abstract_type::i>("i")
-                .func<&an_abstract_type::f>("f")
-                .func<&an_abstract_type::g>("g");
-
-        entt::reflect<another_abstract_type>("another_abstract_type", std::make_pair(properties::prop_int, 42))
-                .data<&another_abstract_type::j>("j")
-                .func<&another_abstract_type::h>("h");
-
-        entt::reflect<concrete_type>("concrete")
+        entt::reflect<data_type>("data"_hs)
+                .data<&data_type::i>("i"_hs, std::make_pair(properties::prop_int, 0))
+                .data<&data_type::j>("j"_hs, std::make_pair(properties::prop_int, 1))
+                .data<&data_type::h>("h"_hs, std::make_pair(properties::prop_int, 2))
+                .data<&data_type::k>("k"_hs, std::make_pair(properties::prop_int, 3))
+                .data<&data_type::empty>("empty"_hs);
+
+        entt::reflect<array_type>("array"_hs)
+                .data<&array_type::global>("global"_hs)
+                .data<&array_type::local>("local"_hs);
+
+        entt::reflect<func_type>("func"_hs)
+                .func<entt::overload<int(const base_type &, int, int)>(&func_type::f)>("f3"_hs)
+                .func<entt::overload<int(int, int)>(&func_type::f)>("f2"_hs, std::make_pair(properties::prop_bool, false))
+                .func<entt::overload<int(int) const>(&func_type::f)>("f1"_hs, std::make_pair(properties::prop_bool, false))
+                .func<&func_type::g>("g"_hs, std::make_pair(properties::prop_bool, false))
+                .func<&func_type::h>("h"_hs, std::make_pair(properties::prop_bool, false))
+                .func<&func_type::k>("k"_hs, std::make_pair(properties::prop_bool, false));
+
+        entt::reflect<setter_getter_type>("setter_getter"_hs)
+                .data<&setter_getter_type::static_setter, &setter_getter_type::static_getter>("x"_hs)
+                .data<&setter_getter_type::setter, &setter_getter_type::getter>("y"_hs)
+                .data<&setter_getter_type::static_setter, &setter_getter_type::getter>("z"_hs)
+                .data<&setter_getter_type::setter_with_ref, &setter_getter_type::getter_with_ref>("w"_hs);
+
+        entt::reflect<an_abstract_type>("an_abstract_type"_hs, std::make_pair(properties::prop_bool, false))
+                .data<&an_abstract_type::i>("i"_hs)
+                .func<&an_abstract_type::f>("f"_hs)
+                .func<&an_abstract_type::g>("g"_hs);
+
+        entt::reflect<another_abstract_type>("another_abstract_type"_hs, std::make_pair(properties::prop_int, 42))
+                .data<&another_abstract_type::j>("j"_hs)
+                .func<&another_abstract_type::h>("h"_hs);
+
+        entt::reflect<concrete_type>("concrete"_hs)
                 .base<an_abstract_type>()
                 .base<another_abstract_type>()
-                .func<&concrete_type::f>("f");
+                .func<&concrete_type::f>("f"_hs);
     }
 
     static void SetUpAfterUnregistration() {
         entt::reflect<double>().conv<float>();
 
-        entt::reflect<derived_type>("my_type", std::make_pair(properties::prop_bool, false))
+        entt::reflect<derived_type>("my_type"_hs, std::make_pair(properties::prop_bool, false))
                 .ctor<>();
 
-        entt::reflect<another_abstract_type>("your_type")
-                .data<&another_abstract_type::j>("a_data_member")
-                .func<&another_abstract_type::h>("a_member_function");
+        entt::reflect<another_abstract_type>("your_type"_hs)
+                .data<&another_abstract_type::j>("a_data_member"_hs)
+                .func<&another_abstract_type::h>("a_member_function"_hs);
     }
 
     void SetUp() override {
@@ -210,7 +210,7 @@ struct Meta: public ::testing::Test {
 };
 
 TEST_F(Meta, Resolve) {
-    ASSERT_EQ(entt::resolve<derived_type>(), entt::resolve("derived"));
+    ASSERT_EQ(entt::resolve<derived_type>(), entt::resolve("derived"_hs));
 
     bool found = false;
 
@@ -621,12 +621,12 @@ TEST_F(Meta, MetaProp) {
 }
 
 TEST_F(Meta, MetaBase) {
-    auto base = entt::resolve<derived_type>().base("base");
+    auto base = entt::resolve<derived_type>().base("base"_hs);
     derived_type derived{};
 
     ASSERT_TRUE(base);
     ASSERT_NE(base, entt::meta_base{});
-    ASSERT_EQ(base.parent(), entt::resolve("derived"));
+    ASSERT_EQ(base.parent(), entt::resolve("derived"_hs));
     ASSERT_EQ(base.type(), entt::resolve<base_type>());
     ASSERT_EQ(base.cast(&derived), static_cast<base_type *>(&derived));
 }
@@ -652,7 +652,7 @@ TEST_F(Meta, MetaCtor) {
 
     ASSERT_TRUE(ctor);
     ASSERT_NE(ctor, entt::meta_ctor{});
-    ASSERT_EQ(ctor.parent(), entt::resolve("derived"));
+    ASSERT_EQ(ctor.parent(), entt::resolve("derived"_hs));
     ASSERT_EQ(ctor.size(), entt::meta_ctor::size_type{3});
     ASSERT_EQ(ctor.arg(entt::meta_ctor::size_type{0}), entt::resolve<base_type>());
     ASSERT_EQ(ctor.arg(entt::meta_ctor::size_type{1}), entt::resolve<int>());
@@ -687,7 +687,7 @@ TEST_F(Meta, MetaCtorFunc) {
     auto ctor = entt::resolve<derived_type>().ctor<const base_type &, int>();
 
     ASSERT_TRUE(ctor);
-    ASSERT_EQ(ctor.parent(), entt::resolve("derived"));
+    ASSERT_EQ(ctor.parent(), entt::resolve("derived"_hs));
     ASSERT_EQ(ctor.size(), entt::meta_ctor::size_type{2});
     ASSERT_EQ(ctor.arg(entt::meta_ctor::size_type{0}), entt::resolve<base_type>());
     ASSERT_EQ(ctor.arg(entt::meta_ctor::size_type{1}), entt::resolve<int>());
@@ -773,7 +773,7 @@ TEST_F(Meta, MetaDtor) {
 
     ASSERT_TRUE(dtor);
     ASSERT_NE(dtor, entt::meta_dtor{});
-    ASSERT_EQ(dtor.parent(), entt::resolve("empty"));
+    ASSERT_EQ(dtor.parent(), entt::resolve("empty"_hs));
     ASSERT_EQ(empty_type::counter, 0);
     ASSERT_TRUE(dtor.invoke(empty));
     ASSERT_EQ(empty_type::counter, 1);
@@ -794,14 +794,14 @@ TEST_F(Meta, MetaDtorMetaAnyInvalidArg) {
 
 
 TEST_F(Meta, MetaData) {
-    auto data = entt::resolve<data_type>().data("i");
+    auto data = entt::resolve<data_type>().data("i"_hs);
     data_type instance{};
 
     ASSERT_TRUE(data);
     ASSERT_NE(data, entt::meta_data{});
-    ASSERT_EQ(data.parent(), entt::resolve("data"));
+    ASSERT_EQ(data.parent(), entt::resolve("data"_hs));
     ASSERT_EQ(data.type(), entt::resolve<int>());
-    ASSERT_STREQ(data.name(), "i");
+    ASSERT_EQ(data.identifier(), "i"_hs);
     ASSERT_FALSE(data.is_const());
     ASSERT_FALSE(data.is_static());
     ASSERT_EQ(data.get(instance).cast<int>(), 0);
@@ -824,13 +824,13 @@ TEST_F(Meta, MetaData) {
 }
 
 TEST_F(Meta, MetaDataConst) {
-    auto data = entt::resolve<data_type>().data("j");
+    auto data = entt::resolve<data_type>().data("j"_hs);
     data_type instance{};
 
     ASSERT_TRUE(data);
-    ASSERT_EQ(data.parent(), entt::resolve("data"));
+    ASSERT_EQ(data.parent(), entt::resolve("data"_hs));
     ASSERT_EQ(data.type(), entt::resolve<int>());
-    ASSERT_STREQ(data.name(), "j");
+    ASSERT_EQ(data.identifier(), "j"_hs);
     ASSERT_TRUE(data.is_const());
     ASSERT_FALSE(data.is_static());
     ASSERT_EQ(data.get(instance).cast<int>(), 1);
@@ -853,12 +853,12 @@ TEST_F(Meta, MetaDataConst) {
 }
 
 TEST_F(Meta, MetaDataStatic) {
-    auto data = entt::resolve<data_type>().data("h");
+    auto data = entt::resolve<data_type>().data("h"_hs);
 
     ASSERT_TRUE(data);
-    ASSERT_EQ(data.parent(), entt::resolve("data"));
+    ASSERT_EQ(data.parent(), entt::resolve("data"_hs));
     ASSERT_EQ(data.type(), entt::resolve<int>());
-    ASSERT_STREQ(data.name(), "h");
+    ASSERT_EQ(data.identifier(), "h"_hs);
     ASSERT_FALSE(data.is_const());
     ASSERT_TRUE(data.is_static());
     ASSERT_EQ(data.get({}).cast<int>(), 2);
@@ -881,12 +881,12 @@ TEST_F(Meta, MetaDataStatic) {
 }
 
 TEST_F(Meta, MetaDataConstStatic) {
-    auto data = entt::resolve<data_type>().data("k");
+    auto data = entt::resolve<data_type>().data("k"_hs);
 
     ASSERT_TRUE(data);
-    ASSERT_EQ(data.parent(), entt::resolve("data"));
+    ASSERT_EQ(data.parent(), entt::resolve("data"_hs));
     ASSERT_EQ(data.type(), entt::resolve<int>());
-    ASSERT_STREQ(data.name(), "k");
+    ASSERT_EQ(data.identifier(), "k"_hs);
     ASSERT_TRUE(data.is_const());
     ASSERT_TRUE(data.is_static());
     ASSERT_EQ(data.get({}).cast<int>(), 3);
@@ -909,7 +909,7 @@ TEST_F(Meta, MetaDataConstStatic) {
 }
 
 TEST_F(Meta, MetaDataGetMetaAnyArg) {
-    auto data = entt::resolve<data_type>().data("i");
+    auto data = entt::resolve<data_type>().data("i"_hs);
     entt::meta_any any{data_type{}};
     any.cast<data_type>().i = 99;
     const auto value = data.get(any);
@@ -920,11 +920,11 @@ TEST_F(Meta, MetaDataGetMetaAnyArg) {
 }
 
 TEST_F(Meta, MetaDataGetInvalidArg) {
-    ASSERT_FALSE(entt::resolve<data_type>().data("i").get(0));
+    ASSERT_FALSE(entt::resolve<data_type>().data("i"_hs).get(0));
 }
 
 TEST_F(Meta, MetaDataSetMetaAnyArg) {
-    auto data = entt::resolve<data_type>().data("i");
+    auto data = entt::resolve<data_type>().data("i"_hs);
     entt::meta_any any{data_type{}};
     entt::meta_any value{42};
 
@@ -934,11 +934,11 @@ TEST_F(Meta, MetaDataSetMetaAnyArg) {
 }
 
 TEST_F(Meta, MetaDataSetInvalidArg) {
-    ASSERT_FALSE(entt::resolve<data_type>().data("i").set({}, 'c'));
+    ASSERT_FALSE(entt::resolve<data_type>().data("i"_hs).set({}, 'c'));
 }
 
 TEST_F(Meta, MetaDataSetCast) {
-    auto data = entt::resolve<data_type>().data("empty");
+    auto data = entt::resolve<data_type>().data("empty"_hs);
     data_type instance{};
 
     ASSERT_EQ(empty_type::counter, 0);
@@ -947,7 +947,7 @@ TEST_F(Meta, MetaDataSetCast) {
 }
 
 TEST_F(Meta, MetaDataSetConvert) {
-    auto data = entt::resolve<data_type>().data("i");
+    auto data = entt::resolve<data_type>().data("i"_hs);
     data_type instance{};
 
     ASSERT_EQ(instance.i, 0);
@@ -956,14 +956,14 @@ TEST_F(Meta, MetaDataSetConvert) {
 }
 
 TEST_F(Meta, MetaDataSetterGetterAsFreeFunctions) {
-    auto data = entt::resolve<setter_getter_type>().data("x");
+    auto data = entt::resolve<setter_getter_type>().data("x"_hs);
     setter_getter_type instance{};
 
     ASSERT_TRUE(data);
     ASSERT_NE(data, entt::meta_data{});
-    ASSERT_EQ(data.parent(), entt::resolve("setter_getter"));
+    ASSERT_EQ(data.parent(), entt::resolve("setter_getter"_hs));
     ASSERT_EQ(data.type(), entt::resolve<int>());
-    ASSERT_STREQ(data.name(), "x");
+    ASSERT_EQ(data.identifier(), "x"_hs);
     ASSERT_FALSE(data.is_const());
     ASSERT_FALSE(data.is_static());
     ASSERT_EQ(data.get(instance).cast<int>(), 0);
@@ -972,14 +972,14 @@ TEST_F(Meta, MetaDataSetterGetterAsFreeFunctions) {
 }
 
 TEST_F(Meta, MetaDataSetterGetterAsMemberFunctions) {
-    auto data = entt::resolve<setter_getter_type>().data("y");
+    auto data = entt::resolve<setter_getter_type>().data("y"_hs);
     setter_getter_type instance{};
 
     ASSERT_TRUE(data);
     ASSERT_NE(data, entt::meta_data{});
-    ASSERT_EQ(data.parent(), entt::resolve("setter_getter"));
+    ASSERT_EQ(data.parent(), entt::resolve("setter_getter"_hs));
     ASSERT_EQ(data.type(), entt::resolve<int>());
-    ASSERT_STREQ(data.name(), "y");
+    ASSERT_EQ(data.identifier(), "y"_hs);
     ASSERT_FALSE(data.is_const());
     ASSERT_FALSE(data.is_static());
     ASSERT_EQ(data.get(instance).cast<int>(), 0);
@@ -988,14 +988,14 @@ TEST_F(Meta, MetaDataSetterGetterAsMemberFunctions) {
 }
 
 TEST_F(Meta, MetaDataSetterGetterWithRefAsMemberFunctions) {
-    auto data = entt::resolve<setter_getter_type>().data("w");
+    auto data = entt::resolve<setter_getter_type>().data("w"_hs);
     setter_getter_type instance{};
 
     ASSERT_TRUE(data);
     ASSERT_NE(data, entt::meta_data{});
-    ASSERT_EQ(data.parent(), entt::resolve("setter_getter"));
+    ASSERT_EQ(data.parent(), entt::resolve("setter_getter"_hs));
     ASSERT_EQ(data.type(), entt::resolve<int>());
-    ASSERT_STREQ(data.name(), "w");
+    ASSERT_EQ(data.identifier(), "w"_hs);
     ASSERT_FALSE(data.is_const());
     ASSERT_FALSE(data.is_static());
     ASSERT_EQ(data.get(instance).cast<int>(), 0);
@@ -1004,14 +1004,14 @@ TEST_F(Meta, MetaDataSetterGetterWithRefAsMemberFunctions) {
 }
 
 TEST_F(Meta, MetaDataSetterGetterMixed) {
-    auto data = entt::resolve<setter_getter_type>().data("z");
+    auto data = entt::resolve<setter_getter_type>().data("z"_hs);
     setter_getter_type instance{};
 
     ASSERT_TRUE(data);
     ASSERT_NE(data, entt::meta_data{});
-    ASSERT_EQ(data.parent(), entt::resolve("setter_getter"));
+    ASSERT_EQ(data.parent(), entt::resolve("setter_getter"_hs));
     ASSERT_EQ(data.type(), entt::resolve<int>());
-    ASSERT_STREQ(data.name(), "z");
+    ASSERT_EQ(data.identifier(), "z"_hs);
     ASSERT_FALSE(data.is_const());
     ASSERT_FALSE(data.is_static());
     ASSERT_EQ(data.get(instance).cast<int>(), 0);
@@ -1020,7 +1020,7 @@ TEST_F(Meta, MetaDataSetterGetterMixed) {
 }
 
 TEST_F(Meta, MetaDataArrayStatic) {
-    auto data = entt::resolve<array_type>().data("global");
+    auto data = entt::resolve<array_type>().data("global"_hs);
 
     array_type::global[0] = 3;
     array_type::global[1] = 5;
@@ -1028,9 +1028,9 @@ TEST_F(Meta, MetaDataArrayStatic) {
 
     ASSERT_TRUE(data);
     ASSERT_NE(data, entt::meta_data{});
-    ASSERT_EQ(data.parent(), entt::resolve("array"));
+    ASSERT_EQ(data.parent(), entt::resolve("array"_hs));
     ASSERT_EQ(data.type(), entt::resolve<int[3]>());
-    ASSERT_STREQ(data.name(), "global");
+    ASSERT_EQ(data.identifier(), "global"_hs);
     ASSERT_FALSE(data.is_const());
     ASSERT_TRUE(data.is_static());
     ASSERT_TRUE(data.type().is_array());
@@ -1049,7 +1049,7 @@ TEST_F(Meta, MetaDataArrayStatic) {
 }
 
 TEST_F(Meta, MetaDataArray) {
-    auto data = entt::resolve<array_type>().data("local");
+    auto data = entt::resolve<array_type>().data("local"_hs);
     array_type instance;
 
     instance.local[0] = 3;
@@ -1058,9 +1058,9 @@ TEST_F(Meta, MetaDataArray) {
 
     ASSERT_TRUE(data);
     ASSERT_NE(data, entt::meta_data{});
-    ASSERT_EQ(data.parent(), entt::resolve("array"));
+    ASSERT_EQ(data.parent(), entt::resolve("array"_hs));
     ASSERT_EQ(data.type(), entt::resolve<int[3]>());
-    ASSERT_STREQ(data.name(), "local");
+    ASSERT_EQ(data.identifier(), "local"_hs);
     ASSERT_FALSE(data.is_const());
     ASSERT_FALSE(data.is_static());
     ASSERT_TRUE(data.type().is_array());
@@ -1079,13 +1079,13 @@ TEST_F(Meta, MetaDataArray) {
 }
 
 TEST_F(Meta, MetaFunc) {
-    auto func = entt::resolve<func_type>().func("f2");
+    auto func = entt::resolve<func_type>().func("f2"_hs);
     func_type instance{};
 
     ASSERT_TRUE(func);
     ASSERT_NE(func, entt::meta_func{});
-    ASSERT_EQ(func.parent(), entt::resolve("func"));
-    ASSERT_STREQ(func.name(), "f2");
+    ASSERT_EQ(func.parent(), entt::resolve("func"_hs));
+    ASSERT_EQ(func.identifier(), "f2"_hs);
     ASSERT_EQ(func.size(), entt::meta_func::size_type{2});
     ASSERT_FALSE(func.is_const());
     ASSERT_FALSE(func.is_static());
@@ -1119,12 +1119,12 @@ TEST_F(Meta, MetaFunc) {
 }
 
 TEST_F(Meta, MetaFuncConst) {
-    auto func = entt::resolve<func_type>().func("f1");
+    auto func = entt::resolve<func_type>().func("f1"_hs);
     func_type instance{};
 
     ASSERT_TRUE(func);
-    ASSERT_EQ(func.parent(), entt::resolve("func"));
-    ASSERT_STREQ(func.name(), "f1");
+    ASSERT_EQ(func.parent(), entt::resolve("func"_hs));
+    ASSERT_EQ(func.identifier(), "f1"_hs);
     ASSERT_EQ(func.size(), entt::meta_func::size_type{1});
     ASSERT_TRUE(func.is_const());
     ASSERT_FALSE(func.is_static());
@@ -1156,12 +1156,12 @@ TEST_F(Meta, MetaFuncConst) {
 }
 
 TEST_F(Meta, MetaFuncRetVoid) {
-    auto func = entt::resolve<func_type>().func("g");
+    auto func = entt::resolve<func_type>().func("g"_hs);
     func_type instance{};
 
     ASSERT_TRUE(func);
-    ASSERT_EQ(func.parent(), entt::resolve("func"));
-    ASSERT_STREQ(func.name(), "g");
+    ASSERT_EQ(func.parent(), entt::resolve("func"_hs));
+    ASSERT_EQ(func.identifier(), "g"_hs);
     ASSERT_EQ(func.size(), entt::meta_func::size_type{1});
     ASSERT_FALSE(func.is_const());
     ASSERT_FALSE(func.is_static());
@@ -1190,11 +1190,11 @@ TEST_F(Meta, MetaFuncRetVoid) {
 }
 
 TEST_F(Meta, MetaFuncStatic) {
-    auto func = entt::resolve<func_type>().func("h");
+    auto func = entt::resolve<func_type>().func("h"_hs);
 
     ASSERT_TRUE(func);
-    ASSERT_EQ(func.parent(), entt::resolve("func"));
-    ASSERT_STREQ(func.name(), "h");
+    ASSERT_EQ(func.parent(), entt::resolve("func"_hs));
+    ASSERT_EQ(func.identifier(), "h"_hs);
     ASSERT_EQ(func.size(), entt::meta_func::size_type{1});
     ASSERT_FALSE(func.is_const());
     ASSERT_TRUE(func.is_static());
@@ -1226,11 +1226,11 @@ TEST_F(Meta, MetaFuncStatic) {
 }
 
 TEST_F(Meta, MetaFuncStaticRetVoid) {
-    auto func = entt::resolve<func_type>().func("k");
+    auto func = entt::resolve<func_type>().func("k"_hs);
 
     ASSERT_TRUE(func);
-    ASSERT_EQ(func.parent(), entt::resolve("func"));
-    ASSERT_STREQ(func.name(), "k");
+    ASSERT_EQ(func.parent(), entt::resolve("func"_hs));
+    ASSERT_EQ(func.identifier(), "k"_hs);
     ASSERT_EQ(func.size(), entt::meta_func::size_type{1});
     ASSERT_FALSE(func.is_const());
     ASSERT_TRUE(func.is_static());
@@ -1259,7 +1259,7 @@ TEST_F(Meta, MetaFuncStaticRetVoid) {
 }
 
 TEST_F(Meta, MetaFuncMetaAnyArgs) {
-    auto func = entt::resolve<func_type>().func("f1");
+    auto func = entt::resolve<func_type>().func("f1"_hs);
     auto any = func.invoke(func_type{}, entt::meta_any{3});
 
     ASSERT_TRUE(any);
@@ -1268,12 +1268,12 @@ TEST_F(Meta, MetaFuncMetaAnyArgs) {
 }
 
 TEST_F(Meta, MetaFuncInvalidArgs) {
-    auto func = entt::resolve<func_type>().func("f1");
+    auto func = entt::resolve<func_type>().func("f1"_hs);
     ASSERT_FALSE(func.invoke(empty_type{}, entt::meta_any{'c'}));
 }
 
 TEST_F(Meta, MetaFuncCastAndConvert) {
-    auto func = entt::resolve<func_type>().func("f3");
+    auto func = entt::resolve<func_type>().func("f3"_hs);
     auto any = func.invoke(func_type{}, derived_type{}, 0, 3.);
 
     ASSERT_TRUE(any);
@@ -1286,7 +1286,7 @@ TEST_F(Meta, MetaType) {
 
     ASSERT_TRUE(type);
     ASSERT_NE(type, entt::meta_type{});
-    ASSERT_STREQ(type.name(), "derived");
+    ASSERT_EQ(type.identifier(), "derived"_hs);
 
     type.prop([](auto prop) {
         ASSERT_TRUE(prop);
@@ -1332,7 +1332,7 @@ TEST_F(Meta, MetaTypeBase) {
     });
 
     ASSERT_TRUE(iterate);
-    ASSERT_EQ(type.base("base").type(), entt::resolve<base_type>());
+    ASSERT_EQ(type.base("base"_hs).type(), entt::resolve<base_type>());
 }
 
 TEST_F(Meta, MetaTypeConv) {
@@ -1379,7 +1379,7 @@ TEST_F(Meta, MetaTypeData) {
     });
 
     ASSERT_EQ(counter, 5);
-    ASSERT_TRUE(type.data("i"));
+    ASSERT_TRUE(type.data("i"_hs));
 }
 
 TEST_F(Meta, MetaTypeFunc) {
@@ -1391,7 +1391,7 @@ TEST_F(Meta, MetaTypeFunc) {
     });
 
     ASSERT_EQ(counter, 6);
-    ASSERT_TRUE(type.func("f1"));
+    ASSERT_TRUE(type.func("f1"_hs));
 }
 
 TEST_F(Meta, MetaTypeConstruct) {
@@ -1475,13 +1475,13 @@ TEST_F(Meta, MetaDataFromBase) {
     auto type = entt::resolve<concrete_type>();
     concrete_type instance;
 
-    ASSERT_TRUE(type.data("i"));
-    ASSERT_TRUE(type.data("j"));
+    ASSERT_TRUE(type.data("i"_hs));
+    ASSERT_TRUE(type.data("j"_hs));
 
     ASSERT_EQ(instance.i, 0);
     ASSERT_EQ(instance.j, char{});
-    ASSERT_TRUE(type.data("i").set(instance, 3));
-    ASSERT_TRUE(type.data("j").set(instance, 'c'));
+    ASSERT_TRUE(type.data("i"_hs).set(instance, 3));
+    ASSERT_TRUE(type.data("j"_hs).set(instance, 'c'));
     ASSERT_EQ(instance.i, 3);
     ASSERT_EQ(instance.j, 'c');
 }
@@ -1491,24 +1491,24 @@ TEST_F(Meta, MetaFuncFromBase) {
     auto base = entt::resolve<an_abstract_type>();
     concrete_type instance;
 
-    ASSERT_TRUE(type.func("f"));
-    ASSERT_TRUE(type.func("g"));
-    ASSERT_TRUE(type.func("h"));
+    ASSERT_TRUE(type.func("f"_hs));
+    ASSERT_TRUE(type.func("g"_hs));
+    ASSERT_TRUE(type.func("h"_hs));
 
-    ASSERT_EQ(type.func("f").parent(), entt::resolve<concrete_type>());
-    ASSERT_EQ(type.func("g").parent(), entt::resolve<an_abstract_type>());
-    ASSERT_EQ(type.func("h").parent(), entt::resolve<another_abstract_type>());
+    ASSERT_EQ(type.func("f"_hs).parent(), entt::resolve<concrete_type>());
+    ASSERT_EQ(type.func("g"_hs).parent(), entt::resolve<an_abstract_type>());
+    ASSERT_EQ(type.func("h"_hs).parent(), entt::resolve<another_abstract_type>());
 
     ASSERT_EQ(instance.i, 0);
     ASSERT_EQ(instance.j, char{});
 
-    type.func("f").invoke(instance, 3);
-    type.func("h").invoke(instance, 'c');
+    type.func("f"_hs).invoke(instance, 3);
+    type.func("h"_hs).invoke(instance, 'c');
 
     ASSERT_EQ(instance.i, 9);
     ASSERT_EQ(instance.j, 'c');
 
-    base.func("g").invoke(instance, 3);
+    base.func("g"_hs).invoke(instance, 3);
 
     ASSERT_EQ(instance.i, -3);
 }
@@ -1531,11 +1531,11 @@ TEST_F(Meta, AbstractClass) {
 
     ASSERT_EQ(instance.i, 0);
 
-    type.func("f").invoke(instance, 3);
+    type.func("f"_hs).invoke(instance, 3);
 
     ASSERT_EQ(instance.i, 3);
 
-    type.func("g").invoke(instance, 3);
+    type.func("g"_hs).invoke(instance, 3);
 
     ASSERT_EQ(instance.i, -3);
 }
@@ -1543,33 +1543,33 @@ TEST_F(Meta, AbstractClass) {
 TEST_F(Meta, EnumAndNamedConstants) {
     auto type = entt::resolve<properties>();
 
-    ASSERT_TRUE(type.data("prop_bool"));
-    ASSERT_TRUE(type.data("prop_int"));
+    ASSERT_TRUE(type.data("prop_bool"_hs));
+    ASSERT_TRUE(type.data("prop_int"_hs));
 
-    ASSERT_EQ(type.data("prop_bool").type(), type);
-    ASSERT_EQ(type.data("prop_int").type(), type);
+    ASSERT_EQ(type.data("prop_bool"_hs).type(), type);
+    ASSERT_EQ(type.data("prop_int"_hs).type(), type);
 
-    ASSERT_FALSE(type.data("prop_bool").set({}, properties::prop_int));
-    ASSERT_FALSE(type.data("prop_int").set({}, properties::prop_bool));
+    ASSERT_FALSE(type.data("prop_bool"_hs).set({}, properties::prop_int));
+    ASSERT_FALSE(type.data("prop_int"_hs).set({}, properties::prop_bool));
 
-    ASSERT_EQ(type.data("prop_bool").get({}).cast<properties>(), properties::prop_bool);
-    ASSERT_EQ(type.data("prop_int").get({}).cast<properties>(), properties::prop_int);
+    ASSERT_EQ(type.data("prop_bool"_hs).get({}).cast<properties>(), properties::prop_bool);
+    ASSERT_EQ(type.data("prop_int"_hs).get({}).cast<properties>(), properties::prop_int);
 }
 
 TEST_F(Meta, ArithmeticTypeAndNamedConstants) {
     auto type = entt::resolve<unsigned int>();
 
-    ASSERT_TRUE(type.data("min"));
-    ASSERT_TRUE(type.data("max"));
+    ASSERT_TRUE(type.data("min"_hs));
+    ASSERT_TRUE(type.data("max"_hs));
 
-    ASSERT_EQ(type.data("min").type(), type);
-    ASSERT_EQ(type.data("max").type(), type);
+    ASSERT_EQ(type.data("min"_hs).type(), type);
+    ASSERT_EQ(type.data("max"_hs).type(), type);
 
-    ASSERT_FALSE(type.data("min").set({}, 100u));
-    ASSERT_FALSE(type.data("max").set({}, 0u));
+    ASSERT_FALSE(type.data("min"_hs).set({}, 100u));
+    ASSERT_FALSE(type.data("max"_hs).set({}, 0u));
 
-    ASSERT_EQ(type.data("min").get({}).cast<unsigned int>(), 0u);
-    ASSERT_EQ(type.data("max").get({}).cast<unsigned int>(), 100u);
+    ASSERT_EQ(type.data("min"_hs).get({}).cast<unsigned int>(), 0u);
+    ASSERT_EQ(type.data("max"_hs).get({}).cast<unsigned int>(), 100u);
 }
 
 TEST_F(Meta, Unregister) {
@@ -1588,17 +1588,17 @@ TEST_F(Meta, Unregister) {
     entt::unregister<another_abstract_type>();
     entt::unregister<concrete_type>();
 
-    ASSERT_FALSE(entt::resolve("char"));
-    ASSERT_FALSE(entt::resolve("base"));
-    ASSERT_FALSE(entt::resolve("derived"));
-    ASSERT_FALSE(entt::resolve("empty"));
-    ASSERT_FALSE(entt::resolve("fat"));
-    ASSERT_FALSE(entt::resolve("data"));
-    ASSERT_FALSE(entt::resolve("func"));
-    ASSERT_FALSE(entt::resolve("setter_getter"));
-    ASSERT_FALSE(entt::resolve("an_abstract_type"));
-    ASSERT_FALSE(entt::resolve("another_abstract_type"));
-    ASSERT_FALSE(entt::resolve("concrete"));
+    ASSERT_FALSE(entt::resolve("char"_hs));
+    ASSERT_FALSE(entt::resolve("base"_hs));
+    ASSERT_FALSE(entt::resolve("derived"_hs));
+    ASSERT_FALSE(entt::resolve("empty"_hs));
+    ASSERT_FALSE(entt::resolve("fat"_hs));
+    ASSERT_FALSE(entt::resolve("data"_hs));
+    ASSERT_FALSE(entt::resolve("func"_hs));
+    ASSERT_FALSE(entt::resolve("setter_getter"_hs));
+    ASSERT_FALSE(entt::resolve("an_abstract_type"_hs));
+    ASSERT_FALSE(entt::resolve("another_abstract_type"_hs));
+    ASSERT_FALSE(entt::resolve("concrete"_hs));
 
     Meta::SetUpAfterUnregistration();
     entt::meta_any any{42.};
@@ -1607,8 +1607,8 @@ TEST_F(Meta, Unregister) {
     ASSERT_FALSE(any.can_convert<int>());
     ASSERT_TRUE(any.can_convert<float>());
 
-    ASSERT_FALSE(entt::resolve("derived"));
-    ASSERT_TRUE(entt::resolve("my_type"));
+    ASSERT_FALSE(entt::resolve("derived"_hs));
+    ASSERT_TRUE(entt::resolve("my_type"_hs));
 
     entt::resolve<derived_type>().prop([](auto prop) {
         ASSERT_TRUE(prop);
@@ -1619,9 +1619,9 @@ TEST_F(Meta, Unregister) {
     ASSERT_FALSE((entt::resolve<derived_type>().ctor<const base_type &, int, char>()));
     ASSERT_TRUE((entt::resolve<derived_type>().ctor<>()));
 
-    ASSERT_TRUE(entt::resolve("your_type").data("a_data_member"));
-    ASSERT_FALSE(entt::resolve("your_type").data("another_data_member"));
+    ASSERT_TRUE(entt::resolve("your_type"_hs).data("a_data_member"_hs));
+    ASSERT_FALSE(entt::resolve("your_type"_hs).data("another_data_member"_hs));
 
-    ASSERT_TRUE(entt::resolve("your_type").func("a_member_function"));
-    ASSERT_FALSE(entt::resolve("your_type").func("another_member_function"));
+    ASSERT_TRUE(entt::resolve("your_type"_hs).func("a_member_function"_hs));
+    ASSERT_FALSE(entt::resolve("your_type"_hs).func("another_member_function"_hs));
 }

+ 1 - 0
test/entt/resource/resource.cpp

@@ -1,5 +1,6 @@
 #include <type_traits>
 #include <gtest/gtest.h>
+#include <entt/core/hashed_string.hpp>
 #include <entt/resource/cache.hpp>
 
 struct resource { int value; };