Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>FYI, this is the design I came out with. Thank you Paul and FredOverflow for your inputs. (And Martin York for your comment.)</p> <h1>Step #1, Compile-time polymorphism with templates</h1> <p>Polymorphism is performed at compile-time using templates and <em>implicit-interfaces</em>:</p> <pre><code>template&lt; typename T &gt; class ImgOp { T m_t; // Not a ptr: when ImgOp is copied, copy ctor and // assignement operator perform a *real* copy of object ImageOp ( const ImageOp &amp;other ) : m_t( other .m_t ) {} ImageOp &amp; operator=( const ImageOp &amp; ); public: ImageOp ( const T &amp;p_t ) : m_t( p_t ) {} ImageOp&lt;T&gt; * clone() const { return new ImageOp&lt;T&gt;( *this ); } bool run( Image &amp;i ) const { return m_t.run( i); } }; // Image operations need not to derive from a base class: they must provide // a compatible interface class CheckImageSize { bool run( Image &amp;i ) const {...} }; class CheckImageResolution { bool run( Image &amp;i ) const {...} }; class RotateImage { bool run( Image &amp;i ) const {...} }; </code></pre> <p>Now all the clone-related code lies within a unique class. However, it is now impossible to have a container of <code>ImgOp</code>s templatized on different operations:</p> <pre><code>vector&lt; ImgOp &gt; v; // Compile error, ImgOp is not a type vector&lt; ImgOp&lt; ImgOp1 &gt; &gt; v; // Only one type of operation :/ </code></pre> <h1>Step #2, Add a level of abstraction</h1> <p>Add a non-template base acting as an interface:</p> <pre><code>class AbstractImgOp { ImageOp&lt;T&gt; * clone() const = 0; bool run( Image &amp;i ) const = 0; }; template&lt; typename T &gt; class ImgOp : public AbstractImgOp { // No modification, especially on the clone() method thanks to // the Covariant Return Type mechanism }; </code></pre> <p>Now we can write:</p> <pre><code>vector&lt; AbstractImgOp* &gt; v; </code></pre> <p>But it becomes hard to manipulate image operation objects:</p> <pre><code>AbstractImgOp *op1 = new AbstractImgOp; op1-&gt;w = ...; // Compile error, AbstractImgOp does not have op2-&gt;h = ...; // member named 'w' or 'h' CheckImageSize *op1 = new CheckImageSize; op1-&gt;w = ...; // Fine op1-&gt;h = ...; AbstractImgOp *op1Ptr = op1; // Compile error, CheckImageSize does not derive // from AbstractImgOp? Confusing CheckImageSize op1; op1.w = ...; // Fine op1.h = ...; CheckImageResolution op2; // ... v.push_back( new ImgOp&lt; CheckImageSize &gt;( op1 ) ); // Confusing! v.push_back( new ImgOp&lt; CheckImageResolution &gt;( op2 ) ); // Argh </code></pre> <h1>Step #3, Add a "cloning pointer" class</h1> <p>Based on the FredOverflow's solution, make a cloning pointer to make the framework simpler to use.<br> However, this pointer needs not to be templatized for it is designed to hold only one type of ptr -- only the ctor needs to be templatized:</p> <pre><code>class ImgOpCloner { AbstractImgOp *ptr; // Ptr is mandatory to achieve polymorphic behavior ImgOpCloner &amp; operator=( const ImgOpCloner &amp; ); public: template&lt; typename T &gt; ImgOpCloner( const T &amp;t ) : ptr( new ImgOp&lt; T &gt;( t ) ) {} ImgOpCloner( const AbstractImgOp &amp;other ) : ptr( other.ptr-&gt;clone() ) {} ~ImgOpCloner() { delete ptr; } AbstractImgOp * operator-&gt;() { return ptr; } AbstractImgOp &amp; operator*() { return *ptr; } }; </code></pre> <p>Now we can write:</p> <pre><code>CheckImageSize op1; op1.w = ...; // Fine op1.h = ...; CheckImageResolution op2; // ... vector&lt; ImgOpCloner &gt; v; v.push_back( ImgOpCloner( op1 ) ); // This looks like a smart-ptr, this is not v.push_back( ImgOpCloner( op2 ) ); // confusing anymore -- and intent is clear </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload