在C++中通过模板包重新创建函数签名和调用
Recreate function signature and call via template packs in C++
我有C代码要用C++重写。C代码是解释器的一部分,其中函数在C中定义,但实际调用来自被解释的源。基本上它的作用如下:
#include <vector>
void f1(int a0) { }
void f2(int a0,int a1) { }
void f3(int a0,int a1,int a2) { }
void f4(int a0,int a1,int a2,int a3) { }
struct m {
void *p;
int c;
};
std::vector<m> ma;
int addfunc(void *p, int c) {
int i = ma.size();
ma.push_back({p,c});
return i;
}
void call(int idx, int *stack) {
switch (ma[idx].c) {
case 1:
((void (*)(int))ma[idx].p) (stack[0]);
break;
case 2:
((void (*)(int,int))ma[idx].p) (stack[0],stack[1]);
break;
case 3:
((void (*)(int,int,int))ma[idx].p) (stack[0],stack[1],stack[2]);
break;
case 4:
((void (*)(int,int,int,int))ma[idx].p) (stack[0],stack[1],stack[2],stack[3]);
break;
}
}
int main (void) {
int stack[5] = { 0,1,2,3,4 };
/* define */
int i1 = addfunc((void*)f1, 1);
int i2 = addfunc((void*)f2, 2);
int i3 = addfunc((void*)f3, 3);
int i4 = addfunc((void*)f4, 4);
/* call */
call(i1,stack);
call(i2,stack);
call(i3,stack);
call(i4,stack);
}
addfunc
创建了一个由函数指针和签名指定的可调用对象,因为参数的类型相同,只需要一个用于参数数量的count参数。当我call
函数时,我指定函数对象的索引和stack
。实际的c调用通过参数计数进行解码并进行类型转换,调用参数取自堆栈。
如何在C++中将addfunc
和call
函数重写为模板对象?如何使用模板包计算给定函数的参数数量并重新生成对该函数的调用?如何摆脱switch语句和函数指针类型转换?我已经看到luawrapper的Binder
类做了类似的事情。然而,代码相当复杂。在我的例子中,这些参数都是相同类型的。最后,我想做一些类似(伪代码)的事情:
vector<meth> ma;
...
int i0 = addfunc([](int a) { });
int i1 = addfunc([](int a,int b) { });
int i2 = addfunc([](int a,int b,int b) { });
int i3 = addfunc([](int a,int b,int c,int c) { });
...
ma[i0](stack);
ma[i1](stack);
ma[i2](stack);
ma[i3](stack);
如果它们只是C函数,为什么不重载函数指针类型呢?
std::function<void(std::array<int, 5>)> addfunc(void (*f)(int)) {
return [f](std::array<int, 5> const& a) { f(a[0]); };
}
std::function<void(std::array<int, 5>)> addfunc(void (*f)(int,int)) {
return [f](std::array<int, 5> const& a) { f(a[0], a[1]); };
}
// repeat for all necessary arities
然后创建std::vector<std::function<void(std::array<int, 5>)>>
并推回所有函数。它很简单,不需要任何模板,而且工作得相当好。不过,它引入了std::function
的开销。
您可以通过引入自己的可调用类型(其中n个)来消除这种情况,这将对应于上面的重载,提供operator()
并在其中存储适当的函数类型。
活生生的例子。
不幸的是,您将无法制作一个完全通用的解决方案,因为无法键入擦除arity。
简化事情的一种方法是为函数创建一组包装器,每个包装器接受一个stack*
,并使用所述stack
中的参数调用实现函数。
您根本不需要类型转换,只需要一个简单的函数指针(指向适当的包装器)就可以了(甚至不需要键入擦除)。
我提出了一个C++17解决方案(根据Jarod42的观察结果简化:谢谢),我认为它过于复杂。
但我觉得很有趣。。。
首先:一个结构,给定(作为模板参数)一个类型和一个无符号数字,将type
定义为接收的类型。
template <typename T, std::size_t>
struct getType
{ using type = T; };
它用于转换具有相同长度的类型序列(在下面的示例中为int
s)中的数字的可变模板列表。
下一步:一个模板类型,注册(setFunc()
)并执行(callFunc()
)一个返回void
的函数和一个长度为int
的序列作为第一个模板参数。
template <std::size_t N, typename = std::make_index_sequence<N>>
struct frHelper;
template <std::size_t N, std::size_t ... Is>
struct frHelper<N, std::index_sequence<Is...>>
{
using fnPnt_t = void(*)(typename getType<int, Is>::type...);
fnPnt_t fp = nullptr;
void setFunc (fnPnt_t fp0)
{ fp = fp0; }
void callFunc (std::array<int, sizeof...(Is)> const & a)
{ if ( fp ) fp(a[Is]...); }
};
最后:一个模板结构,它继承了前面结构的可变列表,并启用(using
)setFunc()
和callFunc()
成员。
template <std::size_t N, typename = std::make_index_sequence<N>>
struct funcRegister;
template <std::size_t N, std::size_t ... Is>
struct funcRegister<N, std::index_sequence<Is...>>
: public frHelper<Is>...
{
using frHelper<Is>::setFunc...;
using frHelper<Is>::callFunc...;
};
使用。
首先,您必须声明一个类型为funcRegister<N>
的对象,其中N
是从函数接收的整数的最大值加1。所以如果你想使用f4()
,那么四个整数,你必须声明
funcRegister<5u> fr;
然后你必须注册功能
fr.setFunc(f1);
fr.setFunc(f2);
fr.setFunc(f3);
fr.setFunc(f4);
并且,给定一些合适大小的std::array<int, N>
,您可以调用已注册的函数
std::array a1 { 1 };
std::array a2 { 1, 2 };
std::array a3 { 1, 2, 3 };
std::array a4 { 1, 2, 3, 4 };
fr.callFunc(a1); // call f1
fr.callFunc(a2); // call f2
fr.callFunc(a3); // call f3
fr.callFunc(a4); // call f4
下面是一个完整的编译C++17的示例
#include <array>
#include <utility>
#include <iostream>
#include <type_traits>
template <typename T, std::size_t>
struct getType
{ using type = T; };
template <std::size_t N, typename = std::make_index_sequence<N>>
struct frHelper;
template <std::size_t N, std::size_t ... Is>
struct frHelper<N, std::index_sequence<Is...>>
{
using fnPnt_t = void(*)(typename getType<int, Is>::type...);
fnPnt_t fp = nullptr;
void setFunc (fnPnt_t fp0)
{ fp = fp0; }
void callFunc (std::array<int, sizeof...(Is)> const & a)
{ if ( fp ) fp(a[Is]...); }
};
template <std::size_t N, typename = std::make_index_sequence<N>>
struct funcRegister;
template <std::size_t N, std::size_t ... Is>
struct funcRegister<N, std::index_sequence<Is...>>
: public frHelper<Is>...
{
using frHelper<Is>::setFunc...;
using frHelper<Is>::callFunc...;
};
void f1(int) { std::cout << "f1 called" << std::endl; }
void f2(int,int) { std::cout << "f2 called" << std::endl;}
void f3(int,int,int) { std::cout << "f3 called" << std::endl;}
void f4(int,int,int,int) { std::cout << "f4 called" << std::endl;}
int main()
{
funcRegister<5u> fr;
fr.setFunc(f1);
fr.setFunc(f2);
fr.setFunc(f3);
fr.setFunc(f4);
std::array a1 { 1 };
std::array a2 { 1, 2 };
std::array a3 { 1, 2, 3 };
std::array a4 { 1, 2, 3, 4 };
fr.callFunc(a1);
fr.callFunc(a2);
fr.callFunc(a3);
fr.callFunc(a4);
}
以下是提取的luarwrapper代码,用于应用上述情况。这更多的是为了完成,因为@Jerod42的代码更可取。
#include <iostream>
#include <string>
#include <array>
#include <vector>
#include <functional>
#include <vector>
template<typename T> struct tag {};
template<typename TFunctionObject, typename TFirstParamType>
struct Binder {
TFunctionObject function;
TFirstParamType param;
template<typename... TParams>
auto operator()(TParams&&... params)
-> decltype(function(param, std::forward<TParams>(params)...))
{
return function(param, std::forward<TParams>(params)...);
}
};
template<typename TCallback>
static void readIntoFunction(int *stack, TCallback&& callback)
{
callback();
}
template<typename TCallback, typename TFirstType, typename... TTypes>
static void readIntoFunction(int *stack, TCallback&& callback, tag<TFirstType>, tag<TTypes>... othersTags)
{
Binder<TCallback, const TFirstType&> binder{ callback, *stack };
return readIntoFunction(++stack, binder, othersTags...);
}
/* decompose arguments */
template<typename TFunctionType, typename... TOtherParams>
std::function<void(int*)> _addfunc(TFunctionType f, tag<void (*)(TOtherParams...)>) {
return std::function<void(int*)>([f](int *stack) {
readIntoFunction(stack, f, tag<TOtherParams>{}...);
});
}
template<typename TFunctionType>
std::function<void(int*)> addfunc(TFunctionType fn)
{
typedef typename std::decay<TFunctionType>::type RealFuncSig;
return _addfunc(std::move(fn), tag<RealFuncSig>{} );
}
void f1(int a0) { std::cout << a0 << std::endl; }
void f2(int a0, int a1) { std::cout << a0 << a1 << std::endl; }
int main() {
int stack[5] = { 0,1,2,3,4 };
auto a0 = addfunc(&f1);
auto a1 = addfunc(&f2);
a0(stack);
a1(stack);
}
您可以使用std:函数作为addfun()的参数,也可以使用std::bind
- 通过构造函数创建的所有对象都具有相同的向量
- 通过构造函数创建一些值并尝试添加到文档中使用 rapidjson 不起作用
- 为 NewObjectA() 函数创建 jvalues 的参数数组时出错 - JNI Invocation API
- 使用带有 ref 参数的成员函数创建线程时出现编译错误
- C++ :: 如何捕获由 void 函数创建的矩阵
- 通过零参数构造函数创建的 glm::mat4 应该包含哪些值?
- 用于基于成员字段或函数创建比较器的快捷方式
- std::使用类成员函数创建线程 - 最佳实践
- 为C++重载函数创建SWIG类型映射
- 使用成员函数创建std::函数不会编译
- 如何为返回特定类型的函数创建 SWIG 类型图
- 为具有多个参数的函数创建 SWIG 类型图的更简单方法?
- 通过另一个宏创建的函数创建所有列表的宏
- std::map<int, A> 运算符 [] 需要使用空构造函数创建 A
- 无法使用 CreateWindowEx 函数创建窗口
- c++ 从成员函数创建新线程并移动对象和整个对象
- 无法使用 c++ 中的类成员函数创建/写入文件
- 使用makeword函数创建错误e0109-表观呼叫的括号前表达式必须具有(指针到 - )函数类型
- 为 OpenMP 函数创建别名 ||部分禁用 openMP
- 稍后使用<Class>调用类构造函数创建 std::vector