peerplays-fc/tests/reflection_tests.cpp
Nathan Hourt c69ea32613 Improve reflection system and static_variant
Previously, fc reflection generated visitor infrastructure capable of
visiting each field of a struct; however, this is only useful for run-
time operations on structures. No compile-time information was preserved
about the fields of a struct, making compile-time introspection of types
impossible.

Now FC reflection generates rich compile-time information about the
members of structs, including types, names, and structure, allowing
code to be written to explore types in detail at compile-time and fetch
arbitrary fields at runtime without needing to iterate over the unwanted
fields.

To make this a reality, a new `typelist` type was added to store this
compile-time information. This type is also useful within the context of
`static_variant`, as SV previously used implementation detail types in a
private namespace to provide this functionality. Now `static_variant`
uses the reusable functionality of `typelist` to operate, dramatically
reducing the amount of code dedicated to `static_variant` and also
making `static_variant` types more flexible to work with since their
infrastructure is now based on the `typelist` public interface.
2019-08-30 14:21:36 -05:00

126 lines
7.3 KiB
C++

#include <boost/test/unit_test.hpp>
#include <fc/exception/exception.hpp>
#include <type_traits>
struct reflect_test_base {
int x = 1;
char y = 'a';
};
struct reflect_test_derived : reflect_test_base {
double z = 3.14;
};
struct reflect_layer_1 { reflect_test_base b; int32_t n; };
struct reflect_layer_2 { reflect_layer_1 l1; reflect_test_derived d; };
struct reflect_layer_3 { reflect_layer_2 l2; int32_t i; };
FC_REFLECT( reflect_test_base, (x)(y) );
FC_REFLECT_DERIVED( reflect_test_derived, (reflect_test_base), (z) );
FC_REFLECT( reflect_layer_1, (b)(n) );
FC_REFLECT( reflect_layer_2, (l1)(d) );
FC_REFLECT( reflect_layer_3, (l2)(i) );
BOOST_AUTO_TEST_SUITE( fc_reflection )
BOOST_AUTO_TEST_CASE( reflection_static_tests )
{
// These are all compile-time tests, nothing actually happens here at runtime
using base_reflection = fc::reflector<reflect_test_base>;
using derived_reflection = fc::reflector<reflect_test_derived>;
static_assert(fc::typelist::length<base_reflection::members>() == 2, "");
static_assert(fc::typelist::length<derived_reflection::members>() == 3, "");
static_assert(fc::typelist::at<derived_reflection::members, 0>::is_derived, "");
static_assert(std::is_same<fc::typelist::at<derived_reflection::members, 0>::field_container,
reflect_test_base>::value, "");
static_assert(fc::typelist::at<derived_reflection::members, 1>::is_derived, "");
static_assert(std::is_same<fc::typelist::at<derived_reflection::members, 1>::field_container,
reflect_test_base>::value, "");
static_assert(fc::typelist::at<derived_reflection::members, 2>::is_derived == false, "");
static_assert(std::is_same<fc::typelist::slice<fc::typelist::list<int, bool, char>, 0, 1>,
fc::typelist::list<int>>::value, "");
static_assert(std::is_same<fc::typelist::slice<fc::typelist::list<int, bool, char>, 0, 2>,
fc::typelist::list<int, bool>>::value, "");
static_assert(std::is_same<fc::typelist::slice<fc::typelist::list<int, bool, char>, 0, 3>,
fc::typelist::list<int, bool, char>>::value, "");
static_assert(std::is_same<fc::typelist::slice<fc::typelist::list<int, bool, char>, 1, 3>,
fc::typelist::list<bool, char>>::value, "");
static_assert(std::is_same<fc::typelist::slice<fc::typelist::list<int, bool, char>, 2, 3>,
fc::typelist::list<char>>::value, "");
static_assert(std::is_same<fc::typelist::slice<fc::typelist::list<int, bool, char>, 1, 2>,
fc::typelist::list<bool>>::value, "");
static_assert(std::is_same<fc::typelist::slice<fc::typelist::list<int, bool, char>, 1>,
fc::typelist::list<bool, char>>::value, "");
static_assert(std::is_same<fc::typelist::make_sequence<0>, fc::typelist::list<>>::value, "");
static_assert(std::is_same<fc::typelist::make_sequence<1>,
fc::typelist::list<std::integral_constant<size_t, 0>>>::value, "");
static_assert(std::is_same<fc::typelist::make_sequence<2>,
fc::typelist::list<std::integral_constant<size_t, 0>,
std::integral_constant<size_t, 1>>>::value, "");
static_assert(std::is_same<fc::typelist::make_sequence<3>,
fc::typelist::list<std::integral_constant<size_t, 0>,
std::integral_constant<size_t, 1>,
std::integral_constant<size_t, 2>>>::value, "");
static_assert(std::is_same<fc::typelist::zip<fc::typelist::list<>, fc::typelist::list<>>,
fc::typelist::list<>>::value, "");
static_assert(std::is_same<fc::typelist::zip<fc::typelist::list<bool>, fc::typelist::list<char>>,
fc::typelist::list<fc::typelist::list<bool, char>>>::value, "");
static_assert(std::is_same<fc::typelist::zip<fc::typelist::list<int, bool>, fc::typelist::list<char, double>>,
fc::typelist::list<fc::typelist::list<int, char>,
fc::typelist::list<bool, double>>>::value, "");
static_assert(std::is_same<fc::typelist::index<fc::typelist::list<>>, fc::typelist::list<>>::value, "");
static_assert(std::is_same<fc::typelist::index<fc::typelist::list<int, bool, char, double>>,
fc::typelist::list<fc::typelist::list<std::integral_constant<size_t, 0>, int>,
fc::typelist::list<std::integral_constant<size_t, 1>, bool>,
fc::typelist::list<std::integral_constant<size_t, 2>, char>,
fc::typelist::list<std::integral_constant<size_t, 3>, double>>
>::value, "");
}
BOOST_AUTO_TEST_CASE( typelist_dispatch_test )
{
using list = fc::typelist::list<float, bool, char>;
auto get_name = [](auto t) -> std::string { return fc::get_typename<typename decltype(t)::type>::name(); };
BOOST_CHECK_EQUAL(fc::typelist::runtime::dispatch(list(), 0ul, get_name), "float");
BOOST_CHECK_EQUAL(fc::typelist::runtime::dispatch(list(), 1ul, get_name), "bool");
BOOST_CHECK_EQUAL(fc::typelist::runtime::dispatch(list(), 2ul, get_name), "char");
}
// Helper template to use fc::typelist::at without a comma, for macro friendliness
template<typename T> struct index_from { template<std::size_t idx> using at = fc::typelist::at<T, idx>; };
BOOST_AUTO_TEST_CASE( reflection_get_test )
{ try {
reflect_test_derived derived;
reflect_test_base& base = derived;
using base_reflector = fc::reflector<reflect_test_base>;
using derived_reflector = fc::reflector<reflect_test_derived>;
BOOST_CHECK(index_from<base_reflector::members>::at<0>::get(base) == 1);
BOOST_CHECK(index_from<base_reflector::members>::at<1>::get(base) == 'a');
fc::typelist::at<base_reflector::members, 0>::get(base) = 5;
fc::typelist::at<base_reflector::members, 1>::get(base) = 'q';
BOOST_CHECK(index_from<base_reflector::members>::at<0>::get(base) == 5);
BOOST_CHECK(index_from<base_reflector::members>::at<1>::get(base) == 'q');
BOOST_CHECK(index_from<derived_reflector::members>::at<0>::get(derived) == 5);
BOOST_CHECK(index_from<derived_reflector::members>::at<1>::get(derived) == 'q');
BOOST_CHECK(index_from<derived_reflector::members>::at<2>::get(derived) == 3.14);
fc::typelist::at<derived_reflector::members, 1>::get(derived) = 'X';
BOOST_CHECK(index_from<base_reflector::members>::at<1>::get(base) == 'X');
reflect_layer_3 l3;
BOOST_CHECK(index_from<index_from<index_from<index_from<fc::reflector<reflect_layer_3>::members>::at<0>
::reflector::members>::at<0>::reflector::members>::at<0>::reflector::members>::at<1>::get(l3.l2.l1.b)
== 'a');
BOOST_CHECK(index_from<index_from<index_from<fc::reflector<reflect_layer_3>::members>::at<0>::reflector::members>
::at<1>::reflector::members>::at<1>::get(l3.l2.d) == 'a');
BOOST_CHECK(index_from<index_from<index_from<fc::reflector<reflect_layer_3>::members>::at<0>::reflector::members>
::at<1>::reflector::members>::at<2>::get(l3.l2.d) == 3.14);
} FC_CAPTURE_LOG_AND_RETHROW( (0) ) }
BOOST_AUTO_TEST_SUITE_END()