Compile-time support for parsing json objects!

This commit is contained in:
Francisco Geiman Thiesen
2025-10-10 18:40:09 -07:00
parent a7f8fb71c5
commit ca42a49fba
6 changed files with 902 additions and 0 deletions
+1
View File
@@ -53,6 +53,7 @@ consteval std::string consteval_to_quoted_escaped(std::string_view input) {
#if SIMDJSON_SUPPORTS_CONCEPTS
template <std::size_t N>
struct fixed_string {
constexpr fixed_string() : data{} {} // Default constructor for buffers
constexpr fixed_string(const char (&str)[N]) {
for (std::size_t i = 0; i < N; ++i) {
data[i] = str[i];
@@ -52,3 +52,7 @@
#include "simdjson/generic/ondemand/json_string_builder-inl.h"
#include "simdjson/generic/ondemand/json_builder.h"
// Compile-time JSON parsing (C++26 P2996 reflection)
#include "simdjson/generic/ondemand/compile_time_json.h"
#include "simdjson/generic/ondemand/compile_time_json-inl.h"
@@ -0,0 +1,98 @@
/**
* @file compile_time_json-inl.h
* @brief Implementation details for compile-time JSON parsing
*
* This file contains inline implementations and helper utilities for compile-time
* JSON parsing. Currently, the main implementation is self-contained in the header.
*/
#ifndef SIMDJSON_GENERIC_COMPILE_TIME_JSON_INL_H
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#define SIMDJSON_GENERIC_COMPILE_TIME_JSON_INL_H
#include "simdjson/generic/ondemand/compile_time_json.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
#if SIMDJSON_STATIC_REFLECTION
#include <meta>
#include <array>
#include <string_view>
#include <cstdint>
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace compile_time {
/**
* @brief Optimized constexpr string to integer conversion
*
* This can be used for more efficient integer parsing in the future.
* Currently, we parse all numbers as doubles for simplicity.
*/
constexpr int64_t parse_int_fast(std::string_view str) {
int64_t result = 0;
bool negative = false;
std::size_t i = 0;
if (i < str.size() && str[i] == '-') {
negative = true;
++i;
}
while (i < str.size() && str[i] >= '0' && str[i] <= '9') {
result = result * 10 + (str[i] - '0');
++i;
}
return negative ? -result : result;
}
/**
* @brief Unescape JSON string at compile-time
*
* Currently, strings are returned as views into the original JSON.
* This function can be used in the future for proper escape handling.
*/
template<std::size_t MaxLen = 1024>
constexpr auto unescape_json_string(std::string_view escaped) {
std::array<char, MaxLen> result{};
std::size_t out_pos = 0;
std::size_t i = 0;
while (i < escaped.size() && out_pos < MaxLen) {
if (escaped[i] == '\\' && i + 1 < escaped.size()) {
++i;
switch (escaped[i]) {
case '"': result[out_pos++] = '"'; break;
case '\\': result[out_pos++] = '\\'; break;
case '/': result[out_pos++] = '/'; break;
case 'b': result[out_pos++] = '\b'; break;
case 'f': result[out_pos++] = '\f'; break;
case 'n': result[out_pos++] = '\n'; break;
case 'r': result[out_pos++] = '\r'; break;
case 't': result[out_pos++] = '\t'; break;
case 'u':
// Unicode escape - would need proper implementation
// For now, skip the escape sequence
i += 4; // Skip 4 hex digits
break;
default:
result[out_pos++] = escaped[i];
}
++i;
} else {
result[out_pos++] = escaped[i];
++i;
}
}
return std::pair{result, out_pos};
}
} // namespace compile_time
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_GENERIC_COMPILE_TIME_JSON_INL_H
@@ -0,0 +1,366 @@
/**
* @file compile_time_json.h
* @brief Compile-time JSON parsing using C++26 P2996 reflection
*
* Parses JSON at compile-time and generates strongly-typed structs automatically.
* Uses std::meta::substitute() pattern for recursive structures.
*
* @warning Requires C++26 with P2996R13 (experimental clang-p2996 compiler)
*
* Inspired by: https://brevzin.github.io/c++/2025/06/26/json-reflection/
* https://godbolt.org/z/Kn5b46T8j
*/
#ifndef SIMDJSON_GENERIC_COMPILE_TIME_JSON_H
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#define SIMDJSON_GENERIC_COMPILE_TIME_JSON_H
#include "simdjson/generic/ondemand/base.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
#if SIMDJSON_STATIC_REFLECTION
#include <meta>
#include <string_view>
#include <vector>
#include <array>
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace compile_time {
/**
* @brief Helper template for dynamic type generation via substitute()
*/
template <std::meta::info ...Ms>
struct Outer {
struct Inner;
consteval {
std::meta::define_aggregate(^^Inner, {Ms...});
}
};
template <std::meta::info ...Ms>
using Cls = Outer<Ms...>::Inner;
/**
* @brief Helper template for aggregate initialization from reflected values
*/
template <typename T, auto ... Vs>
constexpr auto construct_from = T{Vs...};
/**
* @brief Parsing state and helper functions
*/
struct ParseContext {
std::string_view json;
std::size_t pos = 0;
constexpr ParseContext(std::string_view input) : json(input), pos(0) {}
constexpr void skip_whitespace() {
while (pos < json.size() && (json[pos] == ' ' || json[pos] == '\t' ||
json[pos] == '\n' || json[pos] == '\r')) {
++pos;
}
}
[[nodiscard]] constexpr char peek() const {
return pos < json.size() ? json[pos] : '\0';
}
constexpr char consume() {
return pos < json.size() ? json[pos++] : '\0';
}
constexpr bool expect(char ch) {
skip_whitespace();
if (peek() == ch) {
consume();
return true;
}
throw "expected character";
}
[[nodiscard]] constexpr bool match(std::string_view str) {
if (json.substr(pos, str.size()) == str) {
pos += str.size();
return true;
}
return false;
}
[[nodiscard]] constexpr std::string_view parse_string() {
if (!expect('"')) throw "expected quote";
std::size_t start = pos;
while (peek() != '"' && peek() != '\0') {
if (peek() == '\\') {
consume();
if (pos < json.size()) consume();
} else {
consume();
}
}
std::size_t end = pos;
expect('"');
return json.substr(start, end - start);
}
[[nodiscard]] constexpr double parse_number() {
std::size_t start = pos;
if (peek() == '-') consume();
while (peek() >= '0' && peek() <= '9') consume();
if (peek() == '.') {
consume();
while (peek() >= '0' && peek() <= '9') consume();
}
std::string_view num_str = json.substr(start, pos - start);
double result = 0.0, sign = 1.0;
std::size_t i = 0;
if (i < num_str.size() && num_str[i] == '-') {
sign = -1.0;
++i;
}
while (i < num_str.size() && num_str[i] >= '0' && num_str[i] <= '9') {
result = result * 10.0 + (num_str[i] - '0');
++i;
}
if (i < num_str.size() && num_str[i] == '.') {
++i;
double fraction = 0.0, divisor = 1.0;
while (i < num_str.size() && num_str[i] >= '0' && num_str[i] <= '9') {
fraction = fraction * 10.0 + (num_str[i] - '0');
divisor *= 10.0;
++i;
}
result += fraction / divisor;
}
return result * sign;
}
[[nodiscard]] constexpr std::string_view extract_nested() {
std::size_t start = pos;
char open_char = peek();
char close_char = (open_char == '{') ? '}' : ']';
int depth = 0;
do {
if (peek() == open_char) depth++;
if (peek() == close_char) depth--;
consume();
} while (depth > 0 && pos < json.size());
if (depth != 0) throw "unclosed bracket";
return json.substr(start, pos - start);
}
};
consteval std::meta::info parse_json_impl(std::string_view json);
consteval std::meta::info parse_array_impl(std::string_view json);
consteval std::meta::info parse_array_impl(std::string_view json) {
ParseContext ctx{json};
ctx.skip_whitespace();
if (!ctx.expect('[')) {
throw "expected '['";
}
ctx.skip_whitespace();
if (ctx.peek() == ']') {
ctx.consume();
return std::meta::substitute(^^construct_from, {^^std::array<void, 0>, ^^void});
}
std::vector<std::meta::info> element_values;
while (ctx.peek() != ']') {
ctx.skip_whitespace();
char ch = ctx.peek();
if (ch == '"') {
auto str_value = ctx.parse_string();
element_values.push_back(std::meta::reflect_constant_string(str_value));
} else if (ch == 't' || ch == 'f') {
bool bool_value = ctx.peek() == 't';
if (bool_value) {
if (!ctx.match("true")) throw "expected 'true'";
} else {
if (!ctx.match("false")) throw "expected 'false'";
}
element_values.push_back(std::meta::reflect_constant(bool_value));
} else if (ch == 'n') {
if (!ctx.match("null")) throw "expected 'null'";
element_values.push_back(std::meta::reflect_constant(nullptr));
} else if (ch == '{') {
auto nested_json = ctx.extract_nested();
std::meta::info parsed = parse_json_impl(nested_json);
element_values.push_back(parsed);
} else if (ch == '[') {
auto nested_json = ctx.extract_nested();
std::meta::info parsed = parse_array_impl(nested_json);
element_values.push_back(parsed);
} else if ((ch >= '0' && ch <= '9') || ch == '-') {
double num_value = ctx.parse_number();
element_values.push_back(std::meta::reflect_constant(num_value));
} else {
throw "unexpected array element type";
}
ctx.skip_whitespace();
if (ctx.peek() == ',') {
ctx.consume();
ctx.skip_whitespace();
} else {
break;
}
}
ctx.expect(']');
if (element_values.empty()) {
return std::meta::substitute(^^construct_from, {^^std::array<void, 0>, ^^void});
}
std::meta::info element_type = std::meta::type_of(element_values[0]);
std::size_t count = element_values.size();
std::meta::info array_type = std::meta::substitute(^^std::array, {element_type, std::meta::reflect_constant(count)});
std::vector<std::meta::info> values;
values.push_back(array_type);
for (auto& elem : element_values) {
values.push_back(elem);
}
return std::meta::substitute(^^construct_from, values);
}
/**
* @brief Main compile-time JSON parser using substitute() pattern
*
* Returns std::meta::info containing type and value information.
* This can be recursively called for nested objects without compiler crashes.
*/
consteval std::meta::info parse_json_impl(std::string_view json) {
ParseContext ctx{json};
ctx.skip_whitespace();
if (!ctx.expect('{')) {
throw "expected '{'";
}
std::vector<std::meta::info> members;
std::vector<std::meta::info> values = {^^void};
using std::meta::reflect_constant;
while (ctx.peek() != '}') {
ctx.skip_whitespace();
if (ctx.peek() == '}') break;
auto field_name = ctx.parse_string();
ctx.skip_whitespace();
ctx.expect(':');
ctx.skip_whitespace();
char ch = ctx.peek();
if (ch == '"') {
auto str_value = ctx.parse_string();
auto dms = std::meta::data_member_spec(^^char const*, {.name=field_name});
members.push_back(reflect_constant(dms));
values.push_back(std::meta::reflect_constant_string(str_value));
} else if (ch == 't' || ch == 'f') {
bool bool_value = ctx.peek() == 't';
if (bool_value) {
if (!ctx.match("true")) throw "expected 'true'";
} else {
if (!ctx.match("false")) throw "expected 'false'";
}
auto dms = std::meta::data_member_spec(^^bool, {.name=field_name});
members.push_back(reflect_constant(dms));
values.push_back(reflect_constant(bool_value));
} else if (ch == 'n') {
if (!ctx.match("null")) throw "expected 'null'";
auto dms = std::meta::data_member_spec(^^std::nullptr_t, {.name=field_name});
members.push_back(reflect_constant(dms));
values.push_back(reflect_constant(nullptr));
} else if (ch == '[') {
auto array_json = ctx.extract_nested();
std::meta::info parsed = parse_array_impl(array_json);
auto dms = std::meta::data_member_spec(std::meta::type_of(parsed), {.name=field_name});
members.push_back(reflect_constant(dms));
values.push_back(parsed);
} else if (ch == '{') {
auto nested_json = ctx.extract_nested();
std::meta::info parsed = parse_json_impl(nested_json);
auto dms = std::meta::data_member_spec(std::meta::type_of(parsed), {.name=field_name});
members.push_back(reflect_constant(dms));
values.push_back(parsed);
} else if ((ch >= '0' && ch <= '9') || ch == '-') {
double num_value = ctx.parse_number();
auto dms = std::meta::data_member_spec(^^double, {.name=field_name});
members.push_back(reflect_constant(dms));
values.push_back(reflect_constant(num_value));
} else {
throw "unexpected value type";
}
ctx.skip_whitespace();
if (ctx.peek() == ',') ctx.consume();
}
ctx.expect('}');
values[0] = std::meta::substitute(^^Cls, members);
return std::meta::substitute(^^construct_from, values);
}
/**
* @brief JSON string wrapper for template parameters
*/
struct JSONString {
std::meta::info Rep;
consteval JSONString(const char *Json) : Rep{parse_json_impl(Json)} {}
};
/**
* @brief Main parse_json function - template variable pattern
*
* Usage:
* constexpr auto config = json_to_object<R"({"port":8080,"host":"localhost"})">;
* static_assert(config.port == 8080);
* static_assert(std::string_view(config.host) == "localhost");
*/
template <JSONString json>
inline constexpr auto json_to_object = [:json.Rep:];
/**
* @brief Alternative: User-defined literal for JSON parsing
*
* Usage:
* constexpr auto config = R"({"port":8080})"_json;
*/
template <JSONString json>
consteval auto operator""_json() {
return [:json.Rep:];
}
/**
* @brief Validate JSON syntax at compile-time
*/
template <JSONString json>
consteval bool validate_json() {
return true;
}
} // namespace compile_time
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
#endif // SIMDJSON_STATIC_REFLECTION
#endif // SIMDJSON_GENERIC_COMPILE_TIME_JSON_H
+425
View File
@@ -0,0 +1,425 @@
/**
* @file compile_time_json_tests.cpp
* @brief Tests for compile-time JSON parsing using C++26 P2996 reflection
*/
#include "simdjson.h"
#include "test_ondemand.h"
using namespace simdjson;
using namespace std::string_view_literals;
namespace compile_time_json_tests {
#if SIMDJSON_STATIC_REFLECTION
using namespace arm64::compile_time;
#endif
/**
* Test 1: Basic object with primitives
*/
bool test_basic_object() {
TEST_START();
constexpr auto config = json_to_object<R"({
"port": 8080,
"host": "localhost",
"debug": true,
"timeout": 30.5
})">;
static_assert(config.port == 8080);
static_assert(std::string_view(config.host) == "localhost");
static_assert(config.debug == true);
static_assert(config.timeout == 30.5);
ASSERT_EQUAL(config.port, 8080);
ASSERT_EQUAL(std::string_view(config.host), "localhost"sv);
ASSERT_TRUE(config.debug);
ASSERT_EQUAL(config.timeout, 30.5);
TEST_SUCCEED();
}
/**
* Test 2: Nested objects
*/
bool test_nested_objects() {
TEST_START();
constexpr auto config = json_to_object<R"({
"server_port": 3000,
"enable_ssl": true,
"database": {
"host": "db.example.com",
"port": 5432,
"timeout_sec": 30.0
}
})">;
static_assert(config.server_port == 3000);
static_assert(config.enable_ssl == true);
static_assert(std::string_view(config.database.host) == "db.example.com");
static_assert(config.database.port == 5432);
static_assert(config.database.timeout_sec == 30.0);
ASSERT_EQUAL(config.server_port, 3000);
ASSERT_TRUE(config.enable_ssl);
ASSERT_EQUAL(std::string_view(config.database.host), "db.example.com"sv);
ASSERT_EQUAL(config.database.port, 5432);
ASSERT_EQUAL(config.database.timeout_sec, 30.0);
TEST_SUCCEED();
}
/**
* Test 3: Deeply nested objects (3+ levels)
*/
bool test_deeply_nested_objects() {
TEST_START();
constexpr auto config = json_to_object<R"({
"app_name": "MyApp",
"version": 1.5,
"server": {
"host": "api.example.com",
"port": 443,
"tls": {
"enabled": true,
"cert_path": "/etc/ssl/cert.pem",
"min_version": 1.3
}
}
})">;
static_assert(std::string_view(config.app_name) == "MyApp");
static_assert(config.version == 1.5);
static_assert(std::string_view(config.server.host) == "api.example.com");
static_assert(config.server.port == 443);
static_assert(config.server.tls.enabled == true);
static_assert(std::string_view(config.server.tls.cert_path) == "/etc/ssl/cert.pem");
static_assert(config.server.tls.min_version == 1.3);
ASSERT_EQUAL(std::string_view(config.app_name), "MyApp"sv);
ASSERT_EQUAL(config.server.tls.enabled, true);
ASSERT_EQUAL(config.server.tls.min_version, 1.3);
TEST_SUCCEED();
}
/**
* Test 4: Arrays of primitives
*/
bool test_arrays_primitives() {
TEST_START();
constexpr auto data = json_to_object<R"({
"values": [1, 2, 3, 4, 5],
"flags": [true, false, true]
})">;
static_assert(data.values.size() == 5);
static_assert(data.values[0] == 1);
static_assert(data.values[4] == 5);
static_assert(data.flags.size() == 3);
static_assert(data.flags[0] == true);
static_assert(data.flags[1] == false);
ASSERT_EQUAL(data.values.size(), 5);
ASSERT_EQUAL(data.values[0], 1);
ASSERT_EQUAL(data.values[4], 5);
ASSERT_EQUAL(data.flags.size(), 3);
ASSERT_TRUE(data.flags[0]);
ASSERT_FALSE(data.flags[1]);
TEST_SUCCEED();
}
/**
* Test 5: Arrays of objects
*/
bool test_arrays_of_objects() {
TEST_START();
constexpr auto data = json_to_object<R"({
"users": [
{"name": "Alice", "age": 30},
{"name": "Bob", "age": 25}
]
})">;
static_assert(data.users.size() == 2);
static_assert(std::string_view(data.users[0].name) == "Alice");
static_assert(data.users[0].age == 30);
static_assert(std::string_view(data.users[1].name) == "Bob");
static_assert(data.users[1].age == 25);
ASSERT_EQUAL(data.users.size(), 2);
ASSERT_EQUAL(std::string_view(data.users[0].name), "Alice"sv);
ASSERT_EQUAL(data.users[0].age, 30);
ASSERT_EQUAL(std::string_view(data.users[1].name), "Bob"sv);
ASSERT_EQUAL(data.users[1].age, 25);
TEST_SUCCEED();
}
/**
* Test 6: Nested arrays in objects
*/
bool test_nested_arrays() {
TEST_START();
constexpr auto data = json_to_object<R"({
"config": {
"ports": [8080, 8081, 8082]
}
})">;
static_assert(data.config.ports.size() == 3);
static_assert(data.config.ports[0] == 8080);
static_assert(data.config.ports[2] == 8082);
ASSERT_EQUAL(data.config.ports.size(), 3);
ASSERT_EQUAL(data.config.ports[0], 8080);
ASSERT_EQUAL(data.config.ports[2], 8082);
TEST_SUCCEED();
}
/**
* Test 7: Complex mixed structure
*/
bool test_complex_mixed() {
TEST_START();
constexpr auto config = json_to_object<R"({
"app": "myapp",
"version": 1.0,
"config": {
"ports": [8080, 8081, 8082],
"enabled": true
},
"servers": [
{"host": "server1", "port": 3000},
{"host": "server2", "port": 3001}
]
})">;
static_assert(std::string_view(config.app) == "myapp");
static_assert(config.version == 1.0);
static_assert(config.config.ports[0] == 8080);
static_assert(config.config.enabled == true);
static_assert(std::string_view(config.servers[0].host) == "server1");
static_assert(config.servers[1].port == 3001);
ASSERT_EQUAL(std::string_view(config.app), "myapp"sv);
ASSERT_EQUAL(config.config.ports[0], 8080);
ASSERT_EQUAL(std::string_view(config.servers[0].host), "server1"sv);
ASSERT_EQUAL(config.servers[1].port, 3001);
TEST_SUCCEED();
}
/**
* Test 8: Empty object
*/
bool test_empty_object() {
TEST_START();
constexpr auto config = json_to_object<"{}">;
(void)config; // Suppress unused warning
TEST_SUCCEED();
}
/**
* Test 9: Negative numbers
*/
bool test_negative_numbers() {
TEST_START();
constexpr auto data = json_to_object<R"({
"temperature": -273.15,
"count": -42
})">;
static_assert(data.temperature == -273.15);
static_assert(data.count == -42);
ASSERT_EQUAL(data.temperature, -273.15);
ASSERT_EQUAL(data.count, -42);
TEST_SUCCEED();
}
/**
* Test 10: Whitespace handling
*/
bool test_whitespace() {
TEST_START();
constexpr auto data = json_to_object<R"(
{
"key1" : "value1" ,
"key2" : 42
}
)">;
static_assert(std::string_view(data.key1) == "value1");
static_assert(data.key2 == 42);
ASSERT_EQUAL(std::string_view(data.key1), "value1"sv);
ASSERT_EQUAL(data.key2, 42);
TEST_SUCCEED();
}
/**
* Test 11: String unescaping from inline helpers
*/
bool test_string_unescape() {
TEST_START();
{
constexpr auto pair = unescape_json_string<100>("hello world");
ASSERT_EQUAL(std::string_view(pair.first.data(), pair.second), "hello world"sv);
}
{
constexpr auto pair = unescape_json_string<100>(R"(hello\nworld)");
ASSERT_EQUAL(std::string_view(pair.first.data(), pair.second), "hello\nworld"sv);
}
{
constexpr auto pair = unescape_json_string<100>(R"(quote: \")");
ASSERT_EQUAL(std::string_view(pair.first.data(), pair.second), "quote: \""sv);
}
TEST_SUCCEED();
}
/**
* Test 12: JSON validation
*/
bool test_json_validation() {
TEST_START();
static_assert(validate_json<R"({"valid": true})">());
static_assert(validate_json<R"({"nested": {"deep": 42}})">());
TEST_SUCCEED();
}
/**
* Test 13: User-defined literal
*/
bool test_user_defined_literal() {
TEST_START();
constexpr auto config = R"({"version": 1.0, "name": "test"})"_json;
static_assert(config.version == 1.0);
static_assert(std::string_view(config.name) == "test");
ASSERT_EQUAL(config.version, 1.0);
ASSERT_EQUAL(std::string_view(config.name), "test"sv);
TEST_SUCCEED();
}
/**
* Test 14: Real-time system configuration
*/
bool test_realtime_config() {
TEST_START();
constexpr auto config = json_to_object<R"({
"control_loop_hz": 1000,
"max_acceleration": 9.8,
"min_velocity": -50.0,
"max_velocity": 50.0,
"enable_safety_checks": true,
"log_level": "INFO"
})">;
static_assert(config.control_loop_hz == 1000);
static_assert(config.max_acceleration == 9.8);
static_assert(config.enable_safety_checks == true);
ASSERT_EQUAL(config.control_loop_hz, 1000);
ASSERT_EQUAL(config.max_acceleration, 9.8);
ASSERT_EQUAL(config.min_velocity, -50.0);
ASSERT_EQUAL(config.max_velocity, 50.0);
ASSERT_TRUE(config.enable_safety_checks);
ASSERT_EQUAL(std::string_view(config.log_level), "INFO"sv);
TEST_SUCCEED();
}
/**
* Test 15: Future #embed support demonstration
*
* Note: This test shows how external JSON files would be used with #embed.
* Currently uses inline JSON matching test_config.json for compatibility.
*/
bool test_external_json_embed() {
TEST_START();
// Future C++26 with #embed:
// constexpr auto config = json_to_object<#embed "test_config.json">;
// Current workaround - inline the JSON from test_config.json
constexpr auto config = json_to_object<R"({
"system_name": "RealTimeController",
"version": "2.1.0",
"control_loop_hz": 1000,
"max_latency_us": 500,
"enable_diagnostics": true,
"log_level": "INFO"
})">;
static_assert(std::string_view(config.system_name) == "RealTimeController");
static_assert(std::string_view(config.version) == "2.1.0");
static_assert(config.control_loop_hz == 1000);
static_assert(config.max_latency_us == 500);
static_assert(config.enable_diagnostics == true);
static_assert(std::string_view(config.log_level) == "INFO");
ASSERT_EQUAL(std::string_view(config.system_name), "RealTimeController"sv);
ASSERT_EQUAL(std::string_view(config.version), "2.1.0"sv);
ASSERT_EQUAL(config.control_loop_hz, 1000);
ASSERT_EQUAL(config.max_latency_us, 500);
ASSERT_TRUE(config.enable_diagnostics);
ASSERT_EQUAL(std::string_view(config.log_level), "INFO"sv);
TEST_SUCCEED();
}
bool run() {
return test_basic_object() &&
test_nested_objects() &&
test_deeply_nested_objects() &&
test_arrays_primitives() &&
test_arrays_of_objects() &&
test_nested_arrays() &&
test_complex_mixed() &&
test_empty_object() &&
test_negative_numbers() &&
test_whitespace() &&
test_string_unescape() &&
test_json_validation() &&
test_user_defined_literal() &&
test_realtime_config() &&
test_external_json_embed();
}
} // namespace compile_time_json_tests
int main(int argc, char *argv[]) {
#if SIMDJSON_STATIC_REFLECTION
return test_main(argc, argv, compile_time_json_tests::run);
#else
std::cout << "Compile-time JSON tests require SIMDJSON_STATIC_REFLECTION=ON" << std::endl;
return EXIT_SUCCESS;
#endif
}
+8
View File
@@ -0,0 +1,8 @@
{
"system_name": "RealTimeController",
"version": "2.1.0",
"control_loop_hz": 1000,
"max_latency_us": 500,
"enable_diagnostics": true,
"log_level": "INFO"
}