代码之家  ›  专栏  ›  技术社区  ›  Fabio

使用运行时参数桥接模板

  •  2
  • Fabio  · 技术社区  · 7 年前

    我正在处理一个广泛使用模板的第三方C++库。这使得它很难创建一个C API来从我的框架中使用它。

    抽象问题,假设库提供以下功能:

    template <int i> void foo();
    template <int i> void zoo(int v);
    

    我想创建一个带有函数头的C API:

    extern "C" void c_foo(int i);
    extern "C" void c_zoo(int i, int v);
    

    一个明显的实现可能是:

    void c_foo(int i)
    {
         switch(i) {
             case 1: foo<1>(); break;
             case 2: foo<2>(); break;
             case 3: foo<3>(); break;
             default: break;
         };
    };
    

    也同样适用于 void zoo(int) .

    如果 i 很小。如果我想处理 在[1100]中,以这种方式编写代码变得非常丑陋,因为有很多重复。

    有没有更简洁的方法来做到这一点,即写更少的代码行?可能使用递归预处理器宏?

    2 回复  |  直到 7 年前
        1
  •  5
  •   TartanLlama    7 年前

    您可以在内部使用模板来生成必要的代码。

    一种方法是生成一个包含100个函数指针的调度表,然后在运行时对其进行索引。 c_foo 将生成索引的编译时序列并调用助手:

    extern "C" void c_foo(int i) {    
        c_foo_impl(std::make_integer_sequence<int,100>{}, i);
    }
    

    此助手将生成调度表并执行调用:

    template <int... Is>
    void c_foo_impl (std::integer_sequence<int,Is...>, int i) {
        constexpr std::array<void(*)(), sizeof...(Is)> dispatch = { &foo<Is>... };
    
        //assert or some other error handling for i > sizeof...(Is)
        dispatch[i]();
    }
    

    那么你也可以这样做 zoo :

    extern "C" void c_zoo(int i, int v) {   
        c_zoo_impl(std::make_integer_sequence<int,100>{}, i, v);
    }
    
    template <int... Is>
    void c_zoo_impl (std::integer_sequence<int,Is...>, int i, int v) {
        constexpr std::array<void(*)(int), sizeof...(Is)> dispatch = { &zoo<Is>... };
    
        //assert or some other error handling for i > sizeof...(Is)
        dispatch[i](v);
    }
    

    如果您发现在一些地方需要这个,您可以抽象出一些细节,或者使用一个库,例如 Petra ,它提供了 switch_table 进行这种映射。

    Live demo

        2
  •  0
  •   Gem Taylor    7 年前

    我想问题是这个模板号是什么?它有点像内部缓冲区大小,还是更像命令字节? 有人假设,一般的C++程序只会生成特定的几个参数值。

    bast方法可能是枚举一些典型的缓冲区大小或命令名,并仅实例化这些大小,因此如果c代码使用您的枚举作为参数,它将正常工作,否则它会(严重失败)断言。

    如果新条目是合理的,那么添加它们会很麻烦,但您可以为此编写一个脚本。

    另一种方法可能是生成损坏的命名存根,即生成一个foo\uu 1()foo\uu 2()等,也许可以使用boost扩展预处理器来提供帮助。同样,C程序员会自动限制在编译时发布的方法和范围内。