创建动态空终止的C绑定参数

时间:2019-04-01 08:02:56

标签: c++ gstreamer variadic

对于我的项目,我将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链接在一起,从而独立地计算每对元素的功能。

1 个答案:

答案 0 :(得分:1)

参数包是编译时构造,而矢量是运行时构造。这使得参数包与该问题无关。除了重新设计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库,可用于将向量转换为可变参数模板:

  

FFCALL是一个提供用于传递参数的包装器的库   动态变化函数。您所在的功能组   感兴趣的是avcall

以上链接已过时,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添加了一种解决方案,并且我打算将上述所有解决方案都添加进来。