使用可变参数模板构建函数参数

时间:2017-11-09 17:00:19

标签: c++ c++11 templates variadic-templates

我有这个示例代码,它完成了我需要的3参数函数:

GETPARAM

T (*fn)(P1, P2, ...)模板仅用于显示通话。)

我尝试用可变参数模板来概括它,用于具有任意数量参数的函数,但没有成功。有可能吗?

该模板可用于任何具有任何返回类型和任意数量参数的GETPARAM<Pn>(),即时构建参数,为每个参数调用function onEdit() { var sheet = SpreadsheetApp.getActive().getSheetByName('Market'); var isNumber = sheet.getRange('E2:E1000').getValue() if (isNumber };

需要为脚本语言创建绑定系统,从堆栈中获取参数并在完成后调用C ++函数。

3 个答案:

答案 0 :(得分:4)

  

我尝试用可变参数模板来概括它,用于具有任意数量参数的函数,但没有成功。有可能吗?

是;这很简单

template <typename R, typename ... Args>
R BuildArgsN (R(*fn)(Args...))
 { return fn(GETPARAM<Args>()...); }

以下是一个完整的工作示例

#include <iostream>

template<typename T>T GETPARAM(void) { return T(); }
template<>int GETPARAM(void) { return 123; }
template<>double GETPARAM(void) { return 1.2345; }
template<>const char *GETPARAM(void) { return "hello"; }

template <typename R, typename ... Args>
R BuildArgsN (R(*fn)(Args...))
 { return fn(GETPARAM<Args>()...); }

int print3 (int a, double b, char const * c)
 {
   std::cout << "Print3:" << a << ", " << b << ", " << c << "\n";

   return 1;
 }

int main ()
 {
   BuildArgsN(print3);
 }

答案 1 :(得分:2)

如果要对GETPARAM进行调用,则必须在保证特定订单的上下文中展开可变参数包。一个选项是list initialization

  

每个初始化子句在任何初始化子句之前都会被排序   在braced-init-list中跟随它。这与之形成鲜明对比   函数调用表达式的参数,这些参数是未排序的。

让我们考虑您给出的示例:您可以展开参数包,在构造代理对象的花括号内部产生#include <iostream> int pos = 0;// DEBUG: observe the order of `GETPARAM` calls template<typename T>T GETPARAM(); template<> int GETPARAM() { return 100 + pos++; } template<> double GETPARAM() { return 100.5 + pos++; } template<> const char* GETPARAM() { pos++; return "hello"; } //////////////////////////////////////////////////////////////////////////////// template<class Ret> struct ArgEvalOrderer { Ret ret; template<class... Args> ArgEvalOrderer( Ret(*f)(Args...), Args... args ) : ret{f(args...)} {} operator Ret() const { return ret; } }; template<class Ret, class... Args> Ret call_after_ordered_argfetch(Ret(*f)(Args...)) { // evaluation order guaranteed by braced init list return ArgEvalOrderer<Ret>{f, GETPARAM<Args>()...}; } template<class Ret, class... Args> Ret call_after_ordered_argfetch_buggy(Ret(*f)(Args...)) { // BUGGY: NO GUARANTEE on evaluation order return ArgEvalOrderer<Ret>(f, GETPARAM<Args>()...); } template<class Ret, class... Args> Ret call_after_unordered_argfetch(Ret(*f)(Args...)) { // BUGGY: NO GUARANTEE on evaluation order return f(GETPARAM<Args>()...); } int print7(int a, int b, double c, int d, double e, const char* f, double g) { std::cout << "print7: " << a << ", " << b << ", " << c << ", " << d << ", " << e << ", " << f << ", " << g << std::endl; return 1; } int main() { call_after_ordered_argfetch(print7); call_after_ordered_argfetch_buggy(print7); call_after_unordered_argfetch(print7); return 0; } 调用。代理对象可以隐式转换为函数的返回类型。

g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out

print7: 100, 101, 102.5, 103, 104.5, hello, 106.5

print7: 113, 112, 111.5, 110, 109.5, hello, 107.5

print7: 120, 119, 118.5, 117, 116.5, hello, 114.5

请注意,较高版本是唯一一个保证有序评估的版本。实际上,I observe (online demo)以下输出:

[HubName("hitCounter")]
public class HitCounterHub : Hub
{
    private static int _hitCount = 0;    
    public void Recordhit()
    {
        _hitCount += 1;
        this.Clients.All.onHitRecorded(_hitCount);
    }
}

答案 2 :(得分:0)

这是用于用任意数量的参数静态构建任何函数的代码。它完全独立于任何编译器和的库(使用的printf除外,但是您可以安全地删除它们)。但由于可变参数模板,因此需要

#include <stdio.h>

// SIZEOF Type Package
template<typename ... Tn>
struct SIZEOF
{ static const unsigned int Result = 0; };

template<typename T1, typename ... Tn>
struct SIZEOF<T1, Tn ...>
{ static const unsigned int Result = sizeof(T1) + SIZEOF<Tn ...>::Result ; };

template<int ...>
struct MetaSequenceOfIntegers { };

template<int AccumulatedSize, typename Tn, int... GeneratedSequence>
struct GeneratorOfIntegerSequence;

template<
            int AccumulatedSize, 
            typename Grouper, 
            typename Head, 
            typename... Tail, 
            int... GeneratedSequence
        >
struct GeneratorOfIntegerSequence< 
        AccumulatedSize, Grouper( Head, Tail... ), GeneratedSequence... >
{
    typedef typename GeneratorOfIntegerSequence
            < 
                AccumulatedSize + sizeof(Head), 
                Grouper( Tail... ), 
                GeneratedSequence..., 
                AccumulatedSize
            >::type type;
};

template<int AccumulatedSize, typename Grouper, int... GeneratedSequence>
struct GeneratorOfIntegerSequence<AccumulatedSize, Grouper(), GeneratedSequence...>
{
  typedef MetaSequenceOfIntegers<GeneratedSequence...> type;
};

template<typename Tn>
class Closure;

template<typename ReturnType, typename... Tn>
class Closure<ReturnType( Tn... )>
{
public:
    typedef ReturnType(*Function)(Tn ...);
    static const unsigned int PARAMETERS_COUNT = sizeof...( Tn );
    static const unsigned int PARAMETERS_LENGTH = SIZEOF<Tn ...>::Result;

private:
    Function _entry;
    char* _parameters;

public:
    Closure(Function _entry, Tn ... an): _entry(_entry)
    {
        printf( "Closure::Closure(_entry=%d, PARAMETERS_COUNT=%d, 
                PARAMETERS_LENGTH=%d, sizeof=%d) => %d\n",
                &_entry, PARAMETERS_COUNT, PARAMETERS_LENGTH, sizeof(*this), this );

        if(PARAMETERS_LENGTH) _parameters = new char[PARAMETERS_LENGTH];
        pack_helper( _parameters, an ... );
    }

    ~Closure() {
        printf( "Closure::~Closure(this=%d, _entry=%d,
                PARAMETERS_COUNT=%d, PARAMETERS_LENGTH=%d, sizeof=%d)\n",
                this, &_entry, PARAMETERS_COUNT, PARAMETERS_LENGTH, sizeof(*this) );

        if(PARAMETERS_LENGTH) delete _parameters;
    }

    ReturnType operator()() {
        return _run( typename GeneratorOfIntegerSequence< 0, int(Tn...) >::type() );
    }

private:
    template<int ...Sequence>
    ReturnType _run(MetaSequenceOfIntegers<Sequence...>)
    {
        printf( "Closure::_run(this=%d)\n", this );
        return _entry( unpack_helper<Sequence, Tn>()... );
    }

    template<const int position, typename T>
    T unpack_helper()
    {
        printf( "Closure::unpack_helper(Head=%d, address=%d(%d), position=%d)\n",
                sizeof( T ), _parameters + position, _parameters, position );

        return *reinterpret_cast<T *>( _parameters + position );
    }

public:
    template<typename Head, typename ... Tail>
    static void pack_helper(char* pointer_address, Head head, Tail ... tail)
    {
        printf( "Closure::pack_helper(
                Head=%d, address=%d)\n", sizeof( Head ), pointer_address );

        *reinterpret_cast<Head *>(pointer_address) = head;
        pack_helper(pointer_address + sizeof( Head ), tail ...);
    }

    static void pack_helper(char* pointer_address) {}
};

/**
 * Create a closure which can have any return type.
 */
template<typename ReturnType, typename ... Tn>
Closure< ReturnType(Tn ...) > create_closure( 
        ReturnType(*_entry)( Tn ... ), Tn ... an )
{
    auto closure = new Closure< ReturnType(Tn ...) >( _entry, an ... );
    printf( "create_closure=%d\n", closure );
    return *closure;
}

char test_function1(char arg1, int arg2, bool arg3) {
    printf("   test_function1: %c, %d, %d\n", arg1, arg2, arg3);
}

char test_function2(const char* arg1, const char* arg2, char arg3) {
    printf("   test_function2: %s, %s, %c\n", arg1, arg2, arg3);
}

char test_function3() {
    printf("   test_function3\n");
}

void test_function4() {
    printf("   test_function4\n");
}

void test_function5(const char* arg1) {
    printf("   test_function5=%s\n", arg1);
}

template<typename ... Tn>
void test_closure(Tn ... an) {
    auto closure = create_closure(an ...);
    closure();
    printf( "\n" );
}

// clang++ -Xclang -ast-print -fsyntax-only test.cpp > expanded.cpp
int main()
{
    test_closure( &test_function1, 'a', 10, false );
    test_closure( &test_function2, "test1", "test2", 'b' );
    test_closure( &test_function3 );
    test_closure( &test_function4 );
    test_closure( &test_function5, "Testa 3" );
    test_closure( &test_function5, "Testa 4" );
}

运行它,您将看到测试结果:

$ g++ -o test test_variadic_critical_section_dynamic.cpp && ./test
Closure::Closure(_entry=-13672, 
        PARAMETERS_COUNT=3, PARAMETERS_LENGTH=6, sizeof=16) => 164864
Closure::pack_helper(Head=1, address=164976)
Closure::pack_helper(Head=4, address=164977)
Closure::pack_helper(Head=1, address=164981)
create_closure=164864
Closure::_run(this=-13520)
Closure::unpack_helper(Head=1, address=164981(164976), position=5)
Closure::unpack_helper(Head=4, address=164977(164976), position=1)
Closure::unpack_helper(Head=1, address=164976(164976), position=0)
   test_function1: a, 10, 0

Closure::~Closure(this=-13520, _entry=-13520, 
        PARAMETERS_COUNT=3, PARAMETERS_LENGTH=6, sizeof=16)
Closure::Closure(_entry=-13672, 
        PARAMETERS_COUNT=3, PARAMETERS_LENGTH=17, sizeof=16) => 164976
Closure::pack_helper(Head=8, address=165008)
Closure::pack_helper(Head=8, address=165016)
Closure::pack_helper(Head=1, address=165024)
create_closure=164976
Closure::_run(this=-13520)
Closure::unpack_helper(Head=1, address=165024(165008), position=16)
Closure::unpack_helper(Head=8, address=165016(165008), position=8)
Closure::unpack_helper(Head=8, address=165008(165008), position=0)
   test_function2: test1, test2, b

Closure::~Closure(this=-13520, _entry=-13520, 
        PARAMETERS_COUNT=3, PARAMETERS_LENGTH=17, sizeof=16)
Closure::Closure(_entry=-13624, 
        PARAMETERS_COUNT=0, PARAMETERS_LENGTH=0, sizeof=16) => 165008
create_closure=165008
Closure::_run(this=-13520)
   test_function3

Closure::~Closure(this=-13520, _entry=-13520, 
        PARAMETERS_COUNT=0, PARAMETERS_LENGTH=0, sizeof=16)
Closure::Closure(_entry=-13624, 
        PARAMETERS_COUNT=0, PARAMETERS_LENGTH=0, sizeof=16) => 165040
create_closure=165040
Closure::_run(this=-13520)
   test_function4

Closure::~Closure(this=-13520, _entry=-13520, 
        PARAMETERS_COUNT=0, PARAMETERS_LENGTH=0, sizeof=16)
Closure::Closure(_entry=-13624, 
        PARAMETERS_COUNT=1, PARAMETERS_LENGTH=8, sizeof=16) => 165072
Closure::pack_helper(Head=8, address=609568)
create_closure=165072
Closure::_run(this=-13520)
Closure::unpack_helper(Head=8, address=609568(609568), position=0)
   test_function5=Testa 3

Closure::~Closure(this=-13520, _entry=-13520, 
        PARAMETERS_COUNT=1, PARAMETERS_LENGTH=8, sizeof=16)
Closure::Closure(_entry=-13624, 
        PARAMETERS_COUNT=1, PARAMETERS_LENGTH=8, sizeof=16) => 609568
Closure::pack_helper(Head=8, address=609600)
create_closure=609568
Closure::_run(this=-13520)
Closure::unpack_helper(Head=8, address=609600(609600), position=0)
   test_function5=Testa 4

Closure::~Closure(this=-13520, _entry=-13520, 
        PARAMETERS_COUNT=1, PARAMETERS_LENGTH=8, sizeof=16)

您可以使用clang++运行它以查看生成的模板代码:

$ clang++ -Xclang -ast-print -fsyntax-only test.cpp > expanded.cpp
// ...
private:
    template<> char _run<<0, 8, 16>>(MetaSequenceOfIntegers<0, 8, 16>) 
    {
        return this->_entry(
            this->unpack_helper<0, const char *>(), 
            this->unpack_helper<8, const char *>(), 
            this->unpack_helper<16, char>()
        );
    }

    template<> const char *unpack_helper<0, const char *>() 
    {
        return *reinterpret_cast<const char **>(this->_parameters + 0);
    }

    template<> const char *unpack_helper<8, const char *>() {
        return *reinterpret_cast<const char **>(this->_parameters + 8);
    }

    template<> char unpack_helper<16, char>() {
        return *reinterpret_cast<char *>(this->_parameters + 16);
    }
// ...

参考

  1. How to reverse an integer parameter pack?
  2. Can we see the template instantiated code by C++ compiler
  3. Variadic templates, parameter pack and its discussed ambiguity in a parameter list
  4. "unpacking" a tuple to call a matching function pointer