Search code examples
c++stringtemplatessfinae

SFINAE function that returns proper character encoding depending on template type?


I'm creating a class that's writing code strings, The difference between the string and the wring is only what to put in the basic_string, so I'm going to write it as a template class.

The problem is that the literal strings used internally also have to change depending on the Type, so I combined macro and SFINAE to create a function that returns different literal strings depending on the Type.

But my code had a "no instance of function template matches the argument list" error.

this is my code:

#include <filesystem>
#include <string>
#include <type_traits>
#include <fstream>
#include "windows.h"

namespace stdfs = std::filesystem;

template <typename T>
class CCodeWriter
{
public:
    CCodeWriter() {};
    ~CCodeWriter() {};

private:
    template <typename U = T, typename std::enable_if_t<std::is_same_v<U, wchar_t>, wchar_t>>
    const std::basic_string_view<U> ConvString(const std::string_view _AstrView, const std::wstring_view _WstrView)
    {
        return _WstrView;
    }

    template <typename U = T, typename std::enable_if_t<std::is_same_v<U, char>, char>>
    const std::basic_string_view<U> ConvString(const std::string_view _AstrView, const std::wstring_view _WstrView)
    {
        return _AstrView;
    }



#define CONV_STRING(str) ConvString(str, L##str)


private:
    int m_iIndentation;
    std::basic_ofstream<T> m_ofs;

public:
    HRESULT Open(stdfs::path const& _path) { return S_OK; }
    bool IsOpen() const { return m_ofs.is_open(); }

    void WriteCode(const std::basic_string_view<T> _strCode)
    {

        ...

        //Error
        m_ofs << CONV_STRING("\t");

        //Error
        m_ofs << ConvString("\t", L"\t");

        ...
    }
};


int main()
{
    CCodeWriter<char> writer;

    writer.WriteCode("HI");

    return 0;
}

Isn't there any other way but write each literal strings by hand?

I tried to change the parameter type, but I'm not familiar with SFINAE, so everything I've tried has an error.

What I expect is that due to the attibutes of SFINAE, the literal "\t" is returned when the type is entered in , and the literal L"\t" is returned when the <wchar_t> type is entered.

Will it be possible?


Solution

  • Your enable_if usage is incorrect, one possible usage would be

    template <typename U = T, std::enable_if_t<std::is_same_v<U, wchar_t>, int> = 0>
    

    Currently, when T is char, your SFINAE overloads looks like this

    template <typename U = char, char>
    

    So obviously that second char non-type template parameter cannot be deduced.


    In C++17 and above, you can simply use if constexpr

    const std::basic_string_view<T> 
    ConvString(const std::string_view _AstrView, const std::wstring_view _WstrView)
    {
        if constexpr (std::is_same_v<T, char>) { return _AstrView; }
        else { return _WstrView; }
    }
    

    You can also use std::char_traits<T>::to_char_type('\n') to convert '\n' to the appropriate character type. This should work for basic characters (\n and \t in particular).