2016-10-22 3 views
2

Sagen wir, ich habe eine statische Methode, die zwei Objekte für eine enge Übereinstimmung vergleicht und ein gewisses Konfidenzniveau [0,1] zurückgibt.Wie gibt man optionale Debug-Informationen von einer Methode zurück?

Jetzt muss ich zusätzliche Debug-Informationen zurückgeben, die Vergleichsdetails enthalten, abhängig von einer Einstellung in der Konfiguration. Da diese Debug-Informationen nicht in der Produktion verwendet werden, sondern nur zu Testzwecken/Debugging-Zwecken, frage ich mich, was der geeignete Weg ist, um es zu implementieren.

Ich sehe mindestens drei Möglichkeiten:

1: Erstellen Sie eine zusätzliche Klasse CompareResult und speichern Vertrauen + optional gibt es Informationen. Füllen Sie die optionalen Informationen nicht aus, wenn Sie dies nicht benötigen.

Es scheint der sauberste zu sein, aber ich bin mir nicht sicher, ob ich das Ergebnis mit einer optionalen Option kombinieren sollte.

2: Verwenden Sie Ausgangsgröße (so können wir keine zusätzliche Klasse erstellen müssen, aber unsere Methodensignatur ein bisschen wachsen)

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

3: Getrennte vergleichen Methode mit optional-info Abrufverfahren .

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

Diese Option wahrscheinlich diese optionale-info zwischen den Methodenaufrufen speichern würde erfordern, und verschieben von statischen vergleichen Verfahren zum Beispiel Verfahren vergleichen. Aber ich bin mir nicht sicher, ob es angemessen ist oder nicht.

Aus Ihrer Erfahrung, was ist der geeignete Weg in der OOP-Welt, optionale Informationen (die meist nur im Debug-Modus verwendet werden) von einer Methode zurückzugeben?

+0

Möchten Sie einen Debugger verwenden oder nur die zusätzlichen Informationen im Debug-Modus ausgeben? Wenn Sie gdb verwenden und Befehle wie 'call Foo :: compare (foo1, foo2)' verwenden wollen, können Sie 'compare_optional_info_' von Option 1 nicht anfordern. – Franck

+0

Möchten Sie Ihren Produktionscode im Debug-Modus erstellen? testen, oder verwenden Sie ein separates Testprogramm? – Peter

+0

Dies wird eine Meinungen basierte Frage sein (nicht wirklich zum Thema). Ich würde im Allgemeinen Unit-Test meine Funktionen als "Black Boxes", dass sie eine Spezifikation folgen und nur kümmern, was passiert, innerhalb (aus dubug Perspektive), wenn sie scheitern. Wenn es etwas gibt, das du wirklich testen willst, dann könntest du das vielleicht in eine eigene Funktion setzen, die testbar ist (Option 1 oder 2) und diese Funktion separat testen, anstatt die "API" mit Testdetails zu kompromittieren. Eine andere Möglichkeit ist * bedingte Kompilierung * Protokollierung von Informationen in der Funktion. – Galik

Antwort

1

Option 3 ist keine gute Idee überhaupt: mit Funktionen auf statische Daten abhängig ist nicht praktikabel und könnte sogar werden eine Quelle von Fehlern im Debugging sein. Eine solche Konstruktion ist zudem nicht threadsicher; wie schade wäre es, eine solche Beschränkung nur für Debugging Zwecke zu erstellen!

Beispiele für Probleme:

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

Option 2 ist eine praktikable Lösung, aber es ist nicht sehr praktisch: es Sie Info-Objekte zu erstellen zwingt, geben sie nach Adressen etc:

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

Zusätzlich würden Sie diese optionalen Informationen erstellen und das zusätzliche Argument in der Produktion übergeben, auch wenn Sie die Informationen dort nicht mehr aktualisieren (es sei denn du hast eine Menge extra bedingte Kompilationen gemacht)!


Option 1 ist ausgezeichnet! Tue es ! Es nutzt wirklich das OOP-Paradigma und verwendet ein sauberes Design. Es ist praktisch, den Code für die Verwendung zu verwenden und keine Einschränkungen aufzuerlegen.

Alles, was Sie brauchen, ist eine (implizite) Konvertierungsfunktion, um Ihre CompareResult fast zu verwenden, als ob es sich um eine double waren:

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

Ihre Produktion Code würde durch die Debug-Informationen nicht betroffen . Und man konnte immer in Ihrem Debug-Trace die Erklärung eines gegebenen Vergleichsergebnis zurück, zumindest, wenn Sie es speichern:

Beispiel:

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 ?  

Ok, für die allerletzte Sie arbeiten würde auch brauche einen Vergleichsoperator für deine zusätzliche Klasse. Aber das ist eine Codezeile mehr (siehe Online-Demo) ;-)

Schließlich könnte es sich herausstellen, dass sich Ihre "optionalen Debug-Informationen" als nützlicher erweisen als erwartet, zB um dem Benutzer zusätzliche Erklärungen zu geben auf Anfrage. Alles, was Sie dann tun müssen, wäre, die bedingte #ifdef DEBUG zu entfernen, die die Berechnung der optionalen Information umgibt.

+1

Thread Einheimische macht es Thread sicher. Eine Warteschlange von Puffern oder dergleichen von Debug-Informationen bedeutet, dass mehrere akzeptable behandelt werden; Im Extremfall wird dies zur Protokollierung. – Yakk

+0

@Yakk Sie sind natürlich völlig richtig, wenn OP GCC> = 4.8 oder MSVC> = 2015 verwendet. Warteschlangenpuffer ist in der Tat möglich. Aber im Beispiel des Funktionsaufrufs mit mehreren Vergleichen in verschiedenen Parametern wissen Sie immer noch nicht, in welcher Reihenfolge sie in der Warteschlange gespeichert sind. Daher die Frage: Warum investieren, um Option 3 akzeptabel zu machen, wenn Option 1 bereits eine sehr gute Alternative ist? – Christophe

+1

Vielen Dank für die ausführliche Erklärung! Ich mag die Idee, "optionale Debug-Informationen" auf Anfrage in zusätzliche Erklärungen umzuwandeln, und ich werde auf jeden Fall Ihrem Rat folgen. –

0

Ich würde die zweite Option für ihre Kompatibilität mit Debuggern verwenden.

Im Debug-Modus können Sie ein zusätzliches statisches Element hinzufügen. Sie sollten auf den Linker achten, der ihn nicht unterdrücken sollte.

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 

In gdb, jederBreakPoint, können Sie verwenden:

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

Sie vermissen einige NDEBUGs, denke ich. – Yakk

+0

Wenn ich gut verstehe, besteht Ihre Idee darin, im using-Code nur zwei Argumente zu verwenden (das dritte wird standardmäßig auf null gesetzt) ​​und das dritte Argument nur in der Debugger-Sitzung. Das ist für Ad-hoc-Analyse in Ordnung. Wenn Sie jedoch im Debug-Modus auch eine Protokollierung durchführen möchten, müssen Sie die 'CompareOptionalInfo'-Variablen erstellen und ihre Adresse übergeben. Diese würden dann in Produktion bleiben, wenn Sie nicht eine Menge bedingter Kompilierung hinzufügen würden ... – Christophe

+0

@Christophe Alles hängt davon ab, ob Sie die Produktion anmelden möchten. Die 'CompareOptionalInfo' könnte auch in Ihrer Protokollklasse versteckt sein. Ich denke, Sie können die bedingten Kompilationen in einigen Schlüsselklassen/Methoden verbergen. Darüber hinaus können Sie im Debugger nach 'Compare' für Elemente fragen, die im Code nicht verglichen oder in der Vergangenheit/Zukunft verglichen wurden. – Franck

Verwandte Themen