Declare member variables from variadic template parameter
Asked Answered
Z

4

39

Obviously the code below doesn't compile in C++. But I have a case where I'd like to parameterize a class with zero or more data items based on template parameters.

Is there any way I can declare a class whose data members depend on variadic template parameters so I can access each of them? or some other way to achieve what I'd like?

This came up in a real program which I've solved an entirely different way but now I'm interested in the more abstract problem of how I might have done this.

template <typename... Types> class Data
{
    // Declare a variable of each type in the parameter pack
    // This is NOT valid C++ and won't compile...
    Types... items;
};

struct Item1
{
    int a;
};

struct Item2
{
    float x, y, z;
};

struct Item3
{
    std::string name;
}

int main()
{
    Data<Item1, Item2> data1;
    Data<Item3> data2;
}
Zilla answered 24/11, 2016 at 8:36 Comment(1)
Surprisingly enough, this does not appear to be a duplicate.Syncope
C
26

You could use a std::tuple

#include <tuple>

template <typename... Types> class Data
{
    std::tuple<Types...> items;
};

struct Item1
{
    int a;
};

struct Item2
{
    float x, y, z;
};

struct Item3
{
    std::string name;
};

int main()
{
    Data<Item1, Item2> data1;
    Data<Item3> data2;
}

Try it here

Cerebritis answered 24/11, 2016 at 8:40 Comment(3)
Thanks, that's great. I use tuple but somehow it didn't occur to me I could use it with the expanded parameter pack.Zilla
What if you wanted each member to be a 1-bit bitfield? Are macros the only way to do it?Liberec
I suppose you could use a bitset<sizeof...(Types)>, or something like it.Cerebritis
T
10

That is the purpose of std::tuple:

template <typename... Types> class Data
{
    std::tuple<Types...> items;
};
Traditional answered 24/11, 2016 at 8:39 Comment(1)
Ah thank you. That's slightly embarrassing, I use tuple but somehow didn't think to use it with a parameter pack expansion here.Zilla
S
8

The standard has got you covered. Just declare std::tuple<Types...> items.

Syncope answered 24/11, 2016 at 8:39 Comment(0)
S
8

I wanted to be able to use the members with fold expressions. So I ended up with:

template <class T, class... rest> class hold : hold<rest...> {
    using base = hold<rest...>;
    T v_;

public:
    hold(T v, rest... a) : base(a...), v_(v) {}

    template <class F, class... args> auto apply(F f, args... a) {
        return base::apply(f, a..., v_);
    }
};

template <class T> class hold<T> {
    T v_;

public:
    hold(T v) : v_(v) {}

    template <class F, class... args> auto apply(F f, args... a) {
        return f(a..., v_);
    }
};

This then facilitates:

template <class scalar, class... arrays> struct plus_expr {
    hold<arrays...> a_;

    plus_expr(arrays... a) : a_(a...) {}

    scalar operator[](index const i) {
        return a_.apply([i](arrays... a) { return (a[i] + ...); });
    }
};
Stylographic answered 31/10, 2018 at 2:15 Comment(1)
Huge thanks! I was able to use this general structure in C++11 to make a tuple-like class that can do std::apply style behavior. I had similar need for indexing like your example too. I took a different approach though. Instead of the lambda capture, I passed it in to apply, and then instead of apply(f, a..., v_) it was apply(f, a..., sample(v, i)) which was overloaded for different "array-like" inputs.Heuser

© 2022 - 2024 — McMap. All rights reserved.