Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Refactoring c++ template class based on template type

Given class Foo

template <typename T>
class Foo
{
public:

  ...other methods..

  void bar()
  {
    ...
    m_impl.doSomething();
    ...
  }

  void fun()
  {
    ...
    m_impl.doSomethingElse();
    ...
  }

  void fubar()
  {
    ...
  }

private:
  T m_impl;
};

I wanted to cater for situations where T is a boost::shared_ptr. In this case the only change to class Foo is that it should invoke

m_impl->doSomething();

instead of

m_impl.doSomething();

I ended up defining FooPtr in the same header

template <typename T>
class FooPtr
{
public:
  ...other methods..

  void bar()
  {
    ...
    m_pImpl->doSomething();
    ...
  }

  void fun()
  {
    ...
    m_pImpl->doSomethingElse();
    ...
  }

  void fubar()
  {
    ...
  }

private:
  boost::shared_ptr<T> m_pImpl;
};

Now while the approach works for all classes that I want to use with Foo, the problem is that I have a lot of duplicate code lying around and any changes I make to Foo, I also have to make to FooPtr.

How can I refactor the code? E.g. Is there any way that I can determine at compile time if T is of type boost::shared_ptr, and then specialise just the bar and fun methods to invoke the -> operator?

Edit: Thanks for all the answers so far! I just need some time to work through them all and see which solution is the best fit for our software.

Edit 2: @Matthieu: This is the test code I was using

class FooImpl
{
public:
  void doIt()
  {
    cout << "A" << std::endl;
  }
};

int _tmain(int argc, _TCHAR* argv[])
{
  Foo<FooImpl> foo;
  foo.doSomething();
  return 0;
}
like image 837
Ralf Avatar asked Dec 14 '25 12:12

Ralf


1 Answers

Sylvain wrote a DRY solution, but I don't like abusing inheritance.

Using a wrapper class to uniformize the interface is easy, especially since pointer semantics work so well!

namespace details {
  template <typename T>
  struct FooDeducer {
    typedef boost::optional<T> type;
  };

  template <typename T>
  struct FooDeducer< T* > {
    typedef T* type;
  };

  template <typename T>
  struct FooDeducer< boost::shared_ptr<T> > {
    typedef boost::shared_ptr<T> type;
  };
} // namespace details

template <typename T>
class Foo {
public:
  // methods
  void doSomething() { impl->doIt(); }

private:
  typedef typename details::FooDeducer<T>::type Type;
  Type impl;
};

Here, relying on boost::optional which provides the OptionalPointee semantics, we nearly get the same behavior than pointers.

One point I'd like to emphasize though, is the difference in the copying behavior. boost::optional provides deep copy.

like image 81
Matthieu M. Avatar answered Dec 17 '25 01:12

Matthieu M.