Dependencies

FAQ

  1. What is meant by a "dependency" and the phrase "dependency chain"?

    In the context of this FAQ, a dependency is any other library, Boost or Standard or third-party, that a Boost library requires. A primary dependency is a library the top-level library explicitly includes, a secondary dependency is a library that one of the primary, or other secondary dependency, includes.

    Boost libraries are modular, but they can depend on each other for various functionalities - for example, Boost.Asio relies on Boost.System for error codes.

    In general, taking dependencies can add a lot of value and reduce development time considerably. Boost libraries are carefully reviewed and tested to minimize dependency issues.

    As often with powerful concepts, there are pitfalls. Dependencies can lead to "dependency chains," where including one library pulls in others that may not be needed by your project.

  2. What issues do library developers have to address when managing dependencies?

    This includes handling several awkward situations: Version Conflicts - when different dependencies require incompatible versions of the same library, Transitive Dependencies - when a library pulls in additional, indirect dependencies that you may not even realize are part of your project, Bloat - when the sheer number of dependencies makes the build or runtime environment large, slow, or error-prone, and Security Risks - when outdated or unnecessary dependencies introduce vulnerabilities.

    In forum posts you might come across the following phrases, each describing a frustration with dependencies:

    • "Dependency creep" - the gradual accumulation of dependencies over time, often unnecessarily.

    • "Library fatigue" - the exhaustion or frustration of constantly managing and keeping track of too many libraries.

    • "Transitive dependency nightmare" - specifically refers to the frustration caused by indirect dependencies that you don’t directly control.

    • "Package spaghetti" or "Dependency spaghetti" - a messy tangle of interconnected dependencies.

    • "Build chain chaos" - can refer to the difficulties in managing the build process when dependencies are involved.

  3. What is meant by a "standalone" library?

    A standalone library is one where there are no dependencies (or, in reality, few), or the library depends only on the C++ Standard Library. Sometimes separate standalone versions of specific libraries are available, though they might be lightweight versions and not have parity of functionality with the non-standalone version.

  4. What can I do to minimize the number and impact of dependencies?

    A simple question but with a non-trivial answer. Consider working through this list of strategies and carefully applying when you can:

    1. Avoid including headers that aren’t directly needed. When building Boost with B2, you can exclude certain parts of Boost to minimize dependencies. For example, use the --with-[library] flag to build only the libraries you need. Say you only want Boost.System and Boost.Filesystem, then enter: ./b2 --with-system --with-filesystem. This will install only these two libraries, and their essential dependencies. Refer to Building with CMake if you are using CMake as your build tool.

    2. Read the library documentation to find macros that are available to remove unneeded functionality. For example, when using Boost.Asio, if support for timers or SSL are unneeded, then enter the statement: #define BOOST_ASIO_DISABLE_SSL. Refer to Customize Builds to Reduce Dependencies for many more examples.

    3. For powerful libraries like Boost.Asio, you can include only the headers you need, such as <boost/asio/io_context.hpp> rather than its parent <boost/asio.hpp>.

    4. Use forward declarations where possible instead of including full headers.

    5. Use a C++ Standard Library alternative if one exists, and has equivalent functionality and performance. For example, Boost.Variant could be replaced with std::variant.

    6. Use the Header-Only Mode (where possible). Many Boost libraries are header-only, meaning they don’t require linking against precompiled binaries or additional dependencies. Examples include Boost.Optional, Boost.Variant, and Boost.TypeTraits. For details of the binary requirements of Boost libraries refer to Required Compiled Binaries and Optional Compiled Binaries. For example, Boost.Asio has both header-only and compiled modes and you can configure it to work as header-only by defining the macro: #define BOOST_ASIO_SEPARATE_COMPILATION.

    7. For experienced developers only, consider commenting out unused code. This approach is possible but risky because it modifies library source code (Boost libraries are open-source), making updates and maintenance more challenging. It involves first identifying the parts of the library that introduce unnecessary dependencies and then commenting out the sections of source code or headers that you don’t need (such as unused features, optional functionality, error handling code). Finally, rebuild the library and check it compiles and links and runs without unwanted side-effects.

  5. Are there any tools specific to Boost that help manage dependencies?

    Yes, the Boost Copy Tool (bcp) is designed to help with dependency management. It allows you to extract a subset of the libraries and their dependencies into a separate directory, minimizing what gets pulled into your project. Install the tool and run bcp [library-name] [output-dir]. Review the output directory to ensure that only the necessary dependencies are included. For example, if you’re using Boost.Regex, enter bcp regex ./boost_subset and review the contents of your ./boost_subset directory.

    There is also the Boost Dependency Report, which goes into detail on the primary and secondary dependencies of all the libraries.

  6. Are there generally available tools that help with dependency issues?

    You can use static analysis tools, like Clang-Tidy or Cppcheck, to analyze your application and see which parts of any dependency are actually being used. Once identified, you can both remove unnecessary headers or dependencies, and perhaps rewrite portions of your code to avoid unnecessary functionality.