Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ variadic templates and evaluation order

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
like image 952
Sylomex Avatar asked Nov 18 '25 20:11

Sylomex


1 Answers

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.

like image 96
Mike Seymour Avatar answered Nov 21 '25 08:11

Mike Seymour



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!