Compare commits
1 Commits
master
...
7234b31ee0
| Author | SHA1 | Date | |
|---|---|---|---|
| 7234b31ee0 |
4
.github/scripts/README.md
vendored
4
.github/scripts/README.md
vendored
@@ -1,4 +0,0 @@
|
||||
# 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
17
.github/scripts/gbenchmark/linux.sh
vendored
@@ -1,17 +0,0 @@
|
||||
#!/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
17
.github/scripts/gbenchmark/macos.sh
vendored
@@ -1,17 +0,0 @@
|
||||
#!/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
17
.github/scripts/gbenchmark/windows.bat
vendored
@@ -1,17 +0,0 @@
|
||||
@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
17
.github/scripts/gtest/linux.sh
vendored
@@ -1,17 +0,0 @@
|
||||
#!/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
17
.github/scripts/gtest/macos.sh
vendored
@@ -1,17 +0,0 @@
|
||||
#!/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
17
.github/scripts/gtest/windows.bat
vendored
@@ -1,17 +0,0 @@
|
||||
@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
19
.github/scripts/linux.sh
vendored
@@ -1,19 +0,0 @@
|
||||
#!/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
19
.github/scripts/macos.sh
vendored
@@ -1,19 +0,0 @@
|
||||
#!/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
18
.github/scripts/windows.bat
vendored
@@ -1,18 +0,0 @@
|
||||
@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 ..
|
||||
66
.github/workflows/linux.yml
vendored
66
.github/workflows/linux.yml
vendored
@@ -1,66 +0,0 @@
|
||||
name: YYCC Linux Build
|
||||
|
||||
on: [workflow_dispatch]
|
||||
|
||||
jobs:
|
||||
linux-build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y build-essential cmake git
|
||||
- name: Fetch Google Test
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'google/googletest'
|
||||
ref: 'v1.17.0'
|
||||
path: 'extern/googletest'
|
||||
- name: Build Google Test
|
||||
shell: bash
|
||||
run: |
|
||||
cd extern/googletest
|
||||
# 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
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: YYCC-linux-build
|
||||
path: bin/install/*
|
||||
retention-days: 30
|
||||
61
.github/workflows/macos.yml
vendored
61
.github/workflows/macos.yml
vendored
@@ -1,61 +0,0 @@
|
||||
name: YYCC macOS Build
|
||||
|
||||
on: [workflow_dispatch]
|
||||
|
||||
jobs:
|
||||
macos-build:
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Fetch Google Test
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'google/googletest'
|
||||
ref: 'v1.17.0'
|
||||
path: 'extern/googletest'
|
||||
- name: Build Google Test
|
||||
shell: bash
|
||||
run: |
|
||||
cd extern/googletest
|
||||
# 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
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: YYCC-macos-build
|
||||
path: bin/install/*
|
||||
retention-days: 30
|
||||
35
.github/workflows/nightly.yml.disabled
vendored
Normal file
35
.github/workflows/nightly.yml.disabled
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
name: YYCC Nightly Build
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
msvc-build:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
vs: ['2019']
|
||||
msvc_arch: ['x86']
|
||||
|
||||
runs-on: windows-2019
|
||||
|
||||
steps:
|
||||
- name: Fetching Repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Building YYCC
|
||||
shell: cmd
|
||||
run: |
|
||||
set VS=${{ matrix.vs }}
|
||||
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"
|
||||
call %VCVARS% ${{ matrix.msvc_arch }}
|
||||
.\script\build.bat
|
||||
- name: Uploading Nightly Build
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: YYCC-windows-nightly
|
||||
path: bin/install/*
|
||||
retention-days: 30
|
||||
78
.github/workflows/windows.yml
vendored
78
.github/workflows/windows.yml
vendored
@@ -1,78 +0,0 @@
|
||||
name: YYCC Windows Build
|
||||
|
||||
on: [workflow_dispatch]
|
||||
|
||||
jobs:
|
||||
windows-build:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- vs: '2022'
|
||||
msvc_arch: 'x64'
|
||||
|
||||
runs-on: windows-2022
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Fetch Google Test
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'google/googletest'
|
||||
ref: 'v1.17.0'
|
||||
path: 'extern/googletest'
|
||||
- name: Build Google Test
|
||||
shell: cmd
|
||||
run: |
|
||||
CD extern\googletest
|
||||
:: Build Google Test
|
||||
CALL ..\..\.github\scripts\gtest\windows.bat
|
||||
:: Idk why I can't use $GITHUB_ENV, so I use this stupid way to do this.
|
||||
:: This is first entry so we override it.
|
||||
ECHO SET GTest_ROOT=%GTest_ROOT% > ..\envs.bat
|
||||
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: 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
|
||||
shell: cmd
|
||||
run: |
|
||||
:: Prepare Visual Studio
|
||||
set VS=${{ matrix.vs }}
|
||||
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"
|
||||
call %VCVARS% ${{ matrix.msvc_arch }}
|
||||
:: 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
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: YYCC-windows-build
|
||||
path: bin/install/*
|
||||
retention-days: 30
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,7 +3,6 @@
|
||||
out/
|
||||
build/
|
||||
install/
|
||||
extern/
|
||||
|
||||
# Ignore CMake generated stuff
|
||||
src/yycc/version.hpp
|
||||
|
||||
@@ -10,8 +10,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
# Provide options
|
||||
option(YYCC_BUILD_TEST "Build test of YYCCommonplace." OFF)
|
||||
option(YYCC_BUILD_BENCHMARK "Build benchmark of YYCCommonplace." OFF)
|
||||
option(YYCC_BUILD_TESTBENCH "Build testbench of YYCCommonplace." OFF)
|
||||
option(YYCC_BUILD_DOC "Build document of YYCCommonplace." OFF)
|
||||
option(YYCC_ENFORCE_ICONV "Enforce iconv support for this library (e.g. in MSYS2 environment)." OFF)
|
||||
|
||||
@@ -26,36 +25,22 @@ set(YYCC_INSTALL_BIN_PATH ${CMAKE_INSTALL_BINDIR} 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.")
|
||||
|
||||
# Test charconv support due to shitty clang's libcxx.
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/cmake/check_charconv.cmake)
|
||||
|
||||
# Include dependency.
|
||||
# GTest is required if we build test
|
||||
if (YYCC_BUILD_TEST)
|
||||
# GTest is required if we build testbench
|
||||
if (YYCC_BUILD_TESTBENCH)
|
||||
# For Windows: Prevent overriding the parent project's compiler/linker settings
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||
find_package(GTest REQUIRED)
|
||||
endif ()
|
||||
# Google Benchmark is required if we build benchmark
|
||||
if (YYCC_BUILD_BENCHMARK)
|
||||
find_package(benchmark REQUIRED)
|
||||
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
|
||||
if (YYCC_ENFORCE_ICONV OR (NOT WIN32))
|
||||
find_package(Iconv REQUIRED)
|
||||
endif ()
|
||||
|
||||
# Import 4 build targets
|
||||
# Import 3 build targets
|
||||
add_subdirectory(src)
|
||||
if (YYCC_BUILD_TEST)
|
||||
add_subdirectory(test)
|
||||
endif ()
|
||||
if (YYCC_BUILD_BENCHMARK)
|
||||
add_subdirectory(benchmark)
|
||||
if (YYCC_BUILD_TESTBENCH)
|
||||
add_subdirectory(testbench)
|
||||
endif ()
|
||||
if (YYCC_BUILD_DOC)
|
||||
add_subdirectory(doc)
|
||||
|
||||
111
COMPILE.md
111
COMPILE.md
@@ -2,9 +2,6 @@
|
||||
|
||||
## Choose Version
|
||||
|
||||
This manual is only suit for the version equal or newer than YYCC 2.0.
|
||||
For old version, please checkout to corresponding tag and browse how to build them.
|
||||
|
||||
We suggest that you only use stable version (tagged commit).
|
||||
The latest commit always present current works.
|
||||
It means that it is not stable and work in progress.
|
||||
@@ -14,33 +11,20 @@ It means that it is not stable and work in progress.
|
||||
* CMake 3.23 at least.
|
||||
* The common compiler supporting C++ 23 (GCC / Clang / MSVC).
|
||||
* Iconv (Optional on Windows. Required on other systems).
|
||||
* [Google Test](https://github.com/google/googletest) (Required if you build test).
|
||||
* [Google Benchmark](https://github.com/google/benchmark) (Required if you build benchmark).
|
||||
* [GoogleTest](https://github.com/google/googletest) (Required if you build testbench).
|
||||
* Doxygen (Required if you build documentation).
|
||||
* Python and Astral UV (Required if you use "User Build" method)
|
||||
|
||||
If you are just want to build this project to make something works, or build other project, rather than code with it,
|
||||
you commonly do not need build test, benchmark and documentation.
|
||||
So you actually do not need Google Test, Google Benchmark and Doxygen.
|
||||
> [!WARNING]
|
||||
> You may face some issues when building on macOS with Clang. That's not your fault.
|
||||
> Clang used libc++ library lacks some essential features used by this project.
|
||||
> You may try other solutions for compiling this project on macOS or with Clang.
|
||||
|
||||
## Preparing
|
||||
|
||||
### Compiler
|
||||
|
||||
> [!WARNING]
|
||||
> You may face some issues when building on macOS with Apple Clang. That's not your fault.
|
||||
> Clang and Apple Clang used libc++ library lacks some essential features used by this project.
|
||||
> This is especially not good for Apple Clang because Apple Clang is usually behind Clang a bunch of versions.
|
||||
>
|
||||
> 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`.
|
||||
|
||||
### GoogleTest
|
||||
|
||||
Google Test is required if you need to build test.
|
||||
GoogleTest is required if you need to build testbench.
|
||||
If you don't need this please skip this chapter.
|
||||
|
||||
We use GoogleTest v1.17.0.
|
||||
@@ -62,24 +46,6 @@ There are the steps instructing you how to compile Google Test manually.
|
||||
1. Use CMake to build GoogleTest
|
||||
1. Use CMake to install GoogleTest into previous we created `install` directory.
|
||||
|
||||
### Google Benchmark
|
||||
|
||||
Google Benchmark is required if you need to build benchmark.
|
||||
If you don't need this please skip this chapter.
|
||||
|
||||
We use Google Benchmark v1.9.4.
|
||||
It would be okey use other versions but I have not test on them.
|
||||
|
||||
There are the steps instructing you how to compile Google Benchmark manually.
|
||||
|
||||
1. Download Google Benchmark source code with given version in GitHub Release page.
|
||||
1. Extract it into a directory.
|
||||
1. Enter this directory and create link named `googletest` to previous fetched Google Test root directory. This is instructed by official manual because Google Benchmark rely on Google Test. Link can be create by executing `mklink /D googletest <path-to-googletest-root-dir>` on Windows or `ln -s <path-to-googletest-root-dir> googletest` on POSIX-like OS.
|
||||
1. Keep stay in this directory and create 2 subdirectory `build` and `install` for CMake build and install respectively.
|
||||
1. Enter `build` directory and configure CMake with extra `-DCMAKE_CXX_STANDARD=23 -DBENCHMARK_ENABLE_TESTING=OFF` parameters.
|
||||
1. Use CMake to build Google Benchmark
|
||||
1. Use CMake to install Google Benchmark into previous we created `install` directory.
|
||||
|
||||
### Iconv
|
||||
|
||||
Iconv is optional on Windows and disabled in default.
|
||||
@@ -105,81 +71,36 @@ So before compiling, you must make sure `doxygen` are presented in your environm
|
||||
## Build and Install
|
||||
|
||||
There are 2 different ways to build this project.
|
||||
If you are the user of this project (just want this project to make something works, or build other projects), please choose "User Build".
|
||||
If you are the user of this project (just want this project to make something work), please choose "User Build".
|
||||
If you are a developer (developer of this project, or use this project as dependency to develop your project), please choose "Developer Build".
|
||||
|
||||
### User Build
|
||||
|
||||
"User Build" is basically how GitHub Action build this project.
|
||||
|
||||
Under **the root directory** of this project, execute:
|
||||
We use Python 3.11 and UV 0.7.17 to manage our build script generator.
|
||||
It would be okey use other versions but I have not test on them.
|
||||
|
||||
- `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.
|
||||
TODO...
|
||||
|
||||
### Developer Build
|
||||
|
||||
#### Configurable Variables
|
||||
TODO...
|
||||
|
||||
First, there is a list listing all variables you may configure during compiling.
|
||||
There is a list listing all variables you may configure during compiling.
|
||||
|
||||
* `YYCC_BUILD_TEST`: Set it to `ON` to build test. `OFF` in default.
|
||||
* `YYCC_BUILD_TESTBENCH`: Set it to `ON` to build testbench. `OFF` in default.
|
||||
It is useful for the developer of this project.
|
||||
It also suit for the user who has runtime issues on their platforms to check whether this project works as expected.
|
||||
If you are debugging this project to find bug, I suggest that you build this project under Debug mode and use this test project for debugging.
|
||||
* `YYCC_BUILD_BENCHMARK`: Set it to `ON` to build benchmark. `OFF` in default.
|
||||
It is useful for the developer of this project to checking the performace for those homemade functions.
|
||||
It is highly suggested build this project with Release mode to have real benchmark result.
|
||||
* `YYCC_BUILD_DOC`: Set it to `ON` to build documentation. `OFF` in default.
|
||||
It may be useful for the developer who firstly use this project in their own projects.
|
||||
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.
|
||||
The usage of this option has been introduced in previous "Iconv" chapter.
|
||||
* `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.
|
||||
* `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.
|
||||
* `Iconv_ROOT`: The assistant variable for finding Iconv which is exposed by CMake.
|
||||
You usually do not need set it up.
|
||||
* `GTest_ROOT`: TODO
|
||||
* `Iconv_ROOT`: TODO
|
||||
* `CMAKE_CXX_STANDARD`: Set C++ standard version of project.
|
||||
`23` in default and this version can not be lower than C++23.
|
||||
You usually do not need change this.
|
||||
* `CMAKE_POSITION_INDEPENDENT_CODE`: Set it to `True` to enable PIC.
|
||||
This is essential for those project which use this project and produce dynamicing library as final artifact.
|
||||
|
||||
#### Configure CMake
|
||||
|
||||
When configure CMake, you may use different options on different platforms.
|
||||
Following list may help you.
|
||||
|
||||
- On Windows:
|
||||
* `-A Win32` or `-A x64` to specify architecture.
|
||||
- On Linux or other POSIX systems:
|
||||
* `-DCMAKE_BUILD_TYPE=Debug` or `-DCMAKE_BUILD_TYPE=Release` to specify build type.
|
||||
|
||||
Additionally, you can attach any variables introduced above with `-D` option during CMake configurations.
|
||||
|
||||
#### Build with CMake
|
||||
|
||||
After configuration, you can use `cmake --build .` to build project,
|
||||
with additional options on different platforms.
|
||||
Following list may help you.
|
||||
|
||||
- On Windows:
|
||||
* `--config Debug` or `--config Release` to specify build type.
|
||||
- On Linux or other POSIX systems:
|
||||
* None
|
||||
|
||||
#### Install with CMake
|
||||
|
||||
After building, you can use `cmake --install . --prefix <path-to-prefix>`
|
||||
to install project into given path, with additional options on different platforms.
|
||||
Following list may help you.
|
||||
|
||||
- On Windows:
|
||||
* `--config Debug` or `--config Release` to specify build type.
|
||||
- On Linux or other POSIX systems:
|
||||
* None
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2024-2026 yyc12345
|
||||
Copyright (c) 2024-2024 yyc12345
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
# Create executable benchmark
|
||||
add_executable(YYCCBenchmark "")
|
||||
# Setup test sources
|
||||
target_sources(YYCCBenchmark
|
||||
PRIVATE
|
||||
main.cpp
|
||||
|
||||
yycc/string/op.cpp
|
||||
|
||||
yycc/carton/fft.cpp
|
||||
)
|
||||
# target_sources(YYCCBenchmark
|
||||
# PRIVATE
|
||||
# FILE_SET HEADERS
|
||||
# FILES
|
||||
# shared/literals.hpp
|
||||
# )
|
||||
# Setup headers
|
||||
target_include_directories(YYCCBenchmark
|
||||
PUBLIC
|
||||
"${CMAKE_CURRENT_LIST_DIR}"
|
||||
)
|
||||
# Setup libraries
|
||||
target_link_libraries(YYCCBenchmark
|
||||
PRIVATE
|
||||
YYCCommonplace
|
||||
benchmark::benchmark
|
||||
)
|
||||
|
||||
# Install binary
|
||||
install(TARGETS YYCCBenchmark
|
||||
RUNTIME DESTINATION ${YYCC_INSTALL_BIN_PATH}
|
||||
)
|
||||
@@ -1,3 +0,0 @@
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
BENCHMARK_MAIN();
|
||||
@@ -1,40 +0,0 @@
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/carton/fft.hpp>
|
||||
#include <random>
|
||||
#include <chrono>
|
||||
|
||||
#define FFT ::yycc::carton::fft
|
||||
|
||||
namespace yyccbench::carton::fft {
|
||||
|
||||
using TIndex = size_t;
|
||||
using TFloat = float;
|
||||
using TComplex = std::complex<TFloat>;
|
||||
template<TIndex N>
|
||||
using TFft = FFT::Fft<TIndex, TFloat, N>;
|
||||
|
||||
constexpr TIndex FFT_POINTS = 1024u;
|
||||
|
||||
static void BM_FftCompute(benchmark::State& state) {
|
||||
// prepare random buffer
|
||||
constexpr TIndex RND_BUF_CNT = 8u;
|
||||
std::random_device rnd_device;
|
||||
std::default_random_engine rnd_engine(rnd_device());
|
||||
std::uniform_real_distribution<TFloat> rnd_dist(0.0f, 1.0f);
|
||||
std::vector<std::vector<TComplex>> buffer_collection(RND_BUF_CNT);
|
||||
for (auto& buf : buffer_collection) {
|
||||
buf.resize(FFT_POINTS);
|
||||
std::generate(buf.begin(), buf.end(), [&rnd_engine, &rnd_dist]() mutable -> TComplex { return TComplex(rnd_dist(rnd_engine)); });
|
||||
}
|
||||
|
||||
// prepare FFT engine
|
||||
TFft<FFT_POINTS> fft;
|
||||
// do benchmark
|
||||
for (auto _ : state) {
|
||||
fft.compute(buffer_collection[state.iterations() % RND_BUF_CNT].data());
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_FftCompute)->Name("FftCompute");
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/string/op.hpp>
|
||||
|
||||
#define OP ::yycc::string::op
|
||||
using namespace std::literals::string_view_literals;
|
||||
|
||||
namespace yyccbench::string::op {
|
||||
|
||||
static void BM_StringStrip(benchmark::State& state) {
|
||||
std::u8string_view strl = u8" \thello\r\n"sv, words = u8" \t\r\n"sv;
|
||||
for (auto _ : state) {
|
||||
auto rv = OP::strip(strl, words);
|
||||
benchmark::DoNotOptimize(rv);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_StringStrip)->Name("StringStrip");
|
||||
|
||||
static void BM_StringTrim(benchmark::State& state) {
|
||||
std::u8string_view strl = u8" \thello\r\n"sv, words = u8" \t\r\n"sv;
|
||||
for (auto _ : state) {
|
||||
auto rv = OP::trim(strl, words);
|
||||
benchmark::DoNotOptimize(rv);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_StringTrim)->Name("StringTrim");
|
||||
|
||||
}
|
||||
@@ -1,11 +1,6 @@
|
||||
|
||||
@PACKAGE_INIT@
|
||||
|
||||
# Find Iconv if we have found it.
|
||||
if ("@Iconv_FOUND@")
|
||||
find_package(Iconv REQUIRED)
|
||||
endif ()
|
||||
|
||||
# Include targets file
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/YYCCommonplaceTargets.cmake")
|
||||
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
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}")
|
||||
@@ -1,8 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
#include <charconv>
|
||||
#include <system_error>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
std::from_chars_result result {
|
||||
.ptr = nullptr,
|
||||
.ec = std::errc{},
|
||||
};
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
#include <charconv>
|
||||
#include <system_error>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
std::to_chars_result result {
|
||||
.ptr = nullptr,
|
||||
.ec = std::errc{},
|
||||
};
|
||||
}
|
||||
@@ -1,36 +1,19 @@
|
||||
# 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 Doxygen config file
|
||||
configure_file(
|
||||
${CMAKE_CURRENT_LIST_DIR}/Doxyfile.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
|
||||
@ONLY
|
||||
)
|
||||
|
||||
# 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
|
||||
add_custom_target (YYCCDocumentation
|
||||
Doxygen::doxygen "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/Doxyfile"
|
||||
doxygen ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMENT "Generating documentation" VERBATIM
|
||||
)
|
||||
|
||||
# Install built documentation
|
||||
install (DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html
|
||||
CONFIGURATIONS Release RelWithDebInfo MinSizeRel
|
||||
DESTINATION ${YYCC_INSTALL_DOC_PATH}
|
||||
)
|
||||
|
||||
@@ -1031,7 +1031,7 @@ EXCLUDE_SYMBOLS =
|
||||
# that contain example code fragments that are included (see the \include
|
||||
# command).
|
||||
|
||||
EXAMPLE_PATH = @CMAKE_CURRENT_LIST_DIR@/../test
|
||||
EXAMPLE_PATH = @CMAKE_CURRENT_LIST_DIR@/../testbench
|
||||
|
||||
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
|
||||
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
|
||||
@@ -2306,7 +2306,7 @@ PERLMOD_MAKEVAR_PREFIX =
|
||||
# C-preprocessor directives found in the sources and include files.
|
||||
# The default value is: YES.
|
||||
|
||||
ENABLE_PREPROCESSING = YES
|
||||
ENABLE_PREPROCESSING = NO
|
||||
|
||||
# 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
|
||||
@@ -2356,7 +2356,7 @@ INCLUDE_FILE_PATTERNS =
|
||||
# recursively expanded use the := operator instead of the = operator.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
PREDEFINED = @YYCC_MACRO_GENERATOR_EXPRESSIONS@
|
||||
PREDEFINED = YYCC_DOXYGEN
|
||||
|
||||
# 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
|
||||
|
||||
200
doc/src/arg_parser.dox
Normal file
200
doc/src/arg_parser.dox
Normal file
@@ -0,0 +1,200 @@
|
||||
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 testbench 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.
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
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
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
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,74 +0,0 @@
|
||||
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,93 +0,0 @@
|
||||
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,102 +0,0 @@
|
||||
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
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
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.
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
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
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
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.
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
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
|
||||
|
||||
*/
|
||||
}
|
||||
35
doc/src/com_helper.dox
Normal file
35
doc/src/com_helper.dox
Normal file
@@ -0,0 +1,35 @@
|
||||
namespace YYCC::COMHelper {
|
||||
/**
|
||||
|
||||
\page com_helper COM Helper
|
||||
|
||||
This namespace is Windows specific.
|
||||
It will be invisible on other platforms.
|
||||
|
||||
This namespace is used by internal functions as intended.
|
||||
They should not be used outside of this library.
|
||||
But if you compel to use them, it is also okey.
|
||||
|
||||
\section com_helper__memory_safe_ptr Memory Safe Pointer Types
|
||||
|
||||
This namespace also provided various memory-safe types for interacting with COM functions.
|
||||
Although Microsoft also has similar smart pointer called \c CComPtr.
|
||||
But this library is eager to hide all Microsoft-related functions calling.
|
||||
Using \c CComPtr is not corresponding with the philosophy of this library.
|
||||
So these standard library based smart pointer types were created.
|
||||
|
||||
\section com_helper__com_guard COM Guard
|
||||
|
||||
This namespace contain a COM Guard which make sure COM was initialized in current module when loading current module.
|
||||
It is essential because all calling to COM functions should be under the premise that COM has been initialized.
|
||||
This guard also will uninitialize COM when unloading this module.
|
||||
|
||||
There is only an exposed function called #IsInitialized for user calling.
|
||||
This function will check whether COM environment is initialized.
|
||||
If you want YYCC automatically initialize COM environment for you,
|
||||
you must call this function in your program at least one time.
|
||||
Otherwise COM Guard code may be unavailable,
|
||||
because compiler may think they are not essential code and drop them.
|
||||
|
||||
*/
|
||||
}
|
||||
151
doc/src/config_manager.dox
Normal file
151
doc/src/config_manager.dox
Normal file
@@ -0,0 +1,151 @@
|
||||
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.
|
||||
|
||||
*/
|
||||
}
|
||||
181
doc/src/console_helper.dox
Normal file
181
doc/src/console_helper.dox
Normal file
@@ -0,0 +1,181 @@
|
||||
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.
|
||||
|
||||
*/
|
||||
}
|
||||
49
doc/src/constraints.dox
Normal file
49
doc/src/constraints.dox
Normal file
@@ -0,0 +1,49 @@
|
||||
namespace YYCC::Constraints {
|
||||
/**
|
||||
|
||||
\page constraints Constraints
|
||||
|
||||
YYCC::Constraints namespace provide Constraint struct declaration
|
||||
and various common constraint generator function.
|
||||
|
||||
This namespace is specifically used by YYCC::ConfigManager and YYCC::ArgParser namespaces.
|
||||
See \ref config_manager chapter and \ref arg_parser chapter for how to utlize this namespace.
|
||||
|
||||
\section constraints__prototype Prototype
|
||||
|
||||
Constraint instruct library how check whether given value is in range,
|
||||
and how to clamp it if it is invalid.
|
||||
For example, you can use constraint to limit a number in given minimum maximum value,
|
||||
or limit a string in specific format by using regex and etc.
|
||||
|
||||
Constraint is a template struct.
|
||||
The argument of template is the underlying data type which need to be checked.
|
||||
The struct with different template argument is not compatible.
|
||||
|
||||
Currently, this struct only contain 1 function pointer,
|
||||
which is used for detecting whether given value is in range / valid.
|
||||
|
||||
\subsection constraints__presets Constraint Presets
|
||||
|
||||
YYCC::Constraints provides some constraint presets which are commonly used.
|
||||
All functions inside this namespace will return a Constraint instance,
|
||||
and you can directly use it.
|
||||
|
||||
There is a list of all provided functions:
|
||||
|
||||
\li GetMinMaxRangeConstraint(): Limit the number value in given minimum maximum value range (inclusive).
|
||||
\li GetEnumEnumerationConstraint(): Limit the enum value by given all possible value set.
|
||||
\li GetStringEnumerationConstraint(): Limit the string by given all possible value set.
|
||||
|
||||
\subsection config_manager__constraint__custom Custom Constraint
|
||||
|
||||
For creating your personal constraint,
|
||||
you need to create Constraint instance manually.
|
||||
You can browse all existing constraint preset functions code for know how to write it.
|
||||
|
||||
The things you need to do is simple.
|
||||
First, you need decide the template argument of Constraint.
|
||||
Second, you need assign class member of Constraint by C++ lambda syntax.
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace yycc::windows::dialog {
|
||||
namespace YYCC::DialogHelper {
|
||||
/**
|
||||
|
||||
\page windows__dialog Dialog Helper
|
||||
\page dialog_helper Dialog Helper
|
||||
|
||||
Picking files and folders is an important and essential operation under Windows.
|
||||
However the functions picking files and folders are so complex.
|
||||
@@ -11,7 +11,7 @@ In following contents we will tell you how to call them.
|
||||
This helper is Windows specific.
|
||||
It will be totally invisible if you are in other platforms.
|
||||
|
||||
\section windows__dialog__file_dialog Configure File Dialog
|
||||
\section dialog_helper__file_dialog Configure File Dialog
|
||||
|
||||
The first thing is that we should initialize FileDialog,
|
||||
and configure it according to your requirements.
|
||||
@@ -20,16 +20,16 @@ This class is the data struct representing all aspects of file dialog.
|
||||
It also one of the arguments in final dialog function.
|
||||
|
||||
\code
|
||||
FileDialog params;
|
||||
params.set_owner(owner_getter());
|
||||
params.set_title(u8"My File Picker");
|
||||
params.set_init_file_name(u8"test.txt");
|
||||
params.set_init_directory(initial_directory_getter());
|
||||
YYCC::DialogHelper::FileDialog params;
|
||||
params.SetOwner(owner_getter());
|
||||
params.SetTitle(YYCC_U8("My File Picker"));
|
||||
params.SetInitFileName(YYCC_U8("test.txt"));
|
||||
params.SetInitDirectory(initial_directory_getter());
|
||||
\endcode
|
||||
|
||||
\subsection windows__dialog__file_dialog__owner Owner
|
||||
\subsection dialog_helper__file_dialog__owner Owner
|
||||
|
||||
FileDialog::set_owner() will set owner of this dialog.
|
||||
FileDialog::SetOwner will set owner of this dialog.
|
||||
It accepts a Microsoft defined \c HWND as argument which should be familiar with Windows programmer.
|
||||
If you pass \c NULL to it or skip calling this function, it indicate that there is no owner of this dialog.
|
||||
<I>
|
||||
@@ -37,9 +37,9 @@ I don't what will happen if there is no owner for it.
|
||||
But it would be better to have an owner if possible.
|
||||
</I>
|
||||
|
||||
\subsection windows__dialog__file_dialog__title Title
|
||||
\subsection dialog_helper__file_dialog__title Title
|
||||
|
||||
FileDialog::set_title() will set dialog title of this dialog.
|
||||
FileDialog::SetTitle will set dialog title of this dialog.
|
||||
If you pass \c nullptr or skip calling it,
|
||||
the title of dialog will be filled by system and the function type you calling.
|
||||
For example, the title will be "Open..." if you call open file function,
|
||||
@@ -48,9 +48,9 @@ At the same time, the language of this title filled by system is system UI depen
|
||||
It means that you do not need to do any extra I18N work for it.
|
||||
So I suggest you do not set title except you really want to modify title.
|
||||
|
||||
\subsection windows__dialog__file_dialog__init_file_name Initial File Name
|
||||
\subsection dialog_helper__file_dialog__init_file_name Initial File Name
|
||||
|
||||
FileDialog::set_init_file_name() will set the initial file name presented in dialog file name input box.
|
||||
FileDialog::SetInitFileName will set the initial file name presented in dialog file name input box.
|
||||
If you pass \c nullptr or skip calling it, the text in dialog file name input box will be empty.
|
||||
|
||||
User can modify the name presented in input box later.
|
||||
@@ -58,9 +58,9 @@ But if you assign this value, the dialog will lose the ability that remember the
|
||||
In normal case, dialog will try remembering the file name user input in dialog, and represent it in the next calling.
|
||||
However, if you specify this field, the dialog will always presented your specified value in every calling.
|
||||
|
||||
\subsection windows__dialog__file_dialog__init_directory Initial Directory
|
||||
\subsection dialog_helper__file_dialog__init_directory Initial Directory
|
||||
|
||||
FileDialog::set_init_directory() will set the initial directory (startup directory) when opening dialog.
|
||||
FileDialog::SetInitDirectory will set the initial directory (startup directory) when opening dialog.
|
||||
|
||||
In following cases, initial directory will fall back to system behavior:
|
||||
|
||||
@@ -72,7 +72,7 @@ The system default behavior of initial directory is similar with initial file na
|
||||
The dialog will try remembering the last directory you just entering, and will back into it in the next calling.
|
||||
The directory we meeting in the first launch is system defined.
|
||||
|
||||
\section windows__dialog__file_filters Configure File Filters
|
||||
\section dialog_helper__file_filters Configure File Filters
|
||||
|
||||
File filters is a drop down list represented in file dialog which allow user filter files by their extensions.
|
||||
It is beneficial to let user get the file which they want in a directory including massive different files.
|
||||
@@ -84,20 +84,20 @@ Directory can not be filtered.
|
||||
FileFilters takes responsibility for this feature:
|
||||
|
||||
\code
|
||||
auto& filters = params.configure_file_types();
|
||||
filters.add_filter(u8"Microsoft Word (*.docx; *.doc)", { u8"*.docx", u8"*.doc" });
|
||||
filters.add_filter(u8"Microsoft Excel (*.xlsx; *.xls)", { u8"*.xlsx", u8"*.xls" });
|
||||
filters.add_filter(u8"Microsoft PowerPoint (*.pptx; *.ppt)", { u8"*.pptx", u8"*.ppt" });
|
||||
filters.add_filter(u8"Text File (*.txt)", { u8"*.txt" });
|
||||
filters.add_filter(u8"All Files (*.*)", { u8"*.*" });
|
||||
params.set_default_file_type_index(0u);
|
||||
auto& filters = params.ConfigreFileTypes();
|
||||
filters.Add(YYCC_U8("Microsoft Word (*.docx; *.doc)"), { YYCC_U8("*.docx"), YYCC_U8("*.doc") });
|
||||
filters.Add(YYCC_U8("Microsoft Excel (*.xlsx; *.xls)"), { YYCC_U8("*.xlsx"), YYCC_U8("*.xls") });
|
||||
filters.Add(YYCC_U8("Microsoft PowerPoint (*.pptx; *.ppt)"), { YYCC_U8("*.pptx"), YYCC_U8("*.ppt") });
|
||||
filters.Add(YYCC_U8("Text File (*.txt)"), { YYCC_U8("*.txt") });
|
||||
filters.Add(YYCC_U8("All Files (*.*)"), { YYCC_U8("*.*") });
|
||||
params.SetDefaultFileTypeIndex(0u);
|
||||
\endcode
|
||||
|
||||
\subsection windows__dialog__file_filters__setup File Filters
|
||||
\subsection dialog_helper__file_filters__setup File Filters
|
||||
|
||||
We don't need to initialize FileFilters by ourselves.
|
||||
Oppositely, we fetch it from FileDialog instance by calling FileDialog::configure_file_types().
|
||||
After fetching, we can call FileFilters::add_filter() to add a filter pair for file filters.
|
||||
Oppositely, we fetch it from FileDialog instance by calling FileDialog::ConfigreFileTypes.
|
||||
After fetching, we can call FileFilters::Add to add a filter pair for file filters.
|
||||
|
||||
The first argument is the display text which user will see in file filter drop down box.
|
||||
|
||||
@@ -107,51 +107,55 @@ It is okey to use multiple wildcard string in list.
|
||||
This is suit for those file types involving multiple file extensions, such as the old and new file types of Microsoft Office as we illustracted.
|
||||
Empty list not allowed
|
||||
|
||||
FileFilters::add_filter() throws std::invalid_argument if filter name is blank or filter patterns is empty.
|
||||
Because these errors should be found during developing.
|
||||
FileFilters::Add also will return a bool to indicate the success of this adding.
|
||||
|
||||
It should at least has one file filter in file dialog.
|
||||
I don't know the consequence if you don't provide any file filter.
|
||||
|
||||
\subsection windows__dialog__file_filters__default_filter Default File Type
|
||||
\subsection dialog_helper__file_filters__default_filter Default File Type
|
||||
|
||||
FileDialog::set_default_file_type_index() will set the default selected file filter of this dialog.
|
||||
FileDialog::SetDefaultFileTypeIndex will set the default selected file filter of this dialog.
|
||||
It accepts an index pointing to the file filter which you want to show in default for this file dialog.
|
||||
The index of file filters is the order where you call FileFilters::add_filter() above.
|
||||
The index of file filters is the order where you call FileFilters::Add above.
|
||||
If you pass \c NULL to it or skip calling this function, the first one will be default.
|
||||
|
||||
\section windows__dialog__result Create Dialog and Get Result
|
||||
\section dialog_helper__result Create Dialog and Get Result
|
||||
|
||||
Finally, we can call file dialog functions by we initialized FileDialog
|
||||
|
||||
\code
|
||||
auto result1 = open_file(params);
|
||||
auto result2 = open_files(params);
|
||||
auto result3 = save_file(params);
|
||||
auto result4 = open_folder(params);
|
||||
YYCC::yycc_u8string single_selection;
|
||||
std::vector<YYCC::yycc_u8string> multiple_selection;
|
||||
|
||||
YYCC::DialogHelper::OpenFileDialog(params, single_selection);
|
||||
YYCC::DialogHelper::OpenMultipleFileDialog(params, multiple_selection);
|
||||
YYCC::DialogHelper::SaveFileDialog(params, single_selection);
|
||||
YYCC::DialogHelper::OpenFolderDialog(params, single_selection);
|
||||
\endcode
|
||||
|
||||
There are 4 file dialogs you can choose:
|
||||
|
||||
\li open_file(): Open single file
|
||||
\li open_files(): Open multiple files
|
||||
\li save_file(): Save single file
|
||||
\li open_folder(): Open single directory
|
||||
\li #OpenFileDialog: Open single file
|
||||
\li #OpenMultipleFileDialog: Open multiple files
|
||||
\li #SaveFileDialog: Save single file
|
||||
\li #OpenFolderDialog: Open single directory
|
||||
|
||||
\subsection windows__dialog__result__arguments Arguments
|
||||
\subsection dialog_helper__result__arguments Arguments
|
||||
|
||||
Among these 4 functions, the only argument is the reference to FileDialog.
|
||||
Function will use it to decide what would be shown in this file dialog.
|
||||
Among these 4 functions, the first argument always is the reference to FileDialog.
|
||||
Function will use it to decide what should be shown in this file dialog.
|
||||
|
||||
\subsection windows__dialog__result__return_value Return Value
|
||||
The second argument always is the reference to the container receiving the result.
|
||||
For single selection, the return type is \c yycc_u8string.
|
||||
For multiple selection, the return type is a list of strings: \c std::vector<yycc_u8string>.
|
||||
|
||||
Please note these 4 functions will return a dialog specified result type as their return value.
|
||||
If this result type is an error, it means that an error occurred during execution.
|
||||
Otherwise, there is an optional value inside this result type.
|
||||
If user click Cancel button, this optional value will be empty.
|
||||
otherwise, this optional value will hold user selected a file or directory.
|
||||
\subsection dialog_helper__result__return_value Return Value
|
||||
|
||||
\section windows__dialog__notes Notes
|
||||
Please note among these 4 functions will return a bool as its return value to indicate the success of function.
|
||||
If they return false, it means that the execution of functions are failed or user click Cancel button.
|
||||
In this case, there is no guaranteen to the content of second argument (the real return value).
|
||||
|
||||
\section dialog_helper__notes Notes
|
||||
|
||||
You may notice there are various classes which we never introduce.
|
||||
Because they are intermediate classes and should not be used by programmer.
|
||||
@@ -1,166 +0,0 @@
|
||||
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
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
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
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
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
|
||||
|
||||
*/
|
||||
}
|
||||
148
doc/src/encoding_helper.dox
Normal file
148
doc/src/encoding_helper.dox
Normal file
@@ -0,0 +1,148 @@
|
||||
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.
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -1,37 +1,35 @@
|
||||
namespace yycc::cenum {
|
||||
namespace YYCC::EnumHelper {
|
||||
/**
|
||||
|
||||
\page cenum Scoped Enum Helper
|
||||
\page enum_helper Scoped Enum Helper
|
||||
|
||||
\section cenum__intro Intro
|
||||
\section enum_helper__intro Intro
|
||||
|
||||
C++ introduce a new enum called scoped enum.
|
||||
It is better than legacy C enum because it will not leak name into namespace where it locate,
|
||||
and also can specify an underlying type to it to make sure it is stored as specified size.
|
||||
However, the shortcoming of it is that it lack bitwise operator comparing with legacy C enum.
|
||||
Programmer must implement them for scoped enum one by one but it is a hardship and inconvenient.
|
||||
This is the reason why I invent this class.
|
||||
And this is the reason why I call this module "cenum"
|
||||
because it gives scoped enum type with the same abilities of legacy C enum.
|
||||
Programmer must implement them for scoped enum one by one.
|
||||
It is a hardship and inconvenient.
|
||||
This is the reason why I invent this class
|
||||
|
||||
\section cenum__Usage Usage
|
||||
\section enum_helper__Usage Usage
|
||||
|
||||
In this namespace, we provide all bitwise functions related to scoped enum type which may be used.
|
||||
See yycc::cenum for more detail (It is more clear to read function annotation than I introduce in there repeatedly).
|
||||
See YYCC::EnumHelper for more detail (It is more clear to read function annotation than I introduce in there repeatedly).
|
||||
|
||||
\section cenum__why Why not Operator Overload Way
|
||||
\section enum_helper__why Why not Operator Overload
|
||||
|
||||
I have try it (and you even can see the relic of it in source code).
|
||||
But it need a extra statement written in following to include it, otherwise compiler can not see it.
|
||||
|
||||
\code
|
||||
using namespace yycc::cenum;
|
||||
using namespace YYCC::EnumHelper;
|
||||
\endcode
|
||||
|
||||
The last and most important reason why I do not use this method is that
|
||||
Another reason why I do not use this method is that
|
||||
this overload strategy may be applied to some type which should not be applied by accient, such as non-scoped enum type.
|
||||
So I gave up this solution.
|
||||
It is much better that order user explicitly specify when to use them.
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
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
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -1,30 +1,30 @@
|
||||
namespace yycc::carton::ironpad {
|
||||
namespace YYCC::ExceptionHelper {
|
||||
/**
|
||||
|
||||
\page ironpad Unhandled Exception Handler
|
||||
\page exception_helper Unhandled Exception Handler
|
||||
|
||||
Most Linux users are familiar with using core dump to find bugs.
|
||||
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.
|
||||
This is the reason why I create this module, yycc::carton::ironpad.
|
||||
Most Linux users are familiar with core dump.
|
||||
However core dump 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.
|
||||
YYCC provides this feature in YYCC::ExceptionHelper.
|
||||
|
||||
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.
|
||||
I just want to implement a tiny but worked core dump feature on Windows.
|
||||
|
||||
This module is Windows specific.
|
||||
It still be available on other operating systems but all of its functions are do nothing.
|
||||
It will be invisible on other platforms.
|
||||
|
||||
\section ironpad__usage Usage
|
||||
\section exception_helper__usage Usage
|
||||
|
||||
\subsection ironpad__usage__code Register Code
|
||||
\subsection exception_helper__usage__code Register Code
|
||||
|
||||
In most scenarios, programmer only need call #startup when program started or module loaded.
|
||||
And call #shutdown when program exited or module unloaded.
|
||||
In most scenarios, programmer only need call #Register when program started or module loaded.
|
||||
And call #Unregister when program exited or module unloaded.
|
||||
All details are hidden by these 2 feature.
|
||||
Programmer do not need worried about the implementation of unhandled exception handler.
|
||||
|
||||
Optionally, you can provide a function pointer during calling #startup as a callback.
|
||||
Optionally, you can provide a function pointer during calling #Register as a callback.
|
||||
The prototype of this function pointer is #ExceptionCallback.
|
||||
This callback will be called if any unhandled exception happened.
|
||||
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.
|
||||
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,
|
||||
due to \ref ironpad__notes__singleton design.
|
||||
due to \ref exception_helper__notes__singleton design.
|
||||
|
||||
\subsection ironpad__usage__location Location
|
||||
\subsection exception_helper__usage__location Location
|
||||
|
||||
When unhandled exception occurs,
|
||||
unhandled exception handler will try to record error log and core dump in following path:
|
||||
|
||||
\li Error Log: <TT>\%LOCALAPPDATA\%\\IronPad\\<I>program.exe</I>.<I>pid</I>.log</TT>
|
||||
\li Core Dump: <TT>\%LOCALAPPDATA\%\\IronPad\\<I>program.exe</I>.<I>pid</I>.dmp</TT>
|
||||
\li Error Log: <TT>\%LOCALAPPDATA\%\\CrashDumps\\<I>program.exe</I>.<I>pid</I>.log</TT>
|
||||
\li Core Dump: <TT>\%LOCALAPPDATA\%\\CrashDumps\\<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.
|
||||
Directory <TT>\%LOCALAPPDATA\%\\IronPad</TT> is the dedicated directory for this module.
|
||||
So you may see the generated logs and dumps in it.
|
||||
Directory <TT>\%LOCALAPPDATA\%\\CrashDumps</TT> also is Windows used crash dump directory.
|
||||
So you may see some other core dumps done by Windows in it.
|
||||
|
||||
\subsection ironpad__usage__last_remedy Last Remedy
|
||||
\subsection exception_helper__usage__last_remedy Last Remedy
|
||||
|
||||
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.
|
||||
@@ -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.
|
||||
There is no guaranteen that unhandled exception handler must generate error log and core dump.
|
||||
|
||||
\section ironpad__notes Notes
|
||||
\section exception_helper__notes Notes
|
||||
|
||||
\subsection ironpad__notes__thread_safe Thread Safe
|
||||
\subsection exception_helper__notes__thread_safe Thread Safe
|
||||
|
||||
All exposed functions in this namespace are thread safe.
|
||||
The implementation uses \c std::mutex to ensure this.
|
||||
All exposed functions in YYCC::ExceptionHelper are thread safe.
|
||||
The implementation uses \c std:mutex to ensure this.
|
||||
|
||||
\subsection ironpad__notes__singleton Singleton Handler
|
||||
\subsection exception_helper__notes__singleton Singleton Handler
|
||||
|
||||
This namespace also have a mechanism that make sure the same unhandled exception handler implementation only appear once in the same process.
|
||||
YYCC::ExceptionHelper 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.
|
||||
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.
|
||||
Although both A.exe and B.dll call #startup,
|
||||
Although both A.exe and B.dll call #Register,
|
||||
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.
|
||||
|
||||
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 #startup are loaded.
|
||||
no matter how many modules calling #Register are loaded.
|
||||
Only one error report is enough.
|
||||
|
||||
More precisely, we use \c CreateMutexW to create an unique mutex in Windows global scope,
|
||||
to make sure #startup only run once in the same process.
|
||||
to make sure #Register only run once in the same process.
|
||||
It is very like the implementation of singleton application.
|
||||
|
||||
\subsection ironpad__notes__recursive_calling Recursive Calling
|
||||
\subsection exception_helper__notes__recursive_calling Recursive Calling
|
||||
|
||||
The implementation of unhandled exception handler may also will throw exception.
|
||||
This will cause infinite recursive calling.
|
||||
This namespace has internal mechanism to prevent this bad case.
|
||||
YYCC::ExceptionHelper has internal mechanism to prevent this bad case.
|
||||
If this really happened, the handler will quit silent and will not cause any issue.
|
||||
Programmer don't need to worry about this.
|
||||
|
||||
\subsection ironpad__notes__user_callback The Timing of User Callback
|
||||
\subsection exception_helper__notes__user_callback The Timing of User Callback
|
||||
|
||||
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.
|
||||
@@ -10,7 +10,7 @@
|
||||
<TD><CENTER>
|
||||
<B>YYCCommonplace Programming Manual</B>
|
||||
|
||||
Copyright 2024-2026 by yyc12345.
|
||||
Copyright 2024 by yyc12345.
|
||||
</CENTER></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
@@ -25,80 +25,58 @@
|
||||
<TR>
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">
|
||||
|
||||
<B>Overviews</B>
|
||||
<B>General Features</B>
|
||||
|
||||
\li \subpage intro
|
||||
|
||||
\li \subpage premise_and_principle
|
||||
|
||||
<B>STL Enhancements</B>
|
||||
<!--
|
||||
\li \subpage library_macros
|
||||
|
||||
\li \subpage macro
|
||||
\li \subpage library_encoding
|
||||
|
||||
\li \subpage cenum
|
||||
\li \subpage encoding_helper
|
||||
|
||||
\li \subpage string__reinterpret
|
||||
\li \subpage string_helper
|
||||
|
||||
\li \subpage string__op
|
||||
\li \subpage parser_helper
|
||||
|
||||
\li \subpage num__parser
|
||||
\li \subpage console_helper
|
||||
|
||||
\li \subpage num__op
|
||||
\li \subpage io_helper
|
||||
|
||||
\li \subpage num__safe_cast
|
||||
\li \subpage std_patch
|
||||
|
||||
\li \subpage num__safe_op
|
||||
\li \subpage enum_helper
|
||||
-->
|
||||
|
||||
\li \subpage patch
|
||||
<B>Advanced Features</B>
|
||||
|
||||
\li \subpage env
|
||||
<!--
|
||||
\li \subpage constraints
|
||||
|
||||
\li \subpage rust
|
||||
\li \subpage config_manager
|
||||
|
||||
<B>Text Encoding</B>
|
||||
|
||||
\li \subpage encoding__stl
|
||||
|
||||
\li \subpage encoding__windows
|
||||
|
||||
\li \subpage encoding__iconv
|
||||
|
||||
\li \subpage pycodec
|
||||
\li \subpage arg_parser
|
||||
-->
|
||||
|
||||
</TD>
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">
|
||||
|
||||
<B>Advanced Features (Carton)</B>
|
||||
|
||||
\li \subpage termcolor
|
||||
|
||||
\li \subpage csconsole
|
||||
|
||||
\li \subpage ironpad
|
||||
|
||||
\li \subpage clap
|
||||
|
||||
\li \subpage binstore
|
||||
|
||||
\li \subpage fft
|
||||
|
||||
\li \subpage lexer61
|
||||
|
||||
\li \subpage wcwidth
|
||||
|
||||
\li \subpage tabulate
|
||||
|
||||
<B>Windows Specific Features</B>
|
||||
|
||||
\li \subpage windows__import_guard
|
||||
<!--
|
||||
\li \subpage win_import
|
||||
|
||||
\li \subpage windows__com
|
||||
\li \subpage com_helper
|
||||
|
||||
\li \subpage windows__dialog
|
||||
\li \subpage dialog_helper
|
||||
|
||||
\li \subpage windows__winfct
|
||||
\li \subpage win_fct_helper
|
||||
|
||||
\li \subpage windows__console
|
||||
\li \subpage exception_helper
|
||||
-->
|
||||
|
||||
</TD>
|
||||
</TR>
|
||||
|
||||
50
doc/src/io_helper.dox
Normal file
50
doc/src/io_helper.dox
Normal file
@@ -0,0 +1,50 @@
|
||||
namespace YYCC::IOHelper {
|
||||
/**
|
||||
|
||||
\page io_helper IO Helper
|
||||
|
||||
Actually, YYCC::IOHelper includes functions which can not be placed in other place.
|
||||
|
||||
\section io_helper__ptr_pri_padding Pointer Print Padding
|
||||
|
||||
When printing pointer on screen, programmer usually left-pad zero to make it looks good.
|
||||
However, the count of zero for padding is different in x86 and x64 architecture (8 for x86 and 16 for x64).
|
||||
Macro \c PRI_XPTR_LEFT_PADDING will help you to resolve this issue.
|
||||
|
||||
Macro \c PRI_XPTR_LEFT_PADDING will be defined to following value according to the target system architecture.
|
||||
|
||||
\li \c "08": On x86 system.
|
||||
\li \c "016": On x64 system.
|
||||
|
||||
There is an example for how to use it:
|
||||
|
||||
\code
|
||||
void* raw_ptr = blabla();
|
||||
std::printf(stdout, "Raw Pointer 0x%" PRI_XPTR_LEFT_PADDING PRIXPTR, raw_ptr);
|
||||
\endcode
|
||||
|
||||
Note \c PRIXPTR is defined by standard library for formatting pointer as hexadecimal style.
|
||||
|
||||
\section io_helper__smart_file Smart FILE Pointer
|
||||
|
||||
#SmartStdFile use \c std::unique_ptr with custom deleter to implement smart \c FILE*.
|
||||
It is useful in the cases that you want to automatically free opened file when leaving corresponding scope.
|
||||
|
||||
\section io_helper__utf8_fopen UTF8 fopen
|
||||
|
||||
In Windows, standard \c std::fopen can not handle UTF8 file name in common environment.
|
||||
So we create this function to give programmer an universal \c fopen in UTF8 style.
|
||||
|
||||
In Windows platform, this function will try to convert its argument to \c wchar_t
|
||||
and calling Microsoft specific \c _wfopen function to open file.
|
||||
If encoding convertion or \c _wfopen failed, this function will return \c nullptr like \c std::fopen does.
|
||||
In other platforms, it will simply redirect calling to \c std::fopen.
|
||||
|
||||
There is a simple example:
|
||||
|
||||
\code
|
||||
FILE* fs = YYCC::IOHelper::FOpen(YYCC_U8("/path/to/file"), YYCC_U8("rb"));
|
||||
\endcode
|
||||
|
||||
*/
|
||||
}
|
||||
228
doc/src/library_encoding.dox
Normal file
228
doc/src/library_encoding.dox
Normal file
@@ -0,0 +1,228 @@
|
||||
namespace YYCC {
|
||||
/**
|
||||
|
||||
\page library_encoding Library Encoding
|
||||
|
||||
Before using this library, you should know the encoding strategy of this library first.
|
||||
In short words, this library use UTF8 encoding everywhere except some special cases,
|
||||
for example, function explicitly order the encoding of input parameters.
|
||||
|
||||
In following content of this article, you will know the details about how we use UTF8 in this library.
|
||||
|
||||
\section library_encoding__utf8_type UTF8 Type
|
||||
|
||||
YYCC uses custom UTF8 char type, string container and string view all over the library, from parameters to return value.
|
||||
Following content will introduce how we define them.
|
||||
|
||||
\subsection library_encoding__utf8_type__char_type Char Type
|
||||
|
||||
YYCC library has its own UTF8 char type, \c yycc_char8_t.
|
||||
This is how we define it:
|
||||
|
||||
\code
|
||||
#if defined(__cpp_char8_t)
|
||||
using yycc_char8_t = char8_t;
|
||||
#else
|
||||
using yycc_char8_t = unsigned char;
|
||||
#endif
|
||||
\endcode
|
||||
|
||||
If your environment (higher or equal to C++ 20) supports \c char8_t provided by standard library, \c yycc_char8_t is just an alias to \c char8_t,
|
||||
otherwise (lower than C++ 20, e.g. C++ 17), \c yycc_char8_t will be defined as \c unsigned \c char like C++ 20 does (this can be seen as a polyfill).
|
||||
|
||||
This means that if you already have used \c char8_t provided by standard library,
|
||||
you do not need to do any extra modification before using this library.
|
||||
Because all types are compatible.
|
||||
|
||||
\subsection library_encoding__utf8_type__container_type String Container and View
|
||||
|
||||
We define string container and string view like this:
|
||||
|
||||
\code
|
||||
using yycc_u8string = std::basic_string<yycc_char8_t>;
|
||||
using yycc_u8string_view = std::basic_string_view<yycc_char8_t>;
|
||||
\endcode
|
||||
|
||||
The real code written in library may be slightly different with this but they have same meanings.
|
||||
|
||||
In \c char8_t environment, they are just the alias to \c std::u8string and \c std::u8string_view respectively.
|
||||
So if you have already used them, no need to any modification for your code before using this library.
|
||||
|
||||
\subsection library_encoding__utf8_type__why Why?
|
||||
|
||||
You may curious why I create a new UTF8 char type, rather than using standard library UTF8 char type directly. There are 2 reasons.
|
||||
|
||||
First, It was too late that I notice I can use standard library UTF8 char type.
|
||||
My UTF8 char type has been used in library everywhere and its tough to fully replace them into standard library UTF8 char type.
|
||||
|
||||
Second, UTF8 related content of standard library is \e volatile.
|
||||
I notice standard library change UTF8 related functions frequently and its API are not stable.
|
||||
For example, standard library brings \c std::codecvt_utf8 in C++ 11, deprecate it in C++ 17 and even remove it in C++ 26.
|
||||
That's unacceptable! So I create my own UTF8 type to avoid the scenario that standard library remove \c char8_t in future.
|
||||
|
||||
\section library_encoding__concept Concepts
|
||||
|
||||
In following content, you may be face with 2 words: ordinary string and UTF8 string.
|
||||
|
||||
UTF8 string, as its name, is the string encoded with UTF8.
|
||||
The char type of it must is \c yycc_char8_t.
|
||||
(equivalent to \c char8_t after C++ 20.)
|
||||
|
||||
Ordinary string means the plain, native string.
|
||||
The result of C++ string literal without any prefix \c "foo bar" is a rdinary string.
|
||||
The char type of it is \c char.
|
||||
Its encoding depends on compiler and environment.
|
||||
(UTF8 in Linux, or system code page in Windows if UTF8 switch was not enabled in MSVC.)
|
||||
|
||||
For more infomation, please browse CppReference:
|
||||
https://en.cppreference.com/w/cpp/language/string_literal
|
||||
|
||||
\section library_encoding__utf8_literal UTF8 Literal
|
||||
|
||||
String literal is a C++ concept.
|
||||
If you are not familar with it, please browse related article first, such as CppReference.
|
||||
|
||||
\subsection library_encoding__utf8_literal__single Single Literal
|
||||
|
||||
In short words, YYCC allow you declare an UTF8 literal like this:
|
||||
|
||||
\code
|
||||
YYCC_U8("This is UTF8 literal.")
|
||||
\endcode
|
||||
|
||||
YYCC_U8 is macro.
|
||||
You don't need add extra \c u8 prefix in string given to the macro.
|
||||
This macro will do this automatically.
|
||||
|
||||
In detail, this macro do a \c reinterpret_cast to change the type of given argument to \c const \c yycc_char8_t* forcely.
|
||||
This ensure that declared UTF8 literal is compatible with YYCC UTF8 types.
|
||||
|
||||
\subsection library_encoding__utf8_literal__char Single Char
|
||||
|
||||
Same as UTF8 literal, YYCC allow you cast normal \c char into \c yycc_char8_t as following code:
|
||||
|
||||
\code
|
||||
YYCC_U8_CHAR('A')
|
||||
\endcode
|
||||
|
||||
YYCC_U8_CHAR is a macro.
|
||||
It just simply use \c static_cast to cast given value to \c yycc_char8_t.
|
||||
It doesn't mean that you can cast non-ASCII characters,
|
||||
because the space these characters occupied usually more than the maximum value of \c char.
|
||||
For example, following code is \b invalid:
|
||||
|
||||
\code
|
||||
YYCC_U8_CHAR('文') // INVALID!
|
||||
\endcode
|
||||
|
||||
\subsection library_encoding__utf8_literal__concatenation Literal Concatenation
|
||||
|
||||
YYCC_U8 macro also works for string literal concatenation:
|
||||
|
||||
\code
|
||||
YYCC_U8("Error code: " PRIu32 ". Please contact me.");
|
||||
\endcode
|
||||
|
||||
According to C++ standard for string literal concatenation,
|
||||
<I>"If one of the strings has an encoding prefix and the other does not, the one that does not will be considered to have the same encoding prefix as the other."</I>
|
||||
At the same time, YYCC_U8 macro will automatically add \c u8 prefix for the first component of this string literal concatenation.
|
||||
So the whole string will be UTF8 literal.
|
||||
It also order you should \b not add any prefix for other components of this string literal concatenation.
|
||||
|
||||
\subsection library_encoding__utf8_literal__why Why?
|
||||
|
||||
You may know that C++ standard allows programmer declare an UTF8 literal explicitly by writing code like this:
|
||||
|
||||
\code
|
||||
u8"foo bar"
|
||||
\endcode
|
||||
|
||||
This is okey. But it may incompatible with YYCC UTF8 char type.
|
||||
According to C++ standard, this UTF8 literal syntax will only return \c const \c char8_t* if your C++ standard higher or equal to C++ 20,
|
||||
otherwise it will return \c const \c char*.
|
||||
This behavior cause that you can not assign this UTF8 literal to \c yycc_u8string if you are in the environment which do not support \c char8_t,
|
||||
because their types are different.
|
||||
Thereas you can not use the functions provided by this library because they are all use YYCC defined UTF8 char type.
|
||||
|
||||
\section library_encoding__utf8_pointer UTF8 String Pointer
|
||||
|
||||
String pointer means the raw pointer pointing to a string, such as \c const \c char*, \c char*, \c char32_t* and etc.
|
||||
|
||||
Many legacy code assume \c char* is encoded with UTF8 (the exception is Windows). But \c char* is incompatible with \c yycc_char8_t.
|
||||
YYCC provides YYCC::EncodingHelper::ToUTF8 to resolve this issue. There is an exmaple:
|
||||
|
||||
\code
|
||||
const char* absolutely_is_utf8 = "I confirm this is encoded with UTF8.";
|
||||
const yycc_char8_t* converted = YYCC::EncodingHelper::ToUTF8(absolutely_is_utf8);
|
||||
|
||||
char* mutable_utf8 = const_cast<char*>(absolutely_is_utf8); // This is not safe. Just for example.
|
||||
yycc_char8_t* mutable_converted = YYCC::EncodingHelper::ToUTF8(mutable_utf8);
|
||||
\endcode
|
||||
|
||||
YYCC::EncodingHelper::ToUTF8 has 2 overloads which can handle constant and mutable stirng pointer convertion respectively.
|
||||
|
||||
YYCC also has ability that convert YYCC UTF8 char type to ordinary char type by YYCC::EncodingHelper::ToOrdinary.
|
||||
Here is an exmaple:
|
||||
|
||||
\code
|
||||
const yycc_char8_t* yycc_utf8 = YYCC_U8("I am UTF8 string.");
|
||||
const char* converted = YYCC::EncodingHelper::ToOrdinary(yycc_utf8);
|
||||
|
||||
yycc_char8_t* mutable_yycc_utf8 = const_cast<char*>(yycc_utf8); // Not safe. Also just for example.
|
||||
char* mutable_converted = YYCC::EncodingHelper::ToOrdinary(mutable_yycc_utf8);
|
||||
\endcode
|
||||
|
||||
Same as YYCC::EncodingHelper::ToUTF8, YYCC::EncodingHelper::ToOrdinary also has 2 overloads to handle constant and mutable string pointer.
|
||||
|
||||
\section library_encoding__utf8_container UTF8 String Container
|
||||
|
||||
String container usually means the standard library string container, such as \c std::string, \c std::wstring, \c std::u32string and etc.
|
||||
|
||||
In many personal project, programmer may use \c std::string everywhere because \c std::u8string may not be presented when writing peoject.
|
||||
How to do convertion between ordinary string container and YYCC UTF8 string container?
|
||||
It is definitely illegal that directly do force convertion. Because they may have different class layout.
|
||||
Calm down and I will tell you how to do correct convertion.
|
||||
YYCC provides YYCC::EncodingHelper::ToUTF8 to convert ordinary string container to YYCC UTF8 string container.
|
||||
There is an exmaple:
|
||||
|
||||
\code
|
||||
std::string ordinary_string("I am UTF8");
|
||||
yycc_u8string yycc_string = YYCC::EncodingHelper::ToUTF8(ordinary_string);
|
||||
auto result = YYCC::EncodingHelper::UTF8ToUTF32(yycc_string);
|
||||
\endcode
|
||||
|
||||
Actually, YYCC::EncodingHelper::ToUTF8 accepts a reference to \c std::string_view as argument.
|
||||
However, there is a implicit convertion from \c std::string to \c std::string_view,
|
||||
so you can directly pass a \c std::string instance to it.
|
||||
|
||||
String view will reduce unnecessary memory copy.
|
||||
If you just want to pass ordinary string container to function, and this function accepts \c yycc_u8string_view as its argument,
|
||||
you can use alternative YYCC::EncodingHelper::ToUTF8View.
|
||||
|
||||
\code
|
||||
std::string ordinary_string("I am UTF8");
|
||||
yycc_u8string_view yycc_string = YYCC::EncodingHelper::ToUTF8View(ordinary_string);
|
||||
auto result = YYCC::EncodingHelper::UTF8ToUTF32(yycc_string);
|
||||
\endcode
|
||||
|
||||
Comparing with previous one, this example use less memory.
|
||||
The reduced memory is the content of \c yycc_string because string view is a view, not the copy of original string.
|
||||
|
||||
Same as UTF8 string pointer, we also have YYCC::EncodingHelper::ToOrdinary and YYCC::EncodingHelper::ToOrdinaryView do correspondant reverse convertion.
|
||||
Try to do your own research and figure out how to use them.
|
||||
It's pretty easy.
|
||||
|
||||
\section library_encoding__windows Warnings to Windows Programmer
|
||||
|
||||
Due to the legacy of MSVC, the encoding of \c char* may not be UTF8 in most cases.
|
||||
If you run the convertion code introduced in this article with the string which is not encoded with UTF8, it may cause undefined behavior.
|
||||
|
||||
To enable UTF8 mode of MSVC, please deliver \c /utf-8 switch to MSVC.
|
||||
Thus you can use the functions introduced in this article safely.
|
||||
Otherwise, you must guarteen that the argument you provided to these functions is encoded by UTF8 manually.
|
||||
|
||||
Linux user do not need care this.
|
||||
Because almost Linux distro use UTF8 in default.
|
||||
|
||||
*/
|
||||
}
|
||||
122
doc/src/library_macros.dox
Normal file
122
doc/src/library_macros.dox
Normal file
@@ -0,0 +1,122 @@
|
||||
namespace YYCC {
|
||||
/**
|
||||
|
||||
\page library_macros Library Macros
|
||||
|
||||
In this page we will introduce the macros defined by this library
|
||||
which can not be grouped in other topic.
|
||||
|
||||
\section library_macros__batch_class_copy_move Library Version and Version Comparison
|
||||
|
||||
Version is a important things in modern software development, especially for a library.
|
||||
In YYCC, we use Semantic Versioning as our version standard.
|
||||
For more infomations about it, please see: https://semver.org/
|
||||
|
||||
First, YYCC has its own version and it can be visited by
|
||||
\c YYCC_VER_MAJOR, \c YYCC_VER_MINOR, and \c YYCC_VER_PATCH.
|
||||
Each part of Semantic Versioning is provided individually.
|
||||
|
||||
YYCC also provide a bunch of macros to compare 2 versions.
|
||||
It also provides a way to check YYCC version in program using YYCC,
|
||||
because some of them rely on a specific version of YYCC.
|
||||
There is a list of these comparison macros.
|
||||
|
||||
\li YYCC_VERCMP_E
|
||||
\li YYCC_VERCMP_NE
|
||||
\li YYCC_VERCMP_G
|
||||
\li YYCC_VERCMP_GE
|
||||
\li YYCC_VERCMP_NL
|
||||
\li YYCC_VERCMP_L
|
||||
\li YYCC_VERCMP_LE
|
||||
\li YYCC_VERCMP_NG
|
||||
|
||||
You may notice all of these macros are starts with \c YYCC_VERCMP_,
|
||||
and their tails are inspired from x86 ASM comparison jump code.
|
||||
For example, \c E means "equal" and \c NE means "not equal",
|
||||
\c G means "greater", \c GE means "greater or equal", and \c NG means "not gretaer".
|
||||
|
||||
All of these macros take 6 arguments,
|
||||
for the first 3 arguments, we call them "left version".
|
||||
From left to right they are the major part, minor part and patch part of semantic version.
|
||||
And for the last 3 arguments, we call them "right version".
|
||||
From left to right they are the major part, minor part and patch part of semantic version.
|
||||
There is a example about checking whether YYCC library version is exactly what we wanted version.
|
||||
|
||||
\code
|
||||
#if YYCC_VERCMP_NE(YYCC_VER_MAJOR, YYCC_VER_MINOR, YYCC_VER_PATCH, 1, 3 ,0)
|
||||
#error "Not Matched YYCC Version"
|
||||
#endif
|
||||
\endcode
|
||||
|
||||
\section library_macros__platform_checker Platform Checker
|
||||
|
||||
In many cross platform applications,
|
||||
programmer usually write code adapted to different platforms in one source file
|
||||
and enable them respectively by macros representing the target platform.
|
||||
As a cross platform library,
|
||||
YYCC also has this feature and you can utilize it if you don't have other ways to so the same things.
|
||||
|
||||
\subsection library_macros__platform_checker__values Values
|
||||
|
||||
YYCC always define a macro called \c YYCC_OS to indicate the system of target platform.
|
||||
In implementation, it will check following list from top to bottom to set matched value for it.
|
||||
|
||||
\li \c YYCC_OS_WINDOWS: Windows environment. It is done by checking whether environment define \c _WIN32 macro.
|
||||
\li \c YYCC_OS_LINUX: In current implementation, this means target platform is \b NOT Windows.
|
||||
|
||||
\subsection library_macros__platform_checker__usage Usage
|
||||
|
||||
Now you know any possible value of \c YYCC_OS.
|
||||
The next step is how to use it to enable specified code in specific target platform.
|
||||
We take Windows platform for example.
|
||||
Assume \c blabla() function is Windows specific.
|
||||
We have following example code:
|
||||
|
||||
\code
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
blabla();
|
||||
#endif
|
||||
\endcode
|
||||
|
||||
It's enough and simple that use \c \#if to bracket the Windows specified code.
|
||||
|
||||
\section library_macros__batch_class_copy_move Batch Class Copy / Move Functions
|
||||
|
||||
YYCC provides 6 macros to batchly remove class copy constructor and move constructor,
|
||||
or set default class copy constructor and move constructor.
|
||||
|
||||
<UL>
|
||||
<LI>
|
||||
\c YYCC_DEL_CLS_COPY: Declare following 2 statements which delete copy constrcutor and copy assign operator.
|
||||
<UL>
|
||||
<LI><TT>CLSNAME(const CLSNAME&) = delete;</TT></LI>
|
||||
<LI><TT>CLSNAME& operator=(const CLSNAME&) = delete;</TT></LI>
|
||||
</UL>
|
||||
</LI>
|
||||
<LI>
|
||||
\c YYCC_DEL_CLS_MOVE: Declare following 2 statements which delete move constrcutor and move assign operator.
|
||||
<UL>
|
||||
<LI><TT>CLSNAME(CLSNAME&&) = delete;</TT></LI>
|
||||
<LI><TT>CLSNAME& operator=(CLSNAME&&) = delete;</TT></LI>
|
||||
</UL>
|
||||
</LI>
|
||||
<LI>\c YYCC_DEL_CLS_COPY_MOVE: The combination of \c YYCC_DEL_CLS_COPY and \c YYCC_DEL_CLS_MOVE.</LI>
|
||||
<LI>
|
||||
\c YYCC_DEF_CLS_COPY: Declare following 2 statements which set default copy constrcutor and copy assign operator.
|
||||
<UL>
|
||||
<LI><TT>CLSNAME(const CLSNAME&) = default;</TT></LI>
|
||||
<LI><TT>CLSNAME& operator=(const CLSNAME&) = default;</TT></LI>
|
||||
</UL>
|
||||
</LI>
|
||||
<LI>
|
||||
\c YYCC_DEF_CLS_MOVE: Declare following 2 statements which set default move constrcutor and move assign operator.
|
||||
<UL>
|
||||
<LI><TT>CLSNAME(CLSNAME&&) = default;</TT></LI>
|
||||
<LI><TT>CLSNAME& operator=(CLSNAME&&) = default;</TT></LI>
|
||||
</UL>
|
||||
</LI>
|
||||
<LI>\c YYCC_DEF_CLS_COPY_MOVE: The combination of \c YYCC_DEF_CLS_COPY and \c YYCC_DEF_CLS_MOVE.</LI>
|
||||
</UL>
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -1,323 +0,0 @@
|
||||
namespace yycc::macro {
|
||||
/**
|
||||
|
||||
\page macro Library Macros
|
||||
|
||||
In this page we will introduce the macros defined by this library
|
||||
which can not be grouped in other topic.
|
||||
|
||||
\section macro__version Library Version
|
||||
|
||||
Version is a important things in modern software development, especially for a library.
|
||||
In YYCC, we use Semantic Versioning as our version standard.
|
||||
For more infomations about it, please see: https://semver.org/
|
||||
|
||||
First, YYCC has its own version and it can be visited by
|
||||
\c YYCC_VER_MAJOR, \c YYCC_VER_MINOR, and \c YYCC_VER_PATCH.
|
||||
Each part of Semantic Versioning is provided individually.
|
||||
|
||||
\section macro__version_cmp Version Comparison
|
||||
|
||||
YYCC also provide a bunch of macros to compare 2 versions.
|
||||
It also provides a way to check YYCC version in program using YYCC,
|
||||
because some of them rely on a specific version of YYCC.
|
||||
There is a list of these comparison macros.
|
||||
|
||||
\li YYCC_VERCMP_E
|
||||
\li YYCC_VERCMP_NE
|
||||
\li YYCC_VERCMP_G
|
||||
\li YYCC_VERCMP_GE
|
||||
\li YYCC_VERCMP_NL
|
||||
\li YYCC_VERCMP_L
|
||||
\li YYCC_VERCMP_LE
|
||||
\li YYCC_VERCMP_NG
|
||||
|
||||
You may notice all of these macros are all start with \c YYCC_VERCMP_,
|
||||
and their tails are inspired from x86 ASM comparison jump code.
|
||||
For example, \c E means "equal" and \c NE means "not equal",
|
||||
\c G means "greater", \c GE means "greater or equal", and \c NG means "not gretaer".
|
||||
|
||||
All of these macros take 6 arguments,
|
||||
for the first 3 arguments, we call them "left version".
|
||||
From left to right they are the major part, minor part and patch part of semantic version.
|
||||
And for the last 3 arguments, we call them "right version".
|
||||
From left to right they are the major part, minor part and patch part of semantic version.
|
||||
There is a example about checking whether YYCC library version is exactly what we wanted version.
|
||||
|
||||
\code
|
||||
#if YYCC_VERCMP_NE(YYCC_VER_MAJOR, YYCC_VER_MINOR, YYCC_VER_PATCH, 1, 3 ,0)
|
||||
#error "Not Matched YYCC Version"
|
||||
#endif
|
||||
\endcode
|
||||
|
||||
\section macro__copy_move Class Copy / Move Functions
|
||||
|
||||
YYCC provides several macros to manage copy and move constructors and assignment operators for classes.
|
||||
These include macros to delete, default, declare, and implement copy and move operations.
|
||||
|
||||
<UL>
|
||||
<LI>
|
||||
\c YYCC_DELETE_COPY(CLSNAME): Explicitly remove copy constructor and copy assignment operator for the given class.
|
||||
<UL>
|
||||
<LI><TT>CLSNAME(const CLSNAME&) = delete;</TT></LI>
|
||||
<LI><TT>CLSNAME& operator=(const CLSNAME&) = delete;</TT></LI>
|
||||
</UL>
|
||||
</LI>
|
||||
<LI>
|
||||
\c YYCC_DELETE_MOVE(CLSNAME): Explicitly remove move constructor and move assignment operator for the given class.
|
||||
<UL>
|
||||
<LI><TT>CLSNAME(CLSNAME&&) noexcept = delete;</TT></LI>
|
||||
<LI><TT>CLSNAME& operator=(CLSNAME&&) noexcept = delete;</TT></LI>
|
||||
</UL>
|
||||
</LI>
|
||||
<LI>\c YYCC_DELETE_COPY_MOVE(CLSNAME): The combination of \c YYCC_DELETE_COPY and \c YYCC_DELETE_MOVE.</LI>
|
||||
<LI>
|
||||
\c YYCC_DEFAULT_COPY(CLSNAME): Explicitly set default copy constructor and copy assignment operator for the given class.
|
||||
<UL>
|
||||
<LI><TT>CLSNAME(const CLSNAME&) = default;</TT></LI>
|
||||
<LI><TT>CLSNAME& operator=(const CLSNAME&) = default;</TT></LI>
|
||||
</UL>
|
||||
</LI>
|
||||
<LI>
|
||||
\c YYCC_DEFAULT_MOVE(CLSNAME): Explicitly set default move constructor and move assignment operator for the given class.
|
||||
<UL>
|
||||
<LI><TT>CLSNAME(CLSNAME&&) noexcept = default;</TT></LI>
|
||||
<LI><TT>CLSNAME& operator=(CLSNAME&&) noexcept = default;</TT></LI>
|
||||
</UL>
|
||||
</LI>
|
||||
<LI>\c YYCC_DEFAULT_COPY_MOVE(CLSNAME): The combination of \c YYCC_DEFAULT_COPY and \c YYCC_DEFAULT_MOVE.</LI>
|
||||
<LI>
|
||||
\c YYCC_DECL_COPY(CLSNAME): Make declaration of copy constructor and assignment operator for the given class to avoid typos.
|
||||
<UL>
|
||||
<LI><TT>CLSNAME(const CLSNAME&);</TT></LI>
|
||||
<LI><TT>CLSNAME& operator=(const CLSNAME&);</TT></LI>
|
||||
</UL>
|
||||
</LI>
|
||||
<LI>
|
||||
\c YYCC_DECL_MOVE(CLSNAME): Make declaration of move constructor and assignment operator for the given class to avoid typos.
|
||||
<UL>
|
||||
<LI><TT>CLSNAME(CLSNAME&&) noexcept;</TT></LI>
|
||||
<LI><TT>CLSNAME& operator=(CLSNAME&&) noexcept;</TT></LI>
|
||||
</UL>
|
||||
</LI>
|
||||
<LI>\c YYCC_DECL_COPY_MOVE(CLSNAME): The combination of \c YYCC_DECL_COPY and \c YYCC_DECL_MOVE.</LI>
|
||||
<LI>\c YYCC_IMPL_COPY_CTOR(CLSNAME, RHS): Make implementation signature of copy constructor for the given class with the right operand name to avoid typos.</LI>
|
||||
<LI>\c YYCC_IMPL_COPY_OPER(CLSNAME, RHS): Make implementation signature of copy assignment operator for the given class with the right operand name to avoid typos.</LI>
|
||||
<LI>\c YYCC_IMPL_MOVE_CTOR(CLSNAME, RHS): Make implementation signature of move constructor for the given class with the right operand name to avoid typos.</LI>
|
||||
<LI>\c YYCC_IMPL_MOVE_OPER(CLSNAME, RHS): Make implementation signature of move assignment operator for the given class with the right operand name to avoid typos.</LI>
|
||||
</UL>
|
||||
|
||||
Please note that \c YYCC_DECL_ and \c YYCC_IMPL_ should be used together.
|
||||
These macros are designed to make sure that you write correct function signatures.
|
||||
There is an example about how to use it.
|
||||
In HPP file, you can write:
|
||||
|
||||
\code
|
||||
class Foo {
|
||||
YYCC_DECL_COPY_MOVE(Foo)
|
||||
};
|
||||
\endcode
|
||||
|
||||
And in corresponding CPP file, you should write:
|
||||
|
||||
\code
|
||||
YYCC_IMPL_COPY_CTOR(Foo, rhs)
|
||||
{
|
||||
// Copy members from rhs
|
||||
}
|
||||
YYCC_IMPL_COPY_OPER(Foo, rhs)
|
||||
{
|
||||
// Copy members from rhs
|
||||
return *this;
|
||||
}
|
||||
YYCC_IMPL_MOVE_CTOR(Foo, rhs)
|
||||
{
|
||||
// Move members from rhs
|
||||
}
|
||||
YYCC_IMPL_MOVE_OPER(Foo, rhs)
|
||||
{
|
||||
// Move members from rhs
|
||||
return *this;
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section macro__platform_checker OS Detector
|
||||
|
||||
In many cross platform applications,
|
||||
programmer usually write code adapted to different platforms in one source file
|
||||
and enable them respectively by macros representing the target platform.
|
||||
As a cross platform library,
|
||||
YYCC also has this feature and you can utilize it if you don't have other ways to so the same things.
|
||||
|
||||
\subsection macro__platform_checker__macro Macro
|
||||
|
||||
YYCC always define <B>one of following macros</B> to indicate the system of target platform.
|
||||
|
||||
\li \c YYCC_OS_WINDOWS: Windows environment.
|
||||
\li \c YYCC_OS_LINUX: Linux environment.
|
||||
\li \c YYCC_OS_MACOS: macOS environment.
|
||||
|
||||
Assume \c blabla() function is Windows specific.
|
||||
There is an example about how to use it:
|
||||
|
||||
\code
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
// Code specific to Windows
|
||||
blabla();
|
||||
#endif
|
||||
\endcode
|
||||
|
||||
\subsection macro__platform_checker__constexpr_function Constexpr Function
|
||||
|
||||
Additionally, YYCC also provides a bunch of constexpr functions to check whether the target platform is what we want.
|
||||
More precisely, os::get_os() function returns an enum value os::OsKind indicating the target platform.
|
||||
|
||||
There is an example about how to use it:
|
||||
|
||||
\code
|
||||
if constexpr (os::get_os() == os::OsKind::Windows) {
|
||||
// Code specific to Windows
|
||||
blabla();
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section macro__compiler_detector Compiler Detector
|
||||
|
||||
YYCC provides macros and constexpr functions to detect the compiler being used for compilation.
|
||||
|
||||
\subsection macro__compiler_detector__macro Macro
|
||||
|
||||
YYCC defines <B>one of following macros</B> to indicate which compiler is being used.
|
||||
|
||||
\li \c YYCC_CC_MSVC: MSVC compiler (Microsoft Visual C++)
|
||||
\li \c YYCC_CC_GCC: GCC compiler (GNU Compiler Collection)
|
||||
\li \c YYCC_CC_CLANG: Clang compiler
|
||||
|
||||
There is an example about how to use it:
|
||||
|
||||
\code
|
||||
#if defined(YYCC_CC_MSVC)
|
||||
// Code specific to MSVC
|
||||
blabla();
|
||||
#endif
|
||||
\endcode
|
||||
|
||||
\subsection macro__compiler_detector__constexpr_function Constexpr Function
|
||||
|
||||
YYCC also provides a constexpr function to check which compiler is being used at compile time.
|
||||
More precisely, compiler::get_compiler() function returns an enum value compiler::CompilerKind indicating the compiler being used.
|
||||
|
||||
There is an example about how to use it:
|
||||
|
||||
\code
|
||||
if constexpr (compiler::get_compiler() == compiler::CompilerKind::Msvc) {
|
||||
// Code specific to MSVC
|
||||
blabla();
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section macro__endian_detector Endian Detector
|
||||
|
||||
YYCC provides macros and constexpr functions to detect the endianness of the target platform.
|
||||
|
||||
\subsection macro__endian_detector__macro Macro
|
||||
|
||||
YYCC always defines <B>one of following macros</B> to indicate the endianness of the target platform.
|
||||
|
||||
\li \c YYCC_ENDIAN_LITTLE: Little endian system
|
||||
\li \c YYCC_ENDIAN_BIG: Big endian system
|
||||
|
||||
There is an example about how to use it:
|
||||
|
||||
\code
|
||||
#if defined(YYCC_ENDIAN_LITTLE)
|
||||
// Code specific to little endian systems
|
||||
blabla();
|
||||
#endif
|
||||
\endcode
|
||||
|
||||
\subsection macro__endian_detector__constexpr_function Constexpr Function
|
||||
|
||||
YYCC also provides a constexpr function to check the endianness of the target platform.
|
||||
More precisely, endian::get_endian() function returns an enum value endian::EndianKind indicating the endianness.
|
||||
|
||||
There is an example about how to use it:
|
||||
|
||||
\code
|
||||
if constexpr (endian::get_endian() == endian::EndianKind::Little) {
|
||||
// Code specific to little endian systems
|
||||
blabla();
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section macro__stl_detector STL Detector
|
||||
|
||||
YYCC provides macros to detect which Standard Template Library (STL) implementation is being used.
|
||||
|
||||
\subsection macro__stl_detector__macro Macro
|
||||
|
||||
YYCC defines <B>one of following macros</B> to indicate which STL implementation is being used.
|
||||
|
||||
\li \c YYCC_STL_MSSTL: Microsoft STL
|
||||
\li \c YYCC_STL_GNUSTL: GNU STL
|
||||
\li \c YYCC_STL_CLANGSTL: Clang STL
|
||||
|
||||
There is an example about how to use it:
|
||||
|
||||
\code
|
||||
#if defined(YYCC_STL_MSSTL)
|
||||
// Code specific to Microsoft STL
|
||||
blabla();
|
||||
#endif
|
||||
\endcode
|
||||
|
||||
\subsection macro__stl_detector__constexpr_function Constexpr Function
|
||||
|
||||
YYCC also provides a constexpr function to check which STL implementation is being used at compile time.
|
||||
More precisely, stl::get_stl() function returns an enum value stl::StlKind indicating the STL implementation.
|
||||
|
||||
There is an example about how to use it:
|
||||
|
||||
\code
|
||||
if constexpr (stl::get_stl() == stl::StlKind::MsStl) {
|
||||
// Code specific to Microsoft STL
|
||||
blabla();
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section macro__ptr_size_detector Pointer Size Detector
|
||||
|
||||
YYCC provides macros and constexpr functions to detect the pointer size of the target platform.
|
||||
|
||||
\subsection macro__ptr_size_detector__macro Macro
|
||||
|
||||
YYCC always define <B>one of following macros</B> to indicate the pointer size of target platform.
|
||||
|
||||
\li \c YYCC_PTRSIZE_32: 32-bit environment
|
||||
\li \c YYCC_PTRSIZE_64: 64-bit environment
|
||||
|
||||
There is an example about how to use it:
|
||||
|
||||
\code
|
||||
#if defined(YYCC_PTRSIZE_32)
|
||||
// Code specific to 32-bit environment
|
||||
blabla();
|
||||
#endif
|
||||
\endcode
|
||||
|
||||
\subsection macro__ptr_size_detector__constexpr_function Constexpr Function
|
||||
|
||||
YYCC also provides a constexpr function to check the pointer size of the target platform.
|
||||
More precisely, ptr_size::get_ptr_size() function returns an enum value ptr_size::PtrSizeKind indicating the pointer size.
|
||||
|
||||
There is an example about how to use it:
|
||||
|
||||
\code
|
||||
if constexpr (ptr_size::get_ptr_size() == ptr_size::PtrSizeKind::Bits32) {
|
||||
// Code specific to 32-bit environment
|
||||
blabla();
|
||||
}
|
||||
\endcode
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
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
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
namespace yycc::num::parse {
|
||||
/**
|
||||
|
||||
\page num__parser Numeric Parser
|
||||
|
||||
Namespace yycc::num::parse is served for the convertion from string to number.
|
||||
|
||||
\section num__parser__supported_types Supported Types
|
||||
|
||||
Functions located in this namespace support the convertion from string to following types:
|
||||
|
||||
\li Integral types (except \c bool): \c int, \c uint32_t, \c char and etc.
|
||||
\li Floating point types: \c float, \c double and etc.
|
||||
\li \c bool
|
||||
|
||||
Please note in C++, \c bool is integral type but we list it individually because parser will treat it specially.
|
||||
For \c bool type, parser will try doing convertion between it and \c "true" \c "false" string.
|
||||
(\b case-insensitive. It means that \c "true", \c "True" and \c "TRUE", all of them can be converted into \c true.)
|
||||
|
||||
\section num__parser__usage Usage
|
||||
|
||||
This namespace provide a uniform parser function #parse with various overloads.
|
||||
All of them accept an UTF8 string view at first argument,
|
||||
and following argument is different required by different overloads which may change parser behavior.
|
||||
For example, for floating point type, this function allows caller to specify extra argument providing the format of given number string (\c std::chars_format).
|
||||
or for integral type, this function allows caller to specify extra argument providing the base of given number string.
|
||||
The return value is a result type, containing converted value or error occurs.
|
||||
There are some examples:
|
||||
|
||||
\code
|
||||
auto rv = parse::parse<uint32_t>(u8"123");
|
||||
assert(rv.has_value());
|
||||
auto converted = rv.value();
|
||||
\endcode
|
||||
|
||||
\section num__parser__stringify Stringify
|
||||
|
||||
Namespace yycc::num::stringify provide the opposite function of namespace yycc::num::parse.
|
||||
They convert given number into their string representation.
|
||||
There is an example:
|
||||
|
||||
\code
|
||||
auto showcase = stringify::stringify<uint32_t>(UINT32_C(114));
|
||||
\endcode
|
||||
|
||||
Same as parse::parse, stringify::stringify also has same overloads and different second arguments.
|
||||
For floating point type, this function allows caller to specify extra arguments
|
||||
which provides the format (\c std::chars_format) and precision when getting string representation.
|
||||
For integral type, this function allows caller to specify extra argument
|
||||
providing the base of number when getting string representation.
|
||||
However, the result value of stringify::stringify is just the result, not a result type.
|
||||
Because it is mostly impossible to occur error in stringify::stringify.
|
||||
|
||||
\section num__parser__notes Notes
|
||||
|
||||
All functions located in yycc::num::parse and yycc::num::stringify namespace are implementated by standard library functions.
|
||||
These functions just make a good wrapper for complex standard library functions.
|
||||
And give you a experience like Rust \c parse functions.
|
||||
|
||||
Basically, all functions located in this helper have possibility to throw exception.
|
||||
But this possibility are more close to the possibility that \c new statement throw \c std::bad_alloc.
|
||||
So in most cases you can assume these functions will not throw any exception.
|
||||
|
||||
All functions are template functions.
|
||||
The argument of template is the type these functions need to be processed.
|
||||
Although C++ have \e smart template type deduction,
|
||||
it would be better to specify template argument manually to explicitly specify your desired type.
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
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
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
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.
|
||||
|
||||
*/
|
||||
}
|
||||
88
doc/src/parser_helper.dox
Normal file
88
doc/src/parser_helper.dox
Normal file
@@ -0,0 +1,88 @@
|
||||
namespace YYCC::ParserHelper {
|
||||
/**
|
||||
|
||||
\page parser_helper Parser Helper
|
||||
|
||||
This helper is served for the convertion between number and string.
|
||||
|
||||
\section parser_helper_supported_types Supported Types
|
||||
|
||||
Functions located in this helper support the convertion between string and following types:
|
||||
|
||||
\li Integral types (except \c bool): \c int, \c uint32_t, \c char and etc.
|
||||
\li Floating point types: \c float, \c double and etc.
|
||||
\li \c bool
|
||||
|
||||
Please note in C++, \c bool is integral type but we list it individually because parser will treat it specially.
|
||||
For \c bool type, parser will try doing convertion between it and \c "true" \c "false" string.
|
||||
(\b case-insensitive. It means that \c true can be converted from \c "true", \c "True" or \c "TRUE".)
|
||||
|
||||
\section parser_helper__try_parse Try Parse
|
||||
|
||||
#TryParse will try to parse string into caller specified type.
|
||||
All of them accept an UTF8 string view at first argument,
|
||||
require that you provide a container receiving converted result in the second argument,
|
||||
and return a bool value to indicate whether the convertion is successful.
|
||||
There are some examples:
|
||||
|
||||
\code
|
||||
uint32_t val;
|
||||
YYCC::ParserHelper::TryParse<uint32_t>(YYCC_U8("123"), val);
|
||||
YYCC::ParserHelper::TryParse<uint32_t>(YYCC_U8("7fff"), val, 16);
|
||||
\endcode
|
||||
|
||||
For floating point type, this function allows caller to specify extra argument providing the format of given number string (\c std::chars_format).
|
||||
For integral type, this function allows caller to specify extra argument providing the base of given number string.
|
||||
|
||||
\section parser_helper__parse Parse
|
||||
|
||||
#Parse is similar to #TryParse.
|
||||
But it will not return bool value to indicate success and doesn't have the argument receiving result.
|
||||
It only accepts an UTF8 string view as the only one argument, and return result directly.
|
||||
If the convertion failed, the return value is \b undefined (but usually is the default value of given type).
|
||||
There is an example:
|
||||
|
||||
\code
|
||||
uint32_t val = YYCC::ParserHelper::Parse<uint32_t>(YYCC_U8("123"));
|
||||
\endcode
|
||||
|
||||
For integral and floating point value,
|
||||
it has same extra argument with #TryParse to provide more number infomation.
|
||||
|
||||
Using this function is dangerous if the validation of your input is important.
|
||||
In this case, please use #TryParse instead.
|
||||
|
||||
\section parser_helper__to_string To String
|
||||
|
||||
#ToString basically is the reversed operation of #Parse.
|
||||
It gets the string representation of given type.
|
||||
The only argument of these functions is the type which need to be converted to its string representation.
|
||||
And they will return yycc_u8string as result.
|
||||
There is an example:
|
||||
|
||||
\code
|
||||
auto result = YYCC::ParserHelper::ToString<uint32_t>(UINT32_C(114));
|
||||
\endcode
|
||||
|
||||
For floating point type, this function allows caller to specify extra arguments
|
||||
which provides the format (\c std::chars_format) and precision when getting string representation.
|
||||
For integral type, this function allows caller to specify extra argument
|
||||
providing the base of number when getting string representation.
|
||||
|
||||
\section parser_helper__notes Notes
|
||||
|
||||
All functions within this helper are implementated by standard library functions.
|
||||
These functions just make a good wrapper for complex standard library functions.
|
||||
And give you a experience like C\# parser functions.
|
||||
|
||||
Basically, all functions located in this helper have possibility to throw exception.
|
||||
But this possibility are more close to the possibility that \c new statement throw \c std::bad_alloc.
|
||||
So in most cases you can assume these functions will not throw any exception.
|
||||
|
||||
All functions are template functions.
|
||||
The argument of template is the type these functions need to be processed.
|
||||
Although C++ have \e smart template type deduction,
|
||||
it would be better to specify template argument manually to explicitly specify your desired type.
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
namespace yycc::patch {
|
||||
/**
|
||||
|
||||
\page patch Other STL Patches
|
||||
|
||||
There are some other STL patches in this library which can not be organized in single document file individually.
|
||||
So I put them together here.
|
||||
|
||||
\section patch__ptr_pad Pointer Print Padding
|
||||
|
||||
When printing pointer on screen, programmer usually left-pad zero to make it looks good.
|
||||
However, the count of zero for padding is different in x86 and x64 architecture (8 for x86 and 16 for x64).
|
||||
Macro \c PRIXPTR_LPAD will help you to resolve this issue.
|
||||
|
||||
Macro \c PRIXPTR_LPAD will be expended to one of following value according to the target system architecture.
|
||||
|
||||
\li \c "08": On x86 system.
|
||||
\li \c "016": On x64 system.
|
||||
|
||||
There is an example for how to use it:
|
||||
|
||||
\code
|
||||
void* raw_ptr = blabla();
|
||||
std::printf(stdout, "Raw Pointer 0x%" PRIXPTR_LPAD PRIXPTR, raw_ptr);
|
||||
\endcode
|
||||
|
||||
Note \c PRIXPTR is defined by standard library for formatting pointer as hexadecimal style.
|
||||
|
||||
\section patch__smart_file Smart FILE Pointer
|
||||
|
||||
fopen::SmartStdFile use \c std::unique_ptr with custom deleter to implement smart \c FILE*.
|
||||
It is useful in the cases that you want to automatically free opened file when leaving corresponding scope.
|
||||
|
||||
\section patch__utf8_fopen UTF8 fopen
|
||||
|
||||
In Windows, standard \c std::fopen can not handle UTF8 file name in common environment.
|
||||
So we create fopen::fopen to give programmer an universal \c fopen in UTF8 style.
|
||||
|
||||
In Windows platform, this function will try to convert its argument to \c wchar_t
|
||||
and calling Microsoft specific \c _wfopen function to open file.
|
||||
If encoding convertion or \c _wfopen failed, this function will return \c nullptr like \c std::fopen does.
|
||||
In other platforms, it will simply redirect calling to \c std::fopen.
|
||||
|
||||
There is a simple example:
|
||||
|
||||
\code
|
||||
FILE* fs = fopen::fopen(u8"/path/to/file", u8"rb");
|
||||
\endcode
|
||||
|
||||
\section patch__utf8_stream UTF8 Stream Support
|
||||
|
||||
The namespace yycc::patch::stream provides UTF8 support for \c std::ostream.
|
||||
This namespace contains operator overloads that give \c std::ostream the ability to write UTF8 string and its char.
|
||||
To use this feature, you should include its header file first,
|
||||
and then directly use <TT>using namespace ::yycc::patch::stream;</TT> to import this namespace.
|
||||
|
||||
\section patch__utf8_format UTF8 Format Support
|
||||
|
||||
The namespace yycc::patch::format provides a patch for \c std::format to allow UTF8 string as arguments.
|
||||
As \c std::format only allows \c char and \c wchar_t as its char type in C++ 23 currently,
|
||||
it's impossible to use UTF8 string for std::format, both as format string and argument.
|
||||
This namespace gives a patch for this shortcoming.
|
||||
|
||||
First, it define a brandnew format::format function, which resolve the issue that we can not use UTF8 as format string.
|
||||
The implementation of this function is simple. We simply convert given UTF8 format string into ordinary string,
|
||||
and then delegate it to \c std::vformat, the runtime format function in C++ 23.
|
||||
So the performance of this function may be a little worse than \c std::format, but it's not a big deal.
|
||||
We suggest that you use this namespace provided format::format function in your code,
|
||||
to enable this UTF8 format string feature.
|
||||
|
||||
Additionally, this namespace provides \c std::formatter specializations for UTF8 string.
|
||||
Thus we can safely use UTF8 string as argument in \c std::format, also including our invented brandnew format::format function.
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -11,7 +11,7 @@ When some functions throw exception, it should cause program paniked, rather tha
|
||||
This is inspired from Rust, and also the compromise with STL.
|
||||
|
||||
Most functions this library provided has Rust-Result-like return value.
|
||||
It means that programmer can handle error gracefully.
|
||||
It means that programmer can handle error correctly.
|
||||
However, this library is based on STL, another library that may throw C++ exception to indicate error.
|
||||
We can not control this behavior of STL, so I forcely apply this rule.
|
||||
|
||||
@@ -19,22 +19,18 @@ We can not control this behavior of STL, so I forcely apply this rule.
|
||||
|
||||
This library has special treat with Windows to make it works on Windows.
|
||||
However, for other operating system, it do not have too much care.
|
||||
We brutally make a premise that other operating systems are POSIX-compatible and use UTF8 as its encoding.
|
||||
We brutally make a premise that other operating systems are UNIX-liked and use UTF8 as its encoding.
|
||||
|
||||
\section premise_and_principle__string_encoding String Encoding
|
||||
|
||||
Before using this library, you should know the encoding strategy of this library first.
|
||||
After upgrade the whole project into C++23, \c char8_t is the only valid UTF8 char type.
|
||||
\c std::u8string and \c std::u8string_view are the only valid UTF8 string container and viewer.
|
||||
And, \c u8 string literal prefix is the only way to create UTF8 string literal.
|
||||
In brief words, this library use UTF8 encoding everywhere.
|
||||
|
||||
However, there are some special cases that use ordinary string instead of UTF8 string list following
|
||||
(also, not all cases are covered).
|
||||
In short words, this library use UTF8 encoding everywhere except some special cases list following (not all).
|
||||
|
||||
\li Traditional format function in yycc::string::op.
|
||||
Traditional format function provide some overloads for ordinary string formatting.
|
||||
That's because this feature is so common to use in some cases.
|
||||
\li The message of Rust panic in yycc::rust::panic.
|
||||
Due to the limitation of \c std::format, we only can use ordinary string as its message content.
|
||||
\li The message of standard library exception.
|
||||
For the compatibility with C++ standard library exception,
|
||||
we only can use ordinary string as the message of exception.
|
||||
|
||||
133
doc/src/rust.dox
133
doc/src/rust.dox
@@ -1,133 +0,0 @@
|
||||
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
|
||||
|
||||
*/
|
||||
}
|
||||
112
doc/src/std_patch.dox
Normal file
112
doc/src/std_patch.dox
Normal file
@@ -0,0 +1,112 @@
|
||||
namespace YYCC::StdPatch {
|
||||
/**
|
||||
|
||||
\page std_patch Standard Library Patch
|
||||
|
||||
\section std_patch__starts_with_ends_with Starts With & Ends With
|
||||
|
||||
\c std::basic_string::starts_with and \c std::basic_string::ends_with (also available in \c std::basic_string_view)
|
||||
are functions introduced in C++ 20 and unavailable in C++ 17.
|
||||
YYCC::StdPatch provides a patch for these function in C++ 17 environment.
|
||||
Please note these implementations are following implementation instruction presented by CppReference website.
|
||||
And it should have the same performance with vanilla functions because Microsoft STL use the same way to implement.
|
||||
These implementations will not fallback to vanilla function even they are available.
|
||||
Because their performance are good.
|
||||
|
||||
To use these functions, you just need to call them like corresponding vanilla functions.
|
||||
Our implementations provide all necessary overloads.
|
||||
The only thing you need to do is provide the string self as the first argument,
|
||||
because our implementations can not be inserted as a class member of string.
|
||||
There is an example:
|
||||
|
||||
\code
|
||||
YYCC::StdPatch::StartsWith(YYCC_U8("aabbcc"), YYCC_U8("aa"));
|
||||
YYCC::StdPatch::EndsWith(YYCC_U8("aabbcc"), YYCC_U8("cc"));
|
||||
\endcode
|
||||
|
||||
\section std_patch__contains Contains
|
||||
|
||||
\c Contains function in standard library ordered and unordered successive container are also introduced in C++ 20.
|
||||
YYCC::StdPatch provides a patch for this function in C++ 17 environment.
|
||||
|
||||
Please note this implementation will fallback to vanilla function if it is available.
|
||||
Because our implementation is a remedy (there is no way to use public class member to have the same performance of vanilla function).
|
||||
There is an example about how to use it:
|
||||
|
||||
\code
|
||||
std::set<int> test { 1, 5 };
|
||||
YYCC::StdPatch::Contains(test, static_cast<int>(5));
|
||||
\endcode
|
||||
|
||||
\section std_patch__fs_path std::filesystem::path Patch
|
||||
|
||||
As you know, the underlying char type of \c std::filesystem::path is \c wchar_t on Windows,
|
||||
and in other platforms, it is simple \c char.
|
||||
Due to this, if you try to create a \c std::filesystem::path instance by calling constructor with an UTF8 char sequence on Windows,
|
||||
the library implementation will assume your input is based on current Windows code page, not UTF8.
|
||||
And the final path stored in \c std::filesystem::path is not what you expcected.
|
||||
|
||||
This patch gives you a way to create \c std::filesystem::path
|
||||
and extract path string stored in \c std::filesystem::path with UTF8 encoding.
|
||||
This patch namespace always use UTF8 as its argument.
|
||||
You should use the functions provided by this namespace on any platforms
|
||||
instead of vanilla \c std::filesystem::path functions.
|
||||
However, if your C++ standard is higher than C++ 20,
|
||||
you can directly use UTF8 string pointer and string container in \c std::filesystem::path,
|
||||
because standard library has supported them.
|
||||
This patch only just want to provide an uniform programming experience.
|
||||
|
||||
This patch is served for Windows but also works on other plaftoms.
|
||||
If you are in Windows, this patch will perform extra operations to achieve goals,
|
||||
and in other platforms, they just redirect request to corresponding vanilla C++ functions.
|
||||
|
||||
\subsection std_patch__fs_path__from_utf8_path Create Path from UTF8 String
|
||||
|
||||
#ToStdPath provides this feature.
|
||||
It accepts an string pointer to UTF8 string and try to create \c std::filesystem::path from it.
|
||||
Function will throw exception if encoding convertion or constructor self failed.
|
||||
There are some example:
|
||||
|
||||
\code
|
||||
auto foobar_path = YYCC::StdPatch::ToStdPath(YYCC_U8("/foo/bar"));
|
||||
auto slashed_path = foobar_path / YYCC::StdPatch::ToStdPath(YYCC_U8("test"));
|
||||
auto replaced_ext = foobar_path.replace_extension(YYCC::StdPatch::ToStdPath(YYCC_U8(".txt")));
|
||||
\endcode
|
||||
|
||||
For first line in example, it is obvious that you can create a \c std::filesystem::path from this function.
|
||||
However, for the second and third line in example, what we want to tell you is
|
||||
that you should always use this function in other \c std::filesystem::path functions requiring path string.
|
||||
|
||||
\c std::filesystem::path is a very \e conservative class.
|
||||
Most of its functions only accept \c std::filesystem::path self as argument.
|
||||
For example, \c std::filesystem::path::replace_extension do not accept string as argument.
|
||||
It accepts a reference to \c std::filesystem::path as argument.
|
||||
(it still is possible that pass string pointer or string container to it because they can be converted to \c std::filesystem::path implicitly.)
|
||||
It's great. This is what we expected!
|
||||
We now can safely deliver the result generated by our function to these functions,
|
||||
and don't need to worry about the encoding of we provided string.
|
||||
Because all strings have been converted to \c std::filesystem::path by our function before passing them.
|
||||
|
||||
So, the second line will produce \c "/foo/bar/test"
|
||||
and the third line will produce \c "/foo/bar.txt" in any platforms.
|
||||
|
||||
You may notice std::filesystem::u8path.
|
||||
However it is depracted since C++ 20,
|
||||
because \c std::filesystem::path directly supports UTF8 by \c char8_t since C++ 20.
|
||||
Because C++ standard is volatile, we create this function to have an uniform programming experience.
|
||||
|
||||
\subsection std_patch__fs_path__to_utf8_path Extract UTF8 Path String from Path
|
||||
|
||||
#ToUTF8Path provides this feature.
|
||||
It basically is the reversed operation of #ToStdPath.
|
||||
It is usually used when you have done all path work in \c std::filesystem::path
|
||||
and want to get the result.
|
||||
There is an example:
|
||||
|
||||
\code
|
||||
auto foobar_path = YYCC::StdPatch::ToStdPath(YYCC_U8("/foo/bar"));
|
||||
auto result = YYCC::StdPatch::ToUTF8Path(foobar_path / YYCC::StdPatch::ToStdPath(YYCC_U8("test")));
|
||||
\endcode
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
namespace yycc::string::op {
|
||||
/**
|
||||
|
||||
\page string__op String Operations
|
||||
|
||||
\section string__op__printf Printf VPrintf
|
||||
|
||||
yycc::string::op provides 4 functions for formatting string.
|
||||
These functions are originally provided to programmer who can not use C++ 20 \c std::format feature.
|
||||
However, when this project was migrated to C++23 standard, \c std::format is finally available.
|
||||
And we set these functions as the complement to \c std::format feature.
|
||||
|
||||
\code
|
||||
std::u8string printf(const char8_t* format, ...);
|
||||
std::u8string vprintf(const char8_t* format, va_list argptr);
|
||||
std::string printf(const char* format, ...);
|
||||
std::string vprintf(const char* format, va_list argptr);
|
||||
\endcode
|
||||
|
||||
#printf and #vprintf is similar to \c std::sprintf and \c std::vsprintf.
|
||||
#printf accepts UTF8 format string and variadic arguments specifying data to print.
|
||||
This is commonly used by programmer.
|
||||
However, #vprintf also do the same work but its second argument is \c va_list,
|
||||
the representation of variadic arguments.
|
||||
It is mostly used by other function which has variadic arguments.
|
||||
|
||||
The only difference between these function and standard library functions is
|
||||
that you don't need to worry about whether the space of given buffer is enough,
|
||||
because these functions help you to calculate this internally.
|
||||
|
||||
Once there are some exceptions occurs, such as, not enough memeory, or the bad syntax of format string,
|
||||
these functions will throw exception immediately.
|
||||
|
||||
\section string__op__replace Replace
|
||||
|
||||
yycc::string::op provide 2 functions for programmer do string replacement:
|
||||
|
||||
\code
|
||||
void replace(std::u8string& 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
|
||||
|
||||
The first overload will do replacement in given string container directly.
|
||||
The second overload will produce a copy of original string and do replacement on the copied string.
|
||||
|
||||
These #replace functions have special treatments for boundary scenarios:
|
||||
|
||||
\li If given string is empty, the return value will be empty.
|
||||
\li If the character sequence to be replaced is empty string, no replacement will happen.
|
||||
\li If the character sequence will be replaced into string is or empty, it will simply delete found character sequence from given string.
|
||||
|
||||
\section string__op__join Join
|
||||
|
||||
yycc::string::op provide an universal way for joining string and various specialized join functions.
|
||||
|
||||
\subsection string__op__join__universal Universal Join Function
|
||||
|
||||
Because C++ list types are various.
|
||||
There is no unique and convenient way to create an universal join function.
|
||||
So we create #JoinDataProvider to describe join context.
|
||||
|
||||
Before using universal join function,
|
||||
you should setup #JoinDataProvider first, the context of join function.
|
||||
It actually is an \c std::function object which can be easily fetched by C++ lambda syntax.
|
||||
This function pointer returns \c std::optional<std::u8string_view>,
|
||||
which should return \c std::u8string_view for the data to be joined, or \c std::nullopt if there is no more data.
|
||||
As you noticed, this is similar to Rust iterator.
|
||||
|
||||
Then, you can pass the created #JoinDataProvider object to #join function.
|
||||
And specify delimiter at the same time.
|
||||
Then you can get the final joined string.
|
||||
There is an example:
|
||||
|
||||
\code
|
||||
std::vector<std::u8string> data {
|
||||
u8"", u8"1", u8"2", u8""
|
||||
};
|
||||
auto iter = data.cbegin();
|
||||
auto stop = data.cend();
|
||||
std::u8string joined_string = yycc::string::op::join(
|
||||
[&iter, &stop]() -> std::optional<std::u8string_view> {
|
||||
if (iter == stop) return std::nullopt;
|
||||
return *iter++;
|
||||
},
|
||||
delimiter
|
||||
);
|
||||
\endcode
|
||||
|
||||
\subsection string__op__join__specialized Specialized Join Function
|
||||
|
||||
Despite universal join function,
|
||||
yycc::string::op also provide a specialized join functions for standard library container.
|
||||
For example, the code written above can be written in following code by using this specialized overload.
|
||||
The first two argument is just the begin and end iterator.
|
||||
However, you must make sure that the iterator can be dereferenced and then implicitly converted to std::u8string_view.
|
||||
|
||||
\code
|
||||
std::vector<std::u8string> data {
|
||||
u8"", u8"1", u8"2", u8""
|
||||
};
|
||||
std::u8string joined_string = yycc::string::op::join(data.begin(), data.end(), delimiter);
|
||||
\endcode
|
||||
|
||||
\section string__op__lower_upper Lower Upper
|
||||
|
||||
This namespace provides Python-like string lower and upper function.
|
||||
|
||||
\code
|
||||
void lower(std::u8string& strl);
|
||||
std::u8string to_lower(const std::u8string_view& strl);
|
||||
void upper(std::u8string& strl);
|
||||
std::u8string to_upper(const std::u8string_view& strl);
|
||||
\endcode
|
||||
|
||||
The functions start with "to_" prefix accept a string view as argument
|
||||
and return a \b copy whose content are all the lower/upper case of original string.
|
||||
The rest of these functions accept a mutable string container as argument and will modify it in place.
|
||||
|
||||
\section string__op__strip_trim Strip and Trim
|
||||
|
||||
This namespace provides functions for removing leading and trailing characters.
|
||||
There are two sets of functions:
|
||||
|
||||
\subsection string__op__strip Unicode-aware functions
|
||||
|
||||
These functions properly handle Unicode characters when stripping:
|
||||
|
||||
\code
|
||||
std::u8string_view strip(const std::u8string_view& strl, const std::u8string_view& words);
|
||||
std::u8string_view lstrip(const std::u8string_view& strl, const std::u8string_view& words);
|
||||
std::u8string_view rstrip(const std::u8string_view& strl, const std::u8string_view& words);
|
||||
\endcode
|
||||
|
||||
The prefix "l" and "r" are for left and right strip respectively like Python.
|
||||
|
||||
\subsection string__op__trim ASCII-only functions
|
||||
|
||||
These functions treat each byte as an individual character and are faster for ASCII-only scenarios:
|
||||
|
||||
\code
|
||||
std::u8string_view trim(const std::u8string_view& strl, const std::u8string_view& words);
|
||||
std::u8string_view ltrim(const std::u8string_view& strl, const std::u8string_view& words);
|
||||
std::u8string_view rtrim(const std::u8string_view& strl, const std::u8string_view& words);
|
||||
\endcode
|
||||
|
||||
The difference of "trim" and "strip" is same as their invented time in Java.
|
||||
"trim" is inveted at first so its function is confined to ASCII-only strings.
|
||||
"strip" is introduced later and it should accept more scenarios like Unicode.
|
||||
Although all of "trim" and "strip" can handle Unicode in Java.
|
||||
|
||||
\section string__op__split Split
|
||||
|
||||
This namespace provides Python-like string split functions.
|
||||
It has 3 variants for different use cases:
|
||||
|
||||
\code
|
||||
LazySplit lazy_split(const std::u8string_view& strl, const std::u8string_view& delimiter);
|
||||
std::vector<std::u8string_view> split(const std::u8string_view& strl, const std::u8string_view& delimiter);
|
||||
std::vector<std::u8string> split_owned(const std::u8string_view& strl, const std::u8string_view& delimiter);
|
||||
\endcode
|
||||
|
||||
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 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.
|
||||
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.
|
||||
The third function #split_owned returns a vector of strings, which are copies of the original parts.
|
||||
|
||||
If the source string (the string need to be split) is empty, or the delimiter is empty,
|
||||
the result will only has 1 item and this item is source string itself.
|
||||
There is no way that these methods return an empty list, except the code is buggy.
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
namespace yycc::string::reinterpret {
|
||||
/**
|
||||
|
||||
\page string__reinterpret String Reinterpret
|
||||
|
||||
Now, you have know that we use UTF8 string everywhere in this project
|
||||
as we introduced in \ref premise_and_principle__string_encoding.
|
||||
Now it's time to know how to fetch UTF8 string from user or anywhere else.
|
||||
|
||||
\section string__reinterpret__concept Concepts
|
||||
|
||||
In following content, you may be face with 2 words: ordinary string and UTF8 string.
|
||||
|
||||
UTF8 string, as its name, is the string encoded with UTF8.
|
||||
The char type of it must is \c char8_t.
|
||||
|
||||
Ordinary string means the plain, native string.
|
||||
The result of C++ string literal without any prefix \c "foo bar" is a rdinary string.
|
||||
The char type of it is \c char.
|
||||
Its encoding depends on compiler and environment.
|
||||
(UTF8 in Linux, or system code page in Windows if UTF8 switch was not enabled in MSVC.)
|
||||
|
||||
For more infomation, please browse CppReference:
|
||||
https://en.cppreference.com/w/cpp/language/string_literal
|
||||
|
||||
\section string__reinterpret__pointer UTF8 String Pointer
|
||||
|
||||
String pointer means the raw pointer pointing to a string, such as \c const \c char*, \c char*, \c char32_t* and etc.
|
||||
|
||||
Many legacy code assume \c char* is encoded with UTF8 (the exception is Windows). But \c char* is incompatible with \c char8_t.
|
||||
YYCC provides as_utf8() to resolve this issue. There is an exmaple:
|
||||
|
||||
\code
|
||||
const char* absolutely_is_utf8 = "I confirm this is encoded with UTF8.";
|
||||
const char8_t* converted = as_utf8(absolutely_is_utf8);
|
||||
|
||||
char* mutable_utf8 = const_cast<char*>(absolutely_is_utf8); // This is not safe. Just for example.
|
||||
char8_t* mutable_converted = as_utf8(mutable_utf8);
|
||||
\endcode
|
||||
|
||||
as_utf8() has 2 overloads which can handle constant and mutable stirng pointer convertion respectively.
|
||||
|
||||
YYCC also has ability that convert UTF8 char type to ordinary char type by as_ordinary().
|
||||
Here is an exmaple:
|
||||
|
||||
\code
|
||||
const char8_t* utf8 = u8"I am UTF8 string.";
|
||||
const char* converted = as_ordinary(utf8);
|
||||
|
||||
char8_t* mutable_utf8 = const_cast<char*>(utf8); // Not safe. Also just for example.
|
||||
char* mutable_converted = as_ordinary(mutable_utf8);
|
||||
\endcode
|
||||
|
||||
Same as as_utf8(), as_ordinary() also has 2 overloads to handle constant and mutable string pointer.
|
||||
|
||||
\section string__reinterpret__container UTF8 String Container
|
||||
|
||||
String container usually means the standard library string container, such as \c std::string, \c std::wstring, \c std::u32string and etc.
|
||||
|
||||
In many personal project, programmer may use \c std::string everywhere because \c std::u8string may not be presented when writing peoject.
|
||||
How to do convertion between ordinary string container and UTF8 string container?
|
||||
It is definitely illegal that directly do force convertion. Because they may have different class layout.
|
||||
Calm down and I will tell you how to do correct convertion.
|
||||
YYCC provides as_utf8() to convert ordinary string container to UTF8 string container.
|
||||
There is an exmaple:
|
||||
|
||||
\code
|
||||
std::string ordinary_string("I am UTF8");
|
||||
std::u8string utf8_string = as_utf8(ordinary_string);
|
||||
\endcode
|
||||
|
||||
Actually, as_utf8() accepts a reference to \c std::string_view as argument.
|
||||
However, there is a implicit convertion from \c std::string to \c std::string_view,
|
||||
so you can directly pass a \c std::string instance to it.
|
||||
|
||||
String view will reduce unnecessary memory copy.
|
||||
If you just want to pass ordinary string container to function, and this function accepts \c std::u8string_view as its argument,
|
||||
you can use alternative as_utf8_view().
|
||||
|
||||
\code
|
||||
std::string ordinary_string("I am UTF8");
|
||||
std::u8string_view utf8_string = as_utf8_view(ordinary_string);
|
||||
\endcode
|
||||
|
||||
Comparing with previous one, this example use less memory.
|
||||
The reduced memory is the content of \c utf8_string because string view is a view, not the copy of original string.
|
||||
|
||||
Same as UTF8 string pointer, we also have as_ordinary() and as_ordinary_view() do correspondant reverse convertion.
|
||||
Try to do your own research and figure out how to use them.
|
||||
It's pretty easy.
|
||||
|
||||
\section string__reinterpret__clarification Clarification about Usage Scenario
|
||||
|
||||
Let we make a clarification for what this chapter are talking about.
|
||||
In these chapter, what we are talking about the convertion between UTF8 string and ordinary string,
|
||||
which is originally encoded by UTF-8 but presented by \c char type.
|
||||
This spot is crucial. If you apply any functions provided by this namespace to any string which is not encoded by UTF-8,
|
||||
for example, trying converting an CP1252 encoded western europe string to UTF-8 via function given by this namespace,
|
||||
it must cause <B>undefined behavior</B>.
|
||||
|
||||
The correct function for doing these things introduced above is located in yycc::encoding namespace,
|
||||
or a more generic module located in yycc::carton::pycodec.
|
||||
This namespace is only suit for the convertion of UTF-8 string which was mis-presented by non-<TT>char8_t</TT> types.
|
||||
After understand this point, you now can safely use this namespace.
|
||||
|
||||
Additionally, due to the legacy of MSVC, the encoding of \c char* may not be UTF8 in most cases.
|
||||
If you run the convertion code introduced in this article with the string which is not encoded with UTF8,
|
||||
it may cause undefined behavior.
|
||||
|
||||
To enable UTF8 mode of MSVC, please deliver \c /utf-8 switch to MSVC compiler.
|
||||
Thus you can use the functions introduced in this article safely.
|
||||
Otherwise, you must guarteen that the argument you provided to these functions is encoded by UTF8 manually.
|
||||
|
||||
Linux user do not need care this.
|
||||
Because almost Linux distro use UTF8 in default.
|
||||
|
||||
*/
|
||||
}
|
||||
149
doc/src/string_helper.dox
Normal file
149
doc/src/string_helper.dox
Normal file
@@ -0,0 +1,149 @@
|
||||
namespace YYCC::StringHelper {
|
||||
/**
|
||||
|
||||
\page string_helper String Helper
|
||||
|
||||
\section string_helper__printf Printf VPrintf
|
||||
|
||||
YYCC::StringHelper provides 4 functions for formatting string.
|
||||
These functions are mainly provided to programmer who can not use C++ 20 \c std::format feature.
|
||||
|
||||
\code
|
||||
bool Printf(yycc_u8string&, const yycc_char8_t*, ...);
|
||||
bool VPrintf(yycc_u8string&, const yycc_char8_t*, va_list argptr);
|
||||
yycc_u8string Printf(const yycc_char8_t*, ...);
|
||||
yycc_u8string VPrintf(const yycc_char8_t*, va_list argptr);
|
||||
\endcode
|
||||
|
||||
#Printf and #VPrintf is similar to \c std::sprintf and \c std::vsprintf.
|
||||
#Printf accepts UTF8 format string and variadic arguments specifying data to print.
|
||||
This is commonly used by programmer.
|
||||
However, #VPrintf also do the same work but its second argument is \c va_list,
|
||||
the representation of variadic arguments.
|
||||
It is mostly used by other function which has variadic arguments.
|
||||
|
||||
The only difference between these function and standard library functions is
|
||||
that you don't need to worry about whether the space of given buffer is enough,
|
||||
because these functions help you to calculate this internally.
|
||||
|
||||
There is the same design like we introduced in \ref encoding_helper.
|
||||
There are 2 overloads for #Printf and #VPrintf respectively.
|
||||
First overload return bool value and require a string container as argument for storing result.
|
||||
The second overload return result string directly.
|
||||
As you expected, first overload will return false if fail to format string (this is barely happened).
|
||||
and second overload will return empty string when formatter failed.
|
||||
|
||||
\section string_helper__replace Replace
|
||||
|
||||
YYCC::StringHelper provide 2 functions for programmer do string replacement:
|
||||
|
||||
\code
|
||||
void Replace(yycc_u8string&, const yycc_u8string_view&, const yycc_u8string_view&);
|
||||
yycc_u8string Replace(const yycc_u8string_view&, const yycc_u8string_view&, const yycc_u8string_view&);
|
||||
\endcode
|
||||
|
||||
The first overload will do replacement in given string container directly.
|
||||
The second overload will produce a copy of original string and do replacement on the copied string.
|
||||
|
||||
#Replace has special treatments for following scenarios:
|
||||
|
||||
\li If given string is empty, the return value will be empty.
|
||||
\li If the character sequence to be replaced is empty string, no replacement will happen.
|
||||
\li If the character sequence will be replaced into string is or empty, it will simply delete found character sequence from given string.
|
||||
|
||||
\section string_helper__join Join
|
||||
|
||||
YYCC::StringHelper provide an universal way for joining string and various specialized join functions.
|
||||
|
||||
\subsection string_helper__join__universal Universal Join Function
|
||||
|
||||
Because C++ list types are various.
|
||||
There is no unique and convenient way to create an universal join function.
|
||||
So we create #JoinDataProvider to describe join context.
|
||||
|
||||
Before using universal join function,
|
||||
you should setup #JoinDataProvider first, the context of join function.
|
||||
It actually is an \c std::function object which can be easily fetched by C++ lambda syntax.
|
||||
This function pointer accept a reference to \c yycc_u8string_view,
|
||||
programmer should set it to the string to be joined when at each calling.
|
||||
And this function pointer return a bool value to indicate the end of join.
|
||||
You can simply return \c false to terminate join process.
|
||||
The argument you assigned to argument will not be taken into join process when you return false.
|
||||
|
||||
Then, you can pass the created #JoinDataProvider object to #Join function.
|
||||
And specify delimiter at the same time.
|
||||
Then you can get the final joined string.
|
||||
There is an example:
|
||||
|
||||
\code
|
||||
std::vector<yycc_u8string> data {
|
||||
YYCC_U8(""), YYCC_U8("1"), YYCC_U8("2"), YYCC_U8("")
|
||||
};
|
||||
auto iter = data.cbegin();
|
||||
auto stop = data.cend();
|
||||
auto joined_string = YYCC::StringHelper::Join(
|
||||
[&iter, &stop](yycc_u8string_view& view) -> bool {
|
||||
if (iter == stop) return false;
|
||||
view = *iter;
|
||||
++iter;
|
||||
return true;
|
||||
},
|
||||
delimiter
|
||||
);
|
||||
\endcode
|
||||
|
||||
\subsection string_helper__join__specialized Specialized Join Function
|
||||
|
||||
Despite universal join function,
|
||||
YYCC::StringHelper also provide a specialized join functions for standard library container.
|
||||
For example, the code written above can be written in following code by using this specialized overload.
|
||||
The first two argument is just the begin and end iterator.
|
||||
However, you must make sure that we can dereference it and then implicitly convert it to yycc_u8string_view.
|
||||
Otherwise this overload will throw template error.
|
||||
|
||||
\code
|
||||
std::vector<yycc_u8string> data {
|
||||
YYCC_U8(""), YYCC_U8("1"), YYCC_U8("2"), YYCC_U8("")
|
||||
};
|
||||
auto joined_string = YYCC::StringHelper::Join(data.begin(), data.end(), delimiter);
|
||||
\endcode
|
||||
|
||||
\section string_helper__lower_upper Lower Upper
|
||||
|
||||
String helper provides Python-like string lower and upper function.
|
||||
Both lower and upper function have 2 overloads:
|
||||
|
||||
\code
|
||||
yycc_u8string Lower(const yycc_u8string_view&);
|
||||
void Lower(yycc_u8string&);
|
||||
\endcode
|
||||
|
||||
First overload accepts a string view as argument and return a \b copy whose content are all the lower case of original string.
|
||||
Second overload accepts a mutable string container as argument and will make all characters stored in it become their lower case.
|
||||
You can choose on of them for your flavor and requirements.
|
||||
Upper also has similar 2 overloads.
|
||||
|
||||
\section string_helper__split Split
|
||||
|
||||
String helper provides Python-like string split function.
|
||||
It has 2 types for you:
|
||||
|
||||
\code
|
||||
std::vector<yycc_u8string> Split(const yycc_u8string_view&, const yycc_u8string_view&);
|
||||
std::vector<yycc_u8string_view> SplitView(const yycc_u8string_view&, const yycc_u8string_view&);
|
||||
\endcode
|
||||
|
||||
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 only difference between these 2 split function are overt according to their names.
|
||||
The first split function will return a list of copied string as its split result.
|
||||
The second split function will return a list of string view as its split result,
|
||||
and it will keep valid as long as the life time of your given string view argument.
|
||||
It also means that the last overload will cost less memory if you don't need the copy of original string.
|
||||
|
||||
If the source string (the string need to be split) is empty, or the delimiter is empty,
|
||||
the result will only has 1 item and this item is source string itself.
|
||||
There is no way that these methods return an empty list, except the code is buggy.
|
||||
|
||||
*/
|
||||
}
|
||||
23
doc/src/win_fct_helper.dox
Normal file
23
doc/src/win_fct_helper.dox
Normal file
@@ -0,0 +1,23 @@
|
||||
namespace YYCC::WinFctHelper {
|
||||
/**
|
||||
|
||||
\page win_fct_helper Windows Function Helper
|
||||
|
||||
This helper give a more convenient way to call Windows functions.
|
||||
|
||||
This namespace is Windows specific.
|
||||
It will be entirely invisible in other platforms.
|
||||
|
||||
Currently this namespace has following functions:
|
||||
|
||||
\li #GetCurrentModule: Get the handle to current module.
|
||||
\li #GetTempDirectory: Get temporary directory in Windows.
|
||||
\li #GetModuleFileName: Get the path to module in file system by given handle.
|
||||
\li #GetLocalAppData: Get the path inside \%LOCALAPPDATA\%
|
||||
\li #IsValidCodePage: Check whether given code page number is valid.
|
||||
\li #CopyFile: The UTF8 version of Win32 \c CopyFile.
|
||||
\li #MoveFile: The UTF8 version of Win32 \c MoveFile.
|
||||
\li #DeleteFile: The UTF8 version of Win32 \c DeleteFile.
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -1,27 +1,25 @@
|
||||
namespace yycc::windows {
|
||||
namespace YYCC {
|
||||
/**
|
||||
|
||||
\page windows__import_guard Windows Import Guard
|
||||
\page win_import Windows Import Guard
|
||||
|
||||
Windows is shitty for the programmer who is familiar with UNIX programming.
|
||||
Due to legacy reason, Windows defines various things which are not compatible with UNIX or standard C++ programming.
|
||||
|
||||
\section windows__import_guard__usage Usage
|
||||
\section win_import__usage Usage
|
||||
|
||||
YYCC has a way to solve the issue introduced above.
|
||||
|
||||
\code
|
||||
#include <yycc/macro/os_detector.hpp>
|
||||
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
#include <yycc/windows/import_guard_head.hpp>
|
||||
#include <WinImportPrefix.hpp>
|
||||
#include <Windows.h>
|
||||
#include "other_header_depend_on_windows.h"
|
||||
#include <yycc/windows/import_guard_tail.hpp>
|
||||
#include <WinImportSuffix.hpp>
|
||||
#endif
|
||||
\endcode
|
||||
|
||||
The including of import_guard_head.hpp and import_guard_tail.hpp is a pair.
|
||||
The including of WinImportPrefix.hpp and WinImportSuffix.hpp is a pair.
|
||||
They just like a guard bracket the include operation of Windows related headers,
|
||||
to keep all Windows shitty contents will not be leaked outside.
|
||||
|
||||
@@ -32,7 +30,7 @@ This guard can solve following issues:
|
||||
Programmer can not use \c std::max and \c std::min normally.
|
||||
<UL>
|
||||
<LI>Windows defines \c MAX and \c MIN as macros for personal use. This is why this happened.</LI>
|
||||
<LI>This is actually resolved by CMake defined 2 public build macros which tell Windows do not create these 2 macros. But I simply conclude this feature in there.</LI>
|
||||
<LI>Guard defines some special macros to tell Windows do not create these 2 macros.</LI>
|
||||
</UL>
|
||||
</LI>
|
||||
<LI>
|
||||
@@ -46,12 +44,12 @@ This guard can solve following issues:
|
||||
Compiler throw annoy warnings and errors when using specific standard library functions.
|
||||
<UL>
|
||||
<LI>MSVC will throw warnings and errors when you are using Microsoft so-called \e depracted or \e unsafe standard library functions.</LI>
|
||||
<LI>This is also done by CMake public build macros.</LI>
|
||||
<LI>YYCCInternal.hpp, which has been included by this pair, defines some macros to purge these warnings and errors out.</LI>
|
||||
</UL>
|
||||
</LI>
|
||||
</UL>
|
||||
|
||||
\section windows__import_guard__notes Notes
|
||||
\section win_import__notes Notes
|
||||
|
||||
If you have other header files which are strongly depend on Windows header,
|
||||
you should put them into this bracket at the same time like example did.
|
||||
@@ -1,31 +0,0 @@
|
||||
namespace yycc::windows::com {
|
||||
/**
|
||||
|
||||
\page windows__com COM Helper
|
||||
|
||||
This namespace is Windows specific.
|
||||
It will be invisible on other platforms.
|
||||
|
||||
\section windows__com__memory_safe_ptr Memory Safe Pointer Types
|
||||
|
||||
This namespace provides various memory-safe types for interacting with COM functions.
|
||||
Although Microsoft also has similar smart pointer called \c CComPtr.
|
||||
But this library is eager to hide all Microsoft-related functions calling.
|
||||
Using \c CComPtr is not corresponding with the philosophy of this library.
|
||||
So these standard library based smart pointer and corresponding deleter types were created.
|
||||
|
||||
\section windows__com__com_guard COM Guard
|
||||
|
||||
This namespace contains a COM Guard which make sure COM was initialized in current module when loading current module.
|
||||
It is essential because all calling to COM functions should be under the premise that COM has been initialized.
|
||||
This guard also will uninitialize COM when unloading this module.
|
||||
|
||||
There is only an exposed function called is_initialized() for user calling.
|
||||
This function will check whether COM environment is initialized.
|
||||
If you want YYCC automatically initialize COM environment for you,
|
||||
you must call this function in your program at least one time.
|
||||
Otherwise COM Guard code may be unavailable,
|
||||
because compiler may think these code is not referenced by any other code and drop them.
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
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.
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
namespace yycc::windows::winfct {
|
||||
/**
|
||||
|
||||
\page windows__winfct Windows Function Helper
|
||||
|
||||
Namespace yycc::windows::winfct gives a more convenient way to call Windows functions.
|
||||
If you want to know how to use these functions, please read the documentation of each function.
|
||||
The return value of most functions is a specific result type.
|
||||
If any error occurs, the result type will be an error, otherwise it will be the true result.
|
||||
|
||||
This namespace is Windows specific.
|
||||
It will be entirely invisible in other platforms.
|
||||
|
||||
*/
|
||||
}
|
||||
4
asset/.gitignore → script/.gitignore
vendored
4
asset/.gitignore → script/.gitignore
vendored
@@ -2,6 +2,10 @@
|
||||
# Exclude VSCode
|
||||
.vscode/
|
||||
|
||||
# Exclude generated files
|
||||
win_build.bat
|
||||
linux_build.sh
|
||||
|
||||
## ===== Python =====
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
106
script/gen_build_script.py
Normal file
106
script/gen_build_script.py
Normal file
@@ -0,0 +1,106 @@
|
||||
import argparse
|
||||
import typing
|
||||
import re
|
||||
import shlex
|
||||
from pathlib import Path
|
||||
from dataclasses import dataclass
|
||||
import jinja2
|
||||
|
||||
def validate_cpp_ver(ver: str) -> str:
|
||||
if re.match(r'^[0-9]+$', ver) is not None: return ver
|
||||
else: raise argparse.ArgumentTypeError('invalid version of C++ standard.')
|
||||
|
||||
def write_line(f: typing.TextIO, val: str) -> None:
|
||||
f.write(val)
|
||||
f.write('\n')
|
||||
|
||||
# Reference: https://stackoverflow.com/questions/29213106/how-to-securely-escape-command-line-arguments-for-the-cmd-exe-shell-on-windows
|
||||
def escape_for_cmd_exe(arg):
|
||||
meta_re = re.compile(r'([()%!^"<>&|])')
|
||||
return meta_re.sub('^\1', arg)
|
||||
def escape_cmd_argument(arg):
|
||||
if not arg or re.search(r'(["\s])', arg):
|
||||
arg = '"' + arg.replace('"', r'\"') + '"'
|
||||
return escape_for_cmd_exe(arg)
|
||||
def escape_sh_argument(arg):
|
||||
return shlex.quote(arg)
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ScriptSettings:
|
||||
pic: bool
|
||||
cpp_version: str
|
||||
build_doc: bool
|
||||
build_testbench: bool
|
||||
|
||||
class TemplateRender:
|
||||
loader: jinja2.BaseLoader
|
||||
environment: jinja2.Environment
|
||||
|
||||
win_template: jinja2.Template
|
||||
linux_template: jinja2.Template
|
||||
|
||||
settings: ScriptSettings
|
||||
|
||||
def __init__(self, settings: ScriptSettings) -> None:
|
||||
self.loader = jinja2.FileSystemLoader(self.__get_dir())
|
||||
self.environment = jinja2.Environment(loader=self.loader)
|
||||
|
||||
self.win_template = self.environment.get_template('win_build.bat.jinja')
|
||||
self.linux_template = self.environment.get_template('linux_build.sh.jinja')
|
||||
|
||||
self.settings = settings
|
||||
|
||||
def __get_dir(self) -> Path:
|
||||
return Path(__file__).resolve().parent
|
||||
|
||||
def __escape_path(self, val: str, is_win: bool) -> str:
|
||||
if is_win: return escape_cmd_argument(val)
|
||||
else: return escape_sh_argument(val)
|
||||
|
||||
def __render(self, template: jinja2.Template, dest_file: str, is_win: bool) -> None:
|
||||
with open(self.__get_dir() / dest_file, 'w', encoding='utf-8') as f:
|
||||
f.write(template.render(
|
||||
repo_root_dir = self.__escape_path(str(self.__get_dir().parent), is_win),
|
||||
cpp_version = self.settings.cpp_version,
|
||||
build_doc = self.settings.build_doc,
|
||||
pic = settings.pic
|
||||
))
|
||||
|
||||
def render_win_script(self) -> None:
|
||||
self.__render(self.win_template, 'win_build.bat', True)
|
||||
|
||||
def render_linux_script(self) -> None:
|
||||
self.__render(self.linux_template, 'linux_build.sh', False)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# parse argument
|
||||
parser = argparse.ArgumentParser(
|
||||
prog='YYCC Windows Build Script Generator',
|
||||
description='YYCC Windows Build Script Generator'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-c', '--cpp',
|
||||
action='store', default='17', dest='cpp', type=validate_cpp_ver,
|
||||
help='The version of C++ standard used when building.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-d', '--build-doc',
|
||||
action='store_true', dest='build_doc',
|
||||
help='Build YYCC with documentation.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-p', '--pic',
|
||||
action='store_true', dest='pic',
|
||||
help='Enable Position Independent Code flag on non-Windows platform. This is crucial for compiling dynamic library using this library.'
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# build settings
|
||||
settings = ScriptSettings(args.cpp, args.build_doc, args.pic)
|
||||
# build template render and render result
|
||||
render = TemplateRender(settings)
|
||||
render.render_win_script()
|
||||
render.render_linux_script()
|
||||
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
#!/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
|
||||
cd build
|
||||
|
||||
# Build in Release mode
|
||||
cmake -DCMAKE_BUILD_TYPE=Release ../..
|
||||
cmake --build .
|
||||
cmake --install . --prefix=../install
|
||||
|
||||
# Back to root directory
|
||||
cd ..
|
||||
cd ..
|
||||
17
script/linux_build.sh.jinja
Normal file
17
script/linux_build.sh.jinja
Normal file
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
# Navigate to project root directory
|
||||
cd {{ repo_root_dir }}
|
||||
|
||||
# Create build and install directory
|
||||
mkdir build
|
||||
mkdir install
|
||||
|
||||
# Build as release version
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_STANDARD={{ cpp_version }} {{ '-DCMAKE_POSITION_INDEPENDENT_CODE=True' if pic }} {{ '-DYYCC_BUILD_DOC=ON' if build_doc }} {{ '-DYYCC_BUILD_TESTBENCH=ON' if build_testbench }} ../.. --fresh
|
||||
cmake --build .
|
||||
cmake --install . --prefix ../install
|
||||
|
||||
# Exit to original path
|
||||
cd ..
|
||||
echo "YYCC Linux CMake build done"
|
||||
@@ -1,19 +0,0 @@
|
||||
#!/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
|
||||
cd build
|
||||
|
||||
# Build in Release mode
|
||||
cmake -DCMAKE_BUILD_TYPE=Release ../..
|
||||
cmake --build .
|
||||
cmake --install . --prefix=../install
|
||||
|
||||
# Back to root directory
|
||||
cd ..
|
||||
cd ..
|
||||
0
asset/uv.lock → script/uv.lock
generated
0
asset/uv.lock → script/uv.lock
generated
85
script/win_build.bat.jinja
Normal file
85
script/win_build.bat.jinja
Normal file
@@ -0,0 +1,85 @@
|
||||
@ECHO OFF
|
||||
:: Navigate to project root directory
|
||||
CD /d {{ repo_root_dir }}
|
||||
:: Create build directory and enter it
|
||||
MKDIR bin
|
||||
CD bin
|
||||
MKDIR cpp{{ cpp_version }}
|
||||
CD cpp{{ cpp_version }}
|
||||
|
||||
:: Create internal build directory
|
||||
MKDIR Win32
|
||||
MKDIR x64
|
||||
MKDIR documentation
|
||||
:: Create internal install directory
|
||||
MKDIR install
|
||||
CD install
|
||||
MKDIR Win32_Debug
|
||||
MKDIR Win32_Release
|
||||
MKDIR x64_Debug
|
||||
MKDIR x64_Release
|
||||
CD ..
|
||||
:: Create internal MSVC specific install directory
|
||||
MKDIR msvc_install
|
||||
CD msvc_install
|
||||
MKDIR bin
|
||||
MKDIR include
|
||||
MKDIR lib
|
||||
MKDIR share
|
||||
CD bin
|
||||
MKDIR Win32
|
||||
MKDIR x64
|
||||
CD ..
|
||||
CD lib
|
||||
MKDIR Win32\Debug
|
||||
MKDIR Win32\Release
|
||||
MKDIR x64\Debug
|
||||
MKDIR x64\Release
|
||||
CD ..
|
||||
CD ..
|
||||
|
||||
:: Build for Win32
|
||||
CD Win32
|
||||
cmake -A Win32 -DCMAKE_CXX_STANDARD={{ cpp_version }} -DYYCC_BUILD_TESTBENCH=ON ../../..
|
||||
cmake --build . --config Debug
|
||||
cmake --install . --prefix=../install/Win32_Debug --config Debug
|
||||
cmake --build . --config RelWithDebInfo
|
||||
cmake --install . --prefix=../install/Win32_Release --config RelWithDebInfo
|
||||
CD ..
|
||||
:: Build for x64
|
||||
CD x64
|
||||
cmake -A x64 -DCMAKE_CXX_STANDARD={{ cpp_version }} -DYYCC_BUILD_TESTBENCH=ON ../../..
|
||||
cmake --build . --config Debug
|
||||
cmake --install . --prefix=../install/x64_Debug --config Debug
|
||||
cmake --build . --config RelWithDebInfo
|
||||
cmake --install . --prefix=../install/x64_Release --config RelWithDebInfo
|
||||
CD ..
|
||||
|
||||
{% if build_doc %}
|
||||
:: Build for documentation
|
||||
CD documentation
|
||||
cmake -A x64 -DCMAKE_CXX_STANDARD={{ cpp_version }} -DYYCC_BUILD_DOC=ON ../../..
|
||||
cmake --build . --config RelWithDebInfo
|
||||
cmake --build . --target YYCCDocumentation
|
||||
cmake --install . --prefix=../install/x64_Release --config RelWithDebInfo
|
||||
CD ..
|
||||
{% endif %}
|
||||
|
||||
:: Copy header files
|
||||
XCOPY install\x64_Release\include msvc_install\include\ /E /Y
|
||||
:: Copy binary files
|
||||
COPY install\Win32_Release\bin\YYCCTestbench.exe msvc_install\bin\Win32\YYCCTestbench.exe /Y
|
||||
COPY install\x64_Release\bin\YYCCTestbench.exe msvc_install\bin\x64\YYCCTestbench.exe /Y
|
||||
:: Copy library files
|
||||
COPY install\Win32_Debug\lib\YYCCommonplace.lib msvc_install\lib\Win32\Debug\YYCCommonplace.lib /Y
|
||||
COPY install\Win32_Release\lib\YYCCommonplace.lib msvc_install\lib\Win32\Release\YYCCommonplace.lib /Y
|
||||
COPY install\x64_Debug\lib\YYCCommonplace.lib msvc_install\lib\x64\Debug\YYCCommonplace.lib /Y
|
||||
COPY install\x64_Release\lib\YYCCommonplace.lib msvc_install\lib\x64\Release\YYCCommonplace.lib /Y
|
||||
{% if build_doc %}
|
||||
:: Copy documentation files
|
||||
XCOPY install\x64_Release\share msvc_install\share\ /E /Y
|
||||
{% endif %}
|
||||
|
||||
:: Leave build directory and report
|
||||
CD ..\..
|
||||
ECHO Windows CMake Build Done
|
||||
@@ -1,18 +0,0 @@
|
||||
@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
|
||||
CD build
|
||||
|
||||
:: Build with x64 architecture in Release mode
|
||||
cmake -A x64 ../..
|
||||
cmake --build . --config Release
|
||||
cmake --install . --prefix=../install --config Release
|
||||
|
||||
:: Back to root directory
|
||||
CD ..
|
||||
CD ..
|
||||
@@ -14,13 +14,11 @@ PRIVATE
|
||||
yycc/string/reinterpret.cpp
|
||||
yycc/string/op.cpp
|
||||
yycc/patch/fopen.cpp
|
||||
yycc/patch/stream.cpp
|
||||
yycc/panic.cpp
|
||||
yycc/env.cpp
|
||||
yycc/rust/panic.cpp
|
||||
yycc/rust/env.cpp
|
||||
yycc/windows/com.cpp
|
||||
yycc/windows/dialog.cpp
|
||||
yycc/windows/winfct.cpp
|
||||
yycc/windows/console.cpp
|
||||
yycc/encoding/stl.cpp
|
||||
yycc/encoding/windows.cpp
|
||||
yycc/encoding/iconv.cpp
|
||||
@@ -29,20 +27,6 @@ PRIVATE
|
||||
yycc/carton/termcolor.cpp
|
||||
yycc/carton/wcwidth.cpp
|
||||
yycc/carton/tabulate.cpp
|
||||
yycc/carton/ironpad.cpp
|
||||
yycc/carton/csconsole.cpp
|
||||
yycc/carton/clap/option.cpp
|
||||
yycc/carton/clap/variable.cpp
|
||||
yycc/carton/clap/summary.cpp
|
||||
yycc/carton/clap/application.cpp
|
||||
yycc/carton/clap/manual.cpp
|
||||
yycc/carton/clap/parser.cpp
|
||||
yycc/carton/clap/resolver.cpp
|
||||
yycc/carton/binstore/types.cpp
|
||||
yycc/carton/binstore/setting.cpp
|
||||
yycc/carton/binstore/configuration.cpp
|
||||
yycc/carton/binstore/storage.cpp
|
||||
yycc/carton/lexer61.cpp
|
||||
)
|
||||
target_sources(YYCCommonplace
|
||||
PUBLIC
|
||||
@@ -58,35 +42,29 @@ FILES
|
||||
yycc/macro/compiler_detector.hpp
|
||||
yycc/macro/ptr_size_detector.hpp
|
||||
yycc/macro/class_copy_move.hpp
|
||||
yycc/macro/printf_checker.hpp
|
||||
yycc/cenum.hpp
|
||||
yycc/string.hpp
|
||||
yycc/flag_enum.hpp
|
||||
yycc/string/reinterpret.hpp
|
||||
yycc/string/op.hpp
|
||||
yycc/patch/ptr_pad.hpp
|
||||
yycc/patch/fopen.hpp
|
||||
yycc/patch/stream.hpp
|
||||
yycc/patch/format.hpp
|
||||
yycc/patch/libcxx/enumerate.hpp
|
||||
yycc/patch/libcxx/stacktrace.hpp
|
||||
yycc/patch/libcxx/charconv.hpp
|
||||
yycc/num/parse.hpp
|
||||
yycc/num/stringify.hpp
|
||||
yycc/num/safe_cast.hpp
|
||||
yycc/num/safe_op.hpp
|
||||
yycc/num/op.hpp
|
||||
yycc/primitive.hpp
|
||||
yycc/option.hpp
|
||||
yycc/result.hpp
|
||||
yycc/prelude.hpp
|
||||
yycc/panic.hpp
|
||||
yycc/env.hpp
|
||||
yycc/rust/prelude.hpp
|
||||
yycc/rust/primitive.hpp
|
||||
yycc/rust/panic.hpp
|
||||
yycc/rust/option.hpp
|
||||
yycc/rust/result.hpp
|
||||
yycc/rust/env.hpp
|
||||
yycc/windows/import_guard_head.hpp
|
||||
yycc/windows/import_guard_tail.hpp
|
||||
yycc/windows/com.hpp
|
||||
yycc/windows/dialog.hpp
|
||||
yycc/windows/winfct.hpp
|
||||
yycc/windows/console.hpp
|
||||
yycc/constraint.hpp
|
||||
yycc/constraint/builder.hpp
|
||||
yycc/encoding/stl.hpp
|
||||
yycc/encoding/windows.hpp
|
||||
yycc/encoding/iconv.hpp
|
||||
@@ -95,26 +73,6 @@ FILES
|
||||
yycc/carton/termcolor.hpp
|
||||
yycc/carton/wcwidth.hpp
|
||||
yycc/carton/tabulate.hpp
|
||||
yycc/carton/ironpad.hpp
|
||||
yycc/carton/csconsole.hpp
|
||||
yycc/carton/clap.hpp
|
||||
yycc/carton/clap/types.hpp
|
||||
yycc/carton/clap/option.hpp
|
||||
yycc/carton/clap/variable.hpp
|
||||
yycc/carton/clap/summary.hpp
|
||||
yycc/carton/clap/application.hpp
|
||||
yycc/carton/clap/manual.hpp
|
||||
yycc/carton/clap/validator.hpp
|
||||
yycc/carton/clap/parser.hpp
|
||||
yycc/carton/clap/resolver.hpp
|
||||
yycc/carton/binstore.hpp
|
||||
yycc/carton/binstore/types.hpp
|
||||
yycc/carton/binstore/serdes.hpp
|
||||
yycc/carton/binstore/setting.hpp
|
||||
yycc/carton/binstore/configuration.hpp
|
||||
yycc/carton/binstore/storage.hpp
|
||||
yycc/carton/lexer61.hpp
|
||||
yycc/carton/fft.hpp
|
||||
)
|
||||
# Setup header infomations
|
||||
target_include_directories(YYCCommonplace
|
||||
@@ -146,10 +104,9 @@ PUBLIC
|
||||
$<$<PLATFORM_ID:Darwin>:YYCC_OS_MACOS>
|
||||
$<$<PLATFORM_ID:iOS>:YYCC_OS_MACOS> # We brutally think iOS as macOS.
|
||||
# Compiler macro
|
||||
$<$<CXX_COMPILER_ID:MSVC>:YYCC_CC_MSVC>
|
||||
$<$<CXX_COMPILER_ID:GNU>:YYCC_CC_GCC>
|
||||
$<$<CXX_COMPILER_ID:Clang>:YYCC_CC_CLANG>
|
||||
$<$<CXX_COMPILER_ID:AppleClang>:YYCC_CC_CLANG> # We brutally think AppleClang is Clang.
|
||||
$<$<CXX_COMPILER_ID:MSVC>:YYCC_CC_MSVC>
|
||||
# Endian macro
|
||||
$<$<STREQUAL:${CMAKE_CXX_BYTE_ORDER},LITTLE_ENDIAN>:YYCC_ENDIAN_LITTLE>
|
||||
$<$<STREQUAL:${CMAKE_CXX_BYTE_ORDER},BIG_ENDIAN>:YYCC_ENDIAN_BIG>
|
||||
@@ -167,15 +124,6 @@ PUBLIC
|
||||
# Fix Windows header file shit
|
||||
$<$<BOOL:${WIN32}>:WIN32_LEAN_AND_MEAN>
|
||||
$<$<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
|
||||
PUBLIC
|
||||
@@ -187,15 +135,6 @@ PUBLIC
|
||||
$<$<CXX_COMPILER_ID:MSVC>:/Zc:__cplusplus>
|
||||
)
|
||||
|
||||
# Fix GCC std::stacktrace link error
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 14)
|
||||
target_link_libraries(YYCCommonplace PRIVATE stdc++exp)
|
||||
else ()
|
||||
target_link_libraries(YYCCommonplace PRIVATE stdc++_libbacktrace)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
# Install binary and headers
|
||||
install(TARGETS YYCCommonplace
|
||||
EXPORT YYCCommonplaceTargets
|
||||
|
||||
348
src/YYCCLegacy/ArgParser.cpp
Normal file
348
src/YYCCLegacy/ArgParser.cpp
Normal file
@@ -0,0 +1,348 @@
|
||||
#include "ArgParser.hpp"
|
||||
|
||||
#include "EncodingHelper.hpp"
|
||||
#include "ConsoleHelper.hpp"
|
||||
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
#include "WinImportPrefix.hpp"
|
||||
#include <Windows.h>
|
||||
#include <shellapi.h>
|
||||
#include <processenv.h>
|
||||
#include "WinImportSuffix.hpp"
|
||||
#endif
|
||||
|
||||
namespace YYCC::ArgParser {
|
||||
|
||||
#pragma region Arguments List
|
||||
|
||||
ArgumentList ArgumentList::CreateFromStd(int argc, char* argv[]) {
|
||||
std::vector<yycc_u8string> args;
|
||||
for (int i = 1; i < argc; ++i) { // starts with 1 to remove first part (executable self)
|
||||
if (argv[i] != nullptr)
|
||||
args.emplace_back(yycc_u8string(YYCC::EncodingHelper::ToUTF8(argv[i])));
|
||||
}
|
||||
return ArgumentList(std::move(args));
|
||||
}
|
||||
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
ArgumentList ArgumentList::CreateFromWin32() {
|
||||
// Reference: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw
|
||||
|
||||
// prepare list
|
||||
std::vector<yycc_u8string> args;
|
||||
|
||||
// try fetching from Win32 functions
|
||||
int argc;
|
||||
LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc);
|
||||
if (argv != NULL) {
|
||||
for (int i = 1; i < argc; ++i) { // starts with 1 to remove first part (executable self)
|
||||
if (argv[i] != nullptr) {
|
||||
yycc_u8string u8_argv;
|
||||
if (YYCC::EncodingHelper::WcharToUTF8(argv[i], u8_argv))
|
||||
args.emplace_back(std::move(u8_argv));
|
||||
}
|
||||
}
|
||||
}
|
||||
LocalFree(argv);
|
||||
|
||||
// return result
|
||||
return ArgumentList(std::move(args));
|
||||
}
|
||||
#endif
|
||||
|
||||
ArgumentList::ArgumentList(std::vector<yycc_u8string>&& arguments) :
|
||||
m_Arguments(arguments), m_ArgumentsCursor(0u) {}
|
||||
|
||||
void ArgumentList::Prev() {
|
||||
if (m_ArgumentsCursor == 0u)
|
||||
throw std::runtime_error("attempt to move on the head of iterator.");
|
||||
--m_ArgumentsCursor;
|
||||
}
|
||||
|
||||
void ArgumentList::Next() {
|
||||
if (IsEOF()) throw std::runtime_error("attempt to move on the tail of iterator.");
|
||||
++m_ArgumentsCursor;
|
||||
}
|
||||
|
||||
const yycc_u8string& ArgumentList::Argument() const {
|
||||
if (IsEOF()) throw std::runtime_error("attempt to get data on the tail of iterator.");
|
||||
return m_Arguments[m_ArgumentsCursor];
|
||||
}
|
||||
|
||||
bool ArgumentList::IsSwitch(bool* is_long_name, yycc_u8string* long_name, yycc_char8_t* short_name) const {
|
||||
// check eof first
|
||||
if (IsEOF()) throw std::runtime_error("attempt to fetch data on the tail of iterator.");
|
||||
// check long name first, then check short name
|
||||
if (IsLongNameSwitch(long_name)) {
|
||||
if (is_long_name != nullptr) *is_long_name = true;
|
||||
return true;
|
||||
}
|
||||
if (IsShortNameSwitch(short_name)) {
|
||||
if (is_long_name != nullptr) *is_long_name = false;
|
||||
return true;
|
||||
}
|
||||
// not matched
|
||||
return false;
|
||||
}
|
||||
bool ArgumentList::IsLongNameSwitch(yycc_u8string* name_part) const {
|
||||
// fetch current parameter
|
||||
if (IsEOF()) throw std::runtime_error("attempt to fetch data on the tail of iterator.");
|
||||
const yycc_u8string& param = m_Arguments[m_ArgumentsCursor];
|
||||
// find double slash
|
||||
if (param.find(AbstractArgument::DOUBLE_DASH) != 0u) return false;
|
||||
// check gotten long name
|
||||
yycc_u8string_view long_name = yycc_u8string_view(param).substr(2u);
|
||||
if (!AbstractArgument::IsLegalLongName(long_name)) return false;
|
||||
// set checked long name if possible and return
|
||||
if (name_part != nullptr)
|
||||
*name_part = long_name;
|
||||
return true;
|
||||
}
|
||||
bool ArgumentList::IsShortNameSwitch(yycc_char8_t* name_part) const {
|
||||
// fetch current parameter
|
||||
if (IsEOF()) throw std::runtime_error("attempt to fetch data on the tail of iterator.");
|
||||
const yycc_u8string& param = m_Arguments[m_ArgumentsCursor];
|
||||
// if the length is not exactly equal to 2,
|
||||
// or it not starts with dash,
|
||||
// it is impossible a short name
|
||||
if (param.size() != 2u || param[0] != AbstractArgument::DASH) return false;
|
||||
// check gotten short name
|
||||
yycc_char8_t short_name = param[1];
|
||||
if (!AbstractArgument::IsLegalShortName(short_name)) return false;
|
||||
// set checked short name if possible and return
|
||||
if (name_part != nullptr)
|
||||
*name_part = short_name;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ArgumentList::IsValue(yycc_u8string* val) const {
|
||||
bool is_value = !IsSwitch();
|
||||
if (is_value && val != nullptr)
|
||||
*val = m_Arguments[m_ArgumentsCursor];
|
||||
return is_value;
|
||||
}
|
||||
|
||||
bool ArgumentList::IsEOF() const { return m_ArgumentsCursor >= m_Arguments.size(); }
|
||||
|
||||
void ArgumentList::Reset() { m_ArgumentsCursor = 0u; }
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Abstract Argument
|
||||
|
||||
const yycc_u8string AbstractArgument::DOUBLE_DASH = YYCC_U8("--");
|
||||
const yycc_char8_t AbstractArgument::DASH = YYCC_U8_CHAR('-');
|
||||
const yycc_char8_t AbstractArgument::NO_SHORT_NAME = YYCC_U8_CHAR(0);
|
||||
const yycc_char8_t AbstractArgument::MIN_SHORT_NAME = YYCC_U8_CHAR('!');
|
||||
const yycc_char8_t AbstractArgument::MAX_SHORT_NAME = YYCC_U8_CHAR('~');
|
||||
|
||||
bool AbstractArgument::IsLegalShortName(yycc_char8_t short_name) {
|
||||
if (short_name == AbstractArgument::DASH || // dash is not allowed
|
||||
short_name < AbstractArgument::MIN_SHORT_NAME || short_name > AbstractArgument::MAX_SHORT_NAME) { // non-display ASCII chars are not allowed
|
||||
return false;
|
||||
}
|
||||
// okey
|
||||
return true;
|
||||
}
|
||||
bool AbstractArgument::IsLegalLongName(const yycc_u8string_view& long_name) {
|
||||
// empty is not allowed
|
||||
if (long_name.empty()) return false;
|
||||
// non-display ASCII chars are not allowed
|
||||
for (const auto& val : long_name) {
|
||||
if (val < AbstractArgument::MIN_SHORT_NAME || val > AbstractArgument::MAX_SHORT_NAME)
|
||||
return false;
|
||||
}
|
||||
// okey
|
||||
return true;
|
||||
}
|
||||
|
||||
AbstractArgument::AbstractArgument(
|
||||
const yycc_char8_t* long_name, yycc_char8_t short_name,
|
||||
const yycc_char8_t* description, const yycc_char8_t* argument_example,
|
||||
bool is_optional) :
|
||||
m_LongName(), m_ShortName(AbstractArgument::NO_SHORT_NAME), m_Description(), m_ArgumentExample(),
|
||||
m_IsOptional(is_optional), m_IsCaptured(false) {
|
||||
|
||||
// try to assign long name and check it
|
||||
if (long_name != nullptr) {
|
||||
m_LongName = long_name;
|
||||
if (!AbstractArgument::IsLegalLongName(m_LongName))
|
||||
throw std::invalid_argument("Given long name is invalid.");
|
||||
}
|
||||
// try to assign short name and check it
|
||||
if (short_name != AbstractArgument::NO_SHORT_NAME) {
|
||||
m_ShortName = short_name;
|
||||
if (!AbstractArgument::IsLegalShortName(m_ShortName))
|
||||
throw std::invalid_argument("Given short name is invalid.");
|
||||
}
|
||||
// check short name and long name existence
|
||||
if (!HasShortName() && !HasLongName())
|
||||
throw std::invalid_argument("you must specify an one of long name or short name.");
|
||||
|
||||
// try to assign other string values
|
||||
if (description != nullptr) m_Description = description;
|
||||
if (argument_example != nullptr) m_ArgumentExample = argument_example;
|
||||
}
|
||||
|
||||
AbstractArgument::~AbstractArgument() {}
|
||||
|
||||
bool AbstractArgument::HasLongName() const { return !m_LongName.empty(); }
|
||||
const yycc_u8string& AbstractArgument::GetLongName() const { return m_LongName; }
|
||||
bool AbstractArgument::HasShortName() const { return m_ShortName != NO_SHORT_NAME; }
|
||||
yycc_char8_t AbstractArgument::GetShortName() const { return m_ShortName; }
|
||||
bool AbstractArgument::HasDescription() const { return !m_Description.empty(); }
|
||||
const yycc_u8string& AbstractArgument::GetDescription() const { return m_Description; }
|
||||
bool AbstractArgument::HasArgumentExample() const { return !m_ArgumentExample.empty(); }
|
||||
const yycc_u8string& AbstractArgument::GetArgumentExample() const { return m_ArgumentExample; }
|
||||
bool AbstractArgument::IsOptional() const { return m_IsOptional; }
|
||||
|
||||
bool AbstractArgument::IsCaptured() const { return m_IsCaptured; }
|
||||
void AbstractArgument::SetCaptured(bool is_captured) { m_IsCaptured = is_captured; }
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Option Context
|
||||
|
||||
OptionContext::OptionContext(
|
||||
const yycc_char8_t* summary, const yycc_char8_t* description,
|
||||
std::initializer_list<AbstractArgument*> arguments) :
|
||||
m_Summary(), m_Description() {
|
||||
// assign summary and description
|
||||
if (summary != nullptr) m_Summary = summary;
|
||||
if (description != nullptr) m_Description = description;
|
||||
|
||||
// insert argument list and check them
|
||||
for (auto* arg : arguments) {
|
||||
// insert into long name map if necessary
|
||||
if (arg->HasLongName()) {
|
||||
auto result = m_LongNameMap.try_emplace(arg->GetLongName(), arg);
|
||||
if (!result.second) throw std::invalid_argument("duplicated long name!");
|
||||
}
|
||||
// insert into short name map if necessary
|
||||
if (arg->HasShortName()) {
|
||||
auto result = m_ShortNameMap.try_emplace(arg->GetShortName(), arg);
|
||||
if (!result.second) throw std::invalid_argument("duplicated short name!");
|
||||
}
|
||||
// insert into argument list
|
||||
m_Arguments.emplace_back(arg);
|
||||
}
|
||||
}
|
||||
|
||||
OptionContext::~OptionContext() {}
|
||||
|
||||
bool OptionContext::Parse(ArgumentList& al) {
|
||||
// reset argument list first
|
||||
al.Reset();
|
||||
|
||||
// prepare variables and start loop
|
||||
yycc_u8string long_name;
|
||||
yycc_char8_t short_name;
|
||||
bool is_long_name;
|
||||
while (!al.IsEOF()) {
|
||||
// if we can not find any switches, return with error
|
||||
if (!al.IsSwitch(&is_long_name, &long_name, &short_name)) return false;
|
||||
|
||||
// find corresponding argument by long name or short name.
|
||||
// if we can not find it, return with error.
|
||||
AbstractArgument* arg;
|
||||
if (is_long_name) {
|
||||
auto finder = m_LongNameMap.find(long_name);
|
||||
if (finder == m_LongNameMap.end()) return false;
|
||||
arg = finder->second;
|
||||
} else {
|
||||
auto finder = m_ShortNameMap.find(short_name);
|
||||
if (finder == m_ShortNameMap.end()) return false;
|
||||
arg = finder->second;
|
||||
}
|
||||
|
||||
// if this argument has been captured, raise error
|
||||
if (arg->IsCaptured()) return false;
|
||||
// call user parse function of found argument
|
||||
if (arg->Parse(al)) {
|
||||
// success. mark it is captured
|
||||
arg->SetCaptured(true);
|
||||
} else {
|
||||
// failed, return error
|
||||
return false;
|
||||
}
|
||||
|
||||
// move to next argument
|
||||
al.Next();
|
||||
}
|
||||
|
||||
// after processing all argument,
|
||||
// we should check whether all non-optional argument are captured.
|
||||
for (const auto* arg : m_Arguments) {
|
||||
if (!arg->IsOptional() && !arg->IsCaptured())
|
||||
return false;
|
||||
}
|
||||
|
||||
// okey
|
||||
return true;
|
||||
}
|
||||
|
||||
void OptionContext::Reset() {
|
||||
for (auto* arg : m_Arguments) {
|
||||
// clear user data and unset captured
|
||||
arg->Reset();
|
||||
arg->SetCaptured(false);
|
||||
}
|
||||
}
|
||||
|
||||
void OptionContext::Help() const {
|
||||
// print summary and description if necessary
|
||||
if (!m_Summary.empty())
|
||||
YYCC::ConsoleHelper::WriteLine(m_Summary.c_str());
|
||||
if (!m_Description.empty())
|
||||
YYCC::ConsoleHelper::WriteLine(m_Description.c_str());
|
||||
|
||||
// blank line
|
||||
YYCC::ConsoleHelper::WriteLine(YYCC_U8(""));
|
||||
|
||||
// print argument list
|
||||
for (const auto* arg : m_Arguments) {
|
||||
yycc_u8string argstr;
|
||||
|
||||
// print indent
|
||||
argstr += YYCC_U8("\t");
|
||||
// print optional head
|
||||
bool is_optional = arg->IsOptional();
|
||||
if (is_optional) argstr += YYCC_U8("[");
|
||||
|
||||
// switch name
|
||||
bool short_name = arg->HasShortName(), long_name = arg->HasLongName();
|
||||
if (short_name) {
|
||||
argstr += YYCC_U8("-");
|
||||
argstr += arg->GetShortName();
|
||||
}
|
||||
if (long_name) {
|
||||
if (short_name) argstr += YYCC_U8(", ");
|
||||
argstr += YYCC_U8("--");
|
||||
argstr += arg->GetLongName();
|
||||
}
|
||||
|
||||
// argument example
|
||||
if (arg->HasArgumentExample()) {
|
||||
argstr += YYCC_U8(" ");
|
||||
argstr += arg->GetArgumentExample();
|
||||
}
|
||||
|
||||
// optional tail
|
||||
if (is_optional) argstr += YYCC_U8("]");
|
||||
|
||||
// argument description
|
||||
if (arg->HasDescription()) {
|
||||
// eol and double indent
|
||||
argstr += YYCC_U8("\n\t\t");
|
||||
// description
|
||||
argstr += arg->GetDescription();
|
||||
}
|
||||
|
||||
// write into console
|
||||
YYCC::ConsoleHelper::WriteLine(argstr.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
}
|
||||
465
src/YYCCLegacy/ArgParser.hpp
Normal file
465
src/YYCCLegacy/ArgParser.hpp
Normal file
@@ -0,0 +1,465 @@
|
||||
#pragma once
|
||||
#include "YYCCInternal.hpp"
|
||||
|
||||
#include "Constraints.hpp"
|
||||
#include "EncodingHelper.hpp"
|
||||
#include "ParserHelper.hpp"
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
|
||||
/**
|
||||
* @brief Universal argument parser.
|
||||
* @details
|
||||
* For how to use this namespace, please see \ref arg_parser.
|
||||
*/
|
||||
namespace YYCC::ArgParser {
|
||||
|
||||
/**
|
||||
* @brief The advanced wrapper of the list containing command line arguments.
|
||||
* @details
|
||||
* This class is used by OptionContext and argument class internally for convenience.
|
||||
* It should not be constrcuted directly.
|
||||
* Programmer should choose proper static creation function to create instance of this class.
|
||||
*/
|
||||
class ArgumentList {
|
||||
public:
|
||||
/**
|
||||
* @brief Create argument list from the parameters of standard C main function.
|
||||
* @param[in] argc The argument count passed to standard C main function.
|
||||
* @param[in] argv The argument value passed to standard C main function.
|
||||
* @return Extracted argument list instance.
|
||||
* @remarks
|
||||
* First item in command line will be stripped,
|
||||
* because in most cases it points to executable self
|
||||
* and should not be seen as a part of arguments.
|
||||
*/
|
||||
static ArgumentList CreateFromStd(int argc, char* argv[]);
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
/**
|
||||
* @brief Create argument list from Win32 function.
|
||||
* @details
|
||||
* @return Extracted argument list instance.
|
||||
* @remarks
|
||||
* First item in command line will be stripped,
|
||||
* because in most cases it points to executable self
|
||||
* and should not be seen as a part of arguments.
|
||||
* \par
|
||||
* Programmer should use this function instead of CreateFromStd(),
|
||||
* because that function involve encoding issue on Windows, especially command line including non-ASCII chars.
|
||||
* Only this function guaranteen that return correct argument list on Windows.
|
||||
*/
|
||||
static ArgumentList CreateFromWin32();
|
||||
#endif
|
||||
private:
|
||||
/**
|
||||
* @brief Constructor of ArgumentList used internally.
|
||||
* @param[in] arguments
|
||||
* Underlying argument list.
|
||||
* This argument list should remove first executable name before passing it to there.
|
||||
*/
|
||||
ArgumentList(std::vector<yycc_u8string>&& arguments);
|
||||
public:
|
||||
YYCC_DEF_CLS_COPY_MOVE(ArgumentList);
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Move to previous argument.
|
||||
* @exception std::runtime_error Try moving at the head of argument list.
|
||||
*/
|
||||
void Prev();
|
||||
/**
|
||||
* @brief Move to next argument.
|
||||
* @exception std::runtime_error Try moving at the tail of argument list.
|
||||
*/
|
||||
void Next();
|
||||
/**
|
||||
* @brief Get the string of current argument.
|
||||
* @exception std::runtime_error Try fetching data at the tail of argument list.
|
||||
* @return The constant reference to the string of current argument.
|
||||
*/
|
||||
const yycc_u8string& Argument() const;
|
||||
/**
|
||||
* @brief Check whether current argument is a option / switch.
|
||||
* @param[out] is_long_name
|
||||
* It will be set true if this argument is long name, otherwise short name.
|
||||
* nullptr if you don't want to receive this infomation.
|
||||
* @param[out] long_name
|
||||
* The container holding matched long name if it is (double dash stripped).
|
||||
* nullptr if you don't want to receive this infomation.
|
||||
* @param[out] short_name
|
||||
* The variable holding matched short name if it is (dash stripped).
|
||||
* nullptr if you don't want to receive this infomation.
|
||||
* @exception std::runtime_error Try fetching data at the tail of argument list.
|
||||
* @return
|
||||
* True if it is, otherwise false.
|
||||
* If this function return false, all given parameters are in undefined status.
|
||||
*/
|
||||
bool IsSwitch(
|
||||
bool* is_long_name = nullptr,
|
||||
yycc_u8string* long_name = nullptr,
|
||||
yycc_char8_t* short_name = nullptr) const;
|
||||
/**
|
||||
* @brief Check whether current argument is a value.
|
||||
* @param[out] val
|
||||
* The variable holding value if it is.
|
||||
* nullptr if you don't want to receive this infomation.
|
||||
* @exception std::runtime_error Try fetching data at the tail of argument list.
|
||||
* @return True if it is, otherwise false.
|
||||
*/
|
||||
bool IsValue(yycc_u8string* val = nullptr) const;
|
||||
/**
|
||||
* @brief Check whether we are at the tail of argument list.
|
||||
* @details
|
||||
* Please note EOF is a special state that you can not fetch data from it.
|
||||
* EOF is the next element of the last element of argument list.
|
||||
* It more like \c end() in most C++ container.
|
||||
* @return True if it is, otherwise false.
|
||||
*/
|
||||
bool IsEOF() const;
|
||||
/**
|
||||
* @brief Reset cursor to the head of argument list for reuse.
|
||||
*/
|
||||
void Reset();
|
||||
private:
|
||||
/**
|
||||
* @brief Check whether current argument is long name option / switch.
|
||||
* @details This function is used by IsSwitch() internally.
|
||||
* @param[out] name_part
|
||||
* The container holding matched long name if it is (double dash stripped).
|
||||
* nullptr if you don't want to receive this infomation.
|
||||
* @return True if it is, otherwise false.
|
||||
*/
|
||||
bool IsLongNameSwitch(yycc_u8string* name_part = nullptr) const;
|
||||
/**
|
||||
* @brief Check whether current argument is short name option / switch.
|
||||
* @details This function is used by IsSwitch() internally.
|
||||
* @param[out] name_part
|
||||
* The variable holding matched short name if it is (dash stripped).
|
||||
* nullptr if you don't want to receive this infomation.
|
||||
* @return True if it is, otherwise false.
|
||||
*/
|
||||
bool IsShortNameSwitch(yycc_char8_t* name_part = nullptr) const;
|
||||
|
||||
private:
|
||||
std::vector<yycc_u8string> m_Arguments;
|
||||
size_t m_ArgumentsCursor;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The base class of every argument.
|
||||
* @details Programmer can inherit this class and implement essential functions to create custom argument.
|
||||
*/
|
||||
class AbstractArgument {
|
||||
friend class OptionContext;
|
||||
|
||||
// Long name and short name constants and checker.
|
||||
public:
|
||||
static const yycc_u8string DOUBLE_DASH; ///< The constant value representing double dash (\c --)
|
||||
static const yycc_char8_t DASH; ///< The constant value representing dash (\c -)
|
||||
static const yycc_char8_t NO_SHORT_NAME; ///< The constant value representing that there is not short value.
|
||||
static const yycc_char8_t MIN_SHORT_NAME; ///< The constant value representing the minimum value of valid ASCII chars in short and long name.
|
||||
static const yycc_char8_t MAX_SHORT_NAME; ///< The constant value representing the maximum value of valid ASCII chars in short and long name.
|
||||
/**
|
||||
* @brief Check whether given short name is valid.
|
||||
* @details
|
||||
* An ASCII code of valid short name
|
||||
* should not lower than #MIN_SHORT_NAME or higher than #MAX_SHORT_NAME.
|
||||
* It also can not be #DASH.
|
||||
* @param[in] short_name Short name for checking.
|
||||
* @return True if it is valid, otherwise false.
|
||||
*/
|
||||
static bool IsLegalShortName(yycc_char8_t short_name);
|
||||
/**
|
||||
* @brief Check whether given long name is valid.
|
||||
* @details
|
||||
* An ASCII code of every item in valid long name
|
||||
* should not lower than #MIN_SHORT_NAME or higher than #MAX_SHORT_NAME.
|
||||
* However it can be #DASH. This is different with short name.
|
||||
* @param[in] long_name Long name for checking.
|
||||
* @return True if it is valid, otherwise false.
|
||||
*/
|
||||
static bool IsLegalLongName(const yycc_u8string_view& long_name);
|
||||
|
||||
// Constructor & destructor
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor an argument
|
||||
* @param[in] long_name The long name of this argument. nullptr if no long name.
|
||||
* @param[in] short_name The short name of this argument. #NO_SHORT_NAME if no short name.
|
||||
* @param[in] description The description of this argument to indroduce what this argument does. nullptr if no description.
|
||||
* @param[in] argument_example The example string of this argument's value. nullptr if no example.
|
||||
* @param[in] is_optional
|
||||
* True if this argument is optional argument.
|
||||
* Optional argument can be absent in argument list.
|
||||
* Non-optional argument must be presented in argument list,
|
||||
* otherwise parser will fail.
|
||||
* @exception std::invalid_argument Given short name or long name are invalid.
|
||||
*/
|
||||
AbstractArgument(
|
||||
const yycc_char8_t* long_name, yycc_char8_t short_name = AbstractArgument::NO_SHORT_NAME,
|
||||
const yycc_char8_t* description = nullptr, const yycc_char8_t* argument_example = nullptr,
|
||||
bool is_optional = false);
|
||||
virtual ~AbstractArgument();
|
||||
YYCC_DEL_CLS_COPY_MOVE(AbstractArgument);
|
||||
|
||||
// ===== User Implementation =====
|
||||
protected:
|
||||
/**
|
||||
* @brief User implemented custom parse function
|
||||
* @param[in] al The argument list for parsing.
|
||||
* @return True if parse is success, otherwise false.
|
||||
* @remarks
|
||||
* When enter this function, argument list points to switch self.
|
||||
* After success parsing, you should point it to the argument this function last accepted.
|
||||
* For exmaple, for command line "-i 114514",
|
||||
* when enter this function, this argument list point to "-i",
|
||||
* and you should set it to "114514" when exiting this function.
|
||||
*/
|
||||
virtual bool Parse(ArgumentList& al) = 0;
|
||||
/**
|
||||
* @brief User implemented custom reset function
|
||||
* @remarks
|
||||
* In this function, user should claer its stored value if is has.
|
||||
* You don't need clar capture state. That is done by library self.
|
||||
*/
|
||||
virtual void Reset() = 0;
|
||||
|
||||
// ===== Basic Infos =====
|
||||
public:
|
||||
/// @brief Check whether this argument specify long name.
|
||||
/// @return True if it is, otherwise false.
|
||||
bool HasLongName() const;
|
||||
/// @brief Get specified long name.
|
||||
/// @return Specified long name.
|
||||
const yycc_u8string& GetLongName() const;
|
||||
/// @brief Check whether this argument specify short name.
|
||||
/// @return True if it is, otherwise false.
|
||||
bool HasShortName() const;
|
||||
/// @brief Get specified short name.
|
||||
/// @return Specified short name.
|
||||
yycc_char8_t GetShortName() const;
|
||||
/// @brief Check whether this argument specify description.
|
||||
/// @return True if it is, otherwise false.
|
||||
bool HasDescription() const;
|
||||
/// @brief Get specified description.
|
||||
/// @return Specified description.
|
||||
const yycc_u8string& GetDescription() const;
|
||||
/// @brief Check whether this argument specify example.
|
||||
/// @return True if it is, otherwise false.
|
||||
bool HasArgumentExample() const;
|
||||
/// @brief Get specified example.
|
||||
/// @return Specified example.
|
||||
const yycc_u8string& GetArgumentExample() const;
|
||||
/// @brief Check whether this argument is optional.
|
||||
/// @return True if it is, otherwise false.
|
||||
bool IsOptional() const;
|
||||
private:
|
||||
yycc_u8string m_LongName;
|
||||
yycc_char8_t m_ShortName;
|
||||
yycc_u8string m_Description;
|
||||
yycc_u8string m_ArgumentExample;
|
||||
bool m_IsOptional;
|
||||
|
||||
// ===== Capture State =====
|
||||
public:
|
||||
/// @brief Check whether this argument has been captured.
|
||||
/// @return True if it is, otherwise false.
|
||||
bool IsCaptured() const;
|
||||
private:
|
||||
/**
|
||||
* @brief Set capture state of this argument.
|
||||
* @details This function is used internally by OptionContext.
|
||||
* @param[in] is_captured New states of captured.
|
||||
*/
|
||||
void SetCaptured(bool is_captured);
|
||||
bool m_IsCaptured;
|
||||
};
|
||||
|
||||
/// @brief The core of argument parser, also manage all arguments.
|
||||
class OptionContext {
|
||||
public:
|
||||
/**
|
||||
* @brief Construct option context.
|
||||
* @param[in] summary The summary of this application which will be printed in help text.
|
||||
* @param[in] description The description of this application which will be printed in help text.
|
||||
* @param[in] arguments The initializer list including pointers to all arguments.
|
||||
*/
|
||||
OptionContext(
|
||||
const yycc_char8_t* summary, const yycc_char8_t* description,
|
||||
std::initializer_list<AbstractArgument*> arguments);
|
||||
~OptionContext();
|
||||
YYCC_DEL_CLS_COPY_MOVE(OptionContext);
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Start a parse.
|
||||
* @param[in] al The reference to ArgumentList for parsing.
|
||||
* @return
|
||||
* True if success, otherwise false.
|
||||
* If this function return false, you should not visit any arguments it managed.
|
||||
*/
|
||||
bool Parse(ArgumentList& al);
|
||||
/**
|
||||
* @brief Reset all managed argument to default state thus you can start another parsing.
|
||||
*/
|
||||
void Reset();
|
||||
/**
|
||||
* @brief Print help text in \c stdout.
|
||||
*/
|
||||
void Help() const;
|
||||
|
||||
private:
|
||||
yycc_u8string m_Summary;
|
||||
yycc_u8string m_Description;
|
||||
|
||||
std::vector<AbstractArgument*> m_Arguments;
|
||||
std::map<yycc_u8string, AbstractArgument*> m_LongNameMap;
|
||||
std::map<yycc_char8_t, AbstractArgument*> m_ShortNameMap;
|
||||
};
|
||||
|
||||
#pragma region Argument Presets
|
||||
|
||||
/**
|
||||
* @brief Arithmetic (integral, floating point. except bool) type argument
|
||||
* @tparam _Ty The internal stored type belongs to arithmetic type.
|
||||
*/
|
||||
template<typename _Ty, std::enable_if_t<std::is_arithmetic_v<_Ty> && !std::is_same_v<_Ty, bool>, int> = 0>
|
||||
class NumberArgument : public AbstractArgument {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor an arithmetic argument
|
||||
* @param[in] long_name The long name of this argument. nullptr if no long name.
|
||||
* @param[in] short_name The short name of this argument. #NO_SHORT_NAME if no short name.
|
||||
* @param[in] description The description of this argument to indroduce what this argument does. nullptr if no description.
|
||||
* @param[in] argument_example The example string of this argument's value. nullptr if no example.
|
||||
* @param[in] is_optional True if this argument is optional argument.
|
||||
* @param[in] constraint The constraint applied to this argument.
|
||||
* @exception std::invalid_argument Given short name or long name are invalid.
|
||||
*/
|
||||
NumberArgument(
|
||||
const yycc_char8_t* long_name, yycc_char8_t short_name,
|
||||
const yycc_char8_t* description = nullptr, const yycc_char8_t* argument_example = nullptr,
|
||||
bool is_optional = false,
|
||||
Constraints::Constraint<_Ty> constraint = Constraints::Constraint<_Ty> {}) :
|
||||
AbstractArgument(long_name, short_name, description, argument_example, is_optional), m_Data(), m_Constraint(constraint) {}
|
||||
virtual ~NumberArgument() {}
|
||||
YYCC_DEL_CLS_COPY_MOVE(NumberArgument);
|
||||
|
||||
public:
|
||||
/// @brief Get stored data in argument.
|
||||
_Ty Get() const {
|
||||
if (!IsCaptured()) throw std::runtime_error("try fetching data from a not captured argument.");
|
||||
return m_Data;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual bool Parse(ArgumentList& al) override {
|
||||
// try get corresponding value
|
||||
yycc_u8string strval;
|
||||
al.Next();
|
||||
if (al.IsEOF() || !al.IsValue(&strval)) {
|
||||
al.Prev();
|
||||
return false;
|
||||
}
|
||||
// try parsing value
|
||||
if (!YYCC::ParserHelper::TryParse<_Ty>(strval, m_Data)) return false;
|
||||
// check constraint
|
||||
if (m_Constraint.IsValid() && !m_Constraint.m_CheckFct(m_Data))
|
||||
return false;
|
||||
// okey
|
||||
return true;
|
||||
}
|
||||
virtual void Reset() override {
|
||||
std::memset(&m_Data, 0, sizeof(m_Data));
|
||||
}
|
||||
|
||||
protected:
|
||||
_Ty m_Data;
|
||||
Constraints::Constraint<_Ty> m_Constraint;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A simple switch type argument which do not store any value.
|
||||
*/
|
||||
class SwitchArgument : public AbstractArgument {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor an switch argument
|
||||
* @param[in] long_name The long name of this argument. nullptr if no long name.
|
||||
* @param[in] short_name The short name of this argument. #NO_SHORT_NAME if no short name.
|
||||
* @param[in] description The description of this argument to indroduce what this argument does. nullptr if no description.
|
||||
* @exception std::invalid_argument Given short name or long name are invalid.
|
||||
*/
|
||||
SwitchArgument(
|
||||
const yycc_char8_t* long_name, yycc_char8_t short_name,
|
||||
const yycc_char8_t* description = nullptr) :
|
||||
// bool switch must be optional, because it is false if no given switch.
|
||||
// bool switch doesn't have argument, so it doesn't have example property.
|
||||
AbstractArgument(long_name, short_name, description, nullptr, true) {}
|
||||
virtual ~SwitchArgument() {}
|
||||
YYCC_DEL_CLS_COPY_MOVE(SwitchArgument);
|
||||
|
||||
protected:
|
||||
virtual bool Parse(ArgumentList& al) override { return true; } // simply return true because no value to store.
|
||||
virtual void Reset() override {} // nothing need to be reset.
|
||||
};
|
||||
|
||||
/// @brief String type argument
|
||||
class StringArgument : public AbstractArgument {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor a string argument
|
||||
* @param[in] long_name The long name of this argument. nullptr if no long name.
|
||||
* @param[in] short_name The short name of this argument. #NO_SHORT_NAME if no short name.
|
||||
* @param[in] description The description of this argument to indroduce what this argument does. nullptr if no description.
|
||||
* @param[in] argument_example The example string of this argument's value. nullptr if no example.
|
||||
* @param[in] is_optional True if this argument is optional argument.
|
||||
* @param[in] constraint The constraint applied to this argument.
|
||||
* @exception std::invalid_argument Given short name or long name are invalid.
|
||||
*/
|
||||
StringArgument(
|
||||
const yycc_char8_t* long_name, yycc_char8_t short_name,
|
||||
const yycc_char8_t* description = nullptr, const yycc_char8_t* argument_example = nullptr,
|
||||
bool is_optional = false,
|
||||
Constraints::Constraint<yycc_u8string> constraint = Constraints::Constraint<yycc_u8string> {}) :
|
||||
AbstractArgument(long_name, short_name, description, argument_example, is_optional), m_Data(), m_Constraint(constraint) {}
|
||||
virtual ~StringArgument() {}
|
||||
YYCC_DEL_CLS_COPY_MOVE(StringArgument);
|
||||
|
||||
public:
|
||||
/// @brief Get stored data in argument.
|
||||
const yycc_u8string& Get() const {
|
||||
if (!IsCaptured()) throw std::runtime_error("try fetching data from a not captured argument.");
|
||||
return m_Data;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual bool Parse(ArgumentList& al) override {
|
||||
// try get corresponding value
|
||||
al.Next();
|
||||
if (al.IsEOF() || !al.IsValue(&m_Data)) {
|
||||
al.Prev();
|
||||
return false;
|
||||
}
|
||||
// check constraint
|
||||
if (m_Constraint.IsValid() && !m_Constraint.m_CheckFct(m_Data))
|
||||
return false;
|
||||
// okey
|
||||
return true;
|
||||
}
|
||||
virtual void Reset() override {
|
||||
m_Data.clear();
|
||||
}
|
||||
|
||||
protected:
|
||||
yycc_u8string m_Data;
|
||||
Constraints::Constraint<yycc_u8string> m_Constraint;
|
||||
};
|
||||
|
||||
#pragma endregion
|
||||
|
||||
|
||||
}
|
||||
186
src/YYCCLegacy/ConfigManager.cpp
Normal file
186
src/YYCCLegacy/ConfigManager.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
#include "ConfigManager.hpp"
|
||||
|
||||
#include "EncodingHelper.hpp"
|
||||
#include "IOHelper.hpp"
|
||||
#include "EnumHelper.hpp"
|
||||
#include <stdexcept>
|
||||
|
||||
namespace YYCC::ConfigManager {
|
||||
|
||||
#pragma region Abstract Setting
|
||||
|
||||
AbstractSetting::AbstractSetting(const yycc_u8string_view& name) : m_Name(name), m_RawData() {
|
||||
if (m_Name.empty())
|
||||
throw std::invalid_argument("the name of setting should not be empty");
|
||||
}
|
||||
|
||||
AbstractSetting::~AbstractSetting() {}
|
||||
|
||||
const yycc_u8string& AbstractSetting::GetName() const { return m_Name; }
|
||||
|
||||
void AbstractSetting::ResizeData(size_t new_size) { m_RawData.resize(new_size); }
|
||||
const void* AbstractSetting::GetDataPtr() const { return m_RawData.data(); }
|
||||
void* AbstractSetting::GetDataPtr() { return m_RawData.data(); }
|
||||
size_t AbstractSetting::GetDataSize() const { return m_RawData.size(); }
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Core Manager
|
||||
|
||||
CoreManager::CoreManager(
|
||||
const yycc_u8string_view& cfg_file_path,
|
||||
uint64_t version_identifier,
|
||||
std::initializer_list<AbstractSetting*> settings) :
|
||||
m_CfgFilePath(cfg_file_path), m_VersionIdentifier(version_identifier), m_Settings() {
|
||||
// Mark: no need to check cfg file path
|
||||
// it will be checked at creating file handle
|
||||
|
||||
// assign settings
|
||||
for (auto* setting : settings) {
|
||||
auto result = m_Settings.try_emplace(setting->GetName(), setting);
|
||||
if (!result.second) {
|
||||
// if not inserted because duplicated, raise exception
|
||||
throw std::invalid_argument("Duplicated setting name");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ConfigLoadResult CoreManager::Load() {
|
||||
// prepare result variables
|
||||
ConfigLoadResult ret = ConfigLoadResult::OK;
|
||||
|
||||
// reset all settings first
|
||||
Reset();
|
||||
|
||||
// get file handle
|
||||
IOHelper::SmartStdFile fs(IOHelper::UTF8FOpen(m_CfgFilePath.c_str(), YYCC_U8("rb")));
|
||||
if (fs.get() == nullptr) {
|
||||
// if we fail to get, it means that we do not have corresponding cfg file.
|
||||
// all settings should be reset to default value.
|
||||
ret = ConfigLoadResult::Created;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// fetch version info
|
||||
uint64_t version_info;
|
||||
if (std::fread(&version_info, 1u, sizeof(version_info), fs.get()) != sizeof(version_info)) {
|
||||
ret = ConfigLoadResult::Created;
|
||||
return ret;
|
||||
}
|
||||
// check version
|
||||
// if read version is greater than we expected,
|
||||
// it means that this cfg file is created by the program higer than this.
|
||||
// we should not read anything from it.
|
||||
// however, for compaitibility reason, we allow read old cfg data.
|
||||
if (version_info > m_VersionIdentifier) {
|
||||
ret = ConfigLoadResult::ForwardNew;
|
||||
return ret;
|
||||
} else if (version_info < m_VersionIdentifier) {
|
||||
EnumHelper::Add(ret, ConfigLoadResult::Migrated);
|
||||
}
|
||||
|
||||
// fetch setting item from file
|
||||
yycc_u8string name_cache;
|
||||
while (true) {
|
||||
// try fetch setting name
|
||||
// fetch name length
|
||||
size_t name_length;
|
||||
if (std::fread(&name_length, 1u, sizeof(name_length), fs.get()) != sizeof(name_length)) {
|
||||
// we also check whether reach EOF at there.
|
||||
if (std::feof(fs.get())) break;
|
||||
else {
|
||||
EnumHelper::Add(ret, ConfigLoadResult::BrokenFile);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
// fetch name body
|
||||
name_cache.resize(name_length);
|
||||
if (std::fread(name_cache.data(), 1u, name_length, fs.get()) != name_length) {
|
||||
EnumHelper::Add(ret, ConfigLoadResult::BrokenFile);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// get setting data length
|
||||
size_t data_length;
|
||||
if (std::fread(&data_length, 1u, sizeof(data_length), fs.get()) != sizeof(data_length)) {
|
||||
EnumHelper::Add(ret, ConfigLoadResult::BrokenFile);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// get matched setting first
|
||||
const auto& found = m_Settings.find(name_cache);
|
||||
if (found != m_Settings.end()) {
|
||||
// found. read data for it
|
||||
found->second->ResizeData(data_length);
|
||||
if (std::fread(found->second->GetDataPtr(), 1u, data_length, fs.get()) != data_length) {
|
||||
EnumHelper::Add(ret, ConfigLoadResult::BrokenFile);
|
||||
return ret;
|
||||
}
|
||||
// call user defined load function
|
||||
// if fail to parse, reset to default value
|
||||
if (!found->second->UserLoad()) {
|
||||
EnumHelper::Add(ret, ConfigLoadResult::ItemError);
|
||||
found->second->UserReset();
|
||||
}
|
||||
} else {
|
||||
// fail to find. skip this unknown setting
|
||||
if (fseek(fs.get(), static_cast<long>(data_length), SEEK_CUR) != 0) {
|
||||
EnumHelper::Add(ret, ConfigLoadResult::BrokenFile);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool CoreManager::Save() {
|
||||
// get file handle
|
||||
IOHelper::SmartStdFile fs(IOHelper::UTF8FOpen(m_CfgFilePath.c_str(), YYCC_U8("wb")));
|
||||
// if we fail to get, return false.
|
||||
if (fs == nullptr) return false;
|
||||
|
||||
// write config data
|
||||
uint64_t version_info = m_VersionIdentifier;
|
||||
if (std::fwrite(&version_info, 1u, sizeof(version_info), fs.get()) != sizeof(version_info))
|
||||
return false;
|
||||
|
||||
// iterate all data for writing
|
||||
for (const auto& pair : m_Settings) {
|
||||
// do user defined save
|
||||
// if failed, skip this setting
|
||||
if (!pair.second->UserSave())
|
||||
continue;
|
||||
|
||||
// write setting name
|
||||
// write name length
|
||||
size_t name_length = pair.first.size();
|
||||
if (std::fwrite(&name_length, 1u, sizeof(name_length), fs.get()) != sizeof(name_length))
|
||||
return false;
|
||||
// write name body
|
||||
if (std::fwrite(pair.first.c_str(), 1u, name_length, fs.get()) != name_length)
|
||||
return false;
|
||||
|
||||
// write setting daat
|
||||
// write data length
|
||||
size_t data_length = pair.second->GetDataSize();
|
||||
if (std::fwrite(&data_length, 1u, sizeof(data_length), fs.get()) != sizeof(data_length))
|
||||
return false;
|
||||
// write data body
|
||||
if (std::fwrite(pair.second->GetDataPtr(), 1u, data_length, fs.get()) != data_length)
|
||||
return false;
|
||||
}
|
||||
|
||||
// all settings done, return true
|
||||
return true;
|
||||
}
|
||||
|
||||
void CoreManager::Reset() {
|
||||
for (const auto& pair : m_Settings) {
|
||||
pair.second->UserReset();
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
}
|
||||
269
src/YYCCLegacy/ConfigManager.hpp
Normal file
269
src/YYCCLegacy/ConfigManager.hpp
Normal file
@@ -0,0 +1,269 @@
|
||||
#pragma once
|
||||
#include "YYCCInternal.hpp"
|
||||
|
||||
#include "Constraints.hpp"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <initializer_list>
|
||||
#include <type_traits>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <stdexcept>
|
||||
#include <cstring>
|
||||
|
||||
/**
|
||||
* @brief Universal configuration manager
|
||||
* @details For how to use this namespace, please see \ref config_manager.
|
||||
*/
|
||||
namespace YYCC::ConfigManager {
|
||||
|
||||
/**
|
||||
* @brief The load result of loading config.
|
||||
*/
|
||||
enum class ConfigLoadResult {
|
||||
OK = 0, ///< Success load configs.
|
||||
Created = 1 << 0, ///< Given file is not existing, we create all configs in default values.
|
||||
ForwardNew = 1 << 1, ///< Detect the config file created by higher version. We create all configs in default values.
|
||||
Migrated = 1 << 2, ///< Detect the config file created by lower version. We try migrate configs written in it.
|
||||
BrokenFile = 1 << 3, ///< Given file has bad format. Thus some configs are kept as its default values.
|
||||
ItemError = 1 << 4 ///< Some config can not be recognized from the data read from file so they are reset to default value.
|
||||
};
|
||||
using UnderlyingConfigLoadResult_t = std::underlying_type_t<ConfigLoadResult>;
|
||||
|
||||
/// @brief The base class of every setting.
|
||||
/// @details Programmer can inherit this class and implement essential functions to create custom setting.
|
||||
class AbstractSetting {
|
||||
friend class CoreManager;
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a setting
|
||||
* @param[in] name The name of this setting.
|
||||
* @exception std::invalid_argument Name of setting is empty.
|
||||
*/
|
||||
AbstractSetting(const yycc_u8string_view& name);
|
||||
virtual ~AbstractSetting();
|
||||
YYCC_DEL_CLS_COPY_MOVE(AbstractSetting);
|
||||
|
||||
// Name interface
|
||||
public:
|
||||
/// @brief Get name of this setting.
|
||||
/// @details Name was used in storing setting in file.
|
||||
const yycc_u8string& GetName() const;
|
||||
private:
|
||||
yycc_u8string m_Name;
|
||||
|
||||
// User Implementations
|
||||
protected:
|
||||
/// @brief User implemented custom load function
|
||||
/// @remarks
|
||||
/// In this function, programmer should read data from internal buffer
|
||||
/// and store it to its own another internal variables.
|
||||
/// @return True if success, otherwise false.
|
||||
virtual bool UserLoad() = 0;
|
||||
/// @brief User implemented custom save function
|
||||
/// @remarks
|
||||
/// In this function, programmer should write data,
|
||||
/// which is stored in another variavle by it own, to internal buffer.
|
||||
/// @return True if success, otherwise false.
|
||||
virtual bool UserSave() = 0;
|
||||
/// @brief User implemented custom reset function
|
||||
/// @remarks In this function, programmer should reset its internal variable to default value.
|
||||
virtual void UserReset() = 0;
|
||||
|
||||
// Buffer related functions
|
||||
protected:
|
||||
/// @brief Resize internal buffer to given size.
|
||||
/// @remarks It is usually used in UserSave.
|
||||
/// @param[in] new_size The new size of internal buffer.
|
||||
void ResizeData(size_t new_size);
|
||||
/// @brief Get data pointer to internal buffer.
|
||||
/// @remarks It is usually used in UserLoad.
|
||||
const void* GetDataPtr() const;
|
||||
/// @brief Get mutable data pointer to internal buffer.
|
||||
/// @remarks It is usually used in UserSave.
|
||||
void* GetDataPtr();
|
||||
/// @brief Get the length of internal buffer.
|
||||
size_t GetDataSize() const;
|
||||
private:
|
||||
std::vector<uint8_t> m_RawData;
|
||||
};
|
||||
|
||||
/// @brief Settings manager and config file reader writer.
|
||||
class CoreManager {
|
||||
public:
|
||||
/**
|
||||
* @brief Build core manager.
|
||||
* @param[in] cfg_file_path The path to config file.
|
||||
* @param[in] version_identifier The identifier of version. Higher is newer. Lower config will try doing migration.
|
||||
* @param[in] settings An initializer list containing pointers to all managed settings.
|
||||
*/
|
||||
CoreManager(
|
||||
const yycc_u8string_view& cfg_file_path,
|
||||
uint64_t version_identifier,
|
||||
std::initializer_list<AbstractSetting*> settings);
|
||||
~CoreManager() {}
|
||||
YYCC_DEL_CLS_COPY_MOVE(CoreManager);
|
||||
|
||||
// Core functions
|
||||
public:
|
||||
/// @brief Load settings from file.
|
||||
/// @details Before loading, all settings will be reset to default value first.
|
||||
/// @return What happend when loading config. This function always success.
|
||||
ConfigLoadResult Load();
|
||||
/// @brief Save settings to file.
|
||||
/// @return True if success, otherwise false.
|
||||
bool Save();
|
||||
/// @brief Reset all settings to default value.
|
||||
void Reset();
|
||||
|
||||
private:
|
||||
yycc_u8string m_CfgFilePath;
|
||||
uint64_t m_VersionIdentifier;
|
||||
std::map<yycc_u8string, AbstractSetting*> m_Settings;
|
||||
};
|
||||
|
||||
#pragma region Setting Presets
|
||||
|
||||
/**
|
||||
* @brief Arithmetic (integral, floating point, bool) and enum type setting
|
||||
* @tparam _Ty The internal stored type belongs to arithmetic type.
|
||||
*/
|
||||
template<typename _Ty, std::enable_if_t<std::is_arithmetic_v<_Ty> || std::is_enum_v<_Ty>, int> = 0>
|
||||
class NumberSetting : public AbstractSetting {
|
||||
public:
|
||||
/**
|
||||
* @brief Construct arithmetic type setting.
|
||||
* @param[in] name The name of this setting.
|
||||
* @param[in] default_value The default value of this setting.
|
||||
* @param[in] constraint The constraint applied to this setting.
|
||||
* @exception std::invalid_argument Name of setting is empty.
|
||||
*/
|
||||
NumberSetting(
|
||||
const yycc_u8string_view& name, _Ty default_value,
|
||||
Constraints::Constraint<_Ty> constraint = Constraints::Constraint<_Ty> {}) :
|
||||
AbstractSetting(name), m_Data(default_value), m_DefaultData(default_value), m_Constraint(constraint) {}
|
||||
virtual ~NumberSetting() {}
|
||||
YYCC_DEL_CLS_COPY_MOVE(NumberSetting);
|
||||
|
||||
/// @brief Get stored data in setting.
|
||||
_Ty Get() const { return m_Data; }
|
||||
/**
|
||||
* @brief Set data to setting.
|
||||
* @param[in] new_data The new data.
|
||||
* @return True if success, otherwise false (given value is invalid)
|
||||
*/
|
||||
bool Set(_Ty new_data) {
|
||||
// validate data
|
||||
if (m_Constraint.IsValid() && !m_Constraint.m_CheckFct(new_data))
|
||||
return false;
|
||||
// assign data
|
||||
m_Data = new_data;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual bool UserLoad() override {
|
||||
// read data
|
||||
if (sizeof(m_Data) != GetDataSize())
|
||||
return false;
|
||||
m_Data = *reinterpret_cast<const _Ty*>(GetDataPtr());
|
||||
// check data
|
||||
if (m_Constraint.IsValid() && !m_Constraint.m_CheckFct(m_Data))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
virtual bool UserSave() override {
|
||||
// write data
|
||||
ResizeData(sizeof(m_Data));
|
||||
*reinterpret_cast<_Ty*>(GetDataPtr()) = m_Data;
|
||||
return true;
|
||||
}
|
||||
virtual void UserReset() override {
|
||||
m_Data = m_DefaultData;
|
||||
}
|
||||
|
||||
_Ty m_Data, m_DefaultData;
|
||||
Constraints::Constraint<_Ty> m_Constraint;
|
||||
};
|
||||
|
||||
/// @brief String type setting
|
||||
class StringSetting : public AbstractSetting {
|
||||
public:
|
||||
/**
|
||||
* @brief Construct string setting
|
||||
* @param[in] name The name of this setting.
|
||||
* @param[in] default_value The default value of this setting.
|
||||
* @param[in] constraint The constraint applied to this setting.
|
||||
* @exception std::invalid_argument Name of setting is empty.
|
||||
*/
|
||||
StringSetting(
|
||||
const yycc_u8string_view& name, const yycc_u8string_view& default_value,
|
||||
Constraints::Constraint<yycc_u8string> constraint = Constraints::Constraint<yycc_u8string> {}) :
|
||||
AbstractSetting(name), m_Data(), m_DefaultData(), m_Constraint(constraint) {
|
||||
m_Data = default_value;
|
||||
m_DefaultData = default_value;
|
||||
}
|
||||
virtual ~StringSetting() {}
|
||||
YYCC_DEL_CLS_COPY_MOVE(StringSetting);
|
||||
|
||||
/// @brief Get reference to stored string.
|
||||
const yycc_u8string& Get() const { return m_Data; }
|
||||
/**
|
||||
* @brief Set string data to setting.
|
||||
* @param[in] new_data The new string data.
|
||||
* @return True if success, otherwise false (given value is invalid)
|
||||
*/
|
||||
bool Set(const yycc_u8string_view& new_data) {
|
||||
// check data validation
|
||||
yycc_u8string new_data_cache(new_data);
|
||||
if (m_Constraint.IsValid() && !m_Constraint.m_CheckFct(new_data_cache))
|
||||
return false;
|
||||
// assign data
|
||||
m_Data = std::move(new_data_cache);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual bool UserLoad() override {
|
||||
// read string length
|
||||
size_t string_length;
|
||||
if (GetDataSize() < sizeof(string_length))
|
||||
return false;
|
||||
string_length = *reinterpret_cast<const size_t*>(GetDataPtr());
|
||||
// read string body
|
||||
if (GetDataSize() != sizeof(string_length) + string_length)
|
||||
return false;
|
||||
m_Data.assign(
|
||||
reinterpret_cast<const yycc_char8_t*>(static_cast<const uint8_t*>(GetDataPtr()) + sizeof(string_length)),
|
||||
string_length
|
||||
);
|
||||
// check data
|
||||
if (m_Constraint.IsValid() && !m_Constraint.m_CheckFct(m_Data))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
virtual bool UserSave() override {
|
||||
// allocate result buffer
|
||||
size_t string_length = m_Data.size();
|
||||
ResizeData(sizeof(string_length) + string_length);
|
||||
// get pointer
|
||||
uint8_t* ptr = static_cast<uint8_t*>(GetDataPtr());
|
||||
// assign string length
|
||||
*reinterpret_cast<size_t*>(ptr) = string_length;
|
||||
// assign string body
|
||||
std::memcpy(ptr + sizeof(string_length), m_Data.data(), string_length);
|
||||
return true;
|
||||
}
|
||||
virtual void UserReset() override {
|
||||
m_Data = m_DefaultData;
|
||||
}
|
||||
|
||||
yycc_u8string m_Data, m_DefaultData;
|
||||
Constraints::Constraint<yycc_u8string> m_Constraint;
|
||||
};
|
||||
|
||||
#pragma endregion
|
||||
|
||||
|
||||
}
|
||||
282
src/YYCCLegacy/ConsoleHelper.cpp
Normal file
282
src/YYCCLegacy/ConsoleHelper.cpp
Normal file
@@ -0,0 +1,282 @@
|
||||
#include "ConsoleHelper.hpp"
|
||||
|
||||
#include "EncodingHelper.hpp"
|
||||
#include "StringHelper.hpp"
|
||||
#include <iostream>
|
||||
|
||||
// Include Windows used headers in Windows.
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
#include "WinImportPrefix.hpp"
|
||||
#include <Windows.h>
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
#include "WinImportSuffix.hpp"
|
||||
#endif
|
||||
|
||||
namespace YYCC::ConsoleHelper {
|
||||
|
||||
#pragma region Windows Specific Functions
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
|
||||
static bool RawEnableColorfulConsole(FILE* fs) {
|
||||
if (!_isatty(_fileno(fs))) return false;
|
||||
|
||||
HANDLE h_output;
|
||||
DWORD dw_mode;
|
||||
|
||||
h_output = (HANDLE)_get_osfhandle(_fileno(fs));
|
||||
if (!GetConsoleMode(h_output, &dw_mode)) return false;
|
||||
if (!SetConsoleMode(h_output, dw_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_PROCESSED_OUTPUT)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
Reference:
|
||||
* https://stackoverflow.com/questions/45575863/how-to-print-utf-8-strings-to-stdcout-on-windows
|
||||
* https://stackoverflow.com/questions/69830460/reading-utf-8-input
|
||||
|
||||
There is 3 way to make Windows console enable UTF8 mode.
|
||||
|
||||
First one is calling SetConsoleCP and SetConsoleOutputCP.
|
||||
The side effect of this is std::cin and std::cout is broken,
|
||||
however there is a patch for this issue.
|
||||
|
||||
Second one is calling _set_mode with _O_U8TEXT or _O_U16TEXT to enable Unicode mode for Windows console.
|
||||
This also have side effect which is stronger than first one.
|
||||
All puts family functions (ASCII-based output functions) will throw assertion exception.
|
||||
You only can use putws family functions (wide-char-based output functions).
|
||||
However these functions can not be used without calling _set_mode in Windows design.
|
||||
|
||||
There still is another method, using WriteConsoleW directly visiting console.
|
||||
This function family can output correct string without calling any extra functions!
|
||||
This method is what we adopted.
|
||||
*/
|
||||
|
||||
template<bool _bIsConsole>
|
||||
static yycc_u8string WinConsoleRead(HANDLE hStdIn) {
|
||||
using _TChar = std::conditional_t<_bIsConsole, wchar_t, char>;
|
||||
|
||||
// Prepare an internal buffer because the read data may not be fully used.
|
||||
// For example, we may read x\ny in a single calling but after processing \n, this function will return
|
||||
// so y will temporarily stored in this internal buffer for next using.
|
||||
// Thus this function is not thread safe.
|
||||
static std::basic_string<_TChar> internal_buffer;
|
||||
// create return value buffer
|
||||
std::basic_string<_TChar> return_buffer;
|
||||
|
||||
// Prepare some variables
|
||||
DWORD dwReadNumberOfChars;
|
||||
_TChar szReadChars[64];
|
||||
size_t eol_pos;
|
||||
|
||||
// try fetching EOL
|
||||
while (true) {
|
||||
// if internal buffer is empty,
|
||||
// try fetching it.
|
||||
if (internal_buffer.empty()) {
|
||||
// console and non-console use different method to read.
|
||||
if constexpr (_bIsConsole) {
|
||||
// console handle, use ReadConsoleW.
|
||||
// read from console, the read data is wchar based
|
||||
if (!ReadConsoleW(hStdIn, szReadChars, sizeof(szReadChars) / sizeof(_TChar), &dwReadNumberOfChars, NULL))
|
||||
break;
|
||||
} else {
|
||||
// anything else, use ReadFile instead.
|
||||
// the read data is utf8 based
|
||||
if (!ReadFile(hStdIn, szReadChars, sizeof(szReadChars), &dwReadNumberOfChars, NULL))
|
||||
break;
|
||||
}
|
||||
|
||||
// send to internal buffer
|
||||
if (dwReadNumberOfChars == 0) break;
|
||||
internal_buffer.append(szReadChars, dwReadNumberOfChars);
|
||||
}
|
||||
|
||||
// try finding EOL in internal buffer
|
||||
if constexpr (std::is_same_v<_TChar, char>) eol_pos = internal_buffer.find_first_of('\n');
|
||||
else eol_pos = internal_buffer.find_first_of(L'\n');
|
||||
// check finding result
|
||||
if (eol_pos == std::wstring::npos) {
|
||||
// the whole string do not include EOL, fully appended to return value
|
||||
return_buffer += internal_buffer;
|
||||
internal_buffer.clear();
|
||||
// need more data, continue while
|
||||
} else {
|
||||
// split result
|
||||
// push into result and remain some in internal buffer.
|
||||
return_buffer.append(internal_buffer, 0u, eol_pos);
|
||||
internal_buffer.erase(0u, eol_pos + 1u); // +1 because EOL take one place.
|
||||
// break while mean success finding
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// post-process for return value
|
||||
yycc_u8string real_return_buffer;
|
||||
if constexpr (_bIsConsole) {
|
||||
// console mode need convert wchar to utf8
|
||||
YYCC::EncodingHelper::WcharToUTF8(return_buffer, real_return_buffer);
|
||||
} else {
|
||||
// non-console just copt the result
|
||||
real_return_buffer = EncodingHelper::ToUTF8(return_buffer);
|
||||
}
|
||||
// every mode need delete \r words
|
||||
YYCC::StringHelper::Replace(real_return_buffer, YYCC_U8("\r"), YYCC_U8(""));
|
||||
// return value
|
||||
return real_return_buffer;
|
||||
}
|
||||
|
||||
static void WinConsoleWrite(const yycc_u8string& strl, bool to_stderr) {
|
||||
// Prepare some Win32 variables
|
||||
// fetch stdout handle first
|
||||
HANDLE hStdOut = GetStdHandle(to_stderr ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE);
|
||||
DWORD dwConsoleMode;
|
||||
DWORD dwWrittenNumberOfChars;
|
||||
|
||||
// if stdout was redirected, this handle may point to a file handle or anything else,
|
||||
// WriteConsoleW can not write data into such scenario, so we need check whether this handle is console handle
|
||||
if (GetConsoleMode(hStdOut, &dwConsoleMode)) {
|
||||
// console handle, use WriteConsoleW.
|
||||
// convert utf8 string to wide char first
|
||||
std::wstring wstrl(YYCC::EncodingHelper::UTF8ToWchar(strl));
|
||||
size_t wstrl_size = wstrl.size();
|
||||
// write string with size check
|
||||
if (wstrl_size <= std::numeric_limits<DWORD>::max()) {
|
||||
WriteConsoleW(hStdOut, wstrl.c_str(), static_cast<DWORD>(wstrl_size), &dwWrittenNumberOfChars, NULL);
|
||||
}
|
||||
} else {
|
||||
// anything else, use WriteFile instead.
|
||||
// WriteFile do not need extra convertion, because it is direct writing.
|
||||
// check whether string length is overflow
|
||||
size_t strl_size = strl.size() * sizeof(yycc_u8string::value_type);
|
||||
// write string with size check
|
||||
if (strl_size <= std::numeric_limits<DWORD>::max()) {
|
||||
WriteFile(hStdOut, strl.c_str(), static_cast<DWORD>(strl_size), &dwWrittenNumberOfChars, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#pragma endregion
|
||||
|
||||
bool EnableColorfulConsole() {
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
|
||||
bool ret = true;
|
||||
ret &= RawEnableColorfulConsole(stdout);
|
||||
ret &= RawEnableColorfulConsole(stderr);
|
||||
return ret;
|
||||
|
||||
#else
|
||||
|
||||
// just return true and do nothing
|
||||
return true;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
yycc_u8string ReadLine() {
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
|
||||
// get stdin mode
|
||||
HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
|
||||
// use different method to get according to whether stdin is redirected
|
||||
DWORD dwConsoleMode;
|
||||
if (GetConsoleMode(hStdIn, &dwConsoleMode)) {
|
||||
return WinConsoleRead<true>(hStdIn);
|
||||
} else {
|
||||
return WinConsoleRead<false>(hStdIn);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// in linux, directly use C++ function to fetch.
|
||||
std::string cmd;
|
||||
if (std::getline(std::cin, cmd).fail()) cmd.clear();
|
||||
return EncodingHelper::ToUTF8(cmd);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
template<bool bNeedFmt, bool bIsErr, bool bHasEOL>
|
||||
static void RawWrite(const yycc_char8_t* u8_fmt, va_list argptr) {
|
||||
// Buiild string need to be written first
|
||||
// If no format string or plain string for writing, return.
|
||||
if (u8_fmt == nullptr) return;
|
||||
// Build or simply copy string
|
||||
yycc_u8string strl;
|
||||
if constexpr (bNeedFmt) {
|
||||
// treat as format string
|
||||
va_list argcpy;
|
||||
va_copy(argcpy, argptr);
|
||||
strl = YYCC::StringHelper::VPrintf(u8_fmt, argcpy);
|
||||
va_end(argcpy);
|
||||
} else {
|
||||
// treat as plain string
|
||||
strl = u8_fmt;
|
||||
}
|
||||
// Checkout whether add EOL
|
||||
if constexpr (bHasEOL) {
|
||||
strl += YYCC_U8("\n");
|
||||
}
|
||||
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
// call Windows specific writer
|
||||
WinConsoleWrite(strl, bIsErr);
|
||||
#else
|
||||
// in linux, directly use C function to write.
|
||||
std::fputs(EncodingHelper::ToOrdinary(strl.c_str()), bIsErr ? stderr : stdout);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Format(const yycc_char8_t* u8_fmt, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, u8_fmt);
|
||||
RawWrite<true, false, false>(u8_fmt, argptr);
|
||||
va_end(argptr);
|
||||
}
|
||||
|
||||
void FormatLine(const yycc_char8_t* u8_fmt, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, u8_fmt);
|
||||
RawWrite<true, false, true>(u8_fmt, argptr);
|
||||
va_end(argptr);
|
||||
}
|
||||
|
||||
void Write(const yycc_char8_t* u8_strl) {
|
||||
va_list empty{};
|
||||
RawWrite<false, false, false>(u8_strl, empty);
|
||||
}
|
||||
|
||||
void WriteLine(const yycc_char8_t* u8_strl) {
|
||||
va_list empty{};
|
||||
RawWrite<false, false, true>(u8_strl, empty);
|
||||
}
|
||||
|
||||
void ErrFormat(const yycc_char8_t* u8_fmt, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, u8_fmt);
|
||||
RawWrite<true, true, false>(u8_fmt, argptr);
|
||||
va_end(argptr);
|
||||
}
|
||||
|
||||
void ErrFormatLine(const yycc_char8_t* u8_fmt, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, u8_fmt);
|
||||
RawWrite<true, true, true>(u8_fmt, argptr);
|
||||
va_end(argptr);
|
||||
}
|
||||
|
||||
void ErrWrite(const yycc_char8_t* u8_strl) {
|
||||
va_list empty{};
|
||||
RawWrite<false, true, false>(u8_strl, empty);
|
||||
}
|
||||
|
||||
void ErrWriteLine(const yycc_char8_t* u8_strl) {
|
||||
va_list empty{};
|
||||
RawWrite<false, true, true>(u8_strl, empty);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
92
src/YYCCLegacy/ConsoleHelper.hpp
Normal file
92
src/YYCCLegacy/ConsoleHelper.hpp
Normal file
@@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
#include "YYCCInternal.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* @brief The helper providing universal C\# style console function and other console related stuff
|
||||
* @details
|
||||
* For how to utilize this functions provided by this namespace, please view \ref console_helper.
|
||||
*/
|
||||
namespace YYCC::ConsoleHelper {
|
||||
|
||||
/**
|
||||
* @brief Enable console color support for Windows.
|
||||
* @details This actually is enable virtual console feature for \c stdout and \c stderr.
|
||||
* @return True if success, otherwise false.
|
||||
* @remarks
|
||||
* This function only works on Windows and do nothing on other platforms such as Linux,
|
||||
* because we assume all terminals existing on other platform support color feature as default.
|
||||
*/
|
||||
bool EnableColorfulConsole();
|
||||
|
||||
/**
|
||||
* @brief Reads the next line of UTF8 characters from the standard input stream.
|
||||
* @return
|
||||
* The next line of UTF8 characters from the input stream.
|
||||
* Empty string if user just press Enter key or function failed.
|
||||
*/
|
||||
yycc_u8string ReadLine();
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Writes the text representation of the specified object
|
||||
* to the standard output stream using the specified format information.
|
||||
* @param[in] u8_fmt The format string.
|
||||
* @param[in] ... The arguments of format string.
|
||||
*/
|
||||
void Format(const yycc_char8_t* u8_fmt, ...);
|
||||
/**
|
||||
* @brief
|
||||
* Writes the text representation of the specified object,
|
||||
* followed by the current line terminator,
|
||||
* to the standard output stream using the specified format information.
|
||||
* @param[in] u8_fmt The format string.
|
||||
* @param[in] ... The arguments of format string.
|
||||
*/
|
||||
void FormatLine(const yycc_char8_t* u8_fmt, ...);
|
||||
/**
|
||||
* @brief Writes the specified string value to the standard output stream.
|
||||
* @param[in] u8_strl The value to write.
|
||||
*/
|
||||
void Write(const yycc_char8_t* u8_strl);
|
||||
/**
|
||||
* @brief
|
||||
* Writes the specified string value, followed by the current line terminator,
|
||||
* to the standard output stream.
|
||||
* @param[in] u8_strl The value to write.
|
||||
*/
|
||||
void WriteLine(const yycc_char8_t* u8_strl);
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Writes the text representation of the specified object
|
||||
* to the standard error stream using the specified format information.
|
||||
* @param[in] u8_fmt The format string.
|
||||
* @param[in] ... The arguments of format string.
|
||||
*/
|
||||
void ErrFormat(const yycc_char8_t* u8_fmt, ...);
|
||||
/**
|
||||
* @brief
|
||||
* Writes the text representation of the specified object,
|
||||
* followed by the current line terminator,
|
||||
* to the standard error stream using the specified format information.
|
||||
* @param[in] u8_fmt The format string.
|
||||
* @param[in] ... The arguments of format string.
|
||||
*/
|
||||
void ErrFormatLine(const yycc_char8_t* u8_fmt, ...);
|
||||
/**
|
||||
* @brief Writes the specified string value to the standard error stream.
|
||||
* @param[in] u8_strl The value to write.
|
||||
*/
|
||||
void ErrWrite(const yycc_char8_t* u8_strl);
|
||||
/**
|
||||
* @brief
|
||||
* Writes the specified string value, followed by the current line terminator,
|
||||
* to the standard error stream.
|
||||
* @param[in] u8_strl The value to write.
|
||||
*/
|
||||
void ErrWriteLine(const yycc_char8_t* u8_strl);
|
||||
|
||||
}
|
||||
378
src/YYCCLegacy/DialogHelper.cpp
Normal file
378
src/YYCCLegacy/DialogHelper.cpp
Normal file
@@ -0,0 +1,378 @@
|
||||
#include "DialogHelper.hpp"
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
|
||||
#include "EncodingHelper.hpp"
|
||||
#include "StringHelper.hpp"
|
||||
|
||||
namespace YYCC::DialogHelper {
|
||||
|
||||
#pragma region FileFilters
|
||||
|
||||
bool FileFilters::Add(const yycc_char8_t* filter_name, std::initializer_list<const yycc_char8_t*> il) {
|
||||
// assign filter name
|
||||
if (filter_name == nullptr) return false;
|
||||
FilterName name(filter_name);
|
||||
|
||||
// assign filter patterns
|
||||
FilterModes modes;
|
||||
for (const yycc_char8_t* pattern : il) {
|
||||
if (pattern != nullptr)
|
||||
modes.emplace_back(yycc_u8string(pattern));
|
||||
}
|
||||
|
||||
// check filter patterns
|
||||
if (modes.empty()) return false;
|
||||
|
||||
// add into pairs and return
|
||||
m_Filters.emplace_back(std::make_pair(std::move(name), std::move(modes)));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileFilters::Generate(WinFileFilters& win_result) const {
|
||||
// clear Windows oriented data
|
||||
win_result.Clear();
|
||||
|
||||
// build new Windows oriented string vector first
|
||||
for (const auto& it : m_Filters) {
|
||||
// convert name to wchar
|
||||
WinFileFilters::WinFilterName name;
|
||||
if (!YYCC::EncodingHelper::UTF8ToWchar(it.first, name))
|
||||
return false;
|
||||
|
||||
// convert pattern and join them
|
||||
const auto& filter_modes = it.second;
|
||||
yycc_u8string joined_modes(YYCC::StringHelper::Join(filter_modes.begin(), filter_modes.end(), YYCC_U8(";")));
|
||||
WinFileFilters::WinFilterModes modes;
|
||||
if (!YYCC::EncodingHelper::UTF8ToWchar(joined_modes, modes))
|
||||
return false;
|
||||
|
||||
// append new pair
|
||||
win_result.m_WinFilters.emplace_back(std::make_pair(name, modes));
|
||||
}
|
||||
|
||||
// check filter size
|
||||
// if it overflow the maximum value, return false
|
||||
size_t count = win_result.m_WinFilters.size();
|
||||
if (count > std::numeric_limits<UINT>::max())
|
||||
return false;
|
||||
|
||||
// create new win data struct
|
||||
// and assign string pointer from internal built win string vector.
|
||||
win_result.m_WinDataStruct.reset(new COMDLG_FILTERSPEC[count]);
|
||||
for (size_t i = 0u; i < count; ++i) {
|
||||
win_result.m_WinDataStruct[i].pszName = win_result.m_WinFilters[i].first.c_str();
|
||||
win_result.m_WinDataStruct[i].pszSpec = win_result.m_WinFilters[i].second.c_str();
|
||||
}
|
||||
|
||||
// everything is okey
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region File Dialog
|
||||
|
||||
bool FileDialog::Generate(WinFileDialog& win_result) const {
|
||||
// clear Windows oriented data
|
||||
win_result.Clear();
|
||||
|
||||
// set owner
|
||||
win_result.m_WinOwner = m_Owner;
|
||||
|
||||
// build file filters
|
||||
if (!m_FileTypes.Generate(win_result.m_WinFileTypes))
|
||||
return false;
|
||||
|
||||
// check default file type index
|
||||
// check value overflow (comparing with >= because we need plus 1 for file type index later)
|
||||
if (m_DefaultFileTypeIndex >= std::numeric_limits<UINT>::max())
|
||||
return false;
|
||||
// check invalid index (overflow the length or registered file types if there is file type)
|
||||
if (m_FileTypes.Count() != 0u && m_DefaultFileTypeIndex >= m_FileTypes.Count())
|
||||
return false;
|
||||
// set index with additional plus according to Windows specification.
|
||||
win_result.m_WinDefaultFileTypeIndex = static_cast<UINT>(m_DefaultFileTypeIndex + 1);
|
||||
|
||||
// build title and init file name
|
||||
if (m_HasTitle) {
|
||||
if (!YYCC::EncodingHelper::UTF8ToWchar(m_Title, win_result.m_WinTitle))
|
||||
return false;
|
||||
win_result.m_HasTitle = true;
|
||||
}
|
||||
if (m_HasInitFileName) {
|
||||
if (!YYCC::EncodingHelper::UTF8ToWchar(m_InitFileName, win_result.m_WinInitFileName))
|
||||
return false;
|
||||
win_result.m_HasInitFileName = true;
|
||||
}
|
||||
|
||||
// fetch init directory
|
||||
if (m_HasInitDirectory) {
|
||||
// convert to wpath
|
||||
std::wstring w_init_directory;
|
||||
if (!YYCC::EncodingHelper::UTF8ToWchar(m_InitDirectory, w_init_directory))
|
||||
return false;
|
||||
|
||||
// fetch IShellItem*
|
||||
// Ref: https://stackoverflow.com/questions/76306324/how-to-set-default-folder-for-ifileopendialog-interface
|
||||
IShellItem* init_directory = NULL;
|
||||
HRESULT hr = SHCreateItemFromParsingName(w_init_directory.c_str(), NULL, IID_PPV_ARGS(&init_directory));
|
||||
if (FAILED(hr)) return false;
|
||||
|
||||
// assign IShellItem*
|
||||
win_result.m_WinInitDirectory.reset(init_directory);
|
||||
}
|
||||
|
||||
// everything is okey
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Windows Dialog Code
|
||||
|
||||
enum class CommonFileDialogType {
|
||||
OpenFile,
|
||||
OpenMultipleFiles,
|
||||
SaveFile,
|
||||
OpenFolder
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Extract display name from given IShellItem*.
|
||||
* @param item[in] The pointer to IShellItem for extracting.
|
||||
* @param ret[out] Extracted display name container.
|
||||
* @return True if success, otherwise false.
|
||||
* @remarks This is an assist function of CommonFileDialog.
|
||||
*/
|
||||
static bool ExtractDisplayName(IShellItem* item, yycc_u8string& ret) {
|
||||
// fetch display name from IShellItem*
|
||||
LPWSTR _name;
|
||||
HRESULT hr = item->GetDisplayName(SIGDN_FILESYSPATH, &_name);
|
||||
if (FAILED(hr)) return false;
|
||||
COMHelper::SmartLPWSTR display_name(_name);
|
||||
|
||||
// convert result
|
||||
if (!YYCC::EncodingHelper::WcharToUTF8(display_name.get(), ret))
|
||||
return false;
|
||||
|
||||
// finished
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief General file dialog.
|
||||
* @param params[in] User specified parameter controlling the behavior of this file dialog,
|
||||
* including title, file types and etc.
|
||||
* @param ret[out] The path to user selected files or folders.
|
||||
* For multiple selection, the count of items >= 1. For other scenario, the count of item is 1.
|
||||
* @return True if success, otherwise false (input parameters is wrong or user click "Cancel" in popup window).
|
||||
* @remarks This function is the real underlying function of all dialog functions.
|
||||
*/
|
||||
template<CommonFileDialogType EDialogType>
|
||||
static bool CommonFileDialog(const FileDialog& params, std::vector<yycc_u8string>& ret) {
|
||||
// Reference: https://learn.microsoft.com/en-us/windows/win32/shell/common-file-dialog
|
||||
// prepare result variable
|
||||
HRESULT hr;
|
||||
|
||||
// check whether COM environment has been initialized
|
||||
if (!COMHelper::IsInitialized()) return false;
|
||||
|
||||
// create file dialog instance
|
||||
// fetch dialog CLSID first
|
||||
CLSID dialog_clsid;
|
||||
switch (EDialogType) {
|
||||
case CommonFileDialogType::OpenFile:
|
||||
case CommonFileDialogType::OpenMultipleFiles:
|
||||
case CommonFileDialogType::OpenFolder:
|
||||
dialog_clsid = CLSID_FileOpenDialog;
|
||||
break;
|
||||
case CommonFileDialogType::SaveFile:
|
||||
dialog_clsid = CLSID_FileSaveDialog;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
// create raw dialog pointer
|
||||
IFileDialog* _pfd = nullptr;
|
||||
hr = CoCreateInstance(
|
||||
dialog_clsid,
|
||||
NULL,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_PPV_ARGS(&_pfd)
|
||||
);
|
||||
if (FAILED(hr)) return false;
|
||||
// create memory-safe dialog pointer
|
||||
COMHelper::SmartIFileDialog pfd(_pfd);
|
||||
|
||||
// set options for dialog
|
||||
// before setting, always get the options first in order.
|
||||
// not to override existing options.
|
||||
DWORD dwFlags;
|
||||
hr = pfd->GetOptions(&dwFlags);
|
||||
if (FAILED(hr)) return false;
|
||||
// modify options
|
||||
switch (EDialogType) {
|
||||
// We want user only can pick file system files: FOS_FORCEFILESYSTEM.
|
||||
// Open dialog default: FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_NOCHANGEDIR
|
||||
// Save dialog default: FOS_OVERWRITEPROMPT | FOS_NOREADONLYRETURN | FOS_PATHMUSTEXIST | FOS_NOCHANGEDIR
|
||||
// Pick folder: FOS_PICKFOLDERS
|
||||
case CommonFileDialogType::OpenFile:
|
||||
dwFlags |= FOS_FORCEFILESYSTEM;
|
||||
dwFlags |= FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_NOCHANGEDIR;
|
||||
break;
|
||||
case CommonFileDialogType::OpenMultipleFiles:
|
||||
dwFlags |= FOS_FORCEFILESYSTEM;
|
||||
dwFlags |= FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_NOCHANGEDIR;
|
||||
dwFlags |= FOS_ALLOWMULTISELECT;
|
||||
break;
|
||||
case CommonFileDialogType::SaveFile:
|
||||
dwFlags |= FOS_FORCEFILESYSTEM;
|
||||
dwFlags |= FOS_OVERWRITEPROMPT | FOS_NOREADONLYRETURN | FOS_PATHMUSTEXIST | FOS_NOCHANGEDIR;
|
||||
break;
|
||||
case CommonFileDialogType::OpenFolder:
|
||||
dwFlags |= FOS_FORCEFILESYSTEM;
|
||||
dwFlags |= FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_NOCHANGEDIR;
|
||||
dwFlags |= FOS_PICKFOLDERS;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
// set folder dialog options
|
||||
hr = pfd->SetOptions(dwFlags);
|
||||
if (FAILED(hr)) return false;
|
||||
|
||||
// build Windows used file dialog parameters
|
||||
WinFileDialog win_params;
|
||||
if (!params.Generate(win_params))
|
||||
return false;
|
||||
|
||||
// setup title and init file name
|
||||
if (win_params.HasTitle()) {
|
||||
hr = pfd->SetTitle(win_params.GetTitle());
|
||||
if (FAILED(hr)) return false;
|
||||
}
|
||||
if (win_params.HasInitFileName()) {
|
||||
hr = pfd->SetFileName(win_params.GetInitFileName());
|
||||
if (FAILED(hr)) return false;
|
||||
}
|
||||
|
||||
// setup init directory
|
||||
if (win_params.HasInitDirectory()) {
|
||||
hr = pfd->SetFolder(win_params.GetInitDirectory());
|
||||
}
|
||||
|
||||
// set file types and default file index when we picking file
|
||||
if constexpr (EDialogType != CommonFileDialogType::OpenFolder) {
|
||||
// set file types list
|
||||
const auto& file_filters = win_params.GetFileTypes();
|
||||
hr = pfd->SetFileTypes(file_filters.GetFilterCount(), file_filters.GetFilterSpecs());
|
||||
if (FAILED(hr)) return false;
|
||||
|
||||
// set default file type index
|
||||
hr = pfd->SetFileTypeIndex(win_params.GetDefaultFileTypeIndex());
|
||||
if (FAILED(hr)) return false;
|
||||
}
|
||||
|
||||
// show the dialog
|
||||
hr = pfd->Show(win_params.HasOwner() ? win_params.GetOwner() : nullptr);
|
||||
if (FAILED(hr)) return false;
|
||||
|
||||
// obtain result when user click "OK" button.
|
||||
switch (EDialogType) {
|
||||
case CommonFileDialogType::OpenFile:
|
||||
case CommonFileDialogType::OpenFolder:
|
||||
case CommonFileDialogType::SaveFile:
|
||||
{
|
||||
// obtain one file entry
|
||||
IShellItem* _item;
|
||||
hr = pfd->GetResult(&_item);
|
||||
if (FAILED(hr)) return false;
|
||||
COMHelper::SmartIShellItem result_item(_item);
|
||||
|
||||
// extract display name
|
||||
yycc_u8string result_name;
|
||||
if (!ExtractDisplayName(result_item.get(), result_name))
|
||||
return false;
|
||||
|
||||
// append result
|
||||
ret.emplace_back(std::move(result_name));
|
||||
}
|
||||
break;
|
||||
case CommonFileDialogType::OpenMultipleFiles:
|
||||
{
|
||||
// try casting file dialog to file open dialog
|
||||
// Ref: https://learn.microsoft.com/en-us/windows/win32/learnwin32/asking-an-object-for-an-interface
|
||||
IFileOpenDialog* _pfod = nullptr;
|
||||
hr = pfd->QueryInterface(IID_PPV_ARGS(&_pfod));
|
||||
if (FAILED(hr)) return false;
|
||||
COMHelper::SmartIFileOpenDialog pfod(_pfod);
|
||||
|
||||
// obtain multiple file entires
|
||||
IShellItemArray* _items;
|
||||
hr = pfod->GetResults(&_items);
|
||||
if (FAILED(hr)) return false;
|
||||
COMHelper::SmartIShellItemArray result_items(_items);
|
||||
|
||||
// analyze file entries
|
||||
// get array count first
|
||||
DWORD result_items_count = 0u;
|
||||
hr = result_items->GetCount(&result_items_count);
|
||||
if (FAILED(hr)) return false;
|
||||
// iterate array
|
||||
for (DWORD i = 0u; i < result_items_count; ++i) {
|
||||
// fetch item by index
|
||||
IShellItem* _item;;
|
||||
hr = result_items->GetItemAt(i, &_item);
|
||||
if (FAILED(hr)) return false;
|
||||
COMHelper::SmartIShellItem result_item(_item);
|
||||
|
||||
// extract display name
|
||||
yycc_u8string result_name;
|
||||
if (!ExtractDisplayName(result_item.get(), result_name))
|
||||
return false;
|
||||
|
||||
// append result
|
||||
ret.emplace_back(std::move(result_name));
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
// everything is okey
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Wrapper Functions
|
||||
|
||||
bool OpenFileDialog(const FileDialog& params, yycc_u8string& ret) {
|
||||
std::vector<yycc_u8string> cache;
|
||||
bool isok = CommonFileDialog<CommonFileDialogType::OpenFile>(params, cache);
|
||||
if (isok) ret = cache.front();
|
||||
return isok;
|
||||
}
|
||||
bool OpenMultipleFileDialog(const FileDialog& params, std::vector<yycc_u8string>& ret) {
|
||||
return CommonFileDialog<CommonFileDialogType::OpenMultipleFiles>(params, ret);
|
||||
}
|
||||
bool SaveFileDialog(const FileDialog& params, yycc_u8string& ret) {
|
||||
std::vector<yycc_u8string> cache;
|
||||
bool isok = CommonFileDialog<CommonFileDialogType::SaveFile>(params, cache);
|
||||
if (isok) ret = cache.front();
|
||||
return isok;
|
||||
}
|
||||
|
||||
bool OpenFolderDialog(const FileDialog& params, yycc_u8string& ret) {
|
||||
std::vector<yycc_u8string> cache;
|
||||
bool isok = CommonFileDialog<CommonFileDialogType::OpenFolder>(params, cache);
|
||||
if (isok) ret = cache.front();
|
||||
return isok;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
312
src/YYCCLegacy/DialogHelper.hpp
Normal file
312
src/YYCCLegacy/DialogHelper.hpp
Normal file
@@ -0,0 +1,312 @@
|
||||
#pragma once
|
||||
#include "YYCCInternal.hpp"
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
|
||||
#include "COMHelper.hpp"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <initializer_list>
|
||||
|
||||
#include "WinImportPrefix.hpp"
|
||||
#include <Windows.h>
|
||||
#include <shlobj_core.h>
|
||||
#include "WinImportSuffix.hpp"
|
||||
|
||||
/**
|
||||
* @brief The namespace providing Windows universal dialog features.
|
||||
* @details
|
||||
* This namespace only available on Windows platform.
|
||||
* See also \ref dialog_helper.
|
||||
*/
|
||||
namespace YYCC::DialogHelper {
|
||||
|
||||
/**
|
||||
* @brief The class representing the file types region in file dialog.
|
||||
* @details
|
||||
* This class is served for Windows used.
|
||||
* Programmer should \b not create this class manually.
|
||||
*/
|
||||
class WinFileFilters {
|
||||
friend class FileFilters;
|
||||
friend class WinFileDialog;
|
||||
public:
|
||||
WinFileFilters() : m_WinFilters(), m_WinDataStruct(nullptr) {}
|
||||
YYCC_DEL_CLS_COPY_MOVE(WinFileFilters);
|
||||
|
||||
/// @brief Get the count of available file filters
|
||||
UINT GetFilterCount() const {
|
||||
return static_cast<UINT>(m_WinFilters.size());
|
||||
}
|
||||
/// @brief Get pointer to Windows used file filters declarations
|
||||
const COMDLG_FILTERSPEC* GetFilterSpecs() const {
|
||||
return m_WinDataStruct.get();
|
||||
}
|
||||
|
||||
protected:
|
||||
using WinFilterModes = std::wstring;
|
||||
using WinFilterName = std::wstring;
|
||||
using WinFilterPair = std::pair<WinFilterName, WinFilterModes>;
|
||||
|
||||
std::vector<WinFilterPair> m_WinFilters;
|
||||
std::unique_ptr<COMDLG_FILTERSPEC[]> m_WinDataStruct;
|
||||
|
||||
/// @brief Clear all current file filters
|
||||
void Clear() {
|
||||
m_WinDataStruct.reset();
|
||||
m_WinFilters.clear();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The class representing the file types region in file dialog.
|
||||
* @details
|
||||
* This class is served for programmer using.
|
||||
* But you don't need create it on your own.
|
||||
* You can simply fetch it by FileDialog::ConfigreFileTypes ,
|
||||
* because this class is a part of FileDialog.
|
||||
*/
|
||||
class FileFilters {
|
||||
public:
|
||||
FileFilters() : m_Filters() {}
|
||||
YYCC_DEL_CLS_COPY_MOVE(FileFilters);
|
||||
|
||||
/**
|
||||
* @brief Add a filter pair in file types list.
|
||||
* @param[in] filter_name The friendly name of the filter.
|
||||
* @param[in] il
|
||||
* A C++ initialize list containing acceptable file filter pattern.
|
||||
* Every entries must be `const yycc_char8_t*` representing a single filter pattern.
|
||||
* The list at least should have one valid pattern.
|
||||
* This function will not validate these filter patterns, so please write them carefully.
|
||||
* @return True if added success, otherwise false.
|
||||
* @remarks
|
||||
* This function allow you register multiple filter patterns for single friendly name.
|
||||
* For example: <TT>Add(u8"Microsoft Word (*.doc; *.docx)", {u8"*.doc", u8"*.docx"})</TT>
|
||||
*/
|
||||
bool Add(const yycc_char8_t* filter_name, std::initializer_list<const yycc_char8_t*> il);
|
||||
/**
|
||||
* @brief Get the count of added filter pairs.
|
||||
* @return The count of already added filter pairs.
|
||||
*/
|
||||
size_t Count() const { return m_Filters.size(); }
|
||||
|
||||
/// @brief Clear filter pairs for following re-use.
|
||||
void Clear() { m_Filters.clear(); }
|
||||
|
||||
/**
|
||||
* @brief Generate Windows dialog system used data struct.
|
||||
* @param[out] win_result The class receiving the generated filter data struct.
|
||||
* @return True if generation success, otherwise false.
|
||||
* @remarks
|
||||
* Programmer should not call this function,
|
||||
* this function is used as YYCC internal code.
|
||||
*/
|
||||
bool Generate(WinFileFilters& win_result) const;
|
||||
|
||||
protected:
|
||||
using FilterModes = std::vector<yycc_u8string>;
|
||||
using FilterName = yycc_u8string;
|
||||
using FilterPair = std::pair<FilterName, FilterModes>;
|
||||
|
||||
std::vector<FilterPair> m_Filters;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The class representing the file dialog.
|
||||
* @details
|
||||
* This class is served for Windows used.
|
||||
* Programmer should \b not create this class manually.
|
||||
*/
|
||||
class WinFileDialog {
|
||||
friend class FileDialog;
|
||||
public:
|
||||
WinFileDialog() :
|
||||
m_WinOwner(NULL),
|
||||
m_WinFileTypes(), m_WinDefaultFileTypeIndex(0u),
|
||||
m_HasTitle(false), m_HasInitFileName(false), m_WinTitle(), m_WinInitFileName(),
|
||||
m_WinInitDirectory(nullptr) {}
|
||||
YYCC_DEL_CLS_COPY_MOVE(WinFileDialog);
|
||||
|
||||
/// @brief Get whether this dialog has owner.
|
||||
bool HasOwner() const { return m_WinOwner != NULL; }
|
||||
/// @brief Get the \c HWND of dialog owner.
|
||||
HWND GetOwner() const { return m_WinOwner; }
|
||||
|
||||
/// @brief Get the struct holding Windows used file filters data.
|
||||
const WinFileFilters& GetFileTypes() const { return m_WinFileTypes; }
|
||||
/// @brief Get the index of default selected file filter.
|
||||
UINT GetDefaultFileTypeIndex() const { return m_WinDefaultFileTypeIndex; }
|
||||
|
||||
/// @brief Get whether dialog has custom title.
|
||||
bool HasTitle() const { return m_HasTitle; }
|
||||
/// @brief Get custom title of dialog.
|
||||
const wchar_t* GetTitle() const { return m_WinTitle.c_str(); }
|
||||
/// @brief Get whether dialog has custom initial file name.
|
||||
bool HasInitFileName() const { return m_HasInitFileName; }
|
||||
/// @brief Get custom initial file name of dialog
|
||||
const wchar_t* GetInitFileName() const { return m_WinInitFileName.c_str(); }
|
||||
|
||||
/// @brief Get whether dialog has custom initial directory.
|
||||
bool HasInitDirectory() const { return m_WinInitDirectory.get() != nullptr; }
|
||||
/// @brief Get custom initial directory of dialog.
|
||||
IShellItem* GetInitDirectory() const { return m_WinInitDirectory.get(); }
|
||||
|
||||
protected:
|
||||
HWND m_WinOwner;
|
||||
WinFileFilters m_WinFileTypes;
|
||||
/**
|
||||
* @brief The default selected file type in dialog
|
||||
* @remarks
|
||||
* This is 1-based index according to Windows specification.
|
||||
* In other words, when generating this struct from FileDialog to this struct this field should plus 1.
|
||||
* Because the same field located in FileDialog is 0-based index.
|
||||
*/
|
||||
UINT m_WinDefaultFileTypeIndex;
|
||||
bool m_HasTitle, m_HasInitFileName;
|
||||
std::wstring m_WinTitle, m_WinInitFileName;
|
||||
COMHelper::SmartIShellItem m_WinInitDirectory;
|
||||
|
||||
/// @brief Clear all data and reset them to default value.
|
||||
void Clear() {
|
||||
m_WinOwner = nullptr;
|
||||
m_WinFileTypes.Clear();
|
||||
m_WinDefaultFileTypeIndex = 0u;
|
||||
m_HasTitle = m_HasInitFileName = false;
|
||||
m_WinTitle.clear();
|
||||
m_WinInitFileName.clear();
|
||||
m_WinInitDirectory.reset();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The class representing the file dialog.
|
||||
* @details
|
||||
* This class is served for programming using to describe every aspectes of the dialog.
|
||||
* For how to use this struct, see \ref dialog_helper.
|
||||
*/
|
||||
class FileDialog {
|
||||
public:
|
||||
FileDialog() :
|
||||
m_Owner(NULL),
|
||||
m_FileTypes(),
|
||||
m_DefaultFileTypeIndex(0u),
|
||||
m_Title(), m_InitFileName(), m_InitDirectory(),
|
||||
m_HasTitle(false), m_HasInitFileName(false), m_HasInitDirectory(false) {}
|
||||
YYCC_DEL_CLS_COPY_MOVE(FileDialog);
|
||||
|
||||
/**
|
||||
* @brief Set the owner of dialog.
|
||||
* @param[in] owner The \c HWND pointing to the owner of dialog, or NULL to remove owner.
|
||||
*/
|
||||
void SetOwner(HWND owner) { m_Owner = owner; }
|
||||
/**
|
||||
* @brief Set custom title of dialog
|
||||
* @param[in] title The string pointer to custom title, or nullptr to remove it.
|
||||
*/
|
||||
void SetTitle(const yycc_char8_t* title) {
|
||||
if (m_HasTitle = title != nullptr)
|
||||
m_Title = title;
|
||||
}
|
||||
/**
|
||||
* @brief Fetch the struct describing file filters for future configuration.
|
||||
* @return The reference to the struct describing file filters.
|
||||
*/
|
||||
FileFilters& ConfigreFileTypes() {
|
||||
return m_FileTypes;
|
||||
}
|
||||
/**
|
||||
* @brief Set the index of default selected file filter.
|
||||
* @param[in] idx
|
||||
* The index to default one.
|
||||
* This must be a valid index in file filters.
|
||||
*/
|
||||
void SetDefaultFileTypeIndex(size_t idx) { m_DefaultFileTypeIndex = idx; }
|
||||
/**
|
||||
* @brief Set the initial file name of dialog
|
||||
* @details If set, the file name will always be same one when opening dialog.
|
||||
* @param[in] init_filename String pointer to initial file name, or nullptr to remove it.
|
||||
*/
|
||||
void SetInitFileName(const yycc_char8_t* init_filename) {
|
||||
if (m_HasInitFileName = init_filename != nullptr)
|
||||
m_InitFileName = init_filename;
|
||||
}
|
||||
/**
|
||||
* @brief Set the initial directory of dialog
|
||||
* @details If set, the opended directory will always be the same one when opening dialog
|
||||
* @param[in] init_dir
|
||||
* String pointer to initial directory.
|
||||
* Invalid path or nullptr will remove this feature.
|
||||
*/
|
||||
void SetInitDirectory(const yycc_char8_t* init_dir) {
|
||||
if (m_HasInitDirectory = init_dir != nullptr)
|
||||
m_InitDirectory = init_dir;
|
||||
}
|
||||
|
||||
/// @brief Clear file dialog parameters for following re-use.
|
||||
void Clear() {
|
||||
m_Owner = nullptr;
|
||||
m_HasTitle = m_HasInitFileName = m_HasInitDirectory = false;
|
||||
m_Title.clear();
|
||||
m_InitFileName.clear();
|
||||
m_InitDirectory.clear();
|
||||
m_FileTypes.Clear();
|
||||
m_DefaultFileTypeIndex = 0u;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate Windows dialog system used data struct.
|
||||
* @param[out] win_result The class receiving the generated filter data struct.
|
||||
* @return True if generation is success, otherwise false.
|
||||
* @remarks
|
||||
* Programmer should not call this function.
|
||||
* This function is used as YYCC internal code.
|
||||
*/
|
||||
bool Generate(WinFileDialog& win_result) const;
|
||||
|
||||
protected:
|
||||
HWND m_Owner;
|
||||
bool m_HasTitle, m_HasInitFileName, m_HasInitDirectory;
|
||||
yycc_u8string m_Title, m_InitFileName, m_InitDirectory;
|
||||
FileFilters m_FileTypes;
|
||||
/**
|
||||
* @brief The default selected file type in dialog
|
||||
* @remarks
|
||||
* The index Windows used is 1-based index.
|
||||
* But for universal experience, we order this is 0-based index.
|
||||
* And do convertion when generating Windows used struct.
|
||||
*/
|
||||
size_t m_DefaultFileTypeIndex;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Open the dialog which order user select single file to open.
|
||||
* @param[in] params The configuration of dialog.
|
||||
* @param[out] ret Full path to user selected file.
|
||||
* @return False if user calcel the operation or something went wrong, otherwise true.
|
||||
*/
|
||||
bool OpenFileDialog(const FileDialog& params, yycc_u8string& ret);
|
||||
/**
|
||||
* @brief Open the dialog which order user select multiple file to open.
|
||||
* @param[in] params The configuration of dialog.
|
||||
* @param[out] ret The list of full path of user selected files.
|
||||
* @return False if user calcel the operation or something went wrong, otherwise true.
|
||||
*/
|
||||
bool OpenMultipleFileDialog(const FileDialog& params, std::vector<yycc_u8string>& ret);
|
||||
/**
|
||||
* @brief Open the dialog which order user select single file to save.
|
||||
* @param[in] params The configuration of dialog.
|
||||
* @param[out] ret Full path to user selected file.
|
||||
* @return False if user calcel the operation or something went wrong, otherwise true.
|
||||
*/
|
||||
bool SaveFileDialog(const FileDialog& params, yycc_u8string& ret);
|
||||
/**
|
||||
* @brief Open the dialog which order user select single directory to open.
|
||||
* @param[in] params The configuration of dialog.
|
||||
* @param[out] ret Full path to user selected directory.
|
||||
* @return False if user calcel the operation or something went wrong, otherwise true.
|
||||
*/
|
||||
bool OpenFolderDialog(const FileDialog& params, yycc_u8string& ret);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user