c++templatestuplesvariadic-templatesheterogeneous

Heterogenous storage of variadic class parameter member variables


I have a variadic class template that is used to create a top-level class for a variable number of classes. Each class that is to go in the top-level class is derived from a base class, as there is common functionality for them. I don't know the best way to store the derived classes in the parent class, but still be able to access the full functionality of the derived class.

If I store the variadic args in a vector, they'll all be stored as a base class and I can't access the derived functionality. If I store them in a tuple, I can't work out how to access the functions by derived type. If I try to access them as discussed here on SO then make_unique isn't available (C++14?).

So, I want to do the following:

class BaseElement {
public:
    virtual int polymorphicFunction() {return 0;};
};

class DerivedElement1 : public BaseElement {
public:
    virtual int polymorphicFunction() {return 1;};
}

class DerivedElement2 : public BaseElement {
public:
    virtual int polymorphicFunction() {return 2;};
}

template<typename... systems>   // systems will always be of derived class of BaseElement
class System {
    System() : subsystems(systems{}...) {} ;  // all variadic elements stored in tuple
    // tuple used below, the system elements don't need to be stored in a container, I just want to access them 
    // I'd be happy to use a vector or access them directly as a member variable
    // provided that I can access the derived class.  I can't use RTTI.
    const std::tuple<systems...> subSystems;

    // pointer or reference, I don't mind, but pd1/2 will always exist, 
    // (but perhaps be NULL), even if there is no derived element passed to the template parameter
    DerivedElement1 *pd1; 
    DerivedElement2 *pd2;
};

//Desired usage
System<DerivedElement1> sys;  // sys->pd1 == &derivedElement1WithinTuple, sys->pd2 == NULL
System<DerivedElement2> sys;  // sys->pd2 == &derivedElement2WithinTuple, sys->pd2 == NULL
System<DerivedElement1, DerivedElement2> sys;  // sys->pd1 == &derivedElement1WithinTuple, sys->pd1 == &derivedElement1WithinTuple

Does anyone have any suggestions as to how I might achieve this please?


Solution

  • With:

    #include <cstdint>
    #include <type_traits>
    #include <tuple>
    
    namespace detail
    {
    
    template <typename T, typename... Ts> struct get_index;
    
    template <typename T, typename... Ts>
    struct get_index<T, T, Ts...> : std::integral_constant<std::size_t, 0> {};
    
    template <typename T, typename Tail, typename... Ts>
    struct get_index<T, Tail, Ts...> :
        std::integral_constant<std::size_t, 1 + get_index<T, Ts...>::value> {};
    
    template <typename T>
    struct get_index<T> : std::integral_constant<std::size_t, 0> {}; // Not found
    
    
    template <std::size_t N, typename... Ts>
    constexpr
    auto
    safe_get(const std::tuple<Ts...>& t) noexcept
    -> typename std::enable_if<N < sizeof...(Ts), decltype(&std::get<N < sizeof...(Ts) ? N : 0>(t))>::type
    {
        return &std::get<N>(t);
    }
    
    template <std::size_t N, typename... Ts>
    constexpr
    auto
    safe_get(const std::tuple<Ts...>&) noexcept
    -> typename std::enable_if<sizeof...(Ts) <= N, nullptr_t>::type
    {
        return nullptr;
    }
    
    }
    

    You may have:

    template <typename... systems>
    class System {
    public:
        constexpr System() :
            subSystems(),
            pd1(detail::safe_get<detail::get_index<DerivedElement1, systems...>::value>(subSystems)),
            pd2(detail::safe_get<detail::get_index<DerivedElement2, systems...>::value>(subSystems))
        {}
    
        const std::tuple<systems...> subSystems;
        const DerivedElement1 *pd1;
        const DerivedElement2 *pd2;
    };