I have class representing a tree object that uses unique pointers, some nodes that make up the tree, and a function that constructs pointers to an abstract node class based on some arguments (It makes pointers to subclasses, since abstract node is abstract)
class AbstractNode
{
vector<unique_ptr<AbstractNode>> children;
public:
AbstractNode(arguments...);
// other stuff...
};
class Tree
{
unique_ptr<AbstractNode> baseNode;
// other stuff...
}
unique_ptr<AbstractNode> constructNode(AbstractNodeTypes type);
There are a variety of subclasses of abstractNode that will be contained in the tree. The subclasses provide different implementations for some virtual functions in that class.
I want to be able to copy my tree by creating a new set of nodes with the same class types that are distinct copies of the nodes in the original tree.
Here is the problem:
If I write my own copy constructor for the AbstractNode class that deep copies the children, I'll have to write copy constructors for all of the subclasses of AbstractNode, which seems annoying since the only thing that won't copy correctly are the children pointers. It will also be annoying to use copy constructors here because I will need to cast the children to the correct types before I call them, I think.
Is there some way I can get the compiler to let me use the default copy constructor to set up everything except for the children. It can leave those as null pointers or something? Then I can write a simpler function that just recursively adds children to copy a tree.
If that is impossible, is there any non-ugly solution to this problem that anyone knows of?
The typical way to solve this problem is to have a virtual clone function similar to what Kerrek SB describes in his answer. However I would not bother writing your own value_ptr class. It is simpler to just reuse std::unique_ptr as your question presents. It will require a non-default copy constructor in AbstractNode, but does not require explicit or unsafe casting:
class AbstractNode
{
std::vector<std::unique_ptr<AbstractNode>> children;
public:
AbstractNode() = default;
virtual ~AbstractNode() = default;
AbstractNode(AbstractNode const& an)
{
children.reserve(an.children.size());
for (auto const& child : an.children)
children.push_back(child->clone());
}
AbstractNode& operator=(AbstractNode const& an)
{
if (this != &an)
{
children.clear();
children.reserve(an.children.size());
for (auto const& child : an.children)
children.push_back(child->clone());
}
return *this;
}
AbstractNode(AbstractNode&&) = default;
AbstractNode& operator=(AbstractNode&&) = default;
// other stuff...
virtual
std::unique_ptr<AbstractNode>
clone() const = 0;
};
Now a ConcreteNode can be implemented. It must have a valid copy constructor which may be defaulted depending upon what data members ConcreteNode adds to the mix. And it must implement clone(), but that implementation is trivial:
class ConcreteNode
: public AbstractNode
{
public:
ConcreteNode() = default;
virtual ~ConcreteNode() = default;
ConcreteNode(ConcreteNode const&) = default;
ConcreteNode& operator=(ConcreteNode const&) = default;
ConcreteNode(ConcreteNode&&) = default;
ConcreteNode& operator=(ConcreteNode&&) = default;
// other stuff...
virtual
std::unique_ptr<AbstractNode>
clone() const override
{
return std::unique_ptr<AbstractNode>(new ConcreteNode(*this));
}
};
I suggest having clone return a unique_ptr instead of a raw pointer just to ensure that there is no chance that a new'd pointer is ever exposed without an owner.
For completeness I've also shown what the other special members would look like.
At first I was thinking that C++14's make_unique would be nice to use here. And it can be used here. But personally I think in this particular example it really doesn't carry its weight. Fwiw, here is what it would look like:
virtual
std::unique_ptr<AbstractNode>
clone() const override
{
return std::make_unique<ConcreteNode>(*this);
}
Using make_unique you have to first construct a unique_ptr<ConcreteNode>, and then rely on the implicit conversion from that to unique_ptr<AbstractNode>. This is correct, and the extra dance probably will get optimized away as soon as inlining is fully enabled. But the use of make_unique just seems like an unnecessary obfuscation here when what you really clearly need is a unique_ptr<AbstractNode> constructed with a new'd ConcreteNode*.
Instead of using unique_ptr, you may wish to roll your own implementation of a value_ptr. Those designs have been proposed regularly in the past, but until we have a standardized version, either roll your own or find an existing implementation.
It would look a bit like this:
template <typename T> struct value_ptr
{
T * ptr;
// provide access interface...
explicit value_ptr(T * p) noexcept : ptr(p) {}
~value_ptr() { delete ptr; }
value_ptr(value_ptr && rhs) noexcept : ptr(rhs.ptr)
{ rhs.ptr = nullptr; }
value_ptr(value_ptr const & rhs) : ptr(rhs.clone()) {}
value_ptr & operator=(value_ptr && rhs) noexcept
{
if (&rhs != this) { delete ptr; ptr = rhs.ptr; rhs.ptr = nullptr; }
return *this;
}
value_ptr & operator=(value_ptr const & rhs)
{
if (&rhs != this) { T * p = rhs.clone(); delete ptr; ptr = p; }
return *this;
}
};
You can build your tree from a set of clonable nodes.
struct AbstractNode
{
virtual ~AbstractNode() {}
virtual AbstractNode * clone() const = 0;
std::vector<value_ptr<AbstractNode>> children;
};
struct FooNode : AbstractNode
{
virtual FooNode * clone() const override { return new FooNode(this); }
// ...
};
Now your nodes are copyable automatically without the need for you to write any explicit copy constructors. All you need to do is to maintain discipline by overriding clone in every derived class.
The normal pattern for this is a virtual clone method through your hierarchy.
If that is impossible, is there any non-ugly solution to this problem that anyone knows of?
You can also use template instantiation of a clone function based on copy constructors. Here's a solution I use in a web server I'm writing (pet project):
#pragma once
#include <memory>
#include <cassert>
#include <functional>
#include <stdexcept>
#include <vector>
namespace stdex {
inline namespace details {
/// #brief Deep copy construct from (Specialized&)*src
///
/// #retval nullptr if src is nullptr
/// #retval Specialized clone of *src
///
/// #note Undefined behavior src does not point to a Specialized*
template<typename Base, typename Specialized>
Base* polymorphic_clone (const Base* src) {
static_assert(std::is_base_of<Base, Specialized>::value,
"Specialized is not a specialization of Base");
if (src == nullptr)
return nullptr;
return new Specialized{ static_cast<const Specialized&>(*src) };
}
}
/// #brief polymorphic reference interface over a base class
///
/// Respects polymorphic behavior of class ref.
/// Instances have deep copy semantics (clone) and
/// "[const] Base&" interface
///
/// #note Not regular: no trivial way to implement non-intrusive equality
///
/// #note safe to use with standard containers
template<typename Base>
class polymorphic final
{
public:
/// Functor capable to convert a Base* to it's specialized type
/// and clone it (intrusive implementation can be used)
typedef std::function<Base* (const Base*)> clone_functor;
/// #brief construct (takes ownership of ptr)
template<typename Specialized, typename CloneSpecialized>
polymorphic(Specialized* ptr, CloneSpecialized functor) noexcept
: instance_{ptr}, clone_{std::move(functor)}
{
static_assert(std::is_base_of<Base, Specialized>::value,
"Specialized is not a specialization of Base");
static_assert(
std::is_constructible<clone_functor, CloneSpecialized>::value,
"CloneSpecialized is not valid for a clone functor");
}
// not implemented: UB cloning in case client provides specialized ptr
// polymorphic(Base* ptr);
polymorphic()
: polymorphic{ nullptr, clone_functor{} }
{
}
polymorphic(polymorphic&&) = default;
polymorphic(const polymorphic& other)
: polymorphic{std::move(other.clone())}
{
}
polymorphic& operator=(polymorphic other)
{
std::swap(instance_, other.instance_);
std::swap(clone_, other.clone_);
return *this;
}
~polymorphic() = default;
/// #brief Cast to contained type
/// #pre instance not moved
/// #pre *this initialized with valid instance
operator Base&() const
{
assert(instance_.get());
return *instance_.get();
}
/// #brief Cast to contained type
/// #pre instance not moved
/// #pre *this initialized with valid instance
operator const Base&() const
{
assert(instance_.get());
return *instance_.get();
}
private:
polymorphic clone() const
{
return polymorphic{
clone_(instance_.get()), clone_functor{clone_}
};
}
std::unique_ptr<Base> instance_;
clone_functor clone_;
};
template<typename Base, typename Specialized, typename CF>
polymorphic<Base> to_polymorphic(Specialized&& temp, CF functor)
{
static_assert(std::is_base_of<Base, Specialized>::value,
"Specialized is not a specialization of Base");
typedef typename polymorphic<Base>::clone_functor clone_functor;
auto ptr_instance = std::unique_ptr<Base>{
new Specialized{std::move(temp)}
};
auto clone_instance = clone_functor{std::move(functor)};
return polymorphic<Base>{ptr_instance.release(), clone_instance};
}
template<typename Base, typename Specialized>
polymorphic<Base> to_polymorphic(Specialized&& temp)
{
static_assert(std::is_base_of<Base, Specialized>::value,
"Specialized is not a specialization of Base");
return to_polymorphic<Base,Specialized>(
std::move(temp), details::polymorphic_clone<Base,Specialized>
);
}
template<typename Base, typename Specialized, typename ...Args>
polymorphic<Base> to_polymorphic(Args ...args)
{
static_assert(std::is_constructible<Specialized, Args...>::value,
"Cannot instantiate Specialized from arguments");
return to_polymorphic<Base,Specialized>(
std::move(Specialized{std::forward<Args...>(args...)}));
}
template<typename Base> using polymorphic_vector =
std::vector<polymorphic<Base>>;
template<typename Base, typename ...Args>
polymorphic_vector<Base> to_polymorphic_vector(Args&& ...args)
{
return polymorphic_vector<Base>{to_polymorphic<Base>(args)...};
}
} // stdex
Example use:
stdex::polymorphic_vector<view> views = // explicit type for clarity
stdex::to_polymorphic_vector(
echo_view{"/echo"}, // class echo_view : public view
directory_view{"/static_files"} // class directory_view : public view
);
for(auto& v: views)
if(v.matches(reuqest.url())) // bool view::matches(...);
auto response = view.handle(request); // virtual view::handle(...) = 0;
Limitations of this:
If you use multiple inheritance DO NOT USE stdex::details::polymorphic_clone. Write an implementation based on dynamic_cast instead, and use to_polymorphic(Specialized&& temp, CF functor).
If you want to use the default behavior for part of your class, and only enhance it with non-standard behavior for the rest, consider functionally and organisationally splitting your class:
Put all those elements where you want the default-behavior into their own sub-object (inherited or composited), so you can easily use the default-special-function for them, and add the rest outside that sub-object.
Implementation left as an exercise for the interested reader.
Related
I want to store a std::vector<> containing objects which have a common base class, within a host class. The host class should remain copiable since it is stored inside a std::vector<> of it's owner class.
C++ offers multiple ways of doing that, but I want to know the best practice.
Here is an example using std::shared_ptr<>:
class Base{};
class Derivative1: public Base{};
class Derivative2: public Base{};
class Host{
public: std::vector<std::shared_ptr<Base>> _derivativeList_{};
};
class Owner{
public: std::vector<Host> _hostList_;
};
int main(int argc, char** argv){
Owner o;
o._hostList_.resize(10);
Host& h = o._hostList_[0];
h._derivativeList_.emplace_back(std::make_shared<Derivative1>());
// h._derivativeList_.resize(10, std::make_shared<Derivative1>()); // all elements share the same pointer, but I don't want that.
}
Here the main drawback for me is that in order to claim a lot of elements in _derivativeList_ I need to perform emplace_back() for every single element. This takes a lot more time than a simple resize(N) which I can't use with std::shared_ptr<> since it will create the same pointer instance for every slot.
I thought about using std::unique_ptr<> instead, but this is not viable since it makes the Host class non copiable (a feature requested by std::vector).
Otherwise, I could use std::variant<Derived1, Derived2> which can do what I want. However I would need to declare every possible instance of the derived class...
Any thought/advice about this?
tldr: Use a variant or type erasure, depending on context.
What you are asking for in C++ would be described roughly as a value type or a type with value semantics. You want a type that is copyable, and copying just "does the right thing" (copies do not share ownership). But at the same time you want polymorphism. You want to hold a variety of types that satisfy the same interface. So... a polymorphic value type.
Value types are easier to work with, so they will make a more pleasant interface. But, they may actually perform worse, and they are more complex to implement. Therefore, as with everything, discretion and judgment come into play. But we can still talk about the "best practice" for implementing them.
Let's add an interface method so we can illustrate some of the relative merits below:
struct Base {
virtual ~Base() = default;
virtual auto name() const -> std::string = 0;
};
struct Derivative1: Base {
auto name() const -> std::string override {
return "Derivative1";
}
};
struct Derivative2: Base {
auto name() const -> std::string override {
return "Derivative2";
}
};
There are two common approaches: variants and type erasure. These are the best options we have in C++.
Variants
As you imply, variants are the best option when the set of types is finite and closed. Other developers are not expected to add to the set with their own types.
using BaseLike = std::variant<Derivative1, Derivative2>;
struct Host {
std::vector<BaseLike> derivativeList;
};
There's a downside to using the variant directly: BaseLike doesn't act like a Base. You can copy it, but it doesn't implement the interface. Any use of it requires visitation.
So you would wrap it with a small wrapper:
class BaseLike: public Base {
public:
BaseLike(Derivative1&& d1) : data(std::move(d1)) {}
BaseLike(Derivative2&& d2) : data(std::move(d2)) {}
auto name() const -> std::string override {
return std::visit([](auto&& d) { return d.name(); }, data);
}
private:
std::variant<Derivative1, Derivative2> data;
};
struct Host {
std::vector<BaseLike> derivativeList;
};
Now you have a list in which you can put both Derivative1 and Derivative2 and treat a reference to an element as you would any Base&.
What's interesting now is that Base is not providing much value. By virtue of the abstract method, you know that all derived classes correctly implement it. However, in this scenario, we know all the derived classes, and if they fail to implement the method, the visitation will fail to compile. So, Base is actually not providing any value.
struct Derivative1 {
auto name() const -> std::string {
return "Derivative1";
}
};
struct Derivative2 {
auto name() const -> std::string {
return "Derivative2";
}
};
If we need to talk about the interface we can do so by defining a concept:
template <typename T>
concept base_like = std::copyable<T> && requires(const T& t) {
{ t.name() } -> std::same_as<std::string>;
};
static_assert(base_like<Derivative1>);
static_assert(base_like<Derivative2>);
static_assert(base_like<BaseLike>);
In the end, this option looks like: https://godbolt.org/z/7YW9fPv6Y
Type Erasure
Suppose instead we have an open set of types.
The classical and simplest approach is to traffic in pointers or references to a common base class. If you also want ownership, put it in a unique_ptr. (shared_ptr is not a good fit.) Then, you have to implement copy operations, so put the unique_ptr inside a wrapper type and define copy operations. The classical approach is to define a method as part of the base class interface clone() which every derived class overrides to copy itself. The unique_ptr wrapper can call that method when it needs to copy.
That's a valid approach, although it has some tradeoffs. Requiring a base class is intrusive, and may be painful if you simultaneously want to satisfy multiple interfaces. std::vector<T> and std::set<T> do not share a common base class but both are iterable. Additionally, the clone() method is pure boilerplate.
Type erasure takes this one step more and removes the need for a common base class.
In this approach, you still define a base class, but for you, not your user:
struct Base {
virtual ~Base() = default;
virtual auto clone() const -> std::unique_ptr<Base> = 0;
virtual auto name() const -> std::string = 0;
};
And you define an implementation that acts as a type-specific delegator. Again, this is for you, not your user:
template <typename T>
struct Impl: Base {
T t;
Impl(T &&t) : t(std::move(t)) {}
auto clone() const -> std::unique_ptr<Base> override {
return std::make_unique<Impl>(*this);
}
auto name() const -> std::string override {
return t.name();
}
};
And then you can define the type-erased type that the user interacts with:
class BaseLike
{
public:
template <typename B>
BaseLike(B &&b)
requires((!std::is_same_v<std::decay_t<B>, BaseLike>) &&
base_like<std::decay_t<B>>)
: base(std::make_unique<detail::Impl<std::decay_t<B>>>(std::move(b))) {}
BaseLike(const BaseLike& other) : base(other.base->clone()) {}
BaseLike& operator=(const BaseLike& other) {
if (this != &other) {
base = other.base->clone();
}
return *this;
}
BaseLike(BaseLike&&) = default;
BaseLike& operator=(BaseLike&&) = default;
auto name() const -> std::string {
return base->name();
}
private:
std::unique_ptr<Base> base;
};
In the end, this option looks like: https://godbolt.org/z/P3zT9nb5o
I'm currently researching the pimpl idiom and there are very nice tutorials how it could be implement (e.g. here). But i have never seen it implemented as a base template class like this:
#ifndef PIMPL_H
#define PIMPL_H
template <class T>
class Pimpl
{
public:
explicit Pimpl();
explicit Pimpl(T *ptr);
virtual ~Pimpl() = 0;
Pimpl(const Pimpl<T> &other);
Pimpl &operator=(const Pimpl<T> &other);
protected:
T *d_ptr;
};
template<class T>
Pimpl<T>::Pimpl() : d_ptr(new T)
{
}
template<class T>
Pimpl<T>::Pimpl(T *ptr) : d_ptr(ptr)
{
}
template<class T>
Pimpl<T>::~Pimpl()
{
delete d_ptr;
d_ptr = 0;
}
template<class T>
Pimpl<T>::Pimpl(const Pimpl<T> &other) : d_ptr(new T(*other.d_ptr))
{
}
template<class T>
Pimpl<T> &Pimpl<T>::operator=(const Pimpl<T> &other)
{
if (this != &other) {
delete d_ptr;
d_ptr = new T(*other.d_ptr);
}
return *this;
}
#endif // PIMPL_H
Which then could be used in any class you like to pimpl:
#ifndef OBJECT_H
#define OBJECT_H
#include "pimpl.h"
class ObjectPrivate;
class Object : public Pimpl<ObjectPrivate>
{
public:
Object();
virtual ~Object();
/* ... */
};
#endif // OBJECT_H
Currently i'm using it in a small example project (build as a shared library) and the only problem i had, was that MSVC warns about the missing destructor for ObjectPrivate (see C4150). This warning only occurs, because ObjectPrivate is forward declared and therefore not visible to the delete operator in Pimpl::~Pimpl() at compile time.
Does anyone see any sort of problems with this approach? :-)
So there is a now a final version based on the discussion below on GitHub (big thanks to StoryTeller). The repository also contains a simple usage example.
Yes, there are several problems, as I see it.
Your class is essentially a mixin. It's not about dynamic polymorphism, so no-one is ever going to call delete on a pointer to Pimpl<ObjectPrivate>. Drop the virtual destructor. It's introducing overhead that's never going to be required. What you want is static polymorphism only.
You allocate the object with new and release with delete. I won't use your template, because that allocation scheme isn't always appropriate in my applications. You must give a way to customize the allocation scheme in order to make your class actually useful.
Your assignment operator isn't exception safe. If the constructor for T throws, you lose the previously saved data. IMO it's better in this case to use the copy and swap idiom.
The solution to (1) and (2) is to add more template parameters, where the first is for the CRTP. This will allow you to push operations you aren't aware of how to do, onto the class that inherits your mixin. It can override them by defining its own make, unmake and clone. And those will all be bound statically.
template <class Handle, class Impl>
class Pimpl
{
private:
Impl* _make() const
{ return ((Handle const*)this)->make(); }
void _unmake(Impl *p) const
{ ((Handle const*)this)->unmake(p); }
Impl* _clone(Impl *p) const
{ return ((Handle const*)this)->clone(p); }
void swap(Pimpl &other) {
Impl *temp = d_ptr;
d_ptr = other.d_ptr;
other.d_ptr = temp;
}
public:
explicit Pimpl();
~Pimpl();
Pimpl(const Pimpl &other);
Pimpl &operator=(const Pimpl &other);
// fall-backs
static Impl* make() { return new Impl; }
static void unmake(Impl* p) { delete p; }
static Impl* clone(Impl* p) { return new Impl(*p); }
protected:
Impl *d_ptr;
};
template<class Handle, class Impl>
Pimpl<Handle, Impl>::Pimpl() :
d_ptr(_make())
{
}
template<class Handle, class Impl>
Pimpl<Handle, Impl>::~Pimpl()
{
_unmake(d_ptr);
d_ptr = 0;
}
template<class Handle, class Impl>
Pimpl<Handle, Impl>::Pimpl(const Pimpl &other) :
d_ptr(_clone(other.d_ptr))
{
}
template<class Handle, class Impl>
Pimpl<Handle, Impl> &Pimpl<Handle, Impl>::operator=(const Pimpl &other)
{
Pimpl copy(other);
swap(copy);
return *this;
}
Live Example
Now your header can compile cleanly. So long as the destructor for Object isn't defined inline. When it's inline the compiler must instantiate the destructor of the template wherever object.h is included.
If it's defined in a cpp file, after the definition of ObjectPrivate, then the instantiation of ~Pimpl will see the full definition of the private parts.
Further ideas for improvement:
Make the special members protected. It's only the derived Handle class that's supposed to call them, after all.
Add support for move semantics.
But i have never seen it implemented as a base template class
Vladimir Batov did it: https://github.com/yet-another-user/pimpl
Does anyone see any sort of problems with this approach?
You need to take the warning seriously. If your ObjectPrivate actually has a non-trivial destructor (which is as easy as containing a std::string member), you have undefined behavior, and the destructor probably won't get called.
This typically suggests that for some reason, the destructor is instantiated in the wrong place. Make sure that all definitions of all constructors and destructors of the derived class are placed after the full definition of ObjectPrivate. This includes the implicit copy and move constructors, which are probably what triggers the warning in your example code. Yes, this means you have to explicitly declare these special functions (and as a consequence, also the copy and move assignment operators if you want them), but at least you can use a defaulted definition.
I don't know if Vlad's library has the same problem.
Also, nulling out pointers in a destructor is pointless and will probably just get optimized away by some modern compilers.
Modern version that I'm using:
///////////////////////////////
// Header File
template <typename impl_t>
class Pimpl {
public:
Pimpl() = default;
virtual ~Pimpl() = default;
Pimpl(std::shared_ptr<impl_t> handle) : handle(handle) {}
std::shared_ptr<impl_t>
get_handle() const {
return handle;
}
protected:
std::shared_ptr<impl_t> handle;
};
class object_impl;
class object : public Pimpl<object_impl> {
/* whatever constructors you want*/
public:
object(int x);
}
///////////////////////////////
// Cpp File
class object_impl {
public:
object_impl(int x) : x_(x) {}
private:
int x_;
}
object::object(int x) : Pimpl(std::make_shared<object_impl>(x)) {}
Currently, I am building a library in C++ (using C++11 standards), and I am stuck on trying to figure out how to make my design more practical. I have the following abstract class E
template<typename K>
class E
{
public:
virtual ~E() {};
virtual void init() = 0;
virtual void insert(const K& k) = 0;
virtual size_t count() const = 0;
virtual void copy(const E<Key>& x) = 0;
};
which I want to restrict users from instantiating it (i.e., be an interface). E has two sub-classes that implement the corresponding methods:
template<typename K>
class EOne : public E<K>
{
public:
EOne() {}
EOne(const EOne& x) {...}
void init() override
{
...
}
void insert(const K& v) override
{
...
}
size_t count() const override
{
...
}
void copy(const E<K>& o) override
{
...
}
private:
// some private fields
};
and ETwo : public E<K>, which is similar to EOne. Also, there is a different class J, which has a member std::vector<E<K>> that needs to be instantiated during construction:
template<typename K>
class J
{
public:
J(size_t n, const E<K>& e) : v(n, e)
{}
private:
std::vector<E<K>> v;
}
In essence, by getting a constant reference for a E<K> object, I want J's constructor to use the reference to instantiate all n objects of v using e as a template (i.e., call the copy constructor). As you can imagine, my goal is to have e be an object of either EOne<K> or ETwo<K>. For instance, I would make a call to J<K>::J(size_t n, const E<K>& e) the following way:
int main(int argc, char** argv)
{
EOne<std::string> e;
J<std::string> j(10, e); // populate v with 10 copies of e
...
}
However, the above does not compile and the compiler complains that I cannot instantiate abstract class (I am using vc++ but I believe I will get the same error on other compilers as well). Therefore, my question has to do on how I can overcome this problem? Do you have any suggestions on how I can make my design more practical.
Thank you
There is more than one approach to this. What follows is the most complex reasonable one. It requires lots of work in the type definitions, but leads to the cleanest "client" code that uses these types.
It is time to learn how to make a type regular.
An instance of a regular type behaves like a value. C++ algorithms and container work far better with regular types than it does with abstract types.
template<class K>
class E_interface {
public:
virtual ~E_interface() {};
virtual void init() = 0;
virtual void insert(const K& k) = 0;
virtual size_t count() const = 0;
virtual void copy_from(const E_interface& x) = 0;
std::unique_ptr<E_interface> clone() const = 0;
};
this is basically your E, except I added clone().
template<class T, class D=std::default_delete<D>, class Base=std::unique_ptr<T>>
struct clone_ptr:Base {
using Base::Base;
clone_ptr(Base&& b):Base(std::move(b)) {}
clone_ptr()=default;
clone_ptr(clone_ptr&&o)=default;
clone_ptr(clone_ptr const& o):
clone_ptr(
o?clone_ptr(o->clone()):clone_ptr()
)
{}
clone_ptr& operator=(clone_ptr&&o)=default;
clone_ptr& operator=(clone_ptr const&o) {
if (*this && o) {
get()->copy_from(*o.get());
} else {
clone_ptr tmp(o);
*this = std::move(tmp);
}
return *this;
}
};
The clone_ptr is a smart pointer that is a unique_ptr that knows how to copy itself by calling clone() and copy_from on the stored object. It may have some typos.
Now we write our E:
template<class K>
class E {
clone_ptr<E_interface<K>> pImpl;
public:
E() = default;
E( std::unique_ptr<E_interface<K>> in ):pImpl(std::move(in)) {}
E( E const& )=default;
E( E && )=default;
E& operator=( E const& )=default;
E& operator=( E && )=default;
explicit operator bool() const { return (bool)pImpl; }
void init() { if (*this) pImpl->init(); }
void insert(const K& k) ( if (*this) pImpl->insert(k); }
size_t count() const { if (*this) pImpl->count(); else return 0; }
};
Our E<K> is now a value type. It can be stored in a vector, copied, moved, etc.
How do we do EOne and ETwo?
First, take your existing EOne and ETwo and call them EOne_impl and ETwo_impl. Implement a clone() function that does a return std::make_unique<EOne_impl>(*this); for EOne_impl and similar for ETwo_impl.
Then this helper:
template<class Impl, class K>
struct E_impl: E<K> {
using E<K>::E<K>;
E_impl() : E<K>( std::make_unique<Impl>() ) {}
template<class...Args>
E_impl(Args&&...args) : E<K>( std::make_unique<Impl>(std::forward<Args>(args)...) ) {}
};
gives us
template<class K>
using Eone = E_impl< Eone_impl, K >;
template<class K>
using Etwo = E_impl< Etwo_impl, K >;
and I believe your J and main code starts compiling and working as-is.
What we just did was create value-semantics E<K> type that contains a pImpl (pointer to implementation) pointing to a pure-virtual interface that knows how to copy itself, as well as the interface we want on an E<K>.
We then forwarded the interface of E<K> to the E_interface<K> for each method. We didn't expose copy_from or clone, as those become operator= and our copy constructor.
To implement E<K>, you first implement E_interface<K>. Then I wrote a helper to create a derived type from E<K> that implicitly uses that implementation.
Note that our E<K> is almost-never empty; not never-empty. This is more efficient and simpler, but can cause problems down the road.
E<K> becomes a polymorphic value-semantics type. This is a strange beast in some senses (as many languages don't support such a type), but in other senses it behaves exactly the way you'd want it to behave.
A similar solution in C# or Java would have the data stored in the vectors be fully garbage collected reference-semantics types, not value-semantics types.
This is similar to a std::vector<std::shared_ptr<E<K>> (with the note that shared pointers are not fully garbage collected). Also note that copies of the shared_ptr end up being pointing to the same object, not new copies of it.
A std::vector<value_ptr<E_interface<K>> would also be a reasonable solution and get rid of some of the gymnastics I did in my E<K> and E_impl<K>. In this case, you wouldn't rename E to E_interface. You'd initialize the vector with
J<std::string> j(10, std::make_unique<EOne<std::string>>(e))
or somesuch.
Part of your problem is you have to ask yourself "what does it mean to copy an E<K>". In C++ you get to answer this question yourself; depending on how you answer it, you may or may not be permitted to store it in a std::vector.
Since std::vector<E<K>> v; requires a static instantiation of class E, it will never compile (as you already noticed correctly). To make it work should use
std::vector<std::shared_ptr<E<K>>> v;
instead. It can store your objects EOne and ETwo dynamically while they can be referred with a pointed of type E. To add a new object to the vector you can use push_back:
v.push_back(std::make_shared<EOne<K>>{});
To cast between the types you can use dynamic and static cast functions for smart pointers e.g. std::dynamic_pointer_cast<>().
I am having the following problem. Consider this (very simplified) example. I am using one of our existing template classes that I am able to add (up to a point) some generic functionality to:
template<typename T> class myClass
{
public:
typedef T element_type;
explicit myClass(T* p = 0) : ptr(p) {}
~myClass() { delete ptr; };
......
private:
T* ptr;
};
I am also using an external library (which I am obviously not able to adjust) which amongst other things defines (lets say) struct notMine. As this is a C style library, one does some sort of notMine* nmPtr = createNotMine(); call and I need to remember to freeNotMine(nmPtr); when I am done.
Now I want to use myClass with a pointer to a notMine structure but the problem is that when myClass goes out of scope, I 'forget' to free this notMine structure and it calls 'delete' instead.
What is a nice way of fixing this problem?
Do I create class derived from myClass where I can write my own destructor specific to this problem?
Do I adjust myClass so I can pass some sort of optional generic Destructor object?
Do I adjust myClass so I can (optionally) pass a static function that is to be called in the destructor?
EDIT: I probably didn't explain myself properly, but the myClass is already being used elsewhere, so I cannot simply change the existing destructor to suit this particular problem
For you immediate implementation
Given than the class can be modified (maintaining backwards compatibility), you can extend the class to include a custom deleter to manage the resource (with a suitable default). Custom deleters can then be implemented for each type that the class needs to maintain.
template <typename T>
struct DefaultDeleter {
void operator()(T* ptr) const {
delete ptr;
}
}
struct NotMineDeleter { // or a specialisation of DefaultDeleter
void operator()(notMine* nmPtr) const {
if (nmPtr)
freeNotMine(nmPtr);
}
}
template<typename T, typename Deleter = DefaultDeleter<T> >
class myClass
{
public:
typedef T element_type;
explicit myClass(T* p = 0) : ptr(p) {}
~myClass() { deleter_(ptr); };
......
private:
Deleter deleter_;
T* ptr;
};
The myClass can now be used as follows for the external library;
myClass<notMine, NotMineDeleter> obj(createNotMine());
You will also need to deal with the copy and assignments for your class myClass by either making them private: (C++03), deleting them =delete; (C++11) or implementing the appropriately, i.e. implement the full copy and assignment semantics.
You've commented that the copy and assignments are implemented appropriately (this will need to be the case for the notMine* pointers as well). If this is not the case, then you will probably run into further problems.
C++11 also brings with it move semantics that can be implemented to transfer ownership from one instance of myClass to another.
Better yet
Use std::unique_ptr or std::share_ptr with a custom deleter that calls freeNotMine(nmPtr).
RAII - what it looks like you are doing is implementing a classic RAII class. This is a very good thing and one of the core idiomatic uses of resource classes in C++.
Alternative to the dual deleter above
Using template specialisation, the implementation can be simplified (but essentially still maintains deleters for each type being maintained) as follows;
template <typename T>
struct Deleter {
void operator()(T* ptr) const {
delete ptr;
}
}
template <>
struct Deleter<notMine> {
void operator()(notMine* nmPtr) const {
if (nmPtr)
freeNotMine(nmPtr);
}
}
template<typename T>
class myClass
{
public:
typedef T element_type;
explicit myClass(T* p = 0) : ptr(p) {}
~myClass() { deleter_(ptr); };
......
private:
Deleter<T> deleter_;
T* ptr;
};
The myClass can now be used as follows for the external library;
myClass<notMine> obj(createNotMine());
I have a particular class that stores a piece of data, which implements an interface:
template<typename T>
class MyContainer : public Container<T> {
class Something : public IInterface {
public:
// implement *, ->, and ++ here but how?
private:
T x;
};
// implement begin and end here, but how?
private:
Something* data; // data holds the array of Somethings so that references to them can be returned from begin() and end() to items in this array so the interface will work, but this makes the problem described below
};
And I have an array of Somethings.
I have the need for Something to implement an interface class (IInterface in the example) which:
Contains pure virtual member functions which return something such that *retval returns a reference to the x member, retval-> returns the address of x, and ++retval makes retval refer to the next Something in the array.
The things that the pure virtual members return can be inherited from and returned by the implementation of the members
container[i] (where container is the array holding the Something objects) always returns something such that *retval always returns a reference to the same T for the same i.
Right now, the interface looks like this:
template<typename T>
class Container {
class IInterface {
public:
virtual T& operator*() = 0;
virtual T* operator->() = 0;
virtual IInterface& operator++(); // this is the problem
};
// returning a reference right now to support covariance, so subclasses can
// derive from Container and then have a member class derive from IInterface
// and override these to return their derived class, but this has a problem
virtual IInterface& begin() = 0;
virtual IInterface& end() = 0;
};
My current solution (have the virtual methods return an IInterface& and return a Something& in the implementation) has no problem with the requirements, except for the ++retval requirement. Because the Something is directly tied to the object it holds and can't point to a T with a pointer, there's no way that I can find to get ++ to make the variable refer to the next Something in the array.
If it helps to know, this is an iterator type system. I would have made it with the STL style iterators (where you just have an array of T) that are passed around by value and hold pointers to the values they represent, but that would break the interface because only references and pointers are covariant, and the objects already have to exist somewhere else already (in my code they're in the array) so you don't return a reference to a local object.
The purpose of this setup is so that one can write functions that take a Container& and iterate the container without knowing what type of container it is:
void iterate(Container<int>& somecontainer) {
Container<int>::IIterator i = somecontainer.begin(); // right now this would return a reference, but it doesn't/can't work that way
while (i != somecontainer.end()) {
doSomething(*i);
++i; // this is the problem
}
}
It's kind of difficult for me to describe, don't hesitate to let me know if you need more information.
What you are trying to do is called type erasure. Basically you want to provide a value type (which is the same across the whole inheritance hierarchy) that wraps the particular iterator type and offers a uniform dynamic interface.
Type erasure is usually implemented with a non-virtual class (the type erased) that stores a pointer to a virtual base class that implements the erasure, from which you derive different types that wrap each particular iterator. The static class would offer templated constructor/assignment operators that would dynamically instantiate an object of the derived type and store the pointer internally. Then you only need to implement the set of operations as dispatch to the internal object.
For the simplest form of type erasure possible, you can take a look at the implementation of boost::any (documentation is here)
Sketch:
namespace detail {
template<typename T>
struct any_iterator_base {
virtual T* operator->() = 0; // Correct implementation of operator-> is tough!
virtual T& operator*() = 0;
virtual any_iterator_base& operator++() = 0;
};
template <typename T, typename Iterator>
class any_iterator_impl : any_iterator_base {
Iterator it;
public:
any_iterator_impl( Iterator it ) : it(it) {}
virtual T& operator*() {
return *it;
}
any_iterator_impl& operator++() {
++it;
return *this;
}
};
}
template <typename T>
class any_iterator {
detail::any_iterator_base<T>* it;
public:
template <typename Iterator>
any_iterator( Iterator it ) : it( new detail::any_iterator_impl<T,Iterator>(it) ) {}
~any_iterator() {
delete it;
}
// implement other constructors, including copy construction
// implement assignment!!! (Rule of the Three)
T& operator*() {
return *it; // virtual dispatch
}
};
The actual implementation becomes really messy. You need to provide different versions of the iterator for the different iterator types in the standard, and the detail of the implementation of the operators might not be trivial either. In particular operator-> is applied iteratively until a raw pointer is obtained, and you want to make sure that your type erased behavior does not break that invariant or document how you break it (i.e. limitations on the type T that your adaptor can wrap)
For extended reading:
- On the Tension Between Object-Oriented and Generic Programming in C++
- any_iterator: Implementing Erasure for C++ iterators
- adobe any_iterator ,
I would suggest a look at the Visitor pattern.
Other than that, what you want is a value type that will be imbued with polymorphic behavior. There is a much simpler solution than James' using your IInterface.
class IInterface
{
virtual ~IInterface() {}
virtual void next() = 0;
virtual void previous() = 0;
virtual T* pointer() const = 0;
virtual std::unique_ptr<IInterface> clone() const = 0;
};
std::unique_ptr<IInterface> clone(std::unique_ptr<IInterface> const& rhs) {
if (!rhs) { return std::unique_ptr<IInterface>(); }
return rhs->clone();
}
class Iterator
{
friend class Container;
public:
Iterator(): _impl() {}
// Implement deep copy
Iterator(Iterator const& rhs): _impl(clone(rhs._impl)) {}
Iterator& operator=(Iterator rhs) { swap(*this, rhs); return *this; }
friend void swap(Iterator& lhs, Iterator& rhs) {
swap(lhs._impl, rhs._impl);
}
Iterator& operator++() { assert(_impl); _impl->next(); return *this; }
Iterator& operator--() { assert(_impl); _impl->previous(); return *this; }
Iterator operator++(int); // usual
Iterator operator--(int); // usual
T* operator->() const { assert(_impl); return _impl->pointer(); }
T& operator*() const { assert(_impl); return *_impl->pointer(); }
private:
Iterator(std::unique_ptr<IInterface> impl): _impl(impl) {}
std::unique_ptr<IInterface> _impl;
};
And finally, the Container class will propose:
protected:
virtual std::unique_ptr<IInterface> make_begin() = 0;
virtual std::unique_ptr<IInterface> make_end() = 0;
And implement:
public:
Iterator begin() { return Iterator(make_begin()); }
Iteraotr end() { return Iterator(make_end()); }
Note:
You can do away with the std::unique_ptr if you can avoid the ownership issue. If you can restrict the IInterface to be behavioral only (by extracting the state into Iterator), then you can have the Strategy pattern kick-in, and use a pointer a statically allocated object. This way, you avoid dynamic allocation of memory.
Of course, it means your iterators won't be so rich, as it requires IInterface implementations to be stateless, and implementing "filtering" iterators, for example, would become impossible.
Have you thought about using CRTP. I find it a good candidate here. Here is a brief demo. It just explains your ++retval problem (if I understood it correctly). You have to change your IInterface definition from pure virtual to CRTP type interface.
template<class Derived>
struct IInterface
{
Derived& operator ++ ()
{
return ++ *(static_cast<Derived*>(this));
}
};
struct Something : public IInterface<Something>
{
int x;
Something& operator ++ ()
{
++x;
return *this;
}
};
There are some limitations of CRTP, that the template will always follow your IInterface. Which means that if you are passing a Something object to a function like this:
foo(new Something);
Then, foo() should be defined as:
template<typename T>
void foo(IInterface<T> *p)
{
//...
++(*p);
}
However for your problem, it can be a good fit.
Like you said, the problem is that instances of Something are tied to the object it holds. So let's try to untie them.
The key point to remember is that in OOP, public non-const data members are generally frowned upon. In your current implementation, every Something instance is tied to having a data member T x which is publicly accessible. Instead of this, is considered better to make an abstraction of this, i.e. provide accessor methods instead:
class Something : IInterface
{
private:
T x;
public:
T GetX()
{
return x;
}
};
Now the user has know idea what type of thing x is, much less that x exists.
This is a good first step, however, since you wish be able to have x refer to different objects at different times, we're pretty much going to have to make x be a pointer. And as a concession to conventional code, we'll also make GetX() return a const reference, rather than a regular value:
class Something: IInterface
{
private:
T *x;
public:
T const& GetX()
{
return *x;
}
};
It's now trivial to implement the methods in IInterface:
class Something: IInterface
{
private:
T *x;
public:
T const& GetX()
{
return *x;
}
T& operator*()
{
return *x;
}
T* operator->()
{
return x;
}
Something& operator++()
{
++x;
return *this;
}
};
The ++ operator is trivial now - it really just applies the ++ to x.
The user now has no idea that a pointer was used. All they know is that their code works right. That's the most important point in OOP's principle of data abstraction.
Edit
As far as implementing the begin and end methods of Container, that shouldn't be too difficult either, but it will require some changes to Container.
First off, let's add a private constructor to Something which takes a pointer to the starting object. We'll also make MyContainer a friend of Something:
class Something: IInterface
{
friend class MyContainer; // Can't test the code right now - may need to be MyContainer<T> or ::MyContainer<T> or something.
private:
T *x;
Something( T * first )
: x(first)
{
}
public:
T const& GetX()
{
return *x;
}
T& operator*()
{
return *x;
}
T* operator->()
{
return x;
}
Something& operator++()
{
++x;
return *this;
}
};
By making the constructor private, and setting the friend dependancy, we ensure that only MyContainer can make new Something iterators (this protects us iterating over random memory if something erroneous were given by a user).
Next off, we'll change MyContainer a little, so that rather than having an array of Something, we'll just have an array of T:
class MyContainer
{
...
private:
T *data;
};
Before we get to implementing begin and end, let's make that change to Container I talked about:
template<typename T, typename IteratorType>
class Container {
public:
...
// These prototype are the key. Notice the return type is IteratorType (value, not reference)
virtual IteratorType begin() = 0;
virtual IteratorType end() = 0;
};
So rather than relying on covariance (which would be really difficult in this case), we use a little template magic to do what we want.
Of course, since Container now accepts another type parameter, we need a corresponding change to MyContainer; namely we need to provide Something as the type parameter to Container:
template<class T>
class MyContainer : Container<T, Something>
...
And the begin/end methods are now easy:
template<class T>
MyContainer<T>::begin()
{
return Something(data);
}
template<class T>
MyContainer<T>::end()
{
// this part depends on your implementation of MyContainer.
// I'll just assume your have a length field in MyContainer.
return Something(data + length);
}
So this is what I've got for my midnight thinking. Like I mentioned above, I cannot currently test this code, so you might have to tweak it a bit. Hopefully this does what you want.
If the usage is supposed to be similar to stdlib, then the iterator needs to be a value object, because it normally gets copied by value a lot. (Also, otherwise what would the begin and end method return a reference to?)
template <class T>
class Iterator
{
shared_ptr<IIterator> it;
public:
Iterator(shared_ptr<IIterator>);
T& operator*() { it->deref(); }
T* operator->() { return &it->deref(); }
Iterator& operator++() { it->inc(); return *this; }
etc.
};