这是我的代码(简化现实生活中的问题):
class Foo {
public:
void f(const string& s) {
if (s == "lt") {
return lt();
} else if (s == "lte")
return lte();
} else if (s == "gt")
return gt();
} else if (s == "gte")
return gte();
}
}
void lt() { /* skipped */ }
void lte() { /* skipped */ }
void gt() { /* skipped */ }
void gte() { /* skipped */ }
};
这就是我用PHP / Python / JavaScript /许多其他语言(PHP中的例子)的方法:
class Foo {
function f($s) {
return $this->$s();
}
function lt() { /* skipped */ }
function lte() { /* skipped */ }
function gt() { /* skipped */ }
function gte() { /* skipped */ }
}
如何使我的C ++代码像这个PHP示例一样优雅?提前谢谢。
答案 0 :(得分:37)
C ++中没有反映。但是,像std::map<std::string, void (Foo::*)()>
这样的东西应该可以解决问题。
编辑:这是一些难以实现的代码。请注意以下事项:
#define BEGIN_TOKEN_MAP \
template <int n> \
struct add_to_ \
{ \
static void act() {} \
}; \
std::map<std::string, void (Foo::*)()> map_;
#define DECLARE_TOKEN(str, n) \
template <> struct add_to_<n> \
{ \
static void act() { map_[#str] = &Foo::##str; add_to<n+1>::act();} \
};\
void str()
#define END_TOKEN_MAP \
void init_map() { add_to_<0>::act(); } \
void process_token(std::string s) { (this->*map_[s])(); }
class Foo
{
BEGIN_TOKEN_MAP
DECLARE_TOKEN(lt, 0) { ... }
DECLARE_TOKEN(gt, 1) { ... }
...
END_TOKEN_MAP
Foo() { init_map(); }
void f(const std::string& s) { process_token(s); }
};
答案 1 :(得分:12)
您可以使用dispatch table
之类的:
typedef struct {
char *name;
void (*handler)();
} handler_t;
handler_t *handlers = {
{"lt", <},
{"lte", <e},
{"gt", >},
{"gte", >e},
(NULL, NULL}
};
void f(const string &s) {
for (int i=0; handlers[i].handler; ++i) {
if (0 == strcmp(s.c_str(), handlers[i].name)) {
handlers[i].handler();
return;
}
}
}
另见这个问题:How do you implement a dispatch table in your language of choice?
答案 2 :(得分:5)
C ++不是动态的,所以没有确切的等价物。更优雅的是使用地图和可能的功能对象。
答案 3 :(得分:4)
根据Alexandre C.的建议,您可以将std::map<...
方法与operator()
结合使用,以避免调用void Foo::f
。
例如:
class Foo {
private:
map<string,void (Foo::*)()> funs;
public:
// constructors etc.
void operator () (const string& s) {
if (funs.find (s) != funs.end ())
(this->*funs[s])();
}
// remainder
};
现在你可以使用类似于
的fooFoo f;
f("lt"); // calls Foo::lt ()
f("lte"); // calls Foo::lte ();
// etc...
答案 4 :(得分:3)
// Beware, brain-compiled code ahead!
namespace {
typedef std::map<std::string, void (Foo::*)()> operations_map_t;
typedef operations_map_t::value_type operations_entry_t;
const operations_entry_t* operations = { {"lt" , &Foo::lt }
, {"lte", &Foo::lte}
, {"gt" , &Foo::gt }
, {"gte", &Foo::gte} };
const operations_map_t operations_map( operations
, operations + sizeof(operations)
/ sizeof(operations[0]) );
}
void Foo::f(const string& s)
{
operations_map_t::const_iterator it = operations_map.find(s);
if(it == operations_map.end()) throw "Dooh!";
it->second();
}
答案 5 :(得分:2)
我赞成了Alexandre C,但我对在运行时构建数据结构(填充std :: map)有所保留,当数据在编译时都是已知的时候。
我赞成了the_void,但线性搜索仅适用于相对较小的数据集。
值得考虑的一个选项是脚本(用Python编写)来生成散列表或完美平衡的二叉树或构建时的任何内容。当然,如果您经常需要支持大型已知的编译时数据集,那么您将只会这样做。
在C ++中可能有模板技巧的方法 - 它们是Turing完成的,并且至少有一个编译时解析器状态模型生成器,它显然比散列表或二叉树更复杂。但就个人而言,我不会推荐它。代码生成脚本将更简单,更健壮。
我有一个用于生成三元树的脚本,但是(1)这里有点长,而且(2)它不是一个良好编码的光辉榜样。
答案 6 :(得分:1)
你有几种可能性。但我要说的第一件事是C ++是强类型的。因此,一方面处理Foo
的实例而另一方面处理Foo
的方法与处理Foo
和Bar
的方法的类型不同。< / p>
现在,我们假设你只想处理Foo
个对象。那么你有2个解决方案:
函数对象更通用,值得注意的是,它允许您在一个对象中指定多个参数组合。
class OperatorBase
{
public:
virtual ~OperatorBase() {}
bool operator()(Foo const& lhs, Foo const& rhs) const;
bool operator()(Foo const& lhs, Bar const& rhs) const;
bool operator()(Bar const& lhs, Foo const& rhs) const;
bool operator()(Bar const& lhs, Bar const& rhs) const;
private:
// virtual methods to actually implement this
};
struct LessThanOperator: OperatorBase
{
// impl
};
class OperatorFactory
{
public:
static OperatorBase& Get(std::string const& name);
template <class T>
static void Register(std::string const& name);
private:
typedef boost::ptr_map<std::string, OperatorBase> ops_t;
static ops_t& Get() { static ops_t O; return O; }
};
然后你可以继续:
// Choose the operator
OperatorBase& op = OperatorFactory::Get("lt");
Foo foo;
Bar bar;
bool const result = op(foo, bar);
虽然这是相当繁琐的工作。
答案 7 :(得分:0)
有很多方法可以在C ++中使用数组和动态调度来执行类似的操作。
你要做的是创建一个带有一些标准action()的抽象类,如下所示:
class abstract_handler {
public:
virtual void action () = 0;
}
然后使用action()
的不同实现创建子类。例如,对于您的“ffa”分支,您可以写:
class ffa_handler : public abstract_handler {
public:
virtual action() {
// Do your custom "ffa" stuff in here
}
// Add your custom "ffa" members for action() to work on here.
// ...and of course a constructor to initialize them.
}
然后,您创建一个指向每个类对象的指针的映射(在您的情况下,由std::string
索引)。在启动时,您使用正确的字符串索引上的适当对象填充它。然后在运行时,您所要做的就是:
handler_map[index_string].action();