
On Fri, Sep 2, 2011 at 5:45 PM, Andrzej Krzemienski <akrzemi1@gmail.com> wrote:
Comments are always welcome :)
Hi Lorenzo, The number of examples is impressive. It is a pleasure to observe your library growing at this pace. I would like to suggest two things. I could not see from the examples, if the library is capable of supporting "function try blocks" in constructor and destructor definitions. This is what I mean. http://msdn.microsoft.com/en-us/library/e9etx778%28v=vs.71%29.aspx http://www.gotw.ca/gotw/066.htm
I have added this to my TO-DO. Currently this is not possible and I don't know if I can implement it but I will look into it (it's yet another good suggestion you made, thanks). The point is that my macros can essentially expand to any code that might be needed for this but how do I implement this in C++ even if I am willing to program (or have the macros expand to) a lot of boiler-plate code? I need to look into what can be done.
Second, I have seen #ifndef pragmas in some of the examples, e.g., in the body of function myadvance_dispatch you have:
{ #ifndef CONTRACT_CONFIG_NO_LOOP_VARIANTS Distance n_max = n; #endif CONTRACT_LOOP( while(n++) ) { CONTRACT_LOOP_VARIANT( const( n_max, n ) n_max - n ) --i; } }
I am not sure if the necessity to use this conditional code would occur often, but if so, perhaps the code would be slightly more clear if instead the conditional code would be wrapped in an additional macro, like MFC did with DEBUG_ONLY macro:
{ CONTRACT_CONFIG_LOOP_VARIANT_ONLY( Distance n_max = n );
CONTRACT_LOOP( while(n++) ) { CONTRACT_LOOP_VARIANT( const( n_max, n ) n_max - n ) --i; } }
In this case the conditional declaration of n_max is to avoid an "unused variable" warning when loop variants are disabled. However, you can simply program this differently: { Distance m = n; CONTRACT_LOOP( while(m++) ) { CONTRACT_LOOP_VARIANT( const( n, m ) n - m ) --i; } } or accept the warning, or use the pragma. This use case is really a programmers' choice and I wanted to show that using the pragma is one of the options (I will explain this in the docs).
That is, macro CONTRACT_CONFIG_LOOP_VARIANT_ONLY is defined as:
#ifndef CONTRACT_CONFIG_NO_LOOP_VARIANTS #define CONTRACT_CONFIG_LOOP_VARIANT_ONLY(ARG) ARG #else #define CONTRACT_CONFIG_LOOP_VARIANT_ONLY(ARG) #endif
Although I am not sure if a similar trick would be possible with the definition like:
CONTRACT_FUNCTION( template( typename T ) requires( Addable<T> #ifndef CONTRACT_CONFIG_NO_POSTCONDITIONS // Equality only for postconditions. , boost::EqualityComparable<T> #endif ) (T) (add) ( (T) x, (T) y ) postcondition( auto result = return, result == x + y ) ) { return x + y; }
This use case is different then the above (more compelling). The issues here is that when programming contracts you quickly start using extra checks (and especially ==) which add additional requirements on generic types. For example, in this case T does not have to be EqualityComparable to program the body but you still want to program and check the postcondition when T happens to be EqualityComparable. I have been thinking to solve this issue by providing: namespace contract { temlate< typename T> bool equal(T const& left, T const& right) ... } equal will return true if T is not EqualityComparable and left == right if T is EqualityComparable. That way you can program the contracts using contract::equal(x, y) without introducing additional requirements on T at all but still checking the contract conditions for EqualityComparable types. I will also expand equal to handle containers (compare all elements of a vector, etc) and I will provide similar functions for other operations. All of these contract helper functions will be in a separate header <contract/utility.hpp>. I have not started to implement this yet. BTW, I am also wondering if it would be useful to implement a fully contracted version of the STL probably in contract::std: #include <contract/std/vector.hpp> #include <contract/std/algorithm.hpp> contract::std::vector<int> v; contract::std::for_each(v.begin(), v.end(), ...); I think Boost.Contract can essentially implement most/all of the interface contracts (preconditions, postconditions, invariants, and concepts) documented by the SGI: http://www.sgi.com/tech/stl/ Would such a contract::std be any useful? Thanks a lot for your comments! --Lorenzo