2016-10-22 83 views
2

比方說,我有一個靜態方法,比較兩個對象的近距離匹配並返回一些置信度[0,1]。如何從一個方法返回可選的調試信息?

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

現在我需要返回一個包含具體取決於配置的設置比較細節的其他調試信息。 由於此調試信息不​​會在生產中使用,但僅用於測試/調試目的,我想知道實現它的適當方式是什麼。

我看到至少有三個選項:

1:創建一個附加的類CompareResult和存儲信心+可選信息存在。如果您不需要,請不要填充可選信息。

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

... 

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

它似乎是最乾淨的一個,但我不確定是否應該將返回結果與可選信息結合使用。

2:使用輸出變量(這樣,我們將不再需要創建一個額外的類,但我們的方法簽名將增長一點)

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世界中從方法返回可選信息(大多隻用於調試模式)的適當方式是什麼?

+0

您打算使用調試器還是僅在調試模式下打印附加信息?如果你想使用gdb並使用像'call Foo :: compare(foo1,foo2)'這樣的命令,你將不能夠詢問選項1的'compare_optional_info_'。 – Franck

+0

你是否打算在調試模式下將生產代碼構建爲測試,還是你在使用單獨的測試程序? – Peter

+0

這將是一個基於意見的問題(不是真正的主題)。我通常希望單元測試我的函數爲「黑盒子」,它們遵循規範,只關心內部發生的事情(從dbug的角度來看),如果它們失敗的話。如果你真的想要單元測試,那麼也許你可以把它放到它自己的函數中,這個函數可以用來測試(選項1或2),並單獨地測試這個函數,而不是通過測試細節來損害'API'。另一種可能性是*有條件地編譯*在函數中記錄信息。 – Galik

回答

1

選項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 ?  

好了,對於最後一個工作,你會還需要您的額外班級的比較運算符。但是,這是一行代碼更多(請參閱在線演示);-)

最後,它可能會證明您的「可選調試信息」可能證明比預期更有用,例如爲用戶提供額外的解釋根據要求。所有你需要做的事情就是刪除圍繞可選信息計算的條件#ifdef DEBUG

+1

線程本地化使線程安全。調試信息的緩衝區或類似的隊列意味着處理多個可接受的;在極限中,這成爲日誌記錄。 – Yakk

+0

@Yakk如果OP使用GCC> = 4.8或MSVC> = 2015,那麼您當然完全正確。隊列緩衝確實是可能的。但是在函數調用的例子中,在不同參數中進行幾次比較,您仍然不知道它們以什麼順序存儲在隊列中。因此,問題是:爲什麼在選項1已經是一個非常好的選擇時投資於使選項3可以接受? – Christophe

+1

謝謝您的詳細解釋!我喜歡根據要求將「可選調試信息」變成附加解釋的想法,我一定會按照您的建議。 –

0

我會使用第二個選項來與調試器兼容。

在調試模式下,您可以添加一個額外的靜態成員。你應該照顧不應該壓制它的鏈接器。

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_. ... 
+0

您錯過了我認爲的一些NDEBUG。 – Yakk

+0

如果我理解的很好,你的想法是在使用的代碼中只使用兩個參數(第三個默認爲null),並且只在調試器會話中使用第三個參數。這適用於特別分析。但是,如果在調試中你也想做一些日誌記錄,你需要創建'CompareOptionalInfo'變量並傳遞它們的地址。這些將保持在生產中,除非你添加了大量的條件編譯... – Christophe

+0

@Christophe所有取決於是否要在生產中使用日誌記錄。 'CompareOptionalInfo'也可以隱藏在你的日誌類中。我認爲你可以隱藏一些關鍵類/方法中的條件編譯。而且,在調試器中,您可以針對代碼中未比較的元素或過去/未來的元素比較元素。 – Franck