Hello people I hope you an help me out with this problem:
I am currently implementing an interpreter for a scripting language. The language needs a native call interface to C functions, like java has JNI. My problem is, that i want to call the original C functions without writing a wrapper function, which converts the call stack of my scripting language into the C call stack. This means, that I need a way, to generate argument lists of C functions at runtime. Example:
void a(int a, int b) {
printf("function a called %d", a + b);
}
void b(double a, int b, double c) {
printf("function b called %f", a * b + c);
}
interpreter.registerNativeFunction("a", a);
interpreter.registerNativeFunction("b", b);
The interpreter should be able to call the functions, with only knowing the function prototypes of my scripting language: native void a(int a, int b);
and native void b(double a, int b, double c);
Is there any way to generate a C function call stack in C++, or do I have to use assembler for this task. Assembler is a problem, because the interpreter should run on almost any platform.
Edit: The solution is to use libffi, a library, which handles the call stack creation for many different platforms and operating systems. libffi is also used by some prominent language implementations like cpython and openjdk.
Edit: @MatsPetersson Somewhere in my code I have a method like:
void CInterpreter::CallNativeFunction(string name, vector<IValue> arguments, IReturnReference ret) {
// Call here correct native C function.
// this.nativeFunctions is a map which contains the function pointers.
}
Edit: Thanks for all your help! I will stay with libffi, and test it on all required platforms.
Yes we can. No FFI library needed, no restriction to C calls, only pure C++11.
#include <iostream>
#include <list>
#include <iostream>
#include <boost/any.hpp>
template <typename T>
auto fetch_back(T& t) -> typename std::remove_reference<decltype(t.back())>::type
{
typename std::remove_reference<decltype(t.back())>::type ret = t.back();
t.pop_back();
return ret;
}
template <typename X>
struct any_ref_cast
{
X do_cast(boost::any y)
{
return boost::any_cast<X>(y);
}
};
template <typename X>
struct any_ref_cast<X&>
{
X& do_cast(boost::any y)
{
std::reference_wrapper<X> ref = boost::any_cast<std::reference_wrapper<X>>(y);
return ref.get();
}
};
template <typename X>
struct any_ref_cast<const X&>
{
const X& do_cast(boost::any y)
{
std::reference_wrapper<const X> ref = boost::any_cast<std::reference_wrapper<const X>>(y);
return ref.get();
}
};
template <typename Ret, typename...Arg>
Ret call (Ret (*func)(Arg...), std::list<boost::any> args)
{
if (sizeof...(Arg) != args.size())
throw "Argument number mismatch!";
return func(any_ref_cast<Arg>().do_cast(fetch_back(args))...);
}
int foo(int x, double y, const std::string& z, std::string& w)
{
std::cout << "foo called : " << x << " " << y << " " << z << " " << w << std::endl;
return 42;
}
Test drive:
int main ()
{
std::list<boost::any> args;
args.push_back(1);
args.push_back(4.56);
const std::string yyy("abc");
std::string zzz("123");
args.push_back(std::cref(yyy));
args.push_back(std::ref(zzz));
call(foo, args);
}
Exercise for the reader: implement registerNativeFunction
in three easy steps.
call
method that accepts a list of boost::any
, call it AbstractFunction
AbstractFunction
and adds a pointer to a concrete-type function (or std::function
). Implement call
in terms of that function.map<string, AbstractFunction*>
(use smart pointers actually).Drawback: totally cannot call variadic C-style functions (e.g. printf and friends) with this method. There is also no support for implicit argument conversions. If you pass an int
to a function that requires a double
, it will throw an exception (which is slightly better than a core dump you can get with a dynamic solution). It is possible to partially solve this for a finite fixed set of conversions by specializing any_ref_cast
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With