对于我的项目,我将C ++方法绑定到C函数。这个函数来自gstreamer,并且以这种形式是可变的:
GstElement *gst_element_link_many(GstElement *el1, GstElement* el2 ,[...], nullptr);
假设我想将一个向量传递给我的绑定,你的方法是什么?理想情况下,我会得到这个结果
void linkElements(std::vector<GstElement*>& elements) {
[...]
gst_element_link_many(elementList... , nullptr);
}
我在考虑参数包,但我不确定如何实现它们。
谢谢 !
编辑:我无法修改gstreamer功能,所以我无法传递指针。 Gstreamer作为gst_element_link(GstElement * el1,GstElement * el2);函数,但它的行为不同,因为它链接元素2乘2,从而独立计算每对的功能。
参数包是编译时构造,而向量是运行时构造。这使得参数包与此问题无关。有几种解决方案,没有重新设计C功能的界面。
M Oehm对Passing all elements of an array to a function with variable parameters (…)的回答中提到了第一个选项,提到了一个大开关的技术:
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),就像它被调用而没有任何递归一样。
(编辑:用上面显示的线性算法替换O(n2) algorithm)。
第三个选项在Matt Joiner的answer to "Passing parameters dynamically to variadic functions"中提到了一个C库,可用于将矢量转换为可变参数模板:
以上链接已过时,this link似乎更新。
从我理解文档的方式来看,您的代码应如下所示:
#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);
}
我不确定这是多么便携。它似乎适用于Linux Intel机器(32位和64位)。也许它也适用于Windows。如果它在您的系统上不起作用,那么我认为将它移植到您的系统并不太困难。
最后一个选择是使用汇编。可以将来自阵列的数据放入正确的寄存器和/或堆栈中。这不是很复杂,可以找到适用于英特尔架构的here。
不幸的是,所有灵活的解决方案都不是纯粹的C ++,并且需要一些附加功能(无论是来自库还是来自汇编代码)。
编辑:我已经为github添加了一个解决方案,我打算采用上述所有解决方案。