In two lines: create an interface function that takes a command as a string and sets up a functor that is then assigned to the std::function
. The operator()
takes care of evaluating the string in the target language.
Let's assume that in the header file mymodule.hh
you have a class MyModule
with a method void MyModule::Func(const std::function<double(Bunch&)> & func)
where Bunch
is another object.
This can be solved defining an interface function that takes a const char *
representing an expression to eval and wraps it into a functor that will be then assigned to the std::function
. The good news is that this can entirely done in the swig interface file, without touching the C++ code, here is how (I used tcl, you just have to adapt the operator()
):
%module mymodule
%{
#include "bunch.hh"
#include "mymodule.hh"
extern Tcl_Interp* tcl_interp;
struct Tcl_bunch_callback {
std::string callback;
double operator()(Bunch & bunch)
{
Tcl_Obj * bunch_obj = SWIG_Tcl_NewInstanceObj(tcl_interp, &bunch, SWIGTYPE_p_Bunch, /*own pointer?*/0);
Tcl_SetVar2Ex(tcl_interp, "bunch", (const char*)nullptr, bunch_obj, 0);
double resultValue;
const int resultCode = Tcl_ExprDouble(tcl_interp, callback.c_str(), &resultValue);
if (resultCode != TCL_OK) {
std::cerr << "WARNING evaluation of tcl expression failed: "
<< Tcl_GetStringResult(tcl_interp) << std::endl;
resultValue = max_double;
}
Tcl_DeleteCommand(tcl_interp, Tcl_GetString(bunch_obj)); //remove the used command to avoid leaks
return resultValue;
}
};
%}
%include "bunch.hh"
%include "mymodule.hh"
%extend MyModule
{
void Func(const char * cmd) {
$self->Func(std::function<double(Bunch&)>(Tcl_bunch_callback(cmd)));
}
}
In my case the operator()
is pretty bounded to Tcl, but I'm sure that similar procedures can be written also for the others target languages. Follows some details of it.
I give the user the capability to access the methods of the current Bunch
being processed in C++ directly from Tcl. The function: SWIG_NewInstanceObj
allows to convert the Bunch
pointer in its representation in the target language and creates an instance of it in the interpreter (this function is not documented, but digging a bit in whatever swig-generated wrap file it is not too hard to understand its mechanism). With the following command I set that object to a variable named bunch
so that it becomes available to the user simply with $bunch
and all the methods exported with swig are then accessible.
I think it's amazing how such powerful things are made available with such few lines of code thanks to swig!