Compare commits
229 Commits
06e75924f1
...
v2.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 422aa152ff | |||
| 92ce0e29cb | |||
| a0f032c28b | |||
| b51ded2101 | |||
| 09f07d99f7 | |||
| c19561cb54 | |||
| 9ad199073a | |||
| 908d48a7c1 | |||
| aa6dd3031a | |||
| 19df293463 | |||
| 71eb0741f6 | |||
| 09fea7e0a3 | |||
| aecf9bb8cc | |||
| 6449ae1977 | |||
| 1c1e709ed1 | |||
| fe4193efa7 | |||
| 746d20a835 | |||
| 8989e909ad | |||
| 718fe426bf | |||
| 1a4074fd98 | |||
| 74027e7297 | |||
| 044c04aa07 | |||
| e161dafac5 | |||
| 4d9487813b | |||
| 7a34057836 | |||
| 17053f4ebf | |||
| de2b927a14 | |||
| a50233ab6e | |||
| 6dfd957ce9 | |||
| 215a8ce8b8 | |||
| 2b6ac98f27 | |||
| c708e1e672 | |||
| e929ba3776 | |||
| 6dbd031e00 | |||
| 337734d340 | |||
| 45f32297da | |||
| 47bb60f0e4 | |||
| 408ea5ef33 | |||
| cc5e6239ba | |||
| a077604c7d | |||
| 7a2edb92b3 | |||
| 9ce1608be0 | |||
| b8f794a879 | |||
| 622d3e0eb1 | |||
| bac1600558 | |||
| 96e5172d7a | |||
| 5993ae59c0 | |||
| fece224ec5 | |||
| e864b0115e | |||
| 8a604ee813 | |||
| b3ace3d820 | |||
| 75442061e9 | |||
| 194f055039 | |||
| fcd0b3364f | |||
| 8a7387c7ff | |||
| 23c2378ebc | |||
| 9369728759 | |||
| 6c9e23f628 | |||
| f49d974a46 | |||
| 6c2dba74d1 | |||
| 19086f44e2 | |||
| 8cd125a4b9 | |||
| 5ff8f2d8cc | |||
| 772bfbeb15 | |||
| 6b29b7f715 | |||
| 6a97b13f66 | |||
| 79e8af89fe | |||
| d64c6669b4 | |||
| f078dd4399 | |||
| e4387439ee | |||
| a6668dff04 | |||
| e8241e21b9 | |||
| 45cbdc1a2a | |||
| c6d080ad82 | |||
| a1d19cf09c | |||
| d6662dbb53 | |||
| eb9e576d33 | |||
| 8b7ab2c870 | |||
| f76eabee7a | |||
| ab8489c377 | |||
| c48e79753d | |||
| eda801d3c7 | |||
| 64045b1d48 | |||
| 8e0865384d | |||
| c6c450f6fa | |||
| 3dd0c85995 | |||
| 5859264eca | |||
| d69563b5df | |||
| 446f880df4 | |||
| 05a80268ab | |||
| 19d0a5bb4d | |||
| e7a05b3488 | |||
| 82c3ed5b32 | |||
| d6be8a11ac | |||
| 31c624797f | |||
| 6ecf6935d8 | |||
| d6b1d7fd46 | |||
| 8d7eff2a15 | |||
| bd5032cee7 | |||
| cc1ce5bb04 | |||
| 190beeed58 | |||
| 99146ddd55 | |||
| ce3d5b9556 | |||
| c8d763bdcf | |||
| a61955bb09 | |||
| 776adb0c96 | |||
| c85830902b | |||
| 45e4031b5c | |||
| 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 | |||
| e42a3b6e58 | |||
| cec6091996 | |||
| 6e884d865d | |||
| 58ec960e9c | |||
| 732a560a65 | |||
| 3030a67ca3 | |||
| e166dc41ac | |||
| a6382d6a22 | |||
| adc99274f4 | |||
| 3abd0969c0 | |||
| 28ff7008a8 | |||
| ab8d74efe6 | |||
| df3b602110 | |||
| bec36b4b3c | |||
| 0b7e58c8e8 | |||
| 831fa130bc | |||
| 7adac00035 | |||
| 0cd9582757 | |||
| 2206825223 | |||
| 21f7e7f786 | |||
| 50dd086b53 | |||
| c91df3a74f | |||
| 3858b4f3ec | |||
| f3a88e951c | |||
| 59c185a424 | |||
| dc98486fff | |||
| 72a48b703f | |||
| 33cb284eb7 | |||
| e6c24b8b61 | |||
| 6da990876e | |||
| 0ac6b477f9 | |||
| 1cfbcb3b18 | |||
| 598aae69ae | |||
| 656495f22e | |||
| e167479de3 | |||
| 19023cb949 | |||
| 650fcd12ec | |||
| e8a0299fbc | |||
| d1c1743dc9 | |||
| 35318505e4 | |||
| f997990af6 | |||
| 7f373ed354 | |||
| 31a7cb5675 | |||
| 87fa30fe82 | |||
| ecb06504bc | |||
| 805ffe70d6 | |||
| 052fa7f4d1 | |||
| 9e5bd370c4 | |||
| 94386c93aa | |||
| b13bb445e4 | |||
| 81fe72c425 | |||
| b912be082c | |||
| 9f47d0fe24 | |||
| cc689ce8bb | |||
| 1ccea1290e | |||
| ed549592dd | |||
| 1c2007928d | |||
| a6c543c1b5 | |||
| 942e4ff8eb | |||
| 9a18233723 | |||
| 11b2185bb4 | |||
| d27a66e770 | |||
| 4f1e2447d0 | |||
| 3075ec583d | |||
| 2e28dd4c48 | |||
| a1699f13db | |||
| 65b81f5cfa | |||
| 1c5a85bbb2 | |||
| 1f04e23092 |
317
.clang-format
Normal file
317
.clang-format
Normal file
@@ -0,0 +1,317 @@
|
||||
# yaml-language-server: $schema=https://json.schemastore.org/clang-format.json
|
||||
---
|
||||
Language: Cpp
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignArrayOfStructures: None
|
||||
AlignConsecutiveAssignments:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
AlignFunctionPointers: false
|
||||
PadOperators: false
|
||||
AlignConsecutiveBitFields:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
AlignFunctionPointers: false
|
||||
PadOperators: false
|
||||
AlignConsecutiveDeclarations:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
AlignFunctionPointers: false
|
||||
PadOperators: false
|
||||
AlignConsecutiveMacros:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
AlignFunctionPointers: false
|
||||
PadOperators: false
|
||||
AlignConsecutiveShortCaseStatements:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCaseArrows: false
|
||||
AlignCaseColons: false
|
||||
AlignConsecutiveTableGenBreakingDAGArgColons:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
AlignFunctionPointers: false
|
||||
PadOperators: false
|
||||
AlignConsecutiveTableGenCondOperatorColons:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
AlignFunctionPointers: false
|
||||
PadOperators: false
|
||||
AlignConsecutiveTableGenDefinitionColons:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
AlignFunctionPointers: false
|
||||
PadOperators: false
|
||||
AlignEscapedNewlines: DontAlign
|
||||
AlignOperands: Align
|
||||
AlignTrailingComments:
|
||||
Kind: Always
|
||||
OverEmptyLines: 0
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowBreakBeforeNoexceptSpecifier: Never
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
AllowShortCaseExpressionOnASingleLine: true
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortCompoundRequirementOnASingleLine: true
|
||||
AllowShortEnumsOnASingleLine: true
|
||||
AllowShortFunctionsOnASingleLine: Inline
|
||||
AllowShortIfStatementsOnASingleLine: AllIfsAndElse
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AttributeMacros:
|
||||
- __capability
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
BitFieldColonSpacing: Both
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
AfterClass: false
|
||||
AfterControlStatement: Never
|
||||
AfterEnum: false
|
||||
AfterExternBlock: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
BeforeLambdaBody: false
|
||||
BeforeWhile: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: false
|
||||
SplitEmptyRecord: false
|
||||
SplitEmptyNamespace: false
|
||||
BreakAdjacentStringLiterals: true
|
||||
BreakAfterAttributes: Leave
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakAfterReturnType: None
|
||||
BreakArrays: true
|
||||
BreakBeforeBinaryOperators: All
|
||||
BreakBeforeConceptDeclarations: Always
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeInlineASMColon: OnlyMultiline
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializers: AfterColon
|
||||
BreakFunctionDefinitionParameters: false
|
||||
BreakInheritanceList: BeforeColon
|
||||
BreakStringLiterals: false
|
||||
BreakTemplateDeclarations: Yes
|
||||
ColumnLimit: 140
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DerivePointerAlignment: true
|
||||
DisableFormat: false
|
||||
EmptyLineAfterAccessModifier: Never
|
||||
EmptyLineBeforeAccessModifier: LogicalBlock
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- forever
|
||||
- foreach
|
||||
- Q_FOREACH
|
||||
- BOOST_FOREACH
|
||||
IfMacros:
|
||||
- KJ_IF_MAYBE
|
||||
IncludeBlocks: Preserve
|
||||
IncludeCategories:
|
||||
- Regex: '^<Q.*'
|
||||
Priority: 200
|
||||
SortPriority: 200
|
||||
CaseSensitive: true
|
||||
IncludeIsMainRegex: '(Test)?$'
|
||||
IncludeIsMainSourceRegex: ''
|
||||
IndentAccessModifiers: false
|
||||
IndentCaseBlocks: false
|
||||
IndentCaseLabels: true
|
||||
IndentExternBlock: AfterExternBlock
|
||||
IndentGotoLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentRequiresClause: true
|
||||
IndentWidth: 4
|
||||
IndentWrappedFunctionNames: false
|
||||
InsertBraces: false
|
||||
InsertNewlineAtEOF: false
|
||||
InsertTrailingCommas: None
|
||||
IntegerLiteralSeparator:
|
||||
Binary: 0
|
||||
BinaryMinDigits: 0
|
||||
Decimal: 0
|
||||
DecimalMinDigits: 0
|
||||
Hex: 0
|
||||
HexMinDigits: 0
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLines:
|
||||
AtEndOfFile: false
|
||||
AtStartOfBlock: false
|
||||
AtStartOfFile: false
|
||||
LambdaBodyIndentation: Signature
|
||||
LineEnding: DeriveLF
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MainIncludeChar: Quote
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: All
|
||||
ObjCBinPackProtocolList: Auto
|
||||
ObjCBlockIndentWidth: 4
|
||||
ObjCBreakBeforeNestedBlockParam: true
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PackConstructorInitializers: BinPack
|
||||
PenaltyBreakAssignment: 150
|
||||
PenaltyBreakBeforeFirstCallParameter: 300
|
||||
PenaltyBreakComment: 500
|
||||
PenaltyBreakFirstLessLess: 400
|
||||
PenaltyBreakOpenParenthesis: 0
|
||||
PenaltyBreakScopeResolution: 500
|
||||
PenaltyBreakString: 600
|
||||
PenaltyBreakTemplateDeclaration: 10
|
||||
PenaltyExcessCharacter: 50
|
||||
PenaltyIndentedWhitespace: 0
|
||||
PenaltyReturnTypeOnItsOwnLine: 300
|
||||
PointerAlignment: Right
|
||||
PPIndentWidth: -1
|
||||
QualifierAlignment: Leave
|
||||
ReferenceAlignment: Pointer
|
||||
ReflowComments: false
|
||||
RemoveBracesLLVM: false
|
||||
RemoveParentheses: Leave
|
||||
RemoveSemicolon: false
|
||||
RequiresClausePosition: OwnLine
|
||||
RequiresExpressionIndentation: OuterScope
|
||||
SeparateDefinitionBlocks: Leave
|
||||
ShortNamespaceLines: 1
|
||||
SkipMacroDefinitionBody: false
|
||||
SortIncludes: Never
|
||||
SortJavaStaticImport: Before
|
||||
SortUsingDeclarations: Lexicographic
|
||||
SpaceAfterCStyleCast: true
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: false
|
||||
SpaceAroundPointerQualifiers: Default
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCaseColon: false
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeJsonColon: false
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeParensOptions:
|
||||
AfterControlStatements: true
|
||||
AfterForeachMacros: true
|
||||
AfterFunctionDefinitionName: false
|
||||
AfterFunctionDeclarationName: false
|
||||
AfterIfMacros: true
|
||||
AfterOverloadedOperator: false
|
||||
AfterPlacementOperator: true
|
||||
AfterRequiresInClause: false
|
||||
AfterRequiresInExpression: false
|
||||
BeforeNonEmptyParentheses: false
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceBeforeSquareBrackets: false
|
||||
SpaceInEmptyBlock: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: Never
|
||||
SpacesInContainerLiterals: false
|
||||
SpacesInLineCommentPrefix:
|
||||
Minimum: 1
|
||||
Maximum: -1
|
||||
SpacesInParens: Never
|
||||
SpacesInParensOptions:
|
||||
ExceptDoubleParentheses: false
|
||||
InCStyleCasts: false
|
||||
InConditionalStatements: false
|
||||
InEmptyParentheses: false
|
||||
Other: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Auto
|
||||
StatementAttributeLikeMacros:
|
||||
- Q_EMIT
|
||||
- emit
|
||||
StatementMacros:
|
||||
- Q_UNUSED
|
||||
- QT_REQUIRE_VERSION
|
||||
- Q_CLASSINFO
|
||||
- Q_ENUM
|
||||
- Q_ENUM_NS
|
||||
- Q_FLAG
|
||||
- Q_FLAG_NS
|
||||
- Q_GADGET
|
||||
- Q_GADGET_EXPORT
|
||||
- Q_INTERFACES
|
||||
- Q_LOGGING_CATEGORY
|
||||
- Q_MOC_INCLUDE
|
||||
- Q_NAMESPACE
|
||||
- Q_NAMESPACE_EXPORT
|
||||
- Q_OBJECT
|
||||
- Q_PROPERTY
|
||||
- Q_REVISION
|
||||
- Q_DISABLE_COPY
|
||||
- Q_DISABLE_COPY_MOVE
|
||||
- Q_SET_OBJECT_NAME
|
||||
- QT_BEGIN_NAMESPACE
|
||||
- QT_END_NAMESPACE
|
||||
- QML_ADDED_IN_MINOR_VERSION
|
||||
- QML_ANONYMOUS
|
||||
- QML_ATTACHED
|
||||
- QML_DECLARE_TYPE
|
||||
- QML_DECLARE_TYPEINFO
|
||||
- QML_ELEMENT
|
||||
- QML_EXTENDED
|
||||
- QML_EXTENDED_NAMESPACE
|
||||
- QML_EXTRA_VERSION
|
||||
- QML_FOREIGN
|
||||
- QML_FOREIGN_NAMESPACE
|
||||
- QML_IMPLEMENTS_INTERFACES
|
||||
- QML_INTERFACE
|
||||
- QML_NAMED_ELEMENT
|
||||
- QML_REMOVED_IN_MINOR_VERSION
|
||||
- QML_SINGLETON
|
||||
- QML_UNAVAILABLE
|
||||
- QML_UNCREATABLE
|
||||
- QML_VALUE_TYPE
|
||||
- YYCC_DELETE_COPY
|
||||
- YYCC_DELETE_MOVE
|
||||
- YYCC_DELETE_COPY_MOVE
|
||||
- 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
|
||||
VerilogBreakBetweenInstancePorts: true
|
||||
WhitespaceSensitiveMacros:
|
||||
- BOOST_PP_STRINGIZE
|
||||
- CF_SWIFT_NAME
|
||||
- NS_SWIFT_NAME
|
||||
- PP_STRINGIZE
|
||||
- STRINGIZE
|
||||
...
|
||||
|
||||
3
.editorconfig
Normal file
3
.editorconfig
Normal file
@@ -0,0 +1,3 @@
|
||||
[*.{cpp,hpp}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
3
.gitattributes
vendored
3
.gitattributes
vendored
@@ -1,2 +1,3 @@
|
||||
Doxyfile.in eol=lf
|
||||
*.bat eol=crlf
|
||||
*.bat eol=crlf
|
||||
*.sh eol=lf
|
||||
4
.github/scripts/README.md
vendored
Normal file
4
.github/scripts/README.md
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
# GitHub Action Scripts
|
||||
|
||||
These script files are only used for GitHub Action.
|
||||
These script files should only be executed in their root directory respectively.
|
||||
17
.github/scripts/gbenchmark/linux.sh
vendored
Normal file
17
.github/scripts/gbenchmark/linux.sh
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# Create build and install directory
|
||||
mkdir build install
|
||||
|
||||
# Build project
|
||||
cd build
|
||||
cmake -DCMAKE_CXX_STANDARD=23 -DBENCHMARK_ENABLE_TESTING=OFF -DCMAKE_BUILD_TYPE=Release ..
|
||||
cmake --build .
|
||||
cmake --install . --prefix=../install
|
||||
cd ..
|
||||
|
||||
# Record install directory
|
||||
cd install
|
||||
export benchmark_ROOT=$(pwd)
|
||||
cd ..
|
||||
17
.github/scripts/gbenchmark/macos.sh
vendored
Normal file
17
.github/scripts/gbenchmark/macos.sh
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# Create build and install directory
|
||||
mkdir build install
|
||||
|
||||
# Build project
|
||||
cd build
|
||||
cmake -DCMAKE_CXX_STANDARD=23 -DBENCHMARK_ENABLE_TESTING=OFF -DCMAKE_BUILD_TYPE=Release ..
|
||||
cmake --build .
|
||||
cmake --install . --prefix=../install
|
||||
cd ..
|
||||
|
||||
# Record install directory
|
||||
cd install
|
||||
export benchmark_ROOT=$(pwd)
|
||||
cd ..
|
||||
17
.github/scripts/gbenchmark/windows.bat
vendored
Normal file
17
.github/scripts/gbenchmark/windows.bat
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
@ECHO OFF
|
||||
|
||||
:: Create build and install directory
|
||||
MKDIR build
|
||||
MKDIR install
|
||||
|
||||
:: Build project
|
||||
CD build
|
||||
cmake -A x64 -DCMAKE_CXX_STANDARD=23 -DBENCHMARK_ENABLE_TESTING=OFF ..
|
||||
cmake --build . --config Release
|
||||
cmake --install . --prefix=../install --config Release
|
||||
CD ..
|
||||
|
||||
:: Record install directory
|
||||
CD install
|
||||
SET benchmark_ROOT=%CD%
|
||||
CD ..
|
||||
17
.github/scripts/gtest/linux.sh
vendored
Normal file
17
.github/scripts/gtest/linux.sh
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# Create build and install directory
|
||||
mkdir build install
|
||||
|
||||
# Build project
|
||||
cd build
|
||||
cmake -DCMAKE_CXX_STANDARD=23 -Dgtest_force_shared_crt=ON -DCMAKE_BUILD_TYPE=Release ..
|
||||
cmake --build .
|
||||
cmake --install . --prefix=../install
|
||||
cd ..
|
||||
|
||||
# Record install directory
|
||||
cd install
|
||||
export GTest_ROOT=$(pwd)
|
||||
cd ..
|
||||
17
.github/scripts/gtest/macos.sh
vendored
Normal file
17
.github/scripts/gtest/macos.sh
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# Create build and install directory
|
||||
mkdir build install
|
||||
|
||||
# Build project
|
||||
cd build
|
||||
cmake -DCMAKE_CXX_STANDARD=23 -Dgtest_force_shared_crt=ON -DCMAKE_BUILD_TYPE=Release ..
|
||||
cmake --build .
|
||||
cmake --install . --prefix=../install
|
||||
cd ..
|
||||
|
||||
# Record install directory
|
||||
cd install
|
||||
export GTest_ROOT=$(pwd)
|
||||
cd ..
|
||||
17
.github/scripts/gtest/windows.bat
vendored
Normal file
17
.github/scripts/gtest/windows.bat
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
@ECHO OFF
|
||||
|
||||
:: Create build and install directory
|
||||
MKDIR build
|
||||
MKDIR install
|
||||
|
||||
:: Build project
|
||||
CD build
|
||||
cmake -A x64 -DCMAKE_CXX_STANDARD=23 -Dgtest_force_shared_crt=ON ..
|
||||
cmake --build . --config Release
|
||||
cmake --install . --prefix=../install --config Release
|
||||
CD ..
|
||||
|
||||
:: Record install directory
|
||||
CD install
|
||||
SET GTest_ROOT=%CD%
|
||||
CD ..
|
||||
19
.github/scripts/linux.sh
vendored
Normal file
19
.github/scripts/linux.sh
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# Create build directory and enter it
|
||||
mkdir bin
|
||||
cd bin
|
||||
# Create internal build and install directory, then enter it
|
||||
mkdir build
|
||||
mkdir install
|
||||
|
||||
# Build in Release mode
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DYYCC_BUILD_TEST=ON -DGTest_ROOT=$GTest_ROOT -DYYCC_BUILD_BENCHMARK=ON -Dbenchmark_ROOT=$benchmark_ROOT ../..
|
||||
cmake --build .
|
||||
cmake --install . --prefix=../install
|
||||
cd ..
|
||||
|
||||
# Back to root directory
|
||||
cd ..
|
||||
19
.github/scripts/macos.sh
vendored
Normal file
19
.github/scripts/macos.sh
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# Create build directory and enter it
|
||||
mkdir bin
|
||||
cd bin
|
||||
# Create internal build and install directory, then enter it
|
||||
mkdir build
|
||||
mkdir install
|
||||
|
||||
# Build in Release mode
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DYYCC_BUILD_TEST=ON -DGTest_ROOT=$GTest_ROOT -DYYCC_BUILD_BENCHMARK=ON -Dbenchmark_ROOT=$benchmark_ROOT ../..
|
||||
cmake --build .
|
||||
cmake --install . --prefix=../install
|
||||
cd ..
|
||||
|
||||
# Back to root directory
|
||||
cd ..
|
||||
18
.github/scripts/windows.bat
vendored
Normal file
18
.github/scripts/windows.bat
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
@ECHO OFF
|
||||
|
||||
:: Create build directory and enter it
|
||||
MKDIR bin
|
||||
CD bin
|
||||
:: Create internal build and install directory, then enter it
|
||||
MKDIR build
|
||||
MKDIR install
|
||||
|
||||
:: Build with x64 architecture in Release mode
|
||||
CD build
|
||||
cmake -A x64 -DYYCC_BUILD_TEST=ON -DGTest_ROOT=%GTest_ROOT% -DYYCC_BUILD_BENCHMARK=ON -Dbenchmark_ROOT=%benchmark_ROOT% ../..
|
||||
cmake --build . --config Release
|
||||
cmake --install . --prefix=../install --config Release
|
||||
CD ..
|
||||
|
||||
:: Back to root directory
|
||||
CD ..
|
||||
66
.github/workflows/linux.yml
vendored
Normal file
66
.github/workflows/linux.yml
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
name: YYCC Linux Build
|
||||
|
||||
on: [workflow_dispatch]
|
||||
|
||||
jobs:
|
||||
linux-build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y build-essential cmake git
|
||||
- name: Fetch Google Test
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'google/googletest'
|
||||
ref: 'v1.17.0'
|
||||
path: 'extern/googletest'
|
||||
- name: Build Google Test
|
||||
shell: bash
|
||||
run: |
|
||||
cd extern/googletest
|
||||
# Build Google Test
|
||||
source ../../.github/scripts/gtest/linux.sh
|
||||
# Record environment variable
|
||||
echo "GTest_ROOT=$GTest_ROOT" >> "$GITHUB_ENV"
|
||||
cd ../..
|
||||
- name: Fetch Google Benchmark
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'google/benchmark'
|
||||
ref: 'v1.9.4'
|
||||
path: 'extern/benchmark'
|
||||
- name: Build Google Benchmark
|
||||
shell: bash
|
||||
run: |
|
||||
cd extern/benchmark
|
||||
# Create symlink to googletest as required by benchmark
|
||||
ln -s ../googletest googletest
|
||||
# Build Google Benchmark
|
||||
source ../../.github/scripts/gbenchmark/linux.sh
|
||||
# Record environment variable
|
||||
echo "benchmark_ROOT=$benchmark_ROOT" >> "$GITHUB_ENV"
|
||||
cd ../..
|
||||
- name: Build YYCC
|
||||
shell: bash
|
||||
run: |
|
||||
source ./.github/scripts/linux.sh
|
||||
- name: Run YYCC Test
|
||||
shell: bash
|
||||
run: |
|
||||
./bin/install/bin/YYCCTest
|
||||
- name: Run YYCC Benchmark
|
||||
shell: bash
|
||||
run: |
|
||||
./bin/install/bin/YYCCBenchmark
|
||||
- name: Upload Built Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: YYCC-linux-build
|
||||
path: bin/install/*
|
||||
retention-days: 30
|
||||
61
.github/workflows/macos.yml
vendored
Normal file
61
.github/workflows/macos.yml
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
name: YYCC macOS Build
|
||||
|
||||
on: [workflow_dispatch]
|
||||
|
||||
jobs:
|
||||
macos-build:
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Fetch Google Test
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'google/googletest'
|
||||
ref: 'v1.17.0'
|
||||
path: 'extern/googletest'
|
||||
- name: Build Google Test
|
||||
shell: bash
|
||||
run: |
|
||||
cd extern/googletest
|
||||
# Build Google Test
|
||||
source ../../.github/scripts/gtest/macos.sh
|
||||
# Record environment variable
|
||||
echo "GTest_ROOT=$GTest_ROOT" >> "$GITHUB_ENV"
|
||||
cd ../..
|
||||
- name: Fetch Google Benchmark
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'google/benchmark'
|
||||
ref: 'v1.9.4'
|
||||
path: 'extern/benchmark'
|
||||
- name: Build Google Benchmark
|
||||
shell: bash
|
||||
run: |
|
||||
cd extern/benchmark
|
||||
# Create symlink to googletest as required by benchmark
|
||||
ln -s ../googletest googletest
|
||||
# Build Google Benchmark
|
||||
source ../../.github/scripts/gbenchmark/macos.sh
|
||||
# Record environment variable
|
||||
echo "benchmark_ROOT=$benchmark_ROOT" >> "$GITHUB_ENV"
|
||||
cd ../..
|
||||
- name: Build YYCC
|
||||
shell: bash
|
||||
run: |
|
||||
source ./.github/scripts/macos.sh
|
||||
- name: Run YYCC Test
|
||||
shell: bash
|
||||
run: |
|
||||
./bin/install/bin/YYCCTest
|
||||
- name: Run YYCC Benchmark
|
||||
shell: bash
|
||||
run: |
|
||||
./bin/install/bin/YYCCBenchmark
|
||||
- name: Upload Built Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: YYCC-macos-build
|
||||
path: bin/install/*
|
||||
retention-days: 30
|
||||
35
.github/workflows/nightly.yml.disabled
vendored
35
.github/workflows/nightly.yml.disabled
vendored
@@ -1,35 +0,0 @@
|
||||
name: YYCC Nightly Build
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
msvc-build:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
vs: ['2019']
|
||||
msvc_arch: ['x86']
|
||||
|
||||
runs-on: windows-2019
|
||||
|
||||
steps:
|
||||
- name: Fetching Repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Building YYCC
|
||||
shell: cmd
|
||||
run: |
|
||||
set VS=${{ matrix.vs }}
|
||||
set VCVARS="C:\Program Files (x86)\Microsoft Visual Studio\%VS%\Enterprise\VC\Auxiliary\Build\vcvarsall.bat"
|
||||
if not exist %VCVARS% set VCVARS="C:\Program Files\Microsoft Visual Studio\%VS%\Enterprise\VC\Auxiliary\Build\vcvarsall.bat"
|
||||
call %VCVARS% ${{ matrix.msvc_arch }}
|
||||
.\script\build.bat
|
||||
- name: Uploading Nightly Build
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: YYCC-windows-nightly
|
||||
path: bin/install/*
|
||||
retention-days: 30
|
||||
78
.github/workflows/windows.yml
vendored
Normal file
78
.github/workflows/windows.yml
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
name: YYCC Windows Build
|
||||
|
||||
on: [workflow_dispatch]
|
||||
|
||||
jobs:
|
||||
windows-build:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- vs: '2022'
|
||||
msvc_arch: 'x64'
|
||||
|
||||
runs-on: windows-2022
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Fetch Google Test
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'google/googletest'
|
||||
ref: 'v1.17.0'
|
||||
path: 'extern/googletest'
|
||||
- name: Build Google Test
|
||||
shell: cmd
|
||||
run: |
|
||||
CD extern\googletest
|
||||
:: Build Google Test
|
||||
CALL ..\..\.github\scripts\gtest\windows.bat
|
||||
:: Idk why I can't use $GITHUB_ENV, so I use this stupid way to do this.
|
||||
:: This is first entry so we override it.
|
||||
ECHO SET GTest_ROOT=%GTest_ROOT% > ..\envs.bat
|
||||
CD ..\..
|
||||
- name: Fetch Google Benchmark
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'google/benchmark'
|
||||
ref: 'v1.9.4'
|
||||
path: 'extern/benchmark'
|
||||
- name: Build Google Benchmark
|
||||
shell: cmd
|
||||
run: |
|
||||
CD extern\benchmark
|
||||
:: Create symlink to googletest as required by benchmark
|
||||
mklink /D googletest ..\googletest
|
||||
:: Build Google Benchmark
|
||||
CALL ..\..\.github\scripts\gbenchmark\windows.bat
|
||||
:: This is second entry so we append it.
|
||||
ECHO SET benchmark_ROOT=%benchmark_ROOT% >> ..\envs.bat
|
||||
CD ..\..
|
||||
- name: Build YYCC
|
||||
shell: cmd
|
||||
run: |
|
||||
:: Prepare Visual Studio
|
||||
set VS=${{ matrix.vs }}
|
||||
set VCVARS="C:\Program Files (x86)\Microsoft Visual Studio\%VS%\Enterprise\VC\Auxiliary\Build\vcvarsall.bat"
|
||||
if not exist %VCVARS% set VCVARS="C:\Program Files\Microsoft Visual Studio\%VS%\Enterprise\VC\Auxiliary\Build\vcvarsall.bat"
|
||||
call %VCVARS% ${{ matrix.msvc_arch }}
|
||||
:: Extract saved environment variables
|
||||
CALL .\extern\envs.bat
|
||||
:: Build Project
|
||||
CALL .\.github\scripts\windows.bat
|
||||
- name: Run YYCC Test
|
||||
shell: cmd
|
||||
run: |
|
||||
.\bin\install\bin\YYCCTest.exe
|
||||
- name: Run YYCC Benchmark
|
||||
shell: cmd
|
||||
run: |
|
||||
.\bin\install\bin\YYCCBenchmark.exe
|
||||
- name: Upload Built Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: YYCC-windows-build
|
||||
path: bin/install/*
|
||||
retention-days: 30
|
||||
|
||||
15
.gitignore
vendored
15
.gitignore
vendored
@@ -1,11 +1,18 @@
|
||||
# -------------------- Output --------------------
|
||||
## ===== Personal =====
|
||||
# Ignore build resources
|
||||
out/
|
||||
build/
|
||||
install/
|
||||
extern/
|
||||
|
||||
# Ignore CMake generated stuff
|
||||
src/yycc/version.hpp
|
||||
CMakeSettings.json
|
||||
|
||||
# -------------------- VSCode --------------------
|
||||
## ===== VSCode =====
|
||||
.vscode/
|
||||
|
||||
# -------------------- CMake --------------------
|
||||
## ===== CMake =====
|
||||
CMakeLists.txt.user
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
@@ -18,7 +25,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.
|
||||
##
|
||||
|
||||
@@ -1,40 +1,72 @@
|
||||
cmake_minimum_required(VERSION 3.23)
|
||||
project(YYCC
|
||||
VERSION 1.0.0
|
||||
VERSION 2.0.0
|
||||
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_TEST "Build test of YYCCommonplace." OFF)
|
||||
option(YYCC_BUILD_BENCHMARK "Build benchmark of YYCCommonplace." OFF)
|
||||
option(YYCC_BUILD_DOC "Build document of YYCCommonplace." OFF)
|
||||
option(YYCC_ENFORCE_ICONV "Enforce iconv support for this library (e.g. in MSYS2 environment)." OFF)
|
||||
|
||||
# Detect MSVC IDE environment.
|
||||
# If we in it, we should add configuration and build type in install path.
|
||||
if (CMAKE_GENERATOR MATCHES "Visual Studio")
|
||||
# Do Visual Studio specific
|
||||
set(YYCC_INSTALL_PATH_LIB lib/${CMAKE_VS_PLATFORM_NAME}/$<CONFIG>)
|
||||
set(YYCC_INSTALL_PATH_BIN bin/${CMAKE_VS_PLATFORM_NAME})
|
||||
else()
|
||||
# Other stuff
|
||||
set(YYCC_INSTALL_PATH_LIB lib)
|
||||
set(YYCC_INSTALL_PATH_BIN bin)
|
||||
endif()
|
||||
# Setup install path from CMake provided install path for convenient use.
|
||||
include(GNUInstallDirs)
|
||||
set(YYCC_INSTALL_INCLUDE_PATH ${CMAKE_INSTALL_INCLUDEDIR} CACHE PATH
|
||||
"Public header install path relative to CMAKE_INSTALL_PREFIX unless set to an absolute path.")
|
||||
set(YYCC_INSTALL_LIB_PATH ${CMAKE_INSTALL_LIBDIR} CACHE PATH
|
||||
"Library install path relative to CMAKE_INSTALL_PREFIX unless set to an absolute path.")
|
||||
set(YYCC_INSTALL_BIN_PATH ${CMAKE_INSTALL_BINDIR} CACHE PATH
|
||||
"Binary install path relative to CMAKE_INSTALL_PREFIX unless set to an absolute path.")
|
||||
set(YYCC_INSTALL_DOC_PATH ${CMAKE_INSTALL_DOCDIR} CACHE PATH
|
||||
"Non-arch doc install path relative to CMAKE_INSTALL_PREFIX unless set to an absolute path.")
|
||||
|
||||
# Import 2 build targets
|
||||
# Test charconv support due to shitty clang's libcxx.
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/cmake/check_charconv.cmake)
|
||||
|
||||
# Include dependency.
|
||||
# GTest is required if we build test
|
||||
if (YYCC_BUILD_TEST)
|
||||
# For Windows: Prevent overriding the parent project's compiler/linker settings
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||
find_package(GTest REQUIRED)
|
||||
endif ()
|
||||
# Google Benchmark is required if we build benchmark
|
||||
if (YYCC_BUILD_BENCHMARK)
|
||||
find_package(benchmark REQUIRED)
|
||||
endif ()
|
||||
# Doxygen is required if we build doc
|
||||
if (YYCC_BUILD_DOC)
|
||||
find_package(Doxygen REQUIRED)
|
||||
endif ()
|
||||
# Iconv is required if we are not in Windows or user request it
|
||||
if (YYCC_ENFORCE_ICONV OR (NOT WIN32))
|
||||
find_package(Iconv REQUIRED)
|
||||
endif ()
|
||||
|
||||
# Import 4 build targets
|
||||
add_subdirectory(src)
|
||||
if (YYCC_BUILD_TESTBENCH)
|
||||
add_subdirectory(testbench)
|
||||
if (YYCC_BUILD_TEST)
|
||||
add_subdirectory(test)
|
||||
endif ()
|
||||
if (YYCC_BUILD_BENCHMARK)
|
||||
add_subdirectory(benchmark)
|
||||
endif ()
|
||||
if (YYCC_BUILD_DOC)
|
||||
add_subdirectory(doc)
|
||||
endif ()
|
||||
|
||||
# Install project package infos
|
||||
# Package target
|
||||
# Install target and package
|
||||
# Install target
|
||||
install(EXPORT YYCCommonplaceTargets
|
||||
FILE YYCCommonplaceTargets.cmake
|
||||
NAMESPACE YYCC::
|
||||
DESTINATION lib/cmake/YYCCommonplace
|
||||
DESTINATION ${YYCC_INSTALL_LIB_PATH}/cmake/YYCCommonplace
|
||||
)
|
||||
# Package configuration file
|
||||
include(CMakePackageConfigHelpers)
|
||||
@@ -46,14 +78,14 @@ write_basic_package_version_file(
|
||||
configure_package_config_file(
|
||||
${CMAKE_CURRENT_LIST_DIR}/cmake/YYCCommonplaceConfig.cmake.in
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/YYCCommonplaceConfig.cmake"
|
||||
INSTALL_DESTINATION lib/cmake/YYCCommonplace
|
||||
INSTALL_DESTINATION ${YYCC_INSTALL_LIB_PATH}/cmake/YYCCommonplace
|
||||
)
|
||||
# Copy to install destination
|
||||
# Copy package files to install destination
|
||||
install(
|
||||
FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/YYCCommonplaceConfig.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/YYCCommonplaceConfigVersion.cmake"
|
||||
DESTINATION
|
||||
lib/cmake/YYCCommonplace
|
||||
${YYCC_INSTALL_LIB_PATH}/cmake/YYCCommonplace
|
||||
)
|
||||
|
||||
|
||||
185
COMPILE.md
Normal file
185
COMPILE.md
Normal file
@@ -0,0 +1,185 @@
|
||||
# Compile Manual
|
||||
|
||||
## Choose Version
|
||||
|
||||
This manual is only suit for the version equal or newer than YYCC 2.0.
|
||||
For old version, please checkout to corresponding tag and browse how to build them.
|
||||
|
||||
We suggest that you only use stable version (tagged commit).
|
||||
The latest commit always present current works.
|
||||
It means that it is not stable and work in progress.
|
||||
|
||||
## Requirements
|
||||
|
||||
* CMake 3.23 at least.
|
||||
* The common compiler supporting C++ 23 (GCC / Clang / MSVC).
|
||||
* Iconv (Optional on Windows. Required on other systems).
|
||||
* [Google Test](https://github.com/google/googletest) (Required if you build test).
|
||||
* [Google Benchmark](https://github.com/google/benchmark) (Required if you build benchmark).
|
||||
* Doxygen (Required if you build documentation).
|
||||
|
||||
If you are just want to build this project to make something works, or build other project, rather than code with it,
|
||||
you commonly do not need build test, benchmark and documentation.
|
||||
So you actually do not need Google Test, Google Benchmark and Doxygen.
|
||||
|
||||
## Preparing
|
||||
|
||||
### Compiler
|
||||
|
||||
> [!WARNING]
|
||||
> You may face some issues when building on macOS with Apple Clang. That's not your fault.
|
||||
> Clang and Apple Clang used libc++ library lacks some essential features used by this project.
|
||||
> This is especially not good for Apple Clang because Apple Clang is usually behind Clang a bunch of versions.
|
||||
>
|
||||
> For resolving this issue, I have written a series of patch header files for libcxx and you can find them in include directory.
|
||||
> This project should be compiled on macOS but everything has exception.
|
||||
> If you really have this issue, a possible solution is that use GCC and libstdc++ on macOS instead of default Clang and libc++.
|
||||
>
|
||||
> Build issue may be resolved until libc++ finish these features: complete `std::from_chars` and `std::to_chars`,
|
||||
> `std::stacktrace` and `std::views::enumerate`.
|
||||
|
||||
### Google Test
|
||||
|
||||
Google Test is required if you need to build test.
|
||||
If you don't need this please skip this chapter.
|
||||
|
||||
We use Google Test 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 Google Test, 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 Google Test 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 Google Test manually.
|
||||
|
||||
1. Download Google Test 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 Google Test
|
||||
1. Use CMake to install Google Test into previous we created `install` directory.
|
||||
|
||||
### Google Benchmark
|
||||
|
||||
Google Benchmark is required if you need to build benchmark.
|
||||
If you don't need this please skip this chapter.
|
||||
|
||||
We use Google Benchmark v1.9.4.
|
||||
It would be okey use other versions but I have not test on them.
|
||||
|
||||
There are the steps instructing you how to compile Google Benchmark manually.
|
||||
|
||||
1. Download Google Benchmark source code with given version in GitHub Release page.
|
||||
1. Extract it into a directory.
|
||||
1. Enter this directory and create link named `googletest` to previous fetched Google Test root directory. This is instructed by official manual because Google Benchmark rely on Google Test. Link can be create by executing `mklink /D googletest <path-to-googletest-root-dir>` on Windows or `ln -s <path-to-googletest-root-dir> googletest` on POSIX-like OS.
|
||||
1. Keep stay in this directory and create 2 subdirectory `build` and `install` for CMake build and install respectively.
|
||||
1. Enter `build` directory and configure CMake with extra `-DCMAKE_CXX_STANDARD=23 -DBENCHMARK_ENABLE_TESTING=OFF` parameters.
|
||||
1. Use CMake to build Google Benchmark
|
||||
1. Use CMake to install Google Benchmark into previous we created `install` directory.
|
||||
|
||||
### Iconv
|
||||
|
||||
Iconv is optional on Windows and disabled in default.
|
||||
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 works, or build other projects), please choose "User Build".
|
||||
If you are a developer (developer of this project, or use this project as dependency to develop your project), please choose "Developer Build".
|
||||
|
||||
### User Build
|
||||
|
||||
"User Build" is basically how GitHub Action build this project.
|
||||
|
||||
Under **the root directory** of this project, execute:
|
||||
|
||||
- `script/windows_build.bat` on Windows
|
||||
- or `script/linux_build.sh` on Linux
|
||||
- or `script/macos_build.sh` on macOS
|
||||
|
||||
The final built artifact is under `bin/install` directory.
|
||||
|
||||
### Developer Build
|
||||
|
||||
#### Configurable Variables
|
||||
|
||||
First, there is a list listing all variables you may configure during compiling.
|
||||
|
||||
* `YYCC_BUILD_TEST`: Set it to `ON` to build test. `OFF` in default.
|
||||
It is useful for the developer of this project.
|
||||
It also suit for the user who has runtime issues on their platforms to check whether this project works as expected.
|
||||
If you are debugging this project to find bug, I suggest that you build this project under Debug mode and use this test project for debugging.
|
||||
* `YYCC_BUILD_BENCHMARK`: Set it to `ON` to build benchmark. `OFF` in default.
|
||||
It is useful for the developer of this project to checking the performace for those homemade functions.
|
||||
It is highly suggested build this project with Release mode to have real benchmark result.
|
||||
* `YYCC_BUILD_DOC`: Set it to `ON` to build documentation. `OFF` in default.
|
||||
It may be useful for the developer who firstly use this project in their own projects.
|
||||
Please note that generated documentation is different in different platforms.
|
||||
* `YYCC_ENFORCE_ICONV`: Set it to `ON` to enable Iconv feature forcely. `OFF` in default.
|
||||
The usage of this option has been introduced in previous "Iconv" chapter.
|
||||
* `GTest_ROOT`: Set to the install path of Google Test
|
||||
if you have enable `YYCC_BUILD_TEST` and want to use your personal built Google Test.
|
||||
* `benchmark_ROOT`: Set to the install path of Google Benchmark
|
||||
if you have enable `YYCC_BUILD_BENCHMARK` and want to use your personal built Google Benchmark.
|
||||
* `Iconv_ROOT`: The assistant variable for finding Iconv which is exposed by CMake.
|
||||
You usually do not need set it up.
|
||||
* `CMAKE_CXX_STANDARD`: Set C++ standard version of project.
|
||||
`23` in default and this version can not be lower than C++23.
|
||||
You usually do not need change this.
|
||||
* `CMAKE_POSITION_INDEPENDENT_CODE`: Set it to `True` to enable PIC.
|
||||
This is essential for those project which use this project and produce dynamicing library as final artifact.
|
||||
|
||||
#### Configure CMake
|
||||
|
||||
When configure CMake, you may use different options on different platforms.
|
||||
Following list may help you.
|
||||
|
||||
- On Windows:
|
||||
* `-A Win32` or `-A x64` to specify architecture.
|
||||
- On Linux or other POSIX systems:
|
||||
* `-DCMAKE_BUILD_TYPE=Debug` or `-DCMAKE_BUILD_TYPE=Release` to specify build type.
|
||||
|
||||
Additionally, you can attach any variables introduced above with `-D` option during CMake configurations.
|
||||
|
||||
#### Build with CMake
|
||||
|
||||
After configuration, you can use `cmake --build .` to build project,
|
||||
with additional options on different platforms.
|
||||
Following list may help you.
|
||||
|
||||
- On Windows:
|
||||
* `--config Debug` or `--config Release` to specify build type.
|
||||
- On Linux or other POSIX systems:
|
||||
* None
|
||||
|
||||
#### Install with CMake
|
||||
|
||||
After building, you can use `cmake --install . --prefix <path-to-prefix>`
|
||||
to install project into given path, with additional options on different platforms.
|
||||
Following list may help you.
|
||||
|
||||
- On Windows:
|
||||
* `--config Debug` or `--config Release` to specify build type.
|
||||
- On Linux or other POSIX systems:
|
||||
* None
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2024-2026 yyc12345
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
11
README.md
11
README.md
@@ -1,9 +1,12 @@
|
||||
# YYC Commonplace
|
||||
|
||||
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, After this, I do not need to write these duplicated code in each project. 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.
|
||||
YYC Commonplace, or YYCCommonplace (abbr. YYCC) is a static library specifically resolving my requirements in C++ and Windows scope.
|
||||
|
||||
This project mainly is served for my personal use. But I would be honored if you would like to use this in your project. Almost of my projects, except some critical projects (they will copy this project implementations into their own project scope to eliminate non-common library dependency), will gradually adapt to this project and drop their own individual implementations.
|
||||
## Usage
|
||||
|
||||
This project includes Visual Studio project file and CMake support at the same time. So that at least I can use one of them freely.
|
||||
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.
|
||||
|
||||
**WIP. Do not use it now.**
|
||||
## Build
|
||||
|
||||
See [Compile Manual](./COMPILE.md).
|
||||
|
||||
212
asset/.gitignore
vendored
Normal file
212
asset/.gitignore
vendored
Normal file
@@ -0,0 +1,212 @@
|
||||
## ===== Myself =====
|
||||
# Exclude VSCode
|
||||
.vscode/
|
||||
|
||||
## ===== 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__/
|
||||
2
asset/pycodec/.gitignore
vendored
Normal file
2
asset/pycodec/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Exclude result
|
||||
*.cpp
|
||||
7
asset/pycodec/README.md
Normal file
7
asset/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
asset/pycodec/conv_encoding_table.py
Normal file
54
asset/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
asset/pycodec/encoding_table.cpp.jinja
Normal file
23
asset/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
asset/pycodec/encoding_table.csv
Normal file
97
asset/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
asset/pyproject.toml
Normal file
7
asset/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
asset/uv.lock
generated
Normal file
74
asset/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" }]
|
||||
33
benchmark/CMakeLists.txt
Normal file
33
benchmark/CMakeLists.txt
Normal file
@@ -0,0 +1,33 @@
|
||||
# Create executable benchmark
|
||||
add_executable(YYCCBenchmark "")
|
||||
# Setup test sources
|
||||
target_sources(YYCCBenchmark
|
||||
PRIVATE
|
||||
main.cpp
|
||||
|
||||
yycc/string/op.cpp
|
||||
|
||||
yycc/carton/fft.cpp
|
||||
)
|
||||
# target_sources(YYCCBenchmark
|
||||
# PRIVATE
|
||||
# FILE_SET HEADERS
|
||||
# FILES
|
||||
# shared/literals.hpp
|
||||
# )
|
||||
# Setup headers
|
||||
target_include_directories(YYCCBenchmark
|
||||
PUBLIC
|
||||
"${CMAKE_CURRENT_LIST_DIR}"
|
||||
)
|
||||
# Setup libraries
|
||||
target_link_libraries(YYCCBenchmark
|
||||
PRIVATE
|
||||
YYCCommonplace
|
||||
benchmark::benchmark
|
||||
)
|
||||
|
||||
# Install binary
|
||||
install(TARGETS YYCCBenchmark
|
||||
RUNTIME DESTINATION ${YYCC_INSTALL_BIN_PATH}
|
||||
)
|
||||
3
benchmark/main.cpp
Normal file
3
benchmark/main.cpp
Normal file
@@ -0,0 +1,3 @@
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
BENCHMARK_MAIN();
|
||||
40
benchmark/yycc/carton/fft.cpp
Normal file
40
benchmark/yycc/carton/fft.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/carton/fft.hpp>
|
||||
#include <random>
|
||||
#include <chrono>
|
||||
|
||||
#define FFT ::yycc::carton::fft
|
||||
|
||||
namespace yyccbench::carton::fft {
|
||||
|
||||
using TIndex = size_t;
|
||||
using TFloat = float;
|
||||
using TComplex = std::complex<TFloat>;
|
||||
template<TIndex N>
|
||||
using TFft = FFT::Fft<TIndex, TFloat, N>;
|
||||
|
||||
constexpr TIndex FFT_POINTS = 1024u;
|
||||
|
||||
static void BM_FftCompute(benchmark::State& state) {
|
||||
// prepare random buffer
|
||||
constexpr TIndex RND_BUF_CNT = 8u;
|
||||
std::random_device rnd_device;
|
||||
std::default_random_engine rnd_engine(rnd_device());
|
||||
std::uniform_real_distribution<TFloat> rnd_dist(0.0f, 1.0f);
|
||||
std::vector<std::vector<TComplex>> buffer_collection(RND_BUF_CNT);
|
||||
for (auto& buf : buffer_collection) {
|
||||
buf.resize(FFT_POINTS);
|
||||
std::generate(buf.begin(), buf.end(), [&rnd_engine, &rnd_dist]() mutable -> TComplex { return TComplex(rnd_dist(rnd_engine)); });
|
||||
}
|
||||
|
||||
// prepare FFT engine
|
||||
TFft<FFT_POINTS> fft;
|
||||
// do benchmark
|
||||
for (auto _ : state) {
|
||||
fft.compute(buffer_collection[state.iterations() % RND_BUF_CNT].data());
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_FftCompute)->Name("FftCompute");
|
||||
|
||||
}
|
||||
28
benchmark/yycc/string/op.cpp
Normal file
28
benchmark/yycc/string/op.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/string/op.hpp>
|
||||
|
||||
#define OP ::yycc::string::op
|
||||
using namespace std::literals::string_view_literals;
|
||||
|
||||
namespace yyccbench::string::op {
|
||||
|
||||
static void BM_StringStrip(benchmark::State& state) {
|
||||
std::u8string_view strl = u8" \thello\r\n"sv, words = u8" \t\r\n"sv;
|
||||
for (auto _ : state) {
|
||||
auto rv = OP::strip(strl, words);
|
||||
benchmark::DoNotOptimize(rv);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_StringStrip)->Name("StringStrip");
|
||||
|
||||
static void BM_StringTrim(benchmark::State& state) {
|
||||
std::u8string_view strl = u8" \thello\r\n"sv, words = u8" \t\r\n"sv;
|
||||
for (auto _ : state) {
|
||||
auto rv = OP::trim(strl, words);
|
||||
benchmark::DoNotOptimize(rv);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_StringTrim)->Name("StringTrim");
|
||||
|
||||
}
|
||||
@@ -1,2 +1,12 @@
|
||||
# Add the targets file
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/YYCCommonplaceTargets.cmake")
|
||||
|
||||
@PACKAGE_INIT@
|
||||
|
||||
# Find Iconv if we have found it.
|
||||
if ("@Iconv_FOUND@")
|
||||
find_package(Iconv REQUIRED)
|
||||
endif ()
|
||||
|
||||
# Include targets file
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/YYCCommonplaceTargets.cmake")
|
||||
|
||||
check_required_components(YYCCommonplace)
|
||||
|
||||
30
cmake/check_charconv.cmake
Normal file
30
cmake/check_charconv.cmake
Normal file
@@ -0,0 +1,30 @@
|
||||
message(STATUS "Checking charconv implementation...")
|
||||
include(CheckCXXSourceCompiles)
|
||||
|
||||
file(READ "${CMAKE_CURRENT_LIST_DIR}/check_charconv/chars_format.cpp" TEST_CODE_SNIPPET)
|
||||
check_cxx_source_compiles("${TEST_CODE_SNIPPET}" YYCC_CHARCONV_HAS_CHARS_FORMAT)
|
||||
message(STATUS "Support std::chars_format: ${YYCC_CHARCONV_HAS_CHARS_FORMAT}")
|
||||
|
||||
file(READ "${CMAKE_CURRENT_LIST_DIR}/check_charconv/from_chars_result.cpp" TEST_CODE_SNIPPET)
|
||||
check_cxx_source_compiles("${TEST_CODE_SNIPPET}" YYCC_CHARCONV_HAS_FROM_CHARS_RESULT)
|
||||
message(STATUS "Support std::from_chars_result: ${YYCC_CHARCONV_HAS_FROM_CHARS_RESULT}")
|
||||
|
||||
file(READ "${CMAKE_CURRENT_LIST_DIR}/check_charconv/to_chars_result.cpp" TEST_CODE_SNIPPET)
|
||||
check_cxx_source_compiles("${TEST_CODE_SNIPPET}" YYCC_CHARCONV_HAS_TO_CHARS_RESULT)
|
||||
message(STATUS "Support std::to_chars_result: ${YYCC_CHARCONV_HAS_TO_CHARS_RESULT}")
|
||||
|
||||
file(READ "${CMAKE_CURRENT_LIST_DIR}/check_charconv/from_chars_int.cpp" TEST_CODE_SNIPPET)
|
||||
check_cxx_source_compiles("${TEST_CODE_SNIPPET}" YYCC_CHARCONV_HAS_FROM_CHARS_INT)
|
||||
message(STATUS "Support std::from_chars with integral type: ${YYCC_CHARCONV_HAS_FROM_CHARS_INT}")
|
||||
|
||||
file(READ "${CMAKE_CURRENT_LIST_DIR}/check_charconv/from_chars_float.cpp" TEST_CODE_SNIPPET)
|
||||
check_cxx_source_compiles("${TEST_CODE_SNIPPET}" YYCC_CHARCONV_HAS_FROM_CHARS_FLOAT)
|
||||
message(STATUS "Suppoer std::from_chars with float point type: ${YYCC_CHARCONV_HAS_FROM_CHARS_FLOAT}")
|
||||
|
||||
file(READ "${CMAKE_CURRENT_LIST_DIR}/check_charconv/to_chars_int.cpp" TEST_CODE_SNIPPET)
|
||||
check_cxx_source_compiles("${TEST_CODE_SNIPPET}" YYCC_CHARCONV_HAS_TO_CHARS_INT)
|
||||
message(STATUS "Support std::to_chars with integral type: ${YYCC_CHARCONV_HAS_TO_CHARS_INT}")
|
||||
|
||||
file(READ "${CMAKE_CURRENT_LIST_DIR}/check_charconv/to_chars_float.cpp" TEST_CODE_SNIPPET)
|
||||
check_cxx_source_compiles("${TEST_CODE_SNIPPET}" YYCC_CHARCONV_HAS_TO_CHARS_FLOAT)
|
||||
message(STATUS "Support std::to_chars with float point type: ${YYCC_CHARCONV_HAS_TO_CHARS_FLOAT}")
|
||||
8
cmake/check_charconv/chars_format.cpp
Normal file
8
cmake/check_charconv/chars_format.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
#include <charconv>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
auto scientific = std::chars_format::scientific;
|
||||
auto fixed = std::chars_format::fixed;
|
||||
auto general = std::chars_format::general;
|
||||
auto hex = std::chars_format::hex;
|
||||
}
|
||||
16
cmake/check_charconv/from_chars_float.cpp
Normal file
16
cmake/check_charconv/from_chars_float.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include <charconv>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
const char probe[] = "0.0";
|
||||
const char* first = probe;
|
||||
const char* last = first + sizeof(probe);
|
||||
|
||||
{
|
||||
float value;
|
||||
auto rv = std::from_chars(first, last, value, std::chars_format::general);
|
||||
}
|
||||
{
|
||||
double value;
|
||||
auto rv = std::from_chars(first, last, value, std::chars_format::general);
|
||||
}
|
||||
}
|
||||
41
cmake/check_charconv/from_chars_int.cpp
Normal file
41
cmake/check_charconv/from_chars_int.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include <charconv>
|
||||
#include <cstdint>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
const char probe[] = "0";
|
||||
const char* first = probe;
|
||||
const char* last = first + sizeof(probe);
|
||||
|
||||
{
|
||||
std::int8_t value;
|
||||
auto rv = std::from_chars(first, last, value, 10);
|
||||
}
|
||||
{
|
||||
std::int16_t value;
|
||||
auto rv = std::from_chars(first, last, value, 10);
|
||||
}
|
||||
{
|
||||
std::int32_t value;
|
||||
auto rv = std::from_chars(first, last, value, 10);
|
||||
}
|
||||
{
|
||||
std::int64_t value;
|
||||
auto rv = std::from_chars(first, last, value, 10);
|
||||
}
|
||||
{
|
||||
std::uint8_t value;
|
||||
auto rv = std::from_chars(first, last, value, 10);
|
||||
}
|
||||
{
|
||||
std::uint16_t value;
|
||||
auto rv = std::from_chars(first, last, value, 10);
|
||||
}
|
||||
{
|
||||
std::uint32_t value;
|
||||
auto rv = std::from_chars(first, last, value, 10);
|
||||
}
|
||||
{
|
||||
std::uint64_t value;
|
||||
auto rv = std::from_chars(first, last, value, 10);
|
||||
}
|
||||
}
|
||||
9
cmake/check_charconv/from_chars_result.cpp
Normal file
9
cmake/check_charconv/from_chars_result.cpp
Normal file
@@ -0,0 +1,9 @@
|
||||
#include <charconv>
|
||||
#include <system_error>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
std::from_chars_result result {
|
||||
.ptr = nullptr,
|
||||
.ec = std::errc{},
|
||||
};
|
||||
}
|
||||
16
cmake/check_charconv/to_chars_float.cpp
Normal file
16
cmake/check_charconv/to_chars_float.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include <charconv>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
char buffer[1024];
|
||||
char* first = buffer;
|
||||
char* last = first + sizeof(buffer);
|
||||
|
||||
{
|
||||
float value = 0;
|
||||
auto rv = std::to_chars(first, last, value, std::chars_format::general, 6);
|
||||
}
|
||||
{
|
||||
double value = 0;
|
||||
auto rv = std::to_chars(first, last, value, std::chars_format::general, 6);
|
||||
}
|
||||
}
|
||||
41
cmake/check_charconv/to_chars_int.cpp
Normal file
41
cmake/check_charconv/to_chars_int.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include <charconv>
|
||||
#include <cstdint>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
char buffer[1024];
|
||||
char* first = buffer;
|
||||
char* last = first + sizeof(buffer);
|
||||
|
||||
{
|
||||
std::int8_t value = 0;
|
||||
auto rv = std::to_chars(first, last, value, 10);
|
||||
}
|
||||
{
|
||||
std::int16_t value = 0;
|
||||
auto rv = std::to_chars(first, last, value, 10);
|
||||
}
|
||||
{
|
||||
std::int32_t value = 0;
|
||||
auto rv = std::to_chars(first, last, value, 10);
|
||||
}
|
||||
{
|
||||
std::int64_t value = 0;
|
||||
auto rv = std::to_chars(first, last, value, 10);
|
||||
}
|
||||
{
|
||||
std::uint8_t value = 0;
|
||||
auto rv = std::to_chars(first, last, value, 10);
|
||||
}
|
||||
{
|
||||
std::uint16_t value = 0;
|
||||
auto rv = std::to_chars(first, last, value, 10);
|
||||
}
|
||||
{
|
||||
std::uint32_t value = 0;
|
||||
auto rv = std::to_chars(first, last, value, 10);
|
||||
}
|
||||
{
|
||||
std::uint64_t value = 0;
|
||||
auto rv = std::to_chars(first, last, value, 10);
|
||||
}
|
||||
}
|
||||
9
cmake/check_charconv/to_chars_result.cpp
Normal file
9
cmake/check_charconv/to_chars_result.cpp
Normal file
@@ -0,0 +1,9 @@
|
||||
#include <charconv>
|
||||
#include <system_error>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
std::to_chars_result result {
|
||||
.ptr = nullptr,
|
||||
.ec = std::errc{},
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,36 @@
|
||||
# Configure Doxygen config file
|
||||
# Extract all public macros defined in YYCC
|
||||
# However, you should note that these extratcted macros have generator expressions.
|
||||
get_target_property(YYCC_COMPILE_DEFINITIONS YYCCommonplace COMPILE_DEFINITIONS)
|
||||
if (YYCC_COMPILE_DEFINITIONS STREQUAL "YYCC_COMPILE_DEFINITIONS-NOTFOUND")
|
||||
message(FATAL_ERROR "Cannot extract compile definitions from YYCCommonplace.")
|
||||
endif ()
|
||||
# Convert list to string for expanding in future.
|
||||
list(JOIN YYCC_COMPILE_DEFINITIONS " " YYCC_MACRO_GENERATOR_EXPRESSIONS)
|
||||
|
||||
# We simply configure Doxygen config file first.
|
||||
configure_file(
|
||||
${CMAKE_CURRENT_LIST_DIR}/Doxyfile.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
|
||||
@ONLY
|
||||
)
|
||||
|
||||
# Then we use "file GENERATE" syntax to generate per-config truely Doxyfile used by Doxygen.
|
||||
# Because there is no "$<>" syntax in Doxyfile, so we can safely use it.
|
||||
# Please note that the generation of "file GENERATE" syntax will be postponed until the build stage.
|
||||
file(GENERATE
|
||||
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/Doxyfile"
|
||||
INPUT "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile"
|
||||
TARGET YYCCommonplace
|
||||
)
|
||||
|
||||
# Add custom target using per-config Doxyfile
|
||||
add_custom_target (YYCCDocumentation
|
||||
Doxygen::doxygen "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/Doxyfile"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMENT "Generating documentation" VERBATIM
|
||||
)
|
||||
|
||||
# Install built documentation
|
||||
install (DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html
|
||||
DESTINATION ${YYCC_INSTALL_DOC_PATH}
|
||||
)
|
||||
|
||||
@@ -1031,7 +1031,7 @@ EXCLUDE_SYMBOLS =
|
||||
# that contain example code fragments that are included (see the \include
|
||||
# command).
|
||||
|
||||
EXAMPLE_PATH = @CMAKE_CURRENT_LIST_DIR@/../testbench
|
||||
EXAMPLE_PATH = @CMAKE_CURRENT_LIST_DIR@/../test
|
||||
|
||||
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
|
||||
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
|
||||
@@ -2306,7 +2306,7 @@ PERLMOD_MAKEVAR_PREFIX =
|
||||
# C-preprocessor directives found in the sources and include files.
|
||||
# The default value is: YES.
|
||||
|
||||
ENABLE_PREPROCESSING = NO
|
||||
ENABLE_PREPROCESSING = YES
|
||||
|
||||
# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
|
||||
# in the source code. If set to NO, only conditional compilation will be
|
||||
@@ -2356,7 +2356,7 @@ INCLUDE_FILE_PATTERNS =
|
||||
# recursively expanded use the := operator instead of the = operator.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
PREDEFINED =
|
||||
PREDEFINED = @YYCC_MACRO_GENERATOR_EXPRESSIONS@
|
||||
|
||||
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
|
||||
# tag can be used to specify a list of macro names that should be expanded. The
|
||||
|
||||
208
doc/src/carton/binstore.dox
Normal file
208
doc/src/carton/binstore.dox
Normal file
@@ -0,0 +1,208 @@
|
||||
namespace yycc::carton::binstore {
|
||||
/**
|
||||
|
||||
\page binstore Binary Settings Storage (Binstore)
|
||||
|
||||
The binstore module provides a binary settings storage system that allows
|
||||
applications to persistently store and retrieve configuration settings in
|
||||
a binary format. It includes functionality for type-safe serialization and deserialization,
|
||||
setting management with unique tokens for access control, version control with migration strategies,
|
||||
and comprehensive error handling.
|
||||
|
||||
\section binstore__overview Overview
|
||||
|
||||
The binstore module consists of several key components:
|
||||
|
||||
\li types: Basic types and error handling for the module
|
||||
\li serdes: Serialization/deserialization functionality for different data types
|
||||
\li setting: Management of settings with name-based lookup and token-based access
|
||||
\li configuration: Version and settings collection management
|
||||
\li storage: Main storage class for loading/saving settings to/from files or streams
|
||||
|
||||
\section binstore__example Example Usage
|
||||
|
||||
Here is a complete example showing how to use the binstore module:
|
||||
|
||||
\code{.cpp}
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/carton/binstore.hpp>
|
||||
#include <yycc/patch/stream.hpp>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
using namespace yycc::carton::binstore;
|
||||
using namespace yycc::patch::stream;
|
||||
|
||||
enum class LogLevel : uint8_t { Debug, Info, Warning, Error };
|
||||
|
||||
int main() {
|
||||
// Create settings collection
|
||||
auto settings = setting::SettingCollection();
|
||||
auto int_setting_token = settings.add_setting(setting::Setting(u8"max_connections"));
|
||||
auto float_setting_token = settings.add_setting(setting::Setting(u8"timeout"));
|
||||
auto string_setting_token = settings.add_setting(setting::Setting(u8"server_address"));
|
||||
auto bool_setting_token = settings.add_setting(setting::Setting(u8"enable_logging"));
|
||||
auto enum_setting_token = settings.add_setting(setting::Setting(u8"log_level"));
|
||||
|
||||
// Create configuration with version 1
|
||||
auto config = configuration::Configuration(1, std::move(settings));
|
||||
|
||||
// Create storage with the configuration
|
||||
auto storage = storage::Storage(std::move(config));
|
||||
|
||||
// Using appropriate SerDes types for different data types
|
||||
using IntSerDes = serdes::IntegralSerDes<int32_t>;
|
||||
using FloatSerDes = serdes::FloatingPointSerDes<float>;
|
||||
using StringSerDes = serdes::StringSerDes;
|
||||
using BoolSerDes = serdes::BoolSerDes<true>; // true as default value
|
||||
using EnumSerDes = serdes::EnumSerDes<LogLevel, LogLevel::Info>;
|
||||
|
||||
// Set values
|
||||
storage.set_value<IntSerDes>(int_setting_token, 100);
|
||||
storage.set_value<FloatSerDes>(float_setting_token, 2.5f);
|
||||
storage.set_value<StringSerDes>(string_setting_token, u8"localhost");
|
||||
storage.set_value<BoolSerDes>(bool_setting_token, true);
|
||||
storage.set_value<EnumSerDes>(enum_setting_token, LogLevel::Debug);
|
||||
|
||||
// Save to file
|
||||
if (auto result = storage.save_into_file("config.bin"); result.has_value()) {
|
||||
std::cout << "Configuration saved successfully" << std::endl;
|
||||
} else {
|
||||
std::cout << "Failed to save configuration" << std::endl;
|
||||
}
|
||||
|
||||
// Load from file
|
||||
auto new_config = configuration::Configuration(1, setting::SettingCollection());
|
||||
auto new_storage = storage::Storage(std::move(new_config));
|
||||
|
||||
if (auto result = new_storage.load_from_file("config.bin", storage::LoadStrategy::MigrateOld); result.has_value()) {
|
||||
std::cout << "Configuration loaded successfully" << std::endl;
|
||||
|
||||
// Get values
|
||||
int32_t max_conn = new_storage.get_value<IntSerDes>(int_setting_token);
|
||||
float timeout = new_storage.get_value<FloatSerDes>(float_setting_token);
|
||||
std::u8string addr = new_storage.get_value<StringSerDes>(string_setting_token);
|
||||
bool logging = new_storage.get_value<BoolSerDes>(bool_setting_token);
|
||||
LogLevel level = new_storage.get_value<EnumSerDes>(enum_setting_token);
|
||||
|
||||
std::cout << "Max connections: " << max_conn << std::endl;
|
||||
std::cout << "Timeout: " << timeout << std::endl;
|
||||
std::cout << "Server address: " << addr << std::endl;
|
||||
std::cout << "Logging enabled: " << (logging ? "yes" : "no") << std::endl;
|
||||
std::cout << "Log level: " << static_cast<int>(level) << std::endl;
|
||||
} else {
|
||||
std::cout << "Failed to load configuration" << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section binstore__components Components
|
||||
|
||||
\subsection binstore__settings Settings Management
|
||||
|
||||
Settings are identified by unique names and accessed via tokens. The [SettingCollection](\ref setting::SettingCollection)
|
||||
manages a collection of settings and ensures no duplicates.
|
||||
|
||||
\subsection binstore__configuration Configuration
|
||||
|
||||
The [Configuration](\ref configuration::Configuration) class holds the version identifier and the collection of settings.
|
||||
Version control is crucial for handling configuration migration between application versions.
|
||||
|
||||
\subsection binstore__storage Storage
|
||||
|
||||
The [Storage](\ref storage::Storage) class is the main interface for setting/getting values and loading/saving configurations.
|
||||
It provides methods for both file-based and stream-based operations.
|
||||
|
||||
\subsection binstore__serdes Serialization/Deserialization
|
||||
|
||||
SerDes (Serializer/Deserializer) classes handle type-safe conversion between values and their binary representation.
|
||||
Built-in SerDes types include:
|
||||
|
||||
\li Integral types ([IntegralSerDes](\ref serdes::IntegralSerDes))
|
||||
\li Floating-point types ([FloatingPointSerDes](\ref serdes::FloatingPointSerDes))
|
||||
\li String types ([StringSerDes](\ref serdes::StringSerDes))
|
||||
\li Boolean types ([BoolSerDes](\ref serdes::BoolSerDes))
|
||||
\li Enum types ([EnumSerDes](\ref serdes::EnumSerDes))
|
||||
|
||||
For some of them, you can specify value range and default value via template parameters.
|
||||
|
||||
\section binstore__load_strategies Load Strategies
|
||||
|
||||
The binstore module provides different strategies for handling version mismatches:
|
||||
|
||||
\li [OnlyCurrent](\ref storage::LoadStrategy::OnlyCurrent): Only accept configurations with matching version
|
||||
\li [MigrateOld](\ref storage::LoadStrategy::MigrateOld): Accept matching and older versions, reject newer versions
|
||||
\li [AcceptAll](\ref storage::LoadStrategy::AcceptAll): Accept all versions (not recommended for production)
|
||||
|
||||
\section binstore__custom_serdes Custom SerDes
|
||||
|
||||
Custom SerDes (Serializer/Deserializer) can be created by implementing the \c SerDes concept.
|
||||
A valid SerDes must satisfy the following requirements:
|
||||
|
||||
\li Have a type alias called \c ValueType indicating the corresponding setting type
|
||||
\li Have a member function called \c serialize that accepts a const reference of the setting data and returns \c ByteArray
|
||||
or \c std::nullopt if serialization fails.
|
||||
\li Have a member function called \c deserialize that converts \c ByteArray to the desired type
|
||||
or returns \c std::nullopt if deserialization fails.
|
||||
\li Have a member function called \c reset that returns a default \c ByteArray value.
|
||||
|
||||
Here is an example of a custom SerDes for storing IPv4 addresses:
|
||||
|
||||
\code{.cpp}
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
struct IPv4Address {
|
||||
std::uint8_t octets[4];
|
||||
|
||||
IPv4Address() : octets{0, 0, 0, 0} {}
|
||||
IPv4Address(std::uint8_t a, std::uint8_t b, std::uint8_t c, std::uint8_t d) {
|
||||
octets[0] = a; octets[1] = b; octets[2] = c; octets[3] = d;
|
||||
}
|
||||
};
|
||||
|
||||
struct IPv4SerDes {
|
||||
using ValueType = IPv4Address;
|
||||
static constexpr size_t VALUE_SIZE = sizeof(IPv4Address); // 4 octets
|
||||
|
||||
std::optional<types::ByteArray> serialize(const ValueType& value) const {
|
||||
types::ByteArray ba;
|
||||
ba.resize_data(VALUE_SIZE);
|
||||
std::memcpy(ba.get_data_ptr(), value.octets, VALUE_SIZE);
|
||||
return ba;
|
||||
}
|
||||
|
||||
std::optional<ValueType> deserialize(const types::ByteArray& ba) const {
|
||||
if (ba.get_data_size() != VALUE_SIZE) return std::nullopt;
|
||||
|
||||
ValueType value;
|
||||
std::memcpy(value.octets, ba.get_data_ptr(), VALUE_SIZE);
|
||||
return value;
|
||||
}
|
||||
|
||||
types::ByteArray reset() const {
|
||||
// Reset to local address
|
||||
ValueType default_value(127, 0, 0, 1);
|
||||
return this->serialize(default_value).value();
|
||||
}
|
||||
};
|
||||
\endcode
|
||||
|
||||
To use the custom SerDes:
|
||||
|
||||
\code{.cpp}
|
||||
// Add setting to collection
|
||||
auto ip_setting_token = settings.add_setting(setting::Setting(u8"server_ip"));
|
||||
|
||||
// Use custom SerDes
|
||||
IPv4SerDes ip_serdes;
|
||||
storage.set_value<IPv4SerDes>(ip_setting_token, IPv4Address(192, 168, 1, 1));
|
||||
|
||||
// Retrieve value
|
||||
IPv4Address ip_addr = storage.get_value<IPv4SerDes>(ip_setting_token);
|
||||
\endcode
|
||||
|
||||
*/
|
||||
}
|
||||
187
doc/src/carton/clap.dox
Normal file
187
doc/src/carton/clap.dox
Normal file
@@ -0,0 +1,187 @@
|
||||
namespace yycc::carton::clap {
|
||||
/**
|
||||
|
||||
\page clap Command Line Argument Parser (CLAP)
|
||||
|
||||
Command Line Argument Parser (CLAP) module for handling command line arguments and environment variables.
|
||||
This module provides a comprehensive system for defining, parsing, and validating command line
|
||||
arguments and environment variables. It includes components for defining application metadata,
|
||||
command line options, variables, and utilities for parsing and validation.
|
||||
|
||||
\section clap__overview Overview
|
||||
|
||||
The CLAP module consists of several key components:
|
||||
|
||||
\li Types: Error types and result types used throughout the module
|
||||
\li Validator: Type-safe validation for command line argument values
|
||||
\li Option: Command line options with short and long names
|
||||
\li Variable: Environment variables that can be captured
|
||||
\li Summary: Application metadata (name, version, author, description)
|
||||
\li Application: Complete application definition with options and variables
|
||||
\li Manual: Help and version information generation
|
||||
\li Parser: Command line argument parsing functionality
|
||||
\li Resolver: Environment variable resolution functionality
|
||||
|
||||
\section clap__example Example Usage
|
||||
|
||||
Here is a complete example showing how to use the CLAP module:
|
||||
|
||||
\code{.cpp}
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/carton/clap.hpp>
|
||||
#include <yycc/patch/stream.hpp>
|
||||
#include <iostream>
|
||||
|
||||
using namespace yycc::carton::clap;
|
||||
using namespace yycc::patch::stream;
|
||||
|
||||
// Define an application with options and variables
|
||||
int main(int argc, char* argv[]) {
|
||||
// Create application summary
|
||||
auto summary = summary::Summary(u8"MyApp", u8"author", u8"1.0.0", u8"A sample application");
|
||||
|
||||
// Create options collection
|
||||
auto options = option::OptionCollection();
|
||||
auto int_opt = options.add_option(option::Option(u8"i", u8"int", u8"NUM", u8"integral argument"));
|
||||
auto float_opt = options.add_option(option::Option(u8"f", std::nullopt, u8"NUM", u8"floating point argument"));
|
||||
auto string_opt = options.add_option(option::Option(std::nullopt, u8"string", u8"STR", u8"string argument"));
|
||||
auto flag_opt = options.add_option(option::Option(u8"v", std::nullopt, std::nullopt, u8"verbose mode"));
|
||||
|
||||
// Create variables collection
|
||||
auto variables = variable::VariableCollection();
|
||||
auto env_var = variables.add_variable(variable::Variable(u8"ENV_VAR", u8"Environment variable description", true));
|
||||
|
||||
// Create the application and manual
|
||||
auto app = application::Application(std::move(summary), std::move(options), std::move(variables));
|
||||
auto manual = manual::Manual(app);
|
||||
|
||||
// Parse command line arguments
|
||||
auto result = parser::Parser::from_system(app);
|
||||
if (result.has_value()) {
|
||||
auto parser = std::move(result.value());
|
||||
|
||||
// Get values using validators
|
||||
using IntValidator = validator::IntegralValidator<int>;
|
||||
using FloatValidator = validator::FloatingPointValidator<float>;
|
||||
using StringValidator = validator::StringValidator;
|
||||
|
||||
// Check and get integer option
|
||||
if (auto int_val = parser.get_value_option<IntValidator>(int_opt); int_val.has_value()) {
|
||||
std::cout << "Integer value: " << int_val.value() << std::endl;
|
||||
}
|
||||
|
||||
// Check and get float option
|
||||
if (auto float_val = parser.get_value_option<FloatValidator>(float_opt); float_val.has_value()) {
|
||||
std::cout << "Float value: " << float_val.value() << std::endl;
|
||||
}
|
||||
|
||||
// Check and get string option
|
||||
if (auto str_val = parser.get_value_option<StringValidator>(string_opt); str_val.has_value()) {
|
||||
std::cout << "String value: " << str_val.value() << std::endl;
|
||||
}
|
||||
|
||||
// Check flag option
|
||||
if (auto flag_val = parser.get_flag_option(flag_opt); flag_val.has_value() && flag_val.value()) {
|
||||
std::cout << "Verbose mode enabled" << std::endl;
|
||||
}
|
||||
} else {
|
||||
// Print help if parsing failed
|
||||
manual.print_help(std::cout);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
\endcode
|
||||
|
||||
This code handles command lines like:
|
||||
\code{.sh}
|
||||
./myapp -i 123 -f 2.5 --string "hello world" -v
|
||||
\endcode
|
||||
|
||||
\section clap__components Components
|
||||
|
||||
\subsection clap__application Application Definition
|
||||
|
||||
The [Application](\ref application::Application) class represents a complete command line application with its summary, options, and environment variables.
|
||||
It combines the application metadata, command line options, and environment variables into a single unit.
|
||||
|
||||
\subsection clap__options Options
|
||||
|
||||
[Option](\ref option::Option) is command line arguments that can accept values or act as flags.
|
||||
They can have both short names (single character)
|
||||
and long names (full text). The [OptionCollection](\ref option::OptionCollection) manages a collection of options and ensures no duplicates.
|
||||
|
||||
\subsection clap__variables Variables
|
||||
|
||||
[Variable](\ref variable::Variable) represent environment variables that can be captured and validated. The [VariableCollection](\ref variable::VariableCollection)
|
||||
manages a collection of environment variables and ensures no duplicates.
|
||||
|
||||
\subsection clap__parsing Parsing
|
||||
|
||||
The [Parser](\ref parser::Parser) class handles command line argument parsing. It can be created from user-provided arguments
|
||||
or from system arguments (argc/argv). Values are retrieved using type-safe validators.
|
||||
|
||||
\subsection clap__validation Validation
|
||||
|
||||
Validators ensure type-safe validation of command line argument values.
|
||||
The module provides built-in validators for:
|
||||
|
||||
\li Integral types ([IntegralValidator](\ref validator::IntegralValidator))
|
||||
\li Floating-point types ([FloatingPointValidator](\ref validator::FloatingPointValidator))
|
||||
\li String types ([StringValidator](\ref validator::StringValidator))
|
||||
|
||||
For some of them, you also can specify value range via template arguments.
|
||||
|
||||
\section clap__custom_validators Custom Validator
|
||||
|
||||
Custom validators can be created by implementing the \c Validator concept.
|
||||
A valid validator must satisfy the following requirements:
|
||||
|
||||
\li Have a type alias called \c ReturnType indicating the return value type
|
||||
\li Have a member function called \c validate that receives <TT>const std::u8string_view&</TT> as its only argument
|
||||
and returns validated \c ReturnType or \c std::nullopt if validation fails
|
||||
|
||||
Here is an example of a custom validator that validates email addresses:
|
||||
|
||||
\code{.cpp}
|
||||
#include <yycc/string/reinterpret.hpp>
|
||||
#include <regex>
|
||||
|
||||
struct EmailValidator {
|
||||
using ReturnType = std::u8string;
|
||||
|
||||
std::optional<ReturnType> validate(const std::u8string_view& sv) const {
|
||||
// Simple email validation using regex
|
||||
static const std::regex email_regex(
|
||||
R"([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})");
|
||||
|
||||
auto email_str = yycc::string::reinterpret::as_ordinary_view(sv);
|
||||
if (std::regex_match(email_str, email_regex)) {
|
||||
return sv;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
\endcode
|
||||
|
||||
To use the custom validator:
|
||||
|
||||
\code{.cpp}
|
||||
// Add option to application
|
||||
auto email_opt = options.add_option(option::Option(std::nullopt, u8"email", u8"EMAIL", u8"Email address"));
|
||||
|
||||
// Use custom validator
|
||||
if (auto email_val = parser.get_value_option<EmailValidator>(email_opt); email_val.has_value()) {
|
||||
std::cout << yycc::patch::format(u8"Valid email: {}", email_val); << std::endl;
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section clap__limitations Limitations
|
||||
|
||||
Due to the limitations of implementation,
|
||||
CLAP now only allow only zero or one associated value for single option.
|
||||
More than one assocciated value for single option is not supported.
|
||||
|
||||
*/
|
||||
}
|
||||
74
doc/src/carton/csconsole.dox
Normal file
74
doc/src/carton/csconsole.dox
Normal file
@@ -0,0 +1,74 @@
|
||||
namespace yycc::carton::csconsole {
|
||||
/**
|
||||
|
||||
\page csconsole Universal IO Function
|
||||
|
||||
This namespace provide universal console IO function which is more like C\# provided,
|
||||
because Windows is lacking in UTF8 console IO.
|
||||
|
||||
\section csconsole__deprecation Deprecation Notes
|
||||
|
||||
This namespace, or this module is deprecated.
|
||||
Its provided functions are too aggressive and can not cover all use scenarios.
|
||||
So it is suggested not to use this namespace.
|
||||
Programmers should handle Windows UTF8 issues on their own.
|
||||
|
||||
\section csconsole__why Why?
|
||||
|
||||
Windows console doesn't support UTF8 very well.
|
||||
The standard input output functions can not work properly with UTF8 on Windows.
|
||||
So we create this namespace and provide various console-related functions
|
||||
to patch Windows console and let it more like the console in other platforms.
|
||||
|
||||
The function provided in this function can be called in any platforms.
|
||||
In Windows, the implementation will use Windows native function,
|
||||
and in other platform, the implementation will redirect request to standard C function like \c std::fputs and etc.
|
||||
So the programmer do not need to be worried about which function should they use,
|
||||
and don't need to use macro to use different IO function in different platforms.
|
||||
It is just enough that fully use the functions provided in this namespace.
|
||||
|
||||
All IO functions this namespace provided are UTF8-based.
|
||||
It also means that input output string should always be UTF8 encoded.
|
||||
|
||||
\section csconsole__input Input Functions
|
||||
|
||||
Please note that EOL will automatically converted into LF on Windows platform, not CRLF.
|
||||
This action actually is removing all CR chars in result string.
|
||||
This behavior affect nothing in most cases but it still is possible break something in some special case.
|
||||
|
||||
Due to implementation, if you decide to use this function,
|
||||
you should give up using any other function to read stdin stream,
|
||||
such as \c std::gets() and \c std::cin.
|
||||
Because this function may read chars which is more than needed.
|
||||
These extra chars will be stored in this function and can be used next calling.
|
||||
But these chars can not be visited by stdin again.
|
||||
This behavior may cause bug.
|
||||
So if you decide using this function, stick on it and do not change.
|
||||
|
||||
Due to implementation, this function do not support hot switch of stdin.
|
||||
It means that stdin can be redirected before first calling of this function,
|
||||
but it should not be redirected during program running.
|
||||
The reason is the same one introduced above.
|
||||
|
||||
\section csconsole__output Output Functions
|
||||
|
||||
In current implementation, EOL will not be converted automatically to CRLF.
|
||||
This is different with other stream read functions provided in this namespace.
|
||||
|
||||
Comparing with other stream read functions provided in this namespace,
|
||||
stream write function support hot switch of stdout and stderr.
|
||||
Because they do not have internal buffer storing something.
|
||||
|
||||
In this namespace, there are various stream write function.
|
||||
There is a list telling you how to choose one from them for using:
|
||||
|
||||
\li Functions with leading "e" (like eformat, ewrite) will write data into stderr,
|
||||
otherwise they will write data into stdout.
|
||||
\li Functions with embedded "format" (format, format_line, eformat, eformat_line) are output functions with format feature like \c std::fprintf(),
|
||||
otherwise the functions with embedded "write" in the name (write, write_line, ewrite, ewrite_line) will only write plain string like \c std::fputs().
|
||||
\li Functions with trailing "line" (format_line, write_line, eformat_line, ewrite_line) will write extra EOL to break current line.
|
||||
This is commonly used, otherwise functions will only write the text provided by arguments,
|
||||
without adding something.
|
||||
|
||||
*/
|
||||
}
|
||||
93
doc/src/carton/fft.dox
Normal file
93
doc/src/carton/fft.dox
Normal file
@@ -0,0 +1,93 @@
|
||||
namespace yycc::carton::fft {
|
||||
/**
|
||||
|
||||
\page fft Homemade FFT
|
||||
|
||||
This namespace provides a fast Fourier transform (FFT) implementation for signal processing applications.
|
||||
It includes classes for performing FFT computations on complex and real-valued signals, along with
|
||||
window functions to reduce spectral leakage.
|
||||
|
||||
\section fft__basic_usage Basic Usage
|
||||
|
||||
To use the FFT functionality for general purposes, use the FriendlyFft class:
|
||||
|
||||
\code
|
||||
#include "yycc/carton/fft.hpp"
|
||||
using namespace yycc::carton::fft;
|
||||
|
||||
// Create FFT instance for 8-point transform
|
||||
FriendlyFft<size_t, float, 8u> fft;
|
||||
|
||||
// Prepare input data (must be power of 2 in length)
|
||||
float time_scope[8] = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f};
|
||||
float freq_scope[4]; // Output is half the input size
|
||||
|
||||
// Create window function to reduce spectral leakage
|
||||
Window<size_t, float, 8u> window(WindowType::HanningWindow);
|
||||
|
||||
// Perform FFT transformation
|
||||
fft.easy_compute(time_scope, freq_scope, window);
|
||||
|
||||
// freq_scope now contains frequency domain data
|
||||
\endcode
|
||||
|
||||
\section fft__window_functions Window Functions
|
||||
|
||||
The library provides window functions to reduce spectral leakage:
|
||||
|
||||
\code
|
||||
// Create a Hanning window for 16-point data
|
||||
Window<size_t, float, 16u> hanning_window(WindowType::HanningWindow);
|
||||
|
||||
// Apply window to your data
|
||||
float data[16];
|
||||
// ... initialize data ...
|
||||
hanning_window.apply_window(data);
|
||||
\endcode
|
||||
|
||||
\section fft__direct_fft Direct FFT Computation
|
||||
|
||||
For more control over the FFT computation, use the core Fft class:
|
||||
|
||||
\code
|
||||
#include "yycc/carton/fft.hpp"
|
||||
|
||||
// Create FFT instance for 16-point transform
|
||||
Fft<size_t, double, 16u> fft;
|
||||
|
||||
// Prepare complex input data
|
||||
std::complex<double> data[16];
|
||||
// ... initialize complex data ...
|
||||
|
||||
// Perform FFT transformation
|
||||
fft.compute(data);
|
||||
// data now contains transformed values
|
||||
\endcode
|
||||
|
||||
\section fft__predefined_types Predefined Types
|
||||
|
||||
The library provides commonly used FFT types for convenience:
|
||||
|
||||
\code
|
||||
// Float precision FFTs
|
||||
Fft4F fft4f; // 4-point float FFT
|
||||
Fft8F fft8f; // 8-point float FFT
|
||||
Fft16F fft16f; // 16-point float FFT
|
||||
Fft256F fft256f; // 256-point float FFT
|
||||
|
||||
// Double precision FFTs
|
||||
Fft4 fft4; // 4-point double FFT
|
||||
Fft8 fft8; // 8-point double FFT
|
||||
Fft16 fft16; // 16-point double FFT
|
||||
Fft256 fft256; // 256-point double FFT
|
||||
\endcode
|
||||
|
||||
\section fft__requirements Requirements
|
||||
|
||||
- Template parameters must satisfy certain constraints:
|
||||
- \c TIndex: The index type used by FFT which must be an unsigned integral type.
|
||||
- \c TFloat: The float point type used by FFT.
|
||||
- \c VN: The point of FFT which must be a power of 2 and >= 2.
|
||||
|
||||
*/
|
||||
}
|
||||
110
doc/src/carton/ironpad.dox
Normal file
110
doc/src/carton/ironpad.dox
Normal file
@@ -0,0 +1,110 @@
|
||||
namespace yycc::carton::ironpad {
|
||||
/**
|
||||
|
||||
\page ironpad Unhandled Exception Handler
|
||||
|
||||
Most Linux users are familiar with using core dump to find bugs.
|
||||
However finding bugs is a tough work on Windows especially most Windows users are naive for getting core dump.
|
||||
So it is essential to make an easy-to-visit core dump feature for Windows program.
|
||||
This is the reason why I create this module, yycc::carton::ironpad.
|
||||
|
||||
You may know Google also has a similar and universal project called Crashpad used by Google Chrome.
|
||||
That's right. But it is too heavy.
|
||||
I just want to implement a tiny but worked core dump feature on Windows.
|
||||
|
||||
This module is Windows specific.
|
||||
It still be available on other operating systems but all of its functions are do nothing.
|
||||
|
||||
\section ironpad__usage Usage
|
||||
|
||||
\subsection ironpad__usage__code Register Code
|
||||
|
||||
In most scenarios, programmer only need call #startup when program started or module loaded.
|
||||
And call #shutdown when program exited or module unloaded.
|
||||
All details are hidden by these 2 feature.
|
||||
Programmer do not need worried about the implementation of unhandled exception handler.
|
||||
|
||||
Optionally, you can provide a function pointer during calling #startup as a callback.
|
||||
The prototype of this function pointer is #ExceptionCallback.
|
||||
This callback will be called if any unhandled exception happened.
|
||||
It provides 2 pathes to log file and core dump file respectively.
|
||||
So that you can use an explicit way, e.g. \c MessageBox, to tell user exception happened and where are the log files,
|
||||
especially in GUI application because the default output stream, \c stderr, is invisible in GUI application.
|
||||
|
||||
However, please note the pathes provided by callback may be empty.
|
||||
In this case, it means that handler fail to create corresponding log files.
|
||||
Also, if you trying to register unhandled exception handler on the same process in different module with different callback,
|
||||
only the callback provided in first success registering will be called when unhandled exception happened,
|
||||
due to \ref ironpad__notes__singleton design.
|
||||
|
||||
\subsection ironpad__usage__location Location
|
||||
|
||||
When unhandled exception occurs,
|
||||
unhandled exception handler will try to record error log and core dump in following path:
|
||||
|
||||
\li Error Log: <TT>\%LOCALAPPDATA\%\\IronPad\\<I>program.exe</I>.<I>pid</I>.log</TT>
|
||||
\li Core Dump: <TT>\%LOCALAPPDATA\%\\IronPad\\<I>program.exe</I>.<I>pid</I>.dmp</TT>
|
||||
|
||||
The italic characters <I>program.exe</I> and <I>pid</I> will be replaced by program name and process ID respectively at runtime.
|
||||
Directory <TT>\%LOCALAPPDATA\%\\IronPad</TT> is the dedicated directory for this module.
|
||||
So you may see the generated logs and dumps in it.
|
||||
|
||||
\subsection ironpad__usage__last_remedy Last Remedy
|
||||
|
||||
If unhandled exception handler occurs error, these stuff may not be generated correctly.
|
||||
The end user may not find them and send them to you.
|
||||
There is a last remedy for this scenario.
|
||||
|
||||
Unhandled exception handler will still output error log in \c stderr no matter whether error log or core dump is created.
|
||||
So end user always can fetch error log from console.
|
||||
You only need to instruct end user open command prompt, launch application, reproduce error and get the output error log in console.
|
||||
In this case, you can not get core dump. But you can get error log.
|
||||
It is not good for debugging but it is better than nothing.
|
||||
|
||||
Also please note the last remedy may still have a little bit possibility to occurs error and output nothing,
|
||||
especially the error occurs in back trace function.
|
||||
There is no guaranteen that unhandled exception handler must generate error log and core dump.
|
||||
|
||||
\section ironpad__notes Notes
|
||||
|
||||
\subsection ironpad__notes__thread_safe Thread Safe
|
||||
|
||||
All exposed functions in this namespace are thread safe.
|
||||
The implementation uses \c std::mutex to ensure this.
|
||||
|
||||
\subsection ironpad__notes__singleton Singleton Handler
|
||||
|
||||
This namespace also have a mechanism that make sure the same unhandled exception handler implementation only appear once in the same process.
|
||||
For example, you have an executable program A.exe, and 2 dynamic libraries B.dll and C.dll.
|
||||
A.exe and B.dll use YYCC unhandled exception handler feature but C.dll not.
|
||||
A.exe will load B.dll and C.dll at runtime.
|
||||
Although both A.exe and B.dll call #startup,
|
||||
when unhandled exception occurs, there is only one error report output,
|
||||
which may be generated by A.exe or B.dll accoridng to their order of loading.
|
||||
|
||||
The core purpose of this is making sure the program will not output too many error report for the same unhandled exception,
|
||||
no matter how many modules calling #startup are loaded.
|
||||
Only one error report is enough.
|
||||
|
||||
More precisely, we use \c CreateMutexW to create an unique mutex in Windows global scope,
|
||||
to make sure #startup only run once in the same process.
|
||||
It is very like the implementation of singleton application.
|
||||
|
||||
\subsection ironpad__notes__recursive_calling Recursive Calling
|
||||
|
||||
The implementation of unhandled exception handler may also will throw exception.
|
||||
This will cause infinite recursive calling.
|
||||
This namespace has internal mechanism to prevent this bad case.
|
||||
If this really happened, the handler will quit silent and will not cause any issue.
|
||||
Programmer don't need to worry about this.
|
||||
|
||||
\subsection ironpad__notes__user_callback The Timing of User Callback
|
||||
|
||||
The timing of calling user callback is the tail of unhandled exception handler.
|
||||
It means that all log and coredump have been written if possible before calling callback.
|
||||
Because user callback may still raise exception.
|
||||
We want all essential log files has been written before calling it,
|
||||
so that at least we can visit them on disk or console.
|
||||
|
||||
*/
|
||||
}
|
||||
102
doc/src/carton/lexer61.dox
Normal file
102
doc/src/carton/lexer61.dox
Normal file
@@ -0,0 +1,102 @@
|
||||
namespace yycc::carton::lexer61 {
|
||||
/**
|
||||
|
||||
\page lexer61 Homemade Command Line Lexer
|
||||
|
||||
This namespace provides a lexer for parsing command-line arguments, supporting various quoting mechanisms,
|
||||
escape sequences, and Unicode characters. It follows the standard shell parsing rules for handling
|
||||
arguments containing spaces and special characters.
|
||||
|
||||
\section lexer61__basic_usage Basic Usage
|
||||
|
||||
To parse command line arguments, create a Lexer61 instance and call the Lexer61::lex() method:
|
||||
|
||||
\code
|
||||
#include "yycc/carton/lexer61.hpp"
|
||||
using namespace yycc::carton::lexer61;
|
||||
|
||||
Lexer61 lexer;
|
||||
auto result = lexer.lex(u8"program arg1 arg2 arg3");
|
||||
|
||||
if (result.has_value()) {
|
||||
auto args = std::move(result.value());
|
||||
// args contains: [u8"program", u8"arg1", u8"arg2", u8"arg3"]
|
||||
for (const auto& arg : args) {
|
||||
std::wcout << reinterpret_cast<const wchar_t*>(arg.c_str()) << std::endl;
|
||||
}
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section lexer61__quoting_support Quoting Support
|
||||
|
||||
The lexer supports both single and double quotes for grouping arguments with spaces:
|
||||
|
||||
\code
|
||||
Lexer61 lexer;
|
||||
|
||||
// Double quotes
|
||||
auto result1 = lexer.lex(u8R"(program "argument with spaces" end)");
|
||||
// Result: [u8"program", u8"argument with spaces", u8"end"]
|
||||
|
||||
// Single quotes
|
||||
auto result2 = lexer.lex(u8"program 'another argument' end");
|
||||
// Result: [u8"program", u8"another argument", u8"end"]
|
||||
|
||||
// Mixed quotes
|
||||
auto result3 = lexer.lex(u8R"(program "double quoted 'single inside'" 'single quoted "double inside"')");
|
||||
// Result: [u8"program", u8"double quoted 'single inside'", u8"single quoted \"double inside\""]
|
||||
\endcode
|
||||
|
||||
\section lexer61__escape_sequences Escape Sequences
|
||||
|
||||
The lexer supports escape sequences for including special characters:
|
||||
|
||||
\code
|
||||
Lexer61 lexer;
|
||||
auto result = lexer.lex(u8R"(program escaped\ space "quoted with \" quote" 'single with \' quote')");
|
||||
|
||||
// Result: [u8"program", u8"escaped space", u8"quoted with \" quote", u8"single with \' quote"]
|
||||
\endcode
|
||||
|
||||
\section lexer61__unicode_support Unicode Support
|
||||
|
||||
The lexer fully supports Unicode characters in command line arguments:
|
||||
|
||||
\code
|
||||
Lexer61 lexer;
|
||||
auto result = lexer.lex(u8"程序 中文 参数");
|
||||
// Result: [u8"程序", u8"中文", u8"参数"]
|
||||
|
||||
// With quotes
|
||||
auto result2 = lexer.lex(u8R"(程序 "中文 参数" '另一个"引号"参数')");
|
||||
// Result: [u8"程序", u8"中文 参数", u8"另一个\"引号\"参数"]
|
||||
\endcode
|
||||
|
||||
\section lexer61__empty_arguments Empty Arguments
|
||||
|
||||
Empty arguments can be represented with empty quotes:
|
||||
|
||||
\code
|
||||
Lexer61 lexer;
|
||||
auto result = lexer.lex(u8R"(program "" '')");
|
||||
|
||||
// Result: [u8"program", u8"", u8""]
|
||||
\endcode
|
||||
|
||||
\section lexer61__error_handling Error Handling
|
||||
|
||||
The lexer uses \c std::expected for error handling:
|
||||
|
||||
\code
|
||||
Lexer61 lexer;
|
||||
auto result = lexer.lex(u8R"(program "unclosed quote)");
|
||||
|
||||
if (!result.has_value()) {
|
||||
// Handle error - in this case, unclosed quote
|
||||
std::cerr << "Error: unclosed quote" << std::endl;
|
||||
std::abort();
|
||||
}
|
||||
\endcode
|
||||
|
||||
*/
|
||||
}
|
||||
202
doc/src/carton/pycodec.dox
Normal file
202
doc/src/carton/pycodec.dox
Normal file
@@ -0,0 +1,202 @@
|
||||
namespace yycc::carton::pycodec {
|
||||
/**
|
||||
\page pycodec Unified Codec (Python-like Codec)
|
||||
|
||||
\section pycodec__overview Overview
|
||||
|
||||
The unified encoding conversion module provides a consistent interface for character encoding conversion across different platforms.
|
||||
It automatically selects the appropriate backend implementation based on the platform and available features.
|
||||
|
||||
\section pycodec__classes Available Classes
|
||||
|
||||
\subsection pycodec__classes__char Character to/from UTF-8 Conversion
|
||||
|
||||
Convert between named encodings and UTF-8 using a unified interface:
|
||||
|
||||
\code
|
||||
#include <yycc/carton/pycodec.hpp>
|
||||
|
||||
// Example: Converting from a named encoding to UTF-8
|
||||
CharToUtf8 converter("GBK"); // or "ISO-8859-1", "SHIFT-JIS", etc.
|
||||
|
||||
std::string gbk_text = "你好,世界!";
|
||||
auto result = converter.to_utf8(gbk_text);
|
||||
if (result.has_value()) {
|
||||
std::u8string utf8_text = result.value();
|
||||
// Use utf8_text...
|
||||
} else {
|
||||
// Handle conversion error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\code
|
||||
// Example: Converting from UTF-8 to a named encoding
|
||||
Utf8ToChar converter("GBK");
|
||||
|
||||
std::u8string utf8_text = u8"Hello, 世界!";
|
||||
auto result = converter.to_char(utf8_text);
|
||||
if (result.has_value()) {
|
||||
std::string gbk_text = result.value();
|
||||
// Use gbk_text...
|
||||
} else {
|
||||
// Handle conversion error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\subsection pycodec__classes__wchar Wide Character to/from UTF-8 Conversion
|
||||
|
||||
Convert between wide character strings and UTF-8:
|
||||
|
||||
\code
|
||||
#include <yycc/carton/pycodec.hpp>
|
||||
|
||||
// Example: Converting wide character to UTF-8
|
||||
WcharToUtf8 converter;
|
||||
|
||||
std::wstring wide_text = L"Hello, 世界!";
|
||||
auto result = converter.to_utf8(wide_text);
|
||||
if (result.has_value()) {
|
||||
std::u8string utf8_text = result.value();
|
||||
// Use utf8_text...
|
||||
} else {
|
||||
// Handle conversion error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\code
|
||||
// Example: Converting UTF-8 to wide character
|
||||
Utf8ToWchar converter;
|
||||
|
||||
std::u8string utf8_text = u8"Hello, 世界!";
|
||||
auto result = converter.to_wchar(utf8_text);
|
||||
if (result.has_value()) {
|
||||
std::wstring wide_text = result.value();
|
||||
// Use wide_text...
|
||||
} else {
|
||||
// Handle conversion error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\subsection pycodec__classes__utf16_utf32 UTF-8 to/from UTF-16/UTF-32 Conversion
|
||||
|
||||
Convert between UTF encodings:
|
||||
|
||||
\code
|
||||
#include <yycc/carton/pycodec.hpp>
|
||||
|
||||
// Example: Converting UTF-8 to UTF-16
|
||||
Utf8ToUtf16 converter;
|
||||
|
||||
std::u8string utf8_text = u8"Hello, 世界!";
|
||||
auto result = converter.to_utf16(utf8_text);
|
||||
if (result.has_value()) {
|
||||
std::u16string utf16_text = result.value();
|
||||
// Use utf16_text...
|
||||
} else {
|
||||
// Handle conversion error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\code
|
||||
// Example: Converting UTF-16 to UTF-8
|
||||
Utf16ToUtf8 converter;
|
||||
|
||||
std::u16string utf16_text = u"Hello, 世界!";
|
||||
auto result = converter.to_utf8(utf16_text);
|
||||
if (result.has_value()) {
|
||||
std::u8string utf8_text = result.value();
|
||||
// Use utf8_text...
|
||||
} else {
|
||||
// Handle conversion error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\code
|
||||
// Example: Converting UTF-8 to UTF-32
|
||||
Utf8ToUtf32 converter;
|
||||
|
||||
std::u8string utf8_text = u8"Hello, 世界! 🌍";
|
||||
auto result = converter.to_utf32(utf8_text);
|
||||
if (result.has_value()) {
|
||||
std::u32string utf32_text = result.value();
|
||||
// Use utf32_text...
|
||||
} else {
|
||||
// Handle conversion error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\code
|
||||
// Example: Converting UTF-32 to UTF-8
|
||||
Utf32ToUtf8 converter;
|
||||
|
||||
std::u32string utf32_text = U"Hello, 世界! 🌍";
|
||||
auto result = converter.to_utf8(utf32_text);
|
||||
if (result.has_value()) {
|
||||
std::u8string utf8_text = result.value();
|
||||
// Use utf8_text...
|
||||
} else {
|
||||
// Handle conversion error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section pycodec__utility Utility Functions
|
||||
|
||||
\subsection pycodec__utility__validation Encoding Name Validation
|
||||
|
||||
Check if an encoding name is valid in the current environment:
|
||||
|
||||
\code
|
||||
#include <yycc/carton/pycodec.hpp>
|
||||
|
||||
// Example: Validating an encoding name
|
||||
bool is_valid = is_valid_encoding_name(u8"UTF-8");
|
||||
if (is_valid) {
|
||||
std::cout << "UTF-8 is a valid encoding name\n";
|
||||
} else {
|
||||
std::cout << "UTF-8 is not supported\n";
|
||||
}
|
||||
|
||||
// Test another encoding
|
||||
is_valid = is_valid_encoding_name(u8"GBK");
|
||||
\endcode
|
||||
|
||||
\section pycodec__error_handling Error Handling
|
||||
|
||||
All functions in this module return a result containing either
|
||||
a ConvError struct represents conversion errors, or the final converted string.
|
||||
|
||||
\code
|
||||
#include <yycc/carton/pycodec.hpp>
|
||||
|
||||
CharToUtf8 converter("INVALID_ENCODING_NAME");
|
||||
std::string text = "Hello";
|
||||
|
||||
auto result = converter.to_utf8(text);
|
||||
|
||||
if (result.has_value()) {
|
||||
std::u8string converted = result.value();
|
||||
// Process successfully converted string
|
||||
} else {
|
||||
// Handle conversion failure
|
||||
std::cout << "Conversion failed\n";
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section pycodec__backend_specifics Platform-Specific Backends
|
||||
|
||||
For detailed information about the specific platform backends, see:
|
||||
|
||||
\li \ref encoding__windows : Windows-specific implementation using Win32 APIs
|
||||
\li \ref encoding__iconv : Iconv-based implementation for POSIX-like systems
|
||||
|
||||
\section pycodec__notes Notes
|
||||
|
||||
For all supported encoding names and their aliases,
|
||||
please browse code written in <TT>script/pycodec</TT> located in our source code.
|
||||
|
||||
Please also note that not all encoding name has implementation for all platforms.
|
||||
Some uncommon encoding names are not supported on some backend due to the limitations of the corresponding baskend.
|
||||
These also can be found in that directory introduced above.
|
||||
|
||||
*/
|
||||
}
|
||||
107
doc/src/carton/tabulate.dox
Normal file
107
doc/src/carton/tabulate.dox
Normal file
@@ -0,0 +1,107 @@
|
||||
namespace yycc::carton::tabulate {
|
||||
/**
|
||||
|
||||
\page tabulate Tabulate Utilities
|
||||
|
||||
This namespace provides utilities for creating formatted tables in console output.
|
||||
It supports Unicode text, automatic column width calculation, customizable headers,
|
||||
and flexible display options.
|
||||
|
||||
\section tabulate__basic_usage Basic Usage
|
||||
|
||||
To create a simple table with headers and data rows:
|
||||
|
||||
\code
|
||||
#include "yycc/carton/tabulate.hpp"
|
||||
using namespace yycc::carton::tabulate;
|
||||
|
||||
// Create a table with 3 columns
|
||||
Tabulate table(3);
|
||||
|
||||
// Set the header
|
||||
table.set_header({u8"Name", u8"Age", u8"City"});
|
||||
|
||||
// Add data rows
|
||||
table.add_row({u8"Alice", u8"30", u8"New York"});
|
||||
table.add_row({u8"Bob", u8"25", u8"Los Angeles"});
|
||||
table.add_row({u8"Charlie", u8"35", u8"Chicago"});
|
||||
|
||||
// Print the table
|
||||
table.print();
|
||||
\endcode
|
||||
|
||||
This will output:
|
||||
\verbatim
|
||||
Name Age City
|
||||
----- --- ---------
|
||||
Alice 30 New York
|
||||
Bob 25 Los Angeles
|
||||
Charlie 35 Chicago
|
||||
\endverbatim
|
||||
|
||||
\section tabulate__customization Customization Options
|
||||
|
||||
You can customize various aspects of the table:
|
||||
|
||||
\code
|
||||
Tabulate table(3);
|
||||
|
||||
// Set a custom separator bar
|
||||
table.set_bar(u8"===");
|
||||
|
||||
// Add a prefix (useful for indentation)
|
||||
table.set_prefix(u8"> ");
|
||||
|
||||
// Control header and bar visibility
|
||||
table.show_header(false); // Hide header
|
||||
table.show_bar(true); // Show separator bar
|
||||
|
||||
// Set header
|
||||
table.set_header({u8"Col1", u8"Col2", u8"Col3"});
|
||||
|
||||
// Add data
|
||||
table.add_row({u8"data1", u8"data2", u8"data3"});
|
||||
|
||||
table.print();
|
||||
\endcode
|
||||
|
||||
\section tabulate__unicode_support Unicode Support
|
||||
|
||||
The library fully supports Unicode text in tables:
|
||||
|
||||
\code
|
||||
Tabulate table(3);
|
||||
|
||||
// Set Unicode header
|
||||
table.set_header({u8"姓名", u8"年龄", u8"城市"});
|
||||
|
||||
// Add Unicode data
|
||||
table.add_row({u8"张三", u8"30", u8"北京"});
|
||||
table.add_row({u8"李四", u8"25", u8"上海"});
|
||||
|
||||
table.print();
|
||||
\endcode
|
||||
|
||||
\section tabulate__stream_output Stream Output
|
||||
|
||||
You can print to any output stream, not just stdout:
|
||||
|
||||
\code
|
||||
#include <fstream>
|
||||
|
||||
Tabulate table(2);
|
||||
table.set_header({u8"Key", u8"Value"});
|
||||
table.add_row({u8"temp", u8"25C"});
|
||||
|
||||
// Print to file
|
||||
std::ofstream file("table.txt");
|
||||
table.print(file);
|
||||
|
||||
// Or print to stringstream
|
||||
std::stringstream ss;
|
||||
table.print(ss);
|
||||
std::string table_str = ss.str();
|
||||
\endcode
|
||||
|
||||
*/
|
||||
}
|
||||
121
doc/src/carton/termcolor.dox
Normal file
121
doc/src/carton/termcolor.dox
Normal file
@@ -0,0 +1,121 @@
|
||||
namespace yycc::carton::termcolor {
|
||||
/**
|
||||
|
||||
\page termcolor Terminal Color Utilities
|
||||
|
||||
This namespace provides functions to generate ANSI escape sequence for terminal font color and style.
|
||||
It also provides functions to add color and style for given string with ANSI Escape Sequence.
|
||||
|
||||
Supported color is limited in 16 colors,
|
||||
because these color is implemented by ASCII Escape Code: https://en.wikipedia.org/wiki/ANSI_escape_code .
|
||||
So if your terminal do not support this, such as default Windows terminal, or teletypewriter,
|
||||
you will see some unrecognised characters surrounding with your output.
|
||||
That's ASCII Escape Code.
|
||||
|
||||
This namespace is basically the imitation of the Python package with same name.
|
||||
|
||||
\section termcolor__basic_colors Basic Colors
|
||||
|
||||
To use basic foreground and background colors:
|
||||
|
||||
\code
|
||||
#include "yycc/carton/termcolor.hpp"
|
||||
using namespace yycc::carton::termcolor;
|
||||
|
||||
// Print red text
|
||||
std::cout << colored(u8"Error message", Color::Red) << std::endl;
|
||||
|
||||
// Print blue text with yellow background
|
||||
cprint(u8"Info message", Color::Blue, Color::Yellow);
|
||||
\endcode
|
||||
|
||||
\section termcolor__light_colors Light Colors
|
||||
|
||||
The namespace provides both standard and light versions of colors:
|
||||
|
||||
\code
|
||||
// Standard green
|
||||
std::cout << colored(u8"Success", Color::Green) << std::endl;
|
||||
|
||||
// Light green (brighter variant)
|
||||
std::cout << colored(u8"Notice", Color::LightGreen) << std::endl;
|
||||
\endcode
|
||||
|
||||
\section termcolor__text_styles Text Styles
|
||||
|
||||
Multiple text styles can be combined using bitwise operations:
|
||||
|
||||
\code
|
||||
// Bold text
|
||||
std::cout << colored(u8"Important", Color::Red, Color::Default, Attribute::Bold) << std::endl;
|
||||
|
||||
// Underlined text
|
||||
std::cout << colored(u8"Underlined", Color::Blue, Color::Default, Attribute::Underline) << std::endl;
|
||||
|
||||
// Combined styles
|
||||
auto combined_style = yycc::cenum::merge(Attribute::Bold, Attribute::Italic);
|
||||
cprint(u8"Bold and italic", Color::Magenta, Color::Default, combined_style);
|
||||
\endcode
|
||||
|
||||
\section termcolor__convenience_functions Convenience Functions
|
||||
|
||||
Several convenience functions are available for direct printing:
|
||||
|
||||
\code
|
||||
// Print to stdout with color
|
||||
cprint(u8"Hello World", Color::Green);
|
||||
|
||||
// Print to stderr with color and style
|
||||
ceprint(u8"Warning", Color::Yellow, Color::Default, Attribute::Bold);
|
||||
|
||||
// Print with line break
|
||||
cprintln(u8"Success", Color::LightGreen);
|
||||
|
||||
// Print to stderr with line break
|
||||
ceprintln(u8"Critical Error", Color::LightRed, Color::Default, Attribute::Blink);
|
||||
\endcode
|
||||
|
||||
\section termcolor__color_enums Available Colors
|
||||
|
||||
Available foreground and background colors include:
|
||||
|
||||
\li Color::Black
|
||||
\li Color::Red
|
||||
\li Color::Green
|
||||
\li Color::Yellow
|
||||
\li Color::Blue
|
||||
\li Color::Magenta
|
||||
\li Color::Cyan
|
||||
\li Color::White
|
||||
\li Color::LightBlack
|
||||
\li Color::LightRed
|
||||
\li Color::LightGreen
|
||||
\li Color::LightYellow
|
||||
\li Color::LightBlue
|
||||
\li Color::LightMagenta
|
||||
\li Color::LightCyan
|
||||
\li Color::LightWhite
|
||||
\li Color::Default
|
||||
|
||||
\section termcolor__style_enums Available Styles
|
||||
|
||||
Available text styles include:
|
||||
|
||||
\li Attribute::Bold
|
||||
\li Attribute::Dark
|
||||
\li Attribute::Italic
|
||||
\li Attribute::Underline
|
||||
\li Attribute::Blink
|
||||
\li Attribute::Reverse
|
||||
\li Attribute::Concealed
|
||||
\li Attribute::Default
|
||||
|
||||
\section termcolor__old_macros Where is Old Macros
|
||||
|
||||
If you have used YYCC 1.x version, you may know that these features are also presented but in a bunch of macros style.
|
||||
These macros is removed since YYCC 2.0.
|
||||
Since YYCC 2.0, we suggest you to use these new provided functions instead,
|
||||
because they are more robust and correspond with our new style of coding by \c std::format and etc.
|
||||
|
||||
*/
|
||||
}
|
||||
103
doc/src/carton/wcwidth.dox
Normal file
103
doc/src/carton/wcwidth.dox
Normal file
@@ -0,0 +1,103 @@
|
||||
namespace yycc::carton::wcwidth {
|
||||
/**
|
||||
|
||||
\page wcwidth Cross-platform Wcwidth
|
||||
|
||||
This namespace provides cross-platform implementations of character width calculation functions,
|
||||
similar to the Linux-specific \c wcswidth function. It supports Unicode text and ANSI escape sequences,
|
||||
making it suitable for calculating display widths of text in terminals across different platforms.
|
||||
|
||||
\section wcwidth__basic_usage Basic Usage
|
||||
|
||||
To calculate the display width of a string in a terminal:
|
||||
|
||||
\code
|
||||
#include <yycc/carton/wcwidth.hpp>
|
||||
using namespace yycc::carton::wcwidth;
|
||||
|
||||
// Calculate width of ASCII text
|
||||
size_t width1 = wcwidth(U'a'); // Returns 1
|
||||
auto width2 = wcswidth(u8"Hello"); // Returns 5
|
||||
|
||||
// Calculate width of Unicode text
|
||||
auto width3 = wcswidth(u8"你好世界"); // Returns 8 (Chinese chars typically take 2 spaces each)
|
||||
auto width4 = wcswidth(u8"ありがとう"); // Returns 10 (Japanese katakana)
|
||||
\endcode
|
||||
|
||||
\section wcwidth__ansi_support ANSI Escape Sequence Support
|
||||
|
||||
The library can handle ANSI escape sequences (like color codes) in text
|
||||
which is not supported by Linux \c wcswidth.
|
||||
|
||||
\code
|
||||
#include "yycc/carton/termcolor.hpp"
|
||||
using namespace yycc::carton::termcolor;
|
||||
|
||||
// Calculate width of colored text
|
||||
auto colored_text = colored(u8"Hello World", Color::Red);
|
||||
auto width = wcswidth(colored_text);
|
||||
// Returns the width of "Hello World" (ignoring the ANSI escape sequences)
|
||||
\endcode
|
||||
|
||||
\section wcwidth__error_handling Error Handling
|
||||
|
||||
The functions use \c std::expected for error handling:
|
||||
|
||||
\code
|
||||
#include "yycc/carton/wcwidth.hpp"
|
||||
|
||||
// Safe way to handle potential errors
|
||||
auto result = wcswidth(u8"\033?"); // Invalid ANSI sequence
|
||||
|
||||
if (result.has_value()) {
|
||||
size_t width = result.value();
|
||||
std::cout << "Width: " << width << std::endl;
|
||||
} else {
|
||||
std::cout << "Invalid string" << std::endl;
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section wcwidth__character_types Supported Character Types
|
||||
|
||||
The library provides two main functions:
|
||||
|
||||
- wcwidth(): Calculate width of a single character.
|
||||
- wcswidth(): Calculate width of a string.
|
||||
|
||||
\code
|
||||
// Using wcwidth for single characters
|
||||
size_t char_width = wcwidth(U'A'); // Returns 1
|
||||
size_t emoji_width = wcwidth(U'😀'); // Returns width of emoji
|
||||
|
||||
// Using wcswidth for strings
|
||||
auto str_width1 = wcswidth(u8"Hello"); // Returns 5
|
||||
auto str_width2 = wcswidth(U"Unicode String"); // Returns string length in display width
|
||||
\endcode
|
||||
|
||||
\section wcwidth__platform_differences Platform Considerations
|
||||
|
||||
This library addresses platform differences in wide character handling:
|
||||
|
||||
\li On Linux, \c whar_t is 4 bytes and can represent any Unicode character
|
||||
\li On Windows, \c whar_t is 2 bytes and may require surrogate pairs for some characters
|
||||
|
||||
So this library uses \c char32_t internally to ensure consistent behavior across platforms,
|
||||
and expose functions with \c char32_t and \c char8_t string container respectively for user.
|
||||
|
||||
\section wcwidth__unicode_support Unicode and East Asian Widths
|
||||
|
||||
The library properly handles East Asian character widths:
|
||||
|
||||
\code
|
||||
// Chinese characters typically occupy 2 terminal spaces
|
||||
auto chinese_width = wcswidth(u8"中文"); // Returns 4 (2 chars × 2 spaces each)
|
||||
|
||||
// Japanese characters
|
||||
auto kana_width = wcswidth(u8"ありがとう"); // Returns 10 (5 kana × 2 spaces each)
|
||||
|
||||
// Mixed text
|
||||
auto mixed_width = wcswidth(u8"Hello 世界"); // Returns 8 (5 ASCII + 1 space + 2 Chinese chars × 2 spaces)
|
||||
\endcode
|
||||
|
||||
*/
|
||||
}
|
||||
37
doc/src/cenum.dox
Normal file
37
doc/src/cenum.dox
Normal file
@@ -0,0 +1,37 @@
|
||||
namespace yycc::cenum {
|
||||
/**
|
||||
|
||||
\page cenum Scoped Enum Helper
|
||||
|
||||
\section cenum__intro Intro
|
||||
|
||||
C++ introduce a new enum called scoped enum.
|
||||
It is better than legacy C enum because it will not leak name into namespace where it locate,
|
||||
and also can specify an underlying type to it to make sure it is stored as specified size.
|
||||
However, the shortcoming of it is that it lack bitwise operator comparing with legacy C enum.
|
||||
Programmer must implement them for scoped enum one by one but it is a hardship and inconvenient.
|
||||
This is the reason why I invent this class.
|
||||
And this is the reason why I call this module "cenum"
|
||||
because it gives scoped enum type with the same abilities of legacy C enum.
|
||||
|
||||
\section cenum__Usage Usage
|
||||
|
||||
In this namespace, we provide all bitwise functions related to scoped enum type which may be used.
|
||||
See yycc::cenum for more detail (It is more clear to read function annotation than I introduce in there repeatedly).
|
||||
|
||||
\section cenum__why Why not Operator Overload Way
|
||||
|
||||
I have try it (and you even can see the relic of it in source code).
|
||||
But it need a extra statement written in following to include it, otherwise compiler can not see it.
|
||||
|
||||
\code
|
||||
using namespace yycc::cenum;
|
||||
\endcode
|
||||
|
||||
The last and most important reason why I do not use this method is that
|
||||
this overload strategy may be applied to some type which should not be applied by accient, such as non-scoped enum type.
|
||||
So I gave up this solution.
|
||||
It is much better that order user explicitly specify when to use them.
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
/**
|
||||
|
||||
\page config_manager Universal Config Manager
|
||||
|
||||
*/
|
||||
@@ -1,78 +0,0 @@
|
||||
/**
|
||||
|
||||
\page dialog_helper Dialog Helper
|
||||
|
||||
Picking files and folders is an important and essential operation under Windows.
|
||||
However the functions picking files and folders are so complex.
|
||||
This helper provides universal dialog picker by simple classes and functions.
|
||||
In following contents we will tell you how to call them.
|
||||
|
||||
This helper is Windows specific.
|
||||
It will be totally invisible if you are in other platforms.
|
||||
|
||||
\section dialog_helper__file_dialog Configure File Dialog
|
||||
|
||||
The first thing is that we should initialize YYCC::DialogHelper::FileDialog,
|
||||
and configure it according to your requirements.
|
||||
|
||||
This class is the data struct representing all aspects of file dialog.
|
||||
It also one of the arguments in final dialog function.
|
||||
|
||||
\code
|
||||
YYCC::DialogHelper::FileDialog params;
|
||||
params.SetOwner(owner_getter());
|
||||
params.SetTitle(YYCC_U8("My File Picker"));
|
||||
params.SetInitFileName(YYCC_U8("test.txt"));
|
||||
params.SetInitDirectory(initial_directory_getter());
|
||||
\endcode
|
||||
|
||||
\subsection dialog_helper__file_dialog__owner Owner
|
||||
|
||||
YYCC::DialogHelper::FileDialog::SetOwner will set owner of this dialog.
|
||||
It accepts a Microsoft defined \c HWND as argument which should be familiar with Windows programmer.
|
||||
If you pass \c NULL to it or skip calling this function, it indicate that there is no owner of this dialog.
|
||||
<I>
|
||||
I don't what whill happend if there is no owner for it.
|
||||
But it would be better to have an owner if possible.
|
||||
</I>
|
||||
|
||||
\subsection dialog_helper__file_dialog__title Title
|
||||
|
||||
YYCC::DialogHelper::FileDialog::SetTitle will set dialog title of this dialog.
|
||||
If you pass \c nullptr or skip calling it,
|
||||
the title of dialog will be filled by system and the function type you calling.
|
||||
For example, the title will be "Open..." if you call open file function,
|
||||
and will be "Save As..." if you call save file function.
|
||||
At the same time, the language of this title filled by system is system UI dependent.
|
||||
It means that you do not need to do any extra I18N work for it.
|
||||
So I suggest you do not set title except you really want to modify title.
|
||||
|
||||
\subsection dialog_helper__file_dialog__init_file_name Initial File Name
|
||||
|
||||
YYCC::DialogHelper::FileDialog::SetInitFileName will set the initial file name presented in dialog file name input box.
|
||||
If you pass \c nullptr or skip calling it, the text in dialog file name input box will be empty.
|
||||
|
||||
User can modify the name presented in input box later.
|
||||
But if you assign this value, the dialog will lose the ability that remember the previous name user input in previous calling.
|
||||
In normal case, dialog will try remembering the file name user input in dialog, and represent it in the next calling.
|
||||
However, if you specify this field, the dialog will always presented your specified value in every calling.
|
||||
|
||||
\subsection dialog_helper__file_dialog__init_directory Initial Directory
|
||||
|
||||
YYCC::DialogHelper::FileDialog::SetInitDirectory will set the initial directory (startup directory) when opening dialog.
|
||||
|
||||
In following cases, initial directory will fall back to system behavior:
|
||||
|
||||
\li Pass \c nullptr to this function.
|
||||
\li Skip calling this function.
|
||||
\li Given directory path is invalid.
|
||||
|
||||
The system default behavior of initial directory is similar with initial file name.
|
||||
The dialog will try remembering the last directory you just entering, and will back into it in the next calling.
|
||||
The directory we meeting in the first launch is system defined.
|
||||
|
||||
\section dialog_helper__file_filters Configure File Filters
|
||||
|
||||
\section dialog_helper__result Create Dialog and Get Result
|
||||
|
||||
*/
|
||||
166
doc/src/encoding/iconv.dox
Normal file
166
doc/src/encoding/iconv.dox
Normal file
@@ -0,0 +1,166 @@
|
||||
namespace yycc::encoding::iconv {
|
||||
/**
|
||||
\page encoding__iconv Iconv-based Codec
|
||||
|
||||
\section encoding__iconv__overview Overview
|
||||
|
||||
The Iconv-based encoding conversion module provides encoding conversion functionality using the iconv library.
|
||||
This module is available when you are in POSIX system, or enable iconv support manually when configuring the library.
|
||||
|
||||
\section encoding__iconv__classes Available Classes
|
||||
|
||||
\subsection encoding__iconv__classes__char Char to/from UTF-8 Conversion
|
||||
|
||||
Convert between character encodings and UTF-8:
|
||||
|
||||
\code
|
||||
#include <yycc/encoding/iconv.hpp>
|
||||
|
||||
// Example: Creating a converter from Latin-1 to UTF-8
|
||||
CharToUtf8 converter("ISO-8859-1");
|
||||
|
||||
std::string latin1_text = "Café résumé naïve";
|
||||
auto result = converter.to_utf8(latin1_text);
|
||||
if (result.has_value()) {
|
||||
std::u8string utf8_text = result.value();
|
||||
// Use utf8_text...
|
||||
} else {
|
||||
// Handle conversion error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\code
|
||||
// Example: Creating a converter from UTF-8 to Latin-1
|
||||
Utf8ToChar converter("ISO-8859-1");
|
||||
|
||||
std::u8string utf8_text = u8"Café résumé naïve";
|
||||
auto result = converter.to_char(utf8_text);
|
||||
if (result.has_value()) {
|
||||
std::string latin1_text = result.value();
|
||||
// Use latin1_text...
|
||||
} else {
|
||||
// Handle conversion error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\subsection encoding__iconv__classes__wchar WChar to/from UTF-8 Conversion
|
||||
|
||||
Convert between wide character and UTF-8:
|
||||
|
||||
\code
|
||||
#include <yycc/encoding/iconv.hpp>
|
||||
|
||||
// Example: Converting wide character to UTF-8
|
||||
WcharToUtf8 converter;
|
||||
|
||||
std::wstring wide_text = L"Hello, 世界!";
|
||||
auto result = converter.to_utf8(wide_text);
|
||||
if (result.has_value()) {
|
||||
std::u8string utf8_text = result.value();
|
||||
// Use utf8_text...
|
||||
} else {
|
||||
// Handle conversion error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\code
|
||||
// Example: Converting UTF-8 to wide character
|
||||
Utf8ToWchar converter;
|
||||
|
||||
std::u8string utf8_text = u8"Hello, 世界!";
|
||||
auto result = converter.to_wchar(utf8_text);
|
||||
if (result.has_value()) {
|
||||
std::wstring wide_text = result.value();
|
||||
// Use wide_text...
|
||||
} else {
|
||||
// Handle conversion error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\subsection encoding__iconv__classes__utf16_utf32 UTF-8 to/from UTF-16/UTF-32 Conversion
|
||||
|
||||
Convert between UTF encodings:
|
||||
|
||||
\code
|
||||
#include <yycc/encoding/iconv.hpp>
|
||||
|
||||
// Example: Converting UTF-8 to UTF-16
|
||||
Utf8ToUtf16 converter;
|
||||
|
||||
std::u8string utf8_text = u8"Hello, 世界!";
|
||||
auto result = converter.to_utf16(utf8_text);
|
||||
if (result.has_value()) {
|
||||
std::u16string utf16_text = result.value();
|
||||
// Use utf16_text...
|
||||
} else {
|
||||
// Handle conversion error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\code
|
||||
// Example: Converting UTF-16 to UTF-8
|
||||
Utf16ToUtf8 converter;
|
||||
|
||||
std::u16string utf16_text = u"Hello, 世界!";
|
||||
auto result = converter.to_utf8(utf16_text);
|
||||
if (result.has_value()) {
|
||||
std::u8string utf8_text = result.value();
|
||||
// Use utf8_text...
|
||||
} else {
|
||||
// Handle conversion error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\code
|
||||
// Example: Converting UTF-8 to UTF-32
|
||||
Utf8ToUtf32 converter;
|
||||
|
||||
std::u8string utf8_text = u8"Hello, 世界! 🌍";
|
||||
auto result = converter.to_utf32(utf8_text);
|
||||
if (result.has_value()) {
|
||||
std::u32string utf32_text = result.value();
|
||||
// Use utf32_text...
|
||||
} else {
|
||||
// Handle conversion error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\code
|
||||
// Example: Converting UTF-32 to UTF-8
|
||||
Utf32ToUtf8 converter;
|
||||
|
||||
std::u32string utf32_text = U"Hello, 世界! 🌍";
|
||||
auto result = converter.to_utf8(utf32_text);
|
||||
if (result.has_value()) {
|
||||
std::u8string utf8_text = result.value();
|
||||
// Use utf8_text...
|
||||
} else {
|
||||
// Handle conversion error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section encoding__iconv__error_handling Error Handling
|
||||
|
||||
All functions in this module return a result containing either
|
||||
a ConvError struct represents conversion errors, or the final converted string.
|
||||
|
||||
\code
|
||||
#include <yycc/encoding/iconv.hpp>
|
||||
|
||||
CharToUtf8 converter("INVALID_ENCODING");
|
||||
// Note: Constructor errors might be detected during conversion
|
||||
|
||||
std::string text = "Hello";
|
||||
auto result = converter.to_utf8(text);
|
||||
|
||||
if (result.has_value()) {
|
||||
std::u8string converted = result.value();
|
||||
// Process successfully converted string
|
||||
} else {
|
||||
// Handle conversion failure
|
||||
std::cout << "Conversion failed\n";
|
||||
}
|
||||
\endcode
|
||||
|
||||
*/
|
||||
}
|
||||
98
doc/src/encoding/stl.dox
Normal file
98
doc/src/encoding/stl.dox
Normal file
@@ -0,0 +1,98 @@
|
||||
namespace yycc::encoding::stl {
|
||||
/**
|
||||
\page encoding__stl STL-based Codec
|
||||
|
||||
\section encoding__stl__overview Overview
|
||||
|
||||
The STL-based encoding conversion module provides cross-platform encoding conversion functionality using the standard library's codecvt facets.
|
||||
This module is designed to handle conversions between UTF-8, UTF-16, and UTF-32 encodings using the standard C++ locale facilities.
|
||||
|
||||
\section encoding__stl__attentions Attentions
|
||||
|
||||
The underlying implementation of this module is deprecated by C++ STL and may be removed in future versions of C++.
|
||||
So please use this module carefully or considering use our \ref pycodec module instead.
|
||||
|
||||
\section encoding__stl__functions Available Functions
|
||||
|
||||
\subsection encoding__stl__functions__utf16 UTF-8 to/from UTF-16 Conversion
|
||||
|
||||
Convert between UTF-8 and UTF-16 encodings using standard library facilities:
|
||||
|
||||
\code
|
||||
#include <yycc/encoding/stl.hpp>
|
||||
|
||||
// Example: Converting UTF-8 to UTF-16
|
||||
std::u8string utf8_text = u8"Hello, 世界!";
|
||||
auto result = to_utf16(utf8_text);
|
||||
if (result.has_value()) {
|
||||
std::u16string utf16_text = result.value();
|
||||
// Use utf16_text...
|
||||
} else {
|
||||
// Handle conversion error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\code
|
||||
// Example: Converting UTF-16 to UTF-8
|
||||
std::u16string utf16_text = u"Hello, 世界!";
|
||||
auto result = to_utf8(utf16_text);
|
||||
if (result.has_value()) {
|
||||
std::u8string utf8_text = result.value();
|
||||
// Use utf8_text...
|
||||
} else {
|
||||
// Handle conversion error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\subsection encoding__stl__functions__utf32 UTF-8 to/from UTF-32 Conversion
|
||||
|
||||
Convert between UTF-8 and UTF-32 encodings:
|
||||
|
||||
\code
|
||||
#include <yycc/encoding/stl.hpp>
|
||||
|
||||
// Example: Converting UTF-8 to UTF-32
|
||||
std::u8string utf8_text = u8"Hello, 世界! 🌍";
|
||||
auto result = to_utf32(utf8_text);
|
||||
if (result.has_value()) {
|
||||
std::u32string utf32_text = result.value();
|
||||
// Use utf32_text...
|
||||
} else {
|
||||
// Handle conversion error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\code
|
||||
// Example: Converting UTF-32 to UTF-8
|
||||
std::u32string utf32_text = U"Hello, 世界! 🌍";
|
||||
auto result = to_utf8(utf32_text);
|
||||
if (result.has_value()) {
|
||||
std::u8string utf8_text = result.value();
|
||||
// Use utf8_text...
|
||||
} else {
|
||||
// Handle conversion error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section encoding__stl__error_handling Error Handling
|
||||
|
||||
All functions in this module return a result containing either
|
||||
a ConvError struct represents conversion errors, or the final converted string.
|
||||
|
||||
\code
|
||||
#include <yycc/encoding/stl.hpp>
|
||||
|
||||
std::u8string invalid_utf8 = "\xFF\xFE"; // Invalid UTF-8 sequence
|
||||
auto result = to_utf16(invalid_utf8);
|
||||
|
||||
if (result.has_value()) {
|
||||
std::u16string converted = result.value();
|
||||
// Process successfully converted string
|
||||
} else {
|
||||
// Handle conversion failure
|
||||
std::cout << "Conversion failed\n";
|
||||
}
|
||||
\endcode
|
||||
|
||||
*/
|
||||
}
|
||||
191
doc/src/encoding/windows.dox
Normal file
191
doc/src/encoding/windows.dox
Normal file
@@ -0,0 +1,191 @@
|
||||
namespace yycc::encoding::windows {
|
||||
/**
|
||||
\page encoding__windows Win32-based Codec
|
||||
|
||||
\section encoding__windows__overview Overview
|
||||
|
||||
The Windows-specific encoding conversion module provides encoding conversion functionality
|
||||
using Windows API functions such as `WideCharToMultiByte` and `MultiByteToWideChar`.
|
||||
This module is available only on Windows platforms and offers efficient conversion
|
||||
between various character encodings including wide character, multi-byte, and UTF-8.
|
||||
|
||||
\section encoding__windows__functions Available Functions
|
||||
|
||||
\subsection encoding__windows__functions__wchar Wide Character to/from Multi-byte Conversion
|
||||
|
||||
Convert between wide character strings and multi-byte strings using Windows code pages:
|
||||
|
||||
\code
|
||||
#include <yycc/encoding/windows.hpp>
|
||||
|
||||
// Example: Converting wide character string to multi-byte with specific code page
|
||||
std::wstring wide_text = L"Hello, 世界!";
|
||||
auto result = to_char(wide_text, CP_UTF8); // Using UTF-8 code page
|
||||
if (result.has_value()) {
|
||||
std::string multi_byte_text = result.value();
|
||||
// Use multi_byte_text...
|
||||
} else {
|
||||
// Handle conversion error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\code
|
||||
// Example: Converting multi-byte string to wide character with specific code page
|
||||
std::string multi_byte_text = "Hello, 世界!";
|
||||
auto result = to_wchar(multi_byte_text, CP_UTF8);
|
||||
if (result.has_value()) {
|
||||
std::wstring wide_text = result.value();
|
||||
// Use wide_text...
|
||||
} else {
|
||||
// Handle conversion error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\subsection encoding__windows__functions__mbcs Multi-byte to/from Multi-byte Conversion
|
||||
|
||||
Convert between different multi-byte encodings by using wide character as an intermediate:
|
||||
|
||||
\code
|
||||
#include <yycc/encoding/windows.hpp>
|
||||
|
||||
// Example: Converting between two different code pages
|
||||
std::string source_text = "Hello, world!";
|
||||
auto result = to_char(source_text, CP_ACP, CP_UTF8); // ANSI to UTF-8
|
||||
if (result.has_value()) {
|
||||
std::string utf8_text = result.value();
|
||||
// Use converted UTF-8 text...
|
||||
} else {
|
||||
// Handle conversion error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\subsection encoding__windows__functions__utf8 UTF-8 Specific Conversions
|
||||
|
||||
Specialized functions for UTF-8 conversion without requiring explicit code page specification:
|
||||
|
||||
\code
|
||||
#include <yycc/encoding/windows.hpp>
|
||||
|
||||
// Example: Converting wide character to UTF-8
|
||||
std::wstring wide_text = L"Hello, 世界!";
|
||||
auto result = to_utf8(wide_text);
|
||||
if (result.has_value()) {
|
||||
std::u8string utf8_text = result.value();
|
||||
// Use utf8_text...
|
||||
} else {
|
||||
// Handle conversion error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\code
|
||||
// Example: Converting UTF-8 to wide character
|
||||
std::u8string utf8_text = u8"Hello, 世界!";
|
||||
auto result = to_wchar(utf8_text);
|
||||
if (result.has_value()) {
|
||||
std::wstring wide_text = result.value();
|
||||
// Use wide_text...
|
||||
} else {
|
||||
// Handle conversion error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\code
|
||||
// Example: Converting multi-byte to UTF-8
|
||||
std::string multi_byte_text = "Hello, world!";
|
||||
auto result = to_utf8(multi_byte_text, CP_ACP);
|
||||
if (result.has_value()) {
|
||||
std::u8string utf8_text = result.value();
|
||||
// Use utf8_text...
|
||||
} else {
|
||||
// Handle conversion error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\code
|
||||
// Example: Converting UTF-8 to multi-byte
|
||||
std::u8string utf8_text = u8"Hello, world!";
|
||||
auto result = to_char(utf8_text, CP_ACP);
|
||||
if (result.has_value()) {
|
||||
std::string multi_byte_text = result.value();
|
||||
// Use multi_byte_text...
|
||||
} else {
|
||||
// Handle conversion error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\subsection encoding__windows__functions__utf16_utf32 UTF-8 to/from UTF-16/UTF-32 Conversion
|
||||
|
||||
Available on Windows with Microsoft STL for conversion between UTF encodings:
|
||||
|
||||
\code
|
||||
#include <yycc/encoding/windows.hpp>
|
||||
|
||||
// Example: Converting UTF-8 to UTF-16
|
||||
std::u8string utf8_text = u8"Hello, 世界!";
|
||||
auto result = to_utf16(utf8_text);
|
||||
if (result.has_value()) {
|
||||
std::u16string utf16_text = result.value();
|
||||
// Use utf16_text...
|
||||
} else {
|
||||
// Handle conversion error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\code
|
||||
// Example: Converting UTF-16 to UTF-8
|
||||
std::u16string utf16_text = u"Hello, 世界!";
|
||||
auto result = to_utf8(utf16_text);
|
||||
if (result.has_value()) {
|
||||
std::u8string utf8_text = result.value();
|
||||
// Use utf8_text...
|
||||
} else {
|
||||
// Handle conversion error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\code
|
||||
// Example: Converting UTF-8 to UTF-32
|
||||
std::u8string utf8_text = u8"Hello, 世界! 🌍";
|
||||
auto result = to_utf32(utf8_text);
|
||||
if (result.has_value()) {
|
||||
std::u32string utf32_text = result.value();
|
||||
// Use utf32_text...
|
||||
} else {
|
||||
// Handle conversion error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\code
|
||||
// Example: Converting UTF-32 to UTF-8
|
||||
std::u32string utf32_text = U"Hello, 世界! 🌍";
|
||||
auto result = to_utf8(utf32_text);
|
||||
if (result.has_value()) {
|
||||
std::u8string utf8_text = result.value();
|
||||
// Use utf8_text...
|
||||
} else {
|
||||
// Handle conversion error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section encoding__windows__error_handling Error Handling
|
||||
|
||||
All functions in this module return a result containing either
|
||||
a ConvError struct represents conversion errors, or the final converted string.
|
||||
|
||||
\code
|
||||
#include <yycc/encoding/windows.hpp>
|
||||
|
||||
std::wstring invalid_text = /* some problematic string */;
|
||||
auto result = to_char(invalid_text, CP_UTF8);
|
||||
|
||||
if (result.has_value()) {
|
||||
std::string converted = result.value();
|
||||
// Process successfully converted string
|
||||
} else {
|
||||
// Handle conversion failure
|
||||
std::cout << "Conversion failed\n";
|
||||
}
|
||||
\endcode
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
/**
|
||||
|
||||
\page encoding_helper Encoding Helper
|
||||
|
||||
YYCC::EncodingHelper namespace include all encoding related functions:
|
||||
|
||||
\li The convertion between native string and UTF8 string which has been introduced in chapter \ref library_encoding.
|
||||
\li Windows specific convertion between \c WCHAR, UTF8 string and string encoded by other encoding.
|
||||
\li The convertion among UTF8, UTF16 and UTF32.
|
||||
|
||||
*/
|
||||
85
doc/src/env.dox
Normal file
85
doc/src/env.dox
Normal file
@@ -0,0 +1,85 @@
|
||||
namespace yycc::env {
|
||||
/**
|
||||
|
||||
\page env Environment Operations
|
||||
|
||||
This namespace provide various environment operations inspired by Rust standard library.
|
||||
|
||||
\section env__var Environment Variable Operations
|
||||
|
||||
These functions allow manipulation of environment variables with proper error handling using \c std::expected.
|
||||
|
||||
\li get_var(): Get the value of a given environment variable name
|
||||
\li set_var(): Set the value of a given environment variable name
|
||||
\li del_var(): Delete environment variable with given name
|
||||
\li get_vars(): Get all environment variables as a list of name-value pairs
|
||||
|
||||
There is an example usage of these functions below:
|
||||
|
||||
\code
|
||||
#include <yycc/env.hpp>
|
||||
|
||||
auto result = yycc::env::get_var(u8"PATH");
|
||||
if (result.has_value()) {
|
||||
auto value = result.value();
|
||||
// Use the value...
|
||||
} else {
|
||||
// Handle the error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section env__path Path Operations
|
||||
|
||||
These functions provide access to various important paths in the system environment.
|
||||
|
||||
\li current_dir(): Returns the current working directory
|
||||
\li current_exe(): Returns the path of the current running executable
|
||||
\li home_dir(): Returns the path of the current user's home directory if known
|
||||
\li temp_dir(): Returns the path of a temporary directory
|
||||
|
||||
There is an example usage of these functions below:
|
||||
|
||||
\code
|
||||
#include <yycc/env.hpp>
|
||||
#include <yycc/patch/stream.hpp>
|
||||
#include <iostream>
|
||||
|
||||
using namespace yycc::patch::stream;
|
||||
|
||||
auto cwd_result = yycc::env::current_dir();
|
||||
if (cwd_result.has_value()) {
|
||||
std::cout << "Current directory: " << cwd_result.value() << std::endl;
|
||||
}
|
||||
|
||||
auto exe_result = yycc::env::current_exe();
|
||||
if (exe_result.has_value()) {
|
||||
std::cout << "Executable path: " << exe_result.value() << std::endl;
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section env__arg Command Line Argument Operations
|
||||
|
||||
These functions provide access to command-line arguments passed to the program.
|
||||
|
||||
\li get_args(): Returns the arguments that this program was started with
|
||||
|
||||
There is an example usage of these functions below:
|
||||
|
||||
\code
|
||||
#include <yycc/env.hpp>
|
||||
#include <yycc/patch/stream.hpp>
|
||||
#include <iostream>
|
||||
|
||||
using namespace yycc::patch::stream;
|
||||
|
||||
auto args_result = yycc::env::get_args();
|
||||
if (args_result.has_value()) {
|
||||
auto args = args_result.value();
|
||||
for (auto& arg : args) {
|
||||
std::cout << "Arg: " << arg << std::endl;
|
||||
}
|
||||
}
|
||||
\endcode
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
<TD><CENTER>
|
||||
<B>YYCCommonplace Programming Manual</B>
|
||||
|
||||
Copyright 2024 by yyc12345.
|
||||
Copyright 2024-2026 by yyc12345.
|
||||
</CENTER></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
@@ -25,28 +25,80 @@
|
||||
<TR>
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">
|
||||
|
||||
<B>General Features</B>
|
||||
<B>Overviews</B>
|
||||
|
||||
\li \subpage intro
|
||||
|
||||
\li \subpage library_encoding
|
||||
\li \subpage premise_and_principle
|
||||
|
||||
\li \subpage encoding_helper
|
||||
<B>STL Enhancements</B>
|
||||
|
||||
\li \subpage string_helper
|
||||
\li \subpage macro
|
||||
|
||||
<B>Advanced Features</B>
|
||||
\li \subpage cenum
|
||||
|
||||
\li \subpage config_manager
|
||||
\li \subpage string__reinterpret
|
||||
|
||||
\li \subpage string__op
|
||||
|
||||
\li \subpage num__parser
|
||||
|
||||
\li \subpage num__op
|
||||
|
||||
\li \subpage num__safe_cast
|
||||
|
||||
\li \subpage num__safe_op
|
||||
|
||||
\li \subpage patch
|
||||
|
||||
\li \subpage env
|
||||
|
||||
\li \subpage rust
|
||||
|
||||
<B>Text Encoding</B>
|
||||
|
||||
\li \subpage encoding__stl
|
||||
|
||||
\li \subpage encoding__windows
|
||||
|
||||
\li \subpage encoding__iconv
|
||||
|
||||
\li \subpage pycodec
|
||||
|
||||
</TD>
|
||||
<TD ALIGN="LEFT" VALIGN="TOP">
|
||||
|
||||
<B>Advanced Features (Carton)</B>
|
||||
|
||||
\li \subpage termcolor
|
||||
|
||||
\li \subpage csconsole
|
||||
|
||||
\li \subpage ironpad
|
||||
|
||||
\li \subpage clap
|
||||
|
||||
\li \subpage binstore
|
||||
|
||||
\li \subpage fft
|
||||
|
||||
\li \subpage lexer61
|
||||
|
||||
\li \subpage wcwidth
|
||||
|
||||
\li \subpage tabulate
|
||||
|
||||
<B>Windows Specific Features</B>
|
||||
|
||||
\li \subpage win_import
|
||||
\li \subpage windows__import_guard
|
||||
|
||||
\li \subpage dialog_helper
|
||||
\li \subpage windows__com
|
||||
|
||||
\li \subpage windows__dialog
|
||||
|
||||
\li \subpage windows__winfct
|
||||
|
||||
\li \subpage windows__console
|
||||
|
||||
</TD>
|
||||
</TR>
|
||||
|
||||
@@ -2,14 +2,33 @@
|
||||
|
||||
\page intro Introduction to YYCCommonplace
|
||||
|
||||
YYCCommonplace, or YYC Commonplace (abbr. YYCC), is a static library providing various useful C++ functions when programming with standard library or Windows environment.
|
||||
YYCCommonplace, or YYC Commonplace (abbr. YYCC),
|
||||
is a static library providing various useful C++ functions
|
||||
when programming with standard library or Windows environment.
|
||||
|
||||
Actually YYCC provides the functions which I frequently used in my personal projects.
|
||||
Thus I do not need copy these functions from one project to another project.
|
||||
I can write them once and use them everywhere.
|
||||
It's also good for bug fix.
|
||||
If I found bug in these code, I only need to fix it in this project.
|
||||
Otherwise I need to fix them one by one in each project because they share the same code.
|
||||
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.
|
||||
After this, I do not need to write these duplicated code in each project.
|
||||
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.
|
||||
Almost of my projects will gradually adapt to this project and drop their own individual implementations.
|
||||
|
||||
\section intro__why Why YYCCommonplace
|
||||
|
||||
@@ -20,12 +39,12 @@ During programming, I found Windows is super lack in UTF8 supports.
|
||||
Programmer loves UTF8, because it can handle all charcaters over the world in one encoding and is still compatible with C-Style string.
|
||||
However, Windows use a weird way to achieve internationalization, 2 different function trailing, A and W for legacy code and modern code respectively.
|
||||
The worst things is that the char type W trailing function used, \c WCHAR, is defined as 2 bytes long, not 4 bytes long as Linux does (\c wchar_t).
|
||||
It mean that one emoji charcater will be torn into 2 \c WCHAR on Windows because emoji code unit is higher than the manimum value of \c WCHAR.
|
||||
It mean that one emoji charcater will be torn into 2 \c WCHAR on Windows because emoji code unit is higher than the maximum value of \c WCHAR.
|
||||
|
||||
Also, there are various issues which should not be presented.
|
||||
For example, Microsoft invents various \e safe standard library functions to prevent possible overflow issues raised by \c std::fgets and etc.
|
||||
also, MSVC may throw weird error when you using some specific standard library functions.
|
||||
You need to define some weird macro to disable this shitty behavior.
|
||||
You need to define some weird macros to disable this shitty behavior.
|
||||
|
||||
There are various non-standard issue you may faced on Windows programming.
|
||||
All in all, programming on Windows is a tough work.
|
||||
@@ -37,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
|
||||
@@ -56,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.
|
||||
@@ -65,12 +85,119 @@ 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.
|
||||
Otherwise you may make mistake during using this library.
|
||||
I suggest you read this manual from top to bottom in the left tree panel, one by one.
|
||||
|
||||
This library is a static library.
|
||||
YYCC library self provides some build scripts for convenient use which are located in \c script directory.
|
||||
Please note all of these script should be executed in the root of YYCC project, not the script directory
|
||||
(i.e. work directory is the root directory of YYCC).
|
||||
All scripts will try to do a simple check about this if you accidently execute them in a wrong place.
|
||||
|
||||
If you are not willing to use our build script, or our build script went wrong,
|
||||
you can create your personal build script by viewing our build script.
|
||||
|
||||
\subsection intro__usage__linux Linux
|
||||
|
||||
Building YYCC on Linux is easy to do by executing <TT>script/linux_build.sh</TT>.
|
||||
After script done, you will find installation result in directory <TT>bin/install</TT>.
|
||||
Then other CMake project can utilize it (non-CMake project also can utilize this).
|
||||
|
||||
\subsection intro__usage__win Windows
|
||||
|
||||
For building on Windows, there are 2 distribution types which YYCC can create.
|
||||
First is CMake distribution, this distribution is served for other CMake project using.
|
||||
Another one is MSVC distribution, this distribution is served for other MSVC project using.
|
||||
These have different directory layout which is specifically designed for corresponding build tools.
|
||||
See following section for more details.
|
||||
|
||||
\subsection intro__usage__win__execute Execute Build Script
|
||||
|
||||
For creating distribution on Windows, please execute script <TT>python3 script/gen_win_build.py</TT> first.
|
||||
Then execute <TT>script/win_build.bat</TT> to generate final result.
|
||||
|
||||
\c script/gen_win_build.py is the generator of \c script/win_build.bat.
|
||||
It will accept various arguments and generate a proper real build script for you.
|
||||
Currently \c script/gen_win_build.py supports following arguments:
|
||||
|
||||
\li \c -c, \c --cpp \c [cpp_version]: Specify the version of C++ standard for building.
|
||||
Due to the different defination of UTF8 char type,
|
||||
C++ 20 program can not use this library built by C++ 17 environment.
|
||||
So this switch give you a chance to decide the version of C++ standard used when building.
|
||||
The lowest and defult version of C++ standard is 17.
|
||||
\li \c -d, \c --build-doc: Specify this if you want to build documentation.
|
||||
End user usually needs documentation,
|
||||
however if you are the developer of this library, you may need this switch.
|
||||
Because documentation take too much disk space and cost a bunch of time for building and copying.
|
||||
In default, generator will produce script which do not build documentation automatically.
|
||||
\li \c -p, \c --pic: Enable Position Independent Code flag on non-Windows platfotm.
|
||||
This flag is crucial to linking this library to another dynamic library.
|
||||
If you do not specify this flag, the linking process will fail.
|
||||
|
||||
After script done, you will find CMake distribution in directory <TT>bin/<I>cpp_ver</I>/install</TT>.
|
||||
and you will also find your MSVC distribution in directory <TT>bin/<I>cpp_ver</I>/msvc_install</TT>.
|
||||
\e cpp_ver in path will be replaced by the C++ version you specified.
|
||||
|
||||
\subsubsection intro__usage__win__cmake CMake Distribution
|
||||
|
||||
CMake distribution has following directory structure.
|
||||
|
||||
\verbatim
|
||||
YYCC
|
||||
├─Win32_Debug: Win32 Debug package
|
||||
│ ├─include: Headers
|
||||
│ └─lib: Library for linking and CMake package file
|
||||
├─Win32_Release: Win32 Release package
|
||||
│ ├─bin: Executable testbench
|
||||
│ ├─include: Headers
|
||||
│ └─lib: Library for linking and CMake package file
|
||||
├─x64_Debug: x64 Debug package
|
||||
│ ├─include: Headers
|
||||
│ └─lib: Library for linking and CMake package file
|
||||
└─x64_Release: x64 Release package
|
||||
├─bin: Executable testbench
|
||||
├─include: Headers
|
||||
├─lib: Library for linking and CMake package file
|
||||
└─share: Documentation
|
||||
\endverbatim
|
||||
|
||||
Every different architecture and build type have a single and full directory.
|
||||
CMake project can use one of by adding their build type in \c find_package path.
|
||||
So that CMake will automatically utilize correct package when switching build type.
|
||||
|
||||
\subsubsection intro__usage__win__msvc MSVC Distribution
|
||||
|
||||
MSVC distribution has following directory structure.
|
||||
|
||||
\verbatim
|
||||
YYCC
|
||||
├─bin
|
||||
│ ├─Win32: Win32 Release testbench
|
||||
│ └─x64: x64 Release testbench
|
||||
├─include: Headers
|
||||
├─lib
|
||||
│ ├─Win32
|
||||
│ │ ├─Debug: Win32 Debug library for linking
|
||||
│ │ └─Release: Win32 Release library for linking
|
||||
│ └─x64
|
||||
│ ├─Debug: x64 Debug library for linking
|
||||
│ └─Release: x64 Release library for linking
|
||||
└─share: Documentation
|
||||
\endverbatim
|
||||
|
||||
The different between MSVC distribution and CMake distribution is
|
||||
that MSVC distribution places all static library under one director \c lib.
|
||||
Thus in MSVC project user can simply spcify the install path of YYCC,
|
||||
and use MSVC macros in path to choose correct static library for linking
|
||||
|
||||
\section intro__debug Debug Tips
|
||||
|
||||
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.
|
||||
-->
|
||||
*/
|
||||
|
||||
@@ -1,191 +0,0 @@
|
||||
/**
|
||||
|
||||
\page library_encoding Library Encoding
|
||||
|
||||
Before using this library, you should know the encoding strategy of this library first.
|
||||
In short words, this library use UTF8 encoding everywhere except some special cases,
|
||||
for example, function explicitly order the encoding of input parameters.
|
||||
|
||||
In following content of this article, you will know the details about how we use UTF8 in this library.
|
||||
|
||||
\section library_encoding__utf8_type UTF8 Type
|
||||
|
||||
YYCC uses custom UTF8 char type, string container and string view all over the library, from parameters to return value.
|
||||
Following content will introduce how we define them.
|
||||
|
||||
\subsection library_encoding__utf8_type__char_type Char Type
|
||||
|
||||
YYCC library has its own UTF8 char type, \c yycc_char8_t.
|
||||
This is how we define it:
|
||||
|
||||
\code
|
||||
#if defined(__cpp_char8_t)
|
||||
using yycc_char8_t = char8_t;
|
||||
#else
|
||||
using yycc_char8_t = unsigned char;
|
||||
#endif
|
||||
\endcode
|
||||
|
||||
If your environment (higher or equal to C++ 20) supports \c char8_t provided by standard library, \c yycc_char8_t is just an alias to \c char8_t,
|
||||
otherwise (lower than C++ 20, e.g. C++ 17), \c yycc_char8_t will be defined as \c unsigned \c char like C++ 20 does (this can be seen as a polyfill).
|
||||
|
||||
This means that if you already have used \c char8_t provided by standard library,
|
||||
you do not need to do any extra modification before using this library.
|
||||
Because all types are compatible.
|
||||
|
||||
\subsection library_encoding__utf8_type__container_type String Container and View
|
||||
|
||||
We define string container and string view like this:
|
||||
|
||||
\code
|
||||
using yycc_u8string = std::basic_string<yycc_char8_t>;
|
||||
using yycc_u8string_view = std::basic_string_view<yycc_char8_t>;
|
||||
\endcode
|
||||
|
||||
The real code written in library may be slightly different with this but they have same meanings.
|
||||
|
||||
In \c char8_t environment, they are just the alias to \c std::u8string and \c std::u8string_view respectively.
|
||||
So if you have already used them, no need to any modification for your code before using this library.
|
||||
|
||||
\subsection library_encoding__utf8_type__why Why?
|
||||
|
||||
You may curious why I create a new UTF8 char type, rather than using standard library UTF8 char type directly. There are 2 reasons.
|
||||
|
||||
First, It was too late that I notice I can use standard library UTF8 char type.
|
||||
My UTF8 char type has been used in library everywhere and its tough to fully replace them into standard library UTF8 char type.
|
||||
|
||||
Second, UTF8 related content of standard library is \e volatile.
|
||||
I notice standard library change UTF8 related functions frequently and its API are not stable.
|
||||
For example, standard library brings \c std::codecvt_utf8 in C++ 11, deprecate it in C++ 17 and even remove it in C++ 26.
|
||||
That's unacceptable! So I create my own UTF8 type to avoid the scenario that standard library remove \c char8_t in future.
|
||||
|
||||
\section library_encoding__utf8_literal UTF8 Literal
|
||||
|
||||
String literal is a C++ concept.
|
||||
If you are not familar with it, please browse related article first, such as CppReference.
|
||||
|
||||
\subsection library_encoding__utf8_literal__single Single Literal
|
||||
|
||||
In short words, YYCC allow you declare an UTF8 literal like this:
|
||||
|
||||
\code
|
||||
YYCC_U8("This is UTF8 literal.")
|
||||
\endcode
|
||||
|
||||
YYCC_U8 is macro.
|
||||
You don't need add extra \c u8 prefix in string given to the macro.
|
||||
This macro will do this automatically.
|
||||
|
||||
In detail, this macro do a \c reinterpret_cast to change the type of given argument to \c const \c yycc_char8_t* forcely.
|
||||
This ensure that declared UTF8 literal is compatible with YYCC UTF8 types.
|
||||
|
||||
\subsection library_encoding__utf8_literal__concatenation Literal Concatenation
|
||||
|
||||
YYCC_U8 macro also works for string literal concatenation:
|
||||
|
||||
\code
|
||||
YYCC_U8("Error code: " PRIu32 ". Please contact me.");
|
||||
\endcode
|
||||
|
||||
According to C++ standard for string literal concatenation,
|
||||
<I>"If one of the strings has an encoding prefix and the other does not, the one that does not will be considered to have the same encoding prefix as the other."</I>
|
||||
At the same time, YYCC_U8 macro will automatically add \c u8 prefix for the first component of this string literal concatenation.
|
||||
So the whole string will be UTF8 literal.
|
||||
It also order you should \b not add any prefix for other components of this string literal concatenation.
|
||||
|
||||
\subsection library_encoding__utf8_literal__why Why?
|
||||
|
||||
You may know that C++ standard allows programmer declare an UTF8 literal explicitly by writing code like this:
|
||||
|
||||
\code
|
||||
u8"foo bar"
|
||||
\endcode
|
||||
|
||||
This is okey. But it may incompatible with YYCC UTF8 char type.
|
||||
According to C++ standard, this UTF8 literal syntax will only return \c const \c char8_t* if your C++ standard higher or equal to C++ 20,
|
||||
otherwise it will return \c const \c char*.
|
||||
This behavior cause that you can not assign this UTF8 literal to \c yycc_u8string if you are in the environment which do not support \c char8_t,
|
||||
because their types are different.
|
||||
Thereas you can not use the functions provided by this library because they are all use YYCC defined UTF8 char type.
|
||||
|
||||
\section library_encoding__utf8_pointer UTF8 String Pointer
|
||||
|
||||
String pointer means the raw pointer pointing to a string, such as \c const \c char*, \c char*, \c char32_t* and etc.
|
||||
|
||||
Many legacy code assume \c char* is encoded with UTF8 (the exception is Windows). But \c char* is incompatible with \c yycc_char8_t.
|
||||
YYCC provides YYCC::EncodingHelper::ToUTF8 to resolve this issue. There is an exmaple:
|
||||
|
||||
\code
|
||||
const char* absolutely_is_utf8 = "I confirm this is encoded with UTF8.";
|
||||
const yycc_char8_t* converted = YYCC::EncodingHelper::ToUTF8(absolutely_is_utf8);
|
||||
|
||||
char* mutable_utf8 = const_cast<char*>(absolutely_is_utf8); // This is not safe. Just for example.
|
||||
yycc_char8_t* mutable_converted = YYCC::EncodingHelper::ToUTF8(mutable_utf8);
|
||||
\endcode
|
||||
|
||||
YYCC::EncodingHelper::ToUTF8 has 2 overloads which can handle const and mutable stirng pointer convertion respectively.
|
||||
|
||||
YYCC also has ability that convert YYCC UTF8 char type to native char type by YYCC::EncodingHelper::ToNative.
|
||||
Here is an exmaple:
|
||||
|
||||
\code
|
||||
const yycc_char8_t* yycc_utf8 = YYCC_U8("I am UTF8 string.");
|
||||
const char* converted = YYCC::EncodingHelper::ToNative(yycc_utf8);
|
||||
|
||||
yycc_char8_t* mutable_yycc_utf8 = const_cast<char*>(yycc_utf8); // Not safe. Also just for example.
|
||||
char* mutable_converted = YYCC::EncodingHelper::ToNative(mutable_yycc_utf8);
|
||||
\endcode
|
||||
|
||||
Same as YYCC::EncodingHelper::ToUTF8, YYCC::EncodingHelper::ToNative also has 2 overloads to handle const and mutable string pointer.
|
||||
|
||||
\section library_encoding__utf8_container UTF8 String Container
|
||||
|
||||
String container usually means the standard library string container, such as \c std::string, \c std::wstring, \c std::u32string and etc.
|
||||
|
||||
In many personal project, programmer may use \c std::string everywhere because \c std::u8string may not be presented when writing peoject.
|
||||
How to do convertion between native string container and YYCC UTF8 string container?
|
||||
It is definitely illegal that directly do force convertion. Because they may have different class layout.
|
||||
Calm down and I will tell you how to do correct convertion.
|
||||
YYCC provides YYCC::EncodingHelper::ToUTF8 to convert native string container to YYCC UTF8 string container.
|
||||
There is an exmaple:
|
||||
|
||||
\code
|
||||
std::string native_string("I am UTF8");
|
||||
yycc_u8string yycc_string = YYCC::EncodingHelper::ToUTF8(native_string);
|
||||
auto result = YYCC::EncodingHelper::UTF8ToUTF32(yycc_string);
|
||||
\endcode
|
||||
|
||||
Actually, YYCC::EncodingHelper::ToUTF8 accepts a reference to \c std::string_view as argument.
|
||||
However, there is a implicit convertion from \c std::string to \c std::string_view,
|
||||
so you can directly pass a \c std::string instance to it.
|
||||
|
||||
String view will reduce unnecessary memory copy.
|
||||
If you just want to pass native string container to function, and this function accepts \c yycc_u8string_view as its argument,
|
||||
you can use alternative YYCC::EncodingHelper::ToUTF8View.
|
||||
|
||||
\code
|
||||
std::string native_string("I am UTF8");
|
||||
yycc_u8string_view yycc_string = YYCC::EncodingHelper::ToUTF8View(native_string);
|
||||
auto result = YYCC::EncodingHelper::UTF8ToUTF32(yycc_string);
|
||||
\endcode
|
||||
|
||||
Comparing with previous one, this example use less memory.
|
||||
The reduced memory is the content of \c yycc_string because string view is a view, not the copy of original string.
|
||||
|
||||
Same as UTF8 string pointer, we also have YYCC::EncodingHelper::ToNative and YYCC::EncodingHelper::ToNativeView do correspondant reverse convertion.
|
||||
Try to do your own research and figure out how to use them.
|
||||
It's pretty easy.
|
||||
|
||||
\section library_encoding__windows Warnings to Windows Programmer
|
||||
|
||||
Due to the legacy of MSVC, the encoding of \c char* may not be UTF8 in most cases.
|
||||
If you run the convertion code introduced in this article with the string which is not encoded with UTF8, it may cause undefined behavior.
|
||||
|
||||
To enable UTF8 mode of MSVC, please deliver \c /utf-8 switch to MSVC.
|
||||
Thus you can use the functions introduced in this article safely.
|
||||
Otherwise, you must guarteen that the argument you provided to these functions is encoded by UTF8 manually.
|
||||
|
||||
Linux user do not need care this.
|
||||
Because almost Linux distro use UTF8 in default.
|
||||
|
||||
*/
|
||||
323
doc/src/macro.dox
Normal file
323
doc/src/macro.dox
Normal file
@@ -0,0 +1,323 @@
|
||||
namespace yycc::macro {
|
||||
/**
|
||||
|
||||
\page macro Library Macros
|
||||
|
||||
In this page we will introduce the macros defined by this library
|
||||
which can not be grouped in other topic.
|
||||
|
||||
\section macro__version Library Version
|
||||
|
||||
Version is a important things in modern software development, especially for a library.
|
||||
In YYCC, we use Semantic Versioning as our version standard.
|
||||
For more infomations about it, please see: https://semver.org/
|
||||
|
||||
First, YYCC has its own version and it can be visited by
|
||||
\c YYCC_VER_MAJOR, \c YYCC_VER_MINOR, and \c YYCC_VER_PATCH.
|
||||
Each part of Semantic Versioning is provided individually.
|
||||
|
||||
\section macro__version_cmp Version Comparison
|
||||
|
||||
YYCC also provide a bunch of macros to compare 2 versions.
|
||||
It also provides a way to check YYCC version in program using YYCC,
|
||||
because some of them rely on a specific version of YYCC.
|
||||
There is a list of these comparison macros.
|
||||
|
||||
\li YYCC_VERCMP_E
|
||||
\li YYCC_VERCMP_NE
|
||||
\li YYCC_VERCMP_G
|
||||
\li YYCC_VERCMP_GE
|
||||
\li YYCC_VERCMP_NL
|
||||
\li YYCC_VERCMP_L
|
||||
\li YYCC_VERCMP_LE
|
||||
\li YYCC_VERCMP_NG
|
||||
|
||||
You may notice all of these macros are all start with \c YYCC_VERCMP_,
|
||||
and their tails are inspired from x86 ASM comparison jump code.
|
||||
For example, \c E means "equal" and \c NE means "not equal",
|
||||
\c G means "greater", \c GE means "greater or equal", and \c NG means "not gretaer".
|
||||
|
||||
All of these macros take 6 arguments,
|
||||
for the first 3 arguments, we call them "left version".
|
||||
From left to right they are the major part, minor part and patch part of semantic version.
|
||||
And for the last 3 arguments, we call them "right version".
|
||||
From left to right they are the major part, minor part and patch part of semantic version.
|
||||
There is a example about checking whether YYCC library version is exactly what we wanted version.
|
||||
|
||||
\code
|
||||
#if YYCC_VERCMP_NE(YYCC_VER_MAJOR, YYCC_VER_MINOR, YYCC_VER_PATCH, 1, 3 ,0)
|
||||
#error "Not Matched YYCC Version"
|
||||
#endif
|
||||
\endcode
|
||||
|
||||
\section macro__copy_move Class Copy / Move Functions
|
||||
|
||||
YYCC provides several macros to manage copy and move constructors and assignment operators for classes.
|
||||
These include macros to delete, default, declare, and implement copy and move operations.
|
||||
|
||||
<UL>
|
||||
<LI>
|
||||
\c YYCC_DELETE_COPY(CLSNAME): Explicitly remove copy constructor and copy assignment operator for the given class.
|
||||
<UL>
|
||||
<LI><TT>CLSNAME(const CLSNAME&) = delete;</TT></LI>
|
||||
<LI><TT>CLSNAME& operator=(const CLSNAME&) = delete;</TT></LI>
|
||||
</UL>
|
||||
</LI>
|
||||
<LI>
|
||||
\c YYCC_DELETE_MOVE(CLSNAME): Explicitly remove move constructor and move assignment operator for the given class.
|
||||
<UL>
|
||||
<LI><TT>CLSNAME(CLSNAME&&) noexcept = delete;</TT></LI>
|
||||
<LI><TT>CLSNAME& operator=(CLSNAME&&) noexcept = delete;</TT></LI>
|
||||
</UL>
|
||||
</LI>
|
||||
<LI>\c YYCC_DELETE_COPY_MOVE(CLSNAME): The combination of \c YYCC_DELETE_COPY and \c YYCC_DELETE_MOVE.</LI>
|
||||
<LI>
|
||||
\c YYCC_DEFAULT_COPY(CLSNAME): Explicitly set default copy constructor and copy assignment operator for the given class.
|
||||
<UL>
|
||||
<LI><TT>CLSNAME(const CLSNAME&) = default;</TT></LI>
|
||||
<LI><TT>CLSNAME& operator=(const CLSNAME&) = default;</TT></LI>
|
||||
</UL>
|
||||
</LI>
|
||||
<LI>
|
||||
\c YYCC_DEFAULT_MOVE(CLSNAME): Explicitly set default move constructor and move assignment operator for the given class.
|
||||
<UL>
|
||||
<LI><TT>CLSNAME(CLSNAME&&) noexcept = default;</TT></LI>
|
||||
<LI><TT>CLSNAME& operator=(CLSNAME&&) noexcept = default;</TT></LI>
|
||||
</UL>
|
||||
</LI>
|
||||
<LI>\c YYCC_DEFAULT_COPY_MOVE(CLSNAME): The combination of \c YYCC_DEFAULT_COPY and \c YYCC_DEFAULT_MOVE.</LI>
|
||||
<LI>
|
||||
\c YYCC_DECL_COPY(CLSNAME): Make declaration of copy constructor and assignment operator for the given class to avoid typos.
|
||||
<UL>
|
||||
<LI><TT>CLSNAME(const CLSNAME&);</TT></LI>
|
||||
<LI><TT>CLSNAME& operator=(const CLSNAME&);</TT></LI>
|
||||
</UL>
|
||||
</LI>
|
||||
<LI>
|
||||
\c YYCC_DECL_MOVE(CLSNAME): Make declaration of move constructor and assignment operator for the given class to avoid typos.
|
||||
<UL>
|
||||
<LI><TT>CLSNAME(CLSNAME&&) noexcept;</TT></LI>
|
||||
<LI><TT>CLSNAME& operator=(CLSNAME&&) noexcept;</TT></LI>
|
||||
</UL>
|
||||
</LI>
|
||||
<LI>\c YYCC_DECL_COPY_MOVE(CLSNAME): The combination of \c YYCC_DECL_COPY and \c YYCC_DECL_MOVE.</LI>
|
||||
<LI>\c YYCC_IMPL_COPY_CTOR(CLSNAME, RHS): Make implementation signature of copy constructor for the given class with the right operand name to avoid typos.</LI>
|
||||
<LI>\c YYCC_IMPL_COPY_OPER(CLSNAME, RHS): Make implementation signature of copy assignment operator for the given class with the right operand name to avoid typos.</LI>
|
||||
<LI>\c YYCC_IMPL_MOVE_CTOR(CLSNAME, RHS): Make implementation signature of move constructor for the given class with the right operand name to avoid typos.</LI>
|
||||
<LI>\c YYCC_IMPL_MOVE_OPER(CLSNAME, RHS): Make implementation signature of move assignment operator for the given class with the right operand name to avoid typos.</LI>
|
||||
</UL>
|
||||
|
||||
Please note that \c YYCC_DECL_ and \c YYCC_IMPL_ should be used together.
|
||||
These macros are designed to make sure that you write correct function signatures.
|
||||
There is an example about how to use it.
|
||||
In HPP file, you can write:
|
||||
|
||||
\code
|
||||
class Foo {
|
||||
YYCC_DECL_COPY_MOVE(Foo)
|
||||
};
|
||||
\endcode
|
||||
|
||||
And in corresponding CPP file, you should write:
|
||||
|
||||
\code
|
||||
YYCC_IMPL_COPY_CTOR(Foo, rhs)
|
||||
{
|
||||
// Copy members from rhs
|
||||
}
|
||||
YYCC_IMPL_COPY_OPER(Foo, rhs)
|
||||
{
|
||||
// Copy members from rhs
|
||||
return *this;
|
||||
}
|
||||
YYCC_IMPL_MOVE_CTOR(Foo, rhs)
|
||||
{
|
||||
// Move members from rhs
|
||||
}
|
||||
YYCC_IMPL_MOVE_OPER(Foo, rhs)
|
||||
{
|
||||
// Move members from rhs
|
||||
return *this;
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section macro__platform_checker OS Detector
|
||||
|
||||
In many cross platform applications,
|
||||
programmer usually write code adapted to different platforms in one source file
|
||||
and enable them respectively by macros representing the target platform.
|
||||
As a cross platform library,
|
||||
YYCC also has this feature and you can utilize it if you don't have other ways to so the same things.
|
||||
|
||||
\subsection macro__platform_checker__macro Macro
|
||||
|
||||
YYCC always define <B>one of following macros</B> to indicate the system of target platform.
|
||||
|
||||
\li \c YYCC_OS_WINDOWS: Windows environment.
|
||||
\li \c YYCC_OS_LINUX: Linux environment.
|
||||
\li \c YYCC_OS_MACOS: macOS environment.
|
||||
|
||||
Assume \c blabla() function is Windows specific.
|
||||
There is an example about how to use it:
|
||||
|
||||
\code
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
// Code specific to Windows
|
||||
blabla();
|
||||
#endif
|
||||
\endcode
|
||||
|
||||
\subsection macro__platform_checker__constexpr_function Constexpr Function
|
||||
|
||||
Additionally, YYCC also provides a bunch of constexpr functions to check whether the target platform is what we want.
|
||||
More precisely, os::get_os() function returns an enum value os::OsKind indicating the target platform.
|
||||
|
||||
There is an example about how to use it:
|
||||
|
||||
\code
|
||||
if constexpr (os::get_os() == os::OsKind::Windows) {
|
||||
// Code specific to Windows
|
||||
blabla();
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section macro__compiler_detector Compiler Detector
|
||||
|
||||
YYCC provides macros and constexpr functions to detect the compiler being used for compilation.
|
||||
|
||||
\subsection macro__compiler_detector__macro Macro
|
||||
|
||||
YYCC defines <B>one of following macros</B> to indicate which compiler is being used.
|
||||
|
||||
\li \c YYCC_CC_MSVC: MSVC compiler (Microsoft Visual C++)
|
||||
\li \c YYCC_CC_GCC: GCC compiler (GNU Compiler Collection)
|
||||
\li \c YYCC_CC_CLANG: Clang compiler
|
||||
|
||||
There is an example about how to use it:
|
||||
|
||||
\code
|
||||
#if defined(YYCC_CC_MSVC)
|
||||
// Code specific to MSVC
|
||||
blabla();
|
||||
#endif
|
||||
\endcode
|
||||
|
||||
\subsection macro__compiler_detector__constexpr_function Constexpr Function
|
||||
|
||||
YYCC also provides a constexpr function to check which compiler is being used at compile time.
|
||||
More precisely, compiler::get_compiler() function returns an enum value compiler::CompilerKind indicating the compiler being used.
|
||||
|
||||
There is an example about how to use it:
|
||||
|
||||
\code
|
||||
if constexpr (compiler::get_compiler() == compiler::CompilerKind::Msvc) {
|
||||
// Code specific to MSVC
|
||||
blabla();
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section macro__endian_detector Endian Detector
|
||||
|
||||
YYCC provides macros and constexpr functions to detect the endianness of the target platform.
|
||||
|
||||
\subsection macro__endian_detector__macro Macro
|
||||
|
||||
YYCC always defines <B>one of following macros</B> to indicate the endianness of the target platform.
|
||||
|
||||
\li \c YYCC_ENDIAN_LITTLE: Little endian system
|
||||
\li \c YYCC_ENDIAN_BIG: Big endian system
|
||||
|
||||
There is an example about how to use it:
|
||||
|
||||
\code
|
||||
#if defined(YYCC_ENDIAN_LITTLE)
|
||||
// Code specific to little endian systems
|
||||
blabla();
|
||||
#endif
|
||||
\endcode
|
||||
|
||||
\subsection macro__endian_detector__constexpr_function Constexpr Function
|
||||
|
||||
YYCC also provides a constexpr function to check the endianness of the target platform.
|
||||
More precisely, endian::get_endian() function returns an enum value endian::EndianKind indicating the endianness.
|
||||
|
||||
There is an example about how to use it:
|
||||
|
||||
\code
|
||||
if constexpr (endian::get_endian() == endian::EndianKind::Little) {
|
||||
// Code specific to little endian systems
|
||||
blabla();
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section macro__stl_detector STL Detector
|
||||
|
||||
YYCC provides macros to detect which Standard Template Library (STL) implementation is being used.
|
||||
|
||||
\subsection macro__stl_detector__macro Macro
|
||||
|
||||
YYCC defines <B>one of following macros</B> to indicate which STL implementation is being used.
|
||||
|
||||
\li \c YYCC_STL_MSSTL: Microsoft STL
|
||||
\li \c YYCC_STL_GNUSTL: GNU STL
|
||||
\li \c YYCC_STL_CLANGSTL: Clang STL
|
||||
|
||||
There is an example about how to use it:
|
||||
|
||||
\code
|
||||
#if defined(YYCC_STL_MSSTL)
|
||||
// Code specific to Microsoft STL
|
||||
blabla();
|
||||
#endif
|
||||
\endcode
|
||||
|
||||
\subsection macro__stl_detector__constexpr_function Constexpr Function
|
||||
|
||||
YYCC also provides a constexpr function to check which STL implementation is being used at compile time.
|
||||
More precisely, stl::get_stl() function returns an enum value stl::StlKind indicating the STL implementation.
|
||||
|
||||
There is an example about how to use it:
|
||||
|
||||
\code
|
||||
if constexpr (stl::get_stl() == stl::StlKind::MsStl) {
|
||||
// Code specific to Microsoft STL
|
||||
blabla();
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section macro__ptr_size_detector Pointer Size Detector
|
||||
|
||||
YYCC provides macros and constexpr functions to detect the pointer size of the target platform.
|
||||
|
||||
\subsection macro__ptr_size_detector__macro Macro
|
||||
|
||||
YYCC always define <B>one of following macros</B> to indicate the pointer size of target platform.
|
||||
|
||||
\li \c YYCC_PTRSIZE_32: 32-bit environment
|
||||
\li \c YYCC_PTRSIZE_64: 64-bit environment
|
||||
|
||||
There is an example about how to use it:
|
||||
|
||||
\code
|
||||
#if defined(YYCC_PTRSIZE_32)
|
||||
// Code specific to 32-bit environment
|
||||
blabla();
|
||||
#endif
|
||||
\endcode
|
||||
|
||||
\subsection macro__ptr_size_detector__constexpr_function Constexpr Function
|
||||
|
||||
YYCC also provides a constexpr function to check the pointer size of the target platform.
|
||||
More precisely, ptr_size::get_ptr_size() function returns an enum value ptr_size::PtrSizeKind indicating the pointer size.
|
||||
|
||||
There is an example about how to use it:
|
||||
|
||||
\code
|
||||
if constexpr (ptr_size::get_ptr_size() == ptr_size::PtrSizeKind::Bits32) {
|
||||
// Code specific to 32-bit environment
|
||||
blabla();
|
||||
}
|
||||
\endcode
|
||||
|
||||
*/
|
||||
}
|
||||
30
doc/src/num/op.dox
Normal file
30
doc/src/num/op.dox
Normal file
@@ -0,0 +1,30 @@
|
||||
namespace yycc::num::op {
|
||||
/**
|
||||
|
||||
\page num__op Numeric Operations
|
||||
|
||||
Namespace yycc::num::op provides functions for robust numeric operations inspired by Rust's approach to primitive type operations.
|
||||
|
||||
Currently, this namespace only supports unsigned integer ceiling division, though more operations may be added in the future based on demand.
|
||||
|
||||
\section num__op__div_ceil Ceiling Division
|
||||
|
||||
The \c div_ceil function performs division between two unsigned integers and rounds up the result.
|
||||
It uses a safe algorithm that avoids potential overflow issues that could occur with the traditional formula <TT>(lhs + rhs - 1) / rhs</TT>.
|
||||
|
||||
The function computes: <TT>(lhs % rhs == 0) ? (lhs / rhs) : (lhs / rhs) + 1u</TT>
|
||||
The function prevents division by zero by checking the divisor before performing the operation and throwing a std::logic_error if the divisor is zero.
|
||||
|
||||
Here are some examples showing how to use this function:
|
||||
|
||||
\code
|
||||
#include <yycc/num/op.hpp>
|
||||
|
||||
// Ceiling division examples
|
||||
uint32_t result1 = op::div_ceil(uint32_t(10), uint32_t(3)); // Results in 4
|
||||
uint32_t result2 = op::div_ceil(uint32_t(9), uint32_t(3)); // Results in 3
|
||||
uint32_t result3 = op::div_ceil(uint32_t(1), uint32_t(10)); // Results in 1
|
||||
\endcode
|
||||
|
||||
*/
|
||||
}
|
||||
70
doc/src/num/parser.dox
Normal file
70
doc/src/num/parser.dox
Normal file
@@ -0,0 +1,70 @@
|
||||
namespace yycc::num::parse {
|
||||
/**
|
||||
|
||||
\page num__parser Numeric Parser
|
||||
|
||||
Namespace yycc::num::parse is served for the convertion from string to number.
|
||||
|
||||
\section num__parser__supported_types Supported Types
|
||||
|
||||
Functions located in this namespace support the convertion from string to following types:
|
||||
|
||||
\li Integral types (except \c bool): \c int, \c uint32_t, \c char and etc.
|
||||
\li Floating point types: \c float, \c double and etc.
|
||||
\li \c bool
|
||||
|
||||
Please note in C++, \c bool is integral type but we list it individually because parser will treat it specially.
|
||||
For \c bool type, parser will try doing convertion between it and \c "true" \c "false" string.
|
||||
(\b case-insensitive. It means that \c "true", \c "True" and \c "TRUE", all of them can be converted into \c true.)
|
||||
|
||||
\section num__parser__usage Usage
|
||||
|
||||
This namespace provide a uniform parser function #parse with various overloads.
|
||||
All of them accept an UTF8 string view at first argument,
|
||||
and following argument is different required by different overloads which may change parser behavior.
|
||||
For example, for floating point type, this function allows caller to specify extra argument providing the format of given number string (\c std::chars_format).
|
||||
or for integral type, this function allows caller to specify extra argument providing the base of given number string.
|
||||
The return value is a result type, containing converted value or error occurs.
|
||||
There are some examples:
|
||||
|
||||
\code
|
||||
auto rv = parse::parse<uint32_t>(u8"123");
|
||||
assert(rv.has_value());
|
||||
auto converted = rv.value();
|
||||
\endcode
|
||||
|
||||
\section num__parser__stringify Stringify
|
||||
|
||||
Namespace yycc::num::stringify provide the opposite function of namespace yycc::num::parse.
|
||||
They convert given number into their string representation.
|
||||
There is an example:
|
||||
|
||||
\code
|
||||
auto showcase = stringify::stringify<uint32_t>(UINT32_C(114));
|
||||
\endcode
|
||||
|
||||
Same as parse::parse, stringify::stringify also has same overloads and different second arguments.
|
||||
For floating point type, this function allows caller to specify extra arguments
|
||||
which provides the format (\c std::chars_format) and precision when getting string representation.
|
||||
For integral type, this function allows caller to specify extra argument
|
||||
providing the base of number when getting string representation.
|
||||
However, the result value of stringify::stringify is just the result, not a result type.
|
||||
Because it is mostly impossible to occur error in stringify::stringify.
|
||||
|
||||
\section num__parser__notes Notes
|
||||
|
||||
All functions located in yycc::num::parse and yycc::num::stringify namespace are implementated by standard library functions.
|
||||
These functions just make a good wrapper for complex standard library functions.
|
||||
And give you a experience like Rust \c parse functions.
|
||||
|
||||
Basically, all functions located in this helper have possibility to throw exception.
|
||||
But this possibility are more close to the possibility that \c new statement throw \c std::bad_alloc.
|
||||
So in most cases you can assume these functions will not throw any exception.
|
||||
|
||||
All functions are template functions.
|
||||
The argument of template is the type these functions need to be processed.
|
||||
Although C++ have \e smart template type deduction,
|
||||
it would be better to specify template argument manually to explicitly specify your desired type.
|
||||
|
||||
*/
|
||||
}
|
||||
63
doc/src/num/safe_cast.dox
Normal file
63
doc/src/num/safe_cast.dox
Normal file
@@ -0,0 +1,63 @@
|
||||
namespace yycc::num::safe_cast {
|
||||
/**
|
||||
|
||||
\page num__safe_cast Numeric Safe Casting
|
||||
|
||||
Namespace yycc::num::safe_cast provides functions which safely cast numeric value from one type to another.
|
||||
|
||||
\section num__safe_cast__overview Overview
|
||||
|
||||
When writing C++ code, casting between types with different ranges is very important
|
||||
but greatly easy to make mistakes which finally cause fatal errors.
|
||||
Inspired by Rust's approach to type conversion,
|
||||
this namespace provides safe casting functions that handle potential overflow and underflow issues.
|
||||
|
||||
\section num__safe_cast__functions Functions
|
||||
|
||||
The namespace provides two main functions:
|
||||
\li \c to() - Direct conversion for cases where the destination type can definitely hold the source value
|
||||
which means definitely safe conversions (widening conversions).
|
||||
\li \c try_to() - Attempt conversion and return a Result type that includes error information if the conversion fails
|
||||
which means potentially risky conversions (narrowing conversions).
|
||||
|
||||
The \c try_to function returns a \c std::expected.
|
||||
If the conversion succeeds, the result contains the converted value.
|
||||
If it fails, it contains a error info.
|
||||
|
||||
\section num__safe_cast__examples Examples
|
||||
|
||||
Here are some examples showing how to use the safe casting functions:
|
||||
|
||||
\code
|
||||
#include <yycc/num/safe_cast.hpp>
|
||||
|
||||
// Safe conversion using 'to' function
|
||||
uint32_t val1 = safe_cast::to<uint32_t>(static_cast<int16_t>(123));
|
||||
|
||||
// Potentially risky conversion using 'try_to' function
|
||||
auto result = safe_cast::try_to<int16_t>(static_cast<int32_t>(12345));
|
||||
if (result.has_value()) {
|
||||
auto converted = result.value();
|
||||
// Use converted value
|
||||
} else {
|
||||
// Handle error
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section num__safe_cast__notes Notes
|
||||
|
||||
The safety of conversions is determined at compile time using the \c CAN_SAFE_TO meta-programming concept.
|
||||
However, for variable-length data types (like \c size_t ), the safety determination may vary across platforms,
|
||||
which could affect code portability. For this reason, it would be better to use \c try_to for these types
|
||||
for better robust application on different platforms.
|
||||
|
||||
\section num__safe_cast__limitations Limitations
|
||||
|
||||
This namespace supports safe casting between integral types only.
|
||||
Currently unsupported conversions include:
|
||||
|
||||
\li Floating-point to floating-point conversions
|
||||
\li Floating-point to integer conversions
|
||||
|
||||
*/
|
||||
}
|
||||
86
doc/src/num/safe_op.dox
Normal file
86
doc/src/num/safe_op.dox
Normal file
@@ -0,0 +1,86 @@
|
||||
namespace yycc::num::safe_op {
|
||||
/**
|
||||
|
||||
\page num__safe_op Numeric Safe Arithmetic Operations
|
||||
|
||||
Namespace yycc::num::safe_op provides Rust-like safe arithmetic operations
|
||||
for handling overflow, underflow, and other undefined behaviors in C++.
|
||||
|
||||
\section num__safe_op__overview Overview
|
||||
|
||||
Inspired by Rust's rich set of arithmetic operators,
|
||||
this namespace provides safe arithmetic operations that handle potential overflow, underflow, and other undefined behaviors that commonly occur in C++.
|
||||
It offers multiple strategies for handling arithmetic operations including wrapping, checked, overflowing, saturating, and strict operations.
|
||||
|
||||
\section num__safe_op__operation_types Operation Types
|
||||
|
||||
The namespace provides several families of arithmetic operations:
|
||||
|
||||
\li \c wrapping_* operations: Perform arithmetic with wrapping on overflow/underflow (similar to unsigned integer behavior)
|
||||
\li \c checked_* operations: Return std::optional containing the result, or std::nullopt if overflow/underflow occurs
|
||||
\li \c overflowing_* operations: Return a pair with the result and a boolean indicating whether overflow occurred
|
||||
\li \c saturating_* operations: Clamp the result to the min/max value when overflow/underflow occurs
|
||||
\li \c strict_* operations: Throw exceptions when overflow/underflow occurs
|
||||
\li \c ordinary operations (add, sub, mul, div): Alias to wrapping operations for safe default behavior
|
||||
|
||||
\section num__safe_op__arithmetic_functions Arithmetic Functions
|
||||
|
||||
For each operation type, the namespace provides functions for the four basic arithmetic operations:
|
||||
\li \c _add : Addition
|
||||
\li \c _sub : Subtraction
|
||||
\li \c _mul : Multiplication
|
||||
\li \c _div : Division
|
||||
|
||||
For example, for wrapping operations: \c wrapping_add, \c wrapping_sub, \c wrapping_mul, \c wrapping_div.
|
||||
|
||||
\section num__safe_op__examples Examples
|
||||
|
||||
Here are some examples showing how to use the safe arithmetic functions:
|
||||
|
||||
\code
|
||||
#include <yycc/num/safe_op.hpp>
|
||||
#include <iostream>
|
||||
|
||||
// Wrapping addition - wraps around on overflow
|
||||
uint8_t result1 = safe_op::wrapping_add(uint8_t(200), uint8_t(100)); // Results in 44
|
||||
|
||||
// Checked multiplication - returns std::optional
|
||||
auto result2 = safe_op::checked_mul(int32_t(1000000), int32_t(1000000));
|
||||
if (!result2.has_value()) {
|
||||
std::cout << "Multiplication overflowed!" << std::endl;
|
||||
} else {
|
||||
std::cout << "Result: " << result2.value() << std::endl;
|
||||
}
|
||||
|
||||
// Overflowing subtraction - returns pair of result and overflow flag
|
||||
auto [result3, overflowed] = safe_op::overflowing_sub(int32_t(-10), int32_t(INT32_MIN));
|
||||
if (overflowed) {
|
||||
std::cout << "Subtraction overflowed!" << std::endl;
|
||||
}
|
||||
|
||||
// Saturating multiplication - clamps to min/max on overflow
|
||||
int32_t result4 = safe_op::saturating_mul(int32_t(1000000), int32_t(1000000)); // Clamps to INT32_MAX
|
||||
|
||||
// Ordinary operations - safe defaults without undefined behavior
|
||||
int32_t result5 = safe_op::add(int32_t(10), int32_t(20)); // 30
|
||||
\endcode
|
||||
|
||||
\section num__safe_op__undefined_behaviors Handling of Undefined Behaviors
|
||||
|
||||
This namespace handles several undefined behaviors in C++ arithmetic:
|
||||
\li Signed integer overflow and underflow (e.g. INT_MAX + 1)
|
||||
\li Division by zero
|
||||
\li Performing INT_MIN / -1 division (which would result in a value that doesn't fit in the type)
|
||||
|
||||
For division operations, special care is taken to handle these undefined behaviors appropriately depending on the operation type.
|
||||
|
||||
\section num__safe_op__platform_support Platform Support
|
||||
|
||||
The implementation uses hardware-specific overflow detection functions:
|
||||
\li GCC/Clang: Uses built-in functions like __builtin_add_overflow
|
||||
\li Windows: Uses Windows API functions from \c intsafe.h
|
||||
|
||||
This ensures optimal performance across different platforms.
|
||||
|
||||
*/
|
||||
}
|
||||
75
doc/src/patch.dox
Normal file
75
doc/src/patch.dox
Normal file
@@ -0,0 +1,75 @@
|
||||
namespace yycc::patch {
|
||||
/**
|
||||
|
||||
\page patch Other STL Patches
|
||||
|
||||
There are some other STL patches in this library which can not be organized in single document file individually.
|
||||
So I put them together here.
|
||||
|
||||
\section patch__ptr_pad Pointer Print Padding
|
||||
|
||||
When printing pointer on screen, programmer usually left-pad zero to make it looks good.
|
||||
However, the count of zero for padding is different in x86 and x64 architecture (8 for x86 and 16 for x64).
|
||||
Macro \c PRIXPTR_LPAD will help you to resolve this issue.
|
||||
|
||||
Macro \c PRIXPTR_LPAD will be expended to one of following value according to the target system architecture.
|
||||
|
||||
\li \c "08": On x86 system.
|
||||
\li \c "016": On x64 system.
|
||||
|
||||
There is an example for how to use it:
|
||||
|
||||
\code
|
||||
void* raw_ptr = blabla();
|
||||
std::printf(stdout, "Raw Pointer 0x%" PRIXPTR_LPAD PRIXPTR, raw_ptr);
|
||||
\endcode
|
||||
|
||||
Note \c PRIXPTR is defined by standard library for formatting pointer as hexadecimal style.
|
||||
|
||||
\section patch__smart_file Smart FILE Pointer
|
||||
|
||||
fopen::SmartStdFile use \c std::unique_ptr with custom deleter to implement smart \c FILE*.
|
||||
It is useful in the cases that you want to automatically free opened file when leaving corresponding scope.
|
||||
|
||||
\section patch__utf8_fopen UTF8 fopen
|
||||
|
||||
In Windows, standard \c std::fopen can not handle UTF8 file name in common environment.
|
||||
So we create fopen::fopen to give programmer an universal \c fopen in UTF8 style.
|
||||
|
||||
In Windows platform, this function will try to convert its argument to \c wchar_t
|
||||
and calling Microsoft specific \c _wfopen function to open file.
|
||||
If encoding convertion or \c _wfopen failed, this function will return \c nullptr like \c std::fopen does.
|
||||
In other platforms, it will simply redirect calling to \c std::fopen.
|
||||
|
||||
There is a simple example:
|
||||
|
||||
\code
|
||||
FILE* fs = fopen::fopen(u8"/path/to/file", u8"rb");
|
||||
\endcode
|
||||
|
||||
\section patch__utf8_stream UTF8 Stream Support
|
||||
|
||||
The namespace yycc::patch::stream provides UTF8 support for \c std::ostream.
|
||||
This namespace contains operator overloads that give \c std::ostream the ability to write UTF8 string and its char.
|
||||
To use this feature, you should include its header file first,
|
||||
and then directly use <TT>using namespace ::yycc::patch::stream;</TT> to import this namespace.
|
||||
|
||||
\section patch__utf8_format UTF8 Format Support
|
||||
|
||||
The namespace yycc::patch::format provides a patch for \c std::format to allow UTF8 string as arguments.
|
||||
As \c std::format only allows \c char and \c wchar_t as its char type in C++ 23 currently,
|
||||
it's impossible to use UTF8 string for std::format, both as format string and argument.
|
||||
This namespace gives a patch for this shortcoming.
|
||||
|
||||
First, it define a brandnew format::format function, which resolve the issue that we can not use UTF8 as format string.
|
||||
The implementation of this function is simple. We simply convert given UTF8 format string into ordinary string,
|
||||
and then delegate it to \c std::vformat, the runtime format function in C++ 23.
|
||||
So the performance of this function may be a little worse than \c std::format, but it's not a big deal.
|
||||
We suggest that you use this namespace provided format::format function in your code,
|
||||
to enable this UTF8 format string feature.
|
||||
|
||||
Additionally, this namespace provides \c std::formatter specializations for UTF8 string.
|
||||
Thus we can safely use UTF8 string as argument in \c std::format, also including our invented brandnew format::format function.
|
||||
|
||||
*/
|
||||
}
|
||||
51
doc/src/premise_and_principle.dox
Normal file
51
doc/src/premise_and_principle.dox
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
|
||||
\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 gracefully.
|
||||
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 POSIX-compatible and use UTF8 as its encoding.
|
||||
|
||||
\section premise_and_principle__string_encoding String Encoding
|
||||
|
||||
Before using this library, you should know the encoding strategy of this library first.
|
||||
After upgrade the whole project into C++23, \c char8_t is the only valid UTF8 char type.
|
||||
\c std::u8string and \c std::u8string_view are the only valid UTF8 string container and viewer.
|
||||
And, \c u8 string literal prefix is the only way to create UTF8 string literal.
|
||||
In brief words, this library use UTF8 encoding everywhere.
|
||||
|
||||
However, there are some special cases that use ordinary string instead of UTF8 string list following
|
||||
(also, not all cases are covered).
|
||||
|
||||
\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 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.
|
||||
|
||||
*/
|
||||
133
doc/src/rust.dox
Normal file
133
doc/src/rust.dox
Normal file
@@ -0,0 +1,133 @@
|
||||
namespace yycc {
|
||||
/**
|
||||
|
||||
\page rust Rust Facilities in C++
|
||||
|
||||
This collection of following headers brings Rust-style programming facilities to C++.
|
||||
|
||||
\section rust__primitive Primitive Types
|
||||
|
||||
The yycc::primitive namespace provides primitive types similar to Rust's approach.
|
||||
Especially resolve the problem that the names of C++ primitive types are so long.
|
||||
|
||||
There is an example of using primitive types:
|
||||
|
||||
\code
|
||||
#include <yycc/primitive.hpp>
|
||||
using namespace yycc::primitive;
|
||||
|
||||
i32 value = 42;
|
||||
u64 big_number = 1000000ULL;
|
||||
f64 precision = 3.14159265359;
|
||||
\endcode
|
||||
|
||||
\section rust__option Option Type
|
||||
|
||||
The yycc::option namespace reproduces Rust's Option type and its members Some and None in C++.
|
||||
Considering C++ has provide \c std::optional, this namespace provided contents are just an alias to it.
|
||||
|
||||
\li yycc::option::Option - Template alias for std::optional
|
||||
\li yycc::option::Some - Function to create an Option with a value
|
||||
\li yycc::option::None - Function to create an empty Option
|
||||
|
||||
There is an example of using \c Option type:
|
||||
|
||||
\code
|
||||
#include <yycc/option.hpp>
|
||||
#include <iostream>
|
||||
|
||||
using namespace yycc::option;
|
||||
|
||||
Option<int> maybe_value = Some<Option<int>>(42);
|
||||
if (maybe_value.has_value()) {
|
||||
std::cout << "Value: " << maybe_value.value() << std::endl;
|
||||
}
|
||||
|
||||
auto empty_value = None<Option<int>>();
|
||||
if (!empty_value.has_value()) {
|
||||
std::cout << "No value present" << std::endl;
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section rust__result Result Type
|
||||
|
||||
The yycc::result namespace reproduces Rust's Result type and its members Ok and Err in C++.
|
||||
Considering C++ has provide \c std::expected, this namespace provided contents are just an alias to it.
|
||||
|
||||
\li yycc::result::Result - Template alias for std::expected
|
||||
\li yycc::result::Ok - Function to create a Result with a success value
|
||||
\li yycc::result::Err - Function to create a Result with an error value
|
||||
|
||||
There is an example of using \c Result type:
|
||||
|
||||
\code
|
||||
#include <yycc/result.hpp>
|
||||
#include <iostream>
|
||||
|
||||
using namespace yycc::result;
|
||||
|
||||
Result<int, int> divide(int a, int b) {
|
||||
if (b == 0) {
|
||||
return Err<Result<int, int>>(-1); // Error code
|
||||
}
|
||||
return Ok<Result<int, int>>(a / b);
|
||||
}
|
||||
|
||||
auto result = divide(10, 2);
|
||||
if (result.has_value()) {
|
||||
std::cout << "Result: " << result.value() << std::endl;
|
||||
} else {
|
||||
std::cout << "Error occurred: " << result.error() << std::endl;
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section rust__panic Panic Mechanism
|
||||
|
||||
The yycc::panic namespace provides Rust-style panic functionality for immediate program termination on unrecoverable errors.
|
||||
This imitates Rust's panic! macro behavior, allowing the program to immediately exit with error information and stack traces.
|
||||
|
||||
\li RS_PANIC: Macro equivalent to Rust's panic! macro.
|
||||
This macro will help you append all filename, line and function info to the real panic trigger function.
|
||||
\li yycc::panic::panic: The actual function called by the macro.
|
||||
User usually does not need to call this function directly.
|
||||
|
||||
There is an example of using this panic mechanism:
|
||||
|
||||
\code
|
||||
#include <yycc/panic.hpp>
|
||||
|
||||
void critical_function(int err_code) {
|
||||
// Some condition that indicates an unrecoverable error
|
||||
if (err_code != 100) {
|
||||
RS_PANIC("Unrecoverable error in critical function with code {}", err_code);
|
||||
}
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section rust__prelude Prelude
|
||||
|
||||
The yycc::prelude namespace provides a Rust-like prelude for C++. In Rust, types are automatically imported into all files by default.
|
||||
This default-imported set of types is called the "prelude".
|
||||
This namespace provides a similar concept for C++.
|
||||
|
||||
This namespace will extract following content into \b global scope:
|
||||
|
||||
\li All primitive types defined in yycc::primitive.
|
||||
\li Vec: \c std::vector template alias.
|
||||
\li All functionality from yycc::option and yycc::result namespaces.
|
||||
\li Panic mechanism from yycc::panic.
|
||||
|
||||
There is an example of using this header:
|
||||
|
||||
\code
|
||||
#include <yycc/prelude.hpp>
|
||||
|
||||
// Now all Rust-style facilities are available without prefixes:
|
||||
i32 x = 42; // From primitive
|
||||
Vec<i32> numbers = {1, 2, 3}; // Vector of primitive types
|
||||
auto result = Ok<Result<i32, i32>>(x); // Result type
|
||||
RS_PANIC("Something went wrong"); // Panic macro
|
||||
\endcode
|
||||
|
||||
*/
|
||||
}
|
||||
176
doc/src/string/op.dox
Normal file
176
doc/src/string/op.dox
Normal file
@@ -0,0 +1,176 @@
|
||||
namespace yycc::string::op {
|
||||
/**
|
||||
|
||||
\page string__op String Operations
|
||||
|
||||
\section string__op__printf Printf VPrintf
|
||||
|
||||
yycc::string::op provides 4 functions for formatting string.
|
||||
These functions are originally provided to programmer who can not use C++ 20 \c std::format feature.
|
||||
However, when this project was migrated to C++23 standard, \c std::format is finally available.
|
||||
And we set these functions as the complement to \c std::format feature.
|
||||
|
||||
\code
|
||||
std::u8string printf(const char8_t* format, ...);
|
||||
std::u8string vprintf(const char8_t* format, va_list argptr);
|
||||
std::string printf(const char* format, ...);
|
||||
std::string vprintf(const char* format, va_list argptr);
|
||||
\endcode
|
||||
|
||||
#printf and #vprintf is similar to \c std::sprintf and \c std::vsprintf.
|
||||
#printf accepts UTF8 format string and variadic arguments specifying data to print.
|
||||
This is commonly used by programmer.
|
||||
However, #vprintf also do the same work but its second argument is \c va_list,
|
||||
the representation of variadic arguments.
|
||||
It is mostly used by other function which has variadic arguments.
|
||||
|
||||
The only difference between these function and standard library functions is
|
||||
that you don't need to worry about whether the space of given buffer is enough,
|
||||
because these functions help you to calculate this internally.
|
||||
|
||||
Once there are some exceptions occurs, such as, not enough memeory, or the bad syntax of format string,
|
||||
these functions will throw exception immediately.
|
||||
|
||||
\section string__op__replace Replace
|
||||
|
||||
yycc::string::op provide 2 functions for programmer do string replacement:
|
||||
|
||||
\code
|
||||
void replace(std::u8string& strl, const std::u8string_view& from_strl, const std::u8string_view& to_strl);
|
||||
std::u8string to_replace(const std::u8string_view& strl, const std::u8string_view& from_strl, const std::u8string_view& to_strl);
|
||||
\endcode
|
||||
|
||||
The first overload will do replacement in given string container directly.
|
||||
The second overload will produce a copy of original string and do replacement on the copied string.
|
||||
|
||||
These #replace functions have special treatments for boundary scenarios:
|
||||
|
||||
\li If given string is empty, the return value will be empty.
|
||||
\li If the character sequence to be replaced is empty string, no replacement will happen.
|
||||
\li If the character sequence will be replaced into string is or empty, it will simply delete found character sequence from given string.
|
||||
|
||||
\section string__op__join Join
|
||||
|
||||
yycc::string::op provide an universal way for joining string and various specialized join functions.
|
||||
|
||||
\subsection string__op__join__universal Universal Join Function
|
||||
|
||||
Because C++ list types are various.
|
||||
There is no unique and convenient way to create an universal join function.
|
||||
So we create #JoinDataProvider to describe join context.
|
||||
|
||||
Before using universal join function,
|
||||
you should setup #JoinDataProvider first, the context of join function.
|
||||
It actually is an \c std::function object which can be easily fetched by C++ lambda syntax.
|
||||
This function pointer returns \c std::optional<std::u8string_view>,
|
||||
which should return \c std::u8string_view for the data to be joined, or \c std::nullopt if there is no more data.
|
||||
As you noticed, this is similar to Rust iterator.
|
||||
|
||||
Then, you can pass the created #JoinDataProvider object to #join function.
|
||||
And specify delimiter at the same time.
|
||||
Then you can get the final joined string.
|
||||
There is an example:
|
||||
|
||||
\code
|
||||
std::vector<std::u8string> data {
|
||||
u8"", u8"1", u8"2", u8""
|
||||
};
|
||||
auto iter = data.cbegin();
|
||||
auto stop = data.cend();
|
||||
std::u8string joined_string = yycc::string::op::join(
|
||||
[&iter, &stop]() -> std::optional<std::u8string_view> {
|
||||
if (iter == stop) return std::nullopt;
|
||||
return *iter++;
|
||||
},
|
||||
delimiter
|
||||
);
|
||||
\endcode
|
||||
|
||||
\subsection string__op__join__specialized Specialized Join Function
|
||||
|
||||
Despite universal join function,
|
||||
yycc::string::op also provide a specialized join functions for standard library container.
|
||||
For example, the code written above can be written in following code by using this specialized overload.
|
||||
The first two argument is just the begin and end iterator.
|
||||
However, you must make sure that the iterator can be dereferenced and then implicitly converted to std::u8string_view.
|
||||
|
||||
\code
|
||||
std::vector<std::u8string> data {
|
||||
u8"", u8"1", u8"2", u8""
|
||||
};
|
||||
std::u8string joined_string = yycc::string::op::join(data.begin(), data.end(), delimiter);
|
||||
\endcode
|
||||
|
||||
\section string__op__lower_upper Lower Upper
|
||||
|
||||
This namespace provides Python-like string lower and upper function.
|
||||
|
||||
\code
|
||||
void lower(std::u8string& strl);
|
||||
std::u8string to_lower(const std::u8string_view& strl);
|
||||
void upper(std::u8string& strl);
|
||||
std::u8string to_upper(const std::u8string_view& strl);
|
||||
\endcode
|
||||
|
||||
The functions start with "to_" prefix accept a string view as argument
|
||||
and return a \b copy whose content are all the lower/upper case of original string.
|
||||
The rest of these functions accept a mutable string container as argument and will modify it in place.
|
||||
|
||||
\section string__op__strip_trim Strip and Trim
|
||||
|
||||
This namespace provides functions for removing leading and trailing characters.
|
||||
There are two sets of functions:
|
||||
|
||||
\subsection string__op__strip Unicode-aware functions
|
||||
|
||||
These functions properly handle Unicode characters when stripping:
|
||||
|
||||
\code
|
||||
std::u8string_view strip(const std::u8string_view& strl, const std::u8string_view& words);
|
||||
std::u8string_view lstrip(const std::u8string_view& strl, const std::u8string_view& words);
|
||||
std::u8string_view rstrip(const std::u8string_view& strl, const std::u8string_view& words);
|
||||
\endcode
|
||||
|
||||
The prefix "l" and "r" are for left and right strip respectively like Python.
|
||||
|
||||
\subsection string__op__trim ASCII-only functions
|
||||
|
||||
These functions treat each byte as an individual character and are faster for ASCII-only scenarios:
|
||||
|
||||
\code
|
||||
std::u8string_view trim(const std::u8string_view& strl, const std::u8string_view& words);
|
||||
std::u8string_view ltrim(const std::u8string_view& strl, const std::u8string_view& words);
|
||||
std::u8string_view rtrim(const std::u8string_view& strl, const std::u8string_view& words);
|
||||
\endcode
|
||||
|
||||
The difference of "trim" and "strip" is same as their invented time in Java.
|
||||
"trim" is inveted at first so its function is confined to ASCII-only strings.
|
||||
"strip" is introduced later and it should accept more scenarios like Unicode.
|
||||
Although all of "trim" and "strip" can handle Unicode in Java.
|
||||
|
||||
\section string__op__split Split
|
||||
|
||||
This namespace provides Python-like string split functions.
|
||||
It has 3 variants for different use cases:
|
||||
|
||||
\code
|
||||
LazySplit lazy_split(const std::u8string_view& strl, const std::u8string_view& delimiter);
|
||||
std::vector<std::u8string_view> split(const std::u8string_view& strl, const std::u8string_view& delimiter);
|
||||
std::vector<std::u8string> split_owned(const std::u8string_view& strl, const std::u8string_view& delimiter);
|
||||
\endcode
|
||||
|
||||
All these overloads take a string view as the first argument representing the string need to be split.
|
||||
The second argument is a string view representing the delimiter for splitting.
|
||||
|
||||
The first function #lazy_split returns a LazySplit object that can be used in range-based for loops.
|
||||
This is lazy-computed and memory-efficient for large datasets.
|
||||
The second function #split returns a vector of string views, which is memory-efficient
|
||||
but the views are only valid as long as the original string remains valid.
|
||||
The third function #split_owned returns a vector of strings, which are copies of the original parts.
|
||||
|
||||
If the source string (the string need to be split) is empty, or the delimiter is empty,
|
||||
the result will only has 1 item and this item is source string itself.
|
||||
There is no way that these methods return an empty list, except the code is buggy.
|
||||
|
||||
*/
|
||||
}
|
||||
118
doc/src/string/reinterpret.dox
Normal file
118
doc/src/string/reinterpret.dox
Normal file
@@ -0,0 +1,118 @@
|
||||
namespace yycc::string::reinterpret {
|
||||
/**
|
||||
|
||||
\page string__reinterpret String Reinterpret
|
||||
|
||||
Now, you have know that we use UTF8 string everywhere in this project
|
||||
as we introduced in \ref premise_and_principle__string_encoding.
|
||||
Now it's time to know how to fetch UTF8 string from user or anywhere else.
|
||||
|
||||
\section string__reinterpret__concept Concepts
|
||||
|
||||
In following content, you may be face with 2 words: ordinary string and UTF8 string.
|
||||
|
||||
UTF8 string, as its name, is the string encoded with UTF8.
|
||||
The char type of it must is \c char8_t.
|
||||
|
||||
Ordinary string means the plain, native string.
|
||||
The result of C++ string literal without any prefix \c "foo bar" is a rdinary string.
|
||||
The char type of it is \c char.
|
||||
Its encoding depends on compiler and environment.
|
||||
(UTF8 in Linux, or system code page in Windows if UTF8 switch was not enabled in MSVC.)
|
||||
|
||||
For more infomation, please browse CppReference:
|
||||
https://en.cppreference.com/w/cpp/language/string_literal
|
||||
|
||||
\section string__reinterpret__pointer UTF8 String Pointer
|
||||
|
||||
String pointer means the raw pointer pointing to a string, such as \c const \c char*, \c char*, \c char32_t* and etc.
|
||||
|
||||
Many legacy code assume \c char* is encoded with UTF8 (the exception is Windows). But \c char* is incompatible with \c char8_t.
|
||||
YYCC provides as_utf8() to resolve this issue. There is an exmaple:
|
||||
|
||||
\code
|
||||
const char* absolutely_is_utf8 = "I confirm this is encoded with UTF8.";
|
||||
const char8_t* converted = as_utf8(absolutely_is_utf8);
|
||||
|
||||
char* mutable_utf8 = const_cast<char*>(absolutely_is_utf8); // This is not safe. Just for example.
|
||||
char8_t* mutable_converted = as_utf8(mutable_utf8);
|
||||
\endcode
|
||||
|
||||
as_utf8() has 2 overloads which can handle constant and mutable stirng pointer convertion respectively.
|
||||
|
||||
YYCC also has ability that convert UTF8 char type to ordinary char type by as_ordinary().
|
||||
Here is an exmaple:
|
||||
|
||||
\code
|
||||
const char8_t* utf8 = u8"I am UTF8 string.";
|
||||
const char* converted = as_ordinary(utf8);
|
||||
|
||||
char8_t* mutable_utf8 = const_cast<char*>(utf8); // Not safe. Also just for example.
|
||||
char* mutable_converted = as_ordinary(mutable_utf8);
|
||||
\endcode
|
||||
|
||||
Same as as_utf8(), as_ordinary() also has 2 overloads to handle constant and mutable string pointer.
|
||||
|
||||
\section string__reinterpret__container UTF8 String Container
|
||||
|
||||
String container usually means the standard library string container, such as \c std::string, \c std::wstring, \c std::u32string and etc.
|
||||
|
||||
In many personal project, programmer may use \c std::string everywhere because \c std::u8string may not be presented when writing peoject.
|
||||
How to do convertion between ordinary string container and UTF8 string container?
|
||||
It is definitely illegal that directly do force convertion. Because they may have different class layout.
|
||||
Calm down and I will tell you how to do correct convertion.
|
||||
YYCC provides as_utf8() to convert ordinary string container to UTF8 string container.
|
||||
There is an exmaple:
|
||||
|
||||
\code
|
||||
std::string ordinary_string("I am UTF8");
|
||||
std::u8string utf8_string = as_utf8(ordinary_string);
|
||||
\endcode
|
||||
|
||||
Actually, as_utf8() accepts a reference to \c std::string_view as argument.
|
||||
However, there is a implicit convertion from \c std::string to \c std::string_view,
|
||||
so you can directly pass a \c std::string instance to it.
|
||||
|
||||
String view will reduce unnecessary memory copy.
|
||||
If you just want to pass ordinary string container to function, and this function accepts \c std::u8string_view as its argument,
|
||||
you can use alternative as_utf8_view().
|
||||
|
||||
\code
|
||||
std::string ordinary_string("I am UTF8");
|
||||
std::u8string_view utf8_string = as_utf8_view(ordinary_string);
|
||||
\endcode
|
||||
|
||||
Comparing with previous one, this example use less memory.
|
||||
The reduced memory is the content of \c utf8_string because string view is a view, not the copy of original string.
|
||||
|
||||
Same as UTF8 string pointer, we also have as_ordinary() and as_ordinary_view() do correspondant reverse convertion.
|
||||
Try to do your own research and figure out how to use them.
|
||||
It's pretty easy.
|
||||
|
||||
\section string__reinterpret__clarification Clarification about Usage Scenario
|
||||
|
||||
Let we make a clarification for what this chapter are talking about.
|
||||
In these chapter, what we are talking about the convertion between UTF8 string and ordinary string,
|
||||
which is originally encoded by UTF-8 but presented by \c char type.
|
||||
This spot is crucial. If you apply any functions provided by this namespace to any string which is not encoded by UTF-8,
|
||||
for example, trying converting an CP1252 encoded western europe string to UTF-8 via function given by this namespace,
|
||||
it must cause <B>undefined behavior</B>.
|
||||
|
||||
The correct function for doing these things introduced above is located in yycc::encoding namespace,
|
||||
or a more generic module located in yycc::carton::pycodec.
|
||||
This namespace is only suit for the convertion of UTF-8 string which was mis-presented by non-<TT>char8_t</TT> types.
|
||||
After understand this point, you now can safely use this namespace.
|
||||
|
||||
Additionally, due to the legacy of MSVC, the encoding of \c char* may not be UTF8 in most cases.
|
||||
If you run the convertion code introduced in this article with the string which is not encoded with UTF8,
|
||||
it may cause undefined behavior.
|
||||
|
||||
To enable UTF8 mode of MSVC, please deliver \c /utf-8 switch to MSVC compiler.
|
||||
Thus you can use the functions introduced in this article safely.
|
||||
Otherwise, you must guarteen that the argument you provided to these functions is encoded by UTF8 manually.
|
||||
|
||||
Linux user do not need care this.
|
||||
Because almost Linux distro use UTF8 in default.
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/**
|
||||
|
||||
\page string_helper String Helper
|
||||
|
||||
|
||||
\section string_helper_lower_upper Lower Upper
|
||||
|
||||
String helper provides Python-like string lower and upper function.
|
||||
Both lower and upper function have 2 overloads:
|
||||
|
||||
\code
|
||||
yycc_u8string Lower(const yycc_char8_t*);
|
||||
void Lower(yycc_u8string&);
|
||||
\endcode
|
||||
|
||||
First overload accepts a NULL-terminated string as argument and return a \b copy whose content are all the lower case of original string.
|
||||
Second overload accepts a mutable string container as argument and will make all characters stored in it become their lower case.
|
||||
You can choose on of them for your flavor and requirements.
|
||||
Upper also has similar 2 overloads.
|
||||
|
||||
\section string_helper_split Split
|
||||
|
||||
String helper provides Python-like string split function.
|
||||
It has 2 types for you:
|
||||
|
||||
\code
|
||||
std::vector<yycc_u8string> Split(const yycc_u8string_view&, const yycc_char8_t*);
|
||||
std::vector<yycc_u8string_view> SplitView(const yycc_u8string_view&, const yycc_char8_t*);
|
||||
\endcode
|
||||
|
||||
All these overloads take a string view as the first argument for the string need to be split.
|
||||
The second argument is a raw string pointer representing the decilmer for splitting.
|
||||
The only difference between these 2 split function are overt according to their names.
|
||||
The first split function will return a list of copied string as its split result.
|
||||
The second split function will return a list of string view as its split result,
|
||||
and it will keep valid as long as the life time of your given string view argument.
|
||||
It also means that the last type will cost less memory if you don't need the copy of original string.
|
||||
|
||||
If the source string (the string need to be split) is empty, or the decilmer is \c nullptr or empty,
|
||||
the result will only has 1 item and this item is source string itself.
|
||||
There is no way that this method return an empty list, except the code is buggy.
|
||||
|
||||
*/
|
||||
31
doc/src/windows/com.dox
Normal file
31
doc/src/windows/com.dox
Normal file
@@ -0,0 +1,31 @@
|
||||
namespace yycc::windows::com {
|
||||
/**
|
||||
|
||||
\page windows__com COM Helper
|
||||
|
||||
This namespace is Windows specific.
|
||||
It will be invisible on other platforms.
|
||||
|
||||
\section windows__com__memory_safe_ptr Memory Safe Pointer Types
|
||||
|
||||
This namespace provides various memory-safe types for interacting with COM functions.
|
||||
Although Microsoft also has similar smart pointer called \c CComPtr.
|
||||
But this library is eager to hide all Microsoft-related functions calling.
|
||||
Using \c CComPtr is not corresponding with the philosophy of this library.
|
||||
So these standard library based smart pointer and corresponding deleter types were created.
|
||||
|
||||
\section windows__com__com_guard COM Guard
|
||||
|
||||
This namespace contains a COM Guard which make sure COM was initialized in current module when loading current module.
|
||||
It is essential because all calling to COM functions should be under the premise that COM has been initialized.
|
||||
This guard also will uninitialize COM when unloading this module.
|
||||
|
||||
There is only an exposed function called is_initialized() for user calling.
|
||||
This function will check whether COM environment is initialized.
|
||||
If you want YYCC automatically initialize COM environment for you,
|
||||
you must call this function in your program at least one time.
|
||||
Otherwise COM Guard code may be unavailable,
|
||||
because compiler may think these code is not referenced by any other code and drop them.
|
||||
|
||||
*/
|
||||
}
|
||||
23
doc/src/windows/console.dox
Normal file
23
doc/src/windows/console.dox
Normal file
@@ -0,0 +1,23 @@
|
||||
namespace yycc::windows::console {
|
||||
/**
|
||||
|
||||
\page windows__console Windows Console Helper
|
||||
|
||||
Namespace yycc::windows::console is designed to resolve some issue of Windows console
|
||||
which is not corresponding to POSIX system console.
|
||||
This namespace also is only available on Windows platform.
|
||||
|
||||
Currently this namespace only has one function: colorful_console(),
|
||||
which enable colorful console output support for \c stdout and \c stderr in Windows.
|
||||
As we introduced, you may know Windows console does not support ASCII Escape Code color in default.
|
||||
This function can fix this issue.
|
||||
This function will forcely enable ASCII Escape Code support in Windows console if possible.
|
||||
Thus you can write colorful text in Windows console freely.
|
||||
We suggest you to call this function at the beginning of program.
|
||||
|
||||
Considering most Linux console supports ASCII Escape Code very well,
|
||||
this function isn't presented in non-Windows platform.
|
||||
So it is essential that brack this function calling with Windows-only \c \#if.
|
||||
|
||||
*/
|
||||
}
|
||||
165
doc/src/windows/dialog.dox
Normal file
165
doc/src/windows/dialog.dox
Normal file
@@ -0,0 +1,165 @@
|
||||
namespace yycc::windows::dialog {
|
||||
/**
|
||||
|
||||
\page windows__dialog Dialog Helper
|
||||
|
||||
Picking files and folders is an important and essential operation under Windows.
|
||||
However the functions picking files and folders are so complex.
|
||||
This helper provides universal dialog picker by simple classes and functions.
|
||||
In following contents we will tell you how to call them.
|
||||
|
||||
This helper is Windows specific.
|
||||
It will be totally invisible if you are in other platforms.
|
||||
|
||||
\section windows__dialog__file_dialog Configure File Dialog
|
||||
|
||||
The first thing is that we should initialize FileDialog,
|
||||
and configure it according to your requirements.
|
||||
|
||||
This class is the data struct representing all aspects of file dialog.
|
||||
It also one of the arguments in final dialog function.
|
||||
|
||||
\code
|
||||
FileDialog params;
|
||||
params.set_owner(owner_getter());
|
||||
params.set_title(u8"My File Picker");
|
||||
params.set_init_file_name(u8"test.txt");
|
||||
params.set_init_directory(initial_directory_getter());
|
||||
\endcode
|
||||
|
||||
\subsection windows__dialog__file_dialog__owner Owner
|
||||
|
||||
FileDialog::set_owner() will set owner of this dialog.
|
||||
It accepts a Microsoft defined \c HWND as argument which should be familiar with Windows programmer.
|
||||
If you pass \c NULL to it or skip calling this function, it indicate that there is no owner of this dialog.
|
||||
<I>
|
||||
I don't what will happen if there is no owner for it.
|
||||
But it would be better to have an owner if possible.
|
||||
</I>
|
||||
|
||||
\subsection windows__dialog__file_dialog__title Title
|
||||
|
||||
FileDialog::set_title() will set dialog title of this dialog.
|
||||
If you pass \c nullptr or skip calling it,
|
||||
the title of dialog will be filled by system and the function type you calling.
|
||||
For example, the title will be "Open..." if you call open file function,
|
||||
and will be "Save As..." if you call save file function.
|
||||
At the same time, the language of this title filled by system is system UI dependent.
|
||||
It means that you do not need to do any extra I18N work for it.
|
||||
So I suggest you do not set title except you really want to modify title.
|
||||
|
||||
\subsection windows__dialog__file_dialog__init_file_name Initial File Name
|
||||
|
||||
FileDialog::set_init_file_name() will set the initial file name presented in dialog file name input box.
|
||||
If you pass \c nullptr or skip calling it, the text in dialog file name input box will be empty.
|
||||
|
||||
User can modify the name presented in input box later.
|
||||
But if you assign this value, the dialog will lose the ability that remember the previous name user input in previous calling.
|
||||
In normal case, dialog will try remembering the file name user input in dialog, and represent it in the next calling.
|
||||
However, if you specify this field, the dialog will always presented your specified value in every calling.
|
||||
|
||||
\subsection windows__dialog__file_dialog__init_directory Initial Directory
|
||||
|
||||
FileDialog::set_init_directory() will set the initial directory (startup directory) when opening dialog.
|
||||
|
||||
In following cases, initial directory will fall back to system behavior:
|
||||
|
||||
\li Pass \c nullptr to this function.
|
||||
\li Skip calling this function.
|
||||
\li Given directory path is invalid.
|
||||
|
||||
The system default behavior of initial directory is similar with initial file name.
|
||||
The dialog will try remembering the last directory you just entering, and will back into it in the next calling.
|
||||
The directory we meeting in the first launch is system defined.
|
||||
|
||||
\section windows__dialog__file_filters Configure File Filters
|
||||
|
||||
File filters is a drop down list represented in file dialog which allow user filter files by their extensions.
|
||||
It is beneficial to let user get the file which they want in a directory including massive different files.
|
||||
|
||||
<B>For file dialog picking a directory,</B> you can skip this step.
|
||||
Because the file dialog picking directory does not have file filter drop down box.
|
||||
Directory can not be filtered.
|
||||
|
||||
FileFilters takes responsibility for this feature:
|
||||
|
||||
\code
|
||||
auto& filters = params.configure_file_types();
|
||||
filters.add_filter(u8"Microsoft Word (*.docx; *.doc)", { u8"*.docx", u8"*.doc" });
|
||||
filters.add_filter(u8"Microsoft Excel (*.xlsx; *.xls)", { u8"*.xlsx", u8"*.xls" });
|
||||
filters.add_filter(u8"Microsoft PowerPoint (*.pptx; *.ppt)", { u8"*.pptx", u8"*.ppt" });
|
||||
filters.add_filter(u8"Text File (*.txt)", { u8"*.txt" });
|
||||
filters.add_filter(u8"All Files (*.*)", { u8"*.*" });
|
||||
params.set_default_file_type_index(0u);
|
||||
\endcode
|
||||
|
||||
\subsection windows__dialog__file_filters__setup File Filters
|
||||
|
||||
We don't need to initialize FileFilters by ourselves.
|
||||
Oppositely, we fetch it from FileDialog instance by calling FileDialog::configure_file_types().
|
||||
After fetching, we can call FileFilters::add_filter() to add a filter pair for file filters.
|
||||
|
||||
The first argument is the display text which user will see in file filter drop down box.
|
||||
|
||||
The second argument is a \c std::initializer_list.
|
||||
Every items are Windows used wildcard string instructing which file should be shown in file dialog.
|
||||
It is okey to use multiple wildcard string in list.
|
||||
This is suit for those file types involving multiple file extensions, such as the old and new file types of Microsoft Office as we illustracted.
|
||||
Empty list not allowed
|
||||
|
||||
FileFilters::add_filter() throws std::invalid_argument if filter name is blank or filter patterns is empty.
|
||||
Because these errors should be found during developing.
|
||||
|
||||
It should at least has one file filter in file dialog.
|
||||
I don't know the consequence if you don't provide any file filter.
|
||||
|
||||
\subsection windows__dialog__file_filters__default_filter Default File Type
|
||||
|
||||
FileDialog::set_default_file_type_index() will set the default selected file filter of this dialog.
|
||||
It accepts an index pointing to the file filter which you want to show in default for this file dialog.
|
||||
The index of file filters is the order where you call FileFilters::add_filter() above.
|
||||
If you pass \c NULL to it or skip calling this function, the first one will be default.
|
||||
|
||||
\section windows__dialog__result Create Dialog and Get Result
|
||||
|
||||
Finally, we can call file dialog functions by we initialized FileDialog
|
||||
|
||||
\code
|
||||
auto result1 = open_file(params);
|
||||
auto result2 = open_files(params);
|
||||
auto result3 = save_file(params);
|
||||
auto result4 = open_folder(params);
|
||||
\endcode
|
||||
|
||||
There are 4 file dialogs you can choose:
|
||||
|
||||
\li open_file(): Open single file
|
||||
\li open_files(): Open multiple files
|
||||
\li save_file(): Save single file
|
||||
\li open_folder(): Open single directory
|
||||
|
||||
\subsection windows__dialog__result__arguments Arguments
|
||||
|
||||
Among these 4 functions, the only argument is the reference to FileDialog.
|
||||
Function will use it to decide what would be shown in this file dialog.
|
||||
|
||||
\subsection windows__dialog__result__return_value Return Value
|
||||
|
||||
Please note these 4 functions will return a dialog specified result type as their return value.
|
||||
If this result type is an error, it means that an error occurred during execution.
|
||||
Otherwise, there is an optional value inside this result type.
|
||||
If user click Cancel button, this optional value will be empty.
|
||||
otherwise, this optional value will hold user selected a file or directory.
|
||||
|
||||
\section windows__dialog__notes Notes
|
||||
|
||||
You may notice there are various classes which we never introduce.
|
||||
Because they are intermediate classes and should not be used by programmer.
|
||||
For example:
|
||||
|
||||
\li WinFileDialog: The converted FileDialog passed to Windows.
|
||||
\li WinFileFilters: Same as WinFileDialog. It will be passed to Windows functions.
|
||||
\li etc...
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -1,24 +1,27 @@
|
||||
namespace yycc::windows {
|
||||
/**
|
||||
|
||||
\page win_import Windows Import Guard
|
||||
\page windows__import_guard Windows Import Guard
|
||||
|
||||
Windows is shitty for the programmer who is familiar with UNIX programming.
|
||||
Due to legacy reason, Windows defines various things which are not compatible with UNIX or standard C++ programming.
|
||||
|
||||
\section win_import__usage Usage
|
||||
\section windows__import_guard__usage Usage
|
||||
|
||||
YYCC has a way to solve the issue introduced above.
|
||||
|
||||
\code
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
#include <WinImportPrefix.hpp>
|
||||
#include <yycc/macro/os_detector.hpp>
|
||||
|
||||
#if defined(YYCC_OS_WINDOWS)
|
||||
#include <yycc/windows/import_guard_head.hpp>
|
||||
#include <Windows.h>
|
||||
#include "other_header_depend_on_windows.h"
|
||||
#include <WinImportSuffix.hpp>
|
||||
#include <yycc/windows/import_guard_tail.hpp>
|
||||
#endif
|
||||
\endcode
|
||||
|
||||
The including of WinImportPrefix.hpp and WinImportSuffix.hpp is a pair.
|
||||
The including of import_guard_head.hpp and import_guard_tail.hpp is a pair.
|
||||
They just like a guard bracket the include operation of Windows related headers,
|
||||
to keep all Windows shitty contents will not be leaked outside.
|
||||
|
||||
@@ -28,27 +31,27 @@ This guard can solve following issues:
|
||||
<LI>
|
||||
Programmer can not use \c std::max and \c std::min normally.
|
||||
<UL>
|
||||
<LI>Windows defines \c MAX and \c MIN as macros for personal use. This is why this happend.</LI>
|
||||
<LI>Guard defines some special macros to tell Windows do not create these 2 macros.</LI>
|
||||
<LI>Windows defines \c MAX and \c MIN as macros for personal use. This is why this happened.</LI>
|
||||
<LI>This is actually resolved by CMake defined 2 public build macros which tell Windows do not create these 2 macros. But I simply conclude this feature in there.</LI>
|
||||
</UL>
|
||||
</LI>
|
||||
<LI>
|
||||
Programmer will not be affected by the automatical rename of \c GetObject, \c GetClassName and etc.
|
||||
<UL>
|
||||
<LI>These are all macros for Windows personal use to automatically redirect calling to A function and W function by compiling environment.</LI>
|
||||
<LI>Guard \c #undef these annoy macros.</LI>
|
||||
<LI>Guard \c \#undef these annoy macros.</LI>
|
||||
</UL>
|
||||
</LI>
|
||||
<LI>
|
||||
Compiler throw annoy warnings and errors when using specific standard library functions.
|
||||
<UL>
|
||||
<LI>MSVC will throw warnings and errors when you are using Microsoft so-called \e depracted or \e unsafe standard library functions.</LI>
|
||||
<LI>YYCCInternal.hpp, which has been included by this pair, defines some macros to purge these warnings and errors out.</LI>
|
||||
<LI>This is also done by CMake public build macros.</LI>
|
||||
</UL>
|
||||
</LI>
|
||||
</UL>
|
||||
|
||||
\section win_import__notes Notes
|
||||
\section windows__import_guard__notes Notes
|
||||
|
||||
If you have other header files which are strongly depend on Windows header,
|
||||
you should put them into this bracket at the same time like example did.
|
||||
@@ -56,14 +59,15 @@ Because this guard operate some Windows macros as we introduced above.
|
||||
The headers depending on Windows may throw error if you put them outside of this pair.
|
||||
|
||||
Please note WinImportPrefix.hpp and WinImportSuffix.hpp can be included multiple times.
|
||||
Because they do not have the proprocessor command like <I>#pragma once</I> or etc to make sure they only can be included once.
|
||||
Because they do not have the proprocessor command like <I>\#pragma once</I> or etc to make sure they only can be included once.
|
||||
That's by design. Because we actually may use this pair multiple times.
|
||||
The only thing you should pledge is that you must make sure they are presented by pair.
|
||||
|
||||
This guard is Windows specific.
|
||||
It does nothing if you accidently use it in other platforms such as Linux,
|
||||
because the headers use \c #if to check environment out and will do nothing in non-Windows environment.
|
||||
because the headers use \c \#if to check environment out and will do nothing in non-Windows environment.
|
||||
However, we still highly recommend you use this pair with platform checker bracket like example does,
|
||||
if your program need to be run on multiple platforms.
|
||||
|
||||
*/
|
||||
*/
|
||||
}
|
||||
15
doc/src/windows/winfct.dox
Normal file
15
doc/src/windows/winfct.dox
Normal file
@@ -0,0 +1,15 @@
|
||||
namespace yycc::windows::winfct {
|
||||
/**
|
||||
|
||||
\page windows__winfct Windows Function Helper
|
||||
|
||||
Namespace yycc::windows::winfct gives a more convenient way to call Windows functions.
|
||||
If you want to know how to use these functions, please read the documentation of each function.
|
||||
The return value of most functions is a specific result type.
|
||||
If any error occurs, the result type will be an error, otherwise it will be the true result.
|
||||
|
||||
This namespace is Windows specific.
|
||||
It will be entirely invisible in other platforms.
|
||||
|
||||
*/
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
@ECHO OFF
|
||||
SET README_PATH=%CD%\README.md
|
||||
IF EXIST %README_PATH% (
|
||||
REM DO NOTHING
|
||||
) ELSE (
|
||||
ECHO Error: You must run this script at the root folder of this project!
|
||||
EXIT /b
|
||||
)
|
||||
|
||||
:: Create essential folder
|
||||
MKDIR bin
|
||||
CD bin
|
||||
MKDIR Win32
|
||||
MKDIR x64
|
||||
MKDIR install
|
||||
|
||||
:: Build for Win32
|
||||
CD Win32
|
||||
cmake -G "Visual Studio 16 2019" -A Win32 -DYYCC_BUILD_TESTBENCH=ON ../..
|
||||
cmake --build . --config Debug
|
||||
cmake --install . --prefix=../install --config Debug
|
||||
cmake --build . --config Release
|
||||
cmake --install . --prefix=../install --config Release
|
||||
CD ..
|
||||
|
||||
:: Build for x64
|
||||
CD x64
|
||||
cmake -G "Visual Studio 16 2019" -A x64 -DYYCC_BUILD_TESTBENCH=ON ../..
|
||||
cmake --build . --config Debug
|
||||
cmake --install . --prefix=../install --config Debug
|
||||
cmake --build . --config Release
|
||||
cmake --install . --prefix=../install --config Release
|
||||
CD ..
|
||||
|
||||
ECHO DONE
|
||||
19
script/linux_build.sh
Normal file
19
script/linux_build.sh
Normal file
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# Create build directory and enter it
|
||||
mkdir bin
|
||||
cd bin
|
||||
# Create internal build and install directory, then enter it
|
||||
mkdir build
|
||||
mkdir install
|
||||
cd build
|
||||
|
||||
# Build in Release mode
|
||||
cmake -DCMAKE_BUILD_TYPE=Release ../..
|
||||
cmake --build .
|
||||
cmake --install . --prefix=../install
|
||||
|
||||
# Back to root directory
|
||||
cd ..
|
||||
cd ..
|
||||
19
script/macos_build.sh
Normal file
19
script/macos_build.sh
Normal file
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# Create build directory and enter it
|
||||
mkdir bin
|
||||
cd bin
|
||||
# Create internal build and install directory, then enter it
|
||||
mkdir build
|
||||
mkdir install
|
||||
cd build
|
||||
|
||||
# Build in Release mode
|
||||
cmake -DCMAKE_BUILD_TYPE=Release ../..
|
||||
cmake --build .
|
||||
cmake --install . --prefix=../install
|
||||
|
||||
# Back to root directory
|
||||
cd ..
|
||||
cd ..
|
||||
18
script/windows_build.bat
Normal file
18
script/windows_build.bat
Normal file
@@ -0,0 +1,18 @@
|
||||
@ECHO OFF
|
||||
|
||||
:: Create build directory and enter it
|
||||
MKDIR bin
|
||||
CD bin
|
||||
:: Create internal build and install directory, then enter it
|
||||
MKDIR build
|
||||
MKDIR install
|
||||
CD build
|
||||
|
||||
:: Build with x64 architecture in Release mode
|
||||
cmake -A x64 ../..
|
||||
cmake --build . --config Release
|
||||
cmake --install . --prefix=../install --config Release
|
||||
|
||||
:: Back to root directory
|
||||
CD ..
|
||||
CD ..
|
||||
@@ -1,43 +1,120 @@
|
||||
# Configure version file
|
||||
configure_file(
|
||||
${CMAKE_CURRENT_LIST_DIR}/yycc/version.hpp.in
|
||||
${CMAKE_CURRENT_LIST_DIR}/yycc/version.hpp
|
||||
@ONLY
|
||||
)
|
||||
|
||||
# Create static library
|
||||
add_library(YYCCommonplace STATIC "")
|
||||
# Setup static library sources
|
||||
target_sources(YYCCommonplace
|
||||
PRIVATE
|
||||
# Sources
|
||||
COMHelper.cpp
|
||||
ConfigManager.cpp
|
||||
ConsoleHelper.cpp
|
||||
DialogHelper.cpp
|
||||
EncodingHelper.cpp
|
||||
ExceptionHelper.cpp
|
||||
FsPathPatch.cpp
|
||||
IOHelper.cpp
|
||||
StringHelper.cpp
|
||||
WinFctHelper.cpp
|
||||
yycc/string/reinterpret.cpp
|
||||
yycc/string/op.cpp
|
||||
yycc/patch/fopen.cpp
|
||||
yycc/patch/stream.cpp
|
||||
yycc/panic.cpp
|
||||
yycc/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/carton/pycodec.cpp
|
||||
yycc/carton/termcolor.cpp
|
||||
yycc/carton/wcwidth.cpp
|
||||
yycc/carton/tabulate.cpp
|
||||
yycc/carton/ironpad.cpp
|
||||
yycc/carton/csconsole.cpp
|
||||
yycc/carton/clap/option.cpp
|
||||
yycc/carton/clap/variable.cpp
|
||||
yycc/carton/clap/summary.cpp
|
||||
yycc/carton/clap/application.cpp
|
||||
yycc/carton/clap/manual.cpp
|
||||
yycc/carton/clap/parser.cpp
|
||||
yycc/carton/clap/resolver.cpp
|
||||
yycc/carton/binstore/types.cpp
|
||||
yycc/carton/binstore/setting.cpp
|
||||
yycc/carton/binstore/configuration.cpp
|
||||
yycc/carton/binstore/storage.cpp
|
||||
yycc/carton/lexer61.cpp
|
||||
)
|
||||
target_sources(YYCCommonplace
|
||||
PUBLIC
|
||||
FILE_SET HEADERS
|
||||
FILES
|
||||
# Headers
|
||||
# Common headers
|
||||
COMHelper.hpp
|
||||
ConfigManager.hpp
|
||||
ConsoleHelper.hpp
|
||||
DialogHelper.hpp
|
||||
EncodingHelper.hpp
|
||||
ExceptionHelper.hpp
|
||||
FsPathPatch.hpp
|
||||
IOHelper.hpp
|
||||
ParserHelper.hpp
|
||||
StringHelper.hpp
|
||||
WinFctHelper.hpp
|
||||
# Windows including guard pair
|
||||
WinImportPrefix.hpp
|
||||
WinImportSuffix.hpp
|
||||
# Misc
|
||||
YYCCInternal.hpp
|
||||
YYCCommonplace.hpp
|
||||
yycc.hpp
|
||||
yycc/version.hpp
|
||||
yycc/macro/version_cmp.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/macro/printf_checker.hpp
|
||||
yycc/cenum.hpp
|
||||
yycc/string.hpp
|
||||
yycc/string/reinterpret.hpp
|
||||
yycc/string/op.hpp
|
||||
yycc/patch/ptr_pad.hpp
|
||||
yycc/patch/fopen.hpp
|
||||
yycc/patch/stream.hpp
|
||||
yycc/patch/format.hpp
|
||||
yycc/patch/libcxx/enumerate.hpp
|
||||
yycc/patch/libcxx/stacktrace.hpp
|
||||
yycc/patch/libcxx/charconv.hpp
|
||||
yycc/num/parse.hpp
|
||||
yycc/num/stringify.hpp
|
||||
yycc/num/safe_cast.hpp
|
||||
yycc/num/safe_op.hpp
|
||||
yycc/num/op.hpp
|
||||
yycc/primitive.hpp
|
||||
yycc/option.hpp
|
||||
yycc/result.hpp
|
||||
yycc/prelude.hpp
|
||||
yycc/panic.hpp
|
||||
yycc/env.hpp
|
||||
yycc/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/encoding/stl.hpp
|
||||
yycc/encoding/windows.hpp
|
||||
yycc/encoding/iconv.hpp
|
||||
|
||||
yycc/carton/pycodec.hpp
|
||||
yycc/carton/termcolor.hpp
|
||||
yycc/carton/wcwidth.hpp
|
||||
yycc/carton/tabulate.hpp
|
||||
yycc/carton/ironpad.hpp
|
||||
yycc/carton/csconsole.hpp
|
||||
yycc/carton/clap.hpp
|
||||
yycc/carton/clap/types.hpp
|
||||
yycc/carton/clap/option.hpp
|
||||
yycc/carton/clap/variable.hpp
|
||||
yycc/carton/clap/summary.hpp
|
||||
yycc/carton/clap/application.hpp
|
||||
yycc/carton/clap/manual.hpp
|
||||
yycc/carton/clap/validator.hpp
|
||||
yycc/carton/clap/parser.hpp
|
||||
yycc/carton/clap/resolver.hpp
|
||||
yycc/carton/binstore.hpp
|
||||
yycc/carton/binstore/types.hpp
|
||||
yycc/carton/binstore/serdes.hpp
|
||||
yycc/carton/binstore/setting.hpp
|
||||
yycc/carton/binstore/configuration.hpp
|
||||
yycc/carton/binstore/storage.hpp
|
||||
yycc/carton/lexer61.hpp
|
||||
yycc/carton/fft.hpp
|
||||
)
|
||||
# Setup header infomations
|
||||
target_include_directories(YYCCommonplace
|
||||
@@ -45,35 +122,85 @@ 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
|
||||
set_target_properties(YYCCommonplace
|
||||
PROPERTIES
|
||||
CXX_STANDARD 17
|
||||
CXX_STANDARD_REQUIRED 17
|
||||
CXX_EXTENSION OFF
|
||||
)
|
||||
# Order Unicode charset for private using
|
||||
# Setup macros
|
||||
target_compile_definitions(YYCCommonplace
|
||||
PRIVATE
|
||||
PUBLIC
|
||||
# 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:MSVC>:YYCC_CC_MSVC>
|
||||
$<$<CXX_COMPILER_ID:GNU>:YYCC_CC_GCC>
|
||||
$<$<CXX_COMPILER_ID:Clang>:YYCC_CC_CLANG>
|
||||
$<$<CXX_COMPILER_ID:AppleClang>:YYCC_CC_CLANG> # We brutally think AppleClang is Clang.
|
||||
# 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>
|
||||
PUBLIC
|
||||
$<$<BOOL:${YYCC_CHARCONV_HAS_CHARS_FORMAT}>:YYCC_CHARCONV_HAS_CHARS_FORMAT>
|
||||
$<$<BOOL:${YYCC_CHARCONV_HAS_FROM_CHARS_RESULT}>:YYCC_CHARCONV_HAS_FROM_CHARS_RESULT>
|
||||
$<$<BOOL:${YYCC_CHARCONV_HAS_TO_CHARS_RESULT}>:YYCC_CHARCONV_HAS_TO_CHARS_RESULT>
|
||||
$<$<BOOL:${YYCC_CHARCONV_HAS_FROM_CHARS_INT}>:YYCC_CHARCONV_HAS_FROM_CHARS_INT>
|
||||
$<$<BOOL:${YYCC_CHARCONV_HAS_FROM_CHARS_FLOAT}>:YYCC_CHARCONV_HAS_FROM_CHARS_FLOAT>
|
||||
$<$<BOOL:${YYCC_CHARCONV_HAS_TO_CHARS_INT}>:YYCC_CHARCONV_HAS_TO_CHARS_INT>
|
||||
$<$<BOOL:${YYCC_CHARCONV_HAS_TO_CHARS_FLOAT}>:YYCC_CHARCONV_HAS_TO_CHARS_FLOAT>
|
||||
|
||||
)
|
||||
# Order build as UTF-8 in MSVC
|
||||
target_compile_options(YYCCommonplace
|
||||
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>
|
||||
)
|
||||
|
||||
# Fix GCC std::stacktrace link error
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 14)
|
||||
target_link_libraries(YYCCommonplace PRIVATE stdc++exp)
|
||||
else ()
|
||||
target_link_libraries(YYCCommonplace PRIVATE stdc++_libbacktrace)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
# Install binary and headers
|
||||
install(TARGETS YYCCommonplace
|
||||
EXPORT YYCCommonplaceTargets
|
||||
LIBRARY DESTINATION ${YYCC_INSTALL_PATH_LIB}
|
||||
ARCHIVE DESTINATION ${YYCC_INSTALL_PATH_LIB}
|
||||
INCLUDES DESTINATION include
|
||||
FILE_SET HEADERS DESTINATION include
|
||||
LIBRARY DESTINATION ${YYCC_INSTALL_LIB_PATH}
|
||||
ARCHIVE DESTINATION ${YYCC_INSTALL_LIB_PATH}
|
||||
INCLUDES DESTINATION ${YYCC_INSTALL_INCLUDE_PATH}
|
||||
FILE_SET HEADERS DESTINATION ${YYCC_INSTALL_INCLUDE_PATH}
|
||||
)
|
||||
|
||||
@@ -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,80 +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 COM fucntions related namespace.
|
||||
* @details
|
||||
* This namespace is Windows specific and is unavailable on other platforms.
|
||||
*
|
||||
* This namespace contain a COM Guard which make sure COM was initialized in current module when loading current module.
|
||||
* It is essential because all calling to COM functions should be under the premise that COM has been initialized.
|
||||
* This guard also will uninitialize COM when unloading this module.
|
||||
*
|
||||
* This namespace also provided various memory-safe types for interacting with COM functions.
|
||||
* Although Microsoft also has similar smart pointer called \c CComPtr.
|
||||
* But this library is eager to hide all Microsoft-related functions calling.
|
||||
* Using \c CComPtr is not corresponding with the philosophy of this library.
|
||||
* So these std-based smart pointer type were created.
|
||||
*
|
||||
* This namespace is used by internal functions as intended.
|
||||
* They should not be used outside of this library.
|
||||
* But if you compel to use them, it is also okey.
|
||||
*/
|
||||
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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
using SmartIFileDialog = std::unique_ptr<IFileDialog, ComPtrDeleter>;
|
||||
using SmartIFileOpenDialog = std::unique_ptr<IFileOpenDialog, ComPtrDeleter>;
|
||||
using SmartIShellItem = std::unique_ptr<IShellItem, ComPtrDeleter>;
|
||||
using SmartIShellItemArray = std::unique_ptr<IShellItemArray, ComPtrDeleter>;
|
||||
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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,148 +0,0 @@
|
||||
#include "ConfigManager.hpp"
|
||||
|
||||
#include "EncodingHelper.hpp"
|
||||
#include "IOHelper.hpp"
|
||||
|
||||
namespace YYCC::ConfigManager {
|
||||
|
||||
#pragma region Core Manager
|
||||
|
||||
CoreManager::CoreManager(
|
||||
const yycc_char8_t* cfg_file_path,
|
||||
uint64_t version_identifier,
|
||||
std::initializer_list<AbstractSetting*> settings) :
|
||||
m_CfgFilePath(), m_VersionIdentifier(version_identifier), m_Settings() {
|
||||
// assign cfg path
|
||||
if (cfg_file_path != nullptr)
|
||||
m_CfgFilePath = cfg_file_path;
|
||||
// assign settings
|
||||
for (auto* setting : settings) {
|
||||
m_Settings.try_emplace(setting->GetName(), setting);
|
||||
}
|
||||
}
|
||||
|
||||
bool CoreManager::Load() {
|
||||
// reset all settings first
|
||||
Reset();
|
||||
|
||||
// get file handle
|
||||
auto fs = this->GetFileHandle(YYCC_U8("rb"));
|
||||
if (fs.get() == nullptr) {
|
||||
// if we fail to get, it means that we do not have corresponding cfg file.
|
||||
// all settings should be reset to default value.
|
||||
return true;
|
||||
}
|
||||
|
||||
// fetch version info
|
||||
uint64_t version_info;
|
||||
if (std::fread(&version_info, 1u, sizeof(version_info), fs.get()) != sizeof(version_info))
|
||||
return false;
|
||||
// check version
|
||||
// if read version is greater than we expected,
|
||||
// it means that this cfg file is created by the program higer than this.
|
||||
// we should not read anything from it.
|
||||
// however, for compaitibility reason, we allow read old cfg data.
|
||||
if (version_info > m_VersionIdentifier)
|
||||
return true;
|
||||
|
||||
// fetch setting item from file
|
||||
yycc_u8string name_cache;
|
||||
while (true) {
|
||||
// try fetch setting name
|
||||
// fetch name length
|
||||
size_t name_length;
|
||||
if (std::fread(&name_length, 1u, sizeof(name_length), fs.get()) != sizeof(name_length)) {
|
||||
// we also check whether reach EOF at there.
|
||||
if (std::feof(fs.get())) break;
|
||||
else return false;
|
||||
}
|
||||
// fetch name body
|
||||
name_cache.resize(name_length);
|
||||
if (std::fread(name_cache.data(), 1u, name_length, fs.get()) != name_length)
|
||||
return false;
|
||||
|
||||
// get setting data length
|
||||
size_t data_length;
|
||||
if (std::fread(&data_length, 1u, sizeof(data_length), fs.get()) != sizeof(data_length))
|
||||
return false;
|
||||
|
||||
// get matched setting first
|
||||
const auto& found = m_Settings.find(name_cache);
|
||||
if (found != m_Settings.end()) {
|
||||
// found. read data for it
|
||||
found->second->ResizeData(data_length);
|
||||
if (std::fread(found->second->GetDataPtr(), 1u, data_length, fs.get()) != data_length)
|
||||
return false;
|
||||
// call user defined load function
|
||||
// if fail to parse, reset to default value
|
||||
if (!found->second->UserLoad())
|
||||
found->second->UserReset();
|
||||
} else {
|
||||
// fail to find. skip this unknown setting
|
||||
if (fseek(fs.get(), static_cast<long>(data_length), SEEK_CUR) != 0)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CoreManager::Save() {
|
||||
// get file handle
|
||||
auto fs = this->GetFileHandle(YYCC_U8("wb"));
|
||||
// if we fail to get, return false.
|
||||
if (fs == nullptr) return false;
|
||||
|
||||
// write config data
|
||||
uint64_t version_info = m_VersionIdentifier;
|
||||
if (std::fwrite(&version_info, 1u, sizeof(version_info), fs.get()) != sizeof(version_info))
|
||||
return false;
|
||||
|
||||
// iterate all data for writing
|
||||
for (const auto& pair : m_Settings) {
|
||||
// do user defined save
|
||||
// if failed, skip this setting
|
||||
if (!pair.second->UserSave())
|
||||
continue;
|
||||
|
||||
// write setting name
|
||||
// write name length
|
||||
size_t name_length = pair.first.size();
|
||||
if (std::fwrite(&name_length, 1u, sizeof(name_length), fs.get()) != sizeof(name_length))
|
||||
return false;
|
||||
// write name body
|
||||
if (std::fwrite(pair.first.c_str(), 1u, name_length, fs.get()) != name_length)
|
||||
return false;
|
||||
|
||||
// write setting daat
|
||||
// write data length
|
||||
size_t data_length = pair.second->GetDataSize();
|
||||
if (std::fwrite(&data_length, 1u, sizeof(data_length), fs.get()) != sizeof(data_length))
|
||||
return false;
|
||||
// write data body
|
||||
if (std::fwrite(pair.second->GetDataPtr(), 1u, data_length, fs.get()) != data_length)
|
||||
return false;
|
||||
}
|
||||
|
||||
// all settings done, return true
|
||||
return true;
|
||||
}
|
||||
|
||||
void CoreManager::Reset() {
|
||||
for (const auto& pair : m_Settings) {
|
||||
pair.second->UserReset();
|
||||
}
|
||||
}
|
||||
|
||||
CoreManager::FileHandleGuard_t CoreManager::GetFileHandle(const yycc_char8_t* mode) const {
|
||||
return CoreManager::FileHandleGuard_t(
|
||||
IOHelper::UTF8FOpen(this->m_CfgFilePath.c_str(), mode),
|
||||
[](FILE* fs) -> void {
|
||||
if (fs != nullptr) std::fclose(fs);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
#pragma once
|
||||
#include "YYCCInternal.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <initializer_list>
|
||||
#include <type_traits>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace YYCC::ConfigManager {
|
||||
|
||||
template<typename _Ty>
|
||||
struct Constrain {
|
||||
using CheckFct_t = std::function<bool(const _Ty&)>;
|
||||
//using CorrectFct_t = std::function<_Ty(const _Ty&)>;
|
||||
CheckFct_t m_CheckFct;
|
||||
//CorrectFct_t m_CorrectFct;
|
||||
|
||||
bool IsValid() const {
|
||||
return m_CheckFct != nullptr/* && m_CorrectFct != nullptr*/;
|
||||
}
|
||||
};
|
||||
|
||||
namespace ConstrainPresets {
|
||||
|
||||
template<typename _Ty, std::enable_if_t<std::is_arithmetic_v<_Ty> && !std::is_enum_v<_Ty> && !std::is_same_v<_Ty, bool>, int> = 0>
|
||||
Constrain<_Ty> GetNumberRangeConstrain(_Ty min_value, _Ty max_value) {
|
||||
if (min_value > max_value)
|
||||
throw std::invalid_argument("invalid min max value for NumberRangeConstrain");
|
||||
return Constrain<_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); }*/
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class AbstractSetting {
|
||||
friend class CoreManager;
|
||||
public:
|
||||
AbstractSetting(const yycc_char8_t* name) : m_Name(), m_RawData() {
|
||||
if (name != nullptr) m_Name = name;
|
||||
}
|
||||
virtual ~AbstractSetting() {}
|
||||
|
||||
// Name interface
|
||||
public:
|
||||
const yycc_u8string& GetName() const { return m_Name; }
|
||||
private:
|
||||
yycc_u8string m_Name;
|
||||
|
||||
// User Implementations
|
||||
protected:
|
||||
virtual bool UserLoad() = 0;
|
||||
virtual bool UserSave() = 0;
|
||||
virtual void UserReset() = 0;
|
||||
|
||||
// Buffer related functions
|
||||
protected:
|
||||
void ResizeData(size_t new_size) { m_RawData.resize(new_size); }
|
||||
const void* GetDataPtr() const { return m_RawData.data(); }
|
||||
void* GetDataPtr() { return m_RawData.data(); }
|
||||
size_t GetDataSize() const { return m_RawData.size(); }
|
||||
private:
|
||||
std::vector<uint8_t> m_RawData;
|
||||
};
|
||||
|
||||
class CoreManager {
|
||||
public:
|
||||
CoreManager(
|
||||
const yycc_char8_t* cfg_file_path,
|
||||
uint64_t version_identifier,
|
||||
std::initializer_list<AbstractSetting*> settings);
|
||||
~CoreManager() {}
|
||||
|
||||
// Core functions
|
||||
public:
|
||||
bool Load();
|
||||
bool Save();
|
||||
void Reset();
|
||||
|
||||
private:
|
||||
using FileHandleGuard_t = std::unique_ptr<FILE, std::function<void(FILE*)>>;
|
||||
FileHandleGuard_t GetFileHandle(const yycc_char8_t* mode) const;
|
||||
|
||||
yycc_u8string m_CfgFilePath;
|
||||
uint64_t m_VersionIdentifier;
|
||||
std::map<yycc_u8string, AbstractSetting*> m_Settings;
|
||||
};
|
||||
|
||||
#pragma region Setting Presets
|
||||
|
||||
template<typename _Ty, std::enable_if_t<std::is_arithmetic_v<_Ty> || std::is_enum_v<_Ty>, int> = 0>
|
||||
class NumberSetting : public AbstractSetting {
|
||||
public:
|
||||
NumberSetting(const yycc_char8_t* name, _Ty default_value, Constrain<_Ty> constrain = Constrain<_Ty> {}) :
|
||||
AbstractSetting(name), m_Data(default_value), m_DefaultData(default_value), m_Constrain(constrain) {}
|
||||
virtual ~NumberSetting() {}
|
||||
|
||||
_Ty Get() const { return m_Data; }
|
||||
bool Set(_Ty new_data) {
|
||||
// validate data
|
||||
if (m_Constrain.IsValid() && !m_Constrain.m_CheckFct(new_data))
|
||||
return false;
|
||||
// assign data
|
||||
m_Data = new_data;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual bool UserLoad() override {
|
||||
// read data
|
||||
if (sizeof(m_Data) != GetDataSize())
|
||||
return false;
|
||||
m_Data = *reinterpret_cast<const _Ty*>(GetDataPtr());
|
||||
// check data
|
||||
if (m_Constrain.IsValid() && !m_Constrain.m_CheckFct(m_Data))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
virtual bool UserSave() override {
|
||||
// write data
|
||||
ResizeData(sizeof(m_Data));
|
||||
*reinterpret_cast<_Ty*>(GetDataPtr()) = m_Data;
|
||||
return true;
|
||||
}
|
||||
virtual void UserReset() override {
|
||||
m_Data = m_DefaultData;
|
||||
}
|
||||
|
||||
_Ty m_Data, m_DefaultData;
|
||||
Constrain<_Ty> m_Constrain;
|
||||
};
|
||||
|
||||
class StringSetting : public AbstractSetting {
|
||||
public:
|
||||
StringSetting(const yycc_char8_t* name, const yycc_char8_t* default_value, Constrain<yycc_u8string> constrain = Constrain<yycc_u8string> {}) :
|
||||
AbstractSetting(name), m_Data(), m_DefaultData(), m_Constrain(constrain) {
|
||||
if (default_value != nullptr) {
|
||||
m_Data = default_value;
|
||||
m_DefaultData = default_value;
|
||||
}
|
||||
}
|
||||
virtual ~StringSetting() {}
|
||||
|
||||
const yycc_u8string& Get() const { return m_Data; }
|
||||
bool Set(const yycc_char8_t* new_data) {
|
||||
// check data validation
|
||||
if (new_data == nullptr)
|
||||
return false;
|
||||
if (m_Constrain.IsValid() && !m_Constrain.m_CheckFct(m_Data))
|
||||
return false;
|
||||
// assign data
|
||||
m_Data = new_data;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual bool UserLoad() override {
|
||||
// read string length
|
||||
size_t string_length;
|
||||
if (GetDataSize() < sizeof(string_length))
|
||||
return false;
|
||||
string_length = *reinterpret_cast<const size_t*>(GetDataPtr());
|
||||
// read string body
|
||||
if (GetDataSize() != sizeof(string_length) + string_length)
|
||||
return false;
|
||||
m_Data.assign(
|
||||
reinterpret_cast<const yycc_char8_t*>(static_cast<const uint8_t*>(GetDataPtr()) + sizeof(string_length)),
|
||||
string_length
|
||||
);
|
||||
// check data
|
||||
if (m_Constrain.IsValid() && !m_Constrain.m_CheckFct(m_Data))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
virtual bool UserSave() override {
|
||||
// allocate result buffer
|
||||
size_t string_length = m_Data.size();
|
||||
ResizeData(sizeof(string_length) + string_length);
|
||||
// get pointer
|
||||
uint8_t* ptr = static_cast<uint8_t*>(GetDataPtr());
|
||||
// assign string length
|
||||
*reinterpret_cast<size_t*>(ptr) = string_length;
|
||||
// assign string body
|
||||
std::memcpy(ptr + sizeof(string_length), m_Data.data(), string_length);
|
||||
return true;
|
||||
}
|
||||
virtual void UserReset() override {
|
||||
m_Data = m_DefaultData;
|
||||
}
|
||||
|
||||
yycc_u8string m_Data, m_DefaultData;
|
||||
Constrain<yycc_u8string> m_Constrain;
|
||||
};
|
||||
|
||||
#pragma endregion
|
||||
|
||||
|
||||
}
|
||||
@@ -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::ToNative(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,175 +0,0 @@
|
||||
#pragma once
|
||||
#include "YYCCInternal.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* @brief The namespace providing universal Console visiting functions like C-Sharp Console class.
|
||||
* @details
|
||||
* \par Why this Namespace
|
||||
* Windows console doesn't support UTF8 very well.
|
||||
* The standard input output functions can not work properly on Windows with UTF8.
|
||||
* So we create this namespace and provide various console-related functions
|
||||
* to patch Windows console and let it more like the console in other platforms.
|
||||
* \par
|
||||
* The function provided in this function can be called in any platforms.
|
||||
* In Windows, the implementation will use Windows native function,
|
||||
* and in other platform, the implementation will redirect request to standard C function
|
||||
* like std::fputs and etc.
|
||||
* So the programmer do not need to be worried about which function should they use,
|
||||
* and don't need to use macro to use different IO function in different platforms.
|
||||
* It is just enough that fully use the functions provided in this namespace.
|
||||
* \par
|
||||
* All IO functions this namespace provided are UTF8-based.
|
||||
* It also means that input output string should always be UTF8 encoded.
|
||||
*
|
||||
* \par Input Functions
|
||||
* Please note that EOL will automatically converted into LF on Windows platform, not CRLF.
|
||||
* This action actually is removing all CR chars in result string.
|
||||
* This behavior affect nothing in most cases but it still is possible break something in some special case.
|
||||
* \par
|
||||
* Due to implementation, if you decide to use this function,
|
||||
* you should give up using any other function to read stdin stream,
|
||||
* such as std::gets() and std::cin.
|
||||
* Because this function may read chars which is more than needed.
|
||||
* These extra chars will be stored in this function and can be used next calling.
|
||||
* But these chars can not be visited by stdin again.
|
||||
* This behavior may cause bug.
|
||||
* So if you decide using this function, stick on it and do not change.
|
||||
* \par
|
||||
* Due to implementation, this function do not support hot switch of stdin.
|
||||
* It means that stdin can be redirected before first calling of this function,
|
||||
* but it should not be redirected during program running.
|
||||
* The reason is the same one introduced above.
|
||||
*
|
||||
* \par Output Functions
|
||||
* In current implementation, EOL will not be converted automatically to CRLF.
|
||||
* This is different with other stream read functions provided in this namespace.
|
||||
* \par
|
||||
* Comparing with other stream read functions provided in this namespace,
|
||||
* stream write function support hot switch of stdout and stderr.
|
||||
* Because they do not have internal buffer storing something.
|
||||
* \par
|
||||
* In this namespace, there are various stream write function.
|
||||
* There is a list telling you how to choose one from them for using:
|
||||
* \li Functions with leading "Err" will write data into stderr,
|
||||
* otherwise they will write data into stdout.
|
||||
* \li Functions with embedded "Format" are output functions with format feature
|
||||
* like std::fprintf(), otherwise the functions with embedded "Write" will
|
||||
* only write plain string like std::fputs().
|
||||
* \li Functions with trailing "Line" will write extra EOL to break current line.
|
||||
* This is commonly used, otherwise functions will only write the text provided by arguments,
|
||||
* without adding something.
|
||||
*/
|
||||
namespace YYCC::ConsoleHelper {
|
||||
|
||||
#define YYCC_COLORHDR_BLACK "\033[30m"
|
||||
#define YYCC_COLORHDR_RED "\033[31m"
|
||||
#define YYCC_COLORHDR_GREEN "\033[32m"
|
||||
#define YYCC_COLORHDR_YELLOW "\033[33m"
|
||||
#define YYCC_COLORHDR_BLUE "\033[34m"
|
||||
#define YYCC_COLORHDR_MAGENTA "\033[35m"
|
||||
#define YYCC_COLORHDR_CYAN "\033[36m"
|
||||
#define YYCC_COLORHDR_WHITE "\033[37m"
|
||||
|
||||
#define YYCC_COLORHDR_LIGHT_BLACK "\033[90m"
|
||||
#define YYCC_COLORHDR_LIGHT_RED "\033[91m"
|
||||
#define YYCC_COLORHDR_LIGHT_GREEN "\033[92m"
|
||||
#define YYCC_COLORHDR_LIGHT_YELLOW "\033[93m"
|
||||
#define YYCC_COLORHDR_LIGHT_BLUE "\033[94m"
|
||||
#define YYCC_COLORHDR_LIGHT_MAGENTA "\033[95m"
|
||||
#define YYCC_COLORHDR_LIGHT_CYAN "\033[96m"
|
||||
#define YYCC_COLORHDR_LIGHT_WHITE "\033[97m"
|
||||
|
||||
#define YYCC_COLORTAIL "\033[0m"
|
||||
|
||||
|
||||
#define YYCC_COLOR_BLACK(T) "\033[30m" T "\033[0m"
|
||||
#define YYCC_COLOR_RED(T) "\033[31m" T "\033[0m"
|
||||
#define YYCC_COLOR_GREEN(T) "\033[32m" T "\033[0m"
|
||||
#define YYCC_COLOR_YELLOW(T) "\033[33m" T "\033[0m"
|
||||
#define YYCC_COLOR_BLUE(T) "\033[34m" T "\033[0m"
|
||||
#define YYCC_COLOR_MAGENTA(T) "\033[35m" T "\033[0m"
|
||||
#define YYCC_COLOR_CYAN(T) "\033[36m" T "\033[0m"
|
||||
#define YYCC_COLOR_WHITE(T) "\033[37m" T "\033[0m"
|
||||
|
||||
#define YYCC_COLOR_LIGHT_BLACK(T) "\033[90m" T "\033[0m"
|
||||
#define YYCC_COLOR_LIGHT_RED(T) "\033[91m" T "\033[0m"
|
||||
#define YYCC_COLOR_LIGHT_GREEN(T) "\033[92m" T "\033[0m"
|
||||
#define YYCC_COLOR_LIGHT_YELLOW(T) "\033[93m" T "\033[0m"
|
||||
#define YYCC_COLOR_LIGHT_BLUE(T) "\033[94m" T "\033[0m"
|
||||
#define YYCC_COLOR_LIGHT_MAGENTA(T) "\033[95m" T "\033[0m"
|
||||
#define YYCC_COLOR_LIGHT_CYAN(T) "\033[96m" T "\033[0m"
|
||||
#define YYCC_COLOR_LIGHT_WHITE(T) "\033[97m" T "\033[0m"
|
||||
|
||||
/**
|
||||
* @brief Enable Windows console color support.
|
||||
* @details This actually is enable virtual console feature for stdout and 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 Universal console read function
|
||||
* @return
|
||||
* The UTF8 encoded string this function read. EOL is excluded.
|
||||
* Empty string if user just press Enter key or function failed.
|
||||
* @remarks
|
||||
* This function is more like C# Console.ReadLine().
|
||||
* It read user input with UTF8 encoding until reaching EOL.
|
||||
* \par
|
||||
* This function also can be used as ordering user press Enter key by
|
||||
* simply calling this function and ignoring its return value.
|
||||
*/
|
||||
yycc_u8string ReadLine();
|
||||
|
||||
/**
|
||||
* @brief Universal console write function with format feature.
|
||||
* @param[in] u8_fmt The format string.
|
||||
* @param[in] ... The arguments to be formatted.
|
||||
*/
|
||||
void Format(const yycc_char8_t* u8_fmt, ...);
|
||||
/**
|
||||
* @brief Universal console write function with format and auto EOL feature.
|
||||
* @param[in] u8_fmt The format string.
|
||||
* @param[in] ... The arguments to be formatted.
|
||||
*/
|
||||
void FormatLine(const yycc_char8_t* u8_fmt, ...);
|
||||
/**
|
||||
* @brief Universal console write function.
|
||||
* @param[in] u8_strl The string to be written.
|
||||
*/
|
||||
void Write(const yycc_char8_t* u8_strl);
|
||||
/**
|
||||
* @brief Universal console write function with auto EOL feature.
|
||||
* @param[in] u8_strl The string to be written.
|
||||
*/
|
||||
void WriteLine(const yycc_char8_t* u8_strl);
|
||||
|
||||
/**
|
||||
* @brief Universal console error write function with format and feature.
|
||||
* @param[in] u8_fmt The format string.
|
||||
* @param[in] ... The arguments to be formatted.
|
||||
*/
|
||||
void ErrFormat(const yycc_char8_t* u8_fmt, ...);
|
||||
/**
|
||||
* @brief Universal console error write function with format and auto EOL feature.
|
||||
* @param[in] u8_fmt The format string.
|
||||
* @param[in] ... The arguments to be formatted.
|
||||
*/
|
||||
void ErrFormatLine(const yycc_char8_t* u8_fmt, ...);
|
||||
/**
|
||||
* @brief Universal console error write function.
|
||||
* @param[in] u8_strl The string to be written.
|
||||
*/
|
||||
void ErrWrite(const yycc_char8_t* u8_strl);
|
||||
/**
|
||||
* @brief Universal console error write function with auto EOL feature.
|
||||
* @param[in] u8_strl The string to be written.
|
||||
*/
|
||||
void ErrWriteLine(const yycc_char8_t* u8_strl);
|
||||
|
||||
}
|
||||
@@ -1,377 +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
|
||||
yycc_u8string joined_modes(YYCC::StringHelper::Join(it.second, 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,221 +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"
|
||||
|
||||
namespace YYCC::DialogHelper {
|
||||
|
||||
/**
|
||||
* @brief The class represent the file types region in file dialog
|
||||
* @details THis class is specific for Windows use, not user oriented.
|
||||
*/
|
||||
class WinFileFilters {
|
||||
friend class FileFilters;
|
||||
friend class WinFileDialog;
|
||||
public:
|
||||
WinFileFilters() : m_WinFilters(), m_WinDataStruct(nullptr) {}
|
||||
|
||||
UINT GetFilterCount() const {
|
||||
return static_cast<UINT>(m_WinFilters.size());
|
||||
}
|
||||
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;
|
||||
|
||||
void Clear() {
|
||||
m_WinDataStruct.reset();
|
||||
m_WinFilters.clear();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The class represent the file types region in file dialog.
|
||||
* @details This class is user oriented. User can use function manipulate file types
|
||||
* and final generation function will produce Windows-understood data struct from this.
|
||||
*/
|
||||
class FileFilters {
|
||||
public:
|
||||
FileFilters() : m_Filters() {}
|
||||
|
||||
/**
|
||||
* @brief Add a filter pair in file types list.
|
||||
* @param filter_name[in] The friendly name of the filter.
|
||||
* @param il[in] A C++ initialize list.
|
||||
* Every entries must be `const yycc_char8_t*` represent 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: `Add("Microsoft Word (*.doc; *.docx)", {"*.doc", "*.docx"})`
|
||||
*/
|
||||
bool Add(const yycc_char8_t* filter_name, std::initializer_list<const yycc_char8_t*> il);
|
||||
/**
|
||||
* @brief Clear filter pairs for following re-use.
|
||||
*/
|
||||
void Clear() { m_Filters.clear(); }
|
||||
/**
|
||||
* @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 Generate Windows dialog system used data struct.
|
||||
* @param win_result[out] The class holding the generated filter data struct.
|
||||
* @return True if generation is success, otherwise false.
|
||||
* @remarks User should not call this function, this function is used in 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 represent the file dialog
|
||||
* @details THis class is specific for Windows use, not user oriented.
|
||||
*/
|
||||
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) {}
|
||||
|
||||
bool HasOwner() const { return m_WinOwner != NULL; }
|
||||
HWND GetOwner() const { return m_WinOwner; }
|
||||
|
||||
const WinFileFilters& GetFileTypes() const { return m_WinFileTypes; }
|
||||
UINT GetDefaultFileTypeIndex() const { return m_WinDefaultFileTypeIndex; }
|
||||
|
||||
bool HasTitle() const { return m_HasTitle; }
|
||||
const wchar_t* GetTitle() const { return m_WinTitle.c_str(); }
|
||||
bool HasInitFileName() const { return m_HasInitFileName; }
|
||||
const wchar_t* GetInitFileName() const { return m_WinInitFileName.c_str(); }
|
||||
|
||||
bool HasInitDirectory() const { return m_WinInitDirectory.get() != nullptr; }
|
||||
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, you should plus 1 for this index when generating this struct from
|
||||
* user oriented file dialog parameters.
|
||||
*/
|
||||
UINT m_WinDefaultFileTypeIndex;
|
||||
bool m_HasTitle, m_HasInitFileName;
|
||||
std::wstring m_WinTitle, m_WinInitFileName;
|
||||
COMHelper::SmartIShellItem m_WinInitDirectory;
|
||||
|
||||
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 represent the file dialog.
|
||||
* @details This class is user oriented. User can use function manipulate file dialog properties
|
||||
* and final generation function will produce Windows-understood data struct from this.
|
||||
*/
|
||||
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) {}
|
||||
|
||||
void SetOwner(HWND owner) { m_Owner = owner; }
|
||||
void SetTitle(const yycc_char8_t* title) {
|
||||
if (m_HasTitle = title != nullptr)
|
||||
m_Title = title;
|
||||
}
|
||||
FileFilters& ConfigreFileTypes() {
|
||||
return m_FileTypes;
|
||||
}
|
||||
void SetDefaultFileTypeIndex(size_t idx) { m_DefaultFileTypeIndex = idx; }
|
||||
void SetInitFileName(const yycc_char8_t* init_filename) {
|
||||
if (m_HasInitFileName = init_filename != nullptr)
|
||||
m_InitFileName = init_filename;
|
||||
}
|
||||
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 win_result[out] The class holding the generated filter data struct.
|
||||
* @return True if generation is success, otherwise false.
|
||||
* @remarks User should not call this function, this function is used in 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 Although Windows notice that this is a 1-based index,
|
||||
* but for universal experience, we order this is 0-based index.
|
||||
*/
|
||||
size_t m_DefaultFileTypeIndex;
|
||||
};
|
||||
|
||||
bool OpenFileDialog(const FileDialog& params, yycc_u8string& ret);
|
||||
bool OpenMultipleFileDialog(const FileDialog& params, std::vector<yycc_u8string>& ret);
|
||||
bool SaveFileDialog(const FileDialog& params, yycc_u8string& ret);
|
||||
|
||||
bool OpenFolderDialog(const FileDialog& params, yycc_u8string& ret);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,371 +0,0 @@
|
||||
#include "EncodingHelper.hpp"
|
||||
|
||||
#include <locale>
|
||||
|
||||
namespace YYCC::EncodingHelper {
|
||||
|
||||
#pragma region UTF8 Native Convertion
|
||||
|
||||
const yycc_char8_t* ToUTF8(const char* src) {
|
||||
return reinterpret_cast<const yycc_char8_t*>(src);
|
||||
}
|
||||
yycc_char8_t* ToUTF8(char* src) {
|
||||
return reinterpret_cast<yycc_char8_t*>(src);
|
||||
}
|
||||
yycc_u8string ToUTF8(const std::string_view& src) {
|
||||
return yycc_u8string(reinterpret_cast<const yycc_char8_t*>(src.data()), src.size());
|
||||
}
|
||||
yycc_u8string_view ToUTF8View(const std::string_view& src) {
|
||||
return yycc_u8string_view(reinterpret_cast<const yycc_char8_t*>(src.data()), src.size());
|
||||
}
|
||||
|
||||
const char* ToNative(const yycc_char8_t* src) {
|
||||
return reinterpret_cast<const char*>(src);
|
||||
}
|
||||
char* ToNative(yycc_char8_t* src) {
|
||||
return reinterpret_cast<char*>(src);
|
||||
}
|
||||
std::string ToNative(const yycc_u8string_view& src) {
|
||||
return std::string(reinterpret_cast<const char*>(src.data()), src.size());
|
||||
}
|
||||
std::string_view ToNativeView(const yycc_u8string_view& src) {
|
||||
return std::string_view(reinterpret_cast<const char*>(src.data()), src.size());
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
/* 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(ToNativeView(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
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#pragma region UTF8 UTF16 UTF32 Help Funcs
|
||||
|
||||
/*
|
||||
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 \c yycc_char8_t.
|
||||
So it is essential define a type which can correctly trigger specific specializations of \c std::codecv in there.
|
||||
*/
|
||||
#if defined(__cpp_char8_t)
|
||||
using CodecvtUTF8Char_t = char8_t;
|
||||
#else
|
||||
using CodecvtUTF8Char_t = 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_t = std::codecvt<_TChar, CodecvtUTF8Char_t, 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 bool UTF8ToUTFOther(const yycc_u8string_view& src, std::basic_string<_TChar>& dst) {
|
||||
// Reference:
|
||||
// https://zh.cppreference.com/w/cpp/locale/codecvt/in
|
||||
|
||||
// if src is empty, return directly
|
||||
if (src.empty()) {
|
||||
dst.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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_t<_TChar>>(this_locale);
|
||||
|
||||
// convertion preparation
|
||||
std::mbstate_t mb{};
|
||||
dst.resize(src.size());
|
||||
const CodecvtUTF8Char_t* intern_from = reinterpret_cast<const CodecvtUTF8Char_t*>(src.data()),
|
||||
*intern_from_end = reinterpret_cast<const CodecvtUTF8Char_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_t<_TChar>::ok)
|
||||
return false;
|
||||
// resize result and return
|
||||
dst.resize(extern_to_next - dst.data());
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename _TChar, std::enable_if_t<std::is_same_v<_TChar, char16_t> || std::is_same_v<_TChar, char32_t>, int> = 0>
|
||||
static bool UTFOtherToUTF8(const std::basic_string_view<_TChar>& src, yycc_u8string& dst) {
|
||||
// Reference:
|
||||
// https://zh.cppreference.com/w/cpp/locale/codecvt/out
|
||||
|
||||
// if src is empty, return directly
|
||||
if (src.empty()) {
|
||||
dst.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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_t<_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_t* extern_to = reinterpret_cast<CodecvtUTF8Char_t*>(dst.data()),
|
||||
*extern_to_end = reinterpret_cast<CodecvtUTF8Char_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_t<_TChar>::ok)
|
||||
return false;
|
||||
// resize result and retuen
|
||||
dst.resize(extern_to_next - reinterpret_cast<CodecvtUTF8Char_t*>(dst.data()));
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region UTF8ToUTF16
|
||||
|
||||
bool UTF8ToUTF16(const yycc_u8string_view& src, std::u16string& dst) {
|
||||
return UTF8ToUTFOther<char16_t>(src, dst);
|
||||
}
|
||||
bool UTF8ToUTF16(const yycc_char8_t* src, std::u16string& dst) {
|
||||
CONVFCT_TYPE2(UTF8ToUTF16, yycc_char8_t, char16_t);
|
||||
}
|
||||
std::u16string UTF8ToUTF16(const yycc_u8string_view& src) {
|
||||
CONVFCT_TYPE3(UTF8ToUTF16, yycc_char8_t, char16_t);
|
||||
}
|
||||
std::u16string UTF8ToUTF16(const yycc_char8_t* src) {
|
||||
CONVFCT_TYPE4(UTF8ToUTF16, yycc_char8_t, char16_t);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region UTF16ToUTF8
|
||||
|
||||
bool UTF16ToUTF8(const std::u16string_view& src, yycc_u8string& dst) {
|
||||
return UTFOtherToUTF8<char16_t>(src, dst);
|
||||
}
|
||||
bool UTF16ToUTF8(const char16_t* src, yycc_u8string& dst) {
|
||||
CONVFCT_TYPE2(UTF16ToUTF8, char16_t, yycc_char8_t);
|
||||
}
|
||||
yycc_u8string UTF16ToUTF8(const std::u16string_view& src) {
|
||||
CONVFCT_TYPE3(UTF16ToUTF8, char16_t, yycc_char8_t);
|
||||
}
|
||||
yycc_u8string UTF16ToUTF8(const char16_t* src) {
|
||||
CONVFCT_TYPE4(UTF16ToUTF8, char16_t, yycc_char8_t);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region UTF8ToUTF32
|
||||
|
||||
bool UTF8ToUTF32(const yycc_u8string_view& src, std::u32string& dst) {
|
||||
return UTF8ToUTFOther<char32_t>(src, dst);
|
||||
}
|
||||
bool UTF8ToUTF32(const yycc_char8_t* src, std::u32string& dst) {
|
||||
CONVFCT_TYPE2(UTF8ToUTF32, yycc_char8_t, char32_t);
|
||||
}
|
||||
std::u32string UTF8ToUTF32(const yycc_u8string_view& src) {
|
||||
CONVFCT_TYPE3(UTF8ToUTF32, yycc_char8_t, char32_t);
|
||||
}
|
||||
std::u32string UTF8ToUTF32(const yycc_char8_t* src) {
|
||||
CONVFCT_TYPE4(UTF8ToUTF32, yycc_char8_t, char32_t);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region UTF32ToUTF8
|
||||
|
||||
bool UTF32ToUTF8(const std::u32string_view& src, yycc_u8string& dst) {
|
||||
return UTFOtherToUTF8<char32_t>(src, dst);
|
||||
}
|
||||
bool UTF32ToUTF8(const char32_t* src, yycc_u8string& dst) {
|
||||
CONVFCT_TYPE2(UTF32ToUTF8, char32_t, yycc_char8_t);
|
||||
}
|
||||
yycc_u8string UTF32ToUTF8(const std::u32string_view& src) {
|
||||
CONVFCT_TYPE3(UTF32ToUTF8, char32_t, yycc_char8_t);
|
||||
}
|
||||
yycc_u8string UTF32ToUTF8(const char32_t* src) {
|
||||
CONVFCT_TYPE4(UTF32ToUTF8, char32_t, yycc_char8_t);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#undef CONVFCT_TYPE2
|
||||
#undef CONVFCT_TYPE3
|
||||
#undef CONVFCT_TYPE4
|
||||
|
||||
}
|
||||
|
||||
@@ -1,117 +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 namespace handling encoding issues.
|
||||
* @details
|
||||
* \par Windows Encoding Convertion
|
||||
* This namespace provides the convertion between wchar_t, UTF8 and code-page-based string:
|
||||
* The function name has following format: \c AAAToBBB.
|
||||
* AAA is the source string and BBB is target string.
|
||||
* AAA and BBB has following possible value:
|
||||
* \li \c Char: Code-page-based string. Usually it will add a code page parameter for function to get the code page of this string. For code page, please see Microsoft document.
|
||||
* \li \c UTF8: UTF8 string.
|
||||
* \li \c Wchar: wchar_t string.
|
||||
* \par
|
||||
* For example: \c WcharToUTF8 will perform the convertion from wchar_t to UTF8,
|
||||
* and \c CharToChar will perform the convertion between 2 code-page-based string and caller can specify individual code page for these 2 string.
|
||||
* \par
|
||||
* These functions are Windows specific and are unavailable on other platforms.
|
||||
* Becasue Windows use wchar_t string as its function arguments for globalization, and this library use UTF8 everywhere.
|
||||
* So it should have a bidirectional way to do convertion between wchar_t string and UTF8 string.
|
||||
*
|
||||
* \par UTF32, UTF16 and UTF8 Convertion
|
||||
* This namespace also provide the convertion among UTF32, UTF16 and UTF8.
|
||||
* These convertion functions are suit for all platforms, not Windows oriented.
|
||||
* \par
|
||||
* Due to implementation, this library assume all non-Windows system use UTF8 as their C locale.
|
||||
* Otherwise these functions will produce wrong result.
|
||||
*
|
||||
* \par Function Parameters
|
||||
* We provide these encoding convertion functions with following 2 types:
|
||||
* \li Function returns \c bool and its parameter order source string pointer and a corresponding \c std::basic_string container for receiving result.
|
||||
* \li Function returns corresponding \c std::basic_string result, and its parameter only order source string pointer.
|
||||
* \par
|
||||
* For these 2 declarations, both of them will not throw any exception and do not accept nullptr as source string.
|
||||
* The only difference is that the way to indicate convertion error.
|
||||
* \par
|
||||
* First declaration will return false to indicate there is an error when doing convertion. Please note that the content of string container passing in may still be changed!
|
||||
* Last declaration will return empty string to indicate error. Please note if you pass empty string in, they still will output empty string but it doesn't mean an error.
|
||||
* So last declaration is used in the scenario that we don't care whether the convertion success did. For example, output something to console.
|
||||
*
|
||||
*/
|
||||
namespace YYCC::EncodingHelper {
|
||||
|
||||
#define _YYCC_U8(strl) u8 ## strl
|
||||
#define YYCC_U8(strl) (reinterpret_cast<const ::YYCC::yycc_char8_t*>(_YYCC_U8(strl)))
|
||||
|
||||
const yycc_char8_t* ToUTF8(const char* src);
|
||||
yycc_char8_t* ToUTF8(char* src);
|
||||
yycc_u8string ToUTF8(const std::string_view& src);
|
||||
yycc_u8string_view ToUTF8View(const std::string_view& src);
|
||||
|
||||
const char* ToNative(const yycc_char8_t* src);
|
||||
char* ToNative(yycc_char8_t* src);
|
||||
std::string ToNative(const yycc_u8string_view& src);
|
||||
std::string_view ToNativeView(const yycc_u8string_view& src);
|
||||
|
||||
#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);
|
||||
|
||||
#endif
|
||||
|
||||
bool UTF8ToUTF16(const yycc_u8string_view& src, std::u16string& dst);
|
||||
bool UTF8ToUTF16(const yycc_char8_t* src, std::u16string& dst);
|
||||
std::u16string UTF8ToUTF16(const yycc_u8string_view& src);
|
||||
std::u16string UTF8ToUTF16(const yycc_char8_t* src);
|
||||
|
||||
bool UTF16ToUTF8(const std::u16string_view& src, yycc_u8string& dst);
|
||||
bool UTF16ToUTF8(const char16_t* src, yycc_u8string& dst);
|
||||
yycc_u8string UTF16ToUTF8(const std::u16string_view& src);
|
||||
yycc_u8string UTF16ToUTF8(const char16_t* src);
|
||||
|
||||
|
||||
bool UTF8ToUTF32(const yycc_u8string_view& src, std::u32string& dst);
|
||||
bool UTF8ToUTF32(const yycc_char8_t* src, std::u32string& dst);
|
||||
std::u32string UTF8ToUTF32(const yycc_u8string_view& src);
|
||||
std::u32string UTF8ToUTF32(const yycc_char8_t* src);
|
||||
|
||||
bool UTF32ToUTF8(const std::u32string_view& src, yycc_u8string& dst);
|
||||
bool UTF32ToUTF8(const char32_t* src, yycc_u8string& dst);
|
||||
yycc_u8string UTF32ToUTF8(const std::u32string_view& src);
|
||||
yycc_u8string UTF32ToUTF8(const char32_t* src);
|
||||
|
||||
}
|
||||
@@ -1,428 +0,0 @@
|
||||
#include "ExceptionHelper.hpp"
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
|
||||
#include "WinFctHelper.hpp"
|
||||
#include "ConsoleHelper.hpp"
|
||||
#include "StringHelper.hpp"
|
||||
#include "IOHelper.hpp"
|
||||
#include "EncodingHelper.hpp"
|
||||
#include "FsPathPatch.hpp"
|
||||
#include <filesystem>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cinttypes>
|
||||
|
||||
#include "WinImportPrefix.hpp"
|
||||
#include <Windows.h>
|
||||
#include <DbgHelp.h>
|
||||
#include "WinImportSuffix.hpp"
|
||||
|
||||
namespace YYCC::ExceptionHelper {
|
||||
|
||||
/**
|
||||
* @brief True if the exception handler already registered, otherwise false.
|
||||
* @details
|
||||
* This variable is designed to prevent multiple register operation
|
||||
* because unhandled exception handler should only be registered once.
|
||||
* \n
|
||||
* Register function should check whether this variable is false before registering,
|
||||
* and set this variable to true after registing.
|
||||
* Unregister as well as should do the same check.
|
||||
*/
|
||||
static bool g_IsRegistered = false;
|
||||
/**
|
||||
* @brief True if a exception handler is running, otherwise false.
|
||||
* @details
|
||||
* This variable is served for blocking possible infinity recursive exception handling.
|
||||
* \n
|
||||
* When entering unhandled exception handler, we must check whether this variable is true.
|
||||
* If it is true, it mean that there is another unhandled exception handler running.
|
||||
* Then we should exit immediately.
|
||||
* Otherwise, this variable should be set to true indicating we are processing unhandled exception.
|
||||
* After processing exception, at the end of unhandled exception handler,
|
||||
* we should restore this value to false.
|
||||
*
|
||||
*/
|
||||
static bool g_IsProcessing = false;
|
||||
/**
|
||||
* @brief The backup of original exception handler.
|
||||
* @details
|
||||
* This variable was set when registering unhandled exception handler.
|
||||
* And will be used when unregistering for restoring.
|
||||
*/
|
||||
static LPTOP_LEVEL_EXCEPTION_FILTER g_ProcBackup;
|
||||
|
||||
#pragma region Exception Handler Implementation
|
||||
|
||||
/**
|
||||
* @brief Get human-readable exception string from given exception code.
|
||||
* @param[in] code Exception code
|
||||
* @return The const string pointer to corresponding exception explanation string.
|
||||
*/
|
||||
static const yycc_char8_t* UExceptionGetCodeName(DWORD code) {
|
||||
switch (code) {
|
||||
case EXCEPTION_ACCESS_VIOLATION:
|
||||
return YYCC_U8("access violation");
|
||||
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
|
||||
return YYCC_U8("array index out of bound");
|
||||
case EXCEPTION_BREAKPOINT:
|
||||
return YYCC_U8("breakpoint reached");
|
||||
case EXCEPTION_DATATYPE_MISALIGNMENT:
|
||||
return YYCC_U8("misaligned data access");
|
||||
case EXCEPTION_FLT_DENORMAL_OPERAND:
|
||||
return YYCC_U8("operand had denormal value");
|
||||
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
|
||||
return YYCC_U8("floating-point division by zero");
|
||||
case EXCEPTION_FLT_INEXACT_RESULT:
|
||||
return YYCC_U8("no decimal fraction representation for value");
|
||||
case EXCEPTION_FLT_INVALID_OPERATION:
|
||||
return YYCC_U8("invalid floating-point operation");
|
||||
case EXCEPTION_FLT_OVERFLOW:
|
||||
return YYCC_U8("floating-point overflow");
|
||||
case EXCEPTION_FLT_STACK_CHECK:
|
||||
return YYCC_U8("floating-point stack corruption");
|
||||
case EXCEPTION_FLT_UNDERFLOW:
|
||||
return YYCC_U8("floating-point underflow");
|
||||
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
||||
return YYCC_U8("illegal instruction");
|
||||
case EXCEPTION_IN_PAGE_ERROR:
|
||||
return YYCC_U8("inaccessible page");
|
||||
case EXCEPTION_INT_DIVIDE_BY_ZERO:
|
||||
return YYCC_U8("integer division by zero");
|
||||
case EXCEPTION_INT_OVERFLOW:
|
||||
return YYCC_U8("integer overflow");
|
||||
case EXCEPTION_INVALID_DISPOSITION:
|
||||
return YYCC_U8("documentation says this should never happen");
|
||||
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
|
||||
return YYCC_U8("can't continue after a noncontinuable exception");
|
||||
case EXCEPTION_PRIV_INSTRUCTION:
|
||||
return YYCC_U8("attempted to execute a privileged instruction");
|
||||
case EXCEPTION_SINGLE_STEP:
|
||||
return YYCC_U8("one instruction has been executed");
|
||||
case EXCEPTION_STACK_OVERFLOW:
|
||||
return YYCC_U8("stack overflow");
|
||||
default:
|
||||
return YYCC_U8("unknown exception");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Error log (including backtrace) used output function with format feature
|
||||
* @details
|
||||
* This function will format message first.
|
||||
* And write them into given file stream and stderr.
|
||||
* @param[in] fs
|
||||
* The file stream where we write.
|
||||
* If it is nullptr, function will skip writing for file stream.
|
||||
* @param[in] fmt The format string.
|
||||
* @param[in] ... The argument to be formatted.
|
||||
*/
|
||||
static void UExceptionErrLogFormatLine(std::FILE* fs, const yycc_char8_t* fmt, ...) {
|
||||
// write to file
|
||||
if (fs != nullptr) {
|
||||
va_list arg1;
|
||||
va_start(arg1, fmt);
|
||||
std::vfprintf(fs, EncodingHelper::ToNative(fmt), arg1);
|
||||
std::fputs("\n", fs);
|
||||
va_end(arg1);
|
||||
}
|
||||
// write to stderr
|
||||
va_list arg2;
|
||||
va_start(arg2, fmt);
|
||||
ConsoleHelper::ErrWriteLine(YYCC::StringHelper::VPrintf(fmt, arg2).c_str());
|
||||
va_end(arg2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Error log (including backtrace) used output function
|
||||
* @details
|
||||
* This function will write given string into given file stream and stderr.
|
||||
* @param[in] fs
|
||||
* The file stream where we write.
|
||||
* If it is nullptr, function will skip writing for file stream.
|
||||
* @param[in] strl The string to be written.
|
||||
*/
|
||||
static void UExceptionErrLogWriteLine(std::FILE* fs, const yycc_char8_t* strl) {
|
||||
// write to file
|
||||
if (fs != nullptr) {
|
||||
std::fputs(EncodingHelper::ToNative(strl), fs);
|
||||
std::fputs("\n", fs);
|
||||
}
|
||||
// write to stderr
|
||||
ConsoleHelper::ErrWriteLine(strl);
|
||||
}
|
||||
|
||||
static void UExceptionBacktrace(FILE* fs, LPCONTEXT context, int maxdepth) {
|
||||
// setup loading symbol options
|
||||
SymSetOptions(SymGetOptions() | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES); // lazy load symbol, and load line number.
|
||||
|
||||
// setup handle
|
||||
HANDLE process = GetCurrentProcess();
|
||||
HANDLE thread = GetCurrentThread();
|
||||
|
||||
// init symbol
|
||||
if (!SymInitialize(process, 0, TRUE)) {
|
||||
// fail to init. return
|
||||
UExceptionErrLogWriteLine(fs, YYCC_U8("Fail to initialize symbol handle for process!"));
|
||||
return;
|
||||
}
|
||||
|
||||
// ========== CORE DUMP ==========
|
||||
// prepare frame. setup correct fields
|
||||
// references:
|
||||
// https://github.com/rust-lang/backtrace-rs/blob/9ed25b581cfd2ee60e5a3b9054fd023bf6dced90/src/backtrace/dbghelp.rs
|
||||
// https://sourceforge.net/p/predef/wiki/Architectures/
|
||||
DWORD machine_type = 0;
|
||||
STACKFRAME64 frame;
|
||||
memset(&frame, 0, sizeof(frame));
|
||||
#if defined(_M_IX86) || defined(__i386__)
|
||||
// x86
|
||||
machine_type = IMAGE_FILE_MACHINE_I386;
|
||||
frame.AddrPC.Offset = context->Eip;
|
||||
frame.AddrStack.Offset = context->Esp;
|
||||
frame.AddrFrame.Offset = context->Ebp;
|
||||
#elif defined(_M_AMD64) || defined(__amd64__)
|
||||
// amd64
|
||||
machine_type = IMAGE_FILE_MACHINE_AMD64;
|
||||
frame.AddrPC.Offset = context->Rip;
|
||||
frame.AddrStack.Offset = context->Rsp;
|
||||
frame.AddrFrame.Offset = context->Rbp;
|
||||
#elif defined(_M_ARM) || defined(__arm__)
|
||||
// arm (32bit)
|
||||
machine_type = IMAGE_FILE_MACHINE_ARMNT;
|
||||
frame.AddrPC.Offset = context->Pc;
|
||||
frame.AddrStack.Offset = context->Sp;
|
||||
frame.AddrFrame.Offset = context->R11;
|
||||
#elif defined(_M_ARM64) || defined(__aarch64__)
|
||||
// arm64
|
||||
machine_type = IMAGE_FILE_MACHINE_ARM64;
|
||||
frame.AddrPC.Offset = context->Pc;
|
||||
frame.AddrStack.Offset = context->Sp;
|
||||
frame.AddrFrame.Offset = context->DUMMYUNIONNAME.DUMMYSTRUCTNAME.Fp;
|
||||
#else
|
||||
#error "Unsupported platform"
|
||||
//IA-64 anybody?
|
||||
|
||||
#endif
|
||||
frame.AddrPC.Mode = AddrModeFlat;
|
||||
frame.AddrStack.Mode = AddrModeFlat;
|
||||
frame.AddrFrame.Mode = AddrModeFlat;
|
||||
|
||||
// stack walker
|
||||
while (StackWalk64(machine_type, process, thread, &frame, context,
|
||||
0, SymFunctionTableAccess64, SymGetModuleBase64, 0)) {
|
||||
|
||||
// depth breaker
|
||||
--maxdepth;
|
||||
if (maxdepth < 0) {
|
||||
UExceptionErrLogWriteLine(fs, YYCC_U8("...")); // indicate there are some frames not listed
|
||||
break;
|
||||
}
|
||||
|
||||
// get module name
|
||||
const yycc_char8_t* module_name = YYCC_U8("<unknown module>");
|
||||
yycc_u8string module_name_raw;
|
||||
DWORD64 module_base;
|
||||
if (module_base = SymGetModuleBase64(process, frame.AddrPC.Offset)) {
|
||||
if (WinFctHelper::GetModuleFileName((HINSTANCE)module_base, module_name_raw)) {
|
||||
module_name = module_name_raw.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
// get source file and line
|
||||
const yycc_char8_t* source_file = YYCC_U8("<unknown source>");
|
||||
DWORD64 source_file_line = 0;
|
||||
DWORD dwDisplacement;
|
||||
IMAGEHLP_LINE64 winline;
|
||||
winline.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
||||
if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &dwDisplacement, &winline)) {
|
||||
source_file = EncodingHelper::ToUTF8(winline.FileName); // TODO: check whether there is UNICODE file name.
|
||||
source_file_line = winline.LineNumber;
|
||||
}
|
||||
|
||||
// write to file
|
||||
UExceptionErrLogFormatLine(fs, YYCC_U8("0x%" PRI_XPTR_LEFT_PADDING PRIXPTR "[%s+0x%" PRI_XPTR_LEFT_PADDING PRIXPTR "]\t%s#L%" PRIu64),
|
||||
frame.AddrPC.Offset, // memory adress
|
||||
module_name, frame.AddrPC.Offset - module_base, // module name + relative address
|
||||
source_file, source_file_line // source file + source line
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
// ========== END CORE DUMP ==========
|
||||
|
||||
// free symbol
|
||||
SymCleanup(process);
|
||||
}
|
||||
|
||||
static void UExceptionErrorLog(const yycc_u8string& u8_filename, LPEXCEPTION_POINTERS info) {
|
||||
// open file stream if we have file name
|
||||
std::FILE* fs = nullptr;
|
||||
if (!u8_filename.empty()) {
|
||||
fs = IOHelper::UTF8FOpen(u8_filename.c_str(), YYCC_U8("wb"));
|
||||
}
|
||||
|
||||
// record exception type first
|
||||
PEXCEPTION_RECORD rec = info->ExceptionRecord;
|
||||
UExceptionErrLogFormatLine(fs, YYCC_U8("Unhandled exception occured at 0x%" PRI_XPTR_LEFT_PADDING PRIXPTR ": %s (%" PRIu32 ")."),
|
||||
rec->ExceptionAddress,
|
||||
UExceptionGetCodeName(rec->ExceptionCode),
|
||||
rec->ExceptionCode
|
||||
);
|
||||
|
||||
// special proc for 2 exceptions
|
||||
if (rec->ExceptionCode == EXCEPTION_ACCESS_VIOLATION || rec->ExceptionCode == EXCEPTION_IN_PAGE_ERROR) {
|
||||
if (rec->NumberParameters >= 2) {
|
||||
const yycc_char8_t* op =
|
||||
rec->ExceptionInformation[0] == 0 ? YYCC_U8("read") :
|
||||
rec->ExceptionInformation[0] == 1 ? YYCC_U8("written") : YYCC_U8("executed");
|
||||
UExceptionErrLogFormatLine(fs, YYCC_U8("The data at memory address 0x%" PRI_XPTR_LEFT_PADDING PRIxPTR " could not be %s."),
|
||||
rec->ExceptionInformation[1], op);
|
||||
}
|
||||
}
|
||||
|
||||
// output stacktrace
|
||||
UExceptionBacktrace(fs, info->ContextRecord, 1024);
|
||||
|
||||
// close file if necessary
|
||||
if (fs != nullptr) {
|
||||
std::fclose(fs);
|
||||
}
|
||||
}
|
||||
|
||||
static void UExceptionCoreDump(const yycc_u8string& u8_filename, LPEXCEPTION_POINTERS info) {
|
||||
// convert file encoding
|
||||
std::wstring filename;
|
||||
if (u8_filename.empty())
|
||||
return; // if no given file name, return
|
||||
if (!YYCC::EncodingHelper::UTF8ToWchar(u8_filename, filename))
|
||||
return; // if convertion failed, return
|
||||
|
||||
// open file and write
|
||||
HANDLE hFile = CreateFileW(filename.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hFile != INVALID_HANDLE_VALUE) {
|
||||
MINIDUMP_EXCEPTION_INFORMATION exception_info;
|
||||
exception_info.ThreadId = GetCurrentThreadId();
|
||||
exception_info.ExceptionPointers = info;
|
||||
exception_info.ClientPointers = TRUE;
|
||||
MiniDumpWriteDump(
|
||||
GetCurrentProcess(), GetCurrentProcessId(), hFile,
|
||||
MiniDumpNormal,
|
||||
&exception_info,
|
||||
NULL, NULL
|
||||
);
|
||||
CloseHandle(hFile);
|
||||
}
|
||||
}
|
||||
|
||||
static bool UExceptionFetchRecordPath(yycc_u8string& log_path, yycc_u8string& coredump_path) {
|
||||
// build two file names like: "module.dll.1234.log" and "module.dll.1234.dmp".
|
||||
// "module.dll" is the name of current module. "1234" is current process id.
|
||||
// get self module name
|
||||
yycc_u8string u8_self_module_name;
|
||||
{
|
||||
// get module handle
|
||||
HMODULE hSelfModule = YYCC::WinFctHelper::GetCurrentModule();
|
||||
if (hSelfModule == nullptr)
|
||||
return false;
|
||||
// get full path of self module
|
||||
yycc_u8string u8_self_module_path;
|
||||
if (!YYCC::WinFctHelper::GetModuleFileName(hSelfModule, u8_self_module_path))
|
||||
return false;
|
||||
// extract file name from full path by std::filesystem::path
|
||||
std::filesystem::path self_module_path(FsPathPatch::FromUTF8Path(u8_self_module_path.c_str()));
|
||||
u8_self_module_name = FsPathPatch::ToUTF8Path(self_module_path.filename());
|
||||
}
|
||||
// then get process id
|
||||
DWORD process_id = GetCurrentProcessId();
|
||||
// conbine them as a file name prefix
|
||||
yycc_u8string u8_filename_prefix;
|
||||
if (!YYCC::StringHelper::Printf(u8_filename_prefix, YYCC_U8("%s.%" PRIu32), u8_self_module_name.c_str(), process_id))
|
||||
return false;
|
||||
// then get file name for log and minidump
|
||||
yycc_u8string u8_log_filename = u8_filename_prefix + YYCC_U8(".log");
|
||||
yycc_u8string u8_coredump_filename = u8_filename_prefix + YYCC_U8(".dmp");
|
||||
|
||||
// fetch crash report path
|
||||
// get local appdata folder
|
||||
yycc_u8string u8_localappdata_path;
|
||||
if (!WinFctHelper::GetLocalAppData(u8_localappdata_path))
|
||||
return false;
|
||||
// convert to std::filesystem::path
|
||||
std::filesystem::path crash_report_path(FsPathPatch::FromUTF8Path(u8_localappdata_path.c_str()));
|
||||
// slash into crash report folder
|
||||
crash_report_path /= FsPathPatch::FromUTF8Path(YYCC_U8("CrashDumps"));
|
||||
// use create function to make sure it is existing
|
||||
std::filesystem::create_directories(crash_report_path);
|
||||
|
||||
// build log path and coredump path
|
||||
// build std::filesystem::path first
|
||||
std::filesystem::path log_filepath = crash_report_path / FsPathPatch::FromUTF8Path(u8_log_filename.c_str());
|
||||
std::filesystem::path coredump_filepath = crash_report_path / FsPathPatch::FromUTF8Path(u8_coredump_filename.c_str());
|
||||
// output to result
|
||||
log_path = FsPathPatch::ToUTF8Path(log_filepath);
|
||||
coredump_path = FsPathPatch::ToUTF8Path(coredump_filepath);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static LONG WINAPI UExceptionImpl(LPEXCEPTION_POINTERS info) {
|
||||
// detect loop calling
|
||||
if (g_IsProcessing) goto end_proc;
|
||||
// start process
|
||||
g_IsProcessing = true;
|
||||
|
||||
// core implementation
|
||||
{
|
||||
// fetch error report path first
|
||||
yycc_u8string log_path, coredump_path;
|
||||
if (!UExceptionFetchRecordPath(log_path, coredump_path)) {
|
||||
// fail to fetch path, clear them.
|
||||
// we still can handle crash without them
|
||||
log_path.clear();
|
||||
coredump_path.clear();
|
||||
// and tell user we can not output file
|
||||
ConsoleHelper::ErrWriteLine(YYCC_U8("Crash occurs, but we can not create crash log and coredump!"));
|
||||
} else {
|
||||
// okey. output file path to tell user the path where you can find.
|
||||
ConsoleHelper::ErrFormatLine(YYCC_U8("Crash Log: %s"), log_path.c_str());
|
||||
ConsoleHelper::ErrFormatLine(YYCC_U8("Crash Coredump: %s"), coredump_path.c_str());
|
||||
}
|
||||
|
||||
// write crash log
|
||||
UExceptionErrorLog(log_path, info);
|
||||
// write crash coredump
|
||||
UExceptionCoreDump(coredump_path, info);
|
||||
|
||||
}
|
||||
|
||||
// end process
|
||||
failed:
|
||||
g_IsProcessing = false;
|
||||
// if backup proc can be run, run it
|
||||
// otherwise directly return.
|
||||
end_proc:
|
||||
if (g_ProcBackup != nullptr) {
|
||||
return g_ProcBackup(info);
|
||||
} else {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
void Register() {
|
||||
if (g_IsRegistered) return;
|
||||
g_ProcBackup = SetUnhandledExceptionFilter(UExceptionImpl);
|
||||
g_IsRegistered = true;
|
||||
}
|
||||
|
||||
void Unregister() {
|
||||
if (!g_IsRegistered) return;
|
||||
SetUnhandledExceptionFilter(g_ProcBackup);
|
||||
g_IsRegistered = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,44 +0,0 @@
|
||||
#pragma once
|
||||
#include "YYCCInternal.hpp"
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
|
||||
/**
|
||||
* @brief Windows specific unhandled exception processor.
|
||||
* @details
|
||||
* This namespace is Windows specific. On other platforms, the whole namespace is unavailable.
|
||||
*
|
||||
* This namespace allow user register unhandled exception handler on Windows
|
||||
* to output error log into \c stderr and log file, and generate coredump if possible.
|
||||
* This is useful for bug tracing on Windows, especially most Windows user are naive and don't know how to report bug.
|
||||
*
|
||||
*/
|
||||
namespace YYCC::ExceptionHelper {
|
||||
|
||||
/**
|
||||
* @brief Register unhandled exception handler
|
||||
* @details
|
||||
* This function will set an internal function as unhandled exception handler on Windows.
|
||||
*
|
||||
* When unhandled exception raised,
|
||||
* That internal function will output error stacktrace in standard output
|
||||
* and log file (located in temp folder), and also generate a dump file
|
||||
* in temp folder (for convenient debugging of developer when reporting bugs) if it can.
|
||||
*
|
||||
* This function usually is called at the start of program.
|
||||
*/
|
||||
void Register();
|
||||
/**
|
||||
* @brief Unregister unhandled exception handler
|
||||
* @details
|
||||
* The reverse operation of Register().
|
||||
*
|
||||
* This function and Register() should always be used as a pair.
|
||||
* You must call this function if you have called Register() before.
|
||||
*
|
||||
* This function usually is called at the end of program.
|
||||
*/
|
||||
void Unregister();
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,41 +0,0 @@
|
||||
#include "FsPathPatch.hpp"
|
||||
|
||||
#include "EncodingHelper.hpp"
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace YYCC::FsPathPatch {
|
||||
|
||||
std::filesystem::path FromUTF8Path(const yycc_char8_t* 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
|
||||
return std::filesystem::path(EncodingHelper::ToNative(u8_path));
|
||||
#endif
|
||||
}
|
||||
|
||||
yycc_u8string ToUTF8Path(const std::filesystem::path& path) {
|
||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
||||
|
||||
// get and convert to utf8
|
||||
yycc_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
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user