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;
}
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.