0

This question follows on from here and is to do with accessing tuple elements when the elements of the tuple are defined by means of a template.

If I have as a means of accessing the contents of a tuple:

#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;
}

}

I want to access the tuple elements here, but where each tuple element is created by means of a template. Thus:

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

};

template <uint32_t exampleParameter = 1>
class DerivedElement1 : public BaseElement {
public:
    DerivedElement1() : i(exampleParameter) {}
    virtual int polymorphicFunction() {return 1;};
    uint32_t i; /// just used as a placeholder to demo use of parameter

}

template <uint32_t exampleParameter = 2>
class DerivedElement2 : public BaseElement {
public:
    DerivedElement2() : i(exampleParameter) {}

    virtual int polymorphicFunction() {return 2;};

    uint64_t i; /// just used as a placeholder to demo use of parameter (class is different to DE1)
}

template<typename... systems>   // systems will always be of derived class of BaseElement
class System {
    System() : subsystems(systems{}...),
    pd1(detail::safe_get<detail::get_index<DerivedElement1, systems...>::value>(subSystems)),
    pd2(detail::safe_get<detail::get_index<DerivedElement2, systems...>::value>(subSystems))
    {}  // all variadic elements stored in tuple

    const std::tuple<systems...> subSystems;

    DerivedElement1<> *pd1; 
    DerivedElement2<> *pd2;
};

pd1 & pd2 should be set to point to the respective derived elements if they exist when specified in the declaration of System, otherwise they should be set to null.

This works when I do:

System<DerivedElement1<>, DerivedElement2<>> sys; // sys.pd1 points to DerivedElement1<> element of tuple, sys.pd2 points to DerivedElement2<> within tuple

But if I supply a variable to the declaration of System, pd1 & pd2 are both set to nullptr.

System<DerivedElement1<5>, DerivedElement2<6>> sys; // sys.pd1 == nullptr, sys.pd2 == nullptr

How can I get pd1 & pd2 to point to the correct tuple elements please?

Edit to try to be more clear:

Different types, derived from a common class, are stored in a tuple (e.g. DerivedElement1<>, DerivedElement2<6>). I have within the storage class pointers that should point to the derived class elements of the tuple. The set pointer code I have works when I use no template parameters, (i.e. DerivedElement1<> in the example above), but does not when I use a template parameter (e.g. DerivedElement1<5>).

Community
  • 1
  • 1
John
  • 10,837
  • 17
  • 78
  • 141
  • 2
    You either need to simplify the example or show the actual code because this is sufficiently confusing and incomplete as it stands. – AJG85 Aug 05 '14 at 16:40
  • I'm afraid this is as simple as it gets and I can't post the real code: for some reason (and I think it's because I'm supplying a template parameter, not using the default one), accessing the tuple element fails and I get nullptr returned. – John Aug 05 '14 at 16:44
  • It also wont compile, as you are passing templates DerivedElement1 and DerivedElement2 into detail::get_index, which expects a type parameter, not a template parameter. – Dalibor Frivaldsky Aug 05 '14 at 16:44
  • What exactly are you trying? This has no sense, since `DerivedElementX` are templates, but you pass them to your typelist searching tool (`get_index`, which gets the index of a passed type in a given typelist, isn't?). Please be more clear. – Manu343726 Aug 05 '14 at 16:45
  • Sorry - the original question here (http://stackoverflow.com/questions/24831352/heterogenous-storage-of-variadic-class-parameter-member-variables) uses types as that question was a simplification. That said, DerivedElement1<> is a type, not a template, as is DerivedElement1<5> (details here: http://stackoverflow.com/questions/24812913/template-template-parameters-with-variadic-templates) – John Aug 05 '14 at 16:48
  • Ahhh now I get what you are doning: Pass a set of template instances (`subsystems`), then search for an instance of each kind of template you have (Two in the example, `DerivedElement1` and `2`). Get those and store references to that elements as `pd1` and `pd2`. I'm wrong? – Manu343726 Aug 05 '14 at 16:51
  • Now you have the recurrent problem with functional algorithms in C++ template metaprogramming: **type template parameters vs everything else**. Its easy to be generic using variadic templates of type parameters only, but really hard to be fully generic if you use value parameters for example. – Manu343726 Aug 05 '14 at 16:54
  • Yes, although they are stored as pointers, not references. Thanks for looking, I've edited the original post to clarify. – John Aug 05 '14 at 16:54
  • In your specific case, you need a metafunction to search (match) any instance of a given template in a set of types. Thats the problem. Writting such generic template-template parameter `template class T` its easy and fully generic, but works with templates using type parameters only (Just to be clear, in that case the signature of your searching metafunction would be `template – Manu343726 Aug 05 '14 at 16:57
  • If you are completely sure that all the instances passed to that function will have the same (or similar) signature, for example ``, make the signature of the template-template parameter of your search metafunction fixed. Else, I suggest you to provide non-type template parameters through boxing always when writting algorithms for template metaprogramming. – Manu343726 Aug 05 '14 at 17:00

1 Answers1

1

So you need instead of get_index:

namespace detail
{

template <template<typename> class Pred, typename... Ts> struct get_index_if;

template <template<typename> class Pred, typename T, typename... Ts>
struct get_index_if<Pred, T, Ts...> :
    std::integral_constant<
        std::size_t,
        Pred<T>::value ? 0 : 1 + get_index_if<Pred, Ts...>::value>
{};

template <template<typename> class Pred>
struct get_index_if<Pred> : std::integral_constant<std::size_t, 0> {}; // Not found

}

A predicate for your types for get_index_if:

template <typename T> struct is_a_Derived1 : std::false_type {};
template <std::uint32_t N> struct is_a_Derived1<DerivedElement1<N>> : std::true_type {};

template <typename T> struct is_a_Derived2 : std::false_type {};
template <std::uint32_t N> struct is_a_Derived2<DerivedElement2<N>> : std::true_type {};

And finally:

// helper for trailing return type... until C++14
#define Return(Ret) decltype Ret { return Ret; }

template <typename... systems>
class System {
    const std::tuple<systems...> subSystems;

public:
    constexpr System() : subSystems() {}

    auto getDerivedElement1()
    -> Return((detail::safe_get<detail::get_index_if<is_a_Derived1, systems...>::value>(subSystems)))

    auto getDerivedElement2()
    -> Return((detail::safe_get<detail::get_index_if<is_a_Derived2, systems...>::value>(subSystems)))

};

Note: As DerivedElement1<N1> and DerivedElement1<N2> are different types (for N1 != N2), the only possible member type would be he base class. Here you have getter methods with correct type (or nullptr_t when element is absent).

Note: the given predicate doesn't support derived class of DerivedElement1<N>.

Jarod42
  • 203,559
  • 14
  • 181
  • 302