A miniature library for struct-field reflection in C++

Overview

visit_struct

Build Status Appveyor status Boost licensed

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 members of a struct type.

Oftentimes, an application may contain several small "POD" datatypes, and one would like to be able to easily serialize and deserialize, print them in debugging info, and so on. Usually, the programmer has to write a bunch of boilerplate for each one of these, listing the struct members over and over again.

(This is only the most obvious use of structure visitors.)

Naively one would like to be able to write something like:

for (const auto & member : my_struct) {
  std::cerr << member.name << ": " << member.value << std::endl;
}

However, this syntax can never be legal in C++, because when we iterate using a for loop, the iterator has a fixed static type, and member.value similarly has a fixed static type. But the struct member types must be allowed to vary.

Visitors

The usual way to overcome issues like that (without taking a performance hit) is to use the visitor pattern. For our purposes, a visitor is a generic callable object. Suppose our struct looks like this:

struct my_type {
  int a;
  float b;
  std::string c;
};

and suppose we had a function like this, which calls the visitor v once for each member of the struct:

template <typename V>
void visit(const my_type & my_struct, V && v) {
  v("a", my_struct.a);
  v("b", my_struct.b);
  v("c", my_struct.c);
}

(For comparison, see also the function boost::apply_visitor from the boost::variant library, which similarly applies a visitor to the value stored within a variant.)

Then we can "simulate" the for-loop that we wanted to write in a variety of ways. For instance, we can make a template function out of the body of the for-loop and use that as a visitor.

template <typename T>
void log_func(const char * name, const T & value) {
  std::cerr << name << ": " << value << std::endl;
}

visit(my_struct, log_func);

Using a template function here means that even though a struct may contain several different types, the compiler figures out which function to call at compile-time, and we don't do any run-time polymorphism -- the whole call can often be inlined.

Basically we are solving the original problem in a very exact way -- there is no longer an explicit iterator, and each time the "loop body" can be instantiated with different types as needed.

If the loop has internal state or "output", we can use a function object (an object which overloads operator()) as the visitor, and collect the state in its members. Also in C++14 we have generic lambdas, which sometimes makes all this very terse.

Additionally, while making a visitor is sometimes more verbose than you'd like, it has an added benefit that generic visitors can be used and reused many times. Often, when doing things like logging or serialization, you don't want each struct to get a different implementation or policy, you want to reuse the same code for all of them.

Reflection

So, if we have a template function visit for our struct, it may let us simplify code and promote code reuse.

However, that means we still have to actually define visit for every struct we want to use it with, and possibly several versions of it, taking const my_type &, my_type &, my_type &&, and so on. That's also quite a bit of repetitive code, and the whole point of this is to reduce repetition.

Again, ideally we would be able to do something totally generic, like,

template <typename S, typename V>
void for_each(S && s, V && v) {
  // Insert magic here...
  for (auto && member : s) {
    v(member.name, member.value);
  }
}

where both the visitor and struct are template parameters, and use this to visit the members of any struct.

Unfortunately, current versions of C++ lack reflection. It's not possible to programmatically inspect the list of members of a generic class type S, using templates or anything else standard, even if S is a complete type (in which case, the compiler obviously knows its members). If we're lucky we might get something like this in C++20, but right now there's no way to actually implement the fully generic for_each.

This means that any implementation of for_each requires some help, usually in the form of registration macros or similar.

Overview

This library permits the following syntax in a C++11 program:

struct my_type {
  int a;
  float b;
  std::string c;
};

VISITABLE_STRUCT(my_type, a, b, c);



struct debug_printer {
  template <typename T>
  void operator()(const char * name, const T & value) {
    std::cerr << name << ": " << value << std::endl;
  }
};

void debug_print(const my_type & my_struct) {
  visit_struct::for_each(my_struct, debug_printer{});
}

Intuitively, you can think that the macro VISITABLE_STRUCT is defining overloads of visit_struct::for_each for your structure.

In C++14 this can be made more succinct using a lambda:

void debug_print(const my_type & my_struct) {
  visit_struct::for_each(my_struct,
    [](const char * name, const auto & value) {
      std::cerr << name << ": " << value << std::endl;
    });
}

These two things, the macro VISITABLE_STRUCT and the function visit_struct::for_each, represent the most important functionality of the library.

A nice feature of visit_struct is that for_each always respects the C++11 value category of it's arguments. That is, if my_struct is a const l-value reference, non-const l-value reference, or r-value reference, then for_each will pass each of the fields to the visitor correspondingly, and the visitor is also forwarded properly.

It should be noted that there are already libraries that permit iterating over a structure like this, such as boost::fusion, which does this and much more. Or boost::hana, which is like a modern successor to boost::fusion which takes advantage of C++14.

However, our library can be used as a single-header, header-only library with no external dependencies. The core visit_struct.hpp is in total about four hundred lines of code, depending on how you count, and is fully functional on its own. For some applications, visit_struct is all that you need.

Additionally, the syntax for doing these kind of visitations is (IMO) a little nicer than in fusion or hana. And visit_struct has much better compiler support right now than hana. hana requires a high level of conformance to C++14. It only supports gcc-6 and up for instance, and doesn't work with any versions of MSVC. (Its support on clang is quite good.) visit_struct can be used with many "first generation C++11 compilers" that are now quite old, like gcc-4.8 and MSVC 2013.

Note: The macro VISITABLE_STRUCT must be used at filescope, an error will occur if it is used within a namespace. You can simply include the namespaces as part of the type, e.g.

VISITABLE_STRUCT(foo::bar::baz, a, b, c);

Compatibility with boost::fusion

visit_struct also has support code so that it can be used with "fusion-adapted structures". That is, any structure that boost::fusion knows about, can also be used with visit_struct::for_each, if you include the extra header.

#include <visit_struct/visit_struct_boost_fusion.hpp>

This compatability header means that you don't have to register a struct once with fusion and once with visit_struct. It may help if you are migrating from one library to the other.

Compatiblity with boost::hana

visit_struct also has a similar compatibility header for boost::hana.

#include <visit_struct/visit_struct_boost_hana.hpp>

"Intrusive" Syntax

A drawback of the basic syntax is that you have to repeat the field member names.

This introduces a maintenance burden: What if someone adds a field member and doesn't update the list?

  1. It is possible to write a static assertion that all of the members are registered, by comparing sizeof the struct with what it should be given the known registered members. (See test_fully_visitable.cpp )
  2. It may be useful to register only a subset of the field members for serialization.
  3. It may be a requirement for you that you cannot change the header where the struct is defined, and you still want to visit it, so the first syntax may be pretty much the only option for you.

However, none of these changes the fact that with the first syntax, you have to write the names twice.

If visit_struct were e.g. a clang plugin instead of a header-only library, then perhaps we could make the syntax look like this:

struct my_type {
  __attribute__("visitable") int a;
  __attribute__("visitable") float b;
  __attribute__("visitable") std::string c;
};

void debug_print(const my_type & my_struct) {
  __builtin_visit_struct(my_struct,
    [](const char * name, const auto & member) {
      std::cout << name << ": " << member << std::endl;
    });
}

We don't offer a clang plugin like this, but we do offer an additional header, visit_struct_intrusive.hpp which uses macros to get pretty close to this syntax, and which is portable:

struct my_type {
  BEGIN_VISITABLES(my_type);
  VISITABLE(int, a);
  VISITABLE(float, b);
  VISITABLE(std::string, c);
  END_VISITABLES;
};

This declares a structure which is essentially the same as

struct my_type {
  int a;
  float b;
  std::string c;
};

There are no additional data members defined within the type, although there are some "secret" static declarations which are occurring. (Basically, a bunch of typedef's.) That's why it's "intrusive". There is still no run-time overhead.

Each line above expands to a separate series of declarations within the body of my_type, and arbitrary other C++ declarations may appear between them.

struct my_type {

  int not_visitable;
  double not_visitable_either;

  BEGIN_VISITABLES(my_type);
  VISITABLE(int, a);
  VISITABLE(float, b);

  typedef std::pair<std::string, std::string> spair;

  VISITABLE(spair, p);

  void do_nothing() const { }

  VISITABLE(std::string, c);

  END_VISITABLES;
};

When visit_struct::for_each is used, each member declared with VISITABLE will be visited, in the order that they are declared.

The benefits of this version are that, you don't need to type all the member names twice, and you don't need to jump out of your namespaces back to filescope in order to register a struct. The main drawbacks are that this is still somewhat verbose, the implementation is a bit more complicated, and this one may not be useful in some cases, like if the struct you want to visit belongs to some other project and you can't change its definition.

Binary Vistation

visit_struct also supports visiting two instances of the same struct type at once.

For instance, the function call

visit_struct::for_each(s1, s2, v);

is similar to

v("a", s1.a, s2.a);
v("b", s1.b, s2.b);
v("c", s1.c, s2.c);

This is useful for implementing generic equality and comparison operators for visitable structures, for instance. Here's an example of a generic function struct_eq which compares any two visitable structures for equality using operator == on each field, and which short-circuits properly.

struct eq_visitor {
  bool result = true;

  template <typename T>
  void operator()(const char *, const T & t1, const T & t2) {
    result = result && (t1 == t2);
  }
};

template <typename T>
bool struct_eq(const T & t1, const T & t2) {
  eq_visitor vis;
  visit_struct::for_each(t1, t2, vis);
  return vis.result;
}

On clang 3.5 with a simple example, this compiles the same assembly as a hand-rolled equality operator. See it on godbolt compiler explorer.

Visitation without an instance

Besides iteration over an instance of a registered struct, visit_struct also supports visiting the definition of the struct. In this case, instead of passing you the field name and the field value within some instance, it passes you the field name and the pointer to member corresponding to that field.

Suppose that you are serializing many structs in your program as json. You might also want to be able to emit the json schema associated to each struct that your program is expecting, especially to produce good diagnostics if loading the data fails. When you visit without an instance, you can get all the type information for the struct, but you don't have to actually instantiate it, which might be complicated or expensive.

visit_pointers

The function call

visit_struct::visit_pointers<my_type>(v);

is similar to

v("a", &my_type::a);
v("b", &my_type::b);
v("c", &my_type::c);

These may be especially useful when you have a C++14 compiler which has proper constexpr support. In that case, these visitations are constexpr also, so you can use this for some nifty metaprogramming purposes. (For an example, check out test_fully_visitable.cpp.)

There are two alternate versions of this visitation.

visit_types

This function call

visit_struct::visit_types<my_type>(v);

is similar to

v("a", visit_struct::type_c<a>());
v("b", visit_struct::type_c<b>());
v("c", visit_struct::type_c<c>());

Here, type_c is just a tag, so that your visitor can take appropriate action using tag dispatch. This syntax is a little simpler than the pointer to member syntax.

visit_accessors

In the third version, you get passed an "accessor", that is, a function object that implements the function computed by the pointer-to-member.

This call

visit_struct::visit_accessors<my_type>(v);

is roughly similar to

v("a", [](auto s) { return s.a; });
v("b", [](auto s) { return s.b; });
v("c", [](auto s) { return s.c; });

Accessors are convenient because they can be used easily with other standard algorithms that require function objects, they avoid the syntax of member pointers, and because they are well-supported by hana and fusion.

Much thanks to Jarod42 for this patch and subsequent suggestions.

Note: The compatibility headers for boost::fusion and boost::hana don't currently support visit_pointers. They only support visit_types, and visit_accessors.

To my knowledge, there is no way to get the pointers-to-members from boost::fusion or boost::hana. That is, there is no publicly exposed interface to get them.

If you really want or need to be able to get the pointers to members, that's a pretty good reason to use visit_struct honestly. If you think you need the fusion or hana compatibility, then you should probably avoid anything to do with member pointers here, and stick to accessors instead.

Tuple Methods, Indexed Access

for_each is quite powerful, and by crafting special visitors, there is a lot that you can do with it.

However, one thing that you cannot easily do is implement std::tuple methods, like std::get<i> to get the i'th member of the struct. Most if not all libraries that support struct-field reflection support this in some way. So, we decided that we should support this also.

We didn't change our implementation of for_each, which works well on all targets. But we have added new functions which allow indexed access to structures, and to the metadata.

get

visit_struct::get<i>(s);

Gets (a reference to) the i'th visitable member of the struct s. Index is 0-based. Analogous to std::get.

get_name

visit_struct::get_name<i, S>();
visit_struct::get_name<i>(s);

Gets a string constant representing the name of the i'th member of the struct type S. The struct type may be passed as a second template parameter. If an instance is available, it may be passed as an argument, and the struct type will be deduced (the argument will not be accessed).

get_pointer

visit_struct::get_pointer<i, S>();
visit_struct::get_pointer<i>(s);

Gets the pointer-to-member for the i'th visitable element of the struct type S.

get_accessor

visit_struct::get_accessor<i, S>();
visit_struct::get_accessor<i>(s);

Gets the accessor corresponding to the i'th visitable element of the struct type S.

type_at

visit_struct::type_at<i, S>

This alias template gives the declared type of the i'th member of S.

field_count

visit_struct::field_count<S>();
visit_struct::field_count(s);

Gets a size_t which tells how many visitable fields there are.

Other functions

get_name (no index)

visit_struct::get_name<S>();
visit_struct::get_name(s);

Gets a string constant representing the name of the structure. The string here is the token that you passed to the visit_struct macro in order to register the structure.

This could be useful for error messages. E.g. "Failed to match json input with struct of type 'foo', layout: ..."

There are other ways to get a name for the type, such as typeid, but it has implementation-defined behavior and sometimes gives a mangled name. However, the visit_struct name might not always be acceptable either -- it might contain namespaces, or not, depending on if you use standard or intrusive syntax, for instance.

Since the programmer is already taking the trouble of passing this name into a macro to register the struct, we think we might as well give programmatic access to that string if they want it.

Note that there is no equivalent feature in fusion or hana to the best of my knowledge, so there's no support for this in the compatibility headers.

apply_visitor

visit_struct::apply_visitor(v, s);
visit_struct::apply_visitor(v, s1, s2);

This is an alternate syntax for for_each. The only difference is that the visitor comes first rather than last.

Historically, apply_visitor is a much older part of visit_struct than for_each. Its syntax is similar to boost::apply_visitor from the boost::variant library. For a long time, apply_visitor was the only function in the library.

However, experience has shown that for_each is little nicer syntax than apply_visitor. It reads more like a for loop -- the bounds of the loop come first, which are the structure, then the body of the loop, which is repeated.

Additionally, in C++14 one may often use generic lambdas. Then the code is a little more readable if the lambda comes last, since it may span several lines of code.

(I won't say I wasn't influenced by ldionne's opinion. He makes this same point in the boost::hana docs here.)

So, nowadays I prefer and recommend for_each. The original apply_visitor syntax isn't going to be deprecated or broken though.

traits::is_visitable

visit_struct::traits::is_visitable<S>::value

This type trait can be used to check if a structure is visitable. The above expression should resolve to boolean true or false. I consider it part of the forward-facing interface, you can use it in SFINAE to easily select types that visit_struct knows how to use.

Limits

When using VISITABLE_STRUCT, the maximum number of members which can be registered is visit_struct::max_visitable_members, which is by default 69.

When using the intrusive syntax, the maximum number of members is visit_struct::max_visitable_members_intrusive, which is by default 100.

These limits can both be increased, see the source comments and also IMPLEMENTATION_NOTES.md.

Compiler Support

visit_struct targets C++11 -- you need to have r-value references at least, and for the intrusive syntax, you need variadic templates also.

visit_struct is known to work with versions of gcc >= 4.8.2 and versions of clang >= 3.5.

The appveyor build tests against MSVC 2013, 2015, 2017.

MSVC 2015 is believed to be fully supported.

For MSVC 2013, the basic syntax is supported, the intrusive syntax doesn't work there and now isn't tested. Again, patches welcome.

Much thanks again to Jarod42 for significant patches related to MSVC support.

Constexpr Correctness

visit_struct attempts to target three different levels of constexpr support.

  • No support
  • C++11 support
  • C++14 extended support

This is controlled by two macros VISIT_STRUCT_CONSTEXPR and VISIT_STRUCT_CXX14_CONSTEXPR. We use these tokens where we would use the constexpr keyword.

In the visit_struct.hpp header, these macros are defined to either constexpr or nothing.

We attempt to guess the appropriate setting by inspecting the preprocessor symbols __cplusplus and _MSC_VER.

If it doesn't work on your compiler, please open a github issue, especially if you know how to fix it :)

In the meantime, if you don't want to tweak the headers for your project, you can override the behavior by defining these macros yourself, before including visit_struct.hpp. If the header sees that you have defined them it won't touch them and will defer to your settings. In most cases this should not be necessary.

On gcc and clang, we assume at least C++11 constexpr support. If you enabled a later standard using -std=..., we turn on the full constexpr.

On MSVC currently the settings are:

  • VS2013: no support
  • VS2015: C++11 support
  • VS2017: C++14 extended support

Licensing and Distribution

visit_struct is available under the boost software license.

See also

Issues
  • Not compiling using Intel 16.0 (with Visual Studio 2013)

    Not compiling using Intel 16.0 (with Visual Studio 2013)

    Hi,

    very nice small thing! I tested it using Intel Compiler 16.0 (under Visual Studio 2013) but it does not work, unfortunately. Is there a chance that it could be fixed? Error message is " error #54: too few arguments in invocation of macro "VISIT_STRUCT_PP_MAP1". Thank you! With boost/Fusion it is working, but I strongly prefer your approach because of the much much smaller source-code-footprint (and also usage is more elegant).

    opened by nick0000 10
  • Add

    Add "indexed" visitors

    This patch adds a new set of visitor functions that include the index of the field within the struct, in addition to its name. The indices are 0-based, and the field ordering is the same as the ordering of the parameters passed to the VISITABLE_STRUCT macro.

    We also provide two new helpers in the visit_struct namespace. One returns the number of fields in the struct, and the other gives you access to the field index enum.

    opened by dcreager 7
  • if constexpr type deduction in visitor?

    if constexpr type deduction in visitor?

    I'm basically trying to determine type using if constexpr

    #include <cxxabi.h>
    #include <string>
    
    const std::string demangle(const char* name) {
        int status = -4;
        char* res = abi::__cxa_demangle(name, NULL, NULL, &status);
        const char* const demangled_name = (status==0)?res:name;
        std::string ret_val(demangled_name);
        free(res);
        return ret_val;
    }
    
    #include <iostream>
    #include <type_traits>
    
    #include "visit_struct.hpp"
    
    struct something
    {
        char a[12];
        char lot[2];
        char of[35];
        char chars[20];
        int x;
    };
    
    VISITABLE_STRUCT(something, a, lot, of, chars, x);
    
    something s1{"a", "l", "o", "c", 123};
    
    void attempt1()
    {
        visit_struct::for_each(s1, [&](const char* name, const auto& value) {
          std::cout << name << " = " << demangle(typeid(value).name()) << "\n";
    
          using value_type = decltype(value);
          
          if constexpr (std::is_same_v<value_type, int>)
            std::cout << name << " is int\n";
          else if constexpr (std::is_array_v<value_type>) {
            std::cout << name << " is array\n";
            
            using array_type = typename std::remove_all_extents<value_type>::type;
            if constexpr (std::is_same_v<char, array_type>) {
              std::cout << name << " is char array\n";
              return;
            }
          }
          else
            std::cout << name << " is not char array or int\n";
        });
    }
    
    int main()
    {
      attempt1();
    }
    

    Output:

    a = char [12]
    a is not char array or int
    lot = char [2]
    lot is not char array or int
    of = char [35]
    of is not char array or int
    chars = char [20]
    chars is not char array or int
    x = int
    x is not char array or int
    

    Doing

    using value_type = visit_struct::type_at<0, something>;
    

    seems to work for char arrays, but it doesn't solve my problem

    opened by muttleyxd 2
  • Non const visitor function arguments

    Non const visitor function arguments

    Hello,

    This is an awesome library. It has saved me a lot of time and effort.

    Currently I'm attempting to use visit_struct to implement generic deep cloning. My idea was to use the binary for_each method to recursively clone members from one struct to the other.

    struct copy_visitor;
    
    template <typename T,
        std::enable_if_t<visit_struct::traits::is_visitable<std::decay_t<T>>::value, int> = 0>
    T clone_struct(const T& src)
    {
        T dst;
        copy_visitor vis;
        visit_struct::for_each(dst, src, vis);
        return dst;
    }
    
    template <typename T,
        std::enable_if_t<!visit_struct::traits::is_visitable<std::decay_t<T>>::value, int> = 0>
    T clone_struct(const T& src)
    {
        T dst;
        dst = src;
        return dst;
    }
    
    struct copy_visitor {
        template <typename T>
        void operator()(const char* name, T& dst, T& src) {
            dst = clone_struct(src);
        }
    };
    

    My issue is that all of the visitor functions want to operate on const arguments. Is there a way to implement something along these lines with the existing interfaces provided? A way to set a property by name or index at runtime?

    opened by kbirk 1
  • Add `field_count` constexpr function

    Add `field_count` constexpr function

    This patch adds a new function that returns the number of fields in a visitable struct. The function is constexpr, so that it can be used (for instance) to declare the size of arrays at compile time.

    opened by dcreager 1
  • compile error with boost fusion

    compile error with boost fusion

    • compiler: g++ 11 std=20
    • boost 1.77
    • visit_struct: 8c91d22

    source code trigger the error

    #include <visit_struct/visit_struct_boost_fusion.hpp>
    BOOST_FUSION_DEFINE_STRUCT((), FusionParams,
                               (int, param1)(double, param2))
    
    template <class Params>
    constexpr void PopulateVsFoo(const Params& params) {
      visit_struct::for_each(params, []<typename T>(const char*, const T&) {});
    }
    
    void test() {
      FusionParams p;
      PopulateVsFoo(p);
    }
    

    compile error:

    In file included from /param_test.cpp:1:
    /visit_struct/include/visit_struct/visit_struct_boost_fusion.hpp: In instantiation of 'static void visit_struct::traits::visitable<S, typename std::enable_if<std::is_same<typename boost::mpl::sequence_tag<Sequence>::type, boost::fusion::fusion_sequence_tag>::value>::type>::apply(V&&, T&&) [with V = PopulateVsFoo<{anonymous}::FusionParams>(const {anonymous}::FusionParams&)::<lambda(const char*, const T&)>; T = const {anonymous}::FusionParams&; S = {anonymous}::FusionParams]':
    /visit_struct/include/visit_struct/visit_struct.hpp:144:47:   required from 'constexpr typename std::enable_if<visit_struct::traits::is_visitable<typename visit_struct::traits::clean<S>::type>::value>::type visit_struct::for_each(S&&, V&&) [with V = PopulateVsFoo<{anonymous}::FusionParams>(const {anonymous}::FusionParams&)::<lambda(const char*, const T&)>; S = const {anonymous}::FusionParams&; typename std::enable_if<visit_struct::traits::is_visitable<typename visit_struct::traits::clean<S>::type>::value>::type = void; typename visit_struct::traits::clean<S>::type = {anonymous}::FusionParams]'
    /param_test.cpp:6:25:   required from 'constexpr void PopulateVsFoo(const Params&) [with Params = {anonymous}::FusionParams]'
    /param_test.cpp:11:16:   required from here
    /visit_struct/include/visit_struct/visit_struct_boost_fusion.hpp:133:21: error: use of deleted function 'visit_struct::traits::visitable<{anonymous}::FusionParams, void>::fusion_visitor<PopulateVsFoo<{anonymous}::FusionParams>(const {anonymous}::FusionParams&)::<lambda(const char*, const T&)>&&, const {anonymous}::FusionParams&>::fusion_visitor(const visit_struct::traits::visitable<{anonymous}::FusionParams, void>::fusion_visitor<PopulateVsFoo<{anonymous}::FusionParams>(const {anonymous}::FusionParams&)::<lambda(const char*, const T&)>&&, const {anonymous}::FusionParams&>&)'
      133 |     fusion::for_each(Indices(), fv);
          |     ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~
    /visit_struct/include/visit_struct/visit_struct_boost_fusion.hpp:66:10: note: 'visit_struct::traits::visitable<{anonymous}::FusionParams, void>::fusion_visitor<PopulateVsFoo<{anonymous}::FusionParams>(const {anonymous}::FusionParams&)::<lambda(const char*, const T&)>&&, const {anonymous}::FusionParams&>::fusion_visitor(const visit_struct::traits::visitable<{anonymous}::FusionParams, void>::fusion_visitor<PopulateVsFoo<{anonymous}::FusionParams>(const {anonymous}::FusionParams&)::<lambda(const char*, const T&)>&&, const {anonymous}::FusionParams&>&)' is implicitly deleted because the default definition would be ill-formed:
       66 |   struct fusion_visitor {
          |          ^~~~~~~~~~~~~~
    /visit_struct/include/visit_struct/visit_struct_boost_fusion.hpp:66:10: error: copying non-static data member 'PopulateVsFoo<{anonymous}::FusionParams>(const {anonymous}::FusionParams&)::<lambda(const char*, const T&)>&& visit_struct::traits::visitable<{anonymous}::FusionParams, void>::fusion_visitor<PopulateVsFoo<{anonymous}::FusionParams>(const {anonymous}::FusionParams&)::<lambda(const char*, const T&)>&&, const {anonymous}::FusionParams&>::visitor' of rvalue reference type
    In file included from /boost/fusion/include/for_each.hpp:11,
                     from /visit_struct/include/visit_struct/visit_struct_boost_fusion.hpp:12,
                     from /param_test.cpp:1:
    /boost/fusion/algorithm/iteration/for_each.hpp:41:37: note:   initializing argument 2 of 'constexpr typename boost::enable_if<boost::fusion::traits::is_sequence<Sequence> >::type boost::fusion::for_each(const Sequence&, F) [with Sequence = boost::mpl::range_c<unsigned int, 0, 2>; F = visit_struct::traits::visitable<{anonymous}::FusionParams, void>::fusion_visitor<PopulateVsFoo<{anonymous}::FusionParams>(const {anonymous}::FusionParams&)::<lambda(const char*, const T&)>&&, const {anonymous}::FusionParams&>; typename boost::enable_if<boost::fusion::traits::is_sequence<Sequence> >::type = void]'
       41 |     for_each(Sequence const& seq, F f)
          |                                   ~~^
    
    opened by zhuoqiang 0
  • Clang: Compilation error with visitable struct include template class

    Clang: Compilation error with visitable struct include template class

    I ran into problems getting the following to work in Clang 12.0.0, using the latest master of visit_struct:

    template <typename T>
    struct TemplatedStruct
    {
    	struct Visitable
    	{
    		BEGIN_VISITABLES(Visitable);
    		VISITABLE(int, a);
    		VISITABLE(int, b);
    		VISITABLE(int, c);
    		END_VISITABLES;
    	};
    };
    
    int main(int argc, char** argv)
    {
    	TemplatedStruct<int> test;
    	return 0;
    }
    

    For this minimal example, Clang outputs the following:

    main.cpp:12:3: error: functions that differ only in their return type cannot be overloaded VISITABLE(int, c); ^~~~~~~~~~~~~~~~~ visit_struct/include/visit_struct/visit_struct_intrusive.hpp:382:3: note: expanded from macro 'VISITABLE' Visit_Struct_Get_Visitables__(::visit_struct::detail::Rank<VISIT_STRUCT_GET_REGISTERED_MEMBERS::size + 1>);
    ^ main.cpp:11:3: note: previous declaration is here VISITABLE(int, b); ^~~~~~~~~~~~~~~~~ visit_struct/include/visit_struct/visit_struct_intrusive.hpp:382:3: note: expanded from macro 'VISITABLE' Visit_Struct_Get_Visitables__(::visit_struct::detail::Rank<VISIT_STRUCT_GET_REGISTERED_MEMBERS::size + 1>);
    ^ 1 error generated.

    Both GCC and MSVC compile this example just fine, only Clang has problems with it. The problem happens only with visitable structs inside template classes. Without the template, it works just fine. Curiously, it also works fine with exactly 2 visitables in the struct - it starts failing for 3.

    opened by alemariusnexus 0
  • Get member variable pointer at compile-time

    Get member variable pointer at compile-time

    visit_pointers passes the member variable pointers to the given callable. At this point, the constexpr-ness of the pointer is lost. visit_accessors passes accessor objects to the given callable. accessor has the pointer as a template parameter. I propose adding this to accessor:

    static VISIT_STRUCT_CONSTEXPR auto value = ptr;
    

    This would make it possible to access the pointer as a compile-time constant.

    opened by indianakernick 1
  • Separate parser program for 'intrusive'

    Separate parser program for 'intrusive'

    I've begun writing a short C++ program to take a normal header file like so:

    	struct Monkey {
    		int a;
    		float b;
    		char c[5];
    	};
    

    and turn it into:

    	typedef char char_5[5];
    
    	struct Monkey {
    		BEGIN_VISITABLES(Monkey);
    		VISITABLE(int, a);
    		VISITABLE(float, b);
    		VISITABLE(char_5, c);
    		END_VISITABLES;
    	};
    

    I can show you what I have so far.

    opened by healytpk 0
  • Feature request: optional visitable metadata

    Feature request: optional visitable metadata

    I'm using visit_struct to declare structs with parameters and automatically link specific option values to GUI widgets. It works great for simple cases.

    However, more often than not I would like to define additional metadata where I specify the options in the struct. For example for numeric options, I would like to specify a range with minimum and maximum acceptable value, such that a slider with the correct limits can be created in the GUI.

    I'm currently working around this by using some custom types for the struct member variables, e.g.

    template <class T>
    struct ranged {
      using ValueType = T;
      T val {0};
      const T min {0};
      const T max {100};
    };
    
    struct Foo {

        BEGIN_VISITABLES(Foo);

        VISITABLE_INIT(bool, foo, true);
        VISITABLE_DIRECT_INIT(ranged<double>, bar, {0.5, 0, 1});
        END_VISITABLES;

    };
    

    However this is a bit awkward, as a I have to access the actual value always via the val member.

    Foo foo;
    foo.bar.val = 0.1;
    

    Would it be possible to extend visit_struct to allow declaring additional (typed) meta-data for every member in such a way, that it is transparent (i.e. foo.bar refers to the actual value, not some enclosing struct containing also the metadata), and it is (optionally) accessible during visitation.

    I guess for my use-cases I so far only need constant meta-data.

    I'm mostly interested in the intrusive syntax, since I want to define "all in one place", but one might also consider adding metadata to existing structs with a non-intrusive syntax.

    opened by NikolausDemmel 2
Owner
Art (in picture) by: https://kevintitzer.com/Sludge-From-NY-NJ
null
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++ 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.2k Jun 20, 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 modern compile-time reflection library for C++ with support for overloads, templates, attributes and proxies

refl-cpp v0.12.1 Documentation refl-cpp encodes type metadata in the type system to allow compile-time reflection via constexpr and template metaprogr

Veselin Karaganev 658 Jun 23, 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
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
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
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 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 346 Jun 23, 2022
Structy is an irresponsibly dumb and simple struct serialization/deserialization library for C, Python, and vanilla JavaScript.

Structy Structy is an irresponsibly dumb and simple struct serialization/deserialization library for C, Python, and vanilla JavaScript. You can think

Stargirl Flowers 47 Jun 19, 2022
is a c++20 compile and runtime Struct Reflections header only library.

is a c++20 compile and runtime Struct Reflections header only library. It allows you to iterate over aggregate type's member variables.

RedSkittleFox 4 Apr 18, 2022
convert json/xml/bson to c++ struct

xpack 用于在C++结构体和json/xml之间互相转换, bson在xbson中支持。 只需要头文件, 无需编译库文件。 具体可以参考example的例子 基本用法 容器支持 FLAG 别名 位域 继承 枚举 自定义编解码 char数组 第三方类和结构体 格式化缩进 XML数组 Qt支持 重要

null 380 Jun 23, 2022
Parse command line arguments by defining a struct

Parse command line arguments by defining a struct Quick Start #include <structopt/app.hpp> struct Options { // positional argument // e.g., .

Pranav 399 Jun 20, 2022
Visual Studio Extension for C++ struct memory layout visualization

Visual Studio Extension for C++ struct memory layout visualization

Ramon Viladomat 365 Jun 27, 2022
Source code and raw content for NEON STRUCT: Desperation Column

NEON STRUCT: Desperation Column copyright © 2021 Minor Key Games, LLC. Source code and raw content for NEON STRUCT: Desperation Column are release

David Pittman 7 Jun 12, 2022
Platform independent Near Field Communication (NFC) library

*- * Free/Libre Near Field Communication (NFC) library * * Libnfc historical contributors: * Copyright (C) 2009 Roel Verdult * Copyright (C) 2009

null 1.2k Jun 24, 2022
Arduino library for SPI and I2C access to the PN532 RFID/Near Field Communication chip

Adafruit-PN532 This is a library for the Adafruit PN532 NFC/RFID breakout boards This library works with the Adafruit NFC breakout https://www.adafrui

Adafruit Industries 337 Jun 23, 2022
Visualize punctual charged particles and their electrical field line

Hello. My C++ learning journey continues and today I wrote a GUI program to visualize punctual charges and their electrical field. The physics and mat

Long Nguyen 16 Jul 20, 2021
High performance, easy-to-use, and scalable machine learning (ML) package, including linear model (LR), factorization machines (FM), and field-aware factorization machines (FFM) for Python and CLI interface.

What is xLearn? xLearn is a high performance, easy-to-use, and scalable machine learning package that contains linear model (LR), factorization machin

Chao Ma 3k Jun 24, 2022
GPU 3D signed distance field generator, written with DirectX 11 compute shader

GPU SDF Generator GPU 3D signed distance field generator, written with DirectX 11 compute shader Building git clone --recursive https://github.com/Air

Z Guan 23 May 20, 2022
Project in the field of Advanced Programming Techniques by prof. Brian Kernighan, inspired by real event that made Ken Thompson invent grep command

ed_to_grep Project in the field of Advanced Programming Techniques by prof. Brian Kernighan, inspired by real event that made Ken Thompson invent grep

mirko 1 Oct 19, 2021
ESP32 + GitHub Actions + Husarnet. A boilerplate project for ESP32 allowing in-field firmware update using GitHub Actions workflow.

esp32-internet-ota ESP32 + GitHub Actions + Husarnet. A boilerplate project for ESP32 allowing in-field firmware update using GitHub Actions workflow.

Husarnet 28 Apr 30, 2022
Finite Field Operations on GPGPU

ff-gpu Finite Field Operations on GPGPU Background In recent times, I've been interested in Finite Field operations, so I decided to implement few fie

Anjan Roy 4 Apr 21, 2022
ESP Insights is a remote diagnostics solution that allows users to remotely monitor the health of ESP devices in the field.

ESP Insights is a remote diagnostics solution that allows users to remotely monitor the health of ESP devices in the field.

Espressif Systems 27 Jun 9, 2022
Gramado Field - It is a 64bit operating system.

Welcome to Gramado OS - Codename 'Field' This repository has the source code for: Gramado boot loader Gramado kernel Gramado Window Server Application

Gramado 9 Apr 9, 2022
The whole design is modular, parametric (cost and others), field repairable, and super extensible

Easy-Transceiver The whole design is modular, parametric (cost and others), field repairable, and super extensible. It is almost trivial to add suppor

Dhiru Kholia 2 Apr 22, 2022
A field war simulator in console with C language, inspiring from BattleField 1.

CBattleField 一个基于C语言的在控制台上的战争模拟器。其灵感来源于游戏《战地1》 A field war simulator in console with C language, inspiring from BattleField 1. 你可以在这里查看演示。 You could w

1887415157 14 May 3, 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