如何 return 来自方法的可选调试信息?

How to return optional debug information from a method?

假设我有一个静态方法,它比较两个对象的接近匹配和 return 一些置信度 [0,1]。

class Foo
{
  ...
  static double Compare(const Foo& foo1, const Foo& foo2);
  ...
};

现在我需要 return 包含比较详细信息的附加调试信息,具体取决于配置中的设置。 由于此调试信息不​​会在生产中使用,而仅用于 testing/debugging 目的,我想知道实现它的适当方法是什么。

我看到至少三个选项:

1:创建一个额外的 class CompareResult 并在其中存储置信度 + 可选信息。如果不需要,请不要填充可选信息。

class CompareResult
{
  ...
private:
  double confidence_;
  CompareOptionalInfo compare_optional_info_;
  ...
};

...

static CompareResult Compare(const Foo& foo1, const Foo& foo2);

这似乎是最干净的一个,但我不确定是否应该将 return 结果与可选信息结合起来。

2:使用输出变量(这样我们就不需要创建额外的class,但是我们的方法签名会增长一点)

static double Compare(const Foo& foo1, const Foo& foo2, CompareOptionalInfo* out_compare_info = nullptr);

3: 将比较方法与可选信息检索方法分开。

static double Compare(const Foo& foo1, const Foo& foo2);
static CompareOptionalInfo GetCompareOptionalInfo();

此选项可能需要在方法调用之间存储此可选信息,并从静态比较方法转移到实例比较方法。 但是还是那句话,不知道合适不合适。

根据您的经验,在 OOP 世界中从方法中 return 可选信息(主要仅在调试模式下使用)的适当方式是什么?

我会使用第二个选项,因为它与调试器兼容。

在调试模式下,您可以添加一个额外的静态成员。您应该注意不应抑制它的链接器。

class Foo
{
  private:
#ifndef NDEBUG
  CompareOptionalInfo debug_out_compare_info_;
#endif

  ...
  static double Compare(const Foo& foo1, const Foo& foo2,
         CompareOptionalInfo* out_compare_info = nullptr);
  ...
};

#ifndef NDEBUG
CompareOptionalInfo Foo::debug_out_compare_info_;
#endif

在gdb中,在任何断点处,您可以使用:

call Foo::Compare(foo1, foo2, &Foo::debug_out_compare_info_);
print Foo::debug_out_compare_info_. ...

选项 3 根本不是一个好主意:具有依赖于静态数据的函数是不切实际的,甚至可能成为调试中错误的来源。这样的设计也不是线程安全的;多么遗憾,创建这样的限制只是为了调试目的!

问题示例:

double closest = std::max (Foo::compare (x, y), Foo::compare (y,z));   
clog << Foo::GetCompareOptionalInfo();  // undefined which info since order of eval
                                        // of function parameter is not guaranteed

double d = Foo::compare (x, y); 
DoSomething();                    // what if this one does an unexpected  compare ? 
clog << Foo::GetCompareOptionalInfo();  

选项 2 是一个可行的解决方案,但它不是很方便:它迫使您创建信息对象,通过地址等传递它们:

Foo::OptionalCompareInfo o1,o2;    // cumbersome 
double closest = std::max (Foo::compare (x, y, &o1), Foo::compare (y,z, &o2));   

此外,您将创建这些可选信息并在生产中传递额外的参数,即使您不再在那里更新信息(除非您放置大量额外的条件编译)!


选项 1 非常好!去吧 !它确实利用了 OOP 范例并使用了简洁的设计。使用起来很实用,不要对您的代码施加限制以使用它。

您所需要的只是提供一些(隐含的)转换函数来使用您的 CompareResult,就好像它是一个 double:

class CompareResult
{
public:
  CompareResult(double d=0.0) : confidence_(d) {}; 
  operator double() { return confidence_; }
  operator bool() { return confidence_>0.5; }   
private:
  double confidence_;
  CompareOptionalInfo compare_optional_info_;
};

online demo

您的生产代码将不受调试信息的影响。你可以在调试中始终追溯任何给定比较结果的解释,至少如果你存储它:

示例:

auto result = Foo::compare (x, y)
if (result) ... // uses automatically the conversion

auto closest = std::max (Foo::compare (x, y), Foo::compare (y,z));
// here you not only know the closest match but could explain it ! 

vector<CompareResult> v;  
... // populate the vector with 10 000 comparison results
auto r = std::max_element(v.begin(), v.end());  
    // you could still explain the compare info for the largest value 
    // if you're interested in the details of this single value
    // how would you do this with option 3 or option 2 ?     

好的,要使最后一个工作正常,您还需要一个用于附加 class 的比较运算符。但这还多了一行代码(参见在线演示);-)

最后,事实证明您的 "optional debug info" 可能比预期的更有用,例如根据要求向用户提供额外的解释。然后,您需要做的就是删除围绕可选信息计算的条件 #ifdef DEBUG