Listing structure fields

There is a structure and functions:

struct S {
    T1 t1;
    T2 t2;
    // и т.д.
} s;

template <class T> f(const T& t, const char* name) { /* что-то */ }

The following (pseudo) code must work:

for (поле : поля(s))
    f(поле.значение, поле.имя);

That is, you need to somehow list all the fields of the structure, getting their type, value, and name as a string.

I know that there is no reflection in C++. But here we do not need reflection at the execution stage, we need reflection at the compilation or linking stage. In this case, you can pervert the declaration of the S structure in any way, but you can not touch the f function. But in as a result, either a structure or something structurally similar must still be created in some way, allowing access to the fields (s.t1, etc.).

You can't use third-party libraries, but you can use macros, templates, and generally all the available features of C++14.

Author: αλεχολυτ, 2017-02-06

3 answers

As suggested in the comments to the question by @KoVadim, you can use the so-called X Macro. With their use, your task is solved approximately as follows:

#include <iostream>

#define STRUCT_FIELDS \
    X(int, i) \
    X(float, f) \
    X(const char*, c)

struct S {
    #define X(type, name) type name;
        STRUCT_FIELDS
    #undef X
};

template<typename T> void f(const T& t, const char* name)
{
    std::cout << name << " = " << t << std::endl;
}

void printStruct(const S& s) {
    #define X(type, name) f(s.name, #name);
        STRUCT_FIELDS
    #undef X
}

int main(int argc, char* argv[]) {
    S s;
    s.i = 1;
    s.f = 10.1;
    s.c = "s";

    printStruct(s);
}

The result of the program:

i = 1
f = 10.1
c = s
 9
Author: αλεχολυτ, 2017-02-10 12:39:21

You can finish my solution. It is intended for parsing command-line parameters and putting them in a tuple, the elements of which can be accessed by name. At the same time, the names at the compilation stage are replaced by the indexes of the elements in the tuple.

How to work with it:

You need to inherit the class from opt-parser

#ifndef CONVOPTIONS_H
#define CONVOPTIONS_H
#include "opt-parser/optparser.h"

struct optlist
{
    static constexpr auto options=tuple_cat(
                OPTPARSER::makeO<std::string>("input"),
                OPTPARSER::makeO<std::string>("output")
                );

};

class ConvOptions:public OPTPARSER::Options<optlist>
{
public:
    ConvOptions(int argc,char** argv);
};

#endif // CONVOPTIONS_H

After that, you can access its fields by name:

ConvOptions opts(argc,argv);
$(opts,"input")
 3
Author: gbg, 2017-02-06 11:21:26

This is the answer skin with the steps to get it.
We still need to push links everywhere and add support for field names.

  1. http://ideone.com/6LOtIl
  2. http://ideone.com/ac32bj
  3. http://ideone.com/bG8q69
  4. http://ideone.com/umN51y
  5. http://ideone.com/bxgu9H
  6. http://ideone.com/rddXvG
#include <typeinfo>
#include <iostream>

using namespace std;

template <typename typed, int i = 0> struct enumerate
{
    static auto get(typed x) -> decltype(x.template get<i>()) { return x.template get<i>(); };
    const static bool has_next = !is_same<decltype(typed().template get<i+1>()), void>::value;
    typedef enumerate<typed, has_next?i+1:-1> next;

    template <template <class> class handler> static void go(typed x)
    {
        handler<decltype(get(x))>()(get(x));
        if (has_next) next::template go<handler>(x);
    }
};

template <typename typed> struct enumerate <typed, -1>
{
    static void get(typed x) {}
    const static bool has_next = false;
    typedef enumerate<typed, -1> next;

    template <template <class> class handler> static void go(typed x)
    {
    }
};

#define ENUMERABLE_FIELD(type, name, i) public: type name; \
                                        private: type get(key_t<i>) { return this->name; }

struct smth
{
    private: template <int i> struct key_t {};
    private: template <int i> void get(key_t<i>) {}

    ENUMERABLE_FIELD(int, x, 0)
    ENUMERABLE_FIELD(double, y, 1)

    public: template <int i> auto get() -> decltype(this->get(key_t<i>())) { return this->get(key_t<i>()); }
};

template <class typed> void print(const typed& t)
{
    cout << t << endl;
}

template <class typed> struct pass_to_print
{
    void operator () (typed t) { return print(t); }
};

int main()
{
    smth s = {1, 2.5};

    enumerate<smth>::go<pass_to_print>(s);

    return 0;
}
 1
Author: Qwertiy, 2017-02-07 22:04:03