Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
bec36b4b3c | |||
0b7e58c8e8 | |||
831fa130bc | |||
7adac00035 | |||
0cd9582757 | |||
2206825223 | |||
21f7e7f786 | |||
50dd086b53 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,5 +1,6 @@
|
||||
# -------------------- Output --------------------
|
||||
out/
|
||||
src/YYCC/YYCCVersion.hpp
|
||||
CMakeSettings.json
|
||||
|
||||
# -------------------- VSCode --------------------
|
||||
|
@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.23)
|
||||
project(YYCC
|
||||
VERSION 1.2.0
|
||||
VERSION 1.3.0
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
||||
@ -46,7 +46,7 @@ write_basic_package_version_file(
|
||||
configure_package_config_file(
|
||||
${CMAKE_CURRENT_LIST_DIR}/cmake/YYCCommonplaceConfig.cmake.in
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/YYCCommonplaceConfig.cmake"
|
||||
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/YYCCommonplace
|
||||
INSTALL_DESTINATION ${YYCC_INSTALL_LIB_PATH}/cmake/YYCCommonplace
|
||||
)
|
||||
# Copy package files to install destination
|
||||
install(
|
||||
@ -54,6 +54,6 @@ FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/YYCCommonplaceConfig.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/YYCCommonplaceConfigVersion.cmake"
|
||||
DESTINATION
|
||||
${CMAKE_INSTALL_LIBDIR}/cmake/YYCCommonplace
|
||||
${YYCC_INSTALL_LIB_PATH}/cmake/YYCCommonplace
|
||||
)
|
||||
|
||||
|
5
cmake/YYCCVersion.hpp.in
Normal file
5
cmake/YYCCVersion.hpp.in
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#define YYCC_VER_MAJOR @PROJECT_VERSION_MAJOR@
|
||||
#define YYCC_VER_MINOR @PROJECT_VERSION_MINOR@
|
||||
#define YYCC_VER_PATCH @PROJECT_VERSION_PATCH@
|
@ -14,5 +14,6 @@ add_custom_target (YYCCDocumentation
|
||||
|
||||
# Install built documentation
|
||||
install (DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html
|
||||
CONFIGURATIONS Release RelWithDebInfo MinSizeRel
|
||||
DESTINATION ${YYCC_INSTALL_DOC_PATH}
|
||||
)
|
||||
|
@ -86,12 +86,66 @@ so it must be initialized after initializing all settings.
|
||||
|
||||
When initializing core manager, you need assign config file path first.
|
||||
Then you need specify a version number.
|
||||
Version number will be used when reading config file.
|
||||
If the version of config file is higher than your given number,
|
||||
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.
|
||||
Version number is important.
|
||||
It will be used when reading config file and only can be increased if needed (version can not downgrade).
|
||||
The last argument is an initializer list which contain the \b pointer to all settings this manager managed.
|
||||
|
||||
When executing YYCC::ConfigManager::CoreManager::Load to load configs, it will perform following steps one by one:
|
||||
|
||||
<UL>
|
||||
<LI>
|
||||
Open given config file.
|
||||
<UL>
|
||||
<LI>
|
||||
If given file is not existing, loading function will simply return and all configs will be reset to its default value.
|
||||
</LI>
|
||||
<LI>
|
||||
Success to open file, go to next step.
|
||||
</LI>
|
||||
</UL>
|
||||
</LI>
|
||||
|
||||
<LI>
|
||||
Fetch version number from file.
|
||||
<UL>
|
||||
<LI>
|
||||
If fail to read version number from file, loading function will simply return and all configs will be reset to its default value.
|
||||
</LI>
|
||||
<LI>
|
||||
If the version of config file is higher than your specified version number when constructing this class,
|
||||
core manager will assume you are trying to read a config file created by a higher version program,
|
||||
and will reject reading and use default value for all settings.
|
||||
</LI>
|
||||
<LI>
|
||||
If the version of config file is lower than your specified version number,
|
||||
core manager will try to read config file and do proper migration (set default value for configs which do not existing) if possible.
|
||||
</LI>
|
||||
<LI>
|
||||
If the version of config file is equal than your specified version number,
|
||||
core manager will read config file normally.
|
||||
</LI>
|
||||
</UL>
|
||||
</LI>
|
||||
|
||||
<LI>
|
||||
Read config file body.
|
||||
<UL>
|
||||
<LI>
|
||||
If any IO error occurs when reading, loading function will simply return.
|
||||
All read config will keep their read value and all configs which has not been read will keep their default value.
|
||||
</LI>
|
||||
<LI>
|
||||
If some config can not parse binary data to its type,
|
||||
this config will be skipped and core manager will process next config.
|
||||
This config will keep its default value.
|
||||
</LI>
|
||||
</UL>
|
||||
</LI>
|
||||
</UL>
|
||||
|
||||
All of these scenarios can be found by the return value of loading function.
|
||||
The return type of loading function, ConfigLoadResult is a flag enum.
|
||||
You can find whether loading process happend specified issue by using bitwise operation on it.
|
||||
|
||||
*/
|
||||
}
|
35
doc/src/enum_helper.dox
Normal file
35
doc/src/enum_helper.dox
Normal file
@ -0,0 +1,35 @@
|
||||
namespace YYCC::EnumHelper {
|
||||
/**
|
||||
|
||||
\page enum_helper Scoped Enum Helper
|
||||
|
||||
\section enum_helper__intro Intro
|
||||
|
||||
C++ introduce a new enum called scoped enum.
|
||||
It is better than legacy C enum because it will not leak name into namespace where it locate,
|
||||
and also can specify an underlying type to it to make sure it is stored as specified size.
|
||||
However, the shortcoming of it is that it lack bitwise operator comparing with legacy C enum.
|
||||
Programmer must implement them for scoped enum one by one.
|
||||
It is a hardship and inconvenient.
|
||||
This is the reason why I invent this class
|
||||
|
||||
\section enum_helper__Usage Usage
|
||||
|
||||
In this namespace, we provide all bitwise functions related to scoped enum type which may be used.
|
||||
See YYCC::EnumHelper for more detail (It is more clear to read function annotation than I introduce in there repeatedly).
|
||||
|
||||
\section enum_helper__why Why not Operator Overload
|
||||
|
||||
I have try it (and you even can see the relic of it in source code).
|
||||
But it need a extra statement written in following to include it, otherwise compiler can not see it.
|
||||
|
||||
\code
|
||||
using namespace YYCC::EnumHelper;
|
||||
\endcode
|
||||
|
||||
Another reason why I do not use this method is that
|
||||
this overload strategy may be applied to some type which should not be applied by accient, such as non-scoped enum type.
|
||||
So I gave up this solution.
|
||||
|
||||
*/
|
||||
}
|
@ -45,6 +45,8 @@
|
||||
|
||||
\li \subpage std_patch
|
||||
|
||||
\li \subpage enum_helper
|
||||
|
||||
<B>Advanced Features</B>
|
||||
|
||||
\li \subpage constraints
|
||||
|
@ -116,11 +116,14 @@ Due to the different defination of UTF8 char type,
|
||||
C++ 20 program can not use this library built by C++ 17 environment.
|
||||
So this switch give you a chance to decide the version of C++ standard used when building.
|
||||
The lowest and defult version of C++ standard is 17.
|
||||
\li \c -d, \c --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,
|
||||
however if you are the developer of this library, you may need this switch.
|
||||
Because documentation take too much disk space and cost a bunch of time for building and copying.
|
||||
In default, generator will produce script which 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>.
|
||||
and you will also find your MSVC distribution in directory <TT>bin/<I>cpp_ver</I>/msvc_install</TT>.
|
||||
|
@ -6,6 +6,48 @@ namespace YYCC {
|
||||
In this page we will introduce the macros defined by this library
|
||||
which can not be grouped in other topic.
|
||||
|
||||
\section library_macros__batch_class_copy_move Library Version and Version Comparison
|
||||
|
||||
Version is a important things in modern software development, especially for a library.
|
||||
In YYCC, we use Semantic Versioning as our version standard.
|
||||
For more infomations about it, please see: https://semver.org/
|
||||
|
||||
First, YYCC has its own version and it can be visited by
|
||||
\c YYCC_VER_MAJOR, \c YYCC_VER_MINOR, and \c YYCC_VER_PATCH.
|
||||
Each part of Semantic Versioning is provided individually.
|
||||
|
||||
YYCC also provide a bunch of macros to compare 2 versions.
|
||||
It also provides a way to check YYCC version in program using YYCC,
|
||||
because some of them rely on a specific version of YYCC.
|
||||
There is a list of these comparison macros.
|
||||
|
||||
\li YYCC_VERCMP_E
|
||||
\li YYCC_VERCMP_NE
|
||||
\li YYCC_VERCMP_G
|
||||
\li YYCC_VERCMP_GE
|
||||
\li YYCC_VERCMP_NL
|
||||
\li YYCC_VERCMP_L
|
||||
\li YYCC_VERCMP_LE
|
||||
\li YYCC_VERCMP_NG
|
||||
|
||||
You may notice all of these macros are starts with \c YYCC_VERCMP_,
|
||||
and their tails are inspired from x86 ASM comparison jump code.
|
||||
For example, \c E means "equal" and \c NE means "not equal",
|
||||
\c G means "greater", \c GE means "greater or equal", and \c NG means "not gretaer".
|
||||
|
||||
All of these macros take 6 arguments,
|
||||
for the first 3 arguments, we call them "left version".
|
||||
From left to right they are the major part, minor part and patch part of semantic version.
|
||||
And for the last 3 arguments, we call them "right version".
|
||||
From left to right they are the major part, minor part and patch part of semantic version.
|
||||
There is a example about checking whether YYCC library version is exactly what we wanted version.
|
||||
|
||||
\code
|
||||
#if YYCC_VERCMP_NE(YYCC_VER_MAJOR, YYCC_VER_MINOR, YYCC_VER_PATCH, 1, 3 ,0)
|
||||
#error "Not Matched YYCC Version"
|
||||
#endif
|
||||
\endcode
|
||||
|
||||
\section library_macros__platform_checker Platform Checker
|
||||
|
||||
In many cross platform applications,
|
||||
|
1
script/.gitignore
vendored
1
script/.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
# -------------------- Output --------------------
|
||||
win_build.bat
|
||||
linux_build.sh
|
||||
|
108
script/gen_build_script.py
Normal file
108
script/gen_build_script.py
Normal file
@ -0,0 +1,108 @@
|
||||
import jinja2
|
||||
import argparse
|
||||
import os
|
||||
import io
|
||||
import re
|
||||
import shlex
|
||||
|
||||
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_for_cmd_exe(arg):
|
||||
meta_re = re.compile(r'([()%!^"<>&|])')
|
||||
return meta_re.sub('^\1', arg)
|
||||
def escape_cmd_argument(arg):
|
||||
if not arg or re.search(r'(["\s])', arg):
|
||||
arg = '"' + arg.replace('"', r'\"') + '"'
|
||||
return escape_for_cmd_exe(arg)
|
||||
def escape_sh_argument(arg):
|
||||
return shlex.quote(arg)
|
||||
|
||||
class ScriptSettings:
|
||||
m_CppVersion: str
|
||||
m_BuildDoc: bool
|
||||
m_PIC: bool
|
||||
|
||||
def __init__(self, cpp_ver: str, build_doc: bool, pic: bool):
|
||||
self.m_CppVersion = cpp_ver
|
||||
self.m_BuildDoc = build_doc
|
||||
self.m_PIC = pic
|
||||
|
||||
class TemplateRender:
|
||||
m_Loader: jinja2.BaseLoader
|
||||
m_Environment: jinja2.Environment
|
||||
|
||||
m_WinTemplate: jinja2.Template
|
||||
m_LinuxTemplate: jinja2.Template
|
||||
|
||||
m_Settings: ScriptSettings
|
||||
|
||||
def __init__(self, settings: ScriptSettings) -> None:
|
||||
self.m_Loader = jinja2.FileSystemLoader(self.__get_dir())
|
||||
self.m_Environment = jinja2.Environment(loader=self.m_Loader)
|
||||
|
||||
self.m_WinTemplate = self.m_Environment.get_template('win_build.template.bat')
|
||||
self.m_LinuxTemplate = self.m_Environment.get_template('linux_build.template.sh')
|
||||
|
||||
self.m_Settings = settings
|
||||
|
||||
def __get_dir(self) -> str:
|
||||
return os.path.dirname(__file__)
|
||||
|
||||
def __escape_path(self, val: str, is_win: bool) -> str:
|
||||
if is_win: return escape_cmd_argument(val)
|
||||
else: return escape_sh_argument(val)
|
||||
|
||||
def __render(self, template: jinja2.Template, dest_file: str, is_win: bool) -> None:
|
||||
with open(os.path.join(self.__get_dir(), dest_file), 'w', encoding='utf-8') as f:
|
||||
f.write(template.render(
|
||||
repo_root_dir = self.__escape_path(os.path.dirname(self.__get_dir()), is_win),
|
||||
cpp_version = self.m_Settings.m_CppVersion,
|
||||
build_doc = self.m_Settings.m_BuildDoc,
|
||||
pic = settings.m_PIC
|
||||
))
|
||||
|
||||
def render_win_script(self) -> None:
|
||||
self.__render(self.m_WinTemplate, 'win_build.bat', True)
|
||||
|
||||
def render_linux_script(self) -> None:
|
||||
self.__render(self.m_LinuxTemplate, 'linux_build.sh', False)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# parse argument
|
||||
parser = argparse.ArgumentParser(
|
||||
prog='YYCC Windows Build Script Generator',
|
||||
description='YYCC Windows Build Script Generator'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-c', '--cpp',
|
||||
action='store', default='17', dest='cpp', type=validate_cpp_ver,
|
||||
help='The version of C++ standard used when building.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-d', '--build-doc',
|
||||
action='store_true', dest='build_doc',
|
||||
help='Build YYCC without documentation.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-p', '--pic',
|
||||
action='store_true', dest='pic',
|
||||
help='Enable Position Independent Code flag on non-Windows platform. This is crucial for compiling dynamic library using this library.'
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# build settings
|
||||
settings = ScriptSettings(args.cpp, args.build_doc, args.pic)
|
||||
# build template render and render result
|
||||
render = TemplateRender(settings)
|
||||
render.render_win_script()
|
||||
render.render_linux_script()
|
||||
|
||||
|
@ -1,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,9 +1,6 @@
|
||||
#!/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
|
||||
# Navigate to project root directory
|
||||
cd {{ repo_root_dir }}
|
||||
|
||||
# Create main binary directory
|
||||
mkdir bin
|
||||
@ -19,10 +16,10 @@ cd ..
|
||||
|
||||
# Build current system debug and release version
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Debug ../.. --fresh
|
||||
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_STANDARD={{ cpp_version }} {{ '-DCMAKE_POSITION_INDEPENDENT_CODE=True' if pic }} ../.. --fresh
|
||||
cmake --build .
|
||||
cmake --install . --prefix ../install/Debug
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DYYCC_BUILD_TESTBENCH=ON ../.. --fresh
|
||||
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_CXX_STANDARD={{ cpp_version }} {{ '-DCMAKE_POSITION_INDEPENDENT_CODE=True' if pic }} -DYYCC_BUILD_TESTBENCH=ON ../.. --fresh
|
||||
cmake --build .
|
||||
cmake --install . --prefix ../install/Release
|
||||
cd ..
|
@ -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
|
85
script/win_build.template.bat
Normal file
85
script/win_build.template.bat
Normal file
@ -0,0 +1,85 @@
|
||||
@ECHO OFF
|
||||
:: Navigate to project root directory
|
||||
CD /d {{ repo_root_dir }}
|
||||
:: Create build directory and enter it
|
||||
MKDIR bin
|
||||
CD bin
|
||||
MKDIR cpp{{ cpp_version }}
|
||||
CD cpp{{ cpp_version }}
|
||||
|
||||
:: Create internal build directory
|
||||
MKDIR Win32
|
||||
MKDIR x64
|
||||
MKDIR documentation
|
||||
:: Create internal install directory
|
||||
MKDIR install
|
||||
CD install
|
||||
MKDIR Win32_Debug
|
||||
MKDIR Win32_Release
|
||||
MKDIR x64_Debug
|
||||
MKDIR x64_Release
|
||||
CD ..
|
||||
:: Create internal MSVC specific install directory
|
||||
MKDIR msvc_install
|
||||
CD msvc_install
|
||||
MKDIR bin
|
||||
MKDIR include
|
||||
MKDIR lib
|
||||
MKDIR share
|
||||
CD bin
|
||||
MKDIR Win32
|
||||
MKDIR x64
|
||||
CD ..
|
||||
CD lib
|
||||
MKDIR Win32\Debug
|
||||
MKDIR Win32\Release
|
||||
MKDIR x64\Debug
|
||||
MKDIR x64\Release
|
||||
CD ..
|
||||
CD ..
|
||||
|
||||
:: Build for Win32
|
||||
CD Win32
|
||||
cmake -A Win32 -DCMAKE_CXX_STANDARD={{ cpp_version }} -DYYCC_BUILD_TESTBENCH=ON ../../..
|
||||
cmake --build . --config Debug
|
||||
cmake --install . --prefix=../install/Win32_Debug --config Debug
|
||||
cmake --build . --config RelWithDebInfo
|
||||
cmake --install . --prefix=../install/Win32_Release --config RelWithDebInfo
|
||||
CD ..
|
||||
:: Build for x64
|
||||
CD x64
|
||||
cmake -A x64 -DCMAKE_CXX_STANDARD={{ cpp_version }} -DYYCC_BUILD_TESTBENCH=ON ../../..
|
||||
cmake --build . --config Debug
|
||||
cmake --install . --prefix=../install/x64_Debug --config Debug
|
||||
cmake --build . --config RelWithDebInfo
|
||||
cmake --install . --prefix=../install/x64_Release --config RelWithDebInfo
|
||||
CD ..
|
||||
|
||||
{% if build_doc %}
|
||||
:: Build for documentation
|
||||
CD documentation
|
||||
cmake -A x64 -DCMAKE_CXX_STANDARD={{ cpp_version }} -DYYCC_BUILD_DOC=ON ../../..
|
||||
cmake --build . --config RelWithDebInfo
|
||||
cmake --build . --target YYCCDocumentation
|
||||
cmake --install . --prefix=../install/x64_Release --config RelWithDebInfo
|
||||
CD ..
|
||||
{% endif %}
|
||||
|
||||
:: Copy header files
|
||||
XCOPY install\x64_Release\include msvc_install\include\ /E /Y
|
||||
:: Copy binary files
|
||||
COPY install\Win32_Release\bin\YYCCTestbench.exe msvc_install\bin\Win32\YYCCTestbench.exe /Y
|
||||
COPY install\x64_Release\bin\YYCCTestbench.exe msvc_install\bin\x64\YYCCTestbench.exe /Y
|
||||
:: Copy library files
|
||||
COPY install\Win32_Debug\lib\YYCCommonplace.lib msvc_install\lib\Win32\Debug\YYCCommonplace.lib /Y
|
||||
COPY install\Win32_Release\lib\YYCCommonplace.lib msvc_install\lib\Win32\Release\YYCCommonplace.lib /Y
|
||||
COPY install\x64_Debug\lib\YYCCommonplace.lib msvc_install\lib\x64\Debug\YYCCommonplace.lib /Y
|
||||
COPY install\x64_Release\lib\YYCCommonplace.lib msvc_install\lib\x64\Release\YYCCommonplace.lib /Y
|
||||
{% if build_doc %}
|
||||
:: Copy documentation files
|
||||
XCOPY install\x64_Release\share msvc_install\share\ /E /Y
|
||||
{% endif %}
|
||||
|
||||
:: Leave build directory and report
|
||||
CD ..\..
|
||||
ECHO Windows CMake Build Done
|
@ -1,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,20 +1,27 @@
|
||||
# Configure version file
|
||||
configure_file(
|
||||
${CMAKE_CURRENT_LIST_DIR}/../cmake/YYCCVersion.hpp.in
|
||||
${CMAKE_CURRENT_LIST_DIR}/YYCC/YYCCVersion.hpp
|
||||
@ONLY
|
||||
)
|
||||
|
||||
# Create static library
|
||||
add_library(YYCCommonplace STATIC "")
|
||||
# Setup static library sources
|
||||
target_sources(YYCCommonplace
|
||||
PRIVATE
|
||||
# Sources
|
||||
COMHelper.cpp
|
||||
ArgParser.cpp
|
||||
ConfigManager.cpp
|
||||
ConsoleHelper.cpp
|
||||
DialogHelper.cpp
|
||||
EncodingHelper.cpp
|
||||
ExceptionHelper.cpp
|
||||
StdPatch.cpp
|
||||
IOHelper.cpp
|
||||
StringHelper.cpp
|
||||
WinFctHelper.cpp
|
||||
YYCC/COMHelper.cpp
|
||||
YYCC/ArgParser.cpp
|
||||
YYCC/ConfigManager.cpp
|
||||
YYCC/ConsoleHelper.cpp
|
||||
YYCC/DialogHelper.cpp
|
||||
YYCC/EncodingHelper.cpp
|
||||
YYCC/ExceptionHelper.cpp
|
||||
YYCC/StdPatch.cpp
|
||||
YYCC/IOHelper.cpp
|
||||
YYCC/StringHelper.cpp
|
||||
YYCC/WinFctHelper.cpp
|
||||
# Natvis (only for MSVC)
|
||||
$<$<CXX_COMPILER_ID:MSVC>:YYCC.natvis>
|
||||
)
|
||||
@ -24,24 +31,27 @@ FILE_SET HEADERS
|
||||
FILES
|
||||
# Headers
|
||||
# Common headers
|
||||
Constraints.hpp
|
||||
COMHelper.hpp
|
||||
ArgParser.hpp
|
||||
ConfigManager.hpp
|
||||
ConsoleHelper.hpp
|
||||
DialogHelper.hpp
|
||||
EncodingHelper.hpp
|
||||
ExceptionHelper.hpp
|
||||
StdPatch.hpp
|
||||
IOHelper.hpp
|
||||
ParserHelper.hpp
|
||||
StringHelper.hpp
|
||||
WinFctHelper.hpp
|
||||
YYCC/Constraints.hpp
|
||||
YYCC/COMHelper.hpp
|
||||
YYCC/ArgParser.hpp
|
||||
YYCC/ConfigManager.hpp
|
||||
YYCC/ConsoleHelper.hpp
|
||||
YYCC/DialogHelper.hpp
|
||||
YYCC/EncodingHelper.hpp
|
||||
YYCC/EnumHelper.hpp
|
||||
YYCC/ExceptionHelper.hpp
|
||||
YYCC/StdPatch.hpp
|
||||
YYCC/IOHelper.hpp
|
||||
YYCC/ParserHelper.hpp
|
||||
YYCC/StringHelper.hpp
|
||||
YYCC/WinFctHelper.hpp
|
||||
# Windows including guard pair
|
||||
WinImportPrefix.hpp
|
||||
WinImportSuffix.hpp
|
||||
# Misc
|
||||
YYCCInternal.hpp
|
||||
YYCC/WinImportPrefix.hpp
|
||||
YYCC/WinImportSuffix.hpp
|
||||
# Internal
|
||||
YYCC/YYCCVersion.hpp
|
||||
YYCC/YYCCInternal.hpp
|
||||
# Exposed
|
||||
YYCCommonplace.hpp
|
||||
)
|
||||
# Setup header infomations
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "Constraints.hpp"
|
||||
#include "EncodingHelper.hpp"
|
||||
#include "ParserHelper.hpp"
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <map>
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include "EncodingHelper.hpp"
|
||||
#include "IOHelper.hpp"
|
||||
#include "EnumHelper.hpp"
|
||||
#include <stdexcept>
|
||||
|
||||
namespace YYCC::ConfigManager {
|
||||
@ -33,7 +34,7 @@ namespace YYCC::ConfigManager {
|
||||
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);
|
||||
@ -44,7 +45,10 @@ namespace YYCC::ConfigManager {
|
||||
}
|
||||
}
|
||||
|
||||
bool CoreManager::Load() {
|
||||
ConfigLoadResult CoreManager::Load() {
|
||||
// prepare result variables
|
||||
ConfigLoadResult ret = ConfigLoadResult::OK;
|
||||
|
||||
// reset all settings first
|
||||
Reset();
|
||||
|
||||
@ -53,20 +57,27 @@ namespace YYCC::ConfigManager {
|
||||
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;
|
||||
ret = ConfigLoadResult::Created;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// fetch version info
|
||||
uint64_t version_info;
|
||||
if (std::fread(&version_info, 1u, sizeof(version_info), fs.get()) != sizeof(version_info))
|
||||
return false;
|
||||
if (std::fread(&version_info, 1u, sizeof(version_info), fs.get()) != sizeof(version_info)) {
|
||||
ret = ConfigLoadResult::Created;
|
||||
return ret;
|
||||
}
|
||||
// check version
|
||||
// if read version is greater than we expected,
|
||||
// it means that this cfg file is created by the program higer than this.
|
||||
// we should not read anything from it.
|
||||
// however, for compaitibility reason, we allow read old cfg data.
|
||||
if (version_info > m_VersionIdentifier)
|
||||
return true;
|
||||
if (version_info > m_VersionIdentifier) {
|
||||
ret = ConfigLoadResult::ForwardNew;
|
||||
return ret;
|
||||
} else if (version_info < m_VersionIdentifier) {
|
||||
EnumHelper::Add(ret, ConfigLoadResult::Migrated);
|
||||
}
|
||||
|
||||
// fetch setting item from file
|
||||
yycc_u8string name_cache;
|
||||
@ -77,37 +88,50 @@ namespace YYCC::ConfigManager {
|
||||
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;
|
||||
else {
|
||||
EnumHelper::Add(ret, ConfigLoadResult::BrokenFile);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
// fetch name body
|
||||
name_cache.resize(name_length);
|
||||
if (std::fread(name_cache.data(), 1u, name_length, fs.get()) != name_length)
|
||||
return false;
|
||||
if (std::fread(name_cache.data(), 1u, name_length, fs.get()) != name_length) {
|
||||
EnumHelper::Add(ret, ConfigLoadResult::BrokenFile);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// get setting data length
|
||||
size_t data_length;
|
||||
if (std::fread(&data_length, 1u, sizeof(data_length), fs.get()) != sizeof(data_length))
|
||||
return false;
|
||||
if (std::fread(&data_length, 1u, sizeof(data_length), fs.get()) != sizeof(data_length)) {
|
||||
EnumHelper::Add(ret, ConfigLoadResult::BrokenFile);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// get matched setting first
|
||||
const auto& found = m_Settings.find(name_cache);
|
||||
if (found != m_Settings.end()) {
|
||||
// found. read data for it
|
||||
found->second->ResizeData(data_length);
|
||||
if (std::fread(found->second->GetDataPtr(), 1u, data_length, fs.get()) != data_length)
|
||||
return false;
|
||||
if (std::fread(found->second->GetDataPtr(), 1u, data_length, fs.get()) != data_length) {
|
||||
EnumHelper::Add(ret, ConfigLoadResult::BrokenFile);
|
||||
return ret;
|
||||
}
|
||||
// call user defined load function
|
||||
// if fail to parse, reset to default value
|
||||
if (!found->second->UserLoad())
|
||||
if (!found->second->UserLoad()) {
|
||||
EnumHelper::Add(ret, ConfigLoadResult::ItemError);
|
||||
found->second->UserReset();
|
||||
}
|
||||
} else {
|
||||
// fail to find. skip this unknown setting
|
||||
if (fseek(fs.get(), static_cast<long>(data_length), SEEK_CUR) != 0)
|
||||
return false;
|
||||
if (fseek(fs.get(), static_cast<long>(data_length), SEEK_CUR) != 0) {
|
||||
EnumHelper::Add(ret, ConfigLoadResult::BrokenFile);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool CoreManager::Save() {
|
@ -17,6 +17,19 @@
|
||||
* @details For how to use this namespace, please see \ref config_manager.
|
||||
*/
|
||||
namespace YYCC::ConfigManager {
|
||||
|
||||
/**
|
||||
* @brief The load result of loading config.
|
||||
*/
|
||||
enum class ConfigLoadResult {
|
||||
OK = 0, ///< Success load configs.
|
||||
Created = 1 << 0, ///< Given file is not existing, we create all configs in default values.
|
||||
ForwardNew = 1 << 1, ///< Detect the config file created by higher version. We create all configs in default values.
|
||||
Migrated = 1 << 2, ///< Detect the config file created by lower version. We try migrate configs written in it.
|
||||
BrokenFile = 1 << 3, ///< Given file has bad format. Thus some configs are kept as its default values.
|
||||
ItemError = 1 << 4 ///< Some config can not be recognized from the data read from file so they are reset to default value.
|
||||
};
|
||||
using UnderlyingConfigLoadResult_t = std::underlying_type_t<ConfigLoadResult>;
|
||||
|
||||
/// @brief The base class of every setting.
|
||||
/// @details Programmer can inherit this class and implement essential functions to create custom setting.
|
||||
@ -75,7 +88,7 @@ namespace YYCC::ConfigManager {
|
||||
private:
|
||||
std::vector<uint8_t> m_RawData;
|
||||
};
|
||||
|
||||
|
||||
/// @brief Settings manager and config file reader writer.
|
||||
class CoreManager {
|
||||
public:
|
||||
@ -96,8 +109,8 @@ namespace YYCC::ConfigManager {
|
||||
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();
|
||||
/// @return What happend when loading config. This function always success.
|
||||
ConfigLoadResult Load();
|
||||
/// @brief Save settings to file.
|
||||
/// @return True if success, otherwise false.
|
||||
bool Save();
|
182
src/YYCC/EnumHelper.hpp
Normal file
182
src/YYCC/EnumHelper.hpp
Normal file
@ -0,0 +1,182 @@
|
||||
#pragma once
|
||||
#include "YYCCInternal.hpp"
|
||||
|
||||
#include <initializer_list>
|
||||
#include <type_traits>
|
||||
|
||||
/**
|
||||
* @brief The namespace for convenient C++ enum class logic operations.
|
||||
* @details
|
||||
* C++ enum class statement is a modern way to declare enum in C++.
|
||||
* But it lack essential logic operations which is commonly used by programmer.
|
||||
* So we create this helper to resolve this issue.
|
||||
*/
|
||||
namespace YYCC::EnumHelper {
|
||||
|
||||
//// Reference:
|
||||
//// Enum operator overload: https://stackoverflow.com/a/71107019
|
||||
//// Constexpr operator overload: https://stackoverflow.com/a/17746099
|
||||
|
||||
//template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
//inline constexpr TEnum operator|(TEnum lhs, TEnum rhs) {
|
||||
// using ut = std::underlying_type_t<TEnum>;
|
||||
// return static_cast<TEnum>(static_cast<ut>(lhs) | static_cast<ut>(rhs));
|
||||
//}
|
||||
//template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
//inline constexpr TEnum operator|=(TEnum& lhs, TEnum rhs) {
|
||||
// using ut = std::underlying_type_t<TEnum>;
|
||||
// lhs = lhs | rhs;
|
||||
// return lhs;
|
||||
//}
|
||||
//
|
||||
//template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
//inline constexpr TEnum operator&(TEnum lhs, TEnum rhs) {
|
||||
// using ut = std::underlying_type_t<TEnum>;
|
||||
// return static_cast<TEnum>(static_cast<ut>(lhs) & static_cast<ut>(rhs));
|
||||
//}
|
||||
//template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
//inline constexpr TEnum operator&=(TEnum& lhs, TEnum rhs) {
|
||||
// using ut = std::underlying_type_t<TEnum>;
|
||||
// lhs = lhs & rhs;
|
||||
// return lhs;
|
||||
//}
|
||||
//
|
||||
//template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
//inline constexpr TEnum operator^(TEnum lhs, TEnum rhs) {
|
||||
// using ut = std::underlying_type_t<TEnum>;
|
||||
// return static_cast<TEnum>(static_cast<ut>(lhs) ^ static_cast<ut>(rhs));
|
||||
//}
|
||||
//template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
//inline constexpr TEnum operator^=(TEnum& lhs, TEnum rhs) {
|
||||
// using ut = std::underlying_type_t<TEnum>;
|
||||
// lhs = lhs ^ rhs;
|
||||
// return lhs;
|
||||
//}
|
||||
|
||||
//template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
//inline constexpr TEnum operator~(TEnum lhs) {
|
||||
// using ut = std::underlying_type_t<TEnum>;
|
||||
// return static_cast<TEnum>(~(static_cast<ut>(lhs)));
|
||||
//}
|
||||
//
|
||||
//template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
//inline constexpr bool operator bool(TEnum lhs) {
|
||||
// using ut = std::underlying_type_t<TEnum>;
|
||||
// return static_cast<bool>(static_cast<ut>(lhs));
|
||||
//}
|
||||
|
||||
/**
|
||||
* @brief The helper struct to check all given template argument are the same enum type.
|
||||
* @tparam TEnum The template parameter to be checked (first one).
|
||||
* @tparam Ts The template parameter to be checked.
|
||||
*/
|
||||
template<typename TEnum, typename... Ts>
|
||||
struct all_enum_values {
|
||||
public:
|
||||
// Please note it is std::is_same, not std::is_same_v!
|
||||
// That's std::conjunction_v required.
|
||||
static constexpr bool value = std::is_enum_v<std::remove_cv_t<TEnum>> && std::conjunction_v<std::is_same<std::remove_cv_t<TEnum>, std::remove_cv_t<Ts>>...>;
|
||||
};
|
||||
/**
|
||||
* @brief The convenient calling to all_enum_values::value to check enum template parameter.
|
||||
* @tparam TEnum The template parameter to be checked (first one).
|
||||
* @tparam Ts The template parameter to be checked.
|
||||
*/
|
||||
template<typename TEnum, typename... Ts>
|
||||
inline constexpr bool all_enum_values_v = all_enum_values<TEnum, Ts...>::value;
|
||||
|
||||
/**
|
||||
* @brief Merge given enum flags like performing <TT>e1 | e2 | ... | en</TT>
|
||||
* @tparam TEnum Enum type for processing.
|
||||
* @param[in] val The first enum flag to be merged.
|
||||
* @param[in] val_left Left enum flags to be merged.
|
||||
* @return The merged enum flag.
|
||||
* @remarks
|
||||
* This function use recursive expansion to get final merge result.
|
||||
* So there is no difference of each arguments.
|
||||
* We independ first argument just served for expansion.
|
||||
*/
|
||||
template<typename TEnum, typename... Ts, std::enable_if_t<all_enum_values_v<TEnum, Ts...>, int> = 0>
|
||||
constexpr TEnum Merge(TEnum val, Ts... val_left) {
|
||||
using ut = std::underlying_type_t<TEnum>;
|
||||
ut result = static_cast<ut>(val);
|
||||
if constexpr (sizeof...(val_left) > 0) {
|
||||
result |= static_cast<ut>(Merge(val_left...));
|
||||
}
|
||||
return static_cast<TEnum>(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reverse given enum flags like performing <TT>~(e)</TT>
|
||||
* @tparam TEnum Enum type for processing.
|
||||
* @param[in] e The list of enum flags to be inversed.
|
||||
* @return The inversed enum flag.
|
||||
*/
|
||||
template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
constexpr TEnum Invert(TEnum e) {
|
||||
using ut = std::underlying_type_t<TEnum>;
|
||||
return static_cast<TEnum>(~(static_cast<ut>(e)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Use specified enum flag to mask given enum flag like performing <TT>e1 &= e2</TT>
|
||||
* @tparam TEnum Enum type for processing.
|
||||
* @param[in,out] e1 The enum flags to be masked.
|
||||
* @param[in] e2 The mask enum flag.
|
||||
*/
|
||||
template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
constexpr void Mask(TEnum& e1, TEnum e2) {
|
||||
using ut = std::underlying_type_t<TEnum>;
|
||||
e1 = static_cast<TEnum>(static_cast<ut>(e1) & static_cast<ut>(e2));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add multiple enum flags to given enum flag like performing <TT>e1 |= (e2 | e3 | ... | en)</TT>
|
||||
* @tparam TEnum Enum type for processing.
|
||||
* @param[in,out] e1 The enum flag which flags add on.
|
||||
* @param[in] vals The enum flag to be added.
|
||||
*/
|
||||
template<typename TEnum, typename... Ts, std::enable_if_t<all_enum_values_v<TEnum, Ts...>, int> = 0>
|
||||
constexpr void Add(TEnum& e1, Ts... vals) {
|
||||
using ut = std::underlying_type_t<TEnum>;
|
||||
e1 = static_cast<TEnum>(static_cast<ut>(e1) | static_cast<ut>(Merge(vals...)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Remove multiple enum flags from given enum flag like performing <TT>e1 &= ~(e2 | e3 | ... | en)</TT>
|
||||
* @tparam TEnum Enum type for processing.
|
||||
* @param[in,out] e1 The enum flag which flags removed from.
|
||||
* @param[in] vals The enum flag to be removed.
|
||||
*/
|
||||
template<typename TEnum, typename... Ts, std::enable_if_t<all_enum_values_v<TEnum>, int> = 0>
|
||||
constexpr void Remove(TEnum& e1, Ts... vals) {
|
||||
using ut = std::underlying_type_t<TEnum>;
|
||||
e1 = static_cast<TEnum>(static_cast<ut>(e1) & static_cast<ut>(Invert(Merge(vals...))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check whether given enum flag has any of specified multiple enum flags (OR) like performing <TT>bool(e1 & (e2 | e3 | ... | en))</TT>
|
||||
* @tparam TEnum Enum type for processing.
|
||||
* @param[in] e1 The enum flag where we check.
|
||||
* @param[in] vals The enum flags for checking.
|
||||
* @return True if it has any of given flags (OR), otherwise false.
|
||||
*/
|
||||
template<typename TEnum, typename... Ts, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
constexpr bool Has(TEnum e1, Ts... vals) {
|
||||
using ut = std::underlying_type_t<TEnum>;
|
||||
return static_cast<bool>(static_cast<ut>(e1) & static_cast<ut>(Merge(vals...)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Cast given enum flags to its equvalent boolean value like performing <TT>bool(e)</TT>
|
||||
* @tparam TEnum Enum type for processing.
|
||||
* @param e The enum flags to be cast.
|
||||
* @return The equvalent bool value of given enum flag.
|
||||
*/
|
||||
template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
|
||||
constexpr bool Bool(TEnum e) {
|
||||
using ut = std::underlying_type_t<TEnum>;
|
||||
return static_cast<bool>(static_cast<ut>(e));
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#pragma region Library Version and Comparison Macros
|
||||
|
||||
#include "YYCCVersion.hpp"
|
||||
|
||||
/// @brief Return true if left version number is equal to right version number, otherwise false.
|
||||
#define YYCC_VERCMP_E(av1, av2, av3, bv1, bv2, bv3) ((av1) == (bv1) && (av2) == (bv2) && (av3) == (bv3))
|
||||
/// @brief Return true if left version number is not equal to right version number, otherwise false.
|
||||
#define YYCC_VERCMP_NE(av1, av2, av3, bv1, bv2, bv3) (!YYCC_VERCMP_E(av1, av2, av3, bv1, bv2, bv3))
|
||||
/// @brief Return true if left version number is greater than right version number, otherwise false.
|
||||
#define YYCC_VERCMP_G(av1, av2, av3, bv1, bv2, bv3) ( \
|
||||
((av1) > (bv1)) || \
|
||||
((av1) == (bv1) && (av2) > (bv2)) || \
|
||||
((av1) == (bv1) && (av2) == (bv2) && (av3) > (bv3)) \
|
||||
)
|
||||
/// @brief Return true if left version number is greater than or equal to right version number, otherwise false.
|
||||
#define YYCC_VERCMP_GE(av1, av2, av3, bv1, bv2, bv3) (YYCC_VERCMP_G(av1, av2, av3, bv1, bv2, bv3) || YYCC_VERCMP_E(av1, av2, av3, bv1, bv2, bv3))
|
||||
/// @brief Return true if left version number is not lower than right version number, otherwise false.
|
||||
#define YYCC_VERCMP_NL(av1, av2, av3, bv1, bv2, bv3) YYCC_VERCMP_GE(av1, av2, av3, bv1, bv2, bv3)
|
||||
/// @brief Return true if left version number is lower than right version number, otherwise false.
|
||||
#define YYCC_VERCMP_L(av1, av2, av3, bv1, bv2, bv3) ( \
|
||||
((av1) < (bv1)) || \
|
||||
((av1) == (bv1) && (av2) < (bv2)) || \
|
||||
((av1) == (bv1) && (av2) == (bv2) && (av3) < (bv3)) \
|
||||
)
|
||||
/// @brief Return true if left version number is lower than or equal to right version number, otherwise false.
|
||||
#define YYCC_VERCMP_LE(av1, av2, av3, bv1, bv2, bv3) (YYCC_VERCMP_L(av1, av2, av3, bv1, bv2, bv3) || YYCC_VERCMP_E(av1, av2, av3, bv1, bv2, bv3))
|
||||
/// @brief Return true if left version number is not greater than right version number, otherwise false.
|
||||
#define YYCC_VERCMP_NG(av1, av2, av3, bv1, bv2, bv3) YYCC_VERCMP_LE(av1, av2, av3, bv1, bv2, bv3)
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Operating System Identifier Macros
|
||||
|
||||
// Define operating system macros
|
@ -1,17 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "YYCCInternal.hpp"
|
||||
#include "YYCC/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 "YYCC/EncodingHelper.hpp"
|
||||
#include "YYCC/StringHelper.hpp"
|
||||
#include "YYCC/ConsoleHelper.hpp"
|
||||
#include "YYCC/COMHelper.hpp"
|
||||
#include "YYCC/DialogHelper.hpp"
|
||||
#include "YYCC/ParserHelper.hpp"
|
||||
#include "YYCC/IOHelper.hpp"
|
||||
#include "YYCC/WinFctHelper.hpp"
|
||||
#include "YYCC/StdPatch.hpp"
|
||||
#include "YYCC/EnumHelper.hpp"
|
||||
#include "YYCC/ExceptionHelper.hpp"
|
||||
|
||||
#include "ConfigManager.hpp"
|
||||
#include "ArgParser.hpp"
|
||||
#include "YYCC/ConfigManager.hpp"
|
||||
#include "YYCC/ArgParser.hpp"
|
||||
|
@ -31,6 +31,6 @@ PRIVATE
|
||||
|
||||
# Install testbench only on Release mode
|
||||
install(TARGETS YYCCTestbench
|
||||
CONFIGURATIONS Release
|
||||
CONFIGURATIONS Release RelWithDebInfo MinSizeRel
|
||||
RUNTIME DESTINATION ${YYCC_INSTALL_BIN_PATH}
|
||||
)
|
||||
|
@ -242,7 +242,7 @@ namespace YYCCTestbench {
|
||||
#define TEST_MACRO(type_t, value, string_value, ...) { \
|
||||
YYCC::yycc_u8string cache_string(YYCC_U8(string_value)); \
|
||||
type_t cache; \
|
||||
Assert(YYCC::ParserHelper::TryParse<type_t>(cache_string, cache, __VA_ARGS__) && cache == value, YYCC_U8("YYCC::StringHelper::TryParse<" #type_t ">")); \
|
||||
Assert(YYCC::ParserHelper::TryParse<type_t>(cache_string, cache, ##__VA_ARGS__) && cache == value, YYCC_U8("YYCC::StringHelper::TryParse<" #type_t ">")); \
|
||||
}
|
||||
|
||||
TEST_MACRO(int8_t, INT8_C(-61), "-61");
|
||||
@ -264,7 +264,7 @@ namespace YYCCTestbench {
|
||||
#define TEST_MACRO(type_t, string_value, ...) { \
|
||||
YYCC::yycc_u8string cache_string(YYCC_U8(string_value)); \
|
||||
type_t cache; \
|
||||
Assert(!YYCC::ParserHelper::TryParse<type_t>(cache_string, cache, __VA_ARGS__), YYCC_U8("YYCC::StringHelper::TryParse<" #type_t ">")); \
|
||||
Assert(!YYCC::ParserHelper::TryParse<type_t>(cache_string, cache, ##__VA_ARGS__), YYCC_U8("YYCC::StringHelper::TryParse<" #type_t ">")); \
|
||||
}
|
||||
|
||||
TEST_MACRO(int8_t, "6161");
|
||||
@ -284,7 +284,7 @@ namespace YYCCTestbench {
|
||||
// Test ToString
|
||||
#define TEST_MACRO(type_t, value, string_value, ...) { \
|
||||
type_t cache = value; \
|
||||
YYCC::yycc_u8string ret(YYCC::ParserHelper::ToString<type_t>(cache, __VA_ARGS__)); \
|
||||
YYCC::yycc_u8string ret(YYCC::ParserHelper::ToString<type_t>(cache, ##__VA_ARGS__)); \
|
||||
Assert(ret == YYCC_U8(string_value), YYCC_U8("YYCC::StringHelper::ToString<" #type_t ">")); \
|
||||
}
|
||||
|
||||
@ -402,7 +402,7 @@ namespace YYCCTestbench {
|
||||
#endif
|
||||
}
|
||||
|
||||
static void StdPatch() {
|
||||
static void StdPatchTestbench() {
|
||||
|
||||
// Std Path
|
||||
|
||||
@ -439,6 +439,62 @@ namespace YYCCTestbench {
|
||||
|
||||
}
|
||||
|
||||
enum class TestFlagEnum : uint8_t {
|
||||
Test1 = 0b00000000,
|
||||
Test2 = 0b00000001,
|
||||
Test3 = 0b00000010,
|
||||
Test4 = 0b00000100,
|
||||
Test5 = 0b00001000,
|
||||
Test6 = 0b00010000,
|
||||
Test7 = 0b00100000,
|
||||
Test8 = 0b01000000,
|
||||
Test9 = 0b10000000,
|
||||
Inverted = 0b01111111,
|
||||
Merged = Test3 + Test5,
|
||||
};
|
||||
|
||||
static void EnumHelperTestbench() {
|
||||
TestFlagEnum val;
|
||||
|
||||
Assert(YYCC::EnumHelper::Merge(TestFlagEnum::Test3, TestFlagEnum::Test5) == TestFlagEnum::Merged, YYCC_U8("YYCC::EnumHelper::Merge"));
|
||||
|
||||
Assert(YYCC::EnumHelper::Invert(TestFlagEnum::Test9) == TestFlagEnum::Inverted, YYCC_U8("YYCC::EnumHelper::Invert"));
|
||||
|
||||
val = YYCC::EnumHelper::Merge(TestFlagEnum::Test3, TestFlagEnum::Test5);
|
||||
YYCC::EnumHelper::Mask(val, TestFlagEnum::Test3);
|
||||
Assert(YYCC::EnumHelper::Bool(val), YYCC_U8("YYCC::EnumHelper::Mask"));
|
||||
val = YYCC::EnumHelper::Merge(TestFlagEnum::Test3, TestFlagEnum::Test5);
|
||||
YYCC::EnumHelper::Mask(val, TestFlagEnum::Test4);
|
||||
Assert(!YYCC::EnumHelper::Bool(val), YYCC_U8("YYCC::EnumHelper::Mask"));
|
||||
|
||||
val = TestFlagEnum::Test3;
|
||||
YYCC::EnumHelper::Add(val, TestFlagEnum::Test5);
|
||||
Assert(val == TestFlagEnum::Merged, YYCC_U8("YYCC::EnumHelper::Add"));
|
||||
|
||||
val = TestFlagEnum::Merged;
|
||||
YYCC::EnumHelper::Remove(val, TestFlagEnum::Test5);
|
||||
Assert(val == TestFlagEnum::Test3, YYCC_U8("YYCC::EnumHelper::Remove"));
|
||||
|
||||
val = YYCC::EnumHelper::Merge(TestFlagEnum::Test3, TestFlagEnum::Test5);
|
||||
Assert(YYCC::EnumHelper::Has(val, TestFlagEnum::Test3), YYCC_U8("YYCC::EnumHelper::Has"));
|
||||
Assert(!YYCC::EnumHelper::Has(val, TestFlagEnum::Test4), YYCC_U8("YYCC::EnumHelper::Has"));
|
||||
|
||||
Assert(!YYCC::EnumHelper::Bool(TestFlagEnum::Test1), YYCC_U8("YYCC::EnumHelper::Bool"));
|
||||
Assert(YYCC::EnumHelper::Bool(TestFlagEnum::Test2), YYCC_U8("YYCC::EnumHelper::Bool"));
|
||||
|
||||
}
|
||||
|
||||
static void VersionMacroTestbench() {
|
||||
Assert(YYCC_VERCMP_E(1, 2, 3, 1, 2, 3), YYCC_U8("YYCC_VERCMP_E"));
|
||||
Assert(!YYCC_VERCMP_NE(1, 2, 3, 1, 2, 3), YYCC_U8("YYCC_VERCMP_NE"));
|
||||
Assert(YYCC_VERCMP_G(1, 2, 3, 0, 2, 5), YYCC_U8("YYCC_VERCMP_G"));
|
||||
Assert(YYCC_VERCMP_GE(1, 2, 3, 1, 2, 3), YYCC_U8("YYCC_VERCMP_GE"));
|
||||
Assert(YYCC_VERCMP_NL(1, 2, 3, 1, 2, 3), YYCC_U8("YYCC_VERCMP_NL"));
|
||||
Assert(YYCC_VERCMP_L(0, 2, 5, 1, 2, 3), YYCC_U8("YYCC_VERCMP_L"));
|
||||
Assert(YYCC_VERCMP_LE(1, 2, 3, 1, 2, 3), YYCC_U8("YYCC_VERCMP_LE"));
|
||||
Assert(YYCC_VERCMP_NG(1, 2, 3, 1, 2, 3), YYCC_U8("YYCC_VERCMP_NG"));
|
||||
}
|
||||
|
||||
enum class TestEnum : int8_t {
|
||||
Test1, Test2, Test3
|
||||
};
|
||||
@ -518,7 +574,11 @@ namespace YYCCTestbench {
|
||||
Assert(test.m_EnumSetting.Get() == TestEnum::Test1, YYCC_U8("YYCC::ConfigManager::CoreManager::Reset"));
|
||||
|
||||
// test load
|
||||
Assert(test.m_CoreManager.Load(), YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
|
||||
YYCC::ConfigManager::ConfigLoadResult wrong_result = YYCC::EnumHelper::Merge(
|
||||
YYCC::ConfigManager::ConfigLoadResult::ItemError,
|
||||
YYCC::ConfigManager::ConfigLoadResult::BrokenFile
|
||||
);
|
||||
Assert(!YYCC::EnumHelper::Has(test.m_CoreManager.Load(), wrong_result), YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
|
||||
test.PrintSettings();
|
||||
Assert(test.m_IntSetting.Get() == INT32_C(114), YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
|
||||
Assert(test.m_FloatSetting.Get() == 2.0f, YYCC_U8("YYCC::ConfigManager::CoreManager::Load"));
|
||||
@ -649,13 +709,21 @@ auto al = YYCC::ArgParser::ArgumentList::CreateFromStd(sizeof(test_argv) / sizeo
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
|
||||
#if YYCC_VERCMP_NE(YYCC_VER_MAJOR, YYCC_VER_MINOR, YYCC_VER_PATCH, 1, 3 ,0)
|
||||
#error "The YYCC library used when compiling is not match code expected, this may cause build error."
|
||||
#error "If you trust it, please annotate these preprocessor statement, otherwise please contact developer."
|
||||
#endif
|
||||
|
||||
// common testbench
|
||||
// normal
|
||||
YYCCTestbench::EncodingTestbench();
|
||||
YYCCTestbench::StringTestbench();
|
||||
YYCCTestbench::ParserTestbench();
|
||||
YYCCTestbench::WinFctTestbench();
|
||||
YYCCTestbench::StdPatch();
|
||||
YYCCTestbench::StdPatchTestbench();
|
||||
YYCCTestbench::EnumHelperTestbench();
|
||||
YYCCTestbench::VersionMacroTestbench();
|
||||
// advanced
|
||||
YYCCTestbench::ConfigManagerTestbench();
|
||||
YYCCTestbench::ArgParserTestbench(argc, argv);
|
||||
|
Loading…
Reference in New Issue
Block a user