From: dave@boostpro.com   
      
   on Thu Jan 05 2012, Kelvin Chung wrote:   
      
   > Suppose I have a class Base and three derived classes, Derived1,   
   > Derived2, and Derived3. Suppose I also have this thing:   
   >   
   > class Result {   
   > boost::shared_ptr ptr; // This will always be a Derived1 or   
   > a Derived2 pointer, never Derived3   
   > public:   
   > int getProperty();   
   > };   
   >   
   > The value returned from getProperty() can be determined at   
   > compile-time (ie. all Derived1 return the same value and all Derived2   
   > return the same value) - the limitation of Result::ptr to Derived1 or   
   > Derived2 pointers is that this property is not well-defined for   
   > Derived3. The question I have is in implementing getProperty().   
   >   
   > Suppose I have this class:   
   >   
   > template    
   > class DerivedProperty;   
   >   
   > template<>   
   > class DerivedProperty : public boost::integral_constant   
   > {};   
   >   
   > template<>   
   > class DerivedProperty : public boost::integral_constant   
   > {};   
   >   
   > (BTW, How do you ensure that DerivedProperty is never instantiated   
   > with Derived3 due to its not-well-definedness, Base due to its   
   > incompleteness, or any unrelated class due to its unrelatedness?)   
      
   You have already done it by omitting a definition for the primary   
   DerivedProperty template.   
      
   Of course, nothing other than etiquette stops anyone from creating a   
   *specialization* of DerivedProperty, as you have done for   
   DerivedProperty and DerivedProperty. And there's no   
   way to prevent that for an arbitrary unrelated class.   
      
   > As Result::ptr will always be a Derived1 or a Derived2 pointer,   
   > getProperty() should return DerivedProperty::value or   
   > DerivedProperty::value, where appropriate. Except that   
   > Result doesn't have any information on which subclass the pointer   
   > should be.   
      
   Right.   
      
   > From reading about how you can do type erasure in C++, I think I   
   > should do something like this:   
   >   
   > class Result {   
   > struct ResultBase {   
   > virtual boost::shared_ptr getPtr() = 0; // In case the ptr from   
   before is needed elsewhere   
   > virtual int getProperty() = 0;   
   > };   
   >   
   > template    
   > struct ResultModel : public ResultBase {   
   > // Has a constructor that takes in the pointer, virtual destructor, etc.   
   > boost::shared_ptr ptr;   
   > boost::shared_ptr getPtr() { return ptr; }   
   > int getProperty() { return DerivedProperty::value; }   
   > };   
   > public:   
   > template    
   > Result(const boost::shared_ptr& ptr) : impl(new Resu   
   tModel(ptr));   
   >   
   > int getProperty() { return impl.getProperty(); }   
   > private:   
   > boost::shared_ptr impl;   
   > };   
      
   It looks like you are introducing more levels of inheritance and   
   indirection here than you would normally need. Once you have Base,   
   there's little cause for ResultBase unless you expect the ResultBase   
   object to outlive the Base object it corresponds to. But as I see   
   you're storing a shared_ptr to Derived in ResultModel, that's not the   
   case. Oh, but Base shouldn't have a getProperty function because of   
   Derived3.   
      
   I would start by asking myself whether or not Derived1 and Derived2   
   really need to be connected by a common Base class. My instinct tells   
   me that you probably don't. If not, something like this works:   
      
   #+BEGIN_SRC c++   
   // untested   
   class Result {   
    struct ResultBase {   
    virtual int getProperty() const = 0;   
    };   
      
    template    
    struct ResultModel : ResultBase {   
    int getProperty() const { return DerivedProperty::value; }   
    ResultModel(T const& x) : value(x) {}   
    T value;   
    };   
    boost::shared_ptr impl;   
    public:   
    template    
    Result(T const& x) : impl(new ResultModel(x)) {}   
    int getProperty() const { return impl->getProperty(); }   
   };   
      
   Derived1 x;   
   Result erased(x);   
   #+END_SRC   
      
   Otherwise, I suppose you'll be passing in a shared_ptr   
   here... but building another level of inheritance seems like overkill to   
   me. Since the property is just a static feature of the Derived type,   
   why not just store it in Result?   
      
   #+BEGIN_SRC c++   
   // untested   
   class Result {   
    boost::shared_ptr impl;   
    int property;   
    public:   
    int getProperty() const { return property; }   
      
    template    
    Result(shared_ptr const& x)   
    : impl(x), property(DerivedProperty::value)   
    {}   
   };   
   #+END_SRC   
      
   > I believe that should be the proper way to do what I have described,   
   > which retains enough flexibility that I can, say, later define a   
   > Derived4 with a well-defined DerivedProperty, and not have to modify   
   > Result. Is this true,   
      
   Yes, that's true.   
      
   > and what other things should I be aware of if it is?   
      
   You should also consider implementing copy construction and assignment   
   to get value semantics (normally this would require a virtual clone()   
   function in ResultBase), or disabling them. Once you have type erasure,   
   value semantics is low-hanging fruit with major benefits. Disabling   
   them might be as easy as using scoped_ptr instead of shared_ptr.   
      
   Cheers,   
      
   --   
   Dave Abrahams   
   BoostPro Computing   
   http://www.boostpro.com   
      
      
    [ See http://www.gotw.ca/resources/clcm.htm for info about ]   
    [ comp.lang.c++.moderated. First time posters: Do this! ]   
      
   --- SoupGate-Win32 v1.05   
    * Origin: you cannot sedate... all the things you hate (1:229/2)   
|