Boost Concept Library - fusion and other libraries

Lately I've been exploring boost/fusion for a project which I have been
thinking about for 25 years. I believe that this might be the tool to do
what I want. In the course of my experiments with this library and it's
associated documentation, I've been interested in understanding the
libraries "concepts" (e.g. AssociativeSequence, etc) and the relationship if
any to the boost concept library. To try to be more clear, when I see
something in the documentation like "AssociativeSequence", I'm sort of
hoping/expecting to write into my code something like
template<typename S>
struct my_type {
...
BOOST_CONCEPT_ASSERT((

Robert Ramey wrote:
[Noticing that the Boost Concept Check library (BCCL) is not used as much as one might expect from the documentation, however starting to appreciate the value of concepts, and wondering whether there is really a discrepancy between the value of concepts and the extent of the adoption of the BCCL.]
If this isn't a good abstract of your email, please say so. Assuming it's good enough: I wondered about this too. I'll share my personal conclusions with you and hope it helps. For a start, I think the most important purpose of concepts is documentation. You define your concept once, at least in natural language and optionally also through the BCCL formalisms, and after that you can use just a single word again and again to refer to an entire set of operations that should be supported, as well as their semantics. If you adopt this view, it's not so strange for a library to use concepts in the documentation (verbally) but not in the code (as compile-time checks). A secondary value of concepts is that they can make compiler errors more readable. Because the BCCL was developed (almost?) entirely for this purpose, from its documentation it may seem a bit like improving compiler errors is the primary purpose of concepts in general. I think the use of the BCCL (contrary to the use of concepts for documentation) can also have disadvantages, i.e. in terms of maintainability and readability of the code. Hence, when concept requirements are fairly straightforward, the templates in question are so short that the code would almost double if you add a BOOST_CONCEPT_REQUIRES, or the compiler errors aren't too hard to understand after all, this may be enough reason for a library developer to not use the BCCL even though they do use concepts in the documentation. Note, that most (if not all) Boost libraries that don't use the BCCL still use concepts in the names of template arguments or in comments as a form of within-code documentation. For example, I'm quite sure I've seen this in the Boost.Random and the Boost.MinMax header files. Also note that concepts were voted out of the C++0x standard proposal about a year ago (IIRC). This might be partly due to the same reasons that many Boost libraries don't use compile-time concept checks. HTH, Julian

Julian Gonggrijp wrote:
Robert Ramey wrote:
[Noticing that the Boost Concept Check library (BCCL) is not used as much as one might expect from the documentation, however starting to appreciate the value of concepts, and wondering whether there is really a discrepancy between the value of concepts and the extent of the adoption of the BCCL.]
If this isn't a good abstract of your email, please say so.
good enough.
Assuming it's good enough: I wondered about this too. I'll share my personal conclusions with you and hope it helps. For a start, I think the most important purpose of concepts is documentation. You define your concept once, at least in natural language and optionally also through the BCCL formalisms, and after that you can use just a single word again and again to refer to an entire set of operations that should be supported, as well as their semantics. If you adopt this view, it's not so strange for a library to use concepts in the documentation (verbally) but not in the code (as compile-time checks).
I would like to be able to trivially verify that the code and the documention are exactly in sync. It seems to be that BCCL should be able to do this. That is the macros are pretty easy to read and can be easily verified to match the documentation. This is one of the major attractions of using concepts to me.
A secondary value of concepts is that they can make compiler errors more readable. Because the BCCL was developed (almost?) entirely for this purpose, from its documentation it may seem a bit like improving compiler errors is the primary purpose of concepts in general.
To me this is also important
I think the use of the BCCL (contrary to the use of concepts for documentation) can also have disadvantages, i.e. in terms of maintainability and readability of the code.
Hmmm, to me, they don't make the code less readable. In fact, it's the opposite to me. It's like putting in a comment that the compiler understands.
Note, that most (if not all) Boost libraries that don't use the BCCL still use concepts in the names of template arguments or in comments as a form of within-code documentation. For example, I'm quite sure I've seen this in the Boost.Random and the Boost.MinMax header files.
This is the crux of my question. If they work as advertised, I see no downside to using them - especially if you're going to structure your documentation to use them. I'm asking other library authors why they haven't used them. FYI, I know why I didn't use them - it's easy: I didn't understand the concept when I wrote the serialization library. Robert Ramey

Robert Ramey wrote:
Julian Gonggrijp wrote:
I think the use of the BCCL (contrary to the use of concepts for documentation) can also have disadvantages, i.e. in terms of maintainability and readability of the code.
Hmmm, to me, they don't make the code less readable. In fact, it's the opposite to me. It's like putting in a comment that the compiler understands.
I think you make very reasonable points, and also Dave Abrahams has already given excellent input in this discussion, but still let me explain a bit more on my point above. If we compare the following two template definitions with the third: // definition 1 template <class ForwardIter> bool some_algorithm (ForwardIter first, ForwardIter last) { /*...*/ } // definition 2 template <class I> // I: ForwardIterator bool some_algorithm (I first, I last) { /*...*/ } // definition 3 template <class I> BOOST_CONCEPT_REQUIRES( ((boost::ForwardIterator<I>)), (bool) ) some_algorithm (I first, I last) { /*...*/ } then surely the former require less brain resources to interpret and are less error-prone to maintain than the latter, while they are not less self-documenting? I think such brain resource requirement effects shouldn't be underestimated. There is also another minor disadvantage of the macro BOOST_CONCEPT_REQUIRES that affects maintainability in some cases: it breaks automatic symbol recognition in avanced text editors like TextWrangler or Notepad++. For developers who tend to use such advanced text editors this will complicate their workflow. You can alleviate the readability and maintainability issue by using BOOST_CONCEPT_ASSERT rather than BOOST_CONCEPT_REQUIRES, but as the BCCL documentation points out you'll lose a lot of the advantages of BOOST_CONCEPT_REQUIRES in that way. boost.org/doc/libs/1_46_1/libs/concept_check/using_concept_check.htm reads:
One of the nice things about the proposed C++0x syntax for declaring concept constrained function templates is the way that constraints are part of the function declaration, so clients will see them. BOOST_CONCEPT_ASSERT can only express constraints within the function template definition, which hides the constraint in the function body. Aside from the loss of a self-documenting interface, asserting conformance only in the function body can undesirably delay checking if the function is explicitly instantiated in a different translation unit from the one in which it is called, or if the compiler does link-time instantiation.
I'm not saying that the BCCL shouldn't be used, in fact I think it definitely /should/ be used in many cases. I'm only saying that application of the BCCL can also have disadvantages, and that the disadvantages can outweight the advantages when the concept requirements are fairly straightforward or when the "raw" compiler errors aren't too hard to interpret. -Julian

Robert ---
Great questions!
On Sun, Mar 20, 2011 at 3:18 PM, Robert Ramey
this idea is only implemented in graph, icl, iterator and range libraries. Is there a reason for this, or is that it just hasn't been done. If it's the latter, is there a reason I don't see anyone on the requesting that libraries include concept checking classes. Also, reviewers don't seem to mention the lack of these classes in the code - though they complain about it if it's missing from the documentation.
After sort of struggling with the whole concept concept, I'm think I'm coming to appreciate it's utility and maybe it's necessity. But given that no one seems to miss it in the boost libraries, I'm concerned that I'm still not getting it.
Concepts *in documentation* are essential for a generic library in the same way that preconditions e.g. int f(int* p); // Requires: p is non-NULL are essential in the documentation of ordinary libraries: they describe constraints that the user of the library must satisfy but that can't entirely be captured by language constructs, so simply writing down the library interface is insufficient. [By contrast, there are other constraints on the library's user that *can* be entirely captured by language constructs -- e.g. in the example above, that the argument can be converted to an int*; that information is captured in the parameter type declaration] Thus, the description of concept requirements in documentation is an absolute baseline requirement for any generic library. The BCCL lets us do two things: 1. Check that a large fraction of documented concept constraints are satisfied and when they are not, generate errors early enough to avoid deep template instantiation backtraces. This is analogous to the use of assertions at runtime. 2. Check that we've actually documented the right constraints <== **EVERYBODY TENDS TO OVERLOOK THIS ONE, BUT IT HAS THE GREATEST EFFECT ON LIBRARY QUALITY: YOU SHOULD USE ARCHETYPES!** This is analogous to exhaustive random testing at runtime. With the BCCL, there are two things that prevent us from making complete checks for concept constraints: 1. In C++03 anyway, there are some syntactic requirements, e.g. the presence of a certain constructor, that simply can't be checked. 2. Even with all the checking power in the world, you can't check semantic constraints. For example, you can't check that operator== is actually an equivalence relation, as required by the EqualityComparable concept. **this means that concepts in code can never be a complete substitute for concepts in documentation ** Thus, it is a "Best Practice" to use concept checks and archetypes, but not an absolute requirement for generic libraries. The "whole concept concept" is not missing at all in most generic Boost libraries: it's there in the documentation. The translation (to the extent possible) of those concepts into tests and checks in code /is/ sometimes missing. That might be for any number of reasons: * the library author didn't have time to learn BCCL * the library author didn't know about BCCL * the library is used in heavy compile-time code and the cost of making the concept checks could slow compilation to an unacceptable degree, and the library author didn't want to ask users to define a macro to turn them off. I totally agree that it's a great idea to encourage the use of BCCL in generic libraries, and using it can help prevent many defects, but I wouldn't go as far as to say that not using it is, in and of itself, a defect. HTH, -- Dave Abrahams BoostPro Computing http://www.boostpro.com

On Mon, Mar 21, 2011 at 1:01 AM, Dave Abrahams
1. In C++03 anyway, there are some syntactic requirements, e.g. the presence of a certain constructor, that simply can't be checked.
Umm, whoops; this is wrong. There are some things that can't be checked without causing an error upon the check's failure, but BCCL doesn't *want* to avoid compilation errors in these cases. The rest of the post, including the accompanying point #2, still stands. -- Dave Abrahams BoostPro Computing http://www.boostpro.com
participants (3)
-
Dave Abrahams
-
Julian Gonggrijp
-
Robert Ramey