writing and using a C++ template to check for a function's existence

Hi community,

in a template I have a class rho of a type RhoType. This class can have a member rho.dimensions(), but it must not. In this template I want to create another class, with the same "dimensions" as rho, if it has "dimensions", or with some other given "dimensions", if it hasn't. So first I somehow have to find out, if the class rho has "dimensions". I found a possible solution here: stackoverflow.com/questions/257288/is-it-possible-to-write-a-c-template-to-check-for-a-functions-existence

Without understanding the best rated answer, but being keen with copy/paste I've got the following implementation:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
template <typename T>
class hasDimensions
{
    typedef char one;
    typedef long two;

    template <typename C> static one test( typeof(&C::dimensions) ) ;
    template <typename C> static two test(...);


  public:
    static const bool check() 
    { 
        return (sizeof(test<T>(0)) == sizeof(char)); 
    }
};

I hope it works, because I could compile it. But I have problems with using it. Here is the way I thought it can be used:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
template<class RhoType>
void createDimRho
(
    const RhoType& rho,
)
{
    if (hasDimensions<RhoType>::check())
    {
        const dimensionedScalar dimRho("dimRho", rho.dimensions(), 1.0);
    }
    else
    {
        const dimensionedScalar dimRho("dimRho", dimensionSet(0,0,0,0,0,0,0), 1.0);
    }
}

void createDimRho()
{
   createDimRho(rhoWithoutDimension)
}


But I can't compile it because of the error:
error: ‘const class rhoWithoutDimension’ has no member named ‘dimensions’
Well, it know it has no member named 'dimensions', but for such cases I built in the if switch.

Does anyone has an idea, how can I overcome this problem?

Best regards,
Ilya
Sorry, I can't understand your code,
you didn't provide definition of some types used in the code,

You have sintax errors in your code, for example
1
2
3
4
5
template<class RhoType>
void createDimRho
(
    const RhoType& rho, // do you see this?
)


1
2
template <typename C> 
static two test(...); // this is suposted to be variadic function ? 

also that funciton is nested, static and has no definition so I realy can't understand your Sorry.


1
2
template <typename C> 
static one test( typeof(&C::dimensions) ) ;


Is this function or class? anyway how can you take an addres of member function of object that is not a class but template function...

I think you'll have to explain all tha a litle bit more, maybe I don't see but this is unundestandable.
Sorry for the confusion. The above piece of code originates form a thread at stackoverflow.com. Here is the reference.

Question:
Is it possible to write a C++ template that changes behavior depending on if a certain member function is defined on a class?

Reply:
Yes, with SFINAE you can check if a given class does provide a certain method. Here's the working code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>

struct Hello
{
    int helloworld()
    { return 0; }
};

struct Generic {};


// SFINAE test
template <typename T>
class has_helloworld
{
    typedef char one;
    typedef long two;

    template <typename C> static one test( typeof(&C::helloworld) ) ;
    template <typename C> static two test(...);


public:
    enum { value = sizeof(test<T>(0)) == sizeof(char) };
};


int
main(int argc, char *argv[])
{
    std::cout << has_helloworld<Hello>::value << std::endl;
    std::cout << has_helloworld<Generic>::value << std::endl;
    return 0;
}


Because of my poor c++ knowledge, I just copy-pasted it.

Concerning syntax errors, sorry for that. I try to modify a code, which is a part of a library and consist of many files. With the code pieces posted here, I just wanted to schematically show where the problem arise. It is not a working example. And of course there is no comma after const RhoType& rho, there were some other classes provided to the function after rho, which I deleted.

The point is, there is a class rho of the type RhoType. It may have a function dimensions(). I want to use this function if it is there. But I want to prevent the 'no member' error if the class rho doesn't have any function named dimensions().

And I don't know what (...) is. It was already in the code posted above.

I hope it is a little bit more understandable now, and sorry if it still isn't.
Last edited on
For the case you are wondering: "why this noob is trying to change complicated code?" The background is following. The code is open source. There is a bug. I've reported the bug, but it can take days till month to be fixed. Till it happens, I need a solution to overcome the problem. I don't feel myself able to fix it in a "clean" way. But first, I need it know, and second, I'm glad to learn some c++ basics.
No ideas? Or is it still ununderstandable?
I certainly understand the code - and how it works.
the original post is GCC specific really with the typeof thing (I had to use __typeof__ ).

With C++11 - the decltype keyword does the same thing.

I made a testclass (with and without the relevant function name) and I got it
to work for me.
This is what i used to test ( I comment out the dimension function in the mystruct as needed)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//My test class
struct MyStruct
{
    long Dimensions(int)const {return 99;}
};


template <typename T>
class hasDimensions
{
    typedef char one;
    typedef long two;

    //using C++11 decltype - but it works with
    //typeof or __typeof__ on GCC compiler
    template <typename C> 
    static  one test(  decltype(&C::Dimensions) ); 
    
    template <typename C> 
    static  two test(...);
    

  public:
    static const bool check() 
    { 
        return (sizeof(test<T>(0)) == sizeof(char)); 
        
    }
};



template<class RhoType>
void createDimRho( const RhoType& rho)
{
    if (hasDimensions<RhoType>::check())
    {
        cout << "Has Dimensions" << endl;
    }
    else
    {
        //const dimensionedScalar dimRho("dimRho", dimensionSet(0,0,0,0,0,0,0), 1.0);
        cout << "Does not have member dimensions" << endl;
    }
}

void createDimRho()
{
   createDimRho(MyStruct() ); //**TESTING ** USING  the  test struct - see further up the page//
}

//****************************************************************
int main()
{
 
    createDimRho();
}


So maybe there is some other issue in your code - is the function name spelt correctly??



Something else by the way:
Differentiating between sizeof (char) and sizeof(long) is not absolutely failsafe.
The C++ standard only says something along the lines of - that sizeof( long) isat least as big as sizeof(int) which is
at least as big as sizeof(short) which is at least as big as sizeof(char).
So the only thing you are gauranteed is that sizeof(long) is at least as big as (sizeof(char) - you are not guaranteed that long is bigger than char.
Last edited on
Well you can use templates to make various tests,
for example you can check whether some type is an class:

here is how to do that:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <iostream>

namespace templates
{
	template<typename T>
	class IsThisAClass {
	private:
		typedef char One;
		typedef long long Two;

		template<typename C> 
		static One test(decltype(&C::IS_THIS_A_CLASS_OR_IS_IT_NOT)); // TEST IF THIS IS A CLASS

		template<typename C> 
		static Two test(...); // variadic... everything else just not a class :D

		public:
			static const short Yes = sizeof(test<T>(0)) == sizeof(One);
	};

	template <typename T>
	void check()
	{
		if (IsThisAClass<T>::Yes) 
		{
			std::cout << " It is a class type " << std::endl;
		}
		else
		{
			std::cout << " It is not a class type " << std::endl;
		}
	}

	template <typename T>
	void checkT (T)
	{
		check<T>();
	}
}

	class MyType { };
	class MyClass { };
	struct MyStruct { };
	union MyUnion { };
	void myfunc() { }
	enum E{ sss };

using namespace std;
using namespace templates;

int main()
{
	// check if these types are class types

	std::cout << "int: ";
	check<int>();
	std::cout << "MyClass: ";
	check<MyClass>();
	std::cout << "MyStruct:";
	MyStruct s;
	checkT(s);
	std::cout << "MyUnion: ";
	check<MyUnion>();
	std::cout << "enum: ";
	checkT(sss);
	std::cout << "myfunc():";
	checkT(myfunc);
	cin.ignore();
	return 0;
}
In similar way you can test whether a type is build in type or not

By using templates and preprocessor!

Here is an example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
namespace templates
{
	template <typename T>
	class IsBuildInType {
	public:
		enum{ Yes = 0, No = 1};
	};

	// macro to specialize for fundamental types
	#define MAKE_TYPE(T) \
	template<> class IsBuildInType<T> { \
	public: \
		enum { Yes = 1, No = 0 }; \
	};

	#define LONGLONG_EXISTS

	MAKE_TYPE(void)
	MAKE_TYPE(bool)
	MAKE_TYPE(char)
	MAKE_TYPE(signed char)
	MAKE_TYPE(unsigned char)
	MAKE_TYPE(wchar_t)
	MAKE_TYPE(signed short)
	MAKE_TYPE(unsigned short)
	MAKE_TYPE(signed int)
	MAKE_TYPE(unsigned int)
	MAKE_TYPE(signed long)
	MAKE_TYPE(unsigned long)
	#ifdef LONGLONG_EXISTS
	MAKE_TYPE(signed long long)
	MAKE_TYPE(unsigned long long)
	#endif // LONGLONG_EXISTS
	MAKE_TYPE(float)
	MAKE_TYPE(double)
	MAKE_TYPE(long double)
	#undef MAKE_TYPE


	template <typename T>
	void test (T const& t)
	{
		if (IsBuildInType<T>::Yes)
			std::cout << "Is Build in type" << std::endl;

		else 
			std::cout << "Is not Build in type" << std::endl;
	}
}
	class MyType { };
	class MyClass { };
	struct MyStruct { };
	union MyUnion { };
	void myfunc() { }
	enum E{ sss };

	using namespace templates;

int main()
{
	test(4);
	test(MyType());
	test(449.99009);
	cin.ignore();
	return 0;
}
Last edited on
You have done something wrong in the IS_IT_A_CLASS example.
Hi,
My example test's if template argument is of class/enum/union type or build in type.

Why would that be mistake?

while your example test whether a class has a method called "Dimensions".

Two different things :)

1
2
3
4
	check<long>();  // not a class
	check<E>();       // is class
	check<char>();   // not class
	check<MyUnion>(); // is class 

Typesafe test for a non-static member function dimensionSet T::dimensions() const ;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>

struct dimensionSet {} ;

template < typename T > struct has_dimensions // has dimensionSet T::dimensions() const
{
    template < typename MEM_PTR_TYPE, MEM_PTR_TYPE > struct test_it {} ;

    template < typename C > 
    static char strawman( test_it< dimensionSet (C::*)() const, &C::dimensions >* ) ;

    typedef char two_chars[2] ;  template < typename C > static two_chars& strawman(...) ;

    enum { value = ( sizeof( strawman<T>(nullptr) ) == sizeof(char) ) } ;
};

struct with_dimensions { dimensionSet dimensions() const ; } ;

struct without_dimensions {} ;

struct with_wrong_dimensions { int dimensions() const ; } ;

struct with_a_right_overload_of_dimensions
{
    dimensionSet dimensions() const ;
    void dimensions(dimensionSet) ;
};

int main()
{
    std::cout << has_dimensions<with_dimensions>::value << '\n' ; // 1
    std::cout << has_dimensions<without_dimensions>::value << '\n' ; // 0
    std::cout << has_dimensions<with_wrong_dimensions>::value << '\n' ; // 0
    std::cout << has_dimensions<with_a_right_overload_of_dimensions>::value << '\n' ; // 1
}
Last edited on
@codekiddy
-I said that your IS_IT_A_CLASS test program seems to have a problem.
I popped it into MS2008, MS2011 beta, and GCC - it did not compile.
I had to make a correction - so I was querying it.
Last edited on
I'm sorry but here I got oposite...

I have copied my own code from this site into Visual studio 11 Beta and here are the results:

1>------ Rebuild All started: Project: Templates, Configuration: Debug x64 ------
1>Build started 4.5.2012. 20:18:03.
1>_PrepareForClean:
1>  Deleting file "x64\Debug\Templates.lastbuildstate".
1>InitializeBuildStatus:
1>  Creating "x64\Debug\Templates.unsuccessfulbuild" because "AlwaysCreate" was specified.
1>ClCompile:
1>  Main.cpp
1>LinkEmbedManifest:
1>  LINK : padding exhausted: performing full link
1>  Templates.vcxproj -> c:\users\admin\documents\visual studio 11\Projects\MyProjects\x64\Debug\Templates.exe
1>FinalizeBuildStatus:
1>  Deleting file "x64\Debug\Templates.unsuccessfulbuild".
1>  Touching "x64\Debug\Templates.lastbuildstate".
1>
1>Build succeeded.
1>
1>Time Elapsed 00:00:02.31
========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========


When executing output is as expected.

int: It is not a class type
MyClass: It is a class type
MyStruct: It is a class type
MyUnion: It is a class type
enum: It is a class type
myfunc(): It is not a class type


But thanks anyway :)
Generalized check for a const-qualified non-static member function R T::dimensions(A..) const
(Requires conformance wrt variadic templates)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <iostream>

template < typename T, typename R, typename... A  > struct has_dimensions
{
    template < typename PTR_TO_MEN_FUN, PTR_TO_MEN_FUN > struct test_it ;

    template < typename C >
    static char strawman( test_it< R (C::*)(A...) const, &C::dimensions >* ) ;

    typedef char two_chars[2] ; template < typename C > static two_chars& strawman(...) ;

    constexpr static bool value = ( sizeof( strawman<T>(nullptr) ) == sizeof(char) ) ;
};


struct dimensionSet {} ;

struct A
{
    dimensionSet dimensions() const ;
    void dimensions(dimensionSet) ;
    const dimensionSet& dimensions(int) const ;
    void dimensions(char,int,int) ;
    int dimensions(char,int,int) const ;
} ;

int main()
{
    std::cout << std::boolalpha ;

    // there is a dimensionSet A::dimensions() const
    std::cout << has_dimensions<A,dimensionSet>::value << '\n' ; // true

    // there is a void A::dimensions(dimensionSet) const
    std::cout << has_dimensions<A,void,dimensionSet>::value << '\n' ; // false

    // there is a dimensionSet A::dimensions(int,int) const
    std::cout << has_dimensions<A,dimensionSet,int,int>::value << '\n' ; // false

    // there is a int A::dimensions(char,int,int) const
    std::cout << has_dimensions<A,int,char,int,int>::value << '\n' ; // true

    // there is a const dimensionSet& A::dimensions(int) const
    std::cout << has_dimensions<A,const dimensionSet&,int>::value << '\n' ; // true

    // there is a void A::dimensions() const
    std::cout << has_dimensions<A,void>::value << '\n' ; // false
}
Topic archived. No new replies allowed.