Catching functors using SFINAE in structure partial specialisation - c++

For some complicated reason, I want to convert any supported type T (coming from a template) to a list of types I have chosen. For this, I tried using a template structure named "Convert". For example:
Convert<short>::type should be int
Convert<int>::type should be int
Convert<char>::type should be int
Convert<float>::type should be double
Convert<double>::type should be double
Convert<const char*>::type should be std::string
Convert<std::string>::type should be std::string
etc.
Those above are easy to implement using template specialisation. But there is one case that is causing problems :
Convert<T>::type where T is a functor should be T
To handle this, I think I have to use SFINAE but I can't manage to make it compile.
The code below gives me "partial specialization cannot match argument list for primary template" (ie. writing "Convert" is forbidden) :
template<typename T, typename = decltype(&T::operator())>
struct Convert<T> { typedef T type; };
And this one gives me "template parameter not used or deducible in partial specialization" (ie. it thinks that T is not used) :
template<typename T>
struct Convert<typename std::enable_if<std::is_function<typename T::operator()>::value,T>::type>
{ typedef T type; };
I have no idea of what to do, all my attempts result in one of the two errors above.
EDIT: I would like to catch other generic things using the same model, so I can't just write "typedef T type" in the non-specialized structure.
Thanks

I'd suggest you use the first approach, but to make it work, you'll have to
Declare the master template with one unused template-argument:
template <class T, class = void> Convert;
Add a void parameter to all specializations of the template you use now.
Define your "functor specialization" like this:
template<typename T, typename std::enable_if<std::is_function<typename T::operator()>::value,void>::type>
That means you make the second argument void if it is a functor (so it matches the default template argument) or nonexisting if it isn't.
BTW why do you use typename in typename T::operator()? AFAIK, operator() is not a type.

Related

How to get at a C++ Container<T>'s T if no Container::value_type is provided?

It's rather common that container templates contain a value_type typedef. This makes it easy to create other templated code, most recently concepts, which are able to extract the T if only having been given Container and not Container<T>.
However, not all containers (or other templated classes) define such a value_type, especially older ones.
Is it possible to get to the contained T even without it?
I know there are tricks like "if it is an iterator then .begin() should return a value of that type", but that does not help us to e.g. write a concept-requirement that checks whether a class's .begin() indeed follows the requirements that iterators have.
Here's a solution that's similar to Quimby's solution but using partial template specialization instead:
template<typename...>
struct inner_type_impl;
template<template<typename...> typename C, typename T, typename...Args>
struct inner_type_impl<C<T,Args...>>
{
using type = T;
};
template<typename T>
using inner_type = typename inner_type_impl<T>::type;
Here's a demo that's borrowed from Quimby's solution.
Class template specialization or template argument deduction can be used to implement this. Something like the following should work as long as the inner type is the first template argument:
#include <type_traits>
// Consider the first template argument to be the inner type.
template<template<typename,typename...>class C,typename T,typename...Args>
auto inner_type_impl(const C<T,Args...>* v)->T*{return nullptr;};
template<typename T>
using inner_type = std::remove_pointer_t<decltype(inner_type_impl((T*)nullptr))>;
template<typename T>
struct Container;
// Will still deduce T
template<typename T,typename...Extras>
struct ContainerExtraParams;
static_assert(std::is_same_v<int,inner_type<Container<int>>>);
static_assert(std::is_same_v<int,inner_type<ContainerExtraParams<int,double,float>>>);
I use pointers to make the code valid in evaluated contexts too. Contrary to a possible solution involving std::declval.

how can a c++ concept combine concepts?

I have inherited the following:
template <typename T>
concept IsAwaiter = requires {
typename T::await_ready;
typename T::await_suspend;
typename T::await_resume;
};
template <typename ...AWAITABLES>
concept IsAwaitables = typename std::conjunction<IsAwaiter<AWAITABLES>...>::type;
Building this with clang 10.0.0 results in the following error:
IsAwaiter.h:43:50: error: template argument for template type parameter must be a type
Perhaps just a simple syntax issue, but I've found it hard to find an example which shows how to create a concept based on a variadic template concept parameter.
Any help appreciated!
std::conjunction is for type traits. std::conjunction<IsAwaiter<AWAITABLES>...> is a type with a member static bool value = (IsAwaiter<AWAITABLES>::value && ...), where each IsAwaiter<AWAITABLES> is itself expected to be a type trait with its own static bool member value. This is nonsensical when IsAwaiter<AWAITABLES> is a concept, because concepts are not type traits. They are "just" booleans. Use a fold expression.
template <typename... AWAITABLES>
concept IsAwaitables = (IsAwaiter<AWAITABLES> && ...);
That's it.
struct Dummy {
using await_ready = Dummy;
using await_suspend = Dummy;
using await_resume = Dummy;
};
int main() {
static_assert(IsAwaitables<>);
static_assert(IsAwaitables<Dummy>);
static_assert(IsAwaitables<Dummy, Dummy>);
}
As HTNW points out, you want:
template <typename ...T>
concept IsAwaitables = (IsAwaiter<T> && ...);
But really, do you even need this concept at all? You can just use IsAwaiter directly. And it probably should just be named Awaiter - the typical convention for concepts is to name them as nouns rather than questions (e.g. Range vs IsRange).
If you're taking a parameter pack, you would want to use it anyway:
template <Awaiter... T>
void f(T... awaiters);
and the same with the abbreviated function template syntax:
void f(Awaiter auto... awaiters);
or if you have a fixed number of them, it especially doesn't make sense:
template <Awaiter T, Awaiter U>
void f(T, U);
And even in other contexts where it doesn't neatly fit, it seems better to just manually use Awaiter with a fold-expression. So I question the need for the conjunction concept to begin with.

Why do `SFINAE` (std::enable_if) uses bool literals instead of `true_t` / `false_t` tag classes?

I am trying to learn about SFINAE (i am following this tutorial), but there are some... "design choices" I do not understand and, as such, I find them confusing.
Let's assume I have a situation like this (included re-implementation of std::enable_if is there just to demonstrate how I understand enable_if)
// A default class (class type) declaration. Nothing unusual.
template <bool, typename T = void>
struct enable_if
{};
// A specialisation for <true, T> case. I understand 'why-s' of this.
// -- 'why-s': if I attempt to access 'enable_if<false, T>::type' (which does not exist) I will get a substitution failure and compiler will just "move-on" trying to match "other cases".
template <typename T>
struct enable_if<true, T> {
typedef T type;
};
// Here lies my problem:
template <class T,
typename std::enable_if<std::is_integral<T>::value,T>::type* = nullptr>
void do_stuff(T& t) { /* do stuff */ };
(1) The very 1st thing I have a "problem" with, is bool literal (true/false). I understand they are correct and templates can accept compile-time constant values of primitive data types (plain-old-data types) but if I were tasked to design the enable_if "mechanisms" instead of using true/false I would create a tag classes true_t(or True) and false_t (or False) as follows:
class true_t {}; // or True
class false_t {}; // or False
template<typename T>
class is_integral // just to have "something" to use with "enable_if"
{
using result = false_t;
};
template<>
class is_integral<int32_t> // same with all other int types
{
using result = true_t;
};
template <typename B, typename T = void>
struct enable_if
{};
template <typename T>
struct enable_if<true_t, T>
{
using type = T;
};
(2) The second thing I find redundant is the need to specify typename T template parameter. Wouldn't it be easier / better to just implement enable_if as follows:
template <typename B>
struct enable_if
{};
template <>
struct enable_if<true_t>
{
using type = void; // the 'type' exists therefore substitution failure will not occur.
};
I am well aware that all my propositions are extremely inferior to the currently existing solutions, but I don't understand why... What portion of the functionality (important functionality) of current SFINAE did i shave off? (Not even realizing it...)
I know that, on this site, I am obligated to ask a single question within a... single "question-post-like" format, but if you find it acceptable could I also ask what will this syntax:
std::enable_if</* ... */>::type* = nullptr
accomplish? It's beyond my understanding right now...
The very 1st thing I have a "problem" with, is bool literal (true/false). I understand they are correct and templates can accept compile-time constant values of primitive data types (plain-old-data types) but if I were tasked to design the enable_if "mechanisms" instead of using true/false I would create a tag classes true_t(or True) and false_t (or False) as follows
The issue with use a tag type instead of just a bool is that you have to add extra complexity to the code. If you want to check a compile time condition, like sizeof for instance, you couldn't just do sizeof(T) == 8. You would have to make an abstraction that does the check and the returns the appropriate tag type.
The second thing I find redundant is the need to specify typename T template parameter. Wouldn't it be easier / better to just implement enable_if as follows
Not really. What if you want to use the SFINAE for the return type? You would only be able to have a void function then, which is unnecessarily limiting. Instead what you can do is use what was later added in C++14 and C++17 and make aliases. This makes the names non dependent and lets you drop the typename
template< bool B, class T = void >
using enable_if_t = typename enable_if<B,T>::type;
template< class T >
inline constexpr bool is_integral_v = is_integral<T>::value;
This allows you to rewrite
template <class T,
typename std::enable_if<std::is_integral<T>::value,T>::type* = nullptr>
void do_stuff(T& t) { /* do stuff */ };
to
template <class T,
std::enable_if_t<std::is_integral_v<T>,T>* = nullptr>
void do_stuff(T& t) { /* do stuff */ };
although I prefer to use a bool for the type of enable_if_t like
template <class T,
std::enable_if_t<std::is_integral_v<T>, bool> = true>
void do_stuff(T& t) { /* do stuff */ };
I know that, on this site, I am obligated to ask a single question within a... single "question-post-like" format, but if you find it acceptable could I also ask what will this syntax:
std::enable_if</* ... */>::type* = nullptr
accomplish?
It makes a pointer to the type that std::enable_if "returns" and sets it to null pointer. The goal here is to make a template parameter that will only exist if the condition is true. You could rewrite it to
typename = typename std::enable_if</* ... */>::type
so instead of having a non type parameter you have a type parameter. They both accomplish the same thing but the latter wont work with overloading the function for different enable_if's since default template parameters are not part of the signature. The first version which uses non type parameters is included in the function signature and does allow you to overload the enable_if's.
First off there exists tag-types for true and false, namely std::true_type and std::false_type.
Let's say we made the enable_if work with this instead of a bool parameter. You could then no longer do things like std::enable_if<1 == 1>::type since 1 == 1 evaluates to a bool. So does most things you will want to test here.
On the other hand, the existing tag types can be used in a enable_if since they contain a value and have a operator() that return said value.
So it seems to me that a lot of convenience would be lost in doing it your way, and from what I can see nothing would be gained.
For point 2, it's simply a convenience to be able to specify what type you want enable_if to hold if it's true. It defaults to void, but if you want you can easily have it deduce an int, double ect. which can be useful sometimes.

Detecting actual arity of template template argument

I'm fiddling around with template metaprogramming, specifically with type sequences and STL-like algorithms working on those sequences. One thing I ran into was the transformation of predicates, for example by binding one of their parameters
I think it's hard to describe my problem without first providing some background information. Here's an example:
#include <type_traits>
// Type sequence
template<class... T>
struct typeseq
{
static constexpr size_t Size = sizeof...(T);
};
// Some algorithm
template<class TS, template<class...> class P>
using remove_if = /* ... some template logic ... */;
// Usage example:
using a = typeseq<int, float, char*, double*>;
using b = remove_if<a, std::is_pointer>; // b is typeseq<int, float>
As shown here, remove_if requires a predicate that serves as an oracle for the removal algorithm to determine which of the elements to remove. As we're dealing with metaprogramming, it is in the form of a template template parameter. ( Note that P is using a variadic template parameter here. Although I'm expecting a unary template, an earlier version of C++ had the restriction that a variadic template argument can't be used as a non-variadic template parameter, which severely restricts predicate transformations. ) It requires that the predicate, when instantiated, resolves to a type that has a nested compile time value member that is convertible to bool.
All is well, but say you want to remove every type that is convertible to int. Obviously, std::is_convertible is a binary predicate, but remove_if above requires a unary predicate. We just need to fix the second template argument to int. Let's define a bind2nd:
template<template<class, class...> class P, class BIND>
struct bind2nd
{
template<class T1, class... Tn> using type = P<T1, BIND, Tn...>;
};
// and now we should be able to do:
using c = remove_if<ts, bind2nd<std::is_convertible, int>::type>; // c is typeseq<char*, double*>;
Unfortunately, this fails to compile on latest Clang and MSVC++. Apparently, the issue is the pack expansion of Tn... into std::is_convertible<T1, BIND, Tn...> while std::is_convertible only has 2 template parameters. It doesn't seem to matter that the pack is empty in practice (isolated example)
I'd rather not provide 'overloads' for any required arity of the predicate passed into bind2nd. Is there a way to detect the arity of P in bind2nd above? GCC allows me to partially specialize it for non-variadic versions of P:
template<template<class, class> class P, class BIND>
struct bind2nd<P, BIND>
{
template<class T1> using type = P<T1, BIND>;
};
But unfortunately GCC wasn't the one that was complaining in the first place. I also doubt how conformant such a partial specialization is. Is there a way around this? Is it possible to detect the actual arity of a template template parameter, and do something different based on that information?
I think I found a workaround.
The problem seems related to type aliases - they seem to directly pass along any template arguments, rather than instantiating the type as is the case with a class or struct. We can use this in our favor, by using a struct as an intermediate step:
template<template<class, class...> class P, class BIND>
struct bind2nd
{
template<class... Tn>
struct impl
{
using type = P<Tn...>;
};
template<class T1, class... Tn> using type = typename impl<T1, BIND, Tn...>::type;
};
Now it works :). It's a bit convoluted though. I wonder if this is all according to standard, but it seems to compile on all major compilers.
Edit: Why am I complicating things by using nested types and aliases? I can just as well use derivation for predicates:
template<template<class, class...> class P, class BIND>
struct bind2nd
{
template<class T1, class... Tn>
struct type : P<T1, BIND, Tn...> { };
};
Clean and simple. And it makes it almost identical to the first definition of bind2nd in the OP.

Tagging objects using enums via template-template parameters

I would like to use an enum argument of a template, to restrict a second argument, a class, to in turn taking an member of the enum as an argument as it's templated parameter. In code, I would expect this to look like:
CObject<EObjectTag, CSubObject<EObjectTag::CAT_A>> cObject;
this should work, however:
CObject<EObjectTag, CSubObject<ENotAnObjectTag::CAT_OTHER>> cObject;
should fail as ENotAnObjectTag::CAT_OTHER is not a element of EObjectTag.
My implementation (attempt) of this, is as follows and bombs out during compilation (on gcc version 4.9.2 (Ubuntu 4.9.2-10ubuntu13)) with the error message:
source.cc:16:45: error: ‘SUBOBJECT_TAG’ was not declared in this scope
struct CObject>
#include <iostream>
#include <typeinfo>
enum class EObjectTag {CAT_A, CAT_B, CAT_OTHER};
// CSubObject
template<class OBJECT_TAG_T, OBJECT_TAG_T OBJECT_TAG>
struct CSubObject { OBJECT_TAG_T m_tTag = OBJECT_TAG; };
// CObject - Forward declaration
template <class SUBOBJECT_TAG_T, template <SUBOBJECT_TAG_T SUBOBJECT_TAG> class SUBOBJECT_T>
struct CObject;
// CObject - Specialization
template <class SUBOBJECT_TAG_T, template <SUBOBJECT_TAG_T SUBOBJECT_TAG> class SUBOBJECT_T>
struct CObject<SUBOBJECT_T<SUBOBJECT_TAG_T, SUBOBJECT_TAG>>
{
public:
SUBOBJECT_T<SUBOBJECT_TAG_T, SUBOBJECT_TAG> m_cSubObject;
};
int main() {
// The aim is that the second object only accepts a tag that
// belongs to EObjectTag
CObject<EObjectTag, CSubObject<EObjectTag::CAT_A>> cObject;
return 0;
}
The final use case for this involves replacing CSubObject with CObject so that we can use recursion to define a hierarchy of tagged objects, which also requires the use of variadic templates to have multiple objects at the same level. For example:
/* EBase, */
CObject</*EBase::BASE,*/ EObject,
CObject<EObject::INIT, EInitObject,
CObject<EInitObject::INIT_FOO>,
CObject<EInitObject::INIT_BAR>,
>,
CObject<EObject::COUNT, ECountObject,
CObject<ECountObject::COUNT_FOO>,
CObject<ECountObject::COUNT_BAR>,
>,
> cMyObjectHierarchy;
The commented out references to EBase (an enum internal to the library) are there to keep the template parameters of CObject consistent, I would plan (if possible) to do this automatically via template specialization or default arguments.
My goals of specifying this hierarchy of objects would in addition include:
Avoid forcing the user of this library to define additional classes
or structs in their program
Leverage compile time checking via the
templating of CObject with an enum, whose functions in turn use that
enum as an argument to a set of functions common to all CObjects
An argument of template <SUBOBJECT_TAG_T SUBOBJECT_TAG> class SUBOBJECT_T is a template, not an instance of a template. CSubObject<blah> cannot match the kind template<...>class, because CSubObject<blah> is a type that is generated by the template, not a template. template<...>class parameters are templates, not types.
In addition, CSubObject is of kind template<class T, T> class, not template<SUBOBJECT_TAG_T>class. It takes two arguments, the first a type, the second a constant of that type: the kind template<SUBOBJECT_TAG_T>class is a template that takes one argument of type SUBOJECT_TAG_T. These are unrelated kinds of template.
Second, you seem to have issues with template specializations. Template specializations are pattern-matching of your primary specialization. They are not "new overloads". So arguments to CObject must first match the kinds of arguments the primary specialization of CObject expects. The stuff within template< blah > are used to pattern match the pattern in the CObject< blah > part of the specialization.
In general, it is conventional to use ALL CAPS only for macros, not for template arguments.
These are all problems with your code in your question. Your code lacks a clear problem statement or question, so the best I can do is describe fixes to your myriad of problems.
You have revised your question a bit.
template<class T, class U>
struct CObject;
template<class T, template<class Q, Q>class Z, T t>
struct CObject< T, Z<T, t> > {
};
live example.
Now, you still have to pass CSubObject<EObjectTag, EObjectTag::CAT_A> as the 2nd parameter.
You could also add a specialization:
template<class T, template<T>class Z, T t>
struct CObject< T, Z<t> > {
};
which, if you had a template<EObjectTag tag> struct Example;, you could CObject< EObjectTag, Example<EObjectTag::bob> > as well.
I made some changes to make it compile. Although I'm not 100% sure if this actually does what you want it to do; I agree with most of what Yakk sais in his answer.
Note: the following will not compile because I deliberately tried to mix a type of one enum with a value of another enum to verify that it indeed triggers a compile-time error, which I think is sort of what you asked for.
#include <iostream>
#include <typeinfo>
enum class EObjectTag {CAT_A, CAT_B, CAT_OTHER};
enum class FObjectTag {DOG_A, DOG_B, DOG_OTHER};
// CSubObject
template<typename OBJECT_TAG_T, OBJECT_TAG_T OBJECT_TAG>
struct CSubObject { OBJECT_TAG_T m_tTag = OBJECT_TAG; };
// CObject - Specialization
template <class SUBOBJECT_TAG_T, SUBOBJECT_TAG_T SUBOBJECT_TAG, template <typename TYPE_T, TYPE_T TYPE> class SUBOBJECT_T>
struct CObject
{
public:
SUBOBJECT_T<SUBOBJECT_TAG_T, SUBOBJECT_TAG> m_cSubObject;
};
int main() {
// The aim is that the second object only accepts a tag that
// belongs to EObjectTag
CObject<EObjectTag, EObjectTag::CAT_A, CSubObject> cObject1;
CObject<EObjectTag, FObjectTag::DOG_B, CSubObject> cObject2;
return 0;
}