|
Boost : |
Subject: Re: [boost] [Review:Contract] Andrzej's review
From: Lorenzo Caminiti (lorcaminiti_at_[hidden])
Date: 2012-09-01 17:26:55
On Fri, Aug 31, 2012 at 2:41 AM, Andrzej Krzemienski <akrzemi1_at_[hidden]> wrote:
> 2012/8/30 Lorenzo Caminiti <lorcaminiti_at_[hidden]>
>
>> On Wed, Aug 29, 2012 at 2:48 PM, Andrzej Krzemienski <akrzemi1_at_[hidden]>
>> wrote:
>> > I believe the community needs BbC framework: it is necessary in order to
>> be
>> > able to program consciously.
>>
>> I personally agree, that's why I start the documentation with the quote:
>>
>> Our field needs more formality, but the profession has not realized it
>> yet.
>> --Meyer (see [Meyer97] page 400)
>>
>> However, my impression is that the general C++ community is more on
>> the "profession" side ;)
>>
>
> This is my impression also...
>
>
>> > you cannot use some C++ constructs that you
>> > would otherwise use;
>>
>> Like, for example? (Because I made a conscious effort to support "all"
>> C++ declaration features in the DSEL, if at all possible...) I know of
>> function pointers, arrays, member initializers in deferred
>> implementations but there are workaround for those. Anything else?
>>
>
> Indeed, the support for C++03 declaration is satisfactory. When i wrote it
> I mostly meant the limitation where "when member initializers are
> specified, the constructor body must be defined together with its
> declaration and contract". Apart from this and array or function types I
> did not think of anything else. But now, that I think of it, I wonder how
> vendor-speciffic extensions are (or could be) supported, like Microsoft's
> __declspec? While they are not C++-standard I must use them when
> implementing a DLL.
No non-standard declaration feature are currently supported. I'll
consider supporting some in the future:
https://sourceforge.net/apps/trac/contractpp/ticket/69
Especially if they are simple pass through for the lib (i.e., the pp
parses them and the implementation of the macro just expands the C++
declaration code), it's trivial to implement this stuff.
>> > (13)
>> > The documentation says that you can use protected and private members in
>> > contracts because "C++ provides programmers ways around access level
>> > restrictions (e.g., friend and function pointers)". Next, we read that
>> > "only public member functions shall check class invariants. Private and
>> > protected member functions are allowed to brake class invariants because
>> > private and protected member are part of the class implementation and not
>> > of its specification".
>>
>> This 2nd statement is correct. The 1st statement means that in C++ you
>> can never prevent people from programming junk like this which will
>> break the public interface (the same can be done without static):
>>
>> #include <iostream>
>>
>> class x
>> {
>> public: static void pub ( void ) { std::cout << "pub" << std::endl; }
>> private: static void priv ( void ) { std::cout << "priv" << std::endl;
>> }
>>
>> typedef void (*fptr) ( void );
>> public: static fptr bad ( void ) { return priv; }
>> };
>>
>> int main ( void )
>> {
>> x::pub();
>> void (*priv) ( void ) = x::bad();
>> priv(); // calls a private function :(
>> return 0;
>> };
>>
>> The call to priv via the function pointer is done outside the class
>> implementation but it won't check the invariants--but if you program
>> "bad" then you're on your own! That's all I wanted to say with the 1st
>> statement (which is actually an observation from The C++ Programming
>> Language, Stroustrup). I can clarify this in the docs.
>>
>
> I feel I have been misunderstood. Let me clarify. I understand how one can
> overcame the member access restrictions in C++ with hacks. I do not think
> you need to clarify it in the docs. The way I understand the docs is that
> you use these hacks as rationale for allowing private and protected members
> in contracts.
No. Private and protected members are allowed in contracts because
they can have pre and post even if they never check inv. Public
members are allowed in contract because they have pre, post, and also
check inv.
On a separate note, you cal always call a private or protected member
from a public scope using the access hacks. If you do so beware that
inv will not be checked (as they shouldn't because you are calling a
private or protected member) even if the call is made from a public
scope--you're on your own.
That's what I tried to say in the docs. I'll clarify the docs.
> Or in other words, that you chose to allow private members in
> contracts only because "member access hacks" are possible. I think that
> this rationale is too week. Personally, I feel that I would rather have
> only public members allowed in contract specifications. I could be
> convinced otherwise, but I find the above argument unconvincing.
>
>
>>
>>
>> > (16)
>> > Functions like set_precondition_broken() are defined with dynamic
>> exception
>> > specification: throw(). Is this necessary? throw() is known to be causing
>> > problems, adds little value, and some compilers do not support it; some
>> > other issue warnings on dynamic exception specifications; and they are
>> > deprecated.
>>
>> It's the same for set_terminate that is throw() and it was specified by
>> N1962:
>>
>> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1962.html#failure-handler-functions
>>
>> I think you don't want the handler setting function to throw given
>> that what you are setting is the handler itself which will affect how
>> you will handle a thrown exception (e.g., while evaluating a contract
>> assertion).
>>
>
> I agree that this function had better not throw. But...
> You implement this function: you can implement it so that it does not throw
> and guarantee that in the documentation, but you need not use "throw()"
> syntax. By typing "throw()" your implementation does not throw less. But
> you can make the function execute slower because now the run-time will also
> have to check if your function throws (you know it doesn't - but the
> compiler or run-time does not) and if so, call std::unexpected(), and so
> on...
>
> I understand than n1962 proposes it, but dynamic exception specifications
> were not deprecated at the time it was proposed. Now it would probably be
> proposed as noexcept(true).
>
> Also, see this Boost guideline
> http://www.boost.org/development/requirements.html#Exception-specification.
I could provide a config macro to enable disable the throw(). I'll use
noexcept(true) in C++11. That's also what C++11 did for set_terminate:
http://en.cppreference.com/w/cpp/error/set_terminate
>> > (20)
>> > In "Contract Programming Overview" -> "Benefits", in the note we read,
>> > "However, the probability that programmers make a mistake twice (in both
>> > the body *and* the contract) is lower than the probability that the
>> mistake
>> > is made just once (in either the body or the contract)". This implies
>> that
>> > we write contracts in order that the code be duplicated because this
>> > reduces the probability of the bug. I believe that the mechanism of
>> testing
>> > in general is somewhat different (contract-checking is similar to unit
>> > testing in this respect, I think): you want the tests to be much simpler
>> > then the code they test.
>>
>> No, this is just about probabilities. Say p is the probability to
>> program a bug in the body and q the probability to program a bug in
>> the contracts for a given function. Le'ts assume independence of the
>> events for programming a bug in the implementation and programming a
>> bug in the contracts for that function. Then the probably r of
>> programming a bug in both the implementation and the contracts of that
>> function is given by the product r = p * q. Given that p < 1 and q < 1
>> then r < p and r < q. This doesn't assume you duplicate code, it's
>> just that if you do two things independently it's less likely you mess
>> both of them up :) See Section 3.6 of N1613:
>> http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2004/n1613.pdf
>>
>
> Well, I am unconvinced. I understand the argumentation in N1613 but I do
> not feel this is a practical value gained from contracts. Using the
> probabilistic notions, I question the validity of the assumption that p and
> q are independent. But I am not trying to convince you. Just expressing my
> 'feeling'.
>
>
>>
>> > Therefore when some test fails you know (although
>> > not for sure) that it is the code that is broken rather than the test.
>> For
>> > instance:
>> >
>> > double sqrt(double v)
>> > postcondition{ close(return * return = v); };
>> >
>> > Testing almost the same thing as function body makes sense because
>> > multiplication is conceptually much simpler than the logic to find the
>> > square root. Conversely, writing this:
>> >
>> > bool invert(bool b)
>> > postcondition{ b == !return; };
>> >
>> > Would be of little practical value, and I believe if there is a chance I
>>
>> No, contract programming would ask you to leave the b == !return
>> postcondition (Meyer is pretty clear about this). Of course you can do
>> whatever you want but when you write the contracts you don't know
>> about the implementation (e.g., a complex inversion algorithm that
>> does some optimized assembly bit operation could be used by the body
>> but the post still wants b == !return at the end of the day).
>>
>
> This is theoretically convincing, but I cannot imagine using such principle
> in practice. I would never think of implementing 'invert' in any other way
> than '!v'. And since it is so simple I would likely make it an inline
> function:
>
> bool invert(bool b) postcondition{ b == !return; } { return !v; }
>
> And I would not be comfortable encouraging my colleagues to write inline
> functions like this. And using the probabilistic argument for rationale.
> Again, I do not want to discourage anyone from specifying contracts. But I
> somehow feel Contract Programming offers more important benefits than the
> above probability.
IMO, this is just my personal opinion, Contract Programming comes with
some trade-off (at least run-time, and with my lib compile-time and
DSEL syntax) so I respect that people will weight these trade-offs and
decide when to program contract. A simple function like the one you
show above might be an example were the costs out-weight the benefits
given the trivial implementation and the low probability of getting it
wrong (I should never say this... that's when things start going wrong
;) ). The docs propose the probability argument, among others
rationales, as it was listed in N1613 (and I think even in Meyer97)
with the intent to inform the read so he/she can make the best
decision on what contracts to program. That's all :)
Thanks.
--Lorenzo
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk