3

I have the following code:

lib.hxx:

template <typename C, typename R, typename ... Args>
R Lib::extract_call(lua_State* L, R(C::*method)(Args...))
{
  return static_cast<C*>(this)->*method(extract_data<Args>(L)...);
}

lib.cc:

template <>
std::string Lib::extract_data(lua_State* L)
{
  if (! lua_isstring(L, -1))
  {
    return "";
  }

  return lua_tostring(L, -1);
}
[...] // Other specializations following

I am embedding lua in a project, and I'm currently looking for a way to call methods from lua, and from a dispatcher extract arguments automatically from the lua stack. From those "simple" templates it is possible to easily generate the calls needed to extract data from the lua stack given in parameter without any typing error.

BUT, my problem is, when extract_data<Args>(L)... is unpacked, the order of evaluation for all extract_data calls are unspecified (as stated in standard, for optimization purposes), while it really matters in which order you extract data from the lua stack. On the other hand, I can't regroup all of these calls in an initializer list since they are of different type.

Therefore, how can I ensure extract_data calls to be in a specific order, or at least keep an automated way to pass arguments to my member pointer function ?

EDIT: I had forgotten that the calls need to be in revert order, which I don't think is achievable by any language mechanism. Therefore, here is my solution, back to regular, non variadic templates:

template <typename C, typename R, typename A1>
R Lib::extract_call(lua_State* L, R(C::*method)(A1))
{
  return (static_cast<C*>(this)->*method)(extract_data<A1>(L));
}

template <typename C, typename R, typename A1, typename A2>
R Lib::extract_call(lua_State* L, R(C::*method)(A1, A2))
{
  A2 b = extract_data<A2>(L);
  A1 a = extract_data<A1>(L);

  return (static_cast<C*>(this))->*method(a,b);
}

template <typename C, typename R,
          typename A1, typename A2, typename A3>
R Lib::extract_call(lua_State* L, R(C::*method)(A1, A2, A3))
{
  A3 c = extract_data<A3>(L);
  A2 b = extract_data<A2>(L);
  A1 a = extract_data<A1>(L);

  return (static_cast<C*>(this))->*method(a,b,c);
}
// And so on up to 8 arguments
Sylomex
  • 33
  • 4
  • The reverse evaluation is actually rather easy - just call the recursive case before extracting the argument. The complication comes from needing to gather the return values. You can easily do this with `std::tuple`, though: http://coliru.stacked-crooked.com/a/7935d16232f21a5c. The final unpacking into the call can be done with an [index-sequence](http://loungecpp.wikidot.com/tips-and-tricks%3aindices). – Xeo Nov 13 '13 at 12:27

2 Answers2

2

If you can change the method to take a single std::tuple<Args...>, rather than multiple Args..., then you can place extract_data inside a brace-initialiser:

return static_cast<C*>(this)->*method({extract_data<Args>(L)...});

Unlike function arguments, evaluation of the clauses of the initialiser is sequenced from left to right.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
1

Now we will get more complicated templates. The following code I wrote without compiler, there might be some mistakes:

template <unsigned int n>
class tuple_extractor{
    template <typename T, typename ...ArgsOut, typename ...ArgsIn, typename ...ArgsPart>
    static void extractTuple(
            T* obj,
            void (T::*func)(ArgsOut...),
            const std::tuple<ArgsIn...>& inParams,
            ArgsPart... partParams){
        tuple_extractor<n-1>::extractTuple(obj, func, inParams, std::get<n-1>(inParams));
    }
};

template <>
class tuple_extractor<0>{
    template <typename T, typename ...ArgsOut, typename ...ArgsIn>
    static void extractTuple(
            T* obj,
            void (T::*func)(ArgsOut...),
            const std::tuple<ArgsIn...>& inParams,
            ArgsIn... partParams){
        obj->func(partParams...);
    }
};

template <typename C, typename R, typename ... Args>
R Lib::extract_call(lua_State* L, R(C::*method)(Args...))
{
    std::tuple<Args...> tmp{extract_data<Args>(L)...};
    tuple_extractor<sizeof...(Args)>::extractTuple(static_cast<C*>(this), method, tmp);
}

It seems that GCC has a bug that influences ordering of the brace initialization. If you use a an affected version, just use

// For first-to-last order use:
template <typename T, typename ...Args>
inline std::tuple<T, Args...> getTuple(lua_State* L){
    return std::tuple_cat(std::make_tuple<T>(extract_data<T>(L)), getTuple<Args...>(L));
}

template <typename T>
inline std::tuple<T> getTuple(lua_State* L){
    return std::make_tuple<T>(extract_data<T>(L));
}

    template <typename C, typename R, typename ... Args>
R Lib::extract_call(lua_State* L, R(C::*method)(Args...))
{
    std::tuple<Args...> tmp = getTuple<Args...>(L);
    tuple_extractor<sizeof...(Args)>::extractTuple(static_cast<C*>(this), method, tmp);
}

Firstly we create a tuple containing all the parameters, but in an ordered fashion, then we extract obtained tuple into parameters to a method call.

This is answer to the question posted. However I agree with @Mike - if you can change prototype of methods called by the pointer, you should add a overload for a tuple and just pass it as one parameter. Above code could in principle be almost fully inlined and cause very little performance overhead, but I am not sure what today's compilers will actually do with it.

EDIT:

Compilable version here:

j_kubik
  • 6,062
  • 1
  • 23
  • 42
  • I did not test your code, but this is where I wanted to get when thinking about recursive templates, without achieving something usable. There is still a problem in my situation though: arguments need to be extracted in revert order from the lua stack, and I don't think this is possible ... – Sylomex Nov 13 '13 at 10:30
  • I could not compile your code, even after a few fixes the template errors are getting so rude I gave up. I changed for non variadic templates to solve my problem. – Sylomex Nov 13 '13 at 11:39
  • When I have a moment I will probably try to make it compilable, and I am quite certain it is possible to use reverse order as well. – j_kubik Nov 13 '13 at 14:51