This is a little collection of various C++ utilities that I frequently use and I don't want to duplicate in every project anymore. It is composed only by header files that can be included as is, and compiled both in C++11 and C++14.
I've tested the code mainly in clang, but it should work also on recent versions of g++. Please let me know if this is not true.
This header declares a utility trait to check if a class possess a member function that can be called with a given signature. Actually, the header declares a macro that generate the trait for each member name that one wants to check.
The macro is called DECLARE_HAS_MEMBER_TRAIT
and given a name,
e.g. get
, it generates a trait class called, for instance
has_member_get
.
The trait wants a signature as the template parameter and provides a static
boolean constant value
which will be true if a member with the given
name exists and is callable with the given signature.
The macro also declares two tag struct
s called
has_member_<name>_tag
and doesnt_have_<name>_tag
.
The trait provides a typedef type
which can be one of the two tag
structs, useful for calling different implementation of a function with tag
dispatching.
This header provides a few functions that are useful when writing function templates need to be enabled only when complex conditions are met.
The REQUIRES
macro encapsulates the common idiom of using
std::enable_if
, but providing extra convenience.
It is used as follows:
template<typename T, REQUIRES(std::is_integral<T>())>
T sum(T v1, T v2) { return v1 + v2; }
The macro accepts a list of requirements that have to be fulfilled all at the same time to enable the instantiation of the function template.
The first thing you can notice is that I've not used the ::value
member of the type trait, but I've instead instantiated the type and passed
the instance to the macro. This is because the REQUIRES
macro is
built to accept directly instances of types that have a value
member,
to avoid the need to manually type it.
It also accepts directly booleans, so you can mix different types of requirements. The fact that it accepts a list of requirements, instead of a single one, makes very easy to check the same property on all the elements of a variadic pack.
Here follows an example that shows a combination of the available options:
template<typename ...Args,
REQUIRES(sizeof...(Args) % 2 == 0, std::is_integral<Args>()...)>
void func(Args ...args) { ... }
Here, the function is enabled only if the number of arguments supplied is even, and if their types are all integral types. You see that applying a condition on all the elements of a pack is as simple as adding an ellipsis.
Another commonly useful example of this kind is the implementation of a function that takes a variable number of arguments, but of fixed type. In C++ there is currently no way to directly constraint the type of the elements of a variadic pack, so you have to rely on SFINAE, but the amount of code required to implement this seemingly simple task is ridiculous. Here is how easily it can be done with this facility. Suppose we want a function that concatenates an arbitrary number of strings:
template<typename ...String,
REQUIRES(std::is_same<String, std::string>()...)>
std::string concatenate(String s, String ...args) {
return s + concatenate(args...);
}
(yes, they should be passed by forwarding reference, but who cares now)
Multiple macro invocations can be used in the same template, as long as they're
put on different lines. This is because the macro internally uses the
__LINE__
preprocessor symbol, to be able to implement the following
feature.
When using SFINAE to disable some function overload, the condition of
enable_if
, in order to work, has to depend on one of the template
parameters of the function itself. Otherwise, the condition expression is
expanded at the first phase of the template parsing (rather than before
the instantiation) and the substitution failure results in an error.
The REQUIRES
macro, on the other hand, can always be used with
non type-dependent conditions as well, as shown in the following example:
template<typename T>
class example {
public:
/*
* This doesn't work. It triggers an hard error if T is not integral,
* because the condition is not type-dependent
*/
template<typename U,
typename std::enable_if<std::is_integral<T>::value>::type>
U member(U u) { return u; }
/*
* This works as expected, even if the condition doesn't depend on the
* template parameter
*/
template<typename U, REQUIRES(std::is_integral<T>())>
U member(U u) { return u; }
};
The header provides also a couple of functions, useful to compose complex SFINAE conditions. These
are all()
and any()
, that accept a variable number of constraints (again, both booleans
or instances of type traits), and return true respectively if all are fullfilled (which is implicit
in the REQUIRES
macro anyway), or if at least one of them is fulfulled.
The last element is the neg()
function, which negates a constraint condition. This is useful
to negate type traits, since you can't type, e.g., !std::is_integral<T>()
, because the type
doesn't provide an overloaded not operator, but you can then type neg(std::is_integral<T>())
.
-
The
void_t
type trait is very simply a template typedef that always corresponds tovoid
, no matter what you pass as the argument. The usefulness of this little utility in a lot of template metaprogramming techniques is well known. See for example, http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3911.pdf -
The
true_t
type trait is similar tovoid_t
, but is a value type trait that always returntrue
, no matter what.
This header provides two utilities that are useful when dealign with Callable objects, as defined by the standard.
The invoke()
function takes a callable object and any number of arguments
and invoke it with them, following the definition of the Callable concept from
the C++ Standard. This means you can call functions, function objects,
pointers to member functions, and pointers to data members, with a uniform
syntax. Moreover, you can invoke pointers to data members and pointers to
member functions using a reference, a pointer or any smart pointer as the instance.
The invokable()
function builds on invoke()
and wraps any Callable
into a function object, which is useful as an alternative to std::bind
to
conveniently use pointers to data members or pointers to member functions.
A little example:
struct S {
int member = 42;
};
auto f = invokable(&S::member);
S s;
std::cout << f(s) << std::endl; // Prints '42'
The headers in the std14/
subdirectory provide a replacement for some of
the missing pieces of the C++11 standard library that have been added in the
recently published C++14 standard. Of all the things, I've added mostly only those
things that could have been directly added without modifying existing entities
(for example, there's not an entirely rewritten std::tuple
library only
to add constexpr
member functions, but there is a constexpr
enabled
std::array
). Of all of them, I've selected what I think are the must-haves.
Each header under std14/
subdirectory is one-to-one with a standard
header, and includes the standard one. All the declarations are enclosed in a
namespace called std14
, because it's forbidden to add things to
namespace std
. For convenience, the entire std
namespace is
imported inside it. However, when you compile your project in C++14 mode
(because in the future you'll want to), all the declarations disappear and
namespace std14
becomes an alias for std
, so you can use the
library-provided symbols out-of-the-box.
Provided entities are:
std::make_unique
in the<std14/memory>
headerconstexpr
enabledstd::array
in<std14/array>
- Type traits aliases (e.g.
enable_if_t<>
instead ofenable_if<>
) and SFINAE-friendlystd::result_of
in<std14/type_traits>
std::integer_sequence
and related facilities in the<std14/utility>
header.std::experimental::string_view
in the<std14/experimental/string_view>
header. It's a fairly accurate implementation of string_view from the Library Foundamentals TS, even if I've not tested anything for compliance with the actual specification (and I've left out some boring bits).std::experimental::array_view
, in<std14/experimental/array_view>
is a simplistic analogue ofllvm::ArrayRef
, or of the upcomingarray_view
standard proposal, but only a simple unidimensional view, without all the fancy multidimensional stuff.