├── Build └── .gitkeep ├── .gitignore ├── CMakeLists.txt ├── .gitmodules ├── UnitTests ├── Sources │ ├── UnitTests_IdentifyCompiler.cpp │ ├── UnitTests_MFC.cpp │ ├── UnitTests_Algorithm.cpp │ ├── UnitTests_Common.cpp │ └── UnitTests_Extends.cpp ├── CMakeLists.txt └── Headers │ └── MFCAdapter.hpp ├── PerformanceTests ├── CMakeLists.txt └── Sources │ └── PerformanceTestsMain.cpp ├── .gitattributes ├── LICENSE ├── reset_submodules.bat ├── CMake └── CMakeCommon.cmake ├── Benchmark ├── CMakeLists.txt └── Sources │ └── BenchmarkMain.cpp ├── Format ├── StandardLibraryAdapter.hpp ├── Common │ ├── Noncopyable.hpp │ ├── Macros.hpp │ ├── Mpl.hpp │ ├── Algorithm.hpp │ ├── Build.hpp │ ├── Mutex.hpp │ ├── AutoString.hpp │ └── AutoArray.hpp ├── Format.hpp └── Details │ ├── StandardLibrary │ ├── StandardLibraryPolicy.hpp │ └── FormatTo.hpp │ ├── Pattern.hpp │ ├── InlineFiles │ └── FormatTo.inl │ ├── PatternStorage.hpp │ └── FormatTo.hpp ├── test_compiler_compatible.bat ├── generate_projects.bat └── README.md /Build/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/.vs/ 2 | *.user 3 | /Build/** 4 | /.idea/ -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22) 2 | 3 | include(${CMAKE_CURRENT_SOURCE_DIR}/CMake/CMakeCommon.cmake) 4 | 5 | project(CPPStringFormatting) 6 | 7 | add_subdirectory(UnitTests) 8 | add_subdirectory(Benchmark) 9 | add_subdirectory(PerformanceTests) 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "UnitTests/External/googletest"] 2 | path = UnitTests/External/googletest 3 | url = https://github.com/google/googletest.git 4 | [submodule "Benchmark/External/Celero"] 5 | path = Benchmark/External/Celero 6 | url = https://github.com/DigitalInBlue/Celero.git 7 | -------------------------------------------------------------------------------- /UnitTests/Sources/UnitTests_IdentifyCompiler.cpp: -------------------------------------------------------------------------------- 1 | // ReSharper disable CppClangTidyClangDiagnosticPragmaMessages 2 | #include 3 | 4 | #pragma message(FL_CXX_STANDARD) 5 | 6 | #if FL_PLATFORM_X64 7 | #pragma message("64bit platform...") 8 | #else 9 | #pragma message("32bit platform...") 10 | #endif 11 | -------------------------------------------------------------------------------- /PerformanceTests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(PerformanceTests) 3 | 4 | # Set C++ standard 5 | set(CMAKE_CXX_STANDARD 11) 6 | set(CMAKE_CXX_STANDARD_REQUIRED True) 7 | 8 | # Include parent directory 9 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../) 10 | 11 | # Recursively get source files 12 | file(GLOB_RECURSE TEST_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Sources/*.c*) 13 | SOURCE_GROUP_BY_DIR(TEST_SOURCE_FILES) 14 | 15 | # Define macros 16 | add_definitions(-DUNICODE -D_UNICODE) 17 | 18 | # Set output directories for executables and libraries 19 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_DIRECTORY}) 20 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${OUTPUT_DIRECTORY}) 21 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${OUTPUT_DIRECTORY}) 22 | 23 | # Add executable 24 | add_executable(PerformanceTests ${TEST_SOURCE_FILES}) 25 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.bin filter=lfs diff=lfs merge=lfs -text 2 | *.vsix filter=lfs diff=lfs merge=lfs -text 3 | *.exe filter=lfs diff=lfs merge=lfs -text 4 | *.dll filter=lfs diff=lfs merge=lfs -text 5 | *.msi filter=lfs diff=lfs merge=lfs -text 6 | *.zip filter=lfs diff=lfs merge=lfs -text 7 | *.7z filter=lfs diff=lfs merge=lfs -text 8 | *.rar filter=lfs diff=lfs merge=lfs -text 9 | *.pdb filter=lfs diff=lfs merge=lfs -text 10 | *.lib filter=lfs diff=lfs merge=lfs -text 11 | *.so filter=lfs diff=lfs merge=lfs -text 12 | *.pdf filter=lfs diff=lfs merge=lfs -text 13 | *.chm filter=lfs diff=lfs merge=lfs -text 14 | *.pyd filter=lfs diff=lfs merge=lfs -text 15 | *.dmg filter=lfs diff=lfs merge=lfs -text 16 | *.dylib filter=lfs diff=lfs merge=lfs -text 17 | *.a filter=lfs diff=lfs merge=lfs -text 18 | *.png filter=lfs diff=lfs merge=lfs -text 19 | *.xml filter=lfs diff=lfs merge=lfs -text 20 | *.tga filter=lfs diff=lfs merge=lfs -text 21 | *.psd filter=lfs diff=lfs merge=lfs -text 22 | 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 bodong 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /reset_submodules.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | 4 | REM 定义子模块路径和URL 5 | set SUBMODULE1_PATH=UnitTests/External/googletest 6 | set SUBMODULE1_URL=https://github.com/google/googletest.git 7 | 8 | set SUBMODULE2_PATH=Benchmark/External/Celero 9 | set SUBMODULE2_URL=https://github.com/DigitalInBlue/Celero.git 10 | 11 | REM 强制取消初始化子模块 12 | echo Deinitializing submodules... 13 | git submodule deinit -f -- %SUBMODULE1_PATH% 14 | git submodule deinit -f -- %SUBMODULE2_PATH% 15 | 16 | REM 删除子模块目录和 .git 中的子模块配置 17 | echo Removing submodule directories and configurations... 18 | rd /s /q %SUBMODULE1_PATH% 19 | rd /s /q %SUBMODULE2_PATH% 20 | git rm -f %SUBMODULE1_PATH% 21 | git rm -f %SUBMODULE2_PATH% 22 | rd /s /q .git\modules\%SUBMODULE1_PATH% 23 | rd /s /q .git\modules\%SUBMODULE2_PATH% 24 | 25 | REM 重新添加子模块 26 | echo Re-adding submodules... 27 | git submodule add --force %SUBMODULE1_URL% %SUBMODULE1_PATH% 28 | git submodule add --force %SUBMODULE2_URL% %SUBMODULE2_PATH% 29 | 30 | REM 强制更新子模块 31 | echo Updating submodules... 32 | git submodule update --init --recursive --force 33 | 34 | echo Submodules have been reset and updated. 35 | endlocal 36 | pause -------------------------------------------------------------------------------- /UnitTests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories( 2 | ${CMAKE_CURRENT_SOURCE_DIR}/../ 3 | ) 4 | 5 | file( GLOB_RECURSE ROOT_HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/../Format/*.h* ${CMAKE_CURRENT_SOURCE_DIR}/../Format/*.inl ) 6 | 7 | SOURCE_GROUP_BY_DIR(ROOT_HEADER_FILES) 8 | 9 | file( GLOB_RECURSE TEST_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Sources/*.c* ${CMAKE_CURRENT_SOURCE_DIR}/Headers/*.h* ) 10 | SOURCE_GROUP_BY_DIR(TEST_SOURCE_FILES) 11 | 12 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/Headers/) 13 | 14 | # Add subdirectories 15 | add_subdirectory(External/googletest) 16 | 17 | add_definitions(-DUNICODE -D_UNICODE) 18 | 19 | set(OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/$) 20 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_DIRECTORY}) 21 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${OUTPUT_DIRECTORY}) 22 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${OUTPUT_DIRECTORY}) 23 | 24 | add_executable(UnitTests ${ROOT_HEADER_FILES} ${TEST_SOURCE_FILES}) 25 | target_link_libraries(UnitTests gtest gtest_main) 26 | 27 | # Enable MFC if on Windows and using Visual Studio 28 | if(CMAKE_SYSTEM_NAME STREQUAL "Windows" AND CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") 29 | set(CMAKE_MFC_FLAG 2) 30 | endif() 31 | -------------------------------------------------------------------------------- /UnitTests/Sources/UnitTests_MFC.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #if FL_PLATFORM_WINDOWS 4 | #pragma warning(push) 5 | #pragma warning(disable:4616) 6 | #endif 7 | #include 8 | #if FL_PLATFORM_WINDOWS 9 | #pragma warning(pop) 10 | #endif 11 | 12 | #include "MFCAdapter.hpp" 13 | 14 | #if FL_PLATFORM_WINDOWS && FL_COMPILER_MSVC 15 | 16 | using namespace Formatting; 17 | 18 | TEST(MFCFormat, MFC_CString) 19 | { 20 | EXPECT_EQ(MFC::Format(_T("Hello CppMiniToolkit")), _T("Hello CppMiniToolkit")); 21 | EXPECT_EQ(MFC::Format(_T("Hello CppMiniToolkit {0}"), 1024), _T("Hello CppMiniToolkit 1024")); 22 | 23 | EXPECT_EQ(MFC::Format(_T("{0} {1}"), 123, _T("hello")), _T("123 hello")); 24 | EXPECT_EQ(MFC::Format(_T("{1} {0}"), 123, _T("hello")), _T("hello 123")); 25 | EXPECT_EQ(MFC::Format(_T("{0} {0}"), 123), _T("123 123")); 26 | EXPECT_EQ(MFC::Format(_T("{0}"), _T("hello")), _T("hello")); 27 | EXPECT_EQ(MFC::Format(_T("{0}"), _T('a')), _T("a")); 28 | EXPECT_EQ(MFC::Format(_T("{0}"), true), _T("True")); 29 | EXPECT_EQ(MFC::Format(_T("{0}"), false), _T("False")); 30 | EXPECT_EQ(MFC::Format(_T(""), 123), _T("")); 31 | EXPECT_EQ(MFC::Format(_T("hello")), _T("hello")); 32 | EXPECT_EQ(MFC::Format(_T("{0} {1} {2}"), 123, _T("hello"), 1.23), _T("123 hello 1.23")); 33 | } 34 | 35 | #endif -------------------------------------------------------------------------------- /CMake/CMakeCommon.cmake: -------------------------------------------------------------------------------- 1 | # for common functions 2 | cmake_minimum_required(VERSION 3.22) 3 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 4 | 5 | #filter 6 | macro(SOURCE_GROUP_BY_DIR source_files) 7 | set(sgbd_cur_dir ${CMAKE_CURRENT_SOURCE_DIR}) 8 | set(sgbd_root_dir ${CMAKE_SOURCE_DIR}) 9 | foreach(sgbd_file ${${source_files}}) 10 | # 获取相对于项目根目录的路径 11 | file(RELATIVE_PATH sgbd_fpath ${sgbd_root_dir} ${sgbd_file}) 12 | 13 | # 确保路径不包含 ".." 14 | string(REPLACE ".." "" sgbd_fpath ${sgbd_fpath}) 15 | 16 | # 获取组名 17 | string(REGEX REPLACE "(.*)/.*" "\\1" sgbd_group_name ${sgbd_fpath}) 18 | string(COMPARE EQUAL ${sgbd_fpath} ${sgbd_group_name} sgbd_nogroup) 19 | string(REPLACE "/" "\\" sgbd_group_name ${sgbd_group_name}) 20 | 21 | if(sgbd_nogroup) 22 | set(sgbd_group_name "\\") 23 | endif(sgbd_nogroup) 24 | 25 | source_group(${sgbd_group_name} FILES ${sgbd_file}) 26 | endforeach(sgbd_file) 27 | endmacro(SOURCE_GROUP_BY_DIR) 28 | 29 | # Enable C++ 14 30 | if (MSVC AND NOT DEFINED CMAKE_CXX_STANDARD) 31 | set(CMAKE_CXX_STANDARD 14) 32 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 33 | endif() 34 | 35 | # use unicode by default 36 | add_definitions(-DUNICODE -D_UNICODE) 37 | 38 | if(CMAKE_BUILD_TYPE STREQUAL "Debug") 39 | add_definitions(-DDEBUG) 40 | endif() 41 | 42 | -------------------------------------------------------------------------------- /Benchmark/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(Benchmark) 3 | 4 | # Set C++ standard 5 | set(CMAKE_CXX_STANDARD 11) 6 | set(CMAKE_CXX_STANDARD_REQUIRED True) 7 | 8 | # Include parent directory 9 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../) 10 | 11 | # Recursively get source files 12 | file(GLOB_RECURSE TEST_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Sources/*.c*) 13 | SOURCE_GROUP_BY_DIR(TEST_SOURCE_FILES) 14 | 15 | # Define macros 16 | add_definitions(-DUNICODE -D_UNICODE) 17 | 18 | # Add Celero subdirectory 19 | add_subdirectory(External/Celero) 20 | include_directories(External/Celero/include) 21 | 22 | # Set project output directory 23 | set(OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/$) 24 | 25 | # Set output directories for executables and libraries 26 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_DIRECTORY}) 27 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${OUTPUT_DIRECTORY}) 28 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${OUTPUT_DIRECTORY}) 29 | 30 | # Add executable 31 | add_executable(Benchmark ${TEST_SOURCE_FILES}) 32 | 33 | # Set Celero library output directory 34 | set(CELERO_OUTPUT_DIR ${CMAKE_BINARY_DIR}/Benchmark/External/Celero/$) 35 | set(CELERO_OUTPUT_DIR2 ${CMAKE_BINARY_DIR}/Benchmark/External/Celero) 36 | 37 | # Add Celero output directory to link search paths 38 | target_link_directories(Benchmark PRIVATE ${CELERO_OUTPUT_DIR} ${CELERO_OUTPUT_DIR2}) 39 | 40 | # Select library name based on build type 41 | set(CELERO_LIB "$,celero-d,celero>") 42 | 43 | # Link Celero library 44 | target_link_libraries(Benchmark ${CELERO_LIB}) 45 | 46 | -------------------------------------------------------------------------------- /Format/StandardLibraryAdapter.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2024 CPPStringFormatting 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | Project URL: https://github.com/bodong1987/CPPStringFormatting 25 | */ 26 | /* 27 | * This is an adapter implemented for stl strings. 28 | * With these adapter classes, you can format C++ stl strings. 29 | */ 30 | #pragma once 31 | 32 | #include 33 | #include 34 | #include 35 | -------------------------------------------------------------------------------- /Format/Common/Noncopyable.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2024 CPPStringFormatting 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | Project URL: https://github.com/bodong1987/CPPStringFormatting 25 | */ 26 | #pragma once 27 | 28 | #include 29 | 30 | namespace Formatting 31 | { 32 | /// 33 | /// Class Noncopyable. 34 | /// 35 | class Noncopyable // NOLINT 36 | { 37 | public: 38 | Noncopyable() {} // NOLINT 39 | ~Noncopyable() {} // NOLINT 40 | #if FL_COMPILER_IS_GREATER_THAN_CXX11 41 | Noncopyable(const Noncopyable&) = delete; 42 | Noncopyable& operator = (const Noncopyable&) = delete; 43 | #else 44 | private: 45 | // declare without implementation 46 | Noncopyable(const Noncopyable&); 47 | Noncopyable& operator = (const Noncopyable&); 48 | #endif 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /Format/Format.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2024 CPPStringFormatting 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | Project URL: https://github.com/bodong1987/CPPStringFormatting 25 | */ 26 | /* 27 | * This is a library that uses the C# formatted string style to format C++ strings. 28 | * It is a type-safe, reentrant, reorder able formatting library. 29 | * Not all C# format string format descriptors are perfectly supported, it only supports most common formatting methods. 30 | * see also: 31 | * @reference https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings 32 | * usage : 33 | * https://github.com/bodong1987/CPPStringFormatting/blob/master/README.md 34 | */ 35 | #pragma once 36 | 37 | #include 38 | #include 39 | #include 40 | 41 | #include 42 | -------------------------------------------------------------------------------- /test_compiler_compatible.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | 4 | REM set build folder 5 | set PROJECT_ROOT=%~dp0 6 | set BUILD_DIR=%PROJECT_ROOT%Build 7 | 8 | echo search msbuild.exe... 9 | REM define search paths 10 | set "MSBUILD_PATHS="C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\amd64";"C:\Program Files\Microsoft Visual Studio\2022\Professional\MSBuild\Current\Bin\amd64";"C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\amd64";"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\amd64";"C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Current\Bin\amd64";"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\amd64"" 11 | 12 | REM find msbuild.exe 13 | for %%p in (%MSBUILD_PATHS%) do ( 14 | if exist %%~p\msbuild.exe ( 15 | set "MSBUILD_EXE=%%~p\msbuild.exe" 16 | goto found 17 | ) 18 | ) 19 | 20 | echo Unable to find msbuild.exe 21 | exit /b 1 22 | 23 | :found 24 | echo Found msbuild.exe at %MSBUILD_EXE% 25 | 26 | echo generate projects with C++ 14 27 | REM call generate_projects.bat 28 | call generate_projects.bat --nopause --cppstd 14 29 | 30 | REM compile with different C++ standards 31 | echo Compiling with C++ 14... 32 | "%MSBUILD_EXE%" %BUILD_DIR%\vs2022_x64\CPPStringFormatting.sln /p:PlatformToolset=v143 33 | if %errorlevel% neq 0 ( 34 | echo Failed to compile with C++ 14 35 | exit /b %errorlevel% 36 | ) 37 | 38 | echo generate projects with C++ 17 39 | REM call generate_projects.bat 40 | call generate_projects.bat --nopause --cppstd 17 41 | 42 | echo Compiling with C++ 17... 43 | "%MSBUILD_EXE%" %BUILD_DIR%\vs2022_x64\CPPStringFormatting.sln /p:PlatformToolset=v143 44 | if %errorlevel% neq 0 ( 45 | echo Failed to compile with C++ 17 46 | exit /b %errorlevel% 47 | ) 48 | 49 | echo generate projects with C++ 20 50 | REM call generate_projects.bat 51 | call generate_projects.bat --nopause --cppstd 20 52 | 53 | echo Compiling with C++ 20... 54 | "%MSBUILD_EXE%" %BUILD_DIR%\vs2022_x64\CPPStringFormatting.sln /p:PlatformToolset=v143 55 | if %errorlevel% neq 0 ( 56 | echo Failed to compile with C++ 20 57 | exit /b %errorlevel% 58 | ) 59 | 60 | echo Compilation complete. 61 | endlocal -------------------------------------------------------------------------------- /generate_projects.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | 4 | REM set default values 5 | set PAUSE=YES 6 | set CPP_STD=14 7 | 8 | :next_arg 9 | if "%~1"=="" goto args_done 10 | if "%~1"=="--nopause" ( 11 | set PAUSE=NO 12 | shift 13 | goto next_arg 14 | ) 15 | if "%~1"=="--cppstd" ( 16 | shift 17 | set CPP_STD=%~2 18 | shift 19 | goto next_arg 20 | ) 21 | shift 22 | goto next_arg 23 | 24 | :args_done 25 | 26 | echo Pause = %PAUSE% 27 | echo C++ Standard = C++ %CPP_STD% 28 | 29 | REM set root folder 30 | set PROJECT_ROOT=%~dp0 31 | 32 | REM set build folder 33 | set BUILD_DIR=%PROJECT_ROOT%Build 34 | 35 | REM gen x86 36 | echo Generating Visual Studio 2022 project for x86... 37 | cmake -G "Visual Studio 17 2022" -A Win32 -S %PROJECT_ROOT% -B %BUILD_DIR%\vs2022_x86 -DCMAKE_CXX_STANDARD=%CPP_STD% 38 | if %errorlevel% neq 0 ( 39 | echo Failed to generate x86 project 40 | exit /b %errorlevel% 41 | ) 42 | 43 | REM gen x64 44 | echo Generating Visual Studio 2022 project for x64... 45 | cmake -G "Visual Studio 17 2022" -A x64 -S %PROJECT_ROOT% -B %BUILD_DIR%\vs2022_x64 -DCMAKE_CXX_STANDARD=%CPP_STD% 46 | if %errorlevel% neq 0 ( 47 | echo Failed to generate x64 project 48 | exit /b %errorlevel% 49 | ) 50 | 51 | REM check for VS2008 52 | echo Checking for Visual Studio 2008... 53 | set "VS2008_PATH=C:\Program Files (x86)\Microsoft Visual Studio 9.0" 54 | if exist "%VS2008_PATH%" ( 55 | echo Visual Studio 2008 found. Generating Visual Studio 2008 project... 56 | cmake -G "Visual Studio 9 2008" -S %PROJECT_ROOT% -B %BUILD_DIR%\vs2008 -DCMAKE_CXX_STANDARD=%CPP_STD% 57 | if %errorlevel% neq 0 ( 58 | echo Failed to generate VS2008 project 59 | exit /b %errorlevel% 60 | ) 61 | ) else ( 62 | echo Visual Studio 2008 not found. Skipping VS2008 project generation. 63 | ) 64 | 65 | REM check Code::Blocks exists 66 | echo Checking for Code::Blocks... 67 | set "CODEBLOCKS_PATH_X86=C:\Program Files (x86)\CodeBlocks\codeblocks.exe" 68 | set "CODEBLOCKS_PATH_X64=C:\Program Files\CodeBlocks\codeblocks.exe" 69 | set CODEBLOCKS_FOUND=0 70 | 71 | if exist "%CODEBLOCKS_PATH_X86%" ( 72 | set CODEBLOCKS_FOUND=1 73 | set "CODEBLOCKS_PATH=%CODEBLOCKS_PATH_X86%" 74 | ) else if exist "%CODEBLOCKS_PATH_X64%" ( 75 | set CODEBLOCKS_FOUND=1 76 | set "CODEBLOCKS_PATH=%CODEBLOCKS_PATH_X64%" 77 | ) 78 | 79 | if %CODEBLOCKS_FOUND% equ 1 ( 80 | echo Code::Blocks found at %CODEBLOCKS_PATH%. Generating Code::Blocks project... 81 | cmake -G "CodeBlocks - MinGW Makefiles" -S %PROJECT_ROOT% -B %BUILD_DIR%\CodeBlocks -DCMAKE_CXX_STANDARD=%CPP_STD% 82 | if %errorlevel% neq 0 ( 83 | echo Failed to generate Code::Blocks project 84 | exit /b %errorlevel% 85 | ) 86 | ) else ( 87 | echo Code::Blocks not found. Skipping Code::Blocks project generation. 88 | ) 89 | 90 | echo Generation complete. 91 | REM pause if --nopause argument was not provided 92 | if "%PAUSE%"=="YES" pause 93 | 94 | endlocal -------------------------------------------------------------------------------- /Format/Common/Macros.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2024 CPPStringFormatting 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | Project URL: https://github.com/bodong1987/CPPStringFormatting 25 | */ 26 | #pragma once 27 | 28 | // Comma 29 | #define FL_PP_COMMA() , 30 | 31 | // Text 32 | #define _FL_PP_TEXT(Expr) #Expr /*NOLINT*/ 33 | #define FL_PP_TEXT(Expr) _FL_PP_TEXT(Expr) 34 | 35 | // CAT 36 | #define _FL_PP_CAT_IMPL_(a, b ) a ## b /*NOLINT*/ 37 | #define FL_PP_CAT(a, b) _FL_PP_CAT_IMPL_( a, b ) 38 | 39 | // make a unique name 40 | #define FL_PP_UNIQUE_NAME( Prefix ) \ 41 | FL_PP_CAT(Prefix, FL_PP_CAT(_, __LINE__)) 42 | 43 | // support not 44 | // ReSharper disable once CommentTypo 45 | // NOLINTBEGIN 46 | #define FL_PP_COMPL_0 1 47 | #define FL_PP_COMPL_1 0 48 | #define _FL_PP_NOT(Expr) FL_PP_CAT(FL_PP_COMPL_, FL_PP_BOOL(Expr)) 49 | #define FL_PP_NOT(Expr) _FL_PP_NOT(Expr) 50 | // ReSharper disable once CommentTypo 51 | // NOLINTEND 52 | 53 | // Bool 54 | #define FL_PP_BOOL(x) FL_PP_BOOL_I(x) 55 | 56 | #define FL_PP_BOOL_I(x) FL_PP_BOOL_ ## x 57 | 58 | // ReSharper disable once CommentTypo 59 | // NOLINTBEGIN 60 | #define FL_PP_BOOL_0 0 61 | #define FL_PP_BOOL_1 1 62 | #define FL_PP_BOOL_2 1 63 | #define FL_PP_BOOL_3 1 64 | #define FL_PP_BOOL_4 1 65 | #define FL_PP_BOOL_5 1 66 | #define FL_PP_BOOL_6 1 67 | #define FL_PP_BOOL_7 1 68 | #define FL_PP_BOOL_8 1 69 | #define FL_PP_BOOL_9 1 70 | #define FL_PP_BOOL_10 1 71 | #define FL_PP_BOOL_11 1 72 | #define FL_PP_BOOL_12 1 73 | #define FL_PP_BOOL_13 1 74 | #define FL_PP_BOOL_14 1 75 | #define FL_PP_BOOL_15 1 76 | #define FL_PP_BOOL_16 1 77 | // ReSharper disable once CommentTypo 78 | // NOLINTEND 79 | 80 | // Simple If 81 | #define FL_PP_IF(Condition, t, f) FL_PP_IIF(FL_PP_BOOL(Condition), t, f) 82 | #define FL_PP_IIF(Bit, t, f) FL_PP_IIF_I(Bit, t, f) 83 | #define FL_PP_IIF_I(Bit, t, f) FL_PP_IIF_ ## Bit(t, f) 84 | #define FL_PP_IIF_0(t, f) f 85 | #define FL_PP_IIF_1(t, f) t 86 | 87 | 88 | // Comma 89 | #define FL_PP_COMMA() , 90 | #define FL_PP_EMPTY() 91 | #define FL_PP_COMMA_IF(Condition) FL_PP_IF(Condition, FL_PP_COMMA, FL_PP_EMPTY)() 92 | 93 | // Repeat Semantic 94 | #define FL_PP_REPEAT(n, m, d) FL_PP_CAT(FL_PP_REPEAT_, n)(m, d) 95 | 96 | // max iterate count 16 97 | // if you want more, you can add it yourself 98 | #define FL_PP_REPEAT_0(m, d) 99 | #define FL_PP_REPEAT_1(m, d) m(d, 0) 100 | #define FL_PP_REPEAT_2(m, d) FL_PP_REPEAT_1(m, d) m(d, 1) 101 | #define FL_PP_REPEAT_3(m, d) FL_PP_REPEAT_2(m, d) m(d, 2) 102 | #define FL_PP_REPEAT_4(m, d) FL_PP_REPEAT_3(m, d) m(d, 3) 103 | #define FL_PP_REPEAT_5(m, d) FL_PP_REPEAT_4(m, d) m(d, 4) 104 | #define FL_PP_REPEAT_6(m, d) FL_PP_REPEAT_5(m, d) m(d, 5) 105 | #define FL_PP_REPEAT_7(m, d) FL_PP_REPEAT_6(m, d) m(d, 6) 106 | #define FL_PP_REPEAT_8(m, d) FL_PP_REPEAT_7(m, d) m(d, 7) 107 | #define FL_PP_REPEAT_9(m, d) FL_PP_REPEAT_8(m, d) m(d, 8) 108 | #define FL_PP_REPEAT_10(m, d) FL_PP_REPEAT_9(m, d) m(d, 9) 109 | #define FL_PP_REPEAT_11(m, d) FL_PP_REPEAT_10(m, d) m(d, 10) 110 | #define FL_PP_REPEAT_12(m, d) FL_PP_REPEAT_11(m, d) m(d, 11) 111 | #define FL_PP_REPEAT_13(m, d) FL_PP_REPEAT_12(m, d) m(d, 12) 112 | #define FL_PP_REPEAT_14(m, d) FL_PP_REPEAT_13(m, d) m(d, 13) 113 | #define FL_PP_REPEAT_15(m, d) FL_PP_REPEAT_14(m, d) m(d, 14) 114 | #define FL_PP_REPEAT_16(m, d) FL_PP_REPEAT_15(m, d) m(d, 15) 115 | 116 | // Expand 117 | #define FL_PP_EXPAND(X) X 118 | 119 | // enums 120 | #define FL_ENUM_SCALARS( Macro ) \ 121 | Macro(1) \ 122 | Macro(2) \ 123 | Macro(3) \ 124 | Macro(4) \ 125 | Macro(5) \ 126 | Macro(6) \ 127 | Macro(7) \ 128 | Macro(8) \ 129 | Macro(9) \ 130 | Macro(10) \ 131 | Macro(11) \ 132 | Macro(12) \ 133 | Macro(13) \ 134 | Macro(14) \ 135 | Macro(15) \ 136 | Macro(16) 137 | 138 | // make some text here 139 | // it used disable warning for gcc 140 | // 141 | 142 | -------------------------------------------------------------------------------- /Format/Details/StandardLibrary/StandardLibraryPolicy.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2024 CPPStringFormatting 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | Project URL: https://github.com/bodong1987/CPPStringFormatting 25 | */ 26 | #pragma once 27 | 28 | #include 29 | #include 30 | 31 | #if FL_COMPILER_IS_GREATER_THAN_CXX11 32 | #include 33 | #else 34 | #include 35 | #endif 36 | 37 | // ReSharper disable once CppEnforceNestedNamespacesStyle 38 | namespace Formatting // NOLINT(*-concat-nested-namespaces) 39 | { 40 | // ReSharper disable once CppEnforceNestedNamespacesStyle 41 | namespace Details 42 | { 43 | namespace StandardLibrary 44 | { 45 | /// 46 | /// Class TStandardPolicy. 47 | /// stl default policy 48 | /// 49 | template 50 | class TStandardPolicy 51 | { 52 | public: 53 | typedef TCharType CharType; 54 | typedef TFormatPattern FormatPattern; 55 | typedef typename FormatPattern::SizeType SizeType; 56 | typedef typename FormatPattern::ByteType ByteType; 57 | typedef TAutoArray PatternListType; // NOLINT 58 | typedef typename PatternListType::ConstIterator PatternIterator; 59 | typedef std::runtime_error ExceptionType; 60 | typedef TMutexType MutexType; 61 | 62 | #if FL_COMPILER_IS_GREATER_THAN_CXX11 63 | typedef std::unordered_map PatternMapType; 64 | #else 65 | typedef std::map PatternMapType; 66 | #endif 67 | 68 | static const PatternListType* FindByHashKey(const PatternMapType& storageReference, SizeType hashKey) 69 | { 70 | typename PatternMapType::const_iterator itPos = storageReference.find(hashKey); 71 | 72 | return itPos != storageReference.end() ? &itPos->second : nullptr; 73 | } 74 | 75 | static void ReserveList(PatternListType& /*ListRef*/, int /*Len*/) 76 | { 77 | // AutoArray does not need reserve 78 | } 79 | 80 | static const PatternListType* Emplace( 81 | PatternMapType& storageReference, 82 | SizeType hashKey, 83 | #if FL_COMPILER_IS_GREATER_THAN_CXX11 84 | PatternListType&& patterns 85 | #else 86 | const PatternListType& patterns 87 | #endif 88 | ) 89 | { 90 | std::pair< typename PatternMapType::iterator, bool> Results = 91 | #if FL_COMPILER_IS_GREATER_THAN_CXX11 92 | storageReference.emplace(std::make_pair(hashKey, std::move(patterns))); 93 | #else 94 | storageReference.insert(std::make_pair(hashKey, patterns)); 95 | #endif 96 | 97 | return Results.second ? &Results.first->second : nullptr; 98 | } 99 | 100 | static void AppendPattern(PatternListType& patterns, const FormatPattern& pattern) 101 | { 102 | patterns.AddItem(pattern); 103 | } 104 | }; 105 | 106 | #if FL_WITH_THREAD_LOCAL || !FL_WITH_MULTITHREAD_SUPPORT 107 | typedef SharedMutexNone DefaultMutexType; 108 | #else 109 | typedef SharedMutex DefaultMutexType; 110 | #endif 111 | 112 | typedef TPatternStorage< TStandardPolicy > STLPatternStorageA; // NOLINT 113 | typedef TPatternStorage< TStandardPolicy > STLPatternStorageW; // NOLINT 114 | typedef TGlobalPatternStorage< TStandardPolicy > STLGlobalPatternStorageA; // NOLINT 115 | typedef TGlobalPatternStorage< TStandardPolicy > STLGlobalPatternStorageW; // NOLINT 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /Format/Details/Pattern.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2024 CPPStringFormatting 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | Project URL: https://github.com/bodong1987/CPPStringFormatting 25 | */ 26 | #pragma once 27 | 28 | #include 29 | 30 | #if !FL_COMPILER_IS_GREATER_THAN_CXX11 31 | #include 32 | #endif 33 | 34 | namespace Formatting // NOLINT(*-concat-nested-namespaces) 35 | { 36 | namespace Details 37 | { 38 | #if FL_COMPILER_IS_GREATER_THAN_CXX11 39 | enum class EFormatFlag : uint8_t 40 | #else 41 | namespace EFormatFlag 42 | { 43 | enum Enum 44 | #endif 45 | { 46 | Raw, 47 | None, 48 | Decimal, 49 | Exponent, 50 | FixedPoint, 51 | General, 52 | CSV, // NOLINT 53 | Percentage, 54 | Hex, 55 | Binary 56 | }; 57 | #if FL_COMPILER_IS_GREATER_THAN_CXX11 58 | typedef EFormatFlag FormatFlagType; 59 | #else 60 | } 61 | 62 | typedef EFormatFlag::Enum FormatFlagType; 63 | #endif 64 | 65 | #if FL_COMPILER_IS_GREATER_THAN_CXX11 66 | enum class EAlignFlag : uint8_t 67 | #else 68 | namespace EAlignFlag 69 | { 70 | enum Enum 71 | #endif 72 | { 73 | Right, 74 | Left 75 | }; 76 | 77 | #if FL_COMPILER_IS_GREATER_THAN_CXX11 78 | typedef EAlignFlag AlignFlagType; 79 | #else 80 | } 81 | typedef EAlignFlag::Enum AlignFlagType; 82 | #endif 83 | 84 | /// 85 | /// Class TFormatPattern. 86 | /// This is the description of a Format unit 87 | /// example{ 0 } {0:d} 88 | /// 89 | template < typename TCharType > 90 | class TFormatPattern 91 | { 92 | public: 93 | typedef TCharType CharType; 94 | typedef unsigned char ByteType; 95 | typedef size_t SizeType; 96 | 97 | /// 98 | /// Initializes a new instance of the class. 99 | /// 100 | TFormatPattern() : 101 | Start(static_cast(-1)), 102 | Len(0), 103 | Flag(EFormatFlag::Raw), 104 | Align(EAlignFlag::Right), 105 | Index(static_cast(-1)), 106 | Precision(static_cast(-1)), 107 | Width(static_cast(-1)), 108 | IsUpper(false) 109 | { 110 | } 111 | 112 | /// 113 | /// Gets the length. 114 | /// 115 | /// SizeType. 116 | FL_NO_DISCARD SizeType GetLength() const 117 | { 118 | return Len; 119 | } 120 | 121 | /// 122 | /// Returns true if ... is valid. 123 | /// 124 | /// bool. 125 | FL_NO_DISCARD bool IsValid() const 126 | { 127 | return Start != -1 && Len != -1 && Index != -1; 128 | } 129 | 130 | /// 131 | /// Determines whether this instance has width. 132 | /// 133 | /// bool. 134 | FL_NO_DISCARD bool HasWidth() const 135 | { 136 | return Width != static_cast(-1); 137 | } 138 | 139 | /// 140 | /// Determines whether this instance has precision. 141 | /// 142 | /// bool. 143 | FL_NO_DISCARD bool HasPrecision() const 144 | { 145 | return Precision != static_cast(-1); 146 | } 147 | 148 | // ReSharper disable once CppRedundantAccessSpecifier 149 | public: 150 | SizeType Start; 151 | SizeType Len; 152 | FormatFlagType Flag; 153 | AlignFlagType Align; 154 | ByteType Index; 155 | ByteType Precision; 156 | ByteType Width; 157 | bool IsUpper; 158 | }; 159 | } 160 | 161 | #if !FL_COMPILER_IS_GREATER_THAN_CXX11 162 | namespace Mpl 163 | { 164 | /// 165 | /// make a partial specialization, so this TFormatPattern become a simple type 166 | /// it will have a fast Move and copy operation 167 | /// 168 | template < typename TCharType > 169 | struct IsSimple< Details::TFormatPattern > : TrueType{}; 170 | } 171 | #endif 172 | } 173 | -------------------------------------------------------------------------------- /Format/Common/Mpl.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2024 CPPStringFormatting 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | Project URL: https://github.com/bodong1987/CPPStringFormatting 25 | */ 26 | #pragma once 27 | 28 | #include 29 | 30 | #if FL_COMPILER_GCC 31 | // ReSharper disable CppUnusedIncludeDirective 32 | #undef __STRICT_ANSI__ 33 | #include 34 | #include 35 | #include 36 | #include 37 | #if FL_COMPILER_IS_GREATER_THAN_CXX11 38 | #include 39 | #endif 40 | // ReSharper restore CppUnusedIncludeDirective 41 | #endif // FL_COMPILER_GCC 42 | 43 | // ReSharper disable once CppEnforceNestedNamespacesStyle 44 | namespace Formatting // NOLINT(*-concat-nested-namespaces) 45 | { 46 | namespace Mpl 47 | { 48 | template 49 | struct RemoveArray 50 | { 51 | typedef T Type; 52 | }; 53 | 54 | template 55 | struct RemoveArray< T[N]> 56 | { 57 | typedef T Type; 58 | }; 59 | 60 | template 61 | struct RemoveArray< const T[N]> 62 | { 63 | typedef T Type; 64 | }; 65 | 66 | struct TrueType 67 | { 68 | enum { Value = 1 }; // NOLINT(performance-enum-size) 69 | }; 70 | 71 | struct FalseType 72 | { 73 | enum { Value = 0 }; // NOLINT(performance-enum-size) 74 | }; 75 | 76 | template < bool, typename T1, typename T2 > // NOLINT 77 | class IfElse 78 | { 79 | public: 80 | typedef T2 Type; 81 | }; 82 | 83 | template < typename T1, typename T2 > 84 | class IfElse< true, T1, T2 > 85 | { 86 | public: 87 | typedef T1 Type; 88 | }; 89 | 90 | template < typename T1, typename T2 > // NOLINT 91 | struct IsSame : FalseType {}; 92 | 93 | template < typename T > 94 | struct IsSame : TrueType {}; 95 | 96 | template < typename T > // NOLINT 97 | struct IsPtr : FalseType {}; 98 | 99 | template < typename T > // NOLINT 100 | struct IsPtr : TrueType {}; 101 | 102 | template < typename T > // NOLINT 103 | struct IsArray : FalseType {}; 104 | 105 | template < typename T, int Size > 106 | struct IsArray< T[Size] > : TrueType {}; 107 | 108 | template // NOLINT 109 | struct IsSigned : FalseType {}; 110 | 111 | template <> 112 | struct IsSigned : TrueType{}; 113 | 114 | template <> 115 | struct IsSigned : TrueType{}; 116 | 117 | template <> 118 | struct IsSigned : TrueType{}; 119 | 120 | template <> 121 | struct IsSigned : TrueType{}; 122 | 123 | template // NOLINT 124 | struct UnsignedTypeOf{}; 125 | 126 | template <> 127 | struct UnsignedTypeOf 128 | { 129 | typedef uint8_t Type; 130 | }; 131 | 132 | template <> 133 | struct UnsignedTypeOf 134 | { 135 | typedef uint16_t Type; 136 | }; 137 | 138 | template <> 139 | struct UnsignedTypeOf 140 | { 141 | typedef uint32_t Type; 142 | }; 143 | 144 | template <> 145 | struct UnsignedTypeOf 146 | { 147 | typedef uint64_t Type; 148 | }; 149 | 150 | #if !FL_COMPILER_IS_GREATER_THAN_CXX11 151 | /// 152 | /// Struct IsScalar 153 | /// 154 | template < typename T > 155 | struct IsScalar : FalseType{}; 156 | 157 | #define FL_PP_SPECIALIZATION_IS_SCALAR_TRUE_TYPE( Type ) \ 158 | template <> \ 159 | struct IsScalar< Type > : TrueType{} 160 | 161 | FL_PP_SPECIALIZATION_IS_SCALAR_TRUE_TYPE(bool); 162 | FL_PP_SPECIALIZATION_IS_SCALAR_TRUE_TYPE(int8_t); 163 | FL_PP_SPECIALIZATION_IS_SCALAR_TRUE_TYPE(uint8_t); 164 | FL_PP_SPECIALIZATION_IS_SCALAR_TRUE_TYPE(int16_t); 165 | FL_PP_SPECIALIZATION_IS_SCALAR_TRUE_TYPE(uint16_t); 166 | FL_PP_SPECIALIZATION_IS_SCALAR_TRUE_TYPE(int32_t); 167 | FL_PP_SPECIALIZATION_IS_SCALAR_TRUE_TYPE(uint32_t); 168 | FL_PP_SPECIALIZATION_IS_SCALAR_TRUE_TYPE(float); 169 | FL_PP_SPECIALIZATION_IS_SCALAR_TRUE_TYPE(double); 170 | FL_PP_SPECIALIZATION_IS_SCALAR_TRUE_TYPE(long double); 171 | FL_PP_SPECIALIZATION_IS_SCALAR_TRUE_TYPE(int64_t); 172 | FL_PP_SPECIALIZATION_IS_SCALAR_TRUE_TYPE(uint64_t); 173 | 174 | #undef FL_PP_SPECIALIZATION_IS_SCALAR_TRUE_TYPE 175 | 176 | /// 177 | /// Struct IsSimple 178 | /// 179 | template < typename T > 180 | struct IsSimple 181 | { 182 | /// 183 | /// Enum __unnamed_enum_000e_3 184 | /// 185 | enum 186 | { 187 | /// 188 | /// The value 189 | /// 190 | Value = 191 | #if FL_COMPILER_MSVC 192 | #if _MSC_VER >= 1900 // after visual studio 2015 193 | std::is_trivially_copyable::value 194 | #elif _MSC_VER >= 1600 195 | std::has_trivial_assign::value 196 | #else 197 | __has_trivial_assign(T) 198 | #endif 199 | 200 | #elif FL_COMPILER_GCC 201 | #if (__GUNC__ >= 4 ) && (__GNUC_MINOR__ >= 3 ) 202 | __has_trivial_assign(T) 203 | #else 204 | IsScalar::Value || IsPtr::Value 205 | #endif 206 | #else 207 | std::is_trivially_copyable::value 208 | #endif 209 | }; 210 | }; 211 | #endif 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /Format/Details/InlineFiles/FormatTo.inl: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2024 CPPStringFormatting 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | Project URL: https://github.com/bodong1987/CPPStringFormatting 25 | */ 26 | 27 | // ReSharper disable All 28 | // Internal Use 29 | #if FL_COMPILER_IS_GREATER_THAN_CXX11 30 | #error "This file is prepared to support the C++ standard versions which are earlier than C++11. If your compiler already supports C++11 or newer, you should no longer use this set of code." 31 | #endif 32 | 33 | #ifdef FL_FORMAT_TO_INDEX 34 | #define FL_TEMPLATE_PARAMETERS_BODY( d, i ) \ 35 | FL_PP_COMMA_IF(i) typename FL_PP_CAT( T, i ) 36 | 37 | #define FL_TEMPLATE_PARAMETER_TYPE_BODY( d, i ) \ 38 | FL_PP_COMMA_IF(i) FL_PP_CAT( T, i ) 39 | 40 | #define FL_REAL_ARGUMENT_BODY( d, i ) \ 41 | FL_PP_COMMA_IF(i) const FL_PP_CAT(T,i)& FL_PP_CAT(arg,i) 42 | 43 | #define FL_REAL_ARGUMENT_ARG_BODY( d, i ) \ 44 | FL_PP_COMMA_IF(i) FL_PP_CAT(arg,i) 45 | 46 | #define FL_TRANSFER_BODY( d, i ) \ 47 | case i: \ 48 | { \ 49 | typedef FL_PP_CAT(T, i) Type; \ 50 | typedef typename Mpl::IfElse< \ 51 | Mpl::IsArray::Value, \ 52 | const typename Mpl::RemoveArray::Type*, \ 53 | Type >::Type TransferType; \ 54 | if (!TTranslator< TCharType, TransferType >::Transfer(sink, Pattern, FL_PP_CAT(arg, i))) \ 55 | { \ 56 | TRawTranslator< TCharType >::Transfer(sink, Pattern, Shims::PtrOf(format)); \ 57 | } \ 58 | } \ 59 | break; 60 | 61 | //#pragma message( FL_PP_TEXT(FL_PP_REPEAT(FL_FORMAT_TO_INDEX, FL_TEMPLATE_PARAMETERS_BODY, ))) 62 | 63 | /* 64 | * base iterate function 65 | * used to generate the real function for Format... 66 | * need provide pattern list... 67 | */ 68 | template < 69 | typename TCharType, 70 | typename TPatternStorageType 71 | FL_PP_COMMA_IF(FL_FORMAT_TO_INDEX) 72 | FL_PP_REPEAT(FL_FORMAT_TO_INDEX, FL_TEMPLATE_PARAMETERS_BODY, ) 73 | > 74 | inline TAutoString& FormatTo( 75 | TAutoString& sink, 76 | const typename TPatternStorageType::PatternListType* patterns, 77 | const TCharType* format, 78 | const size_t length 79 | FL_PP_COMMA_IF(FL_FORMAT_TO_INDEX) 80 | FL_PP_REPEAT(FL_FORMAT_TO_INDEX, FL_REAL_ARGUMENT_BODY, ) 81 | ) 82 | { 83 | if (patterns == nullptr) 84 | { 85 | sink.AddStr(format, length); 86 | return sink; 87 | } 88 | 89 | // walk though all patterns, and use TTranslator::Transfer to convert target type to string 90 | typename TPatternStorageType::PatternIterator Iter(*patterns); 91 | 92 | while (Iter.IsValid()) 93 | { 94 | const typename TPatternStorageType::FormatPattern& Pattern = *Iter; 95 | 96 | if (Pattern.Flag == EFormatFlag::Raw) 97 | { 98 | TRawTranslator::Transfer(sink, Pattern, Shims::PtrOf(format)); 99 | } 100 | else 101 | { 102 | switch (Pattern.Index) 103 | { 104 | /* 105 | // if you get a compile error with Transfer function can't visit 106 | // it means that you have transfer an unsupported parameter to format pipeline 107 | // you can do them to fix this error: 108 | // 1. change your code, convert it to the support type 109 | // 2. make a specialization of TTranslator for your type. 110 | */ 111 | FL_PP_REPEAT(FL_FORMAT_TO_INDEX, FL_TRANSFER_BODY, ); 112 | default: 113 | TRawTranslator::Transfer(sink, Pattern, Shims::PtrOf(format)); 114 | break; 115 | } 116 | } 117 | 118 | Iter.Next(); 119 | } 120 | 121 | return sink; 122 | } 123 | 124 | 125 | /* 126 | * base iterate function 127 | * used to generate the real function for Format... 128 | */ 129 | template < 130 | typename TCharType, 131 | typename TPatternStorageType, 132 | typename TFormatType 133 | FL_PP_COMMA_IF(FL_FORMAT_TO_INDEX) 134 | FL_PP_REPEAT(FL_FORMAT_TO_INDEX, FL_TEMPLATE_PARAMETERS_BODY, ) 135 | > 136 | inline TAutoString& FormatTo( 137 | TAutoString& sink, 138 | const TFormatType& format 139 | FL_PP_COMMA_IF(FL_FORMAT_TO_INDEX) 140 | FL_PP_REPEAT(FL_FORMAT_TO_INDEX, FL_REAL_ARGUMENT_BODY, ) 141 | ) 142 | { 143 | typedef typename TPatternStorageType::FormatPattern FormatPatternType; 144 | typedef typename TPatternStorageType::PatternListType PatternListType; 145 | typedef typename TPatternStorageType::PatternIterator IteratorType; 146 | 147 | TPatternStorageType* Storage = TPatternStorageType::GetStorage(); 148 | 149 | assert(Storage); 150 | 151 | const TCharType* localFormatText = Shims::PtrOf(format); 152 | const size_t localLength = Shims::LengthOf(format); 153 | 154 | const PatternListType* Patterns = Storage->LookupPatterns( 155 | localFormatText, 156 | localLength, 157 | CalculateByteArrayHash(reinterpret_cast(localFormatText), localLength*sizeof(TCharType)) 158 | ); 159 | 160 | assert(Patterns); 161 | 162 | return FormatTo< 163 | TCharType, 164 | TPatternStorageType 165 | FL_PP_COMMA_IF(FL_FORMAT_TO_INDEX) 166 | FL_PP_REPEAT(FL_FORMAT_TO_INDEX, FL_TEMPLATE_PARAMETER_TYPE_BODY, ) 167 | >( 168 | sink, 169 | Patterns, 170 | localFormatText, 171 | localLength 172 | FL_PP_COMMA_IF(FL_FORMAT_TO_INDEX) 173 | FL_PP_REPEAT(FL_FORMAT_TO_INDEX, FL_REAL_ARGUMENT_ARG_BODY, ) 174 | ); 175 | } 176 | 177 | #undef FL_REAL_ARGUMENT_ARG_BODY 178 | #undef FL_TEMPLATE_PARAMETER_TYPE_BODY 179 | #undef FL_TEMPLATE_PARAMETERS_BODY 180 | #undef FL_REAL_ARGUMENT_BODY 181 | #undef FL_TRANSFER_BODY 182 | #else 183 | #pragma message("This is an internally used file, please do not include this file directly") 184 | #endif 185 | // ReSharper restore All 186 | -------------------------------------------------------------------------------- /Format/Common/Algorithm.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2024 CPPStringFormatting 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | Project URL: https://github.com/bodong1987/CPPStringFormatting 25 | */ 26 | // ReSharper disable CppRedundantInlineSpecifier 27 | #pragma once 28 | 29 | #include 30 | 31 | #if !FL_COMPILER_IS_GREATER_THAN_CXX11 32 | #include 33 | #endif 34 | 35 | // ReSharper disable once CppUnusedIncludeDirective 36 | #include 37 | 38 | // ReSharper disable once CppEnforceNestedNamespacesStyle 39 | namespace Formatting // NOLINT(*-concat-nested-namespaces) 40 | { 41 | namespace Algorithm 42 | { 43 | #if !FL_COMPILER_IS_GREATER_THAN_CXX11 44 | namespace Details 45 | { 46 | /// 47 | /// Moves the array implementation. 48 | /// 49 | /// The start. 50 | /// The end. 51 | /// The dest. 52 | /// The . 53 | /// OutputType. 54 | template 55 | inline OutputType MoveArrayImpl(InputType start, InputType end, OutputType dest, Mpl::FalseType) 56 | { 57 | while (start != end) 58 | { 59 | *dest = FL_MOVE_SEMANTIC(*start); 60 | ++dest; 61 | ++start; 62 | } 63 | 64 | return dest; 65 | } 66 | 67 | /// 68 | /// Moves the array implementation. 69 | /// 70 | /// The start. 71 | /// The end. 72 | /// The dest. 73 | /// The . 74 | /// T *. 75 | template 76 | inline T* MoveArrayImpl(T* start, T* end, T* dest, Mpl::TrueType) 77 | { 78 | const size_t Size = (end - start)*sizeof(T); 79 | 80 | memcpy(dest, start, Size); 81 | 82 | return dest + (end - start); 83 | } 84 | 85 | /// 86 | /// Copies the array implementation. 87 | /// 88 | /// The start. 89 | /// The end. 90 | /// The dest. 91 | /// The . 92 | /// OutputType. 93 | template 94 | inline OutputType CopyArrayImpl(InputType start, InputType end, OutputType dest, Mpl::FalseType) 95 | { 96 | while (start != end) 97 | { 98 | *dest = *start; 99 | ++dest; 100 | ++start; 101 | } 102 | 103 | return dest; 104 | } 105 | 106 | /// 107 | /// Copies the array implementation. 108 | /// 109 | /// The start. 110 | /// The end. 111 | /// The dest. 112 | /// The . 113 | /// T *. 114 | template 115 | inline T* CopyArrayImpl(T* start, T* end, T* dest, Mpl::TrueType) 116 | { 117 | const size_t Size = (end - start)*sizeof(T); 118 | 119 | memcpy(dest, start, Size); 120 | 121 | return dest + (end - start); 122 | } 123 | } 124 | #endif 125 | 126 | /// 127 | /// Moves the array. 128 | /// 129 | /// The start. 130 | /// The end. 131 | /// The dest. 132 | /// OutputType. 133 | template 134 | inline OutputType MoveArray(InputType start, InputType end, OutputType dest) 135 | { 136 | #if !FL_COMPILER_IS_GREATER_THAN_CXX11 137 | typedef typename Mpl::IfElse< 138 | Mpl::IsSimple::Value && 139 | Mpl::IsSame::Value, 140 | Mpl::TrueType, 141 | Mpl::FalseType >::Type Type; 142 | 143 | return Details::MoveArrayImpl(start, end, dest, Type()); 144 | #else 145 | return std::move(start, end, dest); 146 | #endif 147 | } 148 | 149 | /// 150 | /// Copies the array. 151 | /// 152 | /// The start. 153 | /// The end. 154 | /// The dest. 155 | /// OutputType. 156 | template 157 | inline OutputType CopyArray(InputType start, InputType end, OutputType dest) 158 | { 159 | #if !FL_COMPILER_IS_GREATER_THAN_CXX11 160 | typedef typename Mpl::IfElse< 161 | Mpl::IsSimple::Value && 162 | Mpl::IsSame::Value, 163 | Mpl::TrueType, 164 | Mpl::FalseType >::Type Type; 165 | 166 | return Details::CopyArrayImpl(start, end, dest, Type()); 167 | #else 168 | return std::copy(start, end, dest); 169 | #endif 170 | } 171 | 172 | /// 173 | /// Clamps the specified x. 174 | /// 175 | /// The x. 176 | /// The minimum value. 177 | /// The maximum value. 178 | /// T. 179 | template< class T > 180 | constexpr inline T Clamp(const T x, const T minValue, const T maxValue) 181 | { 182 | return x < minValue ? minValue : x < maxValue ? x : maxValue; 183 | } 184 | 185 | /// 186 | /// Determines the maximum of the parameters. 187 | /// 188 | /// The LHS. 189 | /// The RHS. 190 | /// T. 191 | template< class T > 192 | constexpr inline T Max(const T lhs, const T rhs) 193 | { 194 | return lhs > rhs ? lhs : rhs; 195 | } 196 | 197 | /// 198 | /// Determines the minimum of the parameters. 199 | /// 200 | /// The LHS. 201 | /// The RHS. 202 | /// T. 203 | template < typename T > 204 | constexpr inline T Min(const T lhs, const T rhs) 205 | { 206 | return lhs < rhs ? lhs : rhs; 207 | } 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /UnitTests/Sources/UnitTests_Algorithm.cpp: -------------------------------------------------------------------------------- 1 | // ReSharper disable CppUseStdSize 2 | #include 3 | 4 | #if FL_PLATFORM_WINDOWS 5 | #pragma warning(push) 6 | #pragma warning(disable:4616) 7 | #endif 8 | #include 9 | #if FL_PLATFORM_WINDOWS 10 | #pragma warning(pop) 11 | #endif 12 | 13 | #include 14 | #include 15 | 16 | using namespace Formatting; 17 | 18 | TEST(Algorithm, TestMoveArray) 19 | { 20 | int src[] = {1, 2, 3, 4, 5}; 21 | int dest[5] = {0}; 22 | Algorithm::MoveArray(src, src + 5, dest); 23 | for (int i = 0; i < 5; ++i) 24 | { 25 | EXPECT_EQ(dest[i], i + 1); 26 | } 27 | } 28 | 29 | TEST(Algorithm, TestCopyArray) 30 | { 31 | int src[] = {1, 2, 3, 4, 5}; 32 | int dest[5] = {0}; 33 | Algorithm::CopyArray(src, src + 5, dest); 34 | for (int i = 0; i < 5; ++i) 35 | { 36 | EXPECT_EQ(dest[i], i + 1); 37 | } 38 | } 39 | 40 | TEST(Algorithm, TestClamp) 41 | { 42 | EXPECT_EQ(Algorithm::Clamp(10, 1, 5), 5); 43 | EXPECT_EQ(Algorithm::Clamp(0, 1, 5), 1); 44 | EXPECT_EQ(Algorithm::Clamp(3, 1, 5), 3); 45 | } 46 | 47 | TEST(Algorithm, TestMax) 48 | { 49 | EXPECT_EQ(Algorithm::Max(1, 2), 2); 50 | EXPECT_EQ(Algorithm::Max(2, 1), 2); 51 | EXPECT_EQ(Algorithm::Max(1, 1), 1); 52 | } 53 | 54 | TEST(Algorithm, TestMin) 55 | { 56 | EXPECT_EQ(Algorithm::Min(1, 2), 1); 57 | EXPECT_EQ(Algorithm::Min(2, 1), 1); 58 | EXPECT_EQ(Algorithm::Min(1, 1), 1); 59 | } 60 | 61 | TEST(Algorithm, TestIntegerToBinaryString) 62 | { 63 | char buffer[33]; 64 | 65 | EXPECT_EQ((Details::IntegerToBinaryString(123, buffer)), 7); 66 | EXPECT_EQ(buffer[32], 0); 67 | EXPECT_STREQ(buffer + (33-7-1), "1111011"); 68 | } 69 | 70 | TEST(Algorithm, TestInt64ToString) 71 | { 72 | char buffer[24]; 73 | const char* text = Details::IntegerToString(-1234567890123456789LL, buffer, FL_ARRAY_COUNTOF(buffer), false); 74 | EXPECT_EQ(strlen(text), 20); 75 | EXPECT_STREQ(text, "-1234567890123456789"); 76 | 77 | text = Details::IntegerToStringMoved(-1234567890123456789LL, buffer, FL_ARRAY_COUNTOF(buffer), false); 78 | 79 | EXPECT_EQ(reinterpret_cast(text),reinterpret_cast(buffer)); 80 | EXPECT_EQ(strlen(text), 20); 81 | EXPECT_STREQ(text, "-1234567890123456789"); 82 | } 83 | 84 | TEST(Algorithm, TestUInt64ToString) 85 | { 86 | char buffer[24]; 87 | const char* text = Details::IntegerToString(1234567890123456789ULL, buffer, FL_ARRAY_COUNTOF(buffer), false); 88 | EXPECT_EQ(strlen(text), 19); 89 | EXPECT_STREQ(text, "1234567890123456789"); 90 | 91 | const char* MovedText = Details::IntegerToStringMoved(1234567890123456789ULL, buffer, FL_ARRAY_COUNTOF(buffer), false); 92 | EXPECT_EQ(reinterpret_cast(MovedText), reinterpret_cast(buffer)); 93 | EXPECT_EQ(strlen(MovedText), 19); 94 | EXPECT_STREQ(MovedText, "1234567890123456789"); 95 | } 96 | 97 | TEST(Algorithm, TestUInt64ToStringW) 98 | { 99 | wchar_t buffer[24]; 100 | const wchar_t* text = Details::IntegerToString(1234567890123456789ULL, buffer, FL_ARRAY_COUNTOF(buffer), false); 101 | EXPECT_EQ(wcslen(text), 19); 102 | EXPECT_STREQ(text, L"1234567890123456789"); 103 | 104 | const wchar_t* MovedText = Details::IntegerToStringMoved(1234567890123456789ULL, buffer, FL_ARRAY_COUNTOF(buffer), false); 105 | EXPECT_EQ(reinterpret_cast(MovedText), reinterpret_cast(buffer)); 106 | EXPECT_EQ(wcslen(MovedText), 19); 107 | EXPECT_STREQ(MovedText, L"1234567890123456789"); 108 | } 109 | 110 | TEST(Algorithm, TestDoubleToString) 111 | { 112 | char buffer[32]; 113 | const char* const Result = Details::DoubleToString(123.456, buffer, FL_ARRAY_COUNTOF(buffer), 3); 114 | const size_t length = Details::CalculateConvertedStringLength(Result, buffer); 115 | EXPECT_EQ(length, 7); 116 | EXPECT_STREQ(Result, "123.456"); 117 | 118 | const char* const MovedResult = Details::DoubleToStringMoved(123.456, buffer, FL_ARRAY_COUNTOF(buffer), 3); 119 | const size_t MovedLength = strlen(MovedResult); 120 | EXPECT_EQ(MovedLength, 7); 121 | EXPECT_EQ(reinterpret_cast(MovedResult), reinterpret_cast(buffer)); 122 | EXPECT_STREQ(MovedResult, "123.456"); 123 | } 124 | 125 | TEST(Algorithm, TestDoubleToStringW) 126 | { 127 | wchar_t buffer[32]; 128 | const wchar_t* const Result = Details::DoubleToString(123.456, buffer, FL_ARRAY_COUNTOF(buffer), 3); 129 | const size_t length = Details::CalculateConvertedStringLength(Result, buffer); 130 | EXPECT_EQ(length, 7); 131 | EXPECT_STREQ(Result, L"123.456"); 132 | 133 | const wchar_t* const MovedResult = Details::DoubleToStringMoved(123.456, buffer, FL_ARRAY_COUNTOF(buffer), 3); 134 | const size_t MovedLength = wcslen(MovedResult); 135 | EXPECT_EQ(MovedLength, 7); 136 | EXPECT_EQ(reinterpret_cast(MovedResult), reinterpret_cast(buffer)); 137 | EXPECT_STREQ(MovedResult, L"123.456"); 138 | } 139 | 140 | TEST(Algorithm, TestDoubleToStringGreaterThanThresMax) 141 | { 142 | constexpr static double ThresMax = (double)(0x7FFFFFFF) + 0.123456; // NOLINT 143 | char buffer[64]; 144 | const char* const Result = Details::DoubleToString(ThresMax, buffer, FL_ARRAY_COUNTOF(buffer), 3); 145 | const size_t length = Details::CalculateConvertedStringLength(Result, buffer); 146 | 147 | // this is not stable on all platforms, so... 148 | //EXPECT_EQ(length, 12); 149 | //EXPECT_STREQ(Result, "2.147484e+09"); 150 | EXPECT_TRUE(strstr(Result, "e+") != nullptr); 151 | 152 | const char* const MovedResult = Details::DoubleToStringMoved(ThresMax, buffer, FL_ARRAY_COUNTOF(buffer), 3); 153 | const size_t MovedLength = strlen(MovedResult); 154 | EXPECT_TRUE(strstr(MovedResult, "e+") != nullptr); 155 | EXPECT_EQ(reinterpret_cast(MovedResult), reinterpret_cast(buffer)); 156 | } 157 | 158 | TEST(TCharTraits, StringPrintf) 159 | { 160 | char buffer[100]; 161 | TCharTraits::StringPrintf(buffer, "%s %d", "test", 123); 162 | EXPECT_STREQ(buffer, "test 123"); 163 | 164 | wchar_t wbuffer[100]; 165 | 166 | #if FL_COMPILER_GCC 167 | const wchar_t* wFmt = L"%S %d"; 168 | #else 169 | const wchar_t* wFmt = L"%s %d"; 170 | #endif 171 | 172 | TCharTraits::StringPrintf(wbuffer, wFmt, L"test", 123); 173 | EXPECT_EQ(std::wcscmp(wbuffer, L"test 123"), 0); 174 | } 175 | 176 | TEST(TCharTraits, CompareN) 177 | { 178 | EXPECT_EQ(TCharTraits::CompareN("hello111", "hello222", 5), 0); 179 | EXPECT_EQ(TCharTraits::CompareN(L"hello333", L"hello444", 5), 0); 180 | } 181 | 182 | TEST(TCharTraits, iCompare) 183 | { 184 | EXPECT_EQ(TCharTraits::iCompare("hello", "HELLO"), 0); 185 | EXPECT_EQ(TCharTraits::iCompare(L"hello", L"HELLO"), 0); 186 | } 187 | 188 | TEST(TCharTraits, iCompareN) 189 | { 190 | EXPECT_EQ(TCharTraits::iCompareN("hello111", "HELLO2222", 5), 0); 191 | EXPECT_EQ(TCharTraits::iCompareN(L"hello4444", L"HELLO555", 5), 0); 192 | } 193 | 194 | TEST(TCharTraits, Find) 195 | { 196 | EXPECT_STREQ(TCharTraits::Find("hello world", "world"), "world"); 197 | EXPECT_EQ(std::wcscmp(TCharTraits::Find(L"hello world", L"world"), L"world"), 0); 198 | } 199 | 200 | TEST(TCharTraits, iFind) 201 | { 202 | EXPECT_STREQ(TCharTraits::iFind("hello world", "WORLD"), "world"); 203 | EXPECT_EQ(std::wcscmp(TCharTraits::iFind(L"hello world", L"WORLD"), L"world"), 0); 204 | } 205 | 206 | TEST(TCharTraits, rFind) 207 | { 208 | EXPECT_STREQ(TCharTraits::rFind("hello world", 'o'), "orld"); 209 | EXPECT_EQ(std::wcscmp(TCharTraits::rFind(L"hello world", L'o'), L"orld"), 0); 210 | } 211 | 212 | TEST(TCharTraits, rFindAny) 213 | { 214 | EXPECT_STREQ(TCharTraits::rFindAny("hello world", "od"), "d"); 215 | EXPECT_EQ(std::wcscmp(TCharTraits::rFindAny(L"hello world", L"od"), L"d"), 0); 216 | } 217 | 218 | TEST(TCharTraits, rFindNotOfAny) 219 | { 220 | EXPECT_STREQ(TCharTraits::rFindNotOfAny("hello world", "od"), "ld"); 221 | EXPECT_EQ(std::wcscmp(TCharTraits::rFindNotOfAny(L"hello world", L"od"), L"ld"), 0); 222 | } 223 | 224 | TEST(TCharTraits, Fill) 225 | { 226 | char buffer[10] = {0}; 227 | TCharTraits::Fill(buffer, 'a', 5); 228 | EXPECT_STREQ(buffer, "aaaaa"); 229 | 230 | wchar_t wbuffer[10] = {0}; 231 | TCharTraits::Fill(wbuffer, L'a', 5); 232 | EXPECT_EQ(std::wcscmp(wbuffer, L"aaaaa"), 0); 233 | } 234 | -------------------------------------------------------------------------------- /PerformanceTests/Sources/PerformanceTestsMain.cpp: -------------------------------------------------------------------------------- 1 | // ReSharper disable CppClangTidyPerformanceInefficientVectorOperation 2 | // ReSharper disable CppLocalVariableMayBeConst 3 | #include 4 | 5 | #if !FL_COMPILER_IS_GREATER_THAN_CXX11 6 | #error "Need C++ 11" 7 | #endif 8 | 9 | #pragma message(FL_CXX_STANDARD) 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | using namespace Formatting; 19 | using Clock = std::chrono::high_resolution_clock; 20 | 21 | #define TEST_FORMAT_TO 22 | #define TEST_FORMAT_TO_MACROS 23 | //#define TEST_E_FORMAT 24 | 25 | void test_formatting_standard_library(int iterations) 26 | { 27 | std::string str; 28 | for (int i = 0; i < iterations; ++i) 29 | { 30 | StandardLibrary::FormatTo(str, "{0} {1} {2} {3} {4} {5}", 123, "hello", 1.23, 456, "world", 4.56); 31 | StandardLibrary::FormatTo(str, "{0} - {1} - {2} - {3} - {4} - {5}", 789, "foo", 7.89, 101, "bar", 10.11); 32 | StandardLibrary::FormatTo(str, "{0} + {1} + {2} + {3} + {4} + {5}", 112, "baz", 11.12, 131, "qux", 13.14); 33 | StandardLibrary::FormatTo(str, "{0} * {1} * {2} * {3} * {4} * {5}", 215, "alpha", 21.52, 314, "beta", 31.45); 34 | StandardLibrary::FormatTo(str, "{0} / {1} / {2} / {3} / {4} / {5}", 516, "gamma", 51.67, 617, "delta", 61.78); 35 | StandardLibrary::FormatTo(str, "{0} ^ {1} ^ {2} ^ {3} ^ {4} ^ {5}", 718, "epsilon", 71.89, 819, "zeta", 81.90); 36 | StandardLibrary::FormatTo(str, "{0} % {1} % {2} % {3} % {4} % {5}", 920, "eta", 92.01, 101, "theta", 10.12); 37 | StandardLibrary::FormatTo(str, "{0} & {1} & {2} & {3} & {4} & {5}", 122, "iota", 12.23, 233, "kappa", 23.34); 38 | StandardLibrary::FormatTo(str, "{0} | {1} | {2} | {3} | {4} | {5}", 344, "lambda", 34.45, 455, "mu", 45.56); 39 | StandardLibrary::FormatTo(str, "{0} ~ {1} ~ {2} ~ {3} ~ {4} ~ {5}", 566, "nu", 56.67, 677, "xi", 67.78); 40 | 41 | // Padding and alignment 42 | StandardLibrary::FormatTo(str, "{0,5}", 123); 43 | StandardLibrary::FormatTo(str, "{0,-5}", 123); 44 | StandardLibrary::FormatTo(str, "{0,6}", -123); 45 | StandardLibrary::FormatTo(str, "{0,-6}", -123); 46 | StandardLibrary::FormatTo(str, "{0}", "\n\t\""); 47 | 48 | #ifdef TEST_E_FORMAT 49 | StandardLibrary::FormatTo(str, "{0:e}", 123.456); 50 | StandardLibrary::FormatTo(str, "{0:E}", 123.456); 51 | StandardLibrary::FormatTo(str, "{0:e3}", 123.456); 52 | StandardLibrary::FormatTo(str, "{0:E3}", 123.456); 53 | #endif 54 | 55 | StandardLibrary::FormatTo(str, "{0:x}", 123); 56 | StandardLibrary::FormatTo(str, "{0:X}", 123); 57 | StandardLibrary::FormatTo(str, "{0:x8}", 123); 58 | StandardLibrary::FormatTo(str, "{0:X8}", 123); 59 | StandardLibrary::FormatTo(str, "{0,5}", "hi"); 60 | StandardLibrary::FormatTo(str, "{0,-5}", "hi"); 61 | } 62 | } 63 | 64 | void test_formatting_standard_library_macros(int iterations) 65 | { 66 | std::string str; 67 | for (int i = 0; i < iterations; ++i) 68 | { 69 | FL_STD_FORMAT_TO(str, "{0} {1} {2} {3} {4} {5}", 123, "hello", 1.23, 456, "world", 4.56); 70 | FL_STD_FORMAT_TO(str, "{0} - {1} - {2} - {3} - {4} - {5}", 789, "foo", 7.89, 101, "bar", 10.11); 71 | FL_STD_FORMAT_TO(str, "{0} + {1} + {2} + {3} + {4} + {5}", 112, "baz", 11.12, 131, "qux", 13.14); 72 | FL_STD_FORMAT_TO(str, "{0} * {1} * {2} * {3} * {4} * {5}", 215, "alpha", 21.52, 314, "beta", 31.45); 73 | FL_STD_FORMAT_TO(str, "{0} / {1} / {2} / {3} / {4} / {5}", 516, "gamma", 51.67, 617, "delta", 61.78); 74 | FL_STD_FORMAT_TO(str, "{0} ^ {1} ^ {2} ^ {3} ^ {4} ^ {5}", 718, "epsilon", 71.89, 819, "zeta", 81.90); 75 | FL_STD_FORMAT_TO(str, "{0} % {1} % {2} % {3} % {4} % {5}", 920, "eta", 92.01, 101, "theta", 10.12); 76 | FL_STD_FORMAT_TO(str, "{0} & {1} & {2} & {3} & {4} & {5}", 122, "iota", 12.23, 233, "kappa", 23.34); 77 | FL_STD_FORMAT_TO(str, "{0} | {1} | {2} | {3} | {4} | {5}", 344, "lambda", 34.45, 455, "mu", 45.56); 78 | FL_STD_FORMAT_TO(str, "{0} ~ {1} ~ {2} ~ {3} ~ {4} ~ {5}", 566, "nu", 56.67, 677, "xi", 67.78); 79 | 80 | // Padding and alignment 81 | FL_STD_FORMAT_TO(str, "{0,5}", 123); 82 | FL_STD_FORMAT_TO(str, "{0,-5}", 123); 83 | FL_STD_FORMAT_TO(str, "{0,6}", -123); 84 | FL_STD_FORMAT_TO(str, "{0,-6}", -123); 85 | FL_STD_FORMAT_TO(str, "{0}", "\n\t\""); 86 | 87 | #ifdef TEST_E_FORMAT 88 | FL_STD_FORMAT_TO(str, "{0:e}", 123.456); 89 | FL_STD_FORMAT_TO(str, "{0:E}", 123.456); 90 | FL_STD_FORMAT_TO(str, "{0:e3}", 123.456); 91 | FL_STD_FORMAT_TO(str, "{0:E3}", 123.456); 92 | #endif 93 | 94 | FL_STD_FORMAT_TO(str, "{0:x}", 123); 95 | FL_STD_FORMAT_TO(str, "{0:X}", 123); 96 | FL_STD_FORMAT_TO(str, "{0:x8}", 123); 97 | FL_STD_FORMAT_TO(str, "{0:X8}", 123); 98 | FL_STD_FORMAT_TO(str, "{0,5}", "hi"); 99 | FL_STD_FORMAT_TO(str, "{0,-5}", "hi"); 100 | } 101 | } 102 | 103 | void run_single_thread_tests(int iterations) 104 | { 105 | #ifdef TEST_FORMAT_TO 106 | auto start = Clock::now(); 107 | test_formatting_standard_library(iterations); 108 | auto end = Clock::now(); 109 | double standard_library_time = std::chrono::duration(end - start).count(); 110 | #endif 111 | 112 | #ifdef TEST_FORMAT_TO_MACROS 113 | auto start_macros = Clock::now(); 114 | test_formatting_standard_library_macros(iterations); 115 | auto end_macros = Clock::now(); 116 | double standard_library_macros_time = std::chrono::duration(end_macros - start_macros).count(); 117 | #endif 118 | 119 | std::cout << std::fixed << std::setprecision(6); 120 | std::cout << "| | "; 121 | #ifdef TEST_FORMAT_TO 122 | std::cout << standard_library_time << " | "; 123 | #else 124 | std::cout << "N/A | "; 125 | #endif 126 | #ifdef TEST_FORMAT_TO_MACROS 127 | std::cout << standard_library_macros_time << " |\n"; 128 | #else 129 | std::cout << "N/A |\n"; 130 | #endif 131 | } 132 | 133 | void run_multi_thread_tests(int iterations, int thread_count) 134 | { 135 | typedef void (*TestFunctionType)(int); 136 | 137 | auto run_test = [iterations](TestFunctionType test_function) 138 | { 139 | for (int i = 0; i < iterations; ++i) 140 | { 141 | test_function(1); 142 | } 143 | }; 144 | 145 | std::vector threads; 146 | 147 | #ifdef TEST_FORMAT_TO 148 | // StandardLibrary::Format 149 | auto start = Clock::now(); 150 | for (int i = 0; i < thread_count; ++i) 151 | { 152 | threads.emplace_back(run_test, test_formatting_standard_library); 153 | } 154 | for (auto& thread : threads) 155 | { 156 | thread.join(); 157 | } 158 | auto end = Clock::now(); 159 | double standard_library_time = std::chrono::duration(end - start).count(); 160 | threads.clear(); 161 | #endif 162 | 163 | #ifdef TEST_FORMAT_TO_MACROS 164 | // StandardLibrary::Format macros 165 | auto start_macros = Clock::now(); 166 | for (int i = 0; i < thread_count; ++i) 167 | { 168 | threads.emplace_back(run_test, test_formatting_standard_library_macros); 169 | } 170 | for (auto& thread : threads) 171 | { 172 | thread.join(); 173 | } 174 | auto end_macros = Clock::now(); 175 | double standard_library_macros_time = std::chrono::duration(end_macros - start_macros).count(); 176 | threads.clear(); 177 | #endif 178 | 179 | std::cout << std::fixed << std::setprecision(6); 180 | std::cout << "| | "; 181 | #ifdef TEST_FORMAT_TO 182 | std::cout << standard_library_time << " | "; 183 | #else 184 | std::cout << "N/A | "; 185 | #endif 186 | #ifdef TEST_FORMAT_TO_MACROS 187 | std::cout << standard_library_macros_time << " |\n"; 188 | #else 189 | std::cout << "N/A |\n"; 190 | #endif 191 | } 192 | 193 | int main() 194 | { 195 | std::cout << "C++ Version:" << FL_CXX_STANDARD << std::endl; // NOLINT(performance-avoid-endl) 196 | 197 | #if FL_DEBUG 198 | FL_CONSTEXPR11 const int iterations = 50000; 199 | #else 200 | FL_CONSTEXPR11 const int iterations = 500000; 201 | #endif 202 | 203 | FL_CONSTEXPR11 const int thread_count = 4; 204 | 205 | std::cout << "Single-threaded tests:\n"; 206 | std::cout << "| Test Type | StandardLibrary::FormatTo (s) | StandardLibrary::FormatTo Macros (s) |\n"; 207 | std::cout << "|-----------|-------------------------------|--------------------------------------|\n"; 208 | run_single_thread_tests(iterations); 209 | 210 | std::cout << "\nMulti-threaded(" << thread_count << " threads) tests:\n"; 211 | std::cout << "| Test Type | StandardLibrary::FormatTo (s) | StandardLibrary::FormatTo Macros (s) |\n"; 212 | std::cout << "|-----------|-------------------------------|--------------------------------------|\n"; 213 | run_multi_thread_tests(iterations, thread_count); 214 | } 215 | -------------------------------------------------------------------------------- /Format/Common/Build.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2024 CPPStringFormatting 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | Project URL: https://github.com/bodong1987/CPPStringFormatting 25 | */ 26 | #pragma once 27 | 28 | // ReSharper disable once CommentTypo 29 | // NOLINTBEGIN 30 | #if (defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WINDOWS) || defined(_WIN64)) 31 | #define FL_PLATFORM_WINDOWS 1 32 | #else 33 | #define FL_PLATFORM_WINDOWS 0 34 | #endif 35 | 36 | // on apple platform ??? macos or ios ... 37 | #if defined(__APPLE__) || defined(APPLE) || defined(_APPLE_) || defined(PLATFORM_APPLE) 38 | #define FL_PLATFORM_APPLE 1 39 | #include 40 | #else 41 | #define FL_PLATFORM_APPLE 0 42 | #endif 43 | 44 | #if defined(__OBJC__) 45 | #define FL_OBJECTIVE_C 1 46 | #else 47 | #define FL_OBJECTIVE_C 0 48 | #endif 49 | 50 | #ifdef _MSC_VER 51 | #define FL_COMPILER_MSVC 1 52 | #else 53 | #define FL_COMPILER_MSVC 0 54 | #endif 55 | 56 | #ifdef __GNUC__ 57 | #define FL_COMPILER_GCC 1 58 | #else 59 | #define FL_COMPILER_GCC 0 60 | #endif // __GNUC__ // NOLINT 61 | 62 | // ios or ios simulator 63 | #if defined(TARGET_OS_IOS) || defined(TARGET_IPHONE_SIMULATOR) 64 | #define FL_PLATFORM_IOS 1 65 | #else 66 | #define FL_PLATFORM_IOS 0 67 | #endif 68 | 69 | // iphone simulator ??? 70 | #if defined(TARGET_IPHONE_SIMULATOR) 71 | #define FL_PLATFORM_IPHONE_SIMULATOR 1 72 | #else 73 | #define FL_PLATFORM_IPHONE_SIMULATOR 0 74 | #endif 75 | 76 | #if defined(__APPLE__) || defined(APPLE) || defined(_APPLE_) 77 | #define FL_PLATFORM_MACOS 1 78 | #else 79 | #define FL_PLATFORM_MACOS 0 80 | #endif 81 | 82 | #if defined(ANDROID) || defined(__ANDROID__) 83 | #define FL_PLATFORM_ANDROID 1 84 | #else 85 | #define FL_PLATFORM_ANDROID 0 86 | #endif 87 | 88 | 89 | #if defined(_WIN64) || defined(_X64) || defined(WIN64) || defined( __LP64__ ) 90 | #define FL_PLATFORM_X64 1 91 | #elif defined(WIN32) || defined(_WIN32) 92 | #define FL_PLATFORM_X64 0 93 | #endif 94 | 95 | #if defined(__arm__) || defined(__thumb__) || defined(_ARM) || defined(_M_ARM) || defined(_M_ARMT) || defined(__arm) 96 | #define FL_PLATFORM_ARM 1 97 | #else 98 | #define FL_PLATFORM_ARM 0 99 | #endif 100 | 101 | #if defined(DEBUG)||defined(_DEBUG) 102 | #define FL_DEBUG 1 103 | #else 104 | #define FL_DEBUG 0 105 | #endif 106 | 107 | #if defined(UNICODE)||defined(_UNICODE) 108 | #define FL_UNICODE 1 109 | #else 110 | #define FL_UNICODE 0 111 | #endif 112 | 113 | // is C++/CLI enabled??? 114 | #if FL_PLATFORM_WINDOWS && FL_COMPILER_MSVC && (defined(_MANAGED)||defined(_M_CEE)) 115 | #define FL_COMPILER_MSVC_WITH_CPP_CLI 1 116 | #else 117 | #define FL_COMPILER_MSVC_WITH_CPP_CLI 0 118 | #endif 119 | 120 | // disable warning for C++/CLI 121 | #if FL_COMPILER_MSVC_WITH_CPP_CLI 122 | #pragma warning(disable : 4793) 123 | #endif 124 | 125 | #if FL_COMPILER_MSVC 126 | // @reference https://www.cnblogs.com/bodong/p/18293350 127 | // Each version of Visual Studio does not have complete support for the C++ standard, 128 | // so simply judging the feature support of the C++ standard through these version numbers is incomplete. 129 | #if _MSC_FULL_VER <= 150030729 // before Visual Studio 2008 sp1, set C++ 98 130 | #define _MSVC_LANG 199711 131 | #elif _MSC_FULL_VER <= 180021114 // before Visual Studio 2013 Nobemver CTP, set C++ 11 132 | #define _MSVC_LANG 201103 133 | #elif _MSC_FULL_VER <= 190023918 // before Visual Studio 2015 Update 2, set C++ 14 134 | #define _MSVC_LANG 201402 135 | #endif // after Visual Studio 2015 Update 3, _MSVC_LANG exists 136 | 137 | #define FL_COMPILER_LANG_VERSION _MSVC_LANG 138 | #elif defined(__cplusplus) 139 | #define FL_COMPILER_LANG_VERSION __cplusplus 140 | #else // set C++ 98 as default 141 | #define FL_COMPILER_LANG_VERSION 199711 142 | #pragma message("No valid C++ standard identification flag found, default to C++98 standard") 143 | #endif 144 | 145 | // is greater than ? 146 | // Checks whether the current C++ standard is a later version 147 | #define FL_COMPILER_IS_GREATER_THAN_CXX23 (FL_COMPILER_LANG_VERSION >= 202101) 148 | #define FL_COMPILER_IS_GREATER_THAN_CXX20 (FL_COMPILER_LANG_VERSION >= 202002) 149 | #define FL_COMPILER_IS_GREATER_THAN_CXX17 (FL_COMPILER_LANG_VERSION >= 201703) 150 | #define FL_COMPILER_IS_GREATER_THAN_CXX14 (FL_COMPILER_LANG_VERSION >= 201402) 151 | #define FL_COMPILER_IS_GREATER_THAN_CXX11 (FL_COMPILER_LANG_VERSION >= 201103) 152 | #define FL_COMPILER_IS_GREATER_TAHN_CXX98 (FL_COMPILER_LANG_VERSION >= 199711) 153 | 154 | // is C++ xx ? 155 | // Check whether the current C++ standard specifies a certain version 156 | #define FL_COMPILER_IS_CXX23 (FL_COMPILER_LANG_VERSION >= 202101) 157 | #define FL_COMPILER_IS_CXX20 (FL_COMPILER_LANG_VERSION >= 202002 && FL_COMPILER_LANG_VERSION < 202101) 158 | #define FL_COMPILER_IS_CXX17 (FL_COMPILER_LANG_VERSION >= 201703 && FL_COMPILER_LANG_VERSION < 202002) 159 | #define FL_COMPILER_IS_CXX14 (FL_COMPILER_LANG_VERSION >= 201402 && FL_COMPILER_LANG_VERSION < 201703) 160 | #define FL_COMPILER_IS_CXX11 (FL_COMPILER_LANG_VERSION >= 201103 && FL_COMPILER_LANG_VERSION < 201402) 161 | #define FL_COMPILER_IS_CXX98 (FL_COMPILER_LANG_VERSION >= 199711 && FL_COMPILER_LANG_VERSION < 201103) 162 | 163 | #if FL_COMPILER_IS_GREATER_THAN_CXX11 164 | #define FL_STATIC_ASSERT( exp, message ) static_assert( exp, message ) 165 | #define FL_MOVE_SEMANTIC( exp ) std::move(exp) 166 | #else 167 | #define FL_STATIC_ASSERT( exp, message ) \ 168 | typedef unsigned char _Static_Assert_Error_ ## __LINE__ [ exp?1:-1 ] 169 | #define FL_MOVE_SEMANTIC( exp ) exp 170 | #endif 171 | 172 | #ifndef FL_COMPILER_SUPPORT_THREAD_LOCAL 173 | 174 | #if FL_COMPILER_IS_GREATER_THAN_CXX11 || FL_PLATFORM_MACOS || FL_COMPILER_MSVC 175 | #define FL_COMPILER_SUPPORT_THREAD_LOCAL 1 176 | #else 177 | #define FL_COMPILER_SUPPORT_THREAD_LOCAL 0 178 | #endif 179 | 180 | #endif 181 | 182 | #ifndef FL_WITH_MULTITHREAD_SUPPORT 183 | #define FL_WITH_MULTITHREAD_SUPPORT 1 184 | #endif 185 | 186 | #if FL_COMPILER_SUPPORT_THREAD_LOCAL && FL_WITH_MULTITHREAD_SUPPORT 187 | 188 | #define FL_WITH_THREAD_LOCAL 1 189 | 190 | #if FL_PLATFORM_MACOS 191 | #define FL_THREAD_LOCAL __thread 192 | #elif FL_COMPILER_MSVC 193 | #define FL_THREAD_LOCAL __declspec(thread) 194 | #else 195 | #define FL_THREAD_LOCAL thread_local 196 | #endif 197 | 198 | #else 199 | 200 | #define FL_THREAD_LOCAL 201 | #define FL_WITH_THREAD_LOCAL 0 202 | 203 | #endif 204 | 205 | #ifndef FL_ARRAY_COUNTOF 206 | #define FL_ARRAY_COUNTOF( Array ) (sizeof(Array)/sizeof(Array[0])) /*NOLINT*/ 207 | #endif 208 | 209 | #if FL_COMPILER_IS_GREATER_THAN_CXX11 210 | #include 211 | #define FL_CONSTEXPR11 constexpr 212 | #else 213 | typedef char int8_t; 214 | typedef unsigned char uint8_t; 215 | typedef short int16_t; 216 | typedef unsigned short uint16_t; 217 | typedef int int32_t; 218 | typedef unsigned int uint32_t; 219 | typedef int long long int64_t; 220 | typedef unsigned int long long uint64_t; 221 | 222 | #ifndef nullptr 223 | #define nullptr NULL 224 | #endif 225 | 226 | #ifndef constexpr 227 | #define constexpr const 228 | #endif 229 | 230 | #define FL_CONSTEXPR11 231 | #endif 232 | 233 | #if FL_COMPILER_IS_GREATER_THAN_CXX14 234 | #define FL_CONSTEXPR14 constexpr 235 | #else 236 | #define FL_CONSTEXPR14 237 | #endif 238 | 239 | #if FL_COMPILER_IS_GREATER_THAN_CXX17 240 | #define FL_CONSTEXPR17 constexpr 241 | #else 242 | #define FL_CONSTEXPR17 243 | #endif 244 | 245 | #if FL_COMPILER_IS_GREATER_THAN_CXX20 246 | #define FL_CONSTEXPR20 constexpr 247 | #define FL_CONSTEVAL consteval 248 | #else 249 | #define FL_CONSTEXPR20 250 | #define FL_CONSTEVAL 251 | #endif 252 | 253 | #if FL_COMPILER_IS_GREATER_THAN_CXX23 254 | #define FL_CXX_STANDARD "C++ 23" 255 | #elif FL_COMPILER_IS_CXX20 256 | #define FL_CXX_STANDARD "C++ 20" 257 | #elif FL_COMPILER_IS_CXX17 258 | #define FL_CXX_STANDARD "C++ 17" 259 | #elif FL_COMPILER_IS_CXX14 260 | #define FL_CXX_STANDARD "C++ 14" 261 | #elif FL_COMPILER_IS_CXX11 262 | #define FL_CXX_STANDARD "C++ 11" 263 | #elif FL_COMPILER_IS_CXX98 264 | #define FL_CXX_STANDARD "C++ 98" 265 | #else 266 | #error "can't find C++ compiler version." 267 | #endif 268 | 269 | #if FL_COMPILER_IS_GREATER_THAN_CXX17 270 | #define FL_NO_DISCARD [[nodiscard]] 271 | #else 272 | #define FL_NO_DISCARD 273 | #endif 274 | 275 | // unused parameter 276 | #if FL_COMPILER_MSVC 277 | #define FL_UNREFERENCED_PARAMETER(P) (void)(P) 278 | #else 279 | #define FL_UNREFERENCED_PARAMETER(p) (void)(p) 280 | #endif 281 | 282 | // ReSharper disable once CommentTypo 283 | // NOLINTEND 284 | -------------------------------------------------------------------------------- /UnitTests/Sources/UnitTests_Common.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #if FL_PLATFORM_WINDOWS 4 | #pragma warning(push) 5 | #pragma warning(disable:4616) 6 | #endif 7 | #include 8 | #if FL_PLATFORM_WINDOWS 9 | #pragma warning(pop) 10 | #endif 11 | 12 | #include 13 | 14 | using namespace Formatting; 15 | 16 | 17 | TEST(TAutoArray, DefaultConstructor) 18 | { 19 | TAutoArray array; 20 | EXPECT_EQ(array.GetLength(), 0); 21 | } 22 | 23 | TEST(TAutoArray, AddItem) 24 | { 25 | TAutoArray array; 26 | for (int i = 0; i < 10; ++i) 27 | { 28 | array.AddItem(i); 29 | } 30 | EXPECT_EQ(array.GetLength(), 10); 31 | for (int i = 0; i < 10; ++i) 32 | { 33 | EXPECT_EQ(array[i], i); 34 | } 35 | } 36 | 37 | TEST(TAutoArray, CopyConstructor) 38 | { 39 | TAutoArray array1; 40 | for (int i = 0; i < 10; ++i) 41 | { 42 | array1.AddItem(i); 43 | } 44 | TAutoArray array2(array1); 45 | EXPECT_EQ(array2.GetLength(), 10); 46 | for (int i = 0; i < 10; ++i) 47 | { 48 | EXPECT_EQ(array2[i], i); 49 | } 50 | } 51 | 52 | #if FL_COMPILER_IS_GREATER_THAN_CXX11 53 | TEST(TAutoArray, MoveConstructor) 54 | { 55 | TAutoArray array1; 56 | for (int i = 0; i < 10; ++i) 57 | { 58 | array1.AddItem(i); 59 | } 60 | TAutoArray array2(FL_MOVE_SEMANTIC(array1)); 61 | EXPECT_EQ(array2.GetLength(), 10); 62 | for (int i = 0; i < 10; ++i) 63 | { 64 | EXPECT_EQ(array2[i], i); 65 | } 66 | EXPECT_EQ(array1.GetLength(), 0); 67 | } 68 | #endif 69 | 70 | TEST(TAutoArray, ExpandHeapSpace) 71 | { 72 | TAutoArray array; 73 | for (int i = 0; i < 1000; ++i) 74 | { 75 | array.AddItem(i); 76 | } 77 | EXPECT_EQ(array.GetLength(), 1000); 78 | for (int i = 0; i < 1000; ++i) 79 | { 80 | EXPECT_EQ(array[i], i); 81 | } 82 | } 83 | 84 | TEST(TAutoArray, AddItems) 85 | { 86 | TAutoArray array; 87 | constexpr int itemCount = 1000; 88 | int items[itemCount]; 89 | 90 | // Initialize items array 91 | for (int i = 0; i < itemCount; ++i) 92 | { 93 | items[i] = i; 94 | } 95 | 96 | // Add range of items 97 | array.AddItems(items, itemCount); 98 | 99 | // Verify the length of the array 100 | EXPECT_EQ(array.GetLength(), itemCount); 101 | 102 | // Verify the contents of the array 103 | for (int i = 0; i < itemCount; ++i) 104 | { 105 | EXPECT_EQ(array[i], items[i]); 106 | } 107 | } 108 | 109 | TEST(TAutoArray, AddItemsMoreComplex) 110 | { 111 | TAutoArray array; 112 | constexpr int itemCount0 = 128; 113 | constexpr int itemCount1 = 10000; 114 | int items0[itemCount0]; 115 | int items1[itemCount1]; 116 | 117 | // Initialize items array 118 | // ReSharper disable once CppUseStdSize 119 | for (int i = 0; i < FL_ARRAY_COUNTOF(items0); ++i) // NOLINT(clang-diagnostic-sign-compare) 120 | { 121 | items0[i] = i; 122 | } 123 | 124 | // ReSharper disable once CppUseStdSize 125 | for (int i = 0; i < FL_ARRAY_COUNTOF(items1); ++i) // NOLINT(clang-diagnostic-sign-compare) 126 | { 127 | items1[i] = i; 128 | } 129 | 130 | // Add range of items 131 | array.AddItems(items0, itemCount0); 132 | array.AddItems(items1, itemCount1); 133 | 134 | // Verify the length of the array 135 | EXPECT_EQ(array.GetLength(), itemCount0 + itemCount1); 136 | 137 | // Verify the contents of the array 138 | for (int i = 0; i < itemCount0; ++i) 139 | { 140 | EXPECT_EQ(array[i], items0[i]); 141 | } 142 | 143 | for (int i = itemCount0; i < itemCount0 + itemCount1; ++i) 144 | { 145 | EXPECT_EQ(array[i], items1[i - itemCount0]); 146 | } 147 | } 148 | 149 | TEST(TAutoArray, Shrink) 150 | { 151 | TAutoArray array; 152 | for (int i = 0; i < 100; ++i) 153 | { 154 | array.AddItem(i); 155 | } 156 | EXPECT_EQ(array.GetLength(), 100); 157 | size_t initialAllocatedCount = array.GetAllocatedCount(); 158 | EXPECT_GT(initialAllocatedCount, 100); 159 | 160 | array.Shrink(); 161 | EXPECT_EQ(array.GetLength(), 100); 162 | EXPECT_EQ(array.GetAllocatedCount(), 100); 163 | 164 | for (int i = 0; i < 100; ++i) 165 | { 166 | EXPECT_EQ(array[i], i); 167 | } 168 | } 169 | 170 | TEST(TAutoArray, BeginEnd) 171 | { 172 | TAutoArray array; 173 | for (int i = 0; i < 100; ++i) 174 | { 175 | array.AddItem(i); 176 | } 177 | 178 | int index = 0; 179 | for (TAutoArray::iterator it = array.begin(); it != array.end(); ++it) 180 | { 181 | EXPECT_EQ(*it, index); 182 | ++index; 183 | } 184 | EXPECT_EQ(index, 100); 185 | } 186 | 187 | TEST(TAutoArray, RBeginREnd) 188 | { 189 | TAutoArray array; 190 | for (int i = 0; i < 100; ++i) 191 | { 192 | array.AddItem(i); 193 | } 194 | 195 | int index = 99; 196 | for (TAutoArray::reverse_iterator it = array.rbegin(); it != array.rend(); ++it) 197 | { 198 | EXPECT_EQ(*it, index); 199 | --index; 200 | } 201 | EXPECT_EQ(index, -1); 202 | } 203 | 204 | #if FL_COMPILER_IS_GREATER_THAN_CXX11 205 | TEST(TAutoArray, RangeBasedFor) 206 | { 207 | TAutoArray array; 208 | for (int i = 0; i < 100; ++i) 209 | { 210 | array.AddItem(i); 211 | } 212 | 213 | int index = 0; 214 | for (const auto& item : array) 215 | { 216 | EXPECT_EQ(item, index); 217 | ++index; 218 | } 219 | EXPECT_EQ(index, 100); 220 | } 221 | #endif 222 | 223 | TEST(TAutoString, DefaultConstructor) 224 | { 225 | TAutoString str; 226 | EXPECT_STREQ(str.CStr(), ""); 227 | } 228 | 229 | TEST(TAutoString, ConstructorWithCString) 230 | { 231 | TAutoString str("Hello"); 232 | EXPECT_STREQ(str.CStr(), "Hello"); 233 | } 234 | 235 | TEST(TAutoString, AddChar) 236 | { 237 | TAutoString str; 238 | str.AddChar('H'); 239 | str.AddChar('i'); 240 | EXPECT_STREQ(str.CStr(), "Hi"); 241 | } 242 | 243 | TEST(TAutoString, AddStr) 244 | { 245 | TAutoString str; 246 | str.AddStr("Hello"); 247 | str.AddStr(", "); 248 | str.AddStr("World"); 249 | EXPECT_STREQ(str.CStr(), "Hello, World"); 250 | } 251 | 252 | TEST(TAutoString, InjectAdd) 253 | { 254 | TAutoString str; 255 | str.AddChar('H'); 256 | str.InjectAdd(1); 257 | EXPECT_EQ(str.CStr()[1], '\0'); 258 | } 259 | 260 | TEST(TAutoString, AddLongStr) 261 | { 262 | TAutoString str; 263 | str.AddStr("Hello"); 264 | str.AddStr(", "); 265 | str.AddStr("World"); 266 | 267 | const std::string longStr(1024, 'A'); 268 | const std::string longStr2(2048, 'B'); 269 | 270 | str.AddStr(longStr.c_str(), longStr.length()); 271 | str.AddStr(longStr2.c_str(), longStr2.length()); 272 | 273 | EXPECT_STREQ(str.CStr(), (std::string("Hello, World") + longStr + longStr2).c_str()); 274 | } 275 | 276 | TEST(TAutoString, AddAlignStr) 277 | { 278 | TAutoString str; 279 | 280 | str.AddAlignStr("Hello", strlen("Hello"), 10, false, ' '); 281 | EXPECT_STREQ(str.CStr(), "Hello "); 282 | 283 | str.AddAlignStr("World", strlen("World"), 10, true, '$'); 284 | EXPECT_STREQ(str.CStr(), "Hello $$$$$World"); 285 | 286 | str.AddAlignStr("Hi", strlen("Hi"), 5, false, '0'); 287 | EXPECT_STREQ(str.CStr(), "Hello $$$$$WorldHi000"); 288 | 289 | str.AddAlignStr("HelloWorld", strlen("HelloWorld"), 5, true, '0'); 290 | EXPECT_STREQ(str.CStr(), "Hello $$$$$WorldHi000HelloWorld"); 291 | 292 | str.AddAlignStr("12345", strlen("12345"), 5, true, '0'); 293 | EXPECT_STREQ(str.CStr(), "Hello $$$$$WorldHi000HelloWorld12345"); 294 | } 295 | 296 | TEST(TAutoString, AddAlignWStr) 297 | { 298 | TAutoString str; 299 | 300 | str.AddAlignStr(L"Hello", wcslen(L"Hello"), 10, false, ' '); 301 | EXPECT_STREQ(str.CStr(), L"Hello "); 302 | 303 | str.AddAlignStr(L"World", wcslen(L"World"), 10, true, '$'); 304 | EXPECT_STREQ(str.CStr(), L"Hello $$$$$World"); 305 | 306 | str.AddAlignStr(L"Hi", wcslen(L"Hi"), 5, false, '0'); 307 | EXPECT_STREQ(str.CStr(), L"Hello $$$$$WorldHi000"); 308 | 309 | str.AddAlignStr(L"HelloWorld", wcslen(L"HelloWorld"), 5, true, '0'); 310 | EXPECT_STREQ(str.CStr(), L"Hello $$$$$WorldHi000HelloWorld"); 311 | 312 | str.AddAlignStr(L"12345", wcslen(L"12345"), 5, true, '0'); 313 | EXPECT_STREQ(str.CStr(), L"Hello $$$$$WorldHi000HelloWorld12345"); 314 | } 315 | 316 | TEST(TAutoString, Clear) 317 | { 318 | TAutoString str; 319 | 320 | str.AddStr(L"Hello"); 321 | EXPECT_STREQ(str.CStr(), L"Hello"); 322 | 323 | str.Clear(); 324 | 325 | EXPECT_TRUE(str.IsEmpty()); 326 | EXPECT_STREQ(str.CStr(), L""); 327 | } 328 | 329 | TEST(TAutoString, BeginEnd) 330 | { 331 | TAutoString str("Hello, World!"); 332 | const char* expected = "Hello, World!"; 333 | int index = 0; 334 | 335 | for (TAutoString::iterator it = str.begin(); it != str.end(); ++it) 336 | { 337 | EXPECT_EQ(*it, expected[index]); 338 | ++index; 339 | } 340 | EXPECT_EQ(index, strlen(expected)); 341 | } 342 | 343 | TEST(TAutoString, RBeginREnd) 344 | { 345 | TAutoString str("Hello, World!"); 346 | const char* expected = "!dlroW ,olleH"; 347 | int index = 0; 348 | 349 | for (TAutoString::reverse_iterator it = str.rbegin(); it != str.rend(); ++it) 350 | { 351 | EXPECT_EQ(*it, expected[index]); 352 | ++index; 353 | } 354 | EXPECT_EQ(index, strlen(expected)); 355 | } 356 | 357 | #if FL_COMPILER_IS_GREATER_THAN_CXX11 358 | TEST(TAutoString, RangeBasedFor) 359 | { 360 | TAutoString str("Hello, World!"); 361 | const char* expected = "Hello, World!"; 362 | int index = 0; 363 | 364 | for (const auto& ch : str) 365 | { 366 | EXPECT_EQ(ch, expected[index]); 367 | ++index; 368 | } 369 | EXPECT_EQ(index, strlen(expected)); 370 | } 371 | #endif -------------------------------------------------------------------------------- /Format/Common/Mutex.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2024 CPPStringFormatting 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | Project URL: https://github.com/bodong1987/CPPStringFormatting 25 | */ 26 | #pragma once 27 | 28 | #include 29 | #include 30 | 31 | #if FL_PLATFORM_WINDOWS 32 | #ifndef _WINDOWS_ 33 | #include 34 | #if _WIN32_WINNT > 0x0600 35 | #define FL_IS_WINDOWS_VISTA_OR_LATER 1 36 | #else 37 | #define FL_IS_WINDOWS_VISTA_OR_LATER 0 38 | #endif 39 | #endif 40 | #else 41 | // assume your platform support posix thread library 42 | #include 43 | #endif 44 | 45 | // ReSharper disable once CppEnforceNestedNamespacesStyle 46 | namespace Formatting // NOLINT(*-concat-nested-namespaces) 47 | { 48 | namespace Details 49 | { 50 | /// 51 | /// Class SharedMutex. 52 | /// Implements the 53 | /// 54 | /// 55 | class SharedMutex : Noncopyable /*NOLINT*/ 56 | { 57 | public: 58 | SharedMutex() // NOLINT(*-pro-type-member-init) 59 | { 60 | #if FL_PLATFORM_WINDOWS 61 | #if FL_IS_WINDOWS_VISTA_OR_LATER 62 | InitializeSRWLock(&SRWLockValue); 63 | #else 64 | InitializeCriticalSection(&CriticalSectionValue); 65 | SetCriticalSectionSpinCount(&CriticalSectionValue, 4000); 66 | #endif 67 | #else 68 | // pthread_mutexattr_t MutexAtt; 69 | // pthread_mutexattr_init(&MutexAtt); 70 | // pthread_mutexattr_settype(&MutexAtt, PTHREAD_MUTEX_RECURSIVE); 71 | // pthread_mutex_init(&Mutex_t, &MutexAtt); 72 | 73 | pthread_rwlock_init(&RWLock, nullptr); 74 | #endif 75 | } 76 | 77 | ~SharedMutex() 78 | { 79 | #if FL_PLATFORM_WINDOWS 80 | #if FL_IS_WINDOWS_VISTA_OR_LATER 81 | #else 82 | DeleteCriticalSection(&CriticalSectionValue); 83 | #endif 84 | #else 85 | // pthread_mutex_destroy(&Mutex_t); 86 | pthread_rwlock_destroy(&RWLock); 87 | #endif 88 | } 89 | 90 | void LockUnique() 91 | { 92 | #if FL_PLATFORM_WINDOWS 93 | #if FL_IS_WINDOWS_VISTA_OR_LATER 94 | AcquireSRWLockExclusive(&SRWLockValue); 95 | #else 96 | EnterCriticalSection(&CriticalSectionValue); 97 | #endif 98 | #else 99 | // pthread_mutex_lock(&Mutex_t); 100 | pthread_rwlock_wrlock(&RWLock); 101 | #endif 102 | } 103 | 104 | void UnLockUnique() 105 | { 106 | #if FL_PLATFORM_WINDOWS 107 | #if FL_IS_WINDOWS_VISTA_OR_LATER 108 | ReleaseSRWLockExclusive(&SRWLockValue); 109 | #else 110 | LeaveCriticalSection(&CriticalSectionValue); 111 | #endif 112 | #else 113 | // pthread_mutex_unlock(&Mutex_t); 114 | pthread_rwlock_unlock(&RWLock); 115 | #endif 116 | } 117 | 118 | void LockShared() 119 | { 120 | #if FL_PLATFORM_WINDOWS 121 | #if FL_IS_WINDOWS_VISTA_OR_LATER 122 | AcquireSRWLockShared(&SRWLockValue); 123 | #else 124 | EnterCriticalSection(&CriticalSectionValue); 125 | #endif 126 | #else 127 | // pthread_mutex_lock(&Mutex_t); 128 | pthread_rwlock_rdlock(&RWLock); 129 | #endif 130 | } 131 | 132 | void UnLockShared() 133 | { 134 | #if FL_PLATFORM_WINDOWS 135 | #if FL_IS_WINDOWS_VISTA_OR_LATER 136 | ReleaseSRWLockShared(&SRWLockValue); 137 | #else 138 | LeaveCriticalSection(&CriticalSectionValue); 139 | #endif 140 | #else 141 | // pthread_mutex_unlock(&Mutex_t); 142 | pthread_rwlock_unlock(&RWLock); 143 | #endif 144 | } 145 | 146 | private: 147 | #if FL_PLATFORM_WINDOWS 148 | #if FL_IS_WINDOWS_VISTA_OR_LATER 149 | // ReSharper disable once CppInconsistentNaming 150 | SRWLOCK SRWLockValue; 151 | #else 152 | CRITICAL_SECTION CriticalSectionValue; 153 | #endif 154 | #else 155 | // pthread_mutex_t Mutex_t; 156 | pthread_rwlock_t RWLock; 157 | #endif 158 | }; 159 | 160 | /// 161 | /// Class SharedMutexNone. 162 | /// no operation mutex 163 | /// Implements the 164 | /// 165 | /// 166 | class SharedMutexNone : Noncopyable 167 | { 168 | public: 169 | /// 170 | /// unique lock this instance 171 | /// 172 | void LockUnique() {} //NOLINT 173 | /// 174 | /// unique unlock this instance 175 | /// 176 | void UnLockUnique() {} //NOLINT 177 | /// 178 | /// lock this instance in shared mode 179 | /// 180 | void LockShared() {} //NOLINT 181 | /// 182 | /// unlock shared 183 | /// 184 | void UnLockShared() {} //NOLINT 185 | }; 186 | 187 | /// 188 | /// Class TUniqueLocker. 189 | /// LockUnique and UnLockUnique with RAII 190 | /// Implements the 191 | /// 192 | /// 193 | template < typename TReferenceType > 194 | class TUniqueLocker : /*NOLINT*/ 195 | Noncopyable 196 | { 197 | public: 198 | typedef TReferenceType ReferenceType; 199 | 200 | /// 201 | /// Initializes a new instance of the class. 202 | /// 203 | /// The reference target. 204 | explicit TUniqueLocker(TReferenceType& referenceTarget) : 205 | Reference(&referenceTarget) 206 | { 207 | assert(Reference); 208 | Reference->LockUnique(); 209 | } 210 | 211 | /// 212 | /// Initializes a new instance of the class. 213 | /// 214 | /// The reference pointer. 215 | explicit TUniqueLocker(TReferenceType* referencePointer) : 216 | Reference(referencePointer) 217 | { 218 | assert(Reference); 219 | Reference->LockUnique(); 220 | } 221 | 222 | /// 223 | /// Finalizes an instance of the class. 224 | /// 225 | ~TUniqueLocker() 226 | { 227 | Reference->UnLockUnique(); 228 | } 229 | 230 | protected: 231 | TReferenceType* Reference; 232 | }; 233 | 234 | /// 235 | /// Class TSharedLocker. 236 | /// LockShared and UnLockShared with RAII 237 | /// Implements the 238 | /// 239 | /// 240 | template < typename TReferenceType > 241 | class TSharedLocker : /*NOLINT*/ 242 | Noncopyable 243 | { 244 | public: 245 | typedef TReferenceType ReferenceType; 246 | 247 | /// 248 | /// Initializes a new instance of the class. 249 | /// 250 | /// The reference target. 251 | explicit TSharedLocker(TReferenceType& referenceTarget) : 252 | Reference(&referenceTarget) 253 | { 254 | assert(Reference); 255 | Reference->LockShared(); 256 | } 257 | 258 | /// 259 | /// Initializes a new instance of the class. 260 | /// 261 | /// The reference pointer. 262 | explicit TSharedLocker(TReferenceType* referencePointer) : 263 | Reference(referencePointer) 264 | { 265 | assert(Reference); 266 | Reference->LockShared(); 267 | } 268 | 269 | /// 270 | /// Finalizes an instance of the class. 271 | /// 272 | ~TSharedLocker() 273 | { 274 | Reference->UnLockShared(); 275 | } 276 | 277 | protected: 278 | TReferenceType* Reference; 279 | }; 280 | 281 | typedef TUniqueLocker ScopedLocker; 282 | typedef TSharedLocker SharedLocker; 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /UnitTests/Headers/MFCAdapter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #if FL_PLATFORM_WINDOWS && FL_COMPILER_MSVC 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | namespace Formatting 13 | { 14 | namespace Details 15 | { 16 | namespace MFC 17 | { 18 | template 19 | class TMFCPolicy 20 | { 21 | public: 22 | typedef TCharType CharType; 23 | typedef TFormatPattern FormatPattern; 24 | typedef typename FormatPattern::SizeType SizeType; 25 | typedef typename FormatPattern::ByteType ByteType; 26 | typedef TAutoArray PatternListType; 27 | typedef typename PatternListType::ConstIterator PatternIterator; 28 | typedef std::runtime_error ExceptionType; 29 | typedef TMutexType MutexType; 30 | 31 | typedef CMap PatternMapType; 32 | 33 | static const PatternListType* FindByHashKey(const PatternMapType& storageReference, SizeType hashKey) 34 | { 35 | const typename PatternMapType::CPair* pairPtr = storageReference.PLookup(hashKey); 36 | 37 | return pairPtr != nullptr ? &pairPtr->value : nullptr; 38 | } 39 | 40 | static void ReserveList(PatternListType& /*ListRef*/, int /*Len*/) 41 | { 42 | // AutoArray does not need reserve 43 | } 44 | 45 | static const PatternListType* Emplace( 46 | PatternMapType& storageReference, 47 | SizeType hashKey, 48 | #if FL_COMPILER_IS_GREATER_THAN_CXX11 49 | PatternListType&& patterns 50 | #else 51 | const PatternListType& patterns 52 | #endif 53 | ) 54 | { 55 | storageReference[hashKey] = FL_MOVE_SEMANTIC(patterns); 56 | 57 | return FindByHashKey(storageReference, hashKey); 58 | } 59 | 60 | static void AppendPattern(PatternListType& patterns, const FormatPattern& pattern) 61 | { 62 | patterns.AddItem(pattern); 63 | } 64 | }; 65 | 66 | #if FL_WITH_THREAD_LOCAL || !FL_WITH_MULTITHREAD_SUPPORT 67 | typedef SharedMutexNone DefaultMutexType; 68 | #else 69 | typedef SharedMutex DefaultMutexType; 70 | #endif 71 | 72 | typedef TPatternStorage< TMFCPolicy > MFCPatternStorageA; 73 | typedef TPatternStorage< TMFCPolicy > MFCPatternStorageW; 74 | typedef TGlobalPatternStorage< TMFCPolicy > MFCGlobalPatternStorageA; 75 | typedef TGlobalPatternStorage< TMFCPolicy > MFCGlobalPatternStorageW; 76 | 77 | #if FL_UNICODE 78 | typedef MFCPatternStorageW MFCPatternStorage; 79 | typedef MFCGlobalPatternStorageW MFCGlobalPatternStorage; 80 | #else 81 | typedef MFCPatternStorageA MFCPatternStorage; 82 | typedef MFCGlobalPatternStorageA MFCGlobalPatternStorage; 83 | #endif 84 | } 85 | } 86 | } 87 | 88 | namespace Formatting 89 | { 90 | inline const TCHAR* PtrOf(const CString& str) 91 | { 92 | return str.GetString(); 93 | } 94 | 95 | inline size_t LengthOf(const CString& str) 96 | { 97 | return str.GetLength(); 98 | } 99 | 100 | namespace Details 101 | { 102 | // make a specialization of std::basic_string 103 | template <> 104 | class TTranslator 105 | : public TTranslatorBase 106 | { 107 | public: 108 | typedef TTranslatorBase Super; 109 | 110 | static bool Transfer(Super::StringType& s, const Super::FormatPattern& /*Pattern*/, const CString& arg) 111 | { 112 | s.AddStr(arg.GetString(), arg.GetLength()); 113 | 114 | return true; 115 | } 116 | }; 117 | } 118 | 119 | namespace MFC 120 | { 121 | template 122 | inline CString Format(const TCharType* format) 123 | { 124 | typedef TAutoString SinkType; 125 | typedef Details::TGlobalPatternStorage< Details::MFC::TMFCPolicy > GlobalPatternStorageType; 126 | 127 | SinkType Sink; 128 | Details::FormatTo(Sink, format); 129 | 130 | return CString(Sink.CStr(), (int)Sink.GetLength()); 131 | } 132 | 133 | // default FormatTo support Format with no arguments 134 | inline void FormatTo(CString& sink, const TCHAR* format) 135 | { 136 | sink = Format(format); 137 | } 138 | 139 | #if FL_COMPILER_IS_GREATER_THAN_CXX11 140 | template 141 | inline CString Format(const TCharType* format, const T0& arg0, T... args) 142 | { 143 | typedef TAutoString SinkType; 144 | typedef Details::TGlobalPatternStorage< Details::MFC::TMFCPolicy > GlobalPatternStorageType; 145 | 146 | SinkType Sink; 147 | Details::FormatTo(Sink, format, arg0, args...); 148 | 149 | return CString(Sink.CStr(), (int)Sink.GetLength()); 150 | } 151 | 152 | template 153 | inline CString Format(const CString& format, const T0& arg0, T... args) 154 | { 155 | typedef TAutoString SinkType; 156 | typedef Details::TGlobalPatternStorage< Details::MFC::TMFCPolicy > GlobalPatternStorageType; 157 | 158 | SinkType Sink; 159 | Details::FormatTo(Sink, format, arg0, args...); 160 | 161 | return CString(Sink.CStr(), Sink.GetLength()); 162 | } 163 | 164 | template 165 | inline void FormatTo(CString& sink, const TFormatType& format, const T0& arg0, T... args) 166 | { 167 | sink.Empty(); 168 | 169 | typedef TAutoString SinkType; 170 | typedef Details::TGlobalPatternStorage< Details::MFC::TMFCPolicy > GlobalPatternStorageType; 171 | 172 | SinkType Sink; 173 | Details::FormatTo(Sink, format, arg0, args...); 174 | 175 | sink.Append(Sink.CStr(), Sink.GetLength()); 176 | } 177 | #else 178 | #define FL_TEMPLATE_PARAMETERS_BODY( d, i ) \ 179 | FL_PP_COMMA_IF(i) typename FL_PP_CAT(T, i) 180 | 181 | #define FL_TEMPLATE_AGUMENT_BODY( d, i ) \ 182 | FL_PP_COMMA_IF(i) FL_PP_CAT(T, i) 183 | 184 | #define FL_NORMAL_AGUMENT_BODY( d, i ) \ 185 | FL_PP_COMMA_IF(i) const FL_PP_CAT(T, i)& FL_PP_CAT(arg, i) 186 | 187 | #define FL_REAL_ARGUMENT_BODY( d, i ) \ 188 | FL_PP_COMMA_IF(i) FL_PP_CAT(arg, i) 189 | 190 | #define FL_EXPORT_FOR_STRING( i ) \ 191 | template \ 192 | CString Format(const TCHAR* format, FL_PP_REPEAT(i, FL_NORMAL_AGUMENT_BODY, )) \ 193 | { \ 194 | TAutoString Results; \ 195 | Details::FormatTo(Results, format, FL_PP_REPEAT(i, FL_REAL_ARGUMENT_BODY, )); \ 196 | return CString(Results.CStr(), Results.GetLength()); \ 197 | } \ 198 | template \ 199 | CString Format(const CString& format, FL_PP_REPEAT(i, FL_NORMAL_AGUMENT_BODY, )) \ 200 | { \ 201 | TAutoString Results; \ 202 | Details::FormatTo(Results, format.c_str(), FL_PP_REPEAT(i, FL_REAL_ARGUMENT_BODY, )); \ 203 | return CString(Results.CStr(), Results.GetLength()); \ 204 | } \ 205 | template < typename TFormatType, FL_PP_REPEAT(i, FL_TEMPLATE_PARAMETERS_BODY, ) > \ 206 | void FormatTo(CString& sink, const TFormatType& format, FL_PP_REPEAT(i, FL_NORMAL_AGUMENT_BODY, )) \ 207 | { \ 208 | sink.Empty(); \ 209 | TAutoString Results; \ 210 | Details::FormatTo< TCHAR, Details::MFC::MFCGlobalPatternStorage, const TCHAR*, FL_PP_REPEAT(i, FL_TEMPLATE_AGUMENT_BODY, )>(Results, Shims::PtrOf(format), FL_PP_REPEAT(i, FL_REAL_ARGUMENT_BODY, )); \ 211 | sink.Append(Results.CStr(), Results.GetLength()); \ 212 | } 213 | 214 | // #pragma message( FL_PP_TEXT((FL_EXPORT_FOR_STRING(1))) ) 215 | 216 | // FL_EXPORT_FOR_STRING(1) 217 | 218 | FL_ENUM_SCALARS(FL_EXPORT_FOR_STRING); 219 | 220 | #undef FL_TEMPLATE_PARAMETERS_BODY 221 | #undef FL_TEMPLATE_AGUMENT_BODY 222 | #undef FL_REAL_ARGUMENT_BODY 223 | #undef FL_EXPORT_FOR_STRING 224 | #undef FL_NORMAL_AGUMENT_BODY 225 | #endif 226 | } 227 | } 228 | 229 | 230 | 231 | #endif 232 | -------------------------------------------------------------------------------- /UnitTests/Sources/UnitTests_Extends.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #if FL_PLATFORM_WINDOWS 4 | #pragma warning(push) 5 | #pragma warning(disable:4616) 6 | #endif 7 | #include 8 | #if FL_PLATFORM_WINDOWS 9 | #pragma warning(pop) 10 | #endif 11 | 12 | #include 13 | #include 14 | 15 | using namespace Formatting; 16 | 17 | class Vector3 18 | { 19 | public: 20 | Vector3(float x = 0, float y = 0, float z = 0) : x(x), y(y), z(z) {} 21 | 22 | std::string ToString() const 23 | { 24 | char buffer[100]; 25 | TCharTraits::StringPrintf(buffer, "Vector3(%.2f, %.2f, %.2f)", x, y, z); 26 | return std::string(buffer); 27 | } 28 | 29 | std::wstring ToWString() const 30 | { 31 | wchar_t buffer[100]; 32 | TCharTraits::StringPrintf(buffer, L"Vector3(%.2f, %.2f, %.2f)", x, y, z); 33 | return std::wstring(buffer); 34 | } 35 | 36 | 37 | private: 38 | float x, y, z; 39 | }; 40 | 41 | /* 42 | if you disable this translator 43 | you will get a compile error of Transfer can't access : 44 | vs2022: 45 | \CPPStringFormatting\Format\Details\FormatTo.hpp(83,68): error C2280: ��bool Formatting::Details::TTranslator::DoTransfer::TransferType>::Transfer(Formatting::TAutoString &,const Formatting::Details::TFormatPattern &,const T &)��: Try to reference a deleted function 46 | \CPPStringFormatting\Format\Details\FormatTo.hpp(83,68): error C2280: with 47 | \CPPStringFormatting\Format\Details\FormatTo.hpp(83,68): error C2280: [ 48 | \CPPStringFormatting\Format\Details\FormatTo.hpp(83,68): error C2280: TCharType=wchar_t, 49 | \CPPStringFormatting\Format\Details\FormatTo.hpp(83,68): error C2280: TPatternType=Formatting::Details::FormatTo::FormatPatternType, 50 | \CPPStringFormatting\Format\Details\FormatTo.hpp(83,68): error C2280: T0=Vector3, 51 | \CPPStringFormatting\Format\Details\FormatTo.hpp(83,68): error C2280: T=Formatting::Details::Utils::DoTransferHelper::DoTransfer::TransferType 52 | \CPPStringFormatting\Format\Details\FormatTo.hpp(83,68): error C2280: ] 53 | 54 | xcode: 55 | Showing All Messages 56 | /Users/bodong/Desktop/CPPStringFormatting/UnitTests/Sources/UnitTests_Extends.cpp:12:10: in file included from /Users/bodong/Desktop/CPPStringFormatting/UnitTests/Sources/UnitTests_Extends.cpp:12: 57 | /Users/bodong/Desktop/CPPStringFormatting/Format/Format.hpp:39:10: in file included from /Users/bodong/Desktop/CPPStringFormatting/Format/Format.hpp:39: 58 | /Users/bodong/Desktop/CPPStringFormatting/Format/Details/FormatTo.hpp:98:77: In instantiation of member function 'Formatting::Details::Utils::DoTransferHelper, Vector3>::DoTransfer' requested here 59 | /Users/bodong/Desktop/CPPStringFormatting/Format/Details/FormatTo.hpp:154:33: In instantiation of function template specialization 'Formatting::Details::Utils::DoTransfer, Vector3>' requested here 60 | /Users/bodong/Desktop/CPPStringFormatting/Format/Details/StandardLibrary/FormatTo.hpp:88:22: In instantiation of function template specialization 'Formatting::Details::FormatTo>, const char *, Vector3>' requested here 61 | /Users/bodong/Desktop/CPPStringFormatting/UnitTests/Sources/UnitTests_Extends.cpp:118:32: In instantiation of function template specialization 'Formatting::StandardLibrary::Format' requested here 62 | /Users/bodong/Desktop/CPPStringFormatting/Format/Details/Translators.hpp:138:25: 'Transfer' has been explicitly marked deleted here 63 | 64 | gcc: 65 | In file included from I:/CPPStringFormatting/Format/Format.hpp:39, 66 | from I:\CPPStringFormatting\UnitTests\Sources\UnitTests_Extends.cpp:12: 67 | I:/CPPStringFormatting/Format/Details/FormatTo.hpp: In instantiation of 'static bool Formatting::Details::Utils::DoTransferHelper::DoTransfer(int32_t, int32_t&, Formatting::TAutoString&, const TPatternType&, const TCharType*, const T0&) [with TCharType = char; TPatternType = Formatting::Details::TFormatPattern; T0 = Vector3; int32_t = int]': 68 | I:/CPPStringFormatting/Format/Details/FormatTo.hpp:98:87: required from 'bool Formatting::Details::Utils::DoTransfer(int32_t, int32_t&, Formatting::TAutoString&, const TPatternType&, const TCharType*, const T0&, const T& ...) [with TCharType = char; TPatternType = Formatting::Details::TFormatPattern; T0 = Vector3; T = {}; int32_t = int]' 69 | I:/CPPStringFormatting/Format/Details/FormatTo.hpp:154:79: required from 'Formatting::TAutoString& Formatting::Details::FormatTo(Formatting::TAutoString&, const TFormatType&, const T& ...) [with TCharType = char; TPatternStorageType = Formatting::Details::TGlobalPatternStorage >; TFormatType = const char*; T = {Vector3}]' 70 | I:/CPPStringFormatting/Format/Details/StandardLibrary/FormatTo.hpp:88:95: required from 'std::__cxx11::basic_string<_CharT> Formatting::StandardLibrary::Format(const TCharType*, const T0&, T ...) [with TCharType = char; T0 = Vector3; T = {}]' 71 | I:\CPPStringFormatting\UnitTests\Sources\UnitTests_Extends.cpp:124:5: required from here 72 | I:/CPPStringFormatting/Format/Details/FormatTo.hpp:83:76: error: use of deleted function 'static bool Formatting::Details::TTranslator::Transfer(typename Formatting::Details::TTranslator::Super::StringType&, const typename Formatting::Details::TTranslator::Super::FormatPattern&, const T&) [with TCharType = char; T = Vector3; typename Formatting::Details::TTranslator::Super::StringType = Formatting::TAutoString; typename Formatting::Details::TTranslator::Super::FormatPattern = Formatting::Details::TFormatPattern]' 73 | if (!TTranslator::Transfer(sink, pattern, arg0)) 74 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~ 75 | In file included from I:/CPPStringFormatting/Format/Details/FormatTo.hpp:28, 76 | from I:/CPPStringFormatting/Format/Format.hpp:39, 77 | from I:\CPPStringFormatting\UnitTests\Sources\UnitTests_Extends.cpp:12: 78 | I:/CPPStringFormatting/Format/Details/Translators.hpp:138:25: note: declared here 79 | static bool Transfer(typename Super::StringType& strRef, const typename Super::FormatPattern& pattern, const T& arg) = delete; 80 | ^~~~~~~~ 81 | vs2008: 82 | 1>D:\CPPStringFormatting\Format/Details/InlineFiles/FormatTo.inl(114) : error C2248: ��Formatting::Details::TTranslator::Transfer��: �޷����� private ��Ա(�ڡ�Formatting::Details::TTranslator����������) 83 | 1> with 84 | 1> [ 85 | 1> TCharType=char, 86 | 1> T=TransferType 87 | 1> ] 88 | 1> D:\CPPStringFormatting\Format/Details/Translators.hpp(141) : �μ���Formatting::Details::TTranslator::Transfer�������� 89 | 1> with 90 | 1> [ 91 | 1> TCharType=char, 92 | 1> T=TransferType 93 | 1> ] 94 | 1> D:\CPPStringFormatting\Format/Details/StandardLibrary/FormatTo.hpp(181): �μ������ڱ���ĺ��� ģ�� ʵ������Formatting::TAutoString &Formatting::Details::FormatTo(Formatting::TAutoString &,const TFormatType &,const T0 &)�������� 95 | 1> with 96 | 1> [ 97 | 1> TCharType=char, 98 | 1> T0=Vector3, 99 | 1> TFormatType=const char * 100 | 1> ] 101 | 1> ..\UnitTests\Sources\UnitTests_Extends.cpp(143): �μ������ڱ���ĺ��� ģ�� ʵ������std::string Formatting::StandardLibrary::Format(const char *,const T0 &)�������� 102 | 1> with 103 | 1> [ 104 | 1> T0=Vector3 105 | 1> ] 106 | */ 107 | #if 1 // NOLINT 108 | // The following class provides automatic conversion capabilities for Vector3, so that Vector3 type parameters can be formatted directly. 109 | namespace Formatting 110 | { 111 | namespace Details 112 | { 113 | // convert Vector3 to string 114 | template <> 115 | class TTranslator< char, Vector3 > : 116 | public TTranslatorBase< char, Vector3 > 117 | { 118 | public: 119 | typedef TTranslatorBase< char, Vector3 > Super; 120 | typedef Super::CharType CharType; 121 | typedef Super::FormatPattern FormatPattern; 122 | typedef Super::ByteType ByteType; 123 | typedef Super::SizeType SizeType; 124 | typedef Super::StringType StringType; 125 | typedef Super::CharTraits CharTraits; 126 | 127 | static bool Transfer(StringType& strRef, const FormatPattern& pattern, const Vector3& arg) 128 | { 129 | std::string text = arg.ToString(); 130 | 131 | Super::AppendString(strRef, pattern, text.c_str(), text.size()); 132 | 133 | return true; 134 | } 135 | }; 136 | 137 | // convert Vector3 to wstring 138 | template <> 139 | class TTranslator< wchar_t, Vector3 > : 140 | public TTranslatorBase< wchar_t, Vector3 > 141 | { 142 | public: 143 | typedef TTranslatorBase< wchar_t, Vector3 > Super; 144 | typedef Super::CharType CharType; 145 | typedef Super::FormatPattern FormatPattern; 146 | typedef Super::ByteType ByteType; 147 | typedef Super::SizeType SizeType; 148 | typedef Super::StringType StringType; 149 | typedef Super::CharTraits CharTraits; 150 | 151 | static bool Transfer(StringType& strRef, const FormatPattern& pattern, const Vector3& arg) 152 | { 153 | std::wstring text = arg.ToWString(); 154 | 155 | Super::AppendString(strRef, pattern, text.c_str(), text.size()); 156 | 157 | return true; 158 | } 159 | }; 160 | } 161 | } 162 | #endif 163 | 164 | TEST(FormatExtends, Extend_Vector3) 165 | { 166 | Vector3 v(1,2,3); 167 | EXPECT_EQ(StandardLibrary::Format("{0}", v), "Vector3(1.00, 2.00, 3.00)"); 168 | EXPECT_EQ(StandardLibrary::Format(L"{0}", v), L"Vector3(1.00, 2.00, 3.00)"); 169 | } 170 | -------------------------------------------------------------------------------- /Format/Common/AutoString.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2024 CPPStringFormatting 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | Project URL: https://github.com/bodong1987/CPPStringFormatting 25 | */ 26 | #pragma once 27 | 28 | #include 29 | #include 30 | 31 | namespace Formatting 32 | { 33 | // Allows users to directly define the default TAutoString stack character size from the outside 34 | #ifndef FL_DEFAULT_AUTO_STRING_STACK_LENGTH 35 | #define FL_DEFAULT_AUTO_STRING_STACK_LENGTH 120 36 | #endif 37 | 38 | /// 39 | /// Class TAutoString. 40 | /// stack string 41 | /// Implements the 42 | /// 43 | /// 44 | template < typename TCharType > 45 | class TAutoString : 46 | public TAutoArray< TCharType, FL_DEFAULT_AUTO_STRING_STACK_LENGTH, 1 > 47 | { 48 | public: 49 | typedef TAutoArray< TCharType, FL_DEFAULT_AUTO_STRING_STACK_LENGTH, 1 > Super; 50 | typedef TCharTraits CharTraits; 51 | typedef TCharType CharType; 52 | 53 | using Super::Count; 54 | using Super::AllocatedCount; 55 | using Super::HeapValPtr; 56 | using Super::StackVal; 57 | using Super::Allocate; 58 | using Super::IsDataOnStack; 59 | using Super::DEFAULT_LENGTH; 60 | using Super::GetDataPtr; 61 | using Super::ReleaseHeapData; 62 | 63 | TAutoString() 64 | { 65 | FL_STATIC_ASSERT(Super::DEFAULT_LENGTH>0, "Invalid TAutoString usage."); 66 | 67 | Super::StackVal[0] = 0; 68 | } 69 | 70 | explicit TAutoString(const CharType* str) 71 | { 72 | if (str) 73 | { 74 | const size_t Length = CharTraits::length(str); 75 | 76 | Count = Length; 77 | 78 | if (Length <= DEFAULT_LENGTH) 79 | { 80 | CharTraits::copy(StackVal, str, Length); 81 | StackVal[Count] = 0; 82 | } 83 | else 84 | { 85 | HeapValPtr = Allocate(Length); 86 | CharTraits::copy(HeapValPtr, str, Length); 87 | HeapValPtr[Count] = 0; 88 | } 89 | } 90 | } 91 | 92 | void AddChar(CharType value) 93 | { 94 | AddItem(value); 95 | 96 | if (IsDataOnStack()) 97 | { 98 | StackVal[Count] = 0; 99 | } 100 | else 101 | { 102 | HeapValPtr[Count] = 0; 103 | } 104 | } 105 | 106 | void AddStr(const CharType* str) 107 | { 108 | if(str == nullptr) 109 | { 110 | return; 111 | } 112 | 113 | AddStr(str, CharTraits::length(str)); 114 | } 115 | 116 | void AddStr(const CharType* str, const size_t length) 117 | { 118 | if(str == nullptr || length == 0) 119 | { 120 | return; 121 | } 122 | 123 | const size_t NewCount = Count + length; 124 | 125 | if (IsDataOnStack()) 126 | { 127 | if (NewCount <= DEFAULT_LENGTH) 128 | { 129 | CharTraits::copy(StackVal + Count, str, length); 130 | Count = NewCount; 131 | 132 | StackVal[Count] = 0; 133 | } 134 | else 135 | { 136 | assert(!HeapValPtr); 137 | 138 | AllocatedCount = NewCount + NewCount/2; 139 | HeapValPtr = Allocate(AllocatedCount); 140 | assert(HeapValPtr); 141 | 142 | if (Count > 0) 143 | { 144 | CharTraits::copy(HeapValPtr, StackVal, Count); 145 | } 146 | 147 | CharTraits::copy(HeapValPtr + Count, str, length); 148 | Count = NewCount; 149 | 150 | HeapValPtr[Count] = 0; 151 | } 152 | } 153 | else 154 | { 155 | if (NewCount <= AllocatedCount) 156 | { 157 | CharTraits::copy(HeapValPtr + Count, str, length); 158 | Count = NewCount; 159 | 160 | HeapValPtr[Count] = 0; 161 | } 162 | else 163 | { 164 | size_t NewAllocatedCount = NewCount + NewCount/2; 165 | 166 | CharType* DataPtr = Allocate(NewAllocatedCount); 167 | 168 | if (Count > 0) 169 | { 170 | CharTraits::copy(DataPtr, HeapValPtr, Count); 171 | } 172 | 173 | ReleaseHeapData(); 174 | 175 | assert(HeapValPtr == nullptr); 176 | 177 | CharTraits::copy(DataPtr + Count, str, length); 178 | 179 | Count = NewCount; 180 | 181 | AllocatedCount = NewAllocatedCount; 182 | HeapValPtr = DataPtr; 183 | 184 | HeapValPtr[Count] = 0; 185 | } 186 | } 187 | } 188 | 189 | private: 190 | void AppendWithPadding( 191 | CharType* bufferPtr, 192 | const CharType* start, 193 | const size_t length, 194 | const size_t targetLength, 195 | const bool paddingLeft, 196 | const CharType fillChar 197 | ) 198 | { 199 | assert(bufferPtr != nullptr); 200 | 201 | const int PaddingCount = static_cast(targetLength - length); 202 | CharType* const StartPos = bufferPtr + Count; 203 | 204 | assert(bufferPtr); 205 | assert(start); 206 | 207 | if (PaddingCount > 0) 208 | { 209 | if (paddingLeft) 210 | { 211 | CharTraits::Fill(StartPos, fillChar, PaddingCount); 212 | CharTraits::copy(StartPos + PaddingCount, start, length); 213 | } 214 | else 215 | { 216 | CharTraits::copy(StartPos, start, length); 217 | CharTraits::Fill(StartPos + length, fillChar, PaddingCount); 218 | } 219 | } 220 | else 221 | { 222 | CharTraits::copy(StartPos, start, length); 223 | } 224 | 225 | Count += targetLength; 226 | bufferPtr[Count] = TCharTraits::GetEndFlag(); 227 | } 228 | 229 | public: 230 | void AddAlignStr( 231 | const CharType* start, 232 | const size_t length, 233 | const int alignedLength, 234 | const bool paddingLeft, 235 | const CharType fillChar) 236 | { 237 | // get text length 238 | const size_t TargetLength = Algorithm::Max(length, static_cast(alignedLength)); 239 | const size_t NewCount = Count + TargetLength; 240 | 241 | if (IsDataOnStack()) 242 | { 243 | if (NewCount <= DEFAULT_LENGTH) 244 | { 245 | AppendWithPadding(StackVal, start, length, TargetLength, paddingLeft, fillChar); 246 | } 247 | else 248 | { 249 | assert(!HeapValPtr); 250 | AllocatedCount = NewCount + NewCount/2; 251 | HeapValPtr = Allocate(AllocatedCount); 252 | 253 | assert(HeapValPtr); 254 | 255 | if (Count > 0) 256 | { 257 | CharTraits::copy(HeapValPtr, StackVal, Count); 258 | } 259 | 260 | AppendWithPadding(HeapValPtr, start, length, TargetLength, paddingLeft, fillChar); 261 | } 262 | } 263 | else 264 | { 265 | if (NewCount < AllocatedCount) 266 | { 267 | AppendWithPadding(HeapValPtr, start, length, TargetLength, paddingLeft, fillChar); 268 | } 269 | else 270 | { 271 | const size_t NewAllocatedCount = NewCount + NewCount/2; 272 | CharType* DataPtr = Allocate(NewAllocatedCount); 273 | assert(DataPtr); 274 | 275 | if (Count > 0) 276 | { 277 | CharTraits::copy(DataPtr, HeapValPtr, Count); 278 | } 279 | 280 | ReleaseHeapData(); 281 | 282 | AllocatedCount = NewAllocatedCount; 283 | HeapValPtr = DataPtr; 284 | 285 | AppendWithPadding(HeapValPtr, start, length, TargetLength, paddingLeft, fillChar); 286 | } 287 | } 288 | } 289 | 290 | const TCharType* CStr() const // NOLINT(modernize-use-nodiscard) 291 | { 292 | return GetDataPtr(); 293 | } 294 | 295 | void InjectAdd(size_t count) 296 | { 297 | Count += count; 298 | 299 | assert(IsDataOnStack() ? (Count <= DEFAULT_LENGTH) : (Count < AllocatedCount)); // NOLINT 300 | } 301 | 302 | void Clear() 303 | { 304 | Count = 0; 305 | StackVal[0] = 0; 306 | if(HeapValPtr != nullptr) 307 | { 308 | HeapValPtr[0] = 0; 309 | } 310 | } 311 | 312 | bool IsEmpty() const // NOLINT(modernize-use-nodiscard) 313 | { 314 | return Count == 0; 315 | } 316 | 317 | protected: 318 | void AddItem(const TCharType& value) 319 | { 320 | Super::AddItem(value); 321 | } 322 | 323 | void AddItems(const TCharType* items, const size_t length) 324 | { 325 | AddStr(items, length); 326 | } 327 | }; 328 | } 329 | -------------------------------------------------------------------------------- /Format/Details/PatternStorage.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2024 CPPStringFormatting 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | Project URL: https://github.com/bodong1987/CPPStringFormatting 25 | */ 26 | #pragma once 27 | 28 | #include 29 | #include 30 | 31 | // ReSharper disable once CppEnforceNestedNamespacesStyle 32 | namespace Formatting // NOLINT(*-concat-nested-namespaces) 33 | { 34 | namespace Details 35 | { 36 | namespace Utils 37 | { 38 | template 39 | class TPatternStorageBase 40 | { 41 | public: 42 | typedef typename TPolicy::CharType CharType; 43 | typedef typename TPolicy::ByteType ByteType; 44 | typedef typename TPolicy::SizeType SizeType; 45 | typedef typename TPolicy::PatternListType PatternListType; 46 | typedef typename TPolicy::PatternIterator PatternIterator; 47 | typedef typename TPolicy::PatternMapType PatternMapType; 48 | typedef typename TPolicy::ExceptionType ExceptionType; 49 | typedef typename TPolicy::MutexType MutexType; 50 | typedef TFormatPattern FormatPattern; 51 | typedef TPatternParser PatternParser; 52 | typedef TSharedLocker SharedLockerType; 53 | typedef TUniqueLocker UniqueLockerType; 54 | 55 | protected: 56 | /// 57 | /// Lookups the patterns with lockers 58 | /// 59 | /// The format start. 60 | /// The length. 61 | /// The hash key. 62 | /// const PatternListType *. 63 | const PatternListType* LookupPatternsInternal( 64 | const CharType* const formatStart, 65 | const SizeType length, 66 | SizeType hashKey) 67 | { 68 | // First, Find in the cache 69 | { 70 | SharedLockerType Locker(MutexValue); 71 | 72 | const PatternListType* PatternList = TPolicy::FindByHashKey(Storage, hashKey); 73 | 74 | if (nullptr != PatternList) 75 | { 76 | return PatternList; 77 | } 78 | } 79 | 80 | 81 | PatternListType Patterns; 82 | TPolicy::ReserveList(Patterns, 8); 83 | 84 | PatternParser Parser; 85 | if (Parser(formatStart, length, Patterns)) 86 | { 87 | UniqueLockerType Locker(MutexValue); 88 | 89 | return TPolicy::Emplace(Storage, hashKey, FL_MOVE_SEMANTIC(Patterns)); 90 | } 91 | 92 | assert(false && "invalid format expression!"); 93 | 94 | // ReSharper disable once CppDFAUnreachableCode 95 | throw ExceptionType("invalid format expression!"); 96 | 97 | // return nullptr; 98 | } 99 | 100 | MutexType MutexValue; 101 | 102 | PatternMapType Storage; 103 | }; 104 | 105 | template 106 | class TPatternStorageBase 107 | { 108 | public: 109 | typedef typename TPolicy::CharType CharType; 110 | typedef typename TPolicy::ByteType ByteType; 111 | typedef typename TPolicy::SizeType SizeType; 112 | typedef typename TPolicy::PatternListType PatternListType; 113 | typedef typename TPolicy::PatternIterator PatternIterator; 114 | typedef typename TPolicy::PatternMapType PatternMapType; 115 | typedef typename TPolicy::ExceptionType ExceptionType; 116 | typedef typename TPolicy::MutexType MutexType; 117 | typedef TFormatPattern FormatPattern; 118 | typedef TPatternParser PatternParser; 119 | typedef TSharedLocker SharedLockerType; 120 | typedef TUniqueLocker UniqueLockerType; 121 | 122 | protected: 123 | /// 124 | /// Lookups the patterns without lockers 125 | /// 126 | /// The format start. 127 | /// The length. 128 | /// The hash key. 129 | /// const PatternListType *. 130 | const PatternListType* LookupPatternsInternal( 131 | const CharType* const formatStart, 132 | const SizeType length, 133 | SizeType hashKey) 134 | { 135 | // First, Find in the cache 136 | const PatternListType* PatternList = TPolicy::FindByHashKey(Storage, hashKey); 137 | 138 | if (nullptr != PatternList) 139 | { 140 | return PatternList; 141 | } 142 | 143 | PatternListType Patterns; 144 | TPolicy::ReserveList(Patterns, 8); 145 | 146 | PatternParser Parser; 147 | if (Parser(formatStart, length, Patterns)) 148 | { 149 | return TPolicy::Emplace(Storage, hashKey, FL_MOVE_SEMANTIC(Patterns)); 150 | } 151 | 152 | assert(false && "invalid format expression!"); 153 | 154 | // ReSharper disable once CppDFAUnreachableCode 155 | throw ExceptionType("invalid format expression!"); 156 | 157 | // return nullptr; 158 | } 159 | 160 | PatternMapType Storage; 161 | }; 162 | } 163 | 164 | template < typename TPolicy > 165 | class TPatternStorage : 166 | public Utils::TPatternStorageBase< 167 | TPolicy, 168 | Mpl::IsSame::Value 169 | > 170 | { 171 | public: 172 | typedef Utils::TPatternStorageBase< 173 | TPolicy, 174 | Mpl::IsSame< 175 | SharedMutexNone, 176 | typename TPolicy::MutexType 177 | >::Value 178 | > Super; 179 | 180 | typedef typename TPolicy::CharType CharType; 181 | typedef typename TPolicy::SizeType SizeType; 182 | typedef typename TPolicy::PatternListType PatternListType; 183 | typedef typename TPolicy::PatternMapType PatternMapType; 184 | 185 | /// 186 | /// Lookups the patterns without lockers 187 | /// 188 | /// The format start. 189 | /// The length. 190 | /// The hash key. 191 | /// const PatternListType *. 192 | const PatternListType* LookupPatterns( 193 | const CharType* const formatStart, 194 | const SizeType length, 195 | SizeType hashKey) 196 | { 197 | return Super::LookupPatternsInternal(formatStart, length, hashKey); 198 | } 199 | }; 200 | 201 | template < typename TPolicy > 202 | class TGlobalPatternStorage : 203 | public TPatternStorage 204 | { 205 | public: 206 | /// 207 | /// Gets the storage. 208 | /// 209 | /// Formatting.Details.TGlobalPatternStorage<TPolicy> *. 210 | static TGlobalPatternStorage* GetStorage() 211 | { 212 | #if FL_WITH_THREAD_LOCAL 213 | #if FL_COMPILER_IS_GREATER_THAN_CXX11 214 | thread_local TGlobalPatternStorage StaticStorage; 215 | return &StaticStorage; 216 | #else 217 | struct ManagedStorage : Noncopyable // NOLINT 218 | { 219 | typedef TUniqueLocker LockerType; 220 | 221 | SharedMutex MutexValue; 222 | TAutoArray Storages; 223 | 224 | ~ManagedStorage() 225 | { 226 | LockerType Locker(MutexValue); 227 | 228 | for( size_t i=0; i 3 | #include 4 | #include 5 | 6 | #if FL_COMPILER_IS_GREATER_THAN_CXX20 7 | #include 8 | #endif 9 | 10 | char GSprintfBuffer[1024]; 11 | 12 | void test_c_sprintf() 13 | { 14 | Formatting::TCharTraits::StringPrintf(GSprintfBuffer, "%d %s %.2f %d %s %.2f", 123, "hello", 1.23, 456, "world", 4.56); 15 | Formatting::TCharTraits::StringPrintf(GSprintfBuffer, "%d - %s - %.2f - %d - %s - %.2f", 789, "foo", 7.89, 101, "bar", 10.11); 16 | Formatting::TCharTraits::StringPrintf(GSprintfBuffer, "%d + %s + %.2f + %d + %s + %.2f", 112, "baz", 11.12, 131, "qux", 13.14); 17 | Formatting::TCharTraits::StringPrintf(GSprintfBuffer, "%d * %s * %.2f * %d * %s * %.2f", 215, "alpha", 21.52, 314, "beta", 31.45); 18 | Formatting::TCharTraits::StringPrintf(GSprintfBuffer, "%d / %s / %.2f / %d / %s / %.2f", 516, "gamma", 51.67, 617, "delta", 61.78); 19 | Formatting::TCharTraits::StringPrintf(GSprintfBuffer, "%d ^ %s ^ %.2f ^ %d ^ %s ^ %.2f", 718, "epsilon", 71.89, 819, "zeta", 81.90); 20 | Formatting::TCharTraits::StringPrintf(GSprintfBuffer, "%d %% %s %% %.2f %% %d %% %s %% %.2f", 920, "eta", 92.01, 101, "theta", 10.12); 21 | Formatting::TCharTraits::StringPrintf(GSprintfBuffer, "%d & %s & %.2f & %d & %s & %.2f", 122, "iota", 12.23, 233, "kappa", 23.34); 22 | Formatting::TCharTraits::StringPrintf(GSprintfBuffer, "%d | %s | %.2f | %d | %s | %.2f", 344, "lambda", 34.45, 455, "mu", 45.56); 23 | Formatting::TCharTraits::StringPrintf(GSprintfBuffer, "%d ~ %s ~ %.2f ~ %d ~ %s ~ %.2f", 566, "nu", 56.67, 677, "xi", 67.78); 24 | 25 | // Padding and alignment 26 | Formatting::TCharTraits::StringPrintf(GSprintfBuffer, "%5d", 123); 27 | Formatting::TCharTraits::StringPrintf(GSprintfBuffer, "%-5d", 123); 28 | Formatting::TCharTraits::StringPrintf(GSprintfBuffer, "%6d", -123); 29 | Formatting::TCharTraits::StringPrintf(GSprintfBuffer, "%-6d", -123); 30 | Formatting::TCharTraits::StringPrintf(GSprintfBuffer, "%s", "\n\t\""); 31 | Formatting::TCharTraits::StringPrintf(GSprintfBuffer, "%e", 123.456); 32 | Formatting::TCharTraits::StringPrintf(GSprintfBuffer, "%E", 123.456); 33 | Formatting::TCharTraits::StringPrintf(GSprintfBuffer, "%.3e", 123.456); 34 | Formatting::TCharTraits::StringPrintf(GSprintfBuffer, "%.3E", 123.456); 35 | Formatting::TCharTraits::StringPrintf(GSprintfBuffer, "%x", 123); 36 | Formatting::TCharTraits::StringPrintf(GSprintfBuffer, "%X", 123); 37 | Formatting::TCharTraits::StringPrintf(GSprintfBuffer, "%08x", 123); 38 | Formatting::TCharTraits::StringPrintf(GSprintfBuffer, "%08X", 123); 39 | Formatting::TCharTraits::StringPrintf(GSprintfBuffer, "%5s", "hi"); 40 | Formatting::TCharTraits::StringPrintf(GSprintfBuffer, "%-5s", "hi"); 41 | } 42 | 43 | #if FL_COMPILER_IS_GREATER_THAN_CXX20 44 | std::string GStdFormatResult; 45 | void test_std_format() 46 | { 47 | GStdFormatResult = std::format("{} {} {:.2f} {} {} {:.2f}", 123, "hello", 1.23, 456, "world", 4.56); 48 | GStdFormatResult = std::format("{} - {} - {:.2f} - {} - {} - {:.2f}", 789, "foo", 7.89, 101, "bar", 10.11); 49 | GStdFormatResult = std::format("{} + {} + {:.2f} + {} + {} + {:.2f}", 112, "baz", 11.12, 131, "qux", 13.14); 50 | GStdFormatResult = std::format("{} * {} * {:.2f} * {} * {} * {:.2f}", 215, "alpha", 21.52, 314, "beta", 31.45); 51 | GStdFormatResult = std::format("{} / {} / {:.2f} / {} / {} / {:.2f}", 516, "gamma", 51.67, 617, "delta", 61.78); 52 | GStdFormatResult = std::format("{} ^ {} ^ {:.2f} ^ {} ^ {} ^ {:.2f}", 718, "epsilon", 71.89, 819, "zeta", 81.90); 53 | GStdFormatResult = std::format("{} % {} % {:.2f} % {} % {} % {:.2f}", 920, "eta", 92.01, 101, "theta", 10.12); 54 | GStdFormatResult = std::format("{} & {} & {:.2f} & {} & {} & {:.2f}", 122, "iota", 12.23, 233, "kappa", 23.34); 55 | GStdFormatResult = std::format("{} | {} | {:.2f} | {} | {} | {:.2f}", 344, "lambda", 34.45, 455, "mu", 45.56); 56 | GStdFormatResult = std::format("{} ~ {} ~ {:.2f} ~ {} ~ {} ~ {:.2f}", 566, "nu", 56.67, 677, "xi", 67.78); 57 | 58 | // Padding and alignment 59 | GStdFormatResult = std::format("{:5}", 123); 60 | GStdFormatResult = std::format("{:-5}", 123); 61 | GStdFormatResult = std::format("{:6}", -123); 62 | GStdFormatResult = std::format("{:-6}", -123); 63 | GStdFormatResult = std::format("{}", "\n\t\""); 64 | GStdFormatResult = std::format("{:e}", 123.456); 65 | GStdFormatResult = std::format("{:E}", 123.456); 66 | GStdFormatResult = std::format("{:.3e}", 123.456); 67 | GStdFormatResult = std::format("{:.3E}", 123.456); 68 | GStdFormatResult = std::format("{:x}", 123); 69 | GStdFormatResult = std::format("{:X}", 123); 70 | GStdFormatResult = std::format("{:08x}", 123); 71 | GStdFormatResult = std::format("{:08X}", 123); 72 | GStdFormatResult = std::format("{}", "hi"); 73 | GStdFormatResult = std::format("{}", "hi"); 74 | } 75 | #endif 76 | 77 | std::string GCppFormatLibraryCommonResult; 78 | void test_cpp_format_library_common() 79 | { 80 | using namespace Formatting; 81 | StandardLibrary::FormatTo(GCppFormatLibraryCommonResult, "{0} {1} {2} {3} {4} {5}", 123, "hello", 1.23, 456, "world", 4.56); 82 | StandardLibrary::FormatTo(GCppFormatLibraryCommonResult, "{0} - {1} - {2} - {3} - {4} - {5}", 789, "foo", 7.89, 101, "bar", 10.11); 83 | StandardLibrary::FormatTo(GCppFormatLibraryCommonResult, "{0} + {1} + {2} + {3} + {4} + {5}", 112, "baz", 11.12, 131, "qux", 13.14); 84 | StandardLibrary::FormatTo(GCppFormatLibraryCommonResult, "{0} * {1} * {2} * {3} * {4} * {5}", 215, "alpha", 21.52, 314, "beta", 31.45); 85 | StandardLibrary::FormatTo(GCppFormatLibraryCommonResult, "{0} / {1} / {2} / {3} / {4} / {5}", 516, "gamma", 51.67, 617, "delta", 61.78); 86 | StandardLibrary::FormatTo(GCppFormatLibraryCommonResult, "{0} ^ {1} ^ {2} ^ {3} ^ {4} ^ {5}", 718, "epsilon", 71.89, 819, "zeta", 81.90); 87 | StandardLibrary::FormatTo(GCppFormatLibraryCommonResult, "{0} % {1} % {2} % {3} % {4} % {5}", 920, "eta", 92.01, 101, "theta", 10.12); 88 | StandardLibrary::FormatTo(GCppFormatLibraryCommonResult, "{0} & {1} & {2} & {3} & {4} & {5}", 122, "iota", 12.23, 233, "kappa", 23.34); 89 | StandardLibrary::FormatTo(GCppFormatLibraryCommonResult, "{0} | {1} | {2} | {3} | {4} | {5}", 344, "lambda", 34.45, 455, "mu", 45.56); 90 | StandardLibrary::FormatTo(GCppFormatLibraryCommonResult, "{0} ~ {1} ~ {2} ~ {3} ~ {4} ~ {5}", 566, "nu", 56.67, 677, "xi", 67.78); 91 | 92 | // Padding and alignment 93 | StandardLibrary::FormatTo(GCppFormatLibraryCommonResult, "{0,5}", 123); 94 | StandardLibrary::FormatTo(GCppFormatLibraryCommonResult, "{0,-5}", 123); 95 | StandardLibrary::FormatTo(GCppFormatLibraryCommonResult, "{0,6}", -123); 96 | StandardLibrary::FormatTo(GCppFormatLibraryCommonResult, "{0,-6}", -123); 97 | StandardLibrary::FormatTo(GCppFormatLibraryCommonResult, "{0}", "\n\t\""); 98 | StandardLibrary::FormatTo(GCppFormatLibraryCommonResult, "{0:e}", 123.456); 99 | StandardLibrary::FormatTo(GCppFormatLibraryCommonResult, "{0:E}", 123.456); 100 | StandardLibrary::FormatTo(GCppFormatLibraryCommonResult, "{0:e3}", 123.456); 101 | StandardLibrary::FormatTo(GCppFormatLibraryCommonResult, "{0:E3}", 123.456); 102 | StandardLibrary::FormatTo(GCppFormatLibraryCommonResult, "{0:x}", 123); 103 | StandardLibrary::FormatTo(GCppFormatLibraryCommonResult, "{0:X}", 123); 104 | StandardLibrary::FormatTo(GCppFormatLibraryCommonResult, "{0:x8}", 123); 105 | StandardLibrary::FormatTo(GCppFormatLibraryCommonResult, "{0:X8}", 123); 106 | StandardLibrary::FormatTo(GCppFormatLibraryCommonResult, "{0,5}", "hi"); 107 | StandardLibrary::FormatTo(GCppFormatLibraryCommonResult, "{0,-5}", "hi"); 108 | } 109 | 110 | std::string GCppFormatLibraryOptimizedResult; 111 | 112 | void test_cpp_format_library_optimized() 113 | { 114 | FL_STD_FORMAT_TO(GCppFormatLibraryOptimizedResult, "{0} {1} {2} {3} {4} {5}", 123, "hello", 1.23, 456, "world", 4.56); 115 | FL_STD_FORMAT_TO(GCppFormatLibraryOptimizedResult, "{0} - {1} - {2} - {3} - {4} - {5}", 789, "foo", 7.89, 101, "bar", 10.11); 116 | FL_STD_FORMAT_TO(GCppFormatLibraryOptimizedResult, "{0} + {1} + {2} + {3} + {4} + {5}", 112, "baz", 11.12, 131, "qux", 13.14); 117 | FL_STD_FORMAT_TO(GCppFormatLibraryOptimizedResult, "{0} * {1} * {2} * {3} * {4} * {5}", 215, "alpha", 21.52, 314, "beta", 31.45); 118 | FL_STD_FORMAT_TO(GCppFormatLibraryOptimizedResult, "{0} / {1} / {2} / {3} / {4} / {5}", 516, "gamma", 51.67, 617, "delta", 61.78); 119 | FL_STD_FORMAT_TO(GCppFormatLibraryOptimizedResult, "{0} ^ {1} ^ {2} ^ {3} ^ {4} ^ {5}", 718, "epsilon", 71.89, 819, "zeta", 81.90); 120 | FL_STD_FORMAT_TO(GCppFormatLibraryOptimizedResult, "{0} % {1} % {2} % {3} % {4} % {5}", 920, "eta", 92.01, 101, "theta", 10.12); 121 | FL_STD_FORMAT_TO(GCppFormatLibraryOptimizedResult, "{0} & {1} & {2} & {3} & {4} & {5}", 122, "iota", 12.23, 233, "kappa", 23.34); 122 | FL_STD_FORMAT_TO(GCppFormatLibraryOptimizedResult, "{0} | {1} | {2} | {3} | {4} | {5}", 344, "lambda", 34.45, 455, "mu", 45.56); 123 | FL_STD_FORMAT_TO(GCppFormatLibraryOptimizedResult, "{0} ~ {1} ~ {2} ~ {3} ~ {4} ~ {5}", 566, "nu", 56.67, 677, "xi", 67.78); 124 | 125 | // Padding and alignment 126 | FL_STD_FORMAT_TO(GCppFormatLibraryOptimizedResult, "{0,5}", 123); 127 | FL_STD_FORMAT_TO(GCppFormatLibraryOptimizedResult, "{0,-5}", 123); 128 | FL_STD_FORMAT_TO(GCppFormatLibraryOptimizedResult, "{0,6}", -123); 129 | FL_STD_FORMAT_TO(GCppFormatLibraryOptimizedResult, "{0,-6}", -123); 130 | FL_STD_FORMAT_TO(GCppFormatLibraryOptimizedResult, "{0}", "\n\t\""); 131 | FL_STD_FORMAT_TO(GCppFormatLibraryOptimizedResult, "{0:e}", 123.456); 132 | FL_STD_FORMAT_TO(GCppFormatLibraryOptimizedResult, "{0:E}", 123.456); 133 | FL_STD_FORMAT_TO(GCppFormatLibraryOptimizedResult, "{0:e3}", 123.456); 134 | FL_STD_FORMAT_TO(GCppFormatLibraryOptimizedResult, "{0:E3}", 123.456); 135 | FL_STD_FORMAT_TO(GCppFormatLibraryOptimizedResult, "{0:x}", 123); 136 | FL_STD_FORMAT_TO(GCppFormatLibraryOptimizedResult, "{0:X}", 123); 137 | FL_STD_FORMAT_TO(GCppFormatLibraryOptimizedResult, "{0:x8}", 123); 138 | FL_STD_FORMAT_TO(GCppFormatLibraryOptimizedResult, "{0:X8}", 123); 139 | FL_STD_FORMAT_TO(GCppFormatLibraryOptimizedResult, "{0,5}", "hi"); 140 | FL_STD_FORMAT_TO(GCppFormatLibraryOptimizedResult, "{0,-5}", "hi"); 141 | } 142 | 143 | constexpr int SamplesCount = 10; 144 | 145 | #if FL_DEBUG 146 | constexpr int IterationsCount = 100; 147 | #else 148 | constexpr int IterationsCount = 10000; 149 | #endif 150 | 151 | BASELINE(StringFormat, Baseline, SamplesCount, IterationsCount) 152 | { 153 | test_c_sprintf(); 154 | } 155 | 156 | #if FL_COMPILER_IS_GREATER_THAN_CXX20 157 | BENCHMARK(StringFormat, StdFormat, SamplesCount, IterationsCount) 158 | { 159 | test_std_format(); 160 | } 161 | #endif 162 | 163 | 164 | BENCHMARK(StringFormat, Common, SamplesCount, IterationsCount) 165 | { 166 | test_cpp_format_library_common(); 167 | } 168 | 169 | BENCHMARK(StringFormat, Optimized, SamplesCount, IterationsCount) 170 | { 171 | test_cpp_format_library_optimized(); 172 | } 173 | 174 | char GCommonAlgorithmBuffer[0xFF]; 175 | 176 | void test_formatting_numeric_to_string() 177 | { 178 | Formatting::Details::IntegerToString(-1234567890123456789LL, GCommonAlgorithmBuffer, FL_ARRAY_COUNTOF(GCommonAlgorithmBuffer), false); 179 | Formatting::Details::IntegerToString(1234567890123456789ULL, GCommonAlgorithmBuffer, FL_ARRAY_COUNTOF(GCommonAlgorithmBuffer), false); 180 | Formatting::Details::DoubleToString(123.456, GCommonAlgorithmBuffer, FL_ARRAY_COUNTOF(GCommonAlgorithmBuffer), 2); 181 | } 182 | 183 | void test_c_sprintf_numeric_to_string() 184 | { 185 | Formatting::TCharTraits::StringPrintf(GCommonAlgorithmBuffer, "%lld", -1234567890123456789LL); 186 | Formatting::TCharTraits::StringPrintf(GCommonAlgorithmBuffer, "%llu", 1234567890123456789ULL); 187 | Formatting::TCharTraits::StringPrintf(GCommonAlgorithmBuffer, "%.2f", 123.456); 188 | } 189 | 190 | BASELINE(Algorithm, StringPrintf, SamplesCount, IterationsCount) 191 | { 192 | test_c_sprintf_numeric_to_string(); 193 | } 194 | 195 | BENCHMARK(Algorithm, Formmating, SamplesCount, IterationsCount) 196 | { 197 | test_formatting_numeric_to_string(); 198 | } 199 | 200 | #pragma message(FL_CXX_STANDARD) 201 | 202 | int main(int argc, char** argv) 203 | { 204 | std::cout << "C++ Version:" << FL_CXX_STANDARD << std::endl; // NOLINT(performance-avoid-endl) 205 | 206 | const char* StepArgv[] = { argv[0], "-g", "Algorithm"}; 207 | const char* StepArgv2[] = { argv[0], "-g", "StringFormat" }; 208 | 209 | celero::Run(FL_ARRAY_COUNTOF(StepArgv), (char**)StepArgv); 210 | 211 | celero::Run(FL_ARRAY_COUNTOF(StepArgv2), (char**)StepArgv2); 212 | } 213 | -------------------------------------------------------------------------------- /Format/Details/FormatTo.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2024 CPPStringFormatting 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | Project URL: https://github.com/bodong1987/CPPStringFormatting 25 | */ 26 | // ReSharper disable CppRedundantInlineSpecifier 27 | #pragma once 28 | 29 | #include 30 | #include 31 | 32 | // ReSharper disable once CppEnforceNestedNamespacesStyle 33 | namespace Formatting // NOLINT(*-concat-nested-namespaces) 34 | { 35 | namespace Details 36 | { 37 | // calculate byte array hash code 38 | FL_CONSTEXPR14 inline size_t CalculateByteArrayHash(const uint8_t* const start, const size_t length) 39 | { 40 | #if FL_PLATFORM_X64 41 | FL_STATIC_ASSERT(sizeof(void*) == 8, "This code is for 64-bit pointer."); 42 | 43 | constexpr size_t FNVOffsetBasis = 14695981039346656037ULL; 44 | 45 | // ReSharper disable once CppTooWideScope 46 | constexpr size_t FNVPrime = 1099511628211ULL; 47 | 48 | #else 49 | FL_STATIC_ASSERT(sizeof(void*) == 4, "This code is for 32-bit pointer."); 50 | 51 | constexpr size_t FNVOffsetBasis = 2166136261U; 52 | 53 | // ReSharper disable once CppTooWideScope 54 | constexpr size_t FNVPrime = 16777619U; 55 | #endif 56 | 57 | size_t Value = FNVOffsetBasis; 58 | size_t Next = 0; 59 | 60 | for (; Next + sizeof(size_t) <= length; Next += sizeof(size_t)) 61 | { 62 | // fold in another byte 63 | Value ^= *reinterpret_cast(&start[Next]); 64 | Value *= FNVPrime; 65 | } 66 | 67 | // Process remaining bytes 68 | for (; Next < length; ++Next) 69 | { 70 | Value ^= static_cast(start[Next]); 71 | Value *= FNVPrime; 72 | } 73 | 74 | #if FL_PLATFORM_X64 75 | Value ^= Value >> 32; 76 | #endif 77 | 78 | return Value; 79 | } 80 | 81 | #if FL_COMPILER_IS_GREATER_THAN_CXX11 82 | // calculate constexpr string length 83 | // 84 | template < typename TCharType> 85 | #if FL_COMPILER_IS_GREATER_THAN_CXX20 86 | consteval 87 | #else 88 | FL_CONSTEXPR14 89 | #endif 90 | inline size_t CalculateConstexprStringLength(const TCharType* str) 91 | { 92 | #if FL_COMPILER_IS_GREATER_THAN_CXX17 93 | return std::char_traits::length(str); 94 | #else 95 | size_t Count = 0; 96 | while (*str != TCharType()) 97 | { 98 | ++Count; 99 | ++str; 100 | } 101 | 102 | return Count; 103 | #endif // FL_COMPILER_IS_GREATER_THAN_CXX17 104 | } 105 | #endif 106 | 107 | #if FL_COMPILER_IS_GREATER_THAN_CXX11 108 | namespace Utils 109 | { 110 | template 111 | struct DoTransferHelper 112 | { 113 | static bool DoTransfer(TAutoString& sink, const TPatternType& pattern, const TCharType* format, const T0& arg0, const T&... args) 114 | { 115 | if (pattern.Index == Index) 116 | { 117 | typedef typename Mpl::IfElse< 118 | Mpl::IsArray::Value, 119 | const typename Mpl::RemoveArray::Type*, 120 | T0 121 | >::Type TransferType; 122 | 123 | if (!TTranslator::Transfer(sink, pattern, arg0)) 124 | { 125 | TRawTranslator::Transfer(sink, pattern, format); 126 | } 127 | 128 | return true; 129 | } 130 | 131 | return DoTransferHelper::DoTransfer(sink, pattern, format, args...); 132 | } 133 | }; 134 | 135 | template 136 | struct DoTransferHelper 137 | { 138 | static bool DoTransfer(TAutoString& sink, const TPatternType& pattern, const TCharType* format, const T0& arg0) 139 | { 140 | if (pattern.Index == Index) 141 | { 142 | typedef typename Mpl::IfElse< 143 | Mpl::IsArray::Value, 144 | const typename Mpl::RemoveArray::Type*, 145 | T0 146 | >::Type TransferType; 147 | 148 | /* 149 | // if you get a compile error with Transfer function can't visit 150 | // it means that you have transfer an unsupported parameter to format pipeline 151 | // you can do them to fix this error: 152 | // 1. change your code, convert it to the support type 153 | // 2. make a specialization of TTranslator for your type. 154 | */ 155 | if (!TTranslator::Transfer(sink, pattern, arg0)) 156 | { 157 | TRawTranslator::Transfer(sink, pattern, format); 158 | } 159 | 160 | return true; 161 | } 162 | 163 | return false; 164 | } 165 | }; 166 | 167 | template 168 | inline bool DoTransfer(TAutoString& sink, const TPatternType& pattern, const TCharType* format, const T0& arg0, const T&... args) 169 | { 170 | return DoTransferHelper::DoTransfer(sink, pattern, format, arg0, args...); 171 | } 172 | 173 | template // NOLINT 174 | inline bool DoTransfer(TAutoString& sink, const TPatternType& pattern, const TCharType* format) 175 | { 176 | return TRawTranslator::Transfer(sink, pattern, format); 177 | } 178 | } 179 | 180 | /// 181 | /// Formats to. 182 | /// format params to buffer 183 | /// 184 | /// The sink. 185 | /// patterns 186 | /// The format. 187 | /// format length 188 | /// The arguments. 189 | /// TAutoString<TCharType&. 190 | template 191 | inline TAutoString& FormatTo(TAutoString& sink, const TPatternListType* patterns, const TCharType* format, const size_t length, const T&... args) 192 | { 193 | if (patterns == nullptr) 194 | { 195 | sink.AddStr(format, length); 196 | 197 | return sink; 198 | } 199 | 200 | typename TPatternListType::ConstIterator Iter(*patterns); 201 | 202 | while (Iter.IsValid()) 203 | { 204 | // ReSharper disable once CppTooWideScopeInitStatement 205 | const typename TPatternListType::ConstIterator::ValueType& Pattern = *Iter; 206 | 207 | if (Pattern.Flag == EFormatFlag::Raw || 208 | !Utils::DoTransfer(sink, Pattern, format, args...) 209 | ) 210 | { 211 | TRawTranslator::Transfer(sink, Pattern, format); 212 | } 213 | 214 | Iter.Next(); 215 | } 216 | 217 | return sink; 218 | } 219 | 220 | /// 221 | /// Formats to. 222 | /// format params to buffer 223 | /// 224 | /// The sink. 225 | /// The format. 226 | /// The arguments. 227 | /// TAutoString<TCharType&. 228 | template 229 | inline TAutoString& FormatTo(TAutoString& sink, const TFormatType& format, const T&... args) 230 | { 231 | TPatternStorageType* Storage = TPatternStorageType::GetStorage(); 232 | 233 | assert(Storage); 234 | 235 | // find patterns first 236 | const TCharType* localFormatText = Shims::PtrOf(format); 237 | const size_t localLength = Shims::LengthOf(format); 238 | 239 | const typename TPatternStorageType::PatternListType* Patterns = Storage->LookupPatterns( 240 | localFormatText, 241 | localLength, 242 | CalculateByteArrayHash(reinterpret_cast(localFormatText), localLength*sizeof(TCharType)) 243 | ); 244 | 245 | assert(Patterns); 246 | 247 | return FormatTo(sink, Patterns, localFormatText, localLength, args...); 248 | } 249 | #else 250 | #define FL_FORMAT_TO_INDEX 0 251 | #include 252 | #undef FL_FORMAT_TO_INDEX 253 | 254 | #define FL_FORMAT_TO_INDEX 1 255 | #include 256 | #undef FL_FORMAT_TO_INDEX 257 | 258 | #define FL_FORMAT_TO_INDEX 2 259 | #include 260 | #undef FL_FORMAT_TO_INDEX 261 | 262 | #define FL_FORMAT_TO_INDEX 3 263 | #include 264 | #undef FL_FORMAT_TO_INDEX 265 | 266 | #define FL_FORMAT_TO_INDEX 4 267 | #include 268 | #undef FL_FORMAT_TO_INDEX 269 | 270 | #define FL_FORMAT_TO_INDEX 5 271 | #include 272 | #undef FL_FORMAT_TO_INDEX 273 | 274 | #define FL_FORMAT_TO_INDEX 6 275 | #include 276 | #undef FL_FORMAT_TO_INDEX 277 | 278 | #define FL_FORMAT_TO_INDEX 7 279 | #include 280 | #undef FL_FORMAT_TO_INDEX 281 | 282 | #define FL_FORMAT_TO_INDEX 8 283 | #include 284 | #undef FL_FORMAT_TO_INDEX 285 | 286 | #define FL_FORMAT_TO_INDEX 9 287 | #include 288 | #undef FL_FORMAT_TO_INDEX 289 | 290 | #define FL_FORMAT_TO_INDEX 10 291 | #include 292 | #undef FL_FORMAT_TO_INDEX 293 | 294 | #define FL_FORMAT_TO_INDEX 11 295 | #include 296 | #undef FL_FORMAT_TO_INDEX 297 | 298 | #define FL_FORMAT_TO_INDEX 12 299 | #include 300 | #undef FL_FORMAT_TO_INDEX 301 | 302 | #define FL_FORMAT_TO_INDEX 13 303 | #include 304 | #undef FL_FORMAT_TO_INDEX 305 | 306 | #define FL_FORMAT_TO_INDEX 14 307 | #include 308 | #undef FL_FORMAT_TO_INDEX 309 | 310 | #define FL_FORMAT_TO_INDEX 15 311 | #include 312 | #undef FL_FORMAT_TO_INDEX 313 | #endif 314 | 315 | // 316 | // If you want to support more parameters, you can continue to increase them here, but too large a number may cause the compiler to crash during compilation. 317 | // 318 | } 319 | } 320 | 321 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CPPStringFormatting 2 | 这是一个使用C#格式化字符串风格来格式化C++字符串的库,它是类型安全的、多线程安全、可乱序、高效的的格式化库。 3 | This is a library that uses the C# formatted string style to format C++ strings. It is a type-efficient, multi-thread-safe, reorderable formatting library. 4 | 5 | 本项目支持几乎常见所有主流编译,无论有无C++ 11支持都可以。我使用Visual Studio 2008/2010/2022、CodeBlock with gcc、xcode 15对项目进行了测试,并通过了测试。 6 | This project supports almost all common mainstream compilations, whether with or without C++11 support. I tested the project using Visual Studio 2008/2010/2022, CodeBlock with gcc, xcode 15 and it passed. 7 | 8 | ## 如何使用? How to use? 9 | 这是一个纯C++头文件项目,clone项目,然后将项目根目录添加到头文件搜索路径中并包含`Format/Format.hpp`即可。如果你是要配合C++标准库使用,那么直接包含头文件`Format/StandardLibraryAdapter.hpp`也是可以的。 10 | 11 | This is a pure C++ header file project, clone the project, then add the project root directory to the header file search path and include `Format/Format.hpp`.If you want to use it with the C++ standard library, you can also directly include the header file `Format/StandardLibraryAdapter.hpp`. 12 | 13 | ## 如何测试? How to Test? 14 | 请先Clone本项目,然后使用CMake生成项目即可测试。如果你安装CMake时将其添加到了命令行,在Windows下还可以直接使用`generate_projects.bat`来生成项目。 15 | 需要注意的一点是本项目依赖于googletests,所以你在clone之后,需要使用如下指令确保googletests的submodule正确被拉取下来了,否则CMake生成项目时会出错。 16 | 17 | ```bat 18 | git submodule init 19 | git submodule update 20 | ``` 21 | 22 | Please clone this project first, and then use CMake to generate the project for testing. If you added it to the command line when installing CMake, you can also use `generate_projects.bat` directly to generate projects under Windows. 23 | One thing to note is that this project depends on googletests, so after cloning, you need to use the following instructions to ensure that the submodule of googletests is correctly pulled down, otherwise CMake will make an error when generating the project. 24 | 25 | 另外如果你想要测试的C++编译器版本很早,比如不支持C++ 11的`Visual Studio 2008`,那么你可能需要手动将googletests切换到更早的tag上,比如`1.8.x`。 26 | In addition, if the C++ compiler version you want to test is very early, such as Visual Studio 2008 that does not support C++ 11, then you may need to manually switch googletests to an earlier tag, such as `1.8.x`. 27 | 28 | 生成项目后,使用IDE打开生成的项目文件即可,编译和运行`UniTests`项目即可开始测试。`UnitTests_Format.cpp`中代码即是针对`std::basic_string`的使用案例。你可以参考StandardLibraryAdapter.hpp的代码为你自己的字符串类添加适配器,即可让这套系统配合你的字符串类进行工作。 29 | 30 | After generating the project, just use the IDE to open the generated project file, compile and run the `UniTests` project to start testing. The code in `UnitTests_Format.cpp` is a use case for `std::basic_string`. You can refer to the code of StandardLibraryAdapter.hpp to add an adapter to your own string class, so that this system can work with your string class. 31 | 32 | ## 代码片段 Code Segment 33 | 这里简单展示一下代码风格和用法,你可以在`UnitTests_Format.cpp`中找到更多用法和案例。 34 | 35 | Here is a brief demonstration of the code style and usage. You can find more usage and cases in `UnitTests_Format.cpp`. 36 | 37 | ```C++ 38 | #include 39 | #include 40 | 41 | using namespace Formatting; 42 | 43 | TEST(Format, STL_Char_Format) 44 | { 45 | const std::string i0 = "Hello CppMiniToolkit"; 46 | std::string r0 = StandardLibrary::Format(i0.c_str()); 47 | EXPECT_EQ(r0, i0); 48 | 49 | const std::string i1 = "Hello CppMiniToolkit {0}"; 50 | std::string r1 = StandardLibrary::Format(i1.c_str(), 1024); 51 | EXPECT_EQ(r1, "Hello CppMiniToolkit 1024"); 52 | 53 | const std::string r2 = StandardLibrary::Format("{0}--#--{1,8}--#--{2}", 100, -40.2f, " String "); 54 | EXPECT_EQ(r2, "100--#-- -40.20--#-- String "); 55 | 56 | const std::string r3 = StandardLibrary::Format("{0}--#--{1,8}--#--{1}", 100, -40.2f); 57 | EXPECT_EQ(r3, "100--#-- -40.20--#---40.20"); 58 | 59 | const std::string r4 = StandardLibrary::Format("{0}--#--{1,8}--#--{3}", 100, -40.2f, std::string("xxx")); 60 | EXPECT_EQ(r4, "100--#-- -40.20--#--{3}"); 61 | 62 | const std::string r5 = StandardLibrary::Format("{0}", char('c'), short(2)); 63 | EXPECT_EQ(r5, "c"); 64 | 65 | const std::string r6 = StandardLibrary::Format("0x{0:x}", 100, (unsigned long)(100)); 66 | EXPECT_EQ(r6, "0x64"); 67 | 68 | // gen compile error 69 | //StandardLibrary::Format("{0}", std::wstring(L"x")); 70 | } 71 | 72 | TEST(Format, STL_WChar_Format) 73 | { 74 | const std::wstring r7 = StandardLibrary::Format(L"Test{1}, {2:f4}, {0}, {0,4}", L" X ", 20, -10.005f); 75 | EXPECT_EQ(r7, L"Test20, -10.0050, X , X "); 76 | 77 | const std::wstring r8 = StandardLibrary::Format(L"Test{1}, {2:f4}, {0}, {0,4}"); 78 | EXPECT_EQ(r8, L"Test{1}, {2:f4}, {0}, {0,4}"); 79 | 80 | const std::wstring r9 = StandardLibrary::Format(std::wstring(L"Test{1}, {2:f4}, {0}, {0,4}"), L" X ", 20, -10.005f); 81 | EXPECT_EQ(r9, L"Test20, -10.0050, X , X "); 82 | 83 | const std::wstring r11 = StandardLibrary::Format(L"\u4F60\u597D : {0}", L"\u4E2D\u6587"); 84 | EXPECT_EQ(r11, L"\u4F60\u597D : \u4E2D\u6587"); 85 | } 86 | 87 | TEST(Format, STL_WChar_FormatTo) 88 | { 89 | std::wstring v; 90 | 91 | StandardLibrary::FormatTo(v, L"Test{1}, {2:f4}, {0}, {0,4}", L" X ", 20, -10.005f); 92 | EXPECT_EQ(v, L"Test20, -10.0050, X , X "); 93 | 94 | // test invalid param 95 | StandardLibrary::FormatTo(v, L"Test{1}, {2:f4}, {0}, {0,4}"); 96 | EXPECT_EQ(v, L"Test{1}, {2:f4}, {0}, {0,4}"); 97 | 98 | StandardLibrary::FormatTo(v, std::wstring(L"Test{1}, {2:f4}, {0}, {0,4}"), L" X ", 20, -10.005f); 99 | EXPECT_EQ(v, L"Test20, -10.0050, X , X "); 100 | 101 | StandardLibrary::FormatTo(v, L"\u4F60\u597D : {0}", L"\u4E2D\u6587"); 102 | EXPECT_EQ(v, L"\u4F60\u597D : \u4E2D\u6587"); 103 | } 104 | ``` 105 | 项目为C++ 11做了针对性优化,如果你开启了C++ 11支持,那么你可以考虑使用下面的宏,这样将获得更快的执行速度: 106 | The project has been specifically optimized for C++ 11. If you enable C++ 11 support, you may consider using the following macros, which will result in faster execution: 107 | 108 | ```C++ 109 | #ifndef FL_DISABLE_STANDARD_LIBARY_MACROS 110 | TEST(Format, STL_Char_Format_FL_STD_FORMAT) 111 | { 112 | const std::string r2 = FL_STD_FORMAT("{0}--#--{1,8}--#--{2}", 100, -40.2f, " String "); 113 | EXPECT_EQ(r2, "100--#-- -40.20--#-- String "); 114 | 115 | const std::string r3 = FL_STD_FORMAT("{0}--#--{1,8}--#--{1}", 100, -40.2f); 116 | EXPECT_EQ(r3, "100--#-- -40.20--#---40.20"); 117 | 118 | const std::string r4 = FL_STD_FORMAT("{0}--#--{1,8}--#--{3}", 100, -40.2f, std::string("xxx")); 119 | EXPECT_EQ(r4, "100--#-- -40.20--#--{3}"); 120 | 121 | const std::string r5 = FL_STD_FORMAT("{0}", char('c'), short(2)); 122 | EXPECT_EQ(r5, "c"); 123 | 124 | const std::string r6 = FL_STD_FORMAT("0x{0:x}", 100, (unsigned long)(100)); 125 | EXPECT_EQ(r6, "0x64"); 126 | 127 | // can't compile 128 | // FL_CONSTEXPR20 const char* fmt = "0x{0:x}"; 129 | // const std::string r7 = FL_STD_FORMAT(fmt, 100, (unsigned long)(100)); 130 | // EXPECT_EQ(r6, "0x64"); 131 | 132 | // can't compile 133 | // const char* fmt = "0x{0:x}"; 134 | // const std::string r7 = FL_STD_FORMAT(fmt, 100, (unsigned long)(100)); 135 | // EXPECT_EQ(r6, "0x64"); 136 | } 137 | #endif 138 | ``` 139 | 注意:使用宏时格式化字符串只能是字符串常量,无论是constexpr表达式还是`static const char*`等都不被支持。这是因为该宏会在原地创建一个格式化字符串的静态TFormatPattern,只有使用这种方式才能保证TFormatPattern不会被错误的应用到错误的参数上。 140 | Note: When using macros, the formatted string can only be string constants. Neither constexpr expressions nor `static const char*` are supported. This is because this macro will create a static TFormatPattern of the formatted string in place. Only by using this method can we ensure that TFormatPattern will not be mistakenly applied to the wrong parameters. 141 | 142 | 143 | ## 如何集成? How to integrated 144 | 想要将格式化库适配你自己的字符串类或者使用你自己的容器类来接管格式化库内部的容器,那么你只需要做三件事: 145 | * 第一,实现自己的Policy类,这个类需要告知框架必须要的基础类型都是什么,这通过typedef来实现;并且你还需要实现几个基础接口即可:`FindByHashKey`、`ReserveList`、`AppendPattern`。 146 | * 第二,你需要在命名空间`Formatting::Shims`下实现两个垫片函数`PtrOf`和`LengthOf`,这两个函数是针对你的字符串类的重载。 147 | * 第三,你需要实现自己的`Format`函数。如果你使用C++ 11或更新标准,那么你只需要使用不定参数模板即可;如果你要支持C++ 11更早的版本,你需要用到一些宏技巧来生成支持多个参数的`Format`函数。 148 | 你可以参考`UnitTests/Sources/MFCAdapter.hpp`的源代码,这里展示了如何为MFC字符串提供Format支持。当然也可以参考`Format/StandardLibraryAdapter.hpp`,这里是针对`stl::basic_string`的适配代码。 149 | 通过这几个简单的适配器,你就可以将这个字符串格式化库用到你自己的类型上了。 150 | 151 | If you want to adapt the formatting library to your own string class or use your own container class to take over the container inside the formatting library, then you only need to do three things: 152 | * First, implement your own Policy class. This class needs to tell the framework what the basic types are. This is achieved through typedef; and you also need to implement several basic interfaces: `FindByHashKey`, `ReserveList`, `AppendPattern`. 153 | * Second, you need to implement two shim functions `PtrOf` and `LengthOf` under the namespace `Formatting::Shims`. These two functions are overloaded for your string class. 154 | * Third, you need to implement your own `Format` function. If you use C++11 or newer standards, then you only need to use indefinite parameter templates; if you want to support earlier versions of C++11, you need to use some macro tricks to generate a `Format` function that supports multiple parameters. 155 | You can refer to the source code of `UnitTests/Sources/MFCAdapter.hpp`, which shows how to provide Format support for MFC strings. Of course, you can also refer to `Format/StandardLibraryAdapter.hpp`, here is the adaptation code for `stl::basic_string`. 156 | With these simple adapters, you can apply this string formatting library to your own types. 157 | 158 | ## 如何扩展? How to extends? 159 | CPPStringFormatting本身是类型安全的系统,如果你尝试将不支持的类型提交给格式化函数,那么你将会在编译阶段得到一个编译错误。要解决这个错误,你有两种办法: 160 | * 第一,将参数转换成受支持的类型。 161 | * 第二,提供一个针对该类型的自定义Translator,这样框架将能自动帮你完成转换操作。 162 | 你可以在`UnitTests/Sources/UnityTests_Extends.cpp`中找到扩展的方法和举例。 163 | 164 | CPPStringFormatting itself is a type-safe system, if you try to submit an unsupported type to the formatting function, then you will get a compilation error during the compilation phase. To resolve this error, you have two options: 165 | * First, convert the parameters to a supported type. 166 | * Second, provide a custom Translator for this type, so that the framework will automatically complete the conversion operation for you. 167 | You can find extension methods and examples in `UnitTests/Sources/UnityTests_Extends.cpp`. 168 | 169 | ```C++ 170 | // The following class provides automatic conversion capabilities for Vector3, so that Vector3 type parameters can be formatted directly. 171 | namespace Formatting 172 | { 173 | namespace Details 174 | { 175 | // convert Vector3 to string 176 | template <> 177 | class TTranslator< char, Vector3 > : 178 | public TTranslatorBase< char, Vector3 > 179 | { 180 | public: 181 | typedef TTranslatorBase< char, Vector3 > Super; 182 | typedef Super::CharType CharType; 183 | typedef Super::FormatPattern FormatPattern; 184 | typedef Super::ByteType ByteType; 185 | typedef Super::SizeType SizeType; 186 | typedef Super::StringType StringType; 187 | typedef Super::CharTraits CharTraits; 188 | 189 | static bool Transfer(StringType& strRef, const FormatPattern& pattern, const Vector3& arg) 190 | { 191 | std::string text = arg.ToString(); 192 | 193 | Super::AppendString(strRef, pattern, text.c_str(), text.size()); 194 | 195 | return true; 196 | } 197 | }; 198 | 199 | // convert Vector3 to wstring 200 | template <> 201 | class TTranslator< wchar_t, Vector3 > : 202 | public TTranslatorBase< wchar_t, Vector3 > 203 | { 204 | public: 205 | typedef TTranslatorBase< wchar_t, Vector3 > Super; 206 | typedef Super::CharType CharType; 207 | typedef Super::FormatPattern FormatPattern; 208 | typedef Super::ByteType ByteType; 209 | typedef Super::SizeType SizeType; 210 | typedef Super::StringType StringType; 211 | typedef Super::CharTraits CharTraits; 212 | 213 | static bool Transfer(StringType& strRef, const FormatPattern& pattern, const Vector3& arg) 214 | { 215 | std::wstring text = arg.ToWString(); 216 | 217 | Super::AppendString(strRef, pattern, text.c_str(), text.size()); 218 | 219 | return true; 220 | } 221 | }; 222 | } 223 | } 224 | ``` 225 | 226 | ## 差异 Difference 227 | 虽然CPPStringFormatting支持了大部分常见的格式化需求,但并也有许多C#格式化的功能尚不支持。具体C#支持的格式化规范请参考这里: https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings 228 | 229 | Although CPPStringFormatting supports most common formatting needs, there are also many C# formatting functions that are not yet supported. For specific formatting specifications supported by C#, please refer here: 230 | https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings 231 | 232 | 233 | | Type | Status 234 | | :---- | :------: 235 | | B | ✔ 236 | | C | ✖ 237 | | D | ✔ 238 | | E | ✔ 239 | | F | ✔ 240 | | G | ✖ 241 | | N | ✖ 242 | | P | ✖ 243 | | R | ✖ 244 | | X | ✔ 245 | | Percision | ✔ 246 | | Width | ✔ 247 | 248 | 遇到不支持的格式标志符时C#会抛出异常,CPPStringFormatting会直接输出格式化字符串,一些情况会触发assert或忽略。因此当你遇到与预期不一致的问题时,请检查格式标志符是否正确,或者是否使用了不被支持的标志符。 249 | 250 | C# will throw an exception when encountering an unsupported format identifier, and CPPStringFormatting will directly output the format string. In some cases, assert or ignore will be triggered. So when you encounter problems that are inconsistent with expectations, please check whether the format identifier is correct, or whether an unsupported identifier is used. 251 | 252 | ## 性能测试 Performance 253 | 使用CMake生成了项目后,你可以启动Benchmark项目来进行性能测试。性能测试基于Celero,所以目前这个项目无法支持C++ 11之前的版本了。不同的C++标准版本的性能是有差异的,一般来说C++版本越新,性能会更高。 254 | 255 | After using CMake to generate the project, you can start the Benchmark project for performance testing. The performance test is based on Celero, so this project currently cannot support versions before C++ 11. The performance of different C++ standard versions is different. Generally speaking, the newer the C++ version, the higher the performance will be. 256 | 257 | ## 提交错误报告 Bugreport 258 | 直接通过[Issues](https://github.com/bodong1987/CPPStringFormatting/issues)页面提交即可 259 | 260 | Submit directly through the [Issues](https://github.com/bodong1987/CPPStringFormatting/issues) page 261 | -------------------------------------------------------------------------------- /Format/Common/AutoArray.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2024 CPPStringFormatting 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | Project URL: https://github.com/bodong1987/CPPStringFormatting 25 | */ 26 | // ReSharper disable CppDFAUnreachableCode 27 | #pragma once 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | namespace Formatting 35 | { 36 | /// 37 | /// Class TAutoArray. 38 | /// a dynamic array can be place on stack memory if the count is less than DefaultLength 39 | /// 40 | template < 41 | typename T, 42 | int32_t DefaultLength = 0xFF, 43 | int32_t ExtraLength = 0 44 | > 45 | class TAutoArray 46 | { 47 | public: 48 | typedef TAutoArray SelfType; // NOLINT 49 | typedef T ValueType; // NOLINT 50 | 51 | enum // NOLINT(performance-enum-size) 52 | { 53 | DEFAULT_LENGTH = DefaultLength // NOLINT 54 | }; 55 | 56 | class ConstIterator : Noncopyable 57 | { 58 | public: 59 | typedef T ValueType; 60 | 61 | explicit ConstIterator(const SelfType& referenceTarget) : 62 | Ref(referenceTarget), 63 | Index(referenceTarget.GetLength() > 0 ? 0 : -1) 64 | { 65 | } 66 | 67 | bool IsValid() const // NOLINT(modernize-use-nodiscard) 68 | { 69 | return Index < Ref.GetLength(); 70 | } 71 | 72 | void Next() 73 | { 74 | ++Index; 75 | } 76 | 77 | const T& operator *() const 78 | { 79 | const T* Ptr = Ref.GetDataPtr(); 80 | 81 | return Ptr[Index]; 82 | } 83 | protected: 84 | const SelfType& Ref; // NOLINT 85 | size_t Index; 86 | }; 87 | 88 | TAutoArray() : 89 | Count(0), 90 | AllocatedCount(0), 91 | HeapValPtr(nullptr) 92 | 93 | { 94 | } 95 | 96 | ~TAutoArray() 97 | { 98 | ReleaseHeapData(); 99 | 100 | Count = 0; 101 | } 102 | 103 | TAutoArray(const SelfType& other) : 104 | Count(other.Count), 105 | AllocatedCount(other.AllocatedCount), 106 | HeapValPtr(nullptr) 107 | { 108 | if (Count > 0) 109 | { 110 | if (other.IsDataOnStack()) 111 | { 112 | Algorithm::CopyArray(other.StackVal, other.StackVal + Count, StackVal); 113 | } 114 | else 115 | { 116 | HeapValPtr = Allocate(AllocatedCount); 117 | Algorithm::CopyArray(other.HeapValPtr, other.HeapValPtr + Count, HeapValPtr); 118 | } 119 | } 120 | } 121 | 122 | SelfType& operator = (const SelfType& other) // NOLINT 123 | { 124 | if (this == &other) 125 | { 126 | return *this; 127 | } 128 | 129 | ReleaseHeapData(); 130 | 131 | Count = other.Count; 132 | AllocatedCount = other.AllocatedCount; 133 | HeapValPtr = nullptr; 134 | 135 | if (Count > 0) 136 | { 137 | if (other.IsDataOnStack()) 138 | { 139 | Algorithm::CopyArray(other.StackVal, other.StackVal + Count, StackVal); 140 | } 141 | else 142 | { 143 | HeapValPtr = Allocate(AllocatedCount); 144 | Algorithm::CopyArray(other.HeapValPtr, other.HeapValPtr + Count, HeapValPtr); 145 | } 146 | } 147 | 148 | return *this; 149 | } 150 | 151 | SelfType& TakeFrom(SelfType& other) 152 | { 153 | if (this == &other) 154 | { 155 | return *this; 156 | } 157 | 158 | Count = other.Count; 159 | AllocatedCount = other.AllocatedCount; 160 | HeapValPtr = other.HeapValPtr; 161 | 162 | if (Count > 0 && other.IsDataOnStack()) 163 | { 164 | Algorithm::MoveArray(other.StackVal, other.StackVal + Count, StackVal); 165 | } 166 | 167 | other.Count = 0; 168 | other.AllocatedCount = 0; 169 | other.HeapValPtr = nullptr; 170 | 171 | return *this; 172 | } 173 | 174 | #if FL_COMPILER_IS_GREATER_THAN_CXX11 175 | SelfType& TakeFrom(SelfType&& other) noexcept // NOLINT(cppcoreguidelines-rvalue-reference-param-not-moved) 176 | { 177 | if (this == &other) 178 | { 179 | return *this; 180 | } 181 | 182 | Count = other.Count; 183 | AllocatedCount = other.AllocatedCount; 184 | HeapValPtr = other.HeapValPtr; 185 | 186 | if (Count > 0 && other.IsDataOnStack()) 187 | { 188 | Algorithm::MoveArray(other.StackVal, other.StackVal + Count, StackVal); 189 | } 190 | 191 | other.Count = 0; 192 | other.AllocatedCount = 0; 193 | other.HeapValPtr = nullptr; 194 | 195 | return *this; 196 | } 197 | #endif 198 | 199 | void TakeTo(SelfType& other) 200 | { 201 | other.TakeFrom(*this); 202 | } 203 | 204 | #if FL_COMPILER_IS_GREATER_THAN_CXX11 205 | TAutoArray(SelfType&& other) noexcept : 206 | Count(other.Count), 207 | AllocatedCount(other.AllocatedCount), 208 | HeapValPtr(other.HeapValPtr) 209 | { 210 | if (Count > 0 && other.IsDataOnStack()) 211 | { 212 | Algorithm::MoveArray(other.StackVal, other.StackVal + Count, StackVal); 213 | } 214 | 215 | other.Count = 0; 216 | other.AllocatedCount = 0; 217 | other.HeapValPtr = nullptr; 218 | } 219 | 220 | SelfType& operator = (SelfType&& other) noexcept 221 | { 222 | TakeFrom(other); 223 | return *this; 224 | } 225 | #endif 226 | bool IsDataOnStack() const // NOLINT(modernize-use-nodiscard) 227 | { 228 | return HeapValPtr == nullptr; 229 | } 230 | 231 | void AddItem(const T& value) 232 | { 233 | if (IsDataOnStack()) 234 | { 235 | if (Count < DEFAULT_LENGTH) 236 | { 237 | StackVal[Count] = value; 238 | ++Count; 239 | } 240 | else if (Count == DEFAULT_LENGTH) 241 | { 242 | InitialMoveDataToHeap(); 243 | 244 | assert(Count < AllocatedCount); 245 | 246 | HeapValPtr[Count] = value; 247 | ++Count; 248 | } 249 | else 250 | { 251 | assert(false && "internal error"); 252 | } 253 | } 254 | else 255 | { 256 | if (Count < AllocatedCount) 257 | { 258 | HeapValPtr[Count] = value; 259 | ++Count; 260 | } 261 | else 262 | { 263 | ExpandHeapSpace(AllocatedCount * 2); 264 | 265 | assert(Count < AllocatedCount); 266 | HeapValPtr[Count] = value; 267 | ++Count; 268 | } 269 | } 270 | } 271 | 272 | void AddItems(const T* items, size_t length) 273 | { 274 | assert(items != nullptr); 275 | 276 | const size_t TargetCount = Count + length; 277 | 278 | if(IsDataOnStack()) 279 | { 280 | if(TargetCount < DEFAULT_LENGTH) 281 | { 282 | Algorithm::CopyArray(items, items + length, StackVal + Count); 283 | } 284 | else 285 | { 286 | InitialMoveDataToHeap(TargetCount); 287 | 288 | assert(HeapValPtr != nullptr); 289 | assert(TargetCount <= AllocatedCount); 290 | 291 | Algorithm::CopyArray(items, items + length, HeapValPtr + Count); 292 | } 293 | } 294 | else 295 | { 296 | assert(HeapValPtr); 297 | 298 | if(TargetCount < AllocatedCount) 299 | { 300 | Algorithm::CopyArray(items, items + length, HeapValPtr + Count); 301 | } 302 | else 303 | { 304 | ExpandHeapSpace(TargetCount + 0xF); 305 | 306 | Algorithm::CopyArray(items, items + length, HeapValPtr + Count); 307 | } 308 | } 309 | 310 | Count = TargetCount; 311 | } 312 | 313 | FL_NO_DISCARD size_t GetLength() const 314 | { 315 | return Count; 316 | } 317 | 318 | FL_NO_DISCARD size_t GetAllocatedCount() const 319 | { 320 | return AllocatedCount; 321 | } 322 | 323 | T* GetDataPtr() 324 | { 325 | return IsDataOnStack() ? StackVal : HeapValPtr; 326 | } 327 | 328 | const T* GetDataPtr() const // NOLINT(modernize-use-nodiscard) 329 | { 330 | return IsDataOnStack() ? StackVal : HeapValPtr; 331 | } 332 | 333 | T* GetUnusedPtr() 334 | { 335 | return IsDataOnStack() ? StackVal + Count : HeapValPtr + Count; 336 | } 337 | 338 | const T* GetUnusedPtr() const // NOLINT(modernize-use-nodiscard) 339 | { 340 | return IsDataOnStack() ? StackVal + Count : HeapValPtr + Count; 341 | } 342 | 343 | size_t GetCapacity() const // NOLINT(modernize-use-nodiscard) 344 | { 345 | return IsDataOnStack() ? 346 | DEFAULT_LENGTH - Count : 347 | AllocatedCount - Count; 348 | } 349 | 350 | T& operator [](size_t index) 351 | { 352 | assert(index < GetLength()); 353 | 354 | return GetDataPtr()[index]; 355 | } 356 | 357 | const T& operator [](size_t index) const 358 | { 359 | assert(index < GetLength()); 360 | 361 | return GetDataPtr()[index]; 362 | } 363 | 364 | void Shrink() 365 | { 366 | if(AllocatedCount <= Count || HeapValPtr == nullptr) 367 | { 368 | return; 369 | } 370 | 371 | T* DataPtr = Allocate(Count); 372 | assert(DataPtr); 373 | 374 | if(ExtraLength > 0) 375 | { 376 | memset(DataPtr + Count, 0, ExtraLength*sizeof(T)); 377 | } 378 | 379 | #if FL_COMPILER_IS_GREATER_THAN_CXX11 380 | Algorithm::MoveArray(HeapValPtr, HeapValPtr + Count, DataPtr); 381 | #else 382 | Algorithm::CopyArray(HeapValPtr, HeapValPtr + Count, DataPtr); 383 | #endif 384 | 385 | ReleaseHeapData(); 386 | 387 | assert(HeapValPtr == nullptr); 388 | 389 | HeapValPtr = DataPtr; 390 | AllocatedCount = Count; 391 | } 392 | 393 | // ReSharper disable IdentifierTypo 394 | // ReSharper disable CppInconsistentNaming 395 | typedef T* iterator; 396 | typedef const T* const_iterator; 397 | typedef std::reverse_iterator reverse_iterator; 398 | typedef std::reverse_iterator const_reverse_iterator; 399 | 400 | // support range based for 401 | FL_NO_DISCARD T* begin() { return GetDataPtr(); } 402 | FL_NO_DISCARD const T* begin() const { return GetDataPtr(); } 403 | 404 | FL_NO_DISCARD T* end() { return GetDataPtr() + GetLength(); } 405 | FL_NO_DISCARD const T* end() const { return GetDataPtr() + GetLength(); } 406 | 407 | FL_NO_DISCARD std::reverse_iterator rbegin() 408 | { 409 | return std::reverse_iterator(end()); 410 | } 411 | 412 | FL_NO_DISCARD std::reverse_iterator rbegin() const 413 | { 414 | return std::reverse_iterator(end()); 415 | } 416 | 417 | FL_NO_DISCARD std::reverse_iterator rend() 418 | { 419 | return std::reverse_iterator(begin()); 420 | } 421 | 422 | FL_NO_DISCARD std::reverse_iterator rend() const 423 | { 424 | return std::reverse_iterator(begin()); 425 | } 426 | // ReSharper restore IdentifierTypo 427 | // ReSharper restore CppInconsistentNaming 428 | 429 | protected: 430 | void InitialMoveDataToHeap(const size_t initLength = DEFAULT_LENGTH * 2) 431 | { 432 | assert(HeapValPtr == nullptr); 433 | 434 | AllocatedCount = initLength; 435 | 436 | HeapValPtr = Allocate(AllocatedCount); 437 | 438 | #if FL_COMPILER_IS_GREATER_THAN_CXX11 439 | Algorithm::MoveArray(StackVal, StackVal + Count, HeapValPtr); 440 | #else 441 | Algorithm::CopyArray(StackVal, StackVal + Count, HeapValPtr); 442 | #endif 443 | } 444 | 445 | void ExpandHeapSpace(const size_t newCount) 446 | { 447 | const size_t NewCount = newCount <= AllocatedCount ? AllocatedCount * 2 : newCount; 448 | assert(NewCount > AllocatedCount); 449 | 450 | T* DataPtr = Allocate(NewCount); 451 | 452 | assert(DataPtr); 453 | 454 | #if FL_COMPILER_IS_GREATER_THAN_CXX11 455 | Algorithm::MoveArray(HeapValPtr, HeapValPtr + Count, DataPtr); 456 | #else 457 | Algorithm::CopyArray(HeapValPtr, HeapValPtr + Count, DataPtr); 458 | #endif 459 | 460 | ReleaseHeapData(); 461 | 462 | assert(HeapValPtr == nullptr); 463 | 464 | HeapValPtr = DataPtr; 465 | AllocatedCount = NewCount; 466 | } 467 | 468 | void ReleaseHeapData() 469 | { 470 | if (HeapValPtr) 471 | { 472 | delete[] HeapValPtr; 473 | HeapValPtr = nullptr; 474 | } 475 | 476 | AllocatedCount = 0; 477 | } 478 | 479 | static T* Allocate(const size_t allocatedCount) 480 | { 481 | // +ExtraLength this is a hack method for saving string on it. 482 | return new T[allocatedCount + ExtraLength]; 483 | } 484 | 485 | // ReSharper disable once CppRedundantAccessSpecifier 486 | protected: 487 | size_t Count; 488 | size_t AllocatedCount; 489 | T StackVal[DEFAULT_LENGTH + ExtraLength]; 490 | T* HeapValPtr; 491 | }; 492 | } 493 | -------------------------------------------------------------------------------- /Format/Details/StandardLibrary/FormatTo.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2024 CPPStringFormatting 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | Project URL: https://github.com/bodong1987/CPPStringFormatting 25 | */ 26 | // ReSharper disable CppRedundantInlineSpecifier 27 | #pragma once 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | namespace Formatting 34 | { 35 | namespace Details // NOLINT 36 | { 37 | // make a specialization of std::basic_string 38 | template < typename TCharType > 39 | class TTranslator< TCharType, std::basic_string > 40 | : public TTranslatorBase< TCharType, std::basic_string > 41 | { 42 | public: 43 | typedef TTranslatorBase< TCharType, std::basic_string > Super; 44 | 45 | static bool Transfer(typename Super::StringType& s, const typename Super::FormatPattern& /*Pattern*/, const std::basic_string& arg) 46 | { 47 | s.AddStr(arg.c_str(), arg.size()); 48 | 49 | return true; 50 | } 51 | }; 52 | 53 | #if FL_COMPILER_IS_GREATER_THAN_CXX17 54 | // make a specialization of std::basic_string_view 55 | template < typename TCharType > 56 | class TTranslator< TCharType, std::basic_string_view > 57 | : public TTranslatorBase< TCharType, std::basic_string_view > 58 | { 59 | public: 60 | typedef TTranslatorBase< TCharType, std::basic_string_view > Super; 61 | 62 | static bool Transfer(typename Super::StringType& s, const typename Super::FormatPattern& /*Pattern*/, const std::basic_string_view& arg) 63 | { 64 | s.AddStr(arg.data(), arg.size()); 65 | 66 | return true; 67 | } 68 | }; 69 | #endif 70 | } 71 | 72 | namespace StandardLibrary 73 | { 74 | // format with no params 75 | template 76 | inline std::basic_string Format(const TCharType* format) 77 | { 78 | typedef TAutoString SinkType; 79 | typedef Details::TGlobalPatternStorage< Details::StandardLibrary::TStandardPolicy > GlobalPatternStorageType; 80 | 81 | SinkType Sink; 82 | Details::FormatTo(Sink, format); 83 | 84 | return std::basic_string(Sink.CStr(), Sink.GetLength()); 85 | } 86 | 87 | template 88 | inline std::basic_string Format(const std::basic_string& format) 89 | { 90 | typedef TAutoString SinkType; 91 | typedef Details::TGlobalPatternStorage< Details::StandardLibrary::TStandardPolicy > GlobalPatternStorageType; 92 | 93 | SinkType Sink; 94 | Details::FormatTo(Sink, format.c_str()); 95 | 96 | return std::basic_string(Sink.CStr(), Sink.GetLength()); 97 | } 98 | 99 | #if FL_COMPILER_IS_GREATER_THAN_CXX17 100 | template 101 | inline std::basic_string Format(const std::basic_string_view& format) 102 | { 103 | typedef TAutoString SinkType; 104 | typedef Details::TGlobalPatternStorage< Details::StandardLibrary::TStandardPolicy > GlobalPatternStorageType; 105 | 106 | SinkType Sink; 107 | Details::FormatTo(Sink, format.data()); 108 | 109 | return std::basic_string(Sink.CStr(), Sink.GetLength()); 110 | } 111 | #endif 112 | 113 | // default FormatTo support Format with no arguments 114 | inline void FormatTo(std::string& sink, const char* format) 115 | { 116 | sink = Format(format); 117 | } 118 | 119 | // default FormatTo support Format with no arguments 120 | inline void FormatTo(std::wstring& sink, const wchar_t* format) 121 | { 122 | sink = Format(format); 123 | } 124 | 125 | #if FL_COMPILER_IS_GREATER_THAN_CXX11 126 | template 127 | inline std::basic_string Format(const TCharType* format, const T0& arg0, T... args) 128 | { 129 | typedef TAutoString SinkType; 130 | typedef Details::TGlobalPatternStorage< Details::StandardLibrary::TStandardPolicy > GlobalPatternStorageType; 131 | 132 | SinkType Sink; 133 | Details::FormatTo(Sink, format, arg0, args...); 134 | 135 | return std::basic_string(Sink.CStr(), Sink.GetLength()); 136 | } 137 | 138 | template 139 | inline std::basic_string Format(const std::basic_string& format, const T0& arg0, T... args) 140 | { 141 | typedef TAutoString SinkType; 142 | typedef Details::TGlobalPatternStorage< Details::StandardLibrary::TStandardPolicy > GlobalPatternStorageType; 143 | 144 | SinkType Sink; 145 | Details::FormatTo, T0, T...>(Sink, format, arg0, args...); 146 | 147 | return std::basic_string(Sink.CStr(), Sink.GetLength()); 148 | } 149 | 150 | #if FL_COMPILER_IS_GREATER_THAN_CXX17 151 | template 152 | inline std::basic_string Format(const std::basic_string_view& format, const T0& arg0, T... args) 153 | { 154 | typedef TAutoString SinkType; 155 | typedef Details::TGlobalPatternStorage< Details::StandardLibrary::TStandardPolicy > GlobalPatternStorageType; 156 | 157 | SinkType Sink; 158 | Details::FormatTo, T0, T...>(Sink, format, arg0, args...); 159 | 160 | return std::basic_string(Sink.CStr(), Sink.GetLength()); 161 | } 162 | #endif 163 | 164 | template 165 | inline void FormatTo(std::basic_string& sink, const TFormatType& format, const T0& arg0, T... args) 166 | { 167 | sink.clear(); 168 | 169 | typedef TAutoString SinkType; 170 | typedef Details::TGlobalPatternStorage< Details::StandardLibrary::TStandardPolicy > GlobalPatternStorageType; 171 | 172 | SinkType Sink; 173 | Details::FormatTo(Sink, format, arg0, args...); 174 | 175 | sink.assign(Sink.CStr(), Sink.GetLength()); 176 | } 177 | 178 | template 179 | inline std::basic_string Format(const typename Details::StandardLibrary::TStandardPolicy::PatternListType* patterns, const TCharType* format, const size_t length) 180 | { 181 | typedef TAutoString SinkType; 182 | typedef Details::TGlobalPatternStorage< Details::StandardLibrary::TStandardPolicy > GlobalPatternStorageType; 183 | 184 | SinkType Sink; 185 | Details::FormatTo(Sink, patterns, format, length); 186 | 187 | return std::basic_string(Sink.CStr(), Sink.GetLength()); 188 | } 189 | 190 | template 191 | inline std::basic_string Format(const typename Details::StandardLibrary::TStandardPolicy::PatternListType* patterns, const TCharType* format, const size_t length, const T0& arg0, T... args) 192 | { 193 | typedef TAutoString SinkType; 194 | typedef Details::TGlobalPatternStorage< Details::StandardLibrary::TStandardPolicy > GlobalPatternStorageType; 195 | 196 | SinkType Sink; 197 | Details::FormatTo(Sink, patterns, format, length, arg0, args...); 198 | 199 | return std::basic_string(Sink.CStr(), Sink.GetLength()); 200 | } 201 | 202 | template 203 | inline std::basic_string& FormatTo(std::basic_string& sink, const typename Details::StandardLibrary::TStandardPolicy::PatternListType* patterns, const TCharType* format, const size_t length) 204 | { 205 | typedef TAutoString SinkType; 206 | typedef Details::TGlobalPatternStorage< Details::StandardLibrary::TStandardPolicy > GlobalPatternStorageType; 207 | 208 | sink.clear(); 209 | 210 | SinkType Sink; 211 | Details::FormatTo(Sink, patterns, format, length); 212 | 213 | sink.assign(Sink.CStr(), Sink.GetLength()); 214 | 215 | return sink; 216 | } 217 | 218 | template 219 | inline std::basic_string& FormatTo(std::basic_string& sink, const typename Details::StandardLibrary::TStandardPolicy::PatternListType* patterns, const TCharType* format, const size_t length, const T0& arg0, T... args) 220 | { 221 | typedef TAutoString SinkType; 222 | typedef Details::TGlobalPatternStorage< Details::StandardLibrary::TStandardPolicy > GlobalPatternStorageType; 223 | 224 | sink.clear(); 225 | 226 | SinkType Sink; 227 | Details::FormatTo(Sink, patterns, format, length, arg0, args...); 228 | 229 | sink.assign(Sink.CStr(), Sink.GetLength()); 230 | 231 | return sink; 232 | } 233 | #else 234 | #define FL_TEMPLATE_PARAMETERS_BODY( d, i ) \ 235 | FL_PP_COMMA_IF(i) typename FL_PP_CAT(T, i) 236 | 237 | #define FL_TEMPLATE_AGUMENT_BODY( d, i ) \ 238 | FL_PP_COMMA_IF(i) FL_PP_CAT(T, i) 239 | 240 | #define FL_NORMAL_AGUMENT_BODY( d, i ) \ 241 | FL_PP_COMMA_IF(i) const FL_PP_CAT(T, i)& FL_PP_CAT(arg, i) 242 | 243 | #define FL_REAL_ARGUMENT_BODY( d, i ) \ 244 | FL_PP_COMMA_IF(i) FL_PP_CAT(arg, i) 245 | 246 | #define FL_EXPORT_FOR_STRING( i ) \ 247 | template \ 248 | std::string Format(const char* format, FL_PP_REPEAT(i, FL_NORMAL_AGUMENT_BODY, )) \ 249 | { \ 250 | TAutoString Results; \ 251 | Details::FormatTo(Results, format, FL_PP_REPEAT(i, FL_REAL_ARGUMENT_BODY, )); \ 252 | return std::string(Results.CStr(), Results.GetLength()); \ 253 | } \ 254 | template \ 255 | std::wstring Format(const wchar_t* format, FL_PP_REPEAT(i, FL_NORMAL_AGUMENT_BODY, )) \ 256 | { \ 257 | TAutoString Results; \ 258 | Details::FormatTo< wchar_t, Details::StandardLibrary::STLGlobalPatternStorageW, const wchar_t*, FL_PP_REPEAT(i, FL_TEMPLATE_AGUMENT_BODY, )>(Results, format, FL_PP_REPEAT(i, FL_REAL_ARGUMENT_BODY, )); \ 259 | return std::wstring(Results.CStr(), Results.GetLength()); \ 260 | } \ 261 | template \ 262 | std::string Format(const std::string& format, FL_PP_REPEAT(i, FL_NORMAL_AGUMENT_BODY, )) \ 263 | { \ 264 | TAutoString Results; \ 265 | Details::FormatTo(Results, format.c_str(), FL_PP_REPEAT(i, FL_REAL_ARGUMENT_BODY, )); \ 266 | return std::string(Results.CStr(), Results.GetLength()); \ 267 | } \ 268 | template \ 269 | std::wstring Format(const std::wstring& format, FL_PP_REPEAT(i, FL_NORMAL_AGUMENT_BODY, )) \ 270 | { \ 271 | TAutoString Results; \ 272 | Details::FormatTo< wchar_t, Details::StandardLibrary::STLGlobalPatternStorageW, const wchar_t*, FL_PP_REPEAT(i, FL_TEMPLATE_AGUMENT_BODY, )>(Results, format.c_str(), FL_PP_REPEAT(i, FL_REAL_ARGUMENT_BODY, )); \ 273 | return std::wstring(Results.CStr(), Results.GetLength()); \ 274 | } \ 275 | template < typename TFormatType, FL_PP_REPEAT(i, FL_TEMPLATE_PARAMETERS_BODY, ) > \ 276 | void FormatTo(std::string& sink, const TFormatType& format, FL_PP_REPEAT(i, FL_NORMAL_AGUMENT_BODY, )) \ 277 | { \ 278 | sink.clear(); \ 279 | TAutoString Results; \ 280 | Details::FormatTo< char, Details::StandardLibrary::STLGlobalPatternStorageA, const char*, FL_PP_REPEAT(i, FL_TEMPLATE_AGUMENT_BODY, )>(Results, Shims::PtrOf(format), FL_PP_REPEAT(i, FL_REAL_ARGUMENT_BODY, )); \ 281 | sink.append(Results.CStr(), Results.GetLength()); \ 282 | } \ 283 | template < typename TFormatType, FL_PP_REPEAT(i, FL_TEMPLATE_PARAMETERS_BODY, ) > \ 284 | void FormatTo(std::wstring& sink, const TFormatType& format, FL_PP_REPEAT(i, FL_NORMAL_AGUMENT_BODY, )) \ 285 | { \ 286 | sink.clear(); \ 287 | TAutoString Results; \ 288 | Details::FormatTo< wchar_t, Details::StandardLibrary::STLGlobalPatternStorageW, const wchar_t*, FL_PP_REPEAT(i, FL_TEMPLATE_AGUMENT_BODY, )>(Results, Shims::PtrOf(format), FL_PP_REPEAT(i, FL_REAL_ARGUMENT_BODY, )); \ 289 | sink.append(Results.CStr(), Results.GetLength()); \ 290 | } 291 | 292 | // #pragma message( FL_PP_TEXT((FL_EXPORT_FOR_STRING(1))) ) 293 | 294 | // FL_EXPORT_FOR_STRING(1) 295 | 296 | FL_ENUM_SCALARS(FL_EXPORT_FOR_STRING); 297 | 298 | #undef FL_TEMPLATE_PARAMETERS_BODY 299 | #undef FL_TEMPLATE_AGUMENT_BODY 300 | #undef FL_REAL_ARGUMENT_BODY 301 | #undef FL_EXPORT_FOR_STRING 302 | #undef FL_NORMAL_AGUMENT_BODY 303 | #endif 304 | 305 | #ifndef FL_DISABLE_STANDARD_LIBARY_MACROS 306 | #if FL_COMPILER_IS_GREATER_THAN_CXX11 307 | #define FL_STD_FORMAT(format, ...) \ 308 | Formatting::StandardLibrary::Format( \ 309 | [](){ \ 310 | typedef typename std::remove_const::type>::type>::type CharType; \ 311 | static const auto S_Patterns = \ 312 | Formatting::Details::TPatternParser< \ 313 | Formatting::Details::StandardLibrary::TStandardPolicy< \ 314 | CharType, \ 315 | Formatting::Details::StandardLibrary::DefaultMutexType \ 316 | > \ 317 | >::Parse(format, Formatting::Shims::LengthOf(format)); \ 318 | return &S_Patterns; \ 319 | }(), \ 320 | Formatting::Shims::PtrOf(format), \ 321 | [](){ \ 322 | FL_CONSTEXPR14 size_t hash = Formatting::Details::CalculateConstexprStringLength(format); \ 323 | return hash; \ 324 | }(), /*NOLINT(clang-diagnostic-gnu-zero-variadic-macro-arguments)*/\ 325 | ##__VA_ARGS__ /*NOLINT(clang-diagnostic-gnu-zero-variadic-macro-arguments)*/\ 326 | ) 327 | 328 | #define FL_STD_FORMAT_TO(sink, format, ...) \ 329 | Formatting::StandardLibrary::FormatTo( \ 330 | sink, \ 331 | [](){ \ 332 | typedef typename std::remove_const::type>::type>::type CharType; \ 333 | static const auto S_Patterns = \ 334 | Formatting::Details::TPatternParser< \ 335 | Formatting::Details::StandardLibrary::TStandardPolicy< \ 336 | CharType, \ 337 | Formatting::Details::StandardLibrary::DefaultMutexType \ 338 | > \ 339 | >::Parse(format, Formatting::Shims::LengthOf(format)); \ 340 | return &S_Patterns; \ 341 | }(), \ 342 | Formatting::Shims::PtrOf(format), \ 343 | [](){ \ 344 | FL_CONSTEXPR14 size_t hash = Formatting::Details::CalculateConstexprStringLength(format); \ 345 | return hash; \ 346 | }(), /*NOLINT(clang-diagnostic-gnu-zero-variadic-macro-arguments)*/\ 347 | ## __VA_ARGS__ /*NOLINT(clang-diagnostic-gnu-zero-variadic-macro-arguments)*/\ 348 | ) 349 | #else 350 | #define FL_STD_FORMAT Formatting::StandardLibrary::Format 351 | #define FL_STD_FORMAT_TO Formatting::StandardLibrary::FormatTo 352 | #endif 353 | #endif 354 | } 355 | } 356 | 357 | --------------------------------------------------------------------------------