I'm not sure if this is what you're looking for, but anyway...
1. Creating a generic value holder
boost.any didn't make it into the standard, and, in case you don't know what it is, it allows you to store any C++ value in a single type (any) and get it back if you know the type. The following is a toy implementation of it:
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 contains two pointer to functions that know how to copy and how to destroy a specific C++ type. A Value can hold any type because it's composed of a void * and a pointer to a TypeHandler. When copying or destroying is required on the instance it asks to the specific type handler function...
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;
}
};
Note that Value is copyable and can be passed by value and can be returned by functions.
Any copyable object is implicitly convertible into a Value and I can also convert it back to the original type with to<T>().
2. Creating the name->function maps
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);
}
Here I've created explicit maps for 1 and 2 arguments. May be this can be done using C++11 variadic templates, I didn't try. In C++03 libraries it's common to see this kind of stuff copy-n-pasted up to say n=20 to cover reasonable cases.
3. Macrology
To simplify registration of functions I wrote two ugly macros. May be this can be done also using variadic macros or templates (I'm not so sure about it, especially the automatic registration of the wrapper in the map).
#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. Use
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.3f\n", call("square", z).to<double>());
printf("%0.3f\n", call("hyp2", z, z).to<double>());
printf("mylen(\"%s\") = %i\n",
y.to<std::string>().c_str(),
call("mylen", y).to<int>());
return 0;
}
double*or achar**in a string. Anyway, can you show how you are doing it with a fixed number of arguments, say 2?