Search code examples
c++templatesgeneric-programmingduktape

Construct variadic template argument list


Say i have the following scenario:

namespace detail
{
    using duk_c_function_t = std::function<duk_ret_t(duk_context*)>;

    template<typename T_Return(typename ... T_Params), std::function<T_Return(T_Params)>
    duk_ret_t duk_function_proxy(duk_context* ctx)
    {
        const int n = sizeof...(T_Params); //real number of arguments passed.
        duk_idx_t num_arguments = duk_get_top(ctx); //Number of arguments from javascript
        int x = duk_require_int(ctx, 0); //Get integer -> parameter no 1
        const char* c = duk_require_string(ctx, 1); //Get string -> parameter no 2
    }
}
template<typename T_Return(typename ... T_Params)>
duk_c_function_t duk_function(std::function<T_Return(T_Params ...) function_item)
{
    return duk_c_function_t(detail::duk_function_proxy<function_item>);
}

Where duk_function returns a function of the signature duk_ret_t function(duk_context* ctx) { ... }. Now, i know how to call the function object in duk_function_proxy with the variadic templates and such. But there is one problem for me: The javascript interpreter i am using requires that, for every c-function i want to expose, i need query the parameters from the stack, as shown in duk_function_proxy. However, with that being the case, i dont know how to call the function object - i can only store the queried parameters in some sort of container, and thus i cannot call the function object with the given signature. Is there any way to store my queried parameters in some sort of container, and then unpack and pass it to the function object as parameters? I query the required parameters based on the variadic template list, as seen in the prototype.

In short:

I want to iterate over T_Params, get and store the corresponding variables using duk_require_* (in some sort of container), and then use those values to call the function object, which is passed as a template argument.


Solution

  • this should get you started. I've simulated the DUK interface since it's not installed on my machine but you'll get the idea:

    #include <iostream>
    #include <string>
    #include <functional>
    #include <utility>
    
    struct duk_context;
    
    const char* duk_require_string(duk_context*, int i)
    {
        static constexpr const char * strings[] = {
            "i like",
            "hairy",
            "ducks"
        };
        return strings[i];
    }
    
    int duk_require_int(duk_context*, int i)
    {
        return i * 6;
    }
    
    template<class Type> auto get_arg(duk_context* ctx, int i) -> Type;
    
    template<> auto get_arg<std::string>(duk_context* ctx, int i) -> std::string
    {
        return duk_require_string(ctx, i);
    }
    
    template<> auto get_arg<int>(duk_context* ctx, int i) -> int
    {
        return duk_require_int(ctx, i);
    }
    
    template<class...Args, size_t...Is>
    void get_args_impl(duk_context* context, const std::function<void(Args...)>& f, std::index_sequence<Is...>)
    {
        using tuple_type = std::tuple<Args...>;
        f(get_arg<std::tuple_element_t<Is, tuple_type>>(context, Is)...);
    }
    
    template<class...Args>
    void get_args(duk_context* context, const std::function<void(Args...)>& f)
    {
        get_args_impl<Args...>(context, f, std::index_sequence_for<Args...>());
    }
    
    void foo(std::string a, int b, std::string c)
    {
        std::cout << a << " " << b << " " << c << std::endl;
    }
    
    int main()
    {
        duk_context* ctx = nullptr;
    
        get_args(ctx, std::function<void(std::string, int, std::string)>(&foo));
    
        return 0;
    }
    

    expected output:

    i like 6 ducks