1
0

30 Commits

Author SHA1 Message Date
422aa152ff fix: fix libcxx patch error 2026-03-04 13:49:58 +08:00
92ce0e29cb fix: find iconv if we include it 2026-02-03 19:26:21 +08:00
a0f032c28b chore: update yycc gh action build script 2026-02-03 15:23:21 +08:00
b51ded2101 chore: add lost cd in CI script 2026-02-02 16:22:32 +08:00
09f07d99f7 fix: fix wrong value hint in clap test.
- remove the bracker (<bla>) in clap test because clap will automatically add it.
2026-02-02 16:21:39 +08:00
c19561cb54 fix: fix clap manual output error.
- fix the issue that the executable in clap manual output is full path, not the file name.
2026-02-02 16:18:42 +08:00
9ad199073a test: add test for blank string for encoding convertion 2026-01-28 19:36:15 +08:00
908d48a7c1 fix: fix misc content 2026-01-25 10:20:26 +08:00
aa6dd3031a doc: update compile manual 2026-01-23 20:46:26 +08:00
19df293463 fix: fix macos build issue again 2026-01-23 15:11:55 +08:00
71eb0741f6 fix: try fix clang libcxx charconv issue.
- add compile checker in cmake to detect charconv support status.
- and expose macro for yycc to enable different part of charconv repectively to prevent duplicated defines.
2026-01-23 14:46:09 +08:00
09fea7e0a3 feat: add charconv polyfill for clang and apple clang 2026-01-23 11:02:54 +08:00
aecf9bb8cc chore: fix github action build issue 2026-01-22 19:50:20 +08:00
6449ae1977 chore: use github provided package to fetch dependencies 2026-01-22 16:24:21 +08:00
1c1e709ed1 chore: update github build script.
- enable gtest and benchmark build.
- allow install gtest and benchmark binary.
- add test and benchmark step in github action.
2026-01-22 16:12:57 +08:00
fe4193efa7 refactor: change project layout
- move script as asset because they are not build script
- create new script directory for "User Compile" method because github action need to build with gtest.
- change compile manual for this change.
- modify external dependency location in github action and gitignore.
2026-01-22 15:48:28 +08:00
746d20a835 fix: fix environ not found when building env.
- re-organize included headers for env.
- resolve environ not found when building env on macos. use our explicit extern declare to replace system header decl.
2026-01-22 15:26:33 +08:00
8989e909ad refactor: rename one overload of "replace" in string op into "to_replace" to indicate it produce new instance. 2026-01-22 10:23:26 +08:00
718fe426bf fix: fix clap option ctor signature error 2026-01-21 19:22:50 +08:00
1a4074fd98 doc: add last lost doc 2026-01-21 11:17:12 +08:00
74027e7297 fix: fix linux runtime bugs
- use std::filesystem::read_symlink for symlink reading instead of complex homemade linux-syscall-based function. std function is more robust than I written.
- fix linux command line argument getter issue.
2026-01-20 21:27:58 +08:00
044c04aa07 doc: add doc for num namespace 2026-01-20 14:30:14 +08:00
e161dafac5 doc: add new added carton doc 2026-01-20 13:57:09 +08:00
4d9487813b doc: migrate all old doc 2026-01-20 13:20:17 +08:00
7a34057836 doc: finish encoding doc 2026-01-15 13:48:41 +08:00
17053f4ebf doc: finish csconsole doc 2026-01-14 13:59:42 +08:00
de2b927a14 doc: finish win console and termcolor doc 2026-01-14 10:08:45 +08:00
a50233ab6e chore: add preprocessor support for doxygen 2026-01-13 15:45:38 +08:00
6dfd957ce9 doc: fix doxygen issue 2026-01-13 14:33:37 +08:00
215a8ce8b8 doc: fix doc for winfct 2026-01-13 13:24:18 +08:00
88 changed files with 3245 additions and 977 deletions

4
.github/scripts/README.md vendored Normal file
View File

@@ -0,0 +1,4 @@
# GitHub Action Scripts
These script files are only used for GitHub Action.
These script files should only be executed in their root directory respectively.

17
.github/scripts/gbenchmark/linux.sh vendored Normal file
View File

@@ -0,0 +1,17 @@
#!/bin/bash
set -euo pipefail
# Create build and install directory
mkdir build install
# Build project
cd build
cmake -DCMAKE_CXX_STANDARD=23 -DBENCHMARK_ENABLE_TESTING=OFF -DCMAKE_BUILD_TYPE=Release ..
cmake --build .
cmake --install . --prefix=../install
cd ..
# Record install directory
cd install
export benchmark_ROOT=$(pwd)
cd ..

17
.github/scripts/gbenchmark/macos.sh vendored Normal file
View File

@@ -0,0 +1,17 @@
#!/bin/bash
set -euo pipefail
# Create build and install directory
mkdir build install
# Build project
cd build
cmake -DCMAKE_CXX_STANDARD=23 -DBENCHMARK_ENABLE_TESTING=OFF -DCMAKE_BUILD_TYPE=Release ..
cmake --build .
cmake --install . --prefix=../install
cd ..
# Record install directory
cd install
export benchmark_ROOT=$(pwd)
cd ..

17
.github/scripts/gbenchmark/windows.bat vendored Normal file
View File

@@ -0,0 +1,17 @@
@ECHO OFF
:: Create build and install directory
MKDIR build
MKDIR install
:: Build project
CD build
cmake -A x64 -DCMAKE_CXX_STANDARD=23 -DBENCHMARK_ENABLE_TESTING=OFF ..
cmake --build . --config Release
cmake --install . --prefix=../install --config Release
CD ..
:: Record install directory
CD install
SET benchmark_ROOT=%CD%
CD ..

17
.github/scripts/gtest/linux.sh vendored Normal file
View File

@@ -0,0 +1,17 @@
#!/bin/bash
set -euo pipefail
# Create build and install directory
mkdir build install
# Build project
cd build
cmake -DCMAKE_CXX_STANDARD=23 -Dgtest_force_shared_crt=ON -DCMAKE_BUILD_TYPE=Release ..
cmake --build .
cmake --install . --prefix=../install
cd ..
# Record install directory
cd install
export GTest_ROOT=$(pwd)
cd ..

17
.github/scripts/gtest/macos.sh vendored Normal file
View File

@@ -0,0 +1,17 @@
#!/bin/bash
set -euo pipefail
# Create build and install directory
mkdir build install
# Build project
cd build
cmake -DCMAKE_CXX_STANDARD=23 -Dgtest_force_shared_crt=ON -DCMAKE_BUILD_TYPE=Release ..
cmake --build .
cmake --install . --prefix=../install
cd ..
# Record install directory
cd install
export GTest_ROOT=$(pwd)
cd ..

17
.github/scripts/gtest/windows.bat vendored Normal file
View File

@@ -0,0 +1,17 @@
@ECHO OFF
:: Create build and install directory
MKDIR build
MKDIR install
:: Build project
CD build
cmake -A x64 -DCMAKE_CXX_STANDARD=23 -Dgtest_force_shared_crt=ON ..
cmake --build . --config Release
cmake --install . --prefix=../install --config Release
CD ..
:: Record install directory
CD install
SET GTest_ROOT=%CD%
CD ..

19
.github/scripts/linux.sh vendored Normal file
View File

@@ -0,0 +1,19 @@
#!/bin/bash
set -euo pipefail
# Create build directory and enter it
mkdir bin
cd bin
# Create internal build and install directory, then enter it
mkdir build
mkdir install
# Build in Release mode
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DYYCC_BUILD_TEST=ON -DGTest_ROOT=$GTest_ROOT -DYYCC_BUILD_BENCHMARK=ON -Dbenchmark_ROOT=$benchmark_ROOT ../..
cmake --build .
cmake --install . --prefix=../install
cd ..
# Back to root directory
cd ..

19
.github/scripts/macos.sh vendored Normal file
View File

@@ -0,0 +1,19 @@
#!/bin/bash
set -euo pipefail
# Create build directory and enter it
mkdir bin
cd bin
# Create internal build and install directory, then enter it
mkdir build
mkdir install
# Build in Release mode
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DYYCC_BUILD_TEST=ON -DGTest_ROOT=$GTest_ROOT -DYYCC_BUILD_BENCHMARK=ON -Dbenchmark_ROOT=$benchmark_ROOT ../..
cmake --build .
cmake --install . --prefix=../install
cd ..
# Back to root directory
cd ..

18
.github/scripts/windows.bat vendored Normal file
View File

@@ -0,0 +1,18 @@
@ECHO OFF
:: Create build directory and enter it
MKDIR bin
CD bin
:: Create internal build and install directory, then enter it
MKDIR build
MKDIR install
:: Build with x64 architecture in Release mode
CD build
cmake -A x64 -DYYCC_BUILD_TEST=ON -DGTest_ROOT=%GTest_ROOT% -DYYCC_BUILD_BENCHMARK=ON -Dbenchmark_ROOT=%benchmark_ROOT% ../..
cmake --build . --config Release
cmake --install . --prefix=../install --config Release
CD ..
:: Back to root directory
CD ..

View File

@@ -10,35 +10,54 @@ jobs:
- 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: Setup Google Test and Google Benchmark
# run: |
# # Setup Google Test
# git clone -b v1.17.0 https://github.com/google/googletest.git external/googletest
# cd external/googletest
# mkdir build install
# cd build
# cmake -DCMAKE_CXX_STANDARD=23 -Dgtest_force_shared_crt=ON -DCMAKE_BUILD_TYPE=Release ..
# cmake --build .
# cmake --install . --prefix=../install
# cd ../..
# # Setup Google Benchmark
# git clone -b v1.9.4 https://github.com/google/benchmark.git external/benchmark
# cd external/benchmark
# # Create symlink to googletest as required by benchmark
# ln -s ../googletest googletest
# mkdir build install
# cd build
# cmake -DCMAKE_CXX_STANDARD=23 -DBENCHMARK_ENABLE_TESTING=OFF -DCMAKE_BUILD_TYPE=Release ..
# cmake --build .
# cmake --install . --prefix=../install
# cd ../..
- name: Build YYCC
- 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: |
chmod +x ./.github/linux_build.sh
./.github/linux_build.sh
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:

View File

@@ -9,32 +9,50 @@ jobs:
steps:
- name: Checkout Repository
uses: actions/checkout@v4
# - name: Setup Google Test and Google Benchmark
# run: |
# # Setup Google Test
# git clone -b v1.17.0 https://github.com/google/googletest.git external/googletest
# cd external/googletest
# mkdir build install
# cd build
# cmake -DCMAKE_CXX_STANDARD=23 -Dgtest_force_shared_crt=ON -DCMAKE_BUILD_TYPE=Release ..
# cmake --build .
# cmake --install . --prefix=../install
# cd ../..
# # Setup Google Benchmark
# git clone -b v1.9.4 https://github.com/google/benchmark.git external/benchmark
# cd external/benchmark
# # Create symlink to googletest as required by benchmark
# ln -s ../googletest googletest
# mkdir build install
# cd build
# cmake -DCMAKE_CXX_STANDARD=23 -DBENCHMARK_ENABLE_TESTING=OFF -DCMAKE_BUILD_TYPE=Release ..
# cmake --build .
# cmake --install . --prefix=../install
# cd ../..
- name: Build YYCC
- 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: |
chmod +x ./.github/macos_build.sh
./.github/macos_build.sh
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:

View File

@@ -16,36 +16,59 @@ jobs:
steps:
- name: Checkout Repository
uses: actions/checkout@v4
# - name: Setup Google Test and Google Benchmark
# run: |
# # Setup Google Test
# git clone -b v1.17.0 https://github.com/google/googletest.git external/googletest
# cd external/googletest
# mkdir build install
# cd build
# cmake -DCMAKE_CXX_STANDARD=23 -Dgtest_force_shared_crt=ON -DCMAKE_BUILD_TYPE=Release ..
# cmake --build . --config Release
# cmake --install . --prefix=../install --config Release
# cd ../..
# # Setup Google Benchmark
# git clone -b v1.9.4 https://github.com/google/benchmark.git external/benchmark
# cd external/benchmark
# # Create symlink to googletest as required by benchmark
# mklink /D googletest ../googletest
# mkdir build install
# cd build
# cmake -DCMAKE_CXX_STANDARD=23 -DBENCHMARK_ENABLE_TESTING=OFF -DCMAKE_BUILD_TYPE=Release ..
# cmake --build . --config Release
# cmake --install . --prefix=../install --config Release
# cd ../..
- 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 }}
.\.github\windows_build.bat
:: Extract saved environment variables
CALL .\extern\envs.bat
:: Build Project
CALL .\.github\scripts\windows.bat
- name: Run YYCC Test
shell: cmd
run: |
.\bin\install\bin\YYCCTest.exe
- name: Run YYCC Benchmark
shell: cmd
run: |
.\bin\install\bin\YYCCBenchmark.exe
- name: Upload Built Artifact
uses: actions/upload-artifact@v4
with:

1
.gitignore vendored
View File

@@ -3,6 +3,7 @@
out/
build/
install/
extern/
# Ignore CMake generated stuff
src/yycc/version.hpp

View File

@@ -26,6 +26,9 @@ 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)
@@ -37,6 +40,10 @@ endif ()
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)

View File

@@ -27,10 +27,16 @@ So you actually do not need Google Test, Google Benchmark and Doxygen.
### Compiler
> [!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.
> 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: `std::stacktrace` and `std::views::enumerate`.
> 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`.
### Google Test
@@ -106,7 +112,13 @@ If you are a developer (developer of this project, or use this project as depend
"User Build" is basically how GitHub Action build this project.
Execute `.github/windows_build.bat` on Windows or `.github/linux_build.sh` on POSIX-like OS (Linux and macOS) under **the root directory** of this project. The final built artifact is under `bin/install` directory.
Under **the root directory** of this project, execute:
- `script/windows_build.bat` on Windows
- or `script/linux_build.sh` on Linux
- or `script/macos_build.sh` on macOS
The final built artifact is under `bin/install` directory.
### Developer Build
@@ -127,9 +139,9 @@ Please note that generated documentation is different in different platforms.
* `YYCC_ENFORCE_ICONV`: Set it to `ON` to enable Iconv feature forcely. `OFF` in default.
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.
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.
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.
* `CMAKE_CXX_STANDARD`: Set C++ standard version of project.

View File

View File

@@ -26,3 +26,8 @@ PRIVATE
YYCCommonplace
benchmark::benchmark
)
# Install binary
install(TARGETS YYCCBenchmark
RUNTIME DESTINATION ${YYCC_INSTALL_BIN_PATH}
)

View File

@@ -1,7 +1,12 @@
@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")
check_required_components(YYCCommonplace)
check_required_components(YYCCommonplace)

View File

@@ -0,0 +1,30 @@
message(STATUS "Checking charconv implementation...")
include(CheckCXXSourceCompiles)
file(READ "${CMAKE_CURRENT_LIST_DIR}/check_charconv/chars_format.cpp" TEST_CODE_SNIPPET)
check_cxx_source_compiles("${TEST_CODE_SNIPPET}" YYCC_CHARCONV_HAS_CHARS_FORMAT)
message(STATUS "Support std::chars_format: ${YYCC_CHARCONV_HAS_CHARS_FORMAT}")
file(READ "${CMAKE_CURRENT_LIST_DIR}/check_charconv/from_chars_result.cpp" TEST_CODE_SNIPPET)
check_cxx_source_compiles("${TEST_CODE_SNIPPET}" YYCC_CHARCONV_HAS_FROM_CHARS_RESULT)
message(STATUS "Support std::from_chars_result: ${YYCC_CHARCONV_HAS_FROM_CHARS_RESULT}")
file(READ "${CMAKE_CURRENT_LIST_DIR}/check_charconv/to_chars_result.cpp" TEST_CODE_SNIPPET)
check_cxx_source_compiles("${TEST_CODE_SNIPPET}" YYCC_CHARCONV_HAS_TO_CHARS_RESULT)
message(STATUS "Support std::to_chars_result: ${YYCC_CHARCONV_HAS_TO_CHARS_RESULT}")
file(READ "${CMAKE_CURRENT_LIST_DIR}/check_charconv/from_chars_int.cpp" TEST_CODE_SNIPPET)
check_cxx_source_compiles("${TEST_CODE_SNIPPET}" YYCC_CHARCONV_HAS_FROM_CHARS_INT)
message(STATUS "Support std::from_chars with integral type: ${YYCC_CHARCONV_HAS_FROM_CHARS_INT}")
file(READ "${CMAKE_CURRENT_LIST_DIR}/check_charconv/from_chars_float.cpp" TEST_CODE_SNIPPET)
check_cxx_source_compiles("${TEST_CODE_SNIPPET}" YYCC_CHARCONV_HAS_FROM_CHARS_FLOAT)
message(STATUS "Suppoer std::from_chars with float point type: ${YYCC_CHARCONV_HAS_FROM_CHARS_FLOAT}")
file(READ "${CMAKE_CURRENT_LIST_DIR}/check_charconv/to_chars_int.cpp" TEST_CODE_SNIPPET)
check_cxx_source_compiles("${TEST_CODE_SNIPPET}" YYCC_CHARCONV_HAS_TO_CHARS_INT)
message(STATUS "Support std::to_chars with integral type: ${YYCC_CHARCONV_HAS_TO_CHARS_INT}")
file(READ "${CMAKE_CURRENT_LIST_DIR}/check_charconv/to_chars_float.cpp" TEST_CODE_SNIPPET)
check_cxx_source_compiles("${TEST_CODE_SNIPPET}" YYCC_CHARCONV_HAS_TO_CHARS_FLOAT)
message(STATUS "Support std::to_chars with float point type: ${YYCC_CHARCONV_HAS_TO_CHARS_FLOAT}")

View File

@@ -0,0 +1,8 @@
#include <charconv>
int main(int argc, char **argv) {
auto scientific = std::chars_format::scientific;
auto fixed = std::chars_format::fixed;
auto general = std::chars_format::general;
auto hex = std::chars_format::hex;
}

View File

@@ -0,0 +1,16 @@
#include <charconv>
int main(int argc, char **argv) {
const char probe[] = "0.0";
const char* first = probe;
const char* last = first + sizeof(probe);
{
float value;
auto rv = std::from_chars(first, last, value, std::chars_format::general);
}
{
double value;
auto rv = std::from_chars(first, last, value, std::chars_format::general);
}
}

View File

@@ -0,0 +1,41 @@
#include <charconv>
#include <cstdint>
int main(int argc, char **argv) {
const char probe[] = "0";
const char* first = probe;
const char* last = first + sizeof(probe);
{
std::int8_t value;
auto rv = std::from_chars(first, last, value, 10);
}
{
std::int16_t value;
auto rv = std::from_chars(first, last, value, 10);
}
{
std::int32_t value;
auto rv = std::from_chars(first, last, value, 10);
}
{
std::int64_t value;
auto rv = std::from_chars(first, last, value, 10);
}
{
std::uint8_t value;
auto rv = std::from_chars(first, last, value, 10);
}
{
std::uint16_t value;
auto rv = std::from_chars(first, last, value, 10);
}
{
std::uint32_t value;
auto rv = std::from_chars(first, last, value, 10);
}
{
std::uint64_t value;
auto rv = std::from_chars(first, last, value, 10);
}
}

View File

@@ -0,0 +1,9 @@
#include <charconv>
#include <system_error>
int main(int argc, char **argv) {
std::from_chars_result result {
.ptr = nullptr,
.ec = std::errc{},
};
}

View File

@@ -0,0 +1,16 @@
#include <charconv>
int main(int argc, char **argv) {
char buffer[1024];
char* first = buffer;
char* last = first + sizeof(buffer);
{
float value = 0;
auto rv = std::to_chars(first, last, value, std::chars_format::general, 6);
}
{
double value = 0;
auto rv = std::to_chars(first, last, value, std::chars_format::general, 6);
}
}

View File

@@ -0,0 +1,41 @@
#include <charconv>
#include <cstdint>
int main(int argc, char **argv) {
char buffer[1024];
char* first = buffer;
char* last = first + sizeof(buffer);
{
std::int8_t value = 0;
auto rv = std::to_chars(first, last, value, 10);
}
{
std::int16_t value = 0;
auto rv = std::to_chars(first, last, value, 10);
}
{
std::int32_t value = 0;
auto rv = std::to_chars(first, last, value, 10);
}
{
std::int64_t value = 0;
auto rv = std::to_chars(first, last, value, 10);
}
{
std::uint8_t value = 0;
auto rv = std::to_chars(first, last, value, 10);
}
{
std::uint16_t value = 0;
auto rv = std::to_chars(first, last, value, 10);
}
{
std::uint32_t value = 0;
auto rv = std::to_chars(first, last, value, 10);
}
{
std::uint64_t value = 0;
auto rv = std::to_chars(first, last, value, 10);
}
}

View File

@@ -0,0 +1,9 @@
#include <charconv>
#include <system_error>
int main(int argc, char **argv) {
std::to_chars_result result {
.ptr = nullptr,
.ec = std::errc{},
};
}

View File

@@ -1,19 +1,36 @@
# Configure Doxygen config file
# Extract all public macros defined in YYCC
# However, you should note that these extratcted macros have generator expressions.
get_target_property(YYCC_COMPILE_DEFINITIONS YYCCommonplace COMPILE_DEFINITIONS)
if (YYCC_COMPILE_DEFINITIONS STREQUAL "YYCC_COMPILE_DEFINITIONS-NOTFOUND")
message(FATAL_ERROR "Cannot extract compile definitions from YYCCommonplace.")
endif ()
# Convert list to string for expanding in future.
list(JOIN YYCC_COMPILE_DEFINITIONS " " YYCC_MACRO_GENERATOR_EXPRESSIONS)
# We simply configure Doxygen config file first.
configure_file(
${CMAKE_CURRENT_LIST_DIR}/Doxyfile.in
${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
@ONLY
)
# Add custom target
# Then we use "file GENERATE" syntax to generate per-config truely Doxyfile used by Doxygen.
# Because there is no "$<>" syntax in Doxyfile, so we can safely use it.
# Please note that the generation of "file GENERATE" syntax will be postponed until the build stage.
file(GENERATE
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/Doxyfile"
INPUT "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile"
TARGET YYCCommonplace
)
# Add custom target using per-config Doxyfile
add_custom_target (YYCCDocumentation
doxygen ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
Doxygen::doxygen "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/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}
)

View File

@@ -2306,7 +2306,7 @@ PERLMOD_MAKEVAR_PREFIX =
# C-preprocessor directives found in the sources and include files.
# The default value is: YES.
ENABLE_PREPROCESSING = NO
ENABLE_PREPROCESSING = YES
# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
# 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_DOXYGEN
PREDEFINED = @YYCC_MACRO_GENERATOR_EXPRESSIONS@
# 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

View File

@@ -1,200 +0,0 @@
namespace YYCC::ArgParser {
/**
\page arg_parser Universal Argument Parser
YYCC::ArgParser provides an universal way to parsing command line arguments.
Universal argument parser has similar design with universal config manager,
it is highly recommand that read \ref config_manager chapter first,
because you will have a clear understanding of this namespace after reading universal config manager chapter.
There is an example about how to use universal argument parser.
In following content, we will describe it in detail.
\code{.cpp}
class TestArgParser {
public:
TestArgParser() :
m_IntArgument(YYCC_U8("int"), YYCC_U8_CHAR('i'), YYCC_U8("integral argument"), YYCC_U8("114514")),
m_FloatArgument(nullptr, YYCC_U8_CHAR('f'), nullptr, nullptr, true),
m_StringArgument(YYCC_U8("string"), YYCC::ArgParser::AbstractArgument::NO_SHORT_NAME, nullptr, nullptr, true),
m_BoolArgument(nullptr, YYCC_U8_CHAR('b'), nullptr),
m_ClampedFloatArgument(YYCC_U8("clamped-float"), YYCC::ArgParser::AbstractArgument::NO_SHORT_NAME, nullptr, nullptr, true, YYCC::Constraints::GetMinMaxRangeConstraint<float>(-1.0f, 1.0f)),
m_OptionContext(YYCC_U8("TestArgParser"), YYCC_U8("This is the test of argument parser."), {
&m_IntArgument, &m_FloatArgument, &m_StringArgument,
&m_BoolArgument, &m_ClampedFloatArgument
}) {}
~TestArgParser() {}
YYCC::ArgParser::NumberArgument<int32_t> m_IntArgument;
YYCC::ArgParser::NumberArgument<float> m_FloatArgument;
YYCC::ArgParser::StringArgument m_StringArgument;
YYCC::ArgParser::SwitchArgument m_BoolArgument;
YYCC::ArgParser::NumberArgument<float> m_ClampedFloatArgument;
YYCC::ArgParser::OptionContext m_OptionContext;
};
// Initialize argument parser.
TestArgParser test;
// Get argument list for parsing from standard C main function.
auto al = YYCC::ArgParser::ArgumentList::CreateFromStd(argc, argv);
// Start parsing
test.Parse(al);
// Get captured string argument
if (test.m_StringArgument.IsCaptured())
auto val = test.m_StringArgument.Get();
\endcode
These code can resolve following command line:
\code{.sh}
exec -i 114514 -f 2.0 --string fuck -b --clamped-float 0.5
\endcode
For convenience, we define following terms used in this article.
\li Every items in command line: Argument.
\li \c -i, \c --clamped-float: \b Switch / \b Option. the argument starts with dash or double dash.
\li \c 114514: \b Value. the value of switch.
\section arg_parser__argument Argument
Argument is the leaf of argument parser.
It has the same position as setting in universal config manager.
\subsection arg_parser__argument__presets Argument Presets
Like setting in universal config manager,
we also provide various common used argument presets.
Current'y we support following argument presets:
\li NumberArgument: The argument storing arithmetic type (except \c bool) inside. Such as <TT>-i 114514</TT> in example.
\li StringArgument: The argument storing string inside. Such as <TT>--string fuck</TT> in example.
\li SwitchArgument: The argument storing nothing. It is just a simple switch. Such as <TT>-b</TT> in example.
When constructing these argument,
you need provide one from long name or short name, or both of them.
Short name is the argument starting with dash and long name starts with double dash.
You don't need add dash or double dash prefix when providing these names.
Please note only ASCII characters, which can be displayed on screen, can be used in these names.
Optionally, you can provide description when constructing,
which will tell user how this switch does and more infomation about this switch.
And, you can add an example to tell user which value is valid.
Next, you can specify an argument to be optional.
Optional argument can be absent in command line.
Oppositely, non-optional argument must be presented in command line,
otherwise parser will return false to indicate an error.
For checking whether an optional argument is specified,
please call AbstractArgument::IsCaptured().
Last, you can optionally assign a constraint to it,
to help argument limit its value.
However SwitchArgument must be optional argument.
Because it is true if user specify it explicit it,
and will be false if user do not give this flag.
SwitchArgument doesn't have constraint features,
because it doesn't store any value inside.
Thus no need to limit this.
\subsection arg_parser__argument__custom Custom Argument
In most cases, the combination use of argument presets and constraints is enough.
However, if you still are urge to create your personal argument,
please inherit AbstractArgument and implement essential class functions.
For the class functions you need to implement,
please refer to our argument presets.
\section arg_parser__argument_list Argument List
Argument list is a struct used by parser for parsing.
It is a higher wrapper of a simple list containing argument items.
We provide 2 ways to get argument list.
\li ArgumentList::CreateFromStd: Create argument list from standard C main function parameters.
\li ArgumentList::CreateFromWin32: Create argument list from Win32 functions in Windows.
You should use this function in Windows instead of ArgumentList::CreateFromStd.
Because the command line passed in standard C main function has encoding issue in Windows.
Use this function you will fetch correct argument list especially command including non-ASCII characters.
Please note the first argument in given command line will be stripped.
Because in most cases it point to the executable self,
and should not be seen as the part of argument list.
\section arg_parser__option_context Option Context
Please note any unknow argument will let the parser return false.
This is different with other argument parsers.
In other common argument parsers,
they will collect all unknow argument as positional argument,
or just simply ignore them.
OptionContext also will not add \c -h or \c --help switch automatically.
This is also differnent with other parsers.
You should manually add it.
However, OptionContext provide a universal help print function, OptionContext::Help.
You can directly call it to output help text if you needed (fail to parse or user order help).
\section arg_parser__limitation Limitation
This universal argument parser is a tiny parser.
It only just fulfill my personal requirements.
So it only accepts limited command line syntax.
In following content I will tell you some syntaxes which this parser \b not accept.
\subsection arg_parser__limitation__flag_combination Flag Combination
\code{.sh}
exec -l -s -h
exec -lsh
\endcode
Parser accept first line but not accept the second line.
You must write these flags independently.
\subsection arg_parser__limitation__equal_symbol Equal Symbol
\code{.sh}
exec --value 114514
exec --value=114514
exec --value:114514
\endcode
Parser only accept first line command.
You can not use equal symbol or any other symbol to assign value for specified argument.
You must write value after the argument immediately please.
\subsection arg_parser__limitation__variable_argument Variable Argument
\code{.sh}
exec -DSOME_VARABLE=SOME_VALUE
exec -D SOME_VARIABLE=SOME_VALUE
\endcode
Parser only accept second line.
However you nned to write a custom argument or constraint to holding this value.
\subsection arg_parser__limitation__switch_dependency Switch Dependency
\code{.sh}
exec --action-a --action-b
\endcode
For command line written above,
if you hope \c --action-a and \c --action-b is exclusive,
or \c --action-b only be valid if \c --action-a specified,
you should manually implement this.
Parser don't have such features to process this switch dependency.
The thing you need to do is set these switches are \b not optional.
And after parser do a success parsing,
manually calling AbstractArgument::IsCaptured to fetch whether corresponding switches are captured,
then do your personal dependency check.
*/
}

208
doc/src/carton/binstore.dox Normal file
View File

@@ -0,0 +1,208 @@
namespace yycc::carton::binstore {
/**
\page binstore Binary Settings Storage (Binstore)
The binstore module provides a binary settings storage system that allows
applications to persistently store and retrieve configuration settings in
a binary format. It includes functionality for type-safe serialization and deserialization,
setting management with unique tokens for access control, version control with migration strategies,
and comprehensive error handling.
\section binstore__overview Overview
The binstore module consists of several key components:
\li types: Basic types and error handling for the module
\li serdes: Serialization/deserialization functionality for different data types
\li setting: Management of settings with name-based lookup and token-based access
\li configuration: Version and settings collection management
\li storage: Main storage class for loading/saving settings to/from files or streams
\section binstore__example Example Usage
Here is a complete example showing how to use the binstore module:
\code{.cpp}
#include <yycc.hpp>
#include <yycc/carton/binstore.hpp>
#include <yycc/patch/stream.hpp>
#include <iostream>
#include <fstream>
using namespace yycc::carton::binstore;
using namespace yycc::patch::stream;
enum class LogLevel : uint8_t { Debug, Info, Warning, Error };
int main() {
// Create settings collection
auto settings = setting::SettingCollection();
auto int_setting_token = settings.add_setting(setting::Setting(u8"max_connections"));
auto float_setting_token = settings.add_setting(setting::Setting(u8"timeout"));
auto string_setting_token = settings.add_setting(setting::Setting(u8"server_address"));
auto bool_setting_token = settings.add_setting(setting::Setting(u8"enable_logging"));
auto enum_setting_token = settings.add_setting(setting::Setting(u8"log_level"));
// Create configuration with version 1
auto config = configuration::Configuration(1, std::move(settings));
// Create storage with the configuration
auto storage = storage::Storage(std::move(config));
// Using appropriate SerDes types for different data types
using IntSerDes = serdes::IntegralSerDes<int32_t>;
using FloatSerDes = serdes::FloatingPointSerDes<float>;
using StringSerDes = serdes::StringSerDes;
using BoolSerDes = serdes::BoolSerDes<true>; // true as default value
using EnumSerDes = serdes::EnumSerDes<LogLevel, LogLevel::Info>;
// Set values
storage.set_value<IntSerDes>(int_setting_token, 100);
storage.set_value<FloatSerDes>(float_setting_token, 2.5f);
storage.set_value<StringSerDes>(string_setting_token, u8"localhost");
storage.set_value<BoolSerDes>(bool_setting_token, true);
storage.set_value<EnumSerDes>(enum_setting_token, LogLevel::Debug);
// Save to file
if (auto result = storage.save_into_file("config.bin"); result.has_value()) {
std::cout << "Configuration saved successfully" << std::endl;
} else {
std::cout << "Failed to save configuration" << std::endl;
}
// Load from file
auto new_config = configuration::Configuration(1, setting::SettingCollection());
auto new_storage = storage::Storage(std::move(new_config));
if (auto result = new_storage.load_from_file("config.bin", storage::LoadStrategy::MigrateOld); result.has_value()) {
std::cout << "Configuration loaded successfully" << std::endl;
// Get values
int32_t max_conn = new_storage.get_value<IntSerDes>(int_setting_token);
float timeout = new_storage.get_value<FloatSerDes>(float_setting_token);
std::u8string addr = new_storage.get_value<StringSerDes>(string_setting_token);
bool logging = new_storage.get_value<BoolSerDes>(bool_setting_token);
LogLevel level = new_storage.get_value<EnumSerDes>(enum_setting_token);
std::cout << "Max connections: " << max_conn << std::endl;
std::cout << "Timeout: " << timeout << std::endl;
std::cout << "Server address: " << addr << std::endl;
std::cout << "Logging enabled: " << (logging ? "yes" : "no") << std::endl;
std::cout << "Log level: " << static_cast<int>(level) << std::endl;
} else {
std::cout << "Failed to load configuration" << std::endl;
}
return 0;
}
\endcode
\section binstore__components Components
\subsection binstore__settings Settings Management
Settings are identified by unique names and accessed via tokens. The [SettingCollection](\ref setting::SettingCollection)
manages a collection of settings and ensures no duplicates.
\subsection binstore__configuration Configuration
The [Configuration](\ref configuration::Configuration) class holds the version identifier and the collection of settings.
Version control is crucial for handling configuration migration between application versions.
\subsection binstore__storage Storage
The [Storage](\ref storage::Storage) class is the main interface for setting/getting values and loading/saving configurations.
It provides methods for both file-based and stream-based operations.
\subsection binstore__serdes Serialization/Deserialization
SerDes (Serializer/Deserializer) classes handle type-safe conversion between values and their binary representation.
Built-in SerDes types include:
\li Integral types ([IntegralSerDes](\ref serdes::IntegralSerDes))
\li Floating-point types ([FloatingPointSerDes](\ref serdes::FloatingPointSerDes))
\li String types ([StringSerDes](\ref serdes::StringSerDes))
\li Boolean types ([BoolSerDes](\ref serdes::BoolSerDes))
\li Enum types ([EnumSerDes](\ref serdes::EnumSerDes))
For some of them, you can specify value range and default value via template parameters.
\section binstore__load_strategies Load Strategies
The binstore module provides different strategies for handling version mismatches:
\li [OnlyCurrent](\ref storage::LoadStrategy::OnlyCurrent): Only accept configurations with matching version
\li [MigrateOld](\ref storage::LoadStrategy::MigrateOld): Accept matching and older versions, reject newer versions
\li [AcceptAll](\ref storage::LoadStrategy::AcceptAll): Accept all versions (not recommended for production)
\section binstore__custom_serdes Custom SerDes
Custom SerDes (Serializer/Deserializer) can be created by implementing the \c SerDes concept.
A valid SerDes must satisfy the following requirements:
\li Have a type alias called \c ValueType indicating the corresponding setting type
\li Have a member function called \c serialize that accepts a const reference of the setting data and returns \c ByteArray
or \c std::nullopt if serialization fails.
\li Have a member function called \c deserialize that converts \c ByteArray to the desired type
or returns \c std::nullopt if deserialization fails.
\li Have a member function called \c reset that returns a default \c ByteArray value.
Here is an example of a custom SerDes for storing IPv4 addresses:
\code{.cpp}
#include <cstdint>
#include <cstring>
struct IPv4Address {
std::uint8_t octets[4];
IPv4Address() : octets{0, 0, 0, 0} {}
IPv4Address(std::uint8_t a, std::uint8_t b, std::uint8_t c, std::uint8_t d) {
octets[0] = a; octets[1] = b; octets[2] = c; octets[3] = d;
}
};
struct IPv4SerDes {
using ValueType = IPv4Address;
static constexpr size_t VALUE_SIZE = sizeof(IPv4Address); // 4 octets
std::optional<types::ByteArray> serialize(const ValueType& value) const {
types::ByteArray ba;
ba.resize_data(VALUE_SIZE);
std::memcpy(ba.get_data_ptr(), value.octets, VALUE_SIZE);
return ba;
}
std::optional<ValueType> deserialize(const types::ByteArray& ba) const {
if (ba.get_data_size() != VALUE_SIZE) return std::nullopt;
ValueType value;
std::memcpy(value.octets, ba.get_data_ptr(), VALUE_SIZE);
return value;
}
types::ByteArray reset() const {
// Reset to local address
ValueType default_value(127, 0, 0, 1);
return this->serialize(default_value).value();
}
};
\endcode
To use the custom SerDes:
\code{.cpp}
// Add setting to collection
auto ip_setting_token = settings.add_setting(setting::Setting(u8"server_ip"));
// Use custom SerDes
IPv4SerDes ip_serdes;
storage.set_value<IPv4SerDes>(ip_setting_token, IPv4Address(192, 168, 1, 1));
// Retrieve value
IPv4Address ip_addr = storage.get_value<IPv4SerDes>(ip_setting_token);
\endcode
*/
}

187
doc/src/carton/clap.dox Normal file
View File

@@ -0,0 +1,187 @@
namespace yycc::carton::clap {
/**
\page clap Command Line Argument Parser (CLAP)
Command Line Argument Parser (CLAP) module for handling command line arguments and environment variables.
This module provides a comprehensive system for defining, parsing, and validating command line
arguments and environment variables. It includes components for defining application metadata,
command line options, variables, and utilities for parsing and validation.
\section clap__overview Overview
The CLAP module consists of several key components:
\li Types: Error types and result types used throughout the module
\li Validator: Type-safe validation for command line argument values
\li Option: Command line options with short and long names
\li Variable: Environment variables that can be captured
\li Summary: Application metadata (name, version, author, description)
\li Application: Complete application definition with options and variables
\li Manual: Help and version information generation
\li Parser: Command line argument parsing functionality
\li Resolver: Environment variable resolution functionality
\section clap__example Example Usage
Here is a complete example showing how to use the CLAP module:
\code{.cpp}
#include <yycc.hpp>
#include <yycc/carton/clap.hpp>
#include <yycc/patch/stream.hpp>
#include <iostream>
using namespace yycc::carton::clap;
using namespace yycc::patch::stream;
// Define an application with options and variables
int main(int argc, char* argv[]) {
// Create application summary
auto summary = summary::Summary(u8"MyApp", u8"author", u8"1.0.0", u8"A sample application");
// Create options collection
auto options = option::OptionCollection();
auto int_opt = options.add_option(option::Option(u8"i", u8"int", u8"NUM", u8"integral argument"));
auto float_opt = options.add_option(option::Option(u8"f", std::nullopt, u8"NUM", u8"floating point argument"));
auto string_opt = options.add_option(option::Option(std::nullopt, u8"string", u8"STR", u8"string argument"));
auto flag_opt = options.add_option(option::Option(u8"v", std::nullopt, std::nullopt, u8"verbose mode"));
// Create variables collection
auto variables = variable::VariableCollection();
auto env_var = variables.add_variable(variable::Variable(u8"ENV_VAR", u8"Environment variable description", true));
// Create the application and manual
auto app = application::Application(std::move(summary), std::move(options), std::move(variables));
auto manual = manual::Manual(app);
// Parse command line arguments
auto result = parser::Parser::from_system(app);
if (result.has_value()) {
auto parser = std::move(result.value());
// Get values using validators
using IntValidator = validator::IntegralValidator<int>;
using FloatValidator = validator::FloatingPointValidator<float>;
using StringValidator = validator::StringValidator;
// Check and get integer option
if (auto int_val = parser.get_value_option<IntValidator>(int_opt); int_val.has_value()) {
std::cout << "Integer value: " << int_val.value() << std::endl;
}
// Check and get float option
if (auto float_val = parser.get_value_option<FloatValidator>(float_opt); float_val.has_value()) {
std::cout << "Float value: " << float_val.value() << std::endl;
}
// Check and get string option
if (auto str_val = parser.get_value_option<StringValidator>(string_opt); str_val.has_value()) {
std::cout << "String value: " << str_val.value() << std::endl;
}
// Check flag option
if (auto flag_val = parser.get_flag_option(flag_opt); flag_val.has_value() && flag_val.value()) {
std::cout << "Verbose mode enabled" << std::endl;
}
} else {
// Print help if parsing failed
manual.print_help(std::cout);
return 1;
}
return 0;
}
\endcode
This code handles command lines like:
\code{.sh}
./myapp -i 123 -f 2.5 --string "hello world" -v
\endcode
\section clap__components Components
\subsection clap__application Application Definition
The [Application](\ref application::Application) class represents a complete command line application with its summary, options, and environment variables.
It combines the application metadata, command line options, and environment variables into a single unit.
\subsection clap__options Options
[Option](\ref option::Option) is command line arguments that can accept values or act as flags.
They can have both short names (single character)
and long names (full text). The [OptionCollection](\ref option::OptionCollection) manages a collection of options and ensures no duplicates.
\subsection clap__variables Variables
[Variable](\ref variable::Variable) represent environment variables that can be captured and validated. The [VariableCollection](\ref variable::VariableCollection)
manages a collection of environment variables and ensures no duplicates.
\subsection clap__parsing Parsing
The [Parser](\ref parser::Parser) class handles command line argument parsing. It can be created from user-provided arguments
or from system arguments (argc/argv). Values are retrieved using type-safe validators.
\subsection clap__validation Validation
Validators ensure type-safe validation of command line argument values.
The module provides built-in validators for:
\li Integral types ([IntegralValidator](\ref validator::IntegralValidator))
\li Floating-point types ([FloatingPointValidator](\ref validator::FloatingPointValidator))
\li String types ([StringValidator](\ref validator::StringValidator))
For some of them, you also can specify value range via template arguments.
\section clap__custom_validators Custom Validator
Custom validators can be created by implementing the \c Validator concept.
A valid validator must satisfy the following requirements:
\li Have a type alias called \c ReturnType indicating the return value type
\li Have a member function called \c validate that receives <TT>const std::u8string_view&</TT> as its only argument
and returns validated \c ReturnType or \c std::nullopt if validation fails
Here is an example of a custom validator that validates email addresses:
\code{.cpp}
#include <yycc/string/reinterpret.hpp>
#include <regex>
struct EmailValidator {
using ReturnType = std::u8string;
std::optional<ReturnType> validate(const std::u8string_view& sv) const {
// Simple email validation using regex
static const std::regex email_regex(
R"([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})");
auto email_str = yycc::string::reinterpret::as_ordinary_view(sv);
if (std::regex_match(email_str, email_regex)) {
return sv;
}
return std::nullopt;
}
};
\endcode
To use the custom validator:
\code{.cpp}
// Add option to application
auto email_opt = options.add_option(option::Option(std::nullopt, u8"email", u8"EMAIL", u8"Email address"));
// Use custom validator
if (auto email_val = parser.get_value_option<EmailValidator>(email_opt); email_val.has_value()) {
std::cout << yycc::patch::format(u8"Valid email: {}", email_val); << std::endl;
}
\endcode
\section clap__limitations Limitations
Due to the limitations of implementation,
CLAP now only allow only zero or one associated value for single option.
More than one assocciated value for single option is not supported.
*/
}

View File

@@ -1,151 +0,0 @@
namespace YYCC::ConfigManager {
/**
\page config_manager Universal Config Manager
YYCC::ConfigManager give programmer an universal way to manage its program settings.
There is an example about how to use universal config manager.
In following content, we will describe it in detail.
\code
class TestConfigManager {
public:
enum class TestEnum : int8_t {
Test1, Test2, Test3
};
TestConfigManager() :
m_IntSetting(YYCC_U8("int-setting"), INT32_C(0)),
m_StringSetting(YYCC_U8("string-setting"), YYCC_U8("")),
m_FloatSetting(YYCC_U8("float-setting"), 0.0f, YYCC::Constraints::GetNumberRangeConstraint<float>(-1.0f, 1.0f)),
m_EnumSetting(YYCC_U8("enum-setting"), TestEnum::Test1),
m_CoreManager(YYCC_U8("test.cfg"), UINT64_C(0), {
&m_IntSetting, &m_StringSetting, &m_FloatSetting, &m_EnumSetting
})
{}
~TestConfigManager() {}
YYCC::ConfigManager::NumberSetting<int32_t> m_IntSetting;
YYCC::ConfigManager::StringSetting m_StringSetting;
YYCC::ConfigManager::NumberSetting<float> m_FloatSetting;
YYCC::ConfigManager::NumberSetting<TestEnum> m_EnumSetting;
YYCC::ConfigManager::CoreManager m_CoreManager;
};
// Initialize config manager
TestConfigManager test;
// Load settings.
test.m_CoreManager.Load()
// Get string setting value.
auto val = test.m_StringSetting.Get();
\endcode
\section config_manager__setting Setting
Setting can be seen as the leaf of the config tree.
Each setting describe a single configuration entry.
\subsection config_manager__setting__presets Setting Presets
We currently provide 2 setting preset classes which you can directly use.
\li NumberSetting: The setting storing a number inside.
It is a template class. Support all arithmetic and enum types (integral, floating point, bool, enum).
\li StringSetting: The setting storing a string inside.
When constructing these settings,
you need to provide its unique name which will be used when saving to file or reading from file.
Also you need to provide a default value for it.
It will be used when fail to read file or initializing itself.
Optionally, you also can provide a constraint to setting.
Constraint is the struct instructing library to limit value in specified range.
It usually is used for making sure the setting stored value is valid.
See \ref constraints chapters to know how we provide constraints.
\subsection config_manager__setting__custom Custom Setting
In most cases, the combination use of setting presets and constraints is enough.
However, if you still are urge to create your personal setting,
please inherit AbstractSetting and implement essential class functions.
For the class functions you need to implement,
please refer to our setting presets, NumberSetting and StringSetting.
\section config_manager__core_manager Core Manager
CoreManager manage a collection of settings.
And have responsibility to reading and writing config file.
We highly suggest that you create a personal config manager class like example does.
Then put essential settings and core manager inside it.
Please note you must place core manager after all settings.
Because the order of C++ initializing its class member is the order you declared them.
The constructor of core manager need the pointer to all it managed settings,
so it must be initialized after initializing all settings.
When initializing core manager, you need assign config file path first.
Then you need specify a version number.
Version number is important.
It will be used when reading config file and only can be increased if needed (version can not downgrade).
The last argument is an initializer list which contain the \b pointer to all settings this manager managed.
When executing YYCC::ConfigManager::CoreManager::Load to load configs, it will perform following steps one by one:
<UL>
<LI>
Open given config file.
<UL>
<LI>
If given file is not existing, loading function will simply return and all configs will be reset to its default value.
</LI>
<LI>
Success to open file, go to next step.
</LI>
</UL>
</LI>
<LI>
Fetch version number from file.
<UL>
<LI>
If fail to read version number from file, loading function will simply return and all configs will be reset to its default value.
</LI>
<LI>
If the version of config file is higher than your specified version number when constructing this class,
core manager will assume you are trying to read a config file created by a higher version program,
and will reject reading and use default value for all settings.
</LI>
<LI>
If the version of config file is lower than your specified version number,
core manager will try to read config file and do proper migration (set default value for configs which do not existing) if possible.
</LI>
<LI>
If the version of config file is equal than your specified version number,
core manager will read config file normally.
</LI>
</UL>
</LI>
<LI>
Read config file body.
<UL>
<LI>
If any IO error occurs when reading, loading function will simply return.
All read config will keep their read value and all configs which has not been read will keep their default value.
</LI>
<LI>
If some config can not parse binary data to its type,
this config will be skipped and core manager will process next config.
This config will keep its default value.
</LI>
</UL>
</LI>
</UL>
All of these scenarios can be found by the return value of loading function.
The return type of loading function, ConfigLoadResult is a flag enum.
You can find whether loading process happend specified issue by using bitwise operation on it.
*/
}

View File

@@ -1,181 +0,0 @@
namespace YYCC::ConsoleHelper {
/**
\page console_helper Console Helper
This helper provide console related stuff.
This helper includes 2 parts.
First part is console color.
It was constituted by a bunch of macros.
The second part is universal console IO function because Windows is lacking in UTF8 console IO.
All of these parts will be introduced in following content.
\section console_helper__color Console Color
YYCC::ConsoleHelper provide a bunch of macros which can allow you output colorful text in terminal.
Supported color is limited in 16 colors,
because these color is implemented by ASCII Escape Code: https://en.wikipedia.org/wiki/ANSI_escape_code .
So if your terminal do not support this, such as default Windows terminal, or teletypewriter,
you will see some unrecognised characters surrounding with your output.
That's ASCII Escape Code.
\subsection console_helper__color__enable_win_color Enable Color in Windows Console
As we introduced in above,
you may know Windows console does not support ASCII Escape Code color in default.
However #EnableColorfulConsole can fix this issue.
#EnableColorfulConsole will forcely enable ASCII Escape Code support in Windows console if possible.
Thus you can write colorful text in Windows console freely.
We suggest you to call this function at the beginning of program.
Considering most Linux console supports ASCII Escape Code very well,
this function does nothing in non-Windows platform.
So it is not essential that brack this function calling with Windows-only \c \#if.
\subsection console_helper__color__common Common Usage
For common scenarios, you can use macro like this:
\code
YYCC::ConsoleHelper::WriteLine(YYCC_U8(YYCC_COLOR_LIGHT_RED("Light Red Text")));
YYCC::ConsoleHelper::WriteLine(YYCC_U8("I am " YYCC_COLOR_LIGHT_RED("Light Red")));
\endcode
In first line, it will make <TT>"Light Red Text"</TT> to be shown in light red color.
And for second line, it will make <TT>"Light Red"</TT> to be shown in light red color,
but <TT>"I am "</TT> will keep default console font color.
You also may notice this macro is used with YYCC_U8 macro.
Because #WriteLine only accept UTF8 argument.
So please note if you use console color macro with YYCC_U8,
please make YYCC_U8 always is located the outside.
Otherwise, YYCC_U8 will fail to make the whole become UTF8 stirng as we introduced in \ref library_encoding.
Because console color macro is implemented by string literal concatenation internally.
YYCC_COLOR_LIGHT_RED is a member in YYCC_COLOR macro family.
YYCC_COLOR macro family has 16 members for 16 different colors:
\li YYCC_COLOR_BLACK
\li YYCC_COLOR_RED
\li YYCC_COLOR_GREEN
\li YYCC_COLOR_YELLOW
\li YYCC_COLOR_BLUE
\li YYCC_COLOR_MAGENTA
\li YYCC_COLOR_CYAN
\li YYCC_COLOR_WHITE
\li YYCC_COLOR_LIGHT_BLACK
\li YYCC_COLOR_LIGHT_RED
\li YYCC_COLOR_LIGHT_GREEN
\li YYCC_COLOR_LIGHT_YELLOW
\li YYCC_COLOR_LIGHT_BLUE
\li YYCC_COLOR_LIGHT_MAGENTA
\li YYCC_COLOR_LIGHT_CYAN
\li YYCC_COLOR_LIGHT_WHITE
\subsection console_helper__color__embedded Embedded Usgae
In some cases, you want change console at some time point and reset it in another time point.
You can use color macros like following example:
\code
YYCC::ConsoleHelper::WriteLine(YYCC_U8(YYCC_COLORHDR_LIGHT_BLUE));
// Write as much as you liked
YYCC::ConsoleHelper::WriteLine(YYCC_U8("some string"));
YYCC::ConsoleHelper::WriteLine(YYCC_U8("another string"));
YYCC::ConsoleHelper::WriteLine(YYCC_U8(YYCC_COLORTAIL));
\endcode
At first line, we output YYCC_COLORHDR_LIGHT_BLUE which is in YYCC_COLORHDR macro family.
It is colorful text ASCII Escape Code head.
It will make all following output become light blue color,
until the last line we output YYCC_COLORTAIL to reset console color to original color.
Same as YYCC_COLOR macro family,
YYCC_COLORHDR macro family also has 16 members for 16 different colors:
\li YYCC_COLORHDR_BLACK
\li YYCC_COLORHDR_RED
\li YYCC_COLORHDR_GREEN
\li YYCC_COLORHDR_YELLOW
\li YYCC_COLORHDR_BLUE
\li YYCC_COLORHDR_MAGENTA
\li YYCC_COLORHDR_CYAN
\li YYCC_COLORHDR_WHITE
\li YYCC_COLORHDR_LIGHT_BLACK
\li YYCC_COLORHDR_LIGHT_RED
\li YYCC_COLORHDR_LIGHT_GREEN
\li YYCC_COLORHDR_LIGHT_YELLOW
\li YYCC_COLORHDR_LIGHT_BLUE
\li YYCC_COLORHDR_LIGHT_MAGENTA
\li YYCC_COLORHDR_LIGHT_CYAN
\li YYCC_COLORHDR_LIGHT_WHITE
However YYCC_COLORTAIL is YYCC_COLORTAIL.
There is no other variant for different colors.
Because all tail of colorful ASCII Escape Code is same.
\section console_helper__universal_io Universal IO Function
\subsection console_helper__universal_io__why Why?
Windows console doesn't support UTF8 very well.
The standard input output functions can not work properly with UTF8 on Windows.
So we create this namespace and provide various console-related functions
to patch Windows console and let it more like the console in other platforms.
The function provided in this function can be called in any platforms.
In Windows, the implementation will use Windows native function,
and in other platform, the implementation will redirect request to standard C function like \c std::fputs and etc.
So the programmer do not need to be worried about which function should they use,
and don't need to use macro to use different IO function in different platforms.
It is just enough that fully use the functions provided in this namespace.
All IO functions this namespace provided are UTF8-based.
It also means that input output string should always be UTF8 encoded.
\subsection console_helper__universal_io__input Input Functions
Please note that EOL will automatically converted into LF on Windows platform, not CRLF.
This action actually is removing all CR chars in result string.
This behavior affect nothing in most cases but it still is possible break something in some special case.
Due to implementation, if you decide to use this function,
you should give up using any other function to read stdin stream,
such as \c std::gets() and \c std::cin.
Because this function may read chars which is more than needed.
These extra chars will be stored in this function and can be used next calling.
But these chars can not be visited by stdin again.
This behavior may cause bug.
So if you decide using this function, stick on it and do not change.
Due to implementation, this function do not support hot switch of stdin.
It means that stdin can be redirected before first calling of this function,
but it should not be redirected during program running.
The reason is the same one introduced above.
\subsection console_helper__universal_io__output Output Functions
In current implementation, EOL will not be converted automatically to CRLF.
This is different with other stream read functions provided in this namespace.
Comparing with other stream read functions provided in this namespace,
stream write function support hot switch of stdout and stderr.
Because they do not have internal buffer storing something.
In this namespace, there are various stream write function.
There is a list telling you how to choose one from them for using:
\li Functions with leading "Err" will write data into stderr,
otherwise they will write data into stdout.
\li Functions with embedded "Format" are output functions with format feature
like \c std::fprintf(), otherwise the functions with embedded "Write" will
only write plain string like \c std::fputs().
\li Functions with trailing "Line" will write extra EOL to break current line.
This is commonly used, otherwise functions will only write the text provided by arguments,
without adding something.
*/
}

View File

@@ -0,0 +1,74 @@
namespace yycc::carton::csconsole {
/**
\page csconsole Universal IO Function
This namespace provide universal console IO function which is more like C\# provided,
because Windows is lacking in UTF8 console IO.
\section csconsole__deprecation Deprecation Notes
This namespace, or this module is deprecated.
Its provided functions are too aggressive and can not cover all use scenarios.
So it is suggested not to use this namespace.
Programmers should handle Windows UTF8 issues on their own.
\section csconsole__why Why?
Windows console doesn't support UTF8 very well.
The standard input output functions can not work properly with UTF8 on Windows.
So we create this namespace and provide various console-related functions
to patch Windows console and let it more like the console in other platforms.
The function provided in this function can be called in any platforms.
In Windows, the implementation will use Windows native function,
and in other platform, the implementation will redirect request to standard C function like \c std::fputs and etc.
So the programmer do not need to be worried about which function should they use,
and don't need to use macro to use different IO function in different platforms.
It is just enough that fully use the functions provided in this namespace.
All IO functions this namespace provided are UTF8-based.
It also means that input output string should always be UTF8 encoded.
\section csconsole__input Input Functions
Please note that EOL will automatically converted into LF on Windows platform, not CRLF.
This action actually is removing all CR chars in result string.
This behavior affect nothing in most cases but it still is possible break something in some special case.
Due to implementation, if you decide to use this function,
you should give up using any other function to read stdin stream,
such as \c std::gets() and \c std::cin.
Because this function may read chars which is more than needed.
These extra chars will be stored in this function and can be used next calling.
But these chars can not be visited by stdin again.
This behavior may cause bug.
So if you decide using this function, stick on it and do not change.
Due to implementation, this function do not support hot switch of stdin.
It means that stdin can be redirected before first calling of this function,
but it should not be redirected during program running.
The reason is the same one introduced above.
\section csconsole__output Output Functions
In current implementation, EOL will not be converted automatically to CRLF.
This is different with other stream read functions provided in this namespace.
Comparing with other stream read functions provided in this namespace,
stream write function support hot switch of stdout and stderr.
Because they do not have internal buffer storing something.
In this namespace, there are various stream write function.
There is a list telling you how to choose one from them for using:
\li Functions with leading "e" (like eformat, ewrite) will write data into stderr,
otherwise they will write data into stdout.
\li Functions with embedded "format" (format, format_line, eformat, eformat_line) are output functions with format feature like \c std::fprintf(),
otherwise the functions with embedded "write" in the name (write, write_line, ewrite, ewrite_line) will only write plain string like \c std::fputs().
\li Functions with trailing "line" (format_line, write_line, eformat_line, ewrite_line) will write extra EOL to break current line.
This is commonly used, otherwise functions will only write the text provided by arguments,
without adding something.
*/
}

View File

@@ -1,148 +0,0 @@
namespace YYCC::EncodingHelper {
/**
\page encoding_helper Encoding Helper
YYCC::EncodingHelper namespace include all encoding related functions:
\li The convertion between ordinary string and UTF8 string which has been introduced in chapter \ref library_encoding.
\li Windows specific convertion between \c WCHAR, UTF8 string and string encoded by other encoding.
\li The convertion among UTF8, UTF16 and UTF32.
\section encoding_helper__ordinary_utf8_conv Ordinary & UTF8 Convertion
These convertion functions have been introduced in previous page.
See \ref library_encoding for more infomation.
YYCC supports following convertions:
\li #ToUTF8: Convert ordinary string to UTF8 string.
\li #ToUTF8View: Same as ToUTF8, but return string view instead.
\li #ToOrdinary: Convert UTF8 string to ordinary string.
\li #ToOrdinaryView: Same as ToOrdinary, but return string view instead.
\section encoding_helper__win_conv Windows Specific Convertion
During Windows programming, the convertion between Microsoft specified \c wchar_t and \c char is an essential operation.
Because Windows has 2 different function system, the functions ended with A and the functions ended with W.
(Microsoft specified \c wchar_t is \c 2 bytes long. It's different with Linux defined common 4 bytes long).
Thus YYCC provides these convertion functions in Windows to help programmer have better programming experience.
These functions are Windows specific, so they will be invisible in other platforms.
Please use them carefully (make sure that you are using them only in Windows environment).
YYCC supports following convertions:
\li #WcharToChar: Convert \c wchar_t string to code page specified string.
\li #CharToWchar: The reversed convertion of WcharToChar.
\li #CharToChar: Convert string between 2 different code pages. It's a shortcut of calling CharToWchar and WcharToChar successively.
\li #WcharToUTF8: Convert \c wchar_t string to UTF8 string.
\li #UTF8ToWchar: The reversed convertion of WcharToUTF8.
\li #CharToUTF8: Convert code page specified string to UTF8 string.
\li #UTF8ToChar: The reversed convertion of CharToUTF8.
Code Page is a Windows concept.
If you don't understand it, please view corresponding Microsoft documentation.
\section encoding_helper__utf_conv UTF8 UTF16 UTF32 Convertion
The convertion between UTF8, UTF16 and UTF32 is not common but essential.
These convertions can be achieved by standard library functions and classes.
(they are actually done by standard library functions in our implementation)
But we provided functions are easy to use and have clear interface.
These functions are different with the functions introduced above.
They can be used in any platform, not confined in Windows platforms.
YYCC supports following convertions:
\li #UTF8ToUTF16: Convert UTF8 string to UTF16 string.
\li #UTF16ToUTF8: The reversed convertion of UTF8ToUTF16.
\li #UTF8ToUTF32: Convert UTF8 string to UTF32 string.
\li #UTF32ToUTF8: The reversed convertion of UTF8ToUTF32.
\section encoding_helper__overloads Function Overloads
Every encoding convertion functions (except the convertion between UTF8 and ordinary string) have 4 different overloads for different scenarios.
Take #WcharToChar for example.
There are following 4 overloads:
\code
bool WcharToChar(const std::wstring_view& src, std::string& dst, UINT code_page);
bool WcharToChar(const wchar_t* src, std::string& dst, UINT code_page);
std::string WcharToChar(const std::wstring_view& src, UINT code_page);
std::string WcharToChar(const wchar_t* src, UINT code_page);
\endcode
\subsection encoding_helper__overloads_destination Destination String
According to the return value, these 4 overload can be divided into 2 types.
The first type returns bool. The second type returns \c std::string instance.
For the first type, it always return bool to indicate whether the convertion is success.
Due to this, the function must require an argument for holding the result string.
So you can see the functions belonging to this type always require a reference to \c std::string in argument.
Oppositely, the second directly returns result by return value.
It doesn't care the success of convertion and will return empty string if convertion failed.
Programmer can more naturally use it because the retuen value itself is the result.
There is no need to declare a variable before calling convertion function for holding result.
All in all, the first type overload should be used in strict scope.
The success of convertion will massively affect the behavior of your following code.
For example, the convertion code is delivered to some system function and it should not be empty and etc.
The second type overload usually is used in lossen scenarios.
For exmaple, this overload usually is used in console output because it usually doesn't matter.
There is no risk even if the convertion failed (just output a blank string).
For the first type, please note that there is \b NO guarantee that the argument holding return value is not changed.
Even the convertion is failed, the argument holding return value may still be changed by function itself.
In this case, the type of result is \c std::string because this is function required.
In other functions, such as #WcharToUTF8, the type of result can be \c yycc_u8string or etc.
So please note the type of result is decided by convertion function itself, not only \c std::string.
\subsection encoding_helper__overloads__source Source String
According to the way providing source string,
these 4 overload also can be divided into 2 types.
The first type take a reference to constant \c std::wstring_view.
The second type take a pointer to constant \c wchar_t.
For first type, it will take the whole string for convertion, including \b embedded NUL terminal.
Please note we use string view as argument.
It is compatible with corresponding raw string pointer and string container.
So it is safe to directly pass \c std::wstring for this function.
For second type, it will assume that you passed argument is a NUL terminated string and send it for convertion.
The result is clear.
If you want to process string with \b embedded NUL terminal, please choose first type overload.
Otherwise the second type overload is enough.
Same as destination string, the type of source is also decided by the convertion function itself.
For exmaple, the type of source in #UTF8ToWchar is \c yycc_u8string_view and \c yycc_char8_t,
not \c std::wstring and \c wchar_t.
\subsection encoding_helper__overloads__extra Extra Argument
There is an extra argument called \c code_page for #WcharToChar.
It indicates the code page of destination string,
because this function will convert \c wchar_t string to the string with specified code page encoding.
Some convertion functions have extra argument like this,
because they need more infomations to decide what they need to do.
Some convertion functions don't have extra argument.
For exmaple, the convertion between \c wchar_t string and UTF8 string.
Because both source string and destination string are concrete.
There is no need to provide any more infomations.
\subsection encoding_helper__overloads__conclusion Conclusion
Mixing 2 types of source string and 2 types of destination string,
we have 4 different overload as we illustrated before.
Programmer can use them freely according to your requirements.
And don't forget to provide extra argument if function required.
*/
}

93
doc/src/carton/fft.dox Normal file
View File

@@ -0,0 +1,93 @@
namespace yycc::carton::fft {
/**
\page fft Homemade FFT
This namespace provides a fast Fourier transform (FFT) implementation for signal processing applications.
It includes classes for performing FFT computations on complex and real-valued signals, along with
window functions to reduce spectral leakage.
\section fft__basic_usage Basic Usage
To use the FFT functionality for general purposes, use the FriendlyFft class:
\code
#include "yycc/carton/fft.hpp"
using namespace yycc::carton::fft;
// Create FFT instance for 8-point transform
FriendlyFft<size_t, float, 8u> fft;
// Prepare input data (must be power of 2 in length)
float time_scope[8] = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f};
float freq_scope[4]; // Output is half the input size
// Create window function to reduce spectral leakage
Window<size_t, float, 8u> window(WindowType::HanningWindow);
// Perform FFT transformation
fft.easy_compute(time_scope, freq_scope, window);
// freq_scope now contains frequency domain data
\endcode
\section fft__window_functions Window Functions
The library provides window functions to reduce spectral leakage:
\code
// Create a Hanning window for 16-point data
Window<size_t, float, 16u> hanning_window(WindowType::HanningWindow);
// Apply window to your data
float data[16];
// ... initialize data ...
hanning_window.apply_window(data);
\endcode
\section fft__direct_fft Direct FFT Computation
For more control over the FFT computation, use the core Fft class:
\code
#include "yycc/carton/fft.hpp"
// Create FFT instance for 16-point transform
Fft<size_t, double, 16u> fft;
// Prepare complex input data
std::complex<double> data[16];
// ... initialize complex data ...
// Perform FFT transformation
fft.compute(data);
// data now contains transformed values
\endcode
\section fft__predefined_types Predefined Types
The library provides commonly used FFT types for convenience:
\code
// Float precision FFTs
Fft4F fft4f; // 4-point float FFT
Fft8F fft8f; // 8-point float FFT
Fft16F fft16f; // 16-point float FFT
Fft256F fft256f; // 256-point float FFT
// Double precision FFTs
Fft4 fft4; // 4-point double FFT
Fft8 fft8; // 8-point double FFT
Fft16 fft16; // 16-point double FFT
Fft256 fft256; // 256-point double FFT
\endcode
\section fft__requirements Requirements
- Template parameters must satisfy certain constraints:
- \c TIndex: The index type used by FFT which must be an unsigned integral type.
- \c TFloat: The float point type used by FFT.
- \c VN: The point of FFT which must be a power of 2 and >= 2.
*/
}

View File

@@ -1,30 +1,30 @@
namespace YYCC::ExceptionHelper {
namespace yycc::carton::ironpad {
/**
\page exception_helper Unhandled Exception Handler
\page ironpad Unhandled Exception Handler
Most Linux users are familiar with core dump.
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.
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.
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 will be invisible on other platforms.
It still be available on other operating systems but all of its functions are do nothing.
\section exception_helper__usage Usage
\section ironpad__usage Usage
\subsection exception_helper__usage__code Register Code
\subsection ironpad__usage__code Register Code
In most scenarios, programmer only need call #Register when program started or module loaded.
And call #Unregister when program exited or module unloaded.
In most scenarios, programmer only need call #startup when program started or module loaded.
And call #shutdown 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 #Register as a callback.
Optionally, you can provide a function pointer during calling #startup as a callback.
The prototype of this function pointer is #ExceptionCallback.
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 exception_helper__notes__singleton design.
due to \ref ironpad__notes__singleton design.
\subsection exception_helper__usage__location Location
\subsection ironpad__usage__location Location
When unhandled exception occurs,
unhandled exception handler will try to record error log and core dump in following path:
\li Error Log: <TT>\%LOCALAPPDATA\%\\CrashDumps\\<I>program.exe</I>.<I>pid</I>.log</TT>
\li Core Dump: <TT>\%LOCALAPPDATA\%\\CrashDumps\\<I>program.exe</I>.<I>pid</I>.dmp</TT>
\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>
The italic characters <I>program.exe</I> and <I>pid</I> will be replaced by program name and process ID respectively at runtime.
Directory <TT>\%LOCALAPPDATA\%\\CrashDumps</TT> also is Windows used crash dump directory.
So you may see some other core dumps done by Windows in it.
Directory <TT>\%LOCALAPPDATA\%\\IronPad</TT> is the dedicated directory for this module.
So you may see the generated logs and dumps in it.
\subsection exception_helper__usage__last_remedy Last Remedy
\subsection ironpad__usage__last_remedy Last Remedy
If unhandled exception handler occurs error, these stuff may not be generated correctly.
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 exception_helper__notes Notes
\section ironpad__notes Notes
\subsection exception_helper__notes__thread_safe Thread Safe
\subsection ironpad__notes__thread_safe Thread Safe
All exposed functions in YYCC::ExceptionHelper are thread safe.
The implementation uses \c std:mutex to ensure this.
All exposed functions in this namespace are thread safe.
The implementation uses \c std::mutex to ensure this.
\subsection exception_helper__notes__singleton Singleton Handler
\subsection ironpad__notes__singleton Singleton Handler
YYCC::ExceptionHelper also have a mechanism that make sure the same unhandled exception handler implementation only appear once in the same process.
This namespace also have a mechanism that make sure the same unhandled exception handler implementation only appear once in the same process.
For example, you have an executable program A.exe, and 2 dynamic libraries B.dll and C.dll.
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 #Register,
Although both A.exe and B.dll call #startup,
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 #Register are loaded.
no matter how many modules calling #startup 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 #Register only run once in the same process.
to make sure #startup only run once in the same process.
It is very like the implementation of singleton application.
\subsection exception_helper__notes__recursive_calling Recursive Calling
\subsection ironpad__notes__recursive_calling Recursive Calling
The implementation of unhandled exception handler may also will throw exception.
This will cause infinite recursive calling.
YYCC::ExceptionHelper has internal mechanism to prevent this bad case.
This namespace has internal mechanism to prevent this bad case.
If this really happened, the handler will quit silent and will not cause any issue.
Programmer don't need to worry about this.
\subsection exception_helper__notes__user_callback The Timing of User Callback
\subsection ironpad__notes__user_callback The Timing of User Callback
The timing of calling user callback is the tail of unhandled exception handler.
It means that all log and coredump have been written if possible before calling callback.

102
doc/src/carton/lexer61.dox Normal file
View File

@@ -0,0 +1,102 @@
namespace yycc::carton::lexer61 {
/**
\page lexer61 Homemade Command Line Lexer
This namespace provides a lexer for parsing command-line arguments, supporting various quoting mechanisms,
escape sequences, and Unicode characters. It follows the standard shell parsing rules for handling
arguments containing spaces and special characters.
\section lexer61__basic_usage Basic Usage
To parse command line arguments, create a Lexer61 instance and call the Lexer61::lex() method:
\code
#include "yycc/carton/lexer61.hpp"
using namespace yycc::carton::lexer61;
Lexer61 lexer;
auto result = lexer.lex(u8"program arg1 arg2 arg3");
if (result.has_value()) {
auto args = std::move(result.value());
// args contains: [u8"program", u8"arg1", u8"arg2", u8"arg3"]
for (const auto& arg : args) {
std::wcout << reinterpret_cast<const wchar_t*>(arg.c_str()) << std::endl;
}
}
\endcode
\section lexer61__quoting_support Quoting Support
The lexer supports both single and double quotes for grouping arguments with spaces:
\code
Lexer61 lexer;
// Double quotes
auto result1 = lexer.lex(u8R"(program "argument with spaces" end)");
// Result: [u8"program", u8"argument with spaces", u8"end"]
// Single quotes
auto result2 = lexer.lex(u8"program 'another argument' end");
// Result: [u8"program", u8"another argument", u8"end"]
// Mixed quotes
auto result3 = lexer.lex(u8R"(program "double quoted 'single inside'" 'single quoted "double inside"')");
// Result: [u8"program", u8"double quoted 'single inside'", u8"single quoted \"double inside\""]
\endcode
\section lexer61__escape_sequences Escape Sequences
The lexer supports escape sequences for including special characters:
\code
Lexer61 lexer;
auto result = lexer.lex(u8R"(program escaped\ space "quoted with \" quote" 'single with \' quote')");
// Result: [u8"program", u8"escaped space", u8"quoted with \" quote", u8"single with \' quote"]
\endcode
\section lexer61__unicode_support Unicode Support
The lexer fully supports Unicode characters in command line arguments:
\code
Lexer61 lexer;
auto result = lexer.lex(u8"程序 中文 参数");
// Result: [u8"程序", u8"中文", u8"参数"]
// With quotes
auto result2 = lexer.lex(u8R"(程序 "中文 参数" '另一个"引号"参数')");
// Result: [u8"程序", u8"中文 参数", u8"另一个\"引号\"参数"]
\endcode
\section lexer61__empty_arguments Empty Arguments
Empty arguments can be represented with empty quotes:
\code
Lexer61 lexer;
auto result = lexer.lex(u8R"(program "" '')");
// Result: [u8"program", u8"", u8""]
\endcode
\section lexer61__error_handling Error Handling
The lexer uses \c std::expected for error handling:
\code
Lexer61 lexer;
auto result = lexer.lex(u8R"(program "unclosed quote)");
if (!result.has_value()) {
// Handle error - in this case, unclosed quote
std::cerr << "Error: unclosed quote" << std::endl;
std::abort();
}
\endcode
*/
}

202
doc/src/carton/pycodec.dox Normal file
View File

@@ -0,0 +1,202 @@
namespace yycc::carton::pycodec {
/**
\page pycodec Unified Codec (Python-like Codec)
\section pycodec__overview Overview
The unified encoding conversion module provides a consistent interface for character encoding conversion across different platforms.
It automatically selects the appropriate backend implementation based on the platform and available features.
\section pycodec__classes Available Classes
\subsection pycodec__classes__char Character to/from UTF-8 Conversion
Convert between named encodings and UTF-8 using a unified interface:
\code
#include <yycc/carton/pycodec.hpp>
// Example: Converting from a named encoding to UTF-8
CharToUtf8 converter("GBK"); // or "ISO-8859-1", "SHIFT-JIS", etc.
std::string gbk_text = "你好,世界!";
auto result = converter.to_utf8(gbk_text);
if (result.has_value()) {
std::u8string utf8_text = result.value();
// Use utf8_text...
} else {
// Handle conversion error
}
\endcode
\code
// Example: Converting from UTF-8 to a named encoding
Utf8ToChar converter("GBK");
std::u8string utf8_text = u8"Hello, 世界!";
auto result = converter.to_char(utf8_text);
if (result.has_value()) {
std::string gbk_text = result.value();
// Use gbk_text...
} else {
// Handle conversion error
}
\endcode
\subsection pycodec__classes__wchar Wide Character to/from UTF-8 Conversion
Convert between wide character strings and UTF-8:
\code
#include <yycc/carton/pycodec.hpp>
// Example: Converting wide character to UTF-8
WcharToUtf8 converter;
std::wstring wide_text = L"Hello, 世界!";
auto result = converter.to_utf8(wide_text);
if (result.has_value()) {
std::u8string utf8_text = result.value();
// Use utf8_text...
} else {
// Handle conversion error
}
\endcode
\code
// Example: Converting UTF-8 to wide character
Utf8ToWchar converter;
std::u8string utf8_text = u8"Hello, 世界!";
auto result = converter.to_wchar(utf8_text);
if (result.has_value()) {
std::wstring wide_text = result.value();
// Use wide_text...
} else {
// Handle conversion error
}
\endcode
\subsection pycodec__classes__utf16_utf32 UTF-8 to/from UTF-16/UTF-32 Conversion
Convert between UTF encodings:
\code
#include <yycc/carton/pycodec.hpp>
// Example: Converting UTF-8 to UTF-16
Utf8ToUtf16 converter;
std::u8string utf8_text = u8"Hello, 世界!";
auto result = converter.to_utf16(utf8_text);
if (result.has_value()) {
std::u16string utf16_text = result.value();
// Use utf16_text...
} else {
// Handle conversion error
}
\endcode
\code
// Example: Converting UTF-16 to UTF-8
Utf16ToUtf8 converter;
std::u16string utf16_text = u"Hello, 世界!";
auto result = converter.to_utf8(utf16_text);
if (result.has_value()) {
std::u8string utf8_text = result.value();
// Use utf8_text...
} else {
// Handle conversion error
}
\endcode
\code
// Example: Converting UTF-8 to UTF-32
Utf8ToUtf32 converter;
std::u8string utf8_text = u8"Hello, 世界! 🌍";
auto result = converter.to_utf32(utf8_text);
if (result.has_value()) {
std::u32string utf32_text = result.value();
// Use utf32_text...
} else {
// Handle conversion error
}
\endcode
\code
// Example: Converting UTF-32 to UTF-8
Utf32ToUtf8 converter;
std::u32string utf32_text = U"Hello, 世界! 🌍";
auto result = converter.to_utf8(utf32_text);
if (result.has_value()) {
std::u8string utf8_text = result.value();
// Use utf8_text...
} else {
// Handle conversion error
}
\endcode
\section pycodec__utility Utility Functions
\subsection pycodec__utility__validation Encoding Name Validation
Check if an encoding name is valid in the current environment:
\code
#include <yycc/carton/pycodec.hpp>
// Example: Validating an encoding name
bool is_valid = is_valid_encoding_name(u8"UTF-8");
if (is_valid) {
std::cout << "UTF-8 is a valid encoding name\n";
} else {
std::cout << "UTF-8 is not supported\n";
}
// Test another encoding
is_valid = is_valid_encoding_name(u8"GBK");
\endcode
\section pycodec__error_handling Error Handling
All functions in this module return a result containing either
a ConvError struct represents conversion errors, or the final converted string.
\code
#include <yycc/carton/pycodec.hpp>
CharToUtf8 converter("INVALID_ENCODING_NAME");
std::string text = "Hello";
auto result = converter.to_utf8(text);
if (result.has_value()) {
std::u8string converted = result.value();
// Process successfully converted string
} else {
// Handle conversion failure
std::cout << "Conversion failed\n";
}
\endcode
\section pycodec__backend_specifics Platform-Specific Backends
For detailed information about the specific platform backends, see:
\li \ref encoding__windows : Windows-specific implementation using Win32 APIs
\li \ref encoding__iconv : Iconv-based implementation for POSIX-like systems
\section pycodec__notes Notes
For all supported encoding names and their aliases,
please browse code written in <TT>script/pycodec</TT> located in our source code.
Please also note that not all encoding name has implementation for all platforms.
Some uncommon encoding names are not supported on some backend due to the limitations of the corresponding baskend.
These also can be found in that directory introduced above.
*/
}

107
doc/src/carton/tabulate.dox Normal file
View File

@@ -0,0 +1,107 @@
namespace yycc::carton::tabulate {
/**
\page tabulate Tabulate Utilities
This namespace provides utilities for creating formatted tables in console output.
It supports Unicode text, automatic column width calculation, customizable headers,
and flexible display options.
\section tabulate__basic_usage Basic Usage
To create a simple table with headers and data rows:
\code
#include "yycc/carton/tabulate.hpp"
using namespace yycc::carton::tabulate;
// Create a table with 3 columns
Tabulate table(3);
// Set the header
table.set_header({u8"Name", u8"Age", u8"City"});
// Add data rows
table.add_row({u8"Alice", u8"30", u8"New York"});
table.add_row({u8"Bob", u8"25", u8"Los Angeles"});
table.add_row({u8"Charlie", u8"35", u8"Chicago"});
// Print the table
table.print();
\endcode
This will output:
\verbatim
Name Age City
----- --- ---------
Alice 30 New York
Bob 25 Los Angeles
Charlie 35 Chicago
\endverbatim
\section tabulate__customization Customization Options
You can customize various aspects of the table:
\code
Tabulate table(3);
// Set a custom separator bar
table.set_bar(u8"===");
// Add a prefix (useful for indentation)
table.set_prefix(u8"> ");
// Control header and bar visibility
table.show_header(false); // Hide header
table.show_bar(true); // Show separator bar
// Set header
table.set_header({u8"Col1", u8"Col2", u8"Col3"});
// Add data
table.add_row({u8"data1", u8"data2", u8"data3"});
table.print();
\endcode
\section tabulate__unicode_support Unicode Support
The library fully supports Unicode text in tables:
\code
Tabulate table(3);
// Set Unicode header
table.set_header({u8"姓名", u8"年龄", u8"城市"});
// Add Unicode data
table.add_row({u8"张三", u8"30", u8"北京"});
table.add_row({u8"李四", u8"25", u8"上海"});
table.print();
\endcode
\section tabulate__stream_output Stream Output
You can print to any output stream, not just stdout:
\code
#include <fstream>
Tabulate table(2);
table.set_header({u8"Key", u8"Value"});
table.add_row({u8"temp", u8"25C"});
// Print to file
std::ofstream file("table.txt");
table.print(file);
// Or print to stringstream
std::stringstream ss;
table.print(ss);
std::string table_str = ss.str();
\endcode
*/
}

View File

@@ -0,0 +1,121 @@
namespace yycc::carton::termcolor {
/**
\page termcolor Terminal Color Utilities
This namespace provides functions to generate ANSI escape sequence for terminal font color and style.
It also provides functions to add color and style for given string with ANSI Escape Sequence.
Supported color is limited in 16 colors,
because these color is implemented by ASCII Escape Code: https://en.wikipedia.org/wiki/ANSI_escape_code .
So if your terminal do not support this, such as default Windows terminal, or teletypewriter,
you will see some unrecognised characters surrounding with your output.
That's ASCII Escape Code.
This namespace is basically the imitation of the Python package with same name.
\section termcolor__basic_colors Basic Colors
To use basic foreground and background colors:
\code
#include "yycc/carton/termcolor.hpp"
using namespace yycc::carton::termcolor;
// Print red text
std::cout << colored(u8"Error message", Color::Red) << std::endl;
// Print blue text with yellow background
cprint(u8"Info message", Color::Blue, Color::Yellow);
\endcode
\section termcolor__light_colors Light Colors
The namespace provides both standard and light versions of colors:
\code
// Standard green
std::cout << colored(u8"Success", Color::Green) << std::endl;
// Light green (brighter variant)
std::cout << colored(u8"Notice", Color::LightGreen) << std::endl;
\endcode
\section termcolor__text_styles Text Styles
Multiple text styles can be combined using bitwise operations:
\code
// Bold text
std::cout << colored(u8"Important", Color::Red, Color::Default, Attribute::Bold) << std::endl;
// Underlined text
std::cout << colored(u8"Underlined", Color::Blue, Color::Default, Attribute::Underline) << std::endl;
// Combined styles
auto combined_style = yycc::cenum::merge(Attribute::Bold, Attribute::Italic);
cprint(u8"Bold and italic", Color::Magenta, Color::Default, combined_style);
\endcode
\section termcolor__convenience_functions Convenience Functions
Several convenience functions are available for direct printing:
\code
// Print to stdout with color
cprint(u8"Hello World", Color::Green);
// Print to stderr with color and style
ceprint(u8"Warning", Color::Yellow, Color::Default, Attribute::Bold);
// Print with line break
cprintln(u8"Success", Color::LightGreen);
// Print to stderr with line break
ceprintln(u8"Critical Error", Color::LightRed, Color::Default, Attribute::Blink);
\endcode
\section termcolor__color_enums Available Colors
Available foreground and background colors include:
\li Color::Black
\li Color::Red
\li Color::Green
\li Color::Yellow
\li Color::Blue
\li Color::Magenta
\li Color::Cyan
\li Color::White
\li Color::LightBlack
\li Color::LightRed
\li Color::LightGreen
\li Color::LightYellow
\li Color::LightBlue
\li Color::LightMagenta
\li Color::LightCyan
\li Color::LightWhite
\li Color::Default
\section termcolor__style_enums Available Styles
Available text styles include:
\li Attribute::Bold
\li Attribute::Dark
\li Attribute::Italic
\li Attribute::Underline
\li Attribute::Blink
\li Attribute::Reverse
\li Attribute::Concealed
\li Attribute::Default
\section termcolor__old_macros Where is Old Macros
If you have used YYCC 1.x version, you may know that these features are also presented but in a bunch of macros style.
These macros is removed since YYCC 2.0.
Since YYCC 2.0, we suggest you to use these new provided functions instead,
because they are more robust and correspond with our new style of coding by \c std::format and etc.
*/
}

103
doc/src/carton/wcwidth.dox Normal file
View File

@@ -0,0 +1,103 @@
namespace yycc::carton::wcwidth {
/**
\page wcwidth Cross-platform Wcwidth
This namespace provides cross-platform implementations of character width calculation functions,
similar to the Linux-specific \c wcswidth function. It supports Unicode text and ANSI escape sequences,
making it suitable for calculating display widths of text in terminals across different platforms.
\section wcwidth__basic_usage Basic Usage
To calculate the display width of a string in a terminal:
\code
#include <yycc/carton/wcwidth.hpp>
using namespace yycc::carton::wcwidth;
// Calculate width of ASCII text
size_t width1 = wcwidth(U'a'); // Returns 1
auto width2 = wcswidth(u8"Hello"); // Returns 5
// Calculate width of Unicode text
auto width3 = wcswidth(u8"你好世界"); // Returns 8 (Chinese chars typically take 2 spaces each)
auto width4 = wcswidth(u8"ありがとう"); // Returns 10 (Japanese katakana)
\endcode
\section wcwidth__ansi_support ANSI Escape Sequence Support
The library can handle ANSI escape sequences (like color codes) in text
which is not supported by Linux \c wcswidth.
\code
#include "yycc/carton/termcolor.hpp"
using namespace yycc::carton::termcolor;
// Calculate width of colored text
auto colored_text = colored(u8"Hello World", Color::Red);
auto width = wcswidth(colored_text);
// Returns the width of "Hello World" (ignoring the ANSI escape sequences)
\endcode
\section wcwidth__error_handling Error Handling
The functions use \c std::expected for error handling:
\code
#include "yycc/carton/wcwidth.hpp"
// Safe way to handle potential errors
auto result = wcswidth(u8"\033?"); // Invalid ANSI sequence
if (result.has_value()) {
size_t width = result.value();
std::cout << "Width: " << width << std::endl;
} else {
std::cout << "Invalid string" << std::endl;
}
\endcode
\section wcwidth__character_types Supported Character Types
The library provides two main functions:
- wcwidth(): Calculate width of a single character.
- wcswidth(): Calculate width of a string.
\code
// Using wcwidth for single characters
size_t char_width = wcwidth(U'A'); // Returns 1
size_t emoji_width = wcwidth(U'😀'); // Returns width of emoji
// Using wcswidth for strings
auto str_width1 = wcswidth(u8"Hello"); // Returns 5
auto str_width2 = wcswidth(U"Unicode String"); // Returns string length in display width
\endcode
\section wcwidth__platform_differences Platform Considerations
This library addresses platform differences in wide character handling:
\li On Linux, \c whar_t is 4 bytes and can represent any Unicode character
\li On Windows, \c whar_t is 2 bytes and may require surrogate pairs for some characters
So this library uses \c char32_t internally to ensure consistent behavior across platforms,
and expose functions with \c char32_t and \c char8_t string container respectively for user.
\section wcwidth__unicode_support Unicode and East Asian Widths
The library properly handles East Asian character widths:
\code
// Chinese characters typically occupy 2 terminal spaces
auto chinese_width = wcswidth(u8"中文"); // Returns 4 (2 chars × 2 spaces each)
// Japanese characters
auto kana_width = wcswidth(u8"ありがとう"); // Returns 10 (5 kana × 2 spaces each)
// Mixed text
auto mixed_width = wcswidth(u8"Hello 世界"); // Returns 8 (5 ASCII + 1 space + 2 Chinese chars × 2 spaces)
\endcode
*/
}

166
doc/src/encoding/iconv.dox Normal file
View File

@@ -0,0 +1,166 @@
namespace yycc::encoding::iconv {
/**
\page encoding__iconv Iconv-based Codec
\section encoding__iconv__overview Overview
The Iconv-based encoding conversion module provides encoding conversion functionality using the iconv library.
This module is available when you are in POSIX system, or enable iconv support manually when configuring the library.
\section encoding__iconv__classes Available Classes
\subsection encoding__iconv__classes__char Char to/from UTF-8 Conversion
Convert between character encodings and UTF-8:
\code
#include <yycc/encoding/iconv.hpp>
// Example: Creating a converter from Latin-1 to UTF-8
CharToUtf8 converter("ISO-8859-1");
std::string latin1_text = "Café résumé naïve";
auto result = converter.to_utf8(latin1_text);
if (result.has_value()) {
std::u8string utf8_text = result.value();
// Use utf8_text...
} else {
// Handle conversion error
}
\endcode
\code
// Example: Creating a converter from UTF-8 to Latin-1
Utf8ToChar converter("ISO-8859-1");
std::u8string utf8_text = u8"Café résumé naïve";
auto result = converter.to_char(utf8_text);
if (result.has_value()) {
std::string latin1_text = result.value();
// Use latin1_text...
} else {
// Handle conversion error
}
\endcode
\subsection encoding__iconv__classes__wchar WChar to/from UTF-8 Conversion
Convert between wide character and UTF-8:
\code
#include <yycc/encoding/iconv.hpp>
// Example: Converting wide character to UTF-8
WcharToUtf8 converter;
std::wstring wide_text = L"Hello, 世界!";
auto result = converter.to_utf8(wide_text);
if (result.has_value()) {
std::u8string utf8_text = result.value();
// Use utf8_text...
} else {
// Handle conversion error
}
\endcode
\code
// Example: Converting UTF-8 to wide character
Utf8ToWchar converter;
std::u8string utf8_text = u8"Hello, 世界!";
auto result = converter.to_wchar(utf8_text);
if (result.has_value()) {
std::wstring wide_text = result.value();
// Use wide_text...
} else {
// Handle conversion error
}
\endcode
\subsection encoding__iconv__classes__utf16_utf32 UTF-8 to/from UTF-16/UTF-32 Conversion
Convert between UTF encodings:
\code
#include <yycc/encoding/iconv.hpp>
// Example: Converting UTF-8 to UTF-16
Utf8ToUtf16 converter;
std::u8string utf8_text = u8"Hello, 世界!";
auto result = converter.to_utf16(utf8_text);
if (result.has_value()) {
std::u16string utf16_text = result.value();
// Use utf16_text...
} else {
// Handle conversion error
}
\endcode
\code
// Example: Converting UTF-16 to UTF-8
Utf16ToUtf8 converter;
std::u16string utf16_text = u"Hello, 世界!";
auto result = converter.to_utf8(utf16_text);
if (result.has_value()) {
std::u8string utf8_text = result.value();
// Use utf8_text...
} else {
// Handle conversion error
}
\endcode
\code
// Example: Converting UTF-8 to UTF-32
Utf8ToUtf32 converter;
std::u8string utf8_text = u8"Hello, 世界! 🌍";
auto result = converter.to_utf32(utf8_text);
if (result.has_value()) {
std::u32string utf32_text = result.value();
// Use utf32_text...
} else {
// Handle conversion error
}
\endcode
\code
// Example: Converting UTF-32 to UTF-8
Utf32ToUtf8 converter;
std::u32string utf32_text = U"Hello, 世界! 🌍";
auto result = converter.to_utf8(utf32_text);
if (result.has_value()) {
std::u8string utf8_text = result.value();
// Use utf8_text...
} else {
// Handle conversion error
}
\endcode
\section encoding__iconv__error_handling Error Handling
All functions in this module return a result containing either
a ConvError struct represents conversion errors, or the final converted string.
\code
#include <yycc/encoding/iconv.hpp>
CharToUtf8 converter("INVALID_ENCODING");
// Note: Constructor errors might be detected during conversion
std::string text = "Hello";
auto result = converter.to_utf8(text);
if (result.has_value()) {
std::u8string converted = result.value();
// Process successfully converted string
} else {
// Handle conversion failure
std::cout << "Conversion failed\n";
}
\endcode
*/
}

98
doc/src/encoding/stl.dox Normal file
View File

@@ -0,0 +1,98 @@
namespace yycc::encoding::stl {
/**
\page encoding__stl STL-based Codec
\section encoding__stl__overview Overview
The STL-based encoding conversion module provides cross-platform encoding conversion functionality using the standard library's codecvt facets.
This module is designed to handle conversions between UTF-8, UTF-16, and UTF-32 encodings using the standard C++ locale facilities.
\section encoding__stl__attentions Attentions
The underlying implementation of this module is deprecated by C++ STL and may be removed in future versions of C++.
So please use this module carefully or considering use our \ref pycodec module instead.
\section encoding__stl__functions Available Functions
\subsection encoding__stl__functions__utf16 UTF-8 to/from UTF-16 Conversion
Convert between UTF-8 and UTF-16 encodings using standard library facilities:
\code
#include <yycc/encoding/stl.hpp>
// Example: Converting UTF-8 to UTF-16
std::u8string utf8_text = u8"Hello, 世界!";
auto result = to_utf16(utf8_text);
if (result.has_value()) {
std::u16string utf16_text = result.value();
// Use utf16_text...
} else {
// Handle conversion error
}
\endcode
\code
// Example: Converting UTF-16 to UTF-8
std::u16string utf16_text = u"Hello, 世界!";
auto result = to_utf8(utf16_text);
if (result.has_value()) {
std::u8string utf8_text = result.value();
// Use utf8_text...
} else {
// Handle conversion error
}
\endcode
\subsection encoding__stl__functions__utf32 UTF-8 to/from UTF-32 Conversion
Convert between UTF-8 and UTF-32 encodings:
\code
#include <yycc/encoding/stl.hpp>
// Example: Converting UTF-8 to UTF-32
std::u8string utf8_text = u8"Hello, 世界! 🌍";
auto result = to_utf32(utf8_text);
if (result.has_value()) {
std::u32string utf32_text = result.value();
// Use utf32_text...
} else {
// Handle conversion error
}
\endcode
\code
// Example: Converting UTF-32 to UTF-8
std::u32string utf32_text = U"Hello, 世界! 🌍";
auto result = to_utf8(utf32_text);
if (result.has_value()) {
std::u8string utf8_text = result.value();
// Use utf8_text...
} else {
// Handle conversion error
}
\endcode
\section encoding__stl__error_handling Error Handling
All functions in this module return a result containing either
a ConvError struct represents conversion errors, or the final converted string.
\code
#include <yycc/encoding/stl.hpp>
std::u8string invalid_utf8 = "\xFF\xFE"; // Invalid UTF-8 sequence
auto result = to_utf16(invalid_utf8);
if (result.has_value()) {
std::u16string converted = result.value();
// Process successfully converted string
} else {
// Handle conversion failure
std::cout << "Conversion failed\n";
}
\endcode
*/
}

View File

@@ -0,0 +1,191 @@
namespace yycc::encoding::windows {
/**
\page encoding__windows Win32-based Codec
\section encoding__windows__overview Overview
The Windows-specific encoding conversion module provides encoding conversion functionality
using Windows API functions such as `WideCharToMultiByte` and `MultiByteToWideChar`.
This module is available only on Windows platforms and offers efficient conversion
between various character encodings including wide character, multi-byte, and UTF-8.
\section encoding__windows__functions Available Functions
\subsection encoding__windows__functions__wchar Wide Character to/from Multi-byte Conversion
Convert between wide character strings and multi-byte strings using Windows code pages:
\code
#include <yycc/encoding/windows.hpp>
// Example: Converting wide character string to multi-byte with specific code page
std::wstring wide_text = L"Hello, 世界!";
auto result = to_char(wide_text, CP_UTF8); // Using UTF-8 code page
if (result.has_value()) {
std::string multi_byte_text = result.value();
// Use multi_byte_text...
} else {
// Handle conversion error
}
\endcode
\code
// Example: Converting multi-byte string to wide character with specific code page
std::string multi_byte_text = "Hello, 世界!";
auto result = to_wchar(multi_byte_text, CP_UTF8);
if (result.has_value()) {
std::wstring wide_text = result.value();
// Use wide_text...
} else {
// Handle conversion error
}
\endcode
\subsection encoding__windows__functions__mbcs Multi-byte to/from Multi-byte Conversion
Convert between different multi-byte encodings by using wide character as an intermediate:
\code
#include <yycc/encoding/windows.hpp>
// Example: Converting between two different code pages
std::string source_text = "Hello, world!";
auto result = to_char(source_text, CP_ACP, CP_UTF8); // ANSI to UTF-8
if (result.has_value()) {
std::string utf8_text = result.value();
// Use converted UTF-8 text...
} else {
// Handle conversion error
}
\endcode
\subsection encoding__windows__functions__utf8 UTF-8 Specific Conversions
Specialized functions for UTF-8 conversion without requiring explicit code page specification:
\code
#include <yycc/encoding/windows.hpp>
// Example: Converting wide character to UTF-8
std::wstring wide_text = L"Hello, 世界!";
auto result = to_utf8(wide_text);
if (result.has_value()) {
std::u8string utf8_text = result.value();
// Use utf8_text...
} else {
// Handle conversion error
}
\endcode
\code
// Example: Converting UTF-8 to wide character
std::u8string utf8_text = u8"Hello, 世界!";
auto result = to_wchar(utf8_text);
if (result.has_value()) {
std::wstring wide_text = result.value();
// Use wide_text...
} else {
// Handle conversion error
}
\endcode
\code
// Example: Converting multi-byte to UTF-8
std::string multi_byte_text = "Hello, world!";
auto result = to_utf8(multi_byte_text, CP_ACP);
if (result.has_value()) {
std::u8string utf8_text = result.value();
// Use utf8_text...
} else {
// Handle conversion error
}
\endcode
\code
// Example: Converting UTF-8 to multi-byte
std::u8string utf8_text = u8"Hello, world!";
auto result = to_char(utf8_text, CP_ACP);
if (result.has_value()) {
std::string multi_byte_text = result.value();
// Use multi_byte_text...
} else {
// Handle conversion error
}
\endcode
\subsection encoding__windows__functions__utf16_utf32 UTF-8 to/from UTF-16/UTF-32 Conversion
Available on Windows with Microsoft STL for conversion between UTF encodings:
\code
#include <yycc/encoding/windows.hpp>
// Example: Converting UTF-8 to UTF-16
std::u8string utf8_text = u8"Hello, 世界!";
auto result = to_utf16(utf8_text);
if (result.has_value()) {
std::u16string utf16_text = result.value();
// Use utf16_text...
} else {
// Handle conversion error
}
\endcode
\code
// Example: Converting UTF-16 to UTF-8
std::u16string utf16_text = u"Hello, 世界!";
auto result = to_utf8(utf16_text);
if (result.has_value()) {
std::u8string utf8_text = result.value();
// Use utf8_text...
} else {
// Handle conversion error
}
\endcode
\code
// Example: Converting UTF-8 to UTF-32
std::u8string utf8_text = u8"Hello, 世界! 🌍";
auto result = to_utf32(utf8_text);
if (result.has_value()) {
std::u32string utf32_text = result.value();
// Use utf32_text...
} else {
// Handle conversion error
}
\endcode
\code
// Example: Converting UTF-32 to UTF-8
std::u32string utf32_text = U"Hello, 世界! 🌍";
auto result = to_utf8(utf32_text);
if (result.has_value()) {
std::u8string utf8_text = result.value();
// Use utf8_text...
} else {
// Handle conversion error
}
\endcode
\section encoding__windows__error_handling Error Handling
All functions in this module return a result containing either
a ConvError struct represents conversion errors, or the final converted string.
\code
#include <yycc/encoding/windows.hpp>
std::wstring invalid_text = /* some problematic string */;
auto result = to_char(invalid_text, CP_UTF8);
if (result.has_value()) {
std::string converted = result.value();
// Process successfully converted string
} else {
// Handle conversion failure
std::cout << "Conversion failed\n";
}
\endcode
*/
}

85
doc/src/env.dox Normal file
View File

@@ -0,0 +1,85 @@
namespace yycc::env {
/**
\page env Environment Operations
This namespace provide various environment operations inspired by Rust standard library.
\section env__var Environment Variable Operations
These functions allow manipulation of environment variables with proper error handling using \c std::expected.
\li get_var(): Get the value of a given environment variable name
\li set_var(): Set the value of a given environment variable name
\li del_var(): Delete environment variable with given name
\li get_vars(): Get all environment variables as a list of name-value pairs
There is an example usage of these functions below:
\code
#include <yycc/env.hpp>
auto result = yycc::env::get_var(u8"PATH");
if (result.has_value()) {
auto value = result.value();
// Use the value...
} else {
// Handle the error
}
\endcode
\section env__path Path Operations
These functions provide access to various important paths in the system environment.
\li current_dir(): Returns the current working directory
\li current_exe(): Returns the path of the current running executable
\li home_dir(): Returns the path of the current user's home directory if known
\li temp_dir(): Returns the path of a temporary directory
There is an example usage of these functions below:
\code
#include <yycc/env.hpp>
#include <yycc/patch/stream.hpp>
#include <iostream>
using namespace yycc::patch::stream;
auto cwd_result = yycc::env::current_dir();
if (cwd_result.has_value()) {
std::cout << "Current directory: " << cwd_result.value() << std::endl;
}
auto exe_result = yycc::env::current_exe();
if (exe_result.has_value()) {
std::cout << "Executable path: " << exe_result.value() << std::endl;
}
\endcode
\section env__arg Command Line Argument Operations
These functions provide access to command-line arguments passed to the program.
\li get_args(): Returns the arguments that this program was started with
There is an example usage of these functions below:
\code
#include <yycc/env.hpp>
#include <yycc/patch/stream.hpp>
#include <iostream>
using namespace yycc::patch::stream;
auto args_result = yycc::env::get_args();
if (args_result.has_value()) {
auto args = args_result.value();
for (auto& arg : args) {
std::cout << "Arg: " << arg << std::endl;
}
}
\endcode
*/
}

View File

@@ -43,28 +43,51 @@
\li \subpage num__parser
\li \subpage num__op
\li \subpage num__safe_cast
\li \subpage num__safe_op
\li \subpage patch
<!--
\li \subpage encoding_helper
\li \subpage env
\li \subpage console_helper
\li \subpage rust
-->
<B>Text Encoding</B>
<!--
\li \subpage constraints
\li \subpage encoding__stl
\li \subpage config_manager
\li \subpage encoding__windows
\li \subpage arg_parser
-->
\li \subpage encoding__iconv
\li \subpage pycodec
</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
@@ -73,11 +96,9 @@
\li \subpage windows__dialog
<!--
\li \subpage win_fct_helper
\li \subpage windows__winfct
\li \subpage exception_helper
-->
\li \subpage windows__console
</TD>
</TR>

30
doc/src/num/op.dox Normal file
View File

@@ -0,0 +1,30 @@
namespace yycc::num::op {
/**
\page num__op Numeric Operations
Namespace yycc::num::op provides functions for robust numeric operations inspired by Rust's approach to primitive type operations.
Currently, this namespace only supports unsigned integer ceiling division, though more operations may be added in the future based on demand.
\section num__op__div_ceil Ceiling Division
The \c div_ceil function performs division between two unsigned integers and rounds up the result.
It uses a safe algorithm that avoids potential overflow issues that could occur with the traditional formula <TT>(lhs + rhs - 1) / rhs</TT>.
The function computes: <TT>(lhs % rhs == 0) ? (lhs / rhs) : (lhs / rhs) + 1u</TT>
The function prevents division by zero by checking the divisor before performing the operation and throwing a std::logic_error if the divisor is zero.
Here are some examples showing how to use this function:
\code
#include <yycc/num/op.hpp>
// Ceiling division examples
uint32_t result1 = op::div_ceil(uint32_t(10), uint32_t(3)); // Results in 4
uint32_t result2 = op::div_ceil(uint32_t(9), uint32_t(3)); // Results in 3
uint32_t result3 = op::div_ceil(uint32_t(1), uint32_t(10)); // Results in 1
\endcode
*/
}

63
doc/src/num/safe_cast.dox Normal file
View File

@@ -0,0 +1,63 @@
namespace yycc::num::safe_cast {
/**
\page num__safe_cast Numeric Safe Casting
Namespace yycc::num::safe_cast provides functions which safely cast numeric value from one type to another.
\section num__safe_cast__overview Overview
When writing C++ code, casting between types with different ranges is very important
but greatly easy to make mistakes which finally cause fatal errors.
Inspired by Rust's approach to type conversion,
this namespace provides safe casting functions that handle potential overflow and underflow issues.
\section num__safe_cast__functions Functions
The namespace provides two main functions:
\li \c to() - Direct conversion for cases where the destination type can definitely hold the source value
which means definitely safe conversions (widening conversions).
\li \c try_to() - Attempt conversion and return a Result type that includes error information if the conversion fails
which means potentially risky conversions (narrowing conversions).
The \c try_to function returns a \c std::expected.
If the conversion succeeds, the result contains the converted value.
If it fails, it contains a error info.
\section num__safe_cast__examples Examples
Here are some examples showing how to use the safe casting functions:
\code
#include <yycc/num/safe_cast.hpp>
// Safe conversion using 'to' function
uint32_t val1 = safe_cast::to<uint32_t>(static_cast<int16_t>(123));
// Potentially risky conversion using 'try_to' function
auto result = safe_cast::try_to<int16_t>(static_cast<int32_t>(12345));
if (result.has_value()) {
auto converted = result.value();
// Use converted value
} else {
// Handle error
}
\endcode
\section num__safe_cast__notes Notes
The safety of conversions is determined at compile time using the \c CAN_SAFE_TO meta-programming concept.
However, for variable-length data types (like \c size_t ), the safety determination may vary across platforms,
which could affect code portability. For this reason, it would be better to use \c try_to for these types
for better robust application on different platforms.
\section num__safe_cast__limitations Limitations
This namespace supports safe casting between integral types only.
Currently unsupported conversions include:
\li Floating-point to floating-point conversions
\li Floating-point to integer conversions
*/
}

86
doc/src/num/safe_op.dox Normal file
View File

@@ -0,0 +1,86 @@
namespace yycc::num::safe_op {
/**
\page num__safe_op Numeric Safe Arithmetic Operations
Namespace yycc::num::safe_op provides Rust-like safe arithmetic operations
for handling overflow, underflow, and other undefined behaviors in C++.
\section num__safe_op__overview Overview
Inspired by Rust's rich set of arithmetic operators,
this namespace provides safe arithmetic operations that handle potential overflow, underflow, and other undefined behaviors that commonly occur in C++.
It offers multiple strategies for handling arithmetic operations including wrapping, checked, overflowing, saturating, and strict operations.
\section num__safe_op__operation_types Operation Types
The namespace provides several families of arithmetic operations:
\li \c wrapping_* operations: Perform arithmetic with wrapping on overflow/underflow (similar to unsigned integer behavior)
\li \c checked_* operations: Return std::optional containing the result, or std::nullopt if overflow/underflow occurs
\li \c overflowing_* operations: Return a pair with the result and a boolean indicating whether overflow occurred
\li \c saturating_* operations: Clamp the result to the min/max value when overflow/underflow occurs
\li \c strict_* operations: Throw exceptions when overflow/underflow occurs
\li \c ordinary operations (add, sub, mul, div): Alias to wrapping operations for safe default behavior
\section num__safe_op__arithmetic_functions Arithmetic Functions
For each operation type, the namespace provides functions for the four basic arithmetic operations:
\li \c _add : Addition
\li \c _sub : Subtraction
\li \c _mul : Multiplication
\li \c _div : Division
For example, for wrapping operations: \c wrapping_add, \c wrapping_sub, \c wrapping_mul, \c wrapping_div.
\section num__safe_op__examples Examples
Here are some examples showing how to use the safe arithmetic functions:
\code
#include <yycc/num/safe_op.hpp>
#include <iostream>
// Wrapping addition - wraps around on overflow
uint8_t result1 = safe_op::wrapping_add(uint8_t(200), uint8_t(100)); // Results in 44
// Checked multiplication - returns std::optional
auto result2 = safe_op::checked_mul(int32_t(1000000), int32_t(1000000));
if (!result2.has_value()) {
std::cout << "Multiplication overflowed!" << std::endl;
} else {
std::cout << "Result: " << result2.value() << std::endl;
}
// Overflowing subtraction - returns pair of result and overflow flag
auto [result3, overflowed] = safe_op::overflowing_sub(int32_t(-10), int32_t(INT32_MIN));
if (overflowed) {
std::cout << "Subtraction overflowed!" << std::endl;
}
// Saturating multiplication - clamps to min/max on overflow
int32_t result4 = safe_op::saturating_mul(int32_t(1000000), int32_t(1000000)); // Clamps to INT32_MAX
// Ordinary operations - safe defaults without undefined behavior
int32_t result5 = safe_op::add(int32_t(10), int32_t(20)); // 30
\endcode
\section num__safe_op__undefined_behaviors Handling of Undefined Behaviors
This namespace handles several undefined behaviors in C++ arithmetic:
\li Signed integer overflow and underflow (e.g. INT_MAX + 1)
\li Division by zero
\li Performing INT_MIN / -1 division (which would result in a value that doesn't fit in the type)
For division operations, special care is taken to handle these undefined behaviors appropriately depending on the operation type.
\section num__safe_op__platform_support Platform Support
The implementation uses hardware-specific overflow detection functions:
\li GCC/Clang: Uses built-in functions like __builtin_add_overflow
\li Windows: Uses Windows API functions from \c intsafe.h
This ensures optimal performance across different platforms.
*/
}

133
doc/src/rust.dox Normal file
View File

@@ -0,0 +1,133 @@
namespace yycc {
/**
\page rust Rust Facilities in C++
This collection of following headers brings Rust-style programming facilities to C++.
\section rust__primitive Primitive Types
The yycc::primitive namespace provides primitive types similar to Rust's approach.
Especially resolve the problem that the names of C++ primitive types are so long.
There is an example of using primitive types:
\code
#include <yycc/primitive.hpp>
using namespace yycc::primitive;
i32 value = 42;
u64 big_number = 1000000ULL;
f64 precision = 3.14159265359;
\endcode
\section rust__option Option Type
The yycc::option namespace reproduces Rust's Option type and its members Some and None in C++.
Considering C++ has provide \c std::optional, this namespace provided contents are just an alias to it.
\li yycc::option::Option - Template alias for std::optional
\li yycc::option::Some - Function to create an Option with a value
\li yycc::option::None - Function to create an empty Option
There is an example of using \c Option type:
\code
#include <yycc/option.hpp>
#include <iostream>
using namespace yycc::option;
Option<int> maybe_value = Some<Option<int>>(42);
if (maybe_value.has_value()) {
std::cout << "Value: " << maybe_value.value() << std::endl;
}
auto empty_value = None<Option<int>>();
if (!empty_value.has_value()) {
std::cout << "No value present" << std::endl;
}
\endcode
\section rust__result Result Type
The yycc::result namespace reproduces Rust's Result type and its members Ok and Err in C++.
Considering C++ has provide \c std::expected, this namespace provided contents are just an alias to it.
\li yycc::result::Result - Template alias for std::expected
\li yycc::result::Ok - Function to create a Result with a success value
\li yycc::result::Err - Function to create a Result with an error value
There is an example of using \c Result type:
\code
#include <yycc/result.hpp>
#include <iostream>
using namespace yycc::result;
Result<int, int> divide(int a, int b) {
if (b == 0) {
return Err<Result<int, int>>(-1); // Error code
}
return Ok<Result<int, int>>(a / b);
}
auto result = divide(10, 2);
if (result.has_value()) {
std::cout << "Result: " << result.value() << std::endl;
} else {
std::cout << "Error occurred: " << result.error() << std::endl;
}
\endcode
\section rust__panic Panic Mechanism
The yycc::panic namespace provides Rust-style panic functionality for immediate program termination on unrecoverable errors.
This imitates Rust's panic! macro behavior, allowing the program to immediately exit with error information and stack traces.
\li RS_PANIC: Macro equivalent to Rust's panic! macro.
This macro will help you append all filename, line and function info to the real panic trigger function.
\li yycc::panic::panic: The actual function called by the macro.
User usually does not need to call this function directly.
There is an example of using this panic mechanism:
\code
#include <yycc/panic.hpp>
void critical_function(int err_code) {
// Some condition that indicates an unrecoverable error
if (err_code != 100) {
RS_PANIC("Unrecoverable error in critical function with code {}", err_code);
}
}
\endcode
\section rust__prelude Prelude
The yycc::prelude namespace provides a Rust-like prelude for C++. In Rust, types are automatically imported into all files by default.
This default-imported set of types is called the "prelude".
This namespace provides a similar concept for C++.
This namespace will extract following content into \b global scope:
\li All primitive types defined in yycc::primitive.
\li Vec: \c std::vector template alias.
\li All functionality from yycc::option and yycc::result namespaces.
\li Panic mechanism from yycc::panic.
There is an example of using this header:
\code
#include <yycc/prelude.hpp>
// Now all Rust-style facilities are available without prefixes:
i32 x = 42; // From primitive
Vec<i32> numbers = {1, 2, 3}; // Vector of primitive types
auto result = Ok<Result<i32, i32>>(x); // Result type
RS_PANIC("Something went wrong"); // Panic macro
\endcode
*/
}

View File

@@ -37,7 +37,7 @@ 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 replace(const std::u8string_view& strl, const std::u8string_view& from_strl, const std::u8string_view& to_strl);
std::u8string to_replace(const std::u8string_view& strl, const std::u8string_view& from_strl, const std::u8string_view& to_strl);
\endcode
The first overload will do replacement in given string container directly.
@@ -162,7 +162,7 @@ std::vector<std::u8string> split_owned(const std::u8string_view& strl, const std
All these overloads take a string view as the first argument representing the string need to be split.
The second argument is a string view representing the delimiter for splitting.
The first function #lazy_split returns a #LazySplit object that can be used in range-based for loops.
The first function #lazy_split returns a LazySplit object that can be used in range-based for loops.
This is lazy-computed and memory-efficient for large datasets.
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.

View File

@@ -0,0 +1,23 @@
namespace yycc::windows::console {
/**
\page windows__console Windows Console Helper
Namespace yycc::windows::console is designed to resolve some issue of Windows console
which is not corresponding to POSIX system console.
This namespace also is only available on Windows platform.
Currently this namespace only has one function: colorful_console(),
which enable colorful console output support for \c stdout and \c stderr in Windows.
As we introduced, you may know Windows console does not support ASCII Escape Code color in default.
This function can fix this issue.
This function will forcely enable ASCII Escape Code support in Windows console if possible.
Thus you can write colorful text in Windows console freely.
We suggest you to call this function at the beginning of program.
Considering most Linux console supports ASCII Escape Code very well,
this function isn't presented in non-Windows platform.
So it is essential that brack this function calling with Windows-only \c \#if.
*/
}

View File

@@ -133,10 +133,10 @@ auto result4 = open_folder(params);
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 open_file(): Open single file
\li open_files(): Open multiple files
\li save_file(): Save single file
\li open_folder(): Open single directory
\subsection windows__dialog__result__arguments Arguments

View File

@@ -1,23 +0,0 @@
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.
*/
}

View File

@@ -0,0 +1,15 @@
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.
*/
}

View File

@@ -69,6 +69,7 @@ FILES
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
@@ -166,6 +167,15 @@ 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

View File

@@ -5,6 +5,7 @@
#include "../../patch/libcxx/enumerate.hpp"
#include "../../string/op.hpp"
#include "../../env.hpp"
#include <filesystem>
#include <ranges>
#define CLAP ::yycc::carton::clap
@@ -112,8 +113,9 @@ namespace yycc::carton::clap::manual {
// only print usage if we can fetch the name of executable
auto executable = ENV::current_exe();
if (executable.has_value()) {
// Get the filename part and print
TERMCOLOR::cprintln(trctx.usage_title, TERMCOLOR::Color::Yellow, TERMCOLOR::Color::Default, TERMCOLOR::Attribute::Default, dst);
dst << INDENT << FORMAT::format(trctx.usage_body, executable.value().u8string()) << std::endl;
dst << INDENT << FORMAT::format(trctx.usage_body, executable.value().filename().u8string()) << std::endl;
}
const auto &variables = app.get_variables();

View File

@@ -12,7 +12,7 @@ namespace yycc::carton::clap::option {
Option::Option(std::optional<std::u8string_view> short_name,
std::optional<std::u8string_view> long_name,
std::optional<std::u8string_view> value_hint,
const std::u8string& description) :
const std::u8string_view& description) :
short_name(short_name), long_name(long_name), value_hint(value_hint), description(description) {
if (!short_name.has_value() && !long_name.has_value()) {
throw std::logic_error("must have at least one name, short or long name");

View File

@@ -23,7 +23,7 @@ namespace yycc::carton::clap::option {
Option(std::optional<std::u8string_view> short_name,
std::optional<std::u8string_view> long_name,
std::optional<std::u8string_view> value_hint,
const std::u8string& description);
const std::u8string_view& description);
~Option();
YYCC_DEFAULT_COPY_MOVE(Option)

View File

@@ -25,7 +25,7 @@
* \li https://stackoverflow.com/questions/45575863/how-to-print-utf-8-strings-to-stdcout-on-windows
* \li https://stackoverflow.com/questions/69830460/reading-utf-8-input
*
* For how to utilize this functions provided by this namespace, please view \ref console_helper.
* For how to utilize this functions provided by this namespace, please view \ref csconsole.
*
* @warning
* All functions provided by this namespace are too aggressive.

View File

@@ -248,7 +248,7 @@ namespace yycc::carton::fft {
* @param[out] freq_scope The length of this data must be N / 2.
* The first data is 0Hz and the frequency of last data is decided by sample rate which can be computed by get_max_freq() function in this class.
* @param[in] window The window instance applied to data.
* @warnings
* @warning
* This function is \b NOT thread-safe.
* Please do NOT call this function in different thread for one instance.
*/

View File

@@ -8,7 +8,7 @@
* @details
* This namespace is currently works on Windows.
* On other platforms, this namespace provided functions do nothing.
* For how to utilize this namespace, please see \ref exception_helper.
* For how to utilize this namespace, please see \ref ironpad.
*
* This feature is originate from my created Virtools plugin.
* Because its user frequently trigger some weird behaviors but I have no idea about the detail of them.

View File

@@ -3,6 +3,7 @@
#if defined(YYCC_FEAT_ICONV)
#include "../macro/endian_detector.hpp"
#include "../macro/os_detector.hpp"
#include <cerrno>
#include <stdexcept>
#include <cstdint>
@@ -202,11 +203,14 @@ namespace yycc::encoding::iconv {
// If we use UTF16 or UTF32 code name directly, it will produce a BOM at data head.
// That's not what we expected.
// So we need manually check runtime endian and explicitly specify endian in code name.
//
// Also, at the same time, iconv on macOS do not support "WCHAR_T" encoding,
// so we need manually set it as UTF32 encoding.
// See https://developer.apple.com/forums/thread/811508 for more infos.
using namespace std::literals::string_view_literals;
constexpr auto UTF8_CODENAME_LITERAL = "UTF-8"sv;
constexpr auto WCHAR_CODENAME_LITERAL = "WCHAR_T"sv;
constexpr auto UTF16_CODENAME_LITERAL =
#if defined(YYCC_ENDIAN_LITTLE)
"UTF-16LE"sv;
@@ -219,6 +223,13 @@ namespace yycc::encoding::iconv {
#else
"UTF-32BE"sv;
#endif
constexpr auto WCHAR_CODENAME_LITERAL =
#if defined(YYCC_OS_MACOS)
UTF32_CODENAME_LITERAL;
static_assert(sizeof(wchar_t) == sizeof(char32_t), "unexpected wchar_t size");
#else
"WCHAR_T"sv;
#endif
// TODO:
// There is a memory copy in this function. Consider optimizing it in future.

View File

@@ -1,39 +1,42 @@
#include "env.hpp"
#include "macro/os_detector.hpp"
#include "string/reinterpret.hpp"
#include "num/safe_op.hpp"
#include "num/safe_cast.hpp"
#include <string>
#include <string_view>
#include <memory>
#include <type_traits>
#include <stdexcept>
#if defined(YYCC_OS_WINDOWS)
// Windows headers
#include "encoding/windows.hpp"
#include "windows/winfct.hpp"
#include <memory> // For safely free gotten environment variable and commandline argument.
#include <type_traits> // Used by std::unique_ptr to get underlying pointer type.
#include "windows/import_guard_head.hpp"
#include <Windows.h>
#include <winbase.h>
#include <processenv.h> // For getting environment variables and commandline argument.
#include <shellapi.h> // For getting commandline argument.
#include <processenv.h> // For getting environment variables and commandline argument.
#include <shellapi.h> // For getting commandline argument.
#include "windows/import_guard_tail.hpp"
#elif defined(YYCC_OS_LINUX)
#include <cstdlib>
#include <cerrno>
#include <fstream> // For reading commandline argument.
#include <unistd.h>
#include <sys/stat.h> // For reading symlink target.
#else
// POSIX headers
#include "string/reinterpret.hpp"
#include <cstdlib> // For POSIX environment variable operations.
#include <cerrno> // For POSIX errno.
#include <stdexcept> // For re-throw unexpected POSIX errno as STL exception.
#if defined(YYCC_OS_LINUX)
// Linux-only headers
#include <fstream> // For reading commandline argument.
#elif defined(YYCC_OS_MACOS)
#include <cstdlib>
#include <cerrno>
#include <unistd.h>
#include <cstring>
#include <mach-o/dyld.h> // For getting current exe path.
#include <crt_externs.h> // For getting commandline argument.
// macOS-only headers
#include <mach-o/dyld.h> // For getting current exe path.
#include <crt_externs.h> // For getting commandline argument.
#else
#error "Not supported OS"
#endif
#endif
// Extern POSIX environment variables list.
#if !defined(YYCC_OS_WINDOWS)
extern char** environ;
#endif
#define SAFECAST ::yycc::num::safe_cast
#define SAFEOP ::yycc::num::safe_op
@@ -159,6 +162,7 @@ namespace yycc::env {
}
#if defined(YYCC_OS_WINDOWS)
class EnvironmentStringsDeleter {
public:
EnvironmentStringsDeleter() {}
@@ -169,6 +173,7 @@ namespace yycc::env {
}
};
using SmartEnvironmentStrings = std::unique_ptr<std::remove_pointer_t<LPWCH>, EnvironmentStringsDeleter>;
#endif
VarResult<std::vector<VarPair>> get_vars() {
@@ -241,84 +246,6 @@ namespace yycc::env {
return std::filesystem::current_path();
}
#if defined(YYCC_OS_LINUX)
/// @brief All possible error occurs when reading Linux symlink target.
enum class ReadSymlinkTargetError {
SysCall, ///< Fail to call system call.
Truncated, ///< Expected path to target was truncated.
Others, ///< Any other errors
};
/**
* @brief An utility function for convenient reading symlink target on Linux.
* @param[in] link The path to symlink where the target is read.
* @return The read path to to target, or error occurs.
*/
static std::expected<std::u8string, ReadSymlinkTargetError> read_symlink_target(const std::string_view &link) {
// Reference: https://www.man7.org/linux/man-pages/man2/readlink.2.html
// String view is not NUL terminated.
// Create an string container for it.
std::string path(link);
// Get the expected size.
// Query this symlink info first.
struct stat sb;
if (lstat(path.c_str(), &sb) != 0) {
return std::unexpected(ReadSymlinkTargetError::SysCall);
}
// Fetch the size of target path in gotten struct.
// And cast it into expected type.
auto expected_size = SAFECAST::try_to<size_t>(sb.st_size);
if (!expected_size.has_value()) {
return std::unexpected(ReadSymlinkTargetError::Others);
}
auto buf_size = expected_size.value();
// Some magic symlinks under (for example) /proc and /sys report 'st_size' as zero.
// In that case, take PATH_MAX as a "good enough" estimate.
if (buf_size == 0) {
buf_size = PATH_MAX;
}
// Prepare return value and allocate it with previous gotten size.
std::u8string rv(u8'\0', buf_size);
// Copy data into result value.
// Add one to the link size, so that we can determine whether
// the buffer returned by readlink() was truncated.
// Also, due to the add operation, we need do overflow checks.
auto passed_size = SAFEOP::checked_add<size_t>(buf_size, 1);
if (!passed_size.has_value()) {
return std::unexpected(ReadSymlinkTargetError::Others);
}
// Read data into result value.
ssize_t nbytes = readlink(path.c_str(), REINTERPRET::as_ordinary(rv.data()), passed_size.value());
if (nbytes < 0) {
return std::unexpected(ReadSymlinkTargetError::SysCall);
}
// Check written size
// Cast it type into expected type.
auto written_size = SAFECAST::try_to<size_t>(nbytes);
if (!written_size.has_value()) {
return std::unexpected(ReadSymlinkTargetError::Others);
}
// If the return value was equal to the buffer size, then
// the link target was larger than expected (perhaps because the
// target was changed between the call to lstat() and the call to
// readlink()). Return error instead.
if (written_size.value() != buf_size) {
// TODO: There must be a better solution to this truncated issue than simply return error.
return std::unexpected(ReadSymlinkTargetError::Truncated);
}
// Everything is okey
return rv;
}
#endif
PathResult<std::filesystem::path> current_exe() {
std::u8string rv;
@@ -328,9 +255,10 @@ namespace yycc::env {
else return std::unexpected(PathError::SysCall);
#elif defined(YYCC_OS_LINUX)
// Reference: https://www.man7.org/linux/man-pages/man5/proc_pid_exe.5.html
auto target = read_symlink_target("/proc/self/exe");
if (target.has_value()) return rv = std::move(target.value());
else return std::unexpected(PathError::SysCall);
std::error_code ec;
auto target = std::filesystem::read_symlink(std::filesystem::path("/proc/self/exe"), ec);
if (ec) return std::unexpected(PathError::SysCall);
else rv = REINTERPRET::as_utf8(target.string());
#elif defined(YYCC_OS_MACOS)
// TODO: This is AI generated and don't have test and reference.
std::string buffer(PATH_MAX, '\0');
@@ -437,10 +365,10 @@ namespace yycc::env {
while (true) {
// We use NUL as delimiter
std::getline(cmdline, arg, '\0');
// Check whether we reach EOF
if (cmdline.eof()) break;
// Check whether reading is okey.
if (!cmdline.good()) return std::unexpected(ArgError::Others);
// If return string is empty, it means that we reach the tail.
if (arg.empty()) break;
if (cmdline.fail()) return std::unexpected(ArgError::Others);
// Push this argument into result.
rv.emplace_back(REINTERPRET::as_utf8(arg));

View File

@@ -3,9 +3,13 @@
#include "../string/reinterpret.hpp"
#include <string_view>
#include <type_traits>
#include <charconv>
#include <stdexcept>
#include <expected>
#include <charconv>
#if defined(YYCC_STL_CLANGSTL)
#include "../patch/libcxx/charconv.hpp"
#endif
#define NS_YYCC_STRING_REINTERPRET ::yycc::string::reinterpret
#define NS_YYCC_STRING_OP ::yycc::string::op

View File

@@ -3,8 +3,12 @@
#include <string>
#include <array>
#include <type_traits>
#include <charconv>
#include <stdexcept>
#include <charconv>
#if defined(YYCC_STL_CLANGSTL)
#include "../patch/libcxx/charconv.hpp"
#endif
#define NS_YYCC_STRING_REINTERPRET ::yycc::string::reinterpret

View File

@@ -0,0 +1,446 @@
#pragma once
#include "../../macro/stl_detector.hpp"
//#if defined(YYCC_STL_CLANGSTL)
/**
* @private
* @file This is the polyfill for LLVM libcxx charconv header
* which including \c std::from_chars and \c std::to_chars utilities.
* These 2 functions are only \b fully implemented in the latest Clang (20+)
* and partially implemented in the latest Apple Clang (Xcode 16).
* This should be removed once both Clang official and Apple Clang libcxx \b fully provide them.
* This polyfill is generated by AI.
*/
#include <charconv>
#include <system_error>
#include <cctype>
#include <climits>
#include <cstdio>
#include <cerrno>
#include <cinttypes>
#include <type_traits>
#include <concepts>
#include <stdexcept>
#include <optional>
namespace std {
#if !defined(YYCC_CHARCONV_HAS_CHARS_FORMAT)
enum class chars_format : unsigned int {
scientific = 1,
fixed = 2,
hex = 4,
general = fixed | scientific // This should be 6 (fixed|scientific)
};
#endif
#if !defined(YYCC_CHARCONV_HAS_FROM_CHARS_RESULT)
struct from_chars_result {
const char* ptr;
std::errc ec;
friend bool operator==(const from_chars_result&, const from_chars_result&) = default;
constexpr explicit operator bool() const noexcept { return ec == std::errc{}; }
};
#endif
#if !defined(YYCC_CHARCONV_HAS_TO_CHARS_RESULT)
struct to_chars_result {
char* ptr;
std::errc ec;
friend bool operator==(const to_chars_result&, const to_chars_result&) = default;
constexpr explicit operator bool() const noexcept { return ec == std::errc{}; }
};
#endif
#if !defined(YYCC_CHARCONV_HAS_TO_CHARS_INT)
/// @private
enum class __integral_type {
u8,
u16,
u32,
u64,
i8,
i16,
i32,
i64,
};
/// @private
enum class __integral_base_type {
base8,
base10,
base16,
};
/// @private
template<typename T>
constexpr __integral_type __classify_int_type() {
if constexpr (std::is_same_v<T, std::uint8_t>) {
return __integral_type::u8;
} else if constexpr (std::is_same_v<T, std::uint16_t>) {
return __integral_type::u16;
} else if constexpr (std::is_same_v<T, std::uint32_t>) {
return __integral_type::u32;
} else if constexpr (std::is_same_v<T, std::uint64_t>) {
return __integral_type::u64;
} else if constexpr (std::is_same_v<T, std::int8_t>) {
return __integral_type::i8;
} else if constexpr (std::is_same_v<T, std::int16_t>) {
return __integral_type::i16;
} else if constexpr (std::is_same_v<T, std::int32_t>) {
return __integral_type::i32;
} else if constexpr (std::is_same_v<T, std::int64_t>) {
return __integral_type::i64;
} else {
static_cast(false, "Unsupported integral type");
}
}
/// @private
std::optional<__integral_base_type> __classify_int_base(int base) {
if (base == 8) {
return __integral_base_type::base8;
} else if (base == 10) {
return __integral_base_type::base10;
} else if (base == 16) {
return __integral_base_type::base16;
} else {
return std::nullopt;
}
}
/// @private
/// @brief Helper to get printf format specifier based on type and base
template<__integral_type EIntTy>
const char* __get_int_format(__integral_base_type base) {
if (base == __integral_base_type::base8) {
if constexpr (EIntTy == __integral_type::u8) {
return "%" PRIo8;
} else if constexpr (EIntTy == __integral_type::u16) {
return "%" PRIo16;
} else if constexpr (EIntTy == __integral_type::u32) {
return "%" PRIo32;
} else if constexpr (EIntTy == __integral_type::u64) {
return "%" PRIo64;
} else {
static_assert(false, "Unsupported integral type and base");
}
} else if (base == __integral_base_type::base10) {
if constexpr (EIntTy == __integral_type::u8) {
return "%" PRIu8;
} else if constexpr (EIntTy == __integral_type::u16) {
return "%" PRIu16;
} else if constexpr (EIntTy == __integral_type::u32) {
return "%" PRIu32;
} else if constexpr (EIntTy == __integral_type::u64) {
return "%" PRIu64;
} else if constexpr (EIntTy == __integral_type::i8) {
return "%" PRId8;
} else if constexpr (EIntTy == __integral_type::i16) {
return "%" PRId16;
} else if constexpr (EIntTy == __integral_type::i32) {
return "%" PRId32;
} else if constexpr (EIntTy == __integral_type::i64) {
return "%" PRId64;
}
} else if (base == __integral_base_type::base16) {
if constexpr (EIntTy == __integral_type::u8) {
return "%" PRIx8;
} else if constexpr (EIntTy == __integral_type::u16) {
return "%" PRIx16;
} else if constexpr (EIntTy == __integral_type::u32) {
return "%" PRIx32;
} else if constexpr (EIntTy == __integral_type::u64) {
return "%" PRIx64;
} else {
static_assert(false, "Unsupported integral type and base");
}
}
}
// Integer to_chars
template<typename T>
requires std::integral<T> && (!std::same_as<T, bool>)
to_chars_result to_chars(char* first, char* last, T value, int base = 10) {
if (first >= last) {
return {first, std::errc::value_too_large};
}
constexpr auto integral_type = __classify_int_type<T>();
const auto opt_integral_base_type = __classify_int_base(base);
if (!opt_integral_base_type.has_value()) {
return {first, std::errc::invalid_argument};
}
const auto integral_base_type = std::move(opt_integral_base_type.value());
// Use snprintf with appropriate format
const auto max_buffer_size = static_cast<size_t>(last - first);
const char* format_string = __get_int_format<integral_type>(integral_base_type);
int written = std::snprintf(first, max_buffer_size, format_string, value);
if (written < 0 || static_cast<size_t>(written) >= max_buffer_size) {
return {last, std::errc::value_too_large};
}
return {first + written, std::errc{}};
}
#endif
#if !defined(YYCC_CHARCONV_HAS_TO_CHARS_FLOAT)
/// @private
enum class __float_type {
f32,
f64,
};
/// @private
template<typename T>
constexpr __float_type __classify_float_type() {
if constexpr (std::is_same_v<T, float>) {
return __float_type::f32;
} else if constexpr (std::is_same_v<T, double>) {
return __float_type::f64;
} else {
static_assert(false, "Unsupported floating point type");
}
}
/// @private
enum class __float_fmt_type { general, scientific, fixed, hex };
/// @private
std::optional<__float_fmt_type> __classify_float_fmt(chars_format fmt) {
if (fmt == chars_format::general) {
return __float_fmt_type::general;
} else if (fmt == chars_format::scientific) {
return __float_fmt_type::scientific;
} else if (fmt == chars_format::fixed) {
return __float_fmt_type::fixed;
} else if (fmt == chars_format::hex) {
return __float_fmt_type::hex;
} else {
return std::nullopt;
}
}
/// @private
template<__float_type TFpTy>
std::optional<const char*> __get_float_format(chars_format fmt) {
// Precision is passed by extra argument via ".*" format.
if (fmt == chars_format::general) {
if constexpr (TFpTy == __float_type::f32) {
return "%.*g";
} else if constexpr (TFpTy == __float_type::f64) {
return "%.*lg";
}
} else if (fmt == chars_format::scientific) {
if constexpr (TFpTy == __float_type::f32) {
return "%.*e";
} else if constexpr (TFpTy == __float_type::f64) {
return "%.*le";
}
} else if (fmt == chars_format::fixed) {
if constexpr (TFpTy == __float_type::f32) {
return "%.*f";
} else if constexpr (TFpTy == __float_type::f64) {
return "%.*lf";
}
} else if (fmt == chars_format::hex) {
if constexpr (TFpTy == __float_type::f32) {
return "%.*a";
} else if constexpr (TFpTy == __float_type::f64) {
return "%.*la";
}
} else {
return std::nullopt;
}
}
// Float to_chars
template<typename T>
requires std::floating_point<T>
to_chars_result to_chars(char* first, char* last, T value, chars_format fmt, int precision = 6) {
if (first >= last) {
return {first, std::errc::value_too_large};
}
constexpr auto float_type = __classify_float_type<T>();
const auto opt_float_fmt_type = __classify_float_fmt(fmt);
if (!opt_float_fmt_type.has_value()) {
return {first, std::errc::invalid_argument};
}
const auto float_fmt_type = std::move(opt_float_fmt_type.value());
const auto max_buffer_size = static_cast<size_t>(last - first);
const char* format_string = __get_float_format<float_type>(float_fmt_type);
int written = std::snprintf(first, max_buffer_size, format_string, precision, value);
if (written < 0 || static_cast<size_t>(written) >= max_buffer_size) {
return {last, std::errc::value_too_large};
}
return {first + written, std::errc{}};
}
#endif
#if !defined(YYCC_CHARCONV_HAS_FROM_CHARS_INT)
/// @private
enum class __strtoi_cluster { tol, toll, toul, toull };
/// @private
template<typename T>
constexpr __strtoi_cluster __classify_strtoi_cluster() {
if constexpr (std::is_signed_v<T>) {
if constexpr (sizeof(T) <= sizeof(long)) {
return __strtoi_cluster::tol;
} else if constexpr (sizeof(T) <= sizeof(long long)) {
return __strtoi_cluster::toll;
} else {
static_assert(false, "Unsupported signed integral type");
}
} else {
if constexpr (sizeof(T) <= sizeof(unsigned long)) {
return __strtoi_cluster::toul;
} else if constexpr (sizeof(T) <= sizeof(unsigned long long)) {
return __strtoi_cluster::toull;
} else {
static_assert(false, "Unsupported unsigned integral type");
}
}
}
/// @private
template<__strtoi_cluster TFc>
auto __execute_strtoi(const char* str, char** str_end, int base) {
if constexpr (TFc == __strtoi_cluster::tol) {
return std::strtol(str, str_end, base);
} else if constexpr (TFc == __strtoi_cluster::toll) {
return std::strtoll(str, str_end, base);
} else if constexpr (TFc == __strtoi_cluster::toul) {
return std::strtoul(str, str_end, base);
} else if constexpr (TFc == __strtoi_cluster::toull) {
return std::strtoull(str, str_end, base);
}
}
// Integer from_chars
template<typename T>
requires std::integral<T> && (!std::same_as<T, bool>)
from_chars_result from_chars(const char* first, const char* last, T& value, int base = 10) {
if (first >= last) {
return {first, std::errc::invalid_argument};
}
// strtoi function cluster strongly order that given string must be NULL-terminated.
// So we must do a heavy copy in there because first-last pair is not NULL-terminated guaranteed.
std::string buffer(first, static_cast<size_t>(last - first));
constexpr auto strtoi_cluster = __classify_strtoi_cluster<T>();
errno = 0;
char* end_ptr = const_cast<char*>(first);
auto result = __execute_strtoi<strtoi_cluster>(buffer.data(), &end_ptr, base);
if (errno == ERANGE) {
return {end_ptr, std::errc::result_out_of_range};
}
using strtoi_cluster_rvtype = decltype(result);
// Check if result fits in T
if (result < static_cast<strtoi_cluster_rvtype>(std::numeric_limits<T>::min())
|| result > static_cast<strtoi_cluster_rvtype>(std::numeric_limits<T>::max())) {
return {end_ptr, std::errc::result_out_of_range};
}
if (end_ptr == buffer.data()) {
return {first, std::errc::invalid_argument};
}
// Ensure we don't go past 'last'
if ((end_ptr - buffer.data()) > (last - first)) {
return {const_cast<char*>(last), std::errc::invalid_argument};
}
value = static_cast<T>(result);
return {first + (end_ptr - buffer.data()), std::errc{}};
}
#endif
#if !defined(YYCC_CHARCONV_HAS_FROM_CHARS_FLOAT)
/// @private
enum class __strtof_cluster { tof, tod };
/// @private
template<typename T>
constexpr __strtof_cluster __classify_strtof_cluster() {
if constexpr (std::is_same_v<T, float>) {
return __strtof_cluster::tof;
} else if constexpr (std::is_same_v<T, double>) {
return __strtof_cluster::tod;
} else {
static_assert(false, "Unsupported floating point type");
}
}
/// @private
template<__strtof_cluster TFc>
auto __execute_strtof(const char* str, char** str_end) {
if constexpr (TFc == __strtof_cluster::tof) {
return std::strtof(str, str_end);
} else if constexpr (TFc == __strtof_cluster::tod) {
return std::strtod(str, str_end);
}
}
// Float from_chars
template<typename T>
requires std::floating_point<T>
from_chars_result from_chars(const char* first, const char* last, T& value, chars_format fmt = chars_format::general) {
// We ignore "fmt" by design.
if (first >= last) {
return {first, std::errc::invalid_argument};
}
// strtof function cluster strongly order that given string must be NULL-terminated.
// So we must do a heavy copy in there because first-last pair is not NULL-terminated guaranteed.
std::string buffer(first, static_cast<size_t>(last - first));
constexpr auto strtof_cluster = __classify_strtof_cluster<T>();
errno = 0;
char* end_ptr = const_cast<char*>(first);
auto result = __execute_strtof<strtof_cluster>(buffer.data(), &end_ptr);
if (errno == ERANGE) {
return {end_ptr, std::errc::result_out_of_range};
}
if (end_ptr == buffer.data()) {
return {first, std::errc::invalid_argument};
}
// Ensure we don't go past 'last'
if ((end_ptr - buffer.data()) > (last - first)) {
return {const_cast<char*>(last), std::errc::invalid_argument};
}
value = result;
return {first + (end_ptr - buffer.data()), std::errc{}};
}
#endif
} // namespace std
//#endif

View File

@@ -97,7 +97,7 @@ namespace yycc::string::op {
}
}
std::u8string replace(const std::u8string_view& _strl, const std::u8string_view& _from_strl, const std::u8string_view& _to_strl) {
std::u8string to_replace(const std::u8string_view& _strl, const std::u8string_view& _from_strl, const std::u8string_view& _to_strl) {
// prepare result
std::u8string strl(_strl);
replace(strl, _from_strl, _to_strl);

View File

@@ -66,7 +66,7 @@ namespace yycc::string::op {
* @param[in] _to_strl The \e new string.
* @return The result of replacement.
*/
std::u8string replace(const std::u8string_view& _strl, const std::u8string_view& _from_strl, const std::u8string_view& _to_strl);
std::u8string to_replace(const std::u8string_view& _strl, const std::u8string_view& _from_strl, const std::u8string_view& _to_strl);
#pragma endregion

View File

@@ -23,7 +23,7 @@
* @brief The namespace providing Windows universal dialog features.
* @details
* This namespace only available on Windows platform.
* See also \ref dialog_helper.
* See also \ref windows__dialog.
*/
namespace yycc::windows::dialog {
@@ -194,7 +194,7 @@ namespace yycc::windows::dialog {
* @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.
* For how to use this struct, see \ref windows__dialog.
*/
class FileDialog {
public:

View File

@@ -49,7 +49,6 @@ namespace yycc::windows::winfct {
* @param[in] hModule
* The HANDLE to the module where you want to get file name.
* It is same as the HANDLE parameter of Win32 \c GetModuleFileName.
* @param[out] ret The variable receiving UTF8 encoded file name of given module.
* @return Fetched UTF8 encoded file name of given module, or error occurs.
*/
WinFctResult<std::u8string> get_module_file_name(HINSTANCE hModule);
@@ -101,10 +100,9 @@ namespace yycc::windows::winfct {
};
/**
* @brief Get the path to \%LOCALAPPDATA\%.
* @details \%LOCALAPPDATA\% usually was used as putting local app data files
* @param[out] ret The variable receiving UTF8 encoded path to LOCALAPPDATA.
* @return True if success, otherwise false.
* @brief Get the path to known directory in Windows.
* @param[in] path_type The type of known directory.
* @return The result type containing either fetched path, or error occurs.
*/
WinFctResult<std::u8string> get_known_path(KnownDirectory path_type);

View File

@@ -61,6 +61,11 @@ PRIVATE
GTest::gtest_main
)
# Install binary
install(TARGETS YYCCTest
RUNTIME DESTINATION ${YYCC_INSTALL_BIN_PATH}
)
# Discover all test
include(GoogleTest)
gtest_discover_tests(YYCCTest)

View File

@@ -14,6 +14,7 @@ namespace yyccshared::literals {
// UNICODE Test Strings
// Ref: https://stackoverflow.com/questions/478201/how-to-test-an-application-for-correct-encoding-e-g-utf-8
#define UNICODE_STR_BLANK ""
#define UNICODE_STR_JAPAN "\u30E6\u30FC\u30B6\u30FC\u5225\u30B5\u30A4\u30C8"
#define UNICODE_STR_CHINA "\u7B80\u4F53\u4E2D\u6587"
#define UNICODE_STR_KOREA "\uD06C\uB85C\uC2A4 \uD50C\uB7AB\uD3FC\uC73C\uB85C"
@@ -30,6 +31,7 @@ namespace yyccshared::literals {
#define UNICODE_STR_EMOJI "\U0001F363 \u2716 \U0001F37A" // sushi x beer mug
static std::vector<std::u8string> UTFLIT_U8STR_VEC{
U8_LITERAL(UNICODE_STR_BLANK),
U8_LITERAL(UNICODE_STR_JAPAN),
U8_LITERAL(UNICODE_STR_CHINA),
U8_LITERAL(UNICODE_STR_KOREA),
@@ -44,6 +46,7 @@ namespace yyccshared::literals {
U8_LITERAL(UNICODE_STR_EMOJI),
};
static std::vector<std::wstring> UTFLIT_WSTR_VEC{
WSTR_LITERAL(UNICODE_STR_BLANK),
WSTR_LITERAL(UNICODE_STR_JAPAN),
WSTR_LITERAL(UNICODE_STR_CHINA),
WSTR_LITERAL(UNICODE_STR_KOREA),
@@ -58,6 +61,7 @@ namespace yyccshared::literals {
WSTR_LITERAL(UNICODE_STR_EMOJI),
};
static std::vector<std::u16string> UTFLIT_U16STR_VEC{
U16_LITERAL(UNICODE_STR_BLANK),
U16_LITERAL(UNICODE_STR_JAPAN),
U16_LITERAL(UNICODE_STR_CHINA),
U16_LITERAL(UNICODE_STR_KOREA),
@@ -72,6 +76,7 @@ namespace yyccshared::literals {
U16_LITERAL(UNICODE_STR_EMOJI),
};
static std::vector<std::u32string> UTFLIT_U32STR_VEC{
U32_LITERAL(UNICODE_STR_BLANK),
U32_LITERAL(UNICODE_STR_JAPAN),
U32_LITERAL(UNICODE_STR_CHINA),
U32_LITERAL(UNICODE_STR_KOREA),
@@ -157,12 +162,22 @@ namespace yyccshared::literals {
#pragma region OtherLiterals Data
static std::vector<OtherLiteral> OTHERLIT_OTHERSTR_VEC{{"\xC4\xE3\xBA\xC3\xD6\xD0\xB9\xFA", UINT32_C(936), "GBK", u8"gbk"}};
static std::vector<OtherLiteral> OTHERLIT_OTHERSTR_VEC{
{"", UINT32_C(437), "ASCII", u8"ascii"},
{"\xC4\xE3\xBA\xC3\xD6\xD0\xB9\xFA", UINT32_C(936), "GBK", u8"gbk"},
};
#define OTHER_STR_BLANK ""
#define OTHER_STR_GBK "\u4f60\u597d\u4e2d\u56fd"
static std::vector<std::u8string> OTHERLIT_U8STR_VEC{U8_LITERAL(OTHER_STR_GBK)};
static std::vector<std::wstring> OTHERLIT_WSTR_VEC{WSTR_LITERAL(OTHER_STR_GBK)};
static std::vector<std::u8string> OTHERLIT_U8STR_VEC{
U8_LITERAL(OTHER_STR_BLANK),
U8_LITERAL(OTHER_STR_GBK),
};
static std::vector<std::wstring> OTHERLIT_WSTR_VEC{
WSTR_LITERAL(OTHER_STR_BLANK),
WSTR_LITERAL(OTHER_STR_GBK),
};
#pragma endregion

View File

@@ -72,10 +72,10 @@ namespace yycctest::carton::clap {
// Add options
auto options = CLAP::option::OptionCollection();
int_option = options.add_option(CLAP::option::Option(u8"i", u8"int", u8"<integer>", u8"integral argument"));
float_option = options.add_option(CLAP::option::Option(u8"f", std::nullopt, u8"<float>", u8""));
string_option = options.add_option(CLAP::option::Option(std::nullopt, u8"string", u8"<string>", u8""));
clamped_float_option = options.add_option(CLAP::option::Option(std::nullopt, u8"clamped-float", u8"<float>", u8""));
int_option = options.add_option(CLAP::option::Option(u8"i", u8"int", u8"integer", u8"integral argument"));
float_option = options.add_option(CLAP::option::Option(u8"f", std::nullopt, u8"float", u8""));
string_option = options.add_option(CLAP::option::Option(std::nullopt, u8"string", u8"string", u8""));
clamped_float_option = options.add_option(CLAP::option::Option(std::nullopt, u8"clamped-float", u8"float", u8""));
novalue_option = options.add_option(CLAP::option::Option(u8"b", std::nullopt, std::nullopt, u8""));
// Add variables

View File

@@ -25,32 +25,32 @@ namespace yycctest::string::op {
TEST(StringOp, Replace) {
// Normal case
{
auto rv = OP::replace(u8"aabbcc", u8"bb", u8"dd");
auto rv = OP::to_replace(u8"aabbcc", u8"bb", u8"dd");
EXPECT_EQ(rv, u8"aaddcc");
}
// No matched expected string
{
auto rv = OP::replace(u8"aabbcc", u8"zz", u8"yy");
auto rv = OP::to_replace(u8"aabbcc", u8"zz", u8"yy");
EXPECT_EQ(rv, u8"aabbcc");
}
// Empty expected string
{
auto rv = OP::replace(u8"aabbcc", std::u8string_view(), u8"zz");
auto rv = OP::to_replace(u8"aabbcc", std::u8string_view(), u8"zz");
EXPECT_EQ(rv, u8"aabbcc");
}
// Empty replace string
{
auto rv = OP::replace(u8"aaaabbaa", u8"aa", u8"");
auto rv = OP::to_replace(u8"aaaabbaa", u8"aa", u8"");
EXPECT_EQ(rv, u8"bb");
}
// Nested replacing
{
auto rv = OP::replace(u8"aaxcc", u8"x", u8"yx");
auto rv = OP::to_replace(u8"aaxcc", u8"x", u8"yx");
EXPECT_EQ(rv, u8"aayxcc");
}
// Empty source string
{
auto rv = OP::replace(std::u8string_view(), u8"", u8"xy");
auto rv = OP::to_replace(std::u8string_view(), u8"", u8"xy");
EXPECT_EQ(rv, u8"");
}
}

View File

@@ -8,9 +8,10 @@ namespace yycctest::windows::console {
#if defined(YYCC_OS_WINDOWS) && defined(YYCC_STL_MSSTL)
TEST(WindowsConsole, ColorfulConsole) {
// Set colorful console should always be success.
auto rv = CONSOLE::colorful_console();
EXPECT_TRUE(rv.has_value());
// Set colorful console should always be success.
// Unless it is not a TTY.
EXPECT_TRUE(rv.has_value() || rv.error() == CONSOLE::ExecError::NotTty);
}
#endif