Jump to content
  • Advertisement
Sign in to follow this  
Julian90

Determining if a type has a member function

This topic is 4255 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hey everyone, I'm a bit stuck at the moment, I was able to adapt a technique shown by washu here to find the out weather a type T contains a member function with a given signature as follows....
// Tests if a type T has a member function with signature Sig
// T   - the type to test
// Sig - the signature of the function
template<typename T, typename Sig>
struct HasFoo
{
private:
    // ToMemFuncPtr simply takes the object type and signature
    // and combines them to produce a memfunc ptr
    // i.e. ToMemFuncPtr<MyObj, int (float)>::type will be
    //  int (MyObj::*)(float)
    template<ToMemFuncPtr<T, Sig>::type funcPtr>
    struct wrap { };

    template<typename T>
    static one Test(wrap<&T::Foo>*);

    template<typename T>
    static two Test(...);

public:
    enum { Value = sizeof(Test<T>(0)) == sizeof(one) };
};
However instead of this I now need to determine if T has a member function (Foo) with an overload such that the return type can be implicitly converted to the return type of Sig and the argument types specified in Sig can be implicitly converted to those of the Foo overload. So I guess what I'm after is any ideas on how to do this or links to related resources, all helps is greatly apreciated and thanks in advance.

Share this post


Link to post
Share on other sites
Advertisement
This isn't possible, unfortunately. Lies, lies and treachery! SFINAE can't be used to test the existance of member values (of which a function pointer to any specific function is), only member types.

My usual approach is to have a policy type which:

1) Determines if a given type "should" have the appropriate member function based on the existance of typedefs
2) Cause a compile failure should those types be absent (to avoid mistaken false positives)
3) Allow specialization for cases where all the types should be present, but not the member function (to allow correction of mistaken false positives).

Random snippet from libindustry (illustrating all of the above):

namespace industry {
namespace detail {
template < typename C > static sfinae::one is_a_container_helper
( typename C::container_category* = 0
);
template < typename C > static sfinae::one is_a_container_helper
/* If you've got an error for any of the following lines, it's likely because one of the typedefs
* of C was unexpectedly a reference. Please specialize industry::is_a_container< C >, with the
* static boolean ::value representing wheither or not C is in fact a container. Thanks!
*/

( typename C::value_type* = 0
, typename C::iterator* = 0
, typename C::const_iterator* = 0
, typename boost::remove_reference< typename C::reference >::type* = 0
, typename boost::remove_reference< typename C::const_reference >::type* = 0
, typename C::pointer* = 0
, typename C::difference_type* = 0
, typename C::size_type* = 0
/* If you've got an error for any of the following lines, it's because you have all the typedefs
* of a container, but are missing one of the required functions, or it is of an unexpected type.
* please specialize industry::is_a_container< C >, with the static boolean ::value representing
* wheither or not C is in fact a container. Thanks!
*/

, void (C::*)() = & C::clear
, typename C::iterator (C::*)() = & C::begin
, typename C::iterator (C::*)() = & C::end
, typename C::const_iterator (C::*)() const = & C::begin
, typename C::const_iterator (C::*)() const = & C::end
/* End of function signature check area */
);
template < typename C > static sfinae::two is_a_container_helper( ... );
}
template < typename Container > struct is_a_container {
static const bool value = (sizeof(sfinae::one)
== sizeof(detail::is_a_container_helper<typename boost::remove_const<Container>::type >(0))
);
};
template < typename Iterator > class range;
template < typename Iterator > struct is_a_container< range< Iterator > > { static const bool value = false; };
template < typename Iterator > struct is_a_container< const range< Iterator > > { static const bool value = false; };
...
}




This code gave me enough compiler-differences hassle that I'll iterate off a few tips:

1) Even though I'm using defaulting arguments for everything, the explicit parameter 0 is required for GCC's SFINAE to work correctly -- without it, the elipsis (...) function will cause ambiguity errors (whereas if a paramter is passed, GCC correctly treats said function as a last restort).

2) Unlike VS, GCC will filter out non-const member functions if T is const. Compiler errors will occur on GCC if detail::is_a_container_helper is passed const std::vector<int>, for example, because only the const overloads of T::begin will be considered, none of which are convertable to the non-const signature provided. I'm not sure which is the correct behavior here. Hence the boost::remove_const in industry::is_a_container's implementation.

3) The boost::remove_reference bit is pretty much explained in the comment preceeding that section. Pointers to references are illegal, that is.

[Edited by - MaulingMonkey on February 1, 2007 12:02:28 AM]

Share this post


Link to post
Share on other sites
SFINAE allows you to determine type information at compile-time through the usage of overload resolution. However, it cannot prevent non-type syntax errors. An example of which you have demonstrated in your code. The class member T::Foo does not exist, nor is it a type, as such SFINAE does not apply here.

Unfortuantly, there is no real way to ascertain if a particular generic object implements a set of functions except through its respective inheritance trees. If such information is impossible to test for (either because you're dealing with standard containers, unrelated containers, or other) then you really have no other recourse but to fall back to methods such as those that MaulingMonkey described. This has been a particular problem in our efforts on libindustry.

Share this post


Link to post
Share on other sites
Quote:
This isn't possible, unfortunately. SFINAE can't be used to test the existance of member values (of which a function pointer to any specific function is), only member types.
Quote:
SFINAE allows you to determine type information at compile-time through the usage of overload resolution. However, it cannot prevent non-type syntax errors. An example of which you have demonstrated in your code. The class member T::Foo does not exist, nor is it a type, as such SFINAE does not apply here.
I'm not sure I understand you properly here for example couldnt the following be considered "testing the existance of member values"


#include <iostream>

struct SFINAE
{
typedef struct { char c[1]; } One;
typedef struct { char c[2]; } Two;
};

template<typename T>
struct HasIntValue
{

template<int T::*>
struct Wrap {};


template<typename T>
static SFINAE::One Determine(Wrap<&T::Value>* w);

template<typename T>
static SFINAE::Two Determine(...);

public:
enum { Value = sizeof(Determine<T>(0)) == sizeof(MyLib::Interfaces::Selector::One) };
};

struct T1
{
int Value;
};

struct T2
{
};

struct T3
{
float Value;
};

int main()
{
std::cout << HasIntValue<T1>::Value; // outputs 1 since T1 has a member Value
std::cout << HasIntValue<T2>::Value; // outputs 0 since T2 doesn't have a member Value
std::cout << HasIntValue<T3>::Value; // outputs 0 since T3 has a member Value BUT it isn't an int
return 0;
}




Quote:
My usual approach is to have a policy type which:

1) Determines if a given type "should" have the appropriate member function based on the existance of typedefs
2) Cause a compile failure should those types be absent (to avoid mistaken false positives)
3) Allow specialization for cases where all the types should be present, but not the member function (to allow correction of mistaken false positives).

Random snippet from libindustry (illustrating all of the above):

<snip>


Thx for the sugestion, I've got one more idea for how to do what I need but if that doesnt work I'll give this approach a try.

Quote:
This code gave me enough compiler-differences hassle that I'll iterate off a few tips:

<snip>


Thanks for the warning, that would have been quite anoying as i only have VS available to test things on at the minute

Quote:
Unfortuantly, there is no real way to ascertain if a particular generic object implements a set of functions except through its respective inheritance trees. If such information is impossible to test for (either because you're dealing with standard containers, unrelated containers, or other) then you really have no other recourse but to fall back to methods such as those that MaulingMonkey described. This has been a particular problem in our efforts on libindustry.


If this is the case then is there anyway to determine what types a given type can be implicitly converted to and implicitly constructed from?

Edit: Had a mistake in my example

Share this post


Link to post
Share on other sites
Quote:
Original post by Julian90
Quote:
This isn't possible, unfortunately. SFINAE can't be used to test the existance of member values (of which a function pointer to any specific function is), only member types.
Quote:
SFINAE allows you to determine type information at compile-time through the usage of overload resolution. However, it cannot prevent non-type syntax errors. An example of which you have demonstrated in your code. The class member T::Foo does not exist, nor is it a type, as such SFINAE does not apply here.
I'm not sure I understand you properly here for example couldnt the following be considered "testing the existance of member values"

*** Source Snippet Removed ***

Quote:
My usual approach is to have a policy type which:

1) Determines if a given type "should" have the appropriate member function based on the existance of typedefs
2) Cause a compile failure should those types be absent (to avoid mistaken false positives)
3) Allow specialization for cases where all the types should be present, but not the member function (to allow correction of mistaken false positives).

Random snippet from libindustry (illustrating all of the above):

<snip>


Thx for the sugestion, I've got one more idea for how to do what I need but if that doesnt work I'll give this approach a try.

Quote:
This code gave me enough compiler-differences hassle that I'll iterate off a few tips:

<snip>


Thanks for the warning, that would have been quite anoying as i only have VS available to test things on at the minute

Quote:
Unfortuantly, there is no real way to ascertain if a particular generic object implements a set of functions except through its respective inheritance trees. If such information is impossible to test for (either because you're dealing with standard containers, unrelated containers, or other) then you really have no other recourse but to fall back to methods such as those that MaulingMonkey described. This has been a particular problem in our efforts on libindustry.


If this is the case then is there anyway to determine what types a given type can be implicitly converted to and implicitly constructed from?

Edit: Had a mistake in my example


Actually, you've raised an interesting point... the following also works..

#include <iostream>

struct SFINAE
{
typedef char one;
typedef struct { char c[2]; } two;
};

template<typename T>
struct HasF
{

template<void (T::*)()>
struct Wrap {};


template<typename T>
static SFINAE::one Determine(Wrap<&T::F>* w);

template<typename T>
static SFINAE::two Determine(...);

public:
enum { Value = sizeof(Determine<T>(0)) == sizeof(SFINAE::one) };
};

struct T1
{
void F() {}
};

struct T2
{
};

struct T3
{
void F() {}
};

int main()
{
std::cout << HasF<T1>::Value;
std::cout << HasF<T2>::Value;
std::cout << HasF<T3>::Value;
return 0;
}

Share this post


Link to post
Share on other sites
That'll teach me to make sweeping generalizations based on hours upon hours of investigation and consulting with the most knowledgable person of the C++ language that I know.

(And people ask me why I hate C++)

Share this post


Link to post
Share on other sites
Quote:
Original post by MaulingMonkey
That'll teach me to make sweeping generalizations based on hours upon hours of investigation and consulting with the most knowledgable person of the C++ language that I know.

(And people ask me why I hate C++)

Yeah, not my fault [grin].

Share this post


Link to post
Share on other sites
For the record, I'm retroactively justifying leaving it as is on the grounds that if it has all the typedefs to match that signature, there's a good chance that it's supposed to be a container, and defaulting to errors may be a better idea than simply silently failing to match :P.

Share this post


Link to post
Share on other sites
Quote:
Actually, you've raised an interesting point... the following also works..

<snip>

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.


Yep, it does, and the snippet in my first post is a generalization of this, however, it doesnt quite do what i need. For example consider the folloring....

struct Test1
{
void F(float);
};

struct Test2
{
void F(int);
};

int main()
{
// works like a charm
std::cout << HasF<Test1, void (float)>::Value;
// falls apart horibly :(, ok well it doesnt exactly fall apart
// but it doesnt do what i need either, this will print 0 because
// Test2::F takes an int but i need to make it print 1 because
// an int is implicitly convertable to a float
std::cout << HasF<Test2, void (float)>::Value;
}


Quote:
That'll teach me to make sweeping generalizations based on hours upon hours of investigation and consulting with the most knowledgable person of the C++ language that I know.

(And people ask me why I hate C++)


So why do you hate C++? ;)

Quote:
If this is the case then is there anyway to determine what types a given type can be implicitly converted to and implicitly constructed from?


Any ideas on this as if i could get this information then i could test for each posible signiture and "or" the results together to get the desired result.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!