1

I have a class called system. A system takes some object managers and changes all objects in them in some way.

For example there might be a system that draws all images in a imageManager.

Every derived class works somewhat like this (pseudo code):

class someChildClass : public System{
private:
     someObjectManager &mang1; //these are used by the update method.
     someOtherObjectManager &mang2;//the update method changes these somehow
public:
     someChildClass(someObjectManager &mang1, someObjectManager &mang2)
     :mang1(mang1),mang2(mang2){
     }
     virtual void update(){
     //this is pure virtual in the System base class.
     //Do something with the managers here
     }
}

I feel like writing everything but the update method is a waste of time and a source of errors. I wanted to write a macro that basically makes a class like this like so:

QUICKSYSTEM(thisIsTheSystemName, someObjectManager, mang1, someOtherObjectManager, mang2, ... (infinite possible Managers. So a variadic macro?)){
//this is the update function
}
}//this is the end braked for the class declaration. Its ugly but I dont know how I could do the function differently? 

well I am having some problems making the macro. Everything works fine until I need to split the variadic arguments into the names and the types. I dont know if this is even possible now, since I cant go back and forth in the arguments easily or apply a easy step to them to make sure that every 2nd is the name of the variable. I would be ok with omitting the possibility for names and just had the types with some sort of automatic naming (manager1,manager2,manager3 or something like that).

If this isnt possible using a macro, what would be a better way to avoid mistakes and cut some time in the constructor and class declaration part?

MoustacheSpy
  • 743
  • 1
  • 6
  • 27

2 Answers2

3

Yeah, macros are really, really not the way to do this. C++ has templates, which follow C++ syntax and support C++ expressions. Macros instead use their own preprocessor language, which is almost entirely unaware of C++.

You'll want to read up a bit on std::tuple as well. It's going to be rather tricky to handle all those managers with those names. Tuples are the Standard solution for that. managers.get<0> and managers.get<someObjectManager> both work.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
MSalters
  • 173,980
  • 10
  • 155
  • 350
  • Note: I swear I've though of `std::tuple` independently of your answer, but took my lunch break before publishing it ^^ – YSC Apr 23 '18 at 11:01
  • @YSC: You can clearly see from our answers which country has the longer lunchbreaks ^^ – MSalters Apr 23 '18 at 12:40
  • This is the national achievment I'm the most proud of :D – YSC Apr 23 '18 at 12:42
  • @YSC Is more detailed but he uses many things I dont understand yet. I have managed to make my own version using just tuples and some basic variadic templates without the extra stuff. – MoustacheSpy Apr 24 '18 at 13:11
2

Variadic templates are the tool you need here:

#include <iostream>
#include <tuple>
#include <functional>

struct System { void virtual update() = 0; };
template<class... Managers>
struct ManagedSystem : System
{
    std::function<void(Managers&...)> _update;
    std::tuple<Managers&...>          _managers;

    template<class F>
    ManagedSystem(F update, Managers&... managers) : _update(update), _managers(managers...) {}

    void update() override { _update(std::get<Managers&>(_managers)...); }
};

int main()
{
    int n = 0;
    double d = 3.14;
    auto reset = [](int& a, double& d) { a = 0; d = 0.0; };
    ManagedSystem<int, double> ms{reset, n, d};
    ms.update();
    std::cout << "n = " << n << ", d = " << d << "\n";
    // n = 0, d = 0
}

The idea is to define a templated-class (ManagedSystem) taking as template-parameters multiple manager types. This class inherits from Systemand provides a constructor taking:

  1. an update functor,
  2. and references to manager whose type is defined by the template parameters of the class.

The said managers are registered internally in an std::tuple and (with a bit of parameter pack magic fed to the update functor.

From there, you can define an inherited class from System by providing an update function and a type list. This avoids the use of ugly and type-unsafe macros in favor of the not-less ugly but type-string templates ;)

YSC
  • 38,212
  • 9
  • 96
  • 149