Пакеты параметров представляют собой конструкцию времени компиляции, а вектор — конструкцию времени выполнения. Это делает пакеты параметров неактуальными для этого вопроса. Есть несколько решений, если не считать переделки интерфейса функции C.
Первый вариант дан в ответе M Oehm на Передача всех элементов массива в функцию с переменными параметрами ( …) упоминает технику одного большого переключателя:
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"));
}
Недостатком является то, что этот метод определяет максимальное количество параметров во время компиляции.
Второй вариант — автоматизировать оператор switch
. Он использует рекурсию, поэтому он может быть менее эффективным, чем другие варианты, но его очень легко расширить до большего количества параметров:
#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);
}
Вы можете попробовать его на wandbox. Из моих тестов gcc может создать плоскую функцию. Возможно, перед более сложными примерами он действительно будет использовать рекурсию, но, несмотря ни на что, сложность составляет O (n), как если бы он был вызван без какой-либо рекурсии.
(EDIT: заменен алгоритм O(n2) на линейный алгоритм показано выше).
Третий вариант приведен в ответе Мэтта Джойнера на "Динамическая передача параметров функциям с переменным числом элементов". библиотека C, которую можно использовать для преобразования вектора в вариативные шаблоны:
FFCALL — это библиотека, которая предоставляет оболочки для динамической передачи параметров в функции с переменным числом аргументов. . Вас интересует группа функций avcall.
Приведенные выше ссылки устарели, и эта ссылка кажется более актуальной.
Насколько я понимаю документацию, ваш код должен выглядеть так:
#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);
}
Я не уверен, насколько это портативно. Кажется, он работает на машинах Intel с Linux (как 32-битные, так и 64-битные). Возможно, это также может работать в Windows. Если он не работает на вашей системе, то я думаю, что портировать его на вашу систему не так уж и сложно.
И последний вариант — использовать ассемблер. Данные из массива можно поместить в правильные регистры и/или стек. Это не очень сложно, и его можно найти здесь для архитектуры Intel.
К сожалению, все гибкие решения не являются чисто C++ и требуют каких-либо дополнений (либо из библиотеки, либо из ассемблерного кода).
EDIT: я добавил одно из решений в github и Я намерен использовать все вышеперечисленные решения.
person
Michael Veksler
schedule
01.04.2019