A modern compile-time reflection library for C++ with support for overloads, templates, attributes and proxies

Overview

refl-cpp v0.12.1

Documentation

refl-cpp Logo

Contributors Activity CircleCI Gitter Patrons AUR version

refl-cpp encodes type metadata in the type system to allow compile-time reflection via constexpr and template metaprogramming in C++17

Introduction

refl-cpp aims to provide a generic static reflection system that can be extended to suit your needs. Runtime reflection can also be implemented on top of the existing system for when needed (see the examples).

Some nice things refl-cpp supports out-of-the-box:

  • custom attributes - encodes type-level and member-level attributes as a constexpr std::tuple associated with that item
  • proxy types - builds configurable generic types with identical member functions that can be hooked into to wrap or extend functionality
  • overloaded functions - wraps member functions in generic variadic templates
  • template functions - template function for which the template parameters can be inferred from the arguments are also supported
  • template types - uses template specialization to encode metadata for types; template types are perfectly-well supported

Code Example (see on Compiler Explorer)

A basic example showcasing how refl-cpp can be use to convert an arbitrary type to a tuple at compile-time

// Custom attribute to mark fields for validation
struct NonNegative : refl::attr::usage::member {};

struct Point {
    double x, y;
};

REFL_AUTO(
    type(Point),
    field(x, NonNegative()),
    field(y, NonNegative())
)

struct Circle {
    Point origin;
    double radius;
};

REFL_AUTO(
    type(Circle),
    field(origin),
    field(radius, NonNegative())
)

template <typename T>
constexpr bool checkNonNegative(const T& obj) {
    // Get the type descriptor for T
    constexpr auto type = refl::reflect<T>();
    // Get the compile-time refl::type_list<...> of member descriptors
    constexpr auto members = get_members(type);
    // Filter out the non-readable members (not field or getters marked with the property() attribute)
    constexpr auto readableMembers = filter(members, [](auto member) { return is_readable(member); });

    auto invalidMemberCount = count_if(readableMembers, [&](auto member) {
        // Function-call syntax is a uniform way to get the value of a member (whether a field or a getter)
        auto&& value = member(obj);
        // Check if the NonNegative attribute is present
        if constexpr (refl::descriptor::has_attribute<NonNegative>(member)) {
            // And if so, make the necessary checks
            return value < 0;
        }
        // Recursively check the value of the member
        else if (!checkNonNegative(value)) {
            return true;
        }
        return false;
    });

    return invalidMemberCount == 0;
}

// These all run at compile-time
constexpr Circle c1{ {0., 5.}, 100. };
static_assert(checkNonNegative(c1));

constexpr Circle c2{ {0., 5.}, -100. };
static_assert(!checkNonNegative(c2));

constexpr Circle c3{ {0., -5.}, 100. };
static_assert(!checkNonNegative(c3));

Requirements

  • Minimum language standard: C++17

Usage

For developers

Run CMake with -Drefl-cpp_DEVELOPER_MODE=ON flag. You may also want to setup a custom preset for a more convenient developer experience (see this comment on #44).

Contributing

License

Issues
  • Use of `get_reader` and `has_writer` produces absurdly long compile times

    Use of `get_reader` and `has_writer` produces absurdly long compile times

    I use refl-hpp to import C++ classes into LuaBridge. A requirement of LuaBridge is that to import a writable property you must import its getter and setter at the same time. I have hacked around this with modifications to LuaBridge that allow me to import the setters separately, but these hacks are not safe in the long term and will not be merged back into the LuaBridge repository.

    An alternative is to use get_reader and has_writer to import the properties correctly. These work, but they increase the compile-time cpu and memory requirements by a factor of 10x or more. What I've noticed in general with refl-cpp is that these requirements seem to increase exponentially with the number of members in the REFL_TYPE.

    My code looks something like this:

    template<class T>
    void ProcessClass()
    {
       constexpr auto type = refl::reflect<T>();
       constexpr auto members = get_members(type);
    
       for_each(members, [&](auto member)
       {
          if constexpr (refl::descriptor::is_property(member))
          {
             if constexpr (refl::descriptor::is_writable(member))
             {
                auto reader = refl::descriptor::get_reader(member);
                // etc.
             }
             else if constexpr (! refl::descriptor::has_writer(member))
             {
                // etc.
             }
          }
       });
    }
    

    I am wondering if you have a suggestion as to how to refactor this code to be more efficient at compile time. Is there some kind of hash table like std::map that runs at compile time? I have searched but couldn't find one. It would be nice if get_writer and get_reader could do a hash lookup on the display name rather than using find_one. I could pre-process the members in a separate loop if need be, but I haven't been able to figure out how.

    opened by rpatters1 16
  • Question about tuple things and bindings

    Question about tuple things and bindings

    Hello i try to use refl-hpp instead of my own reflection library

    For registering into a scripting type system using reflection i was using the following snippet

    
     template <typename ...Ts>
     using meta::map = std::tuple<Ts...>;
    
    struct transform_2d
        {
            transform_2d() noexcept = default;
    
            transform_2d(float x_,
                         float y_, float width_, float height_, float scale_x_, float scale_y_,
                         float angle_) noexcept :
                x(x_),
                y(y_),
                width(width_),
                height(height_),
                scale_x(scale_x_),
                scale_y(scale_y_),
                angle(angle_),
                original_x(x_)
            {
            }
    
            float x{0.0f}; //!< x
            float y{0.0f}; //!< y
            float width{0.0f};
            float height{0.0f};
            float scale_x{1.0f};
            float scale_y{1.0f};
            float angle{0.0f};
            bool rotating{false};
    
            //! Original (mutable because of EnTT, (should not be modified))
            mutable float original_x{x};
            mutable float original_y{y};
            mutable float original_width{width};
            mutable float original_height{height};
    
            reflect_class(transform_2d)
    
            static constexpr auto reflected_functions() noexcept
            {
                return meta::makeMap();
            }
    
            static constexpr auto reflected_members() noexcept
            {
                return meta::makeMap(reflect_member(&transform_2d::y),
                                     reflect_member(&transform_2d::x),
                                     reflect_member(&transform_2d::width),
                                     reflect_member(&transform_2d::height),
                                     reflect_member(&transform_2d::scale_x),
                                     reflect_member(&transform_2d::scale_y),
                                     reflect_member(&transform_2d::angle),
                                     reflect_member(&transform_2d::rotating));
            }
        };
    
    
     template <typename T>
        void register_type(sol::state &state, shiva::logging::logger logger) noexcept
        {
            const auto table = std::tuple_cat(
                std::make_tuple(T::class_name()),
                T::reflected_functions(),
                T::reflected_members());
    
            try {
                std::apply(
                    [&state](auto &&...params) {
                        state.new_usertype<T>(std::forward<decltype(params)>(params)...);
                    }, table);
            }
            catch (const std::exception &error) {
                logger->error("error: {}", error.what());
                return;
            }
    
            logger->info("successfully registering type: {}", T::class_name());
        }
    
    
    register_type<transform2d>();
    

    It"s was generating a snippet like:

    new_usertype<transform_2d>("transform_2d",
    		"x", &my_class::x,
    		"y", &my_class::y); /
    
    

    Is it possible using your lib ?

    question 
    opened by Milerius 12
  • Visual Studio 2017 code formatter breaks compilation for refl.hpp

    Visual Studio 2017 code formatter breaks compilation for refl.hpp

    Create a new C++ console application. Set warnings to level 4. Target latest. Build with the Visual Studio 2017 (v141) compiler:

    Severity	Code	Description	Line	File	Project	Suppression State
    Error	C2760	syntax error: unexpected token 'identifier', expected 'type specifier'	2563	..\refltestconsole\refl.hpp	ReflTestConsole	
    Error	C2270	'MemberName_': modifiers not allowed on nonmember functions	2565	..\refltestconsole\refl.hpp	ReflTestConsole	
    Error	C2995	'decltype(auto) MemberName_(Args &&...)': function template has already been defined	2568	..\refltestconsole\refl.hpp	ReflTestConsole	
    Error	C2059	syntax error: '}'	2569	..\refltestconsole\refl.hpp	ReflTestConsole	
    Error	C2143	syntax error: missing ';' before '}'	2569	..\refltestconsole\refl.hpp	ReflTestConsole	
    Error	C2143	syntax error: missing ';' before '{'	2610	..\refltestconsole\refl.hpp	ReflTestConsole	
    Error	C2447	'{': missing function header (old-style formal list?)	2610	..\refltestconsole\refl.hpp	ReflTestConsole	
    Error	C2143	syntax error: missing ';' before 'namespace'	2771	..\refltestconsole\refl.hpp	ReflTestConsole	
    Error	C1075	'{': no matching token found	2779	..\refltestconsole\refl.hpp	ReflTestConsole	
    
    
    bug 
    opened by ScottHutchinson 10
  • Usage help

    Usage help

    I just came across this library and I must admit that it's bit too complex for an average user. Nevertheless, i appreciate the immense amount of work into it. I have a particular requirement that brought me here and i would like to know how to achieve them if that is possible.

    1. Is there an equivalent of offsetof(struct, member) and sizeof(member)? My use case requires me to read and write the whole structure or portions of it as a bytestream. This can be achieved with something like... memcpy(buffer, &myStructAsByteStream[offsetof(mystruct, member)], bytesToSend)

    2)Is there a way to get the index of a member of the struct? 3) Is there a way to get the member from index? This is the inverse of the previous.

    question 
    opened by kitts82 7
  • Compilation error with simple example

    Compilation error with simple example

    Hi, I was trying to perform a simple example but I am receiving a lot of errors during compilation. I'm using gcc/g++ 8.4.0 and compiling the program with the following command: g++ -std=c++17 -Wall -g main.cpp -o test

    #define REFL_METADATA_FILE "reflmd/main.cpp"
    #include "libs/refl.hpp"
    #include <iostream>
    
    struct pdu_types_struct
    {
        REFL() // the reflmd file has been generated
        int var1= 28;
    };
    
    struct pdu_types_struct pdu_types;
    
    int main(int argc, char **argv)
    {
         for_each(refl::reflect(pdu_types).members, [&](auto member) {
          std::cout << member.name << "=" << member(pdu_types) << ";";
       });
        return 0;
    } 
    
    #include REFL_METADATA_FILE
    

    With the code above, I receive the following errors:

    g++ -std=c++17 -Wall -g main.cpp -o test   
    In file included from main.cpp:2:
    libs/refl.hpp: In instantiation of ‘class refl::descriptor::type_descriptor<pdu_types_struct>’:
    main.cpp:15:38:   required from here
    libs/refl.hpp:1625:27: error: static assertion failed: This type does not support reflection!
                 static_assert(refl::trait::is_reflectable_v<T>, "This type does not support reflection!");
                               ^~~~
    libs/refl.hpp:2620:55: error: specialization of ‘refl_impl::metadata::type_info__<pdu_types_struct>’ after instantiation
         namespace refl_impl::metadata { template<> struct type_info__<TypeName> { \
                                                           ^~~~~~~~~~~~~~~~~~~~~
    reflmd/main.cpp:3:1: note: in expansion of macro ‘REFL_TYPE’
     REFL_TYPE(pdu_types_struct)
     ^~~~~~~~~
    libs/refl.hpp:2620:55: error: redefinition of ‘struct refl_impl::metadata::type_info__<pdu_types_struct>’
         namespace refl_impl::metadata { template<> struct type_info__<TypeName> { \
                                                           ^~~~~~~~~~~~~~~~~~~~~
    reflmd/main.cpp:3:1: note: in expansion of macro ‘REFL_TYPE’
     REFL_TYPE(pdu_types_struct)
     ^~~~~~~~~
    In file included from main.cpp:2:
    libs/refl.hpp:373:20: note: previous definition of ‘struct refl_impl::metadata::type_info__<pdu_types_struct>’
                 struct type_info__
    
    

    Is there some pre step to use the library I am missing or some additional argument when calling g++?

    Regards

    bug wontfix refl-ht 
    opened by Matheus-Garbelini 7
  • Don't build on Windows 10 in VS 2017 version 15.9.26

    Don't build on Windows 10 in VS 2017 version 15.9.26

    1>c:\source\repos\testtest_0\testtest_0\refl.hpp(1383): error C2672: 'refl::trait::detail::index_of_instance': no matching overloaded function found 1>c:\source\repos\testtest_0\testtest_0\refl.hpp(1385): note: see reference to class template instantiation 'refl::trait::index_of_instance<T,refl::util::type_list<Ts...>>' being compiled 1>c:\source\repos\testtest_0\testtest_0\refl.hpp(1383): error C3207: 'refl::trait::detail::index_of_instance': invalid template argument for 'T', class template expected 1>c:\source\repos\testtest_0\testtest_0\refl.hpp(1326): note: see declaration of 'refl::trait::detail::index_of_instance'

    bug 
    opened by AlexCasual 6
  • Q: how to reflect on member functions' parameters and return types?

    Q: how to reflect on member functions' parameters and return types?

    We are using refl-cpp primarily for reflections on member fields which works perfectly fine for our use case.

    We may have a new application now, where I was wondering whether this could be also extended also to member functions, notably to retrieve their parameter and return type signatures during compile time.

    For example, having the following demo class definition:

    class Circle {
        double r;
    public:
        Circle(double val) : r(val) {}
        double getRadius() const { return r; }
        void setRadius(double radius) { r = radius; }
        std::string getFunc(int a, double b, std::string c) const { return fmt::format("{}{}{}",a,b,c); }
        double getDiameter() const { return 2 * r; }
        double getArea() const { return M_PI * r * r; }
    };
    

    How would one go about retrieving the setRadius(double) calling parameter (double) and return value (void)? Or, more generally, the number of calling parameters of getFunc(...) which would be std::tuple<int,double,std::string> and its return type std::string?

    There is an example that illustrates how to get the function pointer and how to call 'std::invoke(..)` but this seems to implicitly assume the calling parameter and return types:

    using refl::reflect;
    using refl::util::find_one;
    constexpr auto type = reflect<Circle>();
     
    constexpr auto func = find_one(type.members, [](auto m) { return m.name == "getRadius"; }); // -> function_descriptor<Circle, 0>{...}
    
    func.name; // -> const_string<6>{"getRadius"}
    func.attributes; // -> std::tuple<>{}
    func.is_resolved; // -> true
    func.pointer; // -> pointer of type double (Circle::* const)()
     
    using radius_t = double (Circle::* const)();
    func.template resolve<radius_t>; // -> pointer of type radius_t on success, nullptr_t on fail.
     
    Circle c(2.0);
    func.invoke(c); // -> the result of c.getRadius()
    

    Also more generally, how would one register, find and call the different class constructors and parameter signatures?

    This (potentially new) functionality has applications in GUIs and other areas where the compile/run-time definitions are driven by configuration files.

    Any help, suggestions, or feedback would be welcome. Thanks in advance.

    opened by RalphSteinhagen 5
  • Refactor CircleCI config

    Refactor CircleCI config

    This PR will make CircleCI use the gcc and clang images by teeks99, which are based on Ubuntu, so it's less hassle to set things up (install cmake and ninja).

    The gcc and clang jobs are run using a parameterized matrix as well.

    Just a note, but CI env var is always set in CI services and developer mode is force enabled if the CI env var is a true value, i.e. no need to pass refl-cpp_DEVELOPER_MODE.

    Edit: you should enable running CI on PRs. On CircleCI, you can do this in Project settings > Advanced > Build forked pull requests and Auto-cancel redundant builds.

    opened by friendlyanon 5
  • CMake support

    CMake support

    This PR enables clients using CMake to consume this library in a trivial manner.

    For rationale behind the changes, please look at my recent PRs and discussions in FunctionalPlus, where I added similar support.

    opened by friendlyanon 5
  • Is there any way to recusively traverse a type or support std type traits?

    Is there any way to recusively traverse a type or support std type traits?

    I'm working on a comprehensive serializer which has to support a couple of scenarios, such as

    • nested types
    • stl containers
    • dynamic types / inheriting I was experimenting with this library if it could be used to replace a lot of extra overhead including the serialization. However, I've found no way to apply stl type traits to determine if a member is either a pointer or a class (struct) or something else (using my own type traits).

    I'm using the following example code below:

    struct serializable : refl::attr::usage::field, refl::attr::usage::function
    {
    };
    
    template <typename T> void serialize(std::ostream & os, T && value)
    {
    	// iterate over the members of T
    	for_each(refl::reflect(value).members, [&](auto member) {
    		// is_readable checks if the member is a non-const field
    		// or a 0-arg const-qualified function marked with property attribute
    
    		if constexpr (is_readable(member) && refl::descriptor::has_attribute<serializable>(member))
    		{
    
    			if constexpr (std::is_class_v<decltype(member(value))>) // This is always false
    			{
    				os << get_display_name(member) << "= {\n";
    				serialize(os, member(value)); // This always warns for resuesion
    				os << "}\n";
    			}
    			else
    			{
    				os << get_display_name(member) << "=";
    				os << member(value) << ";"; // This always fails 
    			}
    		}
    	});
    }
    
    struct Point
    {
    	float x;
    	float y;
    	[[nodiscard]] float magnitude() const { return std::sqrt(x * x + y * y); }
    };
    
    REFL_TYPE(Point, bases<>)
    REFL_FIELD(x, serializable())
    REFL_FIELD(y, serializable())
    REFL_FUNC(magnitude)
    REFL_END
    
    struct Line
    {
    	Point start;
    	Point end;
    	[[nodiscard]] float length() const { return Point({ end.x - start.x, end.y - start.y }).magnitude(); }
    };
    
    REFL_TYPE(Line, bases<>)
    REFL_FIELD(start, serializable())
    REFL_FIELD(end, serializable())
    REFL_FUNC(length)
    REFL_END
    
    TEST(Reflection, HelloTest)
    {
    	std::cout << "Custom serialization: ";
    	serialize(std::cout, Point{ 1, 1 });
    
    	serialize(std::cout, Line{ { 1, 1 }, { 2, 2 } });
    	std::cout << std::endl;
    
    	SUCCEED();
    }
    
    

    I'd like to use it in more complex scenarios like this. Is there any legitimate way to do this?

    question 
    opened by psaripp 4
  • base class function reflected ?

    base class function reflected ?

    Hello

    i have the following snippet:

    #include "antara/gaming/core/safe.refl.hpp"
    #include "antara/gaming/math/vector.hpp"
    
    namespace antara::gaming::transform
    {
        struct position_2d : public math::vec2f
        {
            template<typename ... Args>
            position_2d(Args&& ...args) noexcept: math::vec2f(std::forward<Args>(args)...)
            {
    
            }
    
            decltype(auto) x()
            {
                return math::vec2f::x();
            }
    
            decltype(auto) y()
            {
                return math::vec2f::y();
            }
        };
    }
    
    REFL_AUTO(type(antara::gaming::transform::position_2d), func(x), func(y))
    

    I would like if possible to remove the x y function that i add to reflect correctly my base class

    but when i try to invoke x() or y() without my trick it's not a valid pointer to member

    It's possible to reflect base class functions ?

    my full vector class which use mixins:

    namespace antara::gaming::math
    {
        template<class Unit, size_t Size, template<class> class...Mixins>
        class basic_vector : public Mixins<basic_vector<Unit, Size, Mixins...>> ...
        {
    
            template<class, size_t, template<class> class...>
            friend
            class basic_vector;
    
            using sequence_type = std::make_index_sequence<Size>;
    
            template<class Res, size_t...Is>
            constexpr auto implicit_cast_to(std::index_sequence<Is...>) const noexcept
            {
                return Res{std::get<Is>(data_)...};
            }
    
            template<class Res, size_t...Is>
            constexpr auto explicit_cast_to(std::index_sequence<Is...>) const noexcept
            {
                using NewUnit = typename Res::value_type;
                return Res{static_cast<NewUnit>(std::get<Is>(data_))...};
            }
    
            template<class F, size_t...Is>
            constexpr basic_vector make_vec(F &&f, std::index_sequence<Is...>) const noexcept
            {
                return {f(std::get<Is>(data_))...};
            }
    
            template<class F, size_t...Is>
            constexpr basic_vector make_vec(F &&f, basic_vector const &rhs, std::index_sequence<Is...>) const noexcept
            {
                return {f(std::get<Is>(data_), std::get<Is>(rhs.data_))...};
            }
    
            template<class F, size_t...Is>
            constexpr void update_vec(F &&f, std::index_sequence<Is...>) noexcept
            {
                (f(std::get<Is>(data_)), ...);
            }
    
            template<class F, size_t...Is>
            constexpr void update_vec(F &&f, basic_vector const &rhs, std::index_sequence<Is...>) noexcept
            {
                (f(std::get<Is>(data_), std::get<Is>(rhs.data_)), ...);
            }
    
            template<size_t...Is>
            constexpr Unit square_length(std::index_sequence<Is...>) const noexcept
            {
                return (... + (std::get<Is>(data_) * std::get<Is>(data_)));
            }
    
            template<class F, class Vec, size_t...Is>
            constexpr bool test_predicate(F &&f, Vec const &rhs, std::index_sequence<Is...>) const noexcept
            {
                return (f(std::get<Is>(data_), std::get<Is>(rhs.data_)) && ...);
            }
    
            std::array<Unit, Size> data_;
        public:
            using value_type = Unit;
    
            // Aggregate-like constructor
            template<class...Args, class = std::enable_if_t<
                    std::conjunction_v<std::is_convertible<Args, Unit>...>
            >>
            constexpr basic_vector(Args...args) noexcept : data_{args...}
            {}
    
            constexpr basic_vector(Unit single_value) noexcept
            {
                std::fill(begin(), end(), single_value);
            }
    
            static constexpr auto scalar(Unit single_value) noexcept
            {
                return basic_vector(single_value);
            }
    
            constexpr Unit operator[](int i) const noexcept
            { return data_[i]; }
    
            constexpr Unit &operator[](int i) noexcept
            { return data_[i]; }
    
            template<size_t I>
            constexpr Unit get() const noexcept
            {
                static_assert(I >= 0 && I < Size, "Index outside of bounds");
                return std::get<I>(data_);
            }
    
            template<size_t I>
            constexpr Unit &get() noexcept
            {
                static_assert(I >= 0 && I < Size, "Index outside of bounds");
                return std::get<I>(data_);
            }
    
            constexpr Unit *data() noexcept
            { return data_.data(); }
    
            constexpr Unit const *data() const noexcept
            { return data_.data(); }
    
            constexpr int size() const noexcept
            { return Size; }
    
            constexpr auto begin() noexcept
            { return data_.begin(); }
    
            constexpr auto begin() const noexcept
            { return data_.begin(); }
    
            constexpr auto end() noexcept
            { return data_.end(); }
    
            constexpr auto end() const noexcept
            { return data_.end(); }
    
            // Implicit cast
            template<class NewUnit, template<class> class...NewMixins>
            constexpr operator basic_vector<NewUnit, Size, NewMixins...>() const noexcept
            {
                static_assert(std::is_convertible_v<Unit, NewUnit>, "Impossible cast from [value_type] to [NewUnit]");
                return implicit_cast_to<basic_vector<NewUnit, Size, NewMixins...>>(sequence_type{});
            }
    
            // Explicit cast
            template<class Vec>
            constexpr Vec to() const noexcept
            {
                using NewUnit = typename Vec::value_type;
                static_assert(std::is_convertible_v<Unit, NewUnit>,
                              "Impossible cast from [value_type] to [Vec::value_type]");
                return explicit_cast_to<Vec>(sequence_type{});
            }
    
            constexpr basic_vector operator+(Unit b) const noexcept
            {
                return make_vec([b](Unit a) { return a + b; }, sequence_type{});
            }
    
            constexpr basic_vector operator-(Unit b) const noexcept
            {
                return make_vec([b](Unit a) { return a - b; }, sequence_type{});
            }
    
            constexpr basic_vector operator*(Unit b) const noexcept
            {
                return make_vec([b](Unit a) { return a * b; }, sequence_type{});
            }
    
            constexpr basic_vector operator/(Unit b) const noexcept
            {
                return make_vec([b](Unit a) { return a / b; }, sequence_type{});
            }
    
            constexpr basic_vector operator+(basic_vector const &rhs) const noexcept
            {
                return make_vec([](Unit a, Unit b) { return a + b; }, rhs, sequence_type{});
            }
    
            constexpr basic_vector operator-(basic_vector const &rhs) const noexcept
            {
                return make_vec([](Unit a, Unit b) { return a - b; }, rhs, sequence_type{});
            }
    
            constexpr basic_vector operator*(basic_vector const &rhs) const noexcept
            {
                return make_vec([](Unit a, Unit b) { return a * b; }, rhs, sequence_type{});
            }
    
            constexpr basic_vector operator/(basic_vector const &rhs) const noexcept
            {
                return make_vec([](Unit a, Unit b) { return a / b; }, rhs, sequence_type{});
            }
    
            constexpr basic_vector &operator+=(Unit b) noexcept
            {
                update_vec([b](Unit &a) { a += b; }, sequence_type{});
                return *this;
            }
    
            constexpr basic_vector &operator-=(Unit b) noexcept
            {
                update_vec([b](Unit &a) { a -= b; }, sequence_type{});
                return *this;
            }
    
            constexpr basic_vector &operator*=(Unit b) noexcept
            {
                update_vec([b](Unit &a) { a *= b; }, sequence_type{});
                return *this;
            }
    
            constexpr basic_vector &operator/=(Unit b) noexcept
            {
                update_vec([b](Unit &a) { a /= b; }, sequence_type{});
                return *this;
            }
    
            constexpr basic_vector &operator+=(basic_vector const &rhs) noexcept
            {
                update_vec([](Unit &a, Unit b) { a += b; }, rhs, sequence_type{});
                return *this;
            }
    
            constexpr basic_vector &operator-=(basic_vector const &rhs) noexcept
            {
                update_vec([](Unit &a, Unit b) { a -= b; }, rhs, sequence_type{});
                return *this;
            }
    
            constexpr basic_vector &operator*=(basic_vector const &rhs) noexcept
            {
                update_vec([](Unit &a, Unit b) { a *= b; }, rhs, sequence_type{});
                return *this;
            }
    
            constexpr basic_vector &operator/=(basic_vector const &rhs) noexcept
            {
                update_vec([](Unit &a, Unit b) { a /= b; }, rhs, sequence_type{});
                return *this;
            }
    
            constexpr basic_vector operator-() const noexcept
            {
                return make_vec([](Unit x) { return -x; }, sequence_type{});
            }
    
            constexpr Unit square_length() const noexcept
            {
                return square_length(sequence_type{});
            }
    
            constexpr Unit length() const noexcept
            {
                return std::sqrt(square_length());
            }
    
            constexpr basic_vector normalized() const noexcept
            {
                return *this / length();
            }
    
            template<class NewUnit, template<class> class...NewMixins>
            constexpr bool operator==(basic_vector<NewUnit, Size, NewMixins...> const &rhs) const noexcept
            {
                return test_predicate([](Unit a, Unit b) { return a == b; }, rhs, sequence_type{});
            }
    
            template<class NewUnit, template<class> class...NewMixins>
            constexpr bool operator!=(basic_vector<NewUnit, Size, NewMixins...> const &rhs) const noexcept
            {
                return !(*this == rhs);
            }
    
            template<class NewUnit, template<class> class...NewMixins>
            constexpr bool operator<(basic_vector<NewUnit, Size, NewMixins...> const &rhs) const noexcept
            {
                return test_predicate([](Unit a, Unit b) { return a < b; }, rhs, sequence_type{});
            }
    
            template<class NewUnit, template<class> class...NewMixins>
            constexpr bool operator>=(basic_vector<NewUnit, Size, NewMixins...> const &rhs) const noexcept
            {
                return !(*this < rhs);
            }
    
            template<class NewUnit, template<class> class...NewMixins>
            constexpr bool operator>(basic_vector<NewUnit, Size, NewMixins...> const &rhs) const noexcept
            {
                return rhs < *this;
            }
    
            template<class NewUnit, template<class> class...NewMixins>
            constexpr bool operator<=(basic_vector<NewUnit, Size, NewMixins...> const &rhs) const noexcept
            {
                return !(rhs < *this);
            }
        };
    
        namespace vector_mixins
        {
            template<class Derived>
            class access_xy
            {
            public:
                constexpr auto x() const noexcept
                { return static_cast<Derived const *>(this)->template get<0>(); }
    
                constexpr auto &x() noexcept
                { return static_cast<Derived *>(this)->template get<0>(); }
    
                constexpr auto y() const noexcept
                { return static_cast<Derived const *>(this)->template get<1>(); }
    
                constexpr auto &y() noexcept
                { return static_cast<Derived *>(this)->template get<1>(); }
            };
    
            template<class Derived>
            class access_z
            {
            public:
                constexpr auto z() const noexcept
                { return static_cast<Derived const *>(this)->template get<2>(); }
    
                constexpr auto &z() noexcept
                { return static_cast<Derived *>(this)->template get<2>(); }
            };
        }
    
        template<class Unit>
        using vec2 = basic_vector<Unit, 2, vector_mixins::access_xy>;
    
        using vec2c   = vec2<char>;
        using vec2uc  = vec2<unsigned char>;
        using vec2s   = vec2<short>;
        using vec2us  = vec2<unsigned short>;
        using vec2i   = vec2<int>;
        using vec2u   = vec2<unsigned>;
        using vec2l   = vec2<long>;
        using vec2ul  = vec2<unsigned long>;
        using vec2ll  = vec2<long long>;
        using vec2ull = vec2<unsigned long long>;
        using vec2f   = vec2<float>;
        using vec2d   = vec2<double>;
        using vec2ld  = vec2<long double>;
    
        template<class Unit>
        using vec3 = basic_vector<Unit, 3, vector_mixins::access_xy, vector_mixins::access_z>;
    
        using vec3c   = vec3<char>;
        using vec3uc  = vec3<unsigned char>;
        using vec3s   = vec3<short>;
        using vec3us  = vec3<unsigned short>;
        using vec3i   = vec3<int>;
        using vec3u   = vec3<unsigned>;
        using vec3l   = vec3<long>;
        using vec3ul  = vec3<unsigned long>;
        using vec3ll  = vec3<long long>;
        using vec3ull = vec3<unsigned long long>;
        using vec3f   = vec3<float>;
        using vec3d   = vec3<double>;
        using vec3ld = vec3<long double>;
    }
    
    namespace std {
    
        template<class Unit, size_t Size, template<class> class...Mixins>
        struct tuple_size<antara::gaming::math::basic_vector<Unit, Size, Mixins...>> : integral_constant<size_t, Size> {};
    
        template <size_t I, class Unit, size_t Size, template<class> class...Mixins>
        struct tuple_element<I, antara::gaming::math::basic_vector<Unit, Size, Mixins...>> { using type = Unit; };
    }
    
    question 
    opened by Milerius 4
  • Is there a way to reflect private fields?

    Is there a way to reflect private fields?

    A bit of newbie question I guess. With the current status of C++, are there any ways we can reflect on private member fields or methods?

    My main use case is de-serialization and populate class private fields from a parser. What's the best approach if reflection is not possible? I'm willing to friend or expose to a particular builder but not public the whole data section. Thanks.

    opened by bboysnick5 4
  • Fix warnings for strict variadic macro checks (stronger C++20 enforcement)

    Fix warnings for strict variadic macro checks (stronger C++20 enforcement)

    While code-base spring cleaning I noticed that I (unwisely) ignored some refl-cpp variadic macro warnings that prevent gcc|clang compiling with more strict checks enabled (i.e. -Werror).

    This shows up already for C++17 with the existing basic refl-cpp's default serialiser example and -pedantic compiler flags. The corresponding sample output and warnings from running the tests at compiler-explorer:

    • gcc 10.2 or newer produces the following warning:
    https://raw.githubusercontent.com/veselink1/refl-cpp/master/include/refl.hpp:4363:31: warning: ISO C++11 requires at least one argument for the "..." in a variadic macro
    
    • clang 12.0 or newer produce a bit more understandable warning outputs:
    https://raw.githubusercontent.com/veselink1/refl-cpp/master/include/refl.hpp:4363:5: warning: must specify at least one argument for '...' parameter of variadic macro [-Wgnu-zero-variadic-macro-arguments]
        REFL_DETAIL_PRIMITIVE(char);
        ^
    https://raw.githubusercontent.com/veselink1/refl-cpp/master/include/refl.hpp:4359:23: note: expanded from macro 'REFL_DETAIL_PRIMITIVE'
        REFL_TYPE(TypeName) \
                          ^
    

    These checks seem to become more enforced for but also there seems to be a clean solution for C++20 #define FOO(x, ...) bar(x __VA_OPT__(,) __VA_ARGS__) as, for example, discussed in more detail here. I was wondering whether this could be addressed under the premise that refl-cpp is C++17-based?

    Any help would be much appreciated! Thanks in advance!

    opened by RalphSteinhagen 0
  • Q: make REFL_AUTO, REFL_TYPE etc. callable from nested namespace

    Q: make REFL_AUTO, REFL_TYPE etc. callable from nested namespace

    Hi @veselink1, thanks again for time and making refl-cpp public! We gained quite a bit more experience with your library and are happily using it for (de-)serialisation in our little lib.

    From a usability point of view: what would it take to make REFL_TYPE callable from a nested namespace?

    In the documentation you correctly pointed out that the macros need to be called from within the global namespace. This becomes a bit unwieldy, a potential source of errors, and less readable in larger and/or structured projects where the structs are declared inside nested namespaces, e.g.:

    
    namespace other_a {
    namespace other_b {
    
    struct Point {
      float x;
      float y;
      float z;
    };
    // REFL_AUTO(type(Point), field(x), field(y), field(z)) // <- would like to define it here
    
    // [..] many unrelated lines of code [..]
    
    } // namespace other_a
    } // namespace other_b
    
    REFL_AUTO(type(Point), field(x), field(y), field(z)) // <- have to define it here (global namespace)
    

    Ideally one would like to declare the visitor macros right after the structs have been declared to minimise synchronisation errors. Any idea how this could be extended (other than closing/reopening the other namespaces)? Any help would be much appreciated!

    Season greetings and wish you a successful New Year 2022! :christmas_tree: :gift: :tada:

    opened by RalphSteinhagen 1
  • Update refl.hpp

    Update refl.hpp

    Fix Warning: ISO C++11 requires at least one argument for "..." in variadic marco Thrown at Macro REFL_TYPE(TypeName) from various REFL_DETAIL_PRIMITIVE()

    opened by Boneau2007 0
Releases(v0.12.3)
  • v0.12.3(Jan 23, 2022)

  • v0.12.2(Jan 2, 2022)

    • Much faster for_each and (has|get)_(reader|writer) c197d35 and #60
    • Fixed get_display_name_const on MSVC #53 (thanks @rpatters1)
    • Added CMake support #44 (thanks @friendlyanon)
    • Moved header file to include/refl.hpp #44 (thanks @friendlyanon)
    • Fixed builds on on Apple, with xCode 12.3 and set(CMAKE_CXX_STANDARD 17) #43 (thanks @michaelahughes)

    NOTE: This version does not contain any breaking changes at the source level (as is clear by the patch version increment). However, this version adds CMake support and this required the refl.hpp source file to be moved to a standard location include/refl.hpp. If you are consuming refl-cpp as a single-header library, you can continue to do so.

    Source code(tar.gz)
    Source code(zip)
  • v0.12.1(Nov 29, 2020)

    • Fixed broken build on Visual Studio 2017 #39 (9b6aa1a)
    • Fixed out-of-bounds read in refl::runtime::debug implementation for basic_string_view (93351ac)
    • Fixed unused function warning for refl::runtime::detail::next_depth #36 (by oberrich)
    Source code(tar.gz)
    Source code(zip)
  • v0.12.0(Jul 18, 2020)

    • Added std::complex support metadata. Disable with REFL_NO_STD_COMPLEX.
    • Added refl::descriptor::get_reader/get_writer utilities for getter/setter properties.
    • Added refl::trait::index_of for finding index of type in a type_list.
    • Added refl::descriptor::get_display_name_const as const_string alternative to get_display_name.
    • Fixed #33 (by james-conrad)
    Source code(tar.gz)
    Source code(zip)
  • v0.11.0(Jun 18, 2020)

    • Much improved support for inheritance (#27, #28)
      • refl::type_descriptor::declared_bases lists base types declared via the bases<...> attribute
      • refl::type_descriptor::bases lists base types declared via the bases<...> attribute as well as inherited members
      • refl::type_descriptor::members now includes declared and inherited members (when base types are specified via bases<...> attribute)
      • refl::type_descriptor::declared_members preserves the old behavior
      • refl::attr::bases<...> types are now validated (via std::is_base_of)
    • Added refl::descriptor::get_simple_name, which strips namespace and template declarations from the type name (std::vector<int> -> vector)
    • Added free function equivalents of members of _descriptor types for symmetry purposes (and might allow for more optimal implementation in the future)
    • Added refl::const_string::find/rfind for searching chars in const_strings
    • Added refl::make_const_string(char) overload
    • Added refl::type_list<T> specialization which exposes typedef T type and a synonym refl::type_tag<T>
    • refl::trait::concat now support an arbitrary number of type_lists as type arguments
    • Added refl::trait::as_tuple (similar to as_type_list but for std::tuple)
    • Added refl::trait::reverse
    • Added refl::util::reflect_types/refl::util::unreflect_types to quickly create a list of type_descriptors from a list of types and then back
    • Introduced support for different types of std::basic_ostream in attr::debug and util::debug (up to the user to take advantage of)
    • Built-in support for std::string_view
    • More type assertions in descriptor:: utilities
    • Simplification of some trait:: types (should lead to better compile-times)
    • Made unit tests more comprehensive
    • Fixed incorrect refl::util::identity implementation for rvalues
    • Fixed static function invocation with operator()/invoke
    • Fixed refl::util::debug for std::tuple #26
    • Deprecated refl::descriptor::get_bases in favor of refl::descriptor::get_base_types
    Source code(tar.gz)
    Source code(zip)
  • v0.10.0(Jun 8, 2020)

    • Introduced automatic property name normalization in get_display_name for properties with no friendly_name. Example: get_display_name(get_foo) -> "foo"
    • Added built-in reflection support for std::string_view #23
    • Fixed: operations on type_list do not properly take into account reference qualifiers
    • Fixed const_string constructor in older versions of clang
    • Fixed util::filter results list order (used to be reversed)
    • Fixed implementation of reflect, is_reflectable, debug
    • Improved runtime::debug print formatting
    • Removed filtering by const_string utils #21. Suggested workaround: use predicate variants
    • Removed refl-ht support
    • Code cleanup #24, #20
    Source code(tar.gz)
    Source code(zip)
Owner
Veselin Karaganev
Veselin Karaganev
A compiling time static reflection framework for C++

static_reflect This is a fully compiling time static reflection lightweight framework for C++. It provides a very rich compile-time reflection functio

null 7 May 14, 2022
C++ reflection library with Lua binding, and JSON and XML serialisation.

Ponder Linux & OSX: - Windows: Currents status: 3.2-alpha. API is unstable as features added/changed. New: Version 3 V1 replaced Boost with C++11. V2

Bill Quith 545 Jun 20, 2022
C++ compile-time enum to string, iteration, in a single header file

Better Enums Reflective compile-time enum library with clean syntax, in a single header file, and without dependencies. In C++11, everything can be us

Anton Bachin 1.3k Jun 23, 2022
C++ Reflection Library

!New Release - 0.9.6! RTTR C++ Reflection Library RTTR stands for Run Time Type Reflection. It describes the ability of a computer program to introspe

rttr.org 2.3k Jul 3, 2022
A miniature library for struct-field reflection in C++

visit_struct A header-only library providing structure visitors for C++11 and C++14. Motivation In C++ there is no built-in way to iterate over the me

null 347 Jun 24, 2022
Customizable C++17 Runtime Reflection Library

Refureku Check the Wiki for more documentation and use examples! Refureku is a powerful C++17 RTTI free runtime reflection library based on Kodgen. It

Julien SOYSOUVANH 164 Jun 12, 2022
A miniature library for struct-field reflection in C++

visit_struct A header-only library providing structure visitors for C++11 and C++14. Motivation In C++ there is no built-in way to iterate over the me

null 347 Jun 24, 2022
Header-only, non-intrusive and macro-free runtime reflection system in C++

Header-only runtime reflection system in C++ The reflection system was born within EnTT and is developed and enriched there. This project is designed

Michele Caini 418 Jun 21, 2022
C++ Reflection Parser / Runtime Skeleton

C++ Reflection Preface I worked on a complete reflection pipeline starting in the summer of 2015 for a game project / editor. My intent by creating th

Austin Brunkhorst 501 Jun 22, 2022
Nameof operator for modern C++, simply obtain the name of a variable, type, function, macro, and enum

_ _ __ _____ | \ | | / _| / ____|_ _ | \| | __ _ _ __ ___ ___ ___ | |_ | | _| |

Daniil Goncharov 1.4k Jun 20, 2022
cpgf library

cpgf library cpgf library is a cross platform C++ library for callback, reflection, serialization and script binding. It's written in standard C++ and

cpgf library 205 May 28, 2022
A compile-time enabled Modern C++ library that provides compile-time dimensional analysis and unit/quantity manipulation.

mp-units - A Units Library for C++ The mp-units library is the subject of ISO standardization for C++23/26. More on this can be found in ISO C++ paper

Mateusz Pusz 603 Jun 24, 2022
A software C library designed to extract data attributes from network packets, server logs, and from structured events in general, in order to make them available for analysis

MMT-DPI A software C library desinged to extract data attributes from network packets, server logs, and from structured events in general, in odrder t

Montimage 3 Apr 14, 2022
Improved and configurable drop-in replacement to std::function that supports move only types, multiple overloads and more

fu2::function an improved drop-in replacement to std::function Provides improved implementations of std::function: copyable fu2::function move-only fu

Denis Blank 391 Jun 18, 2022
Selective Compile-Time Reflection for C++

Introspective Some quotes from StackOverflow regarding reflection in C++: "Inspection by iterating over members of a type, enumerating its methods and

Josip Palavra 26 Mar 10, 2022
Compile-Time Reflection in C++ for use with Scripting Languages

Introspective Introspective is a header file that brings reflection to any class that wants it, regardless of whether the reflected member is a consta

Josip Palavra 26 Mar 10, 2022
Corkscrew is a tool for tunneling SSH through HTTP proxies

Corkscrew is a tool for tunneling SSH through HTTP proxies

Bryan Chan 884 Jun 29, 2022
Blazing-fast Expression Templates Library (ETL) with GPU support, in C++

Expression Templates Library (ETL) 1.3.0 ETL is a header only library for C++ that provides vector and matrix classes with support for Expression Temp

Baptiste Wicht 201 Jun 4, 2022
Mustache text templates for modern C++

About Mustache implementation for modern C++ (requires C++11) Header only Zero dependencies Templated string type for compatibility with any STL-like

Kevin Wojniak 284 Jun 17, 2022
Modern C++ 20 compile time OpenAPI parser and code generator implementation

OpenApi++ : openapipp This is a proof of concept, currently under active work to become the best OpenAPI implementation for C++. It allows compile tim

tipi.build 5 Apr 8, 2022
a lightweight and performant multicast DNS (mDNS) reflector with modern design, supports zone based reflection and IPv6

mDNS Reflector mDNS Reflector (mdns-reflector) is a lightweight and performant multicast DNS (mDNS) reflector with a modern design. It reflects mDNS q

Yuxiang Zhu 61 Jun 14, 2022
A compiling time static reflection framework for C++

static_reflect This is a fully compiling time static reflection lightweight framework for C++. It provides a very rich compile-time reflection functio

null 7 May 14, 2022
Templates, algorithms and data structures implemented and collected for programming contests.

Templates, algorithms and data structures implemented and collected for programming contests.

Shahjalal Shohag 1.8k Jun 27, 2022
Basic jam templates using Handmade libraries to get up and running quickly.

This is a selection of template projects to get up and running with for the Wheel Reinvention Jam. They are built on top of Handmade-inspired librarie

Handmade Network 13 Jun 19, 2022
URI Templates expansion and reverse-matching for C++

URI-template This library implements URI Template with full support up to Level 4 providing expansion and match capabilities. It requires c++17 compil

Tinkoff.ru 7 Jan 21, 2022
📚 C++ Templates 2ed 笔记:C++11/14/17 模板技术

?? C++ Templates 2ed 笔记:C++11/14/17 模板技术

null 1.1k Jun 28, 2022
Variadic recursive expression templates which look like ordinary (possibly nested) containers.

Variadic resursive expressions with lazy evaluation which look like nested containers LazyExpression is a header-only library written in C++17. It imp

null 9 Jan 6, 2022