2017-10-04 201 views
0

我寫了一個簡單的日誌記錄類,在Windows上使用Visual Studio在C++中支持variadic模板。我創建了一個通用的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 )。

因此,爲了嘗試獲得兩全其美的,我嘗試添加以下條件:

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...); 
} 

與理由是編譯器指令,以「先尋找一個const專業化超載,如果沒有找到,則回退到使用stringstream

但是,我得到了堆棧溢出。爲了調試,我只是if條件之前加入printf("%s\n", typeid(First).name());,輸出功率爲:

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

顯然,const_cast<const First>(first)似乎並沒有在實際上鑄造const,即使is_const::value確實返回false。我也嘗試使用std::as_const,但結果沒有差異。

爲什麼演員不工作?我該如何解決這個問題?

回答

1

如果Firstwchar_t*演員陣營將創建一個wchar_t* const而不是const wchar_t*。該轉換不是類型名稱的文本替換。

因此,您創建一個不匹配任何專業化的新類型,並獲得對通用Log函數的遞歸調用。

+0

除了「const to pointer」專精之外,我還爲每種類型添加了「指向const」的特化,並且一切正常!萬分感謝:)) –

1

const_cast僅用於將類型從const轉換爲非const。而且這隻有在所述變量最初不是被聲明爲const時纔有效。

你不需要東西爲const,你可以將它複製到該類型

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

的引用或常量,用來函數會返回一個const char*,而無需進行轉換需要。這也可以通過分配完成。