2

I know, that in C++, there is no reflection. However, I need to iterate variables in compile time and generate code for them.

What I have is this:

class Foo {
   int x;
   double y;

   Foo() {
     Register(x, "x");
     Register(y, "y");
   }

   template <typename T>
   void Register(T val, const std::string & key){
      //do something with val and key
   }
}

However, I would like to call Register automatically for each variable. Sometimes, there could be a lot of them and its easy to forgot to register something or make typo in key (key should be ideally same as variable name).

I have thought of something like CREATE_AND_REGISTER(x, "x") macro, but I dont know if it is possible. I have seen some Boost based solutions, but I need just plain C+11/14.

Martin Perry
  • 9,232
  • 8
  • 46
  • 114
  • IOW you want reflection though you know there isn't any. Life is sometimes unfair and we can't do much about it. Why do you want reflection? – n. m. could be an AI Aug 27 '17 at 08:10
  • It looks like you may want to use something like `map< string, variant >` or maybe tagged tuple. If you still want to iterate over class fields then you'll need to do some hand-written reflection macros like those implemented in [boost::hana](http://www.boost.org/doc/libs/1_65_0/libs/hana/doc/html/index.html#tutorial-introspection-adapting). If you want just "plain C++11/14" then you will most likely just end up reimplement existing stuff from boost yourself. – user7860670 Aug 27 '17 at 08:11
  • 1
    The simplest (but ugly) way would be to use x-macros. – HolyBlackCat Aug 27 '17 at 08:11
  • Take a look at this answer https://stackoverflow.com/a/10887421/8491726 - you can use same approach to generate list of members of the class and list of calls to Register to be always synchronized – Artemy Vysotsky Aug 27 '17 at 08:28

1 Answers1

1

I don't think it's possible to do what do exactly want ("no reflection") but ...

I have thought of something like CREATE_AND_REGISTER(x, "x") macro, but I dont know if it is possible

I think c-macros are distilled evil but... if you really want and if you accept that int x and float y are wrapped in structs... I propose the following macro

#define DEFandREGIS(type, name, def)            \
                                                \
   struct bar_##name                            \
    {                                           \
      type var;                                 \
                                                \
      bar_##name (type val, foo * t) : var{val} \
       { t->regis(var, #name); }                \
    };                                          \
                                                \
   bar_##name b##name{def, this};

that receive the type (int, float, ...), the name (x, y, ...) and a default value

So (if default values are enough for you) you can define foo as follows

struct foo
 {
   DEFandREGIS(int, x, 1)
   DEFandREGIS(float, y, 2.2)

   template <typename T>
   void regis (T val, std::string const & key)
    { std::cout << "regis(" << val << ", \"" << key << "\")" << std::endl; }
 };

and, defining a foo variable, you get printed

 regis(1, "x")
 regis(2.2, "y")

If you want a foo constructor that can create x and y with different (not default values), you can confirm the default constructor (if you want) and create another constructor as follows

foo () = default;

foo (int x0, float y0) : bx{x0, this}, by{y0, this}
 { }

If your Register() method (regis(), in my example) can be a static method, there is no need of this pointer in bar##x and you can simplify a little the example.

The following is a full working example

#include <iostream>

#define DEFandREGIS(type, name, def)            \
                                                \
   struct bar_##name                            \
    {                                           \
      type var;                                 \
                                                \
      bar_##name (type val, foo * t) : var{val} \
       { t->regis(var, #name); }                \
    };                                          \
                                                \
   bar_##name b##name{def, this};

struct foo
 {
   DEFandREGIS(int, x, 1)
   DEFandREGIS(float, y, 2.2f)

   foo () = default;

   foo (int x0, float y0) : bx{x0, this}, by{y0, this}
    { }

   template <typename T>
   void regis (T val, std::string const & key)
    { std::cout << "regis(" << val << ", \"" << key << "\")" << std::endl; }
 };

int main()
 {
   foo a;
   foo b{3, 4.4f};
 }
max66
  • 65,235
  • 10
  • 71
  • 111
  • Thank you. I know, that macros are evil, but in this case, it is more convinient for user to write macro rathen than not to forget register variable manually. I am using it for something like ORM with SQLite, so each variable is anyway "wrapped" in struct with some ORM logic :-) – Martin Perry Aug 27 '17 at 12:36