├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── SConstruct ├── assets └── setup.png ├── build.py ├── generate_cmake.py ├── install.py ├── requirements.txt ├── shared_lib.py ├── src ├── fmt │ ├── args.h │ ├── chrono.h │ ├── color.h │ ├── compile.h │ ├── core.h │ ├── format-inl.h │ ├── format.h │ ├── os.h │ ├── ostream.h │ ├── printf.h │ ├── ranges.h │ ├── std.h │ └── xchar.h ├── register_types.cpp ├── register_types.h ├── ser_comm.cpp ├── ser_comm.h ├── template │ └── sercomm.gdextension.template └── variant_helper.h └── tools ├── read_simulator.py └── write_similator.py /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | 3 | core.eol=native 4 | core.autocrlf=true 5 | 6 | *.txt text 7 | *.gd text 8 | *.tscn text 9 | 10 | *.jpg binary 11 | *.so binary 12 | *.dll binary -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | godot-cpp 3 | deps 4 | 5 | # scons 6 | *.dblite 7 | compile_commands.json 8 | 9 | # binaries 10 | *.obj 11 | *.os 12 | 13 | # Demo 14 | demo 15 | 16 | # Cmake 17 | CMakeLists.txt 18 | build 19 | 20 | # Jetbrains 21 | .idea 22 | 23 | # VS/VSCode 24 | .vs 25 | .vscode 26 | 27 | # Python venv stuff 28 | pvenv/ 29 | __pycache__/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Joel Setterberg 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SerCommPlugin 2 | A Godot 4.1+ plugin to read/write Arduino serial input.\ 3 | Read 4 | [GDExtension](https://docs.godotengine.org/en/stable/tutorials/scripting/gdextension/index.html) 5 | for more 6 | 7 | ## Want MQTT instead of Serial? 8 | If serial input is not what you're looking for maybe a plugin for MQTT would suite you better? 9 | [godot-mqtt repo](https://github.com/goatchurchprime/godot-mqtt?tab=readme-ov-file) made by [Julian Todd](https://github.com/goatchurchprime) 10 | 11 | # Dependencies 12 | * [Godot 4.1 or above](https://github.com/godotengine/godot/releases/tag/4.1.3-stable) 13 | * [godot-cpp](https://github.com/godotengine/godot-cpp) 14 | * [libserialport](https://github.com/sigrokproject/libserialport) (LGPL-3.0 license) 15 | * MSBuild v143 or later for Windows builds 16 | * '```apt install autoconf libtool```' for Linux 17 | * [fmt](https://github.com/fmtlib/fmt) (./src/fmt/) (MIT license) 18 | 19 | # Build system 20 | * [SCons](https://scons.org/) 21 | * [Python 3.10 or above](https://www.python.org/downloads/) 22 | * Python virtual environment 23 | * [Git](https://git-scm.com/) 24 | 25 | ## Compilation Database and/or CMake 26 | **Experimental feature**\ 27 | The Scons build will generate a Compilation Database (CD). CLion can open a CD directly\ 28 | You can also run the generate_cmake.py script to generate a CMakeLists.txt. 29 | ``` 30 | mkdir build 31 | cd build 32 | cmake .. 33 | make 34 | ``` 35 | 36 | # Repo structure 37 | src - Plugin source code 38 | deps - Dependencies, like libserialport. Will be created on install\ 39 | godot-cpp - Godot bindings. Will be created on install\ 40 | 41 | # Full Build 42 | This will build everything form scratch 43 | ``` 44 | python -m venv ./pvenv 45 | 46 | // Activate pvenv for Windows 47 | pvenv\Scripts\activate 48 | // Activate pvenv for Unix/MacOS 49 | source pvenv/bin/activate 50 | 51 | pip install -r requirements.txt 52 | python install.py 53 | ``` 54 | 55 | # Quick Build 56 | This will only build the GDSercomm part. Requires at least on Full Build before working 57 | ``` 58 | python -m venv ./pvenv 59 | 60 | // Activate pvenv for Windows 61 | pvenv\Scripts\activate 62 | // Activate pvenv for Unix/MacOS 63 | source pvenv/bin/activate 64 | 65 | pip install -r requirements.txt 66 | python build.py 67 | ``` 68 | 69 | # Output 70 | After a completed build all files you need will be in ./demo/bin directory. 71 | Except the libserialport library. It will be inside the ./deps/libserialport directory. 72 | ``` 73 | Windows = .\deps\libserialport\x64\Debug\libserialport.dll 74 | Linux = libserialport.so 75 | ``` 76 | 77 | # Usage 78 | 1. Create a SerComm node in your scene. 79 | 2. Select baudrate and port number. 80 | 3. Create a script on any other node in scene 81 | 4. script your logic 82 | ![Screenshot of basic setup](assets/setup.png) 83 | 84 | By toggle the radio button the extension will search for available ports and refresh the port dropdown list. 85 | 86 | Create a SerComm node for each port you want to read or write to. 87 | 88 | > [!NOTE] 89 | > SerComm uses the `on_message(message : String)` when a message is recieved. 90 | > In the `_process` function of the node, Godot automatically calls `read_serial` for you, then emits `on_message`. If you want disable automatic reads, you can override `_process`: 91 | > ```gdscript 92 | > func _process(delta): 93 | > var in = waiting_input_bytes(); 94 | > var read = read_serial(in); 95 | > print(read); 96 | > ``` 97 | -------------------------------------------------------------------------------- /SConstruct: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | import platform 5 | 6 | env = SConscript("godot-cpp/SConstruct") 7 | 8 | # For reference: 9 | # - CCFLAGS are compilation flags shared between C and C++ 10 | # - CFLAGS are for C-specific compilation flags 11 | # - CXXFLAGS are for C++-specific compilation flags 12 | # - CPPFLAGS are for pre-processor flags 13 | # - CPPDEFINES are for pre-processor defines 14 | # - LINKFLAGS are for linking flags 15 | 16 | # tweak this if you want to use different folders, or more folders, to store your source code in. 17 | env.Tool('compilation_db') 18 | env.Append(CPPPATH=['src/', 'deps/libserialport/']) 19 | env.Append(LIBPATH=['deps/libserialport/x64/Debug/']) 20 | env.Append(LIBS=['libserialport']) 21 | 22 | # Define FMT_HEADER_ONLY 23 | env.Append(CPPDEFINES=['FMT_HEADER_ONLY']) 24 | 25 | if platform.system() == 'Windows': 26 | env['CXXFLAGS'].remove('/std:c++17') 27 | env.Append(CXXFLAGS=['/std:c++20']) 28 | else: 29 | env['CXXFLAGS'].remove('-std=c++17') 30 | env.Append(CXXFLAGS=['-std=c++20']) 31 | 32 | sources = Glob("src/*.cpp") 33 | 34 | if env["platform"] == "macos": 35 | library = env.SharedLibrary( 36 | "demo/bin/libsercomm.{}.{}.framework/libsercomm.{}.{}".format( 37 | env["platform"], env["target"], env["platform"], env["target"] 38 | ), 39 | source=sources, 40 | ) 41 | else: 42 | library = env.SharedLibrary( 43 | "demo/bin/libsercomm{}{}".format(env["suffix"], env["SHLIBSUFFIX"]), 44 | source=sources, 45 | ) 46 | 47 | cc = env.CompilationDatabase('compile_commands.json') 48 | 49 | Default(cc, library) 50 | -------------------------------------------------------------------------------- /assets/setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NangiDev/GDSerCommPlugin/f4ada461f03034e6f98991aaf00f2b9b6bbfb36f/assets/setup.png -------------------------------------------------------------------------------- /build.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Build script. 3 | ''' 4 | 5 | import sys 6 | import subprocess 7 | import platform 8 | from shared_lib import get_loc, Loc, is_valid_python_version 9 | 10 | is_valid_python_version() 11 | 12 | 13 | def get_platform(): 14 | ''' 15 | Returns the name the current platform, translated into SCons terms. 16 | Ex. 'Linux = linux', 'Darwin = macos', 'Windows = windows' 17 | 18 | Will exit, with return code 1, if unsupported platform 19 | 20 | Returns: 21 | string: Return SCons terms for current platform 22 | ''' 23 | match platform.system(): 24 | case 'Linux': 25 | return "linux" 26 | case 'Darwin': 27 | return "macos" 28 | case 'Windows': 29 | return "windows" 30 | case other: 31 | print(f"Unsupported platform: {other}") 32 | sys.exit(1) 33 | 34 | 35 | _THREADCOUNT = "16" 36 | _TARGET = "template_release" 37 | _TARGET = "template_debug" 38 | _scons_cmd = f"\"{get_loc(Loc.SCONS)}\" platform={get_platform()} target={_TARGET} -j{_THREADCOUNT}" 39 | 40 | 41 | def scons_compile(working_dir, custom_api_file_path=None, message="Compile plugin"): 42 | ''' 43 | Will try compile SCons project. 44 | Will exit, with return code 1, if compile fails 45 | 46 | Parameters: 47 | working_dir (string): Working directory of where to run the compilation from 48 | custom_api_file_path (string): Path to custom api file if needed. Optional 49 | message (string): Message to output before comilation. For logging purposes. Optional 50 | ''' 51 | print(f"\n===== {message} =====") 52 | 53 | cmd = _scons_cmd + \ 54 | (f" custom_api_file={custom_api_file_path}" if custom_api_file_path else "") 55 | with subprocess.Popen(cmd, shell=True, cwd=working_dir) as process: 56 | process.wait() 57 | 58 | if process.returncode != 0: 59 | print("Compiling failed!") 60 | sys.exit(1) 61 | 62 | 63 | if __name__ == "__main__": 64 | scons_compile(get_loc(Loc.ROOT)) 65 | -------------------------------------------------------------------------------- /generate_cmake.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Generate a CMakeLists from the compilation database if it exists. 3 | ''' 4 | 5 | import json 6 | 7 | 8 | def generate_cmake_lists(db_path): 9 | ''' 10 | Will return a CMakeLists file containing the data from compilation database 11 | 12 | Parameters: 13 | db_path (string): Path to the compilation_database.json file 14 | 15 | Returns: 16 | string: Content of the compilation db in CMakeLists style 17 | ''' 18 | with open(db_path, 'r', encoding="utf-8") as f: 19 | compilation_database = json.load(f) 20 | f.close() 21 | 22 | project_name = "sercomm" 23 | content = "cmake_minimum_required(VERSION 3.5)\n\n" 24 | content += f"project({project_name})\n\n" 25 | 26 | source_files = set() 27 | 28 | for entry in compilation_database: 29 | source_file = entry['file'] 30 | source_files.add(source_file) 31 | 32 | content += f"add_executable({project_name}\n" 33 | for source_file in source_files: 34 | content += " " + source_file + "\n" 35 | content += ")\n" 36 | 37 | return content 38 | 39 | 40 | def save_cmake_lists(content, out_path): 41 | ''' 42 | Will write the given content to given output path. 43 | 44 | Parameters: 45 | content (string): Content to be written 46 | out_path (string): Path to where to write the file 47 | ''' 48 | with open(out_path, 'w', encoding="utf-8") as f: 49 | f.write(content) 50 | f.close() 51 | 52 | 53 | if __name__ == "__main__": 54 | COMPILATION_DATABASE_PATH = "compile_commands.json" 55 | OUTPUT_FILE_PATH = "CMakeLists.txt" 56 | 57 | cmake_lists_content = generate_cmake_lists(COMPILATION_DATABASE_PATH) 58 | save_cmake_lists(cmake_lists_content, OUTPUT_FILE_PATH) 59 | 60 | print(f"CMakeLists.txt generated successfully at {OUTPUT_FILE_PATH}") 61 | -------------------------------------------------------------------------------- /install.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Install script. 3 | ''' 4 | 5 | from subprocess import Popen 6 | from os import mkdir 7 | import shutil 8 | from venv import create 9 | from shutil import copyfile 10 | from shared_lib import is_valid_python_version, try_get_loc, get_loc, Loc 11 | from build import scons_compile, get_platform 12 | 13 | is_valid_python_version() 14 | 15 | # Git Clone godot-cpp 16 | print("\n===== Cloning godot-cpp =====") 17 | (_, path_exists) = try_get_loc(Loc.GODOTCPP) 18 | if not path_exists: 19 | GODOT_VER = "4.1" 20 | GODOT_REPO = "https://github.com/godotengine/godot-cpp" 21 | CMD = f"git clone -b {GODOT_VER} {GODOT_REPO}" 22 | with Popen(CMD, shell=True, cwd=get_loc(Loc.ROOT)) as process: 23 | process.wait() 24 | else: 25 | print("godot-cpp already cloned") 26 | 27 | # Create deps directory if its missing 28 | (deps_path, path_exists) = try_get_loc(Loc.DEPS) 29 | try: 30 | mkdir(deps_path) 31 | except FileExistsError as e: 32 | pass 33 | 34 | # Git Clone libserialport 35 | print("\n===== Cloning libserialport =====") 36 | (_, path_exists) = try_get_loc(Loc.LIBSERIALPORT) 37 | if not path_exists: 38 | LIBSER_REPO = "https://github.com/sigrokproject/libserialport.git" 39 | CMD = f"git clone {LIBSER_REPO}" 40 | with Popen(CMD, shell=True, cwd=deps_path) as process: 41 | process.wait() 42 | else: 43 | print("libserialport already cloned") 44 | 45 | # Create python environment 46 | print("\n===== Creating Python virtual environment =====") 47 | (v_env_dir, path_exists) = try_get_loc(Loc.PVENV) 48 | if not path_exists: 49 | print(f"Creating Python virtual environment. Path: {v_env_dir}") 50 | create(v_env_dir, with_pip=True) 51 | else: 52 | print(f"Python virtual environment already exists. Path: {v_env_dir}") 53 | 54 | # Update pip 55 | print("\n===== Updating pip =====") 56 | CMD = f"{get_loc(Loc.PYTHON)} -m pip install --upgrade pip" 57 | with Popen(CMD, shell=True, cwd=get_loc(Loc.ROOT)) as process: 58 | process.wait() 59 | 60 | # Install SCons through pip 61 | print("\n===== Installing Python requirements =====") 62 | CMD = f"{get_loc(Loc.PIP)} install -r requirements.txt" 63 | with Popen(CMD, shell=True, cwd=get_loc(Loc.ROOT)) as process: 64 | process.wait() 65 | 66 | # Compile libserialport library 67 | print("\n===== Compile libserialport =====") 68 | (lib_ser_path, path_exists) = try_get_loc(Loc.LIBSERIALPORT) 69 | 70 | 71 | MS_BUILD = shutil.which('msbuild') 72 | CMD = f"\"{MS_BUILD}\" -m libserialport.sln /p:Configuration=Debug /p:Platform=x64" 73 | if not get_platform() == 'windows': 74 | CMD = f"./autogen.sh && ./configure && make && sudo make install" 75 | 76 | print(CMD) 77 | with Popen(CMD, shell=True, cwd=lib_ser_path) as process: 78 | process.wait() 79 | 80 | # Compile Godot bindings 81 | (godot_cpp_path, path_exists) = try_get_loc(Loc.GODOTCPP) 82 | MESSAGE = "Compile Godot bindings" 83 | if not path_exists: 84 | custom_api_file_path = f"{godot_cpp_path}/gdextension/extension_api.json" 85 | scons_compile(godot_cpp_path, custom_api_file_path, MESSAGE) 86 | else: 87 | print(f"\n===== {MESSAGE} =====") 88 | print("godot-cpp already compiled") 89 | 90 | # Compile sercomm plugin 91 | scons_compile(get_loc(Loc.ROOT), message="Compile Sercom") 92 | 93 | # Copy sercomm.gdextension file to demo project 94 | print("\n===== Copy gdextension tempplate to demo directory =====") 95 | GDEXTTEMPLFILENAME = "sercomm.gdextension.template" 96 | GDEXTTEMPLFILEPATH = f"{get_loc(Loc.ROOT)}/src/template/{GDEXTTEMPLFILENAME}" 97 | GDEXTFILENAME = "sercomm.gdextension" 98 | GDEXTFILEPATH = f"{get_loc(Loc.ROOT)}/demo/bin/{GDEXTFILENAME}" 99 | copyfile(GDEXTTEMPLFILEPATH, GDEXTFILEPATH) 100 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | SCons==4.5.2 -------------------------------------------------------------------------------- /shared_lib.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Shared functionality between install and build steps. 3 | ''' 4 | 5 | import sys 6 | import enum 7 | import os 8 | import platform 9 | 10 | 11 | def is_valid_python_version(): 12 | ''' 13 | Exit early, with return code 1, if python version is invalid. 14 | ''' 15 | exp_major_ver = 3 16 | exp_minor_ver = 8 17 | cur_major_ver = sys.version_info.major 18 | cur_minor_ver = sys.version_info.minor 19 | 20 | major_is_lower = cur_major_ver < exp_major_ver 21 | major_is_greater_but_minor_is_lower = not major_is_lower and cur_minor_ver < exp_minor_ver 22 | 23 | if major_is_lower or major_is_greater_but_minor_is_lower: 24 | print( 25 | f" Python {exp_major_ver}.{exp_minor_ver} or higher is required.") 26 | print( 27 | f" You are using Python {cur_major_ver}.{cur_minor_ver}.") 28 | sys.exit(1) 29 | 30 | 31 | def name_of_bin_file(): 32 | ''' 33 | Returns the name of the Python venv bin directory. It differs from system to system. 34 | Bin folder in Windows is called 'Scripts' instead of 'bin' 35 | 36 | Returns: 37 | string: Name of Python venv bin directory 38 | ''' 39 | return "Scripts" if platform.system() == 'Windows' else "bin" 40 | 41 | 42 | class Loc(enum.Enum): 43 | ''' 44 | Enums used as keys for local dictionary (LocDict). 45 | ''' 46 | ROOT = 1 47 | PVENV = 2 48 | GODOTCPP = 3 49 | DEPS = 4 50 | LIBSERIALPORT = 5 51 | BIN = 6 52 | SCONS = 7 53 | PYTHON = 8 54 | PIP = 9 55 | GTEST = 10 56 | 57 | 58 | _root_path = os.path.dirname(__file__) 59 | _pvenv_path = os.path.join(_root_path, "pvenv") 60 | _godot_cpp_path = os.path.join(_root_path, "godot-cpp") 61 | _deps_path = os.path.join(_root_path, "deps") 62 | _lib_ser_path = os.path.join(_deps_path, "libserialport") 63 | _g_test_path = os.path.join(_deps_path, "googletest") 64 | _bin_path = os.path.join(_pvenv_path, name_of_bin_file()) 65 | 66 | LocDict = { 67 | Loc.ROOT: _root_path, 68 | Loc.PVENV: _pvenv_path, 69 | Loc.GODOTCPP: _godot_cpp_path, 70 | Loc.DEPS: _deps_path, 71 | Loc.LIBSERIALPORT: _lib_ser_path, 72 | Loc.GTEST: _g_test_path, 73 | Loc.BIN: _bin_path, 74 | Loc.SCONS: os.path.join(_bin_path, "scons"), 75 | Loc.PYTHON: os.path.join(_bin_path, "python"), 76 | Loc.PIP: os.path.join(_bin_path, "pip"), 77 | } 78 | 79 | 80 | def get_loc(loc): 81 | ''' 82 | Get the path of Loc enum. 83 | 84 | Parameters: 85 | loc (Loc(Enum)): Loc enum. Ex Loc.ROOT 86 | 87 | Returns: 88 | string: Path to wanted directory/file 89 | ''' 90 | return LocDict[loc] 91 | 92 | 93 | def try_get_loc(loc): 94 | ''' 95 | Will check if path exists as well as getting the path of parameter enum. 96 | 97 | Parameters: 98 | loc (Loc(Enum)): Loc enum. Ex Loc.ROOT 99 | 100 | Returns: 101 | (string, bool): Return pair (path, path_exists) with path requested and if it exists or not 102 | ''' 103 | p = LocDict[loc] 104 | return (p, os.path.exists(p)) 105 | -------------------------------------------------------------------------------- /src/fmt/args.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - dynamic argument lists 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_ARGS_H_ 9 | #define FMT_ARGS_H_ 10 | 11 | #include // std::reference_wrapper 12 | #include // std::unique_ptr 13 | #include 14 | 15 | #include "core.h" 16 | 17 | FMT_BEGIN_NAMESPACE 18 | 19 | namespace detail { 20 | 21 | template struct is_reference_wrapper : std::false_type {}; 22 | template 23 | struct is_reference_wrapper> : std::true_type {}; 24 | 25 | template const T& unwrap(const T& v) { return v; } 26 | template const T& unwrap(const std::reference_wrapper& v) { 27 | return static_cast(v); 28 | } 29 | 30 | class dynamic_arg_list { 31 | // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for 32 | // templates it doesn't complain about inability to deduce single translation 33 | // unit for placing vtable. So storage_node_base is made a fake template. 34 | template struct node { 35 | virtual ~node() = default; 36 | std::unique_ptr> next; 37 | }; 38 | 39 | template struct typed_node : node<> { 40 | T value; 41 | 42 | template 43 | FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {} 44 | 45 | template 46 | FMT_CONSTEXPR typed_node(const basic_string_view& arg) 47 | : value(arg.data(), arg.size()) {} 48 | }; 49 | 50 | std::unique_ptr> head_; 51 | 52 | public: 53 | template const T& push(const Arg& arg) { 54 | auto new_node = std::unique_ptr>(new typed_node(arg)); 55 | auto& value = new_node->value; 56 | new_node->next = std::move(head_); 57 | head_ = std::move(new_node); 58 | return value; 59 | } 60 | }; 61 | } // namespace detail 62 | 63 | /** 64 | \rst 65 | A dynamic version of `fmt::format_arg_store`. 66 | It's equipped with a storage to potentially temporary objects which lifetimes 67 | could be shorter than the format arguments object. 68 | 69 | It can be implicitly converted into `~fmt::basic_format_args` for passing 70 | into type-erased formatting functions such as `~fmt::vformat`. 71 | \endrst 72 | */ 73 | template 74 | class dynamic_format_arg_store 75 | #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 76 | // Workaround a GCC template argument substitution bug. 77 | : public basic_format_args 78 | #endif 79 | { 80 | private: 81 | using char_type = typename Context::char_type; 82 | 83 | template struct need_copy { 84 | static constexpr detail::type mapped_type = 85 | detail::mapped_type_constant::value; 86 | 87 | enum { 88 | value = !(detail::is_reference_wrapper::value || 89 | std::is_same>::value || 90 | std::is_same>::value || 91 | (mapped_type != detail::type::cstring_type && 92 | mapped_type != detail::type::string_type && 93 | mapped_type != detail::type::custom_type)) 94 | }; 95 | }; 96 | 97 | template 98 | using stored_type = conditional_t< 99 | std::is_convertible>::value && 100 | !detail::is_reference_wrapper::value, 101 | std::basic_string, T>; 102 | 103 | // Storage of basic_format_arg must be contiguous. 104 | std::vector> data_; 105 | std::vector> named_info_; 106 | 107 | // Storage of arguments not fitting into basic_format_arg must grow 108 | // without relocation because items in data_ refer to it. 109 | detail::dynamic_arg_list dynamic_args_; 110 | 111 | friend class basic_format_args; 112 | 113 | unsigned long long get_types() const { 114 | return detail::is_unpacked_bit | data_.size() | 115 | (named_info_.empty() 116 | ? 0ULL 117 | : static_cast(detail::has_named_args_bit)); 118 | } 119 | 120 | const basic_format_arg* data() const { 121 | return named_info_.empty() ? data_.data() : data_.data() + 1; 122 | } 123 | 124 | template void emplace_arg(const T& arg) { 125 | data_.emplace_back(detail::make_arg(arg)); 126 | } 127 | 128 | template 129 | void emplace_arg(const detail::named_arg& arg) { 130 | if (named_info_.empty()) { 131 | constexpr const detail::named_arg_info* zero_ptr{nullptr}; 132 | data_.insert(data_.begin(), {zero_ptr, 0}); 133 | } 134 | data_.emplace_back(detail::make_arg(detail::unwrap(arg.value))); 135 | auto pop_one = [](std::vector>* data) { 136 | data->pop_back(); 137 | }; 138 | std::unique_ptr>, decltype(pop_one)> 139 | guard{&data_, pop_one}; 140 | named_info_.push_back({arg.name, static_cast(data_.size() - 2u)}); 141 | data_[0].value_.named_args = {named_info_.data(), named_info_.size()}; 142 | guard.release(); 143 | } 144 | 145 | public: 146 | constexpr dynamic_format_arg_store() = default; 147 | 148 | /** 149 | \rst 150 | Adds an argument into the dynamic store for later passing to a formatting 151 | function. 152 | 153 | Note that custom types and string types (but not string views) are copied 154 | into the store dynamically allocating memory if necessary. 155 | 156 | **Example**:: 157 | 158 | fmt::dynamic_format_arg_store store; 159 | store.push_back(42); 160 | store.push_back("abc"); 161 | store.push_back(1.5f); 162 | std::string result = fmt::vformat("{} and {} and {}", store); 163 | \endrst 164 | */ 165 | template void push_back(const T& arg) { 166 | if (detail::const_check(need_copy::value)) 167 | emplace_arg(dynamic_args_.push>(arg)); 168 | else 169 | emplace_arg(detail::unwrap(arg)); 170 | } 171 | 172 | /** 173 | \rst 174 | Adds a reference to the argument into the dynamic store for later passing to 175 | a formatting function. 176 | 177 | **Example**:: 178 | 179 | fmt::dynamic_format_arg_store store; 180 | char band[] = "Rolling Stones"; 181 | store.push_back(std::cref(band)); 182 | band[9] = 'c'; // Changing str affects the output. 183 | std::string result = fmt::vformat("{}", store); 184 | // result == "Rolling Scones" 185 | \endrst 186 | */ 187 | template void push_back(std::reference_wrapper arg) { 188 | static_assert( 189 | need_copy::value, 190 | "objects of built-in types and string views are always copied"); 191 | emplace_arg(arg.get()); 192 | } 193 | 194 | /** 195 | Adds named argument into the dynamic store for later passing to a formatting 196 | function. ``std::reference_wrapper`` is supported to avoid copying of the 197 | argument. The name is always copied into the store. 198 | */ 199 | template 200 | void push_back(const detail::named_arg& arg) { 201 | const char_type* arg_name = 202 | dynamic_args_.push>(arg.name).c_str(); 203 | if (detail::const_check(need_copy::value)) { 204 | emplace_arg( 205 | fmt::arg(arg_name, dynamic_args_.push>(arg.value))); 206 | } else { 207 | emplace_arg(fmt::arg(arg_name, arg.value)); 208 | } 209 | } 210 | 211 | /** Erase all elements from the store */ 212 | void clear() { 213 | data_.clear(); 214 | named_info_.clear(); 215 | dynamic_args_ = detail::dynamic_arg_list(); 216 | } 217 | 218 | /** 219 | \rst 220 | Reserves space to store at least *new_cap* arguments including 221 | *new_cap_named* named arguments. 222 | \endrst 223 | */ 224 | void reserve(size_t new_cap, size_t new_cap_named) { 225 | FMT_ASSERT(new_cap >= new_cap_named, 226 | "Set of arguments includes set of named arguments"); 227 | data_.reserve(new_cap); 228 | named_info_.reserve(new_cap_named); 229 | } 230 | }; 231 | 232 | FMT_END_NAMESPACE 233 | 234 | #endif // FMT_ARGS_H_ 235 | -------------------------------------------------------------------------------- /src/fmt/color.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - color support 2 | // 3 | // Copyright (c) 2018 - present, Victor Zverovich and fmt contributors 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_COLOR_H_ 9 | #define FMT_COLOR_H_ 10 | 11 | #include "format.h" 12 | 13 | FMT_BEGIN_NAMESPACE 14 | FMT_BEGIN_EXPORT 15 | 16 | enum class color : uint32_t { 17 | alice_blue = 0xF0F8FF, // rgb(240,248,255) 18 | antique_white = 0xFAEBD7, // rgb(250,235,215) 19 | aqua = 0x00FFFF, // rgb(0,255,255) 20 | aquamarine = 0x7FFFD4, // rgb(127,255,212) 21 | azure = 0xF0FFFF, // rgb(240,255,255) 22 | beige = 0xF5F5DC, // rgb(245,245,220) 23 | bisque = 0xFFE4C4, // rgb(255,228,196) 24 | black = 0x000000, // rgb(0,0,0) 25 | blanched_almond = 0xFFEBCD, // rgb(255,235,205) 26 | blue = 0x0000FF, // rgb(0,0,255) 27 | blue_violet = 0x8A2BE2, // rgb(138,43,226) 28 | brown = 0xA52A2A, // rgb(165,42,42) 29 | burly_wood = 0xDEB887, // rgb(222,184,135) 30 | cadet_blue = 0x5F9EA0, // rgb(95,158,160) 31 | chartreuse = 0x7FFF00, // rgb(127,255,0) 32 | chocolate = 0xD2691E, // rgb(210,105,30) 33 | coral = 0xFF7F50, // rgb(255,127,80) 34 | cornflower_blue = 0x6495ED, // rgb(100,149,237) 35 | cornsilk = 0xFFF8DC, // rgb(255,248,220) 36 | crimson = 0xDC143C, // rgb(220,20,60) 37 | cyan = 0x00FFFF, // rgb(0,255,255) 38 | dark_blue = 0x00008B, // rgb(0,0,139) 39 | dark_cyan = 0x008B8B, // rgb(0,139,139) 40 | dark_golden_rod = 0xB8860B, // rgb(184,134,11) 41 | dark_gray = 0xA9A9A9, // rgb(169,169,169) 42 | dark_green = 0x006400, // rgb(0,100,0) 43 | dark_khaki = 0xBDB76B, // rgb(189,183,107) 44 | dark_magenta = 0x8B008B, // rgb(139,0,139) 45 | dark_olive_green = 0x556B2F, // rgb(85,107,47) 46 | dark_orange = 0xFF8C00, // rgb(255,140,0) 47 | dark_orchid = 0x9932CC, // rgb(153,50,204) 48 | dark_red = 0x8B0000, // rgb(139,0,0) 49 | dark_salmon = 0xE9967A, // rgb(233,150,122) 50 | dark_sea_green = 0x8FBC8F, // rgb(143,188,143) 51 | dark_slate_blue = 0x483D8B, // rgb(72,61,139) 52 | dark_slate_gray = 0x2F4F4F, // rgb(47,79,79) 53 | dark_turquoise = 0x00CED1, // rgb(0,206,209) 54 | dark_violet = 0x9400D3, // rgb(148,0,211) 55 | deep_pink = 0xFF1493, // rgb(255,20,147) 56 | deep_sky_blue = 0x00BFFF, // rgb(0,191,255) 57 | dim_gray = 0x696969, // rgb(105,105,105) 58 | dodger_blue = 0x1E90FF, // rgb(30,144,255) 59 | fire_brick = 0xB22222, // rgb(178,34,34) 60 | floral_white = 0xFFFAF0, // rgb(255,250,240) 61 | forest_green = 0x228B22, // rgb(34,139,34) 62 | fuchsia = 0xFF00FF, // rgb(255,0,255) 63 | gainsboro = 0xDCDCDC, // rgb(220,220,220) 64 | ghost_white = 0xF8F8FF, // rgb(248,248,255) 65 | gold = 0xFFD700, // rgb(255,215,0) 66 | golden_rod = 0xDAA520, // rgb(218,165,32) 67 | gray = 0x808080, // rgb(128,128,128) 68 | green = 0x008000, // rgb(0,128,0) 69 | green_yellow = 0xADFF2F, // rgb(173,255,47) 70 | honey_dew = 0xF0FFF0, // rgb(240,255,240) 71 | hot_pink = 0xFF69B4, // rgb(255,105,180) 72 | indian_red = 0xCD5C5C, // rgb(205,92,92) 73 | indigo = 0x4B0082, // rgb(75,0,130) 74 | ivory = 0xFFFFF0, // rgb(255,255,240) 75 | khaki = 0xF0E68C, // rgb(240,230,140) 76 | lavender = 0xE6E6FA, // rgb(230,230,250) 77 | lavender_blush = 0xFFF0F5, // rgb(255,240,245) 78 | lawn_green = 0x7CFC00, // rgb(124,252,0) 79 | lemon_chiffon = 0xFFFACD, // rgb(255,250,205) 80 | light_blue = 0xADD8E6, // rgb(173,216,230) 81 | light_coral = 0xF08080, // rgb(240,128,128) 82 | light_cyan = 0xE0FFFF, // rgb(224,255,255) 83 | light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210) 84 | light_gray = 0xD3D3D3, // rgb(211,211,211) 85 | light_green = 0x90EE90, // rgb(144,238,144) 86 | light_pink = 0xFFB6C1, // rgb(255,182,193) 87 | light_salmon = 0xFFA07A, // rgb(255,160,122) 88 | light_sea_green = 0x20B2AA, // rgb(32,178,170) 89 | light_sky_blue = 0x87CEFA, // rgb(135,206,250) 90 | light_slate_gray = 0x778899, // rgb(119,136,153) 91 | light_steel_blue = 0xB0C4DE, // rgb(176,196,222) 92 | light_yellow = 0xFFFFE0, // rgb(255,255,224) 93 | lime = 0x00FF00, // rgb(0,255,0) 94 | lime_green = 0x32CD32, // rgb(50,205,50) 95 | linen = 0xFAF0E6, // rgb(250,240,230) 96 | magenta = 0xFF00FF, // rgb(255,0,255) 97 | maroon = 0x800000, // rgb(128,0,0) 98 | medium_aquamarine = 0x66CDAA, // rgb(102,205,170) 99 | medium_blue = 0x0000CD, // rgb(0,0,205) 100 | medium_orchid = 0xBA55D3, // rgb(186,85,211) 101 | medium_purple = 0x9370DB, // rgb(147,112,219) 102 | medium_sea_green = 0x3CB371, // rgb(60,179,113) 103 | medium_slate_blue = 0x7B68EE, // rgb(123,104,238) 104 | medium_spring_green = 0x00FA9A, // rgb(0,250,154) 105 | medium_turquoise = 0x48D1CC, // rgb(72,209,204) 106 | medium_violet_red = 0xC71585, // rgb(199,21,133) 107 | midnight_blue = 0x191970, // rgb(25,25,112) 108 | mint_cream = 0xF5FFFA, // rgb(245,255,250) 109 | misty_rose = 0xFFE4E1, // rgb(255,228,225) 110 | moccasin = 0xFFE4B5, // rgb(255,228,181) 111 | navajo_white = 0xFFDEAD, // rgb(255,222,173) 112 | navy = 0x000080, // rgb(0,0,128) 113 | old_lace = 0xFDF5E6, // rgb(253,245,230) 114 | olive = 0x808000, // rgb(128,128,0) 115 | olive_drab = 0x6B8E23, // rgb(107,142,35) 116 | orange = 0xFFA500, // rgb(255,165,0) 117 | orange_red = 0xFF4500, // rgb(255,69,0) 118 | orchid = 0xDA70D6, // rgb(218,112,214) 119 | pale_golden_rod = 0xEEE8AA, // rgb(238,232,170) 120 | pale_green = 0x98FB98, // rgb(152,251,152) 121 | pale_turquoise = 0xAFEEEE, // rgb(175,238,238) 122 | pale_violet_red = 0xDB7093, // rgb(219,112,147) 123 | papaya_whip = 0xFFEFD5, // rgb(255,239,213) 124 | peach_puff = 0xFFDAB9, // rgb(255,218,185) 125 | peru = 0xCD853F, // rgb(205,133,63) 126 | pink = 0xFFC0CB, // rgb(255,192,203) 127 | plum = 0xDDA0DD, // rgb(221,160,221) 128 | powder_blue = 0xB0E0E6, // rgb(176,224,230) 129 | purple = 0x800080, // rgb(128,0,128) 130 | rebecca_purple = 0x663399, // rgb(102,51,153) 131 | red = 0xFF0000, // rgb(255,0,0) 132 | rosy_brown = 0xBC8F8F, // rgb(188,143,143) 133 | royal_blue = 0x4169E1, // rgb(65,105,225) 134 | saddle_brown = 0x8B4513, // rgb(139,69,19) 135 | salmon = 0xFA8072, // rgb(250,128,114) 136 | sandy_brown = 0xF4A460, // rgb(244,164,96) 137 | sea_green = 0x2E8B57, // rgb(46,139,87) 138 | sea_shell = 0xFFF5EE, // rgb(255,245,238) 139 | sienna = 0xA0522D, // rgb(160,82,45) 140 | silver = 0xC0C0C0, // rgb(192,192,192) 141 | sky_blue = 0x87CEEB, // rgb(135,206,235) 142 | slate_blue = 0x6A5ACD, // rgb(106,90,205) 143 | slate_gray = 0x708090, // rgb(112,128,144) 144 | snow = 0xFFFAFA, // rgb(255,250,250) 145 | spring_green = 0x00FF7F, // rgb(0,255,127) 146 | steel_blue = 0x4682B4, // rgb(70,130,180) 147 | tan = 0xD2B48C, // rgb(210,180,140) 148 | teal = 0x008080, // rgb(0,128,128) 149 | thistle = 0xD8BFD8, // rgb(216,191,216) 150 | tomato = 0xFF6347, // rgb(255,99,71) 151 | turquoise = 0x40E0D0, // rgb(64,224,208) 152 | violet = 0xEE82EE, // rgb(238,130,238) 153 | wheat = 0xF5DEB3, // rgb(245,222,179) 154 | white = 0xFFFFFF, // rgb(255,255,255) 155 | white_smoke = 0xF5F5F5, // rgb(245,245,245) 156 | yellow = 0xFFFF00, // rgb(255,255,0) 157 | yellow_green = 0x9ACD32 // rgb(154,205,50) 158 | }; // enum class color 159 | 160 | enum class terminal_color : uint8_t { 161 | black = 30, 162 | red, 163 | green, 164 | yellow, 165 | blue, 166 | magenta, 167 | cyan, 168 | white, 169 | bright_black = 90, 170 | bright_red, 171 | bright_green, 172 | bright_yellow, 173 | bright_blue, 174 | bright_magenta, 175 | bright_cyan, 176 | bright_white 177 | }; 178 | 179 | enum class emphasis : uint8_t { 180 | bold = 1, 181 | faint = 1 << 1, 182 | italic = 1 << 2, 183 | underline = 1 << 3, 184 | blink = 1 << 4, 185 | reverse = 1 << 5, 186 | conceal = 1 << 6, 187 | strikethrough = 1 << 7, 188 | }; 189 | 190 | // rgb is a struct for red, green and blue colors. 191 | // Using the name "rgb" makes some editors show the color in a tooltip. 192 | struct rgb { 193 | FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {} 194 | FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {} 195 | FMT_CONSTEXPR rgb(uint32_t hex) 196 | : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {} 197 | FMT_CONSTEXPR rgb(color hex) 198 | : r((uint32_t(hex) >> 16) & 0xFF), 199 | g((uint32_t(hex) >> 8) & 0xFF), 200 | b(uint32_t(hex) & 0xFF) {} 201 | uint8_t r; 202 | uint8_t g; 203 | uint8_t b; 204 | }; 205 | 206 | namespace detail { 207 | 208 | // color is a struct of either a rgb color or a terminal color. 209 | struct color_type { 210 | FMT_CONSTEXPR color_type() noexcept : is_rgb(), value{} {} 211 | FMT_CONSTEXPR color_type(color rgb_color) noexcept : is_rgb(true), value{} { 212 | value.rgb_color = static_cast(rgb_color); 213 | } 214 | FMT_CONSTEXPR color_type(rgb rgb_color) noexcept : is_rgb(true), value{} { 215 | value.rgb_color = (static_cast(rgb_color.r) << 16) | 216 | (static_cast(rgb_color.g) << 8) | rgb_color.b; 217 | } 218 | FMT_CONSTEXPR color_type(terminal_color term_color) noexcept 219 | : is_rgb(), value{} { 220 | value.term_color = static_cast(term_color); 221 | } 222 | bool is_rgb; 223 | union color_union { 224 | uint8_t term_color; 225 | uint32_t rgb_color; 226 | } value; 227 | }; 228 | } // namespace detail 229 | 230 | /** A text style consisting of foreground and background colors and emphasis. */ 231 | class text_style { 232 | public: 233 | FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept 234 | : set_foreground_color(), set_background_color(), ems(em) {} 235 | 236 | FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) { 237 | if (!set_foreground_color) { 238 | set_foreground_color = rhs.set_foreground_color; 239 | foreground_color = rhs.foreground_color; 240 | } else if (rhs.set_foreground_color) { 241 | if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) 242 | FMT_THROW(format_error("can't OR a terminal color")); 243 | foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color; 244 | } 245 | 246 | if (!set_background_color) { 247 | set_background_color = rhs.set_background_color; 248 | background_color = rhs.background_color; 249 | } else if (rhs.set_background_color) { 250 | if (!background_color.is_rgb || !rhs.background_color.is_rgb) 251 | FMT_THROW(format_error("can't OR a terminal color")); 252 | background_color.value.rgb_color |= rhs.background_color.value.rgb_color; 253 | } 254 | 255 | ems = static_cast(static_cast(ems) | 256 | static_cast(rhs.ems)); 257 | return *this; 258 | } 259 | 260 | friend FMT_CONSTEXPR text_style operator|(text_style lhs, 261 | const text_style& rhs) { 262 | return lhs |= rhs; 263 | } 264 | 265 | FMT_CONSTEXPR bool has_foreground() const noexcept { 266 | return set_foreground_color; 267 | } 268 | FMT_CONSTEXPR bool has_background() const noexcept { 269 | return set_background_color; 270 | } 271 | FMT_CONSTEXPR bool has_emphasis() const noexcept { 272 | return static_cast(ems) != 0; 273 | } 274 | FMT_CONSTEXPR detail::color_type get_foreground() const noexcept { 275 | FMT_ASSERT(has_foreground(), "no foreground specified for this style"); 276 | return foreground_color; 277 | } 278 | FMT_CONSTEXPR detail::color_type get_background() const noexcept { 279 | FMT_ASSERT(has_background(), "no background specified for this style"); 280 | return background_color; 281 | } 282 | FMT_CONSTEXPR emphasis get_emphasis() const noexcept { 283 | FMT_ASSERT(has_emphasis(), "no emphasis specified for this style"); 284 | return ems; 285 | } 286 | 287 | private: 288 | FMT_CONSTEXPR text_style(bool is_foreground, 289 | detail::color_type text_color) noexcept 290 | : set_foreground_color(), set_background_color(), ems() { 291 | if (is_foreground) { 292 | foreground_color = text_color; 293 | set_foreground_color = true; 294 | } else { 295 | background_color = text_color; 296 | set_background_color = true; 297 | } 298 | } 299 | 300 | friend FMT_CONSTEXPR text_style fg(detail::color_type foreground) noexcept; 301 | 302 | friend FMT_CONSTEXPR text_style bg(detail::color_type background) noexcept; 303 | 304 | detail::color_type foreground_color; 305 | detail::color_type background_color; 306 | bool set_foreground_color; 307 | bool set_background_color; 308 | emphasis ems; 309 | }; 310 | 311 | /** Creates a text style from the foreground (text) color. */ 312 | FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) noexcept { 313 | return text_style(true, foreground); 314 | } 315 | 316 | /** Creates a text style from the background color. */ 317 | FMT_CONSTEXPR inline text_style bg(detail::color_type background) noexcept { 318 | return text_style(false, background); 319 | } 320 | 321 | FMT_CONSTEXPR inline text_style operator|(emphasis lhs, emphasis rhs) noexcept { 322 | return text_style(lhs) | rhs; 323 | } 324 | 325 | namespace detail { 326 | 327 | template struct ansi_color_escape { 328 | FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color, 329 | const char* esc) noexcept { 330 | // If we have a terminal color, we need to output another escape code 331 | // sequence. 332 | if (!text_color.is_rgb) { 333 | bool is_background = esc == string_view("\x1b[48;2;"); 334 | uint32_t value = text_color.value.term_color; 335 | // Background ASCII codes are the same as the foreground ones but with 336 | // 10 more. 337 | if (is_background) value += 10u; 338 | 339 | size_t index = 0; 340 | buffer[index++] = static_cast('\x1b'); 341 | buffer[index++] = static_cast('['); 342 | 343 | if (value >= 100u) { 344 | buffer[index++] = static_cast('1'); 345 | value %= 100u; 346 | } 347 | buffer[index++] = static_cast('0' + value / 10u); 348 | buffer[index++] = static_cast('0' + value % 10u); 349 | 350 | buffer[index++] = static_cast('m'); 351 | buffer[index++] = static_cast('\0'); 352 | return; 353 | } 354 | 355 | for (int i = 0; i < 7; i++) { 356 | buffer[i] = static_cast(esc[i]); 357 | } 358 | rgb color(text_color.value.rgb_color); 359 | to_esc(color.r, buffer + 7, ';'); 360 | to_esc(color.g, buffer + 11, ';'); 361 | to_esc(color.b, buffer + 15, 'm'); 362 | buffer[19] = static_cast(0); 363 | } 364 | FMT_CONSTEXPR ansi_color_escape(emphasis em) noexcept { 365 | uint8_t em_codes[num_emphases] = {}; 366 | if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1; 367 | if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2; 368 | if (has_emphasis(em, emphasis::italic)) em_codes[2] = 3; 369 | if (has_emphasis(em, emphasis::underline)) em_codes[3] = 4; 370 | if (has_emphasis(em, emphasis::blink)) em_codes[4] = 5; 371 | if (has_emphasis(em, emphasis::reverse)) em_codes[5] = 7; 372 | if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8; 373 | if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9; 374 | 375 | size_t index = 0; 376 | for (size_t i = 0; i < num_emphases; ++i) { 377 | if (!em_codes[i]) continue; 378 | buffer[index++] = static_cast('\x1b'); 379 | buffer[index++] = static_cast('['); 380 | buffer[index++] = static_cast('0' + em_codes[i]); 381 | buffer[index++] = static_cast('m'); 382 | } 383 | buffer[index++] = static_cast(0); 384 | } 385 | FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; } 386 | 387 | FMT_CONSTEXPR const Char* begin() const noexcept { return buffer; } 388 | FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const noexcept { 389 | return buffer + std::char_traits::length(buffer); 390 | } 391 | 392 | private: 393 | static constexpr size_t num_emphases = 8; 394 | Char buffer[7u + 3u * num_emphases + 1u]; 395 | 396 | static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, 397 | char delimiter) noexcept { 398 | out[0] = static_cast('0' + c / 100); 399 | out[1] = static_cast('0' + c / 10 % 10); 400 | out[2] = static_cast('0' + c % 10); 401 | out[3] = static_cast(delimiter); 402 | } 403 | static FMT_CONSTEXPR bool has_emphasis(emphasis em, emphasis mask) noexcept { 404 | return static_cast(em) & static_cast(mask); 405 | } 406 | }; 407 | 408 | template 409 | FMT_CONSTEXPR ansi_color_escape make_foreground_color( 410 | detail::color_type foreground) noexcept { 411 | return ansi_color_escape(foreground, "\x1b[38;2;"); 412 | } 413 | 414 | template 415 | FMT_CONSTEXPR ansi_color_escape make_background_color( 416 | detail::color_type background) noexcept { 417 | return ansi_color_escape(background, "\x1b[48;2;"); 418 | } 419 | 420 | template 421 | FMT_CONSTEXPR ansi_color_escape make_emphasis(emphasis em) noexcept { 422 | return ansi_color_escape(em); 423 | } 424 | 425 | template inline void reset_color(buffer& buffer) { 426 | auto reset_color = string_view("\x1b[0m"); 427 | buffer.append(reset_color.begin(), reset_color.end()); 428 | } 429 | 430 | template struct styled_arg : detail::view { 431 | const T& value; 432 | text_style style; 433 | styled_arg(const T& v, text_style s) : value(v), style(s) {} 434 | }; 435 | 436 | template 437 | void vformat_to(buffer& buf, const text_style& ts, 438 | basic_string_view format_str, 439 | basic_format_args>> args) { 440 | bool has_style = false; 441 | if (ts.has_emphasis()) { 442 | has_style = true; 443 | auto emphasis = detail::make_emphasis(ts.get_emphasis()); 444 | buf.append(emphasis.begin(), emphasis.end()); 445 | } 446 | if (ts.has_foreground()) { 447 | has_style = true; 448 | auto foreground = detail::make_foreground_color(ts.get_foreground()); 449 | buf.append(foreground.begin(), foreground.end()); 450 | } 451 | if (ts.has_background()) { 452 | has_style = true; 453 | auto background = detail::make_background_color(ts.get_background()); 454 | buf.append(background.begin(), background.end()); 455 | } 456 | detail::vformat_to(buf, format_str, args, {}); 457 | if (has_style) detail::reset_color(buf); 458 | } 459 | 460 | } // namespace detail 461 | 462 | inline void vprint(std::FILE* f, const text_style& ts, string_view fmt, 463 | format_args args) { 464 | // Legacy wide streams are not supported. 465 | auto buf = memory_buffer(); 466 | detail::vformat_to(buf, ts, fmt, args); 467 | if (detail::is_utf8()) { 468 | detail::print(f, string_view(buf.begin(), buf.size())); 469 | return; 470 | } 471 | buf.push_back('\0'); 472 | int result = std::fputs(buf.data(), f); 473 | if (result < 0) 474 | FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); 475 | } 476 | 477 | /** 478 | \rst 479 | Formats a string and prints it to the specified file stream using ANSI 480 | escape sequences to specify text formatting. 481 | 482 | **Example**:: 483 | 484 | fmt::print(fmt::emphasis::bold | fg(fmt::color::red), 485 | "Elapsed time: {0:.2f} seconds", 1.23); 486 | \endrst 487 | */ 488 | template ::value)> 490 | void print(std::FILE* f, const text_style& ts, const S& format_str, 491 | const Args&... args) { 492 | vprint(f, ts, format_str, 493 | fmt::make_format_args>>(args...)); 494 | } 495 | 496 | /** 497 | \rst 498 | Formats a string and prints it to stdout using ANSI escape sequences to 499 | specify text formatting. 500 | 501 | **Example**:: 502 | 503 | fmt::print(fmt::emphasis::bold | fg(fmt::color::red), 504 | "Elapsed time: {0:.2f} seconds", 1.23); 505 | \endrst 506 | */ 507 | template ::value)> 509 | void print(const text_style& ts, const S& format_str, const Args&... args) { 510 | return print(stdout, ts, format_str, args...); 511 | } 512 | 513 | template > 514 | inline std::basic_string vformat( 515 | const text_style& ts, const S& format_str, 516 | basic_format_args>> args) { 517 | basic_memory_buffer buf; 518 | detail::vformat_to(buf, ts, detail::to_string_view(format_str), args); 519 | return fmt::to_string(buf); 520 | } 521 | 522 | /** 523 | \rst 524 | Formats arguments and returns the result as a string using ANSI 525 | escape sequences to specify text formatting. 526 | 527 | **Example**:: 528 | 529 | #include 530 | std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), 531 | "The answer is {}", 42); 532 | \endrst 533 | */ 534 | template > 535 | inline std::basic_string format(const text_style& ts, const S& format_str, 536 | const Args&... args) { 537 | return fmt::vformat(ts, detail::to_string_view(format_str), 538 | fmt::make_format_args>(args...)); 539 | } 540 | 541 | /** 542 | Formats a string with the given text_style and writes the output to ``out``. 543 | */ 544 | template ::value)> 546 | OutputIt vformat_to( 547 | OutputIt out, const text_style& ts, basic_string_view format_str, 548 | basic_format_args>> args) { 549 | auto&& buf = detail::get_buffer(out); 550 | detail::vformat_to(buf, ts, format_str, args); 551 | return detail::get_iterator(buf, out); 552 | } 553 | 554 | /** 555 | \rst 556 | Formats arguments with the given text_style, writes the result to the output 557 | iterator ``out`` and returns the iterator past the end of the output range. 558 | 559 | **Example**:: 560 | 561 | std::vector out; 562 | fmt::format_to(std::back_inserter(out), 563 | fmt::emphasis::bold | fg(fmt::color::red), "{}", 42); 564 | \endrst 565 | */ 566 | template >::value&& 568 | detail::is_string::value> 569 | inline auto format_to(OutputIt out, const text_style& ts, const S& format_str, 570 | Args&&... args) -> 571 | typename std::enable_if::type { 572 | return vformat_to(out, ts, detail::to_string_view(format_str), 573 | fmt::make_format_args>>(args...)); 574 | } 575 | 576 | template 577 | struct formatter, Char> : formatter { 578 | template 579 | auto format(const detail::styled_arg& arg, FormatContext& ctx) const 580 | -> decltype(ctx.out()) { 581 | const auto& ts = arg.style; 582 | const auto& value = arg.value; 583 | auto out = ctx.out(); 584 | 585 | bool has_style = false; 586 | if (ts.has_emphasis()) { 587 | has_style = true; 588 | auto emphasis = detail::make_emphasis(ts.get_emphasis()); 589 | out = std::copy(emphasis.begin(), emphasis.end(), out); 590 | } 591 | if (ts.has_foreground()) { 592 | has_style = true; 593 | auto foreground = 594 | detail::make_foreground_color(ts.get_foreground()); 595 | out = std::copy(foreground.begin(), foreground.end(), out); 596 | } 597 | if (ts.has_background()) { 598 | has_style = true; 599 | auto background = 600 | detail::make_background_color(ts.get_background()); 601 | out = std::copy(background.begin(), background.end(), out); 602 | } 603 | out = formatter::format(value, ctx); 604 | if (has_style) { 605 | auto reset_color = string_view("\x1b[0m"); 606 | out = std::copy(reset_color.begin(), reset_color.end(), out); 607 | } 608 | return out; 609 | } 610 | }; 611 | 612 | /** 613 | \rst 614 | Returns an argument that will be formatted using ANSI escape sequences, 615 | to be used in a formatting function. 616 | 617 | **Example**:: 618 | 619 | fmt::print("Elapsed time: {0:.2f} seconds", 620 | fmt::styled(1.23, fmt::fg(fmt::color::green) | 621 | fmt::bg(fmt::color::blue))); 622 | \endrst 623 | */ 624 | template 625 | FMT_CONSTEXPR auto styled(const T& value, text_style ts) 626 | -> detail::styled_arg> { 627 | return detail::styled_arg>{value, ts}; 628 | } 629 | 630 | FMT_END_EXPORT 631 | FMT_END_NAMESPACE 632 | 633 | #endif // FMT_COLOR_H_ 634 | -------------------------------------------------------------------------------- /src/fmt/compile.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - experimental format string compilation 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich and fmt contributors 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_COMPILE_H_ 9 | #define FMT_COMPILE_H_ 10 | 11 | #include "format.h" 12 | 13 | FMT_BEGIN_NAMESPACE 14 | namespace detail { 15 | 16 | template 17 | FMT_CONSTEXPR inline counting_iterator copy_str(InputIt begin, InputIt end, 18 | counting_iterator it) { 19 | return it + (end - begin); 20 | } 21 | 22 | // A compile-time string which is compiled into fast formatting code. 23 | class compiled_string {}; 24 | 25 | template 26 | struct is_compiled_string : std::is_base_of {}; 27 | 28 | /** 29 | \rst 30 | Converts a string literal *s* into a format string that will be parsed at 31 | compile time and converted into efficient formatting code. Requires C++17 32 | ``constexpr if`` compiler support. 33 | 34 | **Example**:: 35 | 36 | // Converts 42 into std::string using the most efficient method and no 37 | // runtime format string processing. 38 | std::string s = fmt::format(FMT_COMPILE("{}"), 42); 39 | \endrst 40 | */ 41 | #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) 42 | # define FMT_COMPILE(s) \ 43 | FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit) 44 | #else 45 | # define FMT_COMPILE(s) FMT_STRING(s) 46 | #endif 47 | 48 | #if FMT_USE_NONTYPE_TEMPLATE_ARGS 49 | template Str> 51 | struct udl_compiled_string : compiled_string { 52 | using char_type = Char; 53 | explicit constexpr operator basic_string_view() const { 54 | return {Str.data, N - 1}; 55 | } 56 | }; 57 | #endif 58 | 59 | template 60 | const T& first(const T& value, const Tail&...) { 61 | return value; 62 | } 63 | 64 | #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) 65 | template struct type_list {}; 66 | 67 | // Returns a reference to the argument at index N from [first, rest...]. 68 | template 69 | constexpr const auto& get([[maybe_unused]] const T& first, 70 | [[maybe_unused]] const Args&... rest) { 71 | static_assert(N < 1 + sizeof...(Args), "index is out of bounds"); 72 | if constexpr (N == 0) 73 | return first; 74 | else 75 | return detail::get(rest...); 76 | } 77 | 78 | template 79 | constexpr int get_arg_index_by_name(basic_string_view name, 80 | type_list) { 81 | return get_arg_index_by_name(name); 82 | } 83 | 84 | template struct get_type_impl; 85 | 86 | template struct get_type_impl> { 87 | using type = 88 | remove_cvref_t(std::declval()...))>; 89 | }; 90 | 91 | template 92 | using get_type = typename get_type_impl::type; 93 | 94 | template struct is_compiled_format : std::false_type {}; 95 | 96 | template struct text { 97 | basic_string_view data; 98 | using char_type = Char; 99 | 100 | template 101 | constexpr OutputIt format(OutputIt out, const Args&...) const { 102 | return write(out, data); 103 | } 104 | }; 105 | 106 | template 107 | struct is_compiled_format> : std::true_type {}; 108 | 109 | template 110 | constexpr text make_text(basic_string_view s, size_t pos, 111 | size_t size) { 112 | return {{&s[pos], size}}; 113 | } 114 | 115 | template struct code_unit { 116 | Char value; 117 | using char_type = Char; 118 | 119 | template 120 | constexpr OutputIt format(OutputIt out, const Args&...) const { 121 | *out++ = value; 122 | return out; 123 | } 124 | }; 125 | 126 | // This ensures that the argument type is convertible to `const T&`. 127 | template 128 | constexpr const T& get_arg_checked(const Args&... args) { 129 | const auto& arg = detail::get(args...); 130 | if constexpr (detail::is_named_arg>()) { 131 | return arg.value; 132 | } else { 133 | return arg; 134 | } 135 | } 136 | 137 | template 138 | struct is_compiled_format> : std::true_type {}; 139 | 140 | // A replacement field that refers to argument N. 141 | template struct field { 142 | using char_type = Char; 143 | 144 | template 145 | constexpr OutputIt format(OutputIt out, const Args&... args) const { 146 | const T& arg = get_arg_checked(args...); 147 | if constexpr (std::is_convertible_v>) { 148 | auto s = basic_string_view(arg); 149 | return copy_str(s.begin(), s.end(), out); 150 | } 151 | return write(out, arg); 152 | } 153 | }; 154 | 155 | template 156 | struct is_compiled_format> : std::true_type {}; 157 | 158 | // A replacement field that refers to argument with name. 159 | template struct runtime_named_field { 160 | using char_type = Char; 161 | basic_string_view name; 162 | 163 | template 164 | constexpr static bool try_format_argument( 165 | OutputIt& out, 166 | // [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9 167 | [[maybe_unused]] basic_string_view arg_name, const T& arg) { 168 | if constexpr (is_named_arg::type>::value) { 169 | if (arg_name == arg.name) { 170 | out = write(out, arg.value); 171 | return true; 172 | } 173 | } 174 | return false; 175 | } 176 | 177 | template 178 | constexpr OutputIt format(OutputIt out, const Args&... args) const { 179 | bool found = (try_format_argument(out, name, args) || ...); 180 | if (!found) { 181 | FMT_THROW(format_error("argument with specified name is not found")); 182 | } 183 | return out; 184 | } 185 | }; 186 | 187 | template 188 | struct is_compiled_format> : std::true_type {}; 189 | 190 | // A replacement field that refers to argument N and has format specifiers. 191 | template struct spec_field { 192 | using char_type = Char; 193 | formatter fmt; 194 | 195 | template 196 | constexpr FMT_INLINE OutputIt format(OutputIt out, 197 | const Args&... args) const { 198 | const auto& vargs = 199 | fmt::make_format_args>(args...); 200 | basic_format_context ctx(out, vargs); 201 | return fmt.format(get_arg_checked(args...), ctx); 202 | } 203 | }; 204 | 205 | template 206 | struct is_compiled_format> : std::true_type {}; 207 | 208 | template struct concat { 209 | L lhs; 210 | R rhs; 211 | using char_type = typename L::char_type; 212 | 213 | template 214 | constexpr OutputIt format(OutputIt out, const Args&... args) const { 215 | out = lhs.format(out, args...); 216 | return rhs.format(out, args...); 217 | } 218 | }; 219 | 220 | template 221 | struct is_compiled_format> : std::true_type {}; 222 | 223 | template 224 | constexpr concat make_concat(L lhs, R rhs) { 225 | return {lhs, rhs}; 226 | } 227 | 228 | struct unknown_format {}; 229 | 230 | template 231 | constexpr size_t parse_text(basic_string_view str, size_t pos) { 232 | for (size_t size = str.size(); pos != size; ++pos) { 233 | if (str[pos] == '{' || str[pos] == '}') break; 234 | } 235 | return pos; 236 | } 237 | 238 | template 239 | constexpr auto compile_format_string(S format_str); 240 | 241 | template 242 | constexpr auto parse_tail(T head, S format_str) { 243 | if constexpr (POS != 244 | basic_string_view(format_str).size()) { 245 | constexpr auto tail = compile_format_string(format_str); 246 | if constexpr (std::is_same, 247 | unknown_format>()) 248 | return tail; 249 | else 250 | return make_concat(head, tail); 251 | } else { 252 | return head; 253 | } 254 | } 255 | 256 | template struct parse_specs_result { 257 | formatter fmt; 258 | size_t end; 259 | int next_arg_id; 260 | }; 261 | 262 | enum { manual_indexing_id = -1 }; 263 | 264 | template 265 | constexpr parse_specs_result parse_specs(basic_string_view str, 266 | size_t pos, int next_arg_id) { 267 | str.remove_prefix(pos); 268 | auto ctx = 269 | compile_parse_context(str, max_value(), nullptr, next_arg_id); 270 | auto f = formatter(); 271 | auto end = f.parse(ctx); 272 | return {f, pos + fmt::detail::to_unsigned(end - str.data()), 273 | next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()}; 274 | } 275 | 276 | template struct arg_id_handler { 277 | arg_ref arg_id; 278 | 279 | constexpr int on_auto() { 280 | FMT_ASSERT(false, "handler cannot be used with automatic indexing"); 281 | return 0; 282 | } 283 | constexpr int on_index(int id) { 284 | arg_id = arg_ref(id); 285 | return 0; 286 | } 287 | constexpr int on_name(basic_string_view id) { 288 | arg_id = arg_ref(id); 289 | return 0; 290 | } 291 | }; 292 | 293 | template struct parse_arg_id_result { 294 | arg_ref arg_id; 295 | const Char* arg_id_end; 296 | }; 297 | 298 | template 299 | constexpr auto parse_arg_id(const Char* begin, const Char* end) { 300 | auto handler = arg_id_handler{arg_ref{}}; 301 | auto arg_id_end = parse_arg_id(begin, end, handler); 302 | return parse_arg_id_result{handler.arg_id, arg_id_end}; 303 | } 304 | 305 | template struct field_type { 306 | using type = remove_cvref_t; 307 | }; 308 | 309 | template 310 | struct field_type::value>> { 311 | using type = remove_cvref_t; 312 | }; 313 | 314 | template 316 | constexpr auto parse_replacement_field_then_tail(S format_str) { 317 | using char_type = typename S::char_type; 318 | constexpr auto str = basic_string_view(format_str); 319 | constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type(); 320 | if constexpr (c == '}') { 321 | return parse_tail( 322 | field::type, ARG_INDEX>(), 323 | format_str); 324 | } else if constexpr (c != ':') { 325 | FMT_THROW(format_error("expected ':'")); 326 | } else { 327 | constexpr auto result = parse_specs::type>( 328 | str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID); 329 | if constexpr (result.end >= str.size() || str[result.end] != '}') { 330 | FMT_THROW(format_error("expected '}'")); 331 | return 0; 332 | } else { 333 | return parse_tail( 334 | spec_field::type, ARG_INDEX>{ 335 | result.fmt}, 336 | format_str); 337 | } 338 | } 339 | } 340 | 341 | // Compiles a non-empty format string and returns the compiled representation 342 | // or unknown_format() on unrecognized input. 343 | template 344 | constexpr auto compile_format_string(S format_str) { 345 | using char_type = typename S::char_type; 346 | constexpr auto str = basic_string_view(format_str); 347 | if constexpr (str[POS] == '{') { 348 | if constexpr (POS + 1 == str.size()) 349 | FMT_THROW(format_error("unmatched '{' in format string")); 350 | if constexpr (str[POS + 1] == '{') { 351 | return parse_tail(make_text(str, POS, 1), format_str); 352 | } else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') { 353 | static_assert(ID != manual_indexing_id, 354 | "cannot switch from manual to automatic argument indexing"); 355 | constexpr auto next_id = 356 | ID != manual_indexing_id ? ID + 1 : manual_indexing_id; 357 | return parse_replacement_field_then_tail, Args, 358 | POS + 1, ID, next_id>( 359 | format_str); 360 | } else { 361 | constexpr auto arg_id_result = 362 | parse_arg_id(str.data() + POS + 1, str.data() + str.size()); 363 | constexpr auto arg_id_end_pos = arg_id_result.arg_id_end - str.data(); 364 | constexpr char_type c = 365 | arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type(); 366 | static_assert(c == '}' || c == ':', "missing '}' in format string"); 367 | if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) { 368 | static_assert( 369 | ID == manual_indexing_id || ID == 0, 370 | "cannot switch from automatic to manual argument indexing"); 371 | constexpr auto arg_index = arg_id_result.arg_id.val.index; 372 | return parse_replacement_field_then_tail, 373 | Args, arg_id_end_pos, 374 | arg_index, manual_indexing_id>( 375 | format_str); 376 | } else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) { 377 | constexpr auto arg_index = 378 | get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{}); 379 | if constexpr (arg_index >= 0) { 380 | constexpr auto next_id = 381 | ID != manual_indexing_id ? ID + 1 : manual_indexing_id; 382 | return parse_replacement_field_then_tail< 383 | decltype(get_type::value), Args, arg_id_end_pos, 384 | arg_index, next_id>(format_str); 385 | } else if constexpr (c == '}') { 386 | return parse_tail( 387 | runtime_named_field{arg_id_result.arg_id.val.name}, 388 | format_str); 389 | } else if constexpr (c == ':') { 390 | return unknown_format(); // no type info for specs parsing 391 | } 392 | } 393 | } 394 | } else if constexpr (str[POS] == '}') { 395 | if constexpr (POS + 1 == str.size()) 396 | FMT_THROW(format_error("unmatched '}' in format string")); 397 | return parse_tail(make_text(str, POS, 1), format_str); 398 | } else { 399 | constexpr auto end = parse_text(str, POS + 1); 400 | if constexpr (end - POS > 1) { 401 | return parse_tail(make_text(str, POS, end - POS), 402 | format_str); 403 | } else { 404 | return parse_tail(code_unit{str[POS]}, 405 | format_str); 406 | } 407 | } 408 | } 409 | 410 | template ::value)> 412 | constexpr auto compile(S format_str) { 413 | constexpr auto str = basic_string_view(format_str); 414 | if constexpr (str.size() == 0) { 415 | return detail::make_text(str, 0, 0); 416 | } else { 417 | constexpr auto result = 418 | detail::compile_format_string, 0, 0>( 419 | format_str); 420 | return result; 421 | } 422 | } 423 | #endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) 424 | } // namespace detail 425 | 426 | FMT_BEGIN_EXPORT 427 | 428 | #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) 429 | 430 | template ::value)> 433 | FMT_INLINE std::basic_string format(const CompiledFormat& cf, 434 | const Args&... args) { 435 | auto s = std::basic_string(); 436 | cf.format(std::back_inserter(s), args...); 437 | return s; 438 | } 439 | 440 | template ::value)> 442 | constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf, 443 | const Args&... args) { 444 | return cf.format(out, args...); 445 | } 446 | 447 | template ::value)> 449 | FMT_INLINE std::basic_string format(const S&, 450 | Args&&... args) { 451 | if constexpr (std::is_same::value) { 452 | constexpr auto str = basic_string_view(S()); 453 | if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') { 454 | const auto& first = detail::first(args...); 455 | if constexpr (detail::is_named_arg< 456 | remove_cvref_t>::value) { 457 | return fmt::to_string(first.value); 458 | } else { 459 | return fmt::to_string(first); 460 | } 461 | } 462 | } 463 | constexpr auto compiled = detail::compile(S()); 464 | if constexpr (std::is_same, 465 | detail::unknown_format>()) { 466 | return fmt::format( 467 | static_cast>(S()), 468 | std::forward(args)...); 469 | } else { 470 | return fmt::format(compiled, std::forward(args)...); 471 | } 472 | } 473 | 474 | template ::value)> 476 | FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) { 477 | constexpr auto compiled = detail::compile(S()); 478 | if constexpr (std::is_same, 479 | detail::unknown_format>()) { 480 | return fmt::format_to( 481 | out, static_cast>(S()), 482 | std::forward(args)...); 483 | } else { 484 | return fmt::format_to(out, compiled, std::forward(args)...); 485 | } 486 | } 487 | #endif 488 | 489 | template ::value)> 491 | format_to_n_result format_to_n(OutputIt out, size_t n, 492 | const S& format_str, Args&&... args) { 493 | using traits = detail::fixed_buffer_traits; 494 | auto buf = detail::iterator_buffer(out, n); 495 | format_to(std::back_inserter(buf), format_str, std::forward(args)...); 496 | return {buf.out(), buf.count()}; 497 | } 498 | 499 | template ::value)> 501 | FMT_CONSTEXPR20 size_t formatted_size(const S& format_str, 502 | const Args&... args) { 503 | return fmt::format_to(detail::counting_iterator(), format_str, args...) 504 | .count(); 505 | } 506 | 507 | template ::value)> 509 | void print(std::FILE* f, const S& format_str, const Args&... args) { 510 | memory_buffer buffer; 511 | fmt::format_to(std::back_inserter(buffer), format_str, args...); 512 | detail::print(f, {buffer.data(), buffer.size()}); 513 | } 514 | 515 | template ::value)> 517 | void print(const S& format_str, const Args&... args) { 518 | print(stdout, format_str, args...); 519 | } 520 | 521 | #if FMT_USE_NONTYPE_TEMPLATE_ARGS 522 | inline namespace literals { 523 | template constexpr auto operator""_cf() { 524 | using char_t = remove_cvref_t; 525 | return detail::udl_compiled_string(); 527 | } 528 | } // namespace literals 529 | #endif 530 | 531 | FMT_END_EXPORT 532 | FMT_END_NAMESPACE 533 | 534 | #endif // FMT_COMPILE_H_ 535 | -------------------------------------------------------------------------------- /src/fmt/os.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - optional OS-specific functionality 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_OS_H_ 9 | #define FMT_OS_H_ 10 | 11 | #include 12 | #include 13 | #include 14 | #include // std::system_error 15 | 16 | #include "format.h" 17 | 18 | #if defined __APPLE__ || defined(__FreeBSD__) 19 | # if FMT_HAS_INCLUDE() 20 | # include // for LC_NUMERIC_MASK on OS X 21 | # endif 22 | #endif 23 | 24 | #ifndef FMT_USE_FCNTL 25 | // UWP doesn't provide _pipe. 26 | # if FMT_HAS_INCLUDE("winapifamily.h") 27 | # include 28 | # endif 29 | # if (FMT_HAS_INCLUDE() || defined(__APPLE__) || \ 30 | defined(__linux__)) && \ 31 | (!defined(WINAPI_FAMILY) || \ 32 | (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) 33 | # include // for O_RDONLY 34 | # define FMT_USE_FCNTL 1 35 | # else 36 | # define FMT_USE_FCNTL 0 37 | # endif 38 | #endif 39 | 40 | #ifndef FMT_POSIX 41 | # if defined(_WIN32) && !defined(__MINGW32__) 42 | // Fix warnings about deprecated symbols. 43 | # define FMT_POSIX(call) _##call 44 | # else 45 | # define FMT_POSIX(call) call 46 | # endif 47 | #endif 48 | 49 | // Calls to system functions are wrapped in FMT_SYSTEM for testability. 50 | #ifdef FMT_SYSTEM 51 | # define FMT_HAS_SYSTEM 52 | # define FMT_POSIX_CALL(call) FMT_SYSTEM(call) 53 | #else 54 | # define FMT_SYSTEM(call) ::call 55 | # ifdef _WIN32 56 | // Fix warnings about deprecated symbols. 57 | # define FMT_POSIX_CALL(call) ::_##call 58 | # else 59 | # define FMT_POSIX_CALL(call) ::call 60 | # endif 61 | #endif 62 | 63 | // Retries the expression while it evaluates to error_result and errno 64 | // equals to EINTR. 65 | #ifndef _WIN32 66 | # define FMT_RETRY_VAL(result, expression, error_result) \ 67 | do { \ 68 | (result) = (expression); \ 69 | } while ((result) == (error_result) && errno == EINTR) 70 | #else 71 | # define FMT_RETRY_VAL(result, expression, error_result) result = (expression) 72 | #endif 73 | 74 | #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) 75 | 76 | FMT_BEGIN_NAMESPACE 77 | FMT_BEGIN_EXPORT 78 | 79 | /** 80 | \rst 81 | A reference to a null-terminated string. It can be constructed from a C 82 | string or ``std::string``. 83 | 84 | You can use one of the following type aliases for common character types: 85 | 86 | +---------------+-----------------------------+ 87 | | Type | Definition | 88 | +===============+=============================+ 89 | | cstring_view | basic_cstring_view | 90 | +---------------+-----------------------------+ 91 | | wcstring_view | basic_cstring_view | 92 | +---------------+-----------------------------+ 93 | 94 | This class is most useful as a parameter type to allow passing 95 | different types of strings to a function, for example:: 96 | 97 | template 98 | std::string format(cstring_view format_str, const Args & ... args); 99 | 100 | format("{}", 42); 101 | format(std::string("{}"), 42); 102 | \endrst 103 | */ 104 | template class basic_cstring_view { 105 | private: 106 | const Char* data_; 107 | 108 | public: 109 | /** Constructs a string reference object from a C string. */ 110 | basic_cstring_view(const Char* s) : data_(s) {} 111 | 112 | /** 113 | \rst 114 | Constructs a string reference from an ``std::string`` object. 115 | \endrst 116 | */ 117 | basic_cstring_view(const std::basic_string& s) : data_(s.c_str()) {} 118 | 119 | /** Returns the pointer to a C string. */ 120 | const Char* c_str() const { return data_; } 121 | }; 122 | 123 | using cstring_view = basic_cstring_view; 124 | using wcstring_view = basic_cstring_view; 125 | 126 | #ifdef _WIN32 127 | FMT_API const std::error_category& system_category() noexcept; 128 | 129 | namespace detail { 130 | FMT_API void format_windows_error(buffer& out, int error_code, 131 | const char* message) noexcept; 132 | } 133 | 134 | FMT_API std::system_error vwindows_error(int error_code, string_view format_str, 135 | format_args args); 136 | 137 | /** 138 | \rst 139 | Constructs a :class:`std::system_error` object with the description 140 | of the form 141 | 142 | .. parsed-literal:: 143 | **: ** 144 | 145 | where ** is the formatted message and ** is the 146 | system message corresponding to the error code. 147 | *error_code* is a Windows error code as given by ``GetLastError``. 148 | If *error_code* is not a valid error code such as -1, the system message 149 | will look like "error -1". 150 | 151 | **Example**:: 152 | 153 | // This throws a system_error with the description 154 | // cannot open file 'madeup': The system cannot find the file specified. 155 | // or similar (system message may vary). 156 | const char *filename = "madeup"; 157 | LPOFSTRUCT of = LPOFSTRUCT(); 158 | HFILE file = OpenFile(filename, &of, OF_READ); 159 | if (file == HFILE_ERROR) { 160 | throw fmt::windows_error(GetLastError(), 161 | "cannot open file '{}'", filename); 162 | } 163 | \endrst 164 | */ 165 | template 166 | std::system_error windows_error(int error_code, string_view message, 167 | const Args&... args) { 168 | return vwindows_error(error_code, message, fmt::make_format_args(args...)); 169 | } 170 | 171 | // Reports a Windows error without throwing an exception. 172 | // Can be used to report errors from destructors. 173 | FMT_API void report_windows_error(int error_code, const char* message) noexcept; 174 | #else 175 | inline const std::error_category& system_category() noexcept { 176 | return std::system_category(); 177 | } 178 | #endif // _WIN32 179 | 180 | // std::system is not available on some platforms such as iOS (#2248). 181 | #ifdef __OSX__ 182 | template > 183 | void say(const S& format_str, Args&&... args) { 184 | std::system(format("say \"{}\"", format(format_str, args...)).c_str()); 185 | } 186 | #endif 187 | 188 | // A buffered file. 189 | class buffered_file { 190 | private: 191 | FILE* file_; 192 | 193 | friend class file; 194 | 195 | explicit buffered_file(FILE* f) : file_(f) {} 196 | 197 | public: 198 | buffered_file(const buffered_file&) = delete; 199 | void operator=(const buffered_file&) = delete; 200 | 201 | // Constructs a buffered_file object which doesn't represent any file. 202 | buffered_file() noexcept : file_(nullptr) {} 203 | 204 | // Destroys the object closing the file it represents if any. 205 | FMT_API ~buffered_file() noexcept; 206 | 207 | public: 208 | buffered_file(buffered_file&& other) noexcept : file_(other.file_) { 209 | other.file_ = nullptr; 210 | } 211 | 212 | buffered_file& operator=(buffered_file&& other) { 213 | close(); 214 | file_ = other.file_; 215 | other.file_ = nullptr; 216 | return *this; 217 | } 218 | 219 | // Opens a file. 220 | FMT_API buffered_file(cstring_view filename, cstring_view mode); 221 | 222 | // Closes the file. 223 | FMT_API void close(); 224 | 225 | // Returns the pointer to a FILE object representing this file. 226 | FILE* get() const noexcept { return file_; } 227 | 228 | FMT_API int descriptor() const; 229 | 230 | void vprint(string_view format_str, format_args args) { 231 | fmt::vprint(file_, format_str, args); 232 | } 233 | 234 | template 235 | inline void print(string_view format_str, const Args&... args) { 236 | vprint(format_str, fmt::make_format_args(args...)); 237 | } 238 | }; 239 | 240 | #if FMT_USE_FCNTL 241 | // A file. Closed file is represented by a file object with descriptor -1. 242 | // Methods that are not declared with noexcept may throw 243 | // fmt::system_error in case of failure. Note that some errors such as 244 | // closing the file multiple times will cause a crash on Windows rather 245 | // than an exception. You can get standard behavior by overriding the 246 | // invalid parameter handler with _set_invalid_parameter_handler. 247 | class FMT_API file { 248 | private: 249 | int fd_; // File descriptor. 250 | 251 | // Constructs a file object with a given descriptor. 252 | explicit file(int fd) : fd_(fd) {} 253 | 254 | public: 255 | // Possible values for the oflag argument to the constructor. 256 | enum { 257 | RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only. 258 | WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only. 259 | RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing. 260 | CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist. 261 | APPEND = FMT_POSIX(O_APPEND), // Open in append mode. 262 | TRUNC = FMT_POSIX(O_TRUNC) // Truncate the content of the file. 263 | }; 264 | 265 | // Constructs a file object which doesn't represent any file. 266 | file() noexcept : fd_(-1) {} 267 | 268 | // Opens a file and constructs a file object representing this file. 269 | file(cstring_view path, int oflag); 270 | 271 | public: 272 | file(const file&) = delete; 273 | void operator=(const file&) = delete; 274 | 275 | file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; } 276 | 277 | // Move assignment is not noexcept because close may throw. 278 | file& operator=(file&& other) { 279 | close(); 280 | fd_ = other.fd_; 281 | other.fd_ = -1; 282 | return *this; 283 | } 284 | 285 | // Destroys the object closing the file it represents if any. 286 | ~file() noexcept; 287 | 288 | // Returns the file descriptor. 289 | int descriptor() const noexcept { return fd_; } 290 | 291 | // Closes the file. 292 | void close(); 293 | 294 | // Returns the file size. The size has signed type for consistency with 295 | // stat::st_size. 296 | long long size() const; 297 | 298 | // Attempts to read count bytes from the file into the specified buffer. 299 | size_t read(void* buffer, size_t count); 300 | 301 | // Attempts to write count bytes from the specified buffer to the file. 302 | size_t write(const void* buffer, size_t count); 303 | 304 | // Duplicates a file descriptor with the dup function and returns 305 | // the duplicate as a file object. 306 | static file dup(int fd); 307 | 308 | // Makes fd be the copy of this file descriptor, closing fd first if 309 | // necessary. 310 | void dup2(int fd); 311 | 312 | // Makes fd be the copy of this file descriptor, closing fd first if 313 | // necessary. 314 | void dup2(int fd, std::error_code& ec) noexcept; 315 | 316 | // Creates a pipe setting up read_end and write_end file objects for reading 317 | // and writing respectively. 318 | static void pipe(file& read_end, file& write_end); 319 | 320 | // Creates a buffered_file object associated with this file and detaches 321 | // this file object from the file. 322 | buffered_file fdopen(const char* mode); 323 | 324 | # if defined(_WIN32) && !defined(__MINGW32__) 325 | // Opens a file and constructs a file object representing this file by 326 | // wcstring_view filename. Windows only. 327 | static file open_windows_file(wcstring_view path, int oflag); 328 | # endif 329 | }; 330 | 331 | // Returns the memory page size. 332 | long getpagesize(); 333 | 334 | namespace detail { 335 | 336 | struct buffer_size { 337 | buffer_size() = default; 338 | size_t value = 0; 339 | buffer_size operator=(size_t val) const { 340 | auto bs = buffer_size(); 341 | bs.value = val; 342 | return bs; 343 | } 344 | }; 345 | 346 | struct ostream_params { 347 | int oflag = file::WRONLY | file::CREATE | file::TRUNC; 348 | size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768; 349 | 350 | ostream_params() {} 351 | 352 | template 353 | ostream_params(T... params, int new_oflag) : ostream_params(params...) { 354 | oflag = new_oflag; 355 | } 356 | 357 | template 358 | ostream_params(T... params, detail::buffer_size bs) 359 | : ostream_params(params...) { 360 | this->buffer_size = bs.value; 361 | } 362 | 363 | // Intel has a bug that results in failure to deduce a constructor 364 | // for empty parameter packs. 365 | # if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 2000 366 | ostream_params(int new_oflag) : oflag(new_oflag) {} 367 | ostream_params(detail::buffer_size bs) : buffer_size(bs.value) {} 368 | # endif 369 | }; 370 | 371 | class file_buffer final : public buffer { 372 | file file_; 373 | 374 | FMT_API void grow(size_t) override; 375 | 376 | public: 377 | FMT_API file_buffer(cstring_view path, const ostream_params& params); 378 | FMT_API file_buffer(file_buffer&& other); 379 | FMT_API ~file_buffer(); 380 | 381 | void flush() { 382 | if (size() == 0) return; 383 | file_.write(data(), size() * sizeof(data()[0])); 384 | clear(); 385 | } 386 | 387 | void close() { 388 | flush(); 389 | file_.close(); 390 | } 391 | }; 392 | 393 | } // namespace detail 394 | 395 | // Added {} below to work around default constructor error known to 396 | // occur in Xcode versions 7.2.1 and 8.2.1. 397 | constexpr detail::buffer_size buffer_size{}; 398 | 399 | /** A fast output stream which is not thread-safe. */ 400 | class FMT_API ostream { 401 | private: 402 | FMT_MSC_WARNING(suppress : 4251) 403 | detail::file_buffer buffer_; 404 | 405 | ostream(cstring_view path, const detail::ostream_params& params) 406 | : buffer_(path, params) {} 407 | 408 | public: 409 | ostream(ostream&& other) : buffer_(std::move(other.buffer_)) {} 410 | 411 | ~ostream(); 412 | 413 | void flush() { buffer_.flush(); } 414 | 415 | template 416 | friend ostream output_file(cstring_view path, T... params); 417 | 418 | void close() { buffer_.close(); } 419 | 420 | /** 421 | Formats ``args`` according to specifications in ``fmt`` and writes the 422 | output to the file. 423 | */ 424 | template void print(format_string fmt, T&&... args) { 425 | vformat_to(std::back_inserter(buffer_), fmt, 426 | fmt::make_format_args(args...)); 427 | } 428 | }; 429 | 430 | /** 431 | \rst 432 | Opens a file for writing. Supported parameters passed in *params*: 433 | 434 | * ````: Flags passed to `open 435 | `_ 436 | (``file::WRONLY | file::CREATE | file::TRUNC`` by default) 437 | * ``buffer_size=``: Output buffer size 438 | 439 | **Example**:: 440 | 441 | auto out = fmt::output_file("guide.txt"); 442 | out.print("Don't {}", "Panic"); 443 | \endrst 444 | */ 445 | template 446 | inline ostream output_file(cstring_view path, T... params) { 447 | return {path, detail::ostream_params(params...)}; 448 | } 449 | #endif // FMT_USE_FCNTL 450 | 451 | FMT_END_EXPORT 452 | FMT_END_NAMESPACE 453 | 454 | #endif // FMT_OS_H_ 455 | -------------------------------------------------------------------------------- /src/fmt/ostream.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - std::ostream support 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_OSTREAM_H_ 9 | #define FMT_OSTREAM_H_ 10 | 11 | #include // std::filebuf 12 | 13 | #ifdef _WIN32 14 | # ifdef __GLIBCXX__ 15 | # include 16 | # include 17 | # endif 18 | # include 19 | #endif 20 | 21 | #include "format.h" 22 | 23 | FMT_BEGIN_NAMESPACE 24 | 25 | namespace detail { 26 | 27 | // Generate a unique explicit instantion in every translation unit using a tag 28 | // type in an anonymous namespace. 29 | namespace { 30 | struct file_access_tag {}; 31 | } // namespace 32 | template 33 | class file_access { 34 | friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; } 35 | }; 36 | 37 | #if FMT_MSC_VERSION 38 | template class file_access; 40 | auto get_file(std::filebuf&) -> FILE*; 41 | #endif 42 | 43 | inline bool write_ostream_unicode(std::ostream& os, fmt::string_view data) { 44 | FILE* f = nullptr; 45 | #if FMT_MSC_VERSION 46 | if (auto* buf = dynamic_cast(os.rdbuf())) 47 | f = get_file(*buf); 48 | else 49 | return false; 50 | #elif defined(_WIN32) && defined(__GLIBCXX__) 51 | auto* rdbuf = os.rdbuf(); 52 | if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf*>(rdbuf)) 53 | f = sfbuf->file(); 54 | else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf*>(rdbuf)) 55 | f = fbuf->file(); 56 | else 57 | return false; 58 | #else 59 | ignore_unused(os, data, f); 60 | #endif 61 | #ifdef _WIN32 62 | if (f) { 63 | int fd = _fileno(f); 64 | if (_isatty(fd)) { 65 | os.flush(); 66 | return write_console(fd, data); 67 | } 68 | } 69 | #endif 70 | return false; 71 | } 72 | inline bool write_ostream_unicode(std::wostream&, 73 | fmt::basic_string_view) { 74 | return false; 75 | } 76 | 77 | // Write the content of buf to os. 78 | // It is a separate function rather than a part of vprint to simplify testing. 79 | template 80 | void write_buffer(std::basic_ostream& os, buffer& buf) { 81 | const Char* buf_data = buf.data(); 82 | using unsigned_streamsize = std::make_unsigned::type; 83 | unsigned_streamsize size = buf.size(); 84 | unsigned_streamsize max_size = to_unsigned(max_value()); 85 | do { 86 | unsigned_streamsize n = size <= max_size ? size : max_size; 87 | os.write(buf_data, static_cast(n)); 88 | buf_data += n; 89 | size -= n; 90 | } while (size != 0); 91 | } 92 | 93 | template 94 | void format_value(buffer& buf, const T& value, 95 | locale_ref loc = locale_ref()) { 96 | auto&& format_buf = formatbuf>(buf); 97 | auto&& output = std::basic_ostream(&format_buf); 98 | #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) 99 | if (loc) output.imbue(loc.get()); 100 | #endif 101 | output << value; 102 | output.exceptions(std::ios_base::failbit | std::ios_base::badbit); 103 | } 104 | 105 | template struct streamed_view { 106 | const T& value; 107 | }; 108 | 109 | } // namespace detail 110 | 111 | // Formats an object of type T that has an overloaded ostream operator<<. 112 | template 113 | struct basic_ostream_formatter : formatter, Char> { 114 | void set_debug_format() = delete; 115 | 116 | template 117 | auto format(const T& value, basic_format_context& ctx) const 118 | -> OutputIt { 119 | auto buffer = basic_memory_buffer(); 120 | detail::format_value(buffer, value, ctx.locale()); 121 | return formatter, Char>::format( 122 | {buffer.data(), buffer.size()}, ctx); 123 | } 124 | }; 125 | 126 | using ostream_formatter = basic_ostream_formatter; 127 | 128 | template 129 | struct formatter, Char> 130 | : basic_ostream_formatter { 131 | template 132 | auto format(detail::streamed_view view, 133 | basic_format_context& ctx) const -> OutputIt { 134 | return basic_ostream_formatter::format(view.value, ctx); 135 | } 136 | }; 137 | 138 | /** 139 | \rst 140 | Returns a view that formats `value` via an ostream ``operator<<``. 141 | 142 | **Example**:: 143 | 144 | fmt::print("Current thread id: {}\n", 145 | fmt::streamed(std::this_thread::get_id())); 146 | \endrst 147 | */ 148 | template 149 | constexpr auto streamed(const T& value) -> detail::streamed_view { 150 | return {value}; 151 | } 152 | 153 | namespace detail { 154 | 155 | inline void vprint_directly(std::ostream& os, string_view format_str, 156 | format_args args) { 157 | auto buffer = memory_buffer(); 158 | detail::vformat_to(buffer, format_str, args); 159 | detail::write_buffer(os, buffer); 160 | } 161 | 162 | } // namespace detail 163 | 164 | FMT_EXPORT template 165 | void vprint(std::basic_ostream& os, 166 | basic_string_view> format_str, 167 | basic_format_args>> args) { 168 | auto buffer = basic_memory_buffer(); 169 | detail::vformat_to(buffer, format_str, args); 170 | if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return; 171 | detail::write_buffer(os, buffer); 172 | } 173 | 174 | /** 175 | \rst 176 | Prints formatted data to the stream *os*. 177 | 178 | **Example**:: 179 | 180 | fmt::print(cerr, "Don't {}!", "panic"); 181 | \endrst 182 | */ 183 | FMT_EXPORT template 184 | void print(std::ostream& os, format_string fmt, T&&... args) { 185 | const auto& vargs = fmt::make_format_args(args...); 186 | if (detail::is_utf8()) 187 | vprint(os, fmt, vargs); 188 | else 189 | detail::vprint_directly(os, fmt, vargs); 190 | } 191 | 192 | FMT_EXPORT 193 | template 194 | void print(std::wostream& os, 195 | basic_format_string...> fmt, 196 | Args&&... args) { 197 | vprint(os, fmt, fmt::make_format_args>(args...)); 198 | } 199 | 200 | FMT_EXPORT template 201 | void println(std::ostream& os, format_string fmt, T&&... args) { 202 | fmt::print(os, "{}\n", fmt::format(fmt, std::forward(args)...)); 203 | } 204 | 205 | FMT_EXPORT 206 | template 207 | void println(std::wostream& os, 208 | basic_format_string...> fmt, 209 | Args&&... args) { 210 | print(os, L"{}\n", fmt::format(fmt, std::forward(args)...)); 211 | } 212 | 213 | FMT_END_NAMESPACE 214 | 215 | #endif // FMT_OSTREAM_H_ 216 | -------------------------------------------------------------------------------- /src/fmt/printf.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - legacy printf implementation 2 | // 3 | // Copyright (c) 2012 - 2016, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_PRINTF_H_ 9 | #define FMT_PRINTF_H_ 10 | 11 | #include // std::max 12 | #include // std::numeric_limits 13 | 14 | #include "format.h" 15 | 16 | FMT_BEGIN_NAMESPACE 17 | FMT_BEGIN_EXPORT 18 | 19 | template struct printf_formatter { 20 | printf_formatter() = delete; 21 | }; 22 | 23 | template class basic_printf_context { 24 | private: 25 | detail::buffer_appender out_; 26 | basic_format_args args_; 27 | 28 | static_assert(std::is_same::value || 29 | std::is_same::value, 30 | "Unsupported code unit type."); 31 | 32 | public: 33 | using char_type = Char; 34 | using parse_context_type = basic_format_parse_context; 35 | template using formatter_type = printf_formatter; 36 | 37 | /** 38 | \rst 39 | Constructs a ``printf_context`` object. References to the arguments are 40 | stored in the context object so make sure they have appropriate lifetimes. 41 | \endrst 42 | */ 43 | basic_printf_context(detail::buffer_appender out, 44 | basic_format_args args) 45 | : out_(out), args_(args) {} 46 | 47 | auto out() -> detail::buffer_appender { return out_; } 48 | void advance_to(detail::buffer_appender) {} 49 | 50 | auto locale() -> detail::locale_ref { return {}; } 51 | 52 | auto arg(int id) const -> basic_format_arg { 53 | return args_.get(id); 54 | } 55 | 56 | FMT_CONSTEXPR void on_error(const char* message) { 57 | detail::error_handler().on_error(message); 58 | } 59 | }; 60 | 61 | namespace detail { 62 | 63 | // Checks if a value fits in int - used to avoid warnings about comparing 64 | // signed and unsigned integers. 65 | template struct int_checker { 66 | template static auto fits_in_int(T value) -> bool { 67 | unsigned max = max_value(); 68 | return value <= max; 69 | } 70 | static auto fits_in_int(bool) -> bool { return true; } 71 | }; 72 | 73 | template <> struct int_checker { 74 | template static auto fits_in_int(T value) -> bool { 75 | return value >= (std::numeric_limits::min)() && 76 | value <= max_value(); 77 | } 78 | static auto fits_in_int(int) -> bool { return true; } 79 | }; 80 | 81 | struct printf_precision_handler { 82 | template ::value)> 83 | auto operator()(T value) -> int { 84 | if (!int_checker::is_signed>::fits_in_int(value)) 85 | throw_format_error("number is too big"); 86 | return (std::max)(static_cast(value), 0); 87 | } 88 | 89 | template ::value)> 90 | auto operator()(T) -> int { 91 | throw_format_error("precision is not integer"); 92 | return 0; 93 | } 94 | }; 95 | 96 | // An argument visitor that returns true iff arg is a zero integer. 97 | struct is_zero_int { 98 | template ::value)> 99 | auto operator()(T value) -> bool { 100 | return value == 0; 101 | } 102 | 103 | template ::value)> 104 | auto operator()(T) -> bool { 105 | return false; 106 | } 107 | }; 108 | 109 | template struct make_unsigned_or_bool : std::make_unsigned {}; 110 | 111 | template <> struct make_unsigned_or_bool { 112 | using type = bool; 113 | }; 114 | 115 | template class arg_converter { 116 | private: 117 | using char_type = typename Context::char_type; 118 | 119 | basic_format_arg& arg_; 120 | char_type type_; 121 | 122 | public: 123 | arg_converter(basic_format_arg& arg, char_type type) 124 | : arg_(arg), type_(type) {} 125 | 126 | void operator()(bool value) { 127 | if (type_ != 's') operator()(value); 128 | } 129 | 130 | template ::value)> 131 | void operator()(U value) { 132 | bool is_signed = type_ == 'd' || type_ == 'i'; 133 | using target_type = conditional_t::value, U, T>; 134 | if (const_check(sizeof(target_type) <= sizeof(int))) { 135 | // Extra casts are used to silence warnings. 136 | if (is_signed) { 137 | auto n = static_cast(static_cast(value)); 138 | arg_ = detail::make_arg(n); 139 | } else { 140 | using unsigned_type = typename make_unsigned_or_bool::type; 141 | auto n = static_cast(static_cast(value)); 142 | arg_ = detail::make_arg(n); 143 | } 144 | } else { 145 | if (is_signed) { 146 | // glibc's printf doesn't sign extend arguments of smaller types: 147 | // std::printf("%lld", -42); // prints "4294967254" 148 | // but we don't have to do the same because it's a UB. 149 | auto n = static_cast(value); 150 | arg_ = detail::make_arg(n); 151 | } else { 152 | auto n = static_cast::type>(value); 153 | arg_ = detail::make_arg(n); 154 | } 155 | } 156 | } 157 | 158 | template ::value)> 159 | void operator()(U) {} // No conversion needed for non-integral types. 160 | }; 161 | 162 | // Converts an integer argument to T for printf, if T is an integral type. 163 | // If T is void, the argument is converted to corresponding signed or unsigned 164 | // type depending on the type specifier: 'd' and 'i' - signed, other - 165 | // unsigned). 166 | template 167 | void convert_arg(basic_format_arg& arg, Char type) { 168 | visit_format_arg(arg_converter(arg, type), arg); 169 | } 170 | 171 | // Converts an integer argument to char for printf. 172 | template class char_converter { 173 | private: 174 | basic_format_arg& arg_; 175 | 176 | public: 177 | explicit char_converter(basic_format_arg& arg) : arg_(arg) {} 178 | 179 | template ::value)> 180 | void operator()(T value) { 181 | auto c = static_cast(value); 182 | arg_ = detail::make_arg(c); 183 | } 184 | 185 | template ::value)> 186 | void operator()(T) {} // No conversion needed for non-integral types. 187 | }; 188 | 189 | // An argument visitor that return a pointer to a C string if argument is a 190 | // string or null otherwise. 191 | template struct get_cstring { 192 | template auto operator()(T) -> const Char* { return nullptr; } 193 | auto operator()(const Char* s) -> const Char* { return s; } 194 | }; 195 | 196 | // Checks if an argument is a valid printf width specifier and sets 197 | // left alignment if it is negative. 198 | template class printf_width_handler { 199 | private: 200 | format_specs& specs_; 201 | 202 | public: 203 | explicit printf_width_handler(format_specs& specs) : specs_(specs) {} 204 | 205 | template ::value)> 206 | auto operator()(T value) -> unsigned { 207 | auto width = static_cast>(value); 208 | if (detail::is_negative(value)) { 209 | specs_.align = align::left; 210 | width = 0 - width; 211 | } 212 | unsigned int_max = max_value(); 213 | if (width > int_max) throw_format_error("number is too big"); 214 | return static_cast(width); 215 | } 216 | 217 | template ::value)> 218 | auto operator()(T) -> unsigned { 219 | throw_format_error("width is not integer"); 220 | return 0; 221 | } 222 | }; 223 | 224 | // Workaround for a bug with the XL compiler when initializing 225 | // printf_arg_formatter's base class. 226 | template 227 | auto make_arg_formatter(buffer_appender iter, format_specs& s) 228 | -> arg_formatter { 229 | return {iter, s, locale_ref()}; 230 | } 231 | 232 | // The ``printf`` argument formatter. 233 | template 234 | class printf_arg_formatter : public arg_formatter { 235 | private: 236 | using base = arg_formatter; 237 | using context_type = basic_printf_context; 238 | 239 | context_type& context_; 240 | 241 | void write_null_pointer(bool is_string = false) { 242 | auto s = this->specs; 243 | s.type = presentation_type::none; 244 | write_bytes(this->out, is_string ? "(null)" : "(nil)", s); 245 | } 246 | 247 | public: 248 | printf_arg_formatter(buffer_appender iter, format_specs& s, 249 | context_type& ctx) 250 | : base(make_arg_formatter(iter, s)), context_(ctx) {} 251 | 252 | void operator()(monostate value) { base::operator()(value); } 253 | 254 | template ::value)> 255 | void operator()(T value) { 256 | // MSVC2013 fails to compile separate overloads for bool and Char so use 257 | // std::is_same instead. 258 | if (!std::is_same::value) { 259 | base::operator()(value); 260 | return; 261 | } 262 | format_specs fmt_specs = this->specs; 263 | if (fmt_specs.type != presentation_type::none && 264 | fmt_specs.type != presentation_type::chr) { 265 | return (*this)(static_cast(value)); 266 | } 267 | fmt_specs.sign = sign::none; 268 | fmt_specs.alt = false; 269 | fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types. 270 | // align::numeric needs to be overwritten here since the '0' flag is 271 | // ignored for non-numeric types 272 | if (fmt_specs.align == align::none || fmt_specs.align == align::numeric) 273 | fmt_specs.align = align::right; 274 | write(this->out, static_cast(value), fmt_specs); 275 | } 276 | 277 | template ::value)> 278 | void operator()(T value) { 279 | base::operator()(value); 280 | } 281 | 282 | /** Formats a null-terminated C string. */ 283 | void operator()(const char* value) { 284 | if (value) 285 | base::operator()(value); 286 | else 287 | write_null_pointer(this->specs.type != presentation_type::pointer); 288 | } 289 | 290 | /** Formats a null-terminated wide C string. */ 291 | void operator()(const wchar_t* value) { 292 | if (value) 293 | base::operator()(value); 294 | else 295 | write_null_pointer(this->specs.type != presentation_type::pointer); 296 | } 297 | 298 | void operator()(basic_string_view value) { base::operator()(value); } 299 | 300 | /** Formats a pointer. */ 301 | void operator()(const void* value) { 302 | if (value) 303 | base::operator()(value); 304 | else 305 | write_null_pointer(); 306 | } 307 | 308 | /** Formats an argument of a custom (user-defined) type. */ 309 | void operator()(typename basic_format_arg::handle handle) { 310 | auto parse_ctx = basic_format_parse_context({}); 311 | handle.format(parse_ctx, context_); 312 | } 313 | }; 314 | 315 | template 316 | void parse_flags(format_specs& specs, const Char*& it, const Char* end) { 317 | for (; it != end; ++it) { 318 | switch (*it) { 319 | case '-': 320 | specs.align = align::left; 321 | break; 322 | case '+': 323 | specs.sign = sign::plus; 324 | break; 325 | case '0': 326 | specs.fill[0] = '0'; 327 | break; 328 | case ' ': 329 | if (specs.sign != sign::plus) specs.sign = sign::space; 330 | break; 331 | case '#': 332 | specs.alt = true; 333 | break; 334 | default: 335 | return; 336 | } 337 | } 338 | } 339 | 340 | template 341 | auto parse_header(const Char*& it, const Char* end, format_specs& specs, 342 | GetArg get_arg) -> int { 343 | int arg_index = -1; 344 | Char c = *it; 345 | if (c >= '0' && c <= '9') { 346 | // Parse an argument index (if followed by '$') or a width possibly 347 | // preceded with '0' flag(s). 348 | int value = parse_nonnegative_int(it, end, -1); 349 | if (it != end && *it == '$') { // value is an argument index 350 | ++it; 351 | arg_index = value != -1 ? value : max_value(); 352 | } else { 353 | if (c == '0') specs.fill[0] = '0'; 354 | if (value != 0) { 355 | // Nonzero value means that we parsed width and don't need to 356 | // parse it or flags again, so return now. 357 | if (value == -1) throw_format_error("number is too big"); 358 | specs.width = value; 359 | return arg_index; 360 | } 361 | } 362 | } 363 | parse_flags(specs, it, end); 364 | // Parse width. 365 | if (it != end) { 366 | if (*it >= '0' && *it <= '9') { 367 | specs.width = parse_nonnegative_int(it, end, -1); 368 | if (specs.width == -1) throw_format_error("number is too big"); 369 | } else if (*it == '*') { 370 | ++it; 371 | specs.width = static_cast(visit_format_arg( 372 | detail::printf_width_handler(specs), get_arg(-1))); 373 | } 374 | } 375 | return arg_index; 376 | } 377 | 378 | inline auto parse_printf_presentation_type(char c, type t) 379 | -> presentation_type { 380 | using pt = presentation_type; 381 | constexpr auto integral_set = sint_set | uint_set | bool_set | char_set; 382 | switch (c) { 383 | case 'd': 384 | return in(t, integral_set) ? pt::dec : pt::none; 385 | case 'o': 386 | return in(t, integral_set) ? pt::oct : pt::none; 387 | case 'x': 388 | return in(t, integral_set) ? pt::hex_lower : pt::none; 389 | case 'X': 390 | return in(t, integral_set) ? pt::hex_upper : pt::none; 391 | case 'a': 392 | return in(t, float_set) ? pt::hexfloat_lower : pt::none; 393 | case 'A': 394 | return in(t, float_set) ? pt::hexfloat_upper : pt::none; 395 | case 'e': 396 | return in(t, float_set) ? pt::exp_lower : pt::none; 397 | case 'E': 398 | return in(t, float_set) ? pt::exp_upper : pt::none; 399 | case 'f': 400 | return in(t, float_set) ? pt::fixed_lower : pt::none; 401 | case 'F': 402 | return in(t, float_set) ? pt::fixed_upper : pt::none; 403 | case 'g': 404 | return in(t, float_set) ? pt::general_lower : pt::none; 405 | case 'G': 406 | return in(t, float_set) ? pt::general_upper : pt::none; 407 | case 'c': 408 | return in(t, integral_set) ? pt::chr : pt::none; 409 | case 's': 410 | return in(t, string_set | cstring_set) ? pt::string : pt::none; 411 | case 'p': 412 | return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none; 413 | default: 414 | return pt::none; 415 | } 416 | } 417 | 418 | template 419 | void vprintf(buffer& buf, basic_string_view format, 420 | basic_format_args args) { 421 | using iterator = buffer_appender; 422 | auto out = iterator(buf); 423 | auto context = basic_printf_context(out, args); 424 | auto parse_ctx = basic_format_parse_context(format); 425 | 426 | // Returns the argument with specified index or, if arg_index is -1, the next 427 | // argument. 428 | auto get_arg = [&](int arg_index) { 429 | if (arg_index < 0) 430 | arg_index = parse_ctx.next_arg_id(); 431 | else 432 | parse_ctx.check_arg_id(--arg_index); 433 | return detail::get_arg(context, arg_index); 434 | }; 435 | 436 | const Char* start = parse_ctx.begin(); 437 | const Char* end = parse_ctx.end(); 438 | auto it = start; 439 | while (it != end) { 440 | if (!find(it, end, '%', it)) { 441 | it = end; // find leaves it == nullptr if it doesn't find '%'. 442 | break; 443 | } 444 | Char c = *it++; 445 | if (it != end && *it == c) { 446 | write(out, basic_string_view(start, to_unsigned(it - start))); 447 | start = ++it; 448 | continue; 449 | } 450 | write(out, basic_string_view(start, to_unsigned(it - 1 - start))); 451 | 452 | auto specs = format_specs(); 453 | specs.align = align::right; 454 | 455 | // Parse argument index, flags and width. 456 | int arg_index = parse_header(it, end, specs, get_arg); 457 | if (arg_index == 0) throw_format_error("argument not found"); 458 | 459 | // Parse precision. 460 | if (it != end && *it == '.') { 461 | ++it; 462 | c = it != end ? *it : 0; 463 | if ('0' <= c && c <= '9') { 464 | specs.precision = parse_nonnegative_int(it, end, 0); 465 | } else if (c == '*') { 466 | ++it; 467 | specs.precision = static_cast( 468 | visit_format_arg(printf_precision_handler(), get_arg(-1))); 469 | } else { 470 | specs.precision = 0; 471 | } 472 | } 473 | 474 | auto arg = get_arg(arg_index); 475 | // For d, i, o, u, x, and X conversion specifiers, if a precision is 476 | // specified, the '0' flag is ignored 477 | if (specs.precision >= 0 && arg.is_integral()) { 478 | // Ignore '0' for non-numeric types or if '-' present. 479 | specs.fill[0] = ' '; 480 | } 481 | if (specs.precision >= 0 && arg.type() == type::cstring_type) { 482 | auto str = visit_format_arg(get_cstring(), arg); 483 | auto str_end = str + specs.precision; 484 | auto nul = std::find(str, str_end, Char()); 485 | auto sv = basic_string_view( 486 | str, to_unsigned(nul != str_end ? nul - str : specs.precision)); 487 | arg = make_arg>(sv); 488 | } 489 | if (specs.alt && visit_format_arg(is_zero_int(), arg)) specs.alt = false; 490 | if (specs.fill[0] == '0') { 491 | if (arg.is_arithmetic() && specs.align != align::left) 492 | specs.align = align::numeric; 493 | else 494 | specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-' 495 | // flag is also present. 496 | } 497 | 498 | // Parse length and convert the argument to the required type. 499 | c = it != end ? *it++ : 0; 500 | Char t = it != end ? *it : 0; 501 | switch (c) { 502 | case 'h': 503 | if (t == 'h') { 504 | ++it; 505 | t = it != end ? *it : 0; 506 | convert_arg(arg, t); 507 | } else { 508 | convert_arg(arg, t); 509 | } 510 | break; 511 | case 'l': 512 | if (t == 'l') { 513 | ++it; 514 | t = it != end ? *it : 0; 515 | convert_arg(arg, t); 516 | } else { 517 | convert_arg(arg, t); 518 | } 519 | break; 520 | case 'j': 521 | convert_arg(arg, t); 522 | break; 523 | case 'z': 524 | convert_arg(arg, t); 525 | break; 526 | case 't': 527 | convert_arg(arg, t); 528 | break; 529 | case 'L': 530 | // printf produces garbage when 'L' is omitted for long double, no 531 | // need to do the same. 532 | break; 533 | default: 534 | --it; 535 | convert_arg(arg, c); 536 | } 537 | 538 | // Parse type. 539 | if (it == end) throw_format_error("invalid format string"); 540 | char type = static_cast(*it++); 541 | if (arg.is_integral()) { 542 | // Normalize type. 543 | switch (type) { 544 | case 'i': 545 | case 'u': 546 | type = 'd'; 547 | break; 548 | case 'c': 549 | visit_format_arg(char_converter>(arg), arg); 550 | break; 551 | } 552 | } 553 | specs.type = parse_printf_presentation_type(type, arg.type()); 554 | if (specs.type == presentation_type::none) 555 | throw_format_error("invalid format specifier"); 556 | 557 | start = it; 558 | 559 | // Format argument. 560 | visit_format_arg(printf_arg_formatter(out, specs, context), arg); 561 | } 562 | write(out, basic_string_view(start, to_unsigned(it - start))); 563 | } 564 | } // namespace detail 565 | 566 | using printf_context = basic_printf_context; 567 | using wprintf_context = basic_printf_context; 568 | 569 | using printf_args = basic_format_args; 570 | using wprintf_args = basic_format_args; 571 | 572 | /** 573 | \rst 574 | Constructs an `~fmt::format_arg_store` object that contains references to 575 | arguments and can be implicitly converted to `~fmt::printf_args`. 576 | \endrst 577 | */ 578 | template 579 | inline auto make_printf_args(const T&... args) 580 | -> format_arg_store { 581 | return {args...}; 582 | } 583 | 584 | // DEPRECATED! 585 | template 586 | inline auto make_wprintf_args(const T&... args) 587 | -> format_arg_store { 588 | return {args...}; 589 | } 590 | 591 | template 592 | inline auto vsprintf( 593 | basic_string_view fmt, 594 | basic_format_args>> args) 595 | -> std::basic_string { 596 | auto buf = basic_memory_buffer(); 597 | detail::vprintf(buf, fmt, args); 598 | return to_string(buf); 599 | } 600 | 601 | /** 602 | \rst 603 | Formats arguments and returns the result as a string. 604 | 605 | **Example**:: 606 | 607 | std::string message = fmt::sprintf("The answer is %d", 42); 608 | \endrst 609 | */ 610 | template ::value, char_t>> 612 | inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string { 613 | return vsprintf(detail::to_string_view(fmt), 614 | fmt::make_format_args>(args...)); 615 | } 616 | 617 | template 618 | inline auto vfprintf( 619 | std::FILE* f, basic_string_view fmt, 620 | basic_format_args>> args) 621 | -> int { 622 | auto buf = basic_memory_buffer(); 623 | detail::vprintf(buf, fmt, args); 624 | size_t size = buf.size(); 625 | return std::fwrite(buf.data(), sizeof(Char), size, f) < size 626 | ? -1 627 | : static_cast(size); 628 | } 629 | 630 | /** 631 | \rst 632 | Prints formatted data to the file *f*. 633 | 634 | **Example**:: 635 | 636 | fmt::fprintf(stderr, "Don't %s!", "panic"); 637 | \endrst 638 | */ 639 | template > 640 | inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int { 641 | return vfprintf(f, detail::to_string_view(fmt), 642 | fmt::make_format_args>(args...)); 643 | } 644 | 645 | template 646 | FMT_DEPRECATED inline auto vprintf( 647 | basic_string_view fmt, 648 | basic_format_args>> args) 649 | -> int { 650 | return vfprintf(stdout, fmt, args); 651 | } 652 | 653 | /** 654 | \rst 655 | Prints formatted data to ``stdout``. 656 | 657 | **Example**:: 658 | 659 | fmt::printf("Elapsed time: %.2f seconds", 1.23); 660 | \endrst 661 | */ 662 | template 663 | inline auto printf(string_view fmt, const T&... args) -> int { 664 | return vfprintf(stdout, fmt, make_printf_args(args...)); 665 | } 666 | template 667 | FMT_DEPRECATED inline auto printf(basic_string_view fmt, 668 | const T&... args) -> int { 669 | return vfprintf(stdout, fmt, make_wprintf_args(args...)); 670 | } 671 | 672 | FMT_END_EXPORT 673 | FMT_END_NAMESPACE 674 | 675 | #endif // FMT_PRINTF_H_ 676 | -------------------------------------------------------------------------------- /src/fmt/ranges.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - range and tuple support 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_RANGES_H_ 9 | #define FMT_RANGES_H_ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include "format.h" 16 | 17 | FMT_BEGIN_NAMESPACE 18 | 19 | namespace detail { 20 | 21 | template 22 | auto copy(const Range& range, OutputIt out) -> OutputIt { 23 | for (auto it = range.begin(), end = range.end(); it != end; ++it) 24 | *out++ = *it; 25 | return out; 26 | } 27 | 28 | template 29 | auto copy(const char* str, OutputIt out) -> OutputIt { 30 | while (*str) *out++ = *str++; 31 | return out; 32 | } 33 | 34 | template auto copy(char ch, OutputIt out) -> OutputIt { 35 | *out++ = ch; 36 | return out; 37 | } 38 | 39 | template auto copy(wchar_t ch, OutputIt out) -> OutputIt { 40 | *out++ = ch; 41 | return out; 42 | } 43 | 44 | // Returns true if T has a std::string-like interface, like std::string_view. 45 | template class is_std_string_like { 46 | template 47 | static auto check(U* p) 48 | -> decltype((void)p->find('a'), p->length(), (void)p->data(), int()); 49 | template static void check(...); 50 | 51 | public: 52 | static constexpr const bool value = 53 | is_string::value || 54 | std::is_convertible>::value || 55 | !std::is_void(nullptr))>::value; 56 | }; 57 | 58 | template 59 | struct is_std_string_like> : std::true_type {}; 60 | 61 | template class is_map { 62 | template static auto check(U*) -> typename U::mapped_type; 63 | template static void check(...); 64 | 65 | public: 66 | #ifdef FMT_FORMAT_MAP_AS_LIST // DEPRECATED! 67 | static constexpr const bool value = false; 68 | #else 69 | static constexpr const bool value = 70 | !std::is_void(nullptr))>::value; 71 | #endif 72 | }; 73 | 74 | template class is_set { 75 | template static auto check(U*) -> typename U::key_type; 76 | template static void check(...); 77 | 78 | public: 79 | #ifdef FMT_FORMAT_SET_AS_LIST // DEPRECATED! 80 | static constexpr const bool value = false; 81 | #else 82 | static constexpr const bool value = 83 | !std::is_void(nullptr))>::value && !is_map::value; 84 | #endif 85 | }; 86 | 87 | template struct conditional_helper {}; 88 | 89 | template struct is_range_ : std::false_type {}; 90 | 91 | #if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800 92 | 93 | # define FMT_DECLTYPE_RETURN(val) \ 94 | ->decltype(val) { return val; } \ 95 | static_assert( \ 96 | true, "") // This makes it so that a semicolon is required after the 97 | // macro, which helps clang-format handle the formatting. 98 | 99 | // C array overload 100 | template 101 | auto range_begin(const T (&arr)[N]) -> const T* { 102 | return arr; 103 | } 104 | template 105 | auto range_end(const T (&arr)[N]) -> const T* { 106 | return arr + N; 107 | } 108 | 109 | template 110 | struct has_member_fn_begin_end_t : std::false_type {}; 111 | 112 | template 113 | struct has_member_fn_begin_end_t().begin()), 114 | decltype(std::declval().end())>> 115 | : std::true_type {}; 116 | 117 | // Member function overload 118 | template 119 | auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast(rng).begin()); 120 | template 121 | auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast(rng).end()); 122 | 123 | // ADL overload. Only participates in overload resolution if member functions 124 | // are not found. 125 | template 126 | auto range_begin(T&& rng) 127 | -> enable_if_t::value, 128 | decltype(begin(static_cast(rng)))> { 129 | return begin(static_cast(rng)); 130 | } 131 | template 132 | auto range_end(T&& rng) -> enable_if_t::value, 133 | decltype(end(static_cast(rng)))> { 134 | return end(static_cast(rng)); 135 | } 136 | 137 | template 138 | struct has_const_begin_end : std::false_type {}; 139 | template 140 | struct has_mutable_begin_end : std::false_type {}; 141 | 142 | template 143 | struct has_const_begin_end< 144 | T, 145 | void_t< 146 | decltype(detail::range_begin(std::declval&>())), 147 | decltype(detail::range_end(std::declval&>()))>> 148 | : std::true_type {}; 149 | 150 | template 151 | struct has_mutable_begin_end< 152 | T, void_t())), 153 | decltype(detail::range_end(std::declval())), 154 | // the extra int here is because older versions of MSVC don't 155 | // SFINAE properly unless there are distinct types 156 | int>> : std::true_type {}; 157 | 158 | template 159 | struct is_range_ 160 | : std::integral_constant::value || 161 | has_mutable_begin_end::value)> {}; 162 | # undef FMT_DECLTYPE_RETURN 163 | #endif 164 | 165 | // tuple_size and tuple_element check. 166 | template class is_tuple_like_ { 167 | template 168 | static auto check(U* p) -> decltype(std::tuple_size::value, int()); 169 | template static void check(...); 170 | 171 | public: 172 | static constexpr const bool value = 173 | !std::is_void(nullptr))>::value; 174 | }; 175 | 176 | // Check for integer_sequence 177 | #if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900 178 | template 179 | using integer_sequence = std::integer_sequence; 180 | template using index_sequence = std::index_sequence; 181 | template using make_index_sequence = std::make_index_sequence; 182 | #else 183 | template struct integer_sequence { 184 | using value_type = T; 185 | 186 | static FMT_CONSTEXPR size_t size() { return sizeof...(N); } 187 | }; 188 | 189 | template using index_sequence = integer_sequence; 190 | 191 | template 192 | struct make_integer_sequence : make_integer_sequence {}; 193 | template 194 | struct make_integer_sequence : integer_sequence {}; 195 | 196 | template 197 | using make_index_sequence = make_integer_sequence; 198 | #endif 199 | 200 | template 201 | using tuple_index_sequence = make_index_sequence::value>; 202 | 203 | template ::value> 204 | class is_tuple_formattable_ { 205 | public: 206 | static constexpr const bool value = false; 207 | }; 208 | template class is_tuple_formattable_ { 209 | template 210 | static std::true_type check2(index_sequence, 211 | integer_sequence); 212 | static std::false_type check2(...); 213 | template 214 | static decltype(check2( 215 | index_sequence{}, 216 | integer_sequence< 217 | bool, (is_formattable::type, 218 | C>::value)...>{})) check(index_sequence); 219 | 220 | public: 221 | static constexpr const bool value = 222 | decltype(check(tuple_index_sequence{}))::value; 223 | }; 224 | 225 | template 226 | FMT_CONSTEXPR void for_each(index_sequence, Tuple&& t, F&& f) { 227 | using std::get; 228 | // Using a free function get(Tuple) now. 229 | const int unused[] = {0, ((void)f(get(t)), 0)...}; 230 | ignore_unused(unused); 231 | } 232 | 233 | template 234 | FMT_CONSTEXPR void for_each(Tuple&& t, F&& f) { 235 | for_each(tuple_index_sequence>(), 236 | std::forward(t), std::forward(f)); 237 | } 238 | 239 | template 240 | void for_each2(index_sequence, Tuple1&& t1, Tuple2&& t2, F&& f) { 241 | using std::get; 242 | const int unused[] = {0, ((void)f(get(t1), get(t2)), 0)...}; 243 | ignore_unused(unused); 244 | } 245 | 246 | template 247 | void for_each2(Tuple1&& t1, Tuple2&& t2, F&& f) { 248 | for_each2(tuple_index_sequence>(), 249 | std::forward(t1), std::forward(t2), 250 | std::forward(f)); 251 | } 252 | 253 | namespace tuple { 254 | // Workaround a bug in MSVC 2019 (v140). 255 | template 256 | using result_t = std::tuple, Char>...>; 257 | 258 | using std::get; 259 | template 260 | auto get_formatters(index_sequence) 261 | -> result_t(std::declval()))...>; 262 | } // namespace tuple 263 | 264 | #if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920 265 | // Older MSVC doesn't get the reference type correctly for arrays. 266 | template struct range_reference_type_impl { 267 | using type = decltype(*detail::range_begin(std::declval())); 268 | }; 269 | 270 | template struct range_reference_type_impl { 271 | using type = T&; 272 | }; 273 | 274 | template 275 | using range_reference_type = typename range_reference_type_impl::type; 276 | #else 277 | template 278 | using range_reference_type = 279 | decltype(*detail::range_begin(std::declval())); 280 | #endif 281 | 282 | // We don't use the Range's value_type for anything, but we do need the Range's 283 | // reference type, with cv-ref stripped. 284 | template 285 | using uncvref_type = remove_cvref_t>; 286 | 287 | template 288 | FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set) 289 | -> decltype(f.set_debug_format(set)) { 290 | f.set_debug_format(set); 291 | } 292 | template 293 | FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {} 294 | 295 | // These are not generic lambdas for compatibility with C++11. 296 | template struct parse_empty_specs { 297 | template FMT_CONSTEXPR void operator()(Formatter& f) { 298 | f.parse(ctx); 299 | detail::maybe_set_debug_format(f, true); 300 | } 301 | ParseContext& ctx; 302 | }; 303 | template struct format_tuple_element { 304 | using char_type = typename FormatContext::char_type; 305 | 306 | template 307 | void operator()(const formatter& f, const T& v) { 308 | if (i > 0) 309 | ctx.advance_to(detail::copy_str(separator, ctx.out())); 310 | ctx.advance_to(f.format(v, ctx)); 311 | ++i; 312 | } 313 | 314 | int i; 315 | FormatContext& ctx; 316 | basic_string_view separator; 317 | }; 318 | 319 | } // namespace detail 320 | 321 | template struct is_tuple_like { 322 | static constexpr const bool value = 323 | detail::is_tuple_like_::value && !detail::is_range_::value; 324 | }; 325 | 326 | template struct is_tuple_formattable { 327 | static constexpr const bool value = 328 | detail::is_tuple_formattable_::value; 329 | }; 330 | 331 | template 332 | struct formatter::value && 334 | fmt::is_tuple_formattable::value>> { 335 | private: 336 | decltype(detail::tuple::get_formatters( 337 | detail::tuple_index_sequence())) formatters_; 338 | 339 | basic_string_view separator_ = detail::string_literal{}; 340 | basic_string_view opening_bracket_ = 341 | detail::string_literal{}; 342 | basic_string_view closing_bracket_ = 343 | detail::string_literal{}; 344 | 345 | public: 346 | FMT_CONSTEXPR formatter() {} 347 | 348 | FMT_CONSTEXPR void set_separator(basic_string_view sep) { 349 | separator_ = sep; 350 | } 351 | 352 | FMT_CONSTEXPR void set_brackets(basic_string_view open, 353 | basic_string_view close) { 354 | opening_bracket_ = open; 355 | closing_bracket_ = close; 356 | } 357 | 358 | template 359 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 360 | auto it = ctx.begin(); 361 | if (it != ctx.end() && *it != '}') 362 | FMT_THROW(format_error("invalid format specifier")); 363 | detail::for_each(formatters_, detail::parse_empty_specs{ctx}); 364 | return it; 365 | } 366 | 367 | template 368 | auto format(const Tuple& value, FormatContext& ctx) const 369 | -> decltype(ctx.out()) { 370 | ctx.advance_to(detail::copy_str(opening_bracket_, ctx.out())); 371 | detail::for_each2( 372 | formatters_, value, 373 | detail::format_tuple_element{0, ctx, separator_}); 374 | return detail::copy_str(closing_bracket_, ctx.out()); 375 | } 376 | }; 377 | 378 | template struct is_range { 379 | static constexpr const bool value = 380 | detail::is_range_::value && !detail::is_std_string_like::value && 381 | !std::is_convertible>::value && 382 | !std::is_convertible>::value; 383 | }; 384 | 385 | namespace detail { 386 | template struct range_mapper { 387 | using mapper = arg_mapper; 388 | 389 | template , Context>::value)> 391 | static auto map(T&& value) -> T&& { 392 | return static_cast(value); 393 | } 394 | template , Context>::value)> 396 | static auto map(T&& value) 397 | -> decltype(mapper().map(static_cast(value))) { 398 | return mapper().map(static_cast(value)); 399 | } 400 | }; 401 | 402 | template 403 | using range_formatter_type = 404 | formatter>{}.map( 405 | std::declval()))>, 406 | Char>; 407 | 408 | template 409 | using maybe_const_range = 410 | conditional_t::value, const R, R>; 411 | 412 | // Workaround a bug in MSVC 2015 and earlier. 413 | #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 414 | template 415 | struct is_formattable_delayed 416 | : is_formattable>, Char> {}; 417 | #endif 418 | } // namespace detail 419 | 420 | template 421 | struct range_formatter; 422 | 423 | template 424 | struct range_formatter< 425 | T, Char, 426 | enable_if_t>, 427 | is_formattable>::value>> { 428 | private: 429 | detail::range_formatter_type underlying_; 430 | basic_string_view separator_ = detail::string_literal{}; 431 | basic_string_view opening_bracket_ = 432 | detail::string_literal{}; 433 | basic_string_view closing_bracket_ = 434 | detail::string_literal{}; 435 | 436 | public: 437 | FMT_CONSTEXPR range_formatter() {} 438 | 439 | FMT_CONSTEXPR auto underlying() -> detail::range_formatter_type& { 440 | return underlying_; 441 | } 442 | 443 | FMT_CONSTEXPR void set_separator(basic_string_view sep) { 444 | separator_ = sep; 445 | } 446 | 447 | FMT_CONSTEXPR void set_brackets(basic_string_view open, 448 | basic_string_view close) { 449 | opening_bracket_ = open; 450 | closing_bracket_ = close; 451 | } 452 | 453 | template 454 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 455 | auto it = ctx.begin(); 456 | auto end = ctx.end(); 457 | 458 | if (it != end && *it == 'n') { 459 | set_brackets({}, {}); 460 | ++it; 461 | } 462 | 463 | if (it != end && *it != '}') { 464 | if (*it != ':') FMT_THROW(format_error("invalid format specifier")); 465 | ++it; 466 | } else { 467 | detail::maybe_set_debug_format(underlying_, true); 468 | } 469 | 470 | ctx.advance_to(it); 471 | return underlying_.parse(ctx); 472 | } 473 | 474 | template 475 | auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) { 476 | detail::range_mapper> mapper; 477 | auto out = ctx.out(); 478 | out = detail::copy_str(opening_bracket_, out); 479 | int i = 0; 480 | auto it = detail::range_begin(range); 481 | auto end = detail::range_end(range); 482 | for (; it != end; ++it) { 483 | if (i > 0) out = detail::copy_str(separator_, out); 484 | ctx.advance_to(out); 485 | out = underlying_.format(mapper.map(*it), ctx); 486 | ++i; 487 | } 488 | out = detail::copy_str(closing_bracket_, out); 489 | return out; 490 | } 491 | }; 492 | 493 | enum class range_format { disabled, map, set, sequence, string, debug_string }; 494 | 495 | namespace detail { 496 | template 497 | struct range_format_kind_ 498 | : std::integral_constant, T>::value 500 | ? range_format::disabled 501 | : is_map::value ? range_format::map 502 | : is_set::value ? range_format::set 503 | : range_format::sequence> {}; 504 | 505 | template 506 | struct range_default_formatter; 507 | 508 | template 509 | using range_format_constant = std::integral_constant; 510 | 511 | template 512 | struct range_default_formatter< 513 | K, R, Char, 514 | enable_if_t<(K == range_format::sequence || K == range_format::map || 515 | K == range_format::set)>> { 516 | using range_type = detail::maybe_const_range; 517 | range_formatter, Char> underlying_; 518 | 519 | FMT_CONSTEXPR range_default_formatter() { init(range_format_constant()); } 520 | 521 | FMT_CONSTEXPR void init(range_format_constant) { 522 | underlying_.set_brackets(detail::string_literal{}, 523 | detail::string_literal{}); 524 | } 525 | 526 | FMT_CONSTEXPR void init(range_format_constant) { 527 | underlying_.set_brackets(detail::string_literal{}, 528 | detail::string_literal{}); 529 | underlying_.underlying().set_brackets({}, {}); 530 | underlying_.underlying().set_separator( 531 | detail::string_literal{}); 532 | } 533 | 534 | FMT_CONSTEXPR void init(range_format_constant) {} 535 | 536 | template 537 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 538 | return underlying_.parse(ctx); 539 | } 540 | 541 | template 542 | auto format(range_type& range, FormatContext& ctx) const 543 | -> decltype(ctx.out()) { 544 | return underlying_.format(range, ctx); 545 | } 546 | }; 547 | } // namespace detail 548 | 549 | template 550 | struct range_format_kind 551 | : conditional_t< 552 | is_range::value, detail::range_format_kind_, 553 | std::integral_constant> {}; 554 | 555 | template 556 | struct formatter< 557 | R, Char, 558 | enable_if_t::value != 559 | range_format::disabled> 560 | // Workaround a bug in MSVC 2015 and earlier. 561 | #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 562 | , 563 | detail::is_formattable_delayed 564 | #endif 565 | >::value>> 566 | : detail::range_default_formatter::value, R, 567 | Char> { 568 | }; 569 | 570 | template struct tuple_join_view : detail::view { 571 | const std::tuple& tuple; 572 | basic_string_view sep; 573 | 574 | tuple_join_view(const std::tuple& t, basic_string_view s) 575 | : tuple(t), sep{s} {} 576 | }; 577 | 578 | // Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers 579 | // support in tuple_join. It is disabled by default because of issues with 580 | // the dynamic width and precision. 581 | #ifndef FMT_TUPLE_JOIN_SPECIFIERS 582 | # define FMT_TUPLE_JOIN_SPECIFIERS 0 583 | #endif 584 | 585 | template 586 | struct formatter, Char> { 587 | template 588 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 589 | return do_parse(ctx, std::integral_constant()); 590 | } 591 | 592 | template 593 | auto format(const tuple_join_view& value, 594 | FormatContext& ctx) const -> typename FormatContext::iterator { 595 | return do_format(value, ctx, 596 | std::integral_constant()); 597 | } 598 | 599 | private: 600 | std::tuple::type, Char>...> formatters_; 601 | 602 | template 603 | FMT_CONSTEXPR auto do_parse(ParseContext& ctx, 604 | std::integral_constant) 605 | -> decltype(ctx.begin()) { 606 | return ctx.begin(); 607 | } 608 | 609 | template 610 | FMT_CONSTEXPR auto do_parse(ParseContext& ctx, 611 | std::integral_constant) 612 | -> decltype(ctx.begin()) { 613 | auto end = ctx.begin(); 614 | #if FMT_TUPLE_JOIN_SPECIFIERS 615 | end = std::get(formatters_).parse(ctx); 616 | if (N > 1) { 617 | auto end1 = do_parse(ctx, std::integral_constant()); 618 | if (end != end1) 619 | FMT_THROW(format_error("incompatible format specs for tuple elements")); 620 | } 621 | #endif 622 | return end; 623 | } 624 | 625 | template 626 | auto do_format(const tuple_join_view&, FormatContext& ctx, 627 | std::integral_constant) const -> 628 | typename FormatContext::iterator { 629 | return ctx.out(); 630 | } 631 | 632 | template 633 | auto do_format(const tuple_join_view& value, FormatContext& ctx, 634 | std::integral_constant) const -> 635 | typename FormatContext::iterator { 636 | auto out = std::get(formatters_) 637 | .format(std::get(value.tuple), ctx); 638 | if (N > 1) { 639 | out = std::copy(value.sep.begin(), value.sep.end(), out); 640 | ctx.advance_to(out); 641 | return do_format(value, ctx, std::integral_constant()); 642 | } 643 | return out; 644 | } 645 | }; 646 | 647 | namespace detail { 648 | // Check if T has an interface like a container adaptor (e.g. std::stack, 649 | // std::queue, std::priority_queue). 650 | template class is_container_adaptor_like { 651 | template static auto check(U* p) -> typename U::container_type; 652 | template static void check(...); 653 | 654 | public: 655 | static constexpr const bool value = 656 | !std::is_void(nullptr))>::value; 657 | }; 658 | 659 | template struct all { 660 | const Container& c; 661 | auto begin() const -> typename Container::const_iterator { return c.begin(); } 662 | auto end() const -> typename Container::const_iterator { return c.end(); } 663 | }; 664 | } // namespace detail 665 | 666 | template 667 | struct formatter< 668 | T, Char, 669 | enable_if_t, 670 | bool_constant::value == 671 | range_format::disabled>>::value>> 672 | : formatter, Char> { 673 | using all = detail::all; 674 | template 675 | auto format(const T& t, FormatContext& ctx) const -> decltype(ctx.out()) { 676 | struct getter : T { 677 | static auto get(const T& t) -> all { 678 | return {t.*(&getter::c)}; // Access c through the derived class. 679 | } 680 | }; 681 | return formatter::format(getter::get(t), ctx); 682 | } 683 | }; 684 | 685 | FMT_BEGIN_EXPORT 686 | 687 | /** 688 | \rst 689 | Returns an object that formats `tuple` with elements separated by `sep`. 690 | 691 | **Example**:: 692 | 693 | std::tuple t = {1, 'a'}; 694 | fmt::print("{}", fmt::join(t, ", ")); 695 | // Output: "1, a" 696 | \endrst 697 | */ 698 | template 699 | FMT_CONSTEXPR auto join(const std::tuple& tuple, string_view sep) 700 | -> tuple_join_view { 701 | return {tuple, sep}; 702 | } 703 | 704 | template 705 | FMT_CONSTEXPR auto join(const std::tuple& tuple, 706 | basic_string_view sep) 707 | -> tuple_join_view { 708 | return {tuple, sep}; 709 | } 710 | 711 | /** 712 | \rst 713 | Returns an object that formats `initializer_list` with elements separated by 714 | `sep`. 715 | 716 | **Example**:: 717 | 718 | fmt::print("{}", fmt::join({1, 2, 3}, ", ")); 719 | // Output: "1, 2, 3" 720 | \endrst 721 | */ 722 | template 723 | auto join(std::initializer_list list, string_view sep) 724 | -> join_view { 725 | return join(std::begin(list), std::end(list), sep); 726 | } 727 | 728 | FMT_END_EXPORT 729 | FMT_END_NAMESPACE 730 | 731 | #endif // FMT_RANGES_H_ 732 | -------------------------------------------------------------------------------- /src/fmt/std.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - formatters for standard library types 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_STD_H_ 9 | #define FMT_STD_H_ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "format.h" 23 | #include "ostream.h" 24 | 25 | #if FMT_HAS_INCLUDE() 26 | # include 27 | #endif 28 | // Checking FMT_CPLUSPLUS for warning suppression in MSVC. 29 | #if FMT_CPLUSPLUS >= 201703L 30 | # if FMT_HAS_INCLUDE() 31 | # include 32 | # endif 33 | # if FMT_HAS_INCLUDE() 34 | # include 35 | # endif 36 | # if FMT_HAS_INCLUDE() 37 | # include 38 | # endif 39 | #endif 40 | 41 | // GCC 4 does not support FMT_HAS_INCLUDE. 42 | #if FMT_HAS_INCLUDE() || defined(__GLIBCXX__) 43 | # include 44 | // Android NDK with gabi++ library on some architectures does not implement 45 | // abi::__cxa_demangle(). 46 | # ifndef __GABIXX_CXXABI_H__ 47 | # define FMT_HAS_ABI_CXA_DEMANGLE 48 | # endif 49 | #endif 50 | 51 | // Check if typeid is available. 52 | #ifndef FMT_USE_TYPEID 53 | // __RTTI is for EDG compilers. In MSVC typeid is available without RTTI. 54 | # if defined(__GXX_RTTI) || FMT_HAS_FEATURE(cxx_rtti) || FMT_MSC_VERSION || \ 55 | defined(__INTEL_RTTI__) || defined(__RTTI) 56 | # define FMT_USE_TYPEID 1 57 | # else 58 | # define FMT_USE_TYPEID 0 59 | # endif 60 | #endif 61 | 62 | // For older Xcode versions, __cpp_lib_xxx flags are inaccurately defined. 63 | #ifndef FMT_CPP_LIB_FILESYSTEM 64 | # ifdef __cpp_lib_filesystem 65 | # define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem 66 | # else 67 | # define FMT_CPP_LIB_FILESYSTEM 0 68 | # endif 69 | #endif 70 | 71 | #ifndef FMT_CPP_LIB_VARIANT 72 | # ifdef __cpp_lib_variant 73 | # define FMT_CPP_LIB_VARIANT __cpp_lib_variant 74 | # else 75 | # define FMT_CPP_LIB_VARIANT 0 76 | # endif 77 | #endif 78 | 79 | #if FMT_CPP_LIB_FILESYSTEM 80 | FMT_BEGIN_NAMESPACE 81 | 82 | namespace detail { 83 | 84 | template auto get_path_string( 85 | const std::filesystem::path& p, const std::basic_string& native) { 86 | if constexpr (std::is_same_v && std::is_same_v) 87 | return to_utf8(native, to_utf8_error_policy::replace); 88 | else 89 | return p.string(); 90 | } 91 | 92 | template 93 | void write_escaped_path(basic_memory_buffer& quoted, 94 | const std::filesystem::path& p, 95 | const std::basic_string& native) { 96 | if constexpr (std::is_same_v && std::is_same_v) { 97 | auto buf = basic_memory_buffer(); 98 | write_escaped_string(std::back_inserter(buf), native); 99 | bool valid = to_utf8::convert(quoted, {buf.data(), buf.size()}); 100 | FMT_ASSERT(valid, "invalid utf16"); 101 | } else if constexpr (std::is_same_v) { 102 | write_escaped_string( 103 | std::back_inserter(quoted), native); 104 | } else { 105 | write_escaped_string(std::back_inserter(quoted), p.string()); 106 | } 107 | } 108 | 109 | } // namespace detail 110 | 111 | FMT_EXPORT 112 | template struct formatter { 113 | private: 114 | format_specs specs_; 115 | detail::arg_ref width_ref_; 116 | bool debug_ = false; 117 | 118 | public: 119 | FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; } 120 | 121 | template FMT_CONSTEXPR auto parse(ParseContext& ctx) { 122 | auto it = ctx.begin(), end = ctx.end(); 123 | if (it == end) return it; 124 | 125 | it = detail::parse_align(it, end, specs_); 126 | if (it == end) return it; 127 | 128 | it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx); 129 | if (it != end && *it == '?') { 130 | debug_ = true; 131 | ++it; 132 | } 133 | return it; 134 | } 135 | 136 | template 137 | auto format(const std::filesystem::path& p, FormatContext& ctx) const { 138 | auto specs = specs_; 139 | detail::handle_dynamic_spec(specs.width, width_ref_, 140 | ctx); 141 | if (!debug_) { 142 | auto s = detail::get_path_string(p, p.native()); 143 | return detail::write(ctx.out(), basic_string_view(s), specs); 144 | } 145 | auto quoted = basic_memory_buffer(); 146 | detail::write_escaped_path(quoted, p, p.native()); 147 | return detail::write(ctx.out(), 148 | basic_string_view(quoted.data(), quoted.size()), 149 | specs); 150 | } 151 | }; 152 | FMT_END_NAMESPACE 153 | #endif // FMT_CPP_LIB_FILESYSTEM 154 | 155 | FMT_BEGIN_NAMESPACE 156 | FMT_EXPORT 157 | template 158 | struct formatter, Char> : nested_formatter { 159 | private: 160 | // Functor because C++11 doesn't support generic lambdas. 161 | struct writer { 162 | const std::bitset& bs; 163 | 164 | template 165 | FMT_CONSTEXPR OutputIt operator()(OutputIt out) { 166 | for (auto pos = N; pos > 0; --pos) { 167 | out = detail::write(out, bs[pos - 1] ? Char('1') : Char('0')); 168 | } 169 | 170 | return out; 171 | } 172 | }; 173 | 174 | public: 175 | template 176 | auto format(const std::bitset& bs, FormatContext& ctx) const 177 | -> decltype(ctx.out()) { 178 | return write_padded(ctx, writer{bs}); 179 | } 180 | }; 181 | 182 | FMT_EXPORT 183 | template 184 | struct formatter : basic_ostream_formatter {}; 185 | FMT_END_NAMESPACE 186 | 187 | #ifdef __cpp_lib_optional 188 | FMT_BEGIN_NAMESPACE 189 | FMT_EXPORT 190 | template 191 | struct formatter, Char, 192 | std::enable_if_t::value>> { 193 | private: 194 | formatter underlying_; 195 | static constexpr basic_string_view optional = 196 | detail::string_literal{}; 198 | static constexpr basic_string_view none = 199 | detail::string_literal{}; 200 | 201 | template 202 | FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, bool set) 203 | -> decltype(u.set_debug_format(set)) { 204 | u.set_debug_format(set); 205 | } 206 | 207 | template 208 | FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {} 209 | 210 | public: 211 | template FMT_CONSTEXPR auto parse(ParseContext& ctx) { 212 | maybe_set_debug_format(underlying_, true); 213 | return underlying_.parse(ctx); 214 | } 215 | 216 | template 217 | auto format(std::optional const& opt, FormatContext& ctx) const 218 | -> decltype(ctx.out()) { 219 | if (!opt) return detail::write(ctx.out(), none); 220 | 221 | auto out = ctx.out(); 222 | out = detail::write(out, optional); 223 | ctx.advance_to(out); 224 | out = underlying_.format(*opt, ctx); 225 | return detail::write(out, ')'); 226 | } 227 | }; 228 | FMT_END_NAMESPACE 229 | #endif // __cpp_lib_optional 230 | 231 | #if FMT_CPP_LIB_VARIANT 232 | FMT_BEGIN_NAMESPACE 233 | namespace detail { 234 | 235 | template 236 | using variant_index_sequence = 237 | std::make_index_sequence::value>; 238 | 239 | template struct is_variant_like_ : std::false_type {}; 240 | template 241 | struct is_variant_like_> : std::true_type {}; 242 | 243 | // formattable element check. 244 | template class is_variant_formattable_ { 245 | template 246 | static std::conjunction< 247 | is_formattable, C>...> 248 | check(std::index_sequence); 249 | 250 | public: 251 | static constexpr const bool value = 252 | decltype(check(variant_index_sequence{}))::value; 253 | }; 254 | 255 | template 256 | auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt { 257 | if constexpr (is_string::value) 258 | return write_escaped_string(out, detail::to_string_view(v)); 259 | else if constexpr (std::is_same_v) 260 | return write_escaped_char(out, v); 261 | else 262 | return write(out, v); 263 | } 264 | 265 | } // namespace detail 266 | 267 | template struct is_variant_like { 268 | static constexpr const bool value = detail::is_variant_like_::value; 269 | }; 270 | 271 | template struct is_variant_formattable { 272 | static constexpr const bool value = 273 | detail::is_variant_formattable_::value; 274 | }; 275 | 276 | FMT_EXPORT 277 | template struct formatter { 278 | template 279 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 280 | return ctx.begin(); 281 | } 282 | 283 | template 284 | auto format(const std::monostate&, FormatContext& ctx) const 285 | -> decltype(ctx.out()) { 286 | return detail::write(ctx.out(), "monostate"); 287 | } 288 | }; 289 | 290 | FMT_EXPORT 291 | template 292 | struct formatter< 293 | Variant, Char, 294 | std::enable_if_t, is_variant_formattable>>> { 296 | template 297 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 298 | return ctx.begin(); 299 | } 300 | 301 | template 302 | auto format(const Variant& value, FormatContext& ctx) const 303 | -> decltype(ctx.out()) { 304 | auto out = ctx.out(); 305 | 306 | out = detail::write(out, "variant("); 307 | FMT_TRY { 308 | std::visit( 309 | [&](const auto& v) { 310 | out = detail::write_variant_alternative(out, v); 311 | }, 312 | value); 313 | } 314 | FMT_CATCH(const std::bad_variant_access&) { 315 | detail::write(out, "valueless by exception"); 316 | } 317 | *out++ = ')'; 318 | return out; 319 | } 320 | }; 321 | FMT_END_NAMESPACE 322 | #endif // FMT_CPP_LIB_VARIANT 323 | 324 | FMT_BEGIN_NAMESPACE 325 | FMT_EXPORT 326 | template struct formatter { 327 | template 328 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 329 | return ctx.begin(); 330 | } 331 | 332 | template 333 | FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const 334 | -> decltype(ctx.out()) { 335 | auto out = ctx.out(); 336 | out = detail::write_bytes(out, ec.category().name(), format_specs()); 337 | out = detail::write(out, Char(':')); 338 | out = detail::write(out, ec.value()); 339 | return out; 340 | } 341 | }; 342 | 343 | FMT_EXPORT 344 | template 345 | struct formatter< 346 | T, Char, 347 | typename std::enable_if::value>::type> { 348 | private: 349 | bool with_typename_ = false; 350 | 351 | public: 352 | FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) 353 | -> decltype(ctx.begin()) { 354 | auto it = ctx.begin(); 355 | auto end = ctx.end(); 356 | if (it == end || *it == '}') return it; 357 | if (*it == 't') { 358 | ++it; 359 | with_typename_ = FMT_USE_TYPEID != 0; 360 | } 361 | return it; 362 | } 363 | 364 | template 365 | auto format(const std::exception& ex, 366 | basic_format_context& ctx) const -> OutputIt { 367 | format_specs spec; 368 | auto out = ctx.out(); 369 | if (!with_typename_) 370 | return detail::write_bytes(out, string_view(ex.what()), spec); 371 | 372 | #if FMT_USE_TYPEID 373 | const std::type_info& ti = typeid(ex); 374 | # ifdef FMT_HAS_ABI_CXA_DEMANGLE 375 | int status = 0; 376 | std::size_t size = 0; 377 | std::unique_ptr demangled_name_ptr( 378 | abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free); 379 | 380 | string_view demangled_name_view; 381 | if (demangled_name_ptr) { 382 | demangled_name_view = demangled_name_ptr.get(); 383 | 384 | // Normalization of stdlib inline namespace names. 385 | // libc++ inline namespaces. 386 | // std::__1::* -> std::* 387 | // std::__1::__fs::* -> std::* 388 | // libstdc++ inline namespaces. 389 | // std::__cxx11::* -> std::* 390 | // std::filesystem::__cxx11::* -> std::filesystem::* 391 | if (demangled_name_view.starts_with("std::")) { 392 | char* begin = demangled_name_ptr.get(); 393 | char* to = begin + 5; // std:: 394 | for (char *from = to, *end = begin + demangled_name_view.size(); 395 | from < end;) { 396 | // This is safe, because demangled_name is NUL-terminated. 397 | if (from[0] == '_' && from[1] == '_') { 398 | char* next = from + 1; 399 | while (next < end && *next != ':') next++; 400 | if (next[0] == ':' && next[1] == ':') { 401 | from = next + 2; 402 | continue; 403 | } 404 | } 405 | *to++ = *from++; 406 | } 407 | demangled_name_view = {begin, detail::to_unsigned(to - begin)}; 408 | } 409 | } else { 410 | demangled_name_view = string_view(ti.name()); 411 | } 412 | out = detail::write_bytes(out, demangled_name_view, spec); 413 | # elif FMT_MSC_VERSION 414 | string_view demangled_name_view(ti.name()); 415 | if (demangled_name_view.starts_with("class ")) 416 | demangled_name_view.remove_prefix(6); 417 | else if (demangled_name_view.starts_with("struct ")) 418 | demangled_name_view.remove_prefix(7); 419 | out = detail::write_bytes(out, demangled_name_view, spec); 420 | # else 421 | out = detail::write_bytes(out, string_view(ti.name()), spec); 422 | # endif 423 | *out++ = ':'; 424 | *out++ = ' '; 425 | return detail::write_bytes(out, string_view(ex.what()), spec); 426 | #endif 427 | } 428 | }; 429 | 430 | namespace detail { 431 | 432 | template 433 | struct has_flip : std::false_type {}; 434 | 435 | template 436 | struct has_flip().flip())>> 437 | : std::true_type {}; 438 | 439 | template struct is_bit_reference_like { 440 | static constexpr const bool value = 441 | std::is_convertible::value && 442 | std::is_nothrow_assignable::value && has_flip::value; 443 | }; 444 | 445 | #ifdef _LIBCPP_VERSION 446 | 447 | // Workaround for libc++ incompatibility with C++ standard. 448 | // According to the Standard, `bitset::operator[] const` returns bool. 449 | template 450 | struct is_bit_reference_like> { 451 | static constexpr const bool value = true; 452 | }; 453 | 454 | #endif 455 | 456 | } // namespace detail 457 | 458 | // We can't use std::vector::reference and 459 | // std::bitset::reference because the compiler can't deduce Allocator and N 460 | // in partial specialization. 461 | FMT_EXPORT 462 | template 463 | struct formatter::value>> 465 | : formatter { 466 | template 467 | FMT_CONSTEXPR auto format(const BitRef& v, FormatContext& ctx) const 468 | -> decltype(ctx.out()) { 469 | return formatter::format(v, ctx); 470 | } 471 | }; 472 | 473 | FMT_EXPORT 474 | template 475 | struct formatter, Char, 476 | enable_if_t::value>> 477 | : formatter { 478 | template 479 | auto format(const std::atomic& v, FormatContext& ctx) const 480 | -> decltype(ctx.out()) { 481 | return formatter::format(v.load(), ctx); 482 | } 483 | }; 484 | 485 | #ifdef __cpp_lib_atomic_flag_test 486 | FMT_EXPORT 487 | template 488 | struct formatter : formatter { 489 | template 490 | auto format(const std::atomic_flag& v, FormatContext& ctx) const 491 | -> decltype(ctx.out()) { 492 | return formatter::format(v.test(), ctx); 493 | } 494 | }; 495 | #endif // __cpp_lib_atomic_flag_test 496 | 497 | FMT_END_NAMESPACE 498 | #endif // FMT_STD_H_ 499 | -------------------------------------------------------------------------------- /src/fmt/xchar.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - optional wchar_t and exotic character support 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_XCHAR_H_ 9 | #define FMT_XCHAR_H_ 10 | 11 | #include 12 | 13 | #include "format.h" 14 | 15 | #ifndef FMT_STATIC_THOUSANDS_SEPARATOR 16 | # include 17 | #endif 18 | 19 | FMT_BEGIN_NAMESPACE 20 | namespace detail { 21 | 22 | template 23 | using is_exotic_char = bool_constant::value>; 24 | 25 | inline auto write_loc(std::back_insert_iterator> out, 26 | loc_value value, const format_specs& specs, 27 | locale_ref loc) -> bool { 28 | #ifndef FMT_STATIC_THOUSANDS_SEPARATOR 29 | auto& numpunct = 30 | std::use_facet>(loc.get()); 31 | auto separator = std::wstring(); 32 | auto grouping = numpunct.grouping(); 33 | if (!grouping.empty()) separator = std::wstring(1, numpunct.thousands_sep()); 34 | return value.visit(loc_writer{out, specs, separator, grouping, {}}); 35 | #endif 36 | return false; 37 | } 38 | } // namespace detail 39 | 40 | FMT_BEGIN_EXPORT 41 | 42 | using wstring_view = basic_string_view; 43 | using wformat_parse_context = basic_format_parse_context; 44 | using wformat_context = buffer_context; 45 | using wformat_args = basic_format_args; 46 | using wmemory_buffer = basic_memory_buffer; 47 | 48 | #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 49 | // Workaround broken conversion on older gcc. 50 | template using wformat_string = wstring_view; 51 | inline auto runtime(wstring_view s) -> wstring_view { return s; } 52 | #else 53 | template 54 | using wformat_string = basic_format_string...>; 55 | inline auto runtime(wstring_view s) -> runtime_format_string { 56 | return {{s}}; 57 | } 58 | #endif 59 | 60 | template <> struct is_char : std::true_type {}; 61 | template <> struct is_char : std::true_type {}; 62 | template <> struct is_char : std::true_type {}; 63 | template <> struct is_char : std::true_type {}; 64 | 65 | template 66 | constexpr format_arg_store make_wformat_args( 67 | const T&... args) { 68 | return {args...}; 69 | } 70 | 71 | inline namespace literals { 72 | #if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_ARGS 73 | constexpr detail::udl_arg operator"" _a(const wchar_t* s, size_t) { 74 | return {s}; 75 | } 76 | #endif 77 | } // namespace literals 78 | 79 | template 80 | auto join(It begin, Sentinel end, wstring_view sep) 81 | -> join_view { 82 | return {begin, end, sep}; 83 | } 84 | 85 | template 86 | auto join(Range&& range, wstring_view sep) 87 | -> join_view, detail::sentinel_t, 88 | wchar_t> { 89 | return join(std::begin(range), std::end(range), sep); 90 | } 91 | 92 | template 93 | auto join(std::initializer_list list, wstring_view sep) 94 | -> join_view { 95 | return join(std::begin(list), std::end(list), sep); 96 | } 97 | 98 | template ::value)> 99 | auto vformat(basic_string_view format_str, 100 | basic_format_args>> args) 101 | -> std::basic_string { 102 | auto buf = basic_memory_buffer(); 103 | detail::vformat_to(buf, format_str, args); 104 | return to_string(buf); 105 | } 106 | 107 | template 108 | auto format(wformat_string fmt, T&&... args) -> std::wstring { 109 | return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...)); 110 | } 111 | 112 | // Pass char_t as a default template parameter instead of using 113 | // std::basic_string> to reduce the symbol size. 114 | template , 115 | FMT_ENABLE_IF(!std::is_same::value && 116 | !std::is_same::value)> 117 | auto format(const S& format_str, T&&... args) -> std::basic_string { 118 | return vformat(detail::to_string_view(format_str), 119 | fmt::make_format_args>(args...)); 120 | } 121 | 122 | template , 123 | FMT_ENABLE_IF(detail::is_locale::value&& 124 | detail::is_exotic_char::value)> 125 | inline auto vformat( 126 | const Locale& loc, const S& format_str, 127 | basic_format_args>> args) 128 | -> std::basic_string { 129 | return detail::vformat(loc, detail::to_string_view(format_str), args); 130 | } 131 | 132 | template , 133 | FMT_ENABLE_IF(detail::is_locale::value&& 134 | detail::is_exotic_char::value)> 135 | inline auto format(const Locale& loc, const S& format_str, T&&... args) 136 | -> std::basic_string { 137 | return detail::vformat(loc, detail::to_string_view(format_str), 138 | fmt::make_format_args>(args...)); 139 | } 140 | 141 | template , 142 | FMT_ENABLE_IF(detail::is_output_iterator::value&& 143 | detail::is_exotic_char::value)> 144 | auto vformat_to(OutputIt out, const S& format_str, 145 | basic_format_args>> args) 146 | -> OutputIt { 147 | auto&& buf = detail::get_buffer(out); 148 | detail::vformat_to(buf, detail::to_string_view(format_str), args); 149 | return detail::get_iterator(buf, out); 150 | } 151 | 152 | template , 154 | FMT_ENABLE_IF(detail::is_output_iterator::value&& 155 | detail::is_exotic_char::value)> 156 | inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt { 157 | return vformat_to(out, detail::to_string_view(fmt), 158 | fmt::make_format_args>(args...)); 159 | } 160 | 161 | template , 163 | FMT_ENABLE_IF(detail::is_output_iterator::value&& 164 | detail::is_locale::value&& 165 | detail::is_exotic_char::value)> 166 | inline auto vformat_to( 167 | OutputIt out, const Locale& loc, const S& format_str, 168 | basic_format_args>> args) -> OutputIt { 169 | auto&& buf = detail::get_buffer(out); 170 | vformat_to(buf, detail::to_string_view(format_str), args, 171 | detail::locale_ref(loc)); 172 | return detail::get_iterator(buf, out); 173 | } 174 | 175 | template < 176 | typename OutputIt, typename Locale, typename S, typename... T, 177 | typename Char = char_t, 178 | bool enable = detail::is_output_iterator::value&& 179 | detail::is_locale::value&& detail::is_exotic_char::value> 180 | inline auto format_to(OutputIt out, const Locale& loc, const S& format_str, 181 | T&&... args) -> 182 | typename std::enable_if::type { 183 | return vformat_to(out, loc, detail::to_string_view(format_str), 184 | fmt::make_format_args>(args...)); 185 | } 186 | 187 | template ::value&& 189 | detail::is_exotic_char::value)> 190 | inline auto vformat_to_n( 191 | OutputIt out, size_t n, basic_string_view format_str, 192 | basic_format_args>> args) 193 | -> format_to_n_result { 194 | using traits = detail::fixed_buffer_traits; 195 | auto buf = detail::iterator_buffer(out, n); 196 | detail::vformat_to(buf, format_str, args); 197 | return {buf.out(), buf.count()}; 198 | } 199 | 200 | template , 202 | FMT_ENABLE_IF(detail::is_output_iterator::value&& 203 | detail::is_exotic_char::value)> 204 | inline auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args) 205 | -> format_to_n_result { 206 | return vformat_to_n(out, n, detail::to_string_view(fmt), 207 | fmt::make_format_args>(args...)); 208 | } 209 | 210 | template , 211 | FMT_ENABLE_IF(detail::is_exotic_char::value)> 212 | inline auto formatted_size(const S& fmt, T&&... args) -> size_t { 213 | auto buf = detail::counting_buffer(); 214 | detail::vformat_to(buf, detail::to_string_view(fmt), 215 | fmt::make_format_args>(args...)); 216 | return buf.count(); 217 | } 218 | 219 | inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) { 220 | auto buf = wmemory_buffer(); 221 | detail::vformat_to(buf, fmt, args); 222 | buf.push_back(L'\0'); 223 | if (std::fputws(buf.data(), f) == -1) 224 | FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); 225 | } 226 | 227 | inline void vprint(wstring_view fmt, wformat_args args) { 228 | vprint(stdout, fmt, args); 229 | } 230 | 231 | template 232 | void print(std::FILE* f, wformat_string fmt, T&&... args) { 233 | return vprint(f, wstring_view(fmt), fmt::make_wformat_args(args...)); 234 | } 235 | 236 | template void print(wformat_string fmt, T&&... args) { 237 | return vprint(wstring_view(fmt), fmt::make_wformat_args(args...)); 238 | } 239 | 240 | template 241 | void println(std::FILE* f, wformat_string fmt, T&&... args) { 242 | return print(f, L"{}\n", fmt::format(fmt, std::forward(args)...)); 243 | } 244 | 245 | template void println(wformat_string fmt, T&&... args) { 246 | return print(L"{}\n", fmt::format(fmt, std::forward(args)...)); 247 | } 248 | 249 | /** 250 | Converts *value* to ``std::wstring`` using the default format for type *T*. 251 | */ 252 | template inline auto to_wstring(const T& value) -> std::wstring { 253 | return format(FMT_STRING(L"{}"), value); 254 | } 255 | FMT_END_EXPORT 256 | FMT_END_NAMESPACE 257 | 258 | #endif // FMT_XCHAR_H_ 259 | -------------------------------------------------------------------------------- /src/register_types.cpp: -------------------------------------------------------------------------------- 1 | #include "register_types.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "ser_comm.h" 9 | 10 | using namespace godot; 11 | 12 | void initialize_sercomm_module(ModuleInitializationLevel p_level) 13 | { 14 | if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) 15 | { 16 | return; 17 | } 18 | 19 | ClassDB::register_class(); 20 | } 21 | 22 | void uninitialize_sercomm_module(ModuleInitializationLevel p_level) 23 | { 24 | if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) 25 | { 26 | return; 27 | } 28 | } 29 | 30 | extern "C" 31 | { 32 | // Initialization. 33 | GDExtensionBool GDE_EXPORT sercomm_library_init(GDExtensionInterfaceGetProcAddress p_get_proc_address, const GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) 34 | { 35 | godot::GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization); 36 | 37 | init_obj.register_initializer(initialize_sercomm_module); 38 | init_obj.register_terminator(uninitialize_sercomm_module); 39 | init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE); 40 | 41 | return init_obj.init(); 42 | } 43 | } -------------------------------------------------------------------------------- /src/register_types.h: -------------------------------------------------------------------------------- 1 | #ifndef SERCOMM_REGISTER_TYPES_H 2 | #define SERCOMM_REGISTER_TYPES_H 3 | 4 | void initialize_sercomm_module(); 5 | void uninitialize_sercomm_module(); 6 | 7 | #endif // SERCOMM_REGISTER_TYPES_H -------------------------------------------------------------------------------- /src/ser_comm.cpp: -------------------------------------------------------------------------------- 1 | #include "ser_comm.h" 2 | #include 3 | #include 4 | 5 | using namespace godot; 6 | 7 | void SerComm::_bind_methods() 8 | { 9 | ClassDB::bind_method(D_METHOD("refresh_ports"), &SerComm::refresh_ports); 10 | ClassDB::bind_method(D_METHOD("list_serial_ports"), &SerComm::sercomm_list_ports); 11 | ClassDB::bind_method(D_METHOD("open_serial"), &SerComm::sercomm_open); 12 | ClassDB::bind_method(D_METHOD("open_serial_with_port"), &SerComm::sercomm_open_specific_serial_port); 13 | ClassDB::bind_method(D_METHOD("close_serial"), &SerComm::sercomm_close); 14 | 15 | ClassDB::bind_method(D_METHOD("waiting_input_bytes"), &SerComm::sercomm_get_waiting); 16 | ClassDB::bind_method(D_METHOD("read_serial", "num_bytes"), &SerComm::sercomm_read); 17 | ClassDB::bind_method(D_METHOD("drain"), &SerComm::sercomm_drain); 18 | ClassDB::bind_method(D_METHOD("write_serial", "p_message"), &SerComm::sercomm_write); 19 | ADD_SIGNAL(MethodInfo("on_message", PropertyInfo(Variant::STRING, "message"))); 20 | 21 | ClassDB::bind_method(D_METHOD("get_port"), &SerComm::get_port); 22 | ClassDB::bind_method(D_METHOD("set_port", "id"), &SerComm::set_port); 23 | ADD_PROPERTY(PropertyInfo(Variant::INT, "port", PROPERTY_HINT_ENUM, "", PROPERTY_USAGE_STORAGE), "set_port", "get_port"); 24 | 25 | ClassDB::bind_method(D_METHOD("get_open"), &SerComm::get_open); 26 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "is_open", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY), "", "get_open"); 27 | 28 | ClassDB::bind_method(D_METHOD("get_baud_rate"), &SerComm::get_baud_rate); 29 | ClassDB::bind_method(D_METHOD("set_baud_rate", "b"), &SerComm::set_baud_rate); 30 | ADD_PROPERTY(PropertyInfo(Variant::INT, "baud_rate", PROPERTY_HINT_ENUM, VariantHelper::_baud_rate_to_hint_string()), "set_baud_rate", "get_baud_rate"); 31 | } 32 | 33 | void SerComm::set_port(const int id) { 34 | _port_enum = id; 35 | } 36 | 37 | int SerComm::get_port() const { 38 | return _port_enum; 39 | } 40 | 41 | void SerComm::set_baud_rate(const int b) { 42 | baud_rate = b; 43 | } 44 | 45 | int SerComm::get_baud_rate() const { 46 | return baud_rate; 47 | } 48 | 49 | bool SerComm::get_open() const { 50 | return opened; 51 | } 52 | 53 | SerComm::SerComm() 54 | { 55 | baud_rate = BAUD_9600; 56 | std::string port_name = "No port found!"; 57 | refresh_ports(); 58 | } 59 | 60 | SerComm::~SerComm() 61 | { 62 | // Add your cleanup here. 63 | sercomm_close(); 64 | } 65 | 66 | godot::Array SerComm::SerComm::sercomm_list_ports() 67 | { 68 | godot::TypedArray res_names = {}; 69 | for (auto port : _ports) 70 | { 71 | godot::String s(port.c_str()); 72 | res_names.push_back(s); 73 | } 74 | 75 | return res_names; 76 | } 77 | 78 | void godot::SerComm::sercomm_flush() 79 | { 80 | } 81 | 82 | int SerComm::sercomm_get_waiting() { 83 | return sp_input_waiting(port); 84 | } 85 | 86 | void SerComm::_process(double delta) 87 | { 88 | if (opened) { 89 | int bytes = sercomm_get_waiting(); 90 | if (bytes > 0) { 91 | sercomm_read(bytes); 92 | } 93 | } 94 | } 95 | 96 | void SerComm::sercomm_close() 97 | { 98 | if (opened) { 99 | sp_close(port); 100 | sp_free_port(port); 101 | } 102 | opened = false; 103 | } 104 | 105 | bool SerComm::sercomm_open() 106 | { 107 | if (opened) { 108 | return true; 109 | } 110 | 111 | sp_return result = sp_get_port_by_name(_ports[_port_enum].c_str(), &port); 112 | if (result != SP_OK) 113 | { 114 | std::cerr << "Error getting port" << std::endl; 115 | return false; 116 | } 117 | 118 | result = sp_open(port, SP_MODE_READ_WRITE); 119 | if (result != SP_OK) 120 | { 121 | std::cerr << "Error opening port" << std::endl; 122 | sp_free_port(port); 123 | return false; 124 | } 125 | 126 | opened = true; 127 | 128 | std::cout << "Success opening port!" << std::endl; 129 | sp_set_baudrate(port, baud_rate); 130 | sp_set_bits(port, 8); 131 | sp_set_stopbits(port, 1); 132 | sp_set_parity(port, SP_PARITY_NONE); 133 | return true; 134 | } 135 | 136 | bool SerComm::sercomm_open_specific_serial_port(const String& port_name) 137 | { 138 | if (opened) { 139 | return true; 140 | } 141 | 142 | std::string port_name_std = port_name.utf8().get_data(); 143 | 144 | sp_return result = sp_get_port_by_name(port_name_std.c_str(), &port); 145 | if (result != SP_OK) { 146 | std::cerr << "Error getting port" << std::endl; 147 | return false; 148 | } 149 | 150 | result = sp_open(port, SP_MODE_READ_WRITE); 151 | if (result != SP_OK) { 152 | std::cerr << "Error opening port" << std::endl; 153 | sp_free_port(port); 154 | return false; 155 | } 156 | 157 | opened = true; 158 | 159 | std::cout << "Success opening port: " << port_name_std << std::endl; 160 | sp_set_baudrate(port, baud_rate); 161 | sp_set_bits(port, 8); 162 | sp_set_stopbits(port, 1); 163 | sp_set_parity(port, SP_PARITY_NONE); 164 | return true; 165 | } 166 | 167 | String SerComm::sercomm_read(const int num_bytes) 168 | { 169 | std::vector read_buffer; 170 | if (num_bytes > read_buffer.max_size()) { 171 | std::cerr << "Attempted to read " << num_bytes << " bytes, which is more than the maximum length supported (" << read_buffer.max_size() << " bytes)" << std::endl; 172 | return ""; 173 | } 174 | read_buffer = std::vector(num_bytes); 175 | 176 | sp_return result = sp_nonblocking_read(port, read_buffer.data(), num_bytes); 177 | 178 | if (result < 0) 179 | { 180 | std::cerr << "Error reading data from port" << std::endl; 181 | return ""; 182 | } 183 | 184 | read_buffer[result] = '\0'; 185 | String data = String::utf8(read_buffer.data()); 186 | 187 | if (data.length() > 0) 188 | { 189 | emit_signal("on_message", data); 190 | } 191 | 192 | return data; 193 | } 194 | 195 | void SerComm::sercomm_drain() { 196 | int result = sp_drain(port); 197 | if (result < 0) { 198 | std::cerr << "Error draining port: " << sp_last_error_code() << " - " << sp_last_error_message() << std::endl; 199 | } 200 | } 201 | 202 | void SerComm::sercomm_write(const String &p_message) 203 | { 204 | const char *utf8_data = p_message.utf8().get_data(); 205 | 206 | sp_return result = sp_nonblocking_write(port, utf8_data, std::strlen(utf8_data)); 207 | if (result < 0) 208 | { 209 | std::cerr << "Error writing data to port" << std::endl; 210 | } 211 | 212 | // std::cout << "Writing output: " << utf8_data << std::endl; 213 | }; 214 | 215 | void SerComm::refresh_ports() 216 | { 217 | _ports.clear(); 218 | sp_port **ports; 219 | sp_return error = sp_list_ports(&ports); 220 | 221 | if (error != SP_OK) 222 | { 223 | _err_print_error(__FUNCTION__, __FILE__, __LINE__, "Error listening ports"); 224 | sp_free_port_list(ports); 225 | return; 226 | } 227 | 228 | for (sp_port **ptr = ports; *ptr; ++ptr) 229 | { 230 | const char *l_port_name = sp_get_port_name(*ptr); 231 | _ports.push_back(l_port_name); 232 | } 233 | 234 | if (Engine::get_singleton()->is_editor_hint()) { 235 | port_enum_str = VariantHelper::_ports_to_hint_string(_ports); 236 | } 237 | 238 | sp_free_port_list(ports); 239 | } 240 | 241 | void SerComm::_get_property_list(List *r_list) const 242 | { 243 | r_list->push_back(PropertyInfo(Variant::INT, "port", PROPERTY_HINT_ENUM, port_enum_str, PROPERTY_USAGE_EDITOR)); 244 | r_list->push_back(PropertyInfo(Variant::BOOL, "toggle_to_refresh", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); 245 | } 246 | 247 | bool SerComm::_get(const StringName &p_name, Variant &r_value) const 248 | { 249 | if (p_name == StringName("port")) 250 | { 251 | r_value = get_port(); 252 | return true; 253 | } else if (p_name == StringName("toggle_to_refresh")) { 254 | r_value = false; 255 | return true; 256 | } 257 | 258 | return false; 259 | } 260 | 261 | bool SerComm::_set(const StringName &p_name, const Variant &p_value) 262 | { 263 | if (p_name == StringName("port")) 264 | { 265 | set_port(p_value); 266 | return true; 267 | } else if (p_name == StringName("toggle_to_refresh")) { 268 | refresh_ports(); 269 | notify_property_list_changed(); 270 | return true; 271 | } 272 | 273 | return false; 274 | } 275 | -------------------------------------------------------------------------------- /src/ser_comm.h: -------------------------------------------------------------------------------- 1 | #ifndef SERCOMM_H 2 | #define SERCOMM_H 3 | 4 | #include 5 | #include 6 | #include "variant_helper.h" 7 | 8 | namespace godot 9 | { 10 | using namespace std; 11 | 12 | class SerComm : public Node 13 | { 14 | GDCLASS(SerComm, Node) 15 | 16 | enum BaudRate 17 | { 18 | BAUD_110 = 110, 19 | BAUD_300 = 300, 20 | BAUD_600 = 600, 21 | BAUD_1200 = 1200, 22 | BAUD_2400 = 2400, 23 | BAUD_4800 = 4800, 24 | BAUD_9600 = 9600, 25 | BAUD_14400 = 14400, 26 | BAUD_19200 = 19200, 27 | BAUD_38400 = 38400, 28 | BAUD_57600 = 57600, 29 | BAUD_115200 = 115200, 30 | BAUD_128000 = 128000, 31 | BAUD_256000 = 256000, 32 | }; 33 | 34 | private: 35 | int baud_rate; 36 | sp_port *port; 37 | int _port_enum; 38 | std::vector _ports{}; 39 | godot::String port_enum_str; 40 | 41 | bool opened = false; 42 | 43 | protected: 44 | static void _bind_methods(); 45 | 46 | public: 47 | SerComm(); 48 | ~SerComm(); 49 | 50 | void _process(double delta) override; 51 | 52 | int sercomm_get_waiting(); 53 | String sercomm_read(const int num_bytes); 54 | void sercomm_drain(); 55 | 56 | void set_port(const int p); 57 | int get_port() const; 58 | 59 | void set_baud_rate(const int b); 60 | int get_baud_rate() const; 61 | 62 | bool get_open() const; 63 | 64 | godot::Array sercomm_list_ports(); 65 | bool sercomm_open(); 66 | bool sercomm_open_specific_serial_port(const String& port_name); 67 | 68 | void sercomm_close(); 69 | void sercomm_flush(); 70 | 71 | void sercomm_write(const String &p_message); 72 | 73 | void refresh_ports(); 74 | 75 | void _get_property_list(List *r_list) const; 76 | bool _get(const StringName &p_name, Variant &r_value) const; 77 | bool _set(const StringName &p_name, const Variant &p_value); 78 | }; 79 | } 80 | 81 | #endif // SERCOMM_H 82 | -------------------------------------------------------------------------------- /src/template/sercomm.gdextension.template: -------------------------------------------------------------------------------- 1 | [configuration] 2 | 3 | entry_symbol = "sercomm_library_init" 4 | compatibility_minimum = "4.1" 5 | 6 | [libraries] 7 | 8 | macos.debug = "res://bin/libsercomm.macos.template_debug.framework" 9 | macos.release = "res://bin/libsercomm.macos.template_release.framework" 10 | 11 | windows.debug.x86_32 = "res://bin/libsercomm.windows.template_debug.x86_32.dll" 12 | windows.release.x86_32 = "res://bin/libsercomm.windows.template_release.x86_32.dll" 13 | windows.debug.x86_64 = "res://bin/libsercomm.windows.template_debug.x86_64.dll" 14 | windows.release.x86_64 = "res://bin/libsercomm.windows.template_release.x86_64.dll" 15 | 16 | linux.debug.x86_64 = "res://bin/libsercomm.linux.template_debug.x86_64.so" 17 | linux.release.x86_64 = "res://bin/libsercomm.linux.template_release.x86_64.so" 18 | linux.debug.arm64 = "res://bin/libsercomm.linux.template_debug.arm64.so" 19 | linux.release.arm64 = "res://bin/libsercomm.linux.template_release.arm64.so" 20 | linux.debug.rv64 = "res://bin/libsercomm.linux.template_debug.rv64.so" 21 | linux.release.rv64 = "res://bin/libsercomm.linux.template_release.rv64.so" 22 | 23 | [dependencies] 24 | macos.debug = "res://bin/libserialport.framework" 25 | macos.release = "res://bin/libserialport.framework" 26 | 27 | windows.debug.x86_32 = "res://bin/libserialport.dll" 28 | windows.release.x86_32 = "res://bin/libserialport.dll" 29 | windows.debug.x86_64 = "res://bin/libserialport.dll" 30 | windows.release.x86_64 = "res://bin/libserialport.dll" 31 | 32 | linux.debug.x86_64 = "res://bin/libserialport.so" 33 | linux.release.x86_64 = "res://bin/libserialport.so" 34 | linux.debug.arm64 = "res://bin/libserialport.so" 35 | linux.release.arm64 = "res://bin/libserialport.so" 36 | linux.debug.rv64 = "res://bin/libserialport.so" 37 | linux.release.rv64 = "res://bin/libserialport.so" -------------------------------------------------------------------------------- /src/variant_helper.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace godot; 4 | 5 | class VariantHelper 6 | { 7 | public: 8 | static String _baud_rate_to_hint_string() 9 | { 10 | String hint_string = ""; 11 | const int baud_rates[] = {110, 300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 128000, 256000}; 12 | for (int i = 0; i < sizeof(baud_rates) / sizeof(baud_rates[0]); ++i) 13 | { 14 | if (!hint_string.is_empty()) 15 | { 16 | hint_string += ", "; 17 | } 18 | hint_string += String::num_int64(baud_rates[i]) + ":" + String::num_int64(baud_rates[i]); 19 | } 20 | return hint_string; 21 | }; 22 | 23 | static String _ports_to_hint_string(const std::vector &_ports) 24 | { 25 | String types = "No Ports Found"; 26 | for (size_t i = 0; i < _ports.size(); i++) 27 | { 28 | if (i == 0) 29 | types = _ports[i].c_str(); 30 | else 31 | { 32 | types += ","; 33 | types += _ports[i].c_str(); 34 | } 35 | } 36 | 37 | return types; 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /tools/read_simulator.py: -------------------------------------------------------------------------------- 1 | import serial 2 | 3 | # Replace '/dev/pts/Y' with the actual path of your serial port 4 | ser = serial.Serial('COM3', baudrate=9600, timeout=1) 5 | 6 | try: 7 | while True: 8 | # Read data 9 | received_data = ser.readline() 10 | print("Received:", received_data.decode('utf-8')) 11 | except UnicodeDecodeError: 12 | print("Error decoding data. Check if the data is valid UTF-8.") 13 | except KeyboardInterrupt: 14 | # Handle Ctrl+C to gracefully exit the loop 15 | pass 16 | finally: 17 | ser.close() 18 | -------------------------------------------------------------------------------- /tools/write_similator.py: -------------------------------------------------------------------------------- 1 | import serial 2 | import time 3 | 4 | # Replace '/dev/pts/X' with the actual path of your serial port 5 | ser = serial.Serial('COM3', baudrate=9600, timeout=1) 6 | 7 | try: 8 | while True: 9 | # Send data 10 | data_to_send = b'Hello, world!' 11 | ser.write(data_to_send) 12 | print("Sent:", data_to_send.decode('utf-8')) 13 | 14 | # Add a delay if needed 15 | time.sleep(1) 16 | 17 | except KeyboardInterrupt: 18 | # Handle Ctrl+C to gracefully exit the loop 19 | pass 20 | finally: 21 | ser.close() 22 | --------------------------------------------------------------------------------