TransWikia.com

Yet another reflection library

Code Review Asked by Vladyslav Mozhvylo on November 8, 2021

Tried to do my own implementation of reflection(introspection) for using in my next projects.

Is it optimized at compile time? If no, how can I improve it?

This macro is good interface for adapting structures for reflection?

What features can(should) be added to make reflection(introspection) more useful?

Godbolt

Macros:

#include <tuple>
#include <iostream>
#include <string_view>
#include <boost/preprocessor/comparison/equal.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/seq/subseq.hpp>
#include <boost/preprocessor/seq/to_tuple.hpp>
#include <boost/preprocessor/seq/transform.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/tuple/pop_front.hpp>
#include <boost/preprocessor/variadic/to_seq.hpp>
#include <boost/vmd/is_tuple.hpp>

namespace reflection {
  template<typename T>
  struct meta {
  };
}

namespace {
  template<typename T>
  constexpr bool is_sorted(const std::initializer_list<T> &il) {
    for (auto it = il.begin(); it != il.end() - 1; it++) {
      if (*(it + 1) < *it) {
        return false;
      }
    }
    return true;
  }
}// namespace

#define REFLECTION_ADAPT_STRUCT_ARGS(...) __VA_ARGS__
#define REFLECTION_ADAPT_STRUCT_STRIP_PARENTHESES(X) X
#define REFLECTION_ADAPT_STRUCT_PASS_PARAMETERS(X) REFLECTION_ADAPT_STRUCT_STRIP_PARENTHESES(REFLECTION_ADAPT_STRUCT_ARGS X)

#define REFLECTION_ADAPT_STRUCT_NAME(info) 
  BOOST_PP_IF(BOOST_VMD_IS_TUPLE(info), BOOST_PP_IF(BOOST_VMD_IS_TUPLE(BOOST_PP_TUPLE_ELEM(0, info)), BOOST_PP_TUPLE_ELEM(0, BOOST_PP_TUPLE_ELEM(0, info)), BOOST_PP_TUPLE_ELEM(0, info)), info)

#define REFLECTION_ADAPT_STRUCT_BASE(info) 
  BOOST_PP_IF(BOOST_VMD_IS_TUPLE(info), BOOST_PP_IF(BOOST_VMD_IS_TUPLE(BOOST_PP_TUPLE_ELEM(0, info)), BOOST_PP_IF(BOOST_PP_GREATER(BOOST_PP_TUPLE_SIZE(BOOST_PP_TUPLE_ELEM(0, info)), 1), BOOST_PP_TUPLE_ELEM(1, BOOST_PP_TUPLE_ELEM(0, info)), void), void), void)

#define REFLECTION_ADAPT_STRUCT_ATTRIBUTES(info) 
  BOOST_PP_IF(BOOST_VMD_IS_TUPLE(info), BOOST_PP_IF(BOOST_PP_GREATER(BOOST_PP_TUPLE_SIZE(info), 1), BOOST_PP_TUPLE_POP_FRONT(info), ()), ())

#define REFLECTION_ADAPT_STRUCT_FIELDS_TRANSFORM(s, data, element) 
  BOOST_PP_CAT(field_, REFLECTION_ADAPT_STRUCT_NAME(element)())

#define REFLECTION_ADAPT_STRUCT_FIELDS(info) 
  BOOST_PP_SEQ_TO_TUPLE(BOOST_PP_SEQ_TRANSFORM(REFLECTION_ADAPT_STRUCT_FIELDS_TRANSFORM, _, BOOST_PP_SEQ_SUBSEQ(info, 1, BOOST_PP_DEC(BOOST_PP_SEQ_SIZE(info)))))

#define REFLECTION_ADAPT_STRUCT_FIELD_OFFSETS_TRANSFORM(s, data, element) 
  offsetof(data, REFLECTION_ADAPT_STRUCT_NAME(element))

#define REFLECTION_ADAPT_STRUCT_FIELD_OFFSETS(info) 
  BOOST_PP_SEQ_TO_TUPLE(BOOST_PP_SEQ_TRANSFORM(REFLECTION_ADAPT_STRUCT_FIELD_OFFSETS_TRANSFORM, REFLECTION_ADAPT_STRUCT_NAME(BOOST_PP_SEQ_ELEM(0, info)), BOOST_PP_SEQ_SUBSEQ(info, 1, BOOST_PP_DEC(BOOST_PP_SEQ_SIZE(info)))))

#define REFLECTION_ADAPT_STRUCT_HEADER(info)                                                                  
  template<>                                                                                                  
  struct reflection::meta<REFLECTION_ADAPT_STRUCT_NAME(info)> {                                               
    static constexpr std::string_view name = BOOST_PP_STRINGIZE(REFLECTION_ADAPT_STRUCT_NAME(info));          
    using type = REFLECTION_ADAPT_STRUCT_NAME(info);                                                          
    using base = REFLECTION_ADAPT_STRUCT_BASE(info);                                                          
    static_assert(std::is_same_v<base, void> || std::is_base_of_v<base, REFLECTION_ADAPT_STRUCT_NAME(info)>); 
    static constexpr auto attributes = std::make_tuple(REFLECTION_ADAPT_STRUCT_PASS_PARAMETERS(REFLECTION_ADAPT_STRUCT_ATTRIBUTES(info)));

#define REFLECTION_ADAPT_STRUCT_FIELD(struct_name, info)                                                                                   
  struct BOOST_PP_CAT(field_, REFLECTION_ADAPT_STRUCT_NAME(info)) {                                                                        
    static constexpr std::string_view name = BOOST_PP_STRINGIZE(REFLECTION_ADAPT_STRUCT_NAME(info));                                       
    using type = decltype(struct_name::REFLECTION_ADAPT_STRUCT_NAME(info));                                                                                       
    static constexpr auto reference = &struct_name::REFLECTION_ADAPT_STRUCT_NAME(info);                                                    
    static constexpr auto attributes = std::make_tuple(REFLECTION_ADAPT_STRUCT_PASS_PARAMETERS(REFLECTION_ADAPT_STRUCT_ATTRIBUTES(info))); 
  };

#define REFLECTION_ADAPT_STRUCT_FOOTER(info)                                                                        
  static constexpr auto fields = std::make_tuple REFLECTION_ADAPT_STRUCT_FIELDS(info);                              
  static_assert(is_sorted({REFLECTION_ADAPT_STRUCT_PASS_PARAMETERS(REFLECTION_ADAPT_STRUCT_FIELD_OFFSETS(info))})); 
  }                                                                                                                 
  ;

#define REFLECTION_ADAPT_STRUCT_I(r, data, index, info) 
  BOOST_PP_IF(BOOST_PP_EQUAL(index, 0), REFLECTION_ADAPT_STRUCT_HEADER(info), REFLECTION_ADAPT_STRUCT_FIELD(data, info))

#define REFLECTION_ADAPT_STRUCT(...)                                                                                                                             
  BOOST_PP_SEQ_FOR_EACH_I(REFLECTION_ADAPT_STRUCT_I, REFLECTION_ADAPT_STRUCT_NAME(BOOST_PP_VARIADIC_ELEM_0(__VA_ARGS__)), BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) 
  REFLECTION_ADAPT_STRUCT_FOOTER(BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))

Reflection:

namespace {
  template<typename Tuple, typename F, std::size_t... Indices>
  constexpr void for_each_impl(Tuple &&tuple, F &&f, std::index_sequence<Indices...>) noexcept {
    (f(std::get<Indices>(std::forward<Tuple>(tuple))), ...);
  }

  template<typename Tuple, typename F>
  constexpr void for_each(Tuple &&tuple, F &&f) noexcept {
    const auto N = std::tuple_size<std::remove_reference_t<Tuple>>::value;
    for_each_impl(std::forward<Tuple>(tuple), std::forward<F>(f), std::make_index_sequence<N>{});
  }

  template <typename T, typename Tuple>
  struct has_type : std::false_type {};

  template <typename T, typename... Tuple>
  struct has_type<T, std::tuple<Tuple...>> : std::disjunction<std::is_same<T, Tuple>...> {};
}// namespace

namespace reflection {
  template<typename M, typename F>
  constexpr void meta_for_each_field(F &&f) noexcept {
    for_each(
      M::fields, [&f](const auto &field) constexpr noexcept {
        f(field);
      });
  }

  template<typename F>
  constexpr const auto meta_name = F::name;

  template<typename F>
  using meta__type = typename F::type;

  template<typename F>
  constexpr const auto &meta_attributes = F::attributes;

  template<typename F, typename T>
  constexpr const auto &meta_attribute = std::get<T>(meta_attributes<F>);

  template<typename F, typename T>
  constexpr auto meta_has_attribute = has_type<T, std::decay_t<decltype(meta_attributes<F>)>>::value;

  template<typename F>
  constexpr auto &meta_value(auto &&t) noexcept {
    return t.*F::reference;
  }
}// namespace reflection

Usage:

struct A {
  int a;
};

struct B : A {
  std::string s;
  bool b;
  float f;
  float f1;
};

struct Attr1 {
  std::string_view data;
};

struct Attr2 {
  bool data;
};

REFLECTION_ADAPT_STRUCT(
  ((B, A), Attr1{"test"}),
  s,
  b,
  (f, Attr2{true}),
  (f1)
)

int main() {
  B b;
  b.a = 42;
  b.s = "hello";
  b.b = true;
  b.f = 42.42f;
  b.f1 = -42;

  using meta_B = reflection::meta<B>;

  std::cout << reflection::meta_name<meta_B> << std::endl;

  if constexpr (reflection::meta_has_attribute<meta_B, Attr1>) {
    std::cout << "Attr1 " << reflection::meta_attribute<meta_B, Attr1>.data << std::endl;
  }

  reflection::meta_for_each_field<meta_B>([&b]<typename F>(F) constexpr {
    if constexpr (reflection::meta_has_attribute<F, Attr2>) {
      std::cout << "Attr2 " << reflection::meta_attribute<F, Attr2>.data << std::endl;
    }

    std::cout << reflection::meta_name<F> << " " << reflection::meta_value<F>(b) << std::endl;
  });
}

Output:

B
Attr1 test
s hello
b 1
Attr2 1
f 42.42
f1 -42

REFLECTION_ADAPT_STRUCT in this case will expand to

template<>
struct reflection::meta<B> {
  static constexpr std::string_view name = "B";
  using type = B;
  using base = A;
  static_assert(std::is_same_v<base, void> || std::is_base_of_v<base, B>);
  static constexpr auto attributes = std::make_tuple(Attr1{"test"});
  struct field_s {
    static constexpr std::string_view name = "s";
    using type = decltype(B::s);
    static constexpr auto reference = &B::s;
    static constexpr auto attributes = std::make_tuple();
  };
  struct field_b {
    static constexpr std::string_view name = "b";
    using type = decltype(B::b);
    static constexpr auto reference = &B::b;
    static constexpr auto attributes = std::make_tuple();
  };
  struct field_f {
    static constexpr std::string_view name = "f";
    using type = decltype(B::f);
    static constexpr auto reference = &B::f;
    static constexpr auto attributes = std::make_tuple(Attr2{true});
  };
  struct field_f1 {
    static constexpr std::string_view name = "f1";
    using type = decltype(B::f1);
    static constexpr auto reference = &B::f1;
    static constexpr auto attributes = std::make_tuple();
  };
  static constexpr auto fields = std::make_tuple(field_s(), field_b(), field_f(), field_f1());
  static_assert(is_sorted({offsetof(B, s), offsetof(B, b), offsetof(B, f), offsetof(B, f1)}));
};

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP