2017-10-04 6 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を削除し、私は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...); 
} 

ので、両方の世界の取得のベストを尽くすために、私は次の条件を追加してみました見つからなかった場合は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

Firstwchar_t*の場合、キャストはconst wchar_t*ではなくwchar_t* constとなります。キャストは、型名のテキスト置換ではありません。

したがって、特殊化と一致しない新しいタイプを作成し、汎用Log関数への再帰呼び出しを取得します。

+0

"const to pointer"特殊化とは別に、すべての型の "constへのポインタ"特殊化を追加しました。百万こんにちは:)) –

1

const_castは、constから非constへの型のキャストにのみ使用されます。そして、この変数がもともとconstとして宣言されていない場合にのみ、これは機能します。あなたがする必要はありません

はconstのための何かを投げ、あなたはその機能が必要な鋳造せずに、const char*を返します

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

その型のconst参照またはconstの値にそれを単にコピーすることができます。これは割り当てによっても行うことができます。

関連する問題