Home > Software engineering >  How check instantiation of one type by another with templates?
How check instantiation of one type by another with templates?

Time:06-30

In my case there is func:

// msg can be std::string, std::wstring, const char*, const wchar_t*, ...
template<typename StrType>
void Log(StrType msg) {
   if std::string(msg) can be created from msg {
   // do smth
   }
   if std::wstring(msg) can be created from msg {
   // do smth
   }
   else {
   // wrong argument
   }
}

and i have to check if the std::string(wstring) can be created 'msg' to do appropriate actions.

CodePudding user response:

Since C 17, you may use a constexpr if to check whether std::string or std::wstring can be constructed from the argument you pass to Log:

template <typename StrType>
void Log(StrType msg) {
    if constexpr (std::is_constructible_v<std::string, StrType>) {
        // do smth
    } else if (std::is_constructible_v<std::wstring, StrType>) {
        // do smth
    } else {
        // wrong argument
    }
}

CodePudding user response:

You can either use SFINAE (tested for C 17) or Concepts (C 20)

#include <iostream>
#include <type_traits>
#include <string>


//---------------------------------------------------------------------------------------------------------------------
// C  17 approach
// Use SFINAE to only generate void return type if predicate is met.

template<typename StrType>
auto Log(const StrType& msg) -> std::enable_if_t<std::is_constructible_v<std::string, StrType>, void>
{
    std::cout << "can construct std::string from msg : " << std::string{ msg } << "\n";
}

template<typename StrType>
auto Log(const StrType& msg) -> std::enable_if_t<std::is_constructible_v<std::wstring, StrType>, void>
{
    std::wcout << L"can construct std::wstring from msg : " << std::wstring{ msg } << L"\n";
}

//--------------------------------------------------------------------------------------------------------------------
// C  20 approach (concepts)

template<typename StrType>
concept StdStringCompatible = std::is_constructible_v<std::string, StrType>;

template<typename StrType>
concept StdWStringCompatible = std::is_constructible_v<std::wstring, StrType>;

auto Log20(StdStringCompatible auto const& msg)
{
    std::cout << "can construct std::string from msg : " << std::string{ msg } << "\n";
}

auto Log20(StdWStringCompatible auto const& msg)
{
    std::wcout << L"can construct std::wstring from msg : " << std::wstring{ msg } << L"\n";
}

//--------------------------------------------------------------------------------------------------------------------

int main()
{
    // to show you how you can check if a std::string or std::wstring can be constructed from a type
    static_assert(std::is_constructible_v<std::string, const char*>);
    static_assert(std::is_constructible_v<std::wstring, const wchar_t*>);

    const char* msg = "const char* message";
    const wchar_t* wmsg = L"const wchar_t* message";

    Log(msg);
    Log(wmsg);
    // Log(10); // will not compile as intended
    
    Log20(msg);
    Log20(wmsg);
    // Log20(10); // will not compile as intended

    return 0;
}

CodePudding user response:

If you're ok with a compiler error, if the string is neither convertible to std::string nor std::wstring, you could simply rely on CTAD (Class Template Argument Deduction) to determine the type parameters for std::basic_string for you.

(std::string and std::wstring both are aliases for std::basic_string specializations.)

void DoSomething(std::string const& str)
{
    std::cout << "std::string\n";
}

void DoSomething(std::wstring const& str)
{
    std::cout << "std::wstring\n";
}

template<typename StrType>
void Log(StrType&& msg) {
    DoSomething(std::basic_string(std::forward<StrType>(msg)));
}

int main() {
    using namespace std::literals::string_view_literals;

    Log("Foo bar");
    Log(L"Foo bar");
    Log("abc"sv);
    return 0;
}

This approach requires C 17 or later.

  • Related