commit
7b7b260379
@ -1,242 +0,0 @@
|
||||
#include "cmake.hpp"
|
||||
|
||||
#include "fs.hpp"
|
||||
#include <stdexcept>
|
||||
#include <toml.hpp>
|
||||
|
||||
namespace cmkr {
|
||||
namespace cmake {
|
||||
|
||||
namespace detail {
|
||||
std::vector<std::string> to_string_vec(
|
||||
const std::vector<toml::basic_value<toml::discard_comments, std::unordered_map, std::vector>>
|
||||
&vals) {
|
||||
std::vector<std::string> temp;
|
||||
for (const auto &val : vals)
|
||||
temp.push_back(val.as_string());
|
||||
return temp;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
CMake::CMake(const std::string &path, bool build) {
|
||||
if (!fs::exists(fs::path(path) / "cmake.toml")) {
|
||||
throw std::runtime_error("[cmkr] error: No cmake.toml was found!");
|
||||
}
|
||||
const auto toml = toml::parse((fs::path(path) / "cmake.toml").string());
|
||||
if (build) {
|
||||
if (toml.contains("cmake")) {
|
||||
const auto &cmake = toml::find(toml, "cmake");
|
||||
|
||||
if (cmake.contains("bin-dir")) {
|
||||
bin_dir = toml::find(cmake, "bin-dir").as_string();
|
||||
}
|
||||
|
||||
if (cmake.contains("generator")) {
|
||||
generator = toml::find(cmake, "generator").as_string();
|
||||
}
|
||||
|
||||
if (cmake.contains("config")) {
|
||||
config = toml::find(cmake, "config").as_string();
|
||||
}
|
||||
|
||||
if (cmake.contains("arguments")) {
|
||||
gen_args = detail::to_string_vec(toml::find(cmake, "arguments").as_array());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (toml.contains("cmake")) {
|
||||
const auto &cmake = toml::find(toml, "cmake");
|
||||
cmake_version = toml::find(cmake, "minimum").as_string();
|
||||
|
||||
if (cmake.contains("description")) {
|
||||
desc = toml::find(cmake, "description").as_string();
|
||||
}
|
||||
|
||||
if (cmake.contains("cpp-flags")) {
|
||||
cppflags = detail::to_string_vec(toml::find(cmake, "cpp-flags").as_array());
|
||||
}
|
||||
|
||||
if (cmake.contains("c-flags")) {
|
||||
cflags = detail::to_string_vec(toml::find(cmake, "c-flags").as_array());
|
||||
}
|
||||
|
||||
if (cmake.contains("link-flags")) {
|
||||
linkflags = detail::to_string_vec(toml::find(cmake, "link-flags").as_array());
|
||||
}
|
||||
|
||||
if (cmake.contains("subdirs")) {
|
||||
subdirs = detail::to_string_vec(toml::find(cmake, "subdirs").as_array());
|
||||
}
|
||||
}
|
||||
|
||||
if (toml.contains("project")) {
|
||||
const auto &project = toml::find(toml, "project");
|
||||
proj_name = toml::find(project, "name").as_string();
|
||||
proj_version = toml::find(project, "version").as_string();
|
||||
}
|
||||
|
||||
if (toml.contains("settings")) {
|
||||
using set_map =
|
||||
std::map<std::string, toml::basic_value<toml::discard_comments, std::unordered_map,
|
||||
std::vector>>;
|
||||
const auto &sets = toml::find<set_map>(toml, "settings");
|
||||
for (const auto set : sets) {
|
||||
Setting s;
|
||||
s.name = set.first;
|
||||
if (set.second.is_boolean()) {
|
||||
s.val = set.second.as_boolean();
|
||||
} else if (set.second.is_string()) {
|
||||
s.val = set.second.as_string();
|
||||
} else {
|
||||
if (set.second.contains("comment")) {
|
||||
s.comment = toml::find(set.second, "comment").as_string();
|
||||
}
|
||||
if (set.second.contains("value")) {
|
||||
auto v = toml::find(set.second, "value");
|
||||
if (v.is_boolean()) {
|
||||
s.val = v.as_boolean();
|
||||
} else {
|
||||
s.val = v.as_string();
|
||||
}
|
||||
}
|
||||
if (set.second.contains("cache")) {
|
||||
s.cache = toml::find(set.second, "cache").as_boolean();
|
||||
}
|
||||
if (set.second.contains("force")) {
|
||||
s.force = toml::find(set.second, "force").as_boolean();
|
||||
}
|
||||
}
|
||||
settings.push_back(s);
|
||||
}
|
||||
}
|
||||
|
||||
if (toml.contains("options")) {
|
||||
using opts_map =
|
||||
std::map<std::string, toml::basic_value<toml::discard_comments, std::unordered_map,
|
||||
std::vector>>;
|
||||
const auto &opts = toml::find<opts_map>(toml, "options");
|
||||
for (const auto opt : opts) {
|
||||
Option o;
|
||||
o.name = opt.first;
|
||||
if (opt.second.is_boolean()) {
|
||||
o.val = opt.second.as_boolean();
|
||||
} else {
|
||||
if (opt.second.contains("comment")) {
|
||||
o.comment = toml::find(opt.second, "comment").as_string();
|
||||
}
|
||||
if (opt.second.contains("value")) {
|
||||
o.val = toml::find(opt.second, "value").as_boolean();
|
||||
}
|
||||
}
|
||||
options.push_back(o);
|
||||
}
|
||||
}
|
||||
|
||||
if (toml.contains("find-package")) {
|
||||
using pkg_map =
|
||||
std::map<std::string, toml::basic_value<toml::discard_comments, std::unordered_map,
|
||||
std::vector>>;
|
||||
const auto &pkgs = toml::find<pkg_map>(toml, "find-package");
|
||||
for (const auto &pkg : pkgs) {
|
||||
Package p;
|
||||
p.name = pkg.first;
|
||||
if (pkg.second.is_string()) {
|
||||
p.version = pkg.second.as_string();
|
||||
} else {
|
||||
if (pkg.second.contains("version")) {
|
||||
p.version = toml::find(pkg.second, "version").as_string();
|
||||
}
|
||||
if (pkg.second.contains("required")) {
|
||||
p.required = toml::find(pkg.second, "required").as_boolean();
|
||||
}
|
||||
if (pkg.second.contains("components")) {
|
||||
p.components =
|
||||
detail::to_string_vec(toml::find(pkg.second, "components").as_array());
|
||||
}
|
||||
}
|
||||
packages.push_back(p);
|
||||
}
|
||||
}
|
||||
|
||||
if (toml.contains("fetch-content")) {
|
||||
using content_map = std::map<std::string, std::map<std::string, std::string>>;
|
||||
contents = toml::find<content_map>(toml, "fetch-content");
|
||||
}
|
||||
|
||||
if (toml.contains("bin")) {
|
||||
const auto &bins = toml::find(toml, "bin").as_array();
|
||||
|
||||
for (const auto &bin : bins) {
|
||||
Bin b;
|
||||
b.name = toml::find(bin, "name").as_string();
|
||||
b.type = toml::find(bin, "type").as_string();
|
||||
|
||||
b.sources = detail::to_string_vec(toml::find(bin, "sources").as_array());
|
||||
|
||||
if (bin.contains("include-dirs")) {
|
||||
b.include_dirs =
|
||||
detail::to_string_vec(toml::find(bin, "include-dirs").as_array());
|
||||
}
|
||||
|
||||
if (bin.contains("link-libs")) {
|
||||
b.link_libs = detail::to_string_vec(toml::find(bin, "link-libs").as_array());
|
||||
}
|
||||
|
||||
if (bin.contains("features")) {
|
||||
b.features = detail::to_string_vec(toml::find(bin, "features").as_array());
|
||||
}
|
||||
|
||||
if (bin.contains("definitions")) {
|
||||
b.defines = detail::to_string_vec(toml::find(bin, "definitions").as_array());
|
||||
}
|
||||
|
||||
if (bin.contains("alias")) {
|
||||
b.alias = toml::find(bin, "alias").as_string();
|
||||
}
|
||||
|
||||
if (bin.contains("properties")) {
|
||||
using prop_map = std::map<std::string, std::string>;
|
||||
b.properties = toml::find<prop_map>(bin, "properties");
|
||||
}
|
||||
|
||||
binaries.push_back(b);
|
||||
}
|
||||
}
|
||||
|
||||
if (toml.contains("test")) {
|
||||
const auto &ts = toml::find(toml, "test").as_array();
|
||||
for (const auto &t : ts) {
|
||||
Test test;
|
||||
test.name = toml::find(t, "name").as_string();
|
||||
test.cmd = toml::find(t, "type").as_string();
|
||||
if (t.contains("arguments")) {
|
||||
test.args = detail::to_string_vec(toml::find(t, "arguments").as_array());
|
||||
}
|
||||
tests.push_back(test);
|
||||
}
|
||||
}
|
||||
|
||||
if (toml.contains("install")) {
|
||||
const auto &ts = toml::find(toml, "install").as_array();
|
||||
for (const auto &t : ts) {
|
||||
Install inst;
|
||||
if (t.contains("targets")) {
|
||||
inst.targets = detail::to_string_vec(toml::find(t, "targets").as_array());
|
||||
}
|
||||
if (t.contains("files")) {
|
||||
inst.files = detail::to_string_vec(toml::find(t, "files").as_array());
|
||||
}
|
||||
if (t.contains("dirs")) {
|
||||
inst.dirs = detail::to_string_vec(toml::find(t, "dirs").as_array());
|
||||
}
|
||||
if (t.contains("configs")) {
|
||||
inst.configs = detail::to_string_vec(toml::find(t, "configs").as_array());
|
||||
}
|
||||
inst.destination = toml::find(t, "destination").as_string();
|
||||
installs.push_back(inst);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace cmake
|
||||
} // namespace cmkr
|
@ -1,84 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <mpark/variant.hpp>
|
||||
|
||||
namespace cmkr {
|
||||
namespace cmake {
|
||||
|
||||
struct Setting {
|
||||
std::string name;
|
||||
std::string comment;
|
||||
mpark::variant<bool, std::string> val;
|
||||
bool cache = false;
|
||||
bool force = false;
|
||||
};
|
||||
|
||||
struct Option {
|
||||
std::string name;
|
||||
std::string comment;
|
||||
bool val;
|
||||
};
|
||||
|
||||
struct Package {
|
||||
std::string name;
|
||||
std::string version;
|
||||
bool required = true;
|
||||
std::vector<std::string> components;
|
||||
};
|
||||
|
||||
struct Bin {
|
||||
std::string name;
|
||||
std::string type;
|
||||
std::vector<std::string> sources;
|
||||
std::vector<std::string> include_dirs;
|
||||
std::vector<std::string> features;
|
||||
std::vector<std::string> defines;
|
||||
std::vector<std::string> link_libs;
|
||||
std::string alias;
|
||||
std::map<std::string, std::string> properties;
|
||||
};
|
||||
|
||||
struct Test {
|
||||
std::string name;
|
||||
std::string cmd;
|
||||
std::vector<std::string> args;
|
||||
};
|
||||
|
||||
struct Install {
|
||||
std::vector<std::string> targets;
|
||||
std::vector<std::string> files;
|
||||
std::vector<std::string> dirs;
|
||||
std::vector<std::string> configs;
|
||||
std::string destination;
|
||||
};
|
||||
|
||||
struct CMake {
|
||||
std::string cmake_version = "3.15";
|
||||
std::string desc;
|
||||
std::string bin_dir = "bin";
|
||||
std::string generator;
|
||||
std::string config;
|
||||
std::vector<std::string> subdirs;
|
||||
std::vector<std::string> cppflags;
|
||||
std::vector<std::string> cflags;
|
||||
std::vector<std::string> linkflags;
|
||||
std::vector<std::string> gen_args;
|
||||
std::vector<std::string> build_args;
|
||||
std::string proj_name;
|
||||
std::string proj_version;
|
||||
std::vector<Setting> settings;
|
||||
std::vector<Option> options;
|
||||
std::vector<Package> packages;
|
||||
std::map<std::string, std::map<std::string, std::string>> contents;
|
||||
std::vector<Bin> binaries;
|
||||
std::vector<Test> tests;
|
||||
std::vector<Install> installs;
|
||||
CMake(const std::string &path, bool build);
|
||||
};
|
||||
|
||||
} // namespace cmake
|
||||
} // namespace cmkr
|
@ -0,0 +1,243 @@
|
||||
#include "cmake.hpp"
|
||||
|
||||
#include "enum_helper.hpp"
|
||||
#include "fs.hpp"
|
||||
#include <stdexcept>
|
||||
#include <toml.hpp>
|
||||
#include <tsl/ordered_map.h>
|
||||
|
||||
template <>
|
||||
const char *enumStrings<cmkr::cmake::TargetType>::data[] = {"executable", "library", "shared", "static", "interface", "custom"};
|
||||
|
||||
namespace cmkr {
|
||||
namespace cmake {
|
||||
|
||||
using TomlBasicValue = toml::basic_value<toml::preserve_comments, tsl::ordered_map, std::vector>;
|
||||
|
||||
template <typename EnumType>
|
||||
static EnumType to_enum(const std::string &str, const std::string &help_name) {
|
||||
EnumType value;
|
||||
try {
|
||||
std::stringstream ss(str);
|
||||
ss >> enumFromString(value);
|
||||
} catch (std::invalid_argument &) {
|
||||
std::string supported;
|
||||
for (const auto &s : enumStrings<EnumType>::data) {
|
||||
if (!supported.empty()) {
|
||||
supported += ", ";
|
||||
}
|
||||
supported += s;
|
||||
}
|
||||
throw std::runtime_error("Unknown " + help_name + "'" + str + "'! Supported types are: " + supported);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void get_optional(const TomlBasicValue &v, const toml::key &ky, T &destination) {
|
||||
if (v.contains(ky)) {
|
||||
destination = toml::find<T>(v, ky);
|
||||
}
|
||||
}
|
||||
|
||||
CMake::CMake(const std::string &path, bool build) {
|
||||
if (!fs::exists(fs::path(path) / "cmake.toml")) {
|
||||
throw std::runtime_error("No cmake.toml was found!");
|
||||
}
|
||||
const auto toml = toml::parse<toml::preserve_comments, tsl::ordered_map, std::vector>((fs::path(path) / "cmake.toml").string());
|
||||
if (build) {
|
||||
if (toml.contains("cmake")) {
|
||||
const auto &cmake = toml::find(toml, "cmake");
|
||||
|
||||
if (cmake.contains("bin-dir")) {
|
||||
throw std::runtime_error("bin-dir has been renamed to build-dir");
|
||||
}
|
||||
|
||||
get_optional(cmake, "build-dir", build_dir);
|
||||
get_optional(cmake, "generator", generator);
|
||||
get_optional(cmake, "config", config);
|
||||
get_optional(cmake, "arguments", gen_args);
|
||||
}
|
||||
} else {
|
||||
if (toml.contains("cmake")) {
|
||||
const auto &cmake = toml::find(toml, "cmake");
|
||||
cmake_version = toml::find(cmake, "version").as_string();
|
||||
get_optional(cmake, "cmkr-include", cmkr_include);
|
||||
get_optional(cmake, "cpp-flags", cppflags);
|
||||
get_optional(cmake, "c-flags", cflags);
|
||||
get_optional(cmake, "link-flags", linkflags);
|
||||
}
|
||||
|
||||
if (toml.contains("project")) {
|
||||
const auto &project = toml::find(toml, "project");
|
||||
project_name = toml::find(project, "name").as_string();
|
||||
get_optional(project, "version", project_version);
|
||||
get_optional(project, "description", project_description);
|
||||
get_optional(project, "languages", project_languages);
|
||||
get_optional(project, "cmake-before", cmake_before);
|
||||
get_optional(project, "cmake-after", cmake_after);
|
||||
get_optional(project, "include-before", include_before);
|
||||
get_optional(project, "include-after", include_after);
|
||||
get_optional(project, "subdirs", subdirs);
|
||||
}
|
||||
|
||||
if (toml.contains("settings")) {
|
||||
using set_map = std::map<std::string, TomlBasicValue>;
|
||||
const auto &sets = toml::find<set_map>(toml, "settings");
|
||||
for (const auto &set : sets) {
|
||||
Setting s;
|
||||
s.name = set.first;
|
||||
if (set.second.is_boolean()) {
|
||||
s.val = set.second.as_boolean();
|
||||
} else if (set.second.is_string()) {
|
||||
s.val = set.second.as_string();
|
||||
} else {
|
||||
get_optional(set.second, "comment", s.comment);
|
||||
if (set.second.contains("value")) {
|
||||
auto v = toml::find(set.second, "value");
|
||||
if (v.is_boolean()) {
|
||||
s.val = v.as_boolean();
|
||||
} else {
|
||||
s.val = v.as_string();
|
||||
}
|
||||
}
|
||||
get_optional(set.second, "cache", s.cache);
|
||||
get_optional(set.second, "force", s.force);
|
||||
}
|
||||
settings.push_back(s);
|
||||
}
|
||||
}
|
||||
|
||||
if (toml.contains("options")) {
|
||||
using opts_map = tsl::ordered_map<std::string, TomlBasicValue>;
|
||||
const auto &opts = toml::find<opts_map>(toml, "options");
|
||||
for (const auto &opt : opts) {
|
||||
Option o;
|
||||
o.name = opt.first;
|
||||
if (opt.second.is_boolean()) {
|
||||
o.val = opt.second.as_boolean();
|
||||
} else {
|
||||
get_optional(opt.second, "comment", o.comment);
|
||||
get_optional(opt.second, "value", o.val);
|
||||
}
|
||||
options.push_back(o);
|
||||
}
|
||||
}
|
||||
|
||||
if (toml.contains("find-package")) {
|
||||
using pkg_map = tsl::ordered_map<std::string, TomlBasicValue>;
|
||||
const auto &pkgs = toml::find<pkg_map>(toml, "find-package");
|
||||
for (const auto &pkg : pkgs) {
|
||||
Package p;
|
||||
p.name = pkg.first;
|
||||
if (pkg.second.is_string()) {
|
||||
p.version = pkg.second.as_string();
|
||||
} else {
|
||||
get_optional(pkg.second, "version", p.version);
|
||||
get_optional(pkg.second, "required", p.required);
|
||||
get_optional(pkg.second, "config", p.config);
|
||||
get_optional(pkg.second, "components", p.components);
|
||||
}
|
||||
packages.push_back(p);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: refactor to std::vector<Content> instead of this hacky thing?
|
||||
get_optional(toml, "fetch-content", contents);
|
||||
|
||||
if (toml.contains("bin")) {
|
||||
throw std::runtime_error("[[bin]] has been renamed to [[target]]");
|
||||
}
|
||||
|
||||
if (toml.contains("target")) {
|
||||
const auto &ts = toml::find(toml, "target").as_table();
|
||||
|
||||
for (const auto &itr : ts) {
|
||||
const auto &t = itr.second;
|
||||
Target target;
|
||||
target.name = itr.first;
|
||||
target.type = to_enum<TargetType>(toml::find(t, "type").as_string(), "target type");
|
||||
|
||||
get_optional(t, "sources", target.sources);
|
||||
|
||||
#define renamed(from, to) \
|
||||
if (t.contains(from)) { \
|
||||
throw std::runtime_error(from "has been renamed to " to); \
|
||||
}
|
||||
|
||||
renamed("include-dirs", "include-directories");
|
||||
renamed("link-libs", "link-libraries");
|
||||
renamed("defines", "compile-definitions");
|
||||
renamed("features", "compile-features");
|
||||
|
||||
#undef renamed
|
||||
|
||||
get_optional(t, "compile-definitions", target.compile_definitions);
|
||||
get_optional(t, "compile-features", target.compile_features);
|
||||
get_optional(t, "compile-options", target.compile_options);
|
||||
get_optional(t, "include-directories", target.include_directories);
|
||||
get_optional(t, "link-directories", target.link_directories);
|
||||
get_optional(t, "link-libraries", target.link_libraries);
|
||||
get_optional(t, "link-options", target.link_options);
|
||||
get_optional(t, "precompile-headers", target.precompile_headers);
|
||||
|
||||
if (t.contains("alias")) {
|
||||
target.alias = toml::find(t, "alias").as_string();
|
||||
}
|
||||
|
||||
if (t.contains("properties")) {
|
||||
using prop_map = tsl::ordered_map<std::string, std::string>;
|
||||
target.properties = toml::find<prop_map>(t, "properties");
|
||||
}
|
||||
|
||||
get_optional(t, "cmake-before", target.cmake_before);
|
||||
get_optional(t, "cmake-after", target.cmake_after);
|
||||
get_optional(t, "include-before", target.include_before);
|
||||
get_optional(t, "include-after", target.include_after);
|
||||
|
||||
targets.push_back(target);
|
||||
}
|
||||
}
|
||||
|
||||
if (toml.contains("test")) {
|
||||
const auto &ts = toml::find(toml, "test").as_array();
|
||||
for (const auto &t : ts) {
|
||||
Test test;
|
||||
test.name = toml::find(t, "name").as_string();
|
||||
get_optional(t, "configurations", test.configurations);
|
||||
get_optional(t, "working-directory", test.working_directory);
|
||||
test.command = toml::find(t, "command").as_string();
|
||||
get_optional(t, "arguments", test.arguments);
|
||||
tests.push_back(test);
|
||||
}
|
||||
}
|
||||
|
||||
if (toml.contains("install")) {
|
||||
const auto &ts = toml::find(toml, "install").as_array();
|
||||
for (const auto &t : ts) {
|
||||
Install inst;
|
||||
get_optional(t, "targets", inst.targets);
|
||||
get_optional(t, "files", inst.files);
|
||||
get_optional(t, "dirs", inst.dirs);
|
||||
get_optional(t, "configs", inst.configs);
|
||||
inst.destination = toml::find(t, "destination").as_string();
|
||||
installs.push_back(inst);
|
||||
}
|
||||
}
|
||||
|
||||
if (toml.contains("vcpkg")) {
|
||||
const auto &v = toml::find(toml, "vcpkg");
|
||||
vcpkg.version = toml::find(v, "version").as_string();
|
||||
vcpkg.packages = toml::find<decltype(vcpkg.packages)>(v, "packages");
|
||||
|
||||
// This allows the user to use a custom pmm version if desired
|
||||
if (contents.count("pmm") == 0) {
|
||||
contents["pmm"]["url"] = "https://github.com/vector-of-bool/pmm/archive/refs/tags/1.5.1.tar.gz";
|
||||
// Hack to not execute pmm's example CMakeLists.txt
|
||||
contents["pmm"]["SOURCE_SUBDIR"] = "pmm";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace cmake
|
||||
} // namespace cmkr
|
@ -0,0 +1,122 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <mpark/variant.hpp>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <tsl/ordered_map.h>
|
||||
#include <vector>
|
||||
|
||||
namespace cmkr {
|
||||
namespace cmake {
|
||||
|
||||
struct Setting {
|
||||
std::string name;
|
||||
std::string comment;
|
||||
mpark::variant<bool, std::string> val;
|
||||
bool cache = false;
|
||||
bool force = false;
|
||||
};
|
||||
|
||||
struct Option {
|
||||
std::string name;
|
||||
std::string comment;
|
||||
bool val = false;
|
||||
};
|
||||
|
||||
struct Package {
|
||||
std::string name;
|
||||
std::string version;
|
||||
bool required = true;
|
||||
bool config = true;
|
||||
std::vector<std::string> components;
|
||||
};
|
||||
|
||||
struct Vcpkg {
|
||||
std::string version;
|
||||
std::vector<std::string> packages;
|
||||
};
|
||||
|
||||
enum TargetType {
|
||||
target_executable,
|
||||
target_library,
|
||||
target_shared,
|
||||
target_static,
|
||||
target_interface,
|
||||
target_custom,
|
||||
};
|
||||
|
||||
struct Target {
|
||||
std::string name;
|
||||
TargetType type = {};
|
||||
|
||||
// https://cmake.org/cmake/help/latest/manual/cmake-commands.7.html#project-commands
|
||||
std::vector<std::string> compile_definitions;
|
||||
std::vector<std::string> compile_features;
|
||||
std::vector<std::string> compile_options;
|
||||
std::vector<std::string> include_directories;
|
||||
std::vector<std::string> link_directories;
|
||||
std::vector<std::string> link_libraries;
|
||||
std::vector<std::string> link_options;
|
||||
std::vector<std::string> precompile_headers;
|
||||
std::vector<std::string> sources;
|
||||
|
||||
std::string alias;
|
||||
tsl::ordered_map<std::string, std::string> properties;
|
||||
|
||||
std::string cmake_before;
|
||||
std::string cmake_after;
|
||||
std::vector<std::string> include_before;
|
||||
std::vector<std::string> include_after;
|
||||
};
|
||||
|
||||
struct Test {
|
||||
std::string name;
|
||||
std::vector<std::string> configurations;
|
||||
std::string working_directory;
|
||||
std::string command;
|
||||
std::vector<std::string> arguments;
|
||||
};
|
||||
|
||||
struct Install {
|
||||
std::vector<std::string> targets;
|
||||
std::vector<std::string> files;
|
||||
std::vector<std::string> dirs;
|
||||
std::vector<std::string> configs;
|
||||
std::string destination;
|
||||
};
|
||||
|
||||
struct CMake {
|
||||
// This is the CMake version required to use all cmkr versions.
|
||||
std::string cmake_version = "3.15";
|
||||
std::string cmkr_include = "cmkr.cmake";
|
||||
std::string build_dir = "build";
|
||||
std::string generator;
|
||||
std::string config;
|
||||
std::vector<std::string> subdirs;
|
||||
std::vector<std::string> cppflags;
|
||||
std::vector<std::string> cflags;
|
||||
std::vector<std::string> linkflags;
|
||||
std::vector<std::string> gen_args;
|
||||
std::vector<std::string> build_args;
|
||||
std::string project_name;
|
||||
std::string project_version;
|
||||
std::string project_description;
|
||||
std::vector<std::string> project_languages;
|
||||
std::string cmake_before;
|
||||
std::string cmake_after;
|
||||
std::vector<std::string> include_before;
|
||||
std::vector<std::string> include_after;
|
||||
std::vector<Setting> settings;
|
||||
std::vector<Option> options;
|
||||
std::vector<Package> packages;
|
||||
Vcpkg vcpkg;
|
||||
tsl::ordered_map<std::string, std::map<std::string, std::string>> contents;
|
||||
std::vector<Target> targets;
|
||||
std::vector<Test> tests;
|
||||
std::vector<Install> installs;
|
||||
CMake(const std::string &path, bool build);
|
||||
};
|
||||
|
||||
} // namespace cmake
|
||||
} // namespace cmkr
|
@ -0,0 +1,69 @@
|
||||
// https://codereview.stackexchange.com/a/14315
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
// This is the type that will hold all the strings.
|
||||
// Each enumeration type will declare its own specialization.
|
||||
// Any enum that does not have a specialization will generate a compiler error
|
||||
// indicating that there is no definition of this variable (as there should be
|
||||
// be no definition of a generic version).
|
||||
template <typename T>
|
||||
struct enumStrings {
|
||||
static char const *data[];
|
||||
};
|
||||
|
||||
// This is a utility type.
|
||||
// Created automatically. Should not be used directly.
|
||||
template <typename T>
|
||||
struct enumRefHolder {
|
||||
T &enumVal;
|
||||
enumRefHolder(T &enumVal) : enumVal(enumVal) {}
|
||||
};
|
||||
template <typename T>
|
||||
struct enumConstRefHolder {
|
||||
T const &enumVal;
|
||||
enumConstRefHolder(T const &enumVal) : enumVal(enumVal) {}
|
||||
};
|
||||
|
||||
// The next two functions do the actual work of reading/writing an
|
||||
// enum as a string.
|
||||
template <typename T>
|
||||
std::ostream &operator<<(std::ostream &str, enumConstRefHolder<T> const &data) {
|
||||
return str << enumStrings<T>::data[data.enumVal];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::istream &operator>>(std::istream &str, enumRefHolder<T> const &data) {
|
||||
std::string value;
|
||||
str >> value;
|
||||
|
||||
// These two can be made easier to read in C++11
|
||||
// using std::begin() and std::end()
|
||||
//
|
||||
static auto begin = std::begin(enumStrings<T>::data);
|
||||
static auto end = std::end(enumStrings<T>::data);
|
||||
|
||||
auto find = std::find(begin, end, value);
|
||||
if (find != end) {
|
||||
data.enumVal = static_cast<T>(std::distance(begin, find));
|
||||
} else {
|
||||
throw std::invalid_argument("");
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
// This is the public interface:
|
||||
// use the ability of function to deduce their template type without
|
||||
// being explicitly told to create the correct type of enumRefHolder<T>
|
||||
template <typename T>
|
||||
enumConstRefHolder<T> enumToString(T const &e) {
|
||||
return enumConstRefHolder<T>(e);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
enumRefHolder<T> enumFromString(T &e) {
|
||||
return enumRefHolder<T>(e);
|
||||
}
|
@ -0,0 +1,698 @@
|
||||
#include "gen.h"
|
||||
#include "cmake.hpp"
|
||||
#include "error.h"
|
||||
#include "literals.h"
|
||||
|
||||
#include "fs.hpp"
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <new>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace cmkr {
|
||||
namespace gen {
|
||||
|
||||
inline std::string to_upper(const std::string &str) {
|
||||
std::string temp;
|
||||
temp.reserve(str.size());
|
||||
for (auto c : str) {
|
||||
temp.push_back(::toupper(c));
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
std::string format(const char *fmt, Args... args) {
|
||||
auto sz = snprintf(nullptr, 0, fmt, args...) + 1;
|
||||
char *buf = new char[sz];
|
||||
int ret = snprintf(buf, sz, fmt, args...);
|
||||
if (ret != sz - 1)
|
||||
throw std::runtime_error("Error formatting string!");
|
||||
std::string temp(buf, buf + sz - 1);
|
||||
delete[] buf;
|
||||
return temp;
|
||||
}
|
||||
|
||||
static std::vector<std::string> expand_cmake_path(const fs::path &name, const fs::path &toml_dir) {
|
||||
std::vector<std::string> temp;
|
||||
|
||||
auto extract_suffix = [](const fs::path &base, const fs::path &full) {
|
||||
// TODO: delet this
|
||||
auto fullpath = full.string();
|
||||
auto base_len = base.string().length();
|
||||
auto delet = fullpath.substr(base_len + 1, fullpath.length() - base_len);
|
||||
return delet;
|
||||
};
|
||||
|
||||
auto stem = name.filename().stem().string();
|
||||
auto ext = name.extension();
|
||||
|
||||
if (stem == "*") {
|
||||
for (const auto &f : fs::directory_iterator(toml_dir / name.parent_path(), fs::directory_options::follow_directory_symlink)) {
|
||||
if (!f.is_directory() && f.path().extension() == ext) {
|
||||
temp.push_back(extract_suffix(toml_dir, f));
|
||||
}
|
||||
}
|
||||
} else if (stem == "**") {
|
||||
for (const auto &f : fs::recursive_directory_iterator(toml_dir / name.parent_path(), fs::directory_options::follow_directory_symlink)) {
|
||||
if (!f.is_directory() && f.path().extension() == ext) {
|
||||
temp.push_back(extract_suffix(toml_dir, f.path()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
temp.push_back(name.string());
|
||||
}
|
||||
// Normalize all paths to work with CMake (it needs a / on Windows as well)
|
||||
for (auto &path : temp) {
|
||||
std::replace(path.begin(), path.end(), '\\', '/');
|
||||
}
|
||||
// Sort paths alphabetically for consistent cross-OS generation
|
||||
std::sort(temp.begin(), temp.end());
|
||||
return temp;
|
||||
}
|
||||
|
||||
static std::vector<std::string> expand_cmake_paths(const std::vector<std::string> &sources, const fs::path &toml_dir) {
|
||||
// TODO: add duplicate checking
|
||||
std::vector<std::string> result;
|
||||
for (const auto &src : sources) {
|
||||
auto expanded = expand_cmake_path(src, toml_dir);
|
||||
for (const auto &f : expanded) {
|
||||
result.push_back(f);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int generate_project(const char *str) {
|
||||
fs::create_directory("src");
|
||||
fs::create_directory("include");
|
||||
const auto dir_name = fs::current_path().stem().string();
|
||||
std::string mainbuf;
|
||||
std::string installed;
|
||||
std::string target;
|
||||
std::string dest;
|
||||
if (!strcmp(str, "executable")) {
|
||||
mainbuf = format(hello_world, "main");
|
||||
installed = "targets";
|
||||
target = dir_name;
|
||||
dest = "bin";
|
||||
} else if (!strcmp(str, "static") || !strcmp(str, "shared") || !strcmp(str, "library")) {
|
||||
mainbuf = format(hello_world, "test");
|
||||
installed = "targets";
|
||||
target = dir_name;
|
||||
dest = "lib";
|
||||
} else if (!strcmp(str, "interface")) {
|
||||
installed = "files";
|
||||
target = "include/*.h";
|
||||
dest = "include/" + dir_name;
|
||||
} else {
|
||||
throw std::runtime_error("Unknown project type " + std::string(str) +
|
||||
"! Supported types are: executable, library, shared, static, interface");
|
||||
}
|
||||
|
||||
const auto tomlbuf = format(cmake_toml, dir_name.c_str(), dir_name.c_str(), str, installed.c_str(), target.c_str(), dest.c_str());
|
||||
|
||||
if (strcmp(str, "interface")) {
|
||||
std::ofstream ofs("src/main.cpp");
|
||||
if (ofs.is_open()) {
|
||||
ofs << mainbuf;
|
||||
}
|
||||
ofs.flush();
|
||||
ofs.close();
|
||||
}
|
||||
|
||||
std::ofstream ofs2("cmake.toml");
|
||||
if (ofs2.is_open()) {
|
||||
ofs2 << tomlbuf;
|
||||
}
|
||||
ofs2.flush();
|
||||
ofs2.close();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct CommandEndl {
|
||||
std::stringstream &ss;
|
||||
CommandEndl(std::stringstream &ss) : ss(ss) {}
|
||||
void endl() { ss << '\n'; }
|
||||
};
|
||||
|
||||
// Credit: JustMagic
|
||||
struct Command {
|
||||
std::stringstream &ss;
|
||||
int depth = 0;
|
||||
std::string command;
|
||||
bool first_arg = true;
|
||||
bool had_newline = false;
|
||||
bool generated = false;
|
||||
|
||||
Command(std::stringstream &ss, int depth, const std::string &command) : ss(ss), depth(depth), command(command) {}
|
||||
|
||||
~Command() {
|
||||
if (!generated) {
|
||||
assert(false && "Incorrect usage of cmd()");
|
||||
}
|
||||
}
|
||||
|
||||
std::string quote(const std::string &str) {
|
||||
// Don't quote arguments that don't need quoting
|
||||
if (str.find(' ') == std::string::npos && str.find('\"') == std::string::npos && str.find('/') == std::string::npos) {
|
||||
return str;
|
||||
}
|
||||
std::string result;
|
||||
result += "\"";
|
||||
for (char ch : str) {
|
||||
switch (ch) {
|
||||
case '\\':
|
||||
case '\"':
|
||||
result += '\\';
|
||||
default:
|
||||
result += ch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
result += "\"";
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::string indent(int n) {
|
||||
std::string result;
|
||||
for (int i = 0; i < n; i++) {
|
||||
result += '\t';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool print_arg(const std::vector<T> &vec) {
|
||||
if (vec.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
had_newline = true;
|
||||
for (const auto &value : vec) {
|
||||
print_arg(value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class Key, class Value>
|
||||
bool print_arg(const tsl::ordered_map<Key, Value> &map) {
|
||||
if (map.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const auto &itr : map) {
|
||||
print_arg(itr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class K>
|
||||
bool print_arg(const std::pair<K, std::vector<std::string>> &kv) {
|
||||
if (kv.second.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
had_newline = true;
|
||||
print_arg(kv.first);
|
||||
depth++;
|
||||
for (const auto &s : kv.second) {
|
||||
print_arg(s);
|
||||
}
|
||||
depth--;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class K, class V>
|
||||
bool print_arg(const std::pair<K, V> &kv) {
|
||||
if (kv.second.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
had_newline = true;
|
||||
print_arg(kv.first);
|
||||
depth++;
|
||||
print_arg(kv.second);
|
||||
depth--;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool print_arg(const T &value, bool indentation = true) {
|
||||
std::stringstream tmp;
|
||||
tmp << value;
|
||||
auto str = tmp.str();
|
||||
if (str.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (indentation) {
|
||||
if (had_newline) {
|
||||
first_arg = false;
|
||||
ss << '\n' << indent(depth + 1);
|
||||
} else if (first_arg) {
|
||||
first_arg = false;
|
||||
} else {
|
||||
ss << ' ';
|
||||
}
|
||||
}
|
||||
|
||||
ss << quote(str);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class... Ts>
|
||||
CommandEndl operator()(Ts &&...values) {
|
||||
generated = true;
|
||||
ss << indent(depth) << command << '(';
|
||||
(void)std::initializer_list<bool>{print_arg(values)...};
|
||||
if (had_newline)
|
||||
ss << '\n';
|
||||
ss << ")\n";
|
||||
return CommandEndl(ss);
|
||||
}
|
||||
};
|
||||
|
||||
int generate_cmake(const char *path, bool root) {
|
||||
if (!fs::exists(fs::path(path) / "cmake.toml")) {
|
||||
throw std::runtime_error("No cmake.toml found!");
|
||||
}
|
||||
|
||||
// Helper lambdas for more convenient CMake generation
|
||||
std::stringstream ss;
|
||||
int indent = 0;
|
||||
auto cmd = [&ss, &indent](const std::string &command) {
|
||||
if (command.empty())
|
||||
throw std::invalid_argument("command cannot be empty");
|
||||
if (command == "if") {
|
||||
indent++;
|
||||
return Command(ss, indent - 1, command);
|
||||
} else if (command == "else" || command == "elseif") {
|
||||
return Command(ss, indent - 1, command);
|
||||
} else if (command == "endif") {
|
||||
indent--;
|
||||
}
|
||||
return Command(ss, indent, command);
|
||||
};
|
||||
auto comment = [&ss, &indent](const std::string &comment) {
|
||||
ss << Command::indent(indent) << "# " << comment << '\n';
|
||||
return CommandEndl(ss);
|
||||
};
|
||||
auto endl = [&ss]() { ss << '\n'; };
|
||||
auto tolf = [](const std::string &str) {
|
||||
std::string result;
|
||||
for (char ch : str) {
|
||||
if (ch != '\r') {
|
||||
result += ch;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
auto inject_includes = [&cmd, &endl](const std::vector<std::string> &includes) {
|
||||
if (!includes.empty()) {
|
||||
for (const auto &file : includes) {
|
||||
// TODO: warn/error if file doesn't exist?
|
||||
cmd("include")(file);
|
||||
}
|
||||
endl();
|
||||
}
|
||||
};
|
||||
auto inject_cmake = [&ss, &tolf](const std::string &cmake) {
|
||||
if (!cmake.empty()) {
|
||||
if (cmake.back() == '\"') {
|
||||
throw std::runtime_error("Detected additional \" at the end of cmake block");
|
||||
}
|
||||
ss << tolf(cmake) << "\n\n";
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: add link with proper documentation
|
||||
comment("This file was generated automatically by cmkr.").endl();
|
||||
|
||||
cmake::CMake cmake(path, false);
|
||||
|
||||
if (root) {
|
||||
cmd("cmake_minimum_required")("VERSION", cmake.cmake_version).endl();
|
||||
|
||||
comment("Regenerate CMakeLists.txt automatically in the root project");
|
||||
cmd("set")("CMKR_ROOT_PROJECT", "OFF");
|
||||
// clang-format off
|
||||
cmd("if")("CMAKE_CURRENT_SOURCE_DIR", "STREQUAL", "CMAKE_SOURCE_DIR");
|
||||
cmd("set")("CMKR_ROOT_PROJECT", "ON").endl();
|
||||
|
||||
comment("Bootstrap cmkr");
|
||||
cmd("include")(cmake.cmkr_include, "OPTIONAL", "RESULT_VARIABLE", "CMKR_INCLUDE_RESULT");
|
||||
cmd("if")("CMKR_INCLUDE_RESULT");
|
||||
cmd("cmkr")();
|
||||
cmd("endif")().endl();
|
||||
|
||||
comment("Enable folder support");
|
||||
cmd("set_property")("GLOBAL", "PROPERTY", "USE_FOLDERS", "ON");
|
||||
cmd("endif")().endl();
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
comment("Create a configure-time dependency on cmake.toml to improve IDE support");
|
||||
cmd("if")("CMKR_ROOT_PROJECT");
|
||||
cmd("configure_file")("cmake.toml", "cmake.toml", "COPYONLY");
|
||||
cmd("endif")().endl();
|
||||
// clang-format on
|
||||
|
||||
// TODO: remove support and replace with global compile-features
|
||||
if (!cmake.cppflags.empty()) {
|
||||
ss << "set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} \"";
|
||||
for (const auto &flag : cmake.cppflags) {
|
||||
ss << flag << " ";
|
||||
}
|
||||
ss << "\")\n\n";
|
||||
}
|
||||
|
||||
// TODO: remove support and replace with global compile-features
|
||||
if (!cmake.cflags.empty()) {
|
||||
ss << "set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} \"";
|
||||
for (const auto &flag : cmake.cflags) {
|
||||
ss << flag << " ";
|
||||
}
|
||||
ss << "\")\n\n";
|
||||
}
|
||||
|
||||
// TODO: remove support and replace with global linker-flags
|
||||
if (!cmake.linkflags.empty()) {
|
||||
ss << "set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} \"";
|
||||
for (const auto &flag : cmake.linkflags) {
|
||||
ss << flag << " ";
|
||||
}
|
||||
ss << "\")\n\n";
|
||||
}
|
||||
|
||||
inject_includes(cmake.include_before);
|
||||
inject_cmake(cmake.cmake_before);
|
||||
|
||||
if (!cmake.project_name.empty()) {
|
||||
auto languages = std::make_pair("LANGUAGES", cmake.project_languages);
|
||||
auto version = std::make_pair("VERSION", cmake.project_version);
|
||||
auto description = std::make_pair("DESCRIPTION", cmake.project_description);
|
||||
cmd("project")(cmake.project_name, languages, version, description).endl();
|
||||
}
|
||||
|
||||
inject_includes(cmake.include_after);
|
||||
inject_cmake(cmake.cmake_after);
|
||||
|
||||
if (!cmake.contents.empty()) {
|
||||
cmd("include")("FetchContent").endl();
|
||||
for (const auto &dep : cmake.contents) {
|
||||
cmd("message")("STATUS", "Fetching " + dep.first + "...");
|
||||
ss << "FetchContent_Declare(\n\t" << dep.first << "\n";
|
||||
for (const auto &arg : dep.second) {
|
||||
std::string first_arg = arg.first;
|
||||
if (first_arg == "git") {
|
||||
first_arg = "GIT_REPOSITORY";
|
||||
} else if (first_arg == "tag") {
|
||||
first_arg = "GIT_TAG";
|
||||
} else if (first_arg == "svn") {
|
||||
first_arg = "SVN_REPOSITORY";
|
||||
} else if (first_arg == "rev") {
|
||||
first_arg = "SVN_REVISION";
|
||||
} else if (first_arg == "url") {
|
||||
first_arg = "URL";
|
||||
} else if (first_arg == "hash") {
|
||||
first_arg = "URL_HASH";
|
||||
} else {
|
||||
// don't change arg
|
||||
}
|
||||
ss << "\t" << first_arg << "\n\t\t" << arg.second << "\n";
|
||||
}
|
||||
ss << ")\n";
|
||||
cmd("FetchContent_MakeAvailable")(dep.first).endl();
|
||||
}
|
||||
}
|
||||
|
||||
if (!cmake.vcpkg.version.empty()) {
|
||||
assert("pmm is required in fetch-content for vcpkg to work" && cmake.contents.count("pmm") != 0);
|
||||
comment("Bootstrap vcpkg");
|
||||
cmd("include")("${pmm_SOURCE_DIR}/pmm.cmake");
|
||||
tsl::ordered_map<std::string, std::vector<std::string>> vcpkg_args;
|
||||
vcpkg_args["REVISION"] = { cmake.vcpkg.version };
|
||||
vcpkg_args["REQUIRES"] = cmake.vcpkg.packages;
|
||||
auto vcpkg = std::make_pair("VCPKG", vcpkg_args);
|
||||
cmd("pmm")(vcpkg).endl();
|
||||
}
|
||||
|
||||
if (!cmake.packages.empty()) {
|
||||
comment("Packages");
|
||||
for (const auto &dep : cmake.packages) {
|
||||
auto version = dep.version;
|
||||
if (version == "*")
|
||||
version.clear();
|
||||
auto required = dep.required ? "REQUIRED" : "";
|
||||
auto config = dep.config ? "CONFIG" : "";
|
||||
auto components = std::make_pair("COMPONENTS", dep.components);
|
||||
cmd("find_package")(dep.name, version, required, config, components).endl();
|
||||
}
|
||||
}
|
||||
|
||||
if (!cmake.options.empty()) {
|
||||
comment("Options");
|
||||
for (const auto &opt : cmake.options) {
|
||||
cmd("option")(opt.name, opt.comment, opt.val ? "ON" : "OFF");
|
||||
}
|
||||
endl();
|
||||
}
|
||||
|
||||
if (!cmake.settings.empty()) {
|
||||
comment("Settings");
|
||||
for (const auto &set : cmake.settings) {
|
||||
std::string set_val;
|
||||
if (set.val.index() == 1) {
|
||||
set_val = mpark::get<1>(set.val);
|
||||
} else {
|
||||
set_val = mpark::get<0>(set.val) ? "ON" : "OFF";
|
||||
}
|
||||
|
||||
if (set.cache) {
|
||||
auto typ = set.val.index() == 1 ? "STRING" : "BOOL";
|
||||
auto force = set.force ? "FORCE" : "";
|
||||
cmd("set")(set.name, set_val, typ, set.comment, force);
|
||||
} else {
|
||||
cmd("set")(set.name, set_val);
|
||||
}
|
||||
}
|
||||
endl();
|
||||
}
|
||||
|
||||
// generate_cmake is called on the subdirectories recursively later
|
||||
if (!cmake.subdirs.empty()) {
|
||||
for (const auto &dir : cmake.subdirs) {
|
||||
// clang-format off
|
||||
comment(dir);
|
||||
cmd("set")("CMKR_CMAKE_FOLDER", "${CMAKE_FOLDER}");
|
||||
cmd("if")("CMAKE_FOLDER");
|
||||
cmd("set")("CMAKE_FOLDER", "${CMAKE_FOLDER}/" + dir);
|
||||
cmd("else")();
|
||||
cmd("set")("CMAKE_FOLDER", dir);
|
||||
cmd("endif")();
|
||||
// clang-format on
|
||||
cmd("add_subdirectory")(dir);
|
||||
cmd("set")("CMAKE_FOLDER", "${CMKR_CMAKE_FOLDER}").endl();
|
||||
}
|
||||
endl();
|
||||
}
|
||||
|
||||
if (!cmake.targets.empty()) {
|
||||
for (const auto &target : cmake.targets) {
|
||||
comment("Target " + target.name);
|
||||
inject_includes(target.include_before);
|
||||
inject_cmake(target.cmake_before);
|
||||
|
||||
if (!target.sources.empty()) {
|
||||
auto sources = expand_cmake_paths(target.sources, path);
|
||||
if (sources.empty()) {
|
||||
throw std::runtime_error(target.name + " sources wildcard found 0 files");
|
||||
}
|
||||
if (target.type != cmake::target_interface) {
|
||||
// Do not add cmake.toml twice
|
||||
if (std::find(sources.begin(), sources.end(), "cmake.toml") == sources.end()) {
|
||||
sources.push_back("cmake.toml");
|
||||
}
|
||||
}
|
||||
cmd("set")(target.name + "_SOURCES", sources).endl();
|
||||
}
|
||||
|
||||
std::string add_command;
|
||||
std::string target_type;
|
||||
std::string target_scope;
|
||||
switch (target.type) {
|
||||
case cmake::target_executable:
|
||||
add_command = "add_executable";
|
||||
target_type = "";
|
||||
target_scope = "PRIVATE";
|
||||
break;
|
||||
case cmake::target_library:
|
||||
add_command = "add_library";
|
||||
target_type = "";
|
||||
target_scope = "PUBLIC";
|
||||
break;
|
||||
case cmake::target_shared:
|
||||
add_command = "add_library";
|
||||
target_type = "SHARED";
|
||||
target_scope = "PUBLIC";
|
||||
break;
|
||||
case cmake::target_static:
|
||||
add_command = "add_library";
|
||||
target_type = "STATIC";
|
||||
target_scope = "PUBLIC";
|
||||
break;
|
||||
case cmake::target_interface:
|
||||
add_command = "add_library";
|
||||
target_type = "INTERFACE";
|
||||
target_scope = "INTERFACE";
|
||||
break;
|
||||
case cmake::target_custom:
|
||||
// TODO: add proper support, this is hacky
|
||||
add_command = "add_custom_target";
|
||||
target_type = "SOURCES";
|
||||
target_scope = "PUBLIC";
|
||||
break;
|
||||
default:
|
||||
assert("Unimplemented enum value" && false);
|
||||
}
|
||||
|
||||
cmd(add_command)(target.name, target_type, "${" + target.name + "_SOURCES}").endl();
|
||||
|
||||
// The first executable target will become the Visual Studio startup project
|
||||
if (target.type == cmake::target_executable) {
|
||||
cmd("get_directory_property")("CMKR_VS_STARTUP_PROJECT", "DIRECTORY", "${PROJECT_SOURCE_DIR}", "DEFINITION", "VS_STARTUP_PROJECT");
|
||||
// clang-format off
|
||||
cmd("if")("NOT", "CMKR_VS_STARTUP_PROJECT");
|
||||
cmd("set_property")("DIRECTORY", "${PROJECT_SOURCE_DIR}", "PROPERTY", "VS_STARTUP_PROJECT", target.name);
|
||||
cmd("endif")().endl();
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
if (!target.sources.empty()) {
|
||||
cmd("source_group")("TREE", "${CMAKE_CURRENT_SOURCE_DIR}", "FILES", "${" + target.name + "_SOURCES}").endl();
|
||||
}
|
||||
|
||||
if (!target.alias.empty()) {
|
||||
cmd("add_library")(target.alias, "ALIAS", target.name);
|
||||
}
|
||||
|
||||
auto target_cmd = [&](const char *command, const std::vector<std::string> &args) {
|
||||
if (!args.empty()) {
|
||||
cmd(command)(target.name, target_scope, args).endl();
|
||||
}
|
||||
};
|
||||
|
||||
target_cmd("target_compile_definitions", target.compile_definitions);
|
||||
target_cmd("target_compile_features", target.compile_features);
|
||||
target_cmd("target_compile_options", target.compile_options);
|
||||
target_cmd("target_include_directories", target.include_directories);
|
||||
target_cmd("target_link_directories", target.link_directories);
|
||||
target_cmd("target_link_libraries", target.link_libraries);
|
||||
target_cmd("target_precompile_headers", target.precompile_headers);
|
||||
|
||||
if (!target.properties.empty()) {
|
||||
cmd("set_target_properties")(target.name, "PROPERTIES", target.properties).endl();
|
||||
}
|
||||
|
||||
inject_includes(target.include_after);
|
||||
inject_cmake(target.cmake_after);
|
||||
}
|
||||
}
|
||||
|
||||
if (!cmake.tests.empty()) {
|
||||
cmd("enable_testing")().endl();
|
||||
for (const auto &test : cmake.tests) {
|
||||
auto name = std::make_pair("NAME", test.name);
|
||||
auto configurations = std::make_pair("CONFIGURATIONS", test.configurations);
|
||||
auto working_directory = std::make_pair("WORKING_DIRECTORY", test.working_directory);
|
||||
auto command = std::make_pair("COMMAND", test.command);
|
||||
auto arguments = std::make_pair("", test.arguments);
|
||||
cmd("add_test")(name, configurations, working_directory, command, arguments).endl();
|
||||
}
|
||||
}
|
||||
|
||||
if (!cmake.installs.empty()) {
|
||||
for (const auto &inst : cmake.installs) {
|
||||
auto targets = std::make_pair("TARGETS", inst.targets);
|
||||
auto dirs = std::make_pair("DIRS", inst.dirs);
|
||||
std::vector<std::string> files_data;
|
||||
if (!inst.files.empty()) {
|
||||
files_data = expand_cmake_paths(inst.files, path);
|
||||
if (files_data.empty()) {
|
||||
throw std::runtime_error("[[install]] files wildcard did not resolve to any files");
|
||||
}
|
||||
}
|
||||
auto files = std::make_pair("FILES", inst.files);
|
||||
auto configs = std::make_pair("CONFIGURATIONS", inst.configs);
|
||||
auto destination = std::make_pair("DESTINATION", inst.destination);
|
||||
auto component = std::make_pair("COMPONENT", inst.targets.empty() ? "" : inst.targets.front());
|
||||
cmd("install")(targets, dirs, files, configs, destination, component);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate CMakeLists.txt
|
||||
auto list_path = fs::path(path) / "CMakeLists.txt";
|
||||
|
||||
auto should_regenerate = [&list_path, &ss]() {
|
||||
if (!fs::exists(list_path))
|
||||
return true;
|
||||
|
||||
std::ifstream ifs(list_path, std::ios_base::binary);
|
||||
if (!ifs.is_open()) {
|
||||
throw std::runtime_error("Failed to read " + list_path.string());
|
||||
}
|
||||
|
||||
std::string data((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
|
||||
return data != ss.str();
|
||||
}();
|
||||
|
||||
if (should_regenerate) {
|
||||
std::ofstream ofs(list_path, std::ios_base::binary);
|
||||
if (ofs.is_open()) {
|
||||
ofs << ss.str();
|
||||
} else {
|
||||
throw std::runtime_error("Failed to write " + list_path.string());
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &sub : cmake.subdirs) {
|
||||
auto subpath = fs::path(path) / fs::path(sub);
|
||||
if (fs::exists(subpath / "cmake.toml"))
|
||||
generate_cmake(subpath.string().c_str(), false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace gen
|
||||
} // namespace cmkr
|
||||
|
||||
int cmkr_gen_generate_project(const char *typ) {
|
||||
try {
|
||||
return cmkr::gen::generate_project(typ);
|
||||
} catch (const std::system_error &e) {
|
||||
return e.code().value();
|
||||
} catch (...) {
|
||||
return cmkr::error::Status(cmkr::error::Status::Code::InitError);
|
||||
}
|
||||
}
|
||||
|
||||
int cmkr_gen_generate_cmake(const char *path) {
|
||||
try {
|
||||
return cmkr::gen::generate_cmake(path);
|
||||
} catch (const std::system_error &e) {
|
||||
return e.code().value();
|
||||
} catch (...) {
|
||||
return cmkr::error::Status(cmkr::error::Status::Code::GenerationError);
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
#include "help.h"
|
||||
|
||||
namespace cmkr {
|
||||
namespace help {
|
||||
|
||||
const char *version() noexcept { return "cmkr version 0.1.3"; }
|
||||
|
||||
const char *message() noexcept {
|
||||
return R"lit(
|
||||
Usage: cmkr [arguments]
|
||||
arguments:
|
||||
init [executable|library|shared|static|interface] Starts a new project in the same directory.
|
||||
gen Generates CMakeLists.txt file.
|
||||
build <extra cmake args> Run cmake and build.
|
||||
install Run cmake --install. Needs admin privileges.
|
||||
clean Clean the build directory.
|
||||
help Show help.
|
||||
version Current cmkr version.
|
||||
)lit";
|
||||
}
|
||||
} // namespace help
|
||||
} // namespace cmkr
|
||||
|
||||
const char *cmkr_help_version(void) { return cmkr::help::version(); }
|
||||
|
||||
const char *cmkr_help_message(void) { return cmkr::help::message(); }
|
@ -1,430 +0,0 @@
|
||||
#include "gen.h"
|
||||
#include "cmake.hpp"
|
||||
#include "error.h"
|
||||
#include "literals.h"
|
||||
|
||||
#include "fs.hpp"
|
||||
#include <fstream>
|
||||
#include <new>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
namespace cmkr {
|
||||
namespace gen {
|
||||
|
||||
namespace detail {
|
||||
|
||||
inline std::string to_upper(const std::string &str) {
|
||||
std::string temp;
|
||||
temp.reserve(str.size());
|
||||
for (auto c : str) {
|
||||
temp.push_back(::toupper(c));
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
std::string format(const char *fmt, Args... args) {
|
||||
auto sz = snprintf(nullptr, 0, fmt, args...) + 1;
|
||||
char *buf = new char[sz];
|
||||
int ret = snprintf(buf, sz, fmt, args...);
|
||||
if (ret != sz - 1)
|
||||
throw std::runtime_error("[cmkr] error: Error formatting string!");
|
||||
std::string temp(buf, buf + sz - 1);
|
||||
delete[] buf;
|
||||
return temp;
|
||||
}
|
||||
|
||||
static std::vector<std::string> expand_cmake_path(const fs::path &p) {
|
||||
std::vector<std::string> temp;
|
||||
if (p.filename().stem().string() == "*") {
|
||||
auto ext = p.extension();
|
||||
for (const auto &f : fs::directory_iterator(p.parent_path())) {
|
||||
if (f.path().extension() == ext) {
|
||||
temp.push_back(f.path().string());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
temp.push_back(p.string());
|
||||
}
|
||||
// Normalize all paths to work with CMake (it needs a / on Windows as well)
|
||||
for (auto &path : temp) {
|
||||
std::replace(path.begin(), path.end(), '\\', '/');
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
int generate_project(const char *str) {
|
||||
fs::create_directory("src");
|
||||
fs::create_directory("include");
|
||||
const auto dir_name = fs::current_path().stem().string();
|
||||
std::string mainbuf;
|
||||
std::string installed;
|
||||
std::string target;
|
||||
std::string dest;
|
||||
if (!strcmp(str, "exe")) {
|
||||
mainbuf = detail::format(hello_world, "main");
|
||||
installed = "targets";
|
||||
target = dir_name;
|
||||
dest = "bin";
|
||||
} else if (!strcmp(str, "static") || !strcmp(str, "shared") || !strcmp(str, "lib")) {
|
||||
mainbuf = detail::format(hello_world, "test");
|
||||
installed = "targets";
|
||||
target = dir_name;
|
||||
dest = "lib";
|
||||
} else if (!strcmp(str, "interface")) {
|
||||
installed = "files";
|
||||
target = "include/*.h";
|
||||
dest = "include/" + dir_name;
|
||||
} else {
|
||||
throw std::runtime_error(
|
||||
"[cmkr] error: Unknown project type. Types are exe, lib, shared, static, interface!");
|
||||
}
|
||||
|
||||
const auto tomlbuf = detail::format(cmake_toml, dir_name.c_str(), dir_name.c_str(), str,
|
||||
installed.c_str(), target.c_str(), dest.c_str());
|
||||
|
||||
if (strcmp(str, "interface")) {
|
||||
std::ofstream ofs("src/main.cpp");
|
||||
if (ofs.is_open()) {
|
||||
ofs << mainbuf;
|
||||
}
|
||||
ofs.flush();
|
||||
ofs.close();
|
||||
}
|
||||
|
||||
std::ofstream ofs2("cmake.toml");
|
||||
if (ofs2.is_open()) {
|
||||
ofs2 << tomlbuf;
|
||||
}
|
||||
ofs2.flush();
|
||||
ofs2.close();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int generate_cmake(const char *path) {
|
||||
if (fs::exists(fs::path(path) / "cmake.toml")) {
|
||||
cmake::CMake cmake(path, false);
|
||||
std::stringstream ss;
|
||||
ss << "# This file was generated automatically by cmkr.\n";
|
||||
ss << "\n";
|
||||
|
||||
ss << "# Regenerate CMakeLists.txt file when necessary\n";
|
||||
ss << "include(cmkr.cmake OPTIONAL RESULT_VARIABLE CMKR_INCLUDE_RESULT)\n\n";
|
||||
ss << "if(CMKR_INCLUDE_RESULT)\n";
|
||||
ss << "\tcmkr()\n";
|
||||
ss << "endif()\n";
|
||||
ss << "\n";
|
||||
|
||||
if (!cmake.cmake_version.empty()) {
|
||||
ss << "cmake_minimum_required(VERSION " << cmake.cmake_version << ")\n\n";
|
||||
|
||||
ss << "set(CMAKE_EXPORT_COMPILE_COMMANDS ON)\n\n";
|
||||
}
|
||||
|
||||
if (!cmake.cppflags.empty()) {
|
||||
ss << "set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} \"";
|
||||
for (const auto &flag : cmake.cppflags) {
|
||||
ss << flag << " ";
|
||||
}
|
||||
ss << "\")\n\n";
|
||||
}
|
||||
|
||||
if (!cmake.cflags.empty()) {
|
||||
ss << "set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} \"";
|
||||
for (const auto &flag : cmake.cflags) {
|
||||
ss << flag << " ";
|
||||
}
|
||||
ss << "\")\n\n";
|
||||
}
|
||||
|
||||
if (!cmake.linkflags.empty()) {
|
||||
ss << "set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} \"";
|
||||
for (const auto &flag : cmake.linkflags) {
|
||||
ss << flag << " ";
|
||||
}
|
||||
ss << "\")\n\n";
|
||||
}
|
||||
|
||||
if (!cmake.subdirs.empty()) {
|
||||
for (const auto &dir : cmake.subdirs) {
|
||||
ss << "add_subdirectory(" << dir << ")\n";
|
||||
}
|
||||
ss << "\n\n";
|
||||
}
|
||||
|
||||
std::string desc = "";
|
||||
if (!cmake.desc.empty())
|
||||
desc = "\n\tLANGUAGES C CXX\n\tDESCRIPTION \"" + cmake.desc + "\"";
|
||||
|
||||
if (!cmake.proj_name.empty() && !cmake.proj_version.empty()) {
|
||||
ss << "project(" << cmake.proj_name << "\n\tVERSION " << cmake.proj_version << desc
|
||||
<< "\n\t)\n\n";
|
||||
}
|
||||
|
||||
if (!cmake.packages.empty()) {
|
||||
for (const auto &dep : cmake.packages) {
|
||||
ss << "find_package(" << dep.name << " ";
|
||||
if (dep.version != "*") {
|
||||
ss << dep.version << " ";
|
||||
}
|
||||
if (dep.required) {
|
||||
ss << "REQUIRED ";
|
||||
}
|
||||
if (!dep.components.empty()) {
|
||||
ss << "COMPONENTS ";
|
||||
for (const auto &comp : dep.components) {
|
||||
ss << comp << " ";
|
||||
}
|
||||
}
|
||||
ss << ")\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!cmake.contents.empty()) {
|
||||
ss << "include(FetchContent)\n\n";
|
||||
for (const auto &dep : cmake.contents) {
|
||||
ss << "FetchContent_Declare(\n\t" << dep.first << "\n";
|
||||
for (const auto &arg : dep.second) {
|
||||
std::string first_arg = arg.first;
|
||||
if (first_arg == "git") {
|
||||
first_arg = "GIT_REPOSITORY";
|
||||
} else if (first_arg == "tag") {
|
||||
first_arg = "GIT_TAG";
|
||||
} else if (first_arg == "svn") {
|
||||
first_arg = "SVN_REPOSITORY";
|
||||
} else if (first_arg == "rev") {
|
||||
first_arg = "SVN_REVISION";
|
||||
} else if (first_arg == "url") {
|
||||
first_arg = "URL";
|
||||
} else if (first_arg == "hash") {
|
||||
first_arg = "URL_HASH";
|
||||
} else {
|
||||
// don't change arg
|
||||
}
|
||||
if (first_arg != "GIT_REPOSITORY")
|
||||
ss << "\t" << first_arg << " " << arg.second << "\n";
|
||||
else
|
||||
ss << "\t" << first_arg << " " << arg.second << "\n\t"
|
||||
<< "GIT_SHALLOW "
|
||||
<< "ON\n";
|
||||
}
|
||||
ss << "\t)\n\n"
|
||||
<< "FetchContent_MakeAvailable(" << dep.first << ")\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!cmake.options.empty()) {
|
||||
for (const auto &opt : cmake.options) {
|
||||
ss << "option(" << opt.name << " \"" << opt.comment << "\" "
|
||||
<< (opt.val ? "ON" : "OFF") << ")\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!cmake.settings.empty()) {
|
||||
for (const auto &set : cmake.settings) {
|
||||
std::string set_val;
|
||||
if (set.val.index() == 1) {
|
||||
set_val = mpark::get<1>(set.val);
|
||||
} else {
|
||||
set_val = mpark::get<0>(set.val) ? "ON" : "OFF";
|
||||
}
|
||||
ss << "set(" << set.name << " " << set_val;
|
||||
;
|
||||
if (set.cache) {
|
||||
std::string typ;
|
||||
if (set.val.index() == 1)
|
||||
typ = "STRING";
|
||||
else
|
||||
typ = "BOOL";
|
||||
ss << " CACHE " << typ << " \"" << set.comment << "\"";
|
||||
if (set.force)
|
||||
ss << " FORCE";
|
||||
}
|
||||
ss << ")\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!cmake.binaries.empty()) {
|
||||
for (const auto &bin : cmake.binaries) {
|
||||
std::string bin_type;
|
||||
std::string add_command;
|
||||
if (bin.type == "exe") {
|
||||
bin_type = "";
|
||||
add_command = "add_executable";
|
||||
} else if (bin.type == "shared" || bin.type == "static" ||
|
||||
bin.type == "interface") {
|
||||
bin_type = detail::to_upper(bin.type);
|
||||
add_command = "add_library";
|
||||
} else if (bin.type == "lib") {
|
||||
bin_type = "";
|
||||
add_command = "add_library";
|
||||
} else {
|
||||
throw std::runtime_error("[cmkr] error: Unknown binary type! Supported types "
|
||||
"are exe, lib, shared, static, interface");
|
||||
}
|
||||
|
||||
if (!bin.sources.empty()) {
|
||||
ss << "set(" << detail::to_upper(bin.name) << "_SOURCES\n";
|
||||
for (const auto &src : bin.sources) {
|
||||
auto path = fs::path(src);
|
||||
auto expanded = detail::expand_cmake_path(path);
|
||||
for (const auto &f : expanded) {
|
||||
ss << "\t" << f << "\n";
|
||||
}
|
||||
}
|
||||
ss << "\t)\n\n";
|
||||
}
|
||||
|
||||
ss << add_command << "(" << bin.name << " " << bin_type;
|
||||
|
||||
if (!bin.sources.empty()) {
|
||||
ss << " ${" << detail::to_upper(bin.name) << "_SOURCES})\n\n";
|
||||
} else {
|
||||
ss << ")\n\n";
|
||||
}
|
||||
|
||||
if (!bin.alias.empty()) {
|
||||
ss << "add_library(" << bin.alias << " ALIAS " << bin.name << ")\n\n";
|
||||
}
|
||||
|
||||
if (!bin.include_dirs.empty()) {
|
||||
ss << "target_include_directories(" << bin.name << " PUBLIC\n\t";
|
||||
for (const auto &inc : bin.include_dirs) {
|
||||
ss << fs::path(inc).string() << "\n\t";
|
||||
}
|
||||
ss << ")\n\n";
|
||||
}
|
||||
|
||||
if (!bin.link_libs.empty()) {
|
||||
ss << "target_link_libraries(" << bin.name << " PUBLIC\n\t";
|
||||
for (const auto &l : bin.link_libs) {
|
||||
ss << l << "\n\t";
|
||||
}
|
||||
ss << ")\n\n";
|
||||
}
|
||||
|
||||
if (!bin.features.empty()) {
|
||||
ss << "target_compile_features(" << bin.name << " PUBLIC\n\t";
|
||||
for (const auto &feat : bin.features) {
|
||||
ss << feat << "\n\t";
|
||||
}
|
||||
ss << ")\n\n";
|
||||
}
|
||||
|
||||
if (!bin.defines.empty()) {
|
||||
ss << "target_add_definitions(" << bin.name << " PUBLIC\n\t";
|
||||
for (const auto &def : bin.defines) {
|
||||
ss << def << "\n\t";
|
||||
}
|
||||
ss << ")\n\n";
|
||||
}
|
||||
|
||||
if (!bin.properties.empty()) {
|
||||
ss << "set_target_properties(" << bin.name << " PROPERTIES\n";
|
||||
for (const auto &prop : bin.properties) {
|
||||
ss << "\t" << prop.first << " " << prop.second << "\n";
|
||||
}
|
||||
ss << "\t)\n\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!cmake.tests.empty()) {
|
||||
ss << "include(CTest)\n"
|
||||
<< "enable_testing()\n\n";
|
||||
for (const auto &test : cmake.tests) {
|
||||
ss << "add_test(NAME " << test.name << " COMMAND " << test.cmd;
|
||||
if (!test.args.empty()) {
|
||||
for (const auto &arg : test.args) {
|
||||
ss << " " << arg;
|
||||
}
|
||||
}
|
||||
ss << ")\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (!cmake.installs.empty()) {
|
||||
for (const auto &inst : cmake.installs) {
|
||||
ss << "install(\n";
|
||||
if (!inst.targets.empty()) {
|
||||
ss << "\tTARGETS ";
|
||||
for (const auto &target : inst.targets) {
|
||||
ss << target << " ";
|
||||
}
|
||||
}
|
||||
if (!inst.dirs.empty()) {
|
||||
ss << "\tDIRS ";
|
||||
for (const auto &dir : inst.dirs) {
|
||||
ss << dir << " ";
|
||||
}
|
||||
}
|
||||
if (!inst.files.empty()) {
|
||||
ss << "\tFILES ";
|
||||
for (const auto &file : inst.files) {
|
||||
auto path = detail::expand_cmake_path(fs::path(file));
|
||||
for (const auto &f : path)
|
||||
ss << f << " ";
|
||||
}
|
||||
}
|
||||
if (!inst.configs.empty()) {
|
||||
ss << "\tCONFIGURATIONS";
|
||||
for (const auto &conf : inst.configs) {
|
||||
ss << conf << " ";
|
||||
}
|
||||
}
|
||||
ss << "\n\tDESTINATION " << inst.destination << "\n\t";
|
||||
if (!inst.targets.empty())
|
||||
ss << "COMPONENT " << inst.targets[0] << "\n\t)\n\n";
|
||||
else
|
||||
ss << "\n\t)\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
ss << "\n\n";
|
||||
// printf("%s\n", ss.str().c_str());
|
||||
|
||||
std::ofstream ofs(fs::path(path) / "CMakeLists.txt");
|
||||
if (ofs.is_open()) {
|
||||
ofs << ss.rdbuf();
|
||||
}
|
||||
ofs.flush();
|
||||
ofs.close();
|
||||
|
||||
for (const auto &sub : cmake.subdirs) {
|
||||
if (fs::exists(fs::path(sub) / "cmake.toml"))
|
||||
generate_cmake(sub.c_str());
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error("[cmkr] error: No cmake.toml found!");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace gen
|
||||
} // namespace cmkr
|
||||
|
||||
int cmkr_gen_generate_project(const char *typ) {
|
||||
try {
|
||||
return cmkr::gen::generate_project(typ);
|
||||
} catch (const std::system_error &e) {
|
||||
return e.code().value();
|
||||
} catch (...) {
|
||||
return cmkr::error::Status(cmkr::error::Status::Code::InitError);
|
||||
}
|
||||
}
|
||||
|
||||
int cmkr_gen_generate_cmake(const char *path) {
|
||||
try {
|
||||
return cmkr::gen::generate_cmake(path);
|
||||
} catch (const std::system_error &e) {
|
||||
return e.code().value();
|
||||
} catch (...) {
|
||||
return cmkr::error::Status(cmkr::error::Status::Code::GenerationError);
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
#include "help.h"
|
||||
|
||||
namespace cmkr {
|
||||
namespace help {
|
||||
|
||||
const char *version() noexcept { return "cmkr version 0.1.3"; }
|
||||
|
||||
const char *message() noexcept {
|
||||
return R"lit(
|
||||
Usage: cmkr [arguments]
|
||||
arguments:
|
||||
init [exe|lib|shared|static|interface] Starts a new project in the same directory.
|
||||
gen Generates CMakeLists.txt file.
|
||||
build <extra cmake args> Run cmake and build.
|
||||
install Run cmake --install. Needs admin privileges.
|
||||
clean Clean the build directory.
|
||||
help Show help.
|
||||
version Current cmkr version.
|
||||
)lit";
|
||||
}
|
||||
} // namespace help
|
||||
} // namespace cmkr
|
||||
|
||||
const char *cmkr_help_version(void) { return cmkr::help::version(); }
|
||||
|
||||
const char *cmkr_help_message(void) { return cmkr::help::message(); }
|
@ -1,13 +1,21 @@
|
||||
#include "args.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(int argc, char **argv) try {
|
||||
auto output = cmkr::args::handle_args(argc, argv);
|
||||
return fprintf(stdout, "%s\n", output) < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
auto format = "[cmkr] %s\n";
|
||||
if (strchr(output, '\n') != nullptr)
|
||||
format = "%s\n";
|
||||
(void)fprintf(stderr, format, output);
|
||||
return EXIT_SUCCESS;
|
||||
} catch (const std::exception &e) {
|
||||
(void)fprintf(stderr, "%s\n", e.what());
|
||||
auto format = "[cmkr] error: %s\n";
|
||||
if (strchr(e.what(), '\n') != nullptr)
|
||||
format = "%s\n";
|
||||
(void)fprintf(stderr, format, e.what());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
@ -0,0 +1,19 @@
|
||||
# This file was generated automatically by cmkr.
|
||||
|
||||
# Create a configure-time dependency on cmake.toml to improve IDE support
|
||||
if(CMKR_ROOT_PROJECT)
|
||||
configure_file(cmake.toml cmake.toml COPYONLY)
|
||||
endif()
|
||||
|
||||
enable_testing()
|
||||
|
||||
add_test(
|
||||
NAME
|
||||
basic
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/basic"
|
||||
COMMAND
|
||||
$<TARGET_FILE:cmkr>
|
||||
build
|
||||
)
|
||||
|
@ -0,0 +1,41 @@
|
||||
# This file was generated automatically by cmkr.
|
||||
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# Regenerate CMakeLists.txt automatically in the root project
|
||||
set(CMKR_ROOT_PROJECT OFF)
|
||||
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||
set(CMKR_ROOT_PROJECT ON)
|
||||
|
||||
# Bootstrap cmkr
|
||||
include(cmkr.cmake OPTIONAL RESULT_VARIABLE CMKR_INCLUDE_RESULT)
|
||||
if(CMKR_INCLUDE_RESULT)
|
||||
cmkr()
|
||||
endif()
|
||||
|
||||
# Enable folder support
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
endif()
|
||||
|
||||
# Create a configure-time dependency on cmake.toml to improve IDE support
|
||||
if(CMKR_ROOT_PROJECT)
|
||||
configure_file(cmake.toml cmake.toml COPYONLY)
|
||||
endif()
|
||||
|
||||
project(basic)
|
||||
|
||||
# Target basic
|
||||
set(basic_SOURCES
|
||||
"src/basic.cpp"
|
||||
cmake.toml
|
||||
)
|
||||
|
||||
add_executable(basic ${basic_SOURCES})
|
||||
|
||||
get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} DEFINITION VS_STARTUP_PROJECT)
|
||||
if(NOT CMKR_VS_STARTUP_PROJECT)
|
||||
set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT basic)
|
||||
endif()
|
||||
|
||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${basic_SOURCES})
|
||||
|
@ -0,0 +1,9 @@
|
||||
[cmake]
|
||||
version = "3.5"
|
||||
|
||||
[project]
|
||||
name = "basic"
|
||||
|
||||
[target.basic]
|
||||
type = "executable"
|
||||
sources = ["src/basic.cpp"]
|
@ -0,0 +1,5 @@
|
||||
#include <cstdio>
|
||||
|
||||
int main() {
|
||||
puts("Hello from cmkr!");
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
[[test]]
|
||||
name = "basic"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
working-directory = "${CMAKE_CURRENT_LIST_DIR}/basic"
|
||||
arguments = ["build"]
|
@ -0,0 +1,18 @@
|
||||
# Third party dependencies (manually vendored to improve fetch speeds)
|
||||
|
||||
# https://github.com/gulrak/filesystem (MIT)
|
||||
add_library(ghc_filesystem INTERFACE)
|
||||
target_include_directories(ghc_filesystem INTERFACE filesystem-1.5.2/include)
|
||||
|
||||
# https://github.com/Tessil/ordered-map (MIT)
|
||||
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/mpark/variant (BSL-1.0)
|
||||
add_library(mpark_variant INTERFACE)
|
||||
target_include_directories(mpark_variant INTERFACE variant-1.4.0/include)
|
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,38 @@
|
||||
//---------------------------------------------------------------------------------------
|
||||
//
|
||||
// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14
|
||||
//
|
||||
//---------------------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
//---------------------------------------------------------------------------------------
|
||||
// fs_fwd.hpp - The forwarding header for the header/implementation seperated usage of
|
||||
// ghc::filesystem.
|
||||
// This file can be include at any place, where ghc::filesystem api is needed while
|
||||
// not bleeding implementation details (e.g. system includes) into the global namespace,
|
||||
// as long as one cpp includes fs_impl.hpp to deliver the matching implementations.
|
||||
//---------------------------------------------------------------------------------------
|
||||
#ifndef GHC_FILESYSTEM_FWD_H
|
||||
#define GHC_FILESYSTEM_FWD_H
|
||||
#define GHC_FILESYSTEM_FWD
|
||||
#include <ghc/filesystem.hpp>
|
||||
#endif // GHC_FILESYSTEM_FWD_H
|
@ -0,0 +1,35 @@
|
||||
//---------------------------------------------------------------------------------------
|
||||
//
|
||||
// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14
|
||||
//
|
||||
//---------------------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
//---------------------------------------------------------------------------------------
|
||||
// fs_impl.hpp - The implementation header for the header/implementation seperated usage of
|
||||
// ghc::filesystem.
|
||||
// This file can be used to hide the implementation of ghc::filesystem into a single cpp.
|
||||
// The cpp has to include this before including fs_fwd.hpp directly or via a different
|
||||
// header to work.
|
||||
//---------------------------------------------------------------------------------------
|
||||
#define GHC_FILESYSTEM_IMPLEMENTATION
|
||||
#include <ghc/filesystem.hpp>
|
@ -0,0 +1,56 @@
|
||||
//---------------------------------------------------------------------------------------
|
||||
//
|
||||
// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14
|
||||
//
|
||||
//---------------------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
//---------------------------------------------------------------------------------------
|
||||
// fs_std.hpp - The dynamic switching header that includes std::filesystem if detected
|
||||
// or ghc::filesystem if not, and makes the resulting API available in the
|
||||
// namespace fs.
|
||||
//---------------------------------------------------------------------------------------
|
||||
#ifndef GHC_FILESYSTEM_STD_H
|
||||
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include)
|
||||
#if __has_include(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
|
||||
#define GHC_USE_STD_FS
|
||||
#include <filesystem>
|
||||
namespace fs {
|
||||
using namespace std::filesystem;
|
||||
using ifstream = std::ifstream;
|
||||
using ofstream = std::ofstream;
|
||||
using fstream = std::fstream;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#ifndef GHC_USE_STD_FS
|
||||
//#define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE
|
||||
#include <ghc/filesystem.hpp>
|
||||
namespace fs {
|
||||
using namespace ghc::filesystem;
|
||||
using ifstream = ghc::filesystem::ifstream;
|
||||
using ofstream = ghc::filesystem::ofstream;
|
||||
using fstream = ghc::filesystem::fstream;
|
||||
}
|
||||
#endif
|
||||
#endif // GHC_FILESYSTEM_STD_H
|
||||
|
@ -0,0 +1,60 @@
|
||||
//---------------------------------------------------------------------------------------
|
||||
//
|
||||
// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14
|
||||
//
|
||||
//---------------------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
//---------------------------------------------------------------------------------------
|
||||
// fs_std_fwd.hpp - The forwarding header for the header/implementation seperated usage of
|
||||
// ghc::filesystem that uses std::filesystem if it detects it.
|
||||
// This file can be include at any place, where fs::filesystem api is needed while
|
||||
// not bleeding implementation details (e.g. system includes) into the global namespace,
|
||||
// as long as one cpp includes fs_std_impl.hpp to deliver the matching implementations.
|
||||
//---------------------------------------------------------------------------------------
|
||||
#ifndef GHC_FILESYSTEM_STD_FWD_H
|
||||
#define GHC_FILESYSTEM_STD_FWD_H
|
||||
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include)
|
||||
#if __has_include(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
|
||||
#define GHC_USE_STD_FS
|
||||
#include <filesystem>
|
||||
namespace fs {
|
||||
using namespace std::filesystem;
|
||||
using ifstream = std::ifstream;
|
||||
using ofstream = std::ofstream;
|
||||
using fstream = std::fstream;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#ifndef GHC_USE_STD_FS
|
||||
//#define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE
|
||||
#define GHC_FILESYSTEM_FWD
|
||||
#include <ghc/filesystem.hpp>
|
||||
namespace fs {
|
||||
using namespace ghc::filesystem;
|
||||
using ifstream = ghc::filesystem::ifstream;
|
||||
using ofstream = ghc::filesystem::ofstream;
|
||||
using fstream = ghc::filesystem::fstream;
|
||||
}
|
||||
#endif
|
||||
#endif // GHC_FILESYSTEM_STD_FWD_H
|
||||
|
@ -0,0 +1,43 @@
|
||||
//---------------------------------------------------------------------------------------
|
||||
//
|
||||
// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14
|
||||
//
|
||||
//---------------------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
//---------------------------------------------------------------------------------------
|
||||
// fs_std_impl.hpp - The implementation header for the header/implementation seperated usage of
|
||||
// ghc::filesystem that does nothing if std::filesystem is detected.
|
||||
// This file can be used to hide the implementation of ghc::filesystem into a single cpp.
|
||||
// The cpp has to include this before including fs_std_fwd.hpp directly or via a different
|
||||
// header to work.
|
||||
//---------------------------------------------------------------------------------------
|
||||
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include)
|
||||
#if __has_include(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
|
||||
#define GHC_USE_STD_FS
|
||||
#endif
|
||||
#endif
|
||||
#ifndef GHC_USE_STD_FS
|
||||
//#define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE
|
||||
#define GHC_FILESYSTEM_IMPLEMENTATION
|
||||
#include <ghc/filesystem.hpp>
|
||||
#endif
|
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,863 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef TSL_ORDERED_MAP_H
|
||||
#define TSL_ORDERED_MAP_H
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "ordered_hash.h"
|
||||
|
||||
|
||||
namespace tsl {
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of an hash map using open addressing with robin hood with backshift delete to resolve collisions.
|
||||
*
|
||||
* The particularity of this hash map is that it remembers the order in which the elements were added and
|
||||
* provide a way to access the structure which stores these values through the 'values_container()' method.
|
||||
* The used container is defined by ValueTypeContainer, by default a std::deque is used (grows faster) but
|
||||
* a std::vector may be used. In this case the map provides a 'data()' method which give a direct access
|
||||
* to the memory used to store the values (which can be useful to communicate with C API's).
|
||||
*
|
||||
* The Key and T must be copy constructible and/or move constructible. To use `unordered_erase` they both
|
||||
* must be swappable.
|
||||
*
|
||||
* The behaviour of the hash map is undefined if the destructor of Key or T throws an exception.
|
||||
*
|
||||
* By default the maximum size of a map is limited to 2^32 - 1 values, if needed this can be changed through
|
||||
* the IndexType template parameter. Using an `uint64_t` will raise this limit to 2^64 - 1 values but each
|
||||
* bucket will use 16 bytes instead of 8 bytes in addition to the space needed to store the values.
|
||||
*
|
||||
* Iterators invalidation:
|
||||
* - clear, operator=, reserve, rehash: always invalidate the iterators (also invalidate end()).
|
||||
* - insert, emplace, emplace_hint, operator[]: when a std::vector is used as ValueTypeContainer
|
||||
* and if size() < capacity(), only end().
|
||||
* Otherwise all the iterators are invalidated if an insert occurs.
|
||||
* - erase, unordered_erase: when a std::vector is used as ValueTypeContainer invalidate the iterator of
|
||||
* the erased element and all the ones after the erased element (including end()).
|
||||
* Otherwise all the iterators are invalidated if an erase occurs.
|
||||
*/
|
||||
template<class Key,
|
||||
class T,
|
||||
class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key, T>>,
|
||||
class ValueTypeContainer = std::deque<std::pair<Key, T>, Allocator>,
|
||||
class IndexType = std::uint_least32_t>
|
||||
class ordered_map {
|
||||
private:
|
||||
template<typename U>
|
||||
using has_is_transparent = tsl::detail_ordered_hash::has_is_transparent<U>;
|
||||
|
||||
class KeySelect {
|
||||
public:
|
||||
using key_type = Key;
|
||||
|
||||
const key_type& operator()(const std::pair<Key, T>& key_value) const noexcept {
|
||||
return key_value.first;
|
||||
}
|
||||
|
||||
key_type& operator()(std::pair<Key, T>& key_value) noexcept {
|
||||
return key_value.first;
|
||||
}
|
||||
};
|
||||
|
||||
class ValueSelect {
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
const value_type& operator()(const std::pair<Key, T>& key_value) const noexcept {
|
||||
return key_value.second;
|
||||
}
|
||||
|
||||
value_type& operator()(std::pair<Key, T>& key_value) noexcept {
|
||||
return key_value.second;
|
||||
}
|
||||
};
|
||||
|
||||
using ht = detail_ordered_hash::ordered_hash<std::pair<Key, T>, KeySelect, ValueSelect,
|
||||
Hash, KeyEqual, Allocator, ValueTypeContainer, IndexType>;
|
||||
|
||||
public:
|
||||
using key_type = typename ht::key_type;
|
||||
using mapped_type = T;
|
||||
using value_type = typename ht::value_type;
|
||||
using size_type = typename ht::size_type;
|
||||
using difference_type = typename ht::difference_type;
|
||||
using hasher = typename ht::hasher;
|
||||
using key_equal = typename ht::key_equal;
|
||||
using allocator_type = typename ht::allocator_type;
|
||||
using reference = typename ht::reference;
|
||||
using const_reference = typename ht::const_reference;
|
||||
using pointer = typename ht::pointer;
|
||||
using const_pointer = typename ht::const_pointer;
|
||||
using iterator = typename ht::iterator;
|
||||
using const_iterator = typename ht::const_iterator;
|
||||
using reverse_iterator = typename ht::reverse_iterator;
|
||||
using const_reverse_iterator = typename ht::const_reverse_iterator;
|
||||
|
||||
using values_container_type = typename ht::values_container_type;
|
||||
|
||||
|
||||
/*
|
||||
* Constructors
|
||||
*/
|
||||
ordered_map(): ordered_map(ht::DEFAULT_INIT_BUCKETS_SIZE) {
|
||||
}
|
||||
|
||||
explicit ordered_map(size_type bucket_count,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()):
|
||||
m_ht(bucket_count, hash, equal, alloc, ht::DEFAULT_MAX_LOAD_FACTOR)
|
||||
{
|
||||
}
|
||||
|
||||
ordered_map(size_type bucket_count,
|
||||
const Allocator& alloc): ordered_map(bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
ordered_map(size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc): ordered_map(bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
explicit ordered_map(const Allocator& alloc): ordered_map(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) {
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
ordered_map(InputIt first, InputIt last,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()): ordered_map(bucket_count, hash, equal, alloc)
|
||||
{
|
||||
insert(first, last);
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
ordered_map(InputIt first, InputIt last,
|
||||
size_type bucket_count,
|
||||
const Allocator& alloc): ordered_map(first, last, bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
ordered_map(InputIt first, InputIt last,
|
||||
size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc): ordered_map(first, last, bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
ordered_map(std::initializer_list<value_type> init,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()):
|
||||
ordered_map(init.begin(), init.end(), bucket_count, hash, equal, alloc)
|
||||
{
|
||||
}
|
||||
|
||||
ordered_map(std::initializer_list<value_type> init,
|
||||
size_type bucket_count,
|
||||
const Allocator& alloc):
|
||||
ordered_map(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
ordered_map(std::initializer_list<value_type> init,
|
||||
size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc):
|
||||
ordered_map(init.begin(), init.end(), bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
ordered_map& operator=(std::initializer_list<value_type> ilist) {
|
||||
m_ht.clear();
|
||||
|
||||
m_ht.reserve(ilist.size());
|
||||
m_ht.insert(ilist.begin(), ilist.end());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
allocator_type get_allocator() const { return m_ht.get_allocator(); }
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Iterators
|
||||
*/
|
||||
iterator begin() noexcept { return m_ht.begin(); }
|
||||
const_iterator begin() const noexcept { return m_ht.begin(); }
|
||||
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
|
||||
|
||||
iterator end() noexcept { return m_ht.end(); }
|
||||
const_iterator end() const noexcept { return m_ht.end(); }
|
||||
const_iterator cend() const noexcept { return m_ht.cend(); }
|
||||
|
||||
reverse_iterator rbegin() noexcept { return m_ht.rbegin(); }
|
||||
const_reverse_iterator rbegin() const noexcept { return m_ht.rbegin(); }
|
||||
const_reverse_iterator rcbegin() const noexcept { return m_ht.rcbegin(); }
|
||||
|
||||
reverse_iterator rend() noexcept { return m_ht.rend(); }
|
||||
const_reverse_iterator rend() const noexcept { return m_ht.rend(); }
|
||||
const_reverse_iterator rcend() const noexcept { return m_ht.rcend(); }
|
||||
|
||||
|
||||
/*
|
||||
* Capacity
|
||||
*/
|
||||
bool empty() const noexcept { return m_ht.empty(); }
|
||||
size_type size() const noexcept { return m_ht.size(); }
|
||||
size_type max_size() const noexcept { return m_ht.max_size(); }
|
||||
|
||||
/*
|
||||
* Modifiers
|
||||
*/
|
||||
void clear() noexcept { m_ht.clear(); }
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, bool> insert(const value_type& value) { return m_ht.insert(value); }
|
||||
|
||||
template<class P, typename std::enable_if<std::is_constructible<value_type, P&&>::value>::type* = nullptr>
|
||||
std::pair<iterator, bool> insert(P&& value) { return m_ht.emplace(std::forward<P>(value)); }
|
||||
|
||||
std::pair<iterator, bool> insert(value_type&& value) { return m_ht.insert(std::move(value)); }
|
||||
|
||||
|
||||
iterator insert(const_iterator hint, const value_type& value) {
|
||||
return m_ht.insert_hint(hint, value);
|
||||
}
|
||||
|
||||
template<class P, typename std::enable_if<std::is_constructible<value_type, P&&>::value>::type* = nullptr>
|
||||
iterator insert(const_iterator hint, P&& value) {
|
||||
return m_ht.emplace_hint(hint, std::forward<P>(value));
|
||||
}
|
||||
|
||||
iterator insert(const_iterator hint, value_type&& value) {
|
||||
return m_ht.insert_hint(hint, std::move(value));
|
||||
}
|
||||
|
||||
|
||||
template<class InputIt>
|
||||
void insert(InputIt first, InputIt last) { m_ht.insert(first, last); }
|
||||
void insert(std::initializer_list<value_type> ilist) { m_ht.insert(ilist.begin(), ilist.end()); }
|
||||
|
||||
|
||||
|
||||
|
||||
template<class M>
|
||||
std::pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj) {
|
||||
return m_ht.insert_or_assign(k, std::forward<M>(obj));
|
||||
}
|
||||
|
||||
template<class M>
|
||||
std::pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj) {
|
||||
return m_ht.insert_or_assign(std::move(k), std::forward<M>(obj));
|
||||
}
|
||||
|
||||
|
||||
template<class M>
|
||||
iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj) {
|
||||
return m_ht.insert_or_assign(hint, k, std::forward<M>(obj));
|
||||
}
|
||||
|
||||
template<class M>
|
||||
iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj) {
|
||||
return m_ht.insert_or_assign(hint, std::move(k), std::forward<M>(obj));
|
||||
}
|
||||
|
||||
/**
|
||||
* Due to the way elements are stored, emplace will need to move or copy the key-value once.
|
||||
* The method is equivalent to insert(value_type(std::forward<Args>(args)...));
|
||||
*
|
||||
* Mainly here for compatibility with the std::unordered_map interface.
|
||||
*/
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(Args&&... args) { return m_ht.emplace(std::forward<Args>(args)...); }
|
||||
|
||||
/**
|
||||
* Due to the way elements are stored, emplace_hint will need to move or copy the key-value once.
|
||||
* The method is equivalent to insert(hint, value_type(std::forward<Args>(args)...));
|
||||
*
|
||||
* Mainly here for compatibility with the std::unordered_map interface.
|
||||
*/
|
||||
template <class... Args>
|
||||
iterator emplace_hint(const_iterator hint, Args&&... args) {
|
||||
return m_ht.emplace_hint(hint, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> try_emplace(const key_type& k, Args&&... args) {
|
||||
return m_ht.try_emplace(k, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> try_emplace(key_type&& k, Args&&... args) {
|
||||
return m_ht.try_emplace(std::move(k), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args) {
|
||||
return m_ht.try_emplace_hint(hint, k, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args) {
|
||||
return m_ht.try_emplace_hint(hint, std::move(k), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* When erasing an element, the insert order will be preserved and no holes will be present in the container
|
||||
* returned by 'values_container()'.
|
||||
*
|
||||
* The method is in O(n), if the order is not important 'unordered_erase(...)' method is faster with an O(1)
|
||||
* average complexity.
|
||||
*/
|
||||
iterator erase(iterator pos) { return m_ht.erase(pos); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(iterator pos)
|
||||
*/
|
||||
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(iterator pos)
|
||||
*/
|
||||
iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(iterator pos)
|
||||
*/
|
||||
size_type erase(const key_type& key) { return m_ht.erase(key); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(iterator pos)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
size_type erase(const key_type& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc erase(iterator pos)
|
||||
*
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type erase(const K& key) { return m_ht.erase(key); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(const key_type& key, std::size_t precalculated_hash)
|
||||
*
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type erase(const K& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void swap(ordered_map& other) { other.m_ht.swap(m_ht); }
|
||||
|
||||
/*
|
||||
* Lookup
|
||||
*/
|
||||
T& at(const Key& key) { return m_ht.at(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
T& at(const Key& key, std::size_t precalculated_hash) { return m_ht.at(key, precalculated_hash); }
|
||||
|
||||
|
||||
const T& at(const Key& key) const { return m_ht.at(key); }
|
||||
|
||||
/**
|
||||
* @copydoc at(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
const T& at(const Key& key, std::size_t precalculated_hash) const { return m_ht.at(key, precalculated_hash); }
|
||||
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
T& at(const K& key) { return m_ht.at(key); }
|
||||
|
||||
/**
|
||||
* @copydoc at(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
T& at(const K& key, std::size_t precalculated_hash) { return m_ht.at(key, precalculated_hash); }
|
||||
|
||||
/**
|
||||
* @copydoc at(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const T& at(const K& key) const { return m_ht.at(key); }
|
||||
|
||||
/**
|
||||
* @copydoc at(const K& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const T& at(const K& key, std::size_t precalculated_hash) const { return m_ht.at(key, precalculated_hash); }
|
||||
|
||||
|
||||
|
||||
T& operator[](const Key& key) { return m_ht[key]; }
|
||||
T& operator[](Key&& key) { return m_ht[std::move(key)]; }
|
||||
|
||||
|
||||
|
||||
size_type count(const Key& key) const { return m_ht.count(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
size_type count(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.count(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type count(const K& key) const { return m_ht.count(key); }
|
||||
|
||||
/**
|
||||
* @copydoc count(const K& key) const
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type count(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.count(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
iterator find(const Key& key) { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
iterator find(const Key& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
const_iterator find(const Key& key) const { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
const_iterator find(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
iterator find(const K& key) { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
iterator find(const K& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const_iterator find(const K& key) const { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const_iterator find(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool contains(const Key& key) const { return m_ht.contains(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
bool contains(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.contains(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
bool contains(const K& key) const { return m_ht.contains(key); }
|
||||
|
||||
/**
|
||||
* @copydoc contains(const K& key) const
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
bool contains(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.contains(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, iterator> equal_range(const Key& key) { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range(const Key& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const Key& key) const { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<iterator, iterator> equal_range(const K& key) { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<iterator, iterator> equal_range(const K& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<const_iterator, const_iterator> equal_range(const K& key) const { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<const_iterator, const_iterator> equal_range(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Bucket interface
|
||||
*/
|
||||
size_type bucket_count() const { return m_ht.bucket_count(); }
|
||||
size_type max_bucket_count() const { return m_ht.max_bucket_count(); }
|
||||
|
||||
|
||||
/*
|
||||
* Hash policy
|
||||
*/
|
||||
float load_factor() const { return m_ht.load_factor(); }
|
||||
float max_load_factor() const { return m_ht.max_load_factor(); }
|
||||
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
|
||||
|
||||
void rehash(size_type count) { m_ht.rehash(count); }
|
||||
void reserve(size_type count) { m_ht.reserve(count); }
|
||||
|
||||
|
||||
/*
|
||||
* Observers
|
||||
*/
|
||||
hasher hash_function() const { return m_ht.hash_function(); }
|
||||
key_equal key_eq() const { return m_ht.key_eq(); }
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Other
|
||||
*/
|
||||
|
||||
/**
|
||||
* Convert a const_iterator to an iterator.
|
||||
*/
|
||||
iterator mutable_iterator(const_iterator pos) {
|
||||
return m_ht.mutable_iterator(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requires index <= size().
|
||||
*
|
||||
* Return an iterator to the element at index. Return end() if index == size().
|
||||
*/
|
||||
iterator nth(size_type index) { return m_ht.nth(index); }
|
||||
|
||||
/**
|
||||
* @copydoc nth(size_type index)
|
||||
*/
|
||||
const_iterator nth(size_type index) const { return m_ht.nth(index); }
|
||||
|
||||
|
||||
/**
|
||||
* Return const_reference to the first element. Requires the container to not be empty.
|
||||
*/
|
||||
const_reference front() const { return m_ht.front(); }
|
||||
|
||||
/**
|
||||
* Return const_reference to the last element. Requires the container to not be empty.
|
||||
*/
|
||||
const_reference back() const { return m_ht.back(); }
|
||||
|
||||
|
||||
/**
|
||||
* Only available if ValueTypeContainer is a std::vector. Same as calling 'values_container().data()'.
|
||||
*/
|
||||
template<class U = values_container_type, typename std::enable_if<tsl::detail_ordered_hash::is_vector<U>::value>::type* = nullptr>
|
||||
const typename values_container_type::value_type* data() const noexcept { return m_ht.data(); }
|
||||
|
||||
/**
|
||||
* Return the container in which the values are stored. The values are in the same order as the insertion order
|
||||
* and are contiguous in the structure, no holes (size() == values_container().size()).
|
||||
*/
|
||||
const values_container_type& values_container() const noexcept { return m_ht.values_container(); }
|
||||
|
||||
template<class U = values_container_type, typename std::enable_if<tsl::detail_ordered_hash::is_vector<U>::value>::type* = nullptr>
|
||||
size_type capacity() const noexcept { return m_ht.capacity(); }
|
||||
|
||||
void shrink_to_fit() { m_ht.shrink_to_fit(); }
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Insert the value before pos shifting all the elements on the right of pos (including pos) one position
|
||||
* to the right.
|
||||
*
|
||||
* Amortized linear time-complexity in the distance between pos and end().
|
||||
*/
|
||||
std::pair<iterator, bool> insert_at_position(const_iterator pos, const value_type& value) {
|
||||
return m_ht.insert_at_position(pos, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc insert_at_position(const_iterator pos, const value_type& value)
|
||||
*/
|
||||
std::pair<iterator, bool> insert_at_position(const_iterator pos, value_type&& value) {
|
||||
return m_ht.insert_at_position(pos, std::move(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc insert_at_position(const_iterator pos, const value_type& value)
|
||||
*
|
||||
* Same as insert_at_position(pos, value_type(std::forward<Args>(args)...), mainly
|
||||
* here for coherence.
|
||||
*/
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace_at_position(const_iterator pos, Args&&... args) {
|
||||
return m_ht.emplace_at_position(pos, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc insert_at_position(const_iterator pos, const value_type& value)
|
||||
*/
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> try_emplace_at_position(const_iterator pos, const key_type& k, Args&&... args) {
|
||||
return m_ht.try_emplace_at_position(pos, k, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc insert_at_position(const_iterator pos, const value_type& value)
|
||||
*/
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> try_emplace_at_position(const_iterator pos, key_type&& k, Args&&... args) {
|
||||
return m_ht.try_emplace_at_position(pos, std::move(k), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void pop_back() { m_ht.pop_back(); }
|
||||
|
||||
/**
|
||||
* Faster erase operation with an O(1) average complexity but it doesn't preserve the insertion order.
|
||||
*
|
||||
* If an erasure occurs, the last element of the map will take the place of the erased element.
|
||||
*/
|
||||
iterator unordered_erase(iterator pos) { return m_ht.unordered_erase(pos); }
|
||||
|
||||
/**
|
||||
* @copydoc unordered_erase(iterator pos)
|
||||
*/
|
||||
iterator unordered_erase(const_iterator pos) { return m_ht.unordered_erase(pos); }
|
||||
|
||||
/**
|
||||
* @copydoc unordered_erase(iterator pos)
|
||||
*/
|
||||
size_type unordered_erase(const key_type& key) { return m_ht.unordered_erase(key); }
|
||||
|
||||
/**
|
||||
* @copydoc unordered_erase(iterator pos)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
size_type unordered_erase(const key_type& key, std::size_t precalculated_hash) {
|
||||
return m_ht.unordered_erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc unordered_erase(iterator pos)
|
||||
*
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type unordered_erase(const K& key) { return m_ht.unordered_erase(key); }
|
||||
|
||||
/**
|
||||
* @copydoc unordered_erase(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type unordered_erase(const K& key, std::size_t precalculated_hash) {
|
||||
return m_ht.unordered_erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the map through the `serializer` parameter.
|
||||
*
|
||||
* The `serializer` parameter must be a function object that supports the following call:
|
||||
* - `template<typename U> void operator()(const U& value);` where the types `std::uint64_t`, `float` and `std::pair<Key, T>` must be supported for U.
|
||||
*
|
||||
* The implementation leaves binary compatibility (endianness, IEEE 754 for floats, ...) of the types it serializes
|
||||
* in the hands of the `Serializer` function object if compatibility is required.
|
||||
*/
|
||||
template<class Serializer>
|
||||
void serialize(Serializer& serializer) const {
|
||||
m_ht.serialize(serializer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize a previously serialized map through the `deserializer` parameter.
|
||||
*
|
||||
* The `deserializer` parameter must be a function object that supports the following calls:
|
||||
* - `template<typename U> U operator()();` where the types `std::uint64_t`, `float` and `std::pair<Key, T>` must be supported for U.
|
||||
*
|
||||
* If the deserialized hash map type is hash compatible with the serialized map, the deserialization process can be
|
||||
* sped up by setting `hash_compatible` to true. To be hash compatible, the Hash and KeyEqual must behave the same way
|
||||
* than the ones used on the serialized map. The `std::size_t` must also be of the same size as the one on the platform used
|
||||
* to serialize the map, the same apply for `IndexType`. If these criteria are not met, the behaviour is undefined with
|
||||
* `hash_compatible` sets to true.
|
||||
*
|
||||
* The behaviour is undefined if the type `Key` and `T` of the `ordered_map` are not the same as the
|
||||
* types used during serialization.
|
||||
*
|
||||
* The implementation leaves binary compatibility (endianness, IEEE 754 for floats, size of int, ...) of the types it
|
||||
* deserializes in the hands of the `Deserializer` function object if compatibility is required.
|
||||
*/
|
||||
template<class Deserializer>
|
||||
static ordered_map deserialize(Deserializer& deserializer, bool hash_compatible = false) {
|
||||
ordered_map map(0);
|
||||
map.m_ht.deserialize(deserializer, hash_compatible);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
|
||||
friend bool operator==(const ordered_map& lhs, const ordered_map& rhs) { return lhs.m_ht == rhs.m_ht; }
|
||||
friend bool operator!=(const ordered_map& lhs, const ordered_map& rhs) { return lhs.m_ht != rhs.m_ht; }
|
||||
friend bool operator<(const ordered_map& lhs, const ordered_map& rhs) { return lhs.m_ht < rhs.m_ht; }
|
||||
friend bool operator<=(const ordered_map& lhs, const ordered_map& rhs) { return lhs.m_ht <= rhs.m_ht; }
|
||||
friend bool operator>(const ordered_map& lhs, const ordered_map& rhs) { return lhs.m_ht > rhs.m_ht; }
|
||||
friend bool operator>=(const ordered_map& lhs, const ordered_map& rhs) { return lhs.m_ht >= rhs.m_ht; }
|
||||
|
||||
friend void swap(ordered_map& lhs, ordered_map& rhs) { lhs.swap(rhs); }
|
||||
|
||||
private:
|
||||
ht m_ht;
|
||||
};
|
||||
|
||||
} // end namespace tsl
|
||||
|
||||
#endif
|
@ -0,0 +1,718 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef TSL_ORDERED_SET_H
|
||||
#define TSL_ORDERED_SET_H
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "ordered_hash.h"
|
||||
|
||||
|
||||
namespace tsl {
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of an hash set using open addressing with robin hood with backshift delete to resolve collisions.
|
||||
*
|
||||
* The particularity of this hash set is that it remembers the order in which the elements were added and
|
||||
* provide a way to access the structure which stores these values through the 'values_container()' method.
|
||||
* The used container is defined by ValueTypeContainer, by default a std::deque is used (grows faster) but
|
||||
* a std::vector may be used. In this case the set provides a 'data()' method which give a direct access
|
||||
* to the memory used to store the values (which can be useful to communicate with C API's).
|
||||
*
|
||||
* The Key must be copy constructible and/or move constructible. To use `unordered_erase` it also must be swappable.
|
||||
*
|
||||
* The behaviour of the hash set is undefined if the destructor of Key throws an exception.
|
||||
*
|
||||
* By default the maximum size of a set is limited to 2^32 - 1 values, if needed this can be changed through
|
||||
* the IndexType template parameter. Using an `uint64_t` will raise this limit to 2^64 - 1 values but each
|
||||
* bucket will use 16 bytes instead of 8 bytes in addition to the space needed to store the values.
|
||||
*
|
||||
* Iterators invalidation:
|
||||
* - clear, operator=, reserve, rehash: always invalidate the iterators (also invalidate end()).
|
||||
* - insert, emplace, emplace_hint, operator[]: when a std::vector is used as ValueTypeContainer
|
||||
* and if size() < capacity(), only end().
|
||||
* Otherwise all the iterators are invalidated if an insert occurs.
|
||||
* - erase, unordered_erase: when a std::vector is used as ValueTypeContainer invalidate the iterator of
|
||||
* the erased element and all the ones after the erased element (including end()).
|
||||
* Otherwise all the iterators are invalidated if an erase occurs.
|
||||
*/
|
||||
template<class Key,
|
||||
class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<Key>,
|
||||
class ValueTypeContainer = std::deque<Key, Allocator>,
|
||||
class IndexType = std::uint_least32_t>
|
||||
class ordered_set {
|
||||
private:
|
||||
template<typename U>
|
||||
using has_is_transparent = tsl::detail_ordered_hash::has_is_transparent<U>;
|
||||
|
||||
class KeySelect {
|
||||
public:
|
||||
using key_type = Key;
|
||||
|
||||
const key_type& operator()(const Key& key) const noexcept {
|
||||
return key;
|
||||
}
|
||||
|
||||
key_type& operator()(Key& key) noexcept {
|
||||
return key;
|
||||
}
|
||||
};
|
||||
|
||||
using ht = detail_ordered_hash::ordered_hash<Key, KeySelect, void,
|
||||
Hash, KeyEqual, Allocator, ValueTypeContainer, IndexType>;
|
||||
|
||||
public:
|
||||
using key_type = typename ht::key_type;
|
||||
using value_type = typename ht::value_type;
|
||||
using size_type = typename ht::size_type;
|
||||
using difference_type = typename ht::difference_type;
|
||||
using hasher = typename ht::hasher;
|
||||
using key_equal = typename ht::key_equal;
|
||||
using allocator_type = typename ht::allocator_type;
|
||||
using reference = typename ht::reference;
|
||||
using const_reference = typename ht::const_reference;
|
||||
using pointer = typename ht::pointer;
|
||||
using const_pointer = typename ht::const_pointer;
|
||||
using iterator = typename ht::iterator;
|
||||
using const_iterator = typename ht::const_iterator;
|
||||
using reverse_iterator = typename ht::reverse_iterator;
|
||||
using const_reverse_iterator = typename ht::const_reverse_iterator;
|
||||
|
||||
using values_container_type = typename ht::values_container_type;
|
||||
|
||||
|
||||
/*
|
||||
* Constructors
|
||||
*/
|
||||
ordered_set(): ordered_set(ht::DEFAULT_INIT_BUCKETS_SIZE) {
|
||||
}
|
||||
|
||||
explicit ordered_set(size_type bucket_count,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()):
|
||||
m_ht(bucket_count, hash, equal, alloc, ht::DEFAULT_MAX_LOAD_FACTOR)
|
||||
{
|
||||
}
|
||||
|
||||
ordered_set(size_type bucket_count,
|
||||
const Allocator& alloc): ordered_set(bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
ordered_set(size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc): ordered_set(bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
explicit ordered_set(const Allocator& alloc): ordered_set(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) {
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
ordered_set(InputIt first, InputIt last,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()): ordered_set(bucket_count, hash, equal, alloc)
|
||||
{
|
||||
insert(first, last);
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
ordered_set(InputIt first, InputIt last,
|
||||
size_type bucket_count,
|
||||
const Allocator& alloc): ordered_set(first, last, bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
ordered_set(InputIt first, InputIt last,
|
||||
size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc): ordered_set(first, last, bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
ordered_set(std::initializer_list<value_type> init,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE,
|
||||
const Hash& hash = Hash(),
|
||||
const KeyEqual& equal = KeyEqual(),
|
||||
const Allocator& alloc = Allocator()):
|
||||
ordered_set(init.begin(), init.end(), bucket_count, hash, equal, alloc)
|
||||
{
|
||||
}
|
||||
|
||||
ordered_set(std::initializer_list<value_type> init,
|
||||
size_type bucket_count,
|
||||
const Allocator& alloc):
|
||||
ordered_set(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
ordered_set(std::initializer_list<value_type> init,
|
||||
size_type bucket_count,
|
||||
const Hash& hash,
|
||||
const Allocator& alloc):
|
||||
ordered_set(init.begin(), init.end(), bucket_count, hash, KeyEqual(), alloc)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
ordered_set& operator=(std::initializer_list<value_type> ilist) {
|
||||
m_ht.clear();
|
||||
|
||||
m_ht.reserve(ilist.size());
|
||||
m_ht.insert(ilist.begin(), ilist.end());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
allocator_type get_allocator() const { return m_ht.get_allocator(); }
|
||||
|
||||
|
||||
/*
|
||||
* Iterators
|
||||
*/
|
||||
iterator begin() noexcept { return m_ht.begin(); }
|
||||
const_iterator begin() const noexcept { return m_ht.begin(); }
|
||||
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
|
||||
|
||||
iterator end() noexcept { return m_ht.end(); }
|
||||
const_iterator end() const noexcept { return m_ht.end(); }
|
||||
const_iterator cend() const noexcept { return m_ht.cend(); }
|
||||
|
||||
reverse_iterator rbegin() noexcept { return m_ht.rbegin(); }
|
||||
const_reverse_iterator rbegin() const noexcept { return m_ht.rbegin(); }
|
||||
const_reverse_iterator rcbegin() const noexcept { return m_ht.rcbegin(); }
|
||||
|
||||
reverse_iterator rend() noexcept { return m_ht.rend(); }
|
||||
const_reverse_iterator rend() const noexcept { return m_ht.rend(); }
|
||||
const_reverse_iterator rcend() const noexcept { return m_ht.rcend(); }
|
||||
|
||||
|
||||
/*
|
||||
* Capacity
|
||||
*/
|
||||
bool empty() const noexcept { return m_ht.empty(); }
|
||||
size_type size() const noexcept { return m_ht.size(); }
|
||||
size_type max_size() const noexcept { return m_ht.max_size(); }
|
||||
|
||||
/*
|
||||
* Modifiers
|
||||
*/
|
||||
void clear() noexcept { m_ht.clear(); }
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, bool> insert(const value_type& value) { return m_ht.insert(value); }
|
||||
std::pair<iterator, bool> insert(value_type&& value) { return m_ht.insert(std::move(value)); }
|
||||
|
||||
iterator insert(const_iterator hint, const value_type& value) {
|
||||
return m_ht.insert_hint(hint, value);
|
||||
}
|
||||
|
||||
iterator insert(const_iterator hint, value_type&& value) {
|
||||
return m_ht.insert_hint(hint, std::move(value));
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
void insert(InputIt first, InputIt last) { m_ht.insert(first, last); }
|
||||
void insert(std::initializer_list<value_type> ilist) { m_ht.insert(ilist.begin(), ilist.end()); }
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Due to the way elements are stored, emplace will need to move or copy the key-value once.
|
||||
* The method is equivalent to insert(value_type(std::forward<Args>(args)...));
|
||||
*
|
||||
* Mainly here for compatibility with the std::unordered_map interface.
|
||||
*/
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(Args&&... args) { return m_ht.emplace(std::forward<Args>(args)...); }
|
||||
|
||||
/**
|
||||
* Due to the way elements are stored, emplace_hint will need to move or copy the key-value once.
|
||||
* The method is equivalent to insert(hint, value_type(std::forward<Args>(args)...));
|
||||
*
|
||||
* Mainly here for compatibility with the std::unordered_map interface.
|
||||
*/
|
||||
template<class... Args>
|
||||
iterator emplace_hint(const_iterator hint, Args&&... args) {
|
||||
return m_ht.emplace_hint(hint, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* When erasing an element, the insert order will be preserved and no holes will be present in the container
|
||||
* returned by 'values_container()'.
|
||||
*
|
||||
* The method is in O(n), if the order is not important 'unordered_erase(...)' method is faster with an O(1)
|
||||
* average complexity.
|
||||
*/
|
||||
iterator erase(iterator pos) { return m_ht.erase(pos); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(iterator pos)
|
||||
*/
|
||||
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(iterator pos)
|
||||
*/
|
||||
iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(iterator pos)
|
||||
*/
|
||||
size_type erase(const key_type& key) { return m_ht.erase(key); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(iterator pos)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
size_type erase(const key_type& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc erase(iterator pos)
|
||||
*
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type erase(const K& key) { return m_ht.erase(key); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(const key_type& key, std::size_t precalculated_hash)
|
||||
*
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type erase(const K& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void swap(ordered_set& other) { other.m_ht.swap(m_ht); }
|
||||
|
||||
/*
|
||||
* Lookup
|
||||
*/
|
||||
size_type count(const Key& key) const { return m_ht.count(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
size_type count(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.count(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type count(const K& key) const { return m_ht.count(key); }
|
||||
|
||||
/**
|
||||
* @copydoc count(const K& key) const
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type count(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.count(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
iterator find(const Key& key) { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
iterator find(const Key& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
const_iterator find(const Key& key) const { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
const_iterator find(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
iterator find(const K& key) { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
iterator find(const K& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const_iterator find(const K& key) const { return m_ht.find(key); }
|
||||
|
||||
/**
|
||||
* @copydoc find(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
const_iterator find(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool contains(const Key& key) const { return m_ht.contains(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
bool contains(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.contains(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
bool contains(const K& key) const { return m_ht.contains(key); }
|
||||
|
||||
/**
|
||||
* @copydoc contains(const K& key) const
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
bool contains(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.contains(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::pair<iterator, iterator> equal_range(const Key& key) { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range(const Key& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const Key& key) const { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const Key& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range(const Key& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<iterator, iterator> equal_range(const K& key) { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<iterator, iterator> equal_range(const K& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<const_iterator, const_iterator> equal_range(const K& key) const { return m_ht.equal_range(key); }
|
||||
|
||||
/**
|
||||
* @copydoc equal_range(const K& key, std::size_t precalculated_hash)
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
std::pair<const_iterator, const_iterator> equal_range(const K& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Bucket interface
|
||||
*/
|
||||
size_type bucket_count() const { return m_ht.bucket_count(); }
|
||||
size_type max_bucket_count() const { return m_ht.max_bucket_count(); }
|
||||
|
||||
|
||||
/*
|
||||
* Hash policy
|
||||
*/
|
||||
float load_factor() const { return m_ht.load_factor(); }
|
||||
float max_load_factor() const { return m_ht.max_load_factor(); }
|
||||
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
|
||||
|
||||
void rehash(size_type count) { m_ht.rehash(count); }
|
||||
void reserve(size_type count) { m_ht.reserve(count); }
|
||||
|
||||
|
||||
/*
|
||||
* Observers
|
||||
*/
|
||||
hasher hash_function() const { return m_ht.hash_function(); }
|
||||
key_equal key_eq() const { return m_ht.key_eq(); }
|
||||
|
||||
|
||||
/*
|
||||
* Other
|
||||
*/
|
||||
|
||||
/**
|
||||
* Convert a const_iterator to an iterator.
|
||||
*/
|
||||
iterator mutable_iterator(const_iterator pos) {
|
||||
return m_ht.mutable_iterator(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requires index <= size().
|
||||
*
|
||||
* Return an iterator to the element at index. Return end() if index == size().
|
||||
*/
|
||||
iterator nth(size_type index) { return m_ht.nth(index); }
|
||||
|
||||
/**
|
||||
* @copydoc nth(size_type index)
|
||||
*/
|
||||
const_iterator nth(size_type index) const { return m_ht.nth(index); }
|
||||
|
||||
|
||||
/**
|
||||
* Return const_reference to the first element. Requires the container to not be empty.
|
||||
*/
|
||||
const_reference front() const { return m_ht.front(); }
|
||||
|
||||
/**
|
||||
* Return const_reference to the last element. Requires the container to not be empty.
|
||||
*/
|
||||
const_reference back() const { return m_ht.back(); }
|
||||
|
||||
|
||||
/**
|
||||
* Only available if ValueTypeContainer is a std::vector. Same as calling 'values_container().data()'.
|
||||
*/
|
||||
template<class U = values_container_type, typename std::enable_if<tsl::detail_ordered_hash::is_vector<U>::value>::type* = nullptr>
|
||||
const typename values_container_type::value_type* data() const noexcept { return m_ht.data(); }
|
||||
|
||||
/**
|
||||
* Return the container in which the values are stored. The values are in the same order as the insertion order
|
||||
* and are contiguous in the structure, no holes (size() == values_container().size()).
|
||||
*/
|
||||
const values_container_type& values_container() const noexcept { return m_ht.values_container(); }
|
||||
|
||||
template<class U = values_container_type, typename std::enable_if<tsl::detail_ordered_hash::is_vector<U>::value>::type* = nullptr>
|
||||
size_type capacity() const noexcept { return m_ht.capacity(); }
|
||||
|
||||
void shrink_to_fit() { m_ht.shrink_to_fit(); }
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Insert the value before pos shifting all the elements on the right of pos (including pos) one position
|
||||
* to the right.
|
||||
*
|
||||
* Amortized linear time-complexity in the distance between pos and end().
|
||||
*/
|
||||
std::pair<iterator, bool> insert_at_position(const_iterator pos, const value_type& value) {
|
||||
return m_ht.insert_at_position(pos, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc insert_at_position(const_iterator pos, const value_type& value)
|
||||
*/
|
||||
std::pair<iterator, bool> insert_at_position(const_iterator pos, value_type&& value) {
|
||||
return m_ht.insert_at_position(pos, std::move(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc insert_at_position(const_iterator pos, const value_type& value)
|
||||
*
|
||||
* Same as insert_at_position(pos, value_type(std::forward<Args>(args)...), mainly
|
||||
* here for coherence.
|
||||
*/
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace_at_position(const_iterator pos, Args&&... args) {
|
||||
return m_ht.emplace_at_position(pos, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void pop_back() { m_ht.pop_back(); }
|
||||
|
||||
/**
|
||||
* Faster erase operation with an O(1) average complexity but it doesn't preserve the insertion order.
|
||||
*
|
||||
* If an erasure occurs, the last element of the map will take the place of the erased element.
|
||||
*/
|
||||
iterator unordered_erase(iterator pos) { return m_ht.unordered_erase(pos); }
|
||||
|
||||
/**
|
||||
* @copydoc unordered_erase(iterator pos)
|
||||
*/
|
||||
iterator unordered_erase(const_iterator pos) { return m_ht.unordered_erase(pos); }
|
||||
|
||||
/**
|
||||
* @copydoc unordered_erase(iterator pos)
|
||||
*/
|
||||
size_type unordered_erase(const key_type& key) { return m_ht.unordered_erase(key); }
|
||||
|
||||
/**
|
||||
* @copydoc unordered_erase(iterator pos)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
size_type unordered_erase(const key_type& key, std::size_t precalculated_hash) {
|
||||
return m_ht.unordered_erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc unordered_erase(iterator pos)
|
||||
*
|
||||
* This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists.
|
||||
* If so, K must be hashable and comparable to Key.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type unordered_erase(const K& key) { return m_ht.unordered_erase(key); }
|
||||
|
||||
/**
|
||||
* @copydoc unordered_erase(const K& key)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup if you already have the hash.
|
||||
*/
|
||||
template<class K, class KE = KeyEqual, typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr>
|
||||
size_type unordered_erase(const K& key, std::size_t precalculated_hash) {
|
||||
return m_ht.unordered_erase(key, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the set through the `serializer` parameter.
|
||||
*
|
||||
* The `serializer` parameter must be a function object that supports the following call:
|
||||
* - `void operator()(const U& value);` where the types `std::uint64_t`, `float` and `Key` must be supported for U.
|
||||
*
|
||||
* The implementation leaves binary compatibility (endianness, IEEE 754 for floats, ...) of the types it serializes
|
||||
* in the hands of the `Serializer` function object if compatibility is required.
|
||||
*/
|
||||
template<class Serializer>
|
||||
void serialize(Serializer& serializer) const {
|
||||
m_ht.serialize(serializer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize a previously serialized set through the `deserializer` parameter.
|
||||
*
|
||||
* The `deserializer` parameter must be a function object that supports the following calls:
|
||||
* - `template<typename U> U operator()();` where the types `std::uint64_t`, `float` and `Key` must be supported for U.
|
||||
*
|
||||
* If the deserialized hash set type is hash compatible with the serialized set, the deserialization process can be
|
||||
* sped up by setting `hash_compatible` to true. To be hash compatible, the Hash and KeyEqual must behave the same way
|
||||
* than the ones used on the serialized map. The `std::size_t` must also be of the same size as the one on the platform used
|
||||
* to serialize the map, the same apply for `IndexType`. If these criteria are not met, the behaviour is undefined with
|
||||
* `hash_compatible` sets to true.
|
||||
*
|
||||
* The behaviour is undefined if the type `Key` of the `ordered_set` is not the same as the
|
||||
* type used during serialization.
|
||||
*
|
||||
* The implementation leaves binary compatibility (endianness, IEEE 754 for floats, size of int, ...) of the types it
|
||||
* deserializes in the hands of the `Deserializer` function object if compatibility is required.
|
||||
*/
|
||||
template<class Deserializer>
|
||||
static ordered_set deserialize(Deserializer& deserializer, bool hash_compatible = false) {
|
||||
ordered_set set(0);
|
||||
set.m_ht.deserialize(deserializer, hash_compatible);
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
|
||||
|
||||
friend bool operator==(const ordered_set& lhs, const ordered_set& rhs) { return lhs.m_ht == rhs.m_ht; }
|
||||
friend bool operator!=(const ordered_set& lhs, const ordered_set& rhs) { return lhs.m_ht != rhs.m_ht; }
|
||||
friend bool operator<(const ordered_set& lhs, const ordered_set& rhs) { return lhs.m_ht < rhs.m_ht; }
|
||||
friend bool operator<=(const ordered_set& lhs, const ordered_set& rhs) { return lhs.m_ht <= rhs.m_ht; }
|
||||
friend bool operator>(const ordered_set& lhs, const ordered_set& rhs) { return lhs.m_ht > rhs.m_ht; }
|
||||
friend bool operator>=(const ordered_set& lhs, const ordered_set& rhs) { return lhs.m_ht >= rhs.m_ht; }
|
||||
|
||||
friend void swap(ordered_set& lhs, ordered_set& rhs) { lhs.swap(rhs); }
|
||||
|
||||
private:
|
||||
ht m_ht;
|
||||
};
|
||||
|
||||
} // end namespace tsl
|
||||
|
||||
#endif
|
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 Toru Niina
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2017 Toru Niina
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#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
|
||||
|
||||
#include "toml/parser.hpp"
|
||||
#include "toml/literal.hpp"
|
||||
#include "toml/serializer.hpp"
|
||||
#include "toml/get.hpp"
|
||||
|
||||
#endif// TOML_FOR_MODERN_CPP
|
@ -0,0 +1,64 @@
|
||||
#ifndef TOML11_COLOR_HPP
|
||||
#define TOML11_COLOR_HPP
|
||||
#include <cstdint>
|
||||
#include <ostream>
|
||||
|
||||
#ifdef TOML11_COLORIZE_ERROR_MESSAGE
|
||||
#define TOML11_ERROR_MESSAGE_COLORIZED true
|
||||
#else
|
||||
#define TOML11_ERROR_MESSAGE_COLORIZED false
|
||||
#endif
|
||||
|
||||
namespace toml
|
||||
{
|
||||
|
||||
// put ANSI escape sequence to ostream
|
||||
namespace color_ansi
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
inline int colorize_index()
|
||||
{
|
||||
static const int index = std::ios_base::xalloc();
|
||||
return index;
|
||||
}
|
||||
} // detail
|
||||
|
||||
inline std::ostream& colorize(std::ostream& os)
|
||||
{
|
||||
// by default, it is zero.
|
||||
os.iword(detail::colorize_index()) = 1;
|
||||
return os;
|
||||
}
|
||||
inline std::ostream& nocolorize(std::ostream& os)
|
||||
{
|
||||
os.iword(detail::colorize_index()) = 0;
|
||||
return os;
|
||||
}
|
||||
inline std::ostream& reset (std::ostream& os)
|
||||
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[00m";} return os;}
|
||||
inline std::ostream& bold (std::ostream& os)
|
||||
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[01m";} return os;}
|
||||
inline std::ostream& grey (std::ostream& os)
|
||||
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[30m";} return os;}
|
||||
inline std::ostream& red (std::ostream& os)
|
||||
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[31m";} return os;}
|
||||
inline std::ostream& green (std::ostream& os)
|
||||
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[32m";} return os;}
|
||||
inline std::ostream& yellow (std::ostream& os)
|
||||
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[33m";} return os;}
|
||||
inline std::ostream& blue (std::ostream& os)
|
||||
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[34m";} return os;}
|
||||
inline std::ostream& magenta(std::ostream& os)
|
||||
{if(os.iword(detail::colorize_index()) == 1) {os << "\033[35m";} return os;}
|
||||
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;}
|
||||
} // color_ansi
|
||||
|
||||
// ANSI escape sequence is the only and default colorization method currently
|
||||
namespace color = color_ansi;
|
||||
|
||||
} // toml
|
||||
#endif// TOML11_COLOR_HPP
|
@ -0,0 +1,306 @@
|
||||
// Copyright Toru Niina 2017.
|
||||
// Distributed under the MIT License.
|
||||
#ifndef TOML11_COMBINATOR_HPP
|
||||
#define TOML11_COMBINATOR_HPP
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
|
||||
#include <array>
|
||||
#include <iomanip>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#include "region.hpp"
|
||||
#include "result.hpp"
|
||||
#include "traits.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
// they scans characters and returns region if it matches to the condition.
|
||||
// when they fail, it does not change the location.
|
||||
// in lexer.hpp, these are used.
|
||||
|
||||
namespace toml
|
||||
{
|
||||
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.
|
||||
// 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
|
||||
// unsigned char through pointer. In general, converting pointer to a
|
||||
// pointer that has different type cause UB, but `(signed|unsigned)?char`
|
||||
// are one of the exceptions. Converting pointer only to char and std::byte
|
||||
// (c++17) are valid.
|
||||
if(std::isgraph(*reinterpret_cast<unsigned char const*>(std::addressof(c))))
|
||||
{
|
||||
return std::string(1, c);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::array<char, 5> buf;
|
||||
buf.fill('\0');
|
||||
const auto r = std::snprintf(
|
||||
buf.data(), buf.size(), "0x%02x", static_cast<int>(c) & 0xFF);
|
||||
(void) r; // Unused variable warning
|
||||
assert(r == static_cast<int>(buf.size()) - 1);
|
||||
return std::string(buf.data());
|
||||
}
|
||||
}
|
||||
|
||||
template<char C>
|
||||
struct character
|
||||
{
|
||||
static constexpr char target = C;
|
||||
|
||||
static result<region, none_t>
|
||||
invoke(location& loc)
|
||||
{
|
||||
if(loc.iter() == loc.end()) {return none();}
|
||||
const auto first = loc.iter();
|
||||
|
||||
const char c = *(loc.iter());
|
||||
if(c != target)
|
||||
{
|
||||
return none();
|
||||
}
|
||||
loc.advance(); // update location
|
||||
|
||||
return ok(region(loc, first, loc.iter()));
|
||||
}
|
||||
};
|
||||
template<char C>
|
||||
constexpr char character<C>::target;
|
||||
|
||||
// closed interval [Low, Up]. both Low and Up are included.
|
||||
template<char Low, char Up>
|
||||
struct in_range
|
||||
{
|
||||
// assuming ascii part of UTF-8...
|
||||
static_assert(Low <= Up, "lower bound should be less than upper bound.");
|
||||
|
||||
static constexpr char upper = Up;
|
||||
static constexpr char lower = Low;
|
||||
|
||||
static result<region, none_t>
|
||||
invoke(location& loc)
|
||||
{
|
||||
if(loc.iter() == loc.end()) {return none();}
|
||||
const auto first = loc.iter();
|
||||
|
||||
const char c = *(loc.iter());
|
||||
if(c < lower || upper < c)
|
||||
{
|
||||
return none();
|
||||
}
|
||||
|
||||
loc.advance();
|
||||
return ok(region(loc, first, loc.iter()));
|
||||
}
|
||||
};
|
||||
template<char L, char U> constexpr char in_range<L, U>::upper;
|
||||
template<char L, char U> constexpr char in_range<L, U>::lower;
|
||||
|
||||
// keep iterator if `Combinator` matches. otherwise, increment `iter` by 1 char.
|
||||
// for detecting invalid characters, like control sequences in toml string.
|
||||
template<typename Combinator>
|
||||
struct exclude
|
||||
{
|
||||
static result<region, none_t>
|
||||
invoke(location& loc)
|
||||
{
|
||||
if(loc.iter() == loc.end()) {return none();}
|
||||
auto first = loc.iter();
|
||||
|
||||
auto rslt = Combinator::invoke(loc);
|
||||
if(rslt.is_ok())
|
||||
{
|
||||
loc.reset(first);
|
||||
return none();
|
||||
}
|
||||
loc.reset(std::next(first)); // XXX maybe loc.advance() is okay but...
|
||||
return ok(region(loc, first, loc.iter()));
|
||||
}
|
||||
};
|
||||
|
||||
// increment `iter`, if matches. otherwise, just return empty string.
|
||||
template<typename Combinator>
|
||||
struct maybe
|
||||
{
|
||||
static result<region, none_t>
|
||||
invoke(location& loc)
|
||||
{
|
||||
const auto rslt = Combinator::invoke(loc);
|
||||
if(rslt.is_ok())
|
||||
{
|
||||
return rslt;
|
||||
}
|
||||
return ok(region(loc));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ... Ts>
|
||||
struct sequence;
|
||||
|
||||
template<typename Head, typename ... Tail>
|
||||
struct sequence<Head, Tail...>
|
||||
{
|
||||
static result<region, none_t>
|
||||
invoke(location& loc)
|
||||
{
|
||||
const auto first = loc.iter();
|
||||
const auto rslt = Head::invoke(loc);
|
||||
if(rslt.is_err())
|
||||
{
|
||||
loc.reset(first);
|
||||
return none();
|
||||
}
|
||||
return sequence<Tail...>::invoke(loc, std::move(rslt.unwrap()), first);
|
||||
}
|
||||
|
||||
// called from the above function only, recursively.
|
||||
template<typename Iterator>
|
||||
static result<region, none_t>
|
||||
invoke(location& loc, region reg, Iterator first)
|
||||
{
|
||||
const auto rslt = Head::invoke(loc);
|
||||
if(rslt.is_err())
|
||||
{
|
||||
loc.reset(first);
|
||||
return none();
|
||||
}
|
||||
reg += rslt.unwrap(); // concat regions
|
||||
return sequence<Tail...>::invoke(loc, std::move(reg), first);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Head>
|
||||
struct sequence<Head>
|
||||
{
|
||||
// would be called from sequence<T ...>::invoke only.
|
||||
template<typename Iterator>
|
||||
static result<region, none_t>
|
||||
invoke(location& loc, region reg, Iterator first)
|
||||
{
|
||||
const auto rslt = Head::invoke(loc);
|
||||
if(rslt.is_err())
|
||||
{
|
||||
loc.reset(first);
|
||||
return none();
|
||||
}
|
||||
reg += rslt.unwrap(); // concat regions
|
||||
return ok(reg);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ... Ts>
|
||||
struct either;
|
||||
|
||||
template<typename Head, typename ... Tail>
|
||||
struct either<Head, Tail...>
|
||||
{
|
||||
static result<region, none_t>
|
||||
invoke(location& loc)
|
||||
{
|
||||
const auto rslt = Head::invoke(loc);
|
||||
if(rslt.is_ok()) {return rslt;}
|
||||
return either<Tail...>::invoke(loc);
|
||||
}
|
||||
};
|
||||
template<typename Head>
|
||||
struct either<Head>
|
||||
{
|
||||
static result<region, none_t>
|
||||
invoke(location& loc)
|
||||
{
|
||||
return Head::invoke(loc);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename N>
|
||||
struct repeat;
|
||||
|
||||
template<std::size_t N> struct exactly{};
|
||||
template<std::size_t N> struct at_least{};
|
||||
struct unlimited{};
|
||||
|
||||
template<typename T, std::size_t N>
|
||||
struct repeat<T, exactly<N>>
|
||||
{
|
||||
static result<region, none_t>
|
||||
invoke(location& loc)
|
||||
{
|
||||
region retval(loc);
|
||||
const auto first = loc.iter();
|
||||
for(std::size_t i=0; i<N; ++i)
|
||||
{
|
||||
auto rslt = T::invoke(loc);
|
||||
if(rslt.is_err())
|
||||
{
|
||||
loc.reset(first);
|
||||
return none();
|
||||
}
|
||||
retval += rslt.unwrap();
|
||||
}
|
||||
return ok(std::move(retval));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, std::size_t N>
|
||||
struct repeat<T, at_least<N>>
|
||||
{
|
||||
static result<region, none_t>
|
||||
invoke(location& loc)
|
||||
{
|
||||
region retval(loc);
|
||||
|
||||
const auto first = loc.iter();
|
||||
for(std::size_t i=0; i<N; ++i)
|
||||
{
|
||||
auto rslt = T::invoke(loc);
|
||||
if(rslt.is_err())
|
||||
{
|
||||
loc.reset(first);
|
||||
return none();
|
||||
}
|
||||
retval += rslt.unwrap();
|
||||
}
|
||||
while(true)
|
||||
{
|
||||
auto rslt = T::invoke(loc);
|
||||
if(rslt.is_err())
|
||||
{
|
||||
return ok(std::move(retval));
|
||||
}
|
||||
retval += rslt.unwrap();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct repeat<T, unlimited>
|
||||
{
|
||||
static result<region, none_t>
|
||||
invoke(location& loc)
|
||||
{
|
||||
region retval(loc);
|
||||
while(true)
|
||||
{
|
||||
auto rslt = T::invoke(loc);
|
||||
if(rslt.is_err())
|
||||
{
|
||||
return ok(std::move(retval));
|
||||
}
|
||||
retval += rslt.unwrap();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // toml
|
||||
#endif// TOML11_COMBINATOR_HPP
|
@ -0,0 +1,465 @@
|
||||
// Copyright Toru Niina 2019.
|
||||
// Distributed under the MIT License.
|
||||
#ifndef TOML11_COMMENTS_HPP
|
||||
#define TOML11_COMMENTS_HPP
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
// 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
|
||||
// `std::vector<std::string>` and each `std::string` corresponds to a comment line.
|
||||
// Conversely, `discard_comments` discards all the strings and ignores everything
|
||||
// assigned in it. `discard_comments` is always empty and you will encounter an
|
||||
// error whenever you access to the element.
|
||||
namespace toml
|
||||
{
|
||||
struct discard_comments; // forward decl
|
||||
|
||||
// use it in the following way
|
||||
//
|
||||
// const toml::basic_value<toml::preserve_comments> data =
|
||||
// toml::parse<toml::preserve_comments>("example.toml");
|
||||
//
|
||||
// the interface is almost the same as std::vector<std::string>.
|
||||
struct preserve_comments
|
||||
{
|
||||
// `container_type` is not provided in discard_comments.
|
||||
// do not use this inner-type in a generic code.
|
||||
using container_type = std::vector<std::string>;
|
||||
|
||||
using size_type = container_type::size_type;
|
||||
using difference_type = container_type::difference_type;
|
||||
using value_type = container_type::value_type;
|
||||
using reference = container_type::reference;
|
||||
using const_reference = container_type::const_reference;
|
||||
using pointer = container_type::pointer;
|
||||
using const_pointer = container_type::const_pointer;
|
||||
using iterator = container_type::iterator;
|
||||
using const_iterator = container_type::const_iterator;
|
||||
using reverse_iterator = container_type::reverse_iterator;
|
||||
using const_reverse_iterator = container_type::const_reverse_iterator;
|
||||
|
||||
preserve_comments() = default;
|
||||
~preserve_comments() = default;
|
||||
preserve_comments(preserve_comments const&) = default;
|
||||
preserve_comments(preserve_comments &&) = default;
|
||||
preserve_comments& operator=(preserve_comments const&) = default;
|
||||
preserve_comments& operator=(preserve_comments &&) = default;
|
||||
|
||||
explicit preserve_comments(const std::vector<std::string>& c): comments(c){}
|
||||
explicit preserve_comments(std::vector<std::string>&& c)
|
||||
: comments(std::move(c))
|
||||
{}
|
||||
preserve_comments& operator=(const std::vector<std::string>& c)
|
||||
{
|
||||
comments = c;
|
||||
return *this;
|
||||
}
|
||||
preserve_comments& operator=(std::vector<std::string>&& c)
|
||||
{
|
||||
comments = std::move(c);
|
||||
return *this;
|
||||
}
|
||||
|
||||
explicit preserve_comments(const discard_comments&) {}
|
||||
|
||||
explicit preserve_comments(size_type n): comments(n) {}
|
||||
preserve_comments(size_type n, const std::string& x): comments(n, x) {}
|
||||
preserve_comments(std::initializer_list<std::string> x): comments(x) {}
|
||||
template<typename InputIterator>
|
||||
preserve_comments(InputIterator first, InputIterator last)
|
||||
: comments(first, last)
|
||||
{}
|
||||
|
||||
template<typename InputIterator>
|
||||
void assign(InputIterator first, InputIterator last) {comments.assign(first, last);}
|
||||
void assign(std::initializer_list<std::string> ini) {comments.assign(ini);}
|
||||
void assign(size_type n, const std::string& val) {comments.assign(n, val);}
|
||||
|
||||
// Related to the issue #97.
|
||||
//
|
||||
// It is known that `std::vector::insert` and `std::vector::erase` in
|
||||
// the standard library implementation included in GCC 4.8.5 takes
|
||||
// `std::vector::iterator` instead of `std::vector::const_iterator`.
|
||||
// Because of the const-correctness, we cannot convert a `const_iterator` to
|
||||
// an `iterator`. It causes compilation error in GCC 4.8.5.
|
||||
#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && !defined(__clang__)
|
||||
# if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) <= 40805
|
||||
# define TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION
|
||||
iterator insert(iterator p, const std::string& x)
|
||||
{
|
||||
return comments.insert(p, x);
|
||||
}
|
||||
iterator insert(iterator p, std::string&& x)
|
||||
{
|
||||
return comments.insert(p, std::move(x));
|
||||
}
|
||||
void insert(iterator p, size_type n, const std::string& x)
|
||||
{
|
||||
return comments.insert(p, n, x);
|
||||
}
|
||||
template<typename InputIterator>
|
||||
void insert(iterator p, InputIterator first, InputIterator last)
|
||||
{
|
||||
return comments.insert(p, first, last);
|
||||
}
|
||||
void insert(iterator p, std::initializer_list<std::string> ini)
|
||||
{
|
||||
return comments.insert(p, ini);
|
||||
}
|
||||
|
||||
template<typename ... Ts>
|
||||
iterator emplace(iterator p, Ts&& ... args)
|
||||
{
|
||||
return comments.emplace(p, std::forward<Ts>(args)...);
|
||||
}
|
||||
|
||||
iterator erase(iterator pos) {return comments.erase(pos);}
|
||||
iterator erase(iterator first, iterator last)
|
||||
{
|
||||
return comments.erase(first, last);
|
||||
}
|
||||
#else
|
||||
iterator insert(const_iterator p, const std::string& x)
|
||||
{
|
||||
return comments.insert(p, x);
|
||||
}
|
||||
iterator insert(const_iterator p, std::string&& x)
|
||||
{
|
||||
return comments.insert(p, std::move(x));
|
||||
}
|
||||
iterator insert(const_iterator p, size_type n, const std::string& x)
|
||||
{
|
||||
return comments.insert(p, n, x);
|
||||
}
|
||||
template<typename InputIterator>
|
||||
iterator insert(const_iterator p, InputIterator first, InputIterator last)
|
||||
{
|
||||
return comments.insert(p, first, last);
|
||||
}
|
||||
iterator insert(const_iterator p, std::initializer_list<std::string> ini)
|
||||
{
|
||||
return comments.insert(p, ini);
|
||||
}
|
||||
|
||||
template<typename ... Ts>
|
||||
iterator emplace(const_iterator p, Ts&& ... args)
|
||||
{
|
||||
return comments.emplace(p, std::forward<Ts>(args)...);
|
||||
}
|
||||
|
||||
iterator erase(const_iterator pos) {return comments.erase(pos);}
|
||||
iterator erase(const_iterator first, const_iterator last)
|
||||
{
|
||||
return comments.erase(first, last);
|
||||
}
|
||||
#endif
|
||||
|
||||
void swap(preserve_comments& other) {comments.swap(other.comments);}
|
||||
|
||||
void push_back(const std::string& v) {comments.push_back(v);}
|
||||
void push_back(std::string&& v) {comments.push_back(std::move(v));}
|
||||
void pop_back() {comments.pop_back();}
|
||||
|
||||
template<typename ... Ts>
|
||||
void emplace_back(Ts&& ... args) {comments.emplace_back(std::forward<Ts>(args)...);}
|
||||
|
||||
void clear() {comments.clear();}
|
||||
|
||||
size_type size() const noexcept {return comments.size();}
|
||||
size_type max_size() const noexcept {return comments.max_size();}
|
||||
size_type capacity() const noexcept {return comments.capacity();}
|
||||
bool empty() const noexcept {return comments.empty();}
|
||||
|
||||
void reserve(size_type n) {comments.reserve(n);}
|
||||
void resize(size_type n) {comments.resize(n);}
|
||||
void resize(size_type n, const std::string& c) {comments.resize(n, c);}
|
||||
void shrink_to_fit() {comments.shrink_to_fit();}
|
||||
|
||||
reference operator[](const size_type n) noexcept {return comments[n];}
|
||||
const_reference operator[](const size_type n) const noexcept {return comments[n];}
|
||||
reference at(const size_type n) {return comments.at(n);}
|
||||
const_reference at(const size_type n) const {return comments.at(n);}
|
||||
reference front() noexcept {return comments.front();}
|
||||
const_reference front() const noexcept {return comments.front();}
|
||||
reference back() noexcept {return comments.back();}
|
||||
const_reference back() const noexcept {return comments.back();}
|
||||
|
||||
pointer data() noexcept {return comments.data();}
|
||||
const_pointer data() const noexcept {return comments.data();}
|
||||
|
||||
iterator begin() noexcept {return comments.begin();}
|
||||
iterator end() noexcept {return comments.end();}
|
||||
const_iterator begin() const noexcept {return comments.begin();}
|
||||
const_iterator end() const noexcept {return comments.end();}
|
||||
const_iterator cbegin() const noexcept {return comments.cbegin();}
|
||||
const_iterator cend() const noexcept {return comments.cend();}
|
||||
|
||||
reverse_iterator rbegin() noexcept {return comments.rbegin();}
|
||||
reverse_iterator rend() noexcept {return comments.rend();}
|
||||
const_reverse_iterator rbegin() const noexcept {return comments.rbegin();}
|
||||
const_reverse_iterator rend() const noexcept {return comments.rend();}
|
||||
const_reverse_iterator crbegin() const noexcept {return comments.crbegin();}
|
||||
const_reverse_iterator crend() const noexcept {return comments.crend();}
|
||||
|
||||
friend bool operator==(const preserve_comments&, const preserve_comments&);
|
||||
friend bool operator!=(const preserve_comments&, const preserve_comments&);
|
||||
friend bool operator< (const preserve_comments&, const preserve_comments&);
|
||||
friend bool operator<=(const preserve_comments&, const preserve_comments&);
|
||||
friend bool operator> (const preserve_comments&, const preserve_comments&);
|
||||
friend bool operator>=(const preserve_comments&, const preserve_comments&);
|
||||
|
||||
friend void swap(preserve_comments&, std::vector<std::string>&);
|
||||
friend void swap(std::vector<std::string>&, preserve_comments&);
|
||||
|
||||
private:
|
||||
|
||||
container_type comments;
|
||||
};
|
||||
|
||||
inline bool operator==(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments == rhs.comments;}
|
||||
inline bool operator!=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments != rhs.comments;}
|
||||
inline bool operator< (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments < rhs.comments;}
|
||||
inline bool operator<=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments <= rhs.comments;}
|
||||
inline bool operator> (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments > rhs.comments;}
|
||||
inline bool operator>=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments >= rhs.comments;}
|
||||
|
||||
inline void swap(preserve_comments& lhs, preserve_comments& rhs)
|
||||
{
|
||||
lhs.swap(rhs);
|
||||
return;
|
||||
}
|
||||
inline void swap(preserve_comments& lhs, std::vector<std::string>& rhs)
|
||||
{
|
||||
lhs.comments.swap(rhs);
|
||||
return;
|
||||
}
|
||||
inline void swap(std::vector<std::string>& lhs, preserve_comments& rhs)
|
||||
{
|
||||
lhs.swap(rhs.comments);
|
||||
return;
|
||||
}
|
||||
|
||||
template<typename charT, typename traits>
|
||||
std::basic_ostream<charT, traits>&
|
||||
operator<<(std::basic_ostream<charT, traits>& os, const preserve_comments& com)
|
||||
{
|
||||
for(const auto& c : com)
|
||||
{
|
||||
os << '#' << c << '\n';
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
// To provide the same interface with `preserve_comments`, `discard_comments`
|
||||
// should have an iterator. But it does not contain anything, so we need to
|
||||
// add an iterator that points nothing.
|
||||
//
|
||||
// It always points null, so DO NOT unwrap this iterator. It always crashes
|
||||
// your program.
|
||||
template<typename T, bool is_const>
|
||||
struct empty_iterator
|
||||
{
|
||||
using value_type = T;
|
||||
using reference_type = typename std::conditional<is_const, T const&, T&>::type;
|
||||
using pointer_type = typename std::conditional<is_const, T const*, T*>::type;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
|
||||
empty_iterator() = default;
|
||||
~empty_iterator() = default;
|
||||
empty_iterator(empty_iterator const&) = default;
|
||||
empty_iterator(empty_iterator &&) = default;
|
||||
empty_iterator& operator=(empty_iterator const&) = default;
|
||||
empty_iterator& operator=(empty_iterator &&) = default;
|
||||
|
||||
// DO NOT call these operators.
|
||||
reference_type operator*() const noexcept {std::terminate();}
|
||||
pointer_type operator->() const noexcept {return nullptr;}
|
||||
reference_type operator[](difference_type) const noexcept {return this->operator*();}
|
||||
|
||||
// These operators do nothing.
|
||||
empty_iterator& operator++() noexcept {return *this;}
|
||||
empty_iterator operator++(int) noexcept {return *this;}
|
||||
empty_iterator& operator--() noexcept {return *this;}
|
||||
empty_iterator operator--(int) noexcept {return *this;}
|
||||
|
||||
empty_iterator& operator+=(difference_type) noexcept {return *this;}
|
||||
empty_iterator& operator-=(difference_type) noexcept {return *this;}
|
||||
|
||||
empty_iterator operator+(difference_type) const noexcept {return *this;}
|
||||
empty_iterator operator-(difference_type) const noexcept {return *this;}
|
||||
};
|
||||
|
||||
template<typename T, bool C>
|
||||
bool operator==(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return true;}
|
||||
template<typename T, bool C>
|
||||
bool operator!=(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return false;}
|
||||
template<typename T, bool C>
|
||||
bool operator< (const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return false;}
|
||||
template<typename T, bool C>
|
||||
bool operator<=(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return true;}
|
||||
template<typename T, bool C>
|
||||
bool operator> (const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return false;}
|
||||
template<typename T, bool C>
|
||||
bool operator>=(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return true;}
|
||||
|
||||
template<typename T, bool C>
|
||||
typename empty_iterator<T, C>::difference_type
|
||||
operator-(const empty_iterator<T, C>&, const empty_iterator<T, C>&) noexcept {return 0;}
|
||||
|
||||
template<typename T, bool C>
|
||||
empty_iterator<T, C>
|
||||
operator+(typename empty_iterator<T, C>::difference_type, const empty_iterator<T, C>& rhs) noexcept {return rhs;}
|
||||
template<typename T, bool C>
|
||||
empty_iterator<T, C>
|
||||
operator+(const empty_iterator<T, C>& lhs, typename empty_iterator<T, C>::difference_type) noexcept {return lhs;}
|
||||
|
||||
} // detail
|
||||
|
||||
// The default comment type. It discards all the comments. It requires only one
|
||||
// byte to contain, so the memory footprint is smaller than preserve_comments.
|
||||
//
|
||||
// It just ignores `push_back`, `insert`, `erase`, and any other modifications.
|
||||
// IT always returns size() == 0, the iterator taken by `begin()` is always the
|
||||
// same as that of `end()`, and accessing through `operator[]` or iterators
|
||||
// always causes a segmentation fault. DO NOT access to the element of this.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// To reduce the memory footprint, later we can try empty base optimization (EBO).
|
||||
struct discard_comments
|
||||
{
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = std::string;
|
||||
using reference = std::string&;
|
||||
using const_reference = std::string const&;
|
||||
using pointer = std::string*;
|
||||
using const_pointer = std::string const*;
|
||||
using iterator = detail::empty_iterator<std::string, false>;
|
||||
using const_iterator = detail::empty_iterator<std::string, true>;
|
||||
using reverse_iterator = detail::empty_iterator<std::string, false>;
|
||||
using const_reverse_iterator = detail::empty_iterator<std::string, true>;
|
||||
|
||||
discard_comments() = default;
|
||||
~discard_comments() = default;
|
||||
discard_comments(discard_comments const&) = default;
|
||||
discard_comments(discard_comments &&) = default;
|
||||
discard_comments& operator=(discard_comments const&) = default;
|
||||
discard_comments& operator=(discard_comments &&) = default;
|
||||
|
||||
explicit discard_comments(const std::vector<std::string>&) noexcept {}
|
||||
explicit discard_comments(std::vector<std::string>&&) noexcept {}
|
||||
discard_comments& operator=(const std::vector<std::string>&) noexcept {return *this;}
|
||||
discard_comments& operator=(std::vector<std::string>&&) noexcept {return *this;}
|
||||
|
||||
explicit discard_comments(const preserve_comments&) noexcept {}
|
||||
|
||||
explicit discard_comments(size_type) noexcept {}
|
||||
discard_comments(size_type, const std::string&) noexcept {}
|
||||
discard_comments(std::initializer_list<std::string>) noexcept {}
|
||||
template<typename InputIterator>
|
||||
discard_comments(InputIterator, InputIterator) noexcept {}
|
||||
|
||||
template<typename InputIterator>
|
||||
void assign(InputIterator, InputIterator) noexcept {}
|
||||
void assign(std::initializer_list<std::string>) noexcept {}
|
||||
void assign(size_type, const std::string&) noexcept {}
|
||||
|
||||
iterator insert(const_iterator, const std::string&) {return iterator{};}
|
||||
iterator insert(const_iterator, std::string&&) {return iterator{};}
|
||||
iterator insert(const_iterator, size_type, const std::string&) {return iterator{};}
|
||||
template<typename InputIterator>
|
||||
iterator insert(const_iterator, InputIterator, InputIterator) {return iterator{};}
|
||||
iterator insert(const_iterator, std::initializer_list<std::string>) {return iterator{};}
|
||||
|
||||
template<typename ... Ts>
|
||||
iterator emplace(const_iterator, Ts&& ...) {return iterator{};}
|
||||
iterator erase(const_iterator) {return iterator{};}
|
||||
iterator erase(const_iterator, const_iterator) {return iterator{};}
|
||||
|
||||
void swap(discard_comments&) {return;}
|
||||
|
||||
void push_back(const std::string&) {return;}
|
||||
void push_back(std::string&& ) {return;}
|
||||
void pop_back() {return;}
|
||||
|
||||
template<typename ... Ts>
|
||||
void emplace_back(Ts&& ...) {return;}
|
||||
|
||||
void clear() {return;}
|
||||
|
||||
size_type size() const noexcept {return 0;}
|
||||
size_type max_size() const noexcept {return 0;}
|
||||
size_type capacity() const noexcept {return 0;}
|
||||
bool empty() const noexcept {return true;}
|
||||
|
||||
void reserve(size_type) {return;}
|
||||
void resize(size_type) {return;}
|
||||
void resize(size_type, const std::string&) {return;}
|
||||
void shrink_to_fit() {return;}
|
||||
|
||||
// DO NOT access to the element of this container. This container is always
|
||||
// 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 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();}
|
||||
|
||||
pointer data() noexcept {return nullptr;}
|
||||
const_pointer data() const noexcept {return nullptr;}
|
||||
|
||||
iterator begin() noexcept {return iterator{};}
|
||||
iterator end() noexcept {return iterator{};}
|
||||
const_iterator begin() const noexcept {return const_iterator{};}
|
||||
const_iterator end() const noexcept {return const_iterator{};}
|
||||
const_iterator cbegin() const noexcept {return const_iterator{};}
|
||||
const_iterator cend() const noexcept {return const_iterator{};}
|
||||
|
||||
reverse_iterator rbegin() noexcept {return iterator{};}
|
||||
reverse_iterator rend() noexcept {return iterator{};}
|
||||
const_reverse_iterator rbegin() const noexcept {return const_iterator{};}
|
||||
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{};}
|
||||
};
|
||||
|
||||
inline bool operator==(const discard_comments&, const discard_comments&) noexcept {return true;}
|
||||
inline bool operator!=(const discard_comments&, const discard_comments&) noexcept {return false;}
|
||||
inline bool operator< (const discard_comments&, const discard_comments&) noexcept {return false;}
|
||||
inline bool operator<=(const discard_comments&, const discard_comments&) noexcept {return true;}
|
||||
inline bool operator> (const discard_comments&, const discard_comments&) noexcept {return false;}
|
||||
inline bool operator>=(const discard_comments&, const discard_comments&) noexcept {return true;}
|
||||
|
||||
inline void swap(const discard_comments&, const discard_comments&) noexcept {return;}
|
||||
|
||||
template<typename charT, typename traits>
|
||||
std::basic_ostream<charT, traits>&
|
||||
operator<<(std::basic_ostream<charT, traits>& os, const discard_comments&)
|
||||
{
|
||||
return os;
|
||||
}
|
||||
|
||||
} // toml11
|
||||
#endif// TOML11_COMMENTS_HPP
|
@ -0,0 +1,631 @@
|
||||
// Copyright Toru Niina 2017.
|
||||
// Distributed under the MIT License.
|
||||
#ifndef TOML11_DATETIME_HPP
|
||||
#define TOML11_DATETIME_HPP
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
#include <ostream>
|
||||
#include <tuple>
|
||||
|
||||
namespace toml
|
||||
{
|
||||
|
||||
// To avoid non-threadsafe std::localtime. In C11 (not C++11!), localtime_s is
|
||||
// provided in the absolutely same purpose, but C++11 is actually not compatible
|
||||
// with C11. We need to dispatch the function depending on the OS.
|
||||
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)
|
||||
inline std::tm localtime_s(const std::time_t* src)
|
||||
{
|
||||
std::tm dst;
|
||||
const auto result = ::localtime_s(&dst, src);
|
||||
if (result) { throw std::runtime_error("localtime_s failed."); }
|
||||
return dst;
|
||||
}
|
||||
inline std::tm gmtime_s(const std::time_t* src)
|
||||
{
|
||||
std::tm dst;
|
||||
const auto result = ::gmtime_s(&dst, src);
|
||||
if (result) { throw std::runtime_error("gmtime_s failed."); }
|
||||
return dst;
|
||||
}
|
||||
#else // fallback. not threadsafe
|
||||
inline std::tm localtime_s(const std::time_t* src)
|
||||
{
|
||||
const auto result = std::localtime(src);
|
||||
if (!result) { throw std::runtime_error("localtime failed."); }
|
||||
return *result;
|
||||
}
|
||||
inline std::tm gmtime_s(const std::time_t* src)
|
||||
{
|
||||
const auto result = std::gmtime(src);
|
||||
if (!result) { throw std::runtime_error("gmtime failed."); }
|
||||
return *result;
|
||||
}
|
||||
#endif
|
||||
} // detail
|
||||
|
||||
enum class month_t : std::uint8_t
|
||||
{
|
||||
Jan = 0,
|
||||
Feb = 1,
|
||||
Mar = 2,
|
||||
Apr = 3,
|
||||
May = 4,
|
||||
Jun = 5,
|
||||
Jul = 6,
|
||||
Aug = 7,
|
||||
Sep = 8,
|
||||
Oct = 9,
|
||||
Nov = 10,
|
||||
Dec = 11
|
||||
};
|
||||
|
||||
struct local_date
|
||||
{
|
||||
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)),
|
||||
month(static_cast<std::uint8_t>(m)),
|
||||
day (static_cast<std::uint8_t>(d))
|
||||
{}
|
||||
|
||||
explicit local_date(const std::tm& t)
|
||||
: year (static_cast<std::int16_t>(t.tm_year + 1900)),
|
||||
month(static_cast<std::uint8_t>(t.tm_mon)),
|
||||
day (static_cast<std::uint8_t>(t.tm_mday))
|
||||
{}
|
||||
|
||||
explicit local_date(const std::chrono::system_clock::time_point& tp)
|
||||
{
|
||||
const auto t = std::chrono::system_clock::to_time_t(tp);
|
||||
const auto time = detail::localtime_s(&t);
|
||||
*this = local_date(time);
|
||||
}
|
||||
|
||||
explicit local_date(const std::time_t t)
|
||||
: local_date(std::chrono::system_clock::from_time_t(t))
|
||||
{}
|
||||
|
||||
operator std::chrono::system_clock::time_point() const
|
||||
{
|
||||
// std::mktime returns date as local time zone. no conversion needed
|
||||
std::tm t;
|
||||
t.tm_sec = 0;
|
||||
t.tm_min = 0;
|
||||
t.tm_hour = 0;
|
||||
t.tm_mday = static_cast<int>(this->day);
|
||||
t.tm_mon = static_cast<int>(this->month);
|
||||
t.tm_year = static_cast<int>(this->year) - 1900;
|
||||
t.tm_wday = 0; // the value will be ignored
|
||||
t.tm_yday = 0; // the value will be ignored
|
||||
t.tm_isdst = -1;
|
||||
return std::chrono::system_clock::from_time_t(std::mktime(&t));
|
||||
}
|
||||
|
||||
operator std::time_t() const
|
||||
{
|
||||
return std::chrono::system_clock::to_time_t(
|
||||
std::chrono::system_clock::time_point(*this));
|
||||
}
|
||||
|
||||
local_date() = default;
|
||||
~local_date() = default;
|
||||
local_date(local_date const&) = default;
|
||||
local_date(local_date&&) = default;
|
||||
local_date& operator=(local_date const&) = default;
|
||||
local_date& operator=(local_date&&) = default;
|
||||
};
|
||||
|
||||
inline bool operator==(const local_date& lhs, const local_date& rhs)
|
||||
{
|
||||
return std::make_tuple(lhs.year, lhs.month, lhs.day) ==
|
||||
std::make_tuple(rhs.year, rhs.month, rhs.day);
|
||||
}
|
||||
inline bool operator!=(const local_date& lhs, const local_date& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
inline bool operator< (const local_date& lhs, const local_date& rhs)
|
||||
{
|
||||
return std::make_tuple(lhs.year, lhs.month, lhs.day) <
|
||||
std::make_tuple(rhs.year, rhs.month, rhs.day);
|
||||
}
|
||||
inline bool operator<=(const local_date& lhs, const local_date& rhs)
|
||||
{
|
||||
return (lhs < rhs) || (lhs == rhs);
|
||||
}
|
||||
inline bool operator> (const local_date& lhs, const local_date& rhs)
|
||||
{
|
||||
return !(lhs <= rhs);
|
||||
}
|
||||
inline bool operator>=(const local_date& lhs, const local_date& rhs)
|
||||
{
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
template<typename charT, typename traits>
|
||||
std::basic_ostream<charT, traits>&
|
||||
operator<<(std::basic_ostream<charT, traits>& os, const local_date& date)
|
||||
{
|
||||
os << std::setfill('0') << std::setw(4) << static_cast<int>(date.year ) << '-';
|
||||
os << std::setfill('0') << std::setw(2) << static_cast<int>(date.month) + 1 << '-';
|
||||
os << std::setfill('0') << std::setw(2) << static_cast<int>(date.day ) ;
|
||||
return os;
|
||||
}
|
||||
|
||||
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]
|
||||
|
||||
local_time(int h, int m, int s,
|
||||
int ms = 0, int us = 0, int ns = 0)
|
||||
: hour (static_cast<std::uint8_t>(h)),
|
||||
minute(static_cast<std::uint8_t>(m)),
|
||||
second(static_cast<std::uint8_t>(s)),
|
||||
millisecond(static_cast<std::uint16_t>(ms)),
|
||||
microsecond(static_cast<std::uint16_t>(us)),
|
||||
nanosecond (static_cast<std::uint16_t>(ns))
|
||||
{}
|
||||
|
||||
explicit local_time(const std::tm& t)
|
||||
: hour (static_cast<std::uint8_t>(t.tm_hour)),
|
||||
minute(static_cast<std::uint8_t>(t.tm_min)),
|
||||
second(static_cast<std::uint8_t>(t.tm_sec)),
|
||||
millisecond(0), microsecond(0), nanosecond(0)
|
||||
{}
|
||||
|
||||
template<typename Rep, typename Period>
|
||||
explicit local_time(const std::chrono::duration<Rep, Period>& t)
|
||||
{
|
||||
const auto h = std::chrono::duration_cast<std::chrono::hours>(t);
|
||||
this->hour = static_cast<std::uint8_t>(h.count());
|
||||
const auto t2 = t - h;
|
||||
const auto m = std::chrono::duration_cast<std::chrono::minutes>(t2);
|
||||
this->minute = static_cast<std::uint8_t>(m.count());
|
||||
const auto t3 = t2 - m;
|
||||
const auto s = std::chrono::duration_cast<std::chrono::seconds>(t3);
|
||||
this->second = static_cast<std::uint8_t>(s.count());
|
||||
const auto t4 = t3 - s;
|
||||
const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(t4);
|
||||
this->millisecond = static_cast<std::uint16_t>(ms.count());
|
||||
const auto t5 = t4 - ms;
|
||||
const auto us = std::chrono::duration_cast<std::chrono::microseconds>(t5);
|
||||
this->microsecond = static_cast<std::uint16_t>(us.count());
|
||||
const auto t6 = t5 - us;
|
||||
const auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(t6);
|
||||
this->nanosecond = static_cast<std::uint16_t>(ns.count());
|
||||
}
|
||||
|
||||
operator std::chrono::nanoseconds() const
|
||||
{
|
||||
return std::chrono::nanoseconds (this->nanosecond) +
|
||||
std::chrono::microseconds(this->microsecond) +
|
||||
std::chrono::milliseconds(this->millisecond) +
|
||||
std::chrono::seconds(this->second) +
|
||||
std::chrono::minutes(this->minute) +
|
||||
std::chrono::hours(this->hour);
|
||||
}
|
||||
|
||||
local_time() = default;
|
||||
~local_time() = default;
|
||||
local_time(local_time const&) = default;
|
||||
local_time(local_time&&) = default;
|
||||
local_time& operator=(local_time const&) = default;
|
||||
local_time& operator=(local_time&&) = default;
|
||||
};
|
||||
|
||||
inline bool operator==(const local_time& lhs, const local_time& rhs)
|
||||
{
|
||||
return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) ==
|
||||
std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond);
|
||||
}
|
||||
inline bool operator!=(const local_time& lhs, const local_time& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
inline bool operator< (const local_time& lhs, const local_time& rhs)
|
||||
{
|
||||
return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) <
|
||||
std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond);
|
||||
}
|
||||
inline bool operator<=(const local_time& lhs, const local_time& rhs)
|
||||
{
|
||||
return (lhs < rhs) || (lhs == rhs);
|
||||
}
|
||||
inline bool operator> (const local_time& lhs, const local_time& rhs)
|
||||
{
|
||||
return !(lhs <= rhs);
|
||||
}
|
||||
inline bool operator>=(const local_time& lhs, const local_time& rhs)
|
||||
{
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
template<typename charT, typename traits>
|
||||
std::basic_ostream<charT, traits>&
|
||||
operator<<(std::basic_ostream<charT, traits>& os, const local_time& time)
|
||||
{
|
||||
os << std::setfill('0') << std::setw(2) << static_cast<int>(time.hour ) << ':';
|
||||
os << std::setfill('0') << std::setw(2) << static_cast<int>(time.minute) << ':';
|
||||
os << std::setfill('0') << std::setw(2) << static_cast<int>(time.second);
|
||||
if(time.millisecond != 0 || time.microsecond != 0 || time.nanosecond != 0)
|
||||
{
|
||||
os << '.';
|
||||
os << std::setfill('0') << std::setw(3) << static_cast<int>(time.millisecond);
|
||||
if(time.microsecond != 0 || time.nanosecond != 0)
|
||||
{
|
||||
os << std::setfill('0') << std::setw(3) << static_cast<int>(time.microsecond);
|
||||
if(time.nanosecond != 0)
|
||||
{
|
||||
os << std::setfill('0') << std::setw(3) << static_cast<int>(time.nanosecond);
|
||||
}
|
||||
}
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
struct time_offset
|
||||
{
|
||||
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)),
|
||||
minute(static_cast<std::int8_t>(m))
|
||||
{}
|
||||
|
||||
operator std::chrono::minutes() const
|
||||
{
|
||||
return std::chrono::minutes(this->minute) +
|
||||
std::chrono::hours(this->hour);
|
||||
}
|
||||
|
||||
time_offset() = default;
|
||||
~time_offset() = default;
|
||||
time_offset(time_offset const&) = default;
|
||||
time_offset(time_offset&&) = default;
|
||||
time_offset& operator=(time_offset const&) = default;
|
||||
time_offset& operator=(time_offset&&) = default;
|
||||
};
|
||||
|
||||
inline bool operator==(const time_offset& lhs, const time_offset& rhs)
|
||||
{
|
||||
return std::make_tuple(lhs.hour, lhs.minute) ==
|
||||
std::make_tuple(rhs.hour, rhs.minute);
|
||||
}
|
||||
inline bool operator!=(const time_offset& lhs, const time_offset& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
inline bool operator< (const time_offset& lhs, const time_offset& rhs)
|
||||
{
|
||||
return std::make_tuple(lhs.hour, lhs.minute) <
|
||||
std::make_tuple(rhs.hour, rhs.minute);
|
||||
}
|
||||
inline bool operator<=(const time_offset& lhs, const time_offset& rhs)
|
||||
{
|
||||
return (lhs < rhs) || (lhs == rhs);
|
||||
}
|
||||
inline bool operator> (const time_offset& lhs, const time_offset& rhs)
|
||||
{
|
||||
return !(lhs <= rhs);
|
||||
}
|
||||
inline bool operator>=(const time_offset& lhs, const time_offset& rhs)
|
||||
{
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
template<typename charT, typename traits>
|
||||
std::basic_ostream<charT, traits>&
|
||||
operator<<(std::basic_ostream<charT, traits>& os, const time_offset& offset)
|
||||
{
|
||||
if(offset.hour == 0 && offset.minute == 0)
|
||||
{
|
||||
os << 'Z';
|
||||
return os;
|
||||
}
|
||||
int minute = static_cast<int>(offset.hour) * 60 + offset.minute;
|
||||
if(minute < 0){os << '-'; minute = std::abs(minute);} else {os << '+';}
|
||||
os << std::setfill('0') << std::setw(2) << minute / 60 << ':';
|
||||
os << std::setfill('0') << std::setw(2) << minute % 60;
|
||||
return os;
|
||||
}
|
||||
|
||||
struct local_datetime
|
||||
{
|
||||
local_date date;
|
||||
local_time time;
|
||||
|
||||
local_datetime(local_date d, local_time t): date(d), time(t) {}
|
||||
|
||||
explicit local_datetime(const std::tm& t): date(t), time(t){}
|
||||
|
||||
explicit local_datetime(const std::chrono::system_clock::time_point& tp)
|
||||
{
|
||||
const auto t = std::chrono::system_clock::to_time_t(tp);
|
||||
std::tm ltime = detail::localtime_s(&t);
|
||||
|
||||
this->date = local_date(ltime);
|
||||
this->time = local_time(ltime);
|
||||
|
||||
// std::tm lacks subsecond information, so diff between tp and tm
|
||||
// can be used to get millisecond & microsecond information.
|
||||
const auto t_diff = tp -
|
||||
std::chrono::system_clock::from_time_t(std::mktime(<ime));
|
||||
this->time.millisecond = static_cast<std::uint16_t>(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(t_diff).count());
|
||||
this->time.microsecond = static_cast<std::uint16_t>(
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(t_diff).count());
|
||||
this->time.nanosecond = static_cast<std::uint16_t>(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds >(t_diff).count());
|
||||
}
|
||||
|
||||
explicit local_datetime(const std::time_t t)
|
||||
: local_datetime(std::chrono::system_clock::from_time_t(t))
|
||||
{}
|
||||
|
||||
operator std::chrono::system_clock::time_point() const
|
||||
{
|
||||
using internal_duration =
|
||||
typename std::chrono::system_clock::time_point::duration;
|
||||
|
||||
// Normally DST begins at A.M. 3 or 4. If we re-use conversion operator
|
||||
// of local_date and local_time independently, the conversion fails if
|
||||
// it is the day when DST begins or ends. Since local_date considers the
|
||||
// time is 00:00 A.M. and local_time does not consider DST because it
|
||||
// does not have any date information. We need to consider both date and
|
||||
// time information at the same time to convert it correctly.
|
||||
|
||||
std::tm t;
|
||||
t.tm_sec = static_cast<int>(this->time.second);
|
||||
t.tm_min = static_cast<int>(this->time.minute);
|
||||
t.tm_hour = static_cast<int>(this->time.hour);
|
||||
t.tm_mday = static_cast<int>(this->date.day);
|
||||
t.tm_mon = static_cast<int>(this->date.month);
|
||||
t.tm_year = static_cast<int>(this->date.year) - 1900;
|
||||
t.tm_wday = 0; // the value will be ignored
|
||||
t.tm_yday = 0; // the value will be ignored
|
||||
t.tm_isdst = -1;
|
||||
|
||||
// std::mktime returns date as local time zone. no conversion needed
|
||||
auto dt = std::chrono::system_clock::from_time_t(std::mktime(&t));
|
||||
dt += std::chrono::duration_cast<internal_duration>(
|
||||
std::chrono::milliseconds(this->time.millisecond) +
|
||||
std::chrono::microseconds(this->time.microsecond) +
|
||||
std::chrono::nanoseconds (this->time.nanosecond));
|
||||
return dt;
|
||||
}
|
||||
|
||||
operator std::time_t() const
|
||||
{
|
||||
return std::chrono::system_clock::to_time_t(
|
||||
std::chrono::system_clock::time_point(*this));
|
||||
}
|
||||
|
||||
local_datetime() = default;
|
||||
~local_datetime() = default;
|
||||
local_datetime(local_datetime const&) = default;
|
||||
local_datetime(local_datetime&&) = default;
|
||||
local_datetime& operator=(local_datetime const&) = default;
|
||||
local_datetime& operator=(local_datetime&&) = default;
|
||||
};
|
||||
|
||||
inline bool operator==(const local_datetime& lhs, const local_datetime& rhs)
|
||||
{
|
||||
return std::make_tuple(lhs.date, lhs.time) ==
|
||||
std::make_tuple(rhs.date, rhs.time);
|
||||
}
|
||||
inline bool operator!=(const local_datetime& lhs, const local_datetime& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
inline bool operator< (const local_datetime& lhs, const local_datetime& rhs)
|
||||
{
|
||||
return std::make_tuple(lhs.date, lhs.time) <
|
||||
std::make_tuple(rhs.date, rhs.time);
|
||||
}
|
||||
inline bool operator<=(const local_datetime& lhs, const local_datetime& rhs)
|
||||
{
|
||||
return (lhs < rhs) || (lhs == rhs);
|
||||
}
|
||||
inline bool operator> (const local_datetime& lhs, const local_datetime& rhs)
|
||||
{
|
||||
return !(lhs <= rhs);
|
||||
}
|
||||
inline bool operator>=(const local_datetime& lhs, const local_datetime& rhs)
|
||||
{
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
template<typename charT, typename traits>
|
||||
std::basic_ostream<charT, traits>&
|
||||
operator<<(std::basic_ostream<charT, traits>& os, const local_datetime& dt)
|
||||
{
|
||||
os << dt.date << 'T' << dt.time;
|
||||
return os;
|
||||
}
|
||||
|
||||
struct offset_datetime
|
||||
{
|
||||
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)
|
||||
{}
|
||||
offset_datetime(const local_datetime& dt, time_offset o)
|
||||
: date(dt.date), time(dt.time), offset(o)
|
||||
{}
|
||||
explicit offset_datetime(const local_datetime& ld)
|
||||
: date(ld.date), time(ld.time), offset(get_local_offset(nullptr))
|
||||
// use the current local timezone offset
|
||||
{}
|
||||
explicit offset_datetime(const std::chrono::system_clock::time_point& tp)
|
||||
: offset(0, 0) // use gmtime
|
||||
{
|
||||
const auto timet = std::chrono::system_clock::to_time_t(tp);
|
||||
const auto tm = detail::gmtime_s(&timet);
|
||||
this->date = local_date(tm);
|
||||
this->time = local_time(tm);
|
||||
}
|
||||
explicit offset_datetime(const std::time_t& t)
|
||||
: offset(0, 0) // use gmtime
|
||||
{
|
||||
const auto tm = detail::gmtime_s(&t);
|
||||
this->date = local_date(tm);
|
||||
this->time = local_time(tm);
|
||||
}
|
||||
explicit offset_datetime(const std::tm& t)
|
||||
: offset(0, 0) // assume gmtime
|
||||
{
|
||||
this->date = local_date(t);
|
||||
this->time = local_time(t);
|
||||
}
|
||||
|
||||
operator std::chrono::system_clock::time_point() const
|
||||
{
|
||||
// get date-time
|
||||
using internal_duration =
|
||||
typename std::chrono::system_clock::time_point::duration;
|
||||
|
||||
// first, convert it to local date-time information in the same way as
|
||||
// local_datetime does. later we will use time_t to adjust time offset.
|
||||
std::tm t;
|
||||
t.tm_sec = static_cast<int>(this->time.second);
|
||||
t.tm_min = static_cast<int>(this->time.minute);
|
||||
t.tm_hour = static_cast<int>(this->time.hour);
|
||||
t.tm_mday = static_cast<int>(this->date.day);
|
||||
t.tm_mon = static_cast<int>(this->date.month);
|
||||
t.tm_year = static_cast<int>(this->date.year) - 1900;
|
||||
t.tm_wday = 0; // the value will be ignored
|
||||
t.tm_yday = 0; // the value will be ignored
|
||||
t.tm_isdst = -1;
|
||||
const std::time_t tp_loc = std::mktime(std::addressof(t));
|
||||
|
||||
auto tp = std::chrono::system_clock::from_time_t(tp_loc);
|
||||
tp += std::chrono::duration_cast<internal_duration>(
|
||||
std::chrono::milliseconds(this->time.millisecond) +
|
||||
std::chrono::microseconds(this->time.microsecond) +
|
||||
std::chrono::nanoseconds (this->time.nanosecond));
|
||||
|
||||
// Since mktime uses local time zone, it should be corrected.
|
||||
// `12:00:00+09:00` means `03:00:00Z`. So mktime returns `03:00:00Z` if
|
||||
// we are in `+09:00` timezone. To represent `12:00:00Z` there, we need
|
||||
// to add `+09:00` to `03:00:00Z`.
|
||||
// Here, it uses the time_t converted from date-time info to handle
|
||||
// daylight saving time.
|
||||
const auto ofs = get_local_offset(std::addressof(tp_loc));
|
||||
tp += std::chrono::hours (ofs.hour);
|
||||
tp += std::chrono::minutes(ofs.minute);
|
||||
|
||||
// We got `12:00:00Z` by correcting local timezone applied by mktime.
|
||||
// Then we will apply the offset. Let's say `12:00:00-08:00` is given.
|
||||
// And now, we have `12:00:00Z`. `12:00:00-08:00` means `20:00:00Z`.
|
||||
// So we need to subtract the offset.
|
||||
tp -= std::chrono::minutes(this->offset);
|
||||
return tp;
|
||||
}
|
||||
|
||||
operator std::time_t() const
|
||||
{
|
||||
return std::chrono::system_clock::to_time_t(
|
||||
std::chrono::system_clock::time_point(*this));
|
||||
}
|
||||
|
||||
offset_datetime() = default;
|
||||
~offset_datetime() = default;
|
||||
offset_datetime(offset_datetime const&) = default;
|
||||
offset_datetime(offset_datetime&&) = default;
|
||||
offset_datetime& operator=(offset_datetime const&) = default;
|
||||
offset_datetime& operator=(offset_datetime&&) = default;
|
||||
|
||||
private:
|
||||
|
||||
static time_offset get_local_offset(const std::time_t* tp)
|
||||
{
|
||||
// get local timezone with the same date-time information as mktime
|
||||
const auto t = detail::localtime_s(tp);
|
||||
|
||||
std::array<char, 6> buf;
|
||||
const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0
|
||||
if(result != 5)
|
||||
{
|
||||
throw std::runtime_error("toml::offset_datetime: cannot obtain "
|
||||
"timezone information of current env");
|
||||
}
|
||||
const int ofs = std::atoi(buf.data());
|
||||
const int ofs_h = ofs / 100;
|
||||
const int ofs_m = ofs - (ofs_h * 100);
|
||||
return time_offset(ofs_h, ofs_m);
|
||||
}
|
||||
};
|
||||
|
||||
inline bool operator==(const offset_datetime& lhs, const offset_datetime& rhs)
|
||||
{
|
||||
return std::make_tuple(lhs.date, lhs.time, lhs.offset) ==
|
||||
std::make_tuple(rhs.date, rhs.time, rhs.offset);
|
||||
}
|
||||
inline bool operator!=(const offset_datetime& lhs, const offset_datetime& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
inline bool operator< (const offset_datetime& lhs, const offset_datetime& rhs)
|
||||
{
|
||||
return std::make_tuple(lhs.date, lhs.time, lhs.offset) <
|
||||
std::make_tuple(rhs.date, rhs.time, rhs.offset);
|
||||
}
|
||||
inline bool operator<=(const offset_datetime& lhs, const offset_datetime& rhs)
|
||||
{
|
||||
return (lhs < rhs) || (lhs == rhs);
|
||||
}
|
||||
inline bool operator> (const offset_datetime& lhs, const offset_datetime& rhs)
|
||||
{
|
||||
return !(lhs <= rhs);
|
||||
}
|
||||
inline bool operator>=(const offset_datetime& lhs, const offset_datetime& rhs)
|
||||
{
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
template<typename charT, typename traits>
|
||||
std::basic_ostream<charT, traits>&
|
||||
operator<<(std::basic_ostream<charT, traits>& os, const offset_datetime& dt)
|
||||
{
|
||||
os << dt.date << 'T' << dt.time << dt.offset;
|
||||
return os;
|
||||
}
|
||||
|
||||
}//toml
|
||||
#endif// TOML11_DATETIME
|
@ -0,0 +1,65 @@
|
||||
// Copyright Toru Niina 2017.
|
||||
// Distributed under the MIT License.
|
||||
#ifndef TOML11_EXCEPTION_HPP
|
||||
#define TOML11_EXCEPTION_HPP
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "source_location.hpp"
|
||||
|
||||
namespace toml
|
||||
{
|
||||
|
||||
struct exception : public std::exception
|
||||
{
|
||||
public:
|
||||
explicit exception(const source_location& loc): loc_(loc) {}
|
||||
virtual ~exception() noexcept override = default;
|
||||
virtual const char* what() const noexcept override {return "";}
|
||||
virtual source_location const& location() const noexcept {return loc_;}
|
||||
|
||||
protected:
|
||||
source_location loc_;
|
||||
};
|
||||
|
||||
struct syntax_error : public toml::exception
|
||||
{
|
||||
public:
|
||||
explicit syntax_error(const std::string& what_arg, const source_location& loc)
|
||||
: exception(loc), what_(what_arg)
|
||||
{}
|
||||
virtual ~syntax_error() noexcept override = default;
|
||||
virtual const char* what() const noexcept override {return what_.c_str();}
|
||||
|
||||
protected:
|
||||
std::string what_;
|
||||
};
|
||||
|
||||
struct type_error : public toml::exception
|
||||
{
|
||||
public:
|
||||
explicit type_error(const std::string& what_arg, const source_location& loc)
|
||||
: exception(loc), what_(what_arg)
|
||||
{}
|
||||
virtual ~type_error() noexcept override = default;
|
||||
virtual const char* what() const noexcept override {return what_.c_str();}
|
||||
|
||||
protected:
|
||||
std::string what_;
|
||||
};
|
||||
|
||||
struct internal_error : public toml::exception
|
||||
{
|
||||
public:
|
||||
explicit internal_error(const std::string& what_arg, const source_location& loc)
|
||||
: exception(loc), what_(what_arg)
|
||||
{}
|
||||
virtual ~internal_error() noexcept override = default;
|
||||
virtual const char* what() const noexcept override {return what_.c_str();}
|
||||
|
||||
protected:
|
||||
std::string what_;
|
||||
};
|
||||
|
||||
} // toml
|
||||
#endif // TOML_EXCEPTION
|
@ -0,0 +1,20 @@
|
||||
// Copyright Toru Niina 2019.
|
||||
// Distributed under the MIT License.
|
||||
#ifndef TOML11_FROM_HPP
|
||||
#define TOML11_FROM_HPP
|
||||
#include "traits.hpp"
|
||||
|
||||
namespace toml
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
struct from;
|
||||
// {
|
||||
// static T from_toml(const toml::value& v)
|
||||
// {
|
||||
// // User-defined conversions ...
|
||||
// }
|
||||
// };
|
||||
|
||||
} // toml
|
||||
#endif // TOML11_FROM_HPP
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,20 @@
|
||||
// Copyright Toru Niina 2019.
|
||||
// Distributed under the MIT License.
|
||||
#ifndef TOML11_INTO_HPP
|
||||
#define TOML11_INTO_HPP
|
||||
#include "traits.hpp"
|
||||
|
||||
namespace toml
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
struct into;
|
||||
// {
|
||||
// static toml::value into_toml(const T& user_defined_type)
|
||||
// {
|
||||
// // User-defined conversions ...
|
||||
// }
|
||||
// };
|
||||
|
||||
} // toml
|
||||
#endif // TOML11_INTO_HPP
|
@ -0,0 +1,270 @@
|
||||
// Copyright Toru Niina 2017.
|
||||
// Distributed under the MIT License.
|
||||
#ifndef TOML11_LEXER_HPP
|
||||
#define TOML11_LEXER_HPP
|
||||
#include <istream>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <fstream>
|
||||
|
||||
#include "combinator.hpp"
|
||||
|
||||
namespace toml
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
// these scans contents from current location in a container of char
|
||||
// and extract a region that matches their own pattern.
|
||||
// to see the implementation of each component, see combinator.hpp.
|
||||
|
||||
using lex_wschar = either<character<' '>, character<'\t'>>;
|
||||
using lex_ws = repeat<lex_wschar, at_least<1>>;
|
||||
using lex_newline = either<character<'\n'>,
|
||||
sequence<character<'\r'>, character<'\n'>>>;
|
||||
using lex_lower = in_range<'a', 'z'>;
|
||||
using lex_upper = in_range<'A', 'Z'>;
|
||||
using lex_alpha = either<lex_lower, lex_upper>;
|
||||
using lex_digit = in_range<'0', '9'>;
|
||||
using lex_nonzero = in_range<'1', '9'>;
|
||||
using lex_oct_dig = in_range<'0', '7'>;
|
||||
using lex_bin_dig = in_range<'0', '1'>;
|
||||
using lex_hex_dig = either<lex_digit, in_range<'A', 'F'>, in_range<'a', 'f'>>;
|
||||
|
||||
using lex_hex_prefix = sequence<character<'0'>, character<'x'>>;
|
||||
using lex_oct_prefix = sequence<character<'0'>, character<'o'>>;
|
||||
using lex_bin_prefix = sequence<character<'0'>, character<'b'>>;
|
||||
using lex_underscore = character<'_'>;
|
||||
using lex_plus = character<'+'>;
|
||||
using lex_minus = character<'-'>;
|
||||
using lex_sign = either<lex_plus, lex_minus>;
|
||||
|
||||
// digit | nonzero 1*(digit | _ digit)
|
||||
using lex_unsigned_dec_int = either<sequence<lex_nonzero, repeat<
|
||||
either<lex_digit, sequence<lex_underscore, lex_digit>>, at_least<1>>>,
|
||||
lex_digit>;
|
||||
// (+|-)? unsigned_dec_int
|
||||
using lex_dec_int = sequence<maybe<lex_sign>, lex_unsigned_dec_int>;
|
||||
|
||||
// hex_prefix hex_dig *(hex_dig | _ hex_dig)
|
||||
using lex_hex_int = sequence<lex_hex_prefix, sequence<lex_hex_dig, repeat<
|
||||
either<lex_hex_dig, sequence<lex_underscore, lex_hex_dig>>, unlimited>>>;
|
||||
// oct_prefix oct_dig *(oct_dig | _ oct_dig)
|
||||
using lex_oct_int = sequence<lex_oct_prefix, sequence<lex_oct_dig, repeat<
|
||||
either<lex_oct_dig, sequence<lex_underscore, lex_oct_dig>>, unlimited>>>;
|
||||
// bin_prefix bin_dig *(bin_dig | _ bin_dig)
|
||||
using lex_bin_int = sequence<lex_bin_prefix, sequence<lex_bin_dig, repeat<
|
||||
either<lex_bin_dig, sequence<lex_underscore, lex_bin_dig>>, unlimited>>>;
|
||||
|
||||
// (dec_int | hex_int | oct_int | bin_int)
|
||||
using lex_integer = either<lex_bin_int, lex_oct_int, lex_hex_int, lex_dec_int>;
|
||||
|
||||
// ===========================================================================
|
||||
|
||||
using lex_inf = sequence<character<'i'>, character<'n'>, character<'f'>>;
|
||||
using lex_nan = sequence<character<'n'>, character<'a'>, character<'n'>>;
|
||||
using lex_special_float = sequence<maybe<lex_sign>, either<lex_inf, lex_nan>>;
|
||||
|
||||
using lex_zero_prefixable_int = sequence<lex_digit, repeat<either<lex_digit,
|
||||
sequence<lex_underscore, lex_digit>>, unlimited>>;
|
||||
|
||||
using lex_fractional_part = sequence<character<'.'>, lex_zero_prefixable_int>;
|
||||
|
||||
using lex_exponent_part = sequence<either<character<'e'>, character<'E'>>,
|
||||
maybe<lex_sign>, lex_zero_prefixable_int>;
|
||||
|
||||
using lex_float = either<lex_special_float,
|
||||
sequence<lex_dec_int, either<lex_exponent_part,
|
||||
sequence<lex_fractional_part, maybe<lex_exponent_part>>>>>;
|
||||
|
||||
// ===========================================================================
|
||||
|
||||
using lex_true = sequence<character<'t'>, character<'r'>,
|
||||
character<'u'>, character<'e'>>;
|
||||
using lex_false = sequence<character<'f'>, character<'a'>, character<'l'>,
|
||||
character<'s'>, character<'e'>>;
|
||||
using lex_boolean = either<lex_true, lex_false>;
|
||||
|
||||
// ===========================================================================
|
||||
|
||||
using lex_date_fullyear = repeat<lex_digit, exactly<4>>;
|
||||
using lex_date_month = repeat<lex_digit, exactly<2>>;
|
||||
using lex_date_mday = repeat<lex_digit, exactly<2>>;
|
||||
using lex_time_delim = either<character<'T'>, character<'t'>, character<' '>>;
|
||||
using lex_time_hour = repeat<lex_digit, exactly<2>>;
|
||||
using lex_time_minute = repeat<lex_digit, exactly<2>>;
|
||||
using lex_time_second = repeat<lex_digit, exactly<2>>;
|
||||
using lex_time_secfrac = sequence<character<'.'>,
|
||||
repeat<lex_digit, at_least<1>>>;
|
||||
|
||||
using lex_time_numoffset = sequence<either<character<'+'>, character<'-'>>,
|
||||
sequence<lex_time_hour, character<':'>,
|
||||
lex_time_minute>>;
|
||||
using lex_time_offset = either<character<'Z'>, character<'z'>,
|
||||
lex_time_numoffset>;
|
||||
|
||||
using lex_partial_time = sequence<lex_time_hour, character<':'>,
|
||||
lex_time_minute, character<':'>,
|
||||
lex_time_second, maybe<lex_time_secfrac>>;
|
||||
using lex_full_date = sequence<lex_date_fullyear, character<'-'>,
|
||||
lex_date_month, character<'-'>,
|
||||
lex_date_mday>;
|
||||
using lex_full_time = sequence<lex_partial_time, lex_time_offset>;
|
||||
|
||||
using lex_offset_date_time = sequence<lex_full_date, lex_time_delim, lex_full_time>;
|
||||
using lex_local_date_time = sequence<lex_full_date, lex_time_delim, lex_partial_time>;
|
||||
using lex_local_date = lex_full_date;
|
||||
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
|
||||
character<0x22>, character<0x5C>,
|
||||
character<0x7F>>>;
|
||||
|
||||
using lex_escape = character<'\\'>;
|
||||
using lex_escape_unicode_short = sequence<character<'u'>,
|
||||
repeat<lex_hex_dig, exactly<4>>>;
|
||||
using lex_escape_unicode_long = sequence<character<'U'>,
|
||||
repeat<lex_hex_dig, exactly<8>>>;
|
||||
using lex_escape_seq_char = either<character<'"'>, character<'\\'>,
|
||||
character<'b'>, character<'f'>,
|
||||
character<'n'>, character<'r'>,
|
||||
character<'t'>,
|
||||
lex_escape_unicode_short,
|
||||
lex_escape_unicode_long
|
||||
>;
|
||||
using lex_escaped = sequence<lex_escape, lex_escape_seq_char>;
|
||||
using lex_basic_char = either<lex_basic_unescaped, lex_escaped>;
|
||||
using lex_basic_string = sequence<lex_quotation_mark,
|
||||
repeat<lex_basic_char, unlimited>,
|
||||
lex_quotation_mark>;
|
||||
|
||||
// After toml post-v0.5.0, it is explicitly clarified how quotes in ml-strings
|
||||
// are allowed to be used.
|
||||
// After this, the following strings are *explicitly* allowed.
|
||||
// - One or two `"`s in a multi-line basic string is allowed wherever it is.
|
||||
// - Three consecutive `"`s in a multi-line basic string is considered as a delimiter.
|
||||
// - One or two `"`s can appear just before or after the delimiter.
|
||||
// ```toml
|
||||
// str4 = """Here are two quotation marks: "". Simple enough."""
|
||||
// str5 = """Here are three quotation marks: ""\"."""
|
||||
// str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\"."""
|
||||
// str7 = """"This," she said, "is just a pointless statement.""""
|
||||
// ```
|
||||
// In the current implementation (v3.3.0), it is difficult to parse `str7` in
|
||||
// the above example. It is difficult to recognize `"` at the end of string body
|
||||
// collectly. It will be misunderstood as a `"""` delimiter and an additional,
|
||||
// invalid `"`. Like this:
|
||||
// ```console
|
||||
// what(): [error] toml::parse_table: invalid line format
|
||||
// --> hoge.toml
|
||||
// |
|
||||
// 13 | str7 = """"This," she said, "is just a pointless statement.""""
|
||||
// | ^- 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`.
|
||||
// `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.
|
||||
//
|
||||
using lex_ml_basic_string_delim = repeat<lex_quotation_mark, exactly<3>>;
|
||||
using lex_ml_basic_string_open = lex_ml_basic_string_delim;
|
||||
using lex_ml_basic_string_close = sequence<
|
||||
repeat<lex_quotation_mark, exactly<3>>,
|
||||
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
|
||||
character<0x5C>, // backslash
|
||||
character<0x7F>, // DEL
|
||||
lex_ml_basic_string_delim>>;
|
||||
|
||||
using lex_ml_basic_escaped_newline = sequence<
|
||||
lex_escape, maybe<lex_ws>, lex_newline,
|
||||
repeat<either<lex_ws, lex_newline>, unlimited>>;
|
||||
|
||||
using lex_ml_basic_char = either<lex_ml_basic_unescaped, lex_escaped>;
|
||||
using lex_ml_basic_body = repeat<either<lex_ml_basic_char, lex_newline,
|
||||
lex_ml_basic_escaped_newline>,
|
||||
unlimited>;
|
||||
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_apostrophe = character<'\''>;
|
||||
using lex_literal_string = sequence<lex_apostrophe,
|
||||
repeat<lex_literal_char, unlimited>,
|
||||
lex_apostrophe>;
|
||||
|
||||
// the same reason as above.
|
||||
using lex_ml_literal_string_delim = repeat<lex_apostrophe, exactly<3>>;
|
||||
using lex_ml_literal_string_open = lex_ml_literal_string_delim;
|
||||
using lex_ml_literal_string_close = sequence<
|
||||
repeat<lex_apostrophe, exactly<3>>,
|
||||
maybe<lex_apostrophe>, maybe<lex_apostrophe>
|
||||
>;
|
||||
|
||||
using lex_ml_literal_char = exclude<either<in_range<0x00, 0x08>,
|
||||
in_range<0x10, 0x1F>,
|
||||
character<0x7F>,
|
||||
lex_ml_literal_string_delim>>;
|
||||
using lex_ml_literal_body = repeat<either<lex_ml_literal_char, lex_newline>,
|
||||
unlimited>;
|
||||
using lex_ml_literal_string = sequence<lex_ml_literal_string_open,
|
||||
lex_ml_literal_body,
|
||||
lex_ml_literal_string_close>;
|
||||
|
||||
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,
|
||||
character<'-'>, character<'_'>>,
|
||||
at_least<1>>;
|
||||
using lex_quoted_key = either<lex_basic_string, lex_literal_string>;
|
||||
using lex_simple_key = either<lex_unquoted_key, lex_quoted_key>;
|
||||
using lex_dotted_key = sequence<lex_simple_key,
|
||||
repeat<sequence<lex_dot_sep, lex_simple_key>,
|
||||
at_least<1>
|
||||
>
|
||||
>;
|
||||
using lex_key = either<lex_dotted_key, lex_simple_key>;
|
||||
|
||||
using lex_keyval_sep = sequence<maybe<lex_ws>,
|
||||
character<'='>,
|
||||
maybe<lex_ws>>;
|
||||
|
||||
using lex_std_table_open = character<'['>;
|
||||
using lex_std_table_close = character<']'>;
|
||||
using lex_std_table = sequence<lex_std_table_open,
|
||||
maybe<lex_ws>,
|
||||
lex_key,
|
||||
maybe<lex_ws>,
|
||||
lex_std_table_close>;
|
||||
|
||||
using lex_array_table_open = sequence<lex_std_table_open, lex_std_table_open>;
|
||||
using lex_array_table_close = sequence<lex_std_table_close, lex_std_table_close>;
|
||||
using lex_array_table = sequence<lex_array_table_open,
|
||||
maybe<lex_ws>,
|
||||
lex_key,
|
||||
maybe<lex_ws>,
|
||||
lex_array_table_close>;
|
||||
|
||||
} // detail
|
||||
} // toml
|
||||
#endif // TOML_LEXER_HPP
|
@ -0,0 +1,107 @@
|
||||
// Copyright Toru Niina 2019.
|
||||
// Distributed under the MIT License.
|
||||
#ifndef TOML11_LITERAL_HPP
|
||||
#define TOML11_LITERAL_HPP
|
||||
#include "parser.hpp"
|
||||
|
||||
namespace toml
|
||||
{
|
||||
inline namespace literals
|
||||
{
|
||||
inline namespace toml_literals
|
||||
{
|
||||
|
||||
// implementation
|
||||
inline ::toml::value literal_internal_impl(::toml::detail::location loc)
|
||||
{
|
||||
// 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>,
|
||||
::toml::detail::maybe<::toml::detail::lex_comment>,
|
||||
::toml::detail::lex_newline
|
||||
>, ::toml::detail::at_least<1>>;
|
||||
skip_line::invoke(loc);
|
||||
|
||||
// if there are some whitespaces before a value, skip them.
|
||||
using skip_ws = ::toml::detail::repeat<
|
||||
::toml::detail::lex_ws, ::toml::detail::at_least<1>>;
|
||||
skip_ws::invoke(loc);
|
||||
|
||||
// to distinguish arrays and tables, first check it is a table or not.
|
||||
//
|
||||
// "[1,2,3]"_toml; // this is an array
|
||||
// "[table]"_toml; // a table that has an empty table named "table" inside.
|
||||
// "[[1,2,3]]"_toml; // this is an array of arrays
|
||||
// "[[table]]"_toml; // this is a table that has an array of tables inside.
|
||||
//
|
||||
// "[[1]]"_toml; // this can be both... (currently it becomes a table)
|
||||
// "1 = [{}]"_toml; // this is a table that has an array of table named 1.
|
||||
// "[[1,]]"_toml; // this is an array of arrays.
|
||||
// "[[1],]"_toml; // this also.
|
||||
|
||||
const auto the_front = loc.iter();
|
||||
|
||||
const bool is_table_key = ::toml::detail::lex_std_table::invoke(loc);
|
||||
loc.reset(the_front);
|
||||
|
||||
const bool is_aots_key = ::toml::detail::lex_array_table::invoke(loc);
|
||||
loc.reset(the_front);
|
||||
|
||||
// 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))
|
||||
{
|
||||
return data.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// Note that still it can be a table, because the literal might be something
|
||||
// like the following.
|
||||
// ```cpp
|
||||
// R"( // c++11 raw string literals
|
||||
// key = "value"
|
||||
// int = 42
|
||||
// )"_toml;
|
||||
// ```
|
||||
// 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))
|
||||
{
|
||||
return data.unwrap();
|
||||
}
|
||||
else // none of them.
|
||||
{
|
||||
throw ::toml::syntax_error(data.unwrap_err(), source_location(loc));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
inline ::toml::value 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));
|
||||
return literal_internal_impl(std::move(loc));
|
||||
}
|
||||
|
||||
// value of __cplusplus in C++2a/20 mode is not fixed yet along compilers.
|
||||
// So here we use the feature test macro for `char8_t` itself.
|
||||
#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)
|
||||
{
|
||||
::toml::detail::location loc(
|
||||
std::string("TOML literal encoded in a C++ code"),
|
||||
std::vector<char>(reinterpret_cast<const char*>(str),
|
||||
reinterpret_cast<const char*>(str) + len));
|
||||
return literal_internal_impl(std::move(loc));
|
||||
}
|
||||
#endif
|
||||
|
||||
} // toml_literals
|
||||
} // literals
|
||||
} // toml
|
||||
#endif//TOML11_LITERAL_HPP
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,417 @@
|
||||
// Copyright Toru Niina 2017.
|
||||
// Distributed under the MIT License.
|
||||
#ifndef TOML11_REGION_HPP
|
||||
#define TOML11_REGION_HPP
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <iomanip>
|
||||
#include <cassert>
|
||||
#include "color.hpp"
|
||||
|
||||
namespace toml
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
// helper function to avoid std::string(0, 'c') or std::string(iter, iter)
|
||||
template<typename Iterator>
|
||||
std::string make_string(Iterator first, Iterator last)
|
||||
{
|
||||
if(first == last) {return "";}
|
||||
return std::string(first, last);
|
||||
}
|
||||
inline std::string make_string(std::size_t len, char c)
|
||||
{
|
||||
if(len == 0) {return "";}
|
||||
return std::string(len, c);
|
||||
}
|
||||
|
||||
// region_base is a base class of location and region that are defined below.
|
||||
// it will be used to generate better error messages.
|
||||
struct region_base
|
||||
{
|
||||
region_base() = default;
|
||||
virtual ~region_base() = default;
|
||||
region_base(const region_base&) = default;
|
||||
region_base(region_base&& ) = default;
|
||||
region_base& operator=(const region_base&) = default;
|
||||
region_base& operator=(region_base&& ) = default;
|
||||
|
||||
virtual bool is_ok() const noexcept {return false;}
|
||||
virtual char front() const noexcept {return '\0';}
|
||||
|
||||
virtual std::string str() const {return std::string("unknown region");}
|
||||
virtual std::string name() const {return std::string("unknown file");}
|
||||
virtual std::string line() const {return std::string("unknown line");}
|
||||
virtual std::string line_num() const {return std::string("?");}
|
||||
|
||||
// length of the region
|
||||
virtual std::size_t size() const noexcept {return 0;}
|
||||
// number of characters in the line before the region
|
||||
virtual std::size_t before() const noexcept {return 0;}
|
||||
// number of characters in the line after the region
|
||||
virtual std::size_t after() const noexcept {return 0;}
|
||||
|
||||
virtual std::vector<std::string> comments() const {return {};}
|
||||
// ```toml
|
||||
// # comment_before
|
||||
// key = "value" # comment_inline
|
||||
// ```
|
||||
};
|
||||
|
||||
// location represents a position in a container, which contains a file content.
|
||||
// it can be considered as a region that contains only one character.
|
||||
//
|
||||
// it contains pointer to the file content and iterator that points the current
|
||||
// location.
|
||||
struct location final : public region_base
|
||||
{
|
||||
using const_iterator = typename std::vector<char>::const_iterator;
|
||||
using difference_type = typename const_iterator::difference_type;
|
||||
using source_ptr = std::shared_ptr<const std::vector<char>>;
|
||||
|
||||
location(std::string 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())
|
||||
{}
|
||||
location(std::string 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())
|
||||
{}
|
||||
|
||||
location(const location&) = default;
|
||||
location(location&&) = default;
|
||||
location& operator=(const location&) = default;
|
||||
location& operator=(location&&) = default;
|
||||
~location() = default;
|
||||
|
||||
bool is_ok() const noexcept override {return static_cast<bool>(source_);}
|
||||
char front() const noexcept override {return *iter_;}
|
||||
|
||||
// this const prohibits codes like `++(loc.iter())`.
|
||||
const const_iterator iter() const noexcept {return iter_;}
|
||||
|
||||
const_iterator begin() const noexcept {return source_->cbegin();}
|
||||
const_iterator end() const noexcept {return source_->cend();}
|
||||
|
||||
// XXX `location::line_num()` used to be implemented using `std::count` to
|
||||
// count a number of '\n'. But with a long toml file (typically, 10k lines),
|
||||
// it becomes intolerably slow because each time it generates error messages,
|
||||
// it counts '\n' from thousands of characters. To workaround it, I decided
|
||||
// to introduce `location::line_number_` member variable and synchronize it
|
||||
// to the location changes the point to look. So an overload of `iter()`
|
||||
// which returns mutable reference is removed and `advance()`, `retrace()`
|
||||
// and `reset()` is added.
|
||||
void advance(difference_type n = 1) noexcept
|
||||
{
|
||||
this->line_number_ += static_cast<std::size_t>(
|
||||
std::count(this->iter_, std::next(this->iter_, n), '\n'));
|
||||
this->iter_ += n;
|
||||
return;
|
||||
}
|
||||
void retrace(difference_type n = 1) noexcept
|
||||
{
|
||||
this->line_number_ -= static_cast<std::size_t>(
|
||||
std::count(std::prev(this->iter_, n), this->iter_, '\n'));
|
||||
this->iter_ -= n;
|
||||
return;
|
||||
}
|
||||
void reset(const_iterator rollback) noexcept
|
||||
{
|
||||
// since c++11, std::distance works in both ways for random-access
|
||||
// iterators and returns a negative value if `first > last`.
|
||||
if(0 <= std::distance(rollback, this->iter_)) // rollback < iter
|
||||
{
|
||||
this->line_number_ -= static_cast<std::size_t>(
|
||||
std::count(rollback, this->iter_, '\n'));
|
||||
}
|
||||
else // iter < rollback [[unlikely]]
|
||||
{
|
||||
this->line_number_ += static_cast<std::size_t>(
|
||||
std::count(this->iter_, rollback, '\n'));
|
||||
}
|
||||
this->iter_ = rollback;
|
||||
return;
|
||||
}
|
||||
|
||||
std::string str() const override {return make_string(1, *this->iter());}
|
||||
std::string name() const override {return source_name_;}
|
||||
|
||||
std::string line_num() const override
|
||||
{
|
||||
return std::to_string(this->line_number_);
|
||||
}
|
||||
|
||||
std::string line() const override
|
||||
{
|
||||
return make_string(this->line_begin(), this->line_end());
|
||||
}
|
||||
|
||||
const_iterator line_begin() const noexcept
|
||||
{
|
||||
using reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
return std::find(reverse_iterator(this->iter()),
|
||||
reverse_iterator(this->begin()), '\n').base();
|
||||
}
|
||||
const_iterator line_end() const noexcept
|
||||
{
|
||||
return std::find(this->iter(), this->end(), '\n');
|
||||
}
|
||||
|
||||
// location is always points a character. so the size is 1.
|
||||
std::size_t size() const noexcept override
|
||||
{
|
||||
return 1u;
|
||||
}
|
||||
std::size_t before() const noexcept override
|
||||
{
|
||||
const auto sz = std::distance(this->line_begin(), this->iter());
|
||||
assert(sz >= 0);
|
||||
return static_cast<std::size_t>(sz);
|
||||
}
|
||||
std::size_t after() const noexcept override
|
||||
{
|
||||
const auto sz = std::distance(this->iter(), this->line_end());
|
||||
assert(sz >= 0);
|
||||
return static_cast<std::size_t>(sz);
|
||||
}
|
||||
|
||||
source_ptr const& source() const& noexcept {return source_;}
|
||||
source_ptr&& source() && noexcept {return std::move(source_);}
|
||||
|
||||
private:
|
||||
|
||||
source_ptr source_;
|
||||
std::size_t line_number_;
|
||||
std::string source_name_;
|
||||
const_iterator iter_;
|
||||
};
|
||||
|
||||
// region represents a range in a container, which contains a file content.
|
||||
//
|
||||
// it contains pointer to the file content and iterator that points the first
|
||||
// and last location.
|
||||
struct region final : public region_base
|
||||
{
|
||||
using const_iterator = typename std::vector<char>::const_iterator;
|
||||
using source_ptr = std::shared_ptr<const std::vector<char>>;
|
||||
|
||||
// delete default constructor. source_ never be null.
|
||||
region() = delete;
|
||||
|
||||
explicit region(const location& loc)
|
||||
: source_(loc.source()), source_name_(loc.name()),
|
||||
first_(loc.iter()), last_(loc.iter())
|
||||
{}
|
||||
explicit region(location&& loc)
|
||||
: source_(loc.source()), source_name_(loc.name()),
|
||||
first_(loc.iter()), last_(loc.iter())
|
||||
{}
|
||||
|
||||
region(const location& loc, const_iterator f, const_iterator l)
|
||||
: source_(loc.source()), source_name_(loc.name()), first_(f), last_(l)
|
||||
{}
|
||||
region(location&& loc, const_iterator f, const_iterator l)
|
||||
: source_(loc.source()), source_name_(loc.name()), first_(f), last_(l)
|
||||
{}
|
||||
|
||||
region(const region&) = default;
|
||||
region(region&&) = default;
|
||||
region& operator=(const region&) = default;
|
||||
region& operator=(region&&) = default;
|
||||
~region() = default;
|
||||
|
||||
region& operator+=(const region& other)
|
||||
{
|
||||
// different regions cannot be concatenated
|
||||
assert(this->begin() == other.begin() && this->end() == other.end() &&
|
||||
this->last_ == other.first_);
|
||||
|
||||
this->last_ = other.last_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool is_ok() const noexcept override {return static_cast<bool>(source_);}
|
||||
char front() const noexcept override {return *first_;}
|
||||
|
||||
std::string str() const override {return make_string(first_, last_);}
|
||||
std::string line() const override
|
||||
{
|
||||
if(this->contain_newline())
|
||||
{
|
||||
return make_string(this->line_begin(),
|
||||
std::find(this->line_begin(), this->last(), '\n'));
|
||||
}
|
||||
return make_string(this->line_begin(), this->line_end());
|
||||
}
|
||||
std::string line_num() const override
|
||||
{
|
||||
return std::to_string(1 + std::count(this->begin(), this->first(), '\n'));
|
||||
}
|
||||
|
||||
std::size_t size() const noexcept override
|
||||
{
|
||||
const auto sz = std::distance(first_, last_);
|
||||
assert(sz >= 0);
|
||||
return static_cast<std::size_t>(sz);
|
||||
}
|
||||
std::size_t before() const noexcept override
|
||||
{
|
||||
const auto sz = std::distance(this->line_begin(), this->first());
|
||||
assert(sz >= 0);
|
||||
return static_cast<std::size_t>(sz);
|
||||
}
|
||||
std::size_t after() const noexcept override
|
||||
{
|
||||
const auto sz = std::distance(this->last(), this->line_end());
|
||||
assert(sz >= 0);
|
||||
return static_cast<std::size_t>(sz);
|
||||
}
|
||||
|
||||
bool contain_newline() const noexcept
|
||||
{
|
||||
return std::find(this->first(), this->last(), '\n') != this->last();
|
||||
}
|
||||
|
||||
const_iterator line_begin() const noexcept
|
||||
{
|
||||
using reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
return std::find(reverse_iterator(this->first()),
|
||||
reverse_iterator(this->begin()), '\n').base();
|
||||
}
|
||||
const_iterator line_end() const noexcept
|
||||
{
|
||||
return std::find(this->last(), this->end(), '\n');
|
||||
}
|
||||
|
||||
const_iterator begin() const noexcept {return source_->cbegin();}
|
||||
const_iterator end() const noexcept {return source_->cend();}
|
||||
const_iterator first() const noexcept {return first_;}
|
||||
const_iterator last() const noexcept {return last_;}
|
||||
|
||||
source_ptr const& source() const& noexcept {return source_;}
|
||||
source_ptr&& source() && noexcept {return std::move(source_);}
|
||||
|
||||
std::string name() const override {return source_name_;}
|
||||
|
||||
std::vector<std::string> comments() const override
|
||||
{
|
||||
// assuming the current region (`*this`) points a value.
|
||||
// ```toml
|
||||
// a = "value"
|
||||
// ^^^^^^^- this region
|
||||
// ```
|
||||
using rev_iter = std::reverse_iterator<const_iterator>;
|
||||
|
||||
std::vector<std::string> com{};
|
||||
{
|
||||
// find comments just before the current region.
|
||||
// ```toml
|
||||
// # this should be collected.
|
||||
// # this also.
|
||||
// a = value # not this.
|
||||
// ```
|
||||
|
||||
// # this is a comment for `a`, not array elements.
|
||||
// a = [1, 2, 3, 4, 5]
|
||||
if(this->first() == std::find_if(this->line_begin(), this->first(),
|
||||
[](const char c) noexcept -> bool {return c == '[' || c == '{';}))
|
||||
{
|
||||
auto iter = this->line_begin(); // points the first character
|
||||
while(iter != this->begin())
|
||||
{
|
||||
iter = std::prev(iter);
|
||||
|
||||
// range [line_start, iter) represents the previous line
|
||||
const auto line_start = std::find(
|
||||
rev_iter(iter), rev_iter(this->begin()), '\n').base();
|
||||
const auto comment_found = std::find(line_start, iter, '#');
|
||||
if(comment_found == iter)
|
||||
{
|
||||
break; // comment not found.
|
||||
}
|
||||
|
||||
// exclude the following case.
|
||||
// > a = "foo" # comment // <-- this is not a comment for b but a.
|
||||
// > b = "current value"
|
||||
if(std::all_of(line_start, comment_found,
|
||||
[](const char c) noexcept -> bool {
|
||||
return c == ' ' || c == '\t';
|
||||
}))
|
||||
{
|
||||
// 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));
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
iter = line_start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(com.size() > 1)
|
||||
{
|
||||
std::reverse(com.begin(), com.end());
|
||||
}
|
||||
|
||||
{
|
||||
// find comments just after the current region.
|
||||
// ```toml
|
||||
// # not this.
|
||||
// a = value # this one.
|
||||
// a = [ # not this (technically difficult)
|
||||
//
|
||||
// ] # and this.
|
||||
// ```
|
||||
// The reason why it's difficult is that it requires parsing in the
|
||||
// following case.
|
||||
// ```toml
|
||||
// a = [ 10 # this comment is for `10`. not for `a` but `a[0]`.
|
||||
// # ...
|
||||
// ] # this is apparently a comment for a.
|
||||
//
|
||||
// b = [
|
||||
// 3.14 ] # there is no way to add a comment to `3.14` currently.
|
||||
//
|
||||
// c = [
|
||||
// 3.14 # do this if you need a comment here.
|
||||
// ]
|
||||
// ```
|
||||
const auto comment_found =
|
||||
std::find(this->last(), this->line_end(), '#');
|
||||
if(comment_found != this->line_end()) // '#' found
|
||||
{
|
||||
// table = {key = "value"} # what is this for?
|
||||
// the above comment is not for "value", but {key="value"}.
|
||||
if(comment_found == std::find_if(this->last(), comment_found,
|
||||
[](const char c) noexcept -> bool {
|
||||
return !(c == ' ' || c == '\t' || c == ',');
|
||||
}))
|
||||
{
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
return com;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
source_ptr source_;
|
||||
std::string source_name_;
|
||||
const_iterator first_, last_;
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // toml
|
||||
#endif// TOML11_REGION_H
|
@ -0,0 +1,717 @@
|
||||
// Copyright Toru Niina 2017.
|
||||
// Distributed under the MIT License.
|
||||
#ifndef TOML11_RESULT_HPP
|
||||
#define TOML11_RESULT_HPP
|
||||
#include "traits.hpp"
|
||||
#include <type_traits>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include <new>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <cassert>
|
||||
|
||||
namespace toml
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
struct success
|
||||
{
|
||||
using value_type = T;
|
||||
value_type value;
|
||||
|
||||
explicit success(const value_type& v)
|
||||
noexcept(std::is_nothrow_copy_constructible<value_type>::value)
|
||||
: value(v)
|
||||
{}
|
||||
explicit success(value_type&& v)
|
||||
noexcept(std::is_nothrow_move_constructible<value_type>::value)
|
||||
: value(std::move(v))
|
||||
{}
|
||||
|
||||
template<typename U>
|
||||
explicit success(U&& v): value(std::forward<U>(v)) {}
|
||||
|
||||
template<typename U>
|
||||
explicit success(const success<U>& v): value(v.value) {}
|
||||
template<typename U>
|
||||
explicit success(success<U>&& v): value(std::move(v.value)) {}
|
||||
|
||||
~success() = default;
|
||||
success(const success&) = default;
|
||||
success(success&&) = default;
|
||||
success& operator=(const success&) = default;
|
||||
success& operator=(success&&) = default;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct failure
|
||||
{
|
||||
using value_type = T;
|
||||
value_type value;
|
||||
|
||||
explicit failure(const value_type& v)
|
||||
noexcept(std::is_nothrow_copy_constructible<value_type>::value)
|
||||
: value(v)
|
||||
{}
|
||||
explicit failure(value_type&& v)
|
||||
noexcept(std::is_nothrow_move_constructible<value_type>::value)
|
||||
: value(std::move(v))
|
||||
{}
|
||||
|
||||
template<typename U>
|
||||
explicit failure(U&& v): value(std::forward<U>(v)) {}
|
||||
|
||||
template<typename U>
|
||||
explicit failure(const failure<U>& v): value(v.value) {}
|
||||
template<typename U>
|
||||
explicit failure(failure<U>&& v): value(std::move(v.value)) {}
|
||||
|
||||
~failure() = default;
|
||||
failure(const failure&) = default;
|
||||
failure(failure&&) = default;
|
||||
failure& operator=(const failure&) = default;
|
||||
failure& operator=(failure&&) = default;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
success<typename std::remove_cv<typename std::remove_reference<T>::type>::type>
|
||||
ok(T&& v)
|
||||
{
|
||||
return success<
|
||||
typename std::remove_cv<typename std::remove_reference<T>::type>::type
|
||||
>(std::forward<T>(v));
|
||||
}
|
||||
template<typename T>
|
||||
failure<typename std::remove_cv<typename std::remove_reference<T>::type>::type>
|
||||
err(T&& v)
|
||||
{
|
||||
return failure<
|
||||
typename std::remove_cv<typename std::remove_reference<T>::type>::type
|
||||
>(std::forward<T>(v));
|
||||
}
|
||||
|
||||
inline success<std::string> ok(const char* literal)
|
||||
{
|
||||
return success<std::string>(std::string(literal));
|
||||
}
|
||||
inline failure<std::string> err(const char* literal)
|
||||
{
|
||||
return failure<std::string>(std::string(literal));
|
||||
}
|
||||
|
||||
|
||||
template<typename T, typename E>
|
||||
struct result
|
||||
{
|
||||
using value_type = T;
|
||||
using error_type = E;
|
||||
using success_type = success<value_type>;
|
||||
using failure_type = failure<error_type>;
|
||||
|
||||
result(const success_type& s): is_ok_(true)
|
||||
{
|
||||
auto tmp = ::new(std::addressof(this->succ)) success_type(s);
|
||||
assert(tmp == std::addressof(this->succ));
|
||||
(void)tmp;
|
||||
}
|
||||
result(const failure_type& f): is_ok_(false)
|
||||
{
|
||||
auto tmp = ::new(std::addressof(this->fail)) failure_type(f);
|
||||
assert(tmp == std::addressof(this->fail));
|
||||
(void)tmp;
|
||||
}
|
||||
result(success_type&& s): is_ok_(true)
|
||||
{
|
||||
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s));
|
||||
assert(tmp == std::addressof(this->succ));
|
||||
(void)tmp;
|
||||
}
|
||||
result(failure_type&& f): is_ok_(false)
|
||||
{
|
||||
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f));
|
||||
assert(tmp == std::addressof(this->fail));
|
||||
(void)tmp;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
result(const success<U>& s): is_ok_(true)
|
||||
{
|
||||
auto tmp = ::new(std::addressof(this->succ)) success_type(s.value);
|
||||
assert(tmp == std::addressof(this->succ));
|
||||
(void)tmp;
|
||||
}
|
||||
template<typename U>
|
||||
result(const failure<U>& f): is_ok_(false)
|
||||
{
|
||||
auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value);
|
||||
assert(tmp == std::addressof(this->fail));
|
||||
(void)tmp;
|
||||
}
|
||||
template<typename U>
|
||||
result(success<U>&& s): is_ok_(true)
|
||||
{
|
||||
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value));
|
||||
assert(tmp == std::addressof(this->succ));
|
||||
(void)tmp;
|
||||
}
|
||||
template<typename U>
|
||||
result(failure<U>&& f): is_ok_(false)
|
||||
{
|
||||
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value));
|
||||
assert(tmp == std::addressof(this->fail));
|
||||
(void)tmp;
|
||||
}
|
||||
|
||||
result& operator=(const success_type& s)
|
||||
{
|
||||
this->cleanup();
|
||||
this->is_ok_ = true;
|
||||
auto tmp = ::new(std::addressof(this->succ)) success_type(s);
|
||||
assert(tmp == std::addressof(this->succ));
|
||||
(void)tmp;
|
||||
return *this;
|
||||
}
|
||||
result& operator=(const failure_type& f)
|
||||
{
|
||||
this->cleanup();
|
||||
this->is_ok_ = false;
|
||||
auto tmp = ::new(std::addressof(this->fail)) failure_type(f);
|
||||
assert(tmp == std::addressof(this->fail));
|
||||
(void)tmp;
|
||||
return *this;
|
||||
}
|
||||
result& operator=(success_type&& s)
|
||||
{
|
||||
this->cleanup();
|
||||
this->is_ok_ = true;
|
||||
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s));
|
||||
assert(tmp == std::addressof(this->succ));
|
||||
(void)tmp;
|
||||
return *this;
|
||||
}
|
||||
result& operator=(failure_type&& f)
|
||||
{
|
||||
this->cleanup();
|
||||
this->is_ok_ = false;
|
||||
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f));
|
||||
assert(tmp == std::addressof(this->fail));
|
||||
(void)tmp;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
result& operator=(const success<U>& s)
|
||||
{
|
||||
this->cleanup();
|
||||
this->is_ok_ = true;
|
||||
auto tmp = ::new(std::addressof(this->succ)) success_type(s.value);
|
||||
assert(tmp == std::addressof(this->succ));
|
||||
(void)tmp;
|
||||
return *this;
|
||||
}
|
||||
template<typename U>
|
||||
result& operator=(const failure<U>& f)
|
||||
{
|
||||
this->cleanup();
|
||||
this->is_ok_ = false;
|
||||
auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value);
|
||||
assert(tmp == std::addressof(this->fail));
|
||||
(void)tmp;
|
||||
return *this;
|
||||
}
|
||||
template<typename U>
|
||||
result& operator=(success<U>&& s)
|
||||
{
|
||||
this->cleanup();
|
||||
this->is_ok_ = true;
|
||||
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value));
|
||||
assert(tmp == std::addressof(this->succ));
|
||||
(void)tmp;
|
||||
return *this;
|
||||
}
|
||||
template<typename U>
|
||||
result& operator=(failure<U>&& f)
|
||||
{
|
||||
this->cleanup();
|
||||
this->is_ok_ = false;
|
||||
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value));
|
||||
assert(tmp == std::addressof(this->fail));
|
||||
(void)tmp;
|
||||
return *this;
|
||||
}
|
||||
|
||||
~result() noexcept {this->cleanup();}
|
||||
|
||||
result(const result& other): is_ok_(other.is_ok())
|
||||
{
|
||||
if(other.is_ok())
|
||||
{
|
||||
auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok());
|
||||
assert(tmp == std::addressof(this->succ));
|
||||
(void)tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err());
|
||||
assert(tmp == std::addressof(this->fail));
|
||||
(void)tmp;
|
||||
}
|
||||
}
|
||||
result(result&& other): is_ok_(other.is_ok())
|
||||
{
|
||||
if(other.is_ok())
|
||||
{
|
||||
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok()));
|
||||
assert(tmp == std::addressof(this->succ));
|
||||
(void)tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err()));
|
||||
assert(tmp == std::addressof(this->fail));
|
||||
(void)tmp;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename U, typename F>
|
||||
result(const result<U, F>& other): is_ok_(other.is_ok())
|
||||
{
|
||||
if(other.is_ok())
|
||||
{
|
||||
auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok());
|
||||
assert(tmp == std::addressof(this->succ));
|
||||
(void)tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err());
|
||||
assert(tmp == std::addressof(this->fail));
|
||||
(void)tmp;
|
||||
}
|
||||
}
|
||||
template<typename U, typename F>
|
||||
result(result<U, F>&& other): is_ok_(other.is_ok())
|
||||
{
|
||||
if(other.is_ok())
|
||||
{
|
||||
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok()));
|
||||
assert(tmp == std::addressof(this->succ));
|
||||
(void)tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err()));
|
||||
assert(tmp == std::addressof(this->fail));
|
||||
(void)tmp;
|
||||
}
|
||||
}
|
||||
|
||||
result& operator=(const result& other)
|
||||
{
|
||||
this->cleanup();
|
||||
if(other.is_ok())
|
||||
{
|
||||
auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok());
|
||||
assert(tmp == std::addressof(this->succ));
|
||||
(void)tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err());
|
||||
assert(tmp == std::addressof(this->fail));
|
||||
(void)tmp;
|
||||
}
|
||||
is_ok_ = other.is_ok();
|
||||
return *this;
|
||||
}
|
||||
result& operator=(result&& other)
|
||||
{
|
||||
this->cleanup();
|
||||
if(other.is_ok())
|
||||
{
|
||||
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok()));
|
||||
assert(tmp == std::addressof(this->succ));
|
||||
(void)tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err()));
|
||||
assert(tmp == std::addressof(this->fail));
|
||||
(void)tmp;
|
||||
}
|
||||
is_ok_ = other.is_ok();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename U, typename F>
|
||||
result& operator=(const result<U, F>& other)
|
||||
{
|
||||
this->cleanup();
|
||||
if(other.is_ok())
|
||||
{
|
||||
auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok());
|
||||
assert(tmp == std::addressof(this->succ));
|
||||
(void)tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err());
|
||||
assert(tmp == std::addressof(this->fail));
|
||||
(void)tmp;
|
||||
}
|
||||
is_ok_ = other.is_ok();
|
||||
return *this;
|
||||
}
|
||||
template<typename U, typename F>
|
||||
result& operator=(result<U, F>&& other)
|
||||
{
|
||||
this->cleanup();
|
||||
if(other.is_ok())
|
||||
{
|
||||
auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok()));
|
||||
assert(tmp == std::addressof(this->succ));
|
||||
(void)tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err()));
|
||||
assert(tmp == std::addressof(this->fail));
|
||||
(void)tmp;
|
||||
}
|
||||
is_ok_ = other.is_ok();
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool is_ok() const noexcept {return is_ok_;}
|
||||
bool is_err() const noexcept {return !is_ok_;}
|
||||
|
||||
operator bool() const noexcept {return is_ok_;}
|
||||
|
||||
value_type& unwrap() &
|
||||
{
|
||||
if(is_err())
|
||||
{
|
||||
throw std::runtime_error("toml::result: bad unwrap: " +
|
||||
format_error(this->as_err()));
|
||||
}
|
||||
return this->succ.value;
|
||||
}
|
||||
value_type const& unwrap() const&
|
||||
{
|
||||
if(is_err())
|
||||
{
|
||||
throw std::runtime_error("toml::result: bad unwrap: " +
|
||||
format_error(this->as_err()));
|
||||
}
|
||||
return this->succ.value;
|
||||
}
|
||||
value_type&& unwrap() &&
|
||||
{
|
||||
if(is_err())
|
||||
{
|
||||
throw std::runtime_error("toml::result: bad unwrap: " +
|
||||
format_error(this->as_err()));
|
||||
}
|
||||
return std::move(this->succ.value);
|
||||
}
|
||||
|
||||
value_type& unwrap_or(value_type& opt) &
|
||||
{
|
||||
if(is_err()) {return opt;}
|
||||
return this->succ.value;
|
||||
}
|
||||
value_type const& unwrap_or(value_type const& opt) const&
|
||||
{
|
||||
if(is_err()) {return opt;}
|
||||
return this->succ.value;
|
||||
}
|
||||
value_type unwrap_or(value_type opt) &&
|
||||
{
|
||||
if(is_err()) {return opt;}
|
||||
return this->succ.value;
|
||||
}
|
||||
|
||||
error_type& unwrap_err() &
|
||||
{
|
||||
if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");}
|
||||
return this->fail.value;
|
||||
}
|
||||
error_type const& unwrap_err() const&
|
||||
{
|
||||
if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");}
|
||||
return this->fail.value;
|
||||
}
|
||||
error_type&& unwrap_err() &&
|
||||
{
|
||||
if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");}
|
||||
return std::move(this->fail.value);
|
||||
}
|
||||
|
||||
value_type& as_ok() & noexcept {return this->succ.value;}
|
||||
value_type const& as_ok() const& noexcept {return this->succ.value;}
|
||||
value_type&& as_ok() && noexcept {return std::move(this->succ.value);}
|
||||
|
||||
error_type& as_err() & noexcept {return this->fail.value;}
|
||||
error_type const& as_err() const& noexcept {return this->fail.value;}
|
||||
error_type&& as_err() && noexcept {return std::move(this->fail.value);}
|
||||
|
||||
|
||||
// prerequisities
|
||||
// F: T -> U
|
||||
// retval: result<U, E>
|
||||
template<typename F>
|
||||
result<detail::return_type_of_t<F, value_type&>, error_type>
|
||||
map(F&& f) &
|
||||
{
|
||||
if(this->is_ok()){return ok(f(this->as_ok()));}
|
||||
return err(this->as_err());
|
||||
}
|
||||
template<typename F>
|
||||
result<detail::return_type_of_t<F, value_type const&>, error_type>
|
||||
map(F&& f) const&
|
||||
{
|
||||
if(this->is_ok()){return ok(f(this->as_ok()));}
|
||||
return err(this->as_err());
|
||||
}
|
||||
template<typename F>
|
||||
result<detail::return_type_of_t<F, value_type &&>, error_type>
|
||||
map(F&& f) &&
|
||||
{
|
||||
if(this->is_ok()){return ok(f(std::move(this->as_ok())));}
|
||||
return err(std::move(this->as_err()));
|
||||
}
|
||||
|
||||
// prerequisities
|
||||
// F: E -> F
|
||||
// retval: result<T, F>
|
||||
template<typename F>
|
||||
result<value_type, detail::return_type_of_t<F, error_type&>>
|
||||
map_err(F&& f) &
|
||||
{
|
||||
if(this->is_err()){return err(f(this->as_err()));}
|
||||
return ok(this->as_ok());
|
||||
}
|
||||
template<typename F>
|
||||
result<value_type, detail::return_type_of_t<F, error_type const&>>
|
||||
map_err(F&& f) const&
|
||||
{
|
||||
if(this->is_err()){return err(f(this->as_err()));}
|
||||
return ok(this->as_ok());
|
||||
}
|
||||
template<typename F>
|
||||
result<value_type, detail::return_type_of_t<F, error_type&&>>
|
||||
map_err(F&& f) &&
|
||||
{
|
||||
if(this->is_err()){return err(f(std::move(this->as_err())));}
|
||||
return ok(std::move(this->as_ok()));
|
||||
}
|
||||
|
||||
// prerequisities
|
||||
// F: T -> U
|
||||
// retval: U
|
||||
template<typename F, typename U>
|
||||
detail::return_type_of_t<F, value_type&>
|
||||
map_or_else(F&& f, U&& opt) &
|
||||
{
|
||||
if(this->is_err()){return std::forward<U>(opt);}
|
||||
return f(this->as_ok());
|
||||
}
|
||||
template<typename F, typename U>
|
||||
detail::return_type_of_t<F, value_type const&>
|
||||
map_or_else(F&& f, U&& opt) const&
|
||||
{
|
||||
if(this->is_err()){return std::forward<U>(opt);}
|
||||
return f(this->as_ok());
|
||||
}
|
||||
template<typename F, typename U>
|
||||
detail::return_type_of_t<F, value_type&&>
|
||||
map_or_else(F&& f, U&& opt) &&
|
||||
{
|
||||
if(this->is_err()){return std::forward<U>(opt);}
|
||||
return f(std::move(this->as_ok()));
|
||||
}
|
||||
|
||||
// prerequisities
|
||||
// F: E -> U
|
||||
// retval: U
|
||||
template<typename F, typename U>
|
||||
detail::return_type_of_t<F, error_type&>
|
||||
map_err_or_else(F&& f, U&& opt) &
|
||||
{
|
||||
if(this->is_ok()){return std::forward<U>(opt);}
|
||||
return f(this->as_err());
|
||||
}
|
||||
template<typename F, typename U>
|
||||
detail::return_type_of_t<F, error_type const&>
|
||||
map_err_or_else(F&& f, U&& opt) const&
|
||||
{
|
||||
if(this->is_ok()){return std::forward<U>(opt);}
|
||||
return f(this->as_err());
|
||||
}
|
||||
template<typename F, typename U>
|
||||
detail::return_type_of_t<F, error_type&&>
|
||||
map_err_or_else(F&& f, U&& opt) &&
|
||||
{
|
||||
if(this->is_ok()){return std::forward<U>(opt);}
|
||||
return f(std::move(this->as_err()));
|
||||
}
|
||||
|
||||
// prerequisities:
|
||||
// F: func T -> U
|
||||
// toml::err(error_type) should be convertible to U.
|
||||
// normally, type U is another result<S, F> and E is convertible to F
|
||||
template<typename F>
|
||||
detail::return_type_of_t<F, value_type&>
|
||||
and_then(F&& f) &
|
||||
{
|
||||
if(this->is_ok()){return f(this->as_ok());}
|
||||
return err(this->as_err());
|
||||
}
|
||||
template<typename F>
|
||||
detail::return_type_of_t<F, value_type const&>
|
||||
and_then(F&& f) const&
|
||||
{
|
||||
if(this->is_ok()){return f(this->as_ok());}
|
||||
return err(this->as_err());
|
||||
}
|
||||
template<typename F>
|
||||
detail::return_type_of_t<F, value_type&&>
|
||||
and_then(F&& f) &&
|
||||
{
|
||||
if(this->is_ok()){return f(std::move(this->as_ok()));}
|
||||
return err(std::move(this->as_err()));
|
||||
}
|
||||
|
||||
// prerequisities:
|
||||
// F: func E -> U
|
||||
// toml::ok(value_type) should be convertible to U.
|
||||
// normally, type U is another result<S, F> and T is convertible to S
|
||||
template<typename F>
|
||||
detail::return_type_of_t<F, error_type&>
|
||||
or_else(F&& f) &
|
||||
{
|
||||
if(this->is_err()){return f(this->as_err());}
|
||||
return ok(this->as_ok());
|
||||
}
|
||||
template<typename F>
|
||||
detail::return_type_of_t<F, error_type const&>
|
||||
or_else(F&& f) const&
|
||||
{
|
||||
if(this->is_err()){return f(this->as_err());}
|
||||
return ok(this->as_ok());
|
||||
}
|
||||
template<typename F>
|
||||
detail::return_type_of_t<F, error_type&&>
|
||||
or_else(F&& f) &&
|
||||
{
|
||||
if(this->is_err()){return f(std::move(this->as_err()));}
|
||||
return ok(std::move(this->as_ok()));
|
||||
}
|
||||
|
||||
// if *this is error, returns *this. otherwise, returns other.
|
||||
result and_other(const result& other) const&
|
||||
{
|
||||
return this->is_err() ? *this : other;
|
||||
}
|
||||
result and_other(result&& other) &&
|
||||
{
|
||||
return this->is_err() ? std::move(*this) : std::move(other);
|
||||
}
|
||||
|
||||
// if *this is okay, returns *this. otherwise, returns other.
|
||||
result or_other(const result& other) const&
|
||||
{
|
||||
return this->is_ok() ? *this : other;
|
||||
}
|
||||
result or_other(result&& other) &&
|
||||
{
|
||||
return this->is_ok() ? std::move(*this) : std::move(other);
|
||||
}
|
||||
|
||||
void swap(result<T, E>& other)
|
||||
{
|
||||
result<T, E> tmp(std::move(*this));
|
||||
*this = std::move(other);
|
||||
other = std::move(tmp);
|
||||
return ;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
static std::string format_error(std::exception const& excpt)
|
||||
{
|
||||
return std::string(excpt.what());
|
||||
}
|
||||
template<typename U, typename std::enable_if<!std::is_base_of<
|
||||
std::exception, U>::value, std::nullptr_t>::type = nullptr>
|
||||
static std::string format_error(U const& others)
|
||||
{
|
||||
std::ostringstream oss; oss << others;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
void cleanup() noexcept
|
||||
{
|
||||
if(this->is_ok_) {this->succ.~success_type();}
|
||||
else {this->fail.~failure_type();}
|
||||
return;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
bool is_ok_;
|
||||
union
|
||||
{
|
||||
success_type succ;
|
||||
failure_type fail;
|
||||
};
|
||||
};
|
||||
|
||||
template<typename T, typename E>
|
||||
void swap(result<T, E>& lhs, result<T, E>& rhs)
|
||||
{
|
||||
lhs.swap(rhs);
|
||||
return;
|
||||
}
|
||||
|
||||
// this might be confusing because it eagerly evaluated, while in the other
|
||||
// cases operator && and || are short-circuited.
|
||||
//
|
||||
// template<typename T, typename E>
|
||||
// inline result<T, E>
|
||||
// operator&&(const result<T, E>& lhs, const result<T, E>& rhs) noexcept
|
||||
// {
|
||||
// return lhs.is_ok() ? rhs : lhs;
|
||||
// }
|
||||
//
|
||||
// template<typename T, typename E>
|
||||
// inline result<T, E>
|
||||
// operator||(const result<T, E>& lhs, const result<T, E>& rhs) noexcept
|
||||
// {
|
||||
// return lhs.is_ok() ? lhs : rhs;
|
||||
// }
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// re-use result<T, E> as a optional<T> with none_t
|
||||
|
||||
namespace detail
|
||||
{
|
||||
struct none_t {};
|
||||
inline bool operator==(const none_t&, const none_t&) noexcept {return true;}
|
||||
inline bool operator!=(const none_t&, const none_t&) noexcept {return false;}
|
||||
inline bool operator< (const none_t&, const none_t&) noexcept {return false;}
|
||||
inline bool operator<=(const none_t&, const none_t&) noexcept {return true;}
|
||||
inline bool operator> (const none_t&, const none_t&) noexcept {return false;}
|
||||
inline bool operator>=(const none_t&, const none_t&) noexcept {return true;}
|
||||
template<typename charT, typename traitsT>
|
||||
std::basic_ostream<charT, traitsT>&
|
||||
operator<<(std::basic_ostream<charT, traitsT>& os, const none_t&)
|
||||
{
|
||||
os << "none";
|
||||
return os;
|
||||
}
|
||||
inline failure<none_t> none() noexcept {return failure<none_t>{none_t{}};}
|
||||
} // detail
|
||||
} // toml11
|
||||
#endif// TOML11_RESULT_H
|
@ -0,0 +1,788 @@
|
||||
// Copyright Toru Niina 2019.
|
||||
// Distributed under the MIT License.
|
||||
#ifndef TOML11_SERIALIZER_HPP
|
||||
#define TOML11_SERIALIZER_HPP
|
||||
#include <cstdio>
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "lexer.hpp"
|
||||
#include "value.hpp"
|
||||
|
||||
namespace toml
|
||||
{
|
||||
|
||||
// This function serialize a key. It checks a string is a bare key and
|
||||
// escapes special characters if the string is not compatible to a bare key.
|
||||
// ```cpp
|
||||
// std::string k("non.bare.key"); // the key itself includes `.`s.
|
||||
// std::string formatted = toml::format_key(k);
|
||||
// assert(formatted == "\"non.bare.key\"");
|
||||
// ```
|
||||
//
|
||||
// This function is exposed to make it easy to write a user-defined serializer.
|
||||
// Since toml restricts characters available in a bare key, generally a string
|
||||
// should be escaped. But checking whether a string needs to be surrounded by
|
||||
// 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)
|
||||
{
|
||||
// check the key can be a bare (unquoted) key
|
||||
detail::location loc(key, std::vector<char>(key.begin(), key.end()));
|
||||
detail::lex_unquoted_key::invoke(loc);
|
||||
if(loc.iter() == loc.end())
|
||||
{
|
||||
return key; // 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)
|
||||
{
|
||||
switch(c)
|
||||
{
|
||||
case '\\': {serialized += "\\\\"; break;}
|
||||
case '\"': {serialized += "\\\""; break;}
|
||||
case '\b': {serialized += "\\b"; break;}
|
||||
case '\t': {serialized += "\\t"; break;}
|
||||
case '\f': {serialized += "\\f"; break;}
|
||||
case '\n': {serialized += "\\n"; break;}
|
||||
case '\r': {serialized += "\\r"; break;}
|
||||
default : {serialized += c; break;}
|
||||
}
|
||||
}
|
||||
serialized += "\"";
|
||||
return serialized;
|
||||
}
|
||||
|
||||
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;}
|
||||
|
||||
for(const auto& ky : keys)
|
||||
{
|
||||
serialized += format_key(ky);
|
||||
serialized += charT('.');
|
||||
}
|
||||
serialized.pop_back(); // remove the last dot '.'
|
||||
return serialized;
|
||||
}
|
||||
|
||||
template<typename Value>
|
||||
struct serializer
|
||||
{
|
||||
static_assert(detail::is_basic_value<Value>::value,
|
||||
"toml::serializer is for toml::value and its variants, "
|
||||
"toml::basic_value<...>.");
|
||||
|
||||
using value_type = Value;
|
||||
using key_type = typename value_type::key_type ;
|
||||
using comment_type = typename value_type::comment_type ;
|
||||
using boolean_type = typename value_type::boolean_type ;
|
||||
using integer_type = typename value_type::integer_type ;
|
||||
using floating_type = typename value_type::floating_type ;
|
||||
using string_type = typename value_type::string_type ;
|
||||
using local_time_type = typename value_type::local_time_type ;
|
||||
using local_date_type = typename value_type::local_date_type ;
|
||||
using local_datetime_type = typename value_type::local_datetime_type ;
|
||||
using offset_datetime_type = typename value_type::offset_datetime_type;
|
||||
using array_type = typename value_type::array_type ;
|
||||
using table_type = typename value_type::table_type ;
|
||||
|
||||
serializer(const std::size_t w = 80u,
|
||||
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 = {})
|
||||
: can_be_inlined_(can_be_inlined), no_comment_(no_comment),
|
||||
float_prec_(float_prec), width_(w), keys_(std::move(ks))
|
||||
{}
|
||||
~serializer() = default;
|
||||
|
||||
std::string operator()(const boolean_type& b) const
|
||||
{
|
||||
return b ? "true" : "false";
|
||||
}
|
||||
std::string operator()(const integer_type i) const
|
||||
{
|
||||
return std::to_string(i);
|
||||
}
|
||||
std::string operator()(const floating_type f) const
|
||||
{
|
||||
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);
|
||||
|
||||
std::string token(buf.begin(), std::prev(buf.end()));
|
||||
if(token.back() == '.') // 1. => 1.0
|
||||
{
|
||||
token += '0';
|
||||
}
|
||||
|
||||
const auto e = std::find_if(
|
||||
token.cbegin(), token.cend(), [](const char c) noexcept -> bool {
|
||||
return c == 'e' || c == 'E';
|
||||
});
|
||||
const auto has_exponent = (token.cend() != e);
|
||||
const auto has_fraction = (token.cend() != std::find(
|
||||
token.cbegin(), token.cend(), '.'));
|
||||
|
||||
if(!has_exponent && !has_fraction)
|
||||
{
|
||||
// the resulting value does not have any float specific part!
|
||||
token += ".0";
|
||||
}
|
||||
return token;
|
||||
}
|
||||
std::string operator()(const string_type& s) const
|
||||
{
|
||||
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 linefeed or double-quote is contained,
|
||||
// make it multiline basic string.
|
||||
const auto escaped = this->escape_ml_basic_string(s.str);
|
||||
std::string open("\"\"\"");
|
||||
std::string close("\"\"\"");
|
||||
if(escaped.find('\n') != std::string::npos ||
|
||||
this->width_ < escaped.size() + 6)
|
||||
{
|
||||
// if the string body contains newline or is enough long,
|
||||
// add newlines after and before delimiters.
|
||||
open += "\n";
|
||||
close = std::string("\\\n") + close;
|
||||
}
|
||||
return open + escaped + close;
|
||||
}
|
||||
|
||||
// no linefeed. try to make it oneline-string.
|
||||
std::string oneline = this->escape_basic_string(s.str);
|
||||
if(oneline.size() + 2 < width_ || width_ < 2)
|
||||
{
|
||||
const std::string quote("\"");
|
||||
return quote + oneline + quote;
|
||||
}
|
||||
|
||||
// the line is too long compared to the specified width.
|
||||
// split it into multiple lines.
|
||||
std::string token("\"\"\"\n");
|
||||
while(!oneline.empty())
|
||||
{
|
||||
if(oneline.size() < width_)
|
||||
{
|
||||
token += oneline;
|
||||
oneline.clear();
|
||||
}
|
||||
else if(oneline.at(width_-2) == '\\')
|
||||
{
|
||||
token += oneline.substr(0, width_-2);
|
||||
token += "\\\n";
|
||||
oneline.erase(0, width_-2);
|
||||
}
|
||||
else
|
||||
{
|
||||
token += oneline.substr(0, width_-1);
|
||||
token += "\\\n";
|
||||
oneline.erase(0, width_-1);
|
||||
}
|
||||
}
|
||||
return token + std::string("\\\n\"\"\"");
|
||||
}
|
||||
else // the string `s` is literal-string.
|
||||
{
|
||||
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() ||
|
||||
std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() )
|
||||
{
|
||||
std::string open("'''");
|
||||
if(this->width_ + 6 < s.str.size())
|
||||
{
|
||||
open += '\n'; // the first newline is ignored by TOML spec
|
||||
}
|
||||
const std::string close("'''");
|
||||
return open + s.str + close;
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string quote("'");
|
||||
return quote + s.str + quote;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string operator()(const local_date_type& d) const
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << d;
|
||||
return oss.str();
|
||||
}
|
||||
std::string operator()(const local_time_type& t) const
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << t;
|
||||
return oss.str();
|
||||
}
|
||||
std::string operator()(const local_datetime_type& dt) const
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << dt;
|
||||
return oss.str();
|
||||
}
|
||||
std::string operator()(const offset_datetime_type& odt) const
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << odt;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
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("[]");
|
||||
}
|
||||
|
||||
// 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))
|
||||
{
|
||||
const auto inl = this->make_inline_array(v);
|
||||
if(inl.size() < this->width_ &&
|
||||
std::find(inl.cbegin(), inl.cend(), '\n') == inl.cend())
|
||||
{
|
||||
return inl;
|
||||
}
|
||||
}
|
||||
|
||||
// if the length exceeds this->width_, print multiline array.
|
||||
// key = [
|
||||
// # ...
|
||||
// 42,
|
||||
// ...
|
||||
// ]
|
||||
std::string token;
|
||||
std::string current_line;
|
||||
token += "[\n";
|
||||
for(const auto& item : v)
|
||||
{
|
||||
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.
|
||||
// ```toml
|
||||
// array = [
|
||||
// # comment for what?
|
||||
// 1, 2, 3, 4, 5
|
||||
// ]
|
||||
// ```
|
||||
if(!current_line.empty())
|
||||
{
|
||||
if(current_line.back() != '\n')
|
||||
{
|
||||
current_line += '\n';
|
||||
}
|
||||
token += current_line;
|
||||
current_line.clear();
|
||||
}
|
||||
for(const auto& c : item.comments())
|
||||
{
|
||||
token += '#';
|
||||
token += c;
|
||||
token += '\n';
|
||||
}
|
||||
token += toml::visit(*this, item);
|
||||
if(token.back() == '\n') {token.pop_back();}
|
||||
token += ",\n";
|
||||
continue;
|
||||
}
|
||||
std::string next_elem;
|
||||
next_elem += toml::visit(*this, item);
|
||||
|
||||
// comma before newline.
|
||||
if(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_)
|
||||
{
|
||||
current_line += next_elem;
|
||||
current_line += ',';
|
||||
}
|
||||
else if(current_line.empty())
|
||||
{
|
||||
// if current line was empty, force put the next_elem because
|
||||
// next_elem is not splittable
|
||||
token += next_elem;
|
||||
token += ",\n";
|
||||
// current_line is kept empty
|
||||
}
|
||||
else // reset current_line
|
||||
{
|
||||
assert(current_line.back() == ',');
|
||||
token += current_line;
|
||||
token += '\n';
|
||||
current_line = next_elem;
|
||||
current_line += ',';
|
||||
}
|
||||
}
|
||||
if(!current_line.empty())
|
||||
{
|
||||
if(current_line.back() != '\n') {current_line += '\n';}
|
||||
token += current_line;
|
||||
}
|
||||
token += "]\n";
|
||||
return token;
|
||||
}
|
||||
|
||||
// templatize for any table-like container
|
||||
std::string operator()(const table_type& v) const
|
||||
{
|
||||
// if an element has a comment, then it can't be inlined.
|
||||
// table = {# how can we write a comment for this? key = "value"}
|
||||
if(this->can_be_inlined_ && !(this->has_comment_inside(v)))
|
||||
{
|
||||
std::string token;
|
||||
if(!this->keys_.empty())
|
||||
{
|
||||
token += format_key(this->keys_.back());
|
||||
token += " = ";
|
||||
}
|
||||
token += this->make_inline_table(v);
|
||||
if(token.size() < this->width_ &&
|
||||
token.end() == std::find(token.begin(), token.end(), '\n'))
|
||||
{
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
||||
std::string token;
|
||||
if(!keys_.empty())
|
||||
{
|
||||
token += '[';
|
||||
token += format_keys(keys_);
|
||||
token += "]\n";
|
||||
}
|
||||
token += this->make_multiline_table(v);
|
||||
return token;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
std::string escape_basic_string(const std::string& s) const
|
||||
{
|
||||
//XXX assuming `s` is a valid utf-8 sequence.
|
||||
std::string retval;
|
||||
for(const char c : s)
|
||||
{
|
||||
switch(c)
|
||||
{
|
||||
case '\\': {retval += "\\\\"; break;}
|
||||
case '\"': {retval += "\\\""; break;}
|
||||
case '\b': {retval += "\\b"; break;}
|
||||
case '\t': {retval += "\\t"; break;}
|
||||
case '\f': {retval += "\\f"; break;}
|
||||
case '\n': {retval += "\\n"; break;}
|
||||
case '\r': {retval += "\\r"; break;}
|
||||
default : {retval += c; break;}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
std::string escape_ml_basic_string(const std::string& s) const
|
||||
{
|
||||
std::string retval;
|
||||
for(auto i=s.cbegin(), e=s.cend(); i!=e; ++i)
|
||||
{
|
||||
switch(*i)
|
||||
{
|
||||
case '\\': {retval += "\\\\"; break;}
|
||||
// One or two consecutive "s are allowed.
|
||||
// Later we will check there are no three consecutive "s.
|
||||
// case '\"': {retval += "\\\""; break;}
|
||||
case '\b': {retval += "\\b"; break;}
|
||||
case '\t': {retval += "\\t"; break;}
|
||||
case '\f': {retval += "\\f"; break;}
|
||||
case '\n': {retval += "\n"; break;}
|
||||
case '\r':
|
||||
{
|
||||
if(std::next(i) != e && *std::next(i) == '\n')
|
||||
{
|
||||
retval += "\r\n";
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
retval += "\\r";
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {retval += *i; break;}
|
||||
}
|
||||
}
|
||||
// Only 1 or 2 consecutive `"`s are allowed in multiline basic string.
|
||||
// 3 consecutive `"`s are considered as a closing delimiter.
|
||||
// We need to check if there are 3 or more consecutive `"`s and insert
|
||||
// backslash to break them down into several short `"`s like the `str6`
|
||||
// in the following example.
|
||||
// ```toml
|
||||
// str4 = """Here are two quotation marks: "". Simple enough."""
|
||||
// # str5 = """Here are three quotation marks: """.""" # INVALID
|
||||
// str5 = """Here are three quotation marks: ""\"."""
|
||||
// str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\"."""
|
||||
// ```
|
||||
auto found_3_quotes = retval.find("\"\"\"");
|
||||
while(found_3_quotes != std::string::npos)
|
||||
{
|
||||
retval.replace(found_3_quotes, 3, "\"\"\\\"");
|
||||
found_3_quotes = retval.find("\"\"\"");
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
// if an element of a table or an array has a comment, it cannot be inlined.
|
||||
bool has_comment_inside(const array_type& a) const noexcept
|
||||
{
|
||||
// if no_comment is set, comments would not be written.
|
||||
if(this->no_comment_) {return false;}
|
||||
|
||||
for(const auto& v : a)
|
||||
{
|
||||
if(!v.comments().empty()) {return true;}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool has_comment_inside(const table_type& t) const noexcept
|
||||
{
|
||||
// if no_comment is set, comments would not be written.
|
||||
if(this->no_comment_) {return false;}
|
||||
|
||||
for(const auto& kv : t)
|
||||
{
|
||||
if(!kv.second.comments().empty()) {return true;}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string make_inline_array(const array_type& v) const
|
||||
{
|
||||
assert(!has_comment_inside(v));
|
||||
std::string token;
|
||||
token += '[';
|
||||
bool is_first = true;
|
||||
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 += ']';
|
||||
return token;
|
||||
}
|
||||
|
||||
std::string make_inline_table(const table_type& v) const
|
||||
{
|
||||
assert(!has_comment_inside(v));
|
||||
assert(this->can_be_inlined_);
|
||||
std::string token;
|
||||
token += '{';
|
||||
bool is_first = true;
|
||||
for(const auto& kv : v)
|
||||
{
|
||||
// in inline tables, trailing comma is not allowed (toml-lang #569).
|
||||
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 += '}';
|
||||
return token;
|
||||
}
|
||||
|
||||
std::string make_multiline_table(const table_type& v) const
|
||||
{
|
||||
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]
|
||||
for(const auto& kv : v)
|
||||
{
|
||||
if(kv.second.is_table() || is_array_of_tables(kv.second))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!kv.second.comments().empty() && !no_comment_)
|
||||
{
|
||||
for(const auto& c : kv.second.comments())
|
||||
{
|
||||
token += '#';
|
||||
token += c;
|
||||
token += '\n';
|
||||
}
|
||||
}
|
||||
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);
|
||||
if(token.back() != '\n')
|
||||
{
|
||||
token += '\n';
|
||||
}
|
||||
}
|
||||
|
||||
// normal tables / array of tables
|
||||
|
||||
// after multiline table appeared, the other tables cannot be inline
|
||||
// because the table would be assigned into the table.
|
||||
// [foo]
|
||||
// ...
|
||||
// bar = {...} # <- bar will be a member of [foo].
|
||||
bool multiline_table_printed = false;
|
||||
for(const auto& kv : v)
|
||||
{
|
||||
if(!kv.second.is_table() && !is_array_of_tables(kv.second))
|
||||
{
|
||||
continue; // other stuff are already serialized. skip them.
|
||||
}
|
||||
|
||||
std::vector<toml::key> ks(this->keys_);
|
||||
ks.push_back(kv.first);
|
||||
|
||||
auto tmp = visit(serializer(this->width_, this->float_prec_,
|
||||
!multiline_table_printed, this->no_comment_, ks),
|
||||
kv.second);
|
||||
|
||||
if((!multiline_table_printed) &&
|
||||
std::find(tmp.cbegin(), tmp.cend(), '\n') != tmp.cend())
|
||||
{
|
||||
multiline_table_printed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// still inline tables only.
|
||||
tmp += '\n';
|
||||
}
|
||||
|
||||
if(!kv.second.comments().empty() && !no_comment_)
|
||||
{
|
||||
for(const auto& c : kv.second.comments())
|
||||
{
|
||||
token += '#';
|
||||
token += c;
|
||||
token += '\n';
|
||||
}
|
||||
}
|
||||
token += tmp;
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
bool can_be_inlined_;
|
||||
bool no_comment_;
|
||||
int float_prec_;
|
||||
std::size_t width_;
|
||||
std::vector<toml::key> keys_;
|
||||
};
|
||||
|
||||
template<typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
std::string
|
||||
format(const basic_value<C, M, V>& v, std::size_t w = 80u,
|
||||
int fprec = std::numeric_limits<toml::floating>::max_digits10,
|
||||
bool no_comment = false, bool force_inline = false)
|
||||
{
|
||||
using value_type = basic_value<C, M, V>;
|
||||
// if value is a table, it is considered to be a root object.
|
||||
// the root object can't be an inline table.
|
||||
if(v.is_table())
|
||||
{
|
||||
std::ostringstream oss;
|
||||
if(!v.comments().empty())
|
||||
{
|
||||
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);
|
||||
oss << serialized;
|
||||
return oss.str();
|
||||
}
|
||||
return visit(serializer<value_type>(w, fprec, force_inline), v);
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template<typename charT, typename traits>
|
||||
int comment_index(std::basic_ostream<charT, traits>&)
|
||||
{
|
||||
static const int index = std::ios_base::xalloc();
|
||||
return index;
|
||||
}
|
||||
} // detail
|
||||
|
||||
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.
|
||||
os.iword(detail::comment_index(os)) = 1;
|
||||
return os;
|
||||
}
|
||||
|
||||
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.
|
||||
os.iword(detail::comment_index(os)) = 0;
|
||||
return os;
|
||||
}
|
||||
|
||||
template<typename charT, typename traits, typename C,
|
||||
template<typename ...> class M, template<typename ...> class V>
|
||||
std::basic_ostream<charT, traits>&
|
||||
operator<<(std::basic_ostream<charT, traits>& os, const basic_value<C, M, V>& v)
|
||||
{
|
||||
using value_type = basic_value<C, M, V>;
|
||||
|
||||
// get status of std::setw().
|
||||
const auto w = static_cast<std::size_t>(os.width());
|
||||
const int fprec = static_cast<int>(os.precision());
|
||||
os.width(0);
|
||||
|
||||
// by defualt, iword is initialized byl 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)));
|
||||
|
||||
if(!no_comment && v.is_table() && !v.comments().empty())
|
||||
{
|
||||
os << v.comments();
|
||||
os << '\n'; // to split the file comment from the first element
|
||||
}
|
||||
// the root object can't be an inline table. so pass `false`.
|
||||
const auto serialized = visit(serializer<value_type>(w, fprec, no_comment, false), v);
|
||||
os << serialized;
|
||||
|
||||
// if v is a non-table value, and has only one comment, then
|
||||
// put a comment just after a value. in the following way.
|
||||
//
|
||||
// ```toml
|
||||
// key = "value" # comment.
|
||||
// ```
|
||||
//
|
||||
// Since the top-level toml object is a table, one who want to put a
|
||||
// non-table toml value must use this in a following way.
|
||||
//
|
||||
// ```cpp
|
||||
// toml::value v;
|
||||
// std::cout << "user-defined-key = " << v << std::endl;
|
||||
// ```
|
||||
//
|
||||
// In this case, it is impossible to put comments before key-value pair.
|
||||
// The only way to preserve comments is to put all of them after a value.
|
||||
if(!no_comment && !v.is_table() && !v.comments().empty())
|
||||
{
|
||||
os << " #";
|
||||
for(const auto& c : v.comments()) {os << c;}
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
} // toml
|
||||
#endif// TOML11_SERIALIZER_HPP
|
@ -0,0 +1,232 @@
|
||||
// Copyright Toru Niina 2019.
|
||||
// Distributed under the MIT License.
|
||||
#ifndef TOML11_SOURCE_LOCATION_HPP
|
||||
#define TOML11_SOURCE_LOCATION_HPP
|
||||
#include <cstdint>
|
||||
|
||||
#include "region.hpp"
|
||||
|
||||
namespace toml
|
||||
{
|
||||
|
||||
// A struct to contain location in a toml file.
|
||||
// The interface imitates std::experimental::source_location,
|
||||
// but not completely the same.
|
||||
//
|
||||
// It would be constructed by toml::value. It can be used to generate
|
||||
// user-defined error messages.
|
||||
//
|
||||
// - std::uint_least32_t line() const noexcept
|
||||
// - returns the line number where the region is on.
|
||||
// - std::uint_least32_t column() const noexcept
|
||||
// - returns the column number where the region starts.
|
||||
// - std::uint_least32_t region() const noexcept
|
||||
// - returns the size of the region.
|
||||
//
|
||||
// +-- line() +-- region of interest (region() == 9)
|
||||
// v .---+---.
|
||||
// 12 | value = "foo bar"
|
||||
// ^
|
||||
// +-- column()
|
||||
//
|
||||
// - std::string const& file_name() const noexcept;
|
||||
// - name of the file.
|
||||
// - std::string const& line_str() const noexcept;
|
||||
// - the whole line that contains the region of interest.
|
||||
//
|
||||
struct source_location
|
||||
{
|
||||
public:
|
||||
|
||||
source_location()
|
||||
: line_num_(1), column_num_(1), region_size_(1),
|
||||
file_name_("unknown file"), line_str_("")
|
||||
{}
|
||||
|
||||
explicit source_location(const detail::region_base* reg)
|
||||
: line_num_(1), column_num_(1), region_size_(1),
|
||||
file_name_("unknown file"), line_str_("")
|
||||
{
|
||||
if(reg)
|
||||
{
|
||||
if(reg->line_num() != detail::region_base().line_num())
|
||||
{
|
||||
line_num_ = static_cast<std::uint_least32_t>(
|
||||
std::stoul(reg->line_num()));
|
||||
}
|
||||
column_num_ = static_cast<std::uint_least32_t>(reg->before() + 1);
|
||||
region_size_ = static_cast<std::uint_least32_t>(reg->size());
|
||||
file_name_ = reg->name();
|
||||
line_str_ = reg->line();
|
||||
}
|
||||
}
|
||||
|
||||
explicit source_location(const detail::region& reg)
|
||||
: line_num_(static_cast<std::uint_least32_t>(std::stoul(reg.line_num()))),
|
||||
column_num_(static_cast<std::uint_least32_t>(reg.before() + 1)),
|
||||
region_size_(static_cast<std::uint_least32_t>(reg.size())),
|
||||
file_name_(reg.name()),
|
||||
line_str_ (reg.line())
|
||||
{}
|
||||
explicit source_location(const detail::location& loc)
|
||||
: line_num_(static_cast<std::uint_least32_t>(std::stoul(loc.line_num()))),
|
||||
column_num_(static_cast<std::uint_least32_t>(loc.before() + 1)),
|
||||
region_size_(static_cast<std::uint_least32_t>(loc.size())),
|
||||
file_name_(loc.name()),
|
||||
line_str_ (loc.line())
|
||||
{}
|
||||
|
||||
~source_location() = default;
|
||||
source_location(source_location const&) = default;
|
||||
source_location(source_location &&) = default;
|
||||
source_location& operator=(source_location const&) = default;
|
||||
source_location& operator=(source_location &&) = default;
|
||||
|
||||
std::uint_least32_t line() const noexcept {return line_num_;}
|
||||
std::uint_least32_t column() const noexcept {return column_num_;}
|
||||
std::uint_least32_t region() const noexcept {return region_size_;}
|
||||
|
||||
std::string const& file_name() const noexcept {return file_name_;}
|
||||
std::string const& line_str() const noexcept {return line_str_;}
|
||||
|
||||
private:
|
||||
|
||||
std::uint_least32_t line_num_;
|
||||
std::uint_least32_t column_num_;
|
||||
std::uint_least32_t region_size_;
|
||||
std::string file_name_;
|
||||
std::string line_str_;
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
// internal error message generation.
|
||||
inline std::string format_underline(const std::string& message,
|
||||
const std::vector<std::pair<source_location, std::string>>& loc_com,
|
||||
const std::vector<std::string>& helps = {},
|
||||
const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED)
|
||||
{
|
||||
std::size_t line_num_width = 0;
|
||||
for(const auto& lc : loc_com)
|
||||
{
|
||||
std::uint_least32_t line = lc.first.line();
|
||||
std::size_t digit = 0;
|
||||
while(line != 0)
|
||||
{
|
||||
line /= 10;
|
||||
digit += 1;
|
||||
}
|
||||
line_num_width = (std::max)(line_num_width, digit);
|
||||
}
|
||||
// 1 is the minimum width
|
||||
line_num_width = std::max<std::size_t>(line_num_width, 1);
|
||||
|
||||
std::ostringstream retval;
|
||||
|
||||
if(colorize)
|
||||
{
|
||||
retval << color::colorize; // turn on ANSI color
|
||||
}
|
||||
|
||||
// XXX
|
||||
// Here, before `colorize` support, it does not output `[error]` prefix
|
||||
// automatically. So some user may output it manually and this change may
|
||||
// duplicate the prefix. To avoid it, check the first 7 characters and
|
||||
// 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
|
||||
<< color::bold << message.substr(7) << color::reset << '\n';
|
||||
}
|
||||
else
|
||||
{
|
||||
retval << color::bold << color::red << "[error] " << color::reset
|
||||
<< color::bold << message << color::reset << '\n';
|
||||
}
|
||||
|
||||
const auto format_one_location = [line_num_width]
|
||||
(std::ostringstream& oss,
|
||||
const source_location& loc, const std::string& comment) -> void
|
||||
{
|
||||
oss << ' ' << color::bold << color::blue
|
||||
<< std::setw(static_cast<int>(line_num_width))
|
||||
<< std::right << loc.line() << " | " << color::reset
|
||||
<< loc.line_str() << '\n';
|
||||
|
||||
oss << make_string(line_num_width + 1, ' ')
|
||||
<< color::bold << color::blue << " | " << color::reset
|
||||
<< make_string(loc.column()-1 /*1-origin*/, ' ');
|
||||
|
||||
if(loc.region() == 1)
|
||||
{
|
||||
// invalid
|
||||
// ^------
|
||||
oss << color::bold << color::red << "^---" << color::reset;
|
||||
}
|
||||
else
|
||||
{
|
||||
// invalid
|
||||
// ~~~~~~~
|
||||
const auto underline_len = (std::min)(
|
||||
static_cast<std::size_t>(loc.region()), loc.line_str().size());
|
||||
oss << color::bold << color::red
|
||||
<< make_string(underline_len, '~') << color::reset;
|
||||
}
|
||||
oss << ' ';
|
||||
oss << comment;
|
||||
return;
|
||||
};
|
||||
|
||||
assert(!loc_com.empty());
|
||||
|
||||
// --> example.toml
|
||||
// |
|
||||
retval << color::bold << color::blue << " --> " << color::reset
|
||||
<< loc_com.front().first.file_name() << '\n';
|
||||
retval << make_string(line_num_width + 1, ' ')
|
||||
<< color::bold << color::blue << " |\n" << color::reset;
|
||||
// 1 | key value
|
||||
// | ^--- missing =
|
||||
format_one_location(retval, loc_com.front().first, loc_com.front().second);
|
||||
|
||||
// process the rest of the locations
|
||||
for(std::size_t i=1; i<loc_com.size(); ++i)
|
||||
{
|
||||
const auto& prev = loc_com.at(i-1);
|
||||
const auto& curr = loc_com.at(i);
|
||||
|
||||
retval << '\n';
|
||||
// if the filenames are the same, print "..."
|
||||
if(prev.first.file_name() == curr.first.file_name())
|
||||
{
|
||||
retval << color::bold << color::blue << " ...\n" << color::reset;
|
||||
}
|
||||
else // if filename differs, print " --> filename.toml" again
|
||||
{
|
||||
retval << color::bold << color::blue << " --> " << color::reset
|
||||
<< curr.first.file_name() << '\n';
|
||||
retval << make_string(line_num_width + 1, ' ')
|
||||
<< color::bold << color::blue << " |\n" << color::reset;
|
||||
}
|
||||
|
||||
format_one_location(retval, curr.first, curr.second);
|
||||
}
|
||||
|
||||
if(!helps.empty())
|
||||
{
|
||||
retval << '\n';
|
||||
retval << make_string(line_num_width + 1, ' ');
|
||||
retval << color::bold << color::blue << " |" << color::reset;
|
||||
for(const auto& help : helps)
|
||||
{
|
||||
retval << color::bold << "\nHint: " << color::reset;
|
||||
retval << help;
|
||||
}
|
||||
}
|
||||
return retval.str();
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // toml
|
||||
#endif// TOML11_SOURCE_LOCATION_HPP
|
@ -0,0 +1,43 @@
|
||||
// Copyright Toru Niina 2017.
|
||||
// Distributed under the MIT License.
|
||||
#ifndef TOML11_STORAGE_HPP
|
||||
#define TOML11_STORAGE_HPP
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace toml
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
// this contains pointer and deep-copy the content if copied.
|
||||
// to avoid recursive pointer.
|
||||
template<typename T>
|
||||
struct storage
|
||||
{
|
||||
using value_type = T;
|
||||
|
||||
explicit storage(value_type const& v): ptr(toml::make_unique<T>(v)) {}
|
||||
explicit storage(value_type&& v): ptr(toml::make_unique<T>(std::move(v))) {}
|
||||
~storage() = default;
|
||||
storage(const storage& rhs): ptr(toml::make_unique<T>(*rhs.ptr)) {}
|
||||
storage& operator=(const storage& rhs)
|
||||
{
|
||||
this->ptr = toml::make_unique<T>(*rhs.ptr);
|
||||
return *this;
|
||||
}
|
||||
storage(storage&&) = default;
|
||||
storage& operator=(storage&&) = default;
|
||||
|
||||
bool is_ok() const noexcept {return static_cast<bool>(ptr);}
|
||||
|
||||
value_type& value() & noexcept {return *ptr;}
|
||||
value_type const& value() const& noexcept {return *ptr;}
|
||||
value_type&& value() && noexcept {return std::move(*ptr);}
|
||||
|
||||
private:
|
||||
std::unique_ptr<value_type> ptr;
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // toml
|
||||
#endif// TOML11_STORAGE_HPP
|
@ -0,0 +1,224 @@
|
||||
// Copyright Toru Niina 2017.
|
||||
// Distributed under the MIT License.
|
||||
#ifndef TOML11_STRING_HPP
|
||||
#define TOML11_STRING_HPP
|
||||
#include <cstdint>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
#if __has_include(<string_view>)
|
||||
#include <string_view>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace toml
|
||||
{
|
||||
|
||||
enum class string_t : std::uint8_t
|
||||
{
|
||||
basic = 0,
|
||||
literal = 1,
|
||||
};
|
||||
|
||||
struct string
|
||||
{
|
||||
string() = default;
|
||||
~string() = default;
|
||||
string(const string& s) = default;
|
||||
string(string&& s) = default;
|
||||
string& operator=(const string& s) = default;
|
||||
string& operator=(string&& s) = default;
|
||||
|
||||
string(const std::string& s): kind(string_t::basic), str(s){}
|
||||
string(const std::string& s, string_t k): kind(k), str(s){}
|
||||
string(const char* s): kind(string_t::basic), str(s){}
|
||||
string(const char* s, string_t k): kind(k), str(s){}
|
||||
|
||||
string(std::string&& s): kind(string_t::basic), str(std::move(s)){}
|
||||
string(std::string&& s, string_t k): kind(k), str(std::move(s)){}
|
||||
|
||||
string& operator=(const std::string& s)
|
||||
{kind = string_t::basic; str = s; return *this;}
|
||||
string& operator=(std::string&& s)
|
||||
{kind = string_t::basic; str = std::move(s); return *this;}
|
||||
|
||||
operator std::string& () & noexcept {return str;}
|
||||
operator std::string const& () const& noexcept {return str;}
|
||||
operator std::string&& () && noexcept {return std::move(str);}
|
||||
|
||||
string& operator+=(const char* rhs) {str += rhs; return *this;}
|
||||
string& operator+=(const char rhs) {str += rhs; return *this;}
|
||||
string& operator+=(const std::string& rhs) {str += rhs; return *this;}
|
||||
string& operator+=(const string& rhs) {str += rhs.str; return *this;}
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
explicit string(std::string_view s): kind(string_t::basic), str(s){}
|
||||
string(std::string_view s, string_t k): kind(k), str(s){}
|
||||
|
||||
string& operator=(std::string_view s)
|
||||
{kind = string_t::basic; str = s; return *this;}
|
||||
|
||||
explicit operator std::string_view() const noexcept
|
||||
{return std::string_view(str);}
|
||||
|
||||
string& operator+=(const std::string_view& rhs) {str += rhs; return *this;}
|
||||
#endif
|
||||
|
||||
string_t kind;
|
||||
std::string str;
|
||||
};
|
||||
|
||||
inline bool operator==(const string& lhs, const string& rhs)
|
||||
{
|
||||
return lhs.kind == rhs.kind && lhs.str == rhs.str;
|
||||
}
|
||||
inline bool operator!=(const string& lhs, const string& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
inline bool operator<(const string& lhs, const string& rhs)
|
||||
{
|
||||
return (lhs.kind == rhs.kind) ? (lhs.str < rhs.str) : (lhs.kind < rhs.kind);
|
||||
}
|
||||
inline bool operator>(const string& lhs, const string& rhs)
|
||||
{
|
||||
return rhs < lhs;
|
||||
}
|
||||
inline bool operator<=(const string& lhs, const string& rhs)
|
||||
{
|
||||
return !(rhs < lhs);
|
||||
}
|
||||
inline bool operator>=(const string& lhs, const string& rhs)
|
||||
{
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
inline bool
|
||||
operator==(const string& lhs, const std::string& rhs) {return lhs.str == rhs;}
|
||||
inline bool
|
||||
operator!=(const string& lhs, const std::string& rhs) {return lhs.str != rhs;}
|
||||
inline bool
|
||||
operator< (const string& lhs, const std::string& rhs) {return lhs.str < rhs;}
|
||||
inline bool
|
||||
operator> (const string& lhs, const std::string& rhs) {return lhs.str > rhs;}
|
||||
inline bool
|
||||
operator<=(const string& lhs, const std::string& rhs) {return lhs.str <= rhs;}
|
||||
inline bool
|
||||
operator>=(const string& lhs, const std::string& rhs) {return lhs.str >= rhs;}
|
||||
|
||||
inline bool
|
||||
operator==(const std::string& lhs, const string& rhs) {return lhs == rhs.str;}
|
||||
inline bool
|
||||
operator!=(const std::string& lhs, const string& rhs) {return lhs != rhs.str;}
|
||||
inline bool
|
||||
operator< (const std::string& lhs, const string& rhs) {return lhs < rhs.str;}
|
||||
inline bool
|
||||
operator> (const std::string& lhs, const string& rhs) {return lhs > rhs.str;}
|
||||
inline bool
|
||||
operator<=(const std::string& lhs, const string& rhs) {return lhs <= rhs.str;}
|
||||
inline bool
|
||||
operator>=(const std::string& lhs, const string& rhs) {return lhs >= rhs.str;}
|
||||
|
||||
inline bool
|
||||
operator==(const string& lhs, const char* rhs) {return lhs.str == std::string(rhs);}
|
||||
inline bool
|
||||
operator!=(const string& lhs, const char* rhs) {return lhs.str != std::string(rhs);}
|
||||
inline bool
|
||||
operator< (const string& lhs, const char* rhs) {return lhs.str < std::string(rhs);}
|
||||
inline bool
|
||||
operator> (const string& lhs, const char* rhs) {return lhs.str > std::string(rhs);}
|
||||
inline bool
|
||||
operator<=(const string& lhs, const char* rhs) {return lhs.str <= std::string(rhs);}
|
||||
inline bool
|
||||
operator>=(const string& lhs, const char* rhs) {return lhs.str >= std::string(rhs);}
|
||||
|
||||
inline bool
|
||||
operator==(const char* lhs, const string& rhs) {return std::string(lhs) == rhs.str;}
|
||||
inline bool
|
||||
operator!=(const char* lhs, const string& rhs) {return std::string(lhs) != rhs.str;}
|
||||
inline bool
|
||||
operator< (const char* lhs, const string& rhs) {return std::string(lhs) < rhs.str;}
|
||||
inline bool
|
||||
operator> (const char* lhs, const string& rhs) {return std::string(lhs) > rhs.str;}
|
||||
inline bool
|
||||
operator<=(const char* lhs, const string& rhs) {return std::string(lhs) <= rhs.str;}
|
||||
inline bool
|
||||
operator>=(const char* lhs, const string& rhs) {return std::string(lhs) >= rhs.str;}
|
||||
|
||||
template<typename charT, typename traits>
|
||||
std::basic_ostream<charT, traits>&
|
||||
operator<<(std::basic_ostream<charT, traits>& os, const string& s)
|
||||
{
|
||||
if(s.kind == string_t::basic)
|
||||
{
|
||||
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend())
|
||||
{
|
||||
// it contains newline. make it multiline string.
|
||||
os << "\"\"\"\n";
|
||||
for(auto i=s.str.cbegin(), e=s.str.cend(); i!=e; ++i)
|
||||
{
|
||||
switch(*i)
|
||||
{
|
||||
case '\\': {os << "\\\\"; break;}
|
||||
case '\"': {os << "\\\""; break;}
|
||||
case '\b': {os << "\\b"; break;}
|
||||
case '\t': {os << "\\t"; break;}
|
||||
case '\f': {os << "\\f"; break;}
|
||||
case '\n': {os << '\n'; break;}
|
||||
case '\r':
|
||||
{
|
||||
// since it is a multiline string,
|
||||
// CRLF is not needed to be escaped.
|
||||
if(std::next(i) != e && *std::next(i) == '\n')
|
||||
{
|
||||
os << "\r\n";
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
os << "\\r";
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {os << *i; break;}
|
||||
}
|
||||
}
|
||||
os << "\\\n\"\"\"";
|
||||
return os;
|
||||
}
|
||||
// no newline. make it inline.
|
||||
os << "\"";
|
||||
for(const auto c : s.str)
|
||||
{
|
||||
switch(c)
|
||||
{
|
||||
case '\\': {os << "\\\\"; break;}
|
||||
case '\"': {os << "\\\""; break;}
|
||||
case '\b': {os << "\\b"; break;}
|
||||
case '\t': {os << "\\t"; break;}
|
||||
case '\f': {os << "\\f"; break;}
|
||||
case '\n': {os << "\\n"; break;}
|
||||
case '\r': {os << "\\r"; break;}
|
||||
default : {os << c; break;}
|
||||
}
|
||||
}
|
||||
os << "\"";
|
||||
return os;
|
||||
}
|
||||
// the string `s` is literal-string.
|
||||
if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() ||
|
||||
std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() )
|
||||
{
|
||||
// contains newline or single quote. make it multiline.
|
||||
os << "'''\n" << s.str << "'''";
|
||||
return os;
|
||||
}
|
||||
// normal literal string
|
||||
os << '\'' << s.str << '\'';
|
||||
return os;
|
||||
}
|
||||
|
||||
} // toml
|
||||
#endif// TOML11_STRING_H
|
@ -0,0 +1,300 @@
|
||||
// Copyright Toru Niina 2017.
|
||||
// Distributed under the MIT License.
|
||||
#ifndef TOML11_TRAITS_HPP
|
||||
#define TOML11_TRAITS_HPP
|
||||
#include <chrono>
|
||||
#include <forward_list>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
#if __has_include(<string_view>)
|
||||
#include <string_view>
|
||||
#endif // has_include(<string_view>)
|
||||
#endif // cplusplus >= C++17
|
||||
|
||||
namespace toml
|
||||
{
|
||||
template<typename C, template<typename ...> class T, template<typename ...> class A>
|
||||
class basic_value;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
// ---------------------------------------------------------------------------
|
||||
// check whether type T is a kind of container/map class
|
||||
|
||||
struct has_iterator_impl
|
||||
{
|
||||
template<typename T> static std::true_type check(typename T::iterator*);
|
||||
template<typename T> static std::false_type check(...);
|
||||
};
|
||||
struct has_value_type_impl
|
||||
{
|
||||
template<typename T> static std::true_type check(typename T::value_type*);
|
||||
template<typename T> static std::false_type check(...);
|
||||
};
|
||||
struct has_key_type_impl
|
||||
{
|
||||
template<typename T> static std::true_type check(typename T::key_type*);
|
||||
template<typename T> static std::false_type check(...);
|
||||
};
|
||||
struct has_mapped_type_impl
|
||||
{
|
||||
template<typename T> static std::true_type check(typename T::mapped_type*);
|
||||
template<typename T> static std::false_type check(...);
|
||||
};
|
||||
struct has_reserve_method_impl
|
||||
{
|
||||
template<typename T> static std::false_type check(...);
|
||||
template<typename T> static std::true_type check(
|
||||
decltype(std::declval<T>().reserve(std::declval<std::size_t>()))*);
|
||||
};
|
||||
struct has_push_back_method_impl
|
||||
{
|
||||
template<typename T> static std::false_type check(...);
|
||||
template<typename T> static std::true_type check(
|
||||
decltype(std::declval<T>().push_back(std::declval<typename T::value_type>()))*);
|
||||
};
|
||||
struct is_comparable_impl
|
||||
{
|
||||
template<typename T> static std::false_type check(...);
|
||||
template<typename T> static std::true_type check(
|
||||
decltype(std::declval<T>() < std::declval<T>())*);
|
||||
};
|
||||
|
||||
struct has_from_toml_method_impl
|
||||
{
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class Tb, template<typename ...> class A>
|
||||
static std::true_type check(
|
||||
decltype(std::declval<T>().from_toml(
|
||||
std::declval<::toml::basic_value<C, Tb, A>>()))*);
|
||||
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class Tb, template<typename ...> class A>
|
||||
static std::false_type check(...);
|
||||
};
|
||||
struct has_into_toml_method_impl
|
||||
{
|
||||
template<typename T>
|
||||
static std::true_type check(decltype(std::declval<T>().into_toml())*);
|
||||
template<typename T>
|
||||
static std::false_type check(...);
|
||||
};
|
||||
|
||||
/// 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
|
||||
#define decltype(...) std::enable_if<true, decltype(__VA_ARGS__)>::type
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
struct has_iterator : decltype(has_iterator_impl::check<T>(nullptr)){};
|
||||
template<typename T>
|
||||
struct has_value_type : decltype(has_value_type_impl::check<T>(nullptr)){};
|
||||
template<typename T>
|
||||
struct has_key_type : decltype(has_key_type_impl::check<T>(nullptr)){};
|
||||
template<typename T>
|
||||
struct has_mapped_type : decltype(has_mapped_type_impl::check<T>(nullptr)){};
|
||||
template<typename T>
|
||||
struct has_reserve_method : decltype(has_reserve_method_impl::check<T>(nullptr)){};
|
||||
template<typename T>
|
||||
struct has_push_back_method : decltype(has_push_back_method_impl::check<T>(nullptr)){};
|
||||
template<typename T>
|
||||
struct is_comparable : decltype(is_comparable_impl::check<T>(nullptr)){};
|
||||
|
||||
template<typename T, typename C,
|
||||
template<typename ...> class Tb, template<typename ...> class A>
|
||||
struct has_from_toml_method
|
||||
: decltype(has_from_toml_method_impl::check<T, C, Tb, A>(nullptr)){};
|
||||
|
||||
template<typename T>
|
||||
struct has_into_toml_method
|
||||
: decltype(has_into_toml_method_impl::check<T>(nullptr)){};
|
||||
|
||||
#ifdef __INTEL_COMPILER
|
||||
#undef decltype
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// C++17 and/or/not
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
|
||||
using std::conjunction;
|
||||
using std::disjunction;
|
||||
using std::negation;
|
||||
|
||||
#else
|
||||
|
||||
template<typename ...> struct conjunction : std::true_type{};
|
||||
template<typename T> struct conjunction<T> : T{};
|
||||
template<typename T, typename ... Ts>
|
||||
struct conjunction<T, Ts...> :
|
||||
std::conditional<static_cast<bool>(T::value), conjunction<Ts...>, T>::type
|
||||
{};
|
||||
|
||||
template<typename ...> struct disjunction : std::false_type{};
|
||||
template<typename T> struct disjunction<T> : T {};
|
||||
template<typename T, typename ... Ts>
|
||||
struct disjunction<T, Ts...> :
|
||||
std::conditional<static_cast<bool>(T::value), T, disjunction<Ts...>>::type
|
||||
{};
|
||||
|
||||
template<typename T>
|
||||
struct negation : std::integral_constant<bool, !static_cast<bool>(T::value)>{};
|
||||
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// type checkers
|
||||
|
||||
template<typename T> struct is_std_pair : std::false_type{};
|
||||
template<typename T1, typename T2>
|
||||
struct is_std_pair<std::pair<T1, T2>> : std::true_type{};
|
||||
|
||||
template<typename T> struct is_std_tuple : std::false_type{};
|
||||
template<typename ... Ts>
|
||||
struct is_std_tuple<std::tuple<Ts...>> : std::true_type{};
|
||||
|
||||
template<typename T> struct is_std_forward_list : std::false_type{};
|
||||
template<typename T>
|
||||
struct is_std_forward_list<std::forward_list<T>> : std::true_type{};
|
||||
|
||||
template<typename T> struct is_chrono_duration: std::false_type{};
|
||||
template<typename Rep, typename Period>
|
||||
struct is_chrono_duration<std::chrono::duration<Rep, Period>>: std::true_type{};
|
||||
|
||||
template<typename T>
|
||||
struct is_map : conjunction< // map satisfies all the following conditions
|
||||
has_iterator<T>, // has T::iterator
|
||||
has_value_type<T>, // has T::value_type
|
||||
has_key_type<T>, // has T::key_type
|
||||
has_mapped_type<T> // has T::mapped_type
|
||||
>{};
|
||||
template<typename T> struct is_map<T&> : is_map<T>{};
|
||||
template<typename T> struct is_map<T const&> : is_map<T>{};
|
||||
template<typename T> struct is_map<T volatile&> : is_map<T>{};
|
||||
template<typename T> struct is_map<T const volatile&> : is_map<T>{};
|
||||
|
||||
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
|
||||
negation<std::is_same<T, std::string_view>>, // not a std::string_view
|
||||
#endif
|
||||
has_iterator<T>, // has T::iterator
|
||||
has_value_type<T> // has T::value_type
|
||||
>{};
|
||||
template<typename T> struct is_container<T&> : is_container<T>{};
|
||||
template<typename T> struct is_container<T const&> : is_container<T>{};
|
||||
template<typename T> struct is_container<T volatile&> : is_container<T>{};
|
||||
template<typename T> struct is_container<T const volatile&> : is_container<T>{};
|
||||
|
||||
template<typename T>
|
||||
struct is_basic_value: std::false_type{};
|
||||
template<typename T> struct is_basic_value<T&> : is_basic_value<T>{};
|
||||
template<typename T> struct is_basic_value<T const&> : is_basic_value<T>{};
|
||||
template<typename T> struct is_basic_value<T volatile&> : is_basic_value<T>{};
|
||||
template<typename T> struct is_basic_value<T const volatile&> : is_basic_value<T>{};
|
||||
template<typename C, template<typename ...> class M, template<typename ...> class V>
|
||||
struct is_basic_value<::toml::basic_value<C, M, V>>: std::true_type{};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// C++14 index_sequence
|
||||
|
||||
#if __cplusplus >= 201402L
|
||||
|
||||
using std::index_sequence;
|
||||
using std::make_index_sequence;
|
||||
|
||||
#else
|
||||
|
||||
template<std::size_t ... Ns> struct index_sequence{};
|
||||
|
||||
template<typename IS, std::size_t N> struct push_back_index_sequence{};
|
||||
template<std::size_t N, std::size_t ... Ns>
|
||||
struct push_back_index_sequence<index_sequence<Ns...>, N>
|
||||
{
|
||||
typedef index_sequence<Ns..., N> type;
|
||||
};
|
||||
|
||||
template<std::size_t N>
|
||||
struct index_sequence_maker
|
||||
{
|
||||
typedef typename push_back_index_sequence<
|
||||
typename index_sequence_maker<N-1>::type, N>::type type;
|
||||
};
|
||||
template<>
|
||||
struct index_sequence_maker<0>
|
||||
{
|
||||
typedef index_sequence<0> type;
|
||||
};
|
||||
template<std::size_t N>
|
||||
using make_index_sequence = typename index_sequence_maker<N-1>::type;
|
||||
|
||||
#endif // __cplusplus >= 2014
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// C++14 enable_if_t
|
||||
|
||||
#if __cplusplus >= 201402L
|
||||
|
||||
using std::enable_if_t;
|
||||
|
||||
#else
|
||||
|
||||
template<bool B, typename T>
|
||||
using enable_if_t = typename std::enable_if<B, T>::type;
|
||||
|
||||
#endif // __cplusplus >= 2014
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// return_type_of_t
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
|
||||
template<typename F, typename ... Args>
|
||||
using return_type_of_t = std::invoke_result_t<F, Args...>;
|
||||
|
||||
#else
|
||||
// result_of is deprecated after C++17
|
||||
template<typename F, typename ... Args>
|
||||
using return_type_of_t = typename std::result_of<F(Args...)>::type;
|
||||
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// is_string_literal
|
||||
//
|
||||
// to use this, pass `typename remove_reference<T>::type` to T.
|
||||
|
||||
template<typename T>
|
||||
struct is_string_literal:
|
||||
disjunction<
|
||||
std::is_same<const char*, T>,
|
||||
conjunction<
|
||||
std::is_array<T>,
|
||||
std::is_same<const char, typename std::remove_extent<T>::type>
|
||||
>
|
||||
>{};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// C++20 remove_cvref_t
|
||||
|
||||
template<typename T>
|
||||
struct remove_cvref
|
||||
{
|
||||
using type = typename std::remove_cv<
|
||||
typename std::remove_reference<T>::type>::type;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using remove_cvref_t = typename remove_cvref<T>::type;
|
||||
|
||||
}// detail
|
||||
}//toml
|
||||
#endif // TOML_TRAITS
|
@ -0,0 +1,150 @@
|
||||
// Copyright Toru Niina 2017.
|
||||
// Distributed under the MIT License.
|
||||
#ifndef TOML11_TYPES_HPP
|
||||
#define TOML11_TYPES_HPP
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "comments.hpp"
|
||||
#include "datetime.hpp"
|
||||
#include "string.hpp"
|
||||
#include "traits.hpp"
|
||||
|
||||
namespace toml
|
||||
{
|
||||
|
||||
template<typename Comment, // discard/preserve_comment
|
||||
template<typename ...> class Table, // map-like class
|
||||
template<typename ...> class Array> // vector-like class
|
||||
class basic_value;
|
||||
|
||||
using character = char;
|
||||
using key = std::string;
|
||||
|
||||
using boolean = bool;
|
||||
using integer = std::int64_t;
|
||||
using floating = double; // "float" is a keyward, cannot use it here.
|
||||
// the following stuffs are structs defined here, so aliases are not needed.
|
||||
// - string
|
||||
// - offset_datetime
|
||||
// - offset_datetime
|
||||
// - local_datetime
|
||||
// - local_date
|
||||
// - local_time
|
||||
|
||||
// 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;
|
||||
|
||||
enum class value_t : std::uint8_t
|
||||
{
|
||||
empty = 0,
|
||||
boolean = 1,
|
||||
integer = 2,
|
||||
floating = 3,
|
||||
string = 4,
|
||||
offset_datetime = 5,
|
||||
local_datetime = 6,
|
||||
local_date = 7,
|
||||
local_time = 8,
|
||||
array = 9,
|
||||
table = 10,
|
||||
};
|
||||
|
||||
template<typename charT, typename traits>
|
||||
inline std::basic_ostream<charT, traits>&
|
||||
operator<<(std::basic_ostream<charT, traits>& os, value_t t)
|
||||
{
|
||||
switch(t)
|
||||
{
|
||||
case value_t::boolean : os << "boolean"; return os;
|
||||
case value_t::integer : os << "integer"; return os;
|
||||
case value_t::floating : os << "floating"; return os;
|
||||
case value_t::string : os << "string"; return os;
|
||||
case value_t::offset_datetime : os << "offset_datetime"; return os;
|
||||
case value_t::local_datetime : os << "local_datetime"; return os;
|
||||
case value_t::local_date : os << "local_date"; return os;
|
||||
case value_t::local_time : os << "local_time"; return os;
|
||||
case value_t::array : os << "array"; return os;
|
||||
case value_t::table : os << "table"; return os;
|
||||
case value_t::empty : os << "empty"; return os;
|
||||
default : os << "unknown"; return os;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename charT = char,
|
||||
typename traits = std::char_traits<charT>,
|
||||
typename alloc = std::allocator<charT>>
|
||||
inline std::basic_string<charT, traits, alloc> stringize(value_t t)
|
||||
{
|
||||
std::basic_ostringstream<charT, traits, alloc> oss;
|
||||
oss << t;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
// helper to define a type that represents a value_t value.
|
||||
template<value_t V>
|
||||
using value_t_constant = std::integral_constant<value_t, V>;
|
||||
|
||||
// meta-function that convertes from value_t to the exact toml type that corresponds to.
|
||||
// It takes toml::basic_value type because array and table types depend on it.
|
||||
template<value_t t, typename Value> struct enum_to_type {using type = void ;};
|
||||
template<typename Value> struct enum_to_type<value_t::empty , Value>{using type = void ;};
|
||||
template<typename Value> struct enum_to_type<value_t::boolean , Value>{using type = boolean ;};
|
||||
template<typename Value> struct enum_to_type<value_t::integer , Value>{using type = integer ;};
|
||||
template<typename Value> struct enum_to_type<value_t::floating , Value>{using type = floating ;};
|
||||
template<typename Value> struct enum_to_type<value_t::string , Value>{using type = string ;};
|
||||
template<typename Value> struct enum_to_type<value_t::offset_datetime, Value>{using type = offset_datetime ;};
|
||||
template<typename Value> struct enum_to_type<value_t::local_datetime , Value>{using type = local_datetime ;};
|
||||
template<typename Value> struct enum_to_type<value_t::local_date , Value>{using type = local_date ;};
|
||||
template<typename Value> struct enum_to_type<value_t::local_time , Value>{using type = local_time ;};
|
||||
template<typename Value> struct enum_to_type<value_t::array , Value>{using type = typename Value::array_type;};
|
||||
template<typename Value> struct enum_to_type<value_t::table , Value>{using type = typename Value::table_type;};
|
||||
|
||||
// meta-function that converts from an exact toml type to the enum that corresponds to.
|
||||
template<typename T, typename Value>
|
||||
struct type_to_enum : std::conditional<
|
||||
std::is_same<T, typename Value::array_type>::value, // if T == array_type,
|
||||
value_t_constant<value_t::array>, // then value_t::array
|
||||
typename std::conditional< // else...
|
||||
std::is_same<T, typename Value::table_type>::value, // if T == table_type
|
||||
value_t_constant<value_t::table>, // then value_t::table
|
||||
value_t_constant<value_t::empty> // else value_t::empty
|
||||
>::type
|
||||
>::type {};
|
||||
template<typename Value> struct type_to_enum<boolean , Value>: value_t_constant<value_t::boolean > {};
|
||||
template<typename Value> struct type_to_enum<integer , Value>: value_t_constant<value_t::integer > {};
|
||||
template<typename Value> struct type_to_enum<floating , Value>: value_t_constant<value_t::floating > {};
|
||||
template<typename Value> struct type_to_enum<string , Value>: value_t_constant<value_t::string > {};
|
||||
template<typename Value> struct type_to_enum<offset_datetime, Value>: value_t_constant<value_t::offset_datetime> {};
|
||||
template<typename Value> struct type_to_enum<local_datetime , Value>: value_t_constant<value_t::local_datetime > {};
|
||||
template<typename Value> struct type_to_enum<local_date , Value>: value_t_constant<value_t::local_date > {};
|
||||
template<typename Value> struct type_to_enum<local_time , Value>: value_t_constant<value_t::local_time > {};
|
||||
|
||||
// meta-function that checks the type T is the same as one of the toml::* types.
|
||||
template<typename T, typename Value>
|
||||
struct is_exact_toml_type : disjunction<
|
||||
std::is_same<T, boolean >,
|
||||
std::is_same<T, integer >,
|
||||
std::is_same<T, floating >,
|
||||
std::is_same<T, string >,
|
||||
std::is_same<T, offset_datetime>,
|
||||
std::is_same<T, local_datetime >,
|
||||
std::is_same<T, local_date >,
|
||||
std::is_same<T, local_time >,
|
||||
std::is_same<T, typename Value::array_type>,
|
||||
std::is_same<T, typename Value::table_type>
|
||||
>{};
|
||||
template<typename T, typename V> struct is_exact_toml_type<T&, V> : is_exact_toml_type<T, V>{};
|
||||
template<typename T, typename V> struct is_exact_toml_type<T const&, V> : is_exact_toml_type<T, V>{};
|
||||
template<typename T, typename V> struct is_exact_toml_type<T volatile&, V> : is_exact_toml_type<T, V>{};
|
||||
template<typename T, typename V> struct is_exact_toml_type<T const volatile&, V>: is_exact_toml_type<T, V>{};
|
||||
|
||||
} // detail
|
||||
} // toml
|
||||
#endif// TOML11_TYPES_H
|
@ -0,0 +1,93 @@
|
||||
// Copyright Toru Niina 2017.
|
||||
// Distributed under the MIT License.
|
||||
#ifndef TOML11_UTILITY_HPP
|
||||
#define TOML11_UTILITY_HPP
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
#include "traits.hpp"
|
||||
|
||||
#if __cplusplus >= 201402L
|
||||
# define TOML11_MARK_AS_DEPRECATED(msg) [[deprecated(msg)]]
|
||||
#elif defined(__GNUC__)
|
||||
# define TOML11_MARK_AS_DEPRECATED(msg) __attribute__((deprecated(msg)))
|
||||
#elif defined(_MSC_VER)
|
||||
# define TOML11_MARK_AS_DEPRECATED(msg) __declspec(deprecated(msg))
|
||||
#else
|
||||
# define TOML11_MARK_AS_DEPRECATED
|
||||
#endif
|
||||
|
||||
namespace toml
|
||||
{
|
||||
|
||||
#if __cplusplus >= 201402L
|
||||
|
||||
using std::make_unique;
|
||||
|
||||
#else
|
||||
|
||||
template<typename T, typename ... Ts>
|
||||
inline std::unique_ptr<T> make_unique(Ts&& ... args)
|
||||
{
|
||||
return std::unique_ptr<T>(new T(std::forward<Ts>(args)...));
|
||||
}
|
||||
|
||||
#endif // __cplusplus >= 2014
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template<typename Container>
|
||||
void try_reserve_impl(Container& container, std::size_t N, std::true_type)
|
||||
{
|
||||
container.reserve(N);
|
||||
return;
|
||||
}
|
||||
template<typename Container>
|
||||
void try_reserve_impl(Container&, std::size_t, std::false_type) noexcept
|
||||
{
|
||||
return;
|
||||
}
|
||||
} // detail
|
||||
|
||||
template<typename Container>
|
||||
void try_reserve(Container& container, std::size_t N)
|
||||
{
|
||||
if(N <= container.size()) {return;}
|
||||
detail::try_reserve_impl(container, N, detail::has_reserve_method<Container>{});
|
||||
return;
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
inline std::string concat_to_string_impl(std::ostringstream& oss)
|
||||
{
|
||||
return oss.str();
|
||||
}
|
||||
template<typename T, typename ... Ts>
|
||||
std::string concat_to_string_impl(std::ostringstream& oss, T&& head, Ts&& ... tail)
|
||||
{
|
||||
oss << std::forward<T>(head);
|
||||
return concat_to_string_impl(oss, std::forward<Ts>(tail) ... );
|
||||
}
|
||||
} // detail
|
||||
|
||||
template<typename ... Ts>
|
||||
std::string concat_to_string(Ts&& ... args)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << std::boolalpha << std::fixed;
|
||||
return detail::concat_to_string_impl(oss, std::forward<Ts>(args) ...);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T from_string(const std::string& str, T opt)
|
||||
{
|
||||
T v(opt);
|
||||
std::istringstream iss(str);
|
||||
iss >> v;
|
||||
return v;
|
||||
}
|
||||
|
||||
}// toml
|
||||
#endif // TOML11_UTILITY
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,23 @@
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
@ -0,0 +1,96 @@
|
||||
// MPark.Variant
|
||||
//
|
||||
// Copyright Michael Park, 2015-2017
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef MPARK_CONFIG_HPP
|
||||
#define MPARK_CONFIG_HPP
|
||||
|
||||
// MSVC 2015 Update 3.
|
||||
#if __cplusplus < 201103L && (!defined(_MSC_VER) || _MSC_FULL_VER < 190024210)
|
||||
#error "MPark.Variant requires C++11 support."
|
||||
#endif
|
||||
|
||||
#ifndef __has_attribute
|
||||
#define __has_attribute(x) 0
|
||||
#endif
|
||||
|
||||
#ifndef __has_builtin
|
||||
#define __has_builtin(x) 0
|
||||
#endif
|
||||
|
||||
#ifndef __has_include
|
||||
#define __has_include(x) 0
|
||||
#endif
|
||||
|
||||
#ifndef __has_feature
|
||||
#define __has_feature(x) 0
|
||||
#endif
|
||||
|
||||
#if __has_attribute(always_inline) || defined(__GNUC__)
|
||||
#define MPARK_ALWAYS_INLINE __attribute__((__always_inline__)) inline
|
||||
#elif defined(_MSC_VER)
|
||||
#define MPARK_ALWAYS_INLINE __forceinline
|
||||
#else
|
||||
#define MPARK_ALWAYS_INLINE inline
|
||||
#endif
|
||||
|
||||
#if __has_builtin(__builtin_addressof) || \
|
||||
(defined(__GNUC__) && __GNUC__ >= 7) || defined(_MSC_VER)
|
||||
#define MPARK_BUILTIN_ADDRESSOF
|
||||
#endif
|
||||
|
||||
#if __has_builtin(__builtin_unreachable) || defined(__GNUC__)
|
||||
#define MPARK_BUILTIN_UNREACHABLE __builtin_unreachable()
|
||||
#elif defined(_MSC_VER)
|
||||
#define MPARK_BUILTIN_UNREACHABLE __assume(false)
|
||||
#else
|
||||
#define MPARK_BUILTIN_UNREACHABLE
|
||||
#endif
|
||||
|
||||
#if __has_builtin(__type_pack_element)
|
||||
#define MPARK_TYPE_PACK_ELEMENT
|
||||
#endif
|
||||
|
||||
#if defined(__cpp_constexpr) && __cpp_constexpr >= 200704 && \
|
||||
!(defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 9)
|
||||
#define MPARK_CPP11_CONSTEXPR
|
||||
#endif
|
||||
|
||||
#if defined(__cpp_constexpr) && __cpp_constexpr >= 201304
|
||||
#define MPARK_CPP14_CONSTEXPR
|
||||
#endif
|
||||
|
||||
#if __has_feature(cxx_exceptions) || defined(__cpp_exceptions) || \
|
||||
(defined(_MSC_VER) && defined(_CPPUNWIND))
|
||||
#define MPARK_EXCEPTIONS
|
||||
#endif
|
||||
|
||||
#if defined(__cpp_generic_lambdas) || defined(_MSC_VER)
|
||||
#define MPARK_GENERIC_LAMBDAS
|
||||
#endif
|
||||
|
||||
#if defined(__cpp_lib_integer_sequence)
|
||||
#define MPARK_INTEGER_SEQUENCE
|
||||
#endif
|
||||
|
||||
#if defined(__cpp_return_type_deduction) || defined(_MSC_VER)
|
||||
#define MPARK_RETURN_TYPE_DEDUCTION
|
||||
#endif
|
||||
|
||||
#if defined(__cpp_lib_transparent_operators) || defined(_MSC_VER)
|
||||
#define MPARK_TRANSPARENT_OPERATORS
|
||||
#endif
|
||||
|
||||
#if defined(__cpp_variable_templates) || defined(_MSC_VER)
|
||||
#define MPARK_VARIABLE_TEMPLATES
|
||||
#endif
|
||||
|
||||
#if !defined(__GLIBCXX__) || __has_include(<codecvt>) // >= libstdc++-5
|
||||
#define MPARK_TRIVIALITY_TYPE_TRAITS
|
||||
#define MPARK_INCOMPLETE_TYPE_TRAITS
|
||||
#endif
|
||||
|
||||
#endif // MPARK_CONFIG_HPP
|
@ -0,0 +1,35 @@
|
||||
// MPark.Variant
|
||||
//
|
||||
// Copyright Michael Park, 2015-2017
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef MPARK_IN_PLACE_HPP
|
||||
#define MPARK_IN_PLACE_HPP
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include "config.hpp"
|
||||
|
||||
namespace mpark {
|
||||
|
||||
struct in_place_t { explicit in_place_t() = default; };
|
||||
|
||||
template <std::size_t I>
|
||||
struct in_place_index_t { explicit in_place_index_t() = default; };
|
||||
|
||||
template <typename T>
|
||||
struct in_place_type_t { explicit in_place_type_t() = default; };
|
||||
|
||||
#ifdef MPARK_VARIABLE_TEMPLATES
|
||||
constexpr in_place_t in_place{};
|
||||
|
||||
template <std::size_t I> constexpr in_place_index_t<I> in_place_index{};
|
||||
|
||||
template <typename T> constexpr in_place_type_t<T> in_place_type{};
|
||||
#endif
|
||||
|
||||
} // namespace mpark
|
||||
|
||||
#endif // MPARK_IN_PLACE_HPP
|
@ -0,0 +1,537 @@
|
||||
// MPark.Variant
|
||||
//
|
||||
// Copyright Michael Park, 2015-2017
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef MPARK_LIB_HPP
|
||||
#define MPARK_LIB_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "config.hpp"
|
||||
|
||||
#define MPARK_RETURN(...) \
|
||||
noexcept(noexcept(__VA_ARGS__)) -> decltype(__VA_ARGS__) { return __VA_ARGS__; }
|
||||
|
||||
namespace mpark {
|
||||
namespace lib {
|
||||
template <typename T>
|
||||
struct identity { using type = T; };
|
||||
|
||||
inline namespace cpp14 {
|
||||
template <typename T, std::size_t N>
|
||||
struct array {
|
||||
constexpr const T &operator[](std::size_t index) const {
|
||||
return data[index];
|
||||
}
|
||||
|
||||
T data[N == 0 ? 1 : N];
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using add_pointer_t = typename std::add_pointer<T>::type;
|
||||
|
||||
template <typename... Ts>
|
||||
using common_type_t = typename std::common_type<Ts...>::type;
|
||||
|
||||
template <typename T>
|
||||
using decay_t = typename std::decay<T>::type;
|
||||
|
||||
template <bool B, typename T = void>
|
||||
using enable_if_t = typename std::enable_if<B, T>::type;
|
||||
|
||||
template <typename T>
|
||||
using remove_const_t = typename std::remove_const<T>::type;
|
||||
|
||||
template <typename T>
|
||||
using remove_reference_t = typename std::remove_reference<T>::type;
|
||||
|
||||
template <typename T>
|
||||
inline constexpr T &&forward(remove_reference_t<T> &t) noexcept {
|
||||
return static_cast<T &&>(t);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline constexpr T &&forward(remove_reference_t<T> &&t) noexcept {
|
||||
static_assert(!std::is_lvalue_reference<T>::value,
|
||||
"can not forward an rvalue as an lvalue");
|
||||
return static_cast<T &&>(t);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline constexpr remove_reference_t<T> &&move(T &&t) noexcept {
|
||||
return static_cast<remove_reference_t<T> &&>(t);
|
||||
}
|
||||
|
||||
#ifdef MPARK_INTEGER_SEQUENCE
|
||||
using std::integer_sequence;
|
||||
using std::index_sequence;
|
||||
using std::make_index_sequence;
|
||||
using std::index_sequence_for;
|
||||
#else
|
||||
template <typename T, T... Is>
|
||||
struct integer_sequence {
|
||||
using value_type = T;
|
||||
static constexpr std::size_t size() noexcept { return sizeof...(Is); }
|
||||
};
|
||||
|
||||
template <std::size_t... Is>
|
||||
using index_sequence = integer_sequence<std::size_t, Is...>;
|
||||
|
||||
template <typename Lhs, typename Rhs>
|
||||
struct make_index_sequence_concat;
|
||||
|
||||
template <std::size_t... Lhs, std::size_t... Rhs>
|
||||
struct make_index_sequence_concat<index_sequence<Lhs...>,
|
||||
index_sequence<Rhs...>>
|
||||
: identity<index_sequence<Lhs..., (sizeof...(Lhs) + Rhs)...>> {};
|
||||
|
||||
template <std::size_t N>
|
||||
struct make_index_sequence_impl;
|
||||
|
||||
template <std::size_t N>
|
||||
using make_index_sequence = typename make_index_sequence_impl<N>::type;
|
||||
|
||||
template <std::size_t N>
|
||||
struct make_index_sequence_impl
|
||||
: make_index_sequence_concat<make_index_sequence<N / 2>,
|
||||
make_index_sequence<N - (N / 2)>> {};
|
||||
|
||||
template <>
|
||||
struct make_index_sequence_impl<0> : identity<index_sequence<>> {};
|
||||
|
||||
template <>
|
||||
struct make_index_sequence_impl<1> : identity<index_sequence<0>> {};
|
||||
|
||||
template <typename... Ts>
|
||||
using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
|
||||
#endif
|
||||
|
||||
// <functional>
|
||||
#ifdef MPARK_TRANSPARENT_OPERATORS
|
||||
using equal_to = std::equal_to<>;
|
||||
#else
|
||||
struct equal_to {
|
||||
template <typename Lhs, typename Rhs>
|
||||
inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
|
||||
MPARK_RETURN(lib::forward<Lhs>(lhs) == lib::forward<Rhs>(rhs))
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef MPARK_TRANSPARENT_OPERATORS
|
||||
using not_equal_to = std::not_equal_to<>;
|
||||
#else
|
||||
struct not_equal_to {
|
||||
template <typename Lhs, typename Rhs>
|
||||
inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
|
||||
MPARK_RETURN(lib::forward<Lhs>(lhs) != lib::forward<Rhs>(rhs))
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef MPARK_TRANSPARENT_OPERATORS
|
||||
using less = std::less<>;
|
||||
#else
|
||||
struct less {
|
||||
template <typename Lhs, typename Rhs>
|
||||
inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
|
||||
MPARK_RETURN(lib::forward<Lhs>(lhs) < lib::forward<Rhs>(rhs))
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef MPARK_TRANSPARENT_OPERATORS
|
||||
using greater = std::greater<>;
|
||||
#else
|
||||
struct greater {
|
||||
template <typename Lhs, typename Rhs>
|
||||
inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
|
||||
MPARK_RETURN(lib::forward<Lhs>(lhs) > lib::forward<Rhs>(rhs))
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef MPARK_TRANSPARENT_OPERATORS
|
||||
using less_equal = std::less_equal<>;
|
||||
#else
|
||||
struct less_equal {
|
||||
template <typename Lhs, typename Rhs>
|
||||
inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
|
||||
MPARK_RETURN(lib::forward<Lhs>(lhs) <= lib::forward<Rhs>(rhs))
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef MPARK_TRANSPARENT_OPERATORS
|
||||
using greater_equal = std::greater_equal<>;
|
||||
#else
|
||||
struct greater_equal {
|
||||
template <typename Lhs, typename Rhs>
|
||||
inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
|
||||
MPARK_RETURN(lib::forward<Lhs>(lhs) >= lib::forward<Rhs>(rhs))
|
||||
};
|
||||
#endif
|
||||
} // namespace cpp14
|
||||
|
||||
inline namespace cpp17 {
|
||||
|
||||
// <type_traits>
|
||||
template <bool B>
|
||||
using bool_constant = std::integral_constant<bool, B>;
|
||||
|
||||
template <typename...>
|
||||
struct voider : identity<void> {};
|
||||
|
||||
template <typename... Ts>
|
||||
using void_t = typename voider<Ts...>::type;
|
||||
|
||||
namespace detail {
|
||||
namespace swappable {
|
||||
|
||||
using std::swap;
|
||||
|
||||
template <typename T>
|
||||
struct is_swappable {
|
||||
private:
|
||||
template <typename U,
|
||||
typename = decltype(swap(std::declval<U &>(),
|
||||
std::declval<U &>()))>
|
||||
inline static std::true_type test(int);
|
||||
|
||||
template <typename U>
|
||||
inline static std::false_type test(...);
|
||||
|
||||
public:
|
||||
static constexpr bool value = decltype(test<T>(0))::value;
|
||||
};
|
||||
|
||||
template <bool IsSwappable, typename T>
|
||||
struct is_nothrow_swappable {
|
||||
static constexpr bool value =
|
||||
noexcept(swap(std::declval<T &>(), std::declval<T &>()));
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct is_nothrow_swappable<false, T> : std::false_type {};
|
||||
|
||||
} // namespace swappable
|
||||
} // namespace detail
|
||||
|
||||
using detail::swappable::is_swappable;
|
||||
|
||||
template <typename T>
|
||||
using is_nothrow_swappable =
|
||||
detail::swappable::is_nothrow_swappable<is_swappable<T>::value, T>;
|
||||
|
||||
// <functional>
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
struct is_reference_wrapper : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct is_reference_wrapper<std::reference_wrapper<T>>
|
||||
: std::true_type {};
|
||||
|
||||
template <bool, int>
|
||||
struct Invoke;
|
||||
|
||||
template <>
|
||||
struct Invoke<true /* pmf */, 0 /* is_base_of */> {
|
||||
template <typename R, typename T, typename Arg, typename... Args>
|
||||
inline static constexpr auto invoke(R T::*pmf, Arg &&arg, Args &&... args)
|
||||
MPARK_RETURN((lib::forward<Arg>(arg).*pmf)(lib::forward<Args>(args)...))
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Invoke<true /* pmf */, 1 /* is_reference_wrapper */> {
|
||||
template <typename R, typename T, typename Arg, typename... Args>
|
||||
inline static constexpr auto invoke(R T::*pmf, Arg &&arg, Args &&... args)
|
||||
MPARK_RETURN((lib::forward<Arg>(arg).get().*pmf)(lib::forward<Args>(args)...))
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Invoke<true /* pmf */, 2 /* otherwise */> {
|
||||
template <typename R, typename T, typename Arg, typename... Args>
|
||||
inline static constexpr auto invoke(R T::*pmf, Arg &&arg, Args &&... args)
|
||||
MPARK_RETURN(((*lib::forward<Arg>(arg)).*pmf)(lib::forward<Args>(args)...))
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Invoke<false /* pmo */, 0 /* is_base_of */> {
|
||||
template <typename R, typename T, typename Arg>
|
||||
inline static constexpr auto invoke(R T::*pmo, Arg &&arg)
|
||||
MPARK_RETURN(lib::forward<Arg>(arg).*pmo)
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Invoke<false /* pmo */, 1 /* is_reference_wrapper */> {
|
||||
template <typename R, typename T, typename Arg>
|
||||
inline static constexpr auto invoke(R T::*pmo, Arg &&arg)
|
||||
MPARK_RETURN(lib::forward<Arg>(arg).get().*pmo)
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Invoke<false /* pmo */, 2 /* otherwise */> {
|
||||
template <typename R, typename T, typename Arg>
|
||||
inline static constexpr auto invoke(R T::*pmo, Arg &&arg)
|
||||
MPARK_RETURN((*lib::forward<Arg>(arg)).*pmo)
|
||||
};
|
||||
|
||||
template <typename R, typename T, typename Arg, typename... Args>
|
||||
inline constexpr auto invoke(R T::*f, Arg &&arg, Args &&... args)
|
||||
MPARK_RETURN(
|
||||
Invoke<std::is_function<R>::value,
|
||||
(std::is_base_of<T, lib::decay_t<Arg>>::value
|
||||
? 0
|
||||
: is_reference_wrapper<lib::decay_t<Arg>>::value
|
||||
? 1
|
||||
: 2)>::invoke(f,
|
||||
lib::forward<Arg>(arg),
|
||||
lib::forward<Args>(args)...))
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4100)
|
||||
#endif
|
||||
template <typename F, typename... Args>
|
||||
inline constexpr auto invoke(F &&f, Args &&... args)
|
||||
MPARK_RETURN(lib::forward<F>(f)(lib::forward<Args>(args)...))
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
} // namespace detail
|
||||
|
||||
template <typename F, typename... Args>
|
||||
inline constexpr auto invoke(F &&f, Args &&... args)
|
||||
MPARK_RETURN(detail::invoke(lib::forward<F>(f),
|
||||
lib::forward<Args>(args)...))
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename Void, typename, typename...>
|
||||
struct invoke_result {};
|
||||
|
||||
template <typename F, typename... Args>
|
||||
struct invoke_result<void_t<decltype(lib::invoke(
|
||||
std::declval<F>(), std::declval<Args>()...))>,
|
||||
F,
|
||||
Args...>
|
||||
: identity<decltype(
|
||||
lib::invoke(std::declval<F>(), std::declval<Args>()...))> {};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename F, typename... Args>
|
||||
using invoke_result = detail::invoke_result<void, F, Args...>;
|
||||
|
||||
template <typename F, typename... Args>
|
||||
using invoke_result_t = typename invoke_result<F, Args...>::type;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename Void, typename, typename...>
|
||||
struct is_invocable : std::false_type {};
|
||||
|
||||
template <typename F, typename... Args>
|
||||
struct is_invocable<void_t<invoke_result_t<F, Args...>>, F, Args...>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename Void, typename, typename, typename...>
|
||||
struct is_invocable_r : std::false_type {};
|
||||
|
||||
template <typename R, typename F, typename... Args>
|
||||
struct is_invocable_r<void_t<invoke_result_t<F, Args...>>,
|
||||
R,
|
||||
F,
|
||||
Args...>
|
||||
: std::is_convertible<invoke_result_t<F, Args...>, R> {};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename F, typename... Args>
|
||||
using is_invocable = detail::is_invocable<void, F, Args...>;
|
||||
|
||||
template <typename R, typename F, typename... Args>
|
||||
using is_invocable_r = detail::is_invocable_r<void, R, F, Args...>;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <bool Invocable, typename F, typename... Args>
|
||||
struct is_nothrow_invocable {
|
||||
static constexpr bool value =
|
||||
noexcept(lib::invoke(std::declval<F>(), std::declval<Args>()...));
|
||||
};
|
||||
|
||||
template <typename F, typename... Args>
|
||||
struct is_nothrow_invocable<false, F, Args...> : std::false_type {};
|
||||
|
||||
template <bool Invocable, typename R, typename F, typename... Args>
|
||||
struct is_nothrow_invocable_r {
|
||||
private:
|
||||
inline static R impl() {
|
||||
return lib::invoke(std::declval<F>(), std::declval<Args>()...);
|
||||
}
|
||||
|
||||
public:
|
||||
static constexpr bool value = noexcept(impl());
|
||||
};
|
||||
|
||||
template <typename R, typename F, typename... Args>
|
||||
struct is_nothrow_invocable_r<false, R, F, Args...> : std::false_type {};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename F, typename... Args>
|
||||
using is_nothrow_invocable = detail::
|
||||
is_nothrow_invocable<is_invocable<F, Args...>::value, F, Args...>;
|
||||
|
||||
template <typename R, typename F, typename... Args>
|
||||
using is_nothrow_invocable_r =
|
||||
detail::is_nothrow_invocable_r<is_invocable_r<R, F, Args...>::value,
|
||||
R,
|
||||
F,
|
||||
Args...>;
|
||||
|
||||
// <memory>
|
||||
#ifdef MPARK_BUILTIN_ADDRESSOF
|
||||
template <typename T>
|
||||
inline constexpr T *addressof(T &arg) noexcept {
|
||||
return __builtin_addressof(arg);
|
||||
}
|
||||
#else
|
||||
namespace detail {
|
||||
|
||||
namespace has_addressof_impl {
|
||||
|
||||
struct fail;
|
||||
|
||||
template <typename T>
|
||||
inline fail operator&(T &&);
|
||||
|
||||
template <typename T>
|
||||
inline static constexpr bool impl() {
|
||||
return (std::is_class<T>::value || std::is_union<T>::value) &&
|
||||
!std::is_same<decltype(&std::declval<T &>()), fail>::value;
|
||||
}
|
||||
|
||||
} // namespace has_addressof_impl
|
||||
|
||||
template <typename T>
|
||||
using has_addressof = bool_constant<has_addressof_impl::impl<T>()>;
|
||||
|
||||
template <typename T>
|
||||
inline constexpr T *addressof(T &arg, std::true_type) noexcept {
|
||||
return std::addressof(arg);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline constexpr T *addressof(T &arg, std::false_type) noexcept {
|
||||
return &arg;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename T>
|
||||
inline constexpr T *addressof(T &arg) noexcept {
|
||||
return detail::addressof(arg, detail::has_addressof<T>{});
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
inline constexpr T *addressof(const T &&) = delete;
|
||||
|
||||
} // namespace cpp17
|
||||
|
||||
template <typename T>
|
||||
struct remove_all_extents : identity<T> {};
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
struct remove_all_extents<array<T, N>> : remove_all_extents<T> {};
|
||||
|
||||
template <typename T>
|
||||
using remove_all_extents_t = typename remove_all_extents<T>::type;
|
||||
|
||||
template <std::size_t N>
|
||||
using size_constant = std::integral_constant<std::size_t, N>;
|
||||
|
||||
template <std::size_t I, typename T>
|
||||
struct indexed_type : size_constant<I> { using type = T; };
|
||||
|
||||
template <bool... Bs>
|
||||
using all = std::is_same<integer_sequence<bool, true, Bs...>,
|
||||
integer_sequence<bool, Bs..., true>>;
|
||||
|
||||
#ifdef MPARK_TYPE_PACK_ELEMENT
|
||||
template <std::size_t I, typename... Ts>
|
||||
using type_pack_element_t = __type_pack_element<I, Ts...>;
|
||||
#else
|
||||
template <std::size_t I, typename... Ts>
|
||||
struct type_pack_element_impl {
|
||||
private:
|
||||
template <typename>
|
||||
struct set;
|
||||
|
||||
template <std::size_t... Is>
|
||||
struct set<index_sequence<Is...>> : indexed_type<Is, Ts>... {};
|
||||
|
||||
template <typename T>
|
||||
inline static std::enable_if<true, T> impl(indexed_type<I, T>);
|
||||
|
||||
inline static std::enable_if<false> impl(...);
|
||||
|
||||
public:
|
||||
using type = decltype(impl(set<index_sequence_for<Ts...>>{}));
|
||||
};
|
||||
|
||||
template <std::size_t I, typename... Ts>
|
||||
using type_pack_element = typename type_pack_element_impl<I, Ts...>::type;
|
||||
|
||||
template <std::size_t I, typename... Ts>
|
||||
using type_pack_element_t = typename type_pack_element<I, Ts...>::type;
|
||||
#endif
|
||||
|
||||
#ifdef MPARK_TRIVIALITY_TYPE_TRAITS
|
||||
using std::is_trivially_copy_constructible;
|
||||
using std::is_trivially_move_constructible;
|
||||
using std::is_trivially_copy_assignable;
|
||||
using std::is_trivially_move_assignable;
|
||||
#else
|
||||
template <typename T>
|
||||
struct is_trivially_copy_constructible
|
||||
: bool_constant<
|
||||
std::is_copy_constructible<T>::value && __has_trivial_copy(T)> {};
|
||||
|
||||
template <typename T>
|
||||
struct is_trivially_move_constructible : bool_constant<__is_trivial(T)> {};
|
||||
|
||||
template <typename T>
|
||||
struct is_trivially_copy_assignable
|
||||
: bool_constant<
|
||||
std::is_copy_assignable<T>::value && __has_trivial_assign(T)> {};
|
||||
|
||||
template <typename T>
|
||||
struct is_trivially_move_assignable : bool_constant<__is_trivial(T)> {};
|
||||
#endif
|
||||
|
||||
template <typename T, bool>
|
||||
struct dependent_type : T {};
|
||||
|
||||
template <typename Is, std::size_t J>
|
||||
struct push_back;
|
||||
|
||||
template <typename Is, std::size_t J>
|
||||
using push_back_t = typename push_back<Is, J>::type;
|
||||
|
||||
template <std::size_t... Is, std::size_t J>
|
||||
struct push_back<index_sequence<Is...>, J> {
|
||||
using type = index_sequence<Is..., J>;
|
||||
};
|
||||
|
||||
} // namespace lib
|
||||
} // namespace mpark
|
||||
|
||||
#undef MPARK_RETURN
|
||||
|
||||
#endif // MPARK_LIB_HPP
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue