2016-10-22 4 views
2

근접 일치를 위해 두 개체를 비교하고 일부 신뢰 수준 [0,1]을 반환하는 정적 메서드가 있다고 가정 해 보겠습니다.메소드에서 선택적 디버그 정보를 반환하는 방법은 무엇입니까?

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

이제 구성의 설정에 따라 비교 세부 정보를 포함하는 추가 디버그 정보를 반환해야합니다. 이 디버그 정보는 프로덕션 환경에서는 사용되지 않지만 테스트/디버깅 목적으로 만 사용되므로 적절한 구현 방법이 무엇인지 궁금합니다.

1 : 추가 클래스 CompareResult 및 저장의 신뢰도 +이 옵션 정보를 생성

나는 적어도 세 가지 옵션을 참조하십시오. 필요한 경우 옵션 정보를 채우지 마십시오.

가장 깨끗한 것으로 보이지만 반환 결과를 선택적 정보와 결합해야하는지 잘 모르겠습니다.

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

스레드 로컬은 스레드를 안전하게 만듭니다. 디버그 정보 버퍼의 큐 (queue)는 다수의 허용 가능한; 한계 안에, 이것은 로깅이된다. – Yakk

+0

OP가 GCC> = 4.8 또는 MSVC> = 2015를 사용하는 경우 @Yakk은 물론 완벽하게 맞습니다. 큐 버퍼가 실제로 가능합니다. 그러나 다른 매개 변수에서 여러 비교를 사용하여 함수를 호출하는 예에서는 여전히 대기열에 저장된 순서를 알 수 없습니다. 따라서 질문 : 옵션 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

관련 문제