c++luamfccom

How do I create a generic wrapper that can call other functions that have arbitrary and arbitrarily many inputs?


I have a series of functions and objects in C++ that will be called/invoked by Lua, I already have existing code that does exactly this (using the MFC COM dispatch) for VBScript (see the bottom of this post), so I could recreate/create wrappers for all of it in C++ using Lua's existing C API. But given the number of functions and classes involved that would take much longer than if I could just create something similar to the COM dispatch and just forward the registered Lua functions straight to the existing C++ functions. Note that all Lua functions in the C API basically have the same signature:

// accepts a Lua state with the input/s in the stack
// returns the number of outputs, pushes "actual" outputs onto the stack
int Func(lua_State* L)

Imagine I have, for example, functions that look like this:

int Add(int x, int y)
double Divide(double x, double y)
void DoStuff(int x, double y, CString abc)

I would need a wrapper (let's call it LuaWrapper::GenericFunction) that would look like the above Lua C API call, read the various typed inputs from the stack, call the corresponding function with those inputs, and return the correctly typed output.

Lua normally does dispatches using this syntax to register a function. Here I am roughly outlining the syntax of how LuaWrapper::GenericFunction would be used:

// have the Lua state redirect scripted calls to "Add" to this function:
// LuaWrapper::GenericFunction takes the Lua state, the function itself, the output type, and a list/string/whatever of the input types; then calls the function on those inputs and pushes the output onto the Lua state's stack
lua_register(L, "Add", Add); // vs:
lua_register(L, "Add", [](lua_State* L) { return LuaWrapper::GenericFunction(L, RealFunctions::Add, INT_TYPE, "INT INT"; });

The issue is that I can't figure out how to get the inputs to work and how to call a completely generic function that could have basically any number of inputs of any type (they are guaranteed not to be references unless they are class objects, which I have a do plan for). I know it is possible since the MFC COM API is basically doing exactly this for VBScript.

Side note, the existing COM dispatch code for VBScript integration basically works like this: a dispatch map is written in the code for the C++ class,

// "Function" outputs an int, and takes two strings and a bool as inputs
// VT_I4 is basically an enum, VTS_??? are strings that get concatenated in this syntax
DISP_FUNCTION(CParentClass, "Function", Function, VT_I4, VTS_BSTR VTS_BSTR VTS_BOOL)

Then is written as a standard function of that class; allowing VBScript to invoke it.

int CParentClass::Function(CString str1, CString str2, bool true_or_false)


Solution

  • I ended up using LuaBridge to resolve this issue. For the most part I got away with using the standard pattern of

    getGlobalNamespace(L)
        .beginClass<CClass>("Class")
            .addData("number", &CClass::number)
            .addProperty("string", CSTRING_GETTER(CClass, CClass::string), CSTRING_SETTER(CClass, CClass::string))
            .addFunction("func", &CClass::func)
        .endClass();
    

    Though as you can see I needed to add a conversion macro (basically an overcomplicated type-cast style getter/setter that LuaBridge can understand) to switch between CString and std::string for the string variables. I also needed to do a few functions using the Lua C API standard using the "addCFunction()" component of LuaBridge for functions that had too many arguments, or multiple inputs. The thing that took the most time was simple writing wrappers for the functions I had that took or returned variables that were BSTR, BOOL, DATE, LPDISPATCH, etc. But that was more tedious than problematic.

    In the end though, everything is working as expected so thanks to everyone else for their help/advice.