diff --git a/.clang-format b/.clang-format index 421fa947..0adab5ca 100644 --- a/.clang-format +++ b/.clang-format @@ -1,18 +1,48 @@ --- BasedOnStyle: LLVM -AlignTrailingComments: false -AllowShortIfStatementsOnASingleLine: true -AllowShortLoopsOnASingleLine: true -ColumnLimit: 0 -ConstructorInitializerAllOnOneLineOrOnePerLine: true -BreakConstructorInitializersBeforeComma: false + Standard: Cpp11 IndentWidth: 4 TabWidth: 4 UseTab: Always -BreakBeforeBraces: Attach -#BreakBeforeBraces: Allman -SpacesInParentheses: true +ColumnLimit: 200 +MaxEmptyLinesToKeep: 4 + +AlignTrailingComments: false +AllowShortLoopsOnASingleLine: true + +# require clang-format v14+ +# PackConstructorInitializers: CurrentLine + +BreakBeforeBraces: Custom +BraceWrapping: + AfterClass: true + AfterStruct: true + AfterFunction: true + BeforeElse: true + + +SpaceBeforeParens: Never + +# require clang-format v14+ +# SpaceBeforeParens: Custom +# SpaceBeforeParensOptions: +# AfterControlStatements: false + + +SpacesInParentheses: false +SpacesInConditionalStatement: true SpaceInEmptyParentheses: false -... +SpacesInAngles: Leave + +IndentAccessModifiers: false +AccessModifierOffset: -4 +AllowShortFunctionsOnASingleLine: InlineOnly +AllowShortIfStatementsOnASingleLine: AllIfsAndElse +AllowShortBlocksOnASingleLine: Always + +#PointerAlignment: Middle +#SpaceAroundPointerQualifiers: Both +#ReferenceAlignment: Middle +... diff --git a/.github/workflows/fedora26/Dockerfile b/.github/workflows/fedora26/Dockerfile deleted file mode 100644 index 527827f9..00000000 --- a/.github/workflows/fedora26/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -# Container image that runs your code -FROM fedora:26 - -#Copies your code file from your action repository to the filesystem path `/` of the container -COPY entrypoint.sh /entrypoint.sh - -#Code file to execute when the docker container starts up (`entrypoint.sh`) -ENTRYPOINT ["/entrypoint.sh"] diff --git a/.github/workflows/fedora26/action.yaml b/.github/workflows/fedora26/action.yaml deleted file mode 100644 index 8644a211..00000000 --- a/.github/workflows/fedora26/action.yaml +++ /dev/null @@ -1,15 +0,0 @@ -name: 'Compile binder' -description: 'Binder' -inputs: - theextraargument: # an optional extra argument for docker run. - description: 'Extra docker argument' - required: true - default: 'World' -outputs: - thestatus: # here we put the execution status - description: 'The execution status' -runs: - using: 'docker' - image: 'Dockerfile' - args: - - ${{ inputs.theextraargument }} diff --git a/.github/workflows/fedora26/entrypoint.sh b/.github/workflows/fedora26/entrypoint.sh deleted file mode 100755 index 60b17ffd..00000000 --- a/.github/workflows/fedora26/entrypoint.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -l -set -x -uname -a -cat /etc/issue -yum -y install git zlib zlib-devel ncurses-devel clang clang-devel clang-libs llvm llvm-devel llvm-static -yum -y install libcxx-devel cmake make gcc gcc-c++ -yum -y install pybind11-devel python3 python3-devel python2 python2-devel - -cmake CMakeLists.txt -make -ldd source/binder -ldd -u -r source/binder -ctest . --output-on-failure -out=$? -cat Testing/Temporary/LastTest.log -echo ::set-output name=out::$out diff --git a/.github/workflows/fedora27/Dockerfile b/.github/workflows/fedora27/Dockerfile deleted file mode 100644 index 91f0294a..00000000 --- a/.github/workflows/fedora27/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -# Container image that runs your code -FROM fedora:27 - -#Copies your code file from your action repository to the filesystem path `/` of the container -COPY entrypoint.sh /entrypoint.sh - -#Code file to execute when the docker container starts up (`entrypoint.sh`) -ENTRYPOINT ["/entrypoint.sh"] diff --git a/.github/workflows/fedora27/action.yaml b/.github/workflows/fedora27/action.yaml deleted file mode 100644 index 8644a211..00000000 --- a/.github/workflows/fedora27/action.yaml +++ /dev/null @@ -1,15 +0,0 @@ -name: 'Compile binder' -description: 'Binder' -inputs: - theextraargument: # an optional extra argument for docker run. - description: 'Extra docker argument' - required: true - default: 'World' -outputs: - thestatus: # here we put the execution status - description: 'The execution status' -runs: - using: 'docker' - image: 'Dockerfile' - args: - - ${{ inputs.theextraargument }} diff --git a/.github/workflows/fedora27/entrypoint.sh b/.github/workflows/fedora27/entrypoint.sh deleted file mode 100755 index 60b17ffd..00000000 --- a/.github/workflows/fedora27/entrypoint.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -l -set -x -uname -a -cat /etc/issue -yum -y install git zlib zlib-devel ncurses-devel clang clang-devel clang-libs llvm llvm-devel llvm-static -yum -y install libcxx-devel cmake make gcc gcc-c++ -yum -y install pybind11-devel python3 python3-devel python2 python2-devel - -cmake CMakeLists.txt -make -ldd source/binder -ldd -u -r source/binder -ctest . --output-on-failure -out=$? -cat Testing/Temporary/LastTest.log -echo ::set-output name=out::$out diff --git a/.github/workflows/fedora28/Dockerfile b/.github/workflows/fedora28/Dockerfile deleted file mode 100644 index e8fe99f4..00000000 --- a/.github/workflows/fedora28/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -# Container image that runs your code -FROM fedora:28 - -#Copies your code file from your action repository to the filesystem path `/` of the container -COPY entrypoint.sh /entrypoint.sh - -#Code file to execute when the docker container starts up (`entrypoint.sh`) -ENTRYPOINT ["/entrypoint.sh"] diff --git a/.github/workflows/fedora28/action.yaml b/.github/workflows/fedora28/action.yaml deleted file mode 100644 index 8644a211..00000000 --- a/.github/workflows/fedora28/action.yaml +++ /dev/null @@ -1,15 +0,0 @@ -name: 'Compile binder' -description: 'Binder' -inputs: - theextraargument: # an optional extra argument for docker run. - description: 'Extra docker argument' - required: true - default: 'World' -outputs: - thestatus: # here we put the execution status - description: 'The execution status' -runs: - using: 'docker' - image: 'Dockerfile' - args: - - ${{ inputs.theextraargument }} diff --git a/.github/workflows/fedora28/entrypoint.sh b/.github/workflows/fedora28/entrypoint.sh deleted file mode 100755 index 60b17ffd..00000000 --- a/.github/workflows/fedora28/entrypoint.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -l -set -x -uname -a -cat /etc/issue -yum -y install git zlib zlib-devel ncurses-devel clang clang-devel clang-libs llvm llvm-devel llvm-static -yum -y install libcxx-devel cmake make gcc gcc-c++ -yum -y install pybind11-devel python3 python3-devel python2 python2-devel - -cmake CMakeLists.txt -make -ldd source/binder -ldd -u -r source/binder -ctest . --output-on-failure -out=$? -cat Testing/Temporary/LastTest.log -echo ::set-output name=out::$out diff --git a/.github/workflows/fedora29/Dockerfile b/.github/workflows/fedora29/Dockerfile deleted file mode 100644 index 54d7c7f8..00000000 --- a/.github/workflows/fedora29/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -# Container image that runs your code -FROM fedora:29 - -#Copies your code file from your action repository to the filesystem path `/` of the container -COPY entrypoint.sh /entrypoint.sh - -#Code file to execute when the docker container starts up (`entrypoint.sh`) -ENTRYPOINT ["/entrypoint.sh"] diff --git a/.github/workflows/fedora29/action.yaml b/.github/workflows/fedora29/action.yaml deleted file mode 100644 index 8644a211..00000000 --- a/.github/workflows/fedora29/action.yaml +++ /dev/null @@ -1,15 +0,0 @@ -name: 'Compile binder' -description: 'Binder' -inputs: - theextraargument: # an optional extra argument for docker run. - description: 'Extra docker argument' - required: true - default: 'World' -outputs: - thestatus: # here we put the execution status - description: 'The execution status' -runs: - using: 'docker' - image: 'Dockerfile' - args: - - ${{ inputs.theextraargument }} diff --git a/.github/workflows/fedora29/entrypoint.sh b/.github/workflows/fedora29/entrypoint.sh deleted file mode 100755 index 60b17ffd..00000000 --- a/.github/workflows/fedora29/entrypoint.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -l -set -x -uname -a -cat /etc/issue -yum -y install git zlib zlib-devel ncurses-devel clang clang-devel clang-libs llvm llvm-devel llvm-static -yum -y install libcxx-devel cmake make gcc gcc-c++ -yum -y install pybind11-devel python3 python3-devel python2 python2-devel - -cmake CMakeLists.txt -make -ldd source/binder -ldd -u -r source/binder -ctest . --output-on-failure -out=$? -cat Testing/Temporary/LastTest.log -echo ::set-output name=out::$out diff --git a/.github/workflows/fedora30/Dockerfile b/.github/workflows/fedora30/Dockerfile deleted file mode 100644 index 963a30c4..00000000 --- a/.github/workflows/fedora30/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -# Container image that runs your code -FROM fedora:30 - -#Copies your code file from your action repository to the filesystem path `/` of the container -COPY entrypoint.sh /entrypoint.sh - -#Code file to execute when the docker container starts up (`entrypoint.sh`) -ENTRYPOINT ["/entrypoint.sh"] diff --git a/.github/workflows/fedora30/action.yaml b/.github/workflows/fedora30/action.yaml deleted file mode 100644 index 8644a211..00000000 --- a/.github/workflows/fedora30/action.yaml +++ /dev/null @@ -1,15 +0,0 @@ -name: 'Compile binder' -description: 'Binder' -inputs: - theextraargument: # an optional extra argument for docker run. - description: 'Extra docker argument' - required: true - default: 'World' -outputs: - thestatus: # here we put the execution status - description: 'The execution status' -runs: - using: 'docker' - image: 'Dockerfile' - args: - - ${{ inputs.theextraargument }} diff --git a/.github/workflows/fedora30/entrypoint.sh b/.github/workflows/fedora30/entrypoint.sh deleted file mode 100755 index 60b17ffd..00000000 --- a/.github/workflows/fedora30/entrypoint.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -l -set -x -uname -a -cat /etc/issue -yum -y install git zlib zlib-devel ncurses-devel clang clang-devel clang-libs llvm llvm-devel llvm-static -yum -y install libcxx-devel cmake make gcc gcc-c++ -yum -y install pybind11-devel python3 python3-devel python2 python2-devel - -cmake CMakeLists.txt -make -ldd source/binder -ldd -u -r source/binder -ctest . --output-on-failure -out=$? -cat Testing/Temporary/LastTest.log -echo ::set-output name=out::$out diff --git a/.github/workflows/fedora31/Dockerfile b/.github/workflows/fedora31/Dockerfile deleted file mode 100644 index c9410053..00000000 --- a/.github/workflows/fedora31/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -# Container image that runs your code -FROM fedora:31 - -#Copies your code file from your action repository to the filesystem path `/` of the container -COPY entrypoint.sh /entrypoint.sh - -#Code file to execute when the docker container starts up (`entrypoint.sh`) -ENTRYPOINT ["/entrypoint.sh"] diff --git a/.github/workflows/fedora31/action.yaml b/.github/workflows/fedora31/action.yaml deleted file mode 100644 index 8644a211..00000000 --- a/.github/workflows/fedora31/action.yaml +++ /dev/null @@ -1,15 +0,0 @@ -name: 'Compile binder' -description: 'Binder' -inputs: - theextraargument: # an optional extra argument for docker run. - description: 'Extra docker argument' - required: true - default: 'World' -outputs: - thestatus: # here we put the execution status - description: 'The execution status' -runs: - using: 'docker' - image: 'Dockerfile' - args: - - ${{ inputs.theextraargument }} diff --git a/.github/workflows/fedora31/entrypoint.sh b/.github/workflows/fedora31/entrypoint.sh deleted file mode 100755 index 60b17ffd..00000000 --- a/.github/workflows/fedora31/entrypoint.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -l -set -x -uname -a -cat /etc/issue -yum -y install git zlib zlib-devel ncurses-devel clang clang-devel clang-libs llvm llvm-devel llvm-static -yum -y install libcxx-devel cmake make gcc gcc-c++ -yum -y install pybind11-devel python3 python3-devel python2 python2-devel - -cmake CMakeLists.txt -make -ldd source/binder -ldd -u -r source/binder -ctest . --output-on-failure -out=$? -cat Testing/Temporary/LastTest.log -echo ::set-output name=out::$out diff --git a/.github/workflows/fedora32/Dockerfile b/.github/workflows/fedora32/Dockerfile deleted file mode 100644 index 79031e75..00000000 --- a/.github/workflows/fedora32/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -# Container image that runs your code -FROM fedora:32 - -#Copies your code file from your action repository to the filesystem path `/` of the container -COPY entrypoint.sh /entrypoint.sh - -#Code file to execute when the docker container starts up (`entrypoint.sh`) -ENTRYPOINT ["/entrypoint.sh"] diff --git a/.github/workflows/fedora32/action.yaml b/.github/workflows/fedora32/action.yaml deleted file mode 100644 index 8644a211..00000000 --- a/.github/workflows/fedora32/action.yaml +++ /dev/null @@ -1,15 +0,0 @@ -name: 'Compile binder' -description: 'Binder' -inputs: - theextraargument: # an optional extra argument for docker run. - description: 'Extra docker argument' - required: true - default: 'World' -outputs: - thestatus: # here we put the execution status - description: 'The execution status' -runs: - using: 'docker' - image: 'Dockerfile' - args: - - ${{ inputs.theextraargument }} diff --git a/.github/workflows/fedora32/entrypoint.sh b/.github/workflows/fedora32/entrypoint.sh deleted file mode 100755 index 60b17ffd..00000000 --- a/.github/workflows/fedora32/entrypoint.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -l -set -x -uname -a -cat /etc/issue -yum -y install git zlib zlib-devel ncurses-devel clang clang-devel clang-libs llvm llvm-devel llvm-static -yum -y install libcxx-devel cmake make gcc gcc-c++ -yum -y install pybind11-devel python3 python3-devel python2 python2-devel - -cmake CMakeLists.txt -make -ldd source/binder -ldd -u -r source/binder -ctest . --output-on-failure -out=$? -cat Testing/Temporary/LastTest.log -echo ::set-output name=out::$out diff --git a/.github/workflows/fedora33/Dockerfile b/.github/workflows/fedora33/Dockerfile deleted file mode 100644 index e24b1ff0..00000000 --- a/.github/workflows/fedora33/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -# Container image that runs your code -FROM fedora:33 - -#Copies your code file from your action repository to the filesystem path `/` of the container -COPY entrypoint.sh /entrypoint.sh - -#Code file to execute when the docker container starts up (`entrypoint.sh`) -ENTRYPOINT ["/entrypoint.sh"] diff --git a/.github/workflows/fedora33/action.yaml b/.github/workflows/fedora33/action.yaml deleted file mode 100644 index 8644a211..00000000 --- a/.github/workflows/fedora33/action.yaml +++ /dev/null @@ -1,15 +0,0 @@ -name: 'Compile binder' -description: 'Binder' -inputs: - theextraargument: # an optional extra argument for docker run. - description: 'Extra docker argument' - required: true - default: 'World' -outputs: - thestatus: # here we put the execution status - description: 'The execution status' -runs: - using: 'docker' - image: 'Dockerfile' - args: - - ${{ inputs.theextraargument }} diff --git a/.github/workflows/fedora33/entrypoint.sh b/.github/workflows/fedora33/entrypoint.sh deleted file mode 100755 index b57ada36..00000000 --- a/.github/workflows/fedora33/entrypoint.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -l -set -x -uname -a -cat /etc/issue -yum -y install --nogpgcheck git zlib zlib-devel ncurses-devel clang clang-devel clang-libs llvm llvm-devel llvm-static -yum -y install --nogpgcheck libcxx-devel cmake make gcc gcc-c++ -yum -y install --nogpgcheck pybind11-devel python3 python3-devel python2 python2-devel - -cmake CMakeLists.txt -make -ldd source/binder -ldd -u -r source/binder -ctest . --output-on-failure -out=$? -cat Testing/Temporary/LastTest.log -echo ::set-output name=out::$out diff --git a/.github/workflows/fedora34/Dockerfile b/.github/workflows/fedora34/Dockerfile deleted file mode 100644 index 1a0b6137..00000000 --- a/.github/workflows/fedora34/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -# Container image that runs your code -FROM fedora:34 - -#Copies your code file from your action repository to the filesystem path `/` of the container -COPY entrypoint.sh /entrypoint.sh - -#Code file to execute when the docker container starts up (`entrypoint.sh`) -ENTRYPOINT ["/entrypoint.sh"] diff --git a/.github/workflows/fedora34/action.yaml b/.github/workflows/fedora34/action.yaml deleted file mode 100644 index 8644a211..00000000 --- a/.github/workflows/fedora34/action.yaml +++ /dev/null @@ -1,15 +0,0 @@ -name: 'Compile binder' -description: 'Binder' -inputs: - theextraargument: # an optional extra argument for docker run. - description: 'Extra docker argument' - required: true - default: 'World' -outputs: - thestatus: # here we put the execution status - description: 'The execution status' -runs: - using: 'docker' - image: 'Dockerfile' - args: - - ${{ inputs.theextraargument }} diff --git a/.github/workflows/fedora34/entrypoint.sh b/.github/workflows/fedora34/entrypoint.sh deleted file mode 100755 index b57ada36..00000000 --- a/.github/workflows/fedora34/entrypoint.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -l -set -x -uname -a -cat /etc/issue -yum -y install --nogpgcheck git zlib zlib-devel ncurses-devel clang clang-devel clang-libs llvm llvm-devel llvm-static -yum -y install --nogpgcheck libcxx-devel cmake make gcc gcc-c++ -yum -y install --nogpgcheck pybind11-devel python3 python3-devel python2 python2-devel - -cmake CMakeLists.txt -make -ldd source/binder -ldd -u -r source/binder -ctest . --output-on-failure -out=$? -cat Testing/Temporary/LastTest.log -echo ::set-output name=out::$out diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 64cfe886..1c007faf 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,127 +7,55 @@ on: - cron: "0 0 1/5 * *" jobs: + compilejobFedora: + strategy: + fail-fast: false + matrix: + version: [26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40] + runs-on: ubuntu-latest + name: Binder_on_Fedora${{ matrix.version }} + container: + image: fedora:${{ matrix.version }} + steps: + - name: Install + run: | + set -x + uname -a + cat /etc/issue + yum -y install git zlib zlib-devel ncurses-devel clang clang-devel clang-libs llvm llvm-devel llvm-static \ + libcxx-devel cmake make gcc gcc-c++ \ + pybind11-devel python3 python3-devel python2 python2-devel && yum -y clean all + - name: Checkout + uses: actions/checkout@v3 + - name: Compile + run: | + cmake CMakeLists.txt + make + make install + ldd source/binder + ldd -u -r source/binder || echo "OK" + ctest . --output-on-failure - compilejobFedora26: - runs-on: ubuntu-latest - name: Binder_on_Fedora26 - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Compile - id: compileindocker - uses: ./.github/workflows/fedora26 - - name: Get the output status - run: exit ${{ steps.compileindocker.outputs.out }} - - compilejobFedora27: - runs-on: ubuntu-latest - name: Binder_on_Fedora27 - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Compile - id: compileindocker - uses: ./.github/workflows/fedora27 - - name: Get the output status - run: exit ${{ steps.compileindocker.outputs.out }} - - compilejobFedora28: - runs-on: ubuntu-latest - name: Binder_on_Fedora28 - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Compile - id: compileindocker - uses: ./.github/workflows/fedora28 - - name: Get the output status - run: exit ${{ steps.compileindocker.outputs.out }} - - compilejobFedora29: - runs-on: ubuntu-latest - name: Binder_on_Fedora29 - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Compile - id: compileindocker - uses: ./.github/workflows/fedora29 - - name: Get the output status - run: exit ${{ steps.compileindocker.outputs.out }} - - compilejobFedora30: - runs-on: ubuntu-latest - name: Binder_on_Fedora30 - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Compile - id: compileindocker - uses: ./.github/workflows/fedora30 - - name: Get the output status - run: exit ${{ steps.compileindocker.outputs.out }} - - - compilejobFedora31: - runs-on: ubuntu-latest - name: Binder_on_Fedora31 - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Compile - id: compileindocker - uses: ./.github/workflows/fedora31 - - name: Get the output status - run: exit ${{ steps.compileindocker.outputs.out }} - - - compilejobFedora32: - runs-on: ubuntu-latest - name: Binder_on_Fedora32 - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Compile - id: compileindocker - uses: ./.github/workflows/fedora32 - - name: Get the output status - run: exit ${{ steps.compileindocker.outputs.out }} - - - compilejobFedora33: - runs-on: ubuntu-latest - name: Binder_on_Fedora33 - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Compile - id: compileindocker - uses: ./.github/workflows/fedora33 - - name: Get the output status - run: exit ${{ steps.compileindocker.outputs.out }} - - compilejobFedora34: - runs-on: ubuntu-latest - name: Binder_on_Fedora34 - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Compile - id: compileindocker - uses: ./.github/workflows/fedora34 - - name: Get the output status - run: exit ${{ steps.compileindocker.outputs.out }} - - compilejobOSX: runs-on: macos-latest name: Binder_on_OSX steps: - name: Checkout - uses: actions/checkout@v2 - - name: Compile - id: compile - run: ./.github/workflows/osx/entrypoint.sh - - name: Get the output status - run: exit ${{ steps.compile.outputs.out }} \ No newline at end of file + uses: actions/checkout@v3 + - name: Install + run: | + set -x + brew install wget coreutils xz pybind11 git + wget --no-verbose https://github.com/llvm/llvm-project/releases/download/llvmorg-11.0.0/clang+llvm-11.0.0-x86_64-apple-darwin.tar.xz + ls + tar -xJf clang+llvm-11.0.0-x86_64-apple-darwin.tar.xz + export PATH=$PATH:clang+llvm-11.0.0-x86_64-apple-darwin/bin + - name: Compile + run: | + export PATH=$PATH:clang+llvm-11.0.0-x86_64-apple-darwin/bin + cmake CMakeLists.txt + make + make install + otool -L source/binder + ctest . --output-on-failure + diff --git a/.github/workflows/osx/entrypoint.sh b/.github/workflows/osx/entrypoint.sh deleted file mode 100755 index f32cd528..00000000 --- a/.github/workflows/osx/entrypoint.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -l -set -x -brew install wget coreutils xz pybind11 -wget --no-verbose https://github.com/llvm/llvm-project/releases/download/llvmorg-11.0.0/clang+llvm-11.0.0-x86_64-apple-darwin.tar.xz -ls -tar -xJf clang+llvm-11.0.0-x86_64-apple-darwin.tar.xz -export PATH=$PATH:clang+llvm-11.0.0-x86_64-apple-darwin/bin - -cmake CMakeLists.txt -make -otool -L source/binder -ctest . --output-on-failure -cat Testing/Temporary/LastTest.log diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 00000000..90257c1c --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,18 @@ +# Read the Docs configuration file for Sphinx projects +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +version: 2 + +build: + os: ubuntu-22.04 + tools: + python: "2.7" + +sphinx: + configuration: documentation/conf.py + +formats: all + +python: + install: + - requirements: documentation/requirements.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index f90ff59c..ed5546cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,8 @@ #This file can be used to build binder using preinstalled LLVM/Clang CMAKE_MINIMUM_REQUIRED(VERSION 2.8.0) PROJECT(binder CXX C) -SET(VERSION 1.0.0) +SET(VERSION 1.4.1) option(STATIC "Statically compile Binder. See `documentation/install.rst` for more information." OFF) -#This actually will work even with C++11 -set(CMAKE_CXX_STANDARD 14) set(CMAKE_VERBOSE_MAKEFILE ON) #So far there are exceptions in config.cpp set(LLVM_REQUIRES_EH ON) @@ -12,7 +10,14 @@ set(USE_EXTERNAL_LLVM ON) include("GNUInstallDirs") set(LLVM_DIR_ORIG ${LLVM_DIR}) set(Clang_DIR_ORIG ${Clang_DIR}) -set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/Modules ${CMAKE_MODULE_PATH}) +# cmake version >= 3.19 includes all our modules +if(${CMAKE_VERSION} VERSION_LESS "3.19") + set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/Modules ${CMAKE_MODULE_PATH}) +else() + # set CMP0054 to NEW + # https://cmake.org/cmake/help/latest/policy/CMP0054.html + cmake_policy(SET CMP0054 NEW) +endif() SET(CMAKE_MODULE_PATH_ORIG "${CMAKE_MODULE_PATH}") set (CMAKE_MODULE_PATH "${LLVM_DIR};${Clang_DIR};${CMAKE_MODULE_PATH}") option(BINDER_ENABLE_TEST "Enables building of tests" ON) @@ -29,7 +34,7 @@ MESSAGE(STATUS "binder: Using CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH} to search find_package(Clang CONFIG QUIET PATHS /usr/lib64/cmake/clang /usr/lib/cmake/clang /usr/share/clang/cmake ) find_package(LLVM CONFIG QUIET PATHS /usr/lib64/cmake/clang /usr/lib/cmake/llvm /usr/share/llvm/cmake ) if (Clang_FOUND AND LLVM_FOUND AND NOT LLVMCONFIG ) - set(CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR};${CMAKE_MODULE_PATH}") + set(CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR};${CMAKE_MODULE_PATH}") MESSAGE(STATUS "binder: cmake configurations llvm and clang were found. LLVM_CMAKE_DIR=${LLVM_CMAKE_DIR}") include(AddLLVM) include(LLVMConfig) @@ -41,6 +46,11 @@ if (Clang_FOUND AND LLVM_FOUND AND NOT LLVMCONFIG ) ${LLVM_LIBRARY_DIR}/../../lib/clang/${LLVM_VERSION}/include ${LLVM_LIBRARY_DIR}/../../lib64/clang/${LLVM_VERSION}/include ${CLANG_INCLUDE_DIRS}/clang/${LLVM_VERSION}/include + ${LLVM_LIBRARY_DIR}/clang/${LLVM_VERSION_MAJOR}/include + ${LLVM_LIBRARY_DIR}/../clang/${LLVM_VERSION_MAJOR}/include + ${LLVM_LIBRARY_DIR}/../../lib/clang/${LLVM_VERSION_MAJOR}/include + ${LLVM_LIBRARY_DIR}/../../lib64/clang/${LLVM_VERSION_MAJOR}/include + ${CLANG_INCLUDE_DIRS}/clang/${LLVM_VERSION_MAJOR}/include ${LLVM_LIBRARY_DIR}/clang/${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}/include ${LLVM_LIBRARY_DIR}/../clang/${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}/include ${LLVM_LIBRARY_DIR}/../../lib/clang/${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}/include @@ -113,6 +123,12 @@ MESSAGE(STATUS "binder: LibClang_INCLUDE_DIR, the location of headers is ${LibC MESSAGE(STATUS "binder: LLVM_VERSION_MAJOR=${LLVM_VERSION_MAJOR}") MESSAGE(STATUS "binder: LLVM_VERSION_MINOR=${LLVM_VERSION_MINOR}") MESSAGE(STATUS "binder: LLVM_VERSION_PATCH=${LLVM_VERSION_PATCH}") +if( ${LLVM_VERSION_MAJOR} GREATER_EQUAL 16 ) +# LLVM 16 introduces the use of C++ features from C++17 +set(CMAKE_CXX_STANDARD 17) +else() +set(CMAKE_CXX_STANDARD 14) +endif() macro(add_clang_executable name) add_executable( ${name} ${ARGN} ) # set_target_properties(${name} PROPERTIES FOLDER "Clang executables") @@ -126,7 +142,7 @@ endmacro(add_clang_executable) include_directories(source) add_subdirectory(source) if (BINDER_ENABLE_TEST) - if(${CMAKE_VERSION} VERSION_LESS "3.0.0") + if(${CMAKE_VERSION} VERSION_LESS "3.0.0") message("You are running cmake version ${CMAKE_VERSION}.") message("The testing suite will be disabled as it requires cmake 3.0.0 or higher.") else() diff --git a/build-and-run-tests.py b/build-and-run-tests.py index 9cfa34e6..918bbabf 100755 --- a/build-and-run-tests.py +++ b/build-and-run-tests.py @@ -42,7 +42,7 @@ def main(args): source_path = os.path.abspath('.') - if not Options.binder: Options.binder = build.install_llvm_tool('binder', source_path+'/source', source_path + '/build', Options.binder_debug, jobs=Options.jobs, gcc_install_prefix=Options.gcc_install_prefix) + if not Options.binder: Options.binder = build.install_llvm_tool('binder', source_path+'/source', source_path + '/build', Options.binder_debug, jobs=Options.jobs, gcc_install_prefix=Options.gcc_install_prefix, compiler=Options.compiler) if not Options.pybind11: Options.pybind11 = build.install_pybind11(source_path + '/build') diff --git a/build.py b/build.py index 32ce9bb6..6e7e89cf 100644 --- a/build.py +++ b/build.py @@ -14,7 +14,7 @@ from __future__ import print_function -import os, sys, argparse, platform, subprocess, imp, shutil, distutils.dir_util +import os, sys, argparse, platform, subprocess, imp, shutil, distutils.dir_util, json from collections import OrderedDict @@ -29,11 +29,11 @@ _python_version_ = '{}.{}'.format(sys.version_info.major, sys.version_info.minor) # should be formatted: 2.7, 3.5, 3.6, ... -_pybind11_version_ = '35045eeef8969b7b446c64b192502ac1cbf7c451' +_pybind11_version_ = 'aa304c9c7d725ffb9d10af08a3b34cb372307020' -def execute(message, command_line, return_='status', until_successes=False, terminate_on_failure=True, silent=False): - print(message); print(command_line) +def execute(message, command_line, return_='status', until_successes=False, terminate_on_failure=True, silent=False, silence_output=False): + if not silent: print(message); print(command_line); sys.stdout.flush(); while True: p = subprocess.Popen(command_line, bufsize=0, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) @@ -41,18 +41,18 @@ def execute(message, command_line, return_='status', until_successes=False, term output = output + errors - if sys.version_info[0] == 2: output = output.decode('utf-8', errors='replace').encode('utf-8', 'replace') # Python-2 - else: output = output.decode('utf-8', errors='replace') # Python-3 + output = output.decode(encoding="utf-8", errors="replace") exit_code = p.returncode - if exit_code or not silent: print(output) + if exit_code or not (silent or silence_output): print(output); sys.stdout.flush(); if exit_code and until_successes: pass # Thats right - redability COUNT! else: break print( "Error while executing {}: {}\n".format(message, output) ) print("Sleeping 60s... then I will retry...") + sys.stdout.flush(); time.sleep(60) if return_ == 'tuple': return(exit_code, output) @@ -81,43 +81,118 @@ def get_compiler_family(): return 'unknown' -def install_llvm_tool(name, source_location, prefix, debug, jobs=1, clean=True, gcc_install_prefix=None): +def get_cmake_compiler_options(compiler): + ''' Get cmake compiler flags from Options.compiler ''' + if Platform == "linux" and compiler == 'clang': return ' -DCMAKE_C_COMPILER=`which clang` -DCMAKE_CXX_COMPILER=`which clang++`' + if Platform == "linux" and compiler == 'gcc': return ' -DCMAKE_C_COMPILER=`which gcc` -DCMAKE_CXX_COMPILER=`which g++`' + + return '' + + +def install_llvm_tool(name, source_location, prefix_root, debug, compiler, jobs, gcc_install_prefix, clean=True): ''' Install and update (if needed) custom LLVM tool at given prefix (from config). Return absolute path to executable on success and terminate with error on failure ''' - if not os.path.isdir(prefix): os.makedirs(prefix) + if not os.path.isdir(prefix_root): os.makedirs(prefix_root) - llvm_version='6.0.1' - prefix += '/llvm-' + llvm_version - clang_path = "{prefix}/tools/clang".format(**locals()) + # llvm_version='9.0.0' # v8 and v9 can not be build with Clang-3.4, we if need upgrade to v > 7 then we should probably dynamicly change LLVM version based on complier versions + # llvm_version='7.1.0' # compiling v7.* on clang-3.4 lead to lockup while compiling tools/clang/lib/Sema/SemaChecking.cpp + llvm_version, headers = ('13.0.0', 'tools/clang/lib/Headers/clang-resource-headers clang') if Platform == 'macos' and platform.machine() == 'arm64' else ('6.0.1', 'tools/clang/lib/Headers/clang-headers') + #llvm_version, headers = ('13.0.0', 'tools/clang/lib/Headers/clang-resource-headers clang') if Platform == 'macos' else ('6.0.1', 'tools/clang/lib/Headers/clang-headers') + #llvm_version, headers = ('13.0.0', 'tools/clang/lib/Headers/clang-resource-headers clang') + #if Platform == 'macos': headers += ' clang' - if not os.path.isfile(prefix + '/CMakeLists.txt'): execute('Download llvm source.', 'curl https://releases.llvm.org/{llvm_version}/llvm-{llvm_version}.src.tar.xz | tar -Jxo && mv llvm-{llvm_version}.src {prefix}'.format(llvm_version=llvm_version, prefix=prefix) ) + prefix = prefix_root + '/llvm-' + llvm_version - if not os.path.isdir(clang_path): execute('Download clang source.', 'curl https://releases.llvm.org/{llvm_version}/cfe-{llvm_version}.src.tar.xz | tar -Jxo && mv cfe-{llvm_version}.src {clang_path}'.format(llvm_version=llvm_version, clang_path=clang_path) ) + build_dir = prefix+'/llvm-' + llvm_version + '.' + platform.platform() + ('.debug' if debug else '.release') # + '.' + _machine_name_ - if not os.path.isdir(prefix+'/tools/clang/tools/extra'): os.makedirs(prefix+'/tools/clang/tools/extra') + res, output = execute('Getting binder HEAD commit SHA1...', 'cd {} && git rev-parse HEAD'.format(source_location), return_='tuple', silent=True) + if res: binder_head = 'unknown' + else: binder_head = output.split('\n')[0] - tool_link_path = '{prefix}/tools/clang/tools/extra/{name}'.format(prefix=prefix, name=name) - if os.path.islink(tool_link_path): os.unlink(tool_link_path) - os.symlink(source_location, tool_link_path) + signature = dict(config = 'LLVM install by install_llvm_tool version: 1.5.1, HTTPS', binder = binder_head, llvm_version=llvm_version, compiler=compiler, gcc_install_prefix=gcc_install_prefix) + signature_file_name = build_dir + '/.signature.json' - cmake_lists = prefix + '/tools/clang/tools/extra/CMakeLists.txt' - tool_build_line = 'add_subdirectory({})'.format(name) + disk_signature = dict(config = 'unknown', binder = 'unknown') + if os.path.isfile(signature_file_name): + with open(signature_file_name) as f: disk_signature = json.load(f) - if not os.path.isfile(cmake_lists): - with open(cmake_lists, 'w') as f: f.write(tool_build_line + '\n') + if signature == disk_signature: + print('LLVM:{} + Binder install is detected at {}, skipping LLVM installation and Binder building procedures...\n'.format(llvm_version, build_dir)) - build_dir = prefix+'/build_' + llvm_version + '.' + Platform + '.' +_machine_name_ + ('.debug' if debug else '.release') - if not os.path.isdir(build_dir): os.makedirs(build_dir) - execute( - 'Building tool: {}...'.format(name), - 'cd {build_dir} && cmake -G Ninja -DCMAKE_BUILD_TYPE={build_type} -DLLVM_ENABLE_EH=1 -DLLVM_ENABLE_RTTI=ON {gcc_install_prefix} .. && ninja binder tools/clang/lib/Headers/clang-headers {jobs}'.format( # was 'binder clang', we need to build Clang so lib/clang//include is also built - build_dir=build_dir, - jobs="-j{}".format(jobs) if jobs else "", - build_type='Debug' if debug else 'Release', - gcc_install_prefix='-DGCC_INSTALL_PREFIX='+gcc_install_prefix if gcc_install_prefix else ''), - silent=True) - print() + else: + print('LLVM build detected, but config/binder version has changed, perfoming a clean rebuild...') + if os.path.isdir(build_dir): shutil.rmtree(build_dir) + + clang_path = "{prefix}/tools/clang".format(**locals()) + + llvm_url, clang_url = { + '6.0.1' : ('https://releases.llvm.org/6.0.1/llvm-6.0.1.src.tar.xz', 'https://releases.llvm.org/6.0.1/cfe-6.0.1.src.tar.xz'), + '13.0.0' : ('https://github.com/llvm/llvm-project/releases/download/llvmorg-13.0.0/llvm-13.0.0.src.tar.xz', 'https://github.com/llvm/llvm-project/releases/download/llvmorg-13.0.0/clang-13.0.0.src.tar.xz'), + }[llvm_version] + + if not os.path.isfile(prefix + '/CMakeLists.txt'): + #execute('Download LLVM source...', 'cd {prefix_root} && curl https://releases.llvm.org/{llvm_version}/llvm-{llvm_version}.src.tar.xz | tar -Jxom && mv llvm-{llvm_version}.src {prefix}'.format(**locals()) ) + execute('Download LLVM source...', 'cd {prefix_root} && mkdir llvm-{llvm_version}.src && curl -LJ {llvm_url} | tar --strip-components=1 -Jxom -C llvm-{llvm_version}.src && mv llvm-{llvm_version}.src {prefix}'.format(**locals()) ) + + if not os.path.isdir(clang_path): + #execute('Download Clang source...', 'cd {prefix_root} && curl https://releases.llvm.org/{llvm_version}/cfe-{llvm_version}.src.tar.xz | tar -Jxom && mv cfe-{llvm_version}.src {clang_path}'.format(**locals()) ) + execute('Download Clang source...', 'cd {prefix_root} && mkdir clang-{llvm_version}.src && curl -LJ {clang_url} | tar --strip-components=1 -Jxom -C clang-{llvm_version}.src && mv clang-{llvm_version}.src {clang_path}'.format(**locals()) ) + + if not os.path.isdir(prefix+'/tools/clang/tools/extra'): os.makedirs(prefix+'/tools/clang/tools/extra') + + + # if signature['config'] != disk_signature['config']: + # print( 'LLVM build detected, but config version mismatched: was:"{}" current:"{}", perfoming a clean rebuild...'.format(disk_signature['config'], signature['config']) ) + # if os.path.isdir(build_dir): shutil.rmtree(build_dir) + # else: print( 'Binder build detected, but source version mismatched: was:{} current:{}, rebuilding...'.format(disk_signature['binder'], signature['binder']) ) + ''' + git_checkout = '( git checkout {0} && git reset --hard {0} )'.format(release) if clean else 'git checkout {}'.format(release) + + #if os.path.isdir(prefix) and (not os.path.isdir(prefix+'/.git')): shutil.rmtree(prefix) # removing old style checkoiut + + if not os.path.isdir(prefix): + print( 'No LLVM:{} + Binder install is detected! Going to check out LLVM and install Binder. This procedure will require ~1Gb of free disk space and will only be needed to be done once...\n'.format(release) ) + os.makedirs(prefix) + + if not os.path.isdir(prefix+'/.git'): execute('Clonning llvm...', 'cd {} && git clone git@github.com:llvm-mirror/llvm.git .'.format(prefix) ) + execute('Checking out LLVM revision: {}...'.format(release), 'cd {prefix} && ( {git_checkout} || ( git fetch && {git_checkout} ) )'.format(prefix=prefix, git_checkout=git_checkout) ) + + if not os.path.isdir(prefix+'/tools/clang'): execute('Clonning clang...', 'cd {}/tools && git clone git@github.com:llvm-mirror/clang.git clang'.format(prefix) ) + execute('Checking out Clang revision: {}...'.format(release), 'cd {prefix}/tools/clang && ( {git_checkout} || ( git fetch && {git_checkout} ) )'.format(prefix=prefix, git_checkout=git_checkout) ) + + if not os.path.isdir(prefix+'/tools/clang/tools/extra'): os.makedirs(prefix+'/tools/clang/tools/extra') + ''' + + tool_link_path = '{prefix}/tools/clang/tools/extra/{name}'.format(prefix=prefix, name=name) + if os.path.islink(tool_link_path): os.unlink(tool_link_path) + os.symlink(source_location, tool_link_path) + + cmake_lists = prefix + '/tools/clang/tools/extra/CMakeLists.txt' + tool_build_line = 'add_subdirectory({})'.format(name) + + if not os.path.isfile(cmake_lists): + with open(cmake_lists, 'w') as f: f.write(tool_build_line + '\n') + + config = '-DCMAKE_BUILD_TYPE={build_type}'.format(build_type='Debug' if debug else 'Release') + config += get_cmake_compiler_options(compiler) + + if not os.path.isdir(build_dir): os.makedirs(build_dir) + execute( + 'Building tool: {}...'.format(name), # -DLLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN=1 + 'cd {build_dir} && cmake -G Ninja {config} -DLLVM_ENABLE_EH=1 -DLLVM_ENABLE_RTTI=ON {gcc_install_prefix} .. && ninja binder {headers} {jobs}'.format( # was 'binder clang', we need to build Clang so lib/clang//include is also built + build_dir=build_dir, config=config, + jobs=f'-j{jobs}' if jobs else '', + gcc_install_prefix='-DGCC_INSTALL_PREFIX='+gcc_install_prefix if gcc_install_prefix else '', + headers=headers, + ), + silence_output=True) + print() + # build_dir = prefix+'/build-ninja-' + release + # if not os.path.isdir(build_dir): os.makedirs(build_dir) + # execute('Building tool: {}...'.format(name), 'cd {build_dir} && cmake -DCMAKE_BUILD_TYPE={build_type} .. -G Ninja && ninja -j{jobs}'.format(build_dir=build_dir, jobs=Options.jobs, build_type='Debug' if debug else 'Release')) ) + + with open(signature_file_name, 'w') as f: json.dump(signature, f, sort_keys=True, indent=2) executable = build_dir + '/bin/' + name if not os.path.isfile(executable): print("\nEncounter error while running install_llvm_tool: Build is complete but executable {} is not there!!!".format(executable) ); sys.exit(1) @@ -163,7 +238,7 @@ def main(args): source_path = os.path.abspath('.') - if not Options.binder: Options.binder = install_llvm_tool('binder', source_path+'/source', source_path + '/build', Options.binder_debug, jobs=Options.jobs) + if not Options.binder: Options.binder = install_llvm_tool('binder', source_path+'/source', source_path + '/build', debug=Options.binder_debug, compiler=Options.compiler, jobs=Options.jobs, gcc_install_prefix=None) if not Options.pybind11: Options.pybind11 = install_pybind11(source_path + '/build') diff --git a/cmake/Modules/FindPython/Support.cmake b/cmake/Modules/FindPython/Support.cmake index 08f07fda..8df7576c 100644 --- a/cmake/Modules/FindPython/Support.cmake +++ b/cmake/Modules/FindPython/Support.cmake @@ -17,7 +17,7 @@ if (NOT DEFINED _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) message (FATAL_ERROR "FindPython: INTERNAL ERROR") endif() if (_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR EQUAL 3) - set(_${_PYTHON_PREFIX}_VERSIONS 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0) + set(_${_PYTHON_PREFIX}_VERSIONS 3.12 3.11 3.10 3.9 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0) elseif (_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR EQUAL 2) set(_${_PYTHON_PREFIX}_VERSIONS 2.7 2.6 2.5 2.4 2.3 2.2 2.1 2.0) else() @@ -259,7 +259,7 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) # try more generic names if (NOT ${_PYTHON_PREFIX}_EXECUTABLE) find_program (${_PYTHON_PREFIX}_EXECUTABLE - NAMES python${${_PYTHON_PREFIX}_VERSION_MAJOR} python + NAMES python${${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} python ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES} HINTS ${_${_PYTHON_PREFIX}_HINTS} PATH_SUFFIXES bin) diff --git a/documentation/basics.rst b/documentation/basics.rst index 045d86ba..ea1485d8 100644 --- a/documentation/basics.rst +++ b/documentation/basics.rst @@ -38,7 +38,7 @@ header files from our project. For example: suppose that our C++ project contain .. Note:: Make sure to specify complete-relative-to-project-root path to includes and use ``#include `` and avoid using ``#include "file"`` form. That - way Binder will be able to determine correct include paths for each include which is essential for generating correct include sets on step 2. + way Binder will be able to determine correct include paths for each include which is essential for generating correct include sets on step 2. To bulk-convert quote-includes to angle-bracker-include please see https://github.com/shakfu/header_utils project. For small projects such file could be simply typed by-hands and for large project it might be more practical to use a scripts to diff --git a/documentation/config.rst b/documentation/config.rst index 02a28cd0..7d2caa90 100644 --- a/documentation/config.rst +++ b/documentation/config.rst @@ -33,17 +33,22 @@ files. Typically the following files will be generated: ``.cpp``, ` ``--flat`` if specified instruct Binder to write generate code files into single directory. Generated files will be named as ``.cpp``, ``_1.cpp``, ``_2.cpp``, ... etc. +``--skip-line-number`` if specified prevents Binder from writing the line numbers in the comments to the generated code. + +``--bind-class-template-specialization`` specify if class-template-specialization should be bound by-default ``--suppress-errors`` if the generated bindings codes are correct but there are some fatal errors from clang and you want to get rid of them. This situation can happen when you would like to generate binding codes for a small part of a huge project and the you cannot include all the required header files with ``-I`` to the command. +``--include-pybind11-stl`` "if specified bindings for STL classes in ```` will be used instead of generating custom STL bindings. Note, STL bindings may be overkill and have potential preformance implications if data does not need to be copied between ``C++`` and ``python``. For more information, see `pybind11 STL documentation `_. + + ``--annotate-includes`` [debug] if specified Binder will comment each include with type name which trigger it inclusion. ``--trace`` [debug] if specified instruct Binder to add extra debug output before binding each type. This might be useful when debugging generated code that produce seg-faults during python import. - Config file options =================== @@ -64,6 +69,15 @@ Config file directives: +namespace utility +* ``enum``, specify if particular enum should be bound. Purpose of this directive is to allow developer to cherry-pick + particular enum from otherwise binded/skipped namespaces and mark it for binding/skipping. + +.. code-block:: bash + + -enum utility::pointer::State + +enum protocols::CDR_Type + + * ``class``, specify if particular class/struct should be bound. Purpose of this directive is to allow developer to cherry-pick particular class from otherwise binded/skipped namespaces and mark it for binding/skipping. @@ -73,6 +87,23 @@ Config file directives: -class utility::pointer::ReferenceCount -class std::__weak_ptr +* ``field``, specify if a particular field should be bound. + +.. code-block:: bash + + -field MyClass::some_field + + +* ``python_builtin``, specify if particular class/struct should be considered a python builtin and assume existing bindings for it already exist. + The purpose of this directive is to allow developer to allow developers to toggle if bindings for types like ``std::optional`` or ``pybind11::dict`` should be + generated, or if binder should assume such bindings already exist somewhere else. Alternatively, a developer could declare a type as not-builtin if they + would prefer to force binder to generate bindings for it. Note that removing a builtin (``-python_builtin abc``) always overrides everything else (such as adding a builtin via ``+python_builtin abc``). + +.. code-block:: bash + + -python_builtin std::less + +python_builtin std::vector + * ``function``, specify if particular function should be bound. This could be used for both template and normal function. @@ -131,7 +162,7 @@ Config file directives: * ``+add_on_binder``, similar to ``binder``: specify custom binding function for class/struct that will be called `after` Binder generated code bound it. This allow developer to create extra bindings for particular type (bind special Python methods, - operators, etc.) + operators, etc.) The expected type signature of specified function should be `void f(pybind11::class_ > &)` .. code-block:: bash @@ -143,7 +174,7 @@ Config file directives: * ``+binder_for_namespace``, similar to ``binder``: specify custom binding function for namespace. Call to specified function will be generated - _instead_ of generating bindings for namaspace. + _instead_ of generating bindings for namaspace. Where expected type signature of specified function should be `void f(pybind11::module &)` .. code-block:: bash @@ -183,11 +214,7 @@ Config file directives: * ``default_member_rvalue_reference_return_value_policy``, specify return value policy for member functions returning r-value reference. Default is `pybind11::return_value_policy::automatic`. -* ``default_call_guard``, optionally specify a call guard applied to all function definitions. See `pybind11 documentation `_. Default None. - - - - +* ``default_call_guard``, optionally specify a call guard applied to all function definitions. See `pybind11 documentation `_. Default None. .. code-block:: bash @@ -195,3 +222,35 @@ Config file directives: +default_member_lvalue_reference_return_value_policy pybind11::return_value_policy::reference_internal +default_member_rvalue_reference_return_value_policy pybind11::return_value_policy::move +default_call_guard pybind11::gil_scoped_release + +* ``+custom_shared``: specify a custom shared pointer class that Binder should use instead of ``std::shared_ptr``. + +* ``module_local_namespace``: use to add (or remove) the extra argument module_local to the pybind11 classes and enum of a namespace. This option can be used for all the namaspaces of a given project using `+module_local_namespace @all_namespaces`. + +.. code-block:: bash + + +module_local_namespace @all_namespaces + -module_local_namespace std + +* ``trampoline_member_function_binder``: use to specify a custom trampoline member function defined by the user in a given header file + +.. code-block:: bash + + +include_for_class aaa::A + +trampoline_member_function_binder aaa::A::foo myFoo + + +* ``+prefix_for_static_member_functions``: specify name prefix to use for static member functions, could be useful as workaround Pybind11 limitation restricting having both virtual and static member functions having the same name + +* ``smart_holder``: use to specify that a class requires the usage of the progressive mode of the pybind11 smart_holder branch (https://github.com/pybind/pybind11/tree/smart_holder). As discussed in https://github.com/pybind/pybind11/blob/smart_holder/README_smart_holder.rst, the smart_holder branch is a strict superset of the pybind11 master branch that supports safely passing trampoline objects back to C++: associated Python objects are automatically kept alive for the lifetime of the smart-pointer. This config file directive has been added to fulfil https://github.com/RosettaCommons/binder/issues/263. + +.. code-block:: bash + + +smart_holder example::class + +* ``pybind11_include_file``: use to specify which header file of pybind11 should be included. The header pybind11/pybind11.h is used by default. + +.. code-block:: bash + + +pybind11_include_file pybind11/smart_holder.h + diff --git a/documentation/index.rst b/documentation/index.rst index b017d276..ab5543c1 100644 --- a/documentation/index.rst +++ b/documentation/index.rst @@ -15,6 +15,7 @@ Contents: install basics config + limitations examples debugging testing diff --git a/documentation/install.rst b/documentation/install.rst index 075617e4..c81fd856 100644 --- a/documentation/install.rst +++ b/documentation/install.rst @@ -1,6 +1,6 @@ Installation ============ -**Binder** is written in C++11 and must be built before use. This page describes the steps for the build process. +**Binder** is written in C++11 and must be built before use. This page describes the steps for the build process. Please note that installation require up to ~2.6+ Gb of free disk space. @@ -28,9 +28,9 @@ may not be compatible with the header files on the system Binder where is run. Building ******** -The steps below are encoded in `binder/build.py` and `binder/build-and-run-tests.py` -files so for default install you can just run `build-and-run-tests.py` script directly. -This section describes how to build a dynamically-linked ``binder`` executable. +The steps below are encoded in `binder/build.py` and `binder/build-and-run-tests.py` +files so for default install you can just run `build-and-run-tests.py` script directly. +This section describes how to build a dynamically-linked ``binder`` executable. To *statically* compile binder, see :ref:`building-static`. @@ -46,25 +46,14 @@ To *statically* compile binder, see :ref:`building-static`. mkdir $HOME/prefix && cd $HOME/prefix # Clone LLVM - git clone http://llvm.org/git/llvm.git llvm && cd llvm - git checkout release_60 - - # Clone Clang - cd $HOME/prefix/llvm/tools - git clone http://llvm.org/git/clang.git clang - cd clang && git checkout release_60 - - - # Clone Clang extra tools - cd $HOME/prefix/llvm/tools/clang/tools - git clone http://llvm.org/git/clang-tools-extra.git extra - cd extra && git checkout release_60 + git clone https://github.com/llvm/llvm-project.git llvm && cd llvm + git checkout llvmorg-6.0.1 # Create symlink pointing to binder/src dir - ln -s $HOME/binder/source $HOME/prefix/llvm/tools/clang/tools/extra/binder + ln -s $HOME/binder/source $HOME/prefix/llvm/clang-tools-extra/binder # Create ``llvm/tools/clang/tools/extra/CMakeLists.txt`` file with content: ``add_subdirectory(binder)`` - echo 'add_subdirectory(binder)' > $HOME/prefix/llvm/tools/clang/tools/extra/CMakeLists.txt + echo 'add_subdirectory(binder)' > $HOME/prefix/llvm/clang-tools-extra/CMakeLists.txt # Build Binder mkdir $HOME/prefix/build && cd $HOME/prefix/build @@ -74,66 +63,66 @@ To *statically* compile binder, see :ref:`building-static`. # $HOME/prefix/build/bin/binder -Installation with pre-installed LLVM +Installation with pre-installed LLVM ==================================== Requirements ************ The basic dependencies for this type of installation are very similar to these described above and include: -- CMake, version 3.4.3 or higher from https://cmake.org -- C++ compiler with c++11 support, e.g. gcc from https://gcc.gnu.org/ -- make or Ninja +- CMake, version 3.4.3 or higher from https://cmake.org +- C++ compiler with c++11 support, e.g. gcc from https://gcc.gnu.org/ +- make or Ninja - llvm with development packages (headers) - clang with development packages (headers) The installation process of the required packages varies from system to system. -On the RHEL7/RHEL8/Fedora22+/Ubuntu18+ systems binder can be compiled with the llvm, clang and dependent packages available +On the RHEL7/RHEL8/Fedora22+/Ubuntu18+ systems binder can be compiled with the llvm, clang and dependent packages available for these systems from their default repositories - - + + For RHEL7/RHEL8/Fedora22+: -- To install the needed packages run as root +- To install the needed packages run as root .. code-block:: console - + yum install clang clang-devel llvm-devel llvm-static clang-libs - + - If a newer or specific version of the llvm/clang is needed, it can be installed as root - + .. code-block:: console - + yum install clang8.0 clang8.0-devel llvm8.0-devel llvm8.0-static clang8.0-libs - + to obtain a specific version (8.0 in this case). - -- If the option above is not sufficient, or the available packages are outdated, for the + +- If the option above is not sufficient, or the available packages are outdated, for the CentOS/RHEL/Fedora and compatible systems the llvm-toolset-7.0 toolset (or later) from - https://www.softwarecollections.org/en/scls/rhscl/llvm-toolset-7.0/ provides LLVM of version 7.0. + https://www.softwarecollections.org/en/scls/rhscl/llvm-toolset-7.0/ provides LLVM of version 7.0. To install it run as root - + .. code-block:: console - - yum install llvm-toolset-7.0* - + + yum install llvm-toolset-7.0* + Then the compilation can be performed using the following shell - + .. code-block:: console - + scl enable llvm-toolset-7.0 bash - + - Please note that binder requires cmake of version 3, therefore for some older systems package cmake3 should be installed and used instead of cmake. - + .. code-block:: console - + yum install cmake3 - - + + For Ubuntu18+ run, an example for LLVM/Clang 10: - + .. code-block:: console - + sudo apt-get update sudo apt-get -y install clang-10 llvm-10 libclang-10-dev llvm-10-dev sudo apt-get -y install cmake make gcc g++ @@ -141,22 +130,30 @@ For Ubuntu18+ run, an example for LLVM/Clang 10: For MacOSX: Make sure the XCode is installed. If needed, install cmake, python and other utilities, e.g. using homebrew: - + .. code-block:: console - + brew install wget coreutils xz pybind11 cmake - Note: the pybind11 version from https://github.com/RosettaCommons/pybind11 should be preffered , - but pybind11 version from homebrew might work as well. - - Download and install the llvm+clang from the official site, e.g. using ``wget`` and + Note: the pybind11 version from https://github.com/RosettaCommons/pybind11 should be preffered , + but pybind11 version from homebrew might work as well. + + Download and install the llvm+clang from the official site, e.g. using ``wget`` and add the location of llvm config to the $PATH: .. code-block:: console - - wget --no-verbose https://github.com/llvm/llvm-project/releases/download/llvmorg-11.0.0/clang+llvm-11.0.0-x86_64-apple-darwin.tar.xz - tar -xJf clang+llvm-11.0.0-x86_64-apple-darwin.tar.xz - export PATH=$PATH:$(pwd)/clang+llvm-11.0.0-x86_64-apple-darwin/bin + mkdir -p deps + cd deps + arch=`uname -m` + if [[ "$arch" == "arm64" ]]; then + wget https://github.com/llvm/llvm-project/releases/download/llvmorg-14.0.6/clang+llvm-14.0.6-arm64-apple-darwin22.3.0.tar.xz -O clang_llvm_stock.tar.xz + else + wget https://github.com/llvm/llvm-project/releases/download/llvmorg-11.0.0/clang+llvm-11.0.0-x86_64-apple-darwin.tar.xz -O clang_llvm_stock.tar.xz + fi + tar -xJf clang_llvm_stock.tar.xz + mv clang+llvm-* clang+llvm_stock + # Make sure to add to the END of the path + export PATH="$PATH:$(pwd)/clang+llvm_stock/bin" Building @@ -167,19 +164,20 @@ To build ``binder`` run cmake CMakeLists.txt -DCMAKE_INSTALL_PREFIX:PATH=/home/user/whereiwanttohaveit/ make + ctest make install -To perform the build with a specific version of LLVM, the location of LLVM and CLANG directories +To perform the build with a specific version of LLVM, the location of LLVM and CLANG directories should be set simultaneously via the location of their cmake configurations, i.e. .. code-block:: console - + cmake CMakeLists.txt -DLLVM_DIR=/usr/lib64/llvm8.0/lib/cmake/llvm -DClang_DIR=/usr/lib64/llvm8.0/lib/cmake/clang Alternatively, the location of the llvm-config script could be set. .. code-block:: console - + cmake CMakeLists.txt -DLLVMCONFIG=/usr/lib64/llvm7.0/bin/llvm-config As an example with Ubuntu 18.04 and llvm-10: @@ -193,7 +191,7 @@ Using ``binder`` built with pre-installed LLVM ********************************************** Under some circumstances (e.g. on system where the default compiller is not clang) -``binder`` might emit error messages like +``binder`` might emit error messages like .. code-block:: console @@ -206,7 +204,7 @@ and similar, see https://clang.llvm.org/docs/FAQ.html. To fix this issue, ``bind appropriate clang includes. This can be archived using the clang options that are passed to binder after ``--`` flag, e.g.\ .. code-block:: console - + binder ...binder...options... -- -x c++ ...other...options... -iwithsysroot/where/the/directory/with/includes/is/ See https://clang.llvm.org/docs/ClangCommandLineReference.html for details. @@ -219,21 +217,27 @@ If ``binder`` was build with some older versions of LLVM, one could also set the +With Docker +*********** + +An example `Dockerfile` for building binder can be found in the ``binder`` repository linked here: https://github.com/RosettaCommons/binder/examples + + .. _building-static: Building Statically (Linux only) ******************************** -The first step in the static build is to build the ``libclang`` statically following the instructions -from https://github.com/deech/libclang-static-build. For this quite a recent version of cmake is needed (3.13+). -If the version of cmake form the used distribution is too old (e.g. as in the CentOS8 ) a precompilled +The first step in the static build is to build the ``libclang`` statically following the instructions +from https://github.com/deech/libclang-static-build. For this quite a recent version of cmake is needed (3.13+). +If the version of cmake form the used distribution is too old (e.g. as in the CentOS8 ) a precompilled package from the CMake site from https://cmake.org/ can be used instead. The static build requires some other static libraries to be present in the system. For the CentOS8 install ``libstdc++-static`` and ``ncurses-compat-libs`` runnign as root: .. code-block:: console - + sudo yum install libstdc++-static ncurses-compat-libs @@ -245,5 +249,5 @@ Set the environment variable ``LIBCLANG_STATIC_BUILD_DIR`` to the path of cmake CMakeLists.txt -DSTATIC=on -DLLVMCONFIG="${LIBCLANG_STATIC_BUILD_DIR}/build/_deps/libclang_prebuilt-src/bin/llvm-config" -DLLVM_LIBRARY_DIR="${LIBCLANG_STATIC_BUILD_DIR}/lib" -DCMAKE_INSTALL_PREFIX:PATH=/home/user/whereiwanttohaveit/ make - + ctest make install diff --git a/documentation/limitations.rst b/documentation/limitations.rst new file mode 100644 index 00000000..75dff18e --- /dev/null +++ b/documentation/limitations.rst @@ -0,0 +1,78 @@ +Binder Limitations +################## + +This section lists some of ``binder``'s more prominent limitations. + +------------------ +External Operators +------------------ + +Unlike C++, which allows operators to be defined outside of classes and redefined across different namespaces, python requires operators be member functions and thus lacks the ability to choose which overload to use based on context. +In line with this, ``binder`` will only bind (most) C++ operators if they are member functions (i.e. they cannot be defined externally). + +These operators include, but are not necessarily limited to: + +.. code-block:: console + + operator~ (__invert__) + + operator+ (__add__) + operator- (__sub__) + operator* (__mul__) + operator/ (__div__) + operator% (__mod__) + + operator& (__and__) + operator| (__or__) + operator^ (__xor__) + operator<< (__lshift__) + operator>> (__rshift__) + + operator+= (__iadd__) + operator-= (__isub__) + operator*= (__imul__) + operator/= (__idiv__) + operator%= (__imod__) + + operator&= (__iand__) + operator|= (__ior__) + operator^= (__ixor__) + operator<<= (__ilshift__) + operator>>= (__irshift__) + + operator() (__call__) + operator== (__eq__) + operator!= (__ne__) + operator[] (__getitem__) + operator= (assign) + operator++ (plus_plus) + operator-- (minus_minus) + +----------------- +Ignored Operators +----------------- + +The following operators will be ignored by binder: + +.. code-block:: console + + // Logical + && + || + + // Cast to T + explicit operator T() + operator T() + + // Misc + , + new + new[] + delete + delete[] + +------------- +Miscellaneous +------------- + +1. User defined literals ``operator"" _foo`` end up being named as ``operator_foo``. diff --git a/documentation/requirements.txt b/documentation/requirements.txt new file mode 100644 index 00000000..3acd3cb2 --- /dev/null +++ b/documentation/requirements.txt @@ -0,0 +1,9 @@ +pillow +mock==1.0.1 +alabaster>=0.7,<0.8,!=0.7.5 +commonmark==0.9.1 +recommonmark==0.5.0 +sphinx<2 +sphinx-rtd-theme<0.5 +readthedocs-sphinx-ext<2.3 +jinja2<3.1.0 diff --git a/examples/Dockerfile b/examples/Dockerfile new file mode 100644 index 00000000..7b492813 --- /dev/null +++ b/examples/Dockerfile @@ -0,0 +1,54 @@ +# Config +ARG BRANCH="master" +ARG CLANG_VERSION=14 + + +FROM ubuntu:20.04 as base +ARG BRANCH +ARG CLANG_VERSION + +# General dependencies +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get update \ + && apt-get install -yq wget gnupg \ + && rm -rf /var/lib/apt/lists/* + +# Add llvm repo +RUN wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - +RUN echo "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${CLANG_VERSION} main" >> /etc/apt/sources.list + +# Run dependencies +RUN apt-get update \ + && apt-get install -yq "clang-${CLANG_VERSION}" \ + && rm -rf /var/lib/apt/lists/* + + +# Build binder +FROM base as build +ARG BRANCH +ARG CLANG_VERSION + +# Build dependencies +RUN apt-get update +RUN apt-get install -yq \ + "libclang-${CLANG_VERSION}-dev" \ + cmake \ + git + +# Clone binder source +ARG REPO="https://github.com/RosettaCommons/binder.git" +RUN git clone --depth 1 --branch "${BRANCH}" "${REPO}" /binder + +# Build +WORKDIR "/build" +RUN cmake \ + -DCMAKE_CXX_COMPILER="$(which clang++-"${CLANG_VERSION}")" \ + -DBINDER_ENABLE_TEST=OFF \ + /binder +RUN make "-j$(nproc)" +RUN make install + + +# Install image +FROM base as install +COPY --from=build /usr/local/bin/binder /usr/local/bin/binder diff --git a/examples/example_struct/include/test_struct/test_struct.hpp b/examples/example_struct/include/test_struct/test_struct.hpp index cecd6619..9763de31 100644 --- a/examples/example_struct/include/test_struct/test_struct.hpp +++ b/examples/example_struct/include/test_struct/test_struct.hpp @@ -7,35 +7,27 @@ #include namespace testers { -struct test_my_struct { +struct test_my_struct +{ int an_int; std::string a_string; std::vector a_vector; float a_float; - test_my_struct() { + test_my_struct() + { an_int = 27; a_string = "TEST_STRING"; - a_vector = std::vector{1,2,3,4,5}; + a_vector = std::vector{1, 2, 3, 4, 5}; a_float = 88.88; } - void - increment_int() { - ++an_int; - } - - void - add_float() { - a_float += 22.22; - } + void increment_int() { ++an_int; } - void - append_vec() { - a_vector.push_back(a_vector.back()+1); - } + void add_float() { a_float += 22.22; } + void append_vec() { a_vector.push_back(a_vector.back() + 1); } }; -} +} // namespace testers #endif diff --git a/examples/example_struct/reference_all_includes.hpp b/examples/example_struct/reference_all_includes.hpp index fe2f5af0..2adf5a05 100644 --- a/examples/example_struct/reference_all_includes.hpp +++ b/examples/example_struct/reference_all_includes.hpp @@ -1,5 +1,5 @@ #include -#include #include +#include #include diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index b20a5879..ab6699ff 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -6,8 +6,10 @@ set(LLVM_LINK_COMPONENTS support) set(LLVM_USED_LIBS clangTooling clangBasic clangAST) add_clang_executable(binder - binder.cpp + main.cpp + binder.hpp + binder.cpp context.hpp context.cpp @@ -27,6 +29,9 @@ add_clang_executable(binder function.hpp function.cpp + options.hpp + options.cpp + type.hpp type.cpp @@ -34,8 +39,14 @@ add_clang_executable(binder util.cpp fmt/format.cc - fmt/posix.cc + fmt/format.h + fmt/os.cc + fmt/os.h ) +if (NOT VERSION) + set(VERSION 1.4.1) +endif() +target_compile_definitions(binder PUBLIC BINDER_VERSION_STRING=\"${VERSION}\") if(USE_EXTERNAL_LLVM) if(STATIC) find_library(lib_llvm_path NAMES libclang_static_bundled.a @@ -62,11 +73,11 @@ if(USE_EXTERNAL_LLVM) message(STATUS "binder: lib_llvm_path=${lib_llvm_path}") message(STATUS "binder: CMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}") if ( (LLVM_VERSION_MAJOR GREATER 3) AND (LLVM_VERSION_MAJOR LESS 8) ) - target_link_libraries(binder + target_link_libraries(binder PRIVATE ${lib_llvm_path} clangAST - clangBasic + clangBasic clangFrontend clangTooling ) @@ -115,6 +126,6 @@ else() clangFrontend ) endif() -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/binder -PERMISSIONS WORLD_EXECUTE WORLD_READ OWNER_WRITE OWNER_READ OWNER_EXECUTE +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/binder +PERMISSIONS WORLD_EXECUTE WORLD_READ OWNER_WRITE OWNER_READ OWNER_EXECUTE DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/source/binder.cpp b/source/binder.cpp index e8721a95..57919940 100644 --- a/source/binder.cpp +++ b/source/binder.cpp @@ -7,17 +7,12 @@ // MIT license that can be found in the LICENSE file. /// @file binder/binder.cpp -/// @brief Main +/// @brief Classes IncludeSet and Binder /// @author Sergey Lyskov #include -// Declares clang::SyntaxOnlyAction. -#include "clang/AST/ASTConsumer.h" -#include "clang/AST/ASTContext.h" -#include "clang/Frontend/FrontendActions.h" -#include "clang/Tooling/CommonOptionsParser.h" -#include "clang/Tooling/Tooling.h" +#include // is_python_builtin #include #include @@ -56,59 +51,70 @@ using binder::Config; using namespace clang; using std::string; +using std::vector; +namespace binder { -cl::opt O_root_module("root-module", cl::desc("Name of root module"), /*cl::init("example"),*/ cl::cat(BinderToolCategory)); +// const std::string _module_variable_name_{"M"}; -cl::opt O_max_file_size("max-file-size", cl::desc("Specify maximum length of generated source files"), cl::init(1024*16), cl::cat(BinderToolCategory)); +// check if declaration is already in stack with level at least as 'level' or lower and add it if it is not - return true if declaration was added -cl::opt O_prefix("prefix", cl::desc("Output path for all generated files. Specified path must exists."), cl::init(""), cl::cat(BinderToolCategory)); - -cl::list O_bind("bind", cl::desc("Namespace to bind, could be specified more then once. Specify \"\" to bind all namespaces."), cl::cat(BinderToolCategory)); // , cl::OneOrMore -cl::list O_skip("skip", cl::desc("Namespace to skip, could be specified more then once"), cl::cat(BinderToolCategory)); // , cl::OneOrMore - -cl::opt O_config("config", cl::desc("Specify config file from which bindings setting will be read"), cl::init(""), cl::cat(BinderToolCategory)); - -cl::opt O_annotate_includes("annotate-includes", cl::desc("Annotate each includes in generated code with type name that trigger it inclusion"), cl::init(false), cl::cat(BinderToolCategory)); - -cl::opt O_single_file("single-file", cl::desc("Concatenate all binder output into single file with name: root-module-name + '.cpp'. Use this for a small projects and for testing."), cl::init(false), cl::cat(BinderToolCategory)); +bool IncludeSet::add_decl(clang::NamedDecl const *D, int level) +{ + auto it_inserted = stack_.insert( {D, level} ); + auto & it = it_inserted.first; + auto & inserted = it_inserted.second; + if(inserted) return true; + else { + if( it->second <= level ) return false; + else { + it->second = level; + //it.value() = level; + return true; + } + } -cl::opt O_trace("trace", cl::desc("Add tracer output for each binded object (i.e. for debugging)"), cl::init(false), cl::cat(BinderToolCategory)); + // { + // std::unordered_map mp; + // //tsl::robin_map mp; + // mp[0] = 0; + // mp.find(0)->second = 1; + // mp.insert({0, 1}).first->second = 2; + // } -cl::opt O_verbose("v", cl::desc("Increase verbosity of output"), cl::init(false), cl::cat(BinderToolCategory)); -cl::opt O_suppress_errors("suppress-errors", cl::desc("Suppres all the compilers errors. This option could be useful when you want to tell Binder to ignore non-critical errors (for example due to missing includes) and generate binding for part of code that Binder was able to parse"), cl::init(false), cl::cat(BinderToolCategory)); + // StackType::iterator it; + // bool inserted; + // std::tie(it, inserted) = stack_.insert( StackType::value_type(D, level) ); -cl::opt O_flat("flat", cl::desc("When specified generated files into single directory. Generated files will be named as .cpp, _1.cpp, _2.cpp, ... etc."), cl::init(false), cl::cat(BinderToolCategory)); -class ClassVisitor : public RecursiveASTVisitor -{ -public: - explicit ClassVisitor(DeclContext *dc) /*: decl_context(dc)*/ {} - - virtual ~ClassVisitor() {} + // auto it = stack_.find(D); + // if( it == stack_.end() ) { + // stack_[D] = level; + // return true; + // } + // else { + // if( it->second <= level ) return false; + // else { + // it->second = level; + // return true; + // } + // } - virtual bool VisitEnumDecl(EnumDecl *record) { - errs() << "ClassVisitor EnumDecl: " << record->getQualifiedNameAsString() << "\n"; - record->dump(); - return true; - } + // auto &l = stack_[D]; + // if( l.value and l.value <= level.value ) return false; + // l = level; + // return true; -private: - //DeclContext *decl_context; -}; - -string wrap_CXXRecordDecl(CXXRecordDecl *R) -{ - ClassVisitor v{R}; - v.TraverseDecl( R ); - //R->dump(); - return ""; + // if( stack_.count(D) and stack_[D] <= level ) return false; + // stack_[D] = level; + // return true; } - -class BinderVisitor : public RecursiveASTVisitor +// remove all includes and clear up the stack +void IncludeSet::clear() { +<<<<<<< HEAD public: explicit BinderVisitor(CompilerInstance *ci) : ast_context( &( ci->getASTContext() ) ) { @@ -265,4 +271,38 @@ int main(int argc, const char **argv) //for(auto &s : O_bind) outs() << "Binding: '" << s << "'\n"; return tool.run(newFrontendActionFactory().get()); +======= + includes_.clear(); + stack_.clear(); +>>>>>>> origin/master +} + +/// return true if object declared in system header +bool Binder::is_in_system_header() +{ + using namespace clang; + NamedDecl const *decl(named_decl()); + ASTContext &ast_context(decl->getASTContext()); + SourceManager &sm(ast_context.getSourceManager()); + + return FullSourceLoc(decl->getLocation(), sm).isInSystemHeader(); +} + +// return true if code was already generate for this object +bool Binder::is_binded() const +{ + return code().size() or is_python_builtin(named_decl()); +} + +llvm::raw_ostream &operator<<(llvm::raw_ostream &os, Binder const &b) +{ + clang::NamedDecl const *decl = b.named_decl(); + + string name = decl->getNameAsString(); + string qualified_name = decl->getQualifiedNameAsString(); + string path = decl->getQualifiedNameAsString().substr(0, qualified_name.size() - name.size()); + + return os << "B{name=" << name << ", path=" << path << "\n"; //<< ", include= " code=\n" << b("module") << "\n} } + +} // namespace binder diff --git a/source/binder.hpp b/source/binder.hpp index e044c652..cbd8614a 100644 --- a/source/binder.hpp +++ b/source/binder.hpp @@ -7,24 +7,130 @@ // MIT license that can be found in the LICENSE file. /// @file binder/binder.hpp -/// @brief Options +/// @brief Classes IncludeSet and Binder /// @author Sergey Lyskov - #ifndef _INCLUDED_binder_hpp_ #define _INCLUDED_binder_hpp_ +#include +#include -#include "llvm/Config/llvm-config.h" -#include "llvm/Support/CommandLine.h" +#include +#include +//#include -extern llvm::cl::opt O_annotate_includes; -extern llvm::cl::opt O_single_file; -extern llvm::cl::opt O_trace; -extern llvm::cl::opt O_verbose; -extern llvm::cl::opt O_flat; +#include +#include +#include namespace binder { + +class Config; +class Context; + +// structure to hold include set information and set of NamedDecl objects that was already queried for includes +class IncludeSet +{ + +public: + // add include to the set + void add_include(std::string const &i) { includes_.push_back(i); } + + // check if declaration is already in stack with level at lease as 'level' or lower and add it if it is not - return true if declaration was added + bool add_decl(clang::NamedDecl const *, int level); + + std::vector const &includes() const { return includes_; } + + // remove all includes and clear up the stack + void clear(); + +private: + std::vector includes_; + + //using StackType = std::unordered_map; + using StackType = llvm::DenseMap; + //using StackType = tsl::robin_map; + + StackType stack_; + + friend void add_relevant_includes_cached(clang::CXXRecordDecl const *C, IncludeSet &includes); +}; + + +enum RequestFlags : int8_t { + none=0, skipping = 1, binding = 2, +}; +inline RequestFlags operator|(RequestFlags a, RequestFlags b) { return static_cast(static_cast(a) | static_cast(b)); } +inline RequestFlags operator&(RequestFlags a, RequestFlags b) { return static_cast(static_cast(a) & static_cast(b)); } + +/// Bindings Generator - represent object that can generate binding info for function, class, enum or data variable +class Binder +{ +public: + typedef std::string string; + + virtual ~Binder() {} + + /// Generate string id that uniquely identify C++ binding object. For functions this is function prototype and for classes forward declaration. + virtual string id() const = 0; + + // return Clang AST NamedDecl pointer to original declaration used to create this Binder + virtual clang::NamedDecl const *named_decl() const = 0; + + /// check if generator can create binding + virtual bool bindable() const = 0; + + bool binding_requested() const { return binding_requested_; }; + bool skipping_requested() const { return skipping_requested_; }; + + /// request bindings for this generator + void request_bindings() { binding_requested_ = true; } + + /// request skipping for this generator + void request_skipping() { skipping_requested_ = true; } + + + /// check if user supplied config requested binding for the given declaration and if so request it + virtual void request_bindings_and_skipping(Config const &, RequestFlags flags = RequestFlags::skipping | RequestFlags::binding) = 0; + + /// extract include needed for this generator and add it to includes vector + virtual void add_relevant_includes(IncludeSet &) const = 0; + + /// generate binding code for this object and all its dependencies + virtual void bind(Context &) = 0; + + /// return true if code was already generated for this object + bool is_binded() const; + + /// return binding code + string &code() { return code_; } + string const &code() const { return code_; } + + /// return true if object is declared in system header + bool is_in_system_header(); + + /// return vector of declarations that need to be binded before this one could + virtual std::vector dependencies() const { return std::vector(); } + + /// return prefix portion of bindings code + virtual string prefix_code() const { return string(); } + + /// return unique strting ID for this binder + explicit operator std::string() const { return id(); /*named_decl()->getQualifiedNameAsString();*/ } + +private: + bool binding_requested_ = false, skipping_requested_ = false; + + string code_; +}; + +typedef std::shared_ptr BinderOP; + +typedef std::vector Binders; + +llvm::raw_ostream &operator<<(llvm::raw_ostream &os, Binder const &b); + } // namespace binder #endif // _INCLUDED_binder_hpp_ diff --git a/source/class.cpp b/source/class.cpp index 570306a9..610e078c 100644 --- a/source/class.cpp +++ b/source/class.cpp @@ -10,10 +10,10 @@ /// @brief Binding generation for C++ struct and class objects /// @author Sergey Lyskov -#include #include -#include #include +#include +#include #include #include @@ -26,14 +26,14 @@ using namespace llvm; using namespace clang; -using std::string; +using std::make_pair; using std::pair; +using std::set; +using std::string; using std::tuple; -using std::make_pair; using std::vector; -using std::set; -//using std::unordered_map; +// using std::unordered_map; using namespace fmt::literals; @@ -47,14 +47,17 @@ string template_specialization(clang::CXXRecordDecl const *C) if( auto t = dyn_cast(C) ) { templ += "<"; - for(uint i=0; i < t->getTemplateArgs().size(); ++i) { - //if( t->getTemplateArgs()[i].isInstantiationDependent() ) break; // avoid explicitly specifying SFINAE related arguments + for( uint i = 0; i < t->getTemplateArgs().size(); ++i ) { + // if( t->getTemplateArgs()[i].isInstantiationDependent() ) break; // avoid explicitly specifying SFINAE related arguments - //outs() << " template argument: " << template_argument_to_string(t->getTemplateArgs()[i]) << "\n"; - templ += template_argument_to_string(t->getTemplateArgs()[i]) + ","; + // outs() << " template argument: " << template_argument_to_string(t->getTemplateArgs()[i]) << "\n"; + // templ += template_argument_to_string(t->getTemplateArgs()[i]) + ","; + std::string template_arg = template_argument_to_string(t->getTemplateArgs()[i]); + if ((template_arg[0] == '<') and (template_arg[template_arg.size()-1]=='>')) template_arg = template_arg.substr(1, template_arg.size()-2); + if (template_arg.size()>0) templ += template_arg + ","; - //if( t->getTemplateArgs()[i].ArgKind() == TemplateArgument::ArgKind::Integral ) outs() << " template arg:" << t->getTemplateArgs()[i].<< "\n"; - //outs() << expresion_to_string( t->getTemplateArgs()[i].getAsExpr() ) << "\n"; + // if( t->getTemplateArgs()[i].ArgKind() == TemplateArgument::ArgKind::Integral ) outs() << " template arg:" << t->getTemplateArgs()[i].<< "\n"; + // outs() << expresion_to_string( t->getTemplateArgs()[i].getAsExpr() ) << "\n"; } templ.back() = '>'; } @@ -66,7 +69,7 @@ string template_specialization(clang::CXXRecordDecl const *C) // generate class name that could be used in bindings code indcluding template specialization if any string class_name(CXXRecordDecl const *C) { - string res = standard_name( C->getNameAsString() + template_specialization(C) ); + string res = standard_name(C->getNameAsString() + template_specialization(C)); if( namespace_from_named_decl(C) == "std" ) res = simplify_std_class_name(res); @@ -94,10 +97,8 @@ vector get_type_dependencies(CXXRecordDecl const *C /*, bool include_m vector r; if( auto t = dyn_cast(C) ) { - for(uint i=0; i < t->getTemplateArgs().size(); ++i) { - if( t->getTemplateArgs()[i].getKind() == TemplateArgument::Type ) { - r.push_back( t->getTemplateArgs()[i].getAsType() /*.getDesugaredType(C->getASTContext())*/ ); - } + for( uint i = 0; i < t->getTemplateArgs().size(); ++i ) { + if( t->getTemplateArgs()[i].getKind() == TemplateArgument::Type ) { r.push_back(t->getTemplateArgs()[i].getAsType() /*.getDesugaredType(C->getASTContext())*/); } } } @@ -110,10 +111,10 @@ bool is_inherited_from_enable_shared_from_this(CXXRecordDecl const *C) { if( C->getQualifiedNameAsString() == "std::enable_shared_from_this" ) return true; if( C->isCompleteDefinition() ) { - for(auto b = C->bases_begin(); b!=C->bases_end(); ++b) { - if( auto r = dyn_cast(b->getType().getCanonicalType().getTypePtr() ) ) { + for( auto b = C->bases_begin(); b != C->bases_end(); ++b ) { + if( auto r = dyn_cast(b->getType().getCanonicalType().getTypePtr()) ) { CXXRecordDecl *rd = cast(r->getDecl()); - if( rd /*and rd->isCompleteDefinition()*/ and is_inherited_from_enable_shared_from_this(rd) ) return true; + if( rd /*and rd->isCompleteDefinition()*/ and is_inherited_from_enable_shared_from_this(rd) ) return true; } } } @@ -123,11 +124,11 @@ bool is_inherited_from_enable_shared_from_this(CXXRecordDecl const *C) bool is_field_assignable(FieldDecl const *f) { - if( RecordType const* r = dyn_cast( f->getType() ) ) { - if( CXXRecordDecl *C = cast(r->getDecl() ) ) { // checking if this type has deleted operator= - for(auto m = C->method_begin(); m != C->method_end(); ++m) { - //if( m->getAccess() == AS_public and m->isCopyAssignmentOperator() /*and !m->doesThisDeclarationHaveABody()*/ and m->isDeleted() ) return false; - if( m->isCopyAssignmentOperator() and ( m->getAccess() != AS_public or m->isDeleted() ) ) return false; + if( RecordType const *r = dyn_cast(f->getType()) ) { + if( CXXRecordDecl *C = cast(r->getDecl()) ) { // checking if this type has deleted operator= + for( auto m = C->method_begin(); m != C->method_end(); ++m ) { + // if( m->getAccess() == AS_public and m->isCopyAssignmentOperator() /*and !m->doesThisDeclarationHaveABody()*/ and m->isDeleted() ) return false; + if( m->isCopyAssignmentOperator() and (m->getAccess() != AS_public or m->isDeleted()) ) return false; } } } @@ -138,13 +139,13 @@ bool is_field_assignable(FieldDecl const *f) /// check if generator can create binding bool is_bindable(FieldDecl *f) { - if( f->getType()->isAnyPointerType() or f->getType()->isReferenceType() or f->getType()->isArrayType() ) return false; + if( f->getType()->isAnyPointerType() or f->getType()->isReferenceType() or f->getType()->isArrayType() ) return false; - if( !is_field_assignable(f) ) return false; + //if( !is_field_assignable(f) ) return false; if( f->isAnonymousStructOrUnion() ) return false; - if( !is_bindable( f->getType() ) ) return false; + if( !is_bindable(f->getType()) ) return false; return true; } @@ -153,19 +154,15 @@ bool is_bindable(FieldDecl *f) // Generate bindings for class data member string bind_data_member(FieldDecl const *d, string const &class_qualified_name_) { - static vector anonymous_types { - "::(anonymous)", - "::(anonymous union)" - }; + static vector anonymous_types{"::(anonymous)", "::(anonymous union)"}; string class_qualified_name = class_qualified_name_; - for(const auto& anonymous: anonymous_types) - { - if( ends_with(class_qualified_name, anonymous) ) class_qualified_name.resize( class_qualified_name.size() - anonymous.size() ); + for( const auto &anonymous : anonymous_types ) { + if( ends_with(class_qualified_name, anonymous) ) class_qualified_name.resize(class_qualified_name.size() - anonymous.size()); } - if( d->getType().isConstQualified() ) return ".def_readonly(\"{}\", &{}::{})"_format(d->getNameAsString(), class_qualified_name, d->getNameAsString()); + if( d->getType().isConstQualified() or !is_field_assignable(d) ) return ".def_readonly(\"{}\", &{}::{})"_format(d->getNameAsString(), class_qualified_name, d->getNameAsString()); else return ".def_readwrite(\"{}\", &{}::{})"_format(d->getNameAsString(), class_qualified_name, d->getNameAsString()); } @@ -179,17 +176,17 @@ bool is_const_overload(CXXMethodDecl *mc) string const _const = " const"; if( name.size() > _const.size() ) { - name.resize( name.size() - _const.size() ); + name.resize(name.size() - _const.size()); CXXRecordDecl const *C = mc->getParent(); - for(auto m = C->method_begin(); m != C->method_end(); ++m) { - if( m->getAccess() == mc->getAccess() and !m->isConst() and function_qualified_name(*m, true) == name ) return true; + for( auto m = C->method_begin(); m != C->method_end(); ++m ) { + if( m->getAccess() == mc->getAccess() and !m->isConst() and function_qualified_name(*m, true) == name ) return true; } } } - //outs() << "is_const_overload: " << function_qualified_name(mc, false) << " -> false\n\n"; + // outs() << "is_const_overload: " << function_qualified_name(mc, false) << " -> false\n\n"; return false; } @@ -197,27 +194,27 @@ bool is_const_overload(CXXMethodDecl *mc) // check if given CXXRecordDecl (which known to be template specialization of std::function) is bindable bool is_std_function_bindable(CXXRecordDecl const *C) { - //outs() << "is_std_function_bindable( " << class_qualified_name(C) << "\n"; - if( auto t = dyn_cast(C) ) { - - for(uint i=0; i < t->getTemplateArgs().size(); ++i) { - - if (t->getTemplateArgs()[i].getKind() == TemplateArgument::Declaration) { - //outs() << " template argument: " << template_argument_to_string(t->getTemplateArgs()[i]) << "\n"; - //t->getTemplateArgs()[i].dump(); - QualType qt = t->getTemplateArgs()[i].getParamTypeForDecl(); - //qt.dump(); - - if( FunctionProtoType const *ft = dyn_cast( qt.getTypePtr() ) ) { - if( not is_bindable( ft->getReturnType() ) ) return false; - for(uint i=0; i < ft->getNumParams(); ++i) { - if( not is_bindable( ft->getParamType(i) ) ) return false; - } - } - } - } - } - return true; + // outs() << "is_std_function_bindable( " << class_qualified_name(C) << "\n"; + if( auto t = dyn_cast(C) ) { + + for( uint i = 0; i < t->getTemplateArgs().size(); ++i ) { + + if( t->getTemplateArgs()[i].getKind() == TemplateArgument::Declaration ) { + // outs() << " template argument: " << template_argument_to_string(t->getTemplateArgs()[i]) << "\n"; + // t->getTemplateArgs()[i].dump(); + QualType qt = t->getTemplateArgs()[i].getParamTypeForDecl(); + // qt.dump(); + + if( FunctionProtoType const *ft = dyn_cast(qt.getTypePtr()) ) { + if( not is_bindable(ft->getReturnType()) ) return false; + for( uint i = 0; i < ft->getNumParams(); ++i ) { + if( not is_bindable(ft->getParamType(i)) ) return false; + } + } + } + } + } + return true; } bool is_bindable_raw(clang::CXXRecordDecl const *C); @@ -226,16 +223,23 @@ bool is_bindable_raw(clang::CXXRecordDecl const *C); /// check if generator can create binding bool is_bindable(clang::CXXRecordDecl const *C) { - static std::unordered_map cache; - + static llvm::DenseMap cache; auto it = cache.find(C); - if( it != cache.end() ) return it->second; else { bool r = is_bindable_raw(C); - cache.emplace(C, r); + cache.insert( {C, r} ); return r; } + + // static std::map cache; + // auto it = cache.find(C); + // if( it != cache.end() ) return it->second; + // else { + // bool r = is_bindable_raw(C); + // cache.emplace(C, r); + // return r; + // } } /// check if generator can create binding @@ -257,28 +261,28 @@ bool is_bindable_raw(clang::CXXRecordDecl const *C) // if( C->getAccess() == AS_protected or C->getAccess() == AS_private ) return false; // } - //if( C->getNameAsString() == "" ) return false; + // if( C->getNameAsString() == "" ) return false; // enabling binding of anonymous classes and handle it as special case in `bind_nested_classes` - //if( qualified_name == "(anonymous)" ) return false; - //if( qualified_name.rfind(')') != std::string::npos ) return false; // check for anonymous structs and types in anonymous namespaces + // if( qualified_name == "(anonymous)" ) return false; + // if( qualified_name.rfind(')') != std::string::npos ) return false; // check for anonymous structs and types in anonymous namespaces // disabling bindings for anonymous namespace's // if( qualified_name.rfind("(anonymous namespace)") != std::string::npos ) return false; if( C->isInAnonymousNamespace() ) return false; - //if( C->isAnonymousStructOrUnion() ) return false; - if( !C->hasNameForLinkage() and !C->isCXXClassMember() ) return false; + // if( C->isAnonymousStructOrUnion() ) return false; + if( !C->hasNameForLinkage() and !C->isCXXClassMember() ) return false; - bool anonymous_name = qualified_name.rfind(')') != std::string::npos; // check if type name is "(anonymous)" + bool anonymous_name = qualified_name.rfind(')') != std::string::npos; // check if type name is "(anonymous)" if( anonymous_name and C->hasNameForLinkage() ) return false; if( anonymous_name and !C->hasNameForLinkage() and !C->isAnonymousStructOrUnion() ) return false; - //if( C->isAnonymousStructOrUnion() and C->hasNameForLinkage() ) return false; + // if( C->isAnonymousStructOrUnion() and C->hasNameForLinkage() ) return false; - //outs() << qualified_name << ": anonymous_name:" << anonymous_name << " isAnonymousStructOrUnion: " << C->isAnonymousStructOrUnion() << " hasNameForLinkage:" << C->hasNameForLinkage() << "\n"; + // outs() << qualified_name << ": anonymous_name:" << anonymous_name << " isAnonymousStructOrUnion: " << C->isAnonymousStructOrUnion() << " hasNameForLinkage:" << C->hasNameForLinkage() << "\n"; if( C->isDependentType() ) return false; - if( C->getAccess() == AS_protected or C->getAccess() == AS_private ) return false; + if( C->getAccess() == AS_protected or C->getAccess() == AS_private ) return false; if( !C->isCompleteDefinition() ) { if( auto ts = dyn_cast(C) ) { @@ -286,8 +290,8 @@ bool is_bindable_raw(clang::CXXRecordDecl const *C) if( not is_std_function_bindable(C) ) return false; } else { - if( ts->getPointOfInstantiation()/* SourceLocation */.isInvalid() and not is_python_builtin(C) ) { - //outs() << "is_bindable( " << qualified_name << " " << class_qualified_name(C) << " ): no point of instantiation found, skipping...\n"; + if( ts->getPointOfInstantiation() /* SourceLocation */.isInvalid() and not is_python_builtin(C) ) { + // outs() << "is_bindable( " << qualified_name << " " << class_qualified_name(C) << " ): no point of instantiation found, skipping...\n"; return false; } } @@ -329,18 +333,32 @@ bool is_bindable_raw(clang::CXXRecordDecl const *C) /// check if user requested binding for the given declaration bool is_binding_requested(clang::CXXRecordDecl const *C, Config const &config) { - if( dyn_cast(C) ) return false; - bool bind = config.is_class_binding_requested( standard_name( C->getQualifiedNameAsString() ) ) or config.is_class_binding_requested( class_qualified_name(C) ) or config.is_namespace_binding_requested( namespace_from_named_decl(C) ); - for(auto & t : get_type_dependencies(C) ) bind &= !is_skipping_requested(t, config); + if( config.is_class_binding_requested(class_qualified_name(C)) or config.is_class_binding_requested(standard_name(C->getQualifiedNameAsString())) ) return true; + + static bool bind_class_template_specialization = O_bind_class_template_specialization; + + if( (not bind_class_template_specialization) and dyn_cast(C) ) return false; + + bool bind = config.is_namespace_binding_requested(namespace_from_named_decl(C)); + for( auto &t : get_type_dependencies(C) ) bind &= !is_skipping_requested(t, config); return bind; } // check if user requested skipping for the given declaration bool is_skipping_requested(clang::CXXRecordDecl const *C, Config const &config) { - bool skip = config.is_class_skipping_requested( standard_name( C->getQualifiedNameAsString() ) ) or config.is_class_skipping_requested( class_qualified_name(C) ) or config.is_namespace_skipping_requested( namespace_from_named_decl(C) ); + string qualified_name = standard_name(C->getQualifiedNameAsString()); + string qualified_name_with_template_specialization = class_qualified_name(C); + + if( config.is_class_skipping_requested(qualified_name_with_template_specialization) ) return true; + if( config.is_class_binding_requested(qualified_name_with_template_specialization) ) return false; - for(auto & t : get_type_dependencies(C) ) skip |= is_skipping_requested(t, config); + if( config.is_class_skipping_requested(qualified_name) ) return true; + if( config.is_class_binding_requested(qualified_name) ) return false; + + bool skip = config.is_namespace_skipping_requested(namespace_from_named_decl(C)); + + for( auto &t : get_type_dependencies(C) ) skip |= is_skipping_requested(t, config); return skip; } @@ -355,37 +373,32 @@ void add_relevant_includes(clang::CXXRecordDecl const *C, IncludeSet &includes, if( auto t = dyn_cast(C) ) { - for(uint i=0; i < t->getTemplateArgs().size(); ++i) { - if( t->getTemplateArgs()[i].getKind() == TemplateArgument::Type ) { - add_relevant_includes( t->getTemplateArgs()[i].getAsType().getDesugaredType(C->getASTContext()) , includes, level+1); - } + for( uint i = 0; i < t->getTemplateArgs().size(); ++i ) { + if( t->getTemplateArgs()[i].getKind() == TemplateArgument::Type ) { add_relevant_includes(t->getTemplateArgs()[i].getAsType().getDesugaredType(C->getASTContext()), includes, level + 1); } if( t->getTemplateArgs()[i].getKind() == TemplateArgument::Template ) { - add_relevant_include_for_decl( t->getTemplateArgs()[i].getAsTemplate().getAsTemplateDecl()->getTemplatedDecl(), includes); + add_relevant_include_for_decl(t->getTemplateArgs()[i].getAsTemplate().getAsTemplateDecl()->getTemplatedDecl(), includes); } if( t->getTemplateArgs()[i].getKind() == TemplateArgument::Declaration ) { ValueDecl *v = t->getTemplateArgs()[i].getAsDecl(); add_relevant_include_for_decl(v, includes); } - if( t->getTemplateArgs()[i].getKind() == TemplateArgument::Integral ) { - add_relevant_includes(t->getTemplateArgs()[i].getIntegralType(), includes, level+1); - } + if( t->getTemplateArgs()[i].getKind() == TemplateArgument::Integral ) { add_relevant_includes(t->getTemplateArgs()[i].getIntegralType(), includes, level + 1); } } } // member function templates: we have to treat them separatly because instantiation types might not be included in class header - for(auto d = C->decls_begin(); d != C->decls_end(); ++d) { - if(FunctionTemplateDecl *ft = dyn_cast(*d) ) { - for(auto s = ft->spec_begin(); s != ft->spec_end(); ++s) { - if(CXXMethodDecl *m = dyn_cast(*s) ) { - if( m->getAccess() == AS_public - and is_bindable(m) //and !is_skipping_requested(FunctionDecl const *F, Config const &config) - and !is_skipping_requested(m, Config::get()) - and !isa(m) and !isa(m) - // and !is_const_overload(m) // commenting out for now - because const overload functions might be not bound but still re-implemented in call-back struct's for pure-virtual methods - ) { - //m->dump(); - add_relevant_includes(m, includes, level+1); + for( auto d = C->decls_begin(); d != C->decls_end(); ++d ) { + if( FunctionTemplateDecl *ft = dyn_cast(*d) ) { + for( auto s = ft->spec_begin(); s != ft->spec_end(); ++s ) { + if( CXXMethodDecl *m = dyn_cast(*s) ) { + if( m->getAccess() == AS_public and is_bindable(m) // and !is_skipping_requested(FunctionDecl const *F, Config const &config) + and !is_skipping_requested(m, Config::get()) and !isa(m) and !isa(m) + // and !is_const_overload(m) // commenting out for now - because const overload functions might be not bound but still re-implemented in call-back struct's for pure-virtual + // methods + ) { + // m->dump(); + add_relevant_includes(m, includes, level + 1); } } } @@ -415,20 +428,38 @@ void add_relevant_includes(clang::CXXRecordDecl const *C, IncludeSet &includes, // } // } // } - } } - //outs() << "isCompleteDefinition:" << C->isCompleteDefinition() << " id: " << C->getQualifiedNameAsString() << "\n"; + // outs() << "isCompleteDefinition:" << C->isCompleteDefinition() << " id: " << C->getQualifiedNameAsString() << "\n"; if( level < 2 ) { - for(auto m = C->method_begin(); m != C->method_end(); ++m) { - if( m->getAccess() == AS_public and is_bindable(*m) /*and !isa(*m)*/ and !isa(*m) ) { - add_relevant_includes(*m, includes, level+1); - } + for( auto m = C->method_begin(); m != C->method_end(); ++m ) { + if( m->getAccess() == AS_public and is_bindable(*m) /*and !isa(*m)*/ and !isa(*m) ) { add_relevant_includes(*m, includes, level + 1); } } } } +inline void add_includes_to_set(std::vector const &from, IncludeSet &to) +{ + for(auto const &i : from) to.add_include(i); +} + +void add_relevant_includes_cached(clang::CXXRecordDecl const *C, IncludeSet &includes) +{ + static std::unordered_map > cache; + + auto it = cache.find(C); + + if( it != cache.end() ) add_includes_to_set(it->second, includes); + else { + IncludeSet is; + add_relevant_includes(C, is, 1); + add_includes_to_set(is.includes(), includes); + cache.emplace(C, is.includes_); + } +} + + /// DEPRECATED Check if all bases have public default constructors // bool is_default_default_constructor_available(CXXRecordDecl const *C) @@ -455,15 +486,15 @@ void add_relevant_includes(clang::CXXRecordDecl const *C, IncludeSet &includes, // Check if all bases have public default constructors bool base_default_default_constructor_available(CXXRecordDecl const *C) { - for(auto b = C->bases_begin(); b!=C->bases_end(); ++b) { - if( auto rt = dyn_cast(b->getType().getCanonicalType().getTypePtr() ) ) { - if(CXXRecordDecl *R = cast(rt->getDecl()) ) { + for( auto b = C->bases_begin(); b != C->bases_end(); ++b ) { + if( auto rt = dyn_cast(b->getType().getCanonicalType().getTypePtr()) ) { + if( CXXRecordDecl *R = cast(rt->getDecl()) ) { if( !R->hasDefaultConstructor() ) return false; - for(auto t = R->ctor_begin(); t != R->ctor_end(); ++t) { + for( auto t = R->ctor_begin(); t != R->ctor_end(); ++t ) { if( t->isDefaultConstructor() ) { - if( t->getAccess() == AS_private /*or !t->isUserProvided()*/ or t->isDeleted() ) return false; + if( t->getAccess() == AS_private /*or !t->isUserProvided()*/ or t->isDeleted() ) return false; } } @@ -492,45 +523,62 @@ bool ClassBinder::bindable() const /// check if user requested binding for the given declaration -void ClassBinder::request_bindings_and_skipping(Config const &config) +void ClassBinder::request_bindings_and_skipping(Config const & config, RequestFlags flags) { - if( is_skipping_requested(C, config) ) Binder::request_skipping(); - else if( is_binding_requested(C, config) ) Binder::request_bindings(); + if( (flags&RequestFlags::skipping) and is_skipping_requested(C, config) ) Binder::request_skipping(); + else if( (flags&RequestFlags::binding) and is_binding_requested(C, config) ) Binder::request_bindings(); } /// extract include needed for this generator and add it to includes vector void ClassBinder::add_relevant_includes(IncludeSet &includes) const { - string const qualified_name_without_template = standard_name( C->getQualifiedNameAsString() ); - std::map> const &class_includes= Config::get().class_includes(); + string const qualified_name_without_template = standard_name(C->getQualifiedNameAsString()); + std::map> const &class_includes = Config::get().class_includes(); auto pci = class_includes.find(qualified_name_without_template); - if(pci != class_includes.end()) { - for(auto const & i : pci->second) includes.add_include(O_annotate_includes ? i + " // +include_for_class" : i); + if( pci != class_includes.end() ) { + for( auto const &i : pci->second ) includes.add_include(O_annotate_includes ? i + " // +include_for_class" : i); } - for_public_nested_classes([&includes](CXXRecordDecl const *innerC) { - ClassBinder(innerC).add_relevant_includes(includes); - }); + for_public_nested_classes([&includes](CXXRecordDecl const *innerC) { ClassBinder(innerC).add_relevant_includes(includes); }); - for(auto & m : prefix_includes ) binder::add_relevant_includes(m, includes, 0); + for( auto &m : prefix_includes_ ) binder::add_relevant_includes(m, includes, 0); binder::add_relevant_includes(C, includes, 0); includes.add_include(" // __str__"); } +string generate_opaque_declaration_if_needed(string const & qualified_name, string const & qualified_name_without_template) +{ + // pybind11 container lists https://pybind11.readthedocs.io/en/stable/advanced/cast/stl.html + static vector stl_containers {"std::vector", "std::deque", "std::list", "std::array", "std::valarray", "std::set", "std::unordered_set", "std::map", "std::unordered_map"}; + + if( begins_with(qualified_name_without_template, "std::") ) { + auto it = std::find(stl_containers.begin(), stl_containers.end(), qualified_name_without_template); + if( it != stl_containers.end() ) { + return "PYBIND11_MAKE_OPAQUE(" + qualified_name + ");\n"; + } + } + + return ""; +} + string binding_public_data_members(CXXRecordDecl const *C) { string c; // binding protected data member that was made public in child class by 'using' declaration - for(auto d = C->decls_begin(); d != C->decls_end(); ++d) { - if(UsingDecl *u = dyn_cast(*d) ) { + for( auto d = C->decls_begin(); d != C->decls_end(); ++d ) { + if( UsingDecl *u = dyn_cast(*d) ) { if( u->getAccess() == AS_public ) { - for(auto s = u->shadow_begin(); s != u->shadow_end(); ++s) { - if(UsingShadowDecl *us = dyn_cast(*s) ) { - if( FieldDecl *f = dyn_cast( us->getTargetDecl() ) ) { + for( auto s = u->shadow_begin(); s != u->shadow_end(); ++s ) { + if( UsingShadowDecl *us = dyn_cast(*s) ) { + if( FieldDecl *f = dyn_cast(us->getTargetDecl()) ) { + auto config = Config::get(); + if ( config.is_field_skipping_requested(f->getQualifiedNameAsString())) { + continue; + } if( is_bindable(f) ) c += "\tcl" + bind_data_member(f, class_qualified_name(C)) + ";\n"; } } @@ -539,9 +587,14 @@ string binding_public_data_members(CXXRecordDecl const *C) } } - for(auto d = C->decls_begin(); d != C->decls_end(); ++d) { - if(FieldDecl *f = dyn_cast(*d) ) { - if( f->getAccess() == AS_public and is_bindable(f) ) c += "\tcl" + bind_data_member(f, class_qualified_name(C)) + ";\n"; + for( auto d = C->decls_begin(); d != C->decls_end(); ++d ) { + if( FieldDecl *f = dyn_cast(*d) ) { + //outs() << "Class: " << class_qualified_name(C); f->dump(); outs() << "\n"; + auto config = Config::get(); + if ( config.is_field_skipping_requested(f->getQualifiedNameAsString()) ) { + continue; + } + if( f->getAccess() == AS_public and is_bindable(f) ) c += "\tcl" + bind_data_member(f, class_qualified_name(C)) + ";\n"; } } return c; @@ -551,32 +604,36 @@ string binding_public_data_members(CXXRecordDecl const *C) inline string callback_structure_name(CXXRecordDecl const *C) { string ns = replace_(namespace_from_named_decl(C), "::", "_"); - return "PyCallBack_" + (ns.empty() ? "" : ns + '_') + python_class_name(C); + return mangle_type_name( "PyCallBack_" + (ns.empty() ? "" : ns + '_') + python_class_name(C), false ); } // Check if binding this class require creation of call-back structure to allow overriding virtual functions in Python bool is_callback_structure_needed(CXXRecordDecl const *C) { - //C->dump(); + // C->dump(); // check if all pure-virtual methods could be overridden in Python if( C->isAbstract() ) { - for(auto m = C->method_begin(); m != C->method_end(); ++m) { - if( m->isPure() and is_const_overload(*m) ) return false; // it is not clear how to deal with this case since we can't overrdie const versions in Python, - so disabling for now + for( auto m = C->method_begin(); m != C->method_end(); ++m ) { +#if( LLVM_VERSION_MAJOR >= 18 ) + if( m->isPureVirtual() and is_const_overload(*m) ) return false; // it is not clear how to deal with this case since we can't overrdie const versions in Python, - so disabling for now +#else + if( m->isPure() and is_const_overload(*m) ) return false; // it is not clear how to deal with this case since we can't overrdie const versions in Python, - so disabling for now +#endif } } if( C->hasAttr() ) return false; - for(auto m = C->method_begin(); m != C->method_end(); ++m) { - if( m->getAccess() != AS_private and is_bindable(*m) and m->isVirtual() and !isa(*m) ) return true; + for( auto m = C->method_begin(); m != C->method_end(); ++m ) { + if( m->getAccess() != AS_private and is_bindable(*m) and m->isVirtual() and !isa(*m) ) return true; } - for(auto b = C->bases_begin(); b!=C->bases_end(); ++b) { + for( auto b = C->bases_begin(); b != C->bases_end(); ++b ) { if( b->getAccessSpecifier() != AS_private ) { - if( auto rt = dyn_cast(b->getType().getCanonicalType().getTypePtr() ) ) { - if(CXXRecordDecl *R = cast(rt->getDecl()) ) { + if( auto rt = dyn_cast(b->getType().getCanonicalType().getTypePtr()) ) { + if( CXXRecordDecl *R = cast(rt->getDecl()) ) { if( is_callback_structure_needed(R) ) return true; } } @@ -590,17 +647,21 @@ bool is_callback_structure_needed(CXXRecordDecl const *C) bool is_callback_structure_constructible(CXXRecordDecl const *C) { if( C->isAbstract() ) { - for(auto m = C->method_begin(); m != C->method_end(); ++m) { - if( m->isPure() and !isa(*m) and ( m->getAccess() == AS_private or !is_bindable(*m) or is_skipping_requested(*m, Config::get()) ) ) return false; + for( auto m = C->method_begin(); m != C->method_end(); ++m ) { +#if( LLVM_VERSION_MAJOR >= 18 ) + if( m->isPureVirtual() and !isa(*m) and (m->getAccess() == AS_private or !is_bindable(*m) or is_skipping_requested(*m, Config::get())) ) return false; +#else + if( m->isPure() and !isa(*m) and (m->getAccess() == AS_private or !is_bindable(*m) or is_skipping_requested(*m, Config::get())) ) return false; +#endif } - for(auto b = C->bases_begin(); b!=C->bases_end(); ++b) { - if( auto rt = dyn_cast(b->getType().getCanonicalType().getTypePtr() ) ) { - if(CXXRecordDecl *R = cast(rt->getDecl()) ) { - if( b->getAccessSpecifier() != AS_private ) { - if( !is_callback_structure_constructible(R) ) return false; - } - else if( R->isAbstract() ) return false; + for( auto b = C->bases_begin(); b != C->bases_end(); ++b ) { + if( auto rt = dyn_cast(b->getType().getCanonicalType().getTypePtr()) ) { + if( CXXRecordDecl *R = cast(rt->getDecl()) ) { + if( b->getAccessSpecifier() != AS_private ) { + if( !is_callback_structure_constructible(R) ) return false; + } + else if( R->isAbstract() ) return false; } } } @@ -609,7 +670,7 @@ bool is_callback_structure_constructible(CXXRecordDecl const *C) return true; } - +/* // call_back_function_body_template is almost like PYBIND11_OVERLOAD_INT but specify pybind11::return_value_policy::reference // #define PYBIND11_OVERLOAD_INT(ret_type, cname, name, ...) { \ // pybind11::gil_scoped_acquire gil; \ @@ -628,9 +689,9 @@ bool is_callback_structure_constructible(CXXRecordDecl const *C) // else return pybind11::detail::cast_safe(std::move(o)); \ // } \ // } +*/ - -const char * call_back_function_body_template = R"_( +const char *call_back_function_body_template = R"_( pybind11::gil_scoped_acquire gil; pybind11::function overload = pybind11::get_overload(static_cast(this), "{1}"); if (overload) {{ @@ -639,29 +700,32 @@ if (overload) {{ static pybind11::detail::override_caster_t<{3}> caster; return pybind11::detail::cast_ref<{3}>(std::move(o), caster); }} - else return pybind11::detail::cast_safe<{3}>(std::move(o)); + return pybind11::detail::cast_safe<{3}>(std::move(o)); }} )_"; // generate call-back overloads for all public virtual functions in C including it bases -string bind_member_functions_for_call_back(CXXRecordDecl const *C, string const & class_name, /*string const & base_type_alias,*/ set &binded, int &ret_id, std::vector &prefix_includes/*, std::set &prefix_includes_stack*/) +string bind_member_functions_for_call_back(CXXRecordDecl const *C, string const &class_name, /*string const & base_type_alias,*/ set &binded, int &ret_id, + std::vector &prefix_includes_ /*, std::set &prefix_includes_stack*/) { string c; - for(auto m = C->method_begin(); m != C->method_end(); ++m) { - if( (m->getAccess() != AS_private) and is_bindable(*m) and is_overloadable(*m) - and !is_skipping_requested(*m, Config::get()) - and !isa(*m) and !isa(*m) and m->isVirtual() - and !is_const_overload(*m) // was ( !is_const_overload(*m) or m->isPure() ) but now we ignoring this case since it is not possible to overload both const and non-const variants in Python anyway - ) { + for( auto m = C->method_begin(); m != C->method_end(); ++m ) { + if( (m->getAccess() != AS_private) and is_bindable(*m) and is_overloadable(*m) // + and !is_skipping_requested(*m, Config::get()) // + and !isa(*m) and !isa(*m) and m->isVirtual() // + and !is_const_overload( + *m) // was ( !is_const_overload(*m) or m->isPure() ) but now we ignoring this case since it is not possible to overload both const and non-const variants in Python anyway + ) { - //if( m->hasAttr() ) { - // (*m)->dump(); - // } + // if( m->hasAttr() ) { + // (*m)->dump(); + // } - string return_type = standard_name( m->getReturnType().getCanonicalType().getAsString() ); fix_boolean_types(return_type); + string return_type = standard_name(m->getReturnType()); + fix_boolean_types(return_type); // check if we need to fix return class to be 'derived-class &' or 'derived-class *' // if( m->isVirtual() ) { @@ -675,17 +739,20 @@ string bind_member_functions_for_call_back(CXXRecordDecl const *C, string const string key = /*return_type + ' ' +*/ m->getNameAsString() + '(' + std::get<0>(args) + (m->isConst() ? ") const" : ")"); - //outs() << "Class: " << C->getNameAsString() << "Key: " << key << "\n"; - //c += "// Key: " + key + "\n"; + // outs() << "Class: " << C->getNameAsString() << "Key: " << key << "\n"; + // c += "// Key: " + key + "\n"; if( !binded.count(key) ) { binded.insert(key); // m->hasAttr, attribute list is automatically generated, see /tools/clang/include/clang/Basic/AttrList.inc for attribute list - if( m->hasAttr() ) continue; // we can not test this condition in a big if above because we need 'final' function to be marked as 'binded' so they will be binded by some of the base classes + if( m->hasAttr() ) + continue; // we can not test this condition in a big if above because we need 'final' function to be marked as 'binded' so they will be binded by some of the base classes if( return_type.find(',') != std::string::npos ) { - string return_type_alias = "_binder_ret_" + std::to_string(ret_id); ++ret_id; + string return_type_alias = "_binder_ret_" + std::to_string(ret_id); + ++ret_id; + if (begins_with(return_type,"class ")) return_type = return_type.substr(6); c += "\tusing {} = {};\n"_format(return_type_alias, return_type); return_type = std::move(return_type_alias); } @@ -693,16 +760,35 @@ string bind_member_functions_for_call_back(CXXRecordDecl const *C, string const string python_name = python_function_name(*m); string exception_specification; - if(FunctionProtoType const *fpt = dyn_cast( m->getType().getTypePtr() ) ) { + if( FunctionProtoType const *fpt = dyn_cast(m->getType().getTypePtr()) ) { if( fpt->getExceptionSpecType() & clang::ExceptionSpecificationType::EST_BasicNoexcept ) exception_specification = "noexcept "; - if( fpt->getExceptionSpecType() & (clang::ExceptionSpecificationType::EST_DynamicNone | clang::ExceptionSpecificationType::EST_Dynamic | clang::ExceptionSpecificationType::EST_MSAny) ) exception_specification = "throw() "; + if( fpt->getExceptionSpecType() & + (clang::ExceptionSpecificationType::EST_DynamicNone | clang::ExceptionSpecificationType::EST_Dynamic | clang::ExceptionSpecificationType::EST_MSAny) ) + exception_specification = "throw() "; } c += "\t{} {}({}){} {}override {{"_format(return_type, m->getNameAsString(), std::get<0>(args), m->isConst() ? " const" : "", exception_specification); - c += indent( fmt::format(call_back_function_body_template, class_name, /*class_qualified_name(C), */python_name, std::get<1>(args), return_type), "\t\t"); - if( m->isPure() ) c+= "\t\tpybind11::pybind11_fail(\"Tried to call pure virtual function \\\"{}::{}\\\"\");\n"_format(C->getNameAsString(), python_name); - else c+= "\t\treturn {}::{}({});\n"_format(C->getNameAsString(), m->getNameAsString(), std::get<1>(args)); + string member_function_name = namespace_from_named_decl(C); + if ( member_function_name.length() > 0 ) member_function_name += "::"; + member_function_name += C->getNameAsString() + "::" + m->getNameAsString(); + string custom_function_info = Config::get().is_custom_trampoline_function_requested(member_function_name); + if( custom_function_info == "" ) { + c += indent(fmt::format(call_back_function_body_template, class_name, /*class_qualified_name(C), */ python_name, std::get<1>(args), return_type), "\t\t"); +#if( LLVM_VERSION_MAJOR >= 18 ) + if( m->isPureVirtual() ) c += "\t\tpybind11::pybind11_fail(\"Tried to call pure virtual function \\\"{}::{}\\\"\");\n"_format(C->getNameAsString(), python_name); +#else + if( m->isPure() ) c += "\t\tpybind11::pybind11_fail(\"Tried to call pure virtual function \\\"{}::{}\\\"\");\n"_format(C->getNameAsString(), python_name); +#endif + else c += "\t\treturn {}::{}({});\n"_format(C->getNameAsString(), m->getNameAsString(), std::get<1>(args)); + } + else { + string input_args = std::get<1>(args); + c += "\n\t\treturn {}<{},{}>(this, \"{}\", \"{}\""_format(custom_function_info, C->getNameAsString(), callback_structure_name(C), class_name, m->getNameAsString()); + if ( input_args.length() > 0 ) + c += ", {}"_format(std::get<1>(args)); + c += ");\n"; + } c += "\t}\n"; // c += string(m->isPure() ? "PYBIND11_OVERLOAD_PURE_NAME" : "PYBIND11_OVERLOAD_NAME") + '('; @@ -717,18 +803,18 @@ string bind_member_functions_for_call_back(CXXRecordDecl const *C, string const // else c+= "return {}::{}({});"_format(C->getNameAsString(), m->getNameAsString(), std::get<1>(args)); // c += " }\n"; - prefix_includes.push_back(*m); - //add_relevant_includes(*m, prefix_includes, prefix_includes_stack, 0); + // add_relevant_includes(*m, prefix_includes, prefix_includes_stack, 0); + prefix_includes_.push_back(*m); } } } - for(auto b = C->bases_begin(); b!=C->bases_end(); ++b) { + for( auto b = C->bases_begin(); b != C->bases_end(); ++b ) { if( b->getAccessSpecifier() != AS_private ) { - if( auto rt = dyn_cast(b->getType().getCanonicalType().getTypePtr() ) ) { - if(CXXRecordDecl *R = cast(rt->getDecl()) ) { - c += bind_member_functions_for_call_back(R, class_name, /*base_type_alias,*/ binded, ret_id, prefix_includes); - //add_relevant_includes(R, prefix_includes, prefix_includes_stack, 0); + if( auto rt = dyn_cast(b->getType().getCanonicalType().getTypePtr()) ) { + if( CXXRecordDecl *R = cast(rt->getDecl()) ) { + // add_relevant_includes(R, prefix_includes, prefix_includes_stack, 0); + c += bind_member_functions_for_call_back(R, class_name, /*base_type_alias,*/ binded, ret_id, prefix_includes_); } } } @@ -742,14 +828,15 @@ void ClassBinder::generate_prefix_code() { if( !is_callback_structure_needed(C) ) return; - prefix_code_ = generate_comment_for_declaration(C); + prefix_code_ = generate_comment_for_declaration(C); prefix_code_ += "struct {0} : public {1} {{\n\tusing {1}::{2};\n\n"_format(callback_structure_name(C), class_qualified_name(C), C->getNameAsString()); // string base_type_alias = "_binder_base_"; // prefix_code_ += "\tusing {} = {};\n\n"_format(base_type_alias, class_qualified_name(C)); - set binded; int ret_id = 0; - prefix_code_ += bind_member_functions_for_call_back(C, class_qualified_name(C), /*base_type_alias,*/ binded, ret_id, prefix_includes); + set binded; + int ret_id = 0; + prefix_code_ += bind_member_functions_for_call_back(C, class_qualified_name(C), /*base_type_alias,*/ binded, ret_id, prefix_includes_); prefix_code_ += "};\n\n"; } @@ -757,18 +844,17 @@ string binding_public_member_functions(CXXRecordDecl const *C, bool callback_str { string c; // binding protected/private member functions that was made public in child class by 'using' declaration - for(auto d = C->decls_begin(); d != C->decls_end(); ++d) { - if(UsingDecl *u = dyn_cast(*d) ) { + for( auto d = C->decls_begin(); d != C->decls_end(); ++d ) { + if( UsingDecl *u = dyn_cast(*d) ) { if( u->getAccess() == AS_public ) { - for(auto s = u->shadow_begin(); s != u->shadow_end(); ++s) { - if(UsingShadowDecl *us = dyn_cast(*s) ) { - if( CXXMethodDecl *m = dyn_cast( us->getTargetDecl() ) ) { - if( is_bindable(m) and !is_skipping_requested(m, Config::get()) and !isa(m) and !isa(m) and !is_const_overload(m) ) { - // Create a new CXXRecordDecl and insert base method into inherited class so bind_function correctly resolve parent namespace for function as 'child::' instead of 'base::' - // CXXRecordDecl NC(*C); - // CXXMethodDecl *nm = CXXMethodDecl::Create(m->getParentASTContext(), &NC, m->getLocStart(), m->getNameInfo(), m->getType(), m->getTypeSourceInfo(), - // m->getStorageClass(), m->isInlineSpecified(), m->isConstexpr() , m->getLocStart()); - // it looks like LLVM will delete this object when parent CXXRecordDecl is destroyed so commenting out for now... // delete nm; + for( auto s = u->shadow_begin(); s != u->shadow_end(); ++s ) { + if( UsingShadowDecl *us = dyn_cast(*s) ) { + if( CXXMethodDecl *m = dyn_cast(us->getTargetDecl()) ) { + if( is_bindable(m) and !is_skipping_requested(m, Config::get()) and !isa(m) and !isa(m) and !is_const_overload(m) ) { + // Create a new CXXRecordDecl and insert base method into inherited class so bind_function correctly resolve parent namespace for function as 'child::' instead of + // 'base::' CXXRecordDecl NC(*C); CXXMethodDecl *nm = CXXMethodDecl::Create(m->getParentASTContext(), &NC, m->getLocStart(), m->getNameInfo(), m->getType(), + // m->getTypeSourceInfo(), m->getStorageClass(), m->isInlineSpecified(), m->isConstexpr() , m->getLocStart()); it looks like LLVM will + // delete this object when parent CXXRecordDecl is destroyed so commenting out for now... // delete nm; c += bind_function("\tcl", m, context, C, /*always_use_lambda=*/true); } } @@ -777,15 +863,15 @@ string binding_public_member_functions(CXXRecordDecl const *C, bool callback_str } } - if(FunctionTemplateDecl *ft = dyn_cast(*d) ) { - for(auto s = ft->spec_begin(); s != ft->spec_end(); ++s) { - if(CXXMethodDecl *m = dyn_cast(*s) ) { - if( m->getAccess() == AS_public - and is_bindable(m) //and !is_skipping_requested(FunctionDecl const *F, Config const &config) - and !is_skipping_requested(m, Config::get()) - and !isa(m) and !isa(m) - and !is_const_overload(m) ) { - //m->dump(); + if( FunctionTemplateDecl *ft = dyn_cast(*d) ) { + for( auto s = ft->spec_begin(); s != ft->spec_end(); ++s ) { + if( CXXMethodDecl *m = dyn_cast(*s) ) { + if( m->getAccess() == AS_public // + and is_bindable(m) // and !is_skipping_requested(FunctionDecl const *F, Config const &config) + and !is_skipping_requested(m, Config::get()) // + and !isa(m) and !isa(m) // + and !is_const_overload(m) ) { // + // m->dump(); c += bind_function("\tcl", m, context); } @@ -794,12 +880,12 @@ string binding_public_member_functions(CXXRecordDecl const *C, bool callback_str } } - for(auto m = C->method_begin(); m != C->method_end(); ++m) { - if( m->getAccess() == AS_public - and is_bindable(*m) //and !is_skipping_requested(FunctionDecl const *F, Config const &config) - and !is_skipping_requested(*m, Config::get()) - and !isa(*m) and !isa(*m) - and !is_const_overload(*m) ) { + for( auto m = C->method_begin(); m != C->method_end(); ++m ) { + if( m->getAccess() == AS_public // + and is_bindable(*m) // and !is_skipping_requested(FunctionDecl const *F, Config const &config) + and !is_skipping_requested(*m, Config::get()) // + and !isa(*m) and !isa(*m) // + and !is_const_overload(*m) ) { // //(*m)->dump(); c += bind_function("\tcl", *m, context); @@ -815,10 +901,10 @@ string binding_template_bases(CXXRecordDecl const *C, bool callback_structure, b string c; if( dyn_cast(C) ) { // for template classes explicitly bind data members and member functions from public base classes - for(auto b = C->bases_begin(); b!=C->bases_end(); ++b) { - if( b->getAccessSpecifier() == AS_public) { - if( auto rt = dyn_cast(b->getType().getCanonicalType().getTypePtr() ) ) { - if(CXXRecordDecl *R = cast(rt->getDecl()) ) { + for( auto b = C->bases_begin(); b != C->bases_end(); ++b ) { + if( b->getAccessSpecifier() == AS_public and !b->isVirtual() ) { + if( auto rt = dyn_cast(b->getType().getCanonicalType().getTypePtr()) ) { + if( CXXRecordDecl *R = cast(rt->getDecl()) ) { if( !is_skipping_requested(R, Config::get()) ) { c += binding_public_data_members(R); c += binding_public_member_functions(R, callback_structure, callback_structure_constructible, context); @@ -836,17 +922,22 @@ string binding_template_bases(CXXRecordDecl const *C, bool callback_structure, b /// Create forward-binding for given class which consist of only class type without any member, function or constructors string bind_forward_declaration(CXXRecordDecl const *C, Context &context) { - string const qualified_name{ class_qualified_name(C) }; - string const module_variable_name = context.module_variable_name( namespace_from_named_decl(C) ); - //string const decl_namespace = namespace_from_named_decl(C); + string const qualified_name{class_qualified_name(C)}; + string const module_variable_name = context.module_variable_name(namespace_from_named_decl(C)); + // string const decl_namespace = namespace_from_named_decl(C); string const include = relevant_include(C); - string c = "\t// Forward declaration for: " + qualified_name + " file:" + (include.size() ? include.substr(1, include.size()-2) : "") + " line:" + line_number(C) + "\n"; + string holder_type = Config::get().holder_type(); + + string c = "\t// Forward declaration for: " + qualified_name + " file:" + (include.size() ? include.substr(1, include.size() - 2) : "") + " line:" + line_number(C) + "\n"; - string maybe_holder_type = ", std::shared_ptr<{}>"_format(qualified_name); - if( is_inherited_from_enable_shared_from_this(C) ) maybe_holder_type = ", std::shared_ptr<{}>"_format(qualified_name); - else if( CXXDestructorDecl * d = C->getDestructor() ) { + string maybe_holder_type = ", {}<{}>"_format(holder_type, qualified_name); + //Check if the type is a custom shared pointer: + if( is_inherited_from_enable_shared_from_this(C) ) { + maybe_holder_type = ", {}<{}>"_format(holder_type, qualified_name); + } + else if( CXXDestructorDecl *d = C->getDestructor() ) { if( d->getAccess() != AS_public ) maybe_holder_type = ", " + qualified_name + '*'; } @@ -863,15 +954,15 @@ string ClassBinder::maybe_base_classes(Context &context) static std::vector const skip_list = {"std::enable_shared_from_this", "std::string", "std::basic_string", "std::pair", "std::tuple"}; - for(auto b = C->bases_begin(); b!=C->bases_end(); ++b) { - if( b->getAccessSpecifier() == AS_public) { - if( auto rt = dyn_cast(b->getType().getCanonicalType().getTypePtr() ) ) { - if(CXXRecordDecl *R = cast(rt->getDecl()) ) { - auto e = std::find(skip_list.begin(), skip_list.end(), standard_name( R->getQualifiedNameAsString() )); + for( auto b = C->bases_begin(); b != C->bases_end(); ++b ) { + if( b->getAccessSpecifier() == AS_public ) { + if( auto rt = dyn_cast(b->getType().getCanonicalType().getTypePtr()) ) { + if( CXXRecordDecl *R = cast(rt->getDecl()) ) { + auto e = std::find(skip_list.begin(), skip_list.end(), standard_name(R->getQualifiedNameAsString())); - if( e == skip_list.end() and is_bindable(R) and !is_skipping_requested(R, Config::get()) ) { - //r = ", pybind11::base<{}>()"_format( class_qualified_name(R) ); - r += ", {}"_format( class_qualified_name(R) ); + if( e == skip_list.end() and is_bindable(R) and !is_skipping_requested(R, Config::get()) ) { + // r = ", pybind11::base<{}>()"_format( class_qualified_name(R) ); + r += ", {}"_format(class_qualified_name(R)); binder::request_bindings(b->getType().getCanonicalType(), context); @@ -890,14 +981,14 @@ void ClassBinder::bind_with(string const &binder, Context &context) { string c = '\t' + generate_comment_for_declaration(C); - string const module_variable_name = context.module_variable_name( namespace_from_named_decl(C) ); + string const module_variable_name = context.module_variable_name(namespace_from_named_decl(C)); - c += '\t' + binder + standard_name( template_specialization(C) ) + '(' + module_variable_name; + c += '\t' + binder + standard_name(template_specialization(C)) + '(' + module_variable_name; if( auto t = dyn_cast(C) ) { - for(uint i=0; i < t->getTemplateArgs().size(); ++i) { + for( uint i = 0; i < t->getTemplateArgs().size(); ++i ) { - string templ = mangle_type_name( standard_name( template_argument_to_string(t->getTemplateArgs()[i]) ), true); + string templ = mangle_type_name(standard_name(template_argument_to_string(t->getTemplateArgs()[i])), true); fix_boolean_types(templ); c += ", \"" + templ + '"'; } @@ -910,8 +1001,8 @@ void ClassBinder::bind_with(string const &binder, Context &context) struct ConstructorBindingInfo { - CXXRecordDecl const * C; - CXXConstructorDecl const * T; + CXXRecordDecl const *C; + CXXConstructorDecl const *T; bool trampoline; @@ -922,50 +1013,49 @@ struct ConstructorBindingInfo }; -//char const * constructor_template = "\tcl.def(\"__init__\", []({2} *self_{0}) {{ new (self_) {2}({1}); }}, \"doc\");"; -//char const * constructor_if_template = "\tcl.def(\"__init__\", [cl_type](pybind11::handle self_{0}) {{ if (self_.get_type() == cl_type) new (self_.cast<{2} *>()) {2}({1}); else new (self_.cast<{3} *>()) {3}({1}); }}, \"doc\");"; +// char const * constructor_template = "\tcl.def(\"__init__\", []({2} *self_{0}) {{ new (self_) {2}({1}); }}, \"doc\");"; +// char const * constructor_if_template = "\tcl.def(\"__init__\", [cl_type](pybind11::handle self_{0}) {{ if (self_.get_type() == cl_type) new (self_.cast<{2} *>()) {2}({1}); else new (self_.cast<{3} +// *>()) {3}({1}); }}, \"doc\");"; -char const * constructor_template = "\tcl.def( pybind11::init( []({0}){{ return new {2}({1}); }} ), \"doc\");"; -char const * constructor_template_with_py_arg = "\tcl.def( pybind11::init( []({0}){{ return new {2}({1}); }} ), \"doc\" {3});"; -char const * constructor_with_trampoline_template = "\tcl.def( pybind11::init( []({0}){{ return new {2}({1}); }}, []({0}){{ return new {3}({1}); }} ), \"doc\");"; -//char const * constructor_lambda_template = "\tcl.def(pybind11::init( []({0}){{ return new {2}({1}); }}, []({0}){{ return new {3}({1}); }} ), \"doc\");"; +char const *constructor_template = "\tcl.def( pybind11::init( []({0}){{ return new {2}({1}); }} ), \"doc\");"; +char const *constructor_template_with_py_arg = "\tcl.def( pybind11::init( []({0}){{ return new {2}({1}); }} ), \"doc\" {3});"; +char const *constructor_with_trampoline_template = "\tcl.def( pybind11::init( []({0}){{ return new {2}({1}); }}, []({0}){{ return new {3}({1}); }} ), \"doc\");"; +// char const * constructor_lambda_template = "\tcl.def(pybind11::init( []({0}){{ return new {2}({1}); }}, []({0}){{ return new {3}({1}); }} ), \"doc\");"; // Generate binding for given function: .def("foo", (std::string (aaaa::A::*)(int) ) &aaaa::A::foo, "doc") // constructor_types is pair - if one of these is absent empty string is expected -//string bind_constructor(CXXConstructorDecl const *T, pair const &constructor_types, uint args_to_bind, bool request_bindings_f, Context &context) +// string bind_constructor(CXXConstructorDecl const *T, pair const &constructor_types, uint args_to_bind, bool request_bindings_f, Context &context) string bind_constructor(ConstructorBindingInfo const &CBI, uint args_to_bind, bool request_bindings_f) { - //string function_name = python_function_name(F); - //string function_qualified_name { F->getQualifiedNameAsString() }; + // string function_name = python_function_name(F); + // string function_qualified_name { F->getQualifiedNameAsString() }; string c; - if( args_to_bind == CBI.T->getNumParams() and not CBI.T->isVariadic()) { - c = "\tcl.def( pybind11::init<{}>()"_format( function_arguments(CBI.T) ); + if( args_to_bind == CBI.T->getNumParams() and not CBI.T->isVariadic() ) { + c = "\tcl.def( pybind11::init<{}>()"_format(function_arguments(CBI.T)); - for(uint i=0; igetNumParams() and i < args_to_bind; ++i) { - c += ", pybind11::arg(\"{}\")"_format( string( CBI.T->getParamDecl(i)->getName() ) ); + for( uint i = 0; i < CBI.T->getNumParams() and i < args_to_bind; ++i ) { + c += ", pybind11::arg(\"{}\")"_format(string(CBI.T->getParamDecl(i)->getName())); - if(request_bindings_f) request_bindings( CBI.T->getParamDecl(i)->getOriginalType(), CBI.context); + if( request_bindings_f ) request_bindings(CBI.T->getParamDecl(i)->getOriginalType(), CBI.context); } c += " );\n"; } else { pair args = function_arguments_for_lambda(CBI.T, args_to_bind); - //string params = args_to_bind ? ", " + args.first : ""; + // string params = args_to_bind ? ", " + args.first : ""; string params = args_to_bind ? args.first : ""; string args_helper; - for(uint i=0; igetNumParams() and i < args_to_bind; ++i) { - args_helper += ", pybind11::arg(\"{}\")"_format( string( CBI.T->getParamDecl(i)->getName() ) ); - } + for( uint i = 0; i < CBI.T->getNumParams() and i < args_to_bind; ++i ) { args_helper += ", pybind11::arg(\"{}\")"_format(string(CBI.T->getParamDecl(i)->getName())); } // if( CBI.T->isVariadic() ) c = fmt::format(constructor_lambda_template, params, args.second, constructor_types.first, constructor_types.second); - // else if( constructor_types.first.size() and constructor_types.second.size() ) c = fmt::format(constructor_lambda_template, params, args.second, constructor_types.first, constructor_types.second); - // else if( constructor_types.first.size() ) c = fmt::format(constructor_template, params, args.second, constructor_types.first); - // else c = fmt::format(constructor_template, params, args.second, constructor_types.second); + // else if( constructor_types.first.size() and constructor_types.second.size() ) c = fmt::format(constructor_lambda_template, params, args.second, constructor_types.first, + // constructor_types.second); else if( constructor_types.first.size() ) c = fmt::format(constructor_template, params, args.second, constructor_types.first); else c = + // fmt::format(constructor_template, params, args.second, constructor_types.second); if( CBI.C->isAbstract() ) c = fmt::format(constructor_template, params, args.second, CBI.trampoline_qualified_name); else if( CBI.trampoline ) c = fmt::format(constructor_with_trampoline_template, params, args.second, CBI.class_qualified_name, CBI.trampoline_qualified_name); @@ -977,12 +1067,12 @@ string bind_constructor(ConstructorBindingInfo const &CBI, uint args_to_bind, bo /// Generate code for binding default constructor -string bind_default_constructor(ConstructorBindingInfo const &CBI) // CXXRecordDecl const *, string const & binding_qualified_name) +string bind_default_constructor(ConstructorBindingInfo const &CBI) // CXXRecordDecl const *, string const & binding_qualified_name) { // version before error: chosen constructor is explicit in copy-initialization // return "\tcl.def(pybind11::init<>());__\n"; - //return "\tcl.def( pybind11::init( [](){{ return new {0}(); }} ) );\n"_format(binding_qualified_name); + // return "\tcl.def( pybind11::init( [](){{ return new {0}(); }} ) );\n"_format(binding_qualified_name); if( CBI.C->isAbstract() ) return "\tcl.def( pybind11::init( [](){{ return new {0}(); }} ) );\n"_format(CBI.trampoline_qualified_name); else if( CBI.trampoline ) return "\tcl.def( pybind11::init( [](){{ return new {0}(); }}, [](){{ return new {1}(); }} ) );\n"_format(CBI.class_qualified_name, CBI.trampoline_qualified_name); @@ -991,38 +1081,45 @@ string bind_default_constructor(ConstructorBindingInfo const &CBI) // CXXRecord /// Generate copy constructor in most cases this will be just: "\tcl.def(pybind11::init<{} const &>());\n"_format(binding_qualified_name); /// but for POD structs with zero data mambers this will be a lambda function. This is done as a workaround for Pybind11 2,2+ bug -string bind_copy_constructor(ConstructorBindingInfo const &CBI) //CXXConstructorDecl const *T, string const & binding_qualified_name) +string bind_copy_constructor(ConstructorBindingInfo const &CBI) // CXXConstructorDecl const *T, string const & binding_qualified_name) { - //CXXRecordDecl const *C = T->getParent(); + // CXXRecordDecl const *C = T->getParent(); - //C->dump(); - //outs() << "isCLike=" << C->isCLike() << " isTrivial=" << T->isTrivial() << " isEmpty=" << C->isEmpty() << " isPOD=" << C->isPOD() << " isAggregate=" << C->isAggregate() << "\n"; + // C->dump(); + // outs() << "isCLike=" << C->isCLike() << " isTrivial=" << T->isTrivial() << " isEmpty=" << C->isEmpty() << " isPOD=" << C->isPOD() << " isAggregate=" << C->isAggregate() << "\n"; - //bool use_class_copy_constructor = (not C->isCLike() ) and ( (not T->isTrivial()) or (not C->isEmpty()) ); - //if(use_class_copy_constructor) return "\tcl.def(pybind11::init<{} const &>());\n"_format(binding_qualified_name); - //else return "\tcl.def( pybind11::init( []({0} const &o){{ return new {0}(o); }} ) );\n"_format(binding_qualified_name); + // bool use_class_copy_constructor = (not C->isCLike() ) and ( (not T->isTrivial()) or (not C->isEmpty()) ); + // if(use_class_copy_constructor) return "\tcl.def(pybind11::init<{} const &>());\n"_format(binding_qualified_name); + // else return "\tcl.def( pybind11::init( []({0} const &o){{ return new {0}(o); }} ) );\n"_format(binding_qualified_name); // bool generate_init_with_lambda = CBI.C->isAggregate() or CBI.callback_structure_constructible; //C->isCLike() or (T->isTrivial() and C->isEmpty()); // if(generate_init_with_lambda) { - // if( CBI.callback_structure_constructible and not CBI.C->isAbstract() ) return "\tcl.def( pybind11::init( []({0} const &o){{ return new {0}(o); }}, []({1} const &o){{ return new {1}(o); }} ) );\n"_format(CBI.class_qualified_name, CBI.binding_qualified_name); - // else return "\tcl.def( pybind11::init( []({0} const &o){{ return new {0}(o); }} ) );\n"_format(CBI.class_qualified_name ); + // if( CBI.callback_structure_constructible and not CBI.C->isAbstract() ) return "\tcl.def( pybind11::init( []({0} const &o){{ return new {0}(o); }}, []({1} const &o){{ return new {1}(o); }} ) + // );\n"_format(CBI.class_qualified_name, CBI.binding_qualified_name); else return "\tcl.def( pybind11::init( []({0} const &o){{ return new {0}(o); }} ) );\n"_format(CBI.class_qualified_name ); // } // else { // if( CBI.C->isAbstract() ) return "\tcl.def(pybind11::init<{} const &>());\n"_format( CBI.binding_qualified_name ); // else return "\tcl.def(pybind11::init<{} const &>());\n"_format( CBI.class_qualified_name ); // } + unsigned typequals; + string const_bit; + + CBI.T->isCopyConstructor(typequals); + if( typequals == Qualifiers::TQ::Const ) { + const_bit += " const"; + } + if( CBI.trampoline ) { - if( CBI.C->isAbstract() ) return "\tcl.def(pybind11::init<{} const &>());\n"_format( CBI.trampoline_qualified_name ); + if( CBI.C->isAbstract() ) return "\tcl.def(pybind11::init<{}{} &>());\n"_format(CBI.trampoline_qualified_name, const_bit); else { - // not yet supported by Pybind11? return "\tcl.def( pybind11::init( []({0} const &o){{ return new {0}(o); }}, []({1} const &o){{ return new {1}(o); }} ) );\n"_format(CBI.class_qualified_name, CBI.binding_qualified_name); - return - "\tcl.def( pybind11::init( []({0} const &o){{ return new {0}(o); }} ) );\n"_format(CBI.trampoline_qualified_name) + - ( CBI.T->getAccess() == AS_public ? "\tcl.def( pybind11::init( []({0} const &o){{ return new {0}(o); }} ) );\n"_format(CBI.class_qualified_name) : "" ); + // not yet supported by Pybind11? return "\tcl.def( pybind11::init( []({0} const &o){{ return new {0}(o); }}, []({1} const &o){{ return new {1}(o); }} ) + // );\n"_format(CBI.class_qualified_name, CBI.binding_qualified_name); + return "\tcl.def( pybind11::init( []({0}{1} &o){{ return new {0}(o); }} ) );\n"_format(CBI.trampoline_qualified_name, const_bit) + + (CBI.T->getAccess() == AS_public ? "\tcl.def( pybind11::init( []({0}{1} &o){{ return new {0}(o); }} ) );\n"_format(CBI.class_qualified_name, const_bit) : ""); } } - else - return "\tcl.def( pybind11::init( []({0} const &o){{ return new {0}(o); }} ) );\n"_format(CBI.class_qualified_name); + else return "\tcl.def( pybind11::init( []({0}{1} &o){{ return new {0}(o); }} ) );\n"_format(CBI.class_qualified_name, const_bit); } // Generate binding for given constructor. If constructor have default arguments generate set of bindings by creating separate bindings for each argument with default. @@ -1031,28 +1128,36 @@ string bind_constructor(ConstructorBindingInfo const &CBI) string code; uint args_to_bind = 0; - for(; args_to_bind < CBI.T->getNumParams(); ++args_to_bind) { + for( ; args_to_bind < CBI.T->getNumParams(); ++args_to_bind ) { if( CBI.T->getParamDecl(args_to_bind)->hasDefaultArg() ) break; } - for(; args_to_bind <= CBI.T->getNumParams(); ++args_to_bind) code += bind_constructor(CBI, args_to_bind, args_to_bind == CBI.T->getNumParams()) + '\n'; + for( ; args_to_bind <= CBI.T->getNumParams(); ++args_to_bind ) code += bind_constructor(CBI, args_to_bind, args_to_bind == CBI.T->getNumParams()) + '\n'; return code; } /// generate (if any) bindings for Python __str__ by using appropriate global operator<< -std::string ClassBinder::bind_repr(Context &context) +std::string ClassBinder::bind_repr(Context &context, Config const &config) { string c; + string qualified_name = class_qualified_name(C); + if( config.is_function_skipping_requested(qualified_name + "::__str__") or config.is_function_skipping_requested( standard_name(C->getQualifiedNameAsString() + "::__str__" ) ) ) return c; - if( FunctionDecl const * F = context.global_insertion_operator(C) ) { - string qualified_name = class_qualified_name(C); + if( FunctionDecl const *F = context.global_insertion_operator(C) ) { //outs() << "Found insertion operator for: " << class_qualified_name(C) << "\n"; + //outs() << "insertion operator: " << F->getNameInfo().getAsString() << " qn: " << F->getQualifiedNameAsString() << " dn:" << F->getDeclName() << "\n"; + + string maybe_using_decl; - c += "\n\tcl.def(\"__str__\", []({} const &o) -> std::string {{ std::ostringstream s; s << o; return s.str(); }} );\n"_format(qualified_name); + string ns = namespace_from_named_decl(F); + if(ns.size()) maybe_using_decl = " using namespace {};"_format(ns); - prefix_includes.push_back(F); + //c += "\n\tcl.def(\"__str__\", []({} const &o) -> std::string {{ std::ostringstream s; {}(s, o); return s.str(); }} );\n"_format(qualified_name, F->getQualifiedNameAsString()); + c += "\n\tcl.def(\"__str__\", []({} const &o) -> std::string {{ std::ostringstream s;{} s << o; return s.str(); }} );\n"_format(qualified_name, maybe_using_decl); + + prefix_includes_.push_back(F); } return c; @@ -1061,14 +1166,14 @@ std::string ClassBinder::bind_repr(Context &context) void ClassBinder::for_public_nested_classes(std::function const &f) const { - for(auto d = C->decls_begin(); d != C->decls_end(); ++d) { - if(CXXRecordDecl *innerC = dyn_cast(*d)) { - if(innerC->getAccess() == AS_public) f(innerC); + for( auto d = C->decls_begin(); d != C->decls_end(); ++d ) { + if( CXXRecordDecl *innerC = dyn_cast(*d) ) { + if( innerC->getAccess() == AS_public ) f(innerC); } - else if(ClassTemplateDecl *ct = dyn_cast(*d)) { - if(ct->getAccess() == AS_public) { - for(auto s = ct->spec_begin(); s != ct->spec_end(); ++s) { - if(CXXRecordDecl *innerC = dyn_cast(*s)) f(innerC); + else if( ClassTemplateDecl *ct = dyn_cast(*d) ) { + if( ct->getAccess() == AS_public ) { + for( auto s = ct->spec_begin(); s != ct->spec_end(); ++s ) { + if( CXXRecordDecl *innerC = dyn_cast(*s) ) f(innerC); } } } @@ -1080,21 +1185,22 @@ string ClassBinder::bind_nested_classes(Context &context) { string c; for_public_nested_classes([&c, &context, this](CXXRecordDecl const *innerC) { - if ( !innerC->isFirstDecl() ) return; // avoid multiple bindings: try to bind innerC only at first declaration - if ( !innerC->isThisDeclarationADefinition() && innerC->hasDefinition() ) { + if( !innerC->isFirstDecl() ) return; // avoid multiple bindings: try to bind innerC only at first declaration + if( !innerC->isThisDeclarationADefinition() && innerC->hasDefinition() ) { // innerC seems to be a forward declaration innerC = innerC->getDefinition(); } - if(is_bindable(innerC)) { - //c += "\t// Binding " + C->getNameAsString() + ";\n"; + if( is_bindable(innerC) ) { + // c += "\t// Binding " + C->getNameAsString() + ";\n"; ClassBinder b(innerC); b.bind(context); - c += b.code(); c += '\n'; + c += b.code(); + c += '\n'; prefix_code_ += b.prefix_code(); } }); - return c.empty() ? c : "\n" + c; + return c.empty() ? c : "\n" + c; } @@ -1103,92 +1209,120 @@ void ClassBinder::bind(Context &context) { if( is_binded() ) return; - string const qualified_name_without_template = standard_name( C->getQualifiedNameAsString() ); + string const qualified_name = class_qualified_name(C); + string const qualified_name_without_template = standard_name(C->getQualifiedNameAsString()); + + //prefix_code_ += generate_opaque_declaration_if_needed(qualified_name, qualified_name_without_template); + std::map const &external_binders = Config::get().binders(); if( external_binders.count(qualified_name_without_template) ) { - bind_with( external_binders.at(qualified_name_without_template), context ); + bind_with(external_binders.at(qualified_name_without_template), context); return; } - assert( bindable() && "Attempt to bind non-bindable CXXRecord!"); + assert(bindable() && "Attempt to bind non-bindable CXXRecord!"); bool callback_structure = is_callback_structure_needed(C); bool callback_structure_constructible = callback_structure and is_callback_structure_constructible(C); - bool trampoline = callback_structure and callback_structure_constructible; + bool trampoline = callback_structure and callback_structure_constructible; - if(trampoline) generate_prefix_code(); - - string const qualified_name{ class_qualified_name(C) }; + if( trampoline ) generate_prefix_code(); bool named_class = not C->isAnonymousStructOrUnion(); - //if( named_class and (qualified_name.rfind(')') != std::string::npos) ) named_class = false; // check for anonymous structs and types in anonymous namespaces + // if( named_class and (qualified_name.rfind(')') != std::string::npos) ) named_class = false; // check for anonymous structs and types in anonymous namespaces - string const module_variable_name = C->isCXXClassMember() and named_class ? "enclosing_class" : context.module_variable_name( namespace_from_named_decl(C) ); - //string const decl_namespace = namespace_from_named_decl(C); + string const module_variable_name = C->isCXXClassMember() and named_class ? "enclosing_class" : context.module_variable_name(namespace_from_named_decl(C)); + // string const decl_namespace = namespace_from_named_decl(C); string c = "{ " + generate_comment_for_declaration(C); - //c += " // qualified_name: {}\n"_format(qualified_name); - //c += " // getQualifiedNameAsString: {}{}\n"_format( C->getQualifiedNameAsString(), template_specialization(C) ); + // c += " // qualified_name: {}\n"_format(qualified_name); + // c += " // getQualifiedNameAsString: {}{}\n"_format( C->getQualifiedNameAsString(), template_specialization(C) ); if( C->isCXXClassMember() and named_class ) c += "\tauto & enclosing_class = cl;\n"; - //c += "// namespace: " + namespace_from_named_decl(C->getOuterLexicalRecordContext()) + "\n"; + // c += "// namespace: " + namespace_from_named_decl(C->getOuterLexicalRecordContext()) + "\n"; string const trampoline_name = callback_structure_constructible ? callback_structure_name(C) : ""; string const binding_qualified_name = callback_structure_constructible ? callback_structure_name(C) : qualified_name; - string maybe_holder_type = ", std::shared_ptr<{}>"_format(qualified_name); // for now enable std::shared_ptr by default - if( is_inherited_from_enable_shared_from_this(C) ) maybe_holder_type = ", std::shared_ptr<{}>"_format(qualified_name); - else if( CXXDestructorDecl * d = C->getDestructor() ) { + string holder_type = Config::get().holder_type(); + + string maybe_holder_type = ", {}<{}>"_format(holder_type, qualified_name); + + if( is_inherited_from_enable_shared_from_this(C) ) { + maybe_holder_type = ", {}<{}>"_format(holder_type, qualified_name); + } + else if( CXXDestructorDecl *d = C->getDestructor() ) { if( d->getAccess() != AS_public ) maybe_holder_type = ", " + qualified_name + '*'; } - string maybe_trampoline = callback_structure_constructible ? ", " + binding_qualified_name : ""; - if(named_class) c += '\t' + R"(pybind11::class_<{}{}{}{}> cl({}, "{}", "{}");)"_format(qualified_name, maybe_holder_type, maybe_trampoline, maybe_base_classes(context), module_variable_name, python_class_name(C), generate_documentation_string_for_declaration(C)) + '\n'; - //c += "\tpybind11::handle cl_type = cl;\n\n"; + // Add module local if requested for the namespace + std::string module_local_annotation = ""; + if (Config::get().is_module_local_requested(namespace_from_named_decl(C))) + module_local_annotation = ", pybind11::module_local()"; + + // Add buffer protocol if requested + std::string buffer_protocol_annotation = ""; + if (Config::get().is_buffer_protocol_requested(qualified_name_without_template)) + buffer_protocol_annotation = ", pybind11::buffer_protocol()"; + + std::string extra_annotation = module_local_annotation + buffer_protocol_annotation; - //if( C->isAbstract() and callback_structure) c += "\tcl.def(pybind11::init<>());\n"; + if( named_class ) { + if (Config::get().is_smart_holder_requested(qualified_name_without_template)) { + c += '\t' + + R"(PYBIND11_TYPE_CASTER_BASE_HOLDER({} {}))"_format(qualified_name, maybe_holder_type) + + '\n'; + } + c += '\t' + + R"(pybind11::class_<{}{}{}{}> cl({}, "{}", "{}"{});)"_format(qualified_name, maybe_holder_type, maybe_trampoline, maybe_base_classes(context), module_variable_name, python_class_name(C), + generate_documentation_string_for_declaration(C), extra_annotation) + + '\n'; + } + // c += "\tpybind11::handle cl_type = cl;\n\n"; + + // if( C->isAbstract() and callback_structure) c += "\tcl.def(pybind11::init<>());\n"; - if( (!C->isAbstract() or callback_structure_constructible) and named_class ) { + if( (!C->isAbstract() or callback_structure_constructible) and named_class ) { bool default_constructor_processed = false; - //bool copy_constructor_processed = false; + // bool copy_constructor_processed = false; string constructors; - for(auto t = C->ctor_begin(); t != C->ctor_end(); ++t) { - if( t->getAccess() == AS_public and !t->isMoveConstructor() and is_bindable(*t) and !is_skipping_requested(*t, Config::get()) /*and t->doesThisDeclarationHaveABody()*/ ) { - ConstructorBindingInfo CBI = { C, *t, trampoline, qualified_name, trampoline_name, context }; + for( auto t = C->ctor_begin(); t != C->ctor_end(); ++t ) { + if( t->getAccess() == AS_public and !t->isMoveConstructor() and is_bindable(*t) and !is_skipping_requested(*t, Config::get()) /*and t->doesThisDeclarationHaveABody()*/ ) { + ConstructorBindingInfo CBI = {C, *t, trampoline, qualified_name, trampoline_name, context}; - if( t->isCopyConstructor() /*and not copy_constructor_processed*/) { - //constructors += "\tcl.def(pybind11::init<{} const &>());\n"_format(binding_qualified_name); + if( t->isCopyConstructor() /*and not copy_constructor_processed*/ ) { + // constructors += "\tcl.def(pybind11::init<{} const &>());\n"_format(binding_qualified_name); //(*t) -> dump(); constructors += bind_copy_constructor(CBI); - //copy_constructor_processed = true; + // copy_constructor_processed = true; } - else if( t->isDefaultConstructor() and t->getNumParams()==0 ) constructors += bind_default_constructor(CBI); // workaround for Pybind11-2.2 issues + else if( t->isDefaultConstructor() and t->getNumParams() == 0 ) constructors += bind_default_constructor(CBI); // workaround for Pybind11-2.2 issues else constructors += bind_constructor(CBI); } if( t->isDefaultConstructor() ) default_constructor_processed = true; } // Generate bindings for constructors inherited from base classes though `using` declaration - for(auto d = C->decls_begin(); d != C->decls_end(); ++d) { - if(UsingDecl *u = dyn_cast(*d) ) { + for( auto d = C->decls_begin(); d != C->decls_end(); ++d ) { + if( UsingDecl *u = dyn_cast(*d) ) { if( u->getAccess() == AS_public ) { - for(auto s = u->shadow_begin(); s != u->shadow_end(); ++s) { - if(UsingShadowDecl *us = dyn_cast(*s) ) { - if( CXXConstructorDecl *t = dyn_cast( us->getTargetDecl() ) ) { + for( auto s = u->shadow_begin(); s != u->shadow_end(); ++s ) { + if( UsingShadowDecl *us = dyn_cast(*s) ) { + if( CXXConstructorDecl *t = dyn_cast(us->getTargetDecl()) ) { if( is_bindable(t) ) { - ConstructorBindingInfo CBI = { C, t, trampoline, qualified_name, trampoline_name, context }; + ConstructorBindingInfo CBI = {C, t, trampoline, qualified_name, trampoline_name, context}; - if( t->isCopyConstructor() /*and not copy_constructor_processed*/) { + if( t->isCopyConstructor() /*and not copy_constructor_processed*/ ) { // Do not bind copy-construcotors though thorough using... - //constructors += bind_copy_constructor(t, binding_qualified_name); - //copy_constructor_processed = true; + // constructors += bind_copy_constructor(t, binding_qualified_name); + // copy_constructor_processed = true; } - else if( t->isDefaultConstructor() and t->getNumParams()==0 ) constructors += bind_default_constructor(CBI); // workaround for Pybind11-2.2 issues + else if( t->isDefaultConstructor() and t->getNumParams() == 0 ) constructors += bind_default_constructor(CBI); // workaround for Pybind11-2.2 issues else constructors += bind_constructor(CBI); } if( t->isDefaultConstructor() ) default_constructor_processed = true; @@ -1199,40 +1333,46 @@ void ClassBinder::bind(Context &context) } } - //c += "\t// hasDefaultConstructor={} needsImplicitDefaultConstructor={} base_default_default_constructor_available={}\n"_format(C->hasDefaultConstructor(), C->needsImplicitDefaultConstructor(), base_default_default_constructor_available(C)); + // c += "\t// hasDefaultConstructor={} needsImplicitDefaultConstructor={} base_default_default_constructor_available={}\n"_format(C->hasDefaultConstructor(), + // C->needsImplicitDefaultConstructor(), base_default_default_constructor_available(C)); - if( !default_constructor_processed and C->needsImplicitDefaultConstructor() and base_default_default_constructor_available(C) + if( !default_constructor_processed and C->needsImplicitDefaultConstructor() and base_default_default_constructor_available(C) // ( C->ctor_begin() == C->ctor_end() and is_default_default_constructor_available(C) and !default_constructor_processed) // or (C->hasDefaultConstructor() and !default_constructor_processed ) - // if( C->hasDefaultConstructor() or ( !default_constructor_processed and is_default_default_constructor_available(C)) /*and !C->needsImplicitDefaultConstructor() and !C->hasNonTrivialDefaultConstructor()*/ + // if( C->hasDefaultConstructor() or ( !default_constructor_processed and is_default_default_constructor_available(C)) /*and !C->needsImplicitDefaultConstructor() and + // !C->hasNonTrivialDefaultConstructor()*/ /*and !C->needsImplicitDefaultConstructor() and !C->hasNonTrivialDefaultConstructor()*/ - ) { // No constructors defined, adding default constructor + ) { // No constructors defined, adding default constructor - //c += "\tcl.def(pybind11::init<>());__\n"; // making sure that default is appering first - c += bind_default_constructor( ConstructorBindingInfo {C, nullptr, trampoline, qualified_name, trampoline_name, context} ); // making sure that default is appering first + // c += "\tcl.def(pybind11::init<>());__\n"; // making sure that default is appering first + c += bind_default_constructor(ConstructorBindingInfo{C, nullptr, trampoline, qualified_name, trampoline_name, context}); // making sure that default is appering first } c += constructors; } // binding public enums - for(auto d = C->decls_begin(); d != C->decls_end(); ++d) { - if(EnumDecl *e = dyn_cast(*d) ) { - if( e->getAccess() == AS_public and is_bindable(e) ) { - //outs() << "Enum: " << e->getQualifiedNameAsString() << "\n"; - c += '\n'; - c+= bind_enum("cl", e); + for( auto d = C->decls_begin(); d != C->decls_end(); ++d ) { + if( EnumDecl *e = dyn_cast(*d) ) { + if( e->getAccess() == AS_public and is_bindable(e) ) { + if( is_skipping_requested(e, Config::get()) ) { + //outs() << "Skipping inner class Enum: " << e->getQualifiedNameAsString() << "\n"; + } + else { + c += '\n'; + c += bind_enum("cl", e); + } } } } c += binding_public_data_members(C); - c += binding_public_member_functions(C, callback_structure, callback_structure_constructible, context); + c += binding_public_member_functions(C, callback_structure, callback_structure_constructible, context); - c += binding_template_bases(C, callback_structure, callback_structure_constructible, context); + c += binding_template_bases(C, callback_structure, callback_structure_constructible, context); - c += bind_repr(context); + c += bind_repr(context, Config::get()); std::map const &external_add_on_binders = Config::get().add_on_binders(); if( external_add_on_binders.count(qualified_name_without_template) ) c += "\n\t{}(cl);\n"_format(external_add_on_binders.at(qualified_name_without_template)); diff --git a/source/class.hpp b/source/class.hpp index 64cd0714..dfbbbb68 100644 --- a/source/class.hpp +++ b/source/class.hpp @@ -13,20 +13,20 @@ #ifndef _INCLUDED_class_hpp_ #define _INCLUDED_class_hpp_ -#include +#include #include -#if (LLVM_VERSION_MAJOR >= 10) +#if( LLVM_VERSION_MAJOR >= 10 ) #include #endif +#include #include #include -#include namespace binder { -// generate classtemplate specialization for ClassTemplateSpecializationDecl or empty string otherwise +// generate class template specialization for ClassTemplateSpecializationDecl or empty string otherwise std::string template_specialization(clang::CXXRecordDecl const *C); @@ -34,11 +34,15 @@ std::string template_specialization(clang::CXXRecordDecl const *C); std::string class_name(clang::CXXRecordDecl const *C); -// generate qualified class name that could be used in bindings code indcluding template specialization if any +// generate string represetiong class name that could be used in python +std::string python_class_name(clang::CXXRecordDecl const *C); + + +// generate qualified class name that could be used in bindings code including template specialization if any std::string class_qualified_name(clang::CXXRecordDecl const *C); -// generate vector with all types that class uses if it tempalated +// generate vector with all types that class uses if it is templated std::vector get_type_dependencies(clang::CXXRecordDecl const *C /*, bool include_members=false*/); @@ -61,7 +65,7 @@ bool is_skipping_requested(clang::CXXRecordDecl const *C, Config const &config); void add_relevant_includes(clang::CXXRecordDecl const *C, IncludeSet &includes, int level); -/// Create forward-binding for given class which consist of only class type without any member, function or constructors +/// Create forward-binding for forward declared class (no class members given) std::string bind_forward_declaration(clang::CXXRecordDecl const *C, Context &); @@ -70,17 +74,17 @@ class ClassBinder : public Binder public: ClassBinder(clang::CXXRecordDecl const *c) : C(c) {} - /// Generate string id that uniquly identify C++ binding object. For functions this is function prototype and for classes forward declaration. + /// Generate string id that uniquely identify C++ binding object. For functions this is function prototype and for classes forward declaration. string id() const override; // return Clang AST NamedDecl pointer to original declaration used to create this Binder - clang::NamedDecl const * named_decl() const override { return C; }; + clang::NamedDecl const *named_decl() const override { return C; }; /// check if generator can create binding - bool bindable() const override; + bool bindable() const override; /// check if user requested binding for the given declaration - virtual void request_bindings_and_skipping(Config const &) override; + void request_bindings_and_skipping(Config const &, RequestFlags flags = RequestFlags::skipping | RequestFlags::binding) override; /// extract include needed for this generator and add it to includes vector void add_relevant_includes(IncludeSet &includes) const override; @@ -100,7 +104,7 @@ class ClassBinder : public Binder clang::CXXRecordDecl const *C; std::string prefix_code_; - std::vector prefix_includes; + std::vector prefix_includes_; // vector of classes in which current class depend to be binded (usually base classes) std::vector dependencies_; @@ -110,14 +114,14 @@ class ClassBinder : public Binder void generate_prefix_code(); - // do f for each nested public class - void for_public_nested_classes(std::function const&f) const; + // do for each nested public class + void for_public_nested_classes(std::function const &f) const; // generating bindings for public nested classes string bind_nested_classes(Context &context); /// generate (if any) bindings for Python __str__ by using appropriate global operator<< - std::string bind_repr(Context &); + std::string bind_repr(Context &, Config const &); }; diff --git a/source/config.cpp b/source/config.cpp index 0e1a15ff..4c70aa9c 100644 --- a/source/config.cpp +++ b/source/config.cpp @@ -12,13 +12,15 @@ #include +#include #include #include -#include +#include #include +#include using namespace llvm; @@ -27,7 +29,7 @@ using std::string; namespace binder { /// Split string in two by ether space or tab character -std::pair split_in_two(string const &s, string const & error_string) +std::pair split_in_two(string const &s, string const &error_string) { size_t space = s.find(' '); size_t tab = s.find('\t'); @@ -35,13 +37,13 @@ std::pair split_in_two(string const &s, string const & error_str size_t split = std::min(space, tab); if( split == string::npos ) throw std::runtime_error(error_string); - else return std::make_pair( s.substr(0, split), s.substr(split+1) ); + else return std::make_pair(s.substr(0, split), s.substr(split + 1)); } Config &Config::get() { - static Config * instance = nullptr; + static Config *instance = nullptr; if( !instance ) instance = new Config(); @@ -51,138 +53,204 @@ Config &Config::get() /// Read config setting from file void Config::read(string const &file_name) { - string const _namespace_ {"namespace"}; - string const _function_ {"function"}; - string const _class_ {"class"}; + if( O_verbose ) outs() << "Read config from file " << file_name << "\n"; + string const _namespace_{"namespace"}; + string const _function_{"function"}; + string const _class_{"class"}; + string const _field_{"field"}; + string const _enum_{"enum"}; + + string const _python_builtin_{"python_builtin"}; + + string const _include_{"include"}; + string const _include_for_class_{"include_for_class"}; + string const _include_for_namespace_{"include_for_namespace"}; + + string const _buffer_protocol_{"buffer_protocol"}; + string const _module_local_namespace_{"module_local_namespace"}; + + string const _binder_{"binder"}; + string const _add_on_binder_{"add_on_binder"}; + + string const _binder_for_namespace_{"binder_for_namespace"}; + string const _add_on_binder_for_namespace_{"add_on_binder_for_namespace"}; - string const _include_ {"include"}; - string const _include_for_class_ {"include_for_class"}; - string const _include_for_namespace_ {"include_for_namespace"}; + string const _custom_shared_{"custom_shared"}; - string const _binder_ {"binder"}; - string const _add_on_binder_ {"add_on_binder"}; + string const _smart_holder_{"smart_holder"}; - string const _binder_for_namespace_ {"binder_for_namespace"}; - string const _add_on_binder_for_namespace_ {"add_on_binder_for_namespace"}; + string const _pybind11_include_file_{"pybind11_include_file"}; - string const _default_static_pointer_return_value_policy_ {"default_static_pointer_return_value_policy"}; - string const _default_static_lvalue_reference_return_value_policy_ {"default_static_lvalue_reference_return_value_policy"}; - string const _default_static_rvalue_reference_return_value_policy_ {"default_static_rvalue_reference_return_value_policy"}; + string const _default_static_pointer_return_value_policy_{"default_static_pointer_return_value_policy"}; + string const _default_static_lvalue_reference_return_value_policy_{"default_static_lvalue_reference_return_value_policy"}; + string const _default_static_rvalue_reference_return_value_policy_{"default_static_rvalue_reference_return_value_policy"}; - string const _default_member_pointer_return_value_policy_ {"default_member_pointer_return_value_policy"}; - string const _default_member_lvalue_reference_return_value_policy_ {"default_member_lvalue_reference_return_value_policy"}; - string const _default_member_rvalue_reference_return_value_policy_ {"default_member_rvalue_reference_return_value_policy"}; + string const _default_member_pointer_return_value_policy_{"default_member_pointer_return_value_policy"}; + string const _default_member_lvalue_reference_return_value_policy_{"default_member_lvalue_reference_return_value_policy"}; + string const _default_member_rvalue_reference_return_value_policy_{"default_member_rvalue_reference_return_value_policy"}; - string const _default_call_guard_ {"default_call_guard"}; + string const _trampoline_member_function_binder_{"trampoline_member_function_binder"}; + + string const _default_call_guard_{"default_call_guard"}; + + string const _prefix_for_static_member_functions_{"prefix_for_static_member_functions"}; std::ifstream f(file_name); - if( not f.good() ) { - throw std::runtime_error("can not open file " + file_name + " for reading..."); - } + if( not f.good() ) { throw std::runtime_error("can not open file " + file_name + " for reading..."); } string line; while( std::getline(f, line) ) { - if( line.size() ) { - if( line[0] == '#' ) continue; - - if( line.back() == '\r' ) { - line.pop_back(); - if( line.empty() ) continue; - } - - if( line[0] == '+' or line[0] == '-' ) { - size_t space = line.find(' '); - if( space == string::npos ) throw std::runtime_error("Invalid line in config file! Each line must have token separated with space from object name. For example: '+function aaa::bb::my_function'. Line: " + line); - else { - bool bind = line[0] == '+' ? true : false; - string token = line.substr(1, space-1); - string name = line.substr(space+1); - string name_without_spaces = name; - name_without_spaces.erase(std::remove(name_without_spaces.begin(), name_without_spaces.end(), ' '), name_without_spaces.end()); + if( line.empty() or line[0] == '#' ) continue; - //outs() << token << " " << name << "\n"; + if( line.back() == '\r' ) { + line.pop_back(); + if( line.empty() ) continue; + } - if( token == _namespace_ ) { + if( line[0] != '+' and line[0] != '-' ) throw std::runtime_error("Invalid token at the begining of line in config file! Each line should begin with ether '+' or '-' or '#'! Line: " + line); + size_t space = line.find(' '); + if( space == string::npos ) + throw std::runtime_error("Invalid line in config file! Each line must have token separated with space from object name. For example: '+function aaa::bb::my_function'. Line: " + line); - if(bind) namespaces_to_bind.push_back(name_without_spaces); else namespaces_to_skip.push_back(name_without_spaces); + bool bind = line[0] == '+' ? true : false; + string token = line.substr(1, space - 1); + string name = line.substr(space + 1); + string name_without_spaces = name; + name_without_spaces.erase(std::remove(name_without_spaces.begin(), name_without_spaces.end(), ' '), name_without_spaces.end()); - } else if( token == _class_ ) { + // outs() << token << " " << name << "\n"; - if(bind) classes_to_bind.push_back(name_without_spaces); else classes_to_skip.push_back(name_without_spaces); + if( token == _namespace_ ) { - } else if( token == _function_ ) { + if( bind ) namespaces_to_bind.push_back(name_without_spaces); + else namespaces_to_skip.push_back(name_without_spaces); + } + else if( token == _class_ ) { - if(bind) functions_to_bind.push_back(name_without_spaces); else functions_to_skip.push_back(name_without_spaces); + if( bind ) classes_to_bind.push_back(name_without_spaces); + else classes_to_skip.push_back(name_without_spaces); + } + else if( token == _enum_ ) { - } else if( token == _include_ ) { + if( bind ) enums_to_bind.push_back(name_without_spaces); + else enums_to_skip.push_back(name_without_spaces); + } + else if( token == _python_builtin_ ) { - if(bind) includes_to_add.push_back(name_without_spaces); - else includes_to_skip.push_back(name_without_spaces); + if (bind) python_builtins.insert(name_without_spaces); + else not_python_builtins.insert(name_without_spaces); + } + else if( token == _function_ ) { - } else if( token == _include_for_class_ ) { + if( bind ) functions_to_bind.push_back(name_without_spaces); + else functions_to_skip.push_back(name_without_spaces); + } + else if( token == _include_ ) { - if(bind) { - auto class_and_include = split_in_two(name, "Invalid line for include_for_class specification! Must be: name_of_type + + include_path. Got: " + line); - class_includes_[class_and_include.first].push_back(class_and_include.second); - } - else { - throw std::runtime_error("include_for_class must be '+' configuration."); - } + if( bind ) includes_to_add.push_back(name_without_spaces); + else includes_to_skip.push_back(name_without_spaces); + } + else if( token == _include_for_class_ ) { - } else if( token == _include_for_namespace_ ) { + if( bind ) { + auto class_and_include = split_in_two(name, "Invalid line for include_for_class specification! Must be: name_of_type + + include_path. Got: " + line); + class_includes_[class_and_include.first].push_back(class_and_include.second); + } + else { + throw std::runtime_error("include_for_class must be '+' configuration."); + } + } + else if( token == _include_for_namespace_ ) { - if(bind) { - auto namespace_and_include = split_in_two(name, "Invalid line for include_for_namespace specification! Must be: name_of_type + + include_path. Got: " + line); - namespace_includes_[namespace_and_include.first].push_back(namespace_and_include.second); - } - else { - throw std::runtime_error("include_for_namespace must be '+' configuration."); - } + if( bind ) { + auto namespace_and_include = split_in_two(name, "Invalid line for include_for_namespace specification! Must be: name_of_type + + include_path. Got: " + line); + namespace_includes_[namespace_and_include.first].push_back(namespace_and_include.second); + } + else { + throw std::runtime_error("include_for_namespace must be '+' configuration."); + } + } + else if( token == _buffer_protocol_ ) { + if(bind) { + buffer_protocols.push_back(name_without_spaces); + } + } + else if( token == _module_local_namespace_) { + if(bind) { + module_local_namespaces_to_add.push_back(name_without_spaces); + } + else { + module_local_namespaces_to_skip.push_back(name_without_spaces); + } + } + else if( token == _binder_ ) { - } else if( token == _binder_ ) { + if( bind ) { + auto binder_function = split_in_two(name, "Invalid line for binder specification! Must be: name_of_type + + name_of_binder. Got: " + line); + binders_[binder_function.first] = trim(binder_function.second); + } + } + else if( token == _add_on_binder_ ) { - if(bind) { - auto binder_function = split_in_two(name, "Invalid line for binder specification! Must be: name_of_type + + name_of_binder. Got: " + line); - binders_[binder_function.first] = binder_function.second; - } + if( bind ) { + auto binder_function = split_in_two(name, "Invalid line for add_on_binder specification! Must be: name_of_type + + name_of_binder. Got: " + line); + add_on_binders_[binder_function.first] = trim(binder_function.second); + } + } + else if( token == _binder_for_namespace_ ) { - } else if( token == _add_on_binder_ ) { + if( bind ) { + auto binder_function = split_in_two(name, "Invalid line for binder_for_namespace specification! Must be: name_of_type + + name_of_binder. Got: " + line); + binder_for_namespaces_[binder_function.first] = trim(binder_function.second); + } + } + else if( token == _add_on_binder_for_namespace_ ) { - if(bind) { - auto binder_function = split_in_two(name, "Invalid line for add_on_binder specification! Must be: name_of_type + + name_of_binder. Got: " + line); - add_on_binders_[binder_function.first] = binder_function.second; - } + if( bind ) { + auto binder_function = split_in_two(name, "Invalid line for add_on_binder_for_namespace specification! Must be: name_of_type + + name_of_binder. Got: " + line); + add_on_binder_for_namespaces_[binder_function.first] = trim(binder_function.second); + } + } else if ( token == _field_ ) { - } else if( token == _binder_for_namespace_ ) { + if (!bind) { + fields_to_skip.push_back(name_without_spaces); + } + } + else if( token == _custom_shared_ ) holder_type_ = name_without_spaces; - if(bind) { - auto binder_function = split_in_two(name, "Invalid line for binder_for_namespace specification! Must be: name_of_type + + name_of_binder. Got: " + line); - binder_for_namespaces_[binder_function.first] = binder_function.second; - } + else if( token == _smart_holder_ ) { + if(bind) { + smart_held_classes.push_back(name_without_spaces); + } + } - } else if( token == _add_on_binder_for_namespace_ ) { + else if( token == _pybind11_include_file_ ) { + pybind11_include_file_ = name_without_spaces; + } - if(bind) { - auto binder_function = split_in_two(name, "Invalid line for add_on_binder_for_namespace specification! Must be: name_of_type + + name_of_binder. Got: " + line); - add_on_binder_for_namespaces_[binder_function.first] = binder_function.second; - } - } + else if( token == _default_static_pointer_return_value_policy_ ) default_static_pointer_return_value_policy_ = name_without_spaces; + else if( token == _default_static_lvalue_reference_return_value_policy_ ) default_static_lvalue_reference_return_value_policy_ = name_without_spaces; + else if( token == _default_static_rvalue_reference_return_value_policy_ ) default_static_rvalue_reference_return_value_policy_ = name_without_spaces; - else if( token == _default_static_pointer_return_value_policy_ ) default_static_pointer_return_value_policy_ = name_without_spaces; - else if( token == _default_static_lvalue_reference_return_value_policy_ ) default_static_lvalue_reference_return_value_policy_ = name_without_spaces; - else if( token == _default_static_rvalue_reference_return_value_policy_ ) default_static_rvalue_reference_return_value_policy_ = name_without_spaces; + else if( token == _default_member_pointer_return_value_policy_ ) default_member_pointer_return_value_policy_ = name_without_spaces; + else if( token == _default_member_lvalue_reference_return_value_policy_ ) default_member_lvalue_reference_return_value_policy_ = name_without_spaces; + else if( token == _default_member_rvalue_reference_return_value_policy_ ) default_member_rvalue_reference_return_value_policy_ = name_without_spaces; + else if( token == _default_call_guard_ ) default_call_guard_ = name_without_spaces; - else if( token == _default_member_pointer_return_value_policy_ ) default_member_pointer_return_value_policy_ = name_without_spaces; - else if( token == _default_member_lvalue_reference_return_value_policy_ ) default_member_lvalue_reference_return_value_policy_ = name_without_spaces; - else if( token == _default_member_rvalue_reference_return_value_policy_ ) default_member_rvalue_reference_return_value_policy_ = name_without_spaces; - else if( token == _default_call_guard_ ) default_call_guard_ = name_without_spaces; + else if( token == _prefix_for_static_member_functions_ ) prefix_for_static_member_functions_ = name_without_spaces; - else throw std::runtime_error("Invalid token in config file! Each token must be ether: namespace, class or function! For example: '+function aaa::bb::my_function'. Token: '" + token + "' Line: '" + line + '\''); - } + else if( token == _trampoline_member_function_binder_ ) { + if( bind ) { + auto member_function_name_and_function_name = split_in_two(name, "Invalid line for trampoline_member_function_binder specification! Must be: qualified_class_name::member_funtion_name + + name_of_function. Got: " + line); + custom_trampoline_functions_[member_function_name_and_function_name.first] = member_function_name_and_function_name.second; } - else throw std::runtime_error("Invalid token at the begining of line in config file! Each line should begin with ether '+' or '-' or '#'! Line: " + line); + } + + else { + throw std::runtime_error("Invalid token in config file! Each token must be either: namespace, class or function! For example: '+function aaa::bb::my_function'. Token: '" + token + + "' Line: '" + line + '\''); } } } @@ -190,26 +258,33 @@ void Config::read(string const &file_name) bool Config::is_namespace_binding_requested(string const &namespace_) const { - bool to_bind_flag=false, to_skip_flag=false; + bool to_bind_flag = false, to_skip_flag = false; string to_bind, to_skip; - for(auto &n : namespaces_to_bind) { - if( n.size() == 0 ) to_bind_flag=true; + for( auto &n : namespaces_to_bind ) { + if( n.size() == 0 ) to_bind_flag = true; if( begins_with(namespace_, n) ) { - if( n.size() > to_bind.size() ) { to_bind = n; to_bind_flag=true; } + if( n.size() > to_bind.size() ) { + to_bind = n; + to_bind_flag = true; + } } } - for(auto &s : namespaces_to_skip) { + for( auto &s : namespaces_to_skip ) { if( begins_with(namespace_, s) ) { - if( s.size() > to_skip.size() ) { to_skip = s; to_skip_flag=true; } + if( s.size() > to_skip.size() ) { + to_skip = s; + to_skip_flag = true; + } } } if( to_bind.size() > to_skip.size() ) return true; if( to_bind.size() < to_skip.size() ) return false; - if( to_bind_flag and to_skip_flag ) throw std::runtime_error("Could not determent if namespace '" + namespace_ + "' should be binded or not... please check if options --bind and --skip conflicting!!!"); + if( to_bind_flag and to_skip_flag ) + throw std::runtime_error("Could not determent if namespace '" + namespace_ + "' should be binded or not... please check if options --bind and --skip conflicting!!!"); if( to_bind_flag ) return true; @@ -218,25 +293,32 @@ bool Config::is_namespace_binding_requested(string const &namespace_) const bool Config::is_namespace_skipping_requested(string const &namespace_) const { - bool to_bind_flag=false, to_skip_flag=false; + bool to_bind_flag = false, to_skip_flag = false; string to_bind, to_skip; - for(auto &n : namespaces_to_bind) { + for( auto &n : namespaces_to_bind ) { if( begins_with(namespace_, n) ) { - if( n.size() > to_bind.size() ) { to_bind = n; to_bind_flag=true; } + if( n.size() > to_bind.size() ) { + to_bind = n; + to_bind_flag = true; + } } } - for(auto &s : namespaces_to_skip) { + for( auto &s : namespaces_to_skip ) { if( begins_with(namespace_, s) ) { - if( s.size() > to_skip.size() ) { to_skip = s; to_skip_flag=true; } + if( s.size() > to_skip.size() ) { + to_skip = s; + to_skip_flag = true; + } } } if( to_bind.size() > to_skip.size() ) return false; if( to_bind.size() < to_skip.size() ) return true; - if( to_bind_flag and to_skip_flag ) throw std::runtime_error("Could not determent if namespace '" + namespace_ + "' should be binded or not... please check if options --bind and --skip conflicting!!!"); + if( to_bind_flag and to_skip_flag ) + throw std::runtime_error("Could not determent if namespace '" + namespace_ + "' should be binded or not... please check if options --bind and --skip conflicting!!!"); if( to_skip_flag ) return true; @@ -246,7 +328,7 @@ bool Config::is_namespace_skipping_requested(string const &namespace_) const bool Config::is_function_binding_requested(string const &function_) const { - string function {function_}; + string function{function_}; function.erase(std::remove(function.begin(), function.end(), ' '), function.end()); auto bind = std::find(functions_to_bind.begin(), functions_to_bind.end(), function); @@ -258,13 +340,13 @@ bool Config::is_function_binding_requested(string const &function_) const bool Config::is_function_skipping_requested(string const &function_) const { - string function {function_}; + string function{function_}; function.erase(std::remove(function.begin(), function.end(), ' '), function.end()); auto bind = std::find(functions_to_skip.begin(), functions_to_skip.end(), function); if( bind != functions_to_skip.end() ) { - //outs() << "Skipping: " << function << "\n"; + // outs() << "Skipping: " << function << "\n"; return true; } @@ -275,7 +357,7 @@ bool Config::is_function_skipping_requested(string const &function_) const bool Config::is_class_binding_requested(string const &class__) const { - string class_ {class__}; + string class_{class__}; class_.erase(std::remove(class_.begin(), class_.end(), ' '), class_.end()); auto bind = std::find(classes_to_bind.begin(), classes_to_bind.end(), class_); @@ -288,23 +370,112 @@ bool Config::is_class_binding_requested(string const &class__) const bool Config::is_class_skipping_requested(string const &class__) const { - string class_ {class__}; + string class_{class__}; class_.erase(std::remove(class_.begin(), class_.end(), ' '), class_.end()); auto bind = std::find(classes_to_skip.begin(), classes_to_skip.end(), class_); if( bind != classes_to_skip.end() ) { - //outs() << "Skipping: " << class_ << "\n"; + // outs() << "Skipping: " << class_ << "\n"; + return true; + } + + return false; +} + + +bool Config::is_field_skipping_requested(string const &field_) const +{ + return std::find(fields_to_skip.begin(), fields_to_skip.end(), field_) != fields_to_skip.end(); +} + + +bool Config::is_enum_binding_requested(string const &enum_) const +{ + auto bind = std::find(enums_to_bind.begin(), enums_to_bind.end(), enum_); + + if( bind != enums_to_bind.end() ) return true; + + return false; +} + + +bool Config::is_enum_skipping_requested(string const &enum_) const +{ + auto bind = std::find(enums_to_skip.begin(), enums_to_skip.end(), enum_); + + if( bind != enums_to_skip.end() ) return true; + + return false; +} + + + +string Config::is_custom_trampoline_function_requested(string const &function_) const +{ + auto it = custom_trampoline_functions_.find(function_); + if( it == custom_trampoline_functions_.end() ) return ""; + else return it->second; +} + + +bool Config::is_buffer_protocol_requested(string const &class__) const +{ + string class_{class__}; + class_.erase(std::remove(class_.begin(), class_.end(), ' '), class_.end()); + + auto buffer_protocol = std::find(buffer_protocols.begin(), buffer_protocols.end(), class_); + + if( buffer_protocol != buffer_protocols.end() ) { + // outs() << "Using buffer protocol for class : " << class_ << "\n"; + return true; + } + + return false; +} + +bool Config::is_module_local_requested(string const &namespace_) const +{ + const string namespace_all = "@all_namespaces"; + auto module_local_all = std::find(module_local_namespaces_to_add.begin(), module_local_namespaces_to_add.end(), namespace_all); + if( module_local_all != module_local_namespaces_to_add.end() ) { + auto module_local_to_skip = std::find(module_local_namespaces_to_skip.begin(), module_local_namespaces_to_skip.end(), namespace_); + if( module_local_to_skip != module_local_namespaces_to_skip.end()) { + return false; + } + return true; + } + + auto module_local_to_add = std::find(module_local_namespaces_to_add.begin(), module_local_namespaces_to_add.end(), namespace_); + if( module_local_to_add != module_local_namespaces_to_add.end()) { + auto module_local_to_skip = std::find(module_local_namespaces_to_skip.begin(), module_local_namespaces_to_skip.end(), namespace_); + if( module_local_to_skip != module_local_namespaces_to_skip.end()) { + throw std::runtime_error("Could not determent if namespace '" + namespace_ + "' should use module_local or not... please resolve the conlficting options +module_local_namespace and -module_local_namespace!!!"); + } return true; } return false; } +bool Config::is_smart_holder_requested(string const &class__) const +{ + string class_{class__}; + class_.erase(std::remove(class_.begin(), class_.end(), ' '), class_.end()); + + auto smart_held_class = std::find(smart_held_classes.begin(), smart_held_classes.end(), class_); + + if( smart_held_class != smart_held_classes.end() ) { + // outs() << "Using smart holder for class : " << class_ << "\n"; + return true; + } + + return false; +} bool Config::is_include_skipping_requested(string const &include) const { - for(auto & i : includes_to_skip) + for( auto &i : includes_to_skip ) if( begins_with(include, i) ) return true; return false; @@ -313,9 +484,11 @@ bool Config::is_include_skipping_requested(string const &include) const string Config::includes_code() const { - string c; - for(auto & i: includes_to_add) c += "#include " + i + "\n"; - return c.size() ? c+'\n' : c; + std::ostringstream s; + for( auto &i : includes_to_add ) s << "#include " << i << "\n"; + if (O_include_pybind11_stl) s << "#include \n"; + if (s.tellp() != std::streampos(0)) s << '\n'; + return s.str(); } } // namespace binder diff --git a/source/config.hpp b/source/config.hpp index 1ecb3987..0c6da2f7 100644 --- a/source/config.hpp +++ b/source/config.hpp @@ -14,9 +14,10 @@ #ifndef _INCLUDED_config_hpp_ #define _INCLUDED_config_hpp_ +#include +#include #include #include -#include #ifdef _MSC_VER #include @@ -31,23 +32,30 @@ class Config Config() {} - Config(string const &root_module_, std::vector namespaces_to_bind_, std::vector namespaces_to_skip_, string const &prefix_, std::size_t maximum_file_length_) : - root_module(root_module_), namespaces_to_bind(namespaces_to_bind_), namespaces_to_skip(namespaces_to_skip_), prefix(prefix_), maximum_file_length(maximum_file_length_) {} + Config(string const &root_module_, std::vector namespaces_to_bind_, std::vector namespaces_to_skip_, string const &prefix_, std::size_t maximum_file_length_, bool skip_line_number_) + : root_module(root_module_), namespaces_to_bind(namespaces_to_bind_), namespaces_to_skip(namespaces_to_skip_), prefix(prefix_), maximum_file_length(maximum_file_length_), skip_line_number(skip_line_number_) + { + } private: std::map binders_, add_on_binders_; - std::map binder_for_namespaces_, add_on_binder_for_namespaces_; + std::map binder_for_namespaces_, add_on_binder_for_namespaces_, custom_trampoline_functions_; std::map > class_includes_, namespace_includes_; - string default_static_pointer_return_value_policy_ = "pybind11::return_value_policy::automatic"; + string default_static_pointer_return_value_policy_ = "pybind11::return_value_policy::automatic"; string default_static_lvalue_reference_return_value_policy_ = "pybind11::return_value_policy::automatic"; string default_static_rvalue_reference_return_value_policy_ = "pybind11::return_value_policy::automatic"; - string default_member_pointer_return_value_policy_ = "pybind11::return_value_policy::automatic"; + string default_member_pointer_return_value_policy_ = "pybind11::return_value_policy::automatic"; string default_member_lvalue_reference_return_value_policy_ = "pybind11::return_value_policy::automatic"; string default_member_rvalue_reference_return_value_policy_ = "pybind11::return_value_policy::automatic"; string default_call_guard_ = ""; + string holder_type_ = "std::shared_ptr"; + string pybind11_include_file_ = "pybind11/pybind11.h"; + string prefix_for_static_member_functions_ = ""; + + std::vector enums_to_bind, enums_to_skip; public: static Config &get(); @@ -57,9 +65,8 @@ class Config string root_module; - std::vector namespaces_to_bind, classes_to_bind, functions_to_bind, - namespaces_to_skip, classes_to_skip, functions_to_skip, - includes_to_add, includes_to_skip; + std::vector namespaces_to_bind, classes_to_bind, functions_to_bind, namespaces_to_skip, classes_to_skip, functions_to_skip, includes_to_add, includes_to_skip, fields_to_skip; + std::vector buffer_protocols, module_local_namespaces_to_add, module_local_namespaces_to_skip, smart_held_classes; std::map const &binders() const { return binders_; } std::map const &add_on_binders() const { return add_on_binders_; } @@ -67,25 +74,35 @@ class Config std::map const &binder_for_namespaces() const { return binder_for_namespaces_; } std::map const &add_on_binder_for_namespaces() const { return add_on_binder_for_namespaces_; } + std::set python_builtins, not_python_builtins; + std::map > const &class_includes() const { return class_includes_; } std::map > const &namespace_includes() const { return namespace_includes_; } - string const &default_static_pointer_return_value_policy() { return default_static_pointer_return_value_policy_; } + string const &default_static_pointer_return_value_policy() { return default_static_pointer_return_value_policy_; } string const &default_static_lvalue_reference_return_value_policy() { return default_static_lvalue_reference_return_value_policy_; } string const &default_static_rvalue_reference_return_value_policy() { return default_static_rvalue_reference_return_value_policy_; } - string const &default_member_pointer_return_value_policy() { return default_member_pointer_return_value_policy_; } + string const &default_member_pointer_return_value_policy() { return default_member_pointer_return_value_policy_; } string const &default_member_lvalue_reference_return_value_policy() { return default_member_lvalue_reference_return_value_policy_; } string const &default_member_rvalue_reference_return_value_policy() { return default_member_rvalue_reference_return_value_policy_; } string const &default_call_guard() { return default_call_guard_; } + string const &prefix_for_static_member_functions() { return prefix_for_static_member_functions_; } + + string const &holder_type() const { return holder_type_; } + string const &pybind11_include_file() const { return pybind11_include_file_; } + string prefix; std::size_t maximum_file_length; + bool skip_line_number = false; + /// check if user requested binding for given declaration bool is_namespace_binding_requested(string const &namespace_) const; bool is_namespace_skipping_requested(string const &namespace_) const; + bool is_module_local_requested(string const &namespace_) const; bool is_function_binding_requested(string const &function) const; bool is_function_skipping_requested(string const &function) const; @@ -93,9 +110,20 @@ class Config bool is_class_binding_requested(string const &class_) const; bool is_class_skipping_requested(string const &class_) const; + bool is_enum_binding_requested(string const &enum_) const; + bool is_enum_skipping_requested(string const &enum_) const; + + bool is_buffer_protocol_requested(string const &class_) const; + + bool is_smart_holder_requested(string const &class_) const; + bool is_include_skipping_requested(string const &include) const; + string is_custom_trampoline_function_requested(string const &function__) const; + string includes_code() const; + + bool is_field_skipping_requested(string const &name) const; }; diff --git a/source/context.cpp b/source/context.cpp index eba16e6e..901750a0 100644 --- a/source/context.cpp +++ b/source/context.cpp @@ -7,133 +7,182 @@ // MIT license that can be found in the LICENSE file. /// @file binder/context.cpp -/// @brief Data structures to represent root context and modules +/// @brief Class Context, to hold bindings info for whole TranslationUnit /// @author Sergey Lyskov - #include -#include -#include #include +#include #include +#include +#include #include -#include #include -#include +#include #include #include -#include +#include -// using namespace llvm; -using llvm::outs; using llvm::errs; +using llvm::outs; using namespace clang; using std::string; -using std::vector; using std::unordered_map; -using std::make_pair; +using std::vector; using namespace fmt::literals; -//using fmt::format; namespace binder { -//const std::string _module_variable_name_{"M"}; +namespace { // functions and constants that are only used in this translation unit - -// check if declaration is already in stack with level at lease as 'level' or lower and add it if it is not - return true if declaration was added -bool IncludeSet::add_decl(clang::NamedDecl const *D, int level) +/// Generate file name where binding for given generator should be stored +string file_name_prefix_for_binder(BinderOP &b) { - if( stack_.count(D) and stack_[D] <= level ) return false; + clang::NamedDecl const *decl = b->named_decl(); - stack_[D] = level; - return true; -} + string include = binder::relevant_include(decl); + string exceptions = "_/<>."; + include.erase(std::remove_if(include.begin(), include.end(), [&](unsigned char c) { return not(std::isalnum(c) or std::find(exceptions.begin(), exceptions.end(), c) != exceptions.end()); }), + include.end()); -// remove all includes and clear up the stack -void IncludeSet::clear() -{ - includes_.clear(); - stack_.clear(); -} + if( include.size() <= 2 ) { + include = ""; + // outs() << "Warning: file_name_prefix_for_binder could not determent file name for decl: " + string(*b) + ", result is too short!\n"; + } // throw std::runtime_error( "file_name_for_decl failed!!! include name for decl: " + string(*b) + " is too short!"); + include = include.substr(1, include.size() - 2); + if( namespace_from_named_decl(decl) == "std" or begins_with(namespace_from_named_decl(decl), "std::") ) include = "std/" + (begins_with(include, "bits/") ? include.substr(5) : include); + + replace(include, ".hh", ""); + replace(include, ".hpp", ""); + replace(include, ".h", ""); + replace(include, ".", "_"); + return include; +} -/// return true if object declared in system header -bool Binder::is_in_system_header() +// generate code for include directives and cleanup the includes vector +string generate_include_directives(IncludeSet const &include_set) { - NamedDecl const * decl( named_decl() ); - ASTContext & ast_context( decl->getASTContext() ); - SourceManager & sm( ast_context.getSourceManager() ); + string r; + for( auto &i : std::set(include_set.includes().begin(), include_set.includes().end()) ) + if( !Config::get().is_include_skipping_requested(i) ) r += "#include " + i + '\n'; - return FullSourceLoc( decl->getLocation(), sm ).isInSystemHeader(); + // includes.resize(0); + return r; } +const char *main_module_header = R"_(#include +#include +#include +#include +#include +#include -// return true if code was already generate for this object -bool Binder::is_binded() const -{ - return code().size() or is_python_builtin( named_decl() ); -} +{0} +using ModuleGetter = std::function< pybind11::module & (std::string const &) >; -llvm::raw_ostream & operator << (llvm::raw_ostream & os, Binder const &b) -{ - clang::NamedDecl const *decl = b.named_decl(); +{1} - string name = decl->getNameAsString(); - string qualified_name = decl->getQualifiedNameAsString(); - string path = decl->getQualifiedNameAsString().substr(0, qualified_name.size() - name.size() ); +PYBIND11_MODULE({2}, root_module) {{ + root_module.doc() = "{2} module"; - return os << "B{name=" << name << ", path=" << path << "\n"; //<< ", include= " code=\n" << b("module") << "\n} -} + std::map modules; + ModuleGetter M = [&](std::string const &namespace_) -> pybind11::module & {{ + auto it = modules.find(namespace_); + if( it == modules.end() ) throw std::runtime_error("Attempt to access pybind11::module for namespace " + namespace_ + " before it was created!!!"); + return it->second; + }}; + + modules[""] = root_module; + + static std::vector const reserved_python_words {{"nonlocal", "global", }}; + + auto mangle_namespace_name( + [](std::string const &ns) -> std::string {{ + if ( std::find(reserved_python_words.begin(), reserved_python_words.end(), ns) == reserved_python_words.end() ) return ns; + return ns+'_'; + }} + ); + + std::vector< std::pair > sub_modules {{ +{3} }}; + for(auto &p : sub_modules ) modules[ p.first.empty() ? p.second : p.first+"::"+p.second ] = modules[p.first].def_submodule( mangle_namespace_name(p.second).c_str(), ("Bindings for " + p.first + "::" + p.second + " namespace").c_str() ); + + //pybind11::class_>(M(""), "_encapsulated_data_"); + +{4} +}} +)_"; + +const char *module_header = R"_( +#include +{0} +#include +{1} +#ifndef BINDER_PYBIND11_TYPE_CASTER + #define BINDER_PYBIND11_TYPE_CASTER + {2} + PYBIND11_DECLARE_HOLDER_TYPE(T, T*, false) + {3} +#endif + +)_"; + +const char *module_function_suffix = "(std::function< pybind11::module &(std::string const &namespace_) > &M)"; + +} // namespace void Context::add(BinderOP &b) { - if( ids.count( b->id() ) ) { - //errs() << "Skipping adding duplicate binder for: " + b->id() + "...\n"; + if( ids.count(b->id()) ) { + // errs() << "Skipping adding duplicate binder for: " + b->id() + "...\n"; return; } binders.push_back(b); - ids.insert( b->id() ); + ids.insert(b->id()); - if( TypeDecl const * type_decl = dyn_cast( b->named_decl() ) ) types[ typename_from_type_decl(type_decl) ] = b; + if( TypeDecl const *type_decl = dyn_cast(b->named_decl()) ) types[typename_from_type_decl(type_decl)] = b; } void Context::add_insertion_operator(clang::FunctionDecl const *F) { - insertion_operators[ function_pointer_type(F) ] = F; + insertion_operators[function_pointer_type(F)] = F; } -/// find gloval insertion operator for given type, return nullptr if not such operator find -clang::FunctionDecl const * Context::global_insertion_operator(clang::CXXRecordDecl const *C) +/// find global insertion operator for given type, return nullptr if not such operator find +clang::FunctionDecl const *Context::global_insertion_operator(clang::CXXRecordDecl const *C) { - string op_pointer = "std::ostream & (*)(std::ostream &, const " + (C->isStruct() ? string("struct ") : string("class ") ) + class_qualified_name(C) + " &)"; + string op_pointer = "std::ostream & (*)(std::ostream &, const " + (C->isStruct() ? string("struct ") : string("class ")) + class_qualified_name(C) + " &)"; - //outs() << "Looking for operator: " << op_pointer << "\n"; + // outs() << "Looking for operator: " << op_pointer << "\n"; auto it = insertion_operators.find(op_pointer); - if (it != insertion_operators.end()) return it->second; + if( it != insertion_operators.end() ) return it->second; if( auto t = dyn_cast(C) ) { // if operator<< is template it will have different form: std::ostream & (*)(std::ostream &, const A &) - op_pointer = "std::ostream & (*)(std::ostream &, const " + standard_name(C->getNameAsString()) + "<"; // Note: we do not use `getQualifiedNameAsString` because if argument is templated it string representation will not have namespaces - for(uint i=0; i < t->getTemplateArgs().size(); ++i) op_pointer += "type-parameter-0-" + std::to_string(i) + ", "; - op_pointer.pop_back(); op_pointer.back() = '>'; + op_pointer = "std::ostream & (*)(std::ostream &, const " + standard_name(C->getNameAsString()) + + "<"; // Note: we do not use `getQualifiedNameAsString` because if argument is templated it string representation will not have namespaces + for( uint i = 0; i < t->getTemplateArgs().size(); ++i ) op_pointer += "type-parameter-0-" + std::to_string(i) + ", "; + op_pointer.pop_back(); + op_pointer.back() = '>'; fix_boolean_types(op_pointer); op_pointer += " &)"; // outs() << "Looking for operator: " << op_pointer << "\n"; auto it = insertion_operators.find(op_pointer); - if (it != insertion_operators.end()) return it->second; + if( it != insertion_operators.end() ) return it->second; } // outs() << "Have not found... it\n"; @@ -150,14 +199,14 @@ clang::FunctionDecl const * Context::global_insertion_operator(clang::CXXRecordD /// check if forward declaration for CXXRecordDecl needed bool Context::is_forward_needed(CXXRecordDecl const *C) { - return !binded.count( class_qualified_name(C) ) and !is_python_builtin(C); + return !binded.count(class_qualified_name(C)) and !is_python_builtin(C); } /// add given class to 'aleady binded' set void Context::add_to_binded(CXXRecordDecl const *C) { - binded.insert( class_qualified_name(C) ); + binded.insert(class_qualified_name(C)); } @@ -166,9 +215,9 @@ std::set Context::create_all_nested_namespaces() { vector namespaces; - for(auto & b : binders) { + for( auto &b : binders ) { if( b->code().size() ) { - string ns = namespace_from_named_decl( b->named_decl() ); + string ns = namespace_from_named_decl(b->named_decl()); while( ns.size() ) { namespaces.push_back(ns); @@ -177,25 +226,12 @@ std::set Context::create_all_nested_namespaces() } } - std::set s( namespaces.begin(), namespaces.end() ); + std::set s(namespaces.begin(), namespaces.end()); return s; } - -// generate code for include directives and cleanup the includes vector -string generate_include_directives(IncludeSet const &include_set) -{ - string r; - for(auto &i : std::set(include_set.includes().begin(), include_set.includes().end() ) ) - if( !Config::get().is_include_skipping_requested(i) ) r += "#include " + i + '\n'; - - //includes.resize(0); - return r; -} - - -std::string Context::module_variable_name(std::string const& namespace_) +std::string Context::module_variable_name(std::string const &namespace_) { return "M(\"" + namespace_ + "\")"; } @@ -204,7 +240,7 @@ std::string Context::module_variable_name(std::string const& namespace_) // find binder related to given object name and bind it void Context::request_bindings(std::string const &type) { - if( types.count(type) and !types[type]->is_binded() and types[type]->bindable() and !types[type]->skipping_requested() and !is_python_builtin( types[type]->named_decl() ) ) { + if( types.count(type) and !types[type]->is_binded() and types[type]->bindable() and !types[type]->skipping_requested() and !is_python_builtin(types[type]->named_decl()) ) { types[type]->request_bindings(); } } @@ -213,24 +249,27 @@ void Context::request_bindings(std::string const &type) /// walk over all binders and bind one that belong to requested namespaces void Context::bind(Config const &config) { - for(auto & sp : binders) { - Binder & b( *sp ); - if( !b.is_in_system_header() and b.bindable() ) b.request_bindings_and_skipping(config); + for( auto &sp : binders ) { + Binder &b(*sp); + if( b.bindable() ) { + if( b.is_in_system_header() ) b.request_bindings_and_skipping(config, RequestFlags::skipping); + else b.request_bindings_and_skipping(config); + } } bool flag = true; int pass = 1; - while(flag) { + while( flag ) { flag = false; outs() << "Generate bindings, pass " << pass << "...\n"; - for(auto & sp : binders) { - Binder & b( *sp ); - if( !b.is_binded() and b.bindable() and b.binding_requested() ) { - //outs() << "Binding: " << b.id() /*named_decl()->getQualifiedNameAsString()*/ << "\n"; + for( auto &sp : binders ) { + Binder &b(*sp); + if( !b.is_binded() and b.bindable() and b.binding_requested() ) { + if( O_verbose ) outs() << "Binding: " << b.id() /*named_decl()->getQualifiedNameAsString()*/ << "\n"; b.bind(*this); - flag=true; + flag = true; } } @@ -238,7 +277,7 @@ void Context::bind(Config const &config) } } -/// sort vector of binders by dependecy so python imports could work +/// sort vector of binders by dependency so python imports could work void Context::sort_binders() { outs() << "Sorting Binders...\n"; @@ -246,35 +285,35 @@ void Context::sort_binders() bool repeat = true; std::set forward; - while(repeat) { + while( repeat ) { repeat = false; binded = forward; - for(auto b=binders.begin(); b!=binders.end(); ++b) { - if( CXXRecordDecl const *C = dyn_cast( (*b)->named_decl() ) ) { // right not only querry dependency if we dealing with class + for( auto b = binders.begin(); b != binders.end(); ++b ) { + if( CXXRecordDecl const *C = dyn_cast((*b)->named_decl()) ) { // right not only query dependency if we dealing with class std::vector const dependencies = (*b)->dependencies(); - for(auto & c : dependencies ) { - if( is_forward_needed(c) ) { - //outs() << "Pushing forward binding for " << class_qualified_name(c) << "...\n"; - auto e = find_if(b, binders.end(), [&c](BinderOP const &p) -> bool { return dyn_cast(p->named_decl()) == c; } ); - if( e == binders.end() ) { - if(!repeat) { - errs() << "ERROR: Could not find binder for type: " + class_qualified_name(c) + "!\nUsually cause for this is that type was only forward declared. Please check that Binder input include file have full declaration of this class.\n"; - std::exit(1); - - //throw std::runtime_error( "ERROR: Could not find binder for type: " + class_qualified_name(c) + "!"); - } - // just declare forward declaration for now instead - // outs() << "Could not find binder for type: " + class_qualified_name(c) + "... will use forward declaration instead...\n"; - // forward.insert( class_qualified_name(c) ); - // add_to_binded(c); - } - else { - rotate(b, e, binders.end()); - repeat = true; + for( auto &c : dependencies ) { + if( !is_forward_needed(c) ) continue; + // outs() << "Pushing forward binding for " << class_qualified_name(c) << "...\n"; + auto e = find_if(b, binders.end(), [&c](BinderOP const &p) -> bool { return dyn_cast(p->named_decl()) == c; }); + if( e == binders.end() ) { + if( !repeat ) { + errs() << "ERROR: Could not find binder for type: " + class_qualified_name(c) + + "!\nUsually cause for this is that type was only forward declared. Please check that Binder input include file have full declaration of this class.\n"; + std::exit(1); + + // throw std::runtime_error( "ERROR: Could not find binder for type: " + class_qualified_name(c) + "!"); } + // just declare forward declaration for now instead + // outs() << "Could not find binder for type: " + class_qualified_name(c) + "... will use forward declaration instead...\n"; + // forward.insert( class_qualified_name(c) ); + // add_to_binded(c); + } + else { + rotate(b, e, binders.end()); + repeat = true; } } - if(repeat) break; + if( repeat ) break; add_to_binded(C); } } @@ -283,87 +322,6 @@ void Context::sort_binders() outs() << "Sorting Binders... Done.\n"; } - -/// Generate file name where binding for given generator should be stored -string file_name_prefix_for_binder(BinderOP &b) -{ - clang::NamedDecl const *decl = b->named_decl(); - - string include = relevant_include(decl); - - string exceptions = "_/<>."; - include.erase( - std::remove_if( - include.begin(), - include.end(), - [&](unsigned char c) { return not (std::isalnum(c) or std::find(exceptions.begin(), exceptions.end(), c) != exceptions.end() ); } ), - include.end() ); - - if( include.size() <= 2 ) { - include = ""; - //outs() << "Warning: file_name_prefix_for_binder could not determent file name for decl: " + string(*b) + ", result is too short!\n"; - } //throw std::runtime_error( "file_name_for_decl failed!!! include name for decl: " + string(*b) + " is too short!"); - include = include.substr(1, include.size()-2); - - if( namespace_from_named_decl(decl) == "std" or begins_with(namespace_from_named_decl(decl), "std::" ) ) include = "std/" + ( begins_with(include, "bits/") ? include.substr(5) : include ); - - replace(include, ".hh", ""); replace(include, ".hpp", ""); replace(include, ".h", ""); replace(include, ".", "_"); - return include; -} - - -const char * main_module_header = R"_(#include -#include -#include -#include -#include - -#include - -typedef std::function< pybind11::module & (std::string const &) > ModuleGetter; - -{0} - -PYBIND11_MODULE({1}, root_module) {{ - root_module.doc() = "{1} module"; - - std::map modules; - ModuleGetter M = [&](std::string const &namespace_) -> pybind11::module & {{ - auto it = modules.find(namespace_); - if( it == modules.end() ) throw std::runtime_error("Attempt to access pybind11::module for namespace " + namespace_ + " before it was created!!!"); - return it->second; - }}; - - modules[""] = root_module; - - std::vector< std::pair > sub_modules {{ -{2} }}; - for(auto &p : sub_modules ) modules[p.first.size() ? p.first+"::"+p.second : p.second] = modules[p.first].def_submodule(p.second.c_str(), ("Bindings for " + p.first + "::" + p.second + " namespace").c_str() ); - - //pybind11::class_>(M(""), "_encapsulated_data_"); - -{3} -}} -)_"; - - -const char * module_header = R"_( -#include -#include -#include -{} -#ifndef BINDER_PYBIND11_TYPE_CASTER - #define BINDER_PYBIND11_TYPE_CASTER - PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr) - PYBIND11_DECLARE_HOLDER_TYPE(T, T*) - PYBIND11_MAKE_OPAQUE(std::shared_ptr) -#endif - -)_"; - - -const char * module_function_suffix = "(std::function< pybind11::module &(std::string const &namespace_) > &M)"; - void Context::generate(Config const &config) { bind(config); @@ -384,125 +342,134 @@ void Context::generate(Config const &config) sort_binders(); outs() << "Writing code...\n"; - for(uint i=0; iis_binded() and*/ binders[i]->code().size() ) { + for( uint i = 0; i < binders.size(); ++i ) { + if( binders[i]->code().empty() ) continue; - string np, file_name; + string np, file_name; - if( O_flat ) { - np = config.root_module + "_"; - file_name = np + std::to_string(file_names[np]); - } - else { - np = file_name_prefix_for_binder(binders[i]); - file_name = np + ( file_names[np] ? "_"+std::to_string(file_names[np]) : "" ); - } - ++file_names[np]; + if( O_flat ) { + np = config.root_module + "_"; + file_name = np + std::to_string(file_names[np]); + } + else { + np = file_name_prefix_for_binder(binders[i]); + file_name = np + (file_names[np] ? "_" + std::to_string(file_names[np]) : ""); + } + ++file_names[np]; - string function_name = "bind_" + replace_(file_name, "/", "_"); - file_name += ".cpp"; + string function_name = "bind_" + replace_(file_name, "/", "_"); + file_name += ".cpp"; - sources.push_back(file_name); - binding_function_names.push_back(function_name); - //outs() << string(*binders[i]) << " → " << file_name << "\n"; + sources.push_back(file_name); + binding_function_names.push_back(function_name); + // outs() << string(*binders[i]) << " → " << file_name << "\n"; - string code, prefix_code; - string namespace_ = namespace_from_named_decl( binders[i]->named_decl() ); + string code, prefix_code; + string namespace_ = namespace_from_named_decl(binders[i]->named_decl()); - //vector includes; - //std::set stack; - IncludeSet includes; + // vector includes; + // std::set stack; + IncludeSet includes; - bool skip = false; + bool skip = false; - //outs() << namespace_ << ' ' << namespace_entrance[namespace_] << "\n"; + // outs() << namespace_ << ' ' << namespace_entrance[namespace_] << "\n"; - std::map > const & namespace_includes = Config::get().namespace_includes(); - if( namespace_includes.count(namespace_) ) { - std::vector const & n_includes = namespace_includes.at(namespace_); - for(auto const & i : n_includes) includes.add_include(i); - } + std::map > const &namespace_includes = Config::get().namespace_includes(); + if( namespace_includes.count(namespace_) ) { + std::vector const &n_includes = namespace_includes.at(namespace_); + for( auto const &i : n_includes ) includes.add_include(i); + } - std::map const &binder_for_namespaces = Config::get().binder_for_namespaces(); - if( binder_for_namespaces.count(namespace_) ) { - if( namespace_entrance[namespace_] == 0 ) { - code += "\n\t{}(M(\"{}\"));\n"_format( binder_for_namespaces.at(namespace_), namespace_); - } - skip = true; - } + std::map const &binder_for_namespaces = Config::get().binder_for_namespaces(); + if( binder_for_namespaces.count(namespace_) ) { + if( namespace_entrance[namespace_] == 0 ) { code += "\n\t{}(M(\"{}\"));\n"_format(binder_for_namespaces.at(namespace_), namespace_); } + skip = true; + } - std::map const &add_on_binder_for_namespaces = Config::get().add_on_binder_for_namespaces(); - if( add_on_binder_for_namespaces.count(namespace_) and code.empty() ) { - if( namespace_entrance[namespace_] == 0 ) code += "\n\t{}(M(\"{}\"));\n"_format( add_on_binder_for_namespaces.at(namespace_), namespace_); - } + std::map const &add_on_binder_for_namespaces = Config::get().add_on_binder_for_namespaces(); + if( add_on_binder_for_namespaces.count(namespace_) and code.empty() ) { + if( namespace_entrance[namespace_] == 0 ) code += "\n\t{}(M(\"{}\"));\n"_format(add_on_binder_for_namespaces.at(namespace_), namespace_); + } - for(; code.size()named_decl() ); ++i) { - //outs() << "Binding: " << string(*binders[i]) << "\n"; - // if( ClassBinder * CB = dynamic_cast( binders[i].get() ) ) { - // std::vector const dependencies = CB->dependencies(); - // for(auto & c : dependencies ) { - // if( is_forward_needed(c) ) { - // code += bind_forward_declaration(c, *this); - // add_to_binded(c); - // outs() << "Adding forward binding for " << class_qualified_name(c) << "\n"; - // } - // } - // add_to_binded( dynamic_cast( CB->named_decl() ) ); - // } - - // if( CXXRecordDecl const *C = dyn_cast( binders[i]->named_decl() ) ) { // right not only querry dependency if we dealing with class - // std::vector const dependencies = binders[i]->dependencies(); - // for(auto & c : dependencies ) { - // if( is_forward_needed(c) ) { - // code += bind_forward_declaration(c, *this); - // add_to_binded(c); - // outs() << "Adding forward binding for " << class_qualified_name(c) << "\n"; - // } - // } - // add_to_binded(C); - // } - if( skip ) continue; - - prefix_code += binders[i]->prefix_code(); - - if(O_trace) { - code += trace_line( binders[i]->id() ); - includes.add_include( O_annotate_includes ? " // --trace" : ""); - } + for( ; code.size() < config.maximum_file_length and i < binders.size() and namespace_ == namespace_from_named_decl(binders[i]->named_decl()); ++i ) { + // outs() << "Binding: " << string(*binders[i]) << "\n"; + // if( ClassBinder * CB = dynamic_cast( binders[i].get() ) ) { + // std::vector const dependencies = CB->dependencies(); + // for(auto & c : dependencies ) { + // if( is_forward_needed(c) ) { + // code += bind_forward_declaration(c, *this); + // add_to_binded(c); + // outs() << "Adding forward binding for " << class_qualified_name(c) << "\n"; + // } + // } + // add_to_binded( dynamic_cast( CB->named_decl() ) ); + // } + + // if( CXXRecordDecl const *C = dyn_cast( binders[i]->named_decl() ) ) { // right not only query dependency if we dealing with class + // std::vector const dependencies = binders[i]->dependencies(); + // for(auto & c : dependencies ) { + // if( is_forward_needed(c) ) { + // code += bind_forward_declaration(c, *this); + // add_to_binded(c); + // outs() << "Adding forward binding for " << class_qualified_name(c) << "\n"; + // } + // } + // add_to_binded(C); + // } + if( skip ) continue; + + prefix_code += binders[i]->prefix_code(); + + if( O_trace ) { + code += trace_line(binders[i]->id()); + includes.add_include(O_annotate_includes ? " // --trace" : ""); + } - code += binders[i]->code(); + string generated_code = binders[i]->code(); + if( generated_code.size() ) { + code += generated_code; binders[i]->add_relevant_includes(includes); } + } - if( i < binders.size() ) --i; + if( i < binders.size() ) --i; - code = generate_include_directives(includes) + fmt::format(module_header, config.includes_code()) + prefix_code + "void " + function_name + module_function_suffix + "\n{\n" + code + "}\n"; + string const holder_type = Config::get().holder_type(); - if( O_single_file ) root_module_file_handle << "// File: " << file_name << '\n' << code << "\n\n"; - else update_source_file(config.prefix, file_name, code); + string shared_declare = "PYBIND11_DECLARE_HOLDER_TYPE(T, "+holder_type+", false)"; + string shared_make_opaque = "PYBIND11_MAKE_OPAQUE("+holder_type+")"; - ++namespace_entrance[namespace_]; - } + string const pybind11_include = "#include <" + Config::get().pybind11_include_file() + ">"; + code = generate_include_directives(includes) + fmt::format(module_header, pybind11_include, config.includes_code(), shared_declare, shared_make_opaque) + prefix_code + "void " + function_name + module_function_suffix + "\n{\n" + code + "}\n"; + + if( O_single_file ) root_module_file_handle << "// File: " << file_name << '\n' << code << "\n\n"; + else update_source_file(config.prefix, file_name, code); + + ++namespace_entrance[namespace_]; } outs() << "Writing code... Done.\n"; string modules; string namespace_pairs; std::set namespaces = create_all_nested_namespaces(); - for(auto & n : namespaces) { + for( auto &n : namespaces ) { if( n.size() ) namespace_pairs += "\t\t{{\"{}\", \"{}\"}},\n"_format(base_namespace(n), last_namespace(n)); - modules += n; modules += ' '; + modules += n; + modules += ' '; } replace(modules, "::", "."); string binding_function_decls, binding_function_calls; - for(auto &f : binding_function_names) { + for( auto &f : binding_function_names ) { binding_function_decls += "void " + f + module_function_suffix + ";\n"; binding_function_calls += "\t" + f + "(M);\n"; } + string const pybind11_include = "#include <" + Config::get().pybind11_include_file() + ">"; + std::stringstream s; - s << fmt::format(main_module_header, binding_function_decls, config.root_module, namespace_pairs, binding_function_calls); + s << fmt::format(main_module_header, pybind11_include, binding_function_decls, config.root_module, namespace_pairs, binding_function_calls); root_module_file_handle << s.str(); @@ -510,15 +477,15 @@ void Context::generate(Config const &config) if( O_single_file ) { root_module_file_handle << "\n// Source list file: " << root_module_prefix + ".sources\n"; - for(auto &s : sources) root_module_file_handle << "// "<< s << "\n"; + for( auto &s : sources ) root_module_file_handle << "// " << s << "\n"; root_module_file_handle << "\n// Modules list file: " << root_module_prefix + ".modules\n// "; root_module_file_handle << modules; root_module_file_handle << "\n"; - - } else { + } + else { std::ofstream f(root_module_prefix + ".sources"); - for(auto &s : sources) f << s << "\n"; + for( auto &s : sources ) f << s << "\n"; std::ofstream namespaces_file_handle(root_module_prefix + ".modules"); namespaces_file_handle << modules; @@ -528,7 +495,7 @@ void Context::generate(Config const &config) /// generate unique trace line containing `info` to insert into the code std::string Context::trace_line(std::string const &info) { - return "\tstd::cout << \"B" + std::to_string(++trace_counter) + "_[" + info + "] \";\n"; + return "\tstd::cout << \"B" + std::to_string(++trace_counter) + "_[" + info + "]\" << std::endl;\n"; } diff --git a/source/context.hpp b/source/context.hpp index 0d62f1a3..c1b18f41 100644 --- a/source/context.hpp +++ b/source/context.hpp @@ -7,117 +7,24 @@ // MIT license that can be found in the LICENSE file. /// @file binder/context.hpp -/// @brief Data structures to represent root context and modules +/// @brief Class Context, to hold bindings info for whole TranslationUnit /// @author Sergey Lyskov #ifndef _INCLUDED_context_hpp_ #define _INCLUDED_context_hpp_ -#include +#include #include -#include - -#include -#include #include -#include +#include #include +#include +#include namespace binder { -class Context; - -// structure to hold include set information and set of NamedDecl objects that was already queried for includes -class IncludeSet -{ - std::vector includes_; - - std::unordered_map stack_; - -public: - // add include to the set - void add_include(std::string const &i) { includes_.push_back(i); } - - // check if declaration is already in stack with level at lease as 'level' or lower and add it if it is not - return true if declaration was added - bool add_decl(clang::NamedDecl const *, int level); - - std::vector const &includes() const { return includes_; } - - // remove all includes and clear up the stack - void clear(); -}; - - -/// Bindings Generator - represent object that can generate binding info for function, class, enum or data variable -class Binder -{ -public: - typedef std::string string; - - virtual ~Binder() {} - - /// Generate string id that uniquly identify C++ binding object. For functions this is function prototype and for classes forward declaration. - virtual string id() const = 0; - - // return Clang AST NamedDecl pointer to original declaration used to create this Binder - virtual clang::NamedDecl const * named_decl() const = 0; - - /// check if generator can create binding - virtual bool bindable() const = 0; - - bool binding_requested() const { return binding_requested_; }; - bool skipping_requested() const { return skipping_requested_; }; - - /// request bindings for this generator - void request_bindings() { binding_requested_=true; } - - /// request skipping for this generator - void request_skipping() { skipping_requested_=true; } - - /// check if user supplied config requested binding for the given declaration and if so request it - virtual void request_bindings_and_skipping(Config const &) = 0; - - /// extract include needed for this generator and add it to includes vector - virtual void add_relevant_includes(IncludeSet &) const = 0; - - /// generate binding code for this object and all its dependencies - virtual void bind(Context &) = 0; - - // return true if code was already generate for this object - bool is_binded() const; - - // return binding code - string & code() { return code_; } - string const & code() const { return code_; } - - /// return true if object declared in system header - bool is_in_system_header(); - - // return vector of declarations that need to be binded before this one could - virtual std::vector dependencies() const { return std::vector(); } - - /// return prefix portion of bindings code - virtual string prefix_code() const { return string(); } - - /// return unique strting ID for this binder - explicit operator std::string() const { return id(); /*named_decl()->getQualifiedNameAsString();*/ } - -private: - bool binding_requested_=false, skipping_requested_=false; - - string code_; -}; - - -typedef std::shared_ptr< Binder > BinderOP; - -typedef std::vector Binders; - - -llvm::raw_ostream & operator << (llvm::raw_ostream & os, Binder const &b); - /// Context - root, hold bindings info for whole TranslationUnit class Context @@ -125,55 +32,53 @@ class Context typedef std::string string; public: - void add(BinderOP &); void generate(Config const &config); - // find binder related to given type name and bind it + /// find binder related to given type name and bind it void request_bindings(std::string const &type); /// generate C++ expression for module variable for namespace_ - string module_variable_name(string const & namespace_); + string module_variable_name(string const &namespace_); void add_insertion_operator(clang::FunctionDecl const *f); - /// find gloval insertion operator for given type, return nullptr if not such operator find - clang::FunctionDecl const * global_insertion_operator(clang::CXXRecordDecl const *); + /// find global insertion operator for given type, otherwise return nullptr + clang::FunctionDecl const *global_insertion_operator(clang::CXXRecordDecl const *); /// generate unique trace line containing `info` to insert into the code std::string trace_line(std::string const &info); private: - - /// bind all objects residing in namespaces and it dependency + /// bind all objects residing in namespaces and their dependencies void bind(Config const &config); std::set create_all_nested_namespaces(); - /// check if forward declaration for CXXRecordDecl needed + /// check if forward declaration for CXXRecordDecl needed bool is_forward_needed(clang::CXXRecordDecl const *); - /// add given class to 'aleady binded' set + /// add given class to 'already binded' set void add_to_binded(clang::CXXRecordDecl const *); - /// sort vector of binders by dependecy so python imports could work + /// sort vector of binders by dependency so python imports could work void sort_binders(); - /// map of function-ptr -> Decl* for global instertion operators so we can determent for which types can bind __repr__ + /// map of function-ptr -> Decl* for global instertion operators so we can determine for which types we can bind __repr__ std::map insertion_operators; - /// array of all binderes from translation unit + /// array of all binders from translation unit std::vector binders; /// types → binder std::unordered_map types; - /// set of items unique id's to keep track of whats binders being added + /// set of items unique id's to keep track of binders being added std::unordered_set ids; - /// set of items unique id's to keep track of whats binded and not + /// set of items unique id's to keep track of what is binded and not std::set binded; /// counter to generate unique trace lines for debug diff --git a/source/enum.cpp b/source/enum.cpp index 73e2c9ce..b538d739 100644 --- a/source/enum.cpp +++ b/source/enum.cpp @@ -50,51 +50,88 @@ bool is_bindable(EnumDecl const *E) if( E->isCXXInstanceMember() ) return false; string name = E->getNameAsString(); // getQualifiedNameAsString(); // - //if( name.rfind(')') != string::npos ) return false; // checking that this is not an "(anonymous)" enum - if( name.empty() or name.rfind(')') != string::npos ) return false; // checking that this is not an "(anonymous)" enum + // if( name.rfind(')') != string::npos ) return false; // checking that this is not an "(anonymous)" enum + if( name.empty() or name.rfind(')') != string::npos ) return false; // checking that this is not an "(anonymous)" enum return true; } +/// check if user requested binding for the given declaration +bool is_binding_requested(clang::EnumDecl const *E, Config const &config) +{ + if( config.is_enum_binding_requested( E->getQualifiedNameAsString() ) ) return true; + + bool bind = config.is_namespace_binding_requested(namespace_from_named_decl(E)); + //for( auto &t : get_type_dependencies(E) ) bind &= !is_skipping_requested(t, config); + return bind; +} + +// check if user requested skipping for the given declaration +bool is_skipping_requested(clang::EnumDecl const *E, Config const &config) +{ + string qualified_name = standard_name(E->getQualifiedNameAsString()); + + if( config.is_enum_skipping_requested(qualified_name) ) return true; + if( config.is_enum_binding_requested(qualified_name) ) return false; + + bool skip = config.is_namespace_skipping_requested(namespace_from_named_decl(E)); + + //for( auto &t : get_type_dependencies(E) ) skip |= is_skipping_requested(t, config); + + return skip; +} + + + // This function takes care about the LLVM/Clang bug which was fixed in LLVM6/Clang6. // The body of the function is a backport from LLVM6. -std::string getQualifiedNameAsStringLLVM5Fix( NamedDecl const *E) { +std::string getQualifiedNameAsStringLLVM5Fix(NamedDecl const *E) +{ std::string correct; llvm::raw_string_ostream OS(correct); DeclContext const *Ctx = E->getDeclContext(); SmallVector Contexts; - while (Ctx && isa(Ctx)) { + while( Ctx && isa(Ctx) ) { Contexts.push_back(Ctx); Ctx = Ctx->getParent(); } - for (const DeclContext *DC : reverse(Contexts)) { - if (const auto *ED = dyn_cast(DC)) { - if ( ED->isScoped() ) { - OS<<*ED; OS<<"::"; - } else continue; - } else { - OS << *cast(DC); - OS<<"::"; + for( const DeclContext *DC : reverse(Contexts) ) { + if( const auto *ED = dyn_cast(DC) ) { + if( ED->isScoped() ) { + OS << *ED; + OS << "::"; + } + else continue; + } + else { + OS << *cast(DC); + OS << "::"; } } - if ((E->getDeclName() || isa(E))) OS<<*E; else OS<<"(anonymous)"; + if( (E->getDeclName() || isa(E)) ) OS << *E; + else OS << "(anonymous)"; return correct; } // Generate binding for given function: py::enum_(module, "MyEnum")... -std::string bind_enum(std::string const & module, EnumDecl const *E) +std::string bind_enum(std::string const &module, EnumDecl const *E) { - string name { E->getNameAsString() }; - string qualified_name { E->getQualifiedNameAsString() }; + string name{E->getNameAsString()}; + string qualified_name{E->getQualifiedNameAsString()}; string maybe_arithmetic = E->isScoped() ? "" : ", pybind11::arithmetic()"; - string r = "\tpybind11::enum_<{}>({}, \"{}\"{}, \"{}\")\n"_format(qualified_name, module, name, maybe_arithmetic, generate_documentation_string_for_declaration(E)); + // Add module local if requested for the namespace + std::string module_local_annotation = ""; + if (Config::get().is_module_local_requested(namespace_from_named_decl(E))) + module_local_annotation = ", pybind11::module_local()"; + + string r = "\tpybind11::enum_<{}>({}, \"{}\"{}, \"{}\"{})\n"_format(qualified_name, module, name, maybe_arithmetic, generate_documentation_string_for_declaration(E), module_local_annotation); - //r += "\t // is_bindable " + E->getNameAsString() + " -> " + std::to_string(is_bindable(E)) + "\n"; + // r += "\t // is_bindable " + E->getNameAsString() + " -> " + std::to_string(is_bindable(E)) + "\n"; - for(auto e = E->enumerator_begin(); e != E->enumerator_end(); ++e) { -#if (LLVM_VERSION_MAJOR > 5) + for( auto e = E->enumerator_begin(); e != E->enumerator_end(); ++e ) { +#if( LLVM_VERSION_MAJOR > 5 ) r += "\t\t.value(\"{}\", {})\n"_format(e->getNameAsString(), e->getQualifiedNameAsString()); #else r += "\t\t.value(\"{}\", {})\n"_format(e->getNameAsString(), getQualifiedNameAsStringLLVM5Fix(*e)); @@ -102,7 +139,7 @@ std::string bind_enum(std::string const & module, EnumDecl const *E) } r.pop_back(); - return r + ( E->isScopedUsingClassTag() ? ";\n\n" : "\n\t\t.export_values();\n\n" ); + return r + (E->isScopedUsingClassTag() ? ";\n\n" : "\n\t\t.export_values();\n\n"); } @@ -116,14 +153,15 @@ string EnumBinder::id() const /// check if generator can create binding bool EnumBinder::bindable() const { - return is_bindable(E); + return is_bindable(E) and !is_banned_symbol(E); } /// check if user requested binding for the given declaration -void EnumBinder::request_bindings_and_skipping(Config const &config) +void EnumBinder::request_bindings_and_skipping(Config const &config, RequestFlags flags) { - if( config.is_namespace_binding_requested( namespace_from_named_decl(E) ) ) Binder::request_bindings(); + if( (flags&RequestFlags::skipping) and is_skipping_requested(E, config) ) Binder::request_skipping(); + else if( (flags&RequestFlags::binding) and is_binding_requested(E, config) ) Binder::request_bindings(); } @@ -138,9 +176,9 @@ void EnumBinder::bind(Context &context) { if( is_binded() ) return; - string const module_variable_name = context.module_variable_name( namespace_from_named_decl(E) ); + string const module_variable_name = context.module_variable_name(namespace_from_named_decl(E)); - code() = "\t" + generate_comment_for_declaration(E); + code() = "\t" + generate_comment_for_declaration(E); code() += bind_enum(module_variable_name, E) + ";\n\n"; } diff --git a/source/enum.hpp b/source/enum.hpp index 493e0bd6..22dfc192 100644 --- a/source/enum.hpp +++ b/source/enum.hpp @@ -30,8 +30,16 @@ void add_relevant_includes(clang::EnumDecl const *E, IncludeSet &includes, int l bool is_bindable(clang::EnumDecl const *E); +/// check if user requested binding for the given declaration +bool is_binding_requested(clang::EnumDecl const *E, Config const &config); + + +/// check if user requested skipping for the given declaration +bool is_skipping_requested(clang::EnumDecl const *E, Config const &config); + + // Generate binding for given function: py::enum_(module, "MyEnum")... -std::string bind_enum(std::string const & module, clang::EnumDecl const *E); +std::string bind_enum(std::string const &module, clang::EnumDecl const *E); class EnumBinder : public Binder @@ -43,13 +51,13 @@ class EnumBinder : public Binder string id() const override; // return Clang AST NamedDecl pointer to original declaration used to create this Binder - clang::NamedDecl const * named_decl() const override { return E; }; + clang::NamedDecl const *named_decl() const override { return E; }; /// check if generator can create binding - bool bindable() const override; + bool bindable() const override; /// check if user requested binding for the given declaration - virtual void request_bindings_and_skipping(Config const &) override; + void request_bindings_and_skipping(Config const &, RequestFlags flags = RequestFlags::skipping | RequestFlags::binding) override; /// extract include needed for this generator and add it to includes vector void add_relevant_includes(IncludeSet &includes) const override; diff --git a/source/fmt/LICENSE.rst b/source/fmt/LICENSE.rst index eb6be650..f0ec3db4 100644 --- a/source/fmt/LICENSE.rst +++ b/source/fmt/LICENSE.rst @@ -1,23 +1,27 @@ -Copyright (c) 2012 - 2016, Victor Zverovich +Copyright (c) 2012 - present, Victor Zverovich -All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--- Optional exception to the license --- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into a machine-executable object form of such +source code, you may redistribute such embedded portions in such object form +without including the above copyright and permission notices. diff --git a/source/fmt/chrono.h b/source/fmt/chrono.h new file mode 100644 index 00000000..1a3b8d5e --- /dev/null +++ b/source/fmt/chrono.h @@ -0,0 +1,1118 @@ +// Formatting library for C++ - chrono support +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_CHRONO_H_ +#define FMT_CHRONO_H_ + +#include +#include +#include +#include + +#include "format.h" +#include "locale.h" + +FMT_BEGIN_NAMESPACE + +// Enable safe chrono durations, unless explicitly disabled. +#ifndef FMT_SAFE_DURATION_CAST +# define FMT_SAFE_DURATION_CAST 1 +#endif +#if FMT_SAFE_DURATION_CAST + +// For conversion between std::chrono::durations without undefined +// behaviour or erroneous results. +// This is a stripped down version of duration_cast, for inclusion in fmt. +// See https://github.com/pauldreik/safe_duration_cast +// +// Copyright Paul Dreik 2019 +namespace safe_duration_cast { + +template ::value && + std::numeric_limits::is_signed == + std::numeric_limits::is_signed)> +FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { + ec = 0; + using F = std::numeric_limits; + using T = std::numeric_limits; + static_assert(F::is_integer, "From must be integral"); + static_assert(T::is_integer, "To must be integral"); + + // A and B are both signed, or both unsigned. + if (F::digits <= T::digits) { + // From fits in To without any problem. + } else { + // From does not always fit in To, resort to a dynamic check. + if (from < (T::min)() || from > (T::max)()) { + // outside range. + ec = 1; + return {}; + } + } + return static_cast(from); +} + +/** + * converts From to To, without loss. If the dynamic value of from + * can't be converted to To without loss, ec is set. + */ +template ::value && + std::numeric_limits::is_signed != + std::numeric_limits::is_signed)> +FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { + ec = 0; + using F = std::numeric_limits; + using T = std::numeric_limits; + static_assert(F::is_integer, "From must be integral"); + static_assert(T::is_integer, "To must be integral"); + + if (detail::const_check(F::is_signed && !T::is_signed)) { + // From may be negative, not allowed! + if (fmt::detail::is_negative(from)) { + ec = 1; + return {}; + } + // From is positive. Can it always fit in To? + if (F::digits > T::digits && + from > static_cast(detail::max_value())) { + ec = 1; + return {}; + } + } + + if (!F::is_signed && T::is_signed && F::digits >= T::digits && + from > static_cast(detail::max_value())) { + ec = 1; + return {}; + } + return static_cast(from); // Lossless conversion. +} + +template ::value)> +FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { + ec = 0; + return from; +} // function + +// clang-format off +/** + * converts From to To if possible, otherwise ec is set. + * + * input | output + * ---------------------------------|--------------- + * NaN | NaN + * Inf | Inf + * normal, fits in output | converted (possibly lossy) + * normal, does not fit in output | ec is set + * subnormal | best effort + * -Inf | -Inf + */ +// clang-format on +template ::value)> +FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { + ec = 0; + using T = std::numeric_limits; + static_assert(std::is_floating_point::value, "From must be floating"); + static_assert(std::is_floating_point::value, "To must be floating"); + + // catch the only happy case + if (std::isfinite(from)) { + if (from >= T::lowest() && from <= (T::max)()) { + return static_cast(from); + } + // not within range. + ec = 1; + return {}; + } + + // nan and inf will be preserved + return static_cast(from); +} // function + +template ::value)> +FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { + ec = 0; + static_assert(std::is_floating_point::value, "From must be floating"); + return from; +} + +/** + * safe duration cast between integral durations + */ +template ::value), + FMT_ENABLE_IF(std::is_integral::value)> +To safe_duration_cast(std::chrono::duration from, + int& ec) { + using From = std::chrono::duration; + ec = 0; + // the basic idea is that we need to convert from count() in the from type + // to count() in the To type, by multiplying it with this: + struct Factor + : std::ratio_divide {}; + + static_assert(Factor::num > 0, "num must be positive"); + static_assert(Factor::den > 0, "den must be positive"); + + // the conversion is like this: multiply from.count() with Factor::num + // /Factor::den and convert it to To::rep, all this without + // overflow/underflow. let's start by finding a suitable type that can hold + // both To, From and Factor::num + using IntermediateRep = + typename std::common_type::type; + + // safe conversion to IntermediateRep + IntermediateRep count = + lossless_integral_conversion(from.count(), ec); + if (ec) return {}; + // multiply with Factor::num without overflow or underflow + if (detail::const_check(Factor::num != 1)) { + const auto max1 = detail::max_value() / Factor::num; + if (count > max1) { + ec = 1; + return {}; + } + const auto min1 = + (std::numeric_limits::min)() / Factor::num; + if (count < min1) { + ec = 1; + return {}; + } + count *= Factor::num; + } + + if (detail::const_check(Factor::den != 1)) count /= Factor::den; + auto tocount = lossless_integral_conversion(count, ec); + return ec ? To() : To(tocount); +} + +/** + * safe duration_cast between floating point durations + */ +template ::value), + FMT_ENABLE_IF(std::is_floating_point::value)> +To safe_duration_cast(std::chrono::duration from, + int& ec) { + using From = std::chrono::duration; + ec = 0; + if (std::isnan(from.count())) { + // nan in, gives nan out. easy. + return To{std::numeric_limits::quiet_NaN()}; + } + // maybe we should also check if from is denormal, and decide what to do about + // it. + + // +-inf should be preserved. + if (std::isinf(from.count())) { + return To{from.count()}; + } + + // the basic idea is that we need to convert from count() in the from type + // to count() in the To type, by multiplying it with this: + struct Factor + : std::ratio_divide {}; + + static_assert(Factor::num > 0, "num must be positive"); + static_assert(Factor::den > 0, "den must be positive"); + + // the conversion is like this: multiply from.count() with Factor::num + // /Factor::den and convert it to To::rep, all this without + // overflow/underflow. let's start by finding a suitable type that can hold + // both To, From and Factor::num + using IntermediateRep = + typename std::common_type::type; + + // force conversion of From::rep -> IntermediateRep to be safe, + // even if it will never happen be narrowing in this context. + IntermediateRep count = + safe_float_conversion(from.count(), ec); + if (ec) { + return {}; + } + + // multiply with Factor::num without overflow or underflow + if (Factor::num != 1) { + constexpr auto max1 = detail::max_value() / + static_cast(Factor::num); + if (count > max1) { + ec = 1; + return {}; + } + constexpr auto min1 = std::numeric_limits::lowest() / + static_cast(Factor::num); + if (count < min1) { + ec = 1; + return {}; + } + count *= static_cast(Factor::num); + } + + // this can't go wrong, right? den>0 is checked earlier. + if (Factor::den != 1) { + using common_t = typename std::common_type::type; + count /= static_cast(Factor::den); + } + + // convert to the to type, safely + using ToRep = typename To::rep; + + const ToRep tocount = safe_float_conversion(count, ec); + if (ec) { + return {}; + } + return To{tocount}; +} +} // namespace safe_duration_cast +#endif + +// Prevents expansion of a preceding token as a function-style macro. +// Usage: f FMT_NOMACRO() +#define FMT_NOMACRO + +namespace detail { +inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); } +inline null<> localtime_s(...) { return null<>(); } +inline null<> gmtime_r(...) { return null<>(); } +inline null<> gmtime_s(...) { return null<>(); } +} // namespace detail + +// Thread-safe replacement for std::localtime +inline std::tm localtime(std::time_t time) { + struct dispatcher { + std::time_t time_; + std::tm tm_; + + dispatcher(std::time_t t) : time_(t) {} + + bool run() { + using namespace fmt::detail; + return handle(localtime_r(&time_, &tm_)); + } + + bool handle(std::tm* tm) { return tm != nullptr; } + + bool handle(detail::null<>) { + using namespace fmt::detail; + return fallback(localtime_s(&tm_, &time_)); + } + + bool fallback(int res) { return res == 0; } + +#if !FMT_MSC_VER + bool fallback(detail::null<>) { + using namespace fmt::detail; + std::tm* tm = std::localtime(&time_); + if (tm) tm_ = *tm; + return tm != nullptr; + } +#endif + }; + dispatcher lt(time); + // Too big time values may be unsupported. + if (!lt.run()) FMT_THROW(format_error("time_t value out of range")); + return lt.tm_; +} + +inline std::tm localtime( + std::chrono::time_point time_point) { + return localtime(std::chrono::system_clock::to_time_t(time_point)); +} + +// Thread-safe replacement for std::gmtime +inline std::tm gmtime(std::time_t time) { + struct dispatcher { + std::time_t time_; + std::tm tm_; + + dispatcher(std::time_t t) : time_(t) {} + + bool run() { + using namespace fmt::detail; + return handle(gmtime_r(&time_, &tm_)); + } + + bool handle(std::tm* tm) { return tm != nullptr; } + + bool handle(detail::null<>) { + using namespace fmt::detail; + return fallback(gmtime_s(&tm_, &time_)); + } + + bool fallback(int res) { return res == 0; } + +#if !FMT_MSC_VER + bool fallback(detail::null<>) { + std::tm* tm = std::gmtime(&time_); + if (tm) tm_ = *tm; + return tm != nullptr; + } +#endif + }; + dispatcher gt(time); + // Too big time values may be unsupported. + if (!gt.run()) FMT_THROW(format_error("time_t value out of range")); + return gt.tm_; +} + +inline std::tm gmtime( + std::chrono::time_point time_point) { + return gmtime(std::chrono::system_clock::to_time_t(time_point)); +} + +namespace detail { +inline size_t strftime(char* str, size_t count, const char* format, + const std::tm* time) { + return std::strftime(str, count, format, time); +} + +inline size_t strftime(wchar_t* str, size_t count, const wchar_t* format, + const std::tm* time) { + return std::wcsftime(str, count, format, time); +} +} // namespace detail + +template +struct formatter, Char> + : formatter { + template + auto format(std::chrono::time_point val, + FormatContext& ctx) -> decltype(ctx.out()) { + std::tm time = localtime(val); + return formatter::format(time, ctx); + } +}; + +template struct formatter { + template + auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + auto it = ctx.begin(); + if (it != ctx.end() && *it == ':') ++it; + auto end = it; + while (end != ctx.end() && *end != '}') ++end; + tm_format.reserve(detail::to_unsigned(end - it + 1)); + tm_format.append(it, end); + tm_format.push_back('\0'); + return end; + } + + template + auto format(const std::tm& tm, FormatContext& ctx) -> decltype(ctx.out()) { + basic_memory_buffer buf; + size_t start = buf.size(); + for (;;) { + size_t size = buf.capacity() - start; + size_t count = detail::strftime(&buf[start], size, &tm_format[0], &tm); + if (count != 0) { + buf.resize(start + count); + break; + } + if (size >= tm_format.size() * 256) { + // If the buffer is 256 times larger than the format string, assume + // that `strftime` gives an empty result. There doesn't seem to be a + // better way to distinguish the two cases: + // https://github.com/fmtlib/fmt/issues/367 + break; + } + const size_t MIN_GROWTH = 10; + buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH)); + } + return std::copy(buf.begin(), buf.end(), ctx.out()); + } + + basic_memory_buffer tm_format; +}; + +namespace detail { +template FMT_CONSTEXPR const char* get_units() { + return nullptr; +} +template <> FMT_CONSTEXPR const char* get_units() { return "as"; } +template <> FMT_CONSTEXPR const char* get_units() { return "fs"; } +template <> FMT_CONSTEXPR const char* get_units() { return "ps"; } +template <> FMT_CONSTEXPR const char* get_units() { return "ns"; } +template <> FMT_CONSTEXPR const char* get_units() { return "µs"; } +template <> FMT_CONSTEXPR const char* get_units() { return "ms"; } +template <> FMT_CONSTEXPR const char* get_units() { return "cs"; } +template <> FMT_CONSTEXPR const char* get_units() { return "ds"; } +template <> FMT_CONSTEXPR const char* get_units>() { return "s"; } +template <> FMT_CONSTEXPR const char* get_units() { return "das"; } +template <> FMT_CONSTEXPR const char* get_units() { return "hs"; } +template <> FMT_CONSTEXPR const char* get_units() { return "ks"; } +template <> FMT_CONSTEXPR const char* get_units() { return "Ms"; } +template <> FMT_CONSTEXPR const char* get_units() { return "Gs"; } +template <> FMT_CONSTEXPR const char* get_units() { return "Ts"; } +template <> FMT_CONSTEXPR const char* get_units() { return "Ps"; } +template <> FMT_CONSTEXPR const char* get_units() { return "Es"; } +template <> FMT_CONSTEXPR const char* get_units>() { + return "m"; +} +template <> FMT_CONSTEXPR const char* get_units>() { + return "h"; +} + +enum class numeric_system { + standard, + // Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale. + alternative +}; + +// Parses a put_time-like format string and invokes handler actions. +template +FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, + const Char* end, + Handler&& handler) { + auto ptr = begin; + while (ptr != end) { + auto c = *ptr; + if (c == '}') break; + if (c != '%') { + ++ptr; + continue; + } + if (begin != ptr) handler.on_text(begin, ptr); + ++ptr; // consume '%' + if (ptr == end) FMT_THROW(format_error("invalid format")); + c = *ptr++; + switch (c) { + case '%': + handler.on_text(ptr - 1, ptr); + break; + case 'n': { + const Char newline[] = {'\n'}; + handler.on_text(newline, newline + 1); + break; + } + case 't': { + const Char tab[] = {'\t'}; + handler.on_text(tab, tab + 1); + break; + } + // Day of the week: + case 'a': + handler.on_abbr_weekday(); + break; + case 'A': + handler.on_full_weekday(); + break; + case 'w': + handler.on_dec0_weekday(numeric_system::standard); + break; + case 'u': + handler.on_dec1_weekday(numeric_system::standard); + break; + // Month: + case 'b': + handler.on_abbr_month(); + break; + case 'B': + handler.on_full_month(); + break; + // Hour, minute, second: + case 'H': + handler.on_24_hour(numeric_system::standard); + break; + case 'I': + handler.on_12_hour(numeric_system::standard); + break; + case 'M': + handler.on_minute(numeric_system::standard); + break; + case 'S': + handler.on_second(numeric_system::standard); + break; + // Other: + case 'c': + handler.on_datetime(numeric_system::standard); + break; + case 'x': + handler.on_loc_date(numeric_system::standard); + break; + case 'X': + handler.on_loc_time(numeric_system::standard); + break; + case 'D': + handler.on_us_date(); + break; + case 'F': + handler.on_iso_date(); + break; + case 'r': + handler.on_12_hour_time(); + break; + case 'R': + handler.on_24_hour_time(); + break; + case 'T': + handler.on_iso_time(); + break; + case 'p': + handler.on_am_pm(); + break; + case 'Q': + handler.on_duration_value(); + break; + case 'q': + handler.on_duration_unit(); + break; + case 'z': + handler.on_utc_offset(); + break; + case 'Z': + handler.on_tz_name(); + break; + // Alternative representation: + case 'E': { + if (ptr == end) FMT_THROW(format_error("invalid format")); + c = *ptr++; + switch (c) { + case 'c': + handler.on_datetime(numeric_system::alternative); + break; + case 'x': + handler.on_loc_date(numeric_system::alternative); + break; + case 'X': + handler.on_loc_time(numeric_system::alternative); + break; + default: + FMT_THROW(format_error("invalid format")); + } + break; + } + case 'O': + if (ptr == end) FMT_THROW(format_error("invalid format")); + c = *ptr++; + switch (c) { + case 'w': + handler.on_dec0_weekday(numeric_system::alternative); + break; + case 'u': + handler.on_dec1_weekday(numeric_system::alternative); + break; + case 'H': + handler.on_24_hour(numeric_system::alternative); + break; + case 'I': + handler.on_12_hour(numeric_system::alternative); + break; + case 'M': + handler.on_minute(numeric_system::alternative); + break; + case 'S': + handler.on_second(numeric_system::alternative); + break; + default: + FMT_THROW(format_error("invalid format")); + } + break; + default: + FMT_THROW(format_error("invalid format")); + } + begin = ptr; + } + if (begin != ptr) handler.on_text(begin, ptr); + return ptr; +} + +struct chrono_format_checker { + FMT_NORETURN void report_no_date() { FMT_THROW(format_error("no date")); } + + template void on_text(const Char*, const Char*) {} + FMT_NORETURN void on_abbr_weekday() { report_no_date(); } + FMT_NORETURN void on_full_weekday() { report_no_date(); } + FMT_NORETURN void on_dec0_weekday(numeric_system) { report_no_date(); } + FMT_NORETURN void on_dec1_weekday(numeric_system) { report_no_date(); } + FMT_NORETURN void on_abbr_month() { report_no_date(); } + FMT_NORETURN void on_full_month() { report_no_date(); } + void on_24_hour(numeric_system) {} + void on_12_hour(numeric_system) {} + void on_minute(numeric_system) {} + void on_second(numeric_system) {} + FMT_NORETURN void on_datetime(numeric_system) { report_no_date(); } + FMT_NORETURN void on_loc_date(numeric_system) { report_no_date(); } + FMT_NORETURN void on_loc_time(numeric_system) { report_no_date(); } + FMT_NORETURN void on_us_date() { report_no_date(); } + FMT_NORETURN void on_iso_date() { report_no_date(); } + void on_12_hour_time() {} + void on_24_hour_time() {} + void on_iso_time() {} + void on_am_pm() {} + void on_duration_value() {} + void on_duration_unit() {} + FMT_NORETURN void on_utc_offset() { report_no_date(); } + FMT_NORETURN void on_tz_name() { report_no_date(); } +}; + +template ::value)> +inline bool isnan(T) { + return false; +} +template ::value)> +inline bool isnan(T value) { + return std::isnan(value); +} + +template ::value)> +inline bool isfinite(T) { + return true; +} +template ::value)> +inline bool isfinite(T value) { + return std::isfinite(value); +} + +// Converts value to int and checks that it's in the range [0, upper). +template ::value)> +inline int to_nonnegative_int(T value, int upper) { + FMT_ASSERT(value >= 0 && value <= upper, "invalid value"); + (void)upper; + return static_cast(value); +} +template ::value)> +inline int to_nonnegative_int(T value, int upper) { + FMT_ASSERT( + std::isnan(value) || (value >= 0 && value <= static_cast(upper)), + "invalid value"); + (void)upper; + return static_cast(value); +} + +template ::value)> +inline T mod(T x, int y) { + return x % static_cast(y); +} +template ::value)> +inline T mod(T x, int y) { + return std::fmod(x, static_cast(y)); +} + +// If T is an integral type, maps T to its unsigned counterpart, otherwise +// leaves it unchanged (unlike std::make_unsigned). +template ::value> +struct make_unsigned_or_unchanged { + using type = T; +}; + +template struct make_unsigned_or_unchanged { + using type = typename std::make_unsigned::type; +}; + +#if FMT_SAFE_DURATION_CAST +// throwing version of safe_duration_cast +template +To fmt_safe_duration_cast(std::chrono::duration from) { + int ec; + To to = safe_duration_cast::safe_duration_cast(from, ec); + if (ec) FMT_THROW(format_error("cannot format duration")); + return to; +} +#endif + +template ::value)> +inline std::chrono::duration get_milliseconds( + std::chrono::duration d) { + // this may overflow and/or the result may not fit in the + // target type. +#if FMT_SAFE_DURATION_CAST + using CommonSecondsType = + typename std::common_type::type; + const auto d_as_common = fmt_safe_duration_cast(d); + const auto d_as_whole_seconds = + fmt_safe_duration_cast(d_as_common); + // this conversion should be nonproblematic + const auto diff = d_as_common - d_as_whole_seconds; + const auto ms = + fmt_safe_duration_cast>(diff); + return ms; +#else + auto s = std::chrono::duration_cast(d); + return std::chrono::duration_cast(d - s); +#endif +} + +template ::value)> +inline std::chrono::duration get_milliseconds( + std::chrono::duration d) { + using common_type = typename std::common_type::type; + auto ms = mod(d.count() * static_cast(Period::num) / + static_cast(Period::den) * 1000, + 1000); + return std::chrono::duration(static_cast(ms)); +} + +template +OutputIt format_duration_value(OutputIt out, Rep val, int precision) { + const Char pr_f[] = {'{', ':', '.', '{', '}', 'f', '}', 0}; + if (precision >= 0) return format_to(out, pr_f, val, precision); + const Char fp_f[] = {'{', ':', 'g', '}', 0}; + const Char format[] = {'{', '}', 0}; + return format_to(out, std::is_floating_point::value ? fp_f : format, + val); +} +template +OutputIt copy_unit(string_view unit, OutputIt out, Char) { + return std::copy(unit.begin(), unit.end(), out); +} + +template +OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) { + // This works when wchar_t is UTF-32 because units only contain characters + // that have the same representation in UTF-16 and UTF-32. + utf8_to_utf16 u(unit); + return std::copy(u.c_str(), u.c_str() + u.size(), out); +} + +template +OutputIt format_duration_unit(OutputIt out) { + if (const char* unit = get_units()) + return copy_unit(string_view(unit), out, Char()); + const Char num_f[] = {'[', '{', '}', ']', 's', 0}; + if (const_check(Period::den == 1)) return format_to(out, num_f, Period::num); + const Char num_def_f[] = {'[', '{', '}', '/', '{', '}', ']', 's', 0}; + return format_to(out, num_def_f, Period::num, Period::den); +} + +template +struct chrono_formatter { + FormatContext& context; + OutputIt out; + int precision; + // rep is unsigned to avoid overflow. + using rep = + conditional_t::value && sizeof(Rep) < sizeof(int), + unsigned, typename make_unsigned_or_unchanged::type>; + rep val; + using seconds = std::chrono::duration; + seconds s; + using milliseconds = std::chrono::duration; + bool negative; + + using char_type = typename FormatContext::char_type; + + explicit chrono_formatter(FormatContext& ctx, OutputIt o, + std::chrono::duration d) + : context(ctx), + out(o), + val(static_cast(d.count())), + negative(false) { + if (d.count() < 0) { + val = 0 - val; + negative = true; + } + + // this may overflow and/or the result may not fit in the + // target type. +#if FMT_SAFE_DURATION_CAST + // might need checked conversion (rep!=Rep) + auto tmpval = std::chrono::duration(val); + s = fmt_safe_duration_cast(tmpval); +#else + s = std::chrono::duration_cast( + std::chrono::duration(val)); +#endif + } + + // returns true if nan or inf, writes to out. + bool handle_nan_inf() { + if (isfinite(val)) { + return false; + } + if (isnan(val)) { + write_nan(); + return true; + } + // must be +-inf + if (val > 0) { + write_pinf(); + } else { + write_ninf(); + } + return true; + } + + Rep hour() const { return static_cast(mod((s.count() / 3600), 24)); } + + Rep hour12() const { + Rep hour = static_cast(mod((s.count() / 3600), 12)); + return hour <= 0 ? 12 : hour; + } + + Rep minute() const { return static_cast(mod((s.count() / 60), 60)); } + Rep second() const { return static_cast(mod(s.count(), 60)); } + + std::tm time() const { + auto time = std::tm(); + time.tm_hour = to_nonnegative_int(hour(), 24); + time.tm_min = to_nonnegative_int(minute(), 60); + time.tm_sec = to_nonnegative_int(second(), 60); + return time; + } + + void write_sign() { + if (negative) { + *out++ = '-'; + negative = false; + } + } + + void write(Rep value, int width) { + write_sign(); + if (isnan(value)) return write_nan(); + uint32_or_64_or_128_t n = + to_unsigned(to_nonnegative_int(value, max_value())); + int num_digits = detail::count_digits(n); + if (width > num_digits) out = std::fill_n(out, width - num_digits, '0'); + out = format_decimal(out, n, num_digits).end; + } + + void write_nan() { std::copy_n("nan", 3, out); } + void write_pinf() { std::copy_n("inf", 3, out); } + void write_ninf() { std::copy_n("-inf", 4, out); } + + void format_localized(const tm& time, char format, char modifier = 0) { + if (isnan(val)) return write_nan(); + auto locale = context.locale().template get(); + auto& facet = std::use_facet>(locale); + std::basic_ostringstream os; + os.imbue(locale); + facet.put(os, os, ' ', &time, format, modifier); + auto str = os.str(); + std::copy(str.begin(), str.end(), out); + } + + void on_text(const char_type* begin, const char_type* end) { + std::copy(begin, end, out); + } + + // These are not implemented because durations don't have date information. + void on_abbr_weekday() {} + void on_full_weekday() {} + void on_dec0_weekday(numeric_system) {} + void on_dec1_weekday(numeric_system) {} + void on_abbr_month() {} + void on_full_month() {} + void on_datetime(numeric_system) {} + void on_loc_date(numeric_system) {} + void on_loc_time(numeric_system) {} + void on_us_date() {} + void on_iso_date() {} + void on_utc_offset() {} + void on_tz_name() {} + + void on_24_hour(numeric_system ns) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) return write(hour(), 2); + auto time = tm(); + time.tm_hour = to_nonnegative_int(hour(), 24); + format_localized(time, 'H', 'O'); + } + + void on_12_hour(numeric_system ns) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) return write(hour12(), 2); + auto time = tm(); + time.tm_hour = to_nonnegative_int(hour12(), 12); + format_localized(time, 'I', 'O'); + } + + void on_minute(numeric_system ns) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) return write(minute(), 2); + auto time = tm(); + time.tm_min = to_nonnegative_int(minute(), 60); + format_localized(time, 'M', 'O'); + } + + void on_second(numeric_system ns) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) { + write(second(), 2); +#if FMT_SAFE_DURATION_CAST + // convert rep->Rep + using duration_rep = std::chrono::duration; + using duration_Rep = std::chrono::duration; + auto tmpval = fmt_safe_duration_cast(duration_rep{val}); +#else + auto tmpval = std::chrono::duration(val); +#endif + auto ms = get_milliseconds(tmpval); + if (ms != std::chrono::milliseconds(0)) { + *out++ = '.'; + write(ms.count(), 3); + } + return; + } + auto time = tm(); + time.tm_sec = to_nonnegative_int(second(), 60); + format_localized(time, 'S', 'O'); + } + + void on_12_hour_time() { + if (handle_nan_inf()) return; + format_localized(time(), 'r'); + } + + void on_24_hour_time() { + if (handle_nan_inf()) { + *out++ = ':'; + handle_nan_inf(); + return; + } + + write(hour(), 2); + *out++ = ':'; + write(minute(), 2); + } + + void on_iso_time() { + on_24_hour_time(); + *out++ = ':'; + if (handle_nan_inf()) return; + write(second(), 2); + } + + void on_am_pm() { + if (handle_nan_inf()) return; + format_localized(time(), 'p'); + } + + void on_duration_value() { + if (handle_nan_inf()) return; + write_sign(); + out = format_duration_value(out, val, precision); + } + + void on_duration_unit() { + out = format_duration_unit(out); + } +}; +} // namespace detail + +template +struct formatter, Char> { + private: + basic_format_specs specs; + int precision; + using arg_ref_type = detail::arg_ref; + arg_ref_type width_ref; + arg_ref_type precision_ref; + mutable basic_string_view format_str; + using duration = std::chrono::duration; + + struct spec_handler { + formatter& f; + basic_format_parse_context& context; + basic_string_view format_str; + + template FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) { + context.check_arg_id(arg_id); + return arg_ref_type(arg_id); + } + + FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view arg_id) { + context.check_arg_id(arg_id); + return arg_ref_type(arg_id); + } + + FMT_CONSTEXPR arg_ref_type make_arg_ref(detail::auto_id) { + return arg_ref_type(context.next_arg_id()); + } + + void on_error(const char* msg) { FMT_THROW(format_error(msg)); } + void on_fill(basic_string_view fill) { f.specs.fill = fill; } + void on_align(align_t align) { f.specs.align = align; } + void on_width(int width) { f.specs.width = width; } + void on_precision(int _precision) { f.precision = _precision; } + void end_precision() {} + + template void on_dynamic_width(Id arg_id) { + f.width_ref = make_arg_ref(arg_id); + } + + template void on_dynamic_precision(Id arg_id) { + f.precision_ref = make_arg_ref(arg_id); + } + }; + + using iterator = typename basic_format_parse_context::iterator; + struct parse_range { + iterator begin; + iterator end; + }; + + FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context& ctx) { + auto begin = ctx.begin(), end = ctx.end(); + if (begin == end || *begin == '}') return {begin, begin}; + spec_handler handler{*this, ctx, format_str}; + begin = detail::parse_align(begin, end, handler); + if (begin == end) return {begin, begin}; + begin = detail::parse_width(begin, end, handler); + if (begin == end) return {begin, begin}; + if (*begin == '.') { + if (std::is_floating_point::value) + begin = detail::parse_precision(begin, end, handler); + else + handler.on_error("precision not allowed for this argument type"); + } + end = parse_chrono_format(begin, end, detail::chrono_format_checker()); + return {begin, end}; + } + + public: + formatter() : precision(-1) {} + + FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) + -> decltype(ctx.begin()) { + auto range = do_parse(ctx); + format_str = basic_string_view( + &*range.begin, detail::to_unsigned(range.end - range.begin)); + return range.end; + } + + template + auto format(const duration& d, FormatContext& ctx) -> decltype(ctx.out()) { + auto begin = format_str.begin(), end = format_str.end(); + // As a possible future optimization, we could avoid extra copying if width + // is not specified. + basic_memory_buffer buf; + auto out = std::back_inserter(buf); + detail::handle_dynamic_spec(specs.width, width_ref, + ctx); + detail::handle_dynamic_spec(precision, + precision_ref, ctx); + if (begin == end || *begin == '}') { + out = detail::format_duration_value(out, d.count(), precision); + detail::format_duration_unit(out); + } else { + detail::chrono_formatter f( + ctx, out, d); + f.precision = precision; + parse_chrono_format(begin, end, f); + } + return detail::write( + ctx.out(), basic_string_view(buf.data(), buf.size()), specs); + } +}; + +FMT_END_NAMESPACE + +#endif // FMT_CHRONO_H_ diff --git a/source/fmt/color.h b/source/fmt/color.h new file mode 100644 index 00000000..94e3419d --- /dev/null +++ b/source/fmt/color.h @@ -0,0 +1,603 @@ +// Formatting library for C++ - color support +// +// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_COLOR_H_ +#define FMT_COLOR_H_ + +#include "format.h" + +FMT_BEGIN_NAMESPACE + +enum class color : uint32_t { + alice_blue = 0xF0F8FF, // rgb(240,248,255) + antique_white = 0xFAEBD7, // rgb(250,235,215) + aqua = 0x00FFFF, // rgb(0,255,255) + aquamarine = 0x7FFFD4, // rgb(127,255,212) + azure = 0xF0FFFF, // rgb(240,255,255) + beige = 0xF5F5DC, // rgb(245,245,220) + bisque = 0xFFE4C4, // rgb(255,228,196) + black = 0x000000, // rgb(0,0,0) + blanched_almond = 0xFFEBCD, // rgb(255,235,205) + blue = 0x0000FF, // rgb(0,0,255) + blue_violet = 0x8A2BE2, // rgb(138,43,226) + brown = 0xA52A2A, // rgb(165,42,42) + burly_wood = 0xDEB887, // rgb(222,184,135) + cadet_blue = 0x5F9EA0, // rgb(95,158,160) + chartreuse = 0x7FFF00, // rgb(127,255,0) + chocolate = 0xD2691E, // rgb(210,105,30) + coral = 0xFF7F50, // rgb(255,127,80) + cornflower_blue = 0x6495ED, // rgb(100,149,237) + cornsilk = 0xFFF8DC, // rgb(255,248,220) + crimson = 0xDC143C, // rgb(220,20,60) + cyan = 0x00FFFF, // rgb(0,255,255) + dark_blue = 0x00008B, // rgb(0,0,139) + dark_cyan = 0x008B8B, // rgb(0,139,139) + dark_golden_rod = 0xB8860B, // rgb(184,134,11) + dark_gray = 0xA9A9A9, // rgb(169,169,169) + dark_green = 0x006400, // rgb(0,100,0) + dark_khaki = 0xBDB76B, // rgb(189,183,107) + dark_magenta = 0x8B008B, // rgb(139,0,139) + dark_olive_green = 0x556B2F, // rgb(85,107,47) + dark_orange = 0xFF8C00, // rgb(255,140,0) + dark_orchid = 0x9932CC, // rgb(153,50,204) + dark_red = 0x8B0000, // rgb(139,0,0) + dark_salmon = 0xE9967A, // rgb(233,150,122) + dark_sea_green = 0x8FBC8F, // rgb(143,188,143) + dark_slate_blue = 0x483D8B, // rgb(72,61,139) + dark_slate_gray = 0x2F4F4F, // rgb(47,79,79) + dark_turquoise = 0x00CED1, // rgb(0,206,209) + dark_violet = 0x9400D3, // rgb(148,0,211) + deep_pink = 0xFF1493, // rgb(255,20,147) + deep_sky_blue = 0x00BFFF, // rgb(0,191,255) + dim_gray = 0x696969, // rgb(105,105,105) + dodger_blue = 0x1E90FF, // rgb(30,144,255) + fire_brick = 0xB22222, // rgb(178,34,34) + floral_white = 0xFFFAF0, // rgb(255,250,240) + forest_green = 0x228B22, // rgb(34,139,34) + fuchsia = 0xFF00FF, // rgb(255,0,255) + gainsboro = 0xDCDCDC, // rgb(220,220,220) + ghost_white = 0xF8F8FF, // rgb(248,248,255) + gold = 0xFFD700, // rgb(255,215,0) + golden_rod = 0xDAA520, // rgb(218,165,32) + gray = 0x808080, // rgb(128,128,128) + green = 0x008000, // rgb(0,128,0) + green_yellow = 0xADFF2F, // rgb(173,255,47) + honey_dew = 0xF0FFF0, // rgb(240,255,240) + hot_pink = 0xFF69B4, // rgb(255,105,180) + indian_red = 0xCD5C5C, // rgb(205,92,92) + indigo = 0x4B0082, // rgb(75,0,130) + ivory = 0xFFFFF0, // rgb(255,255,240) + khaki = 0xF0E68C, // rgb(240,230,140) + lavender = 0xE6E6FA, // rgb(230,230,250) + lavender_blush = 0xFFF0F5, // rgb(255,240,245) + lawn_green = 0x7CFC00, // rgb(124,252,0) + lemon_chiffon = 0xFFFACD, // rgb(255,250,205) + light_blue = 0xADD8E6, // rgb(173,216,230) + light_coral = 0xF08080, // rgb(240,128,128) + light_cyan = 0xE0FFFF, // rgb(224,255,255) + light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210) + light_gray = 0xD3D3D3, // rgb(211,211,211) + light_green = 0x90EE90, // rgb(144,238,144) + light_pink = 0xFFB6C1, // rgb(255,182,193) + light_salmon = 0xFFA07A, // rgb(255,160,122) + light_sea_green = 0x20B2AA, // rgb(32,178,170) + light_sky_blue = 0x87CEFA, // rgb(135,206,250) + light_slate_gray = 0x778899, // rgb(119,136,153) + light_steel_blue = 0xB0C4DE, // rgb(176,196,222) + light_yellow = 0xFFFFE0, // rgb(255,255,224) + lime = 0x00FF00, // rgb(0,255,0) + lime_green = 0x32CD32, // rgb(50,205,50) + linen = 0xFAF0E6, // rgb(250,240,230) + magenta = 0xFF00FF, // rgb(255,0,255) + maroon = 0x800000, // rgb(128,0,0) + medium_aquamarine = 0x66CDAA, // rgb(102,205,170) + medium_blue = 0x0000CD, // rgb(0,0,205) + medium_orchid = 0xBA55D3, // rgb(186,85,211) + medium_purple = 0x9370DB, // rgb(147,112,219) + medium_sea_green = 0x3CB371, // rgb(60,179,113) + medium_slate_blue = 0x7B68EE, // rgb(123,104,238) + medium_spring_green = 0x00FA9A, // rgb(0,250,154) + medium_turquoise = 0x48D1CC, // rgb(72,209,204) + medium_violet_red = 0xC71585, // rgb(199,21,133) + midnight_blue = 0x191970, // rgb(25,25,112) + mint_cream = 0xF5FFFA, // rgb(245,255,250) + misty_rose = 0xFFE4E1, // rgb(255,228,225) + moccasin = 0xFFE4B5, // rgb(255,228,181) + navajo_white = 0xFFDEAD, // rgb(255,222,173) + navy = 0x000080, // rgb(0,0,128) + old_lace = 0xFDF5E6, // rgb(253,245,230) + olive = 0x808000, // rgb(128,128,0) + olive_drab = 0x6B8E23, // rgb(107,142,35) + orange = 0xFFA500, // rgb(255,165,0) + orange_red = 0xFF4500, // rgb(255,69,0) + orchid = 0xDA70D6, // rgb(218,112,214) + pale_golden_rod = 0xEEE8AA, // rgb(238,232,170) + pale_green = 0x98FB98, // rgb(152,251,152) + pale_turquoise = 0xAFEEEE, // rgb(175,238,238) + pale_violet_red = 0xDB7093, // rgb(219,112,147) + papaya_whip = 0xFFEFD5, // rgb(255,239,213) + peach_puff = 0xFFDAB9, // rgb(255,218,185) + peru = 0xCD853F, // rgb(205,133,63) + pink = 0xFFC0CB, // rgb(255,192,203) + plum = 0xDDA0DD, // rgb(221,160,221) + powder_blue = 0xB0E0E6, // rgb(176,224,230) + purple = 0x800080, // rgb(128,0,128) + rebecca_purple = 0x663399, // rgb(102,51,153) + red = 0xFF0000, // rgb(255,0,0) + rosy_brown = 0xBC8F8F, // rgb(188,143,143) + royal_blue = 0x4169E1, // rgb(65,105,225) + saddle_brown = 0x8B4513, // rgb(139,69,19) + salmon = 0xFA8072, // rgb(250,128,114) + sandy_brown = 0xF4A460, // rgb(244,164,96) + sea_green = 0x2E8B57, // rgb(46,139,87) + sea_shell = 0xFFF5EE, // rgb(255,245,238) + sienna = 0xA0522D, // rgb(160,82,45) + silver = 0xC0C0C0, // rgb(192,192,192) + sky_blue = 0x87CEEB, // rgb(135,206,235) + slate_blue = 0x6A5ACD, // rgb(106,90,205) + slate_gray = 0x708090, // rgb(112,128,144) + snow = 0xFFFAFA, // rgb(255,250,250) + spring_green = 0x00FF7F, // rgb(0,255,127) + steel_blue = 0x4682B4, // rgb(70,130,180) + tan = 0xD2B48C, // rgb(210,180,140) + teal = 0x008080, // rgb(0,128,128) + thistle = 0xD8BFD8, // rgb(216,191,216) + tomato = 0xFF6347, // rgb(255,99,71) + turquoise = 0x40E0D0, // rgb(64,224,208) + violet = 0xEE82EE, // rgb(238,130,238) + wheat = 0xF5DEB3, // rgb(245,222,179) + white = 0xFFFFFF, // rgb(255,255,255) + white_smoke = 0xF5F5F5, // rgb(245,245,245) + yellow = 0xFFFF00, // rgb(255,255,0) + yellow_green = 0x9ACD32 // rgb(154,205,50) +}; // enum class color + +enum class terminal_color : uint8_t { + black = 30, + red, + green, + yellow, + blue, + magenta, + cyan, + white, + bright_black = 90, + bright_red, + bright_green, + bright_yellow, + bright_blue, + bright_magenta, + bright_cyan, + bright_white +}; + +enum class emphasis : uint8_t { + bold = 1, + italic = 1 << 1, + underline = 1 << 2, + strikethrough = 1 << 3 +}; + +// rgb is a struct for red, green and blue colors. +// Using the name "rgb" makes some editors show the color in a tooltip. +struct rgb { + FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {} + FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {} + FMT_CONSTEXPR rgb(uint32_t hex) + : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {} + FMT_CONSTEXPR rgb(color hex) + : r((uint32_t(hex) >> 16) & 0xFF), + g((uint32_t(hex) >> 8) & 0xFF), + b(uint32_t(hex) & 0xFF) {} + uint8_t r; + uint8_t g; + uint8_t b; +}; + +namespace detail { + +// color is a struct of either a rgb color or a terminal color. +struct color_type { + FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {} + FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true), + value{} { + value.rgb_color = static_cast(rgb_color); + } + FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} { + value.rgb_color = (static_cast(rgb_color.r) << 16) | + (static_cast(rgb_color.g) << 8) | rgb_color.b; + } + FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(), + value{} { + value.term_color = static_cast(term_color); + } + bool is_rgb; + union color_union { + uint8_t term_color; + uint32_t rgb_color; + } value; +}; +} // namespace detail + +// Experimental text formatting support. +class text_style { + public: + FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT + : set_foreground_color(), + set_background_color(), + ems(em) {} + + FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) { + if (!set_foreground_color) { + set_foreground_color = rhs.set_foreground_color; + foreground_color = rhs.foreground_color; + } else if (rhs.set_foreground_color) { + if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) + FMT_THROW(format_error("can't OR a terminal color")); + foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color; + } + + if (!set_background_color) { + set_background_color = rhs.set_background_color; + background_color = rhs.background_color; + } else if (rhs.set_background_color) { + if (!background_color.is_rgb || !rhs.background_color.is_rgb) + FMT_THROW(format_error("can't OR a terminal color")); + background_color.value.rgb_color |= rhs.background_color.value.rgb_color; + } + + ems = static_cast(static_cast(ems) | + static_cast(rhs.ems)); + return *this; + } + + friend FMT_CONSTEXPR text_style operator|(text_style lhs, + const text_style& rhs) { + return lhs |= rhs; + } + + FMT_CONSTEXPR text_style& operator&=(const text_style& rhs) { + if (!set_foreground_color) { + set_foreground_color = rhs.set_foreground_color; + foreground_color = rhs.foreground_color; + } else if (rhs.set_foreground_color) { + if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) + FMT_THROW(format_error("can't AND a terminal color")); + foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color; + } + + if (!set_background_color) { + set_background_color = rhs.set_background_color; + background_color = rhs.background_color; + } else if (rhs.set_background_color) { + if (!background_color.is_rgb || !rhs.background_color.is_rgb) + FMT_THROW(format_error("can't AND a terminal color")); + background_color.value.rgb_color &= rhs.background_color.value.rgb_color; + } + + ems = static_cast(static_cast(ems) & + static_cast(rhs.ems)); + return *this; + } + + friend FMT_CONSTEXPR text_style operator&(text_style lhs, + const text_style& rhs) { + return lhs &= rhs; + } + + FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT { + return set_foreground_color; + } + FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT { + return set_background_color; + } + FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT { + return static_cast(ems) != 0; + } + FMT_CONSTEXPR detail::color_type get_foreground() const FMT_NOEXCEPT { + FMT_ASSERT(has_foreground(), "no foreground specified for this style"); + return foreground_color; + } + FMT_CONSTEXPR detail::color_type get_background() const FMT_NOEXCEPT { + FMT_ASSERT(has_background(), "no background specified for this style"); + return background_color; + } + FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT { + FMT_ASSERT(has_emphasis(), "no emphasis specified for this style"); + return ems; + } + + private: + FMT_CONSTEXPR text_style(bool is_foreground, + detail::color_type text_color) FMT_NOEXCEPT + : set_foreground_color(), + set_background_color(), + ems() { + if (is_foreground) { + foreground_color = text_color; + set_foreground_color = true; + } else { + background_color = text_color; + set_background_color = true; + } + } + + friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground) + FMT_NOEXCEPT; + friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background) + FMT_NOEXCEPT; + + detail::color_type foreground_color; + detail::color_type background_color; + bool set_foreground_color; + bool set_background_color; + emphasis ems; +}; + +FMT_CONSTEXPR text_style fg(detail::color_type foreground) FMT_NOEXCEPT { + return text_style(/*is_foreground=*/true, foreground); +} + +FMT_CONSTEXPR text_style bg(detail::color_type background) FMT_NOEXCEPT { + return text_style(/*is_foreground=*/false, background); +} + +FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT { + return text_style(lhs) | rhs; +} + +namespace detail { + +template struct ansi_color_escape { + FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color, + const char* esc) FMT_NOEXCEPT { + // If we have a terminal color, we need to output another escape code + // sequence. + if (!text_color.is_rgb) { + bool is_background = esc == detail::data::background_color; + uint32_t value = text_color.value.term_color; + // Background ASCII codes are the same as the foreground ones but with + // 10 more. + if (is_background) value += 10u; + + size_t index = 0; + buffer[index++] = static_cast('\x1b'); + buffer[index++] = static_cast('['); + + if (value >= 100u) { + buffer[index++] = static_cast('1'); + value %= 100u; + } + buffer[index++] = static_cast('0' + value / 10u); + buffer[index++] = static_cast('0' + value % 10u); + + buffer[index++] = static_cast('m'); + buffer[index++] = static_cast('\0'); + return; + } + + for (int i = 0; i < 7; i++) { + buffer[i] = static_cast(esc[i]); + } + rgb color(text_color.value.rgb_color); + to_esc(color.r, buffer + 7, ';'); + to_esc(color.g, buffer + 11, ';'); + to_esc(color.b, buffer + 15, 'm'); + buffer[19] = static_cast(0); + } + FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT { + uint8_t em_codes[4] = {}; + uint8_t em_bits = static_cast(em); + if (em_bits & static_cast(emphasis::bold)) em_codes[0] = 1; + if (em_bits & static_cast(emphasis::italic)) em_codes[1] = 3; + if (em_bits & static_cast(emphasis::underline)) em_codes[2] = 4; + if (em_bits & static_cast(emphasis::strikethrough)) + em_codes[3] = 9; + + size_t index = 0; + for (int i = 0; i < 4; ++i) { + if (!em_codes[i]) continue; + buffer[index++] = static_cast('\x1b'); + buffer[index++] = static_cast('['); + buffer[index++] = static_cast('0' + em_codes[i]); + buffer[index++] = static_cast('m'); + } + buffer[index++] = static_cast(0); + } + FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; } + + FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; } + FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT { + return buffer + std::char_traits::length(buffer); + } + + private: + Char buffer[7u + 3u * 4u + 1u]; + + static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, + char delimiter) FMT_NOEXCEPT { + out[0] = static_cast('0' + c / 100); + out[1] = static_cast('0' + c / 10 % 10); + out[2] = static_cast('0' + c % 10); + out[3] = static_cast(delimiter); + } +}; + +template +FMT_CONSTEXPR ansi_color_escape make_foreground_color( + detail::color_type foreground) FMT_NOEXCEPT { + return ansi_color_escape(foreground, detail::data::foreground_color); +} + +template +FMT_CONSTEXPR ansi_color_escape make_background_color( + detail::color_type background) FMT_NOEXCEPT { + return ansi_color_escape(background, detail::data::background_color); +} + +template +FMT_CONSTEXPR ansi_color_escape make_emphasis(emphasis em) FMT_NOEXCEPT { + return ansi_color_escape(em); +} + +template +inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT { + std::fputs(chars, stream); +} + +template <> +inline void fputs(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT { + std::fputws(chars, stream); +} + +template inline void reset_color(FILE* stream) FMT_NOEXCEPT { + fputs(detail::data::reset_color, stream); +} + +template <> inline void reset_color(FILE* stream) FMT_NOEXCEPT { + fputs(detail::data::wreset_color, stream); +} + +template +inline void reset_color(buffer& buffer) FMT_NOEXCEPT { + const char* begin = data::reset_color; + const char* end = begin + sizeof(data::reset_color) - 1; + buffer.append(begin, end); +} + +template +void vformat_to(buffer& buf, const text_style& ts, + basic_string_view format_str, + basic_format_args>> args) { + bool has_style = false; + if (ts.has_emphasis()) { + has_style = true; + auto emphasis = detail::make_emphasis(ts.get_emphasis()); + buf.append(emphasis.begin(), emphasis.end()); + } + if (ts.has_foreground()) { + has_style = true; + auto foreground = detail::make_foreground_color(ts.get_foreground()); + buf.append(foreground.begin(), foreground.end()); + } + if (ts.has_background()) { + has_style = true; + auto background = detail::make_background_color(ts.get_background()); + buf.append(background.begin(), background.end()); + } + detail::vformat_to(buf, format_str, args); + if (has_style) detail::reset_color(buf); +} +} // namespace detail + +template > +void vprint(std::FILE* f, const text_style& ts, const S& format, + basic_format_args>> args) { + basic_memory_buffer buf; + detail::vformat_to(buf, ts, to_string_view(format), args); + buf.push_back(Char(0)); + detail::fputs(buf.data(), f); +} + +/** + \rst + Formats a string and prints it to the specified file stream using ANSI + escape sequences to specify text formatting. + + **Example**:: + + fmt::print(fmt::emphasis::bold | fg(fmt::color::red), + "Elapsed time: {0:.2f} seconds", 1.23); + \endrst + */ +template ::value)> +void print(std::FILE* f, const text_style& ts, const S& format_str, + const Args&... args) { + vprint(f, ts, format_str, + fmt::make_args_checked(format_str, args...)); +} + +/** + Formats a string and prints it to stdout using ANSI escape sequences to + specify text formatting. + Example: + fmt::print(fmt::emphasis::bold | fg(fmt::color::red), + "Elapsed time: {0:.2f} seconds", 1.23); + */ +template ::value)> +void print(const text_style& ts, const S& format_str, const Args&... args) { + return print(stdout, ts, format_str, args...); +} + +template > +inline std::basic_string vformat( + const text_style& ts, const S& format_str, + basic_format_args>> args) { + basic_memory_buffer buf; + detail::vformat_to(buf, ts, to_string_view(format_str), args); + return fmt::to_string(buf); +} + +/** + \rst + Formats arguments and returns the result as a string using ANSI + escape sequences to specify text formatting. + + **Example**:: + + #include + std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), + "The answer is {}", 42); + \endrst +*/ +template > +inline std::basic_string format(const text_style& ts, const S& format_str, + const Args&... args) { + return vformat(ts, to_string_view(format_str), + fmt::make_args_checked(format_str, args...)); +} + +/** + Formats a string with the given text_style and writes the output to ``out``. + */ +template ::value)> +OutputIt vformat_to( + OutputIt out, const text_style& ts, basic_string_view format_str, + basic_format_args>> args) { + decltype(detail::get_buffer(out)) buf(detail::get_buffer_init(out)); + detail::vformat_to(buf, ts, format_str, args); + return detail::get_iterator(buf); +} + +/** + \rst + Formats arguments with the given text_style, writes the result to the output + iterator ``out`` and returns the iterator past the end of the output range. + + **Example**:: + + std::vector out; + fmt::format_to(std::back_inserter(out), + fmt::emphasis::bold | fg(fmt::color::red), "{}", 42); + \endrst +*/ +template >::value&& + detail::is_string::value> +inline auto format_to(OutputIt out, const text_style& ts, const S& format_str, + Args&&... args) -> + typename std::enable_if::type { + return vformat_to(out, ts, to_string_view(format_str), + fmt::make_args_checked(format_str, args...)); +} + +FMT_END_NAMESPACE + +#endif // FMT_COLOR_H_ diff --git a/source/fmt/compile.h b/source/fmt/compile.h new file mode 100644 index 00000000..3a33b020 --- /dev/null +++ b/source/fmt/compile.h @@ -0,0 +1,701 @@ +// Formatting library for C++ - experimental format string compilation +// +// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_COMPILE_H_ +#define FMT_COMPILE_H_ + +#include + +#include "format.h" + +FMT_BEGIN_NAMESPACE +namespace detail { + +// A compile-time string which is compiled into fast formatting code. +class compiled_string {}; + +template +struct is_compiled_string : std::is_base_of {}; + +/** + \rst + Converts a string literal *s* into a format string that will be parsed at + compile time and converted into efficient formatting code. Requires C++17 + ``constexpr if`` compiler support. + + **Example**:: + + // Converts 42 into std::string using the most efficient method and no + // runtime format string processing. + std::string s = fmt::format(FMT_COMPILE("{}"), 42); + \endrst + */ +#define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::detail::compiled_string) + +template +const T& first(const T& value, const Tail&...) { + return value; +} + +// Part of a compiled format string. It can be either literal text or a +// replacement field. +template struct format_part { + enum class kind { arg_index, arg_name, text, replacement }; + + struct replacement { + arg_ref arg_id; + dynamic_format_specs specs; + }; + + kind part_kind; + union value { + int arg_index; + basic_string_view str; + replacement repl; + + FMT_CONSTEXPR value(int index = 0) : arg_index(index) {} + FMT_CONSTEXPR value(basic_string_view s) : str(s) {} + FMT_CONSTEXPR value(replacement r) : repl(r) {} + } val; + // Position past the end of the argument id. + const Char* arg_id_end = nullptr; + + FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {}) + : part_kind(k), val(v) {} + + static FMT_CONSTEXPR format_part make_arg_index(int index) { + return format_part(kind::arg_index, index); + } + static FMT_CONSTEXPR format_part make_arg_name(basic_string_view name) { + return format_part(kind::arg_name, name); + } + static FMT_CONSTEXPR format_part make_text(basic_string_view text) { + return format_part(kind::text, text); + } + static FMT_CONSTEXPR format_part make_replacement(replacement repl) { + return format_part(kind::replacement, repl); + } +}; + +template struct part_counter { + unsigned num_parts = 0; + + FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { + if (begin != end) ++num_parts; + } + + FMT_CONSTEXPR int on_arg_id() { return ++num_parts, 0; } + FMT_CONSTEXPR int on_arg_id(int) { return ++num_parts, 0; } + FMT_CONSTEXPR int on_arg_id(basic_string_view) { + return ++num_parts, 0; + } + + FMT_CONSTEXPR void on_replacement_field(int, const Char*) {} + + FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin, + const Char* end) { + // Find the matching brace. + unsigned brace_counter = 0; + for (; begin != end; ++begin) { + if (*begin == '{') { + ++brace_counter; + } else if (*begin == '}') { + if (brace_counter == 0u) break; + --brace_counter; + } + } + return begin; + } + + FMT_CONSTEXPR void on_error(const char*) {} +}; + +// Counts the number of parts in a format string. +template +FMT_CONSTEXPR unsigned count_parts(basic_string_view format_str) { + part_counter counter; + parse_format_string(format_str, counter); + return counter.num_parts; +} + +template +class format_string_compiler : public error_handler { + private: + using part = format_part; + + PartHandler handler_; + part part_; + basic_string_view format_str_; + basic_format_parse_context parse_context_; + + public: + FMT_CONSTEXPR format_string_compiler(basic_string_view format_str, + PartHandler handler) + : handler_(handler), + format_str_(format_str), + parse_context_(format_str) {} + + FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { + if (begin != end) + handler_(part::make_text({begin, to_unsigned(end - begin)})); + } + + FMT_CONSTEXPR int on_arg_id() { + part_ = part::make_arg_index(parse_context_.next_arg_id()); + return 0; + } + + FMT_CONSTEXPR int on_arg_id(int id) { + parse_context_.check_arg_id(id); + part_ = part::make_arg_index(id); + return 0; + } + + FMT_CONSTEXPR int on_arg_id(basic_string_view id) { + part_ = part::make_arg_name(id); + return 0; + } + + FMT_CONSTEXPR void on_replacement_field(int, const Char* ptr) { + part_.arg_id_end = ptr; + handler_(part_); + } + + FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin, + const Char* end) { + auto repl = typename part::replacement(); + dynamic_specs_handler> handler( + repl.specs, parse_context_); + auto it = parse_format_specs(begin, end, handler); + if (*it != '}') on_error("missing '}' in format string"); + repl.arg_id = part_.part_kind == part::kind::arg_index + ? arg_ref(part_.val.arg_index) + : arg_ref(part_.val.str); + auto part = part::make_replacement(repl); + part.arg_id_end = begin; + handler_(part); + return it; + } +}; + +// Compiles a format string and invokes handler(part) for each parsed part. +template +FMT_CONSTEXPR void compile_format_string(basic_string_view format_str, + PartHandler handler) { + parse_format_string( + format_str, + format_string_compiler(format_str, handler)); +} + +template +void format_arg( + basic_format_parse_context& parse_ctx, + Context& ctx, Id arg_id) { + ctx.advance_to(visit_format_arg( + arg_formatter(ctx, &parse_ctx), + ctx.arg(arg_id))); +} + +// vformat_to is defined in a subnamespace to prevent ADL. +namespace cf { +template +auto vformat_to(OutputIt out, CompiledFormat& cf, + basic_format_args args) -> typename Context::iterator { + using char_type = typename Context::char_type; + basic_format_parse_context parse_ctx( + to_string_view(cf.format_str_)); + Context ctx(out, args); + + const auto& parts = cf.parts(); + for (auto part_it = std::begin(parts); part_it != std::end(parts); + ++part_it) { + const auto& part = *part_it; + const auto& value = part.val; + + using format_part_t = format_part; + switch (part.part_kind) { + case format_part_t::kind::text: { + const auto text = value.str; + auto output = ctx.out(); + auto&& it = reserve(output, text.size()); + it = std::copy_n(text.begin(), text.size(), it); + ctx.advance_to(output); + break; + } + + case format_part_t::kind::arg_index: + advance_to(parse_ctx, part.arg_id_end); + detail::format_arg(parse_ctx, ctx, value.arg_index); + break; + + case format_part_t::kind::arg_name: + advance_to(parse_ctx, part.arg_id_end); + detail::format_arg(parse_ctx, ctx, value.str); + break; + + case format_part_t::kind::replacement: { + const auto& arg_id_value = value.repl.arg_id.val; + const auto arg = value.repl.arg_id.kind == arg_id_kind::index + ? ctx.arg(arg_id_value.index) + : ctx.arg(arg_id_value.name); + + auto specs = value.repl.specs; + + handle_dynamic_spec(specs.width, specs.width_ref, ctx); + handle_dynamic_spec(specs.precision, + specs.precision_ref, ctx); + + error_handler h; + numeric_specs_checker checker(h, arg.type()); + if (specs.align == align::numeric) checker.require_numeric_argument(); + if (specs.sign != sign::none) checker.check_sign(); + if (specs.alt) checker.require_numeric_argument(); + if (specs.precision >= 0) checker.check_precision(); + + advance_to(parse_ctx, part.arg_id_end); + ctx.advance_to( + visit_format_arg(arg_formatter( + ctx, nullptr, &specs), + arg)); + break; + } + } + } + return ctx.out(); +} +} // namespace cf + +struct basic_compiled_format {}; + +template +struct compiled_format_base : basic_compiled_format { + using char_type = char_t; + using parts_container = std::vector>; + + parts_container compiled_parts; + + explicit compiled_format_base(basic_string_view format_str) { + compile_format_string(format_str, + [this](const format_part& part) { + compiled_parts.push_back(part); + }); + } + + const parts_container& parts() const { return compiled_parts; } +}; + +template struct format_part_array { + format_part data[N] = {}; + FMT_CONSTEXPR format_part_array() = default; +}; + +template +FMT_CONSTEXPR format_part_array compile_to_parts( + basic_string_view format_str) { + format_part_array parts; + unsigned counter = 0; + // This is not a lambda for compatibility with older compilers. + struct { + format_part* parts; + unsigned* counter; + FMT_CONSTEXPR void operator()(const format_part& part) { + parts[(*counter)++] = part; + } + } collector{parts.data, &counter}; + compile_format_string(format_str, collector); + if (counter < N) { + parts.data[counter] = + format_part::make_text(basic_string_view()); + } + return parts; +} + +template constexpr const T& constexpr_max(const T& a, const T& b) { + return (a < b) ? b : a; +} + +template +struct compiled_format_base::value>> + : basic_compiled_format { + using char_type = char_t; + + FMT_CONSTEXPR explicit compiled_format_base(basic_string_view) {} + +// Workaround for old compilers. Format string compilation will not be +// performed there anyway. +#if FMT_USE_CONSTEXPR + static FMT_CONSTEXPR_DECL const unsigned num_format_parts = + constexpr_max(count_parts(to_string_view(S())), 1u); +#else + static const unsigned num_format_parts = 1; +#endif + + using parts_container = format_part[num_format_parts]; + + const parts_container& parts() const { + static FMT_CONSTEXPR_DECL const auto compiled_parts = + compile_to_parts( + detail::to_string_view(S())); + return compiled_parts.data; + } +}; + +template +class compiled_format : private compiled_format_base { + public: + using typename compiled_format_base::char_type; + + private: + basic_string_view format_str_; + + template + friend auto cf::vformat_to(OutputIt out, CompiledFormat& cf, + basic_format_args args) -> + typename Context::iterator; + + public: + compiled_format() = delete; + explicit constexpr compiled_format(basic_string_view format_str) + : compiled_format_base(format_str), format_str_(format_str) {} +}; + +#ifdef __cpp_if_constexpr +template struct type_list {}; + +// Returns a reference to the argument at index N from [first, rest...]. +template +constexpr const auto& get([[maybe_unused]] const T& first, + [[maybe_unused]] const Args&... rest) { + static_assert(N < 1 + sizeof...(Args), "index is out of bounds"); + if constexpr (N == 0) + return first; + else + return get(rest...); +} + +template struct get_type_impl; + +template struct get_type_impl> { + using type = remove_cvref_t(std::declval()...))>; +}; + +template +using get_type = typename get_type_impl::type; + +template struct is_compiled_format : std::false_type {}; + +template struct text { + basic_string_view data; + using char_type = Char; + + template + OutputIt format(OutputIt out, const Args&...) const { + return write(out, data); + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +template +constexpr text make_text(basic_string_view s, size_t pos, + size_t size) { + return {{&s[pos], size}}; +} + +template struct code_unit { + Char value; + using char_type = Char; + + template + OutputIt format(OutputIt out, const Args&...) const { + return write(out, value); + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +// A replacement field that refers to argument N. +template struct field { + using char_type = Char; + + template + OutputIt format(OutputIt out, const Args&... args) const { + // This ensures that the argument type is convertile to `const T&`. + const T& arg = get(args...); + return write(out, arg); + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +// A replacement field that refers to argument N and has format specifiers. +template struct spec_field { + using char_type = Char; + mutable formatter fmt; + + template + OutputIt format(OutputIt out, const Args&... args) const { + // This ensures that the argument type is convertile to `const T&`. + const T& arg = get(args...); + const auto& vargs = + make_format_args>(args...); + basic_format_context ctx(out, vargs); + return fmt.format(arg, ctx); + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +template struct concat { + L lhs; + R rhs; + using char_type = typename L::char_type; + + template + OutputIt format(OutputIt out, const Args&... args) const { + out = lhs.format(out, args...); + return rhs.format(out, args...); + } +}; + +template +struct is_compiled_format> : std::true_type {}; + +template +constexpr concat make_concat(L lhs, R rhs) { + return {lhs, rhs}; +} + +struct unknown_format {}; + +template +constexpr size_t parse_text(basic_string_view str, size_t pos) { + for (size_t size = str.size(); pos != size; ++pos) { + if (str[pos] == '{' || str[pos] == '}') break; + } + return pos; +} + +template +constexpr auto compile_format_string(S format_str); + +template +constexpr auto parse_tail(T head, S format_str) { + if constexpr (POS != + basic_string_view(format_str).size()) { + constexpr auto tail = compile_format_string(format_str); + if constexpr (std::is_same, + unknown_format>()) + return tail; + else + return make_concat(head, tail); + } else { + return head; + } +} + +template struct parse_specs_result { + formatter fmt; + size_t end; + int next_arg_id; +}; + +template +constexpr parse_specs_result parse_specs(basic_string_view str, + size_t pos, int arg_id) { + str.remove_prefix(pos); + auto ctx = basic_format_parse_context(str, {}, arg_id + 1); + auto f = formatter(); + auto end = f.parse(ctx); + return {f, pos + (end - str.data()) + 1, ctx.next_arg_id()}; +} + +// Compiles a non-empty format string and returns the compiled representation +// or unknown_format() on unrecognized input. +template +constexpr auto compile_format_string(S format_str) { + using char_type = typename S::char_type; + constexpr basic_string_view str = format_str; + if constexpr (str[POS] == '{') { + if (POS + 1 == str.size()) + throw format_error("unmatched '{' in format string"); + if constexpr (str[POS + 1] == '{') { + return parse_tail(make_text(str, POS, 1), format_str); + } else if constexpr (str[POS + 1] == '}') { + using type = get_type; + return parse_tail(field(), + format_str); + } else if constexpr (str[POS + 1] == ':') { + using type = get_type; + constexpr auto result = parse_specs(str, POS + 2, ID); + return parse_tail( + spec_field{result.fmt}, format_str); + } else { + return unknown_format(); + } + } else if constexpr (str[POS] == '}') { + if (POS + 1 == str.size()) + throw format_error("unmatched '}' in format string"); + return parse_tail(make_text(str, POS, 1), format_str); + } else { + constexpr auto end = parse_text(str, POS + 1); + if constexpr (end - POS > 1) { + return parse_tail(make_text(str, POS, end - POS), + format_str); + } else { + return parse_tail(code_unit{str[POS]}, + format_str); + } + } +} + +template ::value || + detail::is_compiled_string::value)> +constexpr auto compile(S format_str) { + constexpr basic_string_view str = format_str; + if constexpr (str.size() == 0) { + return detail::make_text(str, 0, 0); + } else { + constexpr auto result = + detail::compile_format_string, 0, 0>( + format_str); + if constexpr (std::is_same, + detail::unknown_format>()) { + return detail::compiled_format(to_string_view(format_str)); + } else { + return result; + } + } +} +#else +template ::value)> +constexpr auto compile(S format_str) -> detail::compiled_format { + return detail::compiled_format(to_string_view(format_str)); +} +#endif // __cpp_if_constexpr + +// Compiles the format string which must be a string literal. +template +auto compile(const Char (&format_str)[N]) + -> detail::compiled_format { + return detail::compiled_format( + basic_string_view(format_str, N - 1)); +} +} // namespace detail + +// DEPRECATED! use FMT_COMPILE instead. +template +FMT_DEPRECATED auto compile(const Args&... args) + -> decltype(detail::compile(args...)) { + return detail::compile(args...); +} + +#if FMT_USE_CONSTEXPR +# ifdef __cpp_if_constexpr + +template ::value)> +FMT_INLINE std::basic_string format(const CompiledFormat& cf, + const Args&... args) { + basic_memory_buffer buffer; + cf.format(detail::buffer_appender(buffer), args...); + return to_string(buffer); +} + +template ::value)> +OutputIt format_to(OutputIt out, const CompiledFormat& cf, + const Args&... args) { + return cf.format(out, args...); +} +# endif // __cpp_if_constexpr +#endif // FMT_USE_CONSTEXPR + +template ::value)> +std::basic_string format(const CompiledFormat& cf, const Args&... args) { + basic_memory_buffer buffer; + using context = buffer_context; + detail::cf::vformat_to(detail::buffer_appender(buffer), cf, + make_format_args(args...)); + return to_string(buffer); +} + +template ::value)> +FMT_INLINE std::basic_string format(const S&, + Args&&... args) { +#ifdef __cpp_if_constexpr + if constexpr (std::is_same::value) { + constexpr basic_string_view str = S(); + if (str.size() == 2 && str[0] == '{' && str[1] == '}') + return fmt::to_string(detail::first(args...)); + } +#endif + constexpr auto compiled = detail::compile(S()); + return format(compiled, std::forward(args)...); +} + +template ::value)> +OutputIt format_to(OutputIt out, const CompiledFormat& cf, + const Args&... args) { + using char_type = typename CompiledFormat::char_type; + using context = format_context_t; + return detail::cf::vformat_to(out, cf, + make_format_args(args...)); +} + +template ::value)> +OutputIt format_to(OutputIt out, const S&, const Args&... args) { + constexpr auto compiled = detail::compile(S()); + return format_to(out, compiled, args...); +} + +template +auto format_to_n(OutputIt out, size_t n, const CompiledFormat& cf, + const Args&... args) -> + typename std::enable_if< + detail::is_output_iterator::value && + std::is_base_of::value, + format_to_n_result>::type { + auto it = + format_to(detail::truncating_iterator(out, n), cf, args...); + return {it.base(), it.count()}; +} + +template ::value)> +format_to_n_result format_to_n(OutputIt out, size_t n, const S&, + const Args&... args) { + constexpr auto compiled = detail::compile(S()); + auto it = format_to(detail::truncating_iterator(out, n), compiled, + args...); + return {it.base(), it.count()}; +} + +template +size_t formatted_size(const CompiledFormat& cf, const Args&... args) { + return format_to(detail::counting_iterator(), cf, args...).count(); +} + +FMT_END_NAMESPACE + +#endif // FMT_COMPILE_H_ diff --git a/source/fmt/core.h b/source/fmt/core.h new file mode 100644 index 00000000..0a81e0cc --- /dev/null +++ b/source/fmt/core.h @@ -0,0 +1,2122 @@ +// Formatting library for C++ - the core API +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_CORE_H_ +#define FMT_CORE_H_ + +#include // std::FILE +#include +#include +#include +#include +#include +#include +#include + +// The fmt library version in the form major * 10000 + minor * 100 + patch. +#define FMT_VERSION 70103 + +#ifdef __clang__ +# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) +#else +# define FMT_CLANG_VERSION 0 +#endif + +#if defined(__GNUC__) && !defined(__clang__) +# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) +#else +# define FMT_GCC_VERSION 0 +#endif + +#if defined(__INTEL_COMPILER) +# define FMT_ICC_VERSION __INTEL_COMPILER +#else +# define FMT_ICC_VERSION 0 +#endif + +#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define FMT_HAS_GXX_CXX11 FMT_GCC_VERSION +#else +# define FMT_HAS_GXX_CXX11 0 +#endif + +#ifdef __NVCC__ +# define FMT_NVCC __NVCC__ +#else +# define FMT_NVCC 0 +#endif + +#ifdef _MSC_VER +# define FMT_MSC_VER _MSC_VER +# define FMT_SUPPRESS_MSC_WARNING(n) __pragma(warning(suppress : n)) +#else +# define FMT_MSC_VER 0 +# define FMT_SUPPRESS_MSC_WARNING(n) +#endif + +#ifdef __has_feature +# define FMT_HAS_FEATURE(x) __has_feature(x) +#else +# define FMT_HAS_FEATURE(x) 0 +#endif + +#if defined(__has_include) && !defined(__INTELLISENSE__) && \ + (!FMT_ICC_VERSION || FMT_ICC_VERSION >= 1600) +# define FMT_HAS_INCLUDE(x) __has_include(x) +#else +# define FMT_HAS_INCLUDE(x) 0 +#endif + +#ifdef __has_cpp_attribute +# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#else +# define FMT_HAS_CPP_ATTRIBUTE(x) 0 +#endif + +#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \ + (__cplusplus >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute)) + +#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \ + (__cplusplus >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute)) + +// Check if relaxed C++14 constexpr is supported. +// GCC doesn't allow throw in constexpr until version 6 (bug 67371). +#ifndef FMT_USE_CONSTEXPR +# define FMT_USE_CONSTEXPR \ + (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1910 || \ + (FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) && \ + !FMT_NVCC && !FMT_ICC_VERSION +#endif +#if FMT_USE_CONSTEXPR +# define FMT_CONSTEXPR constexpr +# define FMT_CONSTEXPR_DECL constexpr +#else +# define FMT_CONSTEXPR inline +# define FMT_CONSTEXPR_DECL +#endif + +#ifndef FMT_OVERRIDE +# if FMT_HAS_FEATURE(cxx_override_control) || \ + (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900 +# define FMT_OVERRIDE override +# else +# define FMT_OVERRIDE +# endif +#endif + +// Check if exceptions are disabled. +#ifndef FMT_EXCEPTIONS +# if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \ + FMT_MSC_VER && !_HAS_EXCEPTIONS +# define FMT_EXCEPTIONS 0 +# else +# define FMT_EXCEPTIONS 1 +# endif +#endif + +// Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature). +#ifndef FMT_USE_NOEXCEPT +# define FMT_USE_NOEXCEPT 0 +#endif + +#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ + (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900 +# define FMT_DETECTED_NOEXCEPT noexcept +# define FMT_HAS_CXX11_NOEXCEPT 1 +#else +# define FMT_DETECTED_NOEXCEPT throw() +# define FMT_HAS_CXX11_NOEXCEPT 0 +#endif + +#ifndef FMT_NOEXCEPT +# if FMT_EXCEPTIONS || FMT_HAS_CXX11_NOEXCEPT +# define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT +# else +# define FMT_NOEXCEPT +# endif +#endif + +// [[noreturn]] is disabled on MSVC and NVCC because of bogus unreachable code +// warnings. +#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER && \ + !FMT_NVCC +# define FMT_NORETURN [[noreturn]] +#else +# define FMT_NORETURN +#endif + +#ifndef FMT_DEPRECATED +# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900 +# define FMT_DEPRECATED [[deprecated]] +# else +# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__) +# define FMT_DEPRECATED __attribute__((deprecated)) +# elif FMT_MSC_VER +# define FMT_DEPRECATED __declspec(deprecated) +# else +# define FMT_DEPRECATED /* deprecated */ +# endif +# endif +#endif + +// Workaround broken [[deprecated]] in the Intel, PGI and NVCC compilers. +#if FMT_ICC_VERSION || defined(__PGI) || FMT_NVCC +# define FMT_DEPRECATED_ALIAS +#else +# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED +#endif + +#ifndef FMT_INLINE +# if FMT_GCC_VERSION || FMT_CLANG_VERSION +# define FMT_INLINE inline __attribute__((always_inline)) +# else +# define FMT_INLINE inline +# endif +#endif + +#ifndef FMT_USE_INLINE_NAMESPACES +# if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \ + (FMT_MSC_VER >= 1900 && !_MANAGED) +# define FMT_USE_INLINE_NAMESPACES 1 +# else +# define FMT_USE_INLINE_NAMESPACES 0 +# endif +#endif + +#ifndef FMT_BEGIN_NAMESPACE +# if FMT_USE_INLINE_NAMESPACES +# define FMT_INLINE_NAMESPACE inline namespace +# define FMT_END_NAMESPACE \ + } \ + } +# else +# define FMT_INLINE_NAMESPACE namespace +# define FMT_END_NAMESPACE \ + } \ + using namespace v7; \ + } +# endif +# define FMT_BEGIN_NAMESPACE \ + namespace fmt { \ + FMT_INLINE_NAMESPACE v7 { +#endif + +#if !defined(FMT_HEADER_ONLY) && defined(_WIN32) +# define FMT_CLASS_API FMT_SUPPRESS_MSC_WARNING(4275) +# ifdef FMT_EXPORT +# define FMT_API __declspec(dllexport) +# define FMT_EXTERN_TEMPLATE_API FMT_API +# define FMT_EXPORTED +# elif defined(FMT_SHARED) +# define FMT_API __declspec(dllimport) +# define FMT_EXTERN_TEMPLATE_API FMT_API +# endif +#else +# define FMT_CLASS_API +#endif +#ifndef FMT_API +# define FMT_API +#endif +#ifndef FMT_EXTERN_TEMPLATE_API +# define FMT_EXTERN_TEMPLATE_API +#endif +#ifndef FMT_INSTANTIATION_DEF_API +# define FMT_INSTANTIATION_DEF_API FMT_API +#endif + +#ifndef FMT_HEADER_ONLY +# define FMT_EXTERN extern +#else +# define FMT_EXTERN +#endif + +// libc++ supports string_view in pre-c++17. +#if (FMT_HAS_INCLUDE() && \ + (__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \ + (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910) +# include +# define FMT_USE_STRING_VIEW +#elif FMT_HAS_INCLUDE("experimental/string_view") && __cplusplus >= 201402L +# include +# define FMT_USE_EXPERIMENTAL_STRING_VIEW +#endif + +#ifndef FMT_UNICODE +# define FMT_UNICODE !FMT_MSC_VER +#endif +#if FMT_UNICODE && FMT_MSC_VER +# pragma execution_character_set("utf-8") +#endif + +FMT_BEGIN_NAMESPACE + +// Implementations of enable_if_t and other metafunctions for older systems. +template +using enable_if_t = typename std::enable_if::type; +template +using conditional_t = typename std::conditional::type; +template using bool_constant = std::integral_constant; +template +using remove_reference_t = typename std::remove_reference::type; +template +using remove_const_t = typename std::remove_const::type; +template +using remove_cvref_t = typename std::remove_cv>::type; +template struct type_identity { using type = T; }; +template using type_identity_t = typename type_identity::type; + +struct monostate {}; + +// An enable_if helper to be used in template parameters which results in much +// shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed +// to workaround a bug in MSVC 2019 (see #1140 and #1186). +#define FMT_ENABLE_IF(...) enable_if_t<(__VA_ARGS__), int> = 0 + +namespace detail { + +// A helper function to suppress "conditional expression is constant" warnings. +template constexpr T const_check(T value) { return value; } + +FMT_NORETURN FMT_API void assert_fail(const char* file, int line, + const char* message); + +#ifndef FMT_ASSERT +# ifdef NDEBUG +// FMT_ASSERT is not empty to avoid -Werror=empty-body. +# define FMT_ASSERT(condition, message) ((void)0) +# else +# define FMT_ASSERT(condition, message) \ + ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \ + ? (void)0 \ + : ::fmt::detail::assert_fail(__FILE__, __LINE__, (message))) +# endif +#endif + +#if defined(FMT_USE_STRING_VIEW) +template using std_string_view = std::basic_string_view; +#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW) +template +using std_string_view = std::experimental::basic_string_view; +#else +template struct std_string_view {}; +#endif + +#ifdef FMT_USE_INT128 +// Do nothing. +#elif defined(__SIZEOF_INT128__) && !FMT_NVCC && \ + !(FMT_CLANG_VERSION && FMT_MSC_VER) +# define FMT_USE_INT128 1 +using int128_t = __int128_t; +using uint128_t = __uint128_t; +#else +# define FMT_USE_INT128 0 +#endif +#if !FMT_USE_INT128 +struct int128_t {}; +struct uint128_t {}; +#endif + +// Casts a nonnegative integer to unsigned. +template +FMT_CONSTEXPR typename std::make_unsigned::type to_unsigned(Int value) { + FMT_ASSERT(value >= 0, "negative value"); + return static_cast::type>(value); +} + +FMT_SUPPRESS_MSC_WARNING(4566) constexpr unsigned char micro[] = "\u00B5"; + +template constexpr bool is_unicode() { + return FMT_UNICODE || sizeof(Char) != 1 || + (sizeof(micro) == 3 && micro[0] == 0xC2 && micro[1] == 0xB5); +} + +#ifdef __cpp_char8_t +using char8_type = char8_t; +#else +enum char8_type : unsigned char {}; +#endif +} // namespace detail + +#ifdef FMT_USE_INTERNAL +namespace internal = detail; // DEPRECATED +#endif + +/** + An implementation of ``std::basic_string_view`` for pre-C++17. It provides a + subset of the API. ``fmt::basic_string_view`` is used for format strings even + if ``std::string_view`` is available to prevent issues when a library is + compiled with a different ``-std`` option than the client code (which is not + recommended). + */ +template class basic_string_view { + private: + const Char* data_; + size_t size_; + + public: + using value_type = Char; + using iterator = const Char*; + + constexpr basic_string_view() FMT_NOEXCEPT : data_(nullptr), size_(0) {} + + /** Constructs a string reference object from a C string and a size. */ + constexpr basic_string_view(const Char* s, size_t count) FMT_NOEXCEPT + : data_(s), + size_(count) {} + + /** + \rst + Constructs a string reference object from a C string computing + the size with ``std::char_traits::length``. + \endrst + */ +#if __cplusplus >= 201703L // C++17's char_traits::length() is constexpr. + FMT_CONSTEXPR +#endif + basic_string_view(const Char* s) + : data_(s), size_(std::char_traits::length(s)) {} + + /** Constructs a string reference from a ``std::basic_string`` object. */ + template + FMT_CONSTEXPR basic_string_view( + const std::basic_string& s) FMT_NOEXCEPT + : data_(s.data()), + size_(s.size()) {} + + template >::value)> + FMT_CONSTEXPR basic_string_view(S s) FMT_NOEXCEPT : data_(s.data()), + size_(s.size()) {} + + /** Returns a pointer to the string data. */ + constexpr const Char* data() const { return data_; } + + /** Returns the string size. */ + constexpr size_t size() const { return size_; } + + constexpr iterator begin() const { return data_; } + constexpr iterator end() const { return data_ + size_; } + + constexpr const Char& operator[](size_t pos) const { return data_[pos]; } + + FMT_CONSTEXPR void remove_prefix(size_t n) { + data_ += n; + size_ -= n; + } + + // Lexicographically compare this string reference to other. + int compare(basic_string_view other) const { + size_t str_size = size_ < other.size_ ? size_ : other.size_; + int result = std::char_traits::compare(data_, other.data_, str_size); + if (result == 0) + result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1); + return result; + } + + friend bool operator==(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) == 0; + } + friend bool operator!=(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) != 0; + } + friend bool operator<(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) < 0; + } + friend bool operator<=(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) <= 0; + } + friend bool operator>(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) > 0; + } + friend bool operator>=(basic_string_view lhs, basic_string_view rhs) { + return lhs.compare(rhs) >= 0; + } +}; + +using string_view = basic_string_view; +using wstring_view = basic_string_view; + +/** Specifies if ``T`` is a character type. Can be specialized by users. */ +template struct is_char : std::false_type {}; +template <> struct is_char : std::true_type {}; +template <> struct is_char : std::true_type {}; +template <> struct is_char : std::true_type {}; +template <> struct is_char : std::true_type {}; +template <> struct is_char : std::true_type {}; + +/** + \rst + Returns a string view of `s`. In order to add custom string type support to + {fmt} provide an overload of `to_string_view` for it in the same namespace as + the type for the argument-dependent lookup to work. + + **Example**:: + + namespace my_ns { + inline string_view to_string_view(const my_string& s) { + return {s.data(), s.length()}; + } + } + std::string message = fmt::format(my_string("The answer is {}"), 42); + \endrst + */ +template ::value)> +inline basic_string_view to_string_view(const Char* s) { + return s; +} + +template +inline basic_string_view to_string_view( + const std::basic_string& s) { + return s; +} + +template +inline basic_string_view to_string_view(basic_string_view s) { + return s; +} + +template >::value)> +inline basic_string_view to_string_view(detail::std_string_view s) { + return s; +} + +// A base class for compile-time strings. It is defined in the fmt namespace to +// make formatting functions visible via ADL, e.g. format(FMT_STRING("{}"), 42). +struct compile_string {}; + +template +struct is_compile_string : std::is_base_of {}; + +template ::value)> +constexpr basic_string_view to_string_view(const S& s) { + return s; +} + +namespace detail { +void to_string_view(...); +using fmt::v7::to_string_view; + +// Specifies whether S is a string type convertible to fmt::basic_string_view. +// It should be a constexpr function but MSVC 2017 fails to compile it in +// enable_if and MSVC 2015 fails to compile it as an alias template. +template +struct is_string : std::is_class()))> { +}; + +template struct char_t_impl {}; +template struct char_t_impl::value>> { + using result = decltype(to_string_view(std::declval())); + using type = typename result::value_type; +}; + +// Reports a compile-time error if S is not a valid format string. +template ::value)> +FMT_INLINE void check_format_string(const S&) { +#ifdef FMT_ENFORCE_COMPILE_STRING + static_assert(is_compile_string::value, + "FMT_ENFORCE_COMPILE_STRING requires all format strings to use " + "FMT_STRING."); +#endif +} +template ::value)> +void check_format_string(S); + +struct error_handler { + constexpr error_handler() = default; + constexpr error_handler(const error_handler&) = default; + + // This function is intentionally not constexpr to give a compile-time error. + FMT_NORETURN FMT_API void on_error(const char* message); +}; +} // namespace detail + +/** String's character type. */ +template using char_t = typename detail::char_t_impl::type; + +/** + \rst + Parsing context consisting of a format string range being parsed and an + argument counter for automatic indexing. + + You can use one of the following type aliases for common character types: + + +-----------------------+-------------------------------------+ + | Type | Definition | + +=======================+=====================================+ + | format_parse_context | basic_format_parse_context | + +-----------------------+-------------------------------------+ + | wformat_parse_context | basic_format_parse_context | + +-----------------------+-------------------------------------+ + \endrst + */ +template +class basic_format_parse_context : private ErrorHandler { + private: + basic_string_view format_str_; + int next_arg_id_; + + public: + using char_type = Char; + using iterator = typename basic_string_view::iterator; + + explicit constexpr basic_format_parse_context( + basic_string_view format_str, ErrorHandler eh = {}, + int next_arg_id = 0) + : ErrorHandler(eh), format_str_(format_str), next_arg_id_(next_arg_id) {} + + /** + Returns an iterator to the beginning of the format string range being + parsed. + */ + constexpr iterator begin() const FMT_NOEXCEPT { return format_str_.begin(); } + + /** + Returns an iterator past the end of the format string range being parsed. + */ + constexpr iterator end() const FMT_NOEXCEPT { return format_str_.end(); } + + /** Advances the begin iterator to ``it``. */ + FMT_CONSTEXPR void advance_to(iterator it) { + format_str_.remove_prefix(detail::to_unsigned(it - begin())); + } + + /** + Reports an error if using the manual argument indexing; otherwise returns + the next argument index and switches to the automatic indexing. + */ + FMT_CONSTEXPR int next_arg_id() { + // Don't check if the argument id is valid to avoid overhead and because it + // will be checked during formatting anyway. + if (next_arg_id_ >= 0) return next_arg_id_++; + on_error("cannot switch from manual to automatic argument indexing"); + return 0; + } + + /** + Reports an error if using the automatic argument indexing; otherwise + switches to the manual indexing. + */ + FMT_CONSTEXPR void check_arg_id(int) { + if (next_arg_id_ > 0) + on_error("cannot switch from automatic to manual argument indexing"); + else + next_arg_id_ = -1; + } + + FMT_CONSTEXPR void check_arg_id(basic_string_view) {} + + FMT_CONSTEXPR void on_error(const char* message) { + ErrorHandler::on_error(message); + } + + constexpr ErrorHandler error_handler() const { return *this; } +}; + +using format_parse_context = basic_format_parse_context; +using wformat_parse_context = basic_format_parse_context; + +template class basic_format_arg; +template class basic_format_args; +template class dynamic_format_arg_store; + +// A formatter for objects of type T. +template +struct formatter { + // A deleted default constructor indicates a disabled formatter. + formatter() = delete; +}; + +// Specifies if T has an enabled formatter specialization. A type can be +// formattable even if it doesn't have a formatter e.g. via a conversion. +template +using has_formatter = + std::is_constructible>; + +// Checks whether T is a container with contiguous storage. +template struct is_contiguous : std::false_type {}; +template +struct is_contiguous> : std::true_type {}; + +namespace detail { + +// Extracts a reference to the container from back_insert_iterator. +template +inline Container& get_container(std::back_insert_iterator it) { + using bi_iterator = std::back_insert_iterator; + struct accessor : bi_iterator { + accessor(bi_iterator iter) : bi_iterator(iter) {} + using bi_iterator::container; + }; + return *accessor(it).container; +} + +/** + \rst + A contiguous memory buffer with an optional growing ability. It is an internal + class and shouldn't be used directly, only via `~fmt::basic_memory_buffer`. + \endrst + */ +template class buffer { + private: + T* ptr_; + size_t size_; + size_t capacity_; + + protected: + // Don't initialize ptr_ since it is not accessed to save a few cycles. + FMT_SUPPRESS_MSC_WARNING(26495) + buffer(size_t sz) FMT_NOEXCEPT : size_(sz), capacity_(sz) {} + + buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) FMT_NOEXCEPT + : ptr_(p), + size_(sz), + capacity_(cap) {} + + ~buffer() = default; + + /** Sets the buffer data and capacity. */ + void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT { + ptr_ = buf_data; + capacity_ = buf_capacity; + } + + /** Increases the buffer capacity to hold at least *capacity* elements. */ + virtual void grow(size_t capacity) = 0; + + public: + using value_type = T; + using const_reference = const T&; + + buffer(const buffer&) = delete; + void operator=(const buffer&) = delete; + + T* begin() FMT_NOEXCEPT { return ptr_; } + T* end() FMT_NOEXCEPT { return ptr_ + size_; } + + const T* begin() const FMT_NOEXCEPT { return ptr_; } + const T* end() const FMT_NOEXCEPT { return ptr_ + size_; } + + /** Returns the size of this buffer. */ + size_t size() const FMT_NOEXCEPT { return size_; } + + /** Returns the capacity of this buffer. */ + size_t capacity() const FMT_NOEXCEPT { return capacity_; } + + /** Returns a pointer to the buffer data. */ + T* data() FMT_NOEXCEPT { return ptr_; } + + /** Returns a pointer to the buffer data. */ + const T* data() const FMT_NOEXCEPT { return ptr_; } + + /** Clears this buffer. */ + void clear() { size_ = 0; } + + // Tries resizing the buffer to contain *count* elements. If T is a POD type + // the new elements may not be initialized. + void try_resize(size_t count) { + try_reserve(count); + size_ = count <= capacity_ ? count : capacity_; + } + + // Tries increasing the buffer capacity to *new_capacity*. It can increase the + // capacity by a smaller amount than requested but guarantees there is space + // for at least one additional element either by increasing the capacity or by + // flushing the buffer if it is full. + void try_reserve(size_t new_capacity) { + if (new_capacity > capacity_) grow(new_capacity); + } + + void push_back(const T& value) { + try_reserve(size_ + 1); + ptr_[size_++] = value; + } + + /** Appends data to the end of the buffer. */ + template void append(const U* begin, const U* end); + + template T& operator[](I index) { return ptr_[index]; } + template const T& operator[](I index) const { + return ptr_[index]; + } +}; + +struct buffer_traits { + explicit buffer_traits(size_t) {} + size_t count() const { return 0; } + size_t limit(size_t size) { return size; } +}; + +class fixed_buffer_traits { + private: + size_t count_ = 0; + size_t limit_; + + public: + explicit fixed_buffer_traits(size_t limit) : limit_(limit) {} + size_t count() const { return count_; } + size_t limit(size_t size) { + size_t n = limit_ > count_ ? limit_ - count_ : 0; + count_ += size; + return size < n ? size : n; + } +}; + +// A buffer that writes to an output iterator when flushed. +template +class iterator_buffer final : public Traits, public buffer { + private: + OutputIt out_; + enum { buffer_size = 256 }; + T data_[buffer_size]; + + protected: + void grow(size_t) final FMT_OVERRIDE { + if (this->size() == buffer_size) flush(); + } + void flush(); + + public: + explicit iterator_buffer(OutputIt out, size_t n = buffer_size) + : Traits(n), + buffer(data_, 0, buffer_size), + out_(out) {} + ~iterator_buffer() { flush(); } + + OutputIt out() { + flush(); + return out_; + } + size_t count() const { return Traits::count() + this->size(); } +}; + +template class iterator_buffer final : public buffer { + protected: + void grow(size_t) final FMT_OVERRIDE {} + + public: + explicit iterator_buffer(T* out, size_t = 0) : buffer(out, 0, ~size_t()) {} + + T* out() { return &*this->end(); } +}; + +// A buffer that writes to a container with the contiguous storage. +template +class iterator_buffer, + enable_if_t::value, + typename Container::value_type>> + final : public buffer { + private: + Container& container_; + + protected: + void grow(size_t capacity) final FMT_OVERRIDE { + container_.resize(capacity); + this->set(&container_[0], capacity); + } + + public: + explicit iterator_buffer(Container& c) + : buffer(c.size()), container_(c) {} + explicit iterator_buffer(std::back_insert_iterator out, size_t = 0) + : iterator_buffer(get_container(out)) {} + std::back_insert_iterator out() { + return std::back_inserter(container_); + } +}; + +// A buffer that counts the number of code units written discarding the output. +template class counting_buffer final : public buffer { + private: + enum { buffer_size = 256 }; + T data_[buffer_size]; + size_t count_ = 0; + + protected: + void grow(size_t) final FMT_OVERRIDE { + if (this->size() != buffer_size) return; + count_ += this->size(); + this->clear(); + } + + public: + counting_buffer() : buffer(data_, 0, buffer_size) {} + + size_t count() { return count_ + this->size(); } +}; + +// An output iterator that appends to the buffer. +// It is used to reduce symbol sizes for the common case. +template +class buffer_appender : public std::back_insert_iterator> { + using base = std::back_insert_iterator>; + + public: + explicit buffer_appender(buffer& buf) : base(buf) {} + buffer_appender(base it) : base(it) {} + + buffer_appender& operator++() { + base::operator++(); + return *this; + } + + buffer_appender operator++(int) { + buffer_appender tmp = *this; + ++*this; + return tmp; + } +}; + +// Maps an output iterator into a buffer. +template +iterator_buffer get_buffer(OutputIt); +template buffer& get_buffer(buffer_appender); + +template OutputIt get_buffer_init(OutputIt out) { + return out; +} +template buffer& get_buffer_init(buffer_appender out) { + return get_container(out); +} + +template +auto get_iterator(Buffer& buf) -> decltype(buf.out()) { + return buf.out(); +} +template buffer_appender get_iterator(buffer& buf) { + return buffer_appender(buf); +} + +template +struct fallback_formatter { + fallback_formatter() = delete; +}; + +// Specifies if T has an enabled fallback_formatter specialization. +template +using has_fallback_formatter = + std::is_constructible>; + +struct view {}; + +template struct named_arg : view { + const Char* name; + const T& value; + named_arg(const Char* n, const T& v) : name(n), value(v) {} +}; + +template struct named_arg_info { + const Char* name; + int id; +}; + +template +struct arg_data { + // args_[0].named_args points to named_args_ to avoid bloating format_args. + // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning. + T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : +1)]; + named_arg_info named_args_[NUM_NAMED_ARGS]; + + template + arg_data(const U&... init) : args_{T(named_args_, NUM_NAMED_ARGS), init...} {} + arg_data(const arg_data& other) = delete; + const T* args() const { return args_ + 1; } + named_arg_info* named_args() { return named_args_; } +}; + +template +struct arg_data { + // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning. + T args_[NUM_ARGS != 0 ? NUM_ARGS : +1]; + + template + FMT_INLINE arg_data(const U&... init) : args_{init...} {} + FMT_INLINE const T* args() const { return args_; } + FMT_INLINE std::nullptr_t named_args() { return nullptr; } +}; + +template +inline void init_named_args(named_arg_info*, int, int) {} + +template +void init_named_args(named_arg_info* named_args, int arg_count, + int named_arg_count, const T&, const Tail&... args) { + init_named_args(named_args, arg_count + 1, named_arg_count, args...); +} + +template +void init_named_args(named_arg_info* named_args, int arg_count, + int named_arg_count, const named_arg& arg, + const Tail&... args) { + named_args[named_arg_count++] = {arg.name, arg_count}; + init_named_args(named_args, arg_count + 1, named_arg_count, args...); +} + +template +FMT_INLINE void init_named_args(std::nullptr_t, int, int, const Args&...) {} + +template struct is_named_arg : std::false_type {}; + +template +struct is_named_arg> : std::true_type {}; + +template constexpr size_t count() { return B ? 1 : 0; } +template constexpr size_t count() { + return (B1 ? 1 : 0) + count(); +} + +template constexpr size_t count_named_args() { + return count::value...>(); +} + +enum class type { + none_type, + // Integer types should go first, + int_type, + uint_type, + long_long_type, + ulong_long_type, + int128_type, + uint128_type, + bool_type, + char_type, + last_integer_type = char_type, + // followed by floating-point types. + float_type, + double_type, + long_double_type, + last_numeric_type = long_double_type, + cstring_type, + string_type, + pointer_type, + custom_type +}; + +// Maps core type T to the corresponding type enum constant. +template +struct type_constant : std::integral_constant {}; + +#define FMT_TYPE_CONSTANT(Type, constant) \ + template \ + struct type_constant \ + : std::integral_constant {} + +FMT_TYPE_CONSTANT(int, int_type); +FMT_TYPE_CONSTANT(unsigned, uint_type); +FMT_TYPE_CONSTANT(long long, long_long_type); +FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type); +FMT_TYPE_CONSTANT(int128_t, int128_type); +FMT_TYPE_CONSTANT(uint128_t, uint128_type); +FMT_TYPE_CONSTANT(bool, bool_type); +FMT_TYPE_CONSTANT(Char, char_type); +FMT_TYPE_CONSTANT(float, float_type); +FMT_TYPE_CONSTANT(double, double_type); +FMT_TYPE_CONSTANT(long double, long_double_type); +FMT_TYPE_CONSTANT(const Char*, cstring_type); +FMT_TYPE_CONSTANT(basic_string_view, string_type); +FMT_TYPE_CONSTANT(const void*, pointer_type); + +constexpr bool is_integral_type(type t) { + return t > type::none_type && t <= type::last_integer_type; +} + +constexpr bool is_arithmetic_type(type t) { + return t > type::none_type && t <= type::last_numeric_type; +} + +template struct string_value { + const Char* data; + size_t size; +}; + +template struct named_arg_value { + const named_arg_info* data; + size_t size; +}; + +template struct custom_value { + using parse_context = typename Context::parse_context_type; + const void* value; + void (*format)(const void* arg, parse_context& parse_ctx, Context& ctx); +}; + +// A formatting argument value. +template class value { + public: + using char_type = typename Context::char_type; + + union { + int int_value; + unsigned uint_value; + long long long_long_value; + unsigned long long ulong_long_value; + int128_t int128_value; + uint128_t uint128_value; + bool bool_value; + char_type char_value; + float float_value; + double double_value; + long double long_double_value; + const void* pointer; + string_value string; + custom_value custom; + named_arg_value named_args; + }; + + constexpr FMT_INLINE value(int val = 0) : int_value(val) {} + constexpr FMT_INLINE value(unsigned val) : uint_value(val) {} + FMT_INLINE value(long long val) : long_long_value(val) {} + FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {} + FMT_INLINE value(int128_t val) : int128_value(val) {} + FMT_INLINE value(uint128_t val) : uint128_value(val) {} + FMT_INLINE value(float val) : float_value(val) {} + FMT_INLINE value(double val) : double_value(val) {} + FMT_INLINE value(long double val) : long_double_value(val) {} + FMT_INLINE value(bool val) : bool_value(val) {} + FMT_INLINE value(char_type val) : char_value(val) {} + FMT_INLINE value(const char_type* val) { string.data = val; } + FMT_INLINE value(basic_string_view val) { + string.data = val.data(); + string.size = val.size(); + } + FMT_INLINE value(const void* val) : pointer(val) {} + FMT_INLINE value(const named_arg_info* args, size_t size) + : named_args{args, size} {} + + template FMT_INLINE value(const T& val) { + custom.value = &val; + // Get the formatter type through the context to allow different contexts + // have different extension points, e.g. `formatter` for `format` and + // `printf_formatter` for `printf`. + custom.format = format_custom_arg< + T, conditional_t::value, + typename Context::template formatter_type, + fallback_formatter>>; + } + + private: + // Formats an argument of a custom type, such as a user-defined class. + template + static void format_custom_arg(const void* arg, + typename Context::parse_context_type& parse_ctx, + Context& ctx) { + Formatter f; + parse_ctx.advance_to(f.parse(parse_ctx)); + ctx.advance_to(f.format(*static_cast(arg), ctx)); + } +}; + +template +FMT_CONSTEXPR basic_format_arg make_arg(const T& value); + +// To minimize the number of types we need to deal with, long is translated +// either to int or to long long depending on its size. +enum { long_short = sizeof(long) == sizeof(int) }; +using long_type = conditional_t; +using ulong_type = conditional_t; + +struct unformattable {}; + +// Maps formatting arguments to core types. +template struct arg_mapper { + using char_type = typename Context::char_type; + + FMT_CONSTEXPR int map(signed char val) { return val; } + FMT_CONSTEXPR unsigned map(unsigned char val) { return val; } + FMT_CONSTEXPR int map(short val) { return val; } + FMT_CONSTEXPR unsigned map(unsigned short val) { return val; } + FMT_CONSTEXPR int map(int val) { return val; } + FMT_CONSTEXPR unsigned map(unsigned val) { return val; } + FMT_CONSTEXPR long_type map(long val) { return val; } + FMT_CONSTEXPR ulong_type map(unsigned long val) { return val; } + FMT_CONSTEXPR long long map(long long val) { return val; } + FMT_CONSTEXPR unsigned long long map(unsigned long long val) { return val; } + FMT_CONSTEXPR int128_t map(int128_t val) { return val; } + FMT_CONSTEXPR uint128_t map(uint128_t val) { return val; } + FMT_CONSTEXPR bool map(bool val) { return val; } + + template ::value)> + FMT_CONSTEXPR char_type map(T val) { + static_assert( + std::is_same::value || std::is_same::value, + "mixing character types is disallowed"); + return val; + } + + FMT_CONSTEXPR float map(float val) { return val; } + FMT_CONSTEXPR double map(double val) { return val; } + FMT_CONSTEXPR long double map(long double val) { return val; } + + FMT_CONSTEXPR const char_type* map(char_type* val) { return val; } + FMT_CONSTEXPR const char_type* map(const char_type* val) { return val; } + template ::value)> + FMT_CONSTEXPR basic_string_view map(const T& val) { + static_assert(std::is_same>::value, + "mixing character types is disallowed"); + return to_string_view(val); + } + template , T>::value && + !is_string::value && !has_formatter::value && + !has_fallback_formatter::value)> + FMT_CONSTEXPR basic_string_view map(const T& val) { + return basic_string_view(val); + } + template < + typename T, + FMT_ENABLE_IF( + std::is_constructible, T>::value && + !std::is_constructible, T>::value && + !is_string::value && !has_formatter::value && + !has_fallback_formatter::value)> + FMT_CONSTEXPR basic_string_view map(const T& val) { + return std_string_view(val); + } + FMT_CONSTEXPR const char* map(const signed char* val) { + static_assert(std::is_same::value, "invalid string type"); + return reinterpret_cast(val); + } + FMT_CONSTEXPR const char* map(const unsigned char* val) { + static_assert(std::is_same::value, "invalid string type"); + return reinterpret_cast(val); + } + FMT_CONSTEXPR const char* map(signed char* val) { + const auto* const_val = val; + return map(const_val); + } + FMT_CONSTEXPR const char* map(unsigned char* val) { + const auto* const_val = val; + return map(const_val); + } + + FMT_CONSTEXPR const void* map(void* val) { return val; } + FMT_CONSTEXPR const void* map(const void* val) { return val; } + FMT_CONSTEXPR const void* map(std::nullptr_t val) { return val; } + template FMT_CONSTEXPR int map(const T*) { + // Formatting of arbitrary pointers is disallowed. If you want to output + // a pointer cast it to "void *" or "const void *". In particular, this + // forbids formatting of "[const] volatile char *" which is printed as bool + // by iostreams. + static_assert(!sizeof(T), "formatting of non-void pointers is disallowed"); + return 0; + } + + template ::value && + !has_formatter::value && + !has_fallback_formatter::value)> + FMT_CONSTEXPR auto map(const T& val) + -> decltype(std::declval().map( + static_cast::type>(val))) { + return map(static_cast::type>(val)); + } + template ::value && !is_char::value && + (has_formatter::value || + has_fallback_formatter::value))> + FMT_CONSTEXPR const T& map(const T& val) { + return val; + } + + template + FMT_CONSTEXPR auto map(const named_arg& val) + -> decltype(std::declval().map(val.value)) { + return map(val.value); + } + + unformattable map(...) { return {}; } +}; + +// A type constant after applying arg_mapper. +template +using mapped_type_constant = + type_constant().map(std::declval())), + typename Context::char_type>; + +enum { packed_arg_bits = 4 }; +// Maximum number of arguments with packed types. +enum { max_packed_args = 62 / packed_arg_bits }; +enum : unsigned long long { is_unpacked_bit = 1ULL << 63 }; +enum : unsigned long long { has_named_args_bit = 1ULL << 62 }; +} // namespace detail + +// A formatting argument. It is a trivially copyable/constructible type to +// allow storage in basic_memory_buffer. +template class basic_format_arg { + private: + detail::value value_; + detail::type type_; + + template + friend FMT_CONSTEXPR basic_format_arg detail::make_arg( + const T& value); + + template + friend FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis, + const basic_format_arg& arg) + -> decltype(vis(0)); + + friend class basic_format_args; + friend class dynamic_format_arg_store; + + using char_type = typename Context::char_type; + + template + friend struct detail::arg_data; + + basic_format_arg(const detail::named_arg_info* args, size_t size) + : value_(args, size) {} + + public: + class handle { + public: + explicit handle(detail::custom_value custom) : custom_(custom) {} + + void format(typename Context::parse_context_type& parse_ctx, + Context& ctx) const { + custom_.format(custom_.value, parse_ctx, ctx); + } + + private: + detail::custom_value custom_; + }; + + constexpr basic_format_arg() : type_(detail::type::none_type) {} + + constexpr explicit operator bool() const FMT_NOEXCEPT { + return type_ != detail::type::none_type; + } + + detail::type type() const { return type_; } + + bool is_integral() const { return detail::is_integral_type(type_); } + bool is_arithmetic() const { return detail::is_arithmetic_type(type_); } +}; + +/** + \rst + Visits an argument dispatching to the appropriate visit method based on + the argument type. For example, if the argument type is ``double`` then + ``vis(value)`` will be called with the value of type ``double``. + \endrst + */ +template +FMT_CONSTEXPR_DECL FMT_INLINE auto visit_format_arg( + Visitor&& vis, const basic_format_arg& arg) -> decltype(vis(0)) { + using char_type = typename Context::char_type; + switch (arg.type_) { + case detail::type::none_type: + break; + case detail::type::int_type: + return vis(arg.value_.int_value); + case detail::type::uint_type: + return vis(arg.value_.uint_value); + case detail::type::long_long_type: + return vis(arg.value_.long_long_value); + case detail::type::ulong_long_type: + return vis(arg.value_.ulong_long_value); +#if FMT_USE_INT128 + case detail::type::int128_type: + return vis(arg.value_.int128_value); + case detail::type::uint128_type: + return vis(arg.value_.uint128_value); +#else + case detail::type::int128_type: + case detail::type::uint128_type: + break; +#endif + case detail::type::bool_type: + return vis(arg.value_.bool_value); + case detail::type::char_type: + return vis(arg.value_.char_value); + case detail::type::float_type: + return vis(arg.value_.float_value); + case detail::type::double_type: + return vis(arg.value_.double_value); + case detail::type::long_double_type: + return vis(arg.value_.long_double_value); + case detail::type::cstring_type: + return vis(arg.value_.string.data); + case detail::type::string_type: + return vis(basic_string_view(arg.value_.string.data, + arg.value_.string.size)); + case detail::type::pointer_type: + return vis(arg.value_.pointer); + case detail::type::custom_type: + return vis(typename basic_format_arg::handle(arg.value_.custom)); + } + return vis(monostate()); +} + +template struct formattable : std::false_type {}; + +namespace detail { + +// A workaround for gcc 4.8 to make void_t work in a SFINAE context. +template struct void_t_impl { using type = void; }; +template +using void_t = typename detail::void_t_impl::type; + +template +struct is_output_iterator : std::false_type {}; + +template +struct is_output_iterator< + It, T, + void_t::iterator_category, + decltype(*std::declval() = std::declval())>> + : std::true_type {}; + +template +struct is_back_insert_iterator : std::false_type {}; +template +struct is_back_insert_iterator> + : std::true_type {}; + +template +struct is_contiguous_back_insert_iterator : std::false_type {}; +template +struct is_contiguous_back_insert_iterator> + : is_contiguous {}; +template +struct is_contiguous_back_insert_iterator> + : std::true_type {}; + +// A type-erased reference to an std::locale to avoid heavy include. +class locale_ref { + private: + const void* locale_; // A type-erased pointer to std::locale. + + public: + locale_ref() : locale_(nullptr) {} + template explicit locale_ref(const Locale& loc); + + explicit operator bool() const FMT_NOEXCEPT { return locale_ != nullptr; } + + template Locale get() const; +}; + +template constexpr unsigned long long encode_types() { return 0; } + +template +constexpr unsigned long long encode_types() { + return static_cast(mapped_type_constant::value) | + (encode_types() << packed_arg_bits); +} + +template +FMT_CONSTEXPR basic_format_arg make_arg(const T& value) { + basic_format_arg arg; + arg.type_ = mapped_type_constant::value; + arg.value_ = arg_mapper().map(value); + return arg; +} + +template int check(unformattable) { + static_assert( + formattable(), + "Cannot format an argument. To make type T formattable provide a " + "formatter specialization: https://fmt.dev/latest/api.html#udt"); + return 0; +} +template inline const U& check(const U& val) { + return val; +} + +// The type template parameter is there to avoid an ODR violation when using +// a fallback formatter in one translation unit and an implicit conversion in +// another (not recommended). +template +inline value make_arg(const T& val) { + return check(arg_mapper().map(val)); +} + +template +inline basic_format_arg make_arg(const T& value) { + return make_arg(value); +} + +template struct is_reference_wrapper : std::false_type {}; +template +struct is_reference_wrapper> : std::true_type {}; + +template const T& unwrap(const T& v) { return v; } +template const T& unwrap(const std::reference_wrapper& v) { + return static_cast(v); +} + +class dynamic_arg_list { + // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for + // templates it doesn't complain about inability to deduce single translation + // unit for placing vtable. So storage_node_base is made a fake template. + template struct node { + virtual ~node() = default; + std::unique_ptr> next; + }; + + template struct typed_node : node<> { + T value; + + template + FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {} + + template + FMT_CONSTEXPR typed_node(const basic_string_view& arg) + : value(arg.data(), arg.size()) {} + }; + + std::unique_ptr> head_; + + public: + template const T& push(const Arg& arg) { + auto new_node = std::unique_ptr>(new typed_node(arg)); + auto& value = new_node->value; + new_node->next = std::move(head_); + head_ = std::move(new_node); + return value; + } +}; +} // namespace detail + +// Formatting context. +template class basic_format_context { + public: + /** The character type for the output. */ + using char_type = Char; + + private: + OutputIt out_; + basic_format_args args_; + detail::locale_ref loc_; + + public: + using iterator = OutputIt; + using format_arg = basic_format_arg; + using parse_context_type = basic_format_parse_context; + template using formatter_type = formatter; + + basic_format_context(const basic_format_context&) = delete; + void operator=(const basic_format_context&) = delete; + /** + Constructs a ``basic_format_context`` object. References to the arguments are + stored in the object so make sure they have appropriate lifetimes. + */ + basic_format_context(OutputIt out, + basic_format_args ctx_args, + detail::locale_ref loc = detail::locale_ref()) + : out_(out), args_(ctx_args), loc_(loc) {} + + format_arg arg(int id) const { return args_.get(id); } + format_arg arg(basic_string_view name) { return args_.get(name); } + int arg_id(basic_string_view name) { return args_.get_id(name); } + const basic_format_args& args() const { return args_; } + + detail::error_handler error_handler() { return {}; } + void on_error(const char* message) { error_handler().on_error(message); } + + // Returns an iterator to the beginning of the output range. + iterator out() { return out_; } + + // Advances the begin iterator to ``it``. + void advance_to(iterator it) { + if (!detail::is_back_insert_iterator()) out_ = it; + } + + detail::locale_ref locale() { return loc_; } +}; + +template +using buffer_context = + basic_format_context, Char>; +using format_context = buffer_context; +using wformat_context = buffer_context; + +// Workaround an alias issue: https://stackoverflow.com/q/62767544/471164. +#define FMT_BUFFER_CONTEXT(Char) \ + basic_format_context, Char> + +/** + \rst + An array of references to arguments. It can be implicitly converted into + `~fmt::basic_format_args` for passing into type-erased formatting functions + such as `~fmt::vformat`. + \endrst + */ +template +class format_arg_store +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 + // Workaround a GCC template argument substitution bug. + : public basic_format_args +#endif +{ + private: + static const size_t num_args = sizeof...(Args); + static const size_t num_named_args = detail::count_named_args(); + static const bool is_packed = num_args <= detail::max_packed_args; + + using value_type = conditional_t, + basic_format_arg>; + + detail::arg_data + data_; + + friend class basic_format_args; + + static constexpr unsigned long long desc = + (is_packed ? detail::encode_types() + : detail::is_unpacked_bit | num_args) | + (num_named_args != 0 + ? static_cast(detail::has_named_args_bit) + : 0); + + public: + format_arg_store(const Args&... args) + : +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 + basic_format_args(*this), +#endif + data_{detail::make_arg< + is_packed, Context, + detail::mapped_type_constant::value>(args)...} { + detail::init_named_args(data_.named_args(), 0, 0, args...); + } +}; + +/** + \rst + Constructs a `~fmt::format_arg_store` object that contains references to + arguments and can be implicitly converted to `~fmt::format_args`. `Context` + can be omitted in which case it defaults to `~fmt::context`. + See `~fmt::arg` for lifetime considerations. + \endrst + */ +template +inline format_arg_store make_format_args( + const Args&... args) { + return {args...}; +} + +/** + \rst + Constructs a `~fmt::format_arg_store` object that contains references + to arguments and can be implicitly converted to `~fmt::format_args`. + If ``format_str`` is a compile-time string then `make_args_checked` checks + its validity at compile time. + \endrst + */ +template > +inline auto make_args_checked(const S& format_str, + const remove_reference_t&... args) + -> format_arg_store, remove_reference_t...> { + static_assert( + detail::count<( + std::is_base_of>::value && + std::is_reference::value)...>() == 0, + "passing views as lvalues is disallowed"); + detail::check_format_string(format_str); + return {args...}; +} + +/** + \rst + Returns a named argument to be used in a formatting function. It should only + be used in a call to a formatting function. + + **Example**:: + + fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23)); + \endrst + */ +template +inline detail::named_arg arg(const Char* name, const T& arg) { + static_assert(!detail::is_named_arg(), "nested named arguments"); + return {name, arg}; +} + +/** + \rst + A dynamic version of `fmt::format_arg_store`. + It's equipped with a storage to potentially temporary objects which lifetimes + could be shorter than the format arguments object. + + It can be implicitly converted into `~fmt::basic_format_args` for passing + into type-erased formatting functions such as `~fmt::vformat`. + \endrst + */ +template +class dynamic_format_arg_store +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 + // Workaround a GCC template argument substitution bug. + : public basic_format_args +#endif +{ + private: + using char_type = typename Context::char_type; + + template struct need_copy { + static constexpr detail::type mapped_type = + detail::mapped_type_constant::value; + + enum { + value = !(detail::is_reference_wrapper::value || + std::is_same>::value || + std::is_same>::value || + (mapped_type != detail::type::cstring_type && + mapped_type != detail::type::string_type && + mapped_type != detail::type::custom_type)) + }; + }; + + template + using stored_type = conditional_t::value, + std::basic_string, T>; + + // Storage of basic_format_arg must be contiguous. + std::vector> data_; + std::vector> named_info_; + + // Storage of arguments not fitting into basic_format_arg must grow + // without relocation because items in data_ refer to it. + detail::dynamic_arg_list dynamic_args_; + + friend class basic_format_args; + + unsigned long long get_types() const { + return detail::is_unpacked_bit | data_.size() | + (named_info_.empty() + ? 0ULL + : static_cast(detail::has_named_args_bit)); + } + + const basic_format_arg* data() const { + return named_info_.empty() ? data_.data() : data_.data() + 1; + } + + template void emplace_arg(const T& arg) { + data_.emplace_back(detail::make_arg(arg)); + } + + template + void emplace_arg(const detail::named_arg& arg) { + if (named_info_.empty()) { + constexpr const detail::named_arg_info* zero_ptr{nullptr}; + data_.insert(data_.begin(), {zero_ptr, 0}); + } + data_.emplace_back(detail::make_arg(detail::unwrap(arg.value))); + auto pop_one = [](std::vector>* data) { + data->pop_back(); + }; + std::unique_ptr>, decltype(pop_one)> + guard{&data_, pop_one}; + named_info_.push_back({arg.name, static_cast(data_.size() - 2u)}); + data_[0].value_.named_args = {named_info_.data(), named_info_.size()}; + guard.release(); + } + + public: + /** + \rst + Adds an argument into the dynamic store for later passing to a formatting + function. + + Note that custom types and string types (but not string views) are copied + into the store dynamically allocating memory if necessary. + + **Example**:: + + fmt::dynamic_format_arg_store store; + store.push_back(42); + store.push_back("abc"); + store.push_back(1.5f); + std::string result = fmt::vformat("{} and {} and {}", store); + \endrst + */ + template void push_back(const T& arg) { + if (detail::const_check(need_copy::value)) + emplace_arg(dynamic_args_.push>(arg)); + else + emplace_arg(detail::unwrap(arg)); + } + + /** + \rst + Adds a reference to the argument into the dynamic store for later passing to + a formatting function. Supports named arguments wrapped in + ``std::reference_wrapper`` via ``std::ref()``/``std::cref()``. + + **Example**:: + + fmt::dynamic_format_arg_store store; + char str[] = "1234567890"; + store.push_back(std::cref(str)); + int a1_val{42}; + auto a1 = fmt::arg("a1_", a1_val); + store.push_back(std::cref(a1)); + + // Changing str affects the output but only for string and custom types. + str[0] = 'X'; + + std::string result = fmt::vformat("{} and {a1_}"); + assert(result == "X234567890 and 42"); + \endrst + */ + template void push_back(std::reference_wrapper arg) { + static_assert( + detail::is_named_arg::type>::value || + need_copy::value, + "objects of built-in types and string views are always copied"); + emplace_arg(arg.get()); + } + + /** + Adds named argument into the dynamic store for later passing to a formatting + function. ``std::reference_wrapper`` is supported to avoid copying of the + argument. + */ + template + void push_back(const detail::named_arg& arg) { + const char_type* arg_name = + dynamic_args_.push>(arg.name).c_str(); + if (detail::const_check(need_copy::value)) { + emplace_arg( + fmt::arg(arg_name, dynamic_args_.push>(arg.value))); + } else { + emplace_arg(fmt::arg(arg_name, arg.value)); + } + } + + /** Erase all elements from the store */ + void clear() { + data_.clear(); + named_info_.clear(); + dynamic_args_ = detail::dynamic_arg_list(); + } + + /** + \rst + Reserves space to store at least *new_cap* arguments including + *new_cap_named* named arguments. + \endrst + */ + void reserve(size_t new_cap, size_t new_cap_named) { + FMT_ASSERT(new_cap >= new_cap_named, + "Set of arguments includes set of named arguments"); + data_.reserve(new_cap); + named_info_.reserve(new_cap_named); + } +}; + +/** + \rst + A view of a collection of formatting arguments. To avoid lifetime issues it + should only be used as a parameter type in type-erased functions such as + ``vformat``:: + + void vlog(string_view format_str, format_args args); // OK + format_args args = make_format_args(42); // Error: dangling reference + \endrst + */ +template class basic_format_args { + public: + using size_type = int; + using format_arg = basic_format_arg; + + private: + // A descriptor that contains information about formatting arguments. + // If the number of arguments is less or equal to max_packed_args then + // argument types are passed in the descriptor. This reduces binary code size + // per formatting function call. + unsigned long long desc_; + union { + // If is_packed() returns true then argument values are stored in values_; + // otherwise they are stored in args_. This is done to improve cache + // locality and reduce compiled code size since storing larger objects + // may require more code (at least on x86-64) even if the same amount of + // data is actually copied to stack. It saves ~10% on the bloat test. + const detail::value* values_; + const format_arg* args_; + }; + + bool is_packed() const { return (desc_ & detail::is_unpacked_bit) == 0; } + bool has_named_args() const { + return (desc_ & detail::has_named_args_bit) != 0; + } + + detail::type type(int index) const { + int shift = index * detail::packed_arg_bits; + unsigned int mask = (1 << detail::packed_arg_bits) - 1; + return static_cast((desc_ >> shift) & mask); + } + + basic_format_args(unsigned long long desc, + const detail::value* values) + : desc_(desc), values_(values) {} + basic_format_args(unsigned long long desc, const format_arg* args) + : desc_(desc), args_(args) {} + + public: + basic_format_args() : desc_(0) {} + + /** + \rst + Constructs a `basic_format_args` object from `~fmt::format_arg_store`. + \endrst + */ + template + FMT_INLINE basic_format_args(const format_arg_store& store) + : basic_format_args(store.desc, store.data_.args()) {} + + /** + \rst + Constructs a `basic_format_args` object from + `~fmt::dynamic_format_arg_store`. + \endrst + */ + FMT_INLINE basic_format_args(const dynamic_format_arg_store& store) + : basic_format_args(store.get_types(), store.data()) {} + + /** + \rst + Constructs a `basic_format_args` object from a dynamic set of arguments. + \endrst + */ + basic_format_args(const format_arg* args, int count) + : basic_format_args(detail::is_unpacked_bit | detail::to_unsigned(count), + args) {} + + /** Returns the argument with the specified id. */ + format_arg get(int id) const { + format_arg arg; + if (!is_packed()) { + if (id < max_size()) arg = args_[id]; + return arg; + } + if (id >= detail::max_packed_args) return arg; + arg.type_ = type(id); + if (arg.type_ == detail::type::none_type) return arg; + arg.value_ = values_[id]; + return arg; + } + + template format_arg get(basic_string_view name) const { + int id = get_id(name); + return id >= 0 ? get(id) : format_arg(); + } + + template int get_id(basic_string_view name) const { + if (!has_named_args()) return -1; + const auto& named_args = + (is_packed() ? values_[-1] : args_[-1].value_).named_args; + for (size_t i = 0; i < named_args.size; ++i) { + if (named_args.data[i].name == name) return named_args.data[i].id; + } + return -1; + } + + int max_size() const { + unsigned long long max_packed = detail::max_packed_args; + return static_cast(is_packed() ? max_packed + : desc_ & ~detail::is_unpacked_bit); + } +}; + +#ifdef FMT_ARM_ABI_COMPATIBILITY +/** An alias to ``basic_format_args``. */ +// Separate types would result in shorter symbols but break ABI compatibility +// between clang and gcc on ARM (#1919). +using format_args = basic_format_args; +using wformat_args = basic_format_args; +#else +// DEPRECATED! These are kept for ABI compatibility. +// It is a separate type rather than an alias to make symbols readable. +struct format_args : basic_format_args { + template + FMT_INLINE format_args(const Args&... args) : basic_format_args(args...) {} +}; +struct wformat_args : basic_format_args { + using basic_format_args::basic_format_args; +}; +#endif + +namespace detail { + +template ::value)> +std::basic_string vformat( + basic_string_view format_str, + basic_format_args>> args); + +FMT_API std::string vformat(string_view format_str, format_args args); + +template +void vformat_to( + buffer& buf, basic_string_view format_str, + basic_format_args)> args, + detail::locale_ref loc = {}); + +template ::value)> +inline void vprint_mojibake(std::FILE*, basic_string_view, const Args&) {} + +FMT_API void vprint_mojibake(std::FILE*, string_view, format_args); +#ifndef _WIN32 +inline void vprint_mojibake(std::FILE*, string_view, format_args) {} +#endif +} // namespace detail + +/** Formats a string and writes the output to ``out``. */ +// GCC 8 and earlier cannot handle std::back_insert_iterator with +// vformat_to(...) overload, so SFINAE on iterator type instead. +template , + bool enable = detail::is_output_iterator::value> +auto vformat_to(OutputIt out, const S& format_str, + basic_format_args>> args) + -> typename std::enable_if::type { + decltype(detail::get_buffer(out)) buf(detail::get_buffer_init(out)); + detail::vformat_to(buf, to_string_view(format_str), args); + return detail::get_iterator(buf); +} + +/** + \rst + Formats arguments, writes the result to the output iterator ``out`` and returns + the iterator past the end of the output range. + + **Example**:: + + std::vector out; + fmt::format_to(std::back_inserter(out), "{}", 42); + \endrst + */ +// We cannot use FMT_ENABLE_IF because of a bug in gcc 8.3. +template >::value> +inline auto format_to(OutputIt out, const S& format_str, Args&&... args) -> + typename std::enable_if::type { + const auto& vargs = fmt::make_args_checked(format_str, args...); + return vformat_to(out, to_string_view(format_str), vargs); +} + +template struct format_to_n_result { + /** Iterator past the end of the output range. */ + OutputIt out; + /** Total (not truncated) output size. */ + size_t size; +}; + +template ::value)> +inline format_to_n_result vformat_to_n( + OutputIt out, size_t n, basic_string_view format_str, + basic_format_args>> args) { + detail::iterator_buffer buf(out, + n); + detail::vformat_to(buf, format_str, args); + return {buf.out(), buf.count()}; +} + +/** + \rst + Formats arguments, writes up to ``n`` characters of the result to the output + iterator ``out`` and returns the total output size and the iterator past the + end of the output range. + \endrst + */ +template >::value> +inline auto format_to_n(OutputIt out, size_t n, const S& format_str, + const Args&... args) -> + typename std::enable_if>::type { + const auto& vargs = fmt::make_args_checked(format_str, args...); + return vformat_to_n(out, n, to_string_view(format_str), vargs); +} + +/** + Returns the number of characters in the output of + ``format(format_str, args...)``. + */ +template +inline size_t formatted_size(string_view format_str, Args&&... args) { + const auto& vargs = fmt::make_args_checked(format_str, args...); + detail::counting_buffer<> buf; + detail::vformat_to(buf, format_str, vargs); + return buf.count(); +} + +template > +FMT_INLINE std::basic_string vformat( + const S& format_str, + basic_format_args>> args) { + return detail::vformat(to_string_view(format_str), args); +} + +/** + \rst + Formats arguments and returns the result as a string. + + **Example**:: + + #include + std::string message = fmt::format("The answer is {}", 42); + \endrst +*/ +// Pass char_t as a default template parameter instead of using +// std::basic_string> to reduce the symbol size. +template > +FMT_INLINE std::basic_string format(const S& format_str, Args&&... args) { + const auto& vargs = fmt::make_args_checked(format_str, args...); + return detail::vformat(to_string_view(format_str), vargs); +} + +FMT_API void vprint(string_view, format_args); +FMT_API void vprint(std::FILE*, string_view, format_args); + +/** + \rst + Formats ``args`` according to specifications in ``format_str`` and writes the + output to the file ``f``. Strings are assumed to be Unicode-encoded unless the + ``FMT_UNICODE`` macro is set to 0. + + **Example**:: + + fmt::print(stderr, "Don't {}!", "panic"); + \endrst + */ +template > +inline void print(std::FILE* f, const S& format_str, Args&&... args) { + const auto& vargs = fmt::make_args_checked(format_str, args...); + return detail::is_unicode() + ? vprint(f, to_string_view(format_str), vargs) + : detail::vprint_mojibake(f, to_string_view(format_str), vargs); +} + +/** + \rst + Formats ``args`` according to specifications in ``format_str`` and writes + the output to ``stdout``. Strings are assumed to be Unicode-encoded unless + the ``FMT_UNICODE`` macro is set to 0. + + **Example**:: + + fmt::print("Elapsed time: {0:.2f} seconds", 1.23); + \endrst + */ +template > +inline void print(const S& format_str, Args&&... args) { + const auto& vargs = fmt::make_args_checked(format_str, args...); + return detail::is_unicode() + ? vprint(to_string_view(format_str), vargs) + : detail::vprint_mojibake(stdout, to_string_view(format_str), + vargs); +} +FMT_END_NAMESPACE + +#endif // FMT_CORE_H_ diff --git a/source/fmt/format-inl.h b/source/fmt/format-inl.h new file mode 100644 index 00000000..8f2fe735 --- /dev/null +++ b/source/fmt/format-inl.h @@ -0,0 +1,2801 @@ +// Formatting library for C++ - implementation +// +// Copyright (c) 2012 - 2016, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_FORMAT_INL_H_ +#define FMT_FORMAT_INL_H_ + +#include +#include +#include +#include +#include +#include // std::memmove +#include +#include + +#ifndef FMT_STATIC_THOUSANDS_SEPARATOR +# include +#endif + +#ifdef _WIN32 +# include // _isatty +#endif + +#include "format.h" + +// Dummy implementations of strerror_r and strerror_s called if corresponding +// system functions are not available. +inline fmt::detail::null<> strerror_r(int, char*, ...) { return {}; } +inline fmt::detail::null<> strerror_s(char*, size_t, ...) { return {}; } + +FMT_BEGIN_NAMESPACE +namespace detail { + +FMT_FUNC void assert_fail(const char* file, int line, const char* message) { + // Use unchecked std::fprintf to avoid triggering another assertion when + // writing to stderr fails + std::fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message); + // Chosen instead of std::abort to satisfy Clang in CUDA mode during device + // code pass. + std::terminate(); +} + +#ifndef _MSC_VER +# define FMT_SNPRINTF snprintf +#else // _MSC_VER +inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) { + va_list args; + va_start(args, format); + int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args); + va_end(args); + return result; +} +# define FMT_SNPRINTF fmt_snprintf +#endif // _MSC_VER + +// A portable thread-safe version of strerror. +// Sets buffer to point to a string describing the error code. +// This can be either a pointer to a string stored in buffer, +// or a pointer to some static immutable string. +// Returns one of the following values: +// 0 - success +// ERANGE - buffer is not large enough to store the error message +// other - failure +// Buffer should be at least of size 1. +inline int safe_strerror(int error_code, char*& buffer, + size_t buffer_size) FMT_NOEXCEPT { + FMT_ASSERT(buffer != nullptr && buffer_size != 0, "invalid buffer"); + + class dispatcher { + private: + int error_code_; + char*& buffer_; + size_t buffer_size_; + + // A noop assignment operator to avoid bogus warnings. + void operator=(const dispatcher&) {} + + // Handle the result of XSI-compliant version of strerror_r. + int handle(int result) { + // glibc versions before 2.13 return result in errno. + return result == -1 ? errno : result; + } + + // Handle the result of GNU-specific version of strerror_r. + FMT_MAYBE_UNUSED + int handle(char* message) { + // If the buffer is full then the message is probably truncated. + if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1) + return ERANGE; + buffer_ = message; + return 0; + } + + // Handle the case when strerror_r is not available. + FMT_MAYBE_UNUSED + int handle(detail::null<>) { + return fallback(strerror_s(buffer_, buffer_size_, error_code_)); + } + + // Fallback to strerror_s when strerror_r is not available. + FMT_MAYBE_UNUSED + int fallback(int result) { + // If the buffer is full then the message is probably truncated. + return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? ERANGE + : result; + } + +#if !FMT_MSC_VER + // Fallback to strerror if strerror_r and strerror_s are not available. + int fallback(detail::null<>) { + errno = 0; + buffer_ = strerror(error_code_); + return errno; + } +#endif + + public: + dispatcher(int err_code, char*& buf, size_t buf_size) + : error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {} + + int run() { return handle(strerror_r(error_code_, buffer_, buffer_size_)); } + }; + return dispatcher(error_code, buffer, buffer_size).run(); +} + +FMT_FUNC void format_error_code(detail::buffer& out, int error_code, + string_view message) FMT_NOEXCEPT { + // Report error code making sure that the output fits into + // inline_buffer_size to avoid dynamic memory allocation and potential + // bad_alloc. + out.try_resize(0); + static const char SEP[] = ": "; + static const char ERROR_STR[] = "error "; + // Subtract 2 to account for terminating null characters in SEP and ERROR_STR. + size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2; + auto abs_value = static_cast>(error_code); + if (detail::is_negative(error_code)) { + abs_value = 0 - abs_value; + ++error_code_size; + } + error_code_size += detail::to_unsigned(detail::count_digits(abs_value)); + auto it = buffer_appender(out); + if (message.size() <= inline_buffer_size - error_code_size) + format_to(it, "{}{}", message, SEP); + format_to(it, "{}{}", ERROR_STR, error_code); + assert(out.size() <= inline_buffer_size); +} + +FMT_FUNC void report_error(format_func func, int error_code, + string_view message) FMT_NOEXCEPT { + memory_buffer full_message; + func(full_message, error_code, message); + // Don't use fwrite_fully because the latter may throw. + (void)std::fwrite(full_message.data(), full_message.size(), 1, stderr); + std::fputc('\n', stderr); +} + +// A wrapper around fwrite that throws on error. +inline void fwrite_fully(const void* ptr, size_t size, size_t count, + FILE* stream) { + size_t written = std::fwrite(ptr, size, count, stream); + if (written < count) FMT_THROW(system_error(errno, "cannot write to file")); +} +} // namespace detail + +#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) +namespace detail { + +template +locale_ref::locale_ref(const Locale& loc) : locale_(&loc) { + static_assert(std::is_same::value, ""); +} + +template Locale locale_ref::get() const { + static_assert(std::is_same::value, ""); + return locale_ ? *static_cast(locale_) : std::locale(); +} + +template FMT_FUNC std::string grouping_impl(locale_ref loc) { + return std::use_facet>(loc.get()).grouping(); +} +template FMT_FUNC Char thousands_sep_impl(locale_ref loc) { + return std::use_facet>(loc.get()) + .thousands_sep(); +} +template FMT_FUNC Char decimal_point_impl(locale_ref loc) { + return std::use_facet>(loc.get()) + .decimal_point(); +} +} // namespace detail +#else +template +FMT_FUNC std::string detail::grouping_impl(locale_ref) { + return "\03"; +} +template FMT_FUNC Char detail::thousands_sep_impl(locale_ref) { + return FMT_STATIC_THOUSANDS_SEPARATOR; +} +template FMT_FUNC Char detail::decimal_point_impl(locale_ref) { + return '.'; +} +#endif + +FMT_API FMT_FUNC format_error::~format_error() FMT_NOEXCEPT = default; +FMT_API FMT_FUNC system_error::~system_error() FMT_NOEXCEPT = default; + +FMT_FUNC void system_error::init(int err_code, string_view format_str, + format_args args) { + error_code_ = err_code; + memory_buffer buffer; + format_system_error(buffer, err_code, vformat(format_str, args)); + std::runtime_error& base = *this; + base = std::runtime_error(to_string(buffer)); +} + +namespace detail { + +template <> FMT_FUNC int count_digits<4>(detail::fallback_uintptr n) { + // fallback_uintptr is always stored in little endian. + int i = static_cast(sizeof(void*)) - 1; + while (i > 0 && n.value[i] == 0) --i; + auto char_digits = std::numeric_limits::digits / 4; + return i >= 0 ? i * char_digits + count_digits<4, unsigned>(n.value[i]) : 1; +} + +template +const typename basic_data::digit_pair basic_data::digits[] = { + {'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'}, + {'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, {'1', '0'}, {'1', '1'}, + {'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'}, + {'1', '8'}, {'1', '9'}, {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, + {'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'}, + {'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'}, + {'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, {'4', '0'}, {'4', '1'}, + {'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'}, + {'4', '8'}, {'4', '9'}, {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'}, + {'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'}, + {'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'}, + {'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, {'7', '0'}, {'7', '1'}, + {'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'}, + {'7', '8'}, {'7', '9'}, {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'}, + {'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'}, + {'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'}, + {'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}}; + +template +const char basic_data::hex_digits[] = "0123456789abcdef"; + +#define FMT_POWERS_OF_10(factor) \ + factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \ + (factor)*1000000, (factor)*10000000, (factor)*100000000, \ + (factor)*1000000000 + +template +const uint64_t basic_data::powers_of_10_64[] = { + 1, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL), + 10000000000000000000ULL}; + +template +const uint32_t basic_data::zero_or_powers_of_10_32[] = {0, + FMT_POWERS_OF_10(1)}; +template +const uint64_t basic_data::zero_or_powers_of_10_64[] = { + 0, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL), + 10000000000000000000ULL}; + +template +const uint32_t basic_data::zero_or_powers_of_10_32_new[] = { + 0, 0, FMT_POWERS_OF_10(1)}; + +template +const uint64_t basic_data::zero_or_powers_of_10_64_new[] = { + 0, 0, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL), + 10000000000000000000ULL}; + +// Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340. +// These are generated by support/compute-powers.py. +template +const uint64_t basic_data::grisu_pow10_significands[] = { + 0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76, + 0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df, + 0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c, + 0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5, + 0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57, + 0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7, + 0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e, + 0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996, + 0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126, + 0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053, + 0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f, + 0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b, + 0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06, + 0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb, + 0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000, + 0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984, + 0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068, + 0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8, + 0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758, + 0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85, + 0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d, + 0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25, + 0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2, + 0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a, + 0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410, + 0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129, + 0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85, + 0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841, + 0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b, +}; + +// Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding +// to significands above. +template +const int16_t basic_data::grisu_pow10_exponents[] = { + -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954, + -927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661, + -635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369, + -343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77, + -50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216, + 242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508, + 534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800, + 827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066}; + +template +const divtest_table_entry basic_data::divtest_table_for_pow5_32[] = + {{0x00000001, 0xffffffff}, {0xcccccccd, 0x33333333}, + {0xc28f5c29, 0x0a3d70a3}, {0x26e978d5, 0x020c49ba}, + {0x3afb7e91, 0x0068db8b}, {0x0bcbe61d, 0x0014f8b5}, + {0x68c26139, 0x000431bd}, {0xae8d46a5, 0x0000d6bf}, + {0x22e90e21, 0x00002af3}, {0x3a2e9c6d, 0x00000897}, + {0x3ed61f49, 0x000001b7}}; + +template +const divtest_table_entry basic_data::divtest_table_for_pow5_64[] = + {{0x0000000000000001, 0xffffffffffffffff}, + {0xcccccccccccccccd, 0x3333333333333333}, + {0x8f5c28f5c28f5c29, 0x0a3d70a3d70a3d70}, + {0x1cac083126e978d5, 0x020c49ba5e353f7c}, + {0xd288ce703afb7e91, 0x0068db8bac710cb2}, + {0x5d4e8fb00bcbe61d, 0x0014f8b588e368f0}, + {0x790fb65668c26139, 0x000431bde82d7b63}, + {0xe5032477ae8d46a5, 0x0000d6bf94d5e57a}, + {0xc767074b22e90e21, 0x00002af31dc46118}, + {0x8e47ce423a2e9c6d, 0x0000089705f4136b}, + {0x4fa7f60d3ed61f49, 0x000001b7cdfd9d7b}, + {0x0fee64690c913975, 0x00000057f5ff85e5}, + {0x3662e0e1cf503eb1, 0x000000119799812d}, + {0xa47a2cf9f6433fbd, 0x0000000384b84d09}, + {0x54186f653140a659, 0x00000000b424dc35}, + {0x7738164770402145, 0x0000000024075f3d}, + {0xe4a4d1417cd9a041, 0x000000000734aca5}, + {0xc75429d9e5c5200d, 0x000000000170ef54}, + {0xc1773b91fac10669, 0x000000000049c977}, + {0x26b172506559ce15, 0x00000000000ec1e4}, + {0xd489e3a9addec2d1, 0x000000000002f394}, + {0x90e860bb892c8d5d, 0x000000000000971d}, + {0x502e79bf1b6f4f79, 0x0000000000001e39}, + {0xdcd618596be30fe5, 0x000000000000060b}}; + +template +const uint64_t basic_data::dragonbox_pow10_significands_64[] = { + 0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f, + 0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb, + 0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28, + 0xf1c90080baf72cb2, 0x971da05074da7bef, 0xbce5086492111aeb, + 0xec1e4a7db69561a6, 0x9392ee8e921d5d08, 0xb877aa3236a4b44a, + 0xe69594bec44de15c, 0x901d7cf73ab0acda, 0xb424dc35095cd810, + 0xe12e13424bb40e14, 0x8cbccc096f5088cc, 0xafebff0bcb24aaff, + 0xdbe6fecebdedd5bf, 0x89705f4136b4a598, 0xabcc77118461cefd, + 0xd6bf94d5e57a42bd, 0x8637bd05af6c69b6, 0xa7c5ac471b478424, + 0xd1b71758e219652c, 0x83126e978d4fdf3c, 0xa3d70a3d70a3d70b, + 0xcccccccccccccccd, 0x8000000000000000, 0xa000000000000000, + 0xc800000000000000, 0xfa00000000000000, 0x9c40000000000000, + 0xc350000000000000, 0xf424000000000000, 0x9896800000000000, + 0xbebc200000000000, 0xee6b280000000000, 0x9502f90000000000, + 0xba43b74000000000, 0xe8d4a51000000000, 0x9184e72a00000000, + 0xb5e620f480000000, 0xe35fa931a0000000, 0x8e1bc9bf04000000, + 0xb1a2bc2ec5000000, 0xde0b6b3a76400000, 0x8ac7230489e80000, + 0xad78ebc5ac620000, 0xd8d726b7177a8000, 0x878678326eac9000, + 0xa968163f0a57b400, 0xd3c21bcecceda100, 0x84595161401484a0, + 0xa56fa5b99019a5c8, 0xcecb8f27f4200f3a, 0x813f3978f8940984, + 0xa18f07d736b90be5, 0xc9f2c9cd04674ede, 0xfc6f7c4045812296, + 0x9dc5ada82b70b59d, 0xc5371912364ce305, 0xf684df56c3e01bc6, + 0x9a130b963a6c115c, 0xc097ce7bc90715b3, 0xf0bdc21abb48db20, + 0x96769950b50d88f4, 0xbc143fa4e250eb31, 0xeb194f8e1ae525fd, + 0x92efd1b8d0cf37be, 0xb7abc627050305ad, 0xe596b7b0c643c719, + 0x8f7e32ce7bea5c6f, 0xb35dbf821ae4f38b, 0xe0352f62a19e306e}; + +template +const uint128_wrapper basic_data::dragonbox_pow10_significands_128[] = { +#if FMT_USE_FULL_CACHE_DRAGONBOX + {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, + {0x9faacf3df73609b1, 0x77b191618c54e9ad}, + {0xc795830d75038c1d, 0xd59df5b9ef6a2418}, + {0xf97ae3d0d2446f25, 0x4b0573286b44ad1e}, + {0x9becce62836ac577, 0x4ee367f9430aec33}, + {0xc2e801fb244576d5, 0x229c41f793cda740}, + {0xf3a20279ed56d48a, 0x6b43527578c11110}, + {0x9845418c345644d6, 0x830a13896b78aaaa}, + {0xbe5691ef416bd60c, 0x23cc986bc656d554}, + {0xedec366b11c6cb8f, 0x2cbfbe86b7ec8aa9}, + {0x94b3a202eb1c3f39, 0x7bf7d71432f3d6aa}, + {0xb9e08a83a5e34f07, 0xdaf5ccd93fb0cc54}, + {0xe858ad248f5c22c9, 0xd1b3400f8f9cff69}, + {0x91376c36d99995be, 0x23100809b9c21fa2}, + {0xb58547448ffffb2d, 0xabd40a0c2832a78b}, + {0xe2e69915b3fff9f9, 0x16c90c8f323f516d}, + {0x8dd01fad907ffc3b, 0xae3da7d97f6792e4}, + {0xb1442798f49ffb4a, 0x99cd11cfdf41779d}, + {0xdd95317f31c7fa1d, 0x40405643d711d584}, + {0x8a7d3eef7f1cfc52, 0x482835ea666b2573}, + {0xad1c8eab5ee43b66, 0xda3243650005eed0}, + {0xd863b256369d4a40, 0x90bed43e40076a83}, + {0x873e4f75e2224e68, 0x5a7744a6e804a292}, + {0xa90de3535aaae202, 0x711515d0a205cb37}, + {0xd3515c2831559a83, 0x0d5a5b44ca873e04}, + {0x8412d9991ed58091, 0xe858790afe9486c3}, + {0xa5178fff668ae0b6, 0x626e974dbe39a873}, + {0xce5d73ff402d98e3, 0xfb0a3d212dc81290}, + {0x80fa687f881c7f8e, 0x7ce66634bc9d0b9a}, + {0xa139029f6a239f72, 0x1c1fffc1ebc44e81}, + {0xc987434744ac874e, 0xa327ffb266b56221}, + {0xfbe9141915d7a922, 0x4bf1ff9f0062baa9}, + {0x9d71ac8fada6c9b5, 0x6f773fc3603db4aa}, + {0xc4ce17b399107c22, 0xcb550fb4384d21d4}, + {0xf6019da07f549b2b, 0x7e2a53a146606a49}, + {0x99c102844f94e0fb, 0x2eda7444cbfc426e}, + {0xc0314325637a1939, 0xfa911155fefb5309}, + {0xf03d93eebc589f88, 0x793555ab7eba27cb}, + {0x96267c7535b763b5, 0x4bc1558b2f3458df}, + {0xbbb01b9283253ca2, 0x9eb1aaedfb016f17}, + {0xea9c227723ee8bcb, 0x465e15a979c1cadd}, + {0x92a1958a7675175f, 0x0bfacd89ec191eca}, + {0xb749faed14125d36, 0xcef980ec671f667c}, + {0xe51c79a85916f484, 0x82b7e12780e7401b}, + {0x8f31cc0937ae58d2, 0xd1b2ecb8b0908811}, + {0xb2fe3f0b8599ef07, 0x861fa7e6dcb4aa16}, + {0xdfbdcece67006ac9, 0x67a791e093e1d49b}, + {0x8bd6a141006042bd, 0xe0c8bb2c5c6d24e1}, + {0xaecc49914078536d, 0x58fae9f773886e19}, + {0xda7f5bf590966848, 0xaf39a475506a899f}, + {0x888f99797a5e012d, 0x6d8406c952429604}, + {0xaab37fd7d8f58178, 0xc8e5087ba6d33b84}, + {0xd5605fcdcf32e1d6, 0xfb1e4a9a90880a65}, + {0x855c3be0a17fcd26, 0x5cf2eea09a550680}, + {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f}, + {0xd0601d8efc57b08b, 0xf13b94daf124da27}, + {0x823c12795db6ce57, 0x76c53d08d6b70859}, + {0xa2cb1717b52481ed, 0x54768c4b0c64ca6f}, + {0xcb7ddcdda26da268, 0xa9942f5dcf7dfd0a}, + {0xfe5d54150b090b02, 0xd3f93b35435d7c4d}, + {0x9efa548d26e5a6e1, 0xc47bc5014a1a6db0}, + {0xc6b8e9b0709f109a, 0x359ab6419ca1091c}, + {0xf867241c8cc6d4c0, 0xc30163d203c94b63}, + {0x9b407691d7fc44f8, 0x79e0de63425dcf1e}, + {0xc21094364dfb5636, 0x985915fc12f542e5}, + {0xf294b943e17a2bc4, 0x3e6f5b7b17b2939e}, + {0x979cf3ca6cec5b5a, 0xa705992ceecf9c43}, + {0xbd8430bd08277231, 0x50c6ff782a838354}, + {0xece53cec4a314ebd, 0xa4f8bf5635246429}, + {0x940f4613ae5ed136, 0x871b7795e136be9a}, + {0xb913179899f68584, 0x28e2557b59846e40}, + {0xe757dd7ec07426e5, 0x331aeada2fe589d0}, + {0x9096ea6f3848984f, 0x3ff0d2c85def7622}, + {0xb4bca50b065abe63, 0x0fed077a756b53aa}, + {0xe1ebce4dc7f16dfb, 0xd3e8495912c62895}, + {0x8d3360f09cf6e4bd, 0x64712dd7abbbd95d}, + {0xb080392cc4349dec, 0xbd8d794d96aacfb4}, + {0xdca04777f541c567, 0xecf0d7a0fc5583a1}, + {0x89e42caaf9491b60, 0xf41686c49db57245}, + {0xac5d37d5b79b6239, 0x311c2875c522ced6}, + {0xd77485cb25823ac7, 0x7d633293366b828c}, + {0x86a8d39ef77164bc, 0xae5dff9c02033198}, + {0xa8530886b54dbdeb, 0xd9f57f830283fdfd}, + {0xd267caa862a12d66, 0xd072df63c324fd7c}, + {0x8380dea93da4bc60, 0x4247cb9e59f71e6e}, + {0xa46116538d0deb78, 0x52d9be85f074e609}, + {0xcd795be870516656, 0x67902e276c921f8c}, + {0x806bd9714632dff6, 0x00ba1cd8a3db53b7}, + {0xa086cfcd97bf97f3, 0x80e8a40eccd228a5}, + {0xc8a883c0fdaf7df0, 0x6122cd128006b2ce}, + {0xfad2a4b13d1b5d6c, 0x796b805720085f82}, + {0x9cc3a6eec6311a63, 0xcbe3303674053bb1}, + {0xc3f490aa77bd60fc, 0xbedbfc4411068a9d}, + {0xf4f1b4d515acb93b, 0xee92fb5515482d45}, + {0x991711052d8bf3c5, 0x751bdd152d4d1c4b}, + {0xbf5cd54678eef0b6, 0xd262d45a78a0635e}, + {0xef340a98172aace4, 0x86fb897116c87c35}, + {0x9580869f0e7aac0e, 0xd45d35e6ae3d4da1}, + {0xbae0a846d2195712, 0x8974836059cca10a}, + {0xe998d258869facd7, 0x2bd1a438703fc94c}, + {0x91ff83775423cc06, 0x7b6306a34627ddd0}, + {0xb67f6455292cbf08, 0x1a3bc84c17b1d543}, + {0xe41f3d6a7377eeca, 0x20caba5f1d9e4a94}, + {0x8e938662882af53e, 0x547eb47b7282ee9d}, + {0xb23867fb2a35b28d, 0xe99e619a4f23aa44}, + {0xdec681f9f4c31f31, 0x6405fa00e2ec94d5}, + {0x8b3c113c38f9f37e, 0xde83bc408dd3dd05}, + {0xae0b158b4738705e, 0x9624ab50b148d446}, + {0xd98ddaee19068c76, 0x3badd624dd9b0958}, + {0x87f8a8d4cfa417c9, 0xe54ca5d70a80e5d7}, + {0xa9f6d30a038d1dbc, 0x5e9fcf4ccd211f4d}, + {0xd47487cc8470652b, 0x7647c32000696720}, + {0x84c8d4dfd2c63f3b, 0x29ecd9f40041e074}, + {0xa5fb0a17c777cf09, 0xf468107100525891}, + {0xcf79cc9db955c2cc, 0x7182148d4066eeb5}, + {0x81ac1fe293d599bf, 0xc6f14cd848405531}, + {0xa21727db38cb002f, 0xb8ada00e5a506a7d}, + {0xca9cf1d206fdc03b, 0xa6d90811f0e4851d}, + {0xfd442e4688bd304a, 0x908f4a166d1da664}, + {0x9e4a9cec15763e2e, 0x9a598e4e043287ff}, + {0xc5dd44271ad3cdba, 0x40eff1e1853f29fe}, + {0xf7549530e188c128, 0xd12bee59e68ef47d}, + {0x9a94dd3e8cf578b9, 0x82bb74f8301958cf}, + {0xc13a148e3032d6e7, 0xe36a52363c1faf02}, + {0xf18899b1bc3f8ca1, 0xdc44e6c3cb279ac2}, + {0x96f5600f15a7b7e5, 0x29ab103a5ef8c0ba}, + {0xbcb2b812db11a5de, 0x7415d448f6b6f0e8}, + {0xebdf661791d60f56, 0x111b495b3464ad22}, + {0x936b9fcebb25c995, 0xcab10dd900beec35}, + {0xb84687c269ef3bfb, 0x3d5d514f40eea743}, + {0xe65829b3046b0afa, 0x0cb4a5a3112a5113}, + {0x8ff71a0fe2c2e6dc, 0x47f0e785eaba72ac}, + {0xb3f4e093db73a093, 0x59ed216765690f57}, + {0xe0f218b8d25088b8, 0x306869c13ec3532d}, + {0x8c974f7383725573, 0x1e414218c73a13fc}, + {0xafbd2350644eeacf, 0xe5d1929ef90898fb}, + {0xdbac6c247d62a583, 0xdf45f746b74abf3a}, + {0x894bc396ce5da772, 0x6b8bba8c328eb784}, + {0xab9eb47c81f5114f, 0x066ea92f3f326565}, + {0xd686619ba27255a2, 0xc80a537b0efefebe}, + {0x8613fd0145877585, 0xbd06742ce95f5f37}, + {0xa798fc4196e952e7, 0x2c48113823b73705}, + {0xd17f3b51fca3a7a0, 0xf75a15862ca504c6}, + {0x82ef85133de648c4, 0x9a984d73dbe722fc}, + {0xa3ab66580d5fdaf5, 0xc13e60d0d2e0ebbb}, + {0xcc963fee10b7d1b3, 0x318df905079926a9}, + {0xffbbcfe994e5c61f, 0xfdf17746497f7053}, + {0x9fd561f1fd0f9bd3, 0xfeb6ea8bedefa634}, + {0xc7caba6e7c5382c8, 0xfe64a52ee96b8fc1}, + {0xf9bd690a1b68637b, 0x3dfdce7aa3c673b1}, + {0x9c1661a651213e2d, 0x06bea10ca65c084f}, + {0xc31bfa0fe5698db8, 0x486e494fcff30a63}, + {0xf3e2f893dec3f126, 0x5a89dba3c3efccfb}, + {0x986ddb5c6b3a76b7, 0xf89629465a75e01d}, + {0xbe89523386091465, 0xf6bbb397f1135824}, + {0xee2ba6c0678b597f, 0x746aa07ded582e2d}, + {0x94db483840b717ef, 0xa8c2a44eb4571cdd}, + {0xba121a4650e4ddeb, 0x92f34d62616ce414}, + {0xe896a0d7e51e1566, 0x77b020baf9c81d18}, + {0x915e2486ef32cd60, 0x0ace1474dc1d122f}, + {0xb5b5ada8aaff80b8, 0x0d819992132456bb}, + {0xe3231912d5bf60e6, 0x10e1fff697ed6c6a}, + {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2}, + {0xb1736b96b6fd83b3, 0xbd308ff8a6b17cb3}, + {0xddd0467c64bce4a0, 0xac7cb3f6d05ddbdf}, + {0x8aa22c0dbef60ee4, 0x6bcdf07a423aa96c}, + {0xad4ab7112eb3929d, 0x86c16c98d2c953c7}, + {0xd89d64d57a607744, 0xe871c7bf077ba8b8}, + {0x87625f056c7c4a8b, 0x11471cd764ad4973}, + {0xa93af6c6c79b5d2d, 0xd598e40d3dd89bd0}, + {0xd389b47879823479, 0x4aff1d108d4ec2c4}, + {0x843610cb4bf160cb, 0xcedf722a585139bb}, + {0xa54394fe1eedb8fe, 0xc2974eb4ee658829}, + {0xce947a3da6a9273e, 0x733d226229feea33}, + {0x811ccc668829b887, 0x0806357d5a3f5260}, + {0xa163ff802a3426a8, 0xca07c2dcb0cf26f8}, + {0xc9bcff6034c13052, 0xfc89b393dd02f0b6}, + {0xfc2c3f3841f17c67, 0xbbac2078d443ace3}, + {0x9d9ba7832936edc0, 0xd54b944b84aa4c0e}, + {0xc5029163f384a931, 0x0a9e795e65d4df12}, + {0xf64335bcf065d37d, 0x4d4617b5ff4a16d6}, + {0x99ea0196163fa42e, 0x504bced1bf8e4e46}, + {0xc06481fb9bcf8d39, 0xe45ec2862f71e1d7}, + {0xf07da27a82c37088, 0x5d767327bb4e5a4d}, + {0x964e858c91ba2655, 0x3a6a07f8d510f870}, + {0xbbe226efb628afea, 0x890489f70a55368c}, + {0xeadab0aba3b2dbe5, 0x2b45ac74ccea842f}, + {0x92c8ae6b464fc96f, 0x3b0b8bc90012929e}, + {0xb77ada0617e3bbcb, 0x09ce6ebb40173745}, + {0xe55990879ddcaabd, 0xcc420a6a101d0516}, + {0x8f57fa54c2a9eab6, 0x9fa946824a12232e}, + {0xb32df8e9f3546564, 0x47939822dc96abfa}, + {0xdff9772470297ebd, 0x59787e2b93bc56f8}, + {0x8bfbea76c619ef36, 0x57eb4edb3c55b65b}, + {0xaefae51477a06b03, 0xede622920b6b23f2}, + {0xdab99e59958885c4, 0xe95fab368e45ecee}, + {0x88b402f7fd75539b, 0x11dbcb0218ebb415}, + {0xaae103b5fcd2a881, 0xd652bdc29f26a11a}, + {0xd59944a37c0752a2, 0x4be76d3346f04960}, + {0x857fcae62d8493a5, 0x6f70a4400c562ddc}, + {0xa6dfbd9fb8e5b88e, 0xcb4ccd500f6bb953}, + {0xd097ad07a71f26b2, 0x7e2000a41346a7a8}, + {0x825ecc24c873782f, 0x8ed400668c0c28c9}, + {0xa2f67f2dfa90563b, 0x728900802f0f32fb}, + {0xcbb41ef979346bca, 0x4f2b40a03ad2ffba}, + {0xfea126b7d78186bc, 0xe2f610c84987bfa9}, + {0x9f24b832e6b0f436, 0x0dd9ca7d2df4d7ca}, + {0xc6ede63fa05d3143, 0x91503d1c79720dbc}, + {0xf8a95fcf88747d94, 0x75a44c6397ce912b}, + {0x9b69dbe1b548ce7c, 0xc986afbe3ee11abb}, + {0xc24452da229b021b, 0xfbe85badce996169}, + {0xf2d56790ab41c2a2, 0xfae27299423fb9c4}, + {0x97c560ba6b0919a5, 0xdccd879fc967d41b}, + {0xbdb6b8e905cb600f, 0x5400e987bbc1c921}, + {0xed246723473e3813, 0x290123e9aab23b69}, + {0x9436c0760c86e30b, 0xf9a0b6720aaf6522}, + {0xb94470938fa89bce, 0xf808e40e8d5b3e6a}, + {0xe7958cb87392c2c2, 0xb60b1d1230b20e05}, + {0x90bd77f3483bb9b9, 0xb1c6f22b5e6f48c3}, + {0xb4ecd5f01a4aa828, 0x1e38aeb6360b1af4}, + {0xe2280b6c20dd5232, 0x25c6da63c38de1b1}, + {0x8d590723948a535f, 0x579c487e5a38ad0f}, + {0xb0af48ec79ace837, 0x2d835a9df0c6d852}, + {0xdcdb1b2798182244, 0xf8e431456cf88e66}, + {0x8a08f0f8bf0f156b, 0x1b8e9ecb641b5900}, + {0xac8b2d36eed2dac5, 0xe272467e3d222f40}, + {0xd7adf884aa879177, 0x5b0ed81dcc6abb10}, + {0x86ccbb52ea94baea, 0x98e947129fc2b4ea}, + {0xa87fea27a539e9a5, 0x3f2398d747b36225}, + {0xd29fe4b18e88640e, 0x8eec7f0d19a03aae}, + {0x83a3eeeef9153e89, 0x1953cf68300424ad}, + {0xa48ceaaab75a8e2b, 0x5fa8c3423c052dd8}, + {0xcdb02555653131b6, 0x3792f412cb06794e}, + {0x808e17555f3ebf11, 0xe2bbd88bbee40bd1}, + {0xa0b19d2ab70e6ed6, 0x5b6aceaeae9d0ec5}, + {0xc8de047564d20a8b, 0xf245825a5a445276}, + {0xfb158592be068d2e, 0xeed6e2f0f0d56713}, + {0x9ced737bb6c4183d, 0x55464dd69685606c}, + {0xc428d05aa4751e4c, 0xaa97e14c3c26b887}, + {0xf53304714d9265df, 0xd53dd99f4b3066a9}, + {0x993fe2c6d07b7fab, 0xe546a8038efe402a}, + {0xbf8fdb78849a5f96, 0xde98520472bdd034}, + {0xef73d256a5c0f77c, 0x963e66858f6d4441}, + {0x95a8637627989aad, 0xdde7001379a44aa9}, + {0xbb127c53b17ec159, 0x5560c018580d5d53}, + {0xe9d71b689dde71af, 0xaab8f01e6e10b4a7}, + {0x9226712162ab070d, 0xcab3961304ca70e9}, + {0xb6b00d69bb55c8d1, 0x3d607b97c5fd0d23}, + {0xe45c10c42a2b3b05, 0x8cb89a7db77c506b}, + {0x8eb98a7a9a5b04e3, 0x77f3608e92adb243}, + {0xb267ed1940f1c61c, 0x55f038b237591ed4}, + {0xdf01e85f912e37a3, 0x6b6c46dec52f6689}, + {0x8b61313bbabce2c6, 0x2323ac4b3b3da016}, + {0xae397d8aa96c1b77, 0xabec975e0a0d081b}, + {0xd9c7dced53c72255, 0x96e7bd358c904a22}, + {0x881cea14545c7575, 0x7e50d64177da2e55}, + {0xaa242499697392d2, 0xdde50bd1d5d0b9ea}, + {0xd4ad2dbfc3d07787, 0x955e4ec64b44e865}, + {0x84ec3c97da624ab4, 0xbd5af13bef0b113f}, + {0xa6274bbdd0fadd61, 0xecb1ad8aeacdd58f}, + {0xcfb11ead453994ba, 0x67de18eda5814af3}, + {0x81ceb32c4b43fcf4, 0x80eacf948770ced8}, + {0xa2425ff75e14fc31, 0xa1258379a94d028e}, + {0xcad2f7f5359a3b3e, 0x096ee45813a04331}, + {0xfd87b5f28300ca0d, 0x8bca9d6e188853fd}, + {0x9e74d1b791e07e48, 0x775ea264cf55347e}, + {0xc612062576589dda, 0x95364afe032a819e}, + {0xf79687aed3eec551, 0x3a83ddbd83f52205}, + {0x9abe14cd44753b52, 0xc4926a9672793543}, + {0xc16d9a0095928a27, 0x75b7053c0f178294}, + {0xf1c90080baf72cb1, 0x5324c68b12dd6339}, + {0x971da05074da7bee, 0xd3f6fc16ebca5e04}, + {0xbce5086492111aea, 0x88f4bb1ca6bcf585}, + {0xec1e4a7db69561a5, 0x2b31e9e3d06c32e6}, + {0x9392ee8e921d5d07, 0x3aff322e62439fd0}, + {0xb877aa3236a4b449, 0x09befeb9fad487c3}, + {0xe69594bec44de15b, 0x4c2ebe687989a9b4}, + {0x901d7cf73ab0acd9, 0x0f9d37014bf60a11}, + {0xb424dc35095cd80f, 0x538484c19ef38c95}, + {0xe12e13424bb40e13, 0x2865a5f206b06fba}, + {0x8cbccc096f5088cb, 0xf93f87b7442e45d4}, + {0xafebff0bcb24aafe, 0xf78f69a51539d749}, + {0xdbe6fecebdedd5be, 0xb573440e5a884d1c}, + {0x89705f4136b4a597, 0x31680a88f8953031}, + {0xabcc77118461cefc, 0xfdc20d2b36ba7c3e}, + {0xd6bf94d5e57a42bc, 0x3d32907604691b4d}, + {0x8637bd05af6c69b5, 0xa63f9a49c2c1b110}, + {0xa7c5ac471b478423, 0x0fcf80dc33721d54}, + {0xd1b71758e219652b, 0xd3c36113404ea4a9}, + {0x83126e978d4fdf3b, 0x645a1cac083126ea}, + {0xa3d70a3d70a3d70a, 0x3d70a3d70a3d70a4}, + {0xcccccccccccccccc, 0xcccccccccccccccd}, + {0x8000000000000000, 0x0000000000000000}, + {0xa000000000000000, 0x0000000000000000}, + {0xc800000000000000, 0x0000000000000000}, + {0xfa00000000000000, 0x0000000000000000}, + {0x9c40000000000000, 0x0000000000000000}, + {0xc350000000000000, 0x0000000000000000}, + {0xf424000000000000, 0x0000000000000000}, + {0x9896800000000000, 0x0000000000000000}, + {0xbebc200000000000, 0x0000000000000000}, + {0xee6b280000000000, 0x0000000000000000}, + {0x9502f90000000000, 0x0000000000000000}, + {0xba43b74000000000, 0x0000000000000000}, + {0xe8d4a51000000000, 0x0000000000000000}, + {0x9184e72a00000000, 0x0000000000000000}, + {0xb5e620f480000000, 0x0000000000000000}, + {0xe35fa931a0000000, 0x0000000000000000}, + {0x8e1bc9bf04000000, 0x0000000000000000}, + {0xb1a2bc2ec5000000, 0x0000000000000000}, + {0xde0b6b3a76400000, 0x0000000000000000}, + {0x8ac7230489e80000, 0x0000000000000000}, + {0xad78ebc5ac620000, 0x0000000000000000}, + {0xd8d726b7177a8000, 0x0000000000000000}, + {0x878678326eac9000, 0x0000000000000000}, + {0xa968163f0a57b400, 0x0000000000000000}, + {0xd3c21bcecceda100, 0x0000000000000000}, + {0x84595161401484a0, 0x0000000000000000}, + {0xa56fa5b99019a5c8, 0x0000000000000000}, + {0xcecb8f27f4200f3a, 0x0000000000000000}, + {0x813f3978f8940984, 0x4000000000000000}, + {0xa18f07d736b90be5, 0x5000000000000000}, + {0xc9f2c9cd04674ede, 0xa400000000000000}, + {0xfc6f7c4045812296, 0x4d00000000000000}, + {0x9dc5ada82b70b59d, 0xf020000000000000}, + {0xc5371912364ce305, 0x6c28000000000000}, + {0xf684df56c3e01bc6, 0xc732000000000000}, + {0x9a130b963a6c115c, 0x3c7f400000000000}, + {0xc097ce7bc90715b3, 0x4b9f100000000000}, + {0xf0bdc21abb48db20, 0x1e86d40000000000}, + {0x96769950b50d88f4, 0x1314448000000000}, + {0xbc143fa4e250eb31, 0x17d955a000000000}, + {0xeb194f8e1ae525fd, 0x5dcfab0800000000}, + {0x92efd1b8d0cf37be, 0x5aa1cae500000000}, + {0xb7abc627050305ad, 0xf14a3d9e40000000}, + {0xe596b7b0c643c719, 0x6d9ccd05d0000000}, + {0x8f7e32ce7bea5c6f, 0xe4820023a2000000}, + {0xb35dbf821ae4f38b, 0xdda2802c8a800000}, + {0xe0352f62a19e306e, 0xd50b2037ad200000}, + {0x8c213d9da502de45, 0x4526f422cc340000}, + {0xaf298d050e4395d6, 0x9670b12b7f410000}, + {0xdaf3f04651d47b4c, 0x3c0cdd765f114000}, + {0x88d8762bf324cd0f, 0xa5880a69fb6ac800}, + {0xab0e93b6efee0053, 0x8eea0d047a457a00}, + {0xd5d238a4abe98068, 0x72a4904598d6d880}, + {0x85a36366eb71f041, 0x47a6da2b7f864750}, + {0xa70c3c40a64e6c51, 0x999090b65f67d924}, + {0xd0cf4b50cfe20765, 0xfff4b4e3f741cf6d}, + {0x82818f1281ed449f, 0xbff8f10e7a8921a4}, + {0xa321f2d7226895c7, 0xaff72d52192b6a0d}, + {0xcbea6f8ceb02bb39, 0x9bf4f8a69f764490}, + {0xfee50b7025c36a08, 0x02f236d04753d5b4}, + {0x9f4f2726179a2245, 0x01d762422c946590}, + {0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef5}, + {0xf8ebad2b84e0d58b, 0xd2e0898765a7deb2}, + {0x9b934c3b330c8577, 0x63cc55f49f88eb2f}, + {0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fb}, + {0xf316271c7fc3908a, 0x8bef464e3945ef7a}, + {0x97edd871cfda3a56, 0x97758bf0e3cbb5ac}, + {0xbde94e8e43d0c8ec, 0x3d52eeed1cbea317}, + {0xed63a231d4c4fb27, 0x4ca7aaa863ee4bdd}, + {0x945e455f24fb1cf8, 0x8fe8caa93e74ef6a}, + {0xb975d6b6ee39e436, 0xb3e2fd538e122b44}, + {0xe7d34c64a9c85d44, 0x60dbbca87196b616}, + {0x90e40fbeea1d3a4a, 0xbc8955e946fe31cd}, + {0xb51d13aea4a488dd, 0x6babab6398bdbe41}, + {0xe264589a4dcdab14, 0xc696963c7eed2dd1}, + {0x8d7eb76070a08aec, 0xfc1e1de5cf543ca2}, + {0xb0de65388cc8ada8, 0x3b25a55f43294bcb}, + {0xdd15fe86affad912, 0x49ef0eb713f39ebe}, + {0x8a2dbf142dfcc7ab, 0x6e3569326c784337}, + {0xacb92ed9397bf996, 0x49c2c37f07965404}, + {0xd7e77a8f87daf7fb, 0xdc33745ec97be906}, + {0x86f0ac99b4e8dafd, 0x69a028bb3ded71a3}, + {0xa8acd7c0222311bc, 0xc40832ea0d68ce0c}, + {0xd2d80db02aabd62b, 0xf50a3fa490c30190}, + {0x83c7088e1aab65db, 0x792667c6da79e0fa}, + {0xa4b8cab1a1563f52, 0x577001b891185938}, + {0xcde6fd5e09abcf26, 0xed4c0226b55e6f86}, + {0x80b05e5ac60b6178, 0x544f8158315b05b4}, + {0xa0dc75f1778e39d6, 0x696361ae3db1c721}, + {0xc913936dd571c84c, 0x03bc3a19cd1e38e9}, + {0xfb5878494ace3a5f, 0x04ab48a04065c723}, + {0x9d174b2dcec0e47b, 0x62eb0d64283f9c76}, + {0xc45d1df942711d9a, 0x3ba5d0bd324f8394}, + {0xf5746577930d6500, 0xca8f44ec7ee36479}, + {0x9968bf6abbe85f20, 0x7e998b13cf4e1ecb}, + {0xbfc2ef456ae276e8, 0x9e3fedd8c321a67e}, + {0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101e}, + {0x95d04aee3b80ece5, 0xbba1f1d158724a12}, + {0xbb445da9ca61281f, 0x2a8a6e45ae8edc97}, + {0xea1575143cf97226, 0xf52d09d71a3293bd}, + {0x924d692ca61be758, 0x593c2626705f9c56}, + {0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836c}, + {0xe498f455c38b997a, 0x0b6dfb9c0f956447}, + {0x8edf98b59a373fec, 0x4724bd4189bd5eac}, + {0xb2977ee300c50fe7, 0x58edec91ec2cb657}, + {0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ed}, + {0x8b865b215899f46c, 0xbd79e0d20082ee74}, + {0xae67f1e9aec07187, 0xecd8590680a3aa11}, + {0xda01ee641a708de9, 0xe80e6f4820cc9495}, + {0x884134fe908658b2, 0x3109058d147fdcdd}, + {0xaa51823e34a7eede, 0xbd4b46f0599fd415}, + {0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91a}, + {0x850fadc09923329e, 0x03e2cf6bc604ddb0}, + {0xa6539930bf6bff45, 0x84db8346b786151c}, + {0xcfe87f7cef46ff16, 0xe612641865679a63}, + {0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07e}, + {0xa26da3999aef7749, 0xe3be5e330f38f09d}, + {0xcb090c8001ab551c, 0x5cadf5bfd3072cc5}, + {0xfdcb4fa002162a63, 0x73d9732fc7c8f7f6}, + {0x9e9f11c4014dda7e, 0x2867e7fddcdd9afa}, + {0xc646d63501a1511d, 0xb281e1fd541501b8}, + {0xf7d88bc24209a565, 0x1f225a7ca91a4226}, + {0x9ae757596946075f, 0x3375788de9b06958}, + {0xc1a12d2fc3978937, 0x0052d6b1641c83ae}, + {0xf209787bb47d6b84, 0xc0678c5dbd23a49a}, + {0x9745eb4d50ce6332, 0xf840b7ba963646e0}, + {0xbd176620a501fbff, 0xb650e5a93bc3d898}, + {0xec5d3fa8ce427aff, 0xa3e51f138ab4cebe}, + {0x93ba47c980e98cdf, 0xc66f336c36b10137}, + {0xb8a8d9bbe123f017, 0xb80b0047445d4184}, + {0xe6d3102ad96cec1d, 0xa60dc059157491e5}, + {0x9043ea1ac7e41392, 0x87c89837ad68db2f}, + {0xb454e4a179dd1877, 0x29babe4598c311fb}, + {0xe16a1dc9d8545e94, 0xf4296dd6fef3d67a}, + {0x8ce2529e2734bb1d, 0x1899e4a65f58660c}, + {0xb01ae745b101e9e4, 0x5ec05dcff72e7f8f}, + {0xdc21a1171d42645d, 0x76707543f4fa1f73}, + {0x899504ae72497eba, 0x6a06494a791c53a8}, + {0xabfa45da0edbde69, 0x0487db9d17636892}, + {0xd6f8d7509292d603, 0x45a9d2845d3c42b6}, + {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b2}, + {0xa7f26836f282b732, 0x8e6cac7768d7141e}, + {0xd1ef0244af2364ff, 0x3207d795430cd926}, + {0x8335616aed761f1f, 0x7f44e6bd49e807b8}, + {0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a6}, + {0xcd036837130890a1, 0x36dba887c37a8c0f}, + {0x802221226be55a64, 0xc2494954da2c9789}, + {0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6c}, + {0xc83553c5c8965d3d, 0x6f92829494e5acc7}, + {0xfa42a8b73abbf48c, 0xcb772339ba1f17f9}, + {0x9c69a97284b578d7, 0xff2a760414536efb}, + {0xc38413cf25e2d70d, 0xfef5138519684aba}, + {0xf46518c2ef5b8cd1, 0x7eb258665fc25d69}, + {0x98bf2f79d5993802, 0xef2f773ffbd97a61}, + {0xbeeefb584aff8603, 0xaafb550ffacfd8fa}, + {0xeeaaba2e5dbf6784, 0x95ba2a53f983cf38}, + {0x952ab45cfa97a0b2, 0xdd945a747bf26183}, + {0xba756174393d88df, 0x94f971119aeef9e4}, + {0xe912b9d1478ceb17, 0x7a37cd5601aab85d}, + {0x91abb422ccb812ee, 0xac62e055c10ab33a}, + {0xb616a12b7fe617aa, 0x577b986b314d6009}, + {0xe39c49765fdf9d94, 0xed5a7e85fda0b80b}, + {0x8e41ade9fbebc27d, 0x14588f13be847307}, + {0xb1d219647ae6b31c, 0x596eb2d8ae258fc8}, + {0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bb}, + {0x8aec23d680043bee, 0x25de7bb9480d5854}, + {0xada72ccc20054ae9, 0xaf561aa79a10ae6a}, + {0xd910f7ff28069da4, 0x1b2ba1518094da04}, + {0x87aa9aff79042286, 0x90fb44d2f05d0842}, + {0xa99541bf57452b28, 0x353a1607ac744a53}, + {0xd3fa922f2d1675f2, 0x42889b8997915ce8}, + {0x847c9b5d7c2e09b7, 0x69956135febada11}, + {0xa59bc234db398c25, 0x43fab9837e699095}, + {0xcf02b2c21207ef2e, 0x94f967e45e03f4bb}, + {0x8161afb94b44f57d, 0x1d1be0eebac278f5}, + {0xa1ba1ba79e1632dc, 0x6462d92a69731732}, + {0xca28a291859bbf93, 0x7d7b8f7503cfdcfe}, + {0xfcb2cb35e702af78, 0x5cda735244c3d43e}, + {0x9defbf01b061adab, 0x3a0888136afa64a7}, + {0xc56baec21c7a1916, 0x088aaa1845b8fdd0}, + {0xf6c69a72a3989f5b, 0x8aad549e57273d45}, + {0x9a3c2087a63f6399, 0x36ac54e2f678864b}, + {0xc0cb28a98fcf3c7f, 0x84576a1bb416a7dd}, + {0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d5}, + {0x969eb7c47859e743, 0x9f644ae5a4b1b325}, + {0xbc4665b596706114, 0x873d5d9f0dde1fee}, + {0xeb57ff22fc0c7959, 0xa90cb506d155a7ea}, + {0x9316ff75dd87cbd8, 0x09a7f12442d588f2}, + {0xb7dcbf5354e9bece, 0x0c11ed6d538aeb2f}, + {0xe5d3ef282a242e81, 0x8f1668c8a86da5fa}, + {0x8fa475791a569d10, 0xf96e017d694487bc}, + {0xb38d92d760ec4455, 0x37c981dcc395a9ac}, + {0xe070f78d3927556a, 0x85bbe253f47b1417}, + {0x8c469ab843b89562, 0x93956d7478ccec8e}, + {0xaf58416654a6babb, 0x387ac8d1970027b2}, + {0xdb2e51bfe9d0696a, 0x06997b05fcc0319e}, + {0x88fcf317f22241e2, 0x441fece3bdf81f03}, + {0xab3c2fddeeaad25a, 0xd527e81cad7626c3}, + {0xd60b3bd56a5586f1, 0x8a71e223d8d3b074}, + {0x85c7056562757456, 0xf6872d5667844e49}, + {0xa738c6bebb12d16c, 0xb428f8ac016561db}, + {0xd106f86e69d785c7, 0xe13336d701beba52}, + {0x82a45b450226b39c, 0xecc0024661173473}, + {0xa34d721642b06084, 0x27f002d7f95d0190}, + {0xcc20ce9bd35c78a5, 0x31ec038df7b441f4}, + {0xff290242c83396ce, 0x7e67047175a15271}, + {0x9f79a169bd203e41, 0x0f0062c6e984d386}, + {0xc75809c42c684dd1, 0x52c07b78a3e60868}, + {0xf92e0c3537826145, 0xa7709a56ccdf8a82}, + {0x9bbcc7a142b17ccb, 0x88a66076400bb691}, + {0xc2abf989935ddbfe, 0x6acff893d00ea435}, + {0xf356f7ebf83552fe, 0x0583f6b8c4124d43}, + {0x98165af37b2153de, 0xc3727a337a8b704a}, + {0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5c}, + {0xeda2ee1c7064130c, 0x1162def06f79df73}, + {0x9485d4d1c63e8be7, 0x8addcb5645ac2ba8}, + {0xb9a74a0637ce2ee1, 0x6d953e2bd7173692}, + {0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0437}, + {0x910ab1d4db9914a0, 0x1d9c9892400a22a2}, + {0xb54d5e4a127f59c8, 0x2503beb6d00cab4b}, + {0xe2a0b5dc971f303a, 0x2e44ae64840fd61d}, + {0x8da471a9de737e24, 0x5ceaecfed289e5d2}, + {0xb10d8e1456105dad, 0x7425a83e872c5f47}, + {0xdd50f1996b947518, 0xd12f124e28f77719}, + {0x8a5296ffe33cc92f, 0x82bd6b70d99aaa6f}, + {0xace73cbfdc0bfb7b, 0x636cc64d1001550b}, + {0xd8210befd30efa5a, 0x3c47f7e05401aa4e}, + {0x8714a775e3e95c78, 0x65acfaec34810a71}, + {0xa8d9d1535ce3b396, 0x7f1839a741a14d0d}, + {0xd31045a8341ca07c, 0x1ede48111209a050}, + {0x83ea2b892091e44d, 0x934aed0aab460432}, + {0xa4e4b66b68b65d60, 0xf81da84d5617853f}, + {0xce1de40642e3f4b9, 0x36251260ab9d668e}, + {0x80d2ae83e9ce78f3, 0xc1d72b7c6b426019}, + {0xa1075a24e4421730, 0xb24cf65b8612f81f}, + {0xc94930ae1d529cfc, 0xdee033f26797b627}, + {0xfb9b7cd9a4a7443c, 0x169840ef017da3b1}, + {0x9d412e0806e88aa5, 0x8e1f289560ee864e}, + {0xc491798a08a2ad4e, 0xf1a6f2bab92a27e2}, + {0xf5b5d7ec8acb58a2, 0xae10af696774b1db}, + {0x9991a6f3d6bf1765, 0xacca6da1e0a8ef29}, + {0xbff610b0cc6edd3f, 0x17fd090a58d32af3}, + {0xeff394dcff8a948e, 0xddfc4b4cef07f5b0}, + {0x95f83d0a1fb69cd9, 0x4abdaf101564f98e}, + {0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f1}, + {0xea53df5fd18d5513, 0x84c86189216dc5ed}, + {0x92746b9be2f8552c, 0x32fd3cf5b4e49bb4}, + {0xb7118682dbb66a77, 0x3fbc8c33221dc2a1}, + {0xe4d5e82392a40515, 0x0fabaf3feaa5334a}, + {0x8f05b1163ba6832d, 0x29cb4d87f2a7400e}, + {0xb2c71d5bca9023f8, 0x743e20e9ef511012}, + {0xdf78e4b2bd342cf6, 0x914da9246b255416}, + {0x8bab8eefb6409c1a, 0x1ad089b6c2f7548e}, + {0xae9672aba3d0c320, 0xa184ac2473b529b1}, + {0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741e}, + {0x8865899617fb1871, 0x7e2fa67c7a658892}, + {0xaa7eebfb9df9de8d, 0xddbb901b98feeab7}, + {0xd51ea6fa85785631, 0x552a74227f3ea565}, + {0x8533285c936b35de, 0xd53a88958f87275f}, + {0xa67ff273b8460356, 0x8a892abaf368f137}, + {0xd01fef10a657842c, 0x2d2b7569b0432d85}, + {0x8213f56a67f6b29b, 0x9c3b29620e29fc73}, + {0xa298f2c501f45f42, 0x8349f3ba91b47b8f}, + {0xcb3f2f7642717713, 0x241c70a936219a73}, + {0xfe0efb53d30dd4d7, 0xed238cd383aa0110}, + {0x9ec95d1463e8a506, 0xf4363804324a40aa}, + {0xc67bb4597ce2ce48, 0xb143c6053edcd0d5}, + {0xf81aa16fdc1b81da, 0xdd94b7868e94050a}, + {0x9b10a4e5e9913128, 0xca7cf2b4191c8326}, + {0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f0}, + {0xf24a01a73cf2dccf, 0xbc633b39673c8cec}, + {0x976e41088617ca01, 0xd5be0503e085d813}, + {0xbd49d14aa79dbc82, 0x4b2d8644d8a74e18}, + {0xec9c459d51852ba2, 0xddf8e7d60ed1219e}, + {0x93e1ab8252f33b45, 0xcabb90e5c942b503}, + {0xb8da1662e7b00a17, 0x3d6a751f3b936243}, + {0xe7109bfba19c0c9d, 0x0cc512670a783ad4}, + {0x906a617d450187e2, 0x27fb2b80668b24c5}, + {0xb484f9dc9641e9da, 0xb1f9f660802dedf6}, + {0xe1a63853bbd26451, 0x5e7873f8a0396973}, + {0x8d07e33455637eb2, 0xdb0b487b6423e1e8}, + {0xb049dc016abc5e5f, 0x91ce1a9a3d2cda62}, + {0xdc5c5301c56b75f7, 0x7641a140cc7810fb}, + {0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9d}, + {0xac2820d9623bf429, 0x546345fa9fbdcd44}, + {0xd732290fbacaf133, 0xa97c177947ad4095}, + {0x867f59a9d4bed6c0, 0x49ed8eabcccc485d}, + {0xa81f301449ee8c70, 0x5c68f256bfff5a74}, + {0xd226fc195c6a2f8c, 0x73832eec6fff3111}, + {0x83585d8fd9c25db7, 0xc831fd53c5ff7eab}, + {0xa42e74f3d032f525, 0xba3e7ca8b77f5e55}, + {0xcd3a1230c43fb26f, 0x28ce1bd2e55f35eb}, + {0x80444b5e7aa7cf85, 0x7980d163cf5b81b3}, + {0xa0555e361951c366, 0xd7e105bcc332621f}, + {0xc86ab5c39fa63440, 0x8dd9472bf3fefaa7}, + {0xfa856334878fc150, 0xb14f98f6f0feb951}, + {0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d3}, + {0xc3b8358109e84f07, 0x0a862f80ec4700c8}, + {0xf4a642e14c6262c8, 0xcd27bb612758c0fa}, + {0x98e7e9cccfbd7dbd, 0x8038d51cb897789c}, + {0xbf21e44003acdd2c, 0xe0470a63e6bd56c3}, + {0xeeea5d5004981478, 0x1858ccfce06cac74}, + {0x95527a5202df0ccb, 0x0f37801e0c43ebc8}, + {0xbaa718e68396cffd, 0xd30560258f54e6ba}, + {0xe950df20247c83fd, 0x47c6b82ef32a2069}, + {0x91d28b7416cdd27e, 0x4cdc331d57fa5441}, + {0xb6472e511c81471d, 0xe0133fe4adf8e952}, + {0xe3d8f9e563a198e5, 0x58180fddd97723a6}, + {0x8e679c2f5e44ff8f, 0x570f09eaa7ea7648}, + {0xb201833b35d63f73, 0x2cd2cc6551e513da}, + {0xde81e40a034bcf4f, 0xf8077f7ea65e58d1}, + {0x8b112e86420f6191, 0xfb04afaf27faf782}, + {0xadd57a27d29339f6, 0x79c5db9af1f9b563}, + {0xd94ad8b1c7380874, 0x18375281ae7822bc}, + {0x87cec76f1c830548, 0x8f2293910d0b15b5}, + {0xa9c2794ae3a3c69a, 0xb2eb3875504ddb22}, + {0xd433179d9c8cb841, 0x5fa60692a46151eb}, + {0x849feec281d7f328, 0xdbc7c41ba6bcd333}, + {0xa5c7ea73224deff3, 0x12b9b522906c0800}, + {0xcf39e50feae16bef, 0xd768226b34870a00}, + {0x81842f29f2cce375, 0xe6a1158300d46640}, + {0xa1e53af46f801c53, 0x60495ae3c1097fd0}, + {0xca5e89b18b602368, 0x385bb19cb14bdfc4}, + {0xfcf62c1dee382c42, 0x46729e03dd9ed7b5}, + {0x9e19db92b4e31ba9, 0x6c07a2c26a8346d1}, + {0xc5a05277621be293, 0xc7098b7305241885}, + {0xf70867153aa2db38, 0xb8cbee4fc66d1ea7} +#else + {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, + {0xce5d73ff402d98e3, 0xfb0a3d212dc81290}, + {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f}, + {0x86a8d39ef77164bc, 0xae5dff9c02033198}, + {0xd98ddaee19068c76, 0x3badd624dd9b0958}, + {0xafbd2350644eeacf, 0xe5d1929ef90898fb}, + {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2}, + {0xe55990879ddcaabd, 0xcc420a6a101d0516}, + {0xb94470938fa89bce, 0xf808e40e8d5b3e6a}, + {0x95a8637627989aad, 0xdde7001379a44aa9}, + {0xf1c90080baf72cb1, 0x5324c68b12dd6339}, + {0xc350000000000000, 0x0000000000000000}, + {0x9dc5ada82b70b59d, 0xf020000000000000}, + {0xfee50b7025c36a08, 0x02f236d04753d5b4}, + {0xcde6fd5e09abcf26, 0xed4c0226b55e6f86}, + {0xa6539930bf6bff45, 0x84db8346b786151c}, + {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b2}, + {0xd910f7ff28069da4, 0x1b2ba1518094da04}, + {0xaf58416654a6babb, 0x387ac8d1970027b2}, + {0x8da471a9de737e24, 0x5ceaecfed289e5d2}, + {0xe4d5e82392a40515, 0x0fabaf3feaa5334a}, + {0xb8da1662e7b00a17, 0x3d6a751f3b936243}, + {0x95527a5202df0ccb, 0x0f37801e0c43ebc8} +#endif +}; + +#if !FMT_USE_FULL_CACHE_DRAGONBOX +template +const uint64_t basic_data::powers_of_5_64[] = { + 0x0000000000000001, 0x0000000000000005, 0x0000000000000019, + 0x000000000000007d, 0x0000000000000271, 0x0000000000000c35, + 0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1, + 0x00000000001dcd65, 0x00000000009502f9, 0x0000000002e90edd, + 0x000000000e8d4a51, 0x0000000048c27395, 0x000000016bcc41e9, + 0x000000071afd498d, 0x0000002386f26fc1, 0x000000b1a2bc2ec5, + 0x000003782dace9d9, 0x00001158e460913d, 0x000056bc75e2d631, + 0x0001b1ae4d6e2ef5, 0x000878678326eac9, 0x002a5a058fc295ed, + 0x00d3c21bcecceda1, 0x0422ca8b0a00a425, 0x14adf4b7320334b9}; + +template +const uint32_t basic_data::dragonbox_pow10_recovery_errors[] = { + 0x50001400, 0x54044100, 0x54014555, 0x55954415, 0x54115555, 0x00000001, + 0x50000000, 0x00104000, 0x54010004, 0x05004001, 0x55555544, 0x41545555, + 0x54040551, 0x15445545, 0x51555514, 0x10000015, 0x00101100, 0x01100015, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x04450514, 0x45414110, + 0x55555145, 0x50544050, 0x15040155, 0x11054140, 0x50111514, 0x11451454, + 0x00400541, 0x00000000, 0x55555450, 0x10056551, 0x10054011, 0x55551014, + 0x69514555, 0x05151109, 0x00155555}; +#endif + +template +const char basic_data::foreground_color[] = "\x1b[38;2;"; +template +const char basic_data::background_color[] = "\x1b[48;2;"; +template const char basic_data::reset_color[] = "\x1b[0m"; +template const wchar_t basic_data::wreset_color[] = L"\x1b[0m"; +template const char basic_data::signs[] = {0, '-', '+', ' '}; +template +const char basic_data::left_padding_shifts[] = {31, 31, 0, 1, 0}; +template +const char basic_data::right_padding_shifts[] = {0, 31, 0, 1, 0}; + +template struct bits { + static FMT_CONSTEXPR_DECL const int value = + static_cast(sizeof(T) * std::numeric_limits::digits); +}; + +class fp; +template fp normalize(fp value); + +// Lower (upper) boundary is a value half way between a floating-point value +// and its predecessor (successor). Boundaries have the same exponent as the +// value so only significands are stored. +struct boundaries { + uint64_t lower; + uint64_t upper; +}; + +// A handmade floating-point number f * pow(2, e). +class fp { + private: + using significand_type = uint64_t; + + template + using is_supported_float = bool_constant; + + public: + significand_type f; + int e; + + // All sizes are in bits. + // Subtract 1 to account for an implicit most significant bit in the + // normalized form. + static FMT_CONSTEXPR_DECL const int double_significand_size = + std::numeric_limits::digits - 1; + static FMT_CONSTEXPR_DECL const uint64_t implicit_bit = + 1ULL << double_significand_size; + static FMT_CONSTEXPR_DECL const int significand_size = + bits::value; + + fp() : f(0), e(0) {} + fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {} + + // Constructs fp from an IEEE754 double. It is a template to prevent compile + // errors on platforms where double is not IEEE754. + template explicit fp(Double d) { assign(d); } + + // Assigns d to this and return true iff predecessor is closer than successor. + template ::value)> + bool assign(Float d) { + // Assume float is in the format [sign][exponent][significand]. + using limits = std::numeric_limits; + const int float_significand_size = limits::digits - 1; + const int exponent_size = + bits::value - float_significand_size - 1; // -1 for sign + const uint64_t float_implicit_bit = 1ULL << float_significand_size; + const uint64_t significand_mask = float_implicit_bit - 1; + const uint64_t exponent_mask = (~0ULL >> 1) & ~significand_mask; + const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1; + constexpr bool is_double = sizeof(Float) == sizeof(uint64_t); + auto u = bit_cast>(d); + f = u & significand_mask; + int biased_e = + static_cast((u & exponent_mask) >> float_significand_size); + // Predecessor is closer if d is a normalized power of 2 (f == 0) other than + // the smallest normalized number (biased_e > 1). + bool is_predecessor_closer = f == 0 && biased_e > 1; + if (biased_e != 0) + f += float_implicit_bit; + else + biased_e = 1; // Subnormals use biased exponent 1 (min exponent). + e = biased_e - exponent_bias - float_significand_size; + return is_predecessor_closer; + } + + template ::value)> + bool assign(Float) { + *this = fp(); + return false; + } +}; + +// Normalizes the value converted from double and multiplied by (1 << SHIFT). +template fp normalize(fp value) { + // Handle subnormals. + const auto shifted_implicit_bit = fp::implicit_bit << SHIFT; + while ((value.f & shifted_implicit_bit) == 0) { + value.f <<= 1; + --value.e; + } + // Subtract 1 to account for hidden bit. + const auto offset = + fp::significand_size - fp::double_significand_size - SHIFT - 1; + value.f <<= offset; + value.e -= offset; + return value; +} + +inline bool operator==(fp x, fp y) { return x.f == y.f && x.e == y.e; } + +// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking. +inline uint64_t multiply(uint64_t lhs, uint64_t rhs) { +#if FMT_USE_INT128 + auto product = static_cast<__uint128_t>(lhs) * rhs; + auto f = static_cast(product >> 64); + return (static_cast(product) & (1ULL << 63)) != 0 ? f + 1 : f; +#else + // Multiply 32-bit parts of significands. + uint64_t mask = (1ULL << 32) - 1; + uint64_t a = lhs >> 32, b = lhs & mask; + uint64_t c = rhs >> 32, d = rhs & mask; + uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d; + // Compute mid 64-bit of result and round. + uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31); + return ac + (ad >> 32) + (bc >> 32) + (mid >> 32); +#endif +} + +inline fp operator*(fp x, fp y) { return {multiply(x.f, y.f), x.e + y.e + 64}; } + +// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its +// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`. +inline fp get_cached_power(int min_exponent, int& pow10_exponent) { + const int shift = 32; + const auto significand = static_cast(data::log10_2_significand); + int index = static_cast( + ((min_exponent + fp::significand_size - 1) * (significand >> shift) + + ((int64_t(1) << shift) - 1)) // ceil + >> 32 // arithmetic shift + ); + // Decimal exponent of the first (smallest) cached power of 10. + const int first_dec_exp = -348; + // Difference between 2 consecutive decimal exponents in cached powers of 10. + const int dec_exp_step = 8; + index = (index - first_dec_exp - 1) / dec_exp_step + 1; + pow10_exponent = first_dec_exp + index * dec_exp_step; + return {data::grisu_pow10_significands[index], + data::grisu_pow10_exponents[index]}; +} + +// A simple accumulator to hold the sums of terms in bigint::square if uint128_t +// is not available. +struct accumulator { + uint64_t lower; + uint64_t upper; + + accumulator() : lower(0), upper(0) {} + explicit operator uint32_t() const { return static_cast(lower); } + + void operator+=(uint64_t n) { + lower += n; + if (lower < n) ++upper; + } + void operator>>=(int shift) { + assert(shift == 32); + (void)shift; + lower = (upper << 32) | (lower >> 32); + upper >>= 32; + } +}; + +class bigint { + private: + // A bigint is stored as an array of bigits (big digits), with bigit at index + // 0 being the least significant one. + using bigit = uint32_t; + using double_bigit = uint64_t; + enum { bigits_capacity = 32 }; + basic_memory_buffer bigits_; + int exp_; + + bigit operator[](int index) const { return bigits_[to_unsigned(index)]; } + bigit& operator[](int index) { return bigits_[to_unsigned(index)]; } + + static FMT_CONSTEXPR_DECL const int bigit_bits = bits::value; + + friend struct formatter; + + void subtract_bigits(int index, bigit other, bigit& borrow) { + auto result = static_cast((*this)[index]) - other - borrow; + (*this)[index] = static_cast(result); + borrow = static_cast(result >> (bigit_bits * 2 - 1)); + } + + void remove_leading_zeros() { + int num_bigits = static_cast(bigits_.size()) - 1; + while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits; + bigits_.resize(to_unsigned(num_bigits + 1)); + } + + // Computes *this -= other assuming aligned bigints and *this >= other. + void subtract_aligned(const bigint& other) { + FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints"); + FMT_ASSERT(compare(*this, other) >= 0, ""); + bigit borrow = 0; + int i = other.exp_ - exp_; + for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j) + subtract_bigits(i, other.bigits_[j], borrow); + while (borrow > 0) subtract_bigits(i, 0, borrow); + remove_leading_zeros(); + } + + void multiply(uint32_t value) { + const double_bigit wide_value = value; + bigit carry = 0; + for (size_t i = 0, n = bigits_.size(); i < n; ++i) { + double_bigit result = bigits_[i] * wide_value + carry; + bigits_[i] = static_cast(result); + carry = static_cast(result >> bigit_bits); + } + if (carry != 0) bigits_.push_back(carry); + } + + void multiply(uint64_t value) { + const bigit mask = ~bigit(0); + const double_bigit lower = value & mask; + const double_bigit upper = value >> bigit_bits; + double_bigit carry = 0; + for (size_t i = 0, n = bigits_.size(); i < n; ++i) { + double_bigit result = bigits_[i] * lower + (carry & mask); + carry = + bigits_[i] * upper + (result >> bigit_bits) + (carry >> bigit_bits); + bigits_[i] = static_cast(result); + } + while (carry != 0) { + bigits_.push_back(carry & mask); + carry >>= bigit_bits; + } + } + + public: + bigint() : exp_(0) {} + explicit bigint(uint64_t n) { assign(n); } + ~bigint() { assert(bigits_.capacity() <= bigits_capacity); } + + bigint(const bigint&) = delete; + void operator=(const bigint&) = delete; + + void assign(const bigint& other) { + auto size = other.bigits_.size(); + bigits_.resize(size); + auto data = other.bigits_.data(); + std::copy(data, data + size, make_checked(bigits_.data(), size)); + exp_ = other.exp_; + } + + void assign(uint64_t n) { + size_t num_bigits = 0; + do { + bigits_[num_bigits++] = n & ~bigit(0); + n >>= bigit_bits; + } while (n != 0); + bigits_.resize(num_bigits); + exp_ = 0; + } + + int num_bigits() const { return static_cast(bigits_.size()) + exp_; } + + FMT_NOINLINE bigint& operator<<=(int shift) { + assert(shift >= 0); + exp_ += shift / bigit_bits; + shift %= bigit_bits; + if (shift == 0) return *this; + bigit carry = 0; + for (size_t i = 0, n = bigits_.size(); i < n; ++i) { + bigit c = bigits_[i] >> (bigit_bits - shift); + bigits_[i] = (bigits_[i] << shift) + carry; + carry = c; + } + if (carry != 0) bigits_.push_back(carry); + return *this; + } + + template bigint& operator*=(Int value) { + FMT_ASSERT(value > 0, ""); + multiply(uint32_or_64_or_128_t(value)); + return *this; + } + + friend int compare(const bigint& lhs, const bigint& rhs) { + int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits(); + if (num_lhs_bigits != num_rhs_bigits) + return num_lhs_bigits > num_rhs_bigits ? 1 : -1; + int i = static_cast(lhs.bigits_.size()) - 1; + int j = static_cast(rhs.bigits_.size()) - 1; + int end = i - j; + if (end < 0) end = 0; + for (; i >= end; --i, --j) { + bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j]; + if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1; + } + if (i != j) return i > j ? 1 : -1; + return 0; + } + + // Returns compare(lhs1 + lhs2, rhs). + friend int add_compare(const bigint& lhs1, const bigint& lhs2, + const bigint& rhs) { + int max_lhs_bigits = (std::max)(lhs1.num_bigits(), lhs2.num_bigits()); + int num_rhs_bigits = rhs.num_bigits(); + if (max_lhs_bigits + 1 < num_rhs_bigits) return -1; + if (max_lhs_bigits > num_rhs_bigits) return 1; + auto get_bigit = [](const bigint& n, int i) -> bigit { + return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0; + }; + double_bigit borrow = 0; + int min_exp = (std::min)((std::min)(lhs1.exp_, lhs2.exp_), rhs.exp_); + for (int i = num_rhs_bigits - 1; i >= min_exp; --i) { + double_bigit sum = + static_cast(get_bigit(lhs1, i)) + get_bigit(lhs2, i); + bigit rhs_bigit = get_bigit(rhs, i); + if (sum > rhs_bigit + borrow) return 1; + borrow = rhs_bigit + borrow - sum; + if (borrow > 1) return -1; + borrow <<= bigit_bits; + } + return borrow != 0 ? -1 : 0; + } + + // Assigns pow(10, exp) to this bigint. + void assign_pow10(int exp) { + assert(exp >= 0); + if (exp == 0) return assign(1); + // Find the top bit. + int bitmask = 1; + while (exp >= bitmask) bitmask <<= 1; + bitmask >>= 1; + // pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by + // repeated squaring and multiplication. + assign(5); + bitmask >>= 1; + while (bitmask != 0) { + square(); + if ((exp & bitmask) != 0) *this *= 5; + bitmask >>= 1; + } + *this <<= exp; // Multiply by pow(2, exp) by shifting. + } + + void square() { + basic_memory_buffer n(std::move(bigits_)); + int num_bigits = static_cast(bigits_.size()); + int num_result_bigits = 2 * num_bigits; + bigits_.resize(to_unsigned(num_result_bigits)); + using accumulator_t = conditional_t; + auto sum = accumulator_t(); + for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) { + // Compute bigit at position bigit_index of the result by adding + // cross-product terms n[i] * n[j] such that i + j == bigit_index. + for (int i = 0, j = bigit_index; j >= 0; ++i, --j) { + // Most terms are multiplied twice which can be optimized in the future. + sum += static_cast(n[i]) * n[j]; + } + (*this)[bigit_index] = static_cast(sum); + sum >>= bits::value; // Compute the carry. + } + // Do the same for the top half. + for (int bigit_index = num_bigits; bigit_index < num_result_bigits; + ++bigit_index) { + for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;) + sum += static_cast(n[i++]) * n[j--]; + (*this)[bigit_index] = static_cast(sum); + sum >>= bits::value; + } + --num_result_bigits; + remove_leading_zeros(); + exp_ *= 2; + } + + // If this bigint has a bigger exponent than other, adds trailing zero to make + // exponents equal. This simplifies some operations such as subtraction. + void align(const bigint& other) { + int exp_difference = exp_ - other.exp_; + if (exp_difference <= 0) return; + int num_bigits = static_cast(bigits_.size()); + bigits_.resize(to_unsigned(num_bigits + exp_difference)); + for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j) + bigits_[j] = bigits_[i]; + std::uninitialized_fill_n(bigits_.data(), exp_difference, 0); + exp_ -= exp_difference; + } + + // Divides this bignum by divisor, assigning the remainder to this and + // returning the quotient. + int divmod_assign(const bigint& divisor) { + FMT_ASSERT(this != &divisor, ""); + if (compare(*this, divisor) < 0) return 0; + FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, ""); + align(divisor); + int quotient = 0; + do { + subtract_aligned(divisor); + ++quotient; + } while (compare(*this, divisor) >= 0); + return quotient; + } +}; + +enum class round_direction { unknown, up, down }; + +// Given the divisor (normally a power of 10), the remainder = v % divisor for +// some number v and the error, returns whether v should be rounded up, down, or +// whether the rounding direction can't be determined due to error. +// error should be less than divisor / 2. +inline round_direction get_round_direction(uint64_t divisor, uint64_t remainder, + uint64_t error) { + FMT_ASSERT(remainder < divisor, ""); // divisor - remainder won't overflow. + FMT_ASSERT(error < divisor, ""); // divisor - error won't overflow. + FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow. + // Round down if (remainder + error) * 2 <= divisor. + if (remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2) + return round_direction::down; + // Round up if (remainder - error) * 2 >= divisor. + if (remainder >= error && + remainder - error >= divisor - (remainder - error)) { + return round_direction::up; + } + return round_direction::unknown; +} + +namespace digits { +enum result { + more, // Generate more digits. + done, // Done generating digits. + error // Digit generation cancelled due to an error. +}; +} + +// Generates output using the Grisu digit-gen algorithm. +// error: the size of the region (lower, upper) outside of which numbers +// definitely do not round to value (Delta in Grisu3). +template +FMT_ALWAYS_INLINE digits::result grisu_gen_digits(fp value, uint64_t error, + int& exp, Handler& handler) { + const fp one(1ULL << -value.e, value.e); + // The integral part of scaled value (p1 in Grisu) = value / one. It cannot be + // zero because it contains a product of two 64-bit numbers with MSB set (due + // to normalization) - 1, shifted right by at most 60 bits. + auto integral = static_cast(value.f >> -one.e); + FMT_ASSERT(integral != 0, ""); + FMT_ASSERT(integral == value.f >> -one.e, ""); + // The fractional part of scaled value (p2 in Grisu) c = value % one. + uint64_t fractional = value.f & (one.f - 1); + exp = count_digits(integral); // kappa in Grisu. + // Divide by 10 to prevent overflow. + auto result = handler.on_start(data::powers_of_10_64[exp - 1] << -one.e, + value.f / 10, error * 10, exp); + if (result != digits::more) return result; + // Generate digits for the integral part. This can produce up to 10 digits. + do { + uint32_t digit = 0; + auto divmod_integral = [&](uint32_t divisor) { + digit = integral / divisor; + integral %= divisor; + }; + // This optimization by Milo Yip reduces the number of integer divisions by + // one per iteration. + switch (exp) { + case 10: + divmod_integral(1000000000); + break; + case 9: + divmod_integral(100000000); + break; + case 8: + divmod_integral(10000000); + break; + case 7: + divmod_integral(1000000); + break; + case 6: + divmod_integral(100000); + break; + case 5: + divmod_integral(10000); + break; + case 4: + divmod_integral(1000); + break; + case 3: + divmod_integral(100); + break; + case 2: + divmod_integral(10); + break; + case 1: + digit = integral; + integral = 0; + break; + default: + FMT_ASSERT(false, "invalid number of digits"); + } + --exp; + auto remainder = (static_cast(integral) << -one.e) + fractional; + result = handler.on_digit(static_cast('0' + digit), + data::powers_of_10_64[exp] << -one.e, remainder, + error, exp, true); + if (result != digits::more) return result; + } while (exp > 0); + // Generate digits for the fractional part. + for (;;) { + fractional *= 10; + error *= 10; + char digit = static_cast('0' + (fractional >> -one.e)); + fractional &= one.f - 1; + --exp; + result = handler.on_digit(digit, one.f, fractional, error, exp, false); + if (result != digits::more) return result; + } +} + +// The fixed precision digit handler. +struct fixed_handler { + char* buf; + int size; + int precision; + int exp10; + bool fixed; + + digits::result on_start(uint64_t divisor, uint64_t remainder, uint64_t error, + int& exp) { + // Non-fixed formats require at least one digit and no precision adjustment. + if (!fixed) return digits::more; + // Adjust fixed precision by exponent because it is relative to decimal + // point. + precision += exp + exp10; + // Check if precision is satisfied just by leading zeros, e.g. + // format("{:.2f}", 0.001) gives "0.00" without generating any digits. + if (precision > 0) return digits::more; + if (precision < 0) return digits::done; + auto dir = get_round_direction(divisor, remainder, error); + if (dir == round_direction::unknown) return digits::error; + buf[size++] = dir == round_direction::up ? '1' : '0'; + return digits::done; + } + + digits::result on_digit(char digit, uint64_t divisor, uint64_t remainder, + uint64_t error, int, bool integral) { + FMT_ASSERT(remainder < divisor, ""); + buf[size++] = digit; + if (!integral && error >= remainder) return digits::error; + if (size < precision) return digits::more; + if (!integral) { + // Check if error * 2 < divisor with overflow prevention. + // The check is not needed for the integral part because error = 1 + // and divisor > (1 << 32) there. + if (error >= divisor || error >= divisor - error) return digits::error; + } else { + FMT_ASSERT(error == 1 && divisor > 2, ""); + } + auto dir = get_round_direction(divisor, remainder, error); + if (dir != round_direction::up) + return dir == round_direction::down ? digits::done : digits::error; + ++buf[size - 1]; + for (int i = size - 1; i > 0 && buf[i] > '9'; --i) { + buf[i] = '0'; + ++buf[i - 1]; + } + if (buf[0] > '9') { + buf[0] = '1'; + if (fixed) + buf[size++] = '0'; + else + ++exp10; + } + return digits::done; + } +}; + +// Implementation of Dragonbox algorithm: https://github.com/jk-jeon/dragonbox. +namespace dragonbox { +// Computes 128-bit result of multiplication of two 64-bit unsigned integers. +FMT_SAFEBUFFERS inline uint128_wrapper umul128(uint64_t x, + uint64_t y) FMT_NOEXCEPT { +#if FMT_USE_INT128 + return static_cast(x) * static_cast(y); +#elif defined(_MSC_VER) && defined(_M_X64) + uint128_wrapper result; + result.low_ = _umul128(x, y, &result.high_); + return result; +#else + const uint64_t mask = (uint64_t(1) << 32) - uint64_t(1); + + uint64_t a = x >> 32; + uint64_t b = x & mask; + uint64_t c = y >> 32; + uint64_t d = y & mask; + + uint64_t ac = a * c; + uint64_t bc = b * c; + uint64_t ad = a * d; + uint64_t bd = b * d; + + uint64_t intermediate = (bd >> 32) + (ad & mask) + (bc & mask); + + return {ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32), + (intermediate << 32) + (bd & mask)}; +#endif +} + +// Computes upper 64 bits of multiplication of two 64-bit unsigned integers. +FMT_SAFEBUFFERS inline uint64_t umul128_upper64(uint64_t x, + uint64_t y) FMT_NOEXCEPT { +#if FMT_USE_INT128 + auto p = static_cast(x) * static_cast(y); + return static_cast(p >> 64); +#elif defined(_MSC_VER) && defined(_M_X64) + return __umulh(x, y); +#else + return umul128(x, y).high(); +#endif +} + +// Computes upper 64 bits of multiplication of a 64-bit unsigned integer and a +// 128-bit unsigned integer. +FMT_SAFEBUFFERS inline uint64_t umul192_upper64(uint64_t x, uint128_wrapper y) + FMT_NOEXCEPT { + uint128_wrapper g0 = umul128(x, y.high()); + g0 += umul128_upper64(x, y.low()); + return g0.high(); +} + +// Computes upper 32 bits of multiplication of a 32-bit unsigned integer and a +// 64-bit unsigned integer. +inline uint32_t umul96_upper32(uint32_t x, uint64_t y) FMT_NOEXCEPT { + return static_cast(umul128_upper64(x, y)); +} + +// Computes middle 64 bits of multiplication of a 64-bit unsigned integer and a +// 128-bit unsigned integer. +FMT_SAFEBUFFERS inline uint64_t umul192_middle64(uint64_t x, uint128_wrapper y) + FMT_NOEXCEPT { + uint64_t g01 = x * y.high(); + uint64_t g10 = umul128_upper64(x, y.low()); + return g01 + g10; +} + +// Computes lower 64 bits of multiplication of a 32-bit unsigned integer and a +// 64-bit unsigned integer. +inline uint64_t umul96_lower64(uint32_t x, uint64_t y) FMT_NOEXCEPT { + return x * y; +} + +// Computes floor(log10(pow(2, e))) for e in [-1700, 1700] using the method from +// https://fmt.dev/papers/Grisu-Exact.pdf#page=5, section 3.4. +inline int floor_log10_pow2(int e) FMT_NOEXCEPT { + FMT_ASSERT(e <= 1700 && e >= -1700, "too large exponent"); + const int shift = 22; + return (e * static_cast(data::log10_2_significand >> (64 - shift))) >> + shift; +} + +// Various fast log computations. +inline int floor_log2_pow10(int e) FMT_NOEXCEPT { + FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent"); + const uint64_t log2_10_integer_part = 3; + const uint64_t log2_10_fractional_digits = 0x5269e12f346e2bf9; + const int shift_amount = 19; + return (e * static_cast( + (log2_10_integer_part << shift_amount) | + (log2_10_fractional_digits >> (64 - shift_amount)))) >> + shift_amount; +} +inline int floor_log10_pow2_minus_log10_4_over_3(int e) FMT_NOEXCEPT { + FMT_ASSERT(e <= 1700 && e >= -1700, "too large exponent"); + const uint64_t log10_4_over_3_fractional_digits = 0x1ffbfc2bbc780375; + const int shift_amount = 22; + return (e * static_cast(data::log10_2_significand >> + (64 - shift_amount)) - + static_cast(log10_4_over_3_fractional_digits >> + (64 - shift_amount))) >> + shift_amount; +} + +// Returns true iff x is divisible by pow(2, exp). +inline bool divisible_by_power_of_2(uint32_t x, int exp) FMT_NOEXCEPT { + FMT_ASSERT(exp >= 1, ""); + FMT_ASSERT(x != 0, ""); +#ifdef FMT_BUILTIN_CTZ + return FMT_BUILTIN_CTZ(x) >= exp; +#else + return exp < num_bits() && x == ((x >> exp) << exp); +#endif +} +inline bool divisible_by_power_of_2(uint64_t x, int exp) FMT_NOEXCEPT { + FMT_ASSERT(exp >= 1, ""); + FMT_ASSERT(x != 0, ""); +#ifdef FMT_BUILTIN_CTZLL + return FMT_BUILTIN_CTZLL(x) >= exp; +#else + return exp < num_bits() && x == ((x >> exp) << exp); +#endif +} + +// Returns true iff x is divisible by pow(5, exp). +inline bool divisible_by_power_of_5(uint32_t x, int exp) FMT_NOEXCEPT { + FMT_ASSERT(exp <= 10, "too large exponent"); + return x * data::divtest_table_for_pow5_32[exp].mod_inv <= + data::divtest_table_for_pow5_32[exp].max_quotient; +} +inline bool divisible_by_power_of_5(uint64_t x, int exp) FMT_NOEXCEPT { + FMT_ASSERT(exp <= 23, "too large exponent"); + return x * data::divtest_table_for_pow5_64[exp].mod_inv <= + data::divtest_table_for_pow5_64[exp].max_quotient; +} + +// Replaces n by floor(n / pow(5, N)) returning true if and only if n is +// divisible by pow(5, N). +// Precondition: n <= 2 * pow(5, N + 1). +template +bool check_divisibility_and_divide_by_pow5(uint32_t& n) FMT_NOEXCEPT { + static constexpr struct { + uint32_t magic_number; + int bits_for_comparison; + uint32_t threshold; + int shift_amount; + } infos[] = {{0xcccd, 16, 0x3333, 18}, {0xa429, 8, 0x0a, 20}}; + constexpr auto info = infos[N - 1]; + n *= info.magic_number; + const uint32_t comparison_mask = (1u << info.bits_for_comparison) - 1; + bool result = (n & comparison_mask) <= info.threshold; + n >>= info.shift_amount; + return result; +} + +// Computes floor(n / pow(10, N)) for small n and N. +// Precondition: n <= pow(10, N + 1). +template uint32_t small_division_by_pow10(uint32_t n) FMT_NOEXCEPT { + static constexpr struct { + uint32_t magic_number; + int shift_amount; + uint32_t divisor_times_10; + } infos[] = {{0xcccd, 19, 100}, {0xa3d8, 22, 1000}}; + constexpr auto info = infos[N - 1]; + FMT_ASSERT(n <= info.divisor_times_10, "n is too large"); + return n * info.magic_number >> info.shift_amount; +} + +// Computes floor(n / 10^(kappa + 1)) (float) +inline uint32_t divide_by_10_to_kappa_plus_1(uint32_t n) FMT_NOEXCEPT { + return n / float_info::big_divisor; +} +// Computes floor(n / 10^(kappa + 1)) (double) +inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) FMT_NOEXCEPT { + return umul128_upper64(n, 0x83126e978d4fdf3c) >> 9; +} + +// Various subroutines using pow10 cache +template struct cache_accessor; + +template <> struct cache_accessor { + using carrier_uint = float_info::carrier_uint; + using cache_entry_type = uint64_t; + + static uint64_t get_cached_power(int k) FMT_NOEXCEPT { + FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, + "k is out of range"); + return data::dragonbox_pow10_significands_64[k - float_info::min_k]; + } + + static carrier_uint compute_mul(carrier_uint u, + const cache_entry_type& cache) FMT_NOEXCEPT { + return umul96_upper32(u, cache); + } + + static uint32_t compute_delta(const cache_entry_type& cache, + int beta_minus_1) FMT_NOEXCEPT { + return static_cast(cache >> (64 - 1 - beta_minus_1)); + } + + static bool compute_mul_parity(carrier_uint two_f, + const cache_entry_type& cache, + int beta_minus_1) FMT_NOEXCEPT { + FMT_ASSERT(beta_minus_1 >= 1, ""); + FMT_ASSERT(beta_minus_1 < 64, ""); + + return ((umul96_lower64(two_f, cache) >> (64 - beta_minus_1)) & 1) != 0; + } + + static carrier_uint compute_left_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { + return static_cast( + (cache - (cache >> (float_info::significand_bits + 2))) >> + (64 - float_info::significand_bits - 1 - beta_minus_1)); + } + + static carrier_uint compute_right_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { + return static_cast( + (cache + (cache >> (float_info::significand_bits + 1))) >> + (64 - float_info::significand_bits - 1 - beta_minus_1)); + } + + static carrier_uint compute_round_up_for_shorter_interval_case( + const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { + return (static_cast( + cache >> + (64 - float_info::significand_bits - 2 - beta_minus_1)) + + 1) / + 2; + } +}; + +template <> struct cache_accessor { + using carrier_uint = float_info::carrier_uint; + using cache_entry_type = uint128_wrapper; + + static uint128_wrapper get_cached_power(int k) FMT_NOEXCEPT { + FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, + "k is out of range"); + +#if FMT_USE_FULL_CACHE_DRAGONBOX + return data::dragonbox_pow10_significands_128[k - + float_info::min_k]; +#else + static const int compression_ratio = 27; + + // Compute base index. + int cache_index = (k - float_info::min_k) / compression_ratio; + int kb = cache_index * compression_ratio + float_info::min_k; + int offset = k - kb; + + // Get base cache. + uint128_wrapper base_cache = + data::dragonbox_pow10_significands_128[cache_index]; + if (offset == 0) return base_cache; + + // Compute the required amount of bit-shift. + int alpha = floor_log2_pow10(kb + offset) - floor_log2_pow10(kb) - offset; + FMT_ASSERT(alpha > 0 && alpha < 64, "shifting error detected"); + + // Try to recover the real cache. + uint64_t pow5 = data::powers_of_5_64[offset]; + uint128_wrapper recovered_cache = umul128(base_cache.high(), pow5); + uint128_wrapper middle_low = + umul128(base_cache.low() - (kb < 0 ? 1u : 0u), pow5); + + recovered_cache += middle_low.high(); + + uint64_t high_to_middle = recovered_cache.high() << (64 - alpha); + uint64_t middle_to_low = recovered_cache.low() << (64 - alpha); + + recovered_cache = + uint128_wrapper{(recovered_cache.low() >> alpha) | high_to_middle, + ((middle_low.low() >> alpha) | middle_to_low)}; + + if (kb < 0) recovered_cache += 1; + + // Get error. + int error_idx = (k - float_info::min_k) / 16; + uint32_t error = (data::dragonbox_pow10_recovery_errors[error_idx] >> + ((k - float_info::min_k) % 16) * 2) & + 0x3; + + // Add the error back. + FMT_ASSERT(recovered_cache.low() + error >= recovered_cache.low(), ""); + return {recovered_cache.high(), recovered_cache.low() + error}; +#endif + } + + static carrier_uint compute_mul(carrier_uint u, + const cache_entry_type& cache) FMT_NOEXCEPT { + return umul192_upper64(u, cache); + } + + static uint32_t compute_delta(cache_entry_type const& cache, + int beta_minus_1) FMT_NOEXCEPT { + return static_cast(cache.high() >> (64 - 1 - beta_minus_1)); + } + + static bool compute_mul_parity(carrier_uint two_f, + const cache_entry_type& cache, + int beta_minus_1) FMT_NOEXCEPT { + FMT_ASSERT(beta_minus_1 >= 1, ""); + FMT_ASSERT(beta_minus_1 < 64, ""); + + return ((umul192_middle64(two_f, cache) >> (64 - beta_minus_1)) & 1) != 0; + } + + static carrier_uint compute_left_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { + return (cache.high() - + (cache.high() >> (float_info::significand_bits + 2))) >> + (64 - float_info::significand_bits - 1 - beta_minus_1); + } + + static carrier_uint compute_right_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { + return (cache.high() + + (cache.high() >> (float_info::significand_bits + 1))) >> + (64 - float_info::significand_bits - 1 - beta_minus_1); + } + + static carrier_uint compute_round_up_for_shorter_interval_case( + const cache_entry_type& cache, int beta_minus_1) FMT_NOEXCEPT { + return ((cache.high() >> + (64 - float_info::significand_bits - 2 - beta_minus_1)) + + 1) / + 2; + } +}; + +// Various integer checks +template +bool is_left_endpoint_integer_shorter_interval(int exponent) FMT_NOEXCEPT { + return exponent >= + float_info< + T>::case_shorter_interval_left_endpoint_lower_threshold && + exponent <= + float_info::case_shorter_interval_left_endpoint_upper_threshold; +} +template +bool is_endpoint_integer(typename float_info::carrier_uint two_f, + int exponent, int minus_k) FMT_NOEXCEPT { + if (exponent < float_info::case_fc_pm_half_lower_threshold) return false; + // For k >= 0. + if (exponent <= float_info::case_fc_pm_half_upper_threshold) return true; + // For k < 0. + if (exponent > float_info::divisibility_check_by_5_threshold) return false; + return divisible_by_power_of_5(two_f, minus_k); +} + +template +bool is_center_integer(typename float_info::carrier_uint two_f, int exponent, + int minus_k) FMT_NOEXCEPT { + // Exponent for 5 is negative. + if (exponent > float_info::divisibility_check_by_5_threshold) return false; + if (exponent > float_info::case_fc_upper_threshold) + return divisible_by_power_of_5(two_f, minus_k); + // Both exponents are nonnegative. + if (exponent >= float_info::case_fc_lower_threshold) return true; + // Exponent for 2 is negative. + return divisible_by_power_of_2(two_f, minus_k - exponent + 1); +} + +// Remove trailing zeros from n and return the number of zeros removed (float) +FMT_ALWAYS_INLINE int remove_trailing_zeros(uint32_t& n) FMT_NOEXCEPT { +#ifdef FMT_BUILTIN_CTZ + int t = FMT_BUILTIN_CTZ(n); +#else + int t = ctz(n); +#endif + if (t > float_info::max_trailing_zeros) + t = float_info::max_trailing_zeros; + + const uint32_t mod_inv1 = 0xcccccccd; + const uint32_t max_quotient1 = 0x33333333; + const uint32_t mod_inv2 = 0xc28f5c29; + const uint32_t max_quotient2 = 0x0a3d70a3; + + int s = 0; + for (; s < t - 1; s += 2) { + if (n * mod_inv2 > max_quotient2) break; + n *= mod_inv2; + } + if (s < t && n * mod_inv1 <= max_quotient1) { + n *= mod_inv1; + ++s; + } + n >>= s; + return s; +} + +// Removes trailing zeros and returns the number of zeros removed (double) +FMT_ALWAYS_INLINE int remove_trailing_zeros(uint64_t& n) FMT_NOEXCEPT { +#ifdef FMT_BUILTIN_CTZLL + int t = FMT_BUILTIN_CTZLL(n); +#else + int t = ctzll(n); +#endif + if (t > float_info::max_trailing_zeros) + t = float_info::max_trailing_zeros; + // Divide by 10^8 and reduce to 32-bits + // Since ret_value.significand <= (2^64 - 1) / 1000 < 10^17, + // both of the quotient and the r should fit in 32-bits + + const uint32_t mod_inv1 = 0xcccccccd; + const uint32_t max_quotient1 = 0x33333333; + const uint64_t mod_inv8 = 0xc767074b22e90e21; + const uint64_t max_quotient8 = 0x00002af31dc46118; + + // If the number is divisible by 1'0000'0000, work with the quotient + if (t >= 8) { + auto quotient_candidate = n * mod_inv8; + + if (quotient_candidate <= max_quotient8) { + auto quotient = static_cast(quotient_candidate >> 8); + + int s = 8; + for (; s < t; ++s) { + if (quotient * mod_inv1 > max_quotient1) break; + quotient *= mod_inv1; + } + quotient >>= (s - 8); + n = quotient; + return s; + } + } + + // Otherwise, work with the remainder + auto quotient = static_cast(n / 100000000); + auto remainder = static_cast(n - 100000000 * quotient); + + if (t == 0 || remainder * mod_inv1 > max_quotient1) { + return 0; + } + remainder *= mod_inv1; + + if (t == 1 || remainder * mod_inv1 > max_quotient1) { + n = (remainder >> 1) + quotient * 10000000ull; + return 1; + } + remainder *= mod_inv1; + + if (t == 2 || remainder * mod_inv1 > max_quotient1) { + n = (remainder >> 2) + quotient * 1000000ull; + return 2; + } + remainder *= mod_inv1; + + if (t == 3 || remainder * mod_inv1 > max_quotient1) { + n = (remainder >> 3) + quotient * 100000ull; + return 3; + } + remainder *= mod_inv1; + + if (t == 4 || remainder * mod_inv1 > max_quotient1) { + n = (remainder >> 4) + quotient * 10000ull; + return 4; + } + remainder *= mod_inv1; + + if (t == 5 || remainder * mod_inv1 > max_quotient1) { + n = (remainder >> 5) + quotient * 1000ull; + return 5; + } + remainder *= mod_inv1; + + if (t == 6 || remainder * mod_inv1 > max_quotient1) { + n = (remainder >> 6) + quotient * 100ull; + return 6; + } + remainder *= mod_inv1; + + n = (remainder >> 7) + quotient * 10ull; + return 7; +} + +// The main algorithm for shorter interval case +template +FMT_ALWAYS_INLINE FMT_SAFEBUFFERS decimal_fp shorter_interval_case( + int exponent) FMT_NOEXCEPT { + decimal_fp ret_value; + // Compute k and beta + const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent); + const int beta_minus_1 = exponent + floor_log2_pow10(-minus_k); + + // Compute xi and zi + using cache_entry_type = typename cache_accessor::cache_entry_type; + const cache_entry_type cache = cache_accessor::get_cached_power(-minus_k); + + auto xi = cache_accessor::compute_left_endpoint_for_shorter_interval_case( + cache, beta_minus_1); + auto zi = cache_accessor::compute_right_endpoint_for_shorter_interval_case( + cache, beta_minus_1); + + // If the left endpoint is not an integer, increase it + if (!is_left_endpoint_integer_shorter_interval(exponent)) ++xi; + + // Try bigger divisor + ret_value.significand = zi / 10; + + // If succeed, remove trailing zeros if necessary and return + if (ret_value.significand * 10 >= xi) { + ret_value.exponent = minus_k + 1; + ret_value.exponent += remove_trailing_zeros(ret_value.significand); + return ret_value; + } + + // Otherwise, compute the round-up of y + ret_value.significand = + cache_accessor::compute_round_up_for_shorter_interval_case( + cache, beta_minus_1); + ret_value.exponent = minus_k; + + // When tie occurs, choose one of them according to the rule + if (exponent >= float_info::shorter_interval_tie_lower_threshold && + exponent <= float_info::shorter_interval_tie_upper_threshold) { + ret_value.significand = ret_value.significand % 2 == 0 + ? ret_value.significand + : ret_value.significand - 1; + } else if (ret_value.significand < xi) { + ++ret_value.significand; + } + return ret_value; +} + +template +FMT_SAFEBUFFERS decimal_fp to_decimal(T x) FMT_NOEXCEPT { + // Step 1: integer promotion & Schubfach multiplier calculation. + + using carrier_uint = typename float_info::carrier_uint; + using cache_entry_type = typename cache_accessor::cache_entry_type; + auto br = bit_cast(x); + + // Extract significand bits and exponent bits. + const carrier_uint significand_mask = + (static_cast(1) << float_info::significand_bits) - 1; + carrier_uint significand = (br & significand_mask); + int exponent = static_cast((br & exponent_mask()) >> + float_info::significand_bits); + + if (exponent != 0) { // Check if normal. + exponent += float_info::exponent_bias - float_info::significand_bits; + + // Shorter interval case; proceed like Schubfach. + if (significand == 0) return shorter_interval_case(exponent); + + significand |= + (static_cast(1) << float_info::significand_bits); + } else { + // Subnormal case; the interval is always regular. + if (significand == 0) return {0, 0}; + exponent = float_info::min_exponent - float_info::significand_bits; + } + + const bool include_left_endpoint = (significand % 2 == 0); + const bool include_right_endpoint = include_left_endpoint; + + // Compute k and beta. + const int minus_k = floor_log10_pow2(exponent) - float_info::kappa; + const cache_entry_type cache = cache_accessor::get_cached_power(-minus_k); + const int beta_minus_1 = exponent + floor_log2_pow10(-minus_k); + + // Compute zi and deltai + // 10^kappa <= deltai < 10^(kappa + 1) + const uint32_t deltai = cache_accessor::compute_delta(cache, beta_minus_1); + const carrier_uint two_fc = significand << 1; + const carrier_uint two_fr = two_fc | 1; + const carrier_uint zi = + cache_accessor::compute_mul(two_fr << beta_minus_1, cache); + + // Step 2: Try larger divisor; remove trailing zeros if necessary + + // Using an upper bound on zi, we might be able to optimize the division + // better than the compiler; we are computing zi / big_divisor here + decimal_fp ret_value; + ret_value.significand = divide_by_10_to_kappa_plus_1(zi); + uint32_t r = static_cast(zi - float_info::big_divisor * + ret_value.significand); + + if (r > deltai) { + goto small_divisor_case_label; + } else if (r < deltai) { + // Exclude the right endpoint if necessary + if (r == 0 && !include_right_endpoint && + is_endpoint_integer(two_fr, exponent, minus_k)) { + --ret_value.significand; + r = float_info::big_divisor; + goto small_divisor_case_label; + } + } else { + // r == deltai; compare fractional parts + // Check conditions in the order different from the paper + // to take advantage of short-circuiting + const carrier_uint two_fl = two_fc - 1; + if ((!include_left_endpoint || + !is_endpoint_integer(two_fl, exponent, minus_k)) && + !cache_accessor::compute_mul_parity(two_fl, cache, beta_minus_1)) { + goto small_divisor_case_label; + } + } + ret_value.exponent = minus_k + float_info::kappa + 1; + + // We may need to remove trailing zeros + ret_value.exponent += remove_trailing_zeros(ret_value.significand); + return ret_value; + + // Step 3: Find the significand with the smaller divisor + +small_divisor_case_label: + ret_value.significand *= 10; + ret_value.exponent = minus_k + float_info::kappa; + + const uint32_t mask = (1u << float_info::kappa) - 1; + auto dist = r - (deltai / 2) + (float_info::small_divisor / 2); + + // Is dist divisible by 2^kappa? + if ((dist & mask) == 0) { + const bool approx_y_parity = + ((dist ^ (float_info::small_divisor / 2)) & 1) != 0; + dist >>= float_info::kappa; + + // Is dist divisible by 5^kappa? + if (check_divisibility_and_divide_by_pow5::kappa>(dist)) { + ret_value.significand += dist; + + // Check z^(f) >= epsilon^(f) + // We have either yi == zi - epsiloni or yi == (zi - epsiloni) - 1, + // where yi == zi - epsiloni if and only if z^(f) >= epsilon^(f) + // Since there are only 2 possibilities, we only need to care about the + // parity. Also, zi and r should have the same parity since the divisor + // is an even number + if (cache_accessor::compute_mul_parity(two_fc, cache, beta_minus_1) != + approx_y_parity) { + --ret_value.significand; + } else { + // If z^(f) >= epsilon^(f), we might have a tie + // when z^(f) == epsilon^(f), or equivalently, when y is an integer + if (is_center_integer(two_fc, exponent, minus_k)) { + ret_value.significand = ret_value.significand % 2 == 0 + ? ret_value.significand + : ret_value.significand - 1; + } + } + } + // Is dist not divisible by 5^kappa? + else { + ret_value.significand += dist; + } + } + // Is dist not divisible by 2^kappa? + else { + // Since we know dist is small, we might be able to optimize the division + // better than the compiler; we are computing dist / small_divisor here + ret_value.significand += + small_division_by_pow10::kappa>(dist); + } + return ret_value; +} +} // namespace dragonbox + +// Formats value using a variation of the Fixed-Precision Positive +// Floating-Point Printout ((FPP)^2) algorithm by Steele & White: +// https://fmt.dev/p372-steele.pdf. +template +void fallback_format(Double d, int num_digits, bool binary32, buffer& buf, + int& exp10) { + bigint numerator; // 2 * R in (FPP)^2. + bigint denominator; // 2 * S in (FPP)^2. + // lower and upper are differences between value and corresponding boundaries. + bigint lower; // (M^- in (FPP)^2). + bigint upper_store; // upper's value if different from lower. + bigint* upper = nullptr; // (M^+ in (FPP)^2). + fp value; + // Shift numerator and denominator by an extra bit or two (if lower boundary + // is closer) to make lower and upper integers. This eliminates multiplication + // by 2 during later computations. + const bool is_predecessor_closer = + binary32 ? value.assign(static_cast(d)) : value.assign(d); + int shift = is_predecessor_closer ? 2 : 1; + uint64_t significand = value.f << shift; + if (value.e >= 0) { + numerator.assign(significand); + numerator <<= value.e; + lower.assign(1); + lower <<= value.e; + if (shift != 1) { + upper_store.assign(1); + upper_store <<= value.e + 1; + upper = &upper_store; + } + denominator.assign_pow10(exp10); + denominator <<= shift; + } else if (exp10 < 0) { + numerator.assign_pow10(-exp10); + lower.assign(numerator); + if (shift != 1) { + upper_store.assign(numerator); + upper_store <<= 1; + upper = &upper_store; + } + numerator *= significand; + denominator.assign(1); + denominator <<= shift - value.e; + } else { + numerator.assign(significand); + denominator.assign_pow10(exp10); + denominator <<= shift - value.e; + lower.assign(1); + if (shift != 1) { + upper_store.assign(1ULL << 1); + upper = &upper_store; + } + } + // Invariant: value == (numerator / denominator) * pow(10, exp10). + if (num_digits < 0) { + // Generate the shortest representation. + if (!upper) upper = &lower; + bool even = (value.f & 1) == 0; + num_digits = 0; + char* data = buf.data(); + for (;;) { + int digit = numerator.divmod_assign(denominator); + bool low = compare(numerator, lower) - even < 0; // numerator <[=] lower. + // numerator + upper >[=] pow10: + bool high = add_compare(numerator, *upper, denominator) + even > 0; + data[num_digits++] = static_cast('0' + digit); + if (low || high) { + if (!low) { + ++data[num_digits - 1]; + } else if (high) { + int result = add_compare(numerator, numerator, denominator); + // Round half to even. + if (result > 0 || (result == 0 && (digit % 2) != 0)) + ++data[num_digits - 1]; + } + buf.try_resize(to_unsigned(num_digits)); + exp10 -= num_digits - 1; + return; + } + numerator *= 10; + lower *= 10; + if (upper != &lower) *upper *= 10; + } + } + // Generate the given number of digits. + exp10 -= num_digits - 1; + if (num_digits == 0) { + buf.try_resize(1); + denominator *= 10; + buf[0] = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0'; + return; + } + buf.try_resize(to_unsigned(num_digits)); + for (int i = 0; i < num_digits - 1; ++i) { + int digit = numerator.divmod_assign(denominator); + buf[i] = static_cast('0' + digit); + numerator *= 10; + } + int digit = numerator.divmod_assign(denominator); + auto result = add_compare(numerator, numerator, denominator); + if (result > 0 || (result == 0 && (digit % 2) != 0)) { + if (digit == 9) { + const auto overflow = '0' + 10; + buf[num_digits - 1] = overflow; + // Propagate the carry. + for (int i = num_digits - 1; i > 0 && buf[i] == overflow; --i) { + buf[i] = '0'; + ++buf[i - 1]; + } + if (buf[0] == overflow) { + buf[0] = '1'; + ++exp10; + } + return; + } + ++digit; + } + buf[num_digits - 1] = static_cast('0' + digit); +} + +template +int format_float(T value, int precision, float_specs specs, buffer& buf) { + static_assert(!std::is_same::value, ""); + FMT_ASSERT(value >= 0, "value is negative"); + + const bool fixed = specs.format == float_format::fixed; + if (value <= 0) { // <= instead of == to silence a warning. + if (precision <= 0 || !fixed) { + buf.push_back('0'); + return 0; + } + buf.try_resize(to_unsigned(precision)); + std::uninitialized_fill_n(buf.data(), precision, '0'); + return -precision; + } + + if (!specs.use_grisu) return snprintf_float(value, precision, specs, buf); + + if (precision < 0) { + // Use Dragonbox for the shortest format. + if (specs.binary32) { + auto dec = dragonbox::to_decimal(static_cast(value)); + write(buffer_appender(buf), dec.significand); + return dec.exponent; + } + auto dec = dragonbox::to_decimal(static_cast(value)); + write(buffer_appender(buf), dec.significand); + return dec.exponent; + } + + // Use Grisu + Dragon4 for the given precision: + // https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf. + int exp = 0; + const int min_exp = -60; // alpha in Grisu. + int cached_exp10 = 0; // K in Grisu. + fp normalized = normalize(fp(value)); + const auto cached_pow = get_cached_power( + min_exp - (normalized.e + fp::significand_size), cached_exp10); + normalized = normalized * cached_pow; + // Limit precision to the maximum possible number of significant digits in an + // IEEE754 double because we don't need to generate zeros. + const int max_double_digits = 767; + if (precision > max_double_digits) precision = max_double_digits; + fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed}; + if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error) { + exp += handler.size - cached_exp10 - 1; + fallback_format(value, handler.precision, specs.binary32, buf, exp); + } else { + exp += handler.exp10; + buf.try_resize(to_unsigned(handler.size)); + } + if (!fixed && !specs.showpoint) { + // Remove trailing zeros. + auto num_digits = buf.size(); + while (num_digits > 0 && buf[num_digits - 1] == '0') { + --num_digits; + ++exp; + } + buf.try_resize(num_digits); + } + return exp; +} // namespace detail + +template +int snprintf_float(T value, int precision, float_specs specs, + buffer& buf) { + // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. + FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer"); + static_assert(!std::is_same::value, ""); + + // Subtract 1 to account for the difference in precision since we use %e for + // both general and exponent format. + if (specs.format == float_format::general || + specs.format == float_format::exp) + precision = (precision >= 0 ? precision : 6) - 1; + + // Build the format string. + enum { max_format_size = 7 }; // The longest format is "%#.*Le". + char format[max_format_size]; + char* format_ptr = format; + *format_ptr++ = '%'; + if (specs.showpoint && specs.format == float_format::hex) *format_ptr++ = '#'; + if (precision >= 0) { + *format_ptr++ = '.'; + *format_ptr++ = '*'; + } + if (std::is_same()) *format_ptr++ = 'L'; + *format_ptr++ = specs.format != float_format::hex + ? (specs.format == float_format::fixed ? 'f' : 'e') + : (specs.upper ? 'A' : 'a'); + *format_ptr = '\0'; + + // Format using snprintf. + auto offset = buf.size(); + for (;;) { + auto begin = buf.data() + offset; + auto capacity = buf.capacity() - offset; +#ifdef FMT_FUZZ + if (precision > 100000) + throw std::runtime_error( + "fuzz mode - avoid large allocation inside snprintf"); +#endif + // Suppress the warning about a nonliteral format string. + // Cannot use auto because of a bug in MinGW (#1532). + int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF; + int result = precision >= 0 + ? snprintf_ptr(begin, capacity, format, precision, value) + : snprintf_ptr(begin, capacity, format, value); + if (result < 0) { + // The buffer will grow exponentially. + buf.try_reserve(buf.capacity() + 1); + continue; + } + auto size = to_unsigned(result); + // Size equal to capacity means that the last character was truncated. + if (size >= capacity) { + buf.try_reserve(size + offset + 1); // Add 1 for the terminating '\0'. + continue; + } + auto is_digit = [](char c) { return c >= '0' && c <= '9'; }; + if (specs.format == float_format::fixed) { + if (precision == 0) { + buf.try_resize(size); + return 0; + } + // Find and remove the decimal point. + auto end = begin + size, p = end; + do { + --p; + } while (is_digit(*p)); + int fraction_size = static_cast(end - p - 1); + std::memmove(p, p + 1, to_unsigned(fraction_size)); + buf.try_resize(size - 1); + return -fraction_size; + } + if (specs.format == float_format::hex) { + buf.try_resize(size + offset); + return 0; + } + // Find and parse the exponent. + auto end = begin + size, exp_pos = end; + do { + --exp_pos; + } while (*exp_pos != 'e'); + char sign = exp_pos[1]; + assert(sign == '+' || sign == '-'); + int exp = 0; + auto p = exp_pos + 2; // Skip 'e' and sign. + do { + assert(is_digit(*p)); + exp = exp * 10 + (*p++ - '0'); + } while (p != end); + if (sign == '-') exp = -exp; + int fraction_size = 0; + if (exp_pos != begin + 1) { + // Remove trailing zeros. + auto fraction_end = exp_pos - 1; + while (*fraction_end == '0') --fraction_end; + // Move the fractional part left to get rid of the decimal point. + fraction_size = static_cast(fraction_end - begin - 1); + std::memmove(begin + 1, begin + 2, to_unsigned(fraction_size)); + } + buf.try_resize(to_unsigned(fraction_size) + offset + 1); + return exp - fraction_size; + } +} + +// A public domain branchless UTF-8 decoder by Christopher Wellons: +// https://github.com/skeeto/branchless-utf8 +/* Decode the next character, c, from buf, reporting errors in e. + * + * Since this is a branchless decoder, four bytes will be read from the + * buffer regardless of the actual length of the next character. This + * means the buffer _must_ have at least three bytes of zero padding + * following the end of the data stream. + * + * Errors are reported in e, which will be non-zero if the parsed + * character was somehow invalid: invalid byte sequence, non-canonical + * encoding, or a surrogate half. + * + * The function returns a pointer to the next character. When an error + * occurs, this pointer will be a guess that depends on the particular + * error, but it will always advance at least one byte. + */ +inline const char* utf8_decode(const char* buf, uint32_t* c, int* e) { + static const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07}; + static const uint32_t mins[] = {4194304, 0, 128, 2048, 65536}; + static const int shiftc[] = {0, 18, 12, 6, 0}; + static const int shifte[] = {0, 6, 4, 2, 0}; + + int len = code_point_length(buf); + const char* next = buf + len; + + // Assume a four-byte character and load four bytes. Unused bits are + // shifted out. + auto s = reinterpret_cast(buf); + *c = uint32_t(s[0] & masks[len]) << 18; + *c |= uint32_t(s[1] & 0x3f) << 12; + *c |= uint32_t(s[2] & 0x3f) << 6; + *c |= uint32_t(s[3] & 0x3f) << 0; + *c >>= shiftc[len]; + + // Accumulate the various error conditions. + *e = (*c < mins[len]) << 6; // non-canonical encoding + *e |= ((*c >> 11) == 0x1b) << 7; // surrogate half? + *e |= (*c > 0x10FFFF) << 8; // out of range? + *e |= (s[1] & 0xc0) >> 2; + *e |= (s[2] & 0xc0) >> 4; + *e |= (s[3]) >> 6; + *e ^= 0x2a; // top two bits of each tail byte correct? + *e >>= shifte[len]; + + return next; +} + +struct stringifier { + template FMT_INLINE std::string operator()(T value) const { + return to_string(value); + } + std::string operator()(basic_format_arg::handle h) const { + memory_buffer buf; + format_parse_context parse_ctx({}); + format_context format_ctx(buffer_appender(buf), {}, {}); + h.format(parse_ctx, format_ctx); + return to_string(buf); + } +}; +} // namespace detail + +template <> struct formatter { + format_parse_context::iterator parse(format_parse_context& ctx) { + return ctx.begin(); + } + + format_context::iterator format(const detail::bigint& n, + format_context& ctx) { + auto out = ctx.out(); + bool first = true; + for (auto i = n.bigits_.size(); i > 0; --i) { + auto value = n.bigits_[i - 1u]; + if (first) { + out = format_to(out, "{:x}", value); + first = false; + continue; + } + out = format_to(out, "{:08x}", value); + } + if (n.exp_ > 0) + out = format_to(out, "p{}", n.exp_ * detail::bigint::bigit_bits); + return out; + } +}; + +FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) { + auto transcode = [this](const char* p) { + auto cp = uint32_t(); + auto error = 0; + p = utf8_decode(p, &cp, &error); + if (error != 0) FMT_THROW(std::runtime_error("invalid utf8")); + if (cp <= 0xFFFF) { + buffer_.push_back(static_cast(cp)); + } else { + cp -= 0x10000; + buffer_.push_back(static_cast(0xD800 + (cp >> 10))); + buffer_.push_back(static_cast(0xDC00 + (cp & 0x3FF))); + } + return p; + }; + auto p = s.data(); + const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars. + if (s.size() >= block_size) { + for (auto end = p + s.size() - block_size + 1; p < end;) p = transcode(p); + } + if (auto num_chars_left = s.data() + s.size() - p) { + char buf[2 * block_size - 1] = {}; + memcpy(buf, p, to_unsigned(num_chars_left)); + p = buf; + do { + p = transcode(p); + } while (p - buf < num_chars_left); + } + buffer_.push_back(0); +} + +FMT_FUNC void format_system_error(detail::buffer& out, int error_code, + string_view message) FMT_NOEXCEPT { + FMT_TRY { + memory_buffer buf; + buf.resize(inline_buffer_size); + for (;;) { + char* system_message = &buf[0]; + int result = + detail::safe_strerror(error_code, system_message, buf.size()); + if (result == 0) { + format_to(detail::buffer_appender(out), "{}: {}", message, + system_message); + return; + } + if (result != ERANGE) + break; // Can't get error message, report error code instead. + buf.resize(buf.size() * 2); + } + } + FMT_CATCH(...) {} + format_error_code(out, error_code, message); +} + +FMT_FUNC void detail::error_handler::on_error(const char* message) { + FMT_THROW(format_error(message)); +} + +FMT_FUNC void report_system_error(int error_code, + fmt::string_view message) FMT_NOEXCEPT { + report_error(format_system_error, error_code, message); +} + +FMT_FUNC std::string detail::vformat(string_view format_str, format_args args) { + if (format_str.size() == 2 && equal2(format_str.data(), "{}")) { + auto arg = args.get(0); + if (!arg) error_handler().on_error("argument not found"); + return visit_format_arg(stringifier(), arg); + } + memory_buffer buffer; + detail::vformat_to(buffer, format_str, args); + return to_string(buffer); +} + +#ifdef _WIN32 +namespace detail { +using dword = conditional_t; +extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( // + void*, const void*, dword, dword*, void*); +} // namespace detail +#endif + +FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) { + memory_buffer buffer; + detail::vformat_to(buffer, format_str, + basic_format_args>(args)); +#ifdef _WIN32 + auto fd = _fileno(f); + if (_isatty(fd)) { + detail::utf8_to_utf16 u16(string_view(buffer.data(), buffer.size())); + auto written = detail::dword(); + if (!detail::WriteConsoleW(reinterpret_cast(_get_osfhandle(fd)), + u16.c_str(), static_cast(u16.size()), + &written, nullptr)) { + FMT_THROW(format_error("failed to write to console")); + } + return; + } +#endif + detail::fwrite_fully(buffer.data(), 1, buffer.size(), f); +} + +#ifdef _WIN32 +// Print assuming legacy (non-Unicode) encoding. +FMT_FUNC void detail::vprint_mojibake(std::FILE* f, string_view format_str, + format_args args) { + memory_buffer buffer; + detail::vformat_to(buffer, format_str, + basic_format_args>(args)); + fwrite_fully(buffer.data(), 1, buffer.size(), f); +} +#endif + +FMT_FUNC void vprint(string_view format_str, format_args args) { + vprint(stdout, format_str, args); +} + +FMT_END_NAMESPACE + +#endif // FMT_FORMAT_INL_H_ diff --git a/source/fmt/format.cc b/source/fmt/format.cc index 494a4a71..6141d964 100644 --- a/source/fmt/format.cc +++ b/source/fmt/format.cc @@ -1,542 +1,99 @@ -/* - Formatting library for C++ +// Formatting library for C++ +// +// Copyright (c) 2012 - 2016, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. - Copyright (c) 2012 - 2016, Victor Zverovich - All rights reserved. +#include "fmt/format-inl.h" - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "format.h" - -#include - -#include -#include -#include -#include -#include -#include // for std::ptrdiff_t - -#if defined(_WIN32) && defined(__MINGW32__) -# include -#endif - -#if FMT_USE_WINDOWS_H -# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX) -# include -# else -# define NOMINMAX -# include -# undef NOMINMAX -# endif -#endif - -#if FMT_EXCEPTIONS -# define FMT_TRY try -# define FMT_CATCH(x) catch (x) -#else -# define FMT_TRY if (true) -# define FMT_CATCH(x) if (false) -#endif - -#ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable: 4127) // conditional expression is constant -# pragma warning(disable: 4702) // unreachable code -// Disable deprecation warning for strerror. The latter is not called but -// MSVC fails to detect it. -# pragma warning(disable: 4996) -#endif - -// Dummy implementations of strerror_r and strerror_s called if corresponding -// system functions are not available. -static inline fmt::internal::Null<> strerror_r(int, char *, ...) { - return fmt::internal::Null<>(); -} -static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) { - return fmt::internal::Null<>(); -} - -namespace fmt { - -FMT_FUNC internal::RuntimeError::~RuntimeError() FMT_DTOR_NOEXCEPT {} -FMT_FUNC FormatError::~FormatError() FMT_DTOR_NOEXCEPT {} -FMT_FUNC SystemError::~SystemError() FMT_DTOR_NOEXCEPT {} - -namespace { - -#ifndef _MSC_VER -# define FMT_SNPRINTF snprintf -#else // _MSC_VER -inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) { - va_list args; - va_start(args, format); - int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args); - va_end(args); - return result; -} -# define FMT_SNPRINTF fmt_snprintf -#endif // _MSC_VER - -#if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT) -# define FMT_SWPRINTF snwprintf -#else -# define FMT_SWPRINTF swprintf -#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT) - -const char RESET_COLOR[] = "\x1b[0m"; - -typedef void (*FormatFunc)(Writer &, int, StringRef); - -// Portable thread-safe version of strerror. -// Sets buffer to point to a string describing the error code. -// This can be either a pointer to a string stored in buffer, -// or a pointer to some static immutable string. -// Returns one of the following values: -// 0 - success -// ERANGE - buffer is not large enough to store the error message -// other - failure -// Buffer should be at least of size 1. -int safe_strerror( - int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT { - FMT_ASSERT(buffer != 0 && buffer_size != 0, "invalid buffer"); - - class StrError { - private: - int error_code_; - char *&buffer_; - std::size_t buffer_size_; - - // A noop assignment operator to avoid bogus warnings. - void operator=(const StrError &) {} - - // Handle the result of XSI-compliant version of strerror_r. - int handle(int result) { - // glibc versions before 2.13 return result in errno. - return result == -1 ? errno : result; - } - - // Handle the result of GNU-specific version of strerror_r. - int handle(char *message) { - // If the buffer is full then the message is probably truncated. - if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1) - return ERANGE; - buffer_ = message; - return 0; - } - - // Handle the case when strerror_r is not available. - int handle(internal::Null<>) { - return fallback(strerror_s(buffer_, buffer_size_, error_code_)); - } - - // Fallback to strerror_s when strerror_r is not available. - int fallback(int result) { - // If the buffer is full then the message is probably truncated. - return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? - ERANGE : result; - } - - // Fallback to strerror if strerror_r and strerror_s are not available. - int fallback(internal::Null<>) { - errno = 0; - buffer_ = strerror(error_code_); - return errno; - } - - public: - StrError(int err_code, char *&buf, std::size_t buf_size) - : error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {} - - int run() { - // Suppress a warning about unused strerror_r. - strerror_r(0, FMT_NULL, ""); - return handle(strerror_r(error_code_, buffer_, buffer_size_)); - } - }; - return StrError(error_code, buffer, buffer_size).run(); -} - -void format_error_code(Writer &out, int error_code, - StringRef message) FMT_NOEXCEPT { - // Report error code making sure that the output fits into - // INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential - // bad_alloc. - out.clear(); - static const char SEP[] = ": "; - static const char ERROR_STR[] = "error "; - // Subtract 2 to account for terminating null characters in SEP and ERROR_STR. - std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2; - typedef internal::IntTraits::MainType MainType; - MainType abs_value = static_cast(error_code); - if (internal::is_negative(error_code)) { - abs_value = 0 - abs_value; - ++error_code_size; - } - error_code_size += internal::count_digits(abs_value); - if (message.size() <= internal::INLINE_BUFFER_SIZE - error_code_size) - out << message << SEP; - out << ERROR_STR << error_code; - assert(out.size() <= internal::INLINE_BUFFER_SIZE); -} - -void report_error(FormatFunc func, int error_code, - StringRef message) FMT_NOEXCEPT { - MemoryWriter full_message; - func(full_message, error_code, message); - // Use Writer::data instead of Writer::c_str to avoid potential memory - // allocation. - std::fwrite(full_message.data(), full_message.size(), 1, stderr); - std::fputc('\n', stderr); -} -} // namespace - -namespace internal { - -// This method is used to preserve binary compatibility with fmt 3.0. -// It can be removed in 4.0. -FMT_FUNC void format_system_error( - Writer &out, int error_code, StringRef message) FMT_NOEXCEPT { - fmt::format_system_error(out, error_code, message); -} -} // namespace internal - -FMT_FUNC void SystemError::init( - int err_code, CStringRef format_str, ArgList args) { - error_code_ = err_code; - MemoryWriter w; - format_system_error(w, err_code, format(format_str, args)); - std::runtime_error &base = *this; - base = std::runtime_error(w.str()); -} - -template -int internal::CharTraits::format_float( - char *buffer, std::size_t size, const char *format, - unsigned width, int precision, T value) { - if (width == 0) { - return precision < 0 ? - FMT_SNPRINTF(buffer, size, format, value) : - FMT_SNPRINTF(buffer, size, format, precision, value); - } - return precision < 0 ? - FMT_SNPRINTF(buffer, size, format, width, value) : - FMT_SNPRINTF(buffer, size, format, width, precision, value); -} +FMT_BEGIN_NAMESPACE +namespace detail { template -int internal::CharTraits::format_float( - wchar_t *buffer, std::size_t size, const wchar_t *format, - unsigned width, int precision, T value) { - if (width == 0) { - return precision < 0 ? - FMT_SWPRINTF(buffer, size, format, value) : - FMT_SWPRINTF(buffer, size, format, precision, value); - } - return precision < 0 ? - FMT_SWPRINTF(buffer, size, format, width, value) : - FMT_SWPRINTF(buffer, size, format, width, precision, value); -} - -template -const char internal::BasicData::DIGITS[] = - "0001020304050607080910111213141516171819" - "2021222324252627282930313233343536373839" - "4041424344454647484950515253545556575859" - "6061626364656667686970717273747576777879" - "8081828384858687888990919293949596979899"; - -#define FMT_POWERS_OF_10(factor) \ - factor * 10, \ - factor * 100, \ - factor * 1000, \ - factor * 10000, \ - factor * 100000, \ - factor * 1000000, \ - factor * 10000000, \ - factor * 100000000, \ - factor * 1000000000 - -template -const uint32_t internal::BasicData::POWERS_OF_10_32[] = { - 0, FMT_POWERS_OF_10(1) -}; - -template -const uint64_t internal::BasicData::POWERS_OF_10_64[] = { - 0, - FMT_POWERS_OF_10(1), - FMT_POWERS_OF_10(ULongLong(1000000000)), - // Multiply several constants instead of using a single long long constant - // to avoid warnings about C++98 not supporting long long. - ULongLong(1000000000) * ULongLong(1000000000) * 10 -}; - -FMT_FUNC void internal::report_unknown_type(char code, const char *type) { - (void)type; - if (std::isprint(static_cast(code))) { - FMT_THROW(FormatError( - format("unknown format code '{}' for {}", code, type))); - } - FMT_THROW(FormatError( - format("unknown format code '\\x{:02x}' for {}", - static_cast(code), type))); -} - -#if FMT_USE_WINDOWS_H - -FMT_FUNC internal::UTF8ToUTF16::UTF8ToUTF16(StringRef s) { - static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16"; - if (s.size() > INT_MAX) - FMT_THROW(WindowsError(ERROR_INVALID_PARAMETER, ERROR_MSG)); - int s_size = static_cast(s.size()); - int length = MultiByteToWideChar( - CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, FMT_NULL, 0); - if (length == 0) - FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); - buffer_.resize(length + 1); - length = MultiByteToWideChar( - CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length); - if (length == 0) - FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); - buffer_[length] = 0; -} - -FMT_FUNC internal::UTF16ToUTF8::UTF16ToUTF8(WStringRef s) { - if (int error_code = convert(s)) { - FMT_THROW(WindowsError(error_code, - "cannot convert string from UTF-16 to UTF-8")); - } -} - -FMT_FUNC int internal::UTF16ToUTF8::convert(WStringRef s) { - if (s.size() > INT_MAX) - return ERROR_INVALID_PARAMETER; - int s_size = static_cast(s.size()); - int length = WideCharToMultiByte( - CP_UTF8, 0, s.data(), s_size, FMT_NULL, 0, FMT_NULL, FMT_NULL); - if (length == 0) - return GetLastError(); - buffer_.resize(length + 1); - length = WideCharToMultiByte( - CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, FMT_NULL, FMT_NULL); - if (length == 0) - return GetLastError(); - buffer_[length] = 0; - return 0; -} - -FMT_FUNC void WindowsError::init( - int err_code, CStringRef format_str, ArgList args) { - error_code_ = err_code; - MemoryWriter w; - internal::format_windows_error(w, err_code, format(format_str, args)); - std::runtime_error &base = *this; - base = std::runtime_error(w.str()); -} - -FMT_FUNC void internal::format_windows_error( - Writer &out, int error_code, StringRef message) FMT_NOEXCEPT { - FMT_TRY { - MemoryBuffer buffer; - buffer.resize(INLINE_BUFFER_SIZE); - for (;;) { - wchar_t *system_message = &buffer[0]; - int result = FormatMessageW( - FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - FMT_NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - system_message, static_cast(buffer.size()), FMT_NULL); - if (result != 0) { - UTF16ToUTF8 utf8_message; - if (utf8_message.convert(system_message) == ERROR_SUCCESS) { - out << message << ": " << utf8_message; - return; - } - break; - } - if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) - break; // Can't get error message, report error code instead. - buffer.resize(buffer.size() * 2); - } - } FMT_CATCH(...) {} - fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32. +int format_float(char* buf, std::size_t size, const char* format, int precision, + T value) { +#ifdef FMT_FUZZ + if (precision > 100000) + throw std::runtime_error( + "fuzz mode - avoid large allocation inside snprintf"); +#endif + // Suppress the warning about nonliteral format string. + int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF; + return precision < 0 ? snprintf_ptr(buf, size, format, value) + : snprintf_ptr(buf, size, format, precision, value); } -#endif // FMT_USE_WINDOWS_H - -FMT_FUNC void format_system_error( - Writer &out, int error_code, StringRef message) FMT_NOEXCEPT { - FMT_TRY { - internal::MemoryBuffer buffer; - buffer.resize(internal::INLINE_BUFFER_SIZE); - for (;;) { - char *system_message = &buffer[0]; - int result = safe_strerror(error_code, system_message, buffer.size()); - if (result == 0) { - out << message << ": " << system_message; - return; - } - if (result != ERANGE) - break; // Can't get error message, report error code instead. - buffer.resize(buffer.size() * 2); - } - } FMT_CATCH(...) {} - fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32. -} +template FMT_API dragonbox::decimal_fp dragonbox::to_decimal(float x) + FMT_NOEXCEPT; +template FMT_API dragonbox::decimal_fp dragonbox::to_decimal(double x) + FMT_NOEXCEPT; +// DEPRECATED! This function exists for ABI compatibility. template -void internal::ArgMap::init(const ArgList &args) { - if (!map_.empty()) - return; - typedef internal::NamedArg NamedArg; - const NamedArg *named_arg = FMT_NULL; - bool use_values = - args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE; - if (use_values) { - for (unsigned i = 0;/*nothing*/; ++i) { - internal::Arg::Type arg_type = args.type(i); - switch (arg_type) { - case internal::Arg::NONE: - return; - case internal::Arg::NAMED_ARG: - named_arg = static_cast(args.values_[i].pointer); - map_.push_back(Pair(named_arg->name, *named_arg)); - break; - default: - /*nothing*/; - } - } - return; - } - for (unsigned i = 0; i != ArgList::MAX_PACKED_ARGS; ++i) { - internal::Arg::Type arg_type = args.type(i); - if (arg_type == internal::Arg::NAMED_ARG) { - named_arg = static_cast(args.args_[i].pointer); - map_.push_back(Pair(named_arg->name, *named_arg)); - } - } - for (unsigned i = ArgList::MAX_PACKED_ARGS;/*nothing*/; ++i) { - switch (args.args_[i].type) { - case internal::Arg::NONE: - return; - case internal::Arg::NAMED_ARG: - named_arg = static_cast(args.args_[i].pointer); - map_.push_back(Pair(named_arg->name, *named_arg)); - break; - default: - /*nothing*/; - } - } -} - -template -void internal::FixedBuffer::grow(std::size_t) { - FMT_THROW(std::runtime_error("buffer overflow")); -} - -FMT_FUNC internal::Arg internal::FormatterBase::do_get_arg( - unsigned arg_index, const char *&error) { - internal::Arg arg = args_[arg_index]; - switch (arg.type) { - case internal::Arg::NONE: - error = "argument index out of range"; - break; - case internal::Arg::NAMED_ARG: - arg = *static_cast(arg.pointer); - break; - default: - /*nothing*/; - } - return arg; -} - -FMT_FUNC void report_system_error( - int error_code, fmt::StringRef message) FMT_NOEXCEPT { - // 'fmt::' is for bcc32. - report_error(format_system_error, error_code, message); -} - -#if FMT_USE_WINDOWS_H -FMT_FUNC void report_windows_error( - int error_code, fmt::StringRef message) FMT_NOEXCEPT { - // 'fmt::' is for bcc32. - report_error(internal::format_windows_error, error_code, message); -} +typename basic_format_context>, + Char>::iterator +vformat_to(buffer& buf, basic_string_view format_str, + basic_format_args>>, + type_identity_t>> + args) { + using iterator = std::back_insert_iterator>; + using context = basic_format_context< + std::back_insert_iterator>>, + type_identity_t>; + auto out = iterator(buf); + format_handler h(out, format_str, args, {}); + parse_format_string(format_str, h); + return out; +} +template basic_format_context>, + char>::iterator +vformat_to(buffer&, string_view, + basic_format_args>>, + type_identity_t>>); +} // namespace detail + +template struct FMT_INSTANTIATION_DEF_API detail::basic_data; + +// Workaround a bug in MSVC2013 that prevents instantiation of format_float. +int (*instantiate_format_float)(double, int, detail::float_specs, + detail::buffer&) = detail::format_float; + +#ifndef FMT_STATIC_THOUSANDS_SEPARATOR +template FMT_API detail::locale_ref::locale_ref(const std::locale& loc); +template FMT_API std::locale detail::locale_ref::get() const; #endif -FMT_FUNC void print(std::FILE *f, CStringRef format_str, ArgList args) { - MemoryWriter w; - w.write(format_str, args); - std::fwrite(w.data(), 1, w.size(), f); -} - -FMT_FUNC void print(CStringRef format_str, ArgList args) { - print(stdout, format_str, args); -} - -FMT_FUNC void print_colored(Color c, CStringRef format, ArgList args) { - char escape[] = "\x1b[30m"; - escape[3] = static_cast('0' + c); - std::fputs(escape, stdout); - print(format, args); - std::fputs(RESET_COLOR, stdout); -} - -#ifndef FMT_HEADER_ONLY - -template struct internal::BasicData; - // Explicit instantiations for char. -template void internal::FixedBuffer::grow(std::size_t); +template FMT_API std::string detail::grouping_impl(locale_ref); +template FMT_API char detail::thousands_sep_impl(locale_ref); +template FMT_API char detail::decimal_point_impl(locale_ref); -template void internal::ArgMap::init(const ArgList &args); +template FMT_API void detail::buffer::append(const char*, const char*); -template FMT_API int internal::CharTraits::format_float( - char *buffer, std::size_t size, const char *format, - unsigned width, int precision, double value); +template FMT_API void detail::vformat_to( + detail::buffer&, string_view, + basic_format_args, detail::locale_ref); -template FMT_API int internal::CharTraits::format_float( - char *buffer, std::size_t size, const char *format, - unsigned width, int precision, long double value); +template FMT_API int detail::snprintf_float(double, int, detail::float_specs, + detail::buffer&); +template FMT_API int detail::snprintf_float(long double, int, + detail::float_specs, + detail::buffer&); +template FMT_API int detail::format_float(double, int, detail::float_specs, + detail::buffer&); +template FMT_API int detail::format_float(long double, int, detail::float_specs, + detail::buffer&); // Explicit instantiations for wchar_t. -template void internal::FixedBuffer::grow(std::size_t); - -template void internal::ArgMap::init(const ArgList &args); +template FMT_API std::string detail::grouping_impl(locale_ref); +template FMT_API wchar_t detail::thousands_sep_impl(locale_ref); +template FMT_API wchar_t detail::decimal_point_impl(locale_ref); -template FMT_API int internal::CharTraits::format_float( - wchar_t *buffer, std::size_t size, const wchar_t *format, - unsigned width, int precision, double value); - -template FMT_API int internal::CharTraits::format_float( - wchar_t *buffer, std::size_t size, const wchar_t *format, - unsigned width, int precision, long double value); - -#endif // FMT_HEADER_ONLY - -} // namespace fmt - -#ifdef _MSC_VER -# pragma warning(pop) -#endif +template FMT_API void detail::buffer::append(const wchar_t*, + const wchar_t*); +FMT_END_NAMESPACE diff --git a/source/fmt/format.h b/source/fmt/format.h index a8b5c479..1a037b02 100644 --- a/source/fmt/format.h +++ b/source/fmt/format.h @@ -1,957 +1,932 @@ /* Formatting library for C++ - Copyright (c) 2012 - 2016, Victor Zverovich - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + Copyright (c) 2012 - present, Victor Zverovich + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + --- Optional exception to the license --- + + As an exception, if, as a result of your compiling your source code, portions + of this Software are embedded into a machine-executable object form of such + source code, you may redistribute such embedded portions in such object form + without including the above copyright and permission notices. */ #ifndef FMT_FORMAT_H_ #define FMT_FORMAT_H_ -#include -#include +#include +#include #include -#include -#include +#include #include #include #include -#include -#include -#include // for std::pair -// The fmt library version in the form major * 10000 + minor * 100 + patch. -#define FMT_VERSION 30002 +#include "core.h" -#ifdef _SECURE_SCL -# define FMT_SECURE_SCL _SECURE_SCL -#else -# define FMT_SECURE_SCL 0 -#endif - -#if FMT_SECURE_SCL -# include -#endif - -#ifdef _MSC_VER -# define FMT_MSC_VER _MSC_VER -#else -# define FMT_MSC_VER 0 -#endif - -#if FMT_MSC_VER && FMT_MSC_VER <= 1500 -typedef unsigned __int32 uint32_t; -typedef unsigned __int64 uint64_t; -typedef __int64 intmax_t; -#else -#include -#endif - -#if !defined(FMT_HEADER_ONLY) && defined(_WIN32) -# ifdef FMT_EXPORT -# define FMT_API __declspec(dllexport) -# elif defined(FMT_SHARED) -# define FMT_API __declspec(dllimport) -# endif -#endif -#ifndef FMT_API -# define FMT_API -#endif - -#ifdef __GNUC__ -# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -# define FMT_GCC_EXTENSION __extension__ -# if FMT_GCC_VERSION >= 406 -# pragma GCC diagnostic push -// Disable the warning about "long long" which is sometimes reported even -// when using __extension__. -# pragma GCC diagnostic ignored "-Wlong-long" -// Disable the warning about declaration shadowing because it affects too -// many valid cases. -# pragma GCC diagnostic ignored "-Wshadow" -// Disable the warning about implicit conversions that may change the sign of -// an integer; silencing it otherwise would require many explicit casts. -# pragma GCC diagnostic ignored "-Wsign-conversion" -# endif -# if __cplusplus >= 201103L || defined __GXX_EXPERIMENTAL_CXX0X__ -# define FMT_HAS_GXX_CXX11 1 -# endif -#else -# define FMT_GCC_EXTENSION -#endif - -#if defined(__INTEL_COMPILER) -# define FMT_ICC_VERSION __INTEL_COMPILER +#ifdef __INTEL_COMPILER +# define FMT_ICC_VERSION __INTEL_COMPILER #elif defined(__ICL) -# define FMT_ICC_VERSION __ICL -#endif - -#if defined(__clang__) && !defined(FMT_ICC_VERSION) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wdocumentation-unknown-command" -# pragma clang diagnostic ignored "-Wpadded" -#endif - -#ifdef __GNUC_LIBSTD__ -# define FMT_GNUC_LIBSTD_VERSION (__GNUC_LIBSTD__ * 100 + __GNUC_LIBSTD_MINOR__) +# define FMT_ICC_VERSION __ICL +#else +# define FMT_ICC_VERSION 0 #endif -#ifdef __has_feature -# define FMT_HAS_FEATURE(x) __has_feature(x) +#ifdef __NVCC__ +# define FMT_CUDA_VERSION (__CUDACC_VER_MAJOR__ * 100 + __CUDACC_VER_MINOR__) #else -# define FMT_HAS_FEATURE(x) 0 +# define FMT_CUDA_VERSION 0 #endif #ifdef __has_builtin -# define FMT_HAS_BUILTIN(x) __has_builtin(x) +# define FMT_HAS_BUILTIN(x) __has_builtin(x) #else -# define FMT_HAS_BUILTIN(x) 0 +# define FMT_HAS_BUILTIN(x) 0 #endif -#ifdef __has_cpp_attribute -# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#if FMT_GCC_VERSION || FMT_CLANG_VERSION +# define FMT_NOINLINE __attribute__((noinline)) #else -# define FMT_HAS_CPP_ATTRIBUTE(x) 0 -#endif - -#ifndef FMT_USE_VARIADIC_TEMPLATES -// Variadic templates are available in GCC since version 4.4 -// (http://gcc.gnu.org/projects/cxx0x.html) and in Visual C++ -// since version 2013. -# define FMT_USE_VARIADIC_TEMPLATES \ - (FMT_HAS_FEATURE(cxx_variadic_templates) || \ - (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1800) -#endif - -#ifndef FMT_USE_RVALUE_REFERENCES -// Don't use rvalue references when compiling with clang and an old libstdc++ -// as the latter doesn't provide std::move. -# if defined(FMT_GNUC_LIBSTD_VERSION) && FMT_GNUC_LIBSTD_VERSION <= 402 -# define FMT_USE_RVALUE_REFERENCES 0 -# else -# define FMT_USE_RVALUE_REFERENCES \ - (FMT_HAS_FEATURE(cxx_rvalue_references) || \ - (FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1600) -# endif -#endif - -// Check if exceptions are disabled. -#if defined(__GNUC__) && !defined(__EXCEPTIONS) -# define FMT_EXCEPTIONS 0 -#endif -#if FMT_MSC_VER && !_HAS_EXCEPTIONS -# define FMT_EXCEPTIONS 0 -#endif -#ifndef FMT_EXCEPTIONS -# define FMT_EXCEPTIONS 1 -#endif - -#ifndef FMT_THROW -# if FMT_EXCEPTIONS -# define FMT_THROW(x) throw x -# else -# define FMT_THROW(x) assert(false) -# endif -#endif - -// Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature). -#ifndef FMT_USE_NOEXCEPT -# define FMT_USE_NOEXCEPT 0 -#endif - -#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ - (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \ - FMT_MSC_VER >= 1900 -# define FMT_DETECTED_NOEXCEPT noexcept +# define FMT_NOINLINE +#endif + +#if __cplusplus == 201103L || __cplusplus == 201402L +# if defined(__INTEL_COMPILER) || defined(__PGI) +# define FMT_FALLTHROUGH +# elif defined(__clang__) +# define FMT_FALLTHROUGH [[clang::fallthrough]] +# elif FMT_GCC_VERSION >= 700 && \ + (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520) +# define FMT_FALLTHROUGH [[gnu::fallthrough]] +# else +# define FMT_FALLTHROUGH +# endif +#elif FMT_HAS_CPP17_ATTRIBUTE(fallthrough) || \ + (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define FMT_FALLTHROUGH [[fallthrough]] #else -# define FMT_DETECTED_NOEXCEPT throw() +# define FMT_FALLTHROUGH #endif -#ifndef FMT_NOEXCEPT -# if FMT_EXCEPTIONS -# define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT -# else -# define FMT_NOEXCEPT -# endif +#ifndef FMT_MAYBE_UNUSED +# if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused) +# define FMT_MAYBE_UNUSED [[maybe_unused]] +# else +# define FMT_MAYBE_UNUSED +# endif #endif -// This is needed because GCC still uses throw() in its headers when exceptions -// are disabled. -#if FMT_GCC_VERSION -# define FMT_DTOR_NOEXCEPT FMT_DETECTED_NOEXCEPT +#ifndef FMT_THROW +# if FMT_EXCEPTIONS +# if FMT_MSC_VER || FMT_NVCC +FMT_BEGIN_NAMESPACE +namespace detail { +template inline void do_throw(const Exception& x) { + // Silence unreachable code warnings in MSVC and NVCC because these + // are nearly impossible to fix in a generic code. + volatile bool b = true; + if (b) throw x; +} +} // namespace detail +FMT_END_NAMESPACE +# define FMT_THROW(x) detail::do_throw(x) +# else +# define FMT_THROW(x) throw x +# endif +# else +# define FMT_THROW(x) \ + do { \ + static_cast(sizeof(x)); \ + FMT_ASSERT(false, ""); \ + } while (false) +# endif +#endif + +#if FMT_EXCEPTIONS +# define FMT_TRY try +# define FMT_CATCH(x) catch (x) #else -# define FMT_DTOR_NOEXCEPT FMT_NOEXCEPT -#endif - -#ifndef FMT_OVERRIDE -# if (defined(FMT_USE_OVERRIDE) && FMT_USE_OVERRIDE) || FMT_HAS_FEATURE(cxx_override) || \ - (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \ - FMT_MSC_VER >= 1900 -# define FMT_OVERRIDE override -# else -# define FMT_OVERRIDE -# endif -#endif - -#ifndef FMT_NULL -# if FMT_HAS_FEATURE(cxx_nullptr) || \ - (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \ - FMT_MSC_VER >= 1600 -# define FMT_NULL nullptr -# else -# define FMT_NULL NULL -# endif +# define FMT_TRY if (true) +# define FMT_CATCH(x) if (false) #endif -// A macro to disallow the copy constructor and operator= functions -// This should be used in the private: declarations for a class -#ifndef FMT_USE_DELETED_FUNCTIONS -# define FMT_USE_DELETED_FUNCTIONS 0 +#ifndef FMT_USE_USER_DEFINED_LITERALS +// EDG based compilers (Intel, NVIDIA, Elbrus, etc), GCC and MSVC support UDLs. +# if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \ + FMT_MSC_VER >= 1900) && \ + (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= /* UDL feature */ 480) +# define FMT_USE_USER_DEFINED_LITERALS 1 +# else +# define FMT_USE_USER_DEFINED_LITERALS 0 +# endif #endif -#if FMT_USE_DELETED_FUNCTIONS || FMT_HAS_FEATURE(cxx_deleted_functions) || \ - (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1800 -# define FMT_DELETED_OR_UNDEFINED = delete -# define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName&) = delete; \ - TypeName& operator=(const TypeName&) = delete -#else -# define FMT_DELETED_OR_UNDEFINED -# define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName&); \ - TypeName& operator=(const TypeName&) +#ifndef FMT_USE_UDL_TEMPLATE +// EDG frontend based compilers (icc, nvcc, PGI, etc) and GCC < 6.4 do not +// properly support UDL templates and GCC >= 9 warns about them. +# if FMT_USE_USER_DEFINED_LITERALS && \ + (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 501) && \ + ((FMT_GCC_VERSION >= 604 && __cplusplus >= 201402L) || \ + FMT_CLANG_VERSION >= 304) && \ + !defined(__PGI) && !defined(__NVCC__) +# define FMT_USE_UDL_TEMPLATE 1 +# else +# define FMT_USE_UDL_TEMPLATE 0 +# endif #endif -#ifndef FMT_USE_DEFAULTED_FUNCTIONS -# define FMT_USE_DEFAULTED_FUNCTIONS 0 +#ifndef FMT_USE_FLOAT +# define FMT_USE_FLOAT 1 #endif -#ifndef FMT_DEFAULTED_COPY_CTOR -# if FMT_USE_DEFAULTED_FUNCTIONS || FMT_HAS_FEATURE(cxx_defaulted_functions) || \ - (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1800 -# define FMT_DEFAULTED_COPY_CTOR(TypeName) \ - TypeName(const TypeName&) = default; -# else -# define FMT_DEFAULTED_COPY_CTOR(TypeName) -# endif +#ifndef FMT_USE_DOUBLE +# define FMT_USE_DOUBLE 1 #endif -#ifndef FMT_USE_USER_DEFINED_LITERALS -// All compilers which support UDLs also support variadic templates. This -// makes the fmt::literals implementation easier. However, an explicit check -// for variadic templates is added here just in case. -// For Intel's compiler both it and the system gcc/msc must support UDLs. -# define FMT_USE_USER_DEFINED_LITERALS \ - FMT_USE_VARIADIC_TEMPLATES && FMT_USE_RVALUE_REFERENCES && \ - (FMT_HAS_FEATURE(cxx_user_literals) || \ - (FMT_GCC_VERSION >= 407 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900) && \ - (!defined(FMT_ICC_VERSION) || FMT_ICC_VERSION >= 1500) +#ifndef FMT_USE_LONG_DOUBLE +# define FMT_USE_LONG_DOUBLE 1 #endif -#ifndef FMT_USE_EXTERN_TEMPLATES -// Clang doesn't have a feature check for extern templates so we check -// for variadic templates which were introduced in the same version. -// For GCC according to cppreference.com they were introduced in 3.3. -# define FMT_USE_EXTERN_TEMPLATES \ - ((__clang__ && FMT_USE_VARIADIC_TEMPLATES) || \ - (FMT_GCC_VERSION >= 303 && FMT_HAS_GXX_CXX11)) +// Defining FMT_REDUCE_INT_INSTANTIATIONS to 1, will reduce the number of +// int_writer template instances to just one by only using the largest integer +// type. This results in a reduction in binary size but will cause a decrease in +// integer formatting performance. +#if !defined(FMT_REDUCE_INT_INSTANTIATIONS) +# define FMT_REDUCE_INT_INSTANTIATIONS 0 #endif -// Checks if decltype v1.1 is supported -// http://en.cppreference.com/w/cpp/compiler_support -#define FMT_HAS_DECLTYPE_INCOMPLETE_RETURN_TYPES \ - (FMT_HAS_FEATURE(cxx_decltype_incomplete_return_types) || \ - (FMT_GCC_VERSION >= 407 && FMT_HAS_GXX_CXX11) || \ - FMT_MSC_VER >= 1900 || \ - FMT_ICC_VERSION >= 1200) - -#ifdef FMT_HEADER_ONLY -// If header only do not use extern templates. -# undef FMT_USE_EXTERN_TEMPLATES -# define FMT_USE_EXTERN_TEMPLATES 0 +// __builtin_clz is broken in clang with Microsoft CodeGen: +// https://github.com/fmtlib/fmt/issues/519 +#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clz)) && !FMT_MSC_VER +# define FMT_BUILTIN_CLZ(n) __builtin_clz(n) #endif - -#ifndef FMT_ASSERT -# define FMT_ASSERT(condition, message) assert((condition) && message) +#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clzll)) && !FMT_MSC_VER +# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) #endif - -#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clz) -# define FMT_BUILTIN_CLZ(n) __builtin_clz(n) +#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_ctz)) +# define FMT_BUILTIN_CTZ(n) __builtin_ctz(n) #endif - -#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clzll) -# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) +#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_ctzll)) +# define FMT_BUILTIN_CTZLL(n) __builtin_ctzll(n) #endif -// Some compilers masquerade as both MSVC and GCC-likes or -// otherwise support __builtin_clz and __builtin_clzll, so -// only define FMT_BUILTIN_CLZ using the MSVC intrinsics -// if the clz and clzll builtins are not available. -#if FMT_MSC_VER && !defined(FMT_BUILTIN_CLZLL) && !defined(_MANAGED) -# include // _BitScanReverse, _BitScanReverse64 - -namespace fmt { -namespace internal { -# pragma intrinsic(_BitScanReverse) -inline uint32_t clz(uint32_t x) { +#if FMT_MSC_VER +# include // _BitScanReverse[64], _BitScanForward[64], _umul128 +#endif + +// Some compilers masquerade as both MSVC and GCC-likes or otherwise support +// __builtin_clz and __builtin_clzll, so only define FMT_BUILTIN_CLZ using the +// MSVC intrinsics if the clz and clzll builtins are not available. +#if FMT_MSC_VER && !defined(FMT_BUILTIN_CLZLL) && \ + !defined(FMT_BUILTIN_CTZLL) && !defined(_MANAGED) +FMT_BEGIN_NAMESPACE +namespace detail { +// Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning. +# ifndef __clang__ +# pragma intrinsic(_BitScanForward) +# pragma intrinsic(_BitScanReverse) +# endif +# if defined(_WIN64) && !defined(__clang__) +# pragma intrinsic(_BitScanForward64) +# pragma intrinsic(_BitScanReverse64) +# endif + +inline int clz(uint32_t x) { unsigned long r = 0; _BitScanReverse(&r, x); - - assert(x != 0); + FMT_ASSERT(x != 0, ""); // Static analysis complains about using uninitialized data // "r", but the only way that can happen is if "x" is 0, // which the callers guarantee to not happen. -# pragma warning(suppress: 6102) - return 31 - r; + FMT_SUPPRESS_MSC_WARNING(6102) + return 31 ^ static_cast(r); } -# define FMT_BUILTIN_CLZ(n) fmt::internal::clz(n) - -# ifdef _WIN64 -# pragma intrinsic(_BitScanReverse64) -# endif +# define FMT_BUILTIN_CLZ(n) detail::clz(n) -inline uint32_t clzll(uint64_t x) { +inline int clzll(uint64_t x) { unsigned long r = 0; -# ifdef _WIN64 +# ifdef _WIN64 _BitScanReverse64(&r, x); -# else +# else // Scan the high 32 bits. - if (_BitScanReverse(&r, static_cast(x >> 32))) - return 63 - (r + 32); - + if (_BitScanReverse(&r, static_cast(x >> 32))) return 63 ^ (r + 32); // Scan the low 32 bits. _BitScanReverse(&r, static_cast(x)); -# endif - - assert(x != 0); - // Static analysis complains about using uninitialized data - // "r", but the only way that can happen is if "x" is 0, - // which the callers guarantee to not happen. -# pragma warning(suppress: 6102) - return 63 - r; +# endif + FMT_ASSERT(x != 0, ""); + FMT_SUPPRESS_MSC_WARNING(6102) // Suppress a bogus static analysis warning. + return 63 ^ static_cast(r); } -# define FMT_BUILTIN_CLZLL(n) fmt::internal::clzll(n) +# define FMT_BUILTIN_CLZLL(n) detail::clzll(n) + +inline int ctz(uint32_t x) { + unsigned long r = 0; + _BitScanForward(&r, x); + FMT_ASSERT(x != 0, ""); + FMT_SUPPRESS_MSC_WARNING(6102) // Suppress a bogus static analysis warning. + return static_cast(r); } +# define FMT_BUILTIN_CTZ(n) detail::ctz(n) + +inline int ctzll(uint64_t x) { + unsigned long r = 0; + FMT_ASSERT(x != 0, ""); + FMT_SUPPRESS_MSC_WARNING(6102) // Suppress a bogus static analysis warning. +# ifdef _WIN64 + _BitScanForward64(&r, x); +# else + // Scan the low 32 bits. + if (_BitScanForward(&r, static_cast(x))) return static_cast(r); + // Scan the high 32 bits. + _BitScanForward(&r, static_cast(x >> 32)); + r += 32; +# endif + return static_cast(r); } +# define FMT_BUILTIN_CTZLL(n) detail::ctzll(n) +} // namespace detail +FMT_END_NAMESPACE #endif -namespace fmt { -namespace internal { -struct DummyInt { - int data[2]; - operator int() const { return 0; } -}; -typedef std::numeric_limits FPUtil; - -// Dummy implementations of system functions such as signbit and ecvt called -// if the latter are not available. -inline DummyInt signbit(...) { return DummyInt(); } -inline DummyInt _ecvt_s(...) { return DummyInt(); } -inline DummyInt isinf(...) { return DummyInt(); } -inline DummyInt _finite(...) { return DummyInt(); } -inline DummyInt isnan(...) { return DummyInt(); } -inline DummyInt _isnan(...) { return DummyInt(); } - -// A helper function to suppress bogus "conditional expression is constant" -// warnings. -template -inline T const_check(T value) { return value; } +// Enable the deprecated numeric alignment. +#ifndef FMT_DEPRECATED_NUMERIC_ALIGN +# define FMT_DEPRECATED_NUMERIC_ALIGN 0 +#endif + +FMT_BEGIN_NAMESPACE +namespace detail { + +// An equivalent of `*reinterpret_cast(&source)` that doesn't have +// undefined behavior (e.g. due to type aliasing). +// Example: uint64_t d = bit_cast(2.718); +template +inline Dest bit_cast(const Source& source) { + static_assert(sizeof(Dest) == sizeof(Source), "size mismatch"); + Dest dest; + std::memcpy(&dest, &source, sizeof(dest)); + return dest; } -} // namespace fmt -namespace std { -// Standard permits specialization of std::numeric_limits. This specialization -// is used to resolve ambiguity between isinf and std::isinf in glibc: -// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=48891 -// and the same for isnan and signbit. -template <> -class numeric_limits : - public std::numeric_limits { - public: - // Portable version of isinf. - template - static bool isinfinity(T x) { - using namespace fmt::internal; - // The resolution "priority" is: - // isinf macro > std::isinf > ::isinf > fmt::internal::isinf - if (const_check(sizeof(isinf(x)) == sizeof(bool) || - sizeof(isinf(x)) == sizeof(int))) { - return isinf(x) != 0; - } - return !_finite(static_cast(x)); - } +inline bool is_big_endian() { + const auto u = 1u; + struct bytes { + char data[sizeof(u)]; + }; + return bit_cast(u).data[0] == 0; +} - // Portable version of isnan. - template - static bool isnotanumber(T x) { - using namespace fmt::internal; - if (const_check(sizeof(isnan(x)) == sizeof(bool) || - sizeof(isnan(x)) == sizeof(int))) { - return isnan(x) != 0; - } - return _isnan(static_cast(x)) != 0; - } +// A fallback implementation of uintptr_t for systems that lack it. +struct fallback_uintptr { + unsigned char value[sizeof(void*)]; - // Portable version of signbit. - static bool isnegative(double x) { - using namespace fmt::internal; - if (const_check(sizeof(signbit(x)) == sizeof(bool) || - sizeof(signbit(x)) == sizeof(int))) { - return signbit(x) != 0; + fallback_uintptr() = default; + explicit fallback_uintptr(const void* p) { + *this = bit_cast(p); + if (is_big_endian()) { + for (size_t i = 0, j = sizeof(void*) - 1; i < j; ++i, --j) + std::swap(value[i], value[j]); } - if (x < 0) return true; - if (!isnotanumber(x)) return false; - int dec = 0, sign = 0; - char buffer[2]; // The buffer size must be >= 2 or _ecvt_s will fail. - _ecvt_s(buffer, sizeof(buffer), x, 0, &dec, &sign); - return sign != 0; } }; -} // namespace std - -namespace fmt { - -// Fix the warning about long long on older versions of GCC -// that don't support the diagnostic pragma. -FMT_GCC_EXTENSION typedef long long LongLong; -FMT_GCC_EXTENSION typedef unsigned long long ULongLong; - -#if FMT_USE_RVALUE_REFERENCES -using std::move; +#ifdef UINTPTR_MAX +using uintptr_t = ::uintptr_t; +inline uintptr_t to_uintptr(const void* p) { return bit_cast(p); } +#else +using uintptr_t = fallback_uintptr; +inline fallback_uintptr to_uintptr(const void* p) { + return fallback_uintptr(p); +} #endif -template -class BasicWriter; +// Returns the largest possible value for type T. Same as +// std::numeric_limits::max() but shorter and not affected by the max macro. +template constexpr T max_value() { + return (std::numeric_limits::max)(); +} +template constexpr int num_bits() { + return std::numeric_limits::digits; +} +// std::numeric_limits::digits may return 0 for 128-bit ints. +template <> constexpr int num_bits() { return 128; } +template <> constexpr int num_bits() { return 128; } +template <> constexpr int num_bits() { + return static_cast(sizeof(void*) * + std::numeric_limits::digits); +} -typedef BasicWriter Writer; -typedef BasicWriter WWriter; +FMT_INLINE void assume(bool condition) { + (void)condition; +#if FMT_HAS_BUILTIN(__builtin_assume) + __builtin_assume(condition); +#endif +} -template -class ArgFormatter; +// An approximation of iterator_t for pre-C++20 systems. +template +using iterator_t = decltype(std::begin(std::declval())); +template using sentinel_t = decltype(std::end(std::declval())); -struct FormatSpec; +// A workaround for std::string not having mutable data() until C++17. +template inline Char* get_data(std::basic_string& s) { + return &s[0]; +} +template +inline typename Container::value_type* get_data(Container& c) { + return c.data(); +} -template -class BasicPrintfArgFormatter; +#if defined(_SECURE_SCL) && _SECURE_SCL +// Make a checked iterator to avoid MSVC warnings. +template using checked_ptr = stdext::checked_array_iterator; +template checked_ptr make_checked(T* p, size_t size) { + return {p, size}; +} +#else +template using checked_ptr = T*; +template inline T* make_checked(T* p, size_t) { return p; } +#endif -template > -class BasicFormatter; +template ::value)> +#if FMT_CLANG_VERSION +__attribute__((no_sanitize("undefined"))) +#endif +inline checked_ptr +reserve(std::back_insert_iterator it, size_t n) { + Container& c = get_container(it); + size_t size = c.size(); + c.resize(size + n); + return make_checked(get_data(c) + size, n); +} -/** - \rst - A string reference. It can be constructed from a C string or - ``std::basic_string``. +template +inline buffer_appender reserve(buffer_appender it, size_t n) { + buffer& buf = get_container(it); + buf.try_reserve(buf.size() + n); + return it; +} - You can use one of the following typedefs for common character types: +template inline Iterator& reserve(Iterator& it, size_t) { + return it; +} - +------------+-------------------------+ - | Type | Definition | - +============+=========================+ - | StringRef | BasicStringRef | - +------------+-------------------------+ - | WStringRef | BasicStringRef | - +------------+-------------------------+ +template +constexpr T* to_pointer(OutputIt, size_t) { + return nullptr; +} +template T* to_pointer(buffer_appender it, size_t n) { + buffer& buf = get_container(it); + auto size = buf.size(); + if (buf.capacity() < size + n) return nullptr; + buf.try_resize(size + n); + return buf.data() + size; +} - This class is most useful as a parameter type to allow passing - different types of strings to a function, for example:: +template ::value)> +inline std::back_insert_iterator base_iterator( + std::back_insert_iterator& it, + checked_ptr) { + return it; +} - template - std::string format(StringRef format_str, const Args & ... args); +template +inline Iterator base_iterator(Iterator, Iterator it) { + return it; +} - format("{}", 42); - format(std::string("{}"), 42); - \endrst - */ -template -class BasicStringRef { +// An output iterator that counts the number of objects written to it and +// discards them. +class counting_iterator { private: - const Char *data_; - std::size_t size_; + size_t count_; public: - /** Constructs a string reference object from a C string and a size. */ - BasicStringRef(const Char *s, std::size_t size) : data_(s), size_(size) {} + using iterator_category = std::output_iterator_tag; + using difference_type = std::ptrdiff_t; + using pointer = void; + using reference = void; + using _Unchecked_type = counting_iterator; // Mark iterator as checked. + + struct value_type { + template void operator=(const T&) {} + }; - /** - \rst - Constructs a string reference object from a C string computing - the size with ``std::char_traits::length``. - \endrst - */ - BasicStringRef(const Char *s) - : data_(s), size_(std::char_traits::length(s)) {} + counting_iterator() : count_(0) {} - /** - \rst - Constructs a string reference from a ``std::basic_string`` object. - \endrst - */ - template - BasicStringRef( - const std::basic_string, Allocator> &s) - : data_(s.c_str()), size_(s.size()) {} + size_t count() const { return count_; } - /** - \rst - Converts a string reference to an ``std::string`` object. - \endrst - */ - std::basic_string to_string() const { - return std::basic_string(data_, size_); + counting_iterator& operator++() { + ++count_; + return *this; } - - /** Returns a pointer to the string data. */ - const Char *data() const { return data_; } - - /** Returns the string size. */ - std::size_t size() const { return size_; } - - // Lexicographically compare this string reference to other. - int compare(BasicStringRef other) const { - std::size_t size = size_ < other.size_ ? size_ : other.size_; - int result = std::char_traits::compare(data_, other.data_, size); - if (result == 0) - result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1); - return result; + counting_iterator operator++(int) { + auto it = *this; + ++*this; + return it; } - friend bool operator==(BasicStringRef lhs, BasicStringRef rhs) { - return lhs.compare(rhs) == 0; - } - friend bool operator!=(BasicStringRef lhs, BasicStringRef rhs) { - return lhs.compare(rhs) != 0; - } - friend bool operator<(BasicStringRef lhs, BasicStringRef rhs) { - return lhs.compare(rhs) < 0; - } - friend bool operator<=(BasicStringRef lhs, BasicStringRef rhs) { - return lhs.compare(rhs) <= 0; - } - friend bool operator>(BasicStringRef lhs, BasicStringRef rhs) { - return lhs.compare(rhs) > 0; - } - friend bool operator>=(BasicStringRef lhs, BasicStringRef rhs) { - return lhs.compare(rhs) >= 0; + friend counting_iterator operator+(counting_iterator it, difference_type n) { + it.count_ += static_cast(n); + return it; } + + value_type operator*() const { return {}; } }; -typedef BasicStringRef StringRef; -typedef BasicStringRef WStringRef; +template class truncating_iterator_base { + protected: + OutputIt out_; + size_t limit_; + size_t count_; -/** - \rst - A reference to a null terminated string. It can be constructed from a C - string or ``std::basic_string``. + truncating_iterator_base(OutputIt out, size_t limit) + : out_(out), limit_(limit), count_(0) {} - You can use one of the following typedefs for common character types: + public: + using iterator_category = std::output_iterator_tag; + using value_type = typename std::iterator_traits::value_type; + using difference_type = void; + using pointer = void; + using reference = void; + using _Unchecked_type = + truncating_iterator_base; // Mark iterator as checked. + + OutputIt base() const { return out_; } + size_t count() const { return count_; } +}; - +-------------+--------------------------+ - | Type | Definition | - +=============+==========================+ - | CStringRef | BasicCStringRef | - +-------------+--------------------------+ - | WCStringRef | BasicCStringRef | - +-------------+--------------------------+ +// An output iterator that truncates the output and counts the number of objects +// written to it. +template ::value_type>::type> +class truncating_iterator; - This class is most useful as a parameter type to allow passing - different types of strings to a function, for example:: +template +class truncating_iterator + : public truncating_iterator_base { + mutable typename truncating_iterator_base::value_type blackhole_; - template - std::string format(CStringRef format_str, const Args & ... args); + public: + using value_type = typename truncating_iterator_base::value_type; - format("{}", 42); - format(std::string("{}"), 42); - \endrst - */ -template -class BasicCStringRef { - private: - const Char *data_; + truncating_iterator(OutputIt out, size_t limit) + : truncating_iterator_base(out, limit) {} - public: - /** Constructs a string reference object from a C string. */ - BasicCStringRef(const Char *s) : data_(s) {} + truncating_iterator& operator++() { + if (this->count_++ < this->limit_) ++this->out_; + return *this; + } - /** - \rst - Constructs a string reference from a ``std::basic_string`` object. - \endrst - */ - template - BasicCStringRef( - const std::basic_string, Allocator> &s) - : data_(s.c_str()) {} + truncating_iterator operator++(int) { + auto it = *this; + ++*this; + return it; + } - /** Returns the pointer to a C string. */ - const Char *c_str() const { return data_; } + value_type& operator*() const { + return this->count_ < this->limit_ ? *this->out_ : blackhole_; + } }; -typedef BasicCStringRef CStringRef; -typedef BasicCStringRef WCStringRef; - -/** A formatting error such as invalid format string. */ -class FormatError : public std::runtime_error { +template +class truncating_iterator + : public truncating_iterator_base { public: - explicit FormatError(CStringRef message) - : std::runtime_error(message.c_str()) {} - FormatError(const FormatError &ferr) : std::runtime_error(ferr) {} - FMT_API ~FormatError() FMT_DTOR_NOEXCEPT; + truncating_iterator(OutputIt out, size_t limit) + : truncating_iterator_base(out, limit) {} + + template truncating_iterator& operator=(T val) { + if (this->count_++ < this->limit_) *this->out_++ = val; + return *this; + } + + truncating_iterator& operator++() { return *this; } + truncating_iterator& operator++(int) { return *this; } + truncating_iterator& operator*() { return *this; } }; -namespace internal { +template +inline size_t count_code_points(basic_string_view s) { + return s.size(); +} -// MakeUnsigned::Type gives an unsigned type corresponding to integer type T. -template -struct MakeUnsigned { typedef T Type; }; +// Counts the number of code points in a UTF-8 string. +inline size_t count_code_points(basic_string_view s) { + const char* data = s.data(); + size_t num_code_points = 0; + for (size_t i = 0, size = s.size(); i != size; ++i) { + if ((data[i] & 0xc0) != 0x80) ++num_code_points; + } + return num_code_points; +} -#define FMT_SPECIALIZE_MAKE_UNSIGNED(T, U) \ - template <> \ - struct MakeUnsigned { typedef U Type; } +inline size_t count_code_points(basic_string_view s) { + return count_code_points(basic_string_view( + reinterpret_cast(s.data()), s.size())); +} -FMT_SPECIALIZE_MAKE_UNSIGNED(char, unsigned char); -FMT_SPECIALIZE_MAKE_UNSIGNED(signed char, unsigned char); -FMT_SPECIALIZE_MAKE_UNSIGNED(short, unsigned short); -FMT_SPECIALIZE_MAKE_UNSIGNED(int, unsigned); -FMT_SPECIALIZE_MAKE_UNSIGNED(long, unsigned long); -FMT_SPECIALIZE_MAKE_UNSIGNED(LongLong, ULongLong); +template +inline size_t code_point_index(basic_string_view s, size_t n) { + size_t size = s.size(); + return n < size ? n : size; +} -// Casts nonnegative integer to unsigned. -template -inline typename MakeUnsigned::Type to_unsigned(Int value) { - FMT_ASSERT(value >= 0, "negative value"); - return static_cast::Type>(value); +// Calculates the index of the nth code point in a UTF-8 string. +inline size_t code_point_index(basic_string_view s, size_t n) { + const char8_type* data = s.data(); + size_t num_code_points = 0; + for (size_t i = 0, size = s.size(); i != size; ++i) { + if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) { + return i; + } + } + return s.size(); } -// The number of characters to store in the MemoryBuffer object itself -// to avoid dynamic memory allocation. -enum { INLINE_BUFFER_SIZE = 500 }; +template +using needs_conversion = bool_constant< + std::is_same::value_type, + char>::value && + std::is_same::value>; -#if FMT_SECURE_SCL -// Use checked iterator to avoid warnings on MSVC. -template -inline stdext::checked_array_iterator make_ptr(T *ptr, std::size_t size) { - return stdext::checked_array_iterator(ptr, size); +template ::value)> +OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) { + return std::copy(begin, end, it); } -#else -template -inline T *make_ptr(T *ptr, std::size_t) { return ptr; } -#endif -} // namespace internal -/** - \rst - A buffer supporting a subset of ``std::vector``'s operations. - \endrst - */ -template -class Buffer { - private: - FMT_DISALLOW_COPY_AND_ASSIGN(Buffer); +template ::value)> +OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) { + return std::transform(begin, end, it, + [](char c) { return static_cast(c); }); +} - protected: - T *ptr_; - std::size_t size_; - std::size_t capacity_; +template +inline counting_iterator copy_str(InputIt begin, InputIt end, + counting_iterator it) { + return it + (end - begin); +} - Buffer(T *ptr = FMT_NULL, std::size_t capacity = 0) - : ptr_(ptr), size_(0), capacity_(capacity) {} +template +using is_fast_float = bool_constant::is_iec559 && + sizeof(T) <= sizeof(double)>; - /** - \rst - Increases the buffer capacity to hold at least *size* elements updating - ``ptr_`` and ``capacity_``. - \endrst - */ - virtual void grow(std::size_t size) = 0; +#ifndef FMT_USE_FULL_CACHE_DRAGONBOX +# define FMT_USE_FULL_CACHE_DRAGONBOX 0 +#endif - public: - virtual ~Buffer() {} +template +template +void buffer::append(const U* begin, const U* end) { + do { + auto count = to_unsigned(end - begin); + try_reserve(size_ + count); + auto free_cap = capacity_ - size_; + if (free_cap < count) count = free_cap; + std::uninitialized_copy_n(begin, count, make_checked(ptr_ + size_, count)); + size_ += count; + begin += count; + } while (begin != end); +} - /** Returns the size of this buffer. */ - std::size_t size() const { return size_; } +template +void iterator_buffer::flush() { + out_ = std::copy_n(data_, this->limit(this->size()), out_); + this->clear(); +} +} // namespace detail - /** Returns the capacity of this buffer. */ - std::size_t capacity() const { return capacity_; } +// The number of characters to store in the basic_memory_buffer object itself +// to avoid dynamic memory allocation. +enum { inline_buffer_size = 500 }; - /** - Resizes the buffer. If T is a POD type new elements may not be initialized. - */ - void resize(std::size_t new_size) { - if (new_size > capacity_) - grow(new_size); - size_ = new_size; - } +/** + \rst + A dynamically growing memory buffer for trivially copyable/constructible types + with the first ``SIZE`` elements stored in the object itself. - /** - \rst - Reserves space to store at least *capacity* elements. - \endrst - */ - void reserve(std::size_t capacity) { - if (capacity > capacity_) - grow(capacity); - } + You can use one of the following type aliases for common character types: - void clear() FMT_NOEXCEPT { size_ = 0; } + +----------------+------------------------------+ + | Type | Definition | + +================+==============================+ + | memory_buffer | basic_memory_buffer | + +----------------+------------------------------+ + | wmemory_buffer | basic_memory_buffer | + +----------------+------------------------------+ - void push_back(const T &value) { - if (size_ == capacity_) - grow(size_ + 1); - ptr_[size_++] = value; - } + **Example**:: - /** Appends data to the end of the buffer. */ - template - void append(const U *begin, const U *end); + fmt::memory_buffer out; + format_to(out, "The answer is {}.", 42); - T &operator[](std::size_t index) { return ptr_[index]; } - const T &operator[](std::size_t index) const { return ptr_[index]; } -}; + This will append the following output to the ``out`` object: -template -template -void Buffer::append(const U *begin, const U *end) { - std::size_t new_size = size_ + internal::to_unsigned(end - begin); - if (new_size > capacity_) - grow(new_size); - std::uninitialized_copy(begin, end, - internal::make_ptr(ptr_, capacity_) + size_); - size_ = new_size; -} + .. code-block:: none -namespace internal { + The answer is 42. -// A memory buffer for trivially copyable/constructible types with the first -// SIZE elements stored in the object itself. -template > -class MemoryBuffer : private Allocator, public Buffer { + The output can be converted to an ``std::string`` with ``to_string(out)``. + \endrst + */ +template > +class basic_memory_buffer final : public detail::buffer { private: - T data_[SIZE]; + T store_[SIZE]; + + // Don't inherit from Allocator avoid generating type_info for it. + Allocator alloc_; // Deallocate memory allocated by the buffer. void deallocate() { - if (this->ptr_ != data_) Allocator::deallocate(this->ptr_, this->capacity_); + T* data = this->data(); + if (data != store_) alloc_.deallocate(data, this->capacity()); } protected: - void grow(std::size_t size) FMT_OVERRIDE; + void grow(size_t size) final FMT_OVERRIDE; public: - explicit MemoryBuffer(const Allocator &alloc = Allocator()) - : Allocator(alloc), Buffer(data_, SIZE) {} - ~MemoryBuffer() { deallocate(); } + using value_type = T; + using const_reference = const T&; + + explicit basic_memory_buffer(const Allocator& alloc = Allocator()) + : alloc_(alloc) { + this->set(store_, SIZE); + } + ~basic_memory_buffer() { deallocate(); } -#if FMT_USE_RVALUE_REFERENCES private: // Move data from other to this buffer. - void move(MemoryBuffer &other) { - Allocator &this_alloc = *this, &other_alloc = other; - this_alloc = std::move(other_alloc); - this->size_ = other.size_; - this->capacity_ = other.capacity_; - if (other.ptr_ == other.data_) { - this->ptr_ = data_; - std::uninitialized_copy(other.data_, other.data_ + this->size_, - make_ptr(data_, this->capacity_)); + void move(basic_memory_buffer& other) { + alloc_ = std::move(other.alloc_); + T* data = other.data(); + size_t size = other.size(), capacity = other.capacity(); + if (data == other.store_) { + this->set(store_, capacity); + std::uninitialized_copy(other.store_, other.store_ + size, + detail::make_checked(store_, capacity)); } else { - this->ptr_ = other.ptr_; + this->set(data, capacity); // Set pointer to the inline array so that delete is not called // when deallocating. - other.ptr_ = other.data_; + other.set(other.store_, 0); } + this->resize(size); } public: - MemoryBuffer(MemoryBuffer &&other) { - move(other); - } + /** + \rst + Constructs a :class:`fmt::basic_memory_buffer` object moving the content + of the other object to it. + \endrst + */ + basic_memory_buffer(basic_memory_buffer&& other) FMT_NOEXCEPT { move(other); } - MemoryBuffer &operator=(MemoryBuffer &&other) { - assert(this != &other); + /** + \rst + Moves the content of the other ``basic_memory_buffer`` object to this one. + \endrst + */ + basic_memory_buffer& operator=(basic_memory_buffer&& other) FMT_NOEXCEPT { + FMT_ASSERT(this != &other, ""); deallocate(); move(other); return *this; } -#endif // Returns a copy of the allocator associated with this buffer. - Allocator get_allocator() const { return *this; } + Allocator get_allocator() const { return alloc_; } + + /** + Resizes the buffer to contain *count* elements. If T is a POD type new + elements may not be initialized. + */ + void resize(size_t count) { this->try_resize(count); } + + /** Increases the buffer capacity to *new_capacity*. */ + void reserve(size_t new_capacity) { this->try_reserve(new_capacity); } + + // Directly append data into the buffer + using detail::buffer::append; + template + void append(const ContiguousRange& range) { + append(range.data(), range.data() + range.size()); + } }; -template -void MemoryBuffer::grow(std::size_t size) { - std::size_t new_capacity = this->capacity_ + this->capacity_ / 2; - if (size > new_capacity) - new_capacity = size; - T *new_ptr = this->allocate(new_capacity, FMT_NULL); +template +void basic_memory_buffer::grow(size_t size) { +#ifdef FMT_FUZZ + if (size > 5000) throw std::runtime_error("fuzz mode - won't grow that much"); +#endif + size_t old_capacity = this->capacity(); + size_t new_capacity = old_capacity + old_capacity / 2; + if (size > new_capacity) new_capacity = size; + T* old_data = this->data(); + T* new_data = + std::allocator_traits::allocate(alloc_, new_capacity); // The following code doesn't throw, so the raw pointer above doesn't leak. - std::uninitialized_copy(this->ptr_, this->ptr_ + this->size_, - make_ptr(new_ptr, new_capacity)); - std::size_t old_capacity = this->capacity_; - T *old_ptr = this->ptr_; - this->capacity_ = new_capacity; - this->ptr_ = new_ptr; - // deallocate may throw (at least in principle), but it doesn't matter since - // the buffer already uses the new storage and will deallocate it in case - // of exception. - if (old_ptr != data_) - Allocator::deallocate(old_ptr, old_capacity); -} - -// A fixed-size buffer. -template -class FixedBuffer : public fmt::Buffer { - public: - FixedBuffer(Char *array, std::size_t size) : fmt::Buffer(array, size) {} + std::uninitialized_copy(old_data, old_data + this->size(), + detail::make_checked(new_data, new_capacity)); + this->set(new_data, new_capacity); + // deallocate must not throw according to the standard, but even if it does, + // the buffer already uses the new storage and will deallocate it in + // destructor. + if (old_data != store_) alloc_.deallocate(old_data, old_capacity); +} - protected: - FMT_API void grow(std::size_t size) FMT_OVERRIDE; +using memory_buffer = basic_memory_buffer; +using wmemory_buffer = basic_memory_buffer; + +template +struct is_contiguous> : std::true_type { }; -template -class BasicCharTraits { +/** A formatting error such as invalid format string. */ +FMT_CLASS_API +class FMT_API format_error : public std::runtime_error { public: -#if FMT_SECURE_SCL - typedef stdext::checked_array_iterator CharPtr; -#else - typedef Char *CharPtr; -#endif - static Char cast(int value) { return static_cast(value); } + explicit format_error(const char* message) : std::runtime_error(message) {} + explicit format_error(const std::string& message) + : std::runtime_error(message) {} + format_error(const format_error&) = default; + format_error& operator=(const format_error&) = default; + format_error(format_error&&) = default; + format_error& operator=(format_error&&) = default; + ~format_error() FMT_NOEXCEPT FMT_OVERRIDE; }; -template -class CharTraits; +namespace detail { -template <> -class CharTraits : public BasicCharTraits { - private: - // Conversion from wchar_t to char is not allowed. - static char convert(wchar_t); +template +using is_signed = + std::integral_constant::is_signed || + std::is_same::value>; - public: - static char convert(char value) { return value; } +// Returns true if value is negative, false otherwise. +// Same as `value < 0` but doesn't produce warnings if T is an unsigned type. +template ::value)> +FMT_CONSTEXPR bool is_negative(T value) { + return value < 0; +} +template ::value)> +FMT_CONSTEXPR bool is_negative(T) { + return false; +} - // Formats a floating-point number. - template - FMT_API static int format_float(char *buffer, std::size_t size, - const char *format, unsigned width, int precision, T value); -}; +template ::value)> +FMT_CONSTEXPR bool is_supported_floating_point(T) { + return (std::is_same::value && FMT_USE_FLOAT) || + (std::is_same::value && FMT_USE_DOUBLE) || + (std::is_same::value && FMT_USE_LONG_DOUBLE); +} -#if FMT_USE_EXTERN_TEMPLATES -extern template int CharTraits::format_float - (char *buffer, std::size_t size, - const char* format, unsigned width, int precision, double value); -extern template int CharTraits::format_float - (char *buffer, std::size_t size, - const char* format, unsigned width, int precision, long double value); -#endif +// Smallest of uint32_t, uint64_t, uint128_t that is large enough to +// represent all values of an integral type T. +template +using uint32_or_64_or_128_t = + conditional_t() <= 32 && !FMT_REDUCE_INT_INSTANTIATIONS, + uint32_t, + conditional_t() <= 64, uint64_t, uint128_t>>; -template <> -class CharTraits : public BasicCharTraits { - public: - static wchar_t convert(char value) { return value; } - static wchar_t convert(wchar_t value) { return value; } +// 128-bit integer type used internally +struct FMT_EXTERN_TEMPLATE_API uint128_wrapper { + uint128_wrapper() = default; - template - FMT_API static int format_float(wchar_t *buffer, std::size_t size, - const wchar_t *format, unsigned width, int precision, T value); -}; +#if FMT_USE_INT128 + uint128_t internal_; -#if FMT_USE_EXTERN_TEMPLATES -extern template int CharTraits::format_float - (wchar_t *buffer, std::size_t size, - const wchar_t* format, unsigned width, int precision, double value); -extern template int CharTraits::format_float - (wchar_t *buffer, std::size_t size, - const wchar_t* format, unsigned width, int precision, long double value); -#endif + uint128_wrapper(uint64_t high, uint64_t low) FMT_NOEXCEPT + : internal_{static_cast(low) | + (static_cast(high) << 64)} {} -// Checks if a number is negative - used to avoid warnings. -template -struct SignChecker { - template - static bool is_negative(T value) { return value < 0; } -}; + uint128_wrapper(uint128_t u) : internal_{u} {} -template <> -struct SignChecker { - template - static bool is_negative(T) { return false; } -}; + uint64_t high() const FMT_NOEXCEPT { return uint64_t(internal_ >> 64); } + uint64_t low() const FMT_NOEXCEPT { return uint64_t(internal_); } -// Returns true if value is negative, false otherwise. -// Same as (value < 0) but doesn't produce warnings if T is an unsigned type. -template -inline bool is_negative(T value) { - return SignChecker::is_signed>::is_negative(value); -} + uint128_wrapper& operator+=(uint64_t n) FMT_NOEXCEPT { + internal_ += n; + return *this; + } +#else + uint64_t high_; + uint64_t low_; -// Selects uint32_t if FitsIn32Bits is true, uint64_t otherwise. -template -struct TypeSelector { typedef uint32_t Type; }; + uint128_wrapper(uint64_t high, uint64_t low) FMT_NOEXCEPT : high_{high}, + low_{low} {} -template <> -struct TypeSelector { typedef uint64_t Type; }; + uint64_t high() const FMT_NOEXCEPT { return high_; } + uint64_t low() const FMT_NOEXCEPT { return low_; } -template -struct IntTraits { - // Smallest of uint32_t and uint64_t that is large enough to represent - // all values of T. - typedef typename - TypeSelector::digits <= 32>::Type MainType; + uint128_wrapper& operator+=(uint64_t n) FMT_NOEXCEPT { +# if defined(_MSC_VER) && defined(_M_X64) + unsigned char carry = _addcarry_u64(0, low_, n, &low_); + _addcarry_u64(carry, high_, 0, &high_); + return *this; +# else + uint64_t sum = low_ + n; + high_ += (sum < low_ ? 1 : 0); + low_ = sum; + return *this; +# endif + } +#endif }; -FMT_API void report_unknown_type(char code, const char *type); +// Table entry type for divisibility test used internally +template struct FMT_EXTERN_TEMPLATE_API divtest_table_entry { + T mod_inv; + T max_quotient; +}; -// Static data is placed in this class template to allow header-only -// configuration. -template -struct FMT_API BasicData { - static const uint32_t POWERS_OF_10_32[]; - static const uint64_t POWERS_OF_10_64[]; - static const char DIGITS[]; +// Static data is placed in this class template for the header-only config. +template struct FMT_EXTERN_TEMPLATE_API basic_data { + static const uint64_t powers_of_10_64[]; + static const uint32_t zero_or_powers_of_10_32_new[]; + static const uint64_t zero_or_powers_of_10_64_new[]; + static const uint64_t grisu_pow10_significands[]; + static const int16_t grisu_pow10_exponents[]; + static const divtest_table_entry divtest_table_for_pow5_32[]; + static const divtest_table_entry divtest_table_for_pow5_64[]; + static const uint64_t dragonbox_pow10_significands_64[]; + static const uint128_wrapper dragonbox_pow10_significands_128[]; + // log10(2) = 0x0.4d104d427de7fbcc... + static const uint64_t log10_2_significand = 0x4d104d427de7fbcc; +#if !FMT_USE_FULL_CACHE_DRAGONBOX + static const uint64_t powers_of_5_64[]; + static const uint32_t dragonbox_pow10_recovery_errors[]; +#endif + // GCC generates slightly better code for pairs than chars. + using digit_pair = char[2]; + static const digit_pair digits[]; + static const char hex_digits[]; + static const char foreground_color[]; + static const char background_color[]; + static const char reset_color[5]; + static const wchar_t wreset_color[5]; + static const char signs[]; + static const char left_padding_shifts[5]; + static const char right_padding_shifts[5]; + + // DEPRECATED! These are for ABI compatibility. + static const uint32_t zero_or_powers_of_10_32[]; + static const uint64_t zero_or_powers_of_10_64[]; }; -#if FMT_USE_EXTERN_TEMPLATES -extern template struct BasicData; +// Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)). +// This is a function instead of an array to workaround a bug in GCC10 (#1810). +FMT_INLINE uint16_t bsr2log10(int bsr) { + static constexpr uint16_t data[] = { + 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, + 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, + 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15, + 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20}; + return data[bsr]; +} + +#ifndef FMT_EXPORTED +FMT_EXTERN template struct basic_data; #endif -typedef BasicData<> Data; +// This is a struct rather than an alias to avoid shadowing warnings in gcc. +struct data : basic_data<> {}; #ifdef FMT_BUILTIN_CLZLL // Returns the number of decimal digits in n. Leading zeros are not counted // except for n == 0 in which case count_digits returns 1. -inline unsigned count_digits(uint64_t n) { - // Based on http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 - // and the benchmark https://github.com/localvoid/cxx-benchmark-count-digits. - int t = (64 - FMT_BUILTIN_CLZLL(n | 1)) * 1233 >> 12; - return to_unsigned(t) - (n < Data::POWERS_OF_10_64[t]) + 1; +inline int count_digits(uint64_t n) { + // https://github.com/fmtlib/format-benchmark/blob/master/digits10 + auto t = bsr2log10(FMT_BUILTIN_CLZLL(n | 1) ^ 63); + return t - (n < data::zero_or_powers_of_10_64_new[t]); } #else // Fallback version of count_digits used when __builtin_clz is not available. -inline unsigned count_digits(uint64_t n) { - unsigned count = 1; +inline int count_digits(uint64_t n) { + int count = 1; for (;;) { // Integer division is slow so do it for a group of four digits instead // of for every digit. The idea comes from the talk by Alexandrescu @@ -966,2439 +941,2444 @@ inline unsigned count_digits(uint64_t n) { } #endif +#if FMT_USE_INT128 +inline int count_digits(uint128_t n) { + int count = 1; + for (;;) { + // Integer division is slow so do it for a group of four digits instead + // of for every digit. The idea comes from the talk by Alexandrescu + // "Three Optimization Tips for C++". See speed-test for a comparison. + if (n < 10) return count; + if (n < 100) return count + 1; + if (n < 1000) return count + 2; + if (n < 10000) return count + 3; + n /= 10000U; + count += 4; + } +} +#endif + +// Counts the number of digits in n. BITS = log2(radix). +template inline int count_digits(UInt n) { + int num_digits = 0; + do { + ++num_digits; + } while ((n >>= BITS) != 0); + return num_digits; +} + +template <> int count_digits<4>(detail::fallback_uintptr n); + +#if FMT_GCC_VERSION || FMT_CLANG_VERSION +# define FMT_ALWAYS_INLINE inline __attribute__((always_inline)) +#elif FMT_MSC_VER +# define FMT_ALWAYS_INLINE __forceinline +#else +# define FMT_ALWAYS_INLINE inline +#endif + +// To suppress unnecessary security cookie checks +#if FMT_MSC_VER && !FMT_CLANG_VERSION +# define FMT_SAFEBUFFERS __declspec(safebuffers) +#else +# define FMT_SAFEBUFFERS +#endif + #ifdef FMT_BUILTIN_CLZ // Optional version of count_digits for better performance on 32-bit platforms. -inline unsigned count_digits(uint32_t n) { - int t = (32 - FMT_BUILTIN_CLZ(n | 1)) * 1233 >> 12; - return to_unsigned(t) - (n < Data::POWERS_OF_10_32[t]) + 1; +inline int count_digits(uint32_t n) { + auto t = bsr2log10(FMT_BUILTIN_CLZ(n | 1) ^ 31); + return t - (n < data::zero_or_powers_of_10_32_new[t]); } #endif -// A functor that doesn't add a thousands separator. -struct NoThousandsSep { - template - void operator()(Char *) {} -}; +template constexpr int digits10() FMT_NOEXCEPT { + return std::numeric_limits::digits10; +} +template <> constexpr int digits10() FMT_NOEXCEPT { return 38; } +template <> constexpr int digits10() FMT_NOEXCEPT { return 38; } -// A functor that adds a thousands separator. -class ThousandsSep { - private: - fmt::StringRef sep_; +template FMT_API std::string grouping_impl(locale_ref loc); +template inline std::string grouping(locale_ref loc) { + return grouping_impl(loc); +} +template <> inline std::string grouping(locale_ref loc) { + return grouping_impl(loc); +} - // Index of a decimal digit with the least significant digit having index 0. - unsigned digit_index_; +template FMT_API Char thousands_sep_impl(locale_ref loc); +template inline Char thousands_sep(locale_ref loc) { + return Char(thousands_sep_impl(loc)); +} +template <> inline wchar_t thousands_sep(locale_ref loc) { + return thousands_sep_impl(loc); +} - public: - explicit ThousandsSep(fmt::StringRef sep) : sep_(sep), digit_index_(0) {} +template FMT_API Char decimal_point_impl(locale_ref loc); +template inline Char decimal_point(locale_ref loc) { + return Char(decimal_point_impl(loc)); +} +template <> inline wchar_t decimal_point(locale_ref loc) { + return decimal_point_impl(loc); +} - template - void operator()(Char *&buffer) { - if (++digit_index_ % 3 != 0) - return; - buffer -= sep_.size(); - std::uninitialized_copy(sep_.data(), sep_.data() + sep_.size(), - internal::make_ptr(buffer, sep_.size())); - } +// Compares two characters for equality. +template bool equal2(const Char* lhs, const char* rhs) { + return lhs[0] == rhs[0] && lhs[1] == rhs[1]; +} +inline bool equal2(const char* lhs, const char* rhs) { + return memcmp(lhs, rhs, 2) == 0; +} + +// Copies two characters from src to dst. +template void copy2(Char* dst, const char* src) { + *dst++ = static_cast(*src++); + *dst = static_cast(*src); +} +FMT_INLINE void copy2(char* dst, const char* src) { memcpy(dst, src, 2); } + +template struct format_decimal_result { + Iterator begin; + Iterator end; }; -// Formats a decimal unsigned integer value writing into buffer. -// thousands_sep is a functor that is called after writing each char to -// add a thousands separator if necessary. -template -inline void format_decimal(Char *buffer, UInt value, unsigned num_digits, - ThousandsSep thousands_sep) { - buffer += num_digits; +// Formats a decimal unsigned integer value writing into out pointing to a +// buffer of specified size. The caller must ensure that the buffer is large +// enough. +template +inline format_decimal_result format_decimal(Char* out, UInt value, + int size) { + FMT_ASSERT(size >= count_digits(value), "invalid digit count"); + out += size; + Char* end = out; while (value >= 100) { // Integer division is slow so do it for a group of two digits instead // of for every digit. The idea comes from the talk by Alexandrescu // "Three Optimization Tips for C++". See speed-test for a comparison. - unsigned index = static_cast((value % 100) * 2); + out -= 2; + copy2(out, data::digits[value % 100]); value /= 100; - *--buffer = Data::DIGITS[index + 1]; - thousands_sep(buffer); - *--buffer = Data::DIGITS[index]; - thousands_sep(buffer); } if (value < 10) { - *--buffer = static_cast('0' + value); - return; + *--out = static_cast('0' + value); + return {out, end}; } - unsigned index = static_cast(value * 2); - *--buffer = Data::DIGITS[index + 1]; - thousands_sep(buffer); - *--buffer = Data::DIGITS[index]; + out -= 2; + copy2(out, data::digits[value]); + return {out, end}; } -template -inline void format_decimal(Char *buffer, UInt value, unsigned num_digits) { - format_decimal(buffer, value, num_digits, NoThousandsSep()); - return; +template >::value)> +inline format_decimal_result format_decimal(Iterator out, UInt value, + int size) { + // Buffer is large enough to hold all digits (digits10 + 1). + Char buffer[digits10() + 1]; + auto end = format_decimal(buffer, value, size).end; + return {out, detail::copy_str(buffer, end, out)}; } -#ifndef _WIN32 -# define FMT_USE_WINDOWS_H 0 -#elif !defined(FMT_USE_WINDOWS_H) -# define FMT_USE_WINDOWS_H 1 -#endif +template +inline Char* format_uint(Char* buffer, UInt value, int num_digits, + bool upper = false) { + buffer += num_digits; + Char* end = buffer; + do { + const char* digits = upper ? "0123456789ABCDEF" : data::hex_digits; + unsigned digit = (value & ((1 << BASE_BITS) - 1)); + *--buffer = static_cast(BASE_BITS < 4 ? static_cast('0' + digit) + : digits[digit]); + } while ((value >>= BASE_BITS) != 0); + return end; +} + +template +Char* format_uint(Char* buffer, detail::fallback_uintptr n, int num_digits, + bool = false) { + auto char_digits = std::numeric_limits::digits / 4; + int start = (num_digits + char_digits - 1) / char_digits - 1; + if (int start_digits = num_digits % char_digits) { + unsigned value = n.value[start--]; + buffer = format_uint(buffer, value, start_digits); + } + for (; start >= 0; --start) { + unsigned value = n.value[start]; + buffer += char_digits; + auto p = buffer; + for (int i = 0; i < char_digits; ++i) { + unsigned digit = (value & ((1 << BASE_BITS) - 1)); + *--p = static_cast(data::hex_digits[digit]); + value >>= BASE_BITS; + } + } + return buffer; +} + +template +inline It format_uint(It out, UInt value, int num_digits, bool upper = false) { + if (auto ptr = to_pointer(out, to_unsigned(num_digits))) { + format_uint(ptr, value, num_digits, upper); + return out; + } + // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1). + char buffer[num_bits() / BASE_BITS + 1]; + format_uint(buffer, value, num_digits, upper); + return detail::copy_str(buffer, buffer + num_digits, out); +} -// Define FMT_USE_WINDOWS_H to 0 to disable use of windows.h. -// All the functionality that relies on it will be disabled too. -#if FMT_USE_WINDOWS_H // A converter from UTF-8 to UTF-16. -// It is only provided for Windows since other systems support UTF-8 natively. -class UTF8ToUTF16 { +class utf8_to_utf16 { private: - MemoryBuffer buffer_; + wmemory_buffer buffer_; public: - FMT_API explicit UTF8ToUTF16(StringRef s); - operator WStringRef() const { return WStringRef(&buffer_[0], size()); } + FMT_API explicit utf8_to_utf16(string_view s); + operator wstring_view() const { return {&buffer_[0], size()}; } size_t size() const { return buffer_.size() - 1; } - const wchar_t *c_str() const { return &buffer_[0]; } - std::wstring str() const { return std::wstring(&buffer_[0], size()); } + const wchar_t* c_str() const { return &buffer_[0]; } + std::wstring str() const { return {&buffer_[0], size()}; } }; -// A converter from UTF-16 to UTF-8. -// It is only provided for Windows since other systems support UTF-8 natively. -class UTF16ToUTF8 { +template struct null {}; + +// Workaround an array initialization issue in gcc 4.8. +template struct fill_t { private: - MemoryBuffer buffer_; + enum { max_size = 4 }; + Char data_[max_size] = {Char(' '), Char(0), Char(0), Char(0)}; + unsigned char size_ = 1; public: - UTF16ToUTF8() {} - FMT_API explicit UTF16ToUTF8(WStringRef s); - operator StringRef() const { return StringRef(&buffer_[0], size()); } - size_t size() const { return buffer_.size() - 1; } - const char *c_str() const { return &buffer_[0]; } - std::string str() const { return std::string(&buffer_[0], size()); } - - // Performs conversion returning a system error code instead of - // throwing exception on conversion error. This method may still throw - // in case of memory allocation error. - FMT_API int convert(WStringRef s); -}; + FMT_CONSTEXPR void operator=(basic_string_view s) { + auto size = s.size(); + if (size > max_size) { + FMT_THROW(format_error("invalid fill")); + return; + } + for (size_t i = 0; i < size; ++i) data_[i] = s[i]; + size_ = static_cast(size); + } -FMT_API void format_windows_error(fmt::Writer &out, int error_code, - fmt::StringRef message) FMT_NOEXCEPT; -#endif + size_t size() const { return size_; } + const Char* data() const { return data_; } -// A formatting argument value. -struct Value { - template - struct StringValue { - const Char *value; - std::size_t size; - }; + FMT_CONSTEXPR Char& operator[](size_t index) { return data_[index]; } + FMT_CONSTEXPR const Char& operator[](size_t index) const { + return data_[index]; + } +}; +} // namespace detail - typedef void (*FormatFunc)( - void *formatter, const void *arg, void *format_str_ptr); +// We cannot use enum classes as bit fields because of a gcc bug +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414. +namespace align { +enum type { none, left, right, center, numeric }; +} +using align_t = align::type; - struct CustomValue { - const void *value; - FormatFunc format; - }; +namespace sign { +enum type { none, minus, plus, space }; +} +using sign_t = sign::type; + +// Format specifiers for built-in and string types. +template struct basic_format_specs { + int width; + int precision; + char type; + align_t align : 4; + sign_t sign : 3; + bool alt : 1; // Alternate form ('#'). + detail::fill_t fill; + + constexpr basic_format_specs() + : width(0), + precision(-1), + type(0), + align(align::none), + sign(sign::none), + alt(false) {} +}; - union { - int int_value; - unsigned uint_value; - LongLong long_long_value; - ULongLong ulong_long_value; - double double_value; - long double long_double_value; - const void *pointer; - StringValue string; - StringValue sstring; - StringValue ustring; - StringValue wstring; - CustomValue custom; - }; +using format_specs = basic_format_specs; + +namespace detail { +namespace dragonbox { + +// Type-specific information that Dragonbox uses. +template struct float_info; + +template <> struct float_info { + using carrier_uint = uint32_t; + static const int significand_bits = 23; + static const int exponent_bits = 8; + static const int min_exponent = -126; + static const int max_exponent = 127; + static const int exponent_bias = -127; + static const int decimal_digits = 9; + static const int kappa = 1; + static const int big_divisor = 100; + static const int small_divisor = 10; + static const int min_k = -31; + static const int max_k = 46; + static const int cache_bits = 64; + static const int divisibility_check_by_5_threshold = 39; + static const int case_fc_pm_half_lower_threshold = -1; + static const int case_fc_pm_half_upper_threshold = 6; + static const int case_fc_lower_threshold = -2; + static const int case_fc_upper_threshold = 6; + static const int case_shorter_interval_left_endpoint_lower_threshold = 2; + static const int case_shorter_interval_left_endpoint_upper_threshold = 3; + static const int shorter_interval_tie_lower_threshold = -35; + static const int shorter_interval_tie_upper_threshold = -35; + static const int max_trailing_zeros = 7; +}; - enum Type { - NONE, NAMED_ARG, - // Integer types should go first, - INT, UINT, LONG_LONG, ULONG_LONG, BOOL, CHAR, LAST_INTEGER_TYPE = CHAR, - // followed by floating-point types. - DOUBLE, LONG_DOUBLE, LAST_NUMERIC_TYPE = LONG_DOUBLE, - CSTRING, STRING, WSTRING, POINTER, CUSTOM - }; +template <> struct float_info { + using carrier_uint = uint64_t; + static const int significand_bits = 52; + static const int exponent_bits = 11; + static const int min_exponent = -1022; + static const int max_exponent = 1023; + static const int exponent_bias = -1023; + static const int decimal_digits = 17; + static const int kappa = 2; + static const int big_divisor = 1000; + static const int small_divisor = 100; + static const int min_k = -292; + static const int max_k = 326; + static const int cache_bits = 128; + static const int divisibility_check_by_5_threshold = 86; + static const int case_fc_pm_half_lower_threshold = -2; + static const int case_fc_pm_half_upper_threshold = 9; + static const int case_fc_lower_threshold = -4; + static const int case_fc_upper_threshold = 9; + static const int case_shorter_interval_left_endpoint_lower_threshold = 2; + static const int case_shorter_interval_left_endpoint_upper_threshold = 3; + static const int shorter_interval_tie_lower_threshold = -77; + static const int shorter_interval_tie_upper_threshold = -77; + static const int max_trailing_zeros = 16; }; -// A formatting argument. It is a trivially copyable/constructible type to -// allow storage in internal::MemoryBuffer. -struct Arg : Value { - Type type; +template struct decimal_fp { + using significand_type = typename float_info::carrier_uint; + significand_type significand; + int exponent; }; -template -struct NamedArg; -template -struct NamedArgWithType; +template FMT_API decimal_fp to_decimal(T x) FMT_NOEXCEPT; +} // namespace dragonbox -template -struct Null {}; +template +constexpr typename dragonbox::float_info::carrier_uint exponent_mask() { + using uint = typename dragonbox::float_info::carrier_uint; + return ((uint(1) << dragonbox::float_info::exponent_bits) - 1) + << dragonbox::float_info::significand_bits; +} -// A helper class template to enable or disable overloads taking wide -// characters and strings in MakeValue. -template -struct WCharHelper { - typedef Null Supported; - typedef T Unsupported; +// A floating-point presentation format. +enum class float_format : unsigned char { + general, // General: exponent notation or fixed point based on magnitude. + exp, // Exponent notation with the default precision of 6, e.g. 1.2e-3. + fixed, // Fixed point with the default precision of 6, e.g. 0.0012. + hex }; -template -struct WCharHelper { - typedef T Supported; - typedef Null Unsupported; +struct float_specs { + int precision; + float_format format : 8; + sign_t sign : 8; + bool upper : 1; + bool locale : 1; + bool binary32 : 1; + bool use_grisu : 1; + bool showpoint : 1; }; -typedef char Yes[1]; -typedef char No[2]; +// Writes the exponent exp in the form "[+-]d{2,3}" to buffer. +template It write_exponent(int exp, It it) { + FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range"); + if (exp < 0) { + *it++ = static_cast('-'); + exp = -exp; + } else { + *it++ = static_cast('+'); + } + if (exp >= 100) { + const char* top = data::digits[exp / 100]; + if (exp >= 1000) *it++ = static_cast(top[0]); + *it++ = static_cast(top[1]); + exp %= 100; + } + const char* d = data::digits[exp]; + *it++ = static_cast(d[0]); + *it++ = static_cast(d[1]); + return it; +} template -T &get(); +int format_float(T value, int precision, float_specs specs, buffer& buf); -// These are non-members to workaround an overload resolution bug in bcc32. -Yes &convert(fmt::ULongLong); -No &convert(...); +// Formats a floating-point number with snprintf. +template +int snprintf_float(T value, int precision, float_specs specs, + buffer& buf); -template -struct ConvertToIntImpl { - enum { value = ENABLE_CONVERSION }; -}; +template T promote_float(T value) { return value; } +inline double promote_float(float value) { return static_cast(value); } -template -struct ConvertToIntImpl2 { - enum { value = false }; -}; +template +FMT_CONSTEXPR void handle_int_type_spec(char spec, Handler&& handler) { + switch (spec) { + case 0: + case 'd': + handler.on_dec(); + break; + case 'x': + case 'X': + handler.on_hex(); + break; + case 'b': + case 'B': + handler.on_bin(); + break; + case 'o': + handler.on_oct(); + break; +#ifdef FMT_DEPRECATED_N_SPECIFIER + case 'n': +#endif + case 'L': + handler.on_num(); + break; + case 'c': + handler.on_chr(); + break; + default: + handler.on_error(); + } +} -template -struct ConvertToIntImpl2 { - enum { - // Don't convert numeric types. - value = ConvertToIntImpl::is_specialized>::value - }; -}; +template +FMT_CONSTEXPR float_specs parse_float_type_spec( + const basic_format_specs& specs, ErrorHandler&& eh = {}) { + auto result = float_specs(); + result.showpoint = specs.alt; + switch (specs.type) { + case 0: + result.format = float_format::general; + result.showpoint |= specs.precision > 0; + break; + case 'G': + result.upper = true; + FMT_FALLTHROUGH; + case 'g': + result.format = float_format::general; + break; + case 'E': + result.upper = true; + FMT_FALLTHROUGH; + case 'e': + result.format = float_format::exp; + result.showpoint |= specs.precision != 0; + break; + case 'F': + result.upper = true; + FMT_FALLTHROUGH; + case 'f': + result.format = float_format::fixed; + result.showpoint |= specs.precision != 0; + break; + case 'A': + result.upper = true; + FMT_FALLTHROUGH; + case 'a': + result.format = float_format::hex; + break; +#ifdef FMT_DEPRECATED_N_SPECIFIER + case 'n': +#endif + case 'L': + result.locale = true; + break; + default: + eh.on_error("invalid type specifier"); + break; + } + return result; +} -template -struct ConvertToInt { - enum { - enable_conversion = sizeof(fmt::internal::convert(get())) == sizeof(Yes) - }; - enum { value = ConvertToIntImpl2::value }; -}; +template +FMT_CONSTEXPR void handle_char_specs(const basic_format_specs* specs, + Handler&& handler) { + if (!specs) return handler.on_char(); + if (specs->type && specs->type != 'c') return handler.on_int(); + if (specs->align == align::numeric || specs->sign != sign::none || specs->alt) + handler.on_error("invalid format specifier for char"); + handler.on_char(); +} -#define FMT_DISABLE_CONVERSION_TO_INT(Type) \ - template <> \ - struct ConvertToInt { enum { value = 0 }; } +template +FMT_CONSTEXPR void handle_cstring_type_spec(Char spec, Handler&& handler) { + if (spec == 0 || spec == 's') + handler.on_string(); + else if (spec == 'p') + handler.on_pointer(); + else + handler.on_error("invalid type specifier"); +} -// Silence warnings about convering float to int. -FMT_DISABLE_CONVERSION_TO_INT(float); -FMT_DISABLE_CONVERSION_TO_INT(double); -FMT_DISABLE_CONVERSION_TO_INT(long double); +template +FMT_CONSTEXPR void check_string_type_spec(Char spec, ErrorHandler&& eh) { + if (spec != 0 && spec != 's') eh.on_error("invalid type specifier"); +} -template -struct EnableIf {}; +template +FMT_CONSTEXPR void check_pointer_type_spec(Char spec, ErrorHandler&& eh) { + if (spec != 0 && spec != 'p') eh.on_error("invalid type specifier"); +} -template -struct EnableIf { typedef T type; }; +template class int_type_checker : private ErrorHandler { + public: + FMT_CONSTEXPR explicit int_type_checker(ErrorHandler eh) : ErrorHandler(eh) {} -template -struct Conditional { typedef T type; }; + FMT_CONSTEXPR void on_dec() {} + FMT_CONSTEXPR void on_hex() {} + FMT_CONSTEXPR void on_bin() {} + FMT_CONSTEXPR void on_oct() {} + FMT_CONSTEXPR void on_num() {} + FMT_CONSTEXPR void on_chr() {} -template -struct Conditional { typedef F type; }; + FMT_CONSTEXPR void on_error() { + ErrorHandler::on_error("invalid type specifier"); + } +}; -// For bcc32 which doesn't understand ! in template arguments. -template -struct Not { enum { value = 0 }; }; +template +class char_specs_checker : public ErrorHandler { + private: + char type_; -template <> -struct Not { enum { value = 1 }; }; + public: + FMT_CONSTEXPR char_specs_checker(char type, ErrorHandler eh) + : ErrorHandler(eh), type_(type) {} -template -struct FalseType { enum { value = 0 }; }; + FMT_CONSTEXPR void on_int() { + handle_int_type_spec(type_, int_type_checker(*this)); + } + FMT_CONSTEXPR void on_char() {} +}; + +template +class cstring_type_checker : public ErrorHandler { + public: + FMT_CONSTEXPR explicit cstring_type_checker(ErrorHandler eh) + : ErrorHandler(eh) {} -template struct LConvCheck { - LConvCheck(int) {} + FMT_CONSTEXPR void on_string() {} + FMT_CONSTEXPR void on_pointer() {} }; -// Returns the thousands separator for the current locale. -// We check if ``lconv`` contains ``thousands_sep`` because on Android -// ``lconv`` is stubbed as an empty struct. -template -inline StringRef thousands_sep( - LConv *lc, LConvCheck = 0) { - return lc->thousands_sep; +template +FMT_NOINLINE OutputIt fill(OutputIt it, size_t n, const fill_t& fill) { + auto fill_size = fill.size(); + if (fill_size == 1) return std::fill_n(it, n, fill[0]); + for (size_t i = 0; i < n; ++i) it = std::copy_n(fill.data(), fill_size, it); + return it; } -inline fmt::StringRef thousands_sep(...) { return ""; } +// Writes the output of f, padded according to format specifications in specs. +// size: output size in code units. +// width: output display width in (terminal) column positions. +template +inline OutputIt write_padded(OutputIt out, + const basic_format_specs& specs, size_t size, + size_t width, F&& f) { + static_assert(align == align::left || align == align::right, ""); + unsigned spec_width = to_unsigned(specs.width); + size_t padding = spec_width > width ? spec_width - width : 0; + auto* shifts = align == align::left ? data::left_padding_shifts + : data::right_padding_shifts; + size_t left_padding = padding >> shifts[specs.align]; + auto it = reserve(out, size + padding * specs.fill.size()); + it = fill(it, left_padding, specs.fill); + it = f(it); + it = fill(it, padding - left_padding, specs.fill); + return base_iterator(out, it); +} -#define FMT_CONCAT(a, b) a##b +template +inline OutputIt write_padded(OutputIt out, + const basic_format_specs& specs, size_t size, + F&& f) { + return write_padded(out, specs, size, size, f); +} -#if FMT_GCC_VERSION >= 303 -# define FMT_UNUSED __attribute__((unused)) -#else -# define FMT_UNUSED -#endif +template +OutputIt write_bytes(OutputIt out, string_view bytes, + const basic_format_specs& specs) { + using iterator = remove_reference_t; + return write_padded(out, specs, bytes.size(), [bytes](iterator it) { + const char* data = bytes.data(); + return copy_str(data, data + bytes.size(), it); + }); +} -#ifndef FMT_USE_STATIC_ASSERT -# define FMT_USE_STATIC_ASSERT 0 -#endif +// Data for write_int that doesn't depend on output iterator type. It is used to +// avoid template code bloat. +template struct write_int_data { + size_t size; + size_t padding; + + write_int_data(int num_digits, string_view prefix, + const basic_format_specs& specs) + : size(prefix.size() + to_unsigned(num_digits)), padding(0) { + if (specs.align == align::numeric) { + auto width = to_unsigned(specs.width); + if (width > size) { + padding = width - size; + size = width; + } + } else if (specs.precision > num_digits) { + size = prefix.size() + to_unsigned(specs.precision); + padding = to_unsigned(specs.precision - num_digits); + } + } +}; -#if FMT_USE_STATIC_ASSERT || FMT_HAS_FEATURE(cxx_static_assert) || \ - (FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1600 -# define FMT_STATIC_ASSERT(cond, message) static_assert(cond, message) -#else -# define FMT_CONCAT_(a, b) FMT_CONCAT(a, b) -# define FMT_STATIC_ASSERT(cond, message) \ - typedef int FMT_CONCAT_(Assert, __LINE__)[(cond) ? 1 : -1] FMT_UNUSED -#endif +// Writes an integer in the format +// +// where are written by f(it). +template +OutputIt write_int(OutputIt out, int num_digits, string_view prefix, + const basic_format_specs& specs, F f) { + auto data = write_int_data(num_digits, prefix, specs); + using iterator = remove_reference_t; + return write_padded(out, specs, data.size, [=](iterator it) { + if (prefix.size() != 0) + it = copy_str(prefix.begin(), prefix.end(), it); + it = std::fill_n(it, data.padding, static_cast('0')); + return f(it); + }); +} -template -void format_arg(Formatter &, const Char *, const T &) { - FMT_STATIC_ASSERT(FalseType::value, - "Cannot format argument. To enable the use of ostream " - "operator<< include fmt/ostream.h. Otherwise provide " - "an overload of format_arg."); +template +OutputIt write(OutputIt out, basic_string_view s, + const basic_format_specs& specs) { + auto data = s.data(); + auto size = s.size(); + if (specs.precision >= 0 && to_unsigned(specs.precision) < size) + size = code_point_index(s, to_unsigned(specs.precision)); + auto width = specs.width != 0 + ? count_code_points(basic_string_view(data, size)) + : 0; + using iterator = remove_reference_t; + return write_padded(out, specs, size, width, [=](iterator it) { + return copy_str(data, data + size, it); + }); } -// Makes an Arg object from any type. -template -class MakeValue : public Arg { - public: - typedef typename Formatter::Char Char; +// The handle_int_type_spec handler that writes an integer. +template struct int_writer { + OutputIt out; + locale_ref locale; + const basic_format_specs& specs; + UInt abs_value; + char prefix[4]; + unsigned prefix_size; - private: - // The following two methods are private to disallow formatting of - // arbitrary pointers. If you want to output a pointer cast it to - // "void *" or "const void *". In particular, this forbids formatting - // of "[const] volatile char *" which is printed as bool by iostreams. - // Do not implement! - template - MakeValue(const T *value); - template - MakeValue(T *value); - - // The following methods are private to disallow formatting of wide - // characters and strings into narrow strings as in - // fmt::format("{}", L"test"); - // To fix this, use a wide format string: fmt::format(L"{}", L"test"). -#if !FMT_MSC_VER || defined(_NATIVE_WCHAR_T_DEFINED) - MakeValue(typename WCharHelper::Unsupported); -#endif - MakeValue(typename WCharHelper::Unsupported); - MakeValue(typename WCharHelper::Unsupported); - MakeValue(typename WCharHelper::Unsupported); - MakeValue(typename WCharHelper::Unsupported); + using iterator = + remove_reference_t(), 0))>; - void set_string(StringRef str) { - string.value = str.data(); - string.size = str.size(); - } + string_view get_prefix() const { return string_view(prefix, prefix_size); } - void set_string(WStringRef str) { - wstring.value = str.data(); - wstring.size = str.size(); + template + int_writer(OutputIt output, locale_ref loc, Int value, + const basic_format_specs& s) + : out(output), + locale(loc), + specs(s), + abs_value(static_cast(value)), + prefix_size(0) { + static_assert(std::is_same, UInt>::value, ""); + if (is_negative(value)) { + prefix[0] = '-'; + ++prefix_size; + abs_value = 0 - abs_value; + } else if (specs.sign != sign::none && specs.sign != sign::minus) { + prefix[0] = specs.sign == sign::plus ? '+' : ' '; + ++prefix_size; + } } - // Formats an argument of a custom type, such as a user-defined class. - template - static void format_custom_arg( - void *formatter, const void *arg, void *format_str_ptr) { - format_arg(*static_cast(formatter), - *static_cast(format_str_ptr), - *static_cast(arg)); + void on_dec() { + auto num_digits = count_digits(abs_value); + out = write_int( + out, num_digits, get_prefix(), specs, [this, num_digits](iterator it) { + return format_decimal(it, abs_value, num_digits).end; + }); } - public: - MakeValue() {} - -#define FMT_MAKE_VALUE_(Type, field, TYPE, rhs) \ - MakeValue(Type value) { field = rhs; } \ - static uint64_t type(Type) { return Arg::TYPE; } - -#define FMT_MAKE_VALUE(Type, field, TYPE) \ - FMT_MAKE_VALUE_(Type, field, TYPE, value) - - FMT_MAKE_VALUE(bool, int_value, BOOL) - FMT_MAKE_VALUE(short, int_value, INT) - FMT_MAKE_VALUE(unsigned short, uint_value, UINT) - FMT_MAKE_VALUE(int, int_value, INT) - FMT_MAKE_VALUE(unsigned, uint_value, UINT) - - MakeValue(long value) { - // To minimize the number of types we need to deal with, long is - // translated either to int or to long long depending on its size. - if (const_check(sizeof(long) == sizeof(int))) - int_value = static_cast(value); - else - long_long_value = value; - } - static uint64_t type(long) { - return sizeof(long) == sizeof(int) ? Arg::INT : Arg::LONG_LONG; + void on_hex() { + if (specs.alt) { + prefix[prefix_size++] = '0'; + prefix[prefix_size++] = specs.type; + } + int num_digits = count_digits<4>(abs_value); + out = write_int(out, num_digits, get_prefix(), specs, + [this, num_digits](iterator it) { + return format_uint<4, Char>(it, abs_value, num_digits, + specs.type != 'x'); + }); } - MakeValue(unsigned long value) { - if (const_check(sizeof(unsigned long) == sizeof(unsigned))) - uint_value = static_cast(value); - else - ulong_long_value = value; - } - static uint64_t type(unsigned long) { - return sizeof(unsigned long) == sizeof(unsigned) ? - Arg::UINT : Arg::ULONG_LONG; + void on_bin() { + if (specs.alt) { + prefix[prefix_size++] = '0'; + prefix[prefix_size++] = static_cast(specs.type); + } + int num_digits = count_digits<1>(abs_value); + out = write_int(out, num_digits, get_prefix(), specs, + [this, num_digits](iterator it) { + return format_uint<1, Char>(it, abs_value, num_digits); + }); + } + + void on_oct() { + int num_digits = count_digits<3>(abs_value); + if (specs.alt && specs.precision <= num_digits && abs_value != 0) { + // Octal prefix '0' is counted as a digit, so only add it if precision + // is not greater than the number of digits. + prefix[prefix_size++] = '0'; + } + out = write_int(out, num_digits, get_prefix(), specs, + [this, num_digits](iterator it) { + return format_uint<3, Char>(it, abs_value, num_digits); + }); + } + + enum { sep_size = 1 }; + + void on_num() { + std::string groups = grouping(locale); + if (groups.empty()) return on_dec(); + auto sep = thousands_sep(locale); + if (!sep) return on_dec(); + int num_digits = count_digits(abs_value); + int size = num_digits, n = num_digits; + std::string::const_iterator group = groups.cbegin(); + while (group != groups.cend() && n > *group && *group > 0 && + *group != max_value()) { + size += sep_size; + n -= *group; + ++group; + } + if (group == groups.cend()) size += sep_size * ((n - 1) / groups.back()); + char digits[40]; + format_decimal(digits, abs_value, num_digits); + basic_memory_buffer buffer; + size += static_cast(prefix_size); + const auto usize = to_unsigned(size); + buffer.resize(usize); + basic_string_view s(&sep, sep_size); + // Index of a decimal digit with the least significant digit having index 0. + int digit_index = 0; + group = groups.cbegin(); + auto p = buffer.data() + size - 1; + for (int i = num_digits - 1; i > 0; --i) { + *p-- = static_cast(digits[i]); + if (*group <= 0 || ++digit_index % *group != 0 || + *group == max_value()) + continue; + if (group + 1 != groups.cend()) { + digit_index = 0; + ++group; + } + std::uninitialized_copy(s.data(), s.data() + s.size(), + make_checked(p, s.size())); + p -= s.size(); + } + *p-- = static_cast(*digits); + if (prefix_size != 0) *p = static_cast('-'); + auto data = buffer.data(); + out = write_padded( + out, specs, usize, usize, + [=](iterator it) { return copy_str(data, data + size, it); }); } - FMT_MAKE_VALUE(LongLong, long_long_value, LONG_LONG) - FMT_MAKE_VALUE(ULongLong, ulong_long_value, ULONG_LONG) - FMT_MAKE_VALUE(float, double_value, DOUBLE) - FMT_MAKE_VALUE(double, double_value, DOUBLE) - FMT_MAKE_VALUE(long double, long_double_value, LONG_DOUBLE) - FMT_MAKE_VALUE(signed char, int_value, INT) - FMT_MAKE_VALUE(unsigned char, uint_value, UINT) - FMT_MAKE_VALUE(char, int_value, CHAR) + void on_chr() { *out++ = static_cast(abs_value); } -#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) - MakeValue(typename WCharHelper::Supported value) { - int_value = value; + FMT_NORETURN void on_error() { + FMT_THROW(format_error("invalid type specifier")); } - static uint64_t type(wchar_t) { return Arg::CHAR; } -#endif - -#define FMT_MAKE_STR_VALUE(Type, TYPE) \ - MakeValue(Type value) { set_string(value); } \ - static uint64_t type(Type) { return Arg::TYPE; } - - FMT_MAKE_VALUE(char *, string.value, CSTRING) - FMT_MAKE_VALUE(const char *, string.value, CSTRING) - FMT_MAKE_VALUE(signed char *, sstring.value, CSTRING) - FMT_MAKE_VALUE(const signed char *, sstring.value, CSTRING) - FMT_MAKE_VALUE(unsigned char *, ustring.value, CSTRING) - FMT_MAKE_VALUE(const unsigned char *, ustring.value, CSTRING) - FMT_MAKE_STR_VALUE(const std::string &, STRING) - FMT_MAKE_STR_VALUE(StringRef, STRING) - FMT_MAKE_VALUE_(CStringRef, string.value, CSTRING, value.c_str()) - -#define FMT_MAKE_WSTR_VALUE(Type, TYPE) \ - MakeValue(typename WCharHelper::Supported value) { \ - set_string(value); \ - } \ - static uint64_t type(Type) { return Arg::TYPE; } - - FMT_MAKE_WSTR_VALUE(wchar_t *, WSTRING) - FMT_MAKE_WSTR_VALUE(const wchar_t *, WSTRING) - FMT_MAKE_WSTR_VALUE(const std::wstring &, WSTRING) - FMT_MAKE_WSTR_VALUE(WStringRef, WSTRING) - - FMT_MAKE_VALUE(void *, pointer, POINTER) - FMT_MAKE_VALUE(const void *, pointer, POINTER) - - template - MakeValue(const T &value, - typename EnableIf::value>::value, int>::type = 0) { - custom.value = &value; - custom.format = &format_custom_arg; - } - - template - MakeValue(const T &value, - typename EnableIf::value, int>::type = 0) { - int_value = value; - } - - template - static uint64_t type(const T &) { - return ConvertToInt::value ? Arg::INT : Arg::CUSTOM; - } - - // Additional template param `Char_` is needed here because make_type always - // uses char. - template - MakeValue(const NamedArg &value) { pointer = &value; } - template - MakeValue(const NamedArgWithType &value) { pointer = &value; } - - template - static uint64_t type(const NamedArg &) { return Arg::NAMED_ARG; } - template - static uint64_t type(const NamedArgWithType &) { return Arg::NAMED_ARG; } }; -template -class MakeArg : public Arg { -public: - MakeArg() { - type = Arg::NONE; - } +template +OutputIt write_nonfinite(OutputIt out, bool isinf, + const basic_format_specs& specs, + const float_specs& fspecs) { + auto str = + isinf ? (fspecs.upper ? "INF" : "inf") : (fspecs.upper ? "NAN" : "nan"); + constexpr size_t str_size = 3; + auto sign = fspecs.sign; + auto size = str_size + (sign ? 1 : 0); + using iterator = remove_reference_t; + return write_padded(out, specs, size, [=](iterator it) { + if (sign) *it++ = static_cast(data::signs[sign]); + return copy_str(str, str + str_size, it); + }); +} - template - MakeArg(const T &value) - : Arg(MakeValue(value)) { - type = static_cast(MakeValue::type(value)); - } +// A decimal floating-point number significand * pow(10, exp). +struct big_decimal_fp { + const char* significand; + int significand_size; + int exponent; }; -template -struct NamedArg : Arg { - BasicStringRef name; +inline int get_significand_size(const big_decimal_fp& fp) { + return fp.significand_size; +} +template +inline int get_significand_size(const dragonbox::decimal_fp& fp) { + return count_digits(fp.significand); +} - template - NamedArg(BasicStringRef argname, const T &value) - : Arg(MakeArg< BasicFormatter >(value)), name(argname) {} -}; +template +inline OutputIt write_significand(OutputIt out, const char* significand, + int& significand_size) { + return copy_str(significand, significand + significand_size, out); +} +template +inline OutputIt write_significand(OutputIt out, UInt significand, + int significand_size) { + return format_decimal(out, significand, significand_size).end; +} -template -struct NamedArgWithType : NamedArg { - NamedArgWithType(BasicStringRef argname, const T &value) - : NamedArg(argname, value) {} -}; +template ::value)> +inline Char* write_significand(Char* out, UInt significand, + int significand_size, int integral_size, + Char decimal_point) { + if (!decimal_point) + return format_decimal(out, significand, significand_size).end; + auto end = format_decimal(out + 1, significand, significand_size).end; + if (integral_size == 1) + out[0] = out[1]; + else + std::copy_n(out + 1, integral_size, out); + out[integral_size] = decimal_point; + return end; +} -class RuntimeError : public std::runtime_error { - protected: - RuntimeError() : std::runtime_error("") {} - RuntimeError(const RuntimeError &rerr) : std::runtime_error(rerr) {} - FMT_API ~RuntimeError() FMT_DTOR_NOEXCEPT; -}; +template >::value)> +inline OutputIt write_significand(OutputIt out, UInt significand, + int significand_size, int integral_size, + Char decimal_point) { + // Buffer is large enough to hold digits (digits10 + 1) and a decimal point. + Char buffer[digits10() + 2]; + auto end = write_significand(buffer, significand, significand_size, + integral_size, decimal_point); + return detail::copy_str(buffer, end, out); +} -template -class ArgMap; -} // namespace internal +template +inline OutputIt write_significand(OutputIt out, const char* significand, + int significand_size, int integral_size, + Char decimal_point) { + out = detail::copy_str(significand, significand + integral_size, out); + if (!decimal_point) return out; + *out++ = decimal_point; + return detail::copy_str(significand + integral_size, + significand + significand_size, out); +} -/** An argument list. */ -class ArgList { - private: - // To reduce compiled code size per formatting function call, types of first - // MAX_PACKED_ARGS arguments are passed in the types_ field. - uint64_t types_; - union { - // If the number of arguments is less than MAX_PACKED_ARGS, the argument - // values are stored in values_, otherwise they are stored in args_. - // This is done to reduce compiled code size as storing larger objects - // may require more code (at least on x86-64) even if the same amount of - // data is actually copied to stack. It saves ~10% on the bloat test. - const internal::Value *values_; - const internal::Arg *args_; +template +OutputIt write_float(OutputIt out, const DecimalFP& fp, + const basic_format_specs& specs, float_specs fspecs, + Char decimal_point) { + auto significand = fp.significand; + int significand_size = get_significand_size(fp); + static const Char zero = static_cast('0'); + auto sign = fspecs.sign; + size_t size = to_unsigned(significand_size) + (sign ? 1 : 0); + using iterator = remove_reference_t; + + int output_exp = fp.exponent + significand_size - 1; + auto use_exp_format = [=]() { + if (fspecs.format == float_format::exp) return true; + if (fspecs.format != float_format::general) return false; + // Use the fixed notation if the exponent is in [exp_lower, exp_upper), + // e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation. + const int exp_lower = -4, exp_upper = 16; + return output_exp < exp_lower || + output_exp >= (fspecs.precision > 0 ? fspecs.precision : exp_upper); }; + if (use_exp_format()) { + int num_zeros = 0; + if (fspecs.showpoint) { + num_zeros = (std::max)(fspecs.precision - significand_size, 0); + size += to_unsigned(num_zeros); + } else if (significand_size == 1) { + decimal_point = Char(); + } + auto abs_output_exp = output_exp >= 0 ? output_exp : -output_exp; + int exp_digits = 2; + if (abs_output_exp >= 100) exp_digits = abs_output_exp >= 1000 ? 4 : 3; + + size += to_unsigned((decimal_point ? 1 : 0) + 2 + exp_digits); + char exp_char = fspecs.upper ? 'E' : 'e'; + auto write = [=](iterator it) { + if (sign) *it++ = static_cast(data::signs[sign]); + // Insert a decimal point after the first digit and add an exponent. + it = write_significand(it, significand, significand_size, 1, + decimal_point); + if (num_zeros > 0) it = std::fill_n(it, num_zeros, zero); + *it++ = static_cast(exp_char); + return write_exponent(output_exp, it); + }; + return specs.width > 0 ? write_padded(out, specs, size, write) + : base_iterator(out, write(reserve(out, size))); + } + + int exp = fp.exponent + significand_size; + if (fp.exponent >= 0) { + // 1234e5 -> 123400000[.0+] + size += to_unsigned(fp.exponent); + int num_zeros = fspecs.precision - exp; +#ifdef FMT_FUZZ + if (num_zeros > 5000) + throw std::runtime_error("fuzz mode - avoiding excessive cpu use"); +#endif + if (fspecs.showpoint) { + if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 1; + if (num_zeros > 0) size += to_unsigned(num_zeros); + } + return write_padded(out, specs, size, [&](iterator it) { + if (sign) *it++ = static_cast(data::signs[sign]); + it = write_significand(it, significand, significand_size); + it = std::fill_n(it, fp.exponent, zero); + if (!fspecs.showpoint) return it; + *it++ = decimal_point; + return num_zeros > 0 ? std::fill_n(it, num_zeros, zero) : it; + }); + } else if (exp > 0) { + // 1234e-2 -> 12.34[0+] + int num_zeros = fspecs.showpoint ? fspecs.precision - significand_size : 0; + size += 1 + to_unsigned(num_zeros > 0 ? num_zeros : 0); + return write_padded(out, specs, size, [&](iterator it) { + if (sign) *it++ = static_cast(data::signs[sign]); + it = write_significand(it, significand, significand_size, exp, + decimal_point); + return num_zeros > 0 ? std::fill_n(it, num_zeros, zero) : it; + }); + } + // 1234e-6 -> 0.001234 + int num_zeros = -exp; + if (significand_size == 0 && fspecs.precision >= 0 && + fspecs.precision < num_zeros) { + num_zeros = fspecs.precision; + } + size += 2 + to_unsigned(num_zeros); + return write_padded(out, specs, size, [&](iterator it) { + if (sign) *it++ = static_cast(data::signs[sign]); + *it++ = zero; + if (num_zeros == 0 && significand_size == 0 && !fspecs.showpoint) return it; + *it++ = decimal_point; + it = std::fill_n(it, num_zeros, zero); + return write_significand(it, significand, significand_size); + }); +} - internal::Arg::Type type(unsigned index) const { - return type(types_, index); +template ::value)> +OutputIt write(OutputIt out, T value, basic_format_specs specs, + locale_ref loc = {}) { + if (const_check(!is_supported_floating_point(value))) return out; + float_specs fspecs = parse_float_type_spec(specs); + fspecs.sign = specs.sign; + if (std::signbit(value)) { // value < 0 is false for NaN so use signbit. + fspecs.sign = sign::minus; + value = -value; + } else if (fspecs.sign == sign::minus) { + fspecs.sign = sign::none; } - template - friend class internal::ArgMap; + if (!std::isfinite(value)) + return write_nonfinite(out, std::isinf(value), specs, fspecs); - public: - // Maximum number of arguments with packed types. - enum { MAX_PACKED_ARGS = 16 }; - - ArgList() : types_(0) {} - - ArgList(ULongLong types, const internal::Value *values) - : types_(types), values_(values) {} - ArgList(ULongLong types, const internal::Arg *args) - : types_(types), args_(args) {} - - uint64_t types() const { return types_; } - - /** Returns the argument at specified index. */ - internal::Arg operator[](unsigned index) const { - using internal::Arg; - Arg arg; - bool use_values = type(MAX_PACKED_ARGS - 1) == Arg::NONE; - if (index < MAX_PACKED_ARGS) { - Arg::Type arg_type = type(index); - internal::Value &val = arg; - if (arg_type != Arg::NONE) - val = use_values ? values_[index] : args_[index]; - arg.type = arg_type; - return arg; - } - if (use_values) { - // The index is greater than the number of arguments that can be stored - // in values, so return a "none" argument. - arg.type = Arg::NONE; - return arg; - } - for (unsigned i = MAX_PACKED_ARGS; i <= index; ++i) { - if (args_[i].type == Arg::NONE) - return args_[i]; - } - return args_[index]; + if (specs.align == align::numeric && fspecs.sign) { + auto it = reserve(out, 1); + *it++ = static_cast(data::signs[fspecs.sign]); + out = base_iterator(out, it); + fspecs.sign = sign::none; + if (specs.width != 0) --specs.width; } - static internal::Arg::Type type(uint64_t types, unsigned index) { - unsigned shift = index * 4; - uint64_t mask = 0xf; - return static_cast( - (types & (mask << shift)) >> shift); + memory_buffer buffer; + if (fspecs.format == float_format::hex) { + if (fspecs.sign) buffer.push_back(data::signs[fspecs.sign]); + snprintf_float(promote_float(value), specs.precision, fspecs, buffer); + return write_bytes(out, {buffer.data(), buffer.size()}, specs); } -}; + int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6; + if (fspecs.format == float_format::exp) { + if (precision == max_value()) + FMT_THROW(format_error("number is too big")); + else + ++precision; + } + if (const_check(std::is_same())) fspecs.binary32 = true; + fspecs.use_grisu = is_fast_float(); + int exp = format_float(promote_float(value), precision, fspecs, buffer); + fspecs.precision = precision; + Char point = + fspecs.locale ? decimal_point(loc) : static_cast('.'); + auto fp = big_decimal_fp{buffer.data(), static_cast(buffer.size()), exp}; + return write_float(out, fp, specs, fspecs, point); +} -#define FMT_DISPATCH(call) static_cast(this)->call +template ::value)> +OutputIt write(OutputIt out, T value) { + if (const_check(!is_supported_floating_point(value))) return out; -/** - \rst - An argument visitor based on the `curiously recurring template pattern - `_. - - To use `~fmt::ArgVisitor` define a subclass that implements some or all of the - visit methods with the same signatures as the methods in `~fmt::ArgVisitor`, - for example, `~fmt::ArgVisitor::visit_int()`. - Pass the subclass as the *Impl* template parameter. Then calling - `~fmt::ArgVisitor::visit` for some argument will dispatch to a visit method - specific to the argument type. For example, if the argument type is - ``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass - will be called. If the subclass doesn't contain a method with this signature, - then a corresponding method of `~fmt::ArgVisitor` will be called. + using floaty = conditional_t::value, double, T>; + using uint = typename dragonbox::float_info::carrier_uint; + auto bits = bit_cast(value); - **Example**:: + auto fspecs = float_specs(); + auto sign_bit = bits & (uint(1) << (num_bits() - 1)); + if (sign_bit != 0) { + fspecs.sign = sign::minus; + value = -value; + } - class MyArgVisitor : public fmt::ArgVisitor { - public: - void visit_int(int value) { fmt::print("{}", value); } - void visit_double(double value) { fmt::print("{}", value ); } - }; - \endrst - */ -template -class ArgVisitor { - private: - typedef internal::Arg Arg; + static const auto specs = basic_format_specs(); + uint mask = exponent_mask(); + if ((bits & mask) == mask) + return write_nonfinite(out, std::isinf(value), specs, fspecs); - public: - void report_unhandled_arg() {} + auto dec = dragonbox::to_decimal(static_cast(value)); + return write_float(out, dec, specs, fspecs, static_cast('.')); +} - Result visit_unhandled_arg() { - FMT_DISPATCH(report_unhandled_arg()); - return Result(); - } +template ::value && + !is_fast_float::value)> +inline OutputIt write(OutputIt out, T value) { + return write(out, value, basic_format_specs()); +} - /** Visits an ``int`` argument. **/ - Result visit_int(int value) { - return FMT_DISPATCH(visit_any_int(value)); - } +template +OutputIt write_char(OutputIt out, Char value, + const basic_format_specs& specs) { + using iterator = remove_reference_t; + return write_padded(out, specs, 1, [=](iterator it) { + *it++ = value; + return it; + }); +} - /** Visits a ``long long`` argument. **/ - Result visit_long_long(LongLong value) { - return FMT_DISPATCH(visit_any_int(value)); - } +template +OutputIt write_ptr(OutputIt out, UIntPtr value, + const basic_format_specs* specs) { + int num_digits = count_digits<4>(value); + auto size = to_unsigned(num_digits) + size_t(2); + using iterator = remove_reference_t; + auto write = [=](iterator it) { + *it++ = static_cast('0'); + *it++ = static_cast('x'); + return format_uint<4, Char>(it, value, num_digits); + }; + return specs ? write_padded(out, *specs, size, write) + : base_iterator(out, write(reserve(out, size))); +} - /** Visits an ``unsigned`` argument. **/ - Result visit_uint(unsigned value) { - return FMT_DISPATCH(visit_any_int(value)); - } +template struct is_integral : std::is_integral {}; +template <> struct is_integral : std::true_type {}; +template <> struct is_integral : std::true_type {}; - /** Visits an ``unsigned long long`` argument. **/ - Result visit_ulong_long(ULongLong value) { - return FMT_DISPATCH(visit_any_int(value)); - } +template +OutputIt write(OutputIt out, monostate) { + FMT_ASSERT(false, ""); + return out; +} - /** Visits a ``bool`` argument. **/ - Result visit_bool(bool value) { - return FMT_DISPATCH(visit_any_int(value)); - } +template ::value)> +OutputIt write(OutputIt out, string_view value) { + auto it = reserve(out, value.size()); + it = copy_str(value.begin(), value.end(), it); + return base_iterator(out, it); +} - /** Visits a ``char`` or ``wchar_t`` argument. **/ - Result visit_char(int value) { - return FMT_DISPATCH(visit_any_int(value)); - } +template +OutputIt write(OutputIt out, basic_string_view value) { + auto it = reserve(out, value.size()); + it = std::copy(value.begin(), value.end(), it); + return base_iterator(out, it); +} - /** Visits an argument of any integral type. **/ - template - Result visit_any_int(T) { - return FMT_DISPATCH(visit_unhandled_arg()); - } +template +buffer_appender write(buffer_appender out, + basic_string_view value) { + get_container(out).append(value.begin(), value.end()); + return out; +} - /** Visits a ``double`` argument. **/ - Result visit_double(double value) { - return FMT_DISPATCH(visit_any_double(value)); - } +template ::value && + !std::is_same::value && + !std::is_same::value)> +OutputIt write(OutputIt out, T value) { + auto abs_value = static_cast>(value); + bool negative = is_negative(value); + // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer. + if (negative) abs_value = ~abs_value + 1; + int num_digits = count_digits(abs_value); + auto size = (negative ? 1 : 0) + static_cast(num_digits); + auto it = reserve(out, size); + if (auto ptr = to_pointer(it, size)) { + if (negative) *ptr++ = static_cast('-'); + format_decimal(ptr, abs_value, num_digits); + return out; + } + if (negative) *it++ = static_cast('-'); + it = format_decimal(it, abs_value, num_digits).end; + return base_iterator(out, it); +} - /** Visits a ``long double`` argument. **/ - Result visit_long_double(long double value) { - return FMT_DISPATCH(visit_any_double(value)); - } +template +OutputIt write(OutputIt out, bool value) { + return write(out, string_view(value ? "true" : "false")); +} - /** Visits a ``double`` or ``long double`` argument. **/ - template - Result visit_any_double(T) { - return FMT_DISPATCH(visit_unhandled_arg()); - } +template +OutputIt write(OutputIt out, Char value) { + auto it = reserve(out, 1); + *it++ = value; + return base_iterator(out, it); +} - /** Visits a null-terminated C string (``const char *``) argument. **/ - Result visit_cstring(const char *) { - return FMT_DISPATCH(visit_unhandled_arg()); +template +OutputIt write(OutputIt out, const Char* value) { + if (!value) { + FMT_THROW(format_error("string pointer is null")); + } else { + auto length = std::char_traits::length(value); + out = write(out, basic_string_view(value, length)); } + return out; +} - /** Visits a string argument. **/ - Result visit_string(Arg::StringValue) { - return FMT_DISPATCH(visit_unhandled_arg()); - } +template +OutputIt write(OutputIt out, const void* value) { + return write_ptr(out, to_uintptr(value), nullptr); +} - /** Visits a wide string argument. **/ - Result visit_wstring(Arg::StringValue) { - return FMT_DISPATCH(visit_unhandled_arg()); - } +template +auto write(OutputIt out, const T& value) -> typename std::enable_if< + mapped_type_constant>::value == + type::custom_type, + OutputIt>::type { + using context_type = basic_format_context; + using formatter_type = + conditional_t::value, + typename context_type::template formatter_type, + fallback_formatter>; + context_type ctx(out, {}, {}); + return formatter_type().format(value, ctx); +} - /** Visits a pointer argument. **/ - Result visit_pointer(const void *) { - return FMT_DISPATCH(visit_unhandled_arg()); - } +// An argument visitor that formats the argument and writes it via the output +// iterator. It's a class and not a generic lambda for compatibility with C++11. +template struct default_arg_formatter { + using context = basic_format_context; - /** Visits an argument of a custom (user-defined) type. **/ - Result visit_custom(Arg::CustomValue) { - return FMT_DISPATCH(visit_unhandled_arg()); - } + OutputIt out; + basic_format_args args; + locale_ref loc; - /** - \rst - Visits an argument dispatching to the appropriate visit method based on - the argument type. For example, if the argument type is ``double`` then - the `~fmt::ArgVisitor::visit_double()` method of the *Impl* class will be - called. - \endrst - */ - Result visit(const Arg &arg) { - switch (arg.type) { - case Arg::NONE: - case Arg::NAMED_ARG: - FMT_ASSERT(false, "invalid argument type"); - break; - case Arg::INT: - return FMT_DISPATCH(visit_int(arg.int_value)); - case Arg::UINT: - return FMT_DISPATCH(visit_uint(arg.uint_value)); - case Arg::LONG_LONG: - return FMT_DISPATCH(visit_long_long(arg.long_long_value)); - case Arg::ULONG_LONG: - return FMT_DISPATCH(visit_ulong_long(arg.ulong_long_value)); - case Arg::BOOL: - return FMT_DISPATCH(visit_bool(arg.int_value != 0)); - case Arg::CHAR: - return FMT_DISPATCH(visit_char(arg.int_value)); - case Arg::DOUBLE: - return FMT_DISPATCH(visit_double(arg.double_value)); - case Arg::LONG_DOUBLE: - return FMT_DISPATCH(visit_long_double(arg.long_double_value)); - case Arg::CSTRING: - return FMT_DISPATCH(visit_cstring(arg.string.value)); - case Arg::STRING: - return FMT_DISPATCH(visit_string(arg.string)); - case Arg::WSTRING: - return FMT_DISPATCH(visit_wstring(arg.wstring)); - case Arg::POINTER: - return FMT_DISPATCH(visit_pointer(arg.pointer)); - case Arg::CUSTOM: - return FMT_DISPATCH(visit_custom(arg.custom)); - } - return Result(); + template OutputIt operator()(T value) { + return write(out, value); } -}; - -enum Alignment { - ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC -}; - -// Flags. -enum { - SIGN_FLAG = 1, PLUS_FLAG = 2, MINUS_FLAG = 4, HASH_FLAG = 8, - CHAR_FLAG = 0x10 // Argument has char type - used in error reporting. -}; - -// An empty format specifier. -struct EmptySpec {}; - -// A type specifier. -template -struct TypeSpec : EmptySpec { - Alignment align() const { return ALIGN_DEFAULT; } - unsigned width() const { return 0; } - int precision() const { return -1; } - bool flag(unsigned) const { return false; } - char type() const { return TYPE; } - char type_prefix() const { return TYPE; } - char fill() const { return ' '; } -}; - -// A width specifier. -struct WidthSpec { - unsigned width_; - // Fill is always wchar_t and cast to char if necessary to avoid having - // two specialization of WidthSpec and its subclasses. - wchar_t fill_; - - WidthSpec(unsigned width, wchar_t fill) : width_(width), fill_(fill) {} - - unsigned width() const { return width_; } - wchar_t fill() const { return fill_; } -}; - -// An alignment specifier. -struct AlignSpec : WidthSpec { - Alignment align_; - - AlignSpec(unsigned width, wchar_t fill, Alignment align = ALIGN_DEFAULT) - : WidthSpec(width, fill), align_(align) {} - - Alignment align() const { return align_; } - - int precision() const { return -1; } -}; - -// An alignment and type specifier. -template -struct AlignTypeSpec : AlignSpec { - AlignTypeSpec(unsigned width, wchar_t fill) : AlignSpec(width, fill) {} - - bool flag(unsigned) const { return false; } - char type() const { return TYPE; } - char type_prefix() const { return TYPE; } -}; -// A full format specifier. -struct FormatSpec : AlignSpec { - unsigned flags_; - int precision_; - char type_; - - FormatSpec( - unsigned width = 0, char type = 0, wchar_t fill = ' ') - : AlignSpec(width, fill), flags_(0), precision_(-1), type_(type) {} - - bool flag(unsigned f) const { return (flags_ & f) != 0; } - int precision() const { return precision_; } - char type() const { return type_; } - char type_prefix() const { return type_; } + OutputIt operator()(typename basic_format_arg::handle handle) { + basic_format_parse_context parse_ctx({}); + basic_format_context format_ctx(out, args, loc); + handle.format(parse_ctx, format_ctx); + return format_ctx.out(); + } }; -// An integer format specifier. -template , typename Char = char> -class IntFormatSpec : public SpecT { - private: - T value_; - +template +class arg_formatter_base { public: - IntFormatSpec(T val, const SpecT &spec = SpecT()) - : SpecT(spec), value_(val) {} - - T value() const { return value_; } -}; + using iterator = OutputIt; + using char_type = Char; + using format_specs = basic_format_specs; -// A string format specifier. -template -class StrFormatSpec : public AlignSpec { private: - const Char *str_; + iterator out_; + locale_ref locale_; + format_specs* specs_; - public: - template - StrFormatSpec(const Char *str, unsigned width, FillChar fill) - : AlignSpec(width, fill), str_(str) { - internal::CharTraits::convert(FillChar()); + // Attempts to reserve space for n extra characters in the output range. + // Returns a pointer to the reserved range or a reference to out_. + auto reserve(size_t n) -> decltype(detail::reserve(out_, n)) { + return detail::reserve(out_, n); } - const Char *str() const { return str_; } -}; - -/** - Returns an integer format specifier to format the value in base 2. - */ -IntFormatSpec > bin(int value); - -/** - Returns an integer format specifier to format the value in base 8. - */ -IntFormatSpec > oct(int value); - -/** - Returns an integer format specifier to format the value in base 16 using - lower-case letters for the digits above 9. - */ -IntFormatSpec > hex(int value); - -/** - Returns an integer formatter format specifier to format in base 16 using - upper-case letters for the digits above 9. - */ -IntFormatSpec > hexu(int value); - -/** - \rst - Returns an integer format specifier to pad the formatted argument with the - fill character to the specified width using the default (right) numeric - alignment. - - **Example**:: - - MemoryWriter out; - out << pad(hex(0xcafe), 8, '0'); - // out.str() == "0000cafe" + using reserve_iterator = remove_reference_t(), 0))>; - \endrst - */ -template -IntFormatSpec, Char> pad( - int value, unsigned width, Char fill = ' '); - -#define FMT_DEFINE_INT_FORMATTERS(TYPE) \ -inline IntFormatSpec > bin(TYPE value) { \ - return IntFormatSpec >(value, TypeSpec<'b'>()); \ -} \ - \ -inline IntFormatSpec > oct(TYPE value) { \ - return IntFormatSpec >(value, TypeSpec<'o'>()); \ -} \ - \ -inline IntFormatSpec > hex(TYPE value) { \ - return IntFormatSpec >(value, TypeSpec<'x'>()); \ -} \ - \ -inline IntFormatSpec > hexu(TYPE value) { \ - return IntFormatSpec >(value, TypeSpec<'X'>()); \ -} \ - \ -template \ -inline IntFormatSpec > pad( \ - IntFormatSpec > f, unsigned width) { \ - return IntFormatSpec >( \ - f.value(), AlignTypeSpec(width, ' ')); \ -} \ - \ -/* For compatibility with older compilers we provide two overloads for pad, */ \ -/* one that takes a fill character and one that doesn't. In the future this */ \ -/* can be replaced with one overload making the template argument Char */ \ -/* default to char (C++11). */ \ -template \ -inline IntFormatSpec, Char> pad( \ - IntFormatSpec, Char> f, \ - unsigned width, Char fill) { \ - return IntFormatSpec, Char>( \ - f.value(), AlignTypeSpec(width, fill)); \ -} \ - \ -inline IntFormatSpec > pad( \ - TYPE value, unsigned width) { \ - return IntFormatSpec >( \ - value, AlignTypeSpec<0>(width, ' ')); \ -} \ - \ -template \ -inline IntFormatSpec, Char> pad( \ - TYPE value, unsigned width, Char fill) { \ - return IntFormatSpec, Char>( \ - value, AlignTypeSpec<0>(width, fill)); \ -} - -FMT_DEFINE_INT_FORMATTERS(int) -FMT_DEFINE_INT_FORMATTERS(long) -FMT_DEFINE_INT_FORMATTERS(unsigned) -FMT_DEFINE_INT_FORMATTERS(unsigned long) -FMT_DEFINE_INT_FORMATTERS(LongLong) -FMT_DEFINE_INT_FORMATTERS(ULongLong) + template void write_int(T value, const format_specs& spec) { + using uint_type = uint32_or_64_or_128_t; + int_writer w(out_, locale_, value, spec); + handle_int_type_spec(spec.type, w); + out_ = w.out; + } -/** - \rst - Returns a string formatter that pads the formatted argument with the fill - character to the specified width using the default (left) string alignment. + void write(char value) { + auto&& it = reserve(1); + *it++ = value; + } - **Example**:: + template ::value)> + void write(Ch value) { + out_ = detail::write(out_, value); + } - std::string s = str(MemoryWriter() << pad("abc", 8)); - // s == "abc " + void write(string_view value) { + auto&& it = reserve(value.size()); + it = copy_str(value.begin(), value.end(), it); + } + void write(wstring_view value) { + static_assert(std::is_same::value, ""); + auto&& it = reserve(value.size()); + it = std::copy(value.begin(), value.end(), it); + } - \endrst - */ -template -inline StrFormatSpec pad( - const Char *str, unsigned width, Char fill = ' ') { - return StrFormatSpec(str, width, fill); -} + template + void write(const Ch* s, size_t size, const format_specs& specs) { + auto width = specs.width != 0 + ? count_code_points(basic_string_view(s, size)) + : 0; + out_ = write_padded(out_, specs, size, width, [=](reserve_iterator it) { + return copy_str(s, s + size, it); + }); + } -inline StrFormatSpec pad( - const wchar_t *str, unsigned width, char fill = ' ') { - return StrFormatSpec(str, width, fill); -} + template + void write(basic_string_view s, const format_specs& specs = {}) { + out_ = detail::write(out_, s, specs); + } -namespace internal { + void write_pointer(const void* p) { + out_ = write_ptr(out_, to_uintptr(p), specs_); + } -template -class ArgMap { - private: - typedef std::vector< - std::pair, internal::Arg> > MapType; - typedef typename MapType::value_type Pair; + struct char_spec_handler : ErrorHandler { + arg_formatter_base& formatter; + Char value; - MapType map_; + char_spec_handler(arg_formatter_base& f, Char val) + : formatter(f), value(val) {} - public: - FMT_API void init(const ArgList &args); - - const internal::Arg *find(const fmt::BasicStringRef &name) const { - // The list is unsorted, so just return the first matching name. - for (typename MapType::const_iterator it = map_.begin(), end = map_.end(); - it != end; ++it) { - if (it->first == name) - return &it->second; + void on_int() { + // char is only formatted as int if there are specs. + formatter.write_int(static_cast(value), *formatter.specs_); } - return FMT_NULL; - } -}; + void on_char() { + if (formatter.specs_) + formatter.out_ = write_char(formatter.out_, value, *formatter.specs_); + else + formatter.write(value); + } + }; -template -class ArgFormatterBase : public ArgVisitor { - private: - BasicWriter &writer_; - Spec &spec_; + struct cstring_spec_handler : error_handler { + arg_formatter_base& formatter; + const Char* value; - FMT_DISALLOW_COPY_AND_ASSIGN(ArgFormatterBase); + cstring_spec_handler(arg_formatter_base& f, const Char* val) + : formatter(f), value(val) {} - void write_pointer(const void *p) { - spec_.flags_ = HASH_FLAG; - spec_.type_ = 'x'; - writer_.write_int(reinterpret_cast(p), spec_); - } + void on_string() { formatter.write(value); } + void on_pointer() { formatter.write_pointer(value); } + }; protected: - BasicWriter &writer() { return writer_; } - Spec &spec() { return spec_; } + iterator out() { return out_; } + format_specs* specs() { return specs_; } void write(bool value) { - const char *str_value = value ? "true" : "false"; - Arg::StringValue str = { str_value, std::strlen(str_value) }; - writer_.write_str(str, spec_); - } - - void write(const char *value) { - Arg::StringValue str = {value, value ? std::strlen(value) : 0}; - writer_.write_str(str, spec_); - } - - public: - typedef Spec SpecType; - - ArgFormatterBase(BasicWriter &w, Spec &s) - : writer_(w), spec_(s) {} - - template - void visit_any_int(T value) { writer_.write_int(value, spec_); } - - template - void visit_any_double(T value) { writer_.write_double(value, spec_); } - - void visit_bool(bool value) { - if (spec_.type_) { - visit_any_int(value); - return; - } - write(value); + if (specs_) + write(string_view(value ? "true" : "false"), *specs_); + else + out_ = detail::write(out_, value); } - void visit_char(int value) { - if (spec_.type_ && spec_.type_ != 'c') { - spec_.flags_ |= CHAR_FLAG; - writer_.write_int(value, spec_); - return; - } - if (spec_.align_ == ALIGN_NUMERIC || spec_.flags_ != 0) - FMT_THROW(FormatError("invalid format specifier for char")); - typedef typename BasicWriter::CharPtr CharPtr; - Char fill = internal::CharTraits::cast(spec_.fill()); - CharPtr out = CharPtr(); - const unsigned CHAR_SIZE = 1; - if (spec_.width_ > CHAR_SIZE) { - out = writer_.grow_buffer(spec_.width_); - if (spec_.align_ == ALIGN_RIGHT) { - std::uninitialized_fill_n(out, spec_.width_ - CHAR_SIZE, fill); - out += spec_.width_ - CHAR_SIZE; - } else if (spec_.align_ == ALIGN_CENTER) { - out = writer_.fill_padding(out, spec_.width_, - internal::const_check(CHAR_SIZE), fill); - } else { - std::uninitialized_fill_n(out + CHAR_SIZE, - spec_.width_ - CHAR_SIZE, fill); - } + void write(const Char* value) { + if (!value) { + FMT_THROW(format_error("string pointer is null")); } else { - out = writer_.grow_buffer(CHAR_SIZE); + auto length = std::char_traits::length(value); + basic_string_view sv(value, length); + specs_ ? write(sv, *specs_) : write(sv); } - *out = internal::CharTraits::cast(value); } - void visit_cstring(const char *value) { - if (spec_.type_ == 'p') - return write_pointer(value); - write(value); - } + public: + arg_formatter_base(OutputIt out, format_specs* s, locale_ref loc) + : out_(out), locale_(loc), specs_(s) {} - // Qualification with "internal" here and below is a workaround for nvcc. - void visit_string(internal::Arg::StringValue value) { - writer_.write_str(value, spec_); + iterator operator()(monostate) { + FMT_ASSERT(false, "invalid argument type"); + return out_; } - using ArgVisitor::visit_wstring; - - void visit_wstring(internal::Arg::StringValue value) { - writer_.write_str(value, spec_); + template ::value)> + FMT_INLINE iterator operator()(T value) { + if (specs_) + write_int(value, *specs_); + else + out_ = detail::write(out_, value); + return out_; } - void visit_pointer(const void *value) { - if (spec_.type_ && spec_.type_ != 'p') - report_unknown_type(spec_.type_, "pointer"); - write_pointer(value); + iterator operator()(Char value) { + handle_char_specs(specs_, + char_spec_handler(*this, static_cast(value))); + return out_; } -}; - -class FormatterBase { - private: - ArgList args_; - int next_arg_index_; - - // Returns the argument with specified index. - FMT_API Arg do_get_arg(unsigned arg_index, const char *&error); - protected: - const ArgList &args() const { return args_; } - - explicit FormatterBase(const ArgList &args) { - args_ = args; - next_arg_index_ = 0; + iterator operator()(bool value) { + if (specs_ && specs_->type) return (*this)(value ? 1 : 0); + write(value != 0); + return out_; } - // Returns the next argument. - Arg next_arg(const char *&error) { - if (next_arg_index_ >= 0) - return do_get_arg(internal::to_unsigned(next_arg_index_++), error); - error = "cannot switch from manual to automatic argument indexing"; - return Arg(); + template ::value)> + iterator operator()(T value) { + auto specs = specs_ ? *specs_ : format_specs(); + if (const_check(is_supported_floating_point(value))) + out_ = detail::write(out_, value, specs, locale_); + else + FMT_ASSERT(false, "unsupported float argument type"); + return out_; } - // Checks if manual indexing is used and returns the argument with - // specified index. - Arg get_arg(unsigned arg_index, const char *&error) { - return check_no_auto_index(error) ? do_get_arg(arg_index, error) : Arg(); + iterator operator()(const Char* value) { + if (!specs_) return write(value), out_; + handle_cstring_type_spec(specs_->type, cstring_spec_handler(*this, value)); + return out_; } - bool check_no_auto_index(const char *&error) { - if (next_arg_index_ > 0) { - error = "cannot switch from automatic to manual argument indexing"; - return false; + iterator operator()(basic_string_view value) { + if (specs_) { + check_string_type_spec(specs_->type, error_handler()); + write(value, *specs_); + } else { + write(value); } - next_arg_index_ = -1; - return true; + return out_; } - template - void write(BasicWriter &w, const Char *start, const Char *end) { - if (start != end) - w << BasicStringRef(start, internal::to_unsigned(end - start)); + iterator operator()(const void* value) { + if (specs_) check_pointer_type_spec(specs_->type, error_handler()); + write_pointer(value); + return out_; } }; -} // namespace internal -/** - \rst - An argument formatter based on the `curiously recurring template pattern - `_. - - To use `~fmt::BasicArgFormatter` define a subclass that implements some or - all of the visit methods with the same signatures as the methods in - `~fmt::ArgVisitor`, for example, `~fmt::ArgVisitor::visit_int()`. - Pass the subclass as the *Impl* template parameter. When a formatting - function processes an argument, it will dispatch to a visit method - specific to the argument type. For example, if the argument type is - ``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass - will be called. If the subclass doesn't contain a method with this signature, - then a corresponding method of `~fmt::BasicArgFormatter` or its superclass - will be called. - \endrst - */ -template -class BasicArgFormatter : public internal::ArgFormatterBase { +/** The default argument formatter. */ +template +class arg_formatter : public arg_formatter_base { private: - BasicFormatter &formatter_; - const Char *format_; + using char_type = Char; + using base = arg_formatter_base; + using context_type = basic_format_context; + + context_type& ctx_; + basic_format_parse_context* parse_ctx_; + const Char* ptr_; public: + using iterator = typename base::iterator; + using format_specs = typename base::format_specs; + /** \rst Constructs an argument formatter object. - *formatter* is a reference to the main formatter object, *spec* contains - format specifier information for standard argument types, and *fmt* points - to the part of the format string being parsed for custom argument types. + *ctx* is a reference to the formatting context, + *specs* contains format specifier information for standard argument types. \endrst */ - BasicArgFormatter(BasicFormatter &formatter, - Spec &spec, const Char *fmt) - : internal::ArgFormatterBase(formatter.writer(), spec), - formatter_(formatter), format_(fmt) {} - - /** Formats an argument of a custom (user-defined) type. */ - void visit_custom(internal::Arg::CustomValue c) { - c.format(&formatter_, c.value, &format_); + explicit arg_formatter( + context_type& ctx, + basic_format_parse_context* parse_ctx = nullptr, + format_specs* specs = nullptr, const Char* ptr = nullptr) + : base(ctx.out(), specs, ctx.locale()), + ctx_(ctx), + parse_ctx_(parse_ctx), + ptr_(ptr) {} + + using base::operator(); + + /** Formats an argument of a user-defined type. */ + iterator operator()(typename basic_format_arg::handle handle) { + if (ptr_) advance_to(*parse_ctx_, ptr_); + handle.format(*parse_ctx_, ctx_); + return ctx_.out(); } }; -/** The default argument formatter. */ -template -class ArgFormatter : - public BasicArgFormatter, Char, FormatSpec> { - public: - /** Constructs an argument formatter object. */ - ArgFormatter(BasicFormatter &formatter, - FormatSpec &spec, const Char *fmt) - : BasicArgFormatter, - Char, FormatSpec>(formatter, spec, fmt) {} -}; +template FMT_CONSTEXPR bool is_name_start(Char c) { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c; +} -/** This template formats data and writes the output to a writer. */ -template -class BasicFormatter : private internal::FormatterBase { - public: - /** The character type for the output. */ - typedef CharType Char; +// Parses the range [begin, end) as an unsigned integer. This function assumes +// that the range is non-empty and the first character is a digit. +template +FMT_CONSTEXPR int parse_nonnegative_int(const Char*& begin, const Char* end, + ErrorHandler&& eh) { + FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', ""); + unsigned value = 0; + // Convert to unsigned to prevent a warning. + constexpr unsigned max_int = max_value(); + unsigned big = max_int / 10; + do { + // Check for overflow. + if (value > big) { + value = max_int + 1; + break; + } + value = value * 10 + unsigned(*begin - '0'); + ++begin; + } while (begin != end && '0' <= *begin && *begin <= '9'); + if (value > max_int) eh.on_error("number is too big"); + return static_cast(value); +} +template class custom_formatter { private: - BasicWriter &writer_; - internal::ArgMap map_; - - FMT_DISALLOW_COPY_AND_ASSIGN(BasicFormatter); - - using internal::FormatterBase::get_arg; + using char_type = typename Context::char_type; - // Checks if manual indexing is used and returns the argument with - // specified name. - internal::Arg get_arg(BasicStringRef arg_name, const char *&error); - - // Parses argument index and returns corresponding argument. - internal::Arg parse_arg_index(const Char *&s); - - // Parses argument name and returns corresponding argument. - internal::Arg parse_arg_name(const Char *&s); + basic_format_parse_context& parse_ctx_; + Context& ctx_; public: - /** - \rst - Constructs a ``BasicFormatter`` object. References to the arguments and - the writer are stored in the formatter object so make sure they have - appropriate lifetimes. - \endrst - */ - BasicFormatter(const ArgList &args, BasicWriter &w) - : internal::FormatterBase(args), writer_(w) {} - - /** Returns a reference to the writer associated with this formatter. */ - BasicWriter &writer() { return writer_; } + explicit custom_formatter(basic_format_parse_context& parse_ctx, + Context& ctx) + : parse_ctx_(parse_ctx), ctx_(ctx) {} - /** Formats stored arguments and writes the output to the writer. */ - void format(BasicCStringRef format_str); + void operator()(typename basic_format_arg::handle h) const { + h.format(parse_ctx_, ctx_); + } - // Formats a single argument and advances format_str, a format string pointer. - const Char *format(const Char *&format_str, const internal::Arg &arg); + template void operator()(T) const {} }; -// Generates a comma-separated list with results of applying f to -// numbers 0..n-1. -# define FMT_GEN(n, f) FMT_GEN##n(f) -# define FMT_GEN1(f) f(0) -# define FMT_GEN2(f) FMT_GEN1(f), f(1) -# define FMT_GEN3(f) FMT_GEN2(f), f(2) -# define FMT_GEN4(f) FMT_GEN3(f), f(3) -# define FMT_GEN5(f) FMT_GEN4(f), f(4) -# define FMT_GEN6(f) FMT_GEN5(f), f(5) -# define FMT_GEN7(f) FMT_GEN6(f), f(6) -# define FMT_GEN8(f) FMT_GEN7(f), f(7) -# define FMT_GEN9(f) FMT_GEN8(f), f(8) -# define FMT_GEN10(f) FMT_GEN9(f), f(9) -# define FMT_GEN11(f) FMT_GEN10(f), f(10) -# define FMT_GEN12(f) FMT_GEN11(f), f(11) -# define FMT_GEN13(f) FMT_GEN12(f), f(12) -# define FMT_GEN14(f) FMT_GEN13(f), f(13) -# define FMT_GEN15(f) FMT_GEN14(f), f(14) - -namespace internal { -inline uint64_t make_type() { return 0; } - template -inline uint64_t make_type(const T &arg) { - return MakeValue< BasicFormatter >::type(arg); -} +using is_integer = + bool_constant::value && !std::is_same::value && + !std::is_same::value && + !std::is_same::value>; -template -struct ArgArray; - -template -struct ArgArray { - typedef Value Type[N > 0 ? N : 1]; +template class width_checker { + public: + explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {} - template - static Value make(const T &value) { -#ifdef __clang__ - Value result = MakeValue(value); - // Workaround a bug in Apple LLVM version 4.2 (clang-425.0.28) of clang: - // https://github.com/fmtlib/fmt/issues/276 - (void)result.custom.format; - return result; -#else - return MakeValue(value); -#endif + template ::value)> + FMT_CONSTEXPR unsigned long long operator()(T value) { + if (is_negative(value)) handler_.on_error("negative width"); + return static_cast(value); } -}; -template -struct ArgArray { - typedef Arg Type[N + 1]; // +1 for the list end Arg::NONE + template ::value)> + FMT_CONSTEXPR unsigned long long operator()(T) { + handler_.on_error("width is not integer"); + return 0; + } - template - static Arg make(const T &value) { return MakeArg(value); } + private: + ErrorHandler& handler_; }; -#if FMT_USE_VARIADIC_TEMPLATES -template -inline uint64_t make_type(const Arg &first, const Args & ... tail) { - return make_type(first) | (make_type(tail...) << 4); -} - -#else +template class precision_checker { + public: + explicit FMT_CONSTEXPR precision_checker(ErrorHandler& eh) : handler_(eh) {} -struct ArgType { - uint64_t type; + template ::value)> + FMT_CONSTEXPR unsigned long long operator()(T value) { + if (is_negative(value)) handler_.on_error("negative precision"); + return static_cast(value); + } - ArgType() : type(0) {} + template ::value)> + FMT_CONSTEXPR unsigned long long operator()(T) { + handler_.on_error("precision is not integer"); + return 0; + } - template - ArgType(const T &arg) : type(make_type(arg)) {} + private: + ErrorHandler& handler_; }; -# define FMT_ARG_TYPE_DEFAULT(n) ArgType t##n = ArgType() +// A format specifier handler that sets fields in basic_format_specs. +template class specs_setter { + public: + explicit FMT_CONSTEXPR specs_setter(basic_format_specs& specs) + : specs_(specs) {} -inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) { - return t0.type | (t1.type << 4) | (t2.type << 8) | (t3.type << 12) | - (t4.type << 16) | (t5.type << 20) | (t6.type << 24) | (t7.type << 28) | - (t8.type << 32) | (t9.type << 36) | (t10.type << 40) | (t11.type << 44) | - (t12.type << 48) | (t13.type << 52) | (t14.type << 56); -} -#endif -} // namespace internal - -# define FMT_MAKE_TEMPLATE_ARG(n) typename T##n -# define FMT_MAKE_ARG_TYPE(n) T##n -# define FMT_MAKE_ARG(n) const T##n &v##n -# define FMT_ASSIGN_char(n) \ - arr[n] = fmt::internal::MakeValue< fmt::BasicFormatter >(v##n) -# define FMT_ASSIGN_wchar_t(n) \ - arr[n] = fmt::internal::MakeValue< fmt::BasicFormatter >(v##n) - -#if FMT_USE_VARIADIC_TEMPLATES -// Defines a variadic function returning void. -# define FMT_VARIADIC_VOID(func, arg_type) \ - template \ - void func(arg_type arg0, const Args & ... args) { \ - typedef fmt::internal::ArgArray ArgArray; \ - typename ArgArray::Type array{ \ - ArgArray::template make >(args)...}; \ - func(arg0, fmt::ArgList(fmt::internal::make_type(args...), array)); \ - } - -// Defines a variadic constructor. -# define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \ - template \ - ctor(arg0_type arg0, arg1_type arg1, const Args & ... args) { \ - typedef fmt::internal::ArgArray ArgArray; \ - typename ArgArray::Type array{ \ - ArgArray::template make >(args)...}; \ - func(arg0, arg1, fmt::ArgList(fmt::internal::make_type(args...), array)); \ - } + FMT_CONSTEXPR specs_setter(const specs_setter& other) + : specs_(other.specs_) {} -#else + FMT_CONSTEXPR void on_align(align_t align) { specs_.align = align; } + FMT_CONSTEXPR void on_fill(basic_string_view fill) { + specs_.fill = fill; + } + FMT_CONSTEXPR void on_plus() { specs_.sign = sign::plus; } + FMT_CONSTEXPR void on_minus() { specs_.sign = sign::minus; } + FMT_CONSTEXPR void on_space() { specs_.sign = sign::space; } + FMT_CONSTEXPR void on_hash() { specs_.alt = true; } -# define FMT_MAKE_REF(n) \ - fmt::internal::MakeValue< fmt::BasicFormatter >(v##n) -# define FMT_MAKE_REF2(n) v##n - -// Defines a wrapper for a function taking one argument of type arg_type -// and n additional arguments of arbitrary types. -# define FMT_WRAP1(func, arg_type, n) \ - template \ - inline void func(arg_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ - const fmt::internal::ArgArray::Type array = {FMT_GEN(n, FMT_MAKE_REF)}; \ - func(arg1, fmt::ArgList( \ - fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), array)); \ - } - -// Emulates a variadic function returning void on a pre-C++11 compiler. -# define FMT_VARIADIC_VOID(func, arg_type) \ - inline void func(arg_type arg) { func(arg, fmt::ArgList()); } \ - FMT_WRAP1(func, arg_type, 1) FMT_WRAP1(func, arg_type, 2) \ - FMT_WRAP1(func, arg_type, 3) FMT_WRAP1(func, arg_type, 4) \ - FMT_WRAP1(func, arg_type, 5) FMT_WRAP1(func, arg_type, 6) \ - FMT_WRAP1(func, arg_type, 7) FMT_WRAP1(func, arg_type, 8) \ - FMT_WRAP1(func, arg_type, 9) FMT_WRAP1(func, arg_type, 10) - -# define FMT_CTOR(ctor, func, arg0_type, arg1_type, n) \ - template \ - ctor(arg0_type arg0, arg1_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ - const fmt::internal::ArgArray::Type array = {FMT_GEN(n, FMT_MAKE_REF)}; \ - func(arg0, arg1, fmt::ArgList( \ - fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), array)); \ - } - -// Emulates a variadic constructor on a pre-C++11 compiler. -# define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 1) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 2) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 3) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 4) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 5) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 6) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 7) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 8) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 9) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 10) -#endif + FMT_CONSTEXPR void on_zero() { + specs_.align = align::numeric; + specs_.fill[0] = Char('0'); + } -// Generates a comma-separated list with results of applying f to pairs -// (argument, index). -#define FMT_FOR_EACH1(f, x0) f(x0, 0) -#define FMT_FOR_EACH2(f, x0, x1) \ - FMT_FOR_EACH1(f, x0), f(x1, 1) -#define FMT_FOR_EACH3(f, x0, x1, x2) \ - FMT_FOR_EACH2(f, x0 ,x1), f(x2, 2) -#define FMT_FOR_EACH4(f, x0, x1, x2, x3) \ - FMT_FOR_EACH3(f, x0, x1, x2), f(x3, 3) -#define FMT_FOR_EACH5(f, x0, x1, x2, x3, x4) \ - FMT_FOR_EACH4(f, x0, x1, x2, x3), f(x4, 4) -#define FMT_FOR_EACH6(f, x0, x1, x2, x3, x4, x5) \ - FMT_FOR_EACH5(f, x0, x1, x2, x3, x4), f(x5, 5) -#define FMT_FOR_EACH7(f, x0, x1, x2, x3, x4, x5, x6) \ - FMT_FOR_EACH6(f, x0, x1, x2, x3, x4, x5), f(x6, 6) -#define FMT_FOR_EACH8(f, x0, x1, x2, x3, x4, x5, x6, x7) \ - FMT_FOR_EACH7(f, x0, x1, x2, x3, x4, x5, x6), f(x7, 7) -#define FMT_FOR_EACH9(f, x0, x1, x2, x3, x4, x5, x6, x7, x8) \ - FMT_FOR_EACH8(f, x0, x1, x2, x3, x4, x5, x6, x7), f(x8, 8) -#define FMT_FOR_EACH10(f, x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) \ - FMT_FOR_EACH9(f, x0, x1, x2, x3, x4, x5, x6, x7, x8), f(x9, 9) + FMT_CONSTEXPR void on_width(int width) { specs_.width = width; } + FMT_CONSTEXPR void on_precision(int precision) { + specs_.precision = precision; + } + FMT_CONSTEXPR void end_precision() {} -/** - An error returned by an operating system or a language runtime, - for example a file opening error. -*/ -class SystemError : public internal::RuntimeError { - private: - FMT_API void init(int err_code, CStringRef format_str, ArgList args); + FMT_CONSTEXPR void on_type(Char type) { + specs_.type = static_cast(type); + } protected: - int error_code_; - - typedef char Char; // For FMT_VARIADIC_CTOR. - - SystemError() {} + basic_format_specs& specs_; +}; +template class numeric_specs_checker { public: - /** - \rst - Constructs a :class:`fmt::SystemError` object with a description - formatted with `fmt::format_system_error`. *message* and additional - arguments passed into the constructor are formatted similarly to - `fmt::format`. + FMT_CONSTEXPR numeric_specs_checker(ErrorHandler& eh, detail::type arg_type) + : error_handler_(eh), arg_type_(arg_type) {} - **Example**:: + FMT_CONSTEXPR void require_numeric_argument() { + if (!is_arithmetic_type(arg_type_)) + error_handler_.on_error("format specifier requires numeric argument"); + } - // This throws a SystemError with the description - // cannot open file 'madeup': No such file or directory - // or similar (system message may vary). - const char *filename = "madeup"; - std::FILE *file = std::fopen(filename, "r"); - if (!file) - throw fmt::SystemError(errno, "cannot open file '{}'", filename); - \endrst - */ - SystemError(int error_code, CStringRef message) { - init(error_code, message, ArgList()); + FMT_CONSTEXPR void check_sign() { + require_numeric_argument(); + if (is_integral_type(arg_type_) && arg_type_ != type::int_type && + arg_type_ != type::long_long_type && arg_type_ != type::char_type) { + error_handler_.on_error("format specifier requires signed argument"); + } } - FMT_DEFAULTED_COPY_CTOR(SystemError) - FMT_VARIADIC_CTOR(SystemError, init, int, CStringRef) - FMT_API ~SystemError() FMT_DTOR_NOEXCEPT; + FMT_CONSTEXPR void check_precision() { + if (is_integral_type(arg_type_) || arg_type_ == type::pointer_type) + error_handler_.on_error("precision not allowed for this argument type"); + } - int error_code() const { return error_code_; } + private: + ErrorHandler& error_handler_; + detail::type arg_type_; }; -/** - \rst - Formats an error returned by an operating system or a language runtime, - for example a file opening error, and writes it to *out* in the following - form: - - .. parsed-literal:: - **: ** - - where ** is the passed message and ** is - the system message corresponding to the error code. - *error_code* is a system error code as given by ``errno``. - If *error_code* is not a valid error code such as -1, the system message - may look like "Unknown error -1" and is platform-dependent. - \endrst - */ -FMT_API void format_system_error(fmt::Writer &out, int error_code, - fmt::StringRef message) FMT_NOEXCEPT; - -/** - \rst - This template provides operations for formatting and writing data into - a character stream. The output is stored in a buffer provided by a subclass - such as :class:`fmt::BasicMemoryWriter`. - - You can use one of the following typedefs for common character types: - - +---------+----------------------+ - | Type | Definition | - +=========+======================+ - | Writer | BasicWriter | - +---------+----------------------+ - | WWriter | BasicWriter | - +---------+----------------------+ - - \endrst - */ -template -class BasicWriter { +// A format specifier handler that checks if specifiers are consistent with the +// argument type. +template class specs_checker : public Handler { private: - // Output buffer. - Buffer &buffer_; + numeric_specs_checker checker_; - FMT_DISALLOW_COPY_AND_ASSIGN(BasicWriter); + // Suppress an MSVC warning about using this in initializer list. + FMT_CONSTEXPR Handler& error_handler() { return *this; } - typedef typename internal::CharTraits::CharPtr CharPtr; + public: + FMT_CONSTEXPR specs_checker(const Handler& handler, detail::type arg_type) + : Handler(handler), checker_(error_handler(), arg_type) {} -#if FMT_SECURE_SCL - // Returns pointer value. - static Char *get(CharPtr p) { return p.base(); } -#else - static Char *get(Char *p) { return p; } -#endif + FMT_CONSTEXPR specs_checker(const specs_checker& other) + : Handler(other), checker_(error_handler(), other.arg_type_) {} - // Fills the padding around the content and returns the pointer to the - // content area. - static CharPtr fill_padding(CharPtr buffer, - unsigned total_size, std::size_t content_size, wchar_t fill); + FMT_CONSTEXPR void on_align(align_t align) { + if (align == align::numeric) checker_.require_numeric_argument(); + Handler::on_align(align); + } - // Grows the buffer by n characters and returns a pointer to the newly - // allocated area. - CharPtr grow_buffer(std::size_t n) { - std::size_t size = buffer_.size(); - buffer_.resize(size + n); - return internal::make_ptr(&buffer_[size], n); + FMT_CONSTEXPR void on_plus() { + checker_.check_sign(); + Handler::on_plus(); } - // Writes an unsigned decimal integer. - template - Char *write_unsigned_decimal(UInt value, unsigned prefix_size = 0) { - unsigned num_digits = internal::count_digits(value); - Char *ptr = get(grow_buffer(prefix_size + num_digits)); - internal::format_decimal(ptr + prefix_size, value, num_digits); - return ptr; + FMT_CONSTEXPR void on_minus() { + checker_.check_sign(); + Handler::on_minus(); } - // Writes a decimal integer. - template - void write_decimal(Int value) { - typedef typename internal::IntTraits::MainType MainType; - MainType abs_value = static_cast(value); - if (internal::is_negative(value)) { - abs_value = 0 - abs_value; - *write_unsigned_decimal(abs_value, 1) = '-'; - } else { - write_unsigned_decimal(abs_value, 0); - } + FMT_CONSTEXPR void on_space() { + checker_.check_sign(); + Handler::on_space(); } - // Prepare a buffer for integer formatting. - CharPtr prepare_int_buffer(unsigned num_digits, - const EmptySpec &, const char *prefix, unsigned prefix_size) { - unsigned size = prefix_size + num_digits; - CharPtr p = grow_buffer(size); - std::uninitialized_copy(prefix, prefix + prefix_size, p); - return p + size - 1; + FMT_CONSTEXPR void on_hash() { + checker_.require_numeric_argument(); + Handler::on_hash(); } - template - CharPtr prepare_int_buffer(unsigned num_digits, - const Spec &spec, const char *prefix, unsigned prefix_size); + FMT_CONSTEXPR void on_zero() { + checker_.require_numeric_argument(); + Handler::on_zero(); + } - // Formats an integer. - template - void write_int(T value, Spec spec); + FMT_CONSTEXPR void end_precision() { checker_.check_precision(); } +}; - // Formats a floating-point number (double or long double). - template - void write_double(T value, const Spec &spec); +template