Max Lybbert
Posts: 314
Nickname: mlybbert
Registered: Apr, 2005
|
|
Proposed solution
|
Posted: Apr 28, 2005 4:00 PM
|
|
The Stroustrup Approved technique (from his FAQ -- http://www.research.att.com/~bs/bs_faq2.html#virtual-ctor ) is to use an object factory. After giving your situation some thought, this is where I would start (NOTE: I have not run this through a compiler, it may have syntax errors):
class Attorney { public: static template<class C, class Boiler_Plate> C make_contract(int a, double b, std::string c, Boiler_Plate* bp = default_boiler) { // fiddle with bp return new factory_impl<C>(a, b, c, bp); }; };
[code]factory_impl would then be specialized according to class C . The reason for not handling everything in make_contract is that the factory pretty much must be specialized according to class C , and specializing make_contract may be too early (there may be duplication between specializations if you have different kinds of bp s). On top of that, its easier to handle specialization according to Boiler_Plate s in make_contract and specialization according to C s in factory_impl .
After working with this a little, you should be able to determine if the extra step is needed.
Of course, there is no need to templatize. The C++ Programming Language (the book) has an example of a non-templatized factory in section 12.4.4. You could even stop using static methods, so that even your object factories could be polymorphic. However, I prefer:
Attorney::make_contract<my_class> my_class_contract(2, 3.141, s, base_boiler);
to:
Attorney::make_contract(2, 3.141, s, fully_constructed_class_C, base_boiler);
or:
Attorney atty(...); atty.make_contract(...); .
Especially since the templatized version can create a contract before you have a fully-constructed object of class C (you could even call the templatized version as part of class C s constructor).
The real question is how to write factory_impl . You could have it call class C s real constructor, but then other people would want to call it as well, and thats likely going to lead to trouble.
Im not sure if you can declare a template:
template<class C> void make_contract(...) friend class C ...
which you'd need to do if you wanted to create an empty C and initialize it through calling C s methods. I dont like that route anyway, since its just a roundabout way of doing what youve already proposed constructing empty C s and then filling them with C.init s. The problem I see in this approach is that init can fail, leaving you with constructed, but not initialized C s -- that is C s that may violate the invariant.
I think the best move is to either:
(A) have C constructors that Attorney calls to really construct the object, and constructors that users call, and which then call Attorney (which then calls the real constructor) to create the object; or,
(B) make factory_impl a class, and have a C constructor in the form of C(const factory_impl<C>&) . For instance, my_class would have a constructor my_class::my_class(const factory_impl<my_class>) . The last line in make_contract would then change from
return new factory_impl<C>(a, b, c, bp);
to
return new C(factory_impl<C>(a, b, c, bp));
This is an example of the Curiously Recurring Template Pattern.
Personally, I think Id be more likely to get (A) right, although I think (B) has a certain kind of charm. Then again, you could combine the two. ...
|
|