我发现自己在C ++中编写了一些重复的代码。我正在使用一些自动生成的,如果我想处理Foo,Bar和& Baz他们都有相当类似的方法。例如,get_foo,get_bar,get_baz等
对于每一件“事物”,我或多或少都要做同样的事情。检查它是否存在,是否存在,获取日志,查找日志中的最新条目,检查条目以查找问题等。
这导致了相当多的重复代码,如下所示:
if (obj->has_foo) {
if(obj->get_foo().has_log()) {
Log *l = obj->get_foo().get_foo_log();
if (!l) {
ERROR("Foo does not have a log")
}
... 30-40 more lines of stuff ...
}
}
if (obj->has_bar) {
if(obj->get_bar().has_log()) {
Log *l = obj->get_bar().get_bar_log();
if (!l) {
ERROR("Bar does not have a log")
}
... 30-40 more lines of stuff ...
}
}
if (obj->has_baz) {
if(obj->get_baz().has_log()) {
Log *l = obj->get_baz().get_baz_log();
if (!l) {
ERROR("Baz does not have a log")
}
... 30-40 more lines of stuff ...
}
}
有没有办法构建一个集合,这样集合中的每个项目都有Foo,Bar,Baz的独特方面,我可以在一个代码块中使用它们。
原谅Perl-eese,但有点像:
foreach my $thingie ("foo", "bar", "baz") {
if (obj->has_$thingie) {
if(obj->get_$thingie().has_log()) {
Log *l = obj->get_$thingie().get_$thingie_log();
if (!l) {
ERROR(sprintf("%s does not have a log", $thingie))
}
... 30-40 more lines of stuff ...
}
}
}
或者,如果这不是正确的方向,我如何避免复制/粘贴/调整相同的基本块3次?
答案 0 :(得分:12)
当然(代码未经测试,我可能已经错过了成员指针和类型演绎的一些问题,或者我可能只是留下了错误):
template <typename T, typename M, typename F, typename G>
void doChecks(T *obj, M has_member, F get_fn, G getlog_fn) {
if (obj->*has_member) {
if (obj->*get_fn().has_log()) {
Log *l = obj->*get_fn().*getlog_fn();
if (!l) {
ERROR("%s does not have a log", typeid(T).name());
}
}
}
}
MyObj obj;
doChecks(obj, &MyObj::has_foo, &MyObj::get_foo, &Foo::get_foo_log);
doChecks(obj, &MyObj::has_bar, &MyObj::get_bar, &Bar::get_bar_log);
doChecks(obj, &MyObj::has_baz, &MyObj::get_baz, &Baz::get_baz_log);
显然你可以使用仿函数类型的模板参数,但这是最接近perl方法而不实际在对象中构建字典并滚动自己的调度。你可以根据需要宏观调用doChecks,并使用一些令牌粘贴来缩短它们:
#define DOCHECKS(obj, class, thing) doChecks(obj, &MyObj::has_##thing, &MyObj::get_##thing, & class :: get_##thing##log)
DOCHECKS(obj, Foo, foo);
DOCHECKS(obj, Bar, bar);
DOCHECKS(obj, Baz, baz);
使用额外的预处理器魔法,你可能会使它成为一个循环,不确定。查看Boost预处理器库或Chaos预处理器。
答案 1 :(得分:2)
我认为你需要滚动你自己的间接,可能使用std :: map来存储“foo”,“bar”和“baz”的键,关联的值是带有“get_log”方法的对象(假设你在get_foo()返回的对象上不需要get_foo_log()。在您的Perl-eese中,它可能会变为以下内容:
foreach my $thingie ("foo", "bar", "baz") {
if (obj->has($thingie)) {
if (obj->get($thingie).has_log()) {
Log *l = obj->get($thingie).get_log();
if (!l) {
ERROR(...)
}
... more lines ...
}
}
}
答案 2 :(得分:0)
C ++不支持动态方法 所有方法都在编译时严格定义。
但是30/40行代码可以通过函数调用来完成:
if (!l)
{
ERROR("Bar does not have a log");
return;
}
//... 30-40 more lines of stuff ...
LogStuff(*l);
你显然使用C ++的方式对你来说很自然: 所以我有几个问题:
答案 3 :(得分:0)
请你再解释一下你的问题了吗? obj可以同时包含三个:'' 即所有三个条件都可以 1)if(obj-&gt; has_foo) 2)if(obj-&gt; has_bar) 3)if(obj-&gt; has_baz)
我建议你编写一个具有通用功能的基类。从该基类驱动所有三个foo,bar adn baz,现在使用基类指针执行调用。小号 所以你不需要重复所有三个代码。