2013-09-07 3 views
3

라이브 로깅 프레임 워크 (Apache)가 코드를 단단히 결합하여 대용량 코드베이스에서 로깅 코드를 모듈화하려고하고 있으며 단위 테스트를 작성하기가 매우 어렵습니다. 가상 템플릿 기능을 사용할 수 없다는 사실에 갇혀 있습니다. 현재 접근 방식은 다음과 같이 요약 할 수 있습니다.가짜 가상 가변 템플릿 함수

// Context.h 
struct Logger 
{ 
    template <typename... Args> 
    void operator()(const char* aFormat, Args&&... aArgs) 
    { 
     // This function would ideally be virtual. 
     // Is there a funky way to get this function to call 
     // a derived class' implementation instead. 
     std::cerr << "I don't want to see this printed" << std::endl; 
    } 
}; 

class Context 
{ 
public: 
    Context(const Logger& aLogger) 
    : iLogger(aLogger) 
    { 
    } 

    template <typename... Args> 
    void DEBUG(const char* aFormat, Args&&... aArgs) 
    { 
     iLogger(aFormat, aArgs...); 
    } 

private: 
    const Logger& iLogger; 
}; 


// MyType.h 
#include "Context.h" 

class MyType 
{ 
public: 
    MyType(Context& aCtx) 
    : iCtx(aCtx) 
    { 
     DEBUG("> ctor"); 
     DEBUG("< ctor. this=%p", this); 
    } 

private: 
    template <typename... Args> 
    void DEBUG(const char* aFormat, Args&&... aArgs) 
    { 
     iCtx.DEBUG(aFormat, aArgs...); 
    } 

    Context& iCtx; 
}; 


// main.cpp 
#include "MyType.h" 

template <typename... Args> 
static void StdErrLog(const char* aFormat, Args&&... aArgs) 
{ 
    fprintf(stderr, aFormat, aArgs...); 
} 

struct StdErrLogger : public Logger 
{ 

    // This function never gets called because it's not virtual. 
    template <typename... Args> 
    void operator(const char* aFormat, Args&&... aArgs) 
    { 
     StdErrLog(aFormat, aArgs...); 
    } 
} 

int main(...) 
{ 
    StdErrLogger logger; // For unit tests this could be 'EmptyLogger' for example. 
    Context ctx(logger); 

    MyType t(ctx); 
} 

너무 가까이까지. Context 클래스를 템플릿으로 만들지 않고도이 작업을 수행 할 수 있습니까? 코드베이스는 전혀 템플릿 화되지 않았고 지루한 작업이 많아 질수록이 경로를 따라 가기를 꺼립니다.

템플릿을 기능 수준 범위로 유지함으로써 해결할 수 있다면 해결책을 찾게되어 매우 기쁩니다. 함수 포인터도 받아 들일 만하지만 가변성 템플릿 함수의 주소를 얻는 가능성에 대해서는 잘 모르겠습니다.

감사합니다.

+0

google에서 CRTP 확인 – cooky451

+2

형식 문자열 및 구현의 인수가 필요합니까, 아니면 형식화 결과가 필요합니까?형식 지정의 결과 이상을 필요로하는 경우 중간 양식이 있습니까? 로거 또는 포맷터의 구현 방식은 어떻게 다릅니 까? – Yakk

+0

문제는 템플릿을 컴파일러에서 인스턴스화해야한다는 것입니다. 즉, 컴파일러는 * 함수 템플릿 *에서 실제 함수 *를 만들어야합니다. 따라서 각 번역 단위에서 함수 템플릿을 구현해야합니다 (인스턴스화해야하는 경우). 함수 템플릿은 가상 클래스가 될 수 없습니다. 왜냐하면 기본 클래스의 함수를 사용하려면 (모든) 파생 클래스의 오버라이드를 인스턴스화해야하고 가상 함수로 구현할 수있는 별도의 컴파일로는 불가능합니다. – dyp

답변

0

나는 한 걸음 뒤로 물러나 몇 개의 템플릿을 떼어 냈습니다. 원시 함수 포인터는 내가 필요로하는 것을 주었다. nullptr을 함수로 전달하면 컴파일러가 아무런 코드도 생성하지 않을 정도로 영리 해지기를 바랍니다. 단점은 각 LogFunction이 va_list/start/end faff를 수행해야한다는 것입니다. 다른 사람이 이미 말했듯이

// Context.h 
typedef void (*LogFunction)(const char*, ...); 

class Context 
{ 
public: 
    Context(LogFunction aDebugFunc) 
    : iDebugFunc(aDebugFunc) 
    { 
    } 

    template <typename... Args> 
    void DEBUG(const char* aFormat, Args&&... aArgs) 
    { 
     if (iDebugFunc) 
     { 
      iDebugFunc(aFormat, aArgs...); 
     } 
    } 

private: 
    LogFunction iDebugFunc; 
}; 


// main.cpp 
#include <cstdarg> 

void StdErrLogger(const char* aFormat, ...) 
{ 
    va_list args; 
    va_start(args, aFormat); 

    fprintf(stderr, aFormat, args); 

    va_end(args); 
} 

int main(...) 
{ 
    Context ctx(StdErrLogger); 
    MyType t(ctx); 
} 
0

는, 템플릿은 컴파일러가 템플릿 함수가 인수 집합 사용 등 함수의 해당 인스턴스를 생성 할 수 있음을 알 수있다 즉, 인스턴스화 될 필요가있다.

당신이 원하는 경우에 "가짜"B1<T> 중 하나 B2<T>, B3<T>에 기능 A<T> despatching와 템플릿 함수에 대한 가상 함수의 동작 ... 동적으로, 당신은 B 모든 기능이 인스턴스화되어 있는지 확인해야합니다. 이렇게하려면 A의 구현은 가능한 모든 다른 B을 "알아야"합니다. 그런 다음 Visitor Pattern과 같은 것을 적용 할 수 있습니다.

두 번째 옵션은 주어진 인스턴스 A<T>에 대해 B이 사용될 것임을 컴파일러에 알리는 것입니다. 다른 fords에서는 Context 템플릿을 ConcreteLoggerType으로 지정합니다.

세 번째 옵션은 B을 템플릿으로 지정하지 않고 동적으로 어떻게 든 다른 T을 처리하는 것입니다. James는 가변 인수 목록을 사용하여이를 수행하는 방법을 설명합니다.

은 요약 옵션은 다음과 같습니다

  1. 이 로거의 고정 세트를 사용합니다.
  2. 템플리트 Logger
  3. 사용과 Context 내가 두 번째 솔루션 Policy-Based Design 향하다 것 C++ 템플릿 프로그래밍의 전력의 큰 팬이되는 템플릿이 아닌 Logger

.

관련 문제