37

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.

Community
  • 1
  • 1
Azoth
  • 1,652
  • 16
  • 24
  • This may or may not help: http://bloglitb.blogspot.ca/2010/07/access-to-private-members-thats-easy.html – Yakk - Adam Nevraumont Mar 06 '14 at 20:07
  • 1
    I have one generic implementation and I could check whether it works, but, to be certain, I would need a prototype example of a class having this method and exhibiting all requirements (1-4) to work with. – iavr Mar 08 '14 at 00:29
  • The problem with private access hack is that it requires that the return type be known - see the line `struct Af { typedef void(A::*type)(); };`. In the situation I describe the return type of `shared_from_this` is unknown. – Azoth Mar 09 '14 at 06:53
  • I've added some examples of structs and what the expected behavior of such a type trait should be. – Azoth Mar 09 '14 at 06:58
  • 4
    If it's a final class and if it's protected, do you care? You can't access the method, period. – David Hammen Mar 09 '14 at 07:03
  • I'm not actually doing this to get access to the method itself - I'm trying to get access to the return type of `shared_from_this`, which will let me know the class that `std::enable_shared_from_this` was templated on (it returns a `shared_ptr` to the templated type). This is necessary for a serialization library to properly restore the state of the internal `weak_ptr` held by `enable_shared_from_this`. – Azoth Mar 09 '14 at 07:08
  • @Azoth I checked my implementation, it's quite different than yours ([here](https://github.com/iavr/ivl2/blob/master/include/ivl/root/core/type/traits/members.hpp) by the way, originating from ideas [here](http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Member_Detector)) but still attempts to derive from the class in question, so does not apply in your case, sorry. – iavr Mar 11 '14 at 01:32
  • Interestingly VC2013 compiles without needing "friend" – Jon Mar 12 '14 at 11:01
  • Do you actually need compile time testing, or a runtime check would work ? – xryl669 Mar 20 '14 at 17:54
  • Needs to happen at compile time. – Azoth Mar 21 '14 at 02:00

3 Answers3

1

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.

Alexander Oh
  • 24,223
  • 14
  • 73
  • 76
0

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!
edwinc
  • 1,658
  • 1
  • 14
  • 14
  • 1
    Detecting whether a type is a `shared_ptr` is not an issue and not what the question is after. The difficult task here is determining 1) if a class derives from `enable_shared_from_this` and 2) figuring out the true type of `T`. The best way to do this (that I know of) is to look at the return type of `shared_from_this()`, which will return a `shared_ptr`, from which `T` can easily be extracted. It is detecting whether this function exists under the conditions I outline in the question that is the true challenge. – Azoth May 10 '14 at 03:06
-1

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.

ibizaman
  • 3,053
  • 1
  • 23
  • 34
  • 1
    The end goal is to serialize any user defined type that inherits from `std::enable_shared_from_this`. It is not feasible to require users to use a special wrapper like this, even though it does trivially solve the problem assuming that the typedef is publicly visible. – Azoth Mar 16 '14 at 23:25
  • I edited my post. You said "(specifically shared_from_this(), which is inherited from std::enable_shared_from_this)" so I supposed that all the serializable classes have a common ancestor that you control. – ibizaman Mar 17 '14 at 17:20
  • 1
    No - I have no control over the hierarchy of the users inheritance. I only want to detect times when they inherit from `std::enable_shared_from_this` and ultimately deduce `T` through the `shared_from_this()` function. – Azoth Mar 17 '14 at 18:43