Browse Source

Add support for member to name reverse conversion.

Sam Jaffe 6 years ago
parent
commit
d0c00163e7
2 changed files with 90 additions and 40 deletions
  1. 74 40
      include/reflect/reflect.hpp
  2. 16 0
      test/reflect_test.cpp

+ 74 - 40
include/reflect/reflect.hpp

@@ -5,50 +5,84 @@
 
 template <typename Object>
 class reflection {
-  private:
-    template <typename T>
-    using Field = T Object::*;
-  public:
-    reflection();
+private:
+  template <typename T> using Field = T Object::*;
+  template <typename K, typename V> using map = std::unordered_map<K, V>;
   
-    template <typename T>
-    static bool exists( std::string const & name ) {
-      return s_reflector<T>.count( name );
-    }
-
-    template <typename T>
-    static Field<T> get_pointer( std::string const & name ) {
-      auto it = s_reflector<T>.find( name );
-      if ( it == s_reflector<T>.end() ) { return nullptr; }
-      return it->second;
-    }
-
-    template <typename T>
-    static void bind( Field<T> field, std::string const & name, std::string const & /*discard*/ = "" ) {
-      s_reflector<T>.reserve( s_reflector<T>.size() + 1 );
-      s_reflector<T>.emplace( name, field );
-    }
-  private:
-    template <typename T>
-    static std::unordered_map<std::string, Field<T>> s_reflector;
+private:
+  template <typename T> static map<std::string, Field<T>> s_reflector;
+  static map<std::ptrdiff_t, std::string> s_fieldname;
+  
+private:
+  template <typename T>
+  static std::ptrdiff_t offset(Field<T> field) {
+    return std::ptrdiff_t(&(reinterpret_cast<Object const*>(NULL)->*field));
+  }
+  
+  static std::string_view name(std::ptrdiff_t offset) {
+    auto it = s_fieldname.find(offset);
+    if (it == s_fieldname.end()) { return ""; }
+    return it->second;
+  }
+
+public:
+  reflection();
+
+  template <typename T>
+  static bool exists(std::string const & name) {
+    return s_reflector<T>.count(name);
+  }
+
+  template <typename T>
+  static Field<T> get_pointer(std::string const & name) {
+    auto it = s_reflector<T>.find(name);
+    if (it == s_reflector<T>.end()) { return nullptr; }
+    return it->second;
+  }
+  
+  template <typename T>
+  static std::string_view name(Object const * ptr, T const & data) {
+    return name(std::ptrdiff_t(&data) - std::ptrdiff_t(ptr));
+  }
+  
+  template <typename T>
+  static std::string_view name(Object const & obj, T const & data) {
+    return name(&obj, data);
+  }
+  
+  template <typename T>
+  static std::string_view name(Field<T> field) {
+    return name(offset(field));
+  }
+
+  template <typename T>
+  static void bind(Field<T> field, std::string const & name,
+                   std::string const & /*discard*/ = "") {
+    s_fieldname.emplace(offset(field), name);
+    s_reflector<T>.emplace(name, field);
+  }
 };
 
-#define REFLECT_MEMBER_PP_IMPL2( type, field, ... ) bind( &type::field, ##__VA_ARGS__, #field )
-#define REFLECT_MEMBER_PP_IMPL( type, field, ... ) REFLECT_MEMBER_PP_IMPL2( type, field, ##__VA_ARGS__ )
-#define UNWRAP( ... ) __VA_ARGS__
-#define REFLECT_MEMBER_PP2( data, elem ) REFLECT_MEMBER_PP_IMPL( data, UNWRAP elem )
-#define REFLECT_MEMBER_PP( r, data, elem ) REFLECT_MEMBER_PP2( data, elem );
+#define REFLECT_MEMBER_PP_IMPL2(type, field, ...) \
+  bind(&type::field, ##__VA_ARGS__, #field)
+#define REFLECT_MEMBER_PP_IMPL(type, field, ...) \
+  REFLECT_MEMBER_PP_IMPL2(type, field, ##__VA_ARGS__)
+#define UNWRAP(...) __VA_ARGS__
+#define REFLECT_MEMBER_PP2(data, elem) REFLECT_MEMBER_PP_IMPL(data, UNWRAP elem)
+#define REFLECT_MEMBER_PP(r, data, elem) REFLECT_MEMBER_PP2(data, elem);
 
 #include <boost/preprocessor/variadic/to_seq.hpp>
 #include <boost/preprocessor/seq/for_each.hpp>
-#define CONCAT2( A, B ) A##B
-#define CONCAT( A, B ) CONCAT2( A, B )
-#define CREATE_REFLECTION( type, ... ) \
-  template <> reflection<type>::reflection() { \
-    BOOST_PP_SEQ_FOR_EACH( REFLECT_MEMBER_PP, type, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__) ) \
-  } \
-  static reflection<type> CONCAT( reflector_, __LINE__ ) {}
-
-template <typename Object> 
-template <typename T>
+#define CONCAT2(A, B) A##B
+#define CONCAT(A, B) CONCAT2(A, B)
+#define CREATE_REFLECTION(type, ...)                             \
+  template <> reflection<type>::reflection() {                   \
+    BOOST_PP_SEQ_FOR_EACH(REFLECT_MEMBER_PP, type,               \
+                          BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \
+  }                                                              \
+  static reflection<type> CONCAT(reflector_, __LINE__) {}
+
+template <typename Object> template <typename T>
 std::unordered_map<std::string, T Object::*> reflection<Object>::s_reflector;
+template <typename Object>
+std::unordered_map<std::ptrdiff_t, std::string> reflection<Object>::s_fieldname;

+ 16 - 0
test/reflect_test.cpp

@@ -30,3 +30,19 @@ TEST(ReflectionTest, CanAccessMember) {
   EXPECT_THAT(&(ex.*p), Eq(&ex.a));
   EXPECT_THAT(ex.*p, Eq(5));
 }
+
+TEST(ReflectionTest, CanGetNameFromMemPtr) {
+  EXPECT_THAT(reflection<example>::name(&example::a), Eq("a"));
+  EXPECT_THAT(reflection<example>::name(&example::b), Eq("c"));
+}
+
+TEST(ReflectionTest, CanGetNameFromThis) {
+  example ex = { 5, 6 };
+  EXPECT_THAT(reflection<example>::name(ex, ex.a), Eq("a"));
+  EXPECT_THAT(reflection<example>::name(ex, ex.b), Eq("c"));
+}
+
+TEST(ReflectionTest, NameForMissingIsEmpty) {
+  example ex = { 5, 6 };
+  EXPECT_THAT(reflection<example>::name(ex, 5), Eq(""));
+}