SFINAE approach comparison - c++

The following code shows an SFINAE implementation to check whether a type (basically a class) contains a member function member_func at compile time.
#define CHECKER(func_name,class_name) sizeof(class_name<T>::template func_name<T>(0)) == 1
#include <iostream>
struct A
{
void member_func();
};
struct B {};
template<typename T>struct Check_If_T_Is_Class_Type
{
template<typename C> static char func (char C::*p);
template<typename C> static long func (...);
enum{val = CHECKER(func,Check_If_T_Is_Class_Type)};
};
//APPROACH 1
template <typename T>struct TypeHasMemberFunc
{
template <typename C, C> struct TypeCheck;
template <typename C> struct Prototype_Holder {typedef void (C::*fptr)();};
template <typename C> static char func(TypeCheck
<
typename Prototype_Holder<C>::fptr,
&C::member_func
>*);
template <typename C> static long func(...);
enum {value = CHECKER(func,TypeHasMemberFunc)};
};
//APPROACH 2
template <typename T>struct has_member_func
{
template<class C> static char func(char (*)[sizeof(&C::member_func)]);
template<class C> static long func(...);
enum{value = CHECKER(func,has_member_func)};
};
int main(){
if(Check_If_T_Is_Class_Type<A>::val)
std::cout<<TypeHasMemberFunc<A>::value; //using APPROACH 1
if(Check_If_T_Is_Class_Type<B>::val)
std::cout<<has_member_func<B>::value; //using APPROACH 2
}
However my question is which approach would you prefer (APPROACH 1 or APPROACH 2) and why?
Do you find any inconsistency in the given approaches? If yes please let me know.
P.S : Assume sizeof(char)!= sizeof(long)

Second approach doesn't check function type (return type or arguments types) and does work with all types, not only class types.

I would personally prefer the second approach to play with as it is shorter and easier to understand. But GCC won't compile it, so you have to use something like that for GCC:
namespace detail
{
template<class C> char func(char (*)[sizeof(&C::member_func)]);
template<class C> long func(...);
}
template <typename T>struct has_member_func
{
enum{value = (sizeof(detail::func<T>(0)) == 1)};
};
Also, It would be nice to get rid of CHECKER macro. It makes your code extremely less readable.
Anyway, I would refrain from using such C++ hacks in production code (except you are a Boost team member :-)
Such stuff is error-prone, hard to support, hardly portable between compilers but the principal point is that I do not remember any real life task demanded such hard-code C++.

EDIT: completed and corrected.
Another approach, using ambiguity from inheritance, probably functionally equivalent to your Approach 2. I remember having problems with Approach 1 (it compiles with G++ 4.4.5 though) because the name resolution triggered an error and not a substitution failure. I had to resort to:
template <class T>
struct has_foo
{
struct fallback { void foo(...); };
struct D : T, fallback { };
template <typename U, U> struct K;
// Will be ambiguous for U = D iff T has a foo member function.
// It doesn't trigger an error, since D will always have at least one
// foo member function.
template <class U> static char (&test(K<void (fallback::*)(...), &U::foo>*))[1];
template <class U> static char (&test(...))[2];
static const bool value = sizeof(test<D>(0)) == 2;
};
This works when T is a class, so you may want to add your layer for checking whether T is a class type.
Note that any foo member function will be detected. If you want to check whether the detected foo function can be called with given arguments, you have to do another layer of SFINAE:
// Check whether foo can be called with an argument of type Arg
// and yields an element of type Res.
// If you need Res = void, this code does not work.
template <class T, typename Arg, typename Res>
struct check_foo
{
struct flag {};
struct D : T { using T::foo; flag foo(...); };
template <typename U>
static char (&test(U))[1];
template <typename> static char (&test(...))[2];
static Arg f();
static const bool value = sizeof(test<Arg>( ((D*)0)->foo(f()) )) == 1;
};

Related

A variable template that is true iff a class template would instantiate?

Let's say I have a class template A that has one type template parameter, and a single primary specialization:
template<typename T> struct A {
/*...*/
};
For some T arguments A<T> will instantiate successfully, for others it won't.
Without modifying or extending the definition of A, is it possible to write a bool variable template:
template<typename T>
constexpr bool WorksWithA = /*...*/;
such that WorkWithA<T> is true iff A<T> would instantiate successfully?
Update
Posted this as separate question: Using typename in C++20 requires / concept?
#include <iostream>
template<typename T>
struct A {
using X = typename T::X;
};
template<typename T>
constexpr bool WorksWithA = requires { typename A<T>; };
struct GoodArg {
using X = int;
};
struct BadArg {
};
int main() {
std::cout << WorksWithA<GoodArg> << std::endl;
std::cout << WorksWithA<BadArg> << std::endl;
}
Compiled and run with:
$ clang++ --version
clang version 10.0.0-4ubuntu1
$ clang++ test.cc -std=c++20
$ ./a.out
1
1
This outputs 1 1, expected output is 1 0 ?
What gives?
If I'm understanding this correctly, for SFINAE friendly types, this could be a way:
Match if instantiation is possible:
template<class T>
constexpr auto WorksWithA(int) -> A<T>;
Match if it isn't:
struct no {};
template<class T>
constexpr auto WorksWithA(long) -> no;
Helper:
template<typename T>
inline constexpr bool WorksWithA_v =
not std::is_same_v<no, decltype(WorksWithA<T>(0))>;
Demo
If A isn't SFINAE friendly (as the A in the updated question), you'll have to add the check in WorksWithA. In this case:
template<typename T>
struct A {
using X = typename T::X;
};
template<class T>
constexpr auto WorksWithA(int) -> typename T::X;
struct no {};
template<class T>
constexpr auto WorksWithA(long) -> no;
template<typename T>
inline constexpr bool WorksWithA_v =
not std::is_same_v<no, decltype(WorksWithA<T>(0))>;
Demo
You misunderstand how templates work.
template<typename T>
struct A {
using X = typename T::X;
};
template<typename T>
constexpr bool WorksWithA = requires { typename A<T>; };
struct GoodArg {
using X = int;
};
struct BadArg {
};
BadArg fundamentally works with A. It isn't bad.
template<typename T>
struct A {
using X = typename T::X;
};
this does not require that T have a typename X.
This states that if it does have a typename X, then A::X is that typename.
So WorksWithA does test correctly if T can instantiate the template A.
This doesn't help you directly, but understanding that you are asking the wrong question is important, and why your question is the wrong one. Because that leads to the right question.
Either you want A not not instantiate when T does not have a type X, or you want WorksWithA to do a different kind of test.
For example, maybe you want HasAnX:
template<class T>
concept HasAnX = requires { typename T::X; };
then
static_assert(HasAnX<GoodArg>);
static_assert(!HasAnX<BadArg>);
compiles.
However, I'd guess that semantically this doesn't match what you want it to match. You want it to be the A-ness not the X-ness you are measuring.
Then your problem is probably not with WorksWithA, but with A itself. You thought you where adding a requirement that T HasAnX, but you where not.
template<HasAnX T>
struct A {
using X = typename T::X;
};
that makes A require that T has an X. Now, WorksWithA ... works as you intended.
Live example.
This is a good (and right) question, but I think as up until now there is no answer for this question in general.
Lets assume the class template A is simply like:
template<typename T>
class A{
public:
T variable;
}
This template works with most of types but it does not instantiate with T = void for the obvious reason. now lets assume we want to find that out before hand with a SFINAE friendly method.
The best tool to find out if a type is instantiatable is to check whether it has a size, now Lets define a tool similar to std::void_t:
template<size_t...> using void_size_t = void;
now if we define:
template< class, class = void >
struct pre_WorksWithA : std::false_type { };
template< class T >
struct pre_WorksWithA<T, void_size_t<sizeof(A<T>)>> : std::true_type { };
template<typename T>
constexpr bool WorksWithA = pre_WorksWithA<T>::value;
In case of a normal types like int the result is true which it should be. but in case of void neither the WorksWithA nor pre_WorksWithA can instantiate and the result would be an error from the compiler.
So as you can see, up until now, basically there is no feature in C++ to help us know whether a type is instantiate-able or not without giving an error?
You can use a simple concept:
template<typename T>
constexpr bool WorksWithA = requires { typename A<T>; };

auto template parameter, data member and constness

Suppose I have a pointer to data member and I want to know if it's const or not. In other words:
struct S {
const int i; // this is const
int j;
};
In C++ I used to do something like this:
template<typename Class, typename Type, Type Class:: *>
struct is_const_data_member: std::false_type {};
template<typename Class, typename Type, const Type Class:: *Member>
struct is_const_data_member<Class, const Type, Member>: std::true_type {};
template<typename Class, typename Type, Type Class:: *Member>
void foo() {
const auto bar = is_const_data_member<Class, Type, Member>::value;
// ...
}
However, now there is the auto template parameter and template parameters list are much elegant:
template<auto Member>
void foo() {
// ...
}
In this case, the solo way I found to know if the data member point to something that is const is:
const auto bar = std::is_const_v<std::remove_reference_t<decltype(std::declval<Class>().*Member)>>;
However, it looks ugly to me and I feel like there must be a better way to do it.
Is there any other (shorter) solution for that?
You could change is_const_data_member to operate on a single type template parameter:
template<typename MemPtr>
struct is_const_data_member: std::false_type {};
template<typename Class, typename Type>
struct is_const_data_member<const Type Class::*>: std::true_type {};
Then, from template<typename Class, typename Type, Type Class:: *Member> void foo() you use it as
is_const_data_member<Type Class::*>::value
(Which, in my opinion, is slightly more intuitive.)
And from template<auto Member> void foo() you use it as
is_const_data_member<decltype(Member)>::value
You could also rewrite the trait to operate on an auto template parameter. But by using a type parameter you avoid unnecessary instantinations for different pointers of same type, which is supposedly a good thing.
How about something like this:
template <typename T>
struct is_const_data_member : std::false_type {};
template <typename C, typename T>
struct is_const_data_member<const T C::*> : std::true_type {};
template <auto T>
constexpr bool is_const_data_member_v = is_const_data_member<decltype(T)>::value;
And then, for example
struct Test
{
int a;
const int b;
};
bool x = is_const_data_member_v<&Test::a>;
bool y = is_const_data_member_v<&Test::b>;
working test here

Check the existence of a template function

How can I check the existence of a template function like this: Checking if reader struct has read arithmetic value
struct reader {
template<typename T>
std::enable_if_t<std::is_arithmetic<T>::value, T> read() {
return {};
}
};
I use a checker like this:
template <typename T>
struct test_read {
static constexpr auto value = std::is_convertible<decltype(std::declval<T>().read<int>()),int>::value;
};
But the compiler complains:
error: wrong number of template arguments (1, should be 2)
static constexpr auto value = std::is_convertible<decltype(std::declval<T>().read<int>()),int>::value;
Please give me your advice on that.
Thank you.
Update: Here is the final version I got after discussion, I hope everyone will find it helpful for your code
struct not_reader {
};
struct reader {
template<typename T>
std::enable_if_t<std::is_arithmetic<T>::value, T> read() {
return {};
}
};
template<class T, class Elem>
struct has_read {
private:
template<class C, typename=void>
struct test_read : std::false_type {
};
template<class C>
struct test_read<C, typename std::enable_if<std::is_convertible<decltype(std::declval<C>().template read<Elem>()), Elem>::value>::type>
: std::true_type {
};
public:
using type = typename test_read<T>::type;
static constexpr bool value = test_read<T>::value;
};
static_assert(has_read<reader, int>::value, "reader should have int read()");
static_assert(!has_read<not_reader, int>::value, "not_reader should not have int read()");
You forgot template before read()
static constexpr auto value
= std::is_convertible<
decltype(std::declval<T>().template read<int>()),int>::value;
// .................................#########
But I don't think that your code can check " if reader struct has read arithmetic value": try calling test_read with type int and you should get a compilation error.
The following is an example of an alternative solution
#include <type_traits>
struct reader
{
template<typename T>
std::enable_if_t<std::is_arithmetic<T>::value, T> read()
{ return {}; }
};
template <typename, typename = void>
struct readTypeRet
{ using type = void; };
template <typename T>
struct readTypeRet<T, decltype(std::declval<T>().template read<int>(), void())>
{ using type = decltype(std::declval<T>().template read<int>()); };
template <typename T>
struct test_read
: public std::is_convertible<typename readTypeRet<T>::type, int>
{ };
int main ()
{
static_assert(test_read<reader>::value == true, "!");
static_assert(test_read<int>::value == false, "!");
}
To briefly restate your problem in terms that are a bit clearer:
You have some function that will return T if T satisfies is_arithmetic, otherwise it returns void
You want to assert that calling this function with int will return a type convertible to int
I think the shortest path to fix your code is to take advantage of std::result_of (C++11/14, use std::invoke_result_t in C++17):
template<class T>
struct test_read {
static constexpr auto value = std::is_convertible<
typename std::result_of<decltype(&T::template read<int>)(T)>::type, int
>::value;
};
Live Demo
Some notes about this solution:
When specifying the read member function of T (reader), we need to use the template keyword to inform the compiler that the name reader is a template.
Use of result_of requires a function-like syntax of F(Args), so here we are getting the type of reader::read as the F portion, and then passing reader as the Args portion
we must pass an instance of T (reader) to read because it is a member function (rather than static or free), and member functions implicitly take a reference to the instance of the class they're being called on.

Perform overload resolution with template meta-programming

Inspired by another question I tried to find a way to deduce the type
of an overload member function given the actual argument used to call
that function. Here is what I have so far:
#include <type_traits>
template<typename F, typename Arg>
struct mem_fun_type {
// perform overload resolution here
typedef decltype(std::declval<F>()(std::declval<Arg>())) result_type;
typedef decltype(static_cast<result_type (F::*)(Arg)>(&F::operator())) type;
};
struct foo {};
struct takes_two
{
void operator()(int);
void operator()(foo);
};
struct take_one {
void operator()(float);
};
int main()
{
static_assert(std::is_same<mem_fun_type<take_one, float>::type,
void (take_one::*)(float)>::value, "Zonk");
static_assert(std::is_same<mem_fun_type<takes_two, double>::type,
void (takes_two::*)(float)>::value, "Zonk");
return 0;
}
As long as the template parameter Arg matches the actual type the
static_cast will succeed, but this is only the most trivial case of
overload resolution (exact match). Is it possible to perform the
complete overload resolution process in template metaprogramming?
This is purely hypothetical and not intended for real-world use.
This is closest I came to it so far: define function returning tables of different sizes and your result is sizeof(select(...)) receiving pointer to function you want to match. To ensure that the code will compile even if function does not exist in given class, you can use separate check has_function.
Result of overload resolution is in select<has_function<T>::value, T>::value.
With this code you can even "resolve" data members, not just functions, it's only a question of making right parameter for select function.
However there is one deficiency here - overload resolution is not on function parameters, but on function type. Meaning none of usual parameter type conversions takes place.
// Verify the name is valid
template <typename T>
struct has_function
{
struct F {int function;};
struct D : T, F {};
template <typename U, U> struct same_;
template <typename C> static char(&select_(same_<int F::*, &C::function>*))[1];
template <typename> static char(&select_(...))[2];
enum {value = sizeof(select_<D>(0)) == 2};
};
// Values to report overload results
enum type { none=1 , function_sz_size_t , function_sz , function_string };
template <bool, typename R> struct select;
template <typename R> struct select<false, R>
{
enum {value = none};
};
template <typename R> struct select<true, R>
{
// Define your overloads here, they don't have to be templates.
template <typename Ret, typename Arg> static char(&select_(Ret (R::*)(const char*, Arg)))[function_sz_size_t];
template <typename Ret, typename Arg> static char(&select_(Ret (R::*)(Arg)))[function_sz];
template <typename Ret> static char(&select_(Ret (R::*)(std::string)))[function_string];
template <typename Ret> static char(&select_(Ret (R::*)(std::string&&)))[function_string];
template <typename Ret> static char(&select_(Ret (R::*)(const std::string&)))[function_string];
static char(&select_(...))[none];
enum {value = sizeof(select_(&R::function))};
};

Variadic templates - incomplete type

Having this code:
template<class ...Args>
struct Are_Same
{
enum {value = Are_Same<Args...>::value};
};
template<class A,class... C>
struct Are_Same<A,C...>
{
enum {value = Are_Same<A,C...>::value};//HERE is THE ERROREOUS LINE
};
template<class A,class B>
struct Are_Same<A,B>
{
enum {value = std::is_same<A,B>::value};
};
I'm getting error from gcc 4.6.1:
error: incomplete type 'Are_Same' used in
nested name specifier.
I thought that by doing Are_Same<A,C...>::value I will invoke recursive call which at the end will simply expand to Are_Same<A,B>. Obviously it's not the case. Anyone knows where am I making mistake?
I think that the definitions of the templates are wrong, in both cases you are triggering exact recursion. I would have expected the compiler to die with some stackoverflow inside the compiler but a different error is produced...
An implementation of the are_same variadic template could be:
template <class... Args> // base (optional to declare the template)
struct are_same;
template <class A, class B, class... Args> // recursion
struct are_same<A,B,Args...> {
static const bool value = is_same<A,B>::value && are_same<A,Args...>::value;
};
template <class A, class B> // stop condition
struct are_same<A,B> {
static const bool value = is_same<A,B>::value;
};
Note that in the recursion step, one argument is dropped from the list of arguments, so that the new problem to resolve is a reduced version of the original. This type of template metaprogramming is quite related to recursion, and the same rules apply, to be able to use recursion you need to ensure that each recursive step gets you closer to a solution. In this particular case, given a list of N potentially same types, each step reduces the problem to finding whether N-1 types are the same.
You can use alternatively, as stop condition (replacing the former one) a degenerate version of the are_same problem:
template <class A>
struct are_same<A> {
static const bool value = true;
};
Which is degenerate in the sense that it does not really make sense asking whether a single type *are_same*, but for different metaprogramming tasks it could be appropriate.
A different potentially more efficient algorithm (I am not sure whether the compiler will avoid the instantiation of the template in the recursion step above) that does not depend on is_same could be:
template <class... Args>
struct are_same;
template <class A, class... Args>
struct are_same<A,A,Args...> { // recursion
static const bool value = are_same<A,Args...>::value;
};
template <class A, class B, class... Args>
struct are_same<A,B,Args...> { // cut, A and B are not the same
static const bool value = false;
};
template <class A>
struct are_same<A> { // end of recursion
static const bool value = true;
};
In this case, the compiler will prefer the recursion to the cut steps whenever the two types are the same, so we need not check is_same internally. At the same time, if the compiler goes into the cut step, we don't need to process the rest of the type list, as we already know the answer.
I would do it like this :
#include <type_traits>
#include <iostream>
template <class... Args>
struct are_same
{
static const bool value=true;
};
template <class A, class B, class... Args> // recursion
struct are_same<A,B,Args...> {
static const bool value = std::is_same<A,B>::value && are_same<B,Args...>::value;
};
int main()
{
std::cout<< std::boolalpha << are_same< int >::value << std::endl;
std::cout<< std::boolalpha << are_same< int, int, int >::value << std::endl;
std::cout<< std::boolalpha << are_same< int, int, double, int >::value << std::endl;
}
Probably simplest implementation could be like this:
template <typename... TList>
struct are_same { constexpr static bool value = false; };
template <typename T, typename... TList>
struct are_same<T, T, TList...> {
constexpr static bool value = are_same<T, TList...>::value;
};
template <typename T>
struct are_same<T> { constexpr static bool value = true; };
Alternatively you can replace stop condition with
template <typename T>
struct are_same<T, T> { constexpr static bool value = true; };
But the first one is more general because are_same<type>::value == true. Another question is what should be are_same<>::value equal to. This gives you false but it is not a big deal to add one more template specialisation like this.
template <>
struct are_same<> { constexpr static bool value = true; };