使用变量参数映射函数并通过字符串 c++ 调用
Mapping functions with variable arguments and calling by string c++
我想有一些公平的想法,如何使用变量参数映射函数,返回int类型并通过字符串调用它。
仅举个例子...
int func1(int a, int b);
int func2(int a1, int b1 , int* c1);
int func3(char* dummy);
int func4(double x, long y, int z, char** table);
int func5(double d1, double b1);
int func6(int* p, double* q, int i);
我只需要一个叫做
int CallFunction("funcname", param1, param2, ...);
例如
CallFunction("func1", 10, 20); /* calling function func1 and return func1 result*/
我知道如何使用具有常量参数的函数指针映射函数,但变量参数似乎很复杂..任何人都可以知道如何做到这一点。
我什至探索了可变参数模板。但似乎使用字符串调用函数很复杂。
我遇到了完全相同的问题。
用这个解决方案解决了它:
#include <iostream>
#include <map>
#include <string>
int func0(int x)
{
std::cout << x << std::endl;
}
int func1(int x, int y)
{
std::cout << (x + y) << std::endl;
}
template <class... Args>
struct MapHolder{
static std::map<std::string, int (*)(Args...)> CallbackMap;
};
template <class... Args>
std::map<std::string, int (*)(Args...)> MapHolder<Args...>::CallbackMap;
class Callback {
public:
template <class ...Args>
void RegisterFunction(std::string name, int (*func)(Args...)) {
MapHolder<Args...>::CallbackMap[name] = func;
}
template <class ...Args>
int ExecuteFunction(std::string name, Args &&... args) {
return MapHolder<Args...>::CallbackMap[name](std::forward<Args>(args)...);
};
};
int main(int argc, char *argv[])
{
Callback cb;
cb.RegisterFunction("func0", &func0);
cb.RegisterFunction("func1", &func1);
cb.ExecuteFunction("func0", 42);
cb.ExecuteFunction("func1", 42, 42);
return 0;
}
此代码段基于此答案。我只使用其他类/函数名称。
我不确定这是否是您要找的,但无论如何...
1. 创建通用值持有者
boost.any没有进入标准,如果你不知道它是什么,它允许你将任何C++值存储在单个类型(any
)中,如果你知道该类型,就可以取回它。以下是它的玩具实现:
struct TypeHandler {
void* (*copyFrom)(void *src);
void (*destroy)(void *p);
};
template<typename T>
TypeHandler *thandler() {
struct THandler {
static void *copyFrom(void *p) { return new T(*(T *)p); }
static void destroy(void *p) { delete (T *)p; }
};
static TypeHandler th = { &THandler::copyFrom, &THandler::destroy };
return &th;
}
TypeHandler
包含两个指向知道如何复制以及如何销毁特定C++类型的函数的指针。Value
可以保存任何类型,因为它由void *
和指向TypeHandler
的指针组成。当实例上需要复制或销毁时,它会要求特定的类型处理程序函数...
struct Value {
TypeHandler *th;
void *p;
Value(const Value& other) : th(other.th), p(th->copyFrom(other.p)) { }
template<typename T> Value(const T& x) : th(thandler<T>()), p(new T(x)) { }
~Value() { th->destroy(p); }
Value& operator=(const Value& other) {
if (this != &other) {
th->destroy(p);
th = other.th;
p = th->copyFrom(other.p);
}
return *this;
}
template<typename T>
Value& operator=(const T& other) {
th->destroy(p);
th = thandler<T>();
p = new T(other);
return *this;
}
template<typename T>
T& to() const {
if (th != thandler<T>()) throw Error("type mismatch");
return *(T*)p;
}
};
请注意,Value
是可复制的,可以按值传递,也可以由函数返回。任何可复制的对象都可以隐式转换为Value
,我也可以使用 to<T>()
将其转换回原始类型。
2. 创建名称>函数映射
std::map<std::string, Value (*)(const Value&)> map1;
std::map<std::string, Value (*)(const Value&, const Value&)> map2;
Value call(const std::string& name, const Value& x1) {
return map1.at(name)(x1);
}
Value call(const std::string& name, const Value& x1, const Value& x2) {
return map2.at(name)(x1, x2);
}
在这里,我为 1 和 2 参数创建了显式映射。也许这可以使用 C++11 个可变参数模板来完成,我没有尝试过。在 C++03 库中,经常看到这种东西复制粘贴到说 n=20 以涵盖合理情况。
3. 宏观学
为了简化函数的注册,我写了两个丑陋的宏。可能这也可以使用可变参数宏或模板来完成(我不太确定,尤其是在地图中自动注册包装器)。
#define regfunc1(name, t1)
Value name(const Value& x1) {
return name(x1.to<t1>());
}
struct name##_ {
name##_() { map1[#name]=&name; }
} name##_instance
#define regfunc2(name, t1, t2)
Value name(const Value& x1, const Value& x2) {
return name(x1.to<t1>(), x2.to<t2>());
}
struct name##_ {
name##_() { map2[#name]=&name; }
} name##_instance
4. 使用
double square(double x) {
return x*x;
}
double hyp2(double x, double y) {
return x*x+y*y;
}
int mylen(const std::string& s) {
return s.size();
}
regfunc1(square, double);
regfunc2(hyp2, double, double);
regfunc1(mylen, std::string);
int main() {
Value x = 42;
Value y = std::string("This is a test");
Value z = 3.14;
printf("%0.3fn", call("square", z).to<double>());
printf("%0.3fn", call("hyp2", z, z).to<double>());
printf("mylen("%s") = %in",
y.to<std::string>().c_str(),
call("mylen", y).to<int>());
return 0;
}