views:

1348

answers:

6

Hi, I'm asking for a template trick to detect if a class has a specific member function of a given signature.

The problem is similar to the one cited here http://www.gotw.ca/gotw/071.htm but not the same: in the item of Sutter's book he answered to the question that a class C MUST PROVIDE a member function with a particular signature, else the program won't compile. In my problem I need to do something if a class has that function, else do "something else".

A similar problem was faced by boost::serialization but I don't like the solution they adopted: a template function that invokes by default a free function (that you have to define) with a particular signature unless you define a particular member function (in their case "serialize" that takes 2 parameters of a given type) with a particular signature, else a compile error will happens. That is to implement both intrusive and non-intrusive serialization.

I don't like that solution for two reasons: 1) to be non intrusive you must override the global "serialize" function that is in boost::serialization namespace, so you have IN YOUR CLIENT CODE to open namespace boost and namespace serialization!! And a second, practical reason, is because the stack to resolve that mess was 10 to 12 function invocation... and I'm a game developer.

I need to define a custom behavior for classes that has not that member function, and my entities are inside different namespaces (and I don't want to override a global function defined in one namespace while I'm in another one)

Can you give me an hint to solve this puzzle?

EDIT: @Chris Jester-Young I know well Koenig lookup. In fact I was surprised of what they did in documentation (http://www.boost.org/doc/libs/1_36_0/libs/serialization/doc/index.html)

why open the boost::serialization namespace?

but you are not answering to my question: I don't want to do the same thing of boost::serialization. Maybe I decide to do "nothing" if the class has not that function with that signature. in boost::serialization if you don't have that member function OR if you don't override global "serialize" function... compile error! I don't want this.

EDIT: @Tom Leys I'm sorry that's not what I expected as answer. What you suggest me is not what I want. If you read the link to the gotw site (old site of Herb Sutter) you'll discover that your solution is both intrusive (and I don't want) and it doesn't solve the problem with the signature and it doesn't solve the fact that I can accept classes that don't have that member function... It's a bit more tricky.

+1  A: 

To be non-intrusive, you can also put serialize in the namespace of the class being serialised, or of the archive class, thanks to Koenig lookup. See Namespaces for Free Function Overrides for more details. :-)

Opening up any given namespace to implement a free function is Simply Wrong. (e.g., you're not supposed to open up namespace std to implement swap for your own types, but should use Koenig lookup instead.)

Chris Jester-Young
+1  A: 

Okay. Second try. It's okay if you don't like this one either, I'm looking for more ideas.

Herb Sutter's article talks about traits. So you can have a traits class whose default instantiation has the fallback behaviour, and for each class where your member function exists, then the traits class is specialised to invoke the member function. I believe Herb's article mentions a technique to do this so that it doesn't involve lots of copying and pasting.

Like I said, though, perhaps you don't want the extra work involved with "tagging" classes that do implement that member. In which case, I'm looking at a third solution....

Chris Jester-Young
eh... I've analyzed this solution... I think it's a little bit too expensive for users of my framework. (ok, I admit, I'm developing a streaming framework and I'm choosing between extending iostream or rewriting something easier)
ugasoft
but I thank you for your suggestion :)
ugasoft
My third solution would be to use SFINAE. Since yrp's answer already mentions it, I won't go into it (because I'm still researching on it: I know the idea, but the devil is in the details), unless his solution doesn't work for you in the end. :-)
Chris Jester-Young
+18  A: 

I'm not sure if I understand you correctly, but you may exploit SFINAE to detect function presence at compile-time. Example from my code (tests if class has member function size_t used_memory() const).

template<typename T>
struct HasUsedMemoryMethod
{
 template<typename U, size_t (U::*)() const> struct SFINAE {};
 template<typename U> static char Test(SFINAE<U, &U::used_memory>*);
 template<typename U> static int Test(...);
 static const bool Has = sizeof(Test<T>(0)) == sizeof(char);
};

template<typename TMap>
void ReportMemUsage(const TMap& m, rde::int_to_type<true>)
{
        // We may call used_memory() on m here.
}
template<typename TMap>
void ReportMemUsage(const TMap&, rde::int_to_type<false>)
{
}
template<typename TMap>
void ReportMemUsage(const TMap& m)
{
 ReportMemUsage(m, 
  rde::int_to_type<HasUsedMemoryMethod<TMap>::Has>());
}
yrp
wtf is this??? is it legal c++ code?? can you write "template<typename U, size_t (U::*)() const>"??but... it's a great and new solution! I thank you, I'll analyze better tomorrow with my collegues... great!
ugasoft
Richard Corden
A simple definition of int_to_type could be: 'template <int N> struct int_to_type {};'. Many implementations keep the paramter N value either in an enum or else in a static integer constant (template <int N> struct int_to_type { enum { value = N }; }; / template <int N> struct int_to_type { static const int value = N; })
David Rodríguez - dribeas
When using C++0x you can simply return the type you want and write "decltype(Test<T>(0))" whenever you need the type.
tstenner
Simply take boost::integral_constant instead of int_to_type.
Vadim Ferderer
+4  A: 

This should be sufficient, if you know the name of the member function you are expecting. (In this case, the function bla fails to instantiate if there is no member function (writing one that works anyway is tough because there is a lack of function partial specialization. You may need to use class templates) Also, the enable struct (which is similar to enable_if) could also be templated on the type of function you want it to have as a member.

template <typename T, int (T::*) ()> struct enable { typedef T type; };
template <typename T> typename enable<T, &T::i>::type bla (T&);
struct A { void i(); };
struct B { int i(); };
int main()
{
  A a;
  B b;
  bla(b);
  bla(a);
}
coppro
thaks! it's similar to the solution proposed by yrp. I didn't know that template can be templated over member functions. That's a new feature I've learned today! ... and a new lesson: "never say you are expert on c++" :)
ugasoft
A: 

I believe the answer you are looking for is here.

http://www.martinecker.com/wiki/index.php?title=Detecting_the_Existence_of_Operators_at_Compile-Time

and a slightly more filled out example here

http://pastie.org/298994

I use the technique to detect the presence of a supporting ostream operator << on the class in question and then generate a different bit of code depending.

I didn't believe it was possible before finding the linked solution but it is a very neat trick. Spend the time understanding the code and it is very worth the while.

Brad

A: 

MS also has a non-standard, but easy, partial solution - but only if you can settle for a symbol name, not a complete signature.

Ofek Shilon