C++ Template Type Traits Issue - c++

I am new to making my own type traits and I want to make a type trait that allows me to identify if the passed type is the expected container.
template<typename T, typename ... Types>
struct is_array
{
static constexpr bool value = false;
};
template<typename ... Types>
struct is_array<std::array<Types...>>
{
static constexpr bool value = true;
};
I've adopted the format above and it works for all container types except for array:
constexpr bool state = is_array<std::array<int, 5>>::value;
The above evaluates to false when it should be true. This only happens for the array class and I believe it is due to it having 2 template parameters that do not have default values. I am unable to figure out a fix for this.

First, your trait should have one, and only one template parameter: the expected array type:
template<typename T/*, typename ... Types*/>
struct is_array {
static constexpr bool value = false;
};
Second, std::array is defined as:
template<
class T,
std::size_t N
> struct array;
as first type parameter T and a non-type parameter N of type std::size_t.
Therefore, your true specialization should be:
template<typename T, std::size_t N>
struct is_array<std::array<T,N>>
{
static constexpr bool value = true;
};
Note, that instead of defining a member value, we prefer to inherit from std::true_type and std::false_type which provide some other member alias + conversion operator. The code then becomes:
template<typename T>
struct is_array : std::false_type {};
template<typename T, std::size_t N>
struct is_array<std::array<T,N>> : std::true_type {};

Related

SFINAE for true_type and false_type in std::conditional

What's the best way to make this compile?
// Precondition: Dims is either a pointer or std::map.
using T = std::conditional_t<std::is_pointer_v<Dims>,
std::remove_pointer_t<Dims>,
typename Dims::mapped_type>;
When Dims is a pointer, I am getting:
error: template argument 3 is invalid
How do I make it work in SFINAE manner, when condition is true?
template<class T>
struct mapped_type{using type=typename T::mapped_type;};
using T = typename std::conditional_t<std::is_pointer_v<Dims>,
std::remove_pointer<Dims>,
mapped_type<Dims>>::type;
we defer the "execution" until after the condition.
If the passed type (i.e. Dims) and the mapped_type is always default constructable, you can do something like as follows in c++17:
#include <map>
#include <type_traits> // std::is_pointer_v
template<typename Type> auto helper()
{
if constexpr (std::is_pointer_v<Type>) return std::remove_pointer_t<Type>{};
else if constexpr (!std::is_pointer_v<Type>) return typename Type::mapped_type {};
}
// helper trait
template<typename Dims>
using ConditionalType_t = decltype(helper<Dims>());
(See a Demo)
Or using partial specialization of the template traits (assuming that you will only pass either a pointer or non-pointer std::map type)
#include <type_traits> // std::is_pointer_v
// traits to see passed type is a std::map
template<typename> struct is_std_map final: std::false_type {};
template<typename Key, typename Value, typename... Rest>
struct is_std_map<std::map<Key, Value, Rest...>> final : std::true_type {};
// the partial specialization of helper traits
template<class T, class Enable = void> struct helper_traits final {};
template<typename T>
struct helper_traits<T, std::enable_if_t<std::is_pointer_v<T>>> final {
using type = std::remove_pointer_t<T>;
};
template<typename T>
struct helper_traits<T, std::enable_if_t<is_std_map<T>::value && !std::is_pointer_v<T>>> final {
using type = typename T::mapped_type;
};
// trait helper
template<typename Type> using ConditionalType_t = typename helper_traits<Type>::type;
(See a Demo)

Is it possible to mix SFINAE and template specialisation?

Here is what I am roughly trying to achieve:
// the declaration
template<typename... Args>
struct ArgsEstimate;
// specialisation for string, SFINAE would be overkill
template<typename... Args>
struct ArgsEstimate<std::string&, Args...> {
static const std::size_t size = 64 + ArgsEstimate<Args...>::size;
};
// specialisation for arithmetic types
template<typename AirthmeticT,
typename std::enable_if<std::is_arithmetic<AirthmeticT>::value>::type* = nullptr,
typename... Args>
struct ArgsEstimate<AirthmeticT, Args...> {
static const std::size_t size = sizeof(AirthmeticT) + ArgsEstimate<Args...>::size;
};
// specialisation for pointer types
template<typename PtrT,
typename std::enable_if<std::is_pointer<PtrT>::value>::type* = nullptr,
typename... Args>
struct ArgsEstimate<PtrT, Args...> {
static const std::size_t size = 32 + ArgsEstimate<Args...>::size;
};
The problem is that this code gives a compilation error "template parameters not deducible in partial specialization" at the points I have done enable_if. A static_assert inside the struct won't work either since there will be redefinition.
I know, I can probably do this with SFINAE and function overloading alone. However, for cases like just std::string, using SFINAE is an overkill.
So I was wondering if there is clean way of mixing template specialisation and SFINAE.
Direct answer to your question
You can, but you really can't. Your case is complicated by variadic template arguments.
// specialisation for arithmetic types
template<class AirthmeticT, class... Args>
struct ArgsEstimate<
AirthmeticT,
std::enable_if_t<std::is_arithmetic_v<AirthmeticT>>,
Args...>
{
static const std::size_t size = sizeof(AirthmeticT) + ArgsEstimate<Args...>::size;
};
This works... sort of. You just need to make sure the second parameter is always void:
ArgsEstimate<int, void, /* ... */> ok; // will use the integer specialization
ArgsEstimate<int, int, int> wrong; // oups, will use the base template.
This is impractical.
C++20 concepts
Concepts elegantly solve this:
// specialisation for arithmetic types
template<class T, class... Args>
requires std::is_arithmetic_v<T>
struct ArgsEstimate<T, Args...>
{
static const std::size_t size = sizeof(T) + ArgsEstimate<Args...>::size;
};
The pre-concepts solution
What you need to do is to split your class into two classes. One that defines the size just for 1 argument. Here you can use SFINAE. And the other one that summs them:
template <class T, class Enable = void>
struct ArgEstimate {};
// specialisation for string, SFINAE would be overkill
template<>
struct ArgEstimate<std::string&>
{
static const std::size_t size = 64;
};
// specialisation for arithmetic types
template<class T>
struct ArgEstimate<T, std::enable_if_t<std::is_arithmetic_v<T>>>
{
static const std::size_t size = sizeof(T);
};
// specialisation for pointer types
template <class T>
struct ArgEstimate<T*>
{
static const std::size_t size = 32;
};
// the declaration
template<class... Args> struct ArgsEstimate;
template<class T>
struct ArgsEstimate<T>
{
static const std::size_t size = ArgEstimate<T>::size;
};
template<class Head, class... Tail>
struct ArgsEstimate<Head, Tail...>
{
static const std::size_t size = ArgEstimate<Head>::size + ArgsEstimate<Tail...>::size;
};
And if you have C++17 you can use fold expression to simplify the sum:
template<class... Args>
struct ArgsEstimate
{
static const std::size_t size = (... + ArgEstimate<Args>::size);
};
Also just wanted to point out that you don't need SFINAE for pointers:
// specialisation for pointer types
template <class T, class... Args>
struct ArgsEstimate<T*, Args...> {
static const std::size_t size = 32 + ArgsEstimate<Args...>::size;
};

Constructing std::hash function which accepts any iterable type

I have tried to implement a std::hash function which works on any iterator type, where the user has to implement a hashing function for their type T.
My initial implementation of the std::hash function for std::array be seen below:
template <typename T, size_t N>
struct std::hash<std::array<T, N>> {
std::size_t operator()(const std::array<T, N>& x) const {
auto hash_func = std::hash<T>{};
size_t h_val{};
for (T t : x) {
h_val = h_val ^ hash_func(t);
}
return h_val;
}
};
For supporting any iterator I have been trying to use sfinae, where my current implementation of a container type is_container can be seen below:
template <typename T, typename = void> // primary declaration
struct is_container: std::false_type {}; // when all specializations fail
template <typename T>
struct is_container< // specialization
T, // conditions:
std::void_t<decltype(std::begin(std::declval<T&>()))>
>: std::true_type {};
template <typename C> // *_v value
constexpr auto is_container_v = is_container<C>::value;
My problem is that I can't seem to match the required parameters for the struct std::hash<>.

has_type template returns true for struct type {};

There are a number of ways to implement a has_type<T> template that deduces if T has a nested class or typedef named type. ie
namespace detail {
template<typename> struct tovoid { typedef void type; };
}
template<typename T, typename = void> struct has_type
: std::false_type { };
// this one will only be selected if C::type is valid
template<typename C> struct has_type<C, typename detail::tovoid<typename C::type>::type>
: std::true_type { };
Or
template <typename C> char test_for_type(...) { return '0'; }
template <typename C> double test_for_type(typename C::type const *) { return 0.0; }
template <typename T> struct has_type
{
static const bool value = sizeof(test_for_type<T>(0)) == sizeof(double);
};
however in either case, has_type<type>::value is true for this class:
struct type
{
};
Now the above type doesn't have another type nested within it, but it does have a constructor type::type().
But should that constructor 'trigger' the checks for the nested type? Or is it a compiler bug?
(I would like to think that typename type::type didn't apply to a constructor and/or that you couldn't take a pointer to a constructor, such as what would be produced by the second test method: typename type::type const *.
?
The name of a class is "injected" into the scope of the class, so type::type really is the name of a type, and it's the same type as ::type.

Build an enum using variadic template parameters

I have a fairly simple variant class which supports a predefined set of types, and provides an enumerate to indicate which of the available types is currently active. Something like this:
class variant
{
enum class type { integer, real, string, etc };
type active_type() const;
/* ... */
};
I would like to make the class into a template where the supported types are supplied as template parameters:
template <typename... T>
class variant
{
const std::type_info& active_type() const; // logical, but can't switch on it
/* ... */
};
A key feature that I rely on for catching errors is that I can switch on the active type and the compiler will warn if any of the possible cases have been missed. This is not possible using the above design (nor using boost::variant).
My question is, is there any way for me to automatically generate an enum with the same number of enumerates as the number of arguments in the parameter pack?
The actual names/values of the enumerates would not matter, as they can be hidden behind constexpr functions used to map the type to the correct enumerate. I could imagine an eventual usage like this:
template <typename... T>
class variant
{
enum class type { T... }; // magic here
// specializations provided to map T into type (for use in case labels)
template <typename T>
static constexpr type type_enum();
type active_type() const;
/* ... */
};
typedef variant<int, float, std::string> myvar;
myvar var;
switch (var.active_type())
{
case myvar::type_enum<int>(): // static constexpr function
...
break;
case myvar::type_enum<float>():
...
break;
} // warning: enumeration for string not handled in switch
This question is two years old, but since there are no answers and I found no solutions elsewhere, here's a way how I have solved the problem.
First obvious problem is enum type with matching number of parameters (to let compiler do switch checking). The only way I see is a series of template specializations.
template <typename Tag, size_t size> struct Enum;
template <typename Tag> struct Enum<Tag, 0> {
enum class Type { };
};
template <typename Tag> struct Enum<Tag, 1> {
enum class Type {
VALUE0
};
};
template <typename Tag> struct Enum<Tag, 2> {
enum class Type {
VALUE0, VALUE1
};
};
template <typename Tag> struct Enum<Tag, 3> {
enum class Type {
VALUE0, VALUE1, VALUE2
};
};
template <typename Tag> struct Enum<Tag, 4> {
enum class Type {
VALUE0, VALUE1, VALUE2, VALUE3
};
};
Yes, it requires some manual input, but it's not a big problem, keeping in mind that number of types is usually limited and new specializations can be added as they are required (i.e. the approach is statically safe). The Tag parameter is used to distinguish enums with same number of values.
Secondly we need some recursive template magic to enumerate types:
template <typename EnumType, typename Type, typename... Types> struct TypeInfo;
template <typename EnumType, typename Type, typename... Types> struct TypeInfo<EnumType, Type, Type, Types...> {
static const EnumType value = EnumType::VALUE0;
};
template <typename EnumType, typename Type, typename FirstType, typename... Types> struct TypeInfo<EnumType, Type, FirstType, Types...> {
static const EnumType value = static_cast<EnumType>(static_cast<typename std::underlying_type<EnumType>::type>(TypeInfo<EnumType, Type, Types...>::value) + 1);
};
Finally a class to bring all pieces together:
template <typename Tag, typename... Ts> class TypedEnum {
private:
struct InternalTag;
public:
static const size_t NUM_TYPES = sizeof...(Ts);
typedef typename Enum<InternalTag, NUM_TYPES>::Type Type;
template <typename T> static constexpr decltype(TypeInfo<Type, T, Ts...>::value) getValue() { // SFINAE guard
return TypeInfo<Type, T, Ts...>::value;
}
};
Voila! You can switch! (Tested with clang)
struct Tag0;
struct Tag1;
typedef TypedEnum<Tag0, int, float, char> Enum0;
typedef TypedEnum<Tag0, bool, float, char> Enum1; // Incompatible with Enum0!
Enum0::Type tpy = Enum0::getValue<char>(); // 2
switch(tpy) {
case Enum0::getValue<int>():
break;
case Enum0::getValue<float>():
break;
case Enum0::getValue<char>():
break;
}
Summary of features:
Statically rejecting types that are not in enum
Statically warn about missing switch cases
Incompatible enum types for different tags and/or parameters in pack
Features missing:
Needs some template magic to reject parameter packs with duplicates
Get the index of the type in a variadic template and use that as the enum value. This way, you don't need to copy pasta all the code for different numbers of types. This doesn't handle duplicate types in the list, but the index will be the first occurrence in the variadic list. If there needs to be duplicate detection it can be added using std::is_same and looping over all the types for each type in a similar fashion to IndexOf.
#include <type_traits>
using std::size_t;
template <size_t Idx, class Q, class... Ts>
struct IndexOfImpl;
// base case
template <size_t Idx, class Q>
struct IndexOfImpl<Idx, Q>{};
template <size_t Idx, class Q, class T>
struct IndexOfImpl<Idx, Q, T>
: public std::conditional_t<
std::is_same<Q, T>::value, // Is Query the same as T?
// if so
std::integral_constant<size_t, Idx>, // inheret from Idx
void> // type not found
{
};
template <size_t Idx, class Q, class T, class... Ts>
struct IndexOfImpl<Idx, Q, T, Ts...>
: public std::conditional_t<
std::is_same<Q, T>::value, // Is Query the same as T?
// if so
std::integral_constant<size_t, Idx>, // inheret from Idx
IndexOfImpl<Idx + 1, Q, Ts...>> // else try the trailing types
{
};
// Initial case at Idx 0
template <class Q, class... Ts>
struct IndexOf : public IndexOfImpl<0, Q, Ts...>
{
};
// Base case
template <class Q>
struct IndexOf<Q>
{
};
// Store the type list in Ts...
template <class... Ts>
struct TypeEnum {
// Get index/enum value of type T
template <class T>
static constexpr decltype(IndexOf<T, Ts...>::value) getValue() {
return IndexOf<T, Ts...>::value;
}
};
template <>
struct TypeEnum<> {};
struct Unknown;
int main() {
using Tags = TypeEnum<Unknown, int, float, long long, unsigned int, double>;
size_t tag = Tags::getValue<double>();
switch(tag) {
case Tags::getValue<int>(): return 0;
case Tags::getValue<float>(): return 0;
case Tags::getValue<long long>(): return 0;
case Tags::getValue<unsigned int>(): return 0;
case Tags::getValue<double>(): return 0;
default: return 1;
}
}
On compiler explorer