0

I am trying to write some code that wraps C++ functions into something that Lua can use. One of the main problems I'm trying to solve is passing values popped from the Lua stack into parameters for the C++ functions. The naive approach that has worked for me has been:

        template<typename Ret, typename... Args>
        int luaDRCall(lua_State* s) {
            typedef RDelegate<Ret, Args...>* FunctionPtr;
            FunctionPtr f = (FunctionPtr)popUpvalueObject(s);
            Ret retValue = f->invoke(popValue<Args>(h)...);
            return ScriptValueSender<Ret>::push(h, retValue);
        }

However, this has caused me problems because "popValue(h)..." is not guaranteed to execute from right to left. Under a "Debug" build from MSVC, it works fine.... but the "Release" version calls the functions in reverse order. Therefore, I have written some code that is guaranteed to pop the values in correct order:

        template<typename Ret, typename... Args>
        int luaDRCall(lua_State* s) {
            void* del = popUpvalueObject(s);
            return fRCall<Ret, Args...>(s, del);
        }
        template<typename Ret, typename Arg, typename... Args>
        int fRCall(EnvironmentHandle h, void* del) {
            Arg v = popValue<Arg>(h);
            return fRCall<Ret, Args..., Arg>(h, del, v);
        }
        template<typename Ret, typename Arg, typename... Args, typename... Popped>
        int fRCall(EnvironmentHandle h, void* del, Popped... vPopped) {
            Arg v = popValue<Arg>(h);
            return fRCall<Ret, Args..., Popped..., Arg>(h, del, vPopped..., v);
        }
        template<typename Ret, typename... Args>
        int fRCall(EnvironmentHandle h, void* del, Args... vPopped) {
            typedef RDelegate<Ret, Args...>* FunctionPtr;
            FunctionPtr f = (FunctionPtr)del;
            Ret retValue = f->invoke(vPopped...);
            return ScriptValueSender<Ret>::push(h, retValue);
        }

Now, attempting to compile this code results in:

2>c:\induztry\git\vorb\include\script\Script.h(66): fatal error C1060: compiler is out of heap space
2>c1xx : fatal error C1063: INTERNAL COMPILER ERROR
2>           Please choose the Technical Support command on the Visual C++ 
2>           Help menu, or open the Technical Support help file for more information

Anybody have any ideas what is wrong and how I can fix it? Any other solutions to this problem would also be appreciated. Thank you.

EDIT 1: I have changed my code to the following with the help of one of the answers below, and it works now correctly as long as the function has at least one argument. If no arguments are present, I have compiler errors:

        template<size_t... Is>
        struct index_sequence {};
        template<size_t N, size_t... Is>
        struct make_index_sequence : make_index_sequence<N - 1, N - 1, Is...> {};
        template<size_t... Is>
        struct make_index_sequence<0, Is...> : index_sequence<Is...> {};
        template<typename... T>
        struct index_sequence_for : make_index_sequence<sizeof...(T)> {};

        template <typename Ret, typename F, typename Tuple, size_t... Is>
        Ret invoke(F f, Tuple& t, index_sequence<Is...>) {
            return f->invoke(std::get<Is>(t)...);
        }
        template<typename Ret, typename... Args>
        int luaDRCall(EnvironmentHandle h) {
            typedef RDelegate<Ret, Args...>* FunctionPtr;
            FunctionPtr f = (FunctionPtr)popUpvalueObject(h);
            std::tuple<Args...> tValue { popValue<Args>(h)... };
            Ret retValue = invoke<Ret>(f, tValue, index_sequence_for<Args...>());
            return ScriptValueSender<Ret>::push(h, retValue);
        }

It is necessary for me to put this all into one function for use, and attempting to do template specializations for no arguments results in compiler errors.

1>c:\induztry\git\vorb\include\script\Script.h(68): error C2143: syntax error : missing '}' before '<fake-type>'
1>          c:\induztry\git\vorb\include\script\Script.h(105) : see reference to function template instantiation 'i32 vorb::script::impl::fRCall<Ret,>(vorb::script::EnvironmentHandle,void *)' being compiled
1>          with
1>          [
1>              Ret=int
1>          ]
1>          c:\induztry\git\vorb\include\script\Script.h(122) : see reference to function template instantiation 'int vorb::script::impl::luaDRCall<Ret,>(lua_State *)' being compiled
1>          with
1>          [
1>              Ret=int
1>          ]
1>          C:\InduZtry\Git\Vorb\include/script/Environment.h(76) : see reference to function template instantiation 'vorb::script::ScriptFunc vorb::script::fromRDelegate<Ret,>(void)' being compiled
1>          with
1>          [
1>              Ret=int
1>          ]
1>          VorbScript.cpp(101) : see reference to function template instantiation 'void vorb::script::Environment::addCRDelegate<int,>(const std::string &,RDelegate<int,> &)' being compiled
1>c:\induztry\git\vorb\include\script\Script.h(68): error C2143: syntax error : missing ';' before '<fake-type>'
1>c:\induztry\git\vorb\include\script\Script.h(69): error C2146: syntax error : missing ';' before identifier 'retValue'
1>c:\induztry\git\vorb\include\script\Script.h(69): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>c:\induztry\git\vorb\include\script\Script.h(69): error C2065: 'f' : undeclared identifier
1>c:\induztry\git\vorb\include\script\Script.h(69): error C2065: 'tValue' : undeclared identifier
1>c:\induztry\git\vorb\include\script\Script.h(69): error C3546: '...' : there are no parameter packs available to expand
1>c:\induztry\git\vorb\include\script\Script.h(69): error C2065: 'Args' : undeclared identifier
1>c:\induztry\git\vorb\include\script\Script.h(69): error C3544: 'T': parameter pack expects a type template argument
1>c:\induztry\git\vorb\include\script\Script.h(69): error C2955: 'vorb::script::impl::index_sequence_for' : use of class template requires template argument list
1>          c:\induztry\git\vorb\include\script\Script.h(58) : see declaration of 'vorb::script::impl::index_sequence_for'
1>c:\induztry\git\vorb\include\script\Script.h(70): error C2059: syntax error : 'return'
1>c:\induztry\git\vorb\include\script\Script.h(70): error C2923: 'vorb::script::ScriptValueSender' : 'vorb::script::impl::Ret' is not a valid template type argument for parameter 'T'
1>          c:\induztry\git\vorb\include\script\Script.h(69) : see declaration of 'vorb::script::impl::Ret'
1>c:\induztry\git\vorb\include\script\Script.h(70): error C2955: 'vorb::script::ScriptValueSender' : use of class template requires template argument list
1>          c:\induztry\git\vorb\include\script\ScriptValueSenders.h(36) : see declaration of 'vorb::script::ScriptValueSender'
1>c:\induztry\git\vorb\include\script\Script.h(70): error C2027: use of undefined type 'vorb::script::ScriptValueSender'
1>          c:\induztry\git\vorb\include\script\ScriptValueSenders.h(36) : see declaration of 'vorb::script::ScriptValueSender'
1>c:\induztry\git\vorb\include\script\Script.h(71): error C2059: syntax error : '}'
1>c:\induztry\git\vorb\include\script\Script.h(71): error C2143: syntax error : missing ';' before '}'
1>c:\induztry\git\vorb\include\script\ScriptImpl.h(82): error C2143: syntax error : missing ';' before '{'
1>c:\induztry\git\vorb\include\script\ScriptImpl.h(82): error C2447: '{' : missing function header (old-style formal list?)
Darth Zaloj
  • 153
  • 6
  • 3
    An internal compiler error is always a compiler bug. The best that you can do is find a workaround that gives you the behavior you want; I don't have any suggestions there, but this isn't necessarily your problem (I haven't read the pile of code in enough detail). – Jason R Mar 26 '15 at 18:29
  • I could have sworn unpacking was always guaranteed to pop in the right order... – Mooing Duck Mar 26 '15 at 18:33
  • @MooingDuck I think the issue comes from an unspecified execution order of the unpacked functions. See http://stackoverflow.com/a/833929/234175. So hypothetically, the compiler could execute the middle unpacked function first, then the last and finally the first. Popping stuff in that order will clearly cause problems on the lua stack. – greatwolf Mar 26 '15 at 19:22
  • @DarthZaloj That internal error suggest there might be runaway template instantiation in MSVC. Have you tried building this with mingw or clang? – greatwolf Mar 26 '15 at 19:25
  • @greatwolf: That link is about general function parameters. I'm pretty sure variadic template parameter unpacking has special rules. I'll see what I can find. – Mooing Duck Mar 26 '15 at 20:09
  • @DarthZaloj, did you try this version? http://stackoverflow.com/questions/19942877/c-variadic-templates-and-evaluation-order (Not quite a dupe since link isn't talking about a crash) – Mooing Duck Mar 26 '15 at 20:12
  • @greatwolf: I was mistaken, it was _initializer lists_ that have the special rules. http://stackoverflow.com/a/19943107/845092 As such, if he can convert the code to something using _those_, he's golden. – Mooing Duck Mar 26 '15 at 20:13
  • How could I convert the code to something using initializer lists? At the very end the function expects multiple arguments with different types, not one tuple or special class.... – Darth Zaloj Mar 26 '15 at 20:40
  • If you got a `std::tuple` you can always 'extracts' the argument for the call with `std::index_sequence` and `std::get`. – Jarod42 Mar 26 '15 at 20:44

2 Answers2

1

Following should work, it uses the constructor of std::tuple to force the order of evaluation (left to right):

template <typename Ret, typename Tuple, std::size_t ... Is>
Ret Invoke(FunctionPtr f, Tuple& t, std::index_sequence<Is...>)
{
    return f->invoke(std::get<Is>(t)...);
}

template<typename Ret, typename... Args>
int luaDRCall(lua_State* s) {
    typedef RDelegate<Ret, Args...>* FunctionPtr;
    FunctionPtr f = (FunctionPtr)popUpvalueObject(s);
    std::tuple<Args...> t{popValue<Args>(h)...};
    Ret retValue = Invoke<Ret>(f, t, std::index_sequence_for<Args...>());
    return ScriptValueSender<Ret>::push(h, retValue);
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
1

I was able to fix this with partial specialization:

        template<size_t... Is>
        struct index_sequence {};
        template<size_t N, size_t... Is>
        struct make_index_sequence : make_index_sequence<N - 1, N - 1, Is...> {};
        template<size_t... Is>
        struct make_index_sequence<0, Is...> : index_sequence<Is...> {};
        template<typename... T>
        struct index_sequence_for : make_index_sequence<sizeof...(T)> {};

        template <typename Ret, typename F, typename Tuple, size_t... Is>
        Ret invoke(F f, Tuple& t, index_sequence<Is...>) {
            return f->invoke(std::get<Is>(t)...);
        }
        template<typename Ret, typename... Args>
        i32 fRCall(EnvironmentHandle h, RDelegate<Ret, Args...>* del) {
            typedef RDelegate<Ret, Args...>* FunctionPtr;
            FunctionPtr f = (FunctionPtr)del;
            std::tuple<Args...> tValue { popValue<Args>(h)... };
            Ret retValue = invoke<Ret>(f, tValue, index_sequence_for<Args...>());
            return ScriptValueSender<Ret>::push(h, retValue);
        }
        template<typename Ret>
        i32 fRCall(EnvironmentHandle h, RDelegate<Ret>* del) {
            typedef RDelegate<Ret>* FunctionPtr;
            FunctionPtr f = (FunctionPtr)del;
            Ret retValue = f->invoke();
            return ScriptValueSender<Ret>::push(h, retValue);
        }

        template<typename Ret, typename... Args>
        int luaDRCall(lua_State* s) {
            void* del = popUpvalueObject(s);
            return fRCall<Ret, Args...>(s, (RDelegate<Ret, Args...>*)del);
        }

Thanks for all the help.

Darth Zaloj
  • 153
  • 6