4 Commits

Author SHA1 Message Date
0b7e58c8e8 feat: use CMake to generate library version info.
- use CMake to produce YYCC version header when configuring.
2024-11-03 18:52:02 +08:00
831fa130bc feat: improve EnumHelper
- allow multiple enum flags in EnumHelper::Has to check whether given flags contains specified flags.
- update version in CMake script.
2024-11-03 18:06:36 +08:00
7adac00035 doc: fix document about recent changes
- fix build commandline introduction in documentation.
- update build script template.
- only install document in any Release-like build type.
- now testbench will be installed in any Release-like build type, not only Release.
2024-11-03 17:29:34 +08:00
0cd9582757 refactor: refactor project layout
- move header files into an individual directory to prevent the possibility that file name is conflict in Linux include directory.
- update build script generator. use jinja2 template engine to get better view, rather than python code written by hand.
- add version number and version comparation macros in core library.
2024-11-03 14:51:18 +08:00
45 changed files with 376 additions and 349 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
# -------------------- Output -------------------- # -------------------- Output --------------------
out/ out/
src/YYCC/YYCCVersion.hpp
CMakeSettings.json CMakeSettings.json
# -------------------- VSCode -------------------- # -------------------- VSCode --------------------

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.23) cmake_minimum_required(VERSION 3.23)
project(YYCC project(YYCC
VERSION 1.2.0 VERSION 1.3.0
LANGUAGES CXX LANGUAGES CXX
) )

5
cmake/YYCCVersion.hpp.in Normal file
View 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@

View File

@ -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}
) )

View File

@ -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. 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>.

View File

@ -6,6 +6,48 @@ namespace YYCC {
In this page we will introduce the macros defined by this library In this page we will introduce the macros defined by this library
which can not be grouped in other topic. 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 \section library_macros__platform_checker Platform Checker
In many cross platform applications, In many cross platform applications,

1
script/.gitignore vendored
View File

@ -1,2 +1,3 @@
# -------------------- Output -------------------- # -------------------- Output --------------------
win_build.bat win_build.bat
linux_build.sh

108
script/gen_build_script.py Normal file
View 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()

View File

@ -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)

View File

@ -1,9 +1,6 @@
#!/bin/bash #!/bin/bash
README_PATH=$(pwd)/README.md # Navigate to project root directory
if [ ! -f "$README_PATH" ]; then cd {{ repo_root_dir }}
echo "Error: You must run this script at the root folder of this project!"
exit
fi
# Create main binary directory # Create main binary directory
mkdir bin mkdir bin
@ -19,10 +16,10 @@ cd ..
# Build current system debug and release version # Build current system debug and release version
cd build 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 --build .
cmake --install . --prefix ../install/Debug 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 --build .
cmake --install . --prefix ../install/Release cmake --install . --prefix ../install/Release
cd .. cd ..

View File

@ -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

View 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

View File

@ -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

View File

@ -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 # 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/COMHelper.cpp
ArgParser.cpp YYCC/ArgParser.cpp
ConfigManager.cpp YYCC/ConfigManager.cpp
ConsoleHelper.cpp YYCC/ConsoleHelper.cpp
DialogHelper.cpp YYCC/DialogHelper.cpp
EncodingHelper.cpp YYCC/EncodingHelper.cpp
ExceptionHelper.cpp YYCC/ExceptionHelper.cpp
StdPatch.cpp YYCC/StdPatch.cpp
IOHelper.cpp YYCC/IOHelper.cpp
StringHelper.cpp YYCC/StringHelper.cpp
WinFctHelper.cpp YYCC/WinFctHelper.cpp
# Natvis (only for MSVC) # Natvis (only for MSVC)
$<$<CXX_COMPILER_ID:MSVC>:YYCC.natvis> $<$<CXX_COMPILER_ID:MSVC>:YYCC.natvis>
) )
@ -24,25 +31,27 @@ FILE_SET HEADERS
FILES FILES
# Headers # Headers
# Common headers # Common headers
Constraints.hpp YYCC/Constraints.hpp
COMHelper.hpp YYCC/COMHelper.hpp
ArgParser.hpp YYCC/ArgParser.hpp
ConfigManager.hpp YYCC/ConfigManager.hpp
ConsoleHelper.hpp YYCC/ConsoleHelper.hpp
DialogHelper.hpp YYCC/DialogHelper.hpp
EncodingHelper.hpp YYCC/EncodingHelper.hpp
EnumHelper.hpp YYCC/EnumHelper.hpp
ExceptionHelper.hpp YYCC/ExceptionHelper.hpp
StdPatch.hpp YYCC/StdPatch.hpp
IOHelper.hpp YYCC/IOHelper.hpp
ParserHelper.hpp YYCC/ParserHelper.hpp
StringHelper.hpp YYCC/StringHelper.hpp
WinFctHelper.hpp YYCC/WinFctHelper.hpp
# Windows including guard pair # Windows including guard pair
WinImportPrefix.hpp YYCC/WinImportPrefix.hpp
WinImportSuffix.hpp YYCC/WinImportSuffix.hpp
# Misc # Internal
YYCCInternal.hpp YYCC/YYCCVersion.hpp
YYCC/YYCCInternal.hpp
# Exposed
YYCCommonplace.hpp YYCCommonplace.hpp
) )
# Setup header infomations # Setup header infomations

View File

@ -88,8 +88,13 @@ namespace YYCC::EnumHelper {
/** /**
* @brief Merge given enum flags like performing <TT>e1 | e2 | ... | en</TT> * @brief Merge given enum flags like performing <TT>e1 | e2 | ... | en</TT>
* @tparam TEnum Enum type for processing. * @tparam TEnum Enum type for processing.
* @param[in] il The list of enum flags to be merged. * @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. * @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> 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) { constexpr TEnum Merge(TEnum val, Ts... val_left) {
@ -114,7 +119,7 @@ namespace YYCC::EnumHelper {
} }
/** /**
* @brief Use specified enum flags to mask given enum flags like performing <TT>e1 &= e2</TT> * @brief Use specified enum flag to mask given enum flag like performing <TT>e1 &= e2</TT>
* @tparam TEnum Enum type for processing. * @tparam TEnum Enum type for processing.
* @param[in,out] e1 The enum flags to be masked. * @param[in,out] e1 The enum flags to be masked.
* @param[in] e2 The mask enum flag. * @param[in] e2 The mask enum flag.
@ -126,10 +131,10 @@ namespace YYCC::EnumHelper {
} }
/** /**
* @brief Add specified enum flags to given enum flags like performing <TT>e1 = e1 | e2 | ... | en</TT> * @brief Add multiple enum flags to given enum flag like performing <TT>e1 |= (e2 | e3 | ... | en)</TT>
* @tparam TEnum Enum type for processing. * @tparam TEnum Enum type for processing.
* @param[in,out] e1 The enum flags to be processed. * @param[in,out] e1 The enum flag which flags add on.
* @param[in] e2 The enum flag to be added. * @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> template<typename TEnum, typename... Ts, std::enable_if_t<all_enum_values_v<TEnum, Ts...>, int> = 0>
constexpr void Add(TEnum& e1, Ts... vals) { constexpr void Add(TEnum& e1, Ts... vals) {
@ -138,10 +143,10 @@ namespace YYCC::EnumHelper {
} }
/** /**
* @brief Remove specified enum flags from given enum flags like performing <TT>e1 &= ~(e2 | e3 | ... | en)</TT> * @brief Remove multiple enum flags from given enum flag like performing <TT>e1 &= ~(e2 | e3 | ... | en)</TT>
* @tparam TEnum Enum type for processing. * @tparam TEnum Enum type for processing.
* @param[in,out] e1 The enum flags to be processed. * @param[in,out] e1 The enum flag which flags removed from.
* @param[in] e2 The enum flag to be removed. * @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> template<typename TEnum, typename... Ts, std::enable_if_t<all_enum_values_v<TEnum>, int> = 0>
constexpr void Remove(TEnum& e1, Ts... vals) { constexpr void Remove(TEnum& e1, Ts... vals) {
@ -150,23 +155,23 @@ namespace YYCC::EnumHelper {
} }
/** /**
* @brief Check whether given enum flags has specified enum flag like performing <TT>bool(e & probe)</TT> * @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. * @tparam TEnum Enum type for processing.
* @param[in] e1 The enum flags to be checked. * @param[in] e1 The enum flag where we check.
* @param[in] e2 The enum flag for checking. * @param[in] vals The enum flags for checking.
* @return True if it has, otherwise false. * @return True if it has any of given flags (OR), otherwise false.
*/ */
template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0> template<typename TEnum, typename... Ts, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
constexpr bool Has(TEnum e1, TEnum e2) { constexpr bool Has(TEnum e1, Ts... vals) {
using ut = std::underlying_type_t<TEnum>; using ut = std::underlying_type_t<TEnum>;
return static_cast<bool>(static_cast<ut>(e1) & static_cast<ut>(e2)); 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> * @brief Cast given enum flags to its equvalent boolean value like performing <TT>bool(e)</TT>
* @tparam TEnum Enum type for processing. * @tparam TEnum Enum type for processing.
* @param e The enum flags to be cast. * @param e The enum flags to be cast.
* @return The cast enum flag. * @return The equvalent bool value of given enum flag.
*/ */
template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0> template<typename TEnum, std::enable_if_t<std::is_enum_v<TEnum>, int> = 0>
constexpr bool Bool(TEnum e) { constexpr bool Bool(TEnum e) {

View File

@ -1,5 +1,36 @@
#pragma once #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 #pragma region Operating System Identifier Macros
// Define operating system macros // Define operating system macros

View File

@ -1,18 +1,18 @@
#pragma once #pragma once
#include "YYCCInternal.hpp" #include "YYCC/YYCCInternal.hpp"
#include "EncodingHelper.hpp" #include "YYCC/EncodingHelper.hpp"
#include "StringHelper.hpp" #include "YYCC/StringHelper.hpp"
#include "ConsoleHelper.hpp" #include "YYCC/ConsoleHelper.hpp"
#include "COMHelper.hpp" #include "YYCC/COMHelper.hpp"
#include "DialogHelper.hpp" #include "YYCC/DialogHelper.hpp"
#include "ParserHelper.hpp" #include "YYCC/ParserHelper.hpp"
#include "IOHelper.hpp" #include "YYCC/IOHelper.hpp"
#include "WinFctHelper.hpp" #include "YYCC/WinFctHelper.hpp"
#include "StdPatch.hpp" #include "YYCC/StdPatch.hpp"
#include "EnumHelper.hpp" #include "YYCC/EnumHelper.hpp"
#include "ExceptionHelper.hpp" #include "YYCC/ExceptionHelper.hpp"
#include "ConfigManager.hpp" #include "YYCC/ConfigManager.hpp"
#include "ArgParser.hpp" #include "YYCC/ArgParser.hpp"

View File

@ -31,6 +31,6 @@ PRIVATE
# Install testbench only on Release mode # Install testbench only on Release mode
install(TARGETS YYCCTestbench install(TARGETS YYCCTestbench
CONFIGURATIONS Release CONFIGURATIONS Release RelWithDebInfo MinSizeRel
RUNTIME DESTINATION ${YYCC_INSTALL_BIN_PATH} RUNTIME DESTINATION ${YYCC_INSTALL_BIN_PATH}
) )

View File

@ -484,6 +484,17 @@ namespace YYCCTestbench {
} }
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 { enum class TestEnum : int8_t {
Test1, Test2, Test3 Test1, Test2, Test3
}; };
@ -698,6 +709,12 @@ auto al = YYCC::ArgParser::ArgumentList::CreateFromStd(sizeof(test_argv) / sizeo
} }
int main(int argc, char* argv[]) { 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 // common testbench
// normal // normal
YYCCTestbench::EncodingTestbench(); YYCCTestbench::EncodingTestbench();
@ -706,6 +723,7 @@ int main(int argc, char* argv[]) {
YYCCTestbench::WinFctTestbench(); YYCCTestbench::WinFctTestbench();
YYCCTestbench::StdPatchTestbench(); YYCCTestbench::StdPatchTestbench();
YYCCTestbench::EnumHelperTestbench(); YYCCTestbench::EnumHelperTestbench();
YYCCTestbench::VersionMacroTestbench();
// advanced // advanced
YYCCTestbench::ConfigManagerTestbench(); YYCCTestbench::ConfigManagerTestbench();
YYCCTestbench::ArgParserTestbench(argc, argv); YYCCTestbench::ArgParserTestbench(argc, argv);