代码之家  ›  专栏  ›  技术社区  ›  Bill

如何在C++中使用动态方法

c++
  •  1
  • Bill  · 技术社区  · 15 年前

    我发现自己在C++中写了一些重复的代码。我正在使用一些自动生成的方法,这样如果我想处理foo、bar和baz,它们都有相当相似的方法。例如,抓到foo,抓到bar,抓到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次?

    4 回复  |  直到 15 年前
        1
  •  12
  •   Steve Jessop    15 年前

    当然(代码未经测试,我可能遗漏了一些关于成员指针和类型推断的问题,或者我可能只留下了bug):

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

    显然,您可以使用functor类型的模板参数,但这是最接近Perl的方法,而无需实际在对象中构建字典并滚动自己的调度。如果需要的话,您还可以将这些对docheck的调用进行宏ISE,并使用一些标记粘贴使其更短一些:

    #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预处理器库或混沌预处理器。

        2
  •  2
  •   Anthony Cramp    15 年前

    我认为您需要滚动自己的间接方向,可能需要使用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 ...
            }
        }
    }
    
        3
  •  0
  •   Loki Astari    15 年前

    C++不支持动态方法。
    所有方法都是在编译时严格定义的。

    但是30/40行代码可以通过函数调用完成:

     if (!l)
     {
         ERROR("Bar does not have a log");
         return;
     }    
     //... 30-40 more lines of stuff ...
     LogStuff(*l);
    

    显然,你使用C++的方式对你来说似乎很自然: 所以我有几个问题:

    • 你来自哪种语言?
    • 你想做什么?
        4
  •  0
  •   Satbir    15 年前

    请你多解释一下你的问题好吗 一次能把这三个都做出来吗? 也就是说,这三个条件都是真的 1)如果(obj->有foo) 2)如果(obj->有\u条) 3)如果(obj->has_baz)

    我建议您编写一个具有公共功能的基类。驱动这三个foo,bar和baz从那个基类,现在使用基类指针来执行调用。S 所以您不需要为这三个部分重复代码。