3

I have methods with the following signature:

void DoStuff(int i);
void DoStuff(int i, k);
void DoStuff(int i, int k, int l);

I have a method from where I would like to call the DoStuff methods as follows:

void CallDoStuff(const std::vector<int>& vElements) {
  // What magic is supposed to happen here to make vElements an expandable pack?
  DoStuff(vElemets...);
}

Is there any chance to achieve this? Is using std::index_sequence the right way? If yes, could you please provide me a simple example how to apply this to my problem?

Bob
  • 35
  • 5

3 Answers3

3

The problem is that, from a std::vector, you can't -- compile time -- extract the size() value.

So you can obtain what you want only if you pass, as a compile-time known value, to CallDoStuff() the number of elements that you want to use from the vector.

You can pass it as, by example, a template value.

Using an helper function, you can write something as follows

template <std::size_t ... Is>
void CallDoStuff (std::vector<int> const & vElements,
                  std::index_sequence<Is...> const &)
 { DoStuff(vElements[Is]...); }

template <std::size_t N>
void CallDoStuff (std::vector<int> const & vElements)
 { CallDoStuff(vElements, std::make_index_sequence<N>{}); }

The call could be something as

CallDoStuff<5u>(v);

If you can use a std::array, instead of std::vector, the answer is different: you can extract the size() from the type itself, so

template <std::size_t N, std::size_t ... Is>
void CallDoStuff (std::array<int, N> const & vElements,
                  std::index_sequence<Is...> const &)
 { DoStuff(vElements[Is]...); }

template <std::size_t N>
void CallDoStuff (std::array<int, N> const & vElements)
 { CallDoStuff(vElements, std::make_index_sequence<N>{}); }

that is callable without explicating N as follows

std::array<int, 5u>  arr { 2, 3, 5, 7, 11 };

CallDoStuff(arr); // no more <5u>

End note: observe that std::make_index_sequence and std::index_sequence are available only starting from C++14. In C++11 you have to substitute them in some way.

max66
  • 65,235
  • 10
  • 71
  • 111
  • I totally agree with you, templates need to know the size at compile-time and the size of the vector is only known at run-time. – Bob Nov 05 '19 at 07:50
  • If you use std::array then std::apply is an easy solution. – Ivor Denham-Dyson Dec 14 '19 at 06:00
  • @JakeDenham-Dyson - Do you mean `std::apply(DoStuff, arr);`? Unfortunately, in this case, doesn't works in a so easy way: `DoStuff()` is a overloaded function so the compiler can't select the correct version. Moreover, `std::apply()` is introduced in C++17 and the question is tagged C++11 (but this is also a problem of my answer that is C++14). – max66 Dec 15 '19 at 14:28
  • Can someone break this down for a noob – Edward Nov 22 '21 at 16:25
1

It's possible, as long as you provide an upper bound to the number of arguments.

Using Xeo's implementation of std::index_sequence for C++11:

template <unsigned... Idx>
void trampoline(const std::vector<int>& vElements, seq<Idx...>) {
    return DoStuff(vElements[Idx]...);
}

template <std::size_t Arity>
void trampoline(const std::vector<int>& vElements) {
    return trampoline(vElements, typename gen_seq<Arity>::seq{});
}

template <unsigned... Idx>
void CallDoStuff(const std::vector<int>& vElements, seq<Idx...>) {
    using trampoline_t = void (*)(const std::vector<int>&);
    constexpr trampoline_t trampolines[]{
        trampoline<Idx>...
    };
    trampolines[vElements.size()](vElements);
}

template <std::size_t Max>
void CallDoStuff(const std::vector<int>& vElements) {
    assert(vElements.size() <= Max);
    return CallDoStuff(vElements, typename gen_seq<Max + 1>::seq{});
}

See it live on Wandbox

Quentin
  • 62,093
  • 7
  • 131
  • 191
  • This answer solves my problem best, because I can wrap the function call CallDoStuff<3u>(vElements). But all other answers are also correct. – Bob Nov 11 '19 at 10:15
0

This can't be done, a template method call is bound at compile time but a std::vector doesn't know how many items it contains until runtime so there's no way to mix the two concepts.

DoStuff(vElemets...);

Here the compiler should choose the correct implementation according to how many elements vElements has. You see the flaw in this kind of thinking since std::vector is just an object that could contain any amount of items at point of invocation.

Jack
  • 131,802
  • 30
  • 241
  • 343
  • As already replied to max66, I totally agree with you Jack, templates need to know the size at **compile-time** and the size of the vector is only known at **run-time**. – Bob Nov 05 '19 at 07:57