2017-10-04 11 views
0

Windows에서 Visual Studio를 사용하여 C++에서 다양한 템플릿을 지원하는 간단한 로깅 클래스를 작성했습니다. 가능한 입력의 일반적인 조합을 맞추기 위해 많은 수의 특수화 된 일반 Log 함수 템플릿을 만들었습니다.const_cast는 C++ 템플릿에서 무시되는 것 같습니다.

#pragma once 

#include <Windows.h> 
#include <locale> 
#include <codecvt> 
#include <string> 
#include <sstream> 
#include <utility> 

using namespace std; 

inline static string to_utf8(const wstring& s) { 
    wstring_convert<codecvt_utf8_utf16<wchar_t>> utf16conv; 
    return utf16conv.to_bytes(s); 
} 

class Logger { 
public: 
    HANDLE file_handle; 
    wchar_t file_path[MAX_PATH + 1]; 

    inline Logger(HANDLE handle) : file_handle(handle), file_path{} { 
    } 

    inline Logger(const string& path) : Logger(path.c_str()) { 
    } 

    inline Logger(const wstring& path) : Logger(path.c_str()) { 
    } 

    inline Logger(const char* path) : file_handle(NULL) { 
     wstring_convert<codecvt_utf8_utf16<wchar_t>> converter; 
     wcscpy_s(file_path, MAX_PATH + 1, converter.from_bytes(path).c_str()); 
    } 

    inline Logger(const wchar_t* path) : file_handle(NULL) { 
     wcscpy_s(file_path, MAX_PATH + 1, path); 
    } 

private: 
    inline void VerifyInitialize() { 
     if ((file_handle == NULL || file_handle == INVALID_HANDLE_VALUE) && file_path[0] != '\0') { 
      file_handle = CreateFileW(file_path, FILE_GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 
      SetFilePointer(file_path, 0, NULL, FILE_END); 
     } 
    } 

public: 
    inline void Log() { 
    } 

    template<typename ...Rest> 
    inline void Log(const char* first, Rest... params) { 
     VerifyInitialize(); 
     if (file_handle == NULL || file_handle == INVALID_HANDLE_VALUE) 
      return; 
     DWORD written; 
     WriteFile(file_handle, first, static_cast<DWORD>(strlen(first)), &written, NULL); 
     Log(params...); 
    } 

    template<typename ...Rest> 
    inline void Log(const char first, Rest... params) { 
     char str[2]; 
     str[0] = first; 
     str[1] = '\0'; 
     Log(str, params...); 
    } 

    template<typename ...Rest> 
    inline void Log(const string* first, Rest... params) { 
     VerifyInitialize(); 
     if (file_handle == NULL || file_handle == INVALID_HANDLE_VALUE) 
      return; 
     DWORD written; 
     WriteFile(file_handle, first->c_str(), static_cast<DWORD>(first->size()), &written, NULL); 
     Log(params...); 
    } 

    template<typename ...Rest> 
    inline void Log(const string& first, Rest... params) { 
     Log(&first, params...); 
    } 

    template<typename ...Rest> 
    inline void Log(const wstring* first, Rest... params) { 
     Log(*first, params...); 
    } 

    template<typename ...Rest> 
    inline void Log(const wstring& first, Rest... params) { 
     Log(to_utf8(first), params...); 
    } 

    template<typename ...Rest> 
    inline void Log(const wchar_t* first, Rest... params) { 
     Log(wstring(first), params...); 
    } 

    template<typename ...Rest> 
    inline void Log(const wchar_t first, Rest... params) { 
     wchar_t str[2]; 
     str[0] = first; 
     str[1] = '\0'; 
     Log(str, params...); 
    } 

    template<typename First, typename ...Rest> 
    inline void Log(First first, Rest... params) { 
     printf("%s\n", typeid(First).name()); 
     if (is_const<First>::value) { 
      stringstream stream; 
      stream << first; 
      Log(stream.str(), params...); 
     } else 
      Log(const_cast<const First>(first), params...); 
    } 

    inline ~Logger() { 
     if (!(file_handle == NULL || file_handle == INVALID_HANDLE_VALUE)) { 
      CloseHandle(file_handle); 
      file_handle = NULL; 
     } 
    } 
}; 

코드가 const 값과 잘 작동합니다. 내가 그렇게 같은 const가 아닌 매개 변수를 도입 그러나 경우

int main() { 
    Logger logger(("output.txt")); 
    wchar_t sometext[3]; 
    sometext[0] = '1'; 
    sometext[1] = '8'; 
    sometext[2] = '\0'; 
    logger.Log(sometext, L"\n"); 
    return 0; 
} 

특수화가 호출되지 않고 대신 마지막 일반적인 void Log(First first, Rest... params)stringstream을 사용하고 대신 문자열 문자열 자체로 포인터를 기록하는 호출됩니다.

나는 모든 과부하 매개 변수, 내가 main()를 호출 할 때 작동하지만 나는 const char*sometext를 교체 할 경우, 다음 마지막으로 일반 void Log(First first, Rest... params)은 (대신 전문의 호출 ​​예. 문제가 해결되지 const을 제거하는 const을 제거하는 경우). 이론적 근거는 const를 전문화 과부하 "첫 번째 검색하도록 컴파일러에 지시 인 상태

template<typename First, typename ...Rest> 
inline void Log(First first, Rest... params) { 
    if (is_const<First>::value) { 
     stringstream stream; 
     stream << first; 
     Log(stream.str(), params...); 
    } else 
     Log(const_cast<const First>(first), params...); 
} 

:

그래서 두 세계의 얻는 최선을 다하기 위해, 나는 다음과 같은 조건을 추가 시도 발견되지 않으면 stringstream "을 사용하여 대체합니다.

그러나 스택 오버 플로우가 발생했습니다. 디버깅하려면, 난 그냥 if 조건 전에 printf("%s\n", typeid(First).name());을 추가, 출력했다 : 실제로 false을 반환하는 경우에도 is_const::value 불구하고

wchar_t * __ptr64 
wchar_t * __ptr64 
wchar_t * __ptr64 
wchar_t * __ptr64 
.... 

분명히, const_cast<const First>(first)const에 캐스팅 사실에 될 것 같지 않습니다. 또한 std::as_const을 사용해 보았지만 그 결과에는 차이가 없었습니다.

왜 캐스트가 작동하지 않습니까? 이 문제를 어떻게 해결할 수 있습니까?

답변

1

wchar_t* 캐스트는 wchar_t* const 아닌 const wchar_t* 만듭니다. 캐스트는 유형 이름의 대체 텍스트가 아닙니다.

따라서 특수화와 일치하지 않는 새 유형을 만들고 일반 Log 함수에 대한 재귀 호출을 얻습니다.

+0

"const to pointer"전문 분야 외에, 모든 유형에 대해 "const에 대한 포인터"전문 분야를 추가했습니다. 백만 감사 :)) –

1

const_cast은 형식을 const에서 non-const로 변환하는 데만 사용됩니다. 그리고이 변수가 원래 const로 선언되지 않은 경우에만 작동합니다.

당신은에서 const 캐스트에 뭔가를하지 않아도, 당신이 필요로 주조하지 않고, 그 함수는 const char* 반환 유형

const char* changeSecond(char* string, char change) 
{ 
    string[0] = change; 
    return string; 
} 

의 const를 참조 또는 const를 값에 복사 만 할 수 있습니다. 이것은 할당으로도 수행 할 수 있습니다. First 인 경우