在C ++中动态创建函数调用 [英] Create function call dynamically in C++

查看:63
本文介绍了在C ++中动态创建函数调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

你好,我希望你能帮助我解决这个问题:

Hello people I hope you an help me out with this problem:

我目前正在为脚本语言实现解释器.该语言需要C函数的本机调用接口,例如java具有JNI.我的问题是,我想在不编写包装函数的情况下调用原始C函数,该包装函数会将我的脚本语言的调用堆栈转换为C调用堆栈.这意味着,我需要一种在运行时生成C函数的参数列表的方法.示例:

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);

只有知道我的脚本语言的函数原型,解释器才能调用函数: native void a(int a,int b); native void b(double a,int b,double c);

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);

有什么方法可以在C ++中生成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.

解决方案是使用库libffi,该库可处理许多不同平台和操作系统的调用堆栈创建.一些知名的语言实现(例如cpython和openjdk)也使用libffi.

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.

@MatsPetersson在我的代码中的某个地方,我有一个类似的方法:

@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.
}

感谢你的帮助!我将继续使用libffi,并在所有必需的平台上对其进行测试.

推荐答案

是的.不需要FFI库,对C调用没有限制,只需要纯C ++ 11.

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;
}

试驾:

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);
}

针对读者的锻炼:只需三个简单的步骤即可实现 registerNativeFunction .

Exercise for the reader: implement registerNativeFunction in three easy steps.

  1. 使用纯 call 方法创建一个抽象基类,该方法接受 boost :: any 的列表,并将其命名为 AbstractFunction
  2. 创建一个可变参数的类模板,该模板继承 AbstractFunction 并向具体类型的函数(或 std :: function )添加一个指针.根据该功能实现 call .
  3. 创建一个 map< string,AbstractFunction *> (实际上使用智能指针).
  1. Create an abstract base class with a pure call method that accepts a list of boost::any, call it AbstractFunction
  2. Create a variadic class template that inherits AbstractFunction and adds a pointer to a concrete-type function (or std::function). Implement call in terms of that function.
  3. Create an map<string, AbstractFunction*> (use smart pointers actually).

缺点:完全无法使用此方法调用可变的C样式函数(例如printf和friends).还不支持隐式参数转换.如果将 int 传递给需要 double 的函数,它将引发异常(这比动态解决方案可以获得的核心转储要好一些).通过专门化 any_ref_cast ,可以为固定的一组固定转换部分解决此问题.

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.

这篇关于在C ++中动态创建函数调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆