【问题标题】:Executing certain code for every method call in C++为 C++ 中的每个方法调用执行特定代码
【发布时间】:2011-02-02 22:06:42
【问题描述】:

我有一个要检查的 C++ 类。所以,我希望所有方法在退出之前打印它们的参数和返回值。

后者看起来有些简单。如果我对所有内容都执行 return(),那么宏

#define return(a) cout << (a) << endl; return (a)

如果我将所有返回值加到括号中(或任何可能被称为的),我会这样做(可能是错误的)。如果我想删除它,只需注释掉定义。

但是,打印输入似乎更加困难。有没有办法可以做到这一点,使用 C++ 结构或使用 workaroud hack?

【问题讨论】:

  • 有什么原因你不能只使用调试器吗?
  • 是的,我想将输入和输出发送到文件以便处理数据。我不想调试该方法,它显然是正确且经过良好测试的。

标签: c++ methods


【解决方案1】:

想到几个选项:

  • 使用调试器。
  • 按照 Space_C0wb0y 的建议使用 decorator pattern。但是,这可能需要大量手动输入,因为您必须复制装饰类中的所有方法并自己添加日志记录。也许您可以通过在您的类上运行 doxygen 然后解析其输出来自动创建装饰器对象...
  • 使用aspect-oriented programming。 (记录,这是您想要做的,是 AOP 的常见应用程序。)Wikipedia lists 一些 C++ 的 AOP 实现:AspectC++XWeaverFeatureC++

【讨论】:

  • AOP 提示 +1!但是,请阅读我对建议我使用调试器和 Space_C0wb0y 的人所做的担忧
  • 装饰或子类化会让我更改调用者代码。就算能做到,我也不愿意。最好使用定义(#ifdef STATS ... #endif)就地进行更改,而不是为创建的此类的每个对象进行装饰调用。
【解决方案2】:

但是,打印输入似乎更多 难的。有没有办法我可以做到 使用 C++ 结构或使用 工作中的黑客?

没有。


更新:我将在我的回答中失去一些简洁性,建议您可以通过应用 Design by Contract 来实现您所需要的。

【讨论】:

    【解决方案3】:

    听起来你想使用调试工具给我。这将允许您查看所需的所有参数。

    【讨论】:

    • 我不想看到参数。我想输出所有这些以做一些不同的事情,比如统计研究等,这不是实用程序的一部分。
    【解决方案4】:

    如果您不介意手动插入一些代码,您可以创建一个类:

    1. 在构造函数中记录方法的条目
    2. 提供转储任意参数的方法
    3. 提供一种记录状态的方法
    4. 日志退出并在析构函数中记录状态

    用法如下:

    unsigned long long
    factorial(unsigned long long n) {
        Inspector inspect("factorial", __FILE__, __LINE__);
        inspect.parameter("n", n);
        if (n < 2) {
            return inspect.result(1);
        }
        return inspect.result(n * fact(n-1));
    }
    

    当然,您可以编写宏来声明检查器并检查参数。如果您正在使用支持可变参数列表宏的编译器,那么您可以得到如下所示的结果:

    unsigned long long
    factorial(unsigned long long n) {
        INJECT_INSPECTOR(n);
        if (n < 2) {
            return INSPECT_RETURN(1);
        }
        return INSPECT_RETURN(n * fact(n-1));
    }
    

    我不确定您是否可以在不使用 AOP 环境或某些自定义代码生成工具的情况下获得更简洁的解决方案。

    【讨论】:

    • 很好,虽然可变参数可能会有问题。 C++ 不能很好地处理 varargs 函数,它最终将需要使用。它们受支持,但实际上仅适用于 C 值。
    • 我在考虑INJECT_INSPECTOR 的变量参数列表macros,尽管它们还没有正式融入C++。 C++ 中的 Varargs 函数绝对不能以任何实际方式使用。我会为每个参数使用单独的parameter(name,value) 调用,将调用设为模板,并利用 iostreams 进行格式化。我今天在实践中使用了类似的东西;)
    【解决方案5】:

    如果你的方法都是virtual,你可以使用decorator-pattern以一种非常优雅的方式实现。

    编辑:从您上面的评论(您想要统计输出)我得出结论,您绝对应该使用装饰器模式。它是为这类东西设计的。

    【讨论】:

    • 子类化在这种情况下也能达到同样的效果,不是吗?然而,这不是我要找的!装饰和子类化都需要编写与肮脏技巧(到处打印)相同数量的代码。但是,我还必须更改调用者代码。
    • 嗯,它在你的个人资料中说你喜欢好的设计......遗憾的是,在 C++ 中实现设计模式总是带有很多样板,但它仍然具有高度灵活性的好处。
    • 是的,我真的很喜欢设计模式,GoF 是唯一一本每次都坐在我桌边的书。但我认为这种情况下是矫枉过正的。我想要一个用于开发人员目的的临时“开启”/“关闭”功能。
    • 抱歉,我有点急躁。您当然是对的,必须为每个问题选择合适的解决方案。
    【解决方案6】:

    我只会使用一个日志库(或一些宏)并插入手动日志调用。除非你的类有过多的方法,否则它可能比开发和调试更复杂的解决方案更快。

    【讨论】:

    • Logging 可以很好地满足他的要求,它会比打印每一行稍微干净一些,但是你会有一个额外的库(除非他已经有一个)。
    猜你喜欢
    • 2011-07-27
    • 1970-01-01
    • 2017-11-10
    • 2014-07-30
    • 1970-01-01
    • 2012-04-21
    • 2022-01-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多