adding concept examples to the compile-time JSON. (#2538)

This commit is contained in:
Daniel Lemire
2025-11-04 15:06:40 -05:00
committed by GitHub
parent 235dbc5369
commit 19ff7a572d
5 changed files with 171 additions and 7 deletions
+63
View File
@@ -1,6 +1,7 @@
# Parse json at compile time
* [Introduction](#introduction)
* [Example](#example)
* [Concepts](#concepts)
* [Loading from disk](#loading-from-disk)
* [Limitations (compile-time errors)](#limitations-compile-time-errors)
@@ -106,6 +107,68 @@ static_assert(arr.size() == 3);
static_assert(arr[1] == 2);
```
## Concepts
Given that the parsed data is made of structures that depend on the JSON input, you might
want to check that it conforms to your expectation. You can do so with concepts.
Let us consider this example:
```cpp
constexpr auto config = R"(
[
{ "name": "Alice", "age": 30 },
{ "name": "Bob", "age": 25 },
{ "name": "Charlie", "age": 35 }
]
)"_json;
```
You might want to ensure that the result is an array of persons. You can define your
expection with concepts like so:
```cpp
template <typename T>
concept person = requires(T p) {
std::string_view(p.name); // has name field convertible to string_view
p.age; // has age field
requires std::is_integral_v<decltype(p.age)>; // age is integral
};
/**
* Concept to validate that a type is an array of person objects
*/
template <typename T>
concept array_of_person = requires(T arr) {
arr.size(); // has size method
arr[0]; // can access elements with []
requires person<decltype(arr[0])>; // elements satisfy person concept
};
```
And then a simple static assert with `decltype` is sufficient to check that the expectation is met:
```cpp
constexpr auto config = R"(
[
{ "name": "Alice", "age": 30 },
{ "name": "Bob", "age": 25 },
{ "name": "Charlie", "age": 35 }
]
)"_json;
// Validate that the array satisfies the array_of_person concept
static_assert(array_of_person<decltype(config)>);
```
## Loading from disk
In practice, you may have a JSON file, say `json_data` that you want to parse
+4
View File
@@ -265,6 +265,8 @@ struct simdjson_result_base : protected std::pair<T, error_code> {
*/
simdjson_inline T&& value_unsafe() && noexcept;
using value_type = T;
using error_type = error_code;
}; // struct simdjson_result_base
} // namespace internal
@@ -376,6 +378,8 @@ struct simdjson_result : public internal::simdjson_result_base<T> {
*/
simdjson_inline T&& value_unsafe() && noexcept;
using value_type = T;
using error_type = error_code;
}; // struct simdjson_result
#if SIMDJSON_EXCEPTIONS
@@ -138,6 +138,9 @@ struct implementation_simdjson_result_base {
*/
simdjson_inline T&& value_unsafe() && noexcept;
using value_type = T;
using error_type = error_code;
protected:
/** users should never directly access first and second. **/
T first{}; /** Users should never directly access 'first'. **/
+6 -6
View File
@@ -87,16 +87,16 @@ bool cast_tester<T>::test_get_error(simdjson_result<element> element, error_code
template<typename T>
bool cast_tester<T>::test_get_t(element element, T expected) {
auto actual = element.get<T>();
ASSERT_SUCCESS(actual.error());
return assert_equal(actual.value_unsafe(), expected);
T value;
ASSERT_SUCCESS(element.get<T>().get(value));
return assert_equal(value, expected);
}
template<typename T>
bool cast_tester<T>::test_get_t(simdjson_result<element> element, T expected) {
auto actual = element.get<T>();
ASSERT_SUCCESS(actual.error());
return assert_equal(actual.value_unsafe(), expected);
T value;
ASSERT_SUCCESS(element.get<T>().get(value));
return assert_equal(value, expected);
}
template<typename T>
+95 -1
View File
@@ -597,6 +597,98 @@ bool test_top_level_array_example() {
TEST_SUCCEED();
}
/**
* Concept to validate that a type represents a person with name and age
*/
template <typename T>
concept person = requires(T p) {
std::string_view(p.name); // has name field convertible to string_view
p.age; // has age field
requires std::is_integral_v<decltype(p.age)>; // age is integral
};
/**
* Concept to validate that a type is an array of person objects
*/
template <typename T>
concept array_of_person = requires(T arr) {
arr.size(); // has size method
arr[0]; // can access elements with []
requires person<decltype(arr[0])>; // elements satisfy person concept
};
/**
* Test: Array of objects with concept validation
*/
bool test_array_of_objects_with_concept() {
TEST_START();
constexpr auto config = R"(
[
{ "name": "Alice", "age": 30 },
{ "name": "Bob", "age": 25 },
{ "name": "Charlie", "age": 35 }
]
)"_json;
std::print("Array size: {}\n", config.size());
static_assert(config.size() == 3);
// Validate that the array satisfies the array_of_person concept
static_assert(array_of_person<decltype(config)>);
// Test the actual values
static_assert(std::string_view(config[0].name) == "Alice");
static_assert(config[0].age == 30);
static_assert(std::string_view(config[1].name) == "Bob");
static_assert(config[1].age == 25);
static_assert(std::string_view(config[2].name) == "Charlie");
static_assert(config[2].age == 35);
// Runtime assertions
ASSERT_EQUAL(config.size(), 3);
ASSERT_EQUAL(std::string_view(config[0].name), "Alice"sv);
ASSERT_EQUAL(config[0].age, 30);
ASSERT_EQUAL(std::string_view(config[1].name), "Bob"sv);
ASSERT_EQUAL(config[1].age, 25);
ASSERT_EQUAL(std::string_view(config[2].name), "Charlie"sv);
ASSERT_EQUAL(config[2].age, 35);
TEST_SUCCEED();
}
/**
* Test: #embed support for external JSON files (C++26)
*/
bool test_embed_twitter_json() {
TEST_START();
// C++26 #embed allows embedding files directly into the binary at compile time
// This creates a const char array with the file contents plus null terminator
constexpr const char twitter_json[] = {
#embed TWITTER_JSON
, 0
};
// Parse the embedded JSON at compile time
constexpr auto parsed_twitter = simdjson::compile_time::parse_json<twitter_json>();
// Verify the structure - twitter.json should have a "statuses" array
static_assert(parsed_twitter.statuses.size() > 0);
// Runtime verification
ASSERT_TRUE(parsed_twitter.statuses.size() > 0);
std::cout << "Successfully parsed embedded twitter.json with "
<< parsed_twitter.statuses.size() << " statuses" << std::endl;
TEST_SUCCEED();
}
bool run() {
return test_basic_object() &&
test_nested_objects() &&
@@ -620,7 +712,9 @@ bool run() {
array_of_objects() &&
test_user_config_example() &&
test_nested_servers_example() &&
test_top_level_array_example();
test_top_level_array_example() &&
test_array_of_objects_with_concept() &&
test_embed_twitter_json();
}
} // namespace compile_time_json_tests