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

时间:2019-01-14 20:03:50

标签: c++ templates

我有要用C ++重写的C代码。 C代码是解释器的一部分,其中的功能在C中定义,但是实际的调用来自解释的源。基本上,下面列出了它的作用:

import calendar
import collections
import dateutil

def totals(start_date, end_date):
    """
    start_date and end_date are datetime.date objects.
    """

    results = collections.OrderedDict()  # Remember order things are added.

    result = Purchase.objects.filter(date__gte=start_date, date__lt=end_date).annotate(
        real_total = Case(
            When(total_purchase__isnull=True, then=0),
            default=F('total_purchase')
        )
    )

    date_cursor = start_date
    month_partial_total = 0
    while date_cursor < end_date:
        # The while statement implicitly orders results (it goes from start to end).
        month_partial_total += result.filter(date__month=date_cursor.month).aggregate(
            partial_total=Sum('real_total')
        )['partial_total']


        results[date_cursor.month] = month_partial_total

        # Uncomment following line if you want result contains the month names
        # instead the month's integer values.
        # result[calendar.month_name[month_number]] = month_partial_total

        date_cursor += dateutil.relativedelta.relativedelta(months=1)

    return results

#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); } 创建一个由函数指针和签名指定的可调用对象,因为参数的类型相同,因此仅需要一个数量为count的count参数。 当我addfunc一个函数时,我指定了函数对象的索引和一个call。实际的c调用通过参数计数进行解码并进行类型转换,调用参数从堆栈中获取。

如何在C ++中将stackaddfunc函数重写为模板对象?如何使用模板包来计算给定函数的参数数量并重新生成对该函数的调用? 如何摆脱switch语句和函数指针类型转换?我已经看到luawrappercall类做了类似的事情。但是,代码非常复杂。就我而言,参数都是相同的类型。 最后,我想做类似(伪代码)的事情:

Binder

5 个答案:

答案 0 :(得分:2)

好吧,如果它们只是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()并在其中存储适当的函数类型。

Live example

答案 1 :(得分:1)

不幸的是,您将无法提供一个完全通用的解决方案,因为无法进行类型擦除Arity。

可以简化事情的一种方法是为函数创建一组包装器,每个包装器接受一个stack*,并使用所述stack中的参数调用实现函数。

比起根本不需要类型转换,一个简单的函数指针(用于适当的包装)就可以了(甚至不需要键入-擦除)。

答案 2 :(得分:1)

我提出了我认为过于复杂的C ++ 17解决方案(在Jarod42的观察之后进行了简化:谢谢)。

但我觉得这很有趣...

首先:一个结构(给定类型(作为模板参数)和一个无符号数字)将type定义为接收的类型。

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

它用于转换长度相同的一系列类型(在下面的示例中为int,在以下示例中为数字)的可变参数模板列表。

下一步:注册(setFunc()和exec(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]...); }
 };

Last:一个模板结构,该模板结构从先前的结构的可变列表中继承并启用(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...;
 };

使用。

首先,您必须声明一个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);    
 }

答案 3 :(得分:0)

这里提取了luawrapper的代码以应用于上述情况。对于@ Jerod42的code来说,这样做更容易完成。

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

答案 4 :(得分:-1)

您可以使用std:function作为addfun()的参数,也可以使用std::bind