0

I have a variadic function accepting any number of mixed parameters: the following code works as expected:

template <typename ... Args>
void call_snippet(lua_State *L, const std::string& name, Args... args) {
    lua_rawgeti(L, LUA_REGISTRYINDEX, snippets[name]);

    int nargs = 0;
    for (auto &&x : {args...}) {
        lua_pushinteger(L, x);
        nargs++;
    }
    lua_pcall(L, nargs, LUA_MULTRET, 0);
}

but falls short from needs because it assumes all parameters are int (or convertible to int). I would need something along the lines:

template <typename ... Args>
void call_snippet(lua_State *L, const std::string& name, Args... args) {
    lua_rawgeti(L, LUA_REGISTRYINDEX, snippets[name]);

    int nargs = 0;
    for (auto &&x : {args...}) {
        switch (typeof(x)) {
        case int:
            lua_pushinteger(L, x);
            nargs++;
            break;
        case float:
            lua_pushnumber(L, x);
            nargs++;
            break;
        case std:string:
            lua_pushcstring(L, x.c_str());
            nargs++;
            break;
        case char*:
            lua_pushcstring(L, x);
            nargs++;
            break;
        default:
            //raise error
            ;
    }
    lua_pcall(L, nargs, LUA_MULTRET, 0);
}

How should I actually implement the above pseudocode?

ZioByte
  • 2,690
  • 1
  • 32
  • 68
  • 2
    Have you tried replacing `lua_pushinteger(L, x);` with `push_something_somewhere(L, x)`, and then defining overloaded functions `push_something_somewhere()`, one that takes an `int` parameter, another that takes a `float`, a third one that takes a `const std::string &`, etc...? You never do this kind of thing yourself, manually. That's yucky. You simply let your compiler do all the yucky work for you. – Sam Varshavchik Oct 12 '22 at 18:58

1 Answers1

1

You should be able create function overloads for calling lua_push... and use a fold expression instead of the loop. The sizeof... operator can be used to determine the number of parameters:

void Push(lua_State* l, std::nullptr_t) = delete;

void Push(lua_State* l, std::string const& str)
{
    lua_pushcstring(l, str.c_str());
}

void Push(lua_State* l, char const* str)
{
    lua_pushcstring(l, str);
}

void Push(lua_State* l, int value)
{
    lua_pushinteger(l, value);
}

void Push(lua_State* l, float value)
{
    lua_pushnumber(l, value);
}

template <typename ... Args>
void call_snippet(lua_State *L, const std::string& name, Args&&... args) {
    lua_rawgeti(L, LUA_REGISTRYINDEX, snippets[name]);

    ((Push(L, std::forward<Args>(args))), ...);

    int nargs = sizeof...(Args);

    lua_pcall(L, nargs, LUA_MULTRET, 0);
}

The following complete example should demonstrate this in a similar scenario:

#include <iostream>
#include <utility>

void PrintNumber(float f)
{
    std::cout << f << "(float)\n";
}

void PrintInt(int i)
{
    std::cout << i << "(int)\n";
}

void PrintCstring(char const* str)
{
    std::cout << str << "(char const*)\n";
}

void Print(std::nullptr_t) = delete;

void Print(std::string const& str)
{
    PrintCstring(str.c_str());
}

void Print(char const* str)
{
    PrintCstring(str);
}

void Print(int value)
{
    PrintInt(value);
}

void Print(float value)
{
    PrintNumber(value);
}

template <typename ... Args>
void PrintArgs(Args&&... args)
{

    ((Print(std::forward<Args>(args))), ...);

    int nargs = sizeof...(Args);

    std::cout << "nargs = " << nargs << '\n';
}

int main()
{
    PrintArgs("foo", std::string("bar"), 42, 99.9f);
}

Note: You may need to add some overloads to resolve ambiguity, e.g. when passing 99.9 instead of 99.9f, since the former is a double which results in ambiguity during overload resolution.

fabian
  • 80,457
  • 12
  • 86
  • 114
  • Can you have a look to my next question (https://stackoverflow.com/questions/74059053/nesting-parameter-pack-functions), please? – ZioByte Oct 13 '22 at 16:30