Compare commits
28 Commits
6dfd957ce9
...
v2.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 422aa152ff | |||
| 92ce0e29cb | |||
| a0f032c28b | |||
| b51ded2101 | |||
| 09f07d99f7 | |||
| c19561cb54 | |||
| 9ad199073a | |||
| 908d48a7c1 | |||
| aa6dd3031a | |||
| 19df293463 | |||
| 71eb0741f6 | |||
| 09fea7e0a3 | |||
| aecf9bb8cc | |||
| 6449ae1977 | |||
| 1c1e709ed1 | |||
| fe4193efa7 | |||
| 746d20a835 | |||
| 8989e909ad | |||
| 718fe426bf | |||
| 1a4074fd98 | |||
| 74027e7297 | |||
| 044c04aa07 | |||
| e161dafac5 | |||
| 4d9487813b | |||
| 7a34057836 | |||
| 17053f4ebf | |||
| de2b927a14 | |||
| a50233ab6e |
4
.github/scripts/README.md
vendored
Normal file
4
.github/scripts/README.md
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# GitHub Action Scripts
|
||||||
|
|
||||||
|
These script files are only used for GitHub Action.
|
||||||
|
These script files should only be executed in their root directory respectively.
|
||||||
17
.github/scripts/gbenchmark/linux.sh
vendored
Normal file
17
.github/scripts/gbenchmark/linux.sh
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Create build and install directory
|
||||||
|
mkdir build install
|
||||||
|
|
||||||
|
# Build project
|
||||||
|
cd build
|
||||||
|
cmake -DCMAKE_CXX_STANDARD=23 -DBENCHMARK_ENABLE_TESTING=OFF -DCMAKE_BUILD_TYPE=Release ..
|
||||||
|
cmake --build .
|
||||||
|
cmake --install . --prefix=../install
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
# Record install directory
|
||||||
|
cd install
|
||||||
|
export benchmark_ROOT=$(pwd)
|
||||||
|
cd ..
|
||||||
17
.github/scripts/gbenchmark/macos.sh
vendored
Normal file
17
.github/scripts/gbenchmark/macos.sh
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Create build and install directory
|
||||||
|
mkdir build install
|
||||||
|
|
||||||
|
# Build project
|
||||||
|
cd build
|
||||||
|
cmake -DCMAKE_CXX_STANDARD=23 -DBENCHMARK_ENABLE_TESTING=OFF -DCMAKE_BUILD_TYPE=Release ..
|
||||||
|
cmake --build .
|
||||||
|
cmake --install . --prefix=../install
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
# Record install directory
|
||||||
|
cd install
|
||||||
|
export benchmark_ROOT=$(pwd)
|
||||||
|
cd ..
|
||||||
17
.github/scripts/gbenchmark/windows.bat
vendored
Normal file
17
.github/scripts/gbenchmark/windows.bat
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
@ECHO OFF
|
||||||
|
|
||||||
|
:: Create build and install directory
|
||||||
|
MKDIR build
|
||||||
|
MKDIR install
|
||||||
|
|
||||||
|
:: Build project
|
||||||
|
CD build
|
||||||
|
cmake -A x64 -DCMAKE_CXX_STANDARD=23 -DBENCHMARK_ENABLE_TESTING=OFF ..
|
||||||
|
cmake --build . --config Release
|
||||||
|
cmake --install . --prefix=../install --config Release
|
||||||
|
CD ..
|
||||||
|
|
||||||
|
:: Record install directory
|
||||||
|
CD install
|
||||||
|
SET benchmark_ROOT=%CD%
|
||||||
|
CD ..
|
||||||
17
.github/scripts/gtest/linux.sh
vendored
Normal file
17
.github/scripts/gtest/linux.sh
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Create build and install directory
|
||||||
|
mkdir build install
|
||||||
|
|
||||||
|
# Build project
|
||||||
|
cd build
|
||||||
|
cmake -DCMAKE_CXX_STANDARD=23 -Dgtest_force_shared_crt=ON -DCMAKE_BUILD_TYPE=Release ..
|
||||||
|
cmake --build .
|
||||||
|
cmake --install . --prefix=../install
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
# Record install directory
|
||||||
|
cd install
|
||||||
|
export GTest_ROOT=$(pwd)
|
||||||
|
cd ..
|
||||||
17
.github/scripts/gtest/macos.sh
vendored
Normal file
17
.github/scripts/gtest/macos.sh
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Create build and install directory
|
||||||
|
mkdir build install
|
||||||
|
|
||||||
|
# Build project
|
||||||
|
cd build
|
||||||
|
cmake -DCMAKE_CXX_STANDARD=23 -Dgtest_force_shared_crt=ON -DCMAKE_BUILD_TYPE=Release ..
|
||||||
|
cmake --build .
|
||||||
|
cmake --install . --prefix=../install
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
# Record install directory
|
||||||
|
cd install
|
||||||
|
export GTest_ROOT=$(pwd)
|
||||||
|
cd ..
|
||||||
17
.github/scripts/gtest/windows.bat
vendored
Normal file
17
.github/scripts/gtest/windows.bat
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
@ECHO OFF
|
||||||
|
|
||||||
|
:: Create build and install directory
|
||||||
|
MKDIR build
|
||||||
|
MKDIR install
|
||||||
|
|
||||||
|
:: Build project
|
||||||
|
CD build
|
||||||
|
cmake -A x64 -DCMAKE_CXX_STANDARD=23 -Dgtest_force_shared_crt=ON ..
|
||||||
|
cmake --build . --config Release
|
||||||
|
cmake --install . --prefix=../install --config Release
|
||||||
|
CD ..
|
||||||
|
|
||||||
|
:: Record install directory
|
||||||
|
CD install
|
||||||
|
SET GTest_ROOT=%CD%
|
||||||
|
CD ..
|
||||||
19
.github/scripts/linux.sh
vendored
Normal file
19
.github/scripts/linux.sh
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Create build directory and enter it
|
||||||
|
mkdir bin
|
||||||
|
cd bin
|
||||||
|
# Create internal build and install directory, then enter it
|
||||||
|
mkdir build
|
||||||
|
mkdir install
|
||||||
|
|
||||||
|
# Build in Release mode
|
||||||
|
cd build
|
||||||
|
cmake -DCMAKE_BUILD_TYPE=Release -DYYCC_BUILD_TEST=ON -DGTest_ROOT=$GTest_ROOT -DYYCC_BUILD_BENCHMARK=ON -Dbenchmark_ROOT=$benchmark_ROOT ../..
|
||||||
|
cmake --build .
|
||||||
|
cmake --install . --prefix=../install
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
# Back to root directory
|
||||||
|
cd ..
|
||||||
19
.github/scripts/macos.sh
vendored
Normal file
19
.github/scripts/macos.sh
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Create build directory and enter it
|
||||||
|
mkdir bin
|
||||||
|
cd bin
|
||||||
|
# Create internal build and install directory, then enter it
|
||||||
|
mkdir build
|
||||||
|
mkdir install
|
||||||
|
|
||||||
|
# Build in Release mode
|
||||||
|
cd build
|
||||||
|
cmake -DCMAKE_BUILD_TYPE=Release -DYYCC_BUILD_TEST=ON -DGTest_ROOT=$GTest_ROOT -DYYCC_BUILD_BENCHMARK=ON -Dbenchmark_ROOT=$benchmark_ROOT ../..
|
||||||
|
cmake --build .
|
||||||
|
cmake --install . --prefix=../install
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
# Back to root directory
|
||||||
|
cd ..
|
||||||
18
.github/scripts/windows.bat
vendored
Normal file
18
.github/scripts/windows.bat
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
@ECHO OFF
|
||||||
|
|
||||||
|
:: Create build directory and enter it
|
||||||
|
MKDIR bin
|
||||||
|
CD bin
|
||||||
|
:: Create internal build and install directory, then enter it
|
||||||
|
MKDIR build
|
||||||
|
MKDIR install
|
||||||
|
|
||||||
|
:: Build with x64 architecture in Release mode
|
||||||
|
CD build
|
||||||
|
cmake -A x64 -DYYCC_BUILD_TEST=ON -DGTest_ROOT=%GTest_ROOT% -DYYCC_BUILD_BENCHMARK=ON -Dbenchmark_ROOT=%benchmark_ROOT% ../..
|
||||||
|
cmake --build . --config Release
|
||||||
|
cmake --install . --prefix=../install --config Release
|
||||||
|
CD ..
|
||||||
|
|
||||||
|
:: Back to root directory
|
||||||
|
CD ..
|
||||||
69
.github/workflows/linux.yml
vendored
69
.github/workflows/linux.yml
vendored
@@ -10,35 +10,54 @@ jobs:
|
|||||||
- name: Checkout Repository
|
- name: Checkout Repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
sudo apt update
|
sudo apt update
|
||||||
sudo apt install -y build-essential cmake git
|
sudo apt install -y build-essential cmake git
|
||||||
# - name: Setup Google Test and Google Benchmark
|
- name: Fetch Google Test
|
||||||
# run: |
|
uses: actions/checkout@v4
|
||||||
# # Setup Google Test
|
with:
|
||||||
# git clone -b v1.17.0 https://github.com/google/googletest.git external/googletest
|
repository: 'google/googletest'
|
||||||
# cd external/googletest
|
ref: 'v1.17.0'
|
||||||
# mkdir build install
|
path: 'extern/googletest'
|
||||||
# cd build
|
- name: Build Google Test
|
||||||
# cmake -DCMAKE_CXX_STANDARD=23 -Dgtest_force_shared_crt=ON -DCMAKE_BUILD_TYPE=Release ..
|
shell: bash
|
||||||
# cmake --build .
|
|
||||||
# cmake --install . --prefix=../install
|
|
||||||
# cd ../..
|
|
||||||
# # Setup Google Benchmark
|
|
||||||
# git clone -b v1.9.4 https://github.com/google/benchmark.git external/benchmark
|
|
||||||
# cd external/benchmark
|
|
||||||
# # Create symlink to googletest as required by benchmark
|
|
||||||
# ln -s ../googletest googletest
|
|
||||||
# mkdir build install
|
|
||||||
# cd build
|
|
||||||
# cmake -DCMAKE_CXX_STANDARD=23 -DBENCHMARK_ENABLE_TESTING=OFF -DCMAKE_BUILD_TYPE=Release ..
|
|
||||||
# cmake --build .
|
|
||||||
# cmake --install . --prefix=../install
|
|
||||||
# cd ../..
|
|
||||||
- name: Build YYCC
|
|
||||||
run: |
|
run: |
|
||||||
chmod +x ./.github/linux_build.sh
|
cd extern/googletest
|
||||||
./.github/linux_build.sh
|
# Build Google Test
|
||||||
|
source ../../.github/scripts/gtest/linux.sh
|
||||||
|
# Record environment variable
|
||||||
|
echo "GTest_ROOT=$GTest_ROOT" >> "$GITHUB_ENV"
|
||||||
|
cd ../..
|
||||||
|
- name: Fetch Google Benchmark
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: 'google/benchmark'
|
||||||
|
ref: 'v1.9.4'
|
||||||
|
path: 'extern/benchmark'
|
||||||
|
- name: Build Google Benchmark
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cd extern/benchmark
|
||||||
|
# Create symlink to googletest as required by benchmark
|
||||||
|
ln -s ../googletest googletest
|
||||||
|
# Build Google Benchmark
|
||||||
|
source ../../.github/scripts/gbenchmark/linux.sh
|
||||||
|
# Record environment variable
|
||||||
|
echo "benchmark_ROOT=$benchmark_ROOT" >> "$GITHUB_ENV"
|
||||||
|
cd ../..
|
||||||
|
- name: Build YYCC
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
source ./.github/scripts/linux.sh
|
||||||
|
- name: Run YYCC Test
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
./bin/install/bin/YYCCTest
|
||||||
|
- name: Run YYCC Benchmark
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
./bin/install/bin/YYCCBenchmark
|
||||||
- name: Upload Built Artifact
|
- name: Upload Built Artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
68
.github/workflows/macos.yml
vendored
68
.github/workflows/macos.yml
vendored
@@ -9,32 +9,50 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout Repository
|
- name: Checkout Repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
# - name: Setup Google Test and Google Benchmark
|
- name: Fetch Google Test
|
||||||
# run: |
|
uses: actions/checkout@v4
|
||||||
# # Setup Google Test
|
with:
|
||||||
# git clone -b v1.17.0 https://github.com/google/googletest.git external/googletest
|
repository: 'google/googletest'
|
||||||
# cd external/googletest
|
ref: 'v1.17.0'
|
||||||
# mkdir build install
|
path: 'extern/googletest'
|
||||||
# cd build
|
- name: Build Google Test
|
||||||
# cmake -DCMAKE_CXX_STANDARD=23 -Dgtest_force_shared_crt=ON -DCMAKE_BUILD_TYPE=Release ..
|
shell: bash
|
||||||
# cmake --build .
|
|
||||||
# cmake --install . --prefix=../install
|
|
||||||
# cd ../..
|
|
||||||
# # Setup Google Benchmark
|
|
||||||
# git clone -b v1.9.4 https://github.com/google/benchmark.git external/benchmark
|
|
||||||
# cd external/benchmark
|
|
||||||
# # Create symlink to googletest as required by benchmark
|
|
||||||
# ln -s ../googletest googletest
|
|
||||||
# mkdir build install
|
|
||||||
# cd build
|
|
||||||
# cmake -DCMAKE_CXX_STANDARD=23 -DBENCHMARK_ENABLE_TESTING=OFF -DCMAKE_BUILD_TYPE=Release ..
|
|
||||||
# cmake --build .
|
|
||||||
# cmake --install . --prefix=../install
|
|
||||||
# cd ../..
|
|
||||||
- name: Build YYCC
|
|
||||||
run: |
|
run: |
|
||||||
chmod +x ./.github/macos_build.sh
|
cd extern/googletest
|
||||||
./.github/macos_build.sh
|
# Build Google Test
|
||||||
|
source ../../.github/scripts/gtest/macos.sh
|
||||||
|
# Record environment variable
|
||||||
|
echo "GTest_ROOT=$GTest_ROOT" >> "$GITHUB_ENV"
|
||||||
|
cd ../..
|
||||||
|
- name: Fetch Google Benchmark
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: 'google/benchmark'
|
||||||
|
ref: 'v1.9.4'
|
||||||
|
path: 'extern/benchmark'
|
||||||
|
- name: Build Google Benchmark
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cd extern/benchmark
|
||||||
|
# Create symlink to googletest as required by benchmark
|
||||||
|
ln -s ../googletest googletest
|
||||||
|
# Build Google Benchmark
|
||||||
|
source ../../.github/scripts/gbenchmark/macos.sh
|
||||||
|
# Record environment variable
|
||||||
|
echo "benchmark_ROOT=$benchmark_ROOT" >> "$GITHUB_ENV"
|
||||||
|
cd ../..
|
||||||
|
- name: Build YYCC
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
source ./.github/scripts/macos.sh
|
||||||
|
- name: Run YYCC Test
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
./bin/install/bin/YYCCTest
|
||||||
|
- name: Run YYCC Benchmark
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
./bin/install/bin/YYCCBenchmark
|
||||||
- name: Upload Built Artifact
|
- name: Upload Built Artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
69
.github/workflows/windows.yml
vendored
69
.github/workflows/windows.yml
vendored
@@ -16,36 +16,59 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout Repository
|
- name: Checkout Repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
# - name: Setup Google Test and Google Benchmark
|
- name: Fetch Google Test
|
||||||
# run: |
|
uses: actions/checkout@v4
|
||||||
# # Setup Google Test
|
with:
|
||||||
# git clone -b v1.17.0 https://github.com/google/googletest.git external/googletest
|
repository: 'google/googletest'
|
||||||
# cd external/googletest
|
ref: 'v1.17.0'
|
||||||
# mkdir build install
|
path: 'extern/googletest'
|
||||||
# cd build
|
- name: Build Google Test
|
||||||
# cmake -DCMAKE_CXX_STANDARD=23 -Dgtest_force_shared_crt=ON -DCMAKE_BUILD_TYPE=Release ..
|
shell: cmd
|
||||||
# cmake --build . --config Release
|
run: |
|
||||||
# cmake --install . --prefix=../install --config Release
|
CD extern\googletest
|
||||||
# cd ../..
|
:: Build Google Test
|
||||||
# # Setup Google Benchmark
|
CALL ..\..\.github\scripts\gtest\windows.bat
|
||||||
# git clone -b v1.9.4 https://github.com/google/benchmark.git external/benchmark
|
:: Idk why I can't use $GITHUB_ENV, so I use this stupid way to do this.
|
||||||
# cd external/benchmark
|
:: This is first entry so we override it.
|
||||||
# # Create symlink to googletest as required by benchmark
|
ECHO SET GTest_ROOT=%GTest_ROOT% > ..\envs.bat
|
||||||
# mklink /D googletest ../googletest
|
CD ..\..
|
||||||
# mkdir build install
|
- name: Fetch Google Benchmark
|
||||||
# cd build
|
uses: actions/checkout@v4
|
||||||
# cmake -DCMAKE_CXX_STANDARD=23 -DBENCHMARK_ENABLE_TESTING=OFF -DCMAKE_BUILD_TYPE=Release ..
|
with:
|
||||||
# cmake --build . --config Release
|
repository: 'google/benchmark'
|
||||||
# cmake --install . --prefix=../install --config Release
|
ref: 'v1.9.4'
|
||||||
# cd ../..
|
path: 'extern/benchmark'
|
||||||
|
- name: Build Google Benchmark
|
||||||
|
shell: cmd
|
||||||
|
run: |
|
||||||
|
CD extern\benchmark
|
||||||
|
:: Create symlink to googletest as required by benchmark
|
||||||
|
mklink /D googletest ..\googletest
|
||||||
|
:: Build Google Benchmark
|
||||||
|
CALL ..\..\.github\scripts\gbenchmark\windows.bat
|
||||||
|
:: This is second entry so we append it.
|
||||||
|
ECHO SET benchmark_ROOT=%benchmark_ROOT% >> ..\envs.bat
|
||||||
|
CD ..\..
|
||||||
- name: Build YYCC
|
- name: Build YYCC
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
|
:: Prepare Visual Studio
|
||||||
set VS=${{ matrix.vs }}
|
set VS=${{ matrix.vs }}
|
||||||
set VCVARS="C:\Program Files (x86)\Microsoft Visual Studio\%VS%\Enterprise\VC\Auxiliary\Build\vcvarsall.bat"
|
set VCVARS="C:\Program Files (x86)\Microsoft Visual Studio\%VS%\Enterprise\VC\Auxiliary\Build\vcvarsall.bat"
|
||||||
if not exist %VCVARS% set VCVARS="C:\Program Files\Microsoft Visual Studio\%VS%\Enterprise\VC\Auxiliary\Build\vcvarsall.bat"
|
if not exist %VCVARS% set VCVARS="C:\Program Files\Microsoft Visual Studio\%VS%\Enterprise\VC\Auxiliary\Build\vcvarsall.bat"
|
||||||
call %VCVARS% ${{ matrix.msvc_arch }}
|
call %VCVARS% ${{ matrix.msvc_arch }}
|
||||||
.\.github\windows_build.bat
|
:: Extract saved environment variables
|
||||||
|
CALL .\extern\envs.bat
|
||||||
|
:: Build Project
|
||||||
|
CALL .\.github\scripts\windows.bat
|
||||||
|
- name: Run YYCC Test
|
||||||
|
shell: cmd
|
||||||
|
run: |
|
||||||
|
.\bin\install\bin\YYCCTest.exe
|
||||||
|
- name: Run YYCC Benchmark
|
||||||
|
shell: cmd
|
||||||
|
run: |
|
||||||
|
.\bin\install\bin\YYCCBenchmark.exe
|
||||||
- name: Upload Built Artifact
|
- name: Upload Built Artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,6 +3,7 @@
|
|||||||
out/
|
out/
|
||||||
build/
|
build/
|
||||||
install/
|
install/
|
||||||
|
extern/
|
||||||
|
|
||||||
# Ignore CMake generated stuff
|
# Ignore CMake generated stuff
|
||||||
src/yycc/version.hpp
|
src/yycc/version.hpp
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ set(YYCC_INSTALL_BIN_PATH ${CMAKE_INSTALL_BINDIR} CACHE PATH
|
|||||||
set(YYCC_INSTALL_DOC_PATH ${CMAKE_INSTALL_DOCDIR} CACHE PATH
|
set(YYCC_INSTALL_DOC_PATH ${CMAKE_INSTALL_DOCDIR} CACHE PATH
|
||||||
"Non-arch doc install path relative to CMAKE_INSTALL_PREFIX unless set to an absolute path.")
|
"Non-arch doc install path relative to CMAKE_INSTALL_PREFIX unless set to an absolute path.")
|
||||||
|
|
||||||
|
# Test charconv support due to shitty clang's libcxx.
|
||||||
|
include(${CMAKE_CURRENT_LIST_DIR}/cmake/check_charconv.cmake)
|
||||||
|
|
||||||
# Include dependency.
|
# Include dependency.
|
||||||
# GTest is required if we build test
|
# GTest is required if we build test
|
||||||
if (YYCC_BUILD_TEST)
|
if (YYCC_BUILD_TEST)
|
||||||
@@ -37,6 +40,10 @@ endif ()
|
|||||||
if (YYCC_BUILD_BENCHMARK)
|
if (YYCC_BUILD_BENCHMARK)
|
||||||
find_package(benchmark REQUIRED)
|
find_package(benchmark REQUIRED)
|
||||||
endif ()
|
endif ()
|
||||||
|
# Doxygen is required if we build doc
|
||||||
|
if (YYCC_BUILD_DOC)
|
||||||
|
find_package(Doxygen REQUIRED)
|
||||||
|
endif ()
|
||||||
# Iconv is required if we are not in Windows or user request it
|
# Iconv is required if we are not in Windows or user request it
|
||||||
if (YYCC_ENFORCE_ICONV OR (NOT WIN32))
|
if (YYCC_ENFORCE_ICONV OR (NOT WIN32))
|
||||||
find_package(Iconv REQUIRED)
|
find_package(Iconv REQUIRED)
|
||||||
|
|||||||
26
COMPILE.md
26
COMPILE.md
@@ -27,10 +27,16 @@ So you actually do not need Google Test, Google Benchmark and Doxygen.
|
|||||||
### Compiler
|
### Compiler
|
||||||
|
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
> You may face some issues when building on macOS with Clang. That's not your fault.
|
> You may face some issues when building on macOS with Apple Clang. That's not your fault.
|
||||||
> Clang used libc++ library lacks some essential features used by this project.
|
> Clang and Apple Clang used libc++ library lacks some essential features used by this project.
|
||||||
> A possible solution is that use GCC and libstdc++ on macOS instead of default Clang and libc++.
|
> This is especially not good for Apple Clang because Apple Clang is usually behind Clang a bunch of versions.
|
||||||
> Build issue may be resolved until libc++ finish these features: `std::stacktrace` and `std::views::enumerate`.
|
>
|
||||||
|
> For resolving this issue, I have written a series of patch header files for libcxx and you can find them in include directory.
|
||||||
|
> This project should be compiled on macOS but everything has exception.
|
||||||
|
> If you really have this issue, a possible solution is that use GCC and libstdc++ on macOS instead of default Clang and libc++.
|
||||||
|
>
|
||||||
|
> Build issue may be resolved until libc++ finish these features: complete `std::from_chars` and `std::to_chars`,
|
||||||
|
> `std::stacktrace` and `std::views::enumerate`.
|
||||||
|
|
||||||
### Google Test
|
### Google Test
|
||||||
|
|
||||||
@@ -106,7 +112,13 @@ If you are a developer (developer of this project, or use this project as depend
|
|||||||
|
|
||||||
"User Build" is basically how GitHub Action build this project.
|
"User Build" is basically how GitHub Action build this project.
|
||||||
|
|
||||||
Execute `.github/windows_build.bat` on Windows or `.github/linux_build.sh` on POSIX-like OS (Linux and macOS) under **the root directory** of this project. The final built artifact is under `bin/install` directory.
|
Under **the root directory** of this project, execute:
|
||||||
|
|
||||||
|
- `script/windows_build.bat` on Windows
|
||||||
|
- or `script/linux_build.sh` on Linux
|
||||||
|
- or `script/macos_build.sh` on macOS
|
||||||
|
|
||||||
|
The final built artifact is under `bin/install` directory.
|
||||||
|
|
||||||
### Developer Build
|
### Developer Build
|
||||||
|
|
||||||
@@ -127,9 +139,9 @@ Please note that generated documentation is different in different platforms.
|
|||||||
* `YYCC_ENFORCE_ICONV`: Set it to `ON` to enable Iconv feature forcely. `OFF` in default.
|
* `YYCC_ENFORCE_ICONV`: Set it to `ON` to enable Iconv feature forcely. `OFF` in default.
|
||||||
The usage of this option has been introduced in previous "Iconv" chapter.
|
The usage of this option has been introduced in previous "Iconv" chapter.
|
||||||
* `GTest_ROOT`: Set to the install path of Google Test
|
* `GTest_ROOT`: Set to the install path of Google Test
|
||||||
if you have enable `YYCC_BUILD_TEST` and want to use your personal built Google Test.
|
if you have enable `YYCC_BUILD_TEST` and want to use your personal built Google Test.
|
||||||
* `benchmark_ROOT`: Set to the install path of Google Benchmark
|
* `benchmark_ROOT`: Set to the install path of Google Benchmark
|
||||||
if you have enable `YYCC_BUILD_BENCHMARK` and want to use your personal built Google Benchmark.
|
if you have enable `YYCC_BUILD_BENCHMARK` and want to use your personal built Google Benchmark.
|
||||||
* `Iconv_ROOT`: The assistant variable for finding Iconv which is exposed by CMake.
|
* `Iconv_ROOT`: The assistant variable for finding Iconv which is exposed by CMake.
|
||||||
You usually do not need set it up.
|
You usually do not need set it up.
|
||||||
* `CMAKE_CXX_STANDARD`: Set C++ standard version of project.
|
* `CMAKE_CXX_STANDARD`: Set C++ standard version of project.
|
||||||
|
|||||||
0
script/.gitignore → asset/.gitignore
vendored
0
script/.gitignore → asset/.gitignore
vendored
0
script/uv.lock → asset/uv.lock
generated
0
script/uv.lock → asset/uv.lock
generated
@@ -26,3 +26,8 @@ PRIVATE
|
|||||||
YYCCommonplace
|
YYCCommonplace
|
||||||
benchmark::benchmark
|
benchmark::benchmark
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Install binary
|
||||||
|
install(TARGETS YYCCBenchmark
|
||||||
|
RUNTIME DESTINATION ${YYCC_INSTALL_BIN_PATH}
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
|
|
||||||
@PACKAGE_INIT@
|
@PACKAGE_INIT@
|
||||||
|
|
||||||
|
# Find Iconv if we have found it.
|
||||||
|
if ("@Iconv_FOUND@")
|
||||||
|
find_package(Iconv REQUIRED)
|
||||||
|
endif ()
|
||||||
|
|
||||||
# Include targets file
|
# Include targets file
|
||||||
include("${CMAKE_CURRENT_LIST_DIR}/YYCCommonplaceTargets.cmake")
|
include("${CMAKE_CURRENT_LIST_DIR}/YYCCommonplaceTargets.cmake")
|
||||||
|
|
||||||
check_required_components(YYCCommonplace)
|
check_required_components(YYCCommonplace)
|
||||||
|
|||||||
30
cmake/check_charconv.cmake
Normal file
30
cmake/check_charconv.cmake
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
message(STATUS "Checking charconv implementation...")
|
||||||
|
include(CheckCXXSourceCompiles)
|
||||||
|
|
||||||
|
file(READ "${CMAKE_CURRENT_LIST_DIR}/check_charconv/chars_format.cpp" TEST_CODE_SNIPPET)
|
||||||
|
check_cxx_source_compiles("${TEST_CODE_SNIPPET}" YYCC_CHARCONV_HAS_CHARS_FORMAT)
|
||||||
|
message(STATUS "Support std::chars_format: ${YYCC_CHARCONV_HAS_CHARS_FORMAT}")
|
||||||
|
|
||||||
|
file(READ "${CMAKE_CURRENT_LIST_DIR}/check_charconv/from_chars_result.cpp" TEST_CODE_SNIPPET)
|
||||||
|
check_cxx_source_compiles("${TEST_CODE_SNIPPET}" YYCC_CHARCONV_HAS_FROM_CHARS_RESULT)
|
||||||
|
message(STATUS "Support std::from_chars_result: ${YYCC_CHARCONV_HAS_FROM_CHARS_RESULT}")
|
||||||
|
|
||||||
|
file(READ "${CMAKE_CURRENT_LIST_DIR}/check_charconv/to_chars_result.cpp" TEST_CODE_SNIPPET)
|
||||||
|
check_cxx_source_compiles("${TEST_CODE_SNIPPET}" YYCC_CHARCONV_HAS_TO_CHARS_RESULT)
|
||||||
|
message(STATUS "Support std::to_chars_result: ${YYCC_CHARCONV_HAS_TO_CHARS_RESULT}")
|
||||||
|
|
||||||
|
file(READ "${CMAKE_CURRENT_LIST_DIR}/check_charconv/from_chars_int.cpp" TEST_CODE_SNIPPET)
|
||||||
|
check_cxx_source_compiles("${TEST_CODE_SNIPPET}" YYCC_CHARCONV_HAS_FROM_CHARS_INT)
|
||||||
|
message(STATUS "Support std::from_chars with integral type: ${YYCC_CHARCONV_HAS_FROM_CHARS_INT}")
|
||||||
|
|
||||||
|
file(READ "${CMAKE_CURRENT_LIST_DIR}/check_charconv/from_chars_float.cpp" TEST_CODE_SNIPPET)
|
||||||
|
check_cxx_source_compiles("${TEST_CODE_SNIPPET}" YYCC_CHARCONV_HAS_FROM_CHARS_FLOAT)
|
||||||
|
message(STATUS "Suppoer std::from_chars with float point type: ${YYCC_CHARCONV_HAS_FROM_CHARS_FLOAT}")
|
||||||
|
|
||||||
|
file(READ "${CMAKE_CURRENT_LIST_DIR}/check_charconv/to_chars_int.cpp" TEST_CODE_SNIPPET)
|
||||||
|
check_cxx_source_compiles("${TEST_CODE_SNIPPET}" YYCC_CHARCONV_HAS_TO_CHARS_INT)
|
||||||
|
message(STATUS "Support std::to_chars with integral type: ${YYCC_CHARCONV_HAS_TO_CHARS_INT}")
|
||||||
|
|
||||||
|
file(READ "${CMAKE_CURRENT_LIST_DIR}/check_charconv/to_chars_float.cpp" TEST_CODE_SNIPPET)
|
||||||
|
check_cxx_source_compiles("${TEST_CODE_SNIPPET}" YYCC_CHARCONV_HAS_TO_CHARS_FLOAT)
|
||||||
|
message(STATUS "Support std::to_chars with float point type: ${YYCC_CHARCONV_HAS_TO_CHARS_FLOAT}")
|
||||||
8
cmake/check_charconv/chars_format.cpp
Normal file
8
cmake/check_charconv/chars_format.cpp
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#include <charconv>
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
auto scientific = std::chars_format::scientific;
|
||||||
|
auto fixed = std::chars_format::fixed;
|
||||||
|
auto general = std::chars_format::general;
|
||||||
|
auto hex = std::chars_format::hex;
|
||||||
|
}
|
||||||
16
cmake/check_charconv/from_chars_float.cpp
Normal file
16
cmake/check_charconv/from_chars_float.cpp
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#include <charconv>
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
const char probe[] = "0.0";
|
||||||
|
const char* first = probe;
|
||||||
|
const char* last = first + sizeof(probe);
|
||||||
|
|
||||||
|
{
|
||||||
|
float value;
|
||||||
|
auto rv = std::from_chars(first, last, value, std::chars_format::general);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
double value;
|
||||||
|
auto rv = std::from_chars(first, last, value, std::chars_format::general);
|
||||||
|
}
|
||||||
|
}
|
||||||
41
cmake/check_charconv/from_chars_int.cpp
Normal file
41
cmake/check_charconv/from_chars_int.cpp
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#include <charconv>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
const char probe[] = "0";
|
||||||
|
const char* first = probe;
|
||||||
|
const char* last = first + sizeof(probe);
|
||||||
|
|
||||||
|
{
|
||||||
|
std::int8_t value;
|
||||||
|
auto rv = std::from_chars(first, last, value, 10);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::int16_t value;
|
||||||
|
auto rv = std::from_chars(first, last, value, 10);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::int32_t value;
|
||||||
|
auto rv = std::from_chars(first, last, value, 10);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::int64_t value;
|
||||||
|
auto rv = std::from_chars(first, last, value, 10);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::uint8_t value;
|
||||||
|
auto rv = std::from_chars(first, last, value, 10);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::uint16_t value;
|
||||||
|
auto rv = std::from_chars(first, last, value, 10);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::uint32_t value;
|
||||||
|
auto rv = std::from_chars(first, last, value, 10);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::uint64_t value;
|
||||||
|
auto rv = std::from_chars(first, last, value, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
9
cmake/check_charconv/from_chars_result.cpp
Normal file
9
cmake/check_charconv/from_chars_result.cpp
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#include <charconv>
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
std::from_chars_result result {
|
||||||
|
.ptr = nullptr,
|
||||||
|
.ec = std::errc{},
|
||||||
|
};
|
||||||
|
}
|
||||||
16
cmake/check_charconv/to_chars_float.cpp
Normal file
16
cmake/check_charconv/to_chars_float.cpp
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#include <charconv>
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
char buffer[1024];
|
||||||
|
char* first = buffer;
|
||||||
|
char* last = first + sizeof(buffer);
|
||||||
|
|
||||||
|
{
|
||||||
|
float value = 0;
|
||||||
|
auto rv = std::to_chars(first, last, value, std::chars_format::general, 6);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
double value = 0;
|
||||||
|
auto rv = std::to_chars(first, last, value, std::chars_format::general, 6);
|
||||||
|
}
|
||||||
|
}
|
||||||
41
cmake/check_charconv/to_chars_int.cpp
Normal file
41
cmake/check_charconv/to_chars_int.cpp
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#include <charconv>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
char buffer[1024];
|
||||||
|
char* first = buffer;
|
||||||
|
char* last = first + sizeof(buffer);
|
||||||
|
|
||||||
|
{
|
||||||
|
std::int8_t value = 0;
|
||||||
|
auto rv = std::to_chars(first, last, value, 10);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::int16_t value = 0;
|
||||||
|
auto rv = std::to_chars(first, last, value, 10);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::int32_t value = 0;
|
||||||
|
auto rv = std::to_chars(first, last, value, 10);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::int64_t value = 0;
|
||||||
|
auto rv = std::to_chars(first, last, value, 10);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::uint8_t value = 0;
|
||||||
|
auto rv = std::to_chars(first, last, value, 10);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::uint16_t value = 0;
|
||||||
|
auto rv = std::to_chars(first, last, value, 10);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::uint32_t value = 0;
|
||||||
|
auto rv = std::to_chars(first, last, value, 10);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::uint64_t value = 0;
|
||||||
|
auto rv = std::to_chars(first, last, value, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
9
cmake/check_charconv/to_chars_result.cpp
Normal file
9
cmake/check_charconv/to_chars_result.cpp
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#include <charconv>
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
std::to_chars_result result {
|
||||||
|
.ptr = nullptr,
|
||||||
|
.ec = std::errc{},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,19 +1,36 @@
|
|||||||
# Configure Doxygen config file
|
# Extract all public macros defined in YYCC
|
||||||
|
# However, you should note that these extratcted macros have generator expressions.
|
||||||
|
get_target_property(YYCC_COMPILE_DEFINITIONS YYCCommonplace COMPILE_DEFINITIONS)
|
||||||
|
if (YYCC_COMPILE_DEFINITIONS STREQUAL "YYCC_COMPILE_DEFINITIONS-NOTFOUND")
|
||||||
|
message(FATAL_ERROR "Cannot extract compile definitions from YYCCommonplace.")
|
||||||
|
endif ()
|
||||||
|
# Convert list to string for expanding in future.
|
||||||
|
list(JOIN YYCC_COMPILE_DEFINITIONS " " YYCC_MACRO_GENERATOR_EXPRESSIONS)
|
||||||
|
|
||||||
|
# We simply configure Doxygen config file first.
|
||||||
configure_file(
|
configure_file(
|
||||||
${CMAKE_CURRENT_LIST_DIR}/Doxyfile.in
|
${CMAKE_CURRENT_LIST_DIR}/Doxyfile.in
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
|
${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
|
||||||
@ONLY
|
@ONLY
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add custom target
|
# Then we use "file GENERATE" syntax to generate per-config truely Doxyfile used by Doxygen.
|
||||||
|
# Because there is no "$<>" syntax in Doxyfile, so we can safely use it.
|
||||||
|
# Please note that the generation of "file GENERATE" syntax will be postponed until the build stage.
|
||||||
|
file(GENERATE
|
||||||
|
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/Doxyfile"
|
||||||
|
INPUT "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile"
|
||||||
|
TARGET YYCCommonplace
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add custom target using per-config Doxyfile
|
||||||
add_custom_target (YYCCDocumentation
|
add_custom_target (YYCCDocumentation
|
||||||
doxygen ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
|
Doxygen::doxygen "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/Doxyfile"
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
COMMENT "Generating documentation" VERBATIM
|
COMMENT "Generating documentation" VERBATIM
|
||||||
)
|
)
|
||||||
|
|
||||||
# Install built documentation
|
# Install built documentation
|
||||||
install (DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html
|
install (DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html
|
||||||
CONFIGURATIONS Release RelWithDebInfo MinSizeRel
|
|
||||||
DESTINATION ${YYCC_INSTALL_DOC_PATH}
|
DESTINATION ${YYCC_INSTALL_DOC_PATH}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2306,7 +2306,7 @@ PERLMOD_MAKEVAR_PREFIX =
|
|||||||
# C-preprocessor directives found in the sources and include files.
|
# C-preprocessor directives found in the sources and include files.
|
||||||
# The default value is: YES.
|
# The default value is: YES.
|
||||||
|
|
||||||
ENABLE_PREPROCESSING = NO
|
ENABLE_PREPROCESSING = YES
|
||||||
|
|
||||||
# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
|
# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
|
||||||
# in the source code. If set to NO, only conditional compilation will be
|
# in the source code. If set to NO, only conditional compilation will be
|
||||||
@@ -2356,7 +2356,7 @@ INCLUDE_FILE_PATTERNS =
|
|||||||
# recursively expanded use the := operator instead of the = operator.
|
# recursively expanded use the := operator instead of the = operator.
|
||||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||||
|
|
||||||
PREDEFINED = YYCC_DOXYGEN
|
PREDEFINED = @YYCC_MACRO_GENERATOR_EXPRESSIONS@
|
||||||
|
|
||||||
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
|
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
|
||||||
# tag can be used to specify a list of macro names that should be expanded. The
|
# tag can be used to specify a list of macro names that should be expanded. The
|
||||||
|
|||||||
@@ -1,200 +0,0 @@
|
|||||||
namespace YYCC::ArgParser {
|
|
||||||
/**
|
|
||||||
|
|
||||||
\page arg_parser Universal Argument Parser
|
|
||||||
|
|
||||||
YYCC::ArgParser provides an universal way to parsing command line arguments.
|
|
||||||
|
|
||||||
Universal argument parser has similar design with universal config manager,
|
|
||||||
it is highly recommand that read \ref config_manager chapter first,
|
|
||||||
because you will have a clear understanding of this namespace after reading universal config manager chapter.
|
|
||||||
|
|
||||||
There is an example about how to use universal argument parser.
|
|
||||||
In following content, we will describe it in detail.
|
|
||||||
|
|
||||||
\code{.cpp}
|
|
||||||
class TestArgParser {
|
|
||||||
public:
|
|
||||||
TestArgParser() :
|
|
||||||
m_IntArgument(YYCC_U8("int"), YYCC_U8_CHAR('i'), YYCC_U8("integral argument"), YYCC_U8("114514")),
|
|
||||||
m_FloatArgument(nullptr, YYCC_U8_CHAR('f'), nullptr, nullptr, true),
|
|
||||||
m_StringArgument(YYCC_U8("string"), YYCC::ArgParser::AbstractArgument::NO_SHORT_NAME, nullptr, nullptr, true),
|
|
||||||
m_BoolArgument(nullptr, YYCC_U8_CHAR('b'), nullptr),
|
|
||||||
m_ClampedFloatArgument(YYCC_U8("clamped-float"), YYCC::ArgParser::AbstractArgument::NO_SHORT_NAME, nullptr, nullptr, true, YYCC::Constraints::GetMinMaxRangeConstraint<float>(-1.0f, 1.0f)),
|
|
||||||
m_OptionContext(YYCC_U8("TestArgParser"), YYCC_U8("This is the test of argument parser."), {
|
|
||||||
&m_IntArgument, &m_FloatArgument, &m_StringArgument,
|
|
||||||
&m_BoolArgument, &m_ClampedFloatArgument
|
|
||||||
}) {}
|
|
||||||
~TestArgParser() {}
|
|
||||||
|
|
||||||
YYCC::ArgParser::NumberArgument<int32_t> m_IntArgument;
|
|
||||||
YYCC::ArgParser::NumberArgument<float> m_FloatArgument;
|
|
||||||
YYCC::ArgParser::StringArgument m_StringArgument;
|
|
||||||
|
|
||||||
YYCC::ArgParser::SwitchArgument m_BoolArgument;
|
|
||||||
YYCC::ArgParser::NumberArgument<float> m_ClampedFloatArgument;
|
|
||||||
|
|
||||||
YYCC::ArgParser::OptionContext m_OptionContext;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Initialize argument parser.
|
|
||||||
TestArgParser test;
|
|
||||||
// Get argument list for parsing from standard C main function.
|
|
||||||
auto al = YYCC::ArgParser::ArgumentList::CreateFromStd(argc, argv);
|
|
||||||
// Start parsing
|
|
||||||
test.Parse(al);
|
|
||||||
// Get captured string argument
|
|
||||||
if (test.m_StringArgument.IsCaptured())
|
|
||||||
auto val = test.m_StringArgument.Get();
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
These code can resolve following command line:
|
|
||||||
|
|
||||||
\code{.sh}
|
|
||||||
exec -i 114514 -f 2.0 --string fuck -b --clamped-float 0.5
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
For convenience, we define following terms used in this article.
|
|
||||||
|
|
||||||
\li Every items in command line: Argument.
|
|
||||||
\li \c -i, \c --clamped-float: \b Switch / \b Option. the argument starts with dash or double dash.
|
|
||||||
\li \c 114514: \b Value. the value of switch.
|
|
||||||
|
|
||||||
\section arg_parser__argument Argument
|
|
||||||
|
|
||||||
Argument is the leaf of argument parser.
|
|
||||||
It has the same position as setting in universal config manager.
|
|
||||||
|
|
||||||
\subsection arg_parser__argument__presets Argument Presets
|
|
||||||
|
|
||||||
Like setting in universal config manager,
|
|
||||||
we also provide various common used argument presets.
|
|
||||||
Current'y we support following argument presets:
|
|
||||||
|
|
||||||
\li NumberArgument: The argument storing arithmetic type (except \c bool) inside. Such as <TT>-i 114514</TT> in example.
|
|
||||||
\li StringArgument: The argument storing string inside. Such as <TT>--string fuck</TT> in example.
|
|
||||||
\li SwitchArgument: The argument storing nothing. It is just a simple switch. Such as <TT>-b</TT> in example.
|
|
||||||
|
|
||||||
When constructing these argument,
|
|
||||||
you need provide one from long name or short name, or both of them.
|
|
||||||
Short name is the argument starting with dash and long name starts with double dash.
|
|
||||||
You don't need add dash or double dash prefix when providing these names.
|
|
||||||
Please note only ASCII characters, which can be displayed on screen, can be used in these names.
|
|
||||||
|
|
||||||
Optionally, you can provide description when constructing,
|
|
||||||
which will tell user how this switch does and more infomation about this switch.
|
|
||||||
And, you can add an example to tell user which value is valid.
|
|
||||||
|
|
||||||
Next, you can specify an argument to be optional.
|
|
||||||
Optional argument can be absent in command line.
|
|
||||||
Oppositely, non-optional argument must be presented in command line,
|
|
||||||
otherwise parser will return false to indicate an error.
|
|
||||||
For checking whether an optional argument is specified,
|
|
||||||
please call AbstractArgument::IsCaptured().
|
|
||||||
|
|
||||||
Last, you can optionally assign a constraint to it,
|
|
||||||
to help argument limit its value.
|
|
||||||
|
|
||||||
However SwitchArgument must be optional argument.
|
|
||||||
Because it is true if user specify it explicit it,
|
|
||||||
and will be false if user do not give this flag.
|
|
||||||
SwitchArgument doesn't have constraint features,
|
|
||||||
because it doesn't store any value inside.
|
|
||||||
Thus no need to limit this.
|
|
||||||
|
|
||||||
\subsection arg_parser__argument__custom Custom Argument
|
|
||||||
|
|
||||||
In most cases, the combination use of argument presets and constraints is enough.
|
|
||||||
However, if you still are urge to create your personal argument,
|
|
||||||
please inherit AbstractArgument and implement essential class functions.
|
|
||||||
For the class functions you need to implement,
|
|
||||||
please refer to our argument presets.
|
|
||||||
|
|
||||||
\section arg_parser__argument_list Argument List
|
|
||||||
|
|
||||||
Argument list is a struct used by parser for parsing.
|
|
||||||
It is a higher wrapper of a simple list containing argument items.
|
|
||||||
We provide 2 ways to get argument list.
|
|
||||||
|
|
||||||
\li ArgumentList::CreateFromStd: Create argument list from standard C main function parameters.
|
|
||||||
\li ArgumentList::CreateFromWin32: Create argument list from Win32 functions in Windows.
|
|
||||||
You should use this function in Windows instead of ArgumentList::CreateFromStd.
|
|
||||||
Because the command line passed in standard C main function has encoding issue in Windows.
|
|
||||||
Use this function you will fetch correct argument list especially command including non-ASCII characters.
|
|
||||||
|
|
||||||
Please note the first argument in given command line will be stripped.
|
|
||||||
Because in most cases it point to the executable self,
|
|
||||||
and should not be seen as the part of argument list.
|
|
||||||
|
|
||||||
\section arg_parser__option_context Option Context
|
|
||||||
|
|
||||||
Please note any unknow argument will let the parser return false.
|
|
||||||
This is different with other argument parsers.
|
|
||||||
In other common argument parsers,
|
|
||||||
they will collect all unknow argument as positional argument,
|
|
||||||
or just simply ignore them.
|
|
||||||
|
|
||||||
OptionContext also will not add \c -h or \c --help switch automatically.
|
|
||||||
This is also differnent with other parsers.
|
|
||||||
You should manually add it.
|
|
||||||
However, OptionContext provide a universal help print function, OptionContext::Help.
|
|
||||||
You can directly call it to output help text if you needed (fail to parse or user order help).
|
|
||||||
|
|
||||||
\section arg_parser__limitation Limitation
|
|
||||||
|
|
||||||
This universal argument parser is a tiny parser.
|
|
||||||
It only just fulfill my personal requirements.
|
|
||||||
So it only accepts limited command line syntax.
|
|
||||||
In following content I will tell you some syntaxes which this parser \b not accept.
|
|
||||||
|
|
||||||
\subsection arg_parser__limitation__flag_combination Flag Combination
|
|
||||||
|
|
||||||
\code{.sh}
|
|
||||||
exec -l -s -h
|
|
||||||
exec -lsh
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
Parser accept first line but not accept the second line.
|
|
||||||
You must write these flags independently.
|
|
||||||
|
|
||||||
\subsection arg_parser__limitation__equal_symbol Equal Symbol
|
|
||||||
|
|
||||||
\code{.sh}
|
|
||||||
exec --value 114514
|
|
||||||
exec --value=114514
|
|
||||||
exec --value:114514
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
Parser only accept first line command.
|
|
||||||
You can not use equal symbol or any other symbol to assign value for specified argument.
|
|
||||||
You must write value after the argument immediately please.
|
|
||||||
|
|
||||||
\subsection arg_parser__limitation__variable_argument Variable Argument
|
|
||||||
|
|
||||||
\code{.sh}
|
|
||||||
exec -DSOME_VARABLE=SOME_VALUE
|
|
||||||
exec -D SOME_VARIABLE=SOME_VALUE
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
Parser only accept second line.
|
|
||||||
However you nned to write a custom argument or constraint to holding this value.
|
|
||||||
|
|
||||||
\subsection arg_parser__limitation__switch_dependency Switch Dependency
|
|
||||||
|
|
||||||
\code{.sh}
|
|
||||||
exec --action-a --action-b
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
For command line written above,
|
|
||||||
if you hope \c --action-a and \c --action-b is exclusive,
|
|
||||||
or \c --action-b only be valid if \c --action-a specified,
|
|
||||||
you should manually implement this.
|
|
||||||
Parser don't have such features to process this switch dependency.
|
|
||||||
|
|
||||||
The thing you need to do is set these switches are \b not optional.
|
|
||||||
And after parser do a success parsing,
|
|
||||||
manually calling AbstractArgument::IsCaptured to fetch whether corresponding switches are captured,
|
|
||||||
then do your personal dependency check.
|
|
||||||
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
208
doc/src/carton/binstore.dox
Normal file
208
doc/src/carton/binstore.dox
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
namespace yycc::carton::binstore {
|
||||||
|
/**
|
||||||
|
|
||||||
|
\page binstore Binary Settings Storage (Binstore)
|
||||||
|
|
||||||
|
The binstore module provides a binary settings storage system that allows
|
||||||
|
applications to persistently store and retrieve configuration settings in
|
||||||
|
a binary format. It includes functionality for type-safe serialization and deserialization,
|
||||||
|
setting management with unique tokens for access control, version control with migration strategies,
|
||||||
|
and comprehensive error handling.
|
||||||
|
|
||||||
|
\section binstore__overview Overview
|
||||||
|
|
||||||
|
The binstore module consists of several key components:
|
||||||
|
|
||||||
|
\li types: Basic types and error handling for the module
|
||||||
|
\li serdes: Serialization/deserialization functionality for different data types
|
||||||
|
\li setting: Management of settings with name-based lookup and token-based access
|
||||||
|
\li configuration: Version and settings collection management
|
||||||
|
\li storage: Main storage class for loading/saving settings to/from files or streams
|
||||||
|
|
||||||
|
\section binstore__example Example Usage
|
||||||
|
|
||||||
|
Here is a complete example showing how to use the binstore module:
|
||||||
|
|
||||||
|
\code{.cpp}
|
||||||
|
#include <yycc.hpp>
|
||||||
|
#include <yycc/carton/binstore.hpp>
|
||||||
|
#include <yycc/patch/stream.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
using namespace yycc::carton::binstore;
|
||||||
|
using namespace yycc::patch::stream;
|
||||||
|
|
||||||
|
enum class LogLevel : uint8_t { Debug, Info, Warning, Error };
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
// Create settings collection
|
||||||
|
auto settings = setting::SettingCollection();
|
||||||
|
auto int_setting_token = settings.add_setting(setting::Setting(u8"max_connections"));
|
||||||
|
auto float_setting_token = settings.add_setting(setting::Setting(u8"timeout"));
|
||||||
|
auto string_setting_token = settings.add_setting(setting::Setting(u8"server_address"));
|
||||||
|
auto bool_setting_token = settings.add_setting(setting::Setting(u8"enable_logging"));
|
||||||
|
auto enum_setting_token = settings.add_setting(setting::Setting(u8"log_level"));
|
||||||
|
|
||||||
|
// Create configuration with version 1
|
||||||
|
auto config = configuration::Configuration(1, std::move(settings));
|
||||||
|
|
||||||
|
// Create storage with the configuration
|
||||||
|
auto storage = storage::Storage(std::move(config));
|
||||||
|
|
||||||
|
// Using appropriate SerDes types for different data types
|
||||||
|
using IntSerDes = serdes::IntegralSerDes<int32_t>;
|
||||||
|
using FloatSerDes = serdes::FloatingPointSerDes<float>;
|
||||||
|
using StringSerDes = serdes::StringSerDes;
|
||||||
|
using BoolSerDes = serdes::BoolSerDes<true>; // true as default value
|
||||||
|
using EnumSerDes = serdes::EnumSerDes<LogLevel, LogLevel::Info>;
|
||||||
|
|
||||||
|
// Set values
|
||||||
|
storage.set_value<IntSerDes>(int_setting_token, 100);
|
||||||
|
storage.set_value<FloatSerDes>(float_setting_token, 2.5f);
|
||||||
|
storage.set_value<StringSerDes>(string_setting_token, u8"localhost");
|
||||||
|
storage.set_value<BoolSerDes>(bool_setting_token, true);
|
||||||
|
storage.set_value<EnumSerDes>(enum_setting_token, LogLevel::Debug);
|
||||||
|
|
||||||
|
// Save to file
|
||||||
|
if (auto result = storage.save_into_file("config.bin"); result.has_value()) {
|
||||||
|
std::cout << "Configuration saved successfully" << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cout << "Failed to save configuration" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load from file
|
||||||
|
auto new_config = configuration::Configuration(1, setting::SettingCollection());
|
||||||
|
auto new_storage = storage::Storage(std::move(new_config));
|
||||||
|
|
||||||
|
if (auto result = new_storage.load_from_file("config.bin", storage::LoadStrategy::MigrateOld); result.has_value()) {
|
||||||
|
std::cout << "Configuration loaded successfully" << std::endl;
|
||||||
|
|
||||||
|
// Get values
|
||||||
|
int32_t max_conn = new_storage.get_value<IntSerDes>(int_setting_token);
|
||||||
|
float timeout = new_storage.get_value<FloatSerDes>(float_setting_token);
|
||||||
|
std::u8string addr = new_storage.get_value<StringSerDes>(string_setting_token);
|
||||||
|
bool logging = new_storage.get_value<BoolSerDes>(bool_setting_token);
|
||||||
|
LogLevel level = new_storage.get_value<EnumSerDes>(enum_setting_token);
|
||||||
|
|
||||||
|
std::cout << "Max connections: " << max_conn << std::endl;
|
||||||
|
std::cout << "Timeout: " << timeout << std::endl;
|
||||||
|
std::cout << "Server address: " << addr << std::endl;
|
||||||
|
std::cout << "Logging enabled: " << (logging ? "yes" : "no") << std::endl;
|
||||||
|
std::cout << "Log level: " << static_cast<int>(level) << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cout << "Failed to load configuration" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section binstore__components Components
|
||||||
|
|
||||||
|
\subsection binstore__settings Settings Management
|
||||||
|
|
||||||
|
Settings are identified by unique names and accessed via tokens. The [SettingCollection](\ref setting::SettingCollection)
|
||||||
|
manages a collection of settings and ensures no duplicates.
|
||||||
|
|
||||||
|
\subsection binstore__configuration Configuration
|
||||||
|
|
||||||
|
The [Configuration](\ref configuration::Configuration) class holds the version identifier and the collection of settings.
|
||||||
|
Version control is crucial for handling configuration migration between application versions.
|
||||||
|
|
||||||
|
\subsection binstore__storage Storage
|
||||||
|
|
||||||
|
The [Storage](\ref storage::Storage) class is the main interface for setting/getting values and loading/saving configurations.
|
||||||
|
It provides methods for both file-based and stream-based operations.
|
||||||
|
|
||||||
|
\subsection binstore__serdes Serialization/Deserialization
|
||||||
|
|
||||||
|
SerDes (Serializer/Deserializer) classes handle type-safe conversion between values and their binary representation.
|
||||||
|
Built-in SerDes types include:
|
||||||
|
|
||||||
|
\li Integral types ([IntegralSerDes](\ref serdes::IntegralSerDes))
|
||||||
|
\li Floating-point types ([FloatingPointSerDes](\ref serdes::FloatingPointSerDes))
|
||||||
|
\li String types ([StringSerDes](\ref serdes::StringSerDes))
|
||||||
|
\li Boolean types ([BoolSerDes](\ref serdes::BoolSerDes))
|
||||||
|
\li Enum types ([EnumSerDes](\ref serdes::EnumSerDes))
|
||||||
|
|
||||||
|
For some of them, you can specify value range and default value via template parameters.
|
||||||
|
|
||||||
|
\section binstore__load_strategies Load Strategies
|
||||||
|
|
||||||
|
The binstore module provides different strategies for handling version mismatches:
|
||||||
|
|
||||||
|
\li [OnlyCurrent](\ref storage::LoadStrategy::OnlyCurrent): Only accept configurations with matching version
|
||||||
|
\li [MigrateOld](\ref storage::LoadStrategy::MigrateOld): Accept matching and older versions, reject newer versions
|
||||||
|
\li [AcceptAll](\ref storage::LoadStrategy::AcceptAll): Accept all versions (not recommended for production)
|
||||||
|
|
||||||
|
\section binstore__custom_serdes Custom SerDes
|
||||||
|
|
||||||
|
Custom SerDes (Serializer/Deserializer) can be created by implementing the \c SerDes concept.
|
||||||
|
A valid SerDes must satisfy the following requirements:
|
||||||
|
|
||||||
|
\li Have a type alias called \c ValueType indicating the corresponding setting type
|
||||||
|
\li Have a member function called \c serialize that accepts a const reference of the setting data and returns \c ByteArray
|
||||||
|
or \c std::nullopt if serialization fails.
|
||||||
|
\li Have a member function called \c deserialize that converts \c ByteArray to the desired type
|
||||||
|
or returns \c std::nullopt if deserialization fails.
|
||||||
|
\li Have a member function called \c reset that returns a default \c ByteArray value.
|
||||||
|
|
||||||
|
Here is an example of a custom SerDes for storing IPv4 addresses:
|
||||||
|
|
||||||
|
\code{.cpp}
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
struct IPv4Address {
|
||||||
|
std::uint8_t octets[4];
|
||||||
|
|
||||||
|
IPv4Address() : octets{0, 0, 0, 0} {}
|
||||||
|
IPv4Address(std::uint8_t a, std::uint8_t b, std::uint8_t c, std::uint8_t d) {
|
||||||
|
octets[0] = a; octets[1] = b; octets[2] = c; octets[3] = d;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IPv4SerDes {
|
||||||
|
using ValueType = IPv4Address;
|
||||||
|
static constexpr size_t VALUE_SIZE = sizeof(IPv4Address); // 4 octets
|
||||||
|
|
||||||
|
std::optional<types::ByteArray> serialize(const ValueType& value) const {
|
||||||
|
types::ByteArray ba;
|
||||||
|
ba.resize_data(VALUE_SIZE);
|
||||||
|
std::memcpy(ba.get_data_ptr(), value.octets, VALUE_SIZE);
|
||||||
|
return ba;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<ValueType> deserialize(const types::ByteArray& ba) const {
|
||||||
|
if (ba.get_data_size() != VALUE_SIZE) return std::nullopt;
|
||||||
|
|
||||||
|
ValueType value;
|
||||||
|
std::memcpy(value.octets, ba.get_data_ptr(), VALUE_SIZE);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
types::ByteArray reset() const {
|
||||||
|
// Reset to local address
|
||||||
|
ValueType default_value(127, 0, 0, 1);
|
||||||
|
return this->serialize(default_value).value();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
To use the custom SerDes:
|
||||||
|
|
||||||
|
\code{.cpp}
|
||||||
|
// Add setting to collection
|
||||||
|
auto ip_setting_token = settings.add_setting(setting::Setting(u8"server_ip"));
|
||||||
|
|
||||||
|
// Use custom SerDes
|
||||||
|
IPv4SerDes ip_serdes;
|
||||||
|
storage.set_value<IPv4SerDes>(ip_setting_token, IPv4Address(192, 168, 1, 1));
|
||||||
|
|
||||||
|
// Retrieve value
|
||||||
|
IPv4Address ip_addr = storage.get_value<IPv4SerDes>(ip_setting_token);
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
*/
|
||||||
|
}
|
||||||
187
doc/src/carton/clap.dox
Normal file
187
doc/src/carton/clap.dox
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
namespace yycc::carton::clap {
|
||||||
|
/**
|
||||||
|
|
||||||
|
\page clap Command Line Argument Parser (CLAP)
|
||||||
|
|
||||||
|
Command Line Argument Parser (CLAP) module for handling command line arguments and environment variables.
|
||||||
|
This module provides a comprehensive system for defining, parsing, and validating command line
|
||||||
|
arguments and environment variables. It includes components for defining application metadata,
|
||||||
|
command line options, variables, and utilities for parsing and validation.
|
||||||
|
|
||||||
|
\section clap__overview Overview
|
||||||
|
|
||||||
|
The CLAP module consists of several key components:
|
||||||
|
|
||||||
|
\li Types: Error types and result types used throughout the module
|
||||||
|
\li Validator: Type-safe validation for command line argument values
|
||||||
|
\li Option: Command line options with short and long names
|
||||||
|
\li Variable: Environment variables that can be captured
|
||||||
|
\li Summary: Application metadata (name, version, author, description)
|
||||||
|
\li Application: Complete application definition with options and variables
|
||||||
|
\li Manual: Help and version information generation
|
||||||
|
\li Parser: Command line argument parsing functionality
|
||||||
|
\li Resolver: Environment variable resolution functionality
|
||||||
|
|
||||||
|
\section clap__example Example Usage
|
||||||
|
|
||||||
|
Here is a complete example showing how to use the CLAP module:
|
||||||
|
|
||||||
|
\code{.cpp}
|
||||||
|
#include <yycc.hpp>
|
||||||
|
#include <yycc/carton/clap.hpp>
|
||||||
|
#include <yycc/patch/stream.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace yycc::carton::clap;
|
||||||
|
using namespace yycc::patch::stream;
|
||||||
|
|
||||||
|
// Define an application with options and variables
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
// Create application summary
|
||||||
|
auto summary = summary::Summary(u8"MyApp", u8"author", u8"1.0.0", u8"A sample application");
|
||||||
|
|
||||||
|
// Create options collection
|
||||||
|
auto options = option::OptionCollection();
|
||||||
|
auto int_opt = options.add_option(option::Option(u8"i", u8"int", u8"NUM", u8"integral argument"));
|
||||||
|
auto float_opt = options.add_option(option::Option(u8"f", std::nullopt, u8"NUM", u8"floating point argument"));
|
||||||
|
auto string_opt = options.add_option(option::Option(std::nullopt, u8"string", u8"STR", u8"string argument"));
|
||||||
|
auto flag_opt = options.add_option(option::Option(u8"v", std::nullopt, std::nullopt, u8"verbose mode"));
|
||||||
|
|
||||||
|
// Create variables collection
|
||||||
|
auto variables = variable::VariableCollection();
|
||||||
|
auto env_var = variables.add_variable(variable::Variable(u8"ENV_VAR", u8"Environment variable description", true));
|
||||||
|
|
||||||
|
// Create the application and manual
|
||||||
|
auto app = application::Application(std::move(summary), std::move(options), std::move(variables));
|
||||||
|
auto manual = manual::Manual(app);
|
||||||
|
|
||||||
|
// Parse command line arguments
|
||||||
|
auto result = parser::Parser::from_system(app);
|
||||||
|
if (result.has_value()) {
|
||||||
|
auto parser = std::move(result.value());
|
||||||
|
|
||||||
|
// Get values using validators
|
||||||
|
using IntValidator = validator::IntegralValidator<int>;
|
||||||
|
using FloatValidator = validator::FloatingPointValidator<float>;
|
||||||
|
using StringValidator = validator::StringValidator;
|
||||||
|
|
||||||
|
// Check and get integer option
|
||||||
|
if (auto int_val = parser.get_value_option<IntValidator>(int_opt); int_val.has_value()) {
|
||||||
|
std::cout << "Integer value: " << int_val.value() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check and get float option
|
||||||
|
if (auto float_val = parser.get_value_option<FloatValidator>(float_opt); float_val.has_value()) {
|
||||||
|
std::cout << "Float value: " << float_val.value() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check and get string option
|
||||||
|
if (auto str_val = parser.get_value_option<StringValidator>(string_opt); str_val.has_value()) {
|
||||||
|
std::cout << "String value: " << str_val.value() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check flag option
|
||||||
|
if (auto flag_val = parser.get_flag_option(flag_opt); flag_val.has_value() && flag_val.value()) {
|
||||||
|
std::cout << "Verbose mode enabled" << std::endl;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Print help if parsing failed
|
||||||
|
manual.print_help(std::cout);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
This code handles command lines like:
|
||||||
|
\code{.sh}
|
||||||
|
./myapp -i 123 -f 2.5 --string "hello world" -v
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section clap__components Components
|
||||||
|
|
||||||
|
\subsection clap__application Application Definition
|
||||||
|
|
||||||
|
The [Application](\ref application::Application) class represents a complete command line application with its summary, options, and environment variables.
|
||||||
|
It combines the application metadata, command line options, and environment variables into a single unit.
|
||||||
|
|
||||||
|
\subsection clap__options Options
|
||||||
|
|
||||||
|
[Option](\ref option::Option) is command line arguments that can accept values or act as flags.
|
||||||
|
They can have both short names (single character)
|
||||||
|
and long names (full text). The [OptionCollection](\ref option::OptionCollection) manages a collection of options and ensures no duplicates.
|
||||||
|
|
||||||
|
\subsection clap__variables Variables
|
||||||
|
|
||||||
|
[Variable](\ref variable::Variable) represent environment variables that can be captured and validated. The [VariableCollection](\ref variable::VariableCollection)
|
||||||
|
manages a collection of environment variables and ensures no duplicates.
|
||||||
|
|
||||||
|
\subsection clap__parsing Parsing
|
||||||
|
|
||||||
|
The [Parser](\ref parser::Parser) class handles command line argument parsing. It can be created from user-provided arguments
|
||||||
|
or from system arguments (argc/argv). Values are retrieved using type-safe validators.
|
||||||
|
|
||||||
|
\subsection clap__validation Validation
|
||||||
|
|
||||||
|
Validators ensure type-safe validation of command line argument values.
|
||||||
|
The module provides built-in validators for:
|
||||||
|
|
||||||
|
\li Integral types ([IntegralValidator](\ref validator::IntegralValidator))
|
||||||
|
\li Floating-point types ([FloatingPointValidator](\ref validator::FloatingPointValidator))
|
||||||
|
\li String types ([StringValidator](\ref validator::StringValidator))
|
||||||
|
|
||||||
|
For some of them, you also can specify value range via template arguments.
|
||||||
|
|
||||||
|
\section clap__custom_validators Custom Validator
|
||||||
|
|
||||||
|
Custom validators can be created by implementing the \c Validator concept.
|
||||||
|
A valid validator must satisfy the following requirements:
|
||||||
|
|
||||||
|
\li Have a type alias called \c ReturnType indicating the return value type
|
||||||
|
\li Have a member function called \c validate that receives <TT>const std::u8string_view&</TT> as its only argument
|
||||||
|
and returns validated \c ReturnType or \c std::nullopt if validation fails
|
||||||
|
|
||||||
|
Here is an example of a custom validator that validates email addresses:
|
||||||
|
|
||||||
|
\code{.cpp}
|
||||||
|
#include <yycc/string/reinterpret.hpp>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
|
struct EmailValidator {
|
||||||
|
using ReturnType = std::u8string;
|
||||||
|
|
||||||
|
std::optional<ReturnType> validate(const std::u8string_view& sv) const {
|
||||||
|
// Simple email validation using regex
|
||||||
|
static const std::regex email_regex(
|
||||||
|
R"([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})");
|
||||||
|
|
||||||
|
auto email_str = yycc::string::reinterpret::as_ordinary_view(sv);
|
||||||
|
if (std::regex_match(email_str, email_regex)) {
|
||||||
|
return sv;
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
To use the custom validator:
|
||||||
|
|
||||||
|
\code{.cpp}
|
||||||
|
// Add option to application
|
||||||
|
auto email_opt = options.add_option(option::Option(std::nullopt, u8"email", u8"EMAIL", u8"Email address"));
|
||||||
|
|
||||||
|
// Use custom validator
|
||||||
|
if (auto email_val = parser.get_value_option<EmailValidator>(email_opt); email_val.has_value()) {
|
||||||
|
std::cout << yycc::patch::format(u8"Valid email: {}", email_val); << std::endl;
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section clap__limitations Limitations
|
||||||
|
|
||||||
|
Due to the limitations of implementation,
|
||||||
|
CLAP now only allow only zero or one associated value for single option.
|
||||||
|
More than one assocciated value for single option is not supported.
|
||||||
|
|
||||||
|
*/
|
||||||
|
}
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
namespace YYCC::ConfigManager {
|
|
||||||
/**
|
|
||||||
|
|
||||||
\page config_manager Universal Config Manager
|
|
||||||
|
|
||||||
YYCC::ConfigManager give programmer an universal way to manage its program settings.
|
|
||||||
There is an example about how to use universal config manager.
|
|
||||||
In following content, we will describe it in detail.
|
|
||||||
|
|
||||||
\code
|
|
||||||
class TestConfigManager {
|
|
||||||
public:
|
|
||||||
enum class TestEnum : int8_t {
|
|
||||||
Test1, Test2, Test3
|
|
||||||
};
|
|
||||||
|
|
||||||
TestConfigManager() :
|
|
||||||
m_IntSetting(YYCC_U8("int-setting"), INT32_C(0)),
|
|
||||||
m_StringSetting(YYCC_U8("string-setting"), YYCC_U8("")),
|
|
||||||
m_FloatSetting(YYCC_U8("float-setting"), 0.0f, YYCC::Constraints::GetNumberRangeConstraint<float>(-1.0f, 1.0f)),
|
|
||||||
m_EnumSetting(YYCC_U8("enum-setting"), TestEnum::Test1),
|
|
||||||
m_CoreManager(YYCC_U8("test.cfg"), UINT64_C(0), {
|
|
||||||
&m_IntSetting, &m_StringSetting, &m_FloatSetting, &m_EnumSetting
|
|
||||||
})
|
|
||||||
{}
|
|
||||||
~TestConfigManager() {}
|
|
||||||
|
|
||||||
YYCC::ConfigManager::NumberSetting<int32_t> m_IntSetting;
|
|
||||||
YYCC::ConfigManager::StringSetting m_StringSetting;
|
|
||||||
YYCC::ConfigManager::NumberSetting<float> m_FloatSetting;
|
|
||||||
YYCC::ConfigManager::NumberSetting<TestEnum> m_EnumSetting;
|
|
||||||
|
|
||||||
YYCC::ConfigManager::CoreManager m_CoreManager;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Initialize config manager
|
|
||||||
TestConfigManager test;
|
|
||||||
// Load settings.
|
|
||||||
test.m_CoreManager.Load()
|
|
||||||
// Get string setting value.
|
|
||||||
auto val = test.m_StringSetting.Get();
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
\section config_manager__setting Setting
|
|
||||||
|
|
||||||
Setting can be seen as the leaf of the config tree.
|
|
||||||
Each setting describe a single configuration entry.
|
|
||||||
|
|
||||||
\subsection config_manager__setting__presets Setting Presets
|
|
||||||
|
|
||||||
We currently provide 2 setting preset classes which you can directly use.
|
|
||||||
|
|
||||||
\li NumberSetting: The setting storing a number inside.
|
|
||||||
It is a template class. Support all arithmetic and enum types (integral, floating point, bool, enum).
|
|
||||||
\li StringSetting: The setting storing a string inside.
|
|
||||||
|
|
||||||
When constructing these settings,
|
|
||||||
you need to provide its unique name which will be used when saving to file or reading from file.
|
|
||||||
Also you need to provide a default value for it.
|
|
||||||
It will be used when fail to read file or initializing itself.
|
|
||||||
|
|
||||||
Optionally, you also can provide a constraint to setting.
|
|
||||||
Constraint is the struct instructing library to limit value in specified range.
|
|
||||||
It usually is used for making sure the setting stored value is valid.
|
|
||||||
See \ref constraints chapters to know how we provide constraints.
|
|
||||||
|
|
||||||
\subsection config_manager__setting__custom Custom Setting
|
|
||||||
|
|
||||||
In most cases, the combination use of setting presets and constraints is enough.
|
|
||||||
However, if you still are urge to create your personal setting,
|
|
||||||
please inherit AbstractSetting and implement essential class functions.
|
|
||||||
For the class functions you need to implement,
|
|
||||||
please refer to our setting presets, NumberSetting and StringSetting.
|
|
||||||
|
|
||||||
\section config_manager__core_manager Core Manager
|
|
||||||
|
|
||||||
CoreManager manage a collection of settings.
|
|
||||||
And have responsibility to reading and writing config file.
|
|
||||||
|
|
||||||
We highly suggest that you create a personal config manager class like example does.
|
|
||||||
Then put essential settings and core manager inside it.
|
|
||||||
Please note you must place core manager after all settings.
|
|
||||||
Because the order of C++ initializing its class member is the order you declared them.
|
|
||||||
The constructor of core manager need the pointer to all it managed settings,
|
|
||||||
so it must be initialized after initializing all settings.
|
|
||||||
|
|
||||||
When initializing core manager, you need assign config file path first.
|
|
||||||
Then you need specify a version number.
|
|
||||||
Version number is important.
|
|
||||||
It will be used when reading config file and only can be increased if needed (version can not downgrade).
|
|
||||||
The last argument is an initializer list which contain the \b pointer to all settings this manager managed.
|
|
||||||
|
|
||||||
When executing YYCC::ConfigManager::CoreManager::Load to load configs, it will perform following steps one by one:
|
|
||||||
|
|
||||||
<UL>
|
|
||||||
<LI>
|
|
||||||
Open given config file.
|
|
||||||
<UL>
|
|
||||||
<LI>
|
|
||||||
If given file is not existing, loading function will simply return and all configs will be reset to its default value.
|
|
||||||
</LI>
|
|
||||||
<LI>
|
|
||||||
Success to open file, go to next step.
|
|
||||||
</LI>
|
|
||||||
</UL>
|
|
||||||
</LI>
|
|
||||||
|
|
||||||
<LI>
|
|
||||||
Fetch version number from file.
|
|
||||||
<UL>
|
|
||||||
<LI>
|
|
||||||
If fail to read version number from file, loading function will simply return and all configs will be reset to its default value.
|
|
||||||
</LI>
|
|
||||||
<LI>
|
|
||||||
If the version of config file is higher than your specified version number when constructing this class,
|
|
||||||
core manager will assume you are trying to read a config file created by a higher version program,
|
|
||||||
and will reject reading and use default value for all settings.
|
|
||||||
</LI>
|
|
||||||
<LI>
|
|
||||||
If the version of config file is lower than your specified version number,
|
|
||||||
core manager will try to read config file and do proper migration (set default value for configs which do not existing) if possible.
|
|
||||||
</LI>
|
|
||||||
<LI>
|
|
||||||
If the version of config file is equal than your specified version number,
|
|
||||||
core manager will read config file normally.
|
|
||||||
</LI>
|
|
||||||
</UL>
|
|
||||||
</LI>
|
|
||||||
|
|
||||||
<LI>
|
|
||||||
Read config file body.
|
|
||||||
<UL>
|
|
||||||
<LI>
|
|
||||||
If any IO error occurs when reading, loading function will simply return.
|
|
||||||
All read config will keep their read value and all configs which has not been read will keep their default value.
|
|
||||||
</LI>
|
|
||||||
<LI>
|
|
||||||
If some config can not parse binary data to its type,
|
|
||||||
this config will be skipped and core manager will process next config.
|
|
||||||
This config will keep its default value.
|
|
||||||
</LI>
|
|
||||||
</UL>
|
|
||||||
</LI>
|
|
||||||
</UL>
|
|
||||||
|
|
||||||
All of these scenarios can be found by the return value of loading function.
|
|
||||||
The return type of loading function, ConfigLoadResult is a flag enum.
|
|
||||||
You can find whether loading process happend specified issue by using bitwise operation on it.
|
|
||||||
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
@@ -1,181 +0,0 @@
|
|||||||
namespace YYCC::ConsoleHelper {
|
|
||||||
/**
|
|
||||||
|
|
||||||
\page console_helper Console Helper
|
|
||||||
|
|
||||||
This helper provide console related stuff.
|
|
||||||
This helper includes 2 parts.
|
|
||||||
First part is console color.
|
|
||||||
It was constituted by a bunch of macros.
|
|
||||||
The second part is universal console IO function because Windows is lacking in UTF8 console IO.
|
|
||||||
All of these parts will be introduced in following content.
|
|
||||||
|
|
||||||
\section console_helper__color Console Color
|
|
||||||
|
|
||||||
YYCC::ConsoleHelper provide a bunch of macros which can allow you output colorful text in terminal.
|
|
||||||
Supported color is limited in 16 colors,
|
|
||||||
because these color is implemented by ASCII Escape Code: https://en.wikipedia.org/wiki/ANSI_escape_code .
|
|
||||||
So if your terminal do not support this, such as default Windows terminal, or teletypewriter,
|
|
||||||
you will see some unrecognised characters surrounding with your output.
|
|
||||||
That's ASCII Escape Code.
|
|
||||||
|
|
||||||
\subsection console_helper__color__enable_win_color Enable Color in Windows Console
|
|
||||||
|
|
||||||
As we introduced in above,
|
|
||||||
you may know Windows console does not support ASCII Escape Code color in default.
|
|
||||||
However #EnableColorfulConsole can fix this issue.
|
|
||||||
|
|
||||||
#EnableColorfulConsole will forcely enable ASCII Escape Code support in Windows console if possible.
|
|
||||||
Thus you can write colorful text in Windows console freely.
|
|
||||||
We suggest you to call this function at the beginning of program.
|
|
||||||
|
|
||||||
Considering most Linux console supports ASCII Escape Code very well,
|
|
||||||
this function does nothing in non-Windows platform.
|
|
||||||
So it is not essential that brack this function calling with Windows-only \c \#if.
|
|
||||||
|
|
||||||
\subsection console_helper__color__common Common Usage
|
|
||||||
|
|
||||||
For common scenarios, you can use macro like this:
|
|
||||||
|
|
||||||
\code
|
|
||||||
YYCC::ConsoleHelper::WriteLine(YYCC_U8(YYCC_COLOR_LIGHT_RED("Light Red Text")));
|
|
||||||
YYCC::ConsoleHelper::WriteLine(YYCC_U8("I am " YYCC_COLOR_LIGHT_RED("Light Red")));
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
In first line, it will make <TT>"Light Red Text"</TT> to be shown in light red color.
|
|
||||||
And for second line, it will make <TT>"Light Red"</TT> to be shown in light red color,
|
|
||||||
but <TT>"I am "</TT> will keep default console font color.
|
|
||||||
|
|
||||||
You also may notice this macro is used with YYCC_U8 macro.
|
|
||||||
Because #WriteLine only accept UTF8 argument.
|
|
||||||
So please note if you use console color macro with YYCC_U8,
|
|
||||||
please make YYCC_U8 always is located the outside.
|
|
||||||
Otherwise, YYCC_U8 will fail to make the whole become UTF8 stirng as we introduced in \ref library_encoding.
|
|
||||||
Because console color macro is implemented by string literal concatenation internally.
|
|
||||||
|
|
||||||
YYCC_COLOR_LIGHT_RED is a member in YYCC_COLOR macro family.
|
|
||||||
YYCC_COLOR macro family has 16 members for 16 different colors:
|
|
||||||
|
|
||||||
\li YYCC_COLOR_BLACK
|
|
||||||
\li YYCC_COLOR_RED
|
|
||||||
\li YYCC_COLOR_GREEN
|
|
||||||
\li YYCC_COLOR_YELLOW
|
|
||||||
\li YYCC_COLOR_BLUE
|
|
||||||
\li YYCC_COLOR_MAGENTA
|
|
||||||
\li YYCC_COLOR_CYAN
|
|
||||||
\li YYCC_COLOR_WHITE
|
|
||||||
\li YYCC_COLOR_LIGHT_BLACK
|
|
||||||
\li YYCC_COLOR_LIGHT_RED
|
|
||||||
\li YYCC_COLOR_LIGHT_GREEN
|
|
||||||
\li YYCC_COLOR_LIGHT_YELLOW
|
|
||||||
\li YYCC_COLOR_LIGHT_BLUE
|
|
||||||
\li YYCC_COLOR_LIGHT_MAGENTA
|
|
||||||
\li YYCC_COLOR_LIGHT_CYAN
|
|
||||||
\li YYCC_COLOR_LIGHT_WHITE
|
|
||||||
|
|
||||||
\subsection console_helper__color__embedded Embedded Usgae
|
|
||||||
|
|
||||||
In some cases, you want change console at some time point and reset it in another time point.
|
|
||||||
You can use color macros like following example:
|
|
||||||
|
|
||||||
\code
|
|
||||||
YYCC::ConsoleHelper::WriteLine(YYCC_U8(YYCC_COLORHDR_LIGHT_BLUE));
|
|
||||||
|
|
||||||
// Write as much as you liked
|
|
||||||
YYCC::ConsoleHelper::WriteLine(YYCC_U8("some string"));
|
|
||||||
YYCC::ConsoleHelper::WriteLine(YYCC_U8("another string"));
|
|
||||||
|
|
||||||
YYCC::ConsoleHelper::WriteLine(YYCC_U8(YYCC_COLORTAIL));
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
At first line, we output YYCC_COLORHDR_LIGHT_BLUE which is in YYCC_COLORHDR macro family.
|
|
||||||
It is colorful text ASCII Escape Code head.
|
|
||||||
It will make all following output become light blue color,
|
|
||||||
until the last line we output YYCC_COLORTAIL to reset console color to original color.
|
|
||||||
|
|
||||||
Same as YYCC_COLOR macro family,
|
|
||||||
YYCC_COLORHDR macro family also has 16 members for 16 different colors:
|
|
||||||
|
|
||||||
\li YYCC_COLORHDR_BLACK
|
|
||||||
\li YYCC_COLORHDR_RED
|
|
||||||
\li YYCC_COLORHDR_GREEN
|
|
||||||
\li YYCC_COLORHDR_YELLOW
|
|
||||||
\li YYCC_COLORHDR_BLUE
|
|
||||||
\li YYCC_COLORHDR_MAGENTA
|
|
||||||
\li YYCC_COLORHDR_CYAN
|
|
||||||
\li YYCC_COLORHDR_WHITE
|
|
||||||
\li YYCC_COLORHDR_LIGHT_BLACK
|
|
||||||
\li YYCC_COLORHDR_LIGHT_RED
|
|
||||||
\li YYCC_COLORHDR_LIGHT_GREEN
|
|
||||||
\li YYCC_COLORHDR_LIGHT_YELLOW
|
|
||||||
\li YYCC_COLORHDR_LIGHT_BLUE
|
|
||||||
\li YYCC_COLORHDR_LIGHT_MAGENTA
|
|
||||||
\li YYCC_COLORHDR_LIGHT_CYAN
|
|
||||||
\li YYCC_COLORHDR_LIGHT_WHITE
|
|
||||||
|
|
||||||
However YYCC_COLORTAIL is YYCC_COLORTAIL.
|
|
||||||
There is no other variant for different colors.
|
|
||||||
Because all tail of colorful ASCII Escape Code is same.
|
|
||||||
|
|
||||||
\section console_helper__universal_io Universal IO Function
|
|
||||||
|
|
||||||
\subsection console_helper__universal_io__why Why?
|
|
||||||
|
|
||||||
Windows console doesn't support UTF8 very well.
|
|
||||||
The standard input output functions can not work properly with UTF8 on Windows.
|
|
||||||
So we create this namespace and provide various console-related functions
|
|
||||||
to patch Windows console and let it more like the console in other platforms.
|
|
||||||
|
|
||||||
The function provided in this function can be called in any platforms.
|
|
||||||
In Windows, the implementation will use Windows native function,
|
|
||||||
and in other platform, the implementation will redirect request to standard C function like \c std::fputs and etc.
|
|
||||||
So the programmer do not need to be worried about which function should they use,
|
|
||||||
and don't need to use macro to use different IO function in different platforms.
|
|
||||||
It is just enough that fully use the functions provided in this namespace.
|
|
||||||
|
|
||||||
All IO functions this namespace provided are UTF8-based.
|
|
||||||
It also means that input output string should always be UTF8 encoded.
|
|
||||||
|
|
||||||
\subsection console_helper__universal_io__input Input Functions
|
|
||||||
|
|
||||||
Please note that EOL will automatically converted into LF on Windows platform, not CRLF.
|
|
||||||
This action actually is removing all CR chars in result string.
|
|
||||||
This behavior affect nothing in most cases but it still is possible break something in some special case.
|
|
||||||
|
|
||||||
Due to implementation, if you decide to use this function,
|
|
||||||
you should give up using any other function to read stdin stream,
|
|
||||||
such as \c std::gets() and \c std::cin.
|
|
||||||
Because this function may read chars which is more than needed.
|
|
||||||
These extra chars will be stored in this function and can be used next calling.
|
|
||||||
But these chars can not be visited by stdin again.
|
|
||||||
This behavior may cause bug.
|
|
||||||
So if you decide using this function, stick on it and do not change.
|
|
||||||
|
|
||||||
Due to implementation, this function do not support hot switch of stdin.
|
|
||||||
It means that stdin can be redirected before first calling of this function,
|
|
||||||
but it should not be redirected during program running.
|
|
||||||
The reason is the same one introduced above.
|
|
||||||
|
|
||||||
\subsection console_helper__universal_io__output Output Functions
|
|
||||||
|
|
||||||
In current implementation, EOL will not be converted automatically to CRLF.
|
|
||||||
This is different with other stream read functions provided in this namespace.
|
|
||||||
|
|
||||||
Comparing with other stream read functions provided in this namespace,
|
|
||||||
stream write function support hot switch of stdout and stderr.
|
|
||||||
Because they do not have internal buffer storing something.
|
|
||||||
|
|
||||||
In this namespace, there are various stream write function.
|
|
||||||
There is a list telling you how to choose one from them for using:
|
|
||||||
|
|
||||||
\li Functions with leading "Err" will write data into stderr,
|
|
||||||
otherwise they will write data into stdout.
|
|
||||||
\li Functions with embedded "Format" are output functions with format feature
|
|
||||||
like \c std::fprintf(), otherwise the functions with embedded "Write" will
|
|
||||||
only write plain string like \c std::fputs().
|
|
||||||
\li Functions with trailing "Line" will write extra EOL to break current line.
|
|
||||||
This is commonly used, otherwise functions will only write the text provided by arguments,
|
|
||||||
without adding something.
|
|
||||||
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
74
doc/src/carton/csconsole.dox
Normal file
74
doc/src/carton/csconsole.dox
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
namespace yycc::carton::csconsole {
|
||||||
|
/**
|
||||||
|
|
||||||
|
\page csconsole Universal IO Function
|
||||||
|
|
||||||
|
This namespace provide universal console IO function which is more like C\# provided,
|
||||||
|
because Windows is lacking in UTF8 console IO.
|
||||||
|
|
||||||
|
\section csconsole__deprecation Deprecation Notes
|
||||||
|
|
||||||
|
This namespace, or this module is deprecated.
|
||||||
|
Its provided functions are too aggressive and can not cover all use scenarios.
|
||||||
|
So it is suggested not to use this namespace.
|
||||||
|
Programmers should handle Windows UTF8 issues on their own.
|
||||||
|
|
||||||
|
\section csconsole__why Why?
|
||||||
|
|
||||||
|
Windows console doesn't support UTF8 very well.
|
||||||
|
The standard input output functions can not work properly with UTF8 on Windows.
|
||||||
|
So we create this namespace and provide various console-related functions
|
||||||
|
to patch Windows console and let it more like the console in other platforms.
|
||||||
|
|
||||||
|
The function provided in this function can be called in any platforms.
|
||||||
|
In Windows, the implementation will use Windows native function,
|
||||||
|
and in other platform, the implementation will redirect request to standard C function like \c std::fputs and etc.
|
||||||
|
So the programmer do not need to be worried about which function should they use,
|
||||||
|
and don't need to use macro to use different IO function in different platforms.
|
||||||
|
It is just enough that fully use the functions provided in this namespace.
|
||||||
|
|
||||||
|
All IO functions this namespace provided are UTF8-based.
|
||||||
|
It also means that input output string should always be UTF8 encoded.
|
||||||
|
|
||||||
|
\section csconsole__input Input Functions
|
||||||
|
|
||||||
|
Please note that EOL will automatically converted into LF on Windows platform, not CRLF.
|
||||||
|
This action actually is removing all CR chars in result string.
|
||||||
|
This behavior affect nothing in most cases but it still is possible break something in some special case.
|
||||||
|
|
||||||
|
Due to implementation, if you decide to use this function,
|
||||||
|
you should give up using any other function to read stdin stream,
|
||||||
|
such as \c std::gets() and \c std::cin.
|
||||||
|
Because this function may read chars which is more than needed.
|
||||||
|
These extra chars will be stored in this function and can be used next calling.
|
||||||
|
But these chars can not be visited by stdin again.
|
||||||
|
This behavior may cause bug.
|
||||||
|
So if you decide using this function, stick on it and do not change.
|
||||||
|
|
||||||
|
Due to implementation, this function do not support hot switch of stdin.
|
||||||
|
It means that stdin can be redirected before first calling of this function,
|
||||||
|
but it should not be redirected during program running.
|
||||||
|
The reason is the same one introduced above.
|
||||||
|
|
||||||
|
\section csconsole__output Output Functions
|
||||||
|
|
||||||
|
In current implementation, EOL will not be converted automatically to CRLF.
|
||||||
|
This is different with other stream read functions provided in this namespace.
|
||||||
|
|
||||||
|
Comparing with other stream read functions provided in this namespace,
|
||||||
|
stream write function support hot switch of stdout and stderr.
|
||||||
|
Because they do not have internal buffer storing something.
|
||||||
|
|
||||||
|
In this namespace, there are various stream write function.
|
||||||
|
There is a list telling you how to choose one from them for using:
|
||||||
|
|
||||||
|
\li Functions with leading "e" (like eformat, ewrite) will write data into stderr,
|
||||||
|
otherwise they will write data into stdout.
|
||||||
|
\li Functions with embedded "format" (format, format_line, eformat, eformat_line) are output functions with format feature like \c std::fprintf(),
|
||||||
|
otherwise the functions with embedded "write" in the name (write, write_line, ewrite, ewrite_line) will only write plain string like \c std::fputs().
|
||||||
|
\li Functions with trailing "line" (format_line, write_line, eformat_line, ewrite_line) will write extra EOL to break current line.
|
||||||
|
This is commonly used, otherwise functions will only write the text provided by arguments,
|
||||||
|
without adding something.
|
||||||
|
|
||||||
|
*/
|
||||||
|
}
|
||||||
@@ -1,148 +0,0 @@
|
|||||||
namespace YYCC::EncodingHelper {
|
|
||||||
/**
|
|
||||||
|
|
||||||
\page encoding_helper Encoding Helper
|
|
||||||
|
|
||||||
YYCC::EncodingHelper namespace include all encoding related functions:
|
|
||||||
|
|
||||||
\li The convertion between ordinary string and UTF8 string which has been introduced in chapter \ref library_encoding.
|
|
||||||
\li Windows specific convertion between \c WCHAR, UTF8 string and string encoded by other encoding.
|
|
||||||
\li The convertion among UTF8, UTF16 and UTF32.
|
|
||||||
|
|
||||||
\section encoding_helper__ordinary_utf8_conv Ordinary & UTF8 Convertion
|
|
||||||
|
|
||||||
These convertion functions have been introduced in previous page.
|
|
||||||
See \ref library_encoding for more infomation.
|
|
||||||
|
|
||||||
YYCC supports following convertions:
|
|
||||||
|
|
||||||
\li #ToUTF8: Convert ordinary string to UTF8 string.
|
|
||||||
\li #ToUTF8View: Same as ToUTF8, but return string view instead.
|
|
||||||
\li #ToOrdinary: Convert UTF8 string to ordinary string.
|
|
||||||
\li #ToOrdinaryView: Same as ToOrdinary, but return string view instead.
|
|
||||||
|
|
||||||
\section encoding_helper__win_conv Windows Specific Convertion
|
|
||||||
|
|
||||||
During Windows programming, the convertion between Microsoft specified \c wchar_t and \c char is an essential operation.
|
|
||||||
Because Windows has 2 different function system, the functions ended with A and the functions ended with W.
|
|
||||||
(Microsoft specified \c wchar_t is \c 2 bytes long. It's different with Linux defined common 4 bytes long).
|
|
||||||
Thus YYCC provides these convertion functions in Windows to help programmer have better programming experience.
|
|
||||||
|
|
||||||
These functions are Windows specific, so they will be invisible in other platforms.
|
|
||||||
Please use them carefully (make sure that you are using them only in Windows environment).
|
|
||||||
|
|
||||||
YYCC supports following convertions:
|
|
||||||
|
|
||||||
\li #WcharToChar: Convert \c wchar_t string to code page specified string.
|
|
||||||
\li #CharToWchar: The reversed convertion of WcharToChar.
|
|
||||||
\li #CharToChar: Convert string between 2 different code pages. It's a shortcut of calling CharToWchar and WcharToChar successively.
|
|
||||||
\li #WcharToUTF8: Convert \c wchar_t string to UTF8 string.
|
|
||||||
\li #UTF8ToWchar: The reversed convertion of WcharToUTF8.
|
|
||||||
\li #CharToUTF8: Convert code page specified string to UTF8 string.
|
|
||||||
\li #UTF8ToChar: The reversed convertion of CharToUTF8.
|
|
||||||
|
|
||||||
Code Page is a Windows concept.
|
|
||||||
If you don't understand it, please view corresponding Microsoft documentation.
|
|
||||||
|
|
||||||
\section encoding_helper__utf_conv UTF8 UTF16 UTF32 Convertion
|
|
||||||
|
|
||||||
The convertion between UTF8, UTF16 and UTF32 is not common but essential.
|
|
||||||
These convertions can be achieved by standard library functions and classes.
|
|
||||||
(they are actually done by standard library functions in our implementation)
|
|
||||||
But we provided functions are easy to use and have clear interface.
|
|
||||||
|
|
||||||
These functions are different with the functions introduced above.
|
|
||||||
They can be used in any platform, not confined in Windows platforms.
|
|
||||||
|
|
||||||
YYCC supports following convertions:
|
|
||||||
|
|
||||||
\li #UTF8ToUTF16: Convert UTF8 string to UTF16 string.
|
|
||||||
\li #UTF16ToUTF8: The reversed convertion of UTF8ToUTF16.
|
|
||||||
\li #UTF8ToUTF32: Convert UTF8 string to UTF32 string.
|
|
||||||
\li #UTF32ToUTF8: The reversed convertion of UTF8ToUTF32.
|
|
||||||
|
|
||||||
\section encoding_helper__overloads Function Overloads
|
|
||||||
|
|
||||||
Every encoding convertion functions (except the convertion between UTF8 and ordinary string) have 4 different overloads for different scenarios.
|
|
||||||
Take #WcharToChar for example.
|
|
||||||
There are following 4 overloads:
|
|
||||||
|
|
||||||
\code
|
|
||||||
bool WcharToChar(const std::wstring_view& src, std::string& dst, UINT code_page);
|
|
||||||
bool WcharToChar(const wchar_t* src, std::string& dst, UINT code_page);
|
|
||||||
std::string WcharToChar(const std::wstring_view& src, UINT code_page);
|
|
||||||
std::string WcharToChar(const wchar_t* src, UINT code_page);
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
\subsection encoding_helper__overloads_destination Destination String
|
|
||||||
|
|
||||||
According to the return value, these 4 overload can be divided into 2 types.
|
|
||||||
The first type returns bool. The second type returns \c std::string instance.
|
|
||||||
|
|
||||||
For the first type, it always return bool to indicate whether the convertion is success.
|
|
||||||
Due to this, the function must require an argument for holding the result string.
|
|
||||||
So you can see the functions belonging to this type always require a reference to \c std::string in argument.
|
|
||||||
|
|
||||||
Oppositely, the second directly returns result by return value.
|
|
||||||
It doesn't care the success of convertion and will return empty string if convertion failed.
|
|
||||||
Programmer can more naturally use it because the retuen value itself is the result.
|
|
||||||
There is no need to declare a variable before calling convertion function for holding result.
|
|
||||||
|
|
||||||
All in all, the first type overload should be used in strict scope.
|
|
||||||
The success of convertion will massively affect the behavior of your following code.
|
|
||||||
For example, the convertion code is delivered to some system function and it should not be empty and etc.
|
|
||||||
The second type overload usually is used in lossen scenarios.
|
|
||||||
For exmaple, this overload usually is used in console output because it usually doesn't matter.
|
|
||||||
There is no risk even if the convertion failed (just output a blank string).
|
|
||||||
|
|
||||||
For the first type, please note that there is \b NO guarantee that the argument holding return value is not changed.
|
|
||||||
Even the convertion is failed, the argument holding return value may still be changed by function itself.
|
|
||||||
|
|
||||||
In this case, the type of result is \c std::string because this is function required.
|
|
||||||
In other functions, such as #WcharToUTF8, the type of result can be \c yycc_u8string or etc.
|
|
||||||
So please note the type of result is decided by convertion function itself, not only \c std::string.
|
|
||||||
|
|
||||||
\subsection encoding_helper__overloads__source Source String
|
|
||||||
|
|
||||||
According to the way providing source string,
|
|
||||||
these 4 overload also can be divided into 2 types.
|
|
||||||
The first type take a reference to constant \c std::wstring_view.
|
|
||||||
The second type take a pointer to constant \c wchar_t.
|
|
||||||
|
|
||||||
For first type, it will take the whole string for convertion, including \b embedded NUL terminal.
|
|
||||||
Please note we use string view as argument.
|
|
||||||
It is compatible with corresponding raw string pointer and string container.
|
|
||||||
So it is safe to directly pass \c std::wstring for this function.
|
|
||||||
|
|
||||||
For second type, it will assume that you passed argument is a NUL terminated string and send it for convertion.
|
|
||||||
|
|
||||||
The result is clear.
|
|
||||||
If you want to process string with \b embedded NUL terminal, please choose first type overload.
|
|
||||||
Otherwise the second type overload is enough.
|
|
||||||
|
|
||||||
Same as destination string, the type of source is also decided by the convertion function itself.
|
|
||||||
For exmaple, the type of source in #UTF8ToWchar is \c yycc_u8string_view and \c yycc_char8_t,
|
|
||||||
not \c std::wstring and \c wchar_t.
|
|
||||||
|
|
||||||
\subsection encoding_helper__overloads__extra Extra Argument
|
|
||||||
|
|
||||||
There is an extra argument called \c code_page for #WcharToChar.
|
|
||||||
It indicates the code page of destination string,
|
|
||||||
because this function will convert \c wchar_t string to the string with specified code page encoding.
|
|
||||||
|
|
||||||
Some convertion functions have extra argument like this,
|
|
||||||
because they need more infomations to decide what they need to do.
|
|
||||||
Some convertion functions don't have extra argument.
|
|
||||||
For exmaple, the convertion between \c wchar_t string and UTF8 string.
|
|
||||||
Because both source string and destination string are concrete.
|
|
||||||
There is no need to provide any more infomations.
|
|
||||||
|
|
||||||
\subsection encoding_helper__overloads__conclusion Conclusion
|
|
||||||
|
|
||||||
Mixing 2 types of source string and 2 types of destination string,
|
|
||||||
we have 4 different overload as we illustrated before.
|
|
||||||
Programmer can use them freely according to your requirements.
|
|
||||||
And don't forget to provide extra argument if function required.
|
|
||||||
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
93
doc/src/carton/fft.dox
Normal file
93
doc/src/carton/fft.dox
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
namespace yycc::carton::fft {
|
||||||
|
/**
|
||||||
|
|
||||||
|
\page fft Homemade FFT
|
||||||
|
|
||||||
|
This namespace provides a fast Fourier transform (FFT) implementation for signal processing applications.
|
||||||
|
It includes classes for performing FFT computations on complex and real-valued signals, along with
|
||||||
|
window functions to reduce spectral leakage.
|
||||||
|
|
||||||
|
\section fft__basic_usage Basic Usage
|
||||||
|
|
||||||
|
To use the FFT functionality for general purposes, use the FriendlyFft class:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include "yycc/carton/fft.hpp"
|
||||||
|
using namespace yycc::carton::fft;
|
||||||
|
|
||||||
|
// Create FFT instance for 8-point transform
|
||||||
|
FriendlyFft<size_t, float, 8u> fft;
|
||||||
|
|
||||||
|
// Prepare input data (must be power of 2 in length)
|
||||||
|
float time_scope[8] = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f};
|
||||||
|
float freq_scope[4]; // Output is half the input size
|
||||||
|
|
||||||
|
// Create window function to reduce spectral leakage
|
||||||
|
Window<size_t, float, 8u> window(WindowType::HanningWindow);
|
||||||
|
|
||||||
|
// Perform FFT transformation
|
||||||
|
fft.easy_compute(time_scope, freq_scope, window);
|
||||||
|
|
||||||
|
// freq_scope now contains frequency domain data
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section fft__window_functions Window Functions
|
||||||
|
|
||||||
|
The library provides window functions to reduce spectral leakage:
|
||||||
|
|
||||||
|
\code
|
||||||
|
// Create a Hanning window for 16-point data
|
||||||
|
Window<size_t, float, 16u> hanning_window(WindowType::HanningWindow);
|
||||||
|
|
||||||
|
// Apply window to your data
|
||||||
|
float data[16];
|
||||||
|
// ... initialize data ...
|
||||||
|
hanning_window.apply_window(data);
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section fft__direct_fft Direct FFT Computation
|
||||||
|
|
||||||
|
For more control over the FFT computation, use the core Fft class:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include "yycc/carton/fft.hpp"
|
||||||
|
|
||||||
|
// Create FFT instance for 16-point transform
|
||||||
|
Fft<size_t, double, 16u> fft;
|
||||||
|
|
||||||
|
// Prepare complex input data
|
||||||
|
std::complex<double> data[16];
|
||||||
|
// ... initialize complex data ...
|
||||||
|
|
||||||
|
// Perform FFT transformation
|
||||||
|
fft.compute(data);
|
||||||
|
// data now contains transformed values
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section fft__predefined_types Predefined Types
|
||||||
|
|
||||||
|
The library provides commonly used FFT types for convenience:
|
||||||
|
|
||||||
|
\code
|
||||||
|
// Float precision FFTs
|
||||||
|
Fft4F fft4f; // 4-point float FFT
|
||||||
|
Fft8F fft8f; // 8-point float FFT
|
||||||
|
Fft16F fft16f; // 16-point float FFT
|
||||||
|
Fft256F fft256f; // 256-point float FFT
|
||||||
|
|
||||||
|
// Double precision FFTs
|
||||||
|
Fft4 fft4; // 4-point double FFT
|
||||||
|
Fft8 fft8; // 8-point double FFT
|
||||||
|
Fft16 fft16; // 16-point double FFT
|
||||||
|
Fft256 fft256; // 256-point double FFT
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section fft__requirements Requirements
|
||||||
|
|
||||||
|
- Template parameters must satisfy certain constraints:
|
||||||
|
- \c TIndex: The index type used by FFT which must be an unsigned integral type.
|
||||||
|
- \c TFloat: The float point type used by FFT.
|
||||||
|
- \c VN: The point of FFT which must be a power of 2 and >= 2.
|
||||||
|
|
||||||
|
*/
|
||||||
|
}
|
||||||
@@ -1,30 +1,30 @@
|
|||||||
namespace YYCC::ExceptionHelper {
|
namespace yycc::carton::ironpad {
|
||||||
/**
|
/**
|
||||||
|
|
||||||
\page exception_helper Unhandled Exception Handler
|
\page ironpad Unhandled Exception Handler
|
||||||
|
|
||||||
Most Linux users are familiar with core dump.
|
Most Linux users are familiar with using core dump to find bugs.
|
||||||
However core dump is a tough work on Windows especially most Windows users are naive for getting core dump.
|
However finding bugs is a tough work on Windows especially most Windows users are naive for getting core dump.
|
||||||
So it is essential to make an easy-to-visit core dump Feature for Windows program.
|
So it is essential to make an easy-to-visit core dump feature for Windows program.
|
||||||
YYCC provides this feature in YYCC::ExceptionHelper.
|
This is the reason why I create this module, yycc::carton::ironpad.
|
||||||
|
|
||||||
You may know Google also has a similar and universal project called Crashpad used by Google Chrome.
|
You may know Google also has a similar and universal project called Crashpad used by Google Chrome.
|
||||||
That's right. But it is too heavy.
|
That's right. But it is too heavy.
|
||||||
I just want to implement a tiny but worked core dump feature on Windows.
|
I just want to implement a tiny but worked core dump feature on Windows.
|
||||||
|
|
||||||
This module is Windows specific.
|
This module is Windows specific.
|
||||||
It will be invisible on other platforms.
|
It still be available on other operating systems but all of its functions are do nothing.
|
||||||
|
|
||||||
\section exception_helper__usage Usage
|
\section ironpad__usage Usage
|
||||||
|
|
||||||
\subsection exception_helper__usage__code Register Code
|
\subsection ironpad__usage__code Register Code
|
||||||
|
|
||||||
In most scenarios, programmer only need call #Register when program started or module loaded.
|
In most scenarios, programmer only need call #startup when program started or module loaded.
|
||||||
And call #Unregister when program exited or module unloaded.
|
And call #shutdown when program exited or module unloaded.
|
||||||
All details are hidden by these 2 feature.
|
All details are hidden by these 2 feature.
|
||||||
Programmer do not need worried about the implementation of unhandled exception handler.
|
Programmer do not need worried about the implementation of unhandled exception handler.
|
||||||
|
|
||||||
Optionally, you can provide a function pointer during calling #Register as a callback.
|
Optionally, you can provide a function pointer during calling #startup as a callback.
|
||||||
The prototype of this function pointer is #ExceptionCallback.
|
The prototype of this function pointer is #ExceptionCallback.
|
||||||
This callback will be called if any unhandled exception happened.
|
This callback will be called if any unhandled exception happened.
|
||||||
It provides 2 pathes to log file and core dump file respectively.
|
It provides 2 pathes to log file and core dump file respectively.
|
||||||
@@ -35,21 +35,21 @@ However, please note the pathes provided by callback may be empty.
|
|||||||
In this case, it means that handler fail to create corresponding log files.
|
In this case, it means that handler fail to create corresponding log files.
|
||||||
Also, if you trying to register unhandled exception handler on the same process in different module with different callback,
|
Also, if you trying to register unhandled exception handler on the same process in different module with different callback,
|
||||||
only the callback provided in first success registering will be called when unhandled exception happened,
|
only the callback provided in first success registering will be called when unhandled exception happened,
|
||||||
due to \ref exception_helper__notes__singleton design.
|
due to \ref ironpad__notes__singleton design.
|
||||||
|
|
||||||
\subsection exception_helper__usage__location Location
|
\subsection ironpad__usage__location Location
|
||||||
|
|
||||||
When unhandled exception occurs,
|
When unhandled exception occurs,
|
||||||
unhandled exception handler will try to record error log and core dump in following path:
|
unhandled exception handler will try to record error log and core dump in following path:
|
||||||
|
|
||||||
\li Error Log: <TT>\%LOCALAPPDATA\%\\CrashDumps\\<I>program.exe</I>.<I>pid</I>.log</TT>
|
\li Error Log: <TT>\%LOCALAPPDATA\%\\IronPad\\<I>program.exe</I>.<I>pid</I>.log</TT>
|
||||||
\li Core Dump: <TT>\%LOCALAPPDATA\%\\CrashDumps\\<I>program.exe</I>.<I>pid</I>.dmp</TT>
|
\li Core Dump: <TT>\%LOCALAPPDATA\%\\IronPad\\<I>program.exe</I>.<I>pid</I>.dmp</TT>
|
||||||
|
|
||||||
The italic characters <I>program.exe</I> and <I>pid</I> will be replaced by program name and process ID respectively at runtime.
|
The italic characters <I>program.exe</I> and <I>pid</I> will be replaced by program name and process ID respectively at runtime.
|
||||||
Directory <TT>\%LOCALAPPDATA\%\\CrashDumps</TT> also is Windows used crash dump directory.
|
Directory <TT>\%LOCALAPPDATA\%\\IronPad</TT> is the dedicated directory for this module.
|
||||||
So you may see some other core dumps done by Windows in it.
|
So you may see the generated logs and dumps in it.
|
||||||
|
|
||||||
\subsection exception_helper__usage__last_remedy Last Remedy
|
\subsection ironpad__usage__last_remedy Last Remedy
|
||||||
|
|
||||||
If unhandled exception handler occurs error, these stuff may not be generated correctly.
|
If unhandled exception handler occurs error, these stuff may not be generated correctly.
|
||||||
The end user may not find them and send them to you.
|
The end user may not find them and send them to you.
|
||||||
@@ -65,40 +65,40 @@ Also please note the last remedy may still have a little bit possibility to occu
|
|||||||
especially the error occurs in back trace function.
|
especially the error occurs in back trace function.
|
||||||
There is no guaranteen that unhandled exception handler must generate error log and core dump.
|
There is no guaranteen that unhandled exception handler must generate error log and core dump.
|
||||||
|
|
||||||
\section exception_helper__notes Notes
|
\section ironpad__notes Notes
|
||||||
|
|
||||||
\subsection exception_helper__notes__thread_safe Thread Safe
|
\subsection ironpad__notes__thread_safe Thread Safe
|
||||||
|
|
||||||
All exposed functions in YYCC::ExceptionHelper are thread safe.
|
All exposed functions in this namespace are thread safe.
|
||||||
The implementation uses \c std:mutex to ensure this.
|
The implementation uses \c std::mutex to ensure this.
|
||||||
|
|
||||||
\subsection exception_helper__notes__singleton Singleton Handler
|
\subsection ironpad__notes__singleton Singleton Handler
|
||||||
|
|
||||||
YYCC::ExceptionHelper also have a mechanism that make sure the same unhandled exception handler implementation only appear once in the same process.
|
This namespace also have a mechanism that make sure the same unhandled exception handler implementation only appear once in the same process.
|
||||||
For example, you have an executable program A.exe, and 2 dynamic libraries B.dll and C.dll.
|
For example, you have an executable program A.exe, and 2 dynamic libraries B.dll and C.dll.
|
||||||
A.exe and B.dll use YYCC unhandled exception handler feature but C.dll not.
|
A.exe and B.dll use YYCC unhandled exception handler feature but C.dll not.
|
||||||
A.exe will load B.dll and C.dll at runtime.
|
A.exe will load B.dll and C.dll at runtime.
|
||||||
Although both A.exe and B.dll call #Register,
|
Although both A.exe and B.dll call #startup,
|
||||||
when unhandled exception occurs, there is only one error report output,
|
when unhandled exception occurs, there is only one error report output,
|
||||||
which may be generated by A.exe or B.dll accoridng to their order of loading.
|
which may be generated by A.exe or B.dll accoridng to their order of loading.
|
||||||
|
|
||||||
The core purpose of this is making sure the program will not output too many error report for the same unhandled exception,
|
The core purpose of this is making sure the program will not output too many error report for the same unhandled exception,
|
||||||
no matter how many modules calling #Register are loaded.
|
no matter how many modules calling #startup are loaded.
|
||||||
Only one error report is enough.
|
Only one error report is enough.
|
||||||
|
|
||||||
More precisely, we use \c CreateMutexW to create an unique mutex in Windows global scope,
|
More precisely, we use \c CreateMutexW to create an unique mutex in Windows global scope,
|
||||||
to make sure #Register only run once in the same process.
|
to make sure #startup only run once in the same process.
|
||||||
It is very like the implementation of singleton application.
|
It is very like the implementation of singleton application.
|
||||||
|
|
||||||
\subsection exception_helper__notes__recursive_calling Recursive Calling
|
\subsection ironpad__notes__recursive_calling Recursive Calling
|
||||||
|
|
||||||
The implementation of unhandled exception handler may also will throw exception.
|
The implementation of unhandled exception handler may also will throw exception.
|
||||||
This will cause infinite recursive calling.
|
This will cause infinite recursive calling.
|
||||||
YYCC::ExceptionHelper has internal mechanism to prevent this bad case.
|
This namespace has internal mechanism to prevent this bad case.
|
||||||
If this really happened, the handler will quit silent and will not cause any issue.
|
If this really happened, the handler will quit silent and will not cause any issue.
|
||||||
Programmer don't need to worry about this.
|
Programmer don't need to worry about this.
|
||||||
|
|
||||||
\subsection exception_helper__notes__user_callback The Timing of User Callback
|
\subsection ironpad__notes__user_callback The Timing of User Callback
|
||||||
|
|
||||||
The timing of calling user callback is the tail of unhandled exception handler.
|
The timing of calling user callback is the tail of unhandled exception handler.
|
||||||
It means that all log and coredump have been written if possible before calling callback.
|
It means that all log and coredump have been written if possible before calling callback.
|
||||||
102
doc/src/carton/lexer61.dox
Normal file
102
doc/src/carton/lexer61.dox
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
namespace yycc::carton::lexer61 {
|
||||||
|
/**
|
||||||
|
|
||||||
|
\page lexer61 Homemade Command Line Lexer
|
||||||
|
|
||||||
|
This namespace provides a lexer for parsing command-line arguments, supporting various quoting mechanisms,
|
||||||
|
escape sequences, and Unicode characters. It follows the standard shell parsing rules for handling
|
||||||
|
arguments containing spaces and special characters.
|
||||||
|
|
||||||
|
\section lexer61__basic_usage Basic Usage
|
||||||
|
|
||||||
|
To parse command line arguments, create a Lexer61 instance and call the Lexer61::lex() method:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include "yycc/carton/lexer61.hpp"
|
||||||
|
using namespace yycc::carton::lexer61;
|
||||||
|
|
||||||
|
Lexer61 lexer;
|
||||||
|
auto result = lexer.lex(u8"program arg1 arg2 arg3");
|
||||||
|
|
||||||
|
if (result.has_value()) {
|
||||||
|
auto args = std::move(result.value());
|
||||||
|
// args contains: [u8"program", u8"arg1", u8"arg2", u8"arg3"]
|
||||||
|
for (const auto& arg : args) {
|
||||||
|
std::wcout << reinterpret_cast<const wchar_t*>(arg.c_str()) << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section lexer61__quoting_support Quoting Support
|
||||||
|
|
||||||
|
The lexer supports both single and double quotes for grouping arguments with spaces:
|
||||||
|
|
||||||
|
\code
|
||||||
|
Lexer61 lexer;
|
||||||
|
|
||||||
|
// Double quotes
|
||||||
|
auto result1 = lexer.lex(u8R"(program "argument with spaces" end)");
|
||||||
|
// Result: [u8"program", u8"argument with spaces", u8"end"]
|
||||||
|
|
||||||
|
// Single quotes
|
||||||
|
auto result2 = lexer.lex(u8"program 'another argument' end");
|
||||||
|
// Result: [u8"program", u8"another argument", u8"end"]
|
||||||
|
|
||||||
|
// Mixed quotes
|
||||||
|
auto result3 = lexer.lex(u8R"(program "double quoted 'single inside'" 'single quoted "double inside"')");
|
||||||
|
// Result: [u8"program", u8"double quoted 'single inside'", u8"single quoted \"double inside\""]
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section lexer61__escape_sequences Escape Sequences
|
||||||
|
|
||||||
|
The lexer supports escape sequences for including special characters:
|
||||||
|
|
||||||
|
\code
|
||||||
|
Lexer61 lexer;
|
||||||
|
auto result = lexer.lex(u8R"(program escaped\ space "quoted with \" quote" 'single with \' quote')");
|
||||||
|
|
||||||
|
// Result: [u8"program", u8"escaped space", u8"quoted with \" quote", u8"single with \' quote"]
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section lexer61__unicode_support Unicode Support
|
||||||
|
|
||||||
|
The lexer fully supports Unicode characters in command line arguments:
|
||||||
|
|
||||||
|
\code
|
||||||
|
Lexer61 lexer;
|
||||||
|
auto result = lexer.lex(u8"程序 中文 参数");
|
||||||
|
// Result: [u8"程序", u8"中文", u8"参数"]
|
||||||
|
|
||||||
|
// With quotes
|
||||||
|
auto result2 = lexer.lex(u8R"(程序 "中文 参数" '另一个"引号"参数')");
|
||||||
|
// Result: [u8"程序", u8"中文 参数", u8"另一个\"引号\"参数"]
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section lexer61__empty_arguments Empty Arguments
|
||||||
|
|
||||||
|
Empty arguments can be represented with empty quotes:
|
||||||
|
|
||||||
|
\code
|
||||||
|
Lexer61 lexer;
|
||||||
|
auto result = lexer.lex(u8R"(program "" '')");
|
||||||
|
|
||||||
|
// Result: [u8"program", u8"", u8""]
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section lexer61__error_handling Error Handling
|
||||||
|
|
||||||
|
The lexer uses \c std::expected for error handling:
|
||||||
|
|
||||||
|
\code
|
||||||
|
Lexer61 lexer;
|
||||||
|
auto result = lexer.lex(u8R"(program "unclosed quote)");
|
||||||
|
|
||||||
|
if (!result.has_value()) {
|
||||||
|
// Handle error - in this case, unclosed quote
|
||||||
|
std::cerr << "Error: unclosed quote" << std::endl;
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
*/
|
||||||
|
}
|
||||||
202
doc/src/carton/pycodec.dox
Normal file
202
doc/src/carton/pycodec.dox
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
namespace yycc::carton::pycodec {
|
||||||
|
/**
|
||||||
|
\page pycodec Unified Codec (Python-like Codec)
|
||||||
|
|
||||||
|
\section pycodec__overview Overview
|
||||||
|
|
||||||
|
The unified encoding conversion module provides a consistent interface for character encoding conversion across different platforms.
|
||||||
|
It automatically selects the appropriate backend implementation based on the platform and available features.
|
||||||
|
|
||||||
|
\section pycodec__classes Available Classes
|
||||||
|
|
||||||
|
\subsection pycodec__classes__char Character to/from UTF-8 Conversion
|
||||||
|
|
||||||
|
Convert between named encodings and UTF-8 using a unified interface:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include <yycc/carton/pycodec.hpp>
|
||||||
|
|
||||||
|
// Example: Converting from a named encoding to UTF-8
|
||||||
|
CharToUtf8 converter("GBK"); // or "ISO-8859-1", "SHIFT-JIS", etc.
|
||||||
|
|
||||||
|
std::string gbk_text = "你好,世界!";
|
||||||
|
auto result = converter.to_utf8(gbk_text);
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::u8string utf8_text = result.value();
|
||||||
|
// Use utf8_text...
|
||||||
|
} else {
|
||||||
|
// Handle conversion error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\code
|
||||||
|
// Example: Converting from UTF-8 to a named encoding
|
||||||
|
Utf8ToChar converter("GBK");
|
||||||
|
|
||||||
|
std::u8string utf8_text = u8"Hello, 世界!";
|
||||||
|
auto result = converter.to_char(utf8_text);
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::string gbk_text = result.value();
|
||||||
|
// Use gbk_text...
|
||||||
|
} else {
|
||||||
|
// Handle conversion error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\subsection pycodec__classes__wchar Wide Character to/from UTF-8 Conversion
|
||||||
|
|
||||||
|
Convert between wide character strings and UTF-8:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include <yycc/carton/pycodec.hpp>
|
||||||
|
|
||||||
|
// Example: Converting wide character to UTF-8
|
||||||
|
WcharToUtf8 converter;
|
||||||
|
|
||||||
|
std::wstring wide_text = L"Hello, 世界!";
|
||||||
|
auto result = converter.to_utf8(wide_text);
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::u8string utf8_text = result.value();
|
||||||
|
// Use utf8_text...
|
||||||
|
} else {
|
||||||
|
// Handle conversion error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\code
|
||||||
|
// Example: Converting UTF-8 to wide character
|
||||||
|
Utf8ToWchar converter;
|
||||||
|
|
||||||
|
std::u8string utf8_text = u8"Hello, 世界!";
|
||||||
|
auto result = converter.to_wchar(utf8_text);
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::wstring wide_text = result.value();
|
||||||
|
// Use wide_text...
|
||||||
|
} else {
|
||||||
|
// Handle conversion error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\subsection pycodec__classes__utf16_utf32 UTF-8 to/from UTF-16/UTF-32 Conversion
|
||||||
|
|
||||||
|
Convert between UTF encodings:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include <yycc/carton/pycodec.hpp>
|
||||||
|
|
||||||
|
// Example: Converting UTF-8 to UTF-16
|
||||||
|
Utf8ToUtf16 converter;
|
||||||
|
|
||||||
|
std::u8string utf8_text = u8"Hello, 世界!";
|
||||||
|
auto result = converter.to_utf16(utf8_text);
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::u16string utf16_text = result.value();
|
||||||
|
// Use utf16_text...
|
||||||
|
} else {
|
||||||
|
// Handle conversion error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\code
|
||||||
|
// Example: Converting UTF-16 to UTF-8
|
||||||
|
Utf16ToUtf8 converter;
|
||||||
|
|
||||||
|
std::u16string utf16_text = u"Hello, 世界!";
|
||||||
|
auto result = converter.to_utf8(utf16_text);
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::u8string utf8_text = result.value();
|
||||||
|
// Use utf8_text...
|
||||||
|
} else {
|
||||||
|
// Handle conversion error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\code
|
||||||
|
// Example: Converting UTF-8 to UTF-32
|
||||||
|
Utf8ToUtf32 converter;
|
||||||
|
|
||||||
|
std::u8string utf8_text = u8"Hello, 世界! 🌍";
|
||||||
|
auto result = converter.to_utf32(utf8_text);
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::u32string utf32_text = result.value();
|
||||||
|
// Use utf32_text...
|
||||||
|
} else {
|
||||||
|
// Handle conversion error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\code
|
||||||
|
// Example: Converting UTF-32 to UTF-8
|
||||||
|
Utf32ToUtf8 converter;
|
||||||
|
|
||||||
|
std::u32string utf32_text = U"Hello, 世界! 🌍";
|
||||||
|
auto result = converter.to_utf8(utf32_text);
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::u8string utf8_text = result.value();
|
||||||
|
// Use utf8_text...
|
||||||
|
} else {
|
||||||
|
// Handle conversion error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section pycodec__utility Utility Functions
|
||||||
|
|
||||||
|
\subsection pycodec__utility__validation Encoding Name Validation
|
||||||
|
|
||||||
|
Check if an encoding name is valid in the current environment:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include <yycc/carton/pycodec.hpp>
|
||||||
|
|
||||||
|
// Example: Validating an encoding name
|
||||||
|
bool is_valid = is_valid_encoding_name(u8"UTF-8");
|
||||||
|
if (is_valid) {
|
||||||
|
std::cout << "UTF-8 is a valid encoding name\n";
|
||||||
|
} else {
|
||||||
|
std::cout << "UTF-8 is not supported\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test another encoding
|
||||||
|
is_valid = is_valid_encoding_name(u8"GBK");
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section pycodec__error_handling Error Handling
|
||||||
|
|
||||||
|
All functions in this module return a result containing either
|
||||||
|
a ConvError struct represents conversion errors, or the final converted string.
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include <yycc/carton/pycodec.hpp>
|
||||||
|
|
||||||
|
CharToUtf8 converter("INVALID_ENCODING_NAME");
|
||||||
|
std::string text = "Hello";
|
||||||
|
|
||||||
|
auto result = converter.to_utf8(text);
|
||||||
|
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::u8string converted = result.value();
|
||||||
|
// Process successfully converted string
|
||||||
|
} else {
|
||||||
|
// Handle conversion failure
|
||||||
|
std::cout << "Conversion failed\n";
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section pycodec__backend_specifics Platform-Specific Backends
|
||||||
|
|
||||||
|
For detailed information about the specific platform backends, see:
|
||||||
|
|
||||||
|
\li \ref encoding__windows : Windows-specific implementation using Win32 APIs
|
||||||
|
\li \ref encoding__iconv : Iconv-based implementation for POSIX-like systems
|
||||||
|
|
||||||
|
\section pycodec__notes Notes
|
||||||
|
|
||||||
|
For all supported encoding names and their aliases,
|
||||||
|
please browse code written in <TT>script/pycodec</TT> located in our source code.
|
||||||
|
|
||||||
|
Please also note that not all encoding name has implementation for all platforms.
|
||||||
|
Some uncommon encoding names are not supported on some backend due to the limitations of the corresponding baskend.
|
||||||
|
These also can be found in that directory introduced above.
|
||||||
|
|
||||||
|
*/
|
||||||
|
}
|
||||||
107
doc/src/carton/tabulate.dox
Normal file
107
doc/src/carton/tabulate.dox
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
namespace yycc::carton::tabulate {
|
||||||
|
/**
|
||||||
|
|
||||||
|
\page tabulate Tabulate Utilities
|
||||||
|
|
||||||
|
This namespace provides utilities for creating formatted tables in console output.
|
||||||
|
It supports Unicode text, automatic column width calculation, customizable headers,
|
||||||
|
and flexible display options.
|
||||||
|
|
||||||
|
\section tabulate__basic_usage Basic Usage
|
||||||
|
|
||||||
|
To create a simple table with headers and data rows:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include "yycc/carton/tabulate.hpp"
|
||||||
|
using namespace yycc::carton::tabulate;
|
||||||
|
|
||||||
|
// Create a table with 3 columns
|
||||||
|
Tabulate table(3);
|
||||||
|
|
||||||
|
// Set the header
|
||||||
|
table.set_header({u8"Name", u8"Age", u8"City"});
|
||||||
|
|
||||||
|
// Add data rows
|
||||||
|
table.add_row({u8"Alice", u8"30", u8"New York"});
|
||||||
|
table.add_row({u8"Bob", u8"25", u8"Los Angeles"});
|
||||||
|
table.add_row({u8"Charlie", u8"35", u8"Chicago"});
|
||||||
|
|
||||||
|
// Print the table
|
||||||
|
table.print();
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
This will output:
|
||||||
|
\verbatim
|
||||||
|
Name Age City
|
||||||
|
----- --- ---------
|
||||||
|
Alice 30 New York
|
||||||
|
Bob 25 Los Angeles
|
||||||
|
Charlie 35 Chicago
|
||||||
|
\endverbatim
|
||||||
|
|
||||||
|
\section tabulate__customization Customization Options
|
||||||
|
|
||||||
|
You can customize various aspects of the table:
|
||||||
|
|
||||||
|
\code
|
||||||
|
Tabulate table(3);
|
||||||
|
|
||||||
|
// Set a custom separator bar
|
||||||
|
table.set_bar(u8"===");
|
||||||
|
|
||||||
|
// Add a prefix (useful for indentation)
|
||||||
|
table.set_prefix(u8"> ");
|
||||||
|
|
||||||
|
// Control header and bar visibility
|
||||||
|
table.show_header(false); // Hide header
|
||||||
|
table.show_bar(true); // Show separator bar
|
||||||
|
|
||||||
|
// Set header
|
||||||
|
table.set_header({u8"Col1", u8"Col2", u8"Col3"});
|
||||||
|
|
||||||
|
// Add data
|
||||||
|
table.add_row({u8"data1", u8"data2", u8"data3"});
|
||||||
|
|
||||||
|
table.print();
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section tabulate__unicode_support Unicode Support
|
||||||
|
|
||||||
|
The library fully supports Unicode text in tables:
|
||||||
|
|
||||||
|
\code
|
||||||
|
Tabulate table(3);
|
||||||
|
|
||||||
|
// Set Unicode header
|
||||||
|
table.set_header({u8"姓名", u8"年龄", u8"城市"});
|
||||||
|
|
||||||
|
// Add Unicode data
|
||||||
|
table.add_row({u8"张三", u8"30", u8"北京"});
|
||||||
|
table.add_row({u8"李四", u8"25", u8"上海"});
|
||||||
|
|
||||||
|
table.print();
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section tabulate__stream_output Stream Output
|
||||||
|
|
||||||
|
You can print to any output stream, not just stdout:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
Tabulate table(2);
|
||||||
|
table.set_header({u8"Key", u8"Value"});
|
||||||
|
table.add_row({u8"temp", u8"25C"});
|
||||||
|
|
||||||
|
// Print to file
|
||||||
|
std::ofstream file("table.txt");
|
||||||
|
table.print(file);
|
||||||
|
|
||||||
|
// Or print to stringstream
|
||||||
|
std::stringstream ss;
|
||||||
|
table.print(ss);
|
||||||
|
std::string table_str = ss.str();
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
*/
|
||||||
|
}
|
||||||
121
doc/src/carton/termcolor.dox
Normal file
121
doc/src/carton/termcolor.dox
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
namespace yycc::carton::termcolor {
|
||||||
|
/**
|
||||||
|
|
||||||
|
\page termcolor Terminal Color Utilities
|
||||||
|
|
||||||
|
This namespace provides functions to generate ANSI escape sequence for terminal font color and style.
|
||||||
|
It also provides functions to add color and style for given string with ANSI Escape Sequence.
|
||||||
|
|
||||||
|
Supported color is limited in 16 colors,
|
||||||
|
because these color is implemented by ASCII Escape Code: https://en.wikipedia.org/wiki/ANSI_escape_code .
|
||||||
|
So if your terminal do not support this, such as default Windows terminal, or teletypewriter,
|
||||||
|
you will see some unrecognised characters surrounding with your output.
|
||||||
|
That's ASCII Escape Code.
|
||||||
|
|
||||||
|
This namespace is basically the imitation of the Python package with same name.
|
||||||
|
|
||||||
|
\section termcolor__basic_colors Basic Colors
|
||||||
|
|
||||||
|
To use basic foreground and background colors:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include "yycc/carton/termcolor.hpp"
|
||||||
|
using namespace yycc::carton::termcolor;
|
||||||
|
|
||||||
|
// Print red text
|
||||||
|
std::cout << colored(u8"Error message", Color::Red) << std::endl;
|
||||||
|
|
||||||
|
// Print blue text with yellow background
|
||||||
|
cprint(u8"Info message", Color::Blue, Color::Yellow);
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section termcolor__light_colors Light Colors
|
||||||
|
|
||||||
|
The namespace provides both standard and light versions of colors:
|
||||||
|
|
||||||
|
\code
|
||||||
|
// Standard green
|
||||||
|
std::cout << colored(u8"Success", Color::Green) << std::endl;
|
||||||
|
|
||||||
|
// Light green (brighter variant)
|
||||||
|
std::cout << colored(u8"Notice", Color::LightGreen) << std::endl;
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section termcolor__text_styles Text Styles
|
||||||
|
|
||||||
|
Multiple text styles can be combined using bitwise operations:
|
||||||
|
|
||||||
|
\code
|
||||||
|
// Bold text
|
||||||
|
std::cout << colored(u8"Important", Color::Red, Color::Default, Attribute::Bold) << std::endl;
|
||||||
|
|
||||||
|
// Underlined text
|
||||||
|
std::cout << colored(u8"Underlined", Color::Blue, Color::Default, Attribute::Underline) << std::endl;
|
||||||
|
|
||||||
|
// Combined styles
|
||||||
|
auto combined_style = yycc::cenum::merge(Attribute::Bold, Attribute::Italic);
|
||||||
|
cprint(u8"Bold and italic", Color::Magenta, Color::Default, combined_style);
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section termcolor__convenience_functions Convenience Functions
|
||||||
|
|
||||||
|
Several convenience functions are available for direct printing:
|
||||||
|
|
||||||
|
\code
|
||||||
|
// Print to stdout with color
|
||||||
|
cprint(u8"Hello World", Color::Green);
|
||||||
|
|
||||||
|
// Print to stderr with color and style
|
||||||
|
ceprint(u8"Warning", Color::Yellow, Color::Default, Attribute::Bold);
|
||||||
|
|
||||||
|
// Print with line break
|
||||||
|
cprintln(u8"Success", Color::LightGreen);
|
||||||
|
|
||||||
|
// Print to stderr with line break
|
||||||
|
ceprintln(u8"Critical Error", Color::LightRed, Color::Default, Attribute::Blink);
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section termcolor__color_enums Available Colors
|
||||||
|
|
||||||
|
Available foreground and background colors include:
|
||||||
|
|
||||||
|
\li Color::Black
|
||||||
|
\li Color::Red
|
||||||
|
\li Color::Green
|
||||||
|
\li Color::Yellow
|
||||||
|
\li Color::Blue
|
||||||
|
\li Color::Magenta
|
||||||
|
\li Color::Cyan
|
||||||
|
\li Color::White
|
||||||
|
\li Color::LightBlack
|
||||||
|
\li Color::LightRed
|
||||||
|
\li Color::LightGreen
|
||||||
|
\li Color::LightYellow
|
||||||
|
\li Color::LightBlue
|
||||||
|
\li Color::LightMagenta
|
||||||
|
\li Color::LightCyan
|
||||||
|
\li Color::LightWhite
|
||||||
|
\li Color::Default
|
||||||
|
|
||||||
|
\section termcolor__style_enums Available Styles
|
||||||
|
|
||||||
|
Available text styles include:
|
||||||
|
|
||||||
|
\li Attribute::Bold
|
||||||
|
\li Attribute::Dark
|
||||||
|
\li Attribute::Italic
|
||||||
|
\li Attribute::Underline
|
||||||
|
\li Attribute::Blink
|
||||||
|
\li Attribute::Reverse
|
||||||
|
\li Attribute::Concealed
|
||||||
|
\li Attribute::Default
|
||||||
|
|
||||||
|
\section termcolor__old_macros Where is Old Macros
|
||||||
|
|
||||||
|
If you have used YYCC 1.x version, you may know that these features are also presented but in a bunch of macros style.
|
||||||
|
These macros is removed since YYCC 2.0.
|
||||||
|
Since YYCC 2.0, we suggest you to use these new provided functions instead,
|
||||||
|
because they are more robust and correspond with our new style of coding by \c std::format and etc.
|
||||||
|
|
||||||
|
*/
|
||||||
|
}
|
||||||
103
doc/src/carton/wcwidth.dox
Normal file
103
doc/src/carton/wcwidth.dox
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
namespace yycc::carton::wcwidth {
|
||||||
|
/**
|
||||||
|
|
||||||
|
\page wcwidth Cross-platform Wcwidth
|
||||||
|
|
||||||
|
This namespace provides cross-platform implementations of character width calculation functions,
|
||||||
|
similar to the Linux-specific \c wcswidth function. It supports Unicode text and ANSI escape sequences,
|
||||||
|
making it suitable for calculating display widths of text in terminals across different platforms.
|
||||||
|
|
||||||
|
\section wcwidth__basic_usage Basic Usage
|
||||||
|
|
||||||
|
To calculate the display width of a string in a terminal:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include <yycc/carton/wcwidth.hpp>
|
||||||
|
using namespace yycc::carton::wcwidth;
|
||||||
|
|
||||||
|
// Calculate width of ASCII text
|
||||||
|
size_t width1 = wcwidth(U'a'); // Returns 1
|
||||||
|
auto width2 = wcswidth(u8"Hello"); // Returns 5
|
||||||
|
|
||||||
|
// Calculate width of Unicode text
|
||||||
|
auto width3 = wcswidth(u8"你好世界"); // Returns 8 (Chinese chars typically take 2 spaces each)
|
||||||
|
auto width4 = wcswidth(u8"ありがとう"); // Returns 10 (Japanese katakana)
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section wcwidth__ansi_support ANSI Escape Sequence Support
|
||||||
|
|
||||||
|
The library can handle ANSI escape sequences (like color codes) in text
|
||||||
|
which is not supported by Linux \c wcswidth.
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include "yycc/carton/termcolor.hpp"
|
||||||
|
using namespace yycc::carton::termcolor;
|
||||||
|
|
||||||
|
// Calculate width of colored text
|
||||||
|
auto colored_text = colored(u8"Hello World", Color::Red);
|
||||||
|
auto width = wcswidth(colored_text);
|
||||||
|
// Returns the width of "Hello World" (ignoring the ANSI escape sequences)
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section wcwidth__error_handling Error Handling
|
||||||
|
|
||||||
|
The functions use \c std::expected for error handling:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include "yycc/carton/wcwidth.hpp"
|
||||||
|
|
||||||
|
// Safe way to handle potential errors
|
||||||
|
auto result = wcswidth(u8"\033?"); // Invalid ANSI sequence
|
||||||
|
|
||||||
|
if (result.has_value()) {
|
||||||
|
size_t width = result.value();
|
||||||
|
std::cout << "Width: " << width << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cout << "Invalid string" << std::endl;
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section wcwidth__character_types Supported Character Types
|
||||||
|
|
||||||
|
The library provides two main functions:
|
||||||
|
|
||||||
|
- wcwidth(): Calculate width of a single character.
|
||||||
|
- wcswidth(): Calculate width of a string.
|
||||||
|
|
||||||
|
\code
|
||||||
|
// Using wcwidth for single characters
|
||||||
|
size_t char_width = wcwidth(U'A'); // Returns 1
|
||||||
|
size_t emoji_width = wcwidth(U'😀'); // Returns width of emoji
|
||||||
|
|
||||||
|
// Using wcswidth for strings
|
||||||
|
auto str_width1 = wcswidth(u8"Hello"); // Returns 5
|
||||||
|
auto str_width2 = wcswidth(U"Unicode String"); // Returns string length in display width
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section wcwidth__platform_differences Platform Considerations
|
||||||
|
|
||||||
|
This library addresses platform differences in wide character handling:
|
||||||
|
|
||||||
|
\li On Linux, \c whar_t is 4 bytes and can represent any Unicode character
|
||||||
|
\li On Windows, \c whar_t is 2 bytes and may require surrogate pairs for some characters
|
||||||
|
|
||||||
|
So this library uses \c char32_t internally to ensure consistent behavior across platforms,
|
||||||
|
and expose functions with \c char32_t and \c char8_t string container respectively for user.
|
||||||
|
|
||||||
|
\section wcwidth__unicode_support Unicode and East Asian Widths
|
||||||
|
|
||||||
|
The library properly handles East Asian character widths:
|
||||||
|
|
||||||
|
\code
|
||||||
|
// Chinese characters typically occupy 2 terminal spaces
|
||||||
|
auto chinese_width = wcswidth(u8"中文"); // Returns 4 (2 chars × 2 spaces each)
|
||||||
|
|
||||||
|
// Japanese characters
|
||||||
|
auto kana_width = wcswidth(u8"ありがとう"); // Returns 10 (5 kana × 2 spaces each)
|
||||||
|
|
||||||
|
// Mixed text
|
||||||
|
auto mixed_width = wcswidth(u8"Hello 世界"); // Returns 8 (5 ASCII + 1 space + 2 Chinese chars × 2 spaces)
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
*/
|
||||||
|
}
|
||||||
166
doc/src/encoding/iconv.dox
Normal file
166
doc/src/encoding/iconv.dox
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
namespace yycc::encoding::iconv {
|
||||||
|
/**
|
||||||
|
\page encoding__iconv Iconv-based Codec
|
||||||
|
|
||||||
|
\section encoding__iconv__overview Overview
|
||||||
|
|
||||||
|
The Iconv-based encoding conversion module provides encoding conversion functionality using the iconv library.
|
||||||
|
This module is available when you are in POSIX system, or enable iconv support manually when configuring the library.
|
||||||
|
|
||||||
|
\section encoding__iconv__classes Available Classes
|
||||||
|
|
||||||
|
\subsection encoding__iconv__classes__char Char to/from UTF-8 Conversion
|
||||||
|
|
||||||
|
Convert between character encodings and UTF-8:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include <yycc/encoding/iconv.hpp>
|
||||||
|
|
||||||
|
// Example: Creating a converter from Latin-1 to UTF-8
|
||||||
|
CharToUtf8 converter("ISO-8859-1");
|
||||||
|
|
||||||
|
std::string latin1_text = "Café résumé naïve";
|
||||||
|
auto result = converter.to_utf8(latin1_text);
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::u8string utf8_text = result.value();
|
||||||
|
// Use utf8_text...
|
||||||
|
} else {
|
||||||
|
// Handle conversion error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\code
|
||||||
|
// Example: Creating a converter from UTF-8 to Latin-1
|
||||||
|
Utf8ToChar converter("ISO-8859-1");
|
||||||
|
|
||||||
|
std::u8string utf8_text = u8"Café résumé naïve";
|
||||||
|
auto result = converter.to_char(utf8_text);
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::string latin1_text = result.value();
|
||||||
|
// Use latin1_text...
|
||||||
|
} else {
|
||||||
|
// Handle conversion error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\subsection encoding__iconv__classes__wchar WChar to/from UTF-8 Conversion
|
||||||
|
|
||||||
|
Convert between wide character and UTF-8:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include <yycc/encoding/iconv.hpp>
|
||||||
|
|
||||||
|
// Example: Converting wide character to UTF-8
|
||||||
|
WcharToUtf8 converter;
|
||||||
|
|
||||||
|
std::wstring wide_text = L"Hello, 世界!";
|
||||||
|
auto result = converter.to_utf8(wide_text);
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::u8string utf8_text = result.value();
|
||||||
|
// Use utf8_text...
|
||||||
|
} else {
|
||||||
|
// Handle conversion error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\code
|
||||||
|
// Example: Converting UTF-8 to wide character
|
||||||
|
Utf8ToWchar converter;
|
||||||
|
|
||||||
|
std::u8string utf8_text = u8"Hello, 世界!";
|
||||||
|
auto result = converter.to_wchar(utf8_text);
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::wstring wide_text = result.value();
|
||||||
|
// Use wide_text...
|
||||||
|
} else {
|
||||||
|
// Handle conversion error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\subsection encoding__iconv__classes__utf16_utf32 UTF-8 to/from UTF-16/UTF-32 Conversion
|
||||||
|
|
||||||
|
Convert between UTF encodings:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include <yycc/encoding/iconv.hpp>
|
||||||
|
|
||||||
|
// Example: Converting UTF-8 to UTF-16
|
||||||
|
Utf8ToUtf16 converter;
|
||||||
|
|
||||||
|
std::u8string utf8_text = u8"Hello, 世界!";
|
||||||
|
auto result = converter.to_utf16(utf8_text);
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::u16string utf16_text = result.value();
|
||||||
|
// Use utf16_text...
|
||||||
|
} else {
|
||||||
|
// Handle conversion error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\code
|
||||||
|
// Example: Converting UTF-16 to UTF-8
|
||||||
|
Utf16ToUtf8 converter;
|
||||||
|
|
||||||
|
std::u16string utf16_text = u"Hello, 世界!";
|
||||||
|
auto result = converter.to_utf8(utf16_text);
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::u8string utf8_text = result.value();
|
||||||
|
// Use utf8_text...
|
||||||
|
} else {
|
||||||
|
// Handle conversion error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\code
|
||||||
|
// Example: Converting UTF-8 to UTF-32
|
||||||
|
Utf8ToUtf32 converter;
|
||||||
|
|
||||||
|
std::u8string utf8_text = u8"Hello, 世界! 🌍";
|
||||||
|
auto result = converter.to_utf32(utf8_text);
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::u32string utf32_text = result.value();
|
||||||
|
// Use utf32_text...
|
||||||
|
} else {
|
||||||
|
// Handle conversion error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\code
|
||||||
|
// Example: Converting UTF-32 to UTF-8
|
||||||
|
Utf32ToUtf8 converter;
|
||||||
|
|
||||||
|
std::u32string utf32_text = U"Hello, 世界! 🌍";
|
||||||
|
auto result = converter.to_utf8(utf32_text);
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::u8string utf8_text = result.value();
|
||||||
|
// Use utf8_text...
|
||||||
|
} else {
|
||||||
|
// Handle conversion error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section encoding__iconv__error_handling Error Handling
|
||||||
|
|
||||||
|
All functions in this module return a result containing either
|
||||||
|
a ConvError struct represents conversion errors, or the final converted string.
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include <yycc/encoding/iconv.hpp>
|
||||||
|
|
||||||
|
CharToUtf8 converter("INVALID_ENCODING");
|
||||||
|
// Note: Constructor errors might be detected during conversion
|
||||||
|
|
||||||
|
std::string text = "Hello";
|
||||||
|
auto result = converter.to_utf8(text);
|
||||||
|
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::u8string converted = result.value();
|
||||||
|
// Process successfully converted string
|
||||||
|
} else {
|
||||||
|
// Handle conversion failure
|
||||||
|
std::cout << "Conversion failed\n";
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
*/
|
||||||
|
}
|
||||||
98
doc/src/encoding/stl.dox
Normal file
98
doc/src/encoding/stl.dox
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
namespace yycc::encoding::stl {
|
||||||
|
/**
|
||||||
|
\page encoding__stl STL-based Codec
|
||||||
|
|
||||||
|
\section encoding__stl__overview Overview
|
||||||
|
|
||||||
|
The STL-based encoding conversion module provides cross-platform encoding conversion functionality using the standard library's codecvt facets.
|
||||||
|
This module is designed to handle conversions between UTF-8, UTF-16, and UTF-32 encodings using the standard C++ locale facilities.
|
||||||
|
|
||||||
|
\section encoding__stl__attentions Attentions
|
||||||
|
|
||||||
|
The underlying implementation of this module is deprecated by C++ STL and may be removed in future versions of C++.
|
||||||
|
So please use this module carefully or considering use our \ref pycodec module instead.
|
||||||
|
|
||||||
|
\section encoding__stl__functions Available Functions
|
||||||
|
|
||||||
|
\subsection encoding__stl__functions__utf16 UTF-8 to/from UTF-16 Conversion
|
||||||
|
|
||||||
|
Convert between UTF-8 and UTF-16 encodings using standard library facilities:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include <yycc/encoding/stl.hpp>
|
||||||
|
|
||||||
|
// Example: Converting UTF-8 to UTF-16
|
||||||
|
std::u8string utf8_text = u8"Hello, 世界!";
|
||||||
|
auto result = to_utf16(utf8_text);
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::u16string utf16_text = result.value();
|
||||||
|
// Use utf16_text...
|
||||||
|
} else {
|
||||||
|
// Handle conversion error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\code
|
||||||
|
// Example: Converting UTF-16 to UTF-8
|
||||||
|
std::u16string utf16_text = u"Hello, 世界!";
|
||||||
|
auto result = to_utf8(utf16_text);
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::u8string utf8_text = result.value();
|
||||||
|
// Use utf8_text...
|
||||||
|
} else {
|
||||||
|
// Handle conversion error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\subsection encoding__stl__functions__utf32 UTF-8 to/from UTF-32 Conversion
|
||||||
|
|
||||||
|
Convert between UTF-8 and UTF-32 encodings:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include <yycc/encoding/stl.hpp>
|
||||||
|
|
||||||
|
// Example: Converting UTF-8 to UTF-32
|
||||||
|
std::u8string utf8_text = u8"Hello, 世界! 🌍";
|
||||||
|
auto result = to_utf32(utf8_text);
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::u32string utf32_text = result.value();
|
||||||
|
// Use utf32_text...
|
||||||
|
} else {
|
||||||
|
// Handle conversion error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\code
|
||||||
|
// Example: Converting UTF-32 to UTF-8
|
||||||
|
std::u32string utf32_text = U"Hello, 世界! 🌍";
|
||||||
|
auto result = to_utf8(utf32_text);
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::u8string utf8_text = result.value();
|
||||||
|
// Use utf8_text...
|
||||||
|
} else {
|
||||||
|
// Handle conversion error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section encoding__stl__error_handling Error Handling
|
||||||
|
|
||||||
|
All functions in this module return a result containing either
|
||||||
|
a ConvError struct represents conversion errors, or the final converted string.
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include <yycc/encoding/stl.hpp>
|
||||||
|
|
||||||
|
std::u8string invalid_utf8 = "\xFF\xFE"; // Invalid UTF-8 sequence
|
||||||
|
auto result = to_utf16(invalid_utf8);
|
||||||
|
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::u16string converted = result.value();
|
||||||
|
// Process successfully converted string
|
||||||
|
} else {
|
||||||
|
// Handle conversion failure
|
||||||
|
std::cout << "Conversion failed\n";
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
*/
|
||||||
|
}
|
||||||
191
doc/src/encoding/windows.dox
Normal file
191
doc/src/encoding/windows.dox
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
namespace yycc::encoding::windows {
|
||||||
|
/**
|
||||||
|
\page encoding__windows Win32-based Codec
|
||||||
|
|
||||||
|
\section encoding__windows__overview Overview
|
||||||
|
|
||||||
|
The Windows-specific encoding conversion module provides encoding conversion functionality
|
||||||
|
using Windows API functions such as `WideCharToMultiByte` and `MultiByteToWideChar`.
|
||||||
|
This module is available only on Windows platforms and offers efficient conversion
|
||||||
|
between various character encodings including wide character, multi-byte, and UTF-8.
|
||||||
|
|
||||||
|
\section encoding__windows__functions Available Functions
|
||||||
|
|
||||||
|
\subsection encoding__windows__functions__wchar Wide Character to/from Multi-byte Conversion
|
||||||
|
|
||||||
|
Convert between wide character strings and multi-byte strings using Windows code pages:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include <yycc/encoding/windows.hpp>
|
||||||
|
|
||||||
|
// Example: Converting wide character string to multi-byte with specific code page
|
||||||
|
std::wstring wide_text = L"Hello, 世界!";
|
||||||
|
auto result = to_char(wide_text, CP_UTF8); // Using UTF-8 code page
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::string multi_byte_text = result.value();
|
||||||
|
// Use multi_byte_text...
|
||||||
|
} else {
|
||||||
|
// Handle conversion error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\code
|
||||||
|
// Example: Converting multi-byte string to wide character with specific code page
|
||||||
|
std::string multi_byte_text = "Hello, 世界!";
|
||||||
|
auto result = to_wchar(multi_byte_text, CP_UTF8);
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::wstring wide_text = result.value();
|
||||||
|
// Use wide_text...
|
||||||
|
} else {
|
||||||
|
// Handle conversion error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\subsection encoding__windows__functions__mbcs Multi-byte to/from Multi-byte Conversion
|
||||||
|
|
||||||
|
Convert between different multi-byte encodings by using wide character as an intermediate:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include <yycc/encoding/windows.hpp>
|
||||||
|
|
||||||
|
// Example: Converting between two different code pages
|
||||||
|
std::string source_text = "Hello, world!";
|
||||||
|
auto result = to_char(source_text, CP_ACP, CP_UTF8); // ANSI to UTF-8
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::string utf8_text = result.value();
|
||||||
|
// Use converted UTF-8 text...
|
||||||
|
} else {
|
||||||
|
// Handle conversion error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\subsection encoding__windows__functions__utf8 UTF-8 Specific Conversions
|
||||||
|
|
||||||
|
Specialized functions for UTF-8 conversion without requiring explicit code page specification:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include <yycc/encoding/windows.hpp>
|
||||||
|
|
||||||
|
// Example: Converting wide character to UTF-8
|
||||||
|
std::wstring wide_text = L"Hello, 世界!";
|
||||||
|
auto result = to_utf8(wide_text);
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::u8string utf8_text = result.value();
|
||||||
|
// Use utf8_text...
|
||||||
|
} else {
|
||||||
|
// Handle conversion error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\code
|
||||||
|
// Example: Converting UTF-8 to wide character
|
||||||
|
std::u8string utf8_text = u8"Hello, 世界!";
|
||||||
|
auto result = to_wchar(utf8_text);
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::wstring wide_text = result.value();
|
||||||
|
// Use wide_text...
|
||||||
|
} else {
|
||||||
|
// Handle conversion error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\code
|
||||||
|
// Example: Converting multi-byte to UTF-8
|
||||||
|
std::string multi_byte_text = "Hello, world!";
|
||||||
|
auto result = to_utf8(multi_byte_text, CP_ACP);
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::u8string utf8_text = result.value();
|
||||||
|
// Use utf8_text...
|
||||||
|
} else {
|
||||||
|
// Handle conversion error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\code
|
||||||
|
// Example: Converting UTF-8 to multi-byte
|
||||||
|
std::u8string utf8_text = u8"Hello, world!";
|
||||||
|
auto result = to_char(utf8_text, CP_ACP);
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::string multi_byte_text = result.value();
|
||||||
|
// Use multi_byte_text...
|
||||||
|
} else {
|
||||||
|
// Handle conversion error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\subsection encoding__windows__functions__utf16_utf32 UTF-8 to/from UTF-16/UTF-32 Conversion
|
||||||
|
|
||||||
|
Available on Windows with Microsoft STL for conversion between UTF encodings:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include <yycc/encoding/windows.hpp>
|
||||||
|
|
||||||
|
// Example: Converting UTF-8 to UTF-16
|
||||||
|
std::u8string utf8_text = u8"Hello, 世界!";
|
||||||
|
auto result = to_utf16(utf8_text);
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::u16string utf16_text = result.value();
|
||||||
|
// Use utf16_text...
|
||||||
|
} else {
|
||||||
|
// Handle conversion error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\code
|
||||||
|
// Example: Converting UTF-16 to UTF-8
|
||||||
|
std::u16string utf16_text = u"Hello, 世界!";
|
||||||
|
auto result = to_utf8(utf16_text);
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::u8string utf8_text = result.value();
|
||||||
|
// Use utf8_text...
|
||||||
|
} else {
|
||||||
|
// Handle conversion error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\code
|
||||||
|
// Example: Converting UTF-8 to UTF-32
|
||||||
|
std::u8string utf8_text = u8"Hello, 世界! 🌍";
|
||||||
|
auto result = to_utf32(utf8_text);
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::u32string utf32_text = result.value();
|
||||||
|
// Use utf32_text...
|
||||||
|
} else {
|
||||||
|
// Handle conversion error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\code
|
||||||
|
// Example: Converting UTF-32 to UTF-8
|
||||||
|
std::u32string utf32_text = U"Hello, 世界! 🌍";
|
||||||
|
auto result = to_utf8(utf32_text);
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::u8string utf8_text = result.value();
|
||||||
|
// Use utf8_text...
|
||||||
|
} else {
|
||||||
|
// Handle conversion error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section encoding__windows__error_handling Error Handling
|
||||||
|
|
||||||
|
All functions in this module return a result containing either
|
||||||
|
a ConvError struct represents conversion errors, or the final converted string.
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include <yycc/encoding/windows.hpp>
|
||||||
|
|
||||||
|
std::wstring invalid_text = /* some problematic string */;
|
||||||
|
auto result = to_char(invalid_text, CP_UTF8);
|
||||||
|
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::string converted = result.value();
|
||||||
|
// Process successfully converted string
|
||||||
|
} else {
|
||||||
|
// Handle conversion failure
|
||||||
|
std::cout << "Conversion failed\n";
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
*/
|
||||||
|
}
|
||||||
85
doc/src/env.dox
Normal file
85
doc/src/env.dox
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
namespace yycc::env {
|
||||||
|
/**
|
||||||
|
|
||||||
|
\page env Environment Operations
|
||||||
|
|
||||||
|
This namespace provide various environment operations inspired by Rust standard library.
|
||||||
|
|
||||||
|
\section env__var Environment Variable Operations
|
||||||
|
|
||||||
|
These functions allow manipulation of environment variables with proper error handling using \c std::expected.
|
||||||
|
|
||||||
|
\li get_var(): Get the value of a given environment variable name
|
||||||
|
\li set_var(): Set the value of a given environment variable name
|
||||||
|
\li del_var(): Delete environment variable with given name
|
||||||
|
\li get_vars(): Get all environment variables as a list of name-value pairs
|
||||||
|
|
||||||
|
There is an example usage of these functions below:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include <yycc/env.hpp>
|
||||||
|
|
||||||
|
auto result = yycc::env::get_var(u8"PATH");
|
||||||
|
if (result.has_value()) {
|
||||||
|
auto value = result.value();
|
||||||
|
// Use the value...
|
||||||
|
} else {
|
||||||
|
// Handle the error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section env__path Path Operations
|
||||||
|
|
||||||
|
These functions provide access to various important paths in the system environment.
|
||||||
|
|
||||||
|
\li current_dir(): Returns the current working directory
|
||||||
|
\li current_exe(): Returns the path of the current running executable
|
||||||
|
\li home_dir(): Returns the path of the current user's home directory if known
|
||||||
|
\li temp_dir(): Returns the path of a temporary directory
|
||||||
|
|
||||||
|
There is an example usage of these functions below:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include <yycc/env.hpp>
|
||||||
|
#include <yycc/patch/stream.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace yycc::patch::stream;
|
||||||
|
|
||||||
|
auto cwd_result = yycc::env::current_dir();
|
||||||
|
if (cwd_result.has_value()) {
|
||||||
|
std::cout << "Current directory: " << cwd_result.value() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto exe_result = yycc::env::current_exe();
|
||||||
|
if (exe_result.has_value()) {
|
||||||
|
std::cout << "Executable path: " << exe_result.value() << std::endl;
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section env__arg Command Line Argument Operations
|
||||||
|
|
||||||
|
These functions provide access to command-line arguments passed to the program.
|
||||||
|
|
||||||
|
\li get_args(): Returns the arguments that this program was started with
|
||||||
|
|
||||||
|
There is an example usage of these functions below:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include <yycc/env.hpp>
|
||||||
|
#include <yycc/patch/stream.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace yycc::patch::stream;
|
||||||
|
|
||||||
|
auto args_result = yycc::env::get_args();
|
||||||
|
if (args_result.has_value()) {
|
||||||
|
auto args = args_result.value();
|
||||||
|
for (auto& arg : args) {
|
||||||
|
std::cout << "Arg: " << arg << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
*/
|
||||||
|
}
|
||||||
@@ -43,28 +43,50 @@
|
|||||||
|
|
||||||
\li \subpage num__parser
|
\li \subpage num__parser
|
||||||
|
|
||||||
|
\li \subpage num__op
|
||||||
|
|
||||||
|
\li \subpage num__safe_cast
|
||||||
|
|
||||||
|
\li \subpage num__safe_op
|
||||||
|
|
||||||
\li \subpage patch
|
\li \subpage patch
|
||||||
|
|
||||||
|
\li \subpage env
|
||||||
|
|
||||||
|
\li \subpage rust
|
||||||
|
|
||||||
|
<B>Text Encoding</B>
|
||||||
|
|
||||||
|
\li \subpage encoding__stl
|
||||||
|
|
||||||
|
\li \subpage encoding__windows
|
||||||
|
|
||||||
|
\li \subpage encoding__iconv
|
||||||
|
|
||||||
|
\li \subpage pycodec
|
||||||
|
|
||||||
</TD>
|
</TD>
|
||||||
<TD ALIGN="LEFT" VALIGN="TOP">
|
<TD ALIGN="LEFT" VALIGN="TOP">
|
||||||
|
|
||||||
<B>Advanced Features (Carton)</B>
|
<B>Advanced Features (Carton)</B>
|
||||||
|
|
||||||
<!--
|
\li \subpage termcolor
|
||||||
|
|
||||||
\li \subpage constraints
|
|
||||||
|
|
||||||
\li \subpage encoding_helper
|
\li \subpage csconsole
|
||||||
|
|
||||||
\li \subpage console_helper
|
\li \subpage ironpad
|
||||||
|
|
||||||
\li \subpage config_manager
|
\li \subpage clap
|
||||||
|
|
||||||
\li \subpage arg_parser
|
\li \subpage binstore
|
||||||
|
|
||||||
\li \subpage exception_helper
|
\li \subpage fft
|
||||||
|
|
||||||
-->
|
\li \subpage lexer61
|
||||||
|
|
||||||
|
\li \subpage wcwidth
|
||||||
|
|
||||||
|
\li \subpage tabulate
|
||||||
|
|
||||||
<B>Windows Specific Features</B>
|
<B>Windows Specific Features</B>
|
||||||
|
|
||||||
@@ -76,6 +98,8 @@
|
|||||||
|
|
||||||
\li \subpage windows__winfct
|
\li \subpage windows__winfct
|
||||||
|
|
||||||
|
\li \subpage windows__console
|
||||||
|
|
||||||
</TD>
|
</TD>
|
||||||
</TR>
|
</TR>
|
||||||
</TABLE>
|
</TABLE>
|
||||||
|
|||||||
30
doc/src/num/op.dox
Normal file
30
doc/src/num/op.dox
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
namespace yycc::num::op {
|
||||||
|
/**
|
||||||
|
|
||||||
|
\page num__op Numeric Operations
|
||||||
|
|
||||||
|
Namespace yycc::num::op provides functions for robust numeric operations inspired by Rust's approach to primitive type operations.
|
||||||
|
|
||||||
|
Currently, this namespace only supports unsigned integer ceiling division, though more operations may be added in the future based on demand.
|
||||||
|
|
||||||
|
\section num__op__div_ceil Ceiling Division
|
||||||
|
|
||||||
|
The \c div_ceil function performs division between two unsigned integers and rounds up the result.
|
||||||
|
It uses a safe algorithm that avoids potential overflow issues that could occur with the traditional formula <TT>(lhs + rhs - 1) / rhs</TT>.
|
||||||
|
|
||||||
|
The function computes: <TT>(lhs % rhs == 0) ? (lhs / rhs) : (lhs / rhs) + 1u</TT>
|
||||||
|
The function prevents division by zero by checking the divisor before performing the operation and throwing a std::logic_error if the divisor is zero.
|
||||||
|
|
||||||
|
Here are some examples showing how to use this function:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include <yycc/num/op.hpp>
|
||||||
|
|
||||||
|
// Ceiling division examples
|
||||||
|
uint32_t result1 = op::div_ceil(uint32_t(10), uint32_t(3)); // Results in 4
|
||||||
|
uint32_t result2 = op::div_ceil(uint32_t(9), uint32_t(3)); // Results in 3
|
||||||
|
uint32_t result3 = op::div_ceil(uint32_t(1), uint32_t(10)); // Results in 1
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
*/
|
||||||
|
}
|
||||||
63
doc/src/num/safe_cast.dox
Normal file
63
doc/src/num/safe_cast.dox
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
namespace yycc::num::safe_cast {
|
||||||
|
/**
|
||||||
|
|
||||||
|
\page num__safe_cast Numeric Safe Casting
|
||||||
|
|
||||||
|
Namespace yycc::num::safe_cast provides functions which safely cast numeric value from one type to another.
|
||||||
|
|
||||||
|
\section num__safe_cast__overview Overview
|
||||||
|
|
||||||
|
When writing C++ code, casting between types with different ranges is very important
|
||||||
|
but greatly easy to make mistakes which finally cause fatal errors.
|
||||||
|
Inspired by Rust's approach to type conversion,
|
||||||
|
this namespace provides safe casting functions that handle potential overflow and underflow issues.
|
||||||
|
|
||||||
|
\section num__safe_cast__functions Functions
|
||||||
|
|
||||||
|
The namespace provides two main functions:
|
||||||
|
\li \c to() - Direct conversion for cases where the destination type can definitely hold the source value
|
||||||
|
which means definitely safe conversions (widening conversions).
|
||||||
|
\li \c try_to() - Attempt conversion and return a Result type that includes error information if the conversion fails
|
||||||
|
which means potentially risky conversions (narrowing conversions).
|
||||||
|
|
||||||
|
The \c try_to function returns a \c std::expected.
|
||||||
|
If the conversion succeeds, the result contains the converted value.
|
||||||
|
If it fails, it contains a error info.
|
||||||
|
|
||||||
|
\section num__safe_cast__examples Examples
|
||||||
|
|
||||||
|
Here are some examples showing how to use the safe casting functions:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include <yycc/num/safe_cast.hpp>
|
||||||
|
|
||||||
|
// Safe conversion using 'to' function
|
||||||
|
uint32_t val1 = safe_cast::to<uint32_t>(static_cast<int16_t>(123));
|
||||||
|
|
||||||
|
// Potentially risky conversion using 'try_to' function
|
||||||
|
auto result = safe_cast::try_to<int16_t>(static_cast<int32_t>(12345));
|
||||||
|
if (result.has_value()) {
|
||||||
|
auto converted = result.value();
|
||||||
|
// Use converted value
|
||||||
|
} else {
|
||||||
|
// Handle error
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section num__safe_cast__notes Notes
|
||||||
|
|
||||||
|
The safety of conversions is determined at compile time using the \c CAN_SAFE_TO meta-programming concept.
|
||||||
|
However, for variable-length data types (like \c size_t ), the safety determination may vary across platforms,
|
||||||
|
which could affect code portability. For this reason, it would be better to use \c try_to for these types
|
||||||
|
for better robust application on different platforms.
|
||||||
|
|
||||||
|
\section num__safe_cast__limitations Limitations
|
||||||
|
|
||||||
|
This namespace supports safe casting between integral types only.
|
||||||
|
Currently unsupported conversions include:
|
||||||
|
|
||||||
|
\li Floating-point to floating-point conversions
|
||||||
|
\li Floating-point to integer conversions
|
||||||
|
|
||||||
|
*/
|
||||||
|
}
|
||||||
86
doc/src/num/safe_op.dox
Normal file
86
doc/src/num/safe_op.dox
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
namespace yycc::num::safe_op {
|
||||||
|
/**
|
||||||
|
|
||||||
|
\page num__safe_op Numeric Safe Arithmetic Operations
|
||||||
|
|
||||||
|
Namespace yycc::num::safe_op provides Rust-like safe arithmetic operations
|
||||||
|
for handling overflow, underflow, and other undefined behaviors in C++.
|
||||||
|
|
||||||
|
\section num__safe_op__overview Overview
|
||||||
|
|
||||||
|
Inspired by Rust's rich set of arithmetic operators,
|
||||||
|
this namespace provides safe arithmetic operations that handle potential overflow, underflow, and other undefined behaviors that commonly occur in C++.
|
||||||
|
It offers multiple strategies for handling arithmetic operations including wrapping, checked, overflowing, saturating, and strict operations.
|
||||||
|
|
||||||
|
\section num__safe_op__operation_types Operation Types
|
||||||
|
|
||||||
|
The namespace provides several families of arithmetic operations:
|
||||||
|
|
||||||
|
\li \c wrapping_* operations: Perform arithmetic with wrapping on overflow/underflow (similar to unsigned integer behavior)
|
||||||
|
\li \c checked_* operations: Return std::optional containing the result, or std::nullopt if overflow/underflow occurs
|
||||||
|
\li \c overflowing_* operations: Return a pair with the result and a boolean indicating whether overflow occurred
|
||||||
|
\li \c saturating_* operations: Clamp the result to the min/max value when overflow/underflow occurs
|
||||||
|
\li \c strict_* operations: Throw exceptions when overflow/underflow occurs
|
||||||
|
\li \c ordinary operations (add, sub, mul, div): Alias to wrapping operations for safe default behavior
|
||||||
|
|
||||||
|
\section num__safe_op__arithmetic_functions Arithmetic Functions
|
||||||
|
|
||||||
|
For each operation type, the namespace provides functions for the four basic arithmetic operations:
|
||||||
|
\li \c _add : Addition
|
||||||
|
\li \c _sub : Subtraction
|
||||||
|
\li \c _mul : Multiplication
|
||||||
|
\li \c _div : Division
|
||||||
|
|
||||||
|
For example, for wrapping operations: \c wrapping_add, \c wrapping_sub, \c wrapping_mul, \c wrapping_div.
|
||||||
|
|
||||||
|
\section num__safe_op__examples Examples
|
||||||
|
|
||||||
|
Here are some examples showing how to use the safe arithmetic functions:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include <yycc/num/safe_op.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
// Wrapping addition - wraps around on overflow
|
||||||
|
uint8_t result1 = safe_op::wrapping_add(uint8_t(200), uint8_t(100)); // Results in 44
|
||||||
|
|
||||||
|
// Checked multiplication - returns std::optional
|
||||||
|
auto result2 = safe_op::checked_mul(int32_t(1000000), int32_t(1000000));
|
||||||
|
if (!result2.has_value()) {
|
||||||
|
std::cout << "Multiplication overflowed!" << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cout << "Result: " << result2.value() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overflowing subtraction - returns pair of result and overflow flag
|
||||||
|
auto [result3, overflowed] = safe_op::overflowing_sub(int32_t(-10), int32_t(INT32_MIN));
|
||||||
|
if (overflowed) {
|
||||||
|
std::cout << "Subtraction overflowed!" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Saturating multiplication - clamps to min/max on overflow
|
||||||
|
int32_t result4 = safe_op::saturating_mul(int32_t(1000000), int32_t(1000000)); // Clamps to INT32_MAX
|
||||||
|
|
||||||
|
// Ordinary operations - safe defaults without undefined behavior
|
||||||
|
int32_t result5 = safe_op::add(int32_t(10), int32_t(20)); // 30
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section num__safe_op__undefined_behaviors Handling of Undefined Behaviors
|
||||||
|
|
||||||
|
This namespace handles several undefined behaviors in C++ arithmetic:
|
||||||
|
\li Signed integer overflow and underflow (e.g. INT_MAX + 1)
|
||||||
|
\li Division by zero
|
||||||
|
\li Performing INT_MIN / -1 division (which would result in a value that doesn't fit in the type)
|
||||||
|
|
||||||
|
For division operations, special care is taken to handle these undefined behaviors appropriately depending on the operation type.
|
||||||
|
|
||||||
|
\section num__safe_op__platform_support Platform Support
|
||||||
|
|
||||||
|
The implementation uses hardware-specific overflow detection functions:
|
||||||
|
\li GCC/Clang: Uses built-in functions like __builtin_add_overflow
|
||||||
|
\li Windows: Uses Windows API functions from \c intsafe.h
|
||||||
|
|
||||||
|
This ensures optimal performance across different platforms.
|
||||||
|
|
||||||
|
*/
|
||||||
|
}
|
||||||
133
doc/src/rust.dox
Normal file
133
doc/src/rust.dox
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
namespace yycc {
|
||||||
|
/**
|
||||||
|
|
||||||
|
\page rust Rust Facilities in C++
|
||||||
|
|
||||||
|
This collection of following headers brings Rust-style programming facilities to C++.
|
||||||
|
|
||||||
|
\section rust__primitive Primitive Types
|
||||||
|
|
||||||
|
The yycc::primitive namespace provides primitive types similar to Rust's approach.
|
||||||
|
Especially resolve the problem that the names of C++ primitive types are so long.
|
||||||
|
|
||||||
|
There is an example of using primitive types:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include <yycc/primitive.hpp>
|
||||||
|
using namespace yycc::primitive;
|
||||||
|
|
||||||
|
i32 value = 42;
|
||||||
|
u64 big_number = 1000000ULL;
|
||||||
|
f64 precision = 3.14159265359;
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section rust__option Option Type
|
||||||
|
|
||||||
|
The yycc::option namespace reproduces Rust's Option type and its members Some and None in C++.
|
||||||
|
Considering C++ has provide \c std::optional, this namespace provided contents are just an alias to it.
|
||||||
|
|
||||||
|
\li yycc::option::Option - Template alias for std::optional
|
||||||
|
\li yycc::option::Some - Function to create an Option with a value
|
||||||
|
\li yycc::option::None - Function to create an empty Option
|
||||||
|
|
||||||
|
There is an example of using \c Option type:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include <yycc/option.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace yycc::option;
|
||||||
|
|
||||||
|
Option<int> maybe_value = Some<Option<int>>(42);
|
||||||
|
if (maybe_value.has_value()) {
|
||||||
|
std::cout << "Value: " << maybe_value.value() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto empty_value = None<Option<int>>();
|
||||||
|
if (!empty_value.has_value()) {
|
||||||
|
std::cout << "No value present" << std::endl;
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section rust__result Result Type
|
||||||
|
|
||||||
|
The yycc::result namespace reproduces Rust's Result type and its members Ok and Err in C++.
|
||||||
|
Considering C++ has provide \c std::expected, this namespace provided contents are just an alias to it.
|
||||||
|
|
||||||
|
\li yycc::result::Result - Template alias for std::expected
|
||||||
|
\li yycc::result::Ok - Function to create a Result with a success value
|
||||||
|
\li yycc::result::Err - Function to create a Result with an error value
|
||||||
|
|
||||||
|
There is an example of using \c Result type:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include <yycc/result.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace yycc::result;
|
||||||
|
|
||||||
|
Result<int, int> divide(int a, int b) {
|
||||||
|
if (b == 0) {
|
||||||
|
return Err<Result<int, int>>(-1); // Error code
|
||||||
|
}
|
||||||
|
return Ok<Result<int, int>>(a / b);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = divide(10, 2);
|
||||||
|
if (result.has_value()) {
|
||||||
|
std::cout << "Result: " << result.value() << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cout << "Error occurred: " << result.error() << std::endl;
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section rust__panic Panic Mechanism
|
||||||
|
|
||||||
|
The yycc::panic namespace provides Rust-style panic functionality for immediate program termination on unrecoverable errors.
|
||||||
|
This imitates Rust's panic! macro behavior, allowing the program to immediately exit with error information and stack traces.
|
||||||
|
|
||||||
|
\li RS_PANIC: Macro equivalent to Rust's panic! macro.
|
||||||
|
This macro will help you append all filename, line and function info to the real panic trigger function.
|
||||||
|
\li yycc::panic::panic: The actual function called by the macro.
|
||||||
|
User usually does not need to call this function directly.
|
||||||
|
|
||||||
|
There is an example of using this panic mechanism:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include <yycc/panic.hpp>
|
||||||
|
|
||||||
|
void critical_function(int err_code) {
|
||||||
|
// Some condition that indicates an unrecoverable error
|
||||||
|
if (err_code != 100) {
|
||||||
|
RS_PANIC("Unrecoverable error in critical function with code {}", err_code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\section rust__prelude Prelude
|
||||||
|
|
||||||
|
The yycc::prelude namespace provides a Rust-like prelude for C++. In Rust, types are automatically imported into all files by default.
|
||||||
|
This default-imported set of types is called the "prelude".
|
||||||
|
This namespace provides a similar concept for C++.
|
||||||
|
|
||||||
|
This namespace will extract following content into \b global scope:
|
||||||
|
|
||||||
|
\li All primitive types defined in yycc::primitive.
|
||||||
|
\li Vec: \c std::vector template alias.
|
||||||
|
\li All functionality from yycc::option and yycc::result namespaces.
|
||||||
|
\li Panic mechanism from yycc::panic.
|
||||||
|
|
||||||
|
There is an example of using this header:
|
||||||
|
|
||||||
|
\code
|
||||||
|
#include <yycc/prelude.hpp>
|
||||||
|
|
||||||
|
// Now all Rust-style facilities are available without prefixes:
|
||||||
|
i32 x = 42; // From primitive
|
||||||
|
Vec<i32> numbers = {1, 2, 3}; // Vector of primitive types
|
||||||
|
auto result = Ok<Result<i32, i32>>(x); // Result type
|
||||||
|
RS_PANIC("Something went wrong"); // Panic macro
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
*/
|
||||||
|
}
|
||||||
@@ -37,7 +37,7 @@ yycc::string::op provide 2 functions for programmer do string replacement:
|
|||||||
|
|
||||||
\code
|
\code
|
||||||
void replace(std::u8string& strl, const std::u8string_view& from_strl, const std::u8string_view& to_strl);
|
void replace(std::u8string& strl, const std::u8string_view& from_strl, const std::u8string_view& to_strl);
|
||||||
std::u8string replace(const std::u8string_view& strl, const std::u8string_view& from_strl, const std::u8string_view& to_strl);
|
std::u8string to_replace(const std::u8string_view& strl, const std::u8string_view& from_strl, const std::u8string_view& to_strl);
|
||||||
\endcode
|
\endcode
|
||||||
|
|
||||||
The first overload will do replacement in given string container directly.
|
The first overload will do replacement in given string container directly.
|
||||||
@@ -162,7 +162,7 @@ std::vector<std::u8string> split_owned(const std::u8string_view& strl, const std
|
|||||||
All these overloads take a string view as the first argument representing the string need to be split.
|
All these overloads take a string view as the first argument representing the string need to be split.
|
||||||
The second argument is a string view representing the delimiter for splitting.
|
The second argument is a string view representing the delimiter for splitting.
|
||||||
|
|
||||||
The first function #lazy_split returns a #LazySplit object that can be used in range-based for loops.
|
The first function #lazy_split returns a LazySplit object that can be used in range-based for loops.
|
||||||
This is lazy-computed and memory-efficient for large datasets.
|
This is lazy-computed and memory-efficient for large datasets.
|
||||||
The second function #split returns a vector of string views, which is memory-efficient
|
The second function #split returns a vector of string views, which is memory-efficient
|
||||||
but the views are only valid as long as the original string remains valid.
|
but the views are only valid as long as the original string remains valid.
|
||||||
|
|||||||
23
doc/src/windows/console.dox
Normal file
23
doc/src/windows/console.dox
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
namespace yycc::windows::console {
|
||||||
|
/**
|
||||||
|
|
||||||
|
\page windows__console Windows Console Helper
|
||||||
|
|
||||||
|
Namespace yycc::windows::console is designed to resolve some issue of Windows console
|
||||||
|
which is not corresponding to POSIX system console.
|
||||||
|
This namespace also is only available on Windows platform.
|
||||||
|
|
||||||
|
Currently this namespace only has one function: colorful_console(),
|
||||||
|
which enable colorful console output support for \c stdout and \c stderr in Windows.
|
||||||
|
As we introduced, you may know Windows console does not support ASCII Escape Code color in default.
|
||||||
|
This function can fix this issue.
|
||||||
|
This function will forcely enable ASCII Escape Code support in Windows console if possible.
|
||||||
|
Thus you can write colorful text in Windows console freely.
|
||||||
|
We suggest you to call this function at the beginning of program.
|
||||||
|
|
||||||
|
Considering most Linux console supports ASCII Escape Code very well,
|
||||||
|
this function isn't presented in non-Windows platform.
|
||||||
|
So it is essential that brack this function calling with Windows-only \c \#if.
|
||||||
|
|
||||||
|
*/
|
||||||
|
}
|
||||||
@@ -133,10 +133,10 @@ auto result4 = open_folder(params);
|
|||||||
|
|
||||||
There are 4 file dialogs you can choose:
|
There are 4 file dialogs you can choose:
|
||||||
|
|
||||||
\li #open_file: Open single file
|
\li open_file(): Open single file
|
||||||
\li #open_files: Open multiple files
|
\li open_files(): Open multiple files
|
||||||
\li #save_file: Save single file
|
\li save_file(): Save single file
|
||||||
\li #open_folder: Open single directory
|
\li open_folder(): Open single directory
|
||||||
|
|
||||||
\subsection windows__dialog__result__arguments Arguments
|
\subsection windows__dialog__result__arguments Arguments
|
||||||
|
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ FILES
|
|||||||
yycc/patch/format.hpp
|
yycc/patch/format.hpp
|
||||||
yycc/patch/libcxx/enumerate.hpp
|
yycc/patch/libcxx/enumerate.hpp
|
||||||
yycc/patch/libcxx/stacktrace.hpp
|
yycc/patch/libcxx/stacktrace.hpp
|
||||||
|
yycc/patch/libcxx/charconv.hpp
|
||||||
yycc/num/parse.hpp
|
yycc/num/parse.hpp
|
||||||
yycc/num/stringify.hpp
|
yycc/num/stringify.hpp
|
||||||
yycc/num/safe_cast.hpp
|
yycc/num/safe_cast.hpp
|
||||||
@@ -166,6 +167,15 @@ PUBLIC
|
|||||||
# Fix Windows header file shit
|
# Fix Windows header file shit
|
||||||
$<$<BOOL:${WIN32}>:WIN32_LEAN_AND_MEAN>
|
$<$<BOOL:${WIN32}>:WIN32_LEAN_AND_MEAN>
|
||||||
$<$<BOOL:${WIN32}>:NOMINMAX>
|
$<$<BOOL:${WIN32}>:NOMINMAX>
|
||||||
|
PUBLIC
|
||||||
|
$<$<BOOL:${YYCC_CHARCONV_HAS_CHARS_FORMAT}>:YYCC_CHARCONV_HAS_CHARS_FORMAT>
|
||||||
|
$<$<BOOL:${YYCC_CHARCONV_HAS_FROM_CHARS_RESULT}>:YYCC_CHARCONV_HAS_FROM_CHARS_RESULT>
|
||||||
|
$<$<BOOL:${YYCC_CHARCONV_HAS_TO_CHARS_RESULT}>:YYCC_CHARCONV_HAS_TO_CHARS_RESULT>
|
||||||
|
$<$<BOOL:${YYCC_CHARCONV_HAS_FROM_CHARS_INT}>:YYCC_CHARCONV_HAS_FROM_CHARS_INT>
|
||||||
|
$<$<BOOL:${YYCC_CHARCONV_HAS_FROM_CHARS_FLOAT}>:YYCC_CHARCONV_HAS_FROM_CHARS_FLOAT>
|
||||||
|
$<$<BOOL:${YYCC_CHARCONV_HAS_TO_CHARS_INT}>:YYCC_CHARCONV_HAS_TO_CHARS_INT>
|
||||||
|
$<$<BOOL:${YYCC_CHARCONV_HAS_TO_CHARS_FLOAT}>:YYCC_CHARCONV_HAS_TO_CHARS_FLOAT>
|
||||||
|
|
||||||
)
|
)
|
||||||
target_compile_options(YYCCommonplace
|
target_compile_options(YYCCommonplace
|
||||||
PUBLIC
|
PUBLIC
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "../../patch/libcxx/enumerate.hpp"
|
#include "../../patch/libcxx/enumerate.hpp"
|
||||||
#include "../../string/op.hpp"
|
#include "../../string/op.hpp"
|
||||||
#include "../../env.hpp"
|
#include "../../env.hpp"
|
||||||
|
#include <filesystem>
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
|
|
||||||
#define CLAP ::yycc::carton::clap
|
#define CLAP ::yycc::carton::clap
|
||||||
@@ -112,8 +113,9 @@ namespace yycc::carton::clap::manual {
|
|||||||
// only print usage if we can fetch the name of executable
|
// only print usage if we can fetch the name of executable
|
||||||
auto executable = ENV::current_exe();
|
auto executable = ENV::current_exe();
|
||||||
if (executable.has_value()) {
|
if (executable.has_value()) {
|
||||||
|
// Get the filename part and print
|
||||||
TERMCOLOR::cprintln(trctx.usage_title, TERMCOLOR::Color::Yellow, TERMCOLOR::Color::Default, TERMCOLOR::Attribute::Default, dst);
|
TERMCOLOR::cprintln(trctx.usage_title, TERMCOLOR::Color::Yellow, TERMCOLOR::Color::Default, TERMCOLOR::Attribute::Default, dst);
|
||||||
dst << INDENT << FORMAT::format(trctx.usage_body, executable.value().u8string()) << std::endl;
|
dst << INDENT << FORMAT::format(trctx.usage_body, executable.value().filename().u8string()) << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto &variables = app.get_variables();
|
const auto &variables = app.get_variables();
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace yycc::carton::clap::option {
|
|||||||
Option::Option(std::optional<std::u8string_view> short_name,
|
Option::Option(std::optional<std::u8string_view> short_name,
|
||||||
std::optional<std::u8string_view> long_name,
|
std::optional<std::u8string_view> long_name,
|
||||||
std::optional<std::u8string_view> value_hint,
|
std::optional<std::u8string_view> value_hint,
|
||||||
const std::u8string& description) :
|
const std::u8string_view& description) :
|
||||||
short_name(short_name), long_name(long_name), value_hint(value_hint), description(description) {
|
short_name(short_name), long_name(long_name), value_hint(value_hint), description(description) {
|
||||||
if (!short_name.has_value() && !long_name.has_value()) {
|
if (!short_name.has_value() && !long_name.has_value()) {
|
||||||
throw std::logic_error("must have at least one name, short or long name");
|
throw std::logic_error("must have at least one name, short or long name");
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ namespace yycc::carton::clap::option {
|
|||||||
Option(std::optional<std::u8string_view> short_name,
|
Option(std::optional<std::u8string_view> short_name,
|
||||||
std::optional<std::u8string_view> long_name,
|
std::optional<std::u8string_view> long_name,
|
||||||
std::optional<std::u8string_view> value_hint,
|
std::optional<std::u8string_view> value_hint,
|
||||||
const std::u8string& description);
|
const std::u8string_view& description);
|
||||||
~Option();
|
~Option();
|
||||||
YYCC_DEFAULT_COPY_MOVE(Option)
|
YYCC_DEFAULT_COPY_MOVE(Option)
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
* \li https://stackoverflow.com/questions/45575863/how-to-print-utf-8-strings-to-stdcout-on-windows
|
* \li https://stackoverflow.com/questions/45575863/how-to-print-utf-8-strings-to-stdcout-on-windows
|
||||||
* \li https://stackoverflow.com/questions/69830460/reading-utf-8-input
|
* \li https://stackoverflow.com/questions/69830460/reading-utf-8-input
|
||||||
*
|
*
|
||||||
* For how to utilize this functions provided by this namespace, please view \ref console_helper.
|
* For how to utilize this functions provided by this namespace, please view \ref csconsole.
|
||||||
*
|
*
|
||||||
* @warning
|
* @warning
|
||||||
* All functions provided by this namespace are too aggressive.
|
* All functions provided by this namespace are too aggressive.
|
||||||
|
|||||||
@@ -248,7 +248,7 @@ namespace yycc::carton::fft {
|
|||||||
* @param[out] freq_scope The length of this data must be N / 2.
|
* @param[out] freq_scope The length of this data must be N / 2.
|
||||||
* The first data is 0Hz and the frequency of last data is decided by sample rate which can be computed by get_max_freq() function in this class.
|
* The first data is 0Hz and the frequency of last data is decided by sample rate which can be computed by get_max_freq() function in this class.
|
||||||
* @param[in] window The window instance applied to data.
|
* @param[in] window The window instance applied to data.
|
||||||
* @warnings
|
* @warning
|
||||||
* This function is \b NOT thread-safe.
|
* This function is \b NOT thread-safe.
|
||||||
* Please do NOT call this function in different thread for one instance.
|
* Please do NOT call this function in different thread for one instance.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
* @details
|
* @details
|
||||||
* This namespace is currently works on Windows.
|
* This namespace is currently works on Windows.
|
||||||
* On other platforms, this namespace provided functions do nothing.
|
* On other platforms, this namespace provided functions do nothing.
|
||||||
* For how to utilize this namespace, please see \ref exception_helper.
|
* For how to utilize this namespace, please see \ref ironpad.
|
||||||
*
|
*
|
||||||
* This feature is originate from my created Virtools plugin.
|
* This feature is originate from my created Virtools plugin.
|
||||||
* Because its user frequently trigger some weird behaviors but I have no idea about the detail of them.
|
* Because its user frequently trigger some weird behaviors but I have no idea about the detail of them.
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#if defined(YYCC_FEAT_ICONV)
|
#if defined(YYCC_FEAT_ICONV)
|
||||||
|
|
||||||
#include "../macro/endian_detector.hpp"
|
#include "../macro/endian_detector.hpp"
|
||||||
|
#include "../macro/os_detector.hpp"
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@@ -202,11 +203,14 @@ namespace yycc::encoding::iconv {
|
|||||||
// If we use UTF16 or UTF32 code name directly, it will produce a BOM at data head.
|
// If we use UTF16 or UTF32 code name directly, it will produce a BOM at data head.
|
||||||
// That's not what we expected.
|
// That's not what we expected.
|
||||||
// So we need manually check runtime endian and explicitly specify endian in code name.
|
// So we need manually check runtime endian and explicitly specify endian in code name.
|
||||||
|
//
|
||||||
|
// Also, at the same time, iconv on macOS do not support "WCHAR_T" encoding,
|
||||||
|
// so we need manually set it as UTF32 encoding.
|
||||||
|
// See https://developer.apple.com/forums/thread/811508 for more infos.
|
||||||
|
|
||||||
using namespace std::literals::string_view_literals;
|
using namespace std::literals::string_view_literals;
|
||||||
|
|
||||||
constexpr auto UTF8_CODENAME_LITERAL = "UTF-8"sv;
|
constexpr auto UTF8_CODENAME_LITERAL = "UTF-8"sv;
|
||||||
constexpr auto WCHAR_CODENAME_LITERAL = "WCHAR_T"sv;
|
|
||||||
constexpr auto UTF16_CODENAME_LITERAL =
|
constexpr auto UTF16_CODENAME_LITERAL =
|
||||||
#if defined(YYCC_ENDIAN_LITTLE)
|
#if defined(YYCC_ENDIAN_LITTLE)
|
||||||
"UTF-16LE"sv;
|
"UTF-16LE"sv;
|
||||||
@@ -219,6 +223,13 @@ namespace yycc::encoding::iconv {
|
|||||||
#else
|
#else
|
||||||
"UTF-32BE"sv;
|
"UTF-32BE"sv;
|
||||||
#endif
|
#endif
|
||||||
|
constexpr auto WCHAR_CODENAME_LITERAL =
|
||||||
|
#if defined(YYCC_OS_MACOS)
|
||||||
|
UTF32_CODENAME_LITERAL;
|
||||||
|
static_assert(sizeof(wchar_t) == sizeof(char32_t), "unexpected wchar_t size");
|
||||||
|
#else
|
||||||
|
"WCHAR_T"sv;
|
||||||
|
#endif
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// There is a memory copy in this function. Consider optimizing it in future.
|
// There is a memory copy in this function. Consider optimizing it in future.
|
||||||
|
|||||||
136
src/yycc/env.cpp
136
src/yycc/env.cpp
@@ -1,39 +1,42 @@
|
|||||||
#include "env.hpp"
|
#include "env.hpp"
|
||||||
#include "macro/os_detector.hpp"
|
#include "macro/os_detector.hpp"
|
||||||
#include "string/reinterpret.hpp"
|
|
||||||
#include "num/safe_op.hpp"
|
#include "num/safe_op.hpp"
|
||||||
#include "num/safe_cast.hpp"
|
#include "num/safe_cast.hpp"
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <memory>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
#if defined(YYCC_OS_WINDOWS)
|
#if defined(YYCC_OS_WINDOWS)
|
||||||
|
// Windows headers
|
||||||
#include "encoding/windows.hpp"
|
#include "encoding/windows.hpp"
|
||||||
#include "windows/winfct.hpp"
|
#include "windows/winfct.hpp"
|
||||||
|
#include <memory> // For safely free gotten environment variable and commandline argument.
|
||||||
|
#include <type_traits> // Used by std::unique_ptr to get underlying pointer type.
|
||||||
#include "windows/import_guard_head.hpp"
|
#include "windows/import_guard_head.hpp"
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#include <winbase.h>
|
#include <winbase.h>
|
||||||
#include <processenv.h> // For getting environment variables and commandline argument.
|
#include <processenv.h> // For getting environment variables and commandline argument.
|
||||||
#include <shellapi.h> // For getting commandline argument.
|
#include <shellapi.h> // For getting commandline argument.
|
||||||
#include "windows/import_guard_tail.hpp"
|
#include "windows/import_guard_tail.hpp"
|
||||||
#elif defined(YYCC_OS_LINUX)
|
#else
|
||||||
#include <cstdlib>
|
// POSIX headers
|
||||||
#include <cerrno>
|
#include "string/reinterpret.hpp"
|
||||||
#include <fstream> // For reading commandline argument.
|
#include <cstdlib> // For POSIX environment variable operations.
|
||||||
#include <unistd.h>
|
#include <cerrno> // For POSIX errno.
|
||||||
#include <sys/stat.h> // For reading symlink target.
|
#include <stdexcept> // For re-throw unexpected POSIX errno as STL exception.
|
||||||
|
#if defined(YYCC_OS_LINUX)
|
||||||
|
// Linux-only headers
|
||||||
|
#include <fstream> // For reading commandline argument.
|
||||||
#elif defined(YYCC_OS_MACOS)
|
#elif defined(YYCC_OS_MACOS)
|
||||||
#include <cstdlib>
|
// macOS-only headers
|
||||||
#include <cerrno>
|
#include <mach-o/dyld.h> // For getting current exe path.
|
||||||
#include <unistd.h>
|
#include <crt_externs.h> // For getting commandline argument.
|
||||||
#include <cstring>
|
|
||||||
#include <mach-o/dyld.h> // For getting current exe path.
|
|
||||||
#include <crt_externs.h> // For getting commandline argument.
|
|
||||||
#else
|
#else
|
||||||
#error "Not supported OS"
|
#error "Not supported OS"
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Extern POSIX environment variables list.
|
||||||
|
#if !defined(YYCC_OS_WINDOWS)
|
||||||
|
extern char** environ;
|
||||||
|
#endif
|
||||||
|
|
||||||
#define SAFECAST ::yycc::num::safe_cast
|
#define SAFECAST ::yycc::num::safe_cast
|
||||||
#define SAFEOP ::yycc::num::safe_op
|
#define SAFEOP ::yycc::num::safe_op
|
||||||
@@ -159,6 +162,7 @@ namespace yycc::env {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(YYCC_OS_WINDOWS)
|
#if defined(YYCC_OS_WINDOWS)
|
||||||
|
|
||||||
class EnvironmentStringsDeleter {
|
class EnvironmentStringsDeleter {
|
||||||
public:
|
public:
|
||||||
EnvironmentStringsDeleter() {}
|
EnvironmentStringsDeleter() {}
|
||||||
@@ -169,6 +173,7 @@ namespace yycc::env {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
using SmartEnvironmentStrings = std::unique_ptr<std::remove_pointer_t<LPWCH>, EnvironmentStringsDeleter>;
|
using SmartEnvironmentStrings = std::unique_ptr<std::remove_pointer_t<LPWCH>, EnvironmentStringsDeleter>;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
VarResult<std::vector<VarPair>> get_vars() {
|
VarResult<std::vector<VarPair>> get_vars() {
|
||||||
@@ -241,84 +246,6 @@ namespace yycc::env {
|
|||||||
return std::filesystem::current_path();
|
return std::filesystem::current_path();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(YYCC_OS_LINUX)
|
|
||||||
|
|
||||||
/// @brief All possible error occurs when reading Linux symlink target.
|
|
||||||
enum class ReadSymlinkTargetError {
|
|
||||||
SysCall, ///< Fail to call system call.
|
|
||||||
Truncated, ///< Expected path to target was truncated.
|
|
||||||
Others, ///< Any other errors
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief An utility function for convenient reading symlink target on Linux.
|
|
||||||
* @param[in] link The path to symlink where the target is read.
|
|
||||||
* @return The read path to to target, or error occurs.
|
|
||||||
*/
|
|
||||||
static std::expected<std::u8string, ReadSymlinkTargetError> read_symlink_target(const std::string_view &link) {
|
|
||||||
// Reference: https://www.man7.org/linux/man-pages/man2/readlink.2.html
|
|
||||||
|
|
||||||
// String view is not NUL terminated.
|
|
||||||
// Create an string container for it.
|
|
||||||
std::string path(link);
|
|
||||||
|
|
||||||
// Get the expected size.
|
|
||||||
// Query this symlink info first.
|
|
||||||
struct stat sb;
|
|
||||||
if (lstat(path.c_str(), &sb) != 0) {
|
|
||||||
return std::unexpected(ReadSymlinkTargetError::SysCall);
|
|
||||||
}
|
|
||||||
// Fetch the size of target path in gotten struct.
|
|
||||||
// And cast it into expected type.
|
|
||||||
auto expected_size = SAFECAST::try_to<size_t>(sb.st_size);
|
|
||||||
if (!expected_size.has_value()) {
|
|
||||||
return std::unexpected(ReadSymlinkTargetError::Others);
|
|
||||||
}
|
|
||||||
auto buf_size = expected_size.value();
|
|
||||||
// Some magic symlinks under (for example) /proc and /sys report 'st_size' as zero.
|
|
||||||
// In that case, take PATH_MAX as a "good enough" estimate.
|
|
||||||
if (buf_size == 0) {
|
|
||||||
buf_size = PATH_MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare return value and allocate it with previous gotten size.
|
|
||||||
std::u8string rv(u8'\0', buf_size);
|
|
||||||
|
|
||||||
// Copy data into result value.
|
|
||||||
// Add one to the link size, so that we can determine whether
|
|
||||||
// the buffer returned by readlink() was truncated.
|
|
||||||
// Also, due to the add operation, we need do overflow checks.
|
|
||||||
auto passed_size = SAFEOP::checked_add<size_t>(buf_size, 1);
|
|
||||||
if (!passed_size.has_value()) {
|
|
||||||
return std::unexpected(ReadSymlinkTargetError::Others);
|
|
||||||
}
|
|
||||||
// Read data into result value.
|
|
||||||
ssize_t nbytes = readlink(path.c_str(), REINTERPRET::as_ordinary(rv.data()), passed_size.value());
|
|
||||||
if (nbytes < 0) {
|
|
||||||
return std::unexpected(ReadSymlinkTargetError::SysCall);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check written size
|
|
||||||
// Cast it type into expected type.
|
|
||||||
auto written_size = SAFECAST::try_to<size_t>(nbytes);
|
|
||||||
if (!written_size.has_value()) {
|
|
||||||
return std::unexpected(ReadSymlinkTargetError::Others);
|
|
||||||
}
|
|
||||||
// If the return value was equal to the buffer size, then
|
|
||||||
// the link target was larger than expected (perhaps because the
|
|
||||||
// target was changed between the call to lstat() and the call to
|
|
||||||
// readlink()). Return error instead.
|
|
||||||
if (written_size.value() != buf_size) {
|
|
||||||
// TODO: There must be a better solution to this truncated issue than simply return error.
|
|
||||||
return std::unexpected(ReadSymlinkTargetError::Truncated);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Everything is okey
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
PathResult<std::filesystem::path> current_exe() {
|
PathResult<std::filesystem::path> current_exe() {
|
||||||
std::u8string rv;
|
std::u8string rv;
|
||||||
|
|
||||||
@@ -328,9 +255,10 @@ namespace yycc::env {
|
|||||||
else return std::unexpected(PathError::SysCall);
|
else return std::unexpected(PathError::SysCall);
|
||||||
#elif defined(YYCC_OS_LINUX)
|
#elif defined(YYCC_OS_LINUX)
|
||||||
// Reference: https://www.man7.org/linux/man-pages/man5/proc_pid_exe.5.html
|
// Reference: https://www.man7.org/linux/man-pages/man5/proc_pid_exe.5.html
|
||||||
auto target = read_symlink_target("/proc/self/exe");
|
std::error_code ec;
|
||||||
if (target.has_value()) return rv = std::move(target.value());
|
auto target = std::filesystem::read_symlink(std::filesystem::path("/proc/self/exe"), ec);
|
||||||
else return std::unexpected(PathError::SysCall);
|
if (ec) return std::unexpected(PathError::SysCall);
|
||||||
|
else rv = REINTERPRET::as_utf8(target.string());
|
||||||
#elif defined(YYCC_OS_MACOS)
|
#elif defined(YYCC_OS_MACOS)
|
||||||
// TODO: This is AI generated and don't have test and reference.
|
// TODO: This is AI generated and don't have test and reference.
|
||||||
std::string buffer(PATH_MAX, '\0');
|
std::string buffer(PATH_MAX, '\0');
|
||||||
@@ -437,10 +365,10 @@ namespace yycc::env {
|
|||||||
while (true) {
|
while (true) {
|
||||||
// We use NUL as delimiter
|
// We use NUL as delimiter
|
||||||
std::getline(cmdline, arg, '\0');
|
std::getline(cmdline, arg, '\0');
|
||||||
|
// Check whether we reach EOF
|
||||||
|
if (cmdline.eof()) break;
|
||||||
// Check whether reading is okey.
|
// Check whether reading is okey.
|
||||||
if (!cmdline.good()) return std::unexpected(ArgError::Others);
|
if (cmdline.fail()) return std::unexpected(ArgError::Others);
|
||||||
// If return string is empty, it means that we reach the tail.
|
|
||||||
if (arg.empty()) break;
|
|
||||||
|
|
||||||
// Push this argument into result.
|
// Push this argument into result.
|
||||||
rv.emplace_back(REINTERPRET::as_utf8(arg));
|
rv.emplace_back(REINTERPRET::as_utf8(arg));
|
||||||
|
|||||||
@@ -3,9 +3,13 @@
|
|||||||
#include "../string/reinterpret.hpp"
|
#include "../string/reinterpret.hpp"
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <charconv>
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <expected>
|
#include <expected>
|
||||||
|
#include <charconv>
|
||||||
|
|
||||||
|
#if defined(YYCC_STL_CLANGSTL)
|
||||||
|
#include "../patch/libcxx/charconv.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
#define NS_YYCC_STRING_REINTERPRET ::yycc::string::reinterpret
|
#define NS_YYCC_STRING_REINTERPRET ::yycc::string::reinterpret
|
||||||
#define NS_YYCC_STRING_OP ::yycc::string::op
|
#define NS_YYCC_STRING_OP ::yycc::string::op
|
||||||
|
|||||||
@@ -3,8 +3,12 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <charconv>
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <charconv>
|
||||||
|
|
||||||
|
#if defined(YYCC_STL_CLANGSTL)
|
||||||
|
#include "../patch/libcxx/charconv.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
#define NS_YYCC_STRING_REINTERPRET ::yycc::string::reinterpret
|
#define NS_YYCC_STRING_REINTERPRET ::yycc::string::reinterpret
|
||||||
|
|
||||||
|
|||||||
446
src/yycc/patch/libcxx/charconv.hpp
Normal file
446
src/yycc/patch/libcxx/charconv.hpp
Normal file
@@ -0,0 +1,446 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "../../macro/stl_detector.hpp"
|
||||||
|
|
||||||
|
//#if defined(YYCC_STL_CLANGSTL)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @file This is the polyfill for LLVM libcxx charconv header
|
||||||
|
* which including \c std::from_chars and \c std::to_chars utilities.
|
||||||
|
* These 2 functions are only \b fully implemented in the latest Clang (20+)
|
||||||
|
* and partially implemented in the latest Apple Clang (Xcode 16).
|
||||||
|
* This should be removed once both Clang official and Apple Clang libcxx \b fully provide them.
|
||||||
|
* This polyfill is generated by AI.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <charconv>
|
||||||
|
#include <system_error>
|
||||||
|
#include <cctype>
|
||||||
|
#include <climits>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cerrno>
|
||||||
|
#include <cinttypes>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <concepts>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
|
||||||
|
#if !defined(YYCC_CHARCONV_HAS_CHARS_FORMAT)
|
||||||
|
|
||||||
|
enum class chars_format : unsigned int {
|
||||||
|
scientific = 1,
|
||||||
|
fixed = 2,
|
||||||
|
hex = 4,
|
||||||
|
general = fixed | scientific // This should be 6 (fixed|scientific)
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(YYCC_CHARCONV_HAS_FROM_CHARS_RESULT)
|
||||||
|
|
||||||
|
struct from_chars_result {
|
||||||
|
const char* ptr;
|
||||||
|
std::errc ec;
|
||||||
|
|
||||||
|
friend bool operator==(const from_chars_result&, const from_chars_result&) = default;
|
||||||
|
constexpr explicit operator bool() const noexcept { return ec == std::errc{}; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(YYCC_CHARCONV_HAS_TO_CHARS_RESULT)
|
||||||
|
|
||||||
|
struct to_chars_result {
|
||||||
|
char* ptr;
|
||||||
|
std::errc ec;
|
||||||
|
|
||||||
|
friend bool operator==(const to_chars_result&, const to_chars_result&) = default;
|
||||||
|
constexpr explicit operator bool() const noexcept { return ec == std::errc{}; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(YYCC_CHARCONV_HAS_TO_CHARS_INT)
|
||||||
|
|
||||||
|
/// @private
|
||||||
|
enum class __integral_type {
|
||||||
|
u8,
|
||||||
|
u16,
|
||||||
|
u32,
|
||||||
|
u64,
|
||||||
|
i8,
|
||||||
|
i16,
|
||||||
|
i32,
|
||||||
|
i64,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @private
|
||||||
|
enum class __integral_base_type {
|
||||||
|
base8,
|
||||||
|
base10,
|
||||||
|
base16,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @private
|
||||||
|
template<typename T>
|
||||||
|
constexpr __integral_type __classify_int_type() {
|
||||||
|
if constexpr (std::is_same_v<T, std::uint8_t>) {
|
||||||
|
return __integral_type::u8;
|
||||||
|
} else if constexpr (std::is_same_v<T, std::uint16_t>) {
|
||||||
|
return __integral_type::u16;
|
||||||
|
} else if constexpr (std::is_same_v<T, std::uint32_t>) {
|
||||||
|
return __integral_type::u32;
|
||||||
|
} else if constexpr (std::is_same_v<T, std::uint64_t>) {
|
||||||
|
return __integral_type::u64;
|
||||||
|
} else if constexpr (std::is_same_v<T, std::int8_t>) {
|
||||||
|
return __integral_type::i8;
|
||||||
|
} else if constexpr (std::is_same_v<T, std::int16_t>) {
|
||||||
|
return __integral_type::i16;
|
||||||
|
} else if constexpr (std::is_same_v<T, std::int32_t>) {
|
||||||
|
return __integral_type::i32;
|
||||||
|
} else if constexpr (std::is_same_v<T, std::int64_t>) {
|
||||||
|
return __integral_type::i64;
|
||||||
|
} else {
|
||||||
|
static_cast(false, "Unsupported integral type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @private
|
||||||
|
std::optional<__integral_base_type> __classify_int_base(int base) {
|
||||||
|
if (base == 8) {
|
||||||
|
return __integral_base_type::base8;
|
||||||
|
} else if (base == 10) {
|
||||||
|
return __integral_base_type::base10;
|
||||||
|
} else if (base == 16) {
|
||||||
|
return __integral_base_type::base16;
|
||||||
|
} else {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @private
|
||||||
|
/// @brief Helper to get printf format specifier based on type and base
|
||||||
|
template<__integral_type EIntTy>
|
||||||
|
const char* __get_int_format(__integral_base_type base) {
|
||||||
|
if (base == __integral_base_type::base8) {
|
||||||
|
if constexpr (EIntTy == __integral_type::u8) {
|
||||||
|
return "%" PRIo8;
|
||||||
|
} else if constexpr (EIntTy == __integral_type::u16) {
|
||||||
|
return "%" PRIo16;
|
||||||
|
} else if constexpr (EIntTy == __integral_type::u32) {
|
||||||
|
return "%" PRIo32;
|
||||||
|
} else if constexpr (EIntTy == __integral_type::u64) {
|
||||||
|
return "%" PRIo64;
|
||||||
|
} else {
|
||||||
|
static_assert(false, "Unsupported integral type and base");
|
||||||
|
}
|
||||||
|
} else if (base == __integral_base_type::base10) {
|
||||||
|
if constexpr (EIntTy == __integral_type::u8) {
|
||||||
|
return "%" PRIu8;
|
||||||
|
} else if constexpr (EIntTy == __integral_type::u16) {
|
||||||
|
return "%" PRIu16;
|
||||||
|
} else if constexpr (EIntTy == __integral_type::u32) {
|
||||||
|
return "%" PRIu32;
|
||||||
|
} else if constexpr (EIntTy == __integral_type::u64) {
|
||||||
|
return "%" PRIu64;
|
||||||
|
} else if constexpr (EIntTy == __integral_type::i8) {
|
||||||
|
return "%" PRId8;
|
||||||
|
} else if constexpr (EIntTy == __integral_type::i16) {
|
||||||
|
return "%" PRId16;
|
||||||
|
} else if constexpr (EIntTy == __integral_type::i32) {
|
||||||
|
return "%" PRId32;
|
||||||
|
} else if constexpr (EIntTy == __integral_type::i64) {
|
||||||
|
return "%" PRId64;
|
||||||
|
}
|
||||||
|
} else if (base == __integral_base_type::base16) {
|
||||||
|
if constexpr (EIntTy == __integral_type::u8) {
|
||||||
|
return "%" PRIx8;
|
||||||
|
} else if constexpr (EIntTy == __integral_type::u16) {
|
||||||
|
return "%" PRIx16;
|
||||||
|
} else if constexpr (EIntTy == __integral_type::u32) {
|
||||||
|
return "%" PRIx32;
|
||||||
|
} else if constexpr (EIntTy == __integral_type::u64) {
|
||||||
|
return "%" PRIx64;
|
||||||
|
} else {
|
||||||
|
static_assert(false, "Unsupported integral type and base");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Integer to_chars
|
||||||
|
template<typename T>
|
||||||
|
requires std::integral<T> && (!std::same_as<T, bool>)
|
||||||
|
to_chars_result to_chars(char* first, char* last, T value, int base = 10) {
|
||||||
|
if (first >= last) {
|
||||||
|
return {first, std::errc::value_too_large};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto integral_type = __classify_int_type<T>();
|
||||||
|
const auto opt_integral_base_type = __classify_int_base(base);
|
||||||
|
if (!opt_integral_base_type.has_value()) {
|
||||||
|
return {first, std::errc::invalid_argument};
|
||||||
|
}
|
||||||
|
const auto integral_base_type = std::move(opt_integral_base_type.value());
|
||||||
|
|
||||||
|
// Use snprintf with appropriate format
|
||||||
|
const auto max_buffer_size = static_cast<size_t>(last - first);
|
||||||
|
const char* format_string = __get_int_format<integral_type>(integral_base_type);
|
||||||
|
int written = std::snprintf(first, max_buffer_size, format_string, value);
|
||||||
|
if (written < 0 || static_cast<size_t>(written) >= max_buffer_size) {
|
||||||
|
return {last, std::errc::value_too_large};
|
||||||
|
}
|
||||||
|
return {first + written, std::errc{}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(YYCC_CHARCONV_HAS_TO_CHARS_FLOAT)
|
||||||
|
|
||||||
|
/// @private
|
||||||
|
enum class __float_type {
|
||||||
|
f32,
|
||||||
|
f64,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @private
|
||||||
|
template<typename T>
|
||||||
|
constexpr __float_type __classify_float_type() {
|
||||||
|
if constexpr (std::is_same_v<T, float>) {
|
||||||
|
return __float_type::f32;
|
||||||
|
} else if constexpr (std::is_same_v<T, double>) {
|
||||||
|
return __float_type::f64;
|
||||||
|
} else {
|
||||||
|
static_assert(false, "Unsupported floating point type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @private
|
||||||
|
enum class __float_fmt_type { general, scientific, fixed, hex };
|
||||||
|
|
||||||
|
/// @private
|
||||||
|
std::optional<__float_fmt_type> __classify_float_fmt(chars_format fmt) {
|
||||||
|
if (fmt == chars_format::general) {
|
||||||
|
return __float_fmt_type::general;
|
||||||
|
} else if (fmt == chars_format::scientific) {
|
||||||
|
return __float_fmt_type::scientific;
|
||||||
|
} else if (fmt == chars_format::fixed) {
|
||||||
|
return __float_fmt_type::fixed;
|
||||||
|
} else if (fmt == chars_format::hex) {
|
||||||
|
return __float_fmt_type::hex;
|
||||||
|
} else {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @private
|
||||||
|
template<__float_type TFpTy>
|
||||||
|
std::optional<const char*> __get_float_format(chars_format fmt) {
|
||||||
|
// Precision is passed by extra argument via ".*" format.
|
||||||
|
if (fmt == chars_format::general) {
|
||||||
|
if constexpr (TFpTy == __float_type::f32) {
|
||||||
|
return "%.*g";
|
||||||
|
} else if constexpr (TFpTy == __float_type::f64) {
|
||||||
|
return "%.*lg";
|
||||||
|
}
|
||||||
|
} else if (fmt == chars_format::scientific) {
|
||||||
|
if constexpr (TFpTy == __float_type::f32) {
|
||||||
|
return "%.*e";
|
||||||
|
} else if constexpr (TFpTy == __float_type::f64) {
|
||||||
|
return "%.*le";
|
||||||
|
}
|
||||||
|
} else if (fmt == chars_format::fixed) {
|
||||||
|
if constexpr (TFpTy == __float_type::f32) {
|
||||||
|
return "%.*f";
|
||||||
|
} else if constexpr (TFpTy == __float_type::f64) {
|
||||||
|
return "%.*lf";
|
||||||
|
}
|
||||||
|
} else if (fmt == chars_format::hex) {
|
||||||
|
if constexpr (TFpTy == __float_type::f32) {
|
||||||
|
return "%.*a";
|
||||||
|
} else if constexpr (TFpTy == __float_type::f64) {
|
||||||
|
return "%.*la";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float to_chars
|
||||||
|
template<typename T>
|
||||||
|
requires std::floating_point<T>
|
||||||
|
to_chars_result to_chars(char* first, char* last, T value, chars_format fmt, int precision = 6) {
|
||||||
|
if (first >= last) {
|
||||||
|
return {first, std::errc::value_too_large};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto float_type = __classify_float_type<T>();
|
||||||
|
const auto opt_float_fmt_type = __classify_float_fmt(fmt);
|
||||||
|
if (!opt_float_fmt_type.has_value()) {
|
||||||
|
return {first, std::errc::invalid_argument};
|
||||||
|
}
|
||||||
|
const auto float_fmt_type = std::move(opt_float_fmt_type.value());
|
||||||
|
|
||||||
|
const auto max_buffer_size = static_cast<size_t>(last - first);
|
||||||
|
const char* format_string = __get_float_format<float_type>(float_fmt_type);
|
||||||
|
int written = std::snprintf(first, max_buffer_size, format_string, precision, value);
|
||||||
|
if (written < 0 || static_cast<size_t>(written) >= max_buffer_size) {
|
||||||
|
return {last, std::errc::value_too_large};
|
||||||
|
}
|
||||||
|
return {first + written, std::errc{}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(YYCC_CHARCONV_HAS_FROM_CHARS_INT)
|
||||||
|
|
||||||
|
/// @private
|
||||||
|
enum class __strtoi_cluster { tol, toll, toul, toull };
|
||||||
|
|
||||||
|
/// @private
|
||||||
|
template<typename T>
|
||||||
|
constexpr __strtoi_cluster __classify_strtoi_cluster() {
|
||||||
|
if constexpr (std::is_signed_v<T>) {
|
||||||
|
if constexpr (sizeof(T) <= sizeof(long)) {
|
||||||
|
return __strtoi_cluster::tol;
|
||||||
|
} else if constexpr (sizeof(T) <= sizeof(long long)) {
|
||||||
|
return __strtoi_cluster::toll;
|
||||||
|
} else {
|
||||||
|
static_assert(false, "Unsupported signed integral type");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if constexpr (sizeof(T) <= sizeof(unsigned long)) {
|
||||||
|
return __strtoi_cluster::toul;
|
||||||
|
} else if constexpr (sizeof(T) <= sizeof(unsigned long long)) {
|
||||||
|
return __strtoi_cluster::toull;
|
||||||
|
} else {
|
||||||
|
static_assert(false, "Unsupported unsigned integral type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @private
|
||||||
|
template<__strtoi_cluster TFc>
|
||||||
|
auto __execute_strtoi(const char* str, char** str_end, int base) {
|
||||||
|
if constexpr (TFc == __strtoi_cluster::tol) {
|
||||||
|
return std::strtol(str, str_end, base);
|
||||||
|
} else if constexpr (TFc == __strtoi_cluster::toll) {
|
||||||
|
return std::strtoll(str, str_end, base);
|
||||||
|
} else if constexpr (TFc == __strtoi_cluster::toul) {
|
||||||
|
return std::strtoul(str, str_end, base);
|
||||||
|
} else if constexpr (TFc == __strtoi_cluster::toull) {
|
||||||
|
return std::strtoull(str, str_end, base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Integer from_chars
|
||||||
|
template<typename T>
|
||||||
|
requires std::integral<T> && (!std::same_as<T, bool>)
|
||||||
|
from_chars_result from_chars(const char* first, const char* last, T& value, int base = 10) {
|
||||||
|
if (first >= last) {
|
||||||
|
return {first, std::errc::invalid_argument};
|
||||||
|
}
|
||||||
|
|
||||||
|
// strtoi function cluster strongly order that given string must be NULL-terminated.
|
||||||
|
// So we must do a heavy copy in there because first-last pair is not NULL-terminated guaranteed.
|
||||||
|
std::string buffer(first, static_cast<size_t>(last - first));
|
||||||
|
|
||||||
|
constexpr auto strtoi_cluster = __classify_strtoi_cluster<T>();
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
char* end_ptr = const_cast<char*>(first);
|
||||||
|
auto result = __execute_strtoi<strtoi_cluster>(buffer.data(), &end_ptr, base);
|
||||||
|
if (errno == ERANGE) {
|
||||||
|
return {end_ptr, std::errc::result_out_of_range};
|
||||||
|
}
|
||||||
|
|
||||||
|
using strtoi_cluster_rvtype = decltype(result);
|
||||||
|
// Check if result fits in T
|
||||||
|
if (result < static_cast<strtoi_cluster_rvtype>(std::numeric_limits<T>::min())
|
||||||
|
|| result > static_cast<strtoi_cluster_rvtype>(std::numeric_limits<T>::max())) {
|
||||||
|
return {end_ptr, std::errc::result_out_of_range};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end_ptr == buffer.data()) {
|
||||||
|
return {first, std::errc::invalid_argument};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure we don't go past 'last'
|
||||||
|
if ((end_ptr - buffer.data()) > (last - first)) {
|
||||||
|
return {const_cast<char*>(last), std::errc::invalid_argument};
|
||||||
|
}
|
||||||
|
|
||||||
|
value = static_cast<T>(result);
|
||||||
|
return {first + (end_ptr - buffer.data()), std::errc{}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(YYCC_CHARCONV_HAS_FROM_CHARS_FLOAT)
|
||||||
|
|
||||||
|
/// @private
|
||||||
|
enum class __strtof_cluster { tof, tod };
|
||||||
|
|
||||||
|
/// @private
|
||||||
|
template<typename T>
|
||||||
|
constexpr __strtof_cluster __classify_strtof_cluster() {
|
||||||
|
if constexpr (std::is_same_v<T, float>) {
|
||||||
|
return __strtof_cluster::tof;
|
||||||
|
} else if constexpr (std::is_same_v<T, double>) {
|
||||||
|
return __strtof_cluster::tod;
|
||||||
|
} else {
|
||||||
|
static_assert(false, "Unsupported floating point type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @private
|
||||||
|
template<__strtof_cluster TFc>
|
||||||
|
auto __execute_strtof(const char* str, char** str_end) {
|
||||||
|
if constexpr (TFc == __strtof_cluster::tof) {
|
||||||
|
return std::strtof(str, str_end);
|
||||||
|
} else if constexpr (TFc == __strtof_cluster::tod) {
|
||||||
|
return std::strtod(str, str_end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float from_chars
|
||||||
|
template<typename T>
|
||||||
|
requires std::floating_point<T>
|
||||||
|
from_chars_result from_chars(const char* first, const char* last, T& value, chars_format fmt = chars_format::general) {
|
||||||
|
// We ignore "fmt" by design.
|
||||||
|
if (first >= last) {
|
||||||
|
return {first, std::errc::invalid_argument};
|
||||||
|
}
|
||||||
|
|
||||||
|
// strtof function cluster strongly order that given string must be NULL-terminated.
|
||||||
|
// So we must do a heavy copy in there because first-last pair is not NULL-terminated guaranteed.
|
||||||
|
std::string buffer(first, static_cast<size_t>(last - first));
|
||||||
|
|
||||||
|
constexpr auto strtof_cluster = __classify_strtof_cluster<T>();
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
char* end_ptr = const_cast<char*>(first);
|
||||||
|
auto result = __execute_strtof<strtof_cluster>(buffer.data(), &end_ptr);
|
||||||
|
if (errno == ERANGE) {
|
||||||
|
return {end_ptr, std::errc::result_out_of_range};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end_ptr == buffer.data()) {
|
||||||
|
return {first, std::errc::invalid_argument};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure we don't go past 'last'
|
||||||
|
if ((end_ptr - buffer.data()) > (last - first)) {
|
||||||
|
return {const_cast<char*>(last), std::errc::invalid_argument};
|
||||||
|
}
|
||||||
|
|
||||||
|
value = result;
|
||||||
|
return {first + (end_ptr - buffer.data()), std::errc{}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace std
|
||||||
|
|
||||||
|
//#endif
|
||||||
@@ -97,7 +97,7 @@ namespace yycc::string::op {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::u8string replace(const std::u8string_view& _strl, const std::u8string_view& _from_strl, const std::u8string_view& _to_strl) {
|
std::u8string to_replace(const std::u8string_view& _strl, const std::u8string_view& _from_strl, const std::u8string_view& _to_strl) {
|
||||||
// prepare result
|
// prepare result
|
||||||
std::u8string strl(_strl);
|
std::u8string strl(_strl);
|
||||||
replace(strl, _from_strl, _to_strl);
|
replace(strl, _from_strl, _to_strl);
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ namespace yycc::string::op {
|
|||||||
* @param[in] _to_strl The \e new string.
|
* @param[in] _to_strl The \e new string.
|
||||||
* @return The result of replacement.
|
* @return The result of replacement.
|
||||||
*/
|
*/
|
||||||
std::u8string replace(const std::u8string_view& _strl, const std::u8string_view& _from_strl, const std::u8string_view& _to_strl);
|
std::u8string to_replace(const std::u8string_view& _strl, const std::u8string_view& _from_strl, const std::u8string_view& _to_strl);
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,11 @@ PRIVATE
|
|||||||
GTest::gtest_main
|
GTest::gtest_main
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Install binary
|
||||||
|
install(TARGETS YYCCTest
|
||||||
|
RUNTIME DESTINATION ${YYCC_INSTALL_BIN_PATH}
|
||||||
|
)
|
||||||
|
|
||||||
# Discover all test
|
# Discover all test
|
||||||
include(GoogleTest)
|
include(GoogleTest)
|
||||||
gtest_discover_tests(YYCCTest)
|
gtest_discover_tests(YYCCTest)
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ namespace yyccshared::literals {
|
|||||||
// UNICODE Test Strings
|
// UNICODE Test Strings
|
||||||
// Ref: https://stackoverflow.com/questions/478201/how-to-test-an-application-for-correct-encoding-e-g-utf-8
|
// Ref: https://stackoverflow.com/questions/478201/how-to-test-an-application-for-correct-encoding-e-g-utf-8
|
||||||
|
|
||||||
|
#define UNICODE_STR_BLANK ""
|
||||||
#define UNICODE_STR_JAPAN "\u30E6\u30FC\u30B6\u30FC\u5225\u30B5\u30A4\u30C8"
|
#define UNICODE_STR_JAPAN "\u30E6\u30FC\u30B6\u30FC\u5225\u30B5\u30A4\u30C8"
|
||||||
#define UNICODE_STR_CHINA "\u7B80\u4F53\u4E2D\u6587"
|
#define UNICODE_STR_CHINA "\u7B80\u4F53\u4E2D\u6587"
|
||||||
#define UNICODE_STR_KOREA "\uD06C\uB85C\uC2A4 \uD50C\uB7AB\uD3FC\uC73C\uB85C"
|
#define UNICODE_STR_KOREA "\uD06C\uB85C\uC2A4 \uD50C\uB7AB\uD3FC\uC73C\uB85C"
|
||||||
@@ -30,6 +31,7 @@ namespace yyccshared::literals {
|
|||||||
#define UNICODE_STR_EMOJI "\U0001F363 \u2716 \U0001F37A" // sushi x beer mug
|
#define UNICODE_STR_EMOJI "\U0001F363 \u2716 \U0001F37A" // sushi x beer mug
|
||||||
|
|
||||||
static std::vector<std::u8string> UTFLIT_U8STR_VEC{
|
static std::vector<std::u8string> UTFLIT_U8STR_VEC{
|
||||||
|
U8_LITERAL(UNICODE_STR_BLANK),
|
||||||
U8_LITERAL(UNICODE_STR_JAPAN),
|
U8_LITERAL(UNICODE_STR_JAPAN),
|
||||||
U8_LITERAL(UNICODE_STR_CHINA),
|
U8_LITERAL(UNICODE_STR_CHINA),
|
||||||
U8_LITERAL(UNICODE_STR_KOREA),
|
U8_LITERAL(UNICODE_STR_KOREA),
|
||||||
@@ -44,6 +46,7 @@ namespace yyccshared::literals {
|
|||||||
U8_LITERAL(UNICODE_STR_EMOJI),
|
U8_LITERAL(UNICODE_STR_EMOJI),
|
||||||
};
|
};
|
||||||
static std::vector<std::wstring> UTFLIT_WSTR_VEC{
|
static std::vector<std::wstring> UTFLIT_WSTR_VEC{
|
||||||
|
WSTR_LITERAL(UNICODE_STR_BLANK),
|
||||||
WSTR_LITERAL(UNICODE_STR_JAPAN),
|
WSTR_LITERAL(UNICODE_STR_JAPAN),
|
||||||
WSTR_LITERAL(UNICODE_STR_CHINA),
|
WSTR_LITERAL(UNICODE_STR_CHINA),
|
||||||
WSTR_LITERAL(UNICODE_STR_KOREA),
|
WSTR_LITERAL(UNICODE_STR_KOREA),
|
||||||
@@ -58,6 +61,7 @@ namespace yyccshared::literals {
|
|||||||
WSTR_LITERAL(UNICODE_STR_EMOJI),
|
WSTR_LITERAL(UNICODE_STR_EMOJI),
|
||||||
};
|
};
|
||||||
static std::vector<std::u16string> UTFLIT_U16STR_VEC{
|
static std::vector<std::u16string> UTFLIT_U16STR_VEC{
|
||||||
|
U16_LITERAL(UNICODE_STR_BLANK),
|
||||||
U16_LITERAL(UNICODE_STR_JAPAN),
|
U16_LITERAL(UNICODE_STR_JAPAN),
|
||||||
U16_LITERAL(UNICODE_STR_CHINA),
|
U16_LITERAL(UNICODE_STR_CHINA),
|
||||||
U16_LITERAL(UNICODE_STR_KOREA),
|
U16_LITERAL(UNICODE_STR_KOREA),
|
||||||
@@ -72,6 +76,7 @@ namespace yyccshared::literals {
|
|||||||
U16_LITERAL(UNICODE_STR_EMOJI),
|
U16_LITERAL(UNICODE_STR_EMOJI),
|
||||||
};
|
};
|
||||||
static std::vector<std::u32string> UTFLIT_U32STR_VEC{
|
static std::vector<std::u32string> UTFLIT_U32STR_VEC{
|
||||||
|
U32_LITERAL(UNICODE_STR_BLANK),
|
||||||
U32_LITERAL(UNICODE_STR_JAPAN),
|
U32_LITERAL(UNICODE_STR_JAPAN),
|
||||||
U32_LITERAL(UNICODE_STR_CHINA),
|
U32_LITERAL(UNICODE_STR_CHINA),
|
||||||
U32_LITERAL(UNICODE_STR_KOREA),
|
U32_LITERAL(UNICODE_STR_KOREA),
|
||||||
@@ -157,12 +162,22 @@ namespace yyccshared::literals {
|
|||||||
|
|
||||||
#pragma region OtherLiterals Data
|
#pragma region OtherLiterals Data
|
||||||
|
|
||||||
static std::vector<OtherLiteral> OTHERLIT_OTHERSTR_VEC{{"\xC4\xE3\xBA\xC3\xD6\xD0\xB9\xFA", UINT32_C(936), "GBK", u8"gbk"}};
|
static std::vector<OtherLiteral> OTHERLIT_OTHERSTR_VEC{
|
||||||
|
{"", UINT32_C(437), "ASCII", u8"ascii"},
|
||||||
|
{"\xC4\xE3\xBA\xC3\xD6\xD0\xB9\xFA", UINT32_C(936), "GBK", u8"gbk"},
|
||||||
|
};
|
||||||
|
|
||||||
|
#define OTHER_STR_BLANK ""
|
||||||
#define OTHER_STR_GBK "\u4f60\u597d\u4e2d\u56fd"
|
#define OTHER_STR_GBK "\u4f60\u597d\u4e2d\u56fd"
|
||||||
|
|
||||||
static std::vector<std::u8string> OTHERLIT_U8STR_VEC{U8_LITERAL(OTHER_STR_GBK)};
|
static std::vector<std::u8string> OTHERLIT_U8STR_VEC{
|
||||||
static std::vector<std::wstring> OTHERLIT_WSTR_VEC{WSTR_LITERAL(OTHER_STR_GBK)};
|
U8_LITERAL(OTHER_STR_BLANK),
|
||||||
|
U8_LITERAL(OTHER_STR_GBK),
|
||||||
|
};
|
||||||
|
static std::vector<std::wstring> OTHERLIT_WSTR_VEC{
|
||||||
|
WSTR_LITERAL(OTHER_STR_BLANK),
|
||||||
|
WSTR_LITERAL(OTHER_STR_GBK),
|
||||||
|
};
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
|
|||||||
@@ -72,10 +72,10 @@ namespace yycctest::carton::clap {
|
|||||||
|
|
||||||
// Add options
|
// Add options
|
||||||
auto options = CLAP::option::OptionCollection();
|
auto options = CLAP::option::OptionCollection();
|
||||||
int_option = options.add_option(CLAP::option::Option(u8"i", u8"int", u8"<integer>", u8"integral argument"));
|
int_option = options.add_option(CLAP::option::Option(u8"i", u8"int", u8"integer", u8"integral argument"));
|
||||||
float_option = options.add_option(CLAP::option::Option(u8"f", std::nullopt, u8"<float>", u8""));
|
float_option = options.add_option(CLAP::option::Option(u8"f", std::nullopt, u8"float", u8""));
|
||||||
string_option = options.add_option(CLAP::option::Option(std::nullopt, u8"string", u8"<string>", u8""));
|
string_option = options.add_option(CLAP::option::Option(std::nullopt, u8"string", u8"string", u8""));
|
||||||
clamped_float_option = options.add_option(CLAP::option::Option(std::nullopt, u8"clamped-float", u8"<float>", u8""));
|
clamped_float_option = options.add_option(CLAP::option::Option(std::nullopt, u8"clamped-float", u8"float", u8""));
|
||||||
novalue_option = options.add_option(CLAP::option::Option(u8"b", std::nullopt, std::nullopt, u8""));
|
novalue_option = options.add_option(CLAP::option::Option(u8"b", std::nullopt, std::nullopt, u8""));
|
||||||
|
|
||||||
// Add variables
|
// Add variables
|
||||||
|
|||||||
@@ -25,32 +25,32 @@ namespace yycctest::string::op {
|
|||||||
TEST(StringOp, Replace) {
|
TEST(StringOp, Replace) {
|
||||||
// Normal case
|
// Normal case
|
||||||
{
|
{
|
||||||
auto rv = OP::replace(u8"aabbcc", u8"bb", u8"dd");
|
auto rv = OP::to_replace(u8"aabbcc", u8"bb", u8"dd");
|
||||||
EXPECT_EQ(rv, u8"aaddcc");
|
EXPECT_EQ(rv, u8"aaddcc");
|
||||||
}
|
}
|
||||||
// No matched expected string
|
// No matched expected string
|
||||||
{
|
{
|
||||||
auto rv = OP::replace(u8"aabbcc", u8"zz", u8"yy");
|
auto rv = OP::to_replace(u8"aabbcc", u8"zz", u8"yy");
|
||||||
EXPECT_EQ(rv, u8"aabbcc");
|
EXPECT_EQ(rv, u8"aabbcc");
|
||||||
}
|
}
|
||||||
// Empty expected string
|
// Empty expected string
|
||||||
{
|
{
|
||||||
auto rv = OP::replace(u8"aabbcc", std::u8string_view(), u8"zz");
|
auto rv = OP::to_replace(u8"aabbcc", std::u8string_view(), u8"zz");
|
||||||
EXPECT_EQ(rv, u8"aabbcc");
|
EXPECT_EQ(rv, u8"aabbcc");
|
||||||
}
|
}
|
||||||
// Empty replace string
|
// Empty replace string
|
||||||
{
|
{
|
||||||
auto rv = OP::replace(u8"aaaabbaa", u8"aa", u8"");
|
auto rv = OP::to_replace(u8"aaaabbaa", u8"aa", u8"");
|
||||||
EXPECT_EQ(rv, u8"bb");
|
EXPECT_EQ(rv, u8"bb");
|
||||||
}
|
}
|
||||||
// Nested replacing
|
// Nested replacing
|
||||||
{
|
{
|
||||||
auto rv = OP::replace(u8"aaxcc", u8"x", u8"yx");
|
auto rv = OP::to_replace(u8"aaxcc", u8"x", u8"yx");
|
||||||
EXPECT_EQ(rv, u8"aayxcc");
|
EXPECT_EQ(rv, u8"aayxcc");
|
||||||
}
|
}
|
||||||
// Empty source string
|
// Empty source string
|
||||||
{
|
{
|
||||||
auto rv = OP::replace(std::u8string_view(), u8"", u8"xy");
|
auto rv = OP::to_replace(std::u8string_view(), u8"", u8"xy");
|
||||||
EXPECT_EQ(rv, u8"");
|
EXPECT_EQ(rv, u8"");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,10 @@ namespace yycctest::windows::console {
|
|||||||
#if defined(YYCC_OS_WINDOWS) && defined(YYCC_STL_MSSTL)
|
#if defined(YYCC_OS_WINDOWS) && defined(YYCC_STL_MSSTL)
|
||||||
|
|
||||||
TEST(WindowsConsole, ColorfulConsole) {
|
TEST(WindowsConsole, ColorfulConsole) {
|
||||||
// Set colorful console should always be success.
|
|
||||||
auto rv = CONSOLE::colorful_console();
|
auto rv = CONSOLE::colorful_console();
|
||||||
EXPECT_TRUE(rv.has_value());
|
// Set colorful console should always be success.
|
||||||
|
// Unless it is not a TTY.
|
||||||
|
EXPECT_TRUE(rv.has_value() || rv.error() == CONSOLE::ExecError::NotTty);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user