Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>The key issue is whether or not you should distinguish completely populated objects from incompletely populated objects <strong>at the type level</strong>. If you decide not to make a distinction, then just use <code>boost::optional</code> or similar as you are doing: this makes it easy to get coding quickly. OTOH you can't get the compiler to enforce the requirement that a particular function requires a completely populated object; you need to perform run-time checking of fields each time.</p> <h2>Parameter-group Types</h2> <p>If you do distinguish completely populated objects from incompletely populated objects at the type level, you can enforce the requirement that a function be passed a complete object. To do this I would suggest creating a corresponding type <code>XParams</code> for each relevant type <code>X</code>. <code>XParams</code> has <code>boost::optional</code> members and setter functions for each parameter that can be set after initial construction. Then you can force <code>X</code> to have only one (non-copy) constructor, that takes an <code>XParams</code> as its sole argument and checks that each necessary parameter has been set inside that <code>XParams</code> object. (Not sure if this pattern has a name -- anybody like to edit this to fill us in?)</p> <h2>"Partial Object" Types</h2> <p>This works wonderfully if you don't really have to <em>do</em> anything with the object before it is completely populated (perhaps other than trivial stuff like get the field values back). If you do have to sometimes treat an incompletely populated <code>X</code> like a "full" <code>X</code>, you can instead make <code>X</code> derive from a type <code>XPartial</code>, which contains all the logic, plus <code>protected</code> virtual methods for performing precondition tests that test whether all necessary fields are populated. Then if <code>X</code> ensures that it can only ever be constructed in a completely-populated state, it can override those protected methods with trivial checks that always return <code>true</code>:</p> <pre><code>class XPartial { optional&lt;string&gt; name_; public: void setName(string x) { name_.reset(x); } // Can add getters and/or ctors string makeGreeting(string title) { if (checkMakeGreeting_()) { // Is it safe? return string("Hello, ") + title + " " + *name_; } else { throw domain_error("ZOINKS"); // Or similar } } bool isComplete() const { return checkMakeGreeting_(); } // All tests here protected: virtual bool checkMakeGreeting_() const { return name_; } // Populated? }; class X : public XPartial { X(); // Forbid default-construction; or, you could supply a "full" ctor public: explicit X(XPartial const&amp; x) : XPartial(x) { // Avoid implicit conversion if (!x.isComplete()) throw domain_error("ZOINKS"); } X&amp; operator=(XPartial const&amp; x) { if (!x.isComplete()) throw domain_error("ZOINKS"); return static_cast&lt;X&amp;&gt;(XPartial::operator=(x)); } protected: virtual bool checkMakeGreeting_() { return true; } // No checking needed! }; </code></pre> <p>Although it might seem the inheritance here is "back to front", doing it this way means that an <code>X</code> can safely be supplied anywhere an <code>XPartial&amp;</code> is asked for, so this approach obeys the <a href="http://en.wikipedia.org/wiki/Liskov_substitution_principle" rel="nofollow noreferrer">Liskov Substitution Principle</a>. This means that a function can use a parameter type of <code>X&amp;</code> to indicate it needs a complete <code>X</code> object, or <code>XPartial&amp;</code> to indicate it can handle partially populated objects -- in which case either an <code>XPartial</code> object or a full <code>X</code> can be passed.</p> <p>Originally I had <code>isComplete()</code> as <code>protected</code>, but found this didn't work since <code>X</code>'s copy ctor and assignment operator must call this function on their <code>XPartial&amp;</code> argument, and they don't have sufficient access. On reflection, it makes more sense to publically expose this functionality.</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

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