2

I need a way to pass a variable amount of parameters to a function in this circumstance:

template<typename ...T>
struct Lunch
{
    Lunch(T...){}
};

template<typename T>
T CheckLuaValue(lua_State* luaState,int index)
{
    //Do Stuff
    return value;
}

template <class MemberType, typename ReturnType, typename... Params>
struct MemberFunctionWrapper <ReturnType (MemberType::*) (Params...)>
{
    static int CFunctionWrapper (lua_State* luaState)
    {
        ReturnType (MemberType::*)(Params...) functionPointer = GetFunctionPointer();
        MemberType* member = GetMemberPointer();

        int index = 1;

        //Get a value for each type in Params
        Lunch<Params...>
        {
           (CheckLuaValue<Params>(luaState,index), index++, void(), 0)...
        };

CheckLuaValue returns a value for each type in Params. My problem is that I now need a way to call my function with all of those values

        member->*functionPointer(returnedLuaValues); 

    }
};

How would I go about doing this?

sFuller
  • 1,305
  • 2
  • 14
  • 23

2 Answers2

2

So I stole some of Luc Danton's sequence advice to sFuller and Pubby (about sequences), and I generated this "stop messing around with operator," version:

#include <iostream>
struct lua_State {};
template<typename T>
T CheckLuaValue( int n, lua_State* l)
{
    std::cout << "arg[" << n << "] gotten\n";
    return T(n);
}

template<int ...>
struct seq { };

// generates a seq< First, ..., Last-1 > as "type"
template<int First, int Last>
struct gen_seq
{
  template<int N, int... S>
  struct helper : helper<N-1, N-1, S...> {};
  template<int... S>
  struct helper<First, S...> {
    typedef seq<S...> type;
  };
  typedef typename helper<Last>::type type;
};

template< typename X >
struct MemberFunctionWrapper;

template< typename F >
struct MemberFunctionHelper
{
    typedef F MethodPtr;
};
template<class InstanceType, typename ReturnType, typename... Params>
struct MemberFunctionWrapper< ReturnType(InstanceType::*)(Params...) >
{
  typedef MemberFunctionHelper<ReturnType(InstanceType::*)(Params...)> Helper;
  typedef typename Helper::MethodPtr MethodPtr;
  static MethodPtr& GetFunctionPointer() {static MethodPtr pFunc; return pFunc;}
  static InstanceType*& GetMemberPointer() {static InstanceType* pThis;return pThis;}
  template<int n, typename Param>
  static auto GetLuaValue( lua_State* luaState )->decltype(CheckLuaValue<Param>(n,luaState))
  {
    return CheckLuaValue<Param>(n,luaState);
  }

  template< typename sequence >
  struct call;

  template< int... I >
  struct call<seq<I...>>
  {
    ReturnType operator()( lua_State* luaState, InstanceType* instance, MethodPtr method ) const
    {
      return (instance->*method)( GetLuaValue<I,Params>( luaState )... );
    }
  };
  static int CFunctionWrapper( lua_State* luaState)
  {
    MethodPtr func = GetFunctionPointer();
    InstanceType* instance = GetMemberPointer();
    ReturnType retval = call< typename gen_seq< 1, sizeof...(Params)+1 >::type >()( luaState, instance, func );
    return 0;
  }
};

struct test{ int foo(int x, double d){std::cout << "x:" << x << " d:" << d << "\n";}};
int main(){
    typedef MemberFunctionWrapper< int(test::*)(int, double) > wrapper;
    test bar;
    wrapper::GetFunctionPointer() = &test::foo;
    wrapper::GetMemberPointer() = &bar;
    wrapper::CFunctionWrapper(0);
}

now, note that the calls to CheckLuaValue can be out of order (ie, it can ask for arg 2 from Lua before arg 1), but the right one will be passed to the right argument.

Here is a test run: http://ideone.com/XVmQQ6

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
1

If I understand correctly, you should change the definition Lunch to invoke the member function pointer:

template <class MemberType, typename ReturnType, typename... Params>
struct MemberFunctionWrapper <ReturnType (MemberType::*) (Params...)>
{
  template<typename ...T>
  struct foo // new Lunch
   {
     foo(ReturnType (MemberType::*)(Params...) functionPointer, MemberType* member, T... args){
       member->*functionPointer(args...);
     }
   };

   static int CFunctionWrapper (lua_State* luaState)
    {
        ReturnType (MemberType::*)(Params...) functionPointer = GetFunctionPointer();
        MemberType* member = GetMemberPointer();

        int index = 1;
        //member->*(bindedMemberFunction->memberFunction);

        //Get a value for each type in Params
        foo<Params...>
        {
           functionPointer,
           member,
           (++index, CheckLuaValue<Params>(luaState,index))...
        };
    }
};
Pubby
  • 51,882
  • 13
  • 139
  • 180
  • shouldn't `MemberType member` in the constructor be `MemberType* member` ? – sFuller Nov 19 '12 at 04:10
  • @sFuller Yeah, I didn't compile it so there might be a few syntax errors. – Pubby Nov 19 '12 at 04:18
  • 2
    Parameter arguments are not guaranteed to execute in order. How are you guaranteeing that index++ is evaluated in the right order here? Ie, the compiler could evaluate argument 7, then argument 0, then argument 3. I'm guessing that some of those commas are the comma operator? -- which makes me wonder how it is supposed to work (as `foo<...>` is called with a bunch of `0`s?), plus I suspect you might want to comment the use of the comma operator when you use it in the middle of calling a function. :) – Yakk - Adam Nevraumont Nov 19 '12 at 04:22
  • @Yakk the 0 is there in case Params is empty. as far as I know, void() is unnecessary and can be removed. I'll have to increment the index elsewhere. – sFuller Nov 19 '12 at 04:28
  • 2
    @Yakk Comma operator has a sequence point and using brace enclosed initializer syntax enforces left-to-right ordering. sFuller seems to know this. The `, 0` is to work around `void` functions, although this is clearly incorrect here. – Pubby Nov 19 '12 at 04:30
  • @sFuller There is something wrong with your pack expansion then. `(expression_involving_Pack, void(), 0)...` is `0, 0, ..., 0` with exactly `sizeof...(Pack)` zeroes (which includes 0 zero if the pack is empty) -- putting aside side-effects. Did you intend for something like `Lunch { 0, CheckLuaValue(luaState, index++)... }`? – Luc Danton Nov 19 '12 at 04:37
  • Ah! Ya, found it: §8.5.4.4 (draft n3290) -- order of evaluation is fixed for `{}` enclosed `initializer_list`s. So, start off with index of zero, then `CheckLuaValue(luaState,++index)...`? Or do we still need to `(++index, CheckLuaValue(luaState,index))...` to prevent the `++index's from being evaluated out of order? – Yakk - Adam Nevraumont Nov 19 '12 at 04:38
  • It might be worth mentioning [indices](http://stackoverflow.com/a/7858971/726300), if not for just the fact that it would avoid the order of evaluation bickering altogether. – Luc Danton Nov 19 '12 at 04:43
  • @LucDanton Yes, I think it should be `Lunch{ CheckLuaValue(luaState,index++))... }` as the zeroes are completely unnecessary. – sFuller Nov 19 '12 at 04:43
  • On the other hand, a more sane method might involve indexing the `Params` via `template struct IndexedType {enum{value=n};typedef Param type;};` repackaging in one pass, then write `CheckLuaValueIndexed` to take an `IndexedType` and get its index from the type? ... which Luc just said as I was typing this. Sigh. That trick is less of a hack. :) @sFuller, do what @Luc suggested in his previous post. – Yakk - Adam Nevraumont Nov 19 '12 at 04:44
  • @Pubby I changed the first parameter in the foo constructor to `ReturnType (MemberType::*functionPointer)(Params...)` (yours didn't compile), but now I get this error at `member->*functionPointer(args...);` : `error: must use ‘.*’ or ‘->*’ to call pointer-to-member function in ‘functionPointer (...)’, e.g. ‘(... ->* functionPointer) (...)’` – sFuller Nov 19 '12 at 04:49
  • 1
    @sFuller Syntax when using ptmf's should always be `(a.*b)(c, ..., z)` or `(a->*b)(c, ..., z)`. – Luc Danton Nov 19 '12 at 05:45