简体   繁体   English

重新创建函数签名并通过C ++中的模板包进行调用

[英]Recreate function signature and call via template packs in C++

I have C code that I want to rewrite in C++. 我有要用C ++重写的C代码。 The C code is part of a interpreter where the functions are defined in C however the actual call is from the interpreted source. C代码是解释器的一部分,其中的功能在C中定义,但是实际的调用来自解释的源。 Basically what it does is listed below: 基本上,下面列出了它的作用:

#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);
}

The addfunc creates a callable object specified by a function pointer and a signature, because the arguments are of the same type int only a count argument for the number of arguments is needed. addfunc创建一个由函数指针和签名指定的可调用对象,因为这些参数具有相同的类型int,仅需要一个用于数量的count参数。 When I call a function I specify the function object's index and a stack . call函数时, call指定函数对象的索引和stack The actual c-call is decoded via the argument count and typecasted, the call arguments are taken from the stack. 实际的c调用通过参数计数解码并进行类型转换,调用参数从堆栈中获取。

How can I rewrite the addfunc and call functions as templates objects in C++? 如何在C ++中将addfunccall函数重写为模板对象? How can I use template packs to count the number of arguments for the given function and regenerate the call to the function? 如何使用模板包来计算给定函数的参数数量并重新生成对该函数的调用? How can I get rid of the switch statement and the function pointer typecast ? 如何摆脱switch语句和函数指针类型转换 I have seen that luawrapper 's Binder class does something similar. 我已经看到luawrapperBinder类做了类似的事情。 However the code is quite complicated. 但是,代码非常复杂。 In my case the arguments are all of the same type. 就我而言,参数都是相同的类型。 In the end I want to do something like (pseudocode): 最后,我想做类似(伪代码)的事情:

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);

Well, if they're just C functions, why not overload on the function pointer type? 好吧,如果它们只是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

Then create std::vector<std::function<void(std::array<int, 5>)>> and push back all your functions. 然后创建std::vector<std::function<void(std::array<int, 5>)>>并推回所有函数。 It's easy, doesn't require any templates and will work reasonably well. 这很容易,不需要任何模板,并且可以正常工作。 It introduces the overhead of std::function , though. 不过,它引入了std::function的开销。

You could get rid of that by introducing your own callable type (n of them), that would correspond to the overloads above, provide an operator() and store appropriate function type inside. 您可以通过引入自己的可调用类型(其中n个)来摆脱这种情况,该类型对应于上述重载,提供一个operator()并在其中存储适当的函数类型。

Live example . 现场例子

Unfortunately, you won't be able to make a completely generic solution, as there is no way to type-erase arity. 不幸的是,您将无法提供一个完全通用的解决方案,因为无法进行类型擦除Arity。

One way you can simplify things would be to create a set of wrappers for your functions, each wrapper accepting a stack* , and calling implementation functions with arguments from said stack . 简化事情的一种方法是为函数创建一组包装器,每个包装器接受一个stack* ,并使用来自所述stack参数调用实现函数。

Than you do not need typecasts at all and a simple function pointer (to approriate wrapper) would do (no even need to type-erase). 比起您根本不需要类型转换,一个简单的函数指针(用于适当的包装器)就可以了(甚至不需要类型擦除)。

I propose a C++17 solution (simplified following a Jarod42's observation: thanks) that I suppose is over-complicated. 我提出了一个C ++ 17解决方案(根据Jarod42的观察进行了简化:谢谢),我认为它过于复杂。

But I find it funny... 但我觉得这很有趣...

First: a struct that, given (as template parameters) a type and a unsigned number, define a type as the type received. 第一:给定类型(作为模板参数)和无符号数的结构,将type定义为接收的类型。

template <typename T, std::size_t>
struct getType
 { using type = T; };

It's used to convert a variadic template list of numbers in a sequence of types ( int s, in the following example) of the same length. 它用于转换长度相同的一系列类型( int ,在下面的示例中为ins)的可变参数模板列表。

Next: a template type that register ( setFunc() ) and exec ( callFunc() ) a function returning void and a sequence of int s length as the first template parameter. 接下来:一个模板类型,该模板类型注册( setFunc() )和exec( callFunc() )一个返回voidint长度序列的函数作为第一个模板参数。

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]...); }
 };

Last: a template struct that inherit from a variadic list of preceding structs and enable ( using ) the setFunc() and the callFunc() members. 最后:一个模板结构,该结构从前面的结构的可变列表中继承并启用( usingsetFunc()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...;
 };

Use. 采用。

First you have to declare an object of type funcRegister<N> where N is the max number of integer received from your functions plus one. 首先,您必须声明一个funcRegister<N>类型的对象,其中N是从函数接收的整数的最大数量加1。 So if you want to use f4() , so four integers, you have to declare 因此,如果要使用f4()这样的四个整数,则必须声明

   funcRegister<5u>  fr;

Then you have to register the functions 然后,您必须注册功能

   fr.setFunc(f1);
   fr.setFunc(f2);
   fr.setFunc(f3);
   fr.setFunc(f4);

and, given some std::array<int, N> of the right size, you can call the registered functions 并给定适当大小的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

The following is a full compiling C++17 example 以下是完整的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);    
 }

Here is luawrapper 's code extracted to be applied the above case. 这是在上述情况下提取的luawrapper的代码。 This is more for completion as for @Jerod42's code is preferable. 对于@ 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:function作为addfun()的参数,也可以使用std :: bind

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM