6

How can I pass a dynamic generated C++ function as *funcprt to a C API?

The API exports this:

DllImport void api_register_func(char *func_name, void (*funcptr)(char *, char *));

I have to create the function while runtime because I don't know about it before. So I used a class:

class JsFunc
{
    public:
        char * JsFuncName;
        char * JsParameter;

    void RunFunc(char * val1, char * val2)
    {
        printf("\nJsFunc.runFunc executed, JsParameter=%s passed\n",JsParameter);
    }
};

And call it like this:

JsFunc * jsm = new JsFunc ();
jsm->JsFuncName = external_parameter1; 
jsm->JsParameter = external_parameter2; 
api_register_func(external_parameter1, jsm->RunFunc);

But VisualStudio 2015 tells me:

Error C3867 'JsFunc::runFunc': non-standard syntax; use '&' to create a pointer to member VJCS C:\Users\astrauss\Source\Repos\VCJS\VCJS\VCJS.cpp 54

Sorry if the code is bad, I'm not a C/C++ programmer but need to get this running for my daily work. Thanks!

Andreas
  • 63
  • 4
  • Do you have access to C++ 11? – Alyssa Haroldsen Aug 04 '15 at 20:12
  • 3
    Unfortunately most of this makes no sense at all. It seems like you're asking about the solution instead of asking about the problem ([XY Problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem)). Try editing your question – Amit Aug 04 '15 at 20:12
  • You can't do precisely that, since RunFunc has a "hidden" `this` parameter that C doesn't know anything about (which holds a pointer to your `jsm` object. You'll have to find some way that you can "stow" the address of `jsm` somewhere. – Mats Petersson Aug 04 '15 at 20:13
  • 1
    You can't create a function at runtime. – molbdnilo Aug 04 '15 at 20:14
  • Maybe duplicate: http://stackoverflow.com/q/1000663 – Kerrek SB Aug 04 '15 at 20:17
  • @KerrekSB It may be possible to do this with C++ 11. – Alyssa Haroldsen Aug 04 '15 at 20:20
  • try api_register_func(func_name, &jsm->runMacro); – edin-m Aug 04 '15 at 20:20
  • Hmm, it is new to me that you can create functions at run-time in C++. They seem to have made a great step forward ... – too honest for this site Aug 04 '15 at 20:22
  • Just because you're using C++ doesn't mean you can't create a function outside of a class you can use. – dbush Aug 04 '15 at 20:24
  • There is no runMacro member in your class, if you show how you declared it, I might help. – Cem Kalyoncu Aug 04 '15 at 20:25
  • Can you write a **complete** and **verifiable** example of what it is you want to do? You have inconsistent names throughout, it's unclear what you're trying to accomplish. – Barry Aug 04 '15 at 20:32
  • @Barry, right. Sorry for that. I've updated the Code. – Andreas Aug 04 '15 at 20:35
  • @Andreas Try again please. Your variable references `ctx` and `runFunc`, your class has `Parameter` `and `RunFunc`. And does the API really not allow you to provide some sort of `void* context`? – Barry Aug 04 '15 at 20:37
  • What do the parameters of the registered function actually represent? Are any of them user-defined values? Clearly this example does not allow passing a user-defined value to `api_register_func()` itself, but maybe there is a separate function for assigning context data? If so, you can pass your object pointer as the context data so your registered function can access the object. – Remy Lebeau Aug 04 '15 at 20:47
  • The Paramters come from a JS-Intepreter(duktape) that I have embedded to my dll. The intepreter reads a script that defines the functions i want to execute through the main Program/API. – Andreas Aug 04 '15 at 20:51

1 Answers1

1

Unfortunately, you don't have very many options here. The API:

DllImport void api_register_func(char *func_name, 
                                 void (*funcptr)(char *, char *));

only lets you provide a function pointer, without any context. So you can pass in a free function or a static member function, but you have no way of providing a member function. If you had a context:

DllImport void better_api_register_func(char *func_name, 
                                        void *context,
                                        void (*funcptr)(void *, char *, char *));

Then you could write a simple lambda:

api_register_func(external_parameter1,
                  jsm, //context
                  [](void *ctxt, char* arg1, char* arg2) {
                      static_cast<JsFunc*>(ctxt)->runFunc(arg1, arg2);
                  });

and you're effectively using a member function as the callback. However, this isn't an option. Instead, you're stuck with using a global variable:

std::unique_ptr<JsFunc> the_global_func;

Which you can allocate at runtime:

the_global_func.reset(new JsFunc);
// set stuff

And then use the global in a lambda. Since it's global, we don't need to (in fact, can't) capture it - which means our lambda can be convertible to a function pointer:

api_register_func(external_paramter1,
                  [](char* arg1, char* arg2) {
                      the_global_func->runFunc(arg1, arg2);
                  });
Barry
  • 247,587
  • 26
  • 487
  • 819
  • Thanks a lot! That should work for me. At the moment I can't try it with the API but it compiles :) – Andreas Aug 04 '15 at 21:28
  • Barry, your Code works for one "the_global_func". But I have not found a way to to this for more than one. Is there another possibility than define more than one "the_global_func" (the_global_func1,the_global_func1,...)? I tried to put the_global_func into a vector, but can't pass the index to api_register_func. When I change [](char... to [=](char... I can pass the index but then the lambda returns another type. Thanks again! – Andreas Aug 05 '15 at 09:34