1
votes

I'm working on my own Lua engine with C++ 11, I want to write a function wrapper that register C++ function to Lua environment with variadic parameter. That's simple in C++ 0x, but boring cause I need to write similar codes to support function with 0~N parameters. function push is used to push T to lua stack, where function upvalue_ get C++ function pointer with lua cclosure, and it assume the funtion is has two parameters T1 and T2, T1 is acquired from lua stack with index 1, and T2 is acquired from lua stack with index 2.

template <typename RVal, typename T1, typename T2>
struct functor<RVal,T1,T2>
{
    static int invoke(lua_State *L) 
    { 
        push(L,upvalue_<RVal(*)(T1,T2)>(L)(read<T1>(L,1),read<T2>(L,2))); 
        return 1; 
    }
};

template<typename T>
T upvalue_(lua_State *L)
{
    return user2type<T>::invoke(L, lua_upvalueindex(1));
}

and with C++ 11, I wrote such code snippets:

template< typename RVal, typename ... ARGS>
struct functor
{
    static int invoke(lua_State* L)
    {
        typedef RVal (*FUNC_PTR)(ARGS...);
        FUNC_PTR f = upvalue_<FUNC_PTR>(L);
        push(L, f(read_stack<ARGS>(L)...));
        return 1;
    }
};

template<typename T>
T read_stack(lua_State* L)
{
    T t = read<T>(L, -1);
    lua_pop(L, 1);
    return t;
}

the code shown above could work, but the parameter order is reversed because read_stack read parameter from the last index -1 always. my question is how to read parameter from lua stack from 1 to N(N equals to sizeof...(ARGS) if ARGS not empty) with variadic template argument and pass them to real function pointer f to make real call?

2
if read_stack get parameter always from 1, it could work: <i> template<typename T> T pop(lua_State *L) { T t = read<T>(L, 1); lua_remove(L, 1); //remove the first parameter return t; } </i> - liu jia
Perhaps this has a solution or ideas: github.com/jeremyong/Selene. - lhf

2 Answers

0
votes

Not specific to Lua, here is a general-purpose C++11 solution to reversing the order of given parameters to a function. In the below code, 'apply' is my example target function (here it just outputs a bit of text based on its variadic parameters). The 'main' functions shows how the helper function 'reverse_and_apply' takes a function (or Functor to be precise) and a set of arguments, and applies the given function to the reversed argument list using some template trickery. Note I apologise for the somewhat anal use of perfect forwarding here, which is technically correct but unfortunately obfuscates the code somewhat. Hopefully you get the main message.

#include <iostream>

template <typename ...Args>
void apply(const char* fmtString, const Args&... args)
{
    char output[512];
    snprintf(output, 512, fmtString, args...);

    std::cout << output << std::endl;
}

template <typename F, typename ...Args>
struct ReverseAndApply;

template <typename F>
struct ReverseAndApply<F>
{
    template <typename ... AlreadyReversed>
    static void doIt(F func, AlreadyReversed&& ... args)
    {
        func(args...);
    }
};

template <typename F, typename FirstArg, typename ...RestArgs>
struct ReverseAndApply<F, FirstArg, RestArgs...>
{
    template <typename ... AlreadyReversed>
    static void doIt(F func, FirstArg&& arg, RestArgs&& ... restArgs, AlreadyReversed&& ... revArgs)
    {
        ReverseAndApply<F, RestArgs...>::doIt(func, std::forward<RestArgs>(restArgs)..., std::forward<FirstArg>(arg), std::forward<AlreadyReversed>(revArgs)...);
    }
};

template <typename F, typename... Args>
void reverse_and_apply(F func, Args&&... args)
{
    ReverseAndApply<F, Args...>::doIt(func, std::forward<Args>(args)...);
}

int main()
{
    reverse_and_apply(apply<double, const char*, int>, 1, (const char*)"abc", 2.0, "%f %s %d");
    return 0;
}
0
votes

Your code in C++11 not even work as the evaluation order of arguments is not defined. It should be easy by using std::integer_sequence in C++14.

Sample code:

template< typename RVal, typename... ARGS>
struct functor
{
    template <std::size_t... Is>
    static int invoke_impl(lua_State *L, std::index_sequence<Is...>)
    {
        typedef RVal (*FUNC_PTR)(ARGS...);
        FUNC_PTR f = upvalue_<FUNC_PTR>(L);
        push(L, f(read<ARGS>(L, Is)...));
        return 1;
    }

    static int invoke(lua_State* L)
    {
        return invoke_impl(L, std::index_sequence_for<ARGS...>{});
    }
};