Create function call dynamically in C++

∥☆過路亽.° 提交于 2019-12-04 07:14:27

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.

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

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.

The way to do this is to use pointers to functions:

void (*native)(int a, int b) ;

The problem you will face is finding the address of the function to store in the pointer is system dependent.

On Windoze, you will probably be loading a DLL, finding the address of the function by name within the DLL, then store that point in native to call the function.

In pure standard C++ (or C; see n1570 or n3337 or some newer standard specification, a document written in English), the set of functions is fixed -so cannot change-, and given by the union of all your translation units (and by those from the standard C or C++ library). And in pure standard C++ or C, a function pointer is allowed only to point to some pre-existing function (otherwise it is undefined behavior), when you use it for indirect calls. All functions are, in standard C++ (or C), known at "compile-time", and practically declared in some translation unit (and often implemented in another one, or in some external library).

BTW, when coding an interpreter (for some scripting language), you don't need to grow the set of your (C or C++) functions. You just need to have (generic) interpreting functions coded in C or C++ dealing with some representation of the interpreted scripting code (which, from the point of view of your C++ or C program, is some data), perhaps an AST or some bytecode. For example, a Unix shell, or a Lua or Guile interpreter, don't create C or C++ functions. You could embed Lua or Guile in your program.

However, you might be interested in generating or creating new (C or C++) functions at runtime, for example when compiling your scripting code into C (a common practice) or in machine code. This is not possible in pure standard C or C++, but is practically possible on many implementations, with help from the operating system (at least to grow or add code segments, i.e. new machine code, in your virtual address space).

(notice that any mechanism able to create functions at runtime is outside of the C or C++ standard, and would return function pointers to new machine code)

See also this answer (to a very related question, for C; but you could adapt it for C++), detailing how that is practically possible (notably on Linux).

BTW, libffi is by itself not a way of creating new (C, C++, or machine code) functions, but of calling existing functions of an arbitrary signature with arbitrary arguments.

This means, that I need a way, to generate argument lists of C functions at runtime.

The libffi is only doing that. It knows your ABI (and is partly coded in assembler).

Notice that if your set of functions is fixed (so finite), their signatures are also in a finite set, then you don't really need libffi (because you could special case all your signatures, so your signatures are not arbitrary), even if it could be convenient.

Once you are adding new functions at runtime of arbitrary signatures, libffi or an equivalent mechanism is absolutely needed (because even the set of called signatures could grow).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!