diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000..dc8b8cc5ab --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,756 @@ +# Copyright 2021-2025 Andrey Semashev +# +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt) + +name: CI + +on: + pull_request: + push: + branches: + - master + - develop + - feature/** + +concurrency: + group: ${{format('{0}:{1}', github.repository, github.ref)}} + cancel-in-progress: true + +env: + GIT_FETCH_JOBS: 8 + NET_RETRY_COUNT: 5 + DEFAULT_BUILD_VARIANT: debug,release + BOOST_TEST_CATCH_SYSTEM_ERRORS: 0 + BOOST_TEST_AUTO_START_DBG: 0 + +jobs: + posix: + defaults: + run: + shell: bash + + strategy: + fail-fast: false + matrix: + include: + # Linux, gcc + - toolset: gcc-5 + cxxstd: "11,14,1z" + os: ubuntu-latest + container: ubuntu:16.04 + install: + - g++-5 + extra_tests: 1 + - toolset: gcc-6 + cxxstd: "11,14,1z" + os: ubuntu-latest + container: ubuntu:18.04 + install: + - g++-6 + - toolset: gcc-7 + cxxstd: "11,14,17" + os: ubuntu-latest + container: ubuntu:18.04 + install: + - g++-7 + - toolset: gcc-8 + cxxstd: "11,14,17,2a" + os: ubuntu-latest + container: ubuntu:18.04 + install: + - g++-8 + - toolset: gcc-9 + cxxstd: "11,14,17,2a" + os: ubuntu-latest + container: ubuntu:20.04 + install: + - g++-9 + - toolset: gcc-10 + cxxstd: "11,14,17,20" + os: ubuntu-latest + container: ubuntu:20.04 + install: + - g++-10 + - toolset: gcc-11 + cxxstd: "11,14,17,20,23" + os: ubuntu-22.04 + install: + - g++-11 + - toolset: gcc-12 + cxxstd: "11,14,17,20,23" + os: ubuntu-22.04 + install: + - g++-12 + - toolset: gcc-13 + cxxstd: "11,14,17,20,23" + os: ubuntu-24.04 + install: + - g++-13 + - toolset: gcc-14 + cxxstd: "11,14,17,20,23,26" + os: ubuntu-24.04 + install: + - g++-14 + - toolset: gcc-15 + cxxstd: "11,14,17,20,23,26" + os: ubuntu-latest + container: ubuntu:25.04 + install: + - g++-15 + - name: UBSAN + toolset: gcc-13 + cxxstd: "11,14,17,20,23" + ubsan: 1 + build_variant: debug + os: ubuntu-24.04 + install: + - g++-13 + + # Linux, clang + - toolset: clang + compiler: clang++-3.7 + cxxstd: "11,14" + os: ubuntu-latest + container: ubuntu:16.04 + install: + - clang-3.7 + - toolset: clang + compiler: clang++-3.8 + cxxstd: "11,14" + os: ubuntu-latest + container: ubuntu:16.04 + install: + - clang-3.8 + - toolset: clang + compiler: clang++-3.9 + cxxstd: "11,14" + os: ubuntu-latest + container: ubuntu:18.04 + install: + - clang-3.9 + - toolset: clang + compiler: clang++-4.0 + cxxstd: "11,14" + os: ubuntu-latest + container: ubuntu:18.04 + install: + - clang-4.0 + - toolset: clang + compiler: clang++-5.0 + cxxstd: "11,14" + os: ubuntu-latest + container: ubuntu:18.04 + install: + - clang-5.0 + - toolset: clang + compiler: clang++-6.0 + cxxstd: "11,14,17" + os: ubuntu-latest + container: ubuntu:18.04 + install: + - clang-6.0 + - toolset: clang + compiler: clang++-7 + cxxstd: "11,14,17" + os: ubuntu-latest + container: ubuntu:18.04 + install: + - clang-7 + # Note: clang-8 does not fully support C++20, so it is not compatible with libstdc++-8 in this mode + - toolset: clang + compiler: clang++-8 + cxxstd: "11,14,17,2a" + os: ubuntu-latest + container: ubuntu:18.04 + install: + - clang-8 + - g++-7 + gcc_toolchain: 7 + - toolset: clang + compiler: clang++-9 + cxxstd: "11,14,17,2a" + os: ubuntu-latest + container: ubuntu:20.04 + install: + - clang-9 + - toolset: clang + compiler: clang++-10 + cxxstd: "11,14,17,20" + os: ubuntu-latest + container: ubuntu:20.04 + install: + - clang-10 + - toolset: clang + compiler: clang++-11 + cxxstd: "11,14,17,20" + os: ubuntu-22.04 + install: + - clang-11 + - g++-11 + gcc_toolchain: 11 + - toolset: clang + compiler: clang++-12 + cxxstd: "11,14,17,20,2b" + os: ubuntu-22.04 + install: + - clang-12 + - g++-11 + gcc_toolchain: 11 + - toolset: clang + compiler: clang++-13 + cxxstd: "11,14,17,20,2b" + os: ubuntu-22.04 + install: + - clang-13 + - g++-11 + gcc_toolchain: 11 + - toolset: clang + compiler: clang++-14 + cxxstd: "11,14,17,20,2b" + os: ubuntu-22.04 + install: + - clang-14 + - g++-11 + gcc_toolchain: 11 + - toolset: clang + compiler: clang++-15 + cxxstd: "11,14,17,20,2b" + os: ubuntu-22.04 + install: + - clang-15 + - g++-11 + gcc_toolchain: 11 + - toolset: clang + compiler: clang++-16 + cxxstd: "11,14,17,20,2b" + os: ubuntu-24.04 + install: + - clang-16 + - g++-11 + gcc_toolchain: 11 + - toolset: clang + compiler: clang++-17 + cxxstd: "11,14,17,20,23,26" + os: ubuntu-24.04 + install: + - clang-17 + - g++-11 + gcc_toolchain: 11 + - toolset: clang + compiler: clang++-18 + cxxstd: "11,14,17,20,23,26" + os: ubuntu-24.04 + install: + - clang-18 + - g++-13 + - toolset: clang + compiler: clang++-19 + cxxstd: "11,14,17,20,23,26" + os: ubuntu-24.04 + install: + - clang-19 + - toolset: clang + compiler: clang++-20 + cxxstd: "11,14,17,20,23,26" + os: ubuntu-latest + container: ubuntu:25.04 + install: + - clang-20 + - toolset: clang + compiler: clang++-20 + cxxstd: "11,14,17,20,23,26" + os: ubuntu-latest + container: ubuntu:25.04 + install: + - clang-20 + - libc++-20-dev + - libc++abi-20-dev + cxxflags: -stdlib=libc++ + linkflags: -stdlib=libc++ + - name: UBSAN + toolset: clang + compiler: clang++-18 + cxxstd: "11,14,17,20,23,26" + cxxflags: -stdlib=libc++ + linkflags: "-stdlib=libc++ -lubsan" + ubsan: 1 + build_variant: debug + os: ubuntu-24.04 + install: + - clang-18 + - libc++-18-dev + - libc++abi-18-dev + + - toolset: clang + cxxstd: "11,14,17,20,2b" + os: macos-14 + - toolset: clang + cxxstd: "11,14,17,20,23" + os: macos-15 + + - name: CMake tests + cmake_tests: 1 + os: ubuntu-24.04 + + timeout-minutes: 60 + runs-on: ${{matrix.os}} + container: ${{matrix.container}} + + steps: + - name: Setup environment + run: | + if [ -f "/etc/debian_version" ] + then + echo "DEBIAN_FRONTEND=noninteractive" >> $GITHUB_ENV + export DEBIAN_FRONTEND=noninteractive + fi + if [ -n "${{matrix.container}}" ] + then + echo "GHA_CONTAINER=${{matrix.container}}" >> $GITHUB_ENV + if [ -f "/etc/debian_version" ] + then + # Use Azure APT mirrors in containers to avoid HTTP errors due to DDoS filters triggered by lots of CI jobs launching concurrently. + # Note that not all Ubuntu versions support "mirror+file:..." URIs in APT sources, so just use Azure mirrors exclusively. + # Note also that on recent Ubuntu versions DEB822 format is used for source files. + APT_SOURCES=() + if [ -d "/etc/apt/sources.list.d" ] + then + readarray -t APT_SOURCES < <(find "/etc/apt/sources.list.d" -type f -name '*.sources' -print) + fi + if [ -f "/etc/apt/sources.list" ] + then + APT_SOURCES+=("/etc/apt/sources.list") + fi + if [ "${#APT_SOURCES[@]}" -gt 0 ] + then + sed -i -E -e 's!([^ ]+) (http|https)://(archive|security)\.ubuntu\.com/ubuntu[^ ]*(.*)!\1 http://azure.archive.ubuntu.com/ubuntu/\4!' "${APT_SOURCES[@]}" + fi + apt-get -o Acquire::Retries=$NET_RETRY_COUNT update + if [ "$(apt-cache search "^python-is-python3$" | wc -l)" -ne 0 ] + then + PYTHON_PACKAGE="python-is-python3" + else + PYTHON_PACKAGE="python" + fi + apt-get -o Acquire::Retries=$NET_RETRY_COUNT install -y sudo software-properties-common tzdata wget curl apt-transport-https ca-certificates make build-essential g++ $PYTHON_PACKAGE python3 perl git cmake + fi + fi + git config --global pack.threads 0 + + - name: Install packages + if: matrix.install + run: | + declare -a SOURCE_KEYS SOURCES + if [ -n "${{join(matrix.source_keys, ' ')}}" ] + then + SOURCE_KEYS=("${{join(matrix.source_keys, '" "')}}") + fi + if [ -n "${{join(matrix.sources, ' ')}}" ] + then + SOURCES=("${{join(matrix.sources, '" "')}}") + fi + for key in "${SOURCE_KEYS[@]}" + do + for i in {1..$NET_RETRY_COUNT} + do + echo "Adding key: $key" + wget -O - "$key" | sudo apt-key add - && break || sleep 2 + done + done + if [ ${#SOURCES[@]} -gt 0 ] + then + APT_ADD_REPO_COMMON_ARGS=("-y") + APT_ADD_REPO_SUPPORTED_ARGS="$(apt-add-repository --help | perl -ne 'if (/^\s*-n/) { print "n"; } elsif (/^\s*-P/) { print "P"; } elsif (/^\s*-S/) { print "S"; } elsif (/^\s*-U/) { print "U"; }')" + if [ -n "$APT_ADD_REPO_SUPPORTED_ARGS" -a -z "${APT_ADD_REPO_SUPPORTED_ARGS##*n*}" ] + then + APT_ADD_REPO_COMMON_ARGS+=("-n") + fi + APT_ADD_REPO_HAS_SOURCE_ARGS="$([ -n "$APT_ADD_REPO_SUPPORTED_ARGS" -a -z "${APT_ADD_REPO_SUPPORTED_ARGS##*P*}" -a -z "${APT_ADD_REPO_SUPPORTED_ARGS##*S*}" -a -z "${APT_ADD_REPO_SUPPORTED_ARGS##*U*}" ] && echo 1 || echo 0)" + for source in "${SOURCES[@]}" + do + for i in {1..$NET_RETRY_COUNT} + do + APT_ADD_REPO_ARGS=("${APT_ADD_REPO_COMMON_ARGS[@]}") + if [ $APT_ADD_REPO_HAS_SOURCE_ARGS -ne 0 ] + then + case "$source" in + "ppa:"*) + APT_ADD_REPO_ARGS+=("-P") + ;; + "deb "*) + APT_ADD_REPO_ARGS+=("-S") + ;; + *) + APT_ADD_REPO_ARGS+=("-U") + ;; + esac + fi + APT_ADD_REPO_ARGS+=("$source") + echo "apt-add-repository ${APT_ADD_REPO_ARGS[@]}" + sudo -E apt-add-repository "${APT_ADD_REPO_ARGS[@]}" && break || sleep 2 + done + done + fi + sudo apt-get -o Acquire::Retries=$NET_RETRY_COUNT update + sudo apt-get -o Acquire::Retries=$NET_RETRY_COUNT install -y ${{join(matrix.install, ' ')}} + + - name: Setup GCC Toolchain + if: matrix.gcc_toolchain + run: | + GCC_TOOLCHAIN_ROOT="$HOME/gcc-toolchain" + echo "GCC_TOOLCHAIN_ROOT=\"$GCC_TOOLCHAIN_ROOT\"" >> $GITHUB_ENV + MULTIARCH_TRIPLET="$(dpkg-architecture -qDEB_HOST_MULTIARCH)" + mkdir -p "$GCC_TOOLCHAIN_ROOT" + ln -s /usr/include "$GCC_TOOLCHAIN_ROOT/include" + ln -s /usr/bin "$GCC_TOOLCHAIN_ROOT/bin" + mkdir -p "$GCC_TOOLCHAIN_ROOT/lib/gcc/$MULTIARCH_TRIPLET" + ln -s "/usr/lib/gcc/$MULTIARCH_TRIPLET/${{matrix.gcc_toolchain}}" "$GCC_TOOLCHAIN_ROOT/lib/gcc/$MULTIARCH_TRIPLET/${{matrix.gcc_toolchain}}" + + - name: Setup Boost + run: | + echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY + LIBRARY=${GITHUB_REPOSITORY#*/} + echo LIBRARY: $LIBRARY + echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV + echo GITHUB_BASE_REF: $GITHUB_BASE_REF + echo GITHUB_REF: $GITHUB_REF + REF=${GITHUB_BASE_REF:-$GITHUB_REF} + REF=${REF#refs/heads/} + echo REF: $REF + BOOST_BRANCH=develop && [ "$REF" = "master" ] && BOOST_BRANCH=master || true + echo BOOST_BRANCH: $BOOST_BRANCH + BUILD_JOBS=$((nproc || sysctl -n hw.ncpu) 2> /dev/null) + echo "BUILD_JOBS=$BUILD_JOBS" >> $GITHUB_ENV + echo "CMAKE_BUILD_PARALLEL_LEVEL=$BUILD_JOBS" >> $GITHUB_ENV + DEPINST_ARGS=() + GIT_VERSION="$(git --version | sed -e 's/git version //')" + GIT_HAS_JOBS=1 + if [ -f "/etc/debian_version" ] + then + if $(dpkg --compare-versions "$GIT_VERSION" lt 2.8.0) + then + GIT_HAS_JOBS=0 + fi + else + declare -a GIT_VER=(${GIT_VERSION//./ }) + declare -a GIT_MIN_VER=(2 8 0) + for ((i=0; i<${#GIT_VER[@]}; i++)) + do + if [ -z "${GIT_MIN_VER[i]}" ] + then + GIT_MIN_VER[i]=0 + fi + if [ "${GIT_VER[i]}" -lt "${GIT_MIN_VER[i]}" ] + then + GIT_HAS_JOBS=0 + break + fi + done + fi + if [ "$GIT_HAS_JOBS" -ne 0 ] + then + DEPINST_ARGS+=("--git_args" "--jobs $GIT_FETCH_JOBS") + fi + mkdir -p snapshot + cd snapshot + echo "Downloading library snapshot: https://github.com/${GITHUB_REPOSITORY}/archive/${GITHUB_SHA}.tar.gz" + curl -L --retry "$NET_RETRY_COUNT" -o "${LIBRARY}-${GITHUB_SHA}.tar.gz" "https://github.com/${GITHUB_REPOSITORY}/archive/${GITHUB_SHA}.tar.gz" + tar -xf "${LIBRARY}-${GITHUB_SHA}.tar.gz" + if [ ! -d "${LIBRARY}-${GITHUB_SHA}" ] + then + echo "Library snapshot does not contain the library directory ${LIBRARY}-${GITHUB_SHA}:" + ls -la + exit 1 + fi + rm -f "${LIBRARY}-${GITHUB_SHA}.tar.gz" + cd .. + git clone -b "$BOOST_BRANCH" --depth 1 "https://github.com/boostorg/boost.git" "boost-root" + cd boost-root + mkdir -p libs + rm -rf "libs/$LIBRARY" + mv -f "../snapshot/${LIBRARY}-${GITHUB_SHA}" "libs/$LIBRARY" + rm -rf "../snapshot" + git submodule update --init tools/boostdep + if [ -n "${{matrix.extra_tests}}" ] + then + DEPINST_ARGS+=("--include" "example") + fi + DEPINST_ARGS+=("$LIBRARY") + python tools/boostdep/depinst/depinst.py "${DEPINST_ARGS[@]}" + if [ -z "${{matrix.cmake_tests}}" ] + then + ./bootstrap.sh + ./b2 headers + if [ -n "${{matrix.compiler}}" -o -n "$GCC_TOOLCHAIN_ROOT" ] + then + echo -n "using ${{matrix.toolset}} : : ${{matrix.compiler}}" > ~/user-config.jam + if [ -n "$GCC_TOOLCHAIN_ROOT" ] + then + echo -n " : \"--gcc-toolchain=$GCC_TOOLCHAIN_ROOT\" \"--gcc-toolchain=$GCC_TOOLCHAIN_ROOT\"" >> ~/user-config.jam + fi + echo " ;" >> ~/user-config.jam + fi + fi + + - name: Run tests + if: matrix.cmake_tests == '' + run: | + cd boost-root + if [ -z "${{matrix.extra_tests}}" ] + then + export BOOST_LOG_TEST_WITHOUT_SELF_CONTAINED_HEADER_TESTS=1 + export BOOST_LOG_TEST_WITHOUT_EXAMPLES=1 + fi + B2_ARGS=("-j" "$BUILD_JOBS" "toolset=${{matrix.toolset}}" "cxxstd=${{matrix.cxxstd}}") + if [ -n "${{matrix.build_variant}}" ] + then + B2_ARGS+=("variant=${{matrix.build_variant}}") + else + B2_ARGS+=("variant=$DEFAULT_BUILD_VARIANT") + fi + if [ -n "${{matrix.threading}}" ] + then + B2_ARGS+=("threading=${{matrix.threading}}") + fi + if [ -n "${{matrix.ubsan}}" ] + then + export UBSAN_OPTIONS="print_stacktrace=1" + B2_ARGS+=("cxxflags=-fsanitize=undefined -fno-sanitize-recover=undefined" "linkflags=-fsanitize=undefined -fuse-ld=gold" "define=UBSAN=1" "debug-symbols=on" "visibility=global") + fi + if [ -n "${{matrix.cxxflags}}" ] + then + B2_ARGS+=("cxxflags=${{matrix.cxxflags}}") + fi + if [ -n "${{matrix.linkflags}}" ] + then + B2_ARGS+=("linkflags=${{matrix.linkflags}}") + fi + B2_ARGS+=("libs/$LIBRARY/test") + ./b2 "${B2_ARGS[@]}" + + - name: Run CMake tests + if: matrix.cmake_tests + run: | + cd boost-root + mkdir __build_static__ && cd __build_static__ + cmake ../libs/$LIBRARY/test/test_cmake + cmake --build . --target boost_${LIBRARY}_cmake_self_test -j $BUILD_JOBS + cmake --build . --target boost_${LIBRARY}_setup_cmake_self_test -j $BUILD_JOBS + cd .. + mkdir __build_shared__ && cd __build_shared__ + cmake -DBUILD_SHARED_LIBS=On ../libs/$LIBRARY/test/test_cmake + cmake --build . --target boost_${LIBRARY}_cmake_self_test -j $BUILD_JOBS + cmake --build . --target boost_${LIBRARY}_setup_cmake_self_test -j $BUILD_JOBS + + windows: + defaults: + run: + shell: cmd + + strategy: + fail-fast: false + matrix: + include: + - toolset: msvc-14.3 + cxxstd: "14" + os: windows-2022 + extra_tests: 1 + - toolset: msvc-14.3 + cxxstd: "17,20,latest" + os: windows-2022 + - toolset: clang-win + cxxstd: "14,17,latest" + os: windows-2022 + - toolset: gcc + cxxstd: "11-gnu" + os: windows-2022 + extra_tests: 1 + - toolset: gcc + cxxstd: "14-gnu,17-gnu,20-gnu,23-gnu" + os: windows-2022 + + - name: CMake MSVC tests + cmake_tests: 1 + os: windows-2022 + - name: CMake MinGW-w64 tests + cmake_tests: 1 + os: windows-2022 + cmake_generator: "MinGW Makefiles" + + timeout-minutes: 60 + runs-on: ${{matrix.os}} + + steps: + - name: Setup Boost + run: | + echo GITHUB_REPOSITORY: %GITHUB_REPOSITORY% + for /f %%i in ("%GITHUB_REPOSITORY%") do set LIBRARY=%%~nxi + echo LIBRARY: %LIBRARY% + echo LIBRARY=%LIBRARY%>>%GITHUB_ENV% + echo GITHUB_BASE_REF: %GITHUB_BASE_REF% + echo GITHUB_REF: %GITHUB_REF% + if "%GITHUB_BASE_REF%" == "" set GITHUB_BASE_REF=%GITHUB_REF% + set BOOST_BRANCH=develop + for /f %%i in ("%GITHUB_BASE_REF%") do if "%%~nxi" == "master" set BOOST_BRANCH=master + echo BOOST_BRANCH: %BOOST_BRANCH% + mkdir snapshot + cd snapshot + echo Downloading library snapshot: https://github.com/%GITHUB_REPOSITORY%/archive/%GITHUB_SHA%.zip + curl -L --retry %NET_RETRY_COUNT% -o "%LIBRARY%-%GITHUB_SHA%.zip" "https://github.com/%GITHUB_REPOSITORY%/archive/%GITHUB_SHA%.zip" + tar -xf "%LIBRARY%-%GITHUB_SHA%.zip" + if not exist "%LIBRARY%-%GITHUB_SHA%\" ( + echo Library snapshot does not contain the library directory %LIBRARY%-%GITHUB_SHA%: + dir + exit /b 1 + ) + del /f "%LIBRARY%-%GITHUB_SHA%.zip" + cd .. + git clone -b %BOOST_BRANCH% --depth 1 https://github.com/boostorg/boost.git boost-root + cd boost-root + if not exist "libs\" mkdir libs + if exist "libs\%LIBRARY%\" rmdir /s /q "libs\%LIBRARY%" + move /Y "..\snapshot\%LIBRARY%-%GITHUB_SHA%" "libs\%LIBRARY%" + rmdir /s /q "..\snapshot" + git submodule update --init tools/boostdep + if not "${{matrix.extra_tests}}" == "" set DEPINST_ARG_INCLUDE_EXAMPLES="--include=example" + python tools/boostdep/depinst/depinst.py %DEPINST_ARG_INCLUDE_EXAMPLES% --git_args "--jobs %GIT_FETCH_JOBS%" %LIBRARY% + if "${{matrix.cmake_tests}}" == "" ( + cmd /c bootstrap + b2 -d0 headers + ) + + - name: Run tests + if: matrix.cmake_tests == '' + run: | + cd boost-root + set "B2_ARGS=-j %NUMBER_OF_PROCESSORS% toolset=${{matrix.toolset}} embed-manifest-via=linker" + if not "${{matrix.cxxstd}}" == "" set "B2_ARGS=%B2_ARGS% ^"cxxstd=${{matrix.cxxstd}}^"" + if not "${{matrix.build_variant}}" == "" ( set "B2_ARGS=%B2_ARGS% variant=${{matrix.build_variant}}" ) else ( set "B2_ARGS=%B2_ARGS% variant=%DEFAULT_BUILD_VARIANT%" ) + if not "${{matrix.cxxflags}}" == "" set "B2_ARGS=%B2_ARGS% ^"cxxflags=${{matrix.cxxflags}}^"" + if not "${{matrix.linkflags}}" == "" set "B2_ARGS=%B2_ARGS% ^"linkflags=${{matrix.linkflags}}^"" + if "${{matrix.extra_tests}}" == "" ( + set BOOST_LOG_TEST_WITHOUT_SELF_CONTAINED_HEADER_TESTS=1 + set BOOST_LOG_TEST_WITHOUT_EXAMPLES=1 + ) + b2 %B2_ARGS% libs/%LIBRARY%/test + + - name: Run CMake tests + if: matrix.cmake_tests + run: | + if not "${{matrix.cmake_generator}}" == "" set "CMAKE_GENERATOR=${{matrix.cmake_generator}}" + cd boost-root + mkdir __build_static__ + cd __build_static__ + cmake ../libs/%LIBRARY%/test/test_cmake + cmake --build . --target boost_%LIBRARY%_cmake_self_test -j %NUMBER_OF_PROCESSORS% + cmake --build . --target boost_%LIBRARY%_setup_cmake_self_test -j %NUMBER_OF_PROCESSORS% + cd .. + mkdir __build_shared__ + cd __build_shared__ + cmake -DBUILD_SHARED_LIBS=On ../libs/%LIBRARY%/test/test_cmake + cmake --build . --target boost_%LIBRARY%_cmake_self_test -j %NUMBER_OF_PROCESSORS% + cmake --build . --target boost_%LIBRARY%_setup_cmake_self_test -j %NUMBER_OF_PROCESSORS% + + cygwin: + defaults: + run: + shell: bash + + env: + SHELLOPTS: igncr + + strategy: + fail-fast: false + matrix: + include: + - toolset: gcc + cxxstd: "14-gnu,17-gnu,20-gnu,23-gnu" + build_variant: debug + os: windows-2022 + + timeout-minutes: 60 + runs-on: ${{matrix.os}} + + steps: + - name: Install Cygwin + uses: cygwin/cygwin-install-action@v4 + with: + packages: + gcc-g++ + + - name: Setup Boost + run: | + echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY + LIBRARY=${GITHUB_REPOSITORY#*/} + echo LIBRARY: $LIBRARY + echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV + echo GITHUB_BASE_REF: $GITHUB_BASE_REF + echo GITHUB_REF: $GITHUB_REF + REF=${GITHUB_BASE_REF:-$GITHUB_REF} + REF=${REF#refs/heads/} + echo REF: $REF + BOOST_BRANCH=develop && [ "$REF" = "master" ] && BOOST_BRANCH=master || true + echo BOOST_BRANCH: $BOOST_BRANCH + BUILD_JOBS=$((nproc) 2> /dev/null) + echo "BUILD_JOBS=$BUILD_JOBS" >> $GITHUB_ENV + echo "CMAKE_BUILD_PARALLEL_LEVEL=$BUILD_JOBS" >> $GITHUB_ENV + DEPINST_ARGS=("--git_args" "--jobs $GIT_FETCH_JOBS") + mkdir -p snapshot + cd snapshot + echo "Downloading library snapshot: https://github.com/${GITHUB_REPOSITORY}/archive/${GITHUB_SHA}.tar.gz" + curl -L --retry "$NET_RETRY_COUNT" -o "${LIBRARY}-${GITHUB_SHA}.tar.gz" "https://github.com/${GITHUB_REPOSITORY}/archive/${GITHUB_SHA}.tar.gz" + tar -xf "${LIBRARY}-${GITHUB_SHA}.tar.gz" + if [ ! -d "${LIBRARY}-${GITHUB_SHA}" ] + then + echo "Library snapshot does not contain the library directory ${LIBRARY}-${GITHUB_SHA}:" + ls -la + exit 1 + fi + rm -f "${LIBRARY}-${GITHUB_SHA}.tar.gz" + cd .. + git clone -b "$BOOST_BRANCH" --depth 1 "https://github.com/boostorg/boost.git" "boost-root" + cd boost-root + mkdir -p libs + rm -rf "libs/$LIBRARY" + mv -f "../snapshot/${LIBRARY}-${GITHUB_SHA}" "libs/$LIBRARY" + rm -rf "../snapshot" + git submodule update --init tools/boostdep + if [ -n "${{matrix.extra_tests}}" ] + then + DEPINST_ARGS+=("--include" "example") + fi + DEPINST_ARGS+=("$LIBRARY") + python tools/boostdep/depinst/depinst.py "${DEPINST_ARGS[@]}" + ./bootstrap.sh + ./b2 -d0 headers + + - name: Run tests + run: | + cd boost-root + if [ -z "${{matrix.extra_tests}}" ] + then + export BOOST_LOG_TEST_WITHOUT_SELF_CONTAINED_HEADER_TESTS=1 + export BOOST_LOG_TEST_WITHOUT_EXAMPLES=1 + fi + B2_ARGS=("-j" "$BUILD_JOBS" "toolset=${{matrix.toolset}}" "cxxstd=${{matrix.cxxstd}}") + if [ -n "${{matrix.build_variant}}" ] + then + B2_ARGS+=("variant=${{matrix.build_variant}}") + else + B2_ARGS+=("variant=$DEFAULT_BUILD_VARIANT") + fi + if [ -n "${{matrix.threading}}" ] + then + B2_ARGS+=("threading=${{matrix.threading}}") + fi + if [ -n "${{matrix.cxxflags}}" ] + then + B2_ARGS+=("cxxflags=${{matrix.cxxflags}}") + fi + if [ -n "${{matrix.linkflags}}" ] + then + B2_ARGS+=("linkflags=${{matrix.linkflags}}") + fi + B2_ARGS+=("libs/$LIBRARY/test") + ./b2 "${B2_ARGS[@]}" diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000..66735fe06d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,688 @@ +# Copyright 2021-2025 Andrey Semashev +# +# Distributed under the Boost Software License, Version 1.0. +# See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt + +cmake_minimum_required(VERSION 3.8...3.16) +project(BoostLog VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX) + +include(CheckCXXSourceCompiles) + +set(BOOST_LOG_NO_THREADS OFF CACHE BOOL "Disable multithreading support in Boost.Log") +set(BOOST_LOG_WITHOUT_CHAR OFF CACHE BOOL "Disable support for narrow character logging in Boost.Log") +set(BOOST_LOG_WITHOUT_WCHAR_T OFF CACHE BOOL "Disable support for wide character logging in Boost.Log") +set(BOOST_LOG_WITHOUT_DEFAULT_FACTORIES OFF CACHE BOOL "Disable default factories for filters and formatters in Boost.Log") +set(BOOST_LOG_WITHOUT_SETTINGS_PARSERS OFF CACHE BOOL "Disable settings, filter and formatter parsers in Boost.Log") +if (WIN32) + set(BOOST_LOG_NO_QUERY_PERFORMANCE_COUNTER OFF CACHE BOOL "Disable using QueryPerformanceCounter API on Windows in Boost.Log") + set(BOOST_LOG_WITHOUT_DEBUG_OUTPUT OFF CACHE BOOL "Disable support for debugger output on Windows in Boost.Log") + set(BOOST_LOG_WITHOUT_EVENT_LOG OFF CACHE BOOL "Disable support for event log on Windows in Boost.Log") +endif() +set(BOOST_LOG_WITHOUT_IPC OFF CACHE BOOL "Disable support for inter-process communication in Boost.Log") +set(BOOST_LOG_WITHOUT_ASIO OFF CACHE BOOL "Disable support for Boost.ASIO in Boost.Log") +set(BOOST_LOG_WITHOUT_SYSLOG OFF CACHE BOOL "Disable support for syslog API in Boost.Log") +set(BOOST_LOG_USE_NATIVE_SYSLOG OFF CACHE BOOL "Force-enable using native syslog API in Boost.Log") +set(BOOST_LOG_USE_COMPILER_TLS OFF CACHE BOOL "Enable using compiler-specific intrinsics for thread-local storage in Boost.Log") + +set(BOOST_LOG_REGEX_BACKENDS "std::regex" "Boost.Regex" "Boost.Xpressive") +set(BOOST_LOG_USE_REGEX_BACKEND "Boost.Regex" CACHE STRING "Regular expressions backend to use in Boost.Log") +set_property(CACHE BOOST_LOG_USE_REGEX_BACKEND PROPERTY STRINGS ${BOOST_LOG_REGEX_BACKENDS}) +if (NOT BOOST_LOG_USE_REGEX_BACKEND IN_LIST BOOST_LOG_REGEX_BACKENDS) + message(FATAL_ERROR "BOOST_LOG_USE_REGEX_BACKEND must be one of: ${BOOST_LOG_REGEX_BACKENDS}") +endif() + +if (NOT BOOST_LOG_NO_THREADS) + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package(Threads REQUIRED) +endif() + +# Note: We can't use the Boost::library targets in the configure checks as they may not yet be included +# by the superproject when this CMakeLists.txt is included. We also don't want to hardcode include paths +# of the needed libraries and their dependencies, recursively, as this is too fragile and requires maintenance. +# Instead, we collect include paths of all libraries and use them in the configure checks. This works faster +# if there is a unified Boost include tree in the filesystem (i.e. if `b2 headers` was run or we're in the +# official monolithic Boost distribution tree). +include(cmake/BoostLibraryIncludes.cmake) + +set(CMAKE_REQUIRED_INCLUDES ${BOOST_LIBRARY_INCLUDES}) + +check_cxx_source_compiles("#include <${CMAKE_CURRENT_SOURCE_DIR}/../config/checks/architecture/x86.cpp>\nint main() {}" BOOST_LOG_TARGET_X86) + +set(CMAKE_REQUIRED_DEFINITIONS "-DBOOST_ALL_NO_LIB") +check_cxx_source_compiles("#include <${CMAKE_CURRENT_SOURCE_DIR}/config/atomic-int32/atomic_int32.cpp>" BOOST_LOG_HAS_LOCK_FREE_ATOMIC_INT32) +unset(CMAKE_REQUIRED_DEFINITIONS) + +if (BOOST_LOG_TARGET_X86) + if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + if (CMAKE_SIZEOF_VOID_P EQUAL 4) + set(boost_log_ssse3_cflags "/arch:SSE2") + set(boost_log_avx2_cflags "/arch:AVX") + endif() + elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Intel") + if (WIN32) + set(boost_log_ssse3_cflags "/QxSSSE3") + set(boost_log_avx2_cflags "/arch:CORE-AVX2") + else() + set(boost_log_ssse3_cflags "-xSSSE3") + set(boost_log_avx2_cflags "-xCORE-AVX2 -fabi-version=0") + endif() + else() + set(boost_log_ssse3_cflags "-msse -msse2 -msse3 -mssse3") + set(boost_log_avx2_cflags "-mavx -mavx2") + if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + string(APPEND boost_log_avx2_cflags " -fabi-version=0") + endif() + endif() + + set(CMAKE_REQUIRED_FLAGS "${boost_log_ssse3_cflags}") + check_cxx_source_compiles("#include <${CMAKE_CURRENT_SOURCE_DIR}/config/x86-ext/ssse3.cpp>" BOOST_LOG_COMPILER_HAS_SSSE3) + unset(CMAKE_REQUIRED_FLAGS) + set(CMAKE_REQUIRED_FLAGS "${boost_log_avx2_cflags}") + check_cxx_source_compiles("#include <${CMAKE_CURRENT_SOURCE_DIR}/config/x86-ext/avx2.cpp>" BOOST_LOG_COMPILER_HAS_AVX2) + unset(CMAKE_REQUIRED_FLAGS) +endif() + +if (NOT BOOST_LOG_WITHOUT_SYSLOG AND NOT BOOST_LOG_USE_NATIVE_SYSLOG) + check_cxx_source_compiles("#include <${CMAKE_CURRENT_SOURCE_DIR}/config/native-syslog/native_syslog.cpp>" BOOST_LOG_HAS_NATIVE_SYSLOG) +endif() + +if (CMAKE_SYSTEM_NAME STREQUAL "SunOS") + # Solaris headers are broken and cannot be included in C++03 when _XOPEN_SOURCE=600. At the same time, they cannot be included with _XOPEN_SOURCE=500 in C++11 and later. + # This is because the system headers check the C language version and error out if the version does not match. We have to test if we can request _XOPEN_SOURCE=600. + check_cxx_source_compiles("#include <${CMAKE_CURRENT_SOURCE_DIR}/config/xopen-source-600/xopen_source_600.cpp>" BOOST_LOG_HAS_XOPEN_SOURCE_600) +endif() + +unset(CMAKE_REQUIRED_INCLUDES) + +set(boost_log_common_public_defines + # NOTE: + # We deactivate autolinking, because cmake based builds don't need it + # and we don't implement name mangling for the library file anyway. + # Ususally the parent CMakeLists.txt file should already have globally defined BOOST_ALL_NO_LIB + BOOST_LOG_NO_LIB +) + +set(boost_log_common_private_defines + __STDC_CONSTANT_MACROS + BOOST_SPIRIT_USE_PHOENIX_V3=1 + BOOST_THREAD_DONT_USE_CHRONO=1 # Don't introduce false dependency on Boost.Chrono +) + +set(boost_log_private_defines + BOOST_LOG_BUILDING_THE_LIB +) + +set(boost_log_setup_private_defines + BOOST_LOG_SETUP_BUILDING_THE_LIB +) + +if (BUILD_SHARED_LIBS) + list(APPEND boost_log_common_public_defines BOOST_LOG_DYN_LINK) + list(APPEND boost_log_private_defines BOOST_LOG_DLL) + list(APPEND boost_log_setup_private_defines BOOST_LOG_SETUP_DLL) +else() + list(APPEND boost_log_common_public_defines BOOST_LOG_STATIC_LINK) +endif() + +if (BOOST_LOG_WITHOUT_ASIO) + list(APPEND boost_log_common_public_defines BOOST_LOG_WITHOUT_ASIO) +endif() + +set(boost_log_sources + src/alignment_gap_between.hpp + src/attribute_name.cpp + src/attribute_set_impl.hpp + src/attribute_set.cpp + src/attribute_value_set.cpp + src/bit_tools.hpp + src/code_conversion.cpp + src/stateless_allocator.hpp + src/core.cpp + src/record_ostream.cpp + src/severity_level.cpp + src/global_logger_storage.cpp + src/named_scope.cpp + src/process_name.cpp + src/process_id.cpp + src/thread_id.cpp + src/id_formatting.hpp + src/murmur3.hpp + src/timer.cpp + src/exceptions.cpp + src/default_attribute_names.cpp + src/default_sink.hpp + src/default_sink.cpp + src/text_ostream_backend.cpp + src/text_file_backend.cpp + src/text_multifile_backend.cpp + src/thread_specific.cpp + src/once_block.cpp + src/timestamp.cpp + src/threadsafe_queue.cpp + src/event.cpp + src/trivial.cpp + src/spirit_encoding.hpp + src/spirit_encoding.cpp + src/format_parser.cpp + src/date_time_format_parser.cpp + src/named_scope_format_parser.cpp + src/permissions.cpp + src/dump.cpp +) + +if (BOOST_LOG_COMPILER_HAS_SSSE3) + set(boost_log_sources_ssse3 src/dump_ssse3.cpp) + set_source_files_properties(${boost_log_sources_ssse3} PROPERTIES COMPILE_FLAGS "${boost_log_ssse3_cflags}") + list(APPEND boost_log_private_defines BOOST_LOG_USE_SSSE3) +endif() +if (BOOST_LOG_COMPILER_HAS_AVX2) + set(boost_log_sources_avx2 src/dump_avx2.cpp) + set_source_files_properties(${boost_log_sources_avx2} PROPERTIES COMPILE_FLAGS "${boost_log_avx2_cflags}") + list(APPEND boost_log_private_defines BOOST_LOG_USE_AVX2) +endif() + +if (NOT BOOST_LOG_WITHOUT_SYSLOG) + list(APPEND boost_log_sources src/syslog_backend.cpp) + if (BOOST_LOG_USE_NATIVE_SYSLOG OR BOOST_LOG_HAS_NATIVE_SYSLOG) + list(APPEND boost_log_common_private_defines BOOST_LOG_USE_NATIVE_SYSLOG) + endif() +endif() + +if (WIN32) + list(APPEND boost_log_sources + src/windows/light_rw_mutex.cpp + src/windows/is_debugger_present.cpp + ) + list(APPEND boost_log_common_private_defines + # Disable warnings about using 'insecure' standard C functions. + # These affect MSVC C/C++ library headers, which are used by various compilers. + _SCL_SECURE_NO_WARNINGS + _SCL_SECURE_NO_DEPRECATE + _CRT_SECURE_NO_WARNINGS + _CRT_SECURE_NO_DEPRECATE + ) + + if (NOT BOOST_LOG_WITHOUT_DEBUG_OUTPUT) + list(APPEND boost_log_sources src/windows/debug_output_backend.cpp) + endif() + + if (NOT BOOST_LOG_WITHOUT_IPC) + list(APPEND boost_log_sources + src/windows/auto_handle.hpp + src/windows/object_name.cpp + src/windows/utf_code_conversion.hpp + src/windows/mapped_shared_memory.hpp + src/windows/mapped_shared_memory.cpp + src/windows/ipc_sync_wrappers.hpp + src/windows/ipc_sync_wrappers.cpp + src/windows/ipc_reliable_message_queue.cpp + ) + list(APPEND boost_log_private_libs + secur32 + ) + endif() + + if (NOT BOOST_LOG_WITHOUT_EVENT_LOG) + # Find message compiler (mc) + if (NOT CMAKE_MC_COMPILER AND DEFINED ENV{MC} AND EXISTS "$ENV{MC}" AND NOT IS_DIRECTORY "$ENV{MC}") + set(CMAKE_MC_COMPILER "$ENV{MC}") + endif() + if (NOT CMAKE_MC_COMPILER) + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(mc_executable "windmc.exe") + else() + set(mc_executable "mc.exe") + endif() + if (CMAKE_RC_COMPILER) + # Use resource compiler directory as a hint. CMake will initialize CMAKE_RC_COMPILER + # automatically based on environment variables, and message compiler typically resides + # in the same directory in the Windows SDK installation. Reusing resource compiler + # directory also provides user's choice of Windows SDK version and host and target + # architecture. + get_filename_component(rc_compiler_path "${CMAKE_RC_COMPILER}" DIRECTORY) + list(APPEND mc_search_hints "${rc_compiler_path}") + endif() + if (CMAKE_CXX_COMPILER) + # Message compiler can be located next to the compiler, e.g. on MinGW installations. + get_filename_component(cxx_compiler_path "${CMAKE_CXX_COMPILER}" DIRECTORY) + list(APPEND mc_search_hints "${cxx_compiler_path}") + endif() + message(DEBUG "Boost.Log: ${mc_executable} search hints: ${mc_search_hints}") + find_program(CMAKE_MC_COMPILER "${mc_executable}" HINTS "${mc_search_hints}") + if (CMAKE_MC_COMPILER STREQUAL "CMAKE_MC_COMPILER-NOTFOUND") + message(STATUS "Boost.Log: Message compiler ${mc_executable} not found, event log support will be disabled.") + unset(CMAKE_MC_COMPILER) + else() + message(STATUS "Boost.Log: Message compiler found: ${CMAKE_MC_COMPILER}") + endif() + endif() + + if (CMAKE_MC_COMPILER) + add_custom_command( + OUTPUT + "${CMAKE_CURRENT_BINARY_DIR}/src/windows/simple_event_log.h" + "${CMAKE_CURRENT_BINARY_DIR}/src/windows/simple_event_log.rc" + COMMAND "${CMAKE_COMMAND}" -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/src/windows" + COMMAND "${CMAKE_MC_COMPILER}" + -h "${CMAKE_CURRENT_BINARY_DIR}/src/windows" + -r "${CMAKE_CURRENT_BINARY_DIR}/src/windows" + "${CMAKE_CURRENT_SOURCE_DIR}/src/windows/simple_event_log.mc" + MAIN_DEPENDENCY + "${CMAKE_CURRENT_SOURCE_DIR}/src/windows/simple_event_log.mc" + COMMENT + "Building src/windows/simple_event_log.mc" + VERBATIM + ) + + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + # Workaround for a windres.exe issue on MinGW-w64: by default, it uses popen to pipe preprocessed + # output from the preprocessor, but popen is buggy on non-English Windows systems and causes + # compilation errors such as: + # + # windres.exe: can't open file `page:': Invalid argument + # + # This option forces windres.exe to use a temporary file instead. + # https://github.com/boostorg/log/pull/231 + list(APPEND CMAKE_RC_FLAGS "--use-temp-file") + endif() + + list(APPEND boost_log_sources + "${CMAKE_CURRENT_BINARY_DIR}/src/windows/simple_event_log.h" + "${CMAKE_CURRENT_BINARY_DIR}/src/windows/simple_event_log.rc" + src/windows/event_log_registry.hpp + src/windows/event_log_backend.cpp + ) + list(APPEND boost_log_private_libs + psapi + ) + else() + set(BOOST_LOG_WITHOUT_EVENT_LOG ON) + endif() + endif() +else() + if (NOT BOOST_LOG_WITHOUT_IPC) + list(APPEND boost_log_sources + src/posix/object_name.cpp + src/posix/ipc_sync_wrappers.hpp + src/posix/ipc_reliable_message_queue.cpp + ) + endif() +endif() + +if (CYGWIN) + list(APPEND boost_log_common_private_defines + __USE_W32_SOCKETS + _XOPEN_SOURCE=600 + ) +endif() + +if (WIN32 OR CYGWIN) + list(APPEND boost_log_common_private_defines + BOOST_USE_WINDOWS_H + WIN32_LEAN_AND_MEAN + NOMINMAX + SECURITY_WIN32 + ) + list(APPEND boost_log_private_libs + ws2_32 + mswsock + advapi32 + ) + list(APPEND boost_log_setup_private_libs + ws2_32 + ) +endif() + +if (CMAKE_SYSTEM_NAME STREQUAL "Linux") + list(APPEND boost_log_common_private_defines _XOPEN_SOURCE=600) + list(APPEND boost_log_private_libs rt) +endif() + +if (CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") + list(APPEND boost_log_private_libs rt) +endif() + +if (CMAKE_SYSTEM_NAME STREQUAL "HP-UX") + list(APPEND boost_log_common_private_defines _XOPEN_SOURCE=600) + list(APPEND boost_log_private_libs ipv6) +endif() + +if (CMAKE_SYSTEM_NAME STREQUAL "SunOS") + if (BOOST_LOG_HAS_XOPEN_SOURCE_600) + list(APPEND boost_log_common_private_defines _XOPEN_SOURCE=600) + else() + list(APPEND boost_log_common_private_defines _XOPEN_SOURCE=500) + endif() + list(APPEND boost_log_common_private_defines __EXTENSIONS__) + list(APPEND boost_log_private_libs + socket + nsl + ) +endif() + +if (BOOST_LOG_USE_REGEX_BACKEND STREQUAL "Boost.Regex") + set(boost_log_regex_backend_private_libs Boost::regex) + list(APPEND boost_log_common_private_defines BOOST_LOG_USE_BOOST_REGEX) +elseif (BOOST_LOG_USE_REGEX_BACKEND STREQUAL "Boost.Xpressive") + set(boost_log_regex_backend_private_libs Boost::xpressive) + list(APPEND boost_log_common_private_defines BOOST_LOG_USE_BOOST_XPRESSIVE) +else() + list(APPEND boost_log_common_private_defines BOOST_LOG_USE_STD_REGEX) +endif() + + +if (BOOST_LOG_NO_THREADS) + list(APPEND boost_log_common_public_defines BOOST_LOG_NO_THREADS) +endif() +if (BOOST_LOG_WITHOUT_CHAR) + list(APPEND boost_log_common_public_defines BOOST_LOG_WITHOUT_CHAR) +endif() +if (BOOST_LOG_WITHOUT_WCHAR_T) + list(APPEND boost_log_common_public_defines BOOST_LOG_WITHOUT_WCHAR_T) +endif() +if (BOOST_LOG_WITHOUT_DEFAULT_FACTORIES) + list(APPEND boost_log_setup_private_defines BOOST_LOG_WITHOUT_DEFAULT_FACTORIES) +endif() +if (BOOST_LOG_WITHOUT_SETTINGS_PARSERS) + list(APPEND boost_log_setup_private_defines BOOST_LOG_WITHOUT_SETTINGS_PARSERS) +endif() +if (BOOST_LOG_WITHOUT_SYSLOG) + list(APPEND boost_log_common_private_defines BOOST_LOG_WITHOUT_SYSLOG) +endif() +if (BOOST_LOG_WITHOUT_IPC) + list(APPEND boost_log_common_private_defines BOOST_LOG_WITHOUT_IPC) +endif() +if (WIN32) + if (BOOST_LOG_NO_QUERY_PERFORMANCE_COUNTER) + list(APPEND boost_log_common_private_defines BOOST_LOG_NO_QUERY_PERFORMANCE_COUNTER) + endif() + if (BOOST_LOG_WITHOUT_DEBUG_OUTPUT) + list(APPEND boost_log_common_private_defines BOOST_LOG_WITHOUT_DEBUG_OUTPUT) + endif() + if (BOOST_LOG_WITHOUT_EVENT_LOG) + list(APPEND boost_log_common_private_defines BOOST_LOG_WITHOUT_EVENT_LOG) + endif() +endif() + + +if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + list(APPEND boost_log_common_private_cxxflags + /bigobj + /wd4503 # decorated name length exceeded, name was truncated + /wd4456 # declaration of 'A' hides previous local declaration + /wd4459 # declaration of 'A' hides global declaration + /wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion + ) +endif() + +if (CMAKE_CXX_COMPILER_ID STREQUAL "Intel") + # Disable Intel warnings: + # warning #177: function "X" was declared but never referenced + # warning #780: using-declaration ignored -- it refers to the current namespace + # warning #2196: routine is both "inline" and "noinline" + # remark #1782: #pragma once is obsolete. Use #ifndef guard instead. + # remark #193: zero used for undefined preprocessing identifier "X" + # remark #304: access control not specified ("public" by default) + # remark #981: operands are evaluated in unspecified order + # remark #1418: external function definition with no prior declaration + # Mostly comes from Boost.Phoenix: warning #411: class "X" defines no constructor to initialize the following: reference member "Y"... + # warning #734: "X" (declared at line N of "file.hpp"), required for copy that was eliminated, is inaccessible + # warning #279: controlling expression is constant + if (WIN32) + list(APPEND boost_log_common_private_cxxflags + "/Qwd177,780,2196,1782,193,304,981,1418,411,734,279" + ) + else() + list(APPEND boost_log_common_private_cxxflags + "-wd177,780,2196,1782,193,304,981,1418,411,734,279" + ) + endif() +endif() + +if ((WIN32 OR CYGWIN) AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + list(APPEND boost_log_common_private_linkflags + -Wl,--enable-auto-import + ) +endif() + + +add_library(boost_log + ${boost_log_sources} + ${boost_log_sources_ssse3} + ${boost_log_sources_avx2} +) +if (BOOST_SUPERPROJECT_VERSION) + set_target_properties(boost_log PROPERTIES VERSION "${BOOST_SUPERPROJECT_VERSION}") +endif() +set(boost_log_install_targets boost_log) +add_library(Boost::log ALIAS boost_log) + +target_compile_features(boost_log + PUBLIC + cxx_static_assert + PRIVATE + cxx_lambdas +) + +target_include_directories(boost_log + PUBLIC + include + PRIVATE + src + "${CMAKE_CURRENT_BINARY_DIR}/src" +) + +target_link_libraries(boost_log + PUBLIC + Boost::assert + Boost::config + Boost::core + Boost::date_time + Boost::filesystem + Boost::function_types + Boost::fusion + Boost::intrusive + Boost::move + Boost::mpl + Boost::parameter + Boost::phoenix + Boost::predef + Boost::preprocessor + Boost::proto + Boost::range + Boost::smart_ptr + Boost::system + Boost::throw_exception + Boost::type_index + Boost::type_traits + Boost::utility + + PRIVATE + Boost::align + Boost::bind + Boost::exception + Boost::interprocess + Boost::optional + Boost::spirit + ${boost_log_regex_backend_private_libs} +) + +if (WIN32 OR CYGWIN) + target_link_libraries(boost_log + PUBLIC + Boost::winapi + ) +endif() + +if (NOT BOOST_LOG_NO_THREADS) + target_link_libraries(boost_log + PUBLIC + Boost::atomic + Boost::thread + ) +endif() + +if (NOT BOOST_LOG_WITHOUT_ASIO) + target_link_libraries(boost_log + PRIVATE + Boost::asio_core + ) +endif() + +target_compile_definitions(boost_log + PUBLIC + ${boost_log_common_public_defines} + ${boost_log_public_defines} + PRIVATE + ${boost_log_common_private_defines} + ${boost_log_private_defines} +) + +target_compile_options(boost_log + PRIVATE + ${boost_log_common_private_cxxflags} +) + +target_link_libraries(boost_log + PUBLIC + ${boost_log_public_libs} + PRIVATE + ${boost_log_private_libs} + ${boost_log_common_private_linkflags} +) + +# An alias target for Boost::log but with all optional dependencies (i.e. for support headers) +add_library(boost_log_with_support INTERFACE) +add_library(Boost::log_with_support ALIAS boost_log_with_support) + +target_link_libraries(boost_log_with_support + INTERFACE + Boost::log + + Boost::exception + Boost::regex + Boost::spirit + Boost::xpressive +) + + +set(boost_log_setup_public_deps + Boost::log + + Boost::assert + Boost::config + Boost::core + Boost::iterator + Boost::move + Boost::optional + Boost::parameter + Boost::phoenix + Boost::preprocessor + Boost::property_tree + Boost::smart_ptr + Boost::type_traits +) + +if (NOT BOOST_LOG_WITHOUT_SETTINGS_PARSERS) + set(boost_log_setup_sources + src/setup/parser_utils.hpp + src/setup/parser_utils.cpp + src/setup/init_from_stream.cpp + src/setup/init_from_settings.cpp + src/setup/settings_parser.cpp + src/setup/filter_parser.cpp + src/setup/formatter_parser.cpp + ) + + if (NOT BOOST_LOG_WITHOUT_DEFAULT_FACTORIES) + list(APPEND boost_log_setup_sources + src/setup/default_filter_factory.hpp + src/setup/default_filter_factory.cpp + src/setup/matches_relation_factory.cpp + src/setup/default_formatter_factory.hpp + src/setup/default_formatter_factory.cpp + ) + endif() + + add_library(boost_log_setup ${boost_log_setup_sources}) + if (BOOST_SUPERPROJECT_VERSION) + set_target_properties(boost_log_setup PROPERTIES VERSION "${BOOST_SUPERPROJECT_VERSION}") + endif() + list(APPEND boost_log_install_targets boost_log_setup) + + target_compile_features(boost_log_setup + PUBLIC + cxx_static_assert + cxx_uniform_initialization + PRIVATE + cxx_lambdas + ) + + target_include_directories(boost_log_setup + PUBLIC + include + PRIVATE + src + ) + + target_link_libraries(boost_log_setup + PUBLIC + ${boost_log_setup_public_deps} + + PRIVATE + Boost::bind + Boost::date_time + Boost::exception + Boost::filesystem + Boost::io + Boost::spirit + Boost::throw_exception + Boost::utility + ) + + if (NOT BOOST_LOG_WITHOUT_DEFAULT_FACTORIES) + target_link_libraries(boost_log_setup + PRIVATE + Boost::fusion + Boost::mpl + ${boost_log_regex_backend_private_libs} + ) + endif() + + if (NOT BOOST_LOG_WITHOUT_ASIO) + target_link_libraries(boost_log_setup + PRIVATE + Boost::asio_core + ) + endif() + + target_compile_definitions(boost_log_setup + PUBLIC + ${boost_log_common_public_defines} + PRIVATE + ${boost_log_common_private_defines} + ${boost_log_setup_private_defines} + ) + + target_compile_options(boost_log_setup + PRIVATE + ${boost_log_common_private_cxxflags} + ) + + target_link_libraries(boost_log_setup + PRIVATE + ${boost_log_setup_private_libs} + ${boost_log_common_private_linkflags} + ) +else() + add_library(boost_log_setup INTERFACE) + target_include_directories(boost_log_setup INTERFACE include) + target_compile_definitions(boost_log_setup INTERFACE ${boost_log_common_public_defines}) + target_link_libraries(boost_log_setup INTERFACE ${boost_log_setup_public_deps}) +endif() + +add_library(Boost::log_setup ALIAS boost_log_setup) + +if (BOOST_SUPERPROJECT_VERSION AND NOT CMAKE_VERSION VERSION_LESS 3.13) + boost_install(TARGETS ${boost_log_install_targets} VERSION "${BOOST_SUPERPROJECT_VERSION}" HEADER_DIRECTORY include) +endif() diff --git a/README.md b/README.md index 83e666b537..122207e10a 100644 --- a/README.md +++ b/README.md @@ -14,12 +14,19 @@ Boost.Log, part of collection of the [Boost C++ Libraries](https://github.com/bo ### More information -* [Documentation](https://boost.org/libs/log) +* [Documentation](https://www.boost.org/libs/log) * [Ask questions](https://stackoverflow.com/questions/ask?tags=c%2B%2B,boost,boost-log) -* [Report bugs](https://svn.boost.org/trac/boost/newticket?component=log;version=Boost%20Release%20Branch). Be sure to mention Boost version, platform and compiler you're using. A small compilable code sample to reproduce the problem is always good as well. -* Submit your patches as pull requests against **develop** branch. Note that by submitting patches you agree to license your modifications under the [Boost Software License, Version 1.0](https://www.boost.org/LICENSE_1_0.txt). +* [Report bugs](https://github.com/boostorg/log/issues/new). Be sure to mention Boost version, platform and compiler you're using. A small compilable code sample to reproduce the problem is always good as well. +* Submit your patches as [pull requests](https://github.com/boostorg/log/compare) against **develop** branch. Note that by submitting patches you agree to license your modifications under the [Boost Software License, Version 1.0](https://www.boost.org/LICENSE_1_0.txt). * Discussions about the library are held on the [Boost developers mailing list](https://www.boost.org/community/groups.html#main). Be sure to read the [discussion policy](https://www.boost.org/community/policy.html) before posting and add the `[log]` tag at the beginning of the subject line. +### Build status + +Branch | GitHub Actions | AppVeyor | Test Matrix | Dependencies | +:-------------: | -------------- | -------- | ----------- | ------------ | +[`master`](https://github.com/boostorg/log/tree/master) | [![GitHub Actions](https://github.com/boostorg/log/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/boostorg/log/actions?query=branch%3Amaster) | [![AppVeyor](https://ci.appveyor.com/api/projects/status/w7x67cnm82xihei5/branch/master?svg=true)](https://ci.appveyor.com/project/Lastique/log/branch/master) | [![Tests](https://img.shields.io/badge/matrix-master-brightgreen.svg)](https://regression.boost.io/master/developer/log.html) | [![Dependencies](https://img.shields.io/badge/deps-master-brightgreen.svg)](https://pdimov.github.io/boostdep-report/master/log.html) +[`develop`](https://github.com/boostorg/log/tree/develop) | [![GitHub Actions](https://github.com/boostorg/log/actions/workflows/ci.yml/badge.svg?branch=develop)](https://github.com/boostorg/log/actions?query=branch%3Adevelop) | [![AppVeyor](https://ci.appveyor.com/api/projects/status/w7x67cnm82xihei5/branch/develop?svg=true)](https://ci.appveyor.com/project/Lastique/log/branch/develop) | [![Tests](https://img.shields.io/badge/matrix-develop-brightgreen.svg)](https://regression.boost.io/develop/developer/log.html) | [![Dependencies](https://img.shields.io/badge/deps-develop-brightgreen.svg)](https://pdimov.github.io/boostdep-report/develop/log.html) + ### License Distributed under the [Boost Software License, Version 1.0](https://www.boost.org/LICENSE_1_0.txt). diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000000..7336195277 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,130 @@ +# Copyright 2019 - 2025 Andrey Semashev +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt) + +version: 1.0.{build}-{branch} + +shallow_clone: true + +branches: + only: + - master + - develop + - /feature\/.*/ + +environment: + matrix: + - TOOLSET: msvc-14.0 + ADDRESS_MODEL: 64 + EXTRA_TESTS: 1 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + - TOOLSET: msvc-14.1 + ADDRESS_MODEL: 64 + CXXSTD: 14,17 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + - TOOLSET: msvc-14.2 + ADDRESS_MODEL: 64 + CXXSTD: 14,17,20 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 + - TOOLSET: msvc-14.3 + ADDRESS_MODEL: 64 + CXXSTD: 14,17,20 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 + B2_ARGS: define=_WIN32_WINNT=0x0A00 + - TOOLSET: gcc + ADDRESS_MODEL: 64 + CXXSTD: 11 + ADDPATH: C:\cygwin64\bin + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + - TOOLSET: gcc + ADDRESS_MODEL: 64 + CXXSTD: 11,14,17 + ADDPATH: C:\mingw-w64\x86_64-7.3.0-posix-seh-rt_v5-rev0\mingw64\bin + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + - TOOLSET: gcc + ADDRESS_MODEL: 64 + CXXSTD: 11,14,17,2a + ADDPATH: C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + - TOOLSET: clang-win + ADDRESS_MODEL: 64 + CXXSTD: 14,17 + ENV_SCRIPT: C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 + + - TOOLSET: msvc-14.0 + ADDRESS_MODEL: 32 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + - TOOLSET: msvc-14.1 + ADDRESS_MODEL: 32 + CXXSTD: 14,17 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + - TOOLSET: msvc-14.2 + ADDRESS_MODEL: 32 + CXXSTD: 14,17,20 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 + - TOOLSET: gcc + ADDRESS_MODEL: 32 + CXXSTD: 11 + ADDPATH: C:\cygwin\bin + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + - TOOLSET: gcc + ADDRESS_MODEL: 32 + CXXSTD: 11,14 + ADDPATH: C:\mingw-w64\i686-8.1.0-posix-dwarf-rt_v6-rev0\mingw32\bin + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 + - TOOLSET: clang-win + ADDRESS_MODEL: 32 + CXXSTD: 14,17 + ENV_SCRIPT: C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars32.bat + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 + + - TEST_CMAKE: 1 + ENV_SCRIPT: C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 + +install: + - set GIT_FETCH_JOBS=8 + - set BOOST_BRANCH=develop + - if "%APPVEYOR_REPO_BRANCH%" == "master" set BOOST_BRANCH=master + - cd .. + - git clone -b %BOOST_BRANCH% https://github.com/boostorg/boost.git boost-root + - cd boost-root + - git submodule init tools/boostdep + - git submodule init tools/build + - git submodule init tools/boost_install + - git submodule init libs/headers + - git submodule init libs/config + - git submodule update --jobs %GIT_FETCH_JOBS% + - xcopy /s /e /q %APPVEYOR_BUILD_FOLDER% libs\log + - if not "%EXTRA_TESTS%" == "" set DEPINST_ARG_INCLUDE_EXAMPLES="--include=example" + - python tools/boostdep/depinst/depinst.py %DEPINST_ARG_INCLUDE_EXAMPLES% --git_args "--jobs %GIT_FETCH_JOBS%" log + - cmd /c bootstrap + - b2 -d0 headers + +build: off + +test_script: + - PATH=%ADDPATH%;%PATH% + - if not "%ENV_SCRIPT%" == "" call "%ENV_SCRIPT%" + - if "%EXTRA_TESTS%" == "" set BOOST_LOG_TEST_WITHOUT_SELF_CONTAINED_HEADER_TESTS=1 & set BOOST_LOG_TEST_WITHOUT_EXAMPLES=1 + - if not "%CXXSTD%" == "" set CXXSTD=cxxstd=%CXXSTD% + - b2 -j %NUMBER_OF_PROCESSORS% libs/log/test variant=release toolset=%TOOLSET% address-model=%ADDRESS_MODEL% %CXXSTD% %B2_ARGS% + +for: + - matrix: + only: [TEST_CMAKE: 1] + test_script: + - PATH=%ADDPATH%;%PATH% + - if not "%ENV_SCRIPT%" == "" call "%ENV_SCRIPT%" + - mkdir __build_static__ + - cd __build_static__ + - cmake ../libs/log/test/test_cmake + - cmake --build . --target boost_log_cmake_self_test -j %NUMBER_OF_PROCESSORS% + - cmake --build . --target boost_log_setup_cmake_self_test -j %NUMBER_OF_PROCESSORS% + - cd .. + - mkdir __build_shared__ + - cd __build_shared__ + - cmake -DBUILD_SHARED_LIBS=On ../libs/log/test/test_cmake + - cmake --build . --target boost_log_cmake_self_test -j %NUMBER_OF_PROCESSORS% + - cmake --build . --target boost_log_setup_cmake_self_test -j %NUMBER_OF_PROCESSORS% diff --git a/build.jam b/build.jam new file mode 100644 index 0000000000..aac1c89ecb --- /dev/null +++ b/build.jam @@ -0,0 +1,19 @@ +# Copyright René Ferdinand Rivera Morell 2023-2024 +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +require-b2 5.2 ; + +project /boost/log ; + +explicit + [ alias boost_log : build//boost_log ] + [ alias boost_log_setup : build//boost_log_setup ] + [ alias boost_log_with_support : build//boost_log_with_support ] + [ alias all : boost_log boost_log_setup boost_log_with_support example test ] + ; + +call-if : boost-library log + : install boost_log boost_log_setup + ; diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index 27a7d6fe80..2d0e5c2237 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -1,5 +1,5 @@ # -# Copyright Andrey Semashev 2007 - 2016. +# Copyright Andrey Semashev 2007 - 2025. # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) @@ -12,19 +12,15 @@ import path ; import project ; import feature ; import configure ; -import log-architecture ; +import-search /boost/config/checks ; +import config : requires ; +import log-arch-config ; import log-platform-config ; -using mc ; - -local here = [ modules.binding $(__name__) ] ; +import log-build-config ; +import-search /boost/predef/tools/check ; +import predef ; -project.push-current [ project.current ] ; -project.load [ path.join [ path.make $(here:D) ] ../config/message-compiler ] ; -project.load [ path.join [ path.make $(here:D) ] ../config/x86-ext ] ; -project.load [ path.join [ path.make $(here:D) ] ../config/pthread-mutex-robust ] ; -project.load [ path.join [ path.make $(here:D) ] ../config/native-syslog ] ; -project.load [ path.join [ path.make $(here:D) ] ../config/atomic-int32 ] ; -project.pop-current ; +using mc ; # Windows libs lib psapi ; @@ -32,139 +28,110 @@ lib advapi32 ; lib secur32 ; lib ws2_32 ; lib mswsock ; +explicit psapi advapi32 secur32 ws2_32 mswsock ; # UNIX libs lib rt ; lib socket ; lib nsl ; lib ipv6 ; +explicit rt socket nsl ipv6 ; + +constant boost_log_public_deps : + /boost/assert//boost_assert + /boost/config//boost_config + /boost/core//boost_core + /boost/date_time//boost_date_time + /boost/filesystem//boost_filesystem + /boost/function_types//boost_function_types + /boost/fusion//boost_fusion + /boost/intrusive//boost_intrusive + /boost/move//boost_move + /boost/mpl//boost_mpl + /boost/parameter//boost_parameter + /boost/phoenix//boost_phoenix + /boost/predef//boost_predef + /boost/preprocessor//boost_preprocessor + /boost/proto//boost_proto + /boost/range//boost_range + /boost/smart_ptr//boost_smart_ptr + /boost/system//boost_system + /boost/throw_exception//boost_throw_exception + /boost/type_index//boost_type_index + /boost/type_traits//boost_type_traits + /boost/utility//boost_utility + windows:/boost/winapi//boost_winapi + [ requires + cxx11_static_assert + ] + ; -rule has-config-flag ( flag : properties * ) -{ - if ( "$(flag)" in $(properties) || "$(flag)=1" in $(properties) ) - { - return 1 ; - } - else - { - return ; - } -} - -rule check-instruction-set ( properties * ) -{ - local result ; - local instruction_set = [ feature.get-values "log-instruction-set" : [ log-architecture.deduce-instruction-set $(properties) ] ] ; - - if $(instruction_set) = i386 || $(instruction_set) = i486 - { - if ! $(.annouced-failure) - { - ECHO Boost.Log is not supported on the specified target CPU and will not be built. At least i586 class CPU is required. ; - .annouced-failure = 1 ; - } - result = no ; - } - - return $(result) ; -} - -rule select-regex-backend ( properties * ) -{ - local result = ; - - # Use Boost.Regex backend by default. It produces smaller executables and also has the best performance for small string matching. - if ! ( - [ has-config-flag BOOST_LOG_WITHOUT_SETTINGS_PARSERS : $(properties) ] || - [ has-config-flag BOOST_LOG_WITHOUT_DEFAULT_FACTORIES : $(properties) ] || - [ has-config-flag BOOST_LOG_USE_STD_REGEX : $(properties) ] || - [ has-config-flag BOOST_LOG_USE_BOOST_XPRESSIVE : $(properties) ] ) - { - result = /boost/regex//boost_regex ; - } - - return $(result) ; -} - -rule check-pthread-mutex-robust ( properties * ) -{ - local result = ; - - local has_pthread_mutex_robust = [ configure.builds /boost/log/pthread-mutex-robust//pthread_mutex_robust : $(properties) : pthread-supports-robust-mutexes ] ; - if $(has_pthread_mutex_robust) - { - result = BOOST_LOG_HAS_PTHREAD_MUTEX_ROBUST ; - } - - return $(result) ; -} - -rule check-atomic-int32 ( properties * ) -{ - local result = ; - - local has_atomic_int32 = [ configure.builds /boost/log/atomic-int32//atomic_int32 : $(properties) : native-atomic-int32-supported ] ; - if ! $(has_atomic_int32) - { - result = BOOST_LOG_WITHOUT_IPC ; - } - - return $(result) ; -} - -rule check-native-syslog ( properties * ) -{ - local result = ; - - if ! [ has-config-flag BOOST_LOG_WITHOUT_SYSLOG : $(properties) ] - { - local has_native_syslog = [ configure.builds /boost/log/native-syslog//native_syslog : $(properties) : native-syslog-supported ] ; - if $(has_native_syslog) - { - result = BOOST_LOG_USE_NATIVE_SYSLOG ; - } - } - - return $(result) ; -} +constant boost_log_private_deps : + /boost/align//boost_align + /boost/bind//boost_bind + /boost/exception//boost_exception + /boost/interprocess//boost_interprocess + /boost/optional//boost_optional + /boost/spirit//boost_spirit + @log-build-config.check-asio + [ requires + cxx11_lambdas + ] + ; -rule check-message-compiler ( properties * ) -{ - local result ; +constant boost_log_setup_public_deps : + /boost/assert//boost_assert + /boost/config//boost_config + /boost/core//boost_core + /boost/iterator//boost_iterator + /boost/move//boost_move + /boost/optional//boost_optional + /boost/parameter//boost_parameter + /boost/phoenix//boost_phoenix + /boost/preprocessor//boost_preprocessor + /boost/property_tree//boost_property_tree + /boost/smart_ptr//boost_smart_ptr + /boost/type_traits//boost_type_traits + [ requires + cxx11_static_assert + cxx11_unified_initialization_syntax + ] + ; - if windows in $(properties) - { - if ! [ has-config-flag BOOST_LOG_WITHOUT_EVENT_LOG : $(properties) ] - { - local has_mc = [ configure.builds /boost/log/message-compiler//test-availability : $(properties) : message-compiler ] ; - if ! $(has_mc) - { - result = BOOST_LOG_WITHOUT_EVENT_LOG ; - } - } - else - { - # This branch is needed to fix building with MinGW - result = BOOST_LOG_WITHOUT_EVENT_LOG ; - } - } - else - { - result = BOOST_LOG_WITHOUT_EVENT_LOG ; - } +constant boost_log_setup_private_deps : + /boost/bind//boost_bind + /boost/date_time//boost_date_time + /boost/exception//boost_exception + /boost/filesystem//boost_filesystem + /boost/io//boost_io + /boost/log//boost_log + /boost/spirit//boost_spirit + /boost/throw_exception//boost_throw_exception + /boost/utility//boost_utility + @log-build-config.check-asio + [ requires + cxx11_lambdas + ] + ; - return $(result) ; -} +constant boost_log_support_public_deps : + /boost/log//boost_log + /boost/exception//boost_exception + /boost/spirit//boost_spirit + /boost/xpressive//boost_xpressive + ; -project boost/log +project : source-location ../src + : common-requirements + ../include : requirements - @check-instruction-set - @check-atomic-int32 - @select-regex-backend - @check-pthread-mutex-robust - @check-native-syslog - @check-message-compiler + @log-arch-config.check-instruction-set + @log-build-config.check-atomic-int32 + @log-build-config.select-regex-backend + @log-build-config.check-pthread-mutex-robust + @log-build-config.check-native-syslog + @log-build-config.check-message-compiler @log-platform-config.set-platform-defines ../src @@ -173,15 +140,7 @@ project boost/log BOOST_SPIRIT_USE_PHOENIX_V3=1 BOOST_THREAD_DONT_USE_CHRONO=1 # Don't introduce false dependency on Boost.Chrono - # Disable warnings about using 'insecure' standard C functions - msvc:_SCL_SECURE_NO_WARNINGS - msvc:_SCL_SECURE_NO_DEPRECATE - msvc:_CRT_SECURE_NO_WARNINGS - msvc:_CRT_SECURE_NO_DEPRECATE - intel-win:_SCL_SECURE_NO_WARNINGS - intel-win:_SCL_SECURE_NO_DEPRECATE - intel-win:_CRT_SECURE_NO_WARNINGS - intel-win:_CRT_SECURE_NO_DEPRECATE + 1024 msvc:/bigobj msvc:/wd4503 # decorated name length exceeded, name was truncated @@ -205,25 +164,21 @@ project boost/log intel-linux:"-wd177,780,2196,1782,193,304,981,1418,411,734,279" intel-darwin:"-wd177,780,2196,1782,193,304,981,1418,411,734,279" - darwin:-ftemplate-depth-1024 - clang:-ftemplate-depth-1024 - gcc:-ftemplate-depth-1024 - gcc:-fno-strict-aliasing # avoids strict aliasing violations in other Boost components gcc,windows:-Wl,--enable-auto-import gcc,cygwin:-Wl,--enable-auto-import - /boost/date_time//boost_date_time /boost/filesystem//boost_filesystem - /boost/system//boost_system single:BOOST_LOG_NO_THREADS multi:/boost/atomic//boost_atomic multi:/boost/thread//boost_thread + windows:BOOST_USE_WINDOWS_H windows:ws2_32 windows:mswsock windows:advapi32 + cygwin:BOOST_USE_WINDOWS_H cygwin:ws2_32 cygwin:mswsock cygwin:advapi32 @@ -238,9 +193,17 @@ project boost/log freebsd:rt qnxnto:socket pgi:rt + + # Set these to computed values according to the build. + [ predef.address-model ] + [ predef.architecture ] : usage-requirements clang:-Wno-bind-to-temporary-copy clang:-Wno-unused-function + + single:BOOST_LOG_NO_THREADS + multi:/boost/atomic//boost_atomic + multi:/boost/thread//boost_thread ; local BOOST_LOG_COMMON_SRC = @@ -285,59 +248,13 @@ BOOST_LOG_COMMON_AVX2_SRC = dump_avx2 ; -rule ssse3-targets-cond ( properties * ) -{ - local result = no ; - - if x86 in [ log-architecture.deduce-architecture $(properties) ] - { - local has_ssse3 = [ configure.builds /boost/log/x86-extensions//ssse3 : $(properties) : compiler-supports-ssse3 ] ; - if $(has_ssse3) - { - result = ; - if gcc in $(properties) || clang in $(properties) - { - result = "-msse -msse2 -msse3 -mssse3" ; - } - else if intel in $(properties) - { - if win in $(properties) - { - result = "/QxSSSE3" ; - } - else - { - result = "-xSSSE3" ; - } - } - else if msvc in $(properties) - { - # MSVC doesn't really care about these switches, all SSE intrinsics are always available, but still... - # Also 64 bit MSVC doesn't have the /arch:SSE2 switch as it is the default. - if 32 in [ log-architecture.deduce-address-model $(properties) ] - { - result = "/arch:SSE2" ; - } - } - } - } - -# if ! no in $(result) -# { -# ECHO Boost.Log: Using SSSE3 optimized implementation ; -# } -# ECHO $(result) ; - - return $(result) ; -} - for local src in $(BOOST_LOG_COMMON_SSSE3_SRC) { obj $(src) : ## sources ## $(src).cpp : ## requirements ## - @ssse3-targets-cond + @log-arch-config.ssse3-flags shared:BOOST_LOG_DLL BOOST_LOG_BUILDING_THE_LIB=1 : ## default-build ## @@ -348,58 +265,13 @@ for local src in $(BOOST_LOG_COMMON_SSSE3_SRC) explicit $(src) ; } -rule avx2-targets-cond ( properties * ) -{ - local result = no ; - - if x86 in [ log-architecture.deduce-architecture $(properties) ] - { - local has_avx2 = [ configure.builds /boost/log/x86-extensions//avx2 : $(properties) : compiler-supports-avx2 ] ; - if $(has_avx2) - { - result = ; - if gcc in $(properties) - { - result = "-mavx -mavx2 -fabi-version=0" ; - } - else if clang in $(properties) - { - result = "-mavx -mavx2" ; - } - else if intel in $(properties) - { - if win in $(properties) - { - result = "/arch:CORE-AVX2" ; - } - else - { - result = "-xCORE-AVX2 -fabi-version=0" ; - } - } - else if msvc in $(properties) - { - result = "/arch:AVX" ; - } - } - } - -# if ! no in $(result) -# { -# ECHO Boost.Log: Using AVX2 optimized implementation ; -# } -# ECHO $(result) ; - - return $(result) ; -} - for local src in $(BOOST_LOG_COMMON_AVX2_SRC) { obj $(src) : ## sources ## $(src).cpp : ## requirements ## - @avx2-targets-cond + @log-arch-config.avx2-flags shared:BOOST_LOG_DLL BOOST_LOG_BUILDING_THE_LIB=1 : ## default-build ## @@ -414,16 +286,16 @@ rule select-arch-specific-sources ( properties * ) { local result ; - if x86 in [ log-architecture.deduce-architecture $(properties) ] + if x86 in $(properties) { - local has_ssse3 = [ configure.builds /boost/log/x86-extensions//ssse3 : $(properties) : compiler-supports-ssse3 ] ; + local has_ssse3 = [ configure.builds /boost/log/config/x86-ext//ssse3 : $(properties) : "compiler supports SSSE3" ] ; if $(has_ssse3) { result += BOOST_LOG_USE_SSSE3 ; result += $(BOOST_LOG_COMMON_SSSE3_SRC) ; } - local has_avx2 = [ configure.builds /boost/log/x86-extensions//avx2 : $(properties) : compiler-supports-avx2 ] ; + local has_avx2 = [ configure.builds /boost/log/config/x86-ext//avx2 : $(properties) : "compiler supports AVX2" ] ; if $(has_avx2) { result += BOOST_LOG_USE_AVX2 ; @@ -443,8 +315,9 @@ rule select-platform-specific-sources ( properties * ) if windows in $(properties) { result += windows/light_rw_mutex.cpp ; + result += windows/is_debugger_present.cpp ; - if ! [ has-config-flag BOOST_LOG_WITHOUT_IPC : $(properties) ] + if ! [ log-build-config.has-config-flag BOOST_LOG_WITHOUT_IPC : $(properties) ] { result += windows/object_name.cpp ; result += windows/mapped_shared_memory.cpp ; @@ -453,12 +326,12 @@ rule select-platform-specific-sources ( properties * ) result += secur32 ; } - if ! [ has-config-flag BOOST_LOG_WITHOUT_DEBUG_OUTPUT : $(properties) ] + if ! [ log-build-config.has-config-flag BOOST_LOG_WITHOUT_DEBUG_OUTPUT : $(properties) ] { result += windows/debug_output_backend.cpp ; } - if ! [ has-config-flag BOOST_LOG_WITHOUT_EVENT_LOG : $(properties) ] + if ! [ log-build-config.has-config-flag BOOST_LOG_WITHOUT_EVENT_LOG : $(properties) ] { result += windows/simple_event_log.mc ; result += windows/event_log_backend.cpp ; @@ -472,14 +345,14 @@ rule select-platform-specific-sources ( properties * ) result += BOOST_LOG_WITHOUT_EVENT_LOG ; result += BOOST_LOG_WITHOUT_DEBUG_OUTPUT ; - if ! [ has-config-flag BOOST_LOG_WITHOUT_IPC : $(properties) ] + if ! [ log-build-config.has-config-flag BOOST_LOG_WITHOUT_IPC : $(properties) ] { result += posix/object_name.cpp ; result += posix/ipc_reliable_message_queue.cpp ; } } - if ! [ has-config-flag BOOST_LOG_WITHOUT_SYSLOG : $(properties) ] + if ! [ log-build-config.has-config-flag BOOST_LOG_WITHOUT_SYSLOG : $(properties) ] { result += syslog_backend.cpp ; } @@ -497,10 +370,14 @@ lib boost_log @select-platform-specific-sources shared:BOOST_LOG_DLL BOOST_LOG_BUILDING_THE_LIB=1 + $(boost_log_private_deps) + $(boost_log_public_deps) : ## default-build ## : ## usage-requirements ## shared:BOOST_LOG_DYN_LINK=1 single:BOOST_LOG_NO_THREADS + BOOST_LOG_NO_LIB=1 + $(boost_log_public_deps) ; @@ -516,18 +393,39 @@ local BOOST_LOG_SETUP_COMMON_SRC = default_formatter_factory.cpp ; +rule check-boost_log_setup-enabled ( properties * ) +{ + local result ; + + if [ log-build-config.has-config-flag BOOST_LOG_WITHOUT_SETTINGS_PARSERS : $(properties) ] + { + result = no ; + } + + return $(result) ; +} + lib boost_log_setup : ## sources ## setup/$(BOOST_LOG_SETUP_COMMON_SRC) : ## requirements ## + @check-boost_log_setup-enabled shared:BOOST_LOG_DYN_LINK=1 shared:BOOST_LOG_SETUP_DLL BOOST_LOG_SETUP_BUILDING_THE_LIB=1 - boost_log + $(boost_log_setup_private_deps) + $(boost_log_setup_public_deps) : ## default-build ## : ## usage-requirements ## shared:BOOST_LOG_SETUP_DYN_LINK=1 single:BOOST_LOG_NO_THREADS + BOOST_LOG_SETUP_NO_LIB=1 + $(boost_log_setup_public_deps) ; -boost-install boost_log boost_log_setup ; +alias boost_log_with_support + : requirements + $(boost_log_support_public_deps) + : usage-requirements + $(boost_log_support_public_deps) + ; diff --git a/build/log-arch-config.jam b/build/log-arch-config.jam new file mode 100644 index 0000000000..b5438ff009 --- /dev/null +++ b/build/log-arch-config.jam @@ -0,0 +1,127 @@ +# log-arch-config.jam +# +# Copyright 2012 Steven Watanabe +# Copyright 2013, 2020 Andrey Semashev +# +# Distributed under the Boost Software License Version 1.0. (See +# accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +import configure ; +import project ; +import path ; +import property ; +import feature ; + +rule deduce-address-model ( properties * ) +{ + # The address-model is always set to a deduced value using the predef.address-model checks. + return [ feature.get-values : $(properties) ] ; +} + +rule deduce-architecture ( properties * ) +{ + # The architecture is always set to a deduced value using the predef.architecture checks. + return [ feature.get-values : $(properties) ] ; +} + +rule deduce-instruction-set ( properties * ) +{ + local result ; + local instruction_set = [ feature.get-values : $(properties) ] ; + + if $(instruction_set) + { + result = $(instruction_set) ; + } + else + { + if x86 in [ deduce-architecture $(properties) ] && 32 in [ deduce-address-model $(properties) ] + { + # We build for Pentium Pro and later CPUs by default. This is used as the target in many Linux distributions, and Windows and OS X also seem to not support older CPUs. + result = i686 ; + } + } + + return $(result) ; +} + +rule ssse3-flags ( properties * ) +{ + local result ; + if intel in $(properties) + { + if win in $(properties) + { + result = "/QxSSSE3" ; + } + else + { + result = "-xSSSE3" ; + } + } + else if msvc in $(properties) + { + # MSVC doesn't really care about these switches, all SSE intrinsics are always available, but still... + # Also 64 bit MSVC doesn't have the /arch:SSE2 switch as it is the default. + if 32 in $(properties) + { + result = "/arch:SSE2" ; + } + } + else + { + result = "-msse -msse2 -msse3 -mssse3" ; + } + + return $(result) ; +} + +rule avx2-flags ( properties * ) +{ + local result ; + + if intel in $(properties) + { + if win in $(properties) + { + result = "/arch:CORE-AVX2" ; + } + else + { + result = "-xCORE-AVX2 -fabi-version=0" ; + } + } + else if msvc in $(properties) + { + result = "/arch:AVX" ; + } + else if clang in $(properties) + { + result = "-mavx -mavx2" ; + } + else + { + result = "-mavx -mavx2 -fabi-version=0" ; + } + + return $(result) ; +} + +rule check-instruction-set ( properties * ) +{ + local result ; + local instruction_set = [ log-arch-config.deduce-instruction-set $(properties) ] ; + + if $(instruction_set) = i386 || $(instruction_set) = i486 + { + if ! $(.annouced-failure) + { + ECHO Boost.Log is not supported on the specified target CPU and will not be built. At least i586 class CPU is required. ; + .annouced-failure = 1 ; + } + result = no ; + } + + return $(result) ; +} diff --git a/build/log-architecture.jam b/build/log-architecture.jam deleted file mode 100644 index 785dda0f5c..0000000000 --- a/build/log-architecture.jam +++ /dev/null @@ -1,112 +0,0 @@ -# log-architecture.jam -# -# Copyright 2012 Steven Watanabe -# Copyright 2013 Andrey Semashev -# -# Distributed under the Boost Software License Version 1.0. (See -# accompanying file LICENSE_1_0.txt or copy at -# http://www.boost.org/LICENSE_1_0.txt) - -import configure ; -import project ; -import path ; -import property ; -import feature ; - -local here = [ modules.binding $(__name__) ] ; - -feature.feature log-architecture : : free ; -feature.feature log-address-model : : free ; -feature.feature log-instruction-set : : free ; - -project.push-current [ project.current ] ; -project.load [ path.join [ path.make $(here:D) ] ../../config/checks/architecture ] ; -project.pop-current ; - -rule deduce-address-model ( properties * ) -{ - local address_model = [ feature.get-values "address-model" : $(properties) ] ; - if $(address_model) - { - return $(address_model) ; - } - else - { - if [ configure.builds /boost/architecture//32 : $(properties) : 32-bit ] - { - return 32 ; - } - else if [ configure.builds /boost/architecture//64 : $(properties) : 64-bit ] - { - return 64 ; - } - } -} - -rule address-model ( ) -{ - return @log-architecture.deduce-address-model ; -} - -rule deduce-architecture ( properties * ) -{ - local architecture = [ feature.get-values "architecture" : $(properties) ] ; - if $(architecture) - { - return $(architecture) ; - } - else - { - if [ configure.builds /boost/architecture//x86 : $(properties) : x86 ] - { - return x86 ; - } - else if [ configure.builds /boost/architecture//arm : $(properties) : arm ] - { - return arm ; - } - else if [ configure.builds /boost/architecture//mips1 : $(properties) : mips1 ] - { - return mips1 ; - } - else if [ configure.builds /boost/architecture//power : $(properties) : power ] - { - return power ; - } - else if [ configure.builds /boost/architecture//sparc : $(properties) : sparc ] - { - return sparc ; - } - } -} - -rule architecture ( ) -{ - return @log-architecture.deduce-architecture ; -} - -rule deduce-instruction-set ( properties * ) -{ - local result ; - local instruction_set = [ feature.get-values "instruction-set" : $(properties) ] ; - - if $(instruction_set) - { - result = $(instruction_set) ; - } - else - { - if x86 in $(properties) && 32 in $(properties) - { - # We build for Pentium Pro and later CPUs by default. This is used as the target in many Linux distributions, and Windows and OS X also seem to not support older CPUs. - result = i686 ; - } - } - - return $(result) ; -} - -rule instruction-set ( ) -{ - return @log-architecture.deduce-instruction-set ; -} diff --git a/build/log-build-config.jam b/build/log-build-config.jam new file mode 100644 index 0000000000..a87c11b112 --- /dev/null +++ b/build/log-build-config.jam @@ -0,0 +1,138 @@ +# log-build-config.jam +# +# Copyright 2023-2025 Andrey Semashev +# +# Distributed under the Boost Software License Version 1.0. (See +# accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +import configure ; +import project ; +import path ; +import property ; +import feature ; + +# The rule checks if there is one of the defines listed in `flags` in `properties`. The value of the define is not considered. +rule has-config-flag ( flags + : properties * ) +{ + for local property in $(properties) + { + for local flag in $(flags) + { + if [ MATCH "^($(flag))(=.*)?$" : $(property) ] + { + return 1 ; + } + } + } + + return ; +} + +rule check-regex-header-only ( properties * ) +{ + local result ; + + local has_regex_header_only = [ configure.builds /boost/log/config/regex-header-only//regex_header_only : $(properties) : "Boost.Regex is header-only" ] ; + if ! $(has_regex_header_only) + { + result = /boost/regex//boost_regex ; + } + + return $(result) ; +} + +rule check-atomic-int32 ( properties * ) +{ + local result ; + + local has_atomic_int32 = [ configure.builds /boost/log/config/atomic-int32//atomic_int32 : $(properties) : "native atomic int32 supported" ] ; + if ! $(has_atomic_int32) + { + result = BOOST_LOG_WITHOUT_IPC ; + } + + return $(result) ; +} + +rule check-pthread-mutex-robust ( properties * ) +{ + local result ; + + local has_pthread_mutex_robust = [ configure.builds /boost/log/config/pthread-mutex-robust//pthread_mutex_robust : $(properties) : "pthread supports robust mutexes" ] ; + if $(has_pthread_mutex_robust) + { + result = BOOST_LOG_HAS_PTHREAD_MUTEX_ROBUST ; + } + + return $(result) ; +} + +rule check-native-syslog ( properties * ) +{ + local result ; + + if ! [ has-config-flag BOOST_LOG_WITHOUT_SYSLOG : $(properties) ] + { + local has_native_syslog = [ configure.builds /boost/log/config/native-syslog//native_syslog : $(properties) : "native syslog supported" ] ; + if $(has_native_syslog) + { + result = BOOST_LOG_USE_NATIVE_SYSLOG ; + } + } + + return $(result) ; +} + +rule check-message-compiler ( properties * ) +{ + local result ; + + if windows in $(properties) + { + if ! [ has-config-flag BOOST_LOG_WITHOUT_EVENT_LOG : $(properties) ] + { + local has_mc = [ configure.builds /boost/log/config/message-compiler//test-availability : $(properties) : "has message compiler" ] ; + if ! $(has_mc) + { + result = BOOST_LOG_WITHOUT_EVENT_LOG ; + } + } + else + { + # This branch is needed to fix building with MinGW + result = BOOST_LOG_WITHOUT_EVENT_LOG ; + } + } + else + { + result = BOOST_LOG_WITHOUT_EVENT_LOG ; + } + + return $(result) ; +} + +rule select-regex-backend ( properties * ) +{ + local result ; + + # Use Boost.Regex backend by default. It produces smaller executables and also has the best performance for small string matching. + if ! [ has-config-flag BOOST_LOG_WITHOUT_SETTINGS_PARSERS BOOST_LOG_WITHOUT_DEFAULT_FACTORIES BOOST_LOG_USE_STD_REGEX BOOST_LOG_USE_BOOST_XPRESSIVE : $(properties) ] + { + result = @log-build-config.check-regex-header-only ; + } + + return $(result) ; +} + +rule check-asio ( properties * ) +{ + local result ; + + if ! [ has-config-flag BOOST_LOG_WITHOUT_ASIO : $(properties) ] + { + result = /boost/asio//boost_asio_core ; + } + + return $(result) ; +} diff --git a/build/log-platform-config.jam b/build/log-platform-config.jam index de776c29e2..d83d41f128 100644 --- a/build/log-platform-config.jam +++ b/build/log-platform-config.jam @@ -12,33 +12,38 @@ import path ; import property ; import feature ; -local here = [ modules.binding $(__name__) ] ; - -project.push-current [ project.current ] ; -project.load [ path.join [ path.make $(here:D) ] ../config/xopen-source-600 ] ; -project.pop-current ; - rule set-platform-defines ( properties * ) { - local result = ; + local result ; + + if windows in $(properties) + { + # Disable warnings about using 'insecure' standard C functions. + # These affect MSVC C/C++ library headers, which are used by various compilers. Define them universally on Windows to avoid + # duplicating them for every compiler in every jamfile. + result += _SCL_SECURE_NO_WARNINGS ; + result += _SCL_SECURE_NO_DEPRECATE ; + result += _CRT_SECURE_NO_WARNINGS ; + result += _CRT_SECURE_NO_DEPRECATE ; + } if ( windows in $(properties) ) || ( cygwin in $(properties) ) { result += NOMINMAX ; result += WIN32_LEAN_AND_MEAN ; result += SECURITY_WIN32 ; - result += BOOST_USE_WINDOWS_H ; if cygwin in $(properties) { result += __USE_W32_SOCKETS ; + result += _XOPEN_SOURCE=600 ; } } else if solaris in $(properties) { # Solaris headers are broken and cannot be included in C++03 when _XOPEN_SOURCE=600. At the same time, they cannot be included with _XOPEN_SOURCE=500 in C++11 and later. # This is because the system headers check the C language version and error out if the version does not match. We have to test if we can request _XOPEN_SOURCE=600. - if [ configure.builds /boost/log/xopen-source-600//xopen_source_600 : $(properties) : xopen-source-600-supported ] + if [ configure.builds /boost/log/config/xopen-source-600//xopen_source_600 : $(properties) : xopen-source-600-supported ] { result += _XOPEN_SOURCE=600 ; } diff --git a/cmake/BoostLibraryIncludes.cmake b/cmake/BoostLibraryIncludes.cmake new file mode 100644 index 0000000000..b875bbdba9 --- /dev/null +++ b/cmake/BoostLibraryIncludes.cmake @@ -0,0 +1,37 @@ +# Copyright 2022 Andrey Semashev +# +# Distributed under the Boost Software License, Version 1.0. +# See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt +# +# After including this module, BOOST_LIBRARY_INCLUDES variable is set to the list of include +# directories for all Boost libraries. If the monolithic include directory is found, it is +# used instead. + +if (NOT CMAKE_VERSION VERSION_LESS 3.10) + include_guard() +endif() + +# Generates a list of include paths for all Boost libraries in \a result variable. Uses unified Boost include tree, if available. +function(generate_boost_include_paths result) + if (IS_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/../../../boost" AND EXISTS "${CMAKE_CURRENT_LIST_DIR}/../../../boost/version.hpp") + get_filename_component(include_dir "${CMAKE_CURRENT_LIST_DIR}/../../.." ABSOLUTE) + set(${result} "${include_dir}" PARENT_SCOPE) + return() + endif() + file(GLOB path_list LIST_DIRECTORIES True "${CMAKE_CURRENT_LIST_DIR}/../../../libs/*") + foreach(path IN LISTS path_list) + if (IS_DIRECTORY "${path}/include") + get_filename_component(include_dir "${path}/include" ABSOLUTE) + list(APPEND include_list "${include_dir}") + endif() + endforeach() + set(${result} ${include_list} PARENT_SCOPE) +endfunction() + +if (NOT DEFINED BOOST_LIBRARY_INCLUDES) + generate_boost_include_paths(__BOOST_LIBRARY_INCLUDES) + # Save the paths in a global property to avoid scanning the filesystem if this module is used in multiple libraries + set(BOOST_LIBRARY_INCLUDES ${__BOOST_LIBRARY_INCLUDES} CACHE INTERNAL "List of all Boost library include paths") + unset(__BOOST_LIBRARY_INCLUDES) + # message(STATUS "Boost library includes: ${BOOST_LIBRARY_INCLUDES}") +endif() diff --git a/config/atomic-int32/Jamfile.jam b/config/atomic-int32/Jamfile.jam index 5b08d8d783..bc20c0d02b 100644 --- a/config/atomic-int32/Jamfile.jam +++ b/config/atomic-int32/Jamfile.jam @@ -6,14 +6,15 @@ # import project ; -import log-platform-config ; +import ../../build/log-platform-config ; -project /boost/log/atomic-int32 +project : source-location . : requirements @log-platform-config.set-platform-defines - + /boost/atomic//boost_atomic off ; obj atomic_int32 : atomic_int32.cpp ; +explicit atomic_int32 ; diff --git a/config/atomic-int32/atomic_int32.cpp b/config/atomic-int32/atomic_int32.cpp index f39bcc8f5b..6f3285f6d0 100644 --- a/config/atomic-int32/atomic_int32.cpp +++ b/config/atomic-int32/atomic_int32.cpp @@ -5,9 +5,9 @@ * http://www.boost.org/LICENSE_1_0.txt) */ -#include +#include -#if !defined(BOOST_ATOMIC_INT32_LOCK_FREE) || (BOOST_ATOMIC_INT32_LOCK_FREE+0) != 2 +#if !defined(BOOST_ATOMIC_INT32_LOCK_FREE) || (BOOST_ATOMIC_INT32_LOCK_FREE != 2) #error Boost.Log: Native 32-bit atomic operations are required but not supported by Boost.Atomic on the target platform #endif diff --git a/config/build.jam b/config/build.jam new file mode 100644 index 0000000000..551de845f6 --- /dev/null +++ b/config/build.jam @@ -0,0 +1,14 @@ +# Copyright René Ferdinand Rivera Morell +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +import-search /boost/predef/tools/check ; +import predef ; + +project + : requirements + # Set these to computed values according to the build. + [ predef.address-model ] + [ predef.architecture ] + ; diff --git a/config/message-compiler/Jamfile.jam b/config/message-compiler/Jamfile.jam index 37d98d107c..5eea0a8bab 100644 --- a/config/message-compiler/Jamfile.jam +++ b/config/message-compiler/Jamfile.jam @@ -6,10 +6,10 @@ # import project ; -import log-platform-config ; +import ../../build/log-platform-config ; using mc ; -project /boost/log/message-compiler +project : source-location ../../src : requirements @log-platform-config.set-platform-defines @@ -17,6 +17,8 @@ project /boost/log/message-compiler ; obj simple_event_log : windows/simple_event_log.mc ; +explicit simple_event_log ; # This test target verifies that Message Compiler (mc) is available and supported by the current toolset alias test-availability : simple_event_log ; +explicit test-availability ; diff --git a/config/native-syslog/Jamfile.jam b/config/native-syslog/Jamfile.jam index fa5dfcd0aa..fd4e29714d 100644 --- a/config/native-syslog/Jamfile.jam +++ b/config/native-syslog/Jamfile.jam @@ -6,14 +6,14 @@ # import project ; -import log-platform-config ; +import ../../build/log-platform-config ; -project /boost/log/native-syslog +project : source-location . : requirements @log-platform-config.set-platform-defines - off ; obj native_syslog : native_syslog.cpp ; +explicit native_syslog ; diff --git a/config/pthread-mutex-robust/Jamfile.jam b/config/pthread-mutex-robust/Jamfile.jam index 6082e66e1f..18c7f2de52 100644 --- a/config/pthread-mutex-robust/Jamfile.jam +++ b/config/pthread-mutex-robust/Jamfile.jam @@ -6,14 +6,14 @@ # import project ; -import log-platform-config ; +import ../../build/log-platform-config ; -project /boost/log/pthread-mutex-robust +project : source-location . : requirements @log-platform-config.set-platform-defines - off ; obj pthread_mutex_robust : pthread_mutex_robust.cpp ; +explicit pthread_mutex_robust ; diff --git a/config/regex-header-only/Jamfile.jam b/config/regex-header-only/Jamfile.jam new file mode 100644 index 0000000000..78e78a2020 --- /dev/null +++ b/config/regex-header-only/Jamfile.jam @@ -0,0 +1,20 @@ +# +# Copyright Andrey Semashev 2023. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) +# + +import project ; +import ../../build/log-platform-config ; + +project + : source-location . + : requirements + @log-platform-config.set-platform-defines + /boost/regex//boost_regex + off + ; + +obj regex_header_only : regex_header_only.cpp ; +explicit regex_header_only ; diff --git a/config/regex-header-only/regex_header_only.cpp b/config/regex-header-only/regex_header_only.cpp new file mode 100644 index 0000000000..d584227708 --- /dev/null +++ b/config/regex-header-only/regex_header_only.cpp @@ -0,0 +1,17 @@ +/* + * Copyright Andrey Semashev 2023. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ + +#include + +#if defined(BOOST_REGEX_CXX03) +#error Boost.Log: Boost.Regex is in C++03 mode and is not header-only +#endif + +int main(int, char*[]) +{ + return 0; +} diff --git a/config/x86-ext/Jamfile.jam b/config/x86-ext/Jamfile.jam index 0e9695aae4..a010067e76 100644 --- a/config/x86-ext/Jamfile.jam +++ b/config/x86-ext/Jamfile.jam @@ -6,29 +6,16 @@ # import project ; +import ../../build/log-arch-config ; -project /boost/log/x86-extensions +project : source-location . : requirements off ; -obj ssse3 : ssse3.cpp - : - gcc:"-msse -msse2 -msse3 -mssse3" - clang:"-msse -msse2 -msse3 -mssse3" - intel-linux:"-xSSSE3" - intel-darwin:"-xSSSE3" - intel-win:"/QxSSSE3" - ; - -obj avx2 : avx2.cpp - : - gcc:"-mavx -mavx2 -fabi-version=0" - clang:"-mavx -mavx2" - intel-linux:"-xCORE-AVX2 -fabi-version=0" - intel-darwin:"-xCORE-AVX2 -fabi-version=0" - intel-win:"/arch:CORE-AVX2" - msvc:"/arch:AVX" - ; +obj ssse3 : ssse3.cpp : @log-arch-config.ssse3-flags ; +explicit ssse3 ; +obj avx2 : avx2.cpp : @log-arch-config.avx2-flags ; +explicit avx2 ; diff --git a/config/x86-ext/avx2.cpp b/config/x86-ext/avx2.cpp index fcc1b9215e..56111d90dd 100644 --- a/config/x86-ext/avx2.cpp +++ b/config/x86-ext/avx2.cpp @@ -6,11 +6,14 @@ */ #include +#include int main(int, char*[]) { __m256i mm = _mm256_setzero_si256(); + fread(&mm, 1u, sizeof(mm), stdin); mm = _mm256_shuffle_epi8(_mm256_alignr_epi8(mm, mm, 10), mm); + fwrite(&mm, 1u, sizeof(mm), stdout); _mm256_zeroupper(); return 0; } diff --git a/config/x86-ext/ssse3.cpp b/config/x86-ext/ssse3.cpp index f7bfb30847..f577f4e060 100644 --- a/config/x86-ext/ssse3.cpp +++ b/config/x86-ext/ssse3.cpp @@ -6,10 +6,13 @@ */ #include +#include int main(int, char*[]) { __m128i mm = _mm_setzero_si128(); + fread(&mm, 1u, sizeof(mm), stdin); mm = _mm_shuffle_epi8(_mm_alignr_epi8(mm, mm, 10), mm); + fwrite(&mm, 1u, sizeof(mm), stdout); return 0; } diff --git a/config/xopen-source-600/Jamfile.jam b/config/xopen-source-600/Jamfile.jam index 0d744dcfd7..b35fa46523 100644 --- a/config/xopen-source-600/Jamfile.jam +++ b/config/xopen-source-600/Jamfile.jam @@ -7,10 +7,11 @@ import project ; -project /boost/log/xopen-source-600 +project : source-location . : requirements off ; obj xopen_source_600 : xopen_source_600.cpp ; +explicit xopen_source_600 ; diff --git a/doc/Jamfile.v2 b/doc/Jamfile.v2 index c2837a7c86..46bd2bdb8d 100644 --- a/doc/Jamfile.v2 +++ b/doc/Jamfile.v2 @@ -48,6 +48,7 @@ local doxygen_params = BOOST_LOG_NO_VTABLE= \\ BOOST_SYMBOL_VISIBLE= \\ BOOST_FORCEINLINE=inline \\ + BOOST_INLINE_VARIABLE=inline \\ BOOST_STATIC_ASSERT(x)= \\ BOOST_STATIC_ASSERT_MSG(x,y)= \\ BOOST_STATIC_CONSTANT(x,y)=\"static constexpr x y\" \\ @@ -85,57 +86,57 @@ local doxygen_params = local top_level_includes = [ glob - ../../../boost/log/*.hpp + ../include/boost/log/*.hpp ] ; local core_includes = [ glob - ../../../boost/log/core/*.hpp + ../include/boost/log/core/*.hpp ] ; local attributes_includes = [ glob - ../../../boost/log/attributes/*.hpp + ../include/boost/log/attributes/*.hpp ] ; local expressions_includes = [ glob - ../../../boost/log/expressions/*.hpp - ../../../boost/log/expressions/predicates/*.hpp - ../../../boost/log/expressions/formatters/*.hpp + ../include/boost/log/expressions/*.hpp + ../include/boost/log/expressions/predicates/*.hpp + ../include/boost/log/expressions/formatters/*.hpp ] ; local sources_includes = [ glob - ../../../boost/log/sources/*.hpp + ../include/boost/log/sources/*.hpp ] ; local sinks_includes = [ set.difference # Document all these files... [ glob - ../../../boost/log/sinks/*.hpp + ../include/boost/log/sinks/*.hpp ] : # ...except these [ glob - ../../../boost/log/sinks/nt6_event_log*.hpp + ../include/boost/log/sinks/nt6_event_log*.hpp ] ] ; local utility_includes = [ glob - ../../../boost/log/utility/*.hpp - ../../../boost/log/utility/ipc/*.hpp - ../../../boost/log/utility/setup/*.hpp - ../../../boost/log/utility/type_dispatch/*.hpp - ../../../boost/log/utility/functional/*.hpp - ../../../boost/log/utility/manipulators/*.hpp + ../include/boost/log/utility/*.hpp + ../include/boost/log/utility/ipc/*.hpp + ../include/boost/log/utility/setup/*.hpp + ../include/boost/log/utility/type_dispatch/*.hpp + ../include/boost/log/utility/functional/*.hpp + ../include/boost/log/utility/manipulators/*.hpp ] ; local support_includes = [ glob - ../../../boost/log/support/*.hpp + ../include/boost/log/support/*.hpp ] ; @@ -150,7 +151,6 @@ rule gen-references ( target : source : properties * ) } actions gen-references { -# echo "*** Executing " $(NAME:E=xsltproc) -o "$(TARGET)" "$(STYLESHEET)" "$(>)" $(NAME:E=xsltproc) -o "$(TARGET)" "$(STYLESHEET)" "$(>)" } @@ -262,16 +262,18 @@ boostbook log : log_doc : - html/images/log - boost.root=../../../.. - boost.libraries=../../../libs/libraries.htm - nav.layout=none - boost.image=Boost - navig.graphics=1 - chunk.section.depth=2 - boost.compact.function=0 - pdf:boost.url.prefix=http://www.boost.org/doc/libs/release/libs/log/doc/html - pdf:img.src.path=$(images_location)/ + "html/images/log" + "boost.root=../../../.." + "boost.libraries=../../../libs/libraries.htm" + "nav.layout=none" + "boost.image=Boost" + "navig.graphics=1" + "toc.max.depth=2" + "toc.section.depth=2" + "chunk.section.depth=2" + "boost.compact.function=0" + pdf:"boost.url.prefix=http://www.boost.org/doc/libs/release/libs/log/doc/html" + pdf:"img.src.path=$(images_location)/" ; install html/images/log : [ glob *.png ] ; diff --git a/doc/attributes.qbk b/doc/attributes.qbk index 921a5895d7..96a5df8e53 100644 --- a/doc/attributes.qbk +++ b/doc/attributes.qbk @@ -1,5 +1,5 @@ [/ - Copyright Andrey Semashev 2007 - 2015. + Copyright Andrey Semashev 2007 - 2024. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -13,7 +13,9 @@ #include <``[boost_log_attributes_attribute_cast_hpp]``> #include <``[boost_log_attributes_attribute_value_hpp]``> -All attributes in the library are implemented using the [@http://c2.com/cgi/wiki?PimplIdiom pimpl idiom], or more specifically - shared pimpl idiom. Every attribute provides an interface class which derives from the [class_log_attribute] class and an implementation class that derives from [class_log_attribute_impl]. The interface class only holds a reference counted pointer to the actual implementation of the attribute; this pointer is a member of the [class_log_attribute] class, so derived interface classes don't have any data members. When the interface class is default constructed, it creates the corresponding implementation object and initializes the [class_log_attribute] base class with a pointer to the implementation. Therefore the pimpl nature of attributes is transparent for users in a typical workflow. +As described in the [link log.design Design overview], attributes in Boost.Log represent information to be associated with log records. An attribute is essentially a function that produces an attribute value upon being invoked. The function may produce a different value each time it is called, or always produce the same value on every call. For example, the [link log.detailed.attributes.clock `local_clock`] attribute produces timestamp values according to the local wall time clock, and [link log.detailed.attributes.constant `constant`] always produces the same value every time. + +All attributes in the library are implemented using the [@https://en.cppreference.com/w/cpp/language/pimpl pimpl idiom], or more specifically - shared pimpl idiom. Every attribute provides an interface class which derives from the [class_log_attribute] class and an implementation class that derives from [class_log_attribute_impl]. The interface class only holds a reference counted pointer to the actual implementation of the attribute; this pointer is a member of the [class_log_attribute] class, so derived interface classes don't have any data members. When the interface class is default constructed, it creates the corresponding implementation object and initializes the [class_log_attribute] base class with a pointer to the implementation. Therefore the pimpl nature of attributes is transparent for users in a typical workflow. The shared pimpl design comes significant in a few cases though. One such case is copying the attribute. The copy operation is shallow, so multiple interface objects may refer to a single implementation object. There is no way to deep copy an attribute. Another case is default construction of [class_log_attribute] which creates an empty object that does not refer to an implementation. Attributes in such empty state should not be passed to the library but can be useful in some cases, e.g. when a delayed variable initialization is needed. @@ -24,9 +26,9 @@ It is possible to upcast the attribute interface from [class_log_attribute] to t In this example, the cast will succeed (i.e. the `const_attr` will be non-empty) if the attribute `attr` was originally created as `attrs::constant< int >`. Since all data is stored in the implementation object, no data is lost in the casting process. -The main purpose of attributes is to generate attribute values. Values are semantically distinct from the attributes. Such separation allows implementing attributes that can return different values at different time points (like clock-related attributes, for example) and, on the other hand, allows using different values of the same attribute independently. The [class_log_attribute] interface has a method named `get_value` that returns the actual attribute value. Attribute values are also implemented using the shared pimpl approach, the interface class is [class_log_attribute_value] and implementation classes derive from [class_log_attribute_value_impl]. +As it was said, the main purpose of attributes is to generate attribute values. Values are semantically distinct from the attributes. Such separation allows implementing attributes that can return different values at different times and, on the other hand, allows using different values of the same attribute independently. The [class_log_attribute] interface has a method named `get_value` that returns the actual attribute value. Attribute values are also implemented using the shared pimpl approach, the interface class is [class_log_attribute_value] and implementation classes derive from [class_log_attribute_value_impl]. -The attribute value object is mostly intended to store the actual attribute value and implement type dispatching in order to be able to extract the stored value. One should not confuse the attribute value object type and the stored value type. The former is in most cases not needed by users and provides type erasure, but the latter is needed to be able to extract the value. For brevity we call the stored attribute value type simply the attribute value type in this documentation. +The attribute value object is intended to store the actual attribute value and implement type dispatching in order to be able to extract the stored value. One should not confuse the attribute value implementation type and the stored value type. The former is in most cases not important to users and provides type erasure, but the latter is needed to be able to extract the value. In the example above, the `constant< int >` type is the attribute type, its `get_value()` method produces an object of type [class_log_attribute_value], which is a pimpl wrapper for the [classref boost::log::attributes::attribute_value_impl `attribute_value_impl< int >`] implementation type. The actual stored attribute value type here is `int`, which is what the attribute generates, and what is needed by the user in order to be able to extract the value from [class_log_attribute_value]. For brevity we call the stored attribute value type simply the attribute value type in this documentation. [section:constant Constants] @@ -173,6 +175,8 @@ One of the "must-have" features of any logging library is support for attaching BOOST_LOG(lg) << "This record has a time stamp"; } +[note As any other attribute, the value of the wall clock attribute is obtained at the point of the log record creation, not at the point of its processing in a sink. This means that (a) timetamps attached to log records always reflect the time point of the event occurrence, not the time of storing the log record, and (b) if the same log record is processed by multiple sinks, even at different points in time, these sinks will process (e.g. store in their respective files) the same timestamp. This is a useful property of the wall clock attribute, although it may result in [link log.rationale.why_weak_record_ordering weak ordering of log records] in the output.] + [endsect] [section:timer Stop watch (timer)] @@ -266,6 +270,8 @@ After executing `foo` we will be able to see in the log that the `bar` function [note The `BOOST_LOG_FUNCTION` macro uses compiler-specific extensions to generate the scope name from the enclosing function. C++11 defines a standard macro `__func__` for this purpose, but it is not universally supported. Additionally, format of the string is not standardized and may vary from one compiler to another. For this reason it is generally advised to use `BOOST_LOG_NAMED_SCOPE` instead of `BOOST_LOG_FUNCTION` to ensure consistent and portable behavior.] +[warning The `BOOST_LOG_FUNCTION` and `BOOST_LOG_NAMED_SCOPE` macros effectively form a linked list of scope descriptors on the stack, which is automatically maintained as control enters and leaves scopes in the LIFO (last in, first out) order. The attribute implementation relies on this LIFO order. This breaks when execution switches to another stack and back at arbitrary points, which allows control to enter and leave scopes in any order. This is what happens on thread context switching, for example when using coroutines or low level system APIs like [@https://pubs.opengroup.org/onlinepubs/7908799/xsh/ucontext.h.html ucontext] and [@https://docs.microsoft.com/en-us/windows/win32/procthread/fibers fibers]. Using named scopes in code that utilizes such context switching facilities requires careful programming to maintain the LIFO order of entering/leaving scopes marked with named scope macros. Otherwise, using named scopes will result in undefined behavior and should be avoided. Note that with arbitrary context switching the list of scopes would not provide meaningful information anyway.] + Another good use case is attaching the scope stack information to an exception. With the help of __boost_exception__, this is possible: void bar(int x) @@ -362,7 +368,7 @@ This attribute is a simple wrapper around a user-defined function object. Each a logging::core::get()->add_global_attribute("MyRandomAttr", attrs::make_function(&std::rand)); } -Auto-generated function objects, like the ones defined in __boost_bind__ or STL, are also supported. +Auto-generated function objects, like the ones defined in __boost_bind__ or C++ standard library, are also supported. [note Some deficient compilers may not support `result_of` construct properly. This metafunction is used in the `make_function` function to automatically detect the return type of the function object. If `result_of` breaks or detects incorrect type, one can try to explicitly specify the return type of the function object as a template argument to the `make_function` function.] @@ -390,7 +396,7 @@ The [class_log_attribute_name] class supports an empty (uninitialized) state whe #include <``[boost_log_attributes_attribute_set_hpp]``> -Attribute set is an unordered associative container that maps [link log.detailed.attributes.related_components.attribute_name attribute names] to [link log.detailed.attributes attributes]. It is used in [link log.detailed.sources loggers] and the [link log.detailed.core.core logging core] to store source-specific, thread-specific and global attributes. The interface is very similar to STL associative containers and is described in the [class_log_attribute_set] class reference. +Attribute set is an unordered associative container that maps [link log.detailed.attributes.related_components.attribute_name attribute names] to [link log.detailed.attributes attributes]. It is used in [link log.detailed.sources loggers] and the [link log.detailed.core.core logging core] to store source-specific, thread-specific and global attributes. The interface is very similar to standard library associative containers and is described in the [class_log_attribute_set] class reference. [endsect] diff --git a/doc/changelog.qbk b/doc/changelog.qbk index 5f6a7e68ed..c6158b42c3 100644 --- a/doc/changelog.qbk +++ b/doc/changelog.qbk @@ -1,5 +1,5 @@ [/ - Copyright Andrey Semashev 2007 - 2018. + Copyright Andrey Semashev 2007 - 2025. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -9,6 +9,168 @@ [section:changelog Changelog] +[heading 2.34, Boost 1.91] + +* Enabled building interprocess communication support on Cygwin by default. + +[heading 2.33, Boost 1.90] + +* Fixed a missed optimization in `value_ref` visitation. +* Fixed a possible long and useless loop on log file rotation in [link log.detailed.sink_backends.text_file `text_file_backend`], if the log file name pattern did not include a file counter and the log file size exceeded the `rotation_size` limit. The sink backend would repeatedly try to open a new log file with a different counter value and end up opening the same file every time. ([github_issue 252]) +* Made file size checks more robust against integer overflows in [link log.detailed.sink_backends.text_file `text_file_backend`]. + +[heading 2.32, Boost 1.89] + +* Added support for `BOOST_LOG_WITHOUT_ASIO` configuration macro, which can be used to remove the dependency on __boost_asio__ and disable the related functionality. +* Use locale-independent formatting of the file counter in `text_file_backend` when composing log file names. This fixes failures in the subsequent parsing of the file names in `file_collector::scan_for_files`. ([pull_request 246]) +* Added a new [link log.detailed.expressions.predicates.wrap_filter `wrap_filter`] utility that simplifies injecting user-defined function objects in filtering expressions. ([github_issue 195]) + +[heading 2.31, Boost 1.88] + +* Disabled usage of `std::codecvt` and `std::codecvt` locale facets in C++20 and later modes as they were deprecated in C++20. This means character code conversions to/from `char16_t` and `char32_t` is no longer available in C++20 and later. +* Fixed building issues when using CMake and MinGW-w64. ([pull_request 241]) +* Fixed incorrect file counter used by `text_file_backend` when the backend was configured to append to an existing file and the actively written file name pattern didn't have a counter placeholder but the target file name pattern did, and the log files were written directly into the target storage. ([github_issue 245]) + +[heading 2.30, Boost 1.87] + +* Replaced __boost_thread__ synchronization primitives with equivalents from the C++ standard library. This may improve multithreaded performance, but also has user-facing consequences: + * __boost_thread__ thread interruption is no longer supported. Boost.Log no longer has special treatment for the `thread_interrupted` exception that is used by __boost_thread__ to implement thread interruption. This exception will be handled like any other exception. + In particular, user-specified exception handlers may now be invoked with the `thread_interrupted` pending exception. + * For timed waiting operations, timeouts are now using std::chrono time units. This means that the `ordering_window` named parameter that is supported by the [class_sinks_bounded_ordering_queue] and [class_sinks_unbounded_ordering_queue] classes now expects an `std::chrono::duration` value instead of `boost::posix_time::time_duration` from __boost_date_time__. + * In case of errors indicated by thread synchronization primitives, `std::system_error` exception is thrown instead of __boost_thread__ exception types. +* Added support for C++ standard library lock types to [class_log_strictest_lock]. + +[heading 2.29, Boost 1.86] + +* Added a workaround for `windres.exe` issue, when it is used in CMake to compile event log resource files on MinGW-w64. ([pull_request 231]) + +[heading 2.28, Boost 1.84] + +* C++03 is no longer supported. A C++11 or later compiler is required. +* When built with C++11 compilers that are conforming enough for Boost.Regex v5 to be used, Boost.Log no longer links with Boost.Regex prebuilt library, since Boost.Regex v5 is header-only. +* Support for Windows versions older than Windows 10 is deprecated and will be removed in Boost 1.87. + +[heading 2.27, Boost 1.83] + +* Fixed a possible infinite loop in [link log.detailed.sink_backends.text_file `text_file_backend`], when the size of a formatted log record exceeds the `rotation_size` limit. ([github_issue 209]) + +[heading 2.26, Boost 1.82] + +* Due to changes in Boost libraries used by Boost.Log, support for C++03 is deprecated. C++11 will become the minimum starting with Boost.Log 1.84. + +[heading 2.25, Boost 1.80] + +[*Bug fixes:] + +* Fixed binding incorrect local address in UDP socket-based [link log.detailed.sink_backends.syslog `syslog_backend`] when IPv6 address is used for the syslog server. ([github_issue 181]) +* Added a workaround for a [@https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105857 bug] in libstdc++ from gcc 11.2. When [link log.detailed.expressions.formatters.decorators.max_size `max_size_decor`] was used on a formatting stream, `std::codecvt::do_length` incorrectly accessed the input buffer and caused a buffer overflow. + +[heading 2.24, Boost 1.79] + +[*General changes:] + +* In [link log.detailed.sink_backends.text_file `text_file_backend`], added support for appending to a previously written log file, when file rotation is used and log file names use file counters. For this to work, the target storage directory must be the same as the directory where the active log file is written, and `text_file_backend::scan_for_files` must be called prior to emitting any log records to discover the last file counter used in the target storage directory. Previously, in such a configuration the sink backend would generate a new file name instead of appending to the last written one. +* *Breaking change:* The [class_file_collector] interface has changed: + * `scan_for_files` method returns a `scan_result` structure that contains information collected during the scan; + * `is_in_storage` method added for testing if a path refers to a file within the target storage directory. +* Added a new [link log.detailed.utilities.manipulators.invoke `invoke_manip`] stream manipulator that can be used for injecting user's function into a stream output expression. + +[*Bug fixes:] + +* Fixed file counter being set to zero if the user calls [link log.detailed.sink_backends.text_file.file_scanning `text_file_backend::scan_for_files`] multiple times, and the second and the following calls don't find any new files. ([github_issue 179]) + +[heading 2.23, Boost 1.78] + +[*General changes:] + +* Updated detection of `std::codecvt` specializations for `char16_t` and `char32_t` for compatibility with C++17 and later. ([pull_request 159]) +* Added support for C++17 `std::byte` type to receive methods of the [link log.detailed.utilities.ipc.reliable_message_queue inter-process message queue]. +* On Windows, when building the library for Windows 8 or later, the library will use `nt62` tag in the version namespace to denote the target OS ABI. For example, the version namespace could be named as `v2_mt_nt62`. This name will be part of all symbols exported by the library. Use the `BOOST_USE_WINAPI_VERSION` macro consistenly when building Boost and your code to request the minimum target Windows version. +* Improved performance of SSSE3 and AVX2 implementations of the [link log.detailed.utilities.manipulators.dump `dump`] stream manipulator. +* Added support for CMake build system. Only library build is supported at this moment, Boost.Build is still used for running tests. + +[*Bug fixes:] + +* Fixed a race condition on POSIX systems in [link log.detailed.utilities.ipc.reliable_message_queue `reliable_message_queue::open_or_create`] or the equivalent constructor. Multiple threads or processes calling `open_or_create` concurrently with sending or receiving messages from the same queue could end up corrupting the queue contents and potentially crashing the process. ([github_issue 162]) +* Added a workaround for `b2 install` failing on Windows because of missing `boost_log_setup` library if `BOOST_LOG_WITHOUT_SETTINGS_PARSERS` is defined. ([github_issue 164]) + +[heading 2.22, Boost 1.77] + +[*Bug fixes:] + +* Fixed compilation on Linux for riscv32 target. ([pull_request 150]) +* Setting `std::ios_base::app` file open mode in [link log.detailed.sink_backends.text_file `text_file_backend`] now also implies `std::ios_base::ate`. This fixes incorrect accounting for the initial size of the log file opened for appending. ([github_issue 151]) + +[heading 2.21, Boost 1.76] + +[*Bug fixes:] + +* Corrected a formal race condition in the thread-safe log record queue implementation used in the `unbounded_fifo_queue` policy of the [class_sinks_asynchronous_sink] frontend. The race could be detected by TSAN, but it was not a real problem on most current CPU architectures and compilers. ([github_issue 139]) +* When copying a logger with a channel attribute (e.g. `channel_logger`), the channel attribute is now deep-copied from the original logger. This means that modifying the channel in the new logger will no longer affect the original logger. +* Replaced the use of `std::allocator` in template parameters with a new tag type `boost::log::use_std_allocator` to silence libc++ warnings about the former being deprecated in C++17 and later. The tag indicates that the instantiated template should be using a specialization of `std::allocator` internally to allocate dynamic memory, so the change has no functional effect. ([github_issue 141]) +* Boost.Log no longer defines `__MSVCRT_VERSION__` macro on MinGW and MinGW-w64. This macro is used to select the target C runtime library. Defining this macro caused incompatibility with UCRT, which is available as an option in recent MinGW-w64. ([pull_request 149]) + +[heading 2.20, Boost 1.75] + +[*Bug fixes:] + +* Corrected the file counter that would be used in [link log.detailed.sink_backends.text_file `text_file_backend`] when generating the target file name (based on the pattern set by `set_target_file_name_pattern` method) when the log file is rotated. The backend used to use the next value of file counter, which could cause some counter values to be skipped on application restarts. ([github_issue 125]) +* Replaced a volatile version counter in `basic_sink_frontend` with an atomic. This should silence clang-cl warnings about deprecated uses of `volatile`. ([github_issue 128]) +* In the [class_sinks_asynchronous_sink] frontend, resolved a possible conflict between `flush` and `run` methods, if `run` is called from a user's thread instead of the internal dedicated thread spawned by the frontend. `run` and `feed_records` methods will wait for `flush` to complete, if one is in progress. Additionally, `stop` now explicitly doesn't wait for the ongoing record feeding operation to complete. ([github_issue 131]) + +[heading 2.19, Boost 1.74] + +[*Bug fixes:] + +* The [link log.detailed.sink_backends.syslog syslog] sink backend now verifies the IP version of the local and target addresses set by user. The addresses must have the same IP version as was specified in the `ip_version` named parameter on the sink backend construction (by default, IPv4 is assumed). When an address is obtained as a result of host name resolution, only addresses with matching IP version are considered. ([github_issue 119]) + +[*New Features:] + +* Move constructors and assignment operators of various components were marked `noexcept`. +* Added a new [link log.detailed.utilities.manipulators.range `range_manip`] stream manipulator that can be used for outputting elements of a range, optionally separated by a delimiter. +* Added a new [link log.detailed.utilities.manipulators.tuple `tuple_manip`] stream manipulator that can be used for outputting elements of a tuple or any other heterogeneous sequence, optionally separated by a delimiter. +* Added a new [link log.detailed.utilities.manipulators.optional `optional_manip`] stream manipulator that can be used for outputting optionally present values. + +[heading 2.18, Boost 1.73] + +[*New Features:] + +* Default sink used in trivial logging, when no sinks are registered in the logging core, now automatically flushes output after each log record. ([github_issue 103]) +* `core::flush` now performs a flush on the default sink used for trivial logging, when no sinks are registered. + +[*Bug fixes:] + +* Added a workaround for some syslog API implementations (e.g. glibc), which do not save the application identification string in `openlog` call. Such implementations could access already freed memory on each `syslog` call, resulting in undefined behavior. ([github_issue 97]) +* Fixed that log file rotation on a specific day of month (e.g. `rotation_at_time_point(boost::gregorian::greg_day(1))`) could be silently ignored and not happen. ([github_issue 98]) +* Fixed that [link log.detailed.sink_backends.text_file `text_file_backend`]`::rotate_file` could throw if there were no log records written yet and target file name pattern was set. The method would attempt to rename a missing file, which would result in an exception. Note that `rotate_file` may still throw because of a missing log file, if the file being rotated is removed by a third party. +* Ported various components of the library to `std::allocator_traits` to improve compatibility with C++20 allocators. +* Fixed compilation errors when building in MSYS2 Cygwin environment. ([github_issue 102]) + +[heading 2.17, Boost 1.71] + +[*New features:] + +* Improved support for C++17 `std::string_view` in [class_log_basic_formatting_ostream]. The string view can now participate in character code conversion on output. +* Added `auto_newline` [link log.detailed.expressions.formatters.auto_newline formatter] and [link log.detailed.utilities.manipulators.auto_newline stream manipulator]. It can be used to ensure that formatted output always ends with a newline while avoiding duplicate newlines. +* In the [link log.detailed.sink_backends.text_ostream output stream], [link log.detailed.sink_backends.text_file text file] and [link log.detailed.sink_backends.text_multifile text multi-file] sink backends added support for configuring behavior with regard to appending a trailing newline to every formatted log record. The user can specify a value of the [enumref boost::log::sinks::auto_newline_mode `auto_newline_mode`] enum either in the `auto_newline_mode` named parameter of the backend constructor or by calling the `set_auto_newline_mode` method on the backend post-construction. The `auto_newline_mode` named parameter is also supported in the [link log.detailed.utilities.setup.convenience convenience functions] for initializing sinks. When initializing from [link log.detailed.utilities.setup.settings settings] or a config file, the behavior is controlled by the new "AutoNewline" parameter of the sink. + +[note The default behavior with regard to trailing newlines has changed slightly compared to the previous Boost.Log releases. The backends will now only add a trailing newline if there isn't one in the formatted log message string already. In previous releases a newline was added unconditionally. In general, users are advised to configure their formatters so that the trailing newline is added there, if needed (e.g. by using [link log.detailed.expressions.formatters.auto_newline `auto_newline`] formatter), and disable the automatic trailing newline insertion in the sink backends. This feature mostly exists for backward compatibility and can be considered deprecated.] + +* [link log.detailed.sink_backends.text_ostream Output stream] and [link log.detailed.sink_backends.text_multifile text multi-file] sink backends can now be constructed with named parameters. +* Added support for passing arbitrary function objects (as opposed to only __boost_phoenix__ function objects, which were supported before) in the `filter` and `format` named parameters to sink constructors and [link log.detailed.utilities.setup.convenience convenience functions] for initializing sinks. For example, it is now possible to specify C++11 lambda functions directly in these parameters. ([github_issue 63]) +* In the default filter and formatter factories used in [link log.detailed.utilities.setup.filter_formatter filter and formatter parsers], added support for severity level attribute values of type [enumref boost::log::trivial::severity_level `boost::log::trivial::severity_level`]. For filters, the support is limited to attributes with "Severity" name. This relieves the user from having to register filter and formatter factories in order to use `boost::log::trivial::severity_level` with filters and formatters parsed from strings or [link log.detailed.utilities.setup.settings settings]. Note that any other custom severity level enums must still be [link log.extension.settings registered], as before. + +[*Bug fixes:] + +* Fixed incorrect parsing of components of the rotated file names while scanning for files in the [link log.detailed.sink_backends.text_file text file sink backend]. If the file name pattern ended with a placeholder (for example, a file counter), the `scan_for_files` method would not find files matching that pattern in the target storage, leaving them unmanaged. In particular, such files would not be deleted to free target storage. ([github_issue 78]) +* Updated [class_log_basic_formatting_ostream] and [class_log_basic_record_ostream] to make it possible to overload stream output operators for pointers to user-defined types. User-defined `operator<<` overloads taking `std::basic_ostream` and a pointer argument should now be picked up by the compiler when the pointer is being written to one of Boost.Log streams. ([github_issue 84]) + +[heading 2.16, Boost 1.70] + +[*New features:] + +* Added support for generating another log file name before collecting the file in the [link log.detailed.sink_backends.text_file text file sink backend]. This allows to combine [link log.detailed.sink_backends.text_file.appending appending] to an existing log file with timestamps and file counters in log filenames, and, consequently, [link log.detailed.sink_backends.text_file.file_collection file collection] in general. To enable this feature, one must set the target file name pattern in the text file sink backend (using the `target_file_name` named parameter, `text_file_backend::set_target_file_name_pattern` method call or "TargetFileName" sink parameter in the [link log.detailed.utilities.setup.settings settings]). This pattern will be used to generate a new file name when the file is finished writing and is about to be collected. Therefore, the original (active) file name can be set to a stable pattern (e.g. "app.log") so that appending to a previously written file works. Then the target file name can include a timestamp or a counter (e.g. "app-2019-01-05.log"), so that different rotated files don't conflict in the target storage. + [heading 2.15, Boost 1.69] [*General changes:] diff --git a/doc/core.qbk b/doc/core.qbk index 160c4e739d..b28b96ce0e 100644 --- a/doc/core.qbk +++ b/doc/core.qbk @@ -1,5 +1,5 @@ [/ - Copyright Andrey Semashev 2007 - 2015. + Copyright Andrey Semashev 2007 - 2022. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/doc/design.qbk b/doc/design.qbk index 2371c11ecf..ac9d1b7978 100644 --- a/doc/design.qbk +++ b/doc/design.qbk @@ -1,5 +1,5 @@ [/ - Copyright Andrey Semashev 2007 - 2015. + Copyright Andrey Semashev 2007 - 2024. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -35,13 +35,13 @@ There are three kinds of attribute sets: You can see in the figure that the former two sets are maintained by the logging core and thus need not be passed by the log source in order to initiate logging. Attributes that participate in the global attribute set are attached to any log record ever made. Obviously, thread-specific attributes are attached only to the records made from the thread in which they were registered in the set. The source-specific attribute set is maintained by the source that initiates logging, these attributes are attached only to the records being made through that particular source. -When a source initiates logging, attribute values are acquired from attributes of all three attribute sets. These attribute values then form a single set of named attribute values, which is processed further. You can add more attribute values to the set; these values will only be attached to the particular log record and will not be associated with the logging source or logging core. As you may notice, it is possible for a same-named attribute to appear in several attribute sets. Such conflicts are solved on priority basis: global attributes have the least priority, source-specific attributes have the highest; the lower priority attributes are discarded from consideration in case of conflicts. +When a source initiates logging, attribute values are acquired from attributes of all three attribute sets. These attribute values then form a single set of named attribute values, which is processed further. You can add more attribute values to the set; these values will only be attached to the particular log record and will not be associated with the logging source or logging core. As you may notice, it is possible for a same-named attribute to appear in several attribute sets. Such conflicts are solved on precedence basis: global attributes have the least precedence, source-specific attributes have the highest; the lower precedence attributes are discarded from consideration in case of conflicts. [heading Logging core and filtering] When the set of attribute values is composed, the logging core decides if this log record is going to be processed in sinks. This is called filtering. There are two layers of filtering available: the global filtering is applied first within the logging core itself and allows quickly wiping away unneeded log records; the sink-specific filtering is applied second, for each sink separately. The sink-specific filtering allows directing log records to particular sinks. Note that at this point it is not significant which logging source emitted the record, the filtering relies solely on the set of attribute values attached to the record. -It must be mentioned that for a given log record filtering is performed only once. Obviously, only those attribute values attached to the record before filtering starts can participate in filtering. Some attribute values, like log record message, are typically attached to the record after the filtering is done; such values cannot be used in filters, they can only be used by formatters and sinks themselves. +It must be mentioned that for a given log record filtering is performed only once. Obviously, only the attribute values that were attached to the record before filtering starts can participate in filtering. Some attribute values, like log record message, are typically attached to the record after the filtering is done; such values cannot be used in filters, they can only be used by formatters and sinks themselves. [heading Sinks and formatting] diff --git a/doc/expressions.qbk b/doc/expressions.qbk index 412ffbc16f..73db59582b 100644 --- a/doc/expressions.qbk +++ b/doc/expressions.qbk @@ -1,5 +1,5 @@ [/ - Copyright Andrey Semashev 2007 - 2016. + Copyright Andrey Semashev 2007 - 2025. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -146,7 +146,7 @@ The `message` keyword has to dispatch between different string types, so it is s [section:predicates Predicate expressions] -This section describes several expressions that can be used as predicates in the filtering expressions. +This section describes several expressions that can be used as predicates in filtering expressions. [section:has_attr Attribute presence filter] @@ -262,6 +262,42 @@ This filter is implemented for Windows only. The `is_debugger_present` filter re [endsect] +[section:wrap_filter Injecting user-defined functions in filtering expressions] + + #include <``[boost_log_expressions_predicates_wrap_filter_hpp]``> + +Users may want to combine their custom filtering function objects with the filters provided by Boost.Log. In order to be able to integrate the user-defined function into a filtering expression, the function must be adapted as a __boost_phoenix__ function object. One way to do this is to use one of the __boost_phoenix_adapt__ macros provided by __boost_phoenix__ to create a lazy function for the user's filtering function. For example: + +[example_expressions_filter_adapt_function] + +[@boost:/libs/log/example/doc/expressions_filter_adapt_function.cpp See the complete code]. + +Here, using the `BOOST_PHOENIX_ADAPT_FUNCTION` macro we created a `lazy::my_filter` factory function that, when called, creates a function object that is compatible with __boost_phoenix__ template expressions. This created function object becomes part of the filtering expression and, when invoked during log record filtering, will forward the call to the wrapped `my_filter` function defined by the user. The `_1` placeholder in the filtering expression designates the set of attribute values that will be forwarded to the `my_filter` function. The function must return `true` to indicate that the log record passed the filter and `false` otherwise. + +[tip It is also possible to use __boost_phoenix_bind__ instead of the `BOOST_PHOENIX_ADAPT_*` macros. `boost::phoenix::bind` also creates a function object that can integrate with __boost_phoenix__ expressions: + +``` +logging::add_console_log +( + std::clog, + keywords::filter = + severity >= critical || boost::phoenix::bind(&my_filter, boost::phoenix::placeholders::_1), + keywords::format = + expr::stream << "<" << severity << "> [" << channel << "] " << expr::smessage +); +``` +] + +However, Boost.Log provides a dedicated [funcref boost::log::expressions::wrap_filter `wrap_filter`] utility that removes the need to create a factory function or specify argument placeholders. The function wraps user-defined function object and enables it to participate in filtering expressions. The above example can be simplified as follows: + +[example_expressions_wrap_filter] + +[@boost:/libs/log/example/doc/expressions_wrap_filter.cpp See the complete code]. + +Note that `wrap_filter` allows for wrapping arbitrary function objects, including C++11 lambda functions, as long as the wrapped function object is callable with a single argument of type `attribute_value_set` and returns a value convertible to `bool`. + +[endsect] + [endsect] [section:formatters Formatting expressions] @@ -272,7 +308,7 @@ As was noted in the [link log.tutorial.formatters tutorial], the library provide sink->set_formatter(expr::stream << expr1 << expr2 << ... << exprN); -Here expressions `expr1` through `exprN` may be either manipulators, described in this section, or other expressions resulting in an object that supports putting into an STL-stream. +Here expressions `expr1` through `exprN` may be either manipulators, described in this section, or other expressions resulting in an object that supports putting into a standard library output stream. To use __boost_format__-style syntax one should use `format` construct: @@ -474,6 +510,22 @@ Those familiar with __boost_phoenix__ lambda expressions will find this syntax q [endsect] +[section:auto_newline Automatic newline insertion] + + #include <``[boost_log_expressions_formatters_auto_newline_hpp]``> + +This is an adaptation of the [link log.detailed.utilities.manipulators.auto_newline `auto_newline` manipulator] for formatter expressions. The `auto_newline` formatter can be useful, for example, if log messages generated by one source are terminated with a newline character (and that behavior cannot be changed easily), and other messages are not. The formatter will ensure that all messages are reliably terminated with a newline and there are no duplicate newline characters. Like the manipulator, it will insert a newline unless the last character inserted into the stream before it was a newline. For example: + + sink->set_formatter + ( + expr::stream + // Ensure that the sink outputs one message per line, + // regardless whether the message itself ends with a newline or not + << expr::message << expr::auto_newline + ); + +[endsect] + [section:decorators Character decorators] There are times when one would like to additionally post-process the composed string before passing it to the sink backend. For example, in order to store log into an XML file the formatted log record should be checked for special characters that have a special meaning in XML documents. This is where decorators step in. @@ -655,6 +707,50 @@ The above formatter can produce output like this: [endsect] +[section:wrap_formatter Injecting user-defined functions in formatting expressions] + + #include <``[boost_log_expressions_formatters_wrap_formatter_hpp]``> + +Given that formatting expressions are __boost_phoenix__ expressions, it is possible to inject user-defined functions into the formatter by using __boost_phoenix__ facilities, such as __boost_phoenix_adapt__ macros or __boost_phoenix_bind__. + +[example_expressions_formatter_adapt_function] + +[@boost:/libs/log/example/doc/expressions_formatter_adapt_function.cpp See the complete code]. + +Here, using the `BOOST_PHOENIX_ADAPT_FUNCTION` macro we created a `lazy::extract_channel` factory function that, when called, creates a function object that is compatible with __boost_phoenix__ template expressions. This function object, when called as part of the formatter, forwards the call to our `extract_channel` function to do the actual work. + +Remember that formatters have the following signature: + + void (logging::``[class_log_record_view]`` const& rec, logging::``[class_log_basic_formatting_ostream]``< CharT >& stream) const + +Here, `rec` is a log [link log.detailed.core.record record view] that contains all captured attribute values and `stream` is the output stream that receives the formatted output. In order to pass the log record to the `extract_channel` function we use the `_1` placeholder to identify the first argument of the formatter. + +The `extract_channel` function accepts a log record view and returns the object that will be output into the stream as part of the formatting expression. In our case, the returned string is simply the value of the "Channel" attribute from the log record, if one is present, or a replacement string if it is missing. If needed, the output stream will perform character encoding conversion as it receives the string returned by `extract_channel`. + +[tip In order to use `boost::phoenix::bind` instead of the `BOOST_PHOENIX_ADAPT_*` macros one could write the formatting expression as follows: + +``` +logging::add_console_log +( + std::clog, + keywords::format = + expr::stream << "<" << severity << "> [" + << boost::phoenix::bind(&extract_channel, boost::phoenix::placeholders::_1) + << "] " << expr::smessage +); +``` +] + +The protocol of returning an object to output in the formatting stream, while simple and expressive, may be too limiting or inefficient when a more elaborate formatting logic is required. To address these advanced use cases, Boost.Log provides a [funcref boost::log::expressions::wrap_formatter `wrap_formatter`] utility that wraps a user-defined formatter function and invokes it as part of the formatting process. The user-defined formatter can be any callable that supports the signature defined above, and as such doesn't need to return anything and can use the provided stream directly for output. + +[example_expressions_wrap_formatter] + +[@boost:/libs/log/example/doc/expressions_wrap_formatter.cpp See the complete code]. + +In this example, the application uses a "Host" attribute with values of a custom `host_address` type to identify network hosts to which the application connects. The application may emit log records that are related or unrelated to a given host, as indicated by the presence of the "Host" attribute. We define our `format_host` function to implement the entire formatting logic for host addresses and then use the `wrap_formatter` utility to inject it into the formatting expression. Note that despite that the syntax of the formatting expression suggests that the value returned by `wrap_formatter` is output into the formatting stream, this is not actually the case. + +[endsect] + [endsect] [endsect] diff --git a/doc/extension.qbk b/doc/extension.qbk index 6ac92bd623..c488dde9bf 100644 --- a/doc/extension.qbk +++ b/doc/extension.qbk @@ -1,5 +1,5 @@ [/ - Copyright Andrey Semashev 2007 - 2015. + Copyright Andrey Semashev 2007 - 2022. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/doc/gen_references.xsl b/doc/gen_references.xsl index 9b195eb81f..f95ac1dbcb 100644 --- a/doc/gen_references.xsl +++ b/doc/gen_references.xsl @@ -13,7 +13,7 @@ [/ - Copyright Andrey Semashev 2007 - 2017. + Copyright Andrey Semashev 2007 - 2020. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/doc/install.qbk b/doc/install.qbk new file mode 100644 index 0000000000..522c3dd660 --- /dev/null +++ b/doc/install.qbk @@ -0,0 +1,139 @@ +[/ + Copyright Andrey Semashev 2024. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) + + This document is a part of Boost.Log library documentation. +/] + +[section:installation Installation and compatibility] + +[section:supported_compilers Supported compilers and platforms] + +The library should build and work with a reasonably compliant C++11 compiler. The library was successfully built and tested on the following platforms: + +* Windows 10. MSVC 14.0 and newer. MinGW32 with gcc 5.x and MinGW-w64 with gcc 6.x and newer. +* Cygwin and Cygwin64 with gcc 7.x and newer. +* Linux. GCC 5.x and newer. +* Linux. Clang 3.5 and newer. + +The following compilers/platforms are not supported and will likely fail to compile the library: + +* Compilers that do not support C++11. +* C++11 compilers with non-C++11 standard libraries (like Clang with libstdc++ from GCC 4.2). Please, use a C++11 standard library in C++11 mode. +* MSVC 12.0 and older. +* GCC 4.4 and older. +* Borland C++ 5.5.1 (free version). Newer versions might or might not work. +* Solaris Studio 12.3 and older. +* Windows 9x, ME, NT4, 2000 and older are not supported. + +Boost.Log should be compatible with all hardware architectures supported by Boost. However, in case of 32 bit x86 architecture the library requires at least i586 class CPU to run. + +[heading Notes for GCC users] + +GCC versions since 4.5 support link time optimization (LTO), when most of optimizations and binary code generation happen at linking stage. This allows to perform more advanced optimizations and produce faster code. Unfortunately, it does not play well with projects containing source files that need to be compiled with different compiler options. Boost.Log is one of such projects, some parts of its sources contain optimizations for modern CPUs and will not run on older CPUs. Enabling LTO for Boost.Log will produce binaries incompatible with older CPUs (GCC bugs [@https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61043 61043], [@https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77845 77845]), which will likely result in crashes at run time. For this reason LTO is not supported in Boost.Log. + +There is a GCC [@http://gcc.gnu.org/bugzilla/show_bug.cgi?id=60607 bug] which may cause compilation failures when `-march=native` command line argument is used. It is recommended to avoid using `-march=native` argument (or `instruction-set=native` b2 property) and instead explicitly specify the target CPU (e.g. `instruction-set=sandy-bridge`). + +[heading Notes for MinGW, Cygwin and Visual Studio Express Edition users] + +In order to compile the library with these compilers special preparations are needed. First, in case of MinGW or Cygwin make sure you have installed the latest GCC version. The library will most likely fail to compile with GCC 3.x. + +Second, at some point the library will require a Message Compiler tool (`mc.exe`), which is not available in MinGW, Cygwin and some versions of MSVC Express Edition. Typically the library build scripts will automatically detect if message compiler is present on the system and disable Event log related portion of the library if it's not. If Event log support is required and it is not found on the system, you have three options to settle the problem. The recommended solution is to obtain the original `mc.exe`. This tool is available in Windows SDK, which can be downloaded from the Microsoft site freely (for example, [@http://www.microsoft.com/downloads/details.aspx?FamilyID=71deb800-c591-4f97-a900-bea146e4fae1&displaylang=en here]). Also, this tool should be available in Visual Studio 2010 Express Edition. During the compilation, `mc.exe` should be accessible through one of the directories in your `PATH` environment variable. + +Another way is to attempt to use the `windmc.exe` tool distributed with MinGW and Cygwin, which is the analogue of the original `mc.exe`. In order to do that you will have to patch Boost.Build files (in particular, the `tools/build/tools/mc.jam` file) as described in [@https://svn.boost.org/trac/boost/ticket/4111 this] ticket. After that you will be able to specify the `mc-compiler=windmc` option to b2 to build the library. + +In case if message compiler detection fails for some reason, you can explicitly disable support for event log backend by defining the `BOOST_LOG_WITHOUT_EVENT_LOG` configuration macro when building the library. This will remove the need for the message compiler. See [link log.installation.config this section] for more configuration options. + +MinGW users on Windows XP may be affected by the [@http://sourceforge.net/p/mingw-w64/bugs/307/ bug] in msvcrt.dll that is bundled with the operating system. The bug manifests itself as crashes while the library formats log records. This problem is not specific to Boost.Log and may also show in different contexts related to locale and IO-streams management. + +[heading Additional notes for Cygwin users] + +Cygwin support is very preliminary. The default GCC version available in Cygwin (4.5.3 as of this writing) is unable to compile the library because of compiler errors. You will have to build a newer GCC from sources. Even then some Boost.Log functionality is not available. In particular, the socket-based [link log.detailed.sink_backends.syslog syslog backend] is not supported, as it is based on __boost_asio__, which doesn't compile on this platform. However, the native syslog support is still in place. + +[endsect] + +[section:config Configuring and building the library] + +The library has a separately compiled part which should be built as described in the [@https://www.boost.org/doc/libs/release/more/getting_started/ Getting Started guide]. One thing should be noted, though. If your application consists of more than one module (e.g. an exe and one or several dll's) that use Boost.Log, the library _must_ be built as a shared object. If you have a single executable or a single module that works with Boost.Log, you may build the library as a static library. + +The library supports a number of configuration macros: + +[table Configuration macros + [[Macro name] [Effect] [CMake notes]] + [[`BOOST_LOG_DYN_LINK`] [If defined in user code, the library will assume the binary is built as a dynamically loaded library ("dll" or "so"). Otherwise it is assumed that the library is built in static mode. This macro must be either defined or not defined for all translation units of user application that uses logging. This macro can help with auto-linking on platforms that support it.] [Defined automatically depending on `BUILD_SHARED_LIBS` CMake option.]] + [[`BOOST_ALL_DYN_LINK`] [Same as `BOOST_LOG_DYN_LINK` but also affects other Boost libraries the same way.] []] + [[`BOOST_USE_WINAPI_VERSION`] [Affects compilation of both the library and user's code. This macro is Windows-specific. Selects the target Windows version for various Boost libraries, including Boost.Log. Code compiled for a particular Windows version will likely fail to run on the older Windows versions, but may improve performance because of using newer OS features. The macro is expected to have an integer value equivalent to [@https://msdn.microsoft.com/en-us/library/6sehtctf.aspx `_WIN32_WINNT`].] []] + [[`BOOST_LOG_NO_THREADS`] [If defined, disables multithreading support. Affects the compilation of both the library and users' code. The macro is automatically defined if no threading support is detected.] []] + [[`BOOST_LOG_WITHOUT_CHAR`] [If defined, disables support for narrow character logging. Affects the compilation of both the library and users' code.] []] + [[`BOOST_LOG_WITHOUT_WCHAR_T`] [If defined, disables support for wide character logging. Affects the compilation of both the library and users' code.] []] + [[`BOOST_LOG_NO_QUERY_PERFORMANCE_COUNTER`] [This macro is only useful on Windows. It affects the compilation of both the library and users' code. If defined, disables support for the `QueryPerformanceCounter` API in the `timer` attribute. This will result in significantly less accurate time readings. The macro is intended to solve possible problems with earlier revisions of AMD Athlon CPU, described [@http://support.microsoft.com/?scid=kb;en-us;895980 here] and [@http://support.microsoft.com/?id=896256 here]. There are also known chipset hardware failures that may prevent this API from functioning properly (see [@http://support.microsoft.com/kb/274323 here]).] []] + [[`BOOST_LOG_USE_NATIVE_SYSLOG`] [Affects only compilation of the library. If for some reason support for the native SysLog API is not detected automatically, define this macro to forcibly enable it.] []] + [[`BOOST_LOG_WITHOUT_DEFAULT_FACTORIES`] [Affects only compilation of the library. If defined, the parsers for settings will be built without any default factories for filters and formatters. The user will have to register all attributes in the library before parsing any filters or formatters from strings. This can substantially reduce the binary size.] []] + [[`BOOST_LOG_WITHOUT_SETTINGS_PARSERS`] [Affects only compilation of the library. If defined, none of the facilities related to the parsers for settings will be built. This can substantially reduce the binary size.] [Disables compilation of the `boost_log_setup` library.]] + [[`BOOST_LOG_WITHOUT_DEBUG_OUTPUT`] [Affects only compilation of the library. If defined, the support for debugger output on Windows will not be built.] []] + [[`BOOST_LOG_WITHOUT_EVENT_LOG`] [Affects only compilation of the library. If defined, the support for Windows event log will not be built. Defining the macro also makes Message Compiler toolset unnecessary.] []] + [[`BOOST_LOG_WITHOUT_SYSLOG`] [Affects only compilation of the library. If defined, the support for [link log.detailed.sink_backends.syslog syslog backend] will not be built.] []] + [[`BOOST_LOG_WITHOUT_IPC`] [Affects only compilation of the library. If defined, the support for [link log.detailed.utilities.ipc interprocess queues] and the related [link log.detailed.sink_backends.text_ipc_message_queue backend] will not be built.] []] + [[`BOOST_LOG_WITHOUT_ASIO`] [Affects compilation of both the library and users' code. If defined, the support for Boost.ASIO-dependent functionality will not be built. In particular, this disables socket-based [link log.detailed.sink_backends.syslog syslog backend] implementation.] []] + [[`BOOST_LOG_NO_SHORTHAND_NAMES`] [Affects only compilation of users' code. If defined, some deprecated shorthand macro names will not be available.] [Not a CMake configuration option.]] + [[`BOOST_LOG_USE_COMPILER_TLS`] [Affects only compilation of the library. This macro enables support for compiler intrinsics for thread-local storage. Defining it may improve performance of Boost.Log if certain usage limitations are acceptable. See below for more comments.] []] + [[`BOOST_LOG_USE_STD_REGEX`, `BOOST_LOG_USE_BOOST_REGEX` or `BOOST_LOG_USE_BOOST_XPRESSIVE`] [Affects only compilation of the library. By defining one of these macros the user can instruct Boost.Log to use `std::regex`, __boost_regex__ or __boost_xpressive__ internally for string matching filters parsed from strings and settings. If none of these macros is defined then Boost.Log uses __boost_regex__ by default. Using `std::regex` or __boost_regex__ typically produces smaller executables, __boost_regex__ usually also being the fastest in run time. Using __boost_xpressive__ allows to eliminate the dependency on __boost_regex__ compiled binary. Note that these macros do not affect [link log.detailed.expressions.predicates.advanced_string_matching filtering expressions] created by users.] [Instead of definitng one of these macros, use `BOOST_LOG_USE_REGEX_BACKEND` string option with one of the following values: "std::regex", "Boost.Regex" or "Boost.Xpressive". The macros will be defined accordingly by CMake.]] +] + +You can define configuration macros in the `b2` command line, like this: + +[pre + b2 --with-log variant=release define=BOOST_LOG_WITHOUT_EVENT_LOG define=BOOST_USE_WINAPI_VERSION=0x0600 stage +] + +With CMake, the configuration macros can be specified as CMake options in the command line like this: + +[pre + cmake .. -DCMAKE_BUILD_TYPE=Release -DBOOST_LOG_WITHOUT_EVENT_LOG=On +] + +However, it may be more convenient to define configuration macros in the "boost/config/user.hpp" file in order to automatically define them both for the library and user's projects. If none of the options are specified, the library will try to support the most comprehensive setup, including support for all character types and features available for the target platform. + +The logging library uses several other Boost libraries that require building too. These are __boost_filesystem__, __boost_system__, __boost_date_time__, __boost_thread__ and in some configurations __boost_regex__. Refer to their documentation for detailed instructions on the building procedure. + +One final thing should be added. The library requires run-time type information (RTTI) to be enabled for both the library compilation and user's code compilation. Normally, this won't need anything from you except to verify that RTTI support is not disabled in your project. + +[heading Notes about compiler-supplied intrinsics for TLS] + +Many widely used compilers support builtin intrinsics for managing thread-local storage, which is used in several parts of the library. This feature is also included in the C++11 standard. Generally, these intrinsics allow for a much more efficient access to the storage than any surrogate implementation, be that __boost_thread__ or even native operating system API. However, this feature has several caveats: + +* Some operating systems don't support the use of these intrinsics in case if the TLS is defined in a shared library that is dynamically loaded during the application run time. These systems include Linux and Windows prior to Vista. Windows Vista and later do not have this issue. +* The TLS may not be reliably accessed from global constructors and destructors. At least MSVC 8.0 on Windows is known to have this problem. + +The library provides the `BOOST_LOG_USE_COMPILER_TLS` configuration macro that allows to enable the use of this feature, which will improve the library performance at the cost of these limitations: + +* The application executable must be linked with the Boost.Log library. It should not be loaded dynamically during run time. +* The application must not use logging in global constructors or destructors. + +Note that the `BOOST_LOG_USE_COMPILER_TLS` macro only controls use of TLS in Boost.Log but not in other libraries used by Boost.Log. For example, __boost_asio__ uses compiler-supplied TLS by default. In order to build Boost.Log binaries completely free from use of compiler-supplied TLS, this feature has to be disabled in those other libraries as well (in case of __boost_asio__ this can be achieved by defining `BOOST_ASIO_DISABLE_THREAD_KEYWORD_EXTENSION` when building and using Boost). + +Also note that enabling builtin compiler support for TLS does not remove the dependency on __boost_thread__ or lower level OS threading primitives, including those implementing TLS. The purpose of using compiler intrinsics for TLS is better performance rather than reducing dependencies. + +[heading Notes about native `wchar_t` support] + +Some compilers, most notably MSVC, have an option to disable the native `wchar_t` type, emulating it with a typedef for one of the standard integral types. From the C++ language perspective this behavior is not conforming but it may be useful for compatibility with some legacy code which is difficult to update. + +By default, Boost (and Boost.Log being part of it) is built with native `wchar_t` enabled. At the time of this writing, user will have to modify Boost.Build to enable the emulation mode. It is possible to build Boost.Log in this mode, but there are several caveats that have to be kept in mind: + +* The compiled Boost.Log binaries will be exporting symbols corresponding to the configuration chosen at build time. The user's code will have to use the same setting as was used when building Boost.Log, otherwise linking errors will appear. +* Since in emulation mode `wchar_t` is undistinguishable from one of the integer types, certain parts of the library may behave differently from the normal mode with native `wchar_t`. In particular, wide-character literals may be rejected or formatted differently. +* The emulation mode is not tested, so unexpected breakages are possible. + +Because of that using the emulation mode is discouraged and should be avoided. In future releases of the library its support may be removed completely. + +[heading Notes for CMake users on Windows] + +In order to compile Boost.Log with event log support on Windows using CMake, the initial CMake configuration should be performed with resource (`rc.exe` or `windres.exe`) and message compiler tools (`mc.exe` or `windmc.exe`) available in `PATH` environment variable. With MSVC, it is recommended to run CMake in the Visual Studio command line or otherwise ensure that Windows SDK environment variables are set. + +Alternatively, users may set `RC` and `MC` environment variables to paths of the resource and message compiler executables, respectively, or set `CMAKE_RC_COMPILER` and `CMAKE_MC_COMPILER` CMake options to the corresponding paths in the command line. + +[endsect] + +[endsect] diff --git a/doc/intro.qbk b/doc/intro.qbk new file mode 100644 index 0000000000..337307747c --- /dev/null +++ b/doc/intro.qbk @@ -0,0 +1,71 @@ +[/ + Copyright Andrey Semashev 2024. + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) + + This document is a part of Boost.Log library documentation. +/] + +[section:intro Introduction] + +[section:moti Motivation] + +Today applications grow rapidly, becoming complicated and difficult to test and debug. Most of the time applications run on a remote site, leaving the developer little chance to monitor their execution and figure out the reasons for their failure, once it should happen. Moreover, even the local debugging may become problematic if the application behavior depends heavily on asynchronous side events, such as a device feedback or another process activity. + +This is where logging can help. The application stores all essential information about its execution to a log, and when something goes wrong this information can be used to analyze the program behavior and make the necessary corrections. There are other very useful applications of logging, such as gathering statistical information and highlighting events (i.e. indicating that some situation has occurred or that the application is experiencing some problems). These tasks have proved to be vital for many real-world industrial applications. + +This library aims to make logging significantly easier for the application developer. It provides a wide range of out-of-the-box tools along with public interfaces for extending the library. The main goals of the library are: + +* Simplicity. A small example code snippet should be enough to get the feel of the library and be ready to use its basic features. +* Extensibility. A user should be able to extend functionality of the library for collecting and storing information into logs. +* Performance. The library should have as little performance impact on the user's application as possible. + +[endsect] + +[section:how_to_read How to read the documentation] + +The documentation is oriented to both new and experienced library users. However, users are expected to be familiar with commonly used Boost components, such as `shared_ptr`, `make_shared` (see __boost_smart_ptr__), and `function` (__boost_function__). Some parts of the documentation will refer to other Boost libraries, as needed. + +If this is your first experience with the library, it is recommended to read the [link log.design Design overview] section for a first glance at the library's capabilities and architecture. The [link log.installation Installation] and [link log.tutorial Tutorial] sections will help to get started experimenting with the library. The tutorial gives an overview of the library features with sample code snippets. Some tutorial steps are presented in two forms: simple and advanced. The simple form typically describes the most common and easy way to do the task and it is being recommended to be read by new users. The advanced form usually gives an expanded way to do the same thing but with an in-depth explanation and the ability to do some extra customization. This form may come in handy for more experienced users and should generally be read if the easy way does not satisfy your needs. + +Besides the tutorial there is a [link log.detailed Detailed features description] chapter. This part describes other tools provided by the library that were not covered by the tutorial. This chapter is best read on a case by case basis. + +Last, but not least, there is a [link log.reference Reference] section which gives the formal description of library component interfaces. + +To keep the code snippets in this documentation simple, the following namespace aliases are assumed to be defined: + + namespace logging = boost::log; + namespace sinks = boost::log::sinks; + namespace src = boost::log::sources; + namespace expr = boost::log::expressions; + namespace attrs = boost::log::attributes; + namespace keywords = boost::log::keywords; + +Note that most of the examples are followed by a link to a complete compilable code sample, with all the necessary includes and auxiliary code, if any, that was stripped from the documentation for brevity. Relevant includes are also listed at the beginning of sections. + +[endsect] + +[section:defs Definitions] + +Here are definitions of some terms that will be used widely throughout the documentation: + +[variablelist + [[Log record][A single bundle of information, collected from the user's application, that is a candidate to be put into the log. In a simple case the log record will be represented as a line of text in the log file after being processed by the logging library.]] + [[Attribute][An "attribute" is a piece of meta-information that can be used to specialize a log record. In Boost.Log, attributes are represented by function objects with a specific interface, which return the actual attribute value when invoked. Some example of attributes are a function returning current clock time, a function returning a monotonously increading log record counter, etc.]] + [[Attribute value][Attribute values are the actual data acquired from attributes. This data is attached to the specific log record and processed by the library. Values can have different types (integers, strings and more complex, including user defined types). Some examples of attribute values: current time stamp value, file name, line number, current scope name, etc. Attribute values are enveloped in a type erasing wrapper, so the actual type of the attribute is not visible in the interface. The actual (erased) type of the value is sometimes called the stored type.]] + [[(Attribute) value visitation][A way of processing the attribute value. This approach involves a function object (a visitor) which is applied to the attribute value. The visitor should know the stored type of the attribute value in order to process it.]] + [[(Attribute) value extraction][A way of processing the attribute value when the caller attempts to obtain a reference to the stored value. The caller should know the stored type of the attribute value in order to be able to extract it.]] + [[Log source][An entry point for the user's application to put log records to. In a simple case it is an object (logger) which maintains a set of attributes that will be used to form a log record upon the user's request. However, one can surely create a source that would emit log records on some side events (for example, by intercepting and parsing console output of another application).]] + [[Log sink][A target, to which all log records are fed after being collected from the user's application. The sink defines where and how the log records are going to be stored or processed.]] + [[Log filter][A predicate that takes a log record and tells whether this record should be passed through for further processing or discarded. The predicate typically forms its decision based on the attribute values attached to the record.]] + [[Log formatter][A function object that generates the final textual output from a log record. Some sinks, e.g. a binary logging sink, may not need it, although almost any text-based sink would use a formatter to compose its output.]] + [[Logging core][A global entity that maintains a list of sinks and applies filters to records generated by log sources. In the user's application, it is mainly used when the logging library is configured. There is only one instance of the logging core in an application.]] + [[i18n][Internationalization. The ability to manipulate wide characters.]] + [[TLS][Thread-local storage. The concept of having a variable that has independent values for each thread that attempts to access it.]] + [[RTTI][Run-time type information. This is the C++ language support data structures required for `dynamic_cast` and `typeid` operators to function properly.]] +] + +[endsect] + +[endsect] diff --git a/doc/log.qbk b/doc/log.qbk index 51d9cd2bb1..86788dbbec 100644 --- a/doc/log.qbk +++ b/doc/log.qbk @@ -2,7 +2,7 @@ [quickbook 1.5] [version v2] [authors [Semashev, Andrey]] - [copyright 2007 - 2016 Andrey Semashev] + [copyright 2007 - 2024 Andrey Semashev] [license Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -10,50 +10,55 @@ ] [id log] [source-mode c++] - [last-revision $Date$] ] [c++] [/ Links to external resources /] -[def __boost_config__ [@http://www.boost.org/doc/libs/release/libs/config/doc/html/index.html Boost.Config]] -[def __boost_atomic__ [@http://www.boost.org/doc/libs/release/doc/html/atomic.html Boost.Atomic]] -[def __boost_smart_ptr__ [@http://www.boost.org/doc/libs/release/libs/smart_ptr/smart_ptr.htm Boost.SmartPtr]] -[def __boost_function__ [@http://www.boost.org/doc/libs/release/doc/html/function.html Boost.Function]] -[def __boost_filesystem__ [@http://www.boost.org/doc/libs/release/libs/filesystem/doc/index.htm Boost.Filesystem]] -[def __boost_system__ [@http://www.boost.org/doc/libs/release/libs/system/doc/index.html Boost.System]] -[def __boost_date_time__ [@http://www.boost.org/doc/libs/release/doc/html/date_time.html Boost.DateTime]] -[def __boost_date_time_format__ [@http://www.boost.org/doc/libs/release/doc/html/date_time/date_time_io.html#date_time.format_flags Boost.DateTime]] -[def __boost_thread__ [@http://www.boost.org/doc/libs/release/doc/html/thread.html Boost.Thread]] -[def __boost_regex__ [@http://www.boost.org/doc/libs/release/libs/regex/index.html Boost.Regex]] -[def __boost_xpressive__ [@http://www.boost.org/doc/libs/release/doc/html/xpressive.html Boost.Xpressive]] -[def __boost_parameter__ [@http://www.boost.org/doc/libs/release/libs/parameter/doc/html/index.html Boost.Parameter]] -[def __boost_format__ [@http://www.boost.org/doc/libs/release/libs/format/index.html Boost.Format]] -[def __boost_preprocessor__ [@http://www.boost.org/doc/libs/release/libs/preprocessor/doc/index.html Boost.Preprocessor]] -[def __boost_bind__ [@http://www.boost.org/doc/libs/release/libs/bind/bind.html Boost.Bind]] -[def __boost_lambda__ [@http://www.boost.org/doc/libs/release/doc/html/lambda.html Boost.Lambda]] -[def __boost_phoenix__ [@http://www.boost.org/doc/libs/release/libs/phoenix/doc/html/index.html Boost.Phoenix]] -[def __boost_spirit__ [@http://www.boost.org/doc/libs/release/libs/spirit/classic/index.html Boost.Spirit]] -[def __boost_spirit2__ [@http://www.boost.org/doc/libs/release/libs/spirit/doc/html/index.html Boost.Spirit2]] -[def __boost_optional__ [@http://www.boost.org/doc/libs/release/libs/optional/index.html Boost.Optional]] -[def __boost_variant__ [@http://www.boost.org/doc/libs/release/doc/html/variant.html Boost.Variant]] -[def __boost_intrusive__ [@http://www.boost.org/doc/libs/release/doc/html/intrusive.html Boost.Intrusive]] -[def __boost_iostreams__ [@http://www.boost.org/doc/libs/release/libs/iostreams/doc/index.html Boost.IOStreams]] -[def __boost_mpl__ [@http://www.boost.org/doc/libs/release/libs/mpl/doc/index.html Boost.MPL]] -[def __boost_exception__ [@http://www.boost.org/doc/libs/release/libs/exception/doc/boost-exception.html Boost.Exception]] -[def __boost_scope_exit__ [@http://www.boost.org/doc/libs/release/libs/scope_exit/doc/html/index.html Boost.ScopeExit]] -[def __boost_any__ [@http://www.boost.org/doc/libs/release/doc/html/any.html Boost.Any]] -[def __boost_property_tree__ [@http://www.boost.org/doc/libs/release/doc/html/property_tree.html Boost.PropertyTree]] -[def __boost_asio__ [@http://www.boost.org/doc/libs/release/doc/html/boost_asio.html Boost.ASIO]] -[def __boost_move__ [@http://www.boost.org/doc/libs/release/doc/html/move.html Boost.Move]] -[def __boost_locale__ [@http://www.boost.org/doc/libs/release/libs/locale/doc/html/index.html Boost.Locale]] -[def __boost_type_index__ [@http://www.boost.org/doc/libs/release/doc/html/boost_typeindex.html Boost.TypeIndex]] -[def __boost_utility__ [@http://www.boost.org/doc/libs/release/libs/utility/utility.htm Boost.Utility]] -[def __boost_quickbook__ [@http://www.boost.org/doc/libs/release/doc/html/quickbook.html Boost.Quickbook]] - -[template ticket[key] '''#'''[key]''''''] -[template github_issue[key] '''GH#'''[key]''''''] -[template pull_request[key] '''PR#'''[key]''''''] +[def __boost_config__ [@https://www.boost.org/doc/libs/release/libs/config/doc/html/index.html Boost.Config]] +[def __boost_atomic__ [@https://www.boost.org/doc/libs/release/libs/atomic/doc/html/index.html Boost.Atomic]] +[def __boost_smart_ptr__ [@https://www.boost.org/doc/libs/release/libs/smart_ptr/doc/html/smart_ptr.html Boost.SmartPtr]] +[def __boost_function__ [@https://www.boost.org/doc/libs/release/doc/html/function.html Boost.Function]] +[def __boost_filesystem__ [@https://www.boost.org/doc/libs/release/libs/filesystem/doc/index.htm Boost.Filesystem]] +[def __boost_system__ [@https://www.boost.org/doc/libs/release/libs/system/doc/html/system.html Boost.System]] +[def __boost_date_time__ [@https://www.boost.org/doc/libs/release/doc/html/date_time.html Boost.DateTime]] +[def __boost_date_time_format__ [@https://www.boost.org/doc/libs/release/doc/html/date_time/date_time_io.html#date_time.format_flags Boost.DateTime]] +[def __boost_thread__ [@https://www.boost.org/doc/libs/release/doc/html/thread.html Boost.Thread]] +[def __boost_regex__ [@https://www.boost.org/doc/libs/release/libs/regex/doc/html/index.html Boost.Regex]] +[def __boost_xpressive__ [@https://www.boost.org/doc/libs/release/doc/html/xpressive.html Boost.Xpressive]] +[def __boost_parameter__ [@https://www.boost.org/doc/libs/release/libs/parameter/doc/html/index.html Boost.Parameter]] +[def __boost_format__ [@https://www.boost.org/doc/libs/release/libs/format/index.html Boost.Format]] +[def __boost_preprocessor__ [@https://www.boost.org/doc/libs/release/libs/preprocessor/doc/index.html Boost.Preprocessor]] +[def __boost_bind__ [@https://www.boost.org/doc/libs/release/libs/bind/doc/html/bind.html Boost.Bind]] +[def __boost_lambda__ [@https://www.boost.org/doc/libs/release/doc/html/lambda.html Boost.Lambda]] +[def __boost_phoenix__ [@https://www.boost.org/doc/libs/release/libs/phoenix/doc/html/index.html Boost.Phoenix]] +[def __boost_spirit__ [@https://www.boost.org/doc/libs/release/libs/spirit/classic/index.html Boost.Spirit]] +[def __boost_spirit2__ [@https://www.boost.org/doc/libs/release/libs/spirit/doc/html/index.html Boost.Spirit2]] +[def __boost_optional__ [@https://www.boost.org/doc/libs/release/libs/optional/doc/html/index.html Boost.Optional]] +[def __boost_variant__ [@https://www.boost.org/doc/libs/release/doc/html/variant.html Boost.Variant]] +[def __boost_intrusive__ [@https://www.boost.org/doc/libs/release/doc/html/intrusive.html Boost.Intrusive]] +[def __boost_range__ [@https://www.boost.org/doc/libs/release/libs/range/doc/html/index.html Boost.Range]] +[def __boost_iostreams__ [@https://www.boost.org/doc/libs/release/libs/iostreams/doc/index.html Boost.IOStreams]] +[def __boost_fusion__ [@https://www.boost.org/doc/libs/release/libs/fusion/doc/html/index.html Boost.Fusion]] +[def __boost_mpl__ [@https://www.boost.org/doc/libs/release/libs/mpl/doc/index.html Boost.MPL]] +[def __boost_exception__ [@https://www.boost.org/doc/libs/release/libs/exception/doc/boost-exception.html Boost.Exception]] +[def __boost_scope_exit__ [@https://www.boost.org/doc/libs/release/libs/scope_exit/doc/html/index.html Boost.ScopeExit]] +[def __boost_any__ [@https://www.boost.org/doc/libs/release/doc/html/any.html Boost.Any]] +[def __boost_property_tree__ [@https://www.boost.org/doc/libs/release/doc/html/property_tree.html Boost.PropertyTree]] +[def __boost_asio__ [@https://www.boost.org/doc/libs/release/doc/html/boost_asio.html Boost.Asio]] +[def __boost_move__ [@https://www.boost.org/doc/libs/release/doc/html/move.html Boost.Move]] +[def __boost_locale__ [@https://www.boost.org/doc/libs/release/libs/locale/doc/html/index.html Boost.Locale]] +[def __boost_type_index__ [@https://www.boost.org/doc/libs/release/doc/html/boost_typeindex.html Boost.TypeIndex]] +[def __boost_utility__ [@https://www.boost.org/doc/libs/release/libs/utility/doc/html/index.html Boost.Utility]] +[def __boost_quickbook__ [@https://www.boost.org/doc/libs/release/doc/html/quickbook.html Boost.Quickbook]] + +[def __boost_phoenix_adapt__ [@https://www.boost.org/doc/libs/release/libs/phoenix/doc/html/phoenix/modules/function/adapting_functions.html `BOOST_PHOENIX_ADAPT_*`]] +[def __boost_phoenix_bind__ [@https://www.boost.org/doc/libs/release/libs/phoenix/doc/html/phoenix/modules/bind.html `boost::phoenix::bind`]] + + +[template ticket[key]'''#'''[key]''''''] +[template github_issue[key]'''GH#'''[key]''''''] +[template pull_request[key]'''PR#'''[key]''''''] [/ Auto-generated macros that refer to Reference sections /] [include tmp/top_level_reference.qbk] @@ -86,6 +91,10 @@ [import ../example/doc/expressions_keyword_fmt_tag.cpp] [import ../example/doc/expressions_has_attr_stat_accum.cpp] [import ../example/doc/expressions_channel_severity_filter.cpp] +[import ../example/doc/expressions_filter_adapt_function.cpp] +[import ../example/doc/expressions_wrap_filter.cpp] +[import ../example/doc/expressions_formatter_adapt_function.cpp] +[import ../example/doc/expressions_wrap_formatter.cpp] [import ../example/doc/sources_net_connection.cpp] [import ../example/doc/sources_net_connection_chan.cpp] [import ../example/doc/sources_net_connection_dynamic_chan.cpp] @@ -110,6 +119,7 @@ [import ../example/doc/util_dynamic_type_disp.cpp] [import ../example/doc/util_manip_to_log.cpp] [import ../example/doc/util_ipc_reliable_mq_writer.cpp] +[import ../example/doc/util_setup_settings.cpp] [import ../example/doc/extension_stat_collector.cpp] [import ../example/doc/extension_app_launcher.cpp] [import ../example/doc/extension_record_tagger.cpp] @@ -121,180 +131,9 @@ [import ../example/wide_char/main.cpp] [import ../example/event_log/main.cpp] -[section:moti Motivation] - -Today applications grow rapidly, becoming complicated and difficult to test and debug. Most of the time applications run on a remote site, leaving the developer little chance to monitor their execution and figure out the reasons for their failure, once it should happen. Moreover, even the local debugging may become problematic if the application behavior depends heavily on asynchronous side events, such as a device feedback or another process activity. - -This is where logging can help. The application stores all essential information about its execution to a log, and when something goes wrong this information can be used to analyze the program behavior and make the necessary corrections. There are other very useful applications of logging, such as gathering statistical information and highlighting events (i.e. indicating that some situation has occurred or that the application is experiencing some problems). These tasks have proved to be vital for many real-world industrial applications. - -This library aims to make logging significantly easier for the application developer. It provides a wide range of out-of-the-box tools along with public interfaces for extending the library. The main goals of the library are: - -* Simplicity. A small example code snippet should be enough to get the feel of the library and be ready to use its basic features. -* Extensibility. A user should be able to extend functionality of the library for collecting and storing information into logs. -* Performance. The library should have as little performance impact on the user's application as possible. - -[endsect] - -[section:how_to_read How to read the documentation] - -The documentation is oriented to both new and experienced library users. However, users are expected to be familiar with commonly used Boost components, such as `shared_ptr`, `make_shared` (see __boost_smart_ptr__), and `function` (__boost_function__). Some parts of the documentation will refer to other Boost libraries, as needed. - -If this is your first experience with the library, it is recommended to read the [link log.design Design overview] section for a first glance at the library's capabilities and architecture. The [link log.installation Installation] and [link log.tutorial Tutorial] sections will help to get started experimenting with the library. The tutorial gives an overview of the library features with sample code snippets. Some tutorial steps are presented in two forms: simple and advanced. The simple form typically describes the most common and easy way to do the task and it is being recommended to be read by new users. The advanced form usually gives an expanded way to do the same thing but with an in-depth explanation and the ability to do some extra customization. This form may come in handy for more experienced users and should generally be read if the easy way does not satisfy your needs. - -Besides the tutorial there is a [link log.detailed Detailed features description] chapter. This part describes other tools provided by the library that were not covered by the tutorial. This chapter is best read on a case by case basis. - -Last, but not least, there is a reference which gives the formal description of library components. - -To keep the code snippets in this documentation simple, the following namespace aliases are assumed to be defined: - - namespace logging = boost::log; - namespace sinks = boost::log::sinks; - namespace src = boost::log::sources; - namespace expr = boost::log::expressions; - namespace attrs = boost::log::attributes; - namespace keywords = boost::log::keywords; - -[endsect] - -[section:installation Installation and compatibility] - -[section:supported_compilers Supported compilers and platforms] - -The library should build and work with a reasonably compliant compiler. The library was successfully built and tested on the following platforms: - -* Windows XP, Windows Vista, Windows 7. MSVC 8.0 SP1, MSVC 9.0 and newer. -* Linux. GCC 4.5 and newer. Older versions may work too, but it was not tested. -* Linux. Intel C++ 13.1.0.146 Build 20130121. -* Linux. Clang 3.2 and newer. - -The following compilers/platforms are not supported and will likely fail to compile the library: - -* C++11 compilers with non-C++11 standard libraries (like Clang with libstdc++ from GCC 4.2). Please, use a C++11 standard library in C++11 mode. -* MSVC 8.0 (without SP1) and older. -* GCC 4.2 and older. -* Borland C++ 5.5.1 (free version). Newer versions might or might not work. -* Solaris Studio 12.3 and older. -* Windows 9x, ME, NT4, 2000 and older are not supported. - -Boost.Log should be compatible with all hardware architectures supported by Boost. However, in case of 32 bit x86 architecture the library requires at least i586 class CPU to run. - -[heading Notes for GCC users] - -GCC versions since 4.5 support link time optimization (LTO), when most of optimizations and binary code generation happen at linking stage. This allows to perform more advanced optimizations and produce faster code. Unfortunately, it does not play well with projects containing source files that need to be compiled with different compiler options. Boost.Log is one of such projects, some parts of its sources contain optimizations for modern CPUs and will not run on older CPUs. Enabling LTO for Boost.Log will produce binaries incompatible with older CPUs (GCC bugs [@https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61043 61043], [@https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77845 77845]), which will likely result in crashes at run time. For this reason LTO is not supported in Boost.Log. - -There is a GCC [@http://gcc.gnu.org/bugzilla/show_bug.cgi?id=60607 bug] which may cause compilation failures when `-march=native` command line argument is used. It is recommended to avoid using `-march=native` argument (or `instruction-set=native` bjam property) and instead explicitly specify the target CPU (e.g. `instruction-set=sandy-bridge`). - -[heading Notes for MinGW, Cygwin and Visual Studio Express Edition users] - -In order to compile the library with these compilers special preparations are needed. First, in case of MinGW or Cygwin make sure you have installed the latest GCC version. The library will most likely fail to compile with GCC 3.x. - -Second, at some point the library will require a Message Compiler tool (`mc.exe`), which is not available in MinGW, Cygwin and some versions of MSVC Express Edition. Typically the library build scripts will automatically detect if message compiler is present on the system and disable Event log related portion of the library if it's not. If Event log support is required and it is not found on the system, you have three options to settle the problem. The recommended solution is to obtain the original `mc.exe`. This tool is available in Windows SDK, which can be downloaded from the Microsoft site freely (for example, [@http://www.microsoft.com/downloads/details.aspx?FamilyID=71deb800-c591-4f97-a900-bea146e4fae1&displaylang=en here]). Also, this tool should be available in Visual Studio 2010 Express Edition. During the compilation, `mc.exe` should be accessible through one of the directories in your `PATH` environment variable. - -Another way is to attempt to use the `windmc.exe` tool distributed with MinGW and Cygwin, which is the analogue of the original `mc.exe`. In order to do that you will have to patch Boost.Build files (in particular, the `tools/build/tools/mc.jam` file) as described in [@https://svn.boost.org/trac/boost/ticket/4111 this] ticket. After that you will be able to specify the `mc-compiler=windmc` option to bjam to build the library. - -In case if message compiler detection fails for some reason, you can explicitly disable support for event log backend by defining the `BOOST_LOG_WITHOUT_EVENT_LOG` configuration macro when building the library. This will remove the need for the message compiler. See [link log.installation.config this section] for more configuration options. - -MinGW users on Windows XP may be affected by the [@http://sourceforge.net/p/mingw-w64/bugs/307/ bug] in msvcrt.dll that is bundled with the operating system. The bug manifests itself as crashes while the library formats log records. This problem is not specific to Boost.Log and may also show in different contexts related to locale and IO-streams management. - -[heading Additional notes for Cygwin users] - -Cygwin support is very preliminary. The default GCC version available in Cygwin (4.5.3 as of this writing) is unable to compile the library because of compiler errors. You will have to build a newer GCC from sources. Even then some Boost.Log functionality is not available. In particular, the socket-based syslog backend is not supported, as it is based on __boost_asio__, which doesn't compile on this platform. However, the native syslog support is still in place. - -[endsect] - -[section:config Configuring and building the library] - -The library has a separately compiled part which should be built as described in the [@http://www.boost.org/doc/libs/release/more/getting_started/ Getting Started guide]. One thing should be noted, though. If your application consists of more than one module (e.g. an exe and one or several dll's) that use Boost.Log, the library _must_ be built as a shared object. If you have a single executable or a single module that works with Boost.Log, you may build the library as a static library. - -The library supports a number of configuration macros: - -[table Configuration macros - [[Macro name] [Effect]] - [[`BOOST_LOG_DYN_LINK`] [If defined in user code, the library will assume the binary is built as a dynamically loaded library ("dll" or "so"). Otherwise it is assumed that the library is built in static mode. This macro must be either defined or not defined for all translation units of user application that uses logging. This macro can help with auto-linking on platforms that support it.]] - [[`BOOST_ALL_DYN_LINK`] [Same as `BOOST_LOG_DYN_LINK` but also affects other Boost libraries the same way.]] - [[`BOOST_USE_WINAPI_VERSION`] [Affects compilation of both the library and user's code. This macro is Windows-specific. Selects the target Windows version for various Boost libraries, including Boost.Log. Code compiled for a particular Windows version will likely fail to run on the older Windows versions, but may improve performance because of using newer OS features. The macro is expected to have an integer value equivalent to [@https://msdn.microsoft.com/en-us/library/6sehtctf.aspx `_WIN32_WINNT`].]] - [[`BOOST_LOG_NO_THREADS`] [If defined, disables multithreading support. Affects the compilation of both the library and users' code. The macro is automatically defined if no threading support is detected.]] - [[`BOOST_LOG_WITHOUT_CHAR`] [If defined, disables support for narrow character logging. Affects the compilation of both the library and users' code.]] - [[`BOOST_LOG_WITHOUT_WCHAR_T`] [If defined, disables support for wide character logging. Affects the compilation of both the library and users' code.]] - [[`BOOST_LOG_NO_QUERY_PERFORMANCE_COUNTER`] [This macro is only useful on Windows. It affects the compilation of both the library and users' code. If defined, disables support for the `QueryPerformanceCounter` API in the `timer` attribute. This will result in significantly less accurate time readings. The macro is intended to solve possible problems with earlier revisions of AMD Athlon CPU, described [@http://support.microsoft.com/?scid=kb;en-us;895980 here] and [@http://support.microsoft.com/?id=896256 here]. There are also known chipset hardware failures that may prevent this API from functioning properly (see [@http://support.microsoft.com/kb/274323 here]).]] - [[`BOOST_LOG_USE_NATIVE_SYSLOG`] [Affects only compilation of the library. If for some reason support for the native SysLog API is not detected automatically, define this macro to forcibly enable it.]] - [[`BOOST_LOG_WITHOUT_DEFAULT_FACTORIES`] [Affects only compilation of the library. If defined, the parsers for settings will be built without any default factories for filters and formatters. The user will have to register all attributes in the library before parsing any filters or formatters from strings. This can substantially reduce the binary size.]] - [[`BOOST_LOG_WITHOUT_SETTINGS_PARSERS`] [Affects only compilation of the library. If defined, none of the facilities related to the parsers for settings will be built. This can substantially reduce the binary size.]] - [[`BOOST_LOG_WITHOUT_DEBUG_OUTPUT`] [Affects only compilation of the library. If defined, the support for debugger output on Windows will not be built.]] - [[`BOOST_LOG_WITHOUT_EVENT_LOG`] [Affects only compilation of the library. If defined, the support for Windows event log will not be built. Defining the macro also makes Message Compiler toolset unnecessary.]] - [[`BOOST_LOG_WITHOUT_SYSLOG`] [Affects only compilation of the library. If defined, the support for syslog backend will not be built.]] - [[`BOOST_LOG_WITHOUT_IPC`] [Affects only compilation of the library. If defined, the support for interprocess queues will not be built.]] - [[`BOOST_LOG_NO_SHORTHAND_NAMES`] [Affects only compilation of users' code. If defined, some deprecated shorthand macro names will not be available.]] - [[`BOOST_LOG_USE_COMPILER_TLS`] [Affects only compilation of the library. This macro enables support for compiler intrinsics for thread-local storage. Defining it may improve performance of Boost.Log if certain usage limitations are acceptable. See below for more comments.]] - [[`BOOST_LOG_USE_STD_REGEX`, `BOOST_LOG_USE_BOOST_REGEX` or `BOOST_LOG_USE_BOOST_XPRESSIVE`] [Affects only compilation of the library. By defining one of these macros the user can instruct Boost.Log to use `std::regex`, __boost_regex__ or __boost_xpressive__ internally for string matching filters parsed from strings and settings. If none of these macros is defined then Boost.Log uses __boost_regex__ by default. Using `std::regex` or __boost_regex__ typically produces smaller executables, __boost_regex__ usually also being the fastest in run time. Using __boost_xpressive__ allows to eliminate the dependency on __boost_regex__ compiled binary. Note that these macros do not affect [link log.detailed.expressions.predicates.advanced_string_matching filtering expressions] created by users.]] -] - -You can define configuration macros in the `bjam` command line, like this: - -[pre - bjam --with-log variant=release define=BOOST_LOG_WITHOUT_EVENT_LOG define=BOOST_USE_WINAPI_VERSION=0x0600 stage -] - -However, it may be more convenient to define configuration macros in the "boost/config/user.hpp" file in order to automatically define them both for the library and user's projects. If none of the options are specified, the library will try to support the most comprehensive setup, including support for all character types and features available for the target platform. - -The logging library uses several other Boost libraries that require building too. These are __boost_filesystem__, __boost_system__, __boost_date_time__, __boost_thread__ and in some configurations __boost_regex__. Refer to their documentation for detailed instructions on the building procedure. - -One final thing should be added. The library requires run-time type information (RTTI) to be enabled for both the library compilation and user's code compilation. Normally, this won't need anything from you except to verify that RTTI support is not disabled in your project. - -[heading Notes about compiler-supplied intrinsics for TLS] - -Many widely used compilers support builtin intrinsics for managing thread-local storage, which is used in several parts of the library. This feature is also included in the C++11 standard. Generally, these intrinsics allow for a much more efficient access to the storage than any surrogate implementation, be that __boost_thread__ or even native operating system API. However, this feature has several caveats: - -* Some operating systems don't support the use of these intrinsics in case if the TLS is defined in a shared library that is dynamically loaded during the application run time. These systems include Linux and Windows prior to Vista. Windows Vista and later do not have this issue. -* The TLS may not be reliably accessed from global constructors and destructors. At least MSVC 8.0 on Windows is known to have this problem. - -The library provides the `BOOST_LOG_USE_COMPILER_TLS` configuration macro that allows to enable the use of this feature, which will improve the library performance at the cost of these limitations: - -* The application executable must be linked with the Boost.Log library. It should not be loaded dynamically during run time. -* The application must not use logging in global constructors or destructors. - -Note that the `BOOST_LOG_USE_COMPILER_TLS` macro only controls use of TLS in Boost.Log but not in other libraries used by Boost.Log. For example, __boost_asio__ uses compiler-supplied TLS by default. In order to build Boost.Log binaries completely free from use of compiler-supplied TLS, this feature has to be disabled in those other libraries as well (in case of __boost_asio__ this can be achieved by defining `BOOST_ASIO_DISABLE_THREAD_KEYWORD_EXTENSION` when building and using Boost). - -[heading Notes about native `wchar_t` support] - -Some compilers, most notably MSVC, have an option to disable the native `wchar_t` type, emulating it with a typedef for one of the standard integral types. From the C++ language perspective this behavior is not conforming but it may be useful for compatibility with some legacy code which is difficult to update. - -By default, Boost (and Boost.Log being part of it) is built with native `wchar_t` enabled. At the time of this writing, user will have to modify Boost.Build to enable the emulation mode. It is possible to build Boost.Log in this mode, but there are several caveats that have to be kept in mind: - -* The compiled Boost.Log binaries will be exporting symbols corresponding to the configuration chosen at build time. The user's code will have to use the same setting as was used when building Boost.Log, otherwise linking errors will appear. -* Since in emulation mode `wchar_t` is undistinguishable from one of the integer types, certain parts of the library may behave differently from the normal mode with native `wchar_t`. In particular, wide-character literals may be rejected or formatted differently. -* The emulation mode is not tested, so unexpected breakages are possible. - -Because of that using the emulation mode is discouraged and should be avoided. In future releases of the library its support may be removed completely. - -[endsect] - -[endsect] - -[section:defs Definitions] - -Here are definitions of some terms that will be used widely throughout the documentation: - -[variablelist - [[Log record][A single bundle of information, collected from the user's application, that is a candidate to be put into the log. In a simple case the log record will be represented as a line of text in the log file after being processed by the logging library.]] - [[Attribute][An "attribute" is a piece of meta-information that can be used to specialize a log record. In Boost.Log attributes are represented by function objects with a specific interface, which return the actual attribute value when invoked.]] - [[Attribute value][Attribute values are the actual data acquired from attributes. This data is attached to the specific log record and processed by the library. Values can have different types (integers, strings and more complex, including user defined types). Some examples of attribute values: current time stamp value, file name, line number, current scope name, etc.. Attribute values are enveloped in a type erasing wrapper, so the actual type of the attribute is not visible in the interface. The actual (erased) type of the value is sometimes called the stored type.]] - [[(Attribute) value visitation][A way of processing the attribute value. This approach involves a function object (a visitor) which is applied to the attribute value. The visitor should know the stored type of the attribute value in order to process it.]] - [[(Attribute) value extraction][A way of processing the attribute value when the caller attempts to obtain a reference to the stored value. The caller should know the stored type of the attribute value in order to be able to extract it.]] - [[Log sink][A target, to which all log records are fed after being collected from the user's application. The sink defines where and how the log records are going to be stored or processed.]] - [[Log source][An entry point for the user's application to put log records to. In a simple case it is an object (logger) which maintains a set of attributes that will be used to form a log record upon the user's request. However, one can surely create a source that would emit log records on some side events (for example, by intercepting and parsing console output of another application).]] - [[Log filter][A predicate that takes a log record and tells whether this record should be passed through or discarded. The predicate typically forms its decision based on the attribute values attached to the record.]] - [[Log formatter][A function object that generates the final textual output from a log record. Some sinks, e.g. a binary logging sink, may not need it, although almost any text-based sink would use a formatter to compose its output.]] - [[Logging core][The global entity that maintains connections between sources and sinks and applies filters to records. It is mainly used when the logging library is initialized.]] - [[i18n][Internationalization. The ability to manipulate wide characters.]] - [[TLS][Thread-local storage. The concept of having a variable that has independent values for each thread that attempts to access it.]] - [[RTTI][Run-time type information. This is the C++ language support data structures required for `dynamic_cast` and `typeid` operators to function properly.]] -] - -[endsect] - +[include:log intro.qbk] +[include:log install.qbk] [include:log design.qbk] - [include:log tutorial.qbk] [section:detailed Detailed features description] diff --git a/doc/rationale.qbk b/doc/rationale.qbk index 30bdb09d10..83b76be467 100644 --- a/doc/rationale.qbk +++ b/doc/rationale.qbk @@ -1,5 +1,5 @@ [/ - Copyright Andrey Semashev 2007 - 2015. + Copyright Andrey Semashev 2007 - 2022. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -91,11 +91,11 @@ There were other issues, like having an attribute set iterator that points to on [section:why_weak_record_ordering Why log records are weakly ordered in a multithreaded application?] -Although the library guarantees that log records made in a given thread are always delivered to sinks in the same order as they were made in, the library cannot provide such guarantee for different threads. For instance, it is possible that thread A emits a log record and gets preempted, then thread B emits its log record and manages to deliver it to a sink before being preempted. The resulting log will contain log record from thread B before the record made in thread A. However, attribute values attached to the records will always be actual with regard to the moment of emitting the record and not the moment of passing the record to the sink. This is the reason for a strange, at first glance, situation when a log record with an earlier time stamp follows a record with a later time stamp. The problem appears quite rarely, usually when thread contention on logging is very high. +Although the library guarantees that log records made in a given thread are always delivered to sinks in the same order as they were made in, the library cannot provide such guarantee for different threads. For instance, it is possible that thread A emits a log record and gets preempted, then thread B emits its log record and manages to deliver it to a sink before being preempted. The resulting log will contain log record from thread B before the record made in thread A. However, attribute values attached to the records will always be actual with regard to the moment of emitting the record and not the moment of passing the record to the sink. This is the reason for a strange, at first glance, situation when a log record with an earlier time stamp follows a record with a later time stamp. The problem appears quite rarely, usually when thread contention on logging is rather high. There are few possible ways to cope with the problem: -* Enforce strict serialization of log record being made throughout the application. This solution implies a severe performance impact in multithreaded applications because log records that otherwise could be processed concurrently would have to go serial. Since this controverses one of the [link log.moti main library goals], it was rejected. +* Enforce strict serialization of log record being made throughout the application. This solution implies a severe performance impact in multithreaded applications because log records that otherwise could be processed concurrently would have to go serial. Since this controverses one of the [link log.intro.moti main library goals], it was rejected. * Attempt to maintain log record ordering on the sink level. This solution is more or less viable. On the downside, it would introduce log record buffering, which in turn would compromise logs reliability. In the case of application crash all buffered records would be lost. * Bear with the problem and let mis-ordered records appear in log files occasionally. Order log records upon reading the files, if needed. @@ -208,7 +208,7 @@ Most of the time users won't even notice the existence of this internal namespac * The `` component describes the library major version. It is currently `v2`. * The `` component tells whether the library is linked statically or dynamically. It is `s` if the library is linked statically and empty otherwise. * The `` component is `st` for single-threaded builds and `mt` for multi-threaded ones. -* The `` component describes the underlying OS API used by the library. Currently, it is only specified for multi-threaded builds. Depending on the target platform and configuration, it can be `posix`, `nt5` or `nt6`. +* The `` component describes the underlying OS API used by the library. Currently, it is only specified for multi-threaded builds. Depending on the target platform and configuration, it can be `posix`, `nt5`, `nt6` or `nt62`. As a couple quick examples, `v2s_st` corresponds to v2 static single-threaded build of the library and `v2_mt_posix` - to v2 dynamic multi-threaded build for POSIX system API. @@ -224,4 +224,34 @@ The suggested solution is to upgrade Visual Studio 2010 to Visual Studio 2010 SP [endsect] +[section:how_to_use_in_libraries How to use Boost.Log in libraries?] + +When using Boost.Log with libraries, there are several recommendadions to follow. First, as noted in the [link log.installation.config library configuration] section, using the library in multiple modules (including libraries and the application itself) requires Boost.Log to be built as a shared library. This is needed because Boost.Log maintains a number of process-wide singletons and may not function correctly if these singletons are duplicated. If building Boost.Log as a shared library is not desirable, it is possible to encapsulate it in a single user's shared library and link the rest of the modules with that library. In this case, Boost.Log can be built as a shared library and linked into user's shared library. The shared library API and other modules must not use Boost.Log components, including: + +* Logging core +* Sinks +* Loggers +* Attributes, including named scope markup +* Library configuration helpers, including filter, formatter and settings parsers + +However, user's shared library may provide its own API that will implement similar functionality, using relevant Boost.Log facilities internally. + +Next, it is important to ensure that logging configuration is coordinated between all modules. For example, if a file log is needed, only one file sink must be added, regardless of how many libraries are using logging. The preferred way to achieve this is perform logging configuration only in the main application, for the following reasons: + +* Logging configuration should be performed early in the `main` function, which is implemented in the application. Using global constructors in libraries can be problematic due to undefined order of global initialization and the possibility of dynamic loading and unloading of the libraries. +* Libraries are normally "serving the needs" of the main application, and conceptually it is the application that must decide how the library exposes its diagnostic information such as logs. One application may want to output its logs to console, another one store it in a file, and a third one may want to completely suppress any logging. A well-behaving library should transparently support any such use case and Boost.Log allows to achieve exactly that. + +It should be noted that having logging configured by the application implies that the application is written in C++ and can use Boost.Log. If this is not the case, libraries should still allow for this design and offer an API for configuring logging on behalf of the application. Alternatively, a separate library written in C++ can be used for the sole purpose of configuring logging. This way logging set up decisions are still made by the application, indirectly through the library API. + +To implement this design, here are recommendations for library writers: + +* Libraries should refrain from adding or configuring sinks, filters or formatters, including using library configuration helpers. The exception is the aforementioned API that configures logging on behalf of the application, but this configuretion should not be performed by default. +* Libraries should be careful about adding or removing global and thread-specific attributes in the logging core - any such actions must be clearly documented. +* Libraries can freely create loggers, modify their attributes and emit log records. +* Libraries may use named scope markup, even if they don't register [link log.detailed.attributes.named_scope `named_scope`] attribute themselves. The application can add and configure this attribute, which will enable this information in the output. +* Libraries should document the attributes it uses, incliding their names and value types, so that the application can configure filters and formatters accordingly. This includes the message text attribute - in particular, it is important to know the type of the attribute value (e.g. `std::string` vs. `std::wstring`) and what character encoding it uses. If possible, declare [link log.detailed.expressions.attr_keywords attribute keywords] for all attributes used by the library in a public header. +* Libraries are recommended to mark all log records they emit with an attribute. For example, all log records could be made in a specific [link log.detailed.sources.channel_logger channel]. This way the application will be able to configure logging specifically for the library (for example, extract log records from the library to a separate file or apply a different severity level threshold). + +[endsect] + [endsect] diff --git a/doc/sink_backends.qbk b/doc/sink_backends.qbk index 77e53ff8d3..38586994fd 100644 --- a/doc/sink_backends.qbk +++ b/doc/sink_backends.qbk @@ -1,5 +1,5 @@ [/ - Copyright Andrey Semashev 2007 - 2016. + Copyright Andrey Semashev 2007 - 2022. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -15,7 +15,7 @@ The text output stream sink backend is the most generic backend provided by the library out of the box. The backend is implemented in the [class_sinks_basic_text_ostream_backend] class template (`text_ostream_backend` and `wtext_ostream_backend` convenience typedefs provided for narrow and wide character support). It supports formatting log records into strings and putting into one or several streams. Each attached stream gets the same result of formatting, so if you need to format log records differently for different streams, you will need to create several sinks - each with its own formatter. -The backend also provides a feature that may come useful when debugging your application. With the `auto_flush` method one can tell the sink to automatically flush the buffers of all attached streams after each log record is written. This will, of course, degrade logging performance, but in case of an application crash there is a good chance that last log records will not be lost. +The backend also provides a feature that may come useful when debugging your application. With the `auto_flush` method one can tell the sink to automatically flush the buffers of all attached streams after each log record is written. This will, of course, degrade logging performance, but in case of an application crash there is a good chance that last log records are not lost. [example_sinks_ostream] @@ -31,18 +31,32 @@ Although it is possible to write logs into files with the [link log.detailed.sin * Flexible log file naming * Placing the rotated files into a special location in the file system * Deleting the oldest files in order to free more space on the file system +* Similar to [link log.detailed.sink_backends.text_ostream text stream backend], the file sink backend also supports the auto-flush feature The backend is called [class_sinks_text_file_backend]. [warning This sink uses __boost_filesystem__ internally, which may cause problems on process termination. See [link log.rationale.why_crash_on_term here] for more details.] -[heading File rotation] +[section:file_rotation File rotation] -File rotation is implemented by the sink backend itself. The file name pattern and rotation thresholds can be specified when the [class_sinks_text_file_backend] backend is constructed. +File rotation happens when the sink detects that one or more rotation conditions are met and a new file needs to be created. It is implemented by the sink backend and includes the following steps: + +# If the log file is currently open, invoke the [link log.detailed.sink_backends.text_file.open_close_handlers file close handler] and close the file. +# If a target file name pattern is set, generate a new target file name and rename the log file to that generated name. +# If a [link log.detailed.sink_backends.text_file.file_collection file collector] is configured, pass the log file to it for collection. At this point the file collector may remove older log files to free up some space and move the new file into the target storage. +# When a new log record needs to be written, generate a new file name for the new file and create a file with that name. If [link log.detailed.sink_backends.text_file.appending file appending] is enabled and a file with that name exists, the file is opened in append mode instead of overwriting. + +It is important to note that there are three kinds of file names or paths involved in this process: + +* The file name that is used to create or open the log file to actively write to. This is called an /active/ file name and is specified by the `file_name` named parameter of the sink backend constructor or by calling the `set_file_name_pattern` method. +* The file name that is generated when the log file is closed and about to be collected. This is called a /target/ file name, because it defines how the log files will be named in the target storage. Target file name is optional, it can be specified by the `target_file_name` named parameter or by calling the `set_target_file_name_pattern` method. +* The target storage location, which is a directory with previously rotated log files, managed by a [link log.detailed.sink_backends.text_file.file_collection file collector]. Multiple sinks can share the same target storage. + +The file name patterns and rotation conditions can be specified when the [class_sinks_text_file_backend] backend is constructed. [example_sinks_file] -[note The file size at rotation can be imprecise. The implementation counts the number of characters written to the file, but the underlying API can introduce additional auxiliary data, which would increase the log file's actual size on disk. For instance, it is well known that Windows and DOS operating systems have a special treatment with regard to new-line characters. Each new-line character is written as a two byte sequence 0x0D 0x0A instead of a single 0x0A. Other platform-specific character translations are also known. The actual size on disk can also be less than the number of written characters on compressed filesystems.] +[note The file size at rotation can be imprecise. The implementation counts the number of bytes written to the file, but the underlying API can introduce additional auxiliary data, which would increase the log file's actual size on disk. For instance, it is well known that Windows and DOS operating systems have a special treatment with regard to new-line characters. Each new-line character is written as a two byte sequence 0x0D 0x0A instead of a single 0x0A. Other platform-specific character translations are also known. The actual size on disk can also be less than the number of written characters on compressed filesystems.] The time-based rotation is not limited by only time points. There are following options available out of the box: @@ -86,7 +100,8 @@ If none of the above applies, one can specify his own predicate for time-based r boost::shared_ptr< sinks::text_file_backend > backend = boost::make_shared< sinks::text_file_backend >( - keywords::file_name = "file_%5N.log", + keywords::file_name = "file.log", + keywords::target_file_name = "file_%5N.log", keywords::time_based_rotation = &is_it_time_to_rotate ); @@ -103,14 +118,15 @@ In addition to time and size-based file rotation the backend also performs rotat boost::shared_ptr< sinks::text_file_backend > backend = boost::make_shared< sinks::text_file_backend >( - keywords::file_name = "file_%5N.log", + keywords::file_name = "file.log", + keywords::target_file_name = "file_%5N.log", keywords::enable_final_rotation = false ); // ... } -The file name pattern may contain a number of wildcards, like the one you can see in the example above. Supported placeholders are: +Both active and target file name patterns may contain a number of wildcards, like the one you can see in the example above. Supported placeholders are: * Current date and time components. The placeholders conform to the ones specified by __boost_date_time__ library. * File counter (`%N`) with an optional width specification in the `printf`-like format. The file counter will always be decimal, zero filled to the specified width. @@ -126,17 +142,25 @@ A few quick examples: [[file\_%Y-%m-%d\_%H-%M-%S.%N.log] [file\_2008-07-05\_13-44-23.1.log, file\_2008-07-06\_16-00-10.2.log...]] ] -[important Although all __boost_date_time__ format specifiers will work, there are restrictions on some of them, if you intend to scan for old log files. This functionality is discussed in the next section.] +[important Although all __boost_date_time__ format specifiers will work, there are restrictions on some of them, if you intend to scan for old log files. This functionality is discussed [link log.detailed.sink_backends.text_file.file_scanning here].] + +Note that, as described above, active and target file names are generated at different points in time. Specifically, the active file name is generated when the log file is originally created, and the target file name - when the file is closed. Timestamps used to construct these file names will reflect that difference. + +[tip When [link log.detailed.sink_backends.text_file.appending file appending] is needed, it is recommended to avoid any placeholders in the active file name pattern. Otherwise appending won't happen because of the different active log file names. You can use the target file name pattern to add a timestamp or counter to the log file after rotation.] + +[endsect] + +[section:open_close_handlers File open and close handlers] -The sink backend allows hooking into the file rotation process in order to perform pre- and post-rotation actions. This can be useful to maintain log file validity by writing headers and footers. For example, this is how we could modify the `init_logging` function in order to write logs into XML files: +The sink backend allows hooking into the file rotation process in order to perform pre- and post-rotation actions. This can be useful to maintain log file validity by writing headers and footers. For example, this is how we could modify the `init_logging` function from our previous examples in order to write logs into XML files: [example_sinks_xml_file] [@boost:/libs/log/example/doc/sinks_xml_file.cpp See the complete code]. -Finally, the sink backend also supports the auto-flush feature, like the [link log.detailed.sink_backends.text_ostream text stream backend] does. +[endsect] -[heading Managing rotated files] +[section:file_collection Managing rotated files] After being closed, the rotated files can be collected. In order to do so one has to set up a file collector by specifying the target directory where to collect the rotated files and, optionally, size thresholds. For example, we can modify the `init_logging` function to place rotated files into a distinct directory and limit total size of the files. Let's assume the following function is called by `init_logging` with the constructed sink: @@ -148,15 +172,53 @@ One can create multiple file sink backends that collect files into the same targ [warning The collector does not resolve log file name clashes between different sink backends, so if the clash occurs the behavior is undefined, in general. Depending on the circumstances, the files may overwrite each other or the operation may fail entirely.] -The file collector provides another useful feature. Suppose you ran your application 5 times and you have 5 log files in the "logs" directory. The file sink backend and file collector provide a `scan_for_files` method that searches the target directory for these files and takes them into account. So, if it comes to deleting files, these files are not forgotten. What's more, if the file name pattern in the backend involves a file counter, scanning for older files allows updating the counter to the most recent value. Here is the final version of our `init_logging` function: +[endsect] + +[section:file_scanning Scanning for rotated files] + +The file collector provides another useful feature. Suppose you ran your application 5 times and you have 5 log files in the "logs" directory. The file sink backend and file collector provide a `scan_for_files` method that searches the target directory for these files and takes them into account. So, if it comes to deleting files, these files are not forgotten. What's more, if a file name pattern in the backend involves a file counter, scanning for older files allows updating the counter to the most recent value. Here is the final version of our `init_logging` function: [example_sinks_xml_file_final] -There are two methods of file scanning: the scan that involves file name matching with the file name pattern (the default) and the scan that assumes that all files in the target directory are log files. The former applies certain restrictions on the placeholders that can be used within the file name pattern, in particular only file counter placeholder and these placeholders of __boost_date_time__ are supported: `%y`, `%Y`, `%m`, `%d`, `%H`, `%M`, `%S`, `%f`. The latter scanning method, in its turn, has its own drawback: it does not allow updating the file counter in the backend. It is also considered to be more dangerous as it may result in unintended file deletion, so be cautious. The all-files scanning method can be enabled by passing it as an additional parameter to the `scan_for_files` call: +There are two methods of file scanning: the scan that involves file name matching with the target file name pattern (the default) and the scan that assumes that all files in the target directory are log files. The former applies certain restrictions on the placeholders that can be used within the file name pattern, in particular only file counter placeholder and these placeholders of __boost_date_time__ are supported: `%y`, `%Y`, `%m`, `%d`, `%H`, `%M`, `%S`, `%f`. The latter scanning method, in its turn, has its own drawback: it does not allow updating the file counter in the backend. It is also considered to be more dangerous as it may result in unintended file deletion, so be cautious. The all-files scanning method can be enabled by passing it as an additional parameter to the `scan_for_files` call: // Look for all files in the target directory backend->scan_for_files(sinks::file::scan_all); +When scanning for matching file names, if the target file name is not set then the active file name pattern is used instead. + +[endsect] + +[section:appending Appending to the previously written files] + +The sink backend supports appending to the previously written files (e.g. left from a previous run of your application). In order to enable this mode, one has to add `std::ios_base::app` to the file open mode used by the backend. This can be done with the `open_mode` named parameter of the backend constructor or the `set_open_mode` method. + + void init_logging() + { + // ... + + boost::shared_ptr< sinks::text_file_backend > backend = + boost::make_shared< sinks::text_file_backend >( + keywords::file_name = "file.log", + keywords::target_file_name = "file_%5N.log", + keywords::open_mode = std::ios_base::out | std::ios_base::app, + keywords::enable_final_rotation = false + ); + + // ... + } + +When initializing from [link log.detailed.utilities.setup.settings settings], the "Append" parameter of the "TextFile" sink enables appending. + +In order for file appending to actually happen, it is important that the name of the newly opened log file matches the previously written file. Otherwise, the sink will simply create a new file under the new name. There are several recommendations to follow when file appending is desirable: + +* Don't use placeholders in the active file name pattern. This will ensure that every time the sink opens a file for writing, that file has the same name. +* Use a distinct target file name pattern, preferably with date, time or counter placeholders. This will ensure that when the file is rotated and collected, it doesn't clash with the previously written files, and that the newly opened file will have a different name from the previous one. +* Disable file rotation on sink destruction. This will leave the last actively written log file in its original location and with the original name so that it can be picked up by a future run of your application. +* Prefer a stable location and file name for the actively written log files. The library will not find the previously written file if its name or directory changes between application runs. + +[endsect] + [endsect] [section:text_multifile Text multi-file backend] @@ -165,6 +227,8 @@ There are two methods of file scanning: the scan that involves file name matchin While the text stream and file backends are aimed to store all log records into a single file/stream, this backend serves a different purpose. Assume we have a banking request processing application and we want logs related to every single request to be placed into a separate file. If we can associate some attribute with the request identity then the [class_sinks_text_multifile_backend] backend is the way to go. +[note During its operation, the multi-file backend frequently opens and closes log files, which means that the cost of these operations on a given system will be significant to the logging performance. Windows, especially with antivirus software running, is known to have extremely expensive file open and close operations. For example, compared to Linux, closing a file can be in the order of /hundreds/ times slower, according to [@https://lists.boost.org/Archives/boost/2021/03/250968.php some] [@https://gregoryszorc.com/blog/2015/10/22/append-i/o-performance-on-windows/ reports]. Consider creating multiple [link log.detailed.sink_backends.text_file `text_file_backend`] sinks with appropriate filters or running `text_multifile_backend` as an [link log.detailed.sink_frontends.async asynchronous] sink if you are affected.] + [example_sinks_multifile] You can see we used a regular [link log.detailed.expressions.formatters formatter] in order to specify file naming pattern. Now, every log record with a distinct value of the "RequestID" attribute will be stored in a separate file, no matter how many different requests are being processed by the application concurrently. You can also find the [@boost:/libs/log/example/multiple_files/main.cpp `multiple_files`] example in the library distribution, which shows a similar technique to separate logs generated by different threads of the application. diff --git a/doc/sink_frontends.qbk b/doc/sink_frontends.qbk index 8c00ce0c6a..e9196929b6 100644 --- a/doc/sink_frontends.qbk +++ b/doc/sink_frontends.qbk @@ -1,5 +1,5 @@ [/ - Copyright Andrey Semashev 2007 - 2015. + Copyright Andrey Semashev 2007 - 2022. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/doc/sources.qbk b/doc/sources.qbk index 0f52ecd42e..221329d551 100644 --- a/doc/sources.qbk +++ b/doc/sources.qbk @@ -1,5 +1,5 @@ [/ - Copyright Andrey Semashev 2007 - 2015. + Copyright Andrey Semashev 2007 - 2022. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/doc/todo.qbk b/doc/todo.qbk index 602f0b4e0f..161cde63c5 100644 --- a/doc/todo.qbk +++ b/doc/todo.qbk @@ -1,5 +1,5 @@ [/ - Copyright Andrey Semashev 2007 - 2015. + Copyright Andrey Semashev 2007 - 2022. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/doc/tutorial.qbk b/doc/tutorial.qbk index cc5734227c..fbb73b1220 100644 --- a/doc/tutorial.qbk +++ b/doc/tutorial.qbk @@ -1,5 +1,5 @@ [/ - Copyright Andrey Semashev 2007 - 2015. + Copyright Andrey Semashev 2007 - 2022. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -13,7 +13,7 @@ In this section we shall walk through the essential steps to get started with th [section:trivial Trivial logging] -For those who don't want to read tons of clever manuals and just need a simple tool for logging, here you go: +For those who don't want to read tons of abstruse manuals and just need a simple tool for logging, here you go: [example_tutorial_trivial] @@ -25,7 +25,7 @@ The `BOOST_LOG_TRIVIAL` macro accepts a severity level and results in a stream-l # It is safe to write logs from different threads concurrently, log messages will not be corrupted. # As will be shown later, filtering can be applied. -It must be said that the macro, along with other similar macros provided by the library, is not the only interface the library offers. It is possible to issue log records without using any macros at all. +It must be said that the macro, along with other similar macros provided by the library, is not the only interface the library offers. As will be shown later, it is possible to issue log records without using any macros at all. [endsect] @@ -69,7 +69,7 @@ The added piece is the call to the [link log.detailed.utilities.setup.convenienc You can see that the options are passed to the function in the named form. This approach is also taken in many other places of the library. You'll get used to it. The meaning of the parameters is mostly self-explaining and is documented in this manual (see [link log.detailed.sink_backends.text_file here] for what regards the text file sink). This and other convenience initialization functions are described in [link log.detailed.utilities.setup.convenience this] section. -[note You can register more than one sink. Each sink will receive and process log records as you emit them independently from others.] +[tip You can register more than one sink. Each sink will receive and process log records as you emit them independently from others.] [heading Sinks in depth: More sinks] @@ -85,7 +85,7 @@ Ok, the first thing you may have noticed about sinks is that they are composed o The [link log.detailed.sink_frontends.sync `synchronous_sink`] class template above indicates that the sink is synchronous, that is, it allows for several threads to log simultaneously and will block in case of contention. This means that the backend [link log.detailed.sink_backends.text_ostream `text_ostream_backend`] doesn't have to worry about multithreading at all. There are other sink frontends available, you can read more about them [link log.detailed.sink_frontends here]. -The [link log.detailed.sink_backends.text_ostream `text_ostream_backend`] class writes formatted log records into STL-compatible streams. We have used a file stream above but we could have used any type of stream. For example, adding output to console could look as follows: +The [link log.detailed.sink_backends.text_ostream `text_ostream_backend`] class writes formatted log records into standard library-compatible streams. We have used a file stream above but we could have used any type of stream. For example, adding output to console could look as follows: #include @@ -99,7 +99,7 @@ The [link log.detailed.sink_backends.text_ostream `text_ostream_backend`] suppor The library provides a number of [link log.detailed.sink_backends backends] that provide different log processing logic. For instance, by specifying the [link log.detailed.sink_backends.syslog syslog] backend you can send log records over the network to the syslog server, or by setting up the [link log.detailed.sink_backends.event_log Windows NT event log] backend you can monitor your application run time with the standard Windows tools. -The last thing worth noting here is the `locked_backend` member function call to access the sink backend. It is used to get thread-safe access to the backend and is provided by all sink frontends. This function returns a smart-pointer to the backend and as long as it exists the backend is locked (which means even if another thread tries to log and the log record is passed to the sink, it will not be logged until you release the backend). The only exception is the [link log.detailed.sink_frontends.unlocked `unlocked_sink`] frontend which does not synchronize at all and simply returns an unlocked pointer to the backend. +The last thing worth noting here is the `locked_backend` member function call to access the sink backend. It is used to get thread-safe exclusive access to the backend and is provided by all sink frontends. This function returns a smart-pointer to the backend and as long as it exists the backend is locked (which means even if another thread tries to log and the log record is passed to the sink, it will not be logged until you release the backend). The only exception is the [link log.detailed.sink_frontends.unlocked `unlocked_sink`] frontend which does not synchronize at all and simply returns an unlocked pointer to the backend. [endsect] @@ -107,13 +107,13 @@ The last thing worth noting here is the `locked_backend` member function call to [heading Dedicated logger objects] -Now that we have defined where and how the log is to be stored, it's time to go on and try logging. In order to do this one has to create a logging source. This would be a logger object in our case and it is as simple as that: +Now that we have defined where and how the log is to be stored, it's time to see how we emit log records. In order to be able to make log records one has to create a logging source first. This would be a logger object in our case and it is as simple as that: src::logger lg; -[note A curious reader could have noticed that we did not create any loggers for trivial logging. In fact the logger is provided by the library and is used behind the scenes.] +[note A mindful reader could have noticed that we did not create any loggers when we tried trivial logging. In that case the logger is provided by the library and is used behind the scenes.] -Unlike sinks, sources need not be registered anywhere since they interact directly with the logging core. Also note that there are two versions of loggers provided by the library: the thread-safe ones and the non-thread-safe ones. For the non-thread-safe loggers it is safe for different threads to write logs through different instances of loggers and thus there should be a separate logger for each thread that writes logs. The thread-safe counterparts can be accessed from different threads concurrently, but this will involve locking and may slow things down in case of intense logging. The thread-safe logger types have the `_mt` suffix in their name. +Unlike sinks, sources need not be registered anywhere since they interact directly with the logging core. Also note that there are two versions of loggers provided by the library: the thread-safe ones and the non-thread-safe ones. For the non-thread-safe loggers it is safe for different threads to write logs through different instances of loggers and thus there should be a separate logger for each thread that writes logs. The thread-safe counterparts can be accessed from different threads concurrently, but this will involve locking and may slow things down in case of intense contention. The thread-safe logger types have the `_mt` suffix in their name. Regardless of the thread safety, all loggers provided by the library are default and copy-constructible and support swapping, so there should be no problem in making a logger a member of your class. As you will see later, such approach can give you additional benefits. @@ -133,7 +133,7 @@ Later on you can acquire the logger like this: src::logger_mt& lg = my_logger::get(); -The `lg` will refer to the one and only instance of the logger throughout the application, even if the application consists of multiple modules. The `get` function itself is thread-safe, so there is no need in additional synchronization around it. +Then, `lg` will refer to the one and only instance of the logger throughout the application, even if the application consists of multiple modules. The `get` function itself is thread-safe, so there is no need in additional synchronization around it. [heading Writing logs] @@ -147,7 +147,7 @@ Of course, the above syntax can easily be wrapped in a macro and, in fact, users BOOST_LOG(lg) << "Hello, World!"; -Looks a bit shorter, doesn't it? The `BOOST_LOG` macro, along with other similar ones, is defined by the library. It automatically provides an STL-like stream in order to format the message with ordinary insertion expressions. Having all that code written, compiled and executed you should be able to see the "Hello, World!" record in the "sample.log" file. You will find the full code of this section [@boost:/libs/log/example/doc/tutorial_logging.cpp here]. +Looks a bit shorter, doesn't it? The `BOOST_LOG` macro, along with other similar ones, is defined by the library. It automatically provides a standard library-like output stream in order to format the message with ordinary insertion expressions. Having all that code written, compiled and executed you should be able to see the "Hello, World!" record in the "sample.log" file. You will find the full code of this section [@boost:/libs/log/example/doc/tutorial_logging.cpp here]. [endsect] @@ -155,7 +155,7 @@ Looks a bit shorter, doesn't it? The `BOOST_LOG` macro, along with other similar In previous sections we mentioned attributes and attribute values several times. Here we will discover how attributes can be used to add more data to log records. -Each log record can have a number of named attribute values attached. Attributes can represent any essential information about the conditions in which the log record occurred, such as position in the code, executable module name, current date and time, or any piece of data relevant to your particular application and execution environment. An attribute may behave as a value generator, in which case it would return a different value for each log record it's involved in. As soon as the attribute generates the value, the latter becomes independent from the creator and can be used by filters, formatters and sinks. But in order to use the attribute value one has to know its name and type, or at least a set of types it may have. There are a number of commonly used attributes implemented in the library, you can find the types of their values in the documentation. +Each log record can have a number of named attribute values attached. Attributes can represent any essential information about the conditions in which the log record occurred, such as position in code, executable module name, current date and time, or any piece of data relevant to your particular application and execution environment. An attribute may behave as a value generator, in which case it would return a different value for each log record it's involved in. As soon as the attribute generates the value, the latter becomes independent from the creator and can be used by filters, formatters and sinks. But in order to use the attribute value one has to know its name and type, or at least a set of types it may have. There are a number of commonly used attributes implemented in the library, you can find the types of their values in the documentation. Aside from that, as described in the [link log.design Design overview] section, there are three possible scopes of attributes: source-specific, thread-specific and global. When a log record is made, attribute values from these three sets are joined into a single set and passed to sinks. This implies that the origin of the attribute makes no difference for sinks. Any attribute can be registered in any scope. When registered, an attribute is given a unique name in order to make it possible to search for it. If it happens that the same named attribute is found in several scopes, the attribute from the most specific scope is taken into consideration in any further processing, including filtering and formatting. Such behavior makes it possible to override global or thread-scoped attributes with the ones registered in your local logger, thus reducing thread interference. @@ -314,7 +314,7 @@ Here `CharT` is the target character type. The formatter will be invoked wheneve [tip Record views are very similar to records. The notable distinction is that the view is immutable and implements shallow copy. Formatters and sinks only operate on record views, which prevents them from modifying the record while it can be still in use by other sinks in other threads.] -The formatted record should be composed by insertion into STL-compatible output stream `strm`. Here's an example of a custom formatter function usage: +The formatted record should be composed by insertion into standard library-compatible output stream `strm`. Here's an example of a custom formatter function usage: [example_tutorial_formatters_custom] @@ -336,11 +336,17 @@ Notably, the formatter itself contains a filter here. As you can see, the format Further goes the initialization of the two sinks. The first sink does not have any filter, which means it will save every log record to the file. We call `set_filter` on the second sink to only save log records with severity no less than `warning` or having a "Tag" attribute with value "IMPORTANT_MESSAGE". As you can see, the filter syntax resembles usual C++ very much, especially when attribute keywords are used. -Like with formatters, it is also possible to use custom functions as filters. __boost_phoenix__ can be very helpful in this case as its `bind` implementation is compatible with attribute placeholders. The previous example can be modified in the following way: +Like with formatters, it is also possible to use custom functions as filters. Fundamentally, a filter function must support the following signature: + + bool (logging::attribute_value_set const& attrs); + +When the filter is called, `attrs` will contain a complete set of attribute values, which can be used to decide whether the log record should be passed or suppressed. If the filter returns `true`, the log record will be constructed and further processed by sinks. Otherwise, the record will be discarded. + +__boost_phoenix__ can be very helpful in constructing filters. It allows to automate extraction of attribute values from the `attrs` set as its `bind` implementation is compatible with attribute placeholders. The previous example can be modified in the following way: [example_tutorial_filtering_bind] -As you can see, the custom formatter receives attribute values wrapped into the [link log.detailed.utilities.value_ref `value_ref`] template. This wrapper contains an optional reference to the attribute value of the specified type; the reference is valid if the log record contains the attribute value of the required type. The relational operators used in `my_filter` can be applied unconditionally because they will automatically return `false` if the reference is not valid. The rest is done with the `bind` expression which will recognize the `severity` and `tag_attr` keywords and extract the corresponding values before passing them to `my_filter`. +As you can see, the custom filter receives attribute values as separate arguments, wrapped into the [link log.detailed.utilities.value_ref `value_ref`] template. This wrapper contains an optional reference to the attribute value of the specified type; the reference is valid if the log record contains the attribute value of the required type. The relational operators used in `my_filter` can be applied unconditionally because they will automatically return `false` if the reference is not valid. The rest is done with the `bind` expression which will recognize the `severity` and `tag_attr` keywords and extract the corresponding values before passing them to `my_filter`. [note Because of limitations related to the integration with __boost_phoenix__ (see [ticket 7996]), it is required to explicitly specify the fallback policy in case if the attribute value is missing, when attribute keywords are used with `phoenix::bind` or `phoenix::function`. In the example above, this is done by calling `or_none`, which results in an empty [link log.detailed.utilities.value_ref `value_ref`] if the value is not found. In other contexts this policy is the default. There are [link log.detailed.expressions.attr.fallback_policies other policies] that can be used instead.] diff --git a/doc/utilities.qbk b/doc/utilities.qbk index a5db499af6..7f00d24610 100644 --- a/doc/utilities.qbk +++ b/doc/utilities.qbk @@ -1,5 +1,5 @@ [/ - Copyright Andrey Semashev 2007 - 2016. + Copyright Andrey Semashev 2007 - 2022. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -17,7 +17,7 @@ String literals are used in several places throughout the library. However, this The functionality is implemented in the [class_log_basic_string_literal] class template, which is parametrized with the character and character traits, similar to `std::basic_string`. There are also two convenience typedefs provided: `string_literal` and `wstring_literal`, for narrow and wide character types, respectively. In order to ease string literal construction in generic code there is also a `str_literal` function template that accepts a string literal and returns a [class_log_basic_string_literal] instance for the appropriate character type. -String literals support interface similar to STL strings, except for string modification functions. However, it is possible to assign to or clear string literals, as long as only string literals involved. Relational and stream output operators are also supported. +String literals support interface similar to standard library string types, except for string modification functions. However, it is possible to assign to or clear string literals, as long as only string literals involved. Relational and stream output operators are also supported. [endsect] @@ -67,7 +67,7 @@ One may notice that when using type dispatchers and defining filters and formatt [[`integral_types`] [All integral types, including `bool`, character and 64 bit integral types, if available]] [[`floating_point_types`] [Floating point types]] [[`numeric_types`] [Includes `integral_types` and `floating_point_types`]] - [[`string_types`] [Narrow and wide string types. Currently only includes STL string types and [link log.detailed.utilities.string_literal string literals].]] + [[`string_types`] [Narrow and wide string types. Currently only includes C++ standard library string types and [link log.detailed.utilities.string_literal string literals].]] ] There are also a number of time-related type sequences available: @@ -250,6 +250,139 @@ There is another manipulator called [funcref boost::log::dump_elements `dump_ele [endsect] +[section:auto_newline Automatic newline insertion] + + #include <``[boost_log_utility_manipulators_auto_newline_hpp]``> + +Sometimes it can be useful to be able to insert a newline character in the output stream, but only if it hasn't been inserted as part of the previous output. For example, if a string may or may not end with a newline, and we cannot easily tell which one it is each time. The `auto_newline` manipulator can be used to ensure that all such strings are reliably terminated with a newline and there are no duplicate newline characters. The manipulator will insert a newline unless the last character inserted into the stream before it was a newline. Its use is similar to standard stream manipulators: + + BOOST_LOG(lg) << "Parameter: " << param.name << ", value: " << param.value << logging::auto_newline; + +[note This manipulator inspects previous output to the stream, and therefore can only be used with Boost.Log streams based on [class_log_basic_formatting_ostream].] + +[endsect] + +[section:range Range manipulator] + + #include <``[boost_log_utility_manipulators_range_hpp]``> + +It can be useful to be able to output elements of a container (or, more generally, a range), preferably separated with a delimiter. The `range_manip` manipulator allows to do this: + + std::vector< int > ints{ 1, 2, 3 }; + + BOOST_LOG(lg) << "Integers: { " << logging::range_manip(ints, ", ") << " }"; // Outputs: "Integers: { 1, 2, 3 }" + +[tip This manipulator can also be used with regular output streams, not necessarily loggers.] + +Here, `range_manip` accepts two arguments - the range of elements, and, optionally, a delimiter. The range can be any type supported by __boost_range__ `begin()` and `end()` free functions, which includes any standard containers and arrays. Elements of the range must support stream insertion operator. The delimiter argument, if specified, is inserted into stream between elements. The delimiter can be any type that supports stream insertion. If delimiter is not specified, the elements are output into the stream without separation. + +[endsect] + +[section:tuple Tuple manipulator] + + #include <``[boost_log_utility_manipulators_tuple_hpp]``> + +Tuple manipulator allows to output elements of a heterogeneous sequence (put simply, a tuple) to a stream, possibly separated with a delimiter. For example: + + #include + + std::tuple< std::string, int, char > address{ "Baker Street", 221, 'b' }; + + BOOST_LOG(lg) << "Address: " << logging::tuple_manip(address, ", "); // Outputs: "Address: Baker Street, 221, b" + +[note In order to support different types of sequences, users may need to `#include` additional support headers from __boost_fusion__, like `boost/fusion/include/std_tuple.hpp` in this example. See __boost_fusion__ documentation.] + +[tip This manipulator can also be used with regular output streams, not necessarily loggers.] + +Here, `tuple_manip` accepts two arguments - the sequence of elements, and, optionally, a delimiter. The heterogeneous sequence can be any sequence supported by __boost_fusion__, for example `std::pair`, `std::tuple`, `boost::tuple`, `boost::fusion::vector` or any structure that has been adapted using one of the `BOOST_FUSION_ADAPT*` macros. Elements of the sequence must support stream insertion operator. The delimiter argument, if specified, is inserted into stream between elements. The delimiter can be any type that supports stream insertion. If delimiter is not specified, the elements are output into the stream without separation. + +[tip You can also use tuples generated by `std::tie` or `boost::tie` with this manipulator, which allows unified formatting of disjoint variables without having to copy them into a structure.] + +[endsect] + +[section:optional Optional manipulator] + + #include <``[boost_log_utility_manipulators_optional_hpp]``> + +Optional manipulator allows to output an optional value to the output stream. If the value is not present, the manipulator allows to specify a "none" marker to output instead. For example: + + boost::optional< int > param1; + boost::optional< int > param2 = 10; + + BOOST_LOG(lg) << "param1: " << logging::optional_manip(param1, "[none]") << ", param2: " << logging::optional_manip(param2, "[none]"); // Outputs: "param1: [none], param2: 10" + +[tip This manipulator can also be used with regular output streams, not necessarily loggers.] + +Here, `optional_manip` accepts two arguments - the optional value, and, optionally, a "none" marker. The optional value can be any type that supports the following: + +* Contextually convertible to `bool`, where `true` indicates that the value is present. +* Can be dereferenced using `operator*()`, and the result of this operator is the value. +* The result of dereferencing must support stream insertion operator. + +Examples of types that fit these requirements are `boost::optional` (see __boost_optional__), `std::optional` and pointers to objects. + +The "none" marker argument, if specified, is inserted into stream in case if the optional value is not present (i.e. the contextual conversion to `bool` yields `false`). The marker can be any type that supports stream insertion. If the marker is not specified, no output is produced if the value is not present. + +[endsect] + +[section:invoke Invoke manipulator] + + #include <``[boost_log_utility_manipulators_invoke_hpp]``> + +Invoke manipulator allows to inject a user-defined function call into the stream output expression. The function will be called when the manipulator is inserted into the stream. The manipulator will pass a reference to the stream to the function as the first argument, optionally followed by a number of additional arguments that were specified on the manipulator construction. For example: + + void print_signal(std::ostream& stream, int sig) + { + switch (sig) + { + case SIGINT: + stream << "SIGINT"; + break; + case SIGTERM: + stream << "SIGTERM"; + break; + default: + stream << sig; + break; + } + } + + int sig = SIGINT; + + // Outputs: "Signal received: SIGINT" + BOOST_LOG(lg) << "Signal received: " << logging::invoke_manip(&print_signal, sig); + +[tip This manipulator can also be used with regular output streams, not necessarily loggers.] + +Here, `print_signal` will be called by the manipulator when its `operator<<` is called. The output stream will be passed as the first argument and any additional arguments (`sig`, in this case) following that. The arguments are captured by value by default. One can use `std::ref`/`boost::ref` if capturing by reference is needed. + +[note Passing additional arguments to the function requires a C++14 compiler. Use `std::bind`, __boost_bind__ or C++11 lambdas to bind arguments to the function instead of passing them separately if lower C++ version is required.] + +Please note that the function will be called only if the streaming expression is executed. If the log record is discarded by filters, the streaming expression is skipped and the user's function is not called. Do not use this manipulator for injecting business-critical calls into logging expressions. + +This manipulator is especially convenient with C++11 lambdas. The above example could be rewritten like this: + + int sig = SIGINT; + + // Outputs: "Signal received: SIGINT" + BOOST_LOG(lg) << "Signal received: " << logging::invoke_manip([sig](auto& stream) + { + switch (sig) + { + case SIGINT: + stream << "SIGINT"; + break; + case SIGTERM: + stream << "SIGTERM"; + break; + default: + stream << sig; + break; + } + }); + +[endsect] + [endsect] [section:ipc Interprocess communication tools] @@ -549,6 +682,9 @@ Besides the common settings that all sinks support, some sink backends also acce [[Format] [Format string as described [link log.detailed.utilities.setup.filter_formatter here]] [Log record formatter to be used by the sink. If not specified, the default formatter is used.] ] +[[AutoNewline] ["Disabled", "AlwaysInsert" or "InsertIfMissing"] + [Controls whether the backend should automatically insert a trailing newline after every log record, see [enumref boost::log::sinks::auto_newline_mode `auto_newline_mode`]. If not specified, the default value is "InsertIfMissing".] +] [[AutoFlush] ["true" or "false"] [Enables or disables the auto-flush feature of the backend. If not specified, the default value `false` is assumed.] ] @@ -557,31 +693,37 @@ Besides the common settings that all sinks support, some sink backends also acce [table "TextFile" sink settings [[Parameter] [Format] [Description]] [[FileName] [File name pattern] - [The file name pattern for the sink backend. This parameter is mandatory.] + [The active file name pattern for the sink backend. This parameter is mandatory.] +] +[[TargetFileName] [File name pattern] + [The target file name pattern for the sink backend. If not specified, active file name is preserved after rotation.] ] [[Format] [Format string as described [link log.detailed.utilities.setup.filter_formatter here]] [Log record formatter to be used by the sink. If not specified, the default formatter is used.] ] +[[AutoNewline] ["Disabled", "AlwaysInsert" or "InsertIfMissing"] + [Controls whether the backend should automatically insert a trailing newline after every log record, see [enumref boost::log::sinks::auto_newline_mode `auto_newline_mode`]. If not specified, the default value is "InsertIfMissing".] +] [[AutoFlush] ["true" or "false"] [Enables or disables the auto-flush feature of the backend. If not specified, the default value `false` is assumed.] ] [[Append] ["true" or "false"] - [Enables or disables appending to the existing file instead of overwriting it. If not specified, the default value `false` is assumed.] + [Enables or disables [link log.detailed.sink_backends.text_file.appending appending] to the existing file instead of overwriting it. If not specified, the default value `false` is assumed.] ] [[RotationSize] [Unsigned integer] - [File size, in bytes, upon which file rotation will be performed. If not specified, no size-based rotation will be made.] + [File size, in bytes, upon which [link log.detailed.sink_backends.text_file.file_rotation file rotation] will be performed. If not specified, no size-based rotation will be made.] ] [[RotationInterval] [Unsigned integer] - [Time interval, in seconds, upon which file rotation will be performed. See also the RotationTimePoint parameter and the note below.] + [Time interval, in seconds, upon which [link log.detailed.sink_backends.text_file.file_rotation file rotation] will be performed. See also the RotationTimePoint parameter and the note below.] ] [[RotationTimePoint] [Time point format string, see below] - [Time point or a predicate that detects at what moment of time to perform log file rotation. See also the RotationInterval parameter and the note below.] + [Time point or a predicate that detects at what moment of time to perform log [link log.detailed.sink_backends.text_file.file_rotation file rotation]. See also the RotationInterval parameter and the note below.] ] [[EnableFinalRotation] ["true" or "false"] [Enables or disables final file rotation on sink destruction, which typically happens on program termination. If not specified, the default value `true` is assumed.] ] [[Target] [File system path to a directory] - [Target directory name, in which the rotated files will be stored. If this parameter is specified, rotated file collection is enabled. Otherwise the feature is not enabled, and all corresponding parameters are ignored.] + [Target directory name, in which the rotated files will be stored. If this parameter is specified, rotated [link log.detailed.sink_backends.text_file.file_collection file collection] is enabled. Otherwise the feature is not enabled and all corresponding parameters are ignored.] ] [[MaxSize] [Unsigned integer] [Total size of files in the target directory, in bytes, upon which the oldest file will be deleted. If not specified, no size-based file cleanup will be performed.] @@ -593,7 +735,7 @@ Besides the common settings that all sinks support, some sink backends also acce [Total number of files in the target directory, upon which the oldest file will be deleted. If not specified, no count-based file cleanup will be performed.] ] [[ScanForFiles] ["All" or "Matching"] - [Mode of scanning for old files in the target directory, see [enumref boost::log::sinks::file::scan_method `scan_method`]. If not specified, no scanning will be performed.] + [Mode of [link log.detailed.sink_backends.text_file.file_scanning scanning] for old files in the target directory, see [enumref boost::log::sinks::file::scan_method `scan_method`]. If not specified, no scanning will be performed.] ] ] @@ -638,28 +780,13 @@ The RotationTimePoint parameter should have one of the following formats, accord The user is free to fill the settings container from whatever settings source he needs. The usage example is below: - void init_logging() - { - logging::settings setts; - - setts["Core"]["Filter"] = "%Severity% >= warning"; - setts["Core"]["DisableLogging"] = false; - - // Subsections can be referred to with a single path - setts["Sinks.Console"]["Destination"] = "Console"; - setts["Sinks.Console"]["Filter"] = "%Severity% >= fatal"; - setts["Sinks.Console"]["AutoFlush"] = true; +[example_util_setup_settings] - // ...as well as the individual parameters - setts["Sinks.File.Destination"] = "TextFile"; - setts["Sinks.File.FileName"] = "MyApp_%3N.log"; - setts["Sinks.File.AutoFlush"] = true; - setts["Sinks.File.RotationSize"] = 10 * 1024 * 1024; // 10 MiB +[@boost:/libs/log/example/doc/util_setup_settings.cpp See the complete code]. - logging::init_from_settings(setts); - } +[note Initialization from settings does not automatically create any attributes or loggers, the application developer is still responsible for creating those. It can be said that loggers and attributes define the data that is exported by the application and the settings only describe how that data is going to be presented to the application user.] -The settings reader also allows to be extended to support custom sink types. See the [link log.extension.settings Extending the library] section for more information. +The settings reader also can be extended to support custom sink types. See the [link log.extension.settings Extending the library] section for more information. [endsect] @@ -667,7 +794,7 @@ The settings reader also allows to be extended to support custom sink types. See #include <``[boost_log_utility_setup_from_stream_hpp]``> -Support for configuration files is a frequently requested feature of the library. And despite the fact there is no ultimately convenient and flexible format of the library settings, the library provides preliminary support for this feature. The functionality is implemented with a simple function [funcref boost::log::init_from_stream `init_from_stream`], which accepts an STL input stream and reads the library settings from it. The function then passes on the read settings to the [funcref boost::log::init_from_settings `init_from_settings`] function, described [link log.detailed.utilities.setup.settings above]. Therefore the parameter names and their meaning is the same as for the [funcref boost::log::init_from_settings `init_from_settings`] function. +Support for configuration files is a frequently requested feature of the library. And despite the fact there is no ultimately convenient and flexible format of the library settings, the library provides preliminary support for this feature. The functionality is implemented with a simple function [funcref boost::log::init_from_stream `init_from_stream`], which accepts a `std::istream` and reads the library settings from it. The function then passes on the read settings to the [funcref boost::log::init_from_settings `init_from_settings`] function, described [link log.detailed.utilities.setup.settings above]. Therefore the parameter names and their meaning is the same as for the [funcref boost::log::init_from_settings `init_from_settings`] function. The settings format is quite simple and widely used. Below is the description of syntax and parameters. @@ -712,6 +839,8 @@ Here's the usage example: return 0; } +See a [@boost:/libs/log/example/settings_file/main.cpp more complete example]. + [endsect] [endsect] diff --git a/example/Jamfile.v2 b/example/Jamfile.v2 index 6813a2343e..07ad6ccf5a 100644 --- a/example/Jamfile.v2 +++ b/example/Jamfile.v2 @@ -14,7 +14,7 @@ build-project ./multiple_files ; build-project ./multiple_threads ; build-project ./rotating_file ; build-project ./settings_file ; -build-project ./settings_file_formatter_factory ; +build-project ./settings_file_custom_factories ; build-project ./syslog ; build-project ./native_syslog ; build-project ./wide_char ; diff --git a/example/advanced_usage/Jamfile.v2 b/example/advanced_usage/Jamfile.v2 index 03ea037964..2bebc7a6a1 100644 --- a/example/advanced_usage/Jamfile.v2 +++ b/example/advanced_usage/Jamfile.v2 @@ -5,24 +5,21 @@ # http://www.boost.org/LICENSE_1_0.txt) # +import ../../build/log-platform-config ; + project : requirements + @log-platform-config.set-platform-defines + shared:BOOST_ALL_DYN_LINK - msvc:_SCL_SECURE_NO_WARNINGS - msvc:_SCL_SECURE_NO_DEPRECATE - msvc:_CRT_SECURE_NO_WARNINGS - msvc:_CRT_SECURE_NO_DEPRECATE + + 1024 + msvc:/bigobj msvc:/wd4503 # decorated name length exceeded, name was truncated msvc:/wd4456 # declaration of 'A' hides previous local declaration msvc:/wd4459 # declaration of 'A' hides global declaration msvc:/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion - intel-win:_SCL_SECURE_NO_WARNINGS - intel-win:_SCL_SECURE_NO_DEPRECATE - intel-win:_CRT_SECURE_NO_WARNINGS - intel-win:_CRT_SECURE_NO_DEPRECATE - darwin:-ftemplate-depth-1024 - gcc:-ftemplate-depth-1024 gcc:-fno-strict-aliasing # avoids strict aliasing violations in other Boost components # Disable Intel warnings: @@ -44,8 +41,6 @@ project /boost/log//boost_log /boost/date_time//boost_date_time /boost/filesystem//boost_filesystem - /boost/system//boost_system - /boost/thread//boost_thread multi ; diff --git a/example/async_log/Jamfile.v2 b/example/async_log/Jamfile.v2 index 7af11ffeb8..393086ff4f 100644 --- a/example/async_log/Jamfile.v2 +++ b/example/async_log/Jamfile.v2 @@ -5,24 +5,21 @@ # http://www.boost.org/LICENSE_1_0.txt) # +import ../../build/log-platform-config ; + project : requirements + @log-platform-config.set-platform-defines + shared:BOOST_ALL_DYN_LINK - msvc:_SCL_SECURE_NO_WARNINGS - msvc:_SCL_SECURE_NO_DEPRECATE - msvc:_CRT_SECURE_NO_WARNINGS - msvc:_CRT_SECURE_NO_DEPRECATE + + 1024 + msvc:/bigobj msvc:/wd4503 # decorated name length exceeded, name was truncated msvc:/wd4456 # declaration of 'A' hides previous local declaration msvc:/wd4459 # declaration of 'A' hides global declaration msvc:/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion - intel-win:_SCL_SECURE_NO_WARNINGS - intel-win:_SCL_SECURE_NO_DEPRECATE - intel-win:_CRT_SECURE_NO_WARNINGS - intel-win:_CRT_SECURE_NO_DEPRECATE - darwin:-ftemplate-depth-1024 - gcc:-ftemplate-depth-1024 gcc:-fno-strict-aliasing # avoids strict aliasing violations in other Boost components # Disable Intel warnings: @@ -44,8 +41,6 @@ project /boost/log//boost_log /boost/date_time//boost_date_time /boost/filesystem//boost_filesystem - /boost/system//boost_system - /boost/thread//boost_thread multi ; diff --git a/example/async_log/main.cpp b/example/async_log/main.cpp index 26763a4bc1..99a0687382 100644 --- a/example/async_log/main.cpp +++ b/example/async_log/main.cpp @@ -15,16 +15,13 @@ // #define BOOST_LOG_DYN_LINK 1 #include +#include #include #include #include -#include -#include -#include #include #include -#include -#include +#include #include #include @@ -51,13 +48,13 @@ enum BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(test_lg, src::logger_mt) //! This function is executed in multiple threads -void thread_fun(boost::barrier& bar) +void thread_fun(boost::compat::latch& latch) { // Wait until all threads are created - bar.wait(); + latch.arrive_and_wait(); // Here we go. First, identify the thread. - BOOST_LOG_SCOPED_THREAD_TAG("ThreadID", boost::this_thread::get_id()); + BOOST_LOG_SCOPED_THREAD_TAG("ThreadID", std::this_thread::get_id()); // Now, do some logging for (unsigned int i = 0; i < LOG_RECORDS_TO_WRITE; ++i) @@ -86,7 +83,7 @@ int main(int argc, char* argv[]) shared_ptr< sink_t > sink(new sink_t( boost::make_shared< backend_t >(), // We'll apply record ordering to ensure that records from different threads go sequentially in the file - keywords::order = logging::make_attr_ordering("RecordID", std::less< unsigned int >()))); + keywords::order = logging::make_attr_ordering< unsigned int >("RecordID", std::less< unsigned int >()))); sink->locked_backend()->add_stream(strm); @@ -95,7 +92,7 @@ int main(int argc, char* argv[]) expr::format("%1%: [%2%] [%3%] - %4%") % expr::attr< unsigned int >("RecordID") % expr::attr< boost::posix_time::ptime >("TimeStamp") - % expr::attr< boost::thread::id >("ThreadID") + % expr::attr< std::thread::id >("ThreadID") % expr::smessage ); @@ -107,13 +104,14 @@ int main(int argc, char* argv[]) logging::core::get()->add_global_attribute("RecordID", attrs::counter< unsigned int >()); // Create logging threads - boost::barrier bar(THREAD_COUNT); - boost::thread_group threads; + boost::compat::latch latch(THREAD_COUNT); + std::thread threads[THREAD_COUNT]; for (unsigned int i = 0; i < THREAD_COUNT; ++i) - threads.create_thread(boost::bind(&thread_fun, boost::ref(bar))); + threads[i] = std::thread([&latch]() { thread_fun(latch); }); // Wait until all action ends - threads.join_all(); + for (unsigned int i = 0; i < THREAD_COUNT; ++i) + threads[i].join(); // Flush all buffered records sink->stop(); diff --git a/example/basic_usage/Jamfile.v2 b/example/basic_usage/Jamfile.v2 index b46fd3e0d5..8f4acd7dd5 100644 --- a/example/basic_usage/Jamfile.v2 +++ b/example/basic_usage/Jamfile.v2 @@ -5,24 +5,21 @@ # http://www.boost.org/LICENSE_1_0.txt) # +import ../../build/log-platform-config ; + project : requirements + @log-platform-config.set-platform-defines + shared:BOOST_ALL_DYN_LINK - msvc:_SCL_SECURE_NO_WARNINGS - msvc:_SCL_SECURE_NO_DEPRECATE - msvc:_CRT_SECURE_NO_WARNINGS - msvc:_CRT_SECURE_NO_DEPRECATE + + 1024 + msvc:/bigobj msvc:/wd4503 # decorated name length exceeded, name was truncated msvc:/wd4456 # declaration of 'A' hides previous local declaration msvc:/wd4459 # declaration of 'A' hides global declaration msvc:/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion - intel-win:_SCL_SECURE_NO_WARNINGS - intel-win:_SCL_SECURE_NO_DEPRECATE - intel-win:_CRT_SECURE_NO_WARNINGS - intel-win:_CRT_SECURE_NO_DEPRECATE - darwin:-ftemplate-depth-1024 - gcc:-ftemplate-depth-1024 gcc:-fno-strict-aliasing # avoids strict aliasing violations in other Boost components # Disable Intel warnings: @@ -45,9 +42,7 @@ project /boost/log//boost_log_setup /boost/date_time//boost_date_time /boost/filesystem//boost_filesystem - /boost/system//boost_system single:BOOST_LOG_NO_THREADS - multi:/boost/thread//boost_thread ; exe basic_usage diff --git a/example/bounded_async_log/Jamfile.v2 b/example/bounded_async_log/Jamfile.v2 index be5887e517..4ed0294b3d 100644 --- a/example/bounded_async_log/Jamfile.v2 +++ b/example/bounded_async_log/Jamfile.v2 @@ -5,24 +5,21 @@ # http://www.boost.org/LICENSE_1_0.txt) # +import ../../build/log-platform-config ; + project : requirements + @log-platform-config.set-platform-defines + shared:BOOST_ALL_DYN_LINK - msvc:_SCL_SECURE_NO_WARNINGS - msvc:_SCL_SECURE_NO_DEPRECATE - msvc:_CRT_SECURE_NO_WARNINGS - msvc:_CRT_SECURE_NO_DEPRECATE + + 1024 + msvc:/bigobj msvc:/wd4503 # decorated name length exceeded, name was truncated msvc:/wd4456 # declaration of 'A' hides previous local declaration msvc:/wd4459 # declaration of 'A' hides global declaration msvc:/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion - intel-win:_SCL_SECURE_NO_WARNINGS - intel-win:_SCL_SECURE_NO_DEPRECATE - intel-win:_CRT_SECURE_NO_WARNINGS - intel-win:_CRT_SECURE_NO_DEPRECATE - darwin:-ftemplate-depth-1024 - gcc:-ftemplate-depth-1024 gcc:-fno-strict-aliasing # avoids strict aliasing violations in other Boost components # Disable Intel warnings: @@ -44,8 +41,6 @@ project /boost/log//boost_log /boost/date_time//boost_date_time /boost/filesystem//boost_filesystem - /boost/system//boost_system - /boost/thread//boost_thread multi ; diff --git a/example/bounded_async_log/main.cpp b/example/bounded_async_log/main.cpp index eefeb52af7..9bbd8cc86a 100644 --- a/example/bounded_async_log/main.cpp +++ b/example/bounded_async_log/main.cpp @@ -15,16 +15,13 @@ // #define BOOST_LOG_DYN_LINK 1 #include +#include #include #include #include -#include -#include -#include #include #include -#include -#include +#include #include #include @@ -51,13 +48,13 @@ enum BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(test_lg, src::logger_mt) //! This function is executed in multiple threads -void thread_fun(boost::barrier& bar) +void thread_fun(boost::compat::latch& latch) { // Wait until all threads are created - bar.wait(); + latch.arrive_and_wait(); // Here we go. First, identify the thread. - BOOST_LOG_SCOPED_THREAD_TAG("ThreadID", boost::this_thread::get_id()); + BOOST_LOG_SCOPED_THREAD_TAG("ThreadID", std::this_thread::get_id()); // Now, do some logging for (unsigned int i = 0; i < LOG_RECORDS_TO_WRITE; ++i) @@ -88,7 +85,7 @@ int main(int argc, char* argv[]) shared_ptr< sink_t > sink(new sink_t( boost::make_shared< backend_t >(), // We'll apply record ordering to ensure that records from different threads go sequentially in the file - keywords::order = logging::make_attr_ordering("RecordID", std::less< unsigned int >()))); + keywords::order = logging::make_attr_ordering< unsigned int >("RecordID", std::less< unsigned int >()))); sink->locked_backend()->add_stream(strm); @@ -97,7 +94,7 @@ int main(int argc, char* argv[]) expr::format("%1%: [%2%] [%3%] - %4%") % expr::attr< unsigned int >("RecordID") % expr::attr< boost::posix_time::ptime >("TimeStamp") - % expr::attr< boost::thread::id >("ThreadID") + % expr::attr< std::thread::id >("ThreadID") % expr::smessage ); @@ -109,13 +106,14 @@ int main(int argc, char* argv[]) logging::core::get()->add_global_attribute("RecordID", attrs::counter< unsigned int >()); // Create logging threads - boost::barrier bar(THREAD_COUNT); - boost::thread_group threads; + boost::compat::latch latch(THREAD_COUNT); + std::thread threads[THREAD_COUNT]; for (unsigned int i = 0; i < THREAD_COUNT; ++i) - threads.create_thread(boost::bind(&thread_fun, boost::ref(bar))); + threads[i] = std::thread([&latch]() { thread_fun(latch); }); // Wait until all action ends - threads.join_all(); + for (unsigned int i = 0; i < THREAD_COUNT; ++i) + threads[i].join(); // Flush all buffered records sink->stop(); diff --git a/example/doc/Jamfile.v2 b/example/doc/Jamfile.v2 index ead4e80c94..c1786d9b12 100644 --- a/example/doc/Jamfile.v2 +++ b/example/doc/Jamfile.v2 @@ -6,25 +6,63 @@ # import path ; +import configure ; +import ../../build/log-platform-config ; + +rule has-config-flag ( flag : properties * ) +{ + if ( "$(flag)" in $(properties) || "$(flag)=1" in $(properties) ) + { + return 1 ; + } + else + { + return ; + } +} + +rule check-message-compiler ( properties * ) +{ + local result ; + + if windows in $(properties) + { + if ! [ has-config-flag BOOST_LOG_WITHOUT_EVENT_LOG : $(properties) ] + { + local has_mc = [ configure.builds /boost/log/config/message-compiler//test-availability : $(properties) : message-compiler ] ; + if ! $(has_mc) + { + result = BOOST_LOG_WITHOUT_EVENT_LOG ; + } + } + else + { + # This branch is needed to fix building with MinGW + result = BOOST_LOG_WITHOUT_EVENT_LOG ; + } + } + else + { + result = BOOST_LOG_WITHOUT_EVENT_LOG ; + } + + return $(result) ; +} project : requirements + @log-platform-config.set-platform-defines + @check-message-compiler + shared:BOOST_ALL_DYN_LINK - msvc:_SCL_SECURE_NO_WARNINGS - msvc:_SCL_SECURE_NO_DEPRECATE - msvc:_CRT_SECURE_NO_WARNINGS - msvc:_CRT_SECURE_NO_DEPRECATE + + 1024 + msvc:/bigobj msvc:/wd4503 # decorated name length exceeded, name was truncated msvc:/wd4456 # declaration of 'A' hides previous local declaration msvc:/wd4459 # declaration of 'A' hides global declaration msvc:/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion - intel-win:_SCL_SECURE_NO_WARNINGS - intel-win:_SCL_SECURE_NO_DEPRECATE - intel-win:_CRT_SECURE_NO_WARNINGS - intel-win:_CRT_SECURE_NO_DEPRECATE - darwin:-ftemplate-depth-1024 - gcc:-ftemplate-depth-1024 gcc:-fno-strict-aliasing # avoids strict aliasing violations in other Boost components # Disable Intel warnings: @@ -47,25 +85,27 @@ project /boost/log//boost_log_setup /boost/date_time//boost_date_time /boost/filesystem//boost_filesystem - /boost/system//boost_system /boost/thread//boost_thread + /boost/lambda//boost_lambda + /boost/scope_exit//boost_scope_exit + /boost/format//boost_format multi ; # Compiles each .cpp file in this directory into a separate executable rule compile_all { - #ECHO executing compile_all rule ; - local all_rules = ; - for local file in [ glob *.cpp ] - { - local exename = [ MATCH "([^.]*).cpp$" : [ path.basename $(file) ] ] ; - #ECHO "exename = $(exename)" ; - all_rules += [ exe $(exename) : $(file) ] ; - } + #ECHO executing compile_all rule ; + local all_rules = ; + for local file in [ glob *.cpp ] + { + local exename = [ MATCH "([^.]*).cpp$" : [ path.basename $(file) ] ] ; + #ECHO "exename = $(exename)" ; + all_rules += [ exe $(exename) : $(file) ] ; + } - #ECHO $(all_rules) ; - return $(all_rules) ; + #ECHO $(all_rules) ; + return $(all_rules) ; } compile_all ; diff --git a/example/doc/expressions_filter_adapt_function.cpp b/example/doc/expressions_filter_adapt_function.cpp new file mode 100644 index 0000000000..1e325660d8 --- /dev/null +++ b/example/doc/expressions_filter_adapt_function.cpp @@ -0,0 +1,103 @@ +/* + * Copyright Andrey Semashev 2025. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace logging = boost::log; +namespace src = boost::log::sources; +namespace expr = boost::log::expressions; +namespace keywords = boost::log::keywords; + +enum severity_level +{ + normal, + notification, + warning, + error, + critical +}; + +std::ostream& operator<< (std::ostream& strm, severity_level level) +{ + static const char* strings[] = + { + "normal", + "notification", + "warning", + "error", + "critical" + }; + + if (static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings)) + strm << strings[level]; + else + strm << static_cast< int >(level); + + return strm; +} + +//[ example_expressions_filter_adapt_function +// Define the attribute keywords +BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", severity_level) +BOOST_LOG_ATTRIBUTE_KEYWORD(channel, "Channel", std::string) + +// User-defined filter function +bool my_filter(logging::attribute_value_set const& values) +{ + // Filter implementation. Returns true if the log record passed the filter and false otherwise. + return values[channel] == "network"; +} + +namespace lazy { +BOOST_PHOENIX_ADAPT_FUNCTION(bool, my_filter, ::my_filter, 1) +} + +void init() +{ + logging::add_console_log + ( + std::clog, + keywords::filter = + severity >= critical || lazy::my_filter(boost::phoenix::placeholders::_1), + keywords::format = + expr::stream << "<" << severity << "> [" << channel << "] " << expr::smessage + ); +} +//] + +// Define our logger type +typedef src::severity_channel_logger< severity_level, std::string > logger_type; + +void test_logging(logger_type& lg, std::string const& channel_name) +{ + BOOST_LOG_CHANNEL_SEV(lg, channel_name, normal) << "A normal severity level message"; + BOOST_LOG_CHANNEL_SEV(lg, channel_name, notification) << "A notification severity level message"; + BOOST_LOG_CHANNEL_SEV(lg, channel_name, warning) << "A warning severity level message"; + BOOST_LOG_CHANNEL_SEV(lg, channel_name, error) << "An error severity level message"; + BOOST_LOG_CHANNEL_SEV(lg, channel_name, critical) << "A critical severity level message"; +} + +int main(int, char*[]) +{ + init(); + logging::add_common_attributes(); + + logger_type lg; + test_logging(lg, "general"); + test_logging(lg, "network"); + test_logging(lg, "gui"); + test_logging(lg, "filesystem"); + + return 0; +} diff --git a/example/doc/expressions_formatter_adapt_function.cpp b/example/doc/expressions_formatter_adapt_function.cpp new file mode 100644 index 0000000000..4eb928a556 --- /dev/null +++ b/example/doc/expressions_formatter_adapt_function.cpp @@ -0,0 +1,104 @@ +/* + * Copyright Andrey Semashev 2025. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace logging = boost::log; +namespace src = boost::log::sources; +namespace expr = boost::log::expressions; +namespace keywords = boost::log::keywords; + +enum severity_level +{ + normal, + notification, + warning, + error, + critical +}; + +std::ostream& operator<< (std::ostream& strm, severity_level level) +{ + static const char* strings[] = + { + "normal", + "notification", + "warning", + "error", + "critical" + }; + + if (static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings)) + strm << strings[level]; + else + strm << static_cast< int >(level); + + return strm; +} + +//[ example_expressions_formatter_adapt_function +// Define the attribute keywords +BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", severity_level) +BOOST_LOG_ATTRIBUTE_KEYWORD(channel, "Channel", std::string) + +// User-defined formatting function +std::string extract_channel(logging::record_view const& rec) +{ + logging::value_ref< std::string, tag::channel > chan = rec[channel]; + if (chan) + return chan.get(); + else + return "-"; +} + +namespace lazy { +BOOST_PHOENIX_ADAPT_FUNCTION(std::string, extract_channel, ::extract_channel, 1) +} + +void init() +{ + logging::add_console_log + ( + std::clog, + keywords::format = + expr::stream << "<" << severity << "> [" + << lazy::extract_channel(boost::phoenix::placeholders::_1) + << "] " << expr::smessage + ); +} +//] + +int main(int, char*[]) +{ + init(); + logging::add_common_attributes(); + + src::severity_channel_logger< severity_level, std::string > lg1; + BOOST_LOG_CHANNEL_SEV(lg1, "general", normal) << "A normal severity level message"; + BOOST_LOG_CHANNEL_SEV(lg1, "general", notification) << "A notification severity level message"; + BOOST_LOG_CHANNEL_SEV(lg1, "general", warning) << "A warning severity level message"; + BOOST_LOG_CHANNEL_SEV(lg1, "general", error) << "An error severity level message"; + BOOST_LOG_CHANNEL_SEV(lg1, "general", critical) << "A critical severity level message"; + + src::severity_logger< severity_level > lg2; + BOOST_LOG_SEV(lg2, normal) << "A normal severity level message"; + BOOST_LOG_SEV(lg2, notification) << "A notification severity level message"; + BOOST_LOG_SEV(lg2, warning) << "A warning severity level message"; + BOOST_LOG_SEV(lg2, error) << "An error severity level message"; + BOOST_LOG_SEV(lg2, critical) << "A critical severity level message"; + + return 0; +} diff --git a/example/doc/expressions_wrap_filter.cpp b/example/doc/expressions_wrap_filter.cpp new file mode 100644 index 0000000000..323a10b887 --- /dev/null +++ b/example/doc/expressions_wrap_filter.cpp @@ -0,0 +1,98 @@ +/* + * Copyright Andrey Semashev 2025. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace logging = boost::log; +namespace src = boost::log::sources; +namespace expr = boost::log::expressions; +namespace keywords = boost::log::keywords; + +enum severity_level +{ + normal, + notification, + warning, + error, + critical +}; + +std::ostream& operator<< (std::ostream& strm, severity_level level) +{ + static const char* strings[] = + { + "normal", + "notification", + "warning", + "error", + "critical" + }; + + if (static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings)) + strm << strings[level]; + else + strm << static_cast< int >(level); + + return strm; +} + +//[ example_expressions_wrap_filter +// Define the attribute keywords +BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", severity_level) +BOOST_LOG_ATTRIBUTE_KEYWORD(channel, "Channel", std::string) + +// User-defined filter function +bool my_filter(logging::attribute_value_set const& values) +{ + // Filter implementation. Returns true if the log record passed the filter and false otherwise. + return values[channel] == "network"; +} + +void init() +{ + logging::add_console_log + ( + std::clog, + keywords::filter = + severity >= critical || expr::wrap_filter(&my_filter), + keywords::format = + expr::stream << "<" << severity << "> [" << channel << "] " << expr::smessage + ); +} +//] + +// Define our logger type +typedef src::severity_channel_logger< severity_level, std::string > logger_type; + +void test_logging(logger_type& lg, std::string const& channel_name) +{ + BOOST_LOG_CHANNEL_SEV(lg, channel_name, normal) << "A normal severity level message"; + BOOST_LOG_CHANNEL_SEV(lg, channel_name, notification) << "A notification severity level message"; + BOOST_LOG_CHANNEL_SEV(lg, channel_name, warning) << "A warning severity level message"; + BOOST_LOG_CHANNEL_SEV(lg, channel_name, error) << "An error severity level message"; + BOOST_LOG_CHANNEL_SEV(lg, channel_name, critical) << "A critical severity level message"; +} + +int main(int, char*[]) +{ + init(); + logging::add_common_attributes(); + + logger_type lg; + test_logging(lg, "general"); + test_logging(lg, "network"); + test_logging(lg, "gui"); + test_logging(lg, "filesystem"); + + return 0; +} diff --git a/example/doc/expressions_wrap_formatter.cpp b/example/doc/expressions_wrap_formatter.cpp new file mode 100644 index 0000000000..138b7ad708 --- /dev/null +++ b/example/doc/expressions_wrap_formatter.cpp @@ -0,0 +1,116 @@ +/* + * Copyright Andrey Semashev 2025. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace logging = boost::log; +namespace attrs = boost::log::attributes; +namespace src = boost::log::sources; +namespace expr = boost::log::expressions; +namespace keywords = boost::log::keywords; + +enum severity_level +{ + normal, + notification, + warning, + error, + critical +}; + +std::ostream& operator<< (std::ostream& strm, severity_level level) +{ + static const char* strings[] = + { + "normal", + "notification", + "warning", + "error", + "critical" + }; + + if (static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings)) + strm << strings[level]; + else + strm << static_cast< int >(level); + + return strm; +} + +//[ example_expressions_wrap_formatter +// Define a custom attribute value type +struct host_address +{ + std::string hostname; + std::uint_least16_t port = 0; +}; + +// Define the attribute keywords +BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", severity_level) +BOOST_LOG_ATTRIBUTE_KEYWORD(host, "Host", host_address) + +// User-defined formatter for the Host attribute values +void format_host(logging::record_view const& rec, logging::formatting_ostream& stream) +{ + // Formatter implementation outputs directly into the formatting stream + logging::value_ref< host_address, tag::host > host_ref = rec[host]; + if (host_ref) + { + stream << host_ref->hostname; + + if (host_ref->port != 0) + stream << ":" << host_ref->port; + } + else + { + stream << "-"; + } +} + +void init() +{ + logging::add_console_log + ( + std::clog, + keywords::format = + expr::stream << "<" << severity << "> [" + << expr::wrap_formatter< char >(&format_host) + << "] " << expr::smessage + ); +} +//] + +int main(int, char*[]) +{ + init(); + logging::add_common_attributes(); + + src::severity_logger< severity_level > lg1; + + host_address h; + h.hostname = "hostname"; + h.port = 1234; + lg1.add_attribute(host.get_name(), attrs::make_constant(h)); + + BOOST_LOG_SEV(lg1, normal) << "Connection established"; + BOOST_LOG_SEV(lg1, normal) << "Connection closed"; + + src::severity_logger< severity_level > lg2; + BOOST_LOG_SEV(lg2, normal) << "Message not associated with a host"; + + return 0; +} diff --git a/example/doc/extension_app_launcher.cpp b/example/doc/extension_app_launcher.cpp index eafa0bd044..a72e1a4a39 100644 --- a/example/doc/extension_app_launcher.cpp +++ b/example/doc/extension_app_launcher.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -47,7 +48,8 @@ class app_launcher : // The function consumes the log records that come from the frontend void app_launcher::consume(logging::record_view const& rec, string_type const& command_line) { - std::system(command_line.c_str()); + int res = std::system(command_line.c_str()); + boost::ignore_unused(res); } //] diff --git a/example/doc/extension_filter_parser.cpp b/example/doc/extension_filter_parser.cpp index 016f400830..1b9721fa3c 100644 --- a/example/doc/extension_filter_parser.cpp +++ b/example/doc/extension_filter_parser.cpp @@ -6,11 +6,11 @@ */ #include +#include #include #include #include #include -#include #include #include #include @@ -110,12 +110,23 @@ class point_filter_factory : logging::filter on_equality_relation(logging::attribute_name const& name, string_type const& arg) { - return expr::attr< point >(name) == boost::lexical_cast< point >(arg); + return expr::attr< point >(name) == parse_argument(arg); } logging::filter on_inequality_relation(logging::attribute_name const& name, string_type const& arg) { - return expr::attr< point >(name) != boost::lexical_cast< point >(arg); + return expr::attr< point >(name) != parse_argument(arg); + } + +private: + static point parse_argument(string_type const& arg) + { + std::istringstream strm(arg); + point val; + strm >> val; + if (strm.fail() || strm.bad()) + throw std::runtime_error("Failed to parse point from \"" + arg + "\""); + return val; } }; diff --git a/example/doc/extension_filter_parser_custom_rel.cpp b/example/doc/extension_filter_parser_custom_rel.cpp index 0498a0059f..7390b9c65e 100644 --- a/example/doc/extension_filter_parser_custom_rel.cpp +++ b/example/doc/extension_filter_parser_custom_rel.cpp @@ -6,11 +6,11 @@ */ #include +#include #include #include #include #include -#include #include #include #include @@ -142,22 +142,34 @@ class point_filter_factory : logging::filter on_equality_relation(logging::attribute_name const& name, string_type const& arg) { - return expr::attr< point >(name) == boost::lexical_cast< point >(arg); + return expr::attr< point >(name) == parse_argument< point >(arg); } logging::filter on_inequality_relation(logging::attribute_name const& name, string_type const& arg) { - return expr::attr< point >(name) != boost::lexical_cast< point >(arg); + return expr::attr< point >(name) != parse_argument< point >(arg); } logging::filter on_custom_relation(logging::attribute_name const& name, string_type const& rel, string_type const& arg) { if (rel == "is_in_rectangle") { - return boost::phoenix::bind(&is_in_rectangle, expr::attr< point >(name), boost::lexical_cast< rectangle >(arg)); + return boost::phoenix::bind(&is_in_rectangle, expr::attr< point >(name), parse_argument< rectangle >(arg)); } throw std::runtime_error("Unsupported filter relation: " + rel); } + +private: + template< typename ArgumentT > + static ArgumentT parse_argument(string_type const& arg) + { + std::istringstream strm(arg); + ArgumentT val; + strm >> val; + if (strm.fail() || strm.bad()) + throw std::runtime_error("Failed to parse argument from \"" + arg + "\""); + return val; + } }; void init_factories() diff --git a/example/doc/extension_record_tagger.cpp b/example/doc/extension_record_tagger.cpp index 99cc7cd0db..e7fbfbf52a 100644 --- a/example/doc/extension_record_tagger.cpp +++ b/example/doc/extension_record_tagger.cpp @@ -5,6 +5,7 @@ * http://www.boost.org/LICENSE_1_0.txt) */ +#include #include #include #include @@ -13,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -65,7 +65,7 @@ class record_tagger_feature : // The method will require locking, so we have to define locking requirements for it. // We use the strictest_lock trait in order to choose the most restricting lock type. typedef typename logging::strictest_lock< - boost::lock_guard< threading_model >, + std::lock_guard< threading_model >, typename BaseT::open_record_lock, typename BaseT::add_attribute_lock, typename BaseT::remove_attribute_lock diff --git a/example/doc/extension_stat_collector_settings.cpp b/example/doc/extension_stat_collector_settings.cpp index b333c4e1c1..6d81cb24c7 100644 --- a/example/doc/extension_stat_collector_settings.cpp +++ b/example/doc/extension_stat_collector_settings.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -169,7 +168,7 @@ class stat_collector_factory : boost::posix_time::time_duration write_interval = boost::posix_time::minutes(1); if (boost::optional< std::string > param = settings["WriteInterval"]) { - unsigned int sec = boost::lexical_cast< unsigned int >(param.get()); + unsigned long sec = std::stoul(param.get()); write_interval = boost::posix_time::seconds(sec); } diff --git a/example/doc/sinks_async_ordering.cpp b/example/doc/sinks_async_ordering.cpp index b642b5b6ac..3c2fd10663 100644 --- a/example/doc/sinks_async_ordering.cpp +++ b/example/doc/sinks_async_ordering.cpp @@ -5,13 +5,13 @@ * http://www.boost.org/LICENSE_1_0.txt) */ +#include #include #include #include #include #include #include -#include #include #include #include @@ -60,9 +60,9 @@ boost::shared_ptr< sink_t > init_logging() // Wrap it into the frontend and register in the core boost::shared_ptr< sink_t > sink(new sink_t( backend, /*< pointer to the pre-initialized backend >*/ - keywords::order = - logging::make_attr_ordering("LineID", std::less< unsigned int >()), /*< log record ordering predicate >*/ - keywords::ordering_window = boost::posix_time::seconds(1) /*< latency of log record processing >*/ + keywords::order = logging::make_attr_ordering< unsigned int >( /*< log record ordering predicate >*/ + "LineID", std::less< unsigned int >()), + keywords::ordering_window = std::chrono::seconds(1) /*< latency of log record processing >*/ )); core->add_sink(sink); diff --git a/example/doc/sinks_file.cpp b/example/doc/sinks_file.cpp index 8a3b552f47..79f213b1eb 100644 --- a/example/doc/sinks_file.cpp +++ b/example/doc/sinks_file.cpp @@ -27,7 +27,8 @@ void init_logging() boost::shared_ptr< sinks::text_file_backend > backend = boost::make_shared< sinks::text_file_backend >( - keywords::file_name = "file_%5N.log", /*< file name pattern >*/ + keywords::file_name = "file.log", /*< active file name pattern >*/ + keywords::target_file_name = "file_%5N.log", /*< target file name pattern >*/ keywords::rotation_size = 5 * 1024 * 1024, /*< rotate the file upon reaching 5 MiB size... >*/ keywords::time_based_rotation = sinks::file::rotation_at_time_point(12, 0, 0) /*< ...or every day, at noon, whichever comes first >*/ ); diff --git a/example/doc/sinks_ipc_logger.cpp b/example/doc/sinks_ipc_logger.cpp index f5adeb23db..d203370ad9 100644 --- a/example/doc/sinks_ipc_logger.cpp +++ b/example/doc/sinks_ipc_logger.cpp @@ -6,6 +6,8 @@ * http://www.boost.org/LICENSE_1_0.txt) */ +#if !defined(BOOST_LOG_WITHOUT_IPC) + #include #include #include @@ -81,3 +83,12 @@ int main() return 0; } //] + +#else // !defined(BOOST_LOG_WITHOUT_IPC) + +int main() +{ + return 0; +} + +#endif // !defined(BOOST_LOG_WITHOUT_IPC) diff --git a/example/doc/sinks_ipc_receiver.cpp b/example/doc/sinks_ipc_receiver.cpp index 70e304693c..9fb4a7b724 100644 --- a/example/doc/sinks_ipc_receiver.cpp +++ b/example/doc/sinks_ipc_receiver.cpp @@ -6,6 +6,8 @@ * http://www.boost.org/LICENSE_1_0.txt) */ +#if !defined(BOOST_LOG_WITHOUT_IPC) + #include #include #include @@ -55,3 +57,12 @@ int main() return 0; } //] + +#else // !defined(BOOST_LOG_WITHOUT_IPC) + +int main() +{ + return 0; +} + +#endif // !defined(BOOST_LOG_WITHOUT_IPC) diff --git a/example/doc/sinks_syslog.cpp b/example/doc/sinks_syslog.cpp index 6262304b01..25b0ffea97 100644 --- a/example/doc/sinks_syslog.cpp +++ b/example/doc/sinks_syslog.cpp @@ -54,7 +54,7 @@ void init_native_syslog() //-> //<- -#if !defined(BOOST_LOG_NO_ASIO) +#if !defined(BOOST_LOG_WITHOUT_ASIO) //-> void init_builtin_syslog() { @@ -89,7 +89,7 @@ int main(int, char*[]) { #if defined(BOOST_LOG_USE_NATIVE_SYSLOG) init_native_syslog(); -#elif !defined(BOOST_LOG_NO_ASIO) +#elif !defined(BOOST_LOG_WITHOUT_ASIO) init_builtin_syslog(); #endif diff --git a/example/doc/util_dynamic_type_disp.cpp b/example/doc/util_dynamic_type_disp.cpp index 7f0e3caf3d..b0ad722700 100644 --- a/example/doc/util_dynamic_type_disp.cpp +++ b/example/doc/util_dynamic_type_disp.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include namespace logging = boost::log; @@ -85,13 +86,16 @@ int main(int, char*[]) // These two attributes are supported by the dispatcher bool res = print(my_value< std::string >("Hello world!")); assert(res); + boost::ignore_unused(res); res = print(my_value< double >(1.2)); assert(res); + boost::ignore_unused(res); // This one is not res = print(my_value< float >(-4.3f)); assert(!res); + boost::ignore_unused(res); return 0; } diff --git a/example/doc/util_ipc_reliable_mq_writer.cpp b/example/doc/util_ipc_reliable_mq_writer.cpp index 13c7457393..737adc3582 100644 --- a/example/doc/util_ipc_reliable_mq_writer.cpp +++ b/example/doc/util_ipc_reliable_mq_writer.cpp @@ -5,6 +5,8 @@ * http://www.boost.org/LICENSE_1_0.txt) */ +#if !defined(BOOST_LOG_WITHOUT_IPC) + #include #include #include @@ -79,3 +81,12 @@ int main() return 0; } //] + +#else // !defined(BOOST_LOG_WITHOUT_IPC) + +int main() +{ + return 0; +} + +#endif // !defined(BOOST_LOG_WITHOUT_IPC) diff --git a/example/doc/util_setup_settings.cpp b/example/doc/util_setup_settings.cpp new file mode 100644 index 0000000000..875df30cb1 --- /dev/null +++ b/example/doc/util_setup_settings.cpp @@ -0,0 +1,118 @@ +/* + * Copyright Andrey Semashev 2019. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * https://www.boost.org/LICENSE_1_0.txt) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace logging = boost::log; +namespace src = boost::log::sources; + +// Let's define our own severity levels +enum severity_level +{ + normal, + notification, + warning, + error, + critical +}; + +const char* const severity_strings[] = +{ + "normal", + "notification", + "warning", + "error", + "critical" +}; + +// The operator puts a human-friendly representation of the severity level to the stream +std::ostream& operator<< (std::ostream& strm, severity_level level) +{ + if (static_cast< std::size_t >(level) < sizeof(severity_strings) / sizeof(*severity_strings)) + strm << severity_strings[level]; + else + strm << static_cast< int >(level); + + return strm; +} + +// The operator parses the severity level from the stream +std::istream& operator>> (std::istream& strm, severity_level& level) +{ + if (strm.good()) + { + std::string str; + strm >> str; + + for (unsigned int i = 0; i < sizeof(severity_strings) / sizeof(*severity_strings); ++i) + { + if (str == severity_strings[i]) + { + level = static_cast< severity_level >(i); + return strm; + } + } + + strm.setstate(std::ios_base::failbit); + } + + return strm; +} + +void init_logging() +{ + // Before initializing the library from settings, we need to register any custom filter and formatter factories + logging::register_simple_filter_factory< severity_level >("Severity"); + logging::register_simple_formatter_factory< severity_level, char >("Severity"); + +//[ example_util_setup_settings + logging::settings setts; + + setts["Core"]["Filter"] = "%Severity% >= warning"; + setts["Core"]["DisableLogging"] = false; + + // Subsections can be referred to with a single path + setts["Sinks.Console"]["Destination"] = "Console"; + setts["Sinks.Console"]["Filter"] = "%Severity% >= critical"; + setts["Sinks.Console"]["Format"] = "%TimeStamp% [%Severity%] %Message%"; + setts["Sinks.Console"]["AutoFlush"] = true; + + // ...as well as the individual parameters + setts["Sinks.File.Destination"] = "TextFile"; + setts["Sinks.File.FileName"] = "MyApp_%3N.log"; + setts["Sinks.File.AutoFlush"] = true; + setts["Sinks.File.RotationSize"] = 10 * 1024 * 1024; // 10 MiB + setts["Sinks.File.Format"] = "%TimeStamp% [%Severity%] %Message%"; + + logging::init_from_settings(setts); +//] + + // Add attributes + logging::add_common_attributes(); +} + +int main(int, char*[]) +{ + init_logging(); + + src::severity_logger< severity_level > lg; + + BOOST_LOG_SEV(lg, normal) << "A regular message"; + BOOST_LOG_SEV(lg, warning) << "Something bad is going on but I can handle it"; + BOOST_LOG_SEV(lg, critical) << "Everything crumbles, shoot me now!"; + + return 0; +} diff --git a/example/doc/util_static_type_disp.cpp b/example/doc/util_static_type_disp.cpp index a0adda130e..82862c6d31 100644 --- a/example/doc/util_static_type_disp.cpp +++ b/example/doc/util_static_type_disp.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -82,13 +83,16 @@ int main(int, char*[]) // These two attributes are supported by the dispatcher bool res = print(my_value< std::string >("Hello world!")); assert(res); + boost::ignore_unused(res); res = print(my_value< double >(1.2)); assert(res); + boost::ignore_unused(res); // This one is not res = print(my_value< float >(-4.3f)); assert(!res); + boost::ignore_unused(res); return 0; } diff --git a/example/event_log/Jamfile.v2 b/example/event_log/Jamfile.v2 index c14b289608..89e652d055 100644 --- a/example/event_log/Jamfile.v2 +++ b/example/event_log/Jamfile.v2 @@ -7,6 +7,7 @@ import os ; import configure ; +import ../../build/log-platform-config ; rule has-config-flag ( flag : properties * ) { @@ -28,7 +29,7 @@ rule check-message-compiler ( properties * ) { if ! [ has-config-flag BOOST_LOG_WITHOUT_EVENT_LOG : $(properties) ] { - local has_mc = [ configure.builds /boost/log/message-compiler//test-availability : $(properties) : message-compiler ] ; + local has_mc = [ configure.builds /boost/log/config/message-compiler//test-availability : $(properties) : message-compiler ] ; if ! $(has_mc) { result += no ; @@ -39,28 +40,24 @@ rule check-message-compiler ( properties * ) { result += no ; } + + return $(result) ; } project : requirements + @log-platform-config.set-platform-defines @check-message-compiler shared - msvc:_SCL_SECURE_NO_WARNINGS - msvc:_SCL_SECURE_NO_DEPRECATE - msvc:_CRT_SECURE_NO_WARNINGS - msvc:_CRT_SECURE_NO_DEPRECATE + + 1024 + msvc:/bigobj msvc:/wd4503 # decorated name length exceeded, name was truncated msvc:/wd4456 # declaration of 'A' hides previous local declaration msvc:/wd4459 # declaration of 'A' hides global declaration msvc:/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion - intel-win:_SCL_SECURE_NO_WARNINGS - intel-win:_SCL_SECURE_NO_DEPRECATE - intel-win:_CRT_SECURE_NO_WARNINGS - intel-win:_CRT_SECURE_NO_DEPRECATE - darwin:-ftemplate-depth-1024 - gcc:-ftemplate-depth-1024 gcc:-fno-strict-aliasing # avoids strict aliasing violations in other Boost components # Disable Intel warnings: @@ -82,8 +79,6 @@ project /boost/log//boost_log /boost/date_time//boost_date_time /boost/filesystem//boost_filesystem - /boost/system//boost_system - /boost/thread//boost_thread multi ; diff --git a/example/keywords/Jamfile.v2 b/example/keywords/Jamfile.v2 index 1e4a40cb56..5742e17325 100644 --- a/example/keywords/Jamfile.v2 +++ b/example/keywords/Jamfile.v2 @@ -5,24 +5,21 @@ # http://www.boost.org/LICENSE_1_0.txt) # +import ../../build/log-platform-config ; + project : requirements + @log-platform-config.set-platform-defines + shared:BOOST_ALL_DYN_LINK - msvc:_SCL_SECURE_NO_WARNINGS - msvc:_SCL_SECURE_NO_DEPRECATE - msvc:_CRT_SECURE_NO_WARNINGS - msvc:_CRT_SECURE_NO_DEPRECATE + + 1024 + msvc:/bigobj msvc:/wd4503 # decorated name length exceeded, name was truncated msvc:/wd4456 # declaration of 'A' hides previous local declaration msvc:/wd4459 # declaration of 'A' hides global declaration msvc:/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion - intel-win:_SCL_SECURE_NO_WARNINGS - intel-win:_SCL_SECURE_NO_DEPRECATE - intel-win:_CRT_SECURE_NO_WARNINGS - intel-win:_CRT_SECURE_NO_DEPRECATE - darwin:-ftemplate-depth-1024 - gcc:-ftemplate-depth-1024 gcc:-fno-strict-aliasing # avoids strict aliasing violations in other Boost components # Disable Intel warnings: @@ -45,9 +42,7 @@ project /boost/log//boost_log_setup /boost/date_time//boost_date_time /boost/filesystem//boost_filesystem - /boost/system//boost_system single:BOOST_LOG_NO_THREADS - multi:/boost/thread//boost_thread ; exe keywords diff --git a/example/multiple_files/Jamfile.v2 b/example/multiple_files/Jamfile.v2 index 29566c3f1c..a3f2d3f274 100644 --- a/example/multiple_files/Jamfile.v2 +++ b/example/multiple_files/Jamfile.v2 @@ -5,24 +5,21 @@ # http://www.boost.org/LICENSE_1_0.txt) # +import ../../build/log-platform-config ; + project : requirements + @log-platform-config.set-platform-defines + shared:BOOST_ALL_DYN_LINK - msvc:_SCL_SECURE_NO_WARNINGS - msvc:_SCL_SECURE_NO_DEPRECATE - msvc:_CRT_SECURE_NO_WARNINGS - msvc:_CRT_SECURE_NO_DEPRECATE + + 1024 + msvc:/bigobj msvc:/wd4503 # decorated name length exceeded, name was truncated msvc:/wd4456 # declaration of 'A' hides previous local declaration msvc:/wd4459 # declaration of 'A' hides global declaration msvc:/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion - intel-win:_SCL_SECURE_NO_WARNINGS - intel-win:_SCL_SECURE_NO_DEPRECATE - intel-win:_CRT_SECURE_NO_WARNINGS - intel-win:_CRT_SECURE_NO_DEPRECATE - darwin:-ftemplate-depth-1024 - gcc:-ftemplate-depth-1024 gcc:-fno-strict-aliasing # avoids strict aliasing violations in other Boost components # Disable Intel warnings: @@ -44,8 +41,6 @@ project /boost/log//boost_log /boost/date_time//boost_date_time /boost/filesystem//boost_filesystem - /boost/system//boost_system - /boost/thread//boost_thread multi ; diff --git a/example/multiple_files/main.cpp b/example/multiple_files/main.cpp index 0a1ce47d54..3605d6736d 100644 --- a/example/multiple_files/main.cpp +++ b/example/multiple_files/main.cpp @@ -19,10 +19,10 @@ // #define BOOST_LOG_DYN_LINK 1 #include +#include #include #include #include -#include #include #include @@ -53,7 +53,7 @@ BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(my_logger, src::logger_mt) // This function is executed in a separate thread void thread_foo() { - BOOST_LOG_SCOPED_THREAD_TAG("ThreadID", boost::this_thread::get_id()); + BOOST_LOG_SCOPED_THREAD_TAG("ThreadID", std::this_thread::get_id()); for (unsigned int i = 0; i < LOG_RECORDS_TO_WRITE; ++i) { BOOST_LOG(my_logger::get()) << "Log record " << i; @@ -70,7 +70,7 @@ int main(int argc, char* argv[]) // Set up how the file names will be generated sink->locked_backend()->set_file_name_composer(sinks::file::as_file_name_composer( - expr::stream << "logs/" << expr::attr< boost::thread::id >("ThreadID") << ".log")); + expr::stream << "logs/" << expr::attr< std::thread::id >("ThreadID") << ".log")); // Set the log record formatter sink->set_formatter @@ -89,11 +89,12 @@ int main(int argc, char* argv[]) logging::core::get()->add_global_attribute("RecordID", attrs::counter< unsigned int >()); // Create threads and make some logs - boost::thread_group threads; + std::thread threads[THREAD_COUNT]; for (unsigned int i = 0; i < THREAD_COUNT; ++i) - threads.create_thread(&thread_foo); + threads[i] = std::thread(&thread_foo); - threads.join_all(); + for (unsigned int i = 0; i < THREAD_COUNT; ++i) + threads[i].join(); return 0; } diff --git a/example/multiple_threads/Jamfile.v2 b/example/multiple_threads/Jamfile.v2 index 66dc00f01e..330f1276e3 100644 --- a/example/multiple_threads/Jamfile.v2 +++ b/example/multiple_threads/Jamfile.v2 @@ -5,24 +5,21 @@ # http://www.boost.org/LICENSE_1_0.txt) # +import ../../build/log-platform-config ; + project : requirements + @log-platform-config.set-platform-defines + shared:BOOST_ALL_DYN_LINK - msvc:_SCL_SECURE_NO_WARNINGS - msvc:_SCL_SECURE_NO_DEPRECATE - msvc:_CRT_SECURE_NO_WARNINGS - msvc:_CRT_SECURE_NO_DEPRECATE + + 1024 + msvc:/bigobj msvc:/wd4503 # decorated name length exceeded, name was truncated msvc:/wd4456 # declaration of 'A' hides previous local declaration msvc:/wd4459 # declaration of 'A' hides global declaration msvc:/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion - intel-win:_SCL_SECURE_NO_WARNINGS - intel-win:_SCL_SECURE_NO_DEPRECATE - intel-win:_CRT_SECURE_NO_WARNINGS - intel-win:_CRT_SECURE_NO_DEPRECATE - darwin:-ftemplate-depth-1024 - gcc:-ftemplate-depth-1024 gcc:-fno-strict-aliasing # avoids strict aliasing violations in other Boost components # Disable Intel warnings: @@ -44,8 +41,6 @@ project /boost/log//boost_log /boost/date_time//boost_date_time /boost/filesystem//boost_filesystem - /boost/system//boost_system - /boost/thread//boost_thread multi ; diff --git a/example/multiple_threads/main.cpp b/example/multiple_threads/main.cpp index a9b036fcc4..44f3640110 100644 --- a/example/multiple_threads/main.cpp +++ b/example/multiple_threads/main.cpp @@ -18,15 +18,13 @@ // #define BOOST_LOG_DYN_LINK 1 #include +#include #include #include #include -#include -#include #include #include -#include -#include +#include #include #include @@ -52,10 +50,10 @@ enum BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(test_lg, src::logger_mt) //! This function is executed in multiple threads -void thread_fun(boost::barrier& bar) +void thread_fun(boost::compat::latch& latch) { // Wait until all threads are created - bar.wait(); + latch.arrive_and_wait(); // Now, do some logging for (unsigned int i = 0; i < LOG_RECORDS_TO_WRITE; ++i) @@ -97,13 +95,14 @@ int main(int argc, char* argv[]) logging::core::get()->add_global_attribute("ThreadID", attrs::current_thread_id()); // Create logging threads - boost::barrier bar(THREAD_COUNT); - boost::thread_group threads; + boost::compat::latch latch(THREAD_COUNT); + std::thread threads[THREAD_COUNT]; for (unsigned int i = 0; i < THREAD_COUNT; ++i) - threads.create_thread(boost::bind(&thread_fun, boost::ref(bar))); + threads[i] = std::thread([&latch]() { thread_fun(latch); }); // Wait until all action ends - threads.join_all(); + for (unsigned int i = 0; i < THREAD_COUNT; ++i) + threads[i].join(); return 0; } diff --git a/example/native_syslog/Jamfile.v2 b/example/native_syslog/Jamfile.v2 index 3ec07049bb..de6c572a77 100644 --- a/example/native_syslog/Jamfile.v2 +++ b/example/native_syslog/Jamfile.v2 @@ -5,24 +5,21 @@ # http://www.boost.org/LICENSE_1_0.txt) # +import ../../build/log-platform-config ; + project : requirements + @log-platform-config.set-platform-defines + shared:BOOST_ALL_DYN_LINK - msvc:_SCL_SECURE_NO_WARNINGS - msvc:_SCL_SECURE_NO_DEPRECATE - msvc:_CRT_SECURE_NO_WARNINGS - msvc:_CRT_SECURE_NO_DEPRECATE + + 1024 + msvc:/bigobj msvc:/wd4503 # decorated name length exceeded, name was truncated msvc:/wd4456 # declaration of 'A' hides previous local declaration msvc:/wd4459 # declaration of 'A' hides global declaration msvc:/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion - intel-win:_SCL_SECURE_NO_WARNINGS - intel-win:_SCL_SECURE_NO_DEPRECATE - intel-win:_CRT_SECURE_NO_WARNINGS - intel-win:_CRT_SECURE_NO_DEPRECATE - darwin:-ftemplate-depth-1024 - gcc:-ftemplate-depth-1024 gcc:-fno-strict-aliasing # avoids strict aliasing violations in other Boost components # Disable Intel warnings: @@ -44,8 +41,6 @@ project /boost/log//boost_log /boost/date_time//boost_date_time /boost/filesystem//boost_filesystem - /boost/system//boost_system - /boost/thread//boost_thread multi ; diff --git a/example/rotating_file/Jamfile.v2 b/example/rotating_file/Jamfile.v2 index a9b4748bba..32f62ed9ab 100644 --- a/example/rotating_file/Jamfile.v2 +++ b/example/rotating_file/Jamfile.v2 @@ -5,24 +5,21 @@ # http://www.boost.org/LICENSE_1_0.txt) # +import ../../build/log-platform-config ; + project : requirements + @log-platform-config.set-platform-defines + shared:BOOST_ALL_DYN_LINK - msvc:_SCL_SECURE_NO_WARNINGS - msvc:_SCL_SECURE_NO_DEPRECATE - msvc:_CRT_SECURE_NO_WARNINGS - msvc:_CRT_SECURE_NO_DEPRECATE + + 1024 + msvc:/bigobj msvc:/wd4503 # decorated name length exceeded, name was truncated msvc:/wd4456 # declaration of 'A' hides previous local declaration msvc:/wd4459 # declaration of 'A' hides global declaration msvc:/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion - intel-win:_SCL_SECURE_NO_WARNINGS - intel-win:_SCL_SECURE_NO_DEPRECATE - intel-win:_CRT_SECURE_NO_WARNINGS - intel-win:_CRT_SECURE_NO_DEPRECATE - darwin:-ftemplate-depth-1024 - gcc:-ftemplate-depth-1024 gcc:-fno-strict-aliasing # avoids strict aliasing violations in other Boost components # Disable Intel warnings: @@ -44,8 +41,6 @@ project /boost/log//boost_log /boost/date_time//boost_date_time /boost/filesystem//boost_filesystem - /boost/system//boost_system - /boost/thread//boost_thread multi ; diff --git a/example/rotating_file/main.cpp b/example/rotating_file/main.cpp index d2144c8124..bb221f6eb3 100644 --- a/example/rotating_file/main.cpp +++ b/example/rotating_file/main.cpp @@ -48,16 +48,17 @@ int main(int argc, char* argv[]) // Create a text file sink typedef sinks::synchronous_sink< sinks::text_file_backend > file_sink; shared_ptr< file_sink > sink(new file_sink( - keywords::file_name = "%Y%m%d_%H%M%S_%5N.log", // file name pattern - keywords::rotation_size = 16384 // rotation size, in characters + keywords::file_name = "file.log", // file name pattern + keywords::target_file_name = "%Y%m%d_%H%M%S_%5N.log", // file name pattern + keywords::rotation_size = 16384 // rotation size, in characters )); // Set up where the rotated files will be stored sink->locked_backend()->set_file_collector(sinks::file::make_collector( - keywords::target = "logs", // where to store rotated files - keywords::max_size = 16 * 1024 * 1024, // maximum total size of the stored files, in bytes - keywords::min_free_space = 100 * 1024 * 1024, // minimum free space on the drive, in bytes - keywords::max_files = 512 // maximum number of stored files + keywords::target = "logs", // where to store rotated files + keywords::max_size = 16 * 1024 * 1024, // maximum total size of the stored files, in bytes + keywords::min_free_space = 100 * 1024 * 1024, // minimum free space on the drive, in bytes + keywords::max_files = 512 // maximum number of stored files )); // Upon restart, scan the target directory for files matching the file_name pattern diff --git a/example/settings_file/Jamfile.v2 b/example/settings_file/Jamfile.v2 index d5ce605a1e..fc4af4d96b 100644 --- a/example/settings_file/Jamfile.v2 +++ b/example/settings_file/Jamfile.v2 @@ -5,24 +5,21 @@ # http://www.boost.org/LICENSE_1_0.txt) # +import ../../build/log-platform-config ; + project : requirements + @log-platform-config.set-platform-defines + shared:BOOST_ALL_DYN_LINK - msvc:_SCL_SECURE_NO_WARNINGS - msvc:_SCL_SECURE_NO_DEPRECATE - msvc:_CRT_SECURE_NO_WARNINGS - msvc:_CRT_SECURE_NO_DEPRECATE + + 1024 + msvc:/bigobj msvc:/wd4503 # decorated name length exceeded, name was truncated msvc:/wd4456 # declaration of 'A' hides previous local declaration msvc:/wd4459 # declaration of 'A' hides global declaration msvc:/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion - intel-win:_SCL_SECURE_NO_WARNINGS - intel-win:_SCL_SECURE_NO_DEPRECATE - intel-win:_CRT_SECURE_NO_WARNINGS - intel-win:_CRT_SECURE_NO_DEPRECATE - darwin:-ftemplate-depth-1024 - gcc:-ftemplate-depth-1024 gcc:-fno-strict-aliasing # avoids strict aliasing violations in other Boost components # Disable Intel warnings: @@ -45,9 +42,7 @@ project /boost/log//boost_log_setup /boost/date_time//boost_date_time /boost/filesystem//boost_filesystem - /boost/system//boost_system single:BOOST_LOG_NO_THREADS - multi:/boost/thread//boost_thread ; exe settings_file diff --git a/example/settings_file/main.cpp b/example/settings_file/main.cpp index 436a572663..eb4a13f921 100644 --- a/example/settings_file/main.cpp +++ b/example/settings_file/main.cpp @@ -18,41 +18,25 @@ // #define BOOST_ALL_DYN_LINK 1 #include -#include #include #include +#include #include #include #include namespace logging = boost::log; namespace attrs = boost::log::attributes; -namespace src = boost::log::sources; - -using boost::shared_ptr; - -// Here we define our application severity levels. -enum severity_level -{ - normal, - notification, - warning, - error, - critical -}; - -// Global logger declaration -BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(test_lg, src::severity_logger< >) void try_logging() { - src::severity_logger< >& lg = test_lg::get(); - BOOST_LOG_SEV(lg, normal) << "This is a normal severity record"; - BOOST_LOG_SEV(lg, notification) << "This is a notification severity record"; - BOOST_LOG_SEV(lg, warning) << "This is a warning severity record"; - BOOST_LOG_SEV(lg, error) << "This is a error severity record"; - BOOST_LOG_SEV(lg, critical) << "This is a critical severity record"; + BOOST_LOG_TRIVIAL(trace) << "This is a trace severity record"; + BOOST_LOG_TRIVIAL(debug) << "This is a debug severity record"; + BOOST_LOG_TRIVIAL(info) << "This is an info severity record"; + BOOST_LOG_TRIVIAL(warning) << "This is a warning severity record"; + BOOST_LOG_TRIVIAL(error) << "This is an error severity record"; + BOOST_LOG_TRIVIAL(fatal) << "This is a fatal severity record"; } int main(int argc, char* argv[]) diff --git a/example/settings_file/settings.txt b/example/settings_file/settings.txt index e7f159a538..c9fdb9db3e 100644 --- a/example/settings_file/settings.txt +++ b/example/settings_file/settings.txt @@ -7,14 +7,14 @@ [Core] -Filter="%Severity% >= 2" +Filter="%Severity% >= debug" [Sinks.1] Destination=Console -Format="%TimeStamp% *** %Message%" -Filter="%Tag% | %Severity% > 3" +Format="%TimeStamp% [%Severity%] *** %Message%" +Filter="%Tag% | %Severity% > info" [Sinks.2] @@ -22,4 +22,4 @@ Filter="%Tag% | %Severity% > 3" Destination=TextFile FileName=test.log AutoFlush=true -Format="[%TimeStamp%] %Tag% %Message%" +Format="[%TimeStamp%] [%Severity%] [%Tag%] %Message%" diff --git a/example/settings_file_formatter_factory/Jamfile.v2 b/example/settings_file_custom_factories/Jamfile.v2 similarity index 77% rename from example/settings_file_formatter_factory/Jamfile.v2 rename to example/settings_file_custom_factories/Jamfile.v2 index 6098f8a35b..72edb991b1 100644 --- a/example/settings_file_formatter_factory/Jamfile.v2 +++ b/example/settings_file_custom_factories/Jamfile.v2 @@ -5,24 +5,21 @@ # http://www.boost.org/LICENSE_1_0.txt) # +import ../../build/log-platform-config ; + project : requirements + @log-platform-config.set-platform-defines + shared:BOOST_ALL_DYN_LINK - msvc:_SCL_SECURE_NO_WARNINGS - msvc:_SCL_SECURE_NO_DEPRECATE - msvc:_CRT_SECURE_NO_WARNINGS - msvc:_CRT_SECURE_NO_DEPRECATE + + 1024 + msvc:/bigobj msvc:/wd4503 # decorated name length exceeded, name was truncated msvc:/wd4456 # declaration of 'A' hides previous local declaration msvc:/wd4459 # declaration of 'A' hides global declaration msvc:/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion - intel-win:_SCL_SECURE_NO_WARNINGS - intel-win:_SCL_SECURE_NO_DEPRECATE - intel-win:_CRT_SECURE_NO_WARNINGS - intel-win:_CRT_SECURE_NO_DEPRECATE - darwin:-ftemplate-depth-1024 - gcc:-ftemplate-depth-1024 gcc:-fno-strict-aliasing # avoids strict aliasing violations in other Boost components # Disable Intel warnings: @@ -45,12 +42,9 @@ project /boost/log//boost_log_setup /boost/date_time//boost_date_time /boost/filesystem//boost_filesystem - /boost/system//boost_system single:BOOST_LOG_NO_THREADS - multi:/boost/thread//boost_thread ; -exe settings_file_formatter_factory +exe settings_file_custom_factories : main.cpp ; - diff --git a/example/settings_file_formatter_factory/main.cpp b/example/settings_file_custom_factories/main.cpp similarity index 63% rename from example/settings_file_formatter_factory/main.cpp rename to example/settings_file_custom_factories/main.cpp index 3352a54fba..a82d813eb0 100644 --- a/example/settings_file_formatter_factory/main.cpp +++ b/example/settings_file_custom_factories/main.cpp @@ -10,7 +10,7 @@ * \date 12.05.2010 * * \brief An example of initializing the library from a settings file, - * with a custom formatter for an attribute. + * with custom filter and formatter factories for attributes. */ // #define BOOST_ALL_DYN_LINK 1 @@ -19,8 +19,8 @@ #include #include #include -#include -#include +#include +#include #include #include #include @@ -29,12 +29,14 @@ #include #include #include +#include #include namespace logging = boost::log; namespace attrs = boost::log::attributes; namespace src = boost::log::sources; +//! Enum for our custom severity levels enum severity_level { normal, @@ -44,7 +46,57 @@ enum severity_level critical }; -BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(test_lg, src::severity_logger< >) +//! Formatting operator for severity levels +inline std::ostream& operator<< (std::ostream& strm, severity_level level) +{ + switch (level) + { + case normal: + strm << "normal"; + break; + case notification: + strm << "notification"; + break; + case warning: + strm << "warning"; + break; + case error: + strm << "error"; + break; + case critical: + strm << "critical"; + break; + default: + strm << static_cast< int >(level); + break; + } + + return strm; +} + +//! Parsing operator for severity levels +inline std::istream& operator>> (std::istream& strm, severity_level& level) +{ + if (strm.good()) + { + std::string str; + strm >> str; + if (str == "normal") + level = normal; + else if (str == "notification") + level = notification; + else if (str == "warning") + level = warning; + else if (str == "error") + level = error; + else if (str == "critical") + level = critical; + else + strm.setstate(std::ios_base::failbit); + } + + return strm; +} //! Our custom formatter for the scope list struct scope_list_formatter @@ -63,7 +115,7 @@ struct scope_list_formatter ( name_, rec.attribute_values(), - boost::bind(&scope_list_formatter::format, _1, boost::ref(strm)) + boost::bind(&scope_list_formatter::format, boost::placeholders::_1, boost::ref(strm)) ); } @@ -103,24 +155,32 @@ void init_logging() // First thing - register the custom formatter for MyScopes logging::register_formatter_factory("MyScopes", boost::make_shared< my_scopes_formatter_factory >()); + // Also register filter and formatter factories for our custom severity level enum. Since our operator<< and operator>> implement + // all required behavior, simple factories provided by Boost.Log will do. + logging::register_simple_filter_factory< severity_level >("Severity"); + logging::register_simple_formatter_factory< severity_level, char >("Severity"); + // Then load the settings from the file std::ifstream settings("settings.txt"); if (!settings.is_open()) throw std::runtime_error("Could not open settings.txt file"); logging::init_from_stream(settings); - // Add some attributes + // Add some attributes. Note that severity level will be provided by the logger, so we don't need to add it here. logging::add_common_attributes(); logging::core::get()->add_global_attribute("MyScopes", attrs::named_scope()); } +//! Global logger, which we will use to write log messages +BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(test_lg, src::severity_logger< severity_level >) + //! The function tests logging void try_logging() { BOOST_LOG_FUNCTION(); - src::severity_logger< >& lg = test_lg::get(); + src::severity_logger< severity_level >& lg = test_lg::get(); BOOST_LOG_SEV(lg, critical) << "This is a critical severity record"; diff --git a/example/settings_file_formatter_factory/settings.txt b/example/settings_file_custom_factories/settings.txt similarity index 92% rename from example/settings_file_formatter_factory/settings.txt rename to example/settings_file_custom_factories/settings.txt index e4848a005e..98d8d7fd6d 100644 --- a/example/settings_file_formatter_factory/settings.txt +++ b/example/settings_file_custom_factories/settings.txt @@ -9,5 +9,6 @@ Destination=TextFile FileName=test.log AutoFlush=true +Filter="%Severity% > normal" Format="[%TimeStamp%] [%Severity%]\n%MyScopes%\n\t:: %Message%" Asynchronous=false diff --git a/example/syslog/Jamfile.v2 b/example/syslog/Jamfile.v2 index 2632be9bc0..4c8d181847 100644 --- a/example/syslog/Jamfile.v2 +++ b/example/syslog/Jamfile.v2 @@ -5,24 +5,21 @@ # http://www.boost.org/LICENSE_1_0.txt) # +import ../../build/log-platform-config ; + project : requirements + @log-platform-config.set-platform-defines + shared:BOOST_ALL_DYN_LINK - msvc:_SCL_SECURE_NO_WARNINGS - msvc:_SCL_SECURE_NO_DEPRECATE - msvc:_CRT_SECURE_NO_WARNINGS - msvc:_CRT_SECURE_NO_DEPRECATE + + 1024 + msvc:/bigobj msvc:/wd4503 # decorated name length exceeded, name was truncated msvc:/wd4456 # declaration of 'A' hides previous local declaration msvc:/wd4459 # declaration of 'A' hides global declaration msvc:/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion - intel-win:_SCL_SECURE_NO_WARNINGS - intel-win:_SCL_SECURE_NO_DEPRECATE - intel-win:_CRT_SECURE_NO_WARNINGS - intel-win:_CRT_SECURE_NO_DEPRECATE - darwin:-ftemplate-depth-1024 - gcc:-ftemplate-depth-1024 gcc:-fno-strict-aliasing # avoids strict aliasing violations in other Boost components # Disable Intel warnings: @@ -44,8 +41,6 @@ project /boost/log//boost_log /boost/date_time//boost_date_time /boost/filesystem//boost_filesystem - /boost/system//boost_system - /boost/thread//boost_thread multi ; diff --git a/example/syslog/main.cpp b/example/syslog/main.cpp index e95c1fa756..aef1098ce5 100644 --- a/example/syslog/main.cpp +++ b/example/syslog/main.cpp @@ -73,8 +73,10 @@ int main(int argc, char* argv[]) sink->locked_backend()->set_severity_mapper(mapping); +#if !defined(BOOST_LOG_WITHOUT_ASIO) // Set the remote address to sent syslog messages to sink->locked_backend()->set_target_address("localhost"); +#endif // Add the sink to the core logging::core::get()->add_sink(sink); diff --git a/example/trivial/Jamfile.v2 b/example/trivial/Jamfile.v2 index 7ea90e1d41..aef9de78e8 100644 --- a/example/trivial/Jamfile.v2 +++ b/example/trivial/Jamfile.v2 @@ -5,24 +5,21 @@ # http://www.boost.org/LICENSE_1_0.txt) # +import ../../build/log-platform-config ; + project : requirements + @log-platform-config.set-platform-defines + shared:BOOST_ALL_DYN_LINK - msvc:_SCL_SECURE_NO_WARNINGS - msvc:_SCL_SECURE_NO_DEPRECATE - msvc:_CRT_SECURE_NO_WARNINGS - msvc:_CRT_SECURE_NO_DEPRECATE + + 1024 + msvc:/bigobj msvc:/wd4503 # decorated name length exceeded, name was truncated msvc:/wd4456 # declaration of 'A' hides previous local declaration msvc:/wd4459 # declaration of 'A' hides global declaration msvc:/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion - intel-win:_SCL_SECURE_NO_WARNINGS - intel-win:_SCL_SECURE_NO_DEPRECATE - intel-win:_CRT_SECURE_NO_WARNINGS - intel-win:_CRT_SECURE_NO_DEPRECATE - darwin:-ftemplate-depth-1024 - gcc:-ftemplate-depth-1024 gcc:-fno-strict-aliasing # avoids strict aliasing violations in other Boost components # Disable Intel warnings: @@ -44,9 +41,7 @@ project /boost/log//boost_log /boost/date_time//boost_date_time /boost/filesystem//boost_filesystem - /boost/system//boost_system single:BOOST_LOG_NO_THREADS - multi:/boost/thread//boost_thread ; exe trivial diff --git a/example/wide_char/Jamfile.v2 b/example/wide_char/Jamfile.v2 index ea89208d12..2a7477d6cf 100644 --- a/example/wide_char/Jamfile.v2 +++ b/example/wide_char/Jamfile.v2 @@ -5,24 +5,21 @@ # http://www.boost.org/LICENSE_1_0.txt) # +import ../../build/log-platform-config ; + project : requirements + @log-platform-config.set-platform-defines + shared:BOOST_ALL_DYN_LINK - msvc:_SCL_SECURE_NO_WARNINGS - msvc:_SCL_SECURE_NO_DEPRECATE - msvc:_CRT_SECURE_NO_WARNINGS - msvc:_CRT_SECURE_NO_DEPRECATE + + 1024 + msvc:/bigobj msvc:/wd4503 # decorated name length exceeded, name was truncated msvc:/wd4456 # declaration of 'A' hides previous local declaration msvc:/wd4459 # declaration of 'A' hides global declaration msvc:/wd4003 # not enough actual parameters for macro 'X' - caused by BOOST_PP_IS_EMPTY and BOOST_PP_IS_BEGIN_PARENS which are used by Fusion - intel-win:_SCL_SECURE_NO_WARNINGS - intel-win:_SCL_SECURE_NO_DEPRECATE - intel-win:_CRT_SECURE_NO_WARNINGS - intel-win:_CRT_SECURE_NO_DEPRECATE - darwin:-ftemplate-depth-1024 - gcc:-ftemplate-depth-1024 gcc:-fno-strict-aliasing # avoids strict aliasing violations in other Boost components # Disable Intel warnings: @@ -45,9 +42,7 @@ project /boost/log//boost_log_setup /boost/date_time//boost_date_time /boost/filesystem//boost_filesystem - /boost/system//boost_system /boost/locale//boost_locale - /boost/thread//boost_thread multi # does not build on VxWorks due to lack of boost::locale support diff --git a/include/boost/log/attributes/attribute_set.hpp b/include/boost/log/attributes/attribute_set.hpp index 05fa3aaa96..473a4f2622 100644 --- a/include/boost/log/attributes/attribute_set.hpp +++ b/include/boost/log/attributes/attribute_set.hpp @@ -18,9 +18,11 @@ #include #include #include -#include #include +#include +#include #include +#include #include #include #include @@ -157,12 +159,12 @@ class attribute_set // Standard typedefs typedef attribute_set::difference_type difference_type; typedef attribute_set::value_type value_type; - typedef typename mpl::if_c< + typedef typename boost::conditional< fConstV, attribute_set::const_reference, attribute_set::reference >::type reference; - typedef typename mpl::if_c< + typedef typename boost::conditional< fConstV, attribute_set::const_pointer, attribute_set::pointer @@ -171,23 +173,32 @@ class attribute_set public: // Constructors - BOOST_CONSTEXPR iter() : m_pNode(NULL) {} + BOOST_CONSTEXPR iter() BOOST_NOEXCEPT : m_pNode(NULL) {} explicit iter(node_base* pNode) BOOST_NOEXCEPT : m_pNode(pNode) {} - iter(iter< false > const& that) BOOST_NOEXCEPT : m_pNode(that.m_pNode) {} +#if !defined(BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS) + template< bool fOtherConstV, typename = typename boost::enable_if_c< !fOtherConstV && fOtherConstV != fConstV >::type > + iter(iter< fOtherConstV > const& that) BOOST_NOEXCEPT : m_pNode(that.m_pNode) {} +#else + template< bool fOtherConstV > + iter(iter< fOtherConstV > const& that, typename boost::enable_if_c< !fOtherConstV && fOtherConstV != fConstV, boost::log::aux::sfinae_dummy >::type = boost::log::aux::sfinae_dummy()) BOOST_NOEXCEPT : + m_pNode(that.m_pNode) + { + } +#endif //! Assignment - template< bool f > - iter& operator= (iter< f > const& that) BOOST_NOEXCEPT + template< bool fOtherConstV > + typename boost::enable_if_c< !fOtherConstV && fOtherConstV != fConstV, iter& >::type operator= (iter< fOtherConstV > const& that) BOOST_NOEXCEPT { m_pNode = that.m_pNode; return *this; } // Comparison - template< bool f > - bool operator== (iter< f > const& that) const BOOST_NOEXCEPT { return (m_pNode == that.m_pNode); } - template< bool f > - bool operator!= (iter< f > const& that) const BOOST_NOEXCEPT { return (m_pNode != that.m_pNode); } + template< bool fOtherConstV > + typename boost::enable_if_c< !fOtherConstV || fOtherConstV == fConstV, bool >::type operator== (iter< fOtherConstV > const& that) const BOOST_NOEXCEPT { return (m_pNode == that.m_pNode); } + template< bool fOtherConstV > + typename boost::enable_if_c< !fOtherConstV || fOtherConstV == fConstV, bool >::type operator!= (iter< fOtherConstV > const& that) const BOOST_NOEXCEPT { return (m_pNode != that.m_pNode); } // Modification iter& operator++ () BOOST_NOEXCEPT diff --git a/include/boost/log/attributes/attribute_value.hpp b/include/boost/log/attributes/attribute_value.hpp index 197ef860bf..595ae21887 100644 --- a/include/boost/log/attributes/attribute_value.hpp +++ b/include/boost/log/attributes/attribute_value.hpp @@ -98,7 +98,7 @@ class attribute_value /*! * \return The attribute value that refers to self implementation. */ - virtual attribute_value get_value() { return attribute_value(this); } + attribute_value get_value() BOOST_OVERRIDE { return attribute_value(this); } /*! * \return The attribute value type @@ -114,7 +114,7 @@ class attribute_value /*! * Default constructor. Creates an empty (absent) attribute value. */ - BOOST_DEFAULTED_FUNCTION(attribute_value(), {}) + BOOST_DEFAULTED_FUNCTION(attribute_value(), BOOST_NOEXCEPT {}) /*! * Copy constructor diff --git a/include/boost/log/attributes/attribute_value_impl.hpp b/include/boost/log/attributes/attribute_value_impl.hpp index e18015bec3..f1439525bd 100644 --- a/include/boost/log/attributes/attribute_value_impl.hpp +++ b/include/boost/log/attributes/attribute_value_impl.hpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -64,7 +65,10 @@ class attribute_value_impl : /*! * Constructor with initialization of the stored value */ - explicit attribute_value_impl(BOOST_RV_REF(value_type) v) : m_value(boost::move(v)) {} + explicit attribute_value_impl(BOOST_RV_REF(value_type) v) BOOST_NOEXCEPT_IF(boost::is_nothrow_move_constructible< value_type >::value) : + m_value(boost::move(v)) + { + } /*! * Attribute value dispatching method. @@ -73,7 +77,7 @@ class attribute_value_impl : * * \return \c true if the value has been dispatched, \c false otherwise */ - virtual bool dispatch(type_dispatcher& dispatcher) + bool dispatch(type_dispatcher& dispatcher) BOOST_OVERRIDE { type_dispatcher::callback< value_type > callback = dispatcher.get_callback< value_type >(); if (callback) @@ -88,7 +92,7 @@ class attribute_value_impl : /*! * \return The attribute value type */ - typeindex::type_index get_type() const { return typeindex::type_id< value_type >(); } + typeindex::type_index get_type() const BOOST_OVERRIDE { return typeindex::type_id< value_type >(); } /*! * \return Reference to the contained value. diff --git a/include/boost/log/attributes/constant.hpp b/include/boost/log/attributes/constant.hpp index f10b0a7e5b..bfc2877879 100644 --- a/include/boost/log/attributes/constant.hpp +++ b/include/boost/log/attributes/constant.hpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -66,7 +67,10 @@ class constant : /*! * Constructor with the stored value initialization */ - explicit impl(BOOST_RV_REF(value_type) value) : base_type(boost::move(value)) {} + explicit impl(BOOST_RV_REF(value_type) value) BOOST_NOEXCEPT_IF(boost::is_nothrow_move_constructible< value_type >::value) : + base_type(boost::move(value)) + { + } }; public: diff --git a/include/boost/log/attributes/counter.hpp b/include/boost/log/attributes/counter.hpp index b7d9994027..fa3522a887 100644 --- a/include/boost/log/attributes/counter.hpp +++ b/include/boost/log/attributes/counter.hpp @@ -15,7 +15,6 @@ #ifndef BOOST_LOG_ATTRIBUTES_COUNTER_HPP_INCLUDED_ #define BOOST_LOG_ATTRIBUTES_COUNTER_HPP_INCLUDED_ -#include #include #include #include @@ -48,7 +47,7 @@ template< typename T > class counter : public attribute { - BOOST_STATIC_ASSERT_MSG(is_integral< T >::value, "Boost.Log: Only integral types are supported by the counter attribute"); + static_assert(is_integral< T >::value, "Boost.Log: Only integral types are supported by the counter attribute"); public: //! A counter value type diff --git a/include/boost/log/attributes/function.hpp b/include/boost/log/attributes/function.hpp index 488416c5df..409a604d68 100644 --- a/include/boost/log/attributes/function.hpp +++ b/include/boost/log/attributes/function.hpp @@ -15,7 +15,6 @@ #ifndef BOOST_LOG_ATTRIBUTES_FUNCTION_HPP_INCLUDED_ #define BOOST_LOG_ATTRIBUTES_FUNCTION_HPP_INCLUDED_ -#include #include #include #include @@ -49,7 +48,7 @@ template< typename R > class function : public attribute { - BOOST_STATIC_ASSERT_MSG(!is_void< R >::value, "Boost.Log: Function object return type must not be void"); + static_assert(!is_void< R >::value, "Boost.Log: Function object return type must not be void"); public: //! The attribute value type diff --git a/include/boost/log/attributes/mutable_constant.hpp b/include/boost/log/attributes/mutable_constant.hpp index 6ff76ddc2c..ca659f56a2 100644 --- a/include/boost/log/attributes/mutable_constant.hpp +++ b/include/boost/log/attributes/mutable_constant.hpp @@ -15,12 +15,11 @@ #ifndef BOOST_LOG_ATTRIBUTES_MUTABLE_CONSTANT_HPP_INCLUDED_ #define BOOST_LOG_ATTRIBUTES_MUTABLE_CONSTANT_HPP_INCLUDED_ -#include #include -#include #include #include #include +#include #include #include #include @@ -60,7 +59,7 @@ template< typename MutexT = void, typename ScopedWriteLockT = #ifndef BOOST_LOG_NO_THREADS - typename mpl::if_c< + typename boost::conditional< boost::log::aux::is_exclusively_lockable< MutexT >::value, boost::log::aux::exclusive_lock_guard< MutexT >, void @@ -70,7 +69,7 @@ template< #endif // BOOST_LOG_NO_THREADS typename ScopedReadLockT = #ifndef BOOST_LOG_NO_THREADS - typename mpl::if_c< + typename boost::conditional< boost::log::aux::is_shared_lockable< MutexT >::value, boost::log::aux::shared_lock_guard< MutexT >, ScopedWriteLockT @@ -99,7 +98,7 @@ class mutable_constant : typedef ScopedReadLockT scoped_read_lock; //! Exclusive lock type typedef ScopedWriteLockT scoped_write_lock; - BOOST_STATIC_ASSERT_MSG(!(is_void< mutex_type >::value || is_void< scoped_read_lock >::value || is_void< scoped_write_lock >::value), "Boost.Log: Mutex and both lock types either must not be void or must all be void"); + static_assert(!(is_void< mutex_type >::value || is_void< scoped_read_lock >::value || is_void< scoped_write_lock >::value), "Boost.Log: Mutex and both lock types either must not be void or must all be void"); //! Attribute value wrapper typedef attribute_value_impl< value_type > attr_value; diff --git a/include/boost/log/attributes/named_scope.hpp b/include/boost/log/attributes/named_scope.hpp index 5b33518ddb..a9b2978fc7 100644 --- a/include/boost/log/attributes/named_scope.hpp +++ b/include/boost/log/attributes/named_scope.hpp @@ -20,14 +20,15 @@ #include #include #include -#include #include -#include +#include +#include #include #include #include #include #include +#include #include #ifdef BOOST_HAS_PRAGMA_ONCE @@ -124,13 +125,13 @@ class named_scope_list typedef std::allocator< named_scope_entry > allocator_type; // Standard types - typedef allocator_type::value_type value_type; - typedef allocator_type::reference reference; - typedef allocator_type::const_reference const_reference; - typedef allocator_type::pointer pointer; - typedef allocator_type::const_pointer const_pointer; - typedef allocator_type::size_type size_type; - typedef allocator_type::difference_type difference_type; + typedef log::aux::allocator_traits< allocator_type >::value_type value_type; + typedef log::aux::allocator_traits< allocator_type >::size_type size_type; + typedef log::aux::allocator_traits< allocator_type >::difference_type difference_type; + typedef log::aux::allocator_traits< allocator_type >::pointer pointer; + typedef log::aux::allocator_traits< allocator_type >::const_pointer const_pointer; + typedef value_type& reference; + typedef value_type const& const_reference; #ifndef BOOST_LOG_DOXYGEN_PASS @@ -149,12 +150,12 @@ class named_scope_list // Standard typedefs typedef named_scope_list::difference_type difference_type; typedef named_scope_list::value_type value_type; - typedef typename mpl::if_c< + typedef typename boost::conditional< fConstV, named_scope_list::const_reference, named_scope_list::reference >::type reference; - typedef typename mpl::if_c< + typedef typename boost::conditional< fConstV, named_scope_list::const_pointer, named_scope_list::pointer @@ -369,6 +370,7 @@ class BOOST_LOG_API named_scope : * \param sn Scope name. * \param fn File name, in which the scope is located. * \param ln Line number in the file. + * \param t Scope name type. */ sentry(string_literal const& sn, string_literal const& fn, unsigned int ln, scope_entry::scope_name_type t = scope_entry::general) BOOST_NOEXCEPT : m_Entry(sn, fn, ln, t) diff --git a/include/boost/log/attributes/scoped_attribute.hpp b/include/boost/log/attributes/scoped_attribute.hpp index 88dd218e4c..b258150f15 100644 --- a/include/boost/log/attributes/scoped_attribute.hpp +++ b/include/boost/log/attributes/scoped_attribute.hpp @@ -15,6 +15,7 @@ #ifndef BOOST_LOG_ATTRIBUTES_SCOPED_ATTRIBUTE_HPP_INCLUDED_ #define BOOST_LOG_ATTRIBUTES_SCOPED_ATTRIBUTE_HPP_INCLUDED_ +#include #include #include #include @@ -81,14 +82,14 @@ class scoped_logger_attribute : if (res.second) m_itAttribute = res.first; else - m_pLogger = 0; // if there already is a same-named attribute, don't register anything + m_pLogger = NULL; // if there already is a same-named attribute, don't register anything } //! Move constructor - scoped_logger_attribute(BOOST_RV_REF(scoped_logger_attribute) that) : + scoped_logger_attribute(BOOST_RV_REF(scoped_logger_attribute) that) BOOST_NOEXCEPT : m_pLogger(that.m_pLogger), m_itAttribute(that.m_itAttribute) { - that.m_pLogger = 0; + that.m_pLogger = NULL; } //! Destructor @@ -101,9 +102,9 @@ class scoped_logger_attribute : #ifndef BOOST_LOG_BROKEN_REFERENCE_FROM_RVALUE_INIT BOOST_DELETED_FUNCTION(scoped_logger_attribute(scoped_logger_attribute const&)) #else // BOOST_LOG_BROKEN_REFERENCE_FROM_RVALUE_INIT - scoped_logger_attribute(scoped_logger_attribute const& that) : m_pLogger(that.m_pLogger), m_itAttribute(that.m_itAttribute) + scoped_logger_attribute(scoped_logger_attribute const& that) BOOST_NOEXCEPT : m_pLogger(that.m_pLogger), m_itAttribute(that.m_itAttribute) { - const_cast< scoped_logger_attribute& >(that).m_pLogger = 0; + const_cast< scoped_logger_attribute& >(that).m_pLogger = NULL; } #endif // BOOST_LOG_BROKEN_REFERENCE_FROM_RVALUE_INIT diff --git a/include/boost/log/core/record_view.hpp b/include/boost/log/core/record_view.hpp index 7a3867760b..0455b3933c 100644 --- a/include/boost/log/core/record_view.hpp +++ b/include/boost/log/core/record_view.hpp @@ -23,7 +23,8 @@ #include #include #ifndef BOOST_LOG_NO_THREADS -#include +#include +#include #endif // BOOST_LOG_NO_THREADS #include @@ -63,19 +64,34 @@ class record_view //! Publicly available record data struct public_data { + private: //! Reference counter #ifndef BOOST_LOG_NO_THREADS - mutable boost::detail::atomic_count m_ref_counter; + mutable boost::atomic< unsigned int > m_ref_counter; + + friend void intrusive_ptr_add_ref(const public_data* p) BOOST_NOEXCEPT + { + p->m_ref_counter.opaque_add(1u, boost::memory_order_relaxed); + } + friend void intrusive_ptr_release(const public_data* p) BOOST_NOEXCEPT + { + if (!p->m_ref_counter.sub_and_test(1u, boost::memory_order_acq_rel)) + public_data::destroy(p); + } #else mutable unsigned int m_ref_counter; + + friend void intrusive_ptr_add_ref(const public_data* p) BOOST_NOEXCEPT { ++p->m_ref_counter; } + friend void intrusive_ptr_release(const public_data* p) BOOST_NOEXCEPT { if (--p->m_ref_counter == 0u) public_data::destroy(p); } #endif // BOOST_LOG_NO_THREADS + public: //! Attribute values view attribute_value_set m_attribute_values; //! Constructor from the attribute value set explicit public_data(BOOST_RV_REF(attribute_value_set) values) BOOST_NOEXCEPT : - m_ref_counter(1), + m_ref_counter(1u), m_attribute_values(boost::move(values)) { } @@ -88,9 +104,6 @@ class record_view BOOST_DELETED_FUNCTION(public_data(public_data const&)) BOOST_DELETED_FUNCTION(public_data& operator= (public_data const&)) - - friend void intrusive_ptr_add_ref(const public_data* p) BOOST_NOEXCEPT { ++p->m_ref_counter; } - friend void intrusive_ptr_release(const public_data* p) BOOST_NOEXCEPT { if (--p->m_ref_counter == 0) public_data::destroy(p); } }; private: diff --git a/include/boost/log/detail/adaptive_mutex.hpp b/include/boost/log/detail/adaptive_mutex.hpp index 70efd92e7c..e642a2ac97 100644 --- a/include/boost/log/detail/adaptive_mutex.hpp +++ b/include/boost/log/detail/adaptive_mutex.hpp @@ -1,5 +1,5 @@ /* - * Copyright Andrey Semashev 2007 - 2015. + * Copyright Andrey Semashev 2007 - 2025. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) @@ -24,37 +24,19 @@ #ifndef BOOST_LOG_NO_THREADS -#include -#include +#include -#if defined(BOOST_THREAD_POSIX) // This one can be defined by users, so it should go first -#define BOOST_LOG_ADAPTIVE_MUTEX_USE_PTHREAD -#elif defined(BOOST_WINDOWS) -#define BOOST_LOG_ADAPTIVE_MUTEX_USE_WINAPI -#elif defined(BOOST_HAS_PTHREADS) -#define BOOST_LOG_ADAPTIVE_MUTEX_USE_PTHREAD +#if defined(BOOST_WINDOWS) || (BOOST_ATOMIC_INT_LOCK_FREE == 2) && (BOOST_ATOMIC_HAS_NATIVE_INT_WAIT_NOTIFY == 2) +#define BOOST_LOG_AUX_ADAPTIVE_MUTEX_USE_ATOMIC +#else +#define BOOST_LOG_AUX_ADAPTIVE_MUTEX_USE_PTHREAD #endif -#if defined(BOOST_LOG_ADAPTIVE_MUTEX_USE_WINAPI) - -#include -#include -#include - -#if defined(__INTEL_COMPILER) || defined(_MSC_VER) -# if defined(__INTEL_COMPILER) -# define BOOST_LOG_COMPILER_BARRIER __memory_barrier() -# else -extern "C" void _ReadWriteBarrier(void); -# if defined(BOOST_MSVC) -# pragma intrinsic(_ReadWriteBarrier) -# endif -# define BOOST_LOG_COMPILER_BARRIER _ReadWriteBarrier() -# endif -#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) -# define BOOST_LOG_COMPILER_BARRIER __asm__ __volatile__("" : : : "memory") -#endif +#if defined(BOOST_LOG_AUX_ADAPTIVE_MUTEX_USE_ATOMIC) +#include +#include +#include #include namespace boost { @@ -63,73 +45,97 @@ BOOST_LOG_OPEN_NAMESPACE namespace aux { -//! A mutex that performs spinning or thread yielding in case of contention +//! A mutex that performs spinning or thread blocking in case of contention class adaptive_mutex { private: - enum state + enum state : unsigned int + { + locked_bit = 1u, + waiters_one = 1u << 1u + }; + + enum pause_constants : unsigned int { - initial_pause = 2, - max_pause = 16 + initial_pause = 1u, + max_pause = 16u }; - long m_State; + //! Mutex state. Lowest bit indicates whether the mutex is locked or not, all higher bits store the number of blocked waiters. See \c state enum. + boost::atomics::atomic< unsigned int > m_state; public: - adaptive_mutex() : m_State(0) {} + adaptive_mutex() noexcept : m_state(0u) {} - bool try_lock() + // Non-copyable + adaptive_mutex(adaptive_mutex const&) = delete; + adaptive_mutex& operator= (adaptive_mutex const&) = delete; + + bool try_lock() noexcept { - return (BOOST_INTERLOCKED_COMPARE_EXCHANGE(&m_State, 1L, 0L) == 0L); + unsigned int old_state = m_state.load(boost::memory_order::relaxed); + return (old_state & locked_bit) == 0u && + m_state.compare_exchange_strong(old_state, old_state | locked_bit, boost::memory_order::acquire, boost::memory_order::relaxed); } - void lock() + void lock() noexcept { -#if defined(BOOST_LOG_AUX_PAUSE) unsigned int pause_count = initial_pause; -#endif - while (!try_lock()) + unsigned int waiter_added = 0u; + unsigned int old_state = m_state.load(boost::memory_order::relaxed); + while (true) { -#if defined(BOOST_LOG_AUX_PAUSE) + if ((old_state & locked_bit) == 0u) + { + unsigned int new_state = (old_state - waiter_added) | locked_bit; + if (m_state.compare_exchange_weak(old_state, new_state, boost::memory_order::acquire, boost::memory_order::relaxed)) + break; + + continue; + } + if (pause_count < max_pause) { - for (unsigned int i = 0; i < pause_count; ++i) + if (waiter_added != 0u) + { + old_state = m_state.sub(waiters_one, boost::memory_order::relaxed); + waiter_added = 0u; + } + else { - BOOST_LOG_AUX_PAUSE; + for (unsigned int i = 0u; i < pause_count; ++i) + { + boost::atomics::thread_pause(); + } + pause_count += pause_count; + + old_state = m_state.load(boost::memory_order::relaxed); } - pause_count += pause_count; + } + else if (waiter_added == 0u) + { + old_state = m_state.add(waiters_one, boost::memory_order::relaxed); + waiter_added = waiters_one; } else { // Restart spinning after waking up this thread pause_count = initial_pause; - boost::winapi::SwitchToThread(); + old_state = m_state.wait(old_state, boost::memory_order::relaxed); } -#else - boost::winapi::SwitchToThread(); -#endif } } - void unlock() + void unlock() noexcept { -#if (defined(_M_IX86) || defined(_M_AMD64)) && defined(BOOST_LOG_COMPILER_BARRIER) - BOOST_LOG_COMPILER_BARRIER; - m_State = 0L; - BOOST_LOG_COMPILER_BARRIER; -#else - BOOST_INTERLOCKED_EXCHANGE(&m_State, 0L); -#endif + if (m_state.and_and_test(~static_cast< unsigned int >(locked_bit), boost::memory_order::release)) + { + // If the resulting state is non-zero then there are blocked waiters + m_state.notify_one(); + } } - - // Non-copyable - BOOST_DELETED_FUNCTION(adaptive_mutex(adaptive_mutex const&)) - BOOST_DELETED_FUNCTION(adaptive_mutex& operator= (adaptive_mutex const&)) }; -#undef BOOST_LOG_AUX_PAUSE -#undef BOOST_LOG_COMPILER_BARRIER - } // namespace aux BOOST_LOG_CLOSE_NAMESPACE // namespace log @@ -138,14 +144,17 @@ BOOST_LOG_CLOSE_NAMESPACE // namespace log #include -#elif defined(BOOST_LOG_ADAPTIVE_MUTEX_USE_PTHREAD) +#elif defined(BOOST_LOG_AUX_ADAPTIVE_MUTEX_USE_PTHREAD) #include +#include +#include #include +#include #include #if defined(PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP) -#define BOOST_LOG_ADAPTIVE_MUTEX_USE_PTHREAD_MUTEX_ADAPTIVE_NP +#define BOOST_LOG_AUX_ADAPTIVE_MUTEX_USE_PTHREAD_MUTEX_ADAPTIVE_NP #endif namespace boost { @@ -154,69 +163,64 @@ BOOST_LOG_OPEN_NAMESPACE namespace aux { -//! A mutex that performs spinning or thread yielding in case of contention +//! A mutex that performs spinning or thread blocking in case of contention class adaptive_mutex { private: - pthread_mutex_t m_State; + pthread_mutex_t m_state; public: adaptive_mutex() { -#if defined(BOOST_LOG_ADAPTIVE_MUTEX_USE_PTHREAD_MUTEX_ADAPTIVE_NP) +#if defined(BOOST_LOG_AUX_ADAPTIVE_MUTEX_USE_PTHREAD_MUTEX_ADAPTIVE_NP) pthread_mutexattr_t attrs; pthread_mutexattr_init(&attrs); pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_ADAPTIVE_NP); - const int err = pthread_mutex_init(&m_State, &attrs); + const int err = pthread_mutex_init(&m_state, &attrs); pthread_mutexattr_destroy(&attrs); #else - const int err = pthread_mutex_init(&m_State, NULL); + const int err = pthread_mutex_init(&m_state, nullptr); #endif if (BOOST_UNLIKELY(err != 0)) - throw_exception< thread_resource_error >(err, "Failed to initialize an adaptive mutex", "adaptive_mutex::adaptive_mutex()", __FILE__, __LINE__); + throw_system_error(err, "Failed to initialize an adaptive mutex", "adaptive_mutex::adaptive_mutex()", __FILE__, __LINE__); } + // Non-copyable + adaptive_mutex(adaptive_mutex const&) = delete; + adaptive_mutex& operator= (adaptive_mutex const&) = delete; + ~adaptive_mutex() { - BOOST_VERIFY(pthread_mutex_destroy(&m_State) == 0); + BOOST_VERIFY(pthread_mutex_destroy(&m_state) == 0); } bool try_lock() { - const int err = pthread_mutex_trylock(&m_State); + const int err = pthread_mutex_trylock(&m_state); if (err == 0) return true; if (BOOST_UNLIKELY(err != EBUSY)) - throw_exception< lock_error >(err, "Failed to lock an adaptive mutex", "adaptive_mutex::try_lock()", __FILE__, __LINE__); + throw_system_error(err, "Failed to lock an adaptive mutex", "adaptive_mutex::try_lock()", __FILE__, __LINE__); return false; } void lock() { - const int err = pthread_mutex_lock(&m_State); + const int err = pthread_mutex_lock(&m_state); if (BOOST_UNLIKELY(err != 0)) - throw_exception< lock_error >(err, "Failed to lock an adaptive mutex", "adaptive_mutex::lock()", __FILE__, __LINE__); + throw_system_error(err, "Failed to lock an adaptive mutex", "adaptive_mutex::lock()", __FILE__, __LINE__); } - void unlock() + void unlock() noexcept { - BOOST_VERIFY(pthread_mutex_unlock(&m_State) == 0); + BOOST_VERIFY(pthread_mutex_unlock(&m_state) == 0); } - // Non-copyable - BOOST_DELETED_FUNCTION(adaptive_mutex(adaptive_mutex const&)) - BOOST_DELETED_FUNCTION(adaptive_mutex& operator= (adaptive_mutex const&)) - private: - template< typename ExceptionT > - static BOOST_NOINLINE BOOST_LOG_NORETURN void throw_exception(int err, const char* descr, const char* func, const char* file, int line) + static BOOST_NOINLINE BOOST_LOG_NORETURN void throw_system_error(int err, const char* descr, const char* func, const char* file, int line) { -#if !defined(BOOST_EXCEPTION_DISABLE) - boost::exception_detail::throw_exception_(ExceptionT(err, descr), func, file, line); -#else - boost::throw_exception(ExceptionT(err, descr)); -#endif + boost::throw_exception(std::system_error(std::error_code(err, std::system_category()), descr), boost::source_location(file, line, func)); } }; diff --git a/include/boost/log/detail/allocator_traits.hpp b/include/boost/log/detail/allocator_traits.hpp index 3f9501c655..371b802960 100644 --- a/include/boost/log/detail/allocator_traits.hpp +++ b/include/boost/log/detail/allocator_traits.hpp @@ -19,8 +19,10 @@ #include #include #if defined(BOOST_NO_CXX11_ALLOCATOR) -#include +#include +#include #endif +#include #include #ifdef BOOST_HAS_PRAGMA_ONCE @@ -37,18 +39,14 @@ namespace aux { #if !defined(BOOST_NO_CXX11_ALLOCATOR) using std::allocator_traits; #else -using boost::container::allocator_traits; +using boost::allocator_traits; #endif /*! * \brief A standalone trait to rebind an allocator to another type. * - * The important difference from std::allocator_traits<Alloc>::rebind_alloc<U> is that this - * trait does not require template aliases and thus is compatible with C++03. There is - * boost::container::allocator_traits<Alloc>::portable_rebind_alloc<U>, but it is not present in std::allocator_traits. - * It will also attempt to instantiate the allocator type to test if it provides the nested rebind template. We don't want - * that to happen because it prohibits using std::allocator<void> in C++17 and later, which deprecated - * this allocator specialization. This standalone trait does not use the nested rebind template in this case. + * This trait mostly exists to hide differences between std::allocator_traits and boost::allocator_traits + * in terms of allocator rebinding and also provide custom behavior in some cases. */ template< typename Allocator, typename U > struct rebind_alloc @@ -56,12 +54,24 @@ struct rebind_alloc #if !defined(BOOST_NO_CXX11_ALLOCATOR) typedef typename std::allocator_traits< Allocator >::BOOST_NESTED_TEMPLATE rebind_alloc< U > type; #else - typedef typename boost::container::allocator_traits< Allocator >::BOOST_NESTED_TEMPLATE portable_rebind_alloc< U >::type type; + typedef typename boost::allocator_rebind< Allocator, U >::type type; #endif }; +/*! + * This specialization mostly exists to keep std::allocator<void> working. + * The default template will attempt to instantiate the allocator type to test if it provides the nested rebind template. + * We don't want that to happen because it prohibits using std::allocator<void> in C++17 and later, which deprecated + * this allocator specialization. This specialization does not use the nested rebind template in this case. + */ +template< typename T, typename U > +struct rebind_alloc< std::allocator< T >, U > +{ + typedef std::allocator< U > type; +}; + template< typename U > -struct rebind_alloc< std::allocator< void >, U > +struct rebind_alloc< use_std_allocator, U > { typedef std::allocator< U > type; }; diff --git a/include/boost/log/detail/attachable_sstream_buf.hpp b/include/boost/log/detail/attachable_sstream_buf.hpp index e196689c80..b7005ce248 100644 --- a/include/boost/log/detail/attachable_sstream_buf.hpp +++ b/include/boost/log/detail/attachable_sstream_buf.hpp @@ -22,8 +22,8 @@ #include #include #include +#include #include -#include #include #include @@ -244,7 +244,7 @@ class basic_ostringstreambuf : protected: //! Puts all buffered data to the string - int sync() + int sync() BOOST_OVERRIDE { char_type* pBase = this->pbase(); char_type* pPtr = this->pptr(); @@ -256,7 +256,7 @@ class basic_ostringstreambuf : return 0; } //! Puts an unbuffered character to the string - int_type overflow(int_type c) + int_type overflow(int_type c) BOOST_OVERRIDE { this_type::sync(); if (!traits_type::eq_int_type(c, traits_type::eof())) @@ -268,7 +268,7 @@ class basic_ostringstreambuf : return traits_type::not_eof(c); } //! Puts a character sequence to the string - std::streamsize xsputn(const char_type* s, std::streamsize n) + std::streamsize xsputn(const char_type* s, std::streamsize n) BOOST_OVERRIDE { this_type::sync(); return static_cast< std::streamsize >(this->append(s, static_cast< size_type >(n))); @@ -278,43 +278,45 @@ class basic_ostringstreambuf : size_type length_until_boundary(const char_type* s, size_type n, size_type max_size) const { BOOST_ASSERT(max_size <= n); - return length_until_boundary(s, n, max_size, boost::integral_constant< bool, sizeof(char_type) == 1u >()); + return length_until_boundary(s, n, max_size, boost::integral_constant< std::size_t, sizeof(char_type) >()); } private: //! Finds the string length so that it includes only complete characters, and does not exceed \a max_size - size_type length_until_boundary(const char_type* s, size_type, size_type max_size, boost::true_type) const + size_type length_until_boundary(const char_type* s, size_type n, size_type max_size, boost::integral_constant< std::size_t, 1u >) const { std::locale loc = this->getloc(); std::codecvt< wchar_t, char, std::mbstate_t > const& fac = std::use_facet< std::codecvt< wchar_t, char, std::mbstate_t > >(loc); std::mbstate_t mbs = std::mbstate_t(); - return static_cast< size_type >(fac.length(mbs, s, s + max_size, ~static_cast< std::size_t >(0u))); + return static_cast< size_type >(fac.length(mbs, s, s + max_size, n)); } //! Finds the string length so that it includes only complete characters, and does not exceed \a max_size - static size_type length_until_boundary(const char_type* s, size_type n, size_type max_size, boost::false_type) + static size_type length_until_boundary(const char_type* s, size_type n, size_type max_size, boost::integral_constant< std::size_t, 2u >) { - // Note: Although it's not required to be true for wchar_t, here we assume that the string has Unicode encoding. + // Note: Although it's not required to be true for wchar_t, here we assume that the string has Unicode encoding (UTF-16 or UCS-2). // Compilers use some version of Unicode for wchar_t on all tested platforms, and std::locale doesn't offer a way // to find the character boundary for character types other than char anyway. - typedef boost::locale::utf::utf_traits< char_type > utf_traits; - size_type pos = max_size; while (pos > 0u) { --pos; - if (utf_traits::is_lead(s[pos])) - { - const char_type* p = s + pos; - boost::locale::utf::code_point cp = utf_traits::decode(p, s + n); - if (boost::locale::utf::is_valid_codepoint(cp) && p <= (s + max_size)) - return static_cast< size_type >(p - s); - } + uint_fast16_t c = static_cast< uint_fast16_t >(s[pos]); + // Check if this is a leading surrogate + if ((c & 0xFC00u) != 0xD800u) + return pos + 1u; } return 0u; } + //! Finds the string length so that it includes only complete characters, and does not exceed \a max_size + static size_type length_until_boundary(const char_type* s, size_type n, size_type max_size, boost::integral_constant< std::size_t, 4u >) + { + // In UTF-32 and UCS-4 one code point is encoded as one code unit + return max_size; + } + //! Copy constructor (closed) BOOST_DELETED_FUNCTION(basic_ostringstreambuf(basic_ostringstreambuf const& that)) //! Assignment (closed) diff --git a/include/boost/log/detail/config.hpp b/include/boost/log/detail/config.hpp index 189e5e4746..4f188ef13e 100644 --- a/include/boost/log/detail/config.hpp +++ b/include/boost/log/detail/config.hpp @@ -17,16 +17,8 @@ #ifndef BOOST_LOG_DETAIL_CONFIG_HPP_INCLUDED_ #define BOOST_LOG_DETAIL_CONFIG_HPP_INCLUDED_ -// This check must be before any system headers are included, or __MSVCRT_VERSION__ may get defined to 0x0600 -#if defined(__MINGW32__) && !defined(__MSVCRT_VERSION__) -// Target MinGW headers to at least MSVC 7.0 runtime by default. This will enable some useful functions. -#define __MSVCRT_VERSION__ 0x0700 -#endif - -#include - // Try including WinAPI config as soon as possible so that any other headers don't include Windows SDK headers -#if defined(BOOST_OS_WINDOWS_AVAILABLE) +#if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__) #include #endif @@ -42,10 +34,6 @@ # define BOOST_LOG_HAS_PRAGMA_DETECT_MISMATCH #endif -#if defined(BOOST_LOG_HAS_PRAGMA_DETECT_MISMATCH) -#include -#endif - #if !defined(BOOST_WINDOWS) # ifndef BOOST_LOG_WITHOUT_DEBUG_OUTPUT # define BOOST_LOG_WITHOUT_DEBUG_OUTPUT @@ -104,16 +92,20 @@ # define BOOST_LOG_BROKEN_CONSTANT_EXPRESSIONS #endif -#if defined(BOOST_NO_CXX11_HDR_CODECVT) +#if (defined(BOOST_NO_CXX11_HDR_CODECVT) && BOOST_CXX_VERSION < 201703) || (BOOST_CXX_VERSION >= 202002) || \ + (defined(_MSVC_STL_VERSION) && _MSVC_STL_VERSION < 142) // The compiler does not support std::codecvt and std::codecvt specializations. // The BOOST_NO_CXX11_HDR_CODECVT means there's no usable , which is slightly different from this macro. // But in order for to be implemented the std::codecvt specializations have to be implemented as well. + // We need to check the C++ version as well, since is deprecated from C++17 onwards which may cause + // BOOST_NO_CXX11_HDR_CODECVT to be set, even though std::codecvt in is just fine. + // The std::codecvt and std::codecvt specializations were eventually deprecated in C++20. # define BOOST_LOG_NO_CXX11_CODECVT_FACETS #endif -#if defined(__CYGWIN__) +#if defined(__CYGWIN__) && !defined(BOOST_LOG_WITHOUT_ASIO) // Boost.ASIO is broken on Cygwin -# define BOOST_LOG_NO_ASIO +# define BOOST_LOG_WITHOUT_ASIO #endif #if defined(__VXWORKS__) @@ -123,13 +115,13 @@ # include #endif -#if (!defined(__CRYSTAX__) && defined(__ANDROID__) && (__ANDROID_API__+0) < 21) \ - || (defined(__VXWORKS__) && !defined(_WRS_CONFIG_USER_MANAGEMENT)) +#if (!defined(__CRYSTAX__) && defined(__ANDROID__) && (__ANDROID_API__ < 21)) || \ + (defined(__VXWORKS__) && !defined(_WRS_CONFIG_USER_MANAGEMENT)) // Until Android API version 21 Google NDK does not provide getpwuid_r # define BOOST_LOG_NO_GETPWUID_R #endif -#if !defined(BOOST_LOG_USE_NATIVE_SYSLOG) && defined(BOOST_LOG_NO_ASIO) +#if !defined(BOOST_LOG_USE_NATIVE_SYSLOG) && defined(BOOST_LOG_WITHOUT_ASIO) # ifndef BOOST_LOG_WITHOUT_SYSLOG # define BOOST_LOG_WITHOUT_SYSLOG # endif @@ -147,21 +139,37 @@ #define BOOST_LOG_NO_CXX11_ARG_PACKS_TO_NON_VARIADIC_ARGS_EXPANSION #endif -#if defined(BOOST_NO_CXX11_CONSTEXPR) || (defined(BOOST_GCC) && ((BOOST_GCC+0) / 100) <= 406) +#if defined(BOOST_NO_CXX11_CONSTEXPR) || (defined(BOOST_GCC) && (BOOST_GCC / 100) <= 406) // GCC 4.6 does not support in-class brace initializers for static constexpr array members #define BOOST_LOG_NO_CXX11_CONSTEXPR_DATA_MEMBER_BRACE_INITIALIZERS #endif -#if defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS) || (defined(BOOST_GCC) && ((BOOST_GCC+0) / 100) <= 406) -// GCC 4.6 cannot handle a defaulted function with noexcept specifier +#if defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS) || (defined(BOOST_GCC) && (BOOST_GCC / 100) <= 406) +// GCC 4.6 cannot handle defaulted functions with noexcept specifier or virtual functions #define BOOST_LOG_NO_CXX11_DEFAULTED_NOEXCEPT_FUNCTIONS +#define BOOST_LOG_NO_CXX11_DEFAULTED_VIRTUAL_FUNCTIONS #endif -#if defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS) || (defined(BOOST_CLANG) && (((__clang_major__+0) == 3) && ((__clang_minor__+0) <= 1))) +#if defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS) || (defined(BOOST_CLANG) && ((__clang_major__ == 3) && (__clang_minor__ <= 1))) // Clang 3.1 cannot handle a defaulted constexpr constructor in some cases (presumably, if the class contains a member with a constexpr constructor) #define BOOST_LOG_NO_CXX11_DEFAULTED_CONSTEXPR_CONSTRUCTORS #endif +// The macro indicates that the compiler does not support C++20 pack expansions in lambda init-captures. +// Early gcc, clang and MSVC versions support C++20 pack expansions in lambda init-captures, +// but define __cpp_init_captures to a lower value. +#if (!defined(__cpp_init_captures) || (__cpp_init_captures < 201803)) && \ + !(\ + BOOST_CXX_VERSION > 201703 && \ + (\ + (defined(BOOST_GCC) && (BOOST_GCC >= 90000)) || \ + (defined(BOOST_CLANG) && (BOOST_CLANG_VERSION >= 90000)) || \ + (defined(BOOST_MSVC) && (BOOST_MSVC >= 1922))\ + )\ + ) +#define BOOST_LOG_NO_CXX20_PACK_EXPANSION_IN_LAMBDA_INIT_CAPTURE +#endif + #if defined(_MSC_VER) # define BOOST_LOG_NO_VTABLE __declspec(novtable) #else @@ -171,6 +179,14 @@ // An MS-like compilers' extension that allows to optimize away the needless code #if defined(_MSC_VER) # define BOOST_LOG_ASSUME(expr) __assume(expr) +#elif defined(__has_builtin) +// Clang 3.6 adds __builtin_assume, but enabling it causes weird compilation errors, where the compiler +// doesn't see one of attachable_sstream_buf::append overloads. It works fine with Clang 3.7 and later. +# if __has_builtin(__builtin_assume) && (!defined(__clang__) || (__clang_major__ * 100 + __clang_minor__) >= 307) +# define BOOST_LOG_ASSUME(expr) __builtin_assume(expr) +# else +# define BOOST_LOG_ASSUME(expr) +# endif #else # define BOOST_LOG_ASSUME(expr) #endif @@ -245,15 +261,7 @@ // other Boost libraries. We explicitly add comments here for other libraries. // In dynamic-library builds this is not needed. # if !defined(BOOST_LOG_DLL) -# include # include -# if !defined(BOOST_DATE_TIME_NO_LIB) && !defined(BOOST_DATE_TIME_SOURCE) -# define BOOST_LIB_NAME boost_date_time -# if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_DATE_TIME_DYN_LINK) -# define BOOST_DYN_LINK -# endif -# include -# endif // Boost.Thread's config is included below, if needed # endif # endif // auto-linking disabled @@ -328,7 +336,9 @@ namespace boost { # if defined(BOOST_THREAD_PLATFORM_PTHREAD) # define BOOST_LOG_VERSION_NAMESPACE v2_mt_posix # elif defined(BOOST_THREAD_PLATFORM_WIN32) -# if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 +# if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN8 +# define BOOST_LOG_VERSION_NAMESPACE v2_mt_nt62 +# elif BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 # define BOOST_LOG_VERSION_NAMESPACE v2_mt_nt6 # else # define BOOST_LOG_VERSION_NAMESPACE v2_mt_nt5 @@ -344,7 +354,9 @@ namespace boost { # if defined(BOOST_THREAD_PLATFORM_PTHREAD) # define BOOST_LOG_VERSION_NAMESPACE v2s_mt_posix # elif defined(BOOST_THREAD_PLATFORM_WIN32) -# if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 +# if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN8 +# define BOOST_LOG_VERSION_NAMESPACE v2s_mt_nt62 +# elif BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 # define BOOST_LOG_VERSION_NAMESPACE v2s_mt_nt6 # else # define BOOST_LOG_VERSION_NAMESPACE v2s_mt_nt5 @@ -365,7 +377,7 @@ inline namespace BOOST_LOG_VERSION_NAMESPACE {} # define BOOST_LOG_OPEN_NAMESPACE namespace log { inline namespace BOOST_LOG_VERSION_NAMESPACE { # define BOOST_LOG_CLOSE_NAMESPACE }} -# elif defined(BOOST_GCC) && (BOOST_GCC+0) >= 40400 +# elif defined(BOOST_GCC) && (BOOST_GCC >= 40400) // GCC 7 deprecated strong using directives but allows inline namespaces in C++03 mode since GCC 4.4. __extension__ inline namespace BOOST_LOG_VERSION_NAMESPACE {} @@ -398,7 +410,7 @@ namespace log {} #endif // !defined(BOOST_LOG_DOXYGEN_PASS) #if defined(BOOST_LOG_HAS_PRAGMA_DETECT_MISMATCH) -#pragma detect_mismatch("boost_log_abi", BOOST_PP_STRINGIZE(BOOST_LOG_VERSION_NAMESPACE)) +#pragma detect_mismatch("boost_log_abi", BOOST_STRINGIZE(BOOST_LOG_VERSION_NAMESPACE)) #endif } // namespace boost diff --git a/include/boost/log/detail/date_time_format_parser.hpp b/include/boost/log/detail/date_time_format_parser.hpp index 3219690025..8d66d354ff 100644 --- a/include/boost/log/detail/date_time_format_parser.hpp +++ b/include/boost/log/detail/date_time_format_parser.hpp @@ -343,21 +343,21 @@ struct date_time_format_parser_callback : typedef CharT char_type; //! Destructor - virtual ~date_time_format_parser_callback() {} + ~date_time_format_parser_callback() BOOST_OVERRIDE {} /*! * \brief The function is called when the parser discovers a string literal in the format string * * \param lit The string of characters not interpreted as a placeholder */ - virtual void on_literal(iterator_range< const char_type* > const& lit) = 0; + void on_literal(iterator_range< const char_type* > const& lit) BOOST_OVERRIDE = 0; /*! * \brief The method is called when an unknown placeholder is found in the format string * * \param ph The placeholder with the leading percent sign */ - virtual void on_placeholder(iterator_range< const char_type* > const& ph) + void on_placeholder(iterator_range< const char_type* > const& ph) BOOST_OVERRIDE { // By default interpret all unrecognized placeholders as literals on_literal(ph); diff --git a/include/boost/log/detail/enqueued_record.hpp b/include/boost/log/detail/enqueued_record.hpp index f4821363a2..e1926a68bf 100644 --- a/include/boost/log/detail/enqueued_record.hpp +++ b/include/boost/log/detail/enqueued_record.hpp @@ -17,10 +17,10 @@ #ifndef BOOST_LOG_DETAIL_ENQUEUED_RECORD_HPP_INCLUDED_ #define BOOST_LOG_DETAIL_ENQUEUED_RECORD_HPP_INCLUDED_ +#include #include #include #include -#include #include #include @@ -60,29 +60,29 @@ class enqueued_record } }; - boost::log::aux::timestamp m_timestamp; + std::chrono::steady_clock::time_point m_timestamp; record_view m_record; - enqueued_record(enqueued_record const& that) : m_timestamp(that.m_timestamp), m_record(that.m_record) + enqueued_record(enqueued_record const& that) BOOST_NOEXCEPT : m_timestamp(that.m_timestamp), m_record(that.m_record) { } - enqueued_record(BOOST_RV_REF(enqueued_record) that) : + enqueued_record(BOOST_RV_REF(enqueued_record) that) BOOST_NOEXCEPT : m_timestamp(that.m_timestamp), m_record(boost::move(that.m_record)) { } explicit enqueued_record(record_view const& rec) : - m_timestamp(boost::log::aux::get_timestamp()), + m_timestamp(std::chrono::steady_clock::now()), m_record(rec) { } - enqueued_record& operator= (BOOST_COPY_ASSIGN_REF(enqueued_record) that) + enqueued_record& operator= (BOOST_COPY_ASSIGN_REF(enqueued_record) that) BOOST_NOEXCEPT { m_timestamp = that.m_timestamp; m_record = that.m_record; return *this; } - enqueued_record& operator= (BOOST_RV_REF(enqueued_record) that) + enqueued_record& operator= (BOOST_RV_REF(enqueued_record) that) BOOST_NOEXCEPT { m_timestamp = that.m_timestamp; m_record = boost::move(that.m_record); diff --git a/include/boost/log/detail/event.hpp b/include/boost/log/detail/event.hpp index 0168e170e7..05716d1c33 100644 --- a/include/boost/log/detail/event.hpp +++ b/include/boost/log/detail/event.hpp @@ -1,5 +1,5 @@ /* - * Copyright Andrey Semashev 2007 - 2015. + * Copyright Andrey Semashev 2007 - 2021. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) @@ -21,26 +21,25 @@ #ifndef BOOST_LOG_NO_THREADS -#if defined(BOOST_THREAD_PLATFORM_PTHREAD) -# include -# if (defined(linux) || defined(__linux) || defined(__linux__)) && BOOST_ATOMIC_INT_LOCK_FREE == 2 -# include -# define BOOST_LOG_EVENT_USE_FUTEX -# elif defined(_POSIX_SEMAPHORES) && (_POSIX_SEMAPHORES + 0) > 0 && BOOST_ATOMIC_FLAG_LOCK_FREE == 2 -# include -# include -# include -# define BOOST_LOG_EVENT_USE_POSIX_SEMAPHORE -# endif +#include + +#if BOOST_ATOMIC_HAS_NATIVE_INT32_WAIT_NOTIFY == 2 +#include +#include +#define BOOST_LOG_EVENT_USE_ATOMIC +#elif defined(BOOST_THREAD_PLATFORM_PTHREAD) && defined(_POSIX_SEMAPHORES) && _POSIX_SEMAPHORES > 0 && BOOST_ATOMIC_FLAG_LOCK_FREE == 2 +#include +#include +#include +#define BOOST_LOG_EVENT_USE_POSIX_SEMAPHORE #elif defined(BOOST_THREAD_PLATFORM_WIN32) -# include -# define BOOST_LOG_EVENT_USE_WINAPI -#endif - -#if !defined(BOOST_LOG_EVENT_USE_POSIX_SEMAPHORE) && !defined(BOOST_LOG_EVENT_USE_WINAPI) -# include -# include -# define BOOST_LOG_EVENT_USE_BOOST_CONDITION +#include +#include +#define BOOST_LOG_EVENT_USE_WINAPI +#else +#include +#include +#define BOOST_LOG_EVENT_USE_STD_CONDITION_VARIABLE #endif #include @@ -51,18 +50,16 @@ BOOST_LOG_OPEN_NAMESPACE namespace aux { -#if defined(BOOST_LOG_EVENT_USE_FUTEX) +#if defined(BOOST_LOG_EVENT_USE_ATOMIC) -class futex_based_event +class atomic_based_event { private: - boost::atomic< int > m_state; + boost::atomic< boost::uint32_t > m_state; public: //! Default constructor - BOOST_LOG_API futex_based_event(); - //! Destructor - BOOST_LOG_API ~futex_based_event(); + atomic_based_event() : m_state(0u) {} //! Waits for the object to become signalled BOOST_LOG_API void wait(); @@ -70,11 +67,11 @@ class futex_based_event BOOST_LOG_API void set_signalled(); // Copying prohibited - BOOST_DELETED_FUNCTION(futex_based_event(futex_based_event const&)) - BOOST_DELETED_FUNCTION(futex_based_event& operator= (futex_based_event const&)) + BOOST_DELETED_FUNCTION(atomic_based_event(atomic_based_event const&)) + BOOST_DELETED_FUNCTION(atomic_based_event& operator= (atomic_based_event const&)) }; -typedef futex_based_event event; +typedef atomic_based_event event; #elif defined(BOOST_LOG_EVENT_USE_POSIX_SEMAPHORE) @@ -107,7 +104,7 @@ typedef sem_based_event event; class winapi_based_event { private: - boost::uint32_t m_state; + boost::atomic< boost::uint32_t > m_state; void* m_event; public: @@ -133,8 +130,8 @@ typedef winapi_based_event event; class generic_event { private: - boost::mutex m_mutex; - boost::condition_variable m_cond; + std::mutex m_mutex; + std::condition_variable m_cond; bool m_state; public: diff --git a/include/boost/log/detail/footer.hpp b/include/boost/log/detail/footer.hpp index ba0d2f0cb4..e749822cf6 100644 --- a/include/boost/log/detail/footer.hpp +++ b/include/boost/log/detail/footer.hpp @@ -7,11 +7,12 @@ #if !defined(BOOST_LOG_ENABLE_WARNINGS) -#if defined(_MSC_VER) +#if defined(_MSC_VER) && !defined(__clang__) #pragma warning(pop) -#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 +#elif (defined(__GNUC__) && !(defined(__INTEL_COMPILER) || defined(__ICL) || defined(__ICC) || defined(__ECC)) \ + && (__GNUC__ * 100 + __GNUC_MINOR__) >= 406) || defined(__clang__) #pragma GCC diagnostic pop diff --git a/include/boost/log/detail/format.hpp b/include/boost/log/detail/format.hpp index 1394c14697..0ce0340b7f 100644 --- a/include/boost/log/detail/format.hpp +++ b/include/boost/log/detail/format.hpp @@ -94,13 +94,13 @@ struct format_description { } - format_description(BOOST_RV_REF(format_description) that) + format_description(BOOST_RV_REF(format_description) that) BOOST_NOEXCEPT { literal_chars.swap(that.literal_chars); format_elements.swap(that.format_elements); } - format_description& operator= (format_description that) + format_description& operator= (format_description that) BOOST_NOEXCEPT { literal_chars.swap(that.literal_chars); format_elements.swap(that.format_elements); diff --git a/include/boost/log/detail/header.hpp b/include/boost/log/detail/header.hpp index 95984ff402..cb7b315323 100644 --- a/include/boost/log/detail/header.hpp +++ b/include/boost/log/detail/header.hpp @@ -9,7 +9,7 @@ #if !defined(BOOST_LOG_ENABLE_WARNINGS) -#if defined(_MSC_VER) +#if defined(_MSC_VER) && !defined(__clang__) #pragma warning(push, 3) // 'm_A' : class 'A' needs to have dll-interface to be used by clients of class 'B' @@ -42,11 +42,17 @@ #pragma warning(disable: 4456) // declaration of 'A' hides global declaration #pragma warning(disable: 4459) +// copy constructor was implicitly defined as deleted because a base class copy constructor is inaccessible or deleted +#pragma warning(disable: 4625) +// assignment operator was implicitly defined as deleted because a base class assignment operator is inaccessible or deleted +#pragma warning(disable: 4626) // 'X': This function or variable may be unsafe. Consider using Y instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. #pragma warning(disable: 4996) -#elif defined(__GNUC__) && !(defined(__INTEL_COMPILER) || defined(__ICL) || defined(__ICC) || defined(__ECC)) \ - && (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 +#elif (defined(__GNUC__) && !(defined(__INTEL_COMPILER) || defined(__ICL) || defined(__ICC) || defined(__ECC)) \ + && (__GNUC__ * 100 + __GNUC_MINOR__) >= 406) || defined(__clang__) + +// Note: clang-cl goes here as well, as it seems to support gcc-style warning control pragmas. #pragma GCC diagnostic push // 'var' defined but not used @@ -61,6 +67,11 @@ #pragma GCC diagnostic ignored "-Wunused-local-typedefs" #endif +#if defined(__clang__) +// the argument to '__builtin_assume' has side effects that will be discarded +#pragma clang diagnostic ignored "-Wassume" +#endif // defined(__clang__) + #endif #endif // !defined(BOOST_LOG_ENABLE_WARNINGS) diff --git a/include/boost/log/detail/locking_ptr.hpp b/include/boost/log/detail/locking_ptr.hpp index 7790c4dceb..1596ad13ab 100644 --- a/include/boost/log/detail/locking_ptr.hpp +++ b/include/boost/log/detail/locking_ptr.hpp @@ -17,9 +17,9 @@ #define BOOST_LOG_DETAIL_LOCKING_PTR_HPP_INCLUDED_ #include +#include // try_to_lock_t #include #include -#include #include #include #include @@ -66,7 +66,7 @@ class locking_ptr m_pLock->lock(); } //! Constructor - locking_ptr(shared_ptr< element_type > const& p, lockable_type& l, try_to_lock_t const&) : m_pElement(p), m_pLock(&l) + locking_ptr(shared_ptr< element_type > const& p, lockable_type& l, std::try_to_lock_t) : m_pElement(p), m_pLock(&l) { if (!m_pLock->try_lock()) { diff --git a/include/boost/log/detail/locks.hpp b/include/boost/log/detail/locks.hpp index 224d8e59bb..c5aae25568 100644 --- a/include/boost/log/detail/locks.hpp +++ b/include/boost/log/detail/locks.hpp @@ -17,6 +17,37 @@ #define BOOST_LOG_DETAIL_LOCKS_HPP_INCLUDED_ #include + +#ifndef BOOST_LOG_NO_THREADS + +#include +BOOST_MOVE_STD_NS_BEG + +// Forward declaration of the standard locks. Specified to avoid including and . +#if !defined(BOOST_MSSTL_VERSION) || (BOOST_MSSTL_VERSION != 140) +template< typename > +class lock_guard; +#else +// MSVC 14.0 has a non-confogrming lock_guard +template< typename... > +class lock_guard; +#endif +template< typename > +class unique_lock; +#if !defined(BOOST_NO_CXX14_HDR_SHARED_MUTEX) +template< typename > +class shared_lock; +#endif +#if defined(__cpp_lib_scoped_lock) && (__cpp_lib_scoped_lock >= 201703l) +template< typename... > +class scoped_lock; +#endif + +BOOST_MOVE_STD_NS_END +#include + +#endif // BOOST_LOG_NO_THREADS + #include #ifdef BOOST_HAS_PRAGMA_ONCE @@ -55,7 +86,7 @@ class no_lock /*! * Constructs the pseudo-lock. The mutex is not affected during the construction. */ - explicit no_lock(MutexT&) {} + explicit no_lock(MutexT&) BOOST_NOEXCEPT {} private: no_lock(no_lock const&); @@ -117,7 +148,7 @@ struct exclusive_auto_unlocker template< typename MutexT > struct exclusive_lock_guard { - explicit exclusive_lock_guard(MutexT& m) : m_Mutex(m) + explicit exclusive_lock_guard(MutexT& m) BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(m.lock())) : m_Mutex(m) { m.lock(); } @@ -137,7 +168,7 @@ struct exclusive_lock_guard template< typename MutexT > struct shared_lock_guard { - explicit shared_lock_guard(MutexT& m) : m_Mutex(m) + explicit shared_lock_guard(MutexT& m) BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(m.lock_shared())) : m_Mutex(m) { m.lock_shared(); } @@ -158,7 +189,7 @@ template< typename MutexT1, typename MutexT2 > class multiple_unique_lock2 { public: - multiple_unique_lock2(MutexT1& m1, MutexT2& m2) : + multiple_unique_lock2(MutexT1& m1, MutexT2& m2) BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT_EXPR(m1.lock()) && BOOST_NOEXCEPT_EXPR(m2.lock())) : m_p1(&m1), m_p2(&m2) { diff --git a/include/boost/log/detail/named_scope_fmt_pp.hpp b/include/boost/log/detail/named_scope_fmt_pp.hpp index 16e4508919..7c8966e9f7 100644 --- a/include/boost/log/detail/named_scope_fmt_pp.hpp +++ b/include/boost/log/detail/named_scope_fmt_pp.hpp @@ -41,7 +41,7 @@ BOOST_FORCEINLINE format_named_scope_actor< > format_named_scope(attribute_keyword< DescriptorT, ActorT > const& keyword, BOOST_PP_ENUM_BINARY_PARAMS(BOOST_PP_ITERATION(), ArgT, const& arg)) { - BOOST_STATIC_ASSERT_MSG((is_same< typename DescriptorT::value_type, attributes::named_scope::value_type >::value),\ + static_assert(is_same< typename DescriptorT::value_type, attributes::named_scope::value_type >::value, "Boost.Log: Named scope formatter only accepts attribute values of type attributes::named_scope::value_type."); typedef typename boost::log::aux::deduce_char_type< @@ -68,7 +68,7 @@ BOOST_FORCEINLINE format_named_scope_actor< > format_named_scope(attribute_actor< T, FallbackPolicyT, TagT, ActorT > const& placeholder, BOOST_PP_ENUM_BINARY_PARAMS(BOOST_PP_ITERATION(), ArgT, const& arg)) { - BOOST_STATIC_ASSERT_MSG((is_same< T, attributes::named_scope::value_type >::value),\ + static_assert(is_same< T, attributes::named_scope::value_type >::value, "Boost.Log: Named scope formatter only accepts attribute values of type attributes::named_scope::value_type."); typedef typename boost::log::aux::deduce_char_type< diff --git a/include/boost/log/detail/parameter_tools.hpp b/include/boost/log/detail/parameter_tools.hpp index 55695e3cde..bcf75f8ddd 100644 --- a/include/boost/log/detail/parameter_tools.hpp +++ b/include/boost/log/detail/parameter_tools.hpp @@ -16,7 +16,10 @@ #ifndef BOOST_LOG_DETAIL_PARAMETER_TOOLS_HPP_INCLUDED_ #define BOOST_LOG_DETAIL_PARAMETER_TOOLS_HPP_INCLUDED_ +#include +#include #include +#include #include #include #include @@ -43,36 +46,36 @@ public:\ BOOST_PP_REPEAT_FROM_TO(1, BOOST_LOG_MAX_PARAMETER_ARGS, macro, args) -#define BOOST_LOG_CTOR_FORWARD_1(n, types)\ +#define BOOST_LOG_CTOR_FORWARD_1(z, n, types)\ template< typename T0 >\ explicit BOOST_PP_TUPLE_ELEM(2, 0, types)(T0 const& arg0, typename boost::log::aux::enable_if_named_parameters< T0, boost::log::aux::sfinae_dummy >::type = boost::log::aux::sfinae_dummy()) :\ - BOOST_PP_TUPLE_ELEM(2, 1, types)((BOOST_PP_ENUM_PARAMS(n, arg))) {} + BOOST_PP_TUPLE_ELEM(2, 1, types)((BOOST_PP_ENUM_PARAMS_Z(z, n, arg))) {} -#define BOOST_LOG_CTOR_FORWARD_N(n, types)\ - template< BOOST_PP_ENUM_PARAMS(n, typename T) >\ - explicit BOOST_PP_TUPLE_ELEM(2, 0, types)(BOOST_PP_ENUM_BINARY_PARAMS(n, T, const& arg)) :\ - BOOST_PP_TUPLE_ELEM(2, 1, types)((BOOST_PP_ENUM_PARAMS(n, arg))) {} +#define BOOST_LOG_CTOR_FORWARD_N(z, n, types)\ + template< BOOST_PP_ENUM_PARAMS_Z(z, n, typename T) >\ + explicit BOOST_PP_TUPLE_ELEM(2, 0, types)(BOOST_PP_ENUM_BINARY_PARAMS_Z(z, n, T, const& arg)) :\ + BOOST_PP_TUPLE_ELEM(2, 1, types)((BOOST_PP_ENUM_PARAMS_Z(z, n, arg))) {} #define BOOST_LOG_CTOR_FORWARD(z, n, types)\ - BOOST_PP_IF(BOOST_PP_EQUAL(n, 1), BOOST_LOG_CTOR_FORWARD_1, BOOST_LOG_CTOR_FORWARD_N)(n, types) + BOOST_PP_IF(BOOST_PP_EQUAL(n, 1), BOOST_LOG_CTOR_FORWARD_1, BOOST_LOG_CTOR_FORWARD_N)(z, n, types) // The macro expands to a number of templated constructors that aggregate their named arguments // into an ArgumentsPack and pass it to the base class constructor. #define BOOST_LOG_PARAMETRIZED_CONSTRUCTORS_FORWARD(class_type, base_type)\ BOOST_LOG_PARAMETRIZED_CONSTRUCTORS_GEN(BOOST_LOG_CTOR_FORWARD, (class_type, base_type)) -#define BOOST_LOG_CTOR_CALL_1(n, types)\ +#define BOOST_LOG_CTOR_CALL_1(z, n, types)\ template< typename T0 >\ explicit BOOST_PP_TUPLE_ELEM(2, 0, types)(T0 const& arg0, typename boost::log::aux::enable_if_named_parameters< T0, boost::log::aux::sfinae_dummy >::type = boost::log::aux::sfinae_dummy())\ - { BOOST_PP_TUPLE_ELEM(2, 1, types)((BOOST_PP_ENUM_PARAMS(n, arg))); } + { BOOST_PP_TUPLE_ELEM(2, 1, types)((BOOST_PP_ENUM_PARAMS_Z(z, n, arg))); } -#define BOOST_LOG_CTOR_CALL_N(n, types)\ - template< BOOST_PP_ENUM_PARAMS(n, typename T) >\ - explicit BOOST_PP_TUPLE_ELEM(2, 0, types)(BOOST_PP_ENUM_BINARY_PARAMS(n, T, const& arg))\ - { BOOST_PP_TUPLE_ELEM(2, 1, types)((BOOST_PP_ENUM_PARAMS(n, arg))); } +#define BOOST_LOG_CTOR_CALL_N(z, n, types)\ + template< BOOST_PP_ENUM_PARAMS_Z(z, n, typename T) >\ + explicit BOOST_PP_TUPLE_ELEM(2, 0, types)(BOOST_PP_ENUM_BINARY_PARAMS_Z(z, n, T, const& arg))\ + { BOOST_PP_TUPLE_ELEM(2, 1, types)((BOOST_PP_ENUM_PARAMS_Z(z, n, arg))); } #define BOOST_LOG_CTOR_CALL(z, n, types)\ - BOOST_PP_IF(BOOST_PP_EQUAL(n, 1), BOOST_LOG_CTOR_CALL_1, BOOST_LOG_CTOR_CALL_N)(n, types) + BOOST_PP_IF(BOOST_PP_EQUAL(n, 1), BOOST_LOG_CTOR_CALL_1, BOOST_LOG_CTOR_CALL_N)(z, n, types) // The macro expands to a number of templated constructors that aggregate their named arguments // into an ArgumentsPack and pass it to a function call. @@ -121,26 +124,20 @@ struct make_arg_list< ArgT0, BOOST_PP_ENUM_PARAMS(BOOST_PP_DEC(BOOST_LOG_MAX_PAR #endif +#if !defined(BOOST_NO_CXX11_TEMPLATE_ALIASES) + template< typename T, typename R > -struct enable_if_named_parameters {}; +using enable_if_named_parameters = boost::enable_if_c< boost::mpl::or_< boost::is_base_of< boost::parameter::aux::tagged_argument_base, T >, boost::is_base_of< empty_arg_list, T > >::value, R >; -template< typename R > -struct enable_if_named_parameters< empty_arg_list, R > -{ - typedef R type; -}; +#else // !defined(BOOST_NO_CXX11_TEMPLATE_ALIASES) -template< typename Keyword, typename Arg, typename R > -struct enable_if_named_parameters< boost::parameter::aux::tagged_argument< Keyword, Arg >, R > +template< typename T, typename R > +struct enable_if_named_parameters : + public boost::enable_if_c< boost::mpl::or_< boost::is_base_of< boost::parameter::aux::tagged_argument_base, T >, boost::is_base_of< empty_arg_list, T > >::value, R > { - typedef R type; }; -template< typename TaggedArg, typename Next, typename R > -struct enable_if_named_parameters< boost::parameter::aux::arg_list< TaggedArg, Next >, R > -{ - typedef R type; -}; +#endif // !defined(BOOST_NO_CXX11_TEMPLATE_ALIASES) } // namespace aux diff --git a/include/boost/log/detail/pause.hpp b/include/boost/log/detail/pause.hpp deleted file mode 100644 index c1b43498f0..0000000000 --- a/include/boost/log/detail/pause.hpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright Andrey Semashev 2016. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ -/*! - * \file pause.hpp - * \author Andrey Semashev - * \date 06.01.2016 - * - * \brief This header is the Boost.Log library implementation, see the library documentation - * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. - */ - -#ifndef BOOST_LOG_DETAIL_PAUSE_HPP_INCLUDED_ -#define BOOST_LOG_DETAIL_PAUSE_HPP_INCLUDED_ - -#include -#include - -#ifdef BOOST_HAS_PRAGMA_ONCE -#pragma once -#endif - -#if defined(__INTEL_COMPILER) || defined(_MSC_VER) -# if defined(_M_IX86) -# define BOOST_LOG_AUX_PAUSE __asm { pause } -# elif defined(_M_AMD64) -extern "C" void _mm_pause(void); -# if defined(BOOST_MSVC) -# pragma intrinsic(_mm_pause) -# endif -# define BOOST_LOG_AUX_PAUSE _mm_pause() -# endif -#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) -# define BOOST_LOG_AUX_PAUSE __asm__ __volatile__("pause;") -#endif - -namespace boost { - -BOOST_LOG_OPEN_NAMESPACE - -namespace aux { - -BOOST_FORCEINLINE void pause() BOOST_NOEXCEPT -{ -#if defined(BOOST_LOG_AUX_PAUSE) - BOOST_LOG_AUX_PAUSE; -#endif -} - -} // namespace aux - -BOOST_LOG_CLOSE_NAMESPACE // namespace log - -} // namespace boost - -#include - -#endif // BOOST_LOG_DETAIL_PAUSE_HPP_INCLUDED_ diff --git a/include/boost/log/detail/sink_init_helpers.hpp b/include/boost/log/detail/sink_init_helpers.hpp index a3bbe0e8a1..8d98384cfa 100644 --- a/include/boost/log/detail/sink_init_helpers.hpp +++ b/include/boost/log/detail/sink_init_helpers.hpp @@ -17,12 +17,16 @@ #define BOOST_LOG_DETAIL_SINK_INIT_HELPERS_HPP_INCLUDED_ #include -#include #include #include +#include +#include #include -#include +#include #include +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) +#include +#endif #include #include #include @@ -30,6 +34,7 @@ #include #include #include +#include #include #ifdef BOOST_HAS_PRAGMA_ONCE @@ -44,18 +49,39 @@ namespace aux { // The function creates a filter functional object from the provided argument template< typename CharT > -inline filter acquire_filter(const CharT* filter) +inline typename boost::enable_if_c< + log::aux::is_character_type< CharT >::value, + filter +>::type acquire_filter(const CharT* filter) { return boost::log::parse_filter(filter); } + template< typename CharT, typename TraitsT, typename AllocatorT > inline filter acquire_filter(std::basic_string< CharT, TraitsT, AllocatorT > const& filter) { return boost::log::parse_filter(filter); } + +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) +template< typename CharT, typename TraitsT > +inline filter acquire_filter(std::basic_string_view< CharT, TraitsT > const& filter) +{ + const CharT* p = filter.data(); + return boost::log::parse_filter(p, p + filter.size()); +} +#endif // !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) + +template< typename CharT, typename TraitsT > +inline filter acquire_filter(boost::basic_string_view< CharT, TraitsT > const& filter) +{ + const CharT* p = filter.data(); + return boost::log::parse_filter(p, p + filter.size()); +} + template< typename FilterT > -inline typename boost::enable_if_c< - phoenix::is_actor< FilterT >::value, +inline typename boost::disable_if_c< + boost::is_array< FilterT >::value, FilterT const& >::type acquire_filter(FilterT const& filter) { @@ -64,31 +90,52 @@ inline typename boost::enable_if_c< // The function installs filter into the sink, if provided in the arguments pack template< typename SinkT, typename ArgsT > -inline void setup_filter(SinkT&, ArgsT const&, mpl::true_) +inline void setup_filter(SinkT&, ArgsT const&, boost::true_type) { } template< typename SinkT, typename ArgsT > -inline void setup_filter(SinkT& s, ArgsT const& args, mpl::false_) +inline void setup_filter(SinkT& s, ArgsT const& args, boost::false_type) { s.set_filter(aux::acquire_filter(args[keywords::filter])); } -// The function creates a filter functional object from the provided argument +// The function creates a formatter functional object from the provided argument template< typename CharT > -inline basic_formatter< CharT > acquire_formatter(const CharT* formatter) +inline typename boost::enable_if_c< + log::aux::is_character_type< CharT >::value, + basic_formatter< CharT > +>::type acquire_formatter(const CharT* formatter) { return boost::log::parse_formatter(formatter); } + template< typename CharT, typename TraitsT, typename AllocatorT > inline basic_formatter< CharT > acquire_formatter(std::basic_string< CharT, TraitsT, AllocatorT > const& formatter) { return boost::log::parse_formatter(formatter); } + +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) +template< typename CharT, typename TraitsT > +inline basic_formatter< CharT > acquire_formatter(std::basic_string_view< CharT, TraitsT > const& formatter) +{ + const CharT* p = formatter.data(); + return boost::log::parse_formatter(p, p + formatter.size()); +} +#endif // !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) + +template< typename CharT, typename TraitsT > +inline basic_formatter< CharT > acquire_formatter(boost::basic_string_view< CharT, TraitsT > const& formatter) +{ + const CharT* p = formatter.data(); + return boost::log::parse_formatter(p, p + formatter.size()); +} + template< typename FormatterT > -inline typename boost::enable_if_c< - phoenix::is_actor< FormatterT >::value, +inline typename boost::disable_if_c< + boost::is_array< FormatterT >::value, FormatterT const& >::type acquire_formatter(FormatterT const& formatter) { @@ -97,12 +144,12 @@ inline typename boost::enable_if_c< // The function installs filter into the sink, if provided in the arguments pack template< typename SinkT, typename ArgsT > -inline void setup_formatter(SinkT&, ArgsT const&, mpl::true_) +inline void setup_formatter(SinkT&, ArgsT const&, boost::true_type) { } template< typename SinkT, typename ArgsT > -inline void setup_formatter(SinkT& s, ArgsT const& args, mpl::false_) +inline void setup_formatter(SinkT& s, ArgsT const& args, boost::false_type) { s.set_formatter(aux::acquire_formatter(args[keywords::format])); } diff --git a/include/boost/log/detail/snprintf.hpp b/include/boost/log/detail/snprintf.hpp deleted file mode 100644 index 14c6a6b1dc..0000000000 --- a/include/boost/log/detail/snprintf.hpp +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright Andrey Semashev 2007 - 2015. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ -/*! - * \file snprintf.hpp - * \author Andrey Semashev - * \date 20.02.2009 - * - * \brief This header is the Boost.Log library implementation, see the library documentation - * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. - */ - -#ifndef BOOST_LOG_DETAIL_SNPRINTF_HPP_INCLUDED_ -#define BOOST_LOG_DETAIL_SNPRINTF_HPP_INCLUDED_ - -#include -#include -#include -#include -#ifdef BOOST_LOG_USE_WCHAR_T -#include -#endif // BOOST_LOG_USE_WCHAR_T -#include - -#ifdef BOOST_HAS_PRAGMA_ONCE -#pragma once -#endif - -namespace boost { - -BOOST_LOG_OPEN_NAMESPACE - -namespace aux { - -#if defined(_MSC_VER) || (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) - -// MSVC snprintfs are not conforming but they are good enough for our cases. -// MinGW32, at least the older versions up until gcc 4.7, also provide the non-conforming interface. -inline int vsnprintf(char* buf, std::size_t size, const char* format, std::va_list args) -{ - int n = _vsnprintf(buf, size, format, args); - if (static_cast< unsigned int >(n) >= size) - { - n = static_cast< int >(size); - buf[size - 1] = '\0'; - } - return n; -} - -# ifdef BOOST_LOG_USE_WCHAR_T -inline int vswprintf(wchar_t* buf, std::size_t size, const wchar_t* format, std::va_list args) -{ - int n = _vsnwprintf(buf, size, format, args); - if (static_cast< unsigned int >(n) >= size) - { - n = static_cast< int >(size); - buf[size - 1] = L'\0'; - } - return n; -} -# endif // BOOST_LOG_USE_WCHAR_T - -inline int snprintf(char* buf, std::size_t size, const char* format, ...) -{ - std::va_list args; - va_start(args, format); - int n = vsnprintf(buf, size, format, args); - va_end(args); - return n; -} - -# ifdef BOOST_LOG_USE_WCHAR_T -inline int swprintf(wchar_t* buf, std::size_t size, const wchar_t* format, ...) -{ - std::va_list args; - va_start(args, format); - int n = vswprintf(buf, size, format, args); - va_end(args); - return n; -} -# endif // BOOST_LOG_USE_WCHAR_T - -#else - -// Standard-conforming compilers already have the correct snprintfs -using ::snprintf; -using ::vsnprintf; - -# ifdef BOOST_LOG_USE_WCHAR_T -using ::swprintf; -using ::vswprintf; -# endif // BOOST_LOG_USE_WCHAR_T - -#endif - -} // namespace aux - -BOOST_LOG_CLOSE_NAMESPACE // namespace log - -} // namespace boost - -#include - -#endif // BOOST_LOG_DETAIL_SNPRINTF_HPP_INCLUDED_ diff --git a/include/boost/log/detail/thread_specific.hpp b/include/boost/log/detail/thread_specific.hpp index 7e38be5aab..badcbb9370 100644 --- a/include/boost/log/detail/thread_specific.hpp +++ b/include/boost/log/detail/thread_specific.hpp @@ -16,7 +16,6 @@ #ifndef BOOST_LOG_DETAIL_THREAD_SPECIFIC_HPP_INCLUDED_ #define BOOST_LOG_DETAIL_THREAD_SPECIFIC_HPP_INCLUDED_ -#include #include #include @@ -62,7 +61,7 @@ template< typename T > class thread_specific : public thread_specific_base { - BOOST_STATIC_ASSERT_MSG(sizeof(T) <= sizeof(void*) && is_pod< T >::value, "Boost.Log: Thread-specific values must be PODs and must not exceed the size of a pointer"); + static_assert(sizeof(T) <= sizeof(void*) && is_pod< T >::value, "Boost.Log: Thread-specific values must be PODs and must not exceed the size of a pointer"); //! Union to perform type casting union value_storage diff --git a/include/boost/log/detail/threadsafe_queue.hpp b/include/boost/log/detail/threadsafe_queue.hpp index b747ed4c5d..4015bc5f7e 100644 --- a/include/boost/log/detail/threadsafe_queue.hpp +++ b/include/boost/log/detail/threadsafe_queue.hpp @@ -1,5 +1,5 @@ /* - * Copyright Andrey Semashev 2007 - 2015. + * Copyright Andrey Semashev 2007 - 2021. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) @@ -27,11 +27,12 @@ #include #include #include -#include +#include #include #include #include -#include +#include +#include #include #include @@ -42,32 +43,30 @@ BOOST_LOG_OPEN_NAMESPACE namespace aux { //! Base class for the thread-safe queue implementation -struct threadsafe_queue_impl +class threadsafe_queue_impl { - struct BOOST_LOG_MAY_ALIAS pointer_storage - { - union - { - void* data[2]; - type_with_alignment< 2 * sizeof(void*) >::type alignment; - }; - }; - +public: struct node_base { - pointer_storage next; + boost::atomic< node_base* > next; }; +protected: + threadsafe_queue_impl(); + ~threadsafe_queue_impl(); + +public: static BOOST_LOG_API threadsafe_queue_impl* create(node_base* first_node); + static BOOST_LOG_API void destroy(threadsafe_queue_impl* impl) BOOST_NOEXCEPT; - static BOOST_LOG_API void* operator new (std::size_t size); - static BOOST_LOG_API void operator delete (void* p, std::size_t); + static BOOST_LOG_API node_base* reset_last_node(threadsafe_queue_impl* impl) BOOST_NOEXCEPT; + static BOOST_LOG_API bool unsafe_empty(const threadsafe_queue_impl* impl) BOOST_NOEXCEPT; + static BOOST_LOG_API void push(threadsafe_queue_impl* impl, node_base* p); + static BOOST_LOG_API bool try_pop(threadsafe_queue_impl* impl, node_base*& node_to_free, node_base*& node_with_value); - virtual ~threadsafe_queue_impl() {} - virtual node_base* reset_last_node() = 0; - virtual bool unsafe_empty() = 0; - virtual void push(node_base* p) = 0; - virtual bool try_pop(node_base*& node_to_free, node_base*& node_with_value) = 0; + // Copying and assignment is prohibited + BOOST_DELETED_FUNCTION(threadsafe_queue_impl(threadsafe_queue_impl const&)) + BOOST_DELETED_FUNCTION(threadsafe_queue_impl& operator= (threadsafe_queue_impl const&)) }; //! Thread-safe queue node type @@ -106,7 +105,7 @@ struct threadsafe_queue_node : * * The last requirement is not mandatory but is crucial for decent performance. */ -template< typename T, typename AllocatorT = std::allocator< void > > +template< typename T, typename AllocatorT = use_std_allocator > class threadsafe_queue : private boost::log::aux::rebind_alloc< AllocatorT, threadsafe_queue_node< T > >::type { @@ -161,7 +160,7 @@ class threadsafe_queue : allocator_type(alloc) { node* p = alloc_traits::allocate(get_allocator(), 1); - if (p) + if (BOOST_LIKELY(!!p)) { try { @@ -194,21 +193,21 @@ class threadsafe_queue : if (!unsafe_empty()) { value_type value; - while (try_pop(value)); + while (try_pop(value)) {} } // Remove the last dummy node - node* p = static_cast< node* >(m_pImpl->reset_last_node()); + node* p = static_cast< node* >(threadsafe_queue_impl::reset_last_node(m_pImpl)); alloc_traits::destroy(get_allocator(), p); alloc_traits::deallocate(get_allocator(), p, 1); - delete m_pImpl; + threadsafe_queue_impl::destroy(m_pImpl); } /*! * Checks if the queue is empty. Not thread-safe, the returned result may not be actual. */ - bool unsafe_empty() const { return m_pImpl->unsafe_empty(); } + bool unsafe_empty() const BOOST_NOEXCEPT { return threadsafe_queue_impl::unsafe_empty(m_pImpl); } /*! * Puts a new element to the end of the queue. Thread-safe, can be called @@ -217,7 +216,7 @@ class threadsafe_queue : void push(const_reference value) { node* p = alloc_traits::allocate(get_allocator(), 1); - if (p) + if (BOOST_LIKELY(!!p)) { try { @@ -228,7 +227,7 @@ class threadsafe_queue : alloc_traits::deallocate(get_allocator(), p, 1); throw; } - m_pImpl->push(p); + threadsafe_queue_impl::push(m_pImpl, p); } else throw std::bad_alloc(); @@ -242,7 +241,7 @@ class threadsafe_queue : bool try_pop(reference value) { threadsafe_queue_impl::node_base *dealloc, *destr; - if (m_pImpl->try_pop(dealloc, destr)) + if (threadsafe_queue_impl::try_pop(m_pImpl, dealloc, destr)) { node* p = static_cast< node* >(destr); auto_deallocate guard(static_cast< allocator_type* >(this), static_cast< node* >(dealloc), p); diff --git a/include/boost/log/detail/timestamp.hpp b/include/boost/log/detail/timestamp.hpp index 7c8bd84532..91d6e560a2 100644 --- a/include/boost/log/detail/timestamp.hpp +++ b/include/boost/log/detail/timestamp.hpp @@ -18,7 +18,7 @@ #include #include -#if defined(BOOST_WINDOWS) && !defined(__CYGWIN__) +#if defined(BOOST_WINDOWS) #include #endif #include @@ -41,9 +41,9 @@ class duration int64_t m_ticks; public: - explicit duration(int64_t ticks = 0) : m_ticks(ticks) {} + explicit duration(int64_t ticks = 0) BOOST_NOEXCEPT : m_ticks(ticks) {} -#if defined(BOOST_WINDOWS) && !defined(__CYGWIN__) +#if defined(BOOST_WINDOWS) int64_t milliseconds() const { return m_ticks; } #else BOOST_LOG_API int64_t milliseconds() const; @@ -58,7 +58,7 @@ class timestamp uint64_t m_ticks; public: - explicit timestamp(uint64_t ticks = 0) : m_ticks(ticks) {} + explicit timestamp(uint64_t ticks = 0) BOOST_NOEXCEPT : m_ticks(ticks) {} duration operator- (timestamp that) const { @@ -74,7 +74,7 @@ class timestamp * be affected by clock changes, either manual or seasonal. Also, it * should be as fast as possible. */ -#if defined(BOOST_WINDOWS) && !defined(__CYGWIN__) +#if defined(BOOST_WINDOWS) typedef uint64_t (BOOST_WINAPI_WINAPI_CC* get_tick_count_t)(); extern BOOST_LOG_API get_tick_count_t get_tick_count; diff --git a/include/boost/log/detail/value_ref_visitation.hpp b/include/boost/log/detail/value_ref_visitation.hpp index 8ac598913a..ed86ea1870 100644 --- a/include/boost/log/detail/value_ref_visitation.hpp +++ b/include/boost/log/detail/value_ref_visitation.hpp @@ -67,7 +67,7 @@ struct apply_visitor_dispatch case i: return visitor(*static_cast< typename mpl::at_c< SequenceT, i >::type const* >(p)); #define BOOST_PP_FILENAME_1 -#define BOOST_PP_ITERATION_LIMITS (1, BOOST_PP_INC(BOOST_LOG_VALUE_REF_VISITATION_VTABLE_SIZE)) +#define BOOST_PP_ITERATION_LIMITS (1, BOOST_PP_INC(BOOST_LOG_VALUE_REF_VISITATION_UNROLL_COUNT)) #include BOOST_PP_ITERATE() #undef BOOST_LOG_AUX_CASE_ENTRY diff --git a/include/boost/log/exceptions.hpp b/include/boost/log/exceptions.hpp index 32d55d40c5..f129ea8272 100644 --- a/include/boost/log/exceptions.hpp +++ b/include/boost/log/exceptions.hpp @@ -83,12 +83,12 @@ class BOOST_LOG_API bad_alloc : /*! * Destructor */ - ~bad_alloc() throw(); + ~bad_alloc() throw() BOOST_OVERRIDE; /*! * Error message accessor. */ - const char* what() const throw(); + const char* what() const throw() BOOST_OVERRIDE; #ifndef BOOST_LOG_DOXYGEN_PASS static BOOST_LOG_NORETURN void throw_(const char* file, std::size_t line, const char* descr); @@ -114,7 +114,7 @@ class BOOST_LOG_API capacity_limit_reached : /*! * Destructor */ - ~capacity_limit_reached() throw(); + ~capacity_limit_reached() throw() BOOST_OVERRIDE; #ifndef BOOST_LOG_DOXYGEN_PASS static BOOST_LOG_NORETURN void throw_(const char* file, std::size_t line, const char* descr); @@ -140,7 +140,7 @@ class BOOST_LOG_API runtime_error : /*! * Destructor */ - ~runtime_error() throw(); + ~runtime_error() throw() BOOST_OVERRIDE; }; /*! @@ -161,7 +161,7 @@ class BOOST_LOG_API missing_value : /*! * Destructor */ - ~missing_value() throw(); + ~missing_value() throw() BOOST_OVERRIDE; #ifndef BOOST_LOG_DOXYGEN_PASS static BOOST_LOG_NORETURN void throw_(const char* file, std::size_t line); @@ -190,7 +190,7 @@ class BOOST_LOG_API invalid_type : /*! * Destructor */ - ~invalid_type() throw(); + ~invalid_type() throw() BOOST_OVERRIDE; #ifndef BOOST_LOG_DOXYGEN_PASS static BOOST_LOG_NORETURN void throw_(const char* file, std::size_t line); @@ -223,7 +223,7 @@ class BOOST_LOG_API invalid_value : /*! * Destructor */ - ~invalid_value() throw(); + ~invalid_value() throw() BOOST_OVERRIDE; #ifndef BOOST_LOG_DOXYGEN_PASS static BOOST_LOG_NORETURN void throw_(const char* file, std::size_t line); @@ -250,7 +250,7 @@ class BOOST_LOG_API parse_error : /*! * Destructor */ - ~parse_error() throw(); + ~parse_error() throw() BOOST_OVERRIDE; #ifndef BOOST_LOG_DOXYGEN_PASS static BOOST_LOG_NORETURN void throw_(const char* file, std::size_t line); @@ -281,7 +281,7 @@ class BOOST_LOG_API conversion_error : /*! * Destructor */ - ~conversion_error() throw(); + ~conversion_error() throw() BOOST_OVERRIDE; #ifndef BOOST_LOG_DOXYGEN_PASS static BOOST_LOG_NORETURN void throw_(const char* file, std::size_t line); @@ -304,7 +304,7 @@ class BOOST_LOG_API system_error : /*! * Destructor */ - ~system_error() throw(); + ~system_error() throw() BOOST_OVERRIDE; #ifndef BOOST_LOG_DOXYGEN_PASS static BOOST_LOG_NORETURN void throw_(const char* file, std::size_t line, const char* descr, int system_error_code); @@ -331,7 +331,7 @@ class BOOST_LOG_API logic_error : /*! * Destructor */ - ~logic_error() throw(); + ~logic_error() throw() BOOST_OVERRIDE; #ifndef BOOST_LOG_DOXYGEN_PASS static BOOST_LOG_NORETURN void throw_(const char* file, std::size_t line, const char* descr); @@ -357,7 +357,7 @@ class BOOST_LOG_API odr_violation : /*! * Destructor */ - ~odr_violation() throw(); + ~odr_violation() throw() BOOST_OVERRIDE; #ifndef BOOST_LOG_DOXYGEN_PASS static BOOST_LOG_NORETURN void throw_(const char* file, std::size_t line); @@ -384,7 +384,7 @@ class BOOST_LOG_API unexpected_call : /*! * Destructor */ - ~unexpected_call() throw(); + ~unexpected_call() throw() BOOST_OVERRIDE; #ifndef BOOST_LOG_DOXYGEN_PASS static BOOST_LOG_NORETURN void throw_(const char* file, std::size_t line); @@ -411,7 +411,7 @@ class BOOST_LOG_API setup_error : /*! * Destructor */ - ~setup_error() throw(); + ~setup_error() throw() BOOST_OVERRIDE; #ifndef BOOST_LOG_DOXYGEN_PASS static BOOST_LOG_NORETURN void throw_(const char* file, std::size_t line); @@ -438,7 +438,7 @@ class BOOST_LOG_API limitation_error : /*! * Destructor */ - ~limitation_error() throw(); + ~limitation_error() throw() BOOST_OVERRIDE; #ifndef BOOST_LOG_DOXYGEN_PASS static BOOST_LOG_NORETURN void throw_(const char* file, std::size_t line); diff --git a/include/boost/log/expressions/formatters.hpp b/include/boost/log/expressions/formatters.hpp index f85cb7903b..e61857e1ea 100644 --- a/include/boost/log/expressions/formatters.hpp +++ b/include/boost/log/expressions/formatters.hpp @@ -31,6 +31,7 @@ #include #include +#include #ifdef BOOST_HAS_PRAGMA_ONCE #pragma once diff --git a/include/boost/log/expressions/formatters/auto_newline.hpp b/include/boost/log/expressions/formatters/auto_newline.hpp new file mode 100644 index 0000000000..5677314484 --- /dev/null +++ b/include/boost/log/expressions/formatters/auto_newline.hpp @@ -0,0 +1,46 @@ +/* + * Copyright Andrey Semashev 2019. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * https://www.boost.org/LICENSE_1_0.txt) + */ +/*! + * \file expressions/formatters/auto_newline.hpp + * \author Andrey Semashev + * \date 23.06.2019 + * + * The header contains implementation of formatter for inserting a newline, unless there is already one inserted. + */ + +#ifndef BOOST_LOG_EXPRESSIONS_FORMATTERS_AUTO_NEWLINE_HPP_INCLUDED_ +#define BOOST_LOG_EXPRESSIONS_FORMATTERS_AUTO_NEWLINE_HPP_INCLUDED_ + +#include +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace expressions { + +/*! + * Formatter for inserting a newline character, unless the last character + * inserted into the stream is already a newline. + */ +using boost::log::auto_newline; + +} // namespace expressions + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include + +#endif // BOOST_LOG_EXPRESSIONS_FORMATTERS_AUTO_NEWLINE_HPP_INCLUDED_ diff --git a/include/boost/log/expressions/formatters/c_decorator.hpp b/include/boost/log/expressions/formatters/c_decorator.hpp index ec3f485405..f786b24350 100644 --- a/include/boost/log/expressions/formatters/c_decorator.hpp +++ b/include/boost/log/expressions/formatters/c_decorator.hpp @@ -16,9 +16,9 @@ #define BOOST_LOG_EXPRESSIONS_FORMATTERS_C_DECORATOR_HPP_INCLUDED_ #include +#include #include #include -#include #include #include @@ -60,8 +60,8 @@ struct c_decorator_traits< char > template< unsigned int N > static std::size_t print_escaped(char (&buf)[N], char c) { - int n = boost::log::aux::snprintf(buf, N, "\\x%.2X", static_cast< unsigned int >(static_cast< uint8_t >(c))); - if (n < 0) + int n = boost::core::snprintf(buf, N, "\\x%.2X", static_cast< unsigned int >(static_cast< uint8_t >(c))); + if (BOOST_UNLIKELY(n < 0)) { n = 0; buf[0] = '\0'; @@ -112,8 +112,8 @@ struct c_decorator_traits< wchar_t > val = static_cast< uint32_t >(c); } - int n = boost::log::aux::swprintf(buf, N, format, val); - if (n < 0) + int n = boost::core::swprintf(buf, N, format, val); + if (BOOST_UNLIKELY(n < 0)) { n = 0; buf[0] = L'\0'; @@ -154,10 +154,10 @@ struct c_decorator_gen * For wide-character formatting there is the similar \c wc_decor decorator generator object. */ #ifdef BOOST_LOG_USE_CHAR -const aux::c_decorator_gen< char > c_decor = {}; +BOOST_INLINE_VARIABLE const aux::c_decorator_gen< char > c_decor = {}; #endif #ifdef BOOST_LOG_USE_WCHAR_T -const aux::c_decorator_gen< wchar_t > wc_decor = {}; +BOOST_INLINE_VARIABLE const aux::c_decorator_gen< wchar_t > wc_decor = {}; #endif /*! @@ -254,10 +254,10 @@ struct c_ascii_decorator_gen * For wide-character formatting there is the similar \c wc_ascii_decor decorator generator object. */ #ifdef BOOST_LOG_USE_CHAR -const aux::c_ascii_decorator_gen< char > c_ascii_decor = {}; +BOOST_INLINE_VARIABLE const aux::c_ascii_decorator_gen< char > c_ascii_decor = {}; #endif #ifdef BOOST_LOG_USE_WCHAR_T -const aux::c_ascii_decorator_gen< wchar_t > wc_ascii_decor = {}; +BOOST_INLINE_VARIABLE const aux::c_ascii_decorator_gen< wchar_t > wc_ascii_decor = {}; #endif /*! diff --git a/include/boost/log/expressions/formatters/char_decorator.hpp b/include/boost/log/expressions/formatters/char_decorator.hpp index 229e72ae89..7dca657fae 100644 --- a/include/boost/log/expressions/formatters/char_decorator.hpp +++ b/include/boost/log/expressions/formatters/char_decorator.hpp @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -561,7 +560,7 @@ class char_decorator_gen2 typedef typename boost::log::aux::deduce_char_type< typename range_value< FromRangeT >::type >::type from_char_type; typedef typename boost::log::aux::deduce_char_type< typename range_value< ToRangeT >::type >::type to_char_type; - BOOST_STATIC_ASSERT_MSG((is_same< from_char_type, to_char_type >::value), "Boost.Log: character decorator cannot be instantiated with different character types for source and replacement strings"); + static_assert(is_same< from_char_type, to_char_type >::value, "Boost.Log: character decorator cannot be instantiated with different character types for source and replacement strings"); public: char_decorator_gen2(FromRangeT const& from, ToRangeT const& to) : m_from(from), m_to(to) diff --git a/include/boost/log/expressions/formatters/csv_decorator.hpp b/include/boost/log/expressions/formatters/csv_decorator.hpp index a4c1c28399..e25a89dc39 100644 --- a/include/boost/log/expressions/formatters/csv_decorator.hpp +++ b/include/boost/log/expressions/formatters/csv_decorator.hpp @@ -114,10 +114,10 @@ struct csv_decorator_gen * For wide-character formatting there is the similar \c wcsv_decor decorator generator object. */ #ifdef BOOST_LOG_USE_CHAR -const aux::csv_decorator_gen< char > csv_decor = {}; +BOOST_INLINE_VARIABLE const aux::csv_decorator_gen< char > csv_decor = {}; #endif #ifdef BOOST_LOG_USE_WCHAR_T -const aux::csv_decorator_gen< wchar_t > wcsv_decor = {}; +BOOST_INLINE_VARIABLE const aux::csv_decorator_gen< wchar_t > wcsv_decor = {}; #endif /*! diff --git a/include/boost/log/expressions/formatters/named_scope.hpp b/include/boost/log/expressions/formatters/named_scope.hpp index ae140b1956..e4b754f568 100644 --- a/include/boost/log/expressions/formatters/named_scope.hpp +++ b/include/boost/log/expressions/formatters/named_scope.hpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -524,7 +523,7 @@ template< typename DescriptorT, template< typename > class ActorT, typename Char BOOST_FORCEINLINE format_named_scope_actor< fallback_to_none, CharT, ActorT > format_named_scope(attribute_keyword< DescriptorT, ActorT > const& keyword, const CharT* element_format) { - BOOST_STATIC_ASSERT_MSG((is_same< typename DescriptorT::value_type, attributes::named_scope::value_type >::value),\ + static_assert(is_same< typename DescriptorT::value_type, attributes::named_scope::value_type >::value, "Boost.Log: Named scope formatter only accepts attribute values of type attributes::named_scope::value_type."); typedef format_named_scope_actor< fallback_to_none, CharT, ActorT > actor_type; @@ -544,7 +543,7 @@ template< typename DescriptorT, template< typename > class ActorT, typename Char BOOST_FORCEINLINE format_named_scope_actor< fallback_to_none, CharT, ActorT > format_named_scope(attribute_keyword< DescriptorT, ActorT > const& keyword, std::basic_string< CharT > const& element_format) { - BOOST_STATIC_ASSERT_MSG((is_same< typename DescriptorT::value_type, attributes::named_scope::value_type >::value),\ + static_assert(is_same< typename DescriptorT::value_type, attributes::named_scope::value_type >::value, "Boost.Log: Named scope formatter only accepts attribute values of type attributes::named_scope::value_type."); typedef format_named_scope_actor< fallback_to_none, CharT, ActorT > actor_type; @@ -564,7 +563,7 @@ template< typename T, typename FallbackPolicyT, typename TagT, template< typenam BOOST_FORCEINLINE format_named_scope_actor< FallbackPolicyT, CharT, ActorT > format_named_scope(attribute_actor< T, FallbackPolicyT, TagT, ActorT > const& placeholder, const CharT* element_format) { - BOOST_STATIC_ASSERT_MSG((is_same< T, attributes::named_scope::value_type >::value),\ + static_assert(is_same< T, attributes::named_scope::value_type >::value, "Boost.Log: Named scope formatter only accepts attribute values of type attributes::named_scope::value_type."); typedef format_named_scope_actor< FallbackPolicyT, CharT, ActorT > actor_type; @@ -584,7 +583,7 @@ template< typename T, typename FallbackPolicyT, typename TagT, template< typenam BOOST_FORCEINLINE format_named_scope_actor< FallbackPolicyT, CharT, ActorT > format_named_scope(attribute_actor< T, FallbackPolicyT, TagT, ActorT > const& placeholder, std::basic_string< CharT > const& element_format) { - BOOST_STATIC_ASSERT_MSG((is_same< T, attributes::named_scope::value_type >::value),\ + static_assert(is_same< T, attributes::named_scope::value_type >::value, "Boost.Log: Named scope formatter only accepts attribute values of type attributes::named_scope::value_type."); typedef format_named_scope_actor< FallbackPolicyT, CharT, ActorT > actor_type; diff --git a/include/boost/log/expressions/formatters/stream.hpp b/include/boost/log/expressions/formatters/stream.hpp index 638ccbd2c8..ef1f747d16 100644 --- a/include/boost/log/expressions/formatters/stream.hpp +++ b/include/boost/log/expressions/formatters/stream.hpp @@ -37,7 +37,7 @@ typedef phoenix::expression::argument< 2 >::type stream_type; /*! * Stream placeholder in formatter template expressions. */ -const stream_type stream = {}; +BOOST_INLINE_VARIABLE const stream_type stream = {}; } // namespace expressions diff --git a/include/boost/log/expressions/formatters/wrap_formatter.hpp b/include/boost/log/expressions/formatters/wrap_formatter.hpp index 29c19c2f80..69be0bae08 100644 --- a/include/boost/log/expressions/formatters/wrap_formatter.hpp +++ b/include/boost/log/expressions/formatters/wrap_formatter.hpp @@ -274,7 +274,7 @@ class wrapped_formatter_actor : * function object must be compatible with the following signature: * *
- * void (record_view const&, basic_formatting_ostream< CharT >&)
+ * void (record_view const&, basic_formatting_ostream< CharT >&) const
  * 
* * where \c CharT is the character type of the formatting expression. @@ -293,7 +293,7 @@ BOOST_FORCEINLINE wrapped_formatter_actor< FunT, typename aux::default_char_type * function object must be compatible with the following signature: * *
- * void (record_view const&, basic_formatting_ostream< CharT >&)
+ * void (record_view const&, basic_formatting_ostream< CharT >&) const
  * 
* * where \c CharT is the character type of the formatting expression. diff --git a/include/boost/log/expressions/formatters/xml_decorator.hpp b/include/boost/log/expressions/formatters/xml_decorator.hpp index ca93a7d58a..02b0fc1d12 100644 --- a/include/boost/log/expressions/formatters/xml_decorator.hpp +++ b/include/boost/log/expressions/formatters/xml_decorator.hpp @@ -112,10 +112,10 @@ struct xml_decorator_gen * For wide-character formatting there is the similar \c wxml_decor decorator generator object. */ #ifdef BOOST_LOG_USE_CHAR -const aux::xml_decorator_gen< char > xml_decor = {}; +BOOST_INLINE_VARIABLE const aux::xml_decorator_gen< char > xml_decor = {}; #endif #ifdef BOOST_LOG_USE_WCHAR_T -const aux::xml_decorator_gen< wchar_t > wxml_decor = {}; +BOOST_INLINE_VARIABLE const aux::xml_decorator_gen< wchar_t > wxml_decor = {}; #endif /*! diff --git a/include/boost/log/expressions/keyword.hpp b/include/boost/log/expressions/keyword.hpp index 47f0121539..7a8223ec76 100644 --- a/include/boost/log/expressions/keyword.hpp +++ b/include/boost/log/expressions/keyword.hpp @@ -200,7 +200,7 @@ struct protoify< boost::reference_wrapper< const boost::log::expressions::attrib #define BOOST_LOG_ATTRIBUTE_KEYWORD_IMPL(keyword_, name_, value_type_, tag_ns_)\ BOOST_LOG_ATTRIBUTE_KEYWORD_TYPE_IMPL(keyword_, name_, value_type_, tag_ns_)\ - const BOOST_PP_CAT(keyword_, _type) keyword_ = {}; + BOOST_INLINE_VARIABLE const BOOST_PP_CAT(keyword_, _type) keyword_ = {}; #endif // BOOST_LOG_DOXYGEN_PASS diff --git a/include/boost/log/expressions/message.hpp b/include/boost/log/expressions/message.hpp index e68aa37949..394ad793c6 100644 --- a/include/boost/log/expressions/message.hpp +++ b/include/boost/log/expressions/message.hpp @@ -95,7 +95,7 @@ typedef attribute_keyword< tag::message > message_type; /*! * Generic message keyword. */ -const message_type message = {}; +BOOST_INLINE_VARIABLE const message_type message = {}; #if defined(BOOST_LOG_USE_CHAR) /*! @@ -105,7 +105,7 @@ typedef attribute_keyword< tag::smessage > smessage_type; /*! * Narrow message keyword. */ -const smessage_type smessage = {}; +BOOST_INLINE_VARIABLE const smessage_type smessage = {}; #endif #if defined(BOOST_LOG_USE_WCHAR_T) @@ -116,7 +116,7 @@ typedef attribute_keyword< tag::wmessage > wmessage_type; /*! * Wide message keyword. */ -const wmessage_type wmessage = {}; +BOOST_INLINE_VARIABLE const wmessage_type wmessage = {}; #endif } // namespace expressions diff --git a/include/boost/log/expressions/predicates.hpp b/include/boost/log/expressions/predicates.hpp index f218a27629..8901d34fd4 100644 --- a/include/boost/log/expressions/predicates.hpp +++ b/include/boost/log/expressions/predicates.hpp @@ -24,6 +24,8 @@ #include #include +#include + #include #include diff --git a/include/boost/log/expressions/predicates/channel_severity_filter.hpp b/include/boost/log/expressions/predicates/channel_severity_filter.hpp index e8562e2a81..aca807a617 100644 --- a/include/boost/log/expressions/predicates/channel_severity_filter.hpp +++ b/include/boost/log/expressions/predicates/channel_severity_filter.hpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -53,7 +54,7 @@ template< typename SeverityFallbackT = fallback_to_none, typename ChannelOrderT = less, typename SeverityCompareT = greater_equal, - typename AllocatorT = std::allocator< void > + typename AllocatorT = use_std_allocator > class channel_severity_filter_terminal { @@ -227,7 +228,7 @@ template< typename SeverityFallbackT = fallback_to_none, typename ChannelOrderT = less, typename SeverityCompareT = greater_equal, - typename AllocatorT = std::allocator< void >, + typename AllocatorT = use_std_allocator, template< typename > class ActorT = phoenix::actor > class channel_severity_filter_actor : @@ -318,10 +319,10 @@ channel_severity_filter(attribute_name const& channel_name, attribute_name const //! \overload template< typename SeverityT, typename ChannelDescriptorT, template< typename > class ActorT > -BOOST_FORCEINLINE channel_severity_filter_actor< typename ChannelDescriptorT::value_type, SeverityT, fallback_to_none, fallback_to_none, less, greater_equal, std::allocator< void >, ActorT > +BOOST_FORCEINLINE channel_severity_filter_actor< typename ChannelDescriptorT::value_type, SeverityT, fallback_to_none, fallback_to_none, less, greater_equal, use_std_allocator, ActorT > channel_severity_filter(attribute_keyword< ChannelDescriptorT, ActorT > const& channel_keyword, attribute_name const& severity_name) { - typedef channel_severity_filter_actor< typename ChannelDescriptorT::value_type, SeverityT, fallback_to_none, fallback_to_none, less, greater_equal, std::allocator< void >, ActorT > result_type; + typedef channel_severity_filter_actor< typename ChannelDescriptorT::value_type, SeverityT, fallback_to_none, fallback_to_none, less, greater_equal, use_std_allocator, ActorT > result_type; typedef typename result_type::terminal_type terminal_type; typename result_type::base_type act = {{ terminal_type(channel_keyword.get_name(), severity_name) }}; return result_type(act); @@ -329,10 +330,10 @@ channel_severity_filter(attribute_keyword< ChannelDescriptorT, ActorT > const& c //! \overload template< typename ChannelT, typename SeverityDescriptorT, template< typename > class ActorT > -BOOST_FORCEINLINE channel_severity_filter_actor< ChannelT, typename SeverityDescriptorT::value_type, fallback_to_none, fallback_to_none, less, greater_equal, std::allocator< void >, ActorT > +BOOST_FORCEINLINE channel_severity_filter_actor< ChannelT, typename SeverityDescriptorT::value_type, fallback_to_none, fallback_to_none, less, greater_equal, use_std_allocator, ActorT > channel_severity_filter(attribute_name const& channel_name, attribute_keyword< SeverityDescriptorT, ActorT > const& severity_keyword) { - typedef channel_severity_filter_actor< ChannelT, typename SeverityDescriptorT::value_type, fallback_to_none, fallback_to_none, less, greater_equal, std::allocator< void >, ActorT > result_type; + typedef channel_severity_filter_actor< ChannelT, typename SeverityDescriptorT::value_type, fallback_to_none, fallback_to_none, less, greater_equal, use_std_allocator, ActorT > result_type; typedef typename result_type::terminal_type terminal_type; typename result_type::base_type act = {{ terminal_type(channel_name, severity_keyword.get_name()) }}; return result_type(act); @@ -340,10 +341,10 @@ channel_severity_filter(attribute_name const& channel_name, attribute_keyword< S //! \overload template< typename ChannelDescriptorT, typename SeverityDescriptorT, template< typename > class ActorT > -BOOST_FORCEINLINE channel_severity_filter_actor< typename ChannelDescriptorT::value_type, typename SeverityDescriptorT::value_type, fallback_to_none, fallback_to_none, less, greater_equal, std::allocator< void >, ActorT > +BOOST_FORCEINLINE channel_severity_filter_actor< typename ChannelDescriptorT::value_type, typename SeverityDescriptorT::value_type, fallback_to_none, fallback_to_none, less, greater_equal, use_std_allocator, ActorT > channel_severity_filter(attribute_keyword< ChannelDescriptorT, ActorT > const& channel_keyword, attribute_keyword< SeverityDescriptorT, ActorT > const& severity_keyword) { - typedef channel_severity_filter_actor< typename ChannelDescriptorT::value_type, typename SeverityDescriptorT::value_type, fallback_to_none, fallback_to_none, less, greater_equal, std::allocator< void >, ActorT > result_type; + typedef channel_severity_filter_actor< typename ChannelDescriptorT::value_type, typename SeverityDescriptorT::value_type, fallback_to_none, fallback_to_none, less, greater_equal, use_std_allocator, ActorT > result_type; typedef typename result_type::terminal_type terminal_type; typename result_type::base_type act = {{ terminal_type(channel_keyword.get_name(), severity_keyword.get_name()) }}; return result_type(act); @@ -351,10 +352,10 @@ channel_severity_filter(attribute_keyword< ChannelDescriptorT, ActorT > const& c //! \overload template< typename SeverityT, typename ChannelT, typename ChannelFallbackT, typename ChannelTagT, template< typename > class ActorT > -BOOST_FORCEINLINE channel_severity_filter_actor< ChannelT, SeverityT, ChannelFallbackT, fallback_to_none, less, greater_equal, std::allocator< void >, ActorT > +BOOST_FORCEINLINE channel_severity_filter_actor< ChannelT, SeverityT, ChannelFallbackT, fallback_to_none, less, greater_equal, use_std_allocator, ActorT > channel_severity_filter(attribute_actor< ChannelT, ChannelFallbackT, ChannelTagT, ActorT > const& channel_placeholder, attribute_name const& severity_name) { - typedef channel_severity_filter_actor< ChannelT, SeverityT, ChannelFallbackT, fallback_to_none, less, greater_equal, std::allocator< void >, ActorT > result_type; + typedef channel_severity_filter_actor< ChannelT, SeverityT, ChannelFallbackT, fallback_to_none, less, greater_equal, use_std_allocator, ActorT > result_type; typedef typename result_type::terminal_type terminal_type; typename result_type::base_type act = {{ terminal_type(channel_placeholder.get_name(), severity_name, channel_placeholder.get_fallback_policy()) }}; return result_type(act); @@ -362,10 +363,10 @@ channel_severity_filter(attribute_actor< ChannelT, ChannelFallbackT, ChannelTagT //! \overload template< typename ChannelT, typename SeverityT, typename SeverityFallbackT, typename SeverityTagT, template< typename > class ActorT > -BOOST_FORCEINLINE channel_severity_filter_actor< ChannelT, SeverityT, fallback_to_none, SeverityFallbackT, less, greater_equal, std::allocator< void >, ActorT > +BOOST_FORCEINLINE channel_severity_filter_actor< ChannelT, SeverityT, fallback_to_none, SeverityFallbackT, less, greater_equal, use_std_allocator, ActorT > channel_severity_filter(attribute_name const& channel_name, attribute_actor< SeverityT, SeverityFallbackT, SeverityTagT, ActorT > const& severity_placeholder) { - typedef channel_severity_filter_actor< ChannelT, SeverityT, fallback_to_none, SeverityFallbackT, less, greater_equal, std::allocator< void >, ActorT > result_type; + typedef channel_severity_filter_actor< ChannelT, SeverityT, fallback_to_none, SeverityFallbackT, less, greater_equal, use_std_allocator, ActorT > result_type; typedef typename result_type::terminal_type terminal_type; typename result_type::base_type act = {{ terminal_type(channel_name, severity_placeholder.get_name(), fallback_to_none(), severity_placeholder.get_fallback_policy()) }}; return result_type(act); @@ -373,10 +374,10 @@ channel_severity_filter(attribute_name const& channel_name, attribute_actor< Sev //! \overload template< typename ChannelT, typename ChannelFallbackT, typename ChannelTagT, typename SeverityT, typename SeverityFallbackT, typename SeverityTagT, template< typename > class ActorT > -BOOST_FORCEINLINE channel_severity_filter_actor< ChannelT, SeverityT, ChannelFallbackT, SeverityFallbackT, less, greater_equal, std::allocator< void >, ActorT > +BOOST_FORCEINLINE channel_severity_filter_actor< ChannelT, SeverityT, ChannelFallbackT, SeverityFallbackT, less, greater_equal, use_std_allocator, ActorT > channel_severity_filter(attribute_actor< ChannelT, ChannelFallbackT, ChannelTagT, ActorT > const& channel_placeholder, attribute_actor< SeverityT, SeverityFallbackT, SeverityTagT, ActorT > const& severity_placeholder) { - typedef channel_severity_filter_actor< ChannelT, SeverityT, ChannelFallbackT, SeverityFallbackT, less, greater_equal, std::allocator< void >, ActorT > result_type; + typedef channel_severity_filter_actor< ChannelT, SeverityT, ChannelFallbackT, SeverityFallbackT, less, greater_equal, use_std_allocator, ActorT > result_type; typedef typename result_type::terminal_type terminal_type; typename result_type::base_type act = {{ terminal_type(channel_placeholder.get_name(), severity_placeholder.get_name(), channel_placeholder.get_fallback_policy(), severity_placeholder.get_fallback_policy()) }}; return result_type(act); @@ -396,10 +397,10 @@ channel_severity_filter(attribute_name const& channel_name, attribute_name const //! \overload template< typename SeverityT, typename ChannelDescriptorT, template< typename > class ActorT, typename SeverityCompareT > -BOOST_FORCEINLINE channel_severity_filter_actor< typename ChannelDescriptorT::value_type, SeverityT, fallback_to_none, fallback_to_none, less, SeverityCompareT, std::allocator< void >, ActorT > +BOOST_FORCEINLINE channel_severity_filter_actor< typename ChannelDescriptorT::value_type, SeverityT, fallback_to_none, fallback_to_none, less, SeverityCompareT, use_std_allocator, ActorT > channel_severity_filter(attribute_keyword< ChannelDescriptorT, ActorT > const& channel_keyword, attribute_name const& severity_name, SeverityCompareT const& severity_compare) { - typedef channel_severity_filter_actor< typename ChannelDescriptorT::value_type, SeverityT, fallback_to_none, fallback_to_none, less, SeverityCompareT, std::allocator< void >, ActorT > result_type; + typedef channel_severity_filter_actor< typename ChannelDescriptorT::value_type, SeverityT, fallback_to_none, fallback_to_none, less, SeverityCompareT, use_std_allocator, ActorT > result_type; typedef typename result_type::terminal_type terminal_type; typename result_type::base_type act = {{ terminal_type(channel_keyword.get_name(), severity_name, fallback_to_none(), fallback_to_none(), less(), severity_compare) }}; return result_type(act); @@ -407,10 +408,10 @@ channel_severity_filter(attribute_keyword< ChannelDescriptorT, ActorT > const& c //! \overload template< typename ChannelT, typename SeverityDescriptorT, template< typename > class ActorT, typename SeverityCompareT > -BOOST_FORCEINLINE channel_severity_filter_actor< ChannelT, typename SeverityDescriptorT::value_type, fallback_to_none, fallback_to_none, less, SeverityCompareT, std::allocator< void >, ActorT > +BOOST_FORCEINLINE channel_severity_filter_actor< ChannelT, typename SeverityDescriptorT::value_type, fallback_to_none, fallback_to_none, less, SeverityCompareT, use_std_allocator, ActorT > channel_severity_filter(attribute_name const& channel_name, attribute_keyword< SeverityDescriptorT, ActorT > const& severity_keyword, SeverityCompareT const& severity_compare) { - typedef channel_severity_filter_actor< ChannelT, typename SeverityDescriptorT::value_type, fallback_to_none, fallback_to_none, less, SeverityCompareT, std::allocator< void >, ActorT > result_type; + typedef channel_severity_filter_actor< ChannelT, typename SeverityDescriptorT::value_type, fallback_to_none, fallback_to_none, less, SeverityCompareT, use_std_allocator, ActorT > result_type; typedef typename result_type::terminal_type terminal_type; typename result_type::base_type act = {{ terminal_type(channel_name, severity_keyword.get_name(), fallback_to_none(), fallback_to_none(), less(), severity_compare) }}; return result_type(act); @@ -418,10 +419,10 @@ channel_severity_filter(attribute_name const& channel_name, attribute_keyword< S //! \overload template< typename ChannelDescriptorT, typename SeverityDescriptorT, template< typename > class ActorT, typename SeverityCompareT > -BOOST_FORCEINLINE channel_severity_filter_actor< typename ChannelDescriptorT::value_type, typename SeverityDescriptorT::value_type, fallback_to_none, fallback_to_none, less, SeverityCompareT, std::allocator< void >, ActorT > +BOOST_FORCEINLINE channel_severity_filter_actor< typename ChannelDescriptorT::value_type, typename SeverityDescriptorT::value_type, fallback_to_none, fallback_to_none, less, SeverityCompareT, use_std_allocator, ActorT > channel_severity_filter(attribute_keyword< ChannelDescriptorT, ActorT > const& channel_keyword, attribute_keyword< SeverityDescriptorT, ActorT > const& severity_keyword, SeverityCompareT const& severity_compare) { - typedef channel_severity_filter_actor< typename ChannelDescriptorT::value_type, typename SeverityDescriptorT::value_type, fallback_to_none, fallback_to_none, less, SeverityCompareT, std::allocator< void >, ActorT > result_type; + typedef channel_severity_filter_actor< typename ChannelDescriptorT::value_type, typename SeverityDescriptorT::value_type, fallback_to_none, fallback_to_none, less, SeverityCompareT, use_std_allocator, ActorT > result_type; typedef typename result_type::terminal_type terminal_type; typename result_type::base_type act = {{ terminal_type(channel_keyword.get_name(), severity_keyword.get_name(), fallback_to_none(), fallback_to_none(), less(), severity_compare) }}; return result_type(act); @@ -429,10 +430,10 @@ channel_severity_filter(attribute_keyword< ChannelDescriptorT, ActorT > const& c //! \overload template< typename SeverityT, typename ChannelT, typename ChannelFallbackT, typename ChannelTagT, template< typename > class ActorT, typename SeverityCompareT > -BOOST_FORCEINLINE channel_severity_filter_actor< ChannelT, SeverityT, ChannelFallbackT, fallback_to_none, less, SeverityCompareT, std::allocator< void >, ActorT > +BOOST_FORCEINLINE channel_severity_filter_actor< ChannelT, SeverityT, ChannelFallbackT, fallback_to_none, less, SeverityCompareT, use_std_allocator, ActorT > channel_severity_filter(attribute_actor< ChannelT, ChannelFallbackT, ChannelTagT, ActorT > const& channel_placeholder, attribute_name const& severity_name, SeverityCompareT const& severity_compare) { - typedef channel_severity_filter_actor< ChannelT, SeverityT, ChannelFallbackT, fallback_to_none, less, SeverityCompareT, std::allocator< void >, ActorT > result_type; + typedef channel_severity_filter_actor< ChannelT, SeverityT, ChannelFallbackT, fallback_to_none, less, SeverityCompareT, use_std_allocator, ActorT > result_type; typedef typename result_type::terminal_type terminal_type; typename result_type::base_type act = {{ terminal_type(channel_placeholder.get_name(), severity_name, channel_placeholder.get_fallback_policy(), fallback_to_none(), less(), severity_compare) }}; return result_type(act); @@ -440,10 +441,10 @@ channel_severity_filter(attribute_actor< ChannelT, ChannelFallbackT, ChannelTagT //! \overload template< typename ChannelT, typename SeverityT, typename SeverityFallbackT, typename SeverityTagT, template< typename > class ActorT, typename SeverityCompareT > -BOOST_FORCEINLINE channel_severity_filter_actor< ChannelT, SeverityT, fallback_to_none, SeverityFallbackT, less, SeverityCompareT, std::allocator< void >, ActorT > +BOOST_FORCEINLINE channel_severity_filter_actor< ChannelT, SeverityT, fallback_to_none, SeverityFallbackT, less, SeverityCompareT, use_std_allocator, ActorT > channel_severity_filter(attribute_name const& channel_name, attribute_actor< SeverityT, SeverityFallbackT, SeverityTagT, ActorT > const& severity_placeholder, SeverityCompareT const& severity_compare) { - typedef channel_severity_filter_actor< ChannelT, SeverityT, fallback_to_none, SeverityFallbackT, less, SeverityCompareT, std::allocator< void >, ActorT > result_type; + typedef channel_severity_filter_actor< ChannelT, SeverityT, fallback_to_none, SeverityFallbackT, less, SeverityCompareT, use_std_allocator, ActorT > result_type; typedef typename result_type::terminal_type terminal_type; typename result_type::base_type act = {{ terminal_type(channel_name, severity_placeholder.get_name(), fallback_to_none(), severity_placeholder.get_fallback_policy(), less(), severity_compare) }}; return result_type(act); @@ -451,10 +452,10 @@ channel_severity_filter(attribute_name const& channel_name, attribute_actor< Sev //! \overload template< typename ChannelT, typename ChannelFallbackT, typename ChannelTagT, typename SeverityT, typename SeverityFallbackT, typename SeverityTagT, template< typename > class ActorT, typename SeverityCompareT > -BOOST_FORCEINLINE channel_severity_filter_actor< ChannelT, SeverityT, ChannelFallbackT, SeverityFallbackT, less, SeverityCompareT, std::allocator< void >, ActorT > +BOOST_FORCEINLINE channel_severity_filter_actor< ChannelT, SeverityT, ChannelFallbackT, SeverityFallbackT, less, SeverityCompareT, use_std_allocator, ActorT > channel_severity_filter(attribute_actor< ChannelT, ChannelFallbackT, ChannelTagT, ActorT > const& channel_placeholder, attribute_actor< SeverityT, SeverityFallbackT, SeverityTagT, ActorT > const& severity_placeholder, SeverityCompareT const& severity_compare) { - typedef channel_severity_filter_actor< ChannelT, SeverityT, ChannelFallbackT, SeverityFallbackT, less, SeverityCompareT, std::allocator< void >, ActorT > result_type; + typedef channel_severity_filter_actor< ChannelT, SeverityT, ChannelFallbackT, SeverityFallbackT, less, SeverityCompareT, use_std_allocator, ActorT > result_type; typedef typename result_type::terminal_type terminal_type; typename result_type::base_type act = {{ terminal_type(channel_placeholder.get_name(), severity_placeholder.get_name(), channel_placeholder.get_fallback_policy(), severity_placeholder.get_fallback_policy(), less(), severity_compare) }}; return result_type(act); @@ -474,10 +475,10 @@ channel_severity_filter(attribute_name const& channel_name, attribute_name const //! \overload template< typename SeverityT, typename ChannelDescriptorT, template< typename > class ActorT, typename SeverityCompareT, typename ChannelOrderT > -BOOST_FORCEINLINE channel_severity_filter_actor< typename ChannelDescriptorT::value_type, SeverityT, fallback_to_none, fallback_to_none, ChannelOrderT, SeverityCompareT, std::allocator< void >, ActorT > +BOOST_FORCEINLINE channel_severity_filter_actor< typename ChannelDescriptorT::value_type, SeverityT, fallback_to_none, fallback_to_none, ChannelOrderT, SeverityCompareT, use_std_allocator, ActorT > channel_severity_filter(attribute_keyword< ChannelDescriptorT, ActorT > const& channel_keyword, attribute_name const& severity_name, SeverityCompareT const& severity_compare, ChannelOrderT const& channel_order) { - typedef channel_severity_filter_actor< typename ChannelDescriptorT::value_type, SeverityT, fallback_to_none, fallback_to_none, ChannelOrderT, SeverityCompareT, std::allocator< void >, ActorT > result_type; + typedef channel_severity_filter_actor< typename ChannelDescriptorT::value_type, SeverityT, fallback_to_none, fallback_to_none, ChannelOrderT, SeverityCompareT, use_std_allocator, ActorT > result_type; typedef typename result_type::terminal_type terminal_type; typename result_type::base_type act = {{ terminal_type(channel_keyword.get_name(), severity_name, fallback_to_none(), fallback_to_none(), channel_order, severity_compare) }}; return result_type(act); @@ -485,10 +486,10 @@ channel_severity_filter(attribute_keyword< ChannelDescriptorT, ActorT > const& c //! \overload template< typename ChannelT, typename SeverityDescriptorT, template< typename > class ActorT, typename SeverityCompareT, typename ChannelOrderT > -BOOST_FORCEINLINE channel_severity_filter_actor< ChannelT, typename SeverityDescriptorT::value_type, fallback_to_none, fallback_to_none, ChannelOrderT, SeverityCompareT, std::allocator< void >, ActorT > +BOOST_FORCEINLINE channel_severity_filter_actor< ChannelT, typename SeverityDescriptorT::value_type, fallback_to_none, fallback_to_none, ChannelOrderT, SeverityCompareT, use_std_allocator, ActorT > channel_severity_filter(attribute_name const& channel_name, attribute_keyword< SeverityDescriptorT, ActorT > const& severity_keyword, SeverityCompareT const& severity_compare, ChannelOrderT const& channel_order) { - typedef channel_severity_filter_actor< ChannelT, typename SeverityDescriptorT::value_type, fallback_to_none, fallback_to_none, ChannelOrderT, SeverityCompareT, std::allocator< void >, ActorT > result_type; + typedef channel_severity_filter_actor< ChannelT, typename SeverityDescriptorT::value_type, fallback_to_none, fallback_to_none, ChannelOrderT, SeverityCompareT, use_std_allocator, ActorT > result_type; typedef typename result_type::terminal_type terminal_type; typename result_type::base_type act = {{ terminal_type(channel_name, severity_keyword.get_name(), fallback_to_none(), fallback_to_none(), channel_order, severity_compare) }}; return result_type(act); @@ -496,10 +497,10 @@ channel_severity_filter(attribute_name const& channel_name, attribute_keyword< S //! \overload template< typename ChannelDescriptorT, typename SeverityDescriptorT, template< typename > class ActorT, typename SeverityCompareT, typename ChannelOrderT > -BOOST_FORCEINLINE channel_severity_filter_actor< typename ChannelDescriptorT::value_type, typename SeverityDescriptorT::value_type, fallback_to_none, fallback_to_none, ChannelOrderT, SeverityCompareT, std::allocator< void >, ActorT > +BOOST_FORCEINLINE channel_severity_filter_actor< typename ChannelDescriptorT::value_type, typename SeverityDescriptorT::value_type, fallback_to_none, fallback_to_none, ChannelOrderT, SeverityCompareT, use_std_allocator, ActorT > channel_severity_filter(attribute_keyword< ChannelDescriptorT, ActorT > const& channel_keyword, attribute_keyword< SeverityDescriptorT, ActorT > const& severity_keyword, SeverityCompareT const& severity_compare, ChannelOrderT const& channel_order) { - typedef channel_severity_filter_actor< typename ChannelDescriptorT::value_type, typename SeverityDescriptorT::value_type, fallback_to_none, fallback_to_none, ChannelOrderT, SeverityCompareT, std::allocator< void >, ActorT > result_type; + typedef channel_severity_filter_actor< typename ChannelDescriptorT::value_type, typename SeverityDescriptorT::value_type, fallback_to_none, fallback_to_none, ChannelOrderT, SeverityCompareT, use_std_allocator, ActorT > result_type; typedef typename result_type::terminal_type terminal_type; typename result_type::base_type act = {{ terminal_type(channel_keyword.get_name(), severity_keyword.get_name(), fallback_to_none(), fallback_to_none(), channel_order, severity_compare) }}; return result_type(act); @@ -507,10 +508,10 @@ channel_severity_filter(attribute_keyword< ChannelDescriptorT, ActorT > const& c //! \overload template< typename SeverityT, typename ChannelT, typename ChannelFallbackT, typename ChannelTagT, template< typename > class ActorT, typename SeverityCompareT, typename ChannelOrderT > -BOOST_FORCEINLINE channel_severity_filter_actor< ChannelT, SeverityT, ChannelFallbackT, fallback_to_none, ChannelOrderT, SeverityCompareT, std::allocator< void >, ActorT > +BOOST_FORCEINLINE channel_severity_filter_actor< ChannelT, SeverityT, ChannelFallbackT, fallback_to_none, ChannelOrderT, SeverityCompareT, use_std_allocator, ActorT > channel_severity_filter(attribute_actor< ChannelT, ChannelFallbackT, ChannelTagT, ActorT > const& channel_placeholder, attribute_name const& severity_name, SeverityCompareT const& severity_compare, ChannelOrderT const& channel_order) { - typedef channel_severity_filter_actor< ChannelT, SeverityT, ChannelFallbackT, fallback_to_none, ChannelOrderT, SeverityCompareT, std::allocator< void >, ActorT > result_type; + typedef channel_severity_filter_actor< ChannelT, SeverityT, ChannelFallbackT, fallback_to_none, ChannelOrderT, SeverityCompareT, use_std_allocator, ActorT > result_type; typedef typename result_type::terminal_type terminal_type; typename result_type::base_type act = {{ terminal_type(channel_placeholder.get_name(), severity_name, channel_placeholder.get_fallback_policy(), fallback_to_none(), channel_order, severity_compare) }}; return result_type(act); @@ -518,10 +519,10 @@ channel_severity_filter(attribute_actor< ChannelT, ChannelFallbackT, ChannelTagT //! \overload template< typename ChannelT, typename SeverityT, typename SeverityFallbackT, typename SeverityTagT, template< typename > class ActorT, typename SeverityCompareT, typename ChannelOrderT > -BOOST_FORCEINLINE channel_severity_filter_actor< ChannelT, SeverityT, fallback_to_none, SeverityFallbackT, ChannelOrderT, SeverityCompareT, std::allocator< void >, ActorT > +BOOST_FORCEINLINE channel_severity_filter_actor< ChannelT, SeverityT, fallback_to_none, SeverityFallbackT, ChannelOrderT, SeverityCompareT, use_std_allocator, ActorT > channel_severity_filter(attribute_name const& channel_name, attribute_actor< SeverityT, SeverityFallbackT, SeverityTagT, ActorT > const& severity_placeholder, SeverityCompareT const& severity_compare, ChannelOrderT const& channel_order) { - typedef channel_severity_filter_actor< ChannelT, SeverityT, fallback_to_none, SeverityFallbackT, ChannelOrderT, SeverityCompareT, std::allocator< void >, ActorT > result_type; + typedef channel_severity_filter_actor< ChannelT, SeverityT, fallback_to_none, SeverityFallbackT, ChannelOrderT, SeverityCompareT, use_std_allocator, ActorT > result_type; typedef typename result_type::terminal_type terminal_type; typename result_type::base_type act = {{ terminal_type(channel_name, severity_placeholder.get_name(), fallback_to_none(), severity_placeholder.get_fallback_policy(), channel_order, severity_compare) }}; return result_type(act); @@ -529,10 +530,10 @@ channel_severity_filter(attribute_name const& channel_name, attribute_actor< Sev //! \overload template< typename ChannelT, typename ChannelFallbackT, typename ChannelTagT, typename SeverityT, typename SeverityFallbackT, typename SeverityTagT, template< typename > class ActorT, typename SeverityCompareT, typename ChannelOrderT > -BOOST_FORCEINLINE channel_severity_filter_actor< ChannelT, SeverityT, ChannelFallbackT, SeverityFallbackT, ChannelOrderT, SeverityCompareT, std::allocator< void >, ActorT > +BOOST_FORCEINLINE channel_severity_filter_actor< ChannelT, SeverityT, ChannelFallbackT, SeverityFallbackT, ChannelOrderT, SeverityCompareT, use_std_allocator, ActorT > channel_severity_filter(attribute_actor< ChannelT, ChannelFallbackT, ChannelTagT, ActorT > const& channel_placeholder, attribute_actor< SeverityT, SeverityFallbackT, SeverityTagT, ActorT > const& severity_placeholder, SeverityCompareT const& severity_compare, ChannelOrderT const& channel_order) { - typedef channel_severity_filter_actor< ChannelT, SeverityT, ChannelFallbackT, SeverityFallbackT, ChannelOrderT, SeverityCompareT, std::allocator< void >, ActorT > result_type; + typedef channel_severity_filter_actor< ChannelT, SeverityT, ChannelFallbackT, SeverityFallbackT, ChannelOrderT, SeverityCompareT, use_std_allocator, ActorT > result_type; typedef typename result_type::terminal_type terminal_type; typename result_type::base_type act = {{ terminal_type(channel_placeholder.get_name(), severity_placeholder.get_name(), channel_placeholder.get_fallback_policy(), severity_placeholder.get_fallback_policy(), channel_order, severity_compare) }}; return result_type(act); diff --git a/include/boost/log/expressions/predicates/is_debugger_present.hpp b/include/boost/log/expressions/predicates/is_debugger_present.hpp index 9d4960714d..2614bd71ad 100644 --- a/include/boost/log/expressions/predicates/is_debugger_present.hpp +++ b/include/boost/log/expressions/predicates/is_debugger_present.hpp @@ -1,5 +1,5 @@ /* - * Copyright Andrey Semashev 2007 - 2015. + * Copyright Andrey Semashev 2007 - 2021. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) @@ -27,40 +27,6 @@ #include #include -#if defined(BOOST_USE_WINDOWS_H) - -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0500 -#endif - -#include - -#else // defined(BOOST_USE_WINDOWS_H) - -namespace boost { - -BOOST_LOG_OPEN_NAMESPACE - -namespace expressions { - -namespace aux { - -extern "C" { - -__declspec(dllimport) int __stdcall IsDebuggerPresent(); - -} // extern "C" - -} // namespace aux - -} // namespace expressions - -BOOST_LOG_CLOSE_NAMESPACE // namespace log - -} // namespace boost - -#endif // BOOST_USE_WINDOWS_H - namespace boost { BOOST_LOG_OPEN_NAMESPACE @@ -73,10 +39,7 @@ struct is_debugger_present { typedef bool result_type; - result_type operator() () const - { - return IsDebuggerPresent() != 0; - } + BOOST_LOG_API result_type operator() () const; }; } // namespace aux diff --git a/include/boost/log/expressions/predicates/matches.hpp b/include/boost/log/expressions/predicates/matches.hpp index d9291396b3..1fe2d63595 100644 --- a/include/boost/log/expressions/predicates/matches.hpp +++ b/include/boost/log/expressions/predicates/matches.hpp @@ -69,12 +69,20 @@ class attribute_matches : } }; +#if defined(BOOST_MSVC) && BOOST_MSVC == 1925 +// MSVC 14.2 has a codegen bug that makes inlined `matches` functions below crash on copy constructing the phoenix::actor on return. +// https://developercommunity.visualstudio.com/content/problem/982738/bad-code-generated-in-boostlogboostregex-test-case.html +#define BOOST_LOG_AUX_FORCEINLINE_MSVC_BUG982738 inline BOOST_NOINLINE +#else +#define BOOST_LOG_AUX_FORCEINLINE_MSVC_BUG982738 BOOST_FORCEINLINE +#endif + /*! * The function generates a terminal node in a template expression. The node will check if the attribute value, * which is assumed to be a string, matches the specified regular expression. */ template< typename T, typename FallbackPolicyT, typename TagT, template< typename > class ActorT, typename RegexT > -BOOST_FORCEINLINE ActorT< aux::unary_function_terminal< attribute_matches< T, RegexT, FallbackPolicyT > > > +BOOST_LOG_AUX_FORCEINLINE_MSVC_BUG982738 ActorT< aux::unary_function_terminal< attribute_matches< T, RegexT, FallbackPolicyT > > > matches(attribute_actor< T, FallbackPolicyT, TagT, ActorT > const& attr, RegexT const& rex) { typedef aux::unary_function_terminal< attribute_matches< T, RegexT, FallbackPolicyT > > terminal_type; @@ -87,7 +95,7 @@ matches(attribute_actor< T, FallbackPolicyT, TagT, ActorT > const& attr, RegexT * which is assumed to be a string, matches the specified regular expression. */ template< typename DescriptorT, template< typename > class ActorT, typename RegexT > -BOOST_FORCEINLINE ActorT< aux::unary_function_terminal< attribute_matches< typename DescriptorT::value_type, RegexT > > > +BOOST_LOG_AUX_FORCEINLINE_MSVC_BUG982738 ActorT< aux::unary_function_terminal< attribute_matches< typename DescriptorT::value_type, RegexT > > > matches(attribute_keyword< DescriptorT, ActorT > const&, RegexT const& rex) { typedef aux::unary_function_terminal< attribute_matches< typename DescriptorT::value_type, RegexT > > terminal_type; @@ -100,7 +108,7 @@ matches(attribute_keyword< DescriptorT, ActorT > const&, RegexT const& rex) * which is assumed to be a string, matches the specified regular expression. */ template< typename T, typename RegexT > -BOOST_FORCEINLINE phoenix::actor< aux::unary_function_terminal< attribute_matches< T, RegexT > > > +BOOST_LOG_AUX_FORCEINLINE_MSVC_BUG982738 phoenix::actor< aux::unary_function_terminal< attribute_matches< T, RegexT > > > matches(attribute_name const& name, RegexT const& rex) { typedef aux::unary_function_terminal< attribute_matches< T, RegexT > > terminal_type; @@ -108,6 +116,8 @@ matches(attribute_name const& name, RegexT const& rex) return act; } +#undef BOOST_LOG_AUX_FORCEINLINE_MSVC_BUG982738 + } // namespace expressions BOOST_LOG_CLOSE_NAMESPACE // namespace log diff --git a/include/boost/log/expressions/predicates/wrap_filter.hpp b/include/boost/log/expressions/predicates/wrap_filter.hpp new file mode 100644 index 0000000000..14afb64791 --- /dev/null +++ b/include/boost/log/expressions/predicates/wrap_filter.hpp @@ -0,0 +1,62 @@ +/* + * Copyright Andrey Semashev 2025. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +/*! + * \file predicates/wrap_filter.hpp + * \author Andrey Semashev + * \date 09.07.2025 + * + * The header contains a filter function wrapper that enables third-party functions to participate in filtering expressions. + */ + +#ifndef BOOST_LOG_EXPRESSIONS_PREDICATES_WRAP_FILTER_HPP_INCLUDED_ +#define BOOST_LOG_EXPRESSIONS_PREDICATES_WRAP_FILTER_HPP_INCLUDED_ + +#include +#include +#include +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace expressions { + +/*! + * The function wraps a function object in order it to be able to participate in filtering expressions. The wrapped function must be + * compatible with the following signature: + * + *
+ * bool (attribute_value_set const& values) const
+ * 
+ * + * The wrapped function must return \c true if the log record is to be passed by the filter and \c false otherwise. + */ +template< typename FunT > +BOOST_FORCEINLINE phoenix::actor< + aux::unary_function_terminal< typename std::remove_cv< typename std::remove_reference< FunT >::type >::type > +> wrap_filter(FunT&& fun) +{ + typedef aux::unary_function_terminal< typename std::remove_cv< typename std::remove_reference< FunT >::type >::type > terminal_type; + phoenix::actor< terminal_type > act = {{ terminal_type(static_cast< FunT&& >(fun)) }}; + return act; +} + +} // namespace expressions + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include + +#endif // BOOST_LOG_EXPRESSIONS_PREDICATES_WRAP_FILTER_HPP_INCLUDED_ diff --git a/include/boost/log/expressions/record.hpp b/include/boost/log/expressions/record.hpp index cfcf62b266..e7f360fe0a 100644 --- a/include/boost/log/expressions/record.hpp +++ b/include/boost/log/expressions/record.hpp @@ -37,7 +37,7 @@ typedef phoenix::expression::argument< 1 >::type record_type; /*! * Log record placeholder in formatter template expressions. */ -const record_type record = {}; +BOOST_INLINE_VARIABLE const record_type record = {}; } // namespace expressions diff --git a/include/boost/log/keywords/auto_newline_mode.hpp b/include/boost/log/keywords/auto_newline_mode.hpp new file mode 100644 index 0000000000..6b9a04110c --- /dev/null +++ b/include/boost/log/keywords/auto_newline_mode.hpp @@ -0,0 +1,40 @@ +/* + * Copyright Andrey Semashev 2019. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * https://www.boost.org/LICENSE_1_0.txt) + */ +/*! + * \file keywords/auto_newline_mode.hpp + * \author Andrey Semashev + * \date 23.06.2019 + * + * The header contains the \c auto_newline_mode keyword declaration. + */ + +#ifndef BOOST_LOG_KEYWORDS_AUTO_NEWLINE_MODE_HPP_INCLUDED_ +#define BOOST_LOG_KEYWORDS_AUTO_NEWLINE_MODE_HPP_INCLUDED_ + +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace keywords { + +//! The keyword for passing automatic trailing newline insertion mode to a sink backend initialization +BOOST_PARAMETER_KEYWORD(tag, auto_newline_mode) + +} // namespace keywords + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#endif // BOOST_LOG_KEYWORDS_AUTO_NEWLINE_MODE_HPP_INCLUDED_ diff --git a/include/boost/log/keywords/target_file_name.hpp b/include/boost/log/keywords/target_file_name.hpp new file mode 100644 index 0000000000..ea32ec9dbd --- /dev/null +++ b/include/boost/log/keywords/target_file_name.hpp @@ -0,0 +1,40 @@ +/* + * Copyright Andrey Semashev 2019. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +/*! + * \file keywords/target.hpp + * \author Andrey Semashev + * \date 05.01.2019 + * + * The header contains the \c target_file_name keyword declaration. + */ + +#ifndef BOOST_LOG_KEYWORDS_TARGET_FILE_NAME_HPP_INCLUDED_ +#define BOOST_LOG_KEYWORDS_TARGET_FILE_NAME_HPP_INCLUDED_ + +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace keywords { + +//! The keyword allows to pass the target file name for file sink +BOOST_PARAMETER_KEYWORD(tag, target_file_name) + +} // namespace keywords + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#endif // BOOST_LOG_KEYWORDS_TARGET_FILE_NAME_HPP_INCLUDED_ diff --git a/include/boost/log/sinks/async_frontend.hpp b/include/boost/log/sinks/async_frontend.hpp index ec5371006e..3e4a1d77da 100644 --- a/include/boost/log/sinks/async_frontend.hpp +++ b/include/boost/log/sinks/async_frontend.hpp @@ -15,6 +15,9 @@ #ifndef BOOST_LOG_SINKS_ASYNC_FRONTEND_HPP_INCLUDED_ #define BOOST_LOG_SINKS_ASYNC_FRONTEND_HPP_INCLUDED_ +#include +#include +#include #include // std::terminate #include @@ -26,18 +29,12 @@ #error Boost.Log: Asynchronous sink frontend is only supported in multithreaded environment #endif -#include -#include #include #include #include #include #include #include -#include -#include -#include -#include #include #include #include @@ -56,12 +53,13 @@ namespace sinks { #ifndef BOOST_LOG_DOXYGEN_PASS -#define BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_1(n, data)\ +#define BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_1(z, n, data)\ template< typename T0 >\ explicit asynchronous_sink(T0 const& arg0, typename boost::log::aux::enable_if_named_parameters< T0, boost::log::aux::sfinae_dummy >::type = boost::log::aux::sfinae_dummy()) :\ base_type(true),\ queue_base_type(arg0),\ m_pBackend(boost::make_shared< sink_backend_type >(arg0)),\ + m_ActiveOperation(idle),\ m_StopRequested(false),\ m_FlushRequested(false)\ {\ @@ -73,6 +71,7 @@ namespace sinks { base_type(true),\ queue_base_type(arg0),\ m_pBackend(backend),\ + m_ActiveOperation(idle),\ m_StopRequested(false),\ m_FlushRequested(false)\ {\ @@ -80,32 +79,34 @@ namespace sinks { start_feeding_thread();\ } -#define BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_N(n, data)\ - template< BOOST_PP_ENUM_PARAMS(n, typename T) >\ - explicit asynchronous_sink(BOOST_PP_ENUM_BINARY_PARAMS(n, T, const& arg)) :\ +#define BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_N(z, n, data)\ + template< BOOST_PP_ENUM_PARAMS_Z(z, n, typename T) >\ + explicit asynchronous_sink(BOOST_PP_ENUM_BINARY_PARAMS_Z(z, n, T, const& arg)) :\ base_type(true),\ - queue_base_type((BOOST_PP_ENUM_PARAMS(n, arg))),\ - m_pBackend(boost::make_shared< sink_backend_type >(BOOST_PP_ENUM_PARAMS(n, arg))),\ + queue_base_type((BOOST_PP_ENUM_PARAMS_Z(z, n, arg))),\ + m_pBackend(boost::make_shared< sink_backend_type >(BOOST_PP_ENUM_PARAMS_Z(z, n, arg))),\ + m_ActiveOperation(idle),\ m_StopRequested(false),\ m_FlushRequested(false)\ {\ - if ((BOOST_PP_ENUM_PARAMS(n, arg))[keywords::start_thread | true])\ + if ((BOOST_PP_ENUM_PARAMS_Z(z, n, arg))[keywords::start_thread | true])\ start_feeding_thread();\ }\ - template< BOOST_PP_ENUM_PARAMS(n, typename T) >\ - explicit asynchronous_sink(shared_ptr< sink_backend_type > const& backend, BOOST_PP_ENUM_BINARY_PARAMS(n, T, const& arg)) :\ + template< BOOST_PP_ENUM_PARAMS_Z(z, n, typename T) >\ + explicit asynchronous_sink(shared_ptr< sink_backend_type > const& backend, BOOST_PP_ENUM_BINARY_PARAMS_Z(z, n, T, const& arg)) :\ base_type(true),\ - queue_base_type((BOOST_PP_ENUM_PARAMS(n, arg))),\ + queue_base_type((BOOST_PP_ENUM_PARAMS_Z(z, n, arg))),\ m_pBackend(backend),\ + m_ActiveOperation(idle),\ m_StopRequested(false),\ m_FlushRequested(false)\ {\ - if ((BOOST_PP_ENUM_PARAMS(n, arg))[keywords::start_thread | true])\ + if ((BOOST_PP_ENUM_PARAMS_Z(z, n, arg))[keywords::start_thread | true])\ start_feeding_thread();\ } #define BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL(z, n, data)\ - BOOST_PP_IF(BOOST_PP_EQUAL(n, 1), BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_1, BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_N)(n, data) + BOOST_PP_IF(BOOST_PP_EQUAL(n, 1), BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_1, BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_N)(z, n, data) #endif // BOOST_LOG_DOXYGEN_PASS @@ -113,7 +114,13 @@ namespace sinks { * \brief Asynchronous logging sink frontend * * The frontend starts a separate thread on construction. All logging records are passed - * to the backend in this dedicated thread only. + * to the backend in this dedicated thread. + * + * The user can prevent spawning the internal thread by specifying \c start_thread parameter + * with the value of \c false on construction. In this case log records will be buffered + * in the internal queue until the user calls \c run, \c feed_records or \c flush in his own + * thread. Log record queueing strategy is specified in the \c QueueingStrategyT template + * parameter. */ template< typename SinkBackendT, typename QueueingStrategyT = unbounded_fifo_queue > class asynchronous_sink : @@ -125,55 +132,57 @@ class asynchronous_sink : private: //! Backend synchronization mutex type - typedef boost::recursive_mutex backend_mutex_type; + typedef std::recursive_mutex backend_mutex_type; //! Frontend synchronization mutex type typedef typename base_type::mutex_type frontend_mutex_type; - //! A scope guard that implements thread ID management - class scoped_thread_id + //! Operation bit mask + enum operation + { + idle = 0u, + feeding_records = 1u, + flushing = 3u + }; + + //! Function object to run the log record feeding thread + class run_func { + public: + typedef void result_type; + private: - frontend_mutex_type& m_Mutex; - condition_variable_any& m_Cond; - thread::id& m_ThreadID; - boost::atomic< bool >& m_StopRequested; + asynchronous_sink* m_self; public: - //! Initializing constructor - scoped_thread_id(frontend_mutex_type& mut, condition_variable_any& cond, thread::id& tid, boost::atomic< bool >& sr) - : m_Mutex(mut), m_Cond(cond), m_ThreadID(tid), m_StopRequested(sr) + explicit run_func(asynchronous_sink* self) BOOST_NOEXCEPT : m_self(self) { - lock_guard< frontend_mutex_type > lock(m_Mutex); - if (m_ThreadID != thread::id()) - BOOST_LOG_THROW_DESCR(unexpected_call, "Asynchronous sink frontend already runs a record feeding thread"); - m_ThreadID = this_thread::get_id(); } + + result_type operator()() const + { + m_self->run(); + } + }; + + //! A scope guard that implements active operation management + class scoped_feeding_operation + { + private: + asynchronous_sink& m_self; + + public: //! Initializing constructor - scoped_thread_id(unique_lock< frontend_mutex_type >& l, condition_variable_any& cond, thread::id& tid, boost::atomic< bool >& sr) - : m_Mutex(*l.mutex()), m_Cond(cond), m_ThreadID(tid), m_StopRequested(sr) + explicit scoped_feeding_operation(asynchronous_sink& self) : m_self(self) { - unique_lock< frontend_mutex_type > lock(move(l)); - if (m_ThreadID != thread::id()) - BOOST_LOG_THROW_DESCR(unexpected_call, "Asynchronous sink frontend already runs a record feeding thread"); - m_ThreadID = this_thread::get_id(); } //! Destructor - ~scoped_thread_id() + ~scoped_feeding_operation() { - try - { - lock_guard< frontend_mutex_type > lock(m_Mutex); - m_StopRequested.store(false, boost::memory_order_release); - m_ThreadID = thread::id(); - m_Cond.notify_all(); - } - catch (...) - { - } + m_self.complete_feeding_operation(); } - BOOST_DELETED_FUNCTION(scoped_thread_id(scoped_thread_id const&)) - BOOST_DELETED_FUNCTION(scoped_thread_id& operator= (scoped_thread_id const&)) + BOOST_DELETED_FUNCTION(scoped_feeding_operation(scoped_feeding_operation const&)) + BOOST_DELETED_FUNCTION(scoped_feeding_operation& operator= (scoped_feeding_operation const&)) }; //! A scope guard that resets a flag on destructor @@ -181,11 +190,11 @@ class asynchronous_sink : { private: frontend_mutex_type& m_Mutex; - condition_variable_any& m_Cond; + std::condition_variable_any& m_Cond; boost::atomic< bool >& m_Flag; public: - explicit scoped_flag(frontend_mutex_type& mut, condition_variable_any& cond, boost::atomic< bool >& f) : + explicit scoped_flag(frontend_mutex_type& mut, std::condition_variable_any& cond, boost::atomic< bool >& f) : m_Mutex(mut), m_Cond(cond), m_Flag(f) { } @@ -193,8 +202,8 @@ class asynchronous_sink : { try { - lock_guard< frontend_mutex_type > lock(m_Mutex); - m_Flag.store(false, boost::memory_order_release); + std::lock_guard< frontend_mutex_type > lock(m_Mutex); + m_Flag.store(false, boost::memory_order_relaxed); m_Cond.notify_all(); } catch (...) @@ -210,7 +219,7 @@ class asynchronous_sink : //! Sink implementation type typedef SinkBackendT sink_backend_type; //! \cond - BOOST_STATIC_ASSERT_MSG((has_requirement< typename sink_backend_type::frontend_requirements, synchronized_feeding >::value), "Asynchronous sink frontend is incompatible with the specified backend: thread synchronization requirements are not met"); + static_assert(has_requirement< typename sink_backend_type::frontend_requirements, synchronized_feeding >::value, "Asynchronous sink frontend is incompatible with the specified backend: thread synchronization requirements are not met"); //! \endcond #ifndef BOOST_LOG_DOXYGEN_PASS @@ -232,12 +241,12 @@ class asynchronous_sink : const shared_ptr< sink_backend_type > m_pBackend; //! Dedicated record feeding thread - thread m_DedicatedFeedingThread; - //! Feeding thread ID - thread::id m_FeedingThreadID; + std::thread m_DedicatedFeedingThread; //! Condition variable to implement blocking operations - condition_variable_any m_BlockCond; + std::condition_variable_any m_BlockCond; + //! Currently active operation + operation m_ActiveOperation; //! The flag indicates that the feeding loop has to be stopped boost::atomic< bool > m_StopRequested; //! The flag indicates that queue flush has been requested @@ -251,11 +260,12 @@ class asynchronous_sink : * \param start_thread If \c true, the frontend creates a thread to feed * log records to the backend. Otherwise no thread is * started and it is assumed that the user will call - * either \c run or \c feed_records himself. + * \c run, \c feed_records or \c flush himself. */ - asynchronous_sink(bool start_thread = true) : + explicit asynchronous_sink(bool start_thread = true) : base_type(true), m_pBackend(boost::make_shared< sink_backend_type >()), + m_ActiveOperation(idle), m_StopRequested(false), m_FlushRequested(false) { @@ -269,13 +279,14 @@ class asynchronous_sink : * \param start_thread If \c true, the frontend creates a thread to feed * log records to the backend. Otherwise no thread is * started and it is assumed that the user will call - * either \c run or \c feed_records himself. + * \c run, \c feed_records or \c flush himself. * * \pre \a backend is not \c NULL. */ explicit asynchronous_sink(shared_ptr< sink_backend_type > const& backend, bool start_thread = true) : base_type(true), m_pBackend(backend), + m_ActiveOperation(idle), m_StopRequested(false), m_FlushRequested(false) { @@ -292,7 +303,7 @@ class asynchronous_sink : * \li start_thread - If \c true, the frontend creates a thread to feed * log records to the backend. Otherwise no thread is * started and it is assumed that the user will call - * either \c run or \c feed_records himself. + * \c run, \c feed_records or \c flush himself. */ #ifndef BOOST_LOG_DOXYGEN_PASS BOOST_LOG_PARAMETRIZED_CONSTRUCTORS_GEN(BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL, ~) @@ -304,17 +315,9 @@ class asynchronous_sink : /*! * Destructor. Implicitly stops the dedicated feeding thread, if one is running. */ - ~asynchronous_sink() BOOST_NOEXCEPT + ~asynchronous_sink() BOOST_NOEXCEPT BOOST_OVERRIDE { - try - { - boost::this_thread::disable_interruption no_interrupts; - stop(); - } - catch (...) - { - std::terminate(); - } + stop(); } /*! @@ -328,11 +331,11 @@ class asynchronous_sink : /*! * Enqueues the log record to the backend */ - void consume(record_view const& rec) + void consume(record_view const& rec) BOOST_OVERRIDE { if (BOOST_UNLIKELY(m_FlushRequested.load(boost::memory_order_acquire))) { - unique_lock< frontend_mutex_type > lock(base_type::frontend_mutex()); + std::unique_lock< frontend_mutex_type > lock(base_type::frontend_mutex()); // Wait until flush is done while (m_FlushRequested.load(boost::memory_order_acquire)) m_BlockCond.wait(lock); @@ -343,12 +346,10 @@ class asynchronous_sink : /*! * The method attempts to pass logging record to the backend */ - bool try_consume(record_view const& rec) + bool try_consume(record_view const& rec) BOOST_OVERRIDE { if (!m_FlushRequested.load(boost::memory_order_acquire)) - { return queue_base_type::try_enqueue(rec); - } else return false; } @@ -356,7 +357,7 @@ class asynchronous_sink : /*! * The method starts record feeding loop and effectively blocks until either of this happens: * - * \li the thread is interrupted due to either standard thread interruption or a call to \c stop + * \li the thread is interrupted due to a call to \c stop * \li an exception is thrown while processing a log record in the backend, and the exception is * not terminated by the exception handler, if one is installed * @@ -365,7 +366,13 @@ class asynchronous_sink : void run() { // First check that no other thread is running - scoped_thread_id guard(base_type::frontend_mutex(), m_BlockCond, m_FeedingThreadID, m_StopRequested); + { + std::unique_lock< frontend_mutex_type > lock(base_type::frontend_mutex()); + if (start_feeding_operation(lock, feeding_records)) + return; + } + + scoped_feeding_operation guard(*this); // Now start the feeding loop while (true) @@ -384,40 +391,42 @@ class asynchronous_sink : } /*! - * The method softly interrupts record feeding loop. This method must be called when the \c run - * method execution has to be interrupted. Unlike regular thread interruption, calling - * \c stop will not interrupt the record processing in the middle. Instead, the sink frontend - * will attempt to finish its business with the record in progress and return afterwards. - * This method can be called either if the sink was created with a dedicated thread, - * or if the feeding loop was initiated by user. + * The method softly interrupts record feeding loop. This method must be called when \c run, + * \c feed_records or \c flush method execution has to be interrupted. Unlike regular thread + * interruption, calling \c stop will not interrupt the record processing in the middle. + * Instead, the sink frontend will attempt to finish its business with the record in progress + * and return afterwards. This method can be called either if the sink was created with + * an internal dedicated thread, or if the feeding loop was initiated by user. + * + * If no record feeding operation is in progress, calling \c stop marks the sink frontend + * so that the next feeding operation stops immediately. * * \note Returning from this method does not guarantee that there are no records left buffered * in the sink frontend. It is possible that log records keep coming during and after this * method is called. At some point of execution of this method log records stop being processed, * and all records that come after this point are put into the queue. These records will be * processed upon further calls to \c run or \c feed_records. + * + * \note If the record feeding loop is being run in a user's thread (i.e. \c start_thread was specified + * as \c false on frontend construction), this method does not guarantee that upon return the thread + * has returned from the record feeding loop or that it won't enter it in the future. The method + * only ensures that the record feeding thread will eventually return from the feeding loop. It is + * user's responsibility to synchronize with the user's record feeding thread. */ void stop() { - unique_lock< frontend_mutex_type > lock(base_type::frontend_mutex()); - if (m_FeedingThreadID != thread::id() || m_DedicatedFeedingThread.joinable()) + std::thread feeding_thread; { - try - { - m_StopRequested.store(true, boost::memory_order_release); - queue_base_type::interrupt_dequeue(); - while (m_StopRequested.load(boost::memory_order_acquire)) - m_BlockCond.wait(lock); - } - catch (...) - { - m_StopRequested.store(false, boost::memory_order_release); - throw; - } + std::lock_guard< frontend_mutex_type > lock(base_type::frontend_mutex()); - lock.unlock(); - m_DedicatedFeedingThread.join(); + m_StopRequested.store(true, boost::memory_order_release); + queue_base_type::interrupt_dequeue(); + + m_DedicatedFeedingThread.swap(feeding_thread); } + + if (feeding_thread.joinable()) + feeding_thread.join(); } /*! @@ -428,7 +437,13 @@ class asynchronous_sink : void feed_records() { // First check that no other thread is running - scoped_thread_id guard(base_type::frontend_mutex(), m_BlockCond, m_FeedingThreadID, m_StopRequested); + { + std::unique_lock< frontend_mutex_type > lock(base_type::frontend_mutex()); + if (start_feeding_operation(lock, feeding_records)) + return; + } + + scoped_feeding_operation guard(*this); // Now start the feeding loop do_feed_records(); @@ -437,29 +452,31 @@ class asynchronous_sink : /*! * The method feeds all log records that may have been buffered to the backend and returns. * Unlike \c feed_records, in case of ordering queueing the method also feeds records - * that were enqueued during the ordering window, attempting to empty the queue completely. + * that were enqueued during the ordering window, attempting to drain the queue completely. */ - void flush() + void flush() BOOST_OVERRIDE { - unique_lock< frontend_mutex_type > lock(base_type::frontend_mutex()); - if (m_FeedingThreadID != thread::id() || m_DedicatedFeedingThread.joinable()) { - // There is already a thread feeding records, let it do the job - m_FlushRequested.store(true, boost::memory_order_release); - queue_base_type::interrupt_dequeue(); - while (!m_StopRequested.load(boost::memory_order_acquire) && m_FlushRequested.load(boost::memory_order_acquire)) - m_BlockCond.wait(lock); + std::unique_lock< frontend_mutex_type > lock(base_type::frontend_mutex()); + if (static_cast< unsigned int >(m_ActiveOperation & feeding_records) != 0u) + { + // There is already a thread feeding records, let it do the job + m_FlushRequested.store(true, boost::memory_order_release); + queue_base_type::interrupt_dequeue(); + while (!m_StopRequested.load(boost::memory_order_acquire) && m_FlushRequested.load(boost::memory_order_acquire)) + m_BlockCond.wait(lock); - // The condition may have been signalled when the feeding thread was finishing. - // In that case records may not have been flushed, and we do the flush ourselves. - if (m_FeedingThreadID != thread::id()) - return; - } + // The condition may have been signalled when the feeding operation was finishing. + // In that case records may not have been flushed, and we do the flush ourselves. + if (m_ActiveOperation != idle) + return; + } - m_FlushRequested.store(true, boost::memory_order_release); + m_ActiveOperation = flushing; + m_FlushRequested.store(true, boost::memory_order_relaxed); + } - // Flush records ourselves. The guard releases the lock. - scoped_thread_id guard(lock, m_BlockCond, m_FeedingThreadID, m_StopRequested); + scoped_feeding_operation guard(*this); do_feed_records(); } @@ -469,7 +486,44 @@ class asynchronous_sink : //! The method spawns record feeding thread void start_feeding_thread() { - boost::thread(boost::bind(&asynchronous_sink::run, this)).swap(m_DedicatedFeedingThread); + std::thread(run_func(this)).swap(m_DedicatedFeedingThread); + } + + //! Starts record feeding operation. The method blocks or throws if another feeding operation is in progress. + bool start_feeding_operation(std::unique_lock< frontend_mutex_type >& lock, operation op) + { + while (m_ActiveOperation != idle) + { + if (BOOST_UNLIKELY(op == feeding_records && m_ActiveOperation == feeding_records)) + BOOST_LOG_THROW_DESCR(unexpected_call, "Asynchronous sink frontend already runs a record feeding thread"); + + if (BOOST_UNLIKELY(m_StopRequested.load(boost::memory_order_relaxed))) + { + m_StopRequested.store(false, boost::memory_order_relaxed); + return true; + } + + m_BlockCond.wait(lock); + } + + m_ActiveOperation = op; + + return false; + } + + //! Completes record feeding operation + void complete_feeding_operation() BOOST_NOEXCEPT + { + try + { + std::lock_guard< frontend_mutex_type > lock(base_type::frontend_mutex()); + m_ActiveOperation = idle; + m_StopRequested.store(false, boost::memory_order_relaxed); + m_BlockCond.notify_all(); + } + catch (...) + { + } } //! The record feeding loop diff --git a/include/boost/log/sinks/auto_newline_mode.hpp b/include/boost/log/sinks/auto_newline_mode.hpp new file mode 100644 index 0000000000..012b81b180 --- /dev/null +++ b/include/boost/log/sinks/auto_newline_mode.hpp @@ -0,0 +1,47 @@ +/* + * Copyright Andrey Semashev 2019. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * https://www.boost.org/LICENSE_1_0.txt) + */ +/*! + * \file sinks/auto_newline_mode.hpp + * \author Andrey Semashev + * \date 23.06.2019 + * + * The header contains definition of auto-newline modes. + */ + +#ifndef BOOST_LOG_SINKS_AUTO_NEWLINE_MODE_HPP_INCLUDED_HPP_ +#define BOOST_LOG_SINKS_AUTO_NEWLINE_MODE_HPP_INCLUDED_HPP_ + +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace sinks { + +//! The enum lists automatic trailing newline modes +enum auto_newline_mode +{ + disabled_auto_newline, //!< Do not insert automatic trailing newline characters + always_insert, //!< Always insert automatic trailing newline characters + insert_if_missing //!< Insert automatic trailing newline characters, if not present already +}; + +} // namespace sinks + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include + +#endif // BOOST_LOG_SINKS_AUTO_NEWLINE_MODE_HPP_INCLUDED_HPP_ diff --git a/include/boost/log/sinks/basic_sink_frontend.hpp b/include/boost/log/sinks/basic_sink_frontend.hpp index 3686bc5917..b6409e317b 100644 --- a/include/boost/log/sinks/basic_sink_frontend.hpp +++ b/include/boost/log/sinks/basic_sink_frontend.hpp @@ -15,7 +15,7 @@ #ifndef BOOST_LOG_SINKS_BASIC_SINK_FRONTEND_HPP_INCLUDED_ #define BOOST_LOG_SINKS_BASIC_SINK_FRONTEND_HPP_INCLUDED_ -#include +#include #include #include #include @@ -26,7 +26,8 @@ #include #include #if !defined(BOOST_LOG_NO_THREADS) -#include +#include +#include #include #include #include @@ -122,19 +123,13 @@ class BOOST_LOG_NO_VTABLE basic_sink_frontend : * * \param attrs A set of attribute values of a logging record */ - bool will_consume(attribute_value_set const& attrs) + bool will_consume(attribute_value_set const& attrs) BOOST_OVERRIDE { BOOST_LOG_EXPR_IF_MT(boost::log::aux::shared_lock_guard< mutex_type > lock(m_Mutex);) try { return m_Filter(attrs); } -#if !defined(BOOST_LOG_NO_THREADS) - catch (thread_interrupted&) - { - throw; - } -#endif catch (...) { if (m_ExceptionHandler.empty()) @@ -164,12 +159,6 @@ class BOOST_LOG_NO_VTABLE basic_sink_frontend : BOOST_LOG_EXPR_IF_MT(boost::log::aux::exclusive_lock_guard< BackendMutexT > lock(backend_mutex);) backend.consume(rec); } -#if !defined(BOOST_LOG_NO_THREADS) - catch (thread_interrupted&) - { - throw; - } -#endif catch (...) { BOOST_LOG_EXPR_IF_MT(boost::log::aux::shared_lock_guard< mutex_type > lock(m_Mutex);) @@ -189,10 +178,6 @@ class BOOST_LOG_NO_VTABLE basic_sink_frontend : if (!backend_mutex.try_lock()) return false; } - catch (thread_interrupted&) - { - throw; - } catch (...) { boost::log::aux::shared_lock_guard< mutex_type > frontend_lock(this->frontend_mutex()); @@ -222,19 +207,13 @@ class BOOST_LOG_NO_VTABLE basic_sink_frontend : private: //! Flushes record buffers in the backend (the actual implementation) template< typename BackendMutexT, typename BackendT > - void flush_backend_impl(BackendMutexT& backend_mutex, BackendT& backend, mpl::true_) + void flush_backend_impl(BackendMutexT& backend_mutex, BackendT& backend, boost::true_type) { try { BOOST_LOG_EXPR_IF_MT(boost::log::aux::exclusive_lock_guard< BackendMutexT > lock(backend_mutex);) backend.flush(); } -#if !defined(BOOST_LOG_NO_THREADS) - catch (thread_interrupted&) - { - throw; - } -#endif catch (...) { BOOST_LOG_EXPR_IF_MT(boost::log::aux::shared_lock_guard< mutex_type > lock(m_Mutex);) @@ -245,7 +224,7 @@ class BOOST_LOG_NO_VTABLE basic_sink_frontend : } //! Flushes record buffers in the backend (stub for backends that don't support flushing) template< typename BackendMutexT, typename BackendT > - void flush_backend_impl(BackendMutexT&, BackendT&, mpl::false_) + void flush_backend_impl(BackendMutexT&, BackendT&, boost::false_type) { } }; @@ -312,7 +291,7 @@ class BOOST_LOG_NO_VTABLE basic_formatting_sink_frontend : formatting_context() : #if !defined(BOOST_LOG_NO_THREADS) - m_Version(0), + m_Version(0u), #endif m_FormattingStream(m_FormattedRecord) { @@ -334,7 +313,7 @@ class BOOST_LOG_NO_VTABLE basic_formatting_sink_frontend : #if !defined(BOOST_LOG_NO_THREADS) //! State version - volatile unsigned int m_Version; + boost::atomic< unsigned int > m_Version; //! Formatter functor formatter_type m_Formatter; @@ -360,7 +339,7 @@ class BOOST_LOG_NO_VTABLE basic_formatting_sink_frontend : explicit basic_formatting_sink_frontend(bool cross_thread) : basic_sink_frontend(cross_thread) #if !defined(BOOST_LOG_NO_THREADS) - , m_Version(0) + , m_Version(0u) #endif { } @@ -374,7 +353,7 @@ class BOOST_LOG_NO_VTABLE basic_formatting_sink_frontend : #if !defined(BOOST_LOG_NO_THREADS) boost::log::aux::exclusive_lock_guard< mutex_type > lock(this->frontend_mutex()); m_Formatter = formatter; - ++m_Version; + m_Version.opaque_add(1u, boost::memory_order_relaxed); #else m_Context.m_Formatter = formatter; #endif @@ -387,7 +366,7 @@ class BOOST_LOG_NO_VTABLE basic_formatting_sink_frontend : #if !defined(BOOST_LOG_NO_THREADS) boost::log::aux::exclusive_lock_guard< mutex_type > lock(this->frontend_mutex()); m_Formatter.reset(); - ++m_Version; + m_Version.opaque_add(1u, boost::memory_order_relaxed); #else m_Context.m_Formatter.reset(); #endif @@ -413,7 +392,7 @@ class BOOST_LOG_NO_VTABLE basic_formatting_sink_frontend : #if !defined(BOOST_LOG_NO_THREADS) boost::log::aux::exclusive_lock_guard< mutex_type > lock(this->frontend_mutex()); m_Locale = loc; - ++m_Version; + m_Version.opaque_add(1u, boost::memory_order_relaxed); #else m_Context.m_FormattingStream.imbue(loc); #endif @@ -438,11 +417,11 @@ class BOOST_LOG_NO_VTABLE basic_formatting_sink_frontend : #if !defined(BOOST_LOG_NO_THREADS) context = m_pContext.get(); - if (!context || context->m_Version != m_Version) + if (!context || context->m_Version != m_Version.load(boost::memory_order_relaxed)) { { boost::log::aux::shared_lock_guard< mutex_type > lock(this->frontend_mutex()); - context = new formatting_context(m_Version, m_Locale, m_Formatter); + context = new formatting_context(m_Version.load(boost::memory_order_relaxed), m_Locale, m_Formatter); } m_pContext.reset(context); } @@ -462,12 +441,6 @@ class BOOST_LOG_NO_VTABLE basic_formatting_sink_frontend : BOOST_LOG_EXPR_IF_MT(boost::log::aux::exclusive_lock_guard< BackendMutexT > lock(backend_mutex);) backend.consume(rec, context->m_FormattedRecord); } -#if !defined(BOOST_LOG_NO_THREADS) - catch (thread_interrupted&) - { - throw; - } -#endif catch (...) { BOOST_LOG_EXPR_IF_MT(boost::log::aux::shared_lock_guard< mutex_type > lock(this->frontend_mutex());) @@ -487,10 +460,6 @@ class BOOST_LOG_NO_VTABLE basic_formatting_sink_frontend : if (!backend_mutex.try_lock()) return false; } - catch (thread_interrupted&) - { - throw; - } catch (...) { boost::log::aux::shared_lock_guard< mutex_type > frontend_lock(this->frontend_mutex()); diff --git a/include/boost/log/sinks/block_on_overflow.hpp b/include/boost/log/sinks/block_on_overflow.hpp index 5bfcda2bcd..d5b3d1ff8a 100644 --- a/include/boost/log/sinks/block_on_overflow.hpp +++ b/include/boost/log/sinks/block_on_overflow.hpp @@ -26,10 +26,10 @@ #error Boost.Log: This header content is only supported in multithreaded environment #endif +#include #include #include #include -#include #include #include @@ -58,7 +58,7 @@ class block_on_overflow struct thread_context : public thread_context_hook_t { - condition_variable cond; + std::condition_variable cond; bool result; thread_context() : result(true) {} diff --git a/include/boost/log/sinks/bounded_fifo_queue.hpp b/include/boost/log/sinks/bounded_fifo_queue.hpp index 9a82b3ad39..227191e6d1 100644 --- a/include/boost/log/sinks/bounded_fifo_queue.hpp +++ b/include/boost/log/sinks/bounded_fifo_queue.hpp @@ -28,9 +28,8 @@ #include #include -#include -#include -#include +#include +#include #include #include @@ -64,13 +63,13 @@ class bounded_fifo_queue : private: typedef OverflowStrategyT overflow_strategy; typedef std::queue< record_view > queue_type; - typedef boost::mutex mutex_type; + typedef std::mutex mutex_type; private: //! Synchronization primitive mutex_type m_mutex; //! Condition to block the consuming thread on - condition_variable m_cond; + std::condition_variable m_cond; //! Log record queue queue_type m_queue; //! Interruption flag @@ -90,7 +89,7 @@ class bounded_fifo_queue : //! Enqueues log record to the queue void enqueue(record_view const& rec) { - unique_lock< mutex_type > lock(m_mutex); + std::unique_lock< mutex_type > lock(m_mutex); std::size_t size = m_queue.size(); for (; size >= MaxQueueSizeV; size = m_queue.size()) { @@ -106,7 +105,7 @@ class bounded_fifo_queue : //! Attempts to enqueue log record to the queue bool try_enqueue(record_view const& rec) { - unique_lock< mutex_type > lock(m_mutex, try_to_lock); + std::unique_lock< mutex_type > lock(m_mutex, std::try_to_lock); if (lock.owns_lock()) { const std::size_t size = m_queue.size(); @@ -133,7 +132,7 @@ class bounded_fifo_queue : //! Attempts to dequeue log record from the queue, does not block if the queue is empty bool try_dequeue(record_view& rec) { - lock_guard< mutex_type > lock(m_mutex); + std::lock_guard< mutex_type > lock(m_mutex); const std::size_t size = m_queue.size(); if (size > 0) { @@ -149,7 +148,7 @@ class bounded_fifo_queue : //! Dequeues log record from the queue, blocks if the queue is empty bool dequeue_ready(record_view& rec) { - unique_lock< mutex_type > lock(m_mutex); + std::unique_lock< mutex_type > lock(m_mutex); while (!m_interruption_requested) { @@ -174,7 +173,7 @@ class bounded_fifo_queue : //! Wakes a thread possibly blocked in the \c dequeue method void interrupt_dequeue() { - lock_guard< mutex_type > lock(m_mutex); + std::lock_guard< mutex_type > lock(m_mutex); m_interruption_requested = true; overflow_strategy::interrupt(); m_cond.notify_one(); diff --git a/include/boost/log/sinks/bounded_ordering_queue.hpp b/include/boost/log/sinks/bounded_ordering_queue.hpp index 157843f9ab..a457b4b3fd 100644 --- a/include/boost/log/sinks/bounded_ordering_queue.hpp +++ b/include/boost/log/sinks/bounded_ordering_queue.hpp @@ -29,13 +29,9 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include #include #include #include @@ -73,7 +69,7 @@ class bounded_ordering_queue : { private: typedef OverflowStrategyT overflow_strategy; - typedef boost::mutex mutex_type; + typedef std::mutex mutex_type; typedef sinks::aux::enqueued_record enqueued_record; typedef std::priority_queue< @@ -83,12 +79,12 @@ class bounded_ordering_queue : > queue_type; private: - //! Ordering window duration, in milliseconds - const uint64_t m_ordering_window; + //! Ordering window duration + const std::chrono::steady_clock::duration m_ordering_window; //! Synchronization primitive mutex_type m_mutex; //! Condition to block the consuming thread on - condition_variable m_cond; + std::condition_variable m_cond; //! Log record queue queue_type m_queue; //! Interruption flag @@ -98,16 +94,16 @@ class bounded_ordering_queue : /*! * Returns ordering window size specified during initialization */ - posix_time::time_duration get_ordering_window() const + std::chrono::steady_clock::duration get_ordering_window() const { - return posix_time::milliseconds(m_ordering_window); + return m_ordering_window; } /*! * Returns default ordering window size. * The default window size is specific to the operating system thread scheduling mechanism. */ - static posix_time::time_duration get_default_ordering_window() + static BOOST_CONSTEXPR std::chrono::steady_clock::duration get_default_ordering_window() BOOST_NOEXCEPT { // The main idea behind this parameter is that the ordering window should be large enough // to allow the frontend to order records from different threads on an attribute @@ -116,14 +112,14 @@ class bounded_ordering_queue : // For instance, on Windows it defaults to around 15-16 ms. // * No less than thread switching quant on the current OS. For now 30 ms is large enough window size to // switch threads on any known OS. It can be tuned for other platforms as needed. - return posix_time::milliseconds(30); + return std::chrono::milliseconds(30); } protected: //! Initializing constructor template< typename ArgsT > explicit bounded_ordering_queue(ArgsT const& args) : - m_ordering_window(args[keywords::ordering_window || &bounded_ordering_queue::get_default_ordering_window].total_milliseconds()), + m_ordering_window(std::chrono::duration_cast< std::chrono::steady_clock::duration >(args[keywords::ordering_window || &bounded_ordering_queue::get_default_ordering_window])), m_queue(args[keywords::order]), m_interruption_requested(false) { @@ -132,7 +128,7 @@ class bounded_ordering_queue : //! Enqueues log record to the queue void enqueue(record_view const& rec) { - unique_lock< mutex_type > lock(m_mutex); + std::unique_lock< mutex_type > lock(m_mutex); std::size_t size = m_queue.size(); for (; size >= MaxQueueSizeV; size = m_queue.size()) { @@ -148,7 +144,7 @@ class bounded_ordering_queue : //! Attempts to enqueue log record to the queue bool try_enqueue(record_view const& rec) { - unique_lock< mutex_type > lock(m_mutex, try_to_lock); + std::unique_lock< mutex_type > lock(m_mutex, std::try_to_lock); if (lock.owns_lock()) { const std::size_t size = m_queue.size(); @@ -169,13 +165,13 @@ class bounded_ordering_queue : //! Attempts to dequeue a log record ready for processing from the queue, does not block if the queue is empty bool try_dequeue_ready(record_view& rec) { - lock_guard< mutex_type > lock(m_mutex); + std::lock_guard< mutex_type > lock(m_mutex); const std::size_t size = m_queue.size(); if (size > 0) { - const boost::log::aux::timestamp now = boost::log::aux::get_timestamp(); + const auto now = std::chrono::steady_clock::now(); enqueued_record const& elem = m_queue.top(); - if (static_cast< uint64_t >((now - elem.m_timestamp).milliseconds()) >= m_ordering_window) + if ((now - elem.m_timestamp) >= m_ordering_window) { // We got a new element rec = elem.m_record; @@ -191,7 +187,7 @@ class bounded_ordering_queue : //! Attempts to dequeue log record from the queue, does not block if the queue is empty bool try_dequeue(record_view& rec) { - lock_guard< mutex_type > lock(m_mutex); + std::lock_guard< mutex_type > lock(m_mutex); const std::size_t size = m_queue.size(); if (size > 0) { @@ -208,16 +204,16 @@ class bounded_ordering_queue : //! Dequeues log record from the queue, blocks if the queue is empty bool dequeue_ready(record_view& rec) { - unique_lock< mutex_type > lock(m_mutex); + std::unique_lock< mutex_type > lock(m_mutex); while (!m_interruption_requested) { const std::size_t size = m_queue.size(); if (size > 0) { - const boost::log::aux::timestamp now = boost::log::aux::get_timestamp(); + const auto now = std::chrono::steady_clock::now(); enqueued_record const& elem = m_queue.top(); - const uint64_t difference = (now - elem.m_timestamp).milliseconds(); + const auto difference = now - elem.m_timestamp; if (difference >= m_ordering_window) { rec = elem.m_record; @@ -228,7 +224,7 @@ class bounded_ordering_queue : else { // Wait until the element becomes ready to be processed - m_cond.timed_wait(lock, posix_time::milliseconds(m_ordering_window - difference)); + m_cond.wait_for(lock, m_ordering_window - difference); } } else @@ -244,7 +240,7 @@ class bounded_ordering_queue : //! Wakes a thread possibly blocked in the \c dequeue method void interrupt_dequeue() { - lock_guard< mutex_type > lock(m_mutex); + std::lock_guard< mutex_type > lock(m_mutex); m_interruption_requested = true; overflow_strategy::interrupt(); m_cond.notify_one(); diff --git a/include/boost/log/sinks/frontend_requirements.hpp b/include/boost/log/sinks/frontend_requirements.hpp index 3577ab1905..d89ea9f309 100644 --- a/include/boost/log/sinks/frontend_requirements.hpp +++ b/include/boost/log/sinks/frontend_requirements.hpp @@ -17,6 +17,9 @@ #ifndef BOOST_LOG_SINKS_FRONTEND_REQUIREMENTS_HPP_INCLUDED_ #define BOOST_LOG_SINKS_FRONTEND_REQUIREMENTS_HPP_INCLUDED_ +#include +#include +#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) #include #include #include @@ -24,8 +27,7 @@ #include #include #include -#include -#include +#endif #include #ifdef BOOST_HAS_PRAGMA_ONCE @@ -75,7 +77,7 @@ struct formatted_records {}; */ struct flushing {}; -#ifdef BOOST_LOG_DOXYGEN_PASS +#if defined(BOOST_LOG_DOXYGEN_PASS) /*! * The metafunction combines multiple requirement tags into one type. The resulting type will @@ -84,6 +86,24 @@ struct flushing {}; template< typename... RequirementsT > struct combine_requirements; +#elif !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + +namespace aux { + +template< typename... RequirementsT > +struct combined_requirements : + public RequirementsT... +{ +}; + +} // namespace aux + +template< typename... RequirementsT > +struct combine_requirements +{ + typedef sinks::aux::combined_requirements< RequirementsT... > type; +}; + #else template< BOOST_PP_ENUM_PARAMS_WITH_A_DEFAULT(BOOST_LOG_COMBINE_REQUIREMENTS_LIMIT, typename ReqT, mpl::na) > diff --git a/include/boost/log/sinks/sync_frontend.hpp b/include/boost/log/sinks/sync_frontend.hpp index 6a927d0571..a498cad14a 100644 --- a/include/boost/log/sinks/sync_frontend.hpp +++ b/include/boost/log/sinks/sync_frontend.hpp @@ -15,6 +15,7 @@ #ifndef BOOST_LOG_SINKS_SYNC_FRONTEND_HPP_INCLUDED_ #define BOOST_LOG_SINKS_SYNC_FRONTEND_HPP_INCLUDED_ +#include #include #ifdef BOOST_HAS_PRAGMA_ONCE @@ -25,12 +26,10 @@ #error Boost.Log: Synchronous sink frontend is only supported in multithreaded environment #endif -#include #include #include #include #include -#include #include #include #include @@ -46,20 +45,20 @@ namespace sinks { #ifndef BOOST_LOG_DOXYGEN_PASS -#define BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_1(n, data)\ +#define BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_1(z, n, data)\ template< typename T0 >\ explicit synchronous_sink(T0 const& arg0, typename boost::log::aux::enable_if_named_parameters< T0, boost::log::aux::sfinae_dummy >::type = boost::log::aux::sfinae_dummy()) :\ base_type(false),\ m_pBackend(boost::make_shared< sink_backend_type >(arg0)) {} -#define BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_N(n, data)\ - template< BOOST_PP_ENUM_PARAMS(n, typename T) >\ - explicit synchronous_sink(BOOST_PP_ENUM_BINARY_PARAMS(n, T, const& arg)) :\ +#define BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_N(z, n, data)\ + template< BOOST_PP_ENUM_PARAMS_Z(z, n, typename T) >\ + explicit synchronous_sink(BOOST_PP_ENUM_BINARY_PARAMS_Z(z, n, T, const& arg)) :\ base_type(false),\ - m_pBackend(boost::make_shared< sink_backend_type >(BOOST_PP_ENUM_PARAMS(n, arg))) {} + m_pBackend(boost::make_shared< sink_backend_type >(BOOST_PP_ENUM_PARAMS_Z(z, n, arg))) {} #define BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL(z, n, data)\ - BOOST_PP_IF(BOOST_PP_EQUAL(n, 1), BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_1, BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_N)(n, data) + BOOST_PP_IF(BOOST_PP_EQUAL(n, 1), BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_1, BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_N)(z, n, data) #endif // BOOST_LOG_DOXYGEN_PASS @@ -76,13 +75,13 @@ class synchronous_sink : private: //! Synchronization mutex type - typedef boost::recursive_mutex backend_mutex_type; + typedef std::recursive_mutex backend_mutex_type; public: //! Sink implementation type typedef SinkBackendT sink_backend_type; //! \cond - BOOST_STATIC_ASSERT_MSG((has_requirement< typename sink_backend_type::frontend_requirements, synchronized_feeding >::value), "Synchronous sink frontend is incompatible with the specified backend: thread synchronization requirements are not met"); + static_assert(has_requirement< typename sink_backend_type::frontend_requirements, synchronized_feeding >::value, "Synchronous sink frontend is incompatible with the specified backend: thread synchronization requirements are not met"); //! \endcond #ifndef BOOST_LOG_DOXYGEN_PASS @@ -148,7 +147,7 @@ class synchronous_sink : /*! * Passes the log record to the backend */ - void consume(record_view const& rec) + void consume(record_view const& rec) BOOST_OVERRIDE { base_type::feed_record(rec, m_BackendMutex, *m_pBackend); } @@ -156,7 +155,7 @@ class synchronous_sink : /*! * The method attempts to pass logging record to the backend */ - bool try_consume(record_view const& rec) + bool try_consume(record_view const& rec) BOOST_OVERRIDE { return base_type::try_feed_record(rec, m_BackendMutex, *m_pBackend); } @@ -166,7 +165,7 @@ class synchronous_sink : * may take considerable time to complete and may block both the calling thread and threads * attempting to put new records into the sink while this call is in progress. */ - void flush() + void flush() BOOST_OVERRIDE { base_type::flush_backend(m_BackendMutex, *m_pBackend); } diff --git a/include/boost/log/sinks/syslog_backend.hpp b/include/boost/log/sinks/syslog_backend.hpp index 7424931189..4a4604dae5 100644 --- a/include/boost/log/sinks/syslog_backend.hpp +++ b/include/boost/log/sinks/syslog_backend.hpp @@ -57,11 +57,11 @@ namespace syslog { { #ifdef BOOST_LOG_USE_NATIVE_SYSLOG native = 0 //!< Use native syslog API -#ifndef BOOST_LOG_NO_ASIO +#ifndef BOOST_LOG_WITHOUT_ASIO , #endif #endif -#ifndef BOOST_LOG_NO_ASIO +#ifndef BOOST_LOG_WITHOUT_ASIO udp_socket_based = 1 //!< Use UDP sockets, according to RFC3164 #endif }; @@ -205,7 +205,7 @@ class syslog_backend : */ BOOST_LOG_API void set_severity_mapper(severity_mapper_type const& mapper); -#if !defined(BOOST_LOG_NO_ASIO) +#if !defined(BOOST_LOG_WITHOUT_ASIO) /*! * The method sets the local host name which log records will be sent from. The host name @@ -247,7 +247,7 @@ class syslog_backend : */ BOOST_LOG_API void set_target_address(boost::asio::ip::address const& addr, unsigned short port = 514); -#endif // !defined(BOOST_LOG_NO_ASIO) +#endif // !defined(BOOST_LOG_WITHOUT_ASIO) /*! * The method passes the formatted message to the syslog API or sends to a syslog server @@ -262,7 +262,7 @@ class syslog_backend : { construct( args[keywords::facility | syslog::user], -#if !defined(BOOST_LOG_NO_ASIO) +#if !defined(BOOST_LOG_WITHOUT_ASIO) args[keywords::use_impl | syslog::udp_socket_based], #else args[keywords::use_impl | syslog::native], diff --git a/include/boost/log/sinks/text_file_backend.hpp b/include/boost/log/sinks/text_file_backend.hpp index 899b5c49bc..e6674f54cc 100644 --- a/include/boost/log/sinks/text_file_backend.hpp +++ b/include/boost/log/sinks/text_file_backend.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -30,15 +31,18 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include +#include #include #include #include @@ -63,6 +67,20 @@ enum scan_method scan_all //!< Scan for all files in the directory }; +//! The structure contains filesystem scanning results +struct scan_result +{ + //! The number of found files + uintmax_t found_count; + //! If populated, the largest file counter that was used in the found file names + boost::optional< unsigned int > last_file_counter; + + scan_result() BOOST_NOEXCEPT : + found_count(0u) + { + } +}; + /*! * \brief Base class for file collectors * @@ -78,7 +96,11 @@ struct BOOST_LOG_NO_VTABLE collector /*! * Virtual destructor */ +#if !defined(BOOST_LOG_NO_CXX11_DEFAULTED_VIRTUAL_FUNCTIONS) + BOOST_DEFAULTED_FUNCTION(virtual ~collector(), {}) +#else virtual ~collector() {} +#endif /*! * The function stores the specified file in the storage. May lead to an older file @@ -88,6 +110,13 @@ struct BOOST_LOG_NO_VTABLE collector */ virtual void store_file(filesystem::path const& src_path) = 0; + /*! + * The function checks if the specified path refers to an existing file in the storage. + * + * \param src_path The path to be checked + */ + virtual bool is_in_storage(filesystem::path const& src_path) const = 0; + /*! * Scans the target directory for the files that have already been stored. The found * files are added to the collector in order to be tracked and erased, if needed. @@ -107,19 +136,17 @@ struct BOOST_LOG_NO_VTABLE collector * \param method The method of scanning. If \c no_scan is specified, the call has no effect. * \param pattern The file name pattern if \a method is \c scan_matching. Otherwise the parameter * is not used. - * \param counter If not \c NULL and \a method is \c scan_matching, the method suggests initial value - * of a file counter that may be used in the file name pattern. The parameter - * is not used otherwise. - * \return The number of found files. + * \return The result of filesystem scanning. The last file counter is only populated if + * \a method is \c scan_matching, the \a pattern contains %N placeholder, and at least + * one file matching the pattern is found. * * \note In case if \a method is \c scan_matching the effect of this function is highly dependent * on the \a pattern definition. It is recommended to choose patterns with easily * distinguished placeholders (i.e. having delimiters between them). Otherwise * either some files can be mistakenly found or not found, which in turn may lead - * to an incorrect file deletion. + * to deletion of an unintended file. */ - virtual uintmax_t scan_for_files( - scan_method method, filesystem::path const& pattern = filesystem::path(), unsigned int* counter = 0) = 0; + virtual scan_result scan_for_files(scan_method method, filesystem::path const& pattern = filesystem::path()) = 0; BOOST_DELETED_FUNCTION(collector(collector const&)) BOOST_DELETED_FUNCTION(collector& operator= (collector const&)) @@ -233,8 +260,8 @@ class rotation_at_time_point monthday }; - day_kind m_DayKind : 2; unsigned char m_Day : 6; + unsigned char m_DayKind : 2; // contains day_kind values unsigned char m_Hour, m_Minute, m_Second; mutable posix_time::ptime m_Previous; @@ -371,7 +398,7 @@ class text_file_backend : * Constructor. Creates a sink backend with the specified named parameters. * The following named parameters are supported: * - * \li \c file_name - Specifies the file name pattern where logs are actually written to. The pattern may + * \li \c file_name - Specifies the active file name pattern where logs are actually written to. The pattern may * contain directory and file name portions, but only the file name may contain * placeholders. The backend supports Boost.DateTime placeholders for injecting * current time and date into the file name. Also, an additional %N placeholder is @@ -379,6 +406,10 @@ class text_file_backend : * may also contain width specification in the printf-compatible form (e.g. %5N). The * printed file counter will always be zero-filled. If \c file_name is not specified, * pattern "%5N.log" will be used. + * \li \c target_file_name - Specifies the target file name pattern to use to rename the log file on rotation, + * before passing it to the file collector. The pattern may contain the same + * placeholders as the \c file_name parameter. By default, no renaming is done, + * i.e. the written log file keeps its name according to \c file_name. * \li \c open_mode - File open mode. The mode should be presented in form of mask compatible to * std::ios_base::openmode. If not specified, trunc | out will be used. * \li \c rotation_size - Specifies the approximate size, in characters written, of the temporary file @@ -392,6 +423,8 @@ class text_file_backend : * sink backend destruction. By default, is \c true. * \li \c auto_flush - Specifies a flag, whether or not to automatically flush the file after each * written log record. By default, is \c false. + * \li \c auto_newline_mode - Specifies automatic trailing newline insertion mode. Must be a value of + * the \c auto_newline_mode enum. By default, is auto_newline_mode::insert_if_missing. * * \note Read the caution note regarding file name pattern in the sinks::file::collector::scan_for_files * documentation. @@ -409,7 +442,7 @@ class text_file_backend : BOOST_LOG_API ~text_file_backend(); /*! - * The method sets file name wildcard for the files being written. The wildcard supports + * The method sets the active file name wildcard for the files being written. The wildcard supports * date and time injection into the file name. * * \param pattern The name pattern for the file being written. @@ -420,6 +453,22 @@ class text_file_backend : set_file_name_pattern_internal(filesystem::path(pattern)); } + /*! + * The method sets the target file name wildcard for the files being rotated. The wildcard supports + * date and time injection into the file name. + * + * This pattern will be used when the log file is being rotated, to rename the just written + * log file (which has the name according to the pattern in the \c file_name constructor parameter or + * set by a call to \c set_file_name_pattern), just before passing the file to the file collector. + * + * \param pattern The name pattern for the file being rotated. + */ + template< typename PathT > + void set_target_file_name_pattern(PathT const& pattern) + { + set_target_file_name_pattern_internal(filesystem::path(pattern)); + } + /*! * The method sets the file open mode * @@ -491,6 +540,14 @@ class text_file_backend : */ BOOST_LOG_API void auto_flush(bool enable = true); + /*! + * Selects whether a trailing newline should be automatically inserted after every log record. See + * \c auto_newline_mode description for the possible modes of operation. + * + * \param mode The trailing newline insertion mode. + */ + BOOST_LOG_API void set_auto_newline_mode(auto_newline_mode mode); + /*! * \return The name of the currently open log file. If no file is open, returns an empty path. */ @@ -502,7 +559,7 @@ class text_file_backend : * as if they were rotated. * * The file scan can be performed in two ways: either all files in the target directory will - * be considered as log files, or only those files that satisfy the file name pattern. + * be considered as log files, or only those files that satisfy the target file name pattern. * See documentation on sinks::file::collector::scan_for_files for more information. * * \pre File collector and the proper file name pattern have already been set. @@ -541,23 +598,29 @@ class text_file_backend : { construct( filesystem::path(args[keywords::file_name | filesystem::path()]), + filesystem::path(args[keywords::target_file_name | filesystem::path()]), args[keywords::open_mode | (std::ios_base::trunc | std::ios_base::out)], args[keywords::rotation_size | (std::numeric_limits< uintmax_t >::max)()], args[keywords::time_based_rotation | time_based_rotation_predicate()], + args[keywords::auto_newline_mode | insert_if_missing], args[keywords::auto_flush | false], args[keywords::enable_final_rotation | true]); } //! Constructor implementation BOOST_LOG_API void construct( filesystem::path const& pattern, + filesystem::path const& target_file_name, std::ios_base::openmode mode, uintmax_t rotation_size, time_based_rotation_predicate const& time_based_rotation, + auto_newline_mode auto_newline, bool auto_flush, bool enable_final_rotation); - //! The method sets file name mask + //! The method sets file name pattern BOOST_LOG_API void set_file_name_pattern_internal(filesystem::path const& pattern); + //! The method sets target file name pattern + BOOST_LOG_API void set_target_file_name_pattern_internal(filesystem::path const& pattern); //! Closes the currently open file void close_file(); diff --git a/include/boost/log/sinks/text_ipc_message_queue_backend.hpp b/include/boost/log/sinks/text_ipc_message_queue_backend.hpp index 370ccfb32f..de5ab524bc 100644 --- a/include/boost/log/sinks/text_ipc_message_queue_backend.hpp +++ b/include/boost/log/sinks/text_ipc_message_queue_backend.hpp @@ -44,18 +44,18 @@ namespace sinks { #ifndef BOOST_LOG_DOXYGEN_PASS -#define BOOST_LOG_IPC_BACKEND_CTOR_FORWARD_INTERNAL_1(n, data)\ +#define BOOST_LOG_IPC_BACKEND_CTOR_FORWARD_INTERNAL_1(z, n, data)\ template< typename T0 >\ explicit text_ipc_message_queue_backend(T0 const& arg0, typename boost::log::aux::enable_if_named_parameters< T0, boost::log::aux::sfinae_dummy >::type = boost::log::aux::sfinae_dummy()) :\ m_queue(arg0) {} -#define BOOST_LOG_IPC_BACKEND_CTOR_FORWARD_INTERNAL_N(n, data)\ - template< BOOST_PP_ENUM_PARAMS(n, typename T) >\ - explicit text_ipc_message_queue_backend(BOOST_PP_ENUM_BINARY_PARAMS(n, T, const& arg)) :\ - m_queue(BOOST_PP_ENUM_PARAMS(n, arg)) {} +#define BOOST_LOG_IPC_BACKEND_CTOR_FORWARD_INTERNAL_N(z, n, data)\ + template< BOOST_PP_ENUM_PARAMS_Z(z, n, typename T) >\ + explicit text_ipc_message_queue_backend(BOOST_PP_ENUM_BINARY_PARAMS_Z(z, n, T, const& arg)) :\ + m_queue(BOOST_PP_ENUM_PARAMS_Z(z, n, arg)) {} #define BOOST_LOG_IPC_BACKEND_CTOR_FORWARD_INTERNAL(z, n, data)\ - BOOST_PP_IF(BOOST_PP_EQUAL(n, 1), BOOST_LOG_IPC_BACKEND_CTOR_FORWARD_INTERNAL_1, BOOST_LOG_IPC_BACKEND_CTOR_FORWARD_INTERNAL_N)(n, data) + BOOST_PP_IF(BOOST_PP_EQUAL(n, 1), BOOST_LOG_IPC_BACKEND_CTOR_FORWARD_INTERNAL_1, BOOST_LOG_IPC_BACKEND_CTOR_FORWARD_INTERNAL_N)(z, n, data) #endif // BOOST_LOG_DOXYGEN_PASS diff --git a/include/boost/log/sinks/text_multifile_backend.hpp b/include/boost/log/sinks/text_multifile_backend.hpp index 8e707b990c..2db672b366 100644 --- a/include/boost/log/sinks/text_multifile_backend.hpp +++ b/include/boost/log/sinks/text_multifile_backend.hpp @@ -25,7 +25,10 @@ #include #include #include +#include #include +#include +#include #include #include #include @@ -161,10 +164,24 @@ class text_multifile_backend : public: /*! * Default constructor. The constructed sink backend has no file name composer and - * thus will not write any files. + * thus will not write any files. All other parameters are set to their defaults. */ BOOST_LOG_API text_multifile_backend(); + /*! + * Constructor. Creates a sink backend with the specified named parameters. + * The following named parameters are supported: + * + * \li \c auto_newline_mode - Specifies automatic trailing newline insertion mode. Must be a value of + * the \c auto_newline_mode enum. By default, is auto_newline_mode::insert_if_missing. + */ +#ifndef BOOST_LOG_DOXYGEN_PASS + BOOST_LOG_PARAMETRIZED_CONSTRUCTORS_CALL(text_multifile_backend, construct) +#else + template< typename... ArgsT > + explicit text_multifile_backend(ArgsT... const& args); +#endif + /*! * Destructor */ @@ -181,6 +198,14 @@ class text_multifile_backend : set_file_name_composer_internal(composer); } + /*! + * Selects whether a trailing newline should be automatically inserted after every log record. See + * \c auto_newline_mode description for the possible modes of operation. + * + * \param mode The trailing newline insertion mode. + */ + BOOST_LOG_API void set_auto_newline_mode(auto_newline_mode mode); + /*! * The method writes the message to the sink */ @@ -188,6 +213,15 @@ class text_multifile_backend : private: #ifndef BOOST_LOG_DOXYGEN_PASS + //! Constructor implementation + template< typename ArgsT > + void construct(ArgsT const& args) + { + construct(args[keywords::auto_newline_mode | insert_if_missing]); + } + //! Constructor implementation + BOOST_LOG_API void construct(auto_newline_mode auto_newline); + //! The method sets the file name composer BOOST_LOG_API void set_file_name_composer_internal(file_name_composer_type const& composer); #endif // BOOST_LOG_DOXYGEN_PASS diff --git a/include/boost/log/sinks/text_ostream_backend.hpp b/include/boost/log/sinks/text_ostream_backend.hpp index 9d8ad0d6cd..487c8f8178 100644 --- a/include/boost/log/sinks/text_ostream_backend.hpp +++ b/include/boost/log/sinks/text_ostream_backend.hpp @@ -18,6 +18,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -71,6 +75,23 @@ class basic_text_ostream_backend : * Constructor. No streams attached to the constructed backend, auto flush feature disabled. */ BOOST_LOG_API basic_text_ostream_backend(); + + /*! + * Constructor. Creates a sink backend with the specified named parameters. + * The following named parameters are supported: + * + * \li \c auto_flush - Specifies a flag, whether or not to automatically flush the attached streams after each + * written log record. By default, is \c false. + * \li \c auto_newline_mode - Specifies automatic trailing newline insertion mode. Must be a value of + * the \c auto_newline_mode enum. By default, is auto_newline_mode::insert_if_missing. + */ +#ifndef BOOST_LOG_DOXYGEN_PASS + BOOST_LOG_PARAMETRIZED_CONSTRUCTORS_CALL(basic_text_ostream_backend, construct) +#else + template< typename... ArgsT > + explicit basic_text_ostream_backend(ArgsT... const& args); +#endif + /*! * Destructor */ @@ -91,19 +112,43 @@ class basic_text_ostream_backend : BOOST_LOG_API void remove_stream(shared_ptr< stream_type > const& strm); /*! - * Sets the flag to automatically flush buffers of all attached streams after each log record + * Sets the flag to automatically flush buffers of all attached streams after each log record. + * + * \param enable The flag indicates whether the automatic buffer flush should be performed. */ - BOOST_LOG_API void auto_flush(bool f = true); + BOOST_LOG_API void auto_flush(bool enable = true); /*! - * The method writes the message to the sink + * Selects whether a trailing newline should be automatically inserted after every log record. See + * \c auto_newline_mode description for the possible modes of operation. + * + * \param mode The trailing newline insertion mode. + */ + BOOST_LOG_API void set_auto_newline_mode(auto_newline_mode mode); + + /*! + * The method writes the message to the sink. */ BOOST_LOG_API void consume(record_view const& rec, string_type const& formatted_message); /*! - * The method flushes the associated streams + * The method flushes all attached streams. */ BOOST_LOG_API void flush(); + +private: +#ifndef BOOST_LOG_DOXYGEN_PASS + //! Constructor implementation + template< typename ArgsT > + void construct(ArgsT const& args) + { + construct( + args[keywords::auto_newline_mode | insert_if_missing], + args[keywords::auto_flush | false]); + } + //! Constructor implementation + BOOST_LOG_API void construct(auto_newline_mode auto_newline, bool auto_flush); +#endif // BOOST_LOG_DOXYGEN_PASS }; #ifdef BOOST_LOG_USE_CHAR diff --git a/include/boost/log/sinks/unbounded_ordering_queue.hpp b/include/boost/log/sinks/unbounded_ordering_queue.hpp index c84d8fd4d8..df335f3220 100644 --- a/include/boost/log/sinks/unbounded_ordering_queue.hpp +++ b/include/boost/log/sinks/unbounded_ordering_queue.hpp @@ -28,13 +28,9 @@ #include #include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include #include #include #include @@ -69,7 +65,7 @@ template< typename OrderT > class unbounded_ordering_queue { private: - typedef boost::mutex mutex_type; + typedef std::mutex mutex_type; typedef sinks::aux::enqueued_record enqueued_record; typedef std::priority_queue< @@ -79,12 +75,12 @@ class unbounded_ordering_queue > queue_type; private: - //! Ordering window duration, in milliseconds - const uint64_t m_ordering_window; + //! Ordering window duration + const std::chrono::steady_clock::duration m_ordering_window; //! Synchronization mutex mutex_type m_mutex; //! Condition for blocking - condition_variable m_cond; + std::condition_variable m_cond; //! Thread-safe queue queue_type m_queue; //! Interruption flag @@ -94,16 +90,16 @@ class unbounded_ordering_queue /*! * Returns ordering window size specified during initialization */ - posix_time::time_duration get_ordering_window() const + std::chrono::steady_clock::duration get_ordering_window() const { - return posix_time::milliseconds(m_ordering_window); + return m_ordering_window; } /*! * Returns default ordering window size. * The default window size is specific to the operating system thread scheduling mechanism. */ - static posix_time::time_duration get_default_ordering_window() + static BOOST_CONSTEXPR std::chrono::steady_clock::duration get_default_ordering_window() BOOST_NOEXCEPT { // The main idea behind this parameter is that the ordering window should be large enough // to allow the frontend to order records from different threads on an attribute @@ -112,14 +108,14 @@ class unbounded_ordering_queue // For instance, on Windows it defaults to around 15-16 ms. // * No less than thread switching quant on the current OS. For now 30 ms is large enough window size to // switch threads on any known OS. It can be tuned for other platforms as needed. - return posix_time::milliseconds(30); + return std::chrono::milliseconds(30); } protected: //! Initializing constructor template< typename ArgsT > explicit unbounded_ordering_queue(ArgsT const& args) : - m_ordering_window(args[keywords::ordering_window || &unbounded_ordering_queue::get_default_ordering_window].total_milliseconds()), + m_ordering_window(std::chrono::duration_cast< std::chrono::steady_clock::duration >(args[keywords::ordering_window || &unbounded_ordering_queue::get_default_ordering_window])), m_queue(args[keywords::order]), m_interruption_requested(false) { @@ -128,14 +124,14 @@ class unbounded_ordering_queue //! Enqueues log record to the queue void enqueue(record_view const& rec) { - lock_guard< mutex_type > lock(m_mutex); + std::lock_guard< mutex_type > lock(m_mutex); enqueue_unlocked(rec); } //! Attempts to enqueue log record to the queue bool try_enqueue(record_view const& rec) { - unique_lock< mutex_type > lock(m_mutex, try_to_lock); + std::unique_lock< mutex_type > lock(m_mutex, std::try_to_lock); if (lock.owns_lock()) { enqueue_unlocked(rec); @@ -148,12 +144,12 @@ class unbounded_ordering_queue //! Attempts to dequeue a log record ready for processing from the queue, does not block if no log records are ready to be processed bool try_dequeue_ready(record_view& rec) { - lock_guard< mutex_type > lock(m_mutex); + std::lock_guard< mutex_type > lock(m_mutex); if (!m_queue.empty()) { - const boost::log::aux::timestamp now = boost::log::aux::get_timestamp(); + const auto now = std::chrono::steady_clock::now(); enqueued_record const& elem = m_queue.top(); - if (static_cast< uint64_t >((now - elem.m_timestamp).milliseconds()) >= m_ordering_window) + if ((now - elem.m_timestamp) >= m_ordering_window) { // We got a new element rec = elem.m_record; @@ -168,7 +164,7 @@ class unbounded_ordering_queue //! Attempts to dequeue log record from the queue, does not block. bool try_dequeue(record_view& rec) { - lock_guard< mutex_type > lock(m_mutex); + std::lock_guard< mutex_type > lock(m_mutex); if (!m_queue.empty()) { enqueued_record const& elem = m_queue.top(); @@ -183,14 +179,14 @@ class unbounded_ordering_queue //! Dequeues log record from the queue, blocks if no log records are ready to be processed bool dequeue_ready(record_view& rec) { - unique_lock< mutex_type > lock(m_mutex); + std::unique_lock< mutex_type > lock(m_mutex); while (!m_interruption_requested) { if (!m_queue.empty()) { - const boost::log::aux::timestamp now = boost::log::aux::get_timestamp(); + const auto now = std::chrono::steady_clock::now(); enqueued_record const& elem = m_queue.top(); - const uint64_t difference = (now - elem.m_timestamp).milliseconds(); + const auto difference = now - elem.m_timestamp; if (difference >= m_ordering_window) { // We got a new element @@ -201,7 +197,7 @@ class unbounded_ordering_queue else { // Wait until the element becomes ready to be processed - m_cond.timed_wait(lock, posix_time::milliseconds(m_ordering_window - difference)); + m_cond.wait_for(lock, m_ordering_window - difference); } } else @@ -218,7 +214,7 @@ class unbounded_ordering_queue //! Wakes a thread possibly blocked in the \c dequeue method void interrupt_dequeue() { - lock_guard< mutex_type > lock(m_mutex); + std::lock_guard< mutex_type > lock(m_mutex); m_interruption_requested = true; m_cond.notify_one(); } diff --git a/include/boost/log/sinks/unlocked_frontend.hpp b/include/boost/log/sinks/unlocked_frontend.hpp index 918efeb6d2..472a9624bd 100644 --- a/include/boost/log/sinks/unlocked_frontend.hpp +++ b/include/boost/log/sinks/unlocked_frontend.hpp @@ -15,7 +15,6 @@ #ifndef BOOST_LOG_SINKS_UNLOCKED_FRONTEND_HPP_INCLUDED_ #define BOOST_LOG_SINKS_UNLOCKED_FRONTEND_HPP_INCLUDED_ -#include #include #include #include @@ -39,20 +38,20 @@ namespace sinks { #ifndef BOOST_LOG_DOXYGEN_PASS -#define BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_1(n, data)\ +#define BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_1(z, n, data)\ template< typename T0 >\ explicit unlocked_sink(T0 const& arg0, typename boost::log::aux::enable_if_named_parameters< T0, boost::log::aux::sfinae_dummy >::type = boost::log::aux::sfinae_dummy()) :\ base_type(false),\ m_pBackend(boost::make_shared< sink_backend_type >(arg0)) {} -#define BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_N(n, data)\ - template< BOOST_PP_ENUM_PARAMS(n, typename T) >\ - explicit unlocked_sink(BOOST_PP_ENUM_BINARY_PARAMS(n, T, const& arg)) :\ +#define BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_N(z, n, data)\ + template< BOOST_PP_ENUM_PARAMS_Z(z, n, typename T) >\ + explicit unlocked_sink(BOOST_PP_ENUM_BINARY_PARAMS_Z(z, n, T, const& arg)) :\ base_type(false),\ - m_pBackend(boost::make_shared< sink_backend_type >(BOOST_PP_ENUM_PARAMS(n, arg))) {} + m_pBackend(boost::make_shared< sink_backend_type >(BOOST_PP_ENUM_PARAMS_Z(z, n, arg))) {} #define BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL(z, n, data)\ - BOOST_PP_IF(BOOST_PP_EQUAL(n, 1), BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_1, BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_N)(n, data) + BOOST_PP_IF(BOOST_PP_EQUAL(n, 1), BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_1, BOOST_LOG_SINK_CTOR_FORWARD_INTERNAL_N)(z, n, data) #endif // BOOST_LOG_DOXYGEN_PASS @@ -72,7 +71,7 @@ class unlocked_sink : //! Sink implementation type typedef SinkBackendT sink_backend_type; //! \cond - BOOST_STATIC_ASSERT_MSG((has_requirement< typename sink_backend_type::frontend_requirements, concurrent_feeding >::value), "Unlocked sink frontend is incompatible with the specified backend: thread synchronization requirements are not met"); + static_assert(has_requirement< typename sink_backend_type::frontend_requirements, concurrent_feeding >::value, "Unlocked sink frontend is incompatible with the specified backend: thread synchronization requirements are not met"); //! \endcond //! Type of pointer to the backend diff --git a/include/boost/log/sources/basic_logger.hpp b/include/boost/log/sources/basic_logger.hpp index 58bd6594c5..818f8e259a 100644 --- a/include/boost/log/sources/basic_logger.hpp +++ b/include/boost/log/sources/basic_logger.hpp @@ -23,11 +23,10 @@ #include #include #include +#include +#include #include #include -#include -#include -#include #include #include #include @@ -92,7 +91,7 @@ class basic_logger : #if !defined(BOOST_LOG_NO_THREADS) //! Lock requirement for the swap_unlocked method - typedef boost::log::aux::exclusive_lock_guard< threading_model > swap_lock; + typedef boost::log::aux::multiple_unique_lock2< threading_model, threading_model > swap_lock; //! Lock requirement for the add_attribute_unlocked method typedef boost::log::aux::exclusive_lock_guard< threading_model > add_attribute_lock; //! Lock requirement for the remove_attribute_unlocked method @@ -144,7 +143,7 @@ class basic_logger : */ basic_logger(basic_logger const& that) : threading_model(static_cast< threading_model const& >(that)), - m_pCore(core::get()), + m_pCore(that.m_pCore), m_Attributes(that.m_Attributes) { } @@ -155,11 +154,13 @@ class basic_logger : * * \param that Source logger */ - basic_logger(BOOST_RV_REF(basic_logger) that) : - threading_model(boost::move(static_cast< threading_model& >(that))) + basic_logger(BOOST_RV_REF(basic_logger) that) BOOST_NOEXCEPT_IF(boost::is_nothrow_move_constructible< threading_model >::value && + boost::is_nothrow_move_constructible< core_ptr >::value && + boost::is_nothrow_move_constructible< attribute_set >::value) : + threading_model(boost::move(static_cast< threading_model& >(that))), + m_pCore(boost::move(that.m_pCore)), + m_Attributes(boost::move(that.m_Attributes)) { - m_pCore.swap(that.m_pCore); - m_Attributes.swap(that.m_Attributes); } /*! * Constructor with named arguments. The constructor ignores all arguments. The result of @@ -188,15 +189,15 @@ class basic_logger : /*! * An accessor to the threading model base */ - threading_model& get_threading_model() { return *this; } + threading_model& get_threading_model() BOOST_NOEXCEPT { return *this; } /*! * An accessor to the threading model base */ - threading_model const& get_threading_model() const { return *this; } + threading_model const& get_threading_model() const BOOST_NOEXCEPT { return *this; } /*! * An accessor to the final logger */ - final_type* final_this() + final_type* final_this() BOOST_NOEXCEPT { BOOST_LOG_ASSUME(this != NULL); return static_cast< final_type* >(this); @@ -204,7 +205,7 @@ class basic_logger : /*! * An accessor to the final logger */ - final_type const* final_this() const + final_type const* final_this() const BOOST_NOEXCEPT { BOOST_LOG_ASSUME(this != NULL); return static_cast< final_type const* >(this); @@ -293,7 +294,7 @@ class basic_logger : template< typename CharT, typename FinalT, typename ThreadingModelT > inline void swap( basic_logger< CharT, FinalT, ThreadingModelT >& left, - basic_logger< CharT, FinalT, ThreadingModelT >& right) + basic_logger< CharT, FinalT, ThreadingModelT >& right) BOOST_NOEXCEPT_IF(boost::is_nothrow_swappable< FinalT >::value) { static_cast< FinalT& >(left).swap(static_cast< FinalT& >(right)); } @@ -332,6 +333,9 @@ class basic_composite_logger : //! Threading model being used typedef typename base_type::threading_model threading_model; + //! Lock requirement for the swap_unlocked method + typedef typename base_type::swap_lock swap_lock; + #if !defined(BOOST_LOG_NO_THREADS) public: @@ -353,7 +357,7 @@ class basic_composite_logger : /*! * Move constructor */ - basic_composite_logger(BOOST_RV_REF(logger_base) that) : + basic_composite_logger(BOOST_RV_REF(logger_base) that) BOOST_NOEXCEPT_IF(boost::is_nothrow_move_constructible< base_type >::value) : base_type(boost::move(static_cast< base_type& >(that))) { } @@ -476,11 +480,8 @@ class basic_composite_logger : */ void swap(basic_composite_logger& that) { - boost::log::aux::multiple_unique_lock2< - threading_model, - threading_model - > lock(base_type::get_threading_model(), that.get_threading_model()); - base_type::swap_unlocked(that); + swap_lock lock(base_type::get_threading_model(), that.get_threading_model()); + base_type::swap_unlocked(static_cast< base_type& >(that)); } protected: @@ -530,7 +531,7 @@ class basic_composite_logger< CharT, FinalT, single_thread_model, FeaturesT > : base_type(static_cast< base_type const& >(that)) { } - basic_composite_logger(BOOST_RV_REF(logger_base) that) : + basic_composite_logger(BOOST_RV_REF(logger_base) that) BOOST_NOEXCEPT_IF(boost::is_nothrow_move_constructible< base_type >::value) : base_type(boost::move(static_cast< base_type& >(that))) { } @@ -582,7 +583,7 @@ class basic_composite_logger< CharT, FinalT, single_thread_model, FeaturesT > : } void swap(basic_composite_logger& that) { - base_type::swap_unlocked(that); + base_type::swap_unlocked(static_cast< base_type& >(that)); } protected: @@ -601,7 +602,7 @@ class basic_composite_logger< CharT, FinalT, single_thread_model, FeaturesT > : BOOST_DEFAULTED_FUNCTION(class_type(), {})\ class_type(class_type const& that) : class_type::logger_base(\ static_cast< typename_keyword() class_type::logger_base const& >(that)) {}\ - class_type(BOOST_RV_REF(class_type) that) : class_type::logger_base(\ + class_type(BOOST_RV_REF(class_type) that) BOOST_NOEXCEPT_IF(boost::is_nothrow_move_constructible< typename_keyword() class_type::logger_base >::value) : class_type::logger_base(\ ::boost::move(static_cast< typename_keyword() class_type::logger_base& >(that))) {}\ BOOST_LOG_PARAMETRIZED_CONSTRUCTORS_FORWARD(class_type, class_type::logger_base)\ diff --git a/include/boost/log/sources/channel_feature.hpp b/include/boost/log/sources/channel_feature.hpp index d7e88c4aad..7893ed8090 100644 --- a/include/boost/log/sources/channel_feature.hpp +++ b/include/boost/log/sources/channel_feature.hpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -76,7 +77,7 @@ class basic_channel_logger : typedef typename strictest_lock< typename base_type::swap_lock, #ifndef BOOST_LOG_NO_THREADS - boost::log::aux::exclusive_lock_guard< threading_model > + boost::log::aux::multiple_unique_lock2< threading_model, threading_model > #else no_lock< threading_model > #endif // !defined(BOOST_LOG_NO_THREADS) @@ -107,18 +108,18 @@ class basic_channel_logger : */ basic_channel_logger(basic_channel_logger const& that) : base_type(static_cast< base_type const& >(that)), - m_ChannelAttr(that.m_ChannelAttr) + m_ChannelAttr(that.m_ChannelAttr.get()) { + // Our attributes must refer to our channel attribute base_type::attributes()[boost::log::aux::default_attribute_names::channel()] = m_ChannelAttr; } /*! * Move constructor */ - basic_channel_logger(BOOST_RV_REF(basic_channel_logger) that) : + basic_channel_logger(BOOST_RV_REF(basic_channel_logger) that) BOOST_NOEXCEPT_IF(boost::is_nothrow_move_constructible< base_type >::value && boost::is_nothrow_move_constructible< channel_attribute >::value) : base_type(boost::move(static_cast< base_type& >(that))), m_ChannelAttr(boost::move(that.m_ChannelAttr)) { - base_type::attributes()[boost::log::aux::default_attribute_names::channel()] = m_ChannelAttr; } /*! * Constructor with arguments. Allows to register a channel name attribute on construction. diff --git a/include/boost/log/sources/exception_handler_feature.hpp b/include/boost/log/sources/exception_handler_feature.hpp index d29041109d..b2b1acde56 100644 --- a/include/boost/log/sources/exception_handler_feature.hpp +++ b/include/boost/log/sources/exception_handler_feature.hpp @@ -15,19 +15,17 @@ #ifndef BOOST_LOG_SOURCES_EXCEPTION_HANDLER_FEATURE_HPP_INCLUDED_ #define BOOST_LOG_SOURCES_EXCEPTION_HANDLER_FEATURE_HPP_INCLUDED_ -#include #include #include #include +#include +#include #include #include #include #include #include #include -#if !defined(BOOST_LOG_NO_THREADS) -#include -#endif #include #ifdef BOOST_HAS_PRAGMA_ONCE @@ -77,7 +75,7 @@ class basic_exception_handler_logger : typedef typename strictest_lock< typename base_type::swap_lock, #ifndef BOOST_LOG_NO_THREADS - boost::log::aux::exclusive_lock_guard< threading_model > + boost::log::aux::multiple_unique_lock2< threading_model, threading_model > #else no_lock< threading_model > #endif // !defined(BOOST_LOG_NO_THREADS) @@ -105,7 +103,7 @@ class basic_exception_handler_logger : /*! * Move constructor */ - basic_exception_handler_logger(BOOST_RV_REF(basic_exception_handler_logger) that) : + basic_exception_handler_logger(BOOST_RV_REF(basic_exception_handler_logger) that) BOOST_NOEXCEPT_IF(boost::is_nothrow_move_constructible< base_type >::value && boost::is_nothrow_move_constructible< exception_handler_type >::value) : base_type(boost::move(static_cast< base_type& >(that))), m_ExceptionHandler(boost::move(that.m_ExceptionHandler)) { @@ -154,12 +152,6 @@ class basic_exception_handler_logger : { return base_type::open_record_unlocked(args); } -#ifndef BOOST_LOG_NO_THREADS - catch (thread_interrupted&) - { - throw; - } -#endif catch (...) { handle_exception(); @@ -176,12 +168,6 @@ class basic_exception_handler_logger : { base_type::push_record_unlocked(boost::move(rec)); } -#ifndef BOOST_LOG_NO_THREADS - catch (thread_interrupted&) - { - throw; - } -#endif catch (...) { handle_exception(); @@ -210,8 +196,8 @@ class basic_exception_handler_logger : // we shall acquire a read lock here, when an exception is caught. // If other features do require locking, the thread model is // already locked by now, and we don't do locking at all. - typedef typename mpl::if_< - is_same< no_lock< threading_model >, typename final_type::push_record_lock >, + typedef typename boost::conditional< + is_same< no_lock< threading_model >, typename final_type::push_record_lock >::value, boost::log::aux::shared_lock_guard< threading_model >, no_lock< threading_model > >::type lock_type; diff --git a/include/boost/log/sources/record_ostream.hpp b/include/boost/log/sources/record_ostream.hpp index 87274a2774..776315ae1f 100644 --- a/include/boost/log/sources/record_ostream.hpp +++ b/include/boost/log/sources/record_ostream.hpp @@ -51,17 +51,25 @@ namespace aux { template< typename StreamT, typename T, bool ByValueV, typename R > struct enable_record_ostream_generic_operator {}; + template< typename CharT, typename T, typename R > struct enable_record_ostream_generic_operator< basic_record_ostream< CharT >, T, false, R > : public boost::disable_if_c< boost::is_scalar< typename boost::remove_cv< T >::type >::value, R > { }; + template< typename CharT, typename T, typename R > struct enable_record_ostream_generic_operator< basic_record_ostream< CharT >, T, true, R > : public boost::enable_if_c< boost::is_enum< typename boost::remove_cv< T >::type >::value, R > { }; +template< typename CharT, typename T, typename R > +struct enable_record_ostream_generic_operator< basic_record_ostream< CharT >, T*, true, R > : + public disable_if_streamable_char_type< typename boost::remove_cv< T >::type, R > +{ +}; + } // namespace aux /*! @@ -326,12 +334,6 @@ class basic_record_ostream : return *this; } - basic_record_ostream& operator<< (const void* value) - { - static_cast< base_type& >(*this) << value; - return *this; - } - basic_record_ostream& operator<< (std::basic_streambuf< char_type, traits_type >* buf) { static_cast< base_type& >(*this) << buf; diff --git a/include/boost/log/sources/severity_feature.hpp b/include/boost/log/sources/severity_feature.hpp index 101769bda1..a7db0d92c9 100644 --- a/include/boost/log/sources/severity_feature.hpp +++ b/include/boost/log/sources/severity_feature.hpp @@ -16,10 +16,11 @@ #define BOOST_LOG_SOURCES_SEVERITY_FEATURE_HPP_INCLUDED_ #include -#include +#include #include #include #include +#include #include #include #include @@ -58,7 +59,7 @@ namespace aux { public: //! Stored level type typedef LevelT value_type; - BOOST_STATIC_ASSERT_MSG(sizeof(value_type) <= sizeof(uintmax_t), "Boost.Log: Unsupported severity level type, the severity level must fit into uintmax_t"); + static_assert(sizeof(value_type) <= sizeof(uintmax_t), "Boost.Log: Unsupported severity level type, the severity level must fit into uintmax_t"); protected: //! Factory implementation @@ -67,7 +68,7 @@ namespace aux { { public: //! The method dispatches the value to the given object - bool dispatch(type_dispatcher& dispatcher) + bool dispatch(type_dispatcher& dispatcher) BOOST_OVERRIDE { type_dispatcher::callback< value_type > callback = dispatcher.get_callback< value_type >(); if (callback) @@ -80,7 +81,7 @@ namespace aux { } //! The method is called when the attribute value is passed to another thread - intrusive_ptr< attribute_value::impl > detach_from_thread() + intrusive_ptr< attribute_value::impl > detach_from_thread() BOOST_OVERRIDE { #if !defined(BOOST_LOG_NO_THREADS) return new attributes::attribute_value_impl< value_type >( @@ -98,11 +99,11 @@ namespace aux { { } //! Copy constructor - severity_level(severity_level const& that) : attribute(static_cast< attribute const& >(that)) + severity_level(severity_level const& that) BOOST_NOEXCEPT : attribute(static_cast< attribute const& >(that)) { } //! Move constructor - severity_level(BOOST_RV_REF(severity_level) that) : attribute(boost::move(static_cast< attribute& >(that))) + severity_level(BOOST_RV_REF(severity_level) that) BOOST_NOEXCEPT : attribute(boost::move(static_cast< attribute& >(that))) { } //! Constructor for casting support @@ -114,16 +115,16 @@ namespace aux { /*! * Copy assignment */ - severity_level& operator= (BOOST_COPY_ASSIGN_REF(severity_level) that) + severity_level& operator= (BOOST_COPY_ASSIGN_REF(severity_level) that) BOOST_NOEXCEPT { - attribute::operator= (that); + attribute::operator= (static_cast< attribute const& >(that)); return *this; } /*! * Move assignment */ - severity_level& operator= (BOOST_RV_REF(severity_level) that) + severity_level& operator= (BOOST_RV_REF(severity_level) that) BOOST_NOEXCEPT { this->swap(that); return *this; @@ -175,7 +176,7 @@ class basic_severity_logger : typedef typename strictest_lock< typename base_type::swap_lock, #ifndef BOOST_LOG_NO_THREADS - boost::log::aux::exclusive_lock_guard< threading_model > + boost::log::aux::multiple_unique_lock2< threading_model, threading_model > #else no_lock< threading_model > #endif // !defined(BOOST_LOG_NO_THREADS) @@ -206,17 +207,19 @@ class basic_severity_logger : m_DefaultSeverity(that.m_DefaultSeverity), m_SeverityAttr(that.m_SeverityAttr) { + // Our attributes must refer to our severity attribute base_type::attributes()[boost::log::aux::default_attribute_names::severity()] = m_SeverityAttr; } /*! * Move constructor */ - basic_severity_logger(BOOST_RV_REF(basic_severity_logger) that) : + basic_severity_logger(BOOST_RV_REF(basic_severity_logger) that) BOOST_NOEXCEPT_IF(boost::is_nothrow_move_constructible< base_type >::value && + boost::is_nothrow_move_constructible< severity_level >::value && + boost::is_nothrow_move_constructible< severity_attribute >::value) : base_type(boost::move(static_cast< base_type& >(that))), m_DefaultSeverity(boost::move(that.m_DefaultSeverity)), m_SeverityAttr(boost::move(that.m_SeverityAttr)) { - base_type::attributes()[boost::log::aux::default_attribute_names::severity()] = m_SeverityAttr; } /*! * Constructor with named arguments. Allows to setup the default level for log records. @@ -257,9 +260,7 @@ class basic_severity_logger : void swap_unlocked(basic_severity_logger& that) { base_type::swap_unlocked(static_cast< base_type& >(that)); - severity_level t = m_DefaultSeverity; - m_DefaultSeverity = that.m_DefaultSeverity; - that.m_DefaultSeverity = t; + boost::core::invoke_swap(m_DefaultSeverity, that.m_DefaultSeverity); m_SeverityAttr.swap(that.m_SeverityAttr); } }; diff --git a/include/boost/log/sources/threading_models.hpp b/include/boost/log/sources/threading_models.hpp index 5fe6d1a6e4..20920eec23 100644 --- a/include/boost/log/sources/threading_models.hpp +++ b/include/boost/log/sources/threading_models.hpp @@ -17,10 +17,11 @@ #ifndef BOOST_LOG_SOURCES_THREADING_MODELS_HPP_INCLUDED_ #define BOOST_LOG_SOURCES_THREADING_MODELS_HPP_INCLUDED_ +#include #include #include // is_mutex_type #if !defined(BOOST_LOG_NO_THREADS) -#include +#include #endif #include @@ -38,66 +39,79 @@ namespace sources { struct single_thread_model { // We provide methods for the most advanced locking concept: UpgradeLockable - void lock_shared() const {} - bool try_lock_shared() const { return true; } + void lock_shared() const BOOST_NOEXCEPT {} + bool try_lock_shared() const BOOST_NOEXCEPT { return true; } template< typename TimeT > - bool timed_lock_shared(TimeT const&) const { return true; } - void unlock_shared() const {} - void lock() const {} - bool try_lock() const { return true; } + bool timed_lock_shared(TimeT const&) const BOOST_NOEXCEPT { return true; } + void unlock_shared() const BOOST_NOEXCEPT {} + void lock() const BOOST_NOEXCEPT {} + bool try_lock() const BOOST_NOEXCEPT { return true; } template< typename TimeT > - bool timed_lock(TimeT const&) const { return true; } - void unlock() const {} - void lock_upgrade() const {} - bool try_lock_upgrade() const { return true; } + bool timed_lock(TimeT const&) const BOOST_NOEXCEPT { return true; } + void unlock() const BOOST_NOEXCEPT {} + void lock_upgrade() const BOOST_NOEXCEPT {} + bool try_lock_upgrade() const BOOST_NOEXCEPT { return true; } template< typename TimeT > - bool timed_lock_upgrade(TimeT const&) const { return true; } - void unlock_upgrade() const {} - void unlock_upgrade_and_lock() const {} - void unlock_and_lock_upgrade() const {} - void unlock_and_lock_shared() const {} - void unlock_upgrade_and_lock_shared() const {} - - void swap(single_thread_model&) {} + bool timed_lock_upgrade(TimeT const&) const BOOST_NOEXCEPT { return true; } + void unlock_upgrade() const BOOST_NOEXCEPT {} + void unlock_upgrade_and_lock() const BOOST_NOEXCEPT {} + void unlock_and_lock_upgrade() const BOOST_NOEXCEPT {} + void unlock_and_lock_shared() const BOOST_NOEXCEPT {} + void unlock_upgrade_and_lock_shared() const BOOST_NOEXCEPT {} + + void swap(single_thread_model&) BOOST_NOEXCEPT {} }; +inline void swap(single_thread_model&, single_thread_model&) BOOST_NOEXCEPT +{ +} + #if !defined(BOOST_LOG_NO_THREADS) //! Multi-thread locking model with maximum locking capabilities template< typename MutexT > struct multi_thread_model { - multi_thread_model() {} - multi_thread_model(multi_thread_model const&) {} - multi_thread_model& operator= (multi_thread_model const&) { return *this; } + multi_thread_model() BOOST_NOEXCEPT_IF(boost::has_nothrow_constructor< MutexT >::value) {} + multi_thread_model(multi_thread_model const&) BOOST_NOEXCEPT_IF(boost::has_nothrow_constructor< MutexT >::value) {} + multi_thread_model& operator= (multi_thread_model const&) BOOST_NOEXCEPT { return *this; } +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + multi_thread_model(multi_thread_model&&) BOOST_NOEXCEPT_IF(boost::has_nothrow_constructor< MutexT >::value) {} + multi_thread_model& operator= (multi_thread_model&&) BOOST_NOEXCEPT { return *this; } +#endif void lock_shared() const { m_Mutex.lock_shared(); } bool try_lock_shared() const { return m_Mutex.try_lock_shared(); } template< typename TimeT > bool timed_lock_shared(TimeT const& t) const { return m_Mutex.timed_lock_shared(t); } - void unlock_shared() const { m_Mutex.unlock_shared(); } + void unlock_shared() const BOOST_NOEXCEPT { m_Mutex.unlock_shared(); } void lock() const { m_Mutex.lock(); } bool try_lock() const { return m_Mutex.try_lock(); } template< typename TimeT > bool timed_lock(TimeT const& t) const { return m_Mutex.timed_lock(t); } - void unlock() const { m_Mutex.unlock(); } + void unlock() const BOOST_NOEXCEPT { m_Mutex.unlock(); } void lock_upgrade() const { m_Mutex.lock_upgrade(); } bool try_lock_upgrade() const { return m_Mutex.try_lock_upgrade(); } template< typename TimeT > bool timed_lock_upgrade(TimeT const& t) const { return m_Mutex.timed_lock_upgrade(t); } - void unlock_upgrade() const { m_Mutex.unlock_upgrade(); } + void unlock_upgrade() const BOOST_NOEXCEPT { m_Mutex.unlock_upgrade(); } void unlock_upgrade_and_lock() const { m_Mutex.unlock_upgrade_and_lock(); } void unlock_and_lock_upgrade() const { m_Mutex.unlock_and_lock_upgrade(); } void unlock_and_lock_shared() const { m_Mutex.unlock_and_lock_shared(); } void unlock_upgrade_and_lock_shared() const { m_Mutex.unlock_upgrade_and_lock_shared(); } - void swap(multi_thread_model&) {} + void swap(multi_thread_model&) BOOST_NOEXCEPT {} private: //! Synchronization primitive mutable MutexT m_Mutex; }; +template< typename MutexT > +inline void swap(multi_thread_model< MutexT >&, multi_thread_model< MutexT >&) BOOST_NOEXCEPT +{ +} + #endif // !defined(BOOST_LOG_NO_THREADS) } // namespace sources @@ -107,12 +121,12 @@ BOOST_LOG_CLOSE_NAMESPACE // namespace log #if !defined(BOOST_LOG_NO_THREADS) && !defined(BOOST_LOG_DOXYGEN_PASS) template< > -struct is_mutex_type< boost::log::sources::single_thread_model > : mpl::true_ +struct is_mutex_type< boost::log::sources::single_thread_model > : boost::true_type { }; template< typename T > -struct is_mutex_type< boost::log::sources::multi_thread_model< T > > : mpl::true_ +struct is_mutex_type< boost::log::sources::multi_thread_model< T > > : boost::true_type { }; diff --git a/include/boost/log/trivial.hpp b/include/boost/log/trivial.hpp index ece0c900e6..50df36d663 100644 --- a/include/boost/log/trivial.hpp +++ b/include/boost/log/trivial.hpp @@ -15,6 +15,7 @@ #ifndef BOOST_LOG_TRIVIAL_HPP_INCLUDED_ #define BOOST_LOG_TRIVIAL_HPP_INCLUDED_ +#include #include #include #include @@ -49,15 +50,26 @@ enum severity_level }; //! Returns stringized enumeration value or \c NULL, if the value is not valid -BOOST_LOG_API const char* to_string(severity_level lvl); +template< typename CharT > +BOOST_LOG_API const CharT* to_string(severity_level lvl); + +//! Returns stringized enumeration value or \c NULL, if the value is not valid +inline const char* to_string(severity_level lvl) +{ + return boost::log::trivial::to_string< char >(lvl); +} + +//! Parses enumeration value from string and returns \c true on success and \c false otherwise +template< typename CharT > +BOOST_LOG_API bool from_string(const CharT* str, std::size_t len, severity_level& lvl); //! Outputs stringized representation of the severity level to the stream template< typename CharT, typename TraitsT > inline std::basic_ostream< CharT, TraitsT >& operator<< ( std::basic_ostream< CharT, TraitsT >& strm, severity_level lvl) { - const char* str = boost::log::trivial::to_string(lvl); - if (str) + const CharT* str = boost::log::trivial::to_string< CharT >(lvl); + if (BOOST_LIKELY(!!str)) strm << str; else strm << static_cast< int >(lvl); @@ -79,7 +91,7 @@ typedef sources::severity_logger< severity_level > logger_type; /*! * \brief Trivial logger tag * - * This tag can be used to acquire the logger that is used with lrivial logging macros. + * This tag can be used to acquire the logger that is used with trivial logging macros. * This may be useful when the logger is used with other macros which require a logger. */ struct logger diff --git a/include/boost/log/utility/exception_handler.hpp b/include/boost/log/utility/exception_handler.hpp index 9b8551ac0d..5206fef8b2 100644 --- a/include/boost/log/utility/exception_handler.hpp +++ b/include/boost/log/utility/exception_handler.hpp @@ -248,26 +248,26 @@ inline typename boost::lazy_enable_if_c< } #define BOOST_LOG_MAKE_EXCEPTION_HANDLER_INTERNAL(z, n, data)\ - template< BOOST_PP_ENUM_PARAMS(n, typename T), typename HandlerT >\ + template< BOOST_PP_ENUM_PARAMS_Z(z, n, typename T), typename HandlerT >\ inline exception_handler<\ - BOOST_PP_CAT(mpl::vector, n)< BOOST_PP_ENUM_PARAMS(n, T) >,\ + BOOST_PP_CAT(mpl::vector, n)< BOOST_PP_ENUM_PARAMS_Z(z, n, T) >,\ HandlerT\ > make_exception_handler(HandlerT const& handler)\ {\ typedef exception_handler<\ - BOOST_PP_CAT(mpl::vector, n)< BOOST_PP_ENUM_PARAMS(n, T) >,\ + BOOST_PP_CAT(mpl::vector, n)< BOOST_PP_ENUM_PARAMS_Z(z, n, T) >,\ HandlerT\ > eh_t;\ return eh_t(handler);\ }\ - template< BOOST_PP_ENUM_PARAMS(n, typename T), typename HandlerT >\ + template< BOOST_PP_ENUM_PARAMS_Z(z, n, typename T), typename HandlerT >\ inline nothrow_exception_handler<\ - BOOST_PP_CAT(mpl::vector, n)< BOOST_PP_ENUM_PARAMS(n, T) >,\ + BOOST_PP_CAT(mpl::vector, n)< BOOST_PP_ENUM_PARAMS_Z(z, n, T) >,\ HandlerT\ > make_exception_handler(HandlerT const& handler, std::nothrow_t const&)\ {\ typedef nothrow_exception_handler<\ - BOOST_PP_CAT(mpl::vector, n)< BOOST_PP_ENUM_PARAMS(n, T) >,\ + BOOST_PP_CAT(mpl::vector, n)< BOOST_PP_ENUM_PARAMS_Z(z, n, T) >,\ HandlerT\ > eh_t;\ return eh_t(handler);\ diff --git a/include/boost/log/utility/formatting_ostream.hpp b/include/boost/log/utility/formatting_ostream.hpp index b39feeb664..e2f83aab0c 100644 --- a/include/boost/log/utility/formatting_ostream.hpp +++ b/include/boost/log/utility/formatting_ostream.hpp @@ -27,6 +27,9 @@ #include #include #include +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) +#include +#endif #include #include #include @@ -45,34 +48,52 @@ namespace aux { template< typename T, typename R > struct enable_if_streamable_char_type {}; +template< typename T, typename R > +struct disable_if_streamable_char_type { typedef R type; }; template< typename R > struct enable_if_streamable_char_type< char, R > { typedef R type; }; template< typename R > +struct disable_if_streamable_char_type< char, R > {}; +template< typename R > struct enable_if_streamable_char_type< wchar_t, R > { typedef R type; }; +template< typename R > +struct disable_if_streamable_char_type< wchar_t, R > {}; #if !defined(BOOST_LOG_NO_CXX11_CODECVT_FACETS) #if !defined(BOOST_NO_CXX11_CHAR16_T) template< typename R > struct enable_if_streamable_char_type< char16_t, R > { typedef R type; }; +template< typename R > +struct disable_if_streamable_char_type< char16_t, R > {}; #endif #if !defined(BOOST_NO_CXX11_CHAR32_T) template< typename R > struct enable_if_streamable_char_type< char32_t, R > { typedef R type; }; +template< typename R > +struct disable_if_streamable_char_type< char32_t, R > {}; #endif #endif template< typename StreamT, typename T, bool ByValueV, typename R > struct enable_formatting_ostream_generic_operator {}; + template< typename CharT, typename TraitsT, typename AllocatorT, typename T, typename R > struct enable_formatting_ostream_generic_operator< basic_formatting_ostream< CharT, TraitsT, AllocatorT >, T, false, R > : public boost::disable_if_c< boost::is_scalar< typename boost::remove_cv< T >::type >::value, R > { }; + template< typename CharT, typename TraitsT, typename AllocatorT, typename T, typename R > struct enable_formatting_ostream_generic_operator< basic_formatting_ostream< CharT, TraitsT, AllocatorT >, T, true, R > : public boost::enable_if_c< boost::is_enum< typename boost::remove_cv< T >::type >::value, R > { }; +template< typename CharT, typename TraitsT, typename AllocatorT, typename T, typename R > +struct enable_formatting_ostream_generic_operator< basic_formatting_ostream< CharT, TraitsT, AllocatorT >, T*, true, R > : + public disable_if_streamable_char_type< typename boost::remove_cv< T >::type, R > +{ +}; + } // namespace aux /*! @@ -528,12 +549,6 @@ class basic_formatting_ostream return *this; } - basic_formatting_ostream& operator<< (const void* value) - { - m_stream << value; - return *this; - } - basic_formatting_ostream& operator<< (std::basic_streambuf< char_type, traits_type >* buf) { m_stream << buf; @@ -547,6 +562,15 @@ class basic_formatting_ostream return strm.formatted_write(str.data(), static_cast< std::streamsize >(str.size())); } +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) + template< typename OtherCharT, typename OtherTraitsT > + friend typename aux::enable_if_streamable_char_type< OtherCharT, basic_formatting_ostream& >::type + operator<< (basic_formatting_ostream& strm, std::basic_string_view< OtherCharT, OtherTraitsT > const& str) + { + return strm.formatted_write(str.data(), static_cast< std::streamsize >(str.size())); + } +#endif + template< typename OtherCharT, typename OtherTraitsT > friend typename aux::enable_if_streamable_char_type< OtherCharT, basic_formatting_ostream& >::type operator<< (basic_formatting_ostream& strm, basic_string_literal< OtherCharT, OtherTraitsT > const& str) @@ -576,6 +600,15 @@ class basic_formatting_ostream return strm.formatted_write(str.data(), static_cast< std::streamsize >(str.size())); } +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) + template< typename OtherCharT, typename OtherTraitsT > + friend typename aux::enable_if_streamable_char_type< OtherCharT, basic_formatting_ostream& >::type + operator<< (basic_formatting_ostream& strm, std::basic_string_view< OtherCharT, OtherTraitsT >& str) + { + return strm.formatted_write(str.data(), static_cast< std::streamsize >(str.size())); + } +#endif + template< typename OtherCharT, typename OtherTraitsT > friend typename aux::enable_if_streamable_char_type< OtherCharT, basic_formatting_ostream& >::type operator<< (basic_formatting_ostream& strm, basic_string_literal< OtherCharT, OtherTraitsT >& str) @@ -606,6 +639,15 @@ class basic_formatting_ostream return strm.formatted_write(str.data(), static_cast< std::streamsize >(str.size())); } +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) + template< typename OtherCharT, typename OtherTraitsT > + friend typename aux::enable_if_streamable_char_type< OtherCharT, basic_formatting_ostream& >::type + operator<< (basic_formatting_ostream&& strm, std::basic_string_view< OtherCharT, OtherTraitsT > const& str) + { + return strm.formatted_write(str.data(), static_cast< std::streamsize >(str.size())); + } +#endif + template< typename OtherCharT, typename OtherTraitsT > friend typename aux::enable_if_streamable_char_type< OtherCharT, basic_formatting_ostream& >::type operator<< (basic_formatting_ostream&& strm, basic_string_literal< OtherCharT, OtherTraitsT > const& str) @@ -635,6 +677,15 @@ class basic_formatting_ostream return strm.formatted_write(str.data(), static_cast< std::streamsize >(str.size())); } +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) + template< typename OtherCharT, typename OtherTraitsT > + friend typename aux::enable_if_streamable_char_type< OtherCharT, basic_formatting_ostream& >::type + operator<< (basic_formatting_ostream&& strm, std::basic_string_view< OtherCharT, OtherTraitsT >& str) + { + return strm.formatted_write(str.data(), static_cast< std::streamsize >(str.size())); + } +#endif + template< typename OtherCharT, typename OtherTraitsT > friend typename aux::enable_if_streamable_char_type< OtherCharT, basic_formatting_ostream& >::type operator<< (basic_formatting_ostream&& strm, basic_string_literal< OtherCharT, OtherTraitsT >& str) @@ -656,7 +707,7 @@ class basic_formatting_ostream { return strm.formatted_write(str.data(), static_cast< std::streamsize >(str.size())); } -#endif +#endif // !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) protected: void init_stream() diff --git a/include/boost/log/utility/functional/in_range.hpp b/include/boost/log/utility/functional/in_range.hpp index b87160ef03..90d3b3344d 100644 --- a/include/boost/log/utility/functional/in_range.hpp +++ b/include/boost/log/utility/functional/in_range.hpp @@ -16,6 +16,8 @@ #define BOOST_LOG_UTILITY_FUNCTIONAL_IN_RANGE_HPP_INCLUDED_ #include +#include +#include #include #include // make_common_integral_type #include @@ -36,17 +38,17 @@ struct in_range_fun template< typename T, typename U > bool operator() (T const& value, std::pair< U, U > const& rng) const { - return op(value, rng, typename mpl::and_< is_integral< T >, is_integral< U > >::type()); + return op(value, rng, integral_constant< bool, is_integral< T >::value && is_integral< U >::value >()); } private: template< typename T, typename U > - static bool op(T const& value, std::pair< U, U > const& rng, mpl::false_ const&) + static bool op(T const& value, std::pair< U, U > const& rng, false_type) { return (value >= rng.first && value < rng.second); } template< typename T, typename U > - static bool op(T const& value, std::pair< U, U > const& rng, mpl::true_ const&) + static bool op(T const& value, std::pair< U, U > const& rng, true_type) { typedef typename aux::make_common_integral_type< T, U >::type common_integral_type; return (static_cast< common_integral_type >(value) >= static_cast< common_integral_type >(rng.first)) diff --git a/include/boost/log/utility/functional/logical.hpp b/include/boost/log/utility/functional/logical.hpp index bdba8b8f8d..9481e473a3 100644 --- a/include/boost/log/utility/functional/logical.hpp +++ b/include/boost/log/utility/functional/logical.hpp @@ -21,11 +21,10 @@ #ifndef BOOST_LOG_UTILITY_FUNCTIONAL_LOGICAL_HPP_INCLUDED_ #define BOOST_LOG_UTILITY_FUNCTIONAL_LOGICAL_HPP_INCLUDED_ -#include -#include -#include #include #include +#include +#include #include #include @@ -56,8 +55,8 @@ struct make_common_integral_type< T, U, TSizeV, USizeV, true > //! Specialization for the case when both types have the same size template< typename T, typename U, unsigned int SizeV > struct make_common_integral_type< T, U, SizeV, SizeV, false > : - public mpl::if_< - is_unsigned< T >, + public boost::conditional< + is_unsigned< T >::value, T, U > @@ -74,17 +73,17 @@ struct equal_to template< typename T, typename U > bool operator() (T const& left, U const& right) const { - return op(left, right, typename mpl::and_< is_integral< T >, is_integral< U > >::type()); + return op(left, right, integral_constant< bool, is_integral< T >::value && is_integral< U >::value >()); } private: template< typename T, typename U > - static bool op(T const& left, U const& right, mpl::false_ const&) + static bool op(T const& left, U const& right, false_type) { return (left == right); } template< typename T, typename U > - static bool op(T const& left, U const& right, mpl::true_ const&) + static bool op(T const& left, U const& right, true_type) { typedef typename aux::make_common_integral_type< T, U >::type common_integral_type; return static_cast< common_integral_type >(left) == static_cast< common_integral_type >(right); @@ -99,17 +98,17 @@ struct not_equal_to template< typename T, typename U > bool operator() (T const& left, U const& right) const { - return op(left, right, typename mpl::and_< is_integral< T >, is_integral< U > >::type()); + return op(left, right, integral_constant< bool, is_integral< T >::value && is_integral< U >::value >()); } private: template< typename T, typename U > - static bool op(T const& left, U const& right, mpl::false_ const&) + static bool op(T const& left, U const& right, false_type) { return (left != right); } template< typename T, typename U > - static bool op(T const& left, U const& right, mpl::true_ const&) + static bool op(T const& left, U const& right, true_type) { typedef typename aux::make_common_integral_type< T, U >::type common_integral_type; return static_cast< common_integral_type >(left) != static_cast< common_integral_type >(right); @@ -124,17 +123,17 @@ struct less template< typename T, typename U > bool operator() (T const& left, U const& right) const { - return op(left, right, typename mpl::and_< is_integral< T >, is_integral< U > >::type()); + return op(left, right, integral_constant< bool, is_integral< T >::value && is_integral< U >::value >()); } private: template< typename T, typename U > - static bool op(T const& left, U const& right, mpl::false_ const&) + static bool op(T const& left, U const& right, false_type) { return (left < right); } template< typename T, typename U > - static bool op(T const& left, U const& right, mpl::true_ const&) + static bool op(T const& left, U const& right, true_type) { typedef typename aux::make_common_integral_type< T, U >::type common_integral_type; return static_cast< common_integral_type >(left) < static_cast< common_integral_type >(right); @@ -149,17 +148,17 @@ struct greater template< typename T, typename U > bool operator() (T const& left, U const& right) const { - return op(left, right, typename mpl::and_< is_integral< T >, is_integral< U > >::type()); + return op(left, right, integral_constant< bool, is_integral< T >::value && is_integral< U >::value >()); } private: template< typename T, typename U > - static bool op(T const& left, U const& right, mpl::false_ const&) + static bool op(T const& left, U const& right, false_type) { return (left > right); } template< typename T, typename U > - static bool op(T const& left, U const& right, mpl::true_ const&) + static bool op(T const& left, U const& right, true_type) { typedef typename aux::make_common_integral_type< T, U >::type common_integral_type; return static_cast< common_integral_type >(left) > static_cast< common_integral_type >(right); @@ -174,17 +173,17 @@ struct less_equal template< typename T, typename U > bool operator() (T const& left, U const& right) const { - return op(left, right, typename mpl::and_< is_integral< T >, is_integral< U > >::type()); + return op(left, right, integral_constant< bool, is_integral< T >::value && is_integral< U >::value >()); } private: template< typename T, typename U > - static bool op(T const& left, U const& right, mpl::false_ const&) + static bool op(T const& left, U const& right, false_type) { return (left <= right); } template< typename T, typename U > - static bool op(T const& left, U const& right, mpl::true_ const&) + static bool op(T const& left, U const& right, true_type) { typedef typename aux::make_common_integral_type< T, U >::type common_integral_type; return static_cast< common_integral_type >(left) <= static_cast< common_integral_type >(right); @@ -199,17 +198,17 @@ struct greater_equal template< typename T, typename U > bool operator() (T const& left, U const& right) const { - return op(left, right, typename mpl::and_< is_integral< T >, is_integral< U > >::type()); + return op(left, right, integral_constant< bool, is_integral< T >::value && is_integral< U >::value >()); } private: template< typename T, typename U > - static bool op(T const& left, U const& right, mpl::false_ const&) + static bool op(T const& left, U const& right, false_type) { return (left >= right); } template< typename T, typename U > - static bool op(T const& left, U const& right, mpl::true_ const&) + static bool op(T const& left, U const& right, true_type) { typedef typename aux::make_common_integral_type< T, U >::type common_integral_type; return static_cast< common_integral_type >(left) >= static_cast< common_integral_type >(right); diff --git a/include/boost/log/utility/ipc/reliable_message_queue.hpp b/include/boost/log/utility/ipc/reliable_message_queue.hpp index 5159d72ef6..ac8cb5077f 100644 --- a/include/boost/log/utility/ipc/reliable_message_queue.hpp +++ b/include/boost/log/utility/ipc/reliable_message_queue.hpp @@ -53,6 +53,10 @@ template< typename R > struct enable_if_byte< signed char, R > { typedef R type; }; template< typename R > struct enable_if_byte< unsigned char, R > { typedef R type; }; +#if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603 +template< typename R > +struct enable_if_byte< std::byte, R > { typedef R type; }; +#endif } // namespace aux diff --git a/include/boost/log/utility/manipulators.hpp b/include/boost/log/utility/manipulators.hpp index cb39fd2416..75ea8fc91c 100644 --- a/include/boost/log/utility/manipulators.hpp +++ b/include/boost/log/utility/manipulators.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #ifdef BOOST_HAS_PRAGMA_ONCE #pragma once diff --git a/include/boost/log/utility/manipulators/add_value.hpp b/include/boost/log/utility/manipulators/add_value.hpp index 270cedf65e..6daf3b4588 100644 --- a/include/boost/log/utility/manipulators/add_value.hpp +++ b/include/boost/log/utility/manipulators/add_value.hpp @@ -15,10 +15,10 @@ #ifndef BOOST_LOG_UTILITY_MANIPULATORS_ADD_VALUE_HPP_INCLUDED_ #define BOOST_LOG_UTILITY_MANIPULATORS_ADD_VALUE_HPP_INCLUDED_ -#include #include #include #include +#include #include #include #include @@ -61,14 +61,14 @@ class add_value_manip // To work around this problem we save the value inside the manipulator in this case. typedef typename remove_reference< reference_type >::type& lvalue_reference_type; - typedef typename mpl::if_< - is_scalar< value_type >, + typedef typename conditional< + is_scalar< value_type >::value, value_type, lvalue_reference_type >::type stored_type; - typedef typename mpl::if_< - is_scalar< value_type >, + typedef typename conditional< + is_scalar< value_type >::value, value_type, reference_type >::type get_value_result_type; diff --git a/include/boost/log/utility/manipulators/auto_newline.hpp b/include/boost/log/utility/manipulators/auto_newline.hpp new file mode 100644 index 0000000000..d973b92256 --- /dev/null +++ b/include/boost/log/utility/manipulators/auto_newline.hpp @@ -0,0 +1,66 @@ +/* + * Copyright Andrey Semashev 2019. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * https://www.boost.org/LICENSE_1_0.txt) + */ +/*! + * \file utility/manipulators/auto_newline.hpp + * \author Andrey Semashev + * \date 23.06.2019 + * + * The header contains implementation of a stream manipulator for inserting a newline, unless there is already one inserted. + */ + +#ifndef BOOST_LOG_UTILITY_MANIPULATORS_AUTO_NEWLINE_HPP_INCLUDED_ +#define BOOST_LOG_UTILITY_MANIPULATORS_AUTO_NEWLINE_HPP_INCLUDED_ + +#include +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +/*! + * Stream manipulator for inserting a newline character, unless the last character + * inserted into the stream is already a newline. + */ +struct auto_newline_manip {} +BOOST_INLINE_VARIABLE const auto_newline = {}; + +/*! + * Stream output operator for the \c auto_newline manipulator + */ +template< typename CharT, typename TraitsT, typename AllocatorT > +inline basic_formatting_ostream< CharT, TraitsT, AllocatorT >& operator<< (basic_formatting_ostream< CharT, TraitsT, AllocatorT >& strm, auto_newline_manip) +{ + typedef basic_formatting_ostream< CharT, TraitsT, AllocatorT > stream_type; + typedef typename stream_type::char_type char_type; + typedef typename stream_type::string_type string_type; + if (BOOST_LIKELY(strm.good())) + { + string_type* str = strm.rdbuf()->storage(); + if (BOOST_LIKELY(!!str)) + { + strm.rdbuf()->pubsync(); + if (str->empty() || *str->rbegin() != static_cast< char_type >('\n')) + strm.rdbuf()->push_back(static_cast< char_type >('\n')); + } + } + + return strm; +} + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include + +#endif // BOOST_LOG_UTILITY_MANIPULATORS_AUTO_NEWLINE_HPP_INCLUDED_ diff --git a/include/boost/log/utility/manipulators/dump.hpp b/include/boost/log/utility/manipulators/dump.hpp index 3341376f39..2c55e5c004 100644 --- a/include/boost/log/utility/manipulators/dump.hpp +++ b/include/boost/log/utility/manipulators/dump.hpp @@ -128,7 +128,7 @@ class dump_manip template< typename CharT, typename TraitsT > inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, dump_manip const& manip) { - if (strm.good()) + if (BOOST_LIKELY(strm.good())) aux::dump_data(manip.get_data(), manip.get_size(), strm); return strm; @@ -155,7 +155,7 @@ class bounded_dump_manip : template< typename CharT, typename TraitsT > inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, bounded_dump_manip const& manip) { - if (strm.good()) + if (BOOST_LIKELY(strm.good())) { const std::size_t size = manip.get_size(), max_size = manip.get_max_size(); if (max_size >= size) diff --git a/include/boost/log/utility/manipulators/invoke.hpp b/include/boost/log/utility/manipulators/invoke.hpp new file mode 100644 index 0000000000..9907746718 --- /dev/null +++ b/include/boost/log/utility/manipulators/invoke.hpp @@ -0,0 +1,160 @@ +/* + * Copyright Andrey Semashev 2022. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * https://www.boost.org/LICENSE_1_0.txt) + */ +/*! + * \file utility/manipulators/invoke.hpp + * \author Andrey Semashev + * \date 27.02.2022 + * + * The header contains implementation of a stream manipulator for invoking a user-defined function. + */ + +#ifndef BOOST_LOG_UTILITY_MANIPULATORS_INVOKE_HPP_INCLUDED_ +#define BOOST_LOG_UTILITY_MANIPULATORS_INVOKE_HPP_INCLUDED_ + +#include +#include +#include +#include +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) +#include +#endif +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +/*! + * Stream manipulator for invoking a user-defined function as part of stream output. + */ +template< typename FunctionT > +class invoke_manipulator +{ +private: + mutable FunctionT m_function; + +public: + //! Initializing constructor + explicit invoke_manipulator(FunctionT const& func) : + m_function(func) + { + } + +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + //! Initializing constructor + explicit invoke_manipulator(FunctionT&& func) : + m_function(static_cast< FunctionT&& >(func)) + { + } +#endif + + //! The method invokes the saved function with the output stream + template< typename StreamT > + void output(StreamT& stream) const + { + m_function(stream); + } +}; + +/*! + * Stream output operator for \c invoke_manipulator. Invokes the function saved in the manipulator. + */ +template< typename StreamT, typename FunctionT > +inline typename boost::enable_if_c< log::aux::is_ostream< StreamT >::value, StreamT& >::type operator<< (StreamT& stream, invoke_manipulator< FunctionT > const& manip) +{ + manip.output(stream); + return stream; +} + +#if !defined(BOOST_LOG_DOXYGEN_PASS) + +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + +//! Invoke manipulator generator function +template< typename FunctionT > +inline invoke_manipulator< + typename boost::remove_cv< + typename boost::remove_reference< FunctionT >::type + >::type +> +invoke_manip(FunctionT&& func) +{ + return invoke_manipulator< + typename boost::remove_cv< + typename boost::remove_reference< FunctionT >::type + >::type + >(static_cast< FunctionT&& >(func)); +} + +#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) && \ + !defined(BOOST_NO_CXX14_GENERIC_LAMBDAS) && \ + !defined(BOOST_NO_CXX14_RETURN_TYPE_DEDUCTION) + +//! Invoke manipulator generator function +template< typename FunctionT, typename Arg0, typename... Args > +inline auto invoke_manip(FunctionT&& func, Arg0&& arg0, Args&&... args) +{ + return boost::log::invoke_manip + ( +#if !defined(BOOST_LOG_NO_CXX20_PACK_EXPANSION_IN_LAMBDA_INIT_CAPTURE) + [func = static_cast< FunctionT&& >(func), arg0 = static_cast< Arg0&& >(arg0), ...args = static_cast< Args&& >(args)](auto& stream) mutable +#else + [func, arg0, args...](auto& stream) mutable +#endif + { +#if !defined(BOOST_MSVC) || BOOST_MSVC >= 1910 + static_cast< FunctionT&& >(func)(stream, static_cast< Arg0&& >(arg0), static_cast< Args&& >(args)...); +#else + // MSVC 19.0 (VS 14.0) ICEs if we use perfect forwarding here + func(stream, arg0, args...); +#endif + } + ); +} + +#endif // !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) ... + +#else // !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + +//! Invoke manipulator generator function +template< typename FunctionT > +inline invoke_manipulator< typename boost::remove_cv< FunctionT >::type > +invoke_manip(FunctionT const& func) +{ + return invoke_manipulator< typename boost::remove_cv< FunctionT >::type >(func); +} + +#endif // !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + +#else // !defined(BOOST_LOG_DOXYGEN_PASS) + +/*! + * Invoke manipulator generator function. + * + * \param func User-defined function to invoke on output. The function must be callable with a reference to the output stream as the first argument, followed by \a args. + * \param args Additional arguments to pass to \a func. + * \returns Manipulator to be inserted into the stream. + * + * \note \a args are only supported since C++14. + */ +template< typename FunctionT, typename... Args > +invoke_manipulator< unspecified > invoke_manip(FunctionT&& func, Args&&... args); + +#endif // !defined(BOOST_LOG_DOXYGEN_PASS) + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include + +#endif // BOOST_LOG_UTILITY_MANIPULATORS_INVOKE_HPP_INCLUDED_ diff --git a/include/boost/log/utility/manipulators/optional.hpp b/include/boost/log/utility/manipulators/optional.hpp new file mode 100644 index 0000000000..0f1056b5ef --- /dev/null +++ b/include/boost/log/utility/manipulators/optional.hpp @@ -0,0 +1,267 @@ +/* + * Copyright Andrey Semashev 2020. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * https://www.boost.org/LICENSE_1_0.txt) + */ +/*! + * \file utility/manipulators/optional.hpp + * \author Andrey Semashev + * \date 12.05.2020 + * + * The header contains implementation of a stream manipulator for inserting an optional value. + */ + +#ifndef BOOST_LOG_UTILITY_MANIPULATORS_OPTIONAL_HPP_INCLUDED_ +#define BOOST_LOG_UTILITY_MANIPULATORS_OPTIONAL_HPP_INCLUDED_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +/*! + * Stream manipulator for inserting an optional value. + */ +template< typename OptionalT, typename NoneT > +class optional_manipulator +{ +private: + typedef typename conditional< + is_scalar< OptionalT >::value, + OptionalT, + OptionalT const& + >::type stored_optional_type; + + typedef typename conditional< + is_scalar< NoneT >::value, + NoneT, + NoneT const& + >::type stored_none_type; + +private: + stored_optional_type m_optional; + stored_none_type m_none; + +public: + //! Initializing constructor + optional_manipulator(stored_optional_type opt, stored_none_type none) BOOST_NOEXCEPT : + m_optional(opt), + m_none(none) + { + } + + //! The method outputs the value, if it is present, otherwise outputs the "none" marker + template< typename StreamT > + void output(StreamT& stream) const + { + if (!!m_optional) + stream << *m_optional; + else + stream << m_none; + } +}; + +/*! + * Stream manipulator for inserting an optional value. Specialization for no "none" marker. + */ +template< typename OptionalT > +class optional_manipulator< OptionalT, void > +{ +private: + typedef typename conditional< + is_scalar< OptionalT >::value, + OptionalT, + OptionalT const& + >::type stored_optional_type; + +private: + stored_optional_type m_optional; + +public: + //! Initializing constructor + optional_manipulator(stored_optional_type opt) BOOST_NOEXCEPT : + m_optional(opt) + { + } + + //! The method outputs the value, if it is present + template< typename StreamT > + void output(StreamT& stream) const + { + if (!!m_optional) + stream << *m_optional; + } +}; + +/*! + * Stream output operator for \c optional_manipulator. Outputs the optional value or the "none" marker, if one was specified on manipulator construction. + */ +template< typename StreamT, typename OptionalT, typename NoneT > +inline typename boost::enable_if_c< log::aux::is_ostream< StreamT >::value, StreamT& >::type operator<< (StreamT& strm, optional_manipulator< OptionalT, NoneT > const& manip) +{ + manip.output(strm); + return strm; +} + +/*! + * Optional manipulator generator function. + * + * \param opt Optional value to output. The optional value must support contextual conversion to \c bool and dereferencing, and its dereferencing result must support stream output. + * \param none Marker used to indicate when the value is not present. Optional. If not specified, nothing is output if the value is not present. + * \returns Manipulator to be inserted into the stream. + * + * \note Both \a opt and \a none objects must outlive the created manipulator object. + */ +template< typename OptionalT, typename NoneT > +inline typename boost::enable_if_c< + is_scalar< OptionalT >::value && is_scalar< NoneT >::value, + optional_manipulator< OptionalT, NoneT > +>::type optional_manip(OptionalT opt, NoneT none) BOOST_NOEXCEPT +{ + return optional_manipulator< OptionalT, NoneT >(opt, none); +} + +/*! + * Optional manipulator generator function. + * + * \param opt Optional value to output. The optional value must support contextual conversion to \c bool and dereferencing, and its dereferencing result must support stream output. + * \param none Marker used to indicate when the value is not present. Optional. If not specified, nothing is output if the value is not present. + * \returns Manipulator to be inserted into the stream. + * + * \note Both \a opt and \a none objects must outlive the created manipulator object. + */ +template< typename OptionalT, typename NoneT > +inline typename boost::enable_if_c< + is_scalar< OptionalT >::value && !is_scalar< NoneT >::value, + optional_manipulator< OptionalT, NoneT > +>::type optional_manip(OptionalT opt, NoneT const& none) BOOST_NOEXCEPT +{ + return optional_manipulator< OptionalT, NoneT >(opt, none); +} + +/*! + * Optional manipulator generator function. + * + * \param opt Optional value to output. The optional value must support contextual conversion to \c bool and dereferencing, and its dereferencing result must support stream output. + * \param none Marker used to indicate when the value is not present. Optional. If not specified, nothing is output if the value is not present. + * \returns Manipulator to be inserted into the stream. + * + * \note Both \a opt and \a none objects must outlive the created manipulator object. + */ +template< typename OptionalT, typename NoneElementT, std::size_t N > +inline typename boost::enable_if_c< + is_scalar< OptionalT >::value, + optional_manipulator< OptionalT, NoneElementT* > +>::type optional_manip(OptionalT opt, NoneElementT (&none)[N]) BOOST_NOEXCEPT +{ + return optional_manipulator< OptionalT, NoneElementT* >(opt, none); +} + +/*! + * Optional manipulator generator function. + * + * \param opt Optional value to output. The optional value must support contextual conversion to \c bool and dereferencing, and its dereferencing result must support stream output. + * \param none Marker used to indicate when the value is not present. Optional. If not specified, nothing is output if the value is not present. + * \returns Manipulator to be inserted into the stream. + * + * \note Both \a opt and \a none objects must outlive the created manipulator object. + */ +template< typename OptionalT, typename NoneT > +inline typename boost::enable_if_c< + !is_scalar< OptionalT >::value && !is_array< OptionalT >::value && is_scalar< NoneT >::value, + optional_manipulator< OptionalT, NoneT > +>::type optional_manip(OptionalT const& opt, NoneT none) BOOST_NOEXCEPT +{ + return optional_manipulator< OptionalT, NoneT >(opt, none); +} + +/*! + * Optional manipulator generator function. + * + * \param opt Optional value to output. The optional value must support contextual conversion to \c bool and dereferencing, and its dereferencing result must support stream output. + * \param none Marker used to indicate when the value is not present. Optional. If not specified, nothing is output if the value is not present. + * \returns Manipulator to be inserted into the stream. + * + * \note Both \a opt and \a none objects must outlive the created manipulator object. + */ +template< typename OptionalT, typename NoneT > +inline typename boost::enable_if_c< + !is_scalar< OptionalT >::value && !is_array< OptionalT >::value && !is_scalar< NoneT >::value, + optional_manipulator< OptionalT, NoneT > +>::type optional_manip(OptionalT const& opt, NoneT const& none) BOOST_NOEXCEPT +{ + return optional_manipulator< OptionalT, NoneT >(opt, none); +} + +/*! + * Optional manipulator generator function. + * + * \param opt Optional value to output. The optional value must support contextual conversion to \c bool and dereferencing, and its dereferencing result must support stream output. + * \param none Marker used to indicate when the value is not present. Optional. If not specified, nothing is output if the value is not present. + * \returns Manipulator to be inserted into the stream. + * + * \note Both \a opt and \a none objects must outlive the created manipulator object. + */ +template< typename OptionalT, typename NoneElementT, std::size_t N > +inline typename boost::enable_if_c< + !is_scalar< OptionalT >::value && !is_array< OptionalT >::value, + optional_manipulator< OptionalT, NoneElementT* > +>::type optional_manip(OptionalT const& opt, NoneElementT (&none)[N]) BOOST_NOEXCEPT +{ + return optional_manipulator< OptionalT, NoneElementT* >(opt, none); +} + +/*! + * Optional manipulator generator function. + * + * \param opt Optional value to output. The optional value must support contextual conversion to \c bool and dereferencing, and its dereferencing result must support stream output. + * \returns Manipulator to be inserted into the stream. + * + * \note \a opt object must outlive the created manipulator object. + */ +template< typename OptionalT > +inline typename boost::enable_if_c< + is_scalar< OptionalT >::value, + optional_manipulator< OptionalT, void > +>::type optional_manip(OptionalT opt) BOOST_NOEXCEPT +{ + return optional_manipulator< OptionalT, void >(opt); +} + +/*! + * Optional manipulator generator function. + * + * \param opt Optional value to output. The optional value must support contextual conversion to \c bool and dereferencing, and its dereferencing result must support stream output. + * \returns Manipulator to be inserted into the stream. + * + * \note \a opt object must outlive the created manipulator object. + */ +template< typename OptionalT > +inline typename boost::enable_if_c< + !is_scalar< OptionalT >::value && !is_array< OptionalT >::value, + optional_manipulator< OptionalT, void > +>::type optional_manip(OptionalT const& opt) BOOST_NOEXCEPT +{ + return optional_manipulator< OptionalT, void >(opt); +} + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include + +#endif // BOOST_LOG_UTILITY_MANIPULATORS_OPTIONAL_HPP_INCLUDED_ diff --git a/include/boost/log/utility/manipulators/range.hpp b/include/boost/log/utility/manipulators/range.hpp new file mode 100644 index 0000000000..0c5fb8c920 --- /dev/null +++ b/include/boost/log/utility/manipulators/range.hpp @@ -0,0 +1,195 @@ +/* + * Copyright Andrey Semashev 2020. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * https://www.boost.org/LICENSE_1_0.txt) + */ +/*! + * \file utility/manipulators/range.hpp + * \author Andrey Semashev + * \date 11.05.2020 + * + * The header contains implementation of a stream manipulator for inserting a range of elements, optionally separated with a delimiter. + */ + +#ifndef BOOST_LOG_UTILITY_MANIPULATORS_RANGE_HPP_INCLUDED_ +#define BOOST_LOG_UTILITY_MANIPULATORS_RANGE_HPP_INCLUDED_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +/*! + * Stream manipulator for inserting a range of elements, optionally separated with a delimiter. + */ +template< typename RangeT, typename DelimiterT > +class range_manipulator +{ +private: + typedef typename conditional< + is_scalar< DelimiterT >::value, + DelimiterT, + DelimiterT const& + >::type stored_delimiter_type; + +private: + RangeT const& m_range; + stored_delimiter_type m_delimiter; + +public: + //! Initializing constructor + range_manipulator(RangeT const& range, stored_delimiter_type delimiter) BOOST_NOEXCEPT : + m_range(range), + m_delimiter(delimiter) + { + } + + //! The method outputs elements of the range separated with delimiter + template< typename StreamT > + void output(StreamT& stream) const + { + typedef typename boost::range_const_iterator< RangeT >::type const_iterator; + const_iterator it = boost::begin(m_range); + const const_iterator end = boost::end(m_range); + if (BOOST_LIKELY(it != end)) + { + stream << *it; + + for (++it; it != end; ++it) + { + stream << m_delimiter; + stream << *it; + } + } + } +}; + +/*! + * Stream manipulator for inserting a range of elements. Specialization for when there is no delimiter. + */ +template< typename RangeT > +class range_manipulator< RangeT, void > +{ +private: + RangeT const& m_range; + +public: + //! Initializing constructor + explicit range_manipulator(RangeT const& range) BOOST_NOEXCEPT : + m_range(range) + { + } + + //! The method outputs elements of the range + template< typename StreamT > + void output(StreamT& stream) const + { + typedef typename boost::range_const_iterator< RangeT >::type const_iterator; + const_iterator it = boost::begin(m_range); + const const_iterator end = boost::end(m_range); + for (; it != end; ++it) + { + stream << *it; + } + } +}; + +/*! + * Stream output operator for \c range_manipulator. Outputs every element of the range, separated with a delimiter, if one was specified on manipulator construction. + */ +template< typename StreamT, typename RangeT, typename DelimiterT > +inline typename boost::enable_if_c< log::aux::is_ostream< StreamT >::value, StreamT& >::type operator<< (StreamT& strm, range_manipulator< RangeT, DelimiterT > const& manip) +{ + if (BOOST_LIKELY(strm.good())) + manip.output(strm); + + return strm; +} + +/*! + * Range manipulator generator function. + * + * \param range Range of elements to output. The range must support begin and end iterators, and its elements must support stream output. + * \param delimiter Delimiter to separate elements in the output. Optional. If not specified, elements are output without separation. + * \returns Manipulator to be inserted into the stream. + * + * \note Both \a range and \a delimiter objects must outlive the created manipulator object. + */ +template< typename RangeT, typename DelimiterT > +inline typename boost::enable_if_c< + is_scalar< DelimiterT >::value, + range_manipulator< RangeT, DelimiterT > +>::type range_manip(RangeT const& range, DelimiterT delimiter) BOOST_NOEXCEPT +{ + return range_manipulator< RangeT, DelimiterT >(range, delimiter); +} + +/*! + * Range manipulator generator function. + * + * \param range Range of elements to output. The range must support begin and end iterators, and its elements must support stream output. + * \param delimiter Delimiter to separate elements in the output. Optional. If not specified, elements are output without separation. + * \returns Manipulator to be inserted into the stream. + * + * \note Both \a range and \a delimiter objects must outlive the created manipulator object. + */ +template< typename RangeT, typename DelimiterT > +inline typename boost::disable_if_c< + is_scalar< DelimiterT >::value, + range_manipulator< RangeT, DelimiterT > +>::type range_manip(RangeT const& range, DelimiterT const& delimiter) BOOST_NOEXCEPT +{ + return range_manipulator< RangeT, DelimiterT >(range, delimiter); +} + +/*! + * Range manipulator generator function. + * + * \param range Range of elements to output. The range must support begin and end iterators, and its elements must support stream output. + * \param delimiter Delimiter to separate elements in the output. Optional. If not specified, elements are output without separation. + * \returns Manipulator to be inserted into the stream. + * + * \note Both \a range and \a delimiter objects must outlive the created manipulator object. + */ +template< typename RangeT, typename DelimiterElementT, std::size_t N > +inline range_manipulator< RangeT, DelimiterElementT* > range_manip(RangeT const& range, DelimiterElementT (&delimiter)[N]) BOOST_NOEXCEPT +{ + return range_manipulator< RangeT, DelimiterElementT* >(range, delimiter); +} + +/*! + * Range manipulator generator function. + * + * \param range Range of elements to output. The range must support begin and end iterators, and its elements must support stream output. + * \returns Manipulator to be inserted into the stream. + * + * \note \a delimiter object must outlive the created manipulator object. + */ +template< typename RangeT > +inline range_manipulator< RangeT, void > range_manip(RangeT const& range) BOOST_NOEXCEPT +{ + return range_manipulator< RangeT, void >(range); +} + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include + +#endif // BOOST_LOG_UTILITY_MANIPULATORS_RANGE_HPP_INCLUDED_ diff --git a/include/boost/log/utility/manipulators/tuple.hpp b/include/boost/log/utility/manipulators/tuple.hpp new file mode 100644 index 0000000000..a2bc0c2b59 --- /dev/null +++ b/include/boost/log/utility/manipulators/tuple.hpp @@ -0,0 +1,228 @@ +/* + * Copyright Andrey Semashev 2020. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * https://www.boost.org/LICENSE_1_0.txt) + */ +/*! + * \file utility/manipulators/tuple.hpp + * \author Andrey Semashev + * \date 11.05.2020 + * + * The header contains implementation of a stream manipulator for inserting a tuple or any heterogeneous sequence of elements, optionally separated with a delimiter. + */ + +#ifndef BOOST_LOG_UTILITY_MANIPULATORS_TUPLE_HPP_INCLUDED_ +#define BOOST_LOG_UTILITY_MANIPULATORS_TUPLE_HPP_INCLUDED_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +/*! + * Stream manipulator for inserting a heterogeneous sequence of elements, optionally separated with a delimiter. + */ +template< typename TupleT, typename DelimiterT > +class tuple_manipulator +{ +private: + typedef typename conditional< + is_scalar< DelimiterT >::value, + DelimiterT, + DelimiterT const& + >::type stored_delimiter_type; + + template< typename StreamT > + struct output_visitor + { + typedef boost::true_type result_type; + + output_visitor(StreamT& stream, stored_delimiter_type delimiter) BOOST_NOEXCEPT : + m_stream(stream), + m_delimiter(delimiter) + { + } + + template< typename T > + result_type operator() (boost::true_type, T const& elem) const + { + m_stream << m_delimiter; + return operator()(boost::false_type(), elem); + } + + template< typename T > + result_type operator() (boost::false_type, T const& elem) const + { + m_stream << elem; + return result_type(); + } + + private: + StreamT& m_stream; + stored_delimiter_type m_delimiter; + }; + +private: + TupleT const& m_tuple; + stored_delimiter_type m_delimiter; + +public: + //! Initializing constructor + tuple_manipulator(TupleT const& tuple, stored_delimiter_type delimiter) BOOST_NOEXCEPT : + m_tuple(tuple), + m_delimiter(delimiter) + { + } + + //! The method outputs elements of the sequence separated with delimiter + template< typename StreamT > + void output(StreamT& stream) const + { + boost::fusion::fold(m_tuple, boost::false_type(), output_visitor< StreamT >(stream, m_delimiter)); + } +}; + +/*! + * Stream manipulator for inserting a heterogeneous sequence of elements. Specialization for when there is no delimiter. + */ +template< typename TupleT > +class tuple_manipulator< TupleT, void > +{ +private: + template< typename StreamT > + struct output_visitor + { + typedef void result_type; + + explicit output_visitor(StreamT& stream) BOOST_NOEXCEPT : + m_stream(stream) + { + } + + template< typename T > + result_type operator() (T const& elem) const + { + m_stream << elem; + } + + private: + StreamT& m_stream; + }; + +private: + TupleT const& m_tuple; + +public: + //! Initializing constructor + explicit tuple_manipulator(TupleT const& tuple) BOOST_NOEXCEPT : + m_tuple(tuple) + { + } + + //! The method outputs elements of the sequence + template< typename StreamT > + void output(StreamT& stream) const + { + boost::fusion::for_each(m_tuple, output_visitor< StreamT >(stream)); + } +}; + +/*! + * Stream output operator for \c tuple_manipulator. Outputs every element of the sequence, separated with a delimiter, if one was specified on manipulator construction. + */ +template< typename StreamT, typename TupleT, typename DelimiterT > +inline typename boost::enable_if_c< log::aux::is_ostream< StreamT >::value, StreamT& >::type operator<< (StreamT& strm, tuple_manipulator< TupleT, DelimiterT > const& manip) +{ + if (BOOST_LIKELY(strm.good())) + manip.output(strm); + + return strm; +} + +/*! + * Tuple manipulator generator function. + * + * \param tuple Heterogeneous sequence of elements to output. The sequence must be supported by Boost.Fusion, and its elements must support stream output. + * \param delimiter Delimiter to separate elements in the output. Optional. If not specified, elements are output without separation. + * \returns Manipulator to be inserted into the stream. + * + * \note Both \a tuple and \a delimiter objects must outlive the created manipulator object. + */ +template< typename TupleT, typename DelimiterT > +inline typename boost::enable_if_c< + is_scalar< DelimiterT >::value, + tuple_manipulator< TupleT, DelimiterT > +>::type tuple_manip(TupleT const& tuple, DelimiterT delimiter) BOOST_NOEXCEPT +{ + return tuple_manipulator< TupleT, DelimiterT >(tuple, delimiter); +} + +/*! + * Tuple manipulator generator function. + * + * \param tuple Heterogeneous sequence of elements to output. The sequence must be supported by Boost.Fusion, and its elements must support stream output. + * \param delimiter Delimiter to separate elements in the output. Optional. If not specified, elements are output without separation. + * \returns Manipulator to be inserted into the stream. + * + * \note Both \a tuple and \a delimiter objects must outlive the created manipulator object. + */ +template< typename TupleT, typename DelimiterT > +inline typename boost::disable_if_c< + is_scalar< DelimiterT >::value, + tuple_manipulator< TupleT, DelimiterT > +>::type tuple_manip(TupleT const& tuple, DelimiterT const& delimiter) BOOST_NOEXCEPT +{ + return tuple_manipulator< TupleT, DelimiterT >(tuple, delimiter); +} + +/*! + * Tuple manipulator generator function. + * + * \param tuple Heterogeneous sequence of elements to output. The sequence must be supported by Boost.Fusion, and its elements must support stream output. + * \param delimiter Delimiter to separate elements in the output. Optional. If not specified, elements are output without separation. + * \returns Manipulator to be inserted into the stream. + * + * \note Both \a tuple and \a delimiter objects must outlive the created manipulator object. + */ +template< typename TupleT, typename DelimiterElementT, std::size_t N > +inline tuple_manipulator< TupleT, DelimiterElementT* > tuple_manip(TupleT const& tuple, DelimiterElementT (&delimiter)[N]) BOOST_NOEXCEPT +{ + return tuple_manipulator< TupleT, DelimiterElementT* >(tuple, delimiter); +} + +/*! + * Tuple manipulator generator function. + * + * \param tuple Heterogeneous sequence of elements to output. The sequence must be supported by Boost.Fusion, and its elements must support stream output. + * \returns Manipulator to be inserted into the stream. + * + * \note \a tuple object must outlive the created manipulator object. + */ +template< typename TupleT > +inline tuple_manipulator< TupleT, void > tuple_manip(TupleT const& tuple) BOOST_NOEXCEPT +{ + return tuple_manipulator< TupleT, void >(tuple); +} + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include + +#endif // BOOST_LOG_UTILITY_MANIPULATORS_TUPLE_HPP_INCLUDED_ diff --git a/include/boost/log/utility/permissions.hpp b/include/boost/log/utility/permissions.hpp index 8562f7fe73..d28a9e0897 100644 --- a/include/boost/log/utility/permissions.hpp +++ b/include/boost/log/utility/permissions.hpp @@ -33,9 +33,17 @@ struct _SECURITY_ATTRIBUTES; namespace boost { #ifdef BOOST_WINDOWS +#if defined(BOOST_GCC) && BOOST_GCC >= 40600 +#pragma GCC diagnostic push +// type attributes ignored after type is already defined +#pragma GCC diagnostic ignored "-Wattributes" +#endif namespace winapi { struct BOOST_LOG_MAY_ALIAS _SECURITY_ATTRIBUTES; } +#if defined(BOOST_GCC) && BOOST_GCC >= 40600 +#pragma GCC diagnostic pop +#endif #endif namespace interprocess { diff --git a/include/boost/log/utility/setup/console.hpp b/include/boost/log/utility/setup/console.hpp index 7212c7b74a..08bfd961f0 100644 --- a/include/boost/log/utility/setup/console.hpp +++ b/include/boost/log/utility/setup/console.hpp @@ -16,10 +16,12 @@ #define BOOST_LOG_UTILITY_SETUP_CONSOLE_HPP_INCLUDED_ #include +#include #include #include #include #include +#include #include #ifndef BOOST_LOG_NO_THREADS #include @@ -29,7 +31,6 @@ #include #include #include -#include #include #ifdef BOOST_HAS_PRAGMA_ONCE @@ -62,10 +63,9 @@ shared_ptr< shared_ptr< std::basic_ostream< CharT > > pStream(&strm, boost::null_deleter()); typedef sinks::basic_text_ostream_backend< CharT > backend_t; - shared_ptr< backend_t > pBackend = boost::make_shared< backend_t >(); + shared_ptr< backend_t > pBackend = boost::make_shared< backend_t >(args); pBackend->add_stream(pStream); - pBackend->auto_flush(args[keywords::auto_flush | false]); typedef BOOST_LOG_CONSOLE_SINK_FRONTEND_INTERNAL< backend_t > sink_t; shared_ptr< sink_t > pSink = boost::make_shared< sink_t >(pBackend); @@ -112,7 +112,7 @@ inline shared_ptr< > add_console_log() { return aux::add_console_log( - aux::default_console_stream< CharT >::get(), keywords::auto_flush = false); + aux::default_console_stream< CharT >::get(), log::aux::empty_arg_list()); } @@ -123,7 +123,7 @@ inline shared_ptr< > > add_console_log(std::basic_ostream< CharT >& strm) { - return aux::add_console_log(strm, keywords::auto_flush = false); + return aux::add_console_log(strm, log::aux::empty_arg_list()); } template< typename CharT, typename ArgT1 > @@ -156,6 +156,16 @@ inline shared_ptr< return aux::add_console_log(strm, (arg1, arg2, arg3)); } +template< typename CharT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4 > +inline shared_ptr< + BOOST_LOG_CONSOLE_SINK_FRONTEND_INTERNAL< + sinks::basic_text_ostream_backend< CharT > + > +> add_console_log(std::basic_ostream< CharT >& strm, ArgT1 const& arg1, ArgT2 const& arg2, ArgT3 const& arg3, ArgT3 const& arg4) +{ + return aux::add_console_log(strm, (arg1, arg2, arg3, arg4)); +} + #else // BOOST_LOG_DOXYGEN_PASS /*! @@ -170,6 +180,8 @@ inline shared_ptr< * or a formatter lambda expression (either streaming or Boost.Format-like notation). * \li \c auto_flush A boolean flag that shows whether the sink should automatically flush the stream * after each written record. + * \li \c auto_newline_mode - Specifies automatic trailing newline insertion mode. Must be a value of + * the \c auto_newline_mode enum. By default, is auto_newline_mode::insert_if_missing. * \return Pointer to the constructed sink. */ template< typename CharT, typename... ArgsT > diff --git a/include/boost/log/utility/setup/file.hpp b/include/boost/log/utility/setup/file.hpp index f559174f83..373c740ed9 100644 --- a/include/boost/log/utility/setup/file.hpp +++ b/include/boost/log/utility/setup/file.hpp @@ -15,9 +15,12 @@ #ifndef BOOST_LOG_UTILITY_SETUP_FILE_HPP_INCLUDED_ #define BOOST_LOG_UTILITY_SETUP_FILE_HPP_INCLUDED_ +#include +#include #include #include #include // for is_named_argument +#include #include #include #include @@ -34,6 +37,8 @@ #include #endif #include +#include +#include #include #include @@ -57,12 +62,12 @@ namespace aux { //! The function creates a file collector according to the specified arguments template< typename ArgsT > -inline shared_ptr< sinks::file::collector > setup_file_collector(ArgsT const&, mpl::true_ const&) +inline shared_ptr< sinks::file::collector > setup_file_collector(ArgsT const&, boost::true_type) { return shared_ptr< sinks::file::collector >(); } template< typename ArgsT > -inline shared_ptr< sinks::file::collector > setup_file_collector(ArgsT const& args, mpl::false_ const&) +inline shared_ptr< sinks::file::collector > setup_file_collector(ArgsT const& args, boost::false_type) { return sinks::file::make_collector(args); } @@ -96,32 +101,50 @@ shared_ptr< BOOST_LOG_FILE_SINK_FRONTEND_INTERNAL< sinks::text_file_backend > > return pSink; } -//! The function wraps the argument into a file_name named argument, if needed -template< typename T > -inline T const& wrap_file_name(T const& arg, mpl::true_) +//! The trait wraps the argument into a file_name named argument, if needed +template< typename T, bool IsNamedArgument = parameter::aux::is_named_argument< T >::value > +struct file_name_param_traits { - return arg; -} + static shared_ptr< BOOST_LOG_FILE_SINK_FRONTEND_INTERNAL< sinks::text_file_backend > > wrap_add_file_log(T const& file_name_arg) + { + return aux::add_file_log(file_name_arg); + } + + template< typename ArgsT > + static shared_ptr< BOOST_LOG_FILE_SINK_FRONTEND_INTERNAL< sinks::text_file_backend > > wrap_add_file_log(T const& file_name_arg, ArgsT const& args) + { + return aux::add_file_log((args, file_name_arg)); + } +}; + template< typename T > -inline typename parameter::aux::tag< keywords::tag::file_name, T const& >::type -wrap_file_name(T const& arg, mpl::false_) +struct file_name_param_traits< T, false > { - return keywords::file_name = arg; -} + static shared_ptr< BOOST_LOG_FILE_SINK_FRONTEND_INTERNAL< sinks::text_file_backend > > wrap_add_file_log(T const& file_name_arg) + { + return aux::add_file_log(keywords::file_name = file_name_arg); + } + + template< typename ArgsT > + static shared_ptr< BOOST_LOG_FILE_SINK_FRONTEND_INTERNAL< sinks::text_file_backend > > wrap_add_file_log(T const& file_name_arg, ArgsT const& args) + { + return aux::add_file_log((args, (keywords::file_name = file_name_arg))); + } +}; } // namespace aux #ifndef BOOST_LOG_DOXYGEN_PASS #define BOOST_LOG_INIT_LOG_TO_FILE_INTERNAL(z, n, data)\ - template< BOOST_PP_ENUM_PARAMS(n, typename T) >\ - inline shared_ptr< BOOST_LOG_FILE_SINK_FRONTEND_INTERNAL< sinks::text_file_backend > > add_file_log(BOOST_PP_ENUM_BINARY_PARAMS(n, T, const& arg))\ + template< BOOST_PP_ENUM_PARAMS_Z(z, n, typename T) >\ + inline shared_ptr< BOOST_LOG_FILE_SINK_FRONTEND_INTERNAL< sinks::text_file_backend > > add_file_log(BOOST_PP_ENUM_BINARY_PARAMS_Z(z, n, T, const& arg))\ {\ - return aux::add_file_log((\ - aux::wrap_file_name(arg0, typename parameter::aux::is_named_argument< T0 >::type())\ + return aux::file_name_param_traits< T0 >::wrap_add_file_log(\ + arg0\ BOOST_PP_COMMA_IF(BOOST_PP_GREATER(n, 1))\ - BOOST_PP_ENUM_SHIFTED_PARAMS(n, arg)\ - ));\ + BOOST_PP_EXPR_IF(BOOST_PP_GREATER(n, 1), (BOOST_PP_ENUM_SHIFTED_PARAMS_Z(z, n, arg)))\ + );\ } BOOST_PP_REPEAT_FROM_TO(1, BOOST_LOG_MAX_PARAMETER_ARGS, BOOST_LOG_INIT_LOG_TO_FILE_INTERNAL, ~) @@ -134,13 +157,20 @@ BOOST_PP_REPEAT_FROM_TO(1, BOOST_LOG_MAX_PARAMETER_ARGS, BOOST_LOG_INIT_LOG_TO_F * The function initializes the logging library to write logs to a file stream. * * \param args A number of named arguments. The following parameters are supported: - * \li \c file_name The file name or its pattern. This parameter is mandatory. + * \li \c file_name The active file name or its pattern. This parameter is mandatory. + * \li \c target_file_name - Specifies the target file name pattern to use to rename the log file on rotation, + * before passing it to the file collector. The pattern may contain the same + * placeholders as the \c file_name parameter. By default, no renaming is done, + * i.e. the written log file keeps its name according to \c file_name. * \li \c open_mode The mask that describes the open mode for the file. See std::ios_base::openmode. * \li \c rotation_size The size of the file at which rotation should occur. See basic_text_file_backend. * \li \c time_based_rotation The predicate for time-based file rotations. See basic_text_file_backend. * \li \c auto_flush A boolean flag that shows whether the sink should automatically flush the file * after each written record. - * \li \c target The target directory to store rotated files in. See sinks::file::make_collector. + * \li \c auto_newline_mode - Specifies automatic trailing newline insertion mode. Must be a value of + * the \c auto_newline_mode enum. By default, is auto_newline_mode::insert_if_missing. + * \li \c target The target directory to store rotated files in. Enables file collector and, if specified, limits associated + * with the target directory. See sinks::file::make_collector. * \li \c max_size The maximum total size of rotated files in the target directory. See sinks::file::make_collector. * \li \c min_free_space Minimum free space in the target directory. See sinks::file::make_collector. * \li \c max_files The maximum total number of rotated files in the target directory. See sinks::file::make_collector. @@ -149,7 +179,11 @@ BOOST_PP_REPEAT_FROM_TO(1, BOOST_LOG_MAX_PARAMETER_ARGS, BOOST_LOG_INIT_LOG_TO_F * or a filter lambda expression. * \li \c format Specifies a formatter to install into the sink. May be a string that represents a formatter, * or a formatter lambda expression (either streaming or Boost.Format-like notation). + * * \return Pointer to the constructed sink. + * + * \note The \c target named argument is required to enable the file collector and the limits associated with the target directory. + * If the parameter is not specified, the file collector will not be created and the limits will not be maintained. */ template< typename... ArgsT > shared_ptr< BOOST_LOG_FILE_SINK_FRONTEND_INTERNAL< sinks::text_file_backend > > add_file_log(ArgsT... const& args); diff --git a/include/boost/log/utility/setup/filter_parser.hpp b/include/boost/log/utility/setup/filter_parser.hpp index 9a6c9f6d25..16d2cc5475 100644 --- a/include/boost/log/utility/setup/filter_parser.hpp +++ b/include/boost/log/utility/setup/filter_parser.hpp @@ -16,7 +16,7 @@ #define BOOST_LOG_UTILITY_SETUP_FILTER_PARSER_HPP_INCLUDED_ #include -#include +#include #include #include #include @@ -217,7 +217,12 @@ class basic_filter_factory : */ virtual value_type parse_argument(string_type const& arg) { - return boost::lexical_cast< value_type >(arg); + std::basic_istringstream< typename base_type::char_type > strm(arg); + value_type val{}; + strm >> val; + if (BOOST_UNLIKELY((strm.rdstate() & (std::ios_base::badbit | std::ios_base::failbit)) != 0)) + BOOST_LOG_THROW_DESCR(parse_error, "Failed to parse argument value from \"" + boost::log::aux::to_narrow(arg) + "\""); + return val; } }; diff --git a/include/boost/log/utility/setup/settings.hpp b/include/boost/log/utility/setup/settings.hpp index f35bf8506c..3798cf6ade 100644 --- a/include/boost/log/utility/setup/settings.hpp +++ b/include/boost/log/utility/setup/settings.hpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include @@ -31,7 +31,7 @@ #include #endif #if defined(BOOST_LOG_TYPEOF) && defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES) -#include +#include #endif #include @@ -115,7 +115,7 @@ class basic_settings_section class ref { private: - typedef typename mpl::if_c< + typedef typename boost::conditional< IsConstV, basic_settings_section< char_type > const, basic_settings_section< char_type > @@ -354,14 +354,14 @@ class basic_settings_section /*! * Default constructor. Creates an empty settings container. */ - basic_settings_section() : m_ptree(NULL) + basic_settings_section() BOOST_NOEXCEPT : m_ptree(NULL) { } /*! * Copy constructor. */ - basic_settings_section(basic_settings_section const& that) : m_ptree(that.m_ptree) + basic_settings_section(basic_settings_section const& that) BOOST_NOEXCEPT : m_ptree(that.m_ptree) { } @@ -517,7 +517,7 @@ class basic_settings_section /*! * Swaps two references to settings sections. */ - void swap(basic_settings_section& that) + void swap(basic_settings_section& that) BOOST_NOEXCEPT { property_tree_type* const p = m_ptree; m_ptree = that.m_ptree; @@ -525,13 +525,13 @@ class basic_settings_section } protected: - explicit basic_settings_section(property_tree_type* tree) : m_ptree(tree) + explicit basic_settings_section(property_tree_type* tree) BOOST_NOEXCEPT : m_ptree(tree) { } }; template< typename CharT > -inline void swap(basic_settings_section< CharT >& left, basic_settings_section< CharT >& right) +inline void swap(basic_settings_section< CharT >& left, basic_settings_section< CharT >& right) BOOST_NOEXCEPT { left.swap(right); } @@ -584,7 +584,7 @@ class basic_settings : /*! * Move constructor. */ - basic_settings(BOOST_RV_REF(this_type) that) + basic_settings(BOOST_RV_REF(this_type) that) BOOST_NOEXCEPT { this->swap(that); } @@ -598,7 +598,7 @@ class basic_settings : /*! * Destructor */ - ~basic_settings() + ~basic_settings() BOOST_NOEXCEPT { delete this->m_ptree; } @@ -618,7 +618,7 @@ class basic_settings : /*! * Move assignment operator. */ - basic_settings& operator= (BOOST_RV_REF(basic_settings) that) + basic_settings& operator= (BOOST_RV_REF(basic_settings) that) BOOST_NOEXCEPT { this->swap(that); return *this; diff --git a/include/boost/log/utility/strictest_lock.hpp b/include/boost/log/utility/strictest_lock.hpp index 5dd48b4ffb..2b5ac9fa0c 100644 --- a/include/boost/log/utility/strictest_lock.hpp +++ b/include/boost/log/utility/strictest_lock.hpp @@ -16,7 +16,7 @@ #ifndef BOOST_LOG_UTILITY_STRICTEST_LOCK_HPP_INCLUDED_ #define BOOST_LOG_UTILITY_STRICTEST_LOCK_HPP_INCLUDED_ -#include +#include #include #include #if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) @@ -67,44 +67,80 @@ template< typename LockT > struct thread_access_mode_of; template< typename MutexT > -struct thread_access_mode_of< no_lock< MutexT > > : mpl::integral_c< lock_access_mode, unlocked_access > +struct thread_access_mode_of< no_lock< MutexT > > : boost::integral_constant< lock_access_mode, unlocked_access > { }; #if !defined(BOOST_LOG_NO_THREADS) +#if !defined(BOOST_MSSTL_VERSION) || (BOOST_MSSTL_VERSION != 140) template< typename MutexT > -struct thread_access_mode_of< lock_guard< MutexT > > : mpl::integral_c< lock_access_mode, exclusive_access > +struct thread_access_mode_of< std::lock_guard< MutexT > > : boost::integral_constant< lock_access_mode, exclusive_access > { }; +#else // !defined(BOOST_MSSTL_VERSION) || (BOOST_MSSTL_VERSION != 140) +template< typename... MutexesT > +struct thread_access_mode_of< std::lock_guard< MutexesT... > > : boost::integral_constant< lock_access_mode, exclusive_access > +{ +}; +#endif // !defined(BOOST_MSSTL_VERSION) || (BOOST_MSSTL_VERSION != 140) template< typename MutexT > -struct thread_access_mode_of< shared_lock_guard< MutexT > > : mpl::integral_c< lock_access_mode, shared_access > +struct thread_access_mode_of< std::unique_lock< MutexT > > : boost::integral_constant< lock_access_mode, exclusive_access > { }; +#if !defined(BOOST_NO_CXX14_HDR_SHARED_MUTEX) template< typename MutexT > -struct thread_access_mode_of< unique_lock< MutexT > > : mpl::integral_c< lock_access_mode, exclusive_access > +struct thread_access_mode_of< std::shared_lock< MutexT > > : boost::integral_constant< lock_access_mode, shared_access > +{ +}; +#endif // !defined(BOOST_NO_CXX14_HDR_SHARED_MUTEX) + +#if defined(__cpp_lib_scoped_lock) && (__cpp_lib_scoped_lock >= 201703l) +template< typename... MutexesT > +struct thread_access_mode_of< std::scoped_lock< MutexesT... > > : boost::integral_constant< lock_access_mode, exclusive_access > { }; +#endif template< typename MutexT > -struct thread_access_mode_of< shared_lock< MutexT > > : mpl::integral_c< lock_access_mode, shared_access > +struct thread_access_mode_of< lock_guard< MutexT > > : boost::integral_constant< lock_access_mode, exclusive_access > { }; template< typename MutexT > -struct thread_access_mode_of< upgrade_lock< MutexT > > : mpl::integral_c< lock_access_mode, shared_access > +struct thread_access_mode_of< shared_lock_guard< MutexT > > : boost::integral_constant< lock_access_mode, shared_access > { }; template< typename MutexT > -struct thread_access_mode_of< boost::log::aux::exclusive_lock_guard< MutexT > > : mpl::integral_c< lock_access_mode, exclusive_access > +struct thread_access_mode_of< unique_lock< MutexT > > : boost::integral_constant< lock_access_mode, exclusive_access > { }; template< typename MutexT > -struct thread_access_mode_of< boost::log::aux::shared_lock_guard< MutexT > > : mpl::integral_c< lock_access_mode, shared_access > +struct thread_access_mode_of< shared_lock< MutexT > > : boost::integral_constant< lock_access_mode, shared_access > +{ +}; + +template< typename MutexT > +struct thread_access_mode_of< upgrade_lock< MutexT > > : boost::integral_constant< lock_access_mode, shared_access > +{ +}; + +template< typename MutexT > +struct thread_access_mode_of< boost::log::aux::exclusive_lock_guard< MutexT > > : boost::integral_constant< lock_access_mode, exclusive_access > +{ +}; + +template< typename MutexT > +struct thread_access_mode_of< boost::log::aux::shared_lock_guard< MutexT > > : boost::integral_constant< lock_access_mode, shared_access > +{ +}; + +template< typename MutexT1, typename MutexT2 > +struct thread_access_mode_of< boost::log::aux::multiple_unique_lock2< MutexT1, MutexT2 > > : boost::integral_constant< lock_access_mode, exclusive_access > { }; diff --git a/include/boost/log/utility/type_dispatch/dynamic_type_dispatcher.hpp b/include/boost/log/utility/type_dispatch/dynamic_type_dispatcher.hpp index 21428ff949..67c65ce56f 100644 --- a/include/boost/log/utility/type_dispatch/dynamic_type_dispatcher.hpp +++ b/include/boost/log/utility/type_dispatch/dynamic_type_dispatcher.hpp @@ -18,8 +18,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -63,7 +63,7 @@ class dynamic_type_dispatcher : { this->m_pVisitor = (void*)boost::addressof(m_Visitor); typedef void (*trampoline_t)(void*, T const&); - BOOST_STATIC_ASSERT_MSG(sizeof(trampoline_t) == sizeof(void*), "Boost.Log: Unsupported platform, the size of a function pointer differs from the size of a pointer"); + static_assert(sizeof(trampoline_t) == sizeof(void*), "Boost.Log: Unsupported platform, the size of a function pointer differs from the size of a pointer"); union { void* as_pvoid; diff --git a/include/boost/log/utility/type_dispatch/static_type_dispatcher.hpp b/include/boost/log/utility/type_dispatch/static_type_dispatcher.hpp index dcf2cd033d..00fc1ed884 100644 --- a/include/boost/log/utility/type_dispatch/static_type_dispatcher.hpp +++ b/include/boost/log/utility/type_dispatch/static_type_dispatcher.hpp @@ -19,8 +19,6 @@ #include #include #include -#include -#include #include #include #include @@ -82,7 +80,7 @@ struct dispatching_map_initializer p->first = typeindex::type_id< T >(); typedef void (*trampoline_t)(void*, T const&); - BOOST_STATIC_ASSERT_MSG(sizeof(trampoline_t) == sizeof(void*), "Boost.Log: Unsupported platform, the size of a function pointer differs from the size of a pointer"); + static_assert(sizeof(trampoline_t) == sizeof(void*), "Boost.Log: Unsupported platform, the size of a function pointer differs from the size of a pointer"); union { void* as_pvoid; @@ -98,7 +96,7 @@ struct dispatching_map_initializer class type_sequence_dispatcher_base : public type_dispatcher { -private: +protected: //! Dispatching map element type typedef std::pair< typeindex::type_index, void* > dispatching_map_element_type; @@ -156,11 +154,8 @@ class type_sequence_dispatcher : typedef TypeSequenceT supported_types; private: - //! The dispatching map - typedef array< - std::pair< typeindex::type_index, void* >, - mpl::size< supported_types >::value - > dispatching_map; + //! Number of entries in the dispatching map + static BOOST_CONSTEXPR_OR_CONST std::size_t dispatching_map_size = mpl::size< supported_types >::value; public: /*! @@ -168,33 +163,33 @@ class type_sequence_dispatcher : */ template< typename VisitorT > explicit type_sequence_dispatcher(VisitorT& visitor) : - type_sequence_dispatcher_base(get_dispatching_map< VisitorT >().data(), dispatching_map::static_size, (void*)boost::addressof(visitor)) + type_sequence_dispatcher_base(get_dispatching_map< VisitorT >(), dispatching_map_size, (void*)boost::addressof(visitor)) { } private: //! The method returns the dispatching map instance template< typename VisitorT > - static dispatching_map const& get_dispatching_map() + static const dispatching_map_element_type* get_dispatching_map() { - static const dispatching_map* pinstance = NULL; + static const dispatching_map_element_type* pinstance = NULL; BOOST_LOG_ONCE_BLOCK() { - static dispatching_map instance; - typename dispatching_map::value_type* p = &*instance.begin(); + static dispatching_map_element_type instance[dispatching_map_size]; + dispatching_map_element_type* p = instance; typedef typename mpl::begin< supported_types >::type begin_iterator_type; typedef typename mpl::end< supported_types >::type end_iterator_type; typedef dispatching_map_initializer< VisitorT > initializer; initializer::init(static_cast< begin_iterator_type* >(0), static_cast< end_iterator_type* >(0), p); - std::sort(instance.begin(), instance.end(), dispatching_map_order()); + std::sort(instance, instance + dispatching_map_size, dispatching_map_order()); - pinstance = &instance; + pinstance = instance; } - return *pinstance; + return pinstance; } // Copying and assignment closed diff --git a/include/boost/log/utility/type_dispatch/type_dispatcher.hpp b/include/boost/log/utility/type_dispatch/type_dispatcher.hpp index 148595fcf3..ca633aa16c 100644 --- a/include/boost/log/utility/type_dispatch/type_dispatcher.hpp +++ b/include/boost/log/utility/type_dispatch/type_dispatcher.hpp @@ -16,7 +16,6 @@ #define BOOST_LOG_TYPE_DISPATCHER_HPP_INCLUDED_ #include -#include #include #include #include @@ -59,7 +58,7 @@ class type_dispatcher m_pVisitor(visitor) { typedef void (*trampoline_t)(void*, ValueT const&); - BOOST_STATIC_ASSERT_MSG(sizeof(trampoline_t) == sizeof(void*), "Boost.Log: Unsupported platform, the size of a function pointer differs from the size of a pointer"); + static_assert(sizeof(trampoline_t) == sizeof(void*), "Boost.Log: Unsupported platform, the size of a function pointer differs from the size of a pointer"); union { void* as_pvoid; @@ -100,7 +99,7 @@ class type_dispatcher void operator() (T const& value) const { - BOOST_STATIC_ASSERT_MSG(sizeof(trampoline_t) == sizeof(void*), "Boost.Log: Unsupported platform, the size of a function pointer differs from the size of a pointer"); + static_assert(sizeof(trampoline_t) == sizeof(void*), "Boost.Log: Unsupported platform, the size of a function pointer differs from the size of a pointer"); union { void* as_pvoid; diff --git a/include/boost/log/utility/use_std_allocator.hpp b/include/boost/log/utility/use_std_allocator.hpp new file mode 100644 index 0000000000..c72912672b --- /dev/null +++ b/include/boost/log/utility/use_std_allocator.hpp @@ -0,0 +1,60 @@ +/* + * Copyright Andrey Semashev 2021. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +/*! + * \file use_std_allocator.hpp + * \author Andrey Semashev + * \date 04.03.2021 + * + * The header defines \c use_std_allocator tag type. + */ + +#ifndef BOOST_LOG_UTILITY_USE_STD_ALLOCATOR_HPP_INCLUDED_ +#define BOOST_LOG_UTILITY_USE_STD_ALLOCATOR_HPP_INCLUDED_ + +#include +#include + +#ifdef BOOST_HAS_PRAGMA_ONCE +#pragma once +#endif + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +#ifndef BOOST_LOG_DOXYGEN_PASS + +namespace aux { + +namespace usestdalloc_adl_block { + +struct use_std_allocator {}; + +} // namespace usestdalloc_adl_block + +} // namespace aux + +using aux::usestdalloc_adl_block::use_std_allocator; + +#else // BOOST_LOG_DOXYGEN_PASS + +/*! + * \brief Tag type that indicates that a specialization of \c std::allocator should be used for allocating memory + * + * This tag type can be used in template parameters in various components of Boost.Log. The type itself is not an allocator type. + */ +struct use_std_allocator {}; + +#endif // BOOST_LOG_DOXYGEN_PASS + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include + +#endif // BOOST_LOG_UTILITY_USE_STD_ALLOCATOR_HPP_INCLUDED_ diff --git a/include/boost/log/utility/value_ref.hpp b/include/boost/log/utility/value_ref.hpp index 1f9a360785..c4255acc0d 100644 --- a/include/boost/log/utility/value_ref.hpp +++ b/include/boost/log/utility/value_ref.hpp @@ -112,7 +112,7 @@ class singular_ref protected: //! Default constructor - singular_ref() BOOST_NOEXCEPT : m_ptr(NULL) + BOOST_CONSTEXPR singular_ref() BOOST_NOEXCEPT : m_ptr(NULL) { } @@ -255,6 +255,7 @@ class variant_ref public: //! Referenced value type typedef T value_type; + static_assert(mpl::size< value_type >::value > 0, "Boost.Log: List of types referenced by value_ref must not be empty"); //! Tag type typedef TagT tag_type; @@ -270,7 +271,7 @@ class variant_ref protected: //! Default constructor - variant_ref() BOOST_NOEXCEPT : m_ptr(NULL), m_type_idx(0) + BOOST_CONSTEXPR variant_ref() BOOST_NOEXCEPT : m_ptr(NULL), m_type_idx(0u) { } @@ -447,12 +448,7 @@ class value_ref : /*! * Default constructor. Creates a reference wrapper that does not refer to a value. */ - BOOST_DEFAULTED_FUNCTION(value_ref(), BOOST_NOEXCEPT {}) - - /*! - * Copy constructor. - */ - BOOST_DEFAULTED_FUNCTION(value_ref(value_ref const& that), BOOST_NOEXCEPT : base_type(static_cast< base_type const& >(that)) {}) + BOOST_DEFAULTED_FUNCTION(BOOST_CONSTEXPR value_ref(), BOOST_NOEXCEPT {}) /*! * Initializing constructor. Creates a reference wrapper that refers to the specified value. @@ -461,7 +457,7 @@ class value_ref : explicit value_ref(U const& val #ifndef BOOST_LOG_DOXYGEN_PASS // MSVC-8 can't handle SFINAE in this case properly and often wrongly disables this constructor -#if !defined(_MSC_VER) || (_MSC_VER + 0) >= 1500 +#if !defined(BOOST_MSVC) || BOOST_MSVC >= 1500 , typename boost::enable_if_c< compatibility_traits::BOOST_NESTED_TEMPLATE is_compatible< value_type, U >::value, boost::log::aux::sfinae_dummy >::type = boost::log::aux::sfinae_dummy() #endif #endif diff --git a/meta/libraries.json b/meta/libraries.json index 08e82d14e1..ee35c7b0fc 100644 --- a/meta/libraries.json +++ b/meta/libraries.json @@ -10,5 +10,6 @@ ], "maintainers": [ "Andrey Semashev " - ] + ], + "cxxstd": "11" } diff --git a/src/attribute_set_impl.hpp b/src/attribute_set_impl.hpp index e9de28ec80..2cf62f21df 100644 --- a/src/attribute_set_impl.hpp +++ b/src/attribute_set_impl.hpp @@ -1,5 +1,5 @@ /* - * Copyright Andrey Semashev 2007 - 2015. + * Copyright Andrey Semashev 2007 - 2020. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) @@ -17,19 +17,19 @@ #define BOOST_LOG_ATTRIBUTE_SET_IMPL_HPP_INCLUDED_ #include +#include #include #include #include #include #include -#include #include -#include #include #include #include #include #include +#include #include #ifndef BOOST_LOG_HASH_TABLE_SIZE_LOG @@ -62,16 +62,16 @@ class pool_allocator : #if BOOST_LOG_ATTRIBUTE_SET_MAX_POOL_SIZE > 0 - typedef typename base_type::value_type value_type; - typedef typename base_type::size_type size_type; - typedef typename base_type::difference_type difference_type; - typedef typename base_type::pointer pointer; - typedef typename base_type::const_pointer const_pointer; - typedef typename base_type::reference reference; - typedef typename base_type::const_reference const_reference; + typedef typename log::aux::allocator_traits< base_type >::value_type value_type; + typedef typename log::aux::allocator_traits< base_type >::size_type size_type; + typedef typename log::aux::allocator_traits< base_type >::difference_type difference_type; + typedef typename log::aux::allocator_traits< base_type >::pointer pointer; + typedef typename log::aux::allocator_traits< base_type >::const_pointer const_pointer; + typedef value_type& reference; + typedef value_type const& const_reference; private: - array< pointer, BOOST_LOG_ATTRIBUTE_SET_MAX_POOL_SIZE > m_Pool; + pointer m_Pool[BOOST_LOG_ATTRIBUTE_SET_MAX_POOL_SIZE]; size_type m_PooledCount; public: @@ -96,7 +96,7 @@ class pool_allocator : { for (size_type i = 0; i < m_PooledCount; ++i) { - base_type::deallocate(m_Pool[i], 1); + log::aux::allocator_traits< base_type >::deallocate(*static_cast< base_type* >(this), m_Pool[i], 1); } } @@ -116,24 +116,28 @@ class pool_allocator : pointer allocate(size_type n, const void* hint = NULL) { - if (m_PooledCount > 0) + if (BOOST_LIKELY(m_PooledCount > 0)) { --m_PooledCount; return m_Pool[m_PooledCount]; } else - return base_type::allocate(n, hint); + { + return log::aux::allocator_traits< base_type >::allocate(*static_cast< base_type* >(this), n, hint); + } } void deallocate(pointer p, size_type n) { - if (m_PooledCount < m_Pool.size()) + if (BOOST_LIKELY(m_PooledCount < (sizeof(m_Pool) / sizeof(*m_Pool)))) { m_Pool[m_PooledCount] = p; ++m_PooledCount; } else - base_type::deallocate(p, n); + { + log::aux::allocator_traits< base_type >::deallocate(*static_cast< base_type* >(this), p, n); + } } #else @@ -194,9 +198,6 @@ struct attribute_set::implementation bucket() : first(NULL), last(NULL) {} }; - //! A list of buckets - typedef boost::array< bucket, 1U << BOOST_LOG_HASH_TABLE_SIZE_LOG > buckets; - //! Cleanup function object used to erase elements from the container struct disposer { @@ -220,8 +221,10 @@ struct attribute_set::implementation node_list m_Nodes; //! Node allocator node_allocator m_Allocator; + //! Number of buckets in the hash table + static BOOST_CONSTEXPR_OR_CONST std::size_t bucket_count = static_cast< std::size_t >(1u) << BOOST_LOG_HASH_TABLE_SIZE_LOG; //! Hash table buckets - buckets m_Buckets; + bucket m_Buckets[bucket_count]; public: implementation() @@ -257,7 +260,7 @@ struct attribute_set::implementation void clear() { m_Nodes.clear_and_dispose(disposer(m_Allocator)); - std::fill_n(m_Buckets.begin(), m_Buckets.size(), bucket()); + std::fill_n(m_Buckets, bucket_count, bucket()); } std::pair< iterator, bool > insert(key_type key, mapped_type const& data) @@ -360,7 +363,7 @@ struct attribute_set::implementation //! The function returns a bucket for the specified element bucket& get_bucket(id_type id) { - return m_Buckets[id & (buckets::static_size - 1)]; + return m_Buckets[id & (bucket_count - 1u)]; } //! Attempts to find an element with the specified key in the bucket diff --git a/src/attribute_value_set.cpp b/src/attribute_value_set.cpp index 14d9752a73..1db418a623 100644 --- a/src/attribute_value_set.cpp +++ b/src/attribute_value_set.cpp @@ -14,9 +14,9 @@ */ #include +#include #include #include -#include #include #include #include @@ -94,9 +94,6 @@ struct attribute_value_set::implementation bucket() : first(NULL), last(NULL) {} }; - //! A list of buckets - typedef boost::array< bucket, 1u << BOOST_LOG_HASH_TABLE_SIZE_LOG > buckets; - //! Element disposer struct disposer { @@ -125,8 +122,10 @@ struct attribute_value_set::implementation //! The pointer to the end of storage node* m_pEOS; + //! Number of buckets in the hash table + static BOOST_CONSTEXPR_OR_CONST std::size_t bucket_count = static_cast< std::size_t >(1u) << BOOST_LOG_HASH_TABLE_SIZE_LOG; //! Hash table buckets - buckets m_Buckets; + bucket m_Buckets[bucket_count]; private: //! Constructor @@ -318,7 +317,7 @@ struct attribute_value_set::implementation //! The function returns a bucket for the specified element bucket& get_bucket(id_type id) { - return m_Buckets[id & (buckets::static_size - 1u)]; + return m_Buckets[id & (bucket_count - 1u)]; } //! Attempts to find an element with the specified key in the bucket diff --git a/src/code_conversion.cpp b/src/code_conversion.cpp index 2f253eda3d..b0820ee93c 100644 --- a/src/code_conversion.cpp +++ b/src/code_conversion.cpp @@ -65,6 +65,14 @@ inline std::codecvt_base::result convert( } // namespace +#if defined(BOOST_MSVC) +#pragma warning(push) +// conversion from 'X' to 'Y', possible loss of data +// This warning is triggered for the noconv case below, where we convert wchar_t to char if the locale facet +// reports that no code conversion is needed. In such a case, no data loss will happen. +#pragma warning(disable: 4244) +#endif + template< typename SourceCharT, typename TargetCharT, typename FacetT > inline std::size_t code_convert(const SourceCharT* begin, const SourceCharT* end, std::basic_string< TargetCharT >& converted, std::size_t max_size, FacetT const& fac) { @@ -125,6 +133,7 @@ inline std::size_t code_convert(const SourceCharT* begin, const SourceCharT* end // Looks like the tail of the source buffer contains only part of the last character. // In this case we intentionally fall through to throw an exception. } + BOOST_FALLTHROUGH; default: // std::codecvt_base::error BOOST_LOG_THROW_DESCR(conversion_error, "Could not convert character encoding"); @@ -137,6 +146,10 @@ inline std::size_t code_convert(const SourceCharT* begin, const SourceCharT* end return static_cast< std::size_t >(begin - original_begin); } +#if defined(BOOST_MSVC) +#pragma warning(pop) +#endif + //! The function converts one string to the character type of another BOOST_LOG_API bool code_convert_impl(const wchar_t* str1, std::size_t len, std::string& str2, std::size_t max_size, std::locale const& loc) { diff --git a/src/core.cpp b/src/core.cpp index 07f719962c..60025997ce 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -1,5 +1,5 @@ /* - * Copyright Andrey Semashev 2007 - 2015. + * Copyright Andrey Semashev 2007 - 2025. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) @@ -16,18 +16,19 @@ #include #include #include +#include +#include #include +#include #include #include #include -#include +#include #include #include #include #include #include -#include -#include #include #include #include @@ -37,13 +38,14 @@ #include #include #if !defined(BOOST_LOG_NO_THREADS) +#include +#include #include -#include #include #include #include #endif -#include "unique_ptr.hpp" +#include "xorshift.hpp" #include "default_sink.hpp" #include "stateless_allocator.hpp" #include "alignment_gap_between.hpp" @@ -59,7 +61,7 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { //! Sequence shuffling algorithm. Very similar to std::random_shuffle, used for forward portability with compilers that removed it from the standard library (C++17). template< typename Iterator, typename RandomNumberGenerator > -void random_shuffle(Iterator begin, Iterator end, RandomNumberGenerator& rng) +inline void random_shuffle(Iterator begin, Iterator end, RandomNumberGenerator& rng) { Iterator it = begin; ++it; @@ -67,7 +69,7 @@ void random_shuffle(Iterator begin, Iterator end, RandomNumberGenerator& rng) { Iterator where = begin + rng() % (it - begin + 1u); if (where != it) - boost::swap(*where, *it); + boost::core::invoke_swap(*where, *it); ++it; } } @@ -238,7 +240,7 @@ struct core::implementation : //! Thread-specific attribute set attribute_set m_thread_attributes; //! Random number generator for shuffling - random::taus88 m_rng; + log::aux::xorshift64 m_rng; thread_data() : m_rng(get_random_seed()) { @@ -246,13 +248,27 @@ struct core::implementation : private: //! Creates a seed for RNG - static uint32_t get_random_seed() + static uint64_t get_random_seed() { - uint32_t seed = static_cast< uint32_t >(posix_time::microsec_clock::universal_time().time_of_day().ticks()); + try + { + std::random_device rng; + std::uniform_int_distribution< uint64_t > distrib(1u); + return distrib(rng); + } + catch (...) + { + uint64_t seed; + do + { + seed = static_cast< uint64_t >(std::chrono::system_clock::now().time_since_epoch().count()); #if !defined(BOOST_LOG_NO_THREADS) - seed += static_cast< uint32_t >(log::aux::this_thread::get_id().native_id()); + seed += static_cast< uint64_t >(log::aux::this_thread::get_id().native_id()) * UINT64_C(0x2545F4914F6CDD1D); #endif - return seed; + } + while (seed == 0u); + return seed; + } } }; @@ -280,11 +296,15 @@ struct core::implementation : #else //! Thread-specific data - log::aux::unique_ptr< thread_data > m_thread_data; + std::unique_ptr< thread_data > m_thread_data; #endif //! The global state of logging - volatile bool m_enabled; +#if !defined(BOOST_LOG_NO_THREADS) + boost::atomic< bool > m_enabled; +#else + bool m_enabled; +#endif //! Global filter filter m_filter; @@ -307,14 +327,21 @@ struct core::implementation : bool invoke_exception_handler = true; // Try a quick win first - if (m_enabled) try +#if !defined(BOOST_LOG_NO_THREADS) + if (BOOST_LIKELY(m_enabled.load(boost::memory_order_relaxed))) +#else + if (BOOST_LIKELY(m_enabled)) +#endif + try { thread_data* tsd = get_thread_data(); +#if !defined(BOOST_LOG_NO_THREADS) // Lock the core to be safe against any attribute or sink set modifications - BOOST_LOG_EXPR_IF_MT(scoped_read_lock lock(m_mutex);) + scoped_read_lock lock(m_mutex); - if (m_enabled) + if (BOOST_LIKELY(m_enabled.load(boost::memory_order_relaxed))) +#endif { // Compose a view of attribute values (unfrozen, yet) attribute_value_set attr_values(boost::forward< SourceAttributesT >(source_attributes), tsd->m_thread_attributes, m_global_attributes); @@ -356,14 +383,6 @@ struct core::implementation : } } } -#if !defined(BOOST_LOG_NO_THREADS) - catch (thread_interrupted&) - { - if (rec_impl) - rec_impl->destroy(); - throw; - } -#endif // !defined(BOOST_LOG_NO_THREADS) catch (...) { if (rec_impl) @@ -397,7 +416,7 @@ struct core::implementation : #else thread_data* p = m_thread_data.get(); #endif - if (!p) + if (BOOST_UNLIKELY(!p)) { init_thread_data(); #if defined(BOOST_LOG_USE_COMPILER_TLS) @@ -422,7 +441,7 @@ struct core::implementation : BOOST_LOG_EXPR_IF_MT(scoped_write_lock lock(m_mutex);) if (!m_thread_data.get()) { - log::aux::unique_ptr< thread_data > p(new thread_data()); + std::unique_ptr< thread_data > p(new thread_data()); m_thread_data.reset(p.get()); #if defined(BOOST_LOG_USE_COMPILER_TLS) m_thread_data_cache = p.release(); @@ -450,12 +469,6 @@ struct core::implementation : impl->push_back_accepting_sink(sink); } } -#if !defined(BOOST_LOG_NO_THREADS) - catch (thread_interrupted&) - { - throw; - } -#endif // !defined(BOOST_LOG_NO_THREADS) catch (...) { if (m_exception_handler.empty()) @@ -492,18 +505,23 @@ BOOST_LOG_API core_ptr core::get() //! The method enables or disables logging and returns the previous state of logging flag BOOST_LOG_API bool core::set_logging_enabled(bool enabled) { - BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);) +#if !defined(BOOST_LOG_NO_THREADS) + return m_impl->m_enabled.exchange(enabled, boost::memory_order_relaxed); +#else const bool old_value = m_impl->m_enabled; m_impl->m_enabled = enabled; return old_value; +#endif } //! The method allows to detect if logging is enabled BOOST_LOG_API bool core::get_logging_enabled() const { - // Should have a read barrier here, but for performance reasons it is omitted. - // The function should be used as a quick check and doesn't need to be reliable. +#if !defined(BOOST_LOG_NO_THREADS) + return m_impl->m_enabled.load(boost::memory_order_relaxed); +#else return m_impl->m_enabled; +#endif } //! The method adds a new sink @@ -617,19 +635,29 @@ BOOST_LOG_API void core::flush() { // Acquire exclusive lock to prevent any logging attempts while flushing BOOST_LOG_EXPR_IF_MT(implementation::scoped_write_lock lock(m_impl->m_mutex);) - implementation::sink_list::iterator it = m_impl->m_sinks.begin(), end = m_impl->m_sinks.end(); - for (; it != end; ++it) + if (BOOST_LIKELY(!m_impl->m_sinks.empty())) { - try + implementation::sink_list::iterator it = m_impl->m_sinks.begin(), end = m_impl->m_sinks.end(); + for (; it != end; ++it) { - it->get()->flush(); + try + { + it->get()->flush(); + } + catch (...) + { + if (m_impl->m_exception_handler.empty()) + throw; + m_impl->m_exception_handler(); + } } -#if !defined(BOOST_LOG_NO_THREADS) - catch (thread_interrupted&) + } + else + { + try { - throw; + m_impl->m_default_sink->flush(); } -#endif // !defined(BOOST_LOG_NO_THREADS) catch (...) { if (m_impl->m_exception_handler.empty()) @@ -722,12 +750,6 @@ BOOST_LOG_API void core::push_record_move(record& rec) else break; } -#if !defined(BOOST_LOG_NO_THREADS) - catch (thread_interrupted&) - { - throw; - } -#endif // !defined(BOOST_LOG_NO_THREADS) catch (...) { // Lock the core to be safe against any attribute or sink set modifications @@ -742,12 +764,6 @@ BOOST_LOG_API void core::push_record_move(record& rec) end->swap(*it); } } -#if !defined(BOOST_LOG_NO_THREADS) - catch (thread_interrupted&) - { - throw; - } -#endif // !defined(BOOST_LOG_NO_THREADS) catch (...) { // Lock the core to be safe against any attribute or sink set modifications diff --git a/src/default_sink.cpp b/src/default_sink.cpp index 9258754658..72d0aa2173 100644 --- a/src/default_sink.cpp +++ b/src/default_sink.cpp @@ -17,7 +17,7 @@ #include #include #if !defined(BOOST_LOG_NO_THREADS) -#include +#include #include #endif #include @@ -207,13 +207,14 @@ bool default_sink::will_consume(attribute_value_set const&) void default_sink::consume(record_view const& rec) { - BOOST_LOG_EXPR_IF_MT(lock_guard< mutex_type > lock(m_mutex);) + BOOST_LOG_EXPR_IF_MT(std::lock_guard< mutex_type > lock(m_mutex);) m_message_visitor(m_message_name, rec.attribute_values(), message_printer(m_severity_extractor(m_severity_name, rec).get())); + std::fflush(stdout); } void default_sink::flush() { - BOOST_LOG_EXPR_IF_MT(lock_guard< mutex_type > lock(m_mutex);) + BOOST_LOG_EXPR_IF_MT(std::lock_guard< mutex_type > lock(m_mutex);) std::fflush(stdout); } diff --git a/src/default_sink.hpp b/src/default_sink.hpp index bdcd3f46f0..a89b862aaa 100644 --- a/src/default_sink.hpp +++ b/src/default_sink.hpp @@ -25,7 +25,7 @@ #include #include #if !defined(BOOST_LOG_NO_THREADS) -#include +#include #endif #include @@ -47,7 +47,7 @@ class default_sink : { private: #if !defined(BOOST_LOG_NO_THREADS) - typedef mutex mutex_type; + typedef std::mutex mutex_type; mutex_type m_mutex; #endif attribute_name const m_severity_name, m_message_name; @@ -56,10 +56,10 @@ class default_sink : public: default_sink(); - ~default_sink(); - bool will_consume(attribute_value_set const&); - void consume(record_view const& rec); - void flush(); + ~default_sink() BOOST_OVERRIDE; + bool will_consume(attribute_value_set const&) BOOST_OVERRIDE; + void consume(record_view const& rec) BOOST_OVERRIDE; + void flush() BOOST_OVERRIDE; }; } // namespace aux diff --git a/src/dump.cpp b/src/dump.cpp index e23cb2b43b..5bdba7c8c3 100644 --- a/src/dump.cpp +++ b/src/dump.cpp @@ -1,5 +1,5 @@ /* - * Copyright Andrey Semashev 2007 - 2015. + * Copyright Andrey Semashev 2007 - 2021. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) @@ -18,8 +18,8 @@ #include #include #if defined(_MSC_VER) && (defined(BOOST_LOG_USE_SSSE3) || defined(BOOST_LOG_USE_AVX2)) -#include #include // __cpuid +#include // _xgetbv #endif #include @@ -38,6 +38,14 @@ extern dump_data_char16_t dump_data_char16_ssse3; #if !defined(BOOST_NO_CXX11_CHAR32_T) extern dump_data_char32_t dump_data_char32_ssse3; #endif +extern dump_data_char_t dump_data_char_ssse3_slow_pshufb; +extern dump_data_wchar_t dump_data_wchar_ssse3_slow_pshufb; +#if !defined(BOOST_NO_CXX11_CHAR16_T) +extern dump_data_char16_t dump_data_char16_ssse3_slow_pshufb; +#endif +#if !defined(BOOST_NO_CXX11_CHAR32_T) +extern dump_data_char32_t dump_data_char32_ssse3_slow_pshufb; +#endif #endif #if defined(BOOST_LOG_USE_AVX2) extern dump_data_char_t dump_data_char_avx2; @@ -52,7 +60,7 @@ extern dump_data_char32_t dump_data_char32_avx2; enum { stride = 256 }; -extern const char g_hex_char_table[2][16] = +BOOST_ALIGNMENT(16) extern const char g_hex_char_table[2][16] = { { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }, { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' } @@ -129,6 +137,7 @@ struct function_pointer_initializer cpuid(eax, ebx, ecx, edx); const uint32_t max_cpuid_function = eax; + const uint32_t cpu_vendor[3u] = { ebx, edx, ecx }; if (max_cpuid_function >= 1) { eax = 1; @@ -137,7 +146,21 @@ struct function_pointer_initializer // Check for SSSE3 support if (ecx & (1u << 9)) - enable_ssse3(); + { + const uint32_t family = ((eax >> 8) & 0x0000000F) + ((eax >> 20) & 0x000000FF); + const uint32_t model = ((eax >> 4) & 0x0000000F) | ((eax >> 12) & 0x000000F0); + + // Check if the CPU has slow pshufb. Some old Intel Atoms prior to Silvermont. + if (cpu_vendor[0] == 0x756e6547 && cpu_vendor[1] == 0x49656e69 && cpu_vendor[2] == 0x6c65746e && + family == 6 && (model == 28 || model == 38 || model == 39 || model == 53 || model == 54)) + { + enable_ssse3_slow_pshufb(); + } + else + { + enable_ssse3(); + } + } #if defined(BOOST_LOG_USE_AVX2) if (max_cpuid_function >= 7) @@ -156,20 +179,9 @@ struct function_pointer_initializer : "=a" (eax), "=d" (edx) : "c" (0) ); - mmstate = (eax & 6U) == 6U; -#elif defined(BOOST_WINDOWS) - // MSVC does not have an intrinsic for xgetbv, we have to query OS - boost::winapi::HMODULE_ hKernel32 = boost::winapi::GetModuleHandleW(L"kernel32.dll"); - if (hKernel32) - { - typedef uint64_t (BOOST_WINAPI_WINAPI_CC* get_enabled_extended_features_t)(uint64_t); - get_enabled_extended_features_t get_enabled_extended_features = (get_enabled_extended_features_t)boost::winapi::get_proc_address(hKernel32, "GetEnabledExtendedFeatures"); - if (get_enabled_extended_features) - { - // XSTATE_MASK_LEGACY_SSE | XSTATE_MASK_GSSE == 6 - mmstate = get_enabled_extended_features(6u) == 6u; - } - } + mmstate = (eax & 6u) == 6u; +#elif defined(_MSC_VER) + mmstate = (_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 6u) == 6u; #endif if (mmstate) @@ -179,7 +191,7 @@ struct function_pointer_initializer ebx = ecx = edx = 0; cpuid(eax, ebx, ecx, edx); - if (ebx & (1U << 5)) + if (ebx & (1u << 5)) enable_avx2(); } } @@ -233,6 +245,18 @@ struct function_pointer_initializer #endif } + static void enable_ssse3_slow_pshufb() + { + dump_data_char = &dump_data_char_ssse3_slow_pshufb; + dump_data_wchar = &dump_data_wchar_ssse3_slow_pshufb; +#if !defined(BOOST_NO_CXX11_CHAR16_T) + dump_data_char16 = &dump_data_char16_ssse3_slow_pshufb; +#endif +#if !defined(BOOST_NO_CXX11_CHAR32_T) + dump_data_char32 = &dump_data_char32_ssse3_slow_pshufb; +#endif + } + static void enable_ssse3() { dump_data_char = &dump_data_char_ssse3; diff --git a/src/dump_avx2.cpp b/src/dump_avx2.cpp index 4ab12500fa..5b3a44045f 100644 --- a/src/dump_avx2.cpp +++ b/src/dump_avx2.cpp @@ -1,5 +1,5 @@ /* - * Copyright Andrey Semashev 2007 - 2015. + * Copyright Andrey Semashev 2007 - 2021. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) @@ -47,12 +47,47 @@ enum stride = packs_per_stride * 32 }; +template< typename CharT > +BOOST_FORCEINLINE void store_characters(__m128i mm_chars, CharT* buf) +{ + switch (sizeof(CharT)) + { + case 1: + _mm_store_si128(reinterpret_cast< __m128i* >(buf), mm_chars); + break; + + case 2: + _mm256_store_si256(reinterpret_cast< __m256i* >(buf), _mm256_cvtepu8_epi16(mm_chars)); + break; + + case 4: + { + __m128i mm = _mm_unpackhi_epi64(mm_chars, mm_chars); + _mm256_store_si256(reinterpret_cast< __m256i* >(buf), _mm256_cvtepu8_epi32(mm_chars)); + _mm256_store_si256(reinterpret_cast< __m256i* >(buf) + 1, _mm256_cvtepu8_epi32(mm)); + } + break; + } +} + +template< typename CharT > +BOOST_FORCEINLINE void store_characters_x3(__m256i mm_chars1, __m256i mm_chars2, __m256i mm_chars3, CharT* buf) +{ + store_characters(_mm256_castsi256_si128(mm_chars1), buf); + store_characters(_mm256_castsi256_si128(mm_chars2), buf + 16); + store_characters(_mm256_castsi256_si128(mm_chars3), buf + 32); + store_characters(_mm256_extracti128_si256(mm_chars1, 1), buf + 48); + store_characters(_mm256_extracti128_si256(mm_chars2, 1), buf + 64); + store_characters(_mm256_extracti128_si256(mm_chars3, 1), buf + 80); +} + union ymm_constant { uint8_t as_bytes[32]; __m256i as_mm; BOOST_FORCEINLINE operator __m256i () const { return as_mm; } + BOOST_FORCEINLINE operator __m128i () const { return _mm256_castsi256_si128(as_mm); } }; static const ymm_constant mm_shuffle_pattern1 = {{ 0x80, 0, 1, 0x80, 2, 3, 0x80, 4, 5, 0x80, 6, 7, 0x80, 8, 9, 0x80, 0x80, 0, 1, 0x80, 2, 3, 0x80, 4, 5, 0x80, 6, 7, 0x80, 8, 9, 0x80 }}; @@ -63,24 +98,28 @@ static const ymm_constant mm_shuffle_pattern13 = {{ 0x80, 0, 1, 0x80, 2, 3, 0x80 #if defined(BOOST_LOG_AUX_X86_64) // x86-64 architecture has more registers which we can utilize to pass constants -#define BOOST_LOG_AUX_MM_CONSTANT_ARGS_DECL __m256i mm_15, __m256i mm_9, __m256i mm_char_0, __m256i mm_char_space, -#define BOOST_LOG_AUX_MM_CONSTANT_ARGS mm_15, mm_9, mm_char_0, mm_char_space, -#define BOOST_LOG_AUX_MM_CONSTANTS \ +#define BOOST_LOG_AUX_MM256_CONSTANT_ARGS_DECL __m256i mm_15, __m256i mm_char_space, +#define BOOST_LOG_AUX_MM256_CONSTANT_ARGS mm_15, mm_char_space, +#define BOOST_LOG_AUX_MM256_CONSTANTS \ const __m256i mm_15 = _mm256_set1_epi32(0x0F0F0F0F);\ - const __m256i mm_9 = _mm256_set1_epi32(0x09090909);\ - const __m256i mm_char_0 = _mm256_set1_epi32(0x30303030);\ const __m256i mm_char_space = _mm256_set1_epi32(0x20202020); +#define BOOST_LOG_AUX_MM128_CONSTANT_ARGS_DECL __m128i mm_15, __m128i mm_char_space, +#define BOOST_LOG_AUX_MM128_CONSTANT_ARGS mm_15, mm_char_space, +#define BOOST_LOG_AUX_MM128_CONSTANTS \ + const __m128i mm_15 = _mm_set1_epi32(0x0F0F0F0F);\ + const __m128i mm_char_space = _mm_set1_epi32(0x20202020); #else // MSVC in 32-bit mode is not able to pass all constants to dump_pack, and is also not able to align them on the stack, so we have to fetch them from global constants static const ymm_constant mm_15 = {{ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F }}; -static const ymm_constant mm_9 = {{ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09 }}; -static const ymm_constant mm_char_0 = {{ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30 }}; static const ymm_constant mm_char_space = {{ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 }}; -#define BOOST_LOG_AUX_MM_CONSTANT_ARGS_DECL -#define BOOST_LOG_AUX_MM_CONSTANT_ARGS -#define BOOST_LOG_AUX_MM_CONSTANTS +#define BOOST_LOG_AUX_MM256_CONSTANT_ARGS_DECL +#define BOOST_LOG_AUX_MM256_CONSTANT_ARGS +#define BOOST_LOG_AUX_MM256_CONSTANTS +#define BOOST_LOG_AUX_MM128_CONSTANT_ARGS_DECL +#define BOOST_LOG_AUX_MM128_CONSTANT_ARGS +#define BOOST_LOG_AUX_MM128_CONSTANTS #endif @@ -91,8 +130,8 @@ static const ymm_constant mm_char_space = {{ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, */ static BOOST_FORCEINLINE void dump_pack ( - BOOST_LOG_AUX_MM_CONSTANT_ARGS_DECL - __m256i mm_char_10_to_a, __m256i mm_input, + BOOST_LOG_AUX_MM256_CONSTANT_ARGS_DECL + __m256i mm_char_table, __m256i mm_input, __m256i& mm_output1, __m256i& mm_output2, __m256i& mm_output3 ) { @@ -101,16 +140,8 @@ static BOOST_FORCEINLINE void dump_pack __m256i mm_input_lo = _mm256_and_si256(mm_input, mm_15); // Stringize each of the halves - __m256i mm_addend_hi = _mm256_cmpgt_epi8(mm_input_hi, mm_9); - __m256i mm_addend_lo = _mm256_cmpgt_epi8(mm_input_lo, mm_9); - mm_addend_hi = _mm256_and_si256(mm_char_10_to_a, mm_addend_hi); - mm_addend_lo = _mm256_and_si256(mm_char_10_to_a, mm_addend_lo); - - mm_input_hi = _mm256_add_epi8(mm_input_hi, mm_char_0); - mm_input_lo = _mm256_add_epi8(mm_input_lo, mm_char_0); - - mm_input_hi = _mm256_add_epi8(mm_input_hi, mm_addend_hi); - mm_input_lo = _mm256_add_epi8(mm_input_lo, mm_addend_lo); + mm_input_hi = _mm256_shuffle_epi8(mm_char_table, mm_input_hi); + mm_input_lo = _mm256_shuffle_epi8(mm_char_table, mm_input_lo); // Join them back together __m256i mm_1 = _mm256_unpacklo_epi8(mm_input_hi, mm_input_lo); @@ -131,65 +162,33 @@ static BOOST_FORCEINLINE void dump_pack //! Dumps a pack of input data into a string of 8 bit ASCII characters static BOOST_FORCEINLINE void dump_pack ( - BOOST_LOG_AUX_MM_CONSTANT_ARGS_DECL - __m256i mm_char_10_to_a, __m128i mm_input, + BOOST_LOG_AUX_MM128_CONSTANT_ARGS_DECL + __m128i mm_char_table, __m128i mm_input, __m128i& mm_output1, __m128i& mm_output2, __m128i& mm_output3 ) { // Split half-bytes - __m128i mm_input_hi = _mm_srli_epi16(mm_input, 4); - __m256i mm = _mm256_inserti128_si256(_mm256_castsi128_si256(_mm_unpacklo_epi8(mm_input_hi, mm_input)), _mm_unpackhi_epi8(mm_input_hi, mm_input), 1); - mm = _mm256_and_si256(mm, mm_15); + __m128i mm_input_hi = _mm_and_si128(_mm_srli_epi16(mm_input, 4), mm_15); + __m128i mm_input_lo = _mm_and_si128(mm_input, mm_15); - // Stringize the halves - __m256i mm_addend = _mm256_cmpgt_epi8(mm, mm_9); - mm_addend = _mm256_and_si256(mm_char_10_to_a, mm_addend); + // Stringize each of the halves + mm_input_hi = _mm_shuffle_epi8(mm_char_table, mm_input_hi); + mm_input_lo = _mm_shuffle_epi8(mm_char_table, mm_input_lo); - mm = _mm256_add_epi8(mm, mm_char_0); - mm = _mm256_add_epi8(mm, mm_addend); + // Join them back together + __m128i mm_1 = _mm_unpacklo_epi8(mm_input_hi, mm_input_lo); + __m128i mm_2 = _mm_unpackhi_epi8(mm_input_hi, mm_input_lo); // Insert spaces between stringized bytes: - __m256i mm_out13 = _mm256_shuffle_epi8(mm, mm_shuffle_pattern13.as_mm); - __m128i mm_out2 = _mm_shuffle_epi8(_mm_alignr_epi8(_mm256_extracti128_si256(mm, 1), _mm256_castsi256_si128(mm), 10), _mm256_castsi256_si128(mm_shuffle_pattern2.as_mm)); - - mm_out13 = _mm256_max_epu8(mm_out13, mm_char_space); - mm_output2 = _mm_max_epu8(mm_out2, _mm256_castsi256_si128(mm_char_space)); - mm_output1 = _mm256_castsi256_si128(mm_out13); - mm_output3 = _mm256_extracti128_si256(mm_out13, 1); -} - -template< typename CharT > -BOOST_FORCEINLINE void store_characters(__m128i mm_chars, CharT* buf) -{ - switch (sizeof(CharT)) - { - case 1: - _mm_store_si128(reinterpret_cast< __m128i* >(buf), mm_chars); - break; - - case 2: - _mm256_store_si256(reinterpret_cast< __m256i* >(buf), _mm256_cvtepu8_epi16(mm_chars)); - break; - - case 4: - { - __m128i mm = _mm_unpackhi_epi64(mm_chars, mm_chars); - _mm256_store_si256(reinterpret_cast< __m256i* >(buf), _mm256_cvtepu8_epi32(mm_chars)); - _mm256_store_si256(reinterpret_cast< __m256i* >(buf) + 1, _mm256_cvtepu8_epi32(mm)); - } - break; - } -} + // |0123456789abcdef|0123456789abcdef| + // | 01 23 45 67 89 |ab cd ef 01 23 4|5 67 89 ab cd ef| + mm_output1 = _mm_shuffle_epi8(mm_1, mm_shuffle_pattern1); + mm_output2 = _mm_shuffle_epi8(_mm_alignr_epi8(mm_2, mm_1, 10), mm_shuffle_pattern2); + mm_output3 = _mm_shuffle_epi8(mm_2, mm_shuffle_pattern3); -template< typename CharT > -BOOST_FORCEINLINE void store_characters_x3(__m256i mm_chars1, __m256i mm_chars2, __m256i mm_chars3, CharT* buf) -{ - store_characters(_mm256_castsi256_si128(mm_chars1), buf); - store_characters(_mm256_castsi256_si128(mm_chars2), buf + 16); - store_characters(_mm256_castsi256_si128(mm_chars3), buf + 32); - store_characters(_mm256_extracti128_si256(mm_chars1, 1), buf + 48); - store_characters(_mm256_extracti128_si256(mm_chars2, 1), buf + 64); - store_characters(_mm256_extracti128_si256(mm_chars3, 1), buf + 80); + mm_output1 = _mm_max_epu8(mm_output1, mm_char_space); + mm_output2 = _mm_max_epu8(mm_output2, mm_char_space); + mm_output3 = _mm_max_epu8(mm_output3, mm_char_space); } template< typename CharT > @@ -203,11 +202,16 @@ BOOST_FORCEINLINE void dump_data_avx2(const void* data, std::size_t size, std::b char_type* buf_begin = buf + 1u; // skip the first space of the first chunk char_type* buf_end = buf + stride * 3u; - __m256i mm_char_10_to_a; - if (strm.flags() & std::ios_base::uppercase) - mm_char_10_to_a = _mm256_set1_epi32(0x07070707); // '9' is 0x39 and 'A' is 0x41 in ASCII, so we have to add 0x07 to 0x3A to get uppercase letters - else - mm_char_10_to_a = _mm256_set1_epi32(0x27272727); // ...and 'a' is 0x61, which means we have to add 0x27 to 0x3A to get lowercase letters + const char* const char_table = g_hex_char_table[(strm.flags() & std::ios_base::uppercase) != 0]; +#if defined(__GNUC__) && ((defined(BOOST_GCC) && BOOST_GCC < 40900) || (defined(BOOST_CLANG) && BOOST_CLANG_VERSION < 40000)) + // gcc 4.7 is missing _mm256_broadcastsi128_si256 declaration in immintrin.h. + // gcc 4.8 generates vmovdqu+vinserti128 instead of a single vbroadcasti128. + // clang up until 4.0 generates vmovdqu+vinserti128 or worse. + __m256i mm_char_table; + __asm__("vbroadcasti128 %1, %0" : "=x" (mm_char_table) : "m" (*reinterpret_cast< const __m128i* >(char_table))); +#else + const __m256i mm_char_table = _mm256_broadcastsi128_si256(_mm_loadu_si128(reinterpret_cast< const __m128i* >(char_table))); +#endif // First, check the input alignment. Also, if we can dump the whole data in one go, do it right away. It turns out to be faster than splitting // the work between prealign and tail part. It is also a fairly common case since on most platforms memory is not aligned to 32 bytes (i.e. prealign is often needed). @@ -215,15 +219,15 @@ BOOST_FORCEINLINE void dump_data_avx2(const void* data, std::size_t size, std::b const std::size_t prealign_size = size == 32u ? static_cast< std::size_t >(32u) : static_cast< std::size_t >((32u - ((uintptr_t)p & 31u)) & 31u); if (prealign_size) { - __m256i mm_input = _mm256_lddqu_si256(reinterpret_cast< const __m256i* >(p)); - BOOST_LOG_AUX_MM_CONSTANTS + __m256i mm_input = _mm256_loadu_si256(reinterpret_cast< const __m256i* >(p)); + BOOST_LOG_AUX_MM256_CONSTANTS __m256i mm_output1, mm_output2, mm_output3; - dump_pack(BOOST_LOG_AUX_MM_CONSTANT_ARGS mm_char_10_to_a, mm_input, mm_output1, mm_output2, mm_output3); + dump_pack(BOOST_LOG_AUX_MM256_CONSTANT_ARGS mm_char_table, mm_input, mm_output1, mm_output2, mm_output3); store_characters_x3(mm_output1, mm_output2, mm_output3, buf); - _mm256_zeroall(); // need to zero all ymm registers to avoid register spills/restores the compler generates around the function call + _mm256_zeroall(); // need to zero all ymm registers to avoid register spills/restores the compiler generates around the function call strm.write(buf_begin, prealign_size * 3u - 1u); buf_begin = buf; @@ -236,18 +240,18 @@ BOOST_FORCEINLINE void dump_data_avx2(const void* data, std::size_t size, std::b for (std::size_t i = 0; i < stride_count; ++i) { char_type* b = buf; - BOOST_LOG_AUX_MM_CONSTANTS + BOOST_LOG_AUX_MM256_CONSTANTS for (unsigned int j = 0; j < packs_per_stride; ++j, b += 3u * 32u, p += 32u) { __m256i mm_input = _mm256_load_si256(reinterpret_cast< const __m256i* >(p)); __m256i mm_output1, mm_output2, mm_output3; - dump_pack(BOOST_LOG_AUX_MM_CONSTANT_ARGS mm_char_10_to_a, mm_input, mm_output1, mm_output2, mm_output3); + dump_pack(BOOST_LOG_AUX_MM256_CONSTANT_ARGS mm_char_table, mm_input, mm_output1, mm_output2, mm_output3); store_characters_x3(mm_output1, mm_output2, mm_output3, b); } - _mm256_zeroall(); // need to zero all ymm registers to avoid register spills/restores the compler generates around the function call + _mm256_zeroall(); // need to zero all ymm registers to avoid register spills/restores the compiler generates around the function call strm.write(buf_begin, buf_end - buf_begin); buf_begin = buf; } @@ -258,10 +262,10 @@ BOOST_FORCEINLINE void dump_data_avx2(const void* data, std::size_t size, std::b while (tail_size >= 16u) { __m128i mm_input = _mm_load_si128(reinterpret_cast< const __m128i* >(p)); - BOOST_LOG_AUX_MM_CONSTANTS + BOOST_LOG_AUX_MM128_CONSTANTS __m128i mm_output1, mm_output2, mm_output3; - dump_pack(BOOST_LOG_AUX_MM_CONSTANT_ARGS mm_char_10_to_a, mm_input, mm_output1, mm_output2, mm_output3); + dump_pack(BOOST_LOG_AUX_MM128_CONSTANT_ARGS _mm256_castsi256_si128(mm_char_table), mm_input, mm_output1, mm_output2, mm_output3); store_characters(mm_output1, b); store_characters(mm_output2, b + 16u); @@ -272,8 +276,7 @@ BOOST_FORCEINLINE void dump_data_avx2(const void* data, std::size_t size, std::b tail_size -= 16u; } - _mm256_zeroall(); // need to zero all ymm registers to avoid register spills/restores the compler generates around the function call - const char* const char_table = g_hex_char_table[(strm.flags() & std::ios_base::uppercase) != 0]; + _mm256_zeroall(); // need to zero all ymm registers to avoid register spills/restores the compiler generates around the function call for (unsigned int i = 0; i < tail_size; ++i, ++p, b += 3u) { uint32_t n = *p; @@ -286,6 +289,12 @@ BOOST_FORCEINLINE void dump_data_avx2(const void* data, std::size_t size, std::b } } +#undef BOOST_LOG_AUX_MM256_CONSTANT_ARGS_DECL +#undef BOOST_LOG_AUX_MM256_CONSTANT_ARGS +#undef BOOST_LOG_AUX_MM256_CONSTANTS +#undef BOOST_LOG_AUX_MM128_CONSTANT_ARGS_DECL +#undef BOOST_LOG_AUX_MM128_CONSTANT_ARGS + } // namespace void dump_data_char_avx2(const void* data, std::size_t size, std::basic_ostream< char >& strm) diff --git a/src/dump_ssse3.cpp b/src/dump_ssse3.cpp index 1325b49c04..84ef2054ad 100644 --- a/src/dump_ssse3.cpp +++ b/src/dump_ssse3.cpp @@ -1,5 +1,5 @@ /* - * Copyright Andrey Semashev 2007 - 2015. + * Copyright Andrey Semashev 2007 - 2021. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) @@ -47,6 +47,37 @@ enum stride = packs_per_stride * 16 }; +template< typename CharT > +BOOST_FORCEINLINE void store_characters(__m128i mm_chars, CharT* buf) +{ + switch (sizeof(CharT)) + { + case 1: + _mm_store_si128(reinterpret_cast< __m128i* >(buf), mm_chars); + break; + + case 2: + { + __m128i mm_0 = _mm_setzero_si128(); + _mm_store_si128(reinterpret_cast< __m128i* >(buf), _mm_unpacklo_epi8(mm_chars, mm_0)); + _mm_store_si128(reinterpret_cast< __m128i* >(buf) + 1, _mm_unpackhi_epi8(mm_chars, mm_0)); + } + break; + + case 4: + { + __m128i mm_0 = _mm_setzero_si128(); + __m128i mm = _mm_unpacklo_epi8(mm_chars, mm_0); + _mm_store_si128(reinterpret_cast< __m128i* >(buf), _mm_unpacklo_epi16(mm, mm_0)); + _mm_store_si128(reinterpret_cast< __m128i* >(buf) + 1, _mm_unpackhi_epi16(mm, mm_0)); + mm = _mm_unpackhi_epi8(mm_chars, mm_0); + _mm_store_si128(reinterpret_cast< __m128i* >(buf) + 2, _mm_unpacklo_epi16(mm, mm_0)); + _mm_store_si128(reinterpret_cast< __m128i* >(buf) + 3, _mm_unpackhi_epi16(mm, mm_0)); + } + break; + } +} + union xmm_constant { uint8_t as_bytes[16]; @@ -59,6 +90,158 @@ static const xmm_constant mm_shuffle_pattern1 = {{ 0x80, 0, 1, 0x80, 2, 3, 0x80, static const xmm_constant mm_shuffle_pattern2 = {{ 0, 1, 0x80, 2, 3, 0x80, 4, 5, 0x80, 6, 7, 0x80, 8, 9, 0x80, 10 }}; static const xmm_constant mm_shuffle_pattern3 = {{ 5, 0x80, 6, 7, 0x80, 8, 9, 0x80, 10, 11, 0x80, 12, 13, 0x80, 14, 15 }}; +#if defined(BOOST_LOG_AUX_X86_64) + +// x86-64 architecture has more registers which we can utilize to pass constants +#define BOOST_LOG_AUX_MM_CONSTANT_ARGS_DECL __m128i mm_15, __m128i mm_char_space, +#define BOOST_LOG_AUX_MM_CONSTANT_ARGS mm_15, mm_char_space, +#define BOOST_LOG_AUX_MM_CONSTANTS \ + const __m128i mm_15 = _mm_set1_epi32(0x0F0F0F0F);\ + const __m128i mm_char_space = _mm_set1_epi32(0x20202020); + +#else + +// MSVC in 32-bit mode is not able to pass all constants to dump_pack, and is also not able to align them on the stack, so we have to fetch them from global constants +static const xmm_constant mm_15 = {{ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F }}; +static const xmm_constant mm_char_space = {{ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 }}; +#define BOOST_LOG_AUX_MM_CONSTANT_ARGS_DECL +#define BOOST_LOG_AUX_MM_CONSTANT_ARGS +#define BOOST_LOG_AUX_MM_CONSTANTS + +#endif + +//! Dumps a pack of input data into a string of 8 bit ASCII characters +static BOOST_FORCEINLINE void dump_pack +( + BOOST_LOG_AUX_MM_CONSTANT_ARGS_DECL + __m128i mm_char_table, __m128i mm_input, + __m128i& mm_output1, __m128i& mm_output2, __m128i& mm_output3 +) +{ + // Split half-bytes + __m128i mm_input_hi = _mm_and_si128(_mm_srli_epi16(mm_input, 4), mm_15); + __m128i mm_input_lo = _mm_and_si128(mm_input, mm_15); + + // Stringize each of the halves + mm_input_hi = _mm_shuffle_epi8(mm_char_table, mm_input_hi); + mm_input_lo = _mm_shuffle_epi8(mm_char_table, mm_input_lo); + + // Join them back together + __m128i mm_1 = _mm_unpacklo_epi8(mm_input_hi, mm_input_lo); + __m128i mm_2 = _mm_unpackhi_epi8(mm_input_hi, mm_input_lo); + + // Insert spaces between stringized bytes: + // |0123456789abcdef|0123456789abcdef| + // | 01 23 45 67 89 |ab cd ef 01 23 4|5 67 89 ab cd ef| + mm_output1 = _mm_shuffle_epi8(mm_1, mm_shuffle_pattern1.as_mm); + mm_output2 = _mm_shuffle_epi8(_mm_alignr_epi8(mm_2, mm_1, 10), mm_shuffle_pattern2.as_mm); + mm_output3 = _mm_shuffle_epi8(mm_2, mm_shuffle_pattern3.as_mm); + + mm_output1 = _mm_max_epu8(mm_output1, mm_char_space); + mm_output2 = _mm_max_epu8(mm_output2, mm_char_space); + mm_output3 = _mm_max_epu8(mm_output3, mm_char_space); +} + +template< typename CharT > +BOOST_FORCEINLINE void dump_data_ssse3(const void* data, std::size_t size, std::basic_ostream< CharT >& strm) +{ + typedef CharT char_type; + + char_type buf_storage[stride * 3u + 16u]; + // Align the temporary buffer at 16 bytes + char_type* const buf = reinterpret_cast< char_type* >((uint8_t*)buf_storage + (16u - (((uintptr_t)(char_type*)buf_storage) & 15u))); + char_type* buf_begin = buf + 1u; // skip the first space of the first chunk + char_type* buf_end = buf + stride * 3u; + + const char* const char_table = g_hex_char_table[(strm.flags() & std::ios_base::uppercase) != 0]; + const __m128i mm_char_table = +#if !defined(BOOST_NO_ALIGNMENT) + _mm_load_si128(reinterpret_cast< const __m128i* >(char_table)); +#else + _mm_lddqu_si128(reinterpret_cast< const __m128i* >(char_table)); +#endif + + // First, check the input alignment + const uint8_t* p = static_cast< const uint8_t* >(data); + const std::size_t prealign_size = ((16u - ((uintptr_t)p & 15u)) & 15u); + if (BOOST_UNLIKELY(prealign_size > 0)) + { + __m128i mm_input = _mm_lddqu_si128(reinterpret_cast< const __m128i* >(p)); + BOOST_LOG_AUX_MM_CONSTANTS + + __m128i mm_output1, mm_output2, mm_output3; + dump_pack(BOOST_LOG_AUX_MM_CONSTANT_ARGS mm_char_table, mm_input, mm_output1, mm_output2, mm_output3); + + store_characters(mm_output1, buf); + store_characters(mm_output2, buf + 16u); + store_characters(mm_output3, buf + 32u); + + strm.write(buf_begin, prealign_size * 3u - 1u); + + buf_begin = buf; + size -= prealign_size; + p += prealign_size; + } + + const std::size_t stride_count = size / stride; + std::size_t tail_size = size % stride; + for (std::size_t i = 0; i < stride_count; ++i) + { + char_type* b = buf; + BOOST_LOG_AUX_MM_CONSTANTS + + for (unsigned int j = 0; j < packs_per_stride; ++j, b += 3u * 16u, p += 16u) + { + __m128i mm_input = _mm_load_si128(reinterpret_cast< const __m128i* >(p)); + __m128i mm_output1, mm_output2, mm_output3; + dump_pack(BOOST_LOG_AUX_MM_CONSTANT_ARGS mm_char_table, mm_input, mm_output1, mm_output2, mm_output3); + + store_characters(mm_output1, b); + store_characters(mm_output2, b + 16u); + store_characters(mm_output3, b + 32u); + } + + strm.write(buf_begin, buf_end - buf_begin); + buf_begin = buf; + } + + if (BOOST_UNLIKELY(tail_size > 0)) + { + char_type* b = buf; + while (tail_size >= 16u) + { + __m128i mm_input = _mm_load_si128(reinterpret_cast< const __m128i* >(p)); + BOOST_LOG_AUX_MM_CONSTANTS + + __m128i mm_output1, mm_output2, mm_output3; + dump_pack(BOOST_LOG_AUX_MM_CONSTANT_ARGS mm_char_table, mm_input, mm_output1, mm_output2, mm_output3); + + store_characters(mm_output1, b); + store_characters(mm_output2, b + 16u); + store_characters(mm_output3, b + 32u); + + b += 3u * 16u; + p += 16u; + tail_size -= 16u; + } + + for (unsigned int i = 0; i < tail_size; ++i, ++p, b += 3u) + { + uint32_t n = *p; + b[0] = static_cast< char_type >(' '); + b[1] = static_cast< char_type >(char_table[n >> 4]); + b[2] = static_cast< char_type >(char_table[n & 0x0F]); + } + + strm.write(buf_begin, b - buf_begin); + } +} + +#undef BOOST_LOG_AUX_MM_CONSTANT_ARGS_DECL +#undef BOOST_LOG_AUX_MM_CONSTANT_ARGS +#undef BOOST_LOG_AUX_MM_CONSTANTS + + #if defined(BOOST_LOG_AUX_X86_64) // x86-64 architecture has more registers which we can utilize to pass constants @@ -73,10 +256,8 @@ static const xmm_constant mm_shuffle_pattern3 = {{ 5, 0x80, 6, 7, 0x80, 8, 9, 0x #else // MSVC in 32-bit mode is not able to pass all constants to dump_pack, and is also not able to align them on the stack, so we have to fetch them from global constants -static const xmm_constant mm_15 = {{ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F }}; static const xmm_constant mm_9 = {{ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09 }}; static const xmm_constant mm_char_0 = {{ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30 }}; -static const xmm_constant mm_char_space = {{ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 }}; #define BOOST_LOG_AUX_MM_CONSTANT_ARGS_DECL #define BOOST_LOG_AUX_MM_CONSTANT_ARGS #define BOOST_LOG_AUX_MM_CONSTANTS @@ -84,7 +265,7 @@ static const xmm_constant mm_char_space = {{ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, #endif //! Dumps a pack of input data into a string of 8 bit ASCII characters -static BOOST_FORCEINLINE void dump_pack +static BOOST_FORCEINLINE void dump_pack_slow_pshufb ( BOOST_LOG_AUX_MM_CONSTANT_ARGS_DECL __m128i mm_char_10_to_a, __m128i mm_input, @@ -124,38 +305,7 @@ static BOOST_FORCEINLINE void dump_pack } template< typename CharT > -BOOST_FORCEINLINE void store_characters(__m128i mm_chars, CharT* buf) -{ - switch (sizeof(CharT)) - { - case 1: - _mm_store_si128(reinterpret_cast< __m128i* >(buf), mm_chars); - break; - - case 2: - { - __m128i mm_0 = _mm_setzero_si128(); - _mm_store_si128(reinterpret_cast< __m128i* >(buf), _mm_unpacklo_epi8(mm_chars, mm_0)); - _mm_store_si128(reinterpret_cast< __m128i* >(buf) + 1, _mm_unpackhi_epi8(mm_chars, mm_0)); - } - break; - - case 4: - { - __m128i mm_0 = _mm_setzero_si128(); - __m128i mm = _mm_unpacklo_epi8(mm_chars, mm_0); - _mm_store_si128(reinterpret_cast< __m128i* >(buf), _mm_unpacklo_epi16(mm, mm_0)); - _mm_store_si128(reinterpret_cast< __m128i* >(buf) + 1, _mm_unpackhi_epi16(mm, mm_0)); - mm = _mm_unpackhi_epi8(mm_chars, mm_0); - _mm_store_si128(reinterpret_cast< __m128i* >(buf) + 2, _mm_unpacklo_epi16(mm, mm_0)); - _mm_store_si128(reinterpret_cast< __m128i* >(buf) + 3, _mm_unpackhi_epi16(mm, mm_0)); - } - break; - } -} - -template< typename CharT > -BOOST_FORCEINLINE void dump_data_ssse3(const void* data, std::size_t size, std::basic_ostream< CharT >& strm) +BOOST_FORCEINLINE void dump_data_ssse3_slow_pshufb(const void* data, std::size_t size, std::basic_ostream< CharT >& strm) { typedef CharT char_type; @@ -180,7 +330,7 @@ BOOST_FORCEINLINE void dump_data_ssse3(const void* data, std::size_t size, std:: BOOST_LOG_AUX_MM_CONSTANTS __m128i mm_output1, mm_output2, mm_output3; - dump_pack(BOOST_LOG_AUX_MM_CONSTANT_ARGS mm_char_10_to_a, mm_input, mm_output1, mm_output2, mm_output3); + dump_pack_slow_pshufb(BOOST_LOG_AUX_MM_CONSTANT_ARGS mm_char_10_to_a, mm_input, mm_output1, mm_output2, mm_output3); store_characters(mm_output1, buf); store_characters(mm_output2, buf + 16u); @@ -204,7 +354,7 @@ BOOST_FORCEINLINE void dump_data_ssse3(const void* data, std::size_t size, std:: { __m128i mm_input = _mm_load_si128(reinterpret_cast< const __m128i* >(p)); __m128i mm_output1, mm_output2, mm_output3; - dump_pack(BOOST_LOG_AUX_MM_CONSTANT_ARGS mm_char_10_to_a, mm_input, mm_output1, mm_output2, mm_output3); + dump_pack_slow_pshufb(BOOST_LOG_AUX_MM_CONSTANT_ARGS mm_char_10_to_a, mm_input, mm_output1, mm_output2, mm_output3); store_characters(mm_output1, b); store_characters(mm_output2, b + 16u); @@ -224,7 +374,7 @@ BOOST_FORCEINLINE void dump_data_ssse3(const void* data, std::size_t size, std:: BOOST_LOG_AUX_MM_CONSTANTS __m128i mm_output1, mm_output2, mm_output3; - dump_pack(BOOST_LOG_AUX_MM_CONSTANT_ARGS mm_char_10_to_a, mm_input, mm_output1, mm_output2, mm_output3); + dump_pack_slow_pshufb(BOOST_LOG_AUX_MM_CONSTANT_ARGS mm_char_10_to_a, mm_input, mm_output1, mm_output2, mm_output3); store_characters(mm_output1, b); store_characters(mm_output2, b + 16u); @@ -248,6 +398,10 @@ BOOST_FORCEINLINE void dump_data_ssse3(const void* data, std::size_t size, std:: } } +#undef BOOST_LOG_AUX_MM_CONSTANT_ARGS_DECL +#undef BOOST_LOG_AUX_MM_CONSTANT_ARGS +#undef BOOST_LOG_AUX_MM_CONSTANTS + } // namespace void dump_data_char_ssse3(const void* data, std::size_t size, std::basic_ostream< char >& strm) @@ -302,6 +456,58 @@ void dump_data_char32_ssse3(const void* data, std::size_t size, std::basic_ostre } #endif +void dump_data_char_ssse3_slow_pshufb(const void* data, std::size_t size, std::basic_ostream< char >& strm) +{ + if (size >= 16) + { + dump_data_ssse3_slow_pshufb(data, size, strm); + } + else + { + dump_data_generic(data, size, strm); + } +} + +void dump_data_wchar_ssse3_slow_pshufb(const void* data, std::size_t size, std::basic_ostream< wchar_t >& strm) +{ + if (size >= 16) + { + dump_data_ssse3_slow_pshufb(data, size, strm); + } + else + { + dump_data_generic(data, size, strm); + } +} + +#if !defined(BOOST_NO_CXX11_CHAR16_T) +void dump_data_char16_ssse3_slow_pshufb(const void* data, std::size_t size, std::basic_ostream< char16_t >& strm) +{ + if (size >= 16) + { + dump_data_ssse3_slow_pshufb(data, size, strm); + } + else + { + dump_data_generic(data, size, strm); + } +} +#endif + +#if !defined(BOOST_NO_CXX11_CHAR32_T) +void dump_data_char32_ssse3_slow_pshufb(const void* data, std::size_t size, std::basic_ostream< char32_t >& strm) +{ + if (size >= 16) + { + dump_data_ssse3_slow_pshufb(data, size, strm); + } + else + { + dump_data_generic(data, size, strm); + } +} +#endif + } // namespace aux BOOST_LOG_CLOSE_NAMESPACE // namespace log diff --git a/src/event.cpp b/src/event.cpp index ea9eee2fc0..16f3f451bb 100644 --- a/src/event.cpp +++ b/src/event.cpp @@ -1,5 +1,5 @@ /* - * Copyright Andrey Semashev 2007 - 2015. + * Copyright Andrey Semashev 2007 - 2021. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) @@ -23,32 +23,11 @@ #include #include -#if defined(BOOST_LOG_EVENT_USE_FUTEX) +#if defined(BOOST_LOG_EVENT_USE_ATOMIC) -#include -#include -#include -#include #include - -// Some Android NDKs (Google NDK and older Crystax.NET NDK versions) don't define SYS_futex -#if defined(SYS_futex) -#define BOOST_LOG_SYS_FUTEX SYS_futex -#else -#define BOOST_LOG_SYS_FUTEX __NR_futex -#endif - -#if defined(FUTEX_WAIT_PRIVATE) -#define BOOST_LOG_FUTEX_WAIT FUTEX_WAIT_PRIVATE -#else -#define BOOST_LOG_FUTEX_WAIT FUTEX_WAIT -#endif - -#if defined(FUTEX_WAKE_PRIVATE) -#define BOOST_LOG_FUTEX_WAKE FUTEX_WAKE_PRIVATE -#else -#define BOOST_LOG_FUTEX_WAKE FUTEX_WAKE -#endif +#include +#include #elif defined(BOOST_LOG_EVENT_USE_POSIX_SEMAPHORE) @@ -60,11 +39,13 @@ #elif defined(BOOST_LOG_EVENT_USE_WINAPI) #include -#include +#include +#include +#include #else -#include +#include #endif @@ -76,57 +57,27 @@ BOOST_LOG_OPEN_NAMESPACE namespace aux { -#if defined(BOOST_LOG_EVENT_USE_FUTEX) - -//! Default constructor -BOOST_LOG_API futex_based_event::futex_based_event() : m_state(0) -{ -} - -//! Destructor -BOOST_LOG_API futex_based_event::~futex_based_event() -{ -} +#if defined(BOOST_LOG_EVENT_USE_ATOMIC) //! Waits for the object to become signalled -BOOST_LOG_API void futex_based_event::wait() +BOOST_LOG_API void atomic_based_event::wait() { - if (m_state.exchange(0, boost::memory_order_acq_rel) == 0) + while (m_state.exchange(0u, boost::memory_order_acq_rel) == 0u) { - while (true) - { - if (::syscall(BOOST_LOG_SYS_FUTEX, &m_state.storage(), BOOST_LOG_FUTEX_WAIT, 0, NULL, NULL, 0) == 0) - { - // Another thread has set the event while sleeping - break; - } - - const int err = errno; - if (err == EWOULDBLOCK) - { - // Another thread has set the event before sleeping - break; - } - else if (BOOST_UNLIKELY(err != EINTR)) - { - BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to block on the futex", (err)); - } - } - - m_state.store(0, boost::memory_order_relaxed); + m_state.wait(0u, boost::memory_order_relaxed); } } //! Sets the object to a signalled state -BOOST_LOG_API void futex_based_event::set_signalled() +BOOST_LOG_API void atomic_based_event::set_signalled() { - if (m_state.exchange(1, boost::memory_order_release) == 0) + if (m_state.load(boost::memory_order_relaxed) != 0u) { - if (BOOST_UNLIKELY(::syscall(BOOST_LOG_SYS_FUTEX, &m_state.storage(), BOOST_LOG_FUTEX_WAKE, 1, NULL, NULL, 0) < 0)) - { - const int err = errno; - BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to wake threads blocked on the futex", (err)); - } + boost::atomic_thread_fence(boost::memory_order_release); + } + else if (m_state.exchange(1u, boost::memory_order_release) == 0u) + { + m_state.notify_one(); } } @@ -185,47 +136,73 @@ BOOST_LOG_API void sem_based_event::set_signalled() //! Default constructor BOOST_LOG_API winapi_based_event::winapi_based_event() : - m_state(0), - m_event(CreateEventA(NULL, false, false, NULL)) + m_state(0u), + m_event(NULL) { - if (BOOST_UNLIKELY(!m_event)) + if (!m_state.has_native_wait_notify()) { - const DWORD err = GetLastError(); - BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to create Windows event", (err)); + m_event = CreateEventA(NULL, false, false, NULL); + if (BOOST_UNLIKELY(!m_event)) + { + const DWORD err = GetLastError(); + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to create Windows event", (err)); + } } } //! Destructor BOOST_LOG_API winapi_based_event::~winapi_based_event() { - BOOST_VERIFY(CloseHandle(m_event) != 0); + if (!!m_event) + { + BOOST_VERIFY(CloseHandle(m_event) != 0); + } } //! Waits for the object to become signalled BOOST_LOG_API void winapi_based_event::wait() { - // On Windows we assume that memory view is always actual (Intel x86 and x86_64 arch) - if (const_cast< volatile boost::uint32_t& >(m_state) == 0) + if (!m_event) { - if (BOOST_UNLIKELY(WaitForSingleObject(m_event, INFINITE) != 0)) + while (m_state.exchange(0u, boost::memory_order_acq_rel) == 0u) { - const DWORD err = GetLastError(); - BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to block on Windows event", (err)); + m_state.wait(0u, boost::memory_order_relaxed); + } + } + else + { + while (m_state.exchange(0u, boost::memory_order_acq_rel) == 0u) + { + if (BOOST_UNLIKELY(WaitForSingleObject(m_event, INFINITE) != 0)) + { + const DWORD err = GetLastError(); + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to block on Windows event", (err)); + } } } - const_cast< volatile boost::uint32_t& >(m_state) = 0; } //! Sets the object to a signalled state BOOST_LOG_API void winapi_based_event::set_signalled() { - if (BOOST_INTERLOCKED_COMPARE_EXCHANGE(reinterpret_cast< long* >(&m_state), 1, 0) == 0) + if (m_state.load(boost::memory_order_relaxed) != 0u) + { + boost::atomic_thread_fence(boost::memory_order_release); + } + else if (m_state.exchange(1u, boost::memory_order_release) == 0u) { - if (BOOST_UNLIKELY(SetEvent(m_event) == 0)) + if (!m_event) { - const DWORD err = GetLastError(); - const_cast< volatile boost::uint32_t& >(m_state) = 0; - BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to wake the blocked thread", (err)); + m_state.notify_one(); + } + else + { + if (BOOST_UNLIKELY(SetEvent(m_event) == 0)) + { + const DWORD err = GetLastError(); + m_state.store(0u, boost::memory_order_relaxed); + BOOST_LOG_THROW_DESCR_PARAMS(system_error, "Failed to wake the blocked thread", (err)); + } } } } @@ -245,7 +222,7 @@ BOOST_LOG_API generic_event::~generic_event() //! Waits for the object to become signalled BOOST_LOG_API void generic_event::wait() { - boost::unique_lock< boost::mutex > lock(m_mutex); + std::unique_lock< std::mutex > lock(m_mutex); while (!m_state) { m_cond.wait(lock); @@ -256,7 +233,7 @@ BOOST_LOG_API void generic_event::wait() //! Sets the object to a signalled state BOOST_LOG_API void generic_event::set_signalled() { - boost::lock_guard< boost::mutex > lock(m_mutex); + std::lock_guard< std::mutex > lock(m_mutex); if (!m_state) { m_state = true; diff --git a/src/format_parser.cpp b/src/format_parser.cpp index 1215d6b2c6..5b50763c26 100644 --- a/src/format_parser.cpp +++ b/src/format_parser.cpp @@ -42,6 +42,7 @@ BOOST_LOG_API format_description< CharT > parse_format(const CharT* begin, const typedef CharT char_type; typedef format_description< char_type > description; typedef typename encoding< char_type >::type traits; + typedef typename traits::classify_type char_classify_type; const char_type* original_begin = begin; description descr; @@ -74,7 +75,7 @@ BOOST_LOG_API format_description< CharT > parse_format(const CharT* begin, const } // Check if this is a positional argument - if (traits::isdigit(c)) + if (traits::isdigit(static_cast< char_classify_type >(c))) { if (c != static_cast< char_type >('0')) { diff --git a/src/global_logger_storage.cpp b/src/global_logger_storage.cpp index 6ddcc1b7ce..abb5bc346f 100644 --- a/src/global_logger_storage.cpp +++ b/src/global_logger_storage.cpp @@ -18,13 +18,12 @@ #include #include #include +#include #include -#include #include #include #if !defined(BOOST_LOG_NO_THREADS) -#include -#include +#include #endif #include @@ -47,7 +46,7 @@ struct loggers_repository : #if !defined(BOOST_LOG_NO_THREADS) //! Synchronization primitive - mutable mutex m_Mutex; + mutable std::mutex m_Mutex; #endif //! Map of logger holders loggers_map_t m_Loggers; @@ -61,7 +60,7 @@ BOOST_LOG_API shared_ptr< logger_holder_base > global_storage::get_or_init(typei typedef loggers_repository::loggers_map_t loggers_map_t; loggers_repository& repo = loggers_repository::get(); - BOOST_LOG_EXPR_IF_MT(log::aux::exclusive_lock_guard< mutex > lock(repo.m_Mutex);) + BOOST_LOG_EXPR_IF_MT(std::lock_guard< std::mutex > lock(repo.m_Mutex);) loggers_map_t::iterator it = repo.m_Loggers.find(key); if (it != repo.m_Loggers.end()) { @@ -83,18 +82,27 @@ BOOST_LOG_API BOOST_LOG_NORETURN void throw_odr_violation( typeindex::type_index logger_type, logger_holder_base const& registered) { + std::string str("Could not initialize global logger with tag \""); + str.append(tag_type.pretty_name()); + str.append("\" and type \""); + str.append(logger_type.pretty_name()); + str.append("\". A logger of type \""); + str.append(registered.m_LoggerType.pretty_name()); + str.append("\" with the same tag has already been registered at "); + str.append(registered.m_RegistrationFile); + char buf[std::numeric_limits< unsigned int >::digits10 + 3]; - if (log::aux::snprintf(buf, sizeof(buf), "%u", registered.m_RegistrationLine) < 0) - buf[0] = '\0'; - std::string str = - std::string("Could not initialize global logger with tag \"") + - tag_type.pretty_name() + - "\" and type \"" + - logger_type.pretty_name() + - "\". A logger of type \"" + - registered.m_LoggerType.pretty_name() + - "\" with the same tag has already been registered at " + - registered.m_RegistrationFile + ":" + buf + "."; + int res = boost::core::snprintf(buf, sizeof(buf), "%u", registered.m_RegistrationLine); + if (BOOST_LIKELY(res > 0)) + { + if (BOOST_UNLIKELY(static_cast< unsigned int >(res) >= sizeof(buf))) + res = sizeof(buf) - 1u; + + str.push_back(':'); + str.append(buf, res); + } + + str.push_back('.'); BOOST_LOG_THROW_DESCR(odr_violation, str); } diff --git a/src/named_scope.cpp b/src/named_scope.cpp index 5fa634a380..22c60f6099 100644 --- a/src/named_scope.cpp +++ b/src/named_scope.cpp @@ -14,6 +14,7 @@ */ #include +#include #include #include #include @@ -22,11 +23,11 @@ #include #include #include +#include #include #if !defined(BOOST_LOG_NO_THREADS) #include #endif -#include "unique_ptr.hpp" #include namespace boost { @@ -56,7 +57,6 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { entry._m_pPrev = top; entry._m_pNext = &this->m_RootNode; - BOOST_LOG_ASSUME(&entry != 0); this->m_RootNode._m_pPrev = top->_m_pNext = const_cast< aux::named_scope_list_node* >( static_cast< const aux::named_scope_list_node* >(&entry)); @@ -91,7 +91,7 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { //! The method dispatches the value to the given object. It returns true if the //! object was capable to consume the real attribute value type and false otherwise. - bool dispatch(type_dispatcher& dispatcher) + bool dispatch(type_dispatcher& dispatcher) BOOST_OVERRIDE { type_dispatcher::callback< scope_stack > callback = dispatcher.get_callback< scope_stack >(); @@ -107,11 +107,11 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { /*! * \return The attribute value type */ - typeindex::type_index get_type() const { return typeindex::type_id< scope_stack >(); } + typeindex::type_index get_type() const BOOST_OVERRIDE { return typeindex::type_id< scope_stack >(); } //! The method is called when the attribute value is passed to another thread (e.g. //! in case of asynchronous logging). The value should ensure it properly owns all thread-specific data. - intrusive_ptr< attribute_value::impl > detach_from_thread() + intrusive_ptr< attribute_value::impl > detach_from_thread() BOOST_OVERRIDE { if (!m_DetachedValue) { @@ -153,7 +153,7 @@ struct BOOST_SYMBOL_VISIBLE named_scope::impl : #else //! Pointer to the scope stack - log::aux::unique_ptr< scope_list > pScopes; + std::unique_ptr< scope_list > pScopes; #endif //! The method returns current thread scope stack @@ -166,7 +166,7 @@ struct BOOST_SYMBOL_VISIBLE named_scope::impl : #endif if (!p) { - log::aux::unique_ptr< scope_list > pNew(new scope_list()); + std::unique_ptr< scope_list > pNew(new scope_list()); pScopes.reset(pNew.get()); #if defined(BOOST_LOG_USE_COMPILER_TLS) pScopesCache = p = pNew.release(); @@ -185,7 +185,7 @@ struct BOOST_SYMBOL_VISIBLE named_scope::impl : } //! The method returns the actual attribute value. It must not return NULL. - attribute_value get_value() + attribute_value get_value() BOOST_OVERRIDE { return attribute_value(new named_scope_value(&get_scope_list())); } @@ -210,11 +210,11 @@ BOOST_LOG_API named_scope_list::named_scope_list(named_scope_list const& that) : if (m_Size > 0) { // Copy the container contents - pointer p = allocator_type::allocate(that.size()); + pointer p = log::aux::allocator_traits< allocator_type >::allocate(*static_cast< allocator_type* >(this), that.size()); aux::named_scope_list_node* prev = &m_RootNode; for (const_iterator src = that.begin(), end = that.end(); src != end; ++src, ++p) { - allocator_type::construct(p, *src); // won't throw + log::aux::allocator_traits< allocator_type >::construct(*static_cast< allocator_type* >(this), p, *src); // won't throw p->_m_pPrev = prev; prev->_m_pNext = p; prev = p; @@ -232,8 +232,8 @@ BOOST_LOG_API named_scope_list::~named_scope_list() iterator it(m_RootNode._m_pNext); iterator end(&m_RootNode); while (it != end) - allocator_type::destroy(&*(it++)); - allocator_type::deallocate(static_cast< pointer >(m_RootNode._m_pNext), m_Size); + log::aux::allocator_traits< allocator_type >::destroy(*static_cast< allocator_type* >(this), &*(it++)); + log::aux::allocator_traits< allocator_type >::deallocate(*static_cast< allocator_type* >(this), static_cast< pointer >(m_RootNode._m_pNext), m_Size); } } diff --git a/src/named_scope_format_parser.cpp b/src/named_scope_format_parser.cpp index 729f1fb2f4..5c8cf4fa23 100644 --- a/src/named_scope_format_parser.cpp +++ b/src/named_scope_format_parser.cpp @@ -59,12 +59,7 @@ BOOST_FORCEINLINE bool is_name_character(char c) //! The function checks if there is 'operator' keyword at the specified position BOOST_FORCEINLINE bool is_operator_keyword(const char* p) { -#if defined(__i386__) || defined(__x86_64__) || defined(_M_AMD64) || defined(_M_IX86) - // Intel architecture allows unaligned accesses, so just compare with the whole keyword at once - return *reinterpret_cast< const uint64_t* >(p) == UINT64_C(0x726f74617265706f); -#else return std::memcmp(p, "operator", 8) == 0; -#endif } //! The function tries to parse operator signature @@ -129,12 +124,13 @@ bool detect_operator(const char* begin, const char* end, const char* operator_ke return true; } // Fall through to other cases involving '-' + BOOST_FALLTHROUGH; case '=': case '|': case '&': case '+': - // Handle operator=, operator==, operator+=, operator++, operator||, opeartor&&, etc. + // Handle operator=, operator==, operator+=, operator++, operator||, operator&&, etc. if (end - p >= 2 && (p[0] == p[1] || p[1] == '=')) operator_end = p + 2; else @@ -300,6 +296,7 @@ inline const char* find_opening_parenthesis(const char* begin, const char* end, } } // Fall through to process this character as other characters + BOOST_FALLTHROUGH; default: if (state != operator_detected) @@ -363,6 +360,7 @@ inline const char* find_closing_parenthesis(const char* begin, const char* end, } } // Fall through to process this character as other characters + BOOST_FALLTHROUGH; default: if (!found_first_meaningful_char && c != ' ') @@ -597,9 +595,9 @@ class named_scope_formatter public: BOOST_DEFAULTED_FUNCTION(named_scope_formatter(), {}) named_scope_formatter(named_scope_formatter const& that) : m_formatters(that.m_formatters) {} - named_scope_formatter(BOOST_RV_REF(named_scope_formatter) that) { m_formatters.swap(that.m_formatters); } + named_scope_formatter(BOOST_RV_REF(named_scope_formatter) that) BOOST_NOEXCEPT { m_formatters.swap(that.m_formatters); } - named_scope_formatter& operator= (named_scope_formatter that) + named_scope_formatter& operator= (named_scope_formatter that) BOOST_NOEXCEPT { this->swap(that); return *this; diff --git a/src/once_block.cpp b/src/once_block.cpp index c76009da0a..67326d02b0 100644 --- a/src/once_block.cpp +++ b/src/once_block.cpp @@ -108,12 +108,11 @@ BOOST_LOG_CLOSE_NAMESPACE // namespace log #else // BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 #include // atexit +#include +#include #include #include #include -#include -#include -#include #include namespace boost { @@ -229,13 +228,13 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { public once_block_impl_base { private: - mutex m_Mutex; - condition_variable m_Cond; + std::mutex m_Mutex; + std::condition_variable m_Cond; public: bool enter_once_block(once_block_flag volatile& flag) { - unique_lock< mutex > lock(m_Mutex); + std::unique_lock< std::mutex > lock(m_Mutex); while (flag.status != once_block_flag::initialized) { @@ -261,7 +260,7 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { void commit(once_block_flag& flag) { { - lock_guard< mutex > lock(m_Mutex); + std::lock_guard< std::mutex > lock(m_Mutex); flag.status = once_block_flag::initialized; } m_Cond.notify_all(); @@ -270,7 +269,7 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { void rollback(once_block_flag& flag) { { - lock_guard< mutex > lock(m_Mutex); + std::lock_guard< std::mutex > lock(m_Mutex); flag.status = once_block_flag::uninitialized; } m_Cond.notify_all(); diff --git a/src/posix/ipc_reliable_message_queue.cpp b/src/posix/ipc_reliable_message_queue.cpp index 07d61fa55f..3a80289fb0 100644 --- a/src/posix/ipc_reliable_message_queue.cpp +++ b/src/posix/ipc_reliable_message_queue.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -34,15 +35,16 @@ #include #endif #include -#include #include +#include #include +#include #include +#include #include #include #include #include -#include #include #include #include @@ -56,11 +58,6 @@ #include "bit_tools.hpp" #include -#if BOOST_ATOMIC_INT32_LOCK_FREE != 2 -// 32-bit atomic ops are required to be able to place atomic in the process-shared memory -#error Boost.Log: Native 32-bit atomic operations are required but not supported by Boost.Atomic on the target platform -#endif - namespace boost { BOOST_LOG_OPEN_NAMESPACE @@ -106,7 +103,7 @@ struct reliable_message_queue::implementation //! Padding to protect against alignment changes in Boost.Atomic. Don't use BOOST_ALIGNMENT to ensure portability. unsigned char m_padding[BOOST_LOG_CPU_CACHE_LINE_SIZE - sizeof(uint32_t)]; //! Reference counter. Also acts as a flag indicating that the queue is constructed (i.e. the queue is constructed when the counter is not 0). - boost::atomic< uint32_t > m_ref_count; + boost::ipc_atomic< uint32_t > m_ref_count; //! Number of allocation blocks in the queue. const uint32_t m_capacity; //! Size of an allocation block, in bytes. @@ -133,7 +130,7 @@ struct reliable_message_queue::implementation m_get_pos(0u) { // Must be initialized last. m_ref_count is zero-initialized initially. - m_ref_count.fetch_add(1u, boost::memory_order_release); + m_ref_count.opaque_add(1u, boost::memory_order_release); } //! Returns the header structure ABI tag @@ -199,11 +196,22 @@ struct reliable_message_queue::implementation //! The number of the bit set in block_size (i.e. log base 2 of block_size) uint32_t m_block_size_log2; //! The flag indicates that stop has been requested - bool m_stop; + boost::atomic< bool > m_stop; //! Queue shared memory object name const object_name m_name; + //! The total number of loop iterations in \c adopt_region for waiting for the region initialization to complete + static BOOST_CONSTEXPR_OR_CONST unsigned int region_init_wait_loops = 200u; + //! Threshold of the number of loop iterations in \c adopt_region for using pause instructions for yielding + static BOOST_CONSTEXPR_OR_CONST unsigned int region_init_wait_loops_pause = 16u; + //! Threshold of the number of loop iterations in \c adopt_region for using \c short_yield for yielding + static BOOST_CONSTEXPR_OR_CONST unsigned int region_init_wait_loops_short_yield = 64u; + //! Timeout, in seconds, for performing shared memory creation/opening loop + static BOOST_CONSTEXPR_OR_CONST unsigned int region_open_or_create_timeout = 60u; + //! The number of short yields to perform during the shared memory creation/opening loop + static BOOST_CONSTEXPR_OR_CONST unsigned int region_open_or_create_short_yield_loops = 64u; + public: //! The constructor creates a new shared memory segment implementation @@ -215,7 +223,7 @@ struct reliable_message_queue::implementation overflow_policy oflow_policy, permissions const& perms ) : - m_shared_memory(boost::interprocess::create_only, name.c_str(), boost::interprocess::read_write, boost::interprocess::permissions(perms.get_native())), + m_shared_memory(), m_region(), m_overflow_policy(oflow_policy), m_block_size_mask(0u), @@ -223,6 +231,26 @@ struct reliable_message_queue::implementation m_stop(false), m_name(name) { + BOOST_ASSERT(block_size >= block_header::get_header_overhead()); + + boost::interprocess::permissions ipc_perms(perms.get_native()); + while (true) + { + try + { + boost::interprocess::shared_memory_object shared_memory(boost::interprocess::create_only, name.c_str(), boost::interprocess::read_write, ipc_perms); + m_shared_memory.swap(shared_memory); + break; + } + catch (boost::interprocess::interprocess_exception& e) + { + // shared_memory_object does not handle EINTR returned from shm_open internally. + // https://github.com/boostorg/interprocess/issues/152 + if (e.get_native_error() != EINTR) + throw; + } + } + create_region(capacity, block_size); } @@ -236,7 +264,7 @@ struct reliable_message_queue::implementation overflow_policy oflow_policy, permissions const& perms ) : - m_shared_memory(boost::interprocess::open_or_create, name.c_str(), boost::interprocess::read_write, boost::interprocess::permissions(perms.get_native())), + m_shared_memory(), m_region(), m_overflow_policy(oflow_policy), m_block_size_mask(0u), @@ -244,11 +272,75 @@ struct reliable_message_queue::implementation m_stop(false), m_name(name) { - boost::interprocess::offset_t shmem_size = 0; - if (!m_shared_memory.get_size(shmem_size) || shmem_size == 0) + BOOST_ASSERT(block_size >= block_header::get_header_overhead()); + + // We need to know for certain whether we create the shared memory segment or open an existing one. + // This is to ensure that only one thread initializes the segment and all other threads wait until completion. + // Since shared_memory_object(open_or_create) constructor does not report whether the segment was actually created, + // we have to loop trying to create or open the segment. https://github.com/boostorg/interprocess/issues/151 + boost::interprocess::permissions ipc_perms(perms.get_native()); + bool created = false; + unsigned int i = 0u; + std::time_t start_time = std::time(NULL); + while (true) + { + while (true) + { + try + { + boost::interprocess::shared_memory_object shared_memory(boost::interprocess::create_only, name.c_str(), boost::interprocess::read_write, ipc_perms); + m_shared_memory.swap(shared_memory); + created = true; + goto done; + } + catch (boost::interprocess::interprocess_exception& e) + { + if (e.get_error_code() == boost::interprocess::already_exists_error) + break; + + // shared_memory_object does not handle EINTR returned from shm_open internally. + // https://github.com/boostorg/interprocess/issues/152 + if (e.get_native_error() != EINTR) + throw; + } + } + + while (true) + { + try + { + boost::interprocess::shared_memory_object shared_memory(boost::interprocess::open_only, name.c_str(), boost::interprocess::read_write); + m_shared_memory.swap(shared_memory); + created = false; + goto done; + } + catch (boost::interprocess::interprocess_exception& e) + { + if (e.get_error_code() == boost::interprocess::not_found_error) + break; + + if (e.get_native_error() != EINTR) + throw; + } + } + + std::time_t now = std::time(NULL); + if (BOOST_UNLIKELY((now - start_time) >= static_cast< std::time_t >(region_open_or_create_timeout))) + BOOST_LOG_THROW_DESCR(setup_error, "Boost.Log interprocess message queue cannot be created or opened: shared memory segment failed to be created or opened until timeout (possible livelock)"); + + if (i < region_open_or_create_short_yield_loops) + short_yield(); + else + long_yield(); + + ++i; + } + + done: + if (created) create_region(capacity, block_size); else - adopt_region(shmem_size); + adopt_region(); } //! The constructor opens the existing shared memory segment @@ -258,7 +350,7 @@ struct reliable_message_queue::implementation object_name const& name, overflow_policy oflow_policy ) : - m_shared_memory(boost::interprocess::open_only, name.c_str(), boost::interprocess::read_write), + m_shared_memory(), m_region(), m_overflow_policy(oflow_policy), m_block_size_mask(0u), @@ -266,11 +358,24 @@ struct reliable_message_queue::implementation m_stop(false), m_name(name) { - boost::interprocess::offset_t shmem_size = 0; - if (!m_shared_memory.get_size(shmem_size)) - BOOST_LOG_THROW_DESCR(setup_error, "Boost.Log interprocess message queue cannot be opened: shared memory segment not found"); + while (true) + { + try + { + boost::interprocess::shared_memory_object shared_memory(boost::interprocess::open_only, name.c_str(), boost::interprocess::read_write); + m_shared_memory.swap(shared_memory); + break; + } + catch (boost::interprocess::interprocess_exception& e) + { + // shared_memory_object does not handle EINTR returned from shm_open internally. + // https://github.com/boostorg/interprocess/issues/152 + if (e.get_native_error() != EINTR) + throw; + } + } - adopt_region(shmem_size); + adopt_region(); } ~implementation() @@ -302,7 +407,7 @@ struct reliable_message_queue::implementation if (BOOST_UNLIKELY(block_count > hdr->m_capacity)) BOOST_LOG_THROW_DESCR(logic_error, "Message size exceeds the interprocess queue capacity"); - if (m_stop) + if (m_stop.load(boost::memory_order_relaxed)) return aborted; lock_queue(); @@ -310,7 +415,7 @@ struct reliable_message_queue::implementation while (true) { - if (m_stop) + if (m_stop.load(boost::memory_order_relaxed)) return aborted; if ((hdr->m_capacity - hdr->m_size) >= block_count) @@ -339,13 +444,13 @@ struct reliable_message_queue::implementation if (BOOST_UNLIKELY(block_count > hdr->m_capacity)) BOOST_LOG_THROW_DESCR(logic_error, "Message size exceeds the interprocess queue capacity"); - if (m_stop) + if (m_stop.load(boost::memory_order_relaxed)) return false; lock_queue(); boost::log::ipc::aux::interprocess_mutex::auto_unlock unlock(hdr->m_mutex); - if (m_stop) + if (m_stop.load(boost::memory_order_relaxed)) return false; if ((hdr->m_capacity - hdr->m_size) < block_count) @@ -358,7 +463,7 @@ struct reliable_message_queue::implementation operation_result receive(receive_handler handler, void* state) { - if (m_stop) + if (m_stop.load(boost::memory_order_relaxed)) return aborted; lock_queue(); @@ -367,7 +472,7 @@ struct reliable_message_queue::implementation while (true) { - if (m_stop) + if (m_stop.load(boost::memory_order_relaxed)) return aborted; if (hdr->m_size > 0u) @@ -383,7 +488,7 @@ struct reliable_message_queue::implementation bool try_receive(receive_handler handler, void* state) { - if (m_stop) + if (m_stop.load(boost::memory_order_relaxed)) return false; lock_queue(); @@ -400,14 +505,14 @@ struct reliable_message_queue::implementation void stop_local() { - if (m_stop) + if (m_stop.load(boost::memory_order_relaxed)) return; lock_queue(); header* const hdr = get_header(); boost::log::ipc::aux::interprocess_mutex::auto_unlock unlock(hdr->m_mutex); - m_stop = true; + m_stop.store(true, boost::memory_order_relaxed); hdr->m_nonempty_queue.notify_all(); hdr->m_nonfull_queue.notify_all(); @@ -415,7 +520,7 @@ struct reliable_message_queue::implementation void reset_local() { - m_stop = false; + m_stop.store(false, boost::memory_order_relaxed); } void clear() @@ -448,17 +553,48 @@ struct reliable_message_queue::implementation init_block_size(block_size); } - void adopt_region(std::size_t shmem_size) + void adopt_region() { - if (shmem_size < sizeof(header)) + std::size_t shmem_size = 0u; + unsigned int i = 0u; + std::time_t start_time = std::time(NULL); + while (true) + { + boost::interprocess::offset_t shm_size = 0; + const bool get_size_result = m_shared_memory.get_size(shm_size); + if (BOOST_LIKELY(get_size_result && shm_size > 0)) + { + shmem_size = static_cast< std::size_t >(shm_size); + break; + } + + std::time_t now = std::time(NULL); + if (BOOST_UNLIKELY((now - start_time) >= region_open_or_create_timeout)) + { + if (get_size_result) + goto shmem_size_too_small; + BOOST_LOG_THROW_DESCR(setup_error, "Boost.Log interprocess message queue cannot be opened: shared memory segment size could not be determined until timeout"); + } + + if (i < region_open_or_create_short_yield_loops) + short_yield(); + else + long_yield(); + + ++i; + } + + if (BOOST_UNLIKELY(shmem_size < sizeof(header))) + { + shmem_size_too_small: BOOST_LOG_THROW_DESCR(setup_error, "Boost.Log interprocess message queue cannot be opened: shared memory segment size too small"); + } - boost::interprocess::mapped_region(m_shared_memory, boost::interprocess::read_write, 0u, shmem_size).swap(m_region); + boost::interprocess::mapped_region(m_shared_memory, boost::interprocess::read_write, 0, shmem_size).swap(m_region); // Wait until the mapped region becomes initialized header* const hdr = get_header(); - BOOST_CONSTEXPR_OR_CONST unsigned int wait_loops = 200u, spin_loops = 16u, spins = 16u; - for (unsigned int i = 0; i < wait_loops; ++i) + for (i = 0u; i < region_init_wait_loops; ++i) { uint32_t ref_count = hdr->m_ref_count.load(boost::memory_order_acquire); while (ref_count > 0u) @@ -467,28 +603,12 @@ struct reliable_message_queue::implementation goto done; } - if (i < spin_loops) - { - for (unsigned int j = 0; j < spins; ++j) - { - boost::log::aux::pause(); - } - } + if (i < region_init_wait_loops_pause) + boost::atomics::thread_pause(); + else if (i < region_init_wait_loops_short_yield) + short_yield(); else - { -#if defined(BOOST_HAS_SCHED_YIELD) - sched_yield(); -#elif defined(BOOST_HAS_PTHREAD_YIELD) - pthread_yield(); -#elif defined(BOOST_HAS_NANOSLEEP) - timespec ts = {}; - ts.tv_sec = 0; - ts.tv_nsec = 1000; - nanosleep(&ts, NULL); -#else - usleep(1); -#endif - } + long_yield(); } BOOST_LOG_THROW_DESCR(setup_error, "Boost.Log interprocess message queue cannot be opened: shared memory segment is not initialized by creator for too long"); @@ -614,6 +734,7 @@ struct reliable_message_queue::implementation const uint32_t capacity = hdr->m_capacity; const size_type block_size = hdr->m_block_size; uint32_t pos = hdr->m_put_pos; + BOOST_ASSERT(pos < capacity); block_header* block = hdr->get_block(pos); block->m_size = message_size; @@ -648,6 +769,7 @@ struct reliable_message_queue::implementation const uint32_t capacity = hdr->m_capacity; const size_type block_size = hdr->m_block_size; uint32_t pos = hdr->m_get_pos; + BOOST_ASSERT(pos < capacity); block_header* block = hdr->get_block(pos); size_type message_size = block->m_size; @@ -673,6 +795,29 @@ struct reliable_message_queue::implementation hdr->m_nonfull_queue.notify_all(); } + + static void short_yield() BOOST_NOEXCEPT + { +#if defined(BOOST_HAS_SCHED_YIELD) + sched_yield(); +#elif defined(BOOST_HAS_PTHREAD_YIELD) + pthread_yield(); +#else + long_yield(); +#endif + } + + static void long_yield() BOOST_NOEXCEPT + { +#if defined(BOOST_HAS_NANOSLEEP) + timespec ts = {}; + ts.tv_sec = 0; + ts.tv_nsec = 1000; + nanosleep(&ts, NULL); +#else + usleep(1); +#endif + } }; BOOST_LOG_API void reliable_message_queue::create(object_name const& name, uint32_t capacity, size_type block_size, overflow_policy oflow_policy, permissions const& perms) diff --git a/src/process_name.cpp b/src/process_name.cpp index 864defacaa..b44d00c10e 100644 --- a/src/process_name.cpp +++ b/src/process_name.cpp @@ -113,7 +113,7 @@ BOOST_LOG_CLOSE_NAMESPACE // namespace log #include #include #include -#include +#include #include #include @@ -137,7 +137,7 @@ BOOST_LOG_API std::string get_process_name() if (filesystem::exists("/proc/curproc/file")) return filesystem::read_symlink("/proc/curproc/file").filename().string(); - return boost::lexical_cast< std::string >(getpid()); + return std::to_string(getpid()); } } // namespace aux @@ -151,7 +151,7 @@ BOOST_LOG_CLOSE_NAMESPACE // namespace log #else #include -#include +#include #include #include @@ -173,7 +173,7 @@ BOOST_LOG_API std::string get_process_name() if (filesystem::exists("/proc/curproc/exe")) return filesystem::read_symlink("/proc/curproc/exe").filename().string(); - return boost::lexical_cast< std::string >(getpid()); + return std::to_string(getpid()); } } // namespace aux diff --git a/src/record_ostream.cpp b/src/record_ostream.cpp index 27ed3d09b2..28f3e520dc 100644 --- a/src/record_ostream.cpp +++ b/src/record_ostream.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -23,7 +24,6 @@ #if !defined(BOOST_LOG_NO_THREADS) #include #endif -#include "unique_ptr.hpp" #include namespace boost { @@ -75,7 +75,7 @@ class stream_compound_pool : #if !defined(BOOST_LOG_NO_THREADS) thread_specific_ptr< stream_compound_pool< CharT > > #else - log::aux::unique_ptr< stream_compound_pool< CharT > > + std::unique_ptr< stream_compound_pool< CharT > > #endif > { @@ -86,7 +86,7 @@ class stream_compound_pool : typedef thread_specific_ptr< this_type > tls_ptr_type; #else //! Thread-specific pointer type - typedef log::aux::unique_ptr< this_type > tls_ptr_type; + typedef std::unique_ptr< this_type > tls_ptr_type; #endif //! Singleton base type typedef log::aux::lazy_singleton< @@ -117,7 +117,7 @@ class stream_compound_pool : this_type* p = ptr.get(); if (!p) { - log::aux::unique_ptr< this_type > pNew(new this_type()); + std::unique_ptr< this_type > pNew(new this_type()); ptr.reset(pNew.get()); p = pNew.release(); } diff --git a/src/setup/default_filter_factory.cpp b/src/setup/default_filter_factory.cpp index 225e5d4f48..ff9106efc8 100644 --- a/src/setup/default_filter_factory.cpp +++ b/src/setup/default_filter_factory.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -29,6 +30,7 @@ #include #include #include +#include #if defined(BOOST_LOG_USE_CHAR) && defined(BOOST_LOG_USE_WCHAR_T) #include #include @@ -140,6 +142,37 @@ class string_predicate : #endif // defined(BOOST_LOG_USE_CHAR) && defined(BOOST_LOG_USE_WCHAR_T) +//! A special filtering predicate that adopts the severity level or string operand to the attribute value character type +template< typename RelationT > +class severity_or_string_predicate : + public string_predicate< RelationT > +{ +private: + typedef string_predicate< RelationT > base_type; + +public: + typedef typename base_type::relation_type relation_type; + typedef typename base_type::result_type result_type; + +public: + template< typename StringT > + severity_or_string_predicate(relation_type const& rel, StringT const& string_operand, boost::log::trivial::severity_level severity_operand) : + base_type(rel, string_operand), + m_severity_operand(severity_operand) + { + } + + using base_type::operator(); + + result_type operator() (boost::log::trivial::severity_level val) const + { + return relation_type::operator() (val, m_severity_operand); + } + +private: + const boost::log::trivial::severity_level m_severity_operand; +}; + //! A filtering predicate for numeric relations template< typename NumericT, typename RelationT > class numeric_predicate : @@ -283,13 +316,23 @@ filter default_filter_factory< CharT >::parse_argument(attribute_name const& nam if (qi::parse(begin, end, qi::long_, int_val) && begin == end) { typedef numeric_predicate< long, RelationT > predicate; - typedef default_attribute_types value_types; + typedef default_attribute_value_types value_types; return predicate_wrapper< value_types, predicate >(name, predicate(RelationT(), arg, int_val)); } else { - typedef string_predicate< RelationT > predicate; - return predicate_wrapper< log::string_types, predicate >(name, predicate(RelationT(), arg)); + boost::log::trivial::severity_level lvl; + if (name == boost::log::aux::default_attribute_names::severity() && boost::log::trivial::from_string(arg.data(), arg.size(), lvl)) + { + typedef severity_or_string_predicate< RelationT > predicate; + typedef mpl::vector< boost::log::trivial::severity_level, BOOST_PP_SEQ_ENUM(BOOST_LOG_STANDARD_STRING_TYPES()) > value_types; + return predicate_wrapper< value_types, predicate >(name, predicate(RelationT(), arg, lvl)); + } + else + { + typedef string_predicate< RelationT > predicate; + return predicate_wrapper< log::string_types, predicate >(name, predicate(RelationT(), arg)); + } } } } diff --git a/src/setup/default_filter_factory.hpp b/src/setup/default_filter_factory.hpp index 455eb42434..e5d55a63ee 100644 --- a/src/setup/default_filter_factory.hpp +++ b/src/setup/default_filter_factory.hpp @@ -70,20 +70,20 @@ class default_filter_factory : typedef typename base_type::string_type string_type; //! The callback for equality relation filter - virtual filter on_equality_relation(attribute_name const& name, string_type const& arg); + filter on_equality_relation(attribute_name const& name, string_type const& arg) BOOST_OVERRIDE; //! The callback for inequality relation filter - virtual filter on_inequality_relation(attribute_name const& name, string_type const& arg); + filter on_inequality_relation(attribute_name const& name, string_type const& arg) BOOST_OVERRIDE; //! The callback for less relation filter - virtual filter on_less_relation(attribute_name const& name, string_type const& arg); + filter on_less_relation(attribute_name const& name, string_type const& arg) BOOST_OVERRIDE; //! The callback for greater relation filter - virtual filter on_greater_relation(attribute_name const& name, string_type const& arg); + filter on_greater_relation(attribute_name const& name, string_type const& arg) BOOST_OVERRIDE; //! The callback for less or equal relation filter - virtual filter on_less_or_equal_relation(attribute_name const& name, string_type const& arg); + filter on_less_or_equal_relation(attribute_name const& name, string_type const& arg) BOOST_OVERRIDE; //! The callback for greater or equal relation filter - virtual filter on_greater_or_equal_relation(attribute_name const& name, string_type const& arg); + filter on_greater_or_equal_relation(attribute_name const& name, string_type const& arg) BOOST_OVERRIDE; //! The callback for custom relation filter - virtual filter on_custom_relation(attribute_name const& name, string_type const& rel, string_type const& arg); + filter on_custom_relation(attribute_name const& name, string_type const& rel, string_type const& arg) BOOST_OVERRIDE; private: //! The function parses the argument value for a binary relation and constructs the corresponding filter diff --git a/src/setup/default_formatter_factory.cpp b/src/setup/default_formatter_factory.cpp index 1f4cda1484..5aec94416e 100644 --- a/src/setup/default_formatter_factory.cpp +++ b/src/setup/default_formatter_factory.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -34,7 +36,6 @@ #include #include #include -#include #include #if !defined(BOOST_LOG_NO_THREADS) #include @@ -56,7 +57,10 @@ namespace aux { #endif #define BOOST_LOG_AUX_LOG_ATTRIBUTE_VALUE_TYPES()\ - (boost::log::attributes::named_scope_list)(boost::log::aux::process::id)BOOST_LOG_AUX_THREAD_ID_TYPE() + (boost::log::trivial::severity_level)\ + (boost::log::attributes::named_scope_list)\ + (boost::log::aux::process::id)\ + BOOST_LOG_AUX_THREAD_ID_TYPE() // The list of the attribute value types supported by the default formatter. Note that we have to exclude std::time_t // as it is an integral type, as well as double from the native time duration types - these are part of arithmetic types already. @@ -108,8 +112,8 @@ class default_formatter char buf[32]; std::size_t len = std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &t); std::size_t size = sizeof(buf) - len; - int res = boost::log::aux::snprintf(buf + len, size, ".%.6u", static_cast< unsigned int >(value.time_of_day().total_microseconds() % 1000000)); - if (res < 0) + int res = boost::core::snprintf(buf + len, size, ".%.6u", static_cast< unsigned int >(value.time_of_day().total_microseconds() % 1000000)); + if (BOOST_UNLIKELY(res < 0)) buf[len] = '\0'; else if (static_cast< std::size_t >(res) >= size) len += size - 1; @@ -168,8 +172,8 @@ class default_formatter unsigned int seconds = static_cast< unsigned int >(total_useconds / 1000000ull % 60ull); unsigned int useconds = static_cast< unsigned int >(total_useconds % 1000000ull); char buf[64]; - int len = boost::log::aux::snprintf(buf, sizeof(buf), "%.2llu:%.2u:%.2u.%.6u", hours, minutes, seconds, useconds); - if (len > 0) + int len = boost::core::snprintf(buf, sizeof(buf), "%.2llu:%.2u:%.2u.%.6u", hours, minutes, seconds, useconds); + if (BOOST_LIKELY(len > 0)) { unsigned int size = static_cast< unsigned int >(len) >= sizeof(buf) ? static_cast< unsigned int >(sizeof(buf)) : static_cast< unsigned int >(len); m_strm.write(buf, size); diff --git a/src/setup/default_formatter_factory.hpp b/src/setup/default_formatter_factory.hpp index 6359b3be8b..8428137ed5 100644 --- a/src/setup/default_formatter_factory.hpp +++ b/src/setup/default_formatter_factory.hpp @@ -45,7 +45,7 @@ class default_formatter_factory : typedef typename base_type::args_map args_map; //! The function creates a formatter for the specified attribute. - virtual formatter_type create_formatter(attribute_name const& name, args_map const& args); + formatter_type create_formatter(attribute_name const& name, args_map const& args) BOOST_OVERRIDE; }; } // namespace aux diff --git a/src/setup/filter_parser.cpp b/src/setup/filter_parser.cpp index cf41bca023..4379e1df1c 100644 --- a/src/setup/filter_parser.cpp +++ b/src/setup/filter_parser.cpp @@ -123,6 +123,7 @@ class filter_parser typedef CharT char_type; typedef const char_type* iterator_type; typedef typename log::aux::encoding< char_type >::type encoding; + typedef typename encoding::classify_type char_classify_type; typedef log::aux::encoding_specific< encoding > encoding_specific; typedef std::basic_string< char_type > string_type; typedef log::aux::char_constants< char_type > constants; @@ -314,7 +315,7 @@ class filter_parser else { // Check for custom relation - while (next != end && (encoding::isalnum(*next) || *next == constants::char_underline)) + while (next != end && (encoding::isalnum(static_cast< char_classify_type >(*next)) || *next == constants::char_underline)) ++next; if (p == next) goto DoneL; @@ -347,7 +348,7 @@ class filter_parser char_type c1 = *p, c2 = *keyword; if (c2 == 0) { - if (encoding::isspace(c1)) + if (encoding::isspace(static_cast< char_classify_type >(c1))) { next = p; return true; diff --git a/src/setup/formatter_parser.cpp b/src/setup/formatter_parser.cpp index 09ab8ee618..1cd1d703c0 100644 --- a/src/setup/formatter_parser.cpp +++ b/src/setup/formatter_parser.cpp @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -184,6 +183,7 @@ class formatter_parser typedef basic_formatter< char_type > formatter_type; typedef boost::log::aux::char_constants< char_type > constants; typedef typename log::aux::encoding< char_type >::type encoding; + typedef typename encoding::classify_type char_classify_type; typedef log::aux::encoding_specific< encoding > encoding_specific; typedef formatter_factory< char_type > formatter_factory_type; typedef typename formatter_factory_type::args_map args_map; @@ -300,14 +300,14 @@ class formatter_parser // Read argument name iterator_type start = p; - if (!encoding::isalpha(*p)) + if (!encoding::isalpha(static_cast< char_classify_type >(*p))) BOOST_LOG_THROW_DESCR(parse_error, "Placeholder argument name is invalid"); for (++p; p != end; ++p) { c = *p; - if (encoding::isspace(c) || c == constants::char_equal) + if (encoding::isspace(static_cast< char_classify_type >(c)) || c == constants::char_equal) break; - if (!encoding::isalnum(c) && c != constants::char_underline) + if (!encoding::isalnum(static_cast< char_classify_type >(c)) && c != constants::char_underline) BOOST_LOG_THROW_DESCR(parse_error, "Placeholder argument name is invalid"); } diff --git a/src/setup/init_from_settings.cpp b/src/setup/init_from_settings.cpp index 67d341a259..378b1ea5dc 100644 --- a/src/setup/init_from_settings.cpp +++ b/src/setup/init_from_settings.cpp @@ -35,7 +35,6 @@ #include #include #include -#include #include #include #include @@ -44,8 +43,9 @@ #include #include #include -#include +#include #include +#include #include #include #include @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -61,7 +62,7 @@ #include #include #include -#if !defined(BOOST_LOG_NO_ASIO) +#if !defined(BOOST_LOG_WITHOUT_ASIO) #include #endif #if !defined(BOOST_LOG_NO_THREADS) @@ -94,8 +95,8 @@ template< typename IntT, typename CharT > inline IntT param_cast_to_int(const char* param_name, std::basic_string< CharT > const& value) { IntT res = 0; - typedef typename mpl::if_< - is_unsigned< IntT >, + typedef typename conditional< + is_unsigned< IntT >::value, qi::extract_uint< IntT, 10, 1, -1 >, qi::extract_int< IntT, 10, 1, -1 > >::type extract; @@ -115,7 +116,8 @@ struct is_case_insensitive_equal result_type operator() (CharT left, CharT right) const BOOST_NOEXCEPT { typedef typename boost::log::aux::encoding< CharT >::type encoding; - return encoding::tolower(left) == encoding::tolower(right); + typedef typename encoding::classify_type char_classify_type; + return encoding::tolower(static_cast< char_classify_type >(left)) == encoding::tolower(static_cast< char_classify_type >(right)); } }; @@ -149,14 +151,34 @@ inline bool param_cast_to_bool(const char* param_name, std::basic_string< CharT } } -#if !defined(BOOST_LOG_NO_ASIO) +//! Extracts an \c auto_newline_mode value from parameter value +template< typename CharT > +inline sinks::auto_newline_mode param_cast_to_auto_newline_mode(const char* param_name, std::basic_string< CharT > const& value) +{ + typedef CharT char_type; + typedef boost::log::aux::char_constants< char_type > constants; + + if (value == constants::auto_newline_mode_disabled()) + return sinks::disabled_auto_newline; + else if (value == constants::auto_newline_mode_always_insert()) + return sinks::always_insert; + else if (value == constants::auto_newline_mode_insert_if_missing()) + return sinks::insert_if_missing; + else + { + BOOST_LOG_THROW_DESCR(invalid_value, + "Auto newline mode \"" + boost::log::aux::to_narrow(value) + "\" is not supported"); + } +} + +#if !defined(BOOST_LOG_WITHOUT_ASIO) //! Extracts a network address from parameter value template< typename CharT > inline std::string param_cast_to_address(const char* param_name, std::basic_string< CharT > const& value) { return log::aux::to_narrow(value); } -#endif // !defined(BOOST_LOG_NO_ASIO) +#endif // !defined(BOOST_LOG_WITHOUT_ASIO) template< typename CharT > inline bool is_weekday(const CharT* str, std::size_t len, boost::log::basic_string_literal< CharT > const& weekday, boost::log::basic_string_literal< CharT > const& short_weekday) @@ -172,6 +194,7 @@ sinks::file::rotation_at_time_point param_cast_to_rotation_time_point(const char typedef CharT char_type; typedef boost::log::aux::char_constants< char_type > constants; typedef typename boost::log::aux::encoding< char_type >::type encoding; + typedef typename encoding::classify_type char_classify_type; typedef qi::extract_uint< unsigned short, 10, 1, 2 > day_extract; typedef qi::extract_uint< unsigned char, 10, 2, 2 > time_component_extract; @@ -181,14 +204,14 @@ sinks::file::rotation_at_time_point param_cast_to_rotation_time_point(const char unsigned char hour = 0, minute = 0, second = 0; const char_type* begin = value.c_str(), *end = begin + value.size(); - if (!encoding::isalnum(*begin)) // begin is null-terminated, so we also check that the string is not empty here + if (!encoding::isalnum(static_cast< char_classify_type >(*begin))) // begin is null-terminated, so we also check that the string is not empty here throw_invalid_value(param_name); const char_type* p = begin + 1; - if (encoding::isalpha(*begin)) + if (encoding::isalpha(static_cast< char_classify_type >(*begin))) { // This must be a weekday - while (encoding::isalpha(*p)) + while (encoding::isalpha(static_cast< char_classify_type >(*p))) ++p; std::size_t len = p - begin; @@ -212,10 +235,10 @@ sinks::file::rotation_at_time_point param_cast_to_rotation_time_point(const char else { // This may be either a month day or an hour - while (encoding::isdigit(*p)) + while (encoding::isdigit(static_cast< char_classify_type >(*p))) ++p; - if (encoding::isspace(*p)) + if (encoding::isspace(static_cast< char_classify_type >(*p))) { // This is a month day unsigned short mday = 0; @@ -235,7 +258,7 @@ sinks::file::rotation_at_time_point param_cast_to_rotation_time_point(const char } // Skip spaces - while (encoding::isspace(*p)) + while (encoding::isspace(static_cast< char_classify_type >(*p))) ++p; // Parse hour @@ -347,7 +370,7 @@ class basic_default_sink_factory : private: //! The function initializes formatter for the sinks that support formatting template< typename SinkT > - static shared_ptr< SinkT > init_formatter(shared_ptr< SinkT > const& sink, settings_section const& params, mpl::true_) + static shared_ptr< SinkT > init_formatter(shared_ptr< SinkT > const& sink, settings_section const& params, true_type) { // Formatter if (optional< string_type > format_param = params["Format"]) @@ -360,7 +383,7 @@ class basic_default_sink_factory : return sink; } template< typename SinkT > - static shared_ptr< SinkT > init_formatter(shared_ptr< SinkT > const& sink, settings_section const& params, mpl::false_) + static shared_ptr< SinkT > init_formatter(shared_ptr< SinkT > const& sink, settings_section const& params, false_type) { return sink; } @@ -394,6 +417,12 @@ class default_console_sink_factory : shared_ptr< backend_t > backend = boost::make_shared< backend_t >(); backend->add_stream(shared_ptr< typename backend_t::stream_type >(&constants::get_console_log_stream(), boost::null_deleter())); + // Auto newline mode + if (optional< string_type > auto_newline_param = params["AutoNewline"]) + { + backend->set_auto_newline_mode(param_cast_to_auto_newline_mode("AutoNewline", auto_newline_param.get())); + } + // Auto flush if (optional< string_type > auto_flush_param = params["AutoFlush"]) { @@ -406,7 +435,7 @@ class default_console_sink_factory : public: //! The function constructs a sink that writes log records to the console - shared_ptr< sinks::sink > create_sink(settings_section const& params) + shared_ptr< sinks::sink > create_sink(settings_section const& params) BOOST_OVERRIDE { return base_type::select_backend_character_type(params, impl()); } @@ -426,7 +455,7 @@ class default_text_file_sink_factory : public: //! The function constructs a sink that writes log records to a text file - shared_ptr< sinks::sink > create_sink(settings_section const& params) + shared_ptr< sinks::sink > create_sink(settings_section const& params) BOOST_OVERRIDE { typedef sinks::text_file_backend backend_t; shared_ptr< backend_t > backend = boost::make_shared< backend_t >(); @@ -439,6 +468,12 @@ class default_text_file_sink_factory : else BOOST_LOG_THROW_DESCR(missing_value, "File name is not specified"); + // Target file name + if (optional< string_type > target_file_name_param = params["TargetFileName"]) + { + backend->set_target_file_name_pattern(filesystem::path(target_file_name_param.get())); + } + // File rotation size if (optional< string_type > rotation_size_param = params["RotationSize"]) { @@ -463,6 +498,12 @@ class default_text_file_sink_factory : backend->enable_final_rotation(param_cast_to_bool("EnableFinalRotation", enable_final_rotation_param.get())); } + // Auto newline mode + if (optional< string_type > auto_newline_param = params["AutoNewline"]) + { + backend->set_auto_newline_mode(param_cast_to_auto_newline_mode("AutoNewline", auto_newline_param.get())); + } + // Auto flush if (optional< string_type > auto_flush_param = params["AutoFlush"]) { @@ -539,7 +580,7 @@ class default_syslog_sink_factory : public: //! The function constructs a sink that writes log records to syslog - shared_ptr< sinks::sink > create_sink(settings_section const& params) + shared_ptr< sinks::sink > create_sink(settings_section const& params) BOOST_OVERRIDE { // Construct the backend typedef sinks::syslog_backend backend_t; @@ -548,14 +589,14 @@ class default_syslog_sink_factory : // For now we use only the default level mapping. Will add support for configuration later. backend->set_severity_mapper(sinks::syslog::direct_severity_mapping< >(log::aux::default_attribute_names::severity())); -#if !defined(BOOST_LOG_NO_ASIO) +#if !defined(BOOST_LOG_WITHOUT_ASIO) // Setup local and remote addresses if (optional< string_type > local_address_param = params["LocalAddress"]) backend->set_local_address(param_cast_to_address("LocalAddress", local_address_param.get())); if (optional< string_type > target_address_param = params["TargetAddress"]) backend->set_target_address(param_cast_to_address("TargetAddress", target_address_param.get())); -#endif // !defined(BOOST_LOG_NO_ASIO) +#endif // !defined(BOOST_LOG_WITHOUT_ASIO) return base_type::init_sink(backend, params); } @@ -801,7 +842,8 @@ BOOST_LOG_SETUP_API void init_from_settings(basic_settings_section< CharT > cons if (section sink_params = setts["Sinks"]) { sinks_repo_t& sinks_repo = sinks_repo_t::get(); - std::vector< shared_ptr< sinks::sink > > new_sinks; + typedef std::vector< shared_ptr< sinks::sink > > sink_list_t; + sink_list_t new_sinks; for (typename section::const_iterator it = sink_params.begin(), end = sink_params.end(); it != end; ++it) { @@ -814,7 +856,9 @@ BOOST_LOG_SETUP_API void init_from_settings(basic_settings_section< CharT > cons } } - std::for_each(new_sinks.begin(), new_sinks.end(), boost::bind(&core::add_sink, core::get(), _1)); + core_ptr core = boost::log::core::get(); + for (sink_list_t::const_iterator it = new_sinks.begin(), end = new_sinks.end(); it != end; ++it) + core->add_sink(*it); } } @@ -824,7 +868,7 @@ template< typename CharT > BOOST_LOG_SETUP_API void register_sink_factory(const char* sink_name, shared_ptr< sink_factory< CharT > > const& factory) { sinks_repository< CharT >& repo = sinks_repository< CharT >::get(); - BOOST_LOG_EXPR_IF_MT(lock_guard< log::aux::light_rw_mutex > lock(repo.m_Mutex);) + BOOST_LOG_EXPR_IF_MT(log::aux::exclusive_lock_guard< log::aux::light_rw_mutex > lock(repo.m_Mutex);) repo.m_Factories[sink_name] = factory; } diff --git a/src/setup/parser_utils.cpp b/src/setup/parser_utils.cpp index 08a5de8b06..a8c3299b0f 100644 --- a/src/setup/parser_utils.cpp +++ b/src/setup/parser_utils.cpp @@ -60,7 +60,7 @@ const char_constants< char >::char_type char_constants< char >::char_paren_brack const char* char_constants< char >::trim_spaces_left(const char_type* begin, const char_type* end) { using namespace std; - while (begin != end && isspace(*begin)) + while (begin != end && isspace(static_cast< unsigned char >(*begin))) ++begin; return begin; } @@ -69,7 +69,7 @@ const char* char_constants< char >::trim_spaces_left(const char_type* begin, con const char* char_constants< char >::trim_spaces_right(const char_type* begin, const char_type* end) { using namespace std; - while (begin != end && isspace(*(end - 1))) + while (begin != end && isspace(static_cast< unsigned char >(*(end - 1)))) --end; return end; } @@ -81,7 +81,7 @@ const char* char_constants< char >::scan_attr_placeholder(const char_type* begin while (begin != end) { char_type c = *begin; - if (!isalnum(c) && c != char_underline) + if (!isalnum(static_cast< unsigned char >(c)) && c != char_underline) break; ++begin; } @@ -132,7 +132,7 @@ const char* char_constants< char >::parse_operand(const char_type* begin, const for (++p; p != end; ++p) { c = *p; - if (!isalnum(c) && c != '_' && c != '-' && c != '+' && c != '.') + if (!isalnum(static_cast< unsigned char >(c)) && c != '_' && c != '-' && c != '+' && c != '.') break; } @@ -174,7 +174,7 @@ void char_constants< char >::translate_escape_sequences(string_type& str) if (std::distance(++b, str.end()) >= 2) { char_type c1 = *b++, c2 = *b++; - if (isxdigit(c1) && isxdigit(c2)) + if (isxdigit(static_cast< unsigned char >(c1)) && isxdigit(static_cast< unsigned char >(c2))) { *it++ = char_type((to_number(c1) << 4) | to_number(c2)); it = str.erase(it, b); diff --git a/src/setup/parser_utils.hpp b/src/setup/parser_utils.hpp index 94f5ffb56c..f6e6180d29 100644 --- a/src/setup/parser_utils.hpp +++ b/src/setup/parser_utils.hpp @@ -97,6 +97,7 @@ struct char_constants< char > static const char_type* append_param_name() { return "Append"; } static const char_type* enable_final_rotation_param_name() { return "EnableFinalRotation"; } static const char_type* auto_flush_param_name() { return "AutoFlush"; } + static const char_type* auto_newline_mode_param_name() { return "AutoNewline"; } static const char_type* asynchronous_param_name() { return "Asynchronous"; } static const char_type* format_param_name() { return "Format"; } static const char_type* provider_id_param_name() { return "ProviderID"; } @@ -114,6 +115,10 @@ struct char_constants< char > static const char_type* scan_method_all() { return "All"; } static const char_type* scan_method_matching() { return "Matching"; } + static const char_type* auto_newline_mode_disabled() { return "Disabled"; } + static const char_type* auto_newline_mode_always_insert() { return "AlwaysInsert"; } + static const char_type* auto_newline_mode_insert_if_missing() { return "InsertIfMissing"; } + static const char_type* registration_never() { return "Never"; } static const char_type* registration_on_demand() { return "OnDemand"; } static const char_type* registration_forced() { return "Forced"; } @@ -145,7 +150,7 @@ struct char_constants< char > { using namespace std; // to make sure we can use C functions unqualified int n = 0; - if (isdigit(c)) + if (isdigit(static_cast< unsigned char >(c))) n = c - '0'; else if (c >= 'a' && c <= 'f') n = c - 'a' + 10; @@ -228,6 +233,7 @@ struct char_constants< wchar_t > static const char_type* append_param_name() { return L"Append"; } static const char_type* enable_final_rotation_param_name() { return L"EnableFinalRotation"; } static const char_type* auto_flush_param_name() { return L"AutoFlush"; } + static const char_type* auto_newline_mode_param_name() { return L"AutoNewline"; } static const char_type* asynchronous_param_name() { return L"Asynchronous"; } static const char_type* format_param_name() { return L"Format"; } static const char_type* provider_id_param_name() { return L"ProviderID"; } @@ -245,6 +251,10 @@ struct char_constants< wchar_t > static const char_type* scan_method_all() { return L"All"; } static const char_type* scan_method_matching() { return L"Matching"; } + static const char_type* auto_newline_mode_disabled() { return L"Disabled"; } + static const char_type* auto_newline_mode_always_insert() { return L"AlwaysInsert"; } + static const char_type* auto_newline_mode_insert_if_missing() { return L"InsertIfMissing"; } + static const char_type* registration_never() { return L"Never"; } static const char_type* registration_on_demand() { return L"OnDemand"; } static const char_type* registration_forced() { return L"Forced"; } diff --git a/src/setup/settings_parser.cpp b/src/setup/settings_parser.cpp index d0ebe6fede..e3e99a6806 100644 --- a/src/setup/settings_parser.cpp +++ b/src/setup/settings_parser.cpp @@ -13,9 +13,10 @@ * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. */ +#include + #ifndef BOOST_LOG_WITHOUT_SETTINGS_PARSERS -#include #include #include #include @@ -49,6 +50,7 @@ class settings_parser typedef CharT char_type; typedef const char_type* iterator_type; typedef typename log::aux::encoding< char_type >::type encoding; + typedef typename encoding::classify_type char_classify_type; typedef settings_parser< char_type > this_type; typedef std::basic_string< char_type > string_type; @@ -156,7 +158,7 @@ class settings_parser for (iterator_type p = begin; p != end; ++p) { char_type c = *p; - if (c != constants::char_dot && !encoding::isalnum(c)) + if (c != constants::char_dot && !encoding::isalnum(static_cast< char_classify_type >(c))) BOOST_LOG_THROW_DESCR_PARAMS(parse_error, "Section name is invalid", (m_LineCounter)); } @@ -182,12 +184,12 @@ class settings_parser BOOST_LOG_THROW_DESCR_PARAMS(parse_error, "Parameter name is empty", (m_LineCounter)); iterator_type p = begin; - if (!encoding::isalpha(*p)) + if (!encoding::isalpha(static_cast< char_classify_type >(*p))) BOOST_LOG_THROW_DESCR_PARAMS(parse_error, "Parameter name is invalid", (m_LineCounter)); for (++p; p != end; ++p) { char_type c = *p; - if (!encoding::isgraph(c)) + if (!encoding::isgraph(static_cast< char_classify_type >(c))) BOOST_LOG_THROW_DESCR_PARAMS(parse_error, "Parameter name is invalid", (m_LineCounter)); } diff --git a/src/severity_level.cpp b/src/severity_level.cpp index 25b7c7d548..21fdf586cb 100644 --- a/src/severity_level.cpp +++ b/src/severity_level.cpp @@ -14,17 +14,15 @@ */ #include +#include #include #include #if !defined(BOOST_LOG_NO_THREADS) && !defined(BOOST_LOG_USE_COMPILER_TLS) -#include -#include #include // at_thread_exit #include #include #endif -#include "unique_ptr.hpp" #include namespace boost { @@ -63,10 +61,10 @@ BOOST_LOG_API uintmax_t& get_severity_level() uintmax_t* p = tss.get(); if (BOOST_UNLIKELY(!p)) { - log::aux::unique_ptr< uintmax_t > ptr(new uintmax_t(0)); + std::unique_ptr< uintmax_t > ptr(new uintmax_t(0)); tss.set(ptr.get()); p = ptr.release(); - boost::this_thread::at_thread_exit(boost::bind(checked_deleter< uintmax_t >(), p)); + boost::this_thread::at_thread_exit([p]() { delete p; }); } return *p; } diff --git a/src/syslog_backend.cpp b/src/syslog_backend.cpp index 1c359454b8..43eee4ff24 100644 --- a/src/syslog_backend.cpp +++ b/src/syslog_backend.cpp @@ -1,5 +1,5 @@ /* - * Copyright Andrey Semashev 2007 - 2018. + * Copyright Andrey Semashev 2007 - 2020. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) @@ -13,19 +13,22 @@ * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. */ +#include + #ifndef BOOST_LOG_WITHOUT_SYSLOG -#include #include +#include #include #include #include #include +#include #include #include #include #include -#if !defined(BOOST_LOG_NO_ASIO) +#if !defined(BOOST_LOG_WITHOUT_ASIO) #include #include #include @@ -39,13 +42,10 @@ #include #include #include -#include #include #if !defined(BOOST_LOG_NO_THREADS) -#include -#include +#include #endif -#include "unique_ptr.hpp" #ifdef BOOST_LOG_USE_NATIVE_SYSLOG #include @@ -90,7 +90,7 @@ struct syslog_backend::implementation #ifdef BOOST_LOG_USE_NATIVE_SYSLOG struct native; #endif // BOOST_LOG_USE_NATIVE_SYSLOG -#if !defined(BOOST_LOG_NO_ASIO) +#if !defined(BOOST_LOG_WITHOUT_ASIO) struct udp_socket_based; #endif @@ -124,20 +124,35 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { //! Syslog service initializer (implemented as a weak singleton) #if !defined(BOOST_LOG_NO_THREADS) class native_syslog_initializer : - private log::aux::lazy_singleton< native_syslog_initializer, mutex > + private log::aux::lazy_singleton< native_syslog_initializer, std::mutex > #else class native_syslog_initializer #endif { #if !defined(BOOST_LOG_NO_THREADS) - friend class log::aux::lazy_singleton< native_syslog_initializer, mutex >; - typedef log::aux::lazy_singleton< native_syslog_initializer, mutex > mutex_holder; + friend class log::aux::lazy_singleton< native_syslog_initializer, std::mutex >; + typedef log::aux::lazy_singleton< native_syslog_initializer, std::mutex > mutex_holder; #endif + private: + /*! + * \brief Application identification string + * + * \note We have to keep it as an immutable member because some syslog implementations (e.g. glibc) + * do not deep-copy the ident string to internal storage when \c openlog is called + * and instead save a pointer to the user-provided string. This means the user-provided + * string needs to remain accessible for the whole duration of logging. + * + * https://github.com/boostorg/log/issues/97 + * https://sourceware.org/bugzilla/show_bug.cgi?id=25442 + */ + const std::string m_Ident; + public: - native_syslog_initializer(std::string const& ident, int facility) + native_syslog_initializer(std::string const& ident, int facility) : + m_Ident(ident) { - ::openlog((ident.empty() ? static_cast< const char* >(NULL) : ident.c_str()), 0, facility); + ::openlog((m_Ident.empty() ? static_cast< const char* >(NULL) : m_Ident.c_str()), 0, facility); } ~native_syslog_initializer() { @@ -147,7 +162,7 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { static shared_ptr< native_syslog_initializer > get_instance(std::string const& ident, int facility) { #if !defined(BOOST_LOG_NO_THREADS) - lock_guard< mutex > lock(mutex_holder::get()); + std::lock_guard< std::mutex > lock(mutex_holder::get()); #endif static weak_ptr< native_syslog_initializer > instance; shared_ptr< native_syslog_initializer > p(instance.lock()); @@ -158,6 +173,9 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { } return p; } + + BOOST_DELETED_FUNCTION(native_syslog_initializer(native_syslog_initializer const&)) + BOOST_DELETED_FUNCTION(native_syslog_initializer& operator= (native_syslog_initializer const&)) }; } // namespace @@ -176,7 +194,7 @@ struct syslog_backend::implementation::native : } //! The method sends the formatted message to the syslog host - void send(syslog::level lev, string_type const& formatted_message) + void send(syslog::level lev, string_type const& formatted_message) BOOST_OVERRIDE { int native_level; switch (lev) @@ -285,7 +303,7 @@ struct syslog_backend::implementation::native : // Socket-based implementation //////////////////////////////////////////////////////////////////////////////// -#if !defined(BOOST_LOG_NO_ASIO) +#if !defined(BOOST_LOG_WITHOUT_ASIO) BOOST_LOG_ANONYMOUS_NAMESPACE { @@ -335,7 +353,7 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { #if !defined(BOOST_LOG_NO_THREADS) //! A synchronization primitive to protect the host name resolver - mutex m_Mutex; + std::mutex m_Mutex; //! The resolver is used to acquire connection endpoints asio::ip::udp::resolver m_HostNameResolver; #endif // !defined(BOOST_LOG_NO_THREADS) @@ -373,7 +391,7 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { // The packet size is mandated in RFC3164, plus one for the terminating zero char packet[1025]; - int n = boost::log::aux::snprintf + int n = boost::core::snprintf ( packet, sizeof(packet), @@ -404,7 +422,7 @@ struct syslog_backend::implementation::udp_socket_based : //! Pointer to the list of sockets shared_ptr< syslog_udp_service > m_pService; //! Pointer to the socket being used - log::aux::unique_ptr< syslog_udp_socket > m_pSocket; + std::unique_ptr< syslog_udp_socket > m_pSocket; //! The target host to send packets to asio::ip::udp::endpoint m_TargetHost; @@ -429,11 +447,11 @@ struct syslog_backend::implementation::udp_socket_based : } //! The method sends the formatted message to the syslog host - void send(syslog::level lev, string_type const& formatted_message) + void send(syslog::level lev, string_type const& formatted_message) BOOST_OVERRIDE { if (!m_pSocket.get()) { - asio::ip::udp::endpoint any_local_address; + asio::ip::udp::endpoint any_local_address(m_Protocol, 0u); m_pSocket.reset(new syslog_udp_socket(m_pService->m_IOContext, m_Protocol, any_local_address)); } @@ -445,7 +463,7 @@ struct syslog_backend::implementation::udp_socket_based : } }; -#endif // !defined(BOOST_LOG_NO_ASIO) +#endif // !defined(BOOST_LOG_WITHOUT_ASIO) //////////////////////////////////////////////////////////////////////////////// // Sink backend implementation @@ -488,23 +506,25 @@ BOOST_LOG_API void syslog_backend::construct(syslog::facility fac, syslog::impl_ } #endif // BOOST_LOG_USE_NATIVE_SYSLOG -#if !defined(BOOST_LOG_NO_ASIO) +#if !defined(BOOST_LOG_WITHOUT_ASIO) typedef implementation::udp_socket_based udp_socket_based_impl; + asio::ip::udp protocol = asio::ip::udp::v4(); switch (ip_version) { case v4: - m_pImpl = new udp_socket_based_impl(fac, asio::ip::udp::v4()); break; case v6: - m_pImpl = new udp_socket_based_impl(fac, asio::ip::udp::v6()); + protocol = asio::ip::udp::v6(); break; default: BOOST_LOG_THROW_DESCR(setup_error, "Incorrect IP version specified"); } + + m_pImpl = new udp_socket_based_impl(fac, protocol); #endif } -#if !defined(BOOST_LOG_NO_ASIO) +#if !defined(BOOST_LOG_WITHOUT_ASIO) //! The method sets the local address which log records will be sent from. BOOST_LOG_API void syslog_backend::set_local_address(std::string const& addr, unsigned short port) @@ -513,14 +533,15 @@ BOOST_LOG_API void syslog_backend::set_local_address(std::string const& addr, un typedef implementation::udp_socket_based udp_socket_based_impl; if (udp_socket_based_impl* impl = dynamic_cast< udp_socket_based_impl* >(m_pImpl)) { - char service_name[std::numeric_limits< int >::digits10 + 3]; - boost::log::aux::snprintf(service_name, sizeof(service_name), "%d", static_cast< int >(port)); + char service_name[std::numeric_limits< unsigned int >::digits10 + 3]; + boost::core::snprintf(service_name, sizeof(service_name), "%u", static_cast< unsigned int >(port)); asio::ip::udp::endpoint local_address; { - lock_guard< mutex > lock(impl->m_pService->m_Mutex); + std::lock_guard< std::mutex > lock(impl->m_pService->m_Mutex); asio::ip::udp::resolver::results_type results = impl->m_pService->m_HostNameResolver.resolve ( + impl->m_Protocol, addr, service_name, asio::ip::resolver_base::address_configured | asio::ip::resolver_base::passive @@ -533,8 +554,8 @@ BOOST_LOG_API void syslog_backend::set_local_address(std::string const& addr, un } #else // Boost.ASIO requires threads for the host name resolver, - // so without threads we simply assume the string already contains IP address - set_local_address(boost::asio::ip::address::from_string(addr), port); + // so without threads we simply assume the string already contains an IP address + set_local_address(asio::ip::make_address(addr), port); #endif // !defined(BOOST_LOG_NO_THREADS) } //! The method sets the local address which log records will be sent from. @@ -543,6 +564,9 @@ BOOST_LOG_API void syslog_backend::set_local_address(boost::asio::ip::address co typedef implementation::udp_socket_based udp_socket_based_impl; if (udp_socket_based_impl* impl = dynamic_cast< udp_socket_based_impl* >(m_pImpl)) { + if ((impl->m_Protocol == asio::ip::udp::v4() && !addr.is_v4()) || (impl->m_Protocol == asio::ip::udp::v6() && !addr.is_v6())) + BOOST_LOG_THROW_DESCR(setup_error, "Incorrect IP version specified in the local address"); + impl->m_pSocket.reset(new syslog_udp_socket( impl->m_pService->m_IOContext, impl->m_Protocol, asio::ip::udp::endpoint(addr, port))); } @@ -555,14 +579,15 @@ BOOST_LOG_API void syslog_backend::set_target_address(std::string const& addr, u typedef implementation::udp_socket_based udp_socket_based_impl; if (udp_socket_based_impl* impl = dynamic_cast< udp_socket_based_impl* >(m_pImpl)) { - char service_name[std::numeric_limits< int >::digits10 + 3]; - boost::log::aux::snprintf(service_name, sizeof(service_name), "%d", static_cast< int >(port)); + char service_name[std::numeric_limits< unsigned int >::digits10 + 3]; + boost::core::snprintf(service_name, sizeof(service_name), "%u", static_cast< unsigned int >(port)); asio::ip::udp::endpoint remote_address; { - lock_guard< mutex > lock(impl->m_pService->m_Mutex); + std::lock_guard< std::mutex > lock(impl->m_pService->m_Mutex); asio::ip::udp::resolver::results_type results = impl->m_pService->m_HostNameResolver.resolve ( + impl->m_Protocol, addr, service_name, asio::ip::resolver_query_base::address_configured @@ -575,8 +600,8 @@ BOOST_LOG_API void syslog_backend::set_target_address(std::string const& addr, u } #else // Boost.ASIO requires threads for the host name resolver, - // so without threads we simply assume the string already contains IP address - set_target_address(boost::asio::ip::address::from_string(addr), port); + // so without threads we simply assume the string already contains an IP address + set_target_address(asio::ip::make_address(addr), port); #endif // !defined(BOOST_LOG_NO_THREADS) } //! The method sets the address of the remote host where log records will be sent to. @@ -585,11 +610,14 @@ BOOST_LOG_API void syslog_backend::set_target_address(boost::asio::ip::address c typedef implementation::udp_socket_based udp_socket_based_impl; if (udp_socket_based_impl* impl = dynamic_cast< udp_socket_based_impl* >(m_pImpl)) { + if ((impl->m_Protocol == asio::ip::udp::v4() && !addr.is_v4()) || (impl->m_Protocol == asio::ip::udp::v6() && !addr.is_v6())) + BOOST_LOG_THROW_DESCR(setup_error, "Incorrect IP version specified in the target address"); + impl->m_TargetHost = asio::ip::udp::endpoint(addr, port); } } -#endif // !defined(BOOST_LOG_NO_ASIO) +#endif // !defined(BOOST_LOG_WITHOUT_ASIO) } // namespace sinks diff --git a/src/text_file_backend.cpp b/src/text_file_backend.cpp index 7f8d76f81e..61519740a6 100644 --- a/src/text_file_backend.cpp +++ b/src/text_file_backend.cpp @@ -1,5 +1,5 @@ /* - * Copyright Andrey Semashev 2007 - 2015. + * Copyright Andrey Semashev 2007 - 2025. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -29,20 +30,20 @@ #include #include #include -#include -#include +#include #include +#include #include #include #include -#include #include #include #include +#include +#include #include #include #include -#include #include #include #include @@ -53,12 +54,11 @@ #include #include #include +#include #include -#include "unique_ptr.hpp" #if !defined(BOOST_LOG_NO_THREADS) -#include -#include +#include #endif // !defined(BOOST_LOG_NO_THREADS) #include @@ -76,11 +76,9 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { typedef filesystem::filesystem_error filesystem_error; //! A possible Boost.Filesystem extension - renames or moves the file to the target storage - inline void move_file( - filesystem::path const& from, - filesystem::path const& to) + inline void move_file(filesystem::path const& from, filesystem::path const& to) { -#if defined(BOOST_WINDOWS_API) +#if defined(BOOST_FILESYSTEM_WINDOWS_API) // On Windows MoveFile already does what we need filesystem::rename(from, to); #else @@ -89,7 +87,7 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { filesystem::rename(from, to, ec); if (ec) { - if (ec.value() == system::errc::cross_device_link) + if (BOOST_LIKELY(ec.value() == system::errc::cross_device_link)) { // Attempt to manually move the file instead filesystem::copy_file(from, to); @@ -135,7 +133,7 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { static bool is_digit(char c) { using namespace std; - return (isdigit(c) != 0); + return (isdigit(static_cast< unsigned char >(c)) != 0); } static std::string default_file_name_pattern() { return "%5N.log"; } }; @@ -264,9 +262,7 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { //! The position in the pattern where the file counter placeholder is path_string_type::size_type m_FileCounterPosition; //! File counter width - std::streamsize m_Width; - //! The file counter formatting stream - mutable std::basic_ostringstream< path_char_type > m_Stream; + unsigned int m_Width; public: //! Initializing constructor @@ -274,26 +270,44 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { m_FileCounterPosition(pos), m_Width(width) { - typedef file_char_traits< path_char_type > traits_t; - m_Stream.fill(traits_t::zero); } //! Copy constructor file_counter_formatter(file_counter_formatter const& that) : m_FileCounterPosition(that.m_FileCounterPosition), m_Width(that.m_Width) { - m_Stream.fill(that.m_Stream.fill()); } //! The function formats the file counter into the file name path_string_type operator()(path_string_type const& pattern, unsigned int counter) const { + typedef file_char_traits< path_char_type > traits_t; + + // Perform formatting that does not depend on the locale. This is important to be able to parse the counter match_pattern. + path_char_type buf[(std::numeric_limits< unsigned int >::digits10 + 1u) < 64u ? 64u : (std::numeric_limits< unsigned int >::digits10 + 1u)]; + path_char_type* e = buf + sizeof(buf) / sizeof(*buf); + path_char_type* p = e; + do + { + --p; + *p = traits_t::zero + (counter % 10u); + counter /= 10u; + } + while (counter > 0u); + + while (static_cast< std::size_t >(e - p) < m_Width && p > buf) + { + --p; + *p = traits_t::zero; + } + path_string_type file_name = pattern; - m_Stream.str(path_string_type()); - m_Stream.width(m_Width); - m_Stream << counter; - file_name.insert(m_FileCounterPosition, m_Stream.str()); + typename path_string_type::size_type const size = static_cast< typename path_string_type::size_type >(e - p); + file_name.insert(m_FileCounterPosition, p, size); + + if (size < m_Width) + file_name.insert(m_FileCounterPosition, static_cast< typename path_string_type::size_type >(m_Width - size), traits_t::zero); return file_name; } @@ -390,8 +404,10 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { { for (; n > 0; --n) { + if (it == end) + return false; path_char_type c = *it++; - if (!traits_t::is_digit(c) || it == end) + if (!traits_t::is_digit(c)) return false; } return true; @@ -506,6 +522,117 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { return false; } + //! The function parses file name pattern and splits it into path and filename and creates a function object that will generate the actual filename from the pattern + void parse_file_name_pattern + ( + filesystem::path const& pattern, + filesystem::path& storage_dir, + filesystem::path& file_name_pattern, + boost::log::aux::light_function< path_string_type (unsigned int) >& file_name_generator, + bool& has_file_counter + ) + { + // Note: avoid calling Boost.Filesystem functions that involve path::codecvt() + // https://svn.boost.org/trac/boost/ticket/9119 + + typedef file_char_traits< path_char_type > traits_t; + + file_name_pattern = pattern.filename(); + path_string_type name_pattern = file_name_pattern.native(); + storage_dir = filesystem::absolute(pattern.parent_path()); + + // Let's try to find the file counter placeholder + unsigned int placeholder_count = 0; + unsigned int width = 0; + bool counter_found = false; + path_string_type::size_type counter_pos = 0; + path_string_type::const_iterator end = name_pattern.end(); + path_string_type::const_iterator it = name_pattern.begin(); + + do + { + it = std::find(it, end, traits_t::percent); + if (it == end) + break; + path_string_type::const_iterator placeholder_begin = it++; + if (it == end) + break; + if (*it == traits_t::percent) + { + // An escaped percent detected + ++it; + continue; + } + + ++placeholder_count; + + if (!counter_found) + { + path_string_type::const_iterator it2 = it; + if (parse_counter_placeholder(it2, end, width)) + { + // We've found the file counter placeholder in the pattern + counter_found = true; + counter_pos = placeholder_begin - name_pattern.begin(); + name_pattern.erase(counter_pos, it2 - placeholder_begin); + --placeholder_count; + it = name_pattern.begin() + counter_pos; + end = name_pattern.end(); + } + } + } + while (it != end); + + // Construct the formatter functor + if (placeholder_count > 0) + { + if (counter_found) + { + // Both counter and date/time placeholder in the pattern +#if !defined(BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES) + file_name_generator = [date_and_time_fmt = date_and_time_formatter(), file_counter_fmt = file_counter_formatter(counter_pos, width), name_pattern](unsigned int counter) + { return date_and_time_fmt(file_counter_fmt(name_pattern, counter), counter); }; +#else + date_and_time_formatter date_and_time_fmt; + file_counter_formatter file_counter_fmt(counter_pos, width); + file_name_generator = [date_and_time_fmt, file_counter_fmt, name_pattern](unsigned int counter) + { return date_and_time_fmt(file_counter_fmt(name_pattern, counter), counter); }; +#endif + } + else + { + // Only date/time placeholders in the pattern +#if !defined(BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES) + file_name_generator = [date_and_time_fmt = date_and_time_formatter(), name_pattern](unsigned int counter) + { return date_and_time_fmt(name_pattern, counter); }; +#else + date_and_time_formatter date_and_time_fmt; + file_name_generator = [date_and_time_fmt, name_pattern](unsigned int counter) + { return date_and_time_fmt(name_pattern, counter); }; +#endif + } + } + else if (counter_found) + { + // Only counter placeholder in the pattern +#if !defined(BOOST_NO_CXX14_INITIALIZED_LAMBDA_CAPTURES) + file_name_generator = [file_counter_fmt = file_counter_formatter(counter_pos, width), name_pattern](unsigned int counter) + { return file_counter_fmt(name_pattern, counter); }; +#else + file_counter_formatter file_counter_fmt(counter_pos, width); + file_name_generator = [file_counter_fmt, name_pattern](unsigned int counter) + { return file_counter_fmt(name_pattern, counter); }; +#endif + } + else + { + // No placeholders detected + file_name_generator = empty_formatter(name_pattern); + } + + has_file_counter = counter_found; + } + class file_collector_repository; @@ -524,6 +651,38 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { //! Information about a single stored file struct file_info { + //! Ordering predicate by timestamp + struct order_by_timestamp + { + typedef bool result_type; + + result_type operator()(file_info const& left, file_info const& right) const BOOST_NOEXCEPT + { + return left.m_TimeStamp < right.m_TimeStamp; + } + }; + + //! Predicate for testing if a file_info refers to a file equivalent to another path + class equivalent_file + { + public: + typedef bool result_type; + + private: + filesystem::path const& m_Path; + + public: + explicit equivalent_file(filesystem::path const& path) BOOST_NOEXCEPT : + m_Path(path) + { + } + + result_type operator()(file_info const& info) const + { + return filesystem::equivalent(info.m_Path, m_Path); + } + }; + uintmax_t m_Size; std::time_t m_TimeStamp; filesystem::path m_Path; @@ -539,7 +698,7 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { #if !defined(BOOST_LOG_NO_THREADS) //! Synchronization mutex - mutex m_Mutex; + std::mutex m_Mutex; #endif // !defined(BOOST_LOG_NO_THREADS) //! Total file size upper limit @@ -573,14 +732,16 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { uintmax_t max_files); //! Destructor - ~file_collector(); + ~file_collector() BOOST_OVERRIDE; //! The function stores the specified file in the storage - void store_file(filesystem::path const& file_name); + void store_file(filesystem::path const& file_name) BOOST_OVERRIDE; + + //! The function checks if the specified path refers to an existing file in the storage + bool is_in_storage(filesystem::path const& src_path) const BOOST_OVERRIDE; //! Scans the target directory for the files that have already been stored - uintmax_t scan_for_files( - file::scan_method method, filesystem::path const& pattern, unsigned int* counter); + file::scan_result scan_for_files(file::scan_method method, filesystem::path const& pattern) BOOST_OVERRIDE; //! The function updates storage restrictions void update(uintmax_t max_size, uintmax_t min_free_space, uintmax_t max_files); @@ -593,7 +754,7 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { private: //! Makes relative path absolute with respect to the base path - filesystem::path make_absolute(filesystem::path const& p) + filesystem::path make_absolute(filesystem::path const& p) const { return filesystem::absolute(p, m_BasePath); } @@ -628,7 +789,7 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { private: #if !defined(BOOST_LOG_NO_THREADS) //! Synchronization mutex - mutex m_Mutex; + std::mutex m_Mutex; #endif // !defined(BOOST_LOG_NO_THREADS) //! The list of file collectors file_collectors m_Collectors; @@ -687,14 +848,14 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { info.m_TimeStamp = filesystem::last_write_time(src_path); info.m_Size = filesystem::file_size(src_path); - filesystem::path file_name_path = src_path.filename(); - path_string_type file_name = file_name_path.native(); + const filesystem::path file_name_path = src_path.filename(); + path_string_type const& file_name = file_name_path.native(); info.m_Path = m_StorageDir / file_name_path; // Check if the file is already in the target directory filesystem::path src_dir = src_path.has_parent_path() ? - filesystem::system_complete(src_path.parent_path()) : - m_BasePath; + filesystem::system_complete(src_path.parent_path()) : + m_BasePath; const bool is_in_target_dir = filesystem::equivalent(src_dir, m_StorageDir); if (!is_in_target_dir) { @@ -727,7 +888,7 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { filesystem::create_directories(m_StorageDir); } - BOOST_LOG_EXPR_IF_MT(lock_guard< mutex > lock(m_Mutex);) + BOOST_LOG_EXPR_IF_MT(std::lock_guard< std::mutex > lock(m_Mutex);) file_list::iterator it = m_Files.begin(); const file_list::iterator end = m_Files.end(); @@ -776,7 +937,7 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { if (m_MinFreeSpace) free_space = filesystem::space(m_StorageDir).available; m_TotalSize -= old_info.m_Size; - m_Files.erase(it++); + it = m_Files.erase(it); } catch (system::system_error&) { @@ -788,7 +949,7 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { { // If it's not a file or is absent, just remove it from the list m_TotalSize -= old_info.m_Size; - m_Files.erase(it++); + it = m_Files.erase(it); } } @@ -802,11 +963,34 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { m_TotalSize += info.m_Size; } + //! The function checks if the specified path refers to an existing file in the storage + bool file_collector::is_in_storage(filesystem::path const& src_path) const + { + const filesystem::path file_name_path = src_path.filename(); + const filesystem::path trg_path = m_StorageDir / file_name_path; + + // Check if the file is already in the target directory + system::error_code ec; + filesystem::path src_dir = src_path.has_parent_path() ? + filesystem::system_complete(src_path.parent_path(), ec) : + m_BasePath; + if (ec) + return false; + + filesystem::file_status status = filesystem::status(trg_path, ec); + if (ec || status.type() != filesystem::regular_file) + return false; + bool equiv = filesystem::equivalent(src_dir / file_name_path, trg_path, ec); + if (ec) + return false; + + return equiv; + } + //! Scans the target directory for the files that have already been stored - uintmax_t file_collector::scan_for_files( - file::scan_method method, filesystem::path const& pattern, unsigned int* counter) + file::scan_result file_collector::scan_for_files(file::scan_method method, filesystem::path const& pattern) { - uintmax_t file_count = 0; + file::scan_result result; if (method != file::no_scan) { filesystem::path dir = m_StorageDir; @@ -817,43 +1001,29 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { if (pattern.has_parent_path()) dir = make_absolute(pattern.parent_path()); } - else - { - counter = NULL; - } system::error_code ec; filesystem::file_status status = filesystem::status(dir, ec); if (status.type() == filesystem::directory_file) { - BOOST_LOG_EXPR_IF_MT(lock_guard< mutex > lock(m_Mutex);) - - if (counter) - *counter = 0; + BOOST_LOG_EXPR_IF_MT(std::lock_guard< std::mutex > lock(m_Mutex);) file_list files; filesystem::directory_iterator it(dir), end; - uintmax_t total_size = 0; + uintmax_t total_size = 0u; for (; it != end; ++it) { + filesystem::directory_entry const& dir_entry = *it; file_info info; - info.m_Path = *it; - status = filesystem::status(info.m_Path, ec); + info.m_Path = dir_entry.path(); + status = dir_entry.status(ec); if (status.type() == filesystem::regular_file) { // Check that there are no duplicates in the resulting list - struct local - { - static bool equivalent(filesystem::path const& left, file_info const& right) - { - return filesystem::equivalent(left, right.m_Path); - } - }; - if (std::find_if(m_Files.begin(), m_Files.end(), - boost::bind(&local::equivalent, boost::cref(info.m_Path), _1)) == m_Files.end()) + if (std::find_if(m_Files.begin(), m_Files.end(), file_info::equivalent_file(info.m_Path)) == m_Files.end()) { // Check that the file name matches the pattern - unsigned int file_number = 0; + unsigned int file_number = 0u; bool file_number_parsed = false; if (method != file::scan_matching || match_pattern(filename_string(info.m_Path), mask, file_number, file_number_parsed)) @@ -862,11 +1032,11 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { total_size += info.m_Size; info.m_TimeStamp = filesystem::last_write_time(info.m_Path); files.push_back(info); - ++file_count; + ++result.found_count; - // Test that the file_number >= *counter accounting for the integer overflow - if (file_number_parsed && counter != NULL && (file_number - *counter) < ((~0u) ^ ((~0u) >> 1))) - *counter = file_number + 1u; + // Test that the file_number >= result.last_file_counter accounting for the integer overflow + if (file_number_parsed && (!result.last_file_counter || (file_number - *result.last_file_counter) < ((~0u) ^ ((~0u) >> 1u)))) + result.last_file_counter = file_number; } } } @@ -875,17 +1045,17 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { // Sort files chronologically m_Files.splice(m_Files.end(), files); m_TotalSize += total_size; - m_Files.sort(boost::bind(&file_info::m_TimeStamp, _1) < boost::bind(&file_info::m_TimeStamp, _2)); + m_Files.sort(file_info::order_by_timestamp()); } } - return file_count; + return result; } //! The function updates storage restrictions void file_collector::update(uintmax_t max_size, uintmax_t min_free_space, uintmax_t max_files) { - BOOST_LOG_EXPR_IF_MT(lock_guard< mutex > lock(m_Mutex);) + BOOST_LOG_EXPR_IF_MT(std::lock_guard< std::mutex > lock(m_Mutex);) m_MaxSize = (std::min)(m_MaxSize, max_size); m_MinFreeSpace = (std::max)(m_MinFreeSpace, min_free_space); @@ -897,10 +1067,10 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { shared_ptr< file::collector > file_collector_repository::get_collector( filesystem::path const& target_dir, uintmax_t max_size, uintmax_t min_free_space, uintmax_t max_files) { - BOOST_LOG_EXPR_IF_MT(lock_guard< mutex > lock(m_Mutex);) + BOOST_LOG_EXPR_IF_MT(std::lock_guard< std::mutex > lock(m_Mutex);) file_collectors::iterator it = std::find_if(m_Collectors.begin(), m_Collectors.end(), - boost::bind(&file_collector::is_governed, _1, boost::cref(target_dir))); + [&target_dir](file_collector const& collector) { return collector.is_governed(target_dir); }); shared_ptr< file_collector > p; if (it != m_Collectors.end()) try { @@ -925,26 +1095,26 @@ BOOST_LOG_ANONYMOUS_NAMESPACE { //! Removes the file collector from the list void file_collector_repository::remove_collector(file_collector* p) { - BOOST_LOG_EXPR_IF_MT(lock_guard< mutex > lock(m_Mutex);) + BOOST_LOG_EXPR_IF_MT(std::lock_guard< std::mutex > lock(m_Mutex);) m_Collectors.erase(m_Collectors.iterator_to(*p)); } //! Checks if the time point is valid void check_time_point_validity(unsigned char hour, unsigned char minute, unsigned char second) { - if (hour >= 24) + if (BOOST_UNLIKELY(hour >= 24)) { std::ostringstream strm; strm << "Time point hours value is out of range: " << static_cast< unsigned int >(hour); BOOST_THROW_EXCEPTION(std::out_of_range(strm.str())); } - if (minute >= 60) + if (BOOST_UNLIKELY(minute >= 60)) { std::ostringstream strm; strm << "Time point minutes value is out of range: " << static_cast< unsigned int >(minute); BOOST_THROW_EXCEPTION(std::out_of_range(strm.str())); } - if (second >= 60) + if (BOOST_UNLIKELY(second >= 60)) { std::ostringstream strm; strm << "Time point seconds value is out of range: " << static_cast< unsigned int >(second); @@ -976,8 +1146,8 @@ BOOST_LOG_API rotation_at_time_point::rotation_at_time_point( unsigned char minute, unsigned char second ) : - m_DayKind(not_specified), m_Day(0), + m_DayKind(not_specified), m_Hour(hour), m_Minute(minute), m_Second(second), @@ -993,8 +1163,8 @@ BOOST_LOG_API rotation_at_time_point::rotation_at_time_point( unsigned char minute, unsigned char second ) : - m_DayKind(weekday), m_Day(static_cast< unsigned char >(wday)), + m_DayKind(weekday), m_Hour(hour), m_Minute(minute), m_Second(second), @@ -1010,8 +1180,8 @@ BOOST_LOG_API rotation_at_time_point::rotation_at_time_point( unsigned char minute, unsigned char second ) : - m_DayKind(monthday), m_Day(static_cast< unsigned char >(mday.as_number())), + m_DayKind(monthday), m_Hour(hour), m_Minute(minute), m_Second(second), @@ -1037,7 +1207,7 @@ BOOST_LOG_API bool rotation_at_time_point::operator()() const } const bool time_of_day_passed = rotation_time.total_seconds() <= m_Previous.time_of_day().total_seconds(); - switch (m_DayKind) + switch (static_cast< day_kind >(m_DayKind)) { case not_specified: { @@ -1120,19 +1290,28 @@ BOOST_LOG_API bool rotation_at_time_interval::operator()() const //! Sink implementation data struct text_file_backend::implementation { - //! File open mode - std::ios_base::openmode m_FileOpenMode; - //! File name pattern filesystem::path m_FileNamePattern; //! Directory to store files in filesystem::path m_StorageDir; //! File name generator (according to m_FileNamePattern) boost::log::aux::light_function< path_string_type (unsigned int) > m_FileNameGenerator; + //! The flag indicates whether m_FileNamePattern has a file counter placeholder + bool m_FileNamePatternHasCounter; - //! Stored files counter + //! Target file name pattern + filesystem::path m_TargetFileNamePattern; + //! Target directory to store files in + filesystem::path m_TargetStorageDir; + //! Target file name generator (according to m_TargetFileNamePattern) + boost::log::aux::light_function< path_string_type (unsigned int) > m_TargetFileNameGenerator; + + //! Counter to use in file names unsigned int m_FileCounter; + //! File open mode + std::ios_base::openmode m_FileOpenMode; + //! Current file name filesystem::path m_FileName; //! File stream @@ -1151,18 +1330,29 @@ struct text_file_backend::implementation uintmax_t m_FileRotationSize; //! Time-based rotation predicate time_based_rotation_predicate m_TimeBasedRotation; + //! Indicates whether to append a trailing newline after every log record + auto_newline_mode m_AutoNewlineMode; //! The flag shows if every written record should be flushed bool m_AutoFlush; //! The flag indicates whether the final rotation should be performed bool m_FinalRotationEnabled; - implementation(uintmax_t rotation_size, bool auto_flush, bool enable_final_rotation) : + //! The flag indicates that \c m_FileCounter is set to the last used counter value + bool m_FileCounterIsLastUsed; + //! The flag indicates whether the next opened file will be the first file opened by this backend + bool m_IsFirstFile; + + implementation(uintmax_t rotation_size, auto_newline_mode auto_newline, bool auto_flush, bool enable_final_rotation) : + m_FileNamePatternHasCounter(false), + m_FileCounter(0u), m_FileOpenMode(std::ios_base::trunc | std::ios_base::out), - m_FileCounter(0), - m_CharactersWritten(0), + m_CharactersWritten(0u), m_FileRotationSize(rotation_size), + m_AutoNewlineMode(auto_newline), m_AutoFlush(auto_flush), - m_FinalRotationEnabled(enable_final_rotation) + m_FinalRotationEnabled(enable_final_rotation), + m_FileCounterIsLastUsed(false), + m_IsFirstFile(true) { } }; @@ -1192,14 +1382,17 @@ BOOST_LOG_API text_file_backend::~text_file_backend() //! Constructor implementation BOOST_LOG_API void text_file_backend::construct( filesystem::path const& pattern, + filesystem::path const& target_file_name, std::ios_base::openmode mode, uintmax_t rotation_size, time_based_rotation_predicate const& time_based_rotation, + auto_newline_mode auto_newline, bool auto_flush, bool enable_final_rotation) { - m_pImpl = new implementation(rotation_size, auto_flush, enable_final_rotation); + m_pImpl = new implementation(rotation_size, auto_newline, auto_flush, enable_final_rotation); set_file_name_pattern_internal(pattern); + set_target_file_name_pattern_internal(target_file_name); set_time_based_rotation(time_based_rotation); set_open_mode(mode); } @@ -1228,6 +1421,12 @@ BOOST_LOG_API void text_file_backend::auto_flush(bool enable) m_pImpl->m_AutoFlush = enable; } +//! Selects whether a trailing newline should be automatically inserted after every log record. +BOOST_LOG_API void text_file_backend::set_auto_newline_mode(auto_newline_mode mode) +{ + m_pImpl->m_AutoNewlineMode = mode; +} + //! The method writes the message to the sink BOOST_LOG_API void text_file_backend::consume(record_view const& rec, string_type const& formatted_message) { @@ -1239,7 +1438,7 @@ BOOST_LOG_API void text_file_backend::consume(record_view const& rec, string_typ { // The file stream is not operational. One possible reason is that there is no more free space // on the file system. In this case it is possible that this log record will fail to be written as well, - // leaving the newly creted file empty. Eventually this results in lots of empty log files. + // leaving the newly created file empty. Eventually this results in lots of empty log files. // We should take precautions to avoid this. https://svn.boost.org/trac/boost/ticket/11016 prev_file_name = m_pImpl->m_FileName; close_file(); @@ -1261,7 +1460,7 @@ BOOST_LOG_API void text_file_backend::consume(record_view const& rec, string_typ ( m_pImpl->m_File.is_open() && ( - m_pImpl->m_CharactersWritten + formatted_message.size() >= m_pImpl->m_FileRotationSize || + (m_pImpl->m_CharactersWritten > m_pImpl->m_FileRotationSize || (m_pImpl->m_FileRotationSize - m_pImpl->m_CharactersWritten) < formatted_message.size()) || (!m_pImpl->m_TimeBasedRotation.empty() && m_pImpl->m_TimeBasedRotation()) ) ) @@ -1269,13 +1468,50 @@ BOOST_LOG_API void text_file_backend::consume(record_view const& rec, string_typ rotate_file(); } - if (!m_pImpl->m_File.is_open()) + for (const unsigned int last_file_counter = m_pImpl->m_FileCounter - 1u; !m_pImpl->m_File.is_open();) { filesystem::path new_file_name; if (!use_prev_file_name) - new_file_name = m_pImpl->m_StorageDir / m_pImpl->m_FileNameGenerator(m_pImpl->m_FileCounter++); + { + unsigned int file_counter = m_pImpl->m_FileCounter; + if (BOOST_LIKELY(m_pImpl->m_FileCounterIsLastUsed)) + { + // If the sink backend is configured to append to a previously written file and the file pattern + // includes a file counter, don't increment the counter and try to open the existing file, with the last + // used counter value. Only do this if the file is not moved to a different storage location by the file collector. + bool increment_file_counter = true; + if (BOOST_UNLIKELY(m_pImpl->m_IsFirstFile && (m_pImpl->m_FileOpenMode & std::ios_base::app) != 0 && m_pImpl->m_FileNamePatternHasCounter)) + { + filesystem::path last_file_name = m_pImpl->m_StorageDir / m_pImpl->m_FileNameGenerator(file_counter); + if (!!m_pImpl->m_pFileCollector) + { + increment_file_counter = !m_pImpl->m_pFileCollector->is_in_storage(last_file_name); + } + else + { + system::error_code ec; + increment_file_counter = filesystem::status(last_file_name, ec).type() != filesystem::regular_file; + } + } + + if (BOOST_LIKELY(increment_file_counter)) + { + ++file_counter; + m_pImpl->m_FileCounter = file_counter; + } + } + else + { + m_pImpl->m_FileCounterIsLastUsed = true; + } + + new_file_name = m_pImpl->m_StorageDir / m_pImpl->m_FileNameGenerator(file_counter); + } else + { prev_file_name.swap(new_file_name); + use_prev_file_name = false; + } filesystem::create_directories(new_file_name.parent_path()); @@ -1288,17 +1524,63 @@ BOOST_LOG_API void text_file_backend::consume(record_view const& rec, string_typ system::error_code(system::errc::io_error, system::generic_category()))); } m_pImpl->m_FileName.swap(new_file_name); + m_pImpl->m_IsFirstFile = false; + + // Check the file size before invoking the open handler, as it may write more data to the file. + // Only do this check if the file counter is present in the file name and we haven't exhausted it to avoid looping indefinitely. + m_pImpl->m_CharactersWritten = static_cast< std::streamoff >(m_pImpl->m_File.tellp()); + if (m_pImpl->m_CharactersWritten > 0 && + (m_pImpl->m_CharactersWritten > m_pImpl->m_FileRotationSize || (m_pImpl->m_FileRotationSize - m_pImpl->m_CharactersWritten) < formatted_message.size()) && + m_pImpl->m_FileNamePatternHasCounter && m_pImpl->m_FileCounter != last_file_counter) + { + // Avoid running the close handler, as we haven't run the open handler yet + struct close_handler_backup_guard + { + explicit close_handler_backup_guard(close_handler_type& orig_close_handler) BOOST_NOEXCEPT : + m_orig_close_handler(orig_close_handler) + { + orig_close_handler.swap(m_backup_close_handler); + } + ~close_handler_backup_guard() BOOST_NOEXCEPT + { + m_orig_close_handler.swap(m_backup_close_handler); + } + + private: + close_handler_type& m_orig_close_handler; + close_handler_type m_backup_close_handler; + } + close_handler_guard(m_pImpl->m_CloseHandler); + + rotate_file(); + continue; + } if (!m_pImpl->m_OpenHandler.empty()) + { m_pImpl->m_OpenHandler(m_pImpl->m_File); - m_pImpl->m_CharactersWritten = static_cast< std::streamoff >(m_pImpl->m_File.tellp()); + // Update the size of the written data, but don't rotate the file. If the open handler + // exceeds the file size limit we could end up in an infinite loop, as we are constantly + // rotating the file and immediately exceeding its size limit after the open handler is run. + // Write the log record and then rotate the file upon the next log record. + m_pImpl->m_CharactersWritten = static_cast< std::streamoff >(m_pImpl->m_File.tellp()); + } + + break; } m_pImpl->m_File.write(formatted_message.data(), static_cast< std::streamsize >(formatted_message.size())); - m_pImpl->m_File.put(traits_t::newline); + m_pImpl->m_CharactersWritten += formatted_message.size(); - m_pImpl->m_CharactersWritten += formatted_message.size() + 1; + if (m_pImpl->m_AutoNewlineMode != disabled_auto_newline) + { + if (m_pImpl->m_AutoNewlineMode == always_insert || formatted_message.empty() || *formatted_message.rbegin() != traits_t::newline) + { + m_pImpl->m_File.put(traits_t::newline); + ++m_pImpl->m_CharactersWritten; + } + } if (m_pImpl->m_AutoFlush) m_pImpl->m_File.flush(); @@ -1311,89 +1593,34 @@ BOOST_LOG_API void text_file_backend::flush() m_pImpl->m_File.flush(); } -//! The method sets file name mask +//! The method sets file name pattern BOOST_LOG_API void text_file_backend::set_file_name_pattern_internal(filesystem::path const& pattern) { - // Note: avoid calling Boost.Filesystem functions that involve path::codecvt() - // https://svn.boost.org/trac/boost/ticket/9119 - typedef file_char_traits< path_char_type > traits_t; - filesystem::path p = pattern; - if (p.empty()) - p = filesystem::path(traits_t::default_file_name_pattern()); - - m_pImpl->m_FileNamePattern = p.filename(); - path_string_type name_pattern = m_pImpl->m_FileNamePattern.native(); - m_pImpl->m_StorageDir = filesystem::absolute(p.parent_path()); - - // Let's try to find the file counter placeholder - unsigned int placeholder_count = 0; - unsigned int width = 0; - bool counter_found = false; - path_string_type::size_type counter_pos = 0; - path_string_type::const_iterator end = name_pattern.end(); - path_string_type::const_iterator it = name_pattern.begin(); - - do - { - it = std::find(it, end, traits_t::percent); - if (it == end) - break; - path_string_type::const_iterator placeholder_begin = it++; - if (it == end) - break; - if (*it == traits_t::percent) - { - // An escaped percent detected - ++it; - continue; - } - - ++placeholder_count; - if (!counter_found) - { - path_string_type::const_iterator it2 = it; - if (parse_counter_placeholder(it2, end, width)) - { - // We've found the file counter placeholder in the pattern - counter_found = true; - counter_pos = placeholder_begin - name_pattern.begin(); - name_pattern.erase(counter_pos, it2 - placeholder_begin); - --placeholder_count; - it = name_pattern.begin() + counter_pos; - end = name_pattern.end(); - } - } - } - while (it != end); + parse_file_name_pattern + ( + !pattern.empty() ? pattern : filesystem::path(traits_t::default_file_name_pattern()), + m_pImpl->m_StorageDir, + m_pImpl->m_FileNamePattern, + m_pImpl->m_FileNameGenerator, + m_pImpl->m_FileNamePatternHasCounter + ); +} - // Construct the formatter functor - if (placeholder_count > 0) - { - if (counter_found) - { - // Both counter and date/time placeholder in the pattern - m_pImpl->m_FileNameGenerator = boost::bind(date_and_time_formatter(), - boost::bind(file_counter_formatter(counter_pos, width), name_pattern, _1), _1); - } - else - { - // Only date/time placeholders in the pattern - m_pImpl->m_FileNameGenerator = - boost::bind(date_and_time_formatter(), name_pattern, _1); - } - } - else if (counter_found) +//! The method sets target file name pattern +BOOST_LOG_API void text_file_backend::set_target_file_name_pattern_internal(filesystem::path const& pattern) +{ + if (!pattern.empty()) { - // Only counter placeholder in the pattern - m_pImpl->m_FileNameGenerator = - boost::bind(file_counter_formatter(counter_pos, width), name_pattern, _1); + bool has_file_counter = false; + parse_file_name_pattern(pattern, m_pImpl->m_TargetStorageDir, m_pImpl->m_TargetFileNamePattern, m_pImpl->m_TargetFileNameGenerator, has_file_counter); } else { - // No placeholders detected - m_pImpl->m_FileNameGenerator = empty_formatter(name_pattern); + m_pImpl->m_TargetStorageDir.clear(); + m_pImpl->m_TargetFileNamePattern.clear(); + m_pImpl->m_TargetFileNameGenerator.clear(); } } @@ -1428,12 +1655,25 @@ BOOST_LOG_API void text_file_backend::rotate_file() filesystem::path prev_file_name = m_pImpl->m_FileName; close_file(); - if (!!m_pImpl->m_pFileCollector) + // Check if the file has been created in the first place + system::error_code ec; + filesystem::file_status status = filesystem::status(prev_file_name, ec); + if (status.type() == filesystem::regular_file) { - // Check if the file has not been deleted by another process - system::error_code ec; - filesystem::file_status status = filesystem::status(prev_file_name, ec); - if (status.type() == filesystem::regular_file) + if (!!m_pImpl->m_TargetFileNameGenerator) + { + filesystem::path new_file_name = m_pImpl->m_TargetStorageDir / m_pImpl->m_TargetFileNameGenerator(m_pImpl->m_FileCounter); + + if (new_file_name != prev_file_name) + { + filesystem::create_directories(new_file_name.parent_path()); + move_file(prev_file_name, new_file_name); + + prev_file_name.swap(new_file_name); + } + } + + if (!!m_pImpl->m_pFileCollector) m_pImpl->m_pFileCollector->store_file(prev_file_name); } } @@ -1443,7 +1683,9 @@ BOOST_LOG_API void text_file_backend::set_open_mode(std::ios_base::openmode mode { mode |= std::ios_base::out; mode &= ~std::ios_base::in; - if ((mode & (std::ios_base::trunc | std::ios_base::app)) == 0) + if ((mode & std::ios_base::app) != 0) + mode |= std::ios_base::ate; // we need to seek to end after opening the file to obtain its size + else mode |= std::ios_base::trunc; m_pImpl->m_FileOpenMode = mode; } @@ -1475,15 +1717,27 @@ BOOST_LOG_API filesystem::path text_file_backend::get_current_file_name() const //! Performs scanning of the target directory for log files BOOST_LOG_API uintmax_t text_file_backend::scan_for_files(file::scan_method method, bool update_counter) { - if (m_pImpl->m_pFileCollector) + if (BOOST_UNLIKELY(!m_pImpl->m_pFileCollector)) { - unsigned int* counter = update_counter ? &m_pImpl->m_FileCounter : static_cast< unsigned int* >(NULL); - return m_pImpl->m_pFileCollector->scan_for_files(method, m_pImpl->m_FileNamePattern, counter); + BOOST_LOG_THROW_DESCR(setup_error, "File collector is not set"); } - else + + file::scan_result result = m_pImpl->m_pFileCollector->scan_for_files + ( + method, + m_pImpl->m_TargetFileNamePattern.empty() ? m_pImpl->m_FileNamePattern : m_pImpl->m_TargetFileNamePattern + ); + + if (update_counter && !!result.last_file_counter) { - BOOST_LOG_THROW_DESCR(setup_error, "File collector is not set"); + if (!m_pImpl->m_FileCounterIsLastUsed || (*result.last_file_counter - m_pImpl->m_FileCounter) < ((~0u) ^ ((~0u) >> 1u))) + { + m_pImpl->m_FileCounter = *result.last_file_counter; + m_pImpl->m_FileCounterIsLastUsed = true; + } } + + return result.found_count; } } // namespace sinks diff --git a/src/text_multifile_backend.cpp b/src/text_multifile_backend.cpp index 58669deb66..550dd57f69 100644 --- a/src/text_multifile_backend.cpp +++ b/src/text_multifile_backend.cpp @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include @@ -36,9 +38,12 @@ struct text_multifile_backend::implementation const filesystem::path m_BasePath; //! File stream filesystem::ofstream m_File; + //! Indicates whether to append a trailing newline after every log record + auto_newline_mode m_AutoNewlineMode; - implementation() : - m_BasePath(filesystem::current_path()) + explicit implementation(auto_newline_mode auto_newline) : + m_BasePath(filesystem::current_path()), + m_AutoNewlineMode(auto_newline) { } @@ -50,8 +55,15 @@ struct text_multifile_backend::implementation }; //! Default constructor -BOOST_LOG_API text_multifile_backend::text_multifile_backend() : m_pImpl(new implementation()) +BOOST_LOG_API text_multifile_backend::text_multifile_backend() { + construct(log::aux::empty_arg_list()); +} + +//! Constructor implementation +BOOST_LOG_API void text_multifile_backend::construct(auto_newline_mode auto_newline) +{ + m_pImpl = new implementation(auto_newline); } //! Destructor @@ -66,18 +78,28 @@ BOOST_LOG_API void text_multifile_backend::set_file_name_composer_internal(file_ m_pImpl->m_FileNameComposer = composer; } +//! Selects whether a trailing newline should be automatically inserted after every log record. +BOOST_LOG_API void text_multifile_backend::set_auto_newline_mode(auto_newline_mode mode) +{ + m_pImpl->m_AutoNewlineMode = mode; +} + //! The method writes the message to the sink BOOST_LOG_API void text_multifile_backend::consume(record_view const& rec, string_type const& formatted_message) { - if (!m_pImpl->m_FileNameComposer.empty()) + if (BOOST_LIKELY(!m_pImpl->m_FileNameComposer.empty())) { filesystem::path file_name = m_pImpl->make_absolute(m_pImpl->m_FileNameComposer(rec)); filesystem::create_directories(file_name.parent_path()); m_pImpl->m_File.open(file_name, std::ios_base::out | std::ios_base::app); - if (m_pImpl->m_File.is_open()) + if (BOOST_LIKELY(m_pImpl->m_File.is_open())) { m_pImpl->m_File.write(formatted_message.data(), static_cast< std::streamsize >(formatted_message.size())); - m_pImpl->m_File.put(static_cast< string_type::value_type >('\n')); + if (m_pImpl->m_AutoNewlineMode != disabled_auto_newline) + { + if (m_pImpl->m_AutoNewlineMode == always_insert || formatted_message.empty() || *formatted_message.rbegin() != static_cast< string_type::value_type >('\n')) + m_pImpl->m_File.put(static_cast< string_type::value_type >('\n')); + } m_pImpl->m_File.close(); } } diff --git a/src/text_ostream_backend.cpp b/src/text_ostream_backend.cpp index d803d0dba4..047b0c00ff 100644 --- a/src/text_ostream_backend.cpp +++ b/src/text_ostream_backend.cpp @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include #include @@ -34,10 +36,14 @@ struct basic_text_ostream_backend< CharT >::implementation //! Output stream list ostream_sequence m_Streams; + //! Indicates whether to append a trailing newline after every log record + auto_newline_mode m_AutoNewlineMode; //! Auto-flush flag bool m_fAutoFlush; - implementation() : m_fAutoFlush(false) + implementation(auto_newline_mode auto_newline, bool auto_flush) : + m_AutoNewlineMode(auto_newline), + m_fAutoFlush(auto_flush) { } }; @@ -45,8 +51,16 @@ struct basic_text_ostream_backend< CharT >::implementation //! Constructor template< typename CharT > -BOOST_LOG_API basic_text_ostream_backend< CharT >::basic_text_ostream_backend() : m_pImpl(new implementation()) +BOOST_LOG_API basic_text_ostream_backend< CharT >::basic_text_ostream_backend() { + construct(log::aux::empty_arg_list()); +} + +//! Constructor implementation +template< typename CharT > +BOOST_LOG_API void basic_text_ostream_backend< CharT >::construct(auto_newline_mode auto_newline, bool auto_flush) +{ + m_pImpl = new implementation(auto_newline, auto_flush); } //! Destructor (just to make it link from the shared library) @@ -56,6 +70,13 @@ BOOST_LOG_API basic_text_ostream_backend< CharT >::~basic_text_ostream_backend() delete m_pImpl; } +//! Selects whether a trailing newline should be automatically inserted after every log record. +template< typename CharT > +BOOST_LOG_API void basic_text_ostream_backend< CharT >::set_auto_newline_mode(auto_newline_mode mode) +{ + m_pImpl->m_AutoNewlineMode = mode; +} + //! The method adds a new stream to the sink template< typename CharT > BOOST_LOG_API void basic_text_ostream_backend< CharT >::add_stream(shared_ptr< stream_type > const& strm) @@ -93,13 +114,18 @@ BOOST_LOG_API void basic_text_ostream_backend< CharT >::consume(record_view cons typename string_type::size_type const s = message.size(); typename implementation::ostream_sequence::const_iterator it = m_pImpl->m_Streams.begin(), end = m_pImpl->m_Streams.end(); + bool need_trailing_newline = false; + if (m_pImpl->m_AutoNewlineMode != disabled_auto_newline) + need_trailing_newline = (m_pImpl->m_AutoNewlineMode == always_insert || s == 0u || p[s - 1u] != static_cast< char_type >('\n')); + for (; it != end; ++it) { stream_type* const strm = it->get(); - if (strm->good()) + if (BOOST_LIKELY(strm->good())) { strm->write(p, static_cast< std::streamsize >(s)); - strm->put(static_cast< char_type >('\n')); + if (need_trailing_newline) + strm->put(static_cast< char_type >('\n')); if (m_pImpl->m_fAutoFlush) strm->flush(); @@ -116,7 +142,7 @@ BOOST_LOG_API void basic_text_ostream_backend< CharT >::flush() for (; it != end; ++it) { stream_type* const strm = it->get(); - if (strm->good()) + if (BOOST_LIKELY(strm->good())) strm->flush(); } } diff --git a/src/thread_id.cpp b/src/thread_id.cpp index e3a70a4626..c80d7ab69d 100644 --- a/src/thread_id.cpp +++ b/src/thread_id.cpp @@ -29,7 +29,7 @@ #include #include #elif defined(BOOST_WINDOWS) -#include +#include // at_thread_exit #include #else #include diff --git a/src/thread_specific.cpp b/src/thread_specific.cpp index d8255738a9..851d4c166f 100644 --- a/src/thread_specific.cpp +++ b/src/thread_specific.cpp @@ -69,7 +69,7 @@ BOOST_LOG_CLOSE_NAMESPACE // namespace log #include #include #include -#include +#include #include #include #include @@ -128,13 +128,13 @@ struct pthread_key_traits< KeyT, true, true > typedef KeyT pthread_key_type; #if defined(BOOST_HAS_INTPTR_T) - typedef typename mpl::if_c< + typedef typename boost::conditional< boost::is_signed< pthread_key_type >::value, intptr_t, uintptr_t >::type intptr_type; #else - typedef typename mpl::if_c< + typedef typename boost::conditional< boost::is_signed< pthread_key_type >::value, std::ptrdiff_t, std::size_t diff --git a/src/threadsafe_queue.cpp b/src/threadsafe_queue.cpp index bbad463dec..3ca581ca0a 100644 --- a/src/threadsafe_queue.cpp +++ b/src/threadsafe_queue.cpp @@ -1,5 +1,5 @@ /* - * Copyright Andrey Semashev 2007 - 2015. + * Copyright Andrey Semashev 2007 - 2021. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include #include @@ -76,11 +78,20 @@ class threadsafe_queue_impl_generic : m_Head.node = m_Tail.node = first_node; } - ~threadsafe_queue_impl_generic() + static void* operator new (std::size_t size) { + void* p = alignment::aligned_alloc(BOOST_LOG_CPU_CACHE_LINE_SIZE, size); + if (BOOST_UNLIKELY(!p)) + BOOST_THROW_EXCEPTION(std::bad_alloc()); + return p; } - node_base* reset_last_node() + static void operator delete (void* p, std::size_t) BOOST_NOEXCEPT + { + alignment::aligned_free(p); + } + + node_base* reset_last_node() BOOST_NOEXCEPT { BOOST_ASSERT(m_Head.node == m_Tail.node); node_base* p = m_Head.node; @@ -88,7 +99,7 @@ class threadsafe_queue_impl_generic : return p; } - bool unsafe_empty() + bool unsafe_empty() const BOOST_NOEXCEPT { return m_Head.node == m_Tail.node; } @@ -96,14 +107,14 @@ class threadsafe_queue_impl_generic : void push(node_base* p) { set_next(p, NULL); - exclusive_lock_guard< mutex_type > _(m_Tail.mutex); + exclusive_lock_guard< mutex_type > lock(m_Tail.mutex); set_next(m_Tail.node, p); m_Tail.node = p; } bool try_pop(node_base*& node_to_free, node_base*& node_with_value) { - exclusive_lock_guard< mutex_type > _(m_Head.mutex); + exclusive_lock_guard< mutex_type > lock(m_Head.mutex); node_base* next = get_next(m_Head.node); if (next) { @@ -119,11 +130,11 @@ class threadsafe_queue_impl_generic : private: BOOST_FORCEINLINE static void set_next(node_base* p, node_base* next) { - p->next.data[0] = next; + p->next.store(next, boost::memory_order_relaxed); } BOOST_FORCEINLINE static node_base* get_next(node_base* p) { - return static_cast< node_base* >(p->next.data[0]); + return p->next.load(boost::memory_order_relaxed); } // Copying and assignment are closed @@ -131,22 +142,42 @@ class threadsafe_queue_impl_generic : BOOST_DELETED_FUNCTION(threadsafe_queue_impl_generic& operator= (threadsafe_queue_impl_generic const&)) }; +inline threadsafe_queue_impl::threadsafe_queue_impl() +{ +} + +inline threadsafe_queue_impl::~threadsafe_queue_impl() +{ +} + BOOST_LOG_API threadsafe_queue_impl* threadsafe_queue_impl::create(node_base* first_node) { return new threadsafe_queue_impl_generic(first_node); } -BOOST_LOG_API void* threadsafe_queue_impl::operator new (std::size_t size) +BOOST_LOG_API void threadsafe_queue_impl::destroy(threadsafe_queue_impl* impl) BOOST_NOEXCEPT +{ + delete static_cast< threadsafe_queue_impl_generic* >(impl); +} + +BOOST_LOG_API threadsafe_queue_impl::node_base* threadsafe_queue_impl::reset_last_node(threadsafe_queue_impl* impl) BOOST_NOEXCEPT +{ + return static_cast< threadsafe_queue_impl_generic* >(impl)->reset_last_node(); +} + +BOOST_LOG_API bool threadsafe_queue_impl::unsafe_empty(const threadsafe_queue_impl* impl) BOOST_NOEXCEPT +{ + return static_cast< const threadsafe_queue_impl_generic* >(impl)->unsafe_empty(); +} + +BOOST_LOG_API void threadsafe_queue_impl::push(threadsafe_queue_impl* impl, node_base* p) { - void* p = alignment::aligned_alloc(BOOST_LOG_CPU_CACHE_LINE_SIZE, size); - if (BOOST_UNLIKELY(!p)) - BOOST_THROW_EXCEPTION(std::bad_alloc()); - return p; + static_cast< threadsafe_queue_impl_generic* >(impl)->push(p); } -BOOST_LOG_API void threadsafe_queue_impl::operator delete (void* p, std::size_t) +BOOST_LOG_API bool threadsafe_queue_impl::try_pop(threadsafe_queue_impl* impl, node_base*& node_to_free, node_base*& node_with_value) { - alignment::aligned_free(p); + return static_cast< threadsafe_queue_impl_generic* >(impl)->try_pop(node_to_free, node_with_value); } } // namespace aux diff --git a/src/timer.cpp b/src/timer.cpp index f9b4cdfdb5..928900ccd2 100644 --- a/src/timer.cpp +++ b/src/timer.cpp @@ -22,8 +22,7 @@ #include #include #if !defined(BOOST_LOG_NO_THREADS) -#include -#include +#include #endif #include #include @@ -41,7 +40,7 @@ class BOOST_SYMBOL_VISIBLE timer::impl : private: #if !defined(BOOST_LOG_NO_THREADS) //! Synchronization mutex type - typedef boost::mutex mutex_type; + typedef std::mutex mutex_type; //! Synchronization mutex mutex_type m_Mutex; #endif @@ -70,7 +69,7 @@ class BOOST_SYMBOL_VISIBLE timer::impl : { uint64_t duration; { - BOOST_LOG_EXPR_IF_MT(log::aux::exclusive_lock_guard< mutex_type > lock(m_Mutex);) + BOOST_LOG_EXPR_IF_MT(std::lock_guard< mutex_type > lock(m_Mutex);) LARGE_INTEGER li; QueryPerformanceCounter(&li); @@ -131,7 +130,7 @@ class BOOST_SYMBOL_VISIBLE timer::impl : */ impl() : m_BaseTimePoint(utc_time_traits::get_clock()) {} - attribute_value get_value() + attribute_value get_value() BOOST_OVERRIDE { return attribute_value(new attribute_value_impl< value_type >( utc_time_traits::get_clock() - m_BaseTimePoint)); diff --git a/src/timestamp.cpp b/src/timestamp.cpp index 05e5e50c73..3bdfac5bab 100644 --- a/src/timestamp.cpp +++ b/src/timestamp.cpp @@ -16,7 +16,7 @@ #include #include -#if defined(BOOST_WINDOWS) && !defined(__CYGWIN__) +#if defined(BOOST_WINDOWS) #include #include #include @@ -47,7 +47,7 @@ BOOST_LOG_OPEN_NAMESPACE namespace aux { -#if defined(BOOST_WINDOWS) && !defined(__CYGWIN__) +#if defined(BOOST_WINDOWS) #if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6 diff --git a/src/trivial.cpp b/src/trivial.cpp index b2fd280edf..1461ad71b3 100644 --- a/src/trivial.cpp +++ b/src/trivial.cpp @@ -43,7 +43,7 @@ BOOST_LOG_API logger::logger_type& logger::get() BOOST_LOG_ANONYMOUS_NAMESPACE { -const unsigned int names_count = 6; +BOOST_CONSTEXPR_OR_CONST unsigned int names_count = 6; template< typename CharT > struct severity_level_names @@ -64,43 +64,81 @@ const CharT severity_level_names< CharT >::names[names_count][8] = } // namespace -BOOST_LOG_API const char* to_string(severity_level lvl) +template< typename CharT > +BOOST_LOG_API const CharT* to_string(severity_level lvl) { - typedef severity_level_names< char > level_names; - if (static_cast< unsigned int >(lvl) < names_count) + typedef severity_level_names< CharT > level_names; + if (BOOST_LIKELY(static_cast< unsigned int >(lvl) < names_count)) return level_names::names[static_cast< unsigned int >(lvl)]; - else - return NULL; + return NULL; +} + +//! Parses enumeration value from string and returns \c true on success and \c false otherwise +template< typename CharT > +BOOST_LOG_API bool from_string(const CharT* str, std::size_t len, severity_level& lvl) +{ + typedef severity_level_names< CharT > level_names; + typedef std::char_traits< CharT > char_traits; + + if (len == 5u) + { + if (char_traits::compare(str, level_names::names[0], len) == 0) + lvl = static_cast< severity_level >(0); + else if (char_traits::compare(str, level_names::names[1], len) == 0) + lvl = static_cast< severity_level >(1); + else if (char_traits::compare(str, level_names::names[4], len) == 0) + lvl = static_cast< severity_level >(4); + else if (char_traits::compare(str, level_names::names[5], len) == 0) + lvl = static_cast< severity_level >(5); + else + goto no_match; + return true; + } + else if (len == 4u) + { + if (char_traits::compare(str, level_names::names[2], len) == 0) + lvl = static_cast< severity_level >(2); + else + goto no_match; + return true; + } + else if (len == 7u) + { + if (char_traits::compare(str, level_names::names[3], len) == 0) + lvl = static_cast< severity_level >(3); + else + goto no_match; + return true; + } + +no_match: + return false; } template< typename CharT, typename TraitsT > BOOST_LOG_API std::basic_istream< CharT, TraitsT >& operator>> ( std::basic_istream< CharT, TraitsT >& strm, severity_level& lvl) { - if (strm.good()) + if (BOOST_LIKELY(strm.good())) { - typedef severity_level_names< CharT > level_names; typedef std::basic_string< CharT, TraitsT > string_type; string_type str; strm >> str; - for (unsigned int i = 0; i < names_count; ++i) - { - if (str == level_names::names[i]) - { - lvl = static_cast< severity_level >(i); - return strm; - } - } - strm.setstate(std::ios_base::failbit); + if (BOOST_UNLIKELY(!boost::log::trivial::from_string(str.data(), str.size(), lvl))) + strm.setstate(std::ios_base::failbit); } return strm; } +template BOOST_LOG_API const char* to_string< char >(severity_level lvl); +template BOOST_LOG_API bool from_string< char >(const char* begin, std::size_t len, severity_level& lvl); template BOOST_LOG_API std::basic_istream< char, std::char_traits< char > >& operator>> < char, std::char_traits< char > > ( std::basic_istream< char, std::char_traits< char > >& strm, severity_level& lvl); #ifdef BOOST_LOG_USE_WCHAR_T +template BOOST_LOG_API const wchar_t* to_string< wchar_t >(severity_level lvl); +template BOOST_LOG_API bool from_string< wchar_t >(const wchar_t* begin, std::size_t len, severity_level& lvl); template BOOST_LOG_API std::basic_istream< wchar_t, std::char_traits< wchar_t > >& operator>> < wchar_t, std::char_traits< wchar_t > > ( std::basic_istream< wchar_t, std::char_traits< wchar_t > >& strm, severity_level& lvl); diff --git a/src/unique_ptr.hpp b/src/unique_ptr.hpp deleted file mode 100644 index 091bb9cf8d..0000000000 --- a/src/unique_ptr.hpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright Andrey Semashev 2007 - 2015. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ -/*! - * \file unique_ptr.hpp - * \author Andrey Semashev - * \date 18.07.2015 - * - * \brief This header is the Boost.Log library implementation, see the library documentation - * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. - */ - -#ifndef BOOST_LOG_UNIQUE_PTR_HPP_INCLUDED_ -#define BOOST_LOG_UNIQUE_PTR_HPP_INCLUDED_ - -#include - -#if !defined(BOOST_NO_CXX11_SMART_PTR) - -#include - -namespace boost { - -BOOST_LOG_OPEN_NAMESPACE - -namespace aux { - -using std::unique_ptr; - -} // namespace aux - -BOOST_LOG_CLOSE_NAMESPACE // namespace log - -} // namespace boost - -#else // !defined(BOOST_NO_CXX11_SMART_PTR) - -#include - -namespace boost { - -BOOST_LOG_OPEN_NAMESPACE - -namespace aux { - -using boost::movelib::unique_ptr; - -} // namespace aux - -BOOST_LOG_CLOSE_NAMESPACE // namespace log - -} // namespace boost - -#endif // !defined(BOOST_NO_CXX11_SMART_PTR) - -#endif // BOOST_LOG_UNIQUE_PTR_HPP_INCLUDED_ diff --git a/src/windows/debug_output_backend.cpp b/src/windows/debug_output_backend.cpp index 105dd68f4c..2e59da1c39 100644 --- a/src/windows/debug_output_backend.cpp +++ b/src/windows/debug_output_backend.cpp @@ -12,9 +12,10 @@ * \brief A logging sink backend that uses debugger output */ +#include + #ifndef BOOST_LOG_WITHOUT_DEBUG_OUTPUT -#include #include #include #include diff --git a/src/windows/event_log_backend.cpp b/src/windows/event_log_backend.cpp index a3382c8c51..6f9d81c911 100644 --- a/src/windows/event_log_backend.cpp +++ b/src/windows/event_log_backend.cpp @@ -13,9 +13,11 @@ * for signalling application events. */ +#include + #ifndef BOOST_LOG_WITHOUT_EVENT_LOG -#include +#include #include #include #include @@ -32,7 +34,6 @@ #include #include #include -#include "unique_ptr.hpp" #include "windows/event_log_registry.hpp" #include "windows/simple_event_log.h" #include @@ -261,7 +262,7 @@ BOOST_LOG_API void basic_simple_event_log_backend< CharT >::construct( aux::init_event_log_registry(log_name, source_name, reg_mode == event_log::forced, reg_params); } - log::aux::unique_ptr< implementation > p(new implementation()); + std::unique_ptr< implementation > p(new implementation()); const char_type* target_unc = NULL; if (!target.empty()) @@ -517,7 +518,7 @@ BOOST_LOG_API void basic_event_log_backend< CharT >::construct( aux::init_event_log_registry(log_name, source_name, reg_mode == event_log::forced, reg_params); } - log::aux::unique_ptr< implementation > p(new implementation()); + std::unique_ptr< implementation > p(new implementation()); const char_type* target_unc = NULL; if (!target.empty()) diff --git a/src/windows/ipc_reliable_message_queue.cpp b/src/windows/ipc_reliable_message_queue.cpp index d99d091d1f..ed90feb15a 100644 --- a/src/windows/ipc_reliable_message_queue.cpp +++ b/src/windows/ipc_reliable_message_queue.cpp @@ -26,14 +26,14 @@ #include #include #include -#include #include -#include +#include +#include #include +#include #include #include #include -#include #include #include #include @@ -46,11 +46,6 @@ #include #include -#if BOOST_ATOMIC_INT32_LOCK_FREE != 2 -// 32-bit atomic ops are required to be able to place atomic in the process-shared memory -#error Boost.Log: Native 32-bit atomic operations are required but not supported by Boost.Atomic on the target platform -#endif - //! A suffix used in names of interprocess objects created by the queue. //! Used as a protection against clashing with user-supplied names of interprocess queues and also to resolve conflicts between queues of different types. #define BOOST_LOG_IPC_NAMES_AUX_SUFFIX L".3010b9950926463398eee00b35b44651" @@ -100,7 +95,7 @@ struct reliable_message_queue::implementation //! Padding to protect against alignment changes in Boost.Atomic. Don't use BOOST_ALIGNMENT to ensure portability. unsigned char m_padding[BOOST_LOG_CPU_CACHE_LINE_SIZE - sizeof(uint32_t)]; //! A flag indicating that the queue is constructed (i.e. the queue is constructed when the value is not 0). - boost::atomic< uint32_t > m_initialized; + boost::ipc_atomic< uint32_t > m_initialized; //! Number of allocation blocks in the queue. const uint32_t m_capacity; //! Size of an allocation block, in bytes. @@ -125,7 +120,7 @@ struct reliable_message_queue::implementation m_get_pos(0u) { // Must be initialized last. m_initialized is zero-initialized initially. - m_initialized.fetch_add(1u, boost::memory_order_release); + m_initialized.opaque_add(1u, boost::memory_order_release); } //! Returns the header structure ABI tag @@ -216,6 +211,7 @@ struct reliable_message_queue::implementation m_block_size_log2(0u), m_name(name) { + BOOST_ASSERT(block_size >= block_header::get_header_overhead()); const std::wstring wname = boost::log::aux::utf8_to_utf16(name.c_str()); const std::size_t shmem_size = estimate_region_size(capacity, block_size); m_shared_memory.create(wname.c_str(), shmem_size, perms); @@ -239,6 +235,7 @@ struct reliable_message_queue::implementation m_block_size_log2(0u), m_name(name) { + BOOST_ASSERT(block_size >= block_header::get_header_overhead()); const std::wstring wname = boost::log::aux::utf8_to_utf16(name.c_str()); const std::size_t shmem_size = estimate_region_size(capacity, block_size); const bool created = m_shared_memory.create_or_open(wname.c_str(), shmem_size, perms); @@ -475,7 +472,7 @@ struct reliable_message_queue::implementation { for (unsigned int j = 0; j < spins; ++j) { - boost::log::aux::pause(); + boost::atomics::thread_pause(); } } else @@ -563,6 +560,7 @@ struct reliable_message_queue::implementation const uint32_t capacity = hdr->m_capacity; const size_type block_size = hdr->m_block_size; uint32_t pos = hdr->m_put_pos; + BOOST_ASSERT(pos < capacity); block_header* block = hdr->get_block(pos); block->m_size = message_size; @@ -597,6 +595,7 @@ struct reliable_message_queue::implementation const uint32_t capacity = hdr->m_capacity; const size_type block_size = hdr->m_block_size; uint32_t pos = hdr->m_get_pos; + BOOST_ASSERT(pos < capacity); block_header* block = hdr->get_block(pos); size_type message_size = block->m_size; diff --git a/src/windows/ipc_sync_wrappers.cpp b/src/windows/ipc_sync_wrappers.cpp index bd8bfb215c..6848809235 100644 --- a/src/windows/ipc_sync_wrappers.cpp +++ b/src/windows/ipc_sync_wrappers.cpp @@ -26,15 +26,15 @@ #include // for error codes #include #include +#include #include #include #include #include #include #include +#include #include -#include -#include "unique_ptr.hpp" #include "windows/ipc_sync_wrappers.hpp" #include @@ -217,8 +217,18 @@ bool interprocess_semaphore::is_semaphore_zero_count_nt_query_semaphore(boost::w if (BOOST_UNLIKELY(err != 0u)) { char buf[sizeof(unsigned int) * 2u + 4u]; - boost::log::aux::snprintf(buf, sizeof(buf), "0x%08x", static_cast< unsigned int >(err)); - BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, std::string("Failed to test an interprocess semaphore object for zero count, NT status: ") + buf, (ERROR_INVALID_HANDLE)); + int res = boost::core::snprintf(buf, sizeof(buf), "0x%08x", static_cast< unsigned int >(err)); + if (BOOST_UNLIKELY(res < 0)) + { + buf[0] = '?'; + buf[1] = '\0'; + res = 1; + } + else if (BOOST_UNLIKELY(static_cast< unsigned int >(res) >= sizeof(buf))) + { + res = sizeof(buf) - 1u; + } + BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, std::string("Failed to test an interprocess semaphore object for zero count, NT status: ").append(buf, res), (ERROR_INVALID_HANDLE)); } return info.current_count == 0u; @@ -332,7 +342,7 @@ inline void interprocess_mutex::clear_waiting_and_try_lock(uint32_t& old_state) { new_state = ((old_state & lock_flag_value) ? old_state : ((old_state - 1u) | lock_flag_value)) & ~event_set_flag_value; } - while (!m_shared_state->m_lock_state.compare_exchange_strong(old_state, new_state, boost::memory_order_acq_rel, boost::memory_order_relaxed)); + while (!m_shared_state->m_lock_state.compare_exchange_weak(old_state, new_state, boost::memory_order_acq_rel, boost::memory_order_relaxed)); } @@ -403,7 +413,7 @@ interprocess_condition_variable::semaphore_info* interprocess_condition_variable { // We need to open the semaphore. It is possible that the semaphore does not exist because all processes that had it opened terminated. // Because of this we also attempt to create it. - boost::log::aux::unique_ptr< semaphore_info > p(new semaphore_info(id)); + std::unique_ptr< semaphore_info > p(new semaphore_info(id)); generate_semaphore_name(id); p->m_semaphore.create_or_open(m_semaphore_name.c_str(), m_perms); diff --git a/src/windows/ipc_sync_wrappers.hpp b/src/windows/ipc_sync_wrappers.hpp index 7d1272aa1f..8ecd9635cf 100644 --- a/src/windows/ipc_sync_wrappers.hpp +++ b/src/windows/ipc_sync_wrappers.hpp @@ -52,114 +52,6 @@ namespace ipc { namespace aux { -// TODO: Port to Boost.Atomic when it supports extended atomic ops -#if defined(BOOST_MSVC) && (_MSC_VER >= 1400) && !defined(UNDER_CE) - -#if _MSC_VER == 1400 -extern "C" unsigned char _interlockedbittestandset(long *a, long b); -extern "C" unsigned char _interlockedbittestandreset(long *a, long b); -#else -extern "C" unsigned char _interlockedbittestandset(volatile long *a, long b); -extern "C" unsigned char _interlockedbittestandreset(volatile long *a, long b); -#endif - -#pragma intrinsic(_interlockedbittestandset) -#pragma intrinsic(_interlockedbittestandreset) - -BOOST_FORCEINLINE bool bit_test_and_set(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT -{ - return _interlockedbittestandset(reinterpret_cast< long* >(&x.storage()), static_cast< long >(bit)) != 0; -} - -BOOST_FORCEINLINE bool bit_test_and_reset(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT -{ - return _interlockedbittestandreset(reinterpret_cast< long* >(&x.storage()), static_cast< long >(bit)) != 0; -} - -#elif (defined(BOOST_MSVC) || defined(BOOST_INTEL_WIN)) && defined(_M_IX86) - -BOOST_FORCEINLINE bool bit_test_and_set(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT -{ - boost::atomic< uint32_t >::storage_type* p = &x.storage(); - bool ret; - __asm - { - mov eax, bit - mov edx, p - lock bts [edx], eax - setc ret - }; - return ret; -} - -BOOST_FORCEINLINE bool bit_test_and_reset(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT -{ - boost::atomic< uint32_t >::storage_type* p = &x.storage(); - bool ret; - __asm - { - mov eax, bit - mov edx, p - lock btr [edx], eax - setc ret - }; - return ret; -} - -#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) - -#if !defined(__CUDACC__) -#define BOOST_LOG_DETAIL_ASM_CLOBBER_CC_COMMA "cc", -#else -#define BOOST_LOG_DETAIL_ASM_CLOBBER_CC_COMMA -#endif - -BOOST_FORCEINLINE bool bit_test_and_set(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT -{ - bool res; - __asm__ __volatile__ - ( - "lock; bts %[bit_number], %[storage]\n\t" - "setc %[result]\n\t" - : [storage] "+m" (x.storage()), [result] "=q" (res) - : [bit_number] "Kq" (bit) - : BOOST_LOG_DETAIL_ASM_CLOBBER_CC_COMMA "memory" - ); - return res; -} - -BOOST_FORCEINLINE bool bit_test_and_reset(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT -{ - bool res; - __asm__ __volatile__ - ( - "lock; btr %[bit_number], %[storage]\n\t" - "setc %[result]\n\t" - : [storage] "+m" (x.storage()), [result] "=q" (res) - : [bit_number] "Kq" (bit) - : BOOST_LOG_DETAIL_ASM_CLOBBER_CC_COMMA "memory" - ); - return res; -} - -#else - -BOOST_FORCEINLINE bool bit_test_and_set(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT -{ - const uint32_t mask = uint32_t(1u) << bit; - uint32_t old_val = x.fetch_or(mask, boost::memory_order_acq_rel); - return (old_val & mask) != 0u; -} - -BOOST_FORCEINLINE bool bit_test_and_reset(boost::atomic< uint32_t >& x, uint32_t bit) BOOST_NOEXCEPT -{ - const uint32_t mask = uint32_t(1u) << bit; - uint32_t old_val = x.fetch_and(~mask, boost::memory_order_acq_rel); - return (old_val & mask) != 0u; -} - -#endif - //! Interprocess event object class interprocess_event { @@ -403,7 +295,7 @@ class interprocess_mutex bool try_lock() { - return !bit_test_and_set(m_shared_state->m_lock_state, lock_flag_bit); + return !m_shared_state->m_lock_state.bit_test_and_set(lock_flag_bit, boost::memory_order_acquire); } void lock() @@ -424,7 +316,7 @@ class interprocess_mutex const uint32_t old_count = m_shared_state->m_lock_state.fetch_add(lock_flag_value, boost::memory_order_release); if ((old_count & event_set_flag_value) == 0u && (old_count > lock_flag_value)) { - if (!bit_test_and_set(m_shared_state->m_lock_state, event_set_flag_bit)) + if (!m_shared_state->m_lock_state.bit_test_and_set(event_set_flag_bit, boost::memory_order_relaxed)) { m_event.set_noexcept(); } diff --git a/src/windows/is_debugger_present.cpp b/src/windows/is_debugger_present.cpp new file mode 100644 index 0000000000..cf2b84e0c6 --- /dev/null +++ b/src/windows/is_debugger_present.cpp @@ -0,0 +1,42 @@ +/* + * Copyright Andrey Semashev 2021. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +/*! + * \file is_debugger_present.cpp + * \author Andrey Semashev + * \date 18.09.2021 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + */ + +#include +#include +#include +#include + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace expressions { + +namespace aux { + +BOOST_LOG_API is_debugger_present::result_type is_debugger_present::operator() () const +{ + return IsDebuggerPresent() != 0; +} + +} // namespace aux + +} // namespace expressions + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include diff --git a/src/xorshift.hpp b/src/xorshift.hpp new file mode 100644 index 0000000000..851d4dc52f --- /dev/null +++ b/src/xorshift.hpp @@ -0,0 +1,65 @@ +/* + * Copyright Andrey Semashev 2025. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +/*! + * \file xorshift.hpp + * \author Andrey Semashev + * \date 14.06.2025 + * + * \brief This header is the Boost.Log library implementation, see the library documentation + * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. + * + * This file implements xorshift64 random number generator. See https://en.wikipedia.org/wiki/Xorshift. + */ + +#ifndef BOOST_LOG_XORSHIFT_HPP_INCLUDED_ +#define BOOST_LOG_XORSHIFT_HPP_INCLUDED_ + +#include +#include +#include + +namespace boost { + +BOOST_LOG_OPEN_NAMESPACE + +namespace aux { + +//! Xorshift64 random number generator (https://en.wikipedia.org/wiki/Xorshift) +class xorshift64 +{ +public: + using result_type = uint64_t; + +private: + uint64_t m_state; + +public: + explicit constexpr xorshift64(uint64_t seed) noexcept : + m_state(seed) + { + } + + result_type operator()() noexcept + { + uint64_t state = m_state; + state ^= state << 13u; + state ^= state >> 7u; + state ^= state << 17u; + m_state = state; + return state; + } +}; + +} // namespace aux + +BOOST_LOG_CLOSE_NAMESPACE // namespace log + +} // namespace boost + +#include + +#endif // BOOST_LOG_XORSHIFT_HPP_INCLUDED_ diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index ccda7a1bf0..f71f3af3a2 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -9,20 +9,19 @@ import testing ; import path ; import regex ; +import os ; +import ../build/log-platform-config ; +import ../build/log-build-config ; project : requirements + @log-platform-config.set-platform-defines + @log-build-config.check-atomic-int32 # sets BOOST_LOG_WITHOUT_IPC if native atomic int32 is not supported + @log-build-config.check-regex-header-only + common - # Disable warnings about using 'insecure' standard C functions - msvc:_SCL_SECURE_NO_WARNINGS - msvc:_SCL_SECURE_NO_DEPRECATE - msvc:_CRT_SECURE_NO_WARNINGS - msvc:_CRT_SECURE_NO_DEPRECATE - intel-win:_SCL_SECURE_NO_WARNINGS - intel-win:_SCL_SECURE_NO_DEPRECATE - intel-win:_CRT_SECURE_NO_WARNINGS - intel-win:_CRT_SECURE_NO_DEPRECATE + 1024 msvc:/bigobj msvc:/wd4503 # decorated name length exceeded, name was truncated @@ -47,20 +46,13 @@ project intel-linux:"-wd177,780,2196,1782,193,304,981,1418,411,734,279" intel-darwin:"-wd177,780,2196,1782,193,304,981,1418,411,734,279" - darwin:-ftemplate-depth-1024 - gcc:-ftemplate-depth-1024 - gcc:-fno-strict-aliasing # avoids strict aliasing violations in other Boost components /boost/log//boost_log /boost/log//boost_log_setup - /boost/date_time//boost_date_time - /boost/regex//boost_regex /boost/filesystem//boost_filesystem - /boost/system//boost_system /boost/test//boost_unit_test_framework single:BOOST_LOG_NO_THREADS - multi:/boost/thread//boost_thread : default-build # Testers typically don't specify threading environment and the library can be built and tested for single and multi. I'm more interested in multi though. multi @@ -74,16 +66,21 @@ rule test_all { local all_rules ; local file ; - local headers_path = [ path.make $(BOOST_ROOT)/libs/log/include/boost/log ] ; - for file in [ path.glob-tree $(headers_path) : *.hpp : detail ] + + if ! [ os.environ BOOST_LOG_TEST_WITHOUT_SELF_CONTAINED_HEADER_TESTS ] { - local rel_file = [ path.relative-to $(headers_path) $(file) ] ; - # Note: The test name starts with '~' in order to group these tests in the test report table, preferably at the end. - # All '/' are replaced with '-' because apparently test scripts have a problem with test names containing slashes. - local test_name = [ regex.replace ~hdr/$(rel_file) "/" "-" ] ; - #ECHO $(rel_file) ; - all_rules += [ compile compile/self_contained_header.cpp : "BOOST_LOG_TEST_HEADER=$(rel_file)" $(file) : $(test_name) ] ; + local headers_path = ../include/boost/log ; + for file in [ glob-tree-ex $(headers_path) : *.hpp : detail ] + { + local rel_file = [ path.relative-to $(headers_path) $(file) ] ; + # Note: The test name starts with '~' in order to group these tests in the test report table, preferably at the end. + # All '/' are replaced with '-' because apparently test scripts have a problem with test names containing slashes. + local test_name = [ regex.replace ~hdr/$(rel_file) "/" "-" ] ; + #ECHO $(rel_file) ; + all_rules += [ compile compile/self_contained_header.cpp : "BOOST_LOG_TEST_HEADER=$(rel_file)" $(file) : $(test_name) ] ; + } } + for file in [ glob compile/*.cpp ] { if [ path.basename $(file) ] != "self_contained_header.cpp" @@ -100,8 +97,13 @@ rule test_all all_rules += [ run $(file) ] ; } + if ! [ os.environ BOOST_LOG_TEST_WITHOUT_EXAMPLES ] + { + all_rules += [ build-project ../example ] ; + } + #ECHO All rules: $(all_rules) ; return $(all_rules) ; } -test-suite log : [ test_all ] [ build-project ../example ] ; +test-suite log : [ test_all ] ; diff --git a/test/common/test_barrier.hpp b/test/common/test_barrier.hpp new file mode 100644 index 0000000000..72b2a26f11 --- /dev/null +++ b/test/common/test_barrier.hpp @@ -0,0 +1,62 @@ +// Copyright (c) 2024 Andrey Semashev +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_LOG_TEST_BARRIER_HPP_INCLUDED_ +#define BOOST_LOG_TEST_BARRIER_HPP_INCLUDED_ + +#include +#include + +//! A simplified version of thread barrier from Boost.Thread and C++20 std::barrier +class test_barrier +{ +private: + std::mutex m_mutex; + std::condition_variable m_cond; + unsigned int m_generation; + unsigned int m_count; + const unsigned int m_initial_count; + +public: + explicit test_barrier(unsigned int initial_count) : + m_generation(0u), m_count(initial_count), m_initial_count(initial_count) + { + } + + test_barrier(test_barrier const&) = delete; + test_barrier& operator= (test_barrier const&) = delete; + + void arrive_and_wait() + { + std::unique_lock< std::mutex > lock(m_mutex); + + --m_count; + if (m_count == 0u) + { + ++m_generation; + m_count = m_initial_count; + m_cond.notify_all(); + return; + } + + const unsigned int generation = m_generation; + do + { + m_cond.wait(lock); + } + while (m_generation == generation); + } + + void wake_all() + { + std::lock_guard< std::mutex > lock(m_mutex); + ++m_generation; + m_count = m_initial_count; + m_cond.notify_all(); + } +}; + +#endif // BOOST_LOG_TEST_BARRIER_HPP_INCLUDED_ diff --git a/test/compile/current_function_support.cpp b/test/compile/current_function_support.cpp index cdcd1051ad..3367d4b0d2 100644 --- a/test/compile/current_function_support.cpp +++ b/test/compile/current_function_support.cpp @@ -20,13 +20,12 @@ #define BOOST_TEST_MODULE current_function_support #include -#include #include template< typename T > void check(T& param) { - BOOST_STATIC_ASSERT(boost::is_array< T >::value); + static_assert(boost::is_array< T >::value, "T must be an array"); } int main(int, char*[]) diff --git a/test/performance/record_emission.cpp b/test/performance/record_emission.cpp index 8657091c7b..92446cace8 100644 --- a/test/performance/record_emission.cpp +++ b/test/performance/record_emission.cpp @@ -17,16 +17,13 @@ // #define BOOST_LOG_DYN_LINK 1 #define BOOST_NO_DYN_LINK 1 +#include +#include +#include #include #include -#include -#include #include #include -#include -#include -#include -#include #include #include @@ -39,6 +36,8 @@ #include +#include "test_barrier.hpp" + enum config { RECORD_COUNT = 20000000, @@ -76,12 +75,12 @@ namespace { } // namespace -void test(unsigned int record_count, boost::barrier& bar) +void test(unsigned int record_count, test_barrier& bar) { BOOST_LOG_SCOPED_THREAD_TAG("ThreadID", boost::this_thread::get_id()); src::severity_logger< severity_level > slg; // src::logger lg; - bar.wait(); + bar.arrive_and_wait(); for (unsigned int i = 0; i < record_count; ++i) { @@ -93,7 +92,6 @@ void test(unsigned int record_count, boost::barrier& bar) int main(int argc, char* argv[]) { std::cout << "Test config: " << THREAD_COUNT << " threads, " << SINK_COUNT << " sinks, " << RECORD_COUNT << " records" << std::endl; -//__debugbreak(); // typedef sinks::unlocked_sink< fake_backend > fake_sink; // typedef sinks::synchronous_sink< fake_backend > fake_sink; typedef sinks::asynchronous_sink< fake_backend > fake_sink; @@ -110,19 +108,19 @@ int main(int argc, char* argv[]) // logging::core::get()->set_filter(severity > error); // all records don't pass the filter const unsigned int record_count = RECORD_COUNT / THREAD_COUNT; - boost::barrier bar(THREAD_COUNT); - boost::thread_group threads; + test_barrier bar(THREAD_COUNT); + std::vector< std::thread > threads(THREAD_COUNT - 1); - for (unsigned int i = 1; i < THREAD_COUNT; ++i) - threads.create_thread(boost::bind(&test, record_count, boost::ref(bar))); + for (unsigned int i = 0; i < THREAD_COUNT - 1; ++i) + threads[i] = std::thread([&bar, record_count]() { test(record_count, bar); }); - boost::posix_time::ptime start = boost::date_time::microsec_clock< boost::posix_time::ptime >::universal_time(), end; + const auto start = std::chrono::steady_clock::now(); test(record_count, bar); - if (THREAD_COUNT > 1) - threads.join_all(); - end = boost::date_time::microsec_clock< boost::posix_time::ptime >::universal_time(); + for (unsigned int i = 0; i < THREAD_COUNT - 1; ++i) + threads[i].join(); + const auto finish = std::chrono::steady_clock::now(); - unsigned long long duration = (end - start).total_microseconds(); + unsigned long long duration_us = std::chrono::duration_cast< std::chrono::microseconds >(finish - start).count(); std::cout << "Test duration: " << duration << " us (" << std::fixed << std::setprecision(3) << static_cast< double >(RECORD_COUNT) / (static_cast< double >(duration) / 1000000.0) diff --git a/test/run/attr_attribute_value_impl.cpp b/test/run/attr_attribute_value_impl.cpp index 156277a100..682b2e486e 100644 --- a/test/run/attr_attribute_value_impl.cpp +++ b/test/run/attr_attribute_value_impl.cpp @@ -15,10 +15,10 @@ #define BOOST_TEST_MODULE attr_attribute_value_impl #include -#include +#include #include -#include #include +#include #include #include #include diff --git a/test/run/attr_attribute_value_set.cpp b/test/run/attr_attribute_value_set.cpp index 15fdeece9b..1b80673872 100644 --- a/test/run/attr_attribute_value_set.cpp +++ b/test/run/attr_attribute_value_set.cpp @@ -20,8 +20,8 @@ #include #include #include -#include #include +#include #include #include #include diff --git a/test/run/attr_value_visitation.cpp b/test/run/attr_value_visitation.cpp index 11730d8cc7..f057fb17b6 100644 --- a/test/run/attr_value_visitation.cpp +++ b/test/run/attr_value_visitation.cpp @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/test/run/core.cpp b/test/run/core.cpp index 69b42647a2..99ff64e743 100644 --- a/test/run/core.cpp +++ b/test/run/core.cpp @@ -28,7 +28,7 @@ #include #include #ifndef BOOST_LOG_NO_THREADS -#include +#include #endif // BOOST_LOG_NO_THREADS #include "char_definitions.hpp" #include "test_sink.hpp" @@ -221,7 +221,7 @@ BOOST_AUTO_TEST_CASE(attributes) } #ifndef BOOST_LOG_NO_THREADS { - boost::thread th(&thread_attributes_test); + std::thread th(&thread_attributes_test); th.join(); BOOST_CHECK_EQUAL(pSink->m_RecordCounter, 1UL); BOOST_CHECK_EQUAL(pSink->m_Consumed[data::attr1()], 0UL); diff --git a/test/run/filt_attr.cpp b/test/run/filt_attr.cpp index 237908129f..e1858d496e 100644 --- a/test/run/filt_attr.cpp +++ b/test/run/filt_attr.cpp @@ -14,6 +14,15 @@ #define BOOST_TEST_MODULE filt_attr +#include + +// Try including WinAPI config as soon as possible so that any other headers don't include Windows SDK headers. +// This is important to select the target Windows version, as windows.h will be included by other Boost libraries, e.g. Regex. +#if defined(BOOST_OS_WINDOWS_AVAILABLE) +#define BOOST_USE_WINDOWS_H +#include +#endif + #include #include #include diff --git a/test/run/filt_matches_boost_regex.cpp b/test/run/filt_matches_boost_regex.cpp index c34fc731ec..7b3e4fddfd 100644 --- a/test/run/filt_matches_boost_regex.cpp +++ b/test/run/filt_matches_boost_regex.cpp @@ -14,6 +14,15 @@ #define BOOST_TEST_MODULE filt_matches_boost_regex +#include + +// Try including WinAPI config as soon as possible so that any other headers don't include Windows SDK headers. +// This is important to select the target Windows version, as windows.h will be included by other Boost libraries, e.g. Regex. +#if defined(BOOST_OS_WINDOWS_AVAILABLE) +#define BOOST_USE_WINDOWS_H +#include +#endif + #include #include #include diff --git a/test/run/filt_matches_xpressive.cpp b/test/run/filt_matches_xpressive.cpp index 44ee0a618c..aafcabf21c 100644 --- a/test/run/filt_matches_xpressive.cpp +++ b/test/run/filt_matches_xpressive.cpp @@ -12,6 +12,12 @@ * \brief This header contains tests for the \c matches filter with Boost.Xpressive backend. */ +#include + +// gcc 10 and 11 prior to 11.2 are known to ICE on Boost.Xpressive code in C++03 mode, see: +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102293 +#if !defined(BOOST_GCC) || defined(BOOST_GCC_CXX11) || BOOST_GCC < 100000 || BOOST_GCC >= 110200 + #define BOOST_TEST_MODULE filt_matches_xpressive #include @@ -105,3 +111,12 @@ BOOST_AUTO_TEST_CASE(composition_check) BOOST_CHECK(!f(values2)); BOOST_CHECK(f(values3)); } + +#else // !defined(BOOST_GCC) || defined(BOOST_GCC_CXX11) || BOOST_GCC < 100000 || BOOST_GCC >= 110200 + +int main() +{ + return 0; +} + +#endif // !defined(BOOST_GCC) || defined(BOOST_GCC_CXX11) || BOOST_GCC < 100000 || BOOST_GCC >= 110200 diff --git a/test/run/filt_wrap_filter.cpp b/test/run/filt_wrap_filter.cpp new file mode 100644 index 0000000000..bd5d5cbd9f --- /dev/null +++ b/test/run/filt_wrap_filter.cpp @@ -0,0 +1,91 @@ +/* + * Copyright Andrey Semashev 2025. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +/*! + * \file filt_wrap_filter.cpp + * \author Andrey Semashev + * \date 09.07.2025 + * + * \brief This header contains tests for the \c wrap_filter adapter. + */ + +#define BOOST_TEST_MODULE filt_wrap_filter + +#include +#include +#include +#include +#include +#include +#include "char_definitions.hpp" + +namespace logging = boost::log; +namespace attrs = logging::attributes; +namespace expr = logging::expressions; + +// The test checks that the wrap_filter adapter works +BOOST_AUTO_TEST_CASE(wrap_filter_check) +{ + typedef logging::attribute_set attr_set; + typedef logging::attribute_value_set attr_values; + typedef logging::filter filter; + typedef test_data< char > data; + + attrs::constant< int > attr1(10); + attrs::constant< double > attr2(5.5); + attrs::constant< std::string > attr3("Hello, world!"); + + attr_set set1, set2, set3; + attr_values values1(set1, set2, set3); + values1.freeze(); + set1[data::attr2()] = attr2; + attr_values values2(set1, set2, set3); + values2.freeze(); + set1[data::attr3()] = attr3; + set1[data::attr1()] = attr1; + attr_values values3(set1, set2, set3); + values3.freeze(); + + int call_count1 = 0, call_count2 = 0; + filter f = expr::wrap_filter([&](attr_values const& values) { ++call_count1; return values.find(data::attr1()) != values.end(); }) || + expr::wrap_filter([&](attr_values const& values) { ++call_count2; return values.find(data::attr2()) != values.end(); }); + BOOST_CHECK(!f(values1)); + BOOST_CHECK(call_count1 == 1); + BOOST_CHECK(call_count2 == 1); + + call_count1 = 0; + call_count2 = 0; + BOOST_CHECK(f(values2)); + BOOST_CHECK(call_count1 == 1); + BOOST_CHECK(call_count2 == 1); + + call_count1 = 0; + call_count2 = 0; + BOOST_CHECK(f(values3)); + BOOST_CHECK(call_count1 == 1); + BOOST_CHECK(call_count2 == 0); + + f = expr::wrap_filter([&](attr_values const& values) { ++call_count1; return values.find(data::attr1()) != values.end(); }) && + expr::wrap_filter([&](attr_values const& values) { ++call_count2; return values.find(data::attr2()) != values.end(); }); + + call_count1 = 0; + call_count2 = 0; + BOOST_CHECK(!f(values1)); + BOOST_CHECK(call_count1 == 1); + BOOST_CHECK(call_count2 == 0); + + call_count1 = 0; + call_count2 = 0; + BOOST_CHECK(!f(values2)); + BOOST_CHECK(call_count1 == 1); + BOOST_CHECK(call_count2 == 0); + + call_count1 = 0; + call_count2 = 0; + BOOST_CHECK(f(values3)); + BOOST_CHECK(call_count1 == 1); + BOOST_CHECK(call_count2 == 1); +} diff --git a/test/run/form_auto_newline.cpp b/test/run/form_auto_newline.cpp new file mode 100644 index 0000000000..0ce38e67a6 --- /dev/null +++ b/test/run/form_auto_newline.cpp @@ -0,0 +1,94 @@ +/* + * Copyright Andrey Semashev 2019. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * https://www.boost.org/LICENSE_1_0.txt) + */ +/*! + * \file form_auto_newline.cpp + * \author Andrey Semashev + * \date 23.06.2019 + * + * \brief This header contains tests for the auto_newline formatter. + */ + +#define BOOST_TEST_MODULE form_auto_newline + +#include +#include +#include +#include +#include "char_definitions.hpp" +#include "make_record.hpp" + +namespace logging = boost::log; +namespace expr = logging::expressions; + +// Test appending a newline to a non-empty string +BOOST_AUTO_TEST_CASE_TEMPLATE(append_to_non_empty_string, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + typedef logging::basic_formatting_ostream< char_type > formatting_ostream_type; + typedef typename formatting_ostream_type::string_type string_type; + typedef logging::record_view record_view; + typedef logging::basic_formatter< char_type > formatter; + + record_view rec = make_record_view(); + string_type str_fmt; + formatting_ostream_type strm_fmt(str_fmt); + + formatter f = expr::stream << "Hello" << expr::auto_newline; + f(rec, strm_fmt); + + ostream_type strm_correct; + strm_correct << "Hello\n"; + + BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str())); +} + +// Test appending a newline to an empty string +BOOST_AUTO_TEST_CASE_TEMPLATE(append_to_empty_string, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + typedef logging::basic_formatting_ostream< char_type > formatting_ostream_type; + typedef typename formatting_ostream_type::string_type string_type; + typedef logging::record_view record_view; + typedef logging::basic_formatter< char_type > formatter; + + record_view rec = make_record_view(); + string_type str_fmt; + formatting_ostream_type strm_fmt(str_fmt); + + formatter f = expr::stream << expr::auto_newline; + f(rec, strm_fmt); + + ostream_type strm_correct; + strm_correct << "\n"; + + BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str())); +} + +// Test not appending a newline to a non-empty string which already ends with a newline +BOOST_AUTO_TEST_CASE_TEMPLATE(not_append_if_ends_with_a_newline, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + typedef logging::basic_formatting_ostream< char_type > formatting_ostream_type; + typedef typename formatting_ostream_type::string_type string_type; + typedef logging::record_view record_view; + typedef logging::basic_formatter< char_type > formatter; + + record_view rec = make_record_view(); + string_type str_fmt; + formatting_ostream_type strm_fmt(str_fmt); + + formatter f = expr::stream << "Hello\n" << expr::auto_newline; + f(rec, strm_fmt); + + ostream_type strm_correct; + strm_correct << "Hello\n"; + + BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str())); +} diff --git a/test/run/form_wrap_formatter.cpp b/test/run/form_wrap_formatter.cpp new file mode 100644 index 0000000000..627d7f6d06 --- /dev/null +++ b/test/run/form_wrap_formatter.cpp @@ -0,0 +1,93 @@ +/* + * Copyright Andrey Semashev 2025. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +/*! + * \file form_wrap_formatter.cpp + * \author Andrey Semashev + * \date 11.07.2025 + * + * \brief This header contains tests for the \c wrap_formatter utility. + */ + +#define BOOST_TEST_MODULE form_wrap_formatter + +#include +#include +#include +#include +#include +#include +#include +#include +#include "char_definitions.hpp" +#include "make_record.hpp" + +namespace logging = boost::log; +namespace attrs = logging::attributes; +namespace expr = logging::expressions; + +namespace { + + class my_class + { + int m_Data; + + public: + explicit my_class(int data) : m_Data(data) {} + + int get_data() const { return m_Data; } + }; + + template< typename StreamT > + inline void format_my_class(StreamT& strm, my_class const& obj) + { + strm << "[data: " << obj.get_data() << "]"; + } + +} // namespace + +// The test checks that wrap_formatter works +BOOST_AUTO_TEST_CASE_TEMPLATE(wrap_formatter_check, CharT, char_types) +{ + typedef logging::record_view record_view; + typedef logging::attribute_set attr_set; + typedef std::basic_string< CharT > string; + typedef logging::basic_formatting_ostream< CharT > osstream; + typedef logging::basic_formatter< CharT > formatter; + typedef test_data< CharT > data; + + attrs::constant< int > attr1(10); + attrs::constant< double > attr2(5.5); + attrs::constant< my_class > attr3(my_class(77)); + + attr_set set1; + set1[data::attr1()] = attr1; + set1[data::attr2()] = attr2; + set1[data::attr3()] = attr3; + + record_view rec = make_record_view(set1); + + // Check for various modes of attribute value type specification + { + string str1, str2; + osstream strm1(str1), strm2(str2); + int call_count = 0; + formatter f = + expr::stream << expr::attr< int >(data::attr1()) + << expr::attr< double >(data::attr2()) + << expr::wrap_formatter< CharT >([&](record_view const& rec, osstream& strm) + { + ++call_count; + logging::value_ref< my_class > val = rec[data::attr3()].template extract< my_class >(); + if (val) + format_my_class(strm, val.get()); + }); + f(rec, strm1); + strm2 << 10 << 5.5; + format_my_class(strm2, my_class(77)); + BOOST_CHECK(equal_strings(strm1.str(), strm2.str())); + } +} diff --git a/test/run/sink_text_ipc_mq_backend.cpp b/test/run/sink_text_ipc_mq_backend.cpp index e94463e6e9..e26f53a88b 100644 --- a/test/run/sink_text_ipc_mq_backend.cpp +++ b/test/run/sink_text_ipc_mq_backend.cpp @@ -14,6 +14,8 @@ * \brief The test verifies that \c text_ipc_message_queue_backend works as expected. */ +#if !defined(BOOST_LOG_WITHOUT_IPC) + #define BOOST_TEST_MODULE sink_text_ipc_mq_backend #include @@ -54,3 +56,12 @@ BOOST_AUTO_TEST_CASE(text_ipc_message_queue_backend) BOOST_CHECK(queue.try_receive(msg)); BOOST_CHECK(equal_strings(msg, message)); } + +#else // !defined(BOOST_LOG_WITHOUT_IPC) + +int main() +{ + return 0; +} + +#endif // !defined(BOOST_LOG_WITHOUT_IPC) diff --git a/test/run/src_channel_logger_copy.cpp b/test/run/src_channel_logger_copy.cpp new file mode 100644 index 0000000000..690110a3de --- /dev/null +++ b/test/run/src_channel_logger_copy.cpp @@ -0,0 +1,46 @@ +/* + * Copyright Andrey Semashev 2021. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +/*! + * \file src_channel_logger_copy.cpp + * \author Andrey Semashev + * \date 10.02.2021 + * + * \brief This header contains tests for the channel logger copy constructor and assignment. + */ + +#define BOOST_TEST_MODULE src_channel_logger_copy + +#include +#include +#include + +namespace src = boost::log::sources; + +// Test that copy constructor decouples the channel attribute +BOOST_AUTO_TEST_CASE(copy_constructor) +{ + src::channel_logger< std::string > lg1("channel1"); + src::channel_logger< std::string > lg2 = lg1; + BOOST_CHECK_EQUAL(lg1.channel(), lg2.channel()); + + lg2.channel("channel2"); + BOOST_CHECK_NE(lg1.channel(), lg2.channel()); +} + +// Test that copy assignment decouples the channel attribute +BOOST_AUTO_TEST_CASE(copy_assignment) +{ + src::channel_logger< std::string > lg1("channel1"); + src::channel_logger< std::string > lg2("channel2"); + BOOST_CHECK_NE(lg1.channel(), lg2.channel()); + + lg2 = lg1; + BOOST_CHECK_EQUAL(lg1.channel(), lg2.channel()); + + lg2.channel("channel3"); + BOOST_CHECK_NE(lg1.channel(), lg2.channel()); +} diff --git a/test/run/src_record_ostream.cpp b/test/run/src_record_ostream.cpp index adba3408e1..dd321f922c 100644 --- a/test/run/src_record_ostream.cpp +++ b/test/run/src_record_ostream.cpp @@ -21,6 +21,9 @@ #include #include #include +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) +#include +#endif #include #include #include @@ -243,6 +246,9 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(width_formatting, CharT, char_types) test::BOOST_NESTED_TEMPLATE width_formatting< const CharT* >(); test::BOOST_NESTED_TEMPLATE width_formatting< typename test::string_type >(); test::BOOST_NESTED_TEMPLATE width_formatting< boost::basic_string_view< CharT > >(); +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) + test::BOOST_NESTED_TEMPLATE width_formatting< std::basic_string_view< CharT > >(); +#endif } // Test support for filler character setup @@ -252,6 +258,9 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(fill_formatting, CharT, char_types) test::BOOST_NESTED_TEMPLATE fill_formatting< const CharT* >(); test::BOOST_NESTED_TEMPLATE fill_formatting< typename test::string_type >(); test::BOOST_NESTED_TEMPLATE fill_formatting< boost::basic_string_view< CharT > >(); +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) + test::BOOST_NESTED_TEMPLATE fill_formatting< std::basic_string_view< CharT > >(); +#endif } // Test support for text alignment @@ -261,6 +270,9 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(alignment, CharT, char_types) test::BOOST_NESTED_TEMPLATE alignment< const CharT* >(); test::BOOST_NESTED_TEMPLATE alignment< typename test::string_type >(); test::BOOST_NESTED_TEMPLATE alignment< boost::basic_string_view< CharT > >(); +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) + test::BOOST_NESTED_TEMPLATE alignment< std::basic_string_view< CharT > >(); +#endif } #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) @@ -271,6 +283,9 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(rvalue_stream, CharT, char_types) test::BOOST_NESTED_TEMPLATE rvalue_stream< const CharT* >(); test::BOOST_NESTED_TEMPLATE rvalue_stream< typename test::string_type >(); test::BOOST_NESTED_TEMPLATE rvalue_stream< boost::basic_string_view< CharT > >(); +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) + test::BOOST_NESTED_TEMPLATE rvalue_stream< std::basic_string_view< CharT > >(); +#endif } #endif @@ -306,6 +321,20 @@ inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< Cha return strm; } +template< typename CharT, typename TraitsT > +inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, B*) +{ + strm << "B*"; + return strm; +} + +template< typename CharT, typename TraitsT > +inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, const B*) +{ + strm << "const B*"; + return strm; +} + class C {}; template< typename CharT, typename TraitsT > inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, C const&) @@ -340,11 +369,12 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(operator_forwarding, CharT, char_types) my_namespace::B b; // lvalue strm_fmt << a << b << my_namespace::C(); // rvalue strm_fmt << my_namespace::eee; + strm_fmt << &b << (my_namespace::B const*)&b; strm_fmt.flush(); string_type rec_message = logging::extract_or_throw< string_type >(expr::message.get_name(), rec); ostream_type strm_correct; - strm_correct << a << b << my_namespace::C() << my_namespace::eee; + strm_correct << a << b << my_namespace::C() << my_namespace::eee << &b << (my_namespace::B const*)&b; BOOST_CHECK(equal_strings(rec_message, strm_correct.str())); } diff --git a/test/run/util_dynamic_type_disp.cpp b/test/run/util_dynamic_type_disp.cpp index 485569d255..d1d6d9f49f 100644 --- a/test/run/util_dynamic_type_disp.cpp +++ b/test/run/util_dynamic_type_disp.cpp @@ -15,9 +15,9 @@ #define BOOST_TEST_MODULE util_dynamic_type_disp #include -#include -#include +#include #include +#include #include namespace logging = boost::log; @@ -111,8 +111,8 @@ BOOST_AUTO_TEST_CASE(type_dispatch) logging::dynamic_type_dispatcher disp; // Register type visitors - disp.register_type< int >(boost::bind(&my_visitor::on_int, &vis, _1)); - disp.register_type< double >(boost::bind(&my_visitor::on_double, &vis, _1)); + disp.register_type< int >(boost::bind(&my_visitor::on_int, &vis, boost::placeholders::_1)); + disp.register_type< double >(boost::bind(&my_visitor::on_double, &vis, boost::placeholders::_1)); BOOST_CHECK(disp.registered_types_count() == 2); @@ -121,7 +121,7 @@ BOOST_AUTO_TEST_CASE(type_dispatch) BOOST_CHECK(!val1.dispatch(disp)); // And now they are - disp.register_type< std::string >(boost::bind(&my_visitor::on_string, &vis, _1)); + disp.register_type< std::string >(boost::bind(&my_visitor::on_string, &vis, boost::placeholders::_1)); BOOST_CHECK(disp.registered_types_count() == 3); vis.set_expected(val1.m_Value); diff --git a/test/run/util_formatting_ostream.cpp b/test/run/util_formatting_ostream.cpp index d9e1ae4957..cd73bcd5f0 100644 --- a/test/run/util_formatting_ostream.cpp +++ b/test/run/util_formatting_ostream.cpp @@ -21,6 +21,9 @@ #include #include #include +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) +#include +#endif #include #include #include @@ -198,6 +201,9 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(width_formatting, CharT, char_types) test::BOOST_NESTED_TEMPLATE width_formatting< const CharT* >(); test::BOOST_NESTED_TEMPLATE width_formatting< typename test::string_type >(); test::BOOST_NESTED_TEMPLATE width_formatting< boost::basic_string_view< CharT > >(); +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) + test::BOOST_NESTED_TEMPLATE width_formatting< std::basic_string_view< CharT > >(); +#endif } // Test support for filler character setup @@ -207,6 +213,9 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(fill_formatting, CharT, char_types) test::BOOST_NESTED_TEMPLATE fill_formatting< const CharT* >(); test::BOOST_NESTED_TEMPLATE fill_formatting< typename test::string_type >(); test::BOOST_NESTED_TEMPLATE fill_formatting< boost::basic_string_view< CharT > >(); +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) + test::BOOST_NESTED_TEMPLATE fill_formatting< std::basic_string_view< CharT > >(); +#endif } // Test support for text alignment @@ -216,6 +225,9 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(alignment, CharT, char_types) test::BOOST_NESTED_TEMPLATE alignment< const CharT* >(); test::BOOST_NESTED_TEMPLATE alignment< typename test::string_type >(); test::BOOST_NESTED_TEMPLATE alignment< boost::basic_string_view< CharT > >(); +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) + test::BOOST_NESTED_TEMPLATE alignment< std::basic_string_view< CharT > >(); +#endif } #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) @@ -226,6 +238,9 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(rvalue_stream, CharT, char_types) test::BOOST_NESTED_TEMPLATE rvalue_stream< const CharT* >(); test::BOOST_NESTED_TEMPLATE rvalue_stream< typename test::string_type >(); test::BOOST_NESTED_TEMPLATE rvalue_stream< boost::basic_string_view< CharT > >(); +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) + test::BOOST_NESTED_TEMPLATE rvalue_stream< std::basic_string_view< CharT > >(); +#endif } #endif @@ -254,6 +269,20 @@ inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< Cha return strm; } +template< typename CharT, typename TraitsT > +inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, B*) +{ + strm << "B*"; + return strm; +} + +template< typename CharT, typename TraitsT > +inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, const B*) +{ + strm << "const B*"; + return strm; +} + class C {}; template< typename CharT, typename TraitsT > inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, C const&) @@ -287,10 +316,11 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(operator_forwarding, CharT, char_types) my_namespace::B b; // lvalue strm_fmt << a << b << my_namespace::C(); // rvalue strm_fmt << my_namespace::eee; + strm_fmt << &b << (my_namespace::B const*)&b; strm_fmt.flush(); ostream_type strm_correct; - strm_correct << a << b << my_namespace::C() << my_namespace::eee; + strm_correct << a << b << my_namespace::C() << my_namespace::eee << &b << (my_namespace::B const*)&b; BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str())); } @@ -464,6 +494,10 @@ BOOST_AUTO_TEST_CASE(character_code_conversion) test_widening_code_conversion< std::string >(); test_narrowing_code_conversion< boost::wstring_view >(); test_widening_code_conversion< boost::string_view >(); +#if !defined(BOOST_NO_CXX17_HDR_STRING_VIEW) + test_narrowing_code_conversion< std::wstring_view >(); + test_widening_code_conversion< std::string_view >(); +#endif } #endif diff --git a/test/run/util_ipc_object_name.cpp b/test/run/util_ipc_object_name.cpp index 42ad03c7d5..11e63e852a 100644 --- a/test/run/util_ipc_object_name.cpp +++ b/test/run/util_ipc_object_name.cpp @@ -12,6 +12,8 @@ * \brief The test verifies that \c ipc::object_name works. */ +#if !defined(BOOST_LOG_WITHOUT_IPC) + #define BOOST_TEST_MODULE util_ipc_object_name #include @@ -164,3 +166,12 @@ BOOST_AUTO_TEST_CASE(name_equivalence) BOOST_CHECK_NE(name1, name2); } } + +#else // !defined(BOOST_LOG_WITHOUT_IPC) + +int main() +{ + return 0; +} + +#endif // !defined(BOOST_LOG_WITHOUT_IPC) diff --git a/test/run/util_ipc_reliable_mq.cpp b/test/run/util_ipc_reliable_mq.cpp index d4b68ff865..62732590a3 100644 --- a/test/run/util_ipc_reliable_mq.cpp +++ b/test/run/util_ipc_reliable_mq.cpp @@ -14,6 +14,8 @@ * \brief The test verifies that \c ipc::reliable_message_queue works. */ +#if !defined(BOOST_LOG_WITHOUT_IPC) + #define BOOST_TEST_MODULE util_ipc_reliable_mq #include @@ -22,31 +24,71 @@ #include #include #include +#include +#if defined(BOOST_WINDOWS) +#include +#else +#include +#endif #include #include #include #include +#include #include #include #include #if !defined(BOOST_LOG_NO_THREADS) +#include +#include #include -#include #include -#include -#include #endif #include "char_definitions.hpp" typedef boost::log::ipc::reliable_message_queue queue_t; typedef queue_t::size_type size_type; -const boost::log::ipc::object_name ipc_queue_name(boost::log::ipc::object_name::session, "boost_log_test_ipc_reliable_mq"); +inline boost::log::ipc::object_name generate_ipc_queue_name() +{ + // Make sure IPC queue name is specific to the current process. This is useful when running + // multiple instances of the test concurrently (e.g. debug and release). + std::ostringstream strm; + strm << "boost_log_test_ipc_reliable_mq" +#if defined(BOOST_WINDOWS) + << +boost::winapi::GetCurrentProcessId(); +#else + << +getpid(); +#endif + return boost::log::ipc::object_name(boost::log::ipc::object_name::session, strm.str()); +} + +const boost::log::ipc::object_name ipc_queue_name = generate_ipc_queue_name(); const unsigned int capacity = 512; const size_type block_size = 1024; const char message1[] = "Hello, world!"; const char message2[] = "Hello, the brand new world!"; +struct queue_cleanup +{ + ~queue_cleanup() + { + try + { + queue_t::remove(ipc_queue_name); + } + catch (...) + { + } + } +}; +#if !defined(BOOST_MSVC) || BOOST_MSVC >= 1800 +const queue_cleanup queue_cleanup_guard = {}; +#else +// MSVC prior to 12.0 ICEs on the aggregate initialization of the constant +const queue_cleanup queue_cleanup_guard; +#endif + BOOST_AUTO_TEST_CASE(basic_functionality) { // Default constructor. @@ -55,7 +97,7 @@ BOOST_AUTO_TEST_CASE(basic_functionality) BOOST_CHECK(!queue.is_open()); } - // Do a remove in case if a previous test failed + // Do a remove in case if a previous test crashed queue_t::remove(ipc_queue_name); // Opening a non-existing queue @@ -308,9 +350,9 @@ BOOST_AUTO_TEST_CASE(multithreaded_message_passing) unsigned int failure_count1 = 0, failure_count2 = 0, failure_count3 = 0; boost::atomic_thread_fence(boost::memory_order_release); - boost::thread thread1(&multithreaded_message_passing_feeding_thread, "Thread 1", boost::ref(failure_count1)); - boost::thread thread2(&multithreaded_message_passing_feeding_thread, "Thread 2", boost::ref(failure_count2)); - boost::thread thread3(&multithreaded_message_passing_feeding_thread, "Thread 3", boost::ref(failure_count3)); + std::thread thread1([&failure_count1]() { multithreaded_message_passing_feeding_thread("Thread 1", failure_count1); }); + std::thread thread2([&failure_count2]() { multithreaded_message_passing_feeding_thread("Thread 2", failure_count2); }); + std::thread thread3([&failure_count3]() { multithreaded_message_passing_feeding_thread("Thread 3", failure_count3); }); BOOST_TEST_PASSPOINT(); @@ -404,14 +446,14 @@ BOOST_AUTO_TEST_CASE(stop_reset_local) BOOST_TEST_PASSPOINT(); // Case 1: Let the feeder block and then we unblock it with stop_local() - boost::thread feeder_thread(&stop_reset_feeding_thread, boost::ref(feeder_queue), feeder_results, 3); - boost::thread reader_thread(&stop_reset_reading_thread, boost::ref(reader_queue), reader_results, 1); + std::thread feeder_thread([&feeder_queue, &feeder_results]() { stop_reset_feeding_thread(feeder_queue, feeder_results, 3); }); + std::thread reader_thread([&reader_queue, &reader_results]() { stop_reset_reading_thread(reader_queue, reader_results, 1); }); BOOST_TEST_PASSPOINT(); reader_thread.join(); BOOST_TEST_PASSPOINT(); - boost::this_thread::sleep_for(boost::chrono::milliseconds(500)); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); BOOST_TEST_PASSPOINT(); @@ -437,14 +479,14 @@ BOOST_AUTO_TEST_CASE(stop_reset_local) BOOST_TEST_PASSPOINT(); // Case 2: Let the reader block and then we unblock it with stop_local() - boost::thread(&stop_reset_feeding_thread, boost::ref(feeder_queue), feeder_results, 1).swap(feeder_thread); - boost::thread(&stop_reset_reading_thread, boost::ref(reader_queue), reader_results, 2).swap(reader_thread); + feeder_thread = std::thread([&feeder_queue, &feeder_results]() { stop_reset_feeding_thread(feeder_queue, feeder_results, 1); }); + reader_thread = std::thread([&reader_queue, &reader_results]() { stop_reset_reading_thread(reader_queue, reader_results, 2); }); BOOST_TEST_PASSPOINT(); feeder_thread.join(); BOOST_TEST_PASSPOINT(); - boost::this_thread::sleep_for(boost::chrono::milliseconds(500)); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); BOOST_TEST_PASSPOINT(); @@ -461,3 +503,12 @@ BOOST_AUTO_TEST_CASE(stop_reset_local) } #endif // !defined(BOOST_LOG_NO_THREADS) + +#else // !defined(BOOST_LOG_WITHOUT_IPC) + +int main() +{ + return 0; +} + +#endif // !defined(BOOST_LOG_WITHOUT_IPC) diff --git a/test/run/util_manip_auto_newline.cpp b/test/run/util_manip_auto_newline.cpp new file mode 100644 index 0000000000..91f1d07b5e --- /dev/null +++ b/test/run/util_manip_auto_newline.cpp @@ -0,0 +1,83 @@ +/* + * Copyright Andrey Semashev 2019. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * https://www.boost.org/LICENSE_1_0.txt) + */ +/*! + * \file util_manip_auto_newline.cpp + * \author Andrey Semashev + * \date 23.06.2019 + * + * \brief This header contains tests for the auto_newline manipulator. + */ + +#define BOOST_TEST_MODULE util_manip_auto_newline + +#include +#include +#include +#include +#include "char_definitions.hpp" + +namespace logging = boost::log; + +// Test appending a newline to a non-empty string +BOOST_AUTO_TEST_CASE_TEMPLATE(append_to_non_empty_string, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + typedef logging::basic_formatting_ostream< char_type > formatting_ostream_type; + typedef typename formatting_ostream_type::string_type string_type; + + string_type str_fmt; + formatting_ostream_type strm_fmt(str_fmt); + + strm_fmt << "Hello" << logging::auto_newline; + strm_fmt.flush(); + + ostream_type strm_correct; + strm_correct << "Hello\n"; + + BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str())); +} + +// Test appending a newline to an empty string +BOOST_AUTO_TEST_CASE_TEMPLATE(append_to_empty_string, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + typedef logging::basic_formatting_ostream< char_type > formatting_ostream_type; + typedef typename formatting_ostream_type::string_type string_type; + + string_type str_fmt; + formatting_ostream_type strm_fmt(str_fmt); + + strm_fmt << logging::auto_newline; + strm_fmt.flush(); + + ostream_type strm_correct; + strm_correct << "\n"; + + BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str())); +} + +// Test not appending a newline to a non-empty string which already ends with a newline +BOOST_AUTO_TEST_CASE_TEMPLATE(not_append_if_ends_with_a_newline, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + typedef logging::basic_formatting_ostream< char_type > formatting_ostream_type; + typedef typename formatting_ostream_type::string_type string_type; + + string_type str_fmt; + formatting_ostream_type strm_fmt(str_fmt); + + strm_fmt << "Hello\n" << logging::auto_newline; + strm_fmt.flush(); + + ostream_type strm_correct; + strm_correct << "Hello\n"; + + BOOST_CHECK(equal_strings(strm_fmt.str(), strm_correct.str())); +} diff --git a/test/run/util_manip_invoke.cpp b/test/run/util_manip_invoke.cpp new file mode 100644 index 0000000000..c0ea0e983a --- /dev/null +++ b/test/run/util_manip_invoke.cpp @@ -0,0 +1,152 @@ +/* + * Copyright Andrey Semashev 2022. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +/*! + * \file util_manip_invoke.cpp + * \author Andrey Semashev + * \date 27.02.2022 + * + * \brief This header contains tests for the invoke manipulator. + */ + +#define BOOST_TEST_MODULE util_manip_invoke + +#include +#include +#include +#include +#include +#include +#include +#include "char_definitions.hpp" + +namespace logging = boost::log; + +struct my_function0 +{ + typedef void result_type; + + template< typename StreamT > + result_type operator() (StreamT& stream) const + { + stream << "my_function0"; + } +}; + +struct my_function1 +{ + typedef void result_type; + + template< typename StreamT > + result_type operator() (StreamT& stream, int n) const + { + stream << "my_function1(" << n << ")"; + } +}; + +template< typename StreamT > +void free_function(StreamT& stream, int n) +{ + stream << "free_function(" << n << ")"; +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(invoke_my_function0, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + + ostream_type strm_dump; + strm_dump << logging::invoke_manip(my_function0()); + + ostream_type strm_correct; + my_function0()(strm_correct); + + BOOST_CHECK(equal_strings(strm_dump.str(), strm_correct.str())); +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(invoke_boost_bind, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + + { + ostream_type strm_dump; + strm_dump << logging::invoke_manip(boost::bind(my_function1(), boost::placeholders::_1, 10)); + + ostream_type strm_correct; + boost::bind(my_function1(), boost::placeholders::_1, 10)(strm_correct); + + BOOST_CHECK(equal_strings(strm_dump.str(), strm_correct.str())); + } + { + ostream_type strm_dump; + strm_dump << logging::invoke_manip(boost::bind(&free_function< ostream_type >, boost::placeholders::_1, 10)); + + ostream_type strm_correct; + boost::bind(&free_function< ostream_type >, boost::placeholders::_1, 10)(strm_correct); + + BOOST_CHECK(equal_strings(strm_dump.str(), strm_correct.str())); + } +} + +#if !defined(BOOST_NO_CXX11_LAMBDAS) + +BOOST_AUTO_TEST_CASE_TEMPLATE(invoke_lambda, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + + ostream_type strm_dump; + strm_dump << logging::invoke_manip([](ostream_type& strm) { strm << "lambda"; }); + + ostream_type strm_correct; + strm_correct << "lambda"; + + BOOST_CHECK(equal_strings(strm_dump.str(), strm_correct.str())); +} + +#endif // !defined(BOOST_NO_CXX11_LAMBDAS) + +// This list of config macros matches the similar list in boost/log/utility/manipulators/invoke.hpp +#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) && \ + !defined(BOOST_NO_CXX14_GENERIC_LAMBDAS) && \ + !defined(BOOST_NO_CXX14_RETURN_TYPE_DEDUCTION) + +BOOST_AUTO_TEST_CASE_TEMPLATE(invoke_lambda_args, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + + ostream_type strm_dump; + strm_dump << logging::invoke_manip([](ostream_type& strm, int x, int y) { strm << "lambda(" << x << ", " << y << ")"; }, 10, 20); + + ostream_type strm_correct; + strm_correct << "lambda(" << 10 << ", " << 20 << ")"; + + BOOST_CHECK(equal_strings(strm_dump.str(), strm_correct.str())); +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(invoke_lambda_args_ref, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + + int external_var = 30; + ostream_type strm_dump; + strm_dump << logging::invoke_manip([](ostream_type& strm, int x, int y, int& z) + { + strm << "lambda(" << x << ", " << y << ", " << z << ")"; + ++z; + }, 10, 20, boost::ref(external_var)); + + ostream_type strm_correct; + strm_correct << "lambda(" << 10 << ", " << 20 << ", " << 30 << ")"; + + BOOST_CHECK(equal_strings(strm_dump.str(), strm_correct.str())); + BOOST_CHECK_EQUAL(external_var, 31); +} + +#endif // !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) ... diff --git a/test/run/util_manip_optional.cpp b/test/run/util_manip_optional.cpp new file mode 100644 index 0000000000..226da29f23 --- /dev/null +++ b/test/run/util_manip_optional.cpp @@ -0,0 +1,179 @@ +/* + * Copyright Andrey Semashev 2020. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +/*! + * \file util_manip_optional.cpp + * \author Andrey Semashev + * \date 12.05.2020 + * + * \brief This header contains tests for the optional manipulator. + */ + +#define BOOST_TEST_MODULE util_manip_optional + +#include +#include +#include +#include +#include +#include +#include "char_definitions.hpp" + +namespace logging = boost::log; + +struct my_int_wrapper +{ + int n; + + my_int_wrapper(int x = 0) BOOST_NOEXCEPT : n(x) {} +}; + +template< typename CharT, typename TraitsT > +inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, my_int_wrapper miw) +{ + if (BOOST_LIKELY(strm.good())) + strm << '{' << miw.n << '}'; + + return strm; +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(opt_none_no_marker, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + typedef std::basic_string< char_type > string_type; + + boost::optional< int > data; + + ostream_type strm_dump; + strm_dump << logging::optional_manip(data); + + BOOST_CHECK(equal_strings(strm_dump.str(), string_type())); +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(opt_none_with_marker_char, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + + boost::optional< int > data; + + ostream_type strm_dump; + strm_dump << logging::optional_manip(data, '-'); + + ostream_type strm_correct; + strm_correct << "-"; + + BOOST_CHECK(equal_strings(strm_dump.str(), strm_correct.str())); +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(opt_none_with_marker_string, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + + boost::optional< int > data; + + ostream_type strm_dump; + strm_dump << logging::optional_manip(data, "[none]"); + + ostream_type strm_correct; + strm_correct << "[none]"; + + BOOST_CHECK(equal_strings(strm_dump.str(), strm_correct.str())); +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(opt_none_with_marker_std_string, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + typedef std::basic_string< char_type > string_type; + + boost::optional< int > data; + + string_type marker; + marker.push_back('['); + marker.push_back('n'); + marker.push_back('o'); + marker.push_back('n'); + marker.push_back('e'); + marker.push_back(']'); + + ostream_type strm_dump; + strm_dump << logging::optional_manip(data, marker); + + ostream_type strm_correct; + strm_correct << "[none]"; + + BOOST_CHECK(equal_strings(strm_dump.str(), strm_correct.str())); +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(ptr_none_with_marker_string, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + + const int* data = 0; + + ostream_type strm_dump; + strm_dump << logging::optional_manip(data, "[none]"); + + ostream_type strm_correct; + strm_correct << "[none]"; + + BOOST_CHECK(equal_strings(strm_dump.str(), strm_correct.str())); +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(opt_int_with_marker_string, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + + boost::optional< int > data; + data = 7; + + ostream_type strm_dump; + strm_dump << logging::optional_manip(data, "[none]"); + + ostream_type strm_correct; + strm_correct << "7"; + + BOOST_CHECK(equal_strings(strm_dump.str(), strm_correct.str())); +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(ptr_int_with_marker_string, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + + int n = 7; + int* data = &n; + + ostream_type strm_dump; + strm_dump << logging::optional_manip(data, "[none]"); + + ostream_type strm_correct; + strm_correct << "7"; + + BOOST_CHECK(equal_strings(strm_dump.str(), strm_correct.str())); +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(ptr_miw_with_marker_string, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + + my_int_wrapper n(7); + const my_int_wrapper* data = &n; + + ostream_type strm_dump; + strm_dump << logging::optional_manip(data, "[none]"); + + ostream_type strm_correct; + strm_correct << "{7}"; + + BOOST_CHECK(equal_strings(strm_dump.str(), strm_correct.str())); +} diff --git a/test/run/util_manip_range.cpp b/test/run/util_manip_range.cpp new file mode 100644 index 0000000000..d07914d60b --- /dev/null +++ b/test/run/util_manip_range.cpp @@ -0,0 +1,158 @@ +/* + * Copyright Andrey Semashev 2020. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +/*! + * \file util_manip_range.cpp + * \author Andrey Semashev + * \date 11.05.2020 + * + * \brief This header contains tests for the range manipulator. + */ + +#define BOOST_TEST_MODULE util_manip_range + +#include +#include +#include +#include +#include +#include +#include +#include "char_definitions.hpp" + +namespace logging = boost::log; + +struct my_int_wrapper +{ + int n; + + my_int_wrapper(int x = 0) BOOST_NOEXCEPT : n(x) {} +}; + +template< typename CharT, typename TraitsT > +inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, my_int_wrapper miw) +{ + if (BOOST_LIKELY(strm.good())) + strm << '{' << miw.n << '}'; + + return strm; +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(int_no_separator, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + + std::vector< int > data; + data.push_back(1); + data.push_back(2); + data.push_back(3); + + ostream_type strm_dump; + strm_dump << logging::range_manip(data); + + ostream_type strm_correct; + strm_correct << "123"; + + BOOST_CHECK(equal_strings(strm_dump.str(), strm_correct.str())); +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(int_char_separator, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + + std::vector< int > data; + data.push_back(1); + data.push_back(2); + data.push_back(3); + + ostream_type strm_dump; + strm_dump << logging::range_manip(data, ' '); + + ostream_type strm_correct; + strm_correct << "1 2 3"; + + BOOST_CHECK(equal_strings(strm_dump.str(), strm_correct.str())); +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(int_string_separator, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + + std::vector< int > data; + data.push_back(1); + data.push_back(2); + data.push_back(3); + + ostream_type strm_dump; + strm_dump << logging::range_manip(data, ", "); + + ostream_type strm_correct; + strm_correct << "1, 2, 3"; + + BOOST_CHECK(equal_strings(strm_dump.str(), strm_correct.str())); +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(int_std_string_separator, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + typedef std::basic_string< char_type > string_type; + + std::vector< int > data; + data.push_back(1); + data.push_back(2); + data.push_back(3); + + string_type separator; + separator.push_back(','); + separator.push_back(' '); + + ostream_type strm_dump; + strm_dump << logging::range_manip(data, separator); + + ostream_type strm_correct; + strm_correct << "1, 2, 3"; + + BOOST_CHECK(equal_strings(strm_dump.str(), strm_correct.str())); +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(miw_string_separator, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + + std::list< my_int_wrapper > data; + data.push_back(my_int_wrapper(1)); + data.push_back(my_int_wrapper(2)); + data.push_back(my_int_wrapper(3)); + + ostream_type strm_dump; + strm_dump << logging::range_manip(data, ", "); + + ostream_type strm_correct; + strm_correct << "{1}, {2}, {3}"; + + BOOST_CHECK(equal_strings(strm_dump.str(), strm_correct.str())); +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(array_int_string_separator, CharT, char_types) +{ + typedef CharT char_type; + typedef std::basic_ostringstream< char_type > ostream_type; + + int data[3] = { 1, 2, 3 }; + + ostream_type strm_dump; + strm_dump << logging::range_manip(data, ", "); + + ostream_type strm_correct; + strm_correct << "1, 2, 3"; + + BOOST_CHECK(equal_strings(strm_dump.str(), strm_correct.str())); +} diff --git a/test/run/util_manip_tuple.cpp b/test/run/util_manip_tuple.cpp new file mode 100644 index 0000000000..d30b71fbf9 --- /dev/null +++ b/test/run/util_manip_tuple.cpp @@ -0,0 +1,140 @@ +/* + * Copyright Andrey Semashev 2020. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + */ +/*! + * \file util_manip_tuple.cpp + * \author Andrey Semashev + * \date 11.05.2020 + * + * \brief This header contains tests for the tuple manipulator. + */ + +#define BOOST_TEST_MODULE util_manip_tuple + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "char_definitions.hpp" + +namespace logging = boost::log; + +struct my_int_wrapper +{ + int n; + + my_int_wrapper(int x = 0) BOOST_NOEXCEPT : n(x) {} +}; + +template< typename CharT, typename TraitsT > +inline std::basic_ostream< CharT, TraitsT >& operator<< (std::basic_ostream< CharT, TraitsT >& strm, my_int_wrapper miw) +{ + if (BOOST_LIKELY(strm.good())) + strm << '{' << miw.n << '}'; + + return strm; +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(pair_no_separator, CharT, char_types) +{ + typedef CharT char_type; + typedef test_data< char_type > test_data_type; + typedef std::basic_ostringstream< char_type > ostream_type; + typedef std::basic_string< char_type > string_type; + + std::pair< int, string_type > data(1, test_data_type::abc()); + + ostream_type strm_dump; + strm_dump << logging::tuple_manip(data); + + ostream_type strm_correct; + strm_correct << "1abc"; + + BOOST_CHECK(equal_strings(strm_dump.str(), strm_correct.str())); +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(tuple_char_separator, CharT, char_types) +{ + typedef CharT char_type; + typedef test_data< char_type > test_data_type; + typedef std::basic_ostringstream< char_type > ostream_type; + typedef std::basic_string< char_type > string_type; + + boost::tuple< int, my_int_wrapper, string_type > data(1, my_int_wrapper(5), string_type(test_data_type::abc())); + + ostream_type strm_dump; + strm_dump << logging::tuple_manip(data, ' '); + + ostream_type strm_correct; + strm_correct << "1 {5} abc"; + + BOOST_CHECK(equal_strings(strm_dump.str(), strm_correct.str())); +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(tuple_string_separator, CharT, char_types) +{ + typedef CharT char_type; + typedef test_data< char_type > test_data_type; + typedef std::basic_ostringstream< char_type > ostream_type; + typedef std::basic_string< char_type > string_type; + + boost::tuple< int, my_int_wrapper, string_type > data(1, my_int_wrapper(5), string_type(test_data_type::abc())); + + ostream_type strm_dump; + strm_dump << logging::tuple_manip(data, ", "); + + ostream_type strm_correct; + strm_correct << "1, {5}, abc"; + + BOOST_CHECK(equal_strings(strm_dump.str(), strm_correct.str())); +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(tuple_std_string_separator, CharT, char_types) +{ + typedef CharT char_type; + typedef test_data< char_type > test_data_type; + typedef std::basic_ostringstream< char_type > ostream_type; + typedef std::basic_string< char_type > string_type; + + boost::tuple< int, my_int_wrapper, string_type > data(1, my_int_wrapper(5), string_type(test_data_type::abc())); + + string_type separator; + separator.push_back(','); + separator.push_back(' '); + + ostream_type strm_dump; + strm_dump << logging::tuple_manip(data, separator); + + ostream_type strm_correct; + strm_correct << "1, {5}, abc"; + + BOOST_CHECK(equal_strings(strm_dump.str(), strm_correct.str())); +} + +BOOST_AUTO_TEST_CASE_TEMPLATE(tie_string_separator, CharT, char_types) +{ + typedef CharT char_type; + typedef test_data< char_type > test_data_type; + typedef std::basic_ostringstream< char_type > ostream_type; + typedef std::basic_string< char_type > string_type; + + int x = 1; + my_int_wrapper y(5); + string_type z(test_data_type::abc()); + + ostream_type strm_dump; + strm_dump << logging::tuple_manip(boost::tie(x, y, z), ", "); + + ostream_type strm_correct; + strm_correct << "1, {5}, abc"; + + BOOST_CHECK(equal_strings(strm_dump.str(), strm_correct.str())); +} diff --git a/test/run/util_once_block.cpp b/test/run/util_once_block.cpp index d952dad74b..ced2e94f44 100644 --- a/test/run/util_once_block.cpp +++ b/test/run/util_once_block.cpp @@ -21,12 +21,9 @@ #if !defined(BOOST_LOG_NO_THREADS) -#include -#include -#include -#include -#include -#include +#include +#include +#include "test_barrier.hpp" namespace logging = boost::log; @@ -36,8 +33,8 @@ enum config LOOP_COUNT = 100 }; -boost::mutex m; -typedef boost::lock_guard< boost::mutex > scoped_lock; +std::mutex m; +typedef std::lock_guard< std::mutex > scoped_lock; logging::once_block_flag flag = BOOST_LOG_ONCE_BLOCK_INIT; int var_to_init_once_flag = 0; @@ -50,10 +47,10 @@ void initialize_variable() } -void once_block_flag_thread(boost::barrier& barrier) +void once_block_flag_thread(test_barrier& barrier) { int my_once_value = 0; - barrier.wait(); + barrier.arrive_and_wait(); for (unsigned int i = 0; i < LOOP_COUNT; ++i) { BOOST_LOG_ONCE_BLOCK_FLAG(flag) @@ -74,21 +71,25 @@ void once_block_flag_thread(boost::barrier& barrier) // The test checks if the BOOST_LOG_ONCE_BLOCK_FLAG macro works BOOST_AUTO_TEST_CASE(once_block_flag) { - boost::thread_group group; - boost::barrier barrier(static_cast< unsigned int >(THREAD_COUNT)); + std::thread threads[THREAD_COUNT]; + test_barrier barrier(static_cast< unsigned int >(THREAD_COUNT)); try { for (unsigned int i = 0; i < THREAD_COUNT; ++i) - { - group.create_thread(boost::bind(&once_block_flag_thread, boost::ref(barrier))); - } - group.join_all(); + threads[i] = std::thread([&barrier]() { once_block_flag_thread(barrier); }); + + for (unsigned int i = 0; i < THREAD_COUNT; ++i) + threads[i].join(); } catch (...) { - group.interrupt_all(); - group.join_all(); + barrier.wake_all(); + for (unsigned int i = 0; i < THREAD_COUNT; ++i) + { + if (threads[i].joinable()) + threads[i].join(); + } throw; } @@ -97,10 +98,10 @@ BOOST_AUTO_TEST_CASE(once_block_flag) int var_to_init_once = 0; -void once_block_thread(boost::barrier& barrier) +void once_block_thread(test_barrier& barrier) { int my_once_value = 0; - barrier.wait(); + barrier.arrive_and_wait(); for (unsigned int i = 0; i < LOOP_COUNT; ++i) { BOOST_LOG_ONCE_BLOCK() @@ -123,21 +124,25 @@ void once_block_thread(boost::barrier& barrier) // The test checks if the BOOST_LOG_ONCE_BLOCK macro works BOOST_AUTO_TEST_CASE(once_block) { - boost::thread_group group; - boost::barrier barrier(static_cast< unsigned int >(THREAD_COUNT)); + std::thread threads[THREAD_COUNT]; + test_barrier barrier(static_cast< unsigned int >(THREAD_COUNT)); try { for (unsigned int i = 0; i < THREAD_COUNT; ++i) - { - group.create_thread(boost::bind(&once_block_thread, boost::ref(barrier))); - } - group.join_all(); + threads[i] = std::thread([&barrier]() { once_block_thread(barrier); }); + + for (unsigned int i = 0; i < THREAD_COUNT; ++i) + threads[i].join(); } - catch(...) + catch (...) { - group.interrupt_all(); - group.join_all(); + barrier.wake_all(); + for (unsigned int i = 0; i < THREAD_COUNT; ++i) + { + if (threads[i].joinable()) + threads[i].join(); + } throw; } @@ -152,9 +157,9 @@ struct my_exception unsigned int pass_counter = 0; unsigned int exception_counter = 0; -void once_block_with_exception_thread(boost::barrier& barrier) +void once_block_with_exception_thread(test_barrier& barrier) { - barrier.wait(); + barrier.arrive_and_wait(); try { BOOST_LOG_ONCE_BLOCK() @@ -177,21 +182,25 @@ void once_block_with_exception_thread(boost::barrier& barrier) // The test verifies that the once_block flag is not set if an exception is thrown from the once-block BOOST_AUTO_TEST_CASE(once_block_retried_on_exception) { - boost::thread_group group; - boost::barrier barrier(static_cast< unsigned int >(THREAD_COUNT)); + std::thread threads[THREAD_COUNT]; + test_barrier barrier(static_cast< unsigned int >(THREAD_COUNT)); try { for (unsigned int i = 0; i < THREAD_COUNT; ++i) - { - group.create_thread(boost::bind(&once_block_with_exception_thread, boost::ref(barrier))); - } - group.join_all(); + threads[i] = std::thread([&barrier]() { once_block_with_exception_thread(barrier); }); + + for (unsigned int i = 0; i < THREAD_COUNT; ++i) + threads[i].join(); } - catch(...) + catch (...) { - group.interrupt_all(); - group.join_all(); + barrier.wake_all(); + for (unsigned int i = 0; i < THREAD_COUNT; ++i) + { + if (threads[i].joinable()) + threads[i].join(); + } throw; } diff --git a/test/run/util_static_type_disp.cpp b/test/run/util_static_type_disp.cpp index e318fc5a53..3f30ad5d51 100644 --- a/test/run/util_static_type_disp.cpp +++ b/test/run/util_static_type_disp.cpp @@ -17,8 +17,8 @@ #include #include #include -#include #include +#include #include namespace logging = boost::log; diff --git a/test/test_cmake/CMakeLists.txt b/test/test_cmake/CMakeLists.txt new file mode 100644 index 0000000000..7f679e234d --- /dev/null +++ b/test/test_cmake/CMakeLists.txt @@ -0,0 +1,27 @@ +# Copyright 2021 Andrey Semashev +# +# Distributed under the Boost Software License, Version 1.0. +# See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt +# +# NOTE: This does NOT run the unit tests for Boost.Log. +# It only tests if the CMakeLists.txt file in its root works as expected + +cmake_minimum_required(VERSION 3.5...3.16) + +project(BoostLogCMakeSelfTest) + +# Use experimental superproject to pull library dependencies recursively +set(BOOST_ENABLE_CMAKE 1) +add_subdirectory(../../../.. "${CMAKE_CURRENT_BINARY_DIR}/boost_superproject") + +add_definitions(-DBOOST_ALL_NO_LIB) + +if (BOOST_LOG_WITHOUT_SETTINGS_PARSERS) + add_definitions(-DBOOST_LOG_WITHOUT_SETTINGS_PARSERS) +endif() + +add_executable(boost_log_cmake_self_test log_test.cpp) +target_link_libraries(boost_log_cmake_self_test Boost::log) + +add_executable(boost_log_setup_cmake_self_test log_setup_test.cpp) +target_link_libraries(boost_log_setup_cmake_self_test Boost::log_setup) diff --git a/test/test_cmake/log_setup_test.cpp b/test/test_cmake/log_setup_test.cpp new file mode 100644 index 0000000000..6229ed0f61 --- /dev/null +++ b/test/test_cmake/log_setup_test.cpp @@ -0,0 +1,16 @@ +// Copyright (c) 2021 Andrey Semashev +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +int main() +{ +#if !defined(BOOST_LOG_WITHOUT_SETTINGS_PARSERS) + std::istringstream strm; + boost::log::init_from_stream(strm); +#endif +} diff --git a/test/test_cmake/log_test.cpp b/test/test_cmake/log_test.cpp new file mode 100644 index 0000000000..0ee5305659 --- /dev/null +++ b/test/test_cmake/log_test.cpp @@ -0,0 +1,19 @@ +// Copyright (c) 2021 Andrey Semashev +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE_1_0.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include +#include +#include +#include + +int main() +{ + BOOST_LOG_TRIVIAL(info) << "Hello world!"; +}