mardi 19 décembre 2017

Using CRTP with std::tupel and variadic templates

I want to store an arbitrary number of different classes (all shared-pointered) in some manager-class. The different classes have to be derived from the same CRTP-interface-class. Finally, I want to be able to iterate over all stored classes and calling some functions of the interface. I do not want to create a common base class and I want only use compile-timed stuff.

So I read a few articles in the internet and stole some concepts together. Now I have a working solution (I hope, I am new to templates in C++!), but I think, that is by far too much overkill for such a "simple" requirement.

Can you plz help me to optimize (simplify/shrink/modify) the following minimal example or provide some smarter solution? (no boost and only C++11/14/17)

#include <iostream>
#include <tuple> 
#include <memory>

template <class T_IMPLEMENTATION>
struct ISystem {
    bool run(int i) { return static_cast<T_IMPLEMENTATION*>(this)->runImpl(i); }
};

struct SystemA : public ISystem<SystemA> {
    friend ISystem<SystemA>;
private:
    bool runImpl(int i) { std::cout << this << " A:" << i << std::endl; return i; };
};

struct SystemB : public  ISystem<SystemB> {
    friend ISystem<SystemB>;
private:
    bool runImpl(int i) { std::cout << this << " B:" << i << std::endl;  return i; };
};

template<typename... ARGS>
struct SystemManager {
    template <int index, typename... Ts>
    struct runSystem {
        void operator()(std::tuple<Ts...>& t, int i) {
            std::get<TUPLE_SIZE - index - 1>(t)->run(i++);
            runSystem<index - 1, Ts...>{}(t, i);
        }
    };

    template <typename... Ts>
    struct runSystem<0, Ts...>  {
        void operator()(std::tuple<Ts...>& t, int i) {
            std::get<TUPLE_SIZE - 1>(t)->run(i);
        }
    };

    template <typename...SPTR_ARGS>
    void addSystems(SPTR_ARGS...args) {
        m_tupleSystems = std::make_tuple(args...);
    }

    void run() {
        m_value = 0;
        runSystem<TUPLE_SIZE - 1, std::shared_ptr<ISystem<ARGS>>...>{}(m_tupleSystems, m_value);
    }
private:
    using TUPLE_TYPE = std::tuple<std::shared_ptr<ISystem<ARGS>>...>;
    static constexpr auto TUPLE_SIZE = std::tuple_size<TUPLE_TYPE>::value;
    TUPLE_TYPE m_tupleSystems;
    int m_value;
};

int main() {
    auto sptrSystemA = std::make_shared<SystemA>();
    auto sptrSystemB = std::make_shared<SystemB>();

    SystemManager<SystemA, SystemB> oSystemManager;
    oSystemManager.addSystems(sptrSystemA, sptrSystemB);
    oSystemManager.run();
    return 0;
}

Aucun commentaire:

Enregistrer un commentaire