Compare commits
150 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 |
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
|
||||||
19
.github/linux_build.sh
vendored
Normal file
19
.github/linux_build.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
|
||||||
|
cd build
|
||||||
|
|
||||||
|
# Build in Release mode
|
||||||
|
cmake -DCMAKE_BUILD_TYPE=Release ../..
|
||||||
|
cmake --build .
|
||||||
|
cmake --install . --prefix=../install
|
||||||
|
|
||||||
|
# Back to root directory
|
||||||
|
cd ..
|
||||||
|
cd ..
|
||||||
19
.github/macos_build.sh
vendored
Normal file
19
.github/macos_build.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
|
||||||
|
cd build
|
||||||
|
|
||||||
|
# Build in Release mode
|
||||||
|
cmake -DCMAKE_BUILD_TYPE=Release ../..
|
||||||
|
cmake --build .
|
||||||
|
cmake --install . --prefix=../install
|
||||||
|
|
||||||
|
# Back to root directory
|
||||||
|
cd ..
|
||||||
|
cd ..
|
||||||
18
.github/windows_build.bat
vendored
Normal file
18
.github/windows_build.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
|
||||||
|
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 ..
|
||||||
47
.github/workflows/linux.yml
vendored
Normal file
47
.github/workflows/linux.yml
vendored
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
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
|
||||||
|
run: |
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y build-essential cmake git
|
||||||
|
# - name: Setup Google Test and Google Benchmark
|
||||||
|
# run: |
|
||||||
|
# # Setup Google Test
|
||||||
|
# git clone -b v1.17.0 https://github.com/google/googletest.git external/googletest
|
||||||
|
# cd external/googletest
|
||||||
|
# mkdir build install
|
||||||
|
# cd build
|
||||||
|
# cmake -DCMAKE_CXX_STANDARD=23 -Dgtest_force_shared_crt=ON -DCMAKE_BUILD_TYPE=Release ..
|
||||||
|
# cmake --build .
|
||||||
|
# cmake --install . --prefix=../install
|
||||||
|
# cd ../..
|
||||||
|
# # Setup Google Benchmark
|
||||||
|
# git clone -b v1.9.4 https://github.com/google/benchmark.git external/benchmark
|
||||||
|
# cd external/benchmark
|
||||||
|
# # Create symlink to googletest as required by benchmark
|
||||||
|
# ln -s ../googletest googletest
|
||||||
|
# mkdir build install
|
||||||
|
# cd build
|
||||||
|
# cmake -DCMAKE_CXX_STANDARD=23 -DBENCHMARK_ENABLE_TESTING=OFF -DCMAKE_BUILD_TYPE=Release ..
|
||||||
|
# cmake --build .
|
||||||
|
# cmake --install . --prefix=../install
|
||||||
|
# cd ../..
|
||||||
|
- name: Build YYCC
|
||||||
|
run: |
|
||||||
|
chmod +x ./.github/linux_build.sh
|
||||||
|
./.github/linux_build.sh
|
||||||
|
- name: Upload Built Artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: YYCC-linux-build
|
||||||
|
path: bin/install/*
|
||||||
|
retention-days: 30
|
||||||
43
.github/workflows/macos.yml
vendored
Normal file
43
.github/workflows/macos.yml
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
name: YYCC macOS Build
|
||||||
|
|
||||||
|
on: [workflow_dispatch]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
macos-build:
|
||||||
|
runs-on: macos-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout Repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
# - name: Setup Google Test and Google Benchmark
|
||||||
|
# run: |
|
||||||
|
# # Setup Google Test
|
||||||
|
# git clone -b v1.17.0 https://github.com/google/googletest.git external/googletest
|
||||||
|
# cd external/googletest
|
||||||
|
# mkdir build install
|
||||||
|
# cd build
|
||||||
|
# cmake -DCMAKE_CXX_STANDARD=23 -Dgtest_force_shared_crt=ON -DCMAKE_BUILD_TYPE=Release ..
|
||||||
|
# cmake --build .
|
||||||
|
# cmake --install . --prefix=../install
|
||||||
|
# cd ../..
|
||||||
|
# # Setup Google Benchmark
|
||||||
|
# git clone -b v1.9.4 https://github.com/google/benchmark.git external/benchmark
|
||||||
|
# cd external/benchmark
|
||||||
|
# # Create symlink to googletest as required by benchmark
|
||||||
|
# ln -s ../googletest googletest
|
||||||
|
# mkdir build install
|
||||||
|
# cd build
|
||||||
|
# cmake -DCMAKE_CXX_STANDARD=23 -DBENCHMARK_ENABLE_TESTING=OFF -DCMAKE_BUILD_TYPE=Release ..
|
||||||
|
# cmake --build .
|
||||||
|
# cmake --install . --prefix=../install
|
||||||
|
# cd ../..
|
||||||
|
- name: Build YYCC
|
||||||
|
run: |
|
||||||
|
chmod +x ./.github/macos_build.sh
|
||||||
|
./.github/macos_build.sh
|
||||||
|
- 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
|
|
||||||
55
.github/workflows/windows.yml
vendored
Normal file
55
.github/workflows/windows.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
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: Setup Google Test and Google Benchmark
|
||||||
|
# run: |
|
||||||
|
# # Setup Google Test
|
||||||
|
# git clone -b v1.17.0 https://github.com/google/googletest.git external/googletest
|
||||||
|
# cd external/googletest
|
||||||
|
# mkdir build install
|
||||||
|
# cd build
|
||||||
|
# cmake -DCMAKE_CXX_STANDARD=23 -Dgtest_force_shared_crt=ON -DCMAKE_BUILD_TYPE=Release ..
|
||||||
|
# cmake --build . --config Release
|
||||||
|
# cmake --install . --prefix=../install --config Release
|
||||||
|
# cd ../..
|
||||||
|
# # Setup Google Benchmark
|
||||||
|
# git clone -b v1.9.4 https://github.com/google/benchmark.git external/benchmark
|
||||||
|
# cd external/benchmark
|
||||||
|
# # Create symlink to googletest as required by benchmark
|
||||||
|
# mklink /D googletest ../googletest
|
||||||
|
# mkdir build install
|
||||||
|
# cd build
|
||||||
|
# cmake -DCMAKE_CXX_STANDARD=23 -DBENCHMARK_ENABLE_TESTING=OFF -DCMAKE_BUILD_TYPE=Release ..
|
||||||
|
# cmake --build . --config Release
|
||||||
|
# cmake --install . --prefix=../install --config Release
|
||||||
|
# cd ../..
|
||||||
|
- name: Build 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 }}
|
||||||
|
.\.github\windows_build.bat
|
||||||
|
- name: Upload Built Artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: YYCC-windows-build
|
||||||
|
path: bin/install/*
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
14
.gitignore
vendored
14
.gitignore
vendored
@@ -1,11 +1,17 @@
|
|||||||
# -------------------- Output --------------------
|
## ===== Personal =====
|
||||||
|
# Ignore build resources
|
||||||
out/
|
out/
|
||||||
|
build/
|
||||||
|
install/
|
||||||
|
|
||||||
|
# Ignore CMake generated stuff
|
||||||
|
src/yycc/version.hpp
|
||||||
CMakeSettings.json
|
CMakeSettings.json
|
||||||
|
|
||||||
# -------------------- VSCode --------------------
|
## ===== VSCode =====
|
||||||
.vscode/
|
.vscode/
|
||||||
|
|
||||||
# -------------------- CMake --------------------
|
## ===== CMake =====
|
||||||
CMakeLists.txt.user
|
CMakeLists.txt.user
|
||||||
CMakeCache.txt
|
CMakeCache.txt
|
||||||
CMakeFiles
|
CMakeFiles
|
||||||
@@ -18,7 +24,7 @@ compile_commands.json
|
|||||||
CTestTestfile.cmake
|
CTestTestfile.cmake
|
||||||
_deps
|
_deps
|
||||||
|
|
||||||
# -------------------- Visual Studio --------------------
|
## ===== Visual Studio =====
|
||||||
## Ignore Visual Studio temporary files, build results, and
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
## files generated by popular Visual Studio add-ons.
|
## files generated by popular Visual Studio add-ons.
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -1,13 +1,19 @@
|
|||||||
cmake_minimum_required(VERSION 3.23)
|
cmake_minimum_required(VERSION 3.23)
|
||||||
project(YYCC
|
project(YYCC
|
||||||
VERSION 1.2.0
|
VERSION 2.0.0
|
||||||
LANGUAGES CXX
|
LANGUAGES CXX
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Setup C++ standard
|
||||||
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
# Provide options
|
# 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_BUILD_DOC "Build document of YYCCommonplace." OFF)
|
||||||
option(YYCC_DEBUG_UE_FILTER "YYCC developer used switch for testing Windows unhandled exception filter. Should not set to ON!!!" OFF)
|
option(YYCC_ENFORCE_ICONV "Enforce iconv support for this library (e.g. in MSYS2 environment)." OFF)
|
||||||
|
|
||||||
# Setup install path from CMake provided install path for convenient use.
|
# Setup install path from CMake provided install path for convenient use.
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
@@ -20,10 +26,29 @@ set(YYCC_INSTALL_BIN_PATH ${CMAKE_INSTALL_BINDIR} CACHE PATH
|
|||||||
set(YYCC_INSTALL_DOC_PATH ${CMAKE_INSTALL_DOCDIR} CACHE PATH
|
set(YYCC_INSTALL_DOC_PATH ${CMAKE_INSTALL_DOCDIR} CACHE PATH
|
||||||
"Non-arch doc install path relative to CMAKE_INSTALL_PREFIX unless set to an absolute path.")
|
"Non-arch doc install path relative to CMAKE_INSTALL_PREFIX unless set to an absolute path.")
|
||||||
|
|
||||||
# Import 3 build targets
|
# 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 ()
|
||||||
|
# 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)
|
add_subdirectory(src)
|
||||||
if (YYCC_BUILD_TESTBENCH)
|
if (YYCC_BUILD_TEST)
|
||||||
add_subdirectory(testbench)
|
add_subdirectory(test)
|
||||||
|
endif ()
|
||||||
|
if (YYCC_BUILD_BENCHMARK)
|
||||||
|
add_subdirectory(benchmark)
|
||||||
endif ()
|
endif ()
|
||||||
if (YYCC_BUILD_DOC)
|
if (YYCC_BUILD_DOC)
|
||||||
add_subdirectory(doc)
|
add_subdirectory(doc)
|
||||||
@@ -46,7 +71,7 @@ write_basic_package_version_file(
|
|||||||
configure_package_config_file(
|
configure_package_config_file(
|
||||||
${CMAKE_CURRENT_LIST_DIR}/cmake/YYCCommonplaceConfig.cmake.in
|
${CMAKE_CURRENT_LIST_DIR}/cmake/YYCCommonplaceConfig.cmake.in
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/YYCCommonplaceConfig.cmake"
|
"${CMAKE_CURRENT_BINARY_DIR}/YYCCommonplaceConfig.cmake"
|
||||||
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/YYCCommonplace
|
INSTALL_DESTINATION ${YYCC_INSTALL_LIB_PATH}/cmake/YYCCommonplace
|
||||||
)
|
)
|
||||||
# Copy package files to install destination
|
# Copy package files to install destination
|
||||||
install(
|
install(
|
||||||
@@ -54,6 +79,6 @@ FILES
|
|||||||
"${CMAKE_CURRENT_BINARY_DIR}/YYCCommonplaceConfig.cmake"
|
"${CMAKE_CURRENT_BINARY_DIR}/YYCCommonplaceConfig.cmake"
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/YYCCommonplaceConfigVersion.cmake"
|
"${CMAKE_CURRENT_BINARY_DIR}/YYCCommonplaceConfigVersion.cmake"
|
||||||
DESTINATION
|
DESTINATION
|
||||||
${CMAKE_INSTALL_LIBDIR}/cmake/YYCCommonplace
|
${YYCC_INSTALL_LIB_PATH}/cmake/YYCCommonplace
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
173
COMPILE.md
Normal file
173
COMPILE.md
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
# 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 Clang. That's not your fault.
|
||||||
|
> Clang used libc++ library lacks some essential features used by this project.
|
||||||
|
> A possible solution is that use GCC and libstdc++ on macOS instead of default Clang and libc++.
|
||||||
|
> Build issue may be resolved until libc++ finish these features: `std::stacktrace` and `std::views::enumerate`.
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
|
Execute `.github/windows_build.bat` on Windows or `.github/linux_build.sh` on POSIX-like OS (Linux and macOS) under **the root directory** of this project. The final built artifact is under `bin/install` directory.
|
||||||
|
|
||||||
|
### 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
|
||||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2024-2024 yyc12345
|
Copyright (c) 2024-2026 yyc12345
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
11
README.md
11
README.md
@@ -4,14 +4,9 @@ YYC Commonplace, or YYCCommonplace (abbr. YYCC) is a static library specifically
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
For more usage about this library, please build documentation of this project via Doxygen and read it.
|
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.
|
||||||
And I also highly recommend that you read documentation first before writing with this library.
|
|
||||||
|
|
||||||
However, the documentation need CMake to build and you may don't know how to use CMake in this project. So as the alternative, you also can browse the raw Doxygen documentation file: `doc/src/intro.dox` for how to build this project (including documentation) first.
|
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
This project require at least CMake 3.23 to build. We suggest that you only use stable version (tagged commit). The latest commit may still work in progress and not stable.
|
See [Compile Manual](./COMPILE.md).
|
||||||
|
|
||||||
See documentation for how to build this project.
|
|
||||||
|
|||||||
28
benchmark/CMakeLists.txt
Normal file
28
benchmark/CMakeLists.txt
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# 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
|
||||||
|
)
|
||||||
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");
|
||||||
|
|
||||||
|
}
|
||||||
@@ -14,5 +14,6 @@ add_custom_target (YYCCDocumentation
|
|||||||
|
|
||||||
# Install built documentation
|
# Install built documentation
|
||||||
install (DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html
|
install (DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html
|
||||||
|
CONFIGURATIONS Release RelWithDebInfo MinSizeRel
|
||||||
DESTINATION ${YYCC_INSTALL_DOC_PATH}
|
DESTINATION ${YYCC_INSTALL_DOC_PATH}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1031,7 +1031,7 @@ EXCLUDE_SYMBOLS =
|
|||||||
# that contain example code fragments that are included (see the \include
|
# that contain example code fragments that are included (see the \include
|
||||||
# command).
|
# 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
|
# 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
|
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ public:
|
|||||||
m_StringArgument(YYCC_U8("string"), YYCC::ArgParser::AbstractArgument::NO_SHORT_NAME, nullptr, nullptr, true),
|
m_StringArgument(YYCC_U8("string"), YYCC::ArgParser::AbstractArgument::NO_SHORT_NAME, nullptr, nullptr, true),
|
||||||
m_BoolArgument(nullptr, YYCC_U8_CHAR('b'), nullptr),
|
m_BoolArgument(nullptr, YYCC_U8_CHAR('b'), nullptr),
|
||||||
m_ClampedFloatArgument(YYCC_U8("clamped-float"), YYCC::ArgParser::AbstractArgument::NO_SHORT_NAME, nullptr, nullptr, true, YYCC::Constraints::GetMinMaxRangeConstraint<float>(-1.0f, 1.0f)),
|
m_ClampedFloatArgument(YYCC_U8("clamped-float"), YYCC::ArgParser::AbstractArgument::NO_SHORT_NAME, nullptr, nullptr, true, YYCC::Constraints::GetMinMaxRangeConstraint<float>(-1.0f, 1.0f)),
|
||||||
m_OptionContext(YYCC_U8("TestArgParser"), YYCC_U8("This is the testbench of argument parser."), {
|
m_OptionContext(YYCC_U8("TestArgParser"), YYCC_U8("This is the test of argument parser."), {
|
||||||
&m_IntArgument, &m_FloatArgument, &m_StringArgument,
|
&m_IntArgument, &m_FloatArgument, &m_StringArgument,
|
||||||
&m_BoolArgument, &m_ClampedFloatArgument
|
&m_BoolArgument, &m_ClampedFloatArgument
|
||||||
}) {}
|
}) {}
|
||||||
@@ -86,12 +86,66 @@ so it must be initialized after initializing all settings.
|
|||||||
|
|
||||||
When initializing core manager, you need assign config file path first.
|
When initializing core manager, you need assign config file path first.
|
||||||
Then you need specify a version number.
|
Then you need specify a version number.
|
||||||
Version number will be used when reading config file.
|
Version number is important.
|
||||||
If the version of config file is higher than your given number,
|
It will be used when reading config file and only can be increased if needed (version can not downgrade).
|
||||||
core manager will assume you are trying to read a config file created by a higher version program.
|
|
||||||
Core manager will reject reading and use default value for all settings.
|
|
||||||
Otherwise, core manager will try to read config file and do proper migration if possible.
|
|
||||||
The last argument is an initializer list which contain the \b pointer to all settings this manager managed.
|
The last argument is an initializer list which contain the \b pointer to all settings this manager managed.
|
||||||
|
|
||||||
|
When executing YYCC::ConfigManager::CoreManager::Load to load configs, it will perform following steps one by one:
|
||||||
|
|
||||||
|
<UL>
|
||||||
|
<LI>
|
||||||
|
Open given config file.
|
||||||
|
<UL>
|
||||||
|
<LI>
|
||||||
|
If given file is not existing, loading function will simply return and all configs will be reset to its default value.
|
||||||
|
</LI>
|
||||||
|
<LI>
|
||||||
|
Success to open file, go to next step.
|
||||||
|
</LI>
|
||||||
|
</UL>
|
||||||
|
</LI>
|
||||||
|
|
||||||
|
<LI>
|
||||||
|
Fetch version number from file.
|
||||||
|
<UL>
|
||||||
|
<LI>
|
||||||
|
If fail to read version number from file, loading function will simply return and all configs will be reset to its default value.
|
||||||
|
</LI>
|
||||||
|
<LI>
|
||||||
|
If the version of config file is higher than your specified version number when constructing this class,
|
||||||
|
core manager will assume you are trying to read a config file created by a higher version program,
|
||||||
|
and will reject reading and use default value for all settings.
|
||||||
|
</LI>
|
||||||
|
<LI>
|
||||||
|
If the version of config file is lower than your specified version number,
|
||||||
|
core manager will try to read config file and do proper migration (set default value for configs which do not existing) if possible.
|
||||||
|
</LI>
|
||||||
|
<LI>
|
||||||
|
If the version of config file is equal than your specified version number,
|
||||||
|
core manager will read config file normally.
|
||||||
|
</LI>
|
||||||
|
</UL>
|
||||||
|
</LI>
|
||||||
|
|
||||||
|
<LI>
|
||||||
|
Read config file body.
|
||||||
|
<UL>
|
||||||
|
<LI>
|
||||||
|
If any IO error occurs when reading, loading function will simply return.
|
||||||
|
All read config will keep their read value and all configs which has not been read will keep their default value.
|
||||||
|
</LI>
|
||||||
|
<LI>
|
||||||
|
If some config can not parse binary data to its type,
|
||||||
|
this config will be skipped and core manager will process next config.
|
||||||
|
This config will keep its default value.
|
||||||
|
</LI>
|
||||||
|
</UL>
|
||||||
|
</LI>
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
All of these scenarios can be found by the return value of loading function.
|
||||||
|
The return type of loading function, ConfigLoadResult is a flag enum.
|
||||||
|
You can find whether loading process happend specified issue by using bitwise operation on it.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
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,49 +0,0 @@
|
|||||||
namespace YYCC::Constraints {
|
|
||||||
/**
|
|
||||||
|
|
||||||
\page constraints Constraints
|
|
||||||
|
|
||||||
YYCC::Constraints namespace provide Constraint struct declaration
|
|
||||||
and various common constraint generator function.
|
|
||||||
|
|
||||||
This namespace is specifically used by YYCC::ConfigManager and YYCC::ArgParser namespaces.
|
|
||||||
See \ref config_manager chapter and \ref arg_parser chapter for how to utlize this namespace.
|
|
||||||
|
|
||||||
\section constraints__prototype Prototype
|
|
||||||
|
|
||||||
Constraint instruct library how check whether given value is in range,
|
|
||||||
and how to clamp it if it is invalid.
|
|
||||||
For example, you can use constraint to limit a number in given minimum maximum value,
|
|
||||||
or limit a string in specific format by using regex and etc.
|
|
||||||
|
|
||||||
Constraint is a template struct.
|
|
||||||
The argument of template is the underlying data type which need to be checked.
|
|
||||||
The struct with different template argument is not compatible.
|
|
||||||
|
|
||||||
Currently, this struct only contain 1 function pointer,
|
|
||||||
which is used for detecting whether given value is in range / valid.
|
|
||||||
|
|
||||||
\subsection constraints__presets Constraint Presets
|
|
||||||
|
|
||||||
YYCC::Constraints provides some constraint presets which are commonly used.
|
|
||||||
All functions inside this namespace will return a Constraint instance,
|
|
||||||
and you can directly use it.
|
|
||||||
|
|
||||||
There is a list of all provided functions:
|
|
||||||
|
|
||||||
\li GetMinMaxRangeConstraint(): Limit the number value in given minimum maximum value range (inclusive).
|
|
||||||
\li GetEnumEnumerationConstraint(): Limit the enum value by given all possible value set.
|
|
||||||
\li GetStringEnumerationConstraint(): Limit the string by given all possible value set.
|
|
||||||
|
|
||||||
\subsection config_manager__constraint__custom Custom Constraint
|
|
||||||
|
|
||||||
For creating your personal constraint,
|
|
||||||
you need to create Constraint instance manually.
|
|
||||||
You can browse all existing constraint preset functions code for know how to write it.
|
|
||||||
|
|
||||||
The things you need to do is simple.
|
|
||||||
First, you need decide the template argument of Constraint.
|
|
||||||
Second, you need assign class member of Constraint by C++ lambda syntax.
|
|
||||||
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
<TD><CENTER>
|
<TD><CENTER>
|
||||||
<B>YYCCommonplace Programming Manual</B>
|
<B>YYCCommonplace Programming Manual</B>
|
||||||
|
|
||||||
Copyright 2024 by yyc12345.
|
Copyright 2024-2026 by yyc12345.
|
||||||
</CENTER></TD>
|
</CENTER></TD>
|
||||||
</TR>
|
</TR>
|
||||||
</TABLE>
|
</TABLE>
|
||||||
@@ -25,41 +25,51 @@
|
|||||||
<TR>
|
<TR>
|
||||||
<TD ALIGN="LEFT" VALIGN="TOP">
|
<TD ALIGN="LEFT" VALIGN="TOP">
|
||||||
|
|
||||||
<B>General Features</B>
|
<B>Overviews</B>
|
||||||
|
|
||||||
\li \subpage intro
|
\li \subpage intro
|
||||||
|
|
||||||
\li \subpage library_macros
|
\li \subpage premise_and_principle
|
||||||
|
|
||||||
\li \subpage library_encoding
|
<B>STL Enhancements</B>
|
||||||
|
|
||||||
|
\li \subpage macro
|
||||||
|
|
||||||
|
\li \subpage cenum
|
||||||
|
|
||||||
|
\li \subpage string__reinterpret
|
||||||
|
|
||||||
|
\li \subpage string__op
|
||||||
|
|
||||||
|
\li \subpage num__parser
|
||||||
|
|
||||||
|
\li \subpage patch
|
||||||
|
|
||||||
|
<!--
|
||||||
\li \subpage encoding_helper
|
\li \subpage encoding_helper
|
||||||
|
|
||||||
\li \subpage string_helper
|
|
||||||
|
|
||||||
\li \subpage parser_helper
|
|
||||||
|
|
||||||
\li \subpage console_helper
|
\li \subpage console_helper
|
||||||
|
|
||||||
\li \subpage io_helper
|
-->
|
||||||
|
|
||||||
\li \subpage std_patch
|
|
||||||
|
|
||||||
<B>Advanced Features</B>
|
|
||||||
|
|
||||||
|
<!--
|
||||||
\li \subpage constraints
|
\li \subpage constraints
|
||||||
|
|
||||||
\li \subpage config_manager
|
\li \subpage config_manager
|
||||||
|
|
||||||
\li \subpage arg_parser
|
\li \subpage arg_parser
|
||||||
|
-->
|
||||||
|
|
||||||
</TD>
|
</TD>
|
||||||
<TD ALIGN="LEFT" VALIGN="TOP">
|
<TD ALIGN="LEFT" VALIGN="TOP">
|
||||||
|
|
||||||
|
<B>Advanced Features (Carton)</B>
|
||||||
|
|
||||||
<B>Windows Specific Features</B>
|
<B>Windows Specific Features</B>
|
||||||
|
|
||||||
\li \subpage win_import
|
\li \subpage windows__import_guard
|
||||||
|
|
||||||
|
<!--
|
||||||
\li \subpage com_helper
|
\li \subpage com_helper
|
||||||
|
|
||||||
\li \subpage dialog_helper
|
\li \subpage dialog_helper
|
||||||
@@ -67,6 +77,7 @@
|
|||||||
\li \subpage win_fct_helper
|
\li \subpage win_fct_helper
|
||||||
|
|
||||||
\li \subpage exception_helper
|
\li \subpage exception_helper
|
||||||
|
-->
|
||||||
|
|
||||||
</TD>
|
</TD>
|
||||||
</TR>
|
</TR>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ YYCCommonplace, or YYC Commonplace (abbr. YYCC),
|
|||||||
is a static library providing various useful C++ functions
|
is a static library providing various useful C++ functions
|
||||||
when programming with standard library or Windows environment.
|
when programming with standard library or Windows environment.
|
||||||
|
|
||||||
During the development of a few projects,
|
At the beginning, during the development of a few projects,
|
||||||
I gradually understand how Windows make the compromise with the code written by its old developers,
|
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.
|
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.
|
So I create this static library for all of my C++ project.
|
||||||
@@ -15,6 +15,16 @@ I can use a clear and easy way to manage these codes.
|
|||||||
I can easily fix issues found in project using this library by updating a single project,
|
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
|
rather than fixing these duplicated code in each project one by one
|
||||||
because all of them share the same implementations.
|
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.
|
This project mainly is served for my personal use.
|
||||||
But I would be honored if you would like to use this in your project.
|
But I would be honored if you would like to use this in your project.
|
||||||
@@ -46,17 +56,18 @@ Thus I can have a similar Linux C++ programming experience on Windows.
|
|||||||
|
|
||||||
The eccentric decision of standard commission also is the reason why I create this library.
|
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 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 C++ standard commission loves delete programmer loved convenient functions and classes.
|
||||||
\li etc...
|
\li etc...
|
||||||
|
|
||||||
There is not a proper way to \e format a string in C++ until C++ 20 (\c std::format).
|
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.
|
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.
|
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.
|
Not industrial level, but easy to use and have enough performance in my project.
|
||||||
|
|
||||||
\subsection intro__why__boost Boost Issues
|
\subsection intro__why__boost Boost Issues
|
||||||
@@ -65,7 +76,7 @@ Bosst is a powerful C++ library. But the shortcoming is overt. It's tooooo big.
|
|||||||
This drawback will be more obvious considering the bad dependency mechanism of C++.
|
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.
|
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.
|
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.
|
The functions belonging to Boost is industrial level.
|
||||||
But what I want is not industrial level functions.
|
But what I want is not industrial level functions.
|
||||||
@@ -74,6 +85,7 @@ I don't need extreme performance. I just want my code works.
|
|||||||
|
|
||||||
So I create this library, bring some Boost functions with ordinary but not bad implementation.
|
So I create this library, bring some Boost functions with ordinary but not bad implementation.
|
||||||
|
|
||||||
|
<!--
|
||||||
\section intro__usage Library Usage
|
\section intro__usage Library Usage
|
||||||
|
|
||||||
Before using this library, I suggest you read this manual fully to have a full overview of this library.
|
Before using this library, I suggest you read this manual fully to have a full overview of this library.
|
||||||
@@ -116,11 +128,14 @@ Due to the different defination of UTF8 char type,
|
|||||||
C++ 20 program can not use this library built by C++ 17 environment.
|
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.
|
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.
|
The lowest and defult version of C++ standard is 17.
|
||||||
\li \c -d, \c --no-doc: Specify this if you don't want to build documentation.
|
\li \c -d, \c --build-doc: Specify this if you want to build documentation.
|
||||||
End user usually needs documentation,
|
End user usually needs documentation,
|
||||||
however if you are the developer of this library, you may need this switch.
|
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.
|
Because documentation take too much disk space and cost a bunch of time for building and copying.
|
||||||
In default, generator will produce script which build documentation automatically.
|
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>.
|
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>.
|
and you will also find your MSVC distribution in directory <TT>bin/<I>cpp_ver</I>/msvc_install</TT>.
|
||||||
@@ -184,5 +199,5 @@ YYCC CMake build script contains a special option called \c YYCC_DEBUG_UE_FILTER
|
|||||||
If you set it to true, it will add a public macro \c YYCC_DEBUG_UE_FILTER to YYCC project.
|
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.
|
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.
|
So in common use, user should not enable this option.
|
||||||
|
-->
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
namespace YYCC::IOHelper {
|
|
||||||
/**
|
|
||||||
|
|
||||||
\page io_helper IO Helper
|
|
||||||
|
|
||||||
Actually, YYCC::IOHelper includes functions which can not be placed in other place.
|
|
||||||
|
|
||||||
\section io_helper__ptr_pri_padding Pointer Print Padding
|
|
||||||
|
|
||||||
When printing pointer on screen, programmer usually left-pad zero to make it looks good.
|
|
||||||
However, the count of zero for padding is different in x86 and x64 architecture (8 for x86 and 16 for x64).
|
|
||||||
Macro \c PRI_XPTR_LEFT_PADDING will help you to resolve this issue.
|
|
||||||
|
|
||||||
Macro \c PRI_XPTR_LEFT_PADDING will be defined to following value according to the target system architecture.
|
|
||||||
|
|
||||||
\li \c "08": On x86 system.
|
|
||||||
\li \c "016": On x64 system.
|
|
||||||
|
|
||||||
There is an example for how to use it:
|
|
||||||
|
|
||||||
\code
|
|
||||||
void* raw_ptr = blabla();
|
|
||||||
std::printf(stdout, "Raw Pointer 0x%" PRI_XPTR_LEFT_PADDING PRIXPTR, raw_ptr);
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
Note \c PRIXPTR is defined by standard library for formatting pointer as hexadecimal style.
|
|
||||||
|
|
||||||
\section io_helper__smart_file Smart FILE Pointer
|
|
||||||
|
|
||||||
#SmartStdFile use \c std::unique_ptr with custom deleter to implement smart \c FILE*.
|
|
||||||
It is useful in the cases that you want to automatically free opened file when leaving corresponding scope.
|
|
||||||
|
|
||||||
\section io_helper__utf8_fopen UTF8 fopen
|
|
||||||
|
|
||||||
In Windows, standard \c std::fopen can not handle UTF8 file name in common environment.
|
|
||||||
So we create this function to give programmer an universal \c fopen in UTF8 style.
|
|
||||||
|
|
||||||
In Windows platform, this function will try to convert its argument to \c wchar_t
|
|
||||||
and calling Microsoft specific \c _wfopen function to open file.
|
|
||||||
If encoding convertion or \c _wfopen failed, this function will return \c nullptr like \c std::fopen does.
|
|
||||||
In other platforms, it will simply redirect calling to \c std::fopen.
|
|
||||||
|
|
||||||
There is a simple example:
|
|
||||||
|
|
||||||
\code
|
|
||||||
FILE* fs = YYCC::IOHelper::FOpen(YYCC_U8("/path/to/file"), YYCC_U8("rb"));
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
@@ -1,228 +0,0 @@
|
|||||||
namespace YYCC {
|
|
||||||
/**
|
|
||||||
|
|
||||||
\page library_encoding Library Encoding
|
|
||||||
|
|
||||||
Before using this library, you should know the encoding strategy of this library first.
|
|
||||||
In short words, this library use UTF8 encoding everywhere except some special cases,
|
|
||||||
for example, function explicitly order the encoding of input parameters.
|
|
||||||
|
|
||||||
In following content of this article, you will know the details about how we use UTF8 in this library.
|
|
||||||
|
|
||||||
\section library_encoding__utf8_type UTF8 Type
|
|
||||||
|
|
||||||
YYCC uses custom UTF8 char type, string container and string view all over the library, from parameters to return value.
|
|
||||||
Following content will introduce how we define them.
|
|
||||||
|
|
||||||
\subsection library_encoding__utf8_type__char_type Char Type
|
|
||||||
|
|
||||||
YYCC library has its own UTF8 char type, \c yycc_char8_t.
|
|
||||||
This is how we define it:
|
|
||||||
|
|
||||||
\code
|
|
||||||
#if defined(__cpp_char8_t)
|
|
||||||
using yycc_char8_t = char8_t;
|
|
||||||
#else
|
|
||||||
using yycc_char8_t = unsigned char;
|
|
||||||
#endif
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
If your environment (higher or equal to C++ 20) supports \c char8_t provided by standard library, \c yycc_char8_t is just an alias to \c char8_t,
|
|
||||||
otherwise (lower than C++ 20, e.g. C++ 17), \c yycc_char8_t will be defined as \c unsigned \c char like C++ 20 does (this can be seen as a polyfill).
|
|
||||||
|
|
||||||
This means that if you already have used \c char8_t provided by standard library,
|
|
||||||
you do not need to do any extra modification before using this library.
|
|
||||||
Because all types are compatible.
|
|
||||||
|
|
||||||
\subsection library_encoding__utf8_type__container_type String Container and View
|
|
||||||
|
|
||||||
We define string container and string view like this:
|
|
||||||
|
|
||||||
\code
|
|
||||||
using yycc_u8string = std::basic_string<yycc_char8_t>;
|
|
||||||
using yycc_u8string_view = std::basic_string_view<yycc_char8_t>;
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
The real code written in library may be slightly different with this but they have same meanings.
|
|
||||||
|
|
||||||
In \c char8_t environment, they are just the alias to \c std::u8string and \c std::u8string_view respectively.
|
|
||||||
So if you have already used them, no need to any modification for your code before using this library.
|
|
||||||
|
|
||||||
\subsection library_encoding__utf8_type__why Why?
|
|
||||||
|
|
||||||
You may curious why I create a new UTF8 char type, rather than using standard library UTF8 char type directly. There are 2 reasons.
|
|
||||||
|
|
||||||
First, It was too late that I notice I can use standard library UTF8 char type.
|
|
||||||
My UTF8 char type has been used in library everywhere and its tough to fully replace them into standard library UTF8 char type.
|
|
||||||
|
|
||||||
Second, UTF8 related content of standard library is \e volatile.
|
|
||||||
I notice standard library change UTF8 related functions frequently and its API are not stable.
|
|
||||||
For example, standard library brings \c std::codecvt_utf8 in C++ 11, deprecate it in C++ 17 and even remove it in C++ 26.
|
|
||||||
That's unacceptable! So I create my own UTF8 type to avoid the scenario that standard library remove \c char8_t in future.
|
|
||||||
|
|
||||||
\section library_encoding__concept Concepts
|
|
||||||
|
|
||||||
In following content, you may be face with 2 words: ordinary string and UTF8 string.
|
|
||||||
|
|
||||||
UTF8 string, as its name, is the string encoded with UTF8.
|
|
||||||
The char type of it must is \c yycc_char8_t.
|
|
||||||
(equivalent to \c char8_t after C++ 20.)
|
|
||||||
|
|
||||||
Ordinary string means the plain, native string.
|
|
||||||
The result of C++ string literal without any prefix \c "foo bar" is a rdinary string.
|
|
||||||
The char type of it is \c char.
|
|
||||||
Its encoding depends on compiler and environment.
|
|
||||||
(UTF8 in Linux, or system code page in Windows if UTF8 switch was not enabled in MSVC.)
|
|
||||||
|
|
||||||
For more infomation, please browse CppReference:
|
|
||||||
https://en.cppreference.com/w/cpp/language/string_literal
|
|
||||||
|
|
||||||
\section library_encoding__utf8_literal UTF8 Literal
|
|
||||||
|
|
||||||
String literal is a C++ concept.
|
|
||||||
If you are not familar with it, please browse related article first, such as CppReference.
|
|
||||||
|
|
||||||
\subsection library_encoding__utf8_literal__single Single Literal
|
|
||||||
|
|
||||||
In short words, YYCC allow you declare an UTF8 literal like this:
|
|
||||||
|
|
||||||
\code
|
|
||||||
YYCC_U8("This is UTF8 literal.")
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
YYCC_U8 is macro.
|
|
||||||
You don't need add extra \c u8 prefix in string given to the macro.
|
|
||||||
This macro will do this automatically.
|
|
||||||
|
|
||||||
In detail, this macro do a \c reinterpret_cast to change the type of given argument to \c const \c yycc_char8_t* forcely.
|
|
||||||
This ensure that declared UTF8 literal is compatible with YYCC UTF8 types.
|
|
||||||
|
|
||||||
\subsection library_encoding__utf8_literal__char Single Char
|
|
||||||
|
|
||||||
Same as UTF8 literal, YYCC allow you cast normal \c char into \c yycc_char8_t as following code:
|
|
||||||
|
|
||||||
\code
|
|
||||||
YYCC_U8_CHAR('A')
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
YYCC_U8_CHAR is a macro.
|
|
||||||
It just simply use \c static_cast to cast given value to \c yycc_char8_t.
|
|
||||||
It doesn't mean that you can cast non-ASCII characters,
|
|
||||||
because the space these characters occupied usually more than the maximum value of \c char.
|
|
||||||
For example, following code is \b invalid:
|
|
||||||
|
|
||||||
\code
|
|
||||||
YYCC_U8_CHAR('文') // INVALID!
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
\subsection library_encoding__utf8_literal__concatenation Literal Concatenation
|
|
||||||
|
|
||||||
YYCC_U8 macro also works for string literal concatenation:
|
|
||||||
|
|
||||||
\code
|
|
||||||
YYCC_U8("Error code: " PRIu32 ". Please contact me.");
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
According to C++ standard for string literal concatenation,
|
|
||||||
<I>"If one of the strings has an encoding prefix and the other does not, the one that does not will be considered to have the same encoding prefix as the other."</I>
|
|
||||||
At the same time, YYCC_U8 macro will automatically add \c u8 prefix for the first component of this string literal concatenation.
|
|
||||||
So the whole string will be UTF8 literal.
|
|
||||||
It also order you should \b not add any prefix for other components of this string literal concatenation.
|
|
||||||
|
|
||||||
\subsection library_encoding__utf8_literal__why Why?
|
|
||||||
|
|
||||||
You may know that C++ standard allows programmer declare an UTF8 literal explicitly by writing code like this:
|
|
||||||
|
|
||||||
\code
|
|
||||||
u8"foo bar"
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
This is okey. But it may incompatible with YYCC UTF8 char type.
|
|
||||||
According to C++ standard, this UTF8 literal syntax will only return \c const \c char8_t* if your C++ standard higher or equal to C++ 20,
|
|
||||||
otherwise it will return \c const \c char*.
|
|
||||||
This behavior cause that you can not assign this UTF8 literal to \c yycc_u8string if you are in the environment which do not support \c char8_t,
|
|
||||||
because their types are different.
|
|
||||||
Thereas you can not use the functions provided by this library because they are all use YYCC defined UTF8 char type.
|
|
||||||
|
|
||||||
\section library_encoding__utf8_pointer UTF8 String Pointer
|
|
||||||
|
|
||||||
String pointer means the raw pointer pointing to a string, such as \c const \c char*, \c char*, \c char32_t* and etc.
|
|
||||||
|
|
||||||
Many legacy code assume \c char* is encoded with UTF8 (the exception is Windows). But \c char* is incompatible with \c yycc_char8_t.
|
|
||||||
YYCC provides YYCC::EncodingHelper::ToUTF8 to resolve this issue. There is an exmaple:
|
|
||||||
|
|
||||||
\code
|
|
||||||
const char* absolutely_is_utf8 = "I confirm this is encoded with UTF8.";
|
|
||||||
const yycc_char8_t* converted = YYCC::EncodingHelper::ToUTF8(absolutely_is_utf8);
|
|
||||||
|
|
||||||
char* mutable_utf8 = const_cast<char*>(absolutely_is_utf8); // This is not safe. Just for example.
|
|
||||||
yycc_char8_t* mutable_converted = YYCC::EncodingHelper::ToUTF8(mutable_utf8);
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
YYCC::EncodingHelper::ToUTF8 has 2 overloads which can handle constant and mutable stirng pointer convertion respectively.
|
|
||||||
|
|
||||||
YYCC also has ability that convert YYCC UTF8 char type to ordinary char type by YYCC::EncodingHelper::ToOrdinary.
|
|
||||||
Here is an exmaple:
|
|
||||||
|
|
||||||
\code
|
|
||||||
const yycc_char8_t* yycc_utf8 = YYCC_U8("I am UTF8 string.");
|
|
||||||
const char* converted = YYCC::EncodingHelper::ToOrdinary(yycc_utf8);
|
|
||||||
|
|
||||||
yycc_char8_t* mutable_yycc_utf8 = const_cast<char*>(yycc_utf8); // Not safe. Also just for example.
|
|
||||||
char* mutable_converted = YYCC::EncodingHelper::ToOrdinary(mutable_yycc_utf8);
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
Same as YYCC::EncodingHelper::ToUTF8, YYCC::EncodingHelper::ToOrdinary also has 2 overloads to handle constant and mutable string pointer.
|
|
||||||
|
|
||||||
\section library_encoding__utf8_container UTF8 String Container
|
|
||||||
|
|
||||||
String container usually means the standard library string container, such as \c std::string, \c std::wstring, \c std::u32string and etc.
|
|
||||||
|
|
||||||
In many personal project, programmer may use \c std::string everywhere because \c std::u8string may not be presented when writing peoject.
|
|
||||||
How to do convertion between ordinary string container and YYCC UTF8 string container?
|
|
||||||
It is definitely illegal that directly do force convertion. Because they may have different class layout.
|
|
||||||
Calm down and I will tell you how to do correct convertion.
|
|
||||||
YYCC provides YYCC::EncodingHelper::ToUTF8 to convert ordinary string container to YYCC UTF8 string container.
|
|
||||||
There is an exmaple:
|
|
||||||
|
|
||||||
\code
|
|
||||||
std::string ordinary_string("I am UTF8");
|
|
||||||
yycc_u8string yycc_string = YYCC::EncodingHelper::ToUTF8(ordinary_string);
|
|
||||||
auto result = YYCC::EncodingHelper::UTF8ToUTF32(yycc_string);
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
Actually, YYCC::EncodingHelper::ToUTF8 accepts a reference to \c std::string_view as argument.
|
|
||||||
However, there is a implicit convertion from \c std::string to \c std::string_view,
|
|
||||||
so you can directly pass a \c std::string instance to it.
|
|
||||||
|
|
||||||
String view will reduce unnecessary memory copy.
|
|
||||||
If you just want to pass ordinary string container to function, and this function accepts \c yycc_u8string_view as its argument,
|
|
||||||
you can use alternative YYCC::EncodingHelper::ToUTF8View.
|
|
||||||
|
|
||||||
\code
|
|
||||||
std::string ordinary_string("I am UTF8");
|
|
||||||
yycc_u8string_view yycc_string = YYCC::EncodingHelper::ToUTF8View(ordinary_string);
|
|
||||||
auto result = YYCC::EncodingHelper::UTF8ToUTF32(yycc_string);
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
Comparing with previous one, this example use less memory.
|
|
||||||
The reduced memory is the content of \c yycc_string because string view is a view, not the copy of original string.
|
|
||||||
|
|
||||||
Same as UTF8 string pointer, we also have YYCC::EncodingHelper::ToOrdinary and YYCC::EncodingHelper::ToOrdinaryView do correspondant reverse convertion.
|
|
||||||
Try to do your own research and figure out how to use them.
|
|
||||||
It's pretty easy.
|
|
||||||
|
|
||||||
\section library_encoding__windows Warnings to Windows Programmer
|
|
||||||
|
|
||||||
Due to the legacy of MSVC, the encoding of \c char* may not be UTF8 in most cases.
|
|
||||||
If you run the convertion code introduced in this article with the string which is not encoded with UTF8, it may cause undefined behavior.
|
|
||||||
|
|
||||||
To enable UTF8 mode of MSVC, please deliver \c /utf-8 switch to MSVC.
|
|
||||||
Thus you can use the functions introduced in this article safely.
|
|
||||||
Otherwise, you must guarteen that the argument you provided to these functions is encoded by UTF8 manually.
|
|
||||||
|
|
||||||
Linux user do not need care this.
|
|
||||||
Because almost Linux distro use UTF8 in default.
|
|
||||||
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
namespace YYCC {
|
|
||||||
/**
|
|
||||||
|
|
||||||
\page library_macros Library Macros
|
|
||||||
|
|
||||||
In this page we will introduce the macros defined by this library
|
|
||||||
which can not be grouped in other topic.
|
|
||||||
|
|
||||||
\section library_macros__platform_checker Platform Checker
|
|
||||||
|
|
||||||
In many cross platform applications,
|
|
||||||
programmer usually write code adapted to different platforms in one source file
|
|
||||||
and enable them respectively by macros representing the target platform.
|
|
||||||
As a cross platform library,
|
|
||||||
YYCC also has this feature and you can utilize it if you don't have other ways to so the same things.
|
|
||||||
|
|
||||||
\subsection library_macros__platform_checker__values Values
|
|
||||||
|
|
||||||
YYCC always define a macro called \c YYCC_OS to indicate the system of target platform.
|
|
||||||
In implementation, it will check following list from top to bottom to set matched value for it.
|
|
||||||
|
|
||||||
\li \c YYCC_OS_WINDOWS: Windows environment. It is done by checking whether environment define \c _WIN32 macro.
|
|
||||||
\li \c YYCC_OS_LINUX: In current implementation, this means target platform is \b NOT Windows.
|
|
||||||
|
|
||||||
\subsection library_macros__platform_checker__usage Usage
|
|
||||||
|
|
||||||
Now you know any possible value of \c YYCC_OS.
|
|
||||||
The next step is how to use it to enable specified code in specific target platform.
|
|
||||||
We take Windows platform for example.
|
|
||||||
Assume \c blabla() function is Windows specific.
|
|
||||||
We have following example code:
|
|
||||||
|
|
||||||
\code
|
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
|
||||||
blabla();
|
|
||||||
#endif
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
It's enough and simple that use \c \#if to bracket the Windows specified code.
|
|
||||||
|
|
||||||
\section library_macros__batch_class_copy_move Batch Class Copy / Move Functions
|
|
||||||
|
|
||||||
YYCC provides 6 macros to batchly remove class copy constructor and move constructor,
|
|
||||||
or set default class copy constructor and move constructor.
|
|
||||||
|
|
||||||
<UL>
|
|
||||||
<LI>
|
|
||||||
\c YYCC_DEL_CLS_COPY: Declare following 2 statements which delete copy constrcutor and copy assign operator.
|
|
||||||
<UL>
|
|
||||||
<LI><TT>CLSNAME(const CLSNAME&) = delete;</TT></LI>
|
|
||||||
<LI><TT>CLSNAME& operator=(const CLSNAME&) = delete;</TT></LI>
|
|
||||||
</UL>
|
|
||||||
</LI>
|
|
||||||
<LI>
|
|
||||||
\c YYCC_DEL_CLS_MOVE: Declare following 2 statements which delete move constrcutor and move assign operator.
|
|
||||||
<UL>
|
|
||||||
<LI><TT>CLSNAME(CLSNAME&&) = delete;</TT></LI>
|
|
||||||
<LI><TT>CLSNAME& operator=(CLSNAME&&) = delete;</TT></LI>
|
|
||||||
</UL>
|
|
||||||
</LI>
|
|
||||||
<LI>\c YYCC_DEL_CLS_COPY_MOVE: The combination of \c YYCC_DEL_CLS_COPY and \c YYCC_DEL_CLS_MOVE.</LI>
|
|
||||||
<LI>
|
|
||||||
\c YYCC_DEF_CLS_COPY: Declare following 2 statements which set default copy constrcutor and copy assign operator.
|
|
||||||
<UL>
|
|
||||||
<LI><TT>CLSNAME(const CLSNAME&) = default;</TT></LI>
|
|
||||||
<LI><TT>CLSNAME& operator=(const CLSNAME&) = default;</TT></LI>
|
|
||||||
</UL>
|
|
||||||
</LI>
|
|
||||||
<LI>
|
|
||||||
\c YYCC_DEF_CLS_MOVE: Declare following 2 statements which set default move constrcutor and move assign operator.
|
|
||||||
<UL>
|
|
||||||
<LI><TT>CLSNAME(CLSNAME&&) = default;</TT></LI>
|
|
||||||
<LI><TT>CLSNAME& operator=(CLSNAME&&) = default;</TT></LI>
|
|
||||||
</UL>
|
|
||||||
</LI>
|
|
||||||
<LI>\c YYCC_DEF_CLS_COPY_MOVE: The combination of \c YYCC_DEF_CLS_COPY and \c YYCC_DEF_CLS_MOVE.</LI>
|
|
||||||
</UL>
|
|
||||||
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
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
|
||||||
|
|
||||||
|
*/
|
||||||
|
}
|
||||||
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.
|
||||||
|
|
||||||
|
*/
|
||||||
|
}
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
namespace YYCC::ParserHelper {
|
|
||||||
/**
|
|
||||||
|
|
||||||
\page parser_helper Parser Helper
|
|
||||||
|
|
||||||
This helper is served for the convertion between number and string.
|
|
||||||
|
|
||||||
\section parser_helper_supported_types Supported Types
|
|
||||||
|
|
||||||
Functions located in this helper support the convertion between string and following types:
|
|
||||||
|
|
||||||
\li Integral types (except \c bool): \c int, \c uint32_t, \c char and etc.
|
|
||||||
\li Floating point types: \c float, \c double and etc.
|
|
||||||
\li \c bool
|
|
||||||
|
|
||||||
Please note in C++, \c bool is integral type but we list it individually because parser will treat it specially.
|
|
||||||
For \c bool type, parser will try doing convertion between it and \c "true" \c "false" string.
|
|
||||||
(\b case-insensitive. It means that \c true can be converted from \c "true", \c "True" or \c "TRUE".)
|
|
||||||
|
|
||||||
\section parser_helper__try_parse Try Parse
|
|
||||||
|
|
||||||
#TryParse will try to parse string into caller specified type.
|
|
||||||
All of them accept an UTF8 string view at first argument,
|
|
||||||
require that you provide a container receiving converted result in the second argument,
|
|
||||||
and return a bool value to indicate whether the convertion is successful.
|
|
||||||
There are some examples:
|
|
||||||
|
|
||||||
\code
|
|
||||||
uint32_t val;
|
|
||||||
YYCC::ParserHelper::TryParse<uint32_t>(YYCC_U8("123"), val);
|
|
||||||
YYCC::ParserHelper::TryParse<uint32_t>(YYCC_U8("7fff"), val, 16);
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
For floating point type, this function allows caller to specify extra argument providing the format of given number string (\c std::chars_format).
|
|
||||||
For integral type, this function allows caller to specify extra argument providing the base of given number string.
|
|
||||||
|
|
||||||
\section parser_helper__parse Parse
|
|
||||||
|
|
||||||
#Parse is similar to #TryParse.
|
|
||||||
But it will not return bool value to indicate success and doesn't have the argument receiving result.
|
|
||||||
It only accepts an UTF8 string view as the only one argument, and return result directly.
|
|
||||||
If the convertion failed, the return value is \b undefined (but usually is the default value of given type).
|
|
||||||
There is an example:
|
|
||||||
|
|
||||||
\code
|
|
||||||
uint32_t val = YYCC::ParserHelper::Parse<uint32_t>(YYCC_U8("123"));
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
For integral and floating point value,
|
|
||||||
it has same extra argument with #TryParse to provide more number infomation.
|
|
||||||
|
|
||||||
Using this function is dangerous if the validation of your input is important.
|
|
||||||
In this case, please use #TryParse instead.
|
|
||||||
|
|
||||||
\section parser_helper__to_string To String
|
|
||||||
|
|
||||||
#ToString basically is the reversed operation of #Parse.
|
|
||||||
It gets the string representation of given type.
|
|
||||||
The only argument of these functions is the type which need to be converted to its string representation.
|
|
||||||
And they will return yycc_u8string as result.
|
|
||||||
There is an example:
|
|
||||||
|
|
||||||
\code
|
|
||||||
auto result = YYCC::ParserHelper::ToString<uint32_t>(UINT32_C(114));
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
For floating point type, this function allows caller to specify extra arguments
|
|
||||||
which provides the format (\c std::chars_format) and precision when getting string representation.
|
|
||||||
For integral type, this function allows caller to specify extra argument
|
|
||||||
providing the base of number when getting string representation.
|
|
||||||
|
|
||||||
\section parser_helper__notes Notes
|
|
||||||
|
|
||||||
All functions within this helper are implementated by standard library functions.
|
|
||||||
These functions just make a good wrapper for complex standard library functions.
|
|
||||||
And give you a experience like C\# parser functions.
|
|
||||||
|
|
||||||
Basically, all functions located in this helper have possibility to throw exception.
|
|
||||||
But this possibility are more close to the possibility that \c new statement throw \c std::bad_alloc.
|
|
||||||
So in most cases you can assume these functions will not throw any exception.
|
|
||||||
|
|
||||||
All functions are template functions.
|
|
||||||
The argument of template is the type these functions need to be processed.
|
|
||||||
Although C++ have \e smart template type deduction,
|
|
||||||
it would be better to specify template argument manually to explicitly specify your desired type.
|
|
||||||
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
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.
|
||||||
|
|
||||||
|
*/
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
namespace YYCC::StdPatch {
|
|
||||||
/**
|
|
||||||
|
|
||||||
\page std_patch Standard Library Patch
|
|
||||||
|
|
||||||
\section std_patch__starts_with_ends_with Starts With & Ends With
|
|
||||||
|
|
||||||
\c std::basic_string::starts_with and \c std::basic_string::ends_with (also available in \c std::basic_string_view)
|
|
||||||
are functions introduced in C++ 20 and unavailable in C++ 17.
|
|
||||||
YYCC::StdPatch provides a patch for these function in C++ 17 environment.
|
|
||||||
Please note these implementations are following implementation instruction presented by CppReference website.
|
|
||||||
And it should have the same performance with vanilla functions because Microsoft STL use the same way to implement.
|
|
||||||
These implementations will not fallback to vanilla function even they are available.
|
|
||||||
Because their performance are good.
|
|
||||||
|
|
||||||
To use these functions, you just need to call them like corresponding vanilla functions.
|
|
||||||
Our implementations provide all necessary overloads.
|
|
||||||
The only thing you need to do is provide the string self as the first argument,
|
|
||||||
because our implementations can not be inserted as a class member of string.
|
|
||||||
There is an example:
|
|
||||||
|
|
||||||
\code
|
|
||||||
YYCC::StdPatch::StartsWith(YYCC_U8("aabbcc"), YYCC_U8("aa"));
|
|
||||||
YYCC::StdPatch::EndsWith(YYCC_U8("aabbcc"), YYCC_U8("cc"));
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
\section std_patch__contains Contains
|
|
||||||
|
|
||||||
\c Contains function in standard library ordered and unordered successive container are also introduced in C++ 20.
|
|
||||||
YYCC::StdPatch provides a patch for this function in C++ 17 environment.
|
|
||||||
|
|
||||||
Please note this implementation will fallback to vanilla function if it is available.
|
|
||||||
Because our implementation is a remedy (there is no way to use public class member to have the same performance of vanilla function).
|
|
||||||
There is an example about how to use it:
|
|
||||||
|
|
||||||
\code
|
|
||||||
std::set<int> test { 1, 5 };
|
|
||||||
YYCC::StdPatch::Contains(test, static_cast<int>(5));
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
\section std_patch__fs_path std::filesystem::path Patch
|
|
||||||
|
|
||||||
As you know, the underlying char type of \c std::filesystem::path is \c wchar_t on Windows,
|
|
||||||
and in other platforms, it is simple \c char.
|
|
||||||
Due to this, if you try to create a \c std::filesystem::path instance by calling constructor with an UTF8 char sequence on Windows,
|
|
||||||
the library implementation will assume your input is based on current Windows code page, not UTF8.
|
|
||||||
And the final path stored in \c std::filesystem::path is not what you expcected.
|
|
||||||
|
|
||||||
This patch gives you a way to create \c std::filesystem::path
|
|
||||||
and extract path string stored in \c std::filesystem::path with UTF8 encoding.
|
|
||||||
This patch namespace always use UTF8 as its argument.
|
|
||||||
You should use the functions provided by this namespace on any platforms
|
|
||||||
instead of vanilla \c std::filesystem::path functions.
|
|
||||||
However, if your C++ standard is higher than C++ 20,
|
|
||||||
you can directly use UTF8 string pointer and string container in \c std::filesystem::path,
|
|
||||||
because standard library has supported them.
|
|
||||||
This patch only just want to provide an uniform programming experience.
|
|
||||||
|
|
||||||
This patch is served for Windows but also works on other plaftoms.
|
|
||||||
If you are in Windows, this patch will perform extra operations to achieve goals,
|
|
||||||
and in other platforms, they just redirect request to corresponding vanilla C++ functions.
|
|
||||||
|
|
||||||
\subsection std_patch__fs_path__from_utf8_path Create Path from UTF8 String
|
|
||||||
|
|
||||||
#ToStdPath provides this feature.
|
|
||||||
It accepts an string pointer to UTF8 string and try to create \c std::filesystem::path from it.
|
|
||||||
Function will throw exception if encoding convertion or constructor self failed.
|
|
||||||
There are some example:
|
|
||||||
|
|
||||||
\code
|
|
||||||
auto foobar_path = YYCC::StdPatch::ToStdPath(YYCC_U8("/foo/bar"));
|
|
||||||
auto slashed_path = foobar_path / YYCC::StdPatch::ToStdPath(YYCC_U8("test"));
|
|
||||||
auto replaced_ext = foobar_path.replace_extension(YYCC::StdPatch::ToStdPath(YYCC_U8(".txt")));
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
For first line in example, it is obvious that you can create a \c std::filesystem::path from this function.
|
|
||||||
However, for the second and third line in example, what we want to tell you is
|
|
||||||
that you should always use this function in other \c std::filesystem::path functions requiring path string.
|
|
||||||
|
|
||||||
\c std::filesystem::path is a very \e conservative class.
|
|
||||||
Most of its functions only accept \c std::filesystem::path self as argument.
|
|
||||||
For example, \c std::filesystem::path::replace_extension do not accept string as argument.
|
|
||||||
It accepts a reference to \c std::filesystem::path as argument.
|
|
||||||
(it still is possible that pass string pointer or string container to it because they can be converted to \c std::filesystem::path implicitly.)
|
|
||||||
It's great. This is what we expected!
|
|
||||||
We now can safely deliver the result generated by our function to these functions,
|
|
||||||
and don't need to worry about the encoding of we provided string.
|
|
||||||
Because all strings have been converted to \c std::filesystem::path by our function before passing them.
|
|
||||||
|
|
||||||
So, the second line will produce \c "/foo/bar/test"
|
|
||||||
and the third line will produce \c "/foo/bar.txt" in any platforms.
|
|
||||||
|
|
||||||
You may notice std::filesystem::u8path.
|
|
||||||
However it is depracted since C++ 20,
|
|
||||||
because \c std::filesystem::path directly supports UTF8 by \c char8_t since C++ 20.
|
|
||||||
Because C++ standard is volatile, we create this function to have an uniform programming experience.
|
|
||||||
|
|
||||||
\subsection std_patch__fs_path__to_utf8_path Extract UTF8 Path String from Path
|
|
||||||
|
|
||||||
#ToUTF8Path provides this feature.
|
|
||||||
It basically is the reversed operation of #ToStdPath.
|
|
||||||
It is usually used when you have done all path work in \c std::filesystem::path
|
|
||||||
and want to get the result.
|
|
||||||
There is an example:
|
|
||||||
|
|
||||||
\code
|
|
||||||
auto foobar_path = YYCC::StdPatch::ToStdPath(YYCC_U8("/foo/bar"));
|
|
||||||
auto result = YYCC::StdPatch::ToUTF8Path(foobar_path / YYCC::StdPatch::ToStdPath(YYCC_U8("test")));
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
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 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,149 +0,0 @@
|
|||||||
namespace YYCC::StringHelper {
|
|
||||||
/**
|
|
||||||
|
|
||||||
\page string_helper String Helper
|
|
||||||
|
|
||||||
\section string_helper__printf Printf VPrintf
|
|
||||||
|
|
||||||
YYCC::StringHelper provides 4 functions for formatting string.
|
|
||||||
These functions are mainly provided to programmer who can not use C++ 20 \c std::format feature.
|
|
||||||
|
|
||||||
\code
|
|
||||||
bool Printf(yycc_u8string&, const yycc_char8_t*, ...);
|
|
||||||
bool VPrintf(yycc_u8string&, const yycc_char8_t*, va_list argptr);
|
|
||||||
yycc_u8string Printf(const yycc_char8_t*, ...);
|
|
||||||
yycc_u8string VPrintf(const yycc_char8_t*, va_list argptr);
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
#Printf and #VPrintf is similar to \c std::sprintf and \c std::vsprintf.
|
|
||||||
#Printf accepts UTF8 format string and variadic arguments specifying data to print.
|
|
||||||
This is commonly used by programmer.
|
|
||||||
However, #VPrintf also do the same work but its second argument is \c va_list,
|
|
||||||
the representation of variadic arguments.
|
|
||||||
It is mostly used by other function which has variadic arguments.
|
|
||||||
|
|
||||||
The only difference between these function and standard library functions is
|
|
||||||
that you don't need to worry about whether the space of given buffer is enough,
|
|
||||||
because these functions help you to calculate this internally.
|
|
||||||
|
|
||||||
There is the same design like we introduced in \ref encoding_helper.
|
|
||||||
There are 2 overloads for #Printf and #VPrintf respectively.
|
|
||||||
First overload return bool value and require a string container as argument for storing result.
|
|
||||||
The second overload return result string directly.
|
|
||||||
As you expected, first overload will return false if fail to format string (this is barely happened).
|
|
||||||
and second overload will return empty string when formatter failed.
|
|
||||||
|
|
||||||
\section string_helper__replace Replace
|
|
||||||
|
|
||||||
YYCC::StringHelper provide 2 functions for programmer do string replacement:
|
|
||||||
|
|
||||||
\code
|
|
||||||
void Replace(yycc_u8string&, const yycc_u8string_view&, const yycc_u8string_view&);
|
|
||||||
yycc_u8string Replace(const yycc_u8string_view&, const yycc_u8string_view&, const yycc_u8string_view&);
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
The first overload will do replacement in given string container directly.
|
|
||||||
The second overload will produce a copy of original string and do replacement on the copied string.
|
|
||||||
|
|
||||||
#Replace has special treatments for following scenarios:
|
|
||||||
|
|
||||||
\li If given string is empty, the return value will be empty.
|
|
||||||
\li If the character sequence to be replaced is empty string, no replacement will happen.
|
|
||||||
\li If the character sequence will be replaced into string is or empty, it will simply delete found character sequence from given string.
|
|
||||||
|
|
||||||
\section string_helper__join Join
|
|
||||||
|
|
||||||
YYCC::StringHelper provide an universal way for joining string and various specialized join functions.
|
|
||||||
|
|
||||||
\subsection string_helper__join__universal Universal Join Function
|
|
||||||
|
|
||||||
Because C++ list types are various.
|
|
||||||
There is no unique and convenient way to create an universal join function.
|
|
||||||
So we create #JoinDataProvider to describe join context.
|
|
||||||
|
|
||||||
Before using universal join function,
|
|
||||||
you should setup #JoinDataProvider first, the context of join function.
|
|
||||||
It actually is an \c std::function object which can be easily fetched by C++ lambda syntax.
|
|
||||||
This function pointer accept a reference to \c yycc_u8string_view,
|
|
||||||
programmer should set it to the string to be joined when at each calling.
|
|
||||||
And this function pointer return a bool value to indicate the end of join.
|
|
||||||
You can simply return \c false to terminate join process.
|
|
||||||
The argument you assigned to argument will not be taken into join process when you return false.
|
|
||||||
|
|
||||||
Then, you can pass the created #JoinDataProvider object to #Join function.
|
|
||||||
And specify decilmer at the same time.
|
|
||||||
Then you can get the final joined string.
|
|
||||||
There is an example:
|
|
||||||
|
|
||||||
\code
|
|
||||||
std::vector<yycc_u8string> data {
|
|
||||||
YYCC_U8(""), YYCC_U8("1"), YYCC_U8("2"), YYCC_U8("")
|
|
||||||
};
|
|
||||||
auto iter = data.cbegin();
|
|
||||||
auto stop = data.cend();
|
|
||||||
auto joined_string = YYCC::StringHelper::Join(
|
|
||||||
[&iter, &stop](yycc_u8string_view& view) -> bool {
|
|
||||||
if (iter == stop) return false;
|
|
||||||
view = *iter;
|
|
||||||
++iter;
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
decilmer
|
|
||||||
);
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
\subsection string_helper__join__specialized Specialized Join Function
|
|
||||||
|
|
||||||
Despite universal join function,
|
|
||||||
YYCC::StringHelper also provide a specialized join functions for standard library container.
|
|
||||||
For example, the code written above can be written in following code by using this specialized overload.
|
|
||||||
The first two argument is just the begin and end iterator.
|
|
||||||
However, you must make sure that we can dereference it and then implicitly convert it to yycc_u8string_view.
|
|
||||||
Otherwise this overload will throw template error.
|
|
||||||
|
|
||||||
\code
|
|
||||||
std::vector<yycc_u8string> data {
|
|
||||||
YYCC_U8(""), YYCC_U8("1"), YYCC_U8("2"), YYCC_U8("")
|
|
||||||
};
|
|
||||||
auto joined_string = YYCC::StringHelper::Join(data.begin(), data.end(), decilmer);
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
\section string_helper__lower_upper Lower Upper
|
|
||||||
|
|
||||||
String helper provides Python-like string lower and upper function.
|
|
||||||
Both lower and upper function have 2 overloads:
|
|
||||||
|
|
||||||
\code
|
|
||||||
yycc_u8string Lower(const yycc_u8string_view&);
|
|
||||||
void Lower(yycc_u8string&);
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
First overload accepts a string view as argument and return a \b copy whose content are all the lower case of original string.
|
|
||||||
Second overload accepts a mutable string container as argument and will make all characters stored in it become their lower case.
|
|
||||||
You can choose on of them for your flavor and requirements.
|
|
||||||
Upper also has similar 2 overloads.
|
|
||||||
|
|
||||||
\section string_helper__split Split
|
|
||||||
|
|
||||||
String helper provides Python-like string split function.
|
|
||||||
It has 2 types for you:
|
|
||||||
|
|
||||||
\code
|
|
||||||
std::vector<yycc_u8string> Split(const yycc_u8string_view&, const yycc_u8string_view&);
|
|
||||||
std::vector<yycc_u8string_view> SplitView(const yycc_u8string_view&, const yycc_u8string_view&);
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
All these overloads take a string view as the first argument representing the string need to be split.
|
|
||||||
The second argument is a string view representing the 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 overload will cost less memory if you don't need the copy of original string.
|
|
||||||
|
|
||||||
If the source string (the string need to be split) is empty, or the decilmer is empty,
|
|
||||||
the result will only has 1 item and this item is source string itself.
|
|
||||||
There is no way that these methods return an empty list, except the code is buggy.
|
|
||||||
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
@@ -1,25 +1,27 @@
|
|||||||
namespace YYCC {
|
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.
|
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.
|
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.
|
YYCC has a way to solve the issue introduced above.
|
||||||
|
|
||||||
\code
|
\code
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
#include <yycc/macro/os_detector.hpp>
|
||||||
#include <WinImportPrefix.hpp>
|
|
||||||
|
#if defined(YYCC_OS_WINDOWS)
|
||||||
|
#include <yycc/windows/import_guard_head.hpp>
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#include "other_header_depend_on_windows.h"
|
#include "other_header_depend_on_windows.h"
|
||||||
#include <WinImportSuffix.hpp>
|
#include <yycc/windows/import_guard_tail.hpp>
|
||||||
#endif
|
#endif
|
||||||
\endcode
|
\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,
|
They just like a guard bracket the include operation of Windows related headers,
|
||||||
to keep all Windows shitty contents will not be leaked outside.
|
to keep all Windows shitty contents will not be leaked outside.
|
||||||
|
|
||||||
@@ -30,7 +32,7 @@ This guard can solve following issues:
|
|||||||
Programmer can not use \c std::max and \c std::min normally.
|
Programmer can not use \c std::max and \c std::min normally.
|
||||||
<UL>
|
<UL>
|
||||||
<LI>Windows defines \c MAX and \c MIN as macros for personal use. This is why this happened.</LI>
|
<LI>Windows defines \c MAX and \c MIN as macros for personal use. This is why this happened.</LI>
|
||||||
<LI>Guard defines some special macros to tell Windows do not create these 2 macros.</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>
|
</UL>
|
||||||
</LI>
|
</LI>
|
||||||
<LI>
|
<LI>
|
||||||
@@ -44,12 +46,12 @@ This guard can solve following issues:
|
|||||||
Compiler throw annoy warnings and errors when using specific standard library functions.
|
Compiler throw annoy warnings and errors when using specific standard library functions.
|
||||||
<UL>
|
<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>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>
|
</UL>
|
||||||
</LI>
|
</LI>
|
||||||
</UL>
|
</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,
|
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.
|
you should put them into this bracket at the same time like example did.
|
||||||
214
script/.gitignore
vendored
214
script/.gitignore
vendored
@@ -1,2 +1,212 @@
|
|||||||
# -------------------- Output --------------------
|
## ===== Myself =====
|
||||||
win_build.bat
|
# 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__/
|
||||||
|
|||||||
@@ -1,170 +0,0 @@
|
|||||||
import argparse
|
|
||||||
import os
|
|
||||||
import io
|
|
||||||
import re
|
|
||||||
|
|
||||||
def validate_cpp_ver(ver: str) -> str:
|
|
||||||
if re.match(r'^[0-9]+$', ver) is not None: return ver
|
|
||||||
else: raise argparse.ArgumentTypeError('invalid version of C++ standard.')
|
|
||||||
|
|
||||||
def write_line(f: io.TextIOWrapper, val: str) -> None:
|
|
||||||
f.write(val)
|
|
||||||
f.write('\n')
|
|
||||||
|
|
||||||
# Reference: https://stackoverflow.com/questions/29213106/how-to-securely-escape-command-line-arguments-for-the-cmd-exe-shell-on-windows
|
|
||||||
def escape_argument(arg):
|
|
||||||
if not arg or re.search(r'(["\s])', arg):
|
|
||||||
arg = '"' + arg.replace('"', r'\"') + '"'
|
|
||||||
return escape_for_cmd_exe(arg)
|
|
||||||
def escape_for_cmd_exe(arg):
|
|
||||||
meta_re = re.compile(r'([()%!^"<>&|])')
|
|
||||||
return meta_re.sub('^\1', arg)
|
|
||||||
|
|
||||||
class ScriptSettings:
|
|
||||||
m_CppVersion: str
|
|
||||||
m_NoDoc: bool
|
|
||||||
|
|
||||||
def __init__(self, cpp_ver: str, no_doc: bool):
|
|
||||||
self.m_CppVersion = cpp_ver
|
|
||||||
self.m_NoDoc = no_doc
|
|
||||||
|
|
||||||
def script_head(f: io.TextIOWrapper, s: ScriptSettings) -> None:
|
|
||||||
# change directory to root folder
|
|
||||||
write_line(f, ':: Navigate to project root directory')
|
|
||||||
root_dir: str = os.path.dirname(os.path.dirname(__file__))
|
|
||||||
write_line(f, f'CD /d {escape_argument(root_dir)}')
|
|
||||||
# create build directory and enter
|
|
||||||
write_line(f, ':: Create build directory and enter it')
|
|
||||||
write_line(f, 'MKDIR bin')
|
|
||||||
write_line(f, 'CD bin')
|
|
||||||
cpp_dir: str = f'cpp{s.m_CppVersion}'
|
|
||||||
write_line(f, f'MKDIR {cpp_dir}')
|
|
||||||
write_line(f, f'CD {cpp_dir}')
|
|
||||||
# blank line
|
|
||||||
write_line(f, '')
|
|
||||||
|
|
||||||
def script_tail(f: io.TextIOWrapper, s: ScriptSettings) -> None:
|
|
||||||
# leave build directory and report success
|
|
||||||
write_line(f, ':: Leave build directory and report')
|
|
||||||
write_line(f, 'CD ..\\..')
|
|
||||||
write_line(f, 'ECHO Windows CMake Build Done')
|
|
||||||
|
|
||||||
def create_directory(f: io.TextIOWrapper, s: ScriptSettings) -> None:
|
|
||||||
# create build directory
|
|
||||||
write_line(f, ':: Create internal build directory')
|
|
||||||
write_line(f, 'MKDIR Win32')
|
|
||||||
write_line(f, 'MKDIR x64')
|
|
||||||
write_line(f, 'MKDIR documentation')
|
|
||||||
# create install directory
|
|
||||||
write_line(f, ':: Create internal install directory')
|
|
||||||
write_line(f, 'MKDIR install')
|
|
||||||
write_line(f, 'CD install')
|
|
||||||
write_line(f, 'MKDIR Win32_Debug')
|
|
||||||
write_line(f, 'MKDIR Win32_Release')
|
|
||||||
write_line(f, 'MKDIR x64_Debug')
|
|
||||||
write_line(f, 'MKDIR x64_Release')
|
|
||||||
write_line(f, 'CD ..')
|
|
||||||
# create msvc install directory
|
|
||||||
write_line(f, ':: Create internal MSVC specific install directory')
|
|
||||||
write_line(f, 'MKDIR msvc_install')
|
|
||||||
write_line(f, 'CD msvc_install')
|
|
||||||
write_line(f, 'MKDIR bin')
|
|
||||||
write_line(f, 'MKDIR include')
|
|
||||||
write_line(f, 'MKDIR lib')
|
|
||||||
write_line(f, 'MKDIR share')
|
|
||||||
write_line(f, 'CD bin')
|
|
||||||
write_line(f, 'MKDIR Win32')
|
|
||||||
write_line(f, 'MKDIR x64')
|
|
||||||
write_line(f, 'CD ..')
|
|
||||||
write_line(f, 'CD lib')
|
|
||||||
write_line(f, 'MKDIR Win32\\Debug')
|
|
||||||
write_line(f, 'MKDIR Win32\\Release')
|
|
||||||
write_line(f, 'MKDIR x64\\Debug')
|
|
||||||
write_line(f, 'MKDIR x64\\Release')
|
|
||||||
write_line(f, 'CD ..')
|
|
||||||
write_line(f, 'CD ..')
|
|
||||||
# blank line
|
|
||||||
write_line(f, '')
|
|
||||||
|
|
||||||
def cmake_build(f: io.TextIOWrapper, s: ScriptSettings) -> None:
|
|
||||||
# build for Win32
|
|
||||||
write_line(f, ':: Build for Win32')
|
|
||||||
write_line(f, 'CD Win32')
|
|
||||||
write_line(f, f'cmake -G "Visual Studio 16 2019" -A Win32 -DCMAKE_CXX_STANDARD={s.m_CppVersion} -DYYCC_BUILD_TESTBENCH=ON ../../..')
|
|
||||||
write_line(f, 'cmake --build . --config Debug')
|
|
||||||
write_line(f, 'cmake --install . --prefix=../install/Win32_Debug --config Debug')
|
|
||||||
write_line(f, 'cmake --build . --config Release')
|
|
||||||
write_line(f, 'cmake --install . --prefix=../install/Win32_Release --config Release')
|
|
||||||
write_line(f, 'CD ..')
|
|
||||||
# build for x64
|
|
||||||
write_line(f, ':: Build for x64')
|
|
||||||
write_line(f, 'CD x64')
|
|
||||||
write_line(f, f'cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_CXX_STANDARD={s.m_CppVersion} -DYYCC_BUILD_TESTBENCH=ON ../../..')
|
|
||||||
write_line(f, 'cmake --build . --config Debug')
|
|
||||||
write_line(f, 'cmake --install . --prefix=../install/x64_Debug --config Debug')
|
|
||||||
write_line(f, 'cmake --build . --config Release')
|
|
||||||
write_line(f, 'cmake --install . --prefix=../install/x64_Release --config Release')
|
|
||||||
write_line(f, 'CD ..')
|
|
||||||
# build for documentation
|
|
||||||
if not s.m_NoDoc:
|
|
||||||
write_line(f, ':: Build for documentation')
|
|
||||||
write_line(f, 'CD documentation')
|
|
||||||
write_line(f, f'cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_CXX_STANDARD={s.m_CppVersion} -DYYCC_BUILD_DOC=ON ../../..')
|
|
||||||
write_line(f, 'cmake --build . --config Release')
|
|
||||||
write_line(f, 'cmake --build . --target YYCCDocumentation')
|
|
||||||
write_line(f, 'cmake --install . --prefix=../install/x64_Release --config Release')
|
|
||||||
write_line(f, 'CD ..')
|
|
||||||
# blank line
|
|
||||||
write_line(f, '')
|
|
||||||
|
|
||||||
def msvc_build(f: io.TextIOWrapper, s: ScriptSettings) -> None:
|
|
||||||
# copy include from x64_Release build
|
|
||||||
write_line(f, ':: Copy header files')
|
|
||||||
write_line(f, 'XCOPY install\\x64_Release\\include msvc_install\\include\\ /E /Y')
|
|
||||||
# copy binary testbench
|
|
||||||
write_line(f, ':: Copy binary files')
|
|
||||||
write_line(f, 'COPY install\\Win32_Release\\bin\\YYCCTestbench.exe msvc_install\\bin\\Win32\\YYCCTestbench.exe /Y')
|
|
||||||
write_line(f, 'COPY install\\x64_Release\\bin\\YYCCTestbench.exe msvc_install\\bin\\x64\\YYCCTestbench.exe /Y')
|
|
||||||
# copy static library
|
|
||||||
write_line(f, ':: Copy library files')
|
|
||||||
write_line(f, 'COPY install\\Win32_Debug\\lib\\YYCCommonplace.lib msvc_install\\lib\\Win32\\Debug\\YYCCommonplace.lib /Y')
|
|
||||||
write_line(f, 'COPY install\\Win32_Release\\lib\\YYCCommonplace.lib msvc_install\\lib\\Win32\\Release\\YYCCommonplace.lib /Y')
|
|
||||||
write_line(f, 'COPY install\\x64_Debug\\lib\\YYCCommonplace.lib msvc_install\\lib\\x64\\Debug\\YYCCommonplace.lib /Y')
|
|
||||||
write_line(f, 'COPY install\\x64_Release\\lib\\YYCCommonplace.lib msvc_install\\lib\\x64\\Release\\YYCCommonplace.lib /Y')
|
|
||||||
# Copy document from x64_Release build
|
|
||||||
if not s.m_NoDoc:
|
|
||||||
write_line(f, ':: Copy documentation files')
|
|
||||||
write_line(f, 'XCOPY install\\x64_Release\\share msvc_install\\share\\ /E /Y')
|
|
||||||
# blank line
|
|
||||||
write_line(f, '')
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
# parse argument
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
prog='YYCC Windows Build Script Generator',
|
|
||||||
description='YYCC Windows Build Script Generator'
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
'-c', '--cpp',
|
|
||||||
action='store', default='17', dest='cpp', type=validate_cpp_ver,
|
|
||||||
help='The version of C++ standard used when building.'
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
'-d', '--no-doc',
|
|
||||||
action='store_true', dest='no_doc',
|
|
||||||
help='Build YYCC without documentation.'
|
|
||||||
)
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
# build settings
|
|
||||||
settings = ScriptSettings(args.cpp, args.no_doc)
|
|
||||||
# write result
|
|
||||||
filepath = os.path.join(os.path.dirname(__file__), 'win_build.bat')
|
|
||||||
with open(filepath, 'w') as f:
|
|
||||||
write_line(f, '@ECHO OFF')
|
|
||||||
script_head(f, settings)
|
|
||||||
create_directory(f, settings)
|
|
||||||
cmake_build(f, settings)
|
|
||||||
msvc_build(f, settings)
|
|
||||||
script_tail(f, settings)
|
|
||||||
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
README_PATH=$(pwd)/README.md
|
|
||||||
if [ ! -f "$README_PATH" ]; then
|
|
||||||
echo "Error: You must run this script at the root folder of this project!"
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create main binary directory
|
|
||||||
mkdir bin
|
|
||||||
cd bin
|
|
||||||
# Create build directory
|
|
||||||
mkdir build
|
|
||||||
# Create install directory
|
|
||||||
mkdir install
|
|
||||||
cd install
|
|
||||||
mkdir Debug
|
|
||||||
mkdir Release
|
|
||||||
cd ..
|
|
||||||
|
|
||||||
# Build current system debug and release version
|
|
||||||
cd build
|
|
||||||
cmake -DCMAKE_BUILD_TYPE=Debug ../.. --fresh
|
|
||||||
cmake --build .
|
|
||||||
cmake --install . --prefix ../install/Debug
|
|
||||||
cmake -DCMAKE_BUILD_TYPE=Release -DYYCC_BUILD_TESTBENCH=ON ../.. --fresh
|
|
||||||
cmake --build .
|
|
||||||
cmake --install . --prefix ../install/Release
|
|
||||||
cd ..
|
|
||||||
|
|
||||||
# Exit to original path
|
|
||||||
cd ..
|
|
||||||
echo "Linux CMake Build Done"
|
|
||||||
2
script/pycodec/.gitignore
vendored
Normal file
2
script/pycodec/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Exclude result
|
||||||
|
*.cpp
|
||||||
7
script/pycodec/README.md
Normal file
7
script/pycodec/README.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# PyCodec
|
||||||
|
|
||||||
|
This directory contain all stuff related to PyCodec.
|
||||||
|
|
||||||
|
PyCodec use different encoding system on different OS. In Windows it use Win32 functions, and it will use Iconv in other OS. So we need a table converting PyCodec universal encoding name to Windows Code Page or Iconv Code Name. These relation was stored in CSV file and Python script will render it into C++ source code.
|
||||||
|
|
||||||
|
For the format of CSV file, each line is a record. The first item in record is the standard PyCodec name. The second item is corresponding Windows Code Page. If there is no corresponding Code Page, it can be empty. The third item is corresponding Iconv Code Name. It also can be empty with same case. Then, the count of remain columns is variables after forth item (inclusive). All of them is the alias of this standard PyCodec name.
|
||||||
54
script/pycodec/conv_encoding_table.py
Normal file
54
script/pycodec/conv_encoding_table.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import csv
|
||||||
|
from pathlib import Path
|
||||||
|
import jinja2
|
||||||
|
|
||||||
|
|
||||||
|
class LanguageToken:
|
||||||
|
name: str
|
||||||
|
alias: tuple[str, ...]
|
||||||
|
code_page: str | None
|
||||||
|
iconv_code: str | None
|
||||||
|
|
||||||
|
def __init__(self, row: list[str]):
|
||||||
|
"""Init language token from CSV row."""
|
||||||
|
self.name = row[0].lower()
|
||||||
|
code_page = row[1]
|
||||||
|
self.code_page = None if len(code_page) == 0 else code_page
|
||||||
|
iconv_code = row[2]
|
||||||
|
self.iconv_code = None if len(iconv_code) == 0 else iconv_code
|
||||||
|
# For alias, we strip and to lower them first, and remove all empty entries
|
||||||
|
alias = row[3:]
|
||||||
|
self.alias = tuple(
|
||||||
|
filter(lambda x: len(x) != 0,
|
||||||
|
map(lambda x: x.strip().lower(), alias)))
|
||||||
|
|
||||||
|
|
||||||
|
def _get_self_dir() -> Path:
|
||||||
|
return Path(__file__).resolve().parent
|
||||||
|
|
||||||
|
|
||||||
|
def _extract_tokens() -> list[LanguageToken]:
|
||||||
|
rv: list[LanguageToken] = []
|
||||||
|
csv_file = _get_self_dir() / 'encoding_table.csv'
|
||||||
|
|
||||||
|
with open(csv_file, 'r', encoding='utf-8', newline='') as f:
|
||||||
|
reader = csv.reader(f, delimiter='\t')
|
||||||
|
for row in reader:
|
||||||
|
rv.append(LanguageToken(row))
|
||||||
|
|
||||||
|
return rv
|
||||||
|
|
||||||
|
|
||||||
|
def _render_cpp(tokens: list[LanguageToken]) -> None:
|
||||||
|
loader = jinja2.FileSystemLoader(_get_self_dir())
|
||||||
|
environment = jinja2.Environment(loader=loader)
|
||||||
|
template = environment.get_template('encoding_table.cpp.jinja')
|
||||||
|
|
||||||
|
cpp_file = _get_self_dir() / 'encoding_table.cpp'
|
||||||
|
with open(cpp_file, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(template.render(tokens=tokens))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
tokens = _extract_tokens()
|
||||||
|
_render_cpp(tokens)
|
||||||
23
script/pycodec/encoding_table.cpp.jinja
Normal file
23
script/pycodec/encoding_table.cpp.jinja
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
static const std::map<std::u8string_view, std::u8string_view> ALIAS_MAP {
|
||||||
|
{% for token in tokens -%}
|
||||||
|
{% for alias in token.alias -%}
|
||||||
|
{ u8"{{ alias }}"sv, u8"{{ token.name }}"sv },
|
||||||
|
{% endfor -%}
|
||||||
|
{% endfor -%}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::map<std::u8string_view, CodePage> WINCP_MAP {
|
||||||
|
{% for token in tokens -%}
|
||||||
|
{% if token.code_page is not none -%}
|
||||||
|
{ u8"{{ token.name }}"sv, static_cast<CodePage>({{ token.code_page }}u) },
|
||||||
|
{% endif -%}
|
||||||
|
{% endfor -%}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::map<std::u8string_view, std::string_view> ICONV_MAP {
|
||||||
|
{% for token in tokens -%}
|
||||||
|
{% if token.iconv_code is not none -%}
|
||||||
|
{ u8"{{ token.name }}"sv, "{{ token.iconv_code }}"sv },
|
||||||
|
{% endif -%}
|
||||||
|
{% endfor -%}
|
||||||
|
};
|
||||||
97
script/pycodec/encoding_table.csv
Normal file
97
script/pycodec/encoding_table.csv
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
ascii 437 ASCII 646 us-ascii
|
||||||
|
big5 950 BIG5 big5-tw csbig5
|
||||||
|
big5hkscs BIG5-HKSCS big5-hkscs hkscs
|
||||||
|
cp037 037 IBM037 IBM039
|
||||||
|
cp273 273 IBM273 csIBM273
|
||||||
|
cp424 EBCDIC-CP-HE IBM424
|
||||||
|
cp437 437 437 IBM437
|
||||||
|
cp500 500 EBCDIC-CP-BE EBCDIC-CP-CH IBM500
|
||||||
|
cp720 720
|
||||||
|
cp737 737
|
||||||
|
cp775 775 IBM775
|
||||||
|
cp850 850 CP850 850 IBM850
|
||||||
|
cp852 852 852 IBM852
|
||||||
|
cp855 855 855 IBM855
|
||||||
|
cp856
|
||||||
|
cp857 857 857 IBM857
|
||||||
|
cp858 858 858 IBM858
|
||||||
|
cp860 860 860 IBM860
|
||||||
|
cp861 861 861 CP-IS IBM861
|
||||||
|
cp862 862 CP862 862 IBM862
|
||||||
|
cp863 863 863 IBM863
|
||||||
|
cp864 864 IBM864
|
||||||
|
cp865 865 865 IBM865
|
||||||
|
cp866 866 CP866 866 IBM866
|
||||||
|
cp869 869 869 CP-GR IBM869
|
||||||
|
cp874 874 CP874
|
||||||
|
cp875 875
|
||||||
|
cp932 932 CP932 932 ms932 mskanji ms-kanji windows-31j
|
||||||
|
cp949 949 CP949 949 ms949 uhc
|
||||||
|
cp950 950 CP950 950 ms950
|
||||||
|
cp1006
|
||||||
|
cp1026 1026 ibm1026
|
||||||
|
cp1125 1125 ibm1125 cp866u ruscii
|
||||||
|
cp1140 1140 ibm1140
|
||||||
|
cp1250 1250 CP1250 windows-1250
|
||||||
|
cp1251 1251 CP1251 windows-1251
|
||||||
|
cp1252 1252 CP1252 windows-1252
|
||||||
|
cp1253 1253 CP1253 windows-1253
|
||||||
|
cp1254 1254 CP1254 windows-1254
|
||||||
|
cp1255 1255 CP1255 windows-1255
|
||||||
|
cp1256 1256 CP1256 windows-1256
|
||||||
|
cp1257 1257 CP1257 windows-1257
|
||||||
|
cp1258 1258 CP1258 windows-1258
|
||||||
|
euc_jp 20932 EUC-JP eucjp ujis u-jis
|
||||||
|
euc_jis_2004 jisx0213 eucjis2004
|
||||||
|
euc_jisx0213 eucjisx0213
|
||||||
|
euc_kr 51949 EUC-KR euckr korean ksc5601 ks_c-5601 ks_c-5601-1987 ksx1001 ks_x-1001
|
||||||
|
gb2312 936 CP936 chinese csiso58gb231280 euc-cn euccn eucgb2312-cn gb2312-1980 gb2312-80 iso-ir-58
|
||||||
|
gbk 936 GBK 936 cp936 ms936
|
||||||
|
gb18030 54936 GB18030 gb18030-2000
|
||||||
|
hz 52936 HZ hzgb hz-gb hz-gb-2312
|
||||||
|
iso2022_jp 50220 ISO-2022-JP csiso2022jp iso2022jp iso-2022-jp
|
||||||
|
iso2022_jp_1 ISO-2022-JP-1 iso2022jp-1 iso-2022-jp-1
|
||||||
|
iso2022_jp_2 ISO-2022-JP-2 iso2022jp-2 iso-2022-jp-2
|
||||||
|
iso2022_jp_2004 iso2022jp-2004 iso-2022-jp-2004
|
||||||
|
iso2022_jp_3 iso2022jp-3 iso-2022-jp-3
|
||||||
|
iso2022_jp_ext iso2022jp-ext iso-2022-jp-ext
|
||||||
|
iso2022_kr 50225 ISO-2022-KR csiso2022kr iso2022kr iso-2022-kr
|
||||||
|
latin_1 28591 ISO-8859-1 iso-8859-1 iso8859-1 8859 cp819 latin latin1 L1
|
||||||
|
iso8859_2 28592 ISO-8859-2 iso-8859-2 latin2 L2
|
||||||
|
iso8859_3 28593 ISO-8859-3 iso-8859-3 latin3 L3
|
||||||
|
iso8859_4 28594 ISO-8859-4 iso-8859-4 latin4 L4
|
||||||
|
iso8859_5 28595 ISO-8859-5 iso-8859-5 cyrillic
|
||||||
|
iso8859_6 28596 ISO-8859-6 iso-8859-6 arabic
|
||||||
|
iso8859_7 28597 ISO-8859-7 iso-8859-7 greek greek8
|
||||||
|
iso8859_8 28598 ISO-8859-8 iso-8859-8 hebrew
|
||||||
|
iso8859_9 28599 ISO-8859-9 iso-8859-9 latin5 L5
|
||||||
|
iso8859_10 ISO-8859-10 iso-8859-10 latin6 L6
|
||||||
|
iso8859_11 ISO-8859-11 iso-8859-11 thai
|
||||||
|
iso8859_13 28603 ISO-8859-13 iso-8859-13 latin7 L7
|
||||||
|
iso8859_14 ISO-8859-14 iso-8859-14 latin8 L8
|
||||||
|
iso8859_15 28605 ISO-8859-15 iso-8859-15 latin9 L9
|
||||||
|
iso8859_16 ISO-8859-16 iso-8859-16 latin10 L10
|
||||||
|
johab 1361 JOHAB cp1361 ms1361
|
||||||
|
koi8_r
|
||||||
|
koi8_t KOI8-T
|
||||||
|
koi8_u
|
||||||
|
kz1048 kz_1048 strk1048_2002 rk1048
|
||||||
|
mac_cyrillic 10007 MacCyrillic maccyrillic
|
||||||
|
mac_greek 10006 MacGreek macgreek
|
||||||
|
mac_iceland 10079 MacIceland maciceland
|
||||||
|
mac_latin2 maclatin2 maccentraleurope mac_centeuro
|
||||||
|
mac_roman MacRoman macroman macintosh
|
||||||
|
mac_turkish 10081 MacTurkish macturkish
|
||||||
|
ptcp154 PT154 csptcp154 pt154 cp154 cyrillic-asian
|
||||||
|
shift_jis 932 SHIFT_JIS csshiftjis shiftjis sjis s_jis
|
||||||
|
shift_jis_2004 shiftjis2004 sjis_2004 sjis2004
|
||||||
|
shift_jisx0213 shiftjisx0213 sjisx0213 s_jisx0213
|
||||||
|
utf_32 UTF-32 U32 utf32
|
||||||
|
utf_32_be UTF-32BE UTF-32BE
|
||||||
|
utf_32_le UTF-32LE UTF-32LE
|
||||||
|
utf_16 UTF16 U16 utf16
|
||||||
|
utf_16_be UTF-16BE UTF-16BE
|
||||||
|
utf_16_le UTF-16LE UTF-16LE
|
||||||
|
utf_7 65000 UTF-7 U7 unicode-1-1-utf-7
|
||||||
|
utf_8 65001 UTF-8 U8 UTF utf8 utf-8 cp65001
|
||||||
|
utf_8_sig
|
||||||
|
7
script/pyproject.toml
Normal file
7
script/pyproject.toml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[project]
|
||||||
|
name = "script"
|
||||||
|
version = "0.1.0"
|
||||||
|
requires-python = ">=3.11"
|
||||||
|
dependencies = [
|
||||||
|
"jinja2==3.1.2",
|
||||||
|
]
|
||||||
74
script/uv.lock
generated
Normal file
74
script/uv.lock
generated
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
version = 1
|
||||||
|
revision = 2
|
||||||
|
requires-python = ">=3.11"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jinja2"
|
||||||
|
version = "3.1.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "markupsafe" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/7a/ff/75c28576a1d900e87eb6335b063fab47a8ef3c8b4d88524c4bf78f670cce/Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852", size = 268239, upload-time = "2022-04-28T17:21:27.579Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bc/c3/f068337a370801f372f2f8f6bad74a5c140f6fda3d9de154052708dd3c65/Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61", size = 133101, upload-time = "2022-04-28T17:21:25.336Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "markupsafe"
|
||||||
|
version = "3.0.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353, upload-time = "2024-10-18T15:21:02.187Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392, upload-time = "2024-10-18T15:21:02.941Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984, upload-time = "2024-10-18T15:21:03.953Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120, upload-time = "2024-10-18T15:21:06.495Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032, upload-time = "2024-10-18T15:21:07.295Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057, upload-time = "2024-10-18T15:21:08.073Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359, upload-time = "2024-10-18T15:21:09.318Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306, upload-time = "2024-10-18T15:21:10.185Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094, upload-time = "2024-10-18T15:21:11.005Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521, upload-time = "2024-10-18T15:21:12.911Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274, upload-time = "2024-10-18T15:21:13.777Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348, upload-time = "2024-10-18T15:21:14.822Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149, upload-time = "2024-10-18T15:21:15.642Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118, upload-time = "2024-10-18T15:21:17.133Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993, upload-time = "2024-10-18T15:21:18.064Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178, upload-time = "2024-10-18T15:21:18.859Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319, upload-time = "2024-10-18T15:21:19.671Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352, upload-time = "2024-10-18T15:21:20.971Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097, upload-time = "2024-10-18T15:21:22.646Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601, upload-time = "2024-10-18T15:21:23.499Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "script"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = { virtual = "." }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "jinja2" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.metadata]
|
||||||
|
requires-dist = [{ name = "jinja2", specifier = "==3.1.2" }]
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
@ECHO OFF
|
|
||||||
:: Check environment
|
|
||||||
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 main binary directory
|
|
||||||
MKDIR bin
|
|
||||||
CD bin
|
|
||||||
:: Create build folder
|
|
||||||
MKDIR Win32
|
|
||||||
MKDIR x64
|
|
||||||
MKDIR documentation
|
|
||||||
:: Create install folder
|
|
||||||
MKDIR install
|
|
||||||
CD install
|
|
||||||
MKDIR Win32_Debug
|
|
||||||
MKDIR Win32_Release
|
|
||||||
MKDIR x64_Debug
|
|
||||||
MKDIR x64_Release
|
|
||||||
CD ..
|
|
||||||
|
|
||||||
:: 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/Win32_Debug --config Debug
|
|
||||||
cmake --build . --config Release
|
|
||||||
cmake --install . --prefix=../install/Win32_Release --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/x64_Debug --config Debug
|
|
||||||
cmake --build . --config Release
|
|
||||||
cmake --install . --prefix=../install/x64_Release --config Release
|
|
||||||
CD ..
|
|
||||||
|
|
||||||
:: Build for documentation
|
|
||||||
IF NOT "%1"=="NODOC" (
|
|
||||||
CD documentation
|
|
||||||
cmake -G "Visual Studio 16 2019" -A x64 -DYYCC_BUILD_DOC=ON ../..
|
|
||||||
cmake --build . --config Release
|
|
||||||
cmake --build . --target YYCCDocumentation
|
|
||||||
cmake --install . --prefix=../install/x64_Release --config Release
|
|
||||||
CD ..
|
|
||||||
)
|
|
||||||
|
|
||||||
:: Exit to original path
|
|
||||||
CD ..
|
|
||||||
ECHO Windows CMake Build Done
|
|
||||||
@@ -1,52 +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
|
|
||||||
)
|
|
||||||
|
|
||||||
:: Enter main binary directory
|
|
||||||
CD bin
|
|
||||||
|
|
||||||
:: Create MSVC binary directory
|
|
||||||
MKDIR msvc_install
|
|
||||||
CD msvc_install
|
|
||||||
:: Create direcotries tree
|
|
||||||
MKDIR bin
|
|
||||||
MKDIR include
|
|
||||||
MKDIR lib
|
|
||||||
MKDIR share
|
|
||||||
CD bin
|
|
||||||
MKDIR Win32
|
|
||||||
MKDIR x64
|
|
||||||
CD ..
|
|
||||||
CD lib
|
|
||||||
MKDIR Win32\Debug
|
|
||||||
MKDIR Win32\Release
|
|
||||||
MKDIR x64\Debug
|
|
||||||
MKDIR x64\Release
|
|
||||||
CD ..
|
|
||||||
:: Exit MSVC binary directory
|
|
||||||
CD ..
|
|
||||||
|
|
||||||
:: Copy result
|
|
||||||
:: Copy include from x64_Release build
|
|
||||||
XCOPY install\x64_Release\include msvc_install\include\ /E /Y
|
|
||||||
:: Copy document from x64_Release build
|
|
||||||
IF NOT "%1"=="NODOC" (
|
|
||||||
XCOPY install\x64_Release\share msvc_install\share\ /E /Y
|
|
||||||
)
|
|
||||||
:: Copy binary testbench
|
|
||||||
COPY install\Win32_Release\bin\YYCCTestbench.exe msvc_install\bin\Win32\YYCCTestbench.exe /Y
|
|
||||||
COPY install\x64_Release\bin\YYCCTestbench.exe msvc_install\bin\x64\YYCCTestbench.exe /Y
|
|
||||||
:: Copy static library
|
|
||||||
COPY install\Win32_Debug\lib\YYCCommonplace.lib msvc_install\lib\Win32\Debug\YYCCommonplace.lib /Y
|
|
||||||
COPY install\Win32_Release\lib\YYCCommonplace.lib msvc_install\lib\Win32\Release\YYCCommonplace.lib /Y
|
|
||||||
COPY install\x64_Debug\lib\YYCCommonplace.lib msvc_install\lib\x64\Debug\YYCCommonplace.lib /Y
|
|
||||||
COPY install\x64_Release\lib\YYCCommonplace.lib msvc_install\lib\x64\Release\YYCCommonplace.lib /Y
|
|
||||||
|
|
||||||
:: Exit to original path
|
|
||||||
CD ..
|
|
||||||
ECHO Windows MSVC Build Done
|
|
||||||
@@ -1,348 +0,0 @@
|
|||||||
#include "ArgParser.hpp"
|
|
||||||
|
|
||||||
#include "EncodingHelper.hpp"
|
|
||||||
#include "ConsoleHelper.hpp"
|
|
||||||
|
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
|
||||||
#include "WinImportPrefix.hpp"
|
|
||||||
#include <Windows.h>
|
|
||||||
#include <shellapi.h>
|
|
||||||
#include <processenv.h>
|
|
||||||
#include "WinImportSuffix.hpp"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace YYCC::ArgParser {
|
|
||||||
|
|
||||||
#pragma region Arguments List
|
|
||||||
|
|
||||||
ArgumentList ArgumentList::CreateFromStd(int argc, char* argv[]) {
|
|
||||||
std::vector<yycc_u8string> args;
|
|
||||||
for (int i = 1; i < argc; ++i) { // starts with 1 to remove first part (executable self)
|
|
||||||
if (argv[i] != nullptr)
|
|
||||||
args.emplace_back(yycc_u8string(YYCC::EncodingHelper::ToUTF8(argv[i])));
|
|
||||||
}
|
|
||||||
return ArgumentList(std::move(args));
|
|
||||||
}
|
|
||||||
|
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
|
||||||
ArgumentList ArgumentList::CreateFromWin32() {
|
|
||||||
// Reference: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw
|
|
||||||
|
|
||||||
// prepare list
|
|
||||||
std::vector<yycc_u8string> args;
|
|
||||||
|
|
||||||
// try fetching from Win32 functions
|
|
||||||
int argc;
|
|
||||||
LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc);
|
|
||||||
if (argv != NULL) {
|
|
||||||
for (int i = 1; i < argc; ++i) { // starts with 1 to remove first part (executable self)
|
|
||||||
if (argv[i] != nullptr) {
|
|
||||||
yycc_u8string u8_argv;
|
|
||||||
if (YYCC::EncodingHelper::WcharToUTF8(argv[i], u8_argv))
|
|
||||||
args.emplace_back(std::move(u8_argv));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LocalFree(argv);
|
|
||||||
|
|
||||||
// return result
|
|
||||||
return ArgumentList(std::move(args));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ArgumentList::ArgumentList(std::vector<yycc_u8string>&& arguments) :
|
|
||||||
m_Arguments(arguments), m_ArgumentsCursor(0u) {}
|
|
||||||
|
|
||||||
void ArgumentList::Prev() {
|
|
||||||
if (m_ArgumentsCursor == 0u)
|
|
||||||
throw std::runtime_error("attempt to move on the head of iterator.");
|
|
||||||
--m_ArgumentsCursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ArgumentList::Next() {
|
|
||||||
if (IsEOF()) throw std::runtime_error("attempt to move on the tail of iterator.");
|
|
||||||
++m_ArgumentsCursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
const yycc_u8string& ArgumentList::Argument() const {
|
|
||||||
if (IsEOF()) throw std::runtime_error("attempt to get data on the tail of iterator.");
|
|
||||||
return m_Arguments[m_ArgumentsCursor];
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ArgumentList::IsSwitch(bool* is_long_name, yycc_u8string* long_name, yycc_char8_t* short_name) const {
|
|
||||||
// check eof first
|
|
||||||
if (IsEOF()) throw std::runtime_error("attempt to fetch data on the tail of iterator.");
|
|
||||||
// check long name first, then check short name
|
|
||||||
if (IsLongNameSwitch(long_name)) {
|
|
||||||
if (is_long_name != nullptr) *is_long_name = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (IsShortNameSwitch(short_name)) {
|
|
||||||
if (is_long_name != nullptr) *is_long_name = false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// not matched
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool ArgumentList::IsLongNameSwitch(yycc_u8string* name_part) const {
|
|
||||||
// fetch current parameter
|
|
||||||
if (IsEOF()) throw std::runtime_error("attempt to fetch data on the tail of iterator.");
|
|
||||||
const yycc_u8string& param = m_Arguments[m_ArgumentsCursor];
|
|
||||||
// find double slash
|
|
||||||
if (param.find(AbstractArgument::DOUBLE_DASH) != 0u) return false;
|
|
||||||
// check gotten long name
|
|
||||||
yycc_u8string_view long_name = yycc_u8string_view(param).substr(2u);
|
|
||||||
if (!AbstractArgument::IsLegalLongName(long_name)) return false;
|
|
||||||
// set checked long name if possible and return
|
|
||||||
if (name_part != nullptr)
|
|
||||||
*name_part = long_name;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool ArgumentList::IsShortNameSwitch(yycc_char8_t* name_part) const {
|
|
||||||
// fetch current parameter
|
|
||||||
if (IsEOF()) throw std::runtime_error("attempt to fetch data on the tail of iterator.");
|
|
||||||
const yycc_u8string& param = m_Arguments[m_ArgumentsCursor];
|
|
||||||
// if the length is not exactly equal to 2,
|
|
||||||
// or it not starts with dash,
|
|
||||||
// it is impossible a short name
|
|
||||||
if (param.size() != 2u || param[0] != AbstractArgument::DASH) return false;
|
|
||||||
// check gotten short name
|
|
||||||
yycc_char8_t short_name = param[1];
|
|
||||||
if (!AbstractArgument::IsLegalShortName(short_name)) return false;
|
|
||||||
// set checked short name if possible and return
|
|
||||||
if (name_part != nullptr)
|
|
||||||
*name_part = short_name;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ArgumentList::IsValue(yycc_u8string* val) const {
|
|
||||||
bool is_value = !IsSwitch();
|
|
||||||
if (is_value && val != nullptr)
|
|
||||||
*val = m_Arguments[m_ArgumentsCursor];
|
|
||||||
return is_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ArgumentList::IsEOF() const { return m_ArgumentsCursor >= m_Arguments.size(); }
|
|
||||||
|
|
||||||
void ArgumentList::Reset() { m_ArgumentsCursor = 0u; }
|
|
||||||
|
|
||||||
#pragma endregion
|
|
||||||
|
|
||||||
#pragma region Abstract Argument
|
|
||||||
|
|
||||||
const yycc_u8string AbstractArgument::DOUBLE_DASH = YYCC_U8("--");
|
|
||||||
const yycc_char8_t AbstractArgument::DASH = YYCC_U8_CHAR('-');
|
|
||||||
const yycc_char8_t AbstractArgument::NO_SHORT_NAME = YYCC_U8_CHAR(0);
|
|
||||||
const yycc_char8_t AbstractArgument::MIN_SHORT_NAME = YYCC_U8_CHAR('!');
|
|
||||||
const yycc_char8_t AbstractArgument::MAX_SHORT_NAME = YYCC_U8_CHAR('~');
|
|
||||||
|
|
||||||
bool AbstractArgument::IsLegalShortName(yycc_char8_t short_name) {
|
|
||||||
if (short_name == AbstractArgument::DASH || // dash is not allowed
|
|
||||||
short_name < AbstractArgument::MIN_SHORT_NAME || short_name > AbstractArgument::MAX_SHORT_NAME) { // non-display ASCII chars are not allowed
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// okey
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool AbstractArgument::IsLegalLongName(const yycc_u8string_view& long_name) {
|
|
||||||
// empty is not allowed
|
|
||||||
if (long_name.empty()) return false;
|
|
||||||
// non-display ASCII chars are not allowed
|
|
||||||
for (const auto& val : long_name) {
|
|
||||||
if (val < AbstractArgument::MIN_SHORT_NAME || val > AbstractArgument::MAX_SHORT_NAME)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// okey
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
AbstractArgument::AbstractArgument(
|
|
||||||
const yycc_char8_t* long_name, yycc_char8_t short_name,
|
|
||||||
const yycc_char8_t* description, const yycc_char8_t* argument_example,
|
|
||||||
bool is_optional) :
|
|
||||||
m_LongName(), m_ShortName(AbstractArgument::NO_SHORT_NAME), m_Description(), m_ArgumentExample(),
|
|
||||||
m_IsOptional(is_optional), m_IsCaptured(false) {
|
|
||||||
|
|
||||||
// try to assign long name and check it
|
|
||||||
if (long_name != nullptr) {
|
|
||||||
m_LongName = long_name;
|
|
||||||
if (!AbstractArgument::IsLegalLongName(m_LongName))
|
|
||||||
throw std::invalid_argument("Given long name is invalid.");
|
|
||||||
}
|
|
||||||
// try to assign short name and check it
|
|
||||||
if (short_name != AbstractArgument::NO_SHORT_NAME) {
|
|
||||||
m_ShortName = short_name;
|
|
||||||
if (!AbstractArgument::IsLegalShortName(m_ShortName))
|
|
||||||
throw std::invalid_argument("Given short name is invalid.");
|
|
||||||
}
|
|
||||||
// check short name and long name existence
|
|
||||||
if (!HasShortName() && !HasLongName())
|
|
||||||
throw std::invalid_argument("you must specify an one of long name or short name.");
|
|
||||||
|
|
||||||
// try to assign other string values
|
|
||||||
if (description != nullptr) m_Description = description;
|
|
||||||
if (argument_example != nullptr) m_ArgumentExample = argument_example;
|
|
||||||
}
|
|
||||||
|
|
||||||
AbstractArgument::~AbstractArgument() {}
|
|
||||||
|
|
||||||
bool AbstractArgument::HasLongName() const { return !m_LongName.empty(); }
|
|
||||||
const yycc_u8string& AbstractArgument::GetLongName() const { return m_LongName; }
|
|
||||||
bool AbstractArgument::HasShortName() const { return m_ShortName != NO_SHORT_NAME; }
|
|
||||||
yycc_char8_t AbstractArgument::GetShortName() const { return m_ShortName; }
|
|
||||||
bool AbstractArgument::HasDescription() const { return !m_Description.empty(); }
|
|
||||||
const yycc_u8string& AbstractArgument::GetDescription() const { return m_Description; }
|
|
||||||
bool AbstractArgument::HasArgumentExample() const { return !m_ArgumentExample.empty(); }
|
|
||||||
const yycc_u8string& AbstractArgument::GetArgumentExample() const { return m_ArgumentExample; }
|
|
||||||
bool AbstractArgument::IsOptional() const { return m_IsOptional; }
|
|
||||||
|
|
||||||
bool AbstractArgument::IsCaptured() const { return m_IsCaptured; }
|
|
||||||
void AbstractArgument::SetCaptured(bool is_captured) { m_IsCaptured = is_captured; }
|
|
||||||
|
|
||||||
#pragma endregion
|
|
||||||
|
|
||||||
#pragma region Option Context
|
|
||||||
|
|
||||||
OptionContext::OptionContext(
|
|
||||||
const yycc_char8_t* summary, const yycc_char8_t* description,
|
|
||||||
std::initializer_list<AbstractArgument*> arguments) :
|
|
||||||
m_Summary(), m_Description() {
|
|
||||||
// assign summary and description
|
|
||||||
if (summary != nullptr) m_Summary = summary;
|
|
||||||
if (description != nullptr) m_Description = description;
|
|
||||||
|
|
||||||
// insert argument list and check them
|
|
||||||
for (auto* arg : arguments) {
|
|
||||||
// insert into long name map if necessary
|
|
||||||
if (arg->HasLongName()) {
|
|
||||||
auto result = m_LongNameMap.try_emplace(arg->GetLongName(), arg);
|
|
||||||
if (!result.second) throw std::invalid_argument("duplicated long name!");
|
|
||||||
}
|
|
||||||
// insert into short name map if necessary
|
|
||||||
if (arg->HasShortName()) {
|
|
||||||
auto result = m_ShortNameMap.try_emplace(arg->GetShortName(), arg);
|
|
||||||
if (!result.second) throw std::invalid_argument("duplicated short name!");
|
|
||||||
}
|
|
||||||
// insert into argument list
|
|
||||||
m_Arguments.emplace_back(arg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OptionContext::~OptionContext() {}
|
|
||||||
|
|
||||||
bool OptionContext::Parse(ArgumentList& al) {
|
|
||||||
// reset argument list first
|
|
||||||
al.Reset();
|
|
||||||
|
|
||||||
// prepare variables and start loop
|
|
||||||
yycc_u8string long_name;
|
|
||||||
yycc_char8_t short_name;
|
|
||||||
bool is_long_name;
|
|
||||||
while (!al.IsEOF()) {
|
|
||||||
// if we can not find any switches, return with error
|
|
||||||
if (!al.IsSwitch(&is_long_name, &long_name, &short_name)) return false;
|
|
||||||
|
|
||||||
// find corresponding argument by long name or short name.
|
|
||||||
// if we can not find it, return with error.
|
|
||||||
AbstractArgument* arg;
|
|
||||||
if (is_long_name) {
|
|
||||||
auto finder = m_LongNameMap.find(long_name);
|
|
||||||
if (finder == m_LongNameMap.end()) return false;
|
|
||||||
arg = finder->second;
|
|
||||||
} else {
|
|
||||||
auto finder = m_ShortNameMap.find(short_name);
|
|
||||||
if (finder == m_ShortNameMap.end()) return false;
|
|
||||||
arg = finder->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if this argument has been captured, raise error
|
|
||||||
if (arg->IsCaptured()) return false;
|
|
||||||
// call user parse function of found argument
|
|
||||||
if (arg->Parse(al)) {
|
|
||||||
// success. mark it is captured
|
|
||||||
arg->SetCaptured(true);
|
|
||||||
} else {
|
|
||||||
// failed, return error
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// move to next argument
|
|
||||||
al.Next();
|
|
||||||
}
|
|
||||||
|
|
||||||
// after processing all argument,
|
|
||||||
// we should check whether all non-optional argument are captured.
|
|
||||||
for (const auto* arg : m_Arguments) {
|
|
||||||
if (!arg->IsOptional() && !arg->IsCaptured())
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// okey
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OptionContext::Reset() {
|
|
||||||
for (auto* arg : m_Arguments) {
|
|
||||||
// clear user data and unset captured
|
|
||||||
arg->Reset();
|
|
||||||
arg->SetCaptured(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OptionContext::Help() const {
|
|
||||||
// print summary and description if necessary
|
|
||||||
if (!m_Summary.empty())
|
|
||||||
YYCC::ConsoleHelper::WriteLine(m_Summary.c_str());
|
|
||||||
if (!m_Description.empty())
|
|
||||||
YYCC::ConsoleHelper::WriteLine(m_Description.c_str());
|
|
||||||
|
|
||||||
// blank line
|
|
||||||
YYCC::ConsoleHelper::WriteLine(YYCC_U8(""));
|
|
||||||
|
|
||||||
// print argument list
|
|
||||||
for (const auto* arg : m_Arguments) {
|
|
||||||
yycc_u8string argstr;
|
|
||||||
|
|
||||||
// print indent
|
|
||||||
argstr += YYCC_U8("\t");
|
|
||||||
// print optional head
|
|
||||||
bool is_optional = arg->IsOptional();
|
|
||||||
if (is_optional) argstr += YYCC_U8("[");
|
|
||||||
|
|
||||||
// switch name
|
|
||||||
bool short_name = arg->HasShortName(), long_name = arg->HasLongName();
|
|
||||||
if (short_name) {
|
|
||||||
argstr += YYCC_U8("-");
|
|
||||||
argstr += arg->GetShortName();
|
|
||||||
}
|
|
||||||
if (long_name) {
|
|
||||||
if (short_name) argstr += YYCC_U8(", ");
|
|
||||||
argstr += YYCC_U8("--");
|
|
||||||
argstr += arg->GetLongName();
|
|
||||||
}
|
|
||||||
|
|
||||||
// argument example
|
|
||||||
if (arg->HasArgumentExample()) {
|
|
||||||
argstr += YYCC_U8(" ");
|
|
||||||
argstr += arg->GetArgumentExample();
|
|
||||||
}
|
|
||||||
|
|
||||||
// optional tail
|
|
||||||
if (is_optional) argstr += YYCC_U8("]");
|
|
||||||
|
|
||||||
// argument description
|
|
||||||
if (arg->HasDescription()) {
|
|
||||||
// eol and double indent
|
|
||||||
argstr += YYCC_U8("\n\t\t");
|
|
||||||
// description
|
|
||||||
argstr += arg->GetDescription();
|
|
||||||
}
|
|
||||||
|
|
||||||
// write into console
|
|
||||||
YYCC::ConsoleHelper::WriteLine(argstr.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma endregion
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,464 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "YYCCInternal.hpp"
|
|
||||||
|
|
||||||
#include "Constraints.hpp"
|
|
||||||
#include "EncodingHelper.hpp"
|
|
||||||
#include "ParserHelper.hpp"
|
|
||||||
#include <functional>
|
|
||||||
#include <vector>
|
|
||||||
#include <map>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Universal argument parser.
|
|
||||||
* @details
|
|
||||||
* For how to use this namespace, please see \ref arg_parser.
|
|
||||||
*/
|
|
||||||
namespace YYCC::ArgParser {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The advanced wrapper of the list containing command line arguments.
|
|
||||||
* @details
|
|
||||||
* This class is used by OptionContext and argument class internally for convenience.
|
|
||||||
* It should not be constrcuted directly.
|
|
||||||
* Programmer should choose proper static creation function to create instance of this class.
|
|
||||||
*/
|
|
||||||
class ArgumentList {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Create argument list from the parameters of standard C main function.
|
|
||||||
* @param[in] argc The argument count passed to standard C main function.
|
|
||||||
* @param[in] argv The argument value passed to standard C main function.
|
|
||||||
* @return Extracted argument list instance.
|
|
||||||
* @remarks
|
|
||||||
* First item in command line will be stripped,
|
|
||||||
* because in most cases it points to executable self
|
|
||||||
* and should not be seen as a part of arguments.
|
|
||||||
*/
|
|
||||||
static ArgumentList CreateFromStd(int argc, char* argv[]);
|
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
|
||||||
/**
|
|
||||||
* @brief Create argument list from Win32 function.
|
|
||||||
* @details
|
|
||||||
* @return Extracted argument list instance.
|
|
||||||
* @remarks
|
|
||||||
* First item in command line will be stripped,
|
|
||||||
* because in most cases it points to executable self
|
|
||||||
* and should not be seen as a part of arguments.
|
|
||||||
* \par
|
|
||||||
* Programmer should use this function instead of CreateFromStd(),
|
|
||||||
* because that function involve encoding issue on Windows, especially command line including non-ASCII chars.
|
|
||||||
* Only this function guaranteen that return correct argument list on Windows.
|
|
||||||
*/
|
|
||||||
static ArgumentList CreateFromWin32();
|
|
||||||
#endif
|
|
||||||
private:
|
|
||||||
/**
|
|
||||||
* @brief Constructor of ArgumentList used internally.
|
|
||||||
* @param[in] arguments
|
|
||||||
* Underlying argument list.
|
|
||||||
* This argument list should remove first executable name before passing it to there.
|
|
||||||
*/
|
|
||||||
ArgumentList(std::vector<yycc_u8string>&& arguments);
|
|
||||||
public:
|
|
||||||
YYCC_DEF_CLS_COPY_MOVE(ArgumentList);
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Move to previous argument.
|
|
||||||
* @exception std::runtime_error Try moving at the head of argument list.
|
|
||||||
*/
|
|
||||||
void Prev();
|
|
||||||
/**
|
|
||||||
* @brief Move to next argument.
|
|
||||||
* @exception std::runtime_error Try moving at the tail of argument list.
|
|
||||||
*/
|
|
||||||
void Next();
|
|
||||||
/**
|
|
||||||
* @brief Get the string of current argument.
|
|
||||||
* @exception std::runtime_error Try fetching data at the tail of argument list.
|
|
||||||
* @return The constant reference to the string of current argument.
|
|
||||||
*/
|
|
||||||
const yycc_u8string& Argument() const;
|
|
||||||
/**
|
|
||||||
* @brief Check whether current argument is a option / switch.
|
|
||||||
* @param[out] is_long_name
|
|
||||||
* It will be set true if this argument is long name, otherwise short name.
|
|
||||||
* nullptr if you don't want to receive this infomation.
|
|
||||||
* @param[out] long_name
|
|
||||||
* The container holding matched long name if it is (double dash stripped).
|
|
||||||
* nullptr if you don't want to receive this infomation.
|
|
||||||
* @param[out] short_name
|
|
||||||
* The variable holding matched short name if it is (dash stripped).
|
|
||||||
* nullptr if you don't want to receive this infomation.
|
|
||||||
* @exception std::runtime_error Try fetching data at the tail of argument list.
|
|
||||||
* @return
|
|
||||||
* True if it is, otherwise false.
|
|
||||||
* If this function return false, all given parameters are in undefined status.
|
|
||||||
*/
|
|
||||||
bool IsSwitch(
|
|
||||||
bool* is_long_name = nullptr,
|
|
||||||
yycc_u8string* long_name = nullptr,
|
|
||||||
yycc_char8_t* short_name = nullptr) const;
|
|
||||||
/**
|
|
||||||
* @brief Check whether current argument is a value.
|
|
||||||
* @param[out] val
|
|
||||||
* The variable holding value if it is.
|
|
||||||
* nullptr if you don't want to receive this infomation.
|
|
||||||
* @exception std::runtime_error Try fetching data at the tail of argument list.
|
|
||||||
* @return True if it is, otherwise false.
|
|
||||||
*/
|
|
||||||
bool IsValue(yycc_u8string* val = nullptr) const;
|
|
||||||
/**
|
|
||||||
* @brief Check whether we are at the tail of argument list.
|
|
||||||
* @details
|
|
||||||
* Please note EOF is a special state that you can not fetch data from it.
|
|
||||||
* EOF is the next element of the last element of argument list.
|
|
||||||
* It more like \c end() in most C++ container.
|
|
||||||
* @return True if it is, otherwise false.
|
|
||||||
*/
|
|
||||||
bool IsEOF() const;
|
|
||||||
/**
|
|
||||||
* @brief Reset cursor to the head of argument list for reuse.
|
|
||||||
*/
|
|
||||||
void Reset();
|
|
||||||
private:
|
|
||||||
/**
|
|
||||||
* @brief Check whether current argument is long name option / switch.
|
|
||||||
* @details This function is used by IsSwitch() internally.
|
|
||||||
* @param[out] name_part
|
|
||||||
* The container holding matched long name if it is (double dash stripped).
|
|
||||||
* nullptr if you don't want to receive this infomation.
|
|
||||||
* @return True if it is, otherwise false.
|
|
||||||
*/
|
|
||||||
bool IsLongNameSwitch(yycc_u8string* name_part = nullptr) const;
|
|
||||||
/**
|
|
||||||
* @brief Check whether current argument is short name option / switch.
|
|
||||||
* @details This function is used by IsSwitch() internally.
|
|
||||||
* @param[out] name_part
|
|
||||||
* The variable holding matched short name if it is (dash stripped).
|
|
||||||
* nullptr if you don't want to receive this infomation.
|
|
||||||
* @return True if it is, otherwise false.
|
|
||||||
*/
|
|
||||||
bool IsShortNameSwitch(yycc_char8_t* name_part = nullptr) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<yycc_u8string> m_Arguments;
|
|
||||||
size_t m_ArgumentsCursor;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The base class of every argument.
|
|
||||||
* @details Programmer can inherit this class and implement essential functions to create custom argument.
|
|
||||||
*/
|
|
||||||
class AbstractArgument {
|
|
||||||
friend class OptionContext;
|
|
||||||
|
|
||||||
// Long name and short name constants and checker.
|
|
||||||
public:
|
|
||||||
static const yycc_u8string DOUBLE_DASH; ///< The constant value representing double dash (\c --)
|
|
||||||
static const yycc_char8_t DASH; ///< The constant value representing dash (\c -)
|
|
||||||
static const yycc_char8_t NO_SHORT_NAME; ///< The constant value representing that there is not short value.
|
|
||||||
static const yycc_char8_t MIN_SHORT_NAME; ///< The constant value representing the minimum value of valid ASCII chars in short and long name.
|
|
||||||
static const yycc_char8_t MAX_SHORT_NAME; ///< The constant value representing the maximum value of valid ASCII chars in short and long name.
|
|
||||||
/**
|
|
||||||
* @brief Check whether given short name is valid.
|
|
||||||
* @details
|
|
||||||
* An ASCII code of valid short name
|
|
||||||
* should not lower than #MIN_SHORT_NAME or higher than #MAX_SHORT_NAME.
|
|
||||||
* It also can not be #DASH.
|
|
||||||
* @param[in] short_name Short name for checking.
|
|
||||||
* @return True if it is valid, otherwise false.
|
|
||||||
*/
|
|
||||||
static bool IsLegalShortName(yycc_char8_t short_name);
|
|
||||||
/**
|
|
||||||
* @brief Check whether given long name is valid.
|
|
||||||
* @details
|
|
||||||
* An ASCII code of every item in valid long name
|
|
||||||
* should not lower than #MIN_SHORT_NAME or higher than #MAX_SHORT_NAME.
|
|
||||||
* However it can be #DASH. This is different with short name.
|
|
||||||
* @param[in] long_name Long name for checking.
|
|
||||||
* @return True if it is valid, otherwise false.
|
|
||||||
*/
|
|
||||||
static bool IsLegalLongName(const yycc_u8string_view& long_name);
|
|
||||||
|
|
||||||
// Constructor & destructor
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructor an argument
|
|
||||||
* @param[in] long_name The long name of this argument. nullptr if no long name.
|
|
||||||
* @param[in] short_name The short name of this argument. #NO_SHORT_NAME if no short name.
|
|
||||||
* @param[in] description The description of this argument to indroduce what this argument does. nullptr if no description.
|
|
||||||
* @param[in] argument_example The example string of this argument's value. nullptr if no example.
|
|
||||||
* @param[in] is_optional
|
|
||||||
* True if this argument is optional argument.
|
|
||||||
* Optional argument can be absent in argument list.
|
|
||||||
* Non-optional argument must be presented in argument list,
|
|
||||||
* otherwise parser will fail.
|
|
||||||
* @exception std::invalid_argument Given short name or long name are invalid.
|
|
||||||
*/
|
|
||||||
AbstractArgument(
|
|
||||||
const yycc_char8_t* long_name, yycc_char8_t short_name = AbstractArgument::NO_SHORT_NAME,
|
|
||||||
const yycc_char8_t* description = nullptr, const yycc_char8_t* argument_example = nullptr,
|
|
||||||
bool is_optional = false);
|
|
||||||
virtual ~AbstractArgument();
|
|
||||||
YYCC_DEL_CLS_COPY_MOVE(AbstractArgument);
|
|
||||||
|
|
||||||
// ===== User Implementation =====
|
|
||||||
protected:
|
|
||||||
/**
|
|
||||||
* @brief User implemented custom parse function
|
|
||||||
* @param[in] al The argument list for parsing.
|
|
||||||
* @return True if parse is success, otherwise false.
|
|
||||||
* @remarks
|
|
||||||
* When enter this function, argument list points to switch self.
|
|
||||||
* After success parsing, you should point it to the argument this function last accepted.
|
|
||||||
* For exmaple, for command line "-i 114514",
|
|
||||||
* when enter this function, this argument list point to "-i",
|
|
||||||
* and you should set it to "114514" when exiting this function.
|
|
||||||
*/
|
|
||||||
virtual bool Parse(ArgumentList& al) = 0;
|
|
||||||
/**
|
|
||||||
* @brief User implemented custom reset function
|
|
||||||
* @remarks
|
|
||||||
* In this function, user should claer its stored value if is has.
|
|
||||||
* You don't need clar capture state. That is done by library self.
|
|
||||||
*/
|
|
||||||
virtual void Reset() = 0;
|
|
||||||
|
|
||||||
// ===== Basic Infos =====
|
|
||||||
public:
|
|
||||||
/// @brief Check whether this argument specify long name.
|
|
||||||
/// @return True if it is, otherwise false.
|
|
||||||
bool HasLongName() const;
|
|
||||||
/// @brief Get specified long name.
|
|
||||||
/// @return Specified long name.
|
|
||||||
const yycc_u8string& GetLongName() const;
|
|
||||||
/// @brief Check whether this argument specify short name.
|
|
||||||
/// @return True if it is, otherwise false.
|
|
||||||
bool HasShortName() const;
|
|
||||||
/// @brief Get specified short name.
|
|
||||||
/// @return Specified short name.
|
|
||||||
yycc_char8_t GetShortName() const;
|
|
||||||
/// @brief Check whether this argument specify description.
|
|
||||||
/// @return True if it is, otherwise false.
|
|
||||||
bool HasDescription() const;
|
|
||||||
/// @brief Get specified description.
|
|
||||||
/// @return Specified description.
|
|
||||||
const yycc_u8string& GetDescription() const;
|
|
||||||
/// @brief Check whether this argument specify example.
|
|
||||||
/// @return True if it is, otherwise false.
|
|
||||||
bool HasArgumentExample() const;
|
|
||||||
/// @brief Get specified example.
|
|
||||||
/// @return Specified example.
|
|
||||||
const yycc_u8string& GetArgumentExample() const;
|
|
||||||
/// @brief Check whether this argument is optional.
|
|
||||||
/// @return True if it is, otherwise false.
|
|
||||||
bool IsOptional() const;
|
|
||||||
private:
|
|
||||||
yycc_u8string m_LongName;
|
|
||||||
yycc_char8_t m_ShortName;
|
|
||||||
yycc_u8string m_Description;
|
|
||||||
yycc_u8string m_ArgumentExample;
|
|
||||||
bool m_IsOptional;
|
|
||||||
|
|
||||||
// ===== Capture State =====
|
|
||||||
public:
|
|
||||||
/// @brief Check whether this argument has been captured.
|
|
||||||
/// @return True if it is, otherwise false.
|
|
||||||
bool IsCaptured() const;
|
|
||||||
private:
|
|
||||||
/**
|
|
||||||
* @brief Set capture state of this argument.
|
|
||||||
* @details This function is used internally by OptionContext.
|
|
||||||
* @param[in] is_captured New states of captured.
|
|
||||||
*/
|
|
||||||
void SetCaptured(bool is_captured);
|
|
||||||
bool m_IsCaptured;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @brief The core of argument parser, also manage all arguments.
|
|
||||||
class OptionContext {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Construct option context.
|
|
||||||
* @param[in] summary The summary of this application which will be printed in help text.
|
|
||||||
* @param[in] description The description of this application which will be printed in help text.
|
|
||||||
* @param[in] arguments The initializer list including pointers to all arguments.
|
|
||||||
*/
|
|
||||||
OptionContext(
|
|
||||||
const yycc_char8_t* summary, const yycc_char8_t* description,
|
|
||||||
std::initializer_list<AbstractArgument*> arguments);
|
|
||||||
~OptionContext();
|
|
||||||
YYCC_DEL_CLS_COPY_MOVE(OptionContext);
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Start a parse.
|
|
||||||
* @param[in] al The reference to ArgumentList for parsing.
|
|
||||||
* @return
|
|
||||||
* True if success, otherwise false.
|
|
||||||
* If this function return false, you should not visit any arguments it managed.
|
|
||||||
*/
|
|
||||||
bool Parse(ArgumentList& al);
|
|
||||||
/**
|
|
||||||
* @brief Reset all managed argument to default state thus you can start another parsing.
|
|
||||||
*/
|
|
||||||
void Reset();
|
|
||||||
/**
|
|
||||||
* @brief Print help text in \c stdout.
|
|
||||||
*/
|
|
||||||
void Help() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
yycc_u8string m_Summary;
|
|
||||||
yycc_u8string m_Description;
|
|
||||||
|
|
||||||
std::vector<AbstractArgument*> m_Arguments;
|
|
||||||
std::map<yycc_u8string, AbstractArgument*> m_LongNameMap;
|
|
||||||
std::map<yycc_char8_t, AbstractArgument*> m_ShortNameMap;
|
|
||||||
};
|
|
||||||
|
|
||||||
#pragma region Argument Presets
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Arithmetic (integral, floating point. except bool) type argument
|
|
||||||
* @tparam _Ty The internal stored type belongs to arithmetic type.
|
|
||||||
*/
|
|
||||||
template<typename _Ty, std::enable_if_t<std::is_arithmetic_v<_Ty> && !std::is_same_v<_Ty, bool>, int> = 0>
|
|
||||||
class NumberArgument : public AbstractArgument {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructor an arithmetic argument
|
|
||||||
* @param[in] long_name The long name of this argument. nullptr if no long name.
|
|
||||||
* @param[in] short_name The short name of this argument. #NO_SHORT_NAME if no short name.
|
|
||||||
* @param[in] description The description of this argument to indroduce what this argument does. nullptr if no description.
|
|
||||||
* @param[in] argument_example The example string of this argument's value. nullptr if no example.
|
|
||||||
* @param[in] is_optional True if this argument is optional argument.
|
|
||||||
* @param[in] constraint The constraint applied to this argument.
|
|
||||||
* @exception std::invalid_argument Given short name or long name are invalid.
|
|
||||||
*/
|
|
||||||
NumberArgument(
|
|
||||||
const yycc_char8_t* long_name, yycc_char8_t short_name,
|
|
||||||
const yycc_char8_t* description = nullptr, const yycc_char8_t* argument_example = nullptr,
|
|
||||||
bool is_optional = false,
|
|
||||||
Constraints::Constraint<_Ty> constraint = Constraints::Constraint<_Ty> {}) :
|
|
||||||
AbstractArgument(long_name, short_name, description, argument_example, is_optional), m_Data(), m_Constraint(constraint) {}
|
|
||||||
virtual ~NumberArgument() {}
|
|
||||||
YYCC_DEL_CLS_COPY_MOVE(NumberArgument);
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// @brief Get stored data in argument.
|
|
||||||
_Ty Get() const {
|
|
||||||
if (!IsCaptured()) throw std::runtime_error("try fetching data from a not captured argument.");
|
|
||||||
return m_Data;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual bool Parse(ArgumentList& al) override {
|
|
||||||
// try get corresponding value
|
|
||||||
yycc_u8string strval;
|
|
||||||
al.Next();
|
|
||||||
if (al.IsEOF() || !al.IsValue(&strval)) {
|
|
||||||
al.Prev();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// try parsing value
|
|
||||||
if (!YYCC::ParserHelper::TryParse<_Ty>(strval, m_Data)) return false;
|
|
||||||
// check constraint
|
|
||||||
if (m_Constraint.IsValid() && !m_Constraint.m_CheckFct(m_Data))
|
|
||||||
return false;
|
|
||||||
// okey
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
virtual void Reset() override {
|
|
||||||
std::memset(&m_Data, 0, sizeof(m_Data));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
_Ty m_Data;
|
|
||||||
Constraints::Constraint<_Ty> m_Constraint;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief A simple switch type argument which do not store any value.
|
|
||||||
*/
|
|
||||||
class SwitchArgument : public AbstractArgument {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructor an switch argument
|
|
||||||
* @param[in] long_name The long name of this argument. nullptr if no long name.
|
|
||||||
* @param[in] short_name The short name of this argument. #NO_SHORT_NAME if no short name.
|
|
||||||
* @param[in] description The description of this argument to indroduce what this argument does. nullptr if no description.
|
|
||||||
* @exception std::invalid_argument Given short name or long name are invalid.
|
|
||||||
*/
|
|
||||||
SwitchArgument(
|
|
||||||
const yycc_char8_t* long_name, yycc_char8_t short_name,
|
|
||||||
const yycc_char8_t* description = nullptr) :
|
|
||||||
// bool switch must be optional, because it is false if no given switch.
|
|
||||||
// bool switch doesn't have argument, so it doesn't have example property.
|
|
||||||
AbstractArgument(long_name, short_name, description, nullptr, true) {}
|
|
||||||
virtual ~SwitchArgument() {}
|
|
||||||
YYCC_DEL_CLS_COPY_MOVE(SwitchArgument);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual bool Parse(ArgumentList& al) override { return true; } // simply return true because no value to store.
|
|
||||||
virtual void Reset() override {} // nothing need to be reset.
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @brief String type argument
|
|
||||||
class StringArgument : public AbstractArgument {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructor a string argument
|
|
||||||
* @param[in] long_name The long name of this argument. nullptr if no long name.
|
|
||||||
* @param[in] short_name The short name of this argument. #NO_SHORT_NAME if no short name.
|
|
||||||
* @param[in] description The description of this argument to indroduce what this argument does. nullptr if no description.
|
|
||||||
* @param[in] argument_example The example string of this argument's value. nullptr if no example.
|
|
||||||
* @param[in] is_optional True if this argument is optional argument.
|
|
||||||
* @param[in] constraint The constraint applied to this argument.
|
|
||||||
* @exception std::invalid_argument Given short name or long name are invalid.
|
|
||||||
*/
|
|
||||||
StringArgument(
|
|
||||||
const yycc_char8_t* long_name, yycc_char8_t short_name,
|
|
||||||
const yycc_char8_t* description = nullptr, const yycc_char8_t* argument_example = nullptr,
|
|
||||||
bool is_optional = false,
|
|
||||||
Constraints::Constraint<yycc_u8string> constraint = Constraints::Constraint<yycc_u8string> {}) :
|
|
||||||
AbstractArgument(long_name, short_name, description, argument_example, is_optional), m_Data(), m_Constraint(constraint) {}
|
|
||||||
virtual ~StringArgument() {}
|
|
||||||
YYCC_DEL_CLS_COPY_MOVE(StringArgument);
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// @brief Get stored data in argument.
|
|
||||||
const yycc_u8string& Get() const {
|
|
||||||
if (!IsCaptured()) throw std::runtime_error("try fetching data from a not captured argument.");
|
|
||||||
return m_Data;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual bool Parse(ArgumentList& al) override {
|
|
||||||
// try get corresponding value
|
|
||||||
al.Next();
|
|
||||||
if (al.IsEOF() || !al.IsValue(&m_Data)) {
|
|
||||||
al.Prev();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// check constraint
|
|
||||||
if (m_Constraint.IsValid() && !m_Constraint.m_CheckFct(m_Data))
|
|
||||||
return false;
|
|
||||||
// okey
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
virtual void Reset() override {
|
|
||||||
m_Data.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
yycc_u8string m_Data;
|
|
||||||
Constraints::Constraint<yycc_u8string> m_Constraint;
|
|
||||||
};
|
|
||||||
|
|
||||||
#pragma endregion
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,48 +1,119 @@
|
|||||||
|
# Configure version file
|
||||||
|
configure_file(
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/yycc/version.hpp.in
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/yycc/version.hpp
|
||||||
|
@ONLY
|
||||||
|
)
|
||||||
|
|
||||||
# Create static library
|
# Create static library
|
||||||
add_library(YYCCommonplace STATIC "")
|
add_library(YYCCommonplace STATIC "")
|
||||||
# Setup static library sources
|
# Setup static library sources
|
||||||
target_sources(YYCCommonplace
|
target_sources(YYCCommonplace
|
||||||
PRIVATE
|
PRIVATE
|
||||||
# Sources
|
# Sources
|
||||||
COMHelper.cpp
|
yycc/string/reinterpret.cpp
|
||||||
ArgParser.cpp
|
yycc/string/op.cpp
|
||||||
ConfigManager.cpp
|
yycc/patch/fopen.cpp
|
||||||
ConsoleHelper.cpp
|
yycc/patch/stream.cpp
|
||||||
DialogHelper.cpp
|
yycc/panic.cpp
|
||||||
EncodingHelper.cpp
|
yycc/env.cpp
|
||||||
ExceptionHelper.cpp
|
yycc/windows/com.cpp
|
||||||
StdPatch.cpp
|
yycc/windows/dialog.cpp
|
||||||
IOHelper.cpp
|
yycc/windows/winfct.cpp
|
||||||
StringHelper.cpp
|
yycc/windows/console.cpp
|
||||||
WinFctHelper.cpp
|
yycc/encoding/stl.cpp
|
||||||
# Natvis (only for MSVC)
|
yycc/encoding/windows.cpp
|
||||||
$<$<CXX_COMPILER_ID:MSVC>:YYCC.natvis>
|
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
|
target_sources(YYCCommonplace
|
||||||
PUBLIC
|
PUBLIC
|
||||||
FILE_SET HEADERS
|
FILE_SET HEADERS
|
||||||
FILES
|
FILES
|
||||||
# Headers
|
# Headers
|
||||||
# Common headers
|
yycc.hpp
|
||||||
Constraints.hpp
|
yycc/version.hpp
|
||||||
COMHelper.hpp
|
yycc/macro/version_cmp.hpp
|
||||||
ArgParser.hpp
|
yycc/macro/os_detector.hpp
|
||||||
ConfigManager.hpp
|
yycc/macro/stl_detector.hpp
|
||||||
ConsoleHelper.hpp
|
yycc/macro/endian_detector.hpp
|
||||||
DialogHelper.hpp
|
yycc/macro/compiler_detector.hpp
|
||||||
EncodingHelper.hpp
|
yycc/macro/ptr_size_detector.hpp
|
||||||
ExceptionHelper.hpp
|
yycc/macro/class_copy_move.hpp
|
||||||
StdPatch.hpp
|
yycc/macro/printf_checker.hpp
|
||||||
IOHelper.hpp
|
yycc/cenum.hpp
|
||||||
ParserHelper.hpp
|
yycc/string.hpp
|
||||||
StringHelper.hpp
|
yycc/string/reinterpret.hpp
|
||||||
WinFctHelper.hpp
|
yycc/string/op.hpp
|
||||||
# Windows including guard pair
|
yycc/patch/ptr_pad.hpp
|
||||||
WinImportPrefix.hpp
|
yycc/patch/fopen.hpp
|
||||||
WinImportSuffix.hpp
|
yycc/patch/stream.hpp
|
||||||
# Misc
|
yycc/patch/format.hpp
|
||||||
YYCCInternal.hpp
|
yycc/patch/libcxx/enumerate.hpp
|
||||||
YYCCommonplace.hpp
|
yycc/patch/libcxx/stacktrace.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
|
# Setup header infomations
|
||||||
target_include_directories(YYCCommonplace
|
target_include_directories(YYCCommonplace
|
||||||
@@ -50,30 +121,71 @@ PUBLIC
|
|||||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>"
|
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>"
|
||||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
"$<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
|
# Link with DbgHelp.lib on Windows
|
||||||
target_link_libraries(YYCCommonplace
|
target_link_libraries(YYCCommonplace
|
||||||
PRIVATE
|
PRIVATE
|
||||||
$<$<BOOL:${WIN32}>:DbgHelp.lib>
|
$<$<BOOL:${WIN32}>:DbgHelp.lib>
|
||||||
)
|
)
|
||||||
# Setup C++ standard
|
|
||||||
target_compile_features(YYCCommonplace PUBLIC cxx_std_17)
|
|
||||||
set_target_properties(YYCCommonplace PROPERTIES CXX_EXTENSION OFF)
|
|
||||||
# Setup macros
|
# Setup macros
|
||||||
target_compile_definitions(YYCCommonplace
|
target_compile_definitions(YYCCommonplace
|
||||||
# Debug macro should populate to child projects
|
|
||||||
PUBLIC
|
PUBLIC
|
||||||
$<$<BOOL:${YYCC_DEBUG_UE_FILTER}>:YYCC_DEBUG_UE_FILTER>
|
# Iconv environment macro
|
||||||
# Unicode charset for private using
|
$<$<BOOL:${Iconv_FOUND}>:YYCC_FEAT_ICONV>
|
||||||
PRIVATE
|
# 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>
|
||||||
$<$<CXX_COMPILER_ID:MSVC>:_UNICODE>
|
$<$<CXX_COMPILER_ID:MSVC>:_UNICODE>
|
||||||
|
# Fix MSVC shit
|
||||||
|
$<$<CXX_COMPILER_ID:MSVC>:_CRT_SECURE_NO_WARNINGS>
|
||||||
|
$<$<CXX_COMPILER_ID:MSVC>:_CRT_SECURE_NO_DEPRECATE>
|
||||||
|
$<$<CXX_COMPILER_ID:MSVC>:_CRT_NONSTDC_NO_WARNINGS>
|
||||||
|
$<$<CXX_COMPILER_ID:MSVC>:_CRT_NONSTDC_NO_DEPRECATE>
|
||||||
|
# Fix Windows header file shit
|
||||||
|
$<$<BOOL:${WIN32}>:WIN32_LEAN_AND_MEAN>
|
||||||
|
$<$<BOOL:${WIN32}>:NOMINMAX>
|
||||||
)
|
)
|
||||||
target_compile_options(YYCCommonplace
|
target_compile_options(YYCCommonplace
|
||||||
# Order build as UTF-8 in MSVC
|
PUBLIC
|
||||||
PRIVATE
|
# Order build as UTF-8 in MSVC
|
||||||
$<$<CXX_COMPILER_ID:MSVC>:/utf-8>
|
$<$<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 binary and headers
|
||||||
install(TARGETS YYCCommonplace
|
install(TARGETS YYCCommonplace
|
||||||
EXPORT YYCCommonplaceTargets
|
EXPORT YYCCommonplaceTargets
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
#include "COMHelper.hpp"
|
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
|
||||||
|
|
||||||
namespace YYCC::COMHelper {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The guard for initialize COM environment.
|
|
||||||
* @details This class will try initializing COM environment by calling CoInitialize when constructing,
|
|
||||||
* and it also will try uninitializing COM environment when destructing.
|
|
||||||
* If initialization failed, uninitialization will not be executed.
|
|
||||||
*/
|
|
||||||
class ComGuard {
|
|
||||||
public:
|
|
||||||
ComGuard() : m_HasInit(false) {
|
|
||||||
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
|
|
||||||
if (SUCCEEDED(hr)) m_HasInit = true;
|
|
||||||
}
|
|
||||||
~ComGuard() {
|
|
||||||
if (m_HasInit) {
|
|
||||||
CoUninitialize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsInitialized() const {
|
|
||||||
return m_HasInit;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool m_HasInit;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The instance of COM environment guard.
|
|
||||||
* @details Dialog related function need COM environment,
|
|
||||||
* so we need initializing COM environment when loading this module,
|
|
||||||
* and uninitializing COM environment when we no longer use this module.
|
|
||||||
* So we use a static instance in here.
|
|
||||||
* And make it be const so no one can change it.
|
|
||||||
*/
|
|
||||||
static const ComGuard c_ComGuard {};
|
|
||||||
|
|
||||||
bool IsInitialized() {
|
|
||||||
return c_ComGuard.IsInitialized();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "YYCCInternal.hpp"
|
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "WinImportPrefix.hpp"
|
|
||||||
#include <Windows.h>
|
|
||||||
#include <shlobj_core.h>
|
|
||||||
#include "WinImportSuffix.hpp"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Windows COM related types and checker.
|
|
||||||
* @details
|
|
||||||
* This namespace is Windows specific.
|
|
||||||
* In other platforms, this whole namespace will be unavailable.
|
|
||||||
*
|
|
||||||
* See also \ref com_helper.
|
|
||||||
*/
|
|
||||||
namespace YYCC::COMHelper {
|
|
||||||
|
|
||||||
/// @brief C++ standard deleter for every COM interfaces inheriting IUnknown.
|
|
||||||
class ComPtrDeleter {
|
|
||||||
public:
|
|
||||||
ComPtrDeleter() {}
|
|
||||||
void operator() (IUnknown* com_ptr) {
|
|
||||||
if (com_ptr != nullptr) {
|
|
||||||
com_ptr->Release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @brief Smart unique pointer of \c IFileDialog
|
|
||||||
using SmartIFileDialog = std::unique_ptr<IFileDialog, ComPtrDeleter>;
|
|
||||||
/// @brief Smart unique pointer of \c IFileOpenDialog
|
|
||||||
using SmartIFileOpenDialog = std::unique_ptr<IFileOpenDialog, ComPtrDeleter>;
|
|
||||||
/// @brief Smart unique pointer of \c IShellItem
|
|
||||||
using SmartIShellItem = std::unique_ptr<IShellItem, ComPtrDeleter>;
|
|
||||||
/// @brief Smart unique pointer of \c IShellItemArray
|
|
||||||
using SmartIShellItemArray = std::unique_ptr<IShellItemArray, ComPtrDeleter>;
|
|
||||||
/// @brief Smart unique pointer of \c IShellFolder
|
|
||||||
using SmartIShellFolder = std::unique_ptr<IShellFolder, ComPtrDeleter>;
|
|
||||||
|
|
||||||
/// @brief C++ standard deleter for almost raw pointer used in COM which need to be free by CoTaskMemFree()
|
|
||||||
class CoTaskMemDeleter {
|
|
||||||
public:
|
|
||||||
CoTaskMemDeleter() {}
|
|
||||||
void operator() (void* com_ptr) {
|
|
||||||
if (com_ptr != nullptr) {
|
|
||||||
CoTaskMemFree(com_ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @brief Smart unique pointer of COM created \c WCHAR sequence.
|
|
||||||
using SmartLPWSTR = std::unique_ptr<std::remove_pointer_t<LPWSTR>, CoTaskMemDeleter>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check whether COM environment has been initialized.
|
|
||||||
* @return True if it is, otherwise false.
|
|
||||||
* @remarks
|
|
||||||
* This function will call corresponding function of COM Guard.
|
|
||||||
* Do not remove this function and you must preserve at least one reference to this function in final program.
|
|
||||||
* Some compiler will try to drop COM Guard in final program if no reference to it and it will cause the initialization of COM environment failed.
|
|
||||||
* This is the reason why I order you do the things said above.
|
|
||||||
*/
|
|
||||||
bool IsInitialized();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
#include "ConfigManager.hpp"
|
|
||||||
|
|
||||||
#include "EncodingHelper.hpp"
|
|
||||||
#include "IOHelper.hpp"
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
namespace YYCC::ConfigManager {
|
|
||||||
|
|
||||||
#pragma region Abstract Setting
|
|
||||||
|
|
||||||
AbstractSetting::AbstractSetting(const yycc_u8string_view& name) : m_Name(name), m_RawData() {
|
|
||||||
if (m_Name.empty())
|
|
||||||
throw std::invalid_argument("the name of setting should not be empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
AbstractSetting::~AbstractSetting() {}
|
|
||||||
|
|
||||||
const yycc_u8string& AbstractSetting::GetName() const { return m_Name; }
|
|
||||||
|
|
||||||
void AbstractSetting::ResizeData(size_t new_size) { m_RawData.resize(new_size); }
|
|
||||||
const void* AbstractSetting::GetDataPtr() const { return m_RawData.data(); }
|
|
||||||
void* AbstractSetting::GetDataPtr() { return m_RawData.data(); }
|
|
||||||
size_t AbstractSetting::GetDataSize() const { return m_RawData.size(); }
|
|
||||||
|
|
||||||
#pragma endregion
|
|
||||||
|
|
||||||
#pragma region Core Manager
|
|
||||||
|
|
||||||
CoreManager::CoreManager(
|
|
||||||
const yycc_u8string_view& cfg_file_path,
|
|
||||||
uint64_t version_identifier,
|
|
||||||
std::initializer_list<AbstractSetting*> settings) :
|
|
||||||
m_CfgFilePath(cfg_file_path), m_VersionIdentifier(version_identifier), m_Settings() {
|
|
||||||
// Mark: no need to check cfg file path
|
|
||||||
// it will be checked at creating file handle
|
|
||||||
|
|
||||||
// assign settings
|
|
||||||
for (auto* setting : settings) {
|
|
||||||
auto result = m_Settings.try_emplace(setting->GetName(), setting);
|
|
||||||
if (!result.second) {
|
|
||||||
// if not inserted because duplicated, raise exception
|
|
||||||
throw std::invalid_argument("Duplicated setting name");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CoreManager::Load() {
|
|
||||||
// reset all settings first
|
|
||||||
Reset();
|
|
||||||
|
|
||||||
// get file handle
|
|
||||||
IOHelper::SmartStdFile fs(IOHelper::UTF8FOpen(m_CfgFilePath.c_str(), YYCC_U8("rb")));
|
|
||||||
if (fs.get() == nullptr) {
|
|
||||||
// if we fail to get, it means that we do not have corresponding cfg file.
|
|
||||||
// all settings should be reset to default value.
|
|
||||||
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
|
|
||||||
IOHelper::SmartStdFile fs(IOHelper::UTF8FOpen(m_CfgFilePath.c_str(), YYCC_U8("wb")));
|
|
||||||
// if we fail to get, return false.
|
|
||||||
if (fs == nullptr) return false;
|
|
||||||
|
|
||||||
// write config data
|
|
||||||
uint64_t version_info = m_VersionIdentifier;
|
|
||||||
if (std::fwrite(&version_info, 1u, sizeof(version_info), fs.get()) != sizeof(version_info))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// iterate all data for writing
|
|
||||||
for (const auto& pair : m_Settings) {
|
|
||||||
// do user defined save
|
|
||||||
// if failed, skip this setting
|
|
||||||
if (!pair.second->UserSave())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// write setting name
|
|
||||||
// write name length
|
|
||||||
size_t name_length = pair.first.size();
|
|
||||||
if (std::fwrite(&name_length, 1u, sizeof(name_length), fs.get()) != sizeof(name_length))
|
|
||||||
return false;
|
|
||||||
// write name body
|
|
||||||
if (std::fwrite(pair.first.c_str(), 1u, name_length, fs.get()) != name_length)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// write setting daat
|
|
||||||
// write data length
|
|
||||||
size_t data_length = pair.second->GetDataSize();
|
|
||||||
if (std::fwrite(&data_length, 1u, sizeof(data_length), fs.get()) != sizeof(data_length))
|
|
||||||
return false;
|
|
||||||
// write data body
|
|
||||||
if (std::fwrite(pair.second->GetDataPtr(), 1u, data_length, fs.get()) != data_length)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// all settings done, return true
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreManager::Reset() {
|
|
||||||
for (const auto& pair : m_Settings) {
|
|
||||||
pair.second->UserReset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma endregion
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,256 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "YYCCInternal.hpp"
|
|
||||||
|
|
||||||
#include "Constraints.hpp"
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
#include <map>
|
|
||||||
#include <initializer_list>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <functional>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Universal configuration manager
|
|
||||||
* @details For how to use this namespace, please see \ref config_manager.
|
|
||||||
*/
|
|
||||||
namespace YYCC::ConfigManager {
|
|
||||||
|
|
||||||
/// @brief The base class of every setting.
|
|
||||||
/// @details Programmer can inherit this class and implement essential functions to create custom setting.
|
|
||||||
class AbstractSetting {
|
|
||||||
friend class CoreManager;
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Construct a setting
|
|
||||||
* @param[in] name The name of this setting.
|
|
||||||
* @exception std::invalid_argument Name of setting is empty.
|
|
||||||
*/
|
|
||||||
AbstractSetting(const yycc_u8string_view& name);
|
|
||||||
virtual ~AbstractSetting();
|
|
||||||
YYCC_DEL_CLS_COPY_MOVE(AbstractSetting);
|
|
||||||
|
|
||||||
// Name interface
|
|
||||||
public:
|
|
||||||
/// @brief Get name of this setting.
|
|
||||||
/// @details Name was used in storing setting in file.
|
|
||||||
const yycc_u8string& GetName() const;
|
|
||||||
private:
|
|
||||||
yycc_u8string m_Name;
|
|
||||||
|
|
||||||
// User Implementations
|
|
||||||
protected:
|
|
||||||
/// @brief User implemented custom load function
|
|
||||||
/// @remarks
|
|
||||||
/// In this function, programmer should read data from internal buffer
|
|
||||||
/// and store it to its own another internal variables.
|
|
||||||
/// @return True if success, otherwise false.
|
|
||||||
virtual bool UserLoad() = 0;
|
|
||||||
/// @brief User implemented custom save function
|
|
||||||
/// @remarks
|
|
||||||
/// In this function, programmer should write data,
|
|
||||||
/// which is stored in another variavle by it own, to internal buffer.
|
|
||||||
/// @return True if success, otherwise false.
|
|
||||||
virtual bool UserSave() = 0;
|
|
||||||
/// @brief User implemented custom reset function
|
|
||||||
/// @remarks In this function, programmer should reset its internal variable to default value.
|
|
||||||
virtual void UserReset() = 0;
|
|
||||||
|
|
||||||
// Buffer related functions
|
|
||||||
protected:
|
|
||||||
/// @brief Resize internal buffer to given size.
|
|
||||||
/// @remarks It is usually used in UserSave.
|
|
||||||
/// @param[in] new_size The new size of internal buffer.
|
|
||||||
void ResizeData(size_t new_size);
|
|
||||||
/// @brief Get data pointer to internal buffer.
|
|
||||||
/// @remarks It is usually used in UserLoad.
|
|
||||||
const void* GetDataPtr() const;
|
|
||||||
/// @brief Get mutable data pointer to internal buffer.
|
|
||||||
/// @remarks It is usually used in UserSave.
|
|
||||||
void* GetDataPtr();
|
|
||||||
/// @brief Get the length of internal buffer.
|
|
||||||
size_t GetDataSize() const;
|
|
||||||
private:
|
|
||||||
std::vector<uint8_t> m_RawData;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @brief Settings manager and config file reader writer.
|
|
||||||
class CoreManager {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Build core manager.
|
|
||||||
* @param[in] cfg_file_path The path to config file.
|
|
||||||
* @param[in] version_identifier The identifier of version. Higher is newer. Lower config will try doing migration.
|
|
||||||
* @param[in] settings An initializer list containing pointers to all managed settings.
|
|
||||||
*/
|
|
||||||
CoreManager(
|
|
||||||
const yycc_u8string_view& cfg_file_path,
|
|
||||||
uint64_t version_identifier,
|
|
||||||
std::initializer_list<AbstractSetting*> settings);
|
|
||||||
~CoreManager() {}
|
|
||||||
YYCC_DEL_CLS_COPY_MOVE(CoreManager);
|
|
||||||
|
|
||||||
// Core functions
|
|
||||||
public:
|
|
||||||
/// @brief Load settings from file.
|
|
||||||
/// @details Before loading, all settings will be reset to default value first.
|
|
||||||
/// @return True if success, otherwise false.
|
|
||||||
bool Load();
|
|
||||||
/// @brief Save settings to file.
|
|
||||||
/// @return True if success, otherwise false.
|
|
||||||
bool Save();
|
|
||||||
/// @brief Reset all settings to default value.
|
|
||||||
void Reset();
|
|
||||||
|
|
||||||
private:
|
|
||||||
yycc_u8string m_CfgFilePath;
|
|
||||||
uint64_t m_VersionIdentifier;
|
|
||||||
std::map<yycc_u8string, AbstractSetting*> m_Settings;
|
|
||||||
};
|
|
||||||
|
|
||||||
#pragma region Setting Presets
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Arithmetic (integral, floating point, bool) and enum type setting
|
|
||||||
* @tparam _Ty The internal stored type belongs to arithmetic type.
|
|
||||||
*/
|
|
||||||
template<typename _Ty, std::enable_if_t<std::is_arithmetic_v<_Ty> || std::is_enum_v<_Ty>, int> = 0>
|
|
||||||
class NumberSetting : public AbstractSetting {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Construct arithmetic type setting.
|
|
||||||
* @param[in] name The name of this setting.
|
|
||||||
* @param[in] default_value The default value of this setting.
|
|
||||||
* @param[in] constraint The constraint applied to this setting.
|
|
||||||
* @exception std::invalid_argument Name of setting is empty.
|
|
||||||
*/
|
|
||||||
NumberSetting(
|
|
||||||
const yycc_u8string_view& name, _Ty default_value,
|
|
||||||
Constraints::Constraint<_Ty> constraint = Constraints::Constraint<_Ty> {}) :
|
|
||||||
AbstractSetting(name), m_Data(default_value), m_DefaultData(default_value), m_Constraint(constraint) {}
|
|
||||||
virtual ~NumberSetting() {}
|
|
||||||
YYCC_DEL_CLS_COPY_MOVE(NumberSetting);
|
|
||||||
|
|
||||||
/// @brief Get stored data in setting.
|
|
||||||
_Ty Get() const { return m_Data; }
|
|
||||||
/**
|
|
||||||
* @brief Set data to setting.
|
|
||||||
* @param[in] new_data The new data.
|
|
||||||
* @return True if success, otherwise false (given value is invalid)
|
|
||||||
*/
|
|
||||||
bool Set(_Ty new_data) {
|
|
||||||
// validate data
|
|
||||||
if (m_Constraint.IsValid() && !m_Constraint.m_CheckFct(new_data))
|
|
||||||
return false;
|
|
||||||
// assign data
|
|
||||||
m_Data = new_data;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual bool UserLoad() override {
|
|
||||||
// read data
|
|
||||||
if (sizeof(m_Data) != GetDataSize())
|
|
||||||
return false;
|
|
||||||
m_Data = *reinterpret_cast<const _Ty*>(GetDataPtr());
|
|
||||||
// check data
|
|
||||||
if (m_Constraint.IsValid() && !m_Constraint.m_CheckFct(m_Data))
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
virtual bool UserSave() override {
|
|
||||||
// write data
|
|
||||||
ResizeData(sizeof(m_Data));
|
|
||||||
*reinterpret_cast<_Ty*>(GetDataPtr()) = m_Data;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
virtual void UserReset() override {
|
|
||||||
m_Data = m_DefaultData;
|
|
||||||
}
|
|
||||||
|
|
||||||
_Ty m_Data, m_DefaultData;
|
|
||||||
Constraints::Constraint<_Ty> m_Constraint;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @brief String type setting
|
|
||||||
class StringSetting : public AbstractSetting {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Construct string setting
|
|
||||||
* @param[in] name The name of this setting.
|
|
||||||
* @param[in] default_value The default value of this setting.
|
|
||||||
* @param[in] constraint The constraint applied to this setting.
|
|
||||||
* @exception std::invalid_argument Name of setting is empty.
|
|
||||||
*/
|
|
||||||
StringSetting(
|
|
||||||
const yycc_u8string_view& name, const yycc_u8string_view& default_value,
|
|
||||||
Constraints::Constraint<yycc_u8string> constraint = Constraints::Constraint<yycc_u8string> {}) :
|
|
||||||
AbstractSetting(name), m_Data(), m_DefaultData(), m_Constraint(constraint) {
|
|
||||||
m_Data = default_value;
|
|
||||||
m_DefaultData = default_value;
|
|
||||||
}
|
|
||||||
virtual ~StringSetting() {}
|
|
||||||
YYCC_DEL_CLS_COPY_MOVE(StringSetting);
|
|
||||||
|
|
||||||
/// @brief Get reference to stored string.
|
|
||||||
const yycc_u8string& Get() const { return m_Data; }
|
|
||||||
/**
|
|
||||||
* @brief Set string data to setting.
|
|
||||||
* @param[in] new_data The new string data.
|
|
||||||
* @return True if success, otherwise false (given value is invalid)
|
|
||||||
*/
|
|
||||||
bool Set(const yycc_u8string_view& new_data) {
|
|
||||||
// check data validation
|
|
||||||
yycc_u8string new_data_cache(new_data);
|
|
||||||
if (m_Constraint.IsValid() && !m_Constraint.m_CheckFct(new_data_cache))
|
|
||||||
return false;
|
|
||||||
// assign data
|
|
||||||
m_Data = std::move(new_data_cache);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual bool UserLoad() override {
|
|
||||||
// read string length
|
|
||||||
size_t string_length;
|
|
||||||
if (GetDataSize() < sizeof(string_length))
|
|
||||||
return false;
|
|
||||||
string_length = *reinterpret_cast<const size_t*>(GetDataPtr());
|
|
||||||
// read string body
|
|
||||||
if (GetDataSize() != sizeof(string_length) + string_length)
|
|
||||||
return false;
|
|
||||||
m_Data.assign(
|
|
||||||
reinterpret_cast<const yycc_char8_t*>(static_cast<const uint8_t*>(GetDataPtr()) + sizeof(string_length)),
|
|
||||||
string_length
|
|
||||||
);
|
|
||||||
// check data
|
|
||||||
if (m_Constraint.IsValid() && !m_Constraint.m_CheckFct(m_Data))
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
virtual bool UserSave() override {
|
|
||||||
// allocate result buffer
|
|
||||||
size_t string_length = m_Data.size();
|
|
||||||
ResizeData(sizeof(string_length) + string_length);
|
|
||||||
// get pointer
|
|
||||||
uint8_t* ptr = static_cast<uint8_t*>(GetDataPtr());
|
|
||||||
// assign string length
|
|
||||||
*reinterpret_cast<size_t*>(ptr) = string_length;
|
|
||||||
// assign string body
|
|
||||||
std::memcpy(ptr + sizeof(string_length), m_Data.data(), string_length);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
virtual void UserReset() override {
|
|
||||||
m_Data = m_DefaultData;
|
|
||||||
}
|
|
||||||
|
|
||||||
yycc_u8string m_Data, m_DefaultData;
|
|
||||||
Constraints::Constraint<yycc_u8string> m_Constraint;
|
|
||||||
};
|
|
||||||
|
|
||||||
#pragma endregion
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,282 +0,0 @@
|
|||||||
#include "ConsoleHelper.hpp"
|
|
||||||
|
|
||||||
#include "EncodingHelper.hpp"
|
|
||||||
#include "StringHelper.hpp"
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
// Include Windows used headers in Windows.
|
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
|
||||||
#include "WinImportPrefix.hpp"
|
|
||||||
#include <Windows.h>
|
|
||||||
#include <io.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include "WinImportSuffix.hpp"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace YYCC::ConsoleHelper {
|
|
||||||
|
|
||||||
#pragma region Windows Specific Functions
|
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
|
||||||
|
|
||||||
static bool RawEnableColorfulConsole(FILE* fs) {
|
|
||||||
if (!_isatty(_fileno(fs))) return false;
|
|
||||||
|
|
||||||
HANDLE h_output;
|
|
||||||
DWORD dw_mode;
|
|
||||||
|
|
||||||
h_output = (HANDLE)_get_osfhandle(_fileno(fs));
|
|
||||||
if (!GetConsoleMode(h_output, &dw_mode)) return false;
|
|
||||||
if (!SetConsoleMode(h_output, dw_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_PROCESSED_OUTPUT)) return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Reference:
|
|
||||||
* https://stackoverflow.com/questions/45575863/how-to-print-utf-8-strings-to-stdcout-on-windows
|
|
||||||
* https://stackoverflow.com/questions/69830460/reading-utf-8-input
|
|
||||||
|
|
||||||
There is 3 way to make Windows console enable UTF8 mode.
|
|
||||||
|
|
||||||
First one is calling SetConsoleCP and SetConsoleOutputCP.
|
|
||||||
The side effect of this is std::cin and std::cout is broken,
|
|
||||||
however there is a patch for this issue.
|
|
||||||
|
|
||||||
Second one is calling _set_mode with _O_U8TEXT or _O_U16TEXT to enable Unicode mode for Windows console.
|
|
||||||
This also have side effect which is stronger than first one.
|
|
||||||
All puts family functions (ASCII-based output functions) will throw assertion exception.
|
|
||||||
You only can use putws family functions (wide-char-based output functions).
|
|
||||||
However these functions can not be used without calling _set_mode in Windows design.
|
|
||||||
|
|
||||||
There still is another method, using WriteConsoleW directly visiting console.
|
|
||||||
This function family can output correct string without calling any extra functions!
|
|
||||||
This method is what we adopted.
|
|
||||||
*/
|
|
||||||
|
|
||||||
template<bool _bIsConsole>
|
|
||||||
static yycc_u8string WinConsoleRead(HANDLE hStdIn) {
|
|
||||||
using _TChar = std::conditional_t<_bIsConsole, wchar_t, char>;
|
|
||||||
|
|
||||||
// Prepare an internal buffer because the read data may not be fully used.
|
|
||||||
// For example, we may read x\ny in a single calling but after processing \n, this function will return
|
|
||||||
// so y will temporarily stored in this internal buffer for next using.
|
|
||||||
// Thus this function is not thread safe.
|
|
||||||
static std::basic_string<_TChar> internal_buffer;
|
|
||||||
// create return value buffer
|
|
||||||
std::basic_string<_TChar> return_buffer;
|
|
||||||
|
|
||||||
// Prepare some variables
|
|
||||||
DWORD dwReadNumberOfChars;
|
|
||||||
_TChar szReadChars[64];
|
|
||||||
size_t eol_pos;
|
|
||||||
|
|
||||||
// try fetching EOL
|
|
||||||
while (true) {
|
|
||||||
// if internal buffer is empty,
|
|
||||||
// try fetching it.
|
|
||||||
if (internal_buffer.empty()) {
|
|
||||||
// console and non-console use different method to read.
|
|
||||||
if constexpr (_bIsConsole) {
|
|
||||||
// console handle, use ReadConsoleW.
|
|
||||||
// read from console, the read data is wchar based
|
|
||||||
if (!ReadConsoleW(hStdIn, szReadChars, sizeof(szReadChars) / sizeof(_TChar), &dwReadNumberOfChars, NULL))
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
// anything else, use ReadFile instead.
|
|
||||||
// the read data is utf8 based
|
|
||||||
if (!ReadFile(hStdIn, szReadChars, sizeof(szReadChars), &dwReadNumberOfChars, NULL))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// send to internal buffer
|
|
||||||
if (dwReadNumberOfChars == 0) break;
|
|
||||||
internal_buffer.append(szReadChars, dwReadNumberOfChars);
|
|
||||||
}
|
|
||||||
|
|
||||||
// try finding EOL in internal buffer
|
|
||||||
if constexpr (std::is_same_v<_TChar, char>) eol_pos = internal_buffer.find_first_of('\n');
|
|
||||||
else eol_pos = internal_buffer.find_first_of(L'\n');
|
|
||||||
// check finding result
|
|
||||||
if (eol_pos == std::wstring::npos) {
|
|
||||||
// the whole string do not include EOL, fully appended to return value
|
|
||||||
return_buffer += internal_buffer;
|
|
||||||
internal_buffer.clear();
|
|
||||||
// need more data, continue while
|
|
||||||
} else {
|
|
||||||
// split result
|
|
||||||
// push into result and remain some in internal buffer.
|
|
||||||
return_buffer.append(internal_buffer, 0u, eol_pos);
|
|
||||||
internal_buffer.erase(0u, eol_pos + 1u); // +1 because EOL take one place.
|
|
||||||
// break while mean success finding
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// post-process for return value
|
|
||||||
yycc_u8string real_return_buffer;
|
|
||||||
if constexpr (_bIsConsole) {
|
|
||||||
// console mode need convert wchar to utf8
|
|
||||||
YYCC::EncodingHelper::WcharToUTF8(return_buffer, real_return_buffer);
|
|
||||||
} else {
|
|
||||||
// non-console just copt the result
|
|
||||||
real_return_buffer = EncodingHelper::ToUTF8(return_buffer);
|
|
||||||
}
|
|
||||||
// every mode need delete \r words
|
|
||||||
YYCC::StringHelper::Replace(real_return_buffer, YYCC_U8("\r"), YYCC_U8(""));
|
|
||||||
// return value
|
|
||||||
return real_return_buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void WinConsoleWrite(const yycc_u8string& strl, bool to_stderr) {
|
|
||||||
// Prepare some Win32 variables
|
|
||||||
// fetch stdout handle first
|
|
||||||
HANDLE hStdOut = GetStdHandle(to_stderr ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE);
|
|
||||||
DWORD dwConsoleMode;
|
|
||||||
DWORD dwWrittenNumberOfChars;
|
|
||||||
|
|
||||||
// if stdout was redirected, this handle may point to a file handle or anything else,
|
|
||||||
// WriteConsoleW can not write data into such scenario, so we need check whether this handle is console handle
|
|
||||||
if (GetConsoleMode(hStdOut, &dwConsoleMode)) {
|
|
||||||
// console handle, use WriteConsoleW.
|
|
||||||
// convert utf8 string to wide char first
|
|
||||||
std::wstring wstrl(YYCC::EncodingHelper::UTF8ToWchar(strl));
|
|
||||||
size_t wstrl_size = wstrl.size();
|
|
||||||
// write string with size check
|
|
||||||
if (wstrl_size <= std::numeric_limits<DWORD>::max()) {
|
|
||||||
WriteConsoleW(hStdOut, wstrl.c_str(), static_cast<DWORD>(wstrl_size), &dwWrittenNumberOfChars, NULL);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// anything else, use WriteFile instead.
|
|
||||||
// WriteFile do not need extra convertion, because it is direct writing.
|
|
||||||
// check whether string length is overflow
|
|
||||||
size_t strl_size = strl.size() * sizeof(yycc_u8string::value_type);
|
|
||||||
// write string with size check
|
|
||||||
if (strl_size <= std::numeric_limits<DWORD>::max()) {
|
|
||||||
WriteFile(hStdOut, strl.c_str(), static_cast<DWORD>(strl_size), &dwWrittenNumberOfChars, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
#pragma endregion
|
|
||||||
|
|
||||||
bool EnableColorfulConsole() {
|
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
|
||||||
|
|
||||||
bool ret = true;
|
|
||||||
ret &= RawEnableColorfulConsole(stdout);
|
|
||||||
ret &= RawEnableColorfulConsole(stderr);
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
// just return true and do nothing
|
|
||||||
return true;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
yycc_u8string ReadLine() {
|
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
|
||||||
|
|
||||||
// get stdin mode
|
|
||||||
HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
|
|
||||||
// use different method to get according to whether stdin is redirected
|
|
||||||
DWORD dwConsoleMode;
|
|
||||||
if (GetConsoleMode(hStdIn, &dwConsoleMode)) {
|
|
||||||
return WinConsoleRead<true>(hStdIn);
|
|
||||||
} else {
|
|
||||||
return WinConsoleRead<false>(hStdIn);
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
// in linux, directly use C++ function to fetch.
|
|
||||||
std::string cmd;
|
|
||||||
if (std::getline(std::cin, cmd).fail()) cmd.clear();
|
|
||||||
return EncodingHelper::ToUTF8(cmd);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
template<bool bNeedFmt, bool bIsErr, bool bHasEOL>
|
|
||||||
static void RawWrite(const yycc_char8_t* u8_fmt, va_list argptr) {
|
|
||||||
// Buiild string need to be written first
|
|
||||||
// If no format string or plain string for writing, return.
|
|
||||||
if (u8_fmt == nullptr) return;
|
|
||||||
// Build or simply copy string
|
|
||||||
yycc_u8string strl;
|
|
||||||
if constexpr (bNeedFmt) {
|
|
||||||
// treat as format string
|
|
||||||
va_list argcpy;
|
|
||||||
va_copy(argcpy, argptr);
|
|
||||||
strl = YYCC::StringHelper::VPrintf(u8_fmt, argcpy);
|
|
||||||
va_end(argcpy);
|
|
||||||
} else {
|
|
||||||
// treat as plain string
|
|
||||||
strl = u8_fmt;
|
|
||||||
}
|
|
||||||
// Checkout whether add EOL
|
|
||||||
if constexpr (bHasEOL) {
|
|
||||||
strl += YYCC_U8("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
|
||||||
// call Windows specific writer
|
|
||||||
WinConsoleWrite(strl, bIsErr);
|
|
||||||
#else
|
|
||||||
// in linux, directly use C function to write.
|
|
||||||
std::fputs(EncodingHelper::ToOrdinary(strl.c_str()), bIsErr ? stderr : stdout);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void Format(const yycc_char8_t* u8_fmt, ...) {
|
|
||||||
va_list argptr;
|
|
||||||
va_start(argptr, u8_fmt);
|
|
||||||
RawWrite<true, false, false>(u8_fmt, argptr);
|
|
||||||
va_end(argptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FormatLine(const yycc_char8_t* u8_fmt, ...) {
|
|
||||||
va_list argptr;
|
|
||||||
va_start(argptr, u8_fmt);
|
|
||||||
RawWrite<true, false, true>(u8_fmt, argptr);
|
|
||||||
va_end(argptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Write(const yycc_char8_t* u8_strl) {
|
|
||||||
va_list empty{};
|
|
||||||
RawWrite<false, false, false>(u8_strl, empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WriteLine(const yycc_char8_t* u8_strl) {
|
|
||||||
va_list empty{};
|
|
||||||
RawWrite<false, false, true>(u8_strl, empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ErrFormat(const yycc_char8_t* u8_fmt, ...) {
|
|
||||||
va_list argptr;
|
|
||||||
va_start(argptr, u8_fmt);
|
|
||||||
RawWrite<true, true, false>(u8_fmt, argptr);
|
|
||||||
va_end(argptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ErrFormatLine(const yycc_char8_t* u8_fmt, ...) {
|
|
||||||
va_list argptr;
|
|
||||||
va_start(argptr, u8_fmt);
|
|
||||||
RawWrite<true, true, true>(u8_fmt, argptr);
|
|
||||||
va_end(argptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ErrWrite(const yycc_char8_t* u8_strl) {
|
|
||||||
va_list empty{};
|
|
||||||
RawWrite<false, true, false>(u8_strl, empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ErrWriteLine(const yycc_char8_t* u8_strl) {
|
|
||||||
va_list empty{};
|
|
||||||
RawWrite<false, true, true>(u8_strl, empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,163 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "YYCCInternal.hpp"
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The helper providing universal C\# style console function and other console related stuff
|
|
||||||
* @details
|
|
||||||
* For how to utilize this functions provided by this namespace, please view \ref console_helper.
|
|
||||||
*/
|
|
||||||
namespace YYCC::ConsoleHelper {
|
|
||||||
|
|
||||||
/// @brief The head of ASCII escape code of black color.
|
|
||||||
#define YYCC_COLORHDR_BLACK "\033[30m"
|
|
||||||
/// @brief The head of ASCII escape code of red color.
|
|
||||||
#define YYCC_COLORHDR_RED "\033[31m"
|
|
||||||
/// @brief The head of ASCII escape code of green color.
|
|
||||||
#define YYCC_COLORHDR_GREEN "\033[32m"
|
|
||||||
/// @brief The head of ASCII escape code of yellow color.
|
|
||||||
#define YYCC_COLORHDR_YELLOW "\033[33m"
|
|
||||||
/// @brief The head of ASCII escape code of blue color.
|
|
||||||
#define YYCC_COLORHDR_BLUE "\033[34m"
|
|
||||||
/// @brief The head of ASCII escape code of magenta color.
|
|
||||||
#define YYCC_COLORHDR_MAGENTA "\033[35m"
|
|
||||||
/// @brief The head of ASCII escape code of cyan color.
|
|
||||||
#define YYCC_COLORHDR_CYAN "\033[36m"
|
|
||||||
/// @brief The head of ASCII escape code of white color.
|
|
||||||
#define YYCC_COLORHDR_WHITE "\033[37m"
|
|
||||||
|
|
||||||
/// @brief The head of ASCII escape code of light black color.
|
|
||||||
#define YYCC_COLORHDR_LIGHT_BLACK "\033[90m"
|
|
||||||
/// @brief The head of ASCII escape code of light red color.
|
|
||||||
#define YYCC_COLORHDR_LIGHT_RED "\033[91m"
|
|
||||||
/// @brief The head of ASCII escape code of light green color.
|
|
||||||
#define YYCC_COLORHDR_LIGHT_GREEN "\033[92m"
|
|
||||||
/// @brief The head of ASCII escape code of light yellow color.
|
|
||||||
#define YYCC_COLORHDR_LIGHT_YELLOW "\033[93m"
|
|
||||||
/// @brief The head of ASCII escape code of light blue color.
|
|
||||||
#define YYCC_COLORHDR_LIGHT_BLUE "\033[94m"
|
|
||||||
/// @brief The head of ASCII escape code of light magenta color.
|
|
||||||
#define YYCC_COLORHDR_LIGHT_MAGENTA "\033[95m"
|
|
||||||
/// @brief The head of ASCII escape code of light cyan color.
|
|
||||||
#define YYCC_COLORHDR_LIGHT_CYAN "\033[96m"
|
|
||||||
/// @brief The head of ASCII escape code of light white color.
|
|
||||||
#define YYCC_COLORHDR_LIGHT_WHITE "\033[97m"
|
|
||||||
|
|
||||||
/// @brief The tail of ASCII escape code of every color.
|
|
||||||
#define YYCC_COLORTAIL "\033[0m"
|
|
||||||
|
|
||||||
/// @brief The ASCII escape code pair of black color.
|
|
||||||
#define YYCC_COLOR_BLACK(T) "\033[30m" T "\033[0m"
|
|
||||||
/// @brief The ASCII escape code pair of red color.
|
|
||||||
#define YYCC_COLOR_RED(T) "\033[31m" T "\033[0m"
|
|
||||||
/// @brief The ASCII escape code pair of green color.
|
|
||||||
#define YYCC_COLOR_GREEN(T) "\033[32m" T "\033[0m"
|
|
||||||
/// @brief The ASCII escape code pair of yellow color.
|
|
||||||
#define YYCC_COLOR_YELLOW(T) "\033[33m" T "\033[0m"
|
|
||||||
/// @brief The ASCII escape code pair of blue color.
|
|
||||||
#define YYCC_COLOR_BLUE(T) "\033[34m" T "\033[0m"
|
|
||||||
/// @brief The ASCII escape code pair of magenta color.
|
|
||||||
#define YYCC_COLOR_MAGENTA(T) "\033[35m" T "\033[0m"
|
|
||||||
/// @brief The ASCII escape code pair of cyan color.
|
|
||||||
#define YYCC_COLOR_CYAN(T) "\033[36m" T "\033[0m"
|
|
||||||
/// @brief The ASCII escape code pair of white color.
|
|
||||||
#define YYCC_COLOR_WHITE(T) "\033[37m" T "\033[0m"
|
|
||||||
|
|
||||||
/// @brief The ASCII escape code pair of light black color.
|
|
||||||
#define YYCC_COLOR_LIGHT_BLACK(T) "\033[90m" T "\033[0m"
|
|
||||||
/// @brief The ASCII escape code pair of light red color.
|
|
||||||
#define YYCC_COLOR_LIGHT_RED(T) "\033[91m" T "\033[0m"
|
|
||||||
/// @brief The ASCII escape code pair of light green color.
|
|
||||||
#define YYCC_COLOR_LIGHT_GREEN(T) "\033[92m" T "\033[0m"
|
|
||||||
/// @brief The ASCII escape code pair of light yellow color.
|
|
||||||
#define YYCC_COLOR_LIGHT_YELLOW(T) "\033[93m" T "\033[0m"
|
|
||||||
/// @brief The ASCII escape code pair of light blue color.
|
|
||||||
#define YYCC_COLOR_LIGHT_BLUE(T) "\033[94m" T "\033[0m"
|
|
||||||
/// @brief The ASCII escape code pair of light magenta color.
|
|
||||||
#define YYCC_COLOR_LIGHT_MAGENTA(T) "\033[95m" T "\033[0m"
|
|
||||||
/// @brief The ASCII escape code pair of light cyan color.
|
|
||||||
#define YYCC_COLOR_LIGHT_CYAN(T) "\033[96m" T "\033[0m"
|
|
||||||
/// @brief The ASCII escape code pair of light white color.
|
|
||||||
#define YYCC_COLOR_LIGHT_WHITE(T) "\033[97m" T "\033[0m"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Enable console color support for Windows.
|
|
||||||
* @details This actually is enable virtual console feature for \c stdout and \c stderr.
|
|
||||||
* @return True if success, otherwise false.
|
|
||||||
* @remarks
|
|
||||||
* This function only works on Windows and do nothing on other platforms such as Linux,
|
|
||||||
* because we assume all terminals existing on other platform support color feature as default.
|
|
||||||
*/
|
|
||||||
bool EnableColorfulConsole();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Reads the next line of UTF8 characters from the standard input stream.
|
|
||||||
* @return
|
|
||||||
* The next line of UTF8 characters from the input stream.
|
|
||||||
* Empty string if user just press Enter key or function failed.
|
|
||||||
*/
|
|
||||||
yycc_u8string ReadLine();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief
|
|
||||||
* Writes the text representation of the specified object
|
|
||||||
* to the standard output stream using the specified format information.
|
|
||||||
* @param[in] u8_fmt The format string.
|
|
||||||
* @param[in] ... The arguments of format string.
|
|
||||||
*/
|
|
||||||
void Format(const yycc_char8_t* u8_fmt, ...);
|
|
||||||
/**
|
|
||||||
* @brief
|
|
||||||
* Writes the text representation of the specified object,
|
|
||||||
* followed by the current line terminator,
|
|
||||||
* to the standard output stream using the specified format information.
|
|
||||||
* @param[in] u8_fmt The format string.
|
|
||||||
* @param[in] ... The arguments of format string.
|
|
||||||
*/
|
|
||||||
void FormatLine(const yycc_char8_t* u8_fmt, ...);
|
|
||||||
/**
|
|
||||||
* @brief Writes the specified string value to the standard output stream.
|
|
||||||
* @param[in] u8_strl The value to write.
|
|
||||||
*/
|
|
||||||
void Write(const yycc_char8_t* u8_strl);
|
|
||||||
/**
|
|
||||||
* @brief
|
|
||||||
* Writes the specified string value, followed by the current line terminator,
|
|
||||||
* to the standard output stream.
|
|
||||||
* @param[in] u8_strl The value to write.
|
|
||||||
*/
|
|
||||||
void WriteLine(const yycc_char8_t* u8_strl);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief
|
|
||||||
* Writes the text representation of the specified object
|
|
||||||
* to the standard error stream using the specified format information.
|
|
||||||
* @param[in] u8_fmt The format string.
|
|
||||||
* @param[in] ... The arguments of format string.
|
|
||||||
*/
|
|
||||||
void ErrFormat(const yycc_char8_t* u8_fmt, ...);
|
|
||||||
/**
|
|
||||||
* @brief
|
|
||||||
* Writes the text representation of the specified object,
|
|
||||||
* followed by the current line terminator,
|
|
||||||
* to the standard error stream using the specified format information.
|
|
||||||
* @param[in] u8_fmt The format string.
|
|
||||||
* @param[in] ... The arguments of format string.
|
|
||||||
*/
|
|
||||||
void ErrFormatLine(const yycc_char8_t* u8_fmt, ...);
|
|
||||||
/**
|
|
||||||
* @brief Writes the specified string value to the standard error stream.
|
|
||||||
* @param[in] u8_strl The value to write.
|
|
||||||
*/
|
|
||||||
void ErrWrite(const yycc_char8_t* u8_strl);
|
|
||||||
/**
|
|
||||||
* @brief
|
|
||||||
* Writes the specified string value, followed by the current line terminator,
|
|
||||||
* to the standard error stream.
|
|
||||||
* @param[in] u8_strl The value to write.
|
|
||||||
*/
|
|
||||||
void ErrWriteLine(const yycc_char8_t* u8_strl);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "YYCCInternal.hpp"
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <set>
|
|
||||||
#include <initializer_list>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The namespace containing constraint declaration
|
|
||||||
* and functions generating common used constraint.
|
|
||||||
*/
|
|
||||||
namespace YYCC::Constraints {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The constraint applied to settings to limit its stored value.
|
|
||||||
* @tparam _Ty The data type this constraint need to be processed with.
|
|
||||||
*/
|
|
||||||
template<typename _Ty>
|
|
||||||
struct Constraint {
|
|
||||||
/// @brief Return true if value is legal, otherwise false.
|
|
||||||
using CheckFct_t = std::function<bool(const _Ty&)>;
|
|
||||||
/// @brief The function pointer used for checking whether given value is valid.
|
|
||||||
CheckFct_t m_CheckFct;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check whether this constraint is valid for using.
|
|
||||||
* @return
|
|
||||||
* True if this constraint is valid, otherwise false.
|
|
||||||
* If this function return false, it means that there is no contraint.
|
|
||||||
* And you should not use this constraint provided any function pointer.
|
|
||||||
*/
|
|
||||||
bool IsValid() const {
|
|
||||||
return m_CheckFct != nullptr;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get constraint for arithmetic or enum values by minimum and maximum value range.
|
|
||||||
* @tparam _Ty An arithmetic or enum type (except bool) of underlying stored value.
|
|
||||||
* @param[in] min_value The minimum value of range (inclusive).
|
|
||||||
* @param[in] max_value The maximum value of range (inclusive).
|
|
||||||
* @return The generated constraint instance which can be directly applied.
|
|
||||||
*/
|
|
||||||
template<typename _Ty, std::enable_if_t<std::is_arithmetic_v<_Ty> && !std::is_same_v<_Ty, bool>, int> = 0>
|
|
||||||
Constraint<_Ty> GetMinMaxRangeConstraint(_Ty min_value, _Ty max_value) {
|
|
||||||
if (min_value > max_value)
|
|
||||||
throw std::invalid_argument("invalid min max value for NumberRangeConstraint");
|
|
||||||
return Constraint<_Ty> {
|
|
||||||
[min_value, max_value](const _Ty& val) -> bool { return (val <= max_value) && (val >= min_value); }
|
|
||||||
/*[min_value, max_value](const _Ty& val) -> _Ty { return std::clamp(val, min_value, max_value); }*/
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get constraint for enum values by enumerating all possible values.
|
|
||||||
* @tparam _Ty An enum type (except bool) of underlying stored value.
|
|
||||||
* @param[in] il An initializer list storing all possible values.
|
|
||||||
* @return The generated constraint instance which can be directly applied.
|
|
||||||
*/
|
|
||||||
template<typename _Ty, std::enable_if_t<std::is_enum_v<_Ty>, int> = 0>
|
|
||||||
Constraint<_Ty> GetEnumEnumerationConstraint(const std::initializer_list<_Ty>& il) {
|
|
||||||
std::set<_Ty> data(il);
|
|
||||||
return Constraint<_Ty> {
|
|
||||||
[data](const _Ty& val) -> bool { return data.find(val) != data.end(); }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get constraint for string values by enumerating all possible values.
|
|
||||||
* @param[in] il An initializer list storing all possible values.
|
|
||||||
* @return The generated constraint instance which can be directly applied.
|
|
||||||
* @remarks
|
|
||||||
* Caller must make sure that the string view passed in initializer list is valid until this Constraint life time gone.
|
|
||||||
* Becasue this generator will not copy your given string view into string.
|
|
||||||
*/
|
|
||||||
inline Constraint<yycc_u8string> GetStringEnumerationConstraint(const std::initializer_list<yycc_u8string_view>& il) {
|
|
||||||
std::set<yycc_u8string_view> data(il);
|
|
||||||
return Constraint<yycc_u8string> {
|
|
||||||
[data](const yycc_u8string& val) -> bool { return data.find(yycc_u8string_view(val)) != data.end(); }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,378 +0,0 @@
|
|||||||
#include "DialogHelper.hpp"
|
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
|
||||||
|
|
||||||
#include "EncodingHelper.hpp"
|
|
||||||
#include "StringHelper.hpp"
|
|
||||||
|
|
||||||
namespace YYCC::DialogHelper {
|
|
||||||
|
|
||||||
#pragma region FileFilters
|
|
||||||
|
|
||||||
bool FileFilters::Add(const yycc_char8_t* filter_name, std::initializer_list<const yycc_char8_t*> il) {
|
|
||||||
// assign filter name
|
|
||||||
if (filter_name == nullptr) return false;
|
|
||||||
FilterName name(filter_name);
|
|
||||||
|
|
||||||
// assign filter patterns
|
|
||||||
FilterModes modes;
|
|
||||||
for (const yycc_char8_t* pattern : il) {
|
|
||||||
if (pattern != nullptr)
|
|
||||||
modes.emplace_back(yycc_u8string(pattern));
|
|
||||||
}
|
|
||||||
|
|
||||||
// check filter patterns
|
|
||||||
if (modes.empty()) return false;
|
|
||||||
|
|
||||||
// add into pairs and return
|
|
||||||
m_Filters.emplace_back(std::make_pair(std::move(name), std::move(modes)));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileFilters::Generate(WinFileFilters& win_result) const {
|
|
||||||
// clear Windows oriented data
|
|
||||||
win_result.Clear();
|
|
||||||
|
|
||||||
// build new Windows oriented string vector first
|
|
||||||
for (const auto& it : m_Filters) {
|
|
||||||
// convert name to wchar
|
|
||||||
WinFileFilters::WinFilterName name;
|
|
||||||
if (!YYCC::EncodingHelper::UTF8ToWchar(it.first, name))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// convert pattern and join them
|
|
||||||
const auto& filter_modes = it.second;
|
|
||||||
yycc_u8string joined_modes(YYCC::StringHelper::Join(filter_modes.begin(), filter_modes.end(), YYCC_U8(";")));
|
|
||||||
WinFileFilters::WinFilterModes modes;
|
|
||||||
if (!YYCC::EncodingHelper::UTF8ToWchar(joined_modes, modes))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// append new pair
|
|
||||||
win_result.m_WinFilters.emplace_back(std::make_pair(name, modes));
|
|
||||||
}
|
|
||||||
|
|
||||||
// check filter size
|
|
||||||
// if it overflow the maximum value, return false
|
|
||||||
size_t count = win_result.m_WinFilters.size();
|
|
||||||
if (count > std::numeric_limits<UINT>::max())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// create new win data struct
|
|
||||||
// and assign string pointer from internal built win string vector.
|
|
||||||
win_result.m_WinDataStruct.reset(new COMDLG_FILTERSPEC[count]);
|
|
||||||
for (size_t i = 0u; i < count; ++i) {
|
|
||||||
win_result.m_WinDataStruct[i].pszName = win_result.m_WinFilters[i].first.c_str();
|
|
||||||
win_result.m_WinDataStruct[i].pszSpec = win_result.m_WinFilters[i].second.c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
// everything is okey
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma endregion
|
|
||||||
|
|
||||||
#pragma region File Dialog
|
|
||||||
|
|
||||||
bool FileDialog::Generate(WinFileDialog& win_result) const {
|
|
||||||
// clear Windows oriented data
|
|
||||||
win_result.Clear();
|
|
||||||
|
|
||||||
// set owner
|
|
||||||
win_result.m_WinOwner = m_Owner;
|
|
||||||
|
|
||||||
// build file filters
|
|
||||||
if (!m_FileTypes.Generate(win_result.m_WinFileTypes))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// check default file type index
|
|
||||||
// check value overflow (comparing with >= because we need plus 1 for file type index later)
|
|
||||||
if (m_DefaultFileTypeIndex >= std::numeric_limits<UINT>::max())
|
|
||||||
return false;
|
|
||||||
// check invalid index (overflow the length or registered file types if there is file type)
|
|
||||||
if (m_FileTypes.Count() != 0u && m_DefaultFileTypeIndex >= m_FileTypes.Count())
|
|
||||||
return false;
|
|
||||||
// set index with additional plus according to Windows specification.
|
|
||||||
win_result.m_WinDefaultFileTypeIndex = static_cast<UINT>(m_DefaultFileTypeIndex + 1);
|
|
||||||
|
|
||||||
// build title and init file name
|
|
||||||
if (m_HasTitle) {
|
|
||||||
if (!YYCC::EncodingHelper::UTF8ToWchar(m_Title, win_result.m_WinTitle))
|
|
||||||
return false;
|
|
||||||
win_result.m_HasTitle = true;
|
|
||||||
}
|
|
||||||
if (m_HasInitFileName) {
|
|
||||||
if (!YYCC::EncodingHelper::UTF8ToWchar(m_InitFileName, win_result.m_WinInitFileName))
|
|
||||||
return false;
|
|
||||||
win_result.m_HasInitFileName = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// fetch init directory
|
|
||||||
if (m_HasInitDirectory) {
|
|
||||||
// convert to wpath
|
|
||||||
std::wstring w_init_directory;
|
|
||||||
if (!YYCC::EncodingHelper::UTF8ToWchar(m_InitDirectory, w_init_directory))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// fetch IShellItem*
|
|
||||||
// Ref: https://stackoverflow.com/questions/76306324/how-to-set-default-folder-for-ifileopendialog-interface
|
|
||||||
IShellItem* init_directory = NULL;
|
|
||||||
HRESULT hr = SHCreateItemFromParsingName(w_init_directory.c_str(), NULL, IID_PPV_ARGS(&init_directory));
|
|
||||||
if (FAILED(hr)) return false;
|
|
||||||
|
|
||||||
// assign IShellItem*
|
|
||||||
win_result.m_WinInitDirectory.reset(init_directory);
|
|
||||||
}
|
|
||||||
|
|
||||||
// everything is okey
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma endregion
|
|
||||||
|
|
||||||
#pragma region Windows Dialog Code
|
|
||||||
|
|
||||||
enum class CommonFileDialogType {
|
|
||||||
OpenFile,
|
|
||||||
OpenMultipleFiles,
|
|
||||||
SaveFile,
|
|
||||||
OpenFolder
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Extract display name from given IShellItem*.
|
|
||||||
* @param item[in] The pointer to IShellItem for extracting.
|
|
||||||
* @param ret[out] Extracted display name container.
|
|
||||||
* @return True if success, otherwise false.
|
|
||||||
* @remarks This is an assist function of CommonFileDialog.
|
|
||||||
*/
|
|
||||||
static bool ExtractDisplayName(IShellItem* item, yycc_u8string& ret) {
|
|
||||||
// fetch display name from IShellItem*
|
|
||||||
LPWSTR _name;
|
|
||||||
HRESULT hr = item->GetDisplayName(SIGDN_FILESYSPATH, &_name);
|
|
||||||
if (FAILED(hr)) return false;
|
|
||||||
COMHelper::SmartLPWSTR display_name(_name);
|
|
||||||
|
|
||||||
// convert result
|
|
||||||
if (!YYCC::EncodingHelper::WcharToUTF8(display_name.get(), ret))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// finished
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief General file dialog.
|
|
||||||
* @param params[in] User specified parameter controlling the behavior of this file dialog,
|
|
||||||
* including title, file types and etc.
|
|
||||||
* @param ret[out] The path to user selected files or folders.
|
|
||||||
* For multiple selection, the count of items >= 1. For other scenario, the count of item is 1.
|
|
||||||
* @return True if success, otherwise false (input parameters is wrong or user click "Cancel" in popup window).
|
|
||||||
* @remarks This function is the real underlying function of all dialog functions.
|
|
||||||
*/
|
|
||||||
template<CommonFileDialogType EDialogType>
|
|
||||||
static bool CommonFileDialog(const FileDialog& params, std::vector<yycc_u8string>& ret) {
|
|
||||||
// Reference: https://learn.microsoft.com/en-us/windows/win32/shell/common-file-dialog
|
|
||||||
// prepare result variable
|
|
||||||
HRESULT hr;
|
|
||||||
|
|
||||||
// check whether COM environment has been initialized
|
|
||||||
if (!COMHelper::IsInitialized()) return false;
|
|
||||||
|
|
||||||
// create file dialog instance
|
|
||||||
// fetch dialog CLSID first
|
|
||||||
CLSID dialog_clsid;
|
|
||||||
switch (EDialogType) {
|
|
||||||
case CommonFileDialogType::OpenFile:
|
|
||||||
case CommonFileDialogType::OpenMultipleFiles:
|
|
||||||
case CommonFileDialogType::OpenFolder:
|
|
||||||
dialog_clsid = CLSID_FileOpenDialog;
|
|
||||||
break;
|
|
||||||
case CommonFileDialogType::SaveFile:
|
|
||||||
dialog_clsid = CLSID_FileSaveDialog;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// create raw dialog pointer
|
|
||||||
IFileDialog* _pfd = nullptr;
|
|
||||||
hr = CoCreateInstance(
|
|
||||||
dialog_clsid,
|
|
||||||
NULL,
|
|
||||||
CLSCTX_INPROC_SERVER,
|
|
||||||
IID_PPV_ARGS(&_pfd)
|
|
||||||
);
|
|
||||||
if (FAILED(hr)) return false;
|
|
||||||
// create memory-safe dialog pointer
|
|
||||||
COMHelper::SmartIFileDialog pfd(_pfd);
|
|
||||||
|
|
||||||
// set options for dialog
|
|
||||||
// before setting, always get the options first in order.
|
|
||||||
// not to override existing options.
|
|
||||||
DWORD dwFlags;
|
|
||||||
hr = pfd->GetOptions(&dwFlags);
|
|
||||||
if (FAILED(hr)) return false;
|
|
||||||
// modify options
|
|
||||||
switch (EDialogType) {
|
|
||||||
// We want user only can pick file system files: FOS_FORCEFILESYSTEM.
|
|
||||||
// Open dialog default: FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_NOCHANGEDIR
|
|
||||||
// Save dialog default: FOS_OVERWRITEPROMPT | FOS_NOREADONLYRETURN | FOS_PATHMUSTEXIST | FOS_NOCHANGEDIR
|
|
||||||
// Pick folder: FOS_PICKFOLDERS
|
|
||||||
case CommonFileDialogType::OpenFile:
|
|
||||||
dwFlags |= FOS_FORCEFILESYSTEM;
|
|
||||||
dwFlags |= FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_NOCHANGEDIR;
|
|
||||||
break;
|
|
||||||
case CommonFileDialogType::OpenMultipleFiles:
|
|
||||||
dwFlags |= FOS_FORCEFILESYSTEM;
|
|
||||||
dwFlags |= FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_NOCHANGEDIR;
|
|
||||||
dwFlags |= FOS_ALLOWMULTISELECT;
|
|
||||||
break;
|
|
||||||
case CommonFileDialogType::SaveFile:
|
|
||||||
dwFlags |= FOS_FORCEFILESYSTEM;
|
|
||||||
dwFlags |= FOS_OVERWRITEPROMPT | FOS_NOREADONLYRETURN | FOS_PATHMUSTEXIST | FOS_NOCHANGEDIR;
|
|
||||||
break;
|
|
||||||
case CommonFileDialogType::OpenFolder:
|
|
||||||
dwFlags |= FOS_FORCEFILESYSTEM;
|
|
||||||
dwFlags |= FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_NOCHANGEDIR;
|
|
||||||
dwFlags |= FOS_PICKFOLDERS;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// set folder dialog options
|
|
||||||
hr = pfd->SetOptions(dwFlags);
|
|
||||||
if (FAILED(hr)) return false;
|
|
||||||
|
|
||||||
// build Windows used file dialog parameters
|
|
||||||
WinFileDialog win_params;
|
|
||||||
if (!params.Generate(win_params))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// setup title and init file name
|
|
||||||
if (win_params.HasTitle()) {
|
|
||||||
hr = pfd->SetTitle(win_params.GetTitle());
|
|
||||||
if (FAILED(hr)) return false;
|
|
||||||
}
|
|
||||||
if (win_params.HasInitFileName()) {
|
|
||||||
hr = pfd->SetFileName(win_params.GetInitFileName());
|
|
||||||
if (FAILED(hr)) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// setup init directory
|
|
||||||
if (win_params.HasInitDirectory()) {
|
|
||||||
hr = pfd->SetFolder(win_params.GetInitDirectory());
|
|
||||||
}
|
|
||||||
|
|
||||||
// set file types and default file index when we picking file
|
|
||||||
if constexpr (EDialogType != CommonFileDialogType::OpenFolder) {
|
|
||||||
// set file types list
|
|
||||||
const auto& file_filters = win_params.GetFileTypes();
|
|
||||||
hr = pfd->SetFileTypes(file_filters.GetFilterCount(), file_filters.GetFilterSpecs());
|
|
||||||
if (FAILED(hr)) return false;
|
|
||||||
|
|
||||||
// set default file type index
|
|
||||||
hr = pfd->SetFileTypeIndex(win_params.GetDefaultFileTypeIndex());
|
|
||||||
if (FAILED(hr)) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// show the dialog
|
|
||||||
hr = pfd->Show(win_params.HasOwner() ? win_params.GetOwner() : nullptr);
|
|
||||||
if (FAILED(hr)) return false;
|
|
||||||
|
|
||||||
// obtain result when user click "OK" button.
|
|
||||||
switch (EDialogType) {
|
|
||||||
case CommonFileDialogType::OpenFile:
|
|
||||||
case CommonFileDialogType::OpenFolder:
|
|
||||||
case CommonFileDialogType::SaveFile:
|
|
||||||
{
|
|
||||||
// obtain one file entry
|
|
||||||
IShellItem* _item;
|
|
||||||
hr = pfd->GetResult(&_item);
|
|
||||||
if (FAILED(hr)) return false;
|
|
||||||
COMHelper::SmartIShellItem result_item(_item);
|
|
||||||
|
|
||||||
// extract display name
|
|
||||||
yycc_u8string result_name;
|
|
||||||
if (!ExtractDisplayName(result_item.get(), result_name))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// append result
|
|
||||||
ret.emplace_back(std::move(result_name));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CommonFileDialogType::OpenMultipleFiles:
|
|
||||||
{
|
|
||||||
// try casting file dialog to file open dialog
|
|
||||||
// Ref: https://learn.microsoft.com/en-us/windows/win32/learnwin32/asking-an-object-for-an-interface
|
|
||||||
IFileOpenDialog* _pfod = nullptr;
|
|
||||||
hr = pfd->QueryInterface(IID_PPV_ARGS(&_pfod));
|
|
||||||
if (FAILED(hr)) return false;
|
|
||||||
COMHelper::SmartIFileOpenDialog pfod(_pfod);
|
|
||||||
|
|
||||||
// obtain multiple file entires
|
|
||||||
IShellItemArray* _items;
|
|
||||||
hr = pfod->GetResults(&_items);
|
|
||||||
if (FAILED(hr)) return false;
|
|
||||||
COMHelper::SmartIShellItemArray result_items(_items);
|
|
||||||
|
|
||||||
// analyze file entries
|
|
||||||
// get array count first
|
|
||||||
DWORD result_items_count = 0u;
|
|
||||||
hr = result_items->GetCount(&result_items_count);
|
|
||||||
if (FAILED(hr)) return false;
|
|
||||||
// iterate array
|
|
||||||
for (DWORD i = 0u; i < result_items_count; ++i) {
|
|
||||||
// fetch item by index
|
|
||||||
IShellItem* _item;;
|
|
||||||
hr = result_items->GetItemAt(i, &_item);
|
|
||||||
if (FAILED(hr)) return false;
|
|
||||||
COMHelper::SmartIShellItem result_item(_item);
|
|
||||||
|
|
||||||
// extract display name
|
|
||||||
yycc_u8string result_name;
|
|
||||||
if (!ExtractDisplayName(result_item.get(), result_name))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// append result
|
|
||||||
ret.emplace_back(std::move(result_name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// everything is okey
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma endregion
|
|
||||||
|
|
||||||
#pragma region Wrapper Functions
|
|
||||||
|
|
||||||
bool OpenFileDialog(const FileDialog& params, yycc_u8string& ret) {
|
|
||||||
std::vector<yycc_u8string> cache;
|
|
||||||
bool isok = CommonFileDialog<CommonFileDialogType::OpenFile>(params, cache);
|
|
||||||
if (isok) ret = cache.front();
|
|
||||||
return isok;
|
|
||||||
}
|
|
||||||
bool OpenMultipleFileDialog(const FileDialog& params, std::vector<yycc_u8string>& ret) {
|
|
||||||
return CommonFileDialog<CommonFileDialogType::OpenMultipleFiles>(params, ret);
|
|
||||||
}
|
|
||||||
bool SaveFileDialog(const FileDialog& params, yycc_u8string& ret) {
|
|
||||||
std::vector<yycc_u8string> cache;
|
|
||||||
bool isok = CommonFileDialog<CommonFileDialogType::SaveFile>(params, cache);
|
|
||||||
if (isok) ret = cache.front();
|
|
||||||
return isok;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OpenFolderDialog(const FileDialog& params, yycc_u8string& ret) {
|
|
||||||
std::vector<yycc_u8string> cache;
|
|
||||||
bool isok = CommonFileDialog<CommonFileDialogType::OpenFolder>(params, cache);
|
|
||||||
if (isok) ret = cache.front();
|
|
||||||
return isok;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma endregion
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,312 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "YYCCInternal.hpp"
|
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
|
||||||
|
|
||||||
#include "COMHelper.hpp"
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <initializer_list>
|
|
||||||
|
|
||||||
#include "WinImportPrefix.hpp"
|
|
||||||
#include <Windows.h>
|
|
||||||
#include <shlobj_core.h>
|
|
||||||
#include "WinImportSuffix.hpp"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The namespace providing Windows universal dialog features.
|
|
||||||
* @details
|
|
||||||
* This namespace only available on Windows platform.
|
|
||||||
* See also \ref dialog_helper.
|
|
||||||
*/
|
|
||||||
namespace YYCC::DialogHelper {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The class representing the file types region in file dialog.
|
|
||||||
* @details
|
|
||||||
* This class is served for Windows used.
|
|
||||||
* Programmer should \b not create this class manually.
|
|
||||||
*/
|
|
||||||
class WinFileFilters {
|
|
||||||
friend class FileFilters;
|
|
||||||
friend class WinFileDialog;
|
|
||||||
public:
|
|
||||||
WinFileFilters() : m_WinFilters(), m_WinDataStruct(nullptr) {}
|
|
||||||
YYCC_DEL_CLS_COPY_MOVE(WinFileFilters);
|
|
||||||
|
|
||||||
/// @brief Get the count of available file filters
|
|
||||||
UINT GetFilterCount() const {
|
|
||||||
return static_cast<UINT>(m_WinFilters.size());
|
|
||||||
}
|
|
||||||
/// @brief Get pointer to Windows used file filters declarations
|
|
||||||
const COMDLG_FILTERSPEC* GetFilterSpecs() const {
|
|
||||||
return m_WinDataStruct.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
using WinFilterModes = std::wstring;
|
|
||||||
using WinFilterName = std::wstring;
|
|
||||||
using WinFilterPair = std::pair<WinFilterName, WinFilterModes>;
|
|
||||||
|
|
||||||
std::vector<WinFilterPair> m_WinFilters;
|
|
||||||
std::unique_ptr<COMDLG_FILTERSPEC[]> m_WinDataStruct;
|
|
||||||
|
|
||||||
/// @brief Clear all current file filters
|
|
||||||
void Clear() {
|
|
||||||
m_WinDataStruct.reset();
|
|
||||||
m_WinFilters.clear();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The class representing the file types region in file dialog.
|
|
||||||
* @details
|
|
||||||
* This class is served for programmer using.
|
|
||||||
* But you don't need create it on your own.
|
|
||||||
* You can simply fetch it by FileDialog::ConfigreFileTypes ,
|
|
||||||
* because this class is a part of FileDialog.
|
|
||||||
*/
|
|
||||||
class FileFilters {
|
|
||||||
public:
|
|
||||||
FileFilters() : m_Filters() {}
|
|
||||||
YYCC_DEL_CLS_COPY_MOVE(FileFilters);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Add a filter pair in file types list.
|
|
||||||
* @param[in] filter_name The friendly name of the filter.
|
|
||||||
* @param[in] il
|
|
||||||
* A C++ initialize list containing acceptable file filter pattern.
|
|
||||||
* Every entries must be `const yycc_char8_t*` representing a single filter pattern.
|
|
||||||
* The list at least should have one valid pattern.
|
|
||||||
* This function will not validate these filter patterns, so please write them carefully.
|
|
||||||
* @return True if added success, otherwise false.
|
|
||||||
* @remarks
|
|
||||||
* This function allow you register multiple filter patterns for single friendly name.
|
|
||||||
* For example: <TT>Add(u8"Microsoft Word (*.doc; *.docx)", {u8"*.doc", u8"*.docx"})</TT>
|
|
||||||
*/
|
|
||||||
bool Add(const yycc_char8_t* filter_name, std::initializer_list<const yycc_char8_t*> il);
|
|
||||||
/**
|
|
||||||
* @brief Get the count of added filter pairs.
|
|
||||||
* @return The count of already added filter pairs.
|
|
||||||
*/
|
|
||||||
size_t Count() const { return m_Filters.size(); }
|
|
||||||
|
|
||||||
/// @brief Clear filter pairs for following re-use.
|
|
||||||
void Clear() { m_Filters.clear(); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Generate Windows dialog system used data struct.
|
|
||||||
* @param[out] win_result The class receiving the generated filter data struct.
|
|
||||||
* @return True if generation success, otherwise false.
|
|
||||||
* @remarks
|
|
||||||
* Programmer should not call this function,
|
|
||||||
* this function is used as YYCC internal code.
|
|
||||||
*/
|
|
||||||
bool Generate(WinFileFilters& win_result) const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
using FilterModes = std::vector<yycc_u8string>;
|
|
||||||
using FilterName = yycc_u8string;
|
|
||||||
using FilterPair = std::pair<FilterName, FilterModes>;
|
|
||||||
|
|
||||||
std::vector<FilterPair> m_Filters;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The class representing the file dialog.
|
|
||||||
* @details
|
|
||||||
* This class is served for Windows used.
|
|
||||||
* Programmer should \b not create this class manually.
|
|
||||||
*/
|
|
||||||
class WinFileDialog {
|
|
||||||
friend class FileDialog;
|
|
||||||
public:
|
|
||||||
WinFileDialog() :
|
|
||||||
m_WinOwner(NULL),
|
|
||||||
m_WinFileTypes(), m_WinDefaultFileTypeIndex(0u),
|
|
||||||
m_HasTitle(false), m_HasInitFileName(false), m_WinTitle(), m_WinInitFileName(),
|
|
||||||
m_WinInitDirectory(nullptr) {}
|
|
||||||
YYCC_DEL_CLS_COPY_MOVE(WinFileDialog);
|
|
||||||
|
|
||||||
/// @brief Get whether this dialog has owner.
|
|
||||||
bool HasOwner() const { return m_WinOwner != NULL; }
|
|
||||||
/// @brief Get the \c HWND of dialog owner.
|
|
||||||
HWND GetOwner() const { return m_WinOwner; }
|
|
||||||
|
|
||||||
/// @brief Get the struct holding Windows used file filters data.
|
|
||||||
const WinFileFilters& GetFileTypes() const { return m_WinFileTypes; }
|
|
||||||
/// @brief Get the index of default selected file filter.
|
|
||||||
UINT GetDefaultFileTypeIndex() const { return m_WinDefaultFileTypeIndex; }
|
|
||||||
|
|
||||||
/// @brief Get whether dialog has custom title.
|
|
||||||
bool HasTitle() const { return m_HasTitle; }
|
|
||||||
/// @brief Get custom title of dialog.
|
|
||||||
const wchar_t* GetTitle() const { return m_WinTitle.c_str(); }
|
|
||||||
/// @brief Get whether dialog has custom initial file name.
|
|
||||||
bool HasInitFileName() const { return m_HasInitFileName; }
|
|
||||||
/// @brief Get custom initial file name of dialog
|
|
||||||
const wchar_t* GetInitFileName() const { return m_WinInitFileName.c_str(); }
|
|
||||||
|
|
||||||
/// @brief Get whether dialog has custom initial directory.
|
|
||||||
bool HasInitDirectory() const { return m_WinInitDirectory.get() != nullptr; }
|
|
||||||
/// @brief Get custom initial directory of dialog.
|
|
||||||
IShellItem* GetInitDirectory() const { return m_WinInitDirectory.get(); }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
HWND m_WinOwner;
|
|
||||||
WinFileFilters m_WinFileTypes;
|
|
||||||
/**
|
|
||||||
* @brief The default selected file type in dialog
|
|
||||||
* @remarks
|
|
||||||
* This is 1-based index according to Windows specification.
|
|
||||||
* In other words, when generating this struct from FileDialog to this struct this field should plus 1.
|
|
||||||
* Because the same field located in FileDialog is 0-based index.
|
|
||||||
*/
|
|
||||||
UINT m_WinDefaultFileTypeIndex;
|
|
||||||
bool m_HasTitle, m_HasInitFileName;
|
|
||||||
std::wstring m_WinTitle, m_WinInitFileName;
|
|
||||||
COMHelper::SmartIShellItem m_WinInitDirectory;
|
|
||||||
|
|
||||||
/// @brief Clear all data and reset them to default value.
|
|
||||||
void Clear() {
|
|
||||||
m_WinOwner = nullptr;
|
|
||||||
m_WinFileTypes.Clear();
|
|
||||||
m_WinDefaultFileTypeIndex = 0u;
|
|
||||||
m_HasTitle = m_HasInitFileName = false;
|
|
||||||
m_WinTitle.clear();
|
|
||||||
m_WinInitFileName.clear();
|
|
||||||
m_WinInitDirectory.reset();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The class representing the file dialog.
|
|
||||||
* @details
|
|
||||||
* This class is served for programming using to describe every aspectes of the dialog.
|
|
||||||
* For how to use this struct, see \ref dialog_helper.
|
|
||||||
*/
|
|
||||||
class FileDialog {
|
|
||||||
public:
|
|
||||||
FileDialog() :
|
|
||||||
m_Owner(NULL),
|
|
||||||
m_FileTypes(),
|
|
||||||
m_DefaultFileTypeIndex(0u),
|
|
||||||
m_Title(), m_InitFileName(), m_InitDirectory(),
|
|
||||||
m_HasTitle(false), m_HasInitFileName(false), m_HasInitDirectory(false) {}
|
|
||||||
YYCC_DEL_CLS_COPY_MOVE(FileDialog);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set the owner of dialog.
|
|
||||||
* @param[in] owner The \c HWND pointing to the owner of dialog, or NULL to remove owner.
|
|
||||||
*/
|
|
||||||
void SetOwner(HWND owner) { m_Owner = owner; }
|
|
||||||
/**
|
|
||||||
* @brief Set custom title of dialog
|
|
||||||
* @param[in] title The string pointer to custom title, or nullptr to remove it.
|
|
||||||
*/
|
|
||||||
void SetTitle(const yycc_char8_t* title) {
|
|
||||||
if (m_HasTitle = title != nullptr)
|
|
||||||
m_Title = title;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @brief Fetch the struct describing file filters for future configuration.
|
|
||||||
* @return The reference to the struct describing file filters.
|
|
||||||
*/
|
|
||||||
FileFilters& ConfigreFileTypes() {
|
|
||||||
return m_FileTypes;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @brief Set the index of default selected file filter.
|
|
||||||
* @param[in] idx
|
|
||||||
* The index to default one.
|
|
||||||
* This must be a valid index in file filters.
|
|
||||||
*/
|
|
||||||
void SetDefaultFileTypeIndex(size_t idx) { m_DefaultFileTypeIndex = idx; }
|
|
||||||
/**
|
|
||||||
* @brief Set the initial file name of dialog
|
|
||||||
* @details If set, the file name will always be same one when opening dialog.
|
|
||||||
* @param[in] init_filename String pointer to initial file name, or nullptr to remove it.
|
|
||||||
*/
|
|
||||||
void SetInitFileName(const yycc_char8_t* init_filename) {
|
|
||||||
if (m_HasInitFileName = init_filename != nullptr)
|
|
||||||
m_InitFileName = init_filename;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @brief Set the initial directory of dialog
|
|
||||||
* @details If set, the opended directory will always be the same one when opening dialog
|
|
||||||
* @param[in] init_dir
|
|
||||||
* String pointer to initial directory.
|
|
||||||
* Invalid path or nullptr will remove this feature.
|
|
||||||
*/
|
|
||||||
void SetInitDirectory(const yycc_char8_t* init_dir) {
|
|
||||||
if (m_HasInitDirectory = init_dir != nullptr)
|
|
||||||
m_InitDirectory = init_dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Clear file dialog parameters for following re-use.
|
|
||||||
void Clear() {
|
|
||||||
m_Owner = nullptr;
|
|
||||||
m_HasTitle = m_HasInitFileName = m_HasInitDirectory = false;
|
|
||||||
m_Title.clear();
|
|
||||||
m_InitFileName.clear();
|
|
||||||
m_InitDirectory.clear();
|
|
||||||
m_FileTypes.Clear();
|
|
||||||
m_DefaultFileTypeIndex = 0u;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Generate Windows dialog system used data struct.
|
|
||||||
* @param[out] win_result The class receiving the generated filter data struct.
|
|
||||||
* @return True if generation is success, otherwise false.
|
|
||||||
* @remarks
|
|
||||||
* Programmer should not call this function.
|
|
||||||
* This function is used as YYCC internal code.
|
|
||||||
*/
|
|
||||||
bool Generate(WinFileDialog& win_result) const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
HWND m_Owner;
|
|
||||||
bool m_HasTitle, m_HasInitFileName, m_HasInitDirectory;
|
|
||||||
yycc_u8string m_Title, m_InitFileName, m_InitDirectory;
|
|
||||||
FileFilters m_FileTypes;
|
|
||||||
/**
|
|
||||||
* @brief The default selected file type in dialog
|
|
||||||
* @remarks
|
|
||||||
* The index Windows used is 1-based index.
|
|
||||||
* But for universal experience, we order this is 0-based index.
|
|
||||||
* And do convertion when generating Windows used struct.
|
|
||||||
*/
|
|
||||||
size_t m_DefaultFileTypeIndex;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Open the dialog which order user select single file to open.
|
|
||||||
* @param[in] params The configuration of dialog.
|
|
||||||
* @param[out] ret Full path to user selected file.
|
|
||||||
* @return False if user calcel the operation or something went wrong, otherwise true.
|
|
||||||
*/
|
|
||||||
bool OpenFileDialog(const FileDialog& params, yycc_u8string& ret);
|
|
||||||
/**
|
|
||||||
* @brief Open the dialog which order user select multiple file to open.
|
|
||||||
* @param[in] params The configuration of dialog.
|
|
||||||
* @param[out] ret The list of full path of user selected files.
|
|
||||||
* @return False if user calcel the operation or something went wrong, otherwise true.
|
|
||||||
*/
|
|
||||||
bool OpenMultipleFileDialog(const FileDialog& params, std::vector<yycc_u8string>& ret);
|
|
||||||
/**
|
|
||||||
* @brief Open the dialog which order user select single file to save.
|
|
||||||
* @param[in] params The configuration of dialog.
|
|
||||||
* @param[out] ret Full path to user selected file.
|
|
||||||
* @return False if user calcel the operation or something went wrong, otherwise true.
|
|
||||||
*/
|
|
||||||
bool SaveFileDialog(const FileDialog& params, yycc_u8string& ret);
|
|
||||||
/**
|
|
||||||
* @brief Open the dialog which order user select single directory to open.
|
|
||||||
* @param[in] params The configuration of dialog.
|
|
||||||
* @param[out] ret Full path to user selected directory.
|
|
||||||
* @return False if user calcel the operation or something went wrong, otherwise true.
|
|
||||||
*/
|
|
||||||
bool OpenFolderDialog(const FileDialog& params, yycc_u8string& ret);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,409 +0,0 @@
|
|||||||
#include "EncodingHelper.hpp"
|
|
||||||
|
|
||||||
#include <locale>
|
|
||||||
|
|
||||||
namespace YYCC::EncodingHelper {
|
|
||||||
|
|
||||||
#pragma region UTF8 Ordinary 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* ToOrdinary(const yycc_char8_t* src) {
|
|
||||||
return reinterpret_cast<const char*>(src);
|
|
||||||
}
|
|
||||||
char* ToOrdinary(yycc_char8_t* src) {
|
|
||||||
return reinterpret_cast<char*>(src);
|
|
||||||
}
|
|
||||||
std::string ToOrdinary(const yycc_u8string_view& src) {
|
|
||||||
return std::string(reinterpret_cast<const char*>(src.data()), src.size());
|
|
||||||
}
|
|
||||||
std::string_view ToOrdinaryView(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(ToOrdinaryView(src));
|
|
||||||
return CharToWchar(adapted_src, dst, CP_UTF8);
|
|
||||||
}
|
|
||||||
bool UTF8ToWchar(const yycc_char8_t* src, std::wstring& dst) {
|
|
||||||
CONVFCT_TYPE2(UTF8ToWchar, yycc_char8_t, wchar_t);
|
|
||||||
}
|
|
||||||
std::wstring UTF8ToWchar(const yycc_u8string_view& src) {
|
|
||||||
CONVFCT_TYPE3(UTF8ToWchar, yycc_char8_t, wchar_t);
|
|
||||||
}
|
|
||||||
std::wstring UTF8ToWchar(const yycc_char8_t* src) {
|
|
||||||
CONVFCT_TYPE4(UTF8ToWchar, yycc_char8_t, wchar_t);
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma endregion
|
|
||||||
|
|
||||||
#pragma region CharToUTF8
|
|
||||||
|
|
||||||
bool CharToUTF8(const std::string_view& src, yycc_u8string& dst, UINT code_page) {
|
|
||||||
std::string adapted_dst;
|
|
||||||
bool ret = CharToChar(src, adapted_dst, code_page, CP_UTF8);
|
|
||||||
if (ret) dst = ToUTF8(adapted_dst);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
bool CharToUTF8(const char* src, yycc_u8string& dst, UINT code_page) {
|
|
||||||
CONVFCT_TYPE2(CharToUTF8, char, yycc_char8_t, code_page);
|
|
||||||
}
|
|
||||||
yycc_u8string CharToUTF8(const std::string_view& src, UINT code_page) {
|
|
||||||
CONVFCT_TYPE3(CharToUTF8, char, yycc_char8_t, code_page);
|
|
||||||
}
|
|
||||||
yycc_u8string CharToUTF8(const char* src, UINT code_page) {
|
|
||||||
CONVFCT_TYPE4(CharToUTF8, char, yycc_char8_t, code_page);
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma endregion
|
|
||||||
|
|
||||||
#pragma region UTF8ToChar
|
|
||||||
|
|
||||||
bool UTF8ToChar(const yycc_u8string_view& src, std::string& dst, UINT code_page) {
|
|
||||||
std::string_view adapted_src(ToOrdinaryView(src));
|
|
||||||
return CharToChar(adapted_src, dst, CP_UTF8, code_page);
|
|
||||||
}
|
|
||||||
bool UTF8ToChar(const yycc_char8_t* src, std::string& dst, UINT code_page) {
|
|
||||||
CONVFCT_TYPE2(UTF8ToChar, yycc_char8_t, char, code_page);
|
|
||||||
}
|
|
||||||
std::string UTF8ToChar(const yycc_u8string_view& src, UINT code_page) {
|
|
||||||
CONVFCT_TYPE3(UTF8ToChar, yycc_char8_t, char, code_page);
|
|
||||||
}
|
|
||||||
std::string UTF8ToChar(const yycc_char8_t* src, UINT code_page) {
|
|
||||||
CONVFCT_TYPE4(UTF8ToChar, yycc_char8_t, char, code_page);
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma endregion
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#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,95 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "YYCCInternal.hpp"
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
|
||||||
#include "WinImportPrefix.hpp"
|
|
||||||
#include <Windows.h>
|
|
||||||
#include "WinImportSuffix.hpp"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The helper for all encoding stuff.
|
|
||||||
* @details
|
|
||||||
* For more infomations about how to use the functions provided by this namespace,
|
|
||||||
* please see \ref library_encoding and \ref encoding_helper.
|
|
||||||
*/
|
|
||||||
namespace YYCC::EncodingHelper {
|
|
||||||
|
|
||||||
#define _YYCC_U8(strl) u8 ## strl ///< The assistant macro for YYCC_U8.
|
|
||||||
#define YYCC_U8(strl) (reinterpret_cast<const ::YYCC::yycc_char8_t*>(_YYCC_U8(strl))) ///< The macro for creating UTF8 string literal. See \ref library_encoding.
|
|
||||||
#define YYCC_U8_CHAR(chr) (static_cast<YYCC::yycc_char8_t>(chr)) ///< The macro for casting ordinary char type into YYCC UTF8 char type.
|
|
||||||
|
|
||||||
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* ToOrdinary(const yycc_char8_t* src);
|
|
||||||
char* ToOrdinary(yycc_char8_t* src);
|
|
||||||
std::string ToOrdinary(const yycc_u8string_view& src);
|
|
||||||
std::string_view ToOrdinaryView(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);
|
|
||||||
|
|
||||||
bool CharToUTF8(const std::string_view& src, yycc_u8string& dst, UINT code_page);
|
|
||||||
bool CharToUTF8(const char* src, yycc_u8string& dst, UINT code_page);
|
|
||||||
yycc_u8string CharToUTF8(const std::string_view& src, UINT code_page);
|
|
||||||
yycc_u8string CharToUTF8(const char* src, UINT code_page);
|
|
||||||
|
|
||||||
bool UTF8ToChar(const yycc_u8string_view& src, std::string& dst, UINT code_page);
|
|
||||||
bool UTF8ToChar(const yycc_char8_t* src, std::string& dst, UINT code_page);
|
|
||||||
std::string UTF8ToChar(const yycc_u8string_view& src, UINT code_page);
|
|
||||||
std::string UTF8ToChar(const yycc_char8_t* src, UINT code_page);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
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,563 +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 "StdPatch.hpp"
|
|
||||||
#include <filesystem>
|
|
||||||
#include <cstdarg>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cinttypes>
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
#include "WinImportPrefix.hpp"
|
|
||||||
#include <Windows.h>
|
|
||||||
#include <DbgHelp.h>
|
|
||||||
#include "WinImportSuffix.hpp"
|
|
||||||
|
|
||||||
namespace YYCC::ExceptionHelper {
|
|
||||||
|
|
||||||
static LONG WINAPI UExceptionImpl(LPEXCEPTION_POINTERS);
|
|
||||||
class ExceptionRegister {
|
|
||||||
public:
|
|
||||||
ExceptionRegister() :
|
|
||||||
m_CoreMutex(),
|
|
||||||
m_IsRegistered(false), m_IsProcessing(false), m_PrevProcHandler(nullptr),
|
|
||||||
m_UserCallback(nullptr),
|
|
||||||
m_SingletonMutex(NULL) {}
|
|
||||||
~ExceptionRegister() {
|
|
||||||
Unregister();
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Try to register unhandled exception handler.
|
|
||||||
*/
|
|
||||||
void Register(ExceptionCallback callback) {
|
|
||||||
std::lock_guard<std::mutex> locker(m_CoreMutex);
|
|
||||||
// if we have registered, return
|
|
||||||
if (m_IsRegistered) return;
|
|
||||||
|
|
||||||
// check singleton
|
|
||||||
// build mutex string first
|
|
||||||
yycc_u8string mutex_name;
|
|
||||||
if (!StringHelper::Printf(mutex_name, YYCC_U8("Global\\%" PRIu32 ".{61634294-d23c-43f9-8490-b5e09837eede}"), GetCurrentProcessId()))
|
|
||||||
return;
|
|
||||||
std::wstring mutex_wname;
|
|
||||||
if (!EncodingHelper::UTF8ToWchar(mutex_name, mutex_wname))
|
|
||||||
return;
|
|
||||||
// create mutex
|
|
||||||
m_SingletonMutex = CreateMutexW(NULL, FALSE, mutex_wname.c_str());
|
|
||||||
DWORD errcode = GetLastError();
|
|
||||||
// check whether be created
|
|
||||||
if (m_SingletonMutex == NULL)
|
|
||||||
return;
|
|
||||||
if (errcode == ERROR_ALREADY_EXISTS) {
|
|
||||||
CloseHandle(m_SingletonMutex);
|
|
||||||
m_SingletonMutex = NULL;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// okey, we can register it.
|
|
||||||
// backup old handler
|
|
||||||
m_PrevProcHandler = SetUnhandledExceptionFilter(UExceptionImpl);
|
|
||||||
// set user callback
|
|
||||||
m_UserCallback = callback;
|
|
||||||
// mark registered
|
|
||||||
m_IsRegistered = true;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @brief Try to unregister unhandled exception handler.
|
|
||||||
*/
|
|
||||||
void Unregister() {
|
|
||||||
std::lock_guard<std::mutex> locker(m_CoreMutex);
|
|
||||||
// if we are not registered, skip
|
|
||||||
if (!m_IsRegistered) return;
|
|
||||||
|
|
||||||
// unregister handler
|
|
||||||
// reset user callback
|
|
||||||
m_UserCallback = nullptr;
|
|
||||||
// restore old handler
|
|
||||||
SetUnhandledExceptionFilter(m_PrevProcHandler);
|
|
||||||
m_PrevProcHandler = nullptr;
|
|
||||||
|
|
||||||
// release singleton handler
|
|
||||||
if (m_SingletonMutex != NULL) {
|
|
||||||
CloseHandle(m_SingletonMutex);
|
|
||||||
m_SingletonMutex = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// mark unregistered
|
|
||||||
m_IsRegistered = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Check whether handler is registered.
|
|
||||||
* @return True if it is, otherwise false.
|
|
||||||
*/
|
|
||||||
bool IsRegistered() const {
|
|
||||||
std::lock_guard<std::mutex> locker(m_CoreMutex);
|
|
||||||
return m_IsRegistered;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @brief Check whether we are processing unhandled exception.
|
|
||||||
* @return True if it is, otherwise false.
|
|
||||||
*/
|
|
||||||
bool IsProcessing() const {
|
|
||||||
std::lock_guard<std::mutex> locker(m_CoreMutex);
|
|
||||||
return m_IsProcessing;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @brief Get the old unhandled exception handler before registering.
|
|
||||||
* @return The fucntion pointer to old unhandled exception handler. May be nullptr.
|
|
||||||
*/
|
|
||||||
LPTOP_LEVEL_EXCEPTION_FILTER GetPrevProcHandler() const {
|
|
||||||
std::lock_guard<std::mutex> locker(m_CoreMutex);
|
|
||||||
return m_PrevProcHandler;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @brief Get user specified callback.
|
|
||||||
* @return The function pointer to user callback. nullptr if no associated callback.
|
|
||||||
*/
|
|
||||||
ExceptionCallback GetUserCallback() const {
|
|
||||||
std::lock_guard<std::mutex> locker(m_CoreMutex);
|
|
||||||
return m_UserCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Try to start process unhandled exception.
|
|
||||||
* @return True if you can start to process.
|
|
||||||
* False means there is already a process running. You should not process it now.
|
|
||||||
*/
|
|
||||||
bool StartProcessing() {
|
|
||||||
std::lock_guard<std::mutex> locker(m_CoreMutex);
|
|
||||||
if (m_IsProcessing) return false;
|
|
||||||
else {
|
|
||||||
m_IsProcessing = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @brief Mark current process of unhandled exception has done.
|
|
||||||
* @details This should only be called when StartProcessing() return true.
|
|
||||||
*/
|
|
||||||
void StopProcessing() {
|
|
||||||
std::lock_guard<std::mutex> locker(m_CoreMutex);
|
|
||||||
m_IsProcessing = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
/**
|
|
||||||
* @brief The core mutex for keeping this class is in synchronized.
|
|
||||||
*/
|
|
||||||
mutable std::mutex m_CoreMutex;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Whether we have registered unhandled exception handler.
|
|
||||||
* True if it is, otherwise false.
|
|
||||||
*/
|
|
||||||
bool m_IsRegistered;
|
|
||||||
/**
|
|
||||||
* @brief Whether we are processing unhandled exception.
|
|
||||||
* True if it is, otherwise false.
|
|
||||||
*/
|
|
||||||
bool m_IsProcessing;
|
|
||||||
/**
|
|
||||||
* @brief User defined callback.
|
|
||||||
* @details It will be called at the tail of unhandled exception handler, because it may raise exception.
|
|
||||||
* We must make sure all log and coredump have been done before calling it.
|
|
||||||
*/
|
|
||||||
ExceptionCallback m_UserCallback;
|
|
||||||
/**
|
|
||||||
* @brief The backup of old unhandled exception handler.
|
|
||||||
*/
|
|
||||||
LPTOP_LEVEL_EXCEPTION_FILTER m_PrevProcHandler;
|
|
||||||
/**
|
|
||||||
* @brief The Windows mutex handle for singleton implementation.
|
|
||||||
* Because we may have many DLLs using YYCC in the same process.
|
|
||||||
* But the unhandled exception handler only need to be registered once.
|
|
||||||
*/
|
|
||||||
HANDLE m_SingletonMutex;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @brief Core register singleton.
|
|
||||||
static ExceptionRegister g_ExceptionRegister;
|
|
||||||
|
|
||||||
#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
|
|
||||||
* @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::ToOrdinary(strl), fs);
|
|
||||||
std::fputs("\n", fs);
|
|
||||||
}
|
|
||||||
// write to stderr
|
|
||||||
ConsoleHelper::ErrWriteLine(strl);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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, ...) {
|
|
||||||
// do format first
|
|
||||||
va_list arg;
|
|
||||||
va_start(arg, fmt);
|
|
||||||
auto fmt_result = YYCC::StringHelper::VPrintf(fmt, arg);
|
|
||||||
va_end(arg);
|
|
||||||
// write to file and console
|
|
||||||
UExceptionErrLogWriteLine(fs, fmt_result.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
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* no_module_name = YYCC_U8("<unknown module>");
|
|
||||||
yycc_u8string module_name(no_module_name);
|
|
||||||
DWORD64 module_base;
|
|
||||||
if (module_base = SymGetModuleBase64(process, frame.AddrPC.Offset)) {
|
|
||||||
if (!WinFctHelper::GetModuleFileName((HINSTANCE)module_base, module_name)) {
|
|
||||||
module_name = no_module_name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
// MARK: should not use PRIXPTR to print adddress.
|
|
||||||
// because Windows always use DWORD64 as the type of address.
|
|
||||||
// use PRIX64 instead.
|
|
||||||
UExceptionErrLogFormatLine(fs, YYCC_U8("0x%" PRI_XPTR_LEFT_PADDING PRIX64 "[%s+0x%" PRI_XPTR_LEFT_PADDING PRIX64 "]\t%s#L%" PRIu64),
|
|
||||||
frame.AddrPC.Offset, // memory adress
|
|
||||||
module_name.c_str(), 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: "error.exe.1234.log" and "error.exe.1234.dmp".
|
|
||||||
// "error.exe" is the name of current process. "1234" is current process id.
|
|
||||||
// get process name
|
|
||||||
yycc_u8string u8_process_name;
|
|
||||||
{
|
|
||||||
// get full path of process
|
|
||||||
yycc_u8string u8_process_path;
|
|
||||||
if (!YYCC::WinFctHelper::GetModuleFileName(NULL, u8_process_path))
|
|
||||||
return false;
|
|
||||||
// extract file name from full path by std::filesystem::path
|
|
||||||
std::filesystem::path process_path(StdPatch::ToStdPath(u8_process_path));
|
|
||||||
u8_process_name = StdPatch::ToUTF8Path(process_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_process_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(StdPatch::ToStdPath(u8_localappdata_path));
|
|
||||||
// slash into crash report folder
|
|
||||||
crash_report_path /= StdPatch::ToStdPath(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 / StdPatch::ToStdPath(u8_log_filename);
|
|
||||||
std::filesystem::path coredump_filepath = crash_report_path / StdPatch::ToStdPath(u8_coredump_filename);
|
|
||||||
// output to result
|
|
||||||
log_path = StdPatch::ToUTF8Path(log_filepath);
|
|
||||||
coredump_path = StdPatch::ToUTF8Path(coredump_filepath);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static LONG WINAPI UExceptionImpl(LPEXCEPTION_POINTERS info) {
|
|
||||||
// try to start process current unhandled exception
|
|
||||||
// to prevent any possible recursive calling.
|
|
||||||
if (!g_ExceptionRegister.StartProcessing()) goto end_proc;
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
// call user callback
|
|
||||||
ExceptionCallback user_callback = g_ExceptionRegister.GetUserCallback();
|
|
||||||
if (user_callback != nullptr)
|
|
||||||
user_callback(log_path, coredump_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
// stop process
|
|
||||||
g_ExceptionRegister.StopProcessing();
|
|
||||||
|
|
||||||
end_proc:
|
|
||||||
// if backup proc can be run, run it
|
|
||||||
// otherwise directly return.
|
|
||||||
auto prev_proc = g_ExceptionRegister.GetPrevProcHandler();
|
|
||||||
if (prev_proc != nullptr) {
|
|
||||||
return prev_proc(info);
|
|
||||||
} else {
|
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma endregion
|
|
||||||
|
|
||||||
void Register(ExceptionCallback callback) {
|
|
||||||
g_ExceptionRegister.Register(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Unregister() {
|
|
||||||
g_ExceptionRegister.Unregister();
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(YYCC_DEBUG_UE_FILTER)
|
|
||||||
long __stdcall DebugCallUExceptionImpl(void* data) {
|
|
||||||
return UExceptionImpl(static_cast<LPEXCEPTION_POINTERS>(data));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,64 +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.
|
|
||||||
* For how to utilize this namespace, please see \ref exception_helper.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
namespace YYCC::ExceptionHelper {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The callback function prototype which will be called when unhandled exception happened after registering.
|
|
||||||
* @details
|
|
||||||
* During registering unhandled exception handler,
|
|
||||||
* caller can optionally provide a function pointer matching this prorotype to register.
|
|
||||||
* Then it will be called if unhandled exception hanppened.
|
|
||||||
*
|
|
||||||
* This callback will provide 2 readonly arguments.
|
|
||||||
* First is the path to error log file.
|
|
||||||
* Second is the path to core dump file.
|
|
||||||
* These pathes may be empty if internal handler fail to create them.
|
|
||||||
*
|
|
||||||
* This callback is convenient for programmer using an explicit way to tell user an exception happened.
|
|
||||||
* Because in default, handler will only write error log to \c stderr and file.
|
|
||||||
* It will be totally invisible on a GUI application.
|
|
||||||
*/
|
|
||||||
using ExceptionCallback = void(*)(const yycc_u8string& log_path, const yycc_u8string& coredump_path);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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 generate log file and dump file in \c \%APPDATA\%/CrashDumps folder if it is possible.
|
|
||||||
* (for convenient debugging of developer when reporting bugs.)
|
|
||||||
*
|
|
||||||
* This function usually is called at the start of program.
|
|
||||||
* @param[in] callback User defined callback called when unhandled exception happened. nullptr if no callback.
|
|
||||||
*/
|
|
||||||
void Register(ExceptionCallback callback = nullptr);
|
|
||||||
/**
|
|
||||||
* @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 to release reources if you have called Register().
|
|
||||||
*
|
|
||||||
* This function usually is called at the end of program.
|
|
||||||
*/
|
|
||||||
void Unregister();
|
|
||||||
|
|
||||||
#if defined(YYCC_DEBUG_UE_FILTER)
|
|
||||||
long __stdcall DebugCallUExceptionImpl(void*);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
#include "IOHelper.hpp"
|
|
||||||
|
|
||||||
#include "EncodingHelper.hpp"
|
|
||||||
#include <cstdio>
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
|
||||||
#include "WinImportPrefix.hpp"
|
|
||||||
#include <Windows.h>
|
|
||||||
#include "WinImportSuffix.hpp"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace YYCC::IOHelper {
|
|
||||||
|
|
||||||
std::FILE* UTF8FOpen(const yycc_char8_t* u8_filepath, const yycc_char8_t* u8_mode) {
|
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
|
||||||
|
|
||||||
// convert mode and file path to wchar
|
|
||||||
std::wstring wmode, wpath;
|
|
||||||
if (!YYCC::EncodingHelper::UTF8ToWchar(u8_mode, wmode))
|
|
||||||
return nullptr;
|
|
||||||
if (!YYCC::EncodingHelper::UTF8ToWchar(u8_filepath, wpath))
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
// call microsoft specified fopen which support wchar as argument.
|
|
||||||
return _wfopen(wpath.c_str(), wmode.c_str());
|
|
||||||
|
|
||||||
#else
|
|
||||||
return std::fopen(EncodingHelper::ToOrdinary(u8_filepath), EncodingHelper::ToOrdinary(u8_mode));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "YYCCInternal.hpp"
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include <filesystem>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Some IO related stuff
|
|
||||||
* @details
|
|
||||||
* See also \ref io_helper.
|
|
||||||
*/
|
|
||||||
namespace YYCC::IOHelper {
|
|
||||||
|
|
||||||
#if UINTPTR_MAX == UINT32_MAX
|
|
||||||
#define PRI_XPTR_LEFT_PADDING "08"
|
|
||||||
#elif UINTPTR_MAX == UINT64_MAX
|
|
||||||
/**
|
|
||||||
* @brief The left-padding zero format string of HEX-printed pointer type.
|
|
||||||
* @details
|
|
||||||
* When printing a pointer with HEX style, we always hope it can be left-padded with some zero for easy reading.
|
|
||||||
* In different architecture, the size of this padding is differnet too so we create this macro.
|
|
||||||
*
|
|
||||||
* In 32-bit environment, it will be "08" meaning left pad zero until 8 number position.
|
|
||||||
* In 64-bit environment, it will be "016" meaning left pad zero until 16 number position.
|
|
||||||
*/
|
|
||||||
#define PRI_XPTR_LEFT_PADDING "016"
|
|
||||||
#else
|
|
||||||
#error "Not supported pointer size."
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// @brief C++ standard deleter for std::FILE*
|
|
||||||
class StdFileDeleter {
|
|
||||||
public:
|
|
||||||
StdFileDeleter() {}
|
|
||||||
void operator() (std::FILE* ptr) {
|
|
||||||
if (ptr != nullptr) {
|
|
||||||
std::fclose(ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/// @brief Smart unique pointer of \c std::FILE*
|
|
||||||
using SmartStdFile = std::unique_ptr<std::FILE, StdFileDeleter>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The UTF8 version of \c std::fopen.
|
|
||||||
* @param[in] u8_filepath The UTF8 encoded path to the file to be opened.
|
|
||||||
* @param[in] u8_mode UTF8 encoded mode string of the file to be opened.
|
|
||||||
* @remarks
|
|
||||||
* This function is suit for Windows because std::fopen do not support UTF8 on Windows.
|
|
||||||
* On other platforms, this function will delegate request directly to std::fopen.
|
|
||||||
* @return \c FILE* of the file to be opened, or nullptr if failed.
|
|
||||||
*/
|
|
||||||
std::FILE* UTF8FOpen(const yycc_char8_t* u8_filepath, const yycc_char8_t* u8_mode);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,221 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "YYCCInternal.hpp"
|
|
||||||
|
|
||||||
#include "EncodingHelper.hpp"
|
|
||||||
#include "StringHelper.hpp"
|
|
||||||
#include <string>
|
|
||||||
#include <cinttypes>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <charconv>
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The helper involving convertion between arithmetic types (integral, floating point and bool) and string
|
|
||||||
* @details
|
|
||||||
* See also \ref parser_helper.
|
|
||||||
*/
|
|
||||||
namespace YYCC::ParserHelper {
|
|
||||||
|
|
||||||
// Reference: https://zh.cppreference.com/w/cpp/utility/from_chars
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Try parsing given string to floating point types.
|
|
||||||
* @tparam _Ty The type derived from floating point type.
|
|
||||||
* @param[in] strl The string need to be parsed.
|
|
||||||
* @param[out] num
|
|
||||||
* The variable receiving result.
|
|
||||||
* There is no guarantee that the content is not modified when parsing failed.
|
|
||||||
* @param[in] fmt The floating point format used when try parsing.
|
|
||||||
* @return True if success, otherwise false.
|
|
||||||
*/
|
|
||||||
template<typename _Ty, std::enable_if_t<std::is_floating_point_v<_Ty>, int> = 0>
|
|
||||||
bool TryParse(const yycc_u8string_view& strl, _Ty& num, std::chars_format fmt = std::chars_format::general) {
|
|
||||||
auto [ptr, ec] = std::from_chars(
|
|
||||||
EncodingHelper::ToOrdinary(strl.data()),
|
|
||||||
EncodingHelper::ToOrdinary(strl.data() + strl.size()),
|
|
||||||
num, fmt
|
|
||||||
);
|
|
||||||
if (ec == std::errc()) {
|
|
||||||
// check whether the full string is matched
|
|
||||||
return ptr == EncodingHelper::ToOrdinary(strl.data() + strl.size());
|
|
||||||
} else if (ec == std::errc::invalid_argument) {
|
|
||||||
// given string is invalid
|
|
||||||
return false;
|
|
||||||
} else if (ec == std::errc::result_out_of_range) {
|
|
||||||
// given string is out of range
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
// unreachable
|
|
||||||
throw std::runtime_error("unreachable code.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @brief Try parsing given string to integral types.
|
|
||||||
* @tparam _Ty The type derived from integral type except bool type.
|
|
||||||
* @param[in] strl The string need to be parsed.
|
|
||||||
* @param[out] num
|
|
||||||
* The variable receiving result.
|
|
||||||
* There is no guarantee that the content is not modified when parsing failed.
|
|
||||||
* @param[in] base Integer base to use: a value between 2 and 36 (inclusive).
|
|
||||||
* @return True if success, otherwise false.
|
|
||||||
*/
|
|
||||||
template<typename _Ty, std::enable_if_t<std::is_integral_v<_Ty> && !std::is_same_v<_Ty, bool>, int> = 0>
|
|
||||||
bool TryParse(const yycc_u8string_view& strl, _Ty& num, int base = 10) {
|
|
||||||
auto [ptr, ec] = std::from_chars(
|
|
||||||
EncodingHelper::ToOrdinary(strl.data()),
|
|
||||||
EncodingHelper::ToOrdinary(strl.data() + strl.size()),
|
|
||||||
num, base
|
|
||||||
);
|
|
||||||
if (ec == std::errc()) {
|
|
||||||
// check whether the full string is matched
|
|
||||||
return ptr == EncodingHelper::ToOrdinary(strl.data() + strl.size());
|
|
||||||
} else if (ec == std::errc::invalid_argument) {
|
|
||||||
// given string is invalid
|
|
||||||
return false;
|
|
||||||
} else if (ec == std::errc::result_out_of_range) {
|
|
||||||
// given string is out of range
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
// unreachable
|
|
||||||
throw std::runtime_error("unreachable code.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @brief Try parsing given string to bool types.
|
|
||||||
* @tparam _Ty The type derived from bool type.
|
|
||||||
* @param[in] strl The string need to be parsed ("true" or "false", case insensitive).
|
|
||||||
* @param[out] num
|
|
||||||
* The variable receiving result.
|
|
||||||
* There is no guarantee that the content is not modified when parsing failed.
|
|
||||||
* @return True if success, otherwise false.
|
|
||||||
*/
|
|
||||||
template<typename _Ty, std::enable_if_t<std::is_same_v<_Ty, bool>, int> = 0>
|
|
||||||
bool TryParse(const yycc_u8string_view& strl, _Ty& num) {
|
|
||||||
// get lower case
|
|
||||||
yycc_u8string lower_case(strl);
|
|
||||||
YYCC::StringHelper::Lower(lower_case);
|
|
||||||
// compare result
|
|
||||||
if (strl == YYCC_U8("true")) num = true;
|
|
||||||
else if (strl == YYCC_U8("false")) num = false;
|
|
||||||
else return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Parse given string to floating point types.
|
|
||||||
* @tparam _Ty The type derived from floating point type.
|
|
||||||
* @param[in] strl The string need to be parsed.
|
|
||||||
* @param[in] fmt The floating point format used when try parsing.
|
|
||||||
* @return
|
|
||||||
* The parsing result.
|
|
||||||
* There is no guarantee about the content of this return value when parsing failed.
|
|
||||||
* It may be any possible value but usually is its default value.
|
|
||||||
*/
|
|
||||||
template<typename _Ty, std::enable_if_t<std::is_floating_point_v<_Ty>, int> = 0>
|
|
||||||
_Ty Parse(const yycc_u8string_view& strl, std::chars_format fmt = std::chars_format::general) {
|
|
||||||
_Ty ret;
|
|
||||||
TryParse(strl, ret, fmt);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @brief Parse given string to integral type types.
|
|
||||||
* @tparam _Ty The type derived from integral type except bool type.
|
|
||||||
* @param[in] strl The string need to be parsed.
|
|
||||||
* @param[in] base Integer base to use: a value between 2 and 36 (inclusive).
|
|
||||||
* @return
|
|
||||||
* The parsing result.
|
|
||||||
* There is no guarantee about the content of this return value when parsing failed.
|
|
||||||
* It may be any possible value but usually is its default value.
|
|
||||||
*/
|
|
||||||
template<typename _Ty, std::enable_if_t<std::is_integral_v<_Ty> && !std::is_same_v<_Ty, bool>, int> = 0>
|
|
||||||
_Ty Parse(const yycc_u8string_view& strl, int base = 10) {
|
|
||||||
_Ty ret;
|
|
||||||
TryParse(strl, ret, base);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @brief Parse given string to bool types.
|
|
||||||
* @tparam _Ty The type derived from bool type.
|
|
||||||
* @param[in] strl The string need to be parsed ("true" or "false", case insensitive).
|
|
||||||
* @return
|
|
||||||
* The parsing result.
|
|
||||||
* There is no guarantee about the content of this return value when parsing failed.
|
|
||||||
* It may be any possible value but usually is its default value.
|
|
||||||
*/
|
|
||||||
template<typename _Ty, std::enable_if_t<std::is_same_v<_Ty, bool>, int> = 0>
|
|
||||||
_Ty Parse(const yycc_u8string_view& strl) {
|
|
||||||
_Ty ret;
|
|
||||||
TryParse(strl, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reference: https://en.cppreference.com/w/cpp/utility/to_chars
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Return the string representation of given floating point value.
|
|
||||||
* @tparam _Ty The type derived from floating point type.
|
|
||||||
* @param[in] num The value need to get string representation.
|
|
||||||
* @param[in] fmt The floating point format used when getting string representation.
|
|
||||||
* @param[in] precision The floating point precision used when getting string representation.
|
|
||||||
* @return The string representation of given value.
|
|
||||||
*/
|
|
||||||
template<typename _Ty, std::enable_if_t<std::is_floating_point_v<_Ty>, int> = 0>
|
|
||||||
yycc_u8string ToString(_Ty num, std::chars_format fmt = std::chars_format::general, int precision = 6) {
|
|
||||||
// default precision = 6 is gotten from: https://en.cppreference.com/w/c/io/fprintf
|
|
||||||
std::array<yycc_char8_t, 64> buffer;
|
|
||||||
auto [ptr, ec] = std::to_chars(
|
|
||||||
EncodingHelper::ToOrdinary(buffer.data()),
|
|
||||||
EncodingHelper::ToOrdinary(buffer.data() + buffer.size()),
|
|
||||||
num, fmt, precision
|
|
||||||
);
|
|
||||||
if (ec == std::errc()) {
|
|
||||||
return yycc_u8string(buffer.data(), EncodingHelper::ToUTF8(ptr) - buffer.data());
|
|
||||||
} else if (ec == std::errc::value_too_large) {
|
|
||||||
// too short buffer
|
|
||||||
// this should not happened
|
|
||||||
throw std::out_of_range("ToString() buffer is not sufficient.");
|
|
||||||
} else {
|
|
||||||
// unreachable
|
|
||||||
throw std::runtime_error("unreachable code.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @brief Return the string representation of given integral value.
|
|
||||||
* @tparam _Ty The type derived from integral type except bool type.
|
|
||||||
* @param[in] num The value need to get string representation.
|
|
||||||
* @param[in] base Integer base used when getting string representation: a value between 2 and 36 (inclusive).
|
|
||||||
* @return The string representation of given value.
|
|
||||||
*/
|
|
||||||
template<typename _Ty, std::enable_if_t<std::is_integral_v<_Ty> && !std::is_same_v<_Ty, bool>, int> = 0>
|
|
||||||
yycc_u8string ToString(_Ty num, int base = 10) {
|
|
||||||
std::array<yycc_char8_t, 64> buffer;
|
|
||||||
auto [ptr, ec] = std::to_chars(
|
|
||||||
EncodingHelper::ToOrdinary(buffer.data()),
|
|
||||||
EncodingHelper::ToOrdinary(buffer.data() + buffer.size()),
|
|
||||||
num, base
|
|
||||||
);
|
|
||||||
if (ec == std::errc()) {
|
|
||||||
return yycc_u8string(buffer.data(), EncodingHelper::ToUTF8(ptr) - buffer.data());
|
|
||||||
} else if (ec == std::errc::value_too_large) {
|
|
||||||
// too short buffer
|
|
||||||
// this should not happened
|
|
||||||
throw std::out_of_range("ToString() buffer is not sufficient.");
|
|
||||||
} else {
|
|
||||||
// unreachable
|
|
||||||
throw std::runtime_error("unreachable code.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @brief Return the string representation of given bool value.
|
|
||||||
* @tparam _Ty The type derived from bool type.
|
|
||||||
* @param[in] num The value need to get string representation.
|
|
||||||
* @return The string representation of given value ("true" or "false").
|
|
||||||
*/
|
|
||||||
template<typename _Ty, std::enable_if_t<std::is_same_v<_Ty, bool>, int> = 0>
|
|
||||||
yycc_u8string ToString(_Ty num) {
|
|
||||||
if (num) return yycc_u8string(YYCC_U8("true"));
|
|
||||||
else return yycc_u8string(YYCC_U8("false"));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
#include "StdPatch.hpp"
|
|
||||||
|
|
||||||
#include "EncodingHelper.hpp"
|
|
||||||
#include <string>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
namespace YYCC::StdPatch {
|
|
||||||
|
|
||||||
std::filesystem::path ToStdPath(const yycc_u8string_view& u8_path) {
|
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
|
||||||
|
|
||||||
// convert path to wchar
|
|
||||||
std::wstring wpath;
|
|
||||||
if (!YYCC::EncodingHelper::UTF8ToWchar(u8_path, wpath))
|
|
||||||
throw std::invalid_argument("Fail to convert given UTF8 string.");
|
|
||||||
// return path with wchar_t ctor
|
|
||||||
return std::filesystem::path(wpath);
|
|
||||||
|
|
||||||
#else
|
|
||||||
std::string cache = YYCC::EncodingHelper::ToOrdinary(u8_path);
|
|
||||||
return std::filesystem::path(cache.c_str());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
218
src/StdPatch.hpp
218
src/StdPatch.hpp
@@ -1,218 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "YYCCInternal.hpp"
|
|
||||||
|
|
||||||
#include <filesystem>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief \c Standard library related patches for UTF8 compatibility and the limitation of C++ standard version.
|
|
||||||
* @details
|
|
||||||
* See also \ref std_patch.
|
|
||||||
*/
|
|
||||||
namespace YYCC::StdPatch {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Constructs \c std::filesystem::path from UTF8 path.
|
|
||||||
* @param[in] u8_path UTF8 path string for building.
|
|
||||||
* @return \c std::filesystem::path instance.
|
|
||||||
* @exception std::invalid_argument Fail to parse given UTF8 string (maybe invalid?).
|
|
||||||
*/
|
|
||||||
std::filesystem::path ToStdPath(const yycc_u8string_view& u8_path);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Returns the UTF8 representation of given \c std::filesystem::path.
|
|
||||||
* @param[in] path The \c std::filesystem::path instance converting to UTF8 path.
|
|
||||||
* @return The UTF8 representation of given \c std::filesystem::path.
|
|
||||||
* @exception std::invalid_argument Fail to convert to UTF8 string.
|
|
||||||
*/
|
|
||||||
yycc_u8string ToUTF8Path(const std::filesystem::path& path);
|
|
||||||
|
|
||||||
#pragma region StartsWith EndsWith
|
|
||||||
|
|
||||||
// Reference:
|
|
||||||
// https://en.cppreference.com/w/cpp/string/basic_string_view/starts_with
|
|
||||||
// https://en.cppreference.com/w/cpp/string/basic_string_view/ends_with
|
|
||||||
// https://en.cppreference.com/w/cpp/string/basic_string/starts_with
|
|
||||||
// https://en.cppreference.com/w/cpp/string/basic_string/ends_with
|
|
||||||
|
|
||||||
#pragma region String View
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Checks if the string view begins with the given prefix
|
|
||||||
* @param[in] that The string view to find.
|
|
||||||
* @param[in] sv A string view which may be a result of implicit conversion from \c std::basic_string.
|
|
||||||
* @return True if the string view begins with the provided prefix, false otherwise.
|
|
||||||
*/
|
|
||||||
template<class CharT, class Traits = std::char_traits<CharT>>
|
|
||||||
bool StartsWith(const std::basic_string_view<CharT, Traits>& that, std::basic_string_view<CharT, Traits> sv) noexcept {
|
|
||||||
return std::basic_string_view<CharT, Traits>(that.data(), std::min(that.size(), sv.size())) == sv;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @brief Checks if the string view begins with the given prefix
|
|
||||||
* @param[in] that The string view to find.
|
|
||||||
* @param[in] ch A single character.
|
|
||||||
* @return True if the string view begins with the provided prefix, false otherwise.
|
|
||||||
*/
|
|
||||||
template<class CharT, class Traits = std::char_traits<CharT>>
|
|
||||||
bool StartsWith(const std::basic_string_view<CharT, Traits>& that, CharT ch) noexcept {
|
|
||||||
return !that.empty() && Traits::eq(that.front(), ch);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @brief Checks if the string view begins with the given prefix
|
|
||||||
* @param[in] that The string view to find.
|
|
||||||
* @param[in] s A null-terminated character string.
|
|
||||||
* @return True if the string view begins with the provided prefix, false otherwise.
|
|
||||||
*/
|
|
||||||
template<class CharT, class Traits = std::char_traits<CharT>>
|
|
||||||
bool StartsWith(const std::basic_string_view<CharT, Traits>& that, const CharT* s) noexcept {
|
|
||||||
return StartsWith(that, std::basic_string_view(s));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Checks if the string view ends with the given suffix
|
|
||||||
* @param[in] that The string view to find.
|
|
||||||
* @param[in] sv A string view which may be a result of implicit conversion from \c std::basic_string.
|
|
||||||
* @return True if the string view ends with the provided suffix, false otherwise.
|
|
||||||
*/
|
|
||||||
template<class CharT, class Traits = std::char_traits<CharT>>
|
|
||||||
bool EndsWith(const std::basic_string_view<CharT, Traits>& that, std::basic_string_view<CharT, Traits> sv) noexcept {
|
|
||||||
return that.size() >= sv.size() && that.compare(that.size() - sv.size(), std::basic_string_view<CharT, Traits>::npos, sv) == 0;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @brief Checks if the string view ends with the given suffix
|
|
||||||
* @param[in] that The string view to find.
|
|
||||||
* @param[in] ch A single character.
|
|
||||||
* @return True if the string view ends with the provided suffix, false otherwise.
|
|
||||||
*/
|
|
||||||
template<class CharT, class Traits = std::char_traits<CharT>>
|
|
||||||
bool EndsWith(const std::basic_string_view<CharT, Traits>& that, CharT ch) noexcept {
|
|
||||||
return !that.empty() && Traits::eq(that.back(), ch);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @brief Checks if the string view ends with the given suffix
|
|
||||||
* @param[in] that The string view to find.
|
|
||||||
* @param[in] s A null-terminated character string.
|
|
||||||
* @return True if the string view ends with the provided suffix, false otherwise.
|
|
||||||
*/
|
|
||||||
template<class CharT, class Traits = std::char_traits<CharT>>
|
|
||||||
bool EndsWith(const std::basic_string_view<CharT, Traits>& that, const CharT* s) noexcept {
|
|
||||||
return EndsWith(that, std::basic_string_view(s));
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma endregion
|
|
||||||
|
|
||||||
#pragma region String
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Checks if the string begins with the given prefix
|
|
||||||
* @param[in] that The string to find.
|
|
||||||
* @param[in] sv A string view which may be a result of implicit conversion from \c std::basic_string.
|
|
||||||
* @return True if the string view begins with the provided prefix, false otherwise.
|
|
||||||
*/
|
|
||||||
template<class CharT, class Traits = std::char_traits<CharT>>
|
|
||||||
bool StartsWith(const std::basic_string<CharT, Traits>& that, std::basic_string_view<CharT, Traits> sv) noexcept {
|
|
||||||
return StartsWith(std::basic_string_view<CharT, Traits>(that.data(), that.size()), sv);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @brief Checks if the string begins with the given prefix
|
|
||||||
* @param[in] that The string to find.
|
|
||||||
* @param[in] ch A single character.
|
|
||||||
* @return True if the string view begins with the provided prefix, false otherwise.
|
|
||||||
*/
|
|
||||||
template<class CharT, class Traits = std::char_traits<CharT>>
|
|
||||||
bool StartsWith(const std::basic_string<CharT, Traits>& that, CharT ch) noexcept {
|
|
||||||
return StartsWith(std::basic_string_view<CharT, Traits>(that.data(), that.size()), ch);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @brief Checks if the string begins with the given prefix
|
|
||||||
* @param[in] that The string to find.
|
|
||||||
* @param[in] s A null-terminated character string.
|
|
||||||
* @return True if the string view begins with the provided prefix, false otherwise.
|
|
||||||
*/
|
|
||||||
template<class CharT, class Traits = std::char_traits<CharT>>
|
|
||||||
bool StartsWith(const std::basic_string<CharT, Traits>& that, const CharT* s) noexcept {
|
|
||||||
return StartsWith(std::basic_string_view<CharT, Traits>(that.data(), that.size()), s);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Checks if the string ends with the given suffix
|
|
||||||
* @param[in] that The string to find.
|
|
||||||
* @param[in] sv A string view which may be a result of implicit conversion from \c std::basic_string.
|
|
||||||
* @return True if the string view ends with the provided suffix, false otherwise.
|
|
||||||
*/
|
|
||||||
template<class CharT, class Traits = std::char_traits<CharT>>
|
|
||||||
bool EndsWith(const std::basic_string<CharT, Traits>& that, std::basic_string_view<CharT, Traits> sv) noexcept {
|
|
||||||
return EndsWith(std::basic_string_view<CharT, Traits>(that.data(), that.size()), sv);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @brief Checks if the string ends with the given suffix
|
|
||||||
* @param[in] that The string to find.
|
|
||||||
* @param[in] ch A single character.
|
|
||||||
* @return True if the string view ends with the provided suffix, false otherwise.
|
|
||||||
*/
|
|
||||||
template<class CharT, class Traits = std::char_traits<CharT>>
|
|
||||||
bool EndsWith(const std::basic_string<CharT, Traits>& that, CharT ch) noexcept {
|
|
||||||
return EndsWith(std::basic_string_view<CharT, Traits>(that.data(), that.size()), ch);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @brief Checks if the string ends with the given suffix
|
|
||||||
* @param[in] that The string to find.
|
|
||||||
* @param[in] s A null-terminated character string.
|
|
||||||
* @return True if the string view ends with the provided suffix, false otherwise.
|
|
||||||
*/
|
|
||||||
template<class CharT, class Traits = std::char_traits<CharT>>
|
|
||||||
bool EndsWith(const std::basic_string<CharT, Traits>& that, const CharT* s) noexcept {
|
|
||||||
return EndsWith(std::basic_string_view<CharT, Traits>(that.data(), that.size()), s);
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma endregion
|
|
||||||
|
|
||||||
#pragma endregion
|
|
||||||
|
|
||||||
#pragma region Contain
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Checks if there is an element with key equivalent to key in the container.
|
|
||||||
* @details
|
|
||||||
* The polyfill to \c Contains function of unordered and ordered associative container.
|
|
||||||
* Because this function only present after C++ 20.
|
|
||||||
* This function will use our custom polyfill if the version of C++ standard you are using lower than C++ 20.
|
|
||||||
* Otherwise it will fallback to vanilla standard library function.
|
|
||||||
* @tparam _TContainer
|
|
||||||
* The type of container. This container must have \c find() and \c end() member functions.
|
|
||||||
* @tparam _TKey
|
|
||||||
* The type of key of container.
|
|
||||||
* If the container is a set, this type is the type of item in set.
|
|
||||||
* If the container is a map, this type is the key type of map.
|
|
||||||
* @param[in] container The reference to container to find.
|
|
||||||
* @param[in] key Key value of the element to search for
|
|
||||||
* @return True if there is such an element, otherwise false.
|
|
||||||
* @remarks
|
|
||||||
* This template function do not have constraint check.
|
|
||||||
* If container type has \c find() and \c end() member functions, this template function will be created without any error.
|
|
||||||
* However, this function should be used for standard library associative container according to its original purpose.
|
|
||||||
* It means that the type of container usually and should be one of following types:
|
|
||||||
* \li \c std::set
|
|
||||||
* \li \c std::multiset
|
|
||||||
* \li \c std::map
|
|
||||||
* \li \c std::multimap
|
|
||||||
* \li \c std::unordered_set
|
|
||||||
* \li \c std::unordered_multiset
|
|
||||||
* \li \c std::unordered_map
|
|
||||||
* \li \c std::unordered_multimap
|
|
||||||
*/
|
|
||||||
template<class _TContainer, class _TKey>
|
|
||||||
bool Contains(const _TContainer& container, const _TKey& key) {
|
|
||||||
// __cplusplus macro need special compiler switch enabled when compiling.
|
|
||||||
// So we use _MSVC_LANG check it instead.
|
|
||||||
#if __cplusplus >= 202002L || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)
|
|
||||||
return container.contains(key);
|
|
||||||
#else
|
|
||||||
return container.find(key) != container.end();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma endregion
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,223 +0,0 @@
|
|||||||
#include "StringHelper.hpp"
|
|
||||||
#include "EncodingHelper.hpp"
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace YYCC::StringHelper {
|
|
||||||
|
|
||||||
#pragma region Printf VPrintf
|
|
||||||
|
|
||||||
bool Printf(yycc_u8string& strl, const yycc_char8_t* format, ...) {
|
|
||||||
va_list argptr;
|
|
||||||
va_start(argptr, format);
|
|
||||||
bool ret = VPrintf(strl, format, argptr);
|
|
||||||
va_end(argptr);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VPrintf(yycc_u8string& strl, const yycc_char8_t* format, va_list argptr) {
|
|
||||||
va_list args1;
|
|
||||||
va_copy(args1, argptr);
|
|
||||||
va_list args2;
|
|
||||||
va_copy(args2, argptr);
|
|
||||||
|
|
||||||
// the return value is desired char count without NULL terminal.
|
|
||||||
// minus number means error
|
|
||||||
int count = std::vsnprintf(
|
|
||||||
nullptr,
|
|
||||||
0,
|
|
||||||
EncodingHelper::ToOrdinary(format),
|
|
||||||
args1
|
|
||||||
);
|
|
||||||
if (count < 0) {
|
|
||||||
// invalid length returned by vsnprintf.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
va_end(args1);
|
|
||||||
|
|
||||||
// resize std::string to desired count.
|
|
||||||
// and pass its length + 1 to std::vsnprintf,
|
|
||||||
// because std::vsnprintf only can write "buf_size - 1" chars with a trailing NULL.
|
|
||||||
// however std::vsnprintf already have a trailing NULL, so we plus 1 for it.
|
|
||||||
strl.resize(count);
|
|
||||||
int write_result = std::vsnprintf(
|
|
||||||
EncodingHelper::ToOrdinary(strl.data()),
|
|
||||||
strl.size() + 1,
|
|
||||||
EncodingHelper::ToOrdinary(format),
|
|
||||||
args2
|
|
||||||
);
|
|
||||||
va_end(args2);
|
|
||||||
|
|
||||||
if (write_result < 0 || write_result > count) {
|
|
||||||
// invalid write result in vsnprintf.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
yycc_u8string Printf(const yycc_char8_t* format, ...) {
|
|
||||||
yycc_u8string ret;
|
|
||||||
|
|
||||||
va_list argptr;
|
|
||||||
va_start(argptr, format);
|
|
||||||
VPrintf(ret, format, argptr);
|
|
||||||
va_end(argptr);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
yycc_u8string VPrintf(const yycc_char8_t* format, va_list argptr) {
|
|
||||||
yycc_u8string ret;
|
|
||||||
|
|
||||||
va_list argcpy;
|
|
||||||
va_copy(argcpy, argptr);
|
|
||||||
VPrintf(ret, format, argcpy);
|
|
||||||
va_end(argcpy);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma endregion
|
|
||||||
|
|
||||||
#pragma region Replace
|
|
||||||
|
|
||||||
void Replace(yycc_u8string& strl, const yycc_u8string_view& _from_strl, const yycc_u8string_view& _to_strl) {
|
|
||||||
// Reference: https://stackoverflow.com/questions/3418231/replace-part-of-a-string-with-another-string
|
|
||||||
|
|
||||||
// check requirements
|
|
||||||
// from string should not be empty
|
|
||||||
yycc_u8string from_strl(_from_strl);
|
|
||||||
yycc_u8string to_strl(_to_strl);
|
|
||||||
if (from_strl.empty()) return;
|
|
||||||
|
|
||||||
// start replace one by one
|
|
||||||
size_t start_pos = 0;
|
|
||||||
while ((start_pos = strl.find(from_strl, start_pos)) != yycc_u8string::npos) {
|
|
||||||
strl.replace(start_pos, from_strl.size(), to_strl);
|
|
||||||
start_pos += to_strl.size(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
yycc_u8string Replace(const yycc_u8string_view& _strl, const yycc_u8string_view& _from_strl, const yycc_u8string_view& _to_strl) {
|
|
||||||
// prepare result
|
|
||||||
yycc_u8string strl(_strl);
|
|
||||||
Replace(strl, _from_strl, _to_strl);
|
|
||||||
// return value
|
|
||||||
return strl;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma endregion
|
|
||||||
|
|
||||||
#pragma region Join
|
|
||||||
|
|
||||||
yycc_u8string Join(JoinDataProvider fct_data, const yycc_u8string_view& decilmer) {
|
|
||||||
yycc_u8string ret;
|
|
||||||
bool is_first = true;
|
|
||||||
yycc_u8string_view element;
|
|
||||||
|
|
||||||
// fetch element
|
|
||||||
while (fct_data(element)) {
|
|
||||||
// insert decilmer
|
|
||||||
if (is_first) is_first = false;
|
|
||||||
else {
|
|
||||||
// append decilmer.
|
|
||||||
ret.append(decilmer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// insert element if it is not empty
|
|
||||||
if (!element.empty())
|
|
||||||
ret.append(element);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma endregion
|
|
||||||
|
|
||||||
#pragma region Upper Lower
|
|
||||||
|
|
||||||
template<bool bIsToLower>
|
|
||||||
static void GeneralStringLowerUpper(yycc_u8string& strl) {
|
|
||||||
// References:
|
|
||||||
// https://en.cppreference.com/w/cpp/algorithm/transform
|
|
||||||
// https://en.cppreference.com/w/cpp/string/byte/tolower
|
|
||||||
std::transform(
|
|
||||||
strl.cbegin(), strl.cend(), strl.begin(),
|
|
||||||
[](unsigned char c) -> char {
|
|
||||||
if constexpr (bIsToLower) return std::tolower(c);
|
|
||||||
else return std::toupper(c);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Lower(yycc_u8string& strl) {
|
|
||||||
GeneralStringLowerUpper<true>(strl);
|
|
||||||
}
|
|
||||||
|
|
||||||
yycc_u8string Lower(const yycc_u8string_view& strl) {
|
|
||||||
yycc_u8string ret(strl);
|
|
||||||
Lower(ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Upper(yycc_u8string& strl) {
|
|
||||||
GeneralStringLowerUpper<false>(strl);
|
|
||||||
}
|
|
||||||
|
|
||||||
yycc_u8string Upper(const yycc_u8string_view& strl) {
|
|
||||||
// same as Lower, just replace char transform function.
|
|
||||||
yycc_u8string ret(strl);
|
|
||||||
Upper(ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma endregion
|
|
||||||
|
|
||||||
#pragma region Split
|
|
||||||
|
|
||||||
std::vector<yycc_u8string> Split(const yycc_u8string_view& strl, const yycc_u8string_view& _decilmer) {
|
|
||||||
// call split view
|
|
||||||
auto view_result = SplitView(strl, _decilmer);
|
|
||||||
|
|
||||||
// copy string view result to string
|
|
||||||
std::vector<yycc_u8string> elems;
|
|
||||||
elems.reserve(view_result.size());
|
|
||||||
for (const auto& strl_view : view_result) {
|
|
||||||
elems.emplace_back(yycc_u8string(strl_view));
|
|
||||||
}
|
|
||||||
// return copied result
|
|
||||||
return elems;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<yycc_u8string_view> SplitView(const yycc_u8string_view& strl, const yycc_u8string_view& _decilmer) {
|
|
||||||
// Reference:
|
|
||||||
// https://stackoverflow.com/questions/14265581/parse-split-a-string-in-c-using-string-delimiter-standard-c
|
|
||||||
|
|
||||||
// prepare return value
|
|
||||||
std::vector<yycc_u8string_view> elems;
|
|
||||||
|
|
||||||
// if string need to be splitted is empty, return original string (empty string).
|
|
||||||
// if decilmer is empty, return original string.
|
|
||||||
yycc_u8string decilmer(_decilmer);
|
|
||||||
if (strl.empty() || decilmer.empty()) {
|
|
||||||
elems.emplace_back(strl);
|
|
||||||
return elems;
|
|
||||||
}
|
|
||||||
|
|
||||||
// start spliting
|
|
||||||
std::size_t previous = 0, current;
|
|
||||||
while ((current = strl.find(decilmer.c_str(), previous)) != yycc_u8string::npos) {
|
|
||||||
elems.emplace_back(strl.substr(previous, current - previous));
|
|
||||||
previous = current + decilmer.size();
|
|
||||||
}
|
|
||||||
// try insert last part but prevent possible out of range exception
|
|
||||||
if (previous <= strl.size()) {
|
|
||||||
elems.emplace_back(strl.substr(previous));
|
|
||||||
}
|
|
||||||
return elems;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma endregion
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,159 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "YYCCInternal.hpp"
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <cstdarg>
|
|
||||||
#include <functional>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The helper containing string operations
|
|
||||||
* @details
|
|
||||||
* See also \ref string_helper.
|
|
||||||
*/
|
|
||||||
namespace YYCC::StringHelper {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Perform a string formatting operation.
|
|
||||||
* @param[out] strl
|
|
||||||
* The string container receiving the result.
|
|
||||||
* There is no guarantee that the content is not modified when function failed.
|
|
||||||
* @param[in] format The format string.
|
|
||||||
* @param[in] ... Argument list of format string.
|
|
||||||
* @return True if success, otherwise false.
|
|
||||||
*/
|
|
||||||
bool Printf(yycc_u8string& strl, const yycc_char8_t* format, ...);
|
|
||||||
/**
|
|
||||||
* @brief Perform a string formatting operation.
|
|
||||||
* @param[out] strl
|
|
||||||
* The string container receiving the result.
|
|
||||||
* There is no guarantee that the content is not modified when function failed.
|
|
||||||
* @param[in] format The format string.
|
|
||||||
* @param[in] argptr Argument list of format string.
|
|
||||||
* @return True if success, otherwise false.
|
|
||||||
*/
|
|
||||||
bool VPrintf(yycc_u8string& strl, const yycc_char8_t* format, va_list argptr);
|
|
||||||
/**
|
|
||||||
* @brief Perform a string formatting operation.
|
|
||||||
* @param[in] format The format string.
|
|
||||||
* @param[in] ... Argument list of format string.
|
|
||||||
* @return The formatting result. Empty string if error happened.
|
|
||||||
*/
|
|
||||||
yycc_u8string Printf(const yycc_char8_t* format, ...);
|
|
||||||
/**
|
|
||||||
* @brief Perform a string formatting operation.
|
|
||||||
* @param[in] format The format string.
|
|
||||||
* @param[in] argptr Argument list of format string.
|
|
||||||
* @return The formatting result. Empty string if error happened.
|
|
||||||
*/
|
|
||||||
yycc_u8string VPrintf(const yycc_char8_t* format, va_list argptr);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Modify given string with all occurrences of substring \e old replaced by \e new.
|
|
||||||
* @param[in,out] strl The string for replacing
|
|
||||||
* @param[in] _from_strl The \e old string.
|
|
||||||
* @param[in] _to_strl The \e new string.
|
|
||||||
*/
|
|
||||||
void Replace(yycc_u8string& strl, const yycc_u8string_view& _from_strl, const yycc_u8string_view& _to_strl);
|
|
||||||
/**
|
|
||||||
* @brief Return a copy with all occurrences of substring \e old replaced by \e new.
|
|
||||||
* @param[in] _strl The string for replacing
|
|
||||||
* @param[in] _from_strl The \e old string.
|
|
||||||
* @param[in] _to_strl The \e new string.
|
|
||||||
* @return The result of replacement.
|
|
||||||
*/
|
|
||||||
yycc_u8string Replace(const yycc_u8string_view& _strl, const yycc_u8string_view& _from_strl, const yycc_u8string_view& _to_strl);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The data provider of general join function.
|
|
||||||
* @details
|
|
||||||
* For programmer using lambda to implement this function pointer:
|
|
||||||
* \li During calling, implementation should assign the reference of string view passed in argument
|
|
||||||
* to the string which need to be joined.
|
|
||||||
* \li Function return true to continue joining. otherwise return false to stop joining.
|
|
||||||
* The argument content assigned in the calling returning false is not included in join process.
|
|
||||||
*/
|
|
||||||
using JoinDataProvider = std::function<bool(yycc_u8string_view&)>;
|
|
||||||
/**
|
|
||||||
* @brief Universal join function.
|
|
||||||
* @details
|
|
||||||
* This function use function pointer as a general data provider interface,
|
|
||||||
* so this function suit for all types container.
|
|
||||||
* You can use this universal join function for any custom container by
|
|
||||||
* using C++ lambda syntax to create a code block adapted to this function pointer.
|
|
||||||
* @param[in] fct_data The function pointer in JoinDataProvider type prividing the data to be joined.
|
|
||||||
* @param[in] decilmer The decilmer used for joining.
|
|
||||||
* @return The result string of joining.
|
|
||||||
*/
|
|
||||||
yycc_u8string Join(JoinDataProvider fct_data, const yycc_u8string_view& decilmer);
|
|
||||||
/**
|
|
||||||
* @brief Specialized join function for standard library container.
|
|
||||||
* @tparam InputIt
|
|
||||||
* Must meet the requirements of LegacyInputIterator.
|
|
||||||
* It also can be dereferenced and then implicitly converted to yycc_u8string_view.
|
|
||||||
* @param[in] first The beginning of the range of elements to join.
|
|
||||||
* @param[in] last The terminal of the range of elements to join (exclusive).
|
|
||||||
* @param[in] decilmer The decilmer used for joining.
|
|
||||||
* @return The result string of joining.
|
|
||||||
*/
|
|
||||||
template<class InputIt>
|
|
||||||
yycc_u8string Join(InputIt first, InputIt last, const yycc_u8string_view& decilmer) {
|
|
||||||
return Join([&first, &last](yycc_u8string_view& view) -> bool {
|
|
||||||
// if we reach tail, return false to stop join process
|
|
||||||
if (first == last) return false;
|
|
||||||
// otherwise fetch data, inc iterator and return.
|
|
||||||
view = *first;
|
|
||||||
++first;
|
|
||||||
return true;
|
|
||||||
}, decilmer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Convert given string to lowercase.
|
|
||||||
* @param[in,out] strl The string to be lowercase.
|
|
||||||
*/
|
|
||||||
void Lower(yycc_u8string& strl);
|
|
||||||
/**
|
|
||||||
* @brief Return a copy of the string converted to lowercase.
|
|
||||||
* @param[in] strl The string to be lowercase.
|
|
||||||
* @return The copy of the string converted to lowercase.
|
|
||||||
*/
|
|
||||||
yycc_u8string Lower(const yycc_u8string_view& strl);
|
|
||||||
/**
|
|
||||||
* @brief Convert given string to uppercase.
|
|
||||||
* @param[in,out] strl The string to be uppercase.
|
|
||||||
*/
|
|
||||||
void Upper(yycc_u8string& strl);
|
|
||||||
/**
|
|
||||||
* @brief Return a copy of the string converted to uppercase.
|
|
||||||
* @param[in] strl The string to be uppercase.
|
|
||||||
* @return The copy of the string converted to uppercase.
|
|
||||||
*/
|
|
||||||
yycc_u8string Upper(const yycc_u8string_view& strl);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Split given string with specified decilmer.
|
|
||||||
* @param[in] strl The string need to be splitting.
|
|
||||||
* @param[in] _decilmer The decilmer for splitting.
|
|
||||||
* @return
|
|
||||||
* The split result.
|
|
||||||
* \par
|
|
||||||
* If given string or decilmer are empty,
|
|
||||||
* the result container will only contain 1 entry which is equal to given string.
|
|
||||||
*/
|
|
||||||
std::vector<yycc_u8string> Split(const yycc_u8string_view& strl, const yycc_u8string_view& _decilmer);
|
|
||||||
/**
|
|
||||||
* @brief Split given string with specified decilmer as string view.
|
|
||||||
* @param[in] strl The string need to be splitting.
|
|
||||||
* @param[in] _decilmer The decilmer for splitting.
|
|
||||||
* @return
|
|
||||||
* The split result with string view format.
|
|
||||||
* This will not produce any copy of original string.
|
|
||||||
* \par
|
|
||||||
* If given string or decilmer are empty,
|
|
||||||
* the result container will only contain 1 entry which is equal to given string.
|
|
||||||
* @see Split(const yycc_u8string_view&, const yycc_char8_t*)
|
|
||||||
*/
|
|
||||||
std::vector<yycc_u8string_view> SplitView(const yycc_u8string_view& strl, const yycc_u8string_view& _decilmer);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
#include "WinFctHelper.hpp"
|
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
|
||||||
|
|
||||||
#include "EncodingHelper.hpp"
|
|
||||||
#include "COMHelper.hpp"
|
|
||||||
|
|
||||||
namespace YYCC::WinFctHelper {
|
|
||||||
|
|
||||||
HMODULE GetCurrentModule() {
|
|
||||||
// Reference: https://stackoverflow.com/questions/557081/how-do-i-get-the-hmodule-for-the-currently-executing-code
|
|
||||||
HMODULE hModule = NULL;
|
|
||||||
::GetModuleHandleExW(
|
|
||||||
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, // get address and do not inc ref counter.
|
|
||||||
(LPCWSTR)GetCurrentModule,
|
|
||||||
&hModule);
|
|
||||||
|
|
||||||
return hModule;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GetTempDirectory(yycc_u8string& ret) {
|
|
||||||
// create wchar buffer for receiving the temp path.
|
|
||||||
std::wstring wpath(MAX_PATH + 1u, L'\0');
|
|
||||||
DWORD expected_size;
|
|
||||||
|
|
||||||
// fetch temp folder
|
|
||||||
while (true) {
|
|
||||||
if ((expected_size = ::GetTempPathW(static_cast<DWORD>(wpath.size()), wpath.data())) == 0) {
|
|
||||||
// failed, set to empty
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (expected_size > static_cast<DWORD>(wpath.size())) {
|
|
||||||
// buffer is too short, need enlarge and do fetching again
|
|
||||||
wpath.resize(expected_size);
|
|
||||||
} else {
|
|
||||||
// ok. shrink to real length, break while
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// resize result
|
|
||||||
wpath.resize(expected_size);
|
|
||||||
// convert to utf8 and return
|
|
||||||
return YYCC::EncodingHelper::WcharToUTF8(wpath, ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GetModuleFileName(HINSTANCE hModule, yycc_u8string& ret) {
|
|
||||||
// create wchar buffer for receiving the temp path.
|
|
||||||
std::wstring wpath(MAX_PATH + 1u, L'\0');
|
|
||||||
DWORD copied_size;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
if ((copied_size = ::GetModuleFileNameW(hModule, wpath.data(), static_cast<DWORD>(wpath.size()))) == 0) {
|
|
||||||
// failed, return
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check insufficient buffer
|
|
||||||
if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
|
|
||||||
// buffer is not enough, enlarge it and try again.
|
|
||||||
wpath.resize(wpath.size() + MAX_PATH);
|
|
||||||
} else {
|
|
||||||
// ok, break while
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// resize result
|
|
||||||
wpath.resize(copied_size);
|
|
||||||
// convert to utf8 and return
|
|
||||||
return YYCC::EncodingHelper::WcharToUTF8(wpath, ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GetLocalAppData(yycc_u8string& ret) {
|
|
||||||
// check whether com initialized
|
|
||||||
if (!COMHelper::IsInitialized()) return false;
|
|
||||||
|
|
||||||
// fetch path
|
|
||||||
LPWSTR _known_path;
|
|
||||||
HRESULT hr = SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, NULL, &_known_path);
|
|
||||||
if (FAILED(hr)) return false;
|
|
||||||
COMHelper::SmartLPWSTR known_path(_known_path);
|
|
||||||
|
|
||||||
// convert to utf8
|
|
||||||
return YYCC::EncodingHelper::WcharToUTF8(known_path.get(), ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsValidCodePage(UINT code_page) {
|
|
||||||
CPINFOEXW cpinfo;
|
|
||||||
return ::GetCPInfoExW(code_page, 0, &cpinfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL CopyFile(const yycc_u8string_view& lpExistingFileName, const yycc_u8string_view& lpNewFileName, BOOL bFailIfExists) {
|
|
||||||
std::wstring wExistingFileName, wNewFileName;
|
|
||||||
if (!YYCC::EncodingHelper::UTF8ToWchar(lpExistingFileName, wExistingFileName)) return FALSE;
|
|
||||||
if (!YYCC::EncodingHelper::UTF8ToWchar(lpNewFileName, wNewFileName)) return FALSE;
|
|
||||||
return ::CopyFileW(wExistingFileName.c_str(), wNewFileName.c_str(), bFailIfExists);
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL MoveFile(const yycc_u8string_view& lpExistingFileName, const yycc_u8string_view& lpNewFileName) {
|
|
||||||
std::wstring wExistingFileName, wNewFileName;
|
|
||||||
if (!YYCC::EncodingHelper::UTF8ToWchar(lpExistingFileName, wExistingFileName)) return FALSE;
|
|
||||||
if (!YYCC::EncodingHelper::UTF8ToWchar(lpNewFileName, wNewFileName)) return FALSE;
|
|
||||||
return ::MoveFileW(wExistingFileName.c_str(), wNewFileName.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL DeleteFile(const yycc_u8string_view& lpFileName) {
|
|
||||||
std::wstring wFileName;
|
|
||||||
if (!YYCC::EncodingHelper::UTF8ToWchar(lpFileName, wFileName)) return FALSE;
|
|
||||||
return ::DeleteFileW(wFileName.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "YYCCInternal.hpp"
|
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "WinImportPrefix.hpp"
|
|
||||||
#include <Windows.h>
|
|
||||||
#include "WinImportSuffix.hpp"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The helper providing assistance of Win32 functions.
|
|
||||||
* @details
|
|
||||||
* This helper is Windows specific.
|
|
||||||
* If current environment is not Windows, the whole namespace will be unavailable.
|
|
||||||
* See also \ref win_fct_helper
|
|
||||||
*/
|
|
||||||
namespace YYCC::WinFctHelper {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get Windows used HANDLE for current module.
|
|
||||||
* @details
|
|
||||||
* If your target is EXE, the current module simply is your program self.
|
|
||||||
* However, if your target is DLL, the current module is your DLL, not the EXE loading your DLL.
|
|
||||||
*
|
|
||||||
* This function is frequently used by DLL.
|
|
||||||
* Because some design need the HANDLE of current module, not the host EXE loading your DLL.
|
|
||||||
* For example, you may want to get the path of your built DLL, or fetch resources from your DLL at runtime,
|
|
||||||
* then you should pass current module HANDLE, not NULL or the HANDLE of EXE.
|
|
||||||
* @return A Windows HANDLE pointing to current module, NULL if failed.
|
|
||||||
*/
|
|
||||||
HMODULE GetCurrentModule();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get path to Windows temporary folder.
|
|
||||||
* @details Windows temporary folder usually is the target of \%TEMP\%.
|
|
||||||
* @param[out] ret The variable receiving UTF8 encoded path to Windows temp folder.
|
|
||||||
* @return True if success, otherwise false.
|
|
||||||
*/
|
|
||||||
bool GetTempDirectory(yycc_u8string& ret);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the file name of given module HANDLE
|
|
||||||
* @param[in] hModule
|
|
||||||
* The HANDLE to the module where you want to get file name.
|
|
||||||
* It is same as the HANDLE parameter of Win32 \c GetModuleFileName.
|
|
||||||
* @param[out] ret The variable receiving UTF8 encoded file name of given module.
|
|
||||||
* @return True if success, otherwise false.
|
|
||||||
*/
|
|
||||||
bool GetModuleFileName(HINSTANCE hModule, yycc_u8string& ret);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the path to \%LOCALAPPDATA\%.
|
|
||||||
* @details \%LOCALAPPDATA\% usually was used as putting local app data files
|
|
||||||
* @param[out] ret The variable receiving UTF8 encoded path to LOCALAPPDATA.
|
|
||||||
* @return True if success, otherwise false.
|
|
||||||
*/
|
|
||||||
bool GetLocalAppData(yycc_u8string& ret);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check whether given code page number is a valid one.
|
|
||||||
* @param[in] code_page The code page number.
|
|
||||||
* @return True if it is valid, otherwise false.
|
|
||||||
*/
|
|
||||||
bool IsValidCodePage(UINT code_page);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Copies an existing file to a new file.
|
|
||||||
* @param lpExistingFileName The name of an existing file.
|
|
||||||
* @param lpNewFileName The name of the new file.
|
|
||||||
* @param bFailIfExists
|
|
||||||
* If this parameter is TRUE and the new file specified by \c lpNewFileName already exists, the function fails.
|
|
||||||
* If this parameter is FALSE and the new file already exists, the function overwrites the existing file and succeeds.
|
|
||||||
* @return
|
|
||||||
* If the function succeeds, the return value is nonzero.
|
|
||||||
* If the function fails, the return value is zero. To get extended error information, call \c GetLastError.
|
|
||||||
* @remarks Same as Windows \c CopyFile: https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-copyfilew
|
|
||||||
*/
|
|
||||||
BOOL CopyFile(const yycc_u8string_view& lpExistingFileName, const yycc_u8string_view& lpNewFileName, BOOL bFailIfExists);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Moves an existing file or a directory, including its children.
|
|
||||||
* @param lpExistingFileName The current name of the file or directory on the local computer.
|
|
||||||
* @param lpNewFileName
|
|
||||||
* The new name for the file or directory. The new name must not already exist.
|
|
||||||
* A new file may be on a different file system or drive. A new directory must be on the same drive.
|
|
||||||
* @return
|
|
||||||
* If the function succeeds, the return value is nonzero.
|
|
||||||
* If the function fails, the return value is zero. To get extended error information, call \c GetLastError.
|
|
||||||
* @remarks Same as Windows \c MoveFile: https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-movefilew
|
|
||||||
*/
|
|
||||||
BOOL MoveFile(const yycc_u8string_view& lpExistingFileName, const yycc_u8string_view& lpNewFileName);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Deletes an existing file.
|
|
||||||
* @param lpFileName The name of the file to be deleted.
|
|
||||||
* @return
|
|
||||||
* If the function succeeds, the return value is nonzero.
|
|
||||||
* If the function fails, the return value is zero. To get extended error information, call \c GetLastError.
|
|
||||||
* @remarks Same as Windows \c DeleteFile: https://learn.microsoft.com/e-us/windows/win32/api/winbase/nf-winbase-deletefile
|
|
||||||
*/
|
|
||||||
BOOL DeleteFile(const yycc_u8string_view& lpFileName);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
// It is by design that no pragma once or #if to prevent deplicated including.
|
|
||||||
// Because this header is the part of wrapper, not a real header.
|
|
||||||
// #pragma once
|
|
||||||
|
|
||||||
#include "YYCCInternal.hpp"
|
|
||||||
|
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
|
||||||
|
|
||||||
// Define 2 macros to disallow Windows generate MIN and MAX macros
|
|
||||||
// which cause std::min and std::max can not function as normal.
|
|
||||||
#if !defined(WIN32_LEAN_AND_MEAN)
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(NOMINMAX)
|
|
||||||
#define NOMINMAX
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
// It is by design that no pragma once or #if to prevent deplicated including.
|
|
||||||
// Because this header is the part of wrapper, not a real header.
|
|
||||||
// #pragma once
|
|
||||||
|
|
||||||
#include "YYCCInternal.hpp"
|
|
||||||
|
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
|
||||||
|
|
||||||
// Windows also will generate following macros
|
|
||||||
// which may cause the function sign is different in Windows and other platforms.
|
|
||||||
// So we simply remove them.
|
|
||||||
// Because #undef will not throw error if there are no matched macro,
|
|
||||||
// so we simply #undef them directly.
|
|
||||||
#undef GetObject
|
|
||||||
#undef GetClassName
|
|
||||||
#undef LoadImage
|
|
||||||
#undef GetTempPath
|
|
||||||
#undef GetModuleFileName
|
|
||||||
#undef CopyFile
|
|
||||||
#undef MoveFile
|
|
||||||
#undef DeleteFile
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
Copyright (c) Microsoft Corporation, yyc12345.
|
|
||||||
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
||||||
-->
|
|
||||||
|
|
||||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
|
||||||
|
|
||||||
<!-- Following XML are copied from Visual Studio embedded Natvis files. -->
|
|
||||||
<!-- <Microsoft Visual Studio Install Directory>\Common7\Packages\Debugger\Visualizers\stl.natvis -->
|
|
||||||
|
|
||||||
<!-- VC 2013 -->
|
|
||||||
<Type Name="std::basic_string<YYCC::yycc_char8_t,*>" Priority="MediumLow">
|
|
||||||
<AlternativeType Name="std::basic_string<char8_t,*>" />
|
|
||||||
<AlternativeType Name="std::basic_string<unsigned char,*>" />
|
|
||||||
<DisplayString Condition="_Myres < _BUF_SIZE">{_Bx._Buf,s8}</DisplayString>
|
|
||||||
<DisplayString Condition="_Myres >= _BUF_SIZE">{_Bx._Ptr,s8}</DisplayString>
|
|
||||||
<StringView Condition="_Myres < _BUF_SIZE">_Bx._Buf,s8</StringView>
|
|
||||||
<StringView Condition="_Myres >= _BUF_SIZE">_Bx._Ptr,s8</StringView>
|
|
||||||
<Expand>
|
|
||||||
<Item Name="[size]" ExcludeView="simple">_Mysize</Item>
|
|
||||||
<Item Name="[capacity]" ExcludeView="simple">_Myres</Item>
|
|
||||||
<ArrayItems>
|
|
||||||
<Size>_Mysize</Size>
|
|
||||||
<ValuePointer Condition="_Myres < _BUF_SIZE">_Bx._Buf</ValuePointer>
|
|
||||||
<ValuePointer Condition="_Myres >= _BUF_SIZE">_Bx._Ptr</ValuePointer>
|
|
||||||
</ArrayItems>
|
|
||||||
</Expand>
|
|
||||||
</Type>
|
|
||||||
|
|
||||||
<!-- VC 2015+ ABI basic_string -->
|
|
||||||
<Type Name="std::basic_string<YYCC::yycc_char8_t,*>">
|
|
||||||
<AlternativeType Name="std::basic_string<char8_t,*>" />
|
|
||||||
<AlternativeType Name="std::basic_string<unsigned char,*>" />
|
|
||||||
<Intrinsic Name="size" Expression="_Mypair._Myval2._Mysize" />
|
|
||||||
<Intrinsic Name="capacity" Expression="_Mypair._Myval2._Myres" />
|
|
||||||
<!-- _BUF_SIZE = 16 / sizeof(char) < 1 ? 1 : 16 / sizeof(char) == 16 -->
|
|
||||||
<Intrinsic Name="bufSize" Expression="16" />
|
|
||||||
<Intrinsic Name="isShortString" Expression="capacity() < bufSize()" />
|
|
||||||
<Intrinsic Name="isLongString" Expression="capacity() >= bufSize()" />
|
|
||||||
<DisplayString Condition="isShortString()">{_Mypair._Myval2._Bx._Buf,s8}</DisplayString>
|
|
||||||
<DisplayString Condition="isLongString()">{_Mypair._Myval2._Bx._Ptr,s8}</DisplayString>
|
|
||||||
<StringView Condition="isShortString()">_Mypair._Myval2._Bx._Buf,s8</StringView>
|
|
||||||
<StringView Condition="isLongString()">_Mypair._Myval2._Bx._Ptr,s8</StringView>
|
|
||||||
<Expand>
|
|
||||||
<Item Name="[size]" ExcludeView="simple">size()</Item>
|
|
||||||
<Item Name="[capacity]" ExcludeView="simple">capacity()</Item>
|
|
||||||
<Item Name="[allocator]" ExcludeView="simple">_Mypair</Item>
|
|
||||||
<ArrayItems>
|
|
||||||
<Size>_Mypair._Myval2._Mysize</Size>
|
|
||||||
<ValuePointer Condition="isShortString()">_Mypair._Myval2._Bx._Buf</ValuePointer>
|
|
||||||
<ValuePointer Condition="isLongString()">_Mypair._Myval2._Bx._Ptr</ValuePointer>
|
|
||||||
</ArrayItems>
|
|
||||||
</Expand>
|
|
||||||
</Type>
|
|
||||||
|
|
||||||
<Type Name="std::basic_string_view<YYCC::yycc_char8_t,*>">
|
|
||||||
<AlternativeType Name="std::basic_string_view<char8_t,*>" />
|
|
||||||
<AlternativeType Name="std::basic_string_view<unsigned char,*>" />
|
|
||||||
<Intrinsic Name="size" Expression="_Mysize" />
|
|
||||||
<Intrinsic Name="data" Expression="_Mydata" />
|
|
||||||
<DisplayString>{_Mydata,[_Mysize],s8}</DisplayString>
|
|
||||||
<StringView>_Mydata,[_Mysize],s8</StringView>
|
|
||||||
<Expand>
|
|
||||||
<Item Name="[size]" ExcludeView="simple">size()</Item>
|
|
||||||
<ArrayItems>
|
|
||||||
<Size>size()</Size>
|
|
||||||
<ValuePointer>data()</ValuePointer>
|
|
||||||
</ArrayItems>
|
|
||||||
</Expand>
|
|
||||||
</Type>
|
|
||||||
|
|
||||||
</AutoVisualizer>
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#pragma region Operating System Identifier Macros
|
|
||||||
|
|
||||||
// Define operating system macros
|
|
||||||
#define YYCC_OS_WINDOWS 2
|
|
||||||
#define YYCC_OS_LINUX 3
|
|
||||||
// Check current operating system.
|
|
||||||
#if defined(_WIN32)
|
|
||||||
#define YYCC_OS YYCC_OS_WINDOWS
|
|
||||||
#else
|
|
||||||
#define YYCC_OS YYCC_OS_LINUX
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#pragma endregion
|
|
||||||
|
|
||||||
#pragma region Windows Shitty Behavior Disable Macros
|
|
||||||
|
|
||||||
// If we are in Windows,
|
|
||||||
// we need add 2 macros to disable Windows shitty warnings and errors of
|
|
||||||
// depracted functions and not secure functions.
|
|
||||||
#if YYCC_OS == YYCC_OS_WINDOWS
|
|
||||||
|
|
||||||
#if !defined(_CRT_SECURE_NO_WARNINGS)
|
|
||||||
#define _CRT_SECURE_NO_WARNINGS
|
|
||||||
#endif
|
|
||||||
#if !defined(_CRT_NONSTDC_NO_DEPRECATE)
|
|
||||||
#define _CRT_NONSTDC_NO_DEPRECATE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#pragma endregion
|
|
||||||
|
|
||||||
#pragma region YYCC UTF8 Types
|
|
||||||
|
|
||||||
// Define the UTF8 char type we used.
|
|
||||||
// And do a polyfill if no embedded char8_t type.
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Library core namespace
|
|
||||||
* @details Almost library functions are located in this namespace.
|
|
||||||
*/
|
|
||||||
namespace YYCC {
|
|
||||||
#if defined(__cpp_char8_t)
|
|
||||||
using yycc_char8_t = char8_t;
|
|
||||||
using yycc_u8string = std::u8string;
|
|
||||||
using yycc_u8string_view = std::u8string_view;
|
|
||||||
#else
|
|
||||||
using yycc_char8_t = unsigned char;
|
|
||||||
using yycc_u8string = std::basic_string<yycc_char8_t>;
|
|
||||||
using yycc_u8string_view = std::basic_string_view<yycc_char8_t>;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
\typedef YYCC::yycc_char8_t
|
|
||||||
\brief YYCC UTF8 char type.
|
|
||||||
\details
|
|
||||||
This char type is an alias to \c std::char8_t if your current C++ standard support it.
|
|
||||||
Otherwise it is defined as <TT>unsigned char</TT> as C++ 20 stdandard does.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
\typedef YYCC::yycc_u8string
|
|
||||||
\brief YYCC UTF8 string container type.
|
|
||||||
\details
|
|
||||||
This type is defined as \c std::basic_string<yycc_char8_t>.
|
|
||||||
It is equal to \c std::u8string if your current C++ standard support it.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
\typedef YYCC::yycc_u8string_view
|
|
||||||
\brief YYCC UTF8 string view type.
|
|
||||||
\details
|
|
||||||
This type is defined as \c std::basic_string_view<yycc_char8_t>.
|
|
||||||
It is equal to \c std::u8string_view if your current C++ standard support it.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma endregion
|
|
||||||
|
|
||||||
#pragma region Batch Class Move / Copy Function Macros
|
|
||||||
|
|
||||||
/// @brief Explicitly remove copy (\c constructor and \c operator\=) for given class.
|
|
||||||
#define YYCC_DEL_CLS_COPY(CLSNAME) \
|
|
||||||
CLSNAME(const CLSNAME&) = delete; \
|
|
||||||
CLSNAME& operator=(const CLSNAME&) = delete;
|
|
||||||
|
|
||||||
/// @brief Explicitly remove move (\c constructor and \c operator\=) for given class.
|
|
||||||
#define YYCC_DEL_CLS_MOVE(CLSNAME) \
|
|
||||||
CLSNAME(CLSNAME&&) = delete; \
|
|
||||||
CLSNAME& operator=(CLSNAME&&) = delete;
|
|
||||||
|
|
||||||
/// @brief Explicitly remove (copy and move) (\c constructor and \c operator\=) for given class.
|
|
||||||
#define YYCC_DEL_CLS_COPY_MOVE(CLSNAME) \
|
|
||||||
YYCC_DEL_CLS_COPY(CLSNAME) \
|
|
||||||
YYCC_DEL_CLS_MOVE(CLSNAME)
|
|
||||||
|
|
||||||
/// @brief Explicitly set default copy (\c constructor and \c operator\=) for given class.
|
|
||||||
#define YYCC_DEF_CLS_COPY(CLSNAME) \
|
|
||||||
CLSNAME(const CLSNAME&) = default; \
|
|
||||||
CLSNAME& operator=(const CLSNAME&) = default;
|
|
||||||
|
|
||||||
/// @brief Explicitly set default move (\c constructor and \c operator\=) for given class.
|
|
||||||
#define YYCC_DEF_CLS_MOVE(CLSNAME) \
|
|
||||||
CLSNAME(CLSNAME&&) = default; \
|
|
||||||
CLSNAME& operator=(CLSNAME&&) = default;
|
|
||||||
|
|
||||||
/// @brief Explicitly set default (copy and move) (\c constructor and \c operator\=) for given class.
|
|
||||||
#define YYCC_DEF_CLS_COPY_MOVE(CLSNAME) \
|
|
||||||
YYCC_DEF_CLS_COPY(CLSNAME) \
|
|
||||||
YYCC_DEF_CLS_MOVE(CLSNAME)
|
|
||||||
|
|
||||||
#pragma endregion
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "YYCCInternal.hpp"
|
|
||||||
|
|
||||||
#include "EncodingHelper.hpp"
|
|
||||||
#include "StringHelper.hpp"
|
|
||||||
#include "ConsoleHelper.hpp"
|
|
||||||
#include "COMHelper.hpp"
|
|
||||||
#include "DialogHelper.hpp"
|
|
||||||
#include "ParserHelper.hpp"
|
|
||||||
#include "IOHelper.hpp"
|
|
||||||
#include "WinFctHelper.hpp"
|
|
||||||
#include "StdPatch.hpp"
|
|
||||||
#include "ExceptionHelper.hpp"
|
|
||||||
|
|
||||||
#include "ConfigManager.hpp"
|
|
||||||
#include "ArgParser.hpp"
|
|
||||||
26
src/yycc.hpp
Normal file
26
src/yycc.hpp
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* When you use YYCCommonplace, please make sure that you include this header first,
|
||||||
|
* before including any other headers of YYCC.
|
||||||
|
* This header contain essential check macros and version infos.
|
||||||
|
* They are crucial before using YYCC.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Library version
|
||||||
|
#include "yycc/version.hpp"
|
||||||
|
|
||||||
|
// Detect essential macros
|
||||||
|
// Operating System macros
|
||||||
|
#include "yycc/macro/os_detector.hpp"
|
||||||
|
// Compiler macros
|
||||||
|
#include "yycc/macro/compiler_detector.hpp"
|
||||||
|
// Endian macros
|
||||||
|
#include "yycc/macro/endian_detector.hpp"
|
||||||
|
// Pointer size macros
|
||||||
|
#include "yycc/macro/ptr_size_detector.hpp"
|
||||||
|
// STL macros
|
||||||
|
#include "yycc/macro/stl_detector.hpp"
|
||||||
|
|
||||||
|
namespace yycc {}
|
||||||
29
src/yycc/carton/binstore.hpp
Normal file
29
src/yycc/carton/binstore.hpp
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "binstore/types.hpp"
|
||||||
|
#include "binstore/serdes.hpp"
|
||||||
|
#include "binstore/setting.hpp"
|
||||||
|
#include "binstore/configuration.hpp"
|
||||||
|
#include "binstore/storage.hpp"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The main namespace for the binstore module.
|
||||||
|
* @details
|
||||||
|
* 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:
|
||||||
|
*
|
||||||
|
* \li Type-safe serialization and deserialization of various data types
|
||||||
|
* (integers, floating-point numbers, booleans, strings, enums)
|
||||||
|
* \li Setting management with unique tokens for access control
|
||||||
|
* \li Version control for configuration data with migration strategies
|
||||||
|
* \li File and stream-based storage operations
|
||||||
|
* \li Comprehensive error handling for robust operation
|
||||||
|
*
|
||||||
|
* The main components are:
|
||||||
|
* \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
|
||||||
|
*/
|
||||||
|
namespace yycc::carton::binstore {}
|
||||||
21
src/yycc/carton/binstore/configuration.cpp
Normal file
21
src/yycc/carton/binstore/configuration.cpp
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#include "configuration.hpp"
|
||||||
|
|
||||||
|
#define BINSTORE ::yycc::carton::binstore
|
||||||
|
#define TYPES ::yycc::carton::binstore::types
|
||||||
|
|
||||||
|
namespace yycc::carton::binstore::configuration {
|
||||||
|
|
||||||
|
Configuration::Configuration(TYPES::VersionIdentifier version, BINSTORE::setting::SettingCollection&& settings) :
|
||||||
|
version(version), settings(std::move(settings)) {}
|
||||||
|
|
||||||
|
Configuration::~Configuration() {}
|
||||||
|
|
||||||
|
TYPES::VersionIdentifier Configuration::get_version() const {
|
||||||
|
return this->version;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BINSTORE::setting::SettingCollection& Configuration::get_settings() const {
|
||||||
|
return this->settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace yycc::carton::binstore::configuration
|
||||||
43
src/yycc/carton/binstore/configuration.hpp
Normal file
43
src/yycc/carton/binstore/configuration.hpp
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "../../macro/class_copy_move.hpp"
|
||||||
|
#include "types.hpp"
|
||||||
|
#include "setting.hpp"
|
||||||
|
|
||||||
|
#define NS_YYCC_BINSTORE ::yycc::carton::binstore
|
||||||
|
#define NS_YYCC_BINSTORE_TYPES ::yycc::carton::binstore::types
|
||||||
|
|
||||||
|
namespace yycc::carton::binstore::configuration {
|
||||||
|
|
||||||
|
/// @brief Configuration class that holds version and settings for the binstore module.
|
||||||
|
class Configuration {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Construct a new Configuration object.
|
||||||
|
* @param[in] version The version identifier for this configuration.
|
||||||
|
* @param[in] settings The settings collection to associate with this configuration.
|
||||||
|
*/
|
||||||
|
Configuration(NS_YYCC_BINSTORE_TYPES::VersionIdentifier version, NS_YYCC_BINSTORE::setting::SettingCollection&& settings);
|
||||||
|
~Configuration();
|
||||||
|
YYCC_DEFAULT_COPY_MOVE(Configuration)
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Get the version identifier of this configuration.
|
||||||
|
* @return The version identifier.
|
||||||
|
*/
|
||||||
|
NS_YYCC_BINSTORE_TYPES::VersionIdentifier get_version() const;
|
||||||
|
/**
|
||||||
|
* @brief Get the settings collection associated with this configuration.
|
||||||
|
* @return A const reference to the settings collection.
|
||||||
|
*/
|
||||||
|
const NS_YYCC_BINSTORE::setting::SettingCollection& get_settings() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
NS_YYCC_BINSTORE_TYPES::VersionIdentifier version; ///< The version of current configuration struct.
|
||||||
|
NS_YYCC_BINSTORE::setting::SettingCollection settings; ///< All registered settings.
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace yycc::carton::binstore::configuration
|
||||||
|
|
||||||
|
#undef NS_YYCC_BINSTORE_TYPES
|
||||||
|
#undef NS_YYCC_BINSTORE
|
||||||
226
src/yycc/carton/binstore/serdes.hpp
Normal file
226
src/yycc/carton/binstore/serdes.hpp
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "../../macro/class_copy_move.hpp"
|
||||||
|
#include "types.hpp"
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <limits>
|
||||||
|
#include <concepts>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstring>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#define NS_YYCC_BINSTORE_TYPES ::yycc::carton::binstore::types
|
||||||
|
|
||||||
|
namespace yycc::carton::binstore::serdes {
|
||||||
|
|
||||||
|
// TODO: Support numeric list and string list SerDes.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The concept for checking whether given type is a valid binstore serializer and deserializer (SerDes).
|
||||||
|
* @details
|
||||||
|
* A valid SerDes must have a type name or alias called "ValueType"
|
||||||
|
* indicating the corresponding setting type which read and write with underlying ByteArray.
|
||||||
|
* Please note that the type of this value must can be placed into \c std::optional.
|
||||||
|
*
|
||||||
|
* SerDes must have following 3 functions:
|
||||||
|
* \li Function named "serialize" will accept an const reference of setting data,
|
||||||
|
* and convert it into underlying ByteArray format in return value.
|
||||||
|
* If any error occurs, it will return \c std::nullopt instead.
|
||||||
|
* \li Function named "deserialize" will do the opposite operation with "serialize".
|
||||||
|
* It convert ByteArray into setting desired type, or \c std::nullopt if any error occurs.
|
||||||
|
* \li Function named "reset" will built an ByteArray which holding default value of this option.
|
||||||
|
* This is usually used when fail to deserialize or there is no such setting.
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
concept SerDes = requires(const T& t, const NS_YYCC_BINSTORE_TYPES::ByteArray& cba) {
|
||||||
|
// Check whether there is T::ValueType type
|
||||||
|
typename T::ValueType;
|
||||||
|
// Check whether there are required member functions and they have correct signatures.
|
||||||
|
{ t.serialize(std::declval<const typename T::ValueType&>()) } -> std::same_as<std::optional<NS_YYCC_BINSTORE_TYPES::ByteArray>>;
|
||||||
|
{ t.deserialize(cba) } -> std::same_as<std::optional<typename T::ValueType>>;
|
||||||
|
{ t.reset() } -> std::same_as<NS_YYCC_BINSTORE_TYPES::ByteArray>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief A convenient alias to the value type of SerDes.
|
||||||
|
template<SerDes T>
|
||||||
|
using SerDesValueType = T::ValueType;
|
||||||
|
|
||||||
|
template<std::integral T,
|
||||||
|
auto TDefault = static_cast<T>(0),
|
||||||
|
auto TMin = std::numeric_limits<T>::min(),
|
||||||
|
auto TMax = std::numeric_limits<T>::max()>
|
||||||
|
struct IntegralSerDes {
|
||||||
|
IntegralSerDes() = default;
|
||||||
|
YYCC_DEFAULT_COPY_MOVE(IntegralSerDes)
|
||||||
|
|
||||||
|
static_assert(TMin <= TMax);
|
||||||
|
static_assert(TDefault >= TMin && TDefault <= TMax);
|
||||||
|
|
||||||
|
using ValueType = T;
|
||||||
|
static constexpr size_t VALUE_SIZE = sizeof(ValueType);
|
||||||
|
|
||||||
|
std::optional<NS_YYCC_BINSTORE_TYPES::ByteArray> serialize(const ValueType& value) const {
|
||||||
|
if (value > TMax || value < TMin) return std::nullopt;
|
||||||
|
|
||||||
|
NS_YYCC_BINSTORE_TYPES::ByteArray ba;
|
||||||
|
ba.resize_data(VALUE_SIZE);
|
||||||
|
std::memcpy(ba.get_data_ptr(), &value, VALUE_SIZE);
|
||||||
|
return ba;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<ValueType> deserialize(const NS_YYCC_BINSTORE_TYPES::ByteArray& ba) const {
|
||||||
|
if (ba.get_data_size() != VALUE_SIZE) return std::nullopt;
|
||||||
|
ValueType value;
|
||||||
|
std::memcpy(&value, ba.get_data_ptr(), VALUE_SIZE);
|
||||||
|
|
||||||
|
if (value > TMax || value < TMin) return std::nullopt;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_YYCC_BINSTORE_TYPES::ByteArray reset() const { return this->serialize(TDefault).value(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<std::floating_point T,
|
||||||
|
auto TDefault = static_cast<T>(0),
|
||||||
|
auto TMin = std::numeric_limits<T>::lowest(),
|
||||||
|
auto TMax = std::numeric_limits<T>::max()>
|
||||||
|
struct FloatingPointSerDes {
|
||||||
|
FloatingPointSerDes() {
|
||||||
|
// TODO: Remove this and make it "= default" when 3 common STL make std::isfinite become constexpr.
|
||||||
|
if (!std::isfinite(TMin)) throw std::logic_error("invalid float minimum value.");
|
||||||
|
if (!std::isfinite(TMax)) throw std::logic_error("invalid float maximum value.");
|
||||||
|
}
|
||||||
|
YYCC_DEFAULT_COPY_MOVE(FloatingPointSerDes)
|
||||||
|
|
||||||
|
// TODO: Use static_assert once 3 common STL make this become constexpr.
|
||||||
|
//static_assert(std::isfinite(TMin));
|
||||||
|
//static_assert(std::isfinite(TMax));
|
||||||
|
static_assert(TMin <= TMax);
|
||||||
|
static_assert(TDefault >= TMin && TDefault <= TMax);
|
||||||
|
|
||||||
|
using ValueType = T;
|
||||||
|
static constexpr size_t VALUE_SIZE = sizeof(ValueType);
|
||||||
|
|
||||||
|
std::optional<NS_YYCC_BINSTORE_TYPES::ByteArray> serialize(const ValueType& value) const {
|
||||||
|
if (value > TMax || value < TMin) return std::nullopt;
|
||||||
|
|
||||||
|
NS_YYCC_BINSTORE_TYPES::ByteArray ba;
|
||||||
|
ba.resize_data(VALUE_SIZE);
|
||||||
|
std::memcpy(ba.get_data_ptr(), &value, VALUE_SIZE);
|
||||||
|
return ba;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<ValueType> deserialize(const NS_YYCC_BINSTORE_TYPES::ByteArray& ba) const {
|
||||||
|
if (ba.get_data_size() != VALUE_SIZE) return std::nullopt;
|
||||||
|
|
||||||
|
ValueType value;
|
||||||
|
std::memcpy(&value, ba.get_data_ptr(), VALUE_SIZE);
|
||||||
|
if (value > TMax || value < TMin) return std::nullopt;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_YYCC_BINSTORE_TYPES::ByteArray reset() const { return this->serialize(TDefault).value(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, T TDefault>
|
||||||
|
requires std::is_enum_v<T>
|
||||||
|
struct EnumSerDes {
|
||||||
|
EnumSerDes() = default;
|
||||||
|
YYCC_DEFAULT_COPY_MOVE(EnumSerDes)
|
||||||
|
|
||||||
|
using UnderlyingType = std::underlying_type_t<T>;
|
||||||
|
using ValueType = T;
|
||||||
|
|
||||||
|
std::optional<NS_YYCC_BINSTORE_TYPES::ByteArray> serialize(const ValueType& value) const {
|
||||||
|
return inner.serialize(static_cast<UnderlyingType>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<ValueType> deserialize(const NS_YYCC_BINSTORE_TYPES::ByteArray& ba) const {
|
||||||
|
return inner.deserialize(ba).transform([](auto v) { return static_cast<ValueType>(v); });
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_YYCC_BINSTORE_TYPES::ByteArray reset() const { return inner.reset(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
IntegralSerDes<UnderlyingType, static_cast<UnderlyingType>(TDefault)> inner;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<bool TDefault = false>
|
||||||
|
struct BoolSerDes {
|
||||||
|
BoolSerDes() = default;
|
||||||
|
YYCC_DEFAULT_COPY_MOVE(BoolSerDes)
|
||||||
|
|
||||||
|
using ValueType = bool;
|
||||||
|
static constexpr size_t VALUE_SIZE = sizeof(ValueType);
|
||||||
|
|
||||||
|
std::optional<NS_YYCC_BINSTORE_TYPES::ByteArray> serialize(const ValueType& value) const {
|
||||||
|
NS_YYCC_BINSTORE_TYPES::ByteArray ba;
|
||||||
|
ba.resize_data(VALUE_SIZE);
|
||||||
|
std::memcpy(ba.get_data_ptr(), &value, VALUE_SIZE);
|
||||||
|
return ba;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<ValueType> deserialize(const NS_YYCC_BINSTORE_TYPES::ByteArray& ba) const {
|
||||||
|
if (ba.get_data_size() != VALUE_SIZE) return std::nullopt;
|
||||||
|
|
||||||
|
ValueType value;
|
||||||
|
std::memcpy(&value, ba.get_data_ptr(), VALUE_SIZE);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_YYCC_BINSTORE_TYPES::ByteArray reset() const { return this->serialize(TDefault).value(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StringSerDes {
|
||||||
|
StringSerDes() = default;
|
||||||
|
YYCC_DEFAULT_COPY_MOVE(StringSerDes)
|
||||||
|
|
||||||
|
using ValueType = std::u8string;
|
||||||
|
using HeaderType = size_t;
|
||||||
|
static constexpr size_t HEADER_SIZE = sizeof(HeaderType);
|
||||||
|
|
||||||
|
std::optional<NS_YYCC_BINSTORE_TYPES::ByteArray> serialize(const ValueType& value) const {
|
||||||
|
// Get the size of string
|
||||||
|
HeaderType length = value.size();
|
||||||
|
|
||||||
|
// Prepare byte array and allocate size.
|
||||||
|
NS_YYCC_BINSTORE_TYPES::ByteArray ba;
|
||||||
|
ba.resize_data(HEADER_SIZE + length);
|
||||||
|
|
||||||
|
// Copy length first
|
||||||
|
std::memcpy(ba.get_data_ptr(), &length, HEADER_SIZE);
|
||||||
|
// Copy string data
|
||||||
|
std::memcpy(ba.get_data_ptr(HEADER_SIZE), value.data(), length);
|
||||||
|
|
||||||
|
// Okey
|
||||||
|
return ba;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<ValueType> deserialize(const NS_YYCC_BINSTORE_TYPES::ByteArray& ba) const {
|
||||||
|
// Get byte array size
|
||||||
|
size_t ba_size = ba.get_data_size();
|
||||||
|
// Check whether it has header
|
||||||
|
if (ba_size < HEADER_SIZE) return std::nullopt;
|
||||||
|
|
||||||
|
// Get header
|
||||||
|
HeaderType length;
|
||||||
|
std::memcpy(&length, ba.get_data_ptr(), HEADER_SIZE);
|
||||||
|
|
||||||
|
// Check whether full size is header + length.
|
||||||
|
if (ba_size != HEADER_SIZE + length) return std::nullopt;
|
||||||
|
// Prepare result
|
||||||
|
std::u8string value(length, u8'\0');
|
||||||
|
// Read into result
|
||||||
|
std::memcpy(value.data(), ba.get_data_ptr(HEADER_SIZE), length);
|
||||||
|
|
||||||
|
// Okey
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_YYCC_BINSTORE_TYPES::ByteArray reset() const { return this->serialize(u8"").value(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace yycc::carton::binstore::serdes
|
||||||
|
|
||||||
|
#undef NS_YYCC_BINSTORE_TYPES
|
||||||
89
src/yycc/carton/binstore/setting.cpp
Normal file
89
src/yycc/carton/binstore/setting.cpp
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
#include "setting.hpp"
|
||||||
|
#include "../../patch/format.hpp"
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#define TYPES ::yycc::carton::binstore::types
|
||||||
|
#define FORMAT ::yycc::patch::format
|
||||||
|
|
||||||
|
namespace yycc::carton::binstore::setting {
|
||||||
|
|
||||||
|
#pragma region Setting Class
|
||||||
|
|
||||||
|
Setting::Setting(const std::u8string_view& name) : name(name) {
|
||||||
|
if (name.empty()) {
|
||||||
|
throw std::logic_error("the name of setting should not be empty");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Setting::~Setting() {}
|
||||||
|
|
||||||
|
std::u8string_view Setting::get_name() const {
|
||||||
|
return this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
#pragma region RegisteredSetting Class
|
||||||
|
|
||||||
|
RegisteredSetting::RegisteredSetting(TYPES::Token token, Setting&& setting) : token(token), setting(std::move(setting)) {}
|
||||||
|
|
||||||
|
RegisteredSetting::~RegisteredSetting() {}
|
||||||
|
|
||||||
|
TYPES::Token RegisteredSetting::get_token() const {
|
||||||
|
return this->token;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Setting& RegisteredSetting::get_setting() const {
|
||||||
|
return this->setting;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
#pragma region SettingCollection Class
|
||||||
|
|
||||||
|
SettingCollection::SettingCollection() : names(), settings() {}
|
||||||
|
|
||||||
|
SettingCollection::~SettingCollection() {}
|
||||||
|
|
||||||
|
TYPES::Token SettingCollection::add_setting(Setting&& setting) {
|
||||||
|
auto token = this->settings.size();
|
||||||
|
|
||||||
|
std::u8string name(setting.get_name());
|
||||||
|
auto [_, ok] = this->names.try_emplace(name, token);
|
||||||
|
if (!ok) {
|
||||||
|
throw std::logic_error(FORMAT::format("duplicated setting name {}", name));
|
||||||
|
}
|
||||||
|
|
||||||
|
this->settings.emplace_back(RegisteredSetting(token, std::move(setting)));
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<TYPES::Token> SettingCollection::find_name(const std::u8string_view& name) const {
|
||||||
|
auto finder = this->names.find(std::u8string(name));
|
||||||
|
if (finder == this->names.end()) return std::nullopt;
|
||||||
|
else return finder->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SettingCollection::has_setting(TYPES::Token token) const {
|
||||||
|
return token < this->settings.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
const Setting& SettingCollection::get_setting(TYPES::Token token) const {
|
||||||
|
return this->settings.at(token).get_setting();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<RegisteredSetting>& SettingCollection::all_settings() const {
|
||||||
|
return this->settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t SettingCollection::length() const {
|
||||||
|
return this->settings.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SettingCollection::empty() const {
|
||||||
|
return this->settings.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
} // namespace yycc::carton::binstore::setting
|
||||||
128
src/yycc/carton/binstore/setting.hpp
Normal file
128
src/yycc/carton/binstore/setting.hpp
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
/**
|
||||||
|
* @file setting.hpp
|
||||||
|
* @brief Setting management for the binstore module.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "../../macro/class_copy_move.hpp"
|
||||||
|
#include "types.hpp"
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#define NS_YYCC_BINSTORE_TYPES ::yycc::carton::binstore::types
|
||||||
|
|
||||||
|
namespace yycc::carton::binstore::setting {
|
||||||
|
|
||||||
|
/// @brief Represents a single setting with a name.
|
||||||
|
class Setting {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Construct a new Setting object.
|
||||||
|
* @param[in] name The name of the setting.
|
||||||
|
*/
|
||||||
|
Setting(const std::u8string_view& name);
|
||||||
|
~Setting();
|
||||||
|
YYCC_DEFAULT_COPY_MOVE(Setting)
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Get the name of the setting.
|
||||||
|
* @return A u8string_view of the setting name.
|
||||||
|
*/
|
||||||
|
std::u8string_view get_name() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::u8string name;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief Represents a registered setting with both a token and setting information.
|
||||||
|
class RegisteredSetting {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @brief Construct a new Registered Setting object.
|
||||||
|
* @param[in] token The unique token for the setting.
|
||||||
|
* @param[in] setting The setting information.
|
||||||
|
*/
|
||||||
|
RegisteredSetting(NS_YYCC_BINSTORE_TYPES::Token token, Setting&& setting);
|
||||||
|
~RegisteredSetting();
|
||||||
|
YYCC_DEFAULT_COPY_MOVE(RegisteredSetting)
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Get the token associated with this registered setting.
|
||||||
|
* @return The token.
|
||||||
|
*/
|
||||||
|
NS_YYCC_BINSTORE_TYPES::Token get_token() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the setting instance associated with this registered setting.
|
||||||
|
* @return A const reference to the setting.
|
||||||
|
*/
|
||||||
|
const Setting& get_setting() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
NS_YYCC_BINSTORE_TYPES::Token token; ///< The unique token for the setting.
|
||||||
|
Setting setting; ///< The setting instance.
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief Collection of settings with name-based lookup and token-based access.
|
||||||
|
class SettingCollection {
|
||||||
|
public:
|
||||||
|
/// @brief Construct a new Setting Collection object.
|
||||||
|
SettingCollection();
|
||||||
|
~SettingCollection();
|
||||||
|
YYCC_DEFAULT_COPY_MOVE(SettingCollection)
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Add a setting to the collection.
|
||||||
|
* @param[in] setting The setting to add.
|
||||||
|
* @return The token assigned to the setting.
|
||||||
|
*/
|
||||||
|
NS_YYCC_BINSTORE_TYPES::Token add_setting(Setting&& setting);
|
||||||
|
/**
|
||||||
|
* @brief Find a setting by its name.
|
||||||
|
* @param[in] name The name of the setting to find.
|
||||||
|
* @return An optional token if the setting was found, std::nullopt otherwise.
|
||||||
|
*/
|
||||||
|
std::optional<NS_YYCC_BINSTORE_TYPES::Token> find_name(const std::u8string_view& name) const;
|
||||||
|
/**
|
||||||
|
* @brief Check if a setting with the given token exists.
|
||||||
|
* @param[in] token The token to check.
|
||||||
|
* @return True if the setting exists, false otherwise.
|
||||||
|
*/
|
||||||
|
bool has_setting(NS_YYCC_BINSTORE_TYPES::Token token) const;
|
||||||
|
/**
|
||||||
|
* @brief Get a setting by its token.
|
||||||
|
* @param[in] token The token of the setting to get.
|
||||||
|
* @return A const reference to the setting.
|
||||||
|
* @exception May throw if the token is invalid.
|
||||||
|
*/
|
||||||
|
const Setting& get_setting(NS_YYCC_BINSTORE_TYPES::Token token) const;
|
||||||
|
/**
|
||||||
|
* @brief Get all registered settings.
|
||||||
|
* @return A const reference to the vector of registered settings.
|
||||||
|
*/
|
||||||
|
const std::vector<RegisteredSetting>& all_settings() const;
|
||||||
|
/**
|
||||||
|
* @brief Get the number of settings in the collection.
|
||||||
|
* @return The number of settings.
|
||||||
|
*/
|
||||||
|
size_t length() const;
|
||||||
|
/**
|
||||||
|
* @brief Check if the collection is empty.
|
||||||
|
* @return True if empty, false otherwise.
|
||||||
|
*/
|
||||||
|
bool empty() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::map<std::u8string, NS_YYCC_BINSTORE_TYPES::Token> names;
|
||||||
|
std::vector<RegisteredSetting> settings;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace yycc::carton::binstore::setting
|
||||||
|
|
||||||
|
#undef NS_YYCC_BINSTORE_TYPES
|
||||||
245
src/yycc/carton/binstore/storage.cpp
Normal file
245
src/yycc/carton/binstore/storage.cpp
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
#include "storage.hpp"
|
||||||
|
#include "../../num/safe_cast.hpp"
|
||||||
|
#include <fstream>
|
||||||
|
#include <concepts>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#define TYPES ::yycc::carton::binstore::types
|
||||||
|
#define CFG ::yycc::carton::binstore::configuration
|
||||||
|
#define SERDES ::yycc::carton::binstore::serdes
|
||||||
|
#define SAFECAST ::yycc::num::safe_cast
|
||||||
|
|
||||||
|
namespace yycc::carton::binstore::storage {
|
||||||
|
|
||||||
|
#pragma region Read and Write Helper Functions
|
||||||
|
|
||||||
|
static bool is_eof(std::istream& s) {
|
||||||
|
// Peek next value and check bit flag.
|
||||||
|
s.peek();
|
||||||
|
return s.eof();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool read_buffer(std::istream& s, void* buffer, size_t length) {
|
||||||
|
// Cast length
|
||||||
|
auto rv_length = SAFECAST::try_to<std::streamsize>(length);
|
||||||
|
if (!rv_length.has_value()) return false;
|
||||||
|
auto cast_length = rv_length.value();
|
||||||
|
|
||||||
|
// Read data.
|
||||||
|
s.read(static_cast<std::istream::char_type*>(buffer), cast_length);
|
||||||
|
// Check read count.
|
||||||
|
if (s.gcount() != cast_length) return false;
|
||||||
|
// Return IO status.
|
||||||
|
return s.good();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
requires std::is_pod_v<T>
|
||||||
|
static bool read_pod(std::istream& s, T& val) {
|
||||||
|
return read_buffer(s, &val, sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool read_byte_array(std::istream& s, TYPES::ByteArray& ba) {
|
||||||
|
size_t length = 0;
|
||||||
|
if (!read_pod(s, length)) return false;
|
||||||
|
|
||||||
|
// Resize byte array.
|
||||||
|
// There is an exception may be thrown that resized length is too large.
|
||||||
|
// We need capture it and return error.
|
||||||
|
try {
|
||||||
|
ba.resize_data(length);
|
||||||
|
} catch (const std::exception&) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Read data into byte array.
|
||||||
|
read_buffer(s, ba.get_data_ptr(), length);
|
||||||
|
// Okey
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool read_u8string(std::istream& s, std::u8string& strl) {
|
||||||
|
size_t length = 0;
|
||||||
|
if (!read_pod(s, length)) return false;
|
||||||
|
|
||||||
|
// Same reason for try-catch like ByteArray.
|
||||||
|
try {
|
||||||
|
strl.resize(length);
|
||||||
|
} catch (const std::exception&) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Read data into byte array.
|
||||||
|
read_buffer(s, strl.data(), length);
|
||||||
|
// Okey
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool write_buffer(std::ostream& s, const void* buffer, size_t length) {
|
||||||
|
// Cast length
|
||||||
|
auto rv_length = SAFECAST::try_to<std::streamsize>(length);
|
||||||
|
if (!rv_length.has_value()) return false;
|
||||||
|
auto cast_length = rv_length.value();
|
||||||
|
|
||||||
|
// Write data.
|
||||||
|
s.write(static_cast<const std::istream::char_type*>(buffer), cast_length);
|
||||||
|
// There is no function to tell how many data was written,
|
||||||
|
// so directly return.
|
||||||
|
return s.good();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
requires std::is_pod_v<T>
|
||||||
|
static bool write_pod(std::ostream& s, const T& val) {
|
||||||
|
return write_buffer(s, &val, sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool write_byte_array(std::ostream& s, const TYPES::ByteArray& ba) {
|
||||||
|
// Write length header.
|
||||||
|
auto length = ba.get_data_size();
|
||||||
|
if (!write_pod(s, length)) return false;
|
||||||
|
// Write body
|
||||||
|
return write_buffer(s, ba.get_data_ptr(), length);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool write_u8string(std::ostream& s, const std::u8string_view& sv) {
|
||||||
|
// Write length header.
|
||||||
|
auto length = sv.length();
|
||||||
|
if (!write_pod(s, length)) return false;
|
||||||
|
// Write body
|
||||||
|
return write_buffer(s, sv.data(), length);
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
#pragma region Storage Class
|
||||||
|
|
||||||
|
Storage::Storage(CFG::Configuration&& cfg) : cfg(std::move(cfg)), raws() {}
|
||||||
|
|
||||||
|
Storage::~Storage() {}
|
||||||
|
|
||||||
|
TYPES::BinstoreResult<void> Storage::load_from_file(const std::filesystem::path& fpath, LoadStrategy strategy) {
|
||||||
|
std::ifstream fs(fpath, std::ios::binary);
|
||||||
|
if (fs.is_open()) {
|
||||||
|
auto rv = this->load(fs, strategy);
|
||||||
|
fs.close();
|
||||||
|
return rv;
|
||||||
|
} else {
|
||||||
|
return std::unexpected(TYPES::BinstoreError::Io);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPES::BinstoreResult<void> Storage::load(std::istream& s, LoadStrategy strategy) {
|
||||||
|
// Before loading, we need clear all stored raw data first.
|
||||||
|
this->clear();
|
||||||
|
|
||||||
|
// Read identifier
|
||||||
|
TYPES::VersionIdentifier version;
|
||||||
|
if (!read_pod(s, version)) return std::unexpected(TYPES::BinstoreError::Io);
|
||||||
|
|
||||||
|
// Check identifier with strategy
|
||||||
|
{
|
||||||
|
bool ok_for_read = false;
|
||||||
|
auto expected_version = this->cfg.get_version();
|
||||||
|
switch (strategy) {
|
||||||
|
case LoadStrategy::OnlyCurrent:
|
||||||
|
ok_for_read = (version == expected_version);
|
||||||
|
break;
|
||||||
|
case LoadStrategy::MigrateOld:
|
||||||
|
ok_for_read = (version <= expected_version);
|
||||||
|
break;
|
||||||
|
case LoadStrategy::AcceptAll:
|
||||||
|
ok_for_read = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!ok_for_read) return std::unexpected(TYPES::BinstoreError::BadVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read settings one by one.
|
||||||
|
const auto& settings = this->cfg.get_settings();
|
||||||
|
while (!is_eof(s)) {
|
||||||
|
// Read setting name
|
||||||
|
std::u8string setting_name;
|
||||||
|
if (!read_u8string(s, setting_name)) return std::unexpected(TYPES::BinstoreError::Io);
|
||||||
|
|
||||||
|
// Read setting body
|
||||||
|
TYPES::ByteArray ba;
|
||||||
|
if (!read_byte_array(s, ba)) return std::unexpected(TYPES::BinstoreError::Io);
|
||||||
|
|
||||||
|
// Check whether there is such setting and its token.
|
||||||
|
auto token_finder = settings.find_name(setting_name);
|
||||||
|
// If no such name, skip this setting.
|
||||||
|
if (!token_finder.has_value()) continue;
|
||||||
|
auto token = token_finder.value();
|
||||||
|
|
||||||
|
// If there is duplicated entry, report error
|
||||||
|
if (this->raws.contains(token)) return std::unexpected(TYPES::BinstoreError::DuplicatedAssign);
|
||||||
|
// Otherwise insert it
|
||||||
|
this->raws.emplace(token, std::move(ba));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Okey
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPES::BinstoreResult<void> Storage::save_into_file(const std::filesystem::path& fpath) {
|
||||||
|
std::ofstream fs(fpath, std::ios::binary);
|
||||||
|
if (fs.is_open()) {
|
||||||
|
auto rv = this->save(fs);
|
||||||
|
fs.close();
|
||||||
|
return rv;
|
||||||
|
} else {
|
||||||
|
return std::unexpected(TYPES::BinstoreError::Io);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPES::BinstoreResult<void> Storage::save(std::ostream& s) {
|
||||||
|
// Write version identifier
|
||||||
|
auto version = this->cfg.get_version();
|
||||||
|
if (!write_pod(s, version)) return std::unexpected(TYPES::BinstoreError::Io);
|
||||||
|
|
||||||
|
// Write settings one by one
|
||||||
|
const auto& settings = this->cfg.get_settings();
|
||||||
|
for (const auto& [setting_token, setting_value] : this->raws) {
|
||||||
|
// Fetch setting name first
|
||||||
|
auto setting_name = settings.get_setting(setting_token).get_name();
|
||||||
|
|
||||||
|
// Write name
|
||||||
|
if (!write_u8string(s, setting_name)) return std::unexpected(TYPES::BinstoreError::Io);
|
||||||
|
// Write setting body
|
||||||
|
if (!write_byte_array(s, setting_value)) return std::unexpected(TYPES::BinstoreError::Io);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Okey
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Storage::clear() {
|
||||||
|
this->raws.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Storage::has_setting(TYPES::Token token) const {
|
||||||
|
return this->cfg.get_settings().has_setting(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Storage::is_setting_stored(TYPES::Token token) const {
|
||||||
|
if (!this->has_setting(token)) throw std::logic_error("given setting token is invalid");
|
||||||
|
return this->raws.contains(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
const TYPES::ByteArray& Storage::get_raw_value(TYPES::Token token) const {
|
||||||
|
if (!this->has_setting(token)) throw std::logic_error("given setting token is invalid");
|
||||||
|
auto finder = this->raws.find(token);
|
||||||
|
if (finder != this->raws.end()) {
|
||||||
|
return finder->second;
|
||||||
|
} else {
|
||||||
|
throw std::logic_error("given setting has not been stored yet");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Storage::set_raw_value(TYPES::Token token, TYPES::ByteArray&& ba) {
|
||||||
|
if (!this->has_setting(token)) throw std::logic_error("given setting token is invalid");
|
||||||
|
this->raws.insert_or_assign(token, std::move(ba));
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
} // namespace yycc::carton::binstore::storage
|
||||||
230
src/yycc/carton/binstore/storage.hpp
Normal file
230
src/yycc/carton/binstore/storage.hpp
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
/**
|
||||||
|
* @file storage.hpp
|
||||||
|
* @brief Storage management for the binstore module.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "../../macro/class_copy_move.hpp"
|
||||||
|
#include "types.hpp"
|
||||||
|
#include "configuration.hpp"
|
||||||
|
#include "serdes.hpp"
|
||||||
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
|
#include <utility>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <istream>
|
||||||
|
#include <ostream>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#define NS_YYCC_BINSTORE_TYPES ::yycc::carton::binstore::types
|
||||||
|
#define NS_YYCC_BINSTORE_CFG ::yycc::carton::binstore::configuration
|
||||||
|
#define NS_YYCC_BINSTORE_SERDES ::yycc::carton::binstore::serdes
|
||||||
|
|
||||||
|
namespace yycc::carton::binstore::storage {
|
||||||
|
|
||||||
|
/// @brief The strategy when loading from storage.
|
||||||
|
enum class LoadStrategy {
|
||||||
|
/**
|
||||||
|
* @brief Only accept matched version.
|
||||||
|
* @details
|
||||||
|
* Any loading of other versions will explicitly cause error return.
|
||||||
|
* This is convenient for developer who want control migration by themselves.
|
||||||
|
* They can specify this strategy and try to load data with different version configurations
|
||||||
|
* from older to newer one by one.
|
||||||
|
*/
|
||||||
|
OnlyCurrent,
|
||||||
|
/**
|
||||||
|
* @brief Try to migrate old version.
|
||||||
|
* @details
|
||||||
|
* Accept mateched and any older versions.
|
||||||
|
* Any newer versions will explicitly cause error return.
|
||||||
|
* This strategy is good for developer who are lazy to treat this manually.
|
||||||
|
*/
|
||||||
|
MigrateOld,
|
||||||
|
/**
|
||||||
|
* @brief Accept all version.
|
||||||
|
* @details
|
||||||
|
* This strategy is not suggested.
|
||||||
|
* This strategy only suit for quick demo.
|
||||||
|
*/
|
||||||
|
AcceptAll,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief Main storage class for managing binary settings storage.
|
||||||
|
class Storage {
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief All stored values of setting in raw format.
|
||||||
|
* @details Key is the token to already registered settings.
|
||||||
|
* Valus is its stored value in raw data format.
|
||||||
|
*/
|
||||||
|
std::map<NS_YYCC_BINSTORE_TYPES::Token, NS_YYCC_BINSTORE_TYPES::ByteArray> raws;
|
||||||
|
NS_YYCC_BINSTORE_CFG::Configuration cfg; ///< The configuration associated with this storage.
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Construct a new Storage object.
|
||||||
|
* @param[in] cfg The configuration to associate with this storage.
|
||||||
|
*/
|
||||||
|
Storage(NS_YYCC_BINSTORE_CFG::Configuration&& cfg);
|
||||||
|
~Storage();
|
||||||
|
YYCC_DEFAULT_COPY_MOVE(Storage)
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Load settings from a file.
|
||||||
|
* @param[in] fpath Path to the file to load from.
|
||||||
|
* @param[in] strategy The load strategy to use. Defaults to MigrateOld.
|
||||||
|
* @return A BinstoreResult indicating success or failure.
|
||||||
|
*/
|
||||||
|
NS_YYCC_BINSTORE_TYPES::BinstoreResult<void> load_from_file(const std::filesystem::path& fpath,
|
||||||
|
LoadStrategy strategy = LoadStrategy::MigrateOld);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Load settings from an input stream.
|
||||||
|
* @param[in] s The input stream to load from.
|
||||||
|
* @param[in] strategy The load strategy to use. Defaults to MigrateOld.
|
||||||
|
* @return A BinstoreResult indicating success or failure.
|
||||||
|
*/
|
||||||
|
NS_YYCC_BINSTORE_TYPES::BinstoreResult<void> load(std::istream& s, LoadStrategy strategy = LoadStrategy::MigrateOld);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Save settings to a file.
|
||||||
|
* @param[in] fpath Path to the file to save to.
|
||||||
|
* @return A BinstoreResult indicating success or failure.
|
||||||
|
*/
|
||||||
|
NS_YYCC_BINSTORE_TYPES::BinstoreResult<void> save_into_file(const std::filesystem::path& fpath);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Save settings to an output stream.
|
||||||
|
* @param[in] s The output stream to save to.
|
||||||
|
* @return A BinstoreResult indicating success or failure.
|
||||||
|
*/
|
||||||
|
NS_YYCC_BINSTORE_TYPES::BinstoreResult<void> save(std::ostream& s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clear all raw data saved in internal cache.
|
||||||
|
* @details This will cause every setting was set in default value when user fetching them.
|
||||||
|
*/
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief Check whether given token is a valid token to registered settings.
|
||||||
|
* @param[in] token Token for checking.
|
||||||
|
* @return True if it is a registered setting, otherwise false.
|
||||||
|
*/
|
||||||
|
bool has_setting(NS_YYCC_BINSTORE_TYPES::Token token) const;
|
||||||
|
/**
|
||||||
|
* @brief Check whether given token is stored in raw value dictionary.
|
||||||
|
* @param[in] token Token for checking.
|
||||||
|
* @return True if it is stored, otherwise false.
|
||||||
|
* @exception std::logic_error Given token is not point to a registered setting.
|
||||||
|
*/
|
||||||
|
bool is_setting_stored(NS_YYCC_BINSTORE_TYPES::Token token) const;
|
||||||
|
/**
|
||||||
|
* @brief Get raw value of given setting.
|
||||||
|
* @param[in] token Token for fetching.
|
||||||
|
* @return The const reference to given setting's raw data.
|
||||||
|
* @exception std::logic_error Given token is not point to a registered setting.
|
||||||
|
* @exception std::logic_error Given token pointed to setting is not stored yet.
|
||||||
|
*/
|
||||||
|
const NS_YYCC_BINSTORE_TYPES::ByteArray& get_raw_value(NS_YYCC_BINSTORE_TYPES::Token token) const;
|
||||||
|
/**
|
||||||
|
* @brief Set raw value for given setting.
|
||||||
|
* @param[in] token Token for setting.
|
||||||
|
* @param[in] ba The right-value raw value for setting.
|
||||||
|
* @exception std::logic_error Given token is not point to a registered setting.
|
||||||
|
*/
|
||||||
|
void set_raw_value(NS_YYCC_BINSTORE_TYPES::Token token, NS_YYCC_BINSTORE_TYPES::ByteArray&& ba);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Reset given setting into default value.
|
||||||
|
* @tparam T The SerDes applied to this setting.
|
||||||
|
* @param[in] token Token to setting for resetting.
|
||||||
|
* @param[in] serdes Optional SerDes passed if it can not be constructed from default ctor.
|
||||||
|
*/
|
||||||
|
template<NS_YYCC_BINSTORE_SERDES::SerDes T>
|
||||||
|
void reset_value(NS_YYCC_BINSTORE_TYPES::Token token, const T& serdes = T{}) {
|
||||||
|
// Check whether has this setting.
|
||||||
|
if (!has_setting(token)) throw std::logic_error("given setting token is invalid");
|
||||||
|
// Reset it.
|
||||||
|
auto ba = serdes.reset();
|
||||||
|
this->set_raw_value(token, std::move(ba));
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief Get given setting's value.
|
||||||
|
* @tparam T The SerDes applied to this setting.
|
||||||
|
* @param[in] token Token to setting for fetching value.
|
||||||
|
* @param[in] serdes Optional SerDes passed if it can not be constructed from default ctor.
|
||||||
|
* @return Fetched value. If there is no such setting before or fail to deserialize underlying data,
|
||||||
|
* Default value will be forcely set before the return of this function.
|
||||||
|
*/
|
||||||
|
template<NS_YYCC_BINSTORE_SERDES::SerDes T>
|
||||||
|
NS_YYCC_BINSTORE_SERDES::SerDesValueType<T> get_value(NS_YYCC_BINSTORE_TYPES::Token token, const T& serdes = T{}) {
|
||||||
|
// Check whether has this setting.
|
||||||
|
if (!has_setting(token)) throw std::logic_error("given setting token is invalid");
|
||||||
|
|
||||||
|
// If we have stored raw value, we fetch it first.
|
||||||
|
if (this->is_setting_stored(token)) {
|
||||||
|
// Get raw value.
|
||||||
|
const auto& ba = this->get_raw_value(token);
|
||||||
|
// Try to deserialize it.
|
||||||
|
auto value = serdes.deserialize(ba);
|
||||||
|
// If the result is okey, return it.
|
||||||
|
if (value.has_value()) {
|
||||||
|
return value.value();
|
||||||
|
}
|
||||||
|
// Otherwise we need reset it into default value.
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we do not have this setting, or we need reset it into default value
|
||||||
|
// due to failed deserialization, we need execute following code.
|
||||||
|
// Reset its value first
|
||||||
|
this->reset_value(token, serdes);
|
||||||
|
// The re-fetch its raw value and deserialize it.
|
||||||
|
const auto& ba = this->get_raw_value(token);
|
||||||
|
auto value = serdes.deserialize(ba);
|
||||||
|
// Default value must can be deserialized.
|
||||||
|
// If not, throw exception.
|
||||||
|
if (value.has_value()) {
|
||||||
|
return value.value();
|
||||||
|
} else {
|
||||||
|
throw std::logic_error("default value must can be deserialized");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @brief Set given setting's value.
|
||||||
|
* @tparam T The SerDes applied to this setting.
|
||||||
|
* @param[in] token Token to setting for setting value.
|
||||||
|
* @param[in] value The value to set.
|
||||||
|
* @param[in] serdes Optional SerDes passed if it can not be constructed from default ctor.
|
||||||
|
* @return True if the setting was set to your given value,
|
||||||
|
* otherwise false, the value was set as default value because given value can not be serialized.
|
||||||
|
*/
|
||||||
|
template<NS_YYCC_BINSTORE_SERDES::SerDes T>
|
||||||
|
bool set_value(NS_YYCC_BINSTORE_TYPES::Token token,
|
||||||
|
const NS_YYCC_BINSTORE_SERDES::SerDesValueType<T>& value,
|
||||||
|
const T& serdes = T{}) {
|
||||||
|
// Check whether has this setting.
|
||||||
|
if (!has_setting(token)) throw std::logic_error("given setting token is invalid");
|
||||||
|
|
||||||
|
// We try to serialize given value first.
|
||||||
|
auto rv_ser = serdes.serialize(value);
|
||||||
|
// If we can serialize it, we directly use it,'
|
||||||
|
// otherwise we need fetch it from default value.
|
||||||
|
auto success_ser = rv_ser.has_value();
|
||||||
|
auto ba = success_ser ? std::move(rv_ser.value()) : serdes.reset();
|
||||||
|
// Assign it to setting's raw value.
|
||||||
|
this->set_raw_value(token, std::move(ba));
|
||||||
|
|
||||||
|
// Return result
|
||||||
|
return success_ser;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace yycc::carton::binstore::storage
|
||||||
|
|
||||||
|
#undef NS_YYCC_BINSTORE_SERDES
|
||||||
|
#undef NS_YYCC_BINSTORE_CFG
|
||||||
|
#undef NS_YYCC_BINSTORE_TYPES
|
||||||
29
src/yycc/carton/binstore/types.cpp
Normal file
29
src/yycc/carton/binstore/types.cpp
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#include "types.hpp"
|
||||||
|
|
||||||
|
namespace yycc::carton::binstore::types {
|
||||||
|
|
||||||
|
#pragma region ByteArray Class
|
||||||
|
|
||||||
|
ByteArray::ByteArray() : datas() {}
|
||||||
|
|
||||||
|
ByteArray::~ByteArray() {}
|
||||||
|
|
||||||
|
size_t ByteArray::get_data_size() const {
|
||||||
|
return this->datas.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ByteArray::resize_data(size_t new_size) {
|
||||||
|
this->datas.resize(new_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
const void* ByteArray::get_data_ptr(size_t offset) const {
|
||||||
|
return this->datas.data() + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* ByteArray::get_data_ptr(size_t offset) {
|
||||||
|
return this->datas.data() + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
} // namespace yycc::carton::binstore::types
|
||||||
82
src/yycc/carton/binstore/types.hpp
Normal file
82
src/yycc/carton/binstore/types.hpp
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "../../macro/class_copy_move.hpp"
|
||||||
|
#include <expected>
|
||||||
|
#include <vector>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace yycc::carton::binstore::types {
|
||||||
|
|
||||||
|
/// @brief All possible error kind occurs in this module.
|
||||||
|
enum class BinstoreError {
|
||||||
|
NoSuchSetting, ///< Given token is invalid for setting.
|
||||||
|
DuplicatedAssign, ///< Duplicated setting entry in data.
|
||||||
|
BadVersion, ///< The version provided in given file or IO is rejected by version strategy.
|
||||||
|
Io, ///< C++ IO error.
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief The result type used in this module.
|
||||||
|
template<typename T>
|
||||||
|
using BinstoreResult = std::expected<T, BinstoreError>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An unique token type.
|
||||||
|
* @details
|
||||||
|
* When outside code registering an setting in collection
|
||||||
|
* there must be a token returned by manager.
|
||||||
|
* When outside code want to visit this registered item again,
|
||||||
|
* they should provide this token returned when registering.
|
||||||
|
*
|
||||||
|
* Its value actually is the index of its stored vector.
|
||||||
|
* So this type is an alias to vector size type.
|
||||||
|
*/
|
||||||
|
using Token = size_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The identifier reprsenting version in binstore.
|
||||||
|
* @details Higher number in identifier means that the newer version.
|
||||||
|
* In most cases, new version can accept old version's configurations.
|
||||||
|
* Once the struct of configurations was changed, this identifier should be bumped up.
|
||||||
|
*/
|
||||||
|
using VersionIdentifier = uint64_t;
|
||||||
|
|
||||||
|
/// @private
|
||||||
|
/// @brief The raw data of setting.
|
||||||
|
class ByteArray {
|
||||||
|
public:
|
||||||
|
ByteArray();
|
||||||
|
~ByteArray();
|
||||||
|
YYCC_DEFAULT_COPY_MOVE(ByteArray)
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Get the length of internal buffer.
|
||||||
|
* @remarks This is usually used when reading data.
|
||||||
|
* @return The length of internal buffer.
|
||||||
|
*/
|
||||||
|
size_t get_data_size() const;
|
||||||
|
/**
|
||||||
|
* @brief Resize internal buffer to given size.
|
||||||
|
* @remarks This is usually used when writing data.
|
||||||
|
* @param[in] new_size The new size of internal buffer.
|
||||||
|
*/
|
||||||
|
void resize_data(size_t new_size);
|
||||||
|
/**
|
||||||
|
* @brief Get data pointer to internal buffer for reading.
|
||||||
|
* @remarks This is usually used when reading data.
|
||||||
|
* @param[in] offset The offset in byte added to underlying pointer.
|
||||||
|
* @return The data pointer to internal buffer.
|
||||||
|
*/
|
||||||
|
const void* get_data_ptr(size_t offset = 0) const;
|
||||||
|
/**
|
||||||
|
* @brief Get data pointer to internal buffer for writing.
|
||||||
|
* @remarks This is usually used when writing data.
|
||||||
|
* @param[in] offset The offset in byte added to underlying pointer.
|
||||||
|
* @return The data pointer to internal buffer.
|
||||||
|
*/
|
||||||
|
void* get_data_ptr(size_t offset = 0);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<uint8_t> datas; ///< The internal buffer.
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace yycc::carton::binstore::types
|
||||||
30
src/yycc/carton/clap.hpp
Normal file
30
src/yycc/carton/clap.hpp
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "clap/types.hpp"
|
||||||
|
#include "clap/validator.hpp"
|
||||||
|
#include "clap/option.hpp"
|
||||||
|
#include "clap/variable.hpp"
|
||||||
|
#include "clap/summary.hpp"
|
||||||
|
#include "clap/application.hpp"
|
||||||
|
#include "clap/manual.hpp"
|
||||||
|
#include "clap/parser.hpp"
|
||||||
|
#include "clap/resolver.hpp"
|
||||||
|
|
||||||
|
// TODO: Support multiple arguments for single option.
|
||||||
|
/**
|
||||||
|
* @brief Command Line Argument Parser (CLAP) module for handling command line arguments and environment variables.
|
||||||
|
* @details 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.
|
||||||
|
*
|
||||||
|
* The main components include:
|
||||||
|
* \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
|
||||||
|
*/
|
||||||
|
namespace yycc::carton::clap {}
|
||||||
28
src/yycc/carton/clap/application.cpp
Normal file
28
src/yycc/carton/clap/application.cpp
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#include "application.hpp"
|
||||||
|
|
||||||
|
#define CLAP ::yycc::carton::clap
|
||||||
|
|
||||||
|
namespace yycc::carton::clap::application {
|
||||||
|
|
||||||
|
using Summary = CLAP::summary::Summary;
|
||||||
|
using OptionCollection = CLAP::option::OptionCollection;
|
||||||
|
using VariableCollection = CLAP::variable::VariableCollection;
|
||||||
|
|
||||||
|
Application::Application(Summary &&summary, OptionCollection &&options, VariableCollection &&variables) :
|
||||||
|
summary(std::move(summary)), options(std::move(options)), variables(std::move(variables)) {}
|
||||||
|
|
||||||
|
Application::~Application() {}
|
||||||
|
|
||||||
|
const Summary &Application::get_summary() const {
|
||||||
|
return this->summary;
|
||||||
|
}
|
||||||
|
|
||||||
|
const OptionCollection &Application::get_options() const {
|
||||||
|
return this->options;
|
||||||
|
}
|
||||||
|
|
||||||
|
const VariableCollection &Application::get_variables() const {
|
||||||
|
return this->variables;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace yycc::carton::clap::application
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user