Boost logo

Boost :

Subject: Re: [boost] [Review:Contract] Some questions
From: Lorenzo Caminiti (lorcaminiti_at_[hidden])
Date: 2012-08-30 15:24:15


On Mon, Aug 27, 2012 at 1:05 PM, Lorenzo Caminiti <lorcaminiti_at_[hidden]> wrote:
> On Mon, Aug 27, 2012 at 10:10 AM, Vicente J. Botet Escriba
> <vicente.botet_at_[hidden]> wrote:
>> Le 27/08/12 17:17, Lorenzo Caminiti a écrit :
>>
>>> On Sun, Aug 26, 2012 at 11:33 PM, Vicente J. Botet Escriba
>>> <vicente.botet_at_[hidden]> wrote:
>>>>
>>>> Le 27/08/12 03:20, Lorenzo Caminiti a écrit :
>>>>
>>>>> On Sun, Aug 26, 2012 at 10:04 AM, Vicente J. Botet Escriba
>>>>> <vicente.botet_at_[hidden]> wrote:
>>>>>>
>>>>>> Le 26/08/12 11:16, Vicente J. Botet Escriba a écrit :
>>>>>>
>>>>>> = member initializers =
>>>>>>
>>>>>> Why the following limitation?
>>>>>> "Unfortunately, when member initializers are specified, the constructor
>>>>>> body
>>>>>> must be defined together with its declaration and contract."
>>>>>
>>>>> See note 34:
>>>>>
>>>>>
>>>>> http://contractpp.svn.sourceforge.net/viewvc/contractpp/releases/contractpp_0_4_1/doc/html/contract__/tutorial.html#ftn.contract__.tutorial.forward_declarations_and_body_definitions.f3
>>>>
>>>> I have read these notes. This seems to me a *hard* limitation and I would
>>>
>>> Why "hard"? (For example, take a look to what Boost.Parameter requires
>>> to deal with constructs...)
>>>
>>> You can just program the constructors with member initializers in the
>>> class declarations when using contracts:
>>>
>>> CONTRACT_CLASS(
>>> class (x)
>>> ) {
>>> CONTRACT_CLASS_INVARIANT( void )
>>>
>>> CONTRACT_CONSTRUCTOR(
>>> public (x) ( void )
>>> initialize( a_(1), b_(2), c_(3) )
>>> ) {
>>> ... // body right here
>>> }
>>>
>>> private: int a_, b_, c_;
>>> };
>>>
>>> If the body is really just too complex for an header... then you can
>>> always separate it with an init function (essentially, only the
>>> initializer list remains in the class declaration):
>>>
>>> CONTRACT_CLASS(
>>> class (x)
>>> ) {
>>> CONTRACT_CLASS_INVARIANT( void )
>>>
>>> CONTRACT_CONSTRUCTOR(
>>> public (x) ( void )
>>> initialize( a_(1), b_(2), c_(3) )
>>> ) {
>>> init(); // simple body
>>> }
>>>
>>> private: void init ( void ); // deferred
>>>
>>> private: int a_, b_, c_;
>>> };
>>>
>>> // possibly in another file...
>>> void x::init ( void )
>>> {
>>> ... // body now here (far from the declaration)
>>> }
>>
>> You are right, hard was to strong ;-) Maybe the workaround merits to be
>> include in the documentation.
>
> Will do.
>
>>>> like you explain here deeply and broadly why, maybe someone could give
>>>> you
>>>> some ideas on alternatives.
>>>
>>> With C++11 delegating constructors, this limitation /could/ be removed
>>> -- but I'm not sure because I didn't try. I will follow up with a
>>> separate email thread listing some snapshot of the generated code so
>>> we can discuss ideas and details :)
>>
>> Yes, showing the generated code will help.
>
> This might take me a couple of days because I'm busy with something
> else right now... stay tuned.

The CONSTRUCTOR macro in the following:

CONTRACT_CLASS(
    template( typename T )
    class (vector)
) {
    CONTRACT_CLASS_INVARIANT( void )

    CONTRACT_CONSTRUCTOR(
        public (vector) ( (typename std::vector<T>::size_type) count )
            initialize( vector_(count) )
    ) {}

    public: std::vector<T> vector_;
};

Expands to something like this (this is just an extract of the code):

    public : vector ( typename std::vector<T>::size_type count )
        : ::contract::aux::call_constructor_entry<
ERROR_missing_class_contract >(

contract_auxXcontract_auxXconstructorXXsignature_traitsX13X ::type()
                , count
                , & ERROR_missing_class_contract ::
contract_auxXstatic_class_invariantX
                , & ERROR_missing_class_contract ::
contract_auxXcontract_auxXconstructorXXpreconditionX
            )
          , vector_(count)
    {
        ::contract::aux::call_constructor_exit<
contract_auxXcontract_auxXconstructorXXsignature_traitsX13X ::type >(
              & ERROR_missing_class_contract ::
contract_auxXcontract_auxXconstructorXXbodyX
            , count
            , this
            , & ERROR_missing_class_contract ::
contract_auxXstatic_class_invariantX
            , & ERROR_missing_class_contract :: ERROR_missing_class_invariant
            , & ERROR_missing_class_contract ::
contract_auxXcontract_auxXconstructorXXoldofX
        );
    }
    public : void contract_auxXcontract_auxXconstructorXXbodyX (
typename std::vector<T>::size_type count )
    {}

1) I need an extra base class instantiation which depends on
signature_traitsX13X (the extra base class checks pre before
initializers). 13 is the macro expansion line number and that's known
only at the declaration site (there' no way the CONSTRUCTOR_BODY macro
can detect that). The signature_traitsX13X is needed to correctly
handle overloaded functions/constructors. The CONSTRUCTOR_BODY macro
can't reproduce the signature_traitsX13X because it doesn't know the
constructor signature, just it's name.

2) I need to implement the destructor and then declare the body
function. The CONSTRUCTOR_BODY macro can't do the body declaration
part because it doesn't know the constructor signature, just it's
name.

template< typename T >
CONTRACT_CONSTRUCTOR_BODY(vector<T>, vector) ( typename
std::vector<T>::size_type count )
{}

Expands to either:

template< typename T >
void vector<T>::contract_auxXcontract_auxXconstructorXXbodyX (
typename std::vector<T>::size_type count )
{}

Or just the following when no contracts are disabled:

template< typename T >
void vector<T>::vector ( typename std::vector<T>::size_type count )
{}

If I want to allow member initializers for separated constructor
definitions then I have to complicate the BODY macro to take the
entire constructor declaration again (not just the class type and
name):

    CONTRACT_CONSTRUCTOR_BODY(
        template( typename T )
        (vector<T>)(vector) ( (typename std::vector<T>::size_type) count )
            initialize( vector_(count) )
    ) {}

So I can program signature_traits, the extra base class instantiation,
the constructor body, and then the actual body function signature as
done now by the CONTRACT_CONSTRUCTOR macro. But then the usual syntax
propagates to the BODY macro as well :(

Which delegating constructors I might be able to get around this issue
by leaving the extra base class instatiation in the
CONTRACT_CONSTRUCTOR macro expansion but then allowing the member
initializers outside such a macro in the delegated constructor and
therefore also after the BODY macro as the body function will no
longer be a member function but it will be the delegated
constructor... I'm not sure if I can actually do that...

HTH,
--Lorenzo


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk