2

This is quite possibly the wrong title, but I don't know how else to describe it. What I'm trying to do is call a C++ function from my scripting language (running in a VM). I've run into some trouble figuring out how to pass in parameters to the function.

My best solution so far is to do this:

void func(int a) {

// Start param copy
char c;
char* par_start = &c - sizeof(char) - getCurrentFuncParamOffset();
copyCurrentParams(par_start);
// End copy

// Code

}

Then, to call the function I first expose it to the VM, giving the parameters to it. This is some shortened code, but everything is cast to a void(*) so it can be stored in a hash table.

EXPOSE(test, int);
vm.call("test", 12)

EXPOSE grabs a pointer to the function test, and stores that it requires a single into to be called. It stores the pointer as a void(*)() in a hash table so that when I want to call it, I can make a call from the VM and it is resolved. Then the code inside of the function (which I have expanded from a macro in the question) will copy the parameters that were passed in from the call to the VM to the parameters of the function.

This works but it is not the most elegant solution, especially because I'll have to call a macro for each function that I want to be exposed for scripting. Is there a better solution? Thanks.

8
  • Your part under "Then I do" doesn't really have any connection to the above part. It's hard to really understand enough to help. Commented Jan 9, 2017 at 14:16
  • @AndyG I apologize, hopefully the edit clears it up Commented Jan 9, 2017 at 14:19
  • Not quite. What's the EXPOSE macro? What is cppFunction? What is the point of void(*ptr)()=(void(*)());? Commented Jan 9, 2017 at 14:21
  • @AndyG very sorry, typed this on my phone haha. Hopefully another edit helps Commented Jan 9, 2017 at 14:26
  • Yeah it's a bit better. Now I'm interested in knowing a little more about your hash table. Is it simply a global hash table that will do some kind of check (dispatch) on parameter types so that it can be re-cast to the correct function type? We could write a template instead of a MACRO to do perform the storage. Commented Jan 9, 2017 at 14:29

1 Answer 1

2

You can just as well use what C++ has to offer. Here's a little example I threw together.

class ScriptObj {}; // Your type that encapsulates script objects. 
                    // May be as simple as an integer or a string,
                    // or arbitrarily complex like PyObj

template <typename T> T from_script(ScriptObj); // conversion to and from
template <typename T> ScriptObj to_script(T);   // C++ types. You provide
                                                // specialized implementations.
                                                // Bad conversions should throw.

// Abstract base class for C++ functions callable from the scripts.
// The scripting engine should pass a vector of parameters and a pointer to result.
struct script2cxx
{
    virtual ~script2cxx() {}
    virtual ScriptObj operator()(const std::vector<ScriptObj>& params) = 0;
};    


// Concrete class that exposes a C++ function to the script engine.
template <class Res, class ... Param>
struct script2cxx_impl : script2cxx
{

    using funcType = Res(*)(Param...);

    virtual ScriptObj operator()(const std::vector<ScriptObj>& params)
    {
        if (sizeof...(Param) != params.size())
            throw std::domain_error("Invalid size of parameter array");
        return to_script<Res>(call_impl<std::tuple<Param...>>(func, params, std::make_index_sequence<sizeof...(Param)>()));
    }

    template <class Tuple, std::size_t... N>
        Res call_impl(funcType func, const std::vector<ScriptObj>& params, std::index_sequence<N...>)
        {
            return func(from_script<typename std::tuple_element<N, Tuple>::type>(params[N])...);
        };

    funcType func;

    script2cxx_impl(funcType func) : func(func) {}
};

// a helper biold function
template  <class Res, class ... Param>
auto expose(Res(*func)(Param...)) {
    return new script2cxx_impl<Res, Param...>(func);
}

Now you can build a map of script2cxx (smart) pointers and call them with vectors of your script objects.

 std::map<std::string, std::unique_ptr<script2cxx>> exposed;


 int foo(double, char[], int**) {...}

 // below is the only line you need to add
 // if you want to expose any standalone C++ function.
 // no boilerplate.

 exposed["foo"]=expose(foo); // you can wrap this in a macro if you want

And call them:

 std::vector<ScriptObj> somevector = ...;
 std::string somestring = ...;
 (*exposed[somestring])(somevector); 

No unsafe casts and/or void pointers were harmed in making this example.

Sign up to request clarification or add additional context in comments.

1 Comment

I've never used the ellipsis for templates before, great solution! Thanks

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.