Changes between Version 25 and Version 26 of BestPracticeHandbook


Ignore:
Timestamp:
May 19, 2015, 6:28:02 PM (7 years ago)
Author:
Niall Douglas
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • BestPracticeHandbook

    v25 v26  
    852852}}}
    853853
    854 This works very well when (a) your policy implementations fit nicely into template types and (b) the number of policy taking template types is reasonably low (otherwise you'll be doing a lot of typing as any changes to the policy design requires modifying every single instantiation of the policy taking template types). Another problem with policy based design is that it generates a lot of template instantiations, and generating a lot of template instantiations is bad because compilers often cannot do constant time type lookups and instead have linear or worse type lookups, so instantiating tens of million types is always going to be a lot slower to compile and sometimes link than millions of types.
     854This works very well when (a) your policy implementations fit nicely into template types and (b) the number of policy taking template types is reasonably low (otherwise you'll be doing a lot of typing as any changes to the policy design requires modifying every single instantiation of the policy taking template types). Another problem with policy based design is that it generates a lot of template instantiations, and generating a lot of template instantiations is bad because it is slow (type instantiation is typically linear to the number of types already instantiated and quadratic to the number of partial specialisations affecting a type, but some compilers are quadratic for both).
    855855
    856856Consider instead doing an ADL based namespace composure design pattern which is just a different way of doing policy based design. It can be highly effective in those niches where the traditional policy taking template approach falls down. Here is the same program above written using ADL namespace composure:
     
    926926}}}
    927927
    928 The first example instantiates five types to be thence considered during global type lookup, so let's say it has cost five to future code lookups. The second example instantiates no types at all at global lookup scope, so it has cost zero to future code lookups because all the types are declared inside namespaces normally not considered during global type lookups. The second example may also require less refactoring in the face of changes than the traditional form.
     928The first example instantiates five types, so let's say it adds a cost of five. The second example instantiates merely the three empty tag types in each namespace, and otherwise only free functions which don't participate in global overload resolution unless ADL brings them into consideration. This ought to add much less load to the compiler than the traditional design. This second example may also require less refactoring in the face of changes than the traditional form.
    929929
    930930The above pattern is in fact entirely C++ 03 code and uses no C++ 11. However, template aliasing in C++ 11 makes the above pattern much more flexible. Have a look at https://github.com/ptal/expected/blob/master/include/boost/functional/monads/rebindable.hpp for examples of this ADL invoked namespace composure design pattern.
     931
    931932
    932933== 15. BUILD: Consider defaulting to header only, but actively manage facilities for reducing build times ==
     
    9409413. Precompiled not header only (library implementation put into a shared library)
    9419424. Precompiled header only with link time optimisation
     943
    942944
    943945||= Build flags =||= Microsoft Windows 8.1 x64 with Visual Studio 2013 =||= Ubuntu 14.04 LTS Linux x64 with GCC 4.9 and gold linker =||= Ubuntu 14.04 LTS Linux x64 with clang 3.4 and gold linker =||
     
    10071009 }}}
    10081010
    1009  This looks a bit complicated, but isn't really. Generally you will mark up those classes and structs you implement in a .ipp file (this being the file implementing the APIs declared in the header) with `BOOST_AFIO_DECL`, functions with `BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC`, all out-of-class member functions (i.e. those not implemented inside the class or struct declaration) with `BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC`, all virtual member functions with `BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC` and append to all unimplemented virtual member functions `BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC`. This inserts the correct markup to generate both optimal header only and optimal non header only outcomes.
     1011 This looks a bit complicated, but isn't really. Generally you will mark up those classes and structs you implement in a .ipp file (this being the file implementing the APIs declared in the header which is included by the header if building header only, else is included by a .cpp file if not building header only) with `BOOST_AFIO_DECL`, functions with `BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC`, all out-of-class member functions (i.e. those not implemented inside the class or struct declaration) with `BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC`, all virtual member functions with `BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC` and append to all unimplemented virtual member functions `BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC`. This inserts the correct markup to generate both optimal header only and optimal non header only outcomes.
    10101012
    10111013'''2. Precompiled headers'''
     
    10591061 The idea behind this is to tell the compiler to not instantiate common instantiations of your template types on demand for every single compiland as you will explicitly instantiate them exactly once elsewhere. This can give quite a bump to build times for template heavy libraries.
    10601062
     1063'''4. Use C++ Modules'''
     1064
     1065 If you are on a very recent clang, or on a MSVC made sometime after 2016, there will probably be some implementation of C++ Modules available. C++ Modules as presently proposed for C++ 1z is poorly named in that after multiple rounds of simplification it provides little modularity at all, and is now really the beginnings of an AST database assisted build system which can dramatically improve build times in a similar way to precompiled headers. I therefore really wish that "C++ Modules" were called "C++ Build Acceleration" instead, but unfortunately no one proposing this feature to WG21 seems to agree. Anyway, for clang follow the instructions at http://clang.llvm.org/docs/Modules.html whereby you write up a module map file which delineates your header files into AST database objects, and your compiler will precompile your headers into an AST database. Unfortunately it presently seems you must manually annotate what parts are exposed and what parts are hidden in the module map rather than being able to reuse any dllimport/dllexport infrastructure already present. Your code when it includes a header will now have the compiler automatically go fetch the precompiled version from the database and use all exported types.
     1066
     1067 Microsoft unfortunately presently do not intend to implement Modules in the same way as clang, and requires you to do a lot more work including source code modification. Follow the instructions at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4465.pdf instead.
     1068
     1069 If you think of "C++ Modules" as really a somewhat more flexible and reusable implementation of existing precompiled headers which lets the compiler figure out what precompiled parts to use without you manually telling it as you must right now, you can estimate whether it is worth the effort to add support for Modules to your library as the speedup is likely similar to a precompiled header. The clang method is non-intrusive to your source code and doesn't get in the way of older compilers, but adds significant hassle in keeping the module map and the source code in sync. The Microsoft method seems to require a fair bit of #ifdef, but the module specification must be part of the source code which may aid keeping it in sync.
     1070
    10611071
    10621072== 16. COUPLING: Consider allowing your library users to dependency inject your dependencies on other libraries ==
     1073
     1074
    10631075
    10641076== 17. FUTURE PROOFING: Consider being C++ resumable function ready ==