Compare commits
52 Commits
e42a3b6e58
...
master
Author | SHA1 | Date | |
---|---|---|---|
ccd0219ead | |||
4bfba6f243 | |||
9e994dd4f0 | |||
d6034f8cb0 | |||
0694d923f3 | |||
580b096cb3 | |||
f9365481b9 | |||
15aade052f | |||
050bed400d | |||
244e39c4d1 | |||
d52630ac5c | |||
a76f10722d | |||
8a72e6a655 | |||
dfc0c127c5 | |||
2f11ba6023 | |||
00c8f09907 | |||
734cd01da8 | |||
bdeaea294f | |||
ff8c7d04cc | |||
0ab470367c | |||
c4d441f5fa | |||
f8a696b4e8 | |||
f65eff6edf | |||
8fcfa180b4 | |||
e23a1346eb | |||
2576523dbb | |||
9ce52e8d4b | |||
7785773196 | |||
cfbc3c68e0 | |||
8dbe32cb8e | |||
664763afbb | |||
a34bab07c1 | |||
51d288ac4b | |||
20a9ef4166 | |||
17540072d3 | |||
fcac886f07 | |||
27baf2a080 | |||
b9f81c16a0 | |||
54134b342e | |||
ce2b411b0b | |||
5372af79f8 | |||
b79df0c65e | |||
4f0b3d19d1 | |||
f014e54604 | |||
821a592f02 | |||
6043609709 | |||
53e8a77f47 | |||
6d44c7605b | |||
c2f6e29c36 | |||
c102964703 | |||
3605151caf | |||
fa52d7416f |
@ -117,7 +117,7 @@ BreakFunctionDefinitionParameters: false
|
||||
BreakInheritanceList: BeforeColon
|
||||
BreakStringLiterals: false
|
||||
BreakTemplateDeclarations: Yes
|
||||
ColumnLimit: 100
|
||||
ColumnLimit: 140
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
@ -206,7 +206,7 @@ RequiresExpressionIndentation: OuterScope
|
||||
SeparateDefinitionBlocks: Leave
|
||||
ShortNamespaceLines: 1
|
||||
SkipMacroDefinitionBody: false
|
||||
SortIncludes: CaseSensitive
|
||||
SortIncludes: Never
|
||||
SortJavaStaticImport: Before
|
||||
SortUsingDeclarations: Lexicographic
|
||||
SpaceAfterCStyleCast: true
|
||||
@ -300,6 +300,9 @@ StatementMacros:
|
||||
- YYCC_DEFAULT_COPY
|
||||
- YYCC_DEFAULT_MOVE
|
||||
- YYCC_DEFAULT_COPY_MOVE
|
||||
- YYCC_DECL_COPY
|
||||
- YYCC_DECL_MOVE
|
||||
- YYCC_DECL_COPY_MOVE
|
||||
TableGenBreakInsideDAGArg: DontBreak
|
||||
TabWidth: 4
|
||||
UseTab: Never
|
||||
|
15
.gitignore
vendored
15
.gitignore
vendored
@ -1,12 +1,17 @@
|
||||
# -------------------- Output --------------------
|
||||
## ===== Personal =====
|
||||
# Ignore build resources
|
||||
out/
|
||||
src/YYCC/YYCCVersion.hpp
|
||||
build/
|
||||
install/
|
||||
|
||||
# Ignore CMake generated stuff
|
||||
src/yycc/version.hpp
|
||||
CMakeSettings.json
|
||||
|
||||
# -------------------- VSCode --------------------
|
||||
## ===== VSCode =====
|
||||
.vscode/
|
||||
|
||||
# -------------------- CMake --------------------
|
||||
## ===== CMake =====
|
||||
CMakeLists.txt.user
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
@ -19,7 +24,7 @@ compile_commands.json
|
||||
CTestTestfile.cmake
|
||||
_deps
|
||||
|
||||
# -------------------- Visual Studio --------------------
|
||||
## ===== Visual Studio =====
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
|
@ -4,10 +4,15 @@ project(YYCC
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
||||
# Setup C++ standard
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
# Provide options
|
||||
option(YYCC_BUILD_TESTBENCH "Build testbench of YYCCommonplace." OFF)
|
||||
option(YYCC_BUILD_DOC "Build document of YYCCommonplace." OFF)
|
||||
option(YYCC_DEBUG_UE_FILTER "YYCC developer used switch for testing Windows unhandled exception filter. Should not set to ON!!!" OFF)
|
||||
option(YYCC_ENFORCE_ICONV "Enforce iconv support for this library (e.g. in MSYS2 environment)." OFF)
|
||||
|
||||
# Setup install path from CMake provided install path for convenient use.
|
||||
include(GNUInstallDirs)
|
||||
@ -27,6 +32,10 @@ if (YYCC_BUILD_TESTBENCH)
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||
find_package(GTest REQUIRED)
|
||||
endif ()
|
||||
# Iconv is required if we are not in Windows or user request it
|
||||
if (YYCC_ENFORCE_ICONV OR (NOT WIN32))
|
||||
find_package(Iconv REQUIRED)
|
||||
endif ()
|
||||
|
||||
# Import 3 build targets
|
||||
add_subdirectory(src)
|
||||
|
106
COMPILE.md
Normal file
106
COMPILE.md
Normal file
@ -0,0 +1,106 @@
|
||||
# Compile Manual
|
||||
|
||||
## Choose Version
|
||||
|
||||
We suggest that you only use stable version (tagged commit).
|
||||
The latest commit always present current works.
|
||||
It means that it is not stable and work in progress.
|
||||
|
||||
## Requirements
|
||||
|
||||
* CMake 3.23 at least.
|
||||
* The common compiler supporting C++ 23 (GCC / Clang / MSVC).
|
||||
* Iconv (Optional on Windows. Required on other systems).
|
||||
* [GoogleTest](https://github.com/google/googletest) (Required if you build testbench).
|
||||
* Doxygen (Required if you build documentation).
|
||||
* Python and Astral UV (Required if you use "User Build" method)
|
||||
|
||||
> [!WARNING]
|
||||
> You may face some issues when building on macOS with Clang. That's not your fault.
|
||||
> Clang used libc++ library lacks some essential features used by this project.
|
||||
> You may try other solutions for compiling this project on macOS or with Clang.
|
||||
|
||||
## Preparing
|
||||
|
||||
### GoogleTest
|
||||
|
||||
GoogleTest is required if you need to build testbench.
|
||||
If you don't need this please skip this chapter.
|
||||
|
||||
We use GoogleTest v1.17.0.
|
||||
It would be okey use other versions but I have not test on them.
|
||||
|
||||
> [!WARNING]
|
||||
> When building this project, you may face link error with GoogleTest, especially on Linux.
|
||||
> This issue is caused by that the binary provided by your package manager is built in C++17 and its ABI is incompatible with C++23.
|
||||
> See this [GitHub Issue](https://github.com/google/googletest/issues/4591) for more infomation.
|
||||
> The solution is that download GoogleTest source code and build it in C++23 on your own.
|
||||
> Following content tell you how to do this.
|
||||
|
||||
There are the steps instructing you how to compile GoogleTest manually.
|
||||
|
||||
1. Download GoogleTest source code with given version in GitHub Release page.
|
||||
1. Extract it into a directory.
|
||||
1. Enter this directory and create 2 subdirectory `build` and `install` for CMake build and install respectively.
|
||||
1. Enter `build` directory and configure CMake with extra `-DCMAKE_CXX_STANDARD=23 -Dgtest_force_shared_crt=ON` parameters.
|
||||
1. Use CMake to build GoogleTest
|
||||
1. Use CMake to install GoogleTest into previous we created `install` directory.
|
||||
|
||||
### Iconv
|
||||
|
||||
Iconv is optional on Windows and disabled in default.
|
||||
However, if you are building project on MSYS2 or MINGW platform in Windows, we suggest you enable Iconv feature for more functions.
|
||||
Once you enable this feature, you must prepare installed Iconv which is no problem for MSYS2 environment via package manager.
|
||||
You also can enable this feature under MSVC but you must make sure that you can compile Iconv under MSVC.
|
||||
For how to enable this feature forcely, see following chapters for more infomations.
|
||||
|
||||
On other platforms, Iconv is enabled automatically and can not be disabled.
|
||||
Because there is no other encoding convertion libraries that we can use (Windows has a builtin set of encoding convertion Win32 functions).
|
||||
|
||||
### Doxygen
|
||||
|
||||
Doxygen is required only if you need to build documentation.
|
||||
If you don't need this please skip this chapter.
|
||||
|
||||
We use Doxygen 1.9.7.
|
||||
It would be okey use other versions but I have not test on them.
|
||||
|
||||
YYCCommonplace use Doxygen as its documentation system.
|
||||
So before compiling, you must make sure `doxygen` are presented in your environment.
|
||||
|
||||
## Build and Install
|
||||
|
||||
There are 2 different ways to build this project.
|
||||
If you are the user of this project (just want this project to make something work), please choose "User Build".
|
||||
If you are a developer (developer of this project, or use this project as dependency to develop your project), please choose "Developer Build".
|
||||
|
||||
### User Build
|
||||
|
||||
"User Build" is basically how GitHub Action build this project.
|
||||
|
||||
We use Python 3.11 and UV 0.7.17 to manage our build script generator.
|
||||
It would be okey use other versions but I have not test on them.
|
||||
|
||||
TODO...
|
||||
|
||||
### Developer Build
|
||||
|
||||
TODO...
|
||||
|
||||
There is a list listing all variables you may configure during compiling.
|
||||
|
||||
* `YYCC_BUILD_TESTBENCH`: Set it to `ON` to build testbench. `OFF` in default.
|
||||
It is useful for the developer of this project.
|
||||
It also suit for the user who has runtime issues on their platforms to check whether this project works as expected.
|
||||
* `YYCC_BUILD_DOC`: Set it to `ON` to build documentation. `OFF` in default.
|
||||
It may be useful for the developer who firstly use this project in their own projects.
|
||||
Please note that generated documentation is different in different platforms.
|
||||
* `YYCC_ENFORCE_ICONV`: Set it to `ON` to enable Iconv feature forcely. `OFF` in default.
|
||||
The usage of this option has been introduced in previous "Iconv" chapter.
|
||||
* `GTest_ROOT`: TODO
|
||||
* `Iconv_ROOT`: TODO
|
||||
* `CMAKE_CXX_STANDARD`: Set C++ standard version of project.
|
||||
`23` in default and this version can not be lower than C++23.
|
||||
You usually do not need change this.
|
||||
* `CMAKE_POSITION_INDEPENDENT_CODE`: Set it to `True` to enable PIC.
|
||||
This is essential for those project which use this project and produce dynamicing library as final artifact.
|
11
README.md
11
README.md
@ -4,14 +4,9 @@ YYC Commonplace, or YYCCommonplace (abbr. YYCC) is a static library specifically
|
||||
|
||||
## Usage
|
||||
|
||||
For more usage about this library, please build documentation of this project via Doxygen and read it.
|
||||
|
||||
And I also highly recommend that you read documentation first before writing with this library.
|
||||
|
||||
However, the documentation need CMake to build and you may don't know how to use CMake in this project. So as the alternative, you also can browse the raw Doxygen documentation file: `doc/src/intro.dox` for how to build this project (including documentation) first.
|
||||
For more usage about this library, please read documentation after building this project with documentation.
|
||||
I also highly recommend that you read documentation first before writing with this library.
|
||||
|
||||
## Build
|
||||
|
||||
This project require at least CMake 3.23 to build. We suggest that you only use stable version (tagged commit). The latest commit may still work in progress and not stable.
|
||||
|
||||
See documentation for how to build this project.
|
||||
See [Compile Manual](./COMPILE.md).
|
||||
|
@ -29,6 +29,9 @@
|
||||
|
||||
\li \subpage intro
|
||||
|
||||
\li \subpage premise_and_principle
|
||||
|
||||
<!--
|
||||
\li \subpage library_macros
|
||||
|
||||
\li \subpage library_encoding
|
||||
@ -46,20 +49,24 @@
|
||||
\li \subpage std_patch
|
||||
|
||||
\li \subpage enum_helper
|
||||
-->
|
||||
|
||||
<B>Advanced Features</B>
|
||||
|
||||
<!--
|
||||
\li \subpage constraints
|
||||
|
||||
\li \subpage config_manager
|
||||
|
||||
\li \subpage arg_parser
|
||||
-->
|
||||
|
||||
</TD>
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">
|
||||
|
||||
<B>Windows Specific Features</B>
|
||||
|
||||
<!--
|
||||
\li \subpage win_import
|
||||
|
||||
\li \subpage com_helper
|
||||
@ -69,6 +76,7 @@
|
||||
\li \subpage win_fct_helper
|
||||
|
||||
\li \subpage exception_helper
|
||||
-->
|
||||
|
||||
</TD>
|
||||
</TR>
|
||||
|
@ -6,7 +6,7 @@ YYCCommonplace, or YYC Commonplace (abbr. YYCC),
|
||||
is a static library providing various useful C++ functions
|
||||
when programming with standard library or Windows environment.
|
||||
|
||||
During the development of a few projects,
|
||||
At the beginning, during the development of a few projects,
|
||||
I gradually understand how Windows make the compromise with the code written by its old developers,
|
||||
and what is developer wanted in contemporary C++ standard library under Windows environment.
|
||||
So I create this static library for all of my C++ project.
|
||||
@ -15,6 +15,16 @@ I can use a clear and easy way to manage these codes.
|
||||
I can easily fix issues found in project using this library by updating a single project,
|
||||
rather than fixing these duplicated code in each project one by one
|
||||
because all of them share the same implementations.
|
||||
This is the origin of the 1.x version of YYCC.
|
||||
|
||||
After a few years ago, I start to write in Rust and more complicated codes.
|
||||
I was allured by Rust and hope all these feature Rust holded can be adapted into C++,
|
||||
so I start to refactor this library in modern way.
|
||||
However, the compatibility with low C++ standard version is now become the shortcoming of this library.
|
||||
I was forced to considering the compatibility with C++ 17 and it cause a huge work.
|
||||
So, after think twice, I decide to drop the support of C++ 17, which the higest C++ standard I can used for Virtools project.
|
||||
And increase the standard to C++ 23 directly to have better experience.
|
||||
That's the origin of the 2.x version of YYCC.
|
||||
|
||||
This project mainly is served for my personal use.
|
||||
But I would be honored if you would like to use this in your project.
|
||||
@ -46,17 +56,18 @@ Thus I can have a similar Linux C++ programming experience on Windows.
|
||||
|
||||
The eccentric decision of standard commission also is the reason why I create this library.
|
||||
|
||||
\li C++ standard commission loves to bring one feature with N concepts and P assistant classes.
|
||||
\li C++ standard commission prefer to provide one function with very fundamental classes and functions
|
||||
and programmer need to write too much code to achieve a simple work.
|
||||
\li C++ standard commission seems doesn't want to bring any features the programmer urgent needed.
|
||||
\li C++ standard commission loves delete programmer loved convenient functions and classes.
|
||||
\li etc...
|
||||
|
||||
There is not a proper way to \e format a string in C++ until C++ 20 (\c std::format).
|
||||
String related functions, such as split, lower, higher, replace, now still not be included in standard library.
|
||||
Programmer loved, easy to used UTF8 procession functions and classes was deprecate now and will be removed in future.
|
||||
Programmer loved, easy to used encoding convertion functions and classes are deprecate now and will be removed in future.
|
||||
|
||||
That's why I create this library.
|
||||
I bring these function in this library.
|
||||
I bring these functions in this library.
|
||||
Not industrial level, but easy to use and have enough performance in my project.
|
||||
|
||||
\subsection intro__why__boost Boost Issues
|
||||
@ -65,7 +76,7 @@ Bosst is a powerful C++ library. But the shortcoming is overt. It's tooooo big.
|
||||
This drawback will be more obvious considering the bad dependency mechanism of C++.
|
||||
Although the most of Boost sub-library is header-only, but some library still need to link with Boost.
|
||||
It order you download the whole Boost library and extract it in your hard disk.
|
||||
Cost more than half hours, 5+ GB disk space and the life time of your disk.
|
||||
Cost more than half hour of your life, 5+ GB disk space and the life time of your disk.
|
||||
|
||||
The functions belonging to Boost is industrial level.
|
||||
But what I want is not industrial level functions.
|
||||
@ -74,6 +85,7 @@ I don't need extreme performance. I just want my code works.
|
||||
|
||||
So I create this library, bring some Boost functions with ordinary but not bad implementation.
|
||||
|
||||
<!--
|
||||
\section intro__usage Library Usage
|
||||
|
||||
Before using this library, I suggest you read this manual fully to have a full overview of this library.
|
||||
@ -187,5 +199,5 @@ YYCC CMake build script contains a special option called \c YYCC_DEBUG_UE_FILTER
|
||||
If you set it to true, it will add a public macro \c YYCC_DEBUG_UE_FILTER to YYCC project.
|
||||
This macro will enable special code path for the convenience of debugging \ref exception_helper related features.
|
||||
So in common use, user should not enable this option.
|
||||
|
||||
-->
|
||||
*/
|
||||
|
@ -73,7 +73,7 @@ Assume \c blabla() function is Windows specific.
|
||||
We have following example code:
|
||||
|
||||
\code
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
blabla();
|
||||
#endif
|
||||
\endcode
|
||||
|
47
doc/src/premise_and_principle.dox
Normal file
47
doc/src/premise_and_principle.dox
Normal file
@ -0,0 +1,47 @@
|
||||
/**
|
||||
|
||||
\page premise_and_principle Premise and Principle
|
||||
|
||||
When programming with this library, there is some premise and principle you should noticed.
|
||||
|
||||
\section premise_and_principle__exception_is_error Exception is Error
|
||||
|
||||
The most crucial spot of this library is <B>"Exception is Error"</B>.
|
||||
When some functions throw exception, it should cause program paniked, rather than recover from it.
|
||||
This is inspired from Rust, and also the compromise with STL.
|
||||
|
||||
Most functions this library provided has Rust-Result-like return value.
|
||||
It means that programmer can handle error correctly.
|
||||
However, this library is based on STL, another library that may throw C++ exception to indicate error.
|
||||
We can not control this behavior of STL, so I forcely apply this rule.
|
||||
|
||||
\section premise_and_principle__os_encoding OS Encoding
|
||||
|
||||
This library has special treat with Windows to make it works on Windows.
|
||||
However, for other operating system, it do not have too much care.
|
||||
We brutally make a premise that other operating systems are UNIX-liked and use UTF8 as its encoding.
|
||||
|
||||
\section premise_and_principle__string_encoding String Encoding
|
||||
|
||||
Before using this library, you should know the encoding strategy of this library first.
|
||||
In short words, this library use UTF8 encoding everywhere except some special cases list following (not all).
|
||||
|
||||
\li Traditional format function in yycc::string::op.
|
||||
Traditional format function provide some overloads for ordinary string formatting.
|
||||
That's because this feature is so common to use in some cases.
|
||||
\li The message of Rust panic in yycc::rust::panic.
|
||||
Due to the limitation of \c std::format, we only can use ordinary string as its message content.
|
||||
\li The message of standard library exception.
|
||||
For the compatibility with C++ standard library exception,
|
||||
we only can use ordinary string as the message of exception.
|
||||
|
||||
\section premise_and_principle__cmake All in CMake
|
||||
|
||||
Since YYCC 2.0 version, we do not provide MSVC install layout.
|
||||
Any projects use this project should use CMake or CMake-compatible software as its build system.
|
||||
|
||||
The reason why we make this decision is that some essential contents are written in CMake files.
|
||||
For example, some environment detection macros and Windows environment patches.
|
||||
If you do not use CMake, these contents will not be presented in project and cause bad behavior when using this project.
|
||||
|
||||
*/
|
@ -11,7 +11,7 @@ Due to legacy reason, Windows defines various things which are not compatible wi
|
||||
YYCC has a way to solve the issue introduced above.
|
||||
|
||||
\code
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
#include <WinImportPrefix.hpp>
|
||||
#include <Windows.h>
|
||||
#include "other_header_depend_on_windows.h"
|
||||
|
215
script/.gitignore
vendored
215
script/.gitignore
vendored
@ -1,3 +1,216 @@
|
||||
# -------------------- Output --------------------
|
||||
## ===== Myself =====
|
||||
# Exclude VSCode
|
||||
.vscode/
|
||||
|
||||
# Exclude generated files
|
||||
win_build.bat
|
||||
linux_build.sh
|
||||
|
||||
## ===== Python =====
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[codz]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py.cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# UV
|
||||
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
#uv.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
#poetry.toml
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
||||
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
||||
#pdm.lock
|
||||
#pdm.toml
|
||||
.pdm-python
|
||||
.pdm-build/
|
||||
|
||||
# pixi
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
||||
#pixi.lock
|
||||
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
||||
# in the .venv directory. It is recommended not to include this directory in version control.
|
||||
.pixi
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.envrc
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
# Abstra
|
||||
# Abstra is an AI-powered process automation framework.
|
||||
# Ignore directories containing user credentials, local state, and settings.
|
||||
# Learn more at https://abstra.io/docs
|
||||
.abstra/
|
||||
|
||||
# Visual Studio Code
|
||||
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
||||
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
||||
# you could uncomment the following to ignore the entire vscode folder
|
||||
# .vscode/
|
||||
|
||||
# Ruff stuff:
|
||||
.ruff_cache/
|
||||
|
||||
# PyPI configuration file
|
||||
.pypirc
|
||||
|
||||
# Cursor
|
||||
# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
|
||||
# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
|
||||
# refer to https://docs.cursor.com/context/ignore-files
|
||||
.cursorignore
|
||||
.cursorindexingignore
|
||||
|
||||
# Marimo
|
||||
marimo/_static/
|
||||
marimo/_lsp/
|
||||
__marimo__/
|
||||
|
@ -1,15 +1,16 @@
|
||||
import jinja2
|
||||
import argparse
|
||||
import os
|
||||
import io
|
||||
import typing
|
||||
import re
|
||||
import shlex
|
||||
from pathlib import Path
|
||||
from dataclasses import dataclass
|
||||
import jinja2
|
||||
|
||||
def validate_cpp_ver(ver: str) -> str:
|
||||
if re.match(r'^[0-9]+$', ver) is not None: return ver
|
||||
else: raise argparse.ArgumentTypeError('invalid version of C++ standard.')
|
||||
|
||||
def write_line(f: io.TextIOWrapper, val: str) -> None:
|
||||
def write_line(f: typing.TextIO, val: str) -> None:
|
||||
f.write(val)
|
||||
f.write('\n')
|
||||
|
||||
@ -24,55 +25,52 @@ def escape_cmd_argument(arg):
|
||||
def escape_sh_argument(arg):
|
||||
return shlex.quote(arg)
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ScriptSettings:
|
||||
m_CppVersion: str
|
||||
m_BuildDoc: bool
|
||||
m_PIC: bool
|
||||
|
||||
def __init__(self, cpp_ver: str, build_doc: bool, pic: bool):
|
||||
self.m_CppVersion = cpp_ver
|
||||
self.m_BuildDoc = build_doc
|
||||
self.m_PIC = pic
|
||||
pic: bool
|
||||
cpp_version: str
|
||||
build_doc: bool
|
||||
build_testbench: bool
|
||||
|
||||
class TemplateRender:
|
||||
m_Loader: jinja2.BaseLoader
|
||||
m_Environment: jinja2.Environment
|
||||
loader: jinja2.BaseLoader
|
||||
environment: jinja2.Environment
|
||||
|
||||
m_WinTemplate: jinja2.Template
|
||||
m_LinuxTemplate: jinja2.Template
|
||||
win_template: jinja2.Template
|
||||
linux_template: jinja2.Template
|
||||
|
||||
m_Settings: ScriptSettings
|
||||
settings: ScriptSettings
|
||||
|
||||
def __init__(self, settings: ScriptSettings) -> None:
|
||||
self.m_Loader = jinja2.FileSystemLoader(self.__get_dir())
|
||||
self.m_Environment = jinja2.Environment(loader=self.m_Loader)
|
||||
self.loader = jinja2.FileSystemLoader(self.__get_dir())
|
||||
self.environment = jinja2.Environment(loader=self.loader)
|
||||
|
||||
self.m_WinTemplate = self.m_Environment.get_template('win_build.template.bat')
|
||||
self.m_LinuxTemplate = self.m_Environment.get_template('linux_build.template.sh')
|
||||
self.win_template = self.environment.get_template('win_build.bat.jinja')
|
||||
self.linux_template = self.environment.get_template('linux_build.sh.jinja')
|
||||
|
||||
self.m_Settings = settings
|
||||
self.settings = settings
|
||||
|
||||
def __get_dir(self) -> str:
|
||||
return os.path.dirname(__file__)
|
||||
def __get_dir(self) -> Path:
|
||||
return Path(__file__).resolve().parent
|
||||
|
||||
def __escape_path(self, val: str, is_win: bool) -> str:
|
||||
if is_win: return escape_cmd_argument(val)
|
||||
else: return escape_sh_argument(val)
|
||||
|
||||
def __render(self, template: jinja2.Template, dest_file: str, is_win: bool) -> None:
|
||||
with open(os.path.join(self.__get_dir(), dest_file), 'w', encoding='utf-8') as f:
|
||||
with open(self.__get_dir() / dest_file, 'w', encoding='utf-8') as f:
|
||||
f.write(template.render(
|
||||
repo_root_dir = self.__escape_path(os.path.dirname(self.__get_dir()), is_win),
|
||||
cpp_version = self.m_Settings.m_CppVersion,
|
||||
build_doc = self.m_Settings.m_BuildDoc,
|
||||
pic = settings.m_PIC
|
||||
repo_root_dir = self.__escape_path(str(self.__get_dir().parent), is_win),
|
||||
cpp_version = self.settings.cpp_version,
|
||||
build_doc = self.settings.build_doc,
|
||||
pic = settings.pic
|
||||
))
|
||||
|
||||
def render_win_script(self) -> None:
|
||||
self.__render(self.m_WinTemplate, 'win_build.bat', True)
|
||||
self.__render(self.win_template, 'win_build.bat', True)
|
||||
|
||||
def render_linux_script(self) -> None:
|
||||
self.__render(self.m_LinuxTemplate, 'linux_build.sh', False)
|
||||
self.__render(self.linux_template, 'linux_build.sh', False)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
@ -89,7 +87,7 @@ if __name__ == '__main__':
|
||||
parser.add_argument(
|
||||
'-d', '--build-doc',
|
||||
action='store_true', dest='build_doc',
|
||||
help='Build YYCC without documentation.'
|
||||
help='Build YYCC with documentation.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-p', '--pic',
|
||||
|
17
script/linux_build.sh.jinja
Normal file
17
script/linux_build.sh.jinja
Normal file
@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
# Navigate to project root directory
|
||||
cd {{ repo_root_dir }}
|
||||
|
||||
# Create build and install directory
|
||||
mkdir build
|
||||
mkdir install
|
||||
|
||||
# Build as release version
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_STANDARD={{ cpp_version }} {{ '-DCMAKE_POSITION_INDEPENDENT_CODE=True' if pic }} {{ '-DYYCC_BUILD_DOC=ON' if build_doc }} {{ '-DYYCC_BUILD_TESTBENCH=ON' if build_testbench }} ../.. --fresh
|
||||
cmake --build .
|
||||
cmake --install . --prefix ../install
|
||||
|
||||
# Exit to original path
|
||||
cd ..
|
||||
echo "YYCC Linux CMake build done"
|
@ -1,29 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Navigate to project root directory
|
||||
cd {{ repo_root_dir }}
|
||||
|
||||
# Create main binary directory
|
||||
mkdir bin
|
||||
cd bin
|
||||
# Create build directory
|
||||
mkdir build
|
||||
# Create install directory
|
||||
mkdir install
|
||||
cd install
|
||||
mkdir Debug
|
||||
mkdir Release
|
||||
cd ..
|
||||
|
||||
# Build current system debug and release version
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_STANDARD={{ cpp_version }} {{ '-DCMAKE_POSITION_INDEPENDENT_CODE=True' if pic }} ../.. --fresh
|
||||
cmake --build .
|
||||
cmake --install . --prefix ../install/Debug
|
||||
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_CXX_STANDARD={{ cpp_version }} {{ '-DCMAKE_POSITION_INDEPENDENT_CODE=True' if pic }} -DYYCC_BUILD_TESTBENCH=ON ../.. --fresh
|
||||
cmake --build .
|
||||
cmake --install . --prefix ../install/Release
|
||||
cd ..
|
||||
|
||||
# Exit to original path
|
||||
cd ..
|
||||
echo "Linux CMake Build Done"
|
2
script/pycodec/.gitignore
vendored
Normal file
2
script/pycodec/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# Exclude result
|
||||
*.cpp
|
7
script/pycodec/README.md
Normal file
7
script/pycodec/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# PyCodec
|
||||
|
||||
This directory contain all stuff related to PyCodec.
|
||||
|
||||
PyCodec use different encoding system on different OS. In Windows it use Win32 functions, and it will use Iconv in other OS. So we need a table converting PyCodec universal encoding name to Windows Code Page or Iconv Code Name. These relation was stored in CSV file and Python script will render it into C++ source code.
|
||||
|
||||
For the format of CSV file, each line is a record. The first item in record is the standard PyCodec name. The second item is corresponding Windows Code Page. If there is no corresponding Code Page, it can be empty. The third item is corresponding Iconv Code Name. It also can be empty with same case. Then, the count of remain columns is variables after forth item (inclusive). All of them is the alias of this standard PyCodec name.
|
54
script/pycodec/conv_encoding_table.py
Normal file
54
script/pycodec/conv_encoding_table.py
Normal file
@ -0,0 +1,54 @@
|
||||
import csv
|
||||
from pathlib import Path
|
||||
import jinja2
|
||||
|
||||
|
||||
class LanguageToken:
|
||||
name: str
|
||||
alias: tuple[str, ...]
|
||||
code_page: str | None
|
||||
iconv_code: str | None
|
||||
|
||||
def __init__(self, row: list[str]):
|
||||
"""Init language token from CSV row."""
|
||||
self.name = row[0].lower()
|
||||
code_page = row[1]
|
||||
self.code_page = None if len(code_page) == 0 else code_page
|
||||
iconv_code = row[2]
|
||||
self.iconv_code = None if len(iconv_code) == 0 else iconv_code
|
||||
# For alias, we strip and to lower them first, and remove all empty entries
|
||||
alias = row[3:]
|
||||
self.alias = tuple(
|
||||
filter(lambda x: len(x) != 0,
|
||||
map(lambda x: x.strip().lower(), alias)))
|
||||
|
||||
|
||||
def _get_self_dir() -> Path:
|
||||
return Path(__file__).resolve().parent
|
||||
|
||||
|
||||
def _extract_tokens() -> list[LanguageToken]:
|
||||
rv: list[LanguageToken] = []
|
||||
csv_file = _get_self_dir() / 'encoding_table.csv'
|
||||
|
||||
with open(csv_file, 'r', encoding='utf-8', newline='') as f:
|
||||
reader = csv.reader(f, delimiter='\t')
|
||||
for row in reader:
|
||||
rv.append(LanguageToken(row))
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
def _render_cpp(tokens: list[LanguageToken]) -> None:
|
||||
loader = jinja2.FileSystemLoader(_get_self_dir())
|
||||
environment = jinja2.Environment(loader=loader)
|
||||
template = environment.get_template('encoding_table.cpp.jinja')
|
||||
|
||||
cpp_file = _get_self_dir() / 'encoding_table.cpp'
|
||||
with open(cpp_file, 'w', encoding='utf-8') as f:
|
||||
f.write(template.render(tokens=tokens))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
tokens = _extract_tokens()
|
||||
_render_cpp(tokens)
|
23
script/pycodec/encoding_table.cpp.jinja
Normal file
23
script/pycodec/encoding_table.cpp.jinja
Normal file
@ -0,0 +1,23 @@
|
||||
static const std::map<std::u8string_view, std::u8string_view> ALIAS_MAP {
|
||||
{% for token in tokens -%}
|
||||
{% for alias in token.alias -%}
|
||||
{ u8"{{ alias }}"sv, u8"{{ token.name }}"sv },
|
||||
{% endfor -%}
|
||||
{% endfor -%}
|
||||
};
|
||||
|
||||
static const std::map<std::u8string_view, CodePage> WINCP_MAP {
|
||||
{% for token in tokens -%}
|
||||
{% if token.code_page is not none -%}
|
||||
{ u8"{{ token.name }}"sv, static_cast<CodePage>({{ token.code_page }}u) },
|
||||
{% endif -%}
|
||||
{% endfor -%}
|
||||
};
|
||||
|
||||
static const std::map<std::u8string_view, std::string_view> ICONV_MAP {
|
||||
{% for token in tokens -%}
|
||||
{% if token.iconv_code is not none -%}
|
||||
{ u8"{{ token.name }}"sv, "{{ token.iconv_code }}"sv },
|
||||
{% endif -%}
|
||||
{% endfor -%}
|
||||
};
|
97
script/pycodec/encoding_table.csv
Normal file
97
script/pycodec/encoding_table.csv
Normal file
@ -0,0 +1,97 @@
|
||||
ascii 437 ASCII 646 us-ascii
|
||||
big5 950 BIG5 big5-tw csbig5
|
||||
big5hkscs BIG5-HKSCS big5-hkscs hkscs
|
||||
cp037 037 IBM037 IBM039
|
||||
cp273 273 IBM273 csIBM273
|
||||
cp424 EBCDIC-CP-HE IBM424
|
||||
cp437 437 437 IBM437
|
||||
cp500 500 EBCDIC-CP-BE EBCDIC-CP-CH IBM500
|
||||
cp720 720
|
||||
cp737 737
|
||||
cp775 775 IBM775
|
||||
cp850 850 CP850 850 IBM850
|
||||
cp852 852 852 IBM852
|
||||
cp855 855 855 IBM855
|
||||
cp856
|
||||
cp857 857 857 IBM857
|
||||
cp858 858 858 IBM858
|
||||
cp860 860 860 IBM860
|
||||
cp861 861 861 CP-IS IBM861
|
||||
cp862 862 CP862 862 IBM862
|
||||
cp863 863 863 IBM863
|
||||
cp864 864 IBM864
|
||||
cp865 865 865 IBM865
|
||||
cp866 866 CP866 866 IBM866
|
||||
cp869 869 869 CP-GR IBM869
|
||||
cp874 874 CP874
|
||||
cp875 875
|
||||
cp932 932 CP932 932 ms932 mskanji ms-kanji windows-31j
|
||||
cp949 949 CP949 949 ms949 uhc
|
||||
cp950 950 CP950 950 ms950
|
||||
cp1006
|
||||
cp1026 1026 ibm1026
|
||||
cp1125 1125 ibm1125 cp866u ruscii
|
||||
cp1140 1140 ibm1140
|
||||
cp1250 1250 CP1250 windows-1250
|
||||
cp1251 1251 CP1251 windows-1251
|
||||
cp1252 1252 CP1252 windows-1252
|
||||
cp1253 1253 CP1253 windows-1253
|
||||
cp1254 1254 CP1254 windows-1254
|
||||
cp1255 1255 CP1255 windows-1255
|
||||
cp1256 1256 CP1256 windows-1256
|
||||
cp1257 1257 CP1257 windows-1257
|
||||
cp1258 1258 CP1258 windows-1258
|
||||
euc_jp 20932 EUC-JP eucjp ujis u-jis
|
||||
euc_jis_2004 jisx0213 eucjis2004
|
||||
euc_jisx0213 eucjisx0213
|
||||
euc_kr 51949 EUC-KR euckr korean ksc5601 ks_c-5601 ks_c-5601-1987 ksx1001 ks_x-1001
|
||||
gb2312 936 CP936 chinese csiso58gb231280 euc-cn euccn eucgb2312-cn gb2312-1980 gb2312-80 iso-ir-58
|
||||
gbk 936 GBK 936 cp936 ms936
|
||||
gb18030 54936 GB18030 gb18030-2000
|
||||
hz 52936 HZ hzgb hz-gb hz-gb-2312
|
||||
iso2022_jp 50220 ISO-2022-JP csiso2022jp iso2022jp iso-2022-jp
|
||||
iso2022_jp_1 ISO-2022-JP-1 iso2022jp-1 iso-2022-jp-1
|
||||
iso2022_jp_2 ISO-2022-JP-2 iso2022jp-2 iso-2022-jp-2
|
||||
iso2022_jp_2004 iso2022jp-2004 iso-2022-jp-2004
|
||||
iso2022_jp_3 iso2022jp-3 iso-2022-jp-3
|
||||
iso2022_jp_ext iso2022jp-ext iso-2022-jp-ext
|
||||
iso2022_kr 50225 ISO-2022-KR csiso2022kr iso2022kr iso-2022-kr
|
||||
latin_1 28591 ISO-8859-1 iso-8859-1 iso8859-1 8859 cp819 latin latin1 L1
|
||||
iso8859_2 28592 ISO-8859-2 iso-8859-2 latin2 L2
|
||||
iso8859_3 28593 ISO-8859-3 iso-8859-3 latin3 L3
|
||||
iso8859_4 28594 ISO-8859-4 iso-8859-4 latin4 L4
|
||||
iso8859_5 28595 ISO-8859-5 iso-8859-5 cyrillic
|
||||
iso8859_6 28596 ISO-8859-6 iso-8859-6 arabic
|
||||
iso8859_7 28597 ISO-8859-7 iso-8859-7 greek greek8
|
||||
iso8859_8 28598 ISO-8859-8 iso-8859-8 hebrew
|
||||
iso8859_9 28599 ISO-8859-9 iso-8859-9 latin5 L5
|
||||
iso8859_10 ISO-8859-10 iso-8859-10 latin6 L6
|
||||
iso8859_11 ISO-8859-11 iso-8859-11 thai
|
||||
iso8859_13 28603 ISO-8859-13 iso-8859-13 latin7 L7
|
||||
iso8859_14 ISO-8859-14 iso-8859-14 latin8 L8
|
||||
iso8859_15 28605 ISO-8859-15 iso-8859-15 latin9 L9
|
||||
iso8859_16 ISO-8859-16 iso-8859-16 latin10 L10
|
||||
johab 1361 JOHAB cp1361 ms1361
|
||||
koi8_r
|
||||
koi8_t KOI8-T
|
||||
koi8_u
|
||||
kz1048 kz_1048 strk1048_2002 rk1048
|
||||
mac_cyrillic 10007 MacCyrillic maccyrillic
|
||||
mac_greek 10006 MacGreek macgreek
|
||||
mac_iceland 10079 MacIceland maciceland
|
||||
mac_latin2 maclatin2 maccentraleurope mac_centeuro
|
||||
mac_roman MacRoman macroman macintosh
|
||||
mac_turkish 10081 MacTurkish macturkish
|
||||
ptcp154 PT154 csptcp154 pt154 cp154 cyrillic-asian
|
||||
shift_jis 932 SHIFT_JIS csshiftjis shiftjis sjis s_jis
|
||||
shift_jis_2004 shiftjis2004 sjis_2004 sjis2004
|
||||
shift_jisx0213 shiftjisx0213 sjisx0213 s_jisx0213
|
||||
utf_32 UTF-32 U32 utf32
|
||||
utf_32_be UTF-32BE UTF-32BE
|
||||
utf_32_le UTF-32LE UTF-32LE
|
||||
utf_16 UTF16 U16 utf16
|
||||
utf_16_be UTF-16BE UTF-16BE
|
||||
utf_16_le UTF-16LE UTF-16LE
|
||||
utf_7 65000 UTF-7 U7 unicode-1-1-utf-7
|
||||
utf_8 65001 UTF-8 U8 UTF utf8 utf-8 cp65001
|
||||
utf_8_sig
|
|
7
script/pyproject.toml
Normal file
7
script/pyproject.toml
Normal file
@ -0,0 +1,7 @@
|
||||
[project]
|
||||
name = "script"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.11"
|
||||
dependencies = [
|
||||
"jinja2==3.1.2",
|
||||
]
|
74
script/uv.lock
generated
Normal file
74
script/uv.lock
generated
Normal file
@ -0,0 +1,74 @@
|
||||
version = 1
|
||||
revision = 2
|
||||
requires-python = ">=3.11"
|
||||
|
||||
[[package]]
|
||||
name = "jinja2"
|
||||
version = "3.1.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "markupsafe" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7a/ff/75c28576a1d900e87eb6335b063fab47a8ef3c8b4d88524c4bf78f670cce/Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852", size = 268239, upload-time = "2022-04-28T17:21:27.579Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/c3/f068337a370801f372f2f8f6bad74a5c140f6fda3d9de154052708dd3c65/Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61", size = 133101, upload-time = "2022-04-28T17:21:25.336Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markupsafe"
|
||||
version = "3.0.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353, upload-time = "2024-10-18T15:21:02.187Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392, upload-time = "2024-10-18T15:21:02.941Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984, upload-time = "2024-10-18T15:21:03.953Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120, upload-time = "2024-10-18T15:21:06.495Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032, upload-time = "2024-10-18T15:21:07.295Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057, upload-time = "2024-10-18T15:21:08.073Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359, upload-time = "2024-10-18T15:21:09.318Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306, upload-time = "2024-10-18T15:21:10.185Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094, upload-time = "2024-10-18T15:21:11.005Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521, upload-time = "2024-10-18T15:21:12.911Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274, upload-time = "2024-10-18T15:21:13.777Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348, upload-time = "2024-10-18T15:21:14.822Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149, upload-time = "2024-10-18T15:21:15.642Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118, upload-time = "2024-10-18T15:21:17.133Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993, upload-time = "2024-10-18T15:21:18.064Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178, upload-time = "2024-10-18T15:21:18.859Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319, upload-time = "2024-10-18T15:21:19.671Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352, upload-time = "2024-10-18T15:21:20.971Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097, upload-time = "2024-10-18T15:21:22.646Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601, upload-time = "2024-10-18T15:21:23.499Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "script"
|
||||
version = "0.1.0"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "jinja2" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [{ name = "jinja2", specifier = "==3.1.2" }]
|
@ -13,25 +13,23 @@ PRIVATE
|
||||
# Sources
|
||||
yycc/string/reinterpret.cpp
|
||||
yycc/string/op.cpp
|
||||
yycc/string/stream.cpp
|
||||
yycc/patch/fopen.cpp
|
||||
yycc/rust/panic.cpp
|
||||
yycc/patch/path.cpp
|
||||
yycc/encoding/utf.cpp
|
||||
yycc/rust/env.cpp
|
||||
yycc/windows/com.cpp
|
||||
yycc/windows/dialog.cpp
|
||||
yycc/windows/winfct.cpp
|
||||
yycc/windows/console.cpp
|
||||
yycc/encoding/stl.cpp
|
||||
yycc/encoding/windows.cpp
|
||||
yycc/encoding/iconv.cpp
|
||||
yycc/encoding/united_codec.cpp
|
||||
# YYCC/COMHelper.cpp
|
||||
# YYCC/ArgParser.cpp
|
||||
# YYCC/ConfigManager.cpp
|
||||
# YYCC/ConsoleHelper.cpp
|
||||
# YYCC/DialogHelper.cpp
|
||||
# YYCC/EncodingHelper.cpp
|
||||
# YYCC/ExceptionHelper.cpp
|
||||
# YYCC/StdPatch.cpp
|
||||
# YYCC/IOHelper.cpp
|
||||
# YYCC/StringHelper.cpp
|
||||
# YYCC/WinFctHelper.cpp
|
||||
# # Natvis (only for MSVC)
|
||||
# $<$<CXX_COMPILER_ID:MSVC>:YYCC.natvis>
|
||||
|
||||
yycc/carton/pycodec.cpp
|
||||
yycc/carton/termcolor.cpp
|
||||
yycc/carton/wcwidth.cpp
|
||||
yycc/carton/tabulate.cpp
|
||||
yycc/carton/csconsole.cpp
|
||||
)
|
||||
target_sources(YYCCommonplace
|
||||
PUBLIC
|
||||
@ -40,60 +38,47 @@ FILES
|
||||
# Headers
|
||||
yycc.hpp
|
||||
yycc/version.hpp
|
||||
yycc/prelude/core.hpp
|
||||
yycc/prelude/rust.hpp
|
||||
yycc/macro/version_cmp.hpp
|
||||
yycc/macro/feature_probe.hpp
|
||||
yycc/macro/os_detector.hpp
|
||||
yycc/macro/stl_detector.hpp
|
||||
yycc/macro/endian_detector.hpp
|
||||
yycc/macro/compiler_detector.hpp
|
||||
yycc/macro/ptr_size_detector.hpp
|
||||
yycc/macro/class_copy_move.hpp
|
||||
yycc/string.hpp
|
||||
yycc/flag_enum.hpp
|
||||
yycc/string/reinterpret.hpp
|
||||
yycc/string/op.hpp
|
||||
yycc/string/stream.hpp
|
||||
yycc/patch/ptr_pad.hpp
|
||||
yycc/patch/fopen.hpp
|
||||
yycc/num/parse.hpp
|
||||
yycc/num/stringify.hpp
|
||||
yycc/num/safe_cast.hpp
|
||||
yycc/num/safe_op.hpp
|
||||
yycc/num/op.hpp
|
||||
yycc/rust/prelude.hpp
|
||||
yycc/rust/primitive.hpp
|
||||
yycc/rust/panic.hpp
|
||||
yycc/rust/option.hpp
|
||||
yycc/rust/result.hpp
|
||||
yycc/rust/num/parse.hpp
|
||||
yycc/rust/num/stringify.hpp
|
||||
yycc/windows/unsafe_suppressor.hpp
|
||||
yycc/rust/env.hpp
|
||||
yycc/windows/import_guard_head.hpp
|
||||
yycc/windows/import_guard_tail.hpp
|
||||
yycc/windows/com.hpp
|
||||
yycc/windows/dialog.hpp
|
||||
yycc/windows/winfct.hpp
|
||||
yycc/windows/console.hpp
|
||||
yycc/constraint.hpp
|
||||
yycc/constraint/builder.hpp
|
||||
yycc/patch/path.hpp
|
||||
yycc/patch/contains.hpp
|
||||
yycc/patch/starts_ends_with.hpp
|
||||
yycc/encoding/utf.hpp
|
||||
yycc/encoding/stl.hpp
|
||||
yycc/encoding/windows.hpp
|
||||
yycc/encoding/iconv.hpp
|
||||
yycc/encoding/united_codec.hpp
|
||||
|
||||
# # Headers
|
||||
# # Common headers
|
||||
# YYCC/Constraints.hpp
|
||||
# YYCC/COMHelper.hpp
|
||||
# YYCC/ArgParser.hpp
|
||||
# YYCC/ConfigManager.hpp
|
||||
# YYCC/ConsoleHelper.hpp
|
||||
# YYCC/DialogHelper.hpp
|
||||
# YYCC/EncodingHelper.hpp
|
||||
# YYCC/EnumHelper.hpp
|
||||
# YYCC/ExceptionHelper.hpp
|
||||
# YYCC/StdPatch.hpp
|
||||
# YYCC/IOHelper.hpp
|
||||
# YYCC/ParserHelper.hpp
|
||||
# YYCC/StringHelper.hpp
|
||||
# YYCC/WinFctHelper.hpp
|
||||
# # Windows including guard pair
|
||||
# YYCC/WinImportPrefix.hpp
|
||||
# YYCC/WinImportSuffix.hpp
|
||||
# # Internal
|
||||
# YYCC/YYCCVersion.hpp
|
||||
# YYCC/YYCCInternal.hpp
|
||||
# # Exposed
|
||||
# YYCCommonplace.hpp
|
||||
yycc/carton/pycodec.hpp
|
||||
yycc/carton/termcolor.hpp
|
||||
yycc/carton/wcwidth.hpp
|
||||
yycc/carton/tabulate.hpp
|
||||
yycc/carton/csconsole.hpp
|
||||
)
|
||||
# Setup header infomations
|
||||
target_include_directories(YYCCommonplace
|
||||
@ -101,28 +86,59 @@ PUBLIC
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>"
|
||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
||||
)
|
||||
# Link Iconv if we have import it
|
||||
if (Iconv_FOUND)
|
||||
target_link_libraries(YYCCommonplace
|
||||
PRIVATE
|
||||
Iconv::Iconv
|
||||
)
|
||||
endif ()
|
||||
# Link with DbgHelp.lib on Windows
|
||||
target_link_libraries(YYCCommonplace
|
||||
PRIVATE
|
||||
$<$<BOOL:${WIN32}>:DbgHelp.lib>
|
||||
)
|
||||
# Setup C++ standard
|
||||
target_compile_features(YYCCommonplace PUBLIC cxx_std_17)
|
||||
set_target_properties(YYCCommonplace PROPERTIES CXX_EXTENSION OFF)
|
||||
# Setup macros
|
||||
target_compile_definitions(YYCCommonplace
|
||||
# Debug macro should populate to child projects
|
||||
PUBLIC
|
||||
$<$<BOOL:${YYCC_DEBUG_UE_FILTER}>:YYCC_DEBUG_UE_FILTER>
|
||||
# Unicode charset for private using
|
||||
PRIVATE
|
||||
# Iconv environment macro
|
||||
$<$<BOOL:${Iconv_FOUND}>:YYCC_FEAT_ICONV>
|
||||
# OS macro
|
||||
$<$<PLATFORM_ID:Windows>:YYCC_OS_WINDOWS>
|
||||
$<$<PLATFORM_ID:Linux>:YYCC_OS_LINUX>
|
||||
$<$<PLATFORM_ID:Android>:YYCC_OS_LINUX> # We brutally think Android as Linux.
|
||||
$<$<PLATFORM_ID:Darwin>:YYCC_OS_MACOS>
|
||||
$<$<PLATFORM_ID:iOS>:YYCC_OS_MACOS> # We brutally think iOS as macOS.
|
||||
# Compiler macro
|
||||
$<$<CXX_COMPILER_ID:GNU>:YYCC_CC_GCC>
|
||||
$<$<CXX_COMPILER_ID:Clang>:YYCC_CC_CLANG>
|
||||
$<$<CXX_COMPILER_ID:MSVC>:YYCC_CC_MSVC>
|
||||
# Endian macro
|
||||
$<$<STREQUAL:${CMAKE_CXX_BYTE_ORDER},LITTLE_ENDIAN>:YYCC_ENDIAN_LITTLE>
|
||||
$<$<STREQUAL:${CMAKE_CXX_BYTE_ORDER},BIG_ENDIAN>:YYCC_ENDIAN_BIG>
|
||||
# Pointer size macro
|
||||
$<$<EQUAL:${CMAKE_SIZEOF_VOID_P},4>:YYCC_PTRSIZE_32>
|
||||
$<$<EQUAL:${CMAKE_SIZEOF_VOID_P},8>:YYCC_PTRSIZE_64>
|
||||
# Use Unicode charset on MSVC
|
||||
$<$<CXX_COMPILER_ID:MSVC>:UNICODE>
|
||||
$<$<CXX_COMPILER_ID:MSVC>:_UNICODE>
|
||||
# Fix MSVC shit
|
||||
$<$<CXX_COMPILER_ID:MSVC>:_CRT_SECURE_NO_WARNINGS>
|
||||
$<$<CXX_COMPILER_ID:MSVC>:_CRT_SECURE_NO_DEPRECATE>
|
||||
$<$<CXX_COMPILER_ID:MSVC>:_CRT_NONSTDC_NO_WARNINGS>
|
||||
$<$<CXX_COMPILER_ID:MSVC>:_CRT_NONSTDC_NO_DEPRECATE>
|
||||
# Fix Windows header file shit
|
||||
$<$<BOOL:${WIN32}>:WIN32_LEAN_AND_MEAN>
|
||||
$<$<BOOL:${WIN32}>:NOMINMAX>
|
||||
)
|
||||
target_compile_options(YYCCommonplace
|
||||
# Order build as UTF-8 in MSVC
|
||||
PRIVATE
|
||||
PUBLIC
|
||||
# Order build as UTF-8 in MSVC
|
||||
$<$<CXX_COMPILER_ID:MSVC>:/utf-8>
|
||||
# Order preprocessor conformance mode (fix __VA_OPT__ error in MSVC)
|
||||
$<$<CXX_COMPILER_ID:MSVC>:/Zc:preprocessor>
|
||||
# Resolve MSVC __cplusplus macro value error.
|
||||
$<$<CXX_COMPILER_ID:MSVC>:/Zc:__cplusplus>
|
||||
)
|
||||
|
||||
# Install binary and headers
|
||||
|
@ -1,73 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (c) Microsoft Corporation, yyc12345.
|
||||
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
-->
|
||||
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
|
||||
<!-- Following XML are copied from Visual Studio embedded Natvis files. -->
|
||||
<!-- <Microsoft Visual Studio Install Directory>\Common7\Packages\Debugger\Visualizers\stl.natvis -->
|
||||
|
||||
<!-- VC 2013 -->
|
||||
<Type Name="std::basic_string<YYCC::yycc_char8_t,*>" Priority="MediumLow">
|
||||
<AlternativeType Name="std::basic_string<char8_t,*>" />
|
||||
<AlternativeType Name="std::basic_string<unsigned char,*>" />
|
||||
<DisplayString Condition="_Myres < _BUF_SIZE">{_Bx._Buf,s8}</DisplayString>
|
||||
<DisplayString Condition="_Myres >= _BUF_SIZE">{_Bx._Ptr,s8}</DisplayString>
|
||||
<StringView Condition="_Myres < _BUF_SIZE">_Bx._Buf,s8</StringView>
|
||||
<StringView Condition="_Myres >= _BUF_SIZE">_Bx._Ptr,s8</StringView>
|
||||
<Expand>
|
||||
<Item Name="[size]" ExcludeView="simple">_Mysize</Item>
|
||||
<Item Name="[capacity]" ExcludeView="simple">_Myres</Item>
|
||||
<ArrayItems>
|
||||
<Size>_Mysize</Size>
|
||||
<ValuePointer Condition="_Myres < _BUF_SIZE">_Bx._Buf</ValuePointer>
|
||||
<ValuePointer Condition="_Myres >= _BUF_SIZE">_Bx._Ptr</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<!-- VC 2015+ ABI basic_string -->
|
||||
<Type Name="std::basic_string<YYCC::yycc_char8_t,*>">
|
||||
<AlternativeType Name="std::basic_string<char8_t,*>" />
|
||||
<AlternativeType Name="std::basic_string<unsigned char,*>" />
|
||||
<Intrinsic Name="size" Expression="_Mypair._Myval2._Mysize" />
|
||||
<Intrinsic Name="capacity" Expression="_Mypair._Myval2._Myres" />
|
||||
<!-- _BUF_SIZE = 16 / sizeof(char) < 1 ? 1 : 16 / sizeof(char) == 16 -->
|
||||
<Intrinsic Name="bufSize" Expression="16" />
|
||||
<Intrinsic Name="isShortString" Expression="capacity() < bufSize()" />
|
||||
<Intrinsic Name="isLongString" Expression="capacity() >= bufSize()" />
|
||||
<DisplayString Condition="isShortString()">{_Mypair._Myval2._Bx._Buf,s8}</DisplayString>
|
||||
<DisplayString Condition="isLongString()">{_Mypair._Myval2._Bx._Ptr,s8}</DisplayString>
|
||||
<StringView Condition="isShortString()">_Mypair._Myval2._Bx._Buf,s8</StringView>
|
||||
<StringView Condition="isLongString()">_Mypair._Myval2._Bx._Ptr,s8</StringView>
|
||||
<Expand>
|
||||
<Item Name="[size]" ExcludeView="simple">size()</Item>
|
||||
<Item Name="[capacity]" ExcludeView="simple">capacity()</Item>
|
||||
<Item Name="[allocator]" ExcludeView="simple">_Mypair</Item>
|
||||
<ArrayItems>
|
||||
<Size>_Mypair._Myval2._Mysize</Size>
|
||||
<ValuePointer Condition="isShortString()">_Mypair._Myval2._Bx._Buf</ValuePointer>
|
||||
<ValuePointer Condition="isLongString()">_Mypair._Myval2._Bx._Ptr</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="std::basic_string_view<YYCC::yycc_char8_t,*>">
|
||||
<AlternativeType Name="std::basic_string_view<char8_t,*>" />
|
||||
<AlternativeType Name="std::basic_string_view<unsigned char,*>" />
|
||||
<Intrinsic Name="size" Expression="_Mysize" />
|
||||
<Intrinsic Name="data" Expression="_Mydata" />
|
||||
<DisplayString>{_Mydata,[_Mysize],s8}</DisplayString>
|
||||
<StringView>_Mydata,[_Mysize],s8</StringView>
|
||||
<Expand>
|
||||
<Item Name="[size]" ExcludeView="simple">size()</Item>
|
||||
<ArrayItems>
|
||||
<Size>size()</Size>
|
||||
<ValuePointer>data()</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
</AutoVisualizer>
|
@ -3,7 +3,7 @@
|
||||
#include "EncodingHelper.hpp"
|
||||
#include "ConsoleHelper.hpp"
|
||||
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
#include "WinImportPrefix.hpp"
|
||||
#include <Windows.h>
|
||||
#include <shellapi.h>
|
||||
@ -24,7 +24,7 @@ namespace YYCC::ArgParser {
|
||||
return ArgumentList(std::move(args));
|
||||
}
|
||||
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
ArgumentList ArgumentList::CreateFromWin32() {
|
||||
// Reference: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw
|
||||
|
||||
|
@ -37,7 +37,7 @@ namespace YYCC::ArgParser {
|
||||
* and should not be seen as a part of arguments.
|
||||
*/
|
||||
static ArgumentList CreateFromStd(int argc, char* argv[]);
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
/**
|
||||
* @brief Create argument list from Win32 function.
|
||||
* @details
|
||||
|
@ -1,48 +0,0 @@
|
||||
#include "COMHelper.hpp"
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
|
||||
namespace YYCC::COMHelper {
|
||||
|
||||
/**
|
||||
* @brief The guard for initialize COM environment.
|
||||
* @details This class will try initializing COM environment by calling CoInitialize when constructing,
|
||||
* and it also will try uninitializing COM environment when destructing.
|
||||
* If initialization failed, uninitialization will not be executed.
|
||||
*/
|
||||
class ComGuard {
|
||||
public:
|
||||
ComGuard() : m_HasInit(false) {
|
||||
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
|
||||
if (SUCCEEDED(hr)) m_HasInit = true;
|
||||
}
|
||||
~ComGuard() {
|
||||
if (m_HasInit) {
|
||||
CoUninitialize();
|
||||
}
|
||||
}
|
||||
|
||||
bool IsInitialized() const {
|
||||
return m_HasInit;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool m_HasInit;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The instance of COM environment guard.
|
||||
* @details Dialog related function need COM environment,
|
||||
* so we need initializing COM environment when loading this module,
|
||||
* and uninitializing COM environment when we no longer use this module.
|
||||
* So we use a static instance in here.
|
||||
* And make it be const so no one can change it.
|
||||
*/
|
||||
static const ComGuard c_ComGuard {};
|
||||
|
||||
bool IsInitialized() {
|
||||
return c_ComGuard.IsInitialized();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,71 +0,0 @@
|
||||
#pragma once
|
||||
#include "YYCCInternal.hpp"
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "WinImportPrefix.hpp"
|
||||
#include <Windows.h>
|
||||
#include <shlobj_core.h>
|
||||
#include "WinImportSuffix.hpp"
|
||||
|
||||
/**
|
||||
* @brief Windows COM related types and checker.
|
||||
* @details
|
||||
* This namespace is Windows specific.
|
||||
* In other platforms, this whole namespace will be unavailable.
|
||||
*
|
||||
* See also \ref com_helper.
|
||||
*/
|
||||
namespace YYCC::COMHelper {
|
||||
|
||||
/// @brief C++ standard deleter for every COM interfaces inheriting IUnknown.
|
||||
class ComPtrDeleter {
|
||||
public:
|
||||
ComPtrDeleter() {}
|
||||
void operator() (IUnknown* com_ptr) {
|
||||
if (com_ptr != nullptr) {
|
||||
com_ptr->Release();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// @brief Smart unique pointer of \c IFileDialog
|
||||
using SmartIFileDialog = std::unique_ptr<IFileDialog, ComPtrDeleter>;
|
||||
/// @brief Smart unique pointer of \c IFileOpenDialog
|
||||
using SmartIFileOpenDialog = std::unique_ptr<IFileOpenDialog, ComPtrDeleter>;
|
||||
/// @brief Smart unique pointer of \c IShellItem
|
||||
using SmartIShellItem = std::unique_ptr<IShellItem, ComPtrDeleter>;
|
||||
/// @brief Smart unique pointer of \c IShellItemArray
|
||||
using SmartIShellItemArray = std::unique_ptr<IShellItemArray, ComPtrDeleter>;
|
||||
/// @brief Smart unique pointer of \c IShellFolder
|
||||
using SmartIShellFolder = std::unique_ptr<IShellFolder, ComPtrDeleter>;
|
||||
|
||||
/// @brief C++ standard deleter for almost raw pointer used in COM which need to be free by CoTaskMemFree()
|
||||
class CoTaskMemDeleter {
|
||||
public:
|
||||
CoTaskMemDeleter() {}
|
||||
void operator() (void* com_ptr) {
|
||||
if (com_ptr != nullptr) {
|
||||
CoTaskMemFree(com_ptr);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// @brief Smart unique pointer of COM created \c WCHAR sequence.
|
||||
using SmartLPWSTR = std::unique_ptr<std::remove_pointer_t<LPWSTR>, CoTaskMemDeleter>;
|
||||
|
||||
/**
|
||||
* @brief Check whether COM environment has been initialized.
|
||||
* @return True if it is, otherwise false.
|
||||
* @remarks
|
||||
* This function will call corresponding function of COM Guard.
|
||||
* Do not remove this function and you must preserve at least one reference to this function in final program.
|
||||
* Some compiler will try to drop COM Guard in final program if no reference to it and it will cause the initialization of COM environment failed.
|
||||
* This is the reason why I order you do the things said above.
|
||||
*/
|
||||
bool IsInitialized();
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,282 +0,0 @@
|
||||
#include "ConsoleHelper.hpp"
|
||||
|
||||
#include "EncodingHelper.hpp"
|
||||
#include "StringHelper.hpp"
|
||||
#include <iostream>
|
||||
|
||||
// Include Windows used headers in Windows.
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
#include "WinImportPrefix.hpp"
|
||||
#include <Windows.h>
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
#include "WinImportSuffix.hpp"
|
||||
#endif
|
||||
|
||||
namespace YYCC::ConsoleHelper {
|
||||
|
||||
#pragma region Windows Specific Functions
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
|
||||
static bool RawEnableColorfulConsole(FILE* fs) {
|
||||
if (!_isatty(_fileno(fs))) return false;
|
||||
|
||||
HANDLE h_output;
|
||||
DWORD dw_mode;
|
||||
|
||||
h_output = (HANDLE)_get_osfhandle(_fileno(fs));
|
||||
if (!GetConsoleMode(h_output, &dw_mode)) return false;
|
||||
if (!SetConsoleMode(h_output, dw_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_PROCESSED_OUTPUT)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
Reference:
|
||||
* https://stackoverflow.com/questions/45575863/how-to-print-utf-8-strings-to-stdcout-on-windows
|
||||
* https://stackoverflow.com/questions/69830460/reading-utf-8-input
|
||||
|
||||
There is 3 way to make Windows console enable UTF8 mode.
|
||||
|
||||
First one is calling SetConsoleCP and SetConsoleOutputCP.
|
||||
The side effect of this is std::cin and std::cout is broken,
|
||||
however there is a patch for this issue.
|
||||
|
||||
Second one is calling _set_mode with _O_U8TEXT or _O_U16TEXT to enable Unicode mode for Windows console.
|
||||
This also have side effect which is stronger than first one.
|
||||
All puts family functions (ASCII-based output functions) will throw assertion exception.
|
||||
You only can use putws family functions (wide-char-based output functions).
|
||||
However these functions can not be used without calling _set_mode in Windows design.
|
||||
|
||||
There still is another method, using WriteConsoleW directly visiting console.
|
||||
This function family can output correct string without calling any extra functions!
|
||||
This method is what we adopted.
|
||||
*/
|
||||
|
||||
template<bool _bIsConsole>
|
||||
static yycc_u8string WinConsoleRead(HANDLE hStdIn) {
|
||||
using _TChar = std::conditional_t<_bIsConsole, wchar_t, char>;
|
||||
|
||||
// Prepare an internal buffer because the read data may not be fully used.
|
||||
// For example, we may read x\ny in a single calling but after processing \n, this function will return
|
||||
// so y will temporarily stored in this internal buffer for next using.
|
||||
// Thus this function is not thread safe.
|
||||
static std::basic_string<_TChar> internal_buffer;
|
||||
// create return value buffer
|
||||
std::basic_string<_TChar> return_buffer;
|
||||
|
||||
// Prepare some variables
|
||||
DWORD dwReadNumberOfChars;
|
||||
_TChar szReadChars[64];
|
||||
size_t eol_pos;
|
||||
|
||||
// try fetching EOL
|
||||
while (true) {
|
||||
// if internal buffer is empty,
|
||||
// try fetching it.
|
||||
if (internal_buffer.empty()) {
|
||||
// console and non-console use different method to read.
|
||||
if constexpr (_bIsConsole) {
|
||||
// console handle, use ReadConsoleW.
|
||||
// read from console, the read data is wchar based
|
||||
if (!ReadConsoleW(hStdIn, szReadChars, sizeof(szReadChars) / sizeof(_TChar), &dwReadNumberOfChars, NULL))
|
||||
break;
|
||||
} else {
|
||||
// anything else, use ReadFile instead.
|
||||
// the read data is utf8 based
|
||||
if (!ReadFile(hStdIn, szReadChars, sizeof(szReadChars), &dwReadNumberOfChars, NULL))
|
||||
break;
|
||||
}
|
||||
|
||||
// send to internal buffer
|
||||
if (dwReadNumberOfChars == 0) break;
|
||||
internal_buffer.append(szReadChars, dwReadNumberOfChars);
|
||||
}
|
||||
|
||||
// try finding EOL in internal buffer
|
||||
if constexpr (std::is_same_v<_TChar, char>) eol_pos = internal_buffer.find_first_of('\n');
|
||||
else eol_pos = internal_buffer.find_first_of(L'\n');
|
||||
// check finding result
|
||||
if (eol_pos == std::wstring::npos) {
|
||||
// the whole string do not include EOL, fully appended to return value
|
||||
return_buffer += internal_buffer;
|
||||
internal_buffer.clear();
|
||||
// need more data, continue while
|
||||
} else {
|
||||
// split result
|
||||
// push into result and remain some in internal buffer.
|
||||
return_buffer.append(internal_buffer, 0u, eol_pos);
|
||||
internal_buffer.erase(0u, eol_pos + 1u); // +1 because EOL take one place.
|
||||
// break while mean success finding
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// post-process for return value
|
||||
yycc_u8string real_return_buffer;
|
||||
if constexpr (_bIsConsole) {
|
||||
// console mode need convert wchar to utf8
|
||||
YYCC::EncodingHelper::WcharToUTF8(return_buffer, real_return_buffer);
|
||||
} else {
|
||||
// non-console just copt the result
|
||||
real_return_buffer = EncodingHelper::ToUTF8(return_buffer);
|
||||
}
|
||||
// every mode need delete \r words
|
||||
YYCC::StringHelper::Replace(real_return_buffer, YYCC_U8("\r"), YYCC_U8(""));
|
||||
// return value
|
||||
return real_return_buffer;
|
||||
}
|
||||
|
||||
static void WinConsoleWrite(const yycc_u8string& strl, bool to_stderr) {
|
||||
// Prepare some Win32 variables
|
||||
// fetch stdout handle first
|
||||
HANDLE hStdOut = GetStdHandle(to_stderr ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE);
|
||||
DWORD dwConsoleMode;
|
||||
DWORD dwWrittenNumberOfChars;
|
||||
|
||||
// if stdout was redirected, this handle may point to a file handle or anything else,
|
||||
// WriteConsoleW can not write data into such scenario, so we need check whether this handle is console handle
|
||||
if (GetConsoleMode(hStdOut, &dwConsoleMode)) {
|
||||
// console handle, use WriteConsoleW.
|
||||
// convert utf8 string to wide char first
|
||||
std::wstring wstrl(YYCC::EncodingHelper::UTF8ToWchar(strl));
|
||||
size_t wstrl_size = wstrl.size();
|
||||
// write string with size check
|
||||
if (wstrl_size <= std::numeric_limits<DWORD>::max()) {
|
||||
WriteConsoleW(hStdOut, wstrl.c_str(), static_cast<DWORD>(wstrl_size), &dwWrittenNumberOfChars, NULL);
|
||||
}
|
||||
} else {
|
||||
// anything else, use WriteFile instead.
|
||||
// WriteFile do not need extra convertion, because it is direct writing.
|
||||
// check whether string length is overflow
|
||||
size_t strl_size = strl.size() * sizeof(yycc_u8string::value_type);
|
||||
// write string with size check
|
||||
if (strl_size <= std::numeric_limits<DWORD>::max()) {
|
||||
WriteFile(hStdOut, strl.c_str(), static_cast<DWORD>(strl_size), &dwWrittenNumberOfChars, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#pragma endregion
|
||||
|
||||
bool EnableColorfulConsole() {
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
|
||||
bool ret = true;
|
||||
ret &= RawEnableColorfulConsole(stdout);
|
||||
ret &= RawEnableColorfulConsole(stderr);
|
||||
return ret;
|
||||
|
||||
#else
|
||||
|
||||
// just return true and do nothing
|
||||
return true;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
yycc_u8string ReadLine() {
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
|
||||
// get stdin mode
|
||||
HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
|
||||
// use different method to get according to whether stdin is redirected
|
||||
DWORD dwConsoleMode;
|
||||
if (GetConsoleMode(hStdIn, &dwConsoleMode)) {
|
||||
return WinConsoleRead<true>(hStdIn);
|
||||
} else {
|
||||
return WinConsoleRead<false>(hStdIn);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// in linux, directly use C++ function to fetch.
|
||||
std::string cmd;
|
||||
if (std::getline(std::cin, cmd).fail()) cmd.clear();
|
||||
return EncodingHelper::ToUTF8(cmd);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
template<bool bNeedFmt, bool bIsErr, bool bHasEOL>
|
||||
static void RawWrite(const yycc_char8_t* u8_fmt, va_list argptr) {
|
||||
// Buiild string need to be written first
|
||||
// If no format string or plain string for writing, return.
|
||||
if (u8_fmt == nullptr) return;
|
||||
// Build or simply copy string
|
||||
yycc_u8string strl;
|
||||
if constexpr (bNeedFmt) {
|
||||
// treat as format string
|
||||
va_list argcpy;
|
||||
va_copy(argcpy, argptr);
|
||||
strl = YYCC::StringHelper::VPrintf(u8_fmt, argcpy);
|
||||
va_end(argcpy);
|
||||
} else {
|
||||
// treat as plain string
|
||||
strl = u8_fmt;
|
||||
}
|
||||
// Checkout whether add EOL
|
||||
if constexpr (bHasEOL) {
|
||||
strl += YYCC_U8("\n");
|
||||
}
|
||||
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
// call Windows specific writer
|
||||
WinConsoleWrite(strl, bIsErr);
|
||||
#else
|
||||
// in linux, directly use C function to write.
|
||||
std::fputs(EncodingHelper::ToOrdinary(strl.c_str()), bIsErr ? stderr : stdout);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Format(const yycc_char8_t* u8_fmt, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, u8_fmt);
|
||||
RawWrite<true, false, false>(u8_fmt, argptr);
|
||||
va_end(argptr);
|
||||
}
|
||||
|
||||
void FormatLine(const yycc_char8_t* u8_fmt, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, u8_fmt);
|
||||
RawWrite<true, false, true>(u8_fmt, argptr);
|
||||
va_end(argptr);
|
||||
}
|
||||
|
||||
void Write(const yycc_char8_t* u8_strl) {
|
||||
va_list empty{};
|
||||
RawWrite<false, false, false>(u8_strl, empty);
|
||||
}
|
||||
|
||||
void WriteLine(const yycc_char8_t* u8_strl) {
|
||||
va_list empty{};
|
||||
RawWrite<false, false, true>(u8_strl, empty);
|
||||
}
|
||||
|
||||
void ErrFormat(const yycc_char8_t* u8_fmt, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, u8_fmt);
|
||||
RawWrite<true, true, false>(u8_fmt, argptr);
|
||||
va_end(argptr);
|
||||
}
|
||||
|
||||
void ErrFormatLine(const yycc_char8_t* u8_fmt, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, u8_fmt);
|
||||
RawWrite<true, true, true>(u8_fmt, argptr);
|
||||
va_end(argptr);
|
||||
}
|
||||
|
||||
void ErrWrite(const yycc_char8_t* u8_strl) {
|
||||
va_list empty{};
|
||||
RawWrite<false, true, false>(u8_strl, empty);
|
||||
}
|
||||
|
||||
void ErrWriteLine(const yycc_char8_t* u8_strl) {
|
||||
va_list empty{};
|
||||
RawWrite<false, true, true>(u8_strl, empty);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,163 +0,0 @@
|
||||
#pragma once
|
||||
#include "YYCCInternal.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* @brief The helper providing universal C\# style console function and other console related stuff
|
||||
* @details
|
||||
* For how to utilize this functions provided by this namespace, please view \ref console_helper.
|
||||
*/
|
||||
namespace YYCC::ConsoleHelper {
|
||||
|
||||
/// @brief The head of ASCII escape code of black color.
|
||||
#define YYCC_COLORHDR_BLACK "\033[30m"
|
||||
/// @brief The head of ASCII escape code of red color.
|
||||
#define YYCC_COLORHDR_RED "\033[31m"
|
||||
/// @brief The head of ASCII escape code of green color.
|
||||
#define YYCC_COLORHDR_GREEN "\033[32m"
|
||||
/// @brief The head of ASCII escape code of yellow color.
|
||||
#define YYCC_COLORHDR_YELLOW "\033[33m"
|
||||
/// @brief The head of ASCII escape code of blue color.
|
||||
#define YYCC_COLORHDR_BLUE "\033[34m"
|
||||
/// @brief The head of ASCII escape code of magenta color.
|
||||
#define YYCC_COLORHDR_MAGENTA "\033[35m"
|
||||
/// @brief The head of ASCII escape code of cyan color.
|
||||
#define YYCC_COLORHDR_CYAN "\033[36m"
|
||||
/// @brief The head of ASCII escape code of white color.
|
||||
#define YYCC_COLORHDR_WHITE "\033[37m"
|
||||
|
||||
/// @brief The head of ASCII escape code of light black color.
|
||||
#define YYCC_COLORHDR_LIGHT_BLACK "\033[90m"
|
||||
/// @brief The head of ASCII escape code of light red color.
|
||||
#define YYCC_COLORHDR_LIGHT_RED "\033[91m"
|
||||
/// @brief The head of ASCII escape code of light green color.
|
||||
#define YYCC_COLORHDR_LIGHT_GREEN "\033[92m"
|
||||
/// @brief The head of ASCII escape code of light yellow color.
|
||||
#define YYCC_COLORHDR_LIGHT_YELLOW "\033[93m"
|
||||
/// @brief The head of ASCII escape code of light blue color.
|
||||
#define YYCC_COLORHDR_LIGHT_BLUE "\033[94m"
|
||||
/// @brief The head of ASCII escape code of light magenta color.
|
||||
#define YYCC_COLORHDR_LIGHT_MAGENTA "\033[95m"
|
||||
/// @brief The head of ASCII escape code of light cyan color.
|
||||
#define YYCC_COLORHDR_LIGHT_CYAN "\033[96m"
|
||||
/// @brief The head of ASCII escape code of light white color.
|
||||
#define YYCC_COLORHDR_LIGHT_WHITE "\033[97m"
|
||||
|
||||
/// @brief The tail of ASCII escape code of every color.
|
||||
#define YYCC_COLORTAIL "\033[0m"
|
||||
|
||||
/// @brief The ASCII escape code pair of black color.
|
||||
#define YYCC_COLOR_BLACK(T) "\033[30m" T "\033[0m"
|
||||
/// @brief The ASCII escape code pair of red color.
|
||||
#define YYCC_COLOR_RED(T) "\033[31m" T "\033[0m"
|
||||
/// @brief The ASCII escape code pair of green color.
|
||||
#define YYCC_COLOR_GREEN(T) "\033[32m" T "\033[0m"
|
||||
/// @brief The ASCII escape code pair of yellow color.
|
||||
#define YYCC_COLOR_YELLOW(T) "\033[33m" T "\033[0m"
|
||||
/// @brief The ASCII escape code pair of blue color.
|
||||
#define YYCC_COLOR_BLUE(T) "\033[34m" T "\033[0m"
|
||||
/// @brief The ASCII escape code pair of magenta color.
|
||||
#define YYCC_COLOR_MAGENTA(T) "\033[35m" T "\033[0m"
|
||||
/// @brief The ASCII escape code pair of cyan color.
|
||||
#define YYCC_COLOR_CYAN(T) "\033[36m" T "\033[0m"
|
||||
/// @brief The ASCII escape code pair of white color.
|
||||
#define YYCC_COLOR_WHITE(T) "\033[37m" T "\033[0m"
|
||||
|
||||
/// @brief The ASCII escape code pair of light black color.
|
||||
#define YYCC_COLOR_LIGHT_BLACK(T) "\033[90m" T "\033[0m"
|
||||
/// @brief The ASCII escape code pair of light red color.
|
||||
#define YYCC_COLOR_LIGHT_RED(T) "\033[91m" T "\033[0m"
|
||||
/// @brief The ASCII escape code pair of light green color.
|
||||
#define YYCC_COLOR_LIGHT_GREEN(T) "\033[92m" T "\033[0m"
|
||||
/// @brief The ASCII escape code pair of light yellow color.
|
||||
#define YYCC_COLOR_LIGHT_YELLOW(T) "\033[93m" T "\033[0m"
|
||||
/// @brief The ASCII escape code pair of light blue color.
|
||||
#define YYCC_COLOR_LIGHT_BLUE(T) "\033[94m" T "\033[0m"
|
||||
/// @brief The ASCII escape code pair of light magenta color.
|
||||
#define YYCC_COLOR_LIGHT_MAGENTA(T) "\033[95m" T "\033[0m"
|
||||
/// @brief The ASCII escape code pair of light cyan color.
|
||||
#define YYCC_COLOR_LIGHT_CYAN(T) "\033[96m" T "\033[0m"
|
||||
/// @brief The ASCII escape code pair of light white color.
|
||||
#define YYCC_COLOR_LIGHT_WHITE(T) "\033[97m" T "\033[0m"
|
||||
|
||||
/**
|
||||
* @brief Enable console color support for Windows.
|
||||
* @details This actually is enable virtual console feature for \c stdout and \c stderr.
|
||||
* @return True if success, otherwise false.
|
||||
* @remarks
|
||||
* This function only works on Windows and do nothing on other platforms such as Linux,
|
||||
* because we assume all terminals existing on other platform support color feature as default.
|
||||
*/
|
||||
bool EnableColorfulConsole();
|
||||
|
||||
/**
|
||||
* @brief Reads the next line of UTF8 characters from the standard input stream.
|
||||
* @return
|
||||
* The next line of UTF8 characters from the input stream.
|
||||
* Empty string if user just press Enter key or function failed.
|
||||
*/
|
||||
yycc_u8string ReadLine();
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Writes the text representation of the specified object
|
||||
* to the standard output stream using the specified format information.
|
||||
* @param[in] u8_fmt The format string.
|
||||
* @param[in] ... The arguments of format string.
|
||||
*/
|
||||
void Format(const yycc_char8_t* u8_fmt, ...);
|
||||
/**
|
||||
* @brief
|
||||
* Writes the text representation of the specified object,
|
||||
* followed by the current line terminator,
|
||||
* to the standard output stream using the specified format information.
|
||||
* @param[in] u8_fmt The format string.
|
||||
* @param[in] ... The arguments of format string.
|
||||
*/
|
||||
void FormatLine(const yycc_char8_t* u8_fmt, ...);
|
||||
/**
|
||||
* @brief Writes the specified string value to the standard output stream.
|
||||
* @param[in] u8_strl The value to write.
|
||||
*/
|
||||
void Write(const yycc_char8_t* u8_strl);
|
||||
/**
|
||||
* @brief
|
||||
* Writes the specified string value, followed by the current line terminator,
|
||||
* to the standard output stream.
|
||||
* @param[in] u8_strl The value to write.
|
||||
*/
|
||||
void WriteLine(const yycc_char8_t* u8_strl);
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Writes the text representation of the specified object
|
||||
* to the standard error stream using the specified format information.
|
||||
* @param[in] u8_fmt The format string.
|
||||
* @param[in] ... The arguments of format string.
|
||||
*/
|
||||
void ErrFormat(const yycc_char8_t* u8_fmt, ...);
|
||||
/**
|
||||
* @brief
|
||||
* Writes the text representation of the specified object,
|
||||
* followed by the current line terminator,
|
||||
* to the standard error stream using the specified format information.
|
||||
* @param[in] u8_fmt The format string.
|
||||
* @param[in] ... The arguments of format string.
|
||||
*/
|
||||
void ErrFormatLine(const yycc_char8_t* u8_fmt, ...);
|
||||
/**
|
||||
* @brief Writes the specified string value to the standard error stream.
|
||||
* @param[in] u8_strl The value to write.
|
||||
*/
|
||||
void ErrWrite(const yycc_char8_t* u8_strl);
|
||||
/**
|
||||
* @brief
|
||||
* Writes the specified string value, followed by the current line terminator,
|
||||
* to the standard error stream.
|
||||
* @param[in] u8_strl The value to write.
|
||||
*/
|
||||
void ErrWriteLine(const yycc_char8_t* u8_strl);
|
||||
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
#pragma once
|
||||
#include "YYCCInternal.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <stdexcept>
|
||||
#include <set>
|
||||
#include <initializer_list>
|
||||
|
||||
/**
|
||||
* @brief The namespace containing constraint declaration
|
||||
* and functions generating common used constraint.
|
||||
*/
|
||||
namespace YYCC::Constraints {
|
||||
|
||||
/**
|
||||
* @brief The constraint applied to settings to limit its stored value.
|
||||
* @tparam _Ty The data type this constraint need to be processed with.
|
||||
*/
|
||||
template<typename _Ty>
|
||||
struct Constraint {
|
||||
/// @brief Return true if value is legal, otherwise false.
|
||||
using CheckFct_t = std::function<bool(const _Ty&)>;
|
||||
/// @brief The function pointer used for checking whether given value is valid.
|
||||
CheckFct_t m_CheckFct;
|
||||
|
||||
/**
|
||||
* @brief Check whether this constraint is valid for using.
|
||||
* @return
|
||||
* True if this constraint is valid, otherwise false.
|
||||
* If this function return false, it means that there is no contraint.
|
||||
* And you should not use this constraint provided any function pointer.
|
||||
*/
|
||||
bool IsValid() const {
|
||||
return m_CheckFct != nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Get constraint for arithmetic or enum values by minimum and maximum value range.
|
||||
* @tparam _Ty An arithmetic or enum type (except bool) of underlying stored value.
|
||||
* @param[in] min_value The minimum value of range (inclusive).
|
||||
* @param[in] max_value The maximum value of range (inclusive).
|
||||
* @return The generated constraint instance which can be directly applied.
|
||||
*/
|
||||
template<typename _Ty, std::enable_if_t<std::is_arithmetic_v<_Ty> && !std::is_same_v<_Ty, bool>, int> = 0>
|
||||
Constraint<_Ty> GetMinMaxRangeConstraint(_Ty min_value, _Ty max_value) {
|
||||
if (min_value > max_value)
|
||||
throw std::invalid_argument("invalid min max value for NumberRangeConstraint");
|
||||
return Constraint<_Ty> {
|
||||
[min_value, max_value](const _Ty& val) -> bool { return (val <= max_value) && (val >= min_value); }
|
||||
/*[min_value, max_value](const _Ty& val) -> _Ty { return std::clamp(val, min_value, max_value); }*/
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get constraint for enum values by enumerating all possible values.
|
||||
* @tparam _Ty An enum type (except bool) of underlying stored value.
|
||||
* @param[in] il An initializer list storing all possible values.
|
||||
* @return The generated constraint instance which can be directly applied.
|
||||
*/
|
||||
template<typename _Ty, std::enable_if_t<std::is_enum_v<_Ty>, int> = 0>
|
||||
Constraint<_Ty> GetEnumEnumerationConstraint(const std::initializer_list<_Ty>& il) {
|
||||
std::set<_Ty> data(il);
|
||||
return Constraint<_Ty> {
|
||||
[data](const _Ty& val) -> bool { return data.find(val) != data.end(); }
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get constraint for string values by enumerating all possible values.
|
||||
* @param[in] il An initializer list storing all possible values.
|
||||
* @return The generated constraint instance which can be directly applied.
|
||||
* @remarks
|
||||
* Caller must make sure that the string view passed in initializer list is valid until this Constraint life time gone.
|
||||
* Becasue this generator will not copy your given string view into string.
|
||||
*/
|
||||
inline Constraint<yycc_u8string> GetStringEnumerationConstraint(const std::initializer_list<yycc_u8string_view>& il) {
|
||||
std::set<yycc_u8string_view> data(il);
|
||||
return Constraint<yycc_u8string> {
|
||||
[data](const yycc_u8string& val) -> bool { return data.find(yycc_u8string_view(val)) != data.end(); }
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,378 +0,0 @@
|
||||
#include "DialogHelper.hpp"
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
|
||||
#include "EncodingHelper.hpp"
|
||||
#include "StringHelper.hpp"
|
||||
|
||||
namespace YYCC::DialogHelper {
|
||||
|
||||
#pragma region FileFilters
|
||||
|
||||
bool FileFilters::Add(const yycc_char8_t* filter_name, std::initializer_list<const yycc_char8_t*> il) {
|
||||
// assign filter name
|
||||
if (filter_name == nullptr) return false;
|
||||
FilterName name(filter_name);
|
||||
|
||||
// assign filter patterns
|
||||
FilterModes modes;
|
||||
for (const yycc_char8_t* pattern : il) {
|
||||
if (pattern != nullptr)
|
||||
modes.emplace_back(yycc_u8string(pattern));
|
||||
}
|
||||
|
||||
// check filter patterns
|
||||
if (modes.empty()) return false;
|
||||
|
||||
// add into pairs and return
|
||||
m_Filters.emplace_back(std::make_pair(std::move(name), std::move(modes)));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileFilters::Generate(WinFileFilters& win_result) const {
|
||||
// clear Windows oriented data
|
||||
win_result.Clear();
|
||||
|
||||
// build new Windows oriented string vector first
|
||||
for (const auto& it : m_Filters) {
|
||||
// convert name to wchar
|
||||
WinFileFilters::WinFilterName name;
|
||||
if (!YYCC::EncodingHelper::UTF8ToWchar(it.first, name))
|
||||
return false;
|
||||
|
||||
// convert pattern and join them
|
||||
const auto& filter_modes = it.second;
|
||||
yycc_u8string joined_modes(YYCC::StringHelper::Join(filter_modes.begin(), filter_modes.end(), YYCC_U8(";")));
|
||||
WinFileFilters::WinFilterModes modes;
|
||||
if (!YYCC::EncodingHelper::UTF8ToWchar(joined_modes, modes))
|
||||
return false;
|
||||
|
||||
// append new pair
|
||||
win_result.m_WinFilters.emplace_back(std::make_pair(name, modes));
|
||||
}
|
||||
|
||||
// check filter size
|
||||
// if it overflow the maximum value, return false
|
||||
size_t count = win_result.m_WinFilters.size();
|
||||
if (count > std::numeric_limits<UINT>::max())
|
||||
return false;
|
||||
|
||||
// create new win data struct
|
||||
// and assign string pointer from internal built win string vector.
|
||||
win_result.m_WinDataStruct.reset(new COMDLG_FILTERSPEC[count]);
|
||||
for (size_t i = 0u; i < count; ++i) {
|
||||
win_result.m_WinDataStruct[i].pszName = win_result.m_WinFilters[i].first.c_str();
|
||||
win_result.m_WinDataStruct[i].pszSpec = win_result.m_WinFilters[i].second.c_str();
|
||||
}
|
||||
|
||||
// everything is okey
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region File Dialog
|
||||
|
||||
bool FileDialog::Generate(WinFileDialog& win_result) const {
|
||||
// clear Windows oriented data
|
||||
win_result.Clear();
|
||||
|
||||
// set owner
|
||||
win_result.m_WinOwner = m_Owner;
|
||||
|
||||
// build file filters
|
||||
if (!m_FileTypes.Generate(win_result.m_WinFileTypes))
|
||||
return false;
|
||||
|
||||
// check default file type index
|
||||
// check value overflow (comparing with >= because we need plus 1 for file type index later)
|
||||
if (m_DefaultFileTypeIndex >= std::numeric_limits<UINT>::max())
|
||||
return false;
|
||||
// check invalid index (overflow the length or registered file types if there is file type)
|
||||
if (m_FileTypes.Count() != 0u && m_DefaultFileTypeIndex >= m_FileTypes.Count())
|
||||
return false;
|
||||
// set index with additional plus according to Windows specification.
|
||||
win_result.m_WinDefaultFileTypeIndex = static_cast<UINT>(m_DefaultFileTypeIndex + 1);
|
||||
|
||||
// build title and init file name
|
||||
if (m_HasTitle) {
|
||||
if (!YYCC::EncodingHelper::UTF8ToWchar(m_Title, win_result.m_WinTitle))
|
||||
return false;
|
||||
win_result.m_HasTitle = true;
|
||||
}
|
||||
if (m_HasInitFileName) {
|
||||
if (!YYCC::EncodingHelper::UTF8ToWchar(m_InitFileName, win_result.m_WinInitFileName))
|
||||
return false;
|
||||
win_result.m_HasInitFileName = true;
|
||||
}
|
||||
|
||||
// fetch init directory
|
||||
if (m_HasInitDirectory) {
|
||||
// convert to wpath
|
||||
std::wstring w_init_directory;
|
||||
if (!YYCC::EncodingHelper::UTF8ToWchar(m_InitDirectory, w_init_directory))
|
||||
return false;
|
||||
|
||||
// fetch IShellItem*
|
||||
// Ref: https://stackoverflow.com/questions/76306324/how-to-set-default-folder-for-ifileopendialog-interface
|
||||
IShellItem* init_directory = NULL;
|
||||
HRESULT hr = SHCreateItemFromParsingName(w_init_directory.c_str(), NULL, IID_PPV_ARGS(&init_directory));
|
||||
if (FAILED(hr)) return false;
|
||||
|
||||
// assign IShellItem*
|
||||
win_result.m_WinInitDirectory.reset(init_directory);
|
||||
}
|
||||
|
||||
// everything is okey
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Windows Dialog Code
|
||||
|
||||
enum class CommonFileDialogType {
|
||||
OpenFile,
|
||||
OpenMultipleFiles,
|
||||
SaveFile,
|
||||
OpenFolder
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Extract display name from given IShellItem*.
|
||||
* @param item[in] The pointer to IShellItem for extracting.
|
||||
* @param ret[out] Extracted display name container.
|
||||
* @return True if success, otherwise false.
|
||||
* @remarks This is an assist function of CommonFileDialog.
|
||||
*/
|
||||
static bool ExtractDisplayName(IShellItem* item, yycc_u8string& ret) {
|
||||
// fetch display name from IShellItem*
|
||||
LPWSTR _name;
|
||||
HRESULT hr = item->GetDisplayName(SIGDN_FILESYSPATH, &_name);
|
||||
if (FAILED(hr)) return false;
|
||||
COMHelper::SmartLPWSTR display_name(_name);
|
||||
|
||||
// convert result
|
||||
if (!YYCC::EncodingHelper::WcharToUTF8(display_name.get(), ret))
|
||||
return false;
|
||||
|
||||
// finished
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief General file dialog.
|
||||
* @param params[in] User specified parameter controlling the behavior of this file dialog,
|
||||
* including title, file types and etc.
|
||||
* @param ret[out] The path to user selected files or folders.
|
||||
* For multiple selection, the count of items >= 1. For other scenario, the count of item is 1.
|
||||
* @return True if success, otherwise false (input parameters is wrong or user click "Cancel" in popup window).
|
||||
* @remarks This function is the real underlying function of all dialog functions.
|
||||
*/
|
||||
template<CommonFileDialogType EDialogType>
|
||||
static bool CommonFileDialog(const FileDialog& params, std::vector<yycc_u8string>& ret) {
|
||||
// Reference: https://learn.microsoft.com/en-us/windows/win32/shell/common-file-dialog
|
||||
// prepare result variable
|
||||
HRESULT hr;
|
||||
|
||||
// check whether COM environment has been initialized
|
||||
if (!COMHelper::IsInitialized()) return false;
|
||||
|
||||
// create file dialog instance
|
||||
// fetch dialog CLSID first
|
||||
CLSID dialog_clsid;
|
||||
switch (EDialogType) {
|
||||
case CommonFileDialogType::OpenFile:
|
||||
case CommonFileDialogType::OpenMultipleFiles:
|
||||
case CommonFileDialogType::OpenFolder:
|
||||
dialog_clsid = CLSID_FileOpenDialog;
|
||||
break;
|
||||
case CommonFileDialogType::SaveFile:
|
||||
dialog_clsid = CLSID_FileSaveDialog;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
// create raw dialog pointer
|
||||
IFileDialog* _pfd = nullptr;
|
||||
hr = CoCreateInstance(
|
||||
dialog_clsid,
|
||||
NULL,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_PPV_ARGS(&_pfd)
|
||||
);
|
||||
if (FAILED(hr)) return false;
|
||||
// create memory-safe dialog pointer
|
||||
COMHelper::SmartIFileDialog pfd(_pfd);
|
||||
|
||||
// set options for dialog
|
||||
// before setting, always get the options first in order.
|
||||
// not to override existing options.
|
||||
DWORD dwFlags;
|
||||
hr = pfd->GetOptions(&dwFlags);
|
||||
if (FAILED(hr)) return false;
|
||||
// modify options
|
||||
switch (EDialogType) {
|
||||
// We want user only can pick file system files: FOS_FORCEFILESYSTEM.
|
||||
// Open dialog default: FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_NOCHANGEDIR
|
||||
// Save dialog default: FOS_OVERWRITEPROMPT | FOS_NOREADONLYRETURN | FOS_PATHMUSTEXIST | FOS_NOCHANGEDIR
|
||||
// Pick folder: FOS_PICKFOLDERS
|
||||
case CommonFileDialogType::OpenFile:
|
||||
dwFlags |= FOS_FORCEFILESYSTEM;
|
||||
dwFlags |= FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_NOCHANGEDIR;
|
||||
break;
|
||||
case CommonFileDialogType::OpenMultipleFiles:
|
||||
dwFlags |= FOS_FORCEFILESYSTEM;
|
||||
dwFlags |= FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_NOCHANGEDIR;
|
||||
dwFlags |= FOS_ALLOWMULTISELECT;
|
||||
break;
|
||||
case CommonFileDialogType::SaveFile:
|
||||
dwFlags |= FOS_FORCEFILESYSTEM;
|
||||
dwFlags |= FOS_OVERWRITEPROMPT | FOS_NOREADONLYRETURN | FOS_PATHMUSTEXIST | FOS_NOCHANGEDIR;
|
||||
break;
|
||||
case CommonFileDialogType::OpenFolder:
|
||||
dwFlags |= FOS_FORCEFILESYSTEM;
|
||||
dwFlags |= FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_NOCHANGEDIR;
|
||||
dwFlags |= FOS_PICKFOLDERS;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
// set folder dialog options
|
||||
hr = pfd->SetOptions(dwFlags);
|
||||
if (FAILED(hr)) return false;
|
||||
|
||||
// build Windows used file dialog parameters
|
||||
WinFileDialog win_params;
|
||||
if (!params.Generate(win_params))
|
||||
return false;
|
||||
|
||||
// setup title and init file name
|
||||
if (win_params.HasTitle()) {
|
||||
hr = pfd->SetTitle(win_params.GetTitle());
|
||||
if (FAILED(hr)) return false;
|
||||
}
|
||||
if (win_params.HasInitFileName()) {
|
||||
hr = pfd->SetFileName(win_params.GetInitFileName());
|
||||
if (FAILED(hr)) return false;
|
||||
}
|
||||
|
||||
// setup init directory
|
||||
if (win_params.HasInitDirectory()) {
|
||||
hr = pfd->SetFolder(win_params.GetInitDirectory());
|
||||
}
|
||||
|
||||
// set file types and default file index when we picking file
|
||||
if constexpr (EDialogType != CommonFileDialogType::OpenFolder) {
|
||||
// set file types list
|
||||
const auto& file_filters = win_params.GetFileTypes();
|
||||
hr = pfd->SetFileTypes(file_filters.GetFilterCount(), file_filters.GetFilterSpecs());
|
||||
if (FAILED(hr)) return false;
|
||||
|
||||
// set default file type index
|
||||
hr = pfd->SetFileTypeIndex(win_params.GetDefaultFileTypeIndex());
|
||||
if (FAILED(hr)) return false;
|
||||
}
|
||||
|
||||
// show the dialog
|
||||
hr = pfd->Show(win_params.HasOwner() ? win_params.GetOwner() : nullptr);
|
||||
if (FAILED(hr)) return false;
|
||||
|
||||
// obtain result when user click "OK" button.
|
||||
switch (EDialogType) {
|
||||
case CommonFileDialogType::OpenFile:
|
||||
case CommonFileDialogType::OpenFolder:
|
||||
case CommonFileDialogType::SaveFile:
|
||||
{
|
||||
// obtain one file entry
|
||||
IShellItem* _item;
|
||||
hr = pfd->GetResult(&_item);
|
||||
if (FAILED(hr)) return false;
|
||||
COMHelper::SmartIShellItem result_item(_item);
|
||||
|
||||
// extract display name
|
||||
yycc_u8string result_name;
|
||||
if (!ExtractDisplayName(result_item.get(), result_name))
|
||||
return false;
|
||||
|
||||
// append result
|
||||
ret.emplace_back(std::move(result_name));
|
||||
}
|
||||
break;
|
||||
case CommonFileDialogType::OpenMultipleFiles:
|
||||
{
|
||||
// try casting file dialog to file open dialog
|
||||
// Ref: https://learn.microsoft.com/en-us/windows/win32/learnwin32/asking-an-object-for-an-interface
|
||||
IFileOpenDialog* _pfod = nullptr;
|
||||
hr = pfd->QueryInterface(IID_PPV_ARGS(&_pfod));
|
||||
if (FAILED(hr)) return false;
|
||||
COMHelper::SmartIFileOpenDialog pfod(_pfod);
|
||||
|
||||
// obtain multiple file entires
|
||||
IShellItemArray* _items;
|
||||
hr = pfod->GetResults(&_items);
|
||||
if (FAILED(hr)) return false;
|
||||
COMHelper::SmartIShellItemArray result_items(_items);
|
||||
|
||||
// analyze file entries
|
||||
// get array count first
|
||||
DWORD result_items_count = 0u;
|
||||
hr = result_items->GetCount(&result_items_count);
|
||||
if (FAILED(hr)) return false;
|
||||
// iterate array
|
||||
for (DWORD i = 0u; i < result_items_count; ++i) {
|
||||
// fetch item by index
|
||||
IShellItem* _item;;
|
||||
hr = result_items->GetItemAt(i, &_item);
|
||||
if (FAILED(hr)) return false;
|
||||
COMHelper::SmartIShellItem result_item(_item);
|
||||
|
||||
// extract display name
|
||||
yycc_u8string result_name;
|
||||
if (!ExtractDisplayName(result_item.get(), result_name))
|
||||
return false;
|
||||
|
||||
// append result
|
||||
ret.emplace_back(std::move(result_name));
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
// everything is okey
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Wrapper Functions
|
||||
|
||||
bool OpenFileDialog(const FileDialog& params, yycc_u8string& ret) {
|
||||
std::vector<yycc_u8string> cache;
|
||||
bool isok = CommonFileDialog<CommonFileDialogType::OpenFile>(params, cache);
|
||||
if (isok) ret = cache.front();
|
||||
return isok;
|
||||
}
|
||||
bool OpenMultipleFileDialog(const FileDialog& params, std::vector<yycc_u8string>& ret) {
|
||||
return CommonFileDialog<CommonFileDialogType::OpenMultipleFiles>(params, ret);
|
||||
}
|
||||
bool SaveFileDialog(const FileDialog& params, yycc_u8string& ret) {
|
||||
std::vector<yycc_u8string> cache;
|
||||
bool isok = CommonFileDialog<CommonFileDialogType::SaveFile>(params, cache);
|
||||
if (isok) ret = cache.front();
|
||||
return isok;
|
||||
}
|
||||
|
||||
bool OpenFolderDialog(const FileDialog& params, yycc_u8string& ret) {
|
||||
std::vector<yycc_u8string> cache;
|
||||
bool isok = CommonFileDialog<CommonFileDialogType::OpenFolder>(params, cache);
|
||||
if (isok) ret = cache.front();
|
||||
return isok;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,312 +0,0 @@
|
||||
#pragma once
|
||||
#include "YYCCInternal.hpp"
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
|
||||
#include "COMHelper.hpp"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <initializer_list>
|
||||
|
||||
#include "WinImportPrefix.hpp"
|
||||
#include <Windows.h>
|
||||
#include <shlobj_core.h>
|
||||
#include "WinImportSuffix.hpp"
|
||||
|
||||
/**
|
||||
* @brief The namespace providing Windows universal dialog features.
|
||||
* @details
|
||||
* This namespace only available on Windows platform.
|
||||
* See also \ref dialog_helper.
|
||||
*/
|
||||
namespace YYCC::DialogHelper {
|
||||
|
||||
/**
|
||||
* @brief The class representing the file types region in file dialog.
|
||||
* @details
|
||||
* This class is served for Windows used.
|
||||
* Programmer should \b not create this class manually.
|
||||
*/
|
||||
class WinFileFilters {
|
||||
friend class FileFilters;
|
||||
friend class WinFileDialog;
|
||||
public:
|
||||
WinFileFilters() : m_WinFilters(), m_WinDataStruct(nullptr) {}
|
||||
YYCC_DEL_CLS_COPY_MOVE(WinFileFilters);
|
||||
|
||||
/// @brief Get the count of available file filters
|
||||
UINT GetFilterCount() const {
|
||||
return static_cast<UINT>(m_WinFilters.size());
|
||||
}
|
||||
/// @brief Get pointer to Windows used file filters declarations
|
||||
const COMDLG_FILTERSPEC* GetFilterSpecs() const {
|
||||
return m_WinDataStruct.get();
|
||||
}
|
||||
|
||||
protected:
|
||||
using WinFilterModes = std::wstring;
|
||||
using WinFilterName = std::wstring;
|
||||
using WinFilterPair = std::pair<WinFilterName, WinFilterModes>;
|
||||
|
||||
std::vector<WinFilterPair> m_WinFilters;
|
||||
std::unique_ptr<COMDLG_FILTERSPEC[]> m_WinDataStruct;
|
||||
|
||||
/// @brief Clear all current file filters
|
||||
void Clear() {
|
||||
m_WinDataStruct.reset();
|
||||
m_WinFilters.clear();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The class representing the file types region in file dialog.
|
||||
* @details
|
||||
* This class is served for programmer using.
|
||||
* But you don't need create it on your own.
|
||||
* You can simply fetch it by FileDialog::ConfigreFileTypes ,
|
||||
* because this class is a part of FileDialog.
|
||||
*/
|
||||
class FileFilters {
|
||||
public:
|
||||
FileFilters() : m_Filters() {}
|
||||
YYCC_DEL_CLS_COPY_MOVE(FileFilters);
|
||||
|
||||
/**
|
||||
* @brief Add a filter pair in file types list.
|
||||
* @param[in] filter_name The friendly name of the filter.
|
||||
* @param[in] il
|
||||
* A C++ initialize list containing acceptable file filter pattern.
|
||||
* Every entries must be `const yycc_char8_t*` representing a single filter pattern.
|
||||
* The list at least should have one valid pattern.
|
||||
* This function will not validate these filter patterns, so please write them carefully.
|
||||
* @return True if added success, otherwise false.
|
||||
* @remarks
|
||||
* This function allow you register multiple filter patterns for single friendly name.
|
||||
* For example: <TT>Add(u8"Microsoft Word (*.doc; *.docx)", {u8"*.doc", u8"*.docx"})</TT>
|
||||
*/
|
||||
bool Add(const yycc_char8_t* filter_name, std::initializer_list<const yycc_char8_t*> il);
|
||||
/**
|
||||
* @brief Get the count of added filter pairs.
|
||||
* @return The count of already added filter pairs.
|
||||
*/
|
||||
size_t Count() const { return m_Filters.size(); }
|
||||
|
||||
/// @brief Clear filter pairs for following re-use.
|
||||
void Clear() { m_Filters.clear(); }
|
||||
|
||||
/**
|
||||
* @brief Generate Windows dialog system used data struct.
|
||||
* @param[out] win_result The class receiving the generated filter data struct.
|
||||
* @return True if generation success, otherwise false.
|
||||
* @remarks
|
||||
* Programmer should not call this function,
|
||||
* this function is used as YYCC internal code.
|
||||
*/
|
||||
bool Generate(WinFileFilters& win_result) const;
|
||||
|
||||
protected:
|
||||
using FilterModes = std::vector<yycc_u8string>;
|
||||
using FilterName = yycc_u8string;
|
||||
using FilterPair = std::pair<FilterName, FilterModes>;
|
||||
|
||||
std::vector<FilterPair> m_Filters;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The class representing the file dialog.
|
||||
* @details
|
||||
* This class is served for Windows used.
|
||||
* Programmer should \b not create this class manually.
|
||||
*/
|
||||
class WinFileDialog {
|
||||
friend class FileDialog;
|
||||
public:
|
||||
WinFileDialog() :
|
||||
m_WinOwner(NULL),
|
||||
m_WinFileTypes(), m_WinDefaultFileTypeIndex(0u),
|
||||
m_HasTitle(false), m_HasInitFileName(false), m_WinTitle(), m_WinInitFileName(),
|
||||
m_WinInitDirectory(nullptr) {}
|
||||
YYCC_DEL_CLS_COPY_MOVE(WinFileDialog);
|
||||
|
||||
/// @brief Get whether this dialog has owner.
|
||||
bool HasOwner() const { return m_WinOwner != NULL; }
|
||||
/// @brief Get the \c HWND of dialog owner.
|
||||
HWND GetOwner() const { return m_WinOwner; }
|
||||
|
||||
/// @brief Get the struct holding Windows used file filters data.
|
||||
const WinFileFilters& GetFileTypes() const { return m_WinFileTypes; }
|
||||
/// @brief Get the index of default selected file filter.
|
||||
UINT GetDefaultFileTypeIndex() const { return m_WinDefaultFileTypeIndex; }
|
||||
|
||||
/// @brief Get whether dialog has custom title.
|
||||
bool HasTitle() const { return m_HasTitle; }
|
||||
/// @brief Get custom title of dialog.
|
||||
const wchar_t* GetTitle() const { return m_WinTitle.c_str(); }
|
||||
/// @brief Get whether dialog has custom initial file name.
|
||||
bool HasInitFileName() const { return m_HasInitFileName; }
|
||||
/// @brief Get custom initial file name of dialog
|
||||
const wchar_t* GetInitFileName() const { return m_WinInitFileName.c_str(); }
|
||||
|
||||
/// @brief Get whether dialog has custom initial directory.
|
||||
bool HasInitDirectory() const { return m_WinInitDirectory.get() != nullptr; }
|
||||
/// @brief Get custom initial directory of dialog.
|
||||
IShellItem* GetInitDirectory() const { return m_WinInitDirectory.get(); }
|
||||
|
||||
protected:
|
||||
HWND m_WinOwner;
|
||||
WinFileFilters m_WinFileTypes;
|
||||
/**
|
||||
* @brief The default selected file type in dialog
|
||||
* @remarks
|
||||
* This is 1-based index according to Windows specification.
|
||||
* In other words, when generating this struct from FileDialog to this struct this field should plus 1.
|
||||
* Because the same field located in FileDialog is 0-based index.
|
||||
*/
|
||||
UINT m_WinDefaultFileTypeIndex;
|
||||
bool m_HasTitle, m_HasInitFileName;
|
||||
std::wstring m_WinTitle, m_WinInitFileName;
|
||||
COMHelper::SmartIShellItem m_WinInitDirectory;
|
||||
|
||||
/// @brief Clear all data and reset them to default value.
|
||||
void Clear() {
|
||||
m_WinOwner = nullptr;
|
||||
m_WinFileTypes.Clear();
|
||||
m_WinDefaultFileTypeIndex = 0u;
|
||||
m_HasTitle = m_HasInitFileName = false;
|
||||
m_WinTitle.clear();
|
||||
m_WinInitFileName.clear();
|
||||
m_WinInitDirectory.reset();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The class representing the file dialog.
|
||||
* @details
|
||||
* This class is served for programming using to describe every aspectes of the dialog.
|
||||
* For how to use this struct, see \ref dialog_helper.
|
||||
*/
|
||||
class FileDialog {
|
||||
public:
|
||||
FileDialog() :
|
||||
m_Owner(NULL),
|
||||
m_FileTypes(),
|
||||
m_DefaultFileTypeIndex(0u),
|
||||
m_Title(), m_InitFileName(), m_InitDirectory(),
|
||||
m_HasTitle(false), m_HasInitFileName(false), m_HasInitDirectory(false) {}
|
||||
YYCC_DEL_CLS_COPY_MOVE(FileDialog);
|
||||
|
||||
/**
|
||||
* @brief Set the owner of dialog.
|
||||
* @param[in] owner The \c HWND pointing to the owner of dialog, or NULL to remove owner.
|
||||
*/
|
||||
void SetOwner(HWND owner) { m_Owner = owner; }
|
||||
/**
|
||||
* @brief Set custom title of dialog
|
||||
* @param[in] title The string pointer to custom title, or nullptr to remove it.
|
||||
*/
|
||||
void SetTitle(const yycc_char8_t* title) {
|
||||
if (m_HasTitle = title != nullptr)
|
||||
m_Title = title;
|
||||
}
|
||||
/**
|
||||
* @brief Fetch the struct describing file filters for future configuration.
|
||||
* @return The reference to the struct describing file filters.
|
||||
*/
|
||||
FileFilters& ConfigreFileTypes() {
|
||||
return m_FileTypes;
|
||||
}
|
||||
/**
|
||||
* @brief Set the index of default selected file filter.
|
||||
* @param[in] idx
|
||||
* The index to default one.
|
||||
* This must be a valid index in file filters.
|
||||
*/
|
||||
void SetDefaultFileTypeIndex(size_t idx) { m_DefaultFileTypeIndex = idx; }
|
||||
/**
|
||||
* @brief Set the initial file name of dialog
|
||||
* @details If set, the file name will always be same one when opening dialog.
|
||||
* @param[in] init_filename String pointer to initial file name, or nullptr to remove it.
|
||||
*/
|
||||
void SetInitFileName(const yycc_char8_t* init_filename) {
|
||||
if (m_HasInitFileName = init_filename != nullptr)
|
||||
m_InitFileName = init_filename;
|
||||
}
|
||||
/**
|
||||
* @brief Set the initial directory of dialog
|
||||
* @details If set, the opended directory will always be the same one when opening dialog
|
||||
* @param[in] init_dir
|
||||
* String pointer to initial directory.
|
||||
* Invalid path or nullptr will remove this feature.
|
||||
*/
|
||||
void SetInitDirectory(const yycc_char8_t* init_dir) {
|
||||
if (m_HasInitDirectory = init_dir != nullptr)
|
||||
m_InitDirectory = init_dir;
|
||||
}
|
||||
|
||||
/// @brief Clear file dialog parameters for following re-use.
|
||||
void Clear() {
|
||||
m_Owner = nullptr;
|
||||
m_HasTitle = m_HasInitFileName = m_HasInitDirectory = false;
|
||||
m_Title.clear();
|
||||
m_InitFileName.clear();
|
||||
m_InitDirectory.clear();
|
||||
m_FileTypes.Clear();
|
||||
m_DefaultFileTypeIndex = 0u;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate Windows dialog system used data struct.
|
||||
* @param[out] win_result The class receiving the generated filter data struct.
|
||||
* @return True if generation is success, otherwise false.
|
||||
* @remarks
|
||||
* Programmer should not call this function.
|
||||
* This function is used as YYCC internal code.
|
||||
*/
|
||||
bool Generate(WinFileDialog& win_result) const;
|
||||
|
||||
protected:
|
||||
HWND m_Owner;
|
||||
bool m_HasTitle, m_HasInitFileName, m_HasInitDirectory;
|
||||
yycc_u8string m_Title, m_InitFileName, m_InitDirectory;
|
||||
FileFilters m_FileTypes;
|
||||
/**
|
||||
* @brief The default selected file type in dialog
|
||||
* @remarks
|
||||
* The index Windows used is 1-based index.
|
||||
* But for universal experience, we order this is 0-based index.
|
||||
* And do convertion when generating Windows used struct.
|
||||
*/
|
||||
size_t m_DefaultFileTypeIndex;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Open the dialog which order user select single file to open.
|
||||
* @param[in] params The configuration of dialog.
|
||||
* @param[out] ret Full path to user selected file.
|
||||
* @return False if user calcel the operation or something went wrong, otherwise true.
|
||||
*/
|
||||
bool OpenFileDialog(const FileDialog& params, yycc_u8string& ret);
|
||||
/**
|
||||
* @brief Open the dialog which order user select multiple file to open.
|
||||
* @param[in] params The configuration of dialog.
|
||||
* @param[out] ret The list of full path of user selected files.
|
||||
* @return False if user calcel the operation or something went wrong, otherwise true.
|
||||
*/
|
||||
bool OpenMultipleFileDialog(const FileDialog& params, std::vector<yycc_u8string>& ret);
|
||||
/**
|
||||
* @brief Open the dialog which order user select single file to save.
|
||||
* @param[in] params The configuration of dialog.
|
||||
* @param[out] ret Full path to user selected file.
|
||||
* @return False if user calcel the operation or something went wrong, otherwise true.
|
||||
*/
|
||||
bool SaveFileDialog(const FileDialog& params, yycc_u8string& ret);
|
||||
/**
|
||||
* @brief Open the dialog which order user select single directory to open.
|
||||
* @param[in] params The configuration of dialog.
|
||||
* @param[out] ret Full path to user selected directory.
|
||||
* @return False if user calcel the operation or something went wrong, otherwise true.
|
||||
*/
|
||||
bool OpenFolderDialog(const FileDialog& params, yycc_u8string& ret);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,209 +0,0 @@
|
||||
#include "EncodingHelper.hpp"
|
||||
|
||||
#include <locale>
|
||||
|
||||
namespace YYCC::EncodingHelper {
|
||||
|
||||
/* Define some assistant macros for easy writing. */
|
||||
|
||||
#define CONVFCT_TYPE2(fct_name, src_char_type, dst_char_type, ...) if (src == nullptr) return false; \
|
||||
std::basic_string_view<src_char_type> cache(src); \
|
||||
return fct_name(cache, dst, ##__VA_ARGS__);
|
||||
|
||||
#define CONVFCT_TYPE3(fct_name, src_char_type, dst_char_type, ...) std::basic_string<dst_char_type> ret; \
|
||||
if (!fct_name(src, ret, ##__VA_ARGS__)) ret.clear(); \
|
||||
return ret;
|
||||
|
||||
#define CONVFCT_TYPE4(fct_name, src_char_type, dst_char_type, ...) std::basic_string<dst_char_type> ret; \
|
||||
if (!fct_name(src, ret, ##__VA_ARGS__)) ret.clear(); \
|
||||
return ret;
|
||||
|
||||
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
|
||||
#pragma region WcharToChar
|
||||
|
||||
bool WcharToChar(const std::wstring_view& src, std::string& dst, UINT code_page) {
|
||||
// if src is empty, direct output
|
||||
if (src.empty()) {
|
||||
dst.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
// init WideCharToMultiByte used variables
|
||||
// setup src pointer
|
||||
LPCWCH lpWideCharStr = reinterpret_cast<LPCWCH>(src.data());
|
||||
// check whether source string is too large.
|
||||
size_t cSrcSize = src.size();
|
||||
if (cSrcSize > std::numeric_limits<int>::max()) return false;
|
||||
int cchWideChar = static_cast<int>(src.size());
|
||||
|
||||
// do convertion
|
||||
// do a dry-run first to fetch desired size.
|
||||
int desired_size = WideCharToMultiByte(code_page, 0, lpWideCharStr, cchWideChar, NULL, 0, NULL, NULL);
|
||||
if (desired_size <= 0) return false;
|
||||
// resize dest for receiving result
|
||||
dst.resize(static_cast<size_t>(desired_size));
|
||||
// do real convertion
|
||||
int write_result = WideCharToMultiByte(code_page, 0, lpWideCharStr, cchWideChar, reinterpret_cast<LPSTR>(dst.data()), desired_size, NULL, NULL);
|
||||
if (write_result <= 0) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
bool WcharToChar(const wchar_t* src, std::string& dst, UINT code_page) {
|
||||
CONVFCT_TYPE2(WcharToChar, wchar_t, char, code_page);
|
||||
}
|
||||
std::string WcharToChar(const std::wstring_view& src, UINT code_page) {
|
||||
CONVFCT_TYPE3(WcharToChar, wchar_t, char, code_page);
|
||||
}
|
||||
std::string WcharToChar(const wchar_t* src, UINT code_page) {
|
||||
CONVFCT_TYPE4(WcharToChar, wchar_t, char, code_page);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region CharToWchar
|
||||
|
||||
bool CharToWchar(const std::string_view& src, std::wstring& dst, UINT code_page) {
|
||||
// if src is empty, direct output
|
||||
if (src.empty()) {
|
||||
dst.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
// init WideCharToMultiByte used variables
|
||||
// setup src pointer
|
||||
LPCCH lpMultiByteStr = reinterpret_cast<LPCCH>(src.data());
|
||||
// check whether source string is too large.
|
||||
size_t cSrcSize = src.size();
|
||||
if (cSrcSize > std::numeric_limits<int>::max()) return false;
|
||||
int cbMultiByte = static_cast<int>(src.size());
|
||||
|
||||
// do convertion
|
||||
// do a dry-run first to fetch desired size.
|
||||
int desired_size = MultiByteToWideChar(code_page, 0, lpMultiByteStr, cbMultiByte, NULL, 0);
|
||||
if (desired_size <= 0) return false;
|
||||
// resize dest for receiving result
|
||||
dst.resize(static_cast<size_t>(desired_size));
|
||||
// do real convertion
|
||||
int write_result = MultiByteToWideChar(code_page, 0, lpMultiByteStr, cbMultiByte, reinterpret_cast<LPWSTR>(dst.data()), desired_size);
|
||||
if (write_result <= 0) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
bool CharToWchar(const char* src, std::wstring& dst, UINT code_page) {
|
||||
CONVFCT_TYPE2(CharToWchar, char, wchar_t, code_page);
|
||||
}
|
||||
std::wstring CharToWchar(const std::string_view& src, UINT code_page) {
|
||||
CONVFCT_TYPE3(CharToWchar, char, wchar_t, code_page);
|
||||
}
|
||||
std::wstring CharToWchar(const char* src, UINT code_page) {
|
||||
CONVFCT_TYPE4(CharToWchar, char, wchar_t, code_page);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region CharToChar
|
||||
|
||||
bool CharToChar(const std::string_view& src, std::string& dst, UINT src_code_page, UINT dst_code_page) {
|
||||
std::wstring intermediary;
|
||||
if (!CharToWchar(src, intermediary, src_code_page)) return false;
|
||||
if (!WcharToChar(intermediary, dst, dst_code_page)) return false;
|
||||
return true;
|
||||
}
|
||||
bool CharToChar(const char* src, std::string& dst, UINT src_code_page, UINT dst_code_page) {
|
||||
CONVFCT_TYPE2(CharToChar, char, char, src_code_page, dst_code_page);
|
||||
}
|
||||
std::string CharToChar(const std::string_view& src, UINT src_code_page, UINT dst_code_page) {
|
||||
CONVFCT_TYPE3(CharToChar, char, char, src_code_page, dst_code_page);
|
||||
}
|
||||
std::string CharToChar(const char* src, UINT src_code_page, UINT dst_code_page) {
|
||||
CONVFCT_TYPE4(CharToChar, char, char, src_code_page, dst_code_page);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region WcharToUTF8
|
||||
|
||||
bool WcharToUTF8(const std::wstring_view& src, yycc_u8string& dst) {
|
||||
std::string adapted_dst;
|
||||
bool ret = WcharToChar(src, adapted_dst, CP_UTF8);
|
||||
if (ret) dst = ToUTF8(adapted_dst);
|
||||
return ret;
|
||||
}
|
||||
bool WcharToUTF8(const wchar_t* src, yycc_u8string& dst) {
|
||||
CONVFCT_TYPE2(WcharToUTF8, wchar_t, yycc_char8_t);
|
||||
}
|
||||
yycc_u8string WcharToUTF8(const std::wstring_view& src) {
|
||||
CONVFCT_TYPE3(WcharToUTF8, wchar_t, yycc_char8_t);
|
||||
}
|
||||
yycc_u8string WcharToUTF8(const wchar_t* src) {
|
||||
CONVFCT_TYPE4(WcharToUTF8, wchar_t, yycc_char8_t);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region UTF8ToWchar
|
||||
|
||||
bool UTF8ToWchar(const yycc_u8string_view& src, std::wstring& dst) {
|
||||
std::string_view adapted_src(ToOrdinaryView(src));
|
||||
return CharToWchar(adapted_src, dst, CP_UTF8);
|
||||
}
|
||||
bool UTF8ToWchar(const yycc_char8_t* src, std::wstring& dst) {
|
||||
CONVFCT_TYPE2(UTF8ToWchar, yycc_char8_t, wchar_t);
|
||||
}
|
||||
std::wstring UTF8ToWchar(const yycc_u8string_view& src) {
|
||||
CONVFCT_TYPE3(UTF8ToWchar, yycc_char8_t, wchar_t);
|
||||
}
|
||||
std::wstring UTF8ToWchar(const yycc_char8_t* src) {
|
||||
CONVFCT_TYPE4(UTF8ToWchar, yycc_char8_t, wchar_t);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region CharToUTF8
|
||||
|
||||
bool CharToUTF8(const std::string_view& src, yycc_u8string& dst, UINT code_page) {
|
||||
std::string adapted_dst;
|
||||
bool ret = CharToChar(src, adapted_dst, code_page, CP_UTF8);
|
||||
if (ret) dst = ToUTF8(adapted_dst);
|
||||
return ret;
|
||||
}
|
||||
bool CharToUTF8(const char* src, yycc_u8string& dst, UINT code_page) {
|
||||
CONVFCT_TYPE2(CharToUTF8, char, yycc_char8_t, code_page);
|
||||
}
|
||||
yycc_u8string CharToUTF8(const std::string_view& src, UINT code_page) {
|
||||
CONVFCT_TYPE3(CharToUTF8, char, yycc_char8_t, code_page);
|
||||
}
|
||||
yycc_u8string CharToUTF8(const char* src, UINT code_page) {
|
||||
CONVFCT_TYPE4(CharToUTF8, char, yycc_char8_t, code_page);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region UTF8ToChar
|
||||
|
||||
bool UTF8ToChar(const yycc_u8string_view& src, std::string& dst, UINT code_page) {
|
||||
std::string_view adapted_src(ToOrdinaryView(src));
|
||||
return CharToChar(adapted_src, dst, CP_UTF8, code_page);
|
||||
}
|
||||
bool UTF8ToChar(const yycc_char8_t* src, std::string& dst, UINT code_page) {
|
||||
CONVFCT_TYPE2(UTF8ToChar, yycc_char8_t, char, code_page);
|
||||
}
|
||||
std::string UTF8ToChar(const yycc_u8string_view& src, UINT code_page) {
|
||||
CONVFCT_TYPE3(UTF8ToChar, yycc_char8_t, char, code_page);
|
||||
}
|
||||
std::string UTF8ToChar(const yycc_char8_t* src, UINT code_page) {
|
||||
CONVFCT_TYPE4(UTF8ToChar, yycc_char8_t, char, code_page);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#endif
|
||||
|
||||
#undef CONVFCT_TYPE2
|
||||
#undef CONVFCT_TYPE3
|
||||
#undef CONVFCT_TYPE4
|
||||
|
||||
}
|
||||
|
@ -1,60 +0,0 @@
|
||||
#pragma once
|
||||
#include "YYCCInternal.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
#include "WinImportPrefix.hpp"
|
||||
#include <Windows.h>
|
||||
#include "WinImportSuffix.hpp"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief The helper for all encoding stuff.
|
||||
* @details
|
||||
* For more infomations about how to use the functions provided by this namespace,
|
||||
* please see \ref library_encoding and \ref encoding_helper.
|
||||
*/
|
||||
namespace YYCC::EncodingHelper {
|
||||
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
|
||||
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);
|
||||
|
||||
bool CharToWchar(const std::string_view& src, std::wstring& dst, UINT code_page);
|
||||
bool CharToWchar(const char* src, std::wstring& dst, UINT code_page);
|
||||
std::wstring CharToWchar(const std::string_view& src, UINT code_page);
|
||||
std::wstring CharToWchar(const char* src, UINT code_page);
|
||||
|
||||
bool CharToChar(const std::string_view& src, std::string& dst, UINT src_code_page, UINT dst_code_page);
|
||||
bool CharToChar(const char* src, std::string& dst, UINT src_code_page, UINT dst_code_page);
|
||||
std::string CharToChar(const std::string_view& src, UINT src_code_page, UINT dst_code_page);
|
||||
std::string CharToChar(const char* src, UINT src_code_page, UINT dst_code_page);
|
||||
|
||||
|
||||
bool WcharToUTF8(const std::wstring_view& src, yycc_u8string& dst);
|
||||
bool WcharToUTF8(const wchar_t* src, yycc_u8string& dst);
|
||||
yycc_u8string WcharToUTF8(const std::wstring_view& src);
|
||||
yycc_u8string WcharToUTF8(const wchar_t* src);
|
||||
|
||||
bool UTF8ToWchar(const yycc_u8string_view& src, std::wstring& dst);
|
||||
bool UTF8ToWchar(const yycc_char8_t* src, std::wstring& dst);
|
||||
std::wstring UTF8ToWchar(const yycc_u8string_view& src);
|
||||
std::wstring UTF8ToWchar(const yycc_char8_t* src);
|
||||
|
||||
bool CharToUTF8(const std::string_view& src, yycc_u8string& dst, UINT code_page);
|
||||
bool CharToUTF8(const char* src, yycc_u8string& dst, UINT code_page);
|
||||
yycc_u8string CharToUTF8(const std::string_view& src, UINT code_page);
|
||||
yycc_u8string CharToUTF8(const char* src, UINT code_page);
|
||||
|
||||
bool UTF8ToChar(const yycc_u8string_view& src, std::string& dst, UINT code_page);
|
||||
bool UTF8ToChar(const yycc_char8_t* src, std::string& dst, UINT code_page);
|
||||
std::string UTF8ToChar(const yycc_u8string_view& src, UINT code_page);
|
||||
std::string UTF8ToChar(const yycc_char8_t* src, UINT code_page);
|
||||
|
||||
#endif
|
||||
|
||||
}
|
@ -1,182 +0,0 @@
|
||||
#pragma once
|
||||
#include "YYCCInternal.hpp"
|
||||
|
||||
#include <initializer_list>
|
||||
#include <type_traits>
|
||||
|
||||
/**
|
||||
* @brief The namespace for convenient C++ enum class logic operations.
|
||||
* @details
|
||||
* C++ enum class statement is a modern way to declare enum in C++.
|
||||
* But it lack essential logic operations which is commonly used by programmer.
|
||||
* So we create this helper to resolve this issue.
|
||||
*/
|
||||
namespace YYCC::EnumHelper {
|
||||
|
||||
//// Reference:
|
||||
//// Enum operator overload: https://stackoverflow.com/a/71107019
|
||||
//// Constexpr operator overload: https://stackoverflow.com/a/17746099
|
||||
|
||||
//template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
//inline constexpr TEnum operator|(TEnum lhs, TEnum rhs) {
|
||||
// using ut = std::underlying_type_t<TEnum>;
|
||||
// return static_cast<TEnum>(static_cast<ut>(lhs) | static_cast<ut>(rhs));
|
||||
//}
|
||||
//template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
//inline constexpr TEnum operator|=(TEnum& lhs, TEnum rhs) {
|
||||
// using ut = std::underlying_type_t<TEnum>;
|
||||
// lhs = lhs | rhs;
|
||||
// return lhs;
|
||||
//}
|
||||
//
|
||||
//template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
//inline constexpr TEnum operator&(TEnum lhs, TEnum rhs) {
|
||||
// using ut = std::underlying_type_t<TEnum>;
|
||||
// return static_cast<TEnum>(static_cast<ut>(lhs) & static_cast<ut>(rhs));
|
||||
//}
|
||||
//template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
//inline constexpr TEnum operator&=(TEnum& lhs, TEnum rhs) {
|
||||
// using ut = std::underlying_type_t<TEnum>;
|
||||
// lhs = lhs & rhs;
|
||||
// return lhs;
|
||||
//}
|
||||
//
|
||||
//template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
//inline constexpr TEnum operator^(TEnum lhs, TEnum rhs) {
|
||||
// using ut = std::underlying_type_t<TEnum>;
|
||||
// return static_cast<TEnum>(static_cast<ut>(lhs) ^ static_cast<ut>(rhs));
|
||||
//}
|
||||
//template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
//inline constexpr TEnum operator^=(TEnum& lhs, TEnum rhs) {
|
||||
// using ut = std::underlying_type_t<TEnum>;
|
||||
// lhs = lhs ^ rhs;
|
||||
// return lhs;
|
||||
//}
|
||||
|
||||
//template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
//inline constexpr TEnum operator~(TEnum lhs) {
|
||||
// using ut = std::underlying_type_t<TEnum>;
|
||||
// return static_cast<TEnum>(~(static_cast<ut>(lhs)));
|
||||
//}
|
||||
//
|
||||
//template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
//inline constexpr bool operator bool(TEnum lhs) {
|
||||
// using ut = std::underlying_type_t<TEnum>;
|
||||
// return static_cast<bool>(static_cast<ut>(lhs));
|
||||
//}
|
||||
|
||||
/**
|
||||
* @brief The helper struct to check all given template argument are the same enum type.
|
||||
* @tparam TEnum The template parameter to be checked (first one).
|
||||
* @tparam Ts The template parameter to be checked.
|
||||
*/
|
||||
template<typename TEnum, typename... Ts>
|
||||
struct all_enum_values {
|
||||
public:
|
||||
// Please note it is std::is_same, not std::is_same_v!
|
||||
// That's std::conjunction_v required.
|
||||
static constexpr bool value = std::is_enum_v<std::remove_cv_t<TEnum>> && std::conjunction_v<std::is_same<std::remove_cv_t<TEnum>, std::remove_cv_t<Ts>>...>;
|
||||
};
|
||||
/**
|
||||
* @brief The convenient calling to all_enum_values::value to check enum template parameter.
|
||||
* @tparam TEnum The template parameter to be checked (first one).
|
||||
* @tparam Ts The template parameter to be checked.
|
||||
*/
|
||||
template<typename TEnum, typename... Ts>
|
||||
inline constexpr bool all_enum_values_v = all_enum_values<TEnum, Ts...>::value;
|
||||
|
||||
/**
|
||||
* @brief Merge given enum flags like performing <TT>e1 | e2 | ... | en</TT>
|
||||
* @tparam TEnum Enum type for processing.
|
||||
* @param[in] val The first enum flag to be merged.
|
||||
* @param[in] val_left Left enum flags to be merged.
|
||||
* @return The merged enum flag.
|
||||
* @remarks
|
||||
* This function use recursive expansion to get final merge result.
|
||||
* So there is no difference of each arguments.
|
||||
* We independ first argument just served for expansion.
|
||||
*/
|
||||
template<typename TEnum, typename... Ts, std::enable_if_t<all_enum_values_v<TEnum, Ts...>, int> = 0>
|
||||
constexpr TEnum Merge(TEnum val, Ts... val_left) {
|
||||
using ut = std::underlying_type_t<TEnum>;
|
||||
ut result = static_cast<ut>(val);
|
||||
if constexpr (sizeof...(val_left) > 0) {
|
||||
result |= static_cast<ut>(Merge(val_left...));
|
||||
}
|
||||
return static_cast<TEnum>(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reverse given enum flags like performing <TT>~(e)</TT>
|
||||
* @tparam TEnum Enum type for processing.
|
||||
* @param[in] e The list of enum flags to be inversed.
|
||||
* @return The inversed enum flag.
|
||||
*/
|
||||
template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
constexpr TEnum Invert(TEnum e) {
|
||||
using ut = std::underlying_type_t<TEnum>;
|
||||
return static_cast<TEnum>(~(static_cast<ut>(e)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Use specified enum flag to mask given enum flag like performing <TT>e1 &= e2</TT>
|
||||
* @tparam TEnum Enum type for processing.
|
||||
* @param[in,out] e1 The enum flags to be masked.
|
||||
* @param[in] e2 The mask enum flag.
|
||||
*/
|
||||
template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
constexpr void Mask(TEnum& e1, TEnum e2) {
|
||||
using ut = std::underlying_type_t<TEnum>;
|
||||
e1 = static_cast<TEnum>(static_cast<ut>(e1) & static_cast<ut>(e2));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add multiple enum flags to given enum flag like performing <TT>e1 |= (e2 | e3 | ... | en)</TT>
|
||||
* @tparam TEnum Enum type for processing.
|
||||
* @param[in,out] e1 The enum flag which flags add on.
|
||||
* @param[in] vals The enum flag to be added.
|
||||
*/
|
||||
template<typename TEnum, typename... Ts, std::enable_if_t<all_enum_values_v<TEnum, Ts...>, int> = 0>
|
||||
constexpr void Add(TEnum& e1, Ts... vals) {
|
||||
using ut = std::underlying_type_t<TEnum>;
|
||||
e1 = static_cast<TEnum>(static_cast<ut>(e1) | static_cast<ut>(Merge(vals...)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Remove multiple enum flags from given enum flag like performing <TT>e1 &= ~(e2 | e3 | ... | en)</TT>
|
||||
* @tparam TEnum Enum type for processing.
|
||||
* @param[in,out] e1 The enum flag which flags removed from.
|
||||
* @param[in] vals The enum flag to be removed.
|
||||
*/
|
||||
template<typename TEnum, typename... Ts, std::enable_if_t<all_enum_values_v<TEnum>, int> = 0>
|
||||
constexpr void Remove(TEnum& e1, Ts... vals) {
|
||||
using ut = std::underlying_type_t<TEnum>;
|
||||
e1 = static_cast<TEnum>(static_cast<ut>(e1) & static_cast<ut>(Invert(Merge(vals...))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check whether given enum flag has any of specified multiple enum flags (OR) like performing <TT>bool(e1 & (e2 | e3 | ... | en))</TT>
|
||||
* @tparam TEnum Enum type for processing.
|
||||
* @param[in] e1 The enum flag where we check.
|
||||
* @param[in] vals The enum flags for checking.
|
||||
* @return True if it has any of given flags (OR), otherwise false.
|
||||
*/
|
||||
template<typename TEnum, typename... Ts, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
constexpr bool Has(TEnum e1, Ts... vals) {
|
||||
using ut = std::underlying_type_t<TEnum>;
|
||||
return static_cast<bool>(static_cast<ut>(e1) & static_cast<ut>(Merge(vals...)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Cast given enum flags to its equvalent boolean value like performing <TT>bool(e)</TT>
|
||||
* @tparam TEnum Enum type for processing.
|
||||
* @param e The enum flags to be cast.
|
||||
* @return The equvalent bool value of given enum flag.
|
||||
*/
|
||||
template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
constexpr bool Bool(TEnum e) {
|
||||
using ut = std::underlying_type_t<TEnum>;
|
||||
return static_cast<bool>(static_cast<ut>(e));
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
#include "ExceptionHelper.hpp"
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
|
||||
#include "WinFctHelper.hpp"
|
||||
#include "ConsoleHelper.hpp"
|
||||
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
#include "YYCCInternal.hpp"
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
|
||||
/**
|
||||
* @brief Windows specific unhandled exception processor.
|
||||
|
@ -1,37 +0,0 @@
|
||||
#include "IOHelper.hpp"
|
||||
|
||||
#include "EncodingHelper.hpp"
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <memory>
|
||||
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
#include "WinImportPrefix.hpp"
|
||||
#include <Windows.h>
|
||||
#include "WinImportSuffix.hpp"
|
||||
#endif
|
||||
|
||||
namespace YYCC::IOHelper {
|
||||
|
||||
std::FILE* UTF8FOpen(const yycc_char8_t* u8_filepath, const yycc_char8_t* u8_mode) {
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
|
||||
// convert mode and file path to wchar
|
||||
std::wstring wmode, wpath;
|
||||
if (!YYCC::EncodingHelper::UTF8ToWchar(u8_mode, wmode))
|
||||
return nullptr;
|
||||
if (!YYCC::EncodingHelper::UTF8ToWchar(u8_filepath, wpath))
|
||||
return nullptr;
|
||||
|
||||
// call microsoft specified fopen which support wchar as argument.
|
||||
return _wfopen(wpath.c_str(), wmode.c_str());
|
||||
|
||||
#else
|
||||
return std::fopen(EncodingHelper::ToOrdinary(u8_filepath), EncodingHelper::ToOrdinary(u8_mode));
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,55 +0,0 @@
|
||||
#pragma once
|
||||
#include "YYCCInternal.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
|
||||
/**
|
||||
* @brief Some IO related stuff
|
||||
* @details
|
||||
* See also \ref io_helper.
|
||||
*/
|
||||
namespace YYCC::IOHelper {
|
||||
|
||||
#if UINTPTR_MAX == UINT32_MAX
|
||||
#define PRI_XPTR_LEFT_PADDING "08"
|
||||
#elif UINTPTR_MAX == UINT64_MAX
|
||||
/**
|
||||
* @brief The left-padding zero format string of HEX-printed pointer type.
|
||||
* @details
|
||||
* When printing a pointer with HEX style, we always hope it can be left-padded with some zero for easy reading.
|
||||
* In different architecture, the size of this padding is differnet too so we create this macro.
|
||||
*
|
||||
* In 32-bit environment, it will be "08" meaning left pad zero until 8 number position.
|
||||
* In 64-bit environment, it will be "016" meaning left pad zero until 16 number position.
|
||||
*/
|
||||
#define PRI_XPTR_LEFT_PADDING "016"
|
||||
#else
|
||||
#error "Not supported pointer size."
|
||||
#endif
|
||||
|
||||
/// @brief C++ standard deleter for std::FILE*
|
||||
class StdFileDeleter {
|
||||
public:
|
||||
StdFileDeleter() {}
|
||||
void operator() (std::FILE* ptr) {
|
||||
if (ptr != nullptr) {
|
||||
std::fclose(ptr);
|
||||
}
|
||||
}
|
||||
};
|
||||
/// @brief Smart unique pointer of \c std::FILE*
|
||||
using SmartStdFile = std::unique_ptr<std::FILE, StdFileDeleter>;
|
||||
|
||||
/**
|
||||
* @brief The UTF8 version of \c std::fopen.
|
||||
* @param[in] u8_filepath The UTF8 encoded path to the file to be opened.
|
||||
* @param[in] u8_mode UTF8 encoded mode string of the file to be opened.
|
||||
* @remarks
|
||||
* This function is suit for Windows because std::fopen do not support UTF8 on Windows.
|
||||
* On other platforms, this function will delegate request directly to std::fopen.
|
||||
* @return \c FILE* of the file to be opened, or nullptr if failed.
|
||||
*/
|
||||
std::FILE* UTF8FOpen(const yycc_char8_t* u8_filepath, const yycc_char8_t* u8_mode);
|
||||
|
||||
}
|
@ -1,221 +0,0 @@
|
||||
#pragma once
|
||||
#include "YYCCInternal.hpp"
|
||||
|
||||
#include "EncodingHelper.hpp"
|
||||
#include "StringHelper.hpp"
|
||||
#include <string>
|
||||
#include <cinttypes>
|
||||
#include <type_traits>
|
||||
#include <stdexcept>
|
||||
#include <charconv>
|
||||
#include <array>
|
||||
|
||||
/**
|
||||
* @brief The helper involving convertion between arithmetic types (integral, floating point and bool) and string
|
||||
* @details
|
||||
* See also \ref parser_helper.
|
||||
*/
|
||||
namespace YYCC::ParserHelper {
|
||||
|
||||
// Reference: https://zh.cppreference.com/w/cpp/utility/from_chars
|
||||
|
||||
/**
|
||||
* @brief Try parsing given string to floating point types.
|
||||
* @tparam _Ty The type derived from floating point type.
|
||||
* @param[in] strl The string need to be parsed.
|
||||
* @param[out] num
|
||||
* The variable receiving result.
|
||||
* There is no guarantee that the content is not modified when parsing failed.
|
||||
* @param[in] fmt The floating point format used when try parsing.
|
||||
* @return True if success, otherwise false.
|
||||
*/
|
||||
template<typename _Ty, std::enable_if_t<std::is_floating_point_v<_Ty>, int> = 0>
|
||||
bool TryParse(const yycc_u8string_view& strl, _Ty& num, std::chars_format fmt = std::chars_format::general) {
|
||||
auto [ptr, ec] = std::from_chars(
|
||||
EncodingHelper::ToOrdinary(strl.data()),
|
||||
EncodingHelper::ToOrdinary(strl.data() + strl.size()),
|
||||
num, fmt
|
||||
);
|
||||
if (ec == std::errc()) {
|
||||
// check whether the full string is matched
|
||||
return ptr == EncodingHelper::ToOrdinary(strl.data() + strl.size());
|
||||
} else if (ec == std::errc::invalid_argument) {
|
||||
// given string is invalid
|
||||
return false;
|
||||
} else if (ec == std::errc::result_out_of_range) {
|
||||
// given string is out of range
|
||||
return false;
|
||||
} else {
|
||||
// unreachable
|
||||
throw std::runtime_error("unreachable code.");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @brief Try parsing given string to integral types.
|
||||
* @tparam _Ty The type derived from integral type except bool type.
|
||||
* @param[in] strl The string need to be parsed.
|
||||
* @param[out] num
|
||||
* The variable receiving result.
|
||||
* There is no guarantee that the content is not modified when parsing failed.
|
||||
* @param[in] base Integer base to use: a value between 2 and 36 (inclusive).
|
||||
* @return True if success, otherwise false.
|
||||
*/
|
||||
template<typename _Ty, std::enable_if_t<std::is_integral_v<_Ty> && !std::is_same_v<_Ty, bool>, int> = 0>
|
||||
bool TryParse(const yycc_u8string_view& strl, _Ty& num, int base = 10) {
|
||||
auto [ptr, ec] = std::from_chars(
|
||||
EncodingHelper::ToOrdinary(strl.data()),
|
||||
EncodingHelper::ToOrdinary(strl.data() + strl.size()),
|
||||
num, base
|
||||
);
|
||||
if (ec == std::errc()) {
|
||||
// check whether the full string is matched
|
||||
return ptr == EncodingHelper::ToOrdinary(strl.data() + strl.size());
|
||||
} else if (ec == std::errc::invalid_argument) {
|
||||
// given string is invalid
|
||||
return false;
|
||||
} else if (ec == std::errc::result_out_of_range) {
|
||||
// given string is out of range
|
||||
return false;
|
||||
} else {
|
||||
// unreachable
|
||||
throw std::runtime_error("unreachable code.");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @brief Try parsing given string to bool types.
|
||||
* @tparam _Ty The type derived from bool type.
|
||||
* @param[in] strl The string need to be parsed ("true" or "false", case insensitive).
|
||||
* @param[out] num
|
||||
* The variable receiving result.
|
||||
* There is no guarantee that the content is not modified when parsing failed.
|
||||
* @return True if success, otherwise false.
|
||||
*/
|
||||
template<typename _Ty, std::enable_if_t<std::is_same_v<_Ty, bool>, int> = 0>
|
||||
bool TryParse(const yycc_u8string_view& strl, _Ty& num) {
|
||||
// get lower case
|
||||
yycc_u8string lower_case(strl);
|
||||
YYCC::StringHelper::Lower(lower_case);
|
||||
// compare result
|
||||
if (strl == YYCC_U8("true")) num = true;
|
||||
else if (strl == YYCC_U8("false")) num = false;
|
||||
else return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parse given string to floating point types.
|
||||
* @tparam _Ty The type derived from floating point type.
|
||||
* @param[in] strl The string need to be parsed.
|
||||
* @param[in] fmt The floating point format used when try parsing.
|
||||
* @return
|
||||
* The parsing result.
|
||||
* There is no guarantee about the content of this return value when parsing failed.
|
||||
* It may be any possible value but usually is its default value.
|
||||
*/
|
||||
template<typename _Ty, std::enable_if_t<std::is_floating_point_v<_Ty>, int> = 0>
|
||||
_Ty Parse(const yycc_u8string_view& strl, std::chars_format fmt = std::chars_format::general) {
|
||||
_Ty ret;
|
||||
TryParse(strl, ret, fmt);
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* @brief Parse given string to integral type types.
|
||||
* @tparam _Ty The type derived from integral type except bool type.
|
||||
* @param[in] strl The string need to be parsed.
|
||||
* @param[in] base Integer base to use: a value between 2 and 36 (inclusive).
|
||||
* @return
|
||||
* The parsing result.
|
||||
* There is no guarantee about the content of this return value when parsing failed.
|
||||
* It may be any possible value but usually is its default value.
|
||||
*/
|
||||
template<typename _Ty, std::enable_if_t<std::is_integral_v<_Ty> && !std::is_same_v<_Ty, bool>, int> = 0>
|
||||
_Ty Parse(const yycc_u8string_view& strl, int base = 10) {
|
||||
_Ty ret;
|
||||
TryParse(strl, ret, base);
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* @brief Parse given string to bool types.
|
||||
* @tparam _Ty The type derived from bool type.
|
||||
* @param[in] strl The string need to be parsed ("true" or "false", case insensitive).
|
||||
* @return
|
||||
* The parsing result.
|
||||
* There is no guarantee about the content of this return value when parsing failed.
|
||||
* It may be any possible value but usually is its default value.
|
||||
*/
|
||||
template<typename _Ty, std::enable_if_t<std::is_same_v<_Ty, bool>, int> = 0>
|
||||
_Ty Parse(const yycc_u8string_view& strl) {
|
||||
_Ty ret;
|
||||
TryParse(strl, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Reference: https://en.cppreference.com/w/cpp/utility/to_chars
|
||||
|
||||
/**
|
||||
* @brief Return the string representation of given floating point value.
|
||||
* @tparam _Ty The type derived from floating point type.
|
||||
* @param[in] num The value need to get string representation.
|
||||
* @param[in] fmt The floating point format used when getting string representation.
|
||||
* @param[in] precision The floating point precision used when getting string representation.
|
||||
* @return The string representation of given value.
|
||||
*/
|
||||
template<typename _Ty, std::enable_if_t<std::is_floating_point_v<_Ty>, int> = 0>
|
||||
yycc_u8string ToString(_Ty num, std::chars_format fmt = std::chars_format::general, int precision = 6) {
|
||||
// default precision = 6 is gotten from: https://en.cppreference.com/w/c/io/fprintf
|
||||
std::array<yycc_char8_t, 64> buffer;
|
||||
auto [ptr, ec] = std::to_chars(
|
||||
EncodingHelper::ToOrdinary(buffer.data()),
|
||||
EncodingHelper::ToOrdinary(buffer.data() + buffer.size()),
|
||||
num, fmt, precision
|
||||
);
|
||||
if (ec == std::errc()) {
|
||||
return yycc_u8string(buffer.data(), EncodingHelper::ToUTF8(ptr) - buffer.data());
|
||||
} else if (ec == std::errc::value_too_large) {
|
||||
// too short buffer
|
||||
// this should not happened
|
||||
throw std::out_of_range("ToString() buffer is not sufficient.");
|
||||
} else {
|
||||
// unreachable
|
||||
throw std::runtime_error("unreachable code.");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @brief Return the string representation of given integral value.
|
||||
* @tparam _Ty The type derived from integral type except bool type.
|
||||
* @param[in] num The value need to get string representation.
|
||||
* @param[in] base Integer base used when getting string representation: a value between 2 and 36 (inclusive).
|
||||
* @return The string representation of given value.
|
||||
*/
|
||||
template<typename _Ty, std::enable_if_t<std::is_integral_v<_Ty> && !std::is_same_v<_Ty, bool>, int> = 0>
|
||||
yycc_u8string ToString(_Ty num, int base = 10) {
|
||||
std::array<yycc_char8_t, 64> buffer;
|
||||
auto [ptr, ec] = std::to_chars(
|
||||
EncodingHelper::ToOrdinary(buffer.data()),
|
||||
EncodingHelper::ToOrdinary(buffer.data() + buffer.size()),
|
||||
num, base
|
||||
);
|
||||
if (ec == std::errc()) {
|
||||
return yycc_u8string(buffer.data(), EncodingHelper::ToUTF8(ptr) - buffer.data());
|
||||
} else if (ec == std::errc::value_too_large) {
|
||||
// too short buffer
|
||||
// this should not happened
|
||||
throw std::out_of_range("ToString() buffer is not sufficient.");
|
||||
} else {
|
||||
// unreachable
|
||||
throw std::runtime_error("unreachable code.");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @brief Return the string representation of given bool value.
|
||||
* @tparam _Ty The type derived from bool type.
|
||||
* @param[in] num The value need to get string representation.
|
||||
* @return The string representation of given value ("true" or "false").
|
||||
*/
|
||||
template<typename _Ty, std::enable_if_t<std::is_same_v<_Ty, bool>, int> = 0>
|
||||
yycc_u8string ToString(_Ty num) {
|
||||
if (num) return yycc_u8string(YYCC_U8("true"));
|
||||
else return yycc_u8string(YYCC_U8("false"));
|
||||
}
|
||||
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
#include "StdPatch.hpp"
|
||||
|
||||
#include "EncodingHelper.hpp"
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace YYCC::StdPatch {
|
||||
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
#include "YYCCInternal.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace YYCC::StdPatch {
|
||||
|
||||
/**
|
||||
* @brief Constructs \c std::filesystem::path from UTF8 path.
|
||||
* @param[in] u8_path UTF8 path string for building.
|
||||
* @return \c std::filesystem::path instance.
|
||||
* @exception std::invalid_argument Fail to parse given UTF8 string (maybe invalid?).
|
||||
*/
|
||||
std::filesystem::path ToStdPath(const yycc_u8string_view& u8_path);
|
||||
|
||||
/**
|
||||
* @brief Returns the UTF8 representation of given \c std::filesystem::path.
|
||||
* @param[in] path The \c std::filesystem::path instance converting to UTF8 path.
|
||||
* @return The UTF8 representation of given \c std::filesystem::path.
|
||||
* @exception std::invalid_argument Fail to convert to UTF8 string.
|
||||
*/
|
||||
yycc_u8string ToUTF8Path(const std::filesystem::path& path);
|
||||
|
||||
}
|
@ -1,115 +0,0 @@
|
||||
#include "WinFctHelper.hpp"
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
|
||||
#include "EncodingHelper.hpp"
|
||||
#include "COMHelper.hpp"
|
||||
|
||||
namespace YYCC::WinFctHelper {
|
||||
|
||||
HMODULE GetCurrentModule() {
|
||||
// Reference: https://stackoverflow.com/questions/557081/how-do-i-get-the-hmodule-for-the-currently-executing-code
|
||||
HMODULE hModule = NULL;
|
||||
::GetModuleHandleExW(
|
||||
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, // get address and do not inc ref counter.
|
||||
(LPCWSTR)GetCurrentModule,
|
||||
&hModule);
|
||||
|
||||
return hModule;
|
||||
}
|
||||
|
||||
bool GetTempDirectory(yycc_u8string& ret) {
|
||||
// create wchar buffer for receiving the temp path.
|
||||
std::wstring wpath(MAX_PATH + 1u, L'\0');
|
||||
DWORD expected_size;
|
||||
|
||||
// fetch temp folder
|
||||
while (true) {
|
||||
if ((expected_size = ::GetTempPathW(static_cast<DWORD>(wpath.size()), wpath.data())) == 0) {
|
||||
// failed, set to empty
|
||||
return false;
|
||||
}
|
||||
|
||||
if (expected_size > static_cast<DWORD>(wpath.size())) {
|
||||
// buffer is too short, need enlarge and do fetching again
|
||||
wpath.resize(expected_size);
|
||||
} else {
|
||||
// ok. shrink to real length, break while
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// resize result
|
||||
wpath.resize(expected_size);
|
||||
// convert to utf8 and return
|
||||
return YYCC::EncodingHelper::WcharToUTF8(wpath, ret);
|
||||
}
|
||||
|
||||
bool GetModuleFileName(HINSTANCE hModule, yycc_u8string& ret) {
|
||||
// create wchar buffer for receiving the temp path.
|
||||
std::wstring wpath(MAX_PATH + 1u, L'\0');
|
||||
DWORD copied_size;
|
||||
|
||||
while (true) {
|
||||
if ((copied_size = ::GetModuleFileNameW(hModule, wpath.data(), static_cast<DWORD>(wpath.size()))) == 0) {
|
||||
// failed, return
|
||||
return false;
|
||||
}
|
||||
|
||||
// check insufficient buffer
|
||||
if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
|
||||
// buffer is not enough, enlarge it and try again.
|
||||
wpath.resize(wpath.size() + MAX_PATH);
|
||||
} else {
|
||||
// ok, break while
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// resize result
|
||||
wpath.resize(copied_size);
|
||||
// convert to utf8 and return
|
||||
return YYCC::EncodingHelper::WcharToUTF8(wpath, ret);
|
||||
}
|
||||
|
||||
bool GetLocalAppData(yycc_u8string& ret) {
|
||||
// check whether com initialized
|
||||
if (!COMHelper::IsInitialized()) return false;
|
||||
|
||||
// fetch path
|
||||
LPWSTR _known_path;
|
||||
HRESULT hr = SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, NULL, &_known_path);
|
||||
if (FAILED(hr)) return false;
|
||||
COMHelper::SmartLPWSTR known_path(_known_path);
|
||||
|
||||
// convert to utf8
|
||||
return YYCC::EncodingHelper::WcharToUTF8(known_path.get(), ret);
|
||||
}
|
||||
|
||||
bool IsValidCodePage(UINT code_page) {
|
||||
CPINFOEXW cpinfo;
|
||||
return ::GetCPInfoExW(code_page, 0, &cpinfo);
|
||||
}
|
||||
|
||||
BOOL CopyFile(const yycc_u8string_view& lpExistingFileName, const yycc_u8string_view& lpNewFileName, BOOL bFailIfExists) {
|
||||
std::wstring wExistingFileName, wNewFileName;
|
||||
if (!YYCC::EncodingHelper::UTF8ToWchar(lpExistingFileName, wExistingFileName)) return FALSE;
|
||||
if (!YYCC::EncodingHelper::UTF8ToWchar(lpNewFileName, wNewFileName)) return FALSE;
|
||||
return ::CopyFileW(wExistingFileName.c_str(), wNewFileName.c_str(), bFailIfExists);
|
||||
}
|
||||
|
||||
BOOL MoveFile(const yycc_u8string_view& lpExistingFileName, const yycc_u8string_view& lpNewFileName) {
|
||||
std::wstring wExistingFileName, wNewFileName;
|
||||
if (!YYCC::EncodingHelper::UTF8ToWchar(lpExistingFileName, wExistingFileName)) return FALSE;
|
||||
if (!YYCC::EncodingHelper::UTF8ToWchar(lpNewFileName, wNewFileName)) return FALSE;
|
||||
return ::MoveFileW(wExistingFileName.c_str(), wNewFileName.c_str());
|
||||
}
|
||||
|
||||
BOOL DeleteFile(const yycc_u8string_view& lpFileName) {
|
||||
std::wstring wFileName;
|
||||
if (!YYCC::EncodingHelper::UTF8ToWchar(lpFileName, wFileName)) return FALSE;
|
||||
return ::DeleteFileW(wFileName.c_str());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,106 +0,0 @@
|
||||
#pragma once
|
||||
#include "YYCCInternal.hpp"
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "WinImportPrefix.hpp"
|
||||
#include <Windows.h>
|
||||
#include "WinImportSuffix.hpp"
|
||||
|
||||
/**
|
||||
* @brief The helper providing assistance of Win32 functions.
|
||||
* @details
|
||||
* This helper is Windows specific.
|
||||
* If current environment is not Windows, the whole namespace will be unavailable.
|
||||
* See also \ref win_fct_helper
|
||||
*/
|
||||
namespace YYCC::WinFctHelper {
|
||||
|
||||
/**
|
||||
* @brief Get Windows used HANDLE for current module.
|
||||
* @details
|
||||
* If your target is EXE, the current module simply is your program self.
|
||||
* However, if your target is DLL, the current module is your DLL, not the EXE loading your DLL.
|
||||
*
|
||||
* This function is frequently used by DLL.
|
||||
* Because some design need the HANDLE of current module, not the host EXE loading your DLL.
|
||||
* For example, you may want to get the path of your built DLL, or fetch resources from your DLL at runtime,
|
||||
* then you should pass current module HANDLE, not NULL or the HANDLE of EXE.
|
||||
* @return A Windows HANDLE pointing to current module, NULL if failed.
|
||||
*/
|
||||
HMODULE GetCurrentModule();
|
||||
|
||||
/**
|
||||
* @brief Get path to Windows temporary folder.
|
||||
* @details Windows temporary folder usually is the target of \%TEMP\%.
|
||||
* @param[out] ret The variable receiving UTF8 encoded path to Windows temp folder.
|
||||
* @return True if success, otherwise false.
|
||||
*/
|
||||
bool GetTempDirectory(yycc_u8string& ret);
|
||||
|
||||
/**
|
||||
* @brief Get the file name of given module HANDLE
|
||||
* @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 True if success, otherwise false.
|
||||
*/
|
||||
bool GetModuleFileName(HINSTANCE hModule, yycc_u8string& ret);
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
bool GetLocalAppData(yycc_u8string& ret);
|
||||
|
||||
/**
|
||||
* @brief Check whether given code page number is a valid one.
|
||||
* @param[in] code_page The code page number.
|
||||
* @return True if it is valid, otherwise false.
|
||||
*/
|
||||
bool IsValidCodePage(UINT code_page);
|
||||
|
||||
/**
|
||||
* @brief Copies an existing file to a new file.
|
||||
* @param lpExistingFileName The name of an existing file.
|
||||
* @param lpNewFileName The name of the new file.
|
||||
* @param bFailIfExists
|
||||
* If this parameter is TRUE and the new file specified by \c lpNewFileName already exists, the function fails.
|
||||
* If this parameter is FALSE and the new file already exists, the function overwrites the existing file and succeeds.
|
||||
* @return
|
||||
* If the function succeeds, the return value is nonzero.
|
||||
* If the function fails, the return value is zero. To get extended error information, call \c GetLastError.
|
||||
* @remarks Same as Windows \c CopyFile: https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-copyfilew
|
||||
*/
|
||||
BOOL CopyFile(const yycc_u8string_view& lpExistingFileName, const yycc_u8string_view& lpNewFileName, BOOL bFailIfExists);
|
||||
|
||||
/**
|
||||
* @brief Moves an existing file or a directory, including its children.
|
||||
* @param lpExistingFileName The current name of the file or directory on the local computer.
|
||||
* @param lpNewFileName
|
||||
* The new name for the file or directory. The new name must not already exist.
|
||||
* A new file may be on a different file system or drive. A new directory must be on the same drive.
|
||||
* @return
|
||||
* If the function succeeds, the return value is nonzero.
|
||||
* If the function fails, the return value is zero. To get extended error information, call \c GetLastError.
|
||||
* @remarks Same as Windows \c MoveFile: https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-movefilew
|
||||
*/
|
||||
BOOL MoveFile(const yycc_u8string_view& lpExistingFileName, const yycc_u8string_view& lpNewFileName);
|
||||
|
||||
/**
|
||||
* @brief Deletes an existing file.
|
||||
* @param lpFileName The name of the file to be deleted.
|
||||
* @return
|
||||
* If the function succeeds, the return value is nonzero.
|
||||
* If the function fails, the return value is zero. To get extended error information, call \c GetLastError.
|
||||
* @remarks Same as Windows \c DeleteFile: https://learn.microsoft.com/e-us/windows/win32/api/winbase/nf-winbase-deletefile
|
||||
*/
|
||||
BOOL DeleteFile(const yycc_u8string_view& lpFileName);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,147 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#pragma region Library Version and Comparison Macros
|
||||
|
||||
#include "YYCCVersion.hpp"
|
||||
|
||||
/// @brief Return true if left version number is equal to right version number, otherwise false.
|
||||
#define YYCC_VERCMP_E(av1, av2, av3, bv1, bv2, bv3) ((av1) == (bv1) && (av2) == (bv2) && (av3) == (bv3))
|
||||
/// @brief Return true if left version number is not equal to right version number, otherwise false.
|
||||
#define YYCC_VERCMP_NE(av1, av2, av3, bv1, bv2, bv3) (!YYCC_VERCMP_E(av1, av2, av3, bv1, bv2, bv3))
|
||||
/// @brief Return true if left version number is greater than right version number, otherwise false.
|
||||
#define YYCC_VERCMP_G(av1, av2, av3, bv1, bv2, bv3) ( \
|
||||
((av1) > (bv1)) || \
|
||||
((av1) == (bv1) && (av2) > (bv2)) || \
|
||||
((av1) == (bv1) && (av2) == (bv2) && (av3) > (bv3)) \
|
||||
)
|
||||
/// @brief Return true if left version number is greater than or equal to right version number, otherwise false.
|
||||
#define YYCC_VERCMP_GE(av1, av2, av3, bv1, bv2, bv3) (YYCC_VERCMP_G(av1, av2, av3, bv1, bv2, bv3) || YYCC_VERCMP_E(av1, av2, av3, bv1, bv2, bv3))
|
||||
/// @brief Return true if left version number is not lower than right version number, otherwise false.
|
||||
#define YYCC_VERCMP_NL(av1, av2, av3, bv1, bv2, bv3) YYCC_VERCMP_GE(av1, av2, av3, bv1, bv2, bv3)
|
||||
/// @brief Return true if left version number is lower than right version number, otherwise false.
|
||||
#define YYCC_VERCMP_L(av1, av2, av3, bv1, bv2, bv3) ( \
|
||||
((av1) < (bv1)) || \
|
||||
((av1) == (bv1) && (av2) < (bv2)) || \
|
||||
((av1) == (bv1) && (av2) == (bv2) && (av3) < (bv3)) \
|
||||
)
|
||||
/// @brief Return true if left version number is lower than or equal to right version number, otherwise false.
|
||||
#define YYCC_VERCMP_LE(av1, av2, av3, bv1, bv2, bv3) (YYCC_VERCMP_L(av1, av2, av3, bv1, bv2, bv3) || YYCC_VERCMP_E(av1, av2, av3, bv1, bv2, bv3))
|
||||
/// @brief Return true if left version number is not greater than right version number, otherwise false.
|
||||
#define YYCC_VERCMP_NG(av1, av2, av3, bv1, bv2, bv3) YYCC_VERCMP_LE(av1, av2, av3, bv1, bv2, bv3)
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Operating System Identifier Macros
|
||||
|
||||
// Define operating system macros
|
||||
#define YYCC_OS_WINDOWS 2
|
||||
#define YYCC_OS_LINUX 3
|
||||
// Check current operating system.
|
||||
#if defined(_WIN32)
|
||||
#define YYCC_OS YYCC_OS_WINDOWS
|
||||
#else
|
||||
#define YYCC_OS YYCC_OS_LINUX
|
||||
#endif
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Windows Shitty Behavior Disable Macros
|
||||
|
||||
// If we are in Windows,
|
||||
// we need add 2 macros to disable Windows shitty warnings and errors of
|
||||
// depracted functions and not secure functions.
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
|
||||
#if !defined(_CRT_SECURE_NO_WARNINGS)
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
#if !defined(_CRT_NONSTDC_NO_DEPRECATE)
|
||||
#define _CRT_NONSTDC_NO_DEPRECATE
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region YYCC UTF8 Types
|
||||
|
||||
// Define the UTF8 char type we used.
|
||||
// And do a polyfill if no embedded char8_t type.
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
/**
|
||||
* @brief Library core namespace
|
||||
* @details Almost library functions are located in this namespace.
|
||||
*/
|
||||
namespace YYCC {
|
||||
#if defined(__cpp_char8_t)
|
||||
using yycc_char8_t = char8_t;
|
||||
using yycc_u8string = std::u8string;
|
||||
using yycc_u8string_view = std::u8string_view;
|
||||
#else
|
||||
using yycc_char8_t = unsigned char;
|
||||
using yycc_u8string = std::basic_string<yycc_char8_t>;
|
||||
using yycc_u8string_view = std::basic_string_view<yycc_char8_t>;
|
||||
#endif
|
||||
}
|
||||
/**
|
||||
\typedef YYCC::yycc_char8_t
|
||||
\brief YYCC UTF8 char type.
|
||||
\details
|
||||
This char type is an alias to \c std::char8_t if your current C++ standard support it.
|
||||
Otherwise it is defined as <TT>unsigned char</TT> as C++ 20 stdandard does.
|
||||
*/
|
||||
/**
|
||||
\typedef YYCC::yycc_u8string
|
||||
\brief YYCC UTF8 string container type.
|
||||
\details
|
||||
This type is defined as \c std::basic_string<yycc_char8_t>.
|
||||
It is equal to \c std::u8string if your current C++ standard support it.
|
||||
*/
|
||||
/**
|
||||
\typedef YYCC::yycc_u8string_view
|
||||
\brief YYCC UTF8 string view type.
|
||||
\details
|
||||
This type is defined as \c std::basic_string_view<yycc_char8_t>.
|
||||
It is equal to \c std::u8string_view if your current C++ standard support it.
|
||||
*/
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Batch Class Move / Copy Function Macros
|
||||
|
||||
/// @brief Explicitly remove copy (\c constructor and \c operator\=) for given class.
|
||||
#define YYCC_DEL_CLS_COPY(CLSNAME) \
|
||||
CLSNAME(const CLSNAME&) = delete; \
|
||||
CLSNAME& operator=(const CLSNAME&) = delete;
|
||||
|
||||
/// @brief Explicitly remove move (\c constructor and \c operator\=) for given class.
|
||||
#define YYCC_DEL_CLS_MOVE(CLSNAME) \
|
||||
CLSNAME(CLSNAME&&) = delete; \
|
||||
CLSNAME& operator=(CLSNAME&&) = delete;
|
||||
|
||||
/// @brief Explicitly remove (copy and move) (\c constructor and \c operator\=) for given class.
|
||||
#define YYCC_DEL_CLS_COPY_MOVE(CLSNAME) \
|
||||
YYCC_DEL_CLS_COPY(CLSNAME) \
|
||||
YYCC_DEL_CLS_MOVE(CLSNAME)
|
||||
|
||||
/// @brief Explicitly set default copy (\c constructor and \c operator\=) for given class.
|
||||
#define YYCC_DEF_CLS_COPY(CLSNAME) \
|
||||
CLSNAME(const CLSNAME&) = default; \
|
||||
CLSNAME& operator=(const CLSNAME&) = default;
|
||||
|
||||
/// @brief Explicitly set default move (\c constructor and \c operator\=) for given class.
|
||||
#define YYCC_DEF_CLS_MOVE(CLSNAME) \
|
||||
CLSNAME(CLSNAME&&) = default; \
|
||||
CLSNAME& operator=(CLSNAME&&) = default;
|
||||
|
||||
/// @brief Explicitly set default (copy and move) (\c constructor and \c operator\=) for given class.
|
||||
#define YYCC_DEF_CLS_COPY_MOVE(CLSNAME) \
|
||||
YYCC_DEF_CLS_COPY(CLSNAME) \
|
||||
YYCC_DEF_CLS_MOVE(CLSNAME)
|
||||
|
||||
#pragma endregion
|
||||
|
||||
|
@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "YYCC/YYCCInternal.hpp"
|
||||
|
||||
#include "YYCC/EncodingHelper.hpp"
|
||||
#include "YYCC/StringHelper.hpp"
|
||||
#include "YYCC/ConsoleHelper.hpp"
|
||||
#include "YYCC/COMHelper.hpp"
|
||||
#include "YYCC/DialogHelper.hpp"
|
||||
#include "YYCC/ParserHelper.hpp"
|
||||
#include "YYCC/IOHelper.hpp"
|
||||
#include "YYCC/WinFctHelper.hpp"
|
||||
#include "YYCC/StdPatch.hpp"
|
||||
#include "YYCC/EnumHelper.hpp"
|
||||
#include "YYCC/ExceptionHelper.hpp"
|
||||
|
||||
#include "YYCC/ConfigManager.hpp"
|
||||
#include "YYCC/ArgParser.hpp"
|
28
src/yycc.hpp
28
src/yycc.hpp
@ -1,16 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
// Library Version and Comparison Macros
|
||||
/**
|
||||
* @file
|
||||
* When you use YYCCommonplace, please make sure that you include this header first,
|
||||
* before including any other headers of YYCC.
|
||||
* This header contain essential check macros and version infos.
|
||||
* They are crucial before using YYCC.
|
||||
*/
|
||||
|
||||
// Library version
|
||||
#include "yycc/version.hpp"
|
||||
#include "yycc/macro/version_cmp.hpp"
|
||||
|
||||
// Operating System Identifier Macros
|
||||
// Detect essential macros
|
||||
// Operating System macros
|
||||
#include "yycc/macro/os_detector.hpp"
|
||||
|
||||
// Windows Shitty Behavior Disable Macros
|
||||
#include "yycc/windows/unsafe_suppressor.hpp"
|
||||
|
||||
// Batch Class Move / Copy Function Macros
|
||||
#include "yycc/macro/class_copy_move.hpp"
|
||||
// Compiler macros
|
||||
#include "yycc/macro/compiler_detector.hpp"
|
||||
// Endian macros
|
||||
#include "yycc/macro/endian_detector.hpp"
|
||||
// Pointer size macros
|
||||
#include "yycc/macro/ptr_size_detector.hpp"
|
||||
// STL macros
|
||||
#include "yycc/macro/stl_detector.hpp"
|
||||
|
||||
namespace yycc {}
|
||||
|
247
src/yycc/carton/csconsole.cpp
Normal file
247
src/yycc/carton/csconsole.cpp
Normal file
@ -0,0 +1,247 @@
|
||||
#include "csconsole.hpp"
|
||||
#include "../macro/os_detector.hpp"
|
||||
|
||||
#include "../string/op.hpp"
|
||||
#include "../string/reinterpret.hpp"
|
||||
#include "../encoding/windows.hpp"
|
||||
#include <iostream>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
#include "../windows/import_guard_head.hpp"
|
||||
#include <Windows.h>
|
||||
#include "../windows/import_guard_tail.hpp"
|
||||
#endif
|
||||
|
||||
#define OP ::yycc::string::op
|
||||
#define REINTERPRET ::yycc::string::reinterpret
|
||||
#define ENC ::yycc::encoding::windows
|
||||
|
||||
namespace yycc::carton::csconsole {
|
||||
|
||||
#pragma region Windows Console Specific Functions
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
|
||||
template<bool BIsConsole>
|
||||
static std::u8string win_console_read(HANDLE hStdIn) {
|
||||
using TChar = std::conditional_t<BIsConsole, wchar_t, char>;
|
||||
|
||||
// Prepare an internal buffer because the read data may not be fully used.
|
||||
// For example, we may read x\ny in a single calling but after processing \n, this function will return
|
||||
// so y will temporarily stored in this internal buffer for next using.
|
||||
// Thus this function is not thread safe.
|
||||
static std::basic_string<TChar> internal_buffer;
|
||||
// create return value buffer
|
||||
std::basic_string<TChar> return_buffer;
|
||||
|
||||
// Prepare some variables
|
||||
DWORD dwReadNumberOfChars;
|
||||
TChar szReadChars[64];
|
||||
size_t eol_pos;
|
||||
|
||||
// try fetching EOL
|
||||
while (true) {
|
||||
// if internal buffer is empty,
|
||||
// try fetching it.
|
||||
if (internal_buffer.empty()) {
|
||||
// console and non-console use different method to read.
|
||||
if constexpr (BIsConsole) {
|
||||
// console handle, use ReadConsoleW.
|
||||
// read from console, the read data is wchar based
|
||||
if (!ReadConsoleW(hStdIn, szReadChars, sizeof(szReadChars) / sizeof(TChar), &dwReadNumberOfChars, NULL)) break;
|
||||
} else {
|
||||
// anything else, use ReadFile instead.
|
||||
// the read data is utf8 based
|
||||
if (!ReadFile(hStdIn, szReadChars, sizeof(szReadChars), &dwReadNumberOfChars, NULL)) break;
|
||||
}
|
||||
|
||||
// send to internal buffer
|
||||
if (dwReadNumberOfChars == 0) break;
|
||||
internal_buffer.append(szReadChars, dwReadNumberOfChars);
|
||||
}
|
||||
|
||||
// try finding EOL in internal buffer
|
||||
if constexpr (std::is_same_v<TChar, char>) eol_pos = internal_buffer.find_first_of('\n');
|
||||
else eol_pos = internal_buffer.find_first_of(L'\n');
|
||||
// check finding result
|
||||
if (eol_pos == std::wstring::npos) {
|
||||
// the whole string do not include EOL, fully appended to return value
|
||||
return_buffer += internal_buffer;
|
||||
internal_buffer.clear();
|
||||
// need more data, continue while
|
||||
} else {
|
||||
// split result
|
||||
// push into result and remain some in internal buffer.
|
||||
return_buffer.append(internal_buffer, 0u, eol_pos);
|
||||
internal_buffer.erase(0u, eol_pos + 1u); // +1 because EOL take one place.
|
||||
// break while mean success finding
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// post-process for return value
|
||||
std::u8string real_return_buffer;
|
||||
if constexpr (BIsConsole) {
|
||||
// console mode need convert wchar to utf8
|
||||
auto rv = ENC::to_utf8(return_buffer);
|
||||
if (rv.has_value()) real_return_buffer = std::move(rv.value());
|
||||
} else {
|
||||
// non-console just copt the result
|
||||
real_return_buffer = REINTERPRET::as_utf8(return_buffer);
|
||||
}
|
||||
// every mode need delete \r words
|
||||
OP::replace(real_return_buffer, u8"\r", u8"");
|
||||
// return value
|
||||
return real_return_buffer;
|
||||
}
|
||||
|
||||
static void win_console_write(const std::u8string_view& strl, bool to_stderr) {
|
||||
// Prepare some Win32 variables
|
||||
// fetch stdout handle first
|
||||
HANDLE hStdOut = GetStdHandle(to_stderr ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE);
|
||||
DWORD dwConsoleMode;
|
||||
DWORD dwWrittenNumberOfChars;
|
||||
|
||||
// if stdout was redirected, this handle may point to a file handle or anything else,
|
||||
// WriteConsoleW can not write data into such scenario, so we need check whether this handle is console handle
|
||||
if (GetConsoleMode(hStdOut, &dwConsoleMode)) {
|
||||
// console handle, use WriteConsoleW.
|
||||
// convert utf8 string to wide char first
|
||||
auto rv = ENC::to_wchar(strl);
|
||||
if (!rv.has_value()) return;
|
||||
std::wstring wstrl(std::move(rv.value()));
|
||||
size_t wstrl_size = wstrl.size();
|
||||
// write string with size check
|
||||
if (wstrl_size <= std::numeric_limits<DWORD>::max()) {
|
||||
WriteConsoleW(hStdOut, wstrl.c_str(), static_cast<DWORD>(wstrl_size), &dwWrittenNumberOfChars, NULL);
|
||||
}
|
||||
} else {
|
||||
// anything else, use WriteFile instead.
|
||||
// WriteFile do not need extra convertion, because it is direct writing.
|
||||
// check whether string length is overflow
|
||||
size_t strl_size = strl.size() * sizeof(std::u8string_view::value_type);
|
||||
// write string with size check
|
||||
if (strl_size <= std::numeric_limits<DWORD>::max()) {
|
||||
WriteFile(hStdOut, strl.data(), static_cast<DWORD>(strl_size), &dwWrittenNumberOfChars, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Read Functions
|
||||
|
||||
std::u8string read_line() {
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
|
||||
// get stdin mode
|
||||
HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
|
||||
// use different method to get according to whether stdin is redirected
|
||||
DWORD dwConsoleMode;
|
||||
if (GetConsoleMode(hStdIn, &dwConsoleMode)) {
|
||||
return win_console_read<true>(hStdIn);
|
||||
} else {
|
||||
return win_console_read<false>(hStdIn);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// in linux, directly use C++ function to fetch.
|
||||
std::string cmd;
|
||||
if (std::getline(std::cin, cmd).fail()) cmd.clear();
|
||||
return REINTERPRET::as_utf8(cmd);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Write Functions
|
||||
|
||||
template<bool BNeedFmt, bool BIsErr, bool BHasEOL>
|
||||
static void raw_write(const char8_t* u8_fmt, va_list argptr) {
|
||||
// Buiild string need to be written first
|
||||
// If no format string or plain string for writing, return.
|
||||
if (u8_fmt == nullptr) return;
|
||||
// Build or simply copy string
|
||||
std::u8string strl;
|
||||
if constexpr (BNeedFmt) {
|
||||
// treat as format string
|
||||
va_list argcpy;
|
||||
va_copy(argcpy, argptr);
|
||||
auto rv = OP::vprintf(u8_fmt, argcpy);
|
||||
va_end(argcpy);
|
||||
// check format result
|
||||
if (!rv.has_value()) return;
|
||||
else strl = std::move(rv.value());
|
||||
} else {
|
||||
// treat as plain string
|
||||
strl = u8_fmt;
|
||||
}
|
||||
// Checkout whether add EOL
|
||||
if constexpr (BHasEOL) {
|
||||
strl += u8'\n';
|
||||
}
|
||||
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
// call Windows specific writer
|
||||
win_console_write(strl, BIsErr);
|
||||
#else
|
||||
// in linux, directly use C function to write.
|
||||
std::fputs(REINTERPRET::as_ordinary(strl.c_str()), BIsErr ? stderr : stdout);
|
||||
#endif
|
||||
}
|
||||
|
||||
void format(const char8_t* u8_fmt, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, u8_fmt);
|
||||
raw_write<true, false, false>(u8_fmt, argptr);
|
||||
va_end(argptr);
|
||||
}
|
||||
|
||||
void format_line(const char8_t* u8_fmt, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, u8_fmt);
|
||||
raw_write<true, false, true>(u8_fmt, argptr);
|
||||
va_end(argptr);
|
||||
}
|
||||
|
||||
void write(const char8_t* u8_strl) {
|
||||
va_list empty{};
|
||||
raw_write<false, false, false>(u8_strl, empty);
|
||||
}
|
||||
|
||||
void write_line(const char8_t* u8_strl) {
|
||||
va_list empty{};
|
||||
raw_write<false, false, true>(u8_strl, empty);
|
||||
}
|
||||
|
||||
void eformat(const char8_t* u8_fmt, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, u8_fmt);
|
||||
raw_write<true, true, false>(u8_fmt, argptr);
|
||||
va_end(argptr);
|
||||
}
|
||||
|
||||
void eformat_line(const char8_t* u8_fmt, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, u8_fmt);
|
||||
raw_write<true, true, true>(u8_fmt, argptr);
|
||||
va_end(argptr);
|
||||
}
|
||||
|
||||
void ewrite(const char8_t* u8_strl) {
|
||||
va_list empty{};
|
||||
raw_write<false, true, false>(u8_strl, empty);
|
||||
}
|
||||
|
||||
void ewrite_line(const char8_t* u8_strl) {
|
||||
va_list empty{};
|
||||
raw_write<false, true, true>(u8_strl, empty);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
}
|
111
src/yycc/carton/csconsole.hpp
Normal file
111
src/yycc/carton/csconsole.hpp
Normal file
@ -0,0 +1,111 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* @brief The helper providing universal C\# style console function and other console related stuff
|
||||
* @details
|
||||
* The origin of this namespace is coming from the requirement of UTF8 console in Windows.
|
||||
* There are 3 ways to make Windows console enable UTF8 mode.
|
||||
*
|
||||
* First one is calling \c SetConsoleCP and \c SetConsoleOutputCP.
|
||||
* The side effect of this is \c std::cin and \c std::cout is broken,
|
||||
* however there is a patch for this issue.
|
||||
*
|
||||
* Second one is calling \c _set_mode with \c _O_U8TEXT or \c _O_U16TEXT to enable Unicode mode for Windows console.
|
||||
* This also have side effect which is stronger than first one.
|
||||
* All "puts" family functions (ASCII-based output functions) will throw assertion exception.
|
||||
* You only can use "putws" family functions (wide-char-based output functions).
|
||||
* However these functions can not be used without calling \c _set_mode in Windows design.
|
||||
*
|
||||
* There still is another method, using \c WriteConsoleW directly visiting console.
|
||||
* This function family can output correct string without calling any extra functions!
|
||||
* This method is what we adopted and finally become this namespace.
|
||||
*
|
||||
* Reference:
|
||||
* \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.
|
||||
*
|
||||
* @warning
|
||||
* All functions provided by this namespace are too aggressive.
|
||||
* Once you use it, you should not use any other input output functions.
|
||||
*
|
||||
* @deprecated
|
||||
* This namespace provided functions are too aggressive and can not cover all use scenario.
|
||||
* So I start to give this up when migrating this namespace during developing YYCC 2.x version.
|
||||
* I just do a simple type fix and rename when migrating this namespace to make it "just works".
|
||||
* There is no suggestion for using this namespace. And there is no update for this namespace.
|
||||
* Programmer should treat Windows UTF8 issue on their own.
|
||||
*/
|
||||
namespace yycc::carton::csconsole {
|
||||
|
||||
/**
|
||||
* @brief Reads the next line of UTF8 characters from the standard input stream.
|
||||
* @return
|
||||
* The next line of UTF8 characters from the input stream.
|
||||
* Empty string if user just press Enter key or function failed.
|
||||
*/
|
||||
std::u8string read_line();
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Writes the text representation of the specified object
|
||||
* to the standard output stream using the specified format information.
|
||||
* @param[in] u8_fmt The format string.
|
||||
* @param[in] ... The arguments of format string.
|
||||
*/
|
||||
void format(const char8_t* u8_fmt, ...);
|
||||
/**
|
||||
* @brief
|
||||
* Writes the text representation of the specified object,
|
||||
* followed by the current line terminator,
|
||||
* to the standard output stream using the specified format information.
|
||||
* @param[in] u8_fmt The format string.
|
||||
* @param[in] ... The arguments of format string.
|
||||
*/
|
||||
void format_line(const char8_t* u8_fmt, ...);
|
||||
/**
|
||||
* @brief Writes the specified string value to the standard output stream.
|
||||
* @param[in] u8_strl The value to write.
|
||||
*/
|
||||
void write(const char8_t* u8_strl);
|
||||
/**
|
||||
* @brief
|
||||
* Writes the specified string value, followed by the current line terminator,
|
||||
* to the standard output stream.
|
||||
* @param[in] u8_strl The value to write.
|
||||
*/
|
||||
void write_line(const char8_t* u8_strl);
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* Writes the text representation of the specified object
|
||||
* to the standard error stream using the specified format information.
|
||||
* @param[in] u8_fmt The format string.
|
||||
* @param[in] ... The arguments of format string.
|
||||
*/
|
||||
void eformat(const char8_t* u8_fmt, ...);
|
||||
/**
|
||||
* @brief
|
||||
* Writes the text representation of the specified object,
|
||||
* followed by the current line terminator,
|
||||
* to the standard error stream using the specified format information.
|
||||
* @param[in] u8_fmt The format string.
|
||||
* @param[in] ... The arguments of format string.
|
||||
*/
|
||||
void eformat_line(const char8_t* u8_fmt, ...);
|
||||
/**
|
||||
* @brief Writes the specified string value to the standard error stream.
|
||||
* @param[in] u8_strl The value to write.
|
||||
*/
|
||||
void ewrite(const char8_t* u8_strl);
|
||||
/**
|
||||
* @brief
|
||||
* Writes the specified string value, followed by the current line terminator,
|
||||
* to the standard error stream.
|
||||
* @param[in] u8_strl The value to write.
|
||||
*/
|
||||
void ewrite_line(const char8_t* u8_strl);
|
||||
|
||||
}
|
591
src/yycc/carton/pycodec.cpp
Normal file
591
src/yycc/carton/pycodec.cpp
Normal file
@ -0,0 +1,591 @@
|
||||
#include "pycodec.hpp"
|
||||
|
||||
#include "../string/op.hpp"
|
||||
#include <map>
|
||||
|
||||
using namespace std::literals::string_view_literals;
|
||||
namespace op = ::yycc::string::op;
|
||||
|
||||
namespace yycc::carton::pycodec {
|
||||
|
||||
/// @brief Error occurs when fetching token from encoding name.
|
||||
enum class FetchError {
|
||||
NoSuchName, ///< Given name can not be resolved.
|
||||
};
|
||||
|
||||
/// @brief The Result type used when fetching token from encoding name.
|
||||
template<typename T>
|
||||
using FetchResult = std::expected<T, FetchError>;
|
||||
|
||||
#pragma region Encoding Name
|
||||
|
||||
// clang-format off
|
||||
static const std::map<std::u8string_view, std::u8string_view> ALIAS_MAP{
|
||||
{u8"646"sv, u8"ascii"sv},
|
||||
{u8"us-ascii"sv, u8"ascii"sv},
|
||||
{u8"big5-tw"sv, u8"big5"sv},
|
||||
{u8"csbig5"sv, u8"big5"sv},
|
||||
{u8"big5-hkscs"sv, u8"big5hkscs"sv},
|
||||
{u8"hkscs"sv, u8"big5hkscs"sv},
|
||||
{u8"ibm037"sv, u8"cp037"sv},
|
||||
{u8"ibm039"sv, u8"cp037"sv},
|
||||
{u8"273"sv, u8"cp273"sv},
|
||||
{u8"ibm273"sv, u8"cp273"sv},
|
||||
{u8"csibm273"sv, u8"cp273"sv},
|
||||
{u8"ebcdic-cp-he"sv, u8"cp424"sv},
|
||||
{u8"ibm424"sv, u8"cp424"sv},
|
||||
{u8"437"sv, u8"cp437"sv},
|
||||
{u8"ibm437"sv, u8"cp437"sv},
|
||||
{u8"ebcdic-cp-be"sv, u8"cp500"sv},
|
||||
{u8"ebcdic-cp-ch"sv, u8"cp500"sv},
|
||||
{u8"ibm500"sv, u8"cp500"sv},
|
||||
{u8"ibm775"sv, u8"cp775"sv},
|
||||
{u8"850"sv, u8"cp850"sv},
|
||||
{u8"ibm850"sv, u8"cp850"sv},
|
||||
{u8"852"sv, u8"cp852"sv},
|
||||
{u8"ibm852"sv, u8"cp852"sv},
|
||||
{u8"855"sv, u8"cp855"sv},
|
||||
{u8"ibm855"sv, u8"cp855"sv},
|
||||
{u8"857"sv, u8"cp857"sv},
|
||||
{u8"ibm857"sv, u8"cp857"sv},
|
||||
{u8"858"sv, u8"cp858"sv},
|
||||
{u8"ibm858"sv, u8"cp858"sv},
|
||||
{u8"860"sv, u8"cp860"sv},
|
||||
{u8"ibm860"sv, u8"cp860"sv},
|
||||
{u8"861"sv, u8"cp861"sv},
|
||||
{u8"cp-is"sv, u8"cp861"sv},
|
||||
{u8"ibm861"sv, u8"cp861"sv},
|
||||
{u8"862"sv, u8"cp862"sv},
|
||||
{u8"ibm862"sv, u8"cp862"sv},
|
||||
{u8"863"sv, u8"cp863"sv},
|
||||
{u8"ibm863"sv, u8"cp863"sv},
|
||||
{u8"ibm864"sv, u8"cp864"sv},
|
||||
{u8"865"sv, u8"cp865"sv},
|
||||
{u8"ibm865"sv, u8"cp865"sv},
|
||||
{u8"866"sv, u8"cp866"sv},
|
||||
{u8"ibm866"sv, u8"cp866"sv},
|
||||
{u8"869"sv, u8"cp869"sv},
|
||||
{u8"cp-gr"sv, u8"cp869"sv},
|
||||
{u8"ibm869"sv, u8"cp869"sv},
|
||||
{u8"932"sv, u8"cp932"sv},
|
||||
{u8"ms932"sv, u8"cp932"sv},
|
||||
{u8"mskanji"sv, u8"cp932"sv},
|
||||
{u8"ms-kanji"sv, u8"cp932"sv},
|
||||
{u8"windows-31j"sv, u8"cp932"sv},
|
||||
{u8"949"sv, u8"cp949"sv},
|
||||
{u8"ms949"sv, u8"cp949"sv},
|
||||
{u8"uhc"sv, u8"cp949"sv},
|
||||
{u8"950"sv, u8"cp950"sv},
|
||||
{u8"ms950"sv, u8"cp950"sv},
|
||||
{u8"ibm1026"sv, u8"cp1026"sv},
|
||||
{u8"1125"sv, u8"cp1125"sv},
|
||||
{u8"ibm1125"sv, u8"cp1125"sv},
|
||||
{u8"cp866u"sv, u8"cp1125"sv},
|
||||
{u8"ruscii"sv, u8"cp1125"sv},
|
||||
{u8"ibm1140"sv, u8"cp1140"sv},
|
||||
{u8"windows-1250"sv, u8"cp1250"sv},
|
||||
{u8"windows-1251"sv, u8"cp1251"sv},
|
||||
{u8"windows-1252"sv, u8"cp1252"sv},
|
||||
{u8"windows-1253"sv, u8"cp1253"sv},
|
||||
{u8"windows-1254"sv, u8"cp1254"sv},
|
||||
{u8"windows-1255"sv, u8"cp1255"sv},
|
||||
{u8"windows-1256"sv, u8"cp1256"sv},
|
||||
{u8"windows-1257"sv, u8"cp1257"sv},
|
||||
{u8"windows-1258"sv, u8"cp1258"sv},
|
||||
{u8"eucjp"sv, u8"euc_jp"sv},
|
||||
{u8"ujis"sv, u8"euc_jp"sv},
|
||||
{u8"u-jis"sv, u8"euc_jp"sv},
|
||||
{u8"jisx0213"sv, u8"euc_jis_2004"sv},
|
||||
{u8"eucjis2004"sv, u8"euc_jis_2004"sv},
|
||||
{u8"eucjisx0213"sv, u8"euc_jisx0213"sv},
|
||||
{u8"euckr"sv, u8"euc_kr"sv},
|
||||
{u8"korean"sv, u8"euc_kr"sv},
|
||||
{u8"ksc5601"sv, u8"euc_kr"sv},
|
||||
{u8"ks_c-5601"sv, u8"euc_kr"sv},
|
||||
{u8"ks_c-5601-1987"sv, u8"euc_kr"sv},
|
||||
{u8"ksx1001"sv, u8"euc_kr"sv},
|
||||
{u8"ks_x-1001"sv, u8"euc_kr"sv},
|
||||
{u8"chinese"sv, u8"gb2312"sv},
|
||||
{u8"csiso58gb231280"sv, u8"gb2312"sv},
|
||||
{u8"euc-cn"sv, u8"gb2312"sv},
|
||||
{u8"euccn"sv, u8"gb2312"sv},
|
||||
{u8"eucgb2312-cn"sv, u8"gb2312"sv},
|
||||
{u8"gb2312-1980"sv, u8"gb2312"sv},
|
||||
{u8"gb2312-80"sv, u8"gb2312"sv},
|
||||
{u8"iso-ir-58"sv, u8"gb2312"sv},
|
||||
{u8"936"sv, u8"gbk"sv},
|
||||
{u8"cp936"sv, u8"gbk"sv},
|
||||
{u8"ms936"sv, u8"gbk"sv},
|
||||
{u8"gb18030-2000"sv, u8"gb18030"sv},
|
||||
{u8"hzgb"sv, u8"hz"sv},
|
||||
{u8"hz-gb"sv, u8"hz"sv},
|
||||
{u8"hz-gb-2312"sv, u8"hz"sv},
|
||||
{u8"csiso2022jp"sv, u8"iso2022_jp"sv},
|
||||
{u8"iso2022jp"sv, u8"iso2022_jp"sv},
|
||||
{u8"iso-2022-jp"sv, u8"iso2022_jp"sv},
|
||||
{u8"iso2022jp-1"sv, u8"iso2022_jp_1"sv},
|
||||
{u8"iso-2022-jp-1"sv, u8"iso2022_jp_1"sv},
|
||||
{u8"iso2022jp-2"sv, u8"iso2022_jp_2"sv},
|
||||
{u8"iso-2022-jp-2"sv, u8"iso2022_jp_2"sv},
|
||||
{u8"iso2022jp-2004"sv, u8"iso2022_jp_2004"sv},
|
||||
{u8"iso-2022-jp-2004"sv, u8"iso2022_jp_2004"sv},
|
||||
{u8"iso2022jp-3"sv, u8"iso2022_jp_3"sv},
|
||||
{u8"iso-2022-jp-3"sv, u8"iso2022_jp_3"sv},
|
||||
{u8"iso2022jp-ext"sv, u8"iso2022_jp_ext"sv},
|
||||
{u8"iso-2022-jp-ext"sv, u8"iso2022_jp_ext"sv},
|
||||
{u8"csiso2022kr"sv, u8"iso2022_kr"sv},
|
||||
{u8"iso2022kr"sv, u8"iso2022_kr"sv},
|
||||
{u8"iso-2022-kr"sv, u8"iso2022_kr"sv},
|
||||
{u8"iso-8859-1"sv, u8"latin_1"sv},
|
||||
{u8"iso8859-1"sv, u8"latin_1"sv},
|
||||
{u8"8859"sv, u8"latin_1"sv},
|
||||
{u8"cp819"sv, u8"latin_1"sv},
|
||||
{u8"latin"sv, u8"latin_1"sv},
|
||||
{u8"latin1"sv, u8"latin_1"sv},
|
||||
{u8"l1"sv, u8"latin_1"sv},
|
||||
{u8"iso-8859-2"sv, u8"iso8859_2"sv},
|
||||
{u8"latin2"sv, u8"iso8859_2"sv},
|
||||
{u8"l2"sv, u8"iso8859_2"sv},
|
||||
{u8"iso-8859-3"sv, u8"iso8859_3"sv},
|
||||
{u8"latin3"sv, u8"iso8859_3"sv},
|
||||
{u8"l3"sv, u8"iso8859_3"sv},
|
||||
{u8"iso-8859-4"sv, u8"iso8859_4"sv},
|
||||
{u8"latin4"sv, u8"iso8859_4"sv},
|
||||
{u8"l4"sv, u8"iso8859_4"sv},
|
||||
{u8"iso-8859-5"sv, u8"iso8859_5"sv},
|
||||
{u8"cyrillic"sv, u8"iso8859_5"sv},
|
||||
{u8"iso-8859-6"sv, u8"iso8859_6"sv},
|
||||
{u8"arabic"sv, u8"iso8859_6"sv},
|
||||
{u8"iso-8859-7"sv, u8"iso8859_7"sv},
|
||||
{u8"greek"sv, u8"iso8859_7"sv},
|
||||
{u8"greek8"sv, u8"iso8859_7"sv},
|
||||
{u8"iso-8859-8"sv, u8"iso8859_8"sv},
|
||||
{u8"hebrew"sv, u8"iso8859_8"sv},
|
||||
{u8"iso-8859-9"sv, u8"iso8859_9"sv},
|
||||
{u8"latin5"sv, u8"iso8859_9"sv},
|
||||
{u8"l5"sv, u8"iso8859_9"sv},
|
||||
{u8"iso-8859-10"sv, u8"iso8859_10"sv},
|
||||
{u8"latin6"sv, u8"iso8859_10"sv},
|
||||
{u8"l6"sv, u8"iso8859_10"sv},
|
||||
{u8"iso-8859-11"sv, u8"iso8859_11"sv},
|
||||
{u8"thai"sv, u8"iso8859_11"sv},
|
||||
{u8"iso-8859-13"sv, u8"iso8859_13"sv},
|
||||
{u8"latin7"sv, u8"iso8859_13"sv},
|
||||
{u8"l7"sv, u8"iso8859_13"sv},
|
||||
{u8"iso-8859-14"sv, u8"iso8859_14"sv},
|
||||
{u8"latin8"sv, u8"iso8859_14"sv},
|
||||
{u8"l8"sv, u8"iso8859_14"sv},
|
||||
{u8"iso-8859-15"sv, u8"iso8859_15"sv},
|
||||
{u8"latin9"sv, u8"iso8859_15"sv},
|
||||
{u8"l9"sv, u8"iso8859_15"sv},
|
||||
{u8"iso-8859-16"sv, u8"iso8859_16"sv},
|
||||
{u8"latin10"sv, u8"iso8859_16"sv},
|
||||
{u8"l10"sv, u8"iso8859_16"sv},
|
||||
{u8"cp1361"sv, u8"johab"sv},
|
||||
{u8"ms1361"sv, u8"johab"sv},
|
||||
{u8"kz_1048"sv, u8"kz1048"sv},
|
||||
{u8"strk1048_2002"sv, u8"kz1048"sv},
|
||||
{u8"rk1048"sv, u8"kz1048"sv},
|
||||
{u8"maccyrillic"sv, u8"mac_cyrillic"sv},
|
||||
{u8"macgreek"sv, u8"mac_greek"sv},
|
||||
{u8"maciceland"sv, u8"mac_iceland"sv},
|
||||
{u8"maclatin2"sv, u8"mac_latin2"sv},
|
||||
{u8"maccentraleurope"sv, u8"mac_latin2"sv},
|
||||
{u8"mac_centeuro"sv, u8"mac_latin2"sv},
|
||||
{u8"macroman"sv, u8"mac_roman"sv},
|
||||
{u8"macintosh"sv, u8"mac_roman"sv},
|
||||
{u8"macturkish"sv, u8"mac_turkish"sv},
|
||||
{u8"csptcp154"sv, u8"ptcp154"sv},
|
||||
{u8"pt154"sv, u8"ptcp154"sv},
|
||||
{u8"cp154"sv, u8"ptcp154"sv},
|
||||
{u8"cyrillic-asian"sv, u8"ptcp154"sv},
|
||||
{u8"csshiftjis"sv, u8"shift_jis"sv},
|
||||
{u8"shiftjis"sv, u8"shift_jis"sv},
|
||||
{u8"sjis"sv, u8"shift_jis"sv},
|
||||
{u8"s_jis"sv, u8"shift_jis"sv},
|
||||
{u8"shiftjis2004"sv, u8"shift_jis_2004"sv},
|
||||
{u8"sjis_2004"sv, u8"shift_jis_2004"sv},
|
||||
{u8"sjis2004"sv, u8"shift_jis_2004"sv},
|
||||
{u8"shiftjisx0213"sv, u8"shift_jisx0213"sv},
|
||||
{u8"sjisx0213"sv, u8"shift_jisx0213"sv},
|
||||
{u8"s_jisx0213"sv, u8"shift_jisx0213"sv},
|
||||
{u8"u32"sv, u8"utf_32"sv},
|
||||
{u8"utf32"sv, u8"utf_32"sv},
|
||||
{u8"utf-32be"sv, u8"utf_32_be"sv},
|
||||
{u8"utf-32le"sv, u8"utf_32_le"sv},
|
||||
{u8"u16"sv, u8"utf_16"sv},
|
||||
{u8"utf16"sv, u8"utf_16"sv},
|
||||
{u8"utf-16be"sv, u8"utf_16_be"sv},
|
||||
{u8"utf-16le"sv, u8"utf_16_le"sv},
|
||||
{u8"u7"sv, u8"utf_7"sv},
|
||||
{u8"unicode-1-1-utf-7"sv, u8"utf_7"sv},
|
||||
{u8"u8"sv, u8"utf_8"sv},
|
||||
{u8"utf"sv, u8"utf_8"sv},
|
||||
{u8"utf8"sv, u8"utf_8"sv},
|
||||
{u8"utf-8"sv, u8"utf_8"sv},
|
||||
{u8"cp65001"sv, u8"utf_8"sv},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
/**
|
||||
* @brief Resolve encoding name alias and fetch real encoding name.
|
||||
* @param[in] lang The encoding name for finding.
|
||||
* @return
|
||||
* The given encoding name if given name not present in alias map.
|
||||
* Otherwise the found encoding name by given name.
|
||||
*/
|
||||
static std::u8string resolve_encoding_alias(const std::u8string_view& enc_name) {
|
||||
auto name = op::to_lower(enc_name);
|
||||
auto finder = ALIAS_MAP.find(name);
|
||||
if (finder == ALIAS_MAP.end()) {
|
||||
// not found, use original encoding name.
|
||||
return std::u8string(enc_name);
|
||||
} else {
|
||||
// found, use found encoding name.
|
||||
return std::u8string(finder->second);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(YYCC_PYCODEC_WIN32_BACKEND)
|
||||
|
||||
using CodePage = YYCC_PYCODEC_BACKEND_NS::CodePage;
|
||||
|
||||
// clang-format off
|
||||
static const std::map<std::u8string_view, CodePage> WINCP_MAP {
|
||||
{ u8"ascii"sv, static_cast<CodePage>(437u) },
|
||||
{ u8"big5"sv, static_cast<CodePage>(950u) },
|
||||
{ u8"cp037"sv, static_cast<CodePage>(037u) },
|
||||
{ u8"cp437"sv, static_cast<CodePage>(437u) },
|
||||
{ u8"cp500"sv, static_cast<CodePage>(500u) },
|
||||
{ u8"cp720"sv, static_cast<CodePage>(720u) },
|
||||
{ u8"cp737"sv, static_cast<CodePage>(737u) },
|
||||
{ u8"cp775"sv, static_cast<CodePage>(775u) },
|
||||
{ u8"cp850"sv, static_cast<CodePage>(850u) },
|
||||
{ u8"cp852"sv, static_cast<CodePage>(852u) },
|
||||
{ u8"cp855"sv, static_cast<CodePage>(855u) },
|
||||
{ u8"cp857"sv, static_cast<CodePage>(857u) },
|
||||
{ u8"cp858"sv, static_cast<CodePage>(858u) },
|
||||
{ u8"cp860"sv, static_cast<CodePage>(860u) },
|
||||
{ u8"cp861"sv, static_cast<CodePage>(861u) },
|
||||
{ u8"cp862"sv, static_cast<CodePage>(862u) },
|
||||
{ u8"cp863"sv, static_cast<CodePage>(863u) },
|
||||
{ u8"cp864"sv, static_cast<CodePage>(864u) },
|
||||
{ u8"cp865"sv, static_cast<CodePage>(865u) },
|
||||
{ u8"cp866"sv, static_cast<CodePage>(866u) },
|
||||
{ u8"cp869"sv, static_cast<CodePage>(869u) },
|
||||
{ u8"cp874"sv, static_cast<CodePage>(874u) },
|
||||
{ u8"cp875"sv, static_cast<CodePage>(875u) },
|
||||
{ u8"cp932"sv, static_cast<CodePage>(932u) },
|
||||
{ u8"cp949"sv, static_cast<CodePage>(949u) },
|
||||
{ u8"cp950"sv, static_cast<CodePage>(950u) },
|
||||
{ u8"cp1026"sv, static_cast<CodePage>(1026u) },
|
||||
{ u8"cp1140"sv, static_cast<CodePage>(1140u) },
|
||||
{ u8"cp1250"sv, static_cast<CodePage>(1250u) },
|
||||
{ u8"cp1251"sv, static_cast<CodePage>(1251u) },
|
||||
{ u8"cp1252"sv, static_cast<CodePage>(1252u) },
|
||||
{ u8"cp1253"sv, static_cast<CodePage>(1253u) },
|
||||
{ u8"cp1254"sv, static_cast<CodePage>(1254u) },
|
||||
{ u8"cp1255"sv, static_cast<CodePage>(1255u) },
|
||||
{ u8"cp1256"sv, static_cast<CodePage>(1256u) },
|
||||
{ u8"cp1257"sv, static_cast<CodePage>(1257u) },
|
||||
{ u8"cp1258"sv, static_cast<CodePage>(1258u) },
|
||||
{ u8"euc_jp"sv, static_cast<CodePage>(20932u) },
|
||||
{ u8"euc_kr"sv, static_cast<CodePage>(51949u) },
|
||||
{ u8"gb2312"sv, static_cast<CodePage>(936u) },
|
||||
{ u8"gbk"sv, static_cast<CodePage>(936u) },
|
||||
{ u8"gb18030"sv, static_cast<CodePage>(54936u) },
|
||||
{ u8"hz"sv, static_cast<CodePage>(52936u) },
|
||||
{ u8"iso2022_jp"sv, static_cast<CodePage>(50220u) },
|
||||
{ u8"iso2022_kr"sv, static_cast<CodePage>(50225u) },
|
||||
{ u8"latin_1"sv, static_cast<CodePage>(28591u) },
|
||||
{ u8"iso8859_2"sv, static_cast<CodePage>(28592u) },
|
||||
{ u8"iso8859_3"sv, static_cast<CodePage>(28593u) },
|
||||
{ u8"iso8859_4"sv, static_cast<CodePage>(28594u) },
|
||||
{ u8"iso8859_5"sv, static_cast<CodePage>(28595u) },
|
||||
{ u8"iso8859_6"sv, static_cast<CodePage>(28596u) },
|
||||
{ u8"iso8859_7"sv, static_cast<CodePage>(28597u) },
|
||||
{ u8"iso8859_8"sv, static_cast<CodePage>(28598u) },
|
||||
{ u8"iso8859_9"sv, static_cast<CodePage>(28599u) },
|
||||
{ u8"iso8859_13"sv, static_cast<CodePage>(28603u) },
|
||||
{ u8"iso8859_15"sv, static_cast<CodePage>(28605u) },
|
||||
{ u8"johab"sv, static_cast<CodePage>(1361u) },
|
||||
{ u8"mac_cyrillic"sv, static_cast<CodePage>(10007u) },
|
||||
{ u8"mac_greek"sv, static_cast<CodePage>(10006u) },
|
||||
{ u8"mac_iceland"sv, static_cast<CodePage>(10079u) },
|
||||
{ u8"mac_turkish"sv, static_cast<CodePage>(10081u) },
|
||||
{ u8"shift_jis"sv, static_cast<CodePage>(932u) },
|
||||
{ u8"utf_7"sv, static_cast<CodePage>(65000u) },
|
||||
{ u8"utf_8"sv, static_cast<CodePage>(65001u) },
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
static FetchResult<CodePage> fetch_code_page(const std::u8string_view& enc_name) {
|
||||
// resolve alias
|
||||
std::u8string resolved_name = resolve_encoding_alias(enc_name);
|
||||
// find code page
|
||||
op::lower(resolved_name);
|
||||
auto finder = WINCP_MAP.find(resolved_name);
|
||||
if (finder == WINCP_MAP.end()) return std::unexpected(FetchError::NoSuchName);
|
||||
// okey, we found it.
|
||||
return finder->second;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// clang-format off
|
||||
static const std::map<std::u8string_view, std::string_view> ICONV_MAP{
|
||||
{u8"ascii"sv, "ASCII"sv},
|
||||
{u8"big5"sv, "BIG5"sv},
|
||||
{u8"big5hkscs"sv, "BIG5-HKSCS"sv},
|
||||
{u8"cp850"sv, "CP850"sv},
|
||||
{u8"cp862"sv, "CP862"sv},
|
||||
{u8"cp866"sv, "CP866"sv},
|
||||
{u8"cp874"sv, "CP874"sv},
|
||||
{u8"cp932"sv, "CP932"sv},
|
||||
{u8"cp949"sv, "CP949"sv},
|
||||
{u8"cp950"sv, "CP950"sv},
|
||||
{u8"cp1250"sv, "CP1250"sv},
|
||||
{u8"cp1251"sv, "CP1251"sv},
|
||||
{u8"cp1252"sv, "CP1252"sv},
|
||||
{u8"cp1253"sv, "CP1253"sv},
|
||||
{u8"cp1254"sv, "CP1254"sv},
|
||||
{u8"cp1255"sv, "CP1255"sv},
|
||||
{u8"cp1256"sv, "CP1256"sv},
|
||||
{u8"cp1257"sv, "CP1257"sv},
|
||||
{u8"cp1258"sv, "CP1258"sv},
|
||||
{u8"euc_jp"sv, "EUC-JP"sv},
|
||||
{u8"euc_kr"sv, "EUC-KR"sv},
|
||||
{u8"gb2312"sv, "CP936"sv},
|
||||
{u8"gbk"sv, "GBK"sv},
|
||||
{u8"gb18030"sv, "GB18030"sv},
|
||||
{u8"hz"sv, "HZ"sv},
|
||||
{u8"iso2022_jp"sv, "ISO-2022-JP"sv},
|
||||
{u8"iso2022_jp_1"sv, "ISO-2022-JP-1"sv},
|
||||
{u8"iso2022_jp_2"sv, "ISO-2022-JP-2"sv},
|
||||
{u8"iso2022_kr"sv, "ISO-2022-KR"sv},
|
||||
{u8"latin_1"sv, "ISO-8859-1"sv},
|
||||
{u8"iso8859_2"sv, "ISO-8859-2"sv},
|
||||
{u8"iso8859_3"sv, "ISO-8859-3"sv},
|
||||
{u8"iso8859_4"sv, "ISO-8859-4"sv},
|
||||
{u8"iso8859_5"sv, "ISO-8859-5"sv},
|
||||
{u8"iso8859_6"sv, "ISO-8859-6"sv},
|
||||
{u8"iso8859_7"sv, "ISO-8859-7"sv},
|
||||
{u8"iso8859_8"sv, "ISO-8859-8"sv},
|
||||
{u8"iso8859_9"sv, "ISO-8859-9"sv},
|
||||
{u8"iso8859_10"sv, "ISO-8859-10"sv},
|
||||
{u8"iso8859_11"sv, "ISO-8859-11"sv},
|
||||
{u8"iso8859_13"sv, "ISO-8859-13"sv},
|
||||
{u8"iso8859_14"sv, "ISO-8859-14"sv},
|
||||
{u8"iso8859_15"sv, "ISO-8859-15"sv},
|
||||
{u8"iso8859_16"sv, "ISO-8859-16"sv},
|
||||
{u8"johab"sv, "JOHAB"sv},
|
||||
{u8"koi8_t"sv, "KOI8-T"sv},
|
||||
{u8"mac_cyrillic"sv, "MacCyrillic"sv},
|
||||
{u8"mac_greek"sv, "MacGreek"sv},
|
||||
{u8"mac_iceland"sv, "MacIceland"sv},
|
||||
{u8"mac_roman"sv, "MacRoman"sv},
|
||||
{u8"mac_turkish"sv, "MacTurkish"sv},
|
||||
{u8"ptcp154"sv, "PT154"sv},
|
||||
{u8"shift_jis"sv, "SHIFT_JIS"sv},
|
||||
{u8"utf_32"sv, "UTF-32"sv},
|
||||
{u8"utf_32_be"sv, "UTF-32BE"sv},
|
||||
{u8"utf_32_le"sv, "UTF-32LE"sv},
|
||||
{u8"utf_16"sv, "UTF16"sv},
|
||||
{u8"utf_16_be"sv, "UTF-16BE"sv},
|
||||
{u8"utf_16_le"sv, "UTF-16LE"sv},
|
||||
{u8"utf_7"sv, "UTF-7"sv},
|
||||
{u8"utf_8"sv, "UTF-8"sv},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
static FetchResult<std::string_view> fetch_iconv_name(const std::u8string_view& enc_name) {
|
||||
// resolve alias
|
||||
std::u8string resolved_name = resolve_encoding_alias(enc_name);
|
||||
// find code page
|
||||
op::lower(resolved_name);
|
||||
auto finder = ICONV_MAP.find(resolved_name);
|
||||
if (finder == ICONV_MAP.end()) return std::unexpected(FetchError::NoSuchName);
|
||||
// okey, we found it.
|
||||
return finder->second;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Misc
|
||||
|
||||
ConvError::ConvError(const ConvBackendError& err) : inner(err) {}
|
||||
|
||||
ConvError::ConvError(const ConvFrontendError& err) : inner(err) {}
|
||||
|
||||
ConvError::ConvError(ConvBackendError&& err) noexcept : inner(std::move(err)) {}
|
||||
|
||||
ConvError::ConvError(ConvFrontendError&& err) noexcept : inner(std::move(err)) {}
|
||||
|
||||
bool is_valid_encoding_name(const EncodingName& name) {
|
||||
#if defined(YYCC_PYCODEC_WIN32_BACKEND)
|
||||
return fetch_code_page(name).has_value();
|
||||
#else
|
||||
return fetch_iconv_name(name).has_value();
|
||||
#endif
|
||||
}
|
||||
|
||||
// YYC MARK:
|
||||
// Define a macro for following class ctor
|
||||
// We only need initialize member if we are in Iconv environment.
|
||||
#if defined(YYCC_PYCODEC_WIN32_BACKEND)
|
||||
#define CTOR_INITLIST_TYPE1
|
||||
#else
|
||||
#define CTOR_INITLIST_TYPE1 : inner()
|
||||
#endif
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Char -> UTF8
|
||||
|
||||
CharToUtf8::CharToUtf8(const EncodingName& name) : inner(std::nullopt) {
|
||||
#if defined(YYCC_PYCODEC_WIN32_BACKEND)
|
||||
auto rv = fetch_code_page(name);
|
||||
if (rv.has_value()) inner = rv.value();
|
||||
#else
|
||||
auto rv = fetch_iconv_name(name);
|
||||
if (rv.has_value()) inner = YYCC_PYCODEC_BACKEND_NS::CharToUtf8(rv.value());
|
||||
#endif
|
||||
}
|
||||
|
||||
CharToUtf8::~CharToUtf8() {}
|
||||
|
||||
ConvResult<std::u8string> CharToUtf8::to_utf8(const std::string_view& src) {
|
||||
if (!inner.has_value()) return std::unexpected(ConvFrontendError::NoSuchName);
|
||||
#if defined(YYCC_PYCODEC_WIN32_BACKEND)
|
||||
return YYCC_PYCODEC_BACKEND_NS::to_utf8(src, inner.value());
|
||||
#else
|
||||
return inner.value().to_utf8(src);
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region UTF8 -> Char
|
||||
|
||||
Utf8ToChar::Utf8ToChar(const EncodingName& name) : inner(std::nullopt) {
|
||||
#if defined(YYCC_PYCODEC_WIN32_BACKEND)
|
||||
auto rv = fetch_code_page(name);
|
||||
if (rv.has_value()) inner = rv.value();
|
||||
#else
|
||||
auto rv = fetch_iconv_name(name);
|
||||
if (rv.has_value()) inner = YYCC_PYCODEC_BACKEND_NS::Utf8ToChar(rv.value());
|
||||
#endif
|
||||
}
|
||||
|
||||
Utf8ToChar::~Utf8ToChar() {}
|
||||
|
||||
ConvResult<std::string> Utf8ToChar::to_char(const std::u8string_view& src) {
|
||||
if (!inner.has_value()) return std::unexpected(ConvFrontendError::NoSuchName);
|
||||
#if defined(YYCC_PYCODEC_WIN32_BACKEND)
|
||||
return YYCC_PYCODEC_BACKEND_NS::to_char(src, inner.value());
|
||||
#else
|
||||
return inner.value().to_char(src);
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region WChar -> UTF8
|
||||
|
||||
WcharToUtf8::WcharToUtf8() CTOR_INITLIST_TYPE1 {}
|
||||
|
||||
WcharToUtf8::~WcharToUtf8() {}
|
||||
|
||||
ConvResult<std::u8string> WcharToUtf8::to_utf8(const std::wstring_view& src) {
|
||||
#if defined(YYCC_PYCODEC_WIN32_BACKEND)
|
||||
return YYCC_PYCODEC_BACKEND_NS::to_utf8(src);
|
||||
#else
|
||||
return inner.to_utf8(src);
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region UTF8 -> WChar
|
||||
|
||||
Utf8ToWchar::Utf8ToWchar() CTOR_INITLIST_TYPE1 {}
|
||||
|
||||
Utf8ToWchar::~Utf8ToWchar() {}
|
||||
|
||||
ConvResult<std::wstring> Utf8ToWchar::to_wchar(const std::u8string_view& src) {
|
||||
#if defined(YYCC_PYCODEC_WIN32_BACKEND)
|
||||
return YYCC_PYCODEC_BACKEND_NS::to_wchar(src);
|
||||
#else
|
||||
return inner.to_wchar(src);
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region UTF8 -> UTF16
|
||||
|
||||
Utf8ToUtf16::Utf8ToUtf16() CTOR_INITLIST_TYPE1 {}
|
||||
|
||||
Utf8ToUtf16::~Utf8ToUtf16() {}
|
||||
|
||||
ConvResult<std::u16string> Utf8ToUtf16::to_utf16(const std::u8string_view& src) {
|
||||
#if defined(YYCC_PYCODEC_WIN32_BACKEND)
|
||||
return YYCC_PYCODEC_BACKEND_NS::to_utf16(src);
|
||||
#else
|
||||
return inner.to_utf16(src);
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region UTF16 -> UTF8
|
||||
|
||||
Utf16ToUtf8::Utf16ToUtf8() CTOR_INITLIST_TYPE1 {}
|
||||
|
||||
Utf16ToUtf8::~Utf16ToUtf8() {}
|
||||
|
||||
ConvResult<std::u8string> Utf16ToUtf8::to_utf8(const std::u16string_view& src) {
|
||||
#if defined(YYCC_PYCODEC_WIN32_BACKEND)
|
||||
return YYCC_PYCODEC_BACKEND_NS::to_utf8(src);
|
||||
#else
|
||||
return inner.to_utf8(src);
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region UTF8 -> UTF32
|
||||
|
||||
Utf8ToUtf32::Utf8ToUtf32() CTOR_INITLIST_TYPE1 {}
|
||||
|
||||
Utf8ToUtf32::~Utf8ToUtf32() {}
|
||||
|
||||
ConvResult<std::u32string> Utf8ToUtf32::to_utf32(const std::u8string_view& src) {
|
||||
#if defined(YYCC_PYCODEC_WIN32_BACKEND)
|
||||
return YYCC_PYCODEC_BACKEND_NS::to_utf32(src);
|
||||
#else
|
||||
return inner.to_utf32(src);
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region UTF32 -> UTF8
|
||||
|
||||
Utf32ToUtf8::Utf32ToUtf8() CTOR_INITLIST_TYPE1 {}
|
||||
|
||||
Utf32ToUtf8::~Utf32ToUtf8() {}
|
||||
|
||||
ConvResult<std::u8string> Utf32ToUtf8::to_utf8(const std::u32string_view& src) {
|
||||
#if defined(YYCC_PYCODEC_WIN32_BACKEND)
|
||||
return YYCC_PYCODEC_BACKEND_NS::to_utf8(src);
|
||||
#else
|
||||
return inner.to_utf8(src);
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace yycc::carton::pycodec
|
204
src/yycc/carton/pycodec.hpp
Normal file
204
src/yycc/carton/pycodec.hpp
Normal file
@ -0,0 +1,204 @@
|
||||
#pragma once
|
||||
#include "../macro/os_detector.hpp"
|
||||
#include "../macro/stl_detector.hpp"
|
||||
#include "../macro/class_copy_move.hpp"
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <variant>
|
||||
#include <optional>
|
||||
#include <expected>
|
||||
|
||||
// Choose the backend of PyCodec module
|
||||
#if defined(YYCC_FEAT_ICONV)
|
||||
// We try Iconv first in any cases.
|
||||
#include "../encoding/iconv.hpp"
|
||||
#define YYCC_PYCODEC_ICONV_BACKEND
|
||||
#define YYCC_PYCODEC_BACKEND_NS ::yycc::encoding::iconv
|
||||
#elif defined(YYCC_OS_WINDOWS) && defined(YYCC_STL_MSSTL)
|
||||
// If we can not use Iconv, we try to fallback to Windows implementation.
|
||||
#include "../encoding/windows.hpp"
|
||||
#define YYCC_PYCODEC_WIN32_BACKEND
|
||||
#define YYCC_PYCODEC_BACKEND_NS ::yycc::encoding::windows
|
||||
#else
|
||||
// No viable implementation.
|
||||
#error "Can not find viable encoding convertion solution in current environment for PyCodec module."
|
||||
#endif
|
||||
|
||||
namespace yycc::carton::pycodec {
|
||||
|
||||
/// @brief The universal name of encoding.
|
||||
using EncodingName = std::u8string_view;
|
||||
|
||||
/// @brief The alias to error type of backend.
|
||||
using ConvBackendError = YYCC_PYCODEC_BACKEND_NS::ConvError;
|
||||
|
||||
/// @brief The error occurs in this module self.
|
||||
enum class ConvFrontendError {
|
||||
NoSuchName, ///< Can not find suitable backend token for given encoding name.
|
||||
};
|
||||
|
||||
/// @brief The possible error occurs in this module.
|
||||
class ConvError {
|
||||
public:
|
||||
ConvError(const ConvBackendError& err);
|
||||
ConvError(const ConvFrontendError& err);
|
||||
ConvError(ConvBackendError&& err) noexcept;
|
||||
ConvError(ConvFrontendError&& err) noexcept;
|
||||
YYCC_DEFAULT_COPY_MOVE(ConvError)
|
||||
|
||||
private:
|
||||
std::variant<ConvBackendError, ConvFrontendError> inner;
|
||||
};
|
||||
|
||||
/// @brief The result type of this module.
|
||||
template<typename T>
|
||||
using ConvResult = std::expected<T, ConvError>;
|
||||
|
||||
/**
|
||||
* @brief Check whether given name is a valid encoding name in PyCodec.
|
||||
* @param[in] name The name to be checked.
|
||||
* @return True if it is valid, otherwise false.
|
||||
*/
|
||||
bool is_valid_encoding_name(const EncodingName& name);
|
||||
|
||||
/// @brief Char -> UTF8
|
||||
class CharToUtf8 {
|
||||
public:
|
||||
CharToUtf8(const EncodingName& name);
|
||||
~CharToUtf8();
|
||||
YYCC_DELETE_COPY(CharToUtf8)
|
||||
YYCC_DEFAULT_MOVE(CharToUtf8)
|
||||
|
||||
public:
|
||||
ConvResult<std::u8string> to_utf8(const std::string_view& src);
|
||||
|
||||
private:
|
||||
#if defined(YYCC_PYCODEC_WIN32_BACKEND)
|
||||
std::optional<YYCC_PYCODEC_BACKEND_NS::CodePage> inner;
|
||||
#else
|
||||
std::optional<YYCC_PYCODEC_BACKEND_NS::CharToUtf8> inner;
|
||||
#endif
|
||||
};
|
||||
|
||||
/// @brief UTF8 -> Char
|
||||
class Utf8ToChar {
|
||||
public:
|
||||
Utf8ToChar(const EncodingName& name);
|
||||
~Utf8ToChar();
|
||||
YYCC_DELETE_COPY(Utf8ToChar)
|
||||
YYCC_DEFAULT_MOVE(Utf8ToChar)
|
||||
|
||||
public:
|
||||
ConvResult<std::string> to_char(const std::u8string_view& src);
|
||||
|
||||
private:
|
||||
#if defined(YYCC_PYCODEC_WIN32_BACKEND)
|
||||
std::optional<YYCC_PYCODEC_BACKEND_NS::CodePage> inner;
|
||||
#else
|
||||
std::optional<YYCC_PYCODEC_BACKEND_NS::Utf8ToChar> inner;
|
||||
#endif
|
||||
};
|
||||
|
||||
/// @brief WChar -> UTF8
|
||||
class WcharToUtf8 {
|
||||
public:
|
||||
WcharToUtf8();
|
||||
~WcharToUtf8();
|
||||
YYCC_DELETE_COPY(WcharToUtf8)
|
||||
YYCC_DEFAULT_MOVE(WcharToUtf8)
|
||||
|
||||
public:
|
||||
ConvResult<std::u8string> to_utf8(const std::wstring_view& src);
|
||||
|
||||
private:
|
||||
#if defined(YYCC_PYCODEC_ICONV_BACKEND)
|
||||
YYCC_PYCODEC_BACKEND_NS::WcharToUtf8 inner;
|
||||
#endif
|
||||
};
|
||||
|
||||
/// @brief UTF8 -> WChar
|
||||
class Utf8ToWchar {
|
||||
public:
|
||||
Utf8ToWchar();
|
||||
~Utf8ToWchar();
|
||||
YYCC_DELETE_COPY(Utf8ToWchar)
|
||||
YYCC_DEFAULT_MOVE(Utf8ToWchar)
|
||||
|
||||
public:
|
||||
ConvResult<std::wstring> to_wchar(const std::u8string_view& src);
|
||||
|
||||
private:
|
||||
#if defined(YYCC_PYCODEC_ICONV_BACKEND)
|
||||
YYCC_PYCODEC_BACKEND_NS::Utf8ToWchar inner;
|
||||
#endif
|
||||
};
|
||||
|
||||
/// @brief UTF8 -> UTF16
|
||||
class Utf8ToUtf16 {
|
||||
public:
|
||||
Utf8ToUtf16();
|
||||
~Utf8ToUtf16();
|
||||
YYCC_DELETE_COPY(Utf8ToUtf16)
|
||||
YYCC_DEFAULT_MOVE(Utf8ToUtf16)
|
||||
|
||||
public:
|
||||
ConvResult<std::u16string> to_utf16(const std::u8string_view& src);
|
||||
|
||||
private:
|
||||
#if defined(YYCC_PYCODEC_ICONV_BACKEND)
|
||||
YYCC_PYCODEC_BACKEND_NS::Utf8ToUtf16 inner;
|
||||
#endif
|
||||
};
|
||||
|
||||
/// @brief UTF16 -> UTF8
|
||||
class Utf16ToUtf8 {
|
||||
public:
|
||||
Utf16ToUtf8();
|
||||
~Utf16ToUtf8();
|
||||
YYCC_DELETE_COPY(Utf16ToUtf8)
|
||||
YYCC_DEFAULT_MOVE(Utf16ToUtf8)
|
||||
|
||||
public:
|
||||
ConvResult<std::u8string> to_utf8(const std::u16string_view& src);
|
||||
|
||||
private:
|
||||
#if defined(YYCC_PYCODEC_ICONV_BACKEND)
|
||||
YYCC_PYCODEC_BACKEND_NS::Utf16ToUtf8 inner;
|
||||
#endif
|
||||
};
|
||||
|
||||
/// @brief UTF8 -> UTF32
|
||||
class Utf8ToUtf32 {
|
||||
public:
|
||||
Utf8ToUtf32();
|
||||
~Utf8ToUtf32();
|
||||
YYCC_DELETE_COPY(Utf8ToUtf32)
|
||||
YYCC_DEFAULT_MOVE(Utf8ToUtf32)
|
||||
|
||||
public:
|
||||
ConvResult<std::u32string> to_utf32(const std::u8string_view& src);
|
||||
|
||||
private:
|
||||
#if defined(YYCC_PYCODEC_ICONV_BACKEND)
|
||||
YYCC_PYCODEC_BACKEND_NS::Utf8ToUtf32 inner;
|
||||
#endif
|
||||
};
|
||||
|
||||
/// @brief UTF32 -> UTF8
|
||||
class Utf32ToUtf8 {
|
||||
public:
|
||||
Utf32ToUtf8();
|
||||
~Utf32ToUtf8();
|
||||
YYCC_DELETE_COPY(Utf32ToUtf8)
|
||||
YYCC_DEFAULT_MOVE(Utf32ToUtf8)
|
||||
|
||||
public:
|
||||
ConvResult<std::u8string> to_utf8(const std::u32string_view& src);
|
||||
|
||||
private:
|
||||
#if defined(YYCC_PYCODEC_ICONV_BACKEND)
|
||||
YYCC_PYCODEC_BACKEND_NS::Utf32ToUtf8 inner;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace yycc::carton::pycodec
|
192
src/yycc/carton/tabulate.cpp
Normal file
192
src/yycc/carton/tabulate.cpp
Normal file
@ -0,0 +1,192 @@
|
||||
#include "tabulate.hpp"
|
||||
#include "wcwidth.hpp"
|
||||
#include "../num/safe_op.hpp"
|
||||
#include "../string/reinterpret.hpp"
|
||||
#include <stdexcept>
|
||||
#include <ranges>
|
||||
|
||||
#define WCWIDTH ::yycc::carton::wcwidth
|
||||
#define REINTERPRET ::yycc::string::reinterpret
|
||||
#define SAFEOP ::yycc::num::safe_op
|
||||
|
||||
namespace yycc::carton::tabulate {
|
||||
|
||||
#pragma region Tabulate Width
|
||||
|
||||
TabulateWidth::TabulateWidth(size_t n) : widths(n, 0u) {}
|
||||
|
||||
TabulateWidth::~TabulateWidth() {}
|
||||
|
||||
size_t TabulateWidth::get_column_count() const {
|
||||
return widths.size();
|
||||
}
|
||||
|
||||
size_t TabulateWidth::get_column_width(size_t column_index) const {
|
||||
return widths.at(column_index);
|
||||
}
|
||||
|
||||
void TabulateWidth::update_column_width(size_t column_index, size_t new_size) {
|
||||
auto& width = widths.at(column_index);
|
||||
width = std::max(width, new_size);
|
||||
}
|
||||
|
||||
void TabulateWidth::clear() {
|
||||
std::fill(widths.begin(), widths.end(), 0u);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Tabulate Cell
|
||||
|
||||
TabulateCell::TabulateCell(const std::u8string_view& text) : text(text), text_width(WCWIDTH::wcswidth(text).value_or(0u)) {}
|
||||
|
||||
TabulateCell::~TabulateCell() {}
|
||||
|
||||
const std::u8string& TabulateCell::get_text() const {
|
||||
return text;
|
||||
}
|
||||
|
||||
size_t TabulateCell::get_text_width() const {
|
||||
return text_width;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Tabulate
|
||||
|
||||
/// @brief Default separator literal for Tabulate.
|
||||
static constexpr char8_t SEPARATOR_BAR[] = u8"---";
|
||||
/// @brief A stupid size_t ZERO literal to trigger template type deduce for std::views::iota.
|
||||
static constexpr size_t ZERO = 0;
|
||||
|
||||
Tabulate::Tabulate(size_t n) :
|
||||
n(n), header_display(true), bar_display(true), prefix_string(), rows_widths(n), header_widths(n), header(n, TabulateCell(u8"")),
|
||||
bar(SEPARATOR_BAR), rows() {}
|
||||
|
||||
Tabulate::~Tabulate() {}
|
||||
|
||||
void Tabulate::print(std::ostream& dst) const {
|
||||
// Get column count
|
||||
auto n = this->get_column_count();
|
||||
|
||||
// Create width recorder for final printing
|
||||
// according to whether we show table header and separator bar.
|
||||
auto widths = this->rows_widths;
|
||||
if (this->header_display) {
|
||||
for (auto index : std::views::iota(ZERO, n)) {
|
||||
widths.update_column_width(index, this->header_widths.get_column_width(index));
|
||||
}
|
||||
}
|
||||
if (this->bar_display) {
|
||||
auto bar_width = this->bar.get_text_width();
|
||||
for (auto index : std::views::iota(ZERO, n)) {
|
||||
widths.update_column_width(index, bar_width);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the maximum space char count to build a string filled with spaces,
|
||||
// for the convenient about following printing.
|
||||
size_t max_space = 1;
|
||||
for (auto index : std::views::iota(ZERO, n)) {
|
||||
max_space = std::max(max_space, widths.get_column_width(index));
|
||||
}
|
||||
std::u8string spaces(max_space, u8' ');
|
||||
std::u8string_view spaces_view(spaces);
|
||||
|
||||
// Print table
|
||||
// Define a convenient macro
|
||||
#define CVT(data) REINTERPRET::as_ordinary_view(data)
|
||||
// Show header
|
||||
if (this->header_display) {
|
||||
dst << CVT(this->prefix_string);
|
||||
for (const auto [index, item] : std::views::enumerate(header)) {
|
||||
auto diff = SAFEOP::saturating_sub(widths.get_column_width(index), item.get_text_width());
|
||||
dst << CVT(item.get_text()) << CVT(spaces_view.substr(0, diff)) << " ";
|
||||
}
|
||||
dst << std::endl;
|
||||
}
|
||||
// Show bar
|
||||
if (this->bar_display) {
|
||||
dst << CVT(this->prefix_string);
|
||||
auto bar_width = this->bar.get_text_width();
|
||||
for (auto index : std::views::iota(ZERO, n)) {
|
||||
auto diff = SAFEOP::saturating_sub(widths.get_column_width(index), bar_width);
|
||||
dst << CVT(this->bar.get_text()) << CVT(spaces_view.substr(0, diff)) << " ";
|
||||
}
|
||||
dst << std::endl;
|
||||
}
|
||||
// Show data
|
||||
for (const auto& row : this->rows) {
|
||||
dst << CVT(this->prefix_string);
|
||||
for (const auto [index, item] : std::views::enumerate(row)) {
|
||||
auto diff = SAFEOP::saturating_sub(widths.get_column_width(index), item.get_text_width());
|
||||
dst << CVT(item.get_text()) << CVT(spaces_view.substr(0, diff)) << " ";
|
||||
}
|
||||
dst << std::endl;
|
||||
}
|
||||
// Undef macro
|
||||
#undef CVT
|
||||
}
|
||||
|
||||
size_t Tabulate::get_column_count() const {
|
||||
return this->n;
|
||||
}
|
||||
|
||||
void Tabulate::show_header(bool show_header) {
|
||||
this->header_display = show_header;
|
||||
}
|
||||
|
||||
void Tabulate::show_bar(bool show_bar) {
|
||||
this->bar_display = show_bar;
|
||||
}
|
||||
|
||||
void Tabulate::set_prefix(const std::u8string_view& prefix) {
|
||||
this->prefix_string = prefix;
|
||||
}
|
||||
|
||||
void Tabulate::set_header(std::initializer_list<std::u8string_view> hdr) {
|
||||
// Check data size.
|
||||
if (hdr.size() != get_column_count()) {
|
||||
throw std::invalid_argument("the size of given header is not equal to column count");
|
||||
}
|
||||
|
||||
// Change header data and update header width recorder.
|
||||
header.clear();
|
||||
header_widths.clear();
|
||||
for (const auto [index, item] : std::views::enumerate(hdr)) {
|
||||
auto cell = header.emplace_back(item);
|
||||
header_widths.update_column_width(index, cell.get_text_width());
|
||||
}
|
||||
}
|
||||
|
||||
void Tabulate::set_bar(const std::u8string_view& bar) {
|
||||
this->bar = bar;
|
||||
}
|
||||
|
||||
void Tabulate::add_row(std::initializer_list<std::u8string_view> row) {
|
||||
// Check data size.
|
||||
if (row.size() != get_column_count()) {
|
||||
throw std::invalid_argument("the size of given row is not equal to column count");
|
||||
}
|
||||
|
||||
// Prepare inserted row, and update data width recorder.
|
||||
Row inserted_row;
|
||||
inserted_row.reserve(row.size());
|
||||
for (const auto [index, item] : std::views::enumerate(row)) {
|
||||
auto cell = inserted_row.emplace_back(item);
|
||||
rows_widths.update_column_width(index, cell.get_text_width());
|
||||
}
|
||||
|
||||
// Insert row
|
||||
rows.emplace_back(std::move(inserted_row));
|
||||
}
|
||||
|
||||
void Tabulate::clear() {
|
||||
// Clear data and data width recorder.
|
||||
rows.clear();
|
||||
rows_widths.clear();
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace yycc::carton::tabulate
|
198
src/yycc/carton/tabulate.hpp
Normal file
198
src/yycc/carton/tabulate.hpp
Normal file
@ -0,0 +1,198 @@
|
||||
#pragma once
|
||||
#include "../macro/class_copy_move.hpp"
|
||||
#include <initializer_list>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
namespace yycc::carton::tabulate {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @brief Assistant class recording column width.
|
||||
*/
|
||||
class TabulateWidth {
|
||||
public:
|
||||
/**
|
||||
* @brief Create width recorder with given column count.
|
||||
* @param[in] n Column count.
|
||||
*/
|
||||
TabulateWidth(size_t n);
|
||||
~TabulateWidth();
|
||||
YYCC_DEFAULT_COPY_MOVE(TabulateWidth)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Get column count.
|
||||
* @return Column count.
|
||||
*/
|
||||
size_t get_column_count() const;
|
||||
/**
|
||||
* @brief Get column width of given index.
|
||||
* @param[in] column_index Column index for fetching width.
|
||||
* @return Column width of given index.
|
||||
*/
|
||||
size_t get_column_width(size_t column_index) const;
|
||||
/**
|
||||
* @brief Update column width of given index with given value.
|
||||
* @details The width of column will be updated to the maximum between given value and old value.
|
||||
* @param[in] column_index Column index for updating width.
|
||||
* @param[in] new_size New width value.
|
||||
*/
|
||||
void update_column_width(size_t column_index, size_t new_size);
|
||||
/**
|
||||
* @brief Clear all width data
|
||||
* @details All width data will be reset to zero.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
private:
|
||||
std::vector<size_t> widths;
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @brief Assistant class holding table cell data.
|
||||
* @details
|
||||
* This class holds the data of table cell.
|
||||
* Also make a cache for the width this cell's text occupied in console,
|
||||
* to avoid duplicated calculation for occupied width.
|
||||
*/
|
||||
class TabulateCell {
|
||||
public:
|
||||
/**
|
||||
* @brief Build cell with given text.
|
||||
* @param[in] text Data of cell.
|
||||
*/
|
||||
TabulateCell(const std::u8string_view& text);
|
||||
~TabulateCell();
|
||||
YYCC_DEFAULT_COPY_MOVE(TabulateCell)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Get the text of cell.
|
||||
* @return The text of cell.
|
||||
*/
|
||||
const std::u8string& get_text() const;
|
||||
/**
|
||||
* @brief Get width this cell's text occupied in console.
|
||||
* @return The width this cell occupied.
|
||||
*/
|
||||
size_t get_text_width() const;
|
||||
|
||||
private:
|
||||
/// @brief The data of cell.
|
||||
std::u8string text;
|
||||
/// @brief The width cache of this data occupied in console.
|
||||
size_t text_width;
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @brief The type representing one row of data in table.
|
||||
*/
|
||||
using Row = std::vector<TabulateCell>;
|
||||
/**
|
||||
* @private
|
||||
* @brief The type representing row collection in table.
|
||||
*/
|
||||
using Rows = std::vector<Row>;
|
||||
|
||||
/**
|
||||
* @brief Main class of Tabulate
|
||||
*/
|
||||
class Tabulate {
|
||||
public:
|
||||
/**
|
||||
* @brief Create Tabulate class with given column count.
|
||||
* @details
|
||||
* In default, the separator bar of table is 3 dash.
|
||||
* Header and separator bar are also shown in default.
|
||||
* @param[in] n Column count of table.
|
||||
*/
|
||||
Tabulate(size_t n);
|
||||
~Tabulate();
|
||||
YYCC_DELETE_COPY_MOVE(Tabulate)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Print table into given stream.
|
||||
* @details In default, stream is \c stdout.
|
||||
* @param[in] dst The stream printed into.
|
||||
*/
|
||||
void print(std::ostream& dst = std::cout) const;
|
||||
|
||||
/**
|
||||
* @brief Get the column count of table.
|
||||
* @return Column count of table.
|
||||
*/
|
||||
size_t get_column_count() const;
|
||||
/**
|
||||
* @brief Change whether show table header when printing.
|
||||
* @param[in] show_header True for showing, otherwise false.
|
||||
*/
|
||||
void show_header(bool show_header);
|
||||
/**
|
||||
* @brief Change whether show separator bar when printing.
|
||||
* @param[in] show_bar True for showing, otherwise false.
|
||||
*/
|
||||
void show_bar(bool show_bar);
|
||||
/**
|
||||
* @brief Modify the prefix string of table.
|
||||
* @details
|
||||
* The prefix string of table is the string
|
||||
* which will be printed before each lines of output.
|
||||
* It is usually used for indent.
|
||||
* @param[in] prefix The prefix string.
|
||||
*/
|
||||
void set_prefix(const std::u8string_view& prefix);
|
||||
/**
|
||||
* @brief Modify the header of table.
|
||||
* @param[in] hdr An initializer list holding header texts one by one.
|
||||
* @exception std::invalid_argument The size of given header is mismatch with column count.
|
||||
*/
|
||||
void set_header(std::initializer_list<std::u8string_view> hdr);
|
||||
/**
|
||||
* @brief Modify separator bar string of table.
|
||||
* @param[in] bar New separator bar string.
|
||||
*/
|
||||
void set_bar(const std::u8string_view& bar);
|
||||
/**
|
||||
* @brief Add one data row into table.
|
||||
* @param[in] row An initializer list holding row texts one by one.
|
||||
* @exception std::invalid_argument The size of given header is mismatch with column count.
|
||||
*/
|
||||
void add_row(std::initializer_list<std::u8string_view> row);
|
||||
/**
|
||||
* @brief Clear all data rows of table.
|
||||
* @details Table header and separator bar will not be changed.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
private:
|
||||
|
||||
/// @brief The column count of table.
|
||||
size_t n;
|
||||
|
||||
/// @brief Whether showing table header.
|
||||
bool header_display;
|
||||
/// @brief Whether showing table separator bar between header and data.
|
||||
bool bar_display;
|
||||
/// @brief The prefix string presented in each lines of output table.
|
||||
std::u8string prefix_string;
|
||||
|
||||
/// @brief Width recorder for header.
|
||||
TabulateWidth header_widths;
|
||||
/// @brief Width recorder for data.
|
||||
TabulateWidth rows_widths;
|
||||
|
||||
/// @brief The header of table.
|
||||
Row header;
|
||||
/// @brief The separator bar of table.
|
||||
TabulateCell bar;
|
||||
/// @brief The data of table.
|
||||
Rows rows;
|
||||
};
|
||||
|
||||
}
|
234
src/yycc/carton/termcolor.cpp
Normal file
234
src/yycc/carton/termcolor.cpp
Normal file
@ -0,0 +1,234 @@
|
||||
#include "termcolor.hpp"
|
||||
#include "../flag_enum.hpp"
|
||||
#include "../string/reinterpret.hpp"
|
||||
#include <stdexcept>
|
||||
#include <bit>
|
||||
|
||||
#define FLAG_ENUM ::yycc::flag_enum
|
||||
#define REINTERPRET ::yycc::string::reinterpret
|
||||
|
||||
using namespace std::literals::string_view_literals;
|
||||
|
||||
namespace yycc::carton::termcolor {
|
||||
|
||||
#pragma region Lowlevel Functions
|
||||
|
||||
const std::u8string_view foreground(Color color) {
|
||||
switch (color) {
|
||||
case Color::Default:
|
||||
return u8""sv;
|
||||
case Color::Black:
|
||||
return u8"\033[30m"sv;
|
||||
case Color::Red:
|
||||
return u8"\033[31m"sv;
|
||||
case Color::Green:
|
||||
return u8"\033[32m"sv;
|
||||
case Color::Yellow:
|
||||
return u8"\033[33m"sv;
|
||||
case Color::Blue:
|
||||
return u8"\033[34m"sv;
|
||||
case Color::Magenta:
|
||||
return u8"\033[35m"sv;
|
||||
case Color::Cyan:
|
||||
return u8"\033[36m"sv;
|
||||
case Color::White:
|
||||
return u8"\033[37m"sv;
|
||||
case Color::LightBlack:
|
||||
return u8"\033[90m"sv;
|
||||
case Color::LightRed:
|
||||
return u8"\033[91m"sv;
|
||||
case Color::LightGreen:
|
||||
return u8"\033[92m"sv;
|
||||
case Color::LightYellow:
|
||||
return u8"\033[93m"sv;
|
||||
case Color::LightBlue:
|
||||
return u8"\033[94m"sv;
|
||||
case Color::LightMagenta:
|
||||
return u8"\033[95m"sv;
|
||||
case Color::LightCyan:
|
||||
return u8"\033[96m"sv;
|
||||
case Color::LightWhite:
|
||||
return u8"\033[97m"sv;
|
||||
default:
|
||||
throw std::invalid_argument("invalid color kind");
|
||||
}
|
||||
}
|
||||
|
||||
const std::u8string_view background(Color color) {
|
||||
switch (color) {
|
||||
case Color::Default:
|
||||
return u8""sv;
|
||||
case Color::Black:
|
||||
return u8"\033[40m"sv;
|
||||
case Color::Red:
|
||||
return u8"\033[41m"sv;
|
||||
case Color::Green:
|
||||
return u8"\033[42m"sv;
|
||||
case Color::Yellow:
|
||||
return u8"\033[43m"sv;
|
||||
case Color::Blue:
|
||||
return u8"\033[44m"sv;
|
||||
case Color::Magenta:
|
||||
return u8"\033[45m"sv;
|
||||
case Color::Cyan:
|
||||
return u8"\033[46m"sv;
|
||||
case Color::White:
|
||||
return u8"\033[47m"sv;
|
||||
case Color::LightBlack:
|
||||
return u8"\033[100m"sv;
|
||||
case Color::LightRed:
|
||||
return u8"\033[101m"sv;
|
||||
case Color::LightGreen:
|
||||
return u8"\033[102m"sv;
|
||||
case Color::LightYellow:
|
||||
return u8"\033[103m"sv;
|
||||
case Color::LightBlue:
|
||||
return u8"\033[104m"sv;
|
||||
case Color::LightMagenta:
|
||||
return u8"\033[105m"sv;
|
||||
case Color::LightCyan:
|
||||
return u8"\033[106m"sv;
|
||||
case Color::LightWhite:
|
||||
return u8"\033[107m"sv;
|
||||
default:
|
||||
throw std::invalid_argument("invalid color kind");
|
||||
}
|
||||
}
|
||||
|
||||
const std::u8string_view style(Attribute attr) {
|
||||
// Return for Default first because it can not pass following test
|
||||
if (attr == Attribute::Default) {
|
||||
return u8""sv;
|
||||
}
|
||||
|
||||
// Check whether it only has one flag
|
||||
if (!std::has_single_bit(FLAG_ENUM::integer(attr))) {
|
||||
throw std::invalid_argument("style() only accept single flag attribute");
|
||||
}
|
||||
|
||||
// Return result
|
||||
switch (attr) {
|
||||
case Attribute::Bold:
|
||||
return u8"\033[1m"sv;
|
||||
case Attribute::Dark:
|
||||
return u8"\033[2m"sv;
|
||||
case Attribute::Italic:
|
||||
return u8"\033[3m"sv;
|
||||
case Attribute::Underline:
|
||||
return u8"\033[4m"sv;
|
||||
case Attribute::Blink:
|
||||
return u8"\033[5m"sv;
|
||||
case Attribute::Reverse:
|
||||
return u8"\033[6m"sv;
|
||||
case Attribute::Concealed:
|
||||
return u8"\033[7m"sv;
|
||||
default:
|
||||
throw std::invalid_argument("invalid attribute kind");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @brief The possible maximum length of ANSI Escape Sequence used in this module.
|
||||
* @details This const value is used for computing reserved size of final built string.
|
||||
*/
|
||||
static constexpr size_t ANSI_ESC_LEN = sizeof(u8"\033[000m") - 1;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @brief Count how many single flags combine given attributes.
|
||||
* @details
|
||||
* For function styles() involving multiple font style ANSI Escape Sequence,
|
||||
* this function may be useful for computing desired size of final result,
|
||||
* to reduce useless memory re-allocation.
|
||||
* @param[in] attrs Attributes for counting.
|
||||
* @return The count of single flag.
|
||||
*/
|
||||
static size_t count_attribute_flags(Attribute attrs) {
|
||||
return static_cast<size_t>(std::popcount(FLAG_ENUM::integer(attrs)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @brief Append multiple font styles into given string.
|
||||
* @details
|
||||
* This function will decompose given font styles into single flag.
|
||||
* And append its components one by one into given string.
|
||||
* If there is enough reserved space in given string,
|
||||
* there is no memory re-allocation happened.
|
||||
* @remarks
|
||||
* This function is served for styles() and colored().
|
||||
* @param[in] s The string to be appended.
|
||||
* @param[in] attrs The attributes for writting.
|
||||
*/
|
||||
static void append_styles(std::u8string& s, Attribute attrs) {
|
||||
#define CHECK_ATTR(probe) \
|
||||
if (FLAG_ENUM::has(attrs, probe)) s.append(termcolor::style(probe));
|
||||
|
||||
if (attrs != Attribute::Default) {
|
||||
CHECK_ATTR(Attribute::Bold);
|
||||
CHECK_ATTR(Attribute::Dark);
|
||||
CHECK_ATTR(Attribute::Italic);
|
||||
CHECK_ATTR(Attribute::Blink);
|
||||
CHECK_ATTR(Attribute::Reverse);
|
||||
CHECK_ATTR(Attribute::Concealed);
|
||||
}
|
||||
|
||||
#undef CHECK_ATTR
|
||||
}
|
||||
|
||||
std::u8string styles(Attribute attrs) {
|
||||
// Prepare the result string
|
||||
std::u8string rv;
|
||||
rv.reserve(count_attribute_flags(attrs) * ANSI_ESC_LEN);
|
||||
// Append styles and return
|
||||
append_styles(rv, attrs);
|
||||
return rv;
|
||||
}
|
||||
|
||||
const std::u8string_view reset() {
|
||||
return u8"\033[0m"sv;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Highlevel Functions
|
||||
|
||||
std::u8string colored(const std::u8string_view& words, Color foreground, Color background, Attribute styles) {
|
||||
// Calculate the expected size of result string.
|
||||
// final count = styles count + 1 (foreground) + 1 (background) + 1 (reset)
|
||||
std::u8string rv;
|
||||
size_t ansi_esc_count = count_attribute_flags(styles) + 3;
|
||||
rv.reserve(ansi_esc_count * ANSI_ESC_LEN + words.size());
|
||||
|
||||
// Append data one by one
|
||||
rv.append(termcolor::foreground(foreground));
|
||||
rv.append(termcolor::background(background));
|
||||
append_styles(rv, styles);
|
||||
rv.append(words);
|
||||
rv.append(termcolor::reset());
|
||||
|
||||
// Return result
|
||||
return rv;
|
||||
}
|
||||
|
||||
void cprint(const std::u8string_view& words, Color foreground, Color background, Attribute styles, std::ostream& dst) {
|
||||
dst << REINTERPRET::as_ordinary_view(colored(words, foreground, background, styles));
|
||||
}
|
||||
|
||||
void ecprint(const std::u8string_view& words, Color foreground, Color background, Attribute styles) {
|
||||
cprint(words, foreground, background, styles, std::cerr);
|
||||
}
|
||||
|
||||
void cprintln(const std::u8string_view& words, Color foreground, Color background, Attribute styles, std::ostream& dst) {
|
||||
cprint(words, foreground, background, styles, dst);
|
||||
dst << std::endl;
|
||||
}
|
||||
|
||||
void ecprintln(const std::u8string_view& words, Color foreground, Color background, Attribute styles) {
|
||||
cprintln(words, foreground, background, styles, std::cerr);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace yycc::carton::termcolor
|
166
src/yycc/carton/termcolor.hpp
Normal file
166
src/yycc/carton/termcolor.hpp
Normal file
@ -0,0 +1,166 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <iostream>
|
||||
|
||||
/**
|
||||
* @brief The namespace for terminal font color and style.
|
||||
* @details
|
||||
* 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.
|
||||
*
|
||||
* This namespace is basically the immitation of the Python package with same name.
|
||||
*/
|
||||
namespace yycc::carton::termcolor {
|
||||
|
||||
#pragma region Lowlevel Functions
|
||||
|
||||
/**
|
||||
* @brief The color of font.
|
||||
*/
|
||||
enum class Color {
|
||||
Black,
|
||||
Red,
|
||||
Green,
|
||||
Yellow,
|
||||
Blue,
|
||||
Magenta,
|
||||
Cyan,
|
||||
White,
|
||||
LightBlack,
|
||||
LightRed,
|
||||
LightGreen,
|
||||
LightYellow,
|
||||
LightBlue,
|
||||
LightMagenta,
|
||||
LightCyan,
|
||||
LightWhite,
|
||||
Default
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Get ANSI escape sequence for foreground color
|
||||
* @param[in] color The color to generate sequence for
|
||||
* @return Gotten ANSI escape sequence
|
||||
*/
|
||||
const std::u8string_view foreground(Color color);
|
||||
/**
|
||||
* @brief Get ANSI escape sequence for background color
|
||||
* @param[in] color The color to generate sequence for
|
||||
* @return Gotten ANSI escape sequence
|
||||
*/
|
||||
const std::u8string_view background(Color color);
|
||||
|
||||
/**
|
||||
* @brief The attribute of font
|
||||
* @remarks We define this enum as unsigned integral, so that we can use \c std::has_single_bit.
|
||||
*/
|
||||
enum class Attribute : uint32_t {
|
||||
Default = 0,
|
||||
Bold = 1 << 0,
|
||||
Dark = 1 << 1,
|
||||
Italic = 1 << 2,
|
||||
Underline = 1 << 3,
|
||||
Blink = 1 << 4,
|
||||
Reverse = 1 << 5,
|
||||
Concealed = 1 << 6
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Get ANSI escape sequence for text style
|
||||
* @details
|
||||
* Please note that this function only support single attribute flag.
|
||||
* If you want to use multiple attributes, please use styles() instead.
|
||||
*
|
||||
* However, the difference between this function and styles() is that
|
||||
* there is no memory allocation in this function.
|
||||
* It may have better performance that styles().
|
||||
* @param[in] attr Single attribute to generate sequence for
|
||||
* @return Gotten ANSI escape sequence
|
||||
* @throws std::invalid_argument if attribute is not a single flag
|
||||
*/
|
||||
const std::u8string_view style(Attribute attr);
|
||||
/**
|
||||
* @brief Generates ANSI escape sequence for multiple text styles
|
||||
* @param[in] attrs Combination of attributes to generate sequences for
|
||||
* @return Generated ANSI escape sequence
|
||||
*/
|
||||
std::u8string styles(Attribute attrs);
|
||||
/**
|
||||
* @brief Get ANSI escape sequence for reset style
|
||||
* @return Gotten ANSI reset sequence
|
||||
*/
|
||||
const std::u8string_view reset();
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Highlevel Functions
|
||||
|
||||
/**
|
||||
* @brief Add color and style for given string with ANSI Escape Sequence.
|
||||
* @param[in] words The words to be decorated.
|
||||
* @param[in] foreground The foreground of words.
|
||||
* @param[in] background The background of words.
|
||||
* @param[in] styles The font style of words.
|
||||
* @return Decorated words.
|
||||
*/
|
||||
std::u8string colored(const std::u8string_view& words,
|
||||
Color foreground = Color::Default,
|
||||
Color background = Color::Default,
|
||||
Attribute styles = Attribute::Default);
|
||||
|
||||
/**
|
||||
* @brief Print words into stream with given styles.
|
||||
* @param[in] words The words to be printed.
|
||||
* @param[in] foreground The foreground of words.
|
||||
* @param[in] background The background of words.
|
||||
* @param[in] styles The font style of words.
|
||||
* @param[in] dst The stream written into. \c stdout in default.
|
||||
*/
|
||||
void cprint(const std::u8string_view& words = std::u8string_view(u8""),
|
||||
Color foreground = Color::Default,
|
||||
Color background = Color::Default,
|
||||
Attribute styles = Attribute::Default,
|
||||
std::ostream& dst = std::cout);
|
||||
|
||||
/**
|
||||
* @brief Print words into \c stderr with given styles.
|
||||
* @param[in] words The words to be printed.
|
||||
* @param[in] foreground The foreground of words.
|
||||
* @param[in] background The background of words.
|
||||
* @param[in] styles The font style of words.
|
||||
*/
|
||||
void ceprint(const std::u8string_view& words = std::u8string_view(u8""),
|
||||
Color foreground = Color::Default,
|
||||
Color background = Color::Default,
|
||||
Attribute styles = Attribute::Default);
|
||||
|
||||
/**
|
||||
* @brief Print words into stream with given styles and break line.
|
||||
* @param[in] words The words to be printed.
|
||||
* @param[in] foreground The foreground of words.
|
||||
* @param[in] background The background of words.
|
||||
* @param[in] styles The font style of words.
|
||||
* @param[in] dst The stream written into. \c stdout in default.
|
||||
*/
|
||||
void cprintln(const std::u8string_view& words = std::u8string_view(u8""),
|
||||
Color foreground = Color::Default,
|
||||
Color background = Color::Default,
|
||||
Attribute styles = Attribute::Default,
|
||||
std::ostream& dst = std::cout);
|
||||
|
||||
/**
|
||||
* @brief Print words into \c stderr with given styles and break line.
|
||||
* @param[in] words The words to be printed.
|
||||
* @param[in] foreground The foreground of words.
|
||||
* @param[in] background The background of words.
|
||||
* @param[in] styles The font style of words.
|
||||
*/
|
||||
void ceprintln(const std::u8string_view& words = std::u8string_view(u8""),
|
||||
Color foreground = Color::Default,
|
||||
Color background = Color::Default,
|
||||
Attribute styles = Attribute::Default);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace yycc::carton::termcolor
|
478
src/yycc/carton/wcwidth.cpp
Normal file
478
src/yycc/carton/wcwidth.cpp
Normal file
@ -0,0 +1,478 @@
|
||||
#include "wcwidth.hpp"
|
||||
#include "../encoding/stl.hpp"
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
#define ENC ::yycc::encoding::stl
|
||||
|
||||
namespace yycc::carton::wcwidth {
|
||||
|
||||
using Boundary = std::pair<char32_t, char32_t>;
|
||||
using BoundaryVector = std::vector<Boundary>;
|
||||
|
||||
// YYC MARK:
|
||||
// Following table and code are copied from Python package "wcwidth".
|
||||
// Although the code of this package are also copied from the original "wcwidth" C implementation.
|
||||
//
|
||||
// I do not need so much exact measurement.
|
||||
// I just want a "it works" wcwdith in all platforms.
|
||||
// So these tables are coming from the table with lowest UNICODE version
|
||||
// (original package provides different tables for different UNICODE versions).
|
||||
|
||||
// clang-format off
|
||||
static const BoundaryVector ZERO_WIDTH{
|
||||
{U'\x00000', U'\x00000'}, // (nil)
|
||||
{U'\x000ad', U'\x000ad'}, // Soft Hyphen
|
||||
{U'\x00300', U'\x0036f'}, // Combining Grave Accent ..Combining Latin Small Le
|
||||
{U'\x00483', U'\x00486'}, // Combining Cyrillic Titlo..Combining Cyrillic Psili
|
||||
{U'\x00488', U'\x00489'}, // Combining Cyrillic Hundr..Combining Cyrillic Milli
|
||||
{U'\x00591', U'\x005b9'}, // Hebrew Accent Etnahta ..Hebrew Point Holam
|
||||
{U'\x005bb', U'\x005bd'}, // Hebrew Point Qubuts ..Hebrew Point Meteg
|
||||
{U'\x005bf', U'\x005bf'}, // Hebrew Point Rafe
|
||||
{U'\x005c1', U'\x005c2'}, // Hebrew Point Shin Dot ..Hebrew Point Sin Dot
|
||||
{U'\x005c4', U'\x005c5'}, // Hebrew Mark Upper Dot ..Hebrew Mark Lower Dot
|
||||
{U'\x005c7', U'\x005c7'}, // Hebrew Point Qamats Qatan
|
||||
{U'\x00600', U'\x00603'}, // Arabic Number Sign ..Arabic Sign Safha
|
||||
{U'\x00610', U'\x00615'}, // Arabic Sign Sallallahou ..Arabic Small High Tah
|
||||
{U'\x0064b', U'\x0065e'}, // Arabic Fathatan ..Arabic Fatha With Two Do
|
||||
{U'\x00670', U'\x00670'}, // Arabic Letter Superscript Alef
|
||||
{U'\x006d6', U'\x006e4'}, // Arabic Small High Ligatu..Arabic Small High Madda
|
||||
{U'\x006e7', U'\x006e8'}, // Arabic Small High Yeh ..Arabic Small High Noon
|
||||
{U'\x006ea', U'\x006ed'}, // Arabic Empty Centre Low ..Arabic Small Low Meem
|
||||
{U'\x0070f', U'\x0070f'}, // Syriac Abbreviation Mark
|
||||
{U'\x00711', U'\x00711'}, // Syriac Letter Superscript Alaph
|
||||
{U'\x00730', U'\x0074a'}, // Syriac Pthaha Above ..Syriac Barrekh
|
||||
{U'\x007a6', U'\x007b0'}, // Thaana Abafili ..Thaana Sukun
|
||||
{U'\x00901', U'\x00903'}, // Devanagari Sign Candrabi..Devanagari Sign Visarga
|
||||
{U'\x0093c', U'\x0093c'}, // Devanagari Sign Nukta
|
||||
{U'\x0093e', U'\x0094d'}, // Devanagari Vowel Sign Aa..Devanagari Sign Virama
|
||||
{U'\x00951', U'\x00954'}, // Devanagari Stress Sign U..Devanagari Acute Accent
|
||||
{U'\x00962', U'\x00963'}, // Devanagari Vowel Sign Vo..Devanagari Vowel Sign Vo
|
||||
{U'\x00981', U'\x00983'}, // Bengali Sign Candrabindu..Bengali Sign Visarga
|
||||
{U'\x009bc', U'\x009bc'}, // Bengali Sign Nukta
|
||||
{U'\x009be', U'\x009c4'}, // Bengali Vowel Sign Aa ..Bengali Vowel Sign Vocal
|
||||
{U'\x009c7', U'\x009c8'}, // Bengali Vowel Sign E ..Bengali Vowel Sign Ai
|
||||
{U'\x009cb', U'\x009cd'}, // Bengali Vowel Sign O ..Bengali Sign Virama
|
||||
{U'\x009d7', U'\x009d7'}, // Bengali Au Length Mark
|
||||
{U'\x009e2', U'\x009e3'}, // Bengali Vowel Sign Vocal..Bengali Vowel Sign Vocal
|
||||
{U'\x00a01', U'\x00a03'}, // Gurmukhi Sign Adak Bindi..Gurmukhi Sign Visarga
|
||||
{U'\x00a3c', U'\x00a3c'}, // Gurmukhi Sign Nukta
|
||||
{U'\x00a3e', U'\x00a42'}, // Gurmukhi Vowel Sign Aa ..Gurmukhi Vowel Sign Uu
|
||||
{U'\x00a47', U'\x00a48'}, // Gurmukhi Vowel Sign Ee ..Gurmukhi Vowel Sign Ai
|
||||
{U'\x00a4b', U'\x00a4d'}, // Gurmukhi Vowel Sign Oo ..Gurmukhi Sign Virama
|
||||
{U'\x00a70', U'\x00a71'}, // Gurmukhi Tippi ..Gurmukhi Addak
|
||||
{U'\x00a81', U'\x00a83'}, // Gujarati Sign Candrabind..Gujarati Sign Visarga
|
||||
{U'\x00abc', U'\x00abc'}, // Gujarati Sign Nukta
|
||||
{U'\x00abe', U'\x00ac5'}, // Gujarati Vowel Sign Aa ..Gujarati Vowel Sign Cand
|
||||
{U'\x00ac7', U'\x00ac9'}, // Gujarati Vowel Sign E ..Gujarati Vowel Sign Cand
|
||||
{U'\x00acb', U'\x00acd'}, // Gujarati Vowel Sign O ..Gujarati Sign Virama
|
||||
{U'\x00ae2', U'\x00ae3'}, // Gujarati Vowel Sign Voca..Gujarati Vowel Sign Voca
|
||||
{U'\x00b01', U'\x00b03'}, // Oriya Sign Candrabindu ..Oriya Sign Visarga
|
||||
{U'\x00b3c', U'\x00b3c'}, // Oriya Sign Nukta
|
||||
{U'\x00b3e', U'\x00b43'}, // Oriya Vowel Sign Aa ..Oriya Vowel Sign Vocalic
|
||||
{U'\x00b47', U'\x00b48'}, // Oriya Vowel Sign E ..Oriya Vowel Sign Ai
|
||||
{U'\x00b4b', U'\x00b4d'}, // Oriya Vowel Sign O ..Oriya Sign Virama
|
||||
{U'\x00b56', U'\x00b57'}, // Oriya Ai Length Mark ..Oriya Au Length Mark
|
||||
{U'\x00b82', U'\x00b82'}, // Tamil Sign Anusvara
|
||||
{U'\x00bbe', U'\x00bc2'}, // Tamil Vowel Sign Aa ..Tamil Vowel Sign Uu
|
||||
{U'\x00bc6', U'\x00bc8'}, // Tamil Vowel Sign E ..Tamil Vowel Sign Ai
|
||||
{U'\x00bca', U'\x00bcd'}, // Tamil Vowel Sign O ..Tamil Sign Virama
|
||||
{U'\x00bd7', U'\x00bd7'}, // Tamil Au Length Mark
|
||||
{U'\x00c01', U'\x00c03'}, // Telugu Sign Candrabindu ..Telugu Sign Visarga
|
||||
{U'\x00c3e', U'\x00c44'}, // Telugu Vowel Sign Aa ..Telugu Vowel Sign Vocali
|
||||
{U'\x00c46', U'\x00c48'}, // Telugu Vowel Sign E ..Telugu Vowel Sign Ai
|
||||
{U'\x00c4a', U'\x00c4d'}, // Telugu Vowel Sign O ..Telugu Sign Virama
|
||||
{U'\x00c55', U'\x00c56'}, // Telugu Length Mark ..Telugu Ai Length Mark
|
||||
{U'\x00c82', U'\x00c83'}, // Kannada Sign Anusvara ..Kannada Sign Visarga
|
||||
{U'\x00cbc', U'\x00cbc'}, // Kannada Sign Nukta
|
||||
{U'\x00cbe', U'\x00cc4'}, // Kannada Vowel Sign Aa ..Kannada Vowel Sign Vocal
|
||||
{U'\x00cc6', U'\x00cc8'}, // Kannada Vowel Sign E ..Kannada Vowel Sign Ai
|
||||
{U'\x00cca', U'\x00ccd'}, // Kannada Vowel Sign O ..Kannada Sign Virama
|
||||
{U'\x00cd5', U'\x00cd6'}, // Kannada Length Mark ..Kannada Ai Length Mark
|
||||
{U'\x00d02', U'\x00d03'}, // Malayalam Sign Anusvara ..Malayalam Sign Visarga
|
||||
{U'\x00d3e', U'\x00d43'}, // Malayalam Vowel Sign Aa ..Malayalam Vowel Sign Voc
|
||||
{U'\x00d46', U'\x00d48'}, // Malayalam Vowel Sign E ..Malayalam Vowel Sign Ai
|
||||
{U'\x00d4a', U'\x00d4d'}, // Malayalam Vowel Sign O ..Malayalam Sign Virama
|
||||
{U'\x00d57', U'\x00d57'}, // Malayalam Au Length Mark
|
||||
{U'\x00d82', U'\x00d83'}, // Sinhala Sign Anusvaraya ..Sinhala Sign Visargaya
|
||||
{U'\x00dca', U'\x00dca'}, // Sinhala Sign Al-lakuna
|
||||
{U'\x00dcf', U'\x00dd4'}, // Sinhala Vowel Sign Aela-..Sinhala Vowel Sign Ketti
|
||||
{U'\x00dd6', U'\x00dd6'}, // Sinhala Vowel Sign Diga Paa-pilla
|
||||
{U'\x00dd8', U'\x00ddf'}, // Sinhala Vowel Sign Gaett..Sinhala Vowel Sign Gayan
|
||||
{U'\x00df2', U'\x00df3'}, // Sinhala Vowel Sign Diga ..Sinhala Vowel Sign Diga
|
||||
{U'\x00e31', U'\x00e31'}, // Thai Character Mai Han-akat
|
||||
{U'\x00e34', U'\x00e3a'}, // Thai Character Sara I ..Thai Character Phinthu
|
||||
{U'\x00e47', U'\x00e4e'}, // Thai Character Maitaikhu..Thai Character Yamakkan
|
||||
{U'\x00eb1', U'\x00eb1'}, // Lao Vowel Sign Mai Kan
|
||||
{U'\x00eb4', U'\x00eb9'}, // Lao Vowel Sign I ..Lao Vowel Sign Uu
|
||||
{U'\x00ebb', U'\x00ebc'}, // Lao Vowel Sign Mai Kon ..Lao Semivowel Sign Lo
|
||||
{U'\x00ec8', U'\x00ecd'}, // Lao Tone Mai Ek ..Lao Niggahita
|
||||
{U'\x00f18', U'\x00f19'}, // Tibetan Astrological Sig..Tibetan Astrological Sig
|
||||
{U'\x00f35', U'\x00f35'}, // Tibetan Mark Ngas Bzung Nyi Zla
|
||||
{U'\x00f37', U'\x00f37'}, // Tibetan Mark Ngas Bzung Sgor Rtags
|
||||
{U'\x00f39', U'\x00f39'}, // Tibetan Mark Tsa -phru
|
||||
{U'\x00f3e', U'\x00f3f'}, // Tibetan Sign Yar Tshes ..Tibetan Sign Mar Tshes
|
||||
{U'\x00f71', U'\x00f84'}, // Tibetan Vowel Sign Aa ..Tibetan Mark Halanta
|
||||
{U'\x00f86', U'\x00f87'}, // Tibetan Sign Lci Rtags ..Tibetan Sign Yang Rtags
|
||||
{U'\x00f90', U'\x00f97'}, // Tibetan Subjoined Letter..Tibetan Subjoined Letter
|
||||
{U'\x00f99', U'\x00fbc'}, // Tibetan Subjoined Letter..Tibetan Subjoined Letter
|
||||
{U'\x00fc6', U'\x00fc6'}, // Tibetan Symbol Padma Gdan
|
||||
{U'\x0102c', U'\x01032'}, // Myanmar Vowel Sign Aa ..Myanmar Vowel Sign Ai
|
||||
{U'\x01036', U'\x01039'}, // Myanmar Sign Anusvara ..Myanmar Sign Virama
|
||||
{U'\x01056', U'\x01059'}, // Myanmar Vowel Sign Vocal..Myanmar Vowel Sign Vocal
|
||||
{U'\x01160', U'\x011ff'}, // Hangul Jungseong Filler ..Hangul Jongseong Ssangni
|
||||
{U'\x0135f', U'\x0135f'}, // Ethiopic Combining Gemination Mark
|
||||
{U'\x01712', U'\x01714'}, // Tagalog Vowel Sign I ..Tagalog Sign Virama
|
||||
{U'\x01732', U'\x01734'}, // Hanunoo Vowel Sign I ..Hanunoo Sign Pamudpod
|
||||
{U'\x01752', U'\x01753'}, // Buhid Vowel Sign I ..Buhid Vowel Sign U
|
||||
{U'\x01772', U'\x01773'}, // Tagbanwa Vowel Sign I ..Tagbanwa Vowel Sign U
|
||||
{U'\x017b4', U'\x017d3'}, // Khmer Vowel Inherent Aq ..Khmer Sign Bathamasat
|
||||
{U'\x017dd', U'\x017dd'}, // Khmer Sign Atthacan
|
||||
{U'\x0180b', U'\x0180d'}, // Mongolian Free Variation..Mongolian Free Variation
|
||||
{U'\x018a9', U'\x018a9'}, // Mongolian Letter Ali Gali Dagalga
|
||||
{U'\x01920', U'\x0192b'}, // Limbu Vowel Sign A ..Limbu Subjoined Letter W
|
||||
{U'\x01930', U'\x0193b'}, // Limbu Small Letter Ka ..Limbu Sign Sa-i
|
||||
{U'\x019b0', U'\x019c0'}, // New Tai Lue Vowel Sign V..New Tai Lue Vowel Sign I
|
||||
{U'\x019c8', U'\x019c9'}, // New Tai Lue Tone Mark-1 ..New Tai Lue Tone Mark-2
|
||||
{U'\x01a17', U'\x01a1b'}, // Buginese Vowel Sign I ..Buginese Vowel Sign Ae
|
||||
{U'\x01dc0', U'\x01dc3'}, // Combining Dotted Grave A..Combining Suspension Mar
|
||||
{U'\x0200b', U'\x0200f'}, // Zero Width Space ..Right-to-left Mark
|
||||
{U'\x02028', U'\x0202e'}, // Line Separator ..Right-to-left Override
|
||||
{U'\x02060', U'\x02063'}, // Word Joiner ..Invisible Separator
|
||||
{U'\x0206a', U'\x0206f'}, // Inhibit Symmetric Swappi..Nominal Digit Shapes
|
||||
{U'\x020d0', U'\x020eb'}, // Combining Left Harpoon A..Combining Long Double So
|
||||
{U'\x0302a', U'\x0302f'}, // Ideographic Level Tone M..Hangul Double Dot Tone M
|
||||
{U'\x03099', U'\x0309a'}, // Combining Katakana-hirag..Combining Katakana-hirag
|
||||
{U'\x0a802', U'\x0a802'}, // Syloti Nagri Sign Dvisvara
|
||||
{U'\x0a806', U'\x0a806'}, // Syloti Nagri Sign Hasanta
|
||||
{U'\x0a80b', U'\x0a80b'}, // Syloti Nagri Sign Anusvara
|
||||
{U'\x0a823', U'\x0a827'}, // Syloti Nagri Vowel Sign ..Syloti Nagri Vowel Sign
|
||||
{U'\x0d7b0', U'\x0d7ff'}, // Hangul Jungseong O-yeo ..(nil)
|
||||
{U'\x0fb1e', U'\x0fb1e'}, // Hebrew Point Judeo-spanish Varika
|
||||
{U'\x0fe00', U'\x0fe0f'}, // Variation Selector-1 ..Variation Selector-16
|
||||
{U'\x0fe20', U'\x0fe23'}, // Combining Ligature Left ..Combining Double Tilde R
|
||||
{U'\x0feff', U'\x0feff'}, // Zero Width No-break Space
|
||||
{U'\x0fff9', U'\x0fffb'}, // Interlinear Annotation A..Interlinear Annotation T
|
||||
{U'\x10a01', U'\x10a03'}, // Kharoshthi Vowel Sign I ..Kharoshthi Vowel Sign Vo
|
||||
{U'\x10a05', U'\x10a06'}, // Kharoshthi Vowel Sign E ..Kharoshthi Vowel Sign O
|
||||
{U'\x10a0c', U'\x10a0f'}, // Kharoshthi Vowel Length ..Kharoshthi Sign Visarga
|
||||
{U'\x10a38', U'\x10a3a'}, // Kharoshthi Sign Bar Abov..Kharoshthi Sign Dot Belo
|
||||
{U'\x10a3f', U'\x10a3f'}, // Kharoshthi Virama
|
||||
{U'\x1d165', U'\x1d169'}, // Musical Symbol Combining..Musical Symbol Combining
|
||||
{U'\x1d16d', U'\x1d182'}, // Musical Symbol Combining..Musical Symbol Combining
|
||||
{U'\x1d185', U'\x1d18b'}, // Musical Symbol Combining..Musical Symbol Combining
|
||||
{U'\x1d1aa', U'\x1d1ad'}, // Musical Symbol Combining..Musical Symbol Combining
|
||||
{U'\x1d242', U'\x1d244'}, // Combining Greek Musical ..Combining Greek Musical
|
||||
{U'\xe0001', U'\xe0001'}, // Language Tag
|
||||
{U'\xe0020', U'\xe007f'}, // Tag Space ..Cancel Tag
|
||||
{U'\xe0100', U'\xe01ef'}, // Variation Selector-17 ..Variation Selector-256
|
||||
};
|
||||
|
||||
static const BoundaryVector WIDE_EAST_ASIAN{
|
||||
{U'\x01100', U'\x01159'}, // Hangul Choseong Kiyeok ..Hangul Choseong Yeorinhi
|
||||
{U'\x0115f', U'\x0115f'}, // Hangul Choseong Filler
|
||||
{U'\x02329', U'\x0232a'}, // Left-pointing Angle Brac..Right-pointing Angle Bra
|
||||
{U'\x02e80', U'\x02e99'}, // Cjk Radical Repeat ..Cjk Radical Rap
|
||||
{U'\x02e9b', U'\x02ef3'}, // Cjk Radical Choke ..Cjk Radical C-simplified
|
||||
{U'\x02f00', U'\x02fd5'}, // Kangxi Radical One ..Kangxi Radical Flute
|
||||
{U'\x02ff0', U'\x02ffb'}, // Ideographic Description ..Ideographic Description
|
||||
{U'\x03000', U'\x03029'}, // Ideographic Space ..Hangzhou Numeral Nine
|
||||
{U'\x03030', U'\x0303e'}, // Wavy Dash ..Ideographic Variation In
|
||||
{U'\x03041', U'\x03096'}, // Hiragana Letter Small A ..Hiragana Letter Small Ke
|
||||
{U'\x0309b', U'\x030ff'}, // Katakana-hiragana Voiced..Katakana Digraph Koto
|
||||
{U'\x03105', U'\x0312c'}, // Bopomofo Letter B ..Bopomofo Letter Gn
|
||||
{U'\x03131', U'\x0318e'}, // Hangul Letter Kiyeok ..Hangul Letter Araeae
|
||||
{U'\x03190', U'\x031b7'}, // Ideographic Annotation L..Bopomofo Final Letter H
|
||||
{U'\x031c0', U'\x031cf'}, // Cjk Stroke T ..Cjk Stroke N
|
||||
{U'\x031f0', U'\x0321e'}, // Katakana Letter Small Ku..Parenthesized Korean Cha
|
||||
{U'\x03220', U'\x03243'}, // Parenthesized Ideograph ..Parenthesized Ideograph
|
||||
{U'\x03250', U'\x032fe'}, // Partnership Sign ..Circled Katakana Wo
|
||||
{U'\x03300', U'\x04db5'}, // Square Apaato ..Cjk Unified Ideograph-4d
|
||||
{U'\x04e00', U'\x09fbb'}, // Cjk Unified Ideograph-4e..Cjk Unified Ideograph-9f
|
||||
{U'\x0a000', U'\x0a48c'}, // Yi Syllable It ..Yi Syllable Yyr
|
||||
{U'\x0a490', U'\x0a4c6'}, // Yi Radical Qot ..Yi Radical Ke
|
||||
{U'\x0ac00', U'\x0d7a3'}, // Hangul Syllable Ga ..Hangul Syllable Hih
|
||||
{U'\x0f900', U'\x0fa2d'}, // Cjk Compatibility Ideogr..Cjk Compatibility Ideogr
|
||||
{U'\x0fa30', U'\x0fa6a'}, // Cjk Compatibility Ideogr..Cjk Compatibility Ideogr
|
||||
{U'\x0fa70', U'\x0fad9'}, // Cjk Compatibility Ideogr..Cjk Compatibility Ideogr
|
||||
{U'\x0fe10', U'\x0fe19'}, // Presentation Form For Ve..Presentation Form For Ve
|
||||
{U'\x0fe30', U'\x0fe52'}, // Presentation Form For Ve..Small Full Stop
|
||||
{U'\x0fe54', U'\x0fe66'}, // Small Semicolon ..Small Equals Sign
|
||||
{U'\x0fe68', U'\x0fe6b'}, // Small Reverse Solidus ..Small Commercial At
|
||||
{U'\x0ff01', U'\x0ff60'}, // Fullwidth Exclamation Ma..Fullwidth Right White Pa
|
||||
{U'\x0ffe0', U'\x0ffe6'}, // Fullwidth Cent Sign ..Fullwidth Won Sign
|
||||
{U'\x20000', U'\x2fffd'}, // Cjk Unified Ideograph-20..(nil)
|
||||
{U'\x30000', U'\x3fffd'}, // Cjk Unified Ideograph-30..(nil)
|
||||
};
|
||||
|
||||
static const BoundaryVector VS16_NARROW_TO_WIDE{
|
||||
{U'\x00023', U'\x00023'}, // Number Sign
|
||||
{U'\x0002a', U'\x0002a'}, // Asterisk
|
||||
{U'\x00030', U'\x00039'}, // Digit Zero ..Digit Nine
|
||||
{U'\x000a9', U'\x000a9'}, // Copyright Sign
|
||||
{U'\x000ae', U'\x000ae'}, // Registered Sign
|
||||
{U'\x0203c', U'\x0203c'}, // Double Exclamation Mark
|
||||
{U'\x02049', U'\x02049'}, // Exclamation Question Mark
|
||||
{U'\x02122', U'\x02122'}, // Trade Mark Sign
|
||||
{U'\x02139', U'\x02139'}, // Information Source
|
||||
{U'\x02194', U'\x02199'}, // Left Right Arrow ..South West Arrow
|
||||
{U'\x021a9', U'\x021aa'}, // Leftwards Arrow With Hoo..Rightwards Arrow With Ho
|
||||
{U'\x02328', U'\x02328'}, // Keyboard
|
||||
{U'\x023cf', U'\x023cf'}, // Eject Symbol
|
||||
{U'\x023ed', U'\x023ef'}, // Black Right-pointing Dou..Black Right-pointing Tri
|
||||
{U'\x023f1', U'\x023f2'}, // Stopwatch ..Timer Clock
|
||||
{U'\x023f8', U'\x023fa'}, // Double Vertical Bar ..Black Circle For Record
|
||||
{U'\x024c2', U'\x024c2'}, // Circled Latin Capital Letter M
|
||||
{U'\x025aa', U'\x025ab'}, // Black Small Square ..White Small Square
|
||||
{U'\x025b6', U'\x025b6'}, // Black Right-pointing Triangle
|
||||
{U'\x025c0', U'\x025c0'}, // Black Left-pointing Triangle
|
||||
{U'\x025fb', U'\x025fc'}, // White Medium Square ..Black Medium Square
|
||||
{U'\x02600', U'\x02604'}, // Black Sun With Rays ..Comet
|
||||
{U'\x0260e', U'\x0260e'}, // Black Telephone
|
||||
{U'\x02611', U'\x02611'}, // Ballot Box With Check
|
||||
{U'\x02618', U'\x02618'}, // Shamrock
|
||||
{U'\x0261d', U'\x0261d'}, // White Up Pointing Index
|
||||
{U'\x02620', U'\x02620'}, // Skull And Crossbones
|
||||
{U'\x02622', U'\x02623'}, // Radioactive Sign ..Biohazard Sign
|
||||
{U'\x02626', U'\x02626'}, // Orthodox Cross
|
||||
{U'\x0262a', U'\x0262a'}, // Star And Crescent
|
||||
{U'\x0262e', U'\x0262f'}, // Peace Symbol ..Yin Yang
|
||||
{U'\x02638', U'\x0263a'}, // Wheel Of Dharma ..White Smiling Face
|
||||
{U'\x02640', U'\x02640'}, // Female Sign
|
||||
{U'\x02642', U'\x02642'}, // Male Sign
|
||||
{U'\x0265f', U'\x02660'}, // Black Chess Pawn ..Black Spade Suit
|
||||
{U'\x02663', U'\x02663'}, // Black Club Suit
|
||||
{U'\x02665', U'\x02666'}, // Black Heart Suit ..Black Diamond Suit
|
||||
{U'\x02668', U'\x02668'}, // Hot Springs
|
||||
{U'\x0267b', U'\x0267b'}, // Black Universal Recycling Symbol
|
||||
{U'\x0267e', U'\x0267e'}, // Permanent Paper Sign
|
||||
{U'\x02692', U'\x02692'}, // Hammer And Pick
|
||||
{U'\x02694', U'\x02697'}, // Crossed Swords ..Alembic
|
||||
{U'\x02699', U'\x02699'}, // Gear
|
||||
{U'\x0269b', U'\x0269c'}, // Atom Symbol ..Fleur-de-lis
|
||||
{U'\x026a0', U'\x026a0'}, // Warning Sign
|
||||
{U'\x026a7', U'\x026a7'}, // Male With Stroke And Male And Female Sign
|
||||
{U'\x026b0', U'\x026b1'}, // Coffin ..Funeral Urn
|
||||
{U'\x026c8', U'\x026c8'}, // Thunder Cloud And Rain
|
||||
{U'\x026cf', U'\x026cf'}, // Pick
|
||||
{U'\x026d1', U'\x026d1'}, // Helmet With White Cross
|
||||
{U'\x026d3', U'\x026d3'}, // Chains
|
||||
{U'\x026e9', U'\x026e9'}, // Shinto Shrine
|
||||
{U'\x026f0', U'\x026f1'}, // Mountain ..Umbrella On Ground
|
||||
{U'\x026f4', U'\x026f4'}, // Ferry
|
||||
{U'\x026f7', U'\x026f9'}, // Skier ..Person With Ball
|
||||
{U'\x02702', U'\x02702'}, // Black Scissors
|
||||
{U'\x02708', U'\x02709'}, // Airplane ..Envelope
|
||||
{U'\x0270c', U'\x0270d'}, // Victory Hand ..Writing Hand
|
||||
{U'\x0270f', U'\x0270f'}, // Pencil
|
||||
{U'\x02712', U'\x02712'}, // Black Nib
|
||||
{U'\x02714', U'\x02714'}, // Heavy Check Mark
|
||||
{U'\x02716', U'\x02716'}, // Heavy Multiplication X
|
||||
{U'\x0271d', U'\x0271d'}, // Latin Cross
|
||||
{U'\x02721', U'\x02721'}, // Star Of David
|
||||
{U'\x02733', U'\x02734'}, // Eight Spoked Asterisk ..Eight Pointed Black Star
|
||||
{U'\x02744', U'\x02744'}, // Snowflake
|
||||
{U'\x02747', U'\x02747'}, // Sparkle
|
||||
{U'\x02763', U'\x02764'}, // Heavy Heart Exclamation ..Heavy Black Heart
|
||||
{U'\x027a1', U'\x027a1'}, // Black Rightwards Arrow
|
||||
{U'\x02934', U'\x02935'}, // Arrow Pointing Rightward..Arrow Pointing Rightward
|
||||
{U'\x02b05', U'\x02b07'}, // Leftwards Black Arrow ..Downwards Black Arrow
|
||||
{U'\x1f170', U'\x1f171'}, // Negative Squared Latin C..Negative Squared Latin C
|
||||
{U'\x1f17e', U'\x1f17f'}, // Negative Squared Latin C..Negative Squared Latin C
|
||||
{U'\x1f321', U'\x1f321'}, // Thermometer
|
||||
{U'\x1f324', U'\x1f32c'}, // White Sun With Small Clo..Wind Blowing Face
|
||||
{U'\x1f336', U'\x1f336'}, // Hot Pepper
|
||||
{U'\x1f37d', U'\x1f37d'}, // Fork And Knife With Plate
|
||||
{U'\x1f396', U'\x1f397'}, // Military Medal ..Reminder Ribbon
|
||||
{U'\x1f399', U'\x1f39b'}, // Studio Microphone ..Control Knobs
|
||||
{U'\x1f39e', U'\x1f39f'}, // Film Frames ..Admission Tickets
|
||||
{U'\x1f3cb', U'\x1f3ce'}, // Weight Lifter ..Racing Car
|
||||
{U'\x1f3d4', U'\x1f3df'}, // Snow Capped Mountain ..Stadium
|
||||
{U'\x1f3f3', U'\x1f3f3'}, // Waving White Flag
|
||||
{U'\x1f3f5', U'\x1f3f5'}, // Rosette
|
||||
{U'\x1f3f7', U'\x1f3f7'}, // Label
|
||||
{U'\x1f43f', U'\x1f43f'}, // Chipmunk
|
||||
{U'\x1f441', U'\x1f441'}, // Eye
|
||||
{U'\x1f4fd', U'\x1f4fd'}, // Film Projector
|
||||
{U'\x1f549', U'\x1f54a'}, // Om Symbol ..Dove Of Peace
|
||||
{U'\x1f56f', U'\x1f570'}, // Candle ..Mantelpiece Clock
|
||||
{U'\x1f573', U'\x1f579'}, // Hole ..Joystick
|
||||
{U'\x1f587', U'\x1f587'}, // Linked Paperclips
|
||||
{U'\x1f58a', U'\x1f58d'}, // Lower Left Ballpoint Pen..Lower Left Crayon
|
||||
{U'\x1f590', U'\x1f590'}, // Raised Hand With Fingers Splayed
|
||||
{U'\x1f5a5', U'\x1f5a5'}, // Desktop Computer
|
||||
{U'\x1f5a8', U'\x1f5a8'}, // Printer
|
||||
{U'\x1f5b1', U'\x1f5b2'}, // Three Button Mouse ..Trackball
|
||||
{U'\x1f5bc', U'\x1f5bc'}, // Frame With Picture
|
||||
{U'\x1f5c2', U'\x1f5c4'}, // Card Index Dividers ..File Cabinet
|
||||
{U'\x1f5d1', U'\x1f5d3'}, // Wastebasket ..Spiral Calendar Pad
|
||||
{U'\x1f5dc', U'\x1f5de'}, // Compression ..Rolled-up Newspaper
|
||||
{U'\x1f5e1', U'\x1f5e1'}, // Dagger Knife
|
||||
{U'\x1f5e3', U'\x1f5e3'}, // Speaking Head In Silhouette
|
||||
{U'\x1f5e8', U'\x1f5e8'}, // Left Speech Bubble
|
||||
{U'\x1f5ef', U'\x1f5ef'}, // Right Anger Bubble
|
||||
{U'\x1f5f3', U'\x1f5f3'}, // Ballot Box With Ballot
|
||||
{U'\x1f5fa', U'\x1f5fa'}, // World Map
|
||||
{U'\x1f6cb', U'\x1f6cb'}, // Couch And Lamp
|
||||
{U'\x1f6cd', U'\x1f6cf'}, // Shopping Bags ..Bed
|
||||
{U'\x1f6e0', U'\x1f6e5'}, // Hammer And Wrench ..Motor Boat
|
||||
{U'\x1f6e9', U'\x1f6e9'}, // Small Airplane
|
||||
{U'\x1f6f0', U'\x1f6f0'}, // Satellite
|
||||
{U'\x1f6f3', U'\x1f6f3'}, // Passenger Ship
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
static size_t bisearch(char32_t ucs, const BoundaryVector& table) {
|
||||
// TODO: Use STD algorithm to optimize this function
|
||||
|
||||
// YYC MARK:
|
||||
// Do not change this "int" to "size_t" casually,
|
||||
// because the result of arithmetic operation may be negative.
|
||||
// Do not change this type before using new algorithm.
|
||||
int lbound = 0, ubound = table.size() - 1;
|
||||
|
||||
if (ucs < table.front().first || ucs > table.back().second) return 0;
|
||||
|
||||
while (ubound >= lbound) {
|
||||
int mid = (lbound + ubound) / 2;
|
||||
if (ucs > table[mid].second) lbound = mid + 1;
|
||||
else if (ucs < table[mid].first) ubound = mid - 1;
|
||||
else return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t wcwidth(char32_t wc) {
|
||||
// TODO: Add lru_cache(maxsize=1000) for this function
|
||||
|
||||
// Small optimize for ASCII
|
||||
if (U'\x20' <= wc && wc < U'\x7F') [[likely]]
|
||||
return 1;
|
||||
|
||||
// C0/C1 control char
|
||||
// NOTE: Not vanilla implementation. Return 0 instead of 1.
|
||||
if ((wc && wc < L'\x20') || (L'\x7F' <= wc && wc < L'\xA0')) return 0;
|
||||
|
||||
// Zero-width char
|
||||
if (bisearch(wc, ZERO_WIDTH)) return 0;
|
||||
|
||||
// Width 1 or 2
|
||||
return 1 + bisearch(wc, WIDE_EAST_ASIAN);
|
||||
}
|
||||
|
||||
enum class WcswidthState {
|
||||
/// Normal character.
|
||||
Normal,
|
||||
/// Under ZWJ control char.
|
||||
/// Ignore the width of next char.
|
||||
ZeroWidthJoiner,
|
||||
/// Under ANSI Escape Sequence.
|
||||
/// Following chars should be treated as escape char.
|
||||
AnsiEscape,
|
||||
/// Under CSI control sequence, a part of ANSI Escape Sequence.
|
||||
/// No width was accumulated before terminal char.
|
||||
AnsiCsiEscape,
|
||||
};
|
||||
|
||||
struct WcswidthContext {
|
||||
/// Current state.
|
||||
WcswidthState state;
|
||||
/// Tract the last computed char.
|
||||
/// It will be used for VS16 char.
|
||||
std::optional<char32_t> last_measured_char;
|
||||
};
|
||||
|
||||
Result<size_t> wcswidth(const std::u32string_view& rhs) {
|
||||
WcswidthContext ctx{WcswidthState::Normal, std::nullopt};
|
||||
size_t width = 0;
|
||||
|
||||
for (char32_t chr : rhs) {
|
||||
// Match char value
|
||||
switch (ctx.state) {
|
||||
case WcswidthState::Normal: {
|
||||
switch (chr) {
|
||||
case U'\x200D': {
|
||||
// ZWJ control char
|
||||
ctx.state = WcswidthState::ZeroWidthJoiner;
|
||||
break;
|
||||
}
|
||||
case U'\xFE0F': {
|
||||
// VS16 control char
|
||||
// If we have a char which was acknowledged and has width,
|
||||
// analyse it instead of this control char.
|
||||
if (ctx.last_measured_char.has_value()) {
|
||||
width += bisearch(ctx.last_measured_char.value(), VS16_NARROW_TO_WIDE);
|
||||
ctx.last_measured_char = std::nullopt;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case U'\x1B': {
|
||||
// ANSI escape sequence
|
||||
ctx.state = WcswidthState::AnsiEscape;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// Fetch widht for normal char
|
||||
int wcw = wcwidth(chr);
|
||||
// Tract the final non-zero char for VS16 control char
|
||||
if (wcw > 0) ctx.last_measured_char = wcw;
|
||||
// Accumulate width
|
||||
width += wcw;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WcswidthState::ZeroWidthJoiner: {
|
||||
// Eat this char and back to normal state.
|
||||
// This is what ZWJ does.
|
||||
ctx.state = WcswidthState::Normal;
|
||||
break;
|
||||
}
|
||||
case WcswidthState::AnsiEscape: {
|
||||
// Check the second char of escape sequence.
|
||||
// If it is '[', we enter CSI state,
|
||||
// otherwise we eat it and back to normal state.
|
||||
// Additionally, there is a range requirement for this char (0x40-0x5F).
|
||||
if (chr == U'[') {
|
||||
ctx.state = WcswidthState::AnsiCsiEscape;
|
||||
} else if (chr >= U'\x40' && chr <= U'\x5F') {
|
||||
ctx.state = WcswidthState::Normal;
|
||||
} else {
|
||||
return std::unexpected(Error::BadAnsiEscSeq);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WcswidthState::AnsiCsiEscape: {
|
||||
// CSI sequence is aonsisted by variable Parameter Char (count can be zero),
|
||||
// at least one Middle Char and only one Final Char.
|
||||
// So we eat all chars until we reach the terminal char.
|
||||
if (chr >= U'\x40' && chr <= U'\x7E') {
|
||||
// Final Char. Back to normal state.
|
||||
ctx.state = WcswidthState::Normal;
|
||||
} else if (chr >= U'\x30' && chr <= U'\x3F') {
|
||||
; // Parameter Char. Do nothing
|
||||
} else if (chr >= U'\x20' && chr <= U'\x2F') {
|
||||
; // Middle Char. Do nothing
|
||||
} else {
|
||||
return std::unexpected(Error::BadCsiSeq);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
Result<size_t> wcswidth(const std::u8string_view& rhs) {
|
||||
// Cast encoding
|
||||
auto u32str = ENC::to_utf32(rhs);
|
||||
if (!u32str.has_value()) return std::unexpected(Error::BadEncoding);
|
||||
// Call underlying function
|
||||
return wcswidth(u32str.value());
|
||||
}
|
||||
|
||||
} // namespace yycc::carton::wcwidth
|
47
src/yycc/carton/wcwidth.hpp
Normal file
47
src/yycc/carton/wcwidth.hpp
Normal file
@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
#include <string_view>
|
||||
#include <expected>
|
||||
|
||||
/**
|
||||
* @brief The namespace replicating Linux-specialized function, "wcswidth", in all platforms.
|
||||
* @details
|
||||
* "wcswdith" is a specialized function in Linux.
|
||||
* It was not included in POSIX standard and only provided on Linux.
|
||||
* This function can fetch how many space which given string occupied in terminal.
|
||||
* This is essential and useful function in our library.
|
||||
* So I create this namespace to make "wcswidth" be available on all platforms.
|
||||
*
|
||||
* "wcswidth" is based on \c wchar_t. In Linux, \c wchar_t is 4-bytes length.
|
||||
* It can represent any characters without surrogate pair.
|
||||
* However, in Windows, \c wchar_t is 2-bytes length.
|
||||
* There is possible surrogate pair within \c wchar_t string, which is inconvenient for our programming.
|
||||
* So in this homebrew namespace, I forcelt use \c char32_t as the basic char type.
|
||||
*
|
||||
* Due to the requirements of mine, this implementation is slightly different with original one.
|
||||
* These differences are list below:
|
||||
*
|
||||
* \li We do not return negative value for Control Char in "wcwidth",
|
||||
* because we need to support the analyse of ANSI Escape Sequence.
|
||||
* \li Due to the previous change, the type of return value of "wcwidth" and "wcswidth"
|
||||
* are changed from \c int to \c size_t because there is no negative return value.
|
||||
* \li "wcswidth" now support ANSI Escape Sequence (e.g. terminal color).
|
||||
* So it can analyse colorful output with correct space.
|
||||
*/
|
||||
namespace yycc::carton::wcwidth {
|
||||
|
||||
/// @brief Error occurs in this module
|
||||
enum class Error {
|
||||
BadEncoding, ///< Given
|
||||
BadAnsiEscSeq, ///< Bad char when processing ANSI Escape Sequence
|
||||
BadCsiSeq, ///< Bad char when processing CSI Sequence.
|
||||
};
|
||||
|
||||
/// @brief Result type for this module
|
||||
template<typename T>
|
||||
using Result = std::expected<T, Error>;
|
||||
|
||||
size_t wcwidth(char32_t wc);
|
||||
Result<size_t> wcswidth(const std::u32string_view& rhs);
|
||||
Result<size_t> wcswidth(const std::u8string_view& rhs);
|
||||
|
||||
} // namespace yycc::carton::wcwidth
|
@ -1,10 +1,7 @@
|
||||
#pragma once
|
||||
#include "../constraint.hpp"
|
||||
#include "../string.hpp"
|
||||
#include <set>
|
||||
|
||||
#define NS_YYCC_STRING ::yycc::string
|
||||
|
||||
/// @brief The namespace containing convenient function building common used Constraint instance.
|
||||
namespace yycc::constraint::builder {
|
||||
|
||||
@ -15,18 +12,12 @@ namespace yycc::constraint::builder {
|
||||
* @param[in] max_value The maximum value of range (inclusive).
|
||||
* @return The generated constraint instance which can be directly applied.
|
||||
*/
|
||||
template<typename T,
|
||||
std::enable_if_t<std::is_arithmetic_v<T> && !std::is_same_v<T, bool>, int> = 0>
|
||||
template<typename T, std::enable_if_t<std::is_arithmetic_v<T> && !std::is_same_v<T, bool>, int> = 0>
|
||||
Constraint<T> min_max_constraint(T min_value, T max_value) {
|
||||
if (min_value > max_value)
|
||||
throw std::invalid_argument("the max value must be equal or greater than min value");
|
||||
if (min_value > max_value) throw std::invalid_argument("the max value must be equal or greater than min value");
|
||||
|
||||
auto fn_check = [min_value, max_value](const T& val) -> bool {
|
||||
return (val <= max_value) && (val >= min_value);
|
||||
};
|
||||
auto fn_clamp = [min_value, max_value](const T& val) -> T {
|
||||
return std::clamp(val, min_value, max_value);
|
||||
};
|
||||
auto fn_check = [min_value, max_value](const T& val) -> bool { return (val <= max_value) && (val >= min_value); };
|
||||
auto fn_clamp = [min_value, max_value](const T& val) -> T { return std::clamp(val, min_value, max_value); };
|
||||
return Constraint<T>(std::move(fn_check), std::move(fn_clamp));
|
||||
}
|
||||
|
||||
@ -39,22 +30,15 @@ namespace yycc::constraint::builder {
|
||||
*/
|
||||
template<typename T, std::enable_if_t<std::is_enum_v<T>, int> = 0>
|
||||
Constraint<T> enum_constraint(const std::initializer_list<T>& il, size_t default_index = 0u) {
|
||||
if (default_index >= il.size())
|
||||
throw std::invalid_argument("the default index must be a valid index in given list");
|
||||
if (default_index >= il.size()) throw std::invalid_argument("the default index must be a valid index in given list");
|
||||
|
||||
T default_entry = il.begin()[default_index];
|
||||
std::set<T> entries(il);
|
||||
|
||||
// TODO: modify it as `contain` once we finish patch namespace.
|
||||
auto fn_check = [entries](const T& val) -> bool {
|
||||
return entries.find(val) != entries.end();
|
||||
};
|
||||
auto fn_check = [entries](const T& val) -> bool { return entries.contains(val); };
|
||||
auto fn_clamp = [entries, default_entry](const T& val) -> T {
|
||||
if (entries.find(val) != entries.end()) {
|
||||
return val;
|
||||
} else {
|
||||
return default_entry;
|
||||
}
|
||||
if (entries.contains(val)) return val;
|
||||
else return default_entry;
|
||||
};
|
||||
return Constraint<T>(std::move(fn_check), fn_clamp);
|
||||
}
|
||||
@ -65,30 +49,21 @@ namespace yycc::constraint::builder {
|
||||
* @param[in] default_index The index of default value in given list.
|
||||
* @return The generated constraint instance which can be directly applied.
|
||||
*/
|
||||
inline Constraint<NS_YYCC_STRING::u8string> strenum_constraint(
|
||||
const std::initializer_list<NS_YYCC_STRING::u8string_view>& il, size_t default_index = 0u) {
|
||||
if (default_index >= il.size())
|
||||
throw std::invalid_argument("the default index must be a valid index in given list");
|
||||
inline Constraint<std::u8string> strenum_constraint(const std::initializer_list<std::u8string_view>& il, size_t default_index = 0u) {
|
||||
if (default_index >= il.size()) throw std::invalid_argument("the default index must be a valid index in given list");
|
||||
|
||||
NS_YYCC_STRING::u8string default_entry = NS_YYCC_STRING::u8string(il.begin()[default_index]);
|
||||
std::set<NS_YYCC_STRING::u8string> entries;
|
||||
std::u8string default_entry = std::u8string(il.begin()[default_index]);
|
||||
std::set<std::u8string> entries;
|
||||
for (const auto& i : il) {
|
||||
entries.emplace(i);
|
||||
}
|
||||
|
||||
// TODO: modify it as `contain` once we finish patch namespace.
|
||||
auto fn_check = [entries](const NS_YYCC_STRING::u8string& val) -> bool {
|
||||
return entries.find(val) != entries.end();
|
||||
auto fn_check = [entries](const std::u8string& val) -> bool { return entries.contains(val); };
|
||||
auto fn_clamp = [entries, default_entry](const std::u8string& val) -> std::u8string {
|
||||
if (entries.contains(val)) return val;
|
||||
else return default_entry;
|
||||
};
|
||||
auto fn_clamp = [entries, default_entry](
|
||||
const NS_YYCC_STRING::u8string& val) -> NS_YYCC_STRING::u8string {
|
||||
if (entries.find(val) != entries.end()) {
|
||||
return val;
|
||||
} else {
|
||||
return default_entry;
|
||||
}
|
||||
};
|
||||
return Constraint<NS_YYCC_STRING::u8string>(std::move(fn_check), fn_clamp);
|
||||
return Constraint<std::u8string>(std::move(fn_check), fn_clamp);
|
||||
}
|
||||
|
||||
} // namespace yycc::constraint::builder
|
||||
|
@ -0,0 +1,340 @@
|
||||
#include "iconv.hpp"
|
||||
|
||||
#if defined(YYCC_FEAT_ICONV)
|
||||
|
||||
#include "../macro/endian_detector.hpp"
|
||||
#include <cerrno>
|
||||
#include <stdexcept>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
|
||||
#pragma region Iconv Shit Fix
|
||||
|
||||
// YYC MARK:
|
||||
// I don't know what Iconv is for, Iconv put an huge pieces of shit into its header file "iconv.h" (at least for me).
|
||||
// Especially a macro called iconv, which pollutes my namespace name while also can not be disabled because I need to rely on it to access essential functions.
|
||||
// I can't simply redefine it, because I can't make sure that this "iconv" is defined in that way on all platforms.
|
||||
// So I can only write some definitions of functions and types here, and extract the functions and types I need before I declare the namespace.
|
||||
// And at the same time remove those annoying macro definitions. Hopefully, the compiler will optimize these wrapper functions.
|
||||
|
||||
#include <iconv.h>
|
||||
|
||||
typedef iconv_t that_iconv_t;
|
||||
static iconv_t that_iconv_open(const char* tocode, const char* fromcode) {
|
||||
return iconv_open(tocode, fromcode);
|
||||
}
|
||||
static int that_iconv_close(iconv_t cd) {
|
||||
return iconv_close(cd);
|
||||
}
|
||||
static size_t that_iconv(iconv_t cd, const char** inbuf, size_t* inbytesleft, char** outbuf, size_t* outbytesleft) {
|
||||
// YYC MARK:
|
||||
// This is also bullshit. I don't know why the real signature of this function differ with its document written by GNU.
|
||||
// I have to make a "const" cast in there.
|
||||
return iconv(cd, const_cast<char**>(inbuf), inbytesleft, outbuf, outbytesleft);
|
||||
}
|
||||
|
||||
#undef iconv_t
|
||||
#undef iconv_open
|
||||
#undef iconv_close
|
||||
#undef iconv
|
||||
|
||||
#pragma endregion
|
||||
|
||||
namespace yycc::encoding::iconv {
|
||||
|
||||
static const that_iconv_t INVALID_ICONV_TOKEN = reinterpret_cast<that_iconv_t>(-1);
|
||||
|
||||
#pragma region PrivToken
|
||||
|
||||
class PrivToken {
|
||||
public:
|
||||
PrivToken(const CodeName& from_code, const CodeName& to_code) : inner(INVALID_ICONV_TOKEN) {
|
||||
// We must cast them into string container, not string view,
|
||||
// because they may not have NULL terminator.
|
||||
std::string iconv_from_code(from_code);
|
||||
std::string iconv_to_code(to_code);
|
||||
// Call iconv_t creator
|
||||
that_iconv_t descriptor = that_iconv_open(iconv_to_code.c_str(), iconv_from_code.c_str());
|
||||
if (descriptor == INVALID_ICONV_TOKEN) {
|
||||
if (errno == EINVAL) {
|
||||
return;
|
||||
} else {
|
||||
throw std::runtime_error("impossible errno when calling iconv_open()");
|
||||
}
|
||||
}
|
||||
// Setup value
|
||||
this->inner = descriptor;
|
||||
}
|
||||
~PrivToken() {
|
||||
if (this->inner != INVALID_ICONV_TOKEN) {
|
||||
that_iconv_close(this->inner);
|
||||
}
|
||||
}
|
||||
PrivToken(PrivToken&& rhs) noexcept : inner(rhs.inner) {
|
||||
// Reset rhs inner
|
||||
rhs.inner = INVALID_ICONV_TOKEN;
|
||||
}
|
||||
PrivToken& operator=(PrivToken&& rhs) noexcept {
|
||||
// Free self first
|
||||
if (this->inner != INVALID_ICONV_TOKEN) {
|
||||
that_iconv_close(this->inner);
|
||||
}
|
||||
// Copy rhs inner and reset it.
|
||||
this->inner = rhs.inner;
|
||||
rhs.inner = INVALID_ICONV_TOKEN;
|
||||
// Return self
|
||||
return *this;
|
||||
}
|
||||
YYCC_DELETE_COPY(PrivToken)
|
||||
|
||||
bool is_valid() const { return this->inner != INVALID_ICONV_TOKEN; }
|
||||
that_iconv_t get_inner() const { return this->inner; }
|
||||
|
||||
private:
|
||||
that_iconv_t inner;
|
||||
};
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Token
|
||||
|
||||
Token::Token(const CodeName& from_code, const CodeName& to_code) : inner(nullptr) {
|
||||
this->inner = new PrivToken(from_code, to_code);
|
||||
}
|
||||
|
||||
Token::~Token() {
|
||||
if (this->inner != nullptr) {
|
||||
delete this->inner;
|
||||
}
|
||||
}
|
||||
|
||||
Token::Token(Token&& rhs) noexcept : inner(rhs.inner) {
|
||||
rhs.inner = nullptr;
|
||||
}
|
||||
|
||||
Token& Token::operator=(Token&& rhs) noexcept {
|
||||
this->inner = rhs.inner;
|
||||
rhs.inner = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool Token::is_valid() const {
|
||||
return this->inner->is_valid();
|
||||
}
|
||||
|
||||
PrivToken* Token::get_inner() const {
|
||||
return this->inner;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Kernel
|
||||
|
||||
constexpr const size_t ICONV_INC_LEN = 16u;
|
||||
constexpr size_t ICONV_ERR_RV = static_cast<size_t>(-1);
|
||||
|
||||
// Reference: https://stackoverflow.com/questions/13297458/simple-utf8-utf16-string-conversion-with-iconv
|
||||
|
||||
static ConvResult<std::vector<uint8_t>> iconv_kernel(const Token& token, const uint8_t* str_from_buf, size_t str_from_len) {
|
||||
// ===== Check Requirements =====
|
||||
// Prepare return value
|
||||
std::vector<uint8_t> str_to;
|
||||
|
||||
// Unwrap and check iconv_t
|
||||
that_iconv_t cd = token.get_inner()->get_inner();
|
||||
if (cd == INVALID_ICONV_TOKEN) return std::unexpected(ConvError::InvalidCd);
|
||||
|
||||
// Check empty input
|
||||
if (str_from_len == 0u) return str_to;
|
||||
// Check nullptr input variables
|
||||
if (str_from_buf == nullptr) return std::unexpected(ConvError::NullPointer);
|
||||
|
||||
// ===== Do Iconv =====
|
||||
// setup input variables
|
||||
size_t inbytesleft = str_from_len;
|
||||
const char* inbuf = reinterpret_cast<const char*>(str_from_buf);
|
||||
// pre-allocation output variables
|
||||
str_to.resize(str_from_len + ICONV_INC_LEN);
|
||||
size_t outbytesleft = str_to.size();
|
||||
char* outbuf = reinterpret_cast<char*>(str_to.data());
|
||||
|
||||
// conv core
|
||||
size_t nchars = that_iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
|
||||
while (nchars == ICONV_ERR_RV && errno == E2BIG) {
|
||||
// record the length has been converted
|
||||
size_t len = outbuf - reinterpret_cast<char*>(str_to.data());
|
||||
|
||||
// resize for container and its variables
|
||||
str_to.resize(str_to.size() + ICONV_INC_LEN);
|
||||
outbytesleft += ICONV_INC_LEN;
|
||||
|
||||
// assign new outbuf from failed position
|
||||
outbuf = reinterpret_cast<char*>(str_to.data()) + len;
|
||||
nchars = that_iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
|
||||
}
|
||||
|
||||
// restore descriptor initial state
|
||||
that_iconv(cd, nullptr, nullptr, nullptr, nullptr);
|
||||
|
||||
// check error
|
||||
if (nchars == ICONV_ERR_RV) {
|
||||
if (errno == EILSEQ) {
|
||||
return std::unexpected(ConvError::InvalidMbSeq);
|
||||
} else if (errno == EINVAL) {
|
||||
return std::unexpected(ConvError::IncompleteMbSeq);
|
||||
} else {
|
||||
throw std::runtime_error("impossible errno when calling iconv_open()");
|
||||
}
|
||||
} else {
|
||||
// success
|
||||
// compute result data
|
||||
str_to.resize(str_to.size() - outbytesleft);
|
||||
return str_to;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Convertion Class Helper
|
||||
|
||||
// YYC MARK:
|
||||
// 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.
|
||||
|
||||
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;
|
||||
#else
|
||||
"UTF-16BE"sv;
|
||||
#endif
|
||||
constexpr auto UTF32_CODENAME_LITERAL =
|
||||
#if defined(YYCC_ENDIAN_LITTLE)
|
||||
"UTF-32LE"sv;
|
||||
#else
|
||||
"UTF-32BE"sv;
|
||||
#endif
|
||||
|
||||
// TODO:
|
||||
// There is a memory copy in this function. Consider optimizing it in future.
|
||||
// A possible solution is that create a std::vector-like wrapper for std::basic_string and std::basic_string_view.
|
||||
// We call them VecString and VecStringView, and use them in "iconv_kernel" instead of real std::vector.
|
||||
// They exposed interface are std::vector-like but its inner is std::basic_string and std::basic_string_view.
|
||||
#define USER_CONVFN(src_char_type, dst_char_type) \
|
||||
auto rv = iconv_kernel(this->token, reinterpret_cast<const uint8_t*>(src.data()), src.size() * sizeof(src_char_type)); \
|
||||
if (rv.has_value()) { \
|
||||
const auto& dst = rv.value(); \
|
||||
if constexpr (sizeof(dst_char_type) > 1u) { \
|
||||
if (dst.size() % sizeof(dst_char_type) != 0u) return std::unexpected(ConvError::BadRv); \
|
||||
} \
|
||||
return std::basic_string<dst_char_type>(reinterpret_cast<const dst_char_type*>(dst.data()), dst.size() / sizeof(dst_char_type)); \
|
||||
} else { \
|
||||
return std::unexpected(rv.error()); \
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Char -> UTF8
|
||||
|
||||
CharToUtf8::CharToUtf8(const CodeName& code_name) : token(code_name, UTF8_CODENAME_LITERAL) {}
|
||||
|
||||
CharToUtf8::~CharToUtf8() {}
|
||||
|
||||
ConvResult<std::u8string> CharToUtf8::to_utf8(const std::string_view& src) {
|
||||
USER_CONVFN(char, char8_t);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region UTF8 -> Char
|
||||
|
||||
Utf8ToChar::Utf8ToChar(const CodeName& code_name) : token(UTF8_CODENAME_LITERAL, code_name) {}
|
||||
|
||||
Utf8ToChar::~Utf8ToChar() {}
|
||||
|
||||
ConvResult<std::string> Utf8ToChar::to_char(const std::u8string_view& src) {
|
||||
USER_CONVFN(char8_t, char);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region WChar -> Char
|
||||
|
||||
WcharToUtf8::WcharToUtf8() : token(WCHAR_CODENAME_LITERAL, UTF8_CODENAME_LITERAL) {}
|
||||
|
||||
WcharToUtf8::~WcharToUtf8() {}
|
||||
|
||||
ConvResult<std::u8string> WcharToUtf8::to_utf8(const std::wstring_view& src) {
|
||||
USER_CONVFN(wchar_t, char8_t);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Char -> WChar
|
||||
|
||||
Utf8ToWchar::Utf8ToWchar() : token(UTF8_CODENAME_LITERAL, WCHAR_CODENAME_LITERAL) {}
|
||||
|
||||
Utf8ToWchar::~Utf8ToWchar() {}
|
||||
|
||||
ConvResult<std::wstring> Utf8ToWchar::to_wchar(const std::u8string_view& src) {
|
||||
USER_CONVFN(char8_t, wchar_t);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region UTF8 -> UTF16
|
||||
|
||||
Utf8ToUtf16::Utf8ToUtf16() : token(UTF8_CODENAME_LITERAL, UTF16_CODENAME_LITERAL) {}
|
||||
|
||||
Utf8ToUtf16::~Utf8ToUtf16() {}
|
||||
|
||||
ConvResult<std::u16string> Utf8ToUtf16::to_utf16(const std::u8string_view& src) {
|
||||
USER_CONVFN(char8_t, char16_t);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region UTF16 -> UTF8
|
||||
|
||||
Utf16ToUtf8::Utf16ToUtf8() : token(UTF16_CODENAME_LITERAL, UTF8_CODENAME_LITERAL) {}
|
||||
|
||||
Utf16ToUtf8::~Utf16ToUtf8() {}
|
||||
|
||||
ConvResult<std::u8string> Utf16ToUtf8::to_utf8(const std::u16string_view& src) {
|
||||
USER_CONVFN(char16_t, char8_t);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region UTF8 -> UTF32
|
||||
|
||||
Utf8ToUtf32::Utf8ToUtf32() : token(UTF8_CODENAME_LITERAL, UTF32_CODENAME_LITERAL) {}
|
||||
|
||||
Utf8ToUtf32::~Utf8ToUtf32() {}
|
||||
|
||||
ConvResult<std::u32string> Utf8ToUtf32::to_utf32(const std::u8string_view& src) {
|
||||
USER_CONVFN(char8_t, char32_t);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region UTF32 -> UTF8
|
||||
|
||||
Utf32ToUtf8::Utf32ToUtf8() : token(UTF32_CODENAME_LITERAL, UTF8_CODENAME_LITERAL) {}
|
||||
|
||||
Utf32ToUtf8::~Utf32ToUtf8() {}
|
||||
|
||||
ConvResult<std::u8string> Utf32ToUtf8::to_utf8(const std::u32string_view& src) {
|
||||
USER_CONVFN(char32_t, char8_t);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace yycc::encoding::iconv
|
||||
|
||||
#endif
|
||||
|
@ -0,0 +1,175 @@
|
||||
#pragma once
|
||||
#include "../macro/os_detector.hpp"
|
||||
#include "../macro/class_copy_move.hpp"
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <expected>
|
||||
|
||||
namespace yycc::encoding::iconv {
|
||||
|
||||
// YYC MARK:
|
||||
// I don't want to include "iconv.h" in there.
|
||||
// One of reasons is that I want to hide all implementation of Iconv.
|
||||
// Another reason is that "iconv.h" defines some annoying macros which intervene some names in this files.
|
||||
// So I introduce PIMPL design mode. Use a pointer to hide all details in class PrivToken.
|
||||
|
||||
/// @brief The code name type used by Iconv.
|
||||
using CodeName = std::string_view;
|
||||
|
||||
/// @private
|
||||
class PrivToken;
|
||||
|
||||
/// @private
|
||||
class Token {
|
||||
public:
|
||||
Token(const CodeName& from_code, const CodeName& to_code);
|
||||
~Token();
|
||||
Token(Token&& rhs) noexcept;
|
||||
Token& operator=(Token&& rhs) noexcept;
|
||||
YYCC_DELETE_COPY(Token)
|
||||
|
||||
bool is_valid() const;
|
||||
PrivToken* get_inner() const;
|
||||
|
||||
private:
|
||||
PrivToken* inner;
|
||||
};
|
||||
|
||||
/// @brief The possible error occurs in this module.
|
||||
enum class ConvError {
|
||||
InvalidCd, ///< Given token is invalid.
|
||||
NullPointer, ///< Some of essential pointer in argument is nullptr.
|
||||
InvalidMbSeq, ///< An invalid multibyte sequence has been encountered in the input.
|
||||
IncompleteMbSeq, ///< An incomplete multibyte sequence has been encountered in the input.
|
||||
BadRv, ///< The size of encoding convertion is not matched with expected char type.
|
||||
};
|
||||
|
||||
/// @brief The result type in this module.
|
||||
template<typename T>
|
||||
using ConvResult = std::expected<T, ConvError>;
|
||||
|
||||
#if defined(YYCC_FEAT_ICONV)
|
||||
|
||||
/// @brief Char -> UTF8
|
||||
class CharToUtf8 {
|
||||
public:
|
||||
CharToUtf8(const CodeName& code_name);
|
||||
~CharToUtf8();
|
||||
YYCC_DELETE_COPY(CharToUtf8)
|
||||
YYCC_DEFAULT_MOVE(CharToUtf8)
|
||||
|
||||
public:
|
||||
ConvResult<std::u8string> to_utf8(const std::string_view& src);
|
||||
|
||||
private:
|
||||
Token token;
|
||||
};
|
||||
|
||||
/// @brief UTF8 -> Char
|
||||
class Utf8ToChar {
|
||||
public:
|
||||
Utf8ToChar(const CodeName& code_name);
|
||||
~Utf8ToChar();
|
||||
YYCC_DELETE_COPY(Utf8ToChar)
|
||||
YYCC_DEFAULT_MOVE(Utf8ToChar)
|
||||
|
||||
public:
|
||||
ConvResult<std::string> to_char(const std::u8string_view& src);
|
||||
|
||||
private:
|
||||
Token token;
|
||||
};
|
||||
|
||||
/// @brief WChar -> UTF8
|
||||
class WcharToUtf8 {
|
||||
public:
|
||||
WcharToUtf8();
|
||||
~WcharToUtf8();
|
||||
YYCC_DELETE_COPY(WcharToUtf8)
|
||||
YYCC_DEFAULT_MOVE(WcharToUtf8)
|
||||
|
||||
public:
|
||||
ConvResult<std::u8string> to_utf8(const std::wstring_view& src);
|
||||
|
||||
private:
|
||||
Token token;
|
||||
};
|
||||
|
||||
/// @brief UTF8 -> WChar
|
||||
class Utf8ToWchar {
|
||||
public:
|
||||
Utf8ToWchar();
|
||||
~Utf8ToWchar();
|
||||
YYCC_DELETE_COPY(Utf8ToWchar)
|
||||
YYCC_DEFAULT_MOVE(Utf8ToWchar)
|
||||
|
||||
public:
|
||||
ConvResult<std::wstring> to_wchar(const std::u8string_view& src);
|
||||
|
||||
private:
|
||||
Token token;
|
||||
};
|
||||
|
||||
/// @brief UTF8 -> UTF16
|
||||
class Utf8ToUtf16 {
|
||||
public:
|
||||
Utf8ToUtf16();
|
||||
~Utf8ToUtf16();
|
||||
YYCC_DELETE_COPY(Utf8ToUtf16)
|
||||
YYCC_DEFAULT_MOVE(Utf8ToUtf16)
|
||||
|
||||
public:
|
||||
ConvResult<std::u16string> to_utf16(const std::u8string_view& src);
|
||||
|
||||
private:
|
||||
Token token;
|
||||
};
|
||||
|
||||
/// @brief UTF16 -> UTF8
|
||||
class Utf16ToUtf8 {
|
||||
public:
|
||||
Utf16ToUtf8();
|
||||
~Utf16ToUtf8();
|
||||
YYCC_DELETE_COPY(Utf16ToUtf8)
|
||||
YYCC_DEFAULT_MOVE(Utf16ToUtf8)
|
||||
|
||||
public:
|
||||
ConvResult<std::u8string> to_utf8(const std::u16string_view& src);
|
||||
|
||||
private:
|
||||
Token token;
|
||||
};
|
||||
|
||||
/// @brief UTF8 -> UTF32
|
||||
class Utf8ToUtf32 {
|
||||
public:
|
||||
Utf8ToUtf32();
|
||||
~Utf8ToUtf32();
|
||||
YYCC_DELETE_COPY(Utf8ToUtf32)
|
||||
YYCC_DEFAULT_MOVE(Utf8ToUtf32)
|
||||
|
||||
public:
|
||||
ConvResult<std::u32string> to_utf32(const std::u8string_view& src);
|
||||
|
||||
private:
|
||||
Token token;
|
||||
};
|
||||
|
||||
/// @brief UTF32 -> UTF8
|
||||
class Utf32ToUtf8 {
|
||||
public:
|
||||
Utf32ToUtf8();
|
||||
~Utf32ToUtf8();
|
||||
YYCC_DELETE_COPY(Utf32ToUtf8)
|
||||
YYCC_DEFAULT_MOVE(Utf32ToUtf8)
|
||||
|
||||
public:
|
||||
ConvResult<std::u8string> to_utf8(const std::u32string_view& src);
|
||||
|
||||
private:
|
||||
Token token;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace yycc::encoding::iconv
|
||||
|
118
src/yycc/encoding/stl.cpp
Normal file
118
src/yycc/encoding/stl.cpp
Normal file
@ -0,0 +1,118 @@
|
||||
#include "stl.hpp"
|
||||
#include <locale>
|
||||
|
||||
namespace yycc::encoding::stl {
|
||||
|
||||
#pragma region Generic Converter
|
||||
|
||||
/*
|
||||
* YYC MARK:
|
||||
* According to the documentation introduced in CppReference.
|
||||
* The standard library is guaranteed to provide several specific specializations of \c std::codecvt.
|
||||
* The UTF8 char type in UTF8 related specializations of \c std::codecvt is different in different C++ standard.
|
||||
* But the oldest C++ version YYCC supported is C++ 23, char8_t is the only viable UTF8 char type for \c std::codecvt.
|
||||
* So we can simply and safely use it to correctly trigger specific specializations of \c std::codecv in there.
|
||||
*/
|
||||
|
||||
template<typename TChar>
|
||||
requires(std::is_same_v<TChar, char16_t> || std::is_same_v<TChar, char32_t>)
|
||||
using CodecvtFacet = std::codecvt<TChar, char8_t, std::mbstate_t>;
|
||||
|
||||
template<typename TChar>
|
||||
requires(std::is_same_v<TChar, char16_t> || std::is_same_v<TChar, char32_t>)
|
||||
static ConvResult<std::basic_string<TChar>> generic_to_utf_other(const std::u8string_view& src) {
|
||||
// Reference:
|
||||
// https://en.cppreference.com/w/cpp/locale/codecvt/in
|
||||
|
||||
// prepare return value
|
||||
std::basic_string<TChar> dst;
|
||||
|
||||
// if src is empty, return directly
|
||||
if (src.empty()) {
|
||||
return dst;
|
||||
}
|
||||
|
||||
// init locale and get codecvt facet
|
||||
// same reason in UTFOtherToUTF8 to keeping reference to locale
|
||||
const auto& this_locale = std::locale::classic();
|
||||
const auto& this_codecvt = std::use_facet<CodecvtFacet<TChar>>(this_locale);
|
||||
|
||||
// convertion preparation
|
||||
std::mbstate_t mb{};
|
||||
dst.resize(src.size());
|
||||
const char8_t *intern_from = reinterpret_cast<const char8_t*>(src.data()),
|
||||
*intern_from_end = reinterpret_cast<const char8_t*>(src.data() + src.size()), *intern_from_next = nullptr;
|
||||
TChar *extern_to = dst.data(), *extern_to_end = dst.data() + dst.size(), *extern_to_next = nullptr;
|
||||
// do convertion
|
||||
auto result = this_codecvt.in(mb, intern_from, intern_from_end, intern_from_next, extern_to, extern_to_end, extern_to_next);
|
||||
|
||||
// check result
|
||||
if (result != CodecvtFacet<TChar>::ok) return std::unexpected(ConvError{});
|
||||
// resize result and return
|
||||
dst.resize(extern_to_next - dst.data());
|
||||
return dst;
|
||||
}
|
||||
|
||||
template<typename TChar>
|
||||
requires(std::is_same_v<TChar, char16_t> || std::is_same_v<TChar, char32_t>)
|
||||
static ConvResult<std::u8string> generic_to_utf8(const std::basic_string_view<TChar>& src) {
|
||||
// Reference:
|
||||
// https://en.cppreference.com/w/cpp/locale/codecvt/out
|
||||
|
||||
// prepare return value
|
||||
std::u8string dst;
|
||||
|
||||
// if src is empty, return directly
|
||||
if (src.empty()) {
|
||||
return dst;
|
||||
}
|
||||
|
||||
// init locale and get codecvt facet
|
||||
// the reference to locale must be preserved until convertion done.
|
||||
// because the life time of codecvt facet is equal to the reference to locale.
|
||||
const auto& this_locale = std::locale::classic();
|
||||
const auto& this_codecvt = std::use_facet<CodecvtFacet<TChar>>(this_locale);
|
||||
|
||||
// do convertion preparation
|
||||
std::mbstate_t mb{};
|
||||
dst.resize(src.size() * this_codecvt.max_length());
|
||||
const TChar *intern_from = src.data(), *intern_from_end = src.data() + src.size(), *intern_from_next = nullptr;
|
||||
char8_t *extern_to = reinterpret_cast<char8_t*>(dst.data()), *extern_to_end = reinterpret_cast<char8_t*>(dst.data() + dst.size()),
|
||||
*extern_to_next = nullptr;
|
||||
// do convertion
|
||||
auto result = this_codecvt.out(mb, intern_from, intern_from_end, intern_from_next, extern_to, extern_to_end, extern_to_next);
|
||||
|
||||
// check result
|
||||
if (result != CodecvtFacet<TChar>::ok) return std::unexpected(ConvError{});
|
||||
// resize result and retuen
|
||||
dst.resize(extern_to_next - reinterpret_cast<char8_t*>(dst.data()));
|
||||
return dst;
|
||||
}
|
||||
|
||||
#pragma endregion Converter
|
||||
|
||||
#pragma region
|
||||
|
||||
ConvResult<std::u16string> to_utf16(const std::u8string_view& src) {
|
||||
// UTF8 -> UTF16
|
||||
return generic_to_utf_other<char16_t>(src);
|
||||
}
|
||||
|
||||
ConvResult<std::u8string> to_utf8(const std::u16string_view& src) {
|
||||
// UTF16 -> UTF8
|
||||
return generic_to_utf8<char16_t>(src);
|
||||
}
|
||||
|
||||
ConvResult<std::u32string> to_utf32(const std::u8string_view& src) {
|
||||
// UTF8 -> UTF32
|
||||
return generic_to_utf_other<char32_t>(src);
|
||||
}
|
||||
|
||||
ConvResult<std::u8string> to_utf8(const std::u32string_view& src) {
|
||||
// UTF32 -> UTF8
|
||||
return generic_to_utf8<char32_t>(src);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace yycc::encoding::stlcvt
|
43
src/yycc/encoding/stl.hpp
Normal file
43
src/yycc/encoding/stl.hpp
Normal file
@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <expected>
|
||||
|
||||
namespace yycc::encoding::stl {
|
||||
|
||||
/// @brief Possible convertion error occurs in this module.
|
||||
struct ConvError {};
|
||||
|
||||
/// @brief The result type of this module.
|
||||
template<typename T>
|
||||
using ConvResult = std::expected<T, ConvError>;
|
||||
|
||||
/**
|
||||
* @brief UTF8 -> UTF16
|
||||
* @param[in] src The string to be converted.
|
||||
* @return The converted string, or error occurring.
|
||||
*/
|
||||
ConvResult<std::u16string> to_utf16(const std::u8string_view& src);
|
||||
|
||||
/**
|
||||
* @brief UTF16 -> UTF8
|
||||
* @param[in] src The string to be converted.
|
||||
* @return The converted string, or error occurring.
|
||||
*/
|
||||
ConvResult<std::u8string> to_utf8(const std::u16string_view& src);
|
||||
|
||||
/**
|
||||
* @brief UTF8 -> UTF32
|
||||
* @param[in] src The string to be converted.
|
||||
* @return The converted string, or error occurring.
|
||||
*/
|
||||
ConvResult<std::u32string> to_utf32(const std::u8string_view& src);
|
||||
|
||||
/**
|
||||
* @brief UTF32 -> UTF8
|
||||
* @param[in] src The string to be converted.
|
||||
* @return The converted string, or error occurring.
|
||||
*/
|
||||
ConvResult<std::u8string> to_utf8(const std::u32string_view& src);
|
||||
|
||||
}
|
@ -1,201 +0,0 @@
|
||||
#include "utf.hpp"
|
||||
#include "../macro/feature_probe.hpp"
|
||||
#include <locale>
|
||||
|
||||
#define NS_YYCC_STRING ::yycc::string
|
||||
|
||||
namespace yycc::encoding::utf {
|
||||
|
||||
#pragma region Generic Converter
|
||||
|
||||
/*
|
||||
* NOTE:
|
||||
* According to the documentation introduced in CppReference.
|
||||
* The standard library is guaranteed to provide several specific specializations of \c std::codecvt.
|
||||
* The UTF8 char type in UTF8 related specializations of \c std::codecvt is different.
|
||||
* It is also independend from we defined char type.
|
||||
* So it is essential define a type which can correctly trigger specific specializations of \c std::codecv in there.
|
||||
*/
|
||||
|
||||
#if defined(YYCC_CPPFEAT_UTF8)
|
||||
using CodecvtUtf8Char = char8_t;
|
||||
#else
|
||||
using CodecvtUtf8Char = char;
|
||||
#endif
|
||||
|
||||
template<typename TChar,
|
||||
std::enable_if_t<std::is_same_v<TChar, char16_t> || std::is_same_v<TChar, char32_t>, int>
|
||||
= 0>
|
||||
using CodecvtFacet = std::codecvt<TChar, CodecvtUtf8Char, std::mbstate_t>;
|
||||
|
||||
template<typename TChar,
|
||||
std::enable_if_t<std::is_same_v<TChar, char16_t> || std::is_same_v<TChar, char32_t>, int>
|
||||
= 0>
|
||||
static ConvResult<std::basic_string<TChar>> generic_to_utf_other(
|
||||
const NS_YYCC_STRING::u8string_view& src) {
|
||||
// Reference:
|
||||
// https://en.cppreference.com/w/cpp/locale/codecvt/in
|
||||
|
||||
// prepare return value
|
||||
std::basic_string<TChar> dst;
|
||||
|
||||
// if src is empty, return directly
|
||||
if (src.empty()) {
|
||||
return dst;
|
||||
}
|
||||
|
||||
// init locale and get codecvt facet
|
||||
// same reason in UTFOtherToUTF8 to keeping reference to locale
|
||||
const auto& this_locale = std::locale::classic();
|
||||
const auto& this_codecvt = std::use_facet<CodecvtFacet<TChar>>(this_locale);
|
||||
|
||||
// convertion preparation
|
||||
std::mbstate_t mb{};
|
||||
dst.resize(src.size());
|
||||
const CodecvtUtf8Char *intern_from = reinterpret_cast<const CodecvtUtf8Char*>(src.data()),
|
||||
*intern_from_end = reinterpret_cast<const CodecvtUtf8Char*>(
|
||||
src.data() + src.size()),
|
||||
*intern_from_next = nullptr;
|
||||
TChar *extern_to = dst.data(), *extern_to_end = dst.data() + dst.size(),
|
||||
*extern_to_next = nullptr;
|
||||
// do convertion
|
||||
auto result = this_codecvt.in(mb,
|
||||
intern_from,
|
||||
intern_from_end,
|
||||
intern_from_next,
|
||||
extern_to,
|
||||
extern_to_end,
|
||||
extern_to_next);
|
||||
|
||||
// check result
|
||||
if (result != CodecvtFacet<TChar>::ok) return ConvError();
|
||||
// resize result and return
|
||||
dst.resize(extern_to_next - dst.data());
|
||||
return dst;
|
||||
}
|
||||
|
||||
template<typename TChar,
|
||||
std::enable_if_t<std::is_same_v<TChar, char16_t> || std::is_same_v<TChar, char32_t>, int>
|
||||
= 0>
|
||||
static ConvResult<NS_YYCC_STRING::u8string> generic_to_utf8(
|
||||
const std::basic_string_view<TChar>& src) {
|
||||
// Reference:
|
||||
// https://en.cppreference.com/w/cpp/locale/codecvt/out
|
||||
|
||||
// prepare return value
|
||||
NS_YYCC_STRING::u8string dst;
|
||||
|
||||
// if src is empty, return directly
|
||||
if (src.empty()) {
|
||||
return dst;
|
||||
}
|
||||
|
||||
// init locale and get codecvt facet
|
||||
// the reference to locale must be preserved until convertion done.
|
||||
// because the life time of codecvt facet is equal to the reference to locale.
|
||||
const auto& this_locale = std::locale::classic();
|
||||
const auto& this_codecvt = std::use_facet<CodecvtFacet<TChar>>(this_locale);
|
||||
|
||||
// do convertion preparation
|
||||
std::mbstate_t mb{};
|
||||
dst.resize(src.size() * this_codecvt.max_length());
|
||||
const TChar *intern_from = src.data(), *intern_from_end = src.data() + src.size(),
|
||||
*intern_from_next = nullptr;
|
||||
CodecvtUtf8Char *extern_to = reinterpret_cast<CodecvtUtf8Char*>(dst.data()),
|
||||
*extern_to_end = reinterpret_cast<CodecvtUtf8Char*>(dst.data() + dst.size()),
|
||||
*extern_to_next = nullptr;
|
||||
// do convertion
|
||||
auto result = this_codecvt.out(mb,
|
||||
intern_from,
|
||||
intern_from_end,
|
||||
intern_from_next,
|
||||
extern_to,
|
||||
extern_to_end,
|
||||
extern_to_next);
|
||||
|
||||
// check result
|
||||
if (result != CodecvtFacet<TChar>::ok) return ConvError();
|
||||
// resize result and retuen
|
||||
dst.resize(extern_to_next - reinterpret_cast<CodecvtUtf8Char*>(dst.data()));
|
||||
return dst;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Help Macros
|
||||
|
||||
#define CONVFN_TYPE1(fct_name, src_char_type, dst_char_type) \
|
||||
auto rv = priv_##fct_name(src); \
|
||||
if (const auto* ptr = std::get_if<std::basic_string<dst_char_type>>(&rv)) { \
|
||||
dst = std::move(*ptr); \
|
||||
return true; \
|
||||
} else if (const auto* ptr = std::get_if<ConvError>(&rv)) { \
|
||||
return false; \
|
||||
} else { \
|
||||
throw std::runtime_error("unreachable code"); \
|
||||
}
|
||||
|
||||
#define CONVFN_TYPE2(fct_name, src_char_type, dst_char_type) \
|
||||
std::basic_string<dst_char_type> rv; \
|
||||
if (fct_name(src, rv)) return rv; \
|
||||
else throw std::runtime_error("fail to convert utf string");
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region UTF8 -> UTF16
|
||||
|
||||
ConvResult<std::u16string> priv_to_utf16(const NS_YYCC_STRING::u8string_view& src) {
|
||||
return generic_to_utf_other<char16_t>(src);
|
||||
}
|
||||
bool to_utf16(const NS_YYCC_STRING::u8string_view& src, std::u16string& dst) {
|
||||
CONVFN_TYPE1(to_utf16, NS_YYCC_STRING::u8char, char16_t);
|
||||
}
|
||||
std::u16string to_utf16(const NS_YYCC_STRING::u8string_view& src) {
|
||||
CONVFN_TYPE2(to_utf16, NS_YYCC_STRING::u8char, char16_t);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region UTF16 -> UTF8
|
||||
|
||||
ConvResult<NS_YYCC_STRING::u8string> priv_to_utf8(const std::u16string_view& src) {
|
||||
return generic_to_utf8<char16_t>(src);
|
||||
}
|
||||
bool to_utf8(const std::u16string_view& src, NS_YYCC_STRING::u8string& dst) {
|
||||
CONVFN_TYPE1(to_utf8, char16_t, NS_YYCC_STRING::u8char);
|
||||
}
|
||||
NS_YYCC_STRING::u8string to_utf8(const std::u16string_view& src) {
|
||||
CONVFN_TYPE2(to_utf8, char16_t, NS_YYCC_STRING::u8char);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region UTF8 -> UTF32
|
||||
|
||||
ConvResult<std::u32string> priv_to_utf32(const NS_YYCC_STRING::u8string_view& src) {
|
||||
return generic_to_utf_other<char32_t>(src);
|
||||
}
|
||||
bool to_utf32(const NS_YYCC_STRING::u8string_view& src, std::u32string& dst) {
|
||||
CONVFN_TYPE1(to_utf32, NS_YYCC_STRING::u8char, char32_t);
|
||||
}
|
||||
std::u32string to_utf32(const NS_YYCC_STRING::u8string_view& src) {
|
||||
CONVFN_TYPE2(to_utf32, NS_YYCC_STRING::u8char, char32_t);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region UTF32 -> UTF8
|
||||
|
||||
ConvResult<NS_YYCC_STRING::u8string> priv_to_utf8(const std::u32string_view& src) {
|
||||
return generic_to_utf8<char32_t>(src);
|
||||
}
|
||||
bool to_utf8(const std::u32string_view& src, NS_YYCC_STRING::u8string& dst) {
|
||||
CONVFN_TYPE1(to_utf8, char32_t, NS_YYCC_STRING::u8char);
|
||||
}
|
||||
NS_YYCC_STRING::u8string to_utf8(const std::u32string_view& src) {
|
||||
CONVFN_TYPE2(to_utf8, char32_t, NS_YYCC_STRING::u8char);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace yycc::encoding::utf
|
@ -1,43 +0,0 @@
|
||||
#pragma once
|
||||
#include <yycc/string.hpp>
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
|
||||
#define NS_YYCC_STRING ::yycc::string
|
||||
|
||||
namespace yycc::encoding::utf {
|
||||
|
||||
/// @private
|
||||
struct ConvError {};
|
||||
|
||||
/// @private
|
||||
template<typename T, std::enable_if_t<!std::is_same_v<T, ConvError>, int> = 0>
|
||||
using ConvResult = std::variant<T, ConvError>;
|
||||
|
||||
// UTF8 -> UTF16
|
||||
|
||||
ConvResult<std::u16string> priv_to_utf16(const NS_YYCC_STRING::u8string_view& src);
|
||||
bool to_utf16(const NS_YYCC_STRING::u8string_view& src, std::u16string& dst);
|
||||
std::u16string to_utf16(const NS_YYCC_STRING::u8string_view& src);
|
||||
|
||||
// UTF16 -> UTF8
|
||||
|
||||
ConvResult<NS_YYCC_STRING::u8string> priv_to_utf8(const std::u16string_view& src);
|
||||
bool to_utf8(const std::u16string_view& src, NS_YYCC_STRING::u8string& dst);
|
||||
NS_YYCC_STRING::u8string to_utf8(const std::u16string_view& src);
|
||||
|
||||
// UTF8 -> UTF32
|
||||
|
||||
ConvResult<std::u32string> priv_to_utf32(const NS_YYCC_STRING::u8string_view& src);
|
||||
bool to_utf32(const NS_YYCC_STRING::u8string_view& src, std::u32string& dst);
|
||||
std::u32string to_utf32(const NS_YYCC_STRING::u8string_view& src);
|
||||
|
||||
// UTF32 -> UTF8
|
||||
|
||||
ConvResult<NS_YYCC_STRING::u8string> priv_to_utf8(const std::u32string_view& src);
|
||||
bool to_utf8(const std::u32string_view& src, NS_YYCC_STRING::u8string& dst);
|
||||
NS_YYCC_STRING::u8string to_utf8(const std::u32string_view& src);
|
||||
|
||||
}
|
||||
|
||||
#undef NS_YYCC_STRING
|
@ -0,0 +1,253 @@
|
||||
#include "windows.hpp"
|
||||
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
|
||||
#include "../string/reinterpret.hpp"
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <cuchar>
|
||||
|
||||
#include "../windows/import_guard_head.hpp"
|
||||
#include <Windows.h>
|
||||
#include "../windows/import_guard_tail.hpp"
|
||||
|
||||
#define NS_YYCC_STRING_REINTERPRET ::yycc::string::reinterpret
|
||||
|
||||
namespace yycc::encoding::windows {
|
||||
|
||||
#pragma region WideCharToMultiByte and MultiByteToWideChar stuff
|
||||
|
||||
// WChar -> Char
|
||||
ConvResult<std::string> to_char(const std::wstring_view& src, CodePage code_page) {
|
||||
// prepare result
|
||||
std::string dst;
|
||||
|
||||
// if src is empty, direct output
|
||||
if (src.empty()) {
|
||||
return dst;
|
||||
}
|
||||
|
||||
// init WideCharToMultiByte used variables
|
||||
// setup src pointer
|
||||
LPCWCH lpWideCharStr = reinterpret_cast<LPCWCH>(src.data());
|
||||
// check whether source string is too large.
|
||||
size_t cSrcSize = src.size();
|
||||
if (cSrcSize > std::numeric_limits<int>::max()) return std::unexpected(ConvError::TooLargeLength);
|
||||
int cchWideChar = static_cast<int>(src.size());
|
||||
|
||||
// do convertion
|
||||
// do a dry-run first to fetch desired size.
|
||||
int desired_size = WideCharToMultiByte(code_page, 0, lpWideCharStr, cchWideChar, NULL, 0, NULL, NULL);
|
||||
if (desired_size <= 0) return std::unexpected(ConvError::NoDesiredSize);
|
||||
// resize dest for receiving result
|
||||
dst.resize(static_cast<size_t>(desired_size));
|
||||
// do real convertion
|
||||
int write_result
|
||||
= WideCharToMultiByte(code_page, 0, lpWideCharStr, cchWideChar, reinterpret_cast<LPSTR>(dst.data()), desired_size, NULL, NULL);
|
||||
if (write_result <= 0) return std::unexpected(ConvError::BadWrittenSize);
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
// Char -> WChar
|
||||
ConvResult<std::wstring> to_wchar(const std::string_view& src, CodePage code_page) {
|
||||
// prepare result
|
||||
std::wstring dst;
|
||||
|
||||
// if src is empty, direct output
|
||||
if (src.empty()) {
|
||||
return dst;
|
||||
}
|
||||
|
||||
// init WideCharToMultiByte used variables
|
||||
// setup src pointer
|
||||
LPCCH lpMultiByteStr = reinterpret_cast<LPCCH>(src.data());
|
||||
// check whether source string is too large.
|
||||
size_t cSrcSize = src.size();
|
||||
if (cSrcSize > std::numeric_limits<int>::max()) return std::unexpected(ConvError::TooLargeLength);
|
||||
int cbMultiByte = static_cast<int>(src.size());
|
||||
|
||||
// do convertion
|
||||
// do a dry-run first to fetch desired size.
|
||||
int desired_size = MultiByteToWideChar(code_page, 0, lpMultiByteStr, cbMultiByte, NULL, 0);
|
||||
if (desired_size <= 0) return std::unexpected(ConvError::NoDesiredSize);
|
||||
// resize dest for receiving result
|
||||
dst.resize(static_cast<size_t>(desired_size));
|
||||
// do real convertion
|
||||
int write_result = MultiByteToWideChar(code_page, 0, lpMultiByteStr, cbMultiByte, reinterpret_cast<LPWSTR>(dst.data()), desired_size);
|
||||
if (write_result <= 0) return std::unexpected(ConvError::BadWrittenSize);
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
// Char -> Char
|
||||
ConvResult<std::string> to_char(const std::string_view& src, CodePage src_code_page, CodePage dst_code_page) {
|
||||
auto first_rv = to_wchar(src, src_code_page);
|
||||
return first_rv.and_then([dst_code_page](const auto& src) { return to_char(src, dst_code_page); });
|
||||
}
|
||||
|
||||
// WChar -> UTF8
|
||||
ConvResult<std::u8string> to_utf8(const std::wstring_view& src) {
|
||||
auto rv = to_char(src, CP_UTF8);
|
||||
return rv.transform([](const auto& dst) { return NS_YYCC_STRING_REINTERPRET::as_utf8(dst); });
|
||||
}
|
||||
|
||||
// UTF8 -> WChar
|
||||
ConvResult<std::wstring> to_wchar(const std::u8string_view& src) {
|
||||
return to_wchar(NS_YYCC_STRING_REINTERPRET::as_ordinary_view(src), CP_UTF8);
|
||||
}
|
||||
|
||||
// Char -> UTF8
|
||||
ConvResult<std::u8string> to_utf8(const std::string_view& src, CodePage code_page) {
|
||||
auto rv = to_char(src, code_page, CP_UTF8);
|
||||
return rv.transform([](const auto& dst) { return NS_YYCC_STRING_REINTERPRET::as_utf8(dst); });
|
||||
}
|
||||
|
||||
// UTF8 -> Char
|
||||
ConvResult<std::string> to_char(const std::u8string_view& src, CodePage code_page) {
|
||||
return to_char(NS_YYCC_STRING_REINTERPRET::as_ordinary_view(src), CP_UTF8, code_page);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region UTF stuff
|
||||
|
||||
// YYC MARK:
|
||||
// The convertion between UTF is implemented by c16rtomb, c32rtomb, mbrtoc16 and mbrtoc32.
|
||||
// These function is locale related in C++ standard, but in Microsoft STL, it's only for UTF8.
|
||||
// So we can use them safely in Win32 environment.
|
||||
// Reference:
|
||||
// * https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/c16rtomb-c32rtomb1?view=msvc-170
|
||||
// * https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/mbrtoc16-mbrtoc323?view=msvc-170
|
||||
|
||||
#if defined(YYCC_STL_MSSTL)
|
||||
|
||||
// 1 UTF32 unit can produe 4 UTF8 units or 2 UTF16 units in theory.
|
||||
// So we pre-allocate memory for the result to prevent allocating memory multiple times.
|
||||
constexpr size_t MULTIPLE_UTF8_TO_UTF16 = 1u;
|
||||
constexpr size_t MULTIPLE_UTF16_TO_UTF8 = 2u;
|
||||
constexpr size_t MULTIPLE_UTF8_TO_UTF32 = 1u;
|
||||
constexpr size_t MULTIPLE_UTF32_TO_UTF8 = 4u;
|
||||
|
||||
// UTF8 -> UTF16
|
||||
ConvResult<std::u16string> to_utf16(const std::u8string_view& src) {
|
||||
std::u16string dst;
|
||||
dst.reserve(src.size() * MULTIPLE_UTF8_TO_UTF16);
|
||||
|
||||
std::mbstate_t state{}; // zero-initialized to initial state
|
||||
char16_t c16;
|
||||
const char* ptr = reinterpret_cast<const char*>(src.data());
|
||||
const char* end = ptr + src.size();
|
||||
|
||||
// YYC MARK:
|
||||
// Due to the shitty design of mbrtoc16, it forcely assume that passed string is null-terminated.
|
||||
// And the third argument should >= 1.
|
||||
// However, our given string is string view which do not have null-terminated guaranteen.
|
||||
//
|
||||
// So we manually check whether we have reach the tail of string and simulate a fake null terminal.
|
||||
// If string is still processing, we pass given string.
|
||||
// If we have reach the tail of string, we pass our homemade NULL_TERMINAL to this function to make it works normally.
|
||||
//
|
||||
// This is a stupid polyfill, however, it I do not do this,
|
||||
// there is a bug that the second part of surrogate pair will be dropped in final string,
|
||||
// if there is a Unicode character located at the tail of string which need surrogate pair to be presented.
|
||||
static const char NULL_TERMINAL = '\0';
|
||||
while (true) {
|
||||
bool not_tail = ptr < end;
|
||||
const char* new_ptr = not_tail ? ptr : &NULL_TERMINAL;
|
||||
size_t new_size = not_tail ? end - ptr : sizeof(NULL_TERMINAL);
|
||||
size_t rc = std::mbrtoc16(&c16, new_ptr, new_size, &state);
|
||||
if (!rc) break;
|
||||
|
||||
if (rc == (size_t) -1) return std::unexpected(ConvError::EncodeUtf8);
|
||||
else if (rc == (size_t) -2) return std::unexpected(ConvError::IncompleteUtf8);
|
||||
else if (rc == (size_t) -3) dst.push_back(c16); // from earlier surrogate pair
|
||||
else {
|
||||
dst.push_back(c16);
|
||||
ptr += rc;
|
||||
}
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
// UTF16 -> UTF8
|
||||
ConvResult<std::u8string> to_utf8(const std::u16string_view& src) {
|
||||
std::u8string dst;
|
||||
dst.reserve(src.size() * MULTIPLE_UTF16_TO_UTF8);
|
||||
|
||||
std::mbstate_t state{};
|
||||
char mbout[MB_LEN_MAX]{};
|
||||
size_t rc = 1; // Assign it to ONE to avoid mismatching surrogate pair checker when string is empty.
|
||||
for (char16_t c : src) {
|
||||
rc = std::c16rtomb(mbout, c, &state);
|
||||
|
||||
if (rc == (size_t) -1) return std::unexpected(ConvError::InvalidUtf16);
|
||||
else dst.append(reinterpret_cast<char8_t*>(mbout), rc);
|
||||
}
|
||||
|
||||
if (rc == 0) {
|
||||
// YYC MARK:
|
||||
// If rc is zero after processing all chars,
|
||||
// it means that we are aborted when processing an UTF16 surrogate pair.
|
||||
// We should report it as an error.
|
||||
return std::unexpected(ConvError::InvalidUtf16);
|
||||
}
|
||||
|
||||
// Okey, return result.
|
||||
return dst;
|
||||
}
|
||||
|
||||
// UTF8 -> UTF32
|
||||
ConvResult<std::u32string> to_utf32(const std::u8string_view& src) {
|
||||
std::u32string dst;
|
||||
dst.reserve(src.size() * MULTIPLE_UTF8_TO_UTF32);
|
||||
|
||||
std::mbstate_t state{};
|
||||
char32_t c32;
|
||||
const char* ptr = reinterpret_cast<const char*>(src.data());
|
||||
const char* end = ptr + src.size();
|
||||
|
||||
while (ptr < end) {
|
||||
// YYC MARK:
|
||||
// There is no surrogate pair in UTF32,
|
||||
// so we do not need do that stupid things in UTF8 to UTF32 functions.
|
||||
size_t rc = std::mbrtoc32(&c32, ptr, end - ptr, &state);
|
||||
|
||||
if (rc == (size_t) -1) return std::unexpected(ConvError::EncodeUtf8);
|
||||
else if (rc == (size_t) -2) return std::unexpected(ConvError::IncompleteUtf8);
|
||||
else if (rc == (size_t) -3) throw std::runtime_error("no surrogates in UTF-32");
|
||||
else dst.push_back(c32);
|
||||
|
||||
ptr += rc;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
// UTF32 -> UTF8
|
||||
ConvResult<std::u8string> to_utf8(const std::u32string_view& src) {
|
||||
std::u8string dst;
|
||||
dst.reserve(src.size() * MULTIPLE_UTF32_TO_UTF8);
|
||||
|
||||
std::mbstate_t state{};
|
||||
char mbout[MB_LEN_MAX]{};
|
||||
for (char32_t c : src) {
|
||||
size_t rc = std::c32rtomb(mbout, c, &state);
|
||||
|
||||
if (rc == (size_t) -1) return std::unexpected(ConvError::InvalidUtf32);
|
||||
else dst.append(reinterpret_cast<char8_t*>(mbout), rc);
|
||||
}
|
||||
|
||||
// YYC MARK:
|
||||
// There is no surrogate pair for UTF32,
|
||||
// so this "if" statement only presented in UTF16 to UTF8 function.
|
||||
// In this function, we directly return value.
|
||||
return dst;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace yycc::encoding::windows
|
||||
|
||||
#endif
|
||||
|
@ -0,0 +1,129 @@
|
||||
#pragma once
|
||||
#include "../macro/os_detector.hpp"
|
||||
#include "../macro/stl_detector.hpp"
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <expected>
|
||||
#include <cstdint>
|
||||
|
||||
namespace yycc::encoding::windows {
|
||||
|
||||
/// @brief The type of Windows code page.
|
||||
using CodePage = uint32_t;
|
||||
|
||||
/// @brief The possible error kind occurs in this module.
|
||||
enum class ConvError {
|
||||
TooLargeLength, ///< The length of given string is too large exceeding the maximum capacity of Win32 function.
|
||||
NoDesiredSize, ///< Can not compute the desired size of result string.
|
||||
BadWrittenSize, ///< The size of written data is not matched with expected size.
|
||||
InvalidUtf32, ///< Given char is invalid in UTF32.
|
||||
InvalidUtf16, ///< Given char is invalid in UTF16.
|
||||
EncodeUtf8, ///< Error occurs when encoding UTF8.
|
||||
IncompleteUtf8, ///< Given UTF8 string is incomplete.
|
||||
};
|
||||
|
||||
/// @brief The result type in this module.
|
||||
template<typename T>
|
||||
using ConvResult = std::expected<T, ConvError>;
|
||||
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
|
||||
/**
|
||||
* @brief WChar -> Char
|
||||
* @param[in] src The string to be converted.
|
||||
* @param[in] code_page The code page of native string.
|
||||
* @return The converted string, or error occurring.
|
||||
*/
|
||||
ConvResult<std::string> to_char(const std::wstring_view& src, CodePage code_page);
|
||||
|
||||
/**
|
||||
* @brief Char -> WChar
|
||||
* @param[in] src The string to be converted.
|
||||
* @param[in] code_page The code page of native string.
|
||||
* @return The converted string, or error occurring.
|
||||
*/
|
||||
ConvResult<std::wstring> to_wchar(const std::string_view& src, CodePage code_page);
|
||||
|
||||
/**
|
||||
* @brief Char -> Char
|
||||
* @details This is the combination of "WChar -> Char" and "Char -> WChar"
|
||||
* @param[in] src The string to be converted.
|
||||
* @param[in] src_code_page The code page of source string.
|
||||
* @param[in] dst_code_page The code page of destination string.
|
||||
* @return The converted string, or error occurring.
|
||||
*/
|
||||
ConvResult<std::string> to_char(const std::string_view& src, CodePage src_code_page, CodePage dst_code_page);
|
||||
|
||||
/**
|
||||
* @brief WChar -> UTF8
|
||||
* @details This is the specialization of "WChar -> Char"
|
||||
* @param[in] src The string to be converted.
|
||||
* @return The converted string, or error occurring.
|
||||
*/
|
||||
ConvResult<std::u8string> to_utf8(const std::wstring_view& src);
|
||||
|
||||
/**
|
||||
* @brief UTF8 -> WChar
|
||||
* @details This is the specialization of "Char -> WChar"
|
||||
* @param[in] src The string to be converted.
|
||||
* @return The converted string, or error occurring.
|
||||
*/
|
||||
ConvResult<std::wstring> to_wchar(const std::u8string_view& src);
|
||||
|
||||
/**
|
||||
* @brief Char -> UTF8
|
||||
* @details This is the specialization of "Char -> Char"
|
||||
* @param[in] src The string to be converted.
|
||||
* @param[in] code_page The code page of native string.
|
||||
* @return The converted string, or error occurring.
|
||||
*/
|
||||
ConvResult<std::u8string> to_utf8(const std::string_view& src, CodePage code_page);
|
||||
|
||||
/**
|
||||
* @brief UTF8 -> Char
|
||||
* @details This is the specialization of "Char -> Char"
|
||||
* @param[in] src The string to be converted.
|
||||
* @param[in] code_page The code page of native string.
|
||||
* @return The converted string, or error occurring.
|
||||
*/
|
||||
ConvResult<std::string> to_char(const std::u8string_view& src, CodePage code_page);
|
||||
|
||||
// YYC MARK:
|
||||
// UTF convertion only works on Microsoft STL.
|
||||
// See implementation for more details
|
||||
|
||||
#if defined(YYCC_STL_MSSTL)
|
||||
|
||||
/**
|
||||
* @brief UTF8 -> UTF16
|
||||
* @param[in] src The string to be converted.
|
||||
* @return The converted string, or error occurring.
|
||||
*/
|
||||
ConvResult<std::u16string> to_utf16(const std::u8string_view& src);
|
||||
|
||||
/**
|
||||
* @brief UTF16 -> UTF8
|
||||
* @param[in] src The string to be converted.
|
||||
* @return The converted string, or error occurring.
|
||||
*/
|
||||
ConvResult<std::u8string> to_utf8(const std::u16string_view& src);
|
||||
|
||||
/**
|
||||
* @brief UTF8 -> UTF32
|
||||
* @param[in] src The string to be converted.
|
||||
* @return The converted string, or error occurring.
|
||||
*/
|
||||
ConvResult<std::u32string> to_utf32(const std::u8string_view& src);
|
||||
|
||||
/**
|
||||
* @brief UTF32 -> UTF8
|
||||
* @param[in] src The string to be converted.
|
||||
* @return The converted string, or error occurring.
|
||||
*/
|
||||
ConvResult<std::u8string> to_utf8(const std::u32string_view& src);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace yycc::encoding::windows
|
||||
|
207
src/yycc/flag_enum.hpp
Normal file
207
src/yycc/flag_enum.hpp
Normal file
@ -0,0 +1,207 @@
|
||||
#pragma once
|
||||
#include <type_traits>
|
||||
|
||||
/**
|
||||
* @brief The namespace for convenient C++ enum class logic operations.
|
||||
* @details
|
||||
* C++ enum class statement is a modern way to declare enum in C++.
|
||||
* But it lack essential logic operations which is commonly used by programmer.
|
||||
* So we create this helper to resolve this issue.
|
||||
*/
|
||||
namespace yycc::flag_enum {
|
||||
|
||||
// Reference:
|
||||
// Enum operator overload: https://stackoverflow.com/a/71107019
|
||||
// Constexpr operator overload: https://stackoverflow.com/a/17746099
|
||||
|
||||
// YYC MARK:
|
||||
// Currently, the solution of "Constexpr operator overload" is not used.
|
||||
// We use explicit way, "Enum operator overload".
|
||||
|
||||
// template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
// inline constexpr TEnum operator|(TEnum lhs, TEnum rhs) {
|
||||
// using ut = std::underlying_type_t<TEnum>;
|
||||
// return static_cast<TEnum>(static_cast<ut>(lhs) | static_cast<ut>(rhs));
|
||||
// }
|
||||
// template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
// inline constexpr TEnum operator|=(TEnum& lhs, TEnum rhs) {
|
||||
// using ut = std::underlying_type_t<TEnum>;
|
||||
// lhs = lhs | rhs;
|
||||
// return lhs;
|
||||
// }
|
||||
|
||||
// template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
// inline constexpr TEnum operator&(TEnum lhs, TEnum rhs) {
|
||||
// using ut = std::underlying_type_t<TEnum>;
|
||||
// return static_cast<TEnum>(static_cast<ut>(lhs) & static_cast<ut>(rhs));
|
||||
// }
|
||||
// template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
// inline constexpr TEnum operator&=(TEnum& lhs, TEnum rhs) {
|
||||
// using ut = std::underlying_type_t<TEnum>;
|
||||
// lhs = lhs & rhs;
|
||||
// return lhs;
|
||||
// }
|
||||
|
||||
// template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
// inline constexpr TEnum operator^(TEnum lhs, TEnum rhs) {
|
||||
// using ut = std::underlying_type_t<TEnum>;
|
||||
// return static_cast<TEnum>(static_cast<ut>(lhs) ^ static_cast<ut>(rhs));
|
||||
// }
|
||||
// template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
// inline constexpr TEnum operator^=(TEnum& lhs, TEnum rhs) {
|
||||
// using ut = std::underlying_type_t<TEnum>;
|
||||
// lhs = lhs ^ rhs;
|
||||
// return lhs;
|
||||
// }
|
||||
|
||||
// template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
// inline constexpr TEnum operator~(TEnum lhs) {
|
||||
// using ut = std::underlying_type_t<TEnum>;
|
||||
// return static_cast<TEnum>(~(static_cast<ut>(lhs)));
|
||||
// }
|
||||
|
||||
// template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
// inline constexpr bool operator bool(TEnum lhs) {
|
||||
// using ut = std::underlying_type_t<TEnum>;
|
||||
// return static_cast<bool>(static_cast<ut>(lhs));
|
||||
// }
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @brief The helper struct to check all given template argument are the same enum type.
|
||||
* @tparam TEnum The template parameter to be checked (first one).
|
||||
* @tparam Ts The template parameter to be checked.
|
||||
*/
|
||||
template<typename TEnum, typename... Ts>
|
||||
struct AllSameEnum {
|
||||
public:
|
||||
// YYC MARK:
|
||||
// Please note that we must use std::is_same, not std::is_same_v!
|
||||
// That's std::conjunction_v required.
|
||||
static constexpr bool value = std::is_enum_v<std::remove_cv_t<TEnum>>
|
||||
&& std::conjunction_v<std::is_same<std::remove_cv_t<TEnum>, std::remove_cv_t<Ts>>...>;
|
||||
};
|
||||
/**
|
||||
* @private
|
||||
* @brief The convenient calling to all_enum_values::value to check enum template parameter.
|
||||
* @tparam TEnum The template parameter to be checked (first one).
|
||||
* @tparam Ts The template parameter to be checked.
|
||||
*/
|
||||
template<typename TEnum, typename... Ts>
|
||||
inline constexpr bool ALL_SAME_ENUM = AllSameEnum<TEnum, Ts...>::value;
|
||||
|
||||
/**
|
||||
* @brief Merge given enum flags like performing <TT>e1 | e2 | ... | en</TT>
|
||||
* @tparam TEnum Enum type for processing.
|
||||
* @param[in] val The first enum flag to be merged.
|
||||
* @param[in] val_left Left enum flags to be merged.
|
||||
* @return The merged enum flag.
|
||||
* @remarks
|
||||
* This function use recursive expansion to get final merge result.
|
||||
* So there is no difference of each arguments.
|
||||
* We independ first argument just served for expansion.
|
||||
*/
|
||||
template<typename TEnum, typename... Ts>
|
||||
requires(ALL_SAME_ENUM<TEnum, Ts...>)
|
||||
constexpr TEnum merge(TEnum val, Ts... val_left) {
|
||||
using ut = std::underlying_type_t<TEnum>;
|
||||
ut result = static_cast<ut>(val);
|
||||
if constexpr (sizeof...(val_left) > 0) {
|
||||
result |= static_cast<ut>(merge(val_left...));
|
||||
}
|
||||
return static_cast<TEnum>(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reverse given enum flags like performing <TT>~(e)</TT>
|
||||
* @tparam TEnum Enum type for processing.
|
||||
* @param[in] e The list of enum flags to be inversed.
|
||||
* @return The inversed enum flag.
|
||||
*/
|
||||
template<typename TEnum>
|
||||
requires(std::is_enum_v<TEnum>)
|
||||
constexpr TEnum invert(TEnum e) {
|
||||
using ut = std::underlying_type_t<TEnum>;
|
||||
return static_cast<TEnum>(~(static_cast<ut>(e)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Use specified enum flag to mask given enum flag like performing <TT>e1 &= e2</TT>
|
||||
* @tparam TEnum Enum type for processing.
|
||||
* @param[in,out] e1 The enum flags to be masked.
|
||||
* @param[in] e2 The mask enum flag.
|
||||
*/
|
||||
template<typename TEnum>
|
||||
requires(std::is_enum_v<TEnum>)
|
||||
constexpr void mask(TEnum& e1, TEnum e2) {
|
||||
using ut = std::underlying_type_t<TEnum>;
|
||||
e1 = static_cast<TEnum>(static_cast<ut>(e1) & static_cast<ut>(e2));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add multiple enum flags to given enum flag like performing <TT>e1 |= (e2 | e3 | ... | en)</TT>
|
||||
* @tparam TEnum Enum type for processing.
|
||||
* @param[in,out] e1 The enum flag which flags add on.
|
||||
* @param[in] vals The enum flag to be added.
|
||||
*/
|
||||
template<typename TEnum, typename... Ts>
|
||||
requires(ALL_SAME_ENUM<TEnum, Ts...>)
|
||||
constexpr void add(TEnum& e1, Ts... vals) {
|
||||
using ut = std::underlying_type_t<TEnum>;
|
||||
e1 = static_cast<TEnum>(static_cast<ut>(e1) | static_cast<ut>(merge(vals...)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Remove multiple enum flags from given enum flag like performing <TT>e1 &= ~(e2 | e3 | ... | en)</TT>
|
||||
* @tparam TEnum Enum type for processing.
|
||||
* @param[in,out] e1 The enum flag which flags removed from.
|
||||
* @param[in] vals The enum flag to be removed.
|
||||
*/
|
||||
template<typename TEnum, typename... Ts>
|
||||
requires(ALL_SAME_ENUM<TEnum, Ts...>)
|
||||
constexpr void remove(TEnum& e1, Ts... vals) {
|
||||
using ut = std::underlying_type_t<TEnum>;
|
||||
e1 = static_cast<TEnum>(static_cast<ut>(e1) & static_cast<ut>(invert(merge(vals...))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check whether given enum flag has any of specified multiple enum flags (OR) like performing <TT>bool(e1 & (e2 | e3 | ... | en))</TT>
|
||||
* @tparam TEnum Enum type for processing.
|
||||
* @param[in] e1 The enum flag where we check.
|
||||
* @param[in] vals The enum flags for checking.
|
||||
* @return True if it has any of given flags (OR), otherwise false.
|
||||
*/
|
||||
template<typename TEnum, typename... Ts>
|
||||
requires(ALL_SAME_ENUM<TEnum, Ts...>)
|
||||
constexpr bool has(TEnum e1, Ts... vals) {
|
||||
using ut = std::underlying_type_t<TEnum>;
|
||||
return static_cast<bool>(static_cast<ut>(e1) & static_cast<ut>(merge(vals...)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Cast given enum flags to its equvalent boolean value like performing <TT>bool(e)</TT>
|
||||
* @tparam TEnum Enum type for processing.
|
||||
* @param e The enum flags to be cast.
|
||||
* @return The equvalent bool value of given enum flag.
|
||||
*/
|
||||
template<typename TEnum, typename... Ts>
|
||||
requires(ALL_SAME_ENUM<TEnum, Ts...>)
|
||||
constexpr bool boolean(TEnum e) {
|
||||
using ut = std::underlying_type_t<TEnum>;
|
||||
return static_cast<bool>(static_cast<ut>(e));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Cast given enum flags to its equvalent underlying integer value like performing <TT>static_cast<std::underlying_type_t<T>>(e)</TT>
|
||||
* @tparam TEnum Enum type for processing.
|
||||
* @param e The enum flags to be cast.
|
||||
* @return The equvalent integer value of given enum flag.
|
||||
*/
|
||||
template<typename TEnum>
|
||||
requires(std::is_enum_v<TEnum>)
|
||||
constexpr std::underlying_type_t<TEnum> integer(TEnum e) {
|
||||
using ut = std::underlying_type_t<TEnum>;
|
||||
return static_cast<ut>(e);
|
||||
}
|
||||
|
||||
} // namespace yycc::flag_enum
|
@ -7,8 +7,8 @@
|
||||
|
||||
/// @brief Explicitly remove move (\c constructor and \c operator\=) for given class.
|
||||
#define YYCC_DELETE_MOVE(CLSNAME) \
|
||||
CLSNAME(CLSNAME&&) = delete; \
|
||||
CLSNAME& operator=(CLSNAME&&) = delete;
|
||||
CLSNAME(CLSNAME&&) noexcept = delete; \
|
||||
CLSNAME& operator=(CLSNAME&&) noexcept = delete;
|
||||
|
||||
/// @brief Explicitly remove (copy and move) (\c constructor and \c operator\=) for given class.
|
||||
#define YYCC_DELETE_COPY_MOVE(CLSNAME) \
|
||||
@ -22,10 +22,41 @@
|
||||
|
||||
/// @brief Explicitly set default move (\c constructor and \c operator\=) for given class.
|
||||
#define YYCC_DEFAULT_MOVE(CLSNAME) \
|
||||
CLSNAME(CLSNAME&&) = default; \
|
||||
CLSNAME& operator=(CLSNAME&&) = default;
|
||||
CLSNAME(CLSNAME&&) noexcept = default; \
|
||||
CLSNAME& operator=(CLSNAME&&) noexcept = default;
|
||||
|
||||
/// @brief Explicitly set default (copy and move) (\c constructor and \c operator\=) for given class.
|
||||
#define YYCC_DEFAULT_COPY_MOVE(CLSNAME) \
|
||||
YYCC_DEFAULT_COPY(CLSNAME) \
|
||||
YYCC_DEFAULT_MOVE(CLSNAME)
|
||||
|
||||
/// @brief Make declaration of copy (\c constructor and \c operator\=) for given class to avoid typo.
|
||||
#define YYCC_DECL_COPY(CLSNAME) \
|
||||
CLSNAME(const CLSNAME&); \
|
||||
CLSNAME& operator=(const CLSNAME&);
|
||||
|
||||
/// @brief Make declaration of move (\c constructor and \c operator\=) for given class to avoid typo.
|
||||
#define YYCC_DECL_MOVE(CLSNAME) \
|
||||
CLSNAME(CLSNAME&&) noexcept; \
|
||||
CLSNAME& operator=(CLSNAME&&) noexcept;
|
||||
|
||||
/// @brief Make declaration of copy and move (\c constructor and \c operator\=) for given class to avoid typo.
|
||||
#define YYCC_DECL_COPY_MOVE(CLSNAME) \
|
||||
YYCC_DECL_COPY(CLSNAME) \
|
||||
YYCC_DECL_MOVE(CLSNAME)
|
||||
|
||||
/// @brief Make implementation signature of copy \c constrctor for given class and right operand name to avoid typo.
|
||||
#define YYCC_IMPL_COPY_CTOR(CLSNAME, RHS) \
|
||||
CLSNAME::CLSNAME(const CLSNAME& RHS)
|
||||
|
||||
/// @brief Make implementation signature of copy \c operator\= for given class and right operand name to avoid typo.
|
||||
#define YYCC_IMPL_COPY_OPER(CLSNAME, RHS) \
|
||||
CLSNAME& CLSNAME::operator=(const CLSNAME& RHS)
|
||||
|
||||
/// @brief Make implementation signature of move \c constrctor for given class and right operand name to avoid typo.
|
||||
#define YYCC_IMPL_MOVE_CTOR(CLSNAME, RHS) \
|
||||
CLSNAME::CLSNAME(CLSNAME&& RHS) noexcept
|
||||
|
||||
/// @brief Make implementation signature of move \c operator\= for given class and right operand name to avoid typo.
|
||||
#define YYCC_IMPL_MOVE_OPER(CLSNAME, RHS) \
|
||||
CLSNAME& CLSNAME::operator=(CLSNAME&& RHS) noexcept
|
||||
|
30
src/yycc/macro/compiler_detector.hpp
Normal file
30
src/yycc/macro/compiler_detector.hpp
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#if (defined(YYCC_CC_MSVC) + defined(YYCC_CC_GCC) + defined(YYCC_CC_CLANG)) != 1
|
||||
#error "Current compiler is not supported!"
|
||||
#endif
|
||||
|
||||
namespace yycc::macro::compiler {
|
||||
|
||||
/// @brief The kind of compiler.
|
||||
enum class CompilerKind {
|
||||
Msvc, ///< MSVC
|
||||
Gcc, ///< GCC
|
||||
Clang, ///< Clang
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Fetch the compiler type.
|
||||
* @return The kind of compiler.
|
||||
*/
|
||||
inline constexpr CompilerKind get_compiler() {
|
||||
#if defined(YYCC_CC_MSVC)
|
||||
return CompilerKind::Msvc;
|
||||
#elif defined(YYCC_CC_GCC)
|
||||
return CompilerKind::Gcc;
|
||||
#else
|
||||
return CompilerKind::Clang;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace yycc::macro::compiler
|
28
src/yycc/macro/endian_detector.hpp
Normal file
28
src/yycc/macro/endian_detector.hpp
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
// Check endian
|
||||
#if (defined(YYCC_ENDIAN_LITTLE) + defined(YYCC_ENDIAN_BIG)) != 1
|
||||
#error "Current system endian (byte order) is not supported!"
|
||||
#endif
|
||||
|
||||
namespace yycc::macro::endian {
|
||||
|
||||
/// @brief The endian kind of OS.
|
||||
enum class EndianKind {
|
||||
Little, ///< Little endian.
|
||||
Big, ///< Big endian.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Fetch the endian of OS.
|
||||
* @return The endian of OS.
|
||||
*/
|
||||
inline constexpr EndianKind get_endian() {
|
||||
#if defined(YYCC_ENDIAN_LITTLE)
|
||||
return EndianKind::Little;
|
||||
#else
|
||||
return EndianKind::Big;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace yycc::macro::endian
|
@ -1,54 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Hint for C++ feature detection:
|
||||
// __cplusplus macro need special compiler switch enabled when compiling.
|
||||
// So we use _MSVC_LANG check it instead.
|
||||
|
||||
// ===== C++ Version =====
|
||||
|
||||
// Detect C++ 20
|
||||
#if __cplusplus >= 202002L || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)
|
||||
#define YYCC_CPPFEAT_GE_CPP20
|
||||
#endif
|
||||
|
||||
// Detect C++ 23
|
||||
#if __cplusplus >= 202302L || (defined(_MSVC_LANG) && _MSVC_LANG >= 202302L)
|
||||
#define YYCC_CPPFEAT_GE_CPP23
|
||||
#endif
|
||||
|
||||
// ===== C++ Features =====
|
||||
|
||||
// Check whether there is support of UTF8 string system.
|
||||
#if defined(__cpp_char8_t) || defined(YYCC_CPPFEAT_GE_CPP20)
|
||||
#define YYCC_CPPFEAT_UTF8
|
||||
#endif
|
||||
|
||||
// Check whether there is support of `contains` for `set` and `map` including their varients.
|
||||
#if defined(YYCC_CPPFEAT_GE_CPP20)
|
||||
#define YYCC_CPPFEAT_CONTAINS
|
||||
#endif
|
||||
|
||||
// Check whether there is support of `starts_with` and `ends_with` for `basic_string`.
|
||||
#if defined(__cpp_lib_starts_ends_with) || defined(YYCC_CPPFEAT_GE_CPP20)
|
||||
#define YYCC_CPPFEAT_STARTS_ENDS_WITH
|
||||
#endif
|
||||
|
||||
// Check whether there is support of `std::expected`.
|
||||
#if defined(__cpp_lib_expected) || defined(YYCC_CPPFEAT_GE_CPP23)
|
||||
#define YYCC_CPPFEAT_EXPECTED
|
||||
#endif
|
||||
|
||||
// Check whether there is support of `std::format`.
|
||||
#if defined(YYCC_CPPFEAT_GE_CPP20)
|
||||
#define YYCC_CPPFEAT_FORMAT
|
||||
#endif
|
||||
|
||||
// Check whether there is support of `__VA_OPT__`
|
||||
#if defined(YYCC_CPPFEAT_GE_CPP20)
|
||||
#define YYCC_CPPFEAT_VA_OPT
|
||||
#endif
|
||||
|
||||
// Check whether there is support of `std::stacktrace` and its formatter.
|
||||
#if (defined(__cpp_lib_starts_ends_with) && defined(__cpp_lib_formatters)) || defined(YYCC_CPPFEAT_GE_CPP23)
|
||||
#define YYCC_CPPFEAT_STACKTRACE
|
||||
#endif
|
@ -1,11 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
// Define operating system macros
|
||||
#define YYCC_OS_WINDOWS 2
|
||||
#define YYCC_OS_LINUX 3
|
||||
// Check current operating system.
|
||||
#if defined(_WIN32)
|
||||
#define YYCC_OS YYCC_OS_WINDOWS
|
||||
#else
|
||||
#define YYCC_OS YYCC_OS_LINUX
|
||||
// Check OS macro
|
||||
#if (defined(YYCC_OS_WINDOWS) + defined(YYCC_OS_LINUX) + defined(YYCC_OS_MACOS)) != 1
|
||||
#error "Current operating system is not supported!"
|
||||
#endif
|
||||
|
||||
namespace yycc::macro::os {
|
||||
|
||||
/// @brief The operating system kind.
|
||||
enum class OsKind {
|
||||
Windows, ///< Microsoft Windows
|
||||
Linux, ///< GNU/Linux
|
||||
MacOs, ///< Apple macOS
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Fetch the operating system
|
||||
* @return The kind of operating system.
|
||||
*/
|
||||
inline constexpr OsKind get_os() {
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
return OsKind::Windows;
|
||||
#elif defined(YYCC_OS_LINUX)
|
||||
return OsKind::Linux;
|
||||
#else
|
||||
return OsKind::MacOs;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace yycc::macro::os
|
||||
|
28
src/yycc/macro/ptr_size_detector.hpp
Normal file
28
src/yycc/macro/ptr_size_detector.hpp
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
// Check pointer size macro
|
||||
#if (defined(YYCC_PTRSIZE_32) + defined(YYCC_PTRSIZE_64)) != 1
|
||||
#error "Current environment used pointer size is not supported!"
|
||||
#endif
|
||||
|
||||
namespace yycc::macro::ptr_size {
|
||||
|
||||
/// @brief The pointer size kind.
|
||||
enum class PtrSizeKind {
|
||||
Bits32, ///< 32-bit environment
|
||||
Bits64 ///< 64-bit environment
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Fetch the pointer size
|
||||
* @return The kind of pointer size.
|
||||
*/
|
||||
inline constexpr PtrSizeKind get_ptr_size() {
|
||||
#if defined(YYCC_PTRSIZE_32)
|
||||
return PtrSizeKind::Bits32;
|
||||
#else
|
||||
return PtrSizeKind::Bits64;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace yycc::macro::ptr_size
|
39
src/yycc/macro/stl_detector.hpp
Normal file
39
src/yycc/macro/stl_detector.hpp
Normal file
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
// Include a common used STL header for convenient test.
|
||||
#include <cinttypes>
|
||||
|
||||
#if defined(_MSVC_STL_VERSION)
|
||||
#define YYCC_STL_MSSTL
|
||||
#elif defined(__GLIBCXX__) || defined(__GLIBCPP__)
|
||||
#define YYCC_STL_GNUSTL
|
||||
#elif defined(_LIBCPP_VERSION)
|
||||
#define YYCC_STL_CLANGSTL
|
||||
#else
|
||||
#error "Current STL is not supported!"
|
||||
#endif
|
||||
|
||||
namespace yycc::macro::stl {
|
||||
|
||||
/// @brief The STL implementation kind.
|
||||
enum class StlKind {
|
||||
MsStl, ///< Microsoft STL
|
||||
GnuStl, ///< GNU STL
|
||||
ClangStl ///< Clang STL
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Fetch the STL implementation
|
||||
* @return The kind of STL implementation.
|
||||
*/
|
||||
inline constexpr StlKind get_stl() {
|
||||
#if defined(YYCC_STL_MSSTL)
|
||||
return StlKind::MsStl;
|
||||
#elif defined(YYCC_STL_GNUSTL)
|
||||
return StlKind::GnuStl;
|
||||
#else
|
||||
return StlKind::ClangStl;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace yycc::macro::stl
|
38
src/yycc/num/op.hpp
Normal file
38
src/yycc/num/op.hpp
Normal file
@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include <stdexcept>
|
||||
#include <concepts>
|
||||
|
||||
/**
|
||||
* @brief The namespace providing functions for robust numeric operations.
|
||||
* @details
|
||||
* After writing some programs in Rust, I deeply appreciated the richness of operators
|
||||
* for primitive types in Rust, which provides convenient operations like ceiling integral division.
|
||||
* Therefore, I replicate these convenient features from Rust in this namespace.
|
||||
*
|
||||
* Currently unimplemented features due to lack of demand:
|
||||
* \li Only supports unsigned integer ceiling division
|
||||
*/
|
||||
namespace yycc::num::op {
|
||||
|
||||
/**
|
||||
* @brief Unsigned integer ceiling division
|
||||
* @details
|
||||
* Performs division between two unsigned integers and rounds up the result.
|
||||
* @exception std::logic_error If the divisor is zero
|
||||
* @tparam T The unsigned integer type for division operation
|
||||
* @param[in] lhs Left operand
|
||||
* @param[in] rhs Right operand
|
||||
* @return Ceiling division result
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::unsigned_integral<T>
|
||||
T div_ceil(T lhs, T rhs) {
|
||||
// Check divisor first
|
||||
if (rhs == 0) throw std::logic_error("div with 0");
|
||||
// YYC MARK:
|
||||
// We use this algorithm, instead of traditional `(lhs + rhs - 1) / rhs`,
|
||||
// which may have unsafe overflow case.
|
||||
return (lhs % rhs == 0) ? (lhs / rhs) : (lhs / rhs) + 1u;
|
||||
}
|
||||
|
||||
}
|
@ -1,13 +1,12 @@
|
||||
#pragma once
|
||||
#include "../string.hpp"
|
||||
#include "../string/op.hpp"
|
||||
#include "../string/reinterpret.hpp"
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <charconv>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
#include <expected>
|
||||
|
||||
#define NS_YYCC_STRING ::yycc::string
|
||||
#define NS_YYCC_STRING_REINTERPRET ::yycc::string::reinterpret
|
||||
#define NS_YYCC_STRING_OP ::yycc::string::op
|
||||
|
||||
@ -20,29 +19,27 @@
|
||||
*/
|
||||
namespace yycc::num::parse {
|
||||
|
||||
/// @private
|
||||
/// @brief The error kind when parsing string into number.
|
||||
enum class ParseError {
|
||||
PartiallyParsed, ///< Only a part of given string was parsed. The whole string may be invalid.
|
||||
InvalidString, ///< Given string is a invalid number string.
|
||||
OutOfRange, ///< Given string is valid but its value out of the range of given number type.
|
||||
OutOfRange, ///< Given string is valid but its value out of the range of given number type.
|
||||
};
|
||||
|
||||
/// @private
|
||||
/// @brief The return value of internal parse function which ape `std::expected`.
|
||||
template<typename T, std::enable_if_t<!std::is_same_v<T, ParseError>, int> = 0>
|
||||
using ParseResult = std::variant<T, ParseError>;
|
||||
template<typename T>
|
||||
using ParseResult = std::expected<T, ParseError>;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @brief Internal parsing function for floating point types
|
||||
* @brief Parse given string into floating point types
|
||||
* @tparam T Floating point type (float, double, etc)
|
||||
* @param strl The UTF-8 string view to parse
|
||||
* @param fmt The floating point format to use
|
||||
* @return ParseResult<T> containing either the parsed value or a ParseError
|
||||
*/
|
||||
template<typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
|
||||
ParseResult<T> priv_parse(const NS_YYCC_STRING::u8string_view& strl, std::chars_format fmt) {
|
||||
template<typename T>
|
||||
requires(std::is_floating_point_v<T>)
|
||||
ParseResult<T> parse(const std::u8string_view& strl, std::chars_format fmt = std::chars_format::general) {
|
||||
namespace reinterpret = NS_YYCC_STRING_REINTERPRET;
|
||||
|
||||
T rv;
|
||||
@ -54,13 +51,13 @@ namespace yycc::num::parse {
|
||||
// Parse completely.
|
||||
// But we need to check whether the whole string was parsed.
|
||||
if (ptr == tail) return rv;
|
||||
else return ParseError::PartiallyParsed;
|
||||
else return std::unexpected(ParseError::PartiallyParsed);
|
||||
} else if (ec == std::errc::invalid_argument) {
|
||||
// Given string is invalid
|
||||
return ParseError::InvalidString;
|
||||
return std::unexpected(ParseError::InvalidString);
|
||||
} else if (ec == std::errc::result_out_of_range) {
|
||||
// Given string is out of range
|
||||
return ParseError::OutOfRange;
|
||||
return std::unexpected(ParseError::OutOfRange);
|
||||
} else {
|
||||
// Unreachable
|
||||
throw std::runtime_error("invalid ec.");
|
||||
@ -68,15 +65,15 @@ namespace yycc::num::parse {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @brief Internal parsing function for integral types (except bool)
|
||||
* @brief Parse given string into integral types (except bool)
|
||||
* @tparam T Integral type (int, long, etc)
|
||||
* @param strl The UTF-8 string view to parse
|
||||
* @param base Numeric base (2-36)
|
||||
* @return ParseResult<T> containing either the parsed value or a ParseError
|
||||
*/
|
||||
template<typename T, std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool>, int> = 0>
|
||||
ParseResult<T> priv_parse(const NS_YYCC_STRING::u8string_view& strl, int base) {
|
||||
template<typename T>
|
||||
requires(std::is_integral_v<T> && !std::is_same_v<T, bool>)
|
||||
ParseResult<T> parse(const std::u8string_view& strl, int base = 10) {
|
||||
namespace reinterpret = NS_YYCC_STRING_REINTERPRET;
|
||||
|
||||
T rv;
|
||||
@ -88,13 +85,13 @@ namespace yycc::num::parse {
|
||||
// Parse completely.
|
||||
// But we need to check whether the whole string was parsed.
|
||||
if (ptr == tail) return rv;
|
||||
else return ParseError::PartiallyParsed;
|
||||
else return std::unexpected(ParseError::PartiallyParsed);
|
||||
} else if (ec == std::errc::invalid_argument) {
|
||||
// Given string is invalid
|
||||
return ParseError::InvalidString;
|
||||
return std::unexpected(ParseError::InvalidString);
|
||||
} else if (ec == std::errc::result_out_of_range) {
|
||||
// Given string is out of range
|
||||
return ParseError::OutOfRange;
|
||||
return std::unexpected(ParseError::OutOfRange);
|
||||
} else {
|
||||
// Unreachable
|
||||
throw std::runtime_error("invalid ec.");
|
||||
@ -102,147 +99,23 @@ namespace yycc::num::parse {
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @brief Internal parsing function for boolean type
|
||||
* @brief Parse given string into boolean type
|
||||
* @tparam T Must be bool type
|
||||
* @param strl The UTF-8 string view to parse ("true" or "false", case insensitive)
|
||||
* @return ParseResult<bool> containing either the parsed value or a ParseError
|
||||
*/
|
||||
template<typename T, std::enable_if_t<std::is_same_v<T, bool>, int> = 0>
|
||||
ParseResult<T> priv_parse(const NS_YYCC_STRING::u8string_view& strl) {
|
||||
template<typename T>
|
||||
requires(std::is_same_v<T, bool>)
|
||||
ParseResult<T> parse(const std::u8string_view& strl) {
|
||||
// Get lower case
|
||||
auto lower_case = NS_YYCC_STRING_OP::to_lower(strl);
|
||||
// Compare result
|
||||
if (lower_case == YYCC_U8("true")) return true;
|
||||
else if (lower_case == YYCC_U8("false")) return false;
|
||||
else return ParseError::InvalidString;
|
||||
if (lower_case == u8"true") return true;
|
||||
else if (lower_case == u8"false") return false;
|
||||
else return std::unexpected(ParseError::InvalidString);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Try parsing given string to floating point types.
|
||||
* @tparam T The type derived from floating point type.
|
||||
* @param[in] strl The string need to be parsed.
|
||||
* @param[out] num
|
||||
* The variable receiving result.
|
||||
* There is no guarantee that the content is not modified when parsing failed.
|
||||
* @param[in] fmt The floating point format used when try parsing.
|
||||
* @return True if success, otherwise false.
|
||||
*/
|
||||
template<typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
|
||||
bool try_parse(const NS_YYCC_STRING::u8string_view& strl,
|
||||
T& num,
|
||||
std::chars_format fmt = std::chars_format::general) {
|
||||
auto rv = priv_parse<T>(strl, fmt);
|
||||
if (const auto* ptr = std::get_if<T>(&rv)) {
|
||||
num = *ptr;
|
||||
return true;
|
||||
} else if (const auto* ptr = std::get_if<ParseError>(&rv)) {
|
||||
return false;
|
||||
} else {
|
||||
// Unreachable
|
||||
throw std::runtime_error("unreachable code.");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @brief Try parsing given string to integral types.
|
||||
* @tparam T The type derived from integral type except bool type.
|
||||
* @param[in] strl The string need to be parsed.
|
||||
* @param[out] num
|
||||
* The variable receiving result.
|
||||
* There is no guarantee that the content is not modified when parsing failed.
|
||||
* @param[in] base Integer base to use: a value between 2 and 36 (inclusive).
|
||||
* @return True if success, otherwise false.
|
||||
*/
|
||||
template<typename T, std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool>, int> = 0>
|
||||
bool try_parse(const NS_YYCC_STRING::u8string_view& strl, T& num, int base = 10) {
|
||||
auto rv = priv_parse<T>(strl, base);
|
||||
if (const auto* ptr = std::get_if<T>(&rv)) {
|
||||
num = *ptr;
|
||||
return true;
|
||||
} else if (const auto* ptr = std::get_if<ParseError>(&rv)) {
|
||||
return false;
|
||||
} else {
|
||||
// Unreachable
|
||||
throw std::runtime_error("unreachable code.");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @brief Try parsing given string to bool types.
|
||||
* @tparam T The type derived from bool type.
|
||||
* @param[in] strl The string need to be parsed ("true" or "false", case insensitive).
|
||||
* @param[out] num
|
||||
* The variable receiving result.
|
||||
* There is no guarantee that the content is not modified when parsing failed.
|
||||
* @return True if success, otherwise false.
|
||||
*/
|
||||
template<typename T, std::enable_if_t<std::is_same_v<T, bool>, int> = 0>
|
||||
bool try_parse(const NS_YYCC_STRING::u8string_view& strl, T& num) {
|
||||
auto rv = priv_parse<T>(strl);
|
||||
if (const auto* ptr = std::get_if<T>(&rv)) {
|
||||
num = *ptr;
|
||||
return true;
|
||||
} else if (const auto* ptr = std::get_if<ParseError>(&rv)) {
|
||||
return false;
|
||||
} else {
|
||||
// Unreachable
|
||||
throw std::runtime_error("unreachable code.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parse given string to floating point types.
|
||||
* @tparam T The type derived from floating point type.
|
||||
* @param[in] strl The string need to be parsed.
|
||||
* @param[in] fmt The floating point format used when try parsing.
|
||||
* @return
|
||||
* The parsing result.
|
||||
* There is no guarantee about the content of this return value when parsing failed.
|
||||
* It may be any possible value but usually is its default value.
|
||||
* @exception std::invalid_argument Can not parse given string.
|
||||
*/
|
||||
template<typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
|
||||
T parse(const NS_YYCC_STRING::u8string_view& strl,
|
||||
std::chars_format fmt = std::chars_format::general) {
|
||||
T rv;
|
||||
if (try_parse(strl, rv, fmt)) return rv;
|
||||
else throw std::invalid_argument("can not parse given string");
|
||||
}
|
||||
/**
|
||||
* @brief Parse given string to integral type types.
|
||||
* @tparam T The type derived from integral type except bool type.
|
||||
* @param[in] strl The string need to be parsed.
|
||||
* @param[in] base Integer base to use: a value between 2 and 36 (inclusive).
|
||||
* @return
|
||||
* The parsing result.
|
||||
* There is no guarantee about the content of this return value when parsing failed.
|
||||
* It may be any possible value but usually is its default value.
|
||||
* @exception std::invalid_argument Can not parse given string.
|
||||
*/
|
||||
template<typename T, std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool>, int> = 0>
|
||||
T parse(const NS_YYCC_STRING::u8string_view& strl, int base = 10) {
|
||||
T rv;
|
||||
if (try_parse(strl, rv, base)) return rv;
|
||||
else throw std::invalid_argument("can not parse given string");
|
||||
}
|
||||
/**
|
||||
* @brief Parse given string to bool types.
|
||||
* @tparam T The type derived from bool type.
|
||||
* @param[in] strl The string need to be parsed ("true" or "false", case insensitive).
|
||||
* @return
|
||||
* The parsing result.
|
||||
* There is no guarantee about the content of this return value when parsing failed.
|
||||
* It may be any possible value but usually is its default value.
|
||||
* @exception std::invalid_argument Can not parse given string.
|
||||
*/
|
||||
template<typename T, std::enable_if_t<std::is_same_v<T, bool>, int> = 0>
|
||||
T parse(const NS_YYCC_STRING::u8string_view& strl) {
|
||||
T rv;
|
||||
if (try_parse(strl, rv)) return rv;
|
||||
else throw std::invalid_argument("can not parse given string");
|
||||
}
|
||||
|
||||
} // namespace yycc::string::parse
|
||||
} // namespace yycc::num::parse
|
||||
|
||||
#undef NS_YYCC_STRING_OP
|
||||
#undef NS_YYCC_STRING_REINTERPRET
|
||||
#undef NS_YYCC_STRING
|
||||
|
165
src/yycc/num/safe_cast.hpp
Normal file
165
src/yycc/num/safe_cast.hpp
Normal file
@ -0,0 +1,165 @@
|
||||
#pragma once
|
||||
#include <expected>
|
||||
#include <concepts>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
/**
|
||||
* @brief The namespace providing functions which safely cast numeric value from one type to another.
|
||||
* @details
|
||||
* When writing Rust code, I deeply realized that casting between types with different ranges is very important,
|
||||
* but it is greatly easy to make mistake which finally cause fatal error.
|
||||
* For widening conversions, we can safely perform them directly without consideration.
|
||||
* For narrowing conversions, we need to introduce a Result mechanism to determine if errors occur.
|
||||
* These features are implemented for all primitive types in Rust,
|
||||
* and managed uniformly through the \c From and \c TryFrom trait, which is perfect.
|
||||
* But in C++, we need to manually replicate them.
|
||||
*
|
||||
* In this namespace, we divide conversion functions into two categories:
|
||||
* \li \c to for definitely safe conversions,
|
||||
* \li \c try_to for potentially risky conversions.
|
||||
* There is a metaprogramming concept <TT>CAN_SAFE_TO\<T, U\></TT> to determine if a conversion is safe,
|
||||
* which applied for these functions as the constraint.
|
||||
*
|
||||
* However, directly using \c CAN_SAFE_TO to determine whether the convertion is safe is incorrect.
|
||||
* In Rust, whether these conversions are safe is manually determined by different traits ( \c From and \c TryFrom).
|
||||
* But in C++, we brutally use the size of data types in compile-time to determine safe conversion,
|
||||
* which causes variable-length data types produces different \c CAN_SAFE_TO results on different platforms,
|
||||
* which affecting our code's portability.
|
||||
* For example, we can use 'to' directly for conversion between \c uint64_t and \c size_t: on 64-bit platforms,
|
||||
* but on 32-bit platforms it causes compile-time errors, resulting in portability issues.
|
||||
*
|
||||
* Rust's solution is to define the minimum size of \c usize (32-bit),
|
||||
* allowing safe conversion only for data smaller than this (e.g. \c u16 ).
|
||||
* But in C++ we can't do this because we can't know the minimum size of every variable-length primitive data type.
|
||||
* So we use another solution.
|
||||
*
|
||||
* Our solution is to enforce \c CAN_SAFE_TO rules for \c to functions but not for \c try_to functions.
|
||||
* Inside \c try_to, we use \c CAN_SAFE_TO to determine if safe conversion is possible.
|
||||
* If yes, convert directly, otherwise perform essential checks before convertion.
|
||||
* So under this solution, programmers need manually determine if conversion between two types is definitely safe before using \c to .
|
||||
* But at least, the compiler will throw errors when \c to is inviable, and the only thing you should do is switching to \c try_to.
|
||||
* Also, using \c try_to everywhere won't impact performance as unnecessary checks are removed via <TT>if constexpr</TT> statement in function.
|
||||
*
|
||||
* Currently unsupported conversions due to lack of demand:
|
||||
* \li Floating-point to floating-point conversions
|
||||
* \li Floating-point to integer conversions
|
||||
*/
|
||||
namespace yycc::num::safe_cast {
|
||||
|
||||
/// @brief All possible error raised in this module.
|
||||
enum class CastError {
|
||||
Overflow, ///< Overflow error occurred during conversion.
|
||||
Underflow, ///< Underflow error occurred during conversion.
|
||||
};
|
||||
|
||||
/// @brief The result type in this module.
|
||||
template<typename T>
|
||||
using Result = std::expected<T, CastError>;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @brief Check if an integer type can be safely converted to another integer type
|
||||
* @return True if it can be safely converted, false otherwise.
|
||||
*/
|
||||
template<typename TDst, typename TSrc>
|
||||
requires std::integral<TDst> && std::integral<TSrc>
|
||||
constexpr bool can_safe_to() {
|
||||
// Fetch the sign info of TSrc and TDst.
|
||||
constexpr bool is_src_signed = std::is_signed_v<TSrc>;
|
||||
constexpr bool is_dst_signed = std::is_signed_v<TDst>;
|
||||
|
||||
// Get the range of TSrc and TDst.
|
||||
constexpr TSrc src_min = std::numeric_limits<TSrc>::min();
|
||||
constexpr TSrc src_max = std::numeric_limits<TSrc>::max();
|
||||
constexpr TDst dst_min = std::numeric_limits<TDst>::min();
|
||||
constexpr TDst dst_max = std::numeric_limits<TDst>::max();
|
||||
|
||||
if constexpr (is_src_signed) {
|
||||
if constexpr (is_dst_signed) {
|
||||
// Signed to signed conversion, both upper and lower bound need to be checked.
|
||||
// If completely within the range, it is definitely safe.
|
||||
return dst_min <= src_min && dst_max >= src_max;
|
||||
} else {
|
||||
// Signed to unsigned conversion, always unsafe.
|
||||
// Because negative numbers exist.
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if constexpr (is_dst_signed) {
|
||||
// Unsigned to signed conversion, only check the upper bound.
|
||||
// If the upper bound is small enough, it is definitely safe.
|
||||
return dst_max >= src_max;
|
||||
} else {
|
||||
// Unsigned to unsigned conversion, only check the upper bound,
|
||||
// because the lower bound is 0.
|
||||
return dst_max >= src_max;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @brief Variable version of can_safe_to()
|
||||
* @details Convenience variable for subsequent constraints
|
||||
*/
|
||||
template<typename TDst, typename TSrc>
|
||||
requires std::integral<TDst> && std::integral<TSrc>
|
||||
inline constexpr bool CAN_SAFE_TO = can_safe_to<TDst, TSrc>();
|
||||
|
||||
/**
|
||||
* @brief Convert an integer type to another integer type.
|
||||
* @details Similar to Rust's \c From trait, but with reversed direction (from "from" to "to").
|
||||
* @return The converted result.
|
||||
*/
|
||||
template<typename TDst, typename TSrc>
|
||||
requires std::integral<TDst> && std::integral<TSrc> && CAN_SAFE_TO<TDst, TSrc>
|
||||
TDst to(const TSrc& lhs) {
|
||||
return static_cast<TDst>(lhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Attempt to convert an integer type to another integer type.
|
||||
* @details Similar to Rust's \c TryFrom trait, but with reversed direction (from "from" to "to").
|
||||
* @return A result containing the conversion result or convertion error.
|
||||
*/
|
||||
template<typename TDst, typename TSrc>
|
||||
requires std::integral<TDst> && std::integral<TSrc>
|
||||
Result<TDst> try_to(const TSrc& lhs) {
|
||||
// Check whether we can convert directly.
|
||||
if constexpr (CAN_SAFE_TO<TDst, TSrc>) {
|
||||
return static_cast<TDst>(lhs);
|
||||
} else {
|
||||
// Fetch the sign info of TSrc and TDst.
|
||||
constexpr bool is_src_signed = std::is_signed_v<TSrc>;
|
||||
constexpr bool is_dst_signed = std::is_signed_v<TDst>;
|
||||
|
||||
// Fetch the range of TSrc and TDst.
|
||||
constexpr TSrc src_min = std::numeric_limits<TSrc>::min();
|
||||
constexpr TSrc src_max = std::numeric_limits<TSrc>::max();
|
||||
constexpr TDst dst_min = std::numeric_limits<TDst>::min();
|
||||
constexpr TDst dst_max = std::numeric_limits<TDst>::max();
|
||||
|
||||
// Check whether we can convert safely.
|
||||
if constexpr (is_src_signed == is_dst_signed) {
|
||||
// If both are signed or unsigned, compare ranges directly.
|
||||
if (lhs < dst_min) return std::unexpected(CastError::Underflow);
|
||||
if (lhs > dst_max) return std::unexpected(CastError::Overflow);
|
||||
return static_cast<TDst>(lhs);
|
||||
} else {
|
||||
// If signs are different, we need to check the value range.
|
||||
if constexpr (is_src_signed) {
|
||||
// If TSrc is signed, TDst is unsigned, we need to ensure lhs is not negative.
|
||||
if (lhs < 0) return std::unexpected(CastError::Underflow);
|
||||
if (lhs > dst_max) return std::unexpected(CastError::Overflow);
|
||||
return static_cast<TDst>(lhs);
|
||||
} else {
|
||||
// If TSrc is unsigned, TDst is signed, we need to ensure lhs is not greater than dst_max.
|
||||
if (lhs > dst_max) return std::unexpected(CastError::Overflow);
|
||||
return static_cast<TDst>(lhs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace yycc::num::safe_cast
|
774
src/yycc/num/safe_op.hpp
Normal file
774
src/yycc/num/safe_op.hpp
Normal file
@ -0,0 +1,774 @@
|
||||
#pragma once
|
||||
#include "../macro/os_detector.hpp"
|
||||
#include "../macro/compiler_detector.hpp"
|
||||
#include <stdexcept>
|
||||
#include <optional>
|
||||
#include <concepts>
|
||||
#include <limits>
|
||||
|
||||
// Choose the function family for hardware based overflow.
|
||||
#if defined(YYCC_CC_GCC) || defined(YYCC_CC_CLANG)
|
||||
#define YYCC_HARDWARE_OVERFLOW_GCC_FNS
|
||||
#elif defined(YYCC_OS_WINDOWS)
|
||||
#define YYCC_HARDWARE_OVERFLOW_WIN32_FNS
|
||||
#else
|
||||
#error "Not supported platform or compiler for integral overflow function family."
|
||||
#endif
|
||||
|
||||
// Import essential header if we are using Windows function family.
|
||||
#if defined(YYCC_HARDWARE_OVERFLOW_WIN32_FNS)
|
||||
// YYC MARK:
|
||||
// This macro is crucial for including "intsafe.h"
|
||||
// Without this, "intsafe.h" will not enable signed integral operations.
|
||||
#define ENABLE_INTSAFE_SIGNED_FUNCTIONS
|
||||
|
||||
#include "../windows/import_guard_head.hpp"
|
||||
#include <intsafe.h>
|
||||
#include "../windows/import_guard_tail.hpp"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief The namespace providing Rust-like safe arithmetic operations.
|
||||
* @details
|
||||
* After writing some programs in Rust, I've deeply realized the richness of operators for primitive types in Rust.
|
||||
* You can explicitly specify the behavior of arithmetic overflow
|
||||
* (choose one of wrapping, checked, overflowing, and saturating).
|
||||
* Therefore, I'm replicating these convenient features from Rust in this namespace.
|
||||
*
|
||||
* Additionally, I provide a bunch of extra operations, called ordinary operation.
|
||||
* These functions are just an alias to wrapping operator, indicating wrapping is the normal case.
|
||||
* These normal operators will not have any undefined behavior which C++ will make.
|
||||
* It basically like \c Add, \c Sub, \c Mul, and \c Div traits in Rust,
|
||||
* providing safe operations for primitive types.
|
||||
*/
|
||||
namespace yycc::num::safe_op {
|
||||
|
||||
/*
|
||||
Implementation notes:
|
||||
|
||||
- Wrapping operation:
|
||||
- Unsigned integer use default overflow behavior.
|
||||
- Signed integer will be casted into unsigned integer before operation to simulate wrapping overflow.
|
||||
- Checked operation:
|
||||
- Use compiler built-in function to detect overflow, return std::optional<T>.
|
||||
- Return std::nullopt when division by zero or overflow.
|
||||
- Overflowing operation:
|
||||
- Return std::pair<T, bool> where bool indicates whether overflow occurs.
|
||||
- Explicitly handle 2 division undefined behavior:
|
||||
- Division by zero.
|
||||
- Signed minimum value divided by -1.
|
||||
- Saturating operation:
|
||||
- Return maximum or minimum value when overflow or underflow occurs.
|
||||
- Determine saturation direction (overflow or underflow) based on operand sign.
|
||||
*/
|
||||
|
||||
#pragma region Undefined Behaviors
|
||||
|
||||
/*
|
||||
YYC MARK:
|
||||
Following undefined behaviors should be noticed:
|
||||
- Signed integer overflow and underflow (e.g. INT_MAX + 1).
|
||||
- Perform `INT_MIN / -1` division.
|
||||
- The right operand in division is zero.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @brief Adds two numbers while considering the undefined behavior of signed integer overflow.
|
||||
* @tparam T Integer type
|
||||
* @param[in] a The left operand of the addition
|
||||
* @param[in] b The right operand of the addition
|
||||
* @return The result of the addition that takes into account the undefined behavior of signed integers.
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
T ub_signed_int_add(T a, T b) {
|
||||
if constexpr (std::is_unsigned_v<T>) {
|
||||
// Add, Sub, Mul and Div for unsigned integer is natural wrapping.
|
||||
// So we can use operator simply and directly.
|
||||
return a + b;
|
||||
} else {
|
||||
// In C++, it is undefined behavior that signed integer overflow.
|
||||
// So we need cast them into unsigned integer forcely before operation,
|
||||
// do operation for them, and cast the result back to simulate wrapping overflow.
|
||||
using UT = std::make_unsigned_t<T>;
|
||||
return static_cast<T>(static_cast<UT>(a) + static_cast<UT>(b));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @brief Subtracts two numbers while considering the undefined behavior of signed integer overflow.
|
||||
* @tparam T Integer type
|
||||
* @param[in] a The left operand of the subtraction
|
||||
* @param[in] b The right operand of the subtraction
|
||||
* @return The result of the subtraction that takes into account the undefined behavior of signed integers.
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
T ub_signed_int_sub(T a, T b) {
|
||||
if constexpr (std::is_unsigned_v<T>) {
|
||||
return a - b;
|
||||
} else {
|
||||
using UT = std::make_unsigned_t<T>;
|
||||
return static_cast<T>(static_cast<UT>(a) - static_cast<UT>(b));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @brief Multiplies two numbers while considering the undefined behavior of signed integer overflow.
|
||||
* @tparam T Integer type
|
||||
* @param[in] a The left operand of the multiplication
|
||||
* @param[in] b The right operand of the multiplication
|
||||
* @return The result of the multiplication that takes into account the undefined behavior of signed integers.
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
T ub_signed_int_mul(T a, T b) {
|
||||
if constexpr (std::is_unsigned_v<T>) {
|
||||
return a * b;
|
||||
} else {
|
||||
using UT = std::make_unsigned_t<T>;
|
||||
return static_cast<T>(static_cast<UT>(a) * static_cast<UT>(b));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @brief Checks for undefined behavior when dividing the minimum signed integer value by -1.
|
||||
* @tparam T Integer type
|
||||
* @param[in] a The left operand of the division
|
||||
* @param[in] b The right operand of the division
|
||||
* @return Returns true if undefined behavior will occur, false otherwise.
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
bool ub_signed_int_min_div_minus_one(T a, T b) {
|
||||
if constexpr (std::is_signed_v<T>) {
|
||||
// For signed value, `INT_MIN / -1` may cause overflow,
|
||||
// which finally cause the undefined behavior,
|
||||
// due to the truth that `INT_MIN == -INT_MIN - 1`.
|
||||
if (b == -1 && a == std::numeric_limits<T>::min()) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @brief Checks for the undefined behavior of division by zero.
|
||||
* @tparam T Integer type
|
||||
* @param[in] a The left operand of the division
|
||||
* @param[in] b The right operand of the division
|
||||
* @return Returns true if undefined behavior will occur, false otherwise.
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
bool ub_div_zero([[maybe_unused]] T a, T b) {
|
||||
return b == 0;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Hardware Operation Overflow
|
||||
|
||||
// YYC MARK:
|
||||
// If we are using Windows function family,
|
||||
// we define a convenient macro assisting overflow calculation.
|
||||
#if defined(YYCC_HARDWARE_OVERFLOW_WIN32_FNS)
|
||||
#define WIN_EASY_OPER(fn, ty, a, b, c) FAILED(fn(static_cast<ty>(a), static_cast<ty>(b), reinterpret_cast<ty*>(c)))
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @brief Addition with overflow detection based on hardware instructions.
|
||||
* @param[in] a The left operand of the addition.
|
||||
* @param[in] b The right operand of the addition.
|
||||
* @param[out] c The pointer to the variable storing result.
|
||||
* @return Returns true if an overflow occurs, false otherwise.
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
bool hardware_add_overflow(T a, T b, T* c) {
|
||||
if (c == nullptr) [[unlikely]]
|
||||
throw std::logic_error("invalid nullptr");
|
||||
#if defined(YYCC_HARDWARE_OVERFLOW_GCC_FNS)
|
||||
return __builtin_add_overflow(a, b, c);
|
||||
#else
|
||||
bool overflow = false;
|
||||
constexpr size_t T_SIZE = sizeof(T);
|
||||
if constexpr (std::is_signed_v<T>) {
|
||||
if constexpr (T_SIZE == 8) {
|
||||
overflow = WIN_EASY_OPER(LongLongAdd, LONGLONG, a, b, c);
|
||||
} else if constexpr (T_SIZE == 4) {
|
||||
overflow = WIN_EASY_OPER(LongAdd, LONG, a, b, c);
|
||||
} else if constexpr (T_SIZE == 2) {
|
||||
overflow = WIN_EASY_OPER(ShortAdd, SHORT, a, b, c);
|
||||
} else if constexpr (T_SIZE == 1) {
|
||||
overflow = WIN_EASY_OPER(Int8Add, INT8, a, b, c);
|
||||
} else {
|
||||
static_assert(std::false_type::value, "not supported integral type.");
|
||||
}
|
||||
} else {
|
||||
if constexpr (T_SIZE == 8) {
|
||||
overflow = WIN_EASY_OPER(ULongLongAdd, ULONGLONG, a, b, c);
|
||||
} else if constexpr (T_SIZE == 4) {
|
||||
overflow = WIN_EASY_OPER(ULongAdd, ULONG, a, b, c);
|
||||
} else if constexpr (T_SIZE == 2) {
|
||||
overflow = WIN_EASY_OPER(UShortAdd, USHORT, a, b, c);
|
||||
} else if constexpr (T_SIZE == 1) {
|
||||
overflow = WIN_EASY_OPER(UInt8Add, UINT8, a, b, c);
|
||||
} else {
|
||||
static_assert(std::false_type::value, "not supported integral type.");
|
||||
}
|
||||
}
|
||||
// Due to the limitation of Windows function family,
|
||||
// if overflow or underflow occurs, there is no calculation result.
|
||||
// So we need fill the wrapping value manually.
|
||||
if (overflow) *c = ub_signed_int_add<T>(a, b);
|
||||
return overflow;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @brief Subtraction with overflow detection based on hardware instructions.
|
||||
* @param[in] a The left operand of the subtraction.
|
||||
* @param[in] b The right operand of the subtraction.
|
||||
* @param[out] c The pointer to the variable storing the result.
|
||||
* @return Returns true if an overflow occurs, false otherwise.
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
bool hardware_sub_overflow(T a, T b, T* c) {
|
||||
if (c == nullptr) [[unlikely]]
|
||||
throw std::logic_error("invalid nullptr");
|
||||
#if defined(YYCC_HARDWARE_OVERFLOW_GCC_FNS)
|
||||
return __builtin_sub_overflow(a, b, c);
|
||||
#else
|
||||
bool overflow = false;
|
||||
constexpr size_t T_SIZE = sizeof(T);
|
||||
if constexpr (std::is_signed_v<T>) {
|
||||
if constexpr (T_SIZE == 8) {
|
||||
overflow = WIN_EASY_OPER(LongLongSub, LONGLONG, a, b, c);
|
||||
} else if constexpr (T_SIZE == 4) {
|
||||
overflow = WIN_EASY_OPER(LongSub, LONG, a, b, c);
|
||||
} else if constexpr (T_SIZE == 2) {
|
||||
overflow = WIN_EASY_OPER(ShortSub, SHORT, a, b, c);
|
||||
} else if constexpr (T_SIZE == 1) {
|
||||
overflow = WIN_EASY_OPER(Int8Sub, INT8, a, b, c);
|
||||
} else {
|
||||
static_assert(std::false_type::value, "not supported integral type.");
|
||||
}
|
||||
} else {
|
||||
if constexpr (T_SIZE == 8) {
|
||||
overflow = WIN_EASY_OPER(ULongLongSub, ULONGLONG, a, b, c);
|
||||
} else if constexpr (T_SIZE == 4) {
|
||||
overflow = WIN_EASY_OPER(ULongSub, ULONG, a, b, c);
|
||||
} else if constexpr (T_SIZE == 2) {
|
||||
overflow = WIN_EASY_OPER(UShortSub, USHORT, a, b, c);
|
||||
} else if constexpr (T_SIZE == 1) {
|
||||
overflow = WIN_EASY_OPER(UInt8Sub, UINT8, a, b, c);
|
||||
} else {
|
||||
static_assert(std::false_type::value, "not supported integral type.");
|
||||
}
|
||||
}
|
||||
// Similarly, manually calculate wrapping value.
|
||||
if (overflow) *c = ub_signed_int_sub<T>(a, b);
|
||||
return overflow;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @brief Multiplication with overflow detection based on hardware instructions.
|
||||
* @param[in] a The left operand of the multiplication.
|
||||
* @param[in] b The right operand of the multiplication.
|
||||
* @param[out] c The reference to the variable storing the result.
|
||||
* @return Returns true if an overflow occurs, false otherwise.
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
bool hardware_mul_overflow(T a, T b, T* c) {
|
||||
if (c == nullptr) [[unlikely]]
|
||||
throw std::logic_error("invalid nullptr");
|
||||
#if defined(YYCC_HARDWARE_OVERFLOW_GCC_FNS)
|
||||
return __builtin_mul_overflow(a, b, c);
|
||||
#else
|
||||
bool overflow = false;
|
||||
constexpr size_t T_SIZE = sizeof(T);
|
||||
if constexpr (std::is_signed_v<T>) {
|
||||
if constexpr (T_SIZE == 8) {
|
||||
overflow = WIN_EASY_OPER(LongLongMult, LONGLONG, a, b, c);
|
||||
} else if constexpr (T_SIZE == 4) {
|
||||
overflow = WIN_EASY_OPER(LongMult, LONG, a, b, c);
|
||||
} else if constexpr (T_SIZE == 2) {
|
||||
overflow = WIN_EASY_OPER(ShortMult, SHORT, a, b, c);
|
||||
} else if constexpr (T_SIZE == 1) {
|
||||
overflow = WIN_EASY_OPER(Int8Mult, INT8, a, b, c);
|
||||
} else {
|
||||
static_assert(std::false_type::value, "not supported integral type.");
|
||||
}
|
||||
} else {
|
||||
if constexpr (T_SIZE == 8) {
|
||||
overflow = WIN_EASY_OPER(ULongLongMult, ULONGLONG, a, b, c);
|
||||
} else if constexpr (T_SIZE == 4) {
|
||||
overflow = WIN_EASY_OPER(ULongMult, ULONG, a, b, c);
|
||||
} else if constexpr (T_SIZE == 2) {
|
||||
overflow = WIN_EASY_OPER(UShortMult, USHORT, a, b, c);
|
||||
} else if constexpr (T_SIZE == 1) {
|
||||
overflow = WIN_EASY_OPER(UInt8Mult, UINT8, a, b, c);
|
||||
} else {
|
||||
static_assert(std::false_type::value, "not supported integral type.");
|
||||
}
|
||||
}
|
||||
// Similarly, manually calculate wrapping value.
|
||||
if (overflow) *c = ub_signed_int_mul<T>(a, b);
|
||||
return overflow;
|
||||
#endif
|
||||
}
|
||||
|
||||
// YYC MARK:
|
||||
// Delete the defined macro to prevent polluting the content later.
|
||||
#if defined(YYCC_HARDWARE_OVERFLOW_WIN32_FNS)
|
||||
#undef WIN_EASY_OPER
|
||||
#endif
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Wrapping operations
|
||||
|
||||
// YYC MARK:
|
||||
// wrapping_* function family will wrap the result in any scenario.
|
||||
|
||||
/**
|
||||
* @brief Performs a wrapping addition operation on two integers.
|
||||
* @tparam T Integer type.
|
||||
* @param[in] a The left operand of the addition.
|
||||
* @param[in] b The right operand of the addition.
|
||||
* @return The wrapping result of the addition operation.
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
T wrapping_add(T a, T b) {
|
||||
return ub_signed_int_add(a, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Performs a wrapping subtraction operation on two integers.
|
||||
* @tparam T Integer type.
|
||||
* @param[in] a The left operand of the subtraction.
|
||||
* @param[in] b The right operand of the subtraction.
|
||||
* @return The wrapping result of the subtraction operation.
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
T wrapping_sub(T a, T b) {
|
||||
return ub_signed_int_sub(a, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Performs a wrapping multiplication operation on two integers.
|
||||
* @tparam T Integer type.
|
||||
* @param[in] a The left operand of the multiplication.
|
||||
* @param[in] b The right operand of the multiplication.
|
||||
* @return The wrapping result of the multiplication operation.
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
T wrapping_mul(T a, T b) {
|
||||
return ub_signed_int_mul(a, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Performs a wrapping division operation on two integers.
|
||||
* @tparam T Integer type.
|
||||
* @param[in] a The left operand of the division.
|
||||
* @param[in] b The right operand of the division.
|
||||
* @return The wrapping result of the division operation.
|
||||
* @exception std::logic_error If division by zero occurs.
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
T wrapping_div(T a, T b) {
|
||||
// Division by zero is undefined behavior.
|
||||
if (ub_div_zero(a, b)) throw std::logic_error("div with 0");
|
||||
// `INT_MIN / -1` overflow undefined behavior.
|
||||
if (ub_signed_int_min_div_minus_one(a, b))
|
||||
// "a" self is the minimum value of signed integer, return it directly.
|
||||
// There is no need to re-fetch it by std::numeric_limits.
|
||||
return a;
|
||||
return a / b;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Checked operations
|
||||
|
||||
// YYC MARK:
|
||||
// If overflow occurs when using checked_* function family,
|
||||
// these functions will return std::nullopt, otherwise the computed result.
|
||||
|
||||
/**
|
||||
* @brief Performs a checked addition operation on two integers.
|
||||
* @tparam T Integer type.
|
||||
* @param[in] a The left operand of the addition.
|
||||
* @param[in] b The right operand of the addition.
|
||||
* @return An std::optional containing the result if no overflow occurs, otherwise std::nullopt.
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
std::optional<T> checked_add(T a, T b) {
|
||||
T result;
|
||||
if (hardware_add_overflow(a, b, &result)) return std::nullopt;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Performs a checked subtraction operation on two integers.
|
||||
* @tparam T Integer type.
|
||||
* @param[in] a The left operand of the subtraction.
|
||||
* @param[in] b The right operand of the subtraction.
|
||||
* @return An std::optional containing the result if no overflow occurs, otherwise std::nullopt.
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
std::optional<T> checked_sub(T a, T b) {
|
||||
T result;
|
||||
if (hardware_sub_overflow(a, b, &result)) return std::nullopt;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Performs a checked multiplication operation on two integers.
|
||||
* @tparam T Integer type.
|
||||
* @param[in] a The left operand of the multiplication.
|
||||
* @param[in] b The right operand of the multiplication.
|
||||
* @return An std::optional containing the result if no overflow occurs, otherwise std::nullopt.
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
std::optional<T> checked_mul(T a, T b) {
|
||||
T result;
|
||||
if (hardware_mul_overflow(a, b, &result)) return std::nullopt;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Performs a checked division operation on two integers.
|
||||
* @tparam T Integer type.
|
||||
* @param[in] a The left operand of the division.
|
||||
* @param[in] b The right operand of the division.
|
||||
* @return
|
||||
* An std::optional containing the result,
|
||||
* if no undefined behavior (division by zero or overflow) occurs,
|
||||
* otherwise std::nullopt.
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
std::optional<T> checked_div(T a, T b) {
|
||||
// Division by zero is undefined behavior.
|
||||
if (ub_div_zero(a, b)) return std::nullopt;
|
||||
// `INT_MIN / -1` overflow undefined behavior.
|
||||
if (ub_signed_int_min_div_minus_one(a, b)) return std::nullopt;
|
||||
return a / b;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Overflowing operations
|
||||
|
||||
// YYC MARK:
|
||||
// overflowing_* function family return a tuple with 2 items as the result.
|
||||
// First is the wrapping value and second is a boolean indicates whether overflow occurs.
|
||||
|
||||
/**
|
||||
* @brief The result returned by the overflow function family.
|
||||
* @details
|
||||
* The first item is the operation result.
|
||||
* The second item indicating whether an overflow has occurred. True means overflow, otherwise false.
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
using OverflowingPair = std::pair<T, bool>;
|
||||
|
||||
/**
|
||||
* @brief Performs an addition operation with overflow detection.
|
||||
* @tparam T Integer type.
|
||||
* @param[in] a The left operand of the addition.
|
||||
* @param[in] b The right operand of the addition.
|
||||
* @return A pair holding result and overflow flag.
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
OverflowingPair<T> overflowing_add(T a, T b) {
|
||||
T result;
|
||||
bool overflow = hardware_add_overflow(a, b, &result);
|
||||
return std::make_pair(result, overflow);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Performs a subtraction operation with overflow detection.
|
||||
* @tparam T Integer type.
|
||||
* @param[in] a The left operand of the subtraction.
|
||||
* @param[in] b The right operand of the subtraction.
|
||||
* @return A pair holding result and overflow flag.
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
OverflowingPair<T> overflowing_sub(T a, T b) {
|
||||
T result;
|
||||
bool overflow = hardware_sub_overflow(a, b, &result);
|
||||
return std::make_pair(result, overflow);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Performs a multiplication operation with overflow detection.
|
||||
* @tparam T Integer type.
|
||||
* @param[in] a The left operand of the multiplication.
|
||||
* @param[in] b The right operand of the multiplication.
|
||||
* @return A pair holding result and overflow flag.
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
OverflowingPair<T> overflowing_mul(T a, T b) {
|
||||
T result;
|
||||
bool overflow = hardware_mul_overflow(a, b, &result);
|
||||
return std::make_pair(result, overflow);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Performs a division operation with overflow detection.
|
||||
* @tparam T Integer type.
|
||||
* @param[in] a The left operand of the division.
|
||||
* @param[in] b The right operand of the division.
|
||||
* @return A pair holding result and overflow flag.
|
||||
* @exception std::logic_error If division by zero occurs.
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
OverflowingPair<T> overflowing_div(T a, T b) {
|
||||
// Division by zero is undefined behavior.
|
||||
if (ub_div_zero(a, b)) throw std::logic_error("div with 0");
|
||||
// `INT_MIN / -1` overflow undefined behavior.
|
||||
if (ub_signed_int_min_div_minus_one(a, b)) {
|
||||
// "a" self is minimum value, no need to get it again.
|
||||
return std::make_pair(a, true);
|
||||
} else {
|
||||
return std::make_pair(a / b, false);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Saturating operations
|
||||
|
||||
/*
|
||||
YYC MARK:
|
||||
The direction of saturation is pretty simple for unsigned integer.
|
||||
However, it is slightly complex for signed integer.
|
||||
In detail, it follow following rules:
|
||||
|
||||
Acknowledge the truth for signed integer: abs(MIN) = abs(MAX) + 1
|
||||
- ADD operation:
|
||||
- Range operation rule: [a, b] + [c, d] = [a+c, b+d]
|
||||
- Pos+Pos -> [0, MAX] + [0, MAX] -> [0, 2 * MAX]. May overflow -> Saturating to MAX.
|
||||
- Neg+Neg -> [MIN, -1] + [MIN, -1] -> [2 * MIN, -2]. May underflow -> Saturating to MIN.
|
||||
- Pos+Neg -> [0, MAX] + [MIN, -1] -> [MIN, MAX - 1]. Impossible overflow or underflow.
|
||||
- SUB Operation:
|
||||
- Range operation rule: [a, b] - [c, d] = [a-d, b-c]
|
||||
- Pos-Neg -> [0, MAX] - [MIN, -1] -> [1, MAX - MIN]. Maybe overflow -> Saturating to MAX.
|
||||
- Neg-Pos -> [MIN, -1] - [0, MAX] -> [MIN - MAX, -1]. Maybe underflow -> Saturating to MIN.
|
||||
- Pos-Pos -> [0, MAX] - [0, MAX] -> [-MAX, MAX]. Impossible overflow or underflow.
|
||||
- Neg-Neg -> [MIN, -1] - [MIN, -1] -> [MIN + 1, -(MIN + 1)]. Impossible overflow or underflow.
|
||||
- MUL Operation:
|
||||
- Pos*Pos -> Maybe overflow -> Saturating to MAX.
|
||||
- Pos*Neg -> Maybe underflow -> Saturating to MIN.
|
||||
- Neg*Neg -> Maybe overflow -> Saturating to MAX.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Performs a saturating addition operation on two integers.
|
||||
* @details
|
||||
* If an overflow occurs during the addition,
|
||||
* the result will be saturated to the maximum/minimum value,
|
||||
* according to the the direction of overflow (overflow or underflow).
|
||||
* @tparam T Integer type.
|
||||
* @param[in] a The left operand of the addition.
|
||||
* @param[in] b The right operand of the addition.
|
||||
* @return The result of the saturating addition operation.
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
T saturating_add(T a, T b) {
|
||||
T result;
|
||||
if (hardware_add_overflow(a, b, &result)) {
|
||||
using Limits = std::numeric_limits<T>;
|
||||
if constexpr (std::is_unsigned_v<T>) {
|
||||
return Limits::max();
|
||||
} else {
|
||||
// Overflow only occurs when 2 operand have same sign.
|
||||
// So we simply check the sign of one of them.
|
||||
return (a > 0) ? Limits::max() : Limits::min();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Performs a saturating subtraction operation on two integers.
|
||||
* @details
|
||||
* If an overflow occurs during the subtraction,
|
||||
* the result will be saturated to the maximum/minimum value,
|
||||
* according to the the direction of overflow (overflow or underflow).
|
||||
* @tparam T Integer type.
|
||||
* @param[in] a The left operand of the subtraction.
|
||||
* @param[in] b The right operand of the subtraction.
|
||||
* @return The result of the saturating subtraction operation.
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
T saturating_sub(T a, T b) {
|
||||
T result;
|
||||
if (hardware_sub_overflow(a, b, &result)) {
|
||||
using Limits = std::numeric_limits<T>;
|
||||
if constexpr (std::is_unsigned_v<T>) {
|
||||
return 0;
|
||||
} else {
|
||||
// Overflow only occurs when 2 operand have different sign.
|
||||
// So we simply compare these 2 operand.
|
||||
// If a < b, then "a" is negative, otherwise positive.
|
||||
return (a < b) ? Limits::min() : Limits::max();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Performs a saturating multiplication operation on two integers.
|
||||
* @details
|
||||
* If an overflow occurs during the multiplication,
|
||||
* the result will be saturated to the maximum/minimum value,
|
||||
* according to the the direction of overflow (overflow or underflow).
|
||||
* @tparam T Integer type.
|
||||
* @param[in] a The left operand of the multiplication.
|
||||
* @param[in] b The right operand of the multiplication.
|
||||
* @return The result of the saturating multiplication operation.
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
T saturating_mul(T a, T b) {
|
||||
T result;
|
||||
if (hardware_mul_overflow(a, b, &result)) {
|
||||
using Limits = std::numeric_limits<T>;
|
||||
if constexpr (std::is_unsigned_v<T>) {
|
||||
return Limits::max();
|
||||
} else {
|
||||
// Check whether 2 operands have different sign.
|
||||
// If the result of XOR is true, these 2 operands should have different sign.
|
||||
return ((a ^ b) < 0) ? Limits::min() : Limits::max();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Performs a saturating division operation on two integers.
|
||||
* @details
|
||||
* If an overflow occurs during the division,
|
||||
* the result will be saturated to the maximum/minimum value,
|
||||
* according to the the direction of overflow (overflow or underflow).
|
||||
* @tparam T Integer type.
|
||||
* @param[in] a The left operand of the division.
|
||||
* @param[in] b The right operand of the division.
|
||||
* @return The result of the saturating division operation.
|
||||
* @exception std::logic_error If division by zero occurs.
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
T saturating_div(T a, T b) {
|
||||
// Division by zero is undefined behavior.
|
||||
if (ub_div_zero(a, b)) throw std::logic_error("div with zero");
|
||||
// `INT_MIN / -1` overflow undefined behavior.
|
||||
if (ub_signed_int_min_div_minus_one(a, b)) {
|
||||
return std::numeric_limits<T>::max();
|
||||
} else {
|
||||
return a / b;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Ordinary operations
|
||||
|
||||
// YYC MARK:
|
||||
// Rust-way add, sub, mul and div operators.
|
||||
// There is no any undefined behavior which may occurs in these functions.
|
||||
// These normal operator is just an alias to wrapping_* function family.
|
||||
|
||||
/**
|
||||
* @brief Performs an addition operation on two integers.
|
||||
* @tparam T Integer type.
|
||||
* @param[in] a The left operand of the addition.
|
||||
* @param[in] b The right operand of the addition.
|
||||
* @return The result of the addition operation.
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
T add(T a, T b) {
|
||||
return wrapping_add(a, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Performs a subtraction operation on two integers.
|
||||
* @tparam T Integer type
|
||||
* @param[in] a The left operand of the subtraction.
|
||||
* @param[in] b The right operand of the subtraction.
|
||||
* @return The result of the subtraction operation.
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
T sub(T a, T b) {
|
||||
return wrapping_sub(a, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Performs a multiplication operation on two integers.
|
||||
* @tparam T Integer type.
|
||||
* @param[in] a The left operand of the multiplication.
|
||||
* @param[in] b The right operand of the multiplication.
|
||||
* @return The result of the multiplication operation.
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
T mul(T a, T b) {
|
||||
return wrapping_mul(a, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Performs a division operation on two integers.
|
||||
* @tparam T Integer type.
|
||||
* @param[in] a The left operand of the division.
|
||||
* @param[in] b The right operand of the division.
|
||||
* @return The result of the division operation.
|
||||
* @exception std::logic_error If division by zero or value overflow occurs.
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::integral<T>
|
||||
T div(T a, T b) {
|
||||
return wrapping_div(a, b);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace yycc::num::safe_op
|
||||
|
||||
// YYC MARK:
|
||||
// Delete the macro definition to prevent polluting the content later.
|
||||
#undef YYCC_HARDWARE_OVERFLOW_GCC_FNS
|
||||
#undef YYCC_HARDWARE_OVERFLOW_WIN32_FNS
|
@ -1,12 +1,11 @@
|
||||
#pragma once
|
||||
#include "../string.hpp"
|
||||
#include "../string/reinterpret.hpp"
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <type_traits>
|
||||
#include <charconv>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
#define NS_YYCC_STRING ::yycc::string
|
||||
#define NS_YYCC_STRING_REINTERPRET ::yycc::string::reinterpret
|
||||
|
||||
/**
|
||||
@ -25,7 +24,7 @@ namespace yycc::num::stringify {
|
||||
inline constexpr size_t STRINGIFY_BUFFER_SIZE = 64u;
|
||||
/// @private
|
||||
/// @brief Type alias for the buffer used in string conversion.
|
||||
using StringifyBuffer = std::array<NS_YYCC_STRING::u8char, STRINGIFY_BUFFER_SIZE>;
|
||||
using StringifyBuffer = std::array<char8_t, STRINGIFY_BUFFER_SIZE>;
|
||||
|
||||
/**
|
||||
* @brief Return the string representation of given floating point value.
|
||||
@ -35,10 +34,9 @@ namespace yycc::num::stringify {
|
||||
* @param[in] precision The floating point precision used when getting string representation.
|
||||
* @return The string representation of given value.
|
||||
*/
|
||||
template<typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
|
||||
NS_YYCC_STRING::u8string stringify(T num,
|
||||
std::chars_format fmt = std::chars_format::general,
|
||||
int precision = 6) {
|
||||
template<typename T>
|
||||
requires(std::is_floating_point_v<T>)
|
||||
std::u8string stringify(T num, std::chars_format fmt = std::chars_format::general, int precision = 6) {
|
||||
namespace reinterpret = NS_YYCC_STRING_REINTERPRET;
|
||||
|
||||
StringifyBuffer buffer;
|
||||
@ -48,8 +46,7 @@ namespace yycc::num::stringify {
|
||||
fmt,
|
||||
precision);
|
||||
if (ec == std::errc()) {
|
||||
return NS_YYCC_STRING::u8string(buffer.data(),
|
||||
reinterpret::as_utf8(ptr) - buffer.data());
|
||||
return std::u8string(buffer.data(), reinterpret::as_utf8(ptr) - buffer.data());
|
||||
} else if (ec == std::errc::value_too_large) {
|
||||
// Too short buffer. This should not happen.
|
||||
throw std::out_of_range("stringify() buffer is not sufficient.");
|
||||
@ -58,6 +55,7 @@ namespace yycc::num::stringify {
|
||||
throw std::runtime_error("unreachable code.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the string representation of given integral value.
|
||||
* @tparam T The type derived from integral type except bool type.
|
||||
@ -65,18 +63,18 @@ namespace yycc::num::stringify {
|
||||
* @param[in] base Integer base used when getting string representation: a value between 2 and 36 (inclusive).
|
||||
* @return The string representation of given value.
|
||||
*/
|
||||
template<typename T, std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool>, int> = 0>
|
||||
NS_YYCC_STRING::u8string stringify(T num, int base = 10) {
|
||||
template<typename T>
|
||||
requires(std::is_integral_v<T> && !std::is_same_v<T, bool>)
|
||||
std::u8string stringify(T num, int base = 10) {
|
||||
namespace reinterpret = NS_YYCC_STRING_REINTERPRET;
|
||||
|
||||
|
||||
StringifyBuffer buffer;
|
||||
auto [ptr, ec] = std::to_chars(reinterpret::as_ordinary(buffer.data()),
|
||||
reinterpret::as_ordinary(buffer.data() + buffer.size()),
|
||||
num,
|
||||
base);
|
||||
if (ec == std::errc()) {
|
||||
return NS_YYCC_STRING::u8string(buffer.data(),
|
||||
reinterpret::as_utf8(ptr) - buffer.data());
|
||||
return std::u8string(buffer.data(), reinterpret::as_utf8(ptr) - buffer.data());
|
||||
} else if (ec == std::errc::value_too_large) {
|
||||
// Too short buffer. This should not happen.
|
||||
throw std::out_of_range("stringify() buffer is not sufficient.");
|
||||
@ -85,19 +83,20 @@ namespace yycc::num::stringify {
|
||||
throw std::runtime_error("unreachable code.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return the string representation of given bool value.
|
||||
* @tparam T The type derived from bool type.
|
||||
* @param[in] num The value need to get string representation.
|
||||
* @return The string representation of given value ("true" or "false").
|
||||
*/
|
||||
template<typename T, std::enable_if_t<std::is_same_v<T, bool>, int> = 0>
|
||||
NS_YYCC_STRING::u8string stringify(T num) {
|
||||
if (num) return NS_YYCC_STRING::u8string(YYCC_U8("true"));
|
||||
else return NS_YYCC_STRING::u8string(YYCC_U8("false"));
|
||||
template<typename T>
|
||||
requires(std::is_same_v<T, bool>)
|
||||
std::u8string stringify(T num) {
|
||||
if (num) return std::u8string(u8"true");
|
||||
else return std::u8string(u8"false");
|
||||
}
|
||||
|
||||
} // namespace yycc::string::stringify
|
||||
} // namespace yycc::num::stringify
|
||||
|
||||
#undef NS_YYCC_STRING_REINTERPRET
|
||||
#undef NS_YYCC_STRING
|
||||
|
@ -1,45 +0,0 @@
|
||||
#pragma once
|
||||
#include "../macro/feature_probe.hpp"
|
||||
|
||||
namespace yycc::patch::contains {
|
||||
|
||||
/**
|
||||
* @brief Checks if there is an element with key equivalent to key in the container.
|
||||
* @details
|
||||
* The polyfill to \c contains() function of unordered and ordered associative container.
|
||||
* Because this function only present after C++ 20.
|
||||
* This function will use our custom polyfill if the version of C++ standard you are using lower than C++ 20.
|
||||
* Otherwise it will fallback to vanilla standard library function.
|
||||
* @tparam TContainer
|
||||
* The type of container. This container must have \c find() and \c end() member functions.
|
||||
* @tparam TKey
|
||||
* The type of key of container.
|
||||
* If the container is a set, this type is the type of item in set.
|
||||
* If the container is a map, this type is the key type of map.
|
||||
* @param[in] container The reference to container to find.
|
||||
* @param[in] key Key value of the element to search for
|
||||
* @return True if there is such an element, otherwise false.
|
||||
* @remarks
|
||||
* This template function do not have constraint check.
|
||||
* If container type has \c find() and \c end() member functions, this template function will be created without any error.
|
||||
* However, this function should be used for standard library associative container according to its original purpose.
|
||||
* It means that the type of container usually and should be one of following types:
|
||||
* \li \c std::set
|
||||
* \li \c std::multiset
|
||||
* \li \c std::map
|
||||
* \li \c std::multimap
|
||||
* \li \c std::unordered_set
|
||||
* \li \c std::unordered_multiset
|
||||
* \li \c std::unordered_map
|
||||
* \li \c std::unordered_multimap
|
||||
*/
|
||||
template<class TContainer, class TKey>
|
||||
bool contains(const TContainer& container, const TKey& key) {
|
||||
#if defined(YYCC_CPPFEAT_CONTAINS)
|
||||
return container.contains(key);
|
||||
#else
|
||||
return container.find(key) != container.end();
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace yycc::patch::container
|
39
src/yycc/patch/fopen.cpp
Normal file
39
src/yycc/patch/fopen.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
#include "fopen.hpp"
|
||||
#include "../macro/os_detector.hpp"
|
||||
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
// Include header file including MSVCRT specific function.
|
||||
#include <wchar.h>
|
||||
// Include encoding convertion header
|
||||
#include "../encoding/windows.hpp"
|
||||
// Define namespace macro
|
||||
#define ENC ::yycc::encoding::windows
|
||||
#else
|
||||
// Include reinterpret header and define namespace macro
|
||||
#include "../string/reinterpret.hpp"
|
||||
#define REINTERPRET ::yycc::string::reinterpret
|
||||
#endif
|
||||
|
||||
namespace yycc::patch::fopen {
|
||||
|
||||
std::FILE* fopen(const char8_t* u8_filepath, const char8_t* u8_mode) {
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
// convert mode and file path to wchar
|
||||
auto wmode = ENC::to_wchar(u8_mode);
|
||||
auto wpath = ENC::to_wchar(u8_filepath);
|
||||
|
||||
// check convertion success
|
||||
if (wmode.has_value() && wpath.has_value()) {
|
||||
// Call MSVCRT specified fopen which support wchar as argument.
|
||||
// Reference: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fopen-wfopen?view=msvc-170
|
||||
return _wfopen(wpath.value().c_str(), wmode.value().c_str());
|
||||
} else {
|
||||
// fail to convert encoding
|
||||
return nullptr;
|
||||
}
|
||||
#else
|
||||
return std::fopen(REINTERPRET::as_ordinary(u8_filepath), REINTERPRET::as_ordinary(u8_mode));
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace yycc::patch::fopen
|
31
src/yycc/patch/fopen.hpp
Normal file
31
src/yycc/patch/fopen.hpp
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <cstdio>
|
||||
|
||||
namespace yycc::patch::fopen {
|
||||
|
||||
/// @brief C++ standard deleter for std::FILE*
|
||||
class StdFileDeleter {
|
||||
public:
|
||||
StdFileDeleter() {}
|
||||
void operator() (std::FILE* ptr) {
|
||||
if (ptr != nullptr) {
|
||||
std::fclose(ptr);
|
||||
}
|
||||
}
|
||||
};
|
||||
/// @brief Smart unique pointer of \c std::FILE*
|
||||
using SmartStdFile = std::unique_ptr<std::FILE, StdFileDeleter>;
|
||||
|
||||
/**
|
||||
* @brief The UTF8 version of \c std::fopen.
|
||||
* @param[in] u8_filepath The UTF8 encoded path to the file to be opened.
|
||||
* @param[in] u8_mode UTF8 encoded mode string of the file to be opened.
|
||||
* @remarks
|
||||
* This function is suit for Windows because std::fopen do not support UTF8 on Windows.
|
||||
* On other platforms, this function will delegate request directly to std::fopen.
|
||||
* @return \c FILE* of the file to be opened, or nullptr if failed.
|
||||
*/
|
||||
std::FILE* fopen(const char8_t* u8_filepath, const char8_t* u8_mode);
|
||||
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
#include "path.hpp"
|
||||
#include "../macro/os_detector.hpp"
|
||||
#include <stdexcept>
|
||||
|
||||
#define NS_YYCC_STRING ::yycc::string
|
||||
|
||||
namespace yycc::patch::path {
|
||||
|
||||
// TODO: Fix this after finish encoding parts.
|
||||
|
||||
// TODO: Add native implementation if C++ support it.
|
||||
// So we need add feature test macro at the same time.
|
||||
|
||||
std::filesystem::path to_std_path(const NS_YYCC_STRING::u8string_view& u8_path) {
|
||||
// #if YYCC_OS == YYCC_OS_WINDOWS
|
||||
|
||||
// // convert path to wchar
|
||||
// std::wstring wpath;
|
||||
// if (!YYCC::EncodingHelper::UTF8ToWchar(u8_path, wpath))
|
||||
// throw std::invalid_argument("Fail to convert given UTF8 string.");
|
||||
// // return path with wchar_t ctor
|
||||
// return std::filesystem::path(wpath);
|
||||
|
||||
// #else
|
||||
// std::string cache = YYCC::EncodingHelper::ToOrdinary(u8_path);
|
||||
// return std::filesystem::path(cache.c_str());
|
||||
// #endif
|
||||
return std::filesystem::path();
|
||||
}
|
||||
|
||||
NS_YYCC_STRING::u8string to_u8string(const std::filesystem::path& path) {
|
||||
// #if YYCC_OS == YYCC_OS_WINDOWS
|
||||
|
||||
// // get and convert to utf8
|
||||
// NS_YYCC_STRING::u8string u8_path;
|
||||
// if (!YYCC::EncodingHelper::WcharToUTF8(path.c_str(), u8_path))
|
||||
// throw std::invalid_argument("Fail to convert to UTF8 string.");
|
||||
|
||||
// // return utf8 path
|
||||
// return u8_path;
|
||||
|
||||
// #else
|
||||
// return EncodingHelper::ToUTF8(path.string());
|
||||
// #endif
|
||||
return NS_YYCC_STRING::u8string();
|
||||
}
|
||||
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
#pragma once
|
||||
#include "../string.hpp"
|
||||
#include <filesystem>
|
||||
|
||||
#define NS_YYCC_STRING ::yycc::string
|
||||
|
||||
/**
|
||||
* @brief \c Standard library related patches for UTF8 compatibility and the limitation of C++ standard version.
|
||||
* @details
|
||||
* See also \ref std_patch.
|
||||
*/
|
||||
namespace yycc::patch::path {
|
||||
|
||||
/**
|
||||
* @brief Constructs \c std::filesystem::path from UTF8 path.
|
||||
* @param[in] u8_path UTF8 path string for building.
|
||||
* @return \c std::filesystem::path instance.
|
||||
* @exception std::invalid_argument Fail to parse given UTF8 string (maybe invalid?).
|
||||
*/
|
||||
std::filesystem::path to_std_path(const NS_YYCC_STRING::u8string_view& u8_path);
|
||||
|
||||
/**
|
||||
* @brief Returns the UTF8 representation of given \c std::filesystem::path.
|
||||
* @param[in] path The \c std::filesystem::path instance converting to UTF8 path.
|
||||
* @return The UTF8 representation of given \c std::filesystem::path.
|
||||
* @exception std::invalid_argument Fail to convert to UTF8 string.
|
||||
*/
|
||||
NS_YYCC_STRING::u8string to_u8string(const std::filesystem::path& path);
|
||||
|
||||
} // namespace yycc::patch::path
|
||||
|
||||
#undef NS_YYCC_STRING
|
20
src/yycc/patch/ptr_pad.hpp
Normal file
20
src/yycc/patch/ptr_pad.hpp
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include "../macro/ptr_size_detector.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* @def PRIXPTR_LPAD
|
||||
* @brief The left-padding zero format string of HEX-printed pointer type.
|
||||
* @details
|
||||
* When printing a pointer with HEX style, we usually hope it can be left-padded with some zero for easy reading.
|
||||
* In different architecture, the size of this padding is differnet too so we create this macro.
|
||||
*
|
||||
* In 32-bit environment, it will be "08" meaning left pad zero until 8 number position.
|
||||
* In 64-bit environment, it will be "016" meaning left pad zero until 16 number position.
|
||||
*/
|
||||
|
||||
#if defined(YYCC_PTRSIZE_32)
|
||||
#define PRIXPTR_LPAD "08"
|
||||
#else
|
||||
#define PRIXPTR_LPAD "016"
|
||||
#endif
|
@ -1,203 +0,0 @@
|
||||
#pragma once
|
||||
#include "../macro/feature_probe.hpp"
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace yycc::patch::starts_ends_with {
|
||||
|
||||
// Reference:
|
||||
// https://en.cppreference.com/w/cpp/string/basic_string_view/starts_with
|
||||
// https://en.cppreference.com/w/cpp/string/basic_string_view/ends_with
|
||||
// https://en.cppreference.com/w/cpp/string/basic_string/starts_with
|
||||
// https://en.cppreference.com/w/cpp/string/basic_string/ends_with
|
||||
|
||||
#pragma region For String View
|
||||
|
||||
/**
|
||||
* @brief Checks if the string view begins with the given prefix
|
||||
* @param[in] that The string view to find.
|
||||
* @param[in] sv A string view which may be a result of implicit conversion from \c std::basic_string.
|
||||
* @return True if the string view begins with the provided prefix, false otherwise.
|
||||
*/
|
||||
template<class CharT, class Traits = std::char_traits<CharT>>
|
||||
bool starts_with(const std::basic_string_view<CharT, Traits>& that,
|
||||
std::basic_string_view<CharT, Traits> sv) noexcept {
|
||||
#if defined(YYCC_CPPFEAT_STARTS_ENDS_WITH)
|
||||
return that.starts_with(sv);
|
||||
#else
|
||||
return std::basic_string_view<CharT, Traits>(that.data(), std::min(that.size(), sv.size()))
|
||||
== sv;
|
||||
#endif
|
||||
}
|
||||
/**
|
||||
* @brief Checks if the string view begins with the given prefix
|
||||
* @param[in] that The string view to find.
|
||||
* @param[in] ch A single character.
|
||||
* @return True if the string view begins with the provided prefix, false otherwise.
|
||||
*/
|
||||
template<class CharT, class Traits = std::char_traits<CharT>>
|
||||
bool starts_with(const std::basic_string_view<CharT, Traits>& that, CharT ch) noexcept {
|
||||
#if defined(YYCC_CPPFEAT_STARTS_ENDS_WITH)
|
||||
return that.starts_with(ch);
|
||||
#else
|
||||
return !that.empty() && Traits::eq(that.front(), ch);
|
||||
#endif
|
||||
}
|
||||
/**
|
||||
* @brief Checks if the string view begins with the given prefix
|
||||
* @param[in] that The string view to find.
|
||||
* @param[in] s A null-terminated character string.
|
||||
* @return True if the string view begins with the provided prefix, false otherwise.
|
||||
*/
|
||||
template<class CharT, class Traits = std::char_traits<CharT>>
|
||||
bool starts_with(const std::basic_string_view<CharT, Traits>& that, const CharT* s) noexcept {
|
||||
#if defined(YYCC_CPPFEAT_STARTS_ENDS_WITH)
|
||||
return that.starts_with(s);
|
||||
#else
|
||||
return starts_with(that, std::basic_string_view(s));
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if the string view ends with the given suffix
|
||||
* @param[in] that The string view to find.
|
||||
* @param[in] sv A string view which may be a result of implicit conversion from \c std::basic_string.
|
||||
* @return True if the string view ends with the provided suffix, false otherwise.
|
||||
*/
|
||||
template<class CharT, class Traits = std::char_traits<CharT>>
|
||||
bool ends_with(const std::basic_string_view<CharT, Traits>& that,
|
||||
std::basic_string_view<CharT, Traits> sv) noexcept {
|
||||
#if defined(YYCC_CPPFEAT_STARTS_ENDS_WITH)
|
||||
return that.ends_with(sv);
|
||||
#else
|
||||
return that.size() >= sv.size()
|
||||
&& that.compare(that.size() - sv.size(),
|
||||
std::basic_string_view<CharT, Traits>::npos,
|
||||
sv)
|
||||
== 0;
|
||||
#endif
|
||||
}
|
||||
/**
|
||||
* @brief Checks if the string view ends with the given suffix
|
||||
* @param[in] that The string view to find.
|
||||
* @param[in] ch A single character.
|
||||
* @return True if the string view ends with the provided suffix, false otherwise.
|
||||
*/
|
||||
template<class CharT, class Traits = std::char_traits<CharT>>
|
||||
bool ends_with(const std::basic_string_view<CharT, Traits>& that, CharT ch) noexcept {
|
||||
#if defined(YYCC_CPPFEAT_STARTS_ENDS_WITH)
|
||||
return that.ends_with(ch);
|
||||
#else
|
||||
return !that.empty() && Traits::eq(that.back(), ch);
|
||||
#endif
|
||||
}
|
||||
/**
|
||||
* @brief Checks if the string view ends with the given suffix
|
||||
* @param[in] that The string view to find.
|
||||
* @param[in] s A null-terminated character string.
|
||||
* @return True if the string view ends with the provided suffix, false otherwise.
|
||||
*/
|
||||
template<class CharT, class Traits = std::char_traits<CharT>>
|
||||
bool ends_with(const std::basic_string_view<CharT, Traits>& that, const CharT* s) noexcept {
|
||||
#if defined(YYCC_CPPFEAT_STARTS_ENDS_WITH)
|
||||
return that.ends_with(s);
|
||||
#else
|
||||
return ends_with(that, std::basic_string_view(s));
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region For String
|
||||
|
||||
/**
|
||||
* @brief Checks if the string begins with the given prefix
|
||||
* @param[in] that The string to find.
|
||||
* @param[in] sv A string view which may be a result of implicit conversion from \c std::basic_string.
|
||||
* @return True if the string view begins with the provided prefix, false otherwise.
|
||||
*/
|
||||
template<class CharT, class Traits = std::char_traits<CharT>>
|
||||
bool starts_with(const std::basic_string<CharT, Traits>& that,
|
||||
std::basic_string_view<CharT, Traits> sv) noexcept {
|
||||
#if defined(YYCC_CPPFEAT_STARTS_ENDS_WITH)
|
||||
return that.starts_with(sv);
|
||||
#else
|
||||
return starts_with(std::basic_string_view<CharT, Traits>(that.data(), that.size()), sv);
|
||||
#endif
|
||||
}
|
||||
/**
|
||||
* @brief Checks if the string begins with the given prefix
|
||||
* @param[in] that The string to find.
|
||||
* @param[in] ch A single character.
|
||||
* @return True if the string view begins with the provided prefix, false otherwise.
|
||||
*/
|
||||
template<class CharT, class Traits = std::char_traits<CharT>>
|
||||
bool starts_with(const std::basic_string<CharT, Traits>& that, CharT ch) noexcept {
|
||||
#if defined(YYCC_CPPFEAT_STARTS_ENDS_WITH)
|
||||
return that.starts_with(ch);
|
||||
#else
|
||||
return starts_with(std::basic_string_view<CharT, Traits>(that.data(), that.size()), ch);
|
||||
#endif
|
||||
}
|
||||
/**
|
||||
* @brief Checks if the string begins with the given prefix
|
||||
* @param[in] that The string to find.
|
||||
* @param[in] s A null-terminated character string.
|
||||
* @return True if the string view begins with the provided prefix, false otherwise.
|
||||
*/
|
||||
template<class CharT, class Traits = std::char_traits<CharT>>
|
||||
bool starts_with(const std::basic_string<CharT, Traits>& that, const CharT* s) noexcept {
|
||||
#if defined(YYCC_CPPFEAT_STARTS_ENDS_WITH)
|
||||
return that.starts_with(s);
|
||||
#else
|
||||
return starts_with(std::basic_string_view<CharT, Traits>(that.data(), that.size()), s);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if the string ends with the given suffix
|
||||
* @param[in] that The string to find.
|
||||
* @param[in] sv A string view which may be a result of implicit conversion from \c std::basic_string.
|
||||
* @return True if the string view ends with the provided suffix, false otherwise.
|
||||
*/
|
||||
template<class CharT, class Traits = std::char_traits<CharT>>
|
||||
bool ends_with(const std::basic_string<CharT, Traits>& that,
|
||||
std::basic_string_view<CharT, Traits> sv) noexcept {
|
||||
#if defined(YYCC_CPPFEAT_STARTS_ENDS_WITH)
|
||||
return that.ends_with(sv);
|
||||
#else
|
||||
return ends_with(std::basic_string_view<CharT, Traits>(that.data(), that.size()), sv);
|
||||
#endif
|
||||
}
|
||||
/**
|
||||
* @brief Checks if the string ends with the given suffix
|
||||
* @param[in] that The string to find.
|
||||
* @param[in] ch A single character.
|
||||
* @return True if the string view ends with the provided suffix, false otherwise.
|
||||
*/
|
||||
template<class CharT, class Traits = std::char_traits<CharT>>
|
||||
bool ends_with(const std::basic_string<CharT, Traits>& that, CharT ch) noexcept {
|
||||
#if defined(YYCC_CPPFEAT_STARTS_ENDS_WITH)
|
||||
return that.ends_with(ch);
|
||||
#else
|
||||
return ends_with(std::basic_string_view<CharT, Traits>(that.data(), that.size()), ch);
|
||||
#endif
|
||||
}
|
||||
/**
|
||||
* @brief Checks if the string ends with the given suffix
|
||||
* @param[in] that The string to find.
|
||||
* @param[in] s A null-terminated character string.
|
||||
* @return True if the string view ends with the provided suffix, false otherwise.
|
||||
*/
|
||||
template<class CharT, class Traits = std::char_traits<CharT>>
|
||||
bool ends_with(const std::basic_string<CharT, Traits>& that, const CharT* s) noexcept {
|
||||
#if defined(YYCC_CPPFEAT_STARTS_ENDS_WITH)
|
||||
return that.ends_with(s);
|
||||
#else
|
||||
return ends_with(std::basic_string_view<CharT, Traits>(that.data(), that.size()), s);
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace yycc::patch::starts_ends_with
|
@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Prelude section
|
||||
#include "../string.hpp"
|
||||
namespace yycc::prelude {
|
||||
|
||||
#define NS_YYCC_STRING ::yycc::string
|
||||
|
||||
using u8char = NS_YYCC_STRING::u8char;
|
||||
using u8string = NS_YYCC_STRING::u8string;
|
||||
using u8string_view = NS_YYCC_STRING::u8string_view;
|
||||
|
||||
#undef NS_YYCC_STRING
|
||||
|
||||
} // namespace yycc::prelude
|
||||
|
||||
// Expose all members
|
||||
using namespace yycc::prelude;
|
159
src/yycc/rust/env.cpp
Normal file
159
src/yycc/rust/env.cpp
Normal file
@ -0,0 +1,159 @@
|
||||
#include "env.hpp"
|
||||
#include "../macro/os_detector.hpp"
|
||||
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
#include "../encoding/windows.hpp"
|
||||
#include "../num/safe_op.hpp"
|
||||
#include "../num/safe_cast.hpp"
|
||||
#include <Windows.h>
|
||||
#include <winbase.h>
|
||||
#else
|
||||
#include "../string/reinterpret.hpp"
|
||||
#include <cstdlib>
|
||||
#include <cerrno>
|
||||
#include <stdexcept>
|
||||
#endif
|
||||
|
||||
#define SAFECAST ::yycc::num::safe_cast
|
||||
#define SAFEOP ::yycc::num::safe_op
|
||||
#define ENC ::yycc::encoding::windows
|
||||
#define REINTERPRET ::yycc::string::reinterpret
|
||||
|
||||
namespace yycc::rust::env {
|
||||
|
||||
EnvResult<std::u8string> get_var(const std::u8string_view &name) {
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
// Reference: https://learn.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-getenvironmentvariablew
|
||||
// Convert to wchar
|
||||
auto wname = ENC::to_wchar(name);
|
||||
if (!wname.has_value()) {
|
||||
return std::unexpected(EnvError::BadEncoding);
|
||||
}
|
||||
|
||||
// Prepare a variable with proper init size for holding value.
|
||||
std::wstring wvalue;
|
||||
wvalue.resize(256);
|
||||
|
||||
// Start to fetch value
|
||||
while (true) {
|
||||
// YYC MARK:
|
||||
// Due to the shitty design of GetEnvironmentVariableW,
|
||||
// the size passed to this function must include NULL terminal.
|
||||
// So we forcely use checked add and sub for this bad behavior.
|
||||
auto fct_size = SAFEOP::checked_add<size_t>(wvalue.size(), 1);
|
||||
if (!fct_size.has_value()) return std::unexpected(EnvError::BadArithmetic);
|
||||
auto rv = ::GetEnvironmentVariableW(wname.value().c_str(), wvalue.data(), fct_size.value());
|
||||
|
||||
// Check the return value
|
||||
if (rv == 0) {
|
||||
// Function failed. Extract error reason.
|
||||
auto ec = GetLastError();
|
||||
if (ec == ERROR_ENVVAR_NOT_FOUND) return std::unexpected(EnvError::NoSuchName);
|
||||
else return std::unexpected(EnvError::BadCall);
|
||||
} else {
|
||||
// Function okey. Check the size.
|
||||
// Fetch function expected size.
|
||||
auto rv_size = SAFECAST::try_to<size_t>(rv);
|
||||
if (!rv_size.has_value()) return std::unexpected(EnvError::BadArithmetic);
|
||||
auto exp_size = SAFEOP::checked_sub<size_t>(rv_size.value(), 1);
|
||||
if (!exp_size.has_value()) return std::unexpected(EnvError::BadArithmetic);
|
||||
|
||||
// YYC MARK:
|
||||
// According to Microsoft, the return value of this function is just a bullshit.
|
||||
// If "wvalue" is big enough, the range of return value is [0, wvalue.size()],
|
||||
// indicating the size of final string, excluding NULL terminal.
|
||||
// otherwise, the range of return value is [wvalue.size() + 1, +inf),
|
||||
// indicating the required size of buffer including NULL terminal.
|
||||
// So we must treat this return value carefully.
|
||||
if (exp_size.value() <= wvalue.size()) {
|
||||
// Okey, shrink to the real size of value and break.
|
||||
wvalue.resize(rv_size.value());
|
||||
break;
|
||||
} else {
|
||||
// We need resize it and try again.
|
||||
wvalue.resize(exp_size.value());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convert back to UTF8 string and return.
|
||||
return ENC::to_utf8(wvalue).transform_error([](auto err) { return EnvError::BadEncoding; });
|
||||
#else
|
||||
// String view is not NULL-terminal-guaranted,
|
||||
// so we solve this when casting its type.
|
||||
auto ordinary_name = REINTERPRET::as_ordinary(name);
|
||||
|
||||
// Fetch variable
|
||||
auto finder = std::getenv(ordinary_name.c_str());
|
||||
if (finder == nullptr) return std::unexpected(EnvError::NoSuchName);
|
||||
else return REINTERPRET::as_utf8(finder);
|
||||
#endif
|
||||
}
|
||||
|
||||
EnvResult<void> set_var(const std::u8string_view &name, const std::u8string_view &value) {
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
// Reference: https://learn.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-setenvironmentvariablew
|
||||
|
||||
// Convert to wchar
|
||||
auto wname = ENC::to_wchar(name);
|
||||
auto wvalue = ENC::to_wchar(value);
|
||||
if (!(wname.has_value() && wvalue.has_value())) {
|
||||
return std::unexpected(EnvError::BadEncoding);
|
||||
}
|
||||
|
||||
// Delete variable and check result.
|
||||
auto rv = ::SetEnvironmentVariableW(wname.value().c_str(), wvalue.value().c_str());
|
||||
if (!rv) {
|
||||
return std::unexpected(EnvError::BadCall);
|
||||
}
|
||||
|
||||
return {};
|
||||
#else
|
||||
// Reference: https://pubs.opengroup.org/onlinepubs/9699919799/functions/setenv.html
|
||||
|
||||
// Set variable
|
||||
auto ordinary_name = REINTERPRET::as_ordinary(name);
|
||||
auto ordinary_value = REINTERPRET::as_ordinary(value);
|
||||
auto rv = setenv(ordinary_name.c_str(), ordinary_value.c_str(), 1);
|
||||
if (rv == 0) return {};
|
||||
|
||||
// Check error type
|
||||
if (errno == EINVAL) return std::unexpected(EnvError::BadName);
|
||||
else if (errno == ENOMEM) return std::unexpected(EnvError::NoMemory);
|
||||
else throw std::runtime_error("impossible errno");
|
||||
#endif
|
||||
}
|
||||
|
||||
EnvResult<void> del_var(const std::u8string_view &name) {
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
// Reference: https://learn.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-setenvironmentvariablew
|
||||
|
||||
// Convert to wchar
|
||||
auto wname = ENC::to_wchar(name);
|
||||
if (!wname.has_value()) {
|
||||
return std::unexpected(EnvError::BadEncoding);
|
||||
}
|
||||
|
||||
// Delete variable and check result.
|
||||
auto rv = ::SetEnvironmentVariableW(wname.value().c_str(), NULL);
|
||||
if (!rv) {
|
||||
return std::unexpected(EnvError::BadCall);
|
||||
}
|
||||
|
||||
return {};
|
||||
#else
|
||||
// Reference: https://pubs.opengroup.org/onlinepubs/9699919799/functions/unsetenv.html
|
||||
|
||||
// Delete variable
|
||||
auto ordinary_name = REINTERPRET::as_ordinary(name);
|
||||
auto rv = unsetenv(ordinary_name.c_str());
|
||||
if (rv == 0) return {};
|
||||
|
||||
// Check error type
|
||||
if (errno == EINVAL) return std::unexpected(EnvError::BadName);
|
||||
else throw std::runtime_error("impossible errno");
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace yycc::rust::env
|
62
src/yycc/rust/env.hpp
Normal file
62
src/yycc/rust/env.hpp
Normal file
@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <expected>
|
||||
|
||||
/**
|
||||
* @brief The namespace providing environment variable operations.
|
||||
* @details
|
||||
* When I programming with Rust, I was astonished that
|
||||
* Rust standard library have so much robust environment variable operations.
|
||||
* Oppositly, C++ STL still lake in this even in today.
|
||||
*
|
||||
* The functions manipulating environment variable is different in different OS.
|
||||
* I create this namespace inspired from Rust standard library
|
||||
* to glue all these things up and make a uniform interface.
|
||||
*/
|
||||
namespace yycc::rust::env {
|
||||
|
||||
/// @brief The error occurs in this module.
|
||||
enum class EnvError {
|
||||
NoSuchName, ///< The variable with given name is not presented.
|
||||
BadEncoding, ///< Error when performing encoding convertion.
|
||||
BadArithmetic, ///< Error when performing arithmetic operations.
|
||||
BadCall, ///< Error occurs when calling backend functions.
|
||||
BadName, ///< Given name is ill-formated (empty string or has "=" character).
|
||||
NoMemory, ///< No enough memory to finish this operation.
|
||||
};
|
||||
|
||||
/// @brief The result type in this module.
|
||||
template<typename T>
|
||||
using EnvResult = std::expected<T, EnvError>;
|
||||
|
||||
/**
|
||||
* @brief Get the value of given environment variable name.
|
||||
* @param[in] name The name of environment variable
|
||||
* @return Gotten value, or error occurs.
|
||||
*/
|
||||
EnvResult<std::u8string> get_var(const std::u8string_view& name);
|
||||
|
||||
/**
|
||||
* @brief Set the value of given environment variable name.
|
||||
* @details
|
||||
* If there is no such name variable presented in environment,
|
||||
* a new variable will be created,
|
||||
* otherwise, new value will overwrite old value.
|
||||
* @param[in] name The name of environment variable
|
||||
* @param[in] value The value to be written into.
|
||||
* @return Nothing or error occurs.
|
||||
*/
|
||||
EnvResult<void> set_var(const std::u8string_view& name, const std::u8string_view& value);
|
||||
|
||||
/**
|
||||
* @brief Delete environment variable with given name.
|
||||
* @details
|
||||
* If given variable is not presented in environment,
|
||||
* this function will NOT return error.
|
||||
* @param[in] name The name of environment variable
|
||||
* @return Nothing, or error occurs.
|
||||
*/
|
||||
EnvResult<void> del_var(const std::u8string_view& name);
|
||||
|
||||
} // namespace yycc::rust::env
|
@ -1,99 +0,0 @@
|
||||
#pragma once
|
||||
#include "../../macro/feature_probe.hpp"
|
||||
#include "../../num/parse.hpp"
|
||||
#include "../panic.hpp"
|
||||
#include "../result.hpp"
|
||||
|
||||
#define NS_YYCC_STRING ::yycc::string
|
||||
#define NS_YYCC_NUM_PARSE ::yycc::num::parse
|
||||
#define NS_YYCC_RUST_RESULT ::yycc::rust::result
|
||||
|
||||
/**
|
||||
* @namespace yycc::rust::parse
|
||||
* @brief Provides Rust-inspired parsing utilities for converting strings to various types.
|
||||
* @details
|
||||
* This namespace contains template functions for parsing strings into different types
|
||||
* (floating-point, integral, boolean) with Rust-like Result error handling.
|
||||
*/
|
||||
namespace yycc::rust::num::parse {
|
||||
|
||||
#if defined(YYCC_CPPFEAT_EXPECTED)
|
||||
|
||||
/// @brief The error type of parsing.
|
||||
using Error = NS_YYCC_NUM_PARSE::ParseError;
|
||||
|
||||
/// @brief The result type of parsing.
|
||||
/// @tparam T The expected value type in result.
|
||||
template<typename T>
|
||||
using Result = NS_YYCC_RUST_RESULT::Result<T, Error>;
|
||||
|
||||
/**
|
||||
* @brief Parses a string into a floating-point value.
|
||||
* @tparam T Floating-point type to parse into (float, double, etc.)
|
||||
* @param strl String view to parse
|
||||
* @param fmt Formatting flags for parsing (default: general)
|
||||
* @return Result<T> containing either the parsed value or an error
|
||||
*/
|
||||
template<typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
|
||||
Result<T> parse(const NS_YYCC_STRING::u8string_view& strl,
|
||||
std::chars_format fmt = std::chars_format::general) {
|
||||
auto rv = NS_YYCC_NUM_PARSE::priv_parse<T>(strl, fmt);
|
||||
|
||||
if (const auto* ptr = std::get_if<T>(&rv)) {
|
||||
return NS_YYCC_RUST_RESULT::Ok<Result<T>>(*ptr);
|
||||
} else if (const auto* ptr = std::get_if<Error>(&rv)) {
|
||||
return NS_YYCC_RUST_RESULT::Err<Result<T>>(*ptr);
|
||||
} else {
|
||||
// Unreachable
|
||||
RS_PANIC("unreachable code.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parses a string into an integral value (excluding bool).
|
||||
* @tparam T Integral type to parse into (int, long, etc.)
|
||||
* @param strl String view to parse
|
||||
* @param base Numeric base for parsing (default: 10)
|
||||
* @return Result<T> containing either the parsed value or an error
|
||||
*/
|
||||
template<typename T, std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool>, int> = 0>
|
||||
Result<T> parse(const NS_YYCC_STRING::u8string_view& strl, int base = 10) {
|
||||
auto rv = NS_YYCC_NUM_PARSE::priv_parse<T>(strl, base);
|
||||
|
||||
if (const auto* ptr = std::get_if<T>(&rv)) {
|
||||
return NS_YYCC_RUST_RESULT::Ok<Result<T>>(*ptr);
|
||||
} else if (const auto* ptr = std::get_if<Error>(&rv)) {
|
||||
return NS_YYCC_RUST_RESULT::Err<Result<T>>(*ptr);
|
||||
} else {
|
||||
// Unreachable
|
||||
RS_PANIC("unreachable code.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parses a string into a boolean value.
|
||||
* @tparam T Must be bool type
|
||||
* @param strl String view to parse
|
||||
* @return Result<bool> containing either the parsed value or an error
|
||||
*/
|
||||
template<typename T, std::enable_if_t<std::is_same_v<T, bool>, int> = 0>
|
||||
Result<T> parse(const NS_YYCC_STRING::u8string_view& strl) {
|
||||
auto rv = NS_YYCC_NUM_PARSE::priv_parse<T>(strl);
|
||||
|
||||
if (const auto* ptr = std::get_if<T>(&rv)) {
|
||||
return NS_YYCC_RUST_RESULT::Ok<Result<T>>(*ptr);
|
||||
} else if (const auto* ptr = std::get_if<Error>(&rv)) {
|
||||
return NS_YYCC_RUST_RESULT::Err<Result<T>>(*ptr);
|
||||
} else {
|
||||
// Unreachable
|
||||
RS_PANIC("unreachable code.");
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#undef NS_YYCC_RUST_RESULT
|
||||
#undef NS_YYCC_NUM_PARSE
|
||||
#undef NS_YYCC_STRING
|
@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
#include "../../num/stringify.hpp"
|
||||
|
||||
namespace yycc::rust::num::stringify {
|
||||
|
||||
// There is no modification for legacy "stringify" functions like "parse".
|
||||
// So we simply expose all functions into this namespace.
|
||||
using namespace ::yycc::num::stringify;
|
||||
|
||||
} // namespace yycc::rust::stringify
|
@ -1,10 +1,12 @@
|
||||
#pragma once
|
||||
#include <optional>
|
||||
|
||||
/// @brief The reproduction of Rust Option type.
|
||||
/// @details
|
||||
/// This namespace reproduce Rust Option type, and its members Some and None in C++.
|
||||
/// However Option is not important than Result, so its implementation is very casual.
|
||||
/**
|
||||
* @brief The reproduction of Rust Option type.
|
||||
* @details
|
||||
* This namespace reproduce Rust Option type, and its members Some and None in C++.
|
||||
* However Option is not important than Result, so its implementation is very casual.
|
||||
*/
|
||||
namespace yycc::rust::option {
|
||||
|
||||
template<typename T>
|
||||
|
@ -1,37 +1,35 @@
|
||||
#include "panic.hpp"
|
||||
#include "../macro/feature_probe.hpp"
|
||||
|
||||
#include "../carton/termcolor.hpp"
|
||||
#include "../string/reinterpret.hpp"
|
||||
#include <cstdlib>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#if defined(YYCC_CPPFEAT_STACKTRACE)
|
||||
#include <stacktrace>
|
||||
#endif
|
||||
|
||||
#define TERMCOLOR ::yycc::carton::termcolor
|
||||
#define REINTERPRET ::yycc::string::reinterpret
|
||||
|
||||
namespace yycc::rust::panic {
|
||||
|
||||
// TODO:
|
||||
// I sadly remove the stacktrace feature for panic function.
|
||||
// Because in GCC, it has link error (can be fixed by extra link option).
|
||||
// In Clang, it even lack the whole header file.
|
||||
// It seems that STL providers are not ready for this feature. So I decide remove it entirely.
|
||||
// Once every STL probiders have ready for this, I will add it back.
|
||||
|
||||
void panic(const char* file, int line, const std::string_view& msg) {
|
||||
// Output message in stderr.
|
||||
auto& dst = std::cerr;
|
||||
|
||||
// TODO: Fix colorful output when finishing `termcolor` lib.
|
||||
|
||||
// Print error message if we support it.
|
||||
// // Setup color
|
||||
// dst << FOREGROUND<Color::Red>;
|
||||
// Setup color
|
||||
dst << REINTERPRET::as_ordinary_view(TERMCOLOR::foreground(TERMCOLOR::Color::Red));
|
||||
// File name and line number message
|
||||
dst << "program paniked at " << std::quoted(file) << ":Ln" << line << std::endl;
|
||||
// User custom message
|
||||
dst << "note: " << msg << std::endl;
|
||||
// Stacktrace message if we support it.
|
||||
#if defined(YYCC_CPPFEAT_STACKTRACE)
|
||||
dst << "stacktrace: " << std::endl;
|
||||
dst << std::stacktrace::current() << std::endl;
|
||||
#else
|
||||
dst << "there is no stacktrace because your C++ runtime do not support it." << std::endl;
|
||||
#endif
|
||||
// // Restore color
|
||||
// dst << RESET;
|
||||
// Restore color
|
||||
dst << REINTERPRET::as_ordinary_view(TERMCOLOR::reset());;
|
||||
|
||||
// Make sure all messages are flushed into screen.
|
||||
dst.flush();
|
||||
|
@ -1,10 +1,6 @@
|
||||
#pragma once
|
||||
#include "../macro/feature_probe.hpp"
|
||||
#include <string_view>
|
||||
|
||||
#if defined(YYCC_CPPFEAT_FORMAT)
|
||||
#include <format>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Provides Rust-style panic functionality for immediate program termination on unrecoverable errors.
|
||||
@ -38,13 +34,7 @@ namespace yycc::rust::panic {
|
||||
* The macro parameters are the message to format and its arguments, following \c std::format syntax.
|
||||
* This macro essentially calls \c std::format internally.
|
||||
*/
|
||||
#if defined(YYCC_CPPFEAT_FORMAT)
|
||||
#if defined(YYCC_CPPFEAT_VA_OPT)
|
||||
#define RS_PANICF(msg, ...) RS_PANIC(std::format(msg __VA_OPT__(, ) __VA_ARGS__))
|
||||
#else
|
||||
#define RS_PANICF(msg, ...) RS_PANIC(std::format(msg, ##__VA_ARGS__))
|
||||
#endif
|
||||
#endif
|
||||
#define RS_PANICF(msg, ...) RS_PANIC(std::format(msg __VA_OPT__(,) __VA_ARGS__))
|
||||
|
||||
/**
|
||||
* @brief Immediately crashes the entire program like Rust's \c panic! macro.
|
||||
|
@ -1,16 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
// Include YYCC prelude first
|
||||
#include "core.hpp"
|
||||
|
||||
// Rust prelude section
|
||||
#include "../rust/primitive.hpp"
|
||||
#include "../rust/result.hpp"
|
||||
#include "../rust/option.hpp"
|
||||
#include "../rust/panic.hpp"
|
||||
#include "primitive.hpp"
|
||||
#include "result.hpp"
|
||||
#include "option.hpp"
|
||||
#include "panic.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace yycc::prelude::rust {
|
||||
namespace yycc::rust::prelude {
|
||||
// Include primitive types
|
||||
|
||||
#define NS_RUST_PRIMITIVE ::yycc::rust::primitive
|
||||
@ -35,7 +32,7 @@ namespace yycc::prelude::rust {
|
||||
#undef NS_RUST_PRIMITIVE
|
||||
|
||||
// Other types
|
||||
using String = ::yycc::string::u8string;
|
||||
using String = std::u8string;
|
||||
template<typename T>
|
||||
using Vec = std::vector<T>;
|
||||
|
||||
@ -49,4 +46,4 @@ namespace yycc::prelude::rust {
|
||||
} // namespace yycc::prelude::rust
|
||||
|
||||
// Expose all members
|
||||
using namespace yycc::prelude::rust;
|
||||
using namespace ::yycc::rust::prelude;
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user