Prev: Help With TR1 regex_match()
Next: Is this correct C++?
From: Goran on 6 Oct 2009 21:46 On Oct 7, 6:14 am, ManicQin <manic...(a)gmail.com> wrote: > I tried to think of a way to deep copy the vector into another ->without<- > adding Clone function and such to the objects and I couldnt > think of one, I would be happy if anyone knows of a way... What's not great with your idea is that you get deep or shallow copy depending on whether Interface "implements" ICloneable or not, which might end up in a surprise for the user. But hey, it's simple and will work with that small constraint! You could also add "cloning traits" template parameter to your container. Duty of that thing would be to create a clone of your items ;-). As default, you could use the simplest possible: copy- construction (but, that doesn't work if Interface is abstract). You can also offer ICloneable-using version. E.g. template<class Interface> struct DefaultCloningTrait { // Default: needs "copy-constructible" Interface* Clone(const Interface& Item) const { return new Interface (Item); } }; template<class Interface, class CloningTrait=DefaultCloningTrait<Interface> > class CompositorBase { CloningTrait _CloningTrait; public: CompositorBase(CloningTrait ct=CloningTrait()) : _CloningTrait(ct) {}; Interface* CloneItem(const Interface& Item) { return _CloningTrait.Clone(Item); } }; template<class Interface> struct CloneableCloningTrait { // Using ICloneable interface Interface* Clone(const Interface& Item) const { return dynamic_cast<const ICloneable&>(Item).Clone(); } }; Goran. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Goran on 7 Oct 2009 07:49 On Oct 7, 2:45 pm, Nick Hounsome <nick.houns...(a)googlemail.com> wrote: > In my experience developing a framework that isn't strongly grounded > in real application requirements often leads to this sort of mess > where the developer struggles to create something that could do > everything but actually just constrains the users +1 for this. Scope is king and complicated scope tends to be a bad one. Goran. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: ManicQin on 7 Oct 2009 07:42 On Oct 7, 2:46 pm, Goran <goran.pu...(a)gmail.com> wrote: Thanks everybody, you confirmed my fear that there is no normal way to do a polymorphic deep copy (without Clone) and that the solution I selected is not good. I agree that it's better to give up the copyItem method instead of making it a half deep. Nick about the partial specialization in manners of design I can easily add it but, will it work even though IClonable is an ancestor of the current Interface? lets take Neil Butterworth example lets say my class looks like: template <typename T> class C { public: // stuff private: std::vector <T*> v; }; and I'm using the interface IRestriction that inherits from IClonable C<IRestriction> VicTheVector; my CopyItems should look like: template <> class C<IClonable>{ } The class that I instantiate is C<IRestriction> and not C<ICLonable> should it still work? that was my first try but the compiler doesn't recognizes IRestriction as IClonable... Thanks -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Kenneth 'Bessarion' Boyd on 7 Oct 2009 08:11 On Oct 6, 11:14 pm, ManicQin <manic...(a)gmail.com> wrote: > Hi, I'll do my best to explain my problem... > I have an data type that is wrapping a vector of pointers, currently I > have no control of the type that the vector holds and I need to be as > generic as I can (it's a part of a frame work). > naturally my vector is templated by the client. > > I tried to think of a way to deep copy the vector into another ->without<- adding Clone function and such to the objects and I couldnt > think of one, I would be happy if anyone knows of a way... Aside from syntactical details: it's virtual Clone or bust for a polymorphic-result deep copy in C++98: virtual operator= won't save you, as it doesn't get inherited. The language in draft n2914 of C+ +0X looks like this may have been remediated. Unfortunately, I haven't seen a good way to nonfatally compile-time test for the declaration of a (member) function. That would, at least, allow using a defaulted bool parameter to specialize the proposed class based on the presence of a Clone member function. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Kian Karas on 7 Oct 2009 18:48
{ Please limit your text to 70 columns or so to prevent unwanted breaks in the middle of the lines. -mod } #include <vector> #include <algorithm> using namespace std; /* As written in prior comments, it is a bad idea to determine the copying method at run-time - because you can't! I agree with Goran that what you need is a policy template argument (it is not called traits). However, the suggested DefaultCloningTrait class is unfortunate in that it does not support non-pointer types - std::vector does. Hence, lets make one that support the copy-assignment idiom, which is the behavior you get when copying a container from std vector<int> v1; vector<int> v2(v1); Here is the CopyAssign policy: */ template<class T> struct CopyAssign { static T Copy(const T& t) { return t; } static void Destroy(const T&) {} protected: ~CopyAssign() {} }; /* The intention is to use the policy class like this: CopyAssign<int*> pol; int n = 42; int* pn = pol.Copy(n); // pn == &n Note that Copy() is static to avoid the overhead of passing the 'this' pointer. The presence of the Destroy() function will make sence in a moment. Now lets look at how the CompositorBase will use the policy. */ template<class Element, template<class> class CreationPolicy = CopyAssign> class CompositorBase : public CreationPolicy<Element> { typedef vector<Element> Container; public: CompositorBase() {} CompositorBase(const CompositorBase& c) : m_cData(c.m_cData.size()) { CreationPolicy<Element>& myPolicy = *this; for (size_t n = 0; n < m_cData.size(); ++n) { m_cData[n] = myPolicy.Copy(c.m_cData[n]); } } ~CompositorBase() { CreationPolicy<Element>& myPolicy = *this; for (size_t n = 0; n < m_cData.size(); ++n) { myPolicy.Destroy(m_cData[n]); } } void PushBack(const Element& e) { m_cData.push_back(e); } private: Container m_cData; }; /* There are a few things to note: - The policy is derived instead of being a member. It has the advantage that it gives the compiler the possibility of making the empty base class optimization. - Now it is clear why the destructor of the CopyAssign policy was made protected: it is to avoid partial deletion of CompositorBase objects. Adding a virtual destructor would also have solved that problem, but then the size of the poly class would no longer be zero. - The policy template argument is a template template argument! This has the advantage that the policy can be used inside the CompositorBase class with a different element type. Because we want this implementation of the CompositorBase class to support non pointer elements, it is not possible to write a destructor that simply deletes all elements. Whether or not the operator delete shall be invoked on an element depends on two properties: - Does the CompositorBase class own the elements? - Does the Creation Policy allocate elements on the heap? If the answer to the first question is _no_, then the destructor shall do nothing, else it should be implemented like above. The empty CopyAssign::Destroy() definition now makes sence: Objects not allocated on the heap shall not be deleted in the destructor. Now lets add a policy which can be used to copy objects to the heap. */ template<class T> struct CopyHeap; template<class T> struct CopyHeap<T*> { static T* Copy(const T* pt) { return new T(*pt); } static void Destroy(const T* pt) { delete pt; } protected: ~CopyHeap() {} }; /* The primary template does not have a definition and then it is followed by a strange looking partial specialization. The partial specialization will be instantiated for pointer types and the primary template in all other cases (non-pointers). This will give you a compile-time error if trying to use the CopyHeap policy with non-pointer types (which is what we want): CompositorBase<int, CopyHeap> error; // Won't compile CompositorBase<int*, CopyHeap> ok; Note that the policy now invokes operator delete on its argument. Another reason for using partial specialization is to obtain 'base' type without the pointer. Else, the call to operator new T() would yield a 'pointer to a pointer to int': */ template<class PointerType> struct CopyHeapError { static PointerType Copy(const PointerType p) { return new PointerType(*p); } // Error }; /* Now we only need a policy for cloning objects. */ template<class T> struct ClonePolicy; template<class T> struct ClonePolicy<T*> { static T* Copy(const T* pt) { return pt->Clone(); } static void Destroy(const T* pt) { delete pt; } protected: ~ClonePolicy() {} }; /* The above set of policies solve most cases, but not the case where the policy needs to make a cross-cast to obtain the IClone interface (as shown in your example). As there are no way of determining run-time whether or not the cross- cast is valid, you need to determine how to handle that error. The following implementation will throw a bad_cast exception upon error. */ template<class T> struct CrossClone; template<class T> struct CrossClone<T*> { static T* Copy(const T* pt) { const ICloneable& t = dynamic_cast<const ICloneable&>(*pt); return dynamic_cast<T*>(t.Clone()); } static void Destroy(const T* pt) { delete pt; } protected: ~CrossClone() {} }; /* Note the second dynamic_cast in the Clone() function. It is necessary to cast the new instance from ICloneable back to the type held in the container. It would be a waste to apply the dynamic_cast to IClonable elements. Hence, we can make a 'full' specialization for ICloneable (I honestly don't know if any compiler will/can optimize the dynamic_cast away - better safe than sorry): */ struct ICloneable; template<> struct CrossClone<ICloneable*> { static ICloneable* Copy(const ICloneable* p) { return p->Clone(); } static void Destroy(const ICloneable* pt) { delete pt; } protected: ~CrossClone() {} }; /* Remember also to either declare the CompositorBase::operator=() private or implement it using the CreationPolicy. Note: The suggested CompositorBase constructor is not exception safe. You will have a memory leak if the call to Copy() throws. Luckily this problem can be solved, but I leave that to you. Below is some example code that will compile and use the different policies. */ struct ICloneable { virtual ~ICloneable() {} virtual ICloneable* Clone() const = 0; }; struct Base { virtual ~Base() {} }; struct Cloneable : ICloneable, Base { virtual Cloneable* Clone() const { return new Cloneable(*this); } }; int main(int argc, char* argv[]) { typedef CompositorBase<int> IntAssign; IntAssign intAssign; IntAssign intAssign2(intAssign); //CompositorBase<int, CopyHeap> intHeap; // Error: int is not a valid heap type typedef CompositorBase<int*, CopyHeap> IntPHeap; IntPHeap intPHeap; IntPHeap intPHeap2(intPHeap); typedef CompositorBase<Cloneable*, ClonePolicy> CloneablePClone; CloneablePClone cloneablePClone; CloneablePClone cloneablePClone2(cloneablePClone); typedef CompositorBase<Base*, CrossClone> BasePCross; BasePCross basePCross; basePCross.PushBack(new Cloneable); BasePCross basePCross2(basePCross); typedef CompositorBase<ICloneable*, CrossClone> ICloneablePCross; ICloneablePCross iCloneablePCross; iCloneablePCross.PushBack(new Cloneable); ICloneablePCross iCloneablePCross2(iCloneablePCross); return 0; } -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |