Parameter packs are a compile-time construct, while a vector is a runtime construct. This makes parameter packs irrelevant for this question. There are several solutions, short of redesigning the interface of the C function.
The first option is given in
M Oehm's answer to Passing all elements of an array to a function with variable parameters (…) mentions the technique of one big switch:
void linkElements(std::vector<GstElement*>& elements) {
switch (elements.size()) {
case 0: return gst_element_link_many(nullptr);
case 1: return gst_element_link_many(elements[0], nullptr);
case 2: return gst_element_link_many(elements[0], elements[1], nullptr);
case 3: return gst_element_link_many(elements[0], elements[1], elements[2], nullptr);
case 4: return gst_element_link_many(elements[0], elements[1], elements[2], elements[3], nullptr);
... and so on for how long one wants to support
default:
throw std::runtime_error(std::to_string(elements.size()) + " elements can't be passed (too many elements"));
}
The disadvantage is that this method defines the maximal number of parameters at compile time.
The second option is to automate the switch
statement. It uses recursion, so it may be less efficient than the other options, but it is very easy to extend to more parameters:
#include <iostream>
#include <string>
#include <cstdio>
#include <vector>
#include <utility>
#include <tuple>
template <unsigned size, class Func, class Type, std::size_t... I>
void call_n(Func func, const std::vector<Type> & vec, std::index_sequence<I...>)
{
func(vec[I]...);
}
template <unsigned size, class Func, class Type>
auto call_n(Func func, const std::vector<Type> & vec)
{
return call_n<size>(func, vec, std::make_index_sequence<size>());
}
template <unsigned min, unsigned max, class Func, class Type>
void call_max_n(Func func, std::vector<Type> & elements)
{
if (elements.size() == min) {
call_n<min>(func, elements);
return;
}
if constexpr(min < max)
call_max_n<min+1, max>(func, elements);
else
throw std::runtime_error("Too many elements");
}
int main()
{
std::vector<const char*> elements{"%s %s %s", "hello", "nice", "world"};
call_max_n<1, 4>(std::printf, elements);
}
You can try it out on wandbox. From my tests, gcc is able to create a flat function. Maybe fore more complicated examples it will actually use recursion but, regardless or that, the complexity is O(n) just as if it was called without any recursion.
(EDIT: replaced the O(n2) algorithm with the linear algorithm shown above).
The third option is given in Matt Joiner's answer to "Passing parameters dynamically to variadic functions" mentions a C library that can be used to convert a vector into variadic templates:
FFCALL is a library which provides wrappers for passing parameters
dynamically to variadic functions. The group of functions you're
interested in is avcall.
The above links are outdated, and this link seems to be more up to date.
From the way I understand the documentation, your code should look like:
#include <avcall.h>
void linkElements(std::vector<GstElement*> & elements) {
av_alist alist;
av_start_void(alist, &gst_element_link_many);
for (auto ptr: elements) {
av_ptr(alist, GstElement*, ptr);
}
av_ptr(alist, GstElement*, nullptr);
av_call(alist);
}
I am not sure how portable this is. It seems to work on Linux Intel machines (both 32 and 64 bits). Maybe it can also work on Windows. If it does not work on your system, then I think it is not too difficult to port it to your system.
And the last option is to use assembly. It is possible to put the data from the array into the correct registers and/or the stack. This is not very complicated, and can be found here for the Intel architecture.
Unfortunately, all the flexible solutions are not purely C++ and require some add on (either from a library, or from an assembly code).
EDIT: I have added one of the solutions to github, and I intend to all all of the above solutions.