-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implemented cloning_ptr #202
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some clarifications would help, but other than that LGTM.
We also had a discussion on the design I'll comment below.
* @tparam T The type to check | ||
*/ | ||
template <class T> | ||
concept clonable = requires(const T* t) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that you dont need a pointer here, you could use a reference too.
Design discussed privately and summarized below:While this is enough for our usage, this is not how such type is usually designed when it is designed for general use. The current design of The more generic way to think about it is to assume that if a type hierarchy provides copy operations, we can workaround the lack of virtual dispatch in these operations by wrapping these types with a type that does the virtual dispatch: basically, generating automatically the cloning function as a virtual function, based on the standard copy operations of the type being stored. This is more in line with about everything in generic computing, which relies on value semantics and the idea of "regular" (or "semi-regular") types - which is what makes standard algorithms works about everywhere. Something like that (assuming we want to only support a hierarchy of types inheriting from template<class T>
class polymorphic_ptr
{
// ...
public:
using this_type = polymorphic_ptr<T>;
// ....
// copy
this_type& operator=(const this_type& other)
{
storage = other.storage->clone();
return *this;
}
private:
struct interface
{
//...
virtual interface* clone() = 0;
};
template< class U >
requires std::is_base_of_v<T, U>
struct model : interface
{
U value; // details depends on how the storage is implemented, could have been a pointer here
// ...
interface* clone() override
{
return new model{ value }; // or something similar, relying on the copy operations specific to U
}
};
unique_ptr<interface> storage; // there are various ways to do this, I'll simplify here but dont assume it's always unique_ptr<interface>
};
Obviously this is a very incomplete example which is just made to provide a basic understanding of the technique used internally to generate the copy. Examples of the techniques used in the generic version are often presented by Sean Parent in his talks, notably this one (slides) which is a more in-depth version of his talks about "Inheritance Is The Base Class Of Evil" (that I also recommend). This paper about This is not a request for this PR but just information for future design improvements :) |
As the author of the indirect/polymorphic paper I’d be happy to discuss your design requirements in more detail and potentially provide you with a suitably licensed implementation of our proposed additions. |
@jbcoe thanks! We are currently swamped with other tasks, but happy to discuss when they're done. |
Fixes #195
This is a very basic implementation that can be improved in the future:
std::unique_ptr
)cloning_ptr
and thevalue_ptr
classes.Notice that this adds some complexity to the constructors that I did not want to handle at the moment. I'll open an issue when this is merged.