Baike.dev
All toolsOnline toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
V

visit_struct

> 编程语言
Open source

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

655 stars0 likes1 views
WebsiteGitHub

About

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 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:

cpp
for (const auto & member : my_struct) {
  std::cerr 
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.

cpp
template 
void log_func(const char * name, const T & value) {
  std::cerr 
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:

…

c++ 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 spair;

VISITABLE(spair, p);

void do_nothing() const { }

VISITABLE(std::string, c);

END_VISITABLES; };

…

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


is similar to

```c++
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.

cpp
struct eq_visitor {
  bool result = true;

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

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

…

c++
visit_struct::visit_pointers(v);

is similar to

cpp
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

cpp
visit_struct::visit_types(v);

is similar to

cpp
v("a", visit_struct::type_c());
v("b", visit_struct::type_c());
v("c", visit_struct::type_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

cpp
visit_struct::visit_accessors(v);

is roughly similar to

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

…

c++
visit_struct::get(s);

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

get_name

cpp
visit_struct::get_name();
visit_struct::get_name(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

cpp
visit_struct::get_pointer();
visit_struct::get_pointer(s);

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

get_accessor

cpp
visit_struct::get_accessor();
visit_struct::get_accessor(s);

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

type_at

cpp
visit_struct::type_at

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

field_count

cpp
visit_struct::field_count();
visit_struct::field_count(s);

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

Other functions

get_name (no index)

cpp
visit_struct::get_name();
visit_struct::get_name(s);

…

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

…

c++
visit_struct::traits::is_visitable::value

…


struct Logging {};
struct Scripting {};

struct Foo {
    int i;
    double d;
    bool b;
    string s;
};

VISITABLE_STRUCT(Foo, i, d, b, s);
VISITABLE_STRUCT_IN_CONTEXT(Logging, Foo, i, s);
VISITABLE_STRUCT_IN_CONTEXT(Scripting, Foo, d, b, s);

Then, you can visit it in a special context by using calls such as

visit_struct::context::for_each(foo, visitor);

The whole API of free functions is reproduced under context to invoke those functions within the given context.

If you need to use the same context repeatedly, you can shorten this like so

using C = visit_struct::Context;
C::for_each(foo, visitor);
C::for_each(bar, visitor);

This also allows you to write functions that are generic over the context parameter if you like. The "default" context corresponds to the type void.

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

  • map-macro from swansontec
  • boost-hana from ldionne
  • pod flat reflection from apolukhin
  • self-aware structs from jckarter

Issues· 0 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

C++cpp11cpp14introspectionreflection

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言