How to Implement Concepts in C++17 - c++

I found this great article on how to implement Concepts in C++14. In one section about how to make a compiles checker he gives the following:
template <typename ... Ts>
using void_t = void;
template <typename T, template <typename> class Expression, typename AlwaysVoid = void_t<>>
struct compiles : std::false_type {};
template <typename T, template <typename> class Expression>
struct compiles<T, Expression, void_t<Expression<T>>> : std::true_type {};
The above code will check if an expression compiles, but has no checks on the return type. Later on he mentioned that a compiles_convertible_type and compiles_same_type checker can be created by wrapping the compiles trait but doesn't give an example of how to do that, stating that it is simple. However, I am somewhat new to SFINAE so I'm not sure exactly what to do here.
template <typename T, typename Result, template <typename> class Expression>
struct compiles_convertible_type : /* some invocation of compiles<> trait here */
template <typename T, typename Result, template <typename> class Expression>
struct compiles_same_type : /* some invocation of compiles<> trait here */
For reference, this is what I have tried, but it returns true for everything. I think because the is_same and is_convertible expressions compile.
template <typename T, typename Result, template <typename> class Expression>
struct compiles_convertible_type :
compiles<T, Expression, void_t<std::is_convertible<Result, std::result_of<Expression<T>>>>> {};
template <typename T, typename Result, template <typename> class Expression>
struct compiles_same_type :
compiles<T, Expression, void_t<std::is_same<Result, std::result_of<Expression<T>>>>> {};
namespace memory {
struct memory_block{};
}
struct MyAllocator {
memory::memory_block allocate_block(){return {};};
void deallocate_block(memory::memory_block){};
std::size_t next_block_size() const {return 0;};
};
struct MyBadAllocator {
memory::memory_block allocate_block(){return {};};
void deallocate_block(memory::memory_block){};
void next_block_size() const {};
};
template <typename T>
struct BlockAllocator_impl
{
template <class Allocator>
using allocate_block = decltype(std::declval<Allocator>().allocate_block());
template <class Allocator>
using deallocate_block = decltype(std::declval<Allocator>().deallocate_block(std::declval<memory::memory_block>()));
template <class Allocator>
using next_block_size = decltype(std::declval<const Allocator>().next_block_size());
using result = std::conjunction<
compiles_convertible_type<T, memory::memory_block, allocate_block>,
compiles<T, deallocate_block>,
compiles_same_type<T, std::size_t, next_block_size>
>;
using has_allocate_block = compiles_convertible_type<T, memory::memory_block, allocate_block>;
using has_deallocate_block = compiles<T, deallocate_block>;
using has_next_block_size = compiles_same_type<T, std::size_t, next_block_size>;
};
template <typename T>
using BlockAllocator = typename BlockAllocator_impl<T>::result;
template <typename T>
using BlockAllocatorAllocate = typename BlockAllocator_impl<T>::has_allocate_block;
template <typename T>
using BlockAllocatorDeallocate = typename BlockAllocator_impl<T>::has_deallocate_block;
template <typename T>
using BlockAllocatorNextBlockSize = typename BlockAllocator_impl<T>::has_next_block_size;
#include <fmt/core.h>
int main()
{
fmt::print("MyBadAllocator\n");
fmt::print("has allocate: {}\n", BlockAllocatorAllocate<MyBadAllocator>::value);
fmt::print("has deallocate: {}\n", BlockAllocatorDeallocate<MyBadAllocator>::value);
fmt::print("has next block size: {}\n", BlockAllocatorNextBlockSize<MyBadAllocator>::value);
fmt::print("Is BlockAllocator: {}\n", BlockAllocator<MyBadAllocator>::value);
fmt::print("MyAllocator\n");
fmt::print("has allocate: {}\n", BlockAllocatorAllocate<MyAllocator>::value);
fmt::print("has deallocate: {}\n", BlockAllocatorDeallocate<MyAllocator>::value);
fmt::print("has next block size: {}\n", BlockAllocatorNextBlockSize<MyAllocator>::value);
fmt::print("Is BlockAllocator: {}\n", BlockAllocator<MyAllocator>::value);
}
output:
MyBadAllocator
has allocate: true
has deallocate: true
has next block size: true // expect false
Is BlockAllocator: true // expect false
MyAllocator
has allocate: true
has deallocate: true
has next block size: true
Is BlockAllocator: true

The following implementations give exactly the expected output (and probably those are close to what the author of the article used behind the scenes):
template <typename T, typename Result, template <typename> class Expression, typename AlwaysVoid = void_t<>>
struct compiles_same_type : std::false_type {};
template <typename T, typename Result, template <typename> class Expression>
struct compiles_same_type<T, Result, Expression, void_t<Expression<T>>> : std::is_same<Result, Expression<T>> {};
template <typename T, typename Result, template <typename> class Expression, typename AlwaysVoid = void_t<>>
struct compiles_convertible_type : std::false_type {};
template <typename T, typename Result, template <typename> class Expression>
struct compiles_convertible_type<T, Result, Expression, void_t<Expression<T>>> : std::is_convertible<Result, Expression<T>> {};
The trick is to replace the inherited std::true_type on the template specialization selected by SFINAE when the expression is valid with other struct/class that does the desired type check (aka a type trait in C++'s standard library terminology). Of course you should modify the template parameter list of the initial compiles type with the extra parameters needed by the inherited type trait (in those cases only Result is needed as both std::is_same and std::is_convertible have only two template type parameters).
In addition, the std::result_of is both unnecessary and wrong. std::is_same will check if the value of Result type parameter is something staring with std::result_of rather than the actual type of the expression. You probably would want something like std::result_of<...>::type, but that will be an invalid as Expression<T> is not a callable type in any usage from the BlockAllocator example. You should keep in mind that the actual value of Expression is already the resulted type and not the expression itself:
template <class Allocator>
using allocate_block = decltype(std::declval<Allocator>().allocate_block());
you should interpret that as "the resulted type of the expression a.allocate_block() where a has Allocator type.
If you want to see more information about std::result_of then cppreference is a good place to start: https://en.cppreference.com/w/cpp/types/result_of

Related

How to constrain class template by disabling type argument of specialization itself, and why does(n't) it work?

Is it currently possible to constrain a class template that rejects type argument which is the specialization of the class template itself without using static_assert?
Since I cannot use requires expression to check if it is a valid typename, I have to create a class template instantiation validator that checks whether the class template passed is valid with template arguments:
template <template <typename...> typename Temp, typename... Ts>
requires requires { typename Temp<Ts...>; }
constexpr bool is_valid() { return true; }
template <template <typename...> typename, typename...>
constexpr bool is_valid() { return false; }
template <template <typename...> typename Temp, typename... Ts>
concept valid_instantiation = is_valid<Temp, Ts...>();
Since failed static_assert emits a hard error, just like this one:
template <typename>
class Hello;
template <typename>
inline constexpr bool is_hello_v = false;
template <typename T>
inline constexpr bool is_hello_v<Hello<T>> = true;
template <typename T>
class Hello {
static_assert(!is_hello_v<T>);
};
static_assert(valid_instantiation<Hello, int>);
static_assert(!valid_instantiation<Hello, Hello<int>>);
The second static assertion sure didn't compile unless I remove that ! that returns true which is not what I expected.
What I want to have is to silent the error and replace the static_assert, so that:
static_assert(valid_instantiation<Hello, int>);
static_assert(!valid_instantiation<Hello, Hello<int>>);
can be valid.
For the first static assertion, the Hello<int> instantiation is accepted just fine, while the second static assertion, Hello<Hello<int>> instantiation should be rejected because the template argument passed is the instantiation of the class template itself but I have no knowledge what constraints I'll be using to achieve these.
It's ok if it is impossible to do so, or otherwise.
Not sure it is what you want, but
template <typename T> struct is_Hello;
template <typename T> requires (!is_Hello<T>::value) class Hello;
template <typename T> struct is_Hello : std::false_type{};
template <typename T> struct is_Hello<Hello<T>> : std::true_type{};
template <typename T>
requires (!is_Hello<T>::value) // So Hello<Hello<T>> is not possible
class Hello
{
// ...
};
Not sure how you want to SFINAE or test it (the trait seems equivalent), as Hello<Hello<T>> cannot exist.
Demo

Short way to constrain template parameter without boiler plate code for a struct

consider this example:
#include <iostream>
template <typename T, std::size_t I>
struct Foo
{
};
template <typename T>
struct specializes_foo : std::false_type {};
template <typename T, std::size_t I>
struct specializes_foo<Foo<T, I>> : std::true_type {};
template <typename T>
concept foo = specializes_foo<T>::value;
int main()
{
std::cout << foo<int> << "\n";
std::cout << foo<Foo<int,3>> << "\n";
}
is there a shorter way in C++20? I know that you could do a concept for "specializes template", e.g.:
// checks if a type T specializes a given template class template_class
// note: This only works with typename template parameters.
// e.g.: specializes<std::array<int, 3>, std::array> will yield a compilation error.
// workaround: add a specialization for std::array.
namespace specializes_impl
{
template <typename T, template <typename...> typename>
struct is_specialization_of : std::false_type {};
template <typename... Args, template <typename...> typename template_class>
struct is_specialization_of<template_class<Args...>, template_class> : std::true_type {};
}
template <typename T, template <typename...> typename template_class>
inline constexpr bool is_specialization_of_v = specializes_impl::is_specialization_of<T,template_class>::value;
template <typename T, template <typename...> typename template_class>
using is_specialization_of_t = specializes_impl::is_specialization_of<T,template_class>::type;
template<typename T, template <typename...> typename template_class>
concept specializes = is_specialization_of_v<T, template_class>;
However, this fails with non-type template parameters such as "size_t". Is there a way so I could, for example just write:
void test(Foo auto xy);
I know that since C++20 there came a couple of other ways to constrain template parameters of a function, however, is there a short way to say "I dont care how it specializes the struct as long as it does"?
Nope.
There's no way to write a generic is_specialization_of that works for both templates with all type parameters and stuff like std::array (i.e. your Foo), because there's no way to write a template that takes a parameter of any kind.
There's a proposal for a language feature that would allow that (P1985), but it's still just a proposal and it won't be in C++23. But it directly allows for a solution for this. Likewise, the reflection proposal (P1240) would allow for a way to do this (except that you'd have to spell the concept Specializes<^Foo> instead of Specializes<Foo>).
Until one of these things happens, you're either writing a bespoke concept for your template, or rewriting your template to only take type parameters.

Template template class predicate not working in partial specialization

I have many EnableIf traits that basically check whether the input type satisfies an interface. I was trying to create a generic Resolve trait that can be used to transform those into a boolean trait.
Something like this - https://wandbox.org/permlink/ydEMyErOoaOa60Jx
template <
template <typename...> class Predicate,
typename T,
typename = std::void_t<>>
struct Resolve : std::false_type {};
template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, Predicate<T>> : std::true_type {};
Now if you have an EnableIf trait like so
template <typename T>
using EnableIfHasFoo = std::void_t<decltype(std::declval<T>().foo())>;
You can create a boolean version of that very quickly
template <typename T>
struct HasFoo : Resolve<EnableIfHasFoo, T> {};
Or the analogous variable template.
But for some reason the partial specialization is not working as expected. Resolve does not work as intended. See the output here - https://wandbox.org/permlink/ydEMyErOoaOa60Jx. The same thing implemented "manually" works - https://wandbox.org/permlink/fmcFT3kLSqyiBprm
I am resorting to manually defining the types myself. Is there a detail with partial specializations and template template arguments that I am missing?
I cannot find the exact reason why your example don't work. If you want to dig more into the details of std::void_t, here's an interesting explanation
Even if I cannot explain it in depth, I would like to add another reliable syntax that is used in the detection idiom.
template<
template <typename...> class Predicate,
typename T,
typename = void>
struct Resolve : std::false_type {};
template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, std::void_t<Predicate<T>>> : std::true_type {};
template <typename T>
using EnableIfHasFoo = decltype(std::declval<T>().foo());
live on compiler explorer
The reason why your approach will fail is that Predicate<T>> in the third template parameter is not a non-deduced context. This causes the deduction to directly fail (see [temp.alias]/2), instead of using the deduced template arguments from elsewhere as in a non-deduced context.
You can wrap your Predicate<T>> to a non-deduced context to make it work:
template<class T>
struct identity {
using type = T;
};
template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, typename identity<Predicate<T>>::type> : std::true_type {};
Live Demo
Because inside a non-deduced context, the deduction won't happen for Predicate<T> part, instead, it will use the Predicate and T obtained from elsewhere.
As for why the usual detection-idiom (see Guillaume Racicot's answer) will work, it is because std::void_t as a template alias, will be replaced by void in deduction phase (see [temp.alias]/2), thus no deduction will happen.
Here are some examples to illustrate it more clearly:
template<class T>
using always_int = int;
template<template<class> class TT>
struct deductor {};
template<template<class> class TT, class T>
void foo(T, deductor<TT>) {}
template<template<class> class TT, class T>
void bar(T, deductor<TT>, TT<T>) {}
template<class T>
void baz(T, always_int<T>) {}
int main() {
// ok, both T and TT are deduced
foo(0, deductor<always_int>{});
// ERROR, TT<T> is NOT a non-deduced context, deduction failure
bar(0, deductor<always_int>{}, 0);
// ok, T is deduced, always_int<T> is replaced by int so no deduction
baz(0, 0);
}

Default parameter in template -> template argument involves template parameter

I have a problem similar to this one: SFINAE tried with bool gives compiler error: "template argument ‘T::value’ involves template parameter"
I want to define a trait that tells if a complex representation is an array of structs, where real values are never AoS hence no user defined specilization should be required, but for complex you'd always need one:
/**
* Evaluates to a true type if the given complex type is an Array of Structs, false otherwise
* Defaults to false for Real values
*/
template< typename T, bool T_isComplex = IsComplex<T>::value >
struct IsAoS: std::false_type{};
/**
* Undefined for (unknown) complex types
*/
template< typename T >
struct IsAoS< T, true >;
Specializations are done in the std-way by deriving from std::true/false_type.
The problem is: With this implementation I get the "template argument involves template parameter(s)" error (which is explained in the linked question) but if i switch to types (ommit "::value" in first template and change true to true_type in 2nd) Complex types won't match the 2nd template anymore because the only derive from std::true_type and ARE NOT std::true_type.
Only solution I can think of is using expression SFINAE and change the 2nd parameter of the main template to default void and an enable_if for the real case (isComplex==false). Anything better?
On the assumption that IsAoS<T>'s desired behavior is:
If IsComplex<T>::value is false, it defaults to false_type;
Otherwise, it is an error unless the user provides a specialization.
It's straightforward to implement with a static_assert; no default template argument needed:
template< typename T >
struct IsAoS: std::false_type {
static_assert(!IsComplex<T>::value, "A user specialization must be provided for Complex types");
};
If you want the default argument trickery, just use a wrapper layer:
namespace detail {
template< typename T, bool T_isComplex = IsComplex<T>::value >
struct IsAoS_impl: std::false_type{};
/**
* Undefined for (unknown) complex types
*/
template< typename T >
struct IsAoS_impl< T, true >;
}
template<class T> struct IsAoS : detail::IsAoS_impl<T> {};
Users should just specialize IsAoS.
If I understand correctly, you want:
template<class> struct IsComplex_impl {using type = std::false_type;};
template<class T> struct IsComplex_impl<std::complex<T>> {using type = std::true_type;};
template <typename T>
using IsComplex = typename IsComplex_impl<T>::type;
// Declaration only
template<typename T, typename T_isComplex = IsComplex<T>>
struct IsAoS;
// general case
template< typename T >
struct IsAoS< T, std::false_type >: std::false_type{};
// specialization for complex<double>
template<>
struct IsAoS< std::complex<double>>: std::true_type{};
Live Demo
or with same signature:
template<class> struct IsComplex_impl : std::false_type {};
template<class T> struct IsComplex_impl<std::complex<T>> : std::true_type {};
template <typename T>
constexpr bool IsComplex() {return IsComplex_impl<T>::value;}
// Declaration only
template<typename T, bool T_isComplex = IsComplex<T>()>
struct IsAoS;
// general case
template< typename T >
struct IsAoS< T, false>: std::false_type{};
// specialization for complex<double>
template<>
struct IsAoS< std::complex<double>>: std::true_type{};
Live Demo

Currying for templates in C++ metaprogramming

This is more of a conceptual question. I'm trying to find the easiest way of converting a two-arg template (the arguments being types) into a one-arg template. I.e., binding one of the types.
This would be the meta-programming equivalent of bind in boost/std. My example includes a possible use-case, which is, passing std::is_same as template argument to a template that takes a one-arg template template argument (std::is_same being a two-arg template), i.e. to TypeList::FindIf. The TypeList is not fully implemented here, neither is FindIf, but you get the idea. It takes a "unary predicate" and returns the type for which that predicate is true, or void if not such type.
I have 2 working variants but the first is not a one-liner and the 2nd uses a rather verbose BindFirst contraption, that would not work for non-type template arguments. Is there a simple way to write such a one-liner? I believe the procedure I'm looking for is called currying.
#include <iostream>
template<template<typename, typename> class Function, typename FirstArg>
struct BindFirst
{
template<typename SecondArg>
using Result = Function<FirstArg, SecondArg>;
};
//template<typename Type> using IsInt = BindFirst<_EqualTypes, int>::Result<Type>;
template<typename Type> using IsInt = std::is_same<int, Type>;
struct TypeList
{
template<template<typename> class Predicate>
struct FindIf
{
// this needs to be implemented, return void for now
typedef void Result;
};
};
int main()
{
static_assert(IsInt<int>::value, "");
static_assert(!IsInt<float>::value, "");
// variant #1: using the predefined parameterized type alias as predicate
typedef TypeList::FindIf<IsInt>::Result Result1;
// variant #2: one-liner, using BindFirst and std::is_same directly
typedef TypeList::FindIf< BindFirst<std::is_same, int>::Result>::Result Result2;
// variant #3: one-liner, using currying?
//typedef TypeList::FindIf<std::is_same<int, _>>::Result Result2;
return 0;
}
Click here for code in online compiler GodBolt.
I think the typical way of doing this is keep everything in the world of types. Don't take template templates - they're messy. Let's write a metafunction named ApplyAnInt that will take a "metafunction class" and apply int to it:
template <typename Func>
struct ApplyAnInt {
using type = typename Func::template apply<int>;
};
Where a simple metafunction class might be just checking if the given type is an int:
struct IsInt {
template <typename T>
using apply = std::is_same<T, int>;
};
static_assert(ApplyAnInt<IsInt>::type::value, "");
Now the goal is to support:
static_assert(ApplyAnInt<std::is_same<_, int>>::type::value, "");
We can do that. We're going to call types that contain _ "lambda expressions", and write a metafunction called lambda which will either forward a metafunction class that isn't a lambda expression, or produce a new metafunction if it is:
template <typename T, typename = void>
struct lambda {
using type = T;
};
template <typename T>
struct lambda<T, std::enable_if_t<is_lambda_expr<T>::value>>
{
struct type {
template <typename U>
using apply = typename apply_lambda<T, U>::type;
};
};
template <typename T>
using lambda_t = typename lambda<T>::type;
So we update our original metafunction:
template <typename Func>
struct ApplyAnInt
{
using type = typename lambda_t<Func>::template apply<int>;
};
Now that leaves two things: we need is_lambda_expr and apply_lambda. Those actually aren't so bad at all. For the former, we'll see if it's an instantiation of a class template in which one of the types is _:
template <typename T>
struct is_lambda_expr : std::false_type { };
template <template <typename...> class C, typename... Ts>
struct is_lambda_expr<C<Ts...>> : contains_type<_, Ts...> { };
And for apply_lambda, we just will substitute the _ with the given type:
template <typename T, typename U>
struct apply_lambda;
template <template <typename...> class C, typename... Ts, typename U>
struct apply_lambda<C<Ts...>, U> {
using type = typename C<std::conditional_t<std::is_same<Ts, _>::value, U, Ts>...>::type;
};
And that's all you need actually. I'll leave extending this out to support arg_<N> as an exercise to the reader.
Yeah, I had this issue to. It took a few iterations to figure out a decent way to do this. Basically, to do this, we need to specify a reasonable representation of what we want and need. I borrowed some aspects from std::bind() in that I want to specify the template that I wish to bind and the parameters that I want to bind to it. Then, within that type, there should be a template that will allow you to pass a set of types.
So our interface will look like this:
template <template <typename...> class OP, typename...Ts>
struct tbind;
Now our implementation will have those parameters plus a container of types that will be applied at the end:
template <template <typename...> class OP, typename PARAMS, typename...Ts>
struct tbind_impl;
Our base case will give us a template type, which I'll call ttype, that'll return a template of the contained types:
template <template <typename...> class OP, typename...Ss>
struct tbind_impl<OP, std::tuple<Ss...>>
{
template<typename...Us>
using ttype = OP<Ss...>;
};
Then we have the case of moving the next type into the container and having ttype refer to the ttype in the slightly simpler base case:
template <template <typename...> class OP, typename T, typename...Ts, typename...Ss>
struct tbind_impl<OP, std::tuple<Ss...>, T, Ts...>
{
template<typename...Us>
using ttype = typename tbind_impl<
OP
, std::tuple<Ss..., T>
, Ts...
>::template ttype<Us...>;
};
And finally, we need a remap of the templates that will be passed to ttype:
template <template <typename...> class OP, size_t I, typename...Ts, typename...Ss>
struct tbind_impl<OP, std::tuple<Ss...>, std::integral_constant<size_t, I>, Ts...>
{
template<typename...Us>
using ttype = typename tbind_impl<
OP
, typename std::tuple<
Ss...
, typename std::tuple_element<
I
, typename std::tuple<Us...>
>::type
>
, Ts...
>::template ttype<Us...>;
Now, since programmers are lazy, and don't want to type std::integral_constant<size_t, N> for each parameter to remap, we specify some aliases:
using t0 = std::integral_constant<size_t, 0>;
using t1 = std::integral_constant<size_t, 1>;
using t2 = std::integral_constant<size_t, 2>;
...
Oh, almost forgot the implementation of our interface:
template <template <typename...> class OP, typename...Ts>
struct tbind : detail::tbind_impl<OP, std::tuple<>, Ts...>
{};
Note that tbind_impl was placed in a detail namespace.
And voila, tbind!
Unfortunately, there is a defect prior to c++17. If you pass tbind<parms>::ttype to a template that expects a template with a particular number of parameters, you will get an error as the number of parameters don't match (specific number doesn't match any number). This complicates things slightly requiring an additional level of indirection. :(
template <template <typename...> class OP, size_t N>
struct any_to_specific;
template <template <typename...> class OP>
struct any_to_specific<OP, 1>
{
template <typename T0>
using ttype = OP<T0>;
};
template <template <typename...> class OP>
struct any_to_specific<OP, 2>
{
template <typename T0, typename T1>
using ttype = OP<T0, T1>;
};
...
Using that to wrap tbind will force the compiler to recognize the template having the specified number of parameters.
Example usage:
static_assert(!tbind<std::is_same, float, t0>::ttype<int>::value, "failed");
static_assert( tbind<std::is_same, int , t0>::ttype<int>::value, "failed");
static_assert(!any_to_specific<
tbind<std::is_same, float, t0>::ttype
, 1
>::ttype<int>::value, "failed");
static_assert( any_to_specific<
tbind<std::is_same, int , t0>::ttype
, 1
>::ttype<int>::value, "failed");
All of which succeed.