瀏覽代碼

meta: full support for constant values and enums

Michele Caini 7 年之前
父節點
當前提交
9c164e1cea
共有 4 個文件被更改,包括 116 次插入27 次删除
  1. 0 1
      TODO
  2. 44 5
      docs/meta.md
  3. 40 21
      src/entt/meta/factory.hpp
  4. 32 0
      test/entt/meta/meta.cpp

+ 0 - 1
TODO

@@ -14,7 +14,6 @@
 * work stealing job system (see #100)
 * composable looper so as to pack erased systems, compose runners at different rates and run them at once in the loop
 * meta: sort of meta view based on meta stuff to iterate entities, void * and meta info objects
-* meta: move to head elements and forms LRU lists
 * registry::probe<component>(entt) (returns a component * if entt has the component, nullptr otherwise)
 * hashed string: add implicit check on construction for uniqueness (optional)
 * add a note about multithreading support to the README file

+ 44 - 5
docs/meta.md

@@ -9,6 +9,7 @@
 * [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)
 <!--
 @endcond TURN_OFF_DOXYGEN
@@ -69,10 +70,10 @@ It can be used to extend the reflected type and add the following:
   entt::reflect<my_type>("reflected").dtor<&destroy>();
   ```
 
-* _Data members_. Both real data members of the underlying type and static or
-  global variables can be attached to a meta type. From a client's point of
-  view, all the variables associated with the reflected type will appear as if
-  they were part of the type.<br/>
+* _Data members_. Both real data members of the underlying type and static and
+  global variables, as well as constants of any kind, can be attached to a meta
+  type. From a client's point of view, all the variables associated with the
+  reflected type will appear as if they were part of the type itself.<br/>
   Use the `data` member function for this purpose:
 
   ```cpp
@@ -89,7 +90,7 @@ It can be used to extend the reflected type and add the following:
 * _Member functions_. Both real member functions of the underlying type and free
   functions can be attached to a meta type. From a client's point of view, all
   the functions associated with the reflected type will appear as if they were
-  part of the type.<br/>
+  part of the type itself.<br/>
   Use the `func` member function for this purpose:
 
   ```cpp
@@ -317,6 +318,44 @@ 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
+
+A special mention should be made for constant values and enums. It wouldn't be
+necessary, but it will help distracted readers.
+
+As mentioned, the `data` member function can be used to reflect constants of any
+type among the other things.<br/>
+This allows users to create meta types for enums that will work exactly like any
+other meta type built from a class. Similarly, arithmetic types can be enriched
+with constants of special meaning where required.<br/>
+Personally, I find it very useful not to export what is the difference between
+enums and classes in C++ directly in the space of the reflected types.
+
+All the values thus exported will appear to users as if they were constant data
+members of the reflected types.
+
+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");
+
+entt::reflect<int>().data<2048>("max_int");
+```
+
+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>();
+```
+
+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
 
 Sometimes (ie when it comes to creating an editor) it might be useful to be able

+ 40 - 21
src/entt/meta/factory.hpp

@@ -40,7 +40,7 @@ class meta_factory {
     actual_type();
 
     template<auto Data>
-    static std::enable_if_t<!std::is_member_object_pointer_v<decltype(Data)>, decltype(*Data)>
+    static std::enable_if_t<std::is_pointer_v<decltype(Data)>, decltype(*Data)>
     actual_type();
 
     template<auto Data>
@@ -284,10 +284,10 @@ public:
     /**
      * @brief Assigns a meta data to a meta type.
      *
-     * Both data members and static or global variables can be assigned to a
-     * meta type.<br/>
+     * Both data members and static and global variables, as well as constants
+     * of any kind, can be assigned to a meta type.<br/>
      * From a client's point of view, all the variables associated with the
-     * reflected object will appear as if they were part of the type.
+     * reflected object will appear as if they were part of the type itself.
      *
      * @tparam Data The actual variable to attach to the meta type.
      * @tparam Property Types of properties to assign to the meta data.
@@ -299,22 +299,41 @@ public:
     meta_factory & data(const char *str, Property &&... property) ENTT_NOEXCEPT {
         auto * const type = internal::meta_info<Type>::resolve();
 
-        static internal::meta_data_node node{
-            hashed_string{str},
-            type->data,
-            type,
-            properties<std::integral_constant<decltype(Data), Data>>(std::forward<Property>(property)...),
-            std::is_const_v<data_type<Data>>,
-            !std::is_member_object_pointer_v<decltype(Data)>,
-            &internal::meta_info<data_type<Data>>::resolve,
-            &internal::setter<std::is_const_v<data_type<Data>>, Type, Data>,
-            &internal::getter<Type, Data>
-        };
-
-        assert(!duplicate(hashed_string{str}, node.next));
-        assert((!internal::meta_info<Type>::template data<Data>));
-        internal::meta_info<Type>::template data<Data> = &node;
-        type->data = &node;
+        if constexpr(std::is_same_v<Type, decltype(Data)>) {
+            static internal::meta_data_node node{
+                hashed_string{str},
+                type->data,
+                type,
+                properties<std::integral_constant<Type, Data>>(std::forward<Property>(property)...),
+                true,
+                true,
+                &internal::meta_info<Type>::resolve,
+                [](meta_handle, meta_any &) { return false; },
+                [](meta_handle) -> meta_any { return Data; }
+            };
+
+            assert(!duplicate(hashed_string{str}, node.next));
+            assert((!internal::meta_info<Type>::template data<Data>));
+            internal::meta_info<Type>::template data<Data> = &node;
+            type->data = &node;
+        } else {
+            static internal::meta_data_node node{
+                hashed_string{str},
+                type->data,
+                type,
+                properties<std::integral_constant<decltype(Data), Data>>(std::forward<Property>(property)...),
+                std::is_const_v<data_type<Data>>,
+                !std::is_member_object_pointer_v<decltype(Data)>,
+                &internal::meta_info<data_type<Data>>::resolve,
+                &internal::setter<std::is_const_v<data_type<Data>>, Type, Data>,
+                &internal::getter<Type, Data>
+            };
+
+            assert(!duplicate(hashed_string{str}, node.next));
+            assert((!internal::meta_info<Type>::template data<Data>));
+            internal::meta_info<Type>::template data<Data> = &node;
+            type->data = &node;
+        }
 
         return *this;
     }
@@ -325,7 +344,7 @@ public:
      * Both member functions and free functions can be assigned to a meta
      * type.<br/>
      * From a client's point of view, all the functions associated with the
-     * reflected object will appear as if they were part of the type.
+     * reflected object will appear as if they were part of the type itself.
      *
      * @tparam Func The actual function to attach to the meta type.
      * @tparam Property Types of properties to assign to the meta function.

+ 32 - 0
test/entt/meta/meta.cpp

@@ -106,6 +106,12 @@ struct Meta: public ::testing::Test {
 
         entt::reflect<char>("char", std::make_pair(properties::prop_int, 42));
 
+        entt::reflect<properties>()
+                .data<properties::prop_bool>("prop_bool")
+                .data<properties::prop_int>("prop_int");
+
+        entt::reflect<unsigned int>().data<0u>("min").data<100u>("max");
+
         entt::reflect<base_type>("base");
 
         entt::reflect<derived_type>("derived", std::make_pair(properties::prop_int, 99))
@@ -1323,3 +1329,29 @@ TEST_F(Meta, AbstractClass) {
 
     ASSERT_EQ(instance.i, -3);
 }
+
+TEST_F(Meta, EnumAndNamedConstants) {
+    auto type = entt::resolve<properties>();
+
+    ASSERT_TRUE(type.data("prop_bool"));
+    ASSERT_TRUE(type.data("prop_int"));
+
+    ASSERT_EQ(type.data("prop_bool").type(), type);
+    ASSERT_EQ(type.data("prop_int").type(), type);
+
+    ASSERT_EQ(type.data("prop_bool").get({}).cast<properties>(), properties::prop_bool);
+    ASSERT_EQ(type.data("prop_int").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_EQ(type.data("min").type(), type);
+    ASSERT_EQ(type.data("max").type(), type);
+
+    ASSERT_EQ(type.data("min").get({}).cast<unsigned int>(), 0u);
+    ASSERT_EQ(type.data("max").get({}).cast<unsigned int>(), 100u);
+}