|
Boost : |
Subject: Re: [boost] [contract] member initializers (was "diff n1962")
From: Lorenzo Caminiti (lorcaminiti_at_[hidden])
Date: 2010-04-14 16:43:13
On Sun, Apr 11, 2010 at 10:58 AM, vicente.botet
<vicente.botet_at_[hidden]> wrote:
> I supose that the constructor preconditions are evaluated before the member initialization as this initialization can rely on the preconditions been satisfied. Note also that preconditions/postconditions must to be in the declaration part, and the member initialization is on the definition part.
>
> I understand that it is not easy for Boost.Contract to check the precondition before member initialization . To be able to do that you will need to have a first member or inherit from a 'precondition' class that do this precondition check,
>
> class C : C_constructor_precondition ... {
>
> C(T1 p1) : C_constructor_precondition(p1), ... {
> // ...
> }
> };
>
> but this seems to me a severe constraint and adds more complexity. I think that I can live with preconditions check after member initialization.
Yes, "pre > base-init > member-init" will require to mess with the
inheritance tree and I do not see how that can be done by the
CONTRACT_CONSTRUCTOR() macro which is used at the constructor
declaration level (so inside the class and after its inheritance
declarations). However, special member variables could be used to
implement "base-init > pre > member-init" (as indicated below).
Do you think it is worth implementing "base-init > pre > member-init"
instead of "base-init > member-init > pre"?
For example:
#define ASSERT(cond) if (!(cond)) throw std::runtime_error(# cond)
struct y {
y(int i) {}
};
struct x: y {
x(int i): y(i), x_i_pre_check_(i), num(i) {}
struct x_i_pre_ {
x_i_pre_() {} // Assert nothing (implicitly used by other
constructors).
x_i_pre_(const int& i) {
ASSERT( i >= 123 );
}
} x_i_pre_check_;
x(double d): y(d), x_d_pre_check_(d), num(d) {}
struct x_d_pre_ {
x_d_pre_() {}
x_d_pre_(const double& d) {
ASSERT( d >= 1.23 );
}
} x_d_pre_check_;
int num;
};
I think I can program the CONTRACT_CONSTRUCTOR() macro to do this.
However, limitations are:
1) All member variables must be declared after the constructors.
In this case num is declared at the bottom of the class.
2) Preconditions are NOT checked before base class construction but
only after member variable construction.
This might be OK because base classes will in turn implement their own
preconditions if needed. However, I still think that "pre > base-init
> member-init" is ideal over the "base-init > pre > member-init"
implemented by this approach. That is because the derived class might
have more stringent preconditions than the base classes, preconditions
which could be checked to fail even before the base classes are
constructed.
3) The macro syntax now needs to separate bases from members in the
initializer list (this separation is not done by usual C++ syntax).
This could be done using the following macro syntax:
CONTRACT_CONSTRUCTOR(
(x)( (double)(d) )
(y(d)) // Base class init (before pre).
(precondition)( (i >= 123) )
(num(i)) // Member init (after pre).
(postcondition)( (num == i) )
({}) )
or:
CONTRACT_CONSTRUCTOR(
(x)( (double)(d) ) (y(d)) (members) (num(i)) // Note (members).
(precondition)( (i >= 123) )
(postcondition)( (num == i) )
({}) )
but a more natural C++-like syntax would be:
CONTRACT_CONSTRUCTOR(
(x)( (double)(d) ) (y(d)) (num(i))
(precondition)( (i >= 123) )
(postcondition)( (num == i) )
({}) )
and this syntax cannot be used because I will not know how to inject
the precondition check after y(d) but before num(i).
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk