Changes between Version 35 and Version 36 of BestPracticeHandbook


Ignore:
Timestamp:
Jun 3, 2015, 2:30:20 PM (7 years ago)
Author:
Niall Douglas
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • BestPracticeHandbook

    v35 v36  
    532532Finally, specifically for Boost libraries we have an [http://www.boost.org/development/tests/master/developer/summary.html automated regression testing] system which works by various end users uploading XML results generated by Boost.Test to an FTP site where a cron script regularly runs to generate static HTML tables of passing and failing tests. Needless to say, if your library was as useful as possible to that system everybody wins, and your library is not as useful to that system if it uses assert() and even static_assert() because the XML uploaded is a compiler error console log or an assert failure diagnostic instead of a detailed list of which tests passed and which failed.
    533533
    534 Hopefully by now I have persuaded you to use an XML outputting unit test framework. If you are a Boost library, the obvious choice is to use Boost.Test. Despite its many problems, being slow to develop against and lack of maintenance in its release branch, Boost.Test is still a very competitive choice, and if you ignore the overly dense documentation and simply lift the pattern from this quick sample you'll be up and running very quickly:
     534Hopefully by now I have persuaded you to use an XML outputting unit test framework. If you are a Boost library, the obvious choice is to use Boost.Test. Despite its many problems, being slow to develop against and lack of maintenance in its release branch ('''NOTE:''' [https://raffienficiaud.github.io/test/boost_test/change_log.html Boost.Test v3 is now in testing, and should replace Boost.Test v2 soon]), Boost.Test is still a very competitive choice, and if you ignore the overly dense documentation and simply lift the pattern from this quick sample you'll be up and running very quickly:
    535535
    536536{{{#!C++
     
    571571 BOOST_FAIL(msg):: Immediately exit this test case with a message.
    572572
    573 Boost.Test provides an enormous amount of extra stuff (especially in its unstable branch) for all sorts of advanced testing scenarios, but for most software being developed in a person's free time most of those advanced testing facilities don't provide sufficient benefit for the significant added cost of implementation. Hence, for personally developed open source the above primitive checks, or a combination thereof into more complex solutions, is likely sufficient for 99% of C++ code. There is also a very specific reason I chose this exact subset of Boost.Test's functionality to suggest using here, because [https://github.com/ned14/Boost.APIBind/blob/master/include/boost/test/unit_test.hpp Boost.APIBind]'s lightweight header only Boost.Test emulation defines just the above subset and usefully does so into a header inside APIBind called "boost/test/unit_test.hpp" which is identical to the Boost.Test header path, so if you include just that header you get compatibility with APIBind and Boost.Test. In other words, by using the pattern just suggested you can:
     573Boost.Test provides an enormous amount of extra stuff (especially in its v3 branch) for all sorts of advanced testing scenarios, but for most software being developed in a person's free time most of those advanced testing facilities don't provide sufficient benefit for the significant added cost of implementation. Hence, for personally developed open source the above primitive checks, or a combination thereof into more complex solutions, is likely sufficient for 99% of C++ code. There is also a very specific reason I chose this exact subset of Boost.Test's functionality to suggest using here, because [https://github.com/ned14/Boost.APIBind/blob/master/include/boost/test/unit_test.hpp Boost.APIBind]'s lightweight header only Boost.Test emulation defines just the above subset and usefully does so into a header inside APIBind called "boost/test/unit_test.hpp" which is identical to the Boost.Test header path, so if you include just that header you get compatibility with APIBind and Boost.Test. In other words, by using the pattern just suggested you can:
    574574
    5755751. With a macro switch turn on full fat Boost.Test.
     
    15191519== 18. COUPLING/SOAPBOX: Essay about wisdom of defaulting to standalone capable modular (Boost) C++ 11/14 libraries with no external dependencies ==
    15201520
    1521 If the last section was somewhat speculative due to the present uncertainties about the future developments in C++, these remaining two sections are almost entirely discussion pieces as they have no known good answers. Consider them therefore more as food for thought rather than recommendations. I'll make the following discussion entirely Boost centric, but lots of corporations out there (Google is the most famous example) have truly enormous monolithic C++ codebases where this discussion equally applies if not far more so, so please consider find and replacing all mentions of Boost with ''<insert your large C++ code base here>''.
    1522 
    1523 A very frequently heard request on boost-dev and boost-users is for Boost to become "more modular", where modularity can mean quite a few different things depending on the claimant. Here are some of the more common meanings of "modularity":
    1524 
    1525 1. I would like Boost (as a whole) to have fewer system requirements (minimum compiler versions, minimum OS support etc). This is usually really an argument in favour of better support for either legacy compilers OR embedded or games systems, but do note the substantial distinction for later.
    1526 
    1527 2. I would like my favourite Boost libraries to have fewer requirements on their dependencies including other Boost libraries, especially exact version requirements (i.e. I want my favourite Boost library to detect and work with multiple versions of Boost). This is usually an argument from those who experience problems with things mildly breaking in different places in each Boost release, and they end up having to mash up their own Boost distro made up of newer and older individual Boost libraries to get the stability they need.
    1528 
    1529 3. I would like my favourite Boost libraries to optionally not have the word "Boost" in their names as my employer's legal department have explicitly banned all Boost libraries now and forever in perpetuity.
    1530 
    1531 4. I would like to download my favourite Boost libraries and only their strict dependencies within Boost without having to download or even consider during build or configuration any unnecessary other Boost libraries (i.e. the package manager argument).
    1532 
    1533 5. I would like to use my favourite Boost library using the Standard C++ Library facilities that come with C++ 11 instead of being forced into using Boost near equivalents (i.e. I get annoyed dealing with mixes of `std::future` and `boost::future`).
    1534 
    1535 6. I would like to drop my favourite Boost library/libraries into my project as a single giant include file with no need to worry about Boost.Build or any build system or even dealing with a Boost source control system.
     1521If the last section was somewhat speculative due to the present uncertainties about the future developments in C++, these remaining two sections are almost entirely discussion pieces as they have no known good answers. Consider them therefore more as food for thought rather than recommendations, and they are obviously my (Niall Douglas) personal perspective and interpretation on things. I'll make the following discussion entirely Boost centric, but lots of corporations out there (Google is the most famous example) have truly enormous monolithic C++ codebases where this discussion equally applies if not far more so, so please consider find and replacing all mentions of Boost with ''<insert your large C++ code base here>''.
     1522
     1523=== A brief and cynical history of Boost and its relationship to C++ ===
     1524
     1525Boost has been experiencing in recent years a number of problems surrounding its scalability as it grows and especially as it matures (specifically, maintenance and cultural fear of any substantial change). Historically speaking, [https://web.archive.org/web/19991011120524/http://www.boost.org/ 1999 Boost] was first [http://www.boost.org/users/proposal.pdf a proving ground for new C++ standard library ideas] making better use of the 1998 C++ ISO standard, but from [https://web.archive.org/web/20000816132250/http://www.boost.org/libs/compiler_status.htm August 2000 onwards] it also began to turn into a broken compiler workarounds layer which whilst initially was great for those with broken compilers, it incurred an enormous technical debt into the codebase which still weighs heavily upon anyone trying to change anything substantial affecting more than one library, including cultural beliefs in the importance of support of broken toolsets. Due to its roots as a proving ground for standard C++ libraries, Boost is unusually similar to the C++ standard library which is both good and bad: good in terms of the quality of design, implementation and testing - bad in terms of monolithicity, poor coupling management, and over-reliance on a single person in charge of each library (great for the library if that maintainer is active, not so great across libraries when maintainers must work together, terrible if a maintainer vanishes or departs).
     1526
     1527Boost's "high point" was probably between 2001 [https://web.archive.org/web/20010614021213/http://www.boost.org/index.htm when the modern peer review process was formalised] (and has remained remarkably, perhaps unfortunately, unchanged since) when it had thirty three libraries and 2005 [https://web.archive.org/web/20050815003310/http://www.boost.org/ when the Boost Software Licence was completed, most of the famous Boost libraries had reached their final designs, and ten Boost libraries were accepted into the C++ TR1 standard] when it had sixty-nine libraries.
     1528
     1529Some may be surprised at the choice of 2005 given that BoostCon began in 2006, but if you examine the Wayback Machine for boost.org from 2005 onwards you will see a plateau was reached from 2005 onwards, and I'm going to claim that this is because Boost had achieved its original mission for the first time and had therefore taken its first step towards obsoleting itself. The push was now on for the C++ 0x ISO standard, and I think the period 2007-2008 was crucial because in my opinion it was during this period that many of the Boost old timers began to become bitter with the C++ library process as their items began to be cut for the first initial draft of what would become C++ 11 for purely political reasons. The big problem was that if you proposed some library X for standardisation, someone on ISO would say "library X would look completely different once expected new language feature Y is in the language, therefore library X shouldn't enter the standard right now". The most damaging expected new language feature Y was undoubtedly original C++ concepts as that killed off entire tracts of exciting C++ library standardisation, much to the often bitterness of those who had invested months to years of their spare time developing those libraries. After all, most new language features are developed by highly paid employees where it is their day jobs, whereas of the twenty or so Boost libraries in C++ 11 were generally developed in the family time of enthusiasts who earned a fraction of the pay of those killing off their work, and to keep a chipper attitude whilst those who have not sacrificed shoot down often years of your work is not easy.
     1530
     15312008 was the last time the Boost web site was in any way improved, and even before that stale content was no longer being removed or repaired as it once used to be, thus making the Boost web presence increasingly less authoritative and less a "one stop shop" for answers. We then began to see a slow exodus of Boost old timers, some leaving C++ completely for good for employment in large corporations which frown on employees having any interests outside the corporation, some disavowing Boost forever and having anything to do with Boost, and some no longer trying to get their libraries into Boost (i.e. past any form of review process) and preferring to house their C++ libraries elsewhere and specifically away from the Boost community. A too frequent common factor this author has noticed is the bitterness and anger regarding Boost and its community in some of the big names in Boost who were the minds behind some of its most successful libraries. The peer review process ground to a halt from the end of 2012 onwards, and [https://www.youtube.com/watch?v=8TkCwizP14Y by early 2014 a noticeable increase was obvious in the rate of decline of Boost] since the 2011 ISO C++ standard began to be implemented in available toolchains.
     1532
     1533Since 2014 measures have been taken to reinvigorate Boost by a number of actors including myself, but those measures are not germane to this essay. What I will say is that by Summer 2015, by the strictest measure of these things, perhaps as many as sixty of the one hundred and thirty or so Boost libraries could be considered undermaintained or not maintained. That suggests that Boost, under its current set of procedures, culture, management and infrastructure, is struggling to scale past about seventy libraries -- or put another way, the malaise and staleness within Boost started around the same time as the number of libraries in Boost passed about seventy in 2005. The scaling limit being around seventy is of particular significance because that is exactly around where the human cognitive limit for physically separated group sizes lands, and indeed military tactical units have been organised around groups of sixty to eighty soldiers since the Romans simply because trial and error found that number worked best. ''I am therefore going to assert, rather than claim, that under a volunteer based system where one individual is the maintainer of each library that Boost cannot healthily exceed about seventy libraries (i.e. seventy maintainers) as a single collection'', and you will find all my efforts to reinvigorate Boost - including this Handbook - revolve around that assertion of there being a hard scaling limit to each collection.
     1534
     1535
     1536=== What does this history have to do with defaulting to standalone capable modular C++ libraries? ===
     1537
     1538The obvious answer is that if a library is capable of coexisting inside multiple collections of libraries including the collection of just itself alone, that enormously increases the scope and opportunity for working around the size limit of seventy. You could, as I have often proposed, have a v1.x collection of legacy C++ 03 Boost libraries and a v2.x collection of C++ 14 Boost libraries where there is some overlap between the collections in that some C++ 03 Boost libraries are available in both collections. One therefore raises the scaling limit from about seventy to potentially one hundred and twenty or so for two collections without suffering from undermaintenance, malaise or staleness as an organisation. If your library defaults to being standalone capable and modular, that ''enormously'' improves your ability to have your library coexist in multiple collections of libraries. [https://github.com/boostcon/cppnow_presentations_2015/raw/master/files/Large-Projects-and-CMake-and-git-oh-my.pdf You therefore are writing ''social code'', not ''a-social code''].
     1539
     1540However, let's assume you don't buy the hard scaling limit to a collection size and instead have some more practical use scalability problems such as:
     1541
     1542* I would like Boost (as a whole) to have fewer system requirements (minimum compiler versions, minimum OS support etc). This is usually really an argument in favour of better support for either legacy compilers OR embedded or games systems, but do note the substantial distinction for later.
     1543
     1544* I would like my favourite Boost libraries to have fewer requirements on their dependencies including other Boost libraries, especially exact version requirements (i.e. I want my favourite Boost library to detect and work with multiple versions of Boost). This is usually an argument from those who experience problems with things mildly breaking in different places in each Boost release, and they end up having to mash up their own Boost distro made up of newer and older individual Boost libraries to get the stability they need.
     1545
     1546* I would like my favourite Boost libraries to optionally not have the word "Boost" in their names as my [https://stackoverflow.com/questions/755439/boost-is-just-great-and-free-is-there-a-catch employer has explicitly banned all libraries with Boost in their name now and forever in perpetuity, requiring me to duplicate Boost code which passes the same Boost unit test suite as the original].
     1547
     1548* I would like to download my favourite Boost libraries and only their strict dependencies within Boost without having to download or even consider during build or configuration any unnecessary other Boost libraries (i.e. the package manager argument).
     1549
     1550* I would like to use my favourite Boost library using the Standard C++ Library facilities that come with C++ 11 instead of being forced into using Boost near equivalents (i.e. I get annoyed dealing with mixes of `std::future` and `boost::future`).
     1551
     1552* I would like to drop my favourite Boost library/libraries into my project as a single giant include file with no need to worry about Boost.Build or any build system or even dealing with a Boost source control system.
     1553
     1554As much as these often cited user problems might seem unrelated, they are in fact all due to the lack of:
     1555
     15561. '''Modularity'''
     1557
     1558 Your users can download your library as a self contained distribution, and get immediately to work. The ideal of this form is a single standalone include file which contains everything the library user needs.
     1559
     15602. '''Encapsulation'''
     1561
     1562 Thinking about and specifying your dependencies properly instead of just firing in some reference to `boost::lib::foo` and dragging in a whole library just for a single routine. [http://arxiv.org/abs/1405.3323 C++ has also spectacularly failed to improve on Microsoft COM as the best available technology for fully encapsulating C++ libraries unfortunately], and as much as such work is "unsexy" the fact a proper C++ component and modularisation system doesn't have [https://isocpp.org/std/the-committee a study group at ISO WG21] is an appalling tragedy given the [https://en.wikipedia.org/wiki/Service-orientation enormous and very well understood productivity gains] you get from such a technology.
     1563
     15643. '''Low cost trial'''
     1565
     1566 From a library user perspective, the biggest overwhelming incentive is usually to adopt "[https://en.wikipedia.org/wiki/Not_invented_here Not Invented Here]" as that best ensures continued employment at least in the medium term. So as a library author, if you want anyone to use your library you need to eliminate as many reasons for a library user to find an excuse not to use your library as possible. One of the biggest excuses users will have is simply that of the cost of trying out your library for fit to solving a problem: is installation ready for development a single action? Does including this library quadruple my build times? Does code using this library ever crash, including the compiler? Do I have to click the mouse more than three times to find something in the documentation? If the answer is yes to any of those questions, your library does not have a low cost trial curve, and could even be ''a-social coding''.
     1567
     15684. '''Forcing choices onto the library user unnecessarily'''
     1569
     1570 When your library insists on only ever using `boost::future` and being incapable of using `std::future`, you force your library users to write boilerplate to convert between types of future because another library dependency insists on only ever using `std::future`. There is no good reason whatsoever why your library forces that choice on its users, except your laziness in not upgrading your library to make better use of the C++ 11 standard library.
     1571
     1572Most of the earlier problems go away if you address these four issues, but you'll note that dependency management is essentially being statically done by hand by the library maintainer due to that lack of a modern equivalent to Microsoft COM I mentioned. The wisdom of dependency package managers in C++ 11/14 is exactly what I'll write about next.
     1573
     1574== 19. COUPLING/SOAPBOX: Essay about wisdom of dependency package managers in C++ 11/14 ==
    15361575
    15371576TODO
    15381577
    1539 I'm going to argue in favour of defaulting your C++ 11/14 libraries to use no external dependencies i.e. any C++ headers not in your git repository and not in the STL, which includes Boost. External dependencies do ''''not'''' include any git submodules you may have in your git repository, so if your user types:
    1540 
    1541 {{{
    1542 git clone http://yourlib
    1543 cd yourlib
    1544 git submodule update --init --recursive
    1545 cmake
    1546 }}}
    1547 
    1548 ... then they have a complete, ready to build source code tree with no additional configuration required.
    1549 
    1550 The advantages of defaulting to no external dependencies are obvious:
    1551 
    1552 1. Modularity: your users can download your library as a self contained distribution, and get immediately to work. [https://github.com/boostcon/cppnow_presentations_2015/raw/master/files/Large-Projects-and-CMake-and-git-oh-my.pdf As David Sankel would put it, this is being not anti-social in your coding].
    1553 2. Encapsulation: it forces you to think about your dependencies properly instead of just firing in some `boost::lib::foo` and dragging in a whole library just for a single routine. We did far too much of that historically in the Boost libraries, indeed even putting everything into the `boost` namespace rather than its own sub-namespace.
    1554 3. Build times: it lets you assemble all the header files in your library into a giant single include file which can then be distributed as a self standing single file drop in for your users, and which can be precompiled into a precompiled header such that your users experience greatly reduced build times.
    1555 4. Defaulting to using STL primitives such as `std::future` and `std::function` instead of alternatives greatly eases the lives of your users when they are trying to combine your library with another library in their application. One of the more tedious things in life is constantly having to invoke `std::async` to get `boost::future` and `std::future` to work together :(.
    1556 
    1557 TODO
    1558 
    1559 monolithic vs modular
    1560 git submodules vs biicode
    1561 
    1562 == 19. COUPLING/SOAPBOX: Essay about wisdom of dependency package managers in C++ 11/14 ==
     1578git submodules
     1579biicode
     1580etc