|
Boost : |
Subject: Re: [boost] [Review:Contract] Andrzej's review
From: Andrzej Krzemienski (akrzemi1_at_[hidden])
Date: 2012-08-31 05:41:17
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.
> > (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. 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.
> > (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.
Regards,
7rzej
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk