Compare commits

..

38 Commits

Author SHA1 Message Date
Duncan Ogilvie 25f015bc83 Bump to 0.2.46 2026-02-01 16:39:51 +01:00
Duncan Ogilvie fc674b4c6b Merge pull request #195 from build-cpp/conditional-languages
Add support for conditional [project].languages
2026-02-01 16:39:16 +01:00
Duncan Ogilvie 433dbb0500 Add test for conditional Objective-C support
Closes #183
2026-02-01 16:37:23 +01:00
Duncan Ogilvie 4772a2b683 Add support for conditional [project].languages
Closes #184
2026-02-01 16:14:17 +01:00
Duncan Ogilvie fb4931df1f Merge pull request #193 from build-cpp/toml11-upgrade
Support TOML 1.1.0
2026-02-01 15:36:15 +01:00
Duncan Ogilvie e96cd30217 Merge pull request #194 from build-cpp/add-dependencies
Add support for [target].dependencies
2026-02-01 15:36:04 +01:00
Duncan Ogilvie 2db8d1ee1d Add support for [target].dependencies
Closes #177
2026-02-01 15:33:09 +01:00
Duncan Ogilvie 645737afd4 Merge pull request #191 from de-ep/fix-tilde-expansion
fix #186
2025-12-21 14:33:09 +01:00
deep 98c2e962c0 fix tilde expansion: add windows support 2025-12-21 14:44:32 +05:30
Duncan Ogilvie 438feccf0f Add support for toml 1.1.0 2025-12-20 15:11:36 +01:00
Duncan Ogilvie 80cc29b8e1 Upgrade toml11 to 3.8.1 2025-12-20 14:29:26 +01:00
Duncan Ogilvie 1a62034a8a Fix formatting 2025-12-20 13:49:34 +01:00
Duncan Ogilvie c12472cd8f Merge pull request #180 from d06i/fix
fix #172
2025-12-17 18:35:20 +01:00
deep 228553d43a fix #186 2025-12-10 19:23:24 +05:30
Duncan Ogilvie 8f4b376e42 Bump to 0.2.45 2025-08-26 17:16:10 +02:00
Duncan Ogilvie fcbf937c49 Merge pull request #189 from iamtimmy/main
template add-function arguments
2025-08-26 17:00:55 +02:00
iamtimmy e609c8a48f template add-function arguments
add arguments support to add-function functionality in templates
2025-08-26 16:46:32 +02:00
Duncan Ogilvie 735898dbac Update basics.md 2025-08-11 03:37:01 +02:00
Duncan Ogilvie f7126b0158 Update basics.md 2025-08-11 03:35:53 +02:00
Duncan Ogilvie b6702714eb Fix info 2025-08-11 03:31:57 +02:00
Duncan Ogilvie efa4f716c2 Add CMake phases to basics 2025-08-11 03:30:28 +02:00
Duncan Ogilvie 76b7b8e778 Clarify basics documentation page 2025-08-11 03:05:37 +02:00
Duncan Ogilvie 501b26ced5 Update fetch-content documentation 2025-06-25 01:50:29 +02:00
Duncan Ogilvie 5bc36275ad Merge pull request #185 from build-cpp/gh-actions-bump
Bump github actions
2025-05-30 01:16:56 +02:00
Duncan Ogilvie 35c837a181 Bump github actions 2025-05-30 00:24:22 +02:00
Duncan Ogilvie 710dfa4963 Merge pull request #182 from evanwporter/patch-1
bumped up upload version
2025-05-30 00:15:38 +02:00
Duncan Ogilvie c49ee6bafe Update README.md 2025-05-29 23:21:04 +02:00
Evan 368273bab2 bumped up upload version 2025-04-19 15:49:15 -07:00
d06i ffe08ae60c fix #172 2025-04-18 00:19:51 +03:00
Duncan Ogilvie b1249af365 Bump to 0.2.44 2025-01-19 15:12:34 +01:00
Duncan Ogilvie ea2ff31b5a Merge pull request #173 from build-cpp/fix-empty-properties
Make sure empty target properties are emitted correctly
2025-01-19 15:10:06 +01:00
Duncan Ogilvie a32eb66df4 Make sure empty target properties are emitted correctly 2025-01-19 14:53:50 +01:00
Duncan Ogilvie 9b85833240 Add link to example vcpkg repository 2024-12-06 18:19:35 +01:00
Duncan Ogilvie ccb3d0b9ef Bump to 0.2.43 2024-12-04 13:00:03 +01:00
Duncan Ogilvie 49cdab39bd Merge pull request #169 from build-cpp/namespace-targets
Support `::mylib` which that target that exists at configure-time
2024-12-04 12:55:57 +01:00
Duncan Ogilvie e318139314 Add documentation for ::mylib 2024-12-04 12:15:51 +01:00
Duncan Ogilvie 0f2a9a55fb Support link-libraries = ["::mylib"] which checks the if target exists
https://gitlab.kitware.com/cmake/cmake/-/issues/26497
2024-12-04 12:15:49 +01:00
Duncan Ogilvie 38cec2d5d1 Do not allow lambdas on a single line 2024-12-04 11:55:13 +01:00
48 changed files with 2010 additions and 567 deletions
+1
View File
@@ -13,6 +13,7 @@ AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AllowShortLambdasOnASingleLine: None
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
+7 -10
View File
@@ -10,19 +10,16 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [windows-2022, macos-latest, ubuntu-20.04]
os: [windows-2022, macos-latest, ubuntu-22.04]
env:
BUILD_TYPE: 'Release'
CMAKE_GENERATOR: 'Ninja'
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install Ninja
uses: seanmiddleditch/gha-setup-ninja@6263846cf3c17009dfc81604efabae16044fc074 # master
uses: actions/checkout@v4
- name: Visual Studio Development Environment
uses: ilammy/msvc-dev-cmd@cec98b9d092141f74527d0afa6feb2af698cfe89 # v1.12.1
uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1.13.1
- name: Tag cmkr.cmake
if: ${{ startsWith(github.ref, 'refs/tags/') }}
@@ -45,27 +42,27 @@ jobs:
ctest -C ${{ env.BUILD_TYPE }} --verbose
- name: Upload artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ${{ github.event.repository.name }}-${{ matrix.os }}
path: install/bin/*
- name: Get lowercase OS name
id: osname
uses: ASzc/change-string-case-action@07c1e24a97f0951e13f88870b99c058fcf0b14cf # v5
uses: ASzc/change-string-case-action@ccb130a4e483d3e86287289183704dc9bf53e77e # master 2024-04-05
if: ${{ startsWith(github.ref, 'refs/tags/') }}
with:
string: ${{ runner.os }}
- name: Compress artifacts
uses: vimtor/action-zip@26a249fb00d43ca98dad77a4b3838025fc226aa1 # v1.1
uses: vimtor/action-zip@1379ea20d4c5705669ba81fd626dd01b1c738f26 # v1.2
if: ${{ startsWith(github.ref, 'refs/tags/') }}
with:
files: install/bin/
dest: ${{ github.event.repository.name }}-${{ steps.osname.outputs.lowercase }}.zip
- name: Release
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v0.1.15
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2
if: ${{ startsWith(github.ref, 'refs/tags/') }}
with:
prerelease: ${{ !startsWith(github.ref, 'refs/tags/v') || contains(github.ref, '-pre') }}
+3 -3
View File
@@ -10,7 +10,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: clang-format
id: clang-format
@@ -35,7 +35,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Run editorconfig-checker
uses: editorconfig-checker/action-editorconfig-checker@d4fca16fc71adef10fbe101903b654449fa9570c # master 2022-03-15
uses: editorconfig-checker/action-editorconfig-checker@27dd658c2b1282c7f1f4dc242cdf0f6d8af728ab # master 2025-05-30
+1 -1
View File
@@ -22,7 +22,7 @@ project(cmkr
LANGUAGES
CXX
VERSION
0.2.42
0.2.46
DESCRIPTION
"CMakeLists generator from TOML"
)
+2
View File
@@ -1,5 +1,7 @@
# cmkr
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/build-cpp/cmkr)
`cmkr`, pronounced "cmaker", is a modern build system based on [CMake](https://cmake.org/) and [TOML](https://toml.io).
`cmkr` parses `cmake.toml` files and generates a modern, idiomatic `CMakeLists.txt` for you. A minimal example:
+1 -1
View File
@@ -4,7 +4,7 @@ cmkr-include = false
[project]
name = "cmkr"
version = "0.2.42"
version = "0.2.46"
description = "CMakeLists generator from TOML"
languages = ["CXX"]
include-after = [
+8 -1
View File
@@ -2,7 +2,7 @@ include_guard()
# Change these defaults to point to your infrastructure if desired
set(CMKR_REPO "https://github.com/build-cpp/cmkr" CACHE STRING "cmkr git repository" FORCE)
set(CMKR_TAG "v0.2.42" CACHE STRING "cmkr git tag (this needs to be available forever)" FORCE)
set(CMKR_TAG "v0.2.46" CACHE STRING "cmkr git tag (this needs to be available forever)" FORCE)
set(CMKR_COMMIT_HASH "" CACHE STRING "cmkr git commit hash (optional)" FORCE)
# To bootstrap/generate a cmkr project: cmake -P cmkr.cmake
@@ -61,6 +61,13 @@ endif()
if(DEFINED ENV{CMKR_CACHE})
set(CMKR_DIRECTORY_PREFIX "$ENV{CMKR_CACHE}")
string(REPLACE "\\" "/" CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}")
if(CMKR_DIRECTORY_PREFIX MATCHES "^~")
if(WIN32)
string(REGEX REPLACE "^~" "$ENV{USERPROFILE}" CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}")
elseif(UNIX)
string(REGEX REPLACE "^~" "$ENV{HOME}" CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}")
endif()
endif()
if(NOT CMKR_DIRECTORY_PREFIX MATCHES "\\/$")
set(CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}/")
endif()
+80 -13
View File
@@ -6,7 +6,7 @@ nav_order: 1
# Basics
To effectively use cmkr it helps to understand the basic concepts of CMake.
To effectively use cmkr it helps to understand the basic concepts of CMake. This page serves as an introduction to CMake for beginners. Links to the CMake documentation are included for reference.
## Projects
@@ -16,25 +16,41 @@ A CMake **project** is a collection of targets. In the context of libraries the
## Targets
The basic unit of CMake is called a **target**. A target (also referred to as [binary target](https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#binary-targets) in the CMake documentation) corresponds to an executable or library you can build. There are also [pseudo targets](https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#pseudo-targets), but we ignore them for now.
The basic unit of CMake is called a **target**. A target (also referred to as [binary target](https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#binary-targets) in the CMake documentation) corresponds to an executable or static/dynamic library you can build.
<sub>Visual Studio: a **target** corresponds to a _project_.</sub>
Most important target types:
|Type|Purpose|
|-|-|
|Executable|Program you run on your computer (`.exe`)|
|Static library|Library code _compiled into_ the final executable (`.lib`, `.a`)|
|Dynamic library|Library code loaded at runtime (`.dll`, `.so`, `.dylib`)|
|Interface|Used for organizational purposes (common flags/includes/libraries)|
There are also other [pseudo targets](https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#pseudo-targets), but they are not important for now.
## Target Properties
Targets have a collection of **properties** that describe how to build them.
Targets have a collection of **properties** that describe how the compiler builds them.
Examples of properties:
The most commonly-used properties:
- _Sources_: the `*.cpp` files used to build the target.
- _Compile options_: Command line flags for the compiler.
- _Link libraries_: The **dependencies** required to build this target.
- [_Sources_](https://cmake.org/cmake/help/latest/command/target_sources.html): the `*.cpp` files used to build the target.
- [_Include directories_](https://cmake.org/cmake/help/latest/command/target_include_directories.html): Paths to directories the compiler will search when doing `#include`.
- [_Compile definitions_](https://cmake.org/cmake/help/latest/command/target_compile_definitions.html): Preprocessor macro definitions used for compilation (`-DMYOPTION`).
- [_Compile options_](https://cmake.org/cmake/help/latest/command/target_compile_options.html): Command line flags for the compiler (platform/compiler specific).
- [_Compile features_](https://cmake.org/cmake/help/latest/command/target_compile_features.html): C/C++ standard version to build with.
- [_Link libraries_](https://cmake.org/cmake/help/latest/command/target_link_libraries.html): The **dependencies** required to build this target.
- [_Link options_](https://cmake.org/cmake/help/latest/command/target_link_options.html): Command line flags for the linker (platform/linker specific).
<sub>See the [CMake documentation](https://cmake.org/cmake/help/latest/manual/cmake-properties.7.html#properties-on-targets) for an exhaustive list of target properties.</sub>
**Important**: The term **link** has a slightly different meaning in CMake than you might expect. In addition to adding a library to the command line of the linker, CMake also propagates properties of the target you link to.
**Important**: The term **link** has a slightly different meaning in CMake than you might expect. In addition to adding a library to the command line, CMake also (transitively) propagates properties of the target you link to based on their _visibility_.
<sub>You can think of **linking** as _depending on_.</sub>
You should think of **linking** in CMake as _depending on_.
{:.info}
The propagation of properties depends on their **visibility**:
@@ -44,7 +60,7 @@ The propagation of properties depends on their **visibility**:
In practice you default to **private**, unless consumers of your library _require_ the property to build their target. In that case you use **public**.
### Example
## Example
The most intuitive example is with _include directories_. Imagine there are two targets:
@@ -67,10 +83,61 @@ name = "DataProcessor"
type = "static"
sources = ["StringUtils/src/stringutils.cpp"]
headers = ["StringUtils/include/stringutils.hpp"]
include-directories = ["StringUtils/include"]
include-directories = ["StringUtils/include"] # public (default for libraries)
[target.DataProcessor]
type = "executable"
sources = ["DataProcessor/src/main.cpp"]
link-libraries = ["StringUtils"]
```
link-libraries = ["StringUtils"] # private (default for executables)
```
The generated `CMakeLists.txt` (simplified) looks like this:
```cmake
project(DataProcessor)
# Target: StringUtils
add_library(StringUtils STATIC
"StringUtils/include/stringutils.hpp"
"StringUtils/src/stringutils.cpp"
)
target_include_directories(StringUtils PUBLIC
"StringUtils/include"
)
# Target: DataProcessor
add_executable(DataProcessor
"DataProcessor/src/main.cpp"
)
target_link_libraries(DataProcessor PRIVATE
StringUtils
)
```
## CMake Phases
CMake works in three phases:
1. **Configure**: Execute `CMakeLists.txt` (find dependencies, create targets, OS/compiler-specific conditions, etc).
2. **Generate**: Generate build files (propagates target properties and handles [_generator expressions_](https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html)).
3. **Build**: Execute the compiler to produce executables/libraries.
CMake keeps generated files separate from your source in a **build directory**. You specify a [**generator**](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html) (Visual Studio/Ninja) and [**build type**](https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#build-configurations) (Debug/Release) during configuration.
```bash
# Configure project and generate build files in build directory
cmake -B build -DCMAKE_BUILD_TYPE=Release
# Build the project
cmake --build build --config Release
```
We specify both `CMAKE_BUILD_TYPE` and `--config` to be compatible with generators that support multiple configurations at once ((like Visual Studio/Xcode/Ninja Multi-Config).
{:.info}
**Build Types**:
- `Debug`: No optimization, debug symbols
- `Release`: Full optimization, no debug symbols
- `RelWithDebInfo`: Medium optimizations, debug symbols
- `MinSizeRel`: Optimize for size, no debug symbols
**Generators**: CMake auto-detects (Makefiles on Linux/macOS, Visual Studio on Windows), but you can specify explicitly with `-G` during the _configure_ step.
+18 -12
View File
@@ -171,7 +171,9 @@ The vcpkg `version` will automatically generate the `url` from the [official rep
To specify package features you can use the following syntax: `imgui[docking-experimental,freetype,sdl2-binding,opengl3-binding]`. To disable the [default features](https://learn.microsoft.com/en-us/vcpkg/concepts/default-features) you can do: `cpp-httplib[core,openssl]`
The `overlay-ports` feature allows you to embed vcpkg ports inside your project, without having to fork the main vcpkg registry or creating a custom registry. You can find more information in the relevant [documentation](https://learn.microsoft.com/en-us/vcpkg/concepts/overlay-ports). The `overlay-triplets` feature allows you to customize triplets and change the default behavior (for example always preferring static libraries on Windows). To specify both in one go you can do `[vcpkg].overlay = "my-overlay"`.
The `overlay-ports` feature allows you to embed vcpkg ports inside your project, without having to fork the main vcpkg registry or creating a custom registry. You can find more information in the relevant [documentation](https://learn.microsoft.com/en-us/vcpkg/concepts/overlay-ports). The `overlay-triplets` feature allows you to customize triplets and change the default behavior (for example always preferring static libraries on Windows). To specify both in one go you can do `[vcpkg].overlay = "vcpkg-overlay"`.
For a concrete example of all the features, take a look at the [vcpkg_template](https://github.com/build-cpp/vcpkg_template) repository.
## Packages
@@ -189,26 +191,28 @@ components = ["mycomponent"]
**Note**: The `[fetch-content]` feature is unpolished and will likely change in a future release.
```toml
# Include CMake project from git
[fetch-content.gitcontent]
condition = "mycondition"
git = "https://github.com/myuser/gitcontent"
tag = "v0.1"
shallow = false
shallow = false # shallow clone (--depth 1)
system = false
subdir = ""
subdir = "" # folder containing CMakeLists.txt
# Include a CMake project from a URL
[fetch-content.urlcontent]
condition = "mycondition"
url = "https://content-host.com/urlcontent.zip"
# Other supported algorithms:
# md5, sha1, sha224, sha256, sha384, sha512, sha3_224, sha3_256, sha3_384, sha3_512
hash = "SHA1 502a4e25b8b209889c99c7fa0732102682c2e4ff"
sha1 = "502a4e25b8b209889c99c7fa0732102682c2e4ff"
[fetch-content.svncontent]
condition = "mycondition"
svn = "https://svn-host.com/url"
rev = "svn_rev"
[fetch-content.urlcontent]
condition = "mycondition"
url = "https://content-host.com/urlcontent.zip"
# These are equivalent, supported algorithms:
# md5, sha1, sha224, sha256, sha384, sha512, sha3_224, sha3_256, sha3_384, sha3_512
hash = "SHA1 502a4e25b8b209889c99c7fa0732102682c2e4ff"
sha1 = "502a4e25b8b209889c99c7fa0732102682c2e4ff"
```
Table keys that match CMake variable names (`[A-Z_]+`) will be passed to the [`FetchContent_Declare`](https://cmake.org/cmake/help/latest/module/FetchContent.html#command:fetchcontent_declare) command.
@@ -272,7 +276,7 @@ A table mapping the cmkr features to the relevant CMake construct and the releva
| `compile-options` | [`target_compile_options`](https://cmake.org/cmake/help/latest/command/target_compile_options.html) | Adds compiler flags. |
| `include-directories` | [`target_include_directories`](https://cmake.org/cmake/help/latest/command/target_include_directories.html) | Adds include directories. |
| `link-directories` | [`target_link_directories`](https://cmake.org/cmake/help/latest/command/target_link_directories.html) | Adds library directories. |
| `link-libraries` | [`target_link_libraries`](https://cmake.org/cmake/help/latest/command/target_link_libraries.html) | Adds library dependencies. |
| `link-libraries` | [`target_link_libraries`](https://cmake.org/cmake/help/latest/command/target_link_libraries.html) | Adds library dependencies. Use `::mylib` to make sure a target exists. |
| `link-options` | [`target_link_options`](https://cmake.org/cmake/help/latest/command/target_link_options.html) | Adds linker flags. |
| `precompile-headers` | [`target_precompile_headers`](https://cmake.org/cmake/help/latest/command/target_precompile_headers.html) | Specifies precompiled headers. |
| `properties` | [`set_target_properties`](https://cmake.org/cmake/help/latest/command/set_target_properties.html) | See [properties on targets](https://cmake.org/cmake/help/latest/manual/cmake-properties.7.html#properties-on-targets) for more information. |
@@ -298,6 +302,7 @@ condition = "MYPROJECT_BUILD_EXAMPLES"
type = "executable"
link-libraries = ["myproject::mylib"]
add-function = ""
add-arguments = ["myoption"]
pass-sources = false
# Properties from the template are merged with the ones here
@@ -309,6 +314,7 @@ sources = ["src/myexample.cpp"]
The properties declared on a `template` are the same as the ones you use for targets. The only exceptions are:
- `add-function`: Specifies a custom add function. Projects like [pybind11](https://pybind11.readthedocs.io/en/stable/cmake/index.html#new-findpython-mode) have their own `add_xxx` function, which you can specify here.
- `add-arguments`: Arguments to pass to the `add-function` before the list of sources. See [cmake_parse_arguments](https://cmake.org/cmake/help/latest/command/cmake_parse_arguments.html) for more details.
- `pass-sources`: Pass sources directly to the add function instead of using `target_sources`.
## Tests and installation (unfinished)
+3 -2
View File
@@ -16,8 +16,9 @@ Downloads [fmt v7.1.3](https://fmt.dev/7.1.3/) from [GitHub](https://github.com)
name = "fetch-content"
description = "Fetching from git"
[fetch-content]
fmt = { git = "https://github.com/fmtlib/fmt", tag = "7.1.3" }
[fetch-content.fmt]
git = "https://github.com/fmtlib/fmt"
tag = "7.1.3"
[target.example]
type = "executable"
+30
View File
@@ -0,0 +1,30 @@
---
# Automatically generated from tests/objective-c/cmake.toml - DO NOT EDIT
layout: default
title: Objective-C
permalink: /examples/objective-c
parent: Examples
nav_order: 11
---
# Objective-C
Add Objective-C sources on Apple platforms:
```toml
[project]
name = "objective-c"
description = "Objective-C"
languages = ["C"]
apple.languages = ["OBJC"]
[target.hello]
type = "executable"
sources = ["src/main.c"]
apple.sources = ["src/apple.m"]
apple.link-libraries = ["$<LINK_LIBRARY:FRAMEWORK,Foundation>"]
```
<sup><sub>This page was automatically generated from [tests/objective-c/cmake.toml](https://github.com/build-cpp/cmkr/tree/main/tests/objective-c/cmake.toml).</sub></sup>
+4 -4
View File
@@ -20,11 +20,11 @@ description = "Target templates"
type = "executable"
sources = ["src/templates.cpp"]
compile-definitions = ["IS_APP"]
# Unlike interface targets you can also inherit properties
[template.app.properties]
CXX_STANDARD = "11"
CXX_STANDARD_REQUIRED = true
properties = {
CXX_STANDARD = "11",
CXX_STANDARD_REQUIRED = true,
}
[target.app-a]
type = "app"
+4 -1
View File
@@ -105,6 +105,8 @@ struct Target {
ConditionVector precompile_headers;
ConditionVector private_precompile_headers;
ConditionVector dependencies;
std::string condition;
std::string alias;
Condition<tsl::ordered_map<std::string, std::string>> properties;
@@ -118,6 +120,7 @@ struct Target {
struct Template {
Target outline;
std::string add_function;
std::vector<std::string> add_arguments;
bool pass_sources_to_add_function = false;
};
@@ -191,7 +194,7 @@ struct Project {
std::string project_name;
std::string project_version;
std::string project_description;
std::vector<std::string> project_languages;
ConditionVector project_languages;
bool project_allow_unknown_languages = false;
MsvcRuntimeType project_msvc_runtime = msvc_last;
Condition<std::string> cmake_before;
+116 -54
View File
@@ -54,6 +54,18 @@ static std::string format(const char *format, const tsl::ordered_map<std::string
return s;
}
static std::string quoted(const std::string &str) {
return "\"" + str + "\"";
}
static std::string space_error_check(const std::string &str) {
auto pos = str.find_last_of(' ');
if (pos != std::string::npos)
return quoted(str) + " <------ !space(s)";
else
return quoted(str);
}
static std::vector<fs::path> expand_cmake_path(const fs::path &source_path, const fs::path &toml_dir, bool is_root_project) {
auto is_subdir = [](fs::path p, const fs::path &root) {
while (true) {
@@ -289,6 +301,10 @@ struct RawArg {
}
std::string arg;
bool empty() const {
return arg.empty();
}
};
// Credit: JustMagic
@@ -406,7 +422,7 @@ struct Command {
}
bool print_arg(const RawArg &arg) {
if (arg.arg.empty()) {
if (arg.empty()) {
return true;
}
@@ -558,11 +574,15 @@ struct Generator {
}
void conditional_includes(const parser::ConditionVector &include) {
handle_condition(include, [this](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
handle_condition(include, [this](const std::string &, const std::vector<std::string> &includes) {
inject_includes(includes);
});
}
void conditional_cmake(const parser::Condition<std::string> &cmake) {
handle_condition(cmake, [this](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
handle_condition(cmake, [this](const std::string &, const std::string &cmake) {
inject_cmake(cmake);
});
}
bool if_condition(const std::string &condition) {
@@ -705,23 +725,54 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
parser::Project project(parent_project, path, false);
for (const auto &lang : project.project_languages) {
if (known_languages.find(lang) == known_languages.end()) {
if (project.project_allow_unknown_languages) {
printf("[warning] Unknown language '%s' specified\n", lang.c_str());
} else {
throw std::runtime_error("Unknown language '" + lang + "' specified");
}
tsl::ordered_set<std::string> flat_project_languages;
for (const auto &itr : project.project_languages) {
for (const auto &language : itr.second) {
flat_project_languages.insert(language);
}
}
// Reference: https://gitlab.kitware.com/cmake/cmake/-/issues/24340#note_1304703
flat_project_languages.insert("RC");
// All acceptable extensions based off our given languages.
tsl::ordered_set<std::string> project_extensions;
for (const auto &language : flat_project_languages) {
auto itr = known_languages.find(language);
if (itr == known_languages.end()) {
if (project.project_allow_unknown_languages) {
printf("[warning] Unknown language '%s' specified\n", language.c_str());
} else {
throw std::runtime_error("Unknown language '" + language + "' specified");
}
} else {
project_extensions.insert(itr->second.begin(), itr->second.end());
}
}
auto contains_language_source = [&project_extensions](const std::vector<std::string> &sources) {
for (const auto &source : sources) {
auto extension = fs::path(source).extension().string();
if (project_extensions.count(extension) > 0) {
return true;
}
}
return false;
};
Generator gen(project, path);
// Helper lambdas for more convenient CMake generation
auto &ss = gen.ss;
auto cmd = [&gen](const std::string &command) { return gen.cmd(command); };
auto comment = [&gen](const std::string &comment) { return gen.comment(comment); };
auto endl = [&gen]() { gen.endl(); };
auto cmd = [&gen](const std::string &command) {
return gen.cmd(command);
};
auto comment = [&gen](const std::string &comment) {
return gen.comment(comment);
};
auto endl = [&gen]() {
gen.endl();
};
std::string cmkr_url = "https://github.com/build-cpp/cmkr";
comment("This file is automatically generated from cmake.toml - DO NOT EDIT");
@@ -886,17 +937,29 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
gen.conditional_cmake(project.cmake_before);
if (!project.project_name.empty()) {
auto languages = std::make_pair("LANGUAGES", project.project_languages);
auto languages = std::make_pair("LANGUAGES", project.project_languages[""]);
auto version = std::make_pair("VERSION", project.project_version);
auto description = std::make_pair("DESCRIPTION", project.project_description);
cmd("project")(project.project_name, languages, version, description).endl();
cmd("project")(project.project_name, languages, version, description);
for (const auto &language : project.project_languages) {
for (const auto &language : project.project_languages[""]) {
if (language == "CSharp") {
cmd("include")("CSharpUtilities").endl();
break;
}
}
gen.handle_condition(project.project_languages, [&cmd](const std::string &condition, const std::vector<std::string> &languages) {
if (!condition.empty()) {
cmd("enable_language")(languages).endl();
for (const auto &language : languages) {
if (language == "CSharp") {
cmd("include")("CSharpUtilities").endl();
break;
}
}
}
});
}
gen.conditional_includes(project.include_after);
@@ -1112,40 +1175,14 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
gen.conditional_cmake(subdir.cmake_after);
}
// The implicit default is ["C", "CXX"], so make sure this list isn't
// empty or projects without languages explicitly defined will error.
auto project_languages = project.project_languages;
if (project_languages.empty())
project_languages = {"C", "CXX"};
// Reference: https://gitlab.kitware.com/cmake/cmake/-/issues/24340#note_1304703
project_languages.push_back("RC");
// All acceptable extensions based off our given languages.
tsl::ordered_set<std::string> project_extensions;
for (const auto &language : project_languages) {
auto itr = known_languages.find(language);
if (itr != known_languages.end()) {
project_extensions.insert(itr->second.begin(), itr->second.end());
}
}
auto contains_language_source = [&project_extensions](const std::vector<std::string> &sources) {
for (const auto &source : sources) {
auto extension = fs::path(source).extension().string();
if (project_extensions.count(extension) > 0) {
return true;
}
}
return false;
};
if (!project.targets.empty()) {
auto project_root = project.root();
for (size_t i = 0; i < project.targets.size(); i++) {
const auto &target = project.targets[i];
auto throw_target_error = [&target](const std::string &message) { throw std::runtime_error("[target." + target.name + "] " + message); };
auto throw_target_error = [&target](const std::string &message) {
throw std::runtime_error("[target." + target.name + "] " + message);
};
const parser::Template *tmplate = nullptr;
std::unique_ptr<ConditionScope> tmplate_cs{};
@@ -1278,7 +1315,7 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
continue;
const auto &source_path = fs::path(path) / source;
if (!fs::exists(source_path)) {
throw_target_error("Source file not found: " + source);
throw_target_error("Source file not found: " + space_error_check(source));
}
}
break;
@@ -1356,12 +1393,11 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
// Handle custom add commands from templates.
if (tmplate != nullptr && !tmplate->add_function.empty()) {
add_command = tmplate->add_function;
target_type_string = ""; // TODO: let templates supply options to the add_command here?
if (tmplate->pass_sources_to_add_function) {
cmd(add_command)(target.name, target_type_string, "${" + sources_var + "}");
cmd(add_command)(target.name, tmplate->add_arguments, "${" + sources_var + "}");
} else {
cmd(add_command)(target.name, target_type_string).endl();
cmd(add_command)(target.name, tmplate->add_arguments).endl();
if (has_sources) {
cmd("target_sources")(target.name, target_type == parser::target_interface ? "INTERFACE" : "PRIVATE",
"${" + sources_var + "}");
@@ -1384,8 +1420,29 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
}
auto target_cmd = [&](const char *command, const parser::ConditionVector &cargs, const std::string &scope) {
gen.handle_condition(cargs,
[&](const std::string &, const std::vector<std::string> &args) { cmd(command)(target.name, scope, args); });
gen.handle_condition(cargs, [&](const std::string &, const std::vector<std::string> &args) {
cmd(command)(target.name, scope, args);
});
};
auto link_cmd = [&](const char *command, const parser::ConditionVector &cargs, const std::string &scope) {
gen.handle_condition(cargs, [&](const std::string &, const std::vector<std::string> &args) {
std::vector<std::string> targs;
for (const std::string &arg : args) {
if (arg.find("::") == 0) {
auto library = arg.substr(2);
// clang-format off
cmd("if")("NOT", "TARGET", library);
cmd("message")("FATAL_ERROR", "Target \"" + library + "\" referenced by \"" + target.name + "\" does not exist!");
cmd("endif")().endl();
// clang-format on
targs.push_back(std::move(library));
} else {
targs.push_back(arg);
}
}
cmd(command)(target.name, scope, targs);
});
};
auto gen_target_cmds = [&](const parser::Target &t) {
@@ -1404,14 +1461,16 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
target_cmd("target_link_directories", t.link_directories, target_scope);
target_cmd("target_link_directories", t.private_link_directories, "PRIVATE");
target_cmd("target_link_libraries", t.link_libraries, target_scope);
target_cmd("target_link_libraries", t.private_link_libraries, "PRIVATE");
link_cmd("target_link_libraries", t.link_libraries, target_scope);
link_cmd("target_link_libraries", t.private_link_libraries, "PRIVATE");
target_cmd("target_link_options", t.link_options, target_scope);
target_cmd("target_link_options", t.private_link_options, "PRIVATE");
target_cmd("target_precompile_headers", t.precompile_headers, target_scope);
target_cmd("target_precompile_headers", t.private_precompile_headers, "PRIVATE");
link_cmd("add_dependencies", t.dependencies, "");
};
if (tmplate != nullptr) {
@@ -1428,14 +1487,17 @@ void generate_cmake(const char *path, const parser::Project *parent_project) {
}
gen.handle_condition(props, [&](const std::string &, const tsl::ordered_map<std::string, std::string> &properties) {
tsl::ordered_map<std::string, RawArg> raw_properties;
for (const auto &propItr : properties) {
if (propItr.first == "MSVC_RUNTIME_LIBRARY") {
if (project_root->project_msvc_runtime == parser::msvc_last) {
throw_target_error("You cannot set msvc-runtime without setting the root [project].msvc-runtime");
}
}
// NOTE: We need to make sure that empty strings do not get omitted
raw_properties.emplace(propItr.first, Command::quote(propItr.second));
}
cmd("set_target_properties")(target.name, "PROPERTIES", properties);
cmd("set_target_properties")(target.name, "PROPERTIES", raw_properties);
});
}
+8
View File
@@ -301,6 +301,11 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
project.required("name", project_name);
project.optional("version", project_version);
project.optional("description", project_description);
// The implicit default is ["C", "CXX"], so make sure this list isn't
// empty or projects without languages explicitly defined will error.
project_languages[""] = {"C", "CXX"};
project.optional("languages", project_languages);
project.optional("allow-unknown-languages", project_allow_unknown_languages);
project.optional("cmake-before", cmake_before);
@@ -690,6 +695,8 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
t.optional("precompile-headers", target.precompile_headers);
t.optional("private-precompile-headers", target.private_precompile_headers);
t.optional("dependencies", target.dependencies);
Condition<std::string> msvc_runtime;
t.optional("msvc-runtime", msvc_runtime);
for (const auto &cond_itr : msvc_runtime) {
@@ -785,6 +792,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) : p
tmplate.outline = parse_target(name, t, true);
t.optional("add-function", tmplate.add_function);
t.optional("add-arguments", tmplate.add_arguments);
t.optional("pass-sources-to-add-function", tmplate.pass_sources_to_add_function);
t.optional("pass-sources", tmplate.pass_sources_to_add_function);
+9
View File
@@ -122,3 +122,12 @@ if(WIN32) # windows
)
endif()
add_test(
NAME
objective-c
WORKING_DIRECTORY
"${CMAKE_CURRENT_LIST_DIR}/objective-c"
COMMAND
"$<TARGET_FILE:cmkr>"
build
)
+6
View File
@@ -65,3 +65,9 @@ name = "relative-paths"
working-directory = "relative-paths"
command = "$<TARGET_FILE:cmkr>"
arguments = ["build"]
[[test]]
name = "objective-c"
working-directory = "objective-c"
command = "$<TARGET_FILE:cmkr>"
arguments = ["build"]
+3 -2
View File
@@ -4,8 +4,9 @@
name = "fetch-content"
description = "Fetching from git"
[fetch-content]
fmt = { git = "https://github.com/fmtlib/fmt", tag = "7.1.3" }
[fetch-content.fmt]
git = "https://github.com/fmtlib/fmt"
tag = "7.1.3"
[target.example]
type = "executable"
+13
View File
@@ -0,0 +1,13 @@
# Add Objective-C sources on Apple platforms:
[project]
name = "objective-c"
description = "Objective-C"
languages = ["C"]
apple.languages = ["OBJC"]
[target.hello]
type = "executable"
sources = ["src/main.c"]
apple.sources = ["src/apple.m"]
apple.link-libraries = ["$<LINK_LIBRARY:FRAMEWORK,Foundation>"]
+7
View File
@@ -0,0 +1,7 @@
#import <Foundation/Foundation.h>
void platform_specific() {
NSFileHandle *stdout = [NSFileHandle fileHandleWithStandardOutput];
NSString *message = @"Hello from Objective-C!\n";
[stdout writeData:[message dataUsingEncoding:NSUTF8StringEncoding] error:nil];
}
+14
View File
@@ -0,0 +1,14 @@
#include <stdio.h>
#ifdef __APPLE__
void platform_specific();
#else
void platform_specific() {
puts("This is not an Apple platform.");
}
#endif
int main() {
platform_specific();
return 0;
}
+4 -4
View File
@@ -8,11 +8,11 @@ description = "Target templates"
type = "executable"
sources = ["src/templates.cpp"]
compile-definitions = ["IS_APP"]
# Unlike interface targets you can also inherit properties
[template.app.properties]
CXX_STANDARD = "11"
CXX_STANDARD_REQUIRED = true
properties = {
CXX_STANDARD = "11",
CXX_STANDARD_REQUIRED = true,
}
[target.app-a]
type = "app"
Generated Vendored
+4 -3
View File
@@ -9,9 +9,10 @@ add_library(ordered_map INTERFACE)
target_include_directories(ordered_map INTERFACE ordered-map-1.0.0/include)
target_compile_definitions(ordered_map INTERFACE NOMINMAX)
# https://github.com/ToruNiina/toml11 (MIT)
add_library(toml11 INTERFACE)
target_include_directories(toml11 INTERFACE toml11-3.6.0)
# https://github.com/ToruNiina/toml11 (MIT)
add_library(toml11 INTERFACE)
target_include_directories(toml11 INTERFACE toml11-3.8.1)
target_compile_definitions(toml11 INTERFACE TOML11_USE_UNRELEASED_TOML_FEATURES)
# https://github.com/mpark/variant (BSL-1.0)
add_library(mpark_variant INTERFACE)
@@ -25,21 +25,14 @@
#ifndef TOML_FOR_MODERN_CPP
#define TOML_FOR_MODERN_CPP
#ifndef __cplusplus
# error "__cplusplus is not defined"
#endif
#if __cplusplus < 201103L && _MSC_VER < 1900
# error "toml11 requires C++11 or later."
#endif
#define TOML11_VERSION_MAJOR 3
#define TOML11_VERSION_MINOR 5
#define TOML11_VERSION_PATCH 0
#define TOML11_VERSION_MINOR 8
#define TOML11_VERSION_PATCH 1
#include "toml/parser.hpp"
#include "toml/literal.hpp"
#include "toml/serializer.hpp"
#include "toml/get.hpp"
#include "toml/macros.hpp"
#endif// TOML_FOR_MODERN_CPP
@@ -17,11 +17,41 @@ namespace color_ansi
{
namespace detail
{
inline int colorize_index()
{
static const int index = std::ios_base::xalloc();
return index;
}
// Control color mode globally
class color_mode
{
public:
inline void enable()
{
should_color_ = true;
}
inline void disable()
{
should_color_ = false;
}
inline bool should_color() const
{
return should_color_;
}
static color_mode& status()
{
static color_mode status_;
return status_;
}
private:
bool should_color_ = false;
};
} // detail
inline std::ostream& colorize(std::ostream& os)
@@ -55,6 +85,21 @@ inline std::ostream& cyan (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[36m";} return os;}
inline std::ostream& white (std::ostream& os)
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[37m";} return os;}
inline void enable()
{
return detail::color_mode::status().enable();
}
inline void disable()
{
return detail::color_mode::status().disable();
}
inline bool should_color()
{
return detail::color_mode::status().should_color();
}
} // color_ansi
// ANSI escape sequence is the only and default colorization method currently
@@ -29,7 +29,7 @@ namespace detail
// to output character as an error message.
inline std::string show_char(const char c)
{
// It supress an error that occurs only in Debug mode of MSVC++ on Windows.
// It suppresses an error that occurs only in Debug mode of MSVC++ on Windows.
// I'm not completely sure but they check the value of char to be in the
// range [0, 256) and some of the COMPLETELY VALID utf-8 character sometimes
// has negative value (if char has sign). So here it re-interprets c as
@@ -154,7 +154,7 @@ struct sequence<Head, Tail...>
invoke(location& loc)
{
const auto first = loc.iter();
const auto rslt = Head::invoke(loc);
auto rslt = Head::invoke(loc);
if(rslt.is_err())
{
loc.reset(first);
@@ -4,11 +4,18 @@
#define TOML11_COMMENTS_HPP
#include <initializer_list>
#include <iterator>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#ifdef TOML11_PRESERVE_COMMENTS_BY_DEFAULT
# define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::preserve_comments
#else
# define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::discard_comments
#endif
// This file provides mainly two classes, `preserve_comments` and `discard_comments`.
// Those two are a container that have the same interface as `std::vector<std::string>`
// but bahaves in the opposite way. `preserve_comments` is just the same as
@@ -339,7 +346,7 @@ operator+(const empty_iterator<T, C>& lhs, typename empty_iterator<T, C>::differ
//
// Why this is chose as the default type is because the last version (2.x.y)
// does not contain any comments in a value. To minimize the impact on the
// efficiency, this is choosed as a default.
// efficiency, this is chosen as a default.
//
// To reduce the memory footprint, later we can try empty base optimization (EBO).
struct discard_comments
@@ -418,14 +425,14 @@ struct discard_comments
// empty, so accessing through operator[], front/back, data causes address
// error.
reference operator[](const size_type) noexcept {return *data();}
const_reference operator[](const size_type) const noexcept {return *data();}
reference operator[](const size_type) noexcept {never_call("toml::discard_comment::operator[]");}
const_reference operator[](const size_type) const noexcept {never_call("toml::discard_comment::operator[]");}
reference at(const size_type) {throw std::out_of_range("toml::discard_comment is always empty.");}
const_reference at(const size_type) const {throw std::out_of_range("toml::discard_comment is always empty.");}
reference front() noexcept {return *data();}
const_reference front() const noexcept {return *data();}
reference back() noexcept {return *data();}
const_reference back() const noexcept {return *data();}
reference front() noexcept {never_call("toml::discard_comment::front");}
const_reference front() const noexcept {never_call("toml::discard_comment::front");}
reference back() noexcept {never_call("toml::discard_comment::back");}
const_reference back() const noexcept {never_call("toml::discard_comment::back");}
pointer data() noexcept {return nullptr;}
const_pointer data() const noexcept {return nullptr;}
@@ -443,6 +450,18 @@ struct discard_comments
const_reverse_iterator rend() const noexcept {return const_iterator{};}
const_reverse_iterator crbegin() const noexcept {return const_iterator{};}
const_reverse_iterator crend() const noexcept {return const_iterator{};}
private:
[[noreturn]] static void never_call(const char *const this_function)
{
#ifdef __has_builtin
# if __has_builtin(__builtin_unreachable)
__builtin_unreachable();
# endif
#endif
throw std::logic_error{this_function};
}
};
inline bool operator==(const discard_comments&, const discard_comments&) noexcept {return true;}
@@ -21,22 +21,7 @@ namespace toml
namespace detail
{
// TODO: find more sophisticated way to handle this
#if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE)
inline std::tm localtime_s(const std::time_t* src)
{
std::tm dst;
const auto result = ::localtime_r(src, &dst);
if (!result) { throw std::runtime_error("localtime_r failed."); }
return dst;
}
inline std::tm gmtime_s(const std::time_t* src)
{
std::tm dst;
const auto result = ::gmtime_r(src, &dst);
if (!result) { throw std::runtime_error("gmtime_r failed."); }
return dst;
}
#elif defined(_MSC_VER)
#if defined(_MSC_VER)
inline std::tm localtime_s(const std::time_t* src)
{
std::tm dst;
@@ -51,6 +36,21 @@ inline std::tm gmtime_s(const std::time_t* src)
if (result) { throw std::runtime_error("gmtime_s failed."); }
return dst;
}
#elif (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE)
inline std::tm localtime_s(const std::time_t* src)
{
std::tm dst;
const auto result = ::localtime_r(src, &dst);
if (!result) { throw std::runtime_error("localtime_r failed."); }
return dst;
}
inline std::tm gmtime_s(const std::time_t* src)
{
std::tm dst;
const auto result = ::gmtime_r(src, &dst);
if (!result) { throw std::runtime_error("gmtime_r failed."); }
return dst;
}
#else // fallback. not threadsafe
inline std::tm localtime_s(const std::time_t* src)
{
@@ -85,9 +85,9 @@ enum class month_t : std::uint8_t
struct local_date
{
std::int16_t year; // A.D. (like, 2018)
std::uint8_t month; // [0, 11]
std::uint8_t day; // [1, 31]
std::int16_t year{}; // A.D. (like, 2018)
std::uint8_t month{}; // [0, 11]
std::uint8_t day{}; // [1, 31]
local_date(int y, month_t m, int d)
: year (static_cast<std::int16_t>(y)),
@@ -181,12 +181,12 @@ operator<<(std::basic_ostream<charT, traits>& os, const local_date& date)
struct local_time
{
std::uint8_t hour; // [0, 23]
std::uint8_t minute; // [0, 59]
std::uint8_t second; // [0, 60]
std::uint16_t millisecond; // [0, 999]
std::uint16_t microsecond; // [0, 999]
std::uint16_t nanosecond; // [0, 999]
std::uint8_t hour{}; // [0, 23]
std::uint8_t minute{}; // [0, 59]
std::uint8_t second{}; // [0, 60]
std::uint16_t millisecond{}; // [0, 999]
std::uint16_t microsecond{}; // [0, 999]
std::uint16_t nanosecond{}; // [0, 999]
local_time(int h, int m, int s,
int ms = 0, int us = 0, int ns = 0)
@@ -297,8 +297,8 @@ operator<<(std::basic_ostream<charT, traits>& os, const local_time& time)
struct time_offset
{
std::int8_t hour; // [-12, 12]
std::int8_t minute; // [-59, 59]
std::int8_t hour{}; // [-12, 12]
std::int8_t minute{}; // [-59, 59]
time_offset(int h, int m)
: hour (static_cast<std::int8_t>(h)),
@@ -364,8 +364,8 @@ operator<<(std::basic_ostream<charT, traits>& os, const time_offset& offset)
struct local_datetime
{
local_date date;
local_time time;
local_date date{};
local_time time{};
local_datetime(local_date d, local_time t): date(d), time(t) {}
@@ -478,9 +478,9 @@ operator<<(std::basic_ostream<charT, traits>& os, const local_datetime& dt)
struct offset_datetime
{
local_date date;
local_time time;
time_offset offset;
local_date date{};
local_time time{};
time_offset offset{};
offset_datetime(local_date d, local_time t, time_offset o)
: date(d), time(t), offset(o)
@@ -2,14 +2,32 @@
// Distributed under the MIT License.
#ifndef TOML11_EXCEPTION_HPP
#define TOML11_EXCEPTION_HPP
#include <stdexcept>
#include <array>
#include <string>
#include <stdexcept>
#include <cstring>
#include "source_location.hpp"
namespace toml
{
struct file_io_error : public std::runtime_error
{
public:
file_io_error(int errnum, const std::string& msg, const std::string& fname)
: std::runtime_error(msg + " \"" + fname + "\": errno = " + std::to_string(errnum)),
errno_(errnum)
{}
int get_errno() const noexcept {return errno_;}
private:
int errno_;
};
struct exception : public std::exception
{
public:
@@ -2,7 +2,6 @@
// Distributed under the MIT License.
#ifndef TOML11_FROM_HPP
#define TOML11_FROM_HPP
#include "traits.hpp"
namespace toml
{
@@ -140,7 +140,7 @@ get(basic_value<C, M, V>&& v)
// ============================================================================
// std::string_view
#if __cplusplus >= 201703L
#if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
inline detail::enable_if_t<std::is_same<T, std::string_view>::value, std::string_view>
@@ -215,6 +215,7 @@ template<typename T, typename C,
detail::enable_if_t<detail::conjunction<
detail::is_container<T>, // T is a container
detail::negation<detail::has_push_back_method<T>>, // w/o push_back(...)
detail::negation<detail::has_specialized_from<T>>, // T does not have special conversion
detail::negation< // not toml::array
detail::is_exact_toml_type<T, basic_value<C, M, V>>>
>::value, T>
@@ -255,19 +256,37 @@ get(const basic_value<C, M, V>&);
// toml::from<T>::from_toml(v)
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
std::size_t S = sizeof(::toml::from<T>)>
T get(const basic_value<C, M, V>&);
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::has_specialized_from<T>::value, T>
get(const basic_value<C, M, V>&);
// T(const toml::value&) and T is not toml::basic_value
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::has_specialized_from<T>::value, T>
get(basic_value<C, M, V>&);
// T(const toml::value&) and T is not toml::basic_value,
// and it does not have `from<T>` nor `from_toml`.
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction<
detail::negation<detail::is_basic_value<T>>,
std::is_constructible<T, const basic_value<C, M, V>&>
std::is_constructible<T, const basic_value<C, M, V>&>,
detail::negation<detail::has_from_toml_method<T, C, M, V>>,
detail::negation<detail::has_specialized_from<T>>
>::value, T>
get(const basic_value<C, M, V>&);
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction<
detail::negation<detail::is_basic_value<T>>,
std::is_constructible<T, basic_value<C, M, V>&>,
detail::negation<detail::has_from_toml_method<T, C, M, V>>,
detail::negation<detail::has_specialized_from<T>>
>::value, T>
get(basic_value<C, M, V>&);
// ============================================================================
// array-like types; most likely STL container, like std::vector, etc.
@@ -321,6 +340,7 @@ template<typename T, typename C,
detail::enable_if_t<detail::conjunction<
detail::is_container<T>, // T is a container
detail::negation<detail::has_push_back_method<T>>, // w/o push_back
detail::negation<detail::has_specialized_from<T>>, // T does not have special conversion
detail::negation< // T is not toml::array
detail::is_exact_toml_type<T, basic_value<C, M, V>>>
>::value, T>
@@ -338,8 +358,10 @@ get(const basic_value<C, M, V>& v)
{v.location(), "here"}
}));
}
std::transform(ar.cbegin(), ar.cend(), container.begin(),
[](const value& x){return ::toml::get<value_type>(x);});
for(std::size_t i=0; i<ar.size(); ++i)
{
container[i] = ::toml::get<value_type>(ar[i]);
}
return container;
}
@@ -438,9 +460,16 @@ get(const basic_value<C, M, V>& v)
return ud;
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V,
std::size_t>
T get(const basic_value<C, M, V>& v)
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::has_specialized_from<T>::value, T>
get(const basic_value<C, M, V>& v)
{
return ::toml::from<T>::from_toml(v);
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::has_specialized_from<T>::value, T>
get(basic_value<C, M, V>& v)
{
return ::toml::from<T>::from_toml(v);
}
@@ -448,14 +477,29 @@ T get(const basic_value<C, M, V>& v)
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction<
detail::negation<detail::is_basic_value<T>>,
std::is_constructible<T, const basic_value<C, M, V>&>
detail::negation<detail::is_basic_value<T>>, // T is not a toml::value
std::is_constructible<T, const basic_value<C, M, V>&>, // T is constructible from toml::value
detail::negation<detail::has_from_toml_method<T, C, M, V>>, // and T does not have T.from_toml(v);
detail::negation<detail::has_specialized_from<T>> // and T does not have toml::from<T>{};
>::value, T>
get(const basic_value<C, M, V>& v)
{
return T(v);
}
template<typename T, typename C,
template<typename ...> class M, template<typename ...> class V>
detail::enable_if_t<detail::conjunction<
detail::negation<detail::is_basic_value<T>>, // T is not a toml::value
std::is_constructible<T, basic_value<C, M, V>&>, // T is constructible from toml::value
detail::negation<detail::has_from_toml_method<T, C, M, V>>, // and T does not have T.from_toml(v);
detail::negation<detail::has_specialized_from<T>> // and T does not have toml::from<T>{};
>::value, T>
get(basic_value<C, M, V>& v)
{
return T(v);
}
// ============================================================================
// find
@@ -1031,6 +1075,50 @@ find_or(const basic_value<C, M, V>& v, const toml::key& ky, T&& opt)
return get_or(tab.at(ky), std::forward<T>(opt));
}
// ---------------------------------------------------------------------------
// recursive find-or with type deduction (find_or(value, keys, opt))
template<typename Value, typename ... Ks,
typename detail::enable_if_t<(sizeof...(Ks) > 1), std::nullptr_t> = nullptr>
// here we need to add SFINAE in the template parameter to avoid
// infinite recursion in type deduction on gcc
auto find_or(Value&& v, const toml::key& ky, Ks&& ... keys)
-> decltype(find_or(std::forward<Value>(v), ky, detail::last_one(std::forward<Ks>(keys)...)))
{
if(!v.is_table())
{
return detail::last_one(std::forward<Ks>(keys)...);
}
auto&& tab = std::forward<Value>(v).as_table();
if(tab.count(ky) == 0)
{
return detail::last_one(std::forward<Ks>(keys)...);
}
return find_or(std::forward<decltype(tab)>(tab).at(ky), std::forward<Ks>(keys)...);
}
// ---------------------------------------------------------------------------
// recursive find_or with explicit type specialization, find_or<int>(value, keys...)
template<typename T, typename Value, typename ... Ks,
typename detail::enable_if_t<(sizeof...(Ks) > 1), std::nullptr_t> = nullptr>
// here we need to add SFINAE in the template parameter to avoid
// infinite recursion in type deduction on gcc
auto find_or(Value&& v, const toml::key& ky, Ks&& ... keys)
-> decltype(find_or<T>(std::forward<Value>(v), ky, detail::last_one(std::forward<Ks>(keys)...)))
{
if(!v.is_table())
{
return detail::last_one(std::forward<Ks>(keys)...);
}
auto&& tab = std::forward<Value>(v).as_table();
if(tab.count(ky) == 0)
{
return detail::last_one(std::forward<Ks>(keys)...);
}
return find_or(std::forward<decltype(tab)>(tab).at(ky), std::forward<Ks>(keys)...);
}
// ============================================================================
// expect
@@ -2,7 +2,6 @@
// Distributed under the MIT License.
#ifndef TOML11_INTO_HPP
#define TOML11_INTO_HPP
#include "traits.hpp"
namespace toml
{
@@ -5,7 +5,6 @@
#include <istream>
#include <sstream>
#include <stdexcept>
#include <fstream>
#include "combinator.hpp"
@@ -119,8 +118,8 @@ using lex_local_time = lex_partial_time;
// ===========================================================================
using lex_quotation_mark = character<'"'>;
using lex_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09 (tab)
in_range<0x0a, 0x1F>, // is allowed
using lex_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09 (tab) is allowed
in_range<0x0A, 0x1F>,
character<0x22>, character<0x5C>,
character<0x7F>>>;
@@ -133,6 +132,9 @@ using lex_escape_seq_char = either<character<'"'>, character<'\\'>,
character<'b'>, character<'f'>,
character<'n'>, character<'r'>,
character<'t'>,
#ifdef TOML11_USE_UNRELEASED_TOML_FEATURES
character<'e'>, // ESC (0x1B)
#endif
lex_escape_unicode_short,
lex_escape_unicode_long
>;
@@ -166,7 +168,7 @@ using lex_basic_string = sequence<lex_quotation_mark,
// | ^- expected newline, but got '"'.
// ```
// As a quick workaround for this problem, `lex_ml_basic_string_delim` was
// splitted into two, `lex_ml_basic_string_open` and `lex_ml_basic_string_close`.
// split into two, `lex_ml_basic_string_open` and `lex_ml_basic_string_close`.
// `lex_ml_basic_string_open` allows only `"""`. `_close` allows 3-5 `"`s.
// In parse_ml_basic_string() function, the trailing `"`s will be attached to
// the string body.
@@ -178,8 +180,8 @@ using lex_ml_basic_string_close = sequence<
maybe<lex_quotation_mark>, maybe<lex_quotation_mark>
>;
using lex_ml_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09
in_range<0x0a, 0x1F>, // is tab
using lex_ml_basic_unescaped = exclude<either<in_range<0x00, 0x08>, // 0x09 is tab
in_range<0x0A, 0x1F>,
character<0x5C>, // backslash
character<0x7F>, // DEL
lex_ml_basic_string_delim>>;
@@ -196,8 +198,8 @@ using lex_ml_basic_string = sequence<lex_ml_basic_string_open,
lex_ml_basic_body,
lex_ml_basic_string_close>;
using lex_literal_char = exclude<either<in_range<0x00, 0x08>,
in_range<0x10, 0x19>, character<0x27>>>;
using lex_literal_char = exclude<either<in_range<0x00, 0x08>, in_range<0x0A, 0x1F>,
character<0x7F>, character<0x27>>>;
using lex_apostrophe = character<'\''>;
using lex_literal_string = sequence<lex_apostrophe,
repeat<lex_literal_char, unlimited>,
@@ -212,7 +214,7 @@ using lex_ml_literal_string_close = sequence<
>;
using lex_ml_literal_char = exclude<either<in_range<0x00, 0x08>,
in_range<0x10, 0x1F>,
in_range<0x0A, 0x1F>,
character<0x7F>,
lex_ml_literal_string_delim>>;
using lex_ml_literal_body = repeat<either<lex_ml_literal_char, lex_newline>,
@@ -225,12 +227,6 @@ using lex_string = either<lex_ml_basic_string, lex_basic_string,
lex_ml_literal_string, lex_literal_string>;
// ===========================================================================
using lex_comment_start_symbol = character<'#'>;
using lex_non_eol = either<character<'\t'>, exclude<in_range<0x00, 0x19>>>;
using lex_comment = sequence<lex_comment_start_symbol,
repeat<lex_non_eol, unlimited>>;
using lex_dot_sep = sequence<maybe<lex_ws>, character<'.'>, maybe<lex_ws>>;
using lex_unquoted_key = repeat<either<lex_alpha, lex_digit,
@@ -265,6 +261,34 @@ using lex_array_table = sequence<lex_array_table_open,
maybe<lex_ws>,
lex_array_table_close>;
using lex_utf8_1byte = in_range<0x00, 0x7F>;
using lex_utf8_2byte = sequence<
in_range<'\xC2', '\xDF'>,
in_range<'\x80', '\xBF'>
>;
using lex_utf8_3byte = sequence<either<
sequence<character<'\xE0'>, in_range<'\xA0', '\xBF'>>,
sequence<in_range<'\xE1', '\xEC'>, in_range<'\x80', '\xBF'>>,
sequence<character<'\xED'>, in_range<'\x80', '\x9F'>>,
sequence<in_range<'\xEE', '\xEF'>, in_range<'\x80', '\xBF'>>
>, in_range<'\x80', '\xBF'>>;
using lex_utf8_4byte = sequence<either<
sequence<character<'\xF0'>, in_range<'\x90', '\xBF'>>,
sequence<in_range<'\xF1', '\xF3'>, in_range<'\x80', '\xBF'>>,
sequence<character<'\xF4'>, in_range<'\x80', '\x8F'>>
>, in_range<'\x80', '\xBF'>, in_range<'\x80', '\xBF'>>;
using lex_utf8_code = either<
lex_utf8_1byte,
lex_utf8_2byte,
lex_utf8_3byte,
lex_utf8_4byte
>;
using lex_comment_start_symbol = character<'#'>;
using lex_non_eol_ascii = either<character<0x09>, in_range<0x20, 0x7E>>;
using lex_comment = sequence<lex_comment_start_symbol, repeat<either<
lex_non_eol_ascii, lex_utf8_2byte, lex_utf8_3byte, lex_utf8_4byte>, unlimited>>;
} // detail
} // toml
#endif // TOML_LEXER_HPP
@@ -12,8 +12,11 @@ inline namespace toml_literals
{
// implementation
inline ::toml::value literal_internal_impl(::toml::detail::location loc)
inline ::toml::basic_value<TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>
literal_internal_impl(::toml::detail::location loc)
{
using value_type = ::toml::basic_value<
TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>;
// if there are some comments or empty lines, skip them.
using skip_line = ::toml::detail::repeat<toml::detail::sequence<
::toml::detail::maybe<::toml::detail::lex_ws>,
@@ -50,7 +53,7 @@ inline ::toml::value literal_internal_impl(::toml::detail::location loc)
// If it is neither a table-key or a array-of-table-key, it may be a value.
if(!is_table_key && !is_aots_key)
{
if(auto data = ::toml::detail::parse_value<::toml::value>(loc))
if(auto data = ::toml::detail::parse_value<value_type>(loc, 0))
{
return data.unwrap();
}
@@ -67,7 +70,7 @@ inline ::toml::value literal_internal_impl(::toml::detail::location loc)
// It is a valid toml file.
// It should be parsed as if we parse a file with this content.
if(auto data = ::toml::detail::parse_toml_file<::toml::value>(loc))
if(auto data = ::toml::detail::parse_toml_file<value_type>(loc))
{
return data.unwrap();
}
@@ -78,11 +81,13 @@ inline ::toml::value literal_internal_impl(::toml::detail::location loc)
}
inline ::toml::value operator"" _toml(const char* str, std::size_t len)
inline ::toml::basic_value<TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>
operator"" _toml(const char* str, std::size_t len)
{
::toml::detail::location loc(
std::string("TOML literal encoded in a C++ code"),
std::vector<char>(str, str + len));
// literal length does not include the null character at the end.
return literal_internal_impl(std::move(loc));
}
@@ -91,7 +96,8 @@ inline ::toml::value operator"" _toml(const char* str, std::size_t len)
#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
// value of u8"" literal has been changed from char to char8_t and char8_t is
// NOT compatible to char
inline ::toml::value operator"" _toml(const char8_t* str, std::size_t len)
inline ::toml::basic_value<TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>
operator"" _toml(const char8_t* str, std::size_t len)
{
::toml::detail::location loc(
std::string("TOML literal encoded in a C++ code"),
+121
View File
@@ -0,0 +1,121 @@
#ifndef TOML11_MACROS_HPP
#define TOML11_MACROS_HPP
#define TOML11_STRINGIZE_AUX(x) #x
#define TOML11_STRINGIZE(x) TOML11_STRINGIZE_AUX(x)
#define TOML11_CONCATENATE_AUX(x, y) x##y
#define TOML11_CONCATENATE(x, y) TOML11_CONCATENATE_AUX(x, y)
// ============================================================================
// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE
#ifndef TOML11_WITHOUT_DEFINE_NON_INTRUSIVE
// ----------------------------------------------------------------------------
// TOML11_ARGS_SIZE
#define TOML11_INDEX_RSEQ() \
32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, \
16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
#define TOML11_ARGS_SIZE_IMPL(\
ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8, ARG9, ARG10, \
ARG11, ARG12, ARG13, ARG14, ARG15, ARG16, ARG17, ARG18, ARG19, ARG20, \
ARG21, ARG22, ARG23, ARG24, ARG25, ARG26, ARG27, ARG28, ARG29, ARG30, \
ARG31, ARG32, N, ...) N
#define TOML11_ARGS_SIZE_AUX(...) TOML11_ARGS_SIZE_IMPL(__VA_ARGS__)
#define TOML11_ARGS_SIZE(...) TOML11_ARGS_SIZE_AUX(__VA_ARGS__, TOML11_INDEX_RSEQ())
// ----------------------------------------------------------------------------
// TOML11_FOR_EACH_VA_ARGS
#define TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, ARG1 ) FUNCTOR(ARG1)
#define TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS_AUX_32(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, __VA_ARGS__)
#define TOML11_FOR_EACH_VA_ARGS(FUNCTOR, ...)\
TOML11_CONCATENATE(TOML11_FOR_EACH_VA_ARGS_AUX_, TOML11_ARGS_SIZE(__VA_ARGS__))(FUNCTOR, __VA_ARGS__)
// ----------------------------------------------------------------------------
// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE
// use it in the following way.
// ```cpp
// namespace foo
// {
// struct Foo
// {
// std::string s;
// double d;
// int i;
// };
// } // foo
//
// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(foo::Foo, s, d, i)
// ```
// And then you can use `toml::find<foo::Foo>(file, "foo");`
//
#define TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE(VAR_NAME)\
obj.VAR_NAME = toml::find<decltype(obj.VAR_NAME)>(v, TOML11_STRINGIZE(VAR_NAME));
#define TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE(VAR_NAME)\
v[TOML11_STRINGIZE(VAR_NAME)] = obj.VAR_NAME;
#define TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(NAME, ...)\
namespace toml { \
template<> \
struct from<NAME> \
{ \
template<typename C, template<typename ...> class T, \
template<typename ...> class A> \
static NAME from_toml(const basic_value<C, T, A>& v) \
{ \
NAME obj; \
TOML11_FOR_EACH_VA_ARGS(TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE, __VA_ARGS__) \
return obj; \
} \
}; \
template<> \
struct into<NAME> \
{ \
static value into_toml(const NAME& obj) \
{ \
::toml::value v = ::toml::table{}; \
TOML11_FOR_EACH_VA_ARGS(TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE, __VA_ARGS__) \
return v; \
} \
}; \
} /* toml */
#endif// TOML11_WITHOUT_DEFINE_NON_INTRUSIVE
#endif// TOML11_MACROS_HPP
File diff suppressed because it is too large Load Diff
@@ -70,16 +70,16 @@ struct region_base
struct location final : public region_base
{
using const_iterator = typename std::vector<char>::const_iterator;
using difference_type = typename const_iterator::difference_type;
using difference_type = typename std::iterator_traits<const_iterator>::difference_type;
using source_ptr = std::shared_ptr<const std::vector<char>>;
location(std::string name, std::vector<char> cont)
location(std::string source_name, std::vector<char> cont)
: source_(std::make_shared<std::vector<char>>(std::move(cont))),
line_number_(1), source_name_(std::move(name)), iter_(source_->cbegin())
line_number_(1), source_name_(std::move(source_name)), iter_(source_->cbegin())
{}
location(std::string name, const std::string& cont)
location(std::string source_name, const std::string& cont)
: source_(std::make_shared<std::vector<char>>(cont.begin(), cont.end())),
line_number_(1), source_name_(std::move(name)), iter_(source_->cbegin())
line_number_(1), source_name_(std::move(source_name)), iter_(source_->cbegin())
{}
location(const location&) = default;
@@ -92,7 +92,7 @@ struct location final : public region_base
char front() const noexcept override {return *iter_;}
// this const prohibits codes like `++(loc.iter())`.
const const_iterator iter() const noexcept {return iter_;}
std::add_const<const_iterator>::type iter() const noexcept {return iter_;}
const_iterator begin() const noexcept {return source_->cbegin();}
const_iterator end() const noexcept {return source_->cend();}
@@ -227,8 +227,7 @@ struct region final : public region_base
region& operator+=(const region& other)
{
// different regions cannot be concatenated
assert(this->begin() == other.begin() && this->end() == other.end() &&
this->last_ == other.first_);
assert(this->source_ == other.source_ && this->last_ == other.first_);
this->last_ = other.last_;
return *this;
@@ -343,9 +342,9 @@ struct region final : public region_base
}))
{
// unwrap the first '#' by std::next.
auto str = make_string(std::next(comment_found), iter);
if(str.back() == '\r') {str.pop_back();}
com.push_back(std::move(str));
auto s = make_string(std::next(comment_found), iter);
if(!s.empty() && s.back() == '\r') {s.pop_back();}
com.push_back(std::move(s));
}
else
{
@@ -396,9 +395,9 @@ struct region final : public region_base
}))
{
// unwrap the first '#' by std::next.
auto str = make_string(std::next(comment_found), this->line_end());
if(str.back() == '\r') {str.pop_back();}
com.push_back(std::move(str));
auto s = make_string(std::next(comment_found), this->line_end());
if(!s.empty() && s.back() == '\r') {s.pop_back();}
com.push_back(std::move(s));
}
}
}
@@ -2,10 +2,19 @@
// Distributed under the MIT License.
#ifndef TOML11_SERIALIZER_HPP
#define TOML11_SERIALIZER_HPP
#include <cmath>
#include <cstdio>
#include <limits>
#if defined(_WIN32)
#include <locale.h>
#elif defined(__APPLE__) || defined(__FreeBSD__)
#include <xlocale.h>
#elif defined(__linux__)
#include <locale.h>
#endif
#include "lexer.hpp"
#include "value.hpp"
@@ -26,19 +35,24 @@ namespace toml
// a `"` and escaping some special character is boring.
template<typename charT, typename traits, typename Alloc>
std::basic_string<charT, traits, Alloc>
format_key(const std::basic_string<charT, traits, Alloc>& key)
format_key(const std::basic_string<charT, traits, Alloc>& k)
{
if(k.empty())
{
return std::string("\"\"");
}
// check the key can be a bare (unquoted) key
detail::location loc(key, std::vector<char>(key.begin(), key.end()));
detail::location loc(k, std::vector<char>(k.begin(), k.end()));
detail::lex_unquoted_key::invoke(loc);
if(loc.iter() == loc.end())
{
return key; // all the tokens are consumed. the key is unquoted-key.
return k; // all the tokens are consumed. the key is unquoted-key.
}
//if it includes special characters, then format it in a "quoted" key.
std::basic_string<charT, traits, Alloc> serialized("\"");
for(const char c : key)
for(const char c : k)
{
switch(c)
{
@@ -49,7 +63,19 @@ format_key(const std::basic_string<charT, traits, Alloc>& key)
case '\f': {serialized += "\\f"; break;}
case '\n': {serialized += "\\n"; break;}
case '\r': {serialized += "\\r"; break;}
default : {serialized += c; break;}
default: {
if (c >= 0x00 && c < 0x20)
{
std::array<char, 7> buf;
std::snprintf(buf.data(), buf.size(), "\\u00%02x", static_cast<int>(c));
serialized += buf.data();
}
else
{
serialized += c;
}
break;
}
}
}
serialized += "\"";
@@ -60,9 +86,12 @@ template<typename charT, typename traits, typename Alloc>
std::basic_string<charT, traits, Alloc>
format_keys(const std::vector<std::basic_string<charT, traits, Alloc>>& keys)
{
std::basic_string<charT, traits, Alloc> serialized;
if(keys.empty()) {return serialized;}
if(keys.empty())
{
return std::string("\"\"");
}
std::basic_string<charT, traits, Alloc> serialized;
for(const auto& ky : keys)
{
serialized += format_key(ky);
@@ -97,8 +126,10 @@ struct serializer
const int float_prec = std::numeric_limits<toml::floating>::max_digits10,
const bool can_be_inlined = false,
const bool no_comment = false,
std::vector<toml::key> ks = {})
std::vector<toml::key> ks = {},
const bool value_has_comment = false)
: can_be_inlined_(can_be_inlined), no_comment_(no_comment),
value_has_comment_(value_has_comment && !no_comment),
float_prec_(float_prec), width_(w), keys_(std::move(ks))
{}
~serializer() = default;
@@ -109,18 +140,93 @@ struct serializer
}
std::string operator()(const integer_type i) const
{
return std::to_string(i);
#if defined(_WIN32)
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
const std::string original_locale(setlocale(LC_NUMERIC, nullptr));
setlocale(LC_NUMERIC, "C");
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__)
const auto c_locale = newlocale(LC_NUMERIC_MASK, "C", locale_t(0));
locale_t original_locale(0);
if(c_locale != locale_t(0))
{
original_locale = uselocale(c_locale);
}
#endif
const auto str = std::to_string(i);
#if defined(_WIN32)
setlocale(LC_NUMERIC, original_locale.c_str());
_configthreadlocale(_DISABLE_PER_THREAD_LOCALE);
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__)
if(original_locale != locale_t(0))
{
uselocale(original_locale);
}
#endif
return str;
}
std::string operator()(const floating_type f) const
{
if(std::isnan(f))
{
if(std::signbit(f))
{
return std::string("-nan");
}
else
{
return std::string("nan");
}
}
else if(!std::isfinite(f))
{
if(std::signbit(f))
{
return std::string("-inf");
}
else
{
return std::string("inf");
}
}
// set locale to "C".
// To make it thread-local, we use OS-specific features.
// If we set process-global locale, it can break other thread that also
// outputs something simultaneously.
#if defined(_WIN32)
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
const std::string original_locale(setlocale(LC_NUMERIC, nullptr));
setlocale(LC_NUMERIC, "C");
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__)
const auto c_locale = newlocale(LC_NUMERIC_MASK, "C", locale_t(0));
locale_t original_locale(0);
if(c_locale != locale_t(0))
{
original_locale = uselocale(c_locale);
}
#endif
const auto fmt = "%.*g";
const auto bsz = std::snprintf(nullptr, 0, fmt, this->float_prec_, f);
// +1 for null character(\0)
std::vector<char> buf(static_cast<std::size_t>(bsz + 1), '\0');
std::snprintf(buf.data(), buf.size(), fmt, this->float_prec_, f);
// restore the original locale
#if defined(_WIN32)
setlocale(LC_NUMERIC, original_locale.c_str());
_configthreadlocale(_DISABLE_PER_THREAD_LOCALE);
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__linux__)
if(original_locale != locale_t(0))
{
uselocale(original_locale);
}
#endif
std::string token(buf.begin(), std::prev(buf.end()));
if(token.back() == '.') // 1. => 1.0
if(!token.empty() && token.back() == '.') // 1. => 1.0
{
token += '0';
}
@@ -144,8 +250,9 @@ struct serializer
{
if(s.kind == string_t::basic)
{
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() ||
std::find(s.str.cbegin(), s.str.cend(), '\"') != s.str.cend())
if((std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() ||
std::find(s.str.cbegin(), s.str.cend(), '\"') != s.str.cend()) &&
this->width_ != (std::numeric_limits<std::size_t>::max)())
{
// if linefeed or double-quote is contained,
// make it multiline basic string.
@@ -244,92 +351,18 @@ struct serializer
std::string operator()(const array_type& v) const
{
if(!v.empty() && v.front().is_table())// v is an array of tables
{
// if it's not inlined, we need to add `[[table.key]]`.
// but if it can be inlined,
// ```
// table.key = [
// {...},
// # comment
// {...},
// ]
// ```
if(this->can_be_inlined_)
{
std::string token;
if(!keys_.empty())
{
token += format_key(keys_.back());
token += " = ";
}
bool failed = false;
token += "[\n";
for(const auto& item : v)
{
// if an element of the table has a comment, the table
// cannot be inlined.
if(this->has_comment_inside(item.as_table()))
{
failed = true;
break;
}
if(!no_comment_)
{
for(const auto& c : item.comments())
{
token += '#';
token += c;
token += '\n';
}
}
const auto t = this->make_inline_table(item.as_table());
if(t.size() + 1 > width_ || // +1 for the last comma {...},
std::find(t.cbegin(), t.cend(), '\n') != t.cend())
{
failed = true;
break;
}
token += t;
token += ",\n";
}
if(!failed)
{
token += "]\n";
return token;
}
// if failed, serialize them as [[array.of.tables]].
}
std::string token;
for(const auto& item : v)
{
if(!no_comment_)
{
for(const auto& c : item.comments())
{
token += '#';
token += c;
token += '\n';
}
}
token += "[[";
token += format_keys(keys_);
token += "]]\n";
token += this->make_multiline_table(item.as_table());
}
return token;
}
if(v.empty())
{
return std::string("[]");
}
if(this->is_array_of_tables(v))
{
return make_array_of_tables(v);
}
// not an array of tables. normal array.
// first, try to make it inline if none of the elements have a comment.
if(!this->has_comment_inside(v))
if( ! this->has_comment_inside(v))
{
const auto inl = this->make_inline_array(v);
if(inl.size() < this->width_ &&
@@ -350,7 +383,7 @@ struct serializer
token += "[\n";
for(const auto& item : v)
{
if(!item.comments().empty() && !no_comment_)
if( ! item.comments().empty() && !no_comment_)
{
// if comment exists, the element must be the only element in the line.
// e.g. the following is not allowed.
@@ -376,15 +409,25 @@ struct serializer
token += '\n';
}
token += toml::visit(*this, item);
if(token.back() == '\n') {token.pop_back();}
if(!token.empty() && token.back() == '\n') {token.pop_back();}
token += ",\n";
continue;
}
std::string next_elem;
next_elem += toml::visit(*this, item);
if(item.is_table())
{
serializer ser(*this);
ser.can_be_inlined_ = true;
ser.width_ = (std::numeric_limits<std::size_t>::max)();
next_elem += toml::visit(ser, item);
}
else
{
next_elem += toml::visit(*this, item);
}
// comma before newline.
if(next_elem.back() == '\n') {next_elem.pop_back();}
if(!next_elem.empty() && next_elem.back() == '\n') {next_elem.pop_back();}
// if current line does not exceeds the width limit, continue.
if(current_line.size() + next_elem.size() + 1 < this->width_)
@@ -411,7 +454,10 @@ struct serializer
}
if(!current_line.empty())
{
if(current_line.back() != '\n') {current_line += '\n';}
if(!current_line.empty() && current_line.back() != '\n')
{
current_line += '\n';
}
token += current_line;
}
token += "]\n";
@@ -467,7 +513,19 @@ struct serializer
case '\f': {retval += "\\f"; break;}
case '\n': {retval += "\\n"; break;}
case '\r': {retval += "\\r"; break;}
default : {retval += c; break;}
default :
{
if((0x00 <= c && c <= 0x08) || (0x0A <= c && c <= 0x1F) || c == 0x7F)
{
retval += "\\u00";
retval += char(48 + (c / 16));
retval += char((c % 16 < 10 ? 48 : 55) + (c % 16));
}
else
{
retval += c;
}
}
}
}
return retval;
@@ -501,7 +559,21 @@ struct serializer
}
break;
}
default: {retval += *i; break;}
default :
{
const auto c = *i;
if((0x00 <= c && c <= 0x08) || (0x0A <= c && c <= 0x1F) || c == 0x7F)
{
retval += "\\u00";
retval += char(48 + (c / 16));
retval += char((c % 16 < 10 ? 48 : 55) + (c % 16));
}
else
{
retval += c;
}
}
}
}
// Only 1 or 2 consecutive `"`s are allowed in multiline basic string.
@@ -557,8 +629,10 @@ struct serializer
for(const auto& item : v)
{
if(is_first) {is_first = false;} else {token += ',';}
token += visit(serializer((std::numeric_limits<std::size_t>::max)(),
this->float_prec_, true), item);
token += visit(serializer(
(std::numeric_limits<std::size_t>::max)(), this->float_prec_,
/* inlined */ true, /*no comment*/ false, /*keys*/ {},
/*has_comment*/ !item.comments().empty()), item);
}
token += ']';
return token;
@@ -577,8 +651,10 @@ struct serializer
if(is_first) {is_first = false;} else {token += ',';}
token += format_key(kv.first);
token += '=';
token += visit(serializer((std::numeric_limits<std::size_t>::max)(),
this->float_prec_, true), kv.second);
token += visit(serializer(
(std::numeric_limits<std::size_t>::max)(), this->float_prec_,
/* inlined */ true, /*no comment*/ false, /*keys*/ {},
/*has_comment*/ !kv.second.comments().empty()), kv.second);
}
token += '}';
return token;
@@ -588,8 +664,16 @@ struct serializer
{
std::string token;
// print non-table stuff first. because after printing [foo.bar], the
// remaining non-table values will be assigned into [foo.bar], not [foo]
// print non-table elements first.
// ```toml
// [foo] # a table we're writing now here
// key = "value" # <- non-table element, "key"
// # ...
// [foo.bar] # <- table element, "bar"
// ```
// because after printing [foo.bar], the remaining non-table values will
// be assigned into [foo.bar], not [foo]. Those values should be printed
// earlier.
for(const auto& kv : v)
{
if(kv.second.is_table() || is_array_of_tables(kv.second))
@@ -597,21 +681,16 @@ struct serializer
continue;
}
if(!kv.second.comments().empty() && !no_comment_)
{
for(const auto& c : kv.second.comments())
{
token += '#';
token += c;
token += '\n';
}
}
token += write_comments(kv.second);
const auto key_and_sep = format_key(kv.first) + " = ";
const auto residual_width = (this->width_ > key_and_sep.size()) ?
this->width_ - key_and_sep.size() : 0;
token += key_and_sep;
token += visit(serializer(residual_width, this->float_prec_, true),
kv.second);
token += visit(serializer(residual_width, this->float_prec_,
/*can be inlined*/ true, /*no comment*/ false, /*keys*/ {},
/*has_comment*/ !kv.second.comments().empty()), kv.second);
if(token.back() != '\n')
{
token += '\n';
@@ -637,45 +716,172 @@ struct serializer
ks.push_back(kv.first);
auto tmp = visit(serializer(this->width_, this->float_prec_,
!multiline_table_printed, this->no_comment_, ks),
kv.second);
!multiline_table_printed, this->no_comment_, ks,
/*has_comment*/ !kv.second.comments().empty()), kv.second);
// If it is the first time to print a multi-line table, it would be
// helpful to separate normal key-value pair and subtables by a
// newline.
// (this checks if the current key-value pair contains newlines.
// but it is not perfect because multi-line string can also contain
// a newline. in such a case, an empty line will be written) TODO
if((!multiline_table_printed) &&
std::find(tmp.cbegin(), tmp.cend(), '\n') != tmp.cend())
{
multiline_table_printed = true;
}
else
{
// still inline tables only.
tmp += '\n';
}
token += '\n'; // separate key-value pairs and subtables
if(!kv.second.comments().empty() && !no_comment_)
{
for(const auto& c : kv.second.comments())
token += write_comments(kv.second);
token += tmp;
// care about recursive tables (all tables in each level prints
// newline and there will be a full of newlines)
if(tmp.substr(tmp.size() - 2, 2) != "\n\n" &&
tmp.substr(tmp.size() - 4, 4) != "\r\n\r\n" )
{
token += '#';
token += c;
token += '\n';
}
}
token += tmp;
else
{
token += write_comments(kv.second);
token += tmp;
token += '\n';
}
}
return token;
}
std::string make_array_of_tables(const array_type& v) const
{
// if it's not inlined, we need to add `[[table.key]]`.
// but if it can be inlined, we can format it as the following.
// ```
// table.key = [
// {...},
// # comment
// {...},
// ]
// ```
// This function checks if inlinization is possible or not, and then
// format the array-of-tables in a proper way.
//
// Note about comments:
//
// If the array itself has a comment (value_has_comment_ == true), we
// should try to make it inline.
// ```toml
// # comment about array
// array = [
// # comment about table element
// {of = "table"}
// ]
// ```
// If it is formatted as a multiline table, the two comments becomes
// indistinguishable.
// ```toml
// # comment about array
// # comment about table element
// [[array]]
// of = "table"
// ```
// So we need to try to make it inline, and it force-inlines regardless
// of the line width limit.
// It may fail if the element of a table has comment. In that case,
// the array-of-tables will be formatted as a multiline table.
if(this->can_be_inlined_ || this->value_has_comment_)
{
std::string token;
if(!keys_.empty())
{
token += format_key(keys_.back());
token += " = ";
}
bool failed = false;
token += "[\n";
for(const auto& item : v)
{
// if an element of the table has a comment, the table
// cannot be inlined.
if(this->has_comment_inside(item.as_table()))
{
failed = true;
break;
}
// write comments for the table itself
token += write_comments(item);
const auto t = this->make_inline_table(item.as_table());
if(t.size() + 1 > width_ || // +1 for the last comma {...},
std::find(t.cbegin(), t.cend(), '\n') != t.cend())
{
// if the value itself has a comment, ignore the line width limit
if( ! this->value_has_comment_)
{
failed = true;
break;
}
}
token += t;
token += ",\n";
}
if( ! failed)
{
token += "]\n";
return token;
}
// if failed, serialize them as [[array.of.tables]].
}
std::string token;
for(const auto& item : v)
{
token += write_comments(item);
token += "[[";
token += format_keys(keys_);
token += "]]\n";
token += this->make_multiline_table(item.as_table());
}
return token;
}
std::string write_comments(const value_type& v) const
{
std::string retval;
if(this->no_comment_) {return retval;}
for(const auto& c : v.comments())
{
retval += '#';
retval += c;
retval += '\n';
}
return retval;
}
bool is_array_of_tables(const value_type& v) const
{
if(!v.is_array()) {return false;}
const auto& a = v.as_array();
return !a.empty() && a.front().is_table();
if(!v.is_array() || v.as_array().empty()) {return false;}
return is_array_of_tables(v.as_array());
}
bool is_array_of_tables(const array_type& v) const
{
// Since TOML v0.5.0, heterogeneous arrays are allowed. So we need to
// check all the element in an array to check if the array is an array
// of tables.
return std::all_of(v.begin(), v.end(), [](const value_type& elem) {
return elem.is_table();
});
}
private:
bool can_be_inlined_;
bool no_comment_;
bool value_has_comment_;
int float_prec_;
std::size_t width_;
std::vector<toml::key> keys_;
@@ -699,7 +905,7 @@ format(const basic_value<C, M, V>& v, std::size_t w = 80u,
oss << v.comments();
oss << '\n'; // to split the file comment from the first element
}
const auto serialized = visit(serializer<value_type>(w, fprec, no_comment, false), v);
const auto serialized = visit(serializer<value_type>(w, fprec, false, no_comment), v);
oss << serialized;
return oss.str();
}
@@ -720,7 +926,7 @@ template<typename charT, typename traits>
std::basic_ostream<charT, traits>&
nocomment(std::basic_ostream<charT, traits>& os)
{
// by default, it is zero. and by defalut, it shows comments.
// by default, it is zero. and by default, it shows comments.
os.iword(detail::comment_index(os)) = 1;
return os;
}
@@ -729,7 +935,7 @@ template<typename charT, typename traits>
std::basic_ostream<charT, traits>&
showcomment(std::basic_ostream<charT, traits>& os)
{
// by default, it is zero. and by defalut, it shows comments.
// by default, it is zero. and by default, it shows comments.
os.iword(detail::comment_index(os)) = 0;
return os;
}
@@ -746,7 +952,7 @@ operator<<(std::basic_ostream<charT, traits>& os, const basic_value<C, M, V>& v)
const int fprec = static_cast<int>(os.precision());
os.width(0);
// by defualt, iword is initialized byl 0. And by default, toml11 outputs
// by default, iword is initialized by 0. And by default, toml11 outputs
// comments. So `0` means showcomment. 1 means nocommnet.
const bool no_comment = (1 == os.iword(detail::comment_index(os)));
@@ -3,6 +3,7 @@
#ifndef TOML11_SOURCE_LOCATION_HPP
#define TOML11_SOURCE_LOCATION_HPP
#include <cstdint>
#include <sstream>
#include "region.hpp"
@@ -124,7 +125,7 @@ inline std::string format_underline(const std::string& message,
std::ostringstream retval;
if(colorize)
if(color::should_color() || colorize)
{
retval << color::colorize; // turn on ANSI color
}
@@ -136,12 +137,18 @@ inline std::string format_underline(const std::string& message,
// if it is "[error]", it removes that part from the message shown.
if(message.size() > 7 && message.substr(0, 7) == "[error]")
{
retval << color::bold << color::red << "[error]" << color::reset
retval
#ifndef TOML11_NO_ERROR_PREFIX
<< color::bold << color::red << "[error]" << color::reset
#endif
<< color::bold << message.substr(7) << color::reset << '\n';
}
else
{
retval << color::bold << color::red << "[error] " << color::reset
retval
#ifndef TOML11_NO_ERROR_PREFIX
<< color::bold << color::red << "[error] " << color::reset
#endif
<< color::bold << message << color::reset << '\n';
}
@@ -2,13 +2,17 @@
// Distributed under the MIT License.
#ifndef TOML11_STRING_HPP
#define TOML11_STRING_HPP
#include "version.hpp"
#include <cstdint>
#include <algorithm>
#include <string>
#if __cplusplus >= 201703L
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
#if __has_include(<string_view>)
#define TOML11_USING_STRING_VIEW 1
#include <string_view>
#endif
#endif
@@ -53,7 +57,7 @@ struct string
string& operator+=(const std::string& rhs) {str += rhs; return *this;}
string& operator+=(const string& rhs) {str += rhs.str; return *this;}
#if __cplusplus >= 201703L
#if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0
explicit string(std::string_view s): kind(string_t::basic), str(s){}
string(std::string_view s, string_t k): kind(k), str(s){}
@@ -2,6 +2,11 @@
// Distributed under the MIT License.
#ifndef TOML11_TRAITS_HPP
#define TOML11_TRAITS_HPP
#include "from.hpp"
#include "into.hpp"
#include "version.hpp"
#include <chrono>
#include <forward_list>
#include <string>
@@ -9,7 +14,7 @@
#include <type_traits>
#include <utility>
#if __cplusplus >= 201703L
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
#if __has_include(<string_view>)
#include <string_view>
#endif // has_include(<string_view>)
@@ -84,6 +89,22 @@ struct has_into_toml_method_impl
static std::false_type check(...);
};
struct has_specialized_from_impl
{
template<typename T>
static std::false_type check(...);
template<typename T, std::size_t S = sizeof(::toml::from<T>)>
static std::true_type check(::toml::from<T>*);
};
struct has_specialized_into_impl
{
template<typename T>
static std::false_type check(...);
template<typename T, std::size_t S = sizeof(::toml::into<T>)>
static std::true_type check(::toml::from<T>*);
};
/// Intel C++ compiler can not use decltype in parent class declaration, here
/// is a hack to work around it. https://stackoverflow.com/a/23953090/4692076
#ifdef __INTEL_COMPILER
@@ -114,6 +135,11 @@ template<typename T>
struct has_into_toml_method
: decltype(has_into_toml_method_impl::check<T>(nullptr)){};
template<typename T>
struct has_specialized_from : decltype(has_specialized_from_impl::check<T>(nullptr)){};
template<typename T>
struct has_specialized_into : decltype(has_specialized_into_impl::check<T>(nullptr)){};
#ifdef __INTEL_COMPILER
#undef decltype
#endif
@@ -121,7 +147,7 @@ struct has_into_toml_method
// ---------------------------------------------------------------------------
// C++17 and/or/not
#if __cplusplus >= 201703L
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
using std::conjunction;
using std::disjunction;
@@ -183,8 +209,10 @@ template<typename T>
struct is_container : conjunction<
negation<is_map<T>>, // not a map
negation<std::is_same<T, std::string>>, // not a std::string
#if __cplusplus >= 201703L
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L
#if __has_include(<string_view>)
negation<std::is_same<T, std::string_view>>, // not a std::string_view
#endif // has_include(<string_view>)
#endif
has_iterator<T>, // has T::iterator
has_value_type<T> // has T::value_type
@@ -206,7 +234,7 @@ struct is_basic_value<::toml::basic_value<C, M, V>>: std::true_type{};
// ---------------------------------------------------------------------------
// C++14 index_sequence
#if __cplusplus >= 201402L
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
using std::index_sequence;
using std::make_index_sequence;
@@ -236,12 +264,12 @@ struct index_sequence_maker<0>
template<std::size_t N>
using make_index_sequence = typename index_sequence_maker<N-1>::type;
#endif // __cplusplus >= 2014
#endif // cplusplus >= 2014
// ---------------------------------------------------------------------------
// C++14 enable_if_t
#if __cplusplus >= 201402L
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
using std::enable_if_t;
@@ -250,12 +278,12 @@ using std::enable_if_t;
template<bool B, typename T>
using enable_if_t = typename std::enable_if<B, T>::type;
#endif // __cplusplus >= 2014
#endif // cplusplus >= 2014
// ---------------------------------------------------------------------------
// return_type_of_t
#if __cplusplus >= 201703L
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201703L && defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable>=201703
template<typename F, typename ... Args>
using return_type_of_t = std::invoke_result_t<F, Args...>;
@@ -21,9 +21,14 @@ class basic_value;
using character = char;
using key = std::string;
#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ <= 4
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wshadow"
#endif
using boolean = bool;
using integer = std::int64_t;
using floating = double; // "float" is a keyward, cannot use it here.
using floating = double; // "float" is a keyword, cannot use it here.
// the following stuffs are structs defined here, so aliases are not needed.
// - string
// - offset_datetime
@@ -32,12 +37,26 @@ using floating = double; // "float" is a keyward, cannot use it here.
// - local_date
// - local_time
#if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic pop
#endif
// default toml::value and default array/table. these are defined after defining
// basic_value itself.
// using value = basic_value<discard_comments, std::unordered_map, std::vector>;
// using array = typename value::array_type;
// using table = typename value::table_type;
// to avoid warnings about `value_t::integer` is "shadowing" toml::integer in
// GCC -Wshadow=global.
#if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic push
# if 7 <= __GNUC__
# pragma GCC diagnostic ignored "-Wshadow=global"
# else // gcc-6 or older
# pragma GCC diagnostic ignored "-Wshadow"
# endif
#endif
enum class value_t : std::uint8_t
{
empty = 0,
@@ -52,6 +71,9 @@ enum class value_t : std::uint8_t
array = 9,
table = 10,
};
#if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic pop
#endif
template<typename charT, typename traits>
inline std::basic_ostream<charT, traits>&
@@ -147,4 +169,5 @@ template<typename T, typename V> struct is_exact_toml_type<T const volatile&, V>
} // detail
} // toml
#endif// TOML11_TYPES_H
@@ -7,8 +7,9 @@
#include <utility>
#include "traits.hpp"
#include "version.hpp"
#if __cplusplus >= 201402L
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
# define TOML11_MARK_AS_DEPRECATED(msg) [[deprecated(msg)]]
#elif defined(__GNUC__)
# define TOML11_MARK_AS_DEPRECATED(msg) __attribute__((deprecated(msg)))
@@ -21,7 +22,7 @@
namespace toml
{
#if __cplusplus >= 201402L
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
using std::make_unique;
@@ -33,7 +34,7 @@ inline std::unique_ptr<T> make_unique(Ts&& ... args)
return std::unique_ptr<T>(new T(std::forward<Ts>(args)...));
}
#endif // __cplusplus >= 2014
#endif // TOML11_CPLUSPLUS_STANDARD_VERSION >= 2014
namespace detail
{
@@ -89,5 +90,61 @@ T from_string(const std::string& str, T opt)
return v;
}
namespace detail
{
#if TOML11_CPLUSPLUS_STANDARD_VERSION >= 201402L
template<typename T>
decltype(auto) last_one(T&& tail) noexcept
{
return std::forward<T>(tail);
}
template<typename T, typename ... Ts>
decltype(auto) last_one(T&& /*head*/, Ts&& ... tail) noexcept
{
return last_one(std::forward<Ts>(tail)...);
}
#else // C++11
// The following code
// ```cpp
// 1 | template<typename T, typename ... Ts>
// 2 | auto last_one(T&& /*head*/, Ts&& ... tail)
// 3 | -> decltype(last_one(std::forward<Ts>(tail)...))
// 4 | {
// 5 | return last_one(std::forward<Ts>(tail)...);
// 6 | }
// ```
// does not work because the function `last_one(...)` is not yet defined at
// line #3, so `decltype()` cannot deduce the type returned from `last_one`.
// So we need to determine return type in a different way, like a meta func.
template<typename T, typename ... Ts>
struct last_one_in_pack
{
using type = typename last_one_in_pack<Ts...>::type;
};
template<typename T>
struct last_one_in_pack<T>
{
using type = T;
};
template<typename ... Ts>
using last_one_in_pack_t = typename last_one_in_pack<Ts...>::type;
template<typename T>
T&& last_one(T&& tail) noexcept
{
return std::forward<T>(tail);
}
template<typename T, typename ... Ts>
enable_if_t<(sizeof...(Ts) > 0), last_one_in_pack_t<Ts&& ...>>
last_one(T&& /*head*/, Ts&& ... tail)
{
return last_one(std::forward<Ts>(tail)...);
}
#endif
} // detail
}// toml
#endif // TOML11_UTILITY
@@ -66,9 +66,20 @@ throw_key_not_found_error(const Value& v, const key& ky)
// ```
// It actually points to the top-level table at the first character,
// not `[table]`. But it is too confusing. To avoid the confusion, the error
// message should explicitly say "key not found in the top-level table".
// message should explicitly say "key not found in the top-level table",
// or "the parsed file is empty" if there is no content at all (0 bytes in file).
const auto loc = v.location();
if(loc.line() == 1 && loc.region() == 1)
if(loc.line() == 1 && loc.region() == 0)
{
// First line with a zero-length region means "empty file".
// The region will be generated at `parse_toml_file` function
// if the file contains no bytes.
throw std::out_of_range(format_underline(concat_to_string(
"key \"", ky, "\" not found in the top-level table"), {
{loc, "the parsed file is empty"}
}));
}
else if(loc.line() == 1 && loc.region() == 1)
{
// Here it assumes that top-level table starts at the first character.
// The region corresponds to the top-level table will be generated at
@@ -90,7 +101,7 @@ throw_key_not_found_error(const Value& v, const key& ky)
// ```toml
// a = {b = "c"}
// ```
// toml11 consideres the inline table body as the table region. Here,
// toml11 considers the inline table body as the table region. Here,
// `{b = "c"}` is the region of the table "a". The size of the region
// is 9, not 1. The shotest inline table still has two characters, `{`
// and `}`. The size cannot be 1.
@@ -99,7 +110,7 @@ throw_key_not_found_error(const Value& v, const key& ky)
// ```toml
// [a]
// ```
// toml11 consideres the whole table key as the table region. Here,
// toml11 considers the whole table key as the table region. Here,
// `[a]` is the table region. The size is 3, not 1.
//
throw std::out_of_range(format_underline(concat_to_string(
@@ -236,6 +247,7 @@ class basic_value
}
basic_value& operator=(const basic_value& v)
{
if(this == std::addressof(v)) {return *this;}
this->cleanup();
this->region_info_ = v.region_info_;
this->comments_ = v.comments_;
@@ -258,6 +270,7 @@ class basic_value
}
basic_value& operator=(basic_value&& v)
{
if(this == std::addressof(v)) {return *this;}
this->cleanup();
this->region_info_ = std::move(v.region_info_);
this->comments_ = std::move(v.comments_);
@@ -281,9 +294,9 @@ class basic_value
// overwrite comments ----------------------------------------------------
basic_value(const basic_value& v, std::vector<std::string> comments)
basic_value(const basic_value& v, std::vector<std::string> com)
: type_(v.type()), region_info_(v.region_info_),
comments_(std::move(comments))
comments_(std::move(com))
{
switch(v.type())
{
@@ -301,9 +314,9 @@ class basic_value
}
}
basic_value(basic_value&& v, std::vector<std::string> comments)
basic_value(basic_value&& v, std::vector<std::string> com)
: type_(v.type()), region_info_(std::move(v.region_info_)),
comments_(std::move(comments))
comments_(std::move(com))
{
switch(this->type_) // here this->type_ is already initialized
{
@@ -359,9 +372,9 @@ class basic_value
template<typename C,
template<typename ...> class T,
template<typename ...> class A>
basic_value(const basic_value<C, T, A>& v, std::vector<std::string> comments)
basic_value(const basic_value<C, T, A>& v, std::vector<std::string> com)
: type_(v.type()), region_info_(v.region_info_),
comments_(std::move(comments))
comments_(std::move(com))
{
switch(v.type())
{
@@ -443,10 +456,10 @@ class basic_value
assigner(this->boolean_, b);
return *this;
}
basic_value(boolean b, std::vector<std::string> comments)
basic_value(boolean b, std::vector<std::string> com)
: type_(value_t::boolean),
region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments))
comments_(std::move(com))
{
assigner(this->boolean_, b);
}
@@ -478,10 +491,10 @@ class basic_value
template<typename T, typename std::enable_if<detail::conjunction<
std::is_integral<T>, detail::negation<std::is_same<T, boolean>>>::value,
std::nullptr_t>::type = nullptr>
basic_value(T i, std::vector<std::string> comments)
basic_value(T i, std::vector<std::string> com)
: type_(value_t::integer),
region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments))
comments_(std::move(com))
{
assigner(this->integer_, static_cast<integer>(i));
}
@@ -511,10 +524,10 @@ class basic_value
template<typename T, typename std::enable_if<
std::is_floating_point<T>::value, std::nullptr_t>::type = nullptr>
basic_value(T f, std::vector<std::string> comments)
basic_value(T f, std::vector<std::string> com)
: type_(value_t::floating),
region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments))
comments_(std::move(com))
{
assigner(this->floating_, f);
}
@@ -535,10 +548,10 @@ class basic_value
assigner(this->string_, s);
return *this;
}
basic_value(toml::string s, std::vector<std::string> comments)
basic_value(toml::string s, std::vector<std::string> com)
: type_(value_t::string),
region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments))
comments_(std::move(com))
{
assigner(this->string_, std::move(s));
}
@@ -563,17 +576,17 @@ class basic_value
{
assigner(this->string_, toml::string(std::move(s), kind));
}
basic_value(std::string s, std::vector<std::string> comments)
basic_value(std::string s, std::vector<std::string> com)
: type_(value_t::string),
region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments))
comments_(std::move(com))
{
assigner(this->string_, toml::string(std::move(s)));
}
basic_value(std::string s, string_t kind, std::vector<std::string> comments)
basic_value(std::string s, string_t kind, std::vector<std::string> com)
: type_(value_t::string),
region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments))
comments_(std::move(com))
{
assigner(this->string_, toml::string(std::move(s), kind));
}
@@ -598,22 +611,22 @@ class basic_value
{
assigner(this->string_, toml::string(std::string(s), kind));
}
basic_value(const char* s, std::vector<std::string> comments)
basic_value(const char* s, std::vector<std::string> com)
: type_(value_t::string),
region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments))
comments_(std::move(com))
{
assigner(this->string_, toml::string(std::string(s)));
}
basic_value(const char* s, string_t kind, std::vector<std::string> comments)
basic_value(const char* s, string_t kind, std::vector<std::string> com)
: type_(value_t::string),
region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments))
comments_(std::move(com))
{
assigner(this->string_, toml::string(std::string(s), kind));
}
#if __cplusplus >= 201703L
#if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0
basic_value(std::string_view s)
: type_(value_t::string),
region_info_(std::make_shared<region_base>(region_base{}))
@@ -628,10 +641,10 @@ class basic_value
assigner(this->string_, toml::string(s));
return *this;
}
basic_value(std::string_view s, std::vector<std::string> comments)
basic_value(std::string_view s, std::vector<std::string> com)
: type_(value_t::string),
region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments))
comments_(std::move(com))
{
assigner(this->string_, toml::string(s));
}
@@ -641,10 +654,10 @@ class basic_value
{
assigner(this->string_, toml::string(s, kind));
}
basic_value(std::string_view s, string_t kind, std::vector<std::string> comments)
basic_value(std::string_view s, string_t kind, std::vector<std::string> com)
: type_(value_t::string),
region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments))
comments_(std::move(com))
{
assigner(this->string_, toml::string(s, kind));
}
@@ -666,10 +679,10 @@ class basic_value
assigner(this->local_date_, ld);
return *this;
}
basic_value(const local_date& ld, std::vector<std::string> comments)
basic_value(const local_date& ld, std::vector<std::string> com)
: type_(value_t::local_date),
region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments))
comments_(std::move(com))
{
assigner(this->local_date_, ld);
}
@@ -682,10 +695,10 @@ class basic_value
{
assigner(this->local_time_, lt);
}
basic_value(const local_time& lt, std::vector<std::string> comments)
basic_value(const local_time& lt, std::vector<std::string> com)
: type_(value_t::local_time),
region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments))
comments_(std::move(com))
{
assigner(this->local_time_, lt);
}
@@ -707,10 +720,10 @@ class basic_value
}
template<typename Rep, typename Period>
basic_value(const std::chrono::duration<Rep, Period>& dur,
std::vector<std::string> comments)
std::vector<std::string> com)
: type_(value_t::local_time),
region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments))
comments_(std::move(com))
{
assigner(this->local_time_, local_time(dur));
}
@@ -732,10 +745,10 @@ class basic_value
{
assigner(this->local_datetime_, ldt);
}
basic_value(const local_datetime& ldt, std::vector<std::string> comments)
basic_value(const local_datetime& ldt, std::vector<std::string> com)
: type_(value_t::local_datetime),
region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments))
comments_(std::move(com))
{
assigner(this->local_datetime_, ldt);
}
@@ -756,10 +769,10 @@ class basic_value
{
assigner(this->offset_datetime_, odt);
}
basic_value(const offset_datetime& odt, std::vector<std::string> comments)
basic_value(const offset_datetime& odt, std::vector<std::string> com)
: type_(value_t::offset_datetime),
region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments))
comments_(std::move(com))
{
assigner(this->offset_datetime_, odt);
}
@@ -778,10 +791,10 @@ class basic_value
assigner(this->offset_datetime_, offset_datetime(tp));
}
basic_value(const std::chrono::system_clock::time_point& tp,
std::vector<std::string> comments)
std::vector<std::string> com)
: type_(value_t::offset_datetime),
region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments))
comments_(std::move(com))
{
assigner(this->offset_datetime_, offset_datetime(tp));
}
@@ -802,10 +815,10 @@ class basic_value
{
assigner(this->array_, ary);
}
basic_value(const array_type& ary, std::vector<std::string> comments)
basic_value(const array_type& ary, std::vector<std::string> com)
: type_(value_t::array),
region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments))
comments_(std::move(com))
{
assigner(this->array_, ary);
}
@@ -833,10 +846,10 @@ class basic_value
template<typename T, typename std::enable_if<
std::is_convertible<T, value_type>::value,
std::nullptr_t>::type = nullptr>
basic_value(std::initializer_list<T> list, std::vector<std::string> comments)
basic_value(std::initializer_list<T> list, std::vector<std::string> com)
: type_(value_t::array),
region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments))
comments_(std::move(com))
{
array_type ary(list.begin(), list.end());
assigner(this->array_, std::move(ary));
@@ -876,10 +889,10 @@ class basic_value
detail::negation<std::is_same<T, array_type>>,
detail::is_container<T>
>::value, std::nullptr_t>::type = nullptr>
basic_value(const T& list, std::vector<std::string> comments)
basic_value(const T& list, std::vector<std::string> com)
: type_(value_t::array),
region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments))
comments_(std::move(com))
{
static_assert(std::is_convertible<typename T::value_type, value_type>::value,
"elements of a container should be convertible to toml::value");
@@ -915,10 +928,10 @@ class basic_value
{
assigner(this->table_, tab);
}
basic_value(const table_type& tab, std::vector<std::string> comments)
basic_value(const table_type& tab, std::vector<std::string> com)
: type_(value_t::table),
region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments))
comments_(std::move(com))
{
assigner(this->table_, tab);
}
@@ -943,10 +956,10 @@ class basic_value
}
basic_value(std::initializer_list<std::pair<key, basic_value>> list,
std::vector<std::string> comments)
std::vector<std::string> com)
: type_(value_t::table),
region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments))
comments_(std::move(com))
{
table_type tab;
for(const auto& elem : list) {tab[elem.first] = elem.second;}
@@ -982,10 +995,10 @@ class basic_value
detail::negation<std::is_same<Map, table_type>>,
detail::is_map<Map>
>::value, std::nullptr_t>::type = nullptr>
basic_value(const Map& mp, std::vector<std::string> comments)
basic_value(const Map& mp, std::vector<std::string> com)
: type_(value_t::table),
region_info_(std::make_shared<region_base>(region_base{})),
comments_(std::move(comments))
comments_(std::move(com))
{
table_type tab;
for(const auto& elem : mp) {tab[elem.first] = elem.second;}
@@ -1017,8 +1030,8 @@ class basic_value
template<typename T, typename std::enable_if<
detail::has_into_toml_method<T>::value, std::nullptr_t>::type = nullptr>
basic_value(const T& ud, std::vector<std::string> comments)
: basic_value(ud.into_toml(), std::move(comments))
basic_value(const T& ud, std::vector<std::string> com)
: basic_value(ud.into_toml(), std::move(com))
{}
template<typename T, typename std::enable_if<
detail::has_into_toml_method<T>::value, std::nullptr_t>::type = nullptr>
@@ -1033,8 +1046,8 @@ class basic_value
template<typename T, std::size_t S = sizeof(::toml::into<T>)>
basic_value(const T& ud): basic_value(::toml::into<T>::into_toml(ud)) {}
template<typename T, std::size_t S = sizeof(::toml::into<T>)>
basic_value(const T& ud, std::vector<std::string> comments)
: basic_value(::toml::into<T>::into_toml(ud), std::move(comments))
basic_value(const T& ud, std::vector<std::string> com)
: basic_value(::toml::into<T>::into_toml(ud), std::move(com))
{}
template<typename T, std::size_t S = sizeof(::toml::into<T>)>
basic_value& operator=(const T& ud)
@@ -1047,10 +1060,10 @@ class basic_value
//
// Those constructors take detail::region that contains parse result.
basic_value(boolean b, detail::region reg)
basic_value(boolean b, detail::region reg, std::vector<std::string> cm)
: type_(value_t::boolean),
region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(region_info_->comments())
comments_(std::move(cm))
{
assigner(this->boolean_, b);
}
@@ -1058,68 +1071,75 @@ class basic_value
detail::conjunction<
std::is_integral<T>, detail::negation<std::is_same<T, boolean>>
>::value, std::nullptr_t>::type = nullptr>
basic_value(T i, detail::region reg)
basic_value(T i, detail::region reg, std::vector<std::string> cm)
: type_(value_t::integer),
region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(region_info_->comments())
comments_(std::move(cm))
{
assigner(this->integer_, static_cast<integer>(i));
}
template<typename T, typename std::enable_if<
std::is_floating_point<T>::value, std::nullptr_t>::type = nullptr>
basic_value(T f, detail::region reg)
basic_value(T f, detail::region reg, std::vector<std::string> cm)
: type_(value_t::floating),
region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(region_info_->comments())
comments_(std::move(cm))
{
assigner(this->floating_, static_cast<floating>(f));
}
basic_value(toml::string s, detail::region reg)
basic_value(toml::string s, detail::region reg,
std::vector<std::string> cm)
: type_(value_t::string),
region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(region_info_->comments())
comments_(std::move(cm))
{
assigner(this->string_, std::move(s));
}
basic_value(const local_date& ld, detail::region reg)
basic_value(const local_date& ld, detail::region reg,
std::vector<std::string> cm)
: type_(value_t::local_date),
region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(region_info_->comments())
comments_(std::move(cm))
{
assigner(this->local_date_, ld);
}
basic_value(const local_time& lt, detail::region reg)
basic_value(const local_time& lt, detail::region reg,
std::vector<std::string> cm)
: type_(value_t::local_time),
region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(region_info_->comments())
comments_(std::move(cm))
{
assigner(this->local_time_, lt);
}
basic_value(const local_datetime& ldt, detail::region reg)
basic_value(const local_datetime& ldt, detail::region reg,
std::vector<std::string> cm)
: type_(value_t::local_datetime),
region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(region_info_->comments())
comments_(std::move(cm))
{
assigner(this->local_datetime_, ldt);
}
basic_value(const offset_datetime& odt, detail::region reg)
basic_value(const offset_datetime& odt, detail::region reg,
std::vector<std::string> cm)
: type_(value_t::offset_datetime),
region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(region_info_->comments())
comments_(std::move(cm))
{
assigner(this->offset_datetime_, odt);
}
basic_value(const array_type& ary, detail::region reg)
basic_value(const array_type& ary, detail::region reg,
std::vector<std::string> cm)
: type_(value_t::array),
region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(region_info_->comments())
comments_(std::move(cm))
{
assigner(this->array_, ary);
}
basic_value(const table_type& tab, detail::region reg)
basic_value(const table_type& tab, detail::region reg,
std::vector<std::string> cm)
: type_(value_t::table),
region_info_(std::make_shared<detail::region>(std::move(reg))),
comments_(region_info_->comments())
comments_(std::move(cm))
{
assigner(this->table_, tab);
}
@@ -1127,8 +1147,10 @@ class basic_value
template<typename T, typename std::enable_if<
detail::is_exact_toml_type<T, value_type>::value,
std::nullptr_t>::type = nullptr>
basic_value(std::pair<T, detail::region> parse_result)
: basic_value(std::move(parse_result.first), std::move(parse_result.second))
basic_value(std::pair<T, detail::region> parse_result, std::vector<std::string> com)
: basic_value(std::move(parse_result.first),
std::move(parse_result.second),
std::move(com))
{}
// type checking and casting ============================================
@@ -1692,9 +1714,9 @@ class basic_value
{
switch(this->type_)
{
case value_t::string : {string_.~string(); return;}
case value_t::array : {array_.~array_storage(); return;}
case value_t::table : {table_.~table_storage(); return;}
case value_t::string : {string_.~string(); return;}
case value_t::array : {array_.~array_storage(); return;}
case value_t::table : {table_.~table_storage(); return;}
default : return;
}
}
@@ -1730,7 +1752,8 @@ class basic_value
};
// default toml::value and default array/table.
using value = basic_value<discard_comments, std::unordered_map, std::vector>;
// TOML11_DEFAULT_COMMENT_STRATEGY is defined in comments.hpp
using value = basic_value<TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>;
using array = typename value::array_type;
using table = typename value::table_type;
+42
View File
@@ -0,0 +1,42 @@
#ifndef TOML11_VERSION_HPP
#define TOML11_VERSION_HPP
// This file checks C++ version.
#ifndef __cplusplus
# error "__cplusplus is not defined"
#endif
// Since MSVC does not define `__cplusplus` correctly unless you pass
// `/Zc:__cplusplus` when compiling, the workaround macros are added.
// Those enables you to define version manually or to use MSVC specific
// version macro automatically.
//
// The value of `__cplusplus` macro is defined in the C++ standard spec, but
// MSVC ignores the value, maybe because of backward compatibility. Instead,
// MSVC defines _MSVC_LANG that has the same value as __cplusplus defined in
// the C++ standard. First we check the manual version definition, and then
// we check if _MSVC_LANG is defined. If neither, use normal `__cplusplus`.
//
// FYI: https://docs.microsoft.com/en-us/cpp/build/reference/zc-cplusplus?view=msvc-170
// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170
//
#if defined(TOML11_ENFORCE_CXX11)
# define TOML11_CPLUSPLUS_STANDARD_VERSION 201103L
#elif defined(TOML11_ENFORCE_CXX14)
# define TOML11_CPLUSPLUS_STANDARD_VERSION 201402L
#elif defined(TOML11_ENFORCE_CXX17)
# define TOML11_CPLUSPLUS_STANDARD_VERSION 201703L
#elif defined(TOML11_ENFORCE_CXX20)
# define TOML11_CPLUSPLUS_STANDARD_VERSION 202002L
#elif defined(_MSVC_LANG) && defined(_MSC_VER) && 1910 <= _MSC_VER
# define TOML11_CPLUSPLUS_STANDARD_VERSION _MSVC_LANG
#else
# define TOML11_CPLUSPLUS_STANDARD_VERSION __cplusplus
#endif
#if TOML11_CPLUSPLUS_STANDARD_VERSION < 201103L && _MSC_VER < 1900
# error "toml11 requires C++11 or later."
#endif
#endif// TOML11_VERSION_HPP