3

I'm trying to write a smart pointer wrapper (contains a boost shared_ptr or scoped_ptr or another smart pointer wrapper); each wrapper type injects a bit of extra functionality (eg. logging usage, lazy init, verifying correct construction/destruction order, etc) but I want them to be as invisible as possible to the user (such that I could swap in/out wrappers simply by changing a single typedef, and possibly some constructor code, but none of the usage code).

Normal usage is trivial:

template<typename T>
class some_smart_ptr_wrapper
{
public:
    typedef typename T::value_type value_type;
    ...
    value_type* get() { /*do something magic*/ return m_ptr.get(); }
    const value_type* get() const { /*do something magic*/ return m_ptr.get(); }
    // repeat for operator->, operator*, and other desired methods
private:
    T m_ptr;
};
typedef some_smart_ptr_wrapper< boost::shared_ptr<int> > smart_int;
smart_int p;

(Now all other code can use p indistinguishably from a shared_ptr, at least for the defined operations, and I can add extra wrappers just by changing the typedef.)

Multiple wrappers can be used simultaneously simply by nesting them as you'd expect, and only the code that declares (and sometimes initialises) the variable needs to care.

Occasionally though it's useful to be able to get the "basic" shared/scoped_ptr from a wrapper (particularly to pass a shared_ptr as an argument to a function that doesn't need to trigger the functionality added by the wrappers); for a single wrapper layer it's easy:

    T& getPtr() { return m_ptr; }
    const T& getPtr() const { return m_ptr; }

But this doesn't scale nicely to multiple wrapper layers (the caller has to do p.getPtr().getPtr().getPtr() the correct number of times).

What I'd like to be able to do is to declare getPtr() such that:

  • if T implements getPtr(), then it returns m_ptr.getPtr() (whatever type that is).
  • if T does not implement getPtr(), then it returns m_ptr.
  • it's still available in both const and non-const flavours, as you'd expect.

The net result of which is that a single call to getPtr() from the outside code will "walk up" the chain to the original smart pointer, since that will be the only one that doesn't implement getPtr().

I'm fairly sure that the solution will involve SFINAE and boost::enable_if; I've had a bit of a play towards trying to get something like that to work but haven't had much luck thus far.

Solutions or completely alternate approaches both welcomed; note that I would like it to work in both VC++2008 and GCC (ie. no C++11, sadly).

Miral
  • 12,637
  • 4
  • 53
  • 93
  • `value_type` should be `element_type` and you should only have one `get` member: `element_type* get() const;`. A const pointer does not mean the pointee is const. – Jonathan Wakely May 29 '12 at 09:18

3 Answers3

1

(1) Declare a special member typename which is unique inside:

template<typename T>
class some_smart_ptr_wrapper
{
public:
  typedef T smart_type;  // <-- unique name
...
};

(2) Wrap getPtr() inside a template function wrapper:

template<typename T>
class some_smart_ptr_wrapper
{
...
public:
  T& getPtr () { return m_ptr; }

  typename My_sfinae<T>::RealType& getFinalPtr ()
  { return My_sfinae<T>::getPtr(m_ptr); }
...
};

(3) Where My_sfinae is implemented as usual:

template<typename T>
struct void_ { typedef void type; };

template<typename T, typename = void>
struct My_sfinae {
  typedef T RealType;

  static T& getPtr (T &p) { return p; }
};                        //^^^^^^^^^ business logic!
template<typename T>
struct My_sfinae<T, typename void_<typename T::smart_type>::type> {
  typedef typename My_sfinae<typename T::smart_type>::RealType RealType;

  static RealType& getPtr (T &p)
  { return My_sfinae<typename T::smart_type>::getPtr(p.getPtr()); }
};

As you can see, My_sfinae removes all your wrappers recursively and finally you are left with the final m_ptr.

Here is the demo.

iammilind
  • 68,093
  • 33
  • 169
  • 336
  • 2
    Nice solution. I would prefer a traits class rather than an intrusive modification in general (because it allows tweaking behaviors on classes you don't own) but the core principle of SFINAE is indeed a good idea. – Matthieu M. May 29 '12 at 09:14
  • I don't see how this could work. Remember, in the shared_ptr T is the actual type, in the first-level wrapper T is the shared_ptr, in the second-level wrapper T is the first_level_wrapper>, etc. Calling getPtr() on the second-level wrapper should return the shared_ptr, which is NOT the T of that wrapper type. – Miral May 29 '12 at 23:15
  • @Miral, I have mentioned in the code that you need to use `getFinalPtr()` instead of `getPtr()`. You can rename this methods to get the desired output. – iammilind May 30 '12 at 02:43
  • My point is that the return value of `getFinalPtr()` cannot possibly be T unless there's only one layer of wrappers, in which case it's not required at all. If I call `p.getFinalPtr()` where p has type `wrapper3>>>` it must return a `shared_ptr&`. – Miral May 31 '12 at 03:43
  • @Miral, After many efforts, edited my answer with the working solution. Check it that along with the demo. It has been compiled with c++0x just to support `std::shared_ptr<>`, otherwise the features used are c++03. Let me know if still facing problems. – iammilind May 31 '12 at 06:49
1

It is, actually, a simple problem: just use Overloading :)

template <typename T>
boost::scoped_ptr<T>& get_pointer(boost::scoped_ptr<T>& sp) { return sp; }

template <typename T>
boost::shared_ptr<T>& get_pointer(boost::shared_ptr<T>& sp) { return sp; }

// One overload per "wrapper"
template <typename T>
typename get_pointer_type<T>::type& get_pointer(some_wrapper<T>& p) {
    return get_pointer(p.getPtr());
}

The trick is then to correctly specialize get_pointer_type for each wrapper.

template <typename T>
struct get_pointer_type { typedef T type; };

template <typename T>
struct get_pointer_type< some_wrapper<T> > {
    typedef typename get_pointer_type<T>::type type;
};
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • Easiest and best solution for C++03, +1. – Xeo May 29 '12 at 09:17
  • Btw, [here](http://ideone.com/QQauU)'s a much nicer C++11 version that even feels a bit like Haskell's pattern matching. Note that GCC fails to compile in that version, but Clang 3.1 compiles just fine, which [seems to be the intention](http://stackoverflow.com/questions/3744400/trailing-return-type-using-decltype-with-a-variadic-template-function#comment3959283_3744400) since a user-defined type is involved. – Xeo May 29 '12 at 09:48
  • @Xeo: don't know about easiest (as it requires both one overload and one specialization per wrapper), I quite like iamilind's solution too; but I do agree that `decltype` is just magic :) – Matthieu M. May 29 '12 at 10:15
  • Though it seems GCC 4.7 chokes on that one, I'll reconfirm if it's actually allowed to work that way. :( – Xeo May 29 '12 at 11:18
  • I don't want to have to explicitly spell out each supported pointer type, though, and I don't want to use a free function; I'd prefer a member function. – Miral May 29 '12 at 22:52
0

The closest thing that comes to my mind is the paper written by Bjarne Stroustrup called Wrapping C++ Member Function Calls.

This paper presents a simple, general, and efficient solution to the old problem of ‘‘wrapping’’ calls to an object in pairs of prefix and suffix code. The solution is also non-intrusive, applies to existing classes, allows the use of several prefix/suffix pairs, and can be implemented in 15 simple lines of Standard C++. A robust version of the wrapper is also presented. The claim of efficiency is backed by measurement.

Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
  • 1
    I don't really see how this helps the OP, maybe a bit of example code would help? – Xeo May 29 '12 at 08:56
  • I can only suggest looking harder. – Maxim Egorushkin May 29 '12 at 08:57
  • Then please enlighten me, as the OP is not looking for a way to wrap function calls with prefix and suffix code (unless I completely misunderstood the question), he wants to call a certain functions recursively until he arrives at the base type. – Xeo May 29 '12 at 08:59
  • The first half of my question was about wrapping a class with some prefix-execution code, which that paper is relevant for -- however while it's more generic than what I posted in the question it doesn't really help since I'm trying to intercept calls to the smart pointer itself, not the object being pointed to. (And while it might be possible to make that work, it would break current usage and the "smart pointer" concept.) The second half was about collapsing the call tree to obtain the underlying smart pointer, which is the more interesting problem, and I can't see this helping with that. – Miral May 29 '12 at 23:08