Production and Debug Builds

FAQ

  1. What is the value of using BOOST_ASSERT or BOOST_STATIC_ASSERT over the Standard Library assert macros?

    There are a few advantages of using the Boost asserts, available in <boost/assert.hpp>, including that BOOST_ASSERT is fully customizable using BOOST_ENABLE_ASSERT_HANDLER, which can be used to log extra data or stack traces, and there is better integration with Boost.Test. BOOST_STATIC_ASSERT is best utilized when using older C++ standards (pre-C++17), or you are using deeply templated code. You might also prefer the Boost macros if you are engaging the features of other Boost libraries and are looking for consistent tooling. For a fuller discussion, refer to Boost Macros.

  2. For maximum performance, is it good practice to remove, or comment out, the `BOOST_ASSERT`s for the final production code, or do they simply not get compiled into anything so there is no performance cost for leaving them as is?

    By default, BOOST_ASSERT macros are completely removed from the compiled binary when NDEBUG is defined, just like the standard assert macro. If NDEBUG is not defined a BOOST_ASSERT(x) will expand, usually to an assertion_failed() if the assert condition fails. If NDEBUG is defined it expands to ((void)0) so nothing is generated. Boost does provide the BOOST_DISABLE_ASSERTS macro, which has the same effect on Boost asserts as NDEBUG - but will leave other asserts alone.

  3. What is usually considered to be best practices in handling assertions that fire with a production build?

    Instead of throwing an exception when an assert fails, it is often the best practice to log the failure. For example, here is a custom assert handler using the features of Boost.Log to record the event:

    #include <boost/assert.hpp>
    #include <boost/log/trivial.hpp>
    #include <boost/log/utility/setup/file.hpp>
    #include <boost/log/utility/setup/console.hpp>
    #include <boost/log/utility/setup/common_attributes.hpp>
    #include <boost/log/expressions.hpp>
    #include <sstream>
    #include <cstdlib>
    
    namespace logging = boost::log;
    
    // Configure Boost.Log (call once at startup)
    void init_logging() {
        logging::add_common_attributes();
    
        // Console output
        logging::add_console_log(
            std::clog,
            logging::keywords::format = "[%TimeStamp%] [%Severity%] %Message%"
        );
    
        // File output
        logging::add_file_log(
            logging::keywords::file_name = "assert_failures_%N.log",
            logging::keywords::rotation_size = 10 * 1024 * 1024, // 10 MB
            logging::keywords::format = "[%TimeStamp%] [%Severity%] %Message%"
        );
    }
    
    // Custom handler for BOOST_ASSERT
    namespace boost {
        void assertion_failed(char const* expr, char const* function, char const* file, long line) {
            std::ostringstream oss;
            oss << "BOOST_ASSERT failed!\n"
                << "  Expression: " << expr << "\n"
                << "  Function:   " << function << "\n"
                << "  File:       " << file << "\n"
                << "  Line:       " << line;
    
            BOOST_LOG_TRIVIAL(error) << oss.str();
    
            std::abort(); // Optional: comment out if soft fail is desired
        }
    }

    An example use of this handler would be:

    #include <boost/assert.hpp>
    #include <iostream>
    
    // Declare logging initializer
    void init_logging();
    
    void test_logic(int value) {
        BOOST_ASSERT(value >= 0);
        std::cout << "Value is: " << value << std::endl;
    }
    
    int main() {
        init_logging();
    
        std::cout << "Testing BOOST_ASSERT with value = 42..." << std::endl;
        test_logic(42);
    
        std::cout << "Testing BOOST_ASSERT with value = -1..." << std::endl;
        test_logic(-1); // Logs to file and console, then aborts
    
        return 0;
    }
  4. What should I be aware of when moving from a Debug to a Production release?

    Use this checklist to ensure your application correctly integrates Boost libraries across Debug and Release configurations.

    • Linking and Compatibility

    • Link with the correct Boost library variant (-gd for Debug, none for Release).

    • Ensure runtime settings (Debug CRT or Release CRT) match Boost binaries.

    • Avoid mixing Debug-built Boost libraries with Release-built applications.

    • Macro Definitions and Configuration

    • Define BOOST_DEBUG in Debug builds to enable extra runtime checks (if applicable).

    • Define BOOST_DISABLE_ASSERTS in Release builds to remove BOOST_ASSERT checks.

    • Optionally define BOOST_ENABLE_ASSERT_HANDLER to install custom assertion handlers.

    • Review conditional macros like BOOST_NO_EXCEPTIONS, BOOST_NO_RTTI, etc.

    • Assertions and Diagnostics

    • Use BOOST_ASSERT for critical development-time checks.

    • Consider diagnostic logging using BOOST_LOG_TRIVIAL.

    • Ensure failing assertions are tested and logged in Debug builds.

    • Debugging and Tooling

    • Run AddressSanitizer, Valgrind, or Visual Leak Detector in Debug builds. Refer to Contributor Guide: Sanitizers.

    • Confirm Boost.Pool, Boost.Container, and alloc-heavy libraries don’t leak memory.

    • Validate Boost.Thread, Boost.Asio, and Boost.Fiber components using thread sanitizers.

    • Performance Awareness

    • Avoid benchmarking with Debug builds — optimization is disabled.

    • Use Release builds to test compile times for Boost.Mp11, Boost.Spirit, and any heavy use of templates.

    • Validate any BOOST_FORCEINLINE or BOOST_NOINLINE effects in both builds.

    • Unit Testing

    • Run the full suite of unit tests in both Debug and Release.

    • Ensure no logic is only covered by Debug-only paths or assertions.

    • Use Boost.Test to validate results across optimization levels.

  5. Typically, how should I set up a CMake file to handle Debug and Release builds?

    Here’s an example of how to set up your CMakeLists.txt to handle BOOST_ASSERT correctly by toggling behavior based on the build type (Debug or Release). The example includes linking with some sample libraries (Boost.Log, Boost.System and Boost.Thread):

    cmake_minimum_required(VERSION 3.10)
    project(MyBoostApp)
    
    # Set your C++ standard
    set(CMAKE_CXX_STANDARD 17)
    set(CMAKE_CXX_STANDARD_REQUIRED ON)
    
    # Enable debug symbols for Debug mode
    set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g")
    
    # Link Boost (adjust components as needed)
    find_package(Boost REQUIRED COMPONENTS log log_setup system thread)
    
    target_link_libraries(MyBoostApp PRIVATE
        Boost::log
        Boost::log_setup
        Boost::system
        Boost::thread
    )
    
    add_executable(MyBoostApp main.cpp)
    
    target_include_directories(MyBoostApp PRIVATE ${Boost_INCLUDE_DIRS})
    target_link_libraries(MyBoostApp PRIVATE ${Boost_LIBRARIES})
    
    # Enable BOOST_ASSERT in Debug, disable in Release
    target_compile_definitions(MyBoostApp PRIVATE
        $<$<CONFIG:Debug>:BOOST_ENABLE_ASSERT_HANDLER>
        $<$<CONFIG:Release>:NDEBUG>
    )
    
    # Optional: You can define a custom assert handler in debug builds
    # by linking a file like the assert handler shown above that defines `boost::assertion_failed`