Checking for existence of C++ member function, possibly protected

I'm trying to detect whether a class has a particular function (specifically shared_from_this(), which is inherited from std::enable_shared_from_this<Some Unknown Class>). To make things more complicated, I need to know whether it has this function even if it has been inherited from a distant base class or inherited using protected access.

I've looked at other questions such as this one, but the methods supplied do not work for detecting protected member functions.

The current method I am using is the following:

template <class T>
struct shared_from_this_wrapper : public T
{
  template <class U>
  static auto check( U const & t ) -> decltype( t.shared_from_this(), std::true_type() );

  static auto check( ... ) -> decltype( std::false_type() );
};

template<class T>
struct has_shared_from_this : decltype(shared_from_this_wrapper<T>::check(std::declval<shared_from_this_wrapper<T>>()))
{ };

The flaw in my current solution is that it does not work with classes declared final. So I am after a solution for testing for a member function that satisfies:

  1. Works with classes declared final
  2. Works with protected member functions
  3. Works with inheritance
  4. Does not need to know the return type of the function
  5. Compiles under gcc, clang, and MSVC 2013 (the last one potentially limiting overly fancy SFINAE)

Edit: I have a potential solution that works but requires befriending a helper class, which is also not an ideal solution but possibly a workaround for now (since it satisfies all requirements):

struct access
{
  template <class T>
  static auto shared_from_this( T const & t ) -> decltype( t.shared_from_this() );
};

template <class U>
static auto check( U const & t ) -> decltype( access::shared_from_this(t), std::true_type() );

static auto check( ... ) -> decltype( std::false_type() );

template<class T>
struct has_shared_from_this2 : decltype(check(std::declval<T>()))
{ };

struct A : std::enable_shared_from_this<A> {};
struct B : protected A { friend class access; };    

Another edit: examples of classes and what a type trait checking for the existence of something like shared_from_this should return:

struct A : std::enable_shared_from_this<A> {}; // should return true
struct B final : protected A {}; // should return true
struct C : A {}; // should return true
struct D {}; // should return false

I should mention that my final goal in detecting whether this function exists is to determine the return type of it in order to figure out the type on which std::enable_shared_from_this was templated. Inheriting from std::enable_shared_from_this<T> gives you std::shared_ptr<T> shared_from_this(), and T is ultimately what I need to figure out. This is necessary for proper serialization of types that inherit from std::enable_shared_from_this.

Edit part 3: The editing:

This is being done for the serialization library cereal and as such I have zero control over how a user wants to design their class. I would like to be able to serialize any user type that derives from std::enable_shared_from_this, which includes users that declare their classes as either final or use protected inheritance somewhere along the way. Any solution that requires meddling with the actual type that is checked is not a valid solution.

Answers


I've put some thoughts on how to implement the things you requested and came to a totally different conclusion.

The problem at hand is very interesting: How do I check whether a class implements a hidden interface. Unfortunately the problem is a contradiction to the liskov substitution principle; one of the core object oriented principles.

This is partly due to the type structure of std::shared_ptr. shared_ptr does not reflect the inheritance relationship of its argument types. Given a class T and a class U, where class T : public U {}; holds shared_ptr<T> : public shared_ptr<U> {}; does not!

Your implementation has one fundamental flaw at the interface level. If you are looking at compile time whether a function exists and then extract the type you will only be able deserialize data structures that are use shared pointers.

Furthermore if std::shared_ptr gets deprecated or you want to use some other means to aquire memory (std::allocator interface? some region/pool allocation) you'll have to adapt your interfaces.

My personal opinion is to create some kind of factory interface and register it somewhere in the deserializer.

The second one would be to have a factory class that exposes an implicit template interface (and use CRTP to specialize the interface to the users needs. i.e.:

template <class ActualType, 
          class IfType=ActualType,
          class Allocator=default::allocator<ActualType>>
class Deserializable {
  static IfType alloc(ActualType &&t) {
    Allocator a; // choose  another interface as your please.
    return a.allocate(t); /* implement me */
  }
private:
};

class MyClass
 : public InterfaceClass,
   public Deserializable<MyClass,InterfaceClass> {
  /* your stuff here */
};
  • This gives you a reasonable amount of abstraction in your template classes.
  • The user of your library knows what he wants in return anyway. and if he chooses to allocate something else than a std::shared_ptr he could do (by creating his own Allocator)
  • The user doesn't have to implement anything but specify types (and actually passes them to you, so no second guesses).

You could interpret this as a policy class (not in the strict sense of Andrei Alexandrescu). The serialization library mandates an allocation policy. The user can decide how this policy is implemented. In this case a choice on how to allocate the deserialized object and the type, which could be different. Because the Allocator has a default implementation and is a template argument, another choice is passed to the user if desired.

In order to understand the power of this approach I welcome you to look at the code of boost::operator that uses this technique to specify the return type and arguments of arithmetic operators at compile time.

Note

For people also looking at this post for answers of the original problem I'd suggest to use this approach. However it requires the member to be public, because it checks for a member function pointer of a given name.


I am going to take the liberty of questioning the question. Not every object passed through a shared_ptr inherits from enable_shared_from_this.

Perhaps this would be what you are looking for or give some further ideas:

class Foo1 { };
class Foo2 : public std::enable_shared_from_this< Foo2 > {};
class Foo3  final : protected Foo2 {};

struct Serialize
{
    template <typename T>
    void write( T* ) {  printf( "not shared!\n" ); }

    template <typename T>
    void write( std::shared_ptr<T> ) { printf( "shared!\n" ); }
};

int test( )
{
    typedef std::shared_ptr<Foo2> Foo2Ptr;
    typedef std::shared_ptr<Foo3> Foo3Ptr;

    Serialize   s;
    Foo1*       pFoo1 = nullptr;
    Foo2Ptr     pFoo2;
    Foo3Ptr     pFoo3;
    s.write( pFoo1 );
    s.write( pFoo2 );
    s.write( pFoo3 );

    return 0;
}

At run time, the output is:

not shared!
shared!
shared!

If the only goal is to detect the type T, then I suggest you do like in the STL and add a typedef:

template <class T>
struct enable_shared_from_this : public T
{
    typedef T base_type;
    // ...
};

Then you can use it like such:

class A : enable_shared_from_this<B>
{
}

A::base_type // == B

This example assumes that you know that A inherits from the shared_from_this_wrapper.


Need Your Help

Making facebook login work with an Android Webview

javascript android facebook webview

I am just trying to implement the facebook login on a WebView in Android. The problem is after I click the facebook button on my HTML page and insert the username and password on Facebook dialog. T...

Escape ampersand in PL/SQL Developer

oracle escaping special-characters plsqldeveloper

I'm trying a simple INSERT statement against an Oracle database. One of the values is a VARCHAR2 field and the insert statement contains an ampersand. How do I do this? I've tried the following