From da2a85b50095789b4626712119c6534fc5096355 Mon Sep 17 00:00:00 2001 From: ToruNiina Date: Sat, 15 Jun 2024 19:23:05 +0900 Subject: [PATCH] feat: add examples --- examples/boost_container/.gitignore | 1 + examples/boost_container/CMakeLists.txt | 8 + examples/boost_container/README.md | 13 ++ examples/boost_container/container.cpp | 47 ++++++ examples/boost_multiprecision/.gitignore | 1 + examples/boost_multiprecision/CMakeLists.txt | 8 + examples/boost_multiprecision/README.md | 13 ++ .../boost_multiprecision/multiprecision.cpp | 55 +++++++ examples/parse_file/.gitignore | 4 + examples/parse_file/CMakeLists.txt | 19 +++ examples/parse_file/array_example.cpp | 152 ++++++++++++++++++ examples/parse_file/array_example.toml | 12 ++ .../parse_file/array_of_tables_example.cpp | 135 ++++++++++++++++ .../parse_file/array_of_tables_example.toml | 34 ++++ examples/parse_file/key_example.cpp | 74 +++++++++ examples/parse_file/key_example.toml | 26 +++ examples/parse_file/spec_example.cpp | 86 ++++++++++ examples/parse_file/spec_example.toml | 23 +++ examples/reflect/.gitignore | 1 + examples/reflect/CMakeLists.txt | 18 +++ examples/reflect/README.md | 13 ++ examples/reflect/reflect.cpp | 38 +++++ examples/reflect/reflect.hpp | 61 +++++++ examples/unicode/.gitignore | 1 + examples/unicode/CMakeLists.txt | 15 ++ examples/unicode/README.md | 15 ++ examples/unicode/canonicalize.cpp | 79 +++++++++ examples/unicode/input.toml | 2 + 28 files changed, 954 insertions(+) create mode 100644 examples/boost_container/.gitignore create mode 100644 examples/boost_container/CMakeLists.txt create mode 100644 examples/boost_container/README.md create mode 100644 examples/boost_container/container.cpp create mode 100644 examples/boost_multiprecision/.gitignore create mode 100644 examples/boost_multiprecision/CMakeLists.txt create mode 100644 examples/boost_multiprecision/README.md create mode 100644 examples/boost_multiprecision/multiprecision.cpp create mode 100644 examples/parse_file/.gitignore create mode 100644 examples/parse_file/CMakeLists.txt create mode 100644 examples/parse_file/array_example.cpp create mode 100644 examples/parse_file/array_example.toml create mode 100644 examples/parse_file/array_of_tables_example.cpp create mode 100644 examples/parse_file/array_of_tables_example.toml create mode 100644 examples/parse_file/key_example.cpp create mode 100644 examples/parse_file/key_example.toml create mode 100644 examples/parse_file/spec_example.cpp create mode 100644 examples/parse_file/spec_example.toml create mode 100644 examples/reflect/.gitignore create mode 100644 examples/reflect/CMakeLists.txt create mode 100644 examples/reflect/README.md create mode 100644 examples/reflect/reflect.cpp create mode 100644 examples/reflect/reflect.hpp create mode 100644 examples/unicode/.gitignore create mode 100644 examples/unicode/CMakeLists.txt create mode 100644 examples/unicode/README.md create mode 100644 examples/unicode/canonicalize.cpp create mode 100644 examples/unicode/input.toml diff --git a/examples/boost_container/.gitignore b/examples/boost_container/.gitignore new file mode 100644 index 0000000..5d3a370 --- /dev/null +++ b/examples/boost_container/.gitignore @@ -0,0 +1 @@ +container diff --git a/examples/boost_container/CMakeLists.txt b/examples/boost_container/CMakeLists.txt new file mode 100644 index 0000000..3b8540b --- /dev/null +++ b/examples/boost_container/CMakeLists.txt @@ -0,0 +1,8 @@ +find_package(Boost 1.81.0) + +if(Boost_FOUND) + add_executable(container container.cpp) + target_link_libraries(container PRIVATE toml11::toml11 Boost::boost) + set_target_properties(container PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") +endif() diff --git a/examples/boost_container/README.md b/examples/boost_container/README.md new file mode 100644 index 0000000..a8a2d58 --- /dev/null +++ b/examples/boost_container/README.md @@ -0,0 +1,13 @@ +# container + +Use `boost::container::vector` and `boost::unordered_flat_map` as `array_type` and `table_type`. + +## build + +Install [boost](https://boost.org), especially after 1.81.0 that supports `unordered_flat_map`. + +Then, build toml11 with `-DTOML11_BUILD_EXAMPLES=ON` + +```cpp +$ cmake -B ./build/ -DTOML11_BUILD_EXAMPLES=ON +``` diff --git a/examples/boost_container/container.cpp b/examples/boost_container/container.cpp new file mode 100644 index 0000000..a73d489 --- /dev/null +++ b/examples/boost_container/container.cpp @@ -0,0 +1,47 @@ +#include +#include + +#include + +#include + +struct boost_config +{ + using comment_type = toml::preserve_comments; + + using boolean_type = bool; + using integer_type = std::int64_t; + using floating_type = double; + using string_type = std::string; + + template + using array_type = boost::container::small_vector; + template + using table_type = boost::unordered_flat_map; + + static toml::result + parse_int(const std::string& str, const toml::source_location src, const std::uint8_t base) + { + return toml::read_int(str, src, base); + } + static toml::result + parse_float(const std::string& str, const toml::source_location src, const bool is_hex) + { + return toml::read_float(str, src, is_hex); + } +}; + +int main(int argc, char** argv) +{ + if(argc != 2) + { + std::cerr << "usage: ./container " << std::endl; + return 1; + } + + const auto input = toml::parse_str(argv[1]); + + std::cout << toml::format(input) << std::endl; + + return 0; +} diff --git a/examples/boost_multiprecision/.gitignore b/examples/boost_multiprecision/.gitignore new file mode 100644 index 0000000..49b9b49 --- /dev/null +++ b/examples/boost_multiprecision/.gitignore @@ -0,0 +1 @@ +multiprecision diff --git a/examples/boost_multiprecision/CMakeLists.txt b/examples/boost_multiprecision/CMakeLists.txt new file mode 100644 index 0000000..f822783 --- /dev/null +++ b/examples/boost_multiprecision/CMakeLists.txt @@ -0,0 +1,8 @@ +find_package(Boost 1.67.0) + +if(Boost_FOUND) + add_executable(multiprecision multiprecision.cpp) + target_link_libraries(multiprecision PRIVATE toml11::toml11 Boost::boost) + set_target_properties(multiprecision PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") +endif() diff --git a/examples/boost_multiprecision/README.md b/examples/boost_multiprecision/README.md new file mode 100644 index 0000000..41df535 --- /dev/null +++ b/examples/boost_multiprecision/README.md @@ -0,0 +1,13 @@ +# multiprecision + +Use `boost::multiprecision` as `integer_type` and `floating_type`. + +## build + +Install [boost](https://boost.org). + +Then, build toml11 with `-DTOML11_BUILD_EXAMPLES=ON` + +```cpp +$ cmake -B ./build/ -DTOML11_BUILD_EXAMPLES=ON +``` diff --git a/examples/boost_multiprecision/multiprecision.cpp b/examples/boost_multiprecision/multiprecision.cpp new file mode 100644 index 0000000..c3a34a5 --- /dev/null +++ b/examples/boost_multiprecision/multiprecision.cpp @@ -0,0 +1,55 @@ +#include +#include +#include + +#include + +struct large_num_config +{ + using comment_type = toml::preserve_comments; + + using boolean_type = bool; + using integer_type = boost::multiprecision::cpp_int; + using floating_type = boost::multiprecision::cpp_bin_float_oct; + using string_type = std::string; + + template + using array_type = std::vector; + template + using table_type = std::unordered_map; + + static toml::result + parse_int(const std::string& str, const toml::source_location src, const std::uint8_t base) + { + return toml::read_int(str, src, base); + } + static toml::result + parse_float(const std::string& str, const toml::source_location src, const bool is_hex) + { + return toml::read_float(str, src, is_hex); + } +}; + +int main() +{ + const std::string input_str(R"( +large_int_dec = 10_000_000_000_000_000_000 +large_int_hex = 0x0001_0000_0000_0000_0000 +large_float_pi = 3.1415926535897932384626433832795028842 + )"); + + const auto input = toml::parse_str(input_str); + + std::cout << "int64_t max = " << (std::numeric_limits::max)() << std::endl; + + std::cout << "large_int_dec = " << input.at("large_int_dec" ).as_integer() << std::endl; + std::cout << "large_int_hex = " << input.at("large_int_hex" ).as_integer() << std::endl; + std::cout << "large_float_pi = " + << std::setprecision(std::numeric_limits::max_digits10 - 1) + << input.at("large_float_pi").as_floating() << std::endl; + + std::cout << "=================" << std::endl; + std::cout << toml::format(input) << std::endl; + + return 0; +} diff --git a/examples/parse_file/.gitignore b/examples/parse_file/.gitignore new file mode 100644 index 0000000..7c7bf1a --- /dev/null +++ b/examples/parse_file/.gitignore @@ -0,0 +1,4 @@ +array_example +array_of_tables_example +key_example +spec_example diff --git a/examples/parse_file/CMakeLists.txt b/examples/parse_file/CMakeLists.txt new file mode 100644 index 0000000..2060859 --- /dev/null +++ b/examples/parse_file/CMakeLists.txt @@ -0,0 +1,19 @@ +add_executable(spec_example spec_example.cpp) +target_link_libraries(spec_example PRIVATE toml11::toml11) +set_target_properties(spec_example PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") + +add_executable(key_example key_example.cpp) +target_link_libraries(key_example PRIVATE toml11::toml11) +set_target_properties(key_example PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") + +add_executable(array_example array_example.cpp) +target_link_libraries(array_example PRIVATE toml11::toml11) +set_target_properties(array_example PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") + +add_executable(array_of_tables_example array_of_tables_example.cpp) +target_link_libraries(array_of_tables_example PRIVATE toml11::toml11) +set_target_properties(array_of_tables_example PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") diff --git a/examples/parse_file/array_example.cpp b/examples/parse_file/array_example.cpp new file mode 100644 index 0000000..6cc0e23 --- /dev/null +++ b/examples/parse_file/array_example.cpp @@ -0,0 +1,152 @@ +#include + +#include + +#include + +int main() +{ + const auto root = toml::parse("array_example.toml"); + + // using member functions + { + assert(root.at("integers").is_array()); + assert(root.at("integers").at(0).as_integer() == 1); + assert(root.at("integers").at(1).as_integer() == 2); + assert(root.at("integers").at(2).as_integer() == 3); + + assert(root.at("colors").at(0).as_string() == "red"); + assert(root.at("colors").at(1).as_string() == "yellow"); + assert(root.at("colors").at(2).as_string() == "green"); + + assert(root.at("nested_arrays_of_ints").at(0).at(0).as_integer() == 1); + assert(root.at("nested_arrays_of_ints").at(0).at(1).as_integer() == 2); + assert(root.at("nested_arrays_of_ints").at(1).at(0).as_integer() == 3); + assert(root.at("nested_arrays_of_ints").at(1).at(1).as_integer() == 4); + assert(root.at("nested_arrays_of_ints").at(1).at(2).as_integer() == 5); + + assert(root.at("nested_mixed_array").at(0).at(0).as_integer() == 1); + assert(root.at("nested_mixed_array").at(0).at(1).as_integer() == 2); + assert(root.at("nested_mixed_array").at(1).at(0).as_string() == "a"); + assert(root.at("nested_mixed_array").at(1).at(1).as_string() == "b"); + assert(root.at("nested_mixed_array").at(1).at(2).as_string() == "c"); + + assert(root.at("string_array").at(0).as_string() == "all"); + assert(root.at("string_array").at(1).as_string() == "strings"); + assert(root.at("string_array").at(2).as_string() == "are the same"); + assert(root.at("string_array").at(3).as_string() == "type"); + + assert(root.at("numbers").at(0).as_floating() == std::stod("0.1")); + assert(root.at("numbers").at(1).as_floating() == std::stod("0.2")); + assert(root.at("numbers").at(2).as_floating() == std::stod("0.5")); + assert(root.at("numbers").at(3).as_integer() == 1); + assert(root.at("numbers").at(4).as_integer() == 2); + assert(root.at("numbers").at(5).as_integer() == 5); + + assert(root.at("contributors").at(0).as_string() == "Foo Bar "); + assert(root.at("contributors").at(1).at("name" ).as_string() == "Baz Qux"); + assert(root.at("contributors").at(1).at("email").as_string() == "bazqux@example.com"); + assert(root.at("contributors").at(1).at("url" ).as_string() == "https://example.com/bazqux"); + } + + // using toml::find + { + assert(toml::find(root, "integers", 0) == 1); + assert(toml::find(root, "integers", 1) == 2); + assert(toml::find(root, "integers", 2) == 3); + + const auto integers = toml::find>(root, "integers"); + assert(integers.at(0) == 1); + assert(integers.at(1) == 2); + assert(integers.at(2) == 3); + + assert(toml::find(root, "colors", 0) == "red"); + assert(toml::find(root, "colors", 1) == "yellow"); + assert(toml::find(root, "colors", 2) == "green"); + + const auto colors = toml::find>(root, "colors"); + assert(colors.at(0) == "red"); + assert(colors.at(1) == "yellow"); + assert(colors.at(2) == "green"); + + assert(toml::find(root, "nested_arrays_of_ints", 0, 0) == 1); + assert(toml::find(root, "nested_arrays_of_ints", 0, 1) == 2); + assert(toml::find(root, "nested_arrays_of_ints", 1, 0) == 3); + assert(toml::find(root, "nested_arrays_of_ints", 1, 1) == 4); + assert(toml::find(root, "nested_arrays_of_ints", 1, 2) == 5); + + const auto nested_arrays_of_ints = toml::find>>(root, "nested_arrays_of_ints"); + assert(nested_arrays_of_ints.at(0).at(0) == 1); + assert(nested_arrays_of_ints.at(0).at(1) == 2); + assert(nested_arrays_of_ints.at(1).at(0) == 3); + assert(nested_arrays_of_ints.at(1).at(1) == 4); + assert(nested_arrays_of_ints.at(1).at(2) == 5); + + assert(toml::find(root, "nested_mixed_array", 0, 0) == 1); + assert(toml::find(root, "nested_mixed_array", 0, 1) == 2); + assert(toml::find(root, "nested_mixed_array", 1, 0) == "a"); + assert(toml::find(root, "nested_mixed_array", 1, 1) == "b"); + assert(toml::find(root, "nested_mixed_array", 1, 2) == "c"); + + const auto nested_mixed_array = toml::find< + std::pair, std::vector> + >(root, "nested_mixed_array"); + + assert(nested_mixed_array.first .at(0) == 1); + assert(nested_mixed_array.first .at(1) == 2); + assert(nested_mixed_array.second.at(0) == "a"); + assert(nested_mixed_array.second.at(1) == "b"); + assert(nested_mixed_array.second.at(2) == "c"); + + assert(toml::find(root, "string_array", 0) == "all"); + assert(toml::find(root, "string_array", 1) == "strings"); + assert(toml::find(root, "string_array", 2) == "are the same"); + assert(toml::find(root, "string_array", 3) == "type"); + + const auto numbers = toml::find< + std::tuple + >(root, "numbers"); + + assert(std::get<0>(numbers) == std::stod("0.1")); + assert(std::get<1>(numbers) == std::stod("0.2")); + assert(std::get<2>(numbers) == std::stod("0.5")); + assert(std::get<3>(numbers) == 1); + assert(std::get<4>(numbers) == 2); + assert(std::get<5>(numbers) == 5); + + struct contributor_t + { + contributor_t(const toml::value& v) + { + if(v.is_string()) + { + name = v.as_string(); + } + else + { + assert(v.is_table()); + name = toml::find(v, "name"); + email = toml::find_or(v, "email", std::string("")); + url = toml::find_or(v, "url" , std::string("")); + } + } + + std::string name; + std::string email; + std::string url; + }; + + const auto contributors = toml::find>(root, "contributors"); + + assert(contributors.at(0).name == "Foo Bar "); + assert(contributors.at(0).email == ""); + assert(contributors.at(0).url == ""); + assert(contributors.at(1).name == "Baz Qux"); + assert(contributors.at(1).email == "bazqux@example.com"); + assert(contributors.at(1).url == "https://example.com/bazqux"); + } + + std::cout << "ok." << std::endl; + + return 0; +} diff --git a/examples/parse_file/array_example.toml b/examples/parse_file/array_example.toml new file mode 100644 index 0000000..467a8bc --- /dev/null +++ b/examples/parse_file/array_example.toml @@ -0,0 +1,12 @@ +integers = [ 1, 2, 3 ] +colors = [ "red", "yellow", "green" ] +nested_arrays_of_ints = [ [ 1, 2 ], [3, 4, 5] ] +nested_mixed_array = [ [ 1, 2 ], ["a", "b", "c"] ] +string_array = [ "all", 'strings', """are the same""", '''type''' ] + +# Mixed-type arrays are allowed +numbers = [ 0.1, 0.2, 0.5, 1, 2, 5 ] +contributors = [ + "Foo Bar ", + { name = "Baz Qux", email = "bazqux@example.com", url = "https://example.com/bazqux" } +] diff --git a/examples/parse_file/array_of_tables_example.cpp b/examples/parse_file/array_of_tables_example.cpp new file mode 100644 index 0000000..01378aa --- /dev/null +++ b/examples/parse_file/array_of_tables_example.cpp @@ -0,0 +1,135 @@ +#include + +#include +#include + +#include + +int main() +{ + const auto root = toml::parse("array_of_tables_example.toml"); + + // using member functions + { + assert(root.at("points").is_array()); + assert(root.at("points").at(0).is_table()); + assert(root.at("points").at(1).is_table()); + assert(root.at("points").at(2).is_table()); + + assert(root.at("points").at(0).at("x").as_integer() == 1); + assert(root.at("points").at(0).at("y").as_integer() == 2); + assert(root.at("points").at(0).at("z").as_integer() == 3); + + assert(root.at("points").at(1).at("x").as_integer() == 7); + assert(root.at("points").at(1).at("y").as_integer() == 8); + assert(root.at("points").at(1).at("z").as_integer() == 9); + + assert(root.at("points").at(2).at("x").as_integer() == 2); + assert(root.at("points").at(2).at("y").as_integer() == 4); + assert(root.at("points").at(2).at("z").as_integer() == 8); + + + assert(root.at("products").at(0).at("name").as_string() == "Hammer"); + assert(root.at("products").at(0).at("sku" ).as_integer() == 738594937); + + assert(root.at("products").at(1).as_table().empty()); + + assert(root.at("products").at(2).at("name" ).as_string() == "Nail"); + assert(root.at("products").at(2).at("sku" ).as_integer() == 284758393); + assert(root.at("products").at(2).at("color").as_string() == "gray"); + + assert(root.at("fruits").at(0).at("name").as_string() == "apple"); + assert(root.at("fruits").at(0).at("physical").at("color").as_string() == "red"); + assert(root.at("fruits").at(0).at("physical").at("shape").as_string() == "round"); + assert(root.at("fruits").at(0).at("varieties").at(0).at("name").as_string() == "red delicious"); + assert(root.at("fruits").at(0).at("varieties").at(1).at("name").as_string() == "granny smith"); + + assert(root.at("fruits").at(1).at("name").as_string() == "banana"); + assert(root.at("fruits").at(1).at("varieties").at(0).at("name").as_string() == "plantain"); + } + + // using toml::find + { + assert(toml::find(root, "points", 0, "x") == 1); + assert(toml::find(root, "points", 0, "y") == 2); + assert(toml::find(root, "points", 0, "z") == 3); + + assert(toml::find(root, "points", 1, "x") == 7); + assert(toml::find(root, "points", 1, "y") == 8); + assert(toml::find(root, "points", 1, "z") == 9); + + assert(toml::find(root, "points", 2, "x") == 2); + assert(toml::find(root, "points", 2, "y") == 4); + assert(toml::find(root, "points", 2, "z") == 8); + + const auto points = toml::find< + std::vector> + >(root, "points"); + + assert(points.at(0).at("x") == 1); + assert(points.at(0).at("y") == 2); + assert(points.at(0).at("z") == 3); + + assert(points.at(1).at("x") == 7); + assert(points.at(1).at("y") == 8); + assert(points.at(1).at("z") == 9); + + assert(points.at(2).at("x") == 2); + assert(points.at(2).at("y") == 4); + assert(points.at(2).at("z") == 8); + + struct product_t + { + product_t(const toml::value& v) + : name (toml::find_or(v, "name", "")), + sku (toml::find_or(v, "sku", 0)), + color(toml::find_or(v, "color", "")) + {} + std::string name; + std::uint64_t sku; + std::string color; + }; + + const auto products = toml::find>(root, "products"); + + assert(products.at(0).name == "Hammer"); + assert(products.at(0).sku == 738594937); + assert(products.at(0).color == ""); + + assert(products.at(1).name == ""); + assert(products.at(1).sku == 0); + assert(products.at(1).color == ""); + + assert(products.at(2).name == "Nail"); + assert(products.at(2).sku == 284758393); + assert(products.at(2).color == "gray"); + + struct fruit_t + { + fruit_t(const toml::value& v) + : name(toml::find(v, "name")), + physical (toml::find>(v, "physical")), + varieties(toml::find>>(v, "varieties")) + {} + + std::string name; + std::map physical; + std::vector> varieties; + }; + + const auto fruits = toml::find>(root, "fruits"); + + assert(fruits.at(0).name == "apple"); + assert(fruits.at(0).physical.at("color") == "red"); + assert(fruits.at(0).physical.at("shape") == "round"); + assert(fruits.at(0).varieties.at(0).at("name") == "red delicious"); + assert(fruits.at(0).varieties.at(1).at("name") == "granny smith"); + + assert(fruits.at(1).name == "banana"); + assert(fruits.at(1).varieties.at(0).at("name") == "plantain"); + } + + std::cout << "ok." << std::endl; + + return 0; +} diff --git a/examples/parse_file/array_of_tables_example.toml b/examples/parse_file/array_of_tables_example.toml new file mode 100644 index 0000000..68437d1 --- /dev/null +++ b/examples/parse_file/array_of_tables_example.toml @@ -0,0 +1,34 @@ +points = [ { x = 1, y = 2, z = 3 }, + { x = 7, y = 8, z = 9 }, + { x = 2, y = 4, z = 8 } ] + +[[products]] +name = "Hammer" +sku = 738594937 + +[[products]] # empty table within the array + +[[products]] +name = "Nail" +sku = 284758393 + +color = "gray" + +[[fruits]] +name = "apple" + +[fruits.physical] # subtable +color = "red" +shape = "round" + +[[fruits.varieties]] # nested array of tables +name = "red delicious" + +[[fruits.varieties]] +name = "granny smith" + +[[fruits]] +name = "banana" + +[[fruits.varieties]] +name = "plantain" diff --git a/examples/parse_file/key_example.cpp b/examples/parse_file/key_example.cpp new file mode 100644 index 0000000..d18137d --- /dev/null +++ b/examples/parse_file/key_example.cpp @@ -0,0 +1,74 @@ +#include + +#include + +#include + +int main() +{ + const auto root = toml::parse("key_example.toml"); + + assert(root.is_table()); + assert(root.comments().size() == 2); + assert(root.comments().at(0) == " This is a TOML document."); + assert(root.comments().at(1) == " This contains most of the examples in the spec (from https://toml.io)."); + + assert(root.size() == 1); + assert(root.contains("key")); + assert(root.at("key").is_table()); + + // using member functions + { + const toml::value& keys = root.at("key"); + assert(keys.at("key" ).as_string() == "value"); + assert(keys.at("bare_key").as_string() == "value"); + assert(keys.at("bare-key").as_string() == "value"); + assert(keys.at("1234" ).as_string() == "value"); + + assert(keys.at("127.0.0.1" ).as_string() == "value"); + assert(keys.at("character encoding").as_string() == "value"); + assert(keys.at("ʎǝʞ" ).as_string() == "value"); // requires /utf-8 in MSVC + assert(keys.at("key2" ).as_string() == "value"); + assert(keys.at("quoted \"value\"" ).as_string() == "value"); + + assert(keys.at("").as_string() == "blank"); + + assert(keys.at("fruit").at("apple").at("skin" ).as_string() == "thin"); + assert(keys.at("fruit").at("apple").at("color").as_string() == "red"); + assert(keys.at("fruit").at("orange").at("skin" ).as_string() == "thick"); + assert(keys.at("fruit").at("orange").at("color").as_string() == "orange"); + + assert(keys.at("site").at("google.com").as_boolean() == true); + assert(keys.at("3").at("14159").as_string() == "pi"); + } + + // using toml::find + { + assert(toml::find(root, "keys", "key" ) == "value"); + assert(toml::find(root, "keys", "bare_key") == "value"); + assert(toml::find(root, "keys", "bare-key") == "value"); + assert(toml::find(root, "keys", "1234" ) == "value"); + + const toml::value& keys = toml::find(root, "keys"); + + assert(toml::find(keys, "127.0.0.1" ) == "value"); + assert(toml::find(keys, "character encoding") == "value"); + assert(toml::find(keys, "ʎǝʞ" ) == "value"); // requires /utf-8 in MSVC + assert(toml::find(keys, "key2" ) == "value"); + assert(toml::find(keys, "quoted \"value\"" ) == "value"); + + assert(toml::find(keys, "") == "blank"); + + assert(toml::find(keys, "fruit", "apple" , "skin" ) == "thin"); + assert(toml::find(keys, "fruit", "apple" , "color") == "red"); + assert(toml::find(keys, "fruit", "orange", "skin" ) == "thick"); + assert(toml::find(keys, "fruit", "orange", "color") == "orange"); + + assert(toml::find(keys, "site", "google.com") == true); + assert(toml::find(keys, "3", "14159") == "pi"); + } + + std::cout << "ok." << std::endl; + + return 0; +} diff --git a/examples/parse_file/key_example.toml b/examples/parse_file/key_example.toml new file mode 100644 index 0000000..9a6a2ef --- /dev/null +++ b/examples/parse_file/key_example.toml @@ -0,0 +1,26 @@ +# This is a TOML document. +# This contains most of the examples in the spec (from https://toml.io). + +[keys] + +key = "value" +bare_key = "value" +bare-key = "value" +1234 = "value" + +"127.0.0.1" = "value" +"character encoding" = "value" +"ʎǝʞ" = "value" +'key2' = "value" +'quoted "value"' = "value" + +"" = "blank" + +fruits.apple.skin = "thin" +fruits.apple.color = "red" + +fruits.orange.skin = "thick" +fruits.orange.color = "orange" + +site."google.com" = true +3.14159 = "pi" diff --git a/examples/parse_file/spec_example.cpp b/examples/parse_file/spec_example.cpp new file mode 100644 index 0000000..16a7769 --- /dev/null +++ b/examples/parse_file/spec_example.cpp @@ -0,0 +1,86 @@ +#include + +#include +#include + +#include + +int main() +{ + const auto root = toml::parse("spec_example.toml"); + + // using member functions + { + assert(root.at("title").as_string() == "TOML Example"); + + assert(root.at("owner").at("name").as_string() == "Tom Preston-Werner"); + + const auto dob = root.at("owner").at("dob" ).as_offset_datetime(); + assert(dob.date .year == 1979); + assert(dob.date .month == static_cast(toml::month_t::May)); + assert(dob.date .day == 27); + assert(dob.time .hour == 7); + assert(dob.time .minute == 32); + assert(dob.time .second == 0); + assert(dob.offset.hour == -8); + assert(dob.offset.minute == 0); + + assert(root.at("database").at("enabled").as_boolean()); + assert(root.at("database").at("ports").at(0).as_integer() == 8000); + assert(root.at("database").at("ports").at(1).as_integer() == 8001); + assert(root.at("database").at("ports").at(2).as_integer() == 8002); + assert(root.at("database").at("data").at(0).at(0).as_string() == "delta"); + assert(root.at("database").at("data").at(0).at(1).as_string() == "phi"); + assert(root.at("database").at("data").at(1).at(0).as_floating() == 3.14); + assert(root.at("database").at("temp_targets").at("cpu" ).as_floating() == 79.5); + assert(root.at("database").at("temp_targets").at("case").as_floating() == 72.0); + + assert(root.at("servers").at("alpha").at("ip" ).as_string() == "10.0.0.1"); + assert(root.at("servers").at("alpha").at("role").as_string() == "frontend"); + assert(root.at("servers").at("beta" ).at("ip" ).as_string() == "10.0.0.2"); + assert(root.at("servers").at("beta" ).at("role").as_string() == "backend"); + } + + // using toml::find + { + assert(toml::find(root, "title") == "TOML Example"); + + assert(toml::find(root, "owner", "name") == "Tom Preston-Werner"); + + const auto dob = toml::find(root, "owner", "dob"); + assert(dob.date .year == 1979); + assert(dob.date .month == static_cast(toml::month_t::May)); + assert(dob.date .day == 27); + assert(dob.time .hour == 7); + assert(dob.time .minute == 32); + assert(dob.time .second == 0); + assert(dob.offset.hour == -8); + assert(dob.offset.minute == 0); + + assert(toml::find(root, "database", "enabled")); + + const auto ports = toml::find>(root, "database", "ports"); + assert(ports.at(0) == 8000); + assert(ports.at(1) == 8001); + assert(ports.at(2) == 8002); + + const auto data = toml::find, std::vector>>(root, "database", "data"); + assert(data.first.at(0) == "delta"); + assert(data.first.at(1) == "phi"); + assert(data.second.at(0) == 3.14); + + const auto temp_targets = toml::find>(root, "database", "temp_targets"); + assert(temp_targets.at("cpu" ) == 79.5); + assert(temp_targets.at("case") == 72.0); + + const auto servers = toml::find>>(root, "servers"); + assert(servers.at("alpha").at("ip" ) == "10.0.0.1"); + assert(servers.at("alpha").at("role") == "frontend"); + assert(servers.at("beta" ).at("ip" ) == "10.0.0.2"); + assert(servers.at("beta" ).at("role") == "backend" ); + } + + std::cout << "ok." << std::endl; + + return 0; +} diff --git a/examples/parse_file/spec_example.toml b/examples/parse_file/spec_example.toml new file mode 100644 index 0000000..3fe43f0 --- /dev/null +++ b/examples/parse_file/spec_example.toml @@ -0,0 +1,23 @@ +# This is a TOML document + +title = "TOML Example" + +[owner] +name = "Tom Preston-Werner" +dob = 1979-05-27T07:32:00-08:00 + +[database] +enabled = true +ports = [ 8000, 8001, 8002 ] +data = [ ["delta", "phi"], [3.14] ] +temp_targets = { cpu = 79.5, case = 72.0 } + +[servers] + +[servers.alpha] +ip = "10.0.0.1" +role = "frontend" + +[servers.beta] +ip = "10.0.0.2" +role = "backend" diff --git a/examples/reflect/.gitignore b/examples/reflect/.gitignore new file mode 100644 index 0000000..36a9b61 --- /dev/null +++ b/examples/reflect/.gitignore @@ -0,0 +1 @@ +reflect diff --git a/examples/reflect/CMakeLists.txt b/examples/reflect/CMakeLists.txt new file mode 100644 index 0000000..14da96e --- /dev/null +++ b/examples/reflect/CMakeLists.txt @@ -0,0 +1,18 @@ +include(FetchContent) + +FetchContent_Declare( + boost_ext_reflect + GIT_REPOSITORY https://github.com/boost-ext/reflect + GIT_SHALLOW ON # Download the branch without its history + GIT_TAG v1.1.1 +) +FetchContent_MakeAvailable(boost_ext_reflect) + +add_executable(reflect reflect.cpp) +target_link_libraries(reflect PRIVATE toml11::toml11) +target_include_directories(reflect PRIVATE + ${boost_ext_reflect_SOURCE_DIR} + ) +target_compile_features(reflect PRIVATE cxx_std_20) +set_target_properties(reflect PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") diff --git a/examples/reflect/README.md b/examples/reflect/README.md new file mode 100644 index 0000000..c5ba568 --- /dev/null +++ b/examples/reflect/README.md @@ -0,0 +1,13 @@ +# reflect + +Auto convert from user-defined `struct`s to `toml::value`. + +It depends on [boost-ext/reflect](https://github.com/boost-ext/reflect). + +## build + +Build toml11 with `-DTOML11_BUILD_EXAMPLES=ON`. + +```cpp +$ cmake -B ./build/ -DTOML11_BUILD_EXAMPLES=ON +``` diff --git a/examples/reflect/reflect.cpp b/examples/reflect/reflect.cpp new file mode 100644 index 0000000..a1c7f82 --- /dev/null +++ b/examples/reflect/reflect.cpp @@ -0,0 +1,38 @@ +#include + +#include +#include "reflect.hpp" + +struct Hoge +{ + int foo; + double bar; + std::string baz; +}; +TOML11_REFLECT(Hoge) + +int main() +{ + toml::value v(toml::table{ + {"foo", 42}, + {"bar", 3.14}, + {"baz", "fuga"}, + }); + + const Hoge h = toml::get(v); + + std::cout << "Hoge.foo = " << h.foo << std::endl; + std::cout << "Hoge.bar = " << h.bar << std::endl; + std::cout << "Hoge.baz = " << h.baz << std::endl; + + Hoge h2; + h2.foo = 6 * 9; + h2.bar = 2.718; + h2.baz = "piyo"; + + toml::value v2(h2); + + std::cout << toml::format(v2); + + return 0; +} diff --git a/examples/reflect/reflect.hpp b/examples/reflect/reflect.hpp new file mode 100644 index 0000000..0604222 --- /dev/null +++ b/examples/reflect/reflect.hpp @@ -0,0 +1,61 @@ +#ifndef TOML11_REFLECT_HPP +#define TOML11_REFLECT_HPP + +#include // boost-ext/reflect + +#include + +namespace toml +{ +namespace refl +{ +template +T from(const basic_value& v) +{ + T x; + reflect::for_each([&v, &x](auto I) { + using member_type = std::remove_cvref_t(x))>; + const auto key = std::string(reflect::member_name(x)); + reflect::get(x) = toml::find(v, key); + }, x); + + return x; +} + +template +basic_value into(const T& x) +{ + basic_value v(toml::table{}); + reflect::for_each([&v, &x](auto I) { + using member_type = std::remove_cvref_t(x))>; + const auto key = std::string(reflect::member_name(x)); + v[key] = reflect::get(x); + }, x); + return v; +} +} // refl +} // toml + +#define TOML11_REFLECT(X) \ + namespace toml { \ + template<> \ + struct into \ + { \ + template \ + static toml::basic_value into_toml(const X& x) \ + { \ + return refl::into(x); \ + } \ + }; \ + template<> \ + struct from \ + { \ + template \ + static X from_toml(const toml::basic_value& v) \ + { \ + return refl::from(v); \ + } \ + }; \ + } /* toml */ + +#endif // TOML11_REFLECT_HPP diff --git a/examples/unicode/.gitignore b/examples/unicode/.gitignore new file mode 100644 index 0000000..5bce22e --- /dev/null +++ b/examples/unicode/.gitignore @@ -0,0 +1 @@ +canonicalize diff --git a/examples/unicode/CMakeLists.txt b/examples/unicode/CMakeLists.txt new file mode 100644 index 0000000..d52de4f --- /dev/null +++ b/examples/unicode/CMakeLists.txt @@ -0,0 +1,15 @@ +include(FetchContent) + +FetchContent_Declare(uni-algo + GIT_REPOSITORY https://github.com/uni-algo/uni-algo.git + GIT_SHALLOW ON # Download the branch without its history + GIT_TAG v1.0.0) # The version you want to download + +# Be aware that FetchContent_MakeAvailable requires CMake 3.14 or higher +FetchContent_MakeAvailable(uni-algo) + +add_executable(canonicalize canonicalize.cpp) +target_link_libraries(canonicalize PRIVATE toml11::toml11 uni-algo::uni-algo) +set_target_properties(canonicalize PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") + diff --git a/examples/unicode/README.md b/examples/unicode/README.md new file mode 100644 index 0000000..66a5af2 --- /dev/null +++ b/examples/unicode/README.md @@ -0,0 +1,15 @@ +# reflect + +Compare TOML key after NFC canonicalization. + +It depends on [uni-algo](https://github.com/uni-algo/uni-algo.git). + +The example contains two keys that are different in bytewise comparison, but becomes the same after NFC normalization. + +## build + +Build toml11 with `-DTOML11_BUILD_EXAMPLES=ON`. + +```cpp +$ cmake -B ./build/ -DTOML11_BUILD_EXAMPLES=ON +``` diff --git a/examples/unicode/canonicalize.cpp b/examples/unicode/canonicalize.cpp new file mode 100644 index 0000000..a5fa8c4 --- /dev/null +++ b/examples/unicode/canonicalize.cpp @@ -0,0 +1,79 @@ +#include +#include + +#include +#include + +struct nfc_comparator +{ + using first_argument_type = std::string; + using second_argument_type = std::string; + using result_type = bool; + + result_type operator()(const first_argument_type& lhs, const second_argument_type& rhs) const + { + return una::norm::to_nfc_utf8(lhs) < una::norm::to_nfc_utf8(rhs); + } +}; + +struct nfc_equal_to +{ + using first_argument_type = std::string; + using second_argument_type = std::string; + using result_type = bool; + + result_type operator()(const first_argument_type& lhs, const second_argument_type& rhs) const + { + return una::norm::to_nfc_utf8(lhs) == una::norm::to_nfc_utf8(rhs); + } +}; + +struct nfc_hasher +{ + std::size_t operator()(const std::string& s) const + { + return std::hash{}(una::norm::to_nfc_utf8(s)); + } +}; + +struct nfc_config +{ + using comment_type = toml::preserve_comments; + + using boolean_type = bool; + using integer_type = std::int64_t; + using floating_type = double; + using string_type = std::string; + + template + using array_type = std::vector; + template + // using table_type = std::map; + using table_type = std::unordered_map; + + static toml::result + parse_int(const std::string& str, const toml::source_location src, const std::uint8_t base) + { + return toml::read_int(str, src, base); + } + static toml::result + parse_float(const std::string& str, const toml::source_location src, const bool is_hex) + { + return toml::read_float(str, src, is_hex); + } +}; + + +int main(int argc, char **argv) +{ + if(argc != 2) + { + return 1; + } + + const auto input = toml::parse(argv[1]); + + std::cout << toml::format(input) << std::endl; + + return 0; +} diff --git a/examples/unicode/input.toml b/examples/unicode/input.toml new file mode 100644 index 0000000..c58e95b --- /dev/null +++ b/examples/unicode/input.toml @@ -0,0 +1,2 @@ +"W\u0302" = 1 # in NFC, this key is the same as the following key +"Ŵ" = 2 # so it fails.