simple library for some missing type traits I always need when making unreadable meta programs.
Minimum C++11 with support for variadic macros and variaidc templates and type traits library.
all these are available in namespace zxshady::tmp
and all type traits have _t unconditionally but it also has _v shortcuts if available.
has_operator_*
each of these has their own header for minimizing includes you can look at the file names at include/zxshady/has_operators/
Category | Operator |
---|---|
Increment/Decrement | post_increment |
pre_decrement | |
pre_increment | |
post_increment | |
Unary | dereference |
complement | |
unary_minus | |
unary_plus | |
Bitwise | bit_and |
bit_or | |
bit_xor | |
bit_and_assign | |
bit_or_assign | |
bit_xor_assign | |
Shift | left_shift |
left_shift_assign | |
right_shift | |
right_shift_assign | |
Logical | logical_and |
logical_not | |
logical_or | |
Arithmetic | plus |
minus | |
modulus | |
multiply | |
divide | |
plus_assign | |
minus_assign | |
modulus_assign | |
multiply_assign | |
divide_assign | |
Comparison | equal_to |
not_equal_to | |
less | |
less_equal | |
greater | |
greater_equal | |
Accessors | arrow |
subscript | |
addressof | |
Functor | call |
each has_operator_XXX has 4 static members
has_operator_XXX<T>::member; // whether it is a member function
has_operator_XXX<T>::free; // whether it is a free function
has_operator_XXX<T>::overloaded; // equalivent to member || free
has_operator_XXX<T>::value; // whether it has the operator or not
simple type aliases
template<std::size_t Value>
using index_c = std::integral_constant<std::size_t,Value>;
template<typename T>
using alignof_c = std::integral_constant<std::size_t,alignof(T)>;
template<typename T>
using sizeof_c = std::integral_constant<std::size_t,sizeof(T)>;
// this is avaiable in C++17
template<auto Value>
using constant = std::integral_constant<decltype(Value),Value>;
template<typename Trait>
using t_ = typename Trait::type; // shortcut trait
template<typename Trait>
constexpr bool v_ = Trait::value; // shortcut variable
_
template<typename...>
using always_false = std::false_type; (var alias : false_v)
template<typename...>
using always_true = std::true_type; (var alias: true_v)
template<typename T,typename...>
using type_t = T;
template<typename T> // since C++14
using is_inheritable = bool_constant<std::is_class_v<T> && !std::is_final_v<T> >;
template<typename...>
struct empty_base {};
// does not report true for bool
template<typename T>
using is_signed_integral = std::bool_constant<
!std::is_same_v<bool,std::remove_cv_t<T>> && std::is_integral_v<T> && std::is_signed_v<T>>;
// does not report true for bool
template<typename T>
using is_unsigned_integral = std::bool_constant<!std::is_same_v<bool,std::remove_cv_t<T>> && std::is_integral_v<T> && std::is_unsigned_v<T>>;
utility functions
as_const(T&) // same as std::as__const
as_volatile(T&)
as_cv(T&)
forward_like<Like>(T&)
Standard Library Features in previous C++ versions
// in "extra_traits/standard_library_features.hpp"
std::disjunction
std::conjunction
std::negation
std::is_null_pointer
std::bool_constant
std::type_identity
std::integer_sequence
std::remove_cvref
std::void_t
std::is_scoped_enum
std::is_bounded_array
std::is_unbounded_array
utility traits
template<bool Condition,typename Base>
inherit_if<Condition,Base>::type // if Condition is true then inherit from Base otherwise inherit from empty_base<Base>
non-short circuiting disjunction
and conjunction
and_<Traits...>::value;
or_<Traits...>::value;
constructing predicates
template<template<class...> class... Predicates>
struct predicate_and;
template<template<class...> class... Predicates>
struct predicate_or;
template<template<class...> class Predicates>
struct predicate_not;
// each one of them has a member called
// 'invoke' that is used like this
using is_const_integer = predicate_and<std::is_const,std::is_integral>;
static_assert(template is_const_integer::invoke<int>::value);
unary traits
is_scoped_enum<Enum>::value
is_unscoped_enum<Enum>::value
modifying traits
remove_all_pointers<int******> -> int;
remove_all_pointers<float(*)[]> -> float[];
- from paper
Note each copy_xxxxx
trait has a corrosponding clone_xxxxx
(except copy_signedness
there is no corrosponding clone_signedness
) which is equal to copy_xxxxx<From,remove_xxxxx_t<To>>
*
Show Table
From -> To
const From -> const To
volatile From -> To
const volatile From -> const To
--------------------------------
From& -> To
const From& -> To
volatile From& -> To
const volatile From& -> To
--------------------------------
From&& -> To
const From&& -> To
volatile From&& -> To
const volatile From&& -> To
Show Table
From -> To
const From -> To
volatile From -> volatile To
const volatile From -> volatile To
--------------------------------
From& -> To
const From& -> To
volatile From& -> To
const volatile From& -> To
--------------------------------
From&& -> To
const From&& -> To
volatile From&& -> To
const volatile From&& -> To
is a short cut for copy_const_t <From,copy_volatile_t<From,To>>
Show Table
From -> To const From -> const To volatile From -> volatile To const volatile From -> const volatile To -------------------------------- From& -> To const From& -> To volatile From& -> To const volatile From& -> To -------------------------------- From&& -> To const From&& -> To volatile From&& -> To const volatile From&& -> ToShow Table
NOTE: this uses reference collapsing rules, which may not be wanted if you do not want it then use clone_reference
.
From -> To
const From -> To
volatile From -> To
const volatile From -> To
--------------------------------
From& -> To&
const From& -> To&
volatile From& -> To&
const volatile From& -> To&
--------------------------------
From&& -> To&&
const From&& -> To&&
volatile From&& -> To&&
const volatile From&& -> To&&
a short cut for "copy_reference<From, copy_reference_t<To, copy_cv_t<remove_reference_t<From>, remove_reference_t<To>>>>"
Show Table
From -> To
const From -> To
volatile From -> To
const volatile From -> To
--------------------------------
From* -> To*
From*const -> To*
From*volatile -> To*
From*const volatile -> To*
Show Table
From -> To
const From -> To
volatile From -> To
const volatile From -> To
--------------------------------
From** -> To**
From**const -> To**
From**volatile -> To**
From**const volatile -> To**
Show Table
signed From -> signed To
const signed From -> signed To
volatile signed From -> signed To
const volatile signed From -> signed To
--------------------------------
unsigned From -> unsigned To
const unsigned From -> unsigned To
volatile unsigned From -> unsigned To
const volatile unsigned From -> unsigned To
--------------------------------
char -> [to's sign does not change] To
const char -> [to's sign does not change] To
volatile char -> [to's sign does not change] To
const volatile char -> [to's sign does not change] To
gets the smallest unsigned or signed integer type that can hold this value
Show Example (it can differ depending on the size of types)
least_size_uint_t<200>; // unsigned char
least_size_int_t<500>; // signed short
removes the reference qualifier if it is an L or R value respectivly.
Show Table
typename remove_lvalue_reference<int>::type; // int
typename remove_lvalue_reference<int&>::type; // int
typename remove_lvalue_reference<int&&>::type; // int&&
typename remove_rvalue_reference<int>::type; // int
typename remove_rvalue_reference<int&>::type; // int&
typename remove_rvalue_reference<int&&>::type; // int
the first does a std::forward and the second does std::move, why does these exist? well it is for compile time performance as std::forward is relativly expensive for a glorified static_cast
these macros fix this issue these macros are in the extra_traits/macros.hpp
header file.
Show Example
[](auto&& vals) {
std::forward<decltype(vals)>(vals);
// vs
ZXSHADY_FWD(vals)
std::move(vals);
// vs
ZXSHADY_MOV(vals);
}
it also has is_explicitly_default_constructible<T>
,is_explicitly_copy_constructible<T>
and is_explicitly_move_constructible<T>
using namespace zxshady::tmp;
struct A {
explicit A(int,int);
/*implicit*/ A(int,int,int);
};
is_explicitly_constructible<A,int,int>::value; // true
is_explicitly_constructible<A,int,int,int>::value; // false
is_explicitly_constructible<A,char*>::value; // false
the opposite of is_explicitly_constructible<T,Args...> it checks whether the type is implictly constructible
it also has is_implicitly_default_constructible<T>
,is_implicitly_copy_constructible<T>
and is_implicitly_move_constructible<T>
using namespace zxshady::tmp;
struct A {
explicit A(int,int);
/*implicit*/ A(int,int,int);
};
is_implicitly_constructible<A,int,int>::value; // false
is_implicitly_constructible<A,int,int,int>::value; // true
is_implicitly_constructible<A,char*>::value; // false
a container that contains types.
size
a static constant equal to `sizeof...(Types)`using List = type_list<int,char,long>;
static_assert(List::size == 3);
is_empty
a static boolean constant equal to size == 0using List = type_list<int,char,long>;
using List2 = type_list<>;
static_assert(!List::is_empty);
static_assert(List2::is_empty);
at<Index>
index into the types from the list. if the index is out of bounds a `static_assert` will fire.using List = type_list<int,char,long>;
static_assert(std::is_same_v<char,List::template at<1>>);
at_or<Index,OrType>
index into the types from the list. if the index is out of bounds then returns the OrType instead of failing.using List = type_list<int,char,long>;
static_assert(std::is_same_v<long,List::template at_or<2,void>>);
static_assert(std::is_same_v<void,List::template at_or<5,void>>);
front
equal to `List::template at<0>`note this member does not exist if is_empty is true
using List = type_list<int,char,long>;
static_assert(std::is_same_v<int,List::front>);
back
equal to `List::template at<size-1>`note this member does not exist if is_empty is true
using List = type_list<int,char,long>;
static_assert(std::is_same_v<long,List::back>);
operator+
does concatenation of 2 listsusing List1 = type_list<int[1],int[2]>;
using List2 = type_list<int[3],int[4]>;
static_assert(std::is_same_v<decltype(List1{} + List2{}),type_list>int[1],int[2],int[3],int[4]<>);
reverse
reverses the listusing List = type_list<int,char,long>;
using NewList = List::reverse;
static_assert(std::is_same_v<long,NewList::back>);
static_assert(std::is_same_v<int,NewList::front>);
pop_front
pops the first type in the listusing List = type_list<int,char,unsigned int,long>;
using NewList = List::pop_front;
static_assert(std::is_same_v<char,NewList::front>);
pop_back
pops the last type in the listusing List = type_list<int,char,unsigned int,long>;
using NewList = List::pop_back;
static_assert(std::is_same_v<unsigned int,NewList::back>);
transform<TransformTrait>
apply the TransformTrait to every type in the list,using List = type_list<int,char,long>;
static_assert(std::is_same_v<List::template transform<std::add_const>,type_list<const int,const char,const long> >);
rename_to<Template>
using List = type_list<int,custom_allocator<int>>;
static_assert(std::is_same_v<List::template rename_to<std::vector>,std::vector<int,custom_allocator<int>> >);
insert<Index,Types...>
using List = type_list<int,char,long>;
static_assert(std::is_same_v<List::template insert<1,unsigned int,void>,type_list<int,char,unsigned int ,void,long> >);
erase<Index>
using List = type_list<int,void,char>;
static_assert(std::is_same_v<List::template erase<1>,type_list<int,char> >);
erase_if<Predicate>
using List = type_list<int,const void,char,const long>;
static_assert(std::is_same_v<List::template erase_if<std::is_const>,type_list<int,char> >);
slice<Begin,End = size>
using List = type_list<int[1],int[2],int[3],int[4]>;
static_assert(std::is_same_v<List::template slice<0>,type_list<int[1],int[2],int[3],int[4]>>);
static_assert(std::is_same_v<List::template slice<1>,type_list<int[2],int[3],int[4]>>);
static_assert(std::is_same_v<List::template slice<1,2>,type_list<int[1],int[4]>>);
static_assert(std::is_same_v<List::template slice<4>>,type_list<>>);
drop_first<Count>
using List = type_list<int[1],int[2],int[3],int[4]>;
static_assert(std::is_same_v<List::template drop_first<0>,type_list<int[1],int[2],int[3],int[4]>>);
static_assert(std::is_same_v<List::template drop_first<1>,type_list<int[2],int[3],int[4]>>);
static_assert(std::is_same_v<List::template drop_first<2>,type_list<int[3],int[4]>>);
static_assert(std::is_same_v<List::template drop_first<4>>,type_list<>>);
drop_last<Count>
using List = type_list<int[1],int[2],int[3],int[4]>;
static_assert(std::is_same_v<List::template drop_last<0>,type_list<int[1],int[2],int[3],int[4]>>);
static_assert(std::is_same_v<List::template drop_last<1>,type_list<int[1],int[2],int[3]>>);
static_assert(std::is_same_v<List::template drop_last<2>,type_list<int[1],int[2]>>);
static_assert(std::is_same_v<List::template drop_last<4>>,type_list<>>);
replace_at<Index,T>
using List = type_list<int,char,long>;
static_assert(std::is_same_v<List::template replace_at<0,void>, type_list<void,char,long>>);
static_assert(std::is_same_v<List::template replace_at<1,void>, type_list<int,void,long>>);
static_assert(std::is_same_v<List::template replace_at<2,void>, type_list<int,char,void>>);
find_if<Predicate,Pos = 0>
*note* returns std::size_t(-1) on not foundusing List = type_list<int,char,const long,volatile void>;
static_assert(find_if<std::is_const>(List{}) == 2);
static_assert(find_if<std::is_void>(List{}) == 3);
find<T,Pos = 0>
using List = type_list<int,char,long>;
static_assert(find<void>(List{}) == std::size_t(-1));
static_assert(find<char,0>(List{}) == 1);
type_list_repeat_n<N,T>
returns a type_list with N Ts in itstatic_assert(std::is_same_v<type_list_repeat_n<5, int>, type_list<int, int, int, int, int>>);
static_assert(std::is_same_v<type_list_repeat_n<0, int>, type_list<>>);
static_assert(std::is_same_v<type_list_repeat_n<1, int>, type_list<int>>);
there is still more methods.
documentation incomplete...