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.