diff --git a/implementations/Crafter.Build-Test.cpp b/implementations/Crafter.Build-Test.cpp index 23ef72d..8ad80fb 100644 --- a/implementations/Crafter.Build-Test.cpp +++ b/implementations/Crafter.Build-Test.cpp @@ -17,12 +17,6 @@ License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -module; -// toml++ is consumed as a translation-unit-private dependency in the GMF: the -// parser is only needed for test.toml discovery here, so keeping it out of -// `import std`-using module purviews avoids dragging it through the module -// graph (and through every PCM consumer of Crafter.Build). -#include "../lib/toml.hpp" export module Crafter.Build:Test_impl; import std; import :Test; @@ -109,11 +103,15 @@ namespace { #endif } - std::string JoinAndQuoteArgs(std::span args) { + std::string JoinAndQuoteArgs(std::span args, TestRunner::Shell shell) { std::string out; for (const auto& a : args) { if (!out.empty()) out.push_back(' '); - out += ShellQuoteHost(a); + switch (shell) { + case TestRunner::Shell::Sh: out += ShellQuoteSh(a); break; + case TestRunner::Shell::Cmd: out += ShellQuoteCmd(a); break; + case TestRunner::Shell::Host: out += ShellQuoteHost(a); break; + } } return out; } @@ -237,12 +235,69 @@ Configuration* Crafter::ParentLib(std::string_view name) { TestRunner TestRunner::Local() { TestRunner r; r.name = "local"; + r.argsShell = Shell::Host; + return r; +} + +TestRunner TestRunner::Ssh(std::string host, std::string remoteDir) { + TestRunner r; + r.name = std::format("ssh:{}", host); + r.remoteDir = std::move(remoteDir); + r.argsShell = Shell::Sh; + // Outer "..." (not '...') so the wrapper survives both sh (Linux host) + // and cmd (Windows host). cmd doesn't honor single quotes, which would + // cause it to split on the inner '&&'. Args inside use POSIX quoting + // because they reach a remote bash regardless of which host issued them. + r.copy = std::format("ssh -q {0} \"mkdir -p {{remote_bundle}}\" && scp -r -q {{bundle}}/. {0}:{{remote_bundle}}/", host); + r.exec = std::format("ssh -q {} \"cd {{remote_bundle}} && ./{{bin_name}} {{args}}\"", host); + r.cleanup = std::format("ssh -q {} \"rm -rf {{remote_bundle}}\"", host); + // RunCommandChecked captures stdout+stderr internally — no shell redirect + // needed in the probe spec, which means it works the same way under cmd + // (Windows host) and sh (Linux host) since `> /dev/null` doesn't translate. + r.probe = std::format("ssh -q -o BatchMode=yes -o ConnectTimeout=5 {} true", host); + return r; +} + +TestRunner TestRunner::SshWin(std::string host, std::string remoteDir) { + TestRunner r; + r.name = std::format("sshwin:{}", host); + r.remoteDir = std::move(remoteDir); + r.argsShell = Shell::Cmd; + // cmd.exe-friendly templates. Use backslash variants of remote paths because + // cmd's mkdir won't auto-create intermediate directories with forward + // slashes. scp tolerates either, so we keep forward slashes for the scp + // dest (which `{remote_bundle}` provides). 2>nul + `& rem ok` swallows + // mkdir's "already exists" error and forces exit code 0. + r.copy = std::format("ssh -q {0} \"mkdir {{remote_bundle_win}} 2>nul & rem ok\" && scp -r -q {{bundle}}/. {0}:{{remote_bundle}}/", host); + r.exec = std::format("ssh -q {} \"{{bin_win}} {{args}}\"", host); + r.cleanup = std::format("ssh -q {} \"rd /s /q {{remote_bundle_win}} 2>nul & rem ok\"", host); + // No shell redirect in the probe — see TestRunner::Ssh for rationale. + r.probe = std::format("ssh -q -o BatchMode=yes -o ConnectTimeout=5 {} \"ver\"", host); + return r; +} + +TestRunner TestRunner::Wsl(std::string remoteDir) { + TestRunner r; + r.name = "wsl"; + r.argsShell = Shell::Sh; + r.remoteDir = std::move(remoteDir); + // Transport runner: stage the test bundle into WSL's native filesystem + // (faster than executing in-place from /mnt/c) then run via bash. {bundle_wsl} + // is the local Windows path translated to /mnt//... form so wsl cp + // can read it. Args are POSIX-quoted because they reach a Linux shell. + r.copy = "wsl mkdir -p {remote_bundle} && wsl cp -r {bundle_wsl}/. {remote_bundle}/"; + r.exec = "wsl bash -c \"cd {remote_bundle} && ./{bin_name} {args}\""; + r.cleanup = "wsl rm -rf {remote_bundle}"; + // `where wsl` matches the host-shell convention; on a non-Windows host + // it fails (no `where` command), which correctly skips this runner. + r.probe = "where wsl"; return r; } TestRunner TestRunner::Cmd(std::string command) { TestRunner r; r.name = std::format("cmd:{}", command); + r.argsShell = Shell::Host; r.exec = std::format("{} {{bin}} {{args}}", command); #ifdef _WIN32 r.probe = std::format("where {}", command); @@ -252,68 +307,6 @@ TestRunner TestRunner::Cmd(std::string command) { return r; } -TestRunner TestRunner::Wine() { - TestRunner r; - r.name = "wine"; - r.exec = "wine {bin} {args}"; -#ifdef _WIN32 - r.probe = "where wine"; -#else - r.probe = "which wine"; -#endif - return r; -} - -TestRunner TestRunner::ForTarget(const Configuration& cfg) { - const std::string& target = cfg.target; - - // Same triple as the host → run the binary directly. Covers the common - // case (cfg.target defaulted to HostTarget()) without any wrapper. - if (target == HostTarget()) return Local(); - - // Windows targets: native on a Windows host, Wine on Linux. We don't - // distinguish mingw vs msvc here — the produced .exe runs the same way. - if (TargetIsWindows(target)) { - return TargetIsWindows(HostTarget()) ? Local() : Wine(); - } - - // WASI: a .wasm file isn't directly executable; wasmtime is the canonical - // runtime. wasi-cli also works but the upstream Bytecode Alliance name is - // wasmtime, so we standardize on that. - if (target.starts_with("wasm32-wasi") || target.starts_with("wasm64-wasi")) { - return Cmd("wasmtime"); - } - - // Non-host Linux triple: extract the architecture and route through - // qemu-user. Triple is --- (or sometimes 3 parts); - // qemu-user's binary names mostly follow the arch field, with two known - // mismatches handled below. cfg.sysroot, when set, becomes QEMU_LD_PREFIX - // so the target's dynamic linker / shared libs are reachable — without - // it qemu-user crashes on dynamic ELFs with "could not open /lib/ld...". - if (target.find("-linux-") != std::string::npos) { - auto dash = target.find('-'); - std::string arch = target.substr(0, dash); - // i686-linux-gnu → qemu-i386; arm-* already matches qemu-arm; aarch64, - // riscv64, ppc64le, mips, mips64, s390x all match their qemu names. - if (arch == "i686") arch = "i386"; - TestRunner r = Cmd(std::format("qemu-{}", arch)); - if (!cfg.sysroot.empty()) { - // Use `env VAR=value cmd` rather than the shell's `VAR=value cmd` - // prefix syntax: RunCommandWithTimeout pipes through GNU `timeout`, - // which execvp's its argument list directly without going through a - // shell. A bare VAR=value would be exec'd as a command path and - // fail with "No such file or directory". - r.exec = std::format("env QEMU_LD_PREFIX={} {}", cfg.sysroot, r.exec); - } - return r; - } - - // Unknown / bare-metal / freestanding targets: fall back to Local. The - // caller's runner-availability probe (or absence of the binary) surfaces - // the problem rather than us inventing a wrong wrapper here. - return Local(); -} - namespace { std::string NormalizeTriple(std::string_view target) { std::string out(target); @@ -323,18 +316,56 @@ namespace { return out; } - // Spec grammar: "local" | "cmd:". Used by CRAFTER_BUILD_RUNNER_* - // env vars and --runner=. The set used to include ssh/sshwin/wsl variants; - // those were removed when transport-style runners were dropped (issue #8). std::optional ParseRunnerSpec(std::string_view spec) { if (spec.empty()) return std::nullopt; - if (spec == "local") return TestRunner::Local(); - if (spec.starts_with("cmd:") && spec.size() > 4) { - return TestRunner::Cmd(std::string(spec.substr(4))); + std::vector parts; + for (auto piece : std::views::split(spec, ':')) { + parts.emplace_back(std::string_view(piece.begin(), piece.end())); + } + if (parts.empty()) return std::nullopt; + if (parts[0] == "local") return TestRunner::Local(); + if (parts[0] == "cmd" && parts.size() == 2) return TestRunner::Cmd(parts[1]); + if (parts[0] == "ssh" && parts.size() == 2) return TestRunner::Ssh(parts[1]); + if (parts[0] == "ssh" && parts.size() >= 3) { + std::string remote; + for (std::size_t i = 2; i < parts.size(); ++i) { + if (i > 2) remote.push_back(':'); + remote += parts[i]; + } + return TestRunner::Ssh(parts[1], remote); + } + if (parts[0] == "sshwin" && parts.size() == 2) return TestRunner::SshWin(parts[1]); + if (parts[0] == "sshwin" && parts.size() >= 3) { + std::string remote; + for (std::size_t i = 2; i < parts.size(); ++i) { + if (i > 2) remote.push_back(':'); + remote += parts[i]; + } + return TestRunner::SshWin(parts[1], remote); + } + if (parts[0] == "wsl" && parts.size() == 1) return TestRunner::Wsl(); + if (parts[0] == "wsl" && parts.size() >= 2) { + std::string remote; + for (std::size_t i = 1; i < parts.size(); ++i) { + if (i > 1) remote.push_back(':'); + remote += parts[i]; + } + return TestRunner::Wsl(remote); } throw std::runtime_error(std::format( - "TestRunner::FromSpec: unrecognized runner spec '{}' " - "(expected 'local' or 'cmd:')", spec)); + "TestRunner::FromSpec: unrecognized runner spec '{}'", spec)); + } + + // C:\Users\jorij -> /mnt/c/Users/jorij. Idempotent on paths that don't + // start with a drive letter, so callers can compute it unconditionally. + std::string WindowsPathToWsl(std::string_view p) { + if (p.size() < 2 || p[1] != ':') return std::string(p); + char drive = static_cast(std::tolower(static_cast(p[0]))); + std::string out = std::format("/mnt/{}", drive); + for (std::size_t i = 2; i < p.size(); ++i) { + out.push_back(p[i] == '\\' ? '/' : p[i]); + } + return out; } } @@ -351,24 +382,60 @@ TestRunner TestRunner::FromEnv(std::string_view target, TestRunner fallback) { } TestResult Crafter::RunSingleTest(const Test& test, const fs::path& binary, std::chrono::seconds timeout) { + using namespace std::chrono_literals; TestResult result; result.name = test.config.name; std::map ph; - ph["{args}"] = JoinAndQuoteArgs(test.args); + ph["{args}"] = JoinAndQuoteArgs(test.args, test.runner.argsShell); + ph["{bin_name}"] = binary.filename().string(); + ph["{bundle}"] = binary.parent_path().string(); auto start = std::chrono::steady_clock::now(); CommandResult r; if (test.runner.exec.empty()) { - // Local runner: spawn the binary directly through the host shell. + // Pure-local runner: spawn the binary directly through the host shell. std::string cmd = std::format("{} {}", ShellQuoteHost(binary.string()), ph["{args}"]); r = RunCommandWithTimeout(cmd, timeout); - } else { - // Prefix runner (qemu-user, wasmtime, wine, ...): templated exec - // wraps the local binary. + } else if (test.runner.copy.empty()) { + // Prefix runner (qemu-user, wsl, ...): templated exec wraps a local binary. ph["{bin}"] = binary.string(); r = RunCommandWithTimeout(Substitute(test.runner.exec, ph), timeout); + } else { + // Transport runner (ssh, ...): copy bundle → exec remotely → cleanup. + std::string unique = std::format("{}-{}-{}", + test.config.name, + std::hash{}(std::this_thread::get_id()), + std::chrono::steady_clock::now().time_since_epoch().count()); + ph["{remote_bundle}"] = std::format("{}/{}", test.runner.remoteDir, unique); + ph["{bin}"] = std::format("{}/{}", ph["{remote_bundle}"], ph["{bin_name}"]); + // Backslash variants for cmd.exe-shell remotes (cmd's mkdir won't + // auto-create intermediate directories when path uses forward slashes). + ph["{remote_bundle_win}"] = ph["{remote_bundle}"]; + std::replace(ph["{remote_bundle_win}"].begin(), ph["{remote_bundle_win}"].end(), '/', '\\'); + ph["{bin_win}"] = ph["{bin}"]; + std::replace(ph["{bin_win}"].begin(), ph["{bin_win}"].end(), '/', '\\'); + // WSL form of the local bundle path: C:\foo -> /mnt/c/foo. Used by + // the Wsl runner's `wsl cp -r` step to read the test bundle out of + // the Windows host's filesystem. + ph["{bundle_wsl}"] = WindowsPathToWsl(ph["{bundle}"]); + + CommandResult cp = RunCommandWithTimeout(Substitute(test.runner.copy, ph), 5min); + if (cp.exitCode != 0) { + auto end = std::chrono::steady_clock::now(); + result.duration = std::chrono::duration_cast(end - start); + result.outcome = TestOutcome::Fail; + result.exitCode = cp.exitCode; + result.output = std::format("copy step failed (exit {}):\n{}", cp.exitCode, cp.output); + return result; + } + + r = RunCommandWithTimeout(Substitute(test.runner.exec, ph), timeout); + + if (!test.runner.cleanup.empty()) { + std::system(Substitute(test.runner.cleanup, ph).c_str()); + } } auto end = std::chrono::steady_clock::now(); @@ -386,165 +453,20 @@ TestResult Crafter::RunSingleTest(const Test& test, const fs::path& binary, std: return result; } -namespace { - // Declarative test metadata loaded from tests//test.toml. Lets a test - // ship just main.cpp + a few lines of config instead of a whole project.cpp - // when its needs are "pick a target, gate on prerequisites, run with these - // args". project.cpp stays the escape hatch for outer-driver tests that - // call Build() / inspect intermediate state. - struct TestManifest { - std::optional target; - std::optional march; - std::optional mtune; - std::optional sysroot; - std::vector requires_; - std::optional timeoutSeconds; - std::vector args; - std::vector> defines; - }; - - TestManifest ParseTestManifest(const fs::path& path) { - // toml++ builds with exceptions enabled by default; parse_file throws - // toml::parse_error on malformed input. Rethrow with the path attached - // so the discovery loop's catch can surface "where the error came from" - // alongside toml++'s "what was wrong". - toml::table t; - try { - t = toml::parse_file(path.string()); - } catch (const toml::parse_error& e) { - throw std::runtime_error(std::format( - "test.toml parse error in {}: {}", - path.string(), - std::string_view(e.description()))); - } - TestManifest m; - if (auto v = t["target"].value()) m.target = *v; - if (auto v = t["march"].value()) m.march = *v; - if (auto v = t["mtune"].value()) m.mtune = *v; - if (auto v = t["sysroot"].value()) m.sysroot = *v; - if (auto v = t["timeout"].value()) m.timeoutSeconds = static_cast(*v); - if (auto arr = t["requires"].as_array()) { - for (auto& el : *arr) { - if (auto s = el.value()) m.requires_.push_back(*s); - } - } - if (auto arr = t["args"].as_array()) { - for (auto& el : *arr) { - if (auto s = el.value()) m.args.push_back(*s); - } - } - if (auto tbl = t["defines"].as_table()) { - for (auto&& [k, v] : *tbl) { - if (auto s = v.value()) { - m.defines.emplace_back(std::string(k.str()), *s); - } - } - } - return m; - } - - // Apply manifest overlay onto a Configuration synthesized from the test - // folder. Target overrides come last so a manifest's `target = "..."` - // wins over the synth default (= run's targetFilter). Defines accumulate; - // they don't replace pre-existing ones. - void ApplyManifest(Configuration& cfg, const TestManifest& m) { - if (m.target) cfg.target = *m.target; - if (m.march) cfg.march = *m.march; - if (m.mtune) cfg.mtune = *m.mtune; - if (m.sysroot) cfg.sysroot = *m.sysroot; - for (auto& [k, v] : m.defines) cfg.defines.push_back({k, v}); - } - - bool ToolOnPath(std::string_view name) { -#ifdef _WIN32 - std::string cmd = std::format("where {} > nul 2>&1", name); -#else - std::string cmd = std::format("which {} > /dev/null 2>&1", name); -#endif - return std::system(cmd.c_str()) == 0; - } - - struct RequireResult { - bool ok; - std::string reason; // human-readable when !ok - }; - - // Evaluate each `:` precondition. Returns the first failure - // (short-circuit; reporting one missing dep at a time is enough to act on - // and keeps the test log uncluttered). - RequireResult EvaluateRequires(std::span reqs) { - for (const auto& r : reqs) { - auto sep = r.find(':'); - if (sep == std::string::npos || sep == 0 || sep == r.size() - 1) { - return {false, std::format("malformed require '{}' (expected kind:arg)", r)}; - } - std::string_view kind(r.data(), sep); - std::string_view arg(r.data() + sep + 1, r.size() - sep - 1); - if (kind == "tool") { - if (!ToolOnPath(arg)) { - return {false, std::format("tool '{}' not on PATH", arg)}; - } - } else if (kind == "file") { - if (!fs::exists(std::string(arg))) { - return {false, std::format("file '{}' missing", arg)}; - } - } else if (kind == "env") { - const char* v = std::getenv(std::string(arg).c_str()); - if (!v || !*v) { - return {false, std::format("env '{}' unset", arg)}; - } - } else { - return {false, std::format( - "unknown require kind '{}' (expected tool/file/env)", kind)}; - } - } - return {true, ""}; - } - - // Match a runner's tool dependency against the test's declared - // requirements. Used to decide between Skip (declared, may legitimately - // be missing) and Fail (runner unavailable but test didn't declare it — - // a silent skip would mask broken cross-arch CI configuration). - bool RequiresMentionsTool(std::span reqs, std::string_view tool) { - std::string needle = std::format("tool:{}", tool); - return std::ranges::any_of(reqs, [&](const std::string& s) { return s == needle; }); - } - - // Best-effort extraction of the runner-tool name from a TestRunner so the - // hard-fail-unless-declared check can match it against `requires`. For - // Cmd("foo"), the name is "cmd:foo"; for Wine, it's "wine". Anything else - // (Local, transport runners) returns empty — those don't trigger the - // declared/undeclared gate. - std::string RunnerToolName(const TestRunner& runner) { - if (runner.name == "wine") return "wine"; - if (runner.name.starts_with("cmd:")) { - std::string tool = runner.name.substr(4); - // QEMU_LD_PREFIX prefix may be glued onto exec but the runner's - // `name` field already isolates the command, so no extra parsing. - return tool; - } - return ""; - } -} - namespace { // Synthesize a Configuration for tests// folders that don't contain // a project.cpp. Convention: cfg.path = the folder, cfg.name/outputName = - // folder basename, cfg.target = host (overridable via test.toml `target`), - // cfg.type = exe. Sources: top-level *.cpp (excluding project.cpp) become - // implementations, interfaces/*.cppm become module interfaces. Tests with - // deeper layouts or dependencies still need an explicit project.cpp. - // - // Why host-default instead of targetFilter-default: under the multi-target - // sweep, an arch-agnostic test (no test.toml target) should run at the - // host iteration only — not get rebuilt against every cross-target the - // suite happens to declare. Cross-targeting is an opt-in via test.toml. - Configuration SynthesizeTest(const fs::path& dir) { + // folder basename, cfg.target = the run's targetFilter, cfg.type = exe. + // Sources: top-level *.cpp (excluding project.cpp) become implementations, + // interfaces/*.cppm become module interfaces (matching the layout used + // elsewhere in this codebase). Tests with deeper layouts, defines, or + // dependencies still need an explicit project.cpp. + Configuration SynthesizeTest(const fs::path& dir, std::string_view target) { Configuration cfg; cfg.path = dir; cfg.name = dir.filename().string(); cfg.outputName = cfg.name; - cfg.target = HostTarget(); + cfg.target = std::string(target); cfg.type = ConfigurationType::Executable; std::vector impls; @@ -580,61 +502,6 @@ namespace { } TestSummary Crafter::RunTests(Configuration& projectCfg, const RunTestsOptions& opts, std::span projectArgs) { - // Multi-target sweep: when no --target= was given, the run covers every - // distinct target a test.toml declares plus the host target. Lets a bare - // `crafter-build test` exercise cross-arch tests without the user having - // to know which targets exist in this project. An explicit --target=X - // bypasses the sweep and runs that target only. - if (opts.targetFilter.empty()) { - std::set sweep; - sweep.insert(HostTarget()); - fs::path testsDir = fs::current_path() / "tests"; - if (fs::exists(testsDir) && fs::is_directory(testsDir)) { - for (auto& e : fs::directory_iterator(testsDir)) { - if (!e.is_directory()) continue; - auto stem = e.path().filename().string(); - if (stem.empty() || stem[0] == '_' || stem[0] == '.') continue; - fs::path tomlPath = e.path() / "test.toml"; - if (!fs::exists(tomlPath)) continue; - try { - TestManifest m = ParseTestManifest(tomlPath); - if (m.target) sweep.insert(*m.target); - } catch (...) { - // Parse failures surface as discovery failures during the - // actual run; the sweep phase just collects targets. - } - } - } - TestSummary aggregate; - // Inline tests pushed by the caller (fixture-driven inner RunTests - // calls, e.g. RunnerClassification) must survive each sweep - // iteration. Configuration isn't copyable so we can't snapshot+restore; - // instead, remember the inline count and erase only the entries - // appended by the previous iteration's auto-discovery. - size_t inlineCount = projectCfg.tests.size(); - for (const auto& target : sweep) { - RunTestsOptions perTarget = opts; - perTarget.targetFilter = target; - if (projectCfg.tests.size() > inlineCount) { - projectCfg.tests.erase( - projectCfg.tests.begin() + inlineCount, - projectCfg.tests.end()); - } - if (sweep.size() > 1) { - Progress::Clear(); - std::println("\n=== target: {} ===", target); - } - TestSummary s = RunTests(projectCfg, perTarget, projectArgs); - aggregate.passed += s.passed; - aggregate.failed += s.failed; - aggregate.crashed += s.crashed; - aggregate.timedOut += s.timedOut; - aggregate.skipped += s.skipped; - for (auto& r : s.results) aggregate.results.push_back(std::move(r)); - } - return aggregate; - } - TestSummary summary; std::vector discoveryFailures; @@ -650,16 +517,7 @@ TestSummary Crafter::RunTests(Configuration& projectCfg, const RunTestsOptions& // when projectCfg.path points at a subdirectory like "./src/" or "./lib/". fs::path testsDir = fs::current_path() / "tests"; if (fs::exists(testsDir) && fs::is_directory(testsDir)) { - // A discovered fixture is one of: - // - project.cpp present → outer-driver test (LoadProject) - // - test.toml present → declarative synth + manifest - // - neither, just *.cpp → bare synth (host-target) - // - both project.cpp and test.toml → XOR violation, discovery Fail - struct TestEntry { - fs::path dir; - fs::path pcpp; // outer-driver path - std::optional manifest; - }; + struct TestEntry { fs::path dir; fs::path pcpp; }; // pcpp empty = synth std::vector entries; for (auto& entry : fs::directory_iterator(testsDir)) { if (!entry.is_directory()) continue; @@ -668,34 +526,7 @@ TestSummary Crafter::RunTests(Configuration& projectCfg, const RunTestsOptions& TestEntry te; te.dir = entry.path(); auto pcpp = te.dir / "project.cpp"; - auto tomlPath = te.dir / "test.toml"; - bool hasPcpp = fs::exists(pcpp); - bool hasToml = fs::exists(tomlPath); - if (hasPcpp && hasToml) { - TestResult r; - r.name = stem; - r.outcome = TestOutcome::Fail; - r.exitCode = -1; - r.output = "both project.cpp and test.toml present — they're " - "mutually exclusive (delete one to disambiguate " - "outer-driver vs declarative test)"; - discoveryFailures.push_back(std::move(r)); - continue; - } - if (hasPcpp) te.pcpp = pcpp; - if (hasToml) { - try { - te.manifest = ParseTestManifest(tomlPath); - } catch (const std::exception& e) { - TestResult r; - r.name = stem; - r.outcome = TestOutcome::Fail; - r.exitCode = -1; - r.output = e.what(); - discoveryFailures.push_back(std::move(r)); - continue; - } - } + if (fs::exists(pcpp)) te.pcpp = pcpp; entries.push_back(std::move(te)); } std::ranges::sort(entries, [](auto& a, auto& b) { return a.dir < b.dir; }); @@ -717,15 +548,7 @@ TestSummary Crafter::RunTests(Configuration& projectCfg, const RunTestsOptions& if (!te.pcpp.empty()) { t.config = LoadProject(te.pcpp, fixtureArgs); } else { - t.config = SynthesizeTest(te.dir); - if (te.manifest) { - ApplyManifest(t.config, *te.manifest); - if (te.manifest->timeoutSeconds) { - t.timeout = std::chrono::seconds(*te.manifest->timeoutSeconds); - } - t.args = te.manifest->args; - t.requires_ = te.manifest->requires_; - } + t.config = SynthesizeTest(te.dir, opts.targetFilter); } } catch (const std::exception& e) { // A broken fixture shouldn't kill the whole run. Surface as a @@ -741,7 +564,7 @@ TestSummary Crafter::RunTests(Configuration& projectCfg, const RunTestsOptions& continue; } if (t.config.target != opts.targetFilter) continue; - t.runner = TestRunner::FromEnv(t.config.target, TestRunner::ForTarget(t.config)); + t.runner = TestRunner::FromEnv(t.config.target, TestRunner::Local()); if (opts.runnerOverride) { if (auto r = TestRunner::FromSpec(*opts.runnerOverride)) { t.runner = std::move(*r); @@ -816,42 +639,9 @@ TestSummary Crafter::RunTests(Configuration& projectCfg, const RunTestsOptions& TestResult r; r.name = t.config.name; - // Declarative preconditions (test.toml requires = [...] or Test.requires_ - // set in project.cpp). Evaluated before the build so a missing tool/file/env - // turns into a Skip without paying the compile cost. Reports the first - // failure only — once one precondition is unmet the test couldn't run - // anyway, and a wall of "also missing X, also missing Y" buries the - // actionable root cause. - if (auto req = EvaluateRequires(t.requires_); !req.ok) { - r.outcome = TestOutcome::Skipped; - r.output = req.reason; - { - std::lock_guard lk(printMutex); - PrintResult(r, t.runner.name); - } - results[i] = std::move(r); - continue; - } - if (!runnerAvailable(t.runner)) { - // Hard-fail-unless-declared: if the runner depends on a tool - // (qemu-aarch64, wasmtime, wine, ...) and the test didn't say - // "tool:" in requires, the missing runner is a Fail. The - // intent is to surface broken cross-arch CI configuration - // instead of letting it masquerade as a Skip; tests that - // legitimately may run without their runner have to opt in. - std::string tool = RunnerToolName(t.runner); - if (!tool.empty() && !RequiresMentionsTool(t.requires_, tool)) { - r.outcome = TestOutcome::Fail; - r.exitCode = -1; - r.output = std::format( - "runner '{}' unavailable and not declared in requires " - "(add 'tool:{}' to test.toml requires to permit skipping)", - t.runner.name, tool); - } else { - r.outcome = TestOutcome::Skipped; - r.output = std::format("runner '{}' not available", t.runner.name); - } + r.outcome = TestOutcome::Skipped; + r.output = std::format("runner '{}' not available", t.runner.name); { std::lock_guard lk(printMutex); PrintResult(r, t.runner.name); diff --git a/interfaces/Crafter.Build-Clang.cppm b/interfaces/Crafter.Build-Clang.cppm index 45e8809..2fa60bd 100644 --- a/interfaces/Crafter.Build-Clang.cppm +++ b/interfaces/Crafter.Build-Clang.cppm @@ -53,43 +53,32 @@ export namespace Crafter { struct Test; struct TestRunner { - // Command template the harness executes to run the test binary. - // Local runners leave this empty; prefix runners (Cmd, Wine) set it to - // a template like "wine {bin} {args}" or "qemu-aarch64 {bin} {args}". + // Quoting style for {args} when this runner is used. Args reach + // whichever shell the runner ultimately delivers them to: Host for + // Local + Cmd-prefix (host shell parses), Sh for Ssh (remote bash), + // Cmd for SshWin (remote cmd.exe). + enum class Shell { Host, Sh, Cmd }; + + std::string copy; std::string exec; - // Display name; also the cache key for the availability probe. + std::string cleanup; + std::string remoteDir; std::string name; // Runs once per RunTests invocation (cached by `name`). Exit 0 = runner // is available; non-zero = skip every Test using this runner with a // "runner unavailable" message. Empty = always available (e.g., Local). std::string probe; + Shell argsShell = Shell::Host; bool IsLocal() const { return exec.empty(); } static CRAFTER_API TestRunner Local(); - // Prefix runner: wraps the local binary in ` {bin} {args}`. - // Used for qemu-user, wasmtime, and similar single-binary wrappers. + static CRAFTER_API TestRunner Ssh(std::string host, std::string remoteDir = "/tmp/crafter-tests"); + static CRAFTER_API TestRunner SshWin(std::string host, std::string remoteDir = "C:/temp/crafter-tests"); + static CRAFTER_API TestRunner Wsl(std::string remoteDir = "/tmp/crafter-tests-wsl"); static CRAFTER_API TestRunner Cmd(std::string command); - // Run a Windows .exe through Wine. Probes `wine` on PATH; on a Windows - // host the wine wrapper is pointless, so callers should route to Local - // before reaching here. - static CRAFTER_API TestRunner Wine(); - // Parse a `[:]` spec used by CRAFTER_BUILD_RUNNER_ - // and --runner=. Supported: "local", "cmd:". Returns nullopt - // for an empty string; throws on a non-empty unrecognized spec. static CRAFTER_API std::optional FromSpec(std::string_view spec); - // Honor CRAFTER_BUILD_RUNNER_ (power-user override). Triple - // dashes/dots become underscores so they're valid in env-var names. - // Returns `fallback` when the env var is unset. static CRAFTER_API TestRunner FromEnv(std::string_view target, TestRunner fallback = Local()); - // Derive a runner from a Configuration's target triple + sysroot. - // Returns Local() when target equals the host, Wine() for Windows - // targets on a non-Windows host, `qemu-` (with QEMU_LD_PREFIX - // set when cfg.sysroot is non-empty) for non-host -linux- triples, - // `wasmtime` for wasm32-wasi/wasm64-wasi, and Local() as a last - // resort. CRAFTER_BUILD_RUNNER_ still wins as an override - // upstream of this — see FromEnv. - static CRAFTER_API TestRunner ForTarget(const struct Configuration& cfg); }; enum class TestOutcome { Pass, Fail, Crash, Timeout, Skipped }; @@ -196,15 +185,6 @@ export namespace Crafter { TestRunner runner; std::chrono::seconds timeout{60}; std::vector args; - // Declarative preconditions. Each entry is "tool:", - // "file:", or "env:". Evaluated before the test runs; any - // unmet require turns the test into a Skip with a derived reason. - // Also doubles as the "I know this runner might not be here" opt-in: - // when the test's derived runner needs a tool (e.g. qemu-aarch64, - // wasmtime, wine) and the matching tool: entry isn't present, an - // unavailable runner becomes a Fail instead of a silent Skip — the - // dependency has to be declared to be allowed to be missing. - std::vector requires_; }; CRAFTER_API BuildResult Build(Configuration& config, std::unordered_map>& depResults, std::mutex& depMutex); diff --git a/interfaces/Crafter.Build-Test.cppm b/interfaces/Crafter.Build-Test.cppm index b2a340c..5e630ba 100644 --- a/interfaces/Crafter.Build-Test.cppm +++ b/interfaces/Crafter.Build-Test.cppm @@ -29,13 +29,15 @@ export namespace Crafter { int jobs = 0; std::optional timeoutOverride; bool listOnly = false; - // Single-target run: only tests whose Configuration::target matches - // are included. Empty (default) = run every distinct target declared - // across discovered tests, plus the host target. Set from --target=... - // (when omitted, the harness sweeps all declared targets so cross-arch - // tests run by default without the user having to know which targets - // exist). - std::string targetFilter; + // Only tests whose Configuration::target equals targetFilter are run. + // Set from --target=... (host triple if unspecified). Tests for other + // targets are silently excluded so e.g. `--target=mingw` doesn't drag + // in host-only outer-driver tests. +#ifdef _WIN32 + std::string targetFilter = "x86_64-pc-windows-msvc"; +#else + std::string targetFilter = "x86_64-pc-linux-gnu"; +#endif // CLI override for --runner=: applies to every test in the run. // Target scoping is unnecessary because targetFilter ensures the run // contains only one target's tests. diff --git a/lib/toml.hpp b/lib/toml.hpp deleted file mode 100644 index caf87c4..0000000 --- a/lib/toml.hpp +++ /dev/null @@ -1,17899 +0,0 @@ -//---------------------------------------------------------------------------------------------------------------------- -// -// toml++ v3.4.0 -// https://github.com/marzer/tomlplusplus -// SPDX-License-Identifier: MIT -// -//---------------------------------------------------------------------------------------------------------------------- -// -// - THIS FILE WAS ASSEMBLED FROM MULTIPLE HEADER FILES BY A SCRIPT - PLEASE DON'T EDIT IT DIRECTLY - -// -// If you wish to submit a contribution to toml++, hooray and thanks! Before you crack on, please be aware that this -// file was assembled from a number of smaller files by a python script, and code contributions should not be made -// against it directly. You should instead make your changes in the relevant source file(s). The file names of the files -// that contributed to this header can be found at the beginnings and ends of the corresponding sections of this file. -// -//---------------------------------------------------------------------------------------------------------------------- -// -// TOML Language Specifications: -// latest: https://github.com/toml-lang/toml/blob/master/README.md -// v1.0.0: https://toml.io/en/v1.0.0 -// v0.5.0: https://toml.io/en/v0.5.0 -// changelog: https://github.com/toml-lang/toml/blob/master/CHANGELOG.md -// -//---------------------------------------------------------------------------------------------------------------------- -// -// MIT License -// -// Copyright (c) Mark Gillard -// -// 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 TOMLPLUSPLUS_HPP -#define TOMLPLUSPLUS_HPP - -#define INCLUDE_TOMLPLUSPLUS_H // old guard name used pre-v3 -#define TOMLPLUSPLUS_H // guard name used in the legacy toml.h - -//******** impl/preprocessor.hpp ************************************************************************************* - -#ifndef __cplusplus -#error toml++ is a C++ library. -#endif - -#ifndef TOML_CPP -#ifdef _MSVC_LANG -#if _MSVC_LANG > __cplusplus -#define TOML_CPP _MSVC_LANG -#endif -#endif -#ifndef TOML_CPP -#define TOML_CPP __cplusplus -#endif -#if TOML_CPP >= 202900L -#undef TOML_CPP -#define TOML_CPP 29 -#elif TOML_CPP >= 202600L -#undef TOML_CPP -#define TOML_CPP 26 -#elif TOML_CPP >= 202302L -#undef TOML_CPP -#define TOML_CPP 23 -#elif TOML_CPP >= 202002L -#undef TOML_CPP -#define TOML_CPP 20 -#elif TOML_CPP >= 201703L -#undef TOML_CPP -#define TOML_CPP 17 -#elif TOML_CPP >= 201402L -#undef TOML_CPP -#define TOML_CPP 14 -#elif TOML_CPP >= 201103L -#undef TOML_CPP -#define TOML_CPP 11 -#else -#undef TOML_CPP -#define TOML_CPP 0 -#endif -#endif - -#if !TOML_CPP -#error toml++ requires C++17 or higher. For a pre-C++11 TOML library see https://github.com/ToruNiina/Boost.toml -#elif TOML_CPP < 17 -#error toml++ requires C++17 or higher. For a C++11 TOML library see https://github.com/ToruNiina/toml11 -#endif - -#ifndef TOML_MAKE_VERSION -#define TOML_MAKE_VERSION(major, minor, patch) (((major)*10000) + ((minor)*100) + ((patch))) -#endif - -#ifndef TOML_INTELLISENSE -#ifdef __INTELLISENSE__ -#define TOML_INTELLISENSE 1 -#else -#define TOML_INTELLISENSE 0 -#endif -#endif - -#ifndef TOML_DOXYGEN -#if defined(DOXYGEN) || defined(__DOXYGEN) || defined(__DOXYGEN__) || defined(__doxygen__) || defined(__POXY__) \ - || defined(__poxy__) -#define TOML_DOXYGEN 1 -#else -#define TOML_DOXYGEN 0 -#endif -#endif - -#ifndef TOML_CLANG -#ifdef __clang__ -#define TOML_CLANG __clang_major__ -#else -#define TOML_CLANG 0 -#endif - -// special handling for apple clang; see: -// - https://github.com/marzer/tomlplusplus/issues/189 -// - https://en.wikipedia.org/wiki/Xcode -// - -// https://stackoverflow.com/questions/19387043/how-can-i-reliably-detect-the-version-of-clang-at-preprocessing-time -#if TOML_CLANG && defined(__apple_build_version__) -#undef TOML_CLANG -#define TOML_CLANG_VERSION TOML_MAKE_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) -#if TOML_CLANG_VERSION >= TOML_MAKE_VERSION(15, 0, 0) -#define TOML_CLANG 16 -#elif TOML_CLANG_VERSION >= TOML_MAKE_VERSION(14, 3, 0) -#define TOML_CLANG 15 -#elif TOML_CLANG_VERSION >= TOML_MAKE_VERSION(14, 0, 0) -#define TOML_CLANG 14 -#elif TOML_CLANG_VERSION >= TOML_MAKE_VERSION(13, 1, 6) -#define TOML_CLANG 13 -#elif TOML_CLANG_VERSION >= TOML_MAKE_VERSION(13, 0, 0) -#define TOML_CLANG 12 -#elif TOML_CLANG_VERSION >= TOML_MAKE_VERSION(12, 0, 5) -#define TOML_CLANG 11 -#elif TOML_CLANG_VERSION >= TOML_MAKE_VERSION(12, 0, 0) -#define TOML_CLANG 10 -#elif TOML_CLANG_VERSION >= TOML_MAKE_VERSION(11, 0, 3) -#define TOML_CLANG 9 -#elif TOML_CLANG_VERSION >= TOML_MAKE_VERSION(11, 0, 0) -#define TOML_CLANG 8 -#elif TOML_CLANG_VERSION >= TOML_MAKE_VERSION(10, 0, 1) -#define TOML_CLANG 7 -#else -#define TOML_CLANG 6 // not strictly correct but doesn't matter below this -#endif -#undef TOML_CLANG_VERSION -#endif -#endif - -#ifndef TOML_ICC -#ifdef __INTEL_COMPILER -#define TOML_ICC __INTEL_COMPILER -#ifdef __ICL -#define TOML_ICC_CL TOML_ICC -#else -#define TOML_ICC_CL 0 -#endif -#else -#define TOML_ICC 0 -#define TOML_ICC_CL 0 -#endif -#endif - -#ifndef TOML_MSVC_LIKE -#ifdef _MSC_VER -#define TOML_MSVC_LIKE _MSC_VER -#else -#define TOML_MSVC_LIKE 0 -#endif -#endif - -#ifndef TOML_MSVC -#if TOML_MSVC_LIKE && !TOML_CLANG && !TOML_ICC -#define TOML_MSVC TOML_MSVC_LIKE -#else -#define TOML_MSVC 0 -#endif -#endif - -#ifndef TOML_GCC_LIKE -#ifdef __GNUC__ -#define TOML_GCC_LIKE __GNUC__ -#else -#define TOML_GCC_LIKE 0 -#endif -#endif - -#ifndef TOML_GCC -#if TOML_GCC_LIKE && !TOML_CLANG && !TOML_ICC -#define TOML_GCC TOML_GCC_LIKE -#else -#define TOML_GCC 0 -#endif -#endif - -#ifndef TOML_CUDA -#if defined(__CUDACC__) || defined(__CUDA_ARCH__) || defined(__CUDA_LIBDEVICE__) -#define TOML_CUDA 1 -#else -#define TOML_CUDA 0 -#endif -#endif - -#ifndef TOML_NVCC -#ifdef __NVCOMPILER_MAJOR__ -#define TOML_NVCC __NVCOMPILER_MAJOR__ -#else -#define TOML_NVCC 0 -#endif -#endif - -#ifndef TOML_ARCH_ITANIUM -#if defined(__ia64__) || defined(__ia64) || defined(_IA64) || defined(__IA64__) || defined(_M_IA64) -#define TOML_ARCH_ITANIUM 1 -#define TOML_ARCH_BITNESS 64 -#else -#define TOML_ARCH_ITANIUM 0 -#endif -#endif - -#ifndef TOML_ARCH_AMD64 -#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_AMD64) -#define TOML_ARCH_AMD64 1 -#define TOML_ARCH_BITNESS 64 -#else -#define TOML_ARCH_AMD64 0 -#endif -#endif - -#ifndef TOML_ARCH_X86 -#if defined(__i386__) || defined(_M_IX86) -#define TOML_ARCH_X86 1 -#define TOML_ARCH_BITNESS 32 -#else -#define TOML_ARCH_X86 0 -#endif -#endif - -#ifndef TOML_ARCH_ARM -#if defined(__aarch64__) || defined(__ARM_ARCH_ISA_A64) || defined(_M_ARM64) || defined(__ARM_64BIT_STATE) \ - || defined(_M_ARM64EC) -#define TOML_ARCH_ARM32 0 -#define TOML_ARCH_ARM64 1 -#define TOML_ARCH_ARM 1 -#define TOML_ARCH_BITNESS 64 -#elif defined(__arm__) || defined(_M_ARM) || defined(__ARM_32BIT_STATE) -#define TOML_ARCH_ARM32 1 -#define TOML_ARCH_ARM64 0 -#define TOML_ARCH_ARM 1 -#define TOML_ARCH_BITNESS 32 -#else -#define TOML_ARCH_ARM32 0 -#define TOML_ARCH_ARM64 0 -#define TOML_ARCH_ARM 0 -#endif -#endif - -#ifndef TOML_ARCH_BITNESS -#define TOML_ARCH_BITNESS 0 -#endif - -#ifndef TOML_ARCH_X64 -#if TOML_ARCH_BITNESS == 64 -#define TOML_ARCH_X64 1 -#else -#define TOML_ARCH_X64 0 -#endif -#endif - -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || defined(__CYGWIN__) -#define TOML_WINDOWS 1 -#else -#define TOML_WINDOWS 0 -#endif - -#ifdef __unix__ -#define TOML_UNIX 1 -#else -#define TOML_UNIX 0 -#endif - -#ifdef __linux__ -#define TOML_LINUX 1 -#else -#define TOML_LINUX 0 -#endif - -// TOML_HAS_INCLUDE -#ifndef TOML_HAS_INCLUDE -#ifdef __has_include -#define TOML_HAS_INCLUDE(header) __has_include(header) -#else -#define TOML_HAS_INCLUDE(header) 0 -#endif -#endif - -// TOML_HAS_BUILTIN -#ifndef TOML_HAS_BUILTIN -#ifdef __has_builtin -#define TOML_HAS_BUILTIN(name) __has_builtin(name) -#else -#define TOML_HAS_BUILTIN(name) 0 -#endif -#endif - -// TOML_HAS_FEATURE -#ifndef TOML_HAS_FEATURE -#ifdef __has_feature -#define TOML_HAS_FEATURE(name) __has_feature(name) -#else -#define TOML_HAS_FEATURE(name) 0 -#endif -#endif - -// TOML_HAS_ATTR -#ifndef TOML_HAS_ATTR -#ifdef __has_attribute -#define TOML_HAS_ATTR(attr) __has_attribute(attr) -#else -#define TOML_HAS_ATTR(attr) 0 -#endif -#endif - -// TOML_HAS_CPP_ATTR -#ifndef TOML_HAS_CPP_ATTR -#ifdef __has_cpp_attribute -#define TOML_HAS_CPP_ATTR(attr) __has_cpp_attribute(attr) -#else -#define TOML_HAS_CPP_ATTR(attr) 0 -#endif -#endif - -// TOML_ATTR (gnu attributes) -#ifndef TOML_ATTR -#if TOML_CLANG || TOML_GCC_LIKE -#define TOML_ATTR(...) __attribute__((__VA_ARGS__)) -#else -#define TOML_ATTR(...) -#endif -#endif - -// TOML_DECLSPEC (msvc attributes) -#ifndef TOML_DECLSPEC -#if TOML_MSVC_LIKE -#define TOML_DECLSPEC(...) __declspec(__VA_ARGS__) -#else -#define TOML_DECLSPEC(...) -#endif -#endif - -// TOML_COMPILER_HAS_EXCEPTIONS -#ifndef TOML_COMPILER_HAS_EXCEPTIONS -#if defined(__EXCEPTIONS) || defined(_CPPUNWIND) || defined(__cpp_exceptions) -#define TOML_COMPILER_HAS_EXCEPTIONS 1 -#else -#define TOML_COMPILER_HAS_EXCEPTIONS 0 -#endif -#endif - -// TOML_COMPILER_HAS_RTTI -#ifndef TOML_COMPILER_HAS_RTTI -#if defined(_CPPRTTI) || defined(__GXX_RTTI) || TOML_HAS_FEATURE(cxx_rtti) -#define TOML_COMPILER_HAS_RTTI 1 -#else -#define TOML_COMPILER_HAS_RTTI 0 -#endif -#endif - -// TOML_CONCAT -#define TOML_CONCAT_1(x, y) x##y -#define TOML_CONCAT(x, y) TOML_CONCAT_1(x, y) - -// TOML_MAKE_STRING -#define TOML_MAKE_STRING_1(s) #s -#define TOML_MAKE_STRING(s) TOML_MAKE_STRING_1(s) - -// TOML_PRAGMA_XXXX (compiler-specific pragmas) -#if TOML_CLANG -#define TOML_PRAGMA_CLANG(decl) _Pragma(TOML_MAKE_STRING(clang decl)) -#else -#define TOML_PRAGMA_CLANG(decl) -#endif -#if TOML_CLANG >= 8 -#define TOML_PRAGMA_CLANG_GE_8(decl) TOML_PRAGMA_CLANG(decl) -#else -#define TOML_PRAGMA_CLANG_GE_8(decl) -#endif -#if TOML_CLANG >= 9 -#define TOML_PRAGMA_CLANG_GE_9(decl) TOML_PRAGMA_CLANG(decl) -#else -#define TOML_PRAGMA_CLANG_GE_9(decl) -#endif -#if TOML_CLANG >= 10 -#define TOML_PRAGMA_CLANG_GE_10(decl) TOML_PRAGMA_CLANG(decl) -#else -#define TOML_PRAGMA_CLANG_GE_10(decl) -#endif -#if TOML_CLANG >= 11 -#define TOML_PRAGMA_CLANG_GE_11(decl) TOML_PRAGMA_CLANG(decl) -#else -#define TOML_PRAGMA_CLANG_GE_11(decl) -#endif -#if TOML_GCC -#define TOML_PRAGMA_GCC(decl) _Pragma(TOML_MAKE_STRING(GCC decl)) -#else -#define TOML_PRAGMA_GCC(decl) -#endif -#if TOML_MSVC -#define TOML_PRAGMA_MSVC(...) __pragma(__VA_ARGS__) -#else -#define TOML_PRAGMA_MSVC(...) -#endif -#if TOML_ICC -#define TOML_PRAGMA_ICC(...) __pragma(__VA_ARGS__) -#else -#define TOML_PRAGMA_ICC(...) -#endif - -// TOML_ALWAYS_INLINE -#ifndef TOML_ALWAYS_INLINE -#ifdef _MSC_VER -#define TOML_ALWAYS_INLINE __forceinline -#elif TOML_GCC || TOML_CLANG || TOML_HAS_ATTR(__always_inline__) -#define TOML_ALWAYS_INLINE \ - TOML_ATTR(__always_inline__) \ - inline -#else -#define TOML_ALWAYS_INLINE inline -#endif -#endif - -// TOML_NEVER_INLINE -#ifndef TOML_NEVER_INLINE -#ifdef _MSC_VER -#define TOML_NEVER_INLINE TOML_DECLSPEC(noinline) -#elif TOML_CUDA // https://gitlab.gnome.org/GNOME/glib/-/issues/2555 -#define TOML_NEVER_INLINE TOML_ATTR(noinline) -#else -#if TOML_GCC || TOML_CLANG || TOML_HAS_ATTR(__noinline__) -#define TOML_NEVER_INLINE TOML_ATTR(__noinline__) -#endif -#endif -#ifndef TOML_NEVER_INLINE -#define TOML_NEVER_INLINE -#endif -#endif - -// MSVC attributes -#ifndef TOML_ABSTRACT_INTERFACE -#define TOML_ABSTRACT_INTERFACE TOML_DECLSPEC(novtable) -#endif -#ifndef TOML_EMPTY_BASES -#define TOML_EMPTY_BASES TOML_DECLSPEC(empty_bases) -#endif - -// TOML_TRIVIAL_ABI -#ifndef TOML_TRIVIAL_ABI -#if TOML_CLANG || TOML_HAS_ATTR(__trivial_abi__) -#define TOML_TRIVIAL_ABI TOML_ATTR(__trivial_abi__) -#else -#define TOML_TRIVIAL_ABI -#endif -#endif - -// TOML_NODISCARD -#ifndef TOML_NODISCARD -#if TOML_CPP >= 17 && TOML_HAS_CPP_ATTR(nodiscard) >= 201603 -#define TOML_NODISCARD [[nodiscard]] -#elif TOML_CLANG || TOML_GCC || TOML_HAS_ATTR(__warn_unused_result__) -#define TOML_NODISCARD TOML_ATTR(__warn_unused_result__) -#else -#define TOML_NODISCARD -#endif -#endif - -// TOML_NODISCARD_CTOR -#ifndef TOML_NODISCARD_CTOR -#if TOML_CPP >= 17 && TOML_HAS_CPP_ATTR(nodiscard) >= 201907 -#define TOML_NODISCARD_CTOR [[nodiscard]] -#else -#define TOML_NODISCARD_CTOR -#endif -#endif - -// pure + const -#ifndef TOML_PURE -#ifdef NDEBUG -#define TOML_PURE \ - TOML_DECLSPEC(noalias) \ - TOML_ATTR(pure) -#else -#define TOML_PURE -#endif -#endif -#ifndef TOML_CONST -#ifdef NDEBUG -#define TOML_CONST \ - TOML_DECLSPEC(noalias) \ - TOML_ATTR(const) -#else -#define TOML_CONST -#endif -#endif -#ifndef TOML_INLINE_GETTER -#define TOML_INLINE_GETTER \ - TOML_NODISCARD \ - TOML_ALWAYS_INLINE -#endif -#ifndef TOML_PURE_GETTER -#define TOML_PURE_GETTER \ - TOML_NODISCARD \ - TOML_PURE -#endif -#ifndef TOML_PURE_INLINE_GETTER -#define TOML_PURE_INLINE_GETTER \ - TOML_NODISCARD \ - TOML_ALWAYS_INLINE \ - TOML_PURE -#endif -#ifndef TOML_CONST_GETTER -#define TOML_CONST_GETTER \ - TOML_NODISCARD \ - TOML_CONST -#endif -#ifndef TOML_CONST_INLINE_GETTER -#define TOML_CONST_INLINE_GETTER \ - TOML_NODISCARD \ - TOML_ALWAYS_INLINE \ - TOML_CONST -#endif - -// TOML_ASSUME -#ifndef TOML_ASSUME -#ifdef _MSC_VER -#define TOML_ASSUME(expr) __assume(expr) -#elif TOML_ICC || TOML_CLANG || TOML_HAS_BUILTIN(__builtin_assume) -#define TOML_ASSUME(expr) __builtin_assume(expr) -#elif TOML_HAS_CPP_ATTR(assume) >= 202207 -#define TOML_ASSUME(expr) [[assume(expr)]] -#elif TOML_HAS_ATTR(__assume__) -#define TOML_ASSUME(expr) __attribute__((__assume__(expr))) -#else -#define TOML_ASSUME(expr) static_cast(0) -#endif -#endif - -// TOML_UNREACHABLE -#ifndef TOML_UNREACHABLE -#ifdef _MSC_VER -#define TOML_UNREACHABLE __assume(0) -#elif TOML_ICC || TOML_CLANG || TOML_GCC || TOML_HAS_BUILTIN(__builtin_unreachable) -#define TOML_UNREACHABLE __builtin_unreachable() -#else -#define TOML_UNREACHABLE static_cast(0) -#endif -#endif - -// TOML_LIKELY -#if TOML_CPP >= 20 && TOML_HAS_CPP_ATTR(likely) >= 201803 -#define TOML_LIKELY(...) (__VA_ARGS__) [[likely]] -#define TOML_LIKELY_CASE [[likely]] -#elif TOML_GCC || TOML_CLANG || TOML_HAS_BUILTIN(__builtin_expect) -#define TOML_LIKELY(...) (__builtin_expect(!!(__VA_ARGS__), 1)) -#else -#define TOML_LIKELY(...) (__VA_ARGS__) -#endif -#ifndef TOML_LIKELY_CASE -#define TOML_LIKELY_CASE -#endif - -// TOML_UNLIKELY -#if TOML_CPP >= 20 && TOML_HAS_CPP_ATTR(unlikely) >= 201803 -#define TOML_UNLIKELY(...) (__VA_ARGS__) [[unlikely]] -#define TOML_UNLIKELY_CASE [[unlikely]] -#elif TOML_GCC || TOML_CLANG || TOML_HAS_BUILTIN(__builtin_expect) -#define TOML_UNLIKELY(...) (__builtin_expect(!!(__VA_ARGS__), 0)) -#else -#define TOML_UNLIKELY(...) (__VA_ARGS__) -#endif -#ifndef TOML_UNLIKELY_CASE -#define TOML_UNLIKELY_CASE -#endif - -// TOML_FLAGS_ENUM -#if TOML_CLANG || TOML_HAS_ATTR(flag_enum) -#define TOML_FLAGS_ENUM __attribute__((flag_enum)) -#else -#define TOML_FLAGS_ENUM -#endif - -// TOML_OPEN_ENUM + TOML_CLOSED_ENUM -#if TOML_CLANG || TOML_HAS_ATTR(enum_extensibility) -#define TOML_OPEN_ENUM __attribute__((enum_extensibility(open))) -#define TOML_CLOSED_ENUM __attribute__((enum_extensibility(closed))) -#else -#define TOML_OPEN_ENUM -#define TOML_CLOSED_ENUM -#endif - -// TOML_OPEN_FLAGS_ENUM + TOML_CLOSED_FLAGS_ENUM -#define TOML_OPEN_FLAGS_ENUM TOML_OPEN_ENUM TOML_FLAGS_ENUM -#define TOML_CLOSED_FLAGS_ENUM TOML_CLOSED_ENUM TOML_FLAGS_ENUM - -// TOML_MAKE_FLAGS -#define TOML_MAKE_FLAGS_2(T, op, linkage) \ - TOML_CONST_INLINE_GETTER \ - linkage constexpr T operator op(T lhs, T rhs) noexcept \ - { \ - using under = std::underlying_type_t; \ - return static_cast(static_cast(lhs) op static_cast(rhs)); \ - } \ - \ - linkage constexpr T& operator TOML_CONCAT(op, =)(T & lhs, T rhs) noexcept \ - { \ - return lhs = (lhs op rhs); \ - } \ - \ - static_assert(true) -#define TOML_MAKE_FLAGS_1(T, linkage) \ - static_assert(std::is_enum_v); \ - \ - TOML_MAKE_FLAGS_2(T, &, linkage); \ - TOML_MAKE_FLAGS_2(T, |, linkage); \ - TOML_MAKE_FLAGS_2(T, ^, linkage); \ - \ - TOML_CONST_INLINE_GETTER \ - linkage constexpr T operator~(T val) noexcept \ - { \ - using under = std::underlying_type_t; \ - return static_cast(~static_cast(val)); \ - } \ - \ - TOML_CONST_INLINE_GETTER \ - linkage constexpr bool operator!(T val) noexcept \ - { \ - using under = std::underlying_type_t; \ - return !static_cast(val); \ - } \ - \ - static_assert(true) -#define TOML_MAKE_FLAGS(T) TOML_MAKE_FLAGS_1(T, ) - -#define TOML_UNUSED(...) static_cast(__VA_ARGS__) - -#define TOML_DELETE_DEFAULTS(T) \ - T(const T&) = delete; \ - T(T&&) = delete; \ - T& operator=(const T&) = delete; \ - T& operator=(T&&) = delete - -#define TOML_ASYMMETRICAL_EQUALITY_OPS(LHS, RHS, ...) \ - __VA_ARGS__ TOML_NODISCARD \ - friend bool operator==(RHS rhs, LHS lhs) noexcept \ - { \ - return lhs == rhs; \ - } \ - __VA_ARGS__ TOML_NODISCARD \ - friend bool operator!=(LHS lhs, RHS rhs) noexcept \ - { \ - return !(lhs == rhs); \ - } \ - __VA_ARGS__ TOML_NODISCARD \ - friend bool operator!=(RHS rhs, LHS lhs) noexcept \ - { \ - return !(lhs == rhs); \ - } \ - static_assert(true) - -#define TOML_EVAL_BOOL_1(T, F) T -#define TOML_EVAL_BOOL_0(T, F) F - -#if !defined(__POXY__) && !defined(POXY_IMPLEMENTATION_DETAIL) -#define POXY_IMPLEMENTATION_DETAIL(...) __VA_ARGS__ -#endif - -// COMPILER-SPECIFIC WARNING MANAGEMENT - -#if TOML_CLANG - -#define TOML_PUSH_WARNINGS \ - TOML_PRAGMA_CLANG(diagnostic push) \ - TOML_PRAGMA_CLANG(diagnostic ignored "-Wunknown-warning-option") \ - static_assert(true) - -#define TOML_DISABLE_SWITCH_WARNINGS \ - TOML_PRAGMA_CLANG(diagnostic ignored "-Wswitch") \ - static_assert(true) - -#define TOML_DISABLE_ARITHMETIC_WARNINGS \ - TOML_PRAGMA_CLANG_GE_10(diagnostic ignored "-Wimplicit-int-float-conversion") \ - TOML_PRAGMA_CLANG(diagnostic ignored "-Wfloat-equal") \ - TOML_PRAGMA_CLANG(diagnostic ignored "-Wdouble-promotion") \ - TOML_PRAGMA_CLANG(diagnostic ignored "-Wchar-subscripts") \ - TOML_PRAGMA_CLANG(diagnostic ignored "-Wshift-sign-overflow") \ - static_assert(true) - -#define TOML_DISABLE_SPAM_WARNINGS \ - TOML_PRAGMA_CLANG_GE_8(diagnostic ignored "-Wdefaulted-function-deleted") \ - TOML_PRAGMA_CLANG_GE_9(diagnostic ignored "-Wctad-maybe-unsupported") \ - TOML_PRAGMA_CLANG_GE_10(diagnostic ignored "-Wzero-as-null-pointer-constant") \ - TOML_PRAGMA_CLANG_GE_11(diagnostic ignored "-Wsuggest-destructor-override") \ - TOML_PRAGMA_CLANG(diagnostic ignored "-Wweak-vtables") \ - TOML_PRAGMA_CLANG(diagnostic ignored "-Wweak-template-vtables") \ - TOML_PRAGMA_CLANG(diagnostic ignored "-Wdouble-promotion") \ - TOML_PRAGMA_CLANG(diagnostic ignored "-Wchar-subscripts") \ - TOML_PRAGMA_CLANG(diagnostic ignored "-Wmissing-field-initializers") \ - TOML_PRAGMA_CLANG(diagnostic ignored "-Wpadded") \ - static_assert(true) - -#define TOML_POP_WARNINGS \ - TOML_PRAGMA_CLANG(diagnostic pop) \ - static_assert(true) - -#define TOML_DISABLE_WARNINGS \ - TOML_PRAGMA_CLANG(diagnostic push) \ - TOML_PRAGMA_CLANG(diagnostic ignored "-Weverything") \ - static_assert(true, "") - -#define TOML_ENABLE_WARNINGS \ - TOML_PRAGMA_CLANG(diagnostic pop) \ - static_assert(true) - -#define TOML_SIMPLE_STATIC_ASSERT_MESSAGES 1 - -#elif TOML_MSVC - -#define TOML_PUSH_WARNINGS \ - __pragma(warning(push)) \ - static_assert(true) - -#if TOML_HAS_INCLUDE() -#pragma warning(push, 0) -#include -#pragma warning(pop) -#define TOML_DISABLE_CODE_ANALYSIS_WARNINGS \ - __pragma(warning(disable : ALL_CODE_ANALYSIS_WARNINGS)) \ - static_assert(true) -#else -#define TOML_DISABLE_CODE_ANALYSIS_WARNINGS static_assert(true) -#endif - -#define TOML_DISABLE_SWITCH_WARNINGS \ - __pragma(warning(disable : 4061)) \ - __pragma(warning(disable : 4062)) \ - __pragma(warning(disable : 4063)) \ - __pragma(warning(disable : 5262)) /* switch-case implicit fallthrough (false-positive) */ \ - __pragma(warning(disable : 26819)) /* cg: unannotated fallthrough */ \ - static_assert(true) - -#define TOML_DISABLE_SPAM_WARNINGS \ - __pragma(warning(disable : 4127)) /* conditional expr is constant */ \ - __pragma(warning(disable : 4324)) /* structure was padded due to alignment specifier */ \ - __pragma(warning(disable : 4348)) \ - __pragma(warning(disable : 4464)) /* relative include path contains '..' */ \ - __pragma(warning(disable : 4505)) /* unreferenced local function removed */ \ - __pragma(warning(disable : 4514)) /* unreferenced inline function has been removed */ \ - __pragma(warning(disable : 4582)) /* constructor is not implicitly called */ \ - __pragma(warning(disable : 4619)) /* there is no warning number 'XXXX' */ \ - __pragma(warning(disable : 4623)) /* default constructor was implicitly defined as deleted */ \ - __pragma(warning(disable : 4625)) /* copy constructor was implicitly defined as deleted */ \ - __pragma(warning(disable : 4626)) /* assignment operator was implicitly defined as deleted */ \ - __pragma(warning(disable : 4710)) /* function not inlined */ \ - __pragma(warning(disable : 4711)) /* function selected for automatic expansion */ \ - __pragma(warning(disable : 4820)) /* N bytes padding added */ \ - __pragma(warning(disable : 5026)) /* move constructor was implicitly defined as deleted */ \ - __pragma(warning(disable : 5027)) /* move assignment operator was implicitly defined as deleted */ \ - __pragma(warning(disable : 5039)) /* potentially throwing function passed to 'extern "C"' function */ \ - __pragma(warning(disable : 5045)) /* Compiler will insert Spectre mitigation */ \ - __pragma(warning(disable : 5264)) /* const variable is not used (false-positive) */ \ - __pragma(warning(disable : 26451)) \ - __pragma(warning(disable : 26490)) \ - __pragma(warning(disable : 26495)) \ - __pragma(warning(disable : 26812)) \ - __pragma(warning(disable : 26819)) \ - static_assert(true) - -#define TOML_DISABLE_ARITHMETIC_WARNINGS \ - __pragma(warning(disable : 4365)) /* argument signed/unsigned mismatch */ \ - __pragma(warning(disable : 4738)) /* storing 32-bit float result in memory */ \ - __pragma(warning(disable : 5219)) /* implicit conversion from integral to float */ \ - static_assert(true) - -#define TOML_POP_WARNINGS \ - __pragma(warning(pop)) \ - static_assert(true) - -#define TOML_DISABLE_WARNINGS \ - __pragma(warning(push, 0)) \ - __pragma(warning(disable : 4348)) \ - __pragma(warning(disable : 4668)) \ - __pragma(warning(disable : 5105)) \ - __pragma(warning(disable : 5264)) \ - TOML_DISABLE_CODE_ANALYSIS_WARNINGS; \ - TOML_DISABLE_SWITCH_WARNINGS; \ - TOML_DISABLE_SPAM_WARNINGS; \ - TOML_DISABLE_ARITHMETIC_WARNINGS; \ - static_assert(true) - -#define TOML_ENABLE_WARNINGS TOML_POP_WARNINGS - -#elif TOML_ICC - -#define TOML_PUSH_WARNINGS \ - __pragma(warning(push)) \ - static_assert(true) - -#define TOML_DISABLE_SPAM_WARNINGS \ - __pragma(warning(disable : 82)) /* storage class is not first */ \ - __pragma(warning(disable : 111)) /* statement unreachable (false-positive) */ \ - __pragma(warning(disable : 869)) /* unreferenced parameter */ \ - __pragma(warning(disable : 1011)) /* missing return (false-positive) */ \ - __pragma(warning(disable : 2261)) /* assume expr side-effects discarded */ \ - static_assert(true) - -#define TOML_POP_WARNINGS \ - __pragma(warning(pop)) \ - static_assert(true) - -#define TOML_DISABLE_WARNINGS \ - __pragma(warning(push, 0)) \ - TOML_DISABLE_SPAM_WARNINGS - -#define TOML_ENABLE_WARNINGS \ - __pragma(warning(pop)) \ - static_assert(true) - -#elif TOML_GCC - -#define TOML_PUSH_WARNINGS \ - TOML_PRAGMA_GCC(diagnostic push) \ - static_assert(true) - -#define TOML_DISABLE_SWITCH_WARNINGS \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wswitch") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wswitch-enum") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wswitch-default") \ - static_assert(true) - -#define TOML_DISABLE_ARITHMETIC_WARNINGS \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wfloat-equal") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wsign-conversion") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wchar-subscripts") \ - static_assert(true) - -#define TOML_DISABLE_SUGGEST_ATTR_WARNINGS \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wsuggest-attribute=const") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wsuggest-attribute=pure") \ - static_assert(true) - -#define TOML_DISABLE_SPAM_WARNINGS \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wpadded") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wcast-align") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wcomment") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wtype-limits") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wuseless-cast") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wchar-subscripts") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wsubobject-linkage") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wmissing-field-initializers") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wmaybe-uninitialized") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wnoexcept") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wnull-dereference") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wduplicated-branches") \ - static_assert(true) - -#define TOML_POP_WARNINGS \ - TOML_PRAGMA_GCC(diagnostic pop) \ - static_assert(true) - -#define TOML_DISABLE_WARNINGS \ - TOML_PRAGMA_GCC(diagnostic push) \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wall") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wextra") \ - TOML_PRAGMA_GCC(diagnostic ignored "-Wpedantic") \ - TOML_DISABLE_SWITCH_WARNINGS; \ - TOML_DISABLE_ARITHMETIC_WARNINGS; \ - TOML_DISABLE_SUGGEST_ATTR_WARNINGS; \ - TOML_DISABLE_SPAM_WARNINGS; \ - static_assert(true) - -#define TOML_ENABLE_WARNINGS \ - TOML_PRAGMA_GCC(diagnostic pop) \ - static_assert(true) - -#endif - -#ifndef TOML_PUSH_WARNINGS -#define TOML_PUSH_WARNINGS static_assert(true) -#endif -#ifndef TOML_DISABLE_CODE_ANALYSIS_WARNINGS -#define TOML_DISABLE_CODE_ANALYSIS_WARNINGS static_assert(true) -#endif -#ifndef TOML_DISABLE_SWITCH_WARNINGS -#define TOML_DISABLE_SWITCH_WARNINGS static_assert(true) -#endif -#ifndef TOML_DISABLE_SUGGEST_ATTR_WARNINGS -#define TOML_DISABLE_SUGGEST_ATTR_WARNINGS static_assert(true) -#endif -#ifndef TOML_DISABLE_SPAM_WARNINGS -#define TOML_DISABLE_SPAM_WARNINGS static_assert(true) -#endif -#ifndef TOML_DISABLE_ARITHMETIC_WARNINGS -#define TOML_DISABLE_ARITHMETIC_WARNINGS static_assert(true) -#endif -#ifndef TOML_POP_WARNINGS -#define TOML_POP_WARNINGS static_assert(true) -#endif -#ifndef TOML_DISABLE_WARNINGS -#define TOML_DISABLE_WARNINGS static_assert(true) -#endif -#ifndef TOML_ENABLE_WARNINGS -#define TOML_ENABLE_WARNINGS static_assert(true) -#endif -#ifndef TOML_SIMPLE_STATIC_ASSERT_MESSAGES -#define TOML_SIMPLE_STATIC_ASSERT_MESSAGES 0 -#endif - -#ifdef TOML_CONFIG_HEADER -#include TOML_CONFIG_HEADER -#endif - -// is the library being built as a shared lib/dll using meson and friends? -#ifndef TOML_SHARED_LIB -#define TOML_SHARED_LIB 0 -#endif - -// header-only mode -#if !defined(TOML_HEADER_ONLY) && defined(TOML_ALL_INLINE) // was TOML_ALL_INLINE pre-2.0 -#define TOML_HEADER_ONLY TOML_ALL_INLINE -#endif -#if !defined(TOML_HEADER_ONLY) || (defined(TOML_HEADER_ONLY) && TOML_HEADER_ONLY) || TOML_INTELLISENSE -#undef TOML_HEADER_ONLY -#define TOML_HEADER_ONLY 1 -#endif -#if TOML_DOXYGEN || TOML_SHARED_LIB -#undef TOML_HEADER_ONLY -#define TOML_HEADER_ONLY 0 -#endif - -// internal implementation switch -#if defined(TOML_IMPLEMENTATION) || TOML_HEADER_ONLY -#undef TOML_IMPLEMENTATION -#define TOML_IMPLEMENTATION 1 -#else -#define TOML_IMPLEMENTATION 0 -#endif - -// dll/shared lib function exports (legacy - TOML_API was the old name for this setting) -#if !defined(TOML_EXPORTED_MEMBER_FUNCTION) && !defined(TOML_EXPORTED_STATIC_FUNCTION) \ - && !defined(TOML_EXPORTED_FREE_FUNCTION) && !defined(TOML_EXPORTED_CLASS) && defined(TOML_API) -#define TOML_EXPORTED_MEMBER_FUNCTION TOML_API -#define TOML_EXPORTED_STATIC_FUNCTION TOML_API -#define TOML_EXPORTED_FREE_FUNCTION TOML_API -#endif - -// dll/shared lib exports -#if TOML_SHARED_LIB -#undef TOML_API -#undef TOML_EXPORTED_CLASS -#undef TOML_EXPORTED_MEMBER_FUNCTION -#undef TOML_EXPORTED_STATIC_FUNCTION -#undef TOML_EXPORTED_FREE_FUNCTION -#if TOML_WINDOWS -#if TOML_IMPLEMENTATION -#define TOML_EXPORTED_CLASS __declspec(dllexport) -#define TOML_EXPORTED_FREE_FUNCTION __declspec(dllexport) -#else -#define TOML_EXPORTED_CLASS __declspec(dllimport) -#define TOML_EXPORTED_FREE_FUNCTION __declspec(dllimport) -#endif -#ifndef TOML_CALLCONV -#define TOML_CALLCONV __cdecl -#endif -#elif defined(__GNUC__) && __GNUC__ >= 4 -#define TOML_EXPORTED_CLASS __attribute__((visibility("default"))) -#define TOML_EXPORTED_MEMBER_FUNCTION __attribute__((visibility("default"))) -#define TOML_EXPORTED_STATIC_FUNCTION __attribute__((visibility("default"))) -#define TOML_EXPORTED_FREE_FUNCTION __attribute__((visibility("default"))) -#endif -#endif -#ifndef TOML_EXPORTED_CLASS -#define TOML_EXPORTED_CLASS -#endif -#ifndef TOML_EXPORTED_MEMBER_FUNCTION -#define TOML_EXPORTED_MEMBER_FUNCTION -#endif -#ifndef TOML_EXPORTED_STATIC_FUNCTION -#define TOML_EXPORTED_STATIC_FUNCTION -#endif -#ifndef TOML_EXPORTED_FREE_FUNCTION -#define TOML_EXPORTED_FREE_FUNCTION -#endif - -// experimental language features -#if !defined(TOML_ENABLE_UNRELEASED_FEATURES) && defined(TOML_UNRELEASED_FEATURES) // was TOML_UNRELEASED_FEATURES - // pre-3.0 -#define TOML_ENABLE_UNRELEASED_FEATURES TOML_UNRELEASED_FEATURES -#endif -#if (defined(TOML_ENABLE_UNRELEASED_FEATURES) && TOML_ENABLE_UNRELEASED_FEATURES) || TOML_INTELLISENSE -#undef TOML_ENABLE_UNRELEASED_FEATURES -#define TOML_ENABLE_UNRELEASED_FEATURES 1 -#endif -#ifndef TOML_ENABLE_UNRELEASED_FEATURES -#define TOML_ENABLE_UNRELEASED_FEATURES 0 -#endif - -// parser -#if !defined(TOML_ENABLE_PARSER) && defined(TOML_PARSER) // was TOML_PARSER pre-3.0 -#define TOML_ENABLE_PARSER TOML_PARSER -#endif -#if !defined(TOML_ENABLE_PARSER) || (defined(TOML_ENABLE_PARSER) && TOML_ENABLE_PARSER) || TOML_INTELLISENSE -#undef TOML_ENABLE_PARSER -#define TOML_ENABLE_PARSER 1 -#endif - -// formatters -#if !defined(TOML_ENABLE_FORMATTERS) || (defined(TOML_ENABLE_FORMATTERS) && TOML_ENABLE_FORMATTERS) || TOML_INTELLISENSE -#undef TOML_ENABLE_FORMATTERS -#define TOML_ENABLE_FORMATTERS 1 -#endif - -// SIMD -#if !defined(TOML_ENABLE_SIMD) || (defined(TOML_ENABLE_SIMD) && TOML_ENABLE_SIMD) || TOML_INTELLISENSE -#undef TOML_ENABLE_SIMD -#define TOML_ENABLE_SIMD 1 -#endif - -// windows compat -#if !defined(TOML_ENABLE_WINDOWS_COMPAT) && defined(TOML_WINDOWS_COMPAT) // was TOML_WINDOWS_COMPAT pre-3.0 -#define TOML_ENABLE_WINDOWS_COMPAT TOML_WINDOWS_COMPAT -#endif -#if !defined(TOML_ENABLE_WINDOWS_COMPAT) || (defined(TOML_ENABLE_WINDOWS_COMPAT) && TOML_ENABLE_WINDOWS_COMPAT) \ - || TOML_INTELLISENSE -#undef TOML_ENABLE_WINDOWS_COMPAT -#define TOML_ENABLE_WINDOWS_COMPAT 1 -#endif - -#if !TOML_WINDOWS -#undef TOML_ENABLE_WINDOWS_COMPAT -#define TOML_ENABLE_WINDOWS_COMPAT 0 -#endif - -#ifndef TOML_INCLUDE_WINDOWS_H -#define TOML_INCLUDE_WINDOWS_H 0 -#endif - -// custom optional -#ifdef TOML_OPTIONAL_TYPE -#define TOML_HAS_CUSTOM_OPTIONAL_TYPE 1 -#else -#define TOML_HAS_CUSTOM_OPTIONAL_TYPE 0 -#endif - -// exceptions (library use) -#if TOML_COMPILER_HAS_EXCEPTIONS -#if !defined(TOML_EXCEPTIONS) || (defined(TOML_EXCEPTIONS) && TOML_EXCEPTIONS) -#undef TOML_EXCEPTIONS -#define TOML_EXCEPTIONS 1 -#endif -#else -#if defined(TOML_EXCEPTIONS) && TOML_EXCEPTIONS -#error TOML_EXCEPTIONS was explicitly enabled but exceptions are disabled/unsupported by the compiler. -#endif -#undef TOML_EXCEPTIONS -#define TOML_EXCEPTIONS 0 -#endif - -// calling convention for static/free/friend functions -#ifndef TOML_CALLCONV -#define TOML_CALLCONV -#endif - -#ifndef TOML_UNDEF_MACROS -#define TOML_UNDEF_MACROS 1 -#endif - -#ifndef TOML_MAX_NESTED_VALUES -#define TOML_MAX_NESTED_VALUES 128 -// this refers to the depth of nested values, e.g. inline tables and arrays. -// 128 is very generous; real TOML files rarely exceed single-digit nesting. -// keep this value low enough to avoid stack overflows in sanitizer-instrumented builds -// where each recursion cycle may consume ~3KB of stack. -#endif - -#ifndef TOML_MAX_DOTTED_KEYS_DEPTH -#define TOML_MAX_DOTTED_KEYS_DEPTH 1024 -#endif - -#ifdef TOML_CHAR_8_STRINGS -#if TOML_CHAR_8_STRINGS -#error TOML_CHAR_8_STRINGS was removed in toml++ 2.0.0; all value setters and getters now work with char8_t strings implicitly. -#endif -#endif - -#ifdef TOML_LARGE_FILES -#if !TOML_LARGE_FILES -#error Support for !TOML_LARGE_FILES (i.e. 'small files') was removed in toml++ 3.0.0. -#endif -#endif - -#ifndef TOML_LIFETIME_HOOKS -#define TOML_LIFETIME_HOOKS 0 -#endif - -#ifdef NDEBUG -#undef TOML_ASSERT -#define TOML_ASSERT(expr) static_assert(true) -#endif -#ifndef TOML_ASSERT -#ifndef assert -TOML_DISABLE_WARNINGS; -#include -TOML_ENABLE_WARNINGS; -#endif -#define TOML_ASSERT(expr) assert(expr) -#endif -#ifdef NDEBUG -#define TOML_ASSERT_ASSUME(expr) TOML_ASSUME(expr) -#else -#define TOML_ASSERT_ASSUME(expr) TOML_ASSERT(expr) -#endif - -#ifndef TOML_ENABLE_FLOAT16 -#define TOML_ENABLE_FLOAT16 0 -#endif - -#ifndef TOML_DISABLE_CONDITIONAL_NOEXCEPT_LAMBDA -#define TOML_DISABLE_CONDITIONAL_NOEXCEPT_LAMBDA 0 -#endif - -#ifndef TOML_DISABLE_NOEXCEPT_NOEXCEPT -#define TOML_DISABLE_NOEXCEPT_NOEXCEPT 0 - #ifdef _MSC_VER - #if _MSC_VER <= 1943 // Up to Visual Studio 2022 Version 17.13.6 - #undef TOML_DISABLE_NOEXCEPT_NOEXCEPT - #define TOML_DISABLE_NOEXCEPT_NOEXCEPT 1 - #endif - #endif -#endif - -#if !defined(TOML_FLOAT_CHARCONV) && (TOML_GCC || TOML_CLANG || (TOML_ICC && !TOML_ICC_CL)) -// not supported by any version of GCC or Clang as of 26/11/2020 -// not supported by any version of ICC on Linux as of 11/01/2021 -#define TOML_FLOAT_CHARCONV 0 -#endif -#if !defined(TOML_INT_CHARCONV) && (defined(__EMSCRIPTEN__) || defined(__APPLE__)) -// causes link errors on emscripten -// causes Mac OS SDK version errors on some versions of Apple Clang -#define TOML_INT_CHARCONV 0 -#endif -#ifndef TOML_INT_CHARCONV -#define TOML_INT_CHARCONV 1 -#endif -#ifndef TOML_FLOAT_CHARCONV -#define TOML_FLOAT_CHARCONV 1 -#endif -#if (TOML_INT_CHARCONV || TOML_FLOAT_CHARCONV) && !TOML_HAS_INCLUDE() -#undef TOML_INT_CHARCONV -#undef TOML_FLOAT_CHARCONV -#define TOML_INT_CHARCONV 0 -#define TOML_FLOAT_CHARCONV 0 -#endif - -#if defined(__cpp_concepts) && __cpp_concepts >= 201907 -#define TOML_REQUIRES(...) requires(__VA_ARGS__) -#else -#define TOML_REQUIRES(...) -#endif -#define TOML_ENABLE_IF(...) , typename std::enable_if<(__VA_ARGS__), int>::type = 0 -#define TOML_CONSTRAINED_TEMPLATE(condition, ...) \ - template <__VA_ARGS__ TOML_ENABLE_IF(condition)> \ - TOML_REQUIRES(condition) -#define TOML_HIDDEN_CONSTRAINT(condition, ...) TOML_CONSTRAINED_TEMPLATE(condition, __VA_ARGS__) - -#if defined(__SIZEOF_FLOAT128__) && defined(__FLT128_MANT_DIG__) && defined(__LDBL_MANT_DIG__) \ - && __FLT128_MANT_DIG__ > __LDBL_MANT_DIG__ -#define TOML_FLOAT128 __float128 -#endif - -#ifdef __SIZEOF_INT128__ -#define TOML_INT128 __int128_t -#define TOML_UINT128 __uint128_t -#endif - -// clang-format off - -//******** impl/version.hpp ****************************************************************************************** - -#define TOML_LIB_MAJOR 3 -#define TOML_LIB_MINOR 4 -#define TOML_LIB_PATCH 0 - -#define TOML_LANG_MAJOR 1 -#define TOML_LANG_MINOR 0 -#define TOML_LANG_PATCH 0 - -//******** impl/preprocessor.hpp ************************************************************************************* - -#define TOML_LIB_SINGLE_HEADER 1 - -#if TOML_ENABLE_UNRELEASED_FEATURES - #define TOML_LANG_EFFECTIVE_VERSION \ - TOML_MAKE_VERSION(TOML_LANG_MAJOR, TOML_LANG_MINOR, TOML_LANG_PATCH+1) -#else - #define TOML_LANG_EFFECTIVE_VERSION \ - TOML_MAKE_VERSION(TOML_LANG_MAJOR, TOML_LANG_MINOR, TOML_LANG_PATCH) -#endif - -#define TOML_LANG_HIGHER_THAN(major, minor, patch) \ - (TOML_LANG_EFFECTIVE_VERSION > TOML_MAKE_VERSION(major, minor, patch)) - -#define TOML_LANG_AT_LEAST(major, minor, patch) \ - (TOML_LANG_EFFECTIVE_VERSION >= TOML_MAKE_VERSION(major, minor, patch)) - -#define TOML_LANG_UNRELEASED \ - TOML_LANG_HIGHER_THAN(TOML_LANG_MAJOR, TOML_LANG_MINOR, TOML_LANG_PATCH) - -#ifndef TOML_ABI_NAMESPACES - #if TOML_DOXYGEN - #define TOML_ABI_NAMESPACES 0 - #else - #define TOML_ABI_NAMESPACES 1 - #endif -#endif -#if TOML_ABI_NAMESPACES - #define TOML_NAMESPACE_START namespace toml { inline namespace TOML_CONCAT(v, TOML_LIB_MAJOR) - #define TOML_NAMESPACE_END } static_assert(true) - #define TOML_NAMESPACE ::toml::TOML_CONCAT(v, TOML_LIB_MAJOR) - #define TOML_ABI_NAMESPACE_START(name) inline namespace name { static_assert(true) - #define TOML_ABI_NAMESPACE_BOOL(cond, T, F) TOML_ABI_NAMESPACE_START(TOML_CONCAT(TOML_EVAL_BOOL_, cond)(T, F)) - #define TOML_ABI_NAMESPACE_END } static_assert(true) -#else - #define TOML_NAMESPACE_START namespace toml - #define TOML_NAMESPACE_END static_assert(true) - #define TOML_NAMESPACE toml - #define TOML_ABI_NAMESPACE_START(...) static_assert(true) - #define TOML_ABI_NAMESPACE_BOOL(...) static_assert(true) - #define TOML_ABI_NAMESPACE_END static_assert(true) -#endif -#define TOML_IMPL_NAMESPACE_START TOML_NAMESPACE_START { namespace impl -#define TOML_IMPL_NAMESPACE_END } TOML_NAMESPACE_END -#if TOML_HEADER_ONLY - #define TOML_ANON_NAMESPACE_START static_assert(TOML_IMPLEMENTATION); TOML_IMPL_NAMESPACE_START - #define TOML_ANON_NAMESPACE_END TOML_IMPL_NAMESPACE_END - #define TOML_ANON_NAMESPACE TOML_NAMESPACE::impl - #define TOML_EXTERNAL_LINKAGE inline - #define TOML_INTERNAL_LINKAGE inline -#else - #define TOML_ANON_NAMESPACE_START static_assert(TOML_IMPLEMENTATION); \ - using namespace toml; \ - namespace - #define TOML_ANON_NAMESPACE_END static_assert(true) - #define TOML_ANON_NAMESPACE - #define TOML_EXTERNAL_LINKAGE - #define TOML_INTERNAL_LINKAGE static -#endif - -// clang-format on - -// clang-format off - -#if TOML_SIMPLE_STATIC_ASSERT_MESSAGES - - #define TOML_SA_NEWLINE " " - #define TOML_SA_LIST_SEP ", " - #define TOML_SA_LIST_BEG " (" - #define TOML_SA_LIST_END ")" - #define TOML_SA_LIST_NEW " " - #define TOML_SA_LIST_NXT ", " - -#else - - #define TOML_SA_NEWLINE "\n| " - #define TOML_SA_LIST_SEP TOML_SA_NEWLINE " - " - #define TOML_SA_LIST_BEG TOML_SA_LIST_SEP - #define TOML_SA_LIST_END - #define TOML_SA_LIST_NEW TOML_SA_NEWLINE TOML_SA_NEWLINE - #define TOML_SA_LIST_NXT TOML_SA_LIST_NEW - -#endif - -#define TOML_SA_NATIVE_VALUE_TYPE_LIST \ - TOML_SA_LIST_BEG "std::string" \ - TOML_SA_LIST_SEP "int64_t" \ - TOML_SA_LIST_SEP "double" \ - TOML_SA_LIST_SEP "bool" \ - TOML_SA_LIST_SEP "toml::date" \ - TOML_SA_LIST_SEP "toml::time" \ - TOML_SA_LIST_SEP "toml::date_time" \ - TOML_SA_LIST_END - -#define TOML_SA_NODE_TYPE_LIST \ - TOML_SA_LIST_BEG "toml::table" \ - TOML_SA_LIST_SEP "toml::array" \ - TOML_SA_LIST_SEP "toml::value" \ - TOML_SA_LIST_SEP "toml::value" \ - TOML_SA_LIST_SEP "toml::value" \ - TOML_SA_LIST_SEP "toml::value" \ - TOML_SA_LIST_SEP "toml::value" \ - TOML_SA_LIST_SEP "toml::value" \ - TOML_SA_LIST_SEP "toml::value" \ - TOML_SA_LIST_END - -#define TOML_SA_UNWRAPPED_NODE_TYPE_LIST \ - TOML_SA_LIST_NEW "A native TOML value type" \ - TOML_SA_NATIVE_VALUE_TYPE_LIST \ - \ - TOML_SA_LIST_NXT "A TOML node type" \ - TOML_SA_NODE_TYPE_LIST - -// clang-format on - -TOML_PUSH_WARNINGS; -TOML_DISABLE_SPAM_WARNINGS; -TOML_DISABLE_SWITCH_WARNINGS; -TOML_DISABLE_SUGGEST_ATTR_WARNINGS; - -// misc warning false-positives -#if TOML_MSVC -#pragma warning(disable : 5031) // #pragma warning(pop): likely mismatch -#if TOML_SHARED_LIB -#pragma warning(disable : 4251) // dll exports for std lib types -#endif -#elif TOML_CLANG -TOML_PRAGMA_CLANG(diagnostic ignored "-Wheader-hygiene") -#if TOML_CLANG >= 12 -TOML_PRAGMA_CLANG(diagnostic ignored "-Wc++20-extensions") -#endif -#if TOML_CLANG == 13 -TOML_PRAGMA_CLANG(diagnostic ignored "-Wreserved-identifier") -#endif -#endif - -//******** impl/std_new.hpp ****************************************************************************************** - -TOML_DISABLE_WARNINGS; -#include -TOML_ENABLE_WARNINGS; - -#if (!defined(__apple_build_version__) && TOML_CLANG >= 8) || TOML_GCC >= 7 || TOML_ICC >= 1910 || TOML_MSVC >= 1914 -#define TOML_LAUNDER(x) __builtin_launder(x) -#elif defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606 -#define TOML_LAUNDER(x) std::launder(x) -#else -#define TOML_LAUNDER(x) x -#endif - -//******** impl/std_string.hpp *************************************************************************************** - -TOML_DISABLE_WARNINGS; -#include -#include -TOML_ENABLE_WARNINGS; - -#if TOML_DOXYGEN \ - || (defined(__cpp_char8_t) && __cpp_char8_t >= 201811 && defined(__cpp_lib_char8_t) \ - && __cpp_lib_char8_t >= 201907) -#define TOML_HAS_CHAR8 1 -#else -#define TOML_HAS_CHAR8 0 -#endif - -namespace toml // non-abi namespace; this is not an error -{ - using namespace std::string_literals; - using namespace std::string_view_literals; -} - -#if TOML_ENABLE_WINDOWS_COMPAT - -TOML_IMPL_NAMESPACE_START -{ - TOML_NODISCARD - TOML_EXPORTED_FREE_FUNCTION - std::string narrow(std::wstring_view); - - TOML_NODISCARD - TOML_EXPORTED_FREE_FUNCTION - std::wstring widen(std::string_view); - -#if TOML_HAS_CHAR8 - - TOML_NODISCARD - TOML_EXPORTED_FREE_FUNCTION - std::wstring widen(std::u8string_view); - -#endif -} -TOML_IMPL_NAMESPACE_END; - -#endif // TOML_ENABLE_WINDOWS_COMPAT - -//******** impl/std_optional.hpp ************************************************************************************* - -TOML_DISABLE_WARNINGS; -#if !TOML_HAS_CUSTOM_OPTIONAL_TYPE -#include -#endif -TOML_ENABLE_WARNINGS; - -TOML_NAMESPACE_START -{ -#if TOML_HAS_CUSTOM_OPTIONAL_TYPE - - template - using optional = TOML_OPTIONAL_TYPE; - -#else - - template - using optional = std::optional; - -#endif -} -TOML_NAMESPACE_END; - -//******** impl/forward_declarations.hpp ***************************************************************************** - -TOML_DISABLE_WARNINGS; -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -TOML_ENABLE_WARNINGS; -TOML_PUSH_WARNINGS; -#ifdef _MSC_VER -#ifndef __clang__ -#pragma inline_recursion(on) -#endif -#pragma push_macro("min") -#pragma push_macro("max") -#undef min -#undef max -#endif - -#ifndef TOML_DISABLE_ENVIRONMENT_CHECKS -#define TOML_ENV_MESSAGE \ - "If you're seeing this error it's because you're building toml++ for an environment that doesn't conform to " \ - "one of the 'ground truths' assumed by the library. Essentially this just means that I don't have the " \ - "resources to test on more platforms, but I wish I did! You can try disabling the checks by defining " \ - "TOML_DISABLE_ENVIRONMENT_CHECKS, but your mileage may vary. Please consider filing an issue at " \ - "https://github.com/marzer/tomlplusplus/issues to help me improve support for your target environment. " \ - "Thanks!" - -static_assert(CHAR_BIT == 8, TOML_ENV_MESSAGE); -#ifdef FLT_RADIX -static_assert(FLT_RADIX == 2, TOML_ENV_MESSAGE); -#endif -static_assert('A' == 65, TOML_ENV_MESSAGE); -static_assert(sizeof(double) == 8, TOML_ENV_MESSAGE); -static_assert(std::numeric_limits::is_iec559, TOML_ENV_MESSAGE); -static_assert(std::numeric_limits::digits == 53, TOML_ENV_MESSAGE); -static_assert(std::numeric_limits::digits10 == 15, TOML_ENV_MESSAGE); -static_assert(std::numeric_limits::radix == 2, TOML_ENV_MESSAGE); - -#undef TOML_ENV_MESSAGE -#endif // !TOML_DISABLE_ENVIRONMENT_CHECKS - -// undocumented forward declarations are hidden from doxygen because they fuck it up =/ - -namespace toml // non-abi namespace; this is not an error -{ - using ::std::size_t; - using ::std::intptr_t; - using ::std::uintptr_t; - using ::std::ptrdiff_t; - using ::std::nullptr_t; - using ::std::int8_t; - using ::std::int16_t; - using ::std::int32_t; - using ::std::int64_t; - using ::std::uint8_t; - using ::std::uint16_t; - using ::std::uint32_t; - using ::std::uint64_t; - using ::std::uint_least32_t; - using ::std::uint_least64_t; -} - -TOML_NAMESPACE_START -{ - struct date; - struct time; - struct time_offset; - - TOML_ABI_NAMESPACE_BOOL(TOML_HAS_CUSTOM_OPTIONAL_TYPE, custopt, stdopt); - struct date_time; - TOML_ABI_NAMESPACE_END; - - struct source_position; - struct source_region; - - class node; - template - class node_view; - - class key; - class array; - class table; - template - class value; - - class path; - - class toml_formatter; - class json_formatter; - class yaml_formatter; - - TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, ex, noex); -#if TOML_EXCEPTIONS - using parse_result = table; -#else - class parse_result; -#endif - TOML_ABI_NAMESPACE_END; // TOML_EXCEPTIONS -} -TOML_NAMESPACE_END; - -TOML_IMPL_NAMESPACE_START -{ - using node_ptr = std::unique_ptr; - - TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, impl_ex, impl_noex); - class parser; - TOML_ABI_NAMESPACE_END; // TOML_EXCEPTIONS - - // clang-format off - - inline constexpr std::string_view control_char_escapes[] = - { - "\\u0000"sv, - "\\u0001"sv, - "\\u0002"sv, - "\\u0003"sv, - "\\u0004"sv, - "\\u0005"sv, - "\\u0006"sv, - "\\u0007"sv, - "\\b"sv, - "\\t"sv, - "\\n"sv, - "\\u000B"sv, - "\\f"sv, - "\\r"sv, - "\\u000E"sv, - "\\u000F"sv, - "\\u0010"sv, - "\\u0011"sv, - "\\u0012"sv, - "\\u0013"sv, - "\\u0014"sv, - "\\u0015"sv, - "\\u0016"sv, - "\\u0017"sv, - "\\u0018"sv, - "\\u0019"sv, - "\\u001A"sv, - "\\u001B"sv, - "\\u001C"sv, - "\\u001D"sv, - "\\u001E"sv, - "\\u001F"sv, - }; - - inline constexpr std::string_view node_type_friendly_names[] = - { - "none"sv, - "table"sv, - "array"sv, - "string"sv, - "integer"sv, - "floating-point"sv, - "boolean"sv, - "date"sv, - "time"sv, - "date-time"sv - }; - - // clang-format on -} -TOML_IMPL_NAMESPACE_END; - -#if TOML_ABI_NAMESPACES -#if TOML_EXCEPTIONS -#define TOML_PARSER_TYPENAME TOML_NAMESPACE::impl::impl_ex::parser -#else -#define TOML_PARSER_TYPENAME TOML_NAMESPACE::impl::impl_noex::parser -#endif -#else -#define TOML_PARSER_TYPENAME TOML_NAMESPACE::impl::parser -#endif - -namespace toml -{ -} - -TOML_NAMESPACE_START // abi namespace -{ - inline namespace literals - { - } - - enum class TOML_CLOSED_ENUM node_type : uint8_t - { - none, - table, - array, - string, - integer, - floating_point, - boolean, - date, - time, - date_time - }; - - template - inline std::basic_ostream& operator<<(std::basic_ostream& lhs, node_type rhs) - { - const auto str = impl::node_type_friendly_names[static_cast>(rhs)]; - using str_char_t = decltype(str)::value_type; - if constexpr (std::is_same_v) - return lhs << str; - else - { - if constexpr (sizeof(Char) == sizeof(str_char_t)) - return lhs << std::basic_string_view{ reinterpret_cast(str.data()), str.length() }; - else - return lhs << str.data(); - } - } - - enum class TOML_OPEN_FLAGS_ENUM value_flags : uint16_t // being an "OPEN" flags enum is not an error - { - none, - format_as_binary = 1, - format_as_octal = 2, - format_as_hexadecimal = 3, - }; - TOML_MAKE_FLAGS(value_flags); - - inline constexpr value_flags preserve_source_value_flags = - POXY_IMPLEMENTATION_DETAIL(value_flags{ static_cast>(-1) }); - - enum class TOML_CLOSED_FLAGS_ENUM format_flags : uint64_t - { - none, - quote_dates_and_times = (1ull << 0), - quote_infinities_and_nans = (1ull << 1), - allow_literal_strings = (1ull << 2), - allow_multi_line_strings = (1ull << 3), - allow_real_tabs_in_strings = (1ull << 4), - allow_unicode_strings = (1ull << 5), - allow_binary_integers = (1ull << 6), - allow_octal_integers = (1ull << 7), - allow_hexadecimal_integers = (1ull << 8), - indent_sub_tables = (1ull << 9), - indent_array_elements = (1ull << 10), - indentation = indent_sub_tables | indent_array_elements, - relaxed_float_precision = (1ull << 11), - terse_key_value_pairs = (1ull << 12), - force_multiline_arrays = (1ull << 13), - }; - TOML_MAKE_FLAGS(format_flags); - - template - struct TOML_TRIVIAL_ABI inserter - { - static_assert(std::is_reference_v); - - T value; - }; - template - inserter(T&&) -> inserter; - template - inserter(T&) -> inserter; - - using default_formatter = toml_formatter; -} -TOML_NAMESPACE_END; - -TOML_IMPL_NAMESPACE_START -{ - template - using remove_cvref = std::remove_cv_t>; - - template - using common_signed_type = std::common_type_t...>; - - template - inline constexpr bool is_one_of = (false || ... || std::is_same_v); - - template - inline constexpr bool all_integral = (std::is_integral_v && ...); - - template - inline constexpr bool is_cvref = std::is_reference_v || std::is_const_v || std::is_volatile_v; - - template - inline constexpr bool is_wide_string = - is_one_of, const wchar_t*, wchar_t*, std::wstring_view, std::wstring>; - - template - inline constexpr bool value_retrieval_is_nothrow = !std::is_same_v, std::string> -#if TOML_HAS_CHAR8 - && !std::is_same_v, std::u8string> -#endif - - && !is_wide_string; - - template - struct copy_ref_; - template - using copy_ref = typename copy_ref_::type; - - template - struct copy_ref_ - { - using type = Dest; - }; - - template - struct copy_ref_ - { - using type = std::add_lvalue_reference_t; - }; - - template - struct copy_ref_ - { - using type = std::add_rvalue_reference_t; - }; - - template - struct copy_cv_; - template - using copy_cv = typename copy_cv_::type; - - template - struct copy_cv_ - { - using type = Dest; - }; - - template - struct copy_cv_ - { - using type = std::add_const_t; - }; - - template - struct copy_cv_ - { - using type = std::add_volatile_t; - }; - - template - struct copy_cv_ - { - using type = std::add_cv_t; - }; - - template - using copy_cvref = - copy_ref, std::remove_reference_t>, Dest>, Src>; - - template - inline constexpr bool always_false = false; - - template - inline constexpr bool first_is_same = false; - template - inline constexpr bool first_is_same = true; - - template > - struct underlying_type_ - { - using type = std::underlying_type_t; - }; - template - struct underlying_type_ - { - using type = T; - }; - template - using underlying_type = typename underlying_type_::type; - - // general value traits - // (as they relate to their equivalent native TOML type) - struct default_value_traits - { - using native_type = void; - static constexpr bool is_native = false; - static constexpr bool is_losslessly_convertible_to_native = false; - static constexpr bool can_represent_native = false; - static constexpr bool can_partially_represent_native = false; - static constexpr auto type = node_type::none; - }; - - template - struct value_traits; - - template > - struct value_traits_base_selector - { - static_assert(!is_cvref); - - using type = default_value_traits; - }; - template - struct value_traits_base_selector - { - static_assert(!is_cvref); - - using type = value_traits>; - }; - - template - struct value_traits : value_traits_base_selector::type - {}; - template - struct value_traits : value_traits - {}; - template - struct value_traits : value_traits - {}; - template - struct value_traits : value_traits - {}; - template - struct value_traits : value_traits - {}; - template - struct value_traits : value_traits - {}; - - // integer value_traits specializations - standard types - template - struct integer_limits - { - static constexpr T min = T{ (std::numeric_limits>::min)() }; - static constexpr T max = T{ (std::numeric_limits>::max)() }; - }; - template - struct integer_traits_base : integer_limits - { - using native_type = int64_t; - static constexpr bool is_native = std::is_same_v, native_type>; - static constexpr bool is_signed = static_cast>(-1) < underlying_type{}; - static constexpr auto type = node_type::integer; - static constexpr bool can_partially_represent_native = true; - }; - template - struct unsigned_integer_traits : integer_traits_base - { - static constexpr bool is_losslessly_convertible_to_native = - integer_limits>::max <= 9223372036854775807ULL; - static constexpr bool can_represent_native = false; - }; - template - struct signed_integer_traits : integer_traits_base - { - using native_type = int64_t; - static constexpr bool is_losslessly_convertible_to_native = - integer_limits>::min >= (-9223372036854775807LL - 1LL) - && integer_limits>::max <= 9223372036854775807LL; - static constexpr bool can_represent_native = - integer_limits>::min <= (-9223372036854775807LL - 1LL) - && integer_limits>::max >= 9223372036854775807LL; - }; - template ::is_signed> - struct integer_traits : signed_integer_traits - {}; - template - struct integer_traits : unsigned_integer_traits - {}; - template <> - struct value_traits : integer_traits - {}; - template <> - struct value_traits : integer_traits - {}; - template <> - struct value_traits : integer_traits - {}; - template <> - struct value_traits : integer_traits - {}; - template <> - struct value_traits : integer_traits - {}; - template <> - struct value_traits : integer_traits - {}; - template <> - struct value_traits : integer_traits - {}; - template <> - struct value_traits : integer_traits - {}; - template <> - struct value_traits : integer_traits - {}; - template <> - struct value_traits : integer_traits - {}; - static_assert(value_traits::is_native); - static_assert(value_traits::is_signed); - static_assert(value_traits::is_losslessly_convertible_to_native); - static_assert(value_traits::can_represent_native); - static_assert(value_traits::can_partially_represent_native); - - // integer value_traits specializations - non-standard types -#ifdef TOML_INT128 - template <> - struct integer_limits - { - static constexpr TOML_INT128 max = - static_cast((TOML_UINT128{ 1u } << ((__SIZEOF_INT128__ * CHAR_BIT) - 1)) - 1); - static constexpr TOML_INT128 min = -max - TOML_INT128{ 1 }; - }; - template <> - struct integer_limits - { - static constexpr TOML_UINT128 min = TOML_UINT128{}; - static constexpr TOML_UINT128 max = (2u * static_cast(integer_limits::max)) + 1u; - }; - template <> - struct value_traits : integer_traits - {}; - template <> - struct value_traits : integer_traits - {}; -#endif -#ifdef TOML_SMALL_INT_TYPE - template <> - struct value_traits : signed_integer_traits - {}; -#endif - - // floating-point traits base - template - struct float_traits_base - { - static constexpr auto type = node_type::floating_point; - using native_type = double; - static constexpr bool is_native = std::is_same_v; - static constexpr bool is_signed = true; - - static constexpr int bits = static_cast(sizeof(T) * CHAR_BIT); - static constexpr int digits = MantissaDigits; - static constexpr int digits10 = DecimalDigits; - - static constexpr bool is_losslessly_convertible_to_native = bits <= 64 // - && digits <= 53 // DBL_MANT_DIG - && digits10 <= 15; // DBL_DIG - - static constexpr bool can_represent_native = digits >= 53 // DBL_MANT_DIG - && digits10 >= 15; // DBL_DIG - - static constexpr bool can_partially_represent_native = digits > 0 && digits10 > 0; - }; - template - struct float_traits : float_traits_base::digits, std::numeric_limits::digits10> - {}; -#if TOML_ENABLE_FLOAT16 - template <> - struct float_traits<_Float16> : float_traits_base<_Float16, __FLT16_MANT_DIG__, __FLT16_DIG__> - {}; -#endif -#ifdef TOML_FLOAT128 - template <> - struct float_traits : float_traits_base - {}; -#endif - - // floating-point traits - template <> - struct value_traits : float_traits - {}; - template <> - struct value_traits : float_traits - {}; - template <> - struct value_traits : float_traits - {}; -#if TOML_ENABLE_FLOAT16 - template <> - struct value_traits<_Float16> : float_traits<_Float16> - {}; -#endif -#ifdef TOML_FLOAT128 - template <> - struct value_traits : float_traits - {}; -#endif -#ifdef TOML_SMALL_FLOAT_TYPE - template <> - struct value_traits : float_traits - {}; -#endif - static_assert(value_traits::is_native); - static_assert(value_traits::is_losslessly_convertible_to_native); - static_assert(value_traits::can_represent_native); - static_assert(value_traits::can_partially_represent_native); - - // string value_traits specializations - char-based strings - template - struct string_traits - { - using native_type = std::string; - static constexpr bool is_native = std::is_same_v; - static constexpr bool is_losslessly_convertible_to_native = true; - static constexpr bool can_represent_native = - !std::is_array_v && (!std::is_pointer_v || std::is_const_v>); - static constexpr bool can_partially_represent_native = can_represent_native; - static constexpr auto type = node_type::string; - }; - template <> - struct value_traits : string_traits - {}; - template <> - struct value_traits : string_traits - {}; - template <> - struct value_traits : string_traits - {}; - template - struct value_traits : string_traits - {}; - template <> - struct value_traits : string_traits - {}; - template - struct value_traits : string_traits - {}; - - // string value_traits specializations - char8_t-based strings -#if TOML_HAS_CHAR8 - template <> - struct value_traits : string_traits - {}; - template <> - struct value_traits : string_traits - {}; - template <> - struct value_traits : string_traits - {}; - template - struct value_traits : string_traits - {}; - template <> - struct value_traits : string_traits - {}; - template - struct value_traits : string_traits - {}; -#endif - - // string value_traits specializations - wchar_t-based strings on Windows -#if TOML_ENABLE_WINDOWS_COMPAT - template - struct wstring_traits - { - using native_type = std::string; - static constexpr bool is_native = false; - static constexpr bool is_losslessly_convertible_to_native = true; // narrow - static constexpr bool can_represent_native = std::is_same_v; // widen - static constexpr bool can_partially_represent_native = can_represent_native; - static constexpr auto type = node_type::string; - }; - template <> - struct value_traits : wstring_traits - {}; - template <> - struct value_traits : wstring_traits - {}; - template <> - struct value_traits : wstring_traits - {}; - template - struct value_traits : wstring_traits - {}; - template <> - struct value_traits : wstring_traits - {}; - template - struct value_traits : wstring_traits - {}; -#endif - - // other 'native' value_traits specializations - template - struct native_value_traits - { - using native_type = T; - static constexpr bool is_native = true; - static constexpr bool is_losslessly_convertible_to_native = true; - static constexpr bool can_represent_native = true; - static constexpr bool can_partially_represent_native = true; - static constexpr auto type = NodeType; - }; - template <> - struct value_traits : native_value_traits - {}; - template <> - struct value_traits : native_value_traits - {}; - template <> - struct value_traits