I need to do some special handling of string types in a template I'm writing, but I'm running into a problem with fixed size char arrays. I'm aware I can write code like mentioned in this answer to create a wrapper: Const char array with template argument size vs. char pointer
But I was wondering if there was a way to test if this is a const char array from within a templated function with a static assert, something like this:
template<typename T>
void f(T val /* a handful of other params */ )
{
static_assert(
std::is_same_v < T, const char* > ||
std::is_same_v < T, char[N] > || // how do I get/discard N here?
std::is_same_v < T, std::string >
, "Unsupported type" );
}
My motivation for this is to avoid having a ton of function signatures for all valid type combinations, while still ensuring only valid types are allowed. Writing all those type declarations and definitions is a nightmare for a few reasons. But as strings can come in various fixed size arrays, I'm unsure how to check them without a wrapper to peel off the length data, but then I am back to writing a ton of wrappers which is exactly what I'm trying to avoid (if there is some way to write a simple reusable test using a single wrapper that would be ok, but I haven't been able to figure one out). And for each string like this, if I was to manually add a wrapper for each time a string param appears in a param list, it will obviously double the number of declarations and definitions I need to write, creating a lot of redundant code. So is there any way to determine if a type is a fixed array type, and only then extract the pointer without the length so I can check it against a const char*? Or someway to ignore the array length and just check if it's a char type array in my assert?
CodePudding user response:
You cannot determine whether a T
is an array like that directly, but the standard library provides traits for determining whether a given type is an array already: std::is_array
.
Though std::is_array
also accepts char[]
, which may be undesirable. In that case if you have access to C 20, you can use std::is_bounded_array
. If you don't have access to C 20, implementing one is rather trivial, or if you want to only check for char[N]
:
template <class> struct is_bounded_char_array : std::false_type {};
template <size_t N>
struct is_bounded_char_array<char[N]> : std::true_type {};
template <class> struct is_bounded_array : std::false_type {};
template <class T, size_t N>
struct is_bounded_array<T[N]> : std::true_type {};
static_assert(!is_bounded_array<char>{});
static_assert(!is_bounded_array<char[]>{});
static_assert(is_bounded_array<char[42]>{});
Then, instead of std::is_same_v < T, char[N] >
, you can say is_bounded_array<T>
.
To get a const char*
from any of your types, you could use std::string_view(t).data()
instead of handling each case yourself.
CodePudding user response:
It is not possible due to type decay; Result type of T (from function foo) is char*. You can figure it out via my example, just try to compile it:
#include <type_traits>
template<typename T>
struct is_inbuild_array final : std::false_type{};
template<typename T, std::size_t N>
struct is_inbuild_array<T[N]> final : std::true_type{};
template<typename T>
void f(T val)
{
static_assert(
is_inbuild_array<T>::value,
"Unsupported type"
);
}
int main()
{
char array [] = "Test";
f(array);
}
But you can modify your function like this(use universal reference):
template<typename T>
void f(T&& val)
{
static_assert(
is_inbuild_array<std::remove_reference_t<T>>::value,
"Unsupported type"
);
}
And it works well