2008-11-14 9 views
5

나는 목적이 할 수있을 것입니다 로깅 수준을 writting하고연산자 << 그 취하거나 반환하지 않습니다 ostream에 과부하하는 방법

원래 질문 : 현재

// thread one 
Logger() << "Some string" << std::ios::hex << 45; 
// thread two 
Logger() << L"Some wide string" << std::endl; 

:이 클래스에 대한

#pragma once; 
#include <ostream>  
class Logger 
{ 
public: 
    Logger(); 
    ~Logger(); 

    std::ostream* out_stream; 
}; 

template <typename T> 
Logger& operator<< (Logger& logger, T thing) { 
    *logger.out_stream << thing; 
    return logger; 
} 

일부 노트 : 내 로거 헤더는 다음과 같이 보입니다

  1. 플랫폼 간 호환성은 문제되지 않습니다.
  2. Logger.cpp 내부에는 "실제"ostream 생성을 담당하는 싱글 톤 클래스가 있습니다.
  3. 로거 생성자 및 deconstructor가 필요한 싱글 톤 잠금을 수행합니다.

    • 가 어떻게 조작 < < 기능 그래서 비공개로 out_stream 설정할 수 있습니다 친구 또는 멤버를 만들 수 있죠 :

내가 세 가지 문제가?

  • 조작자를 만들려면 어떻게해야합니까? < < 조작원 용 기능 작동?
  • 어떻게 T를 WCHAR * 또는 std :: wstring으로 변환하여 out * stream으로 전달하기 전에 char * 또는 std :: string으로 변환 할 수 있도록 특수화를 추가 할 수 있습니까? (. 내 경우에는 문제가되지 않습니다 높은 유니 코드 문자를 잃고 변환 할 수 있습니다.) 답변에서 배운 것들을
  • 요약 : 후 친구 대신 전

    • 넣어 템플릿을.
    • std :: ios :: hex는 조작자가 아닙니다. std :: hex는 조작자입니다.

    최종 결과 (... 세 개의 점으로) 그에게 printf와 방법을하고 다중 매개 변수 방법을 사용하지 왜

    #pragma once 
    #include <ostream> 
    #include <string> 
    
    std::string ConvertWstringToString(std::wstring wstr); 
    
    class Logger 
    { 
    public: 
        Logger(); 
        ~Logger(); 
    
        template <typename T> 
        Logger& operator<< (T data) { 
         *out << data; 
         return *this; 
        } 
        Logger& operator<< (std::wstring data) { 
         return *this << ConvertWstringToString(data); 
        } 
        Logger& operator<< (const wchar_t* data) { 
         std::wstring str(data); 
         return *this << str; 
        } 
    
    private: 
        std::ostream* out; 
    }; 
    
    +0

    최종 결과가 잘못되었습니다. 클래스 범위의 특수화는 허용되지 않습니다. 대신에 오버로드됩니다 (템플릿 <> 부분 생략). 그렇지 않으면 (다음 일반 연산자 기능) 친구가 아니기 때문에 Adam의 대답 (네임 스페이스 범위에서 특수화)에서 필요합니다. –

    +0

    흥미롭게도, 그들은 거기에서 그들과 함께 일했습니다. 하지만, 정확하기 위해서, 어쨌든 그들을 제거했습니다. 감사! –

    답변

    7

    친구를 작동하는 경우 클래스의 주변 네임 스페이스에 연산자를 정의하고 연산자 오버로딩 확인에서만 볼 수 있도록합니다 (:: operator < < ... 구문을 사용하여 수동으로 호출 할 수 없음).

    ,210

    대안은, 그대로 코드를 유지하고 단지 운영자에게 < < 템플릿 친구를 만들기 위해, 당신은 당신의 클래스 정의에이 줄을 추가 :

    조작하는 문제에 대한
    template <typename T> 
    friend Logger& operator<< (Logger& logger, T thing); 
    

    , 난 그냥 줄 것이다 당신은 내 코드 나는 몇 시간 전에 쓰기 :

    #include <iostream> 
    #include <cstdlib> 
    using namespace std; 
    
    template<typename Char, typename Traits = char_traits<Char> > 
    struct logger{ 
        typedef std::basic_ostream<Char, Traits> ostream_type; 
        typedef ostream_type& (*manip_type)(ostream_type&); 
        logger(ostream_type& os):os(os){} 
        logger &operator<<(manip_type pfn) { 
         if(pfn == static_cast<manip_type>(std::endl)) { 
          time_t t = time(0); 
          os << " --- " << ctime(&t) << pfn; 
         } else 
          os << pfn; 
         return *this; 
        } 
        template<typename T> 
        logger &operator<<(T const& t) { 
         os << t; 
         return *this; 
        } 
    private:   
        ostream_type & os; 
    }; 
    
    namespace { logger<char> clogged(cout); } 
    int main() { clogged << "something with log functionality" << std::endl; } 
    

    };

    std :: hex이지만 std :: ios :: hex는 아니라는 것을 유의하십시오.. 후자는 스트림의 setf 기능에 대한 조작자 플래그로 사용됩니다. 예를 들어, 조작자에 대한 특별한 대우가 필요하지 않습니다. std :: endl의 위의 특별한 대우는 std :: endl이 사용될 때뿐만 아니라 시간을 스트림하기 때문에 필요합니다. 템플릿을 사용하여

    +0

    아! 내가 그랬을 때 로그에서 204845를 얻는 것이 놀랄 일이 아닙니다. 조작자가 그대로 작동하는 것처럼 보입니다. 감사! –

    0

    . 이것은 여전히 ​​당신에게 힘을 형성하는 많은 것을 제공하고 그것이 당신이 < <을 사용할 때 그 지저분 해 지도록하지 않습니다. 예를 들어

    :

    Logger("This is my log msg %0X", 45); 
    

    이 초에 잠시 아픈 당신을위한 코드 샘플을 당깁니다.

    편집 :

    void Logger(const char* format, ...) 
    { 
        char szMsg[3000]; 
    
        va_list args; 
        va_start(args, format); 
        vsnprintf(szMsg, sizeof(szMsg) - 1, format, args); 
        va_end(args); 
    
        // code to print szMsg to a file or whatever here 
    } 
    

    당신이) 독립 실행 형 기능이 로거 연산자 (오버로드 할 수 없습니다 클래스로 사용하고 싶은 단지 같은 당신이 사용할 수있는

    +0

    가장 훌륭한 제안! << 형식을 사용하려는 두 가지 이유가 있습니다. 하나는 체인화 <<가 함께 가독성을 선호한다는 것입니다. 다른 하나는 많은 char 버퍼 생성을 피하기 위해서입니다. –

    +0

    C++ 코드에서 varargs 방법을 사용하지 않는 이유는 여러 가지가 있습니다. 특히 형식 안전성을 제거하고 호출자/호출 수신자 간의 밀접한 결합 및 UDT의 정의되지 않은 동작을 허용합니다. – twokats

    +0

    잘 알고 있습니다. << C++의 팬이 아니 었습니다 – Lodle

    2

    그것을 할 올바른 방법입니다,하지만 당신은 템플릿의 헤더 파일 (logger.h, 또는 당신이 무엇이라고 함)에 구현 파일에서 하지 있는지 확인하기 위해 (logger.cpp)가 . 이것은 std::ostream으로 정의 된 operator <<이있는 모든 유형에 대해 자동으로 작동합니다. 또한 스트림 조작자 객체와도 자동으로 작동합니다. 이는 실제로 std::ostream 매개 변수를 취하는 함수이고 operator <<ostream에서 함수를 호출하기 만합니다. 다음과 같이

    을 친구 기능 operator <<를 만들 수 있습니다

    template <typename T> friend Logger& operator<< (Logger& logger, T thing); 
    

    전문화 쉬운 - 단지 (헤더 파일에 다시) 템플릿 특수화를 사용

    template <typename T> 
    Logger& operator<< (Logger& logger, T thing) { 
        *logger.out_stream << thing; 
        return logger; 
    } 
    
    // Template specialization - the "template <>" part is necessary 
    template <> 
    Logger& operator<< (Logger& logger, const wchar_t *wstr) 
    { 
        // convert wstr to an ANSI string and log it 
    } 
    
    template <> 
    Logger& operator<< (Logger& logger, const std::wstring & wstr) 
    { 
        // convert wstr to an ANSI string and log it 
    } 
    
    2

    필요 없음 우정 선언 :

    class Logger 
    { 
    public: 
        Logger(); 
        ~Logger(); 
    
    template <typename T> 
    inline Logger& Display(T thing) 
    { 
        *out_stream << thing; 
        return *this; 
    } 
    private: 
        std::ostream* out_stream; 
    }; 
    
    template <typename T> 
    Logger& operator<< (Logger& logger, T thing) 
    { 
        return logger.Display(thing); 
    } 
    
    관련 문제