diff --git a/CMakeLists.txt b/CMakeLists.txt index f341af91ee7..7e2464c4088 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -240,6 +240,8 @@ if(GINKGO_BUILD_TESTS) endif() if(GINKGO_BUILD_BENCHMARKS) find_package(gflags 2.2.2 QUIET) +endif() +if(GINKGO_BUILD_TESTS OR GINKGO_BUILD_BENCHMARKS) find_package(nlohmann_json 3.9.1 QUIET) endif() diff --git a/extensions/test/CMakeLists.txt b/extensions/test/CMakeLists.txt index a729661d909..7376de49171 100644 --- a/extensions/test/CMakeLists.txt +++ b/extensions/test/CMakeLists.txt @@ -4,3 +4,5 @@ find_package(Kokkos 4.1.00 QUIET) if (Kokkos_FOUND) add_subdirectory(kokkos) endif () + +add_subdirectory(config) diff --git a/extensions/test/config/CMakeLists.txt b/extensions/test/config/CMakeLists.txt new file mode 100644 index 00000000000..83103e9519f --- /dev/null +++ b/extensions/test/config/CMakeLists.txt @@ -0,0 +1,5 @@ +ginkgo_create_test(json_config ADDITIONAL_LIBRARIES nlohmann_json::nlohmann_json) +# set the working directory to the current binary folder for file test +file(RELATIVE_PATH REL_BINARY_DIR ${PROJECT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}) +set_tests_properties("${REL_BINARY_DIR}/json_config" PROPERTIES WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") +configure_file(test.json test.json COPYONLY) diff --git a/extensions/test/config/json_config.cpp b/extensions/test/config/json_config.cpp new file mode 100644 index 00000000000..cd73209a7fb --- /dev/null +++ b/extensions/test/config/json_config.cpp @@ -0,0 +1,91 @@ +// SPDX-FileCopyrightText: 2017 - 2024 The Ginkgo authors +// +// SPDX-License-Identifier: BSD-3-Clause + +#include + + +#include +#include + + +#include +#include + + +#include "core/test/utils.hpp" + + +TEST(JsonConfig, ThrowIfInvalid) +{ + const char json[] = R"({"test": null})"; + auto d = nlohmann::json::parse(json); + + ASSERT_THROW(gko::ext::config::parse_json(d), std::runtime_error); +} + + +TEST(JsonConfig, ReadMap) +{ + const char json[] = R"({"test": "A", "bool": true})"; + auto d = nlohmann::json::parse(json); + + auto ptree = gko::ext::config::parse_json(d); + + ASSERT_EQ(ptree.get_map().size(), 2); + ASSERT_EQ(ptree.get("test").get_string(), "A"); + ASSERT_EQ(ptree.get("bool").get_boolean(), true); +} + + +TEST(JsonConfig, ReadArray) +{ + const char json[] = R"(["A", "B", "C"])"; + auto d = nlohmann::json::parse(json); + + auto ptree = gko::ext::config::parse_json(d); + + ASSERT_EQ(ptree.get_array().size(), 3); + ASSERT_EQ(ptree.get(0).get_string(), "A"); + ASSERT_EQ(ptree.get(1).get_string(), "B"); + ASSERT_EQ(ptree.get(2).get_string(), "C"); +} + + +TEST(JsonConfig, ReadInput) +{ + const char json[] = + R"({"item": 4, + "array": [3.0, 4.5], + "map": {"bool": false}})"; + + auto d = nlohmann::json::parse(json); + + auto ptree = gko::ext::config::parse_json(d); + + auto& child_array = ptree.get("array").get_array(); + auto& child_map = ptree.get("map").get_map(); + ASSERT_EQ(ptree.get_map().size(), 3); + ASSERT_EQ(ptree.get("item").get_integer(), 4); + ASSERT_EQ(child_array.size(), 2); + ASSERT_EQ(child_array.at(0).get_real(), 3.0); + ASSERT_EQ(child_array.at(1).get_real(), 4.5); + ASSERT_EQ(child_map.size(), 1); + ASSERT_EQ(child_map.at("bool").get_boolean(), false); +} + + +TEST(JsonConfig, ReadInputFromFile) +{ + auto ptree = gko::ext::config::parse_json_file("test.json"); + + auto& child_array = ptree.get("array").get_array(); + auto& child_map = ptree.get("map").get_map(); + ASSERT_EQ(ptree.get_map().size(), 3); + ASSERT_EQ(ptree.get("item").get_integer(), 4); + ASSERT_EQ(child_array.size(), 2); + ASSERT_EQ(child_array.at(0).get_real(), 3.0); + ASSERT_EQ(child_array.at(1).get_real(), 4.5); + ASSERT_EQ(child_map.size(), 1); + ASSERT_EQ(child_map.at("bool").get_boolean(), false); +} diff --git a/extensions/test/config/test.json b/extensions/test/config/test.json new file mode 100644 index 00000000000..b22a625ebf5 --- /dev/null +++ b/extensions/test/config/test.json @@ -0,0 +1,10 @@ +{ + "item": 4, + "array": [ + 3.0, + 4.5 + ], + "map": { + "bool": false + } +} diff --git a/include/ginkgo/extensions/config/json_config.hpp b/include/ginkgo/extensions/config/json_config.hpp new file mode 100644 index 00000000000..d21a3623f46 --- /dev/null +++ b/include/ginkgo/extensions/config/json_config.hpp @@ -0,0 +1,91 @@ +// SPDX-FileCopyrightText: 2017 - 2024 The Ginkgo authors +// +// SPDX-License-Identifier: BSD-3-Clause + +#ifndef GKO_PUBLIC_EXTENSIONS_CONFIG_JSON_CONFIG_HPP_ +#define GKO_PUBLIC_EXTENSIONS_CONFIG_JSON_CONFIG_HPP_ + + +#include +#include +#include + + +#include + + +#include + + +namespace gko { +namespace ext { +namespace config { + + +/** + * parse_json takes the nlohmann json object to generate the property tree + * object + */ +inline gko::config::pnode parse_json(const nlohmann::json& input) +{ + const auto& dom = input; + + auto parse_array = [](const auto& arr) { + gko::config::pnode::array_type nodes; + for (auto it : arr) { + nodes.emplace_back(parse_json(it)); + } + return gko::config::pnode{nodes}; + }; + auto parse_map = [](const auto& map) { + gko::config::pnode::map_type nodes; + for (auto& el : map.items()) { + nodes.emplace(el.key(), parse_json(el.value())); + } + return gko::config::pnode{nodes}; + }; + auto parse_data = [](const auto& data) { + if (data.is_number_integer()) { + return gko::config::pnode{data.template get()}; + } + if (data.is_boolean()) { + return gko::config::pnode{data.template get()}; + } + if (data.is_number_float()) { + return gko::config::pnode{data.template get()}; + } + if (data.is_string()) { + return gko::config::pnode{ + std::string(data.template get())}; + } + throw std::runtime_error( + "property_tree can not handle the node with content: " + + data.dump()); + }; + + if (dom.is_array()) { + return parse_array(dom); + } + if (dom.is_object()) { + return parse_map(dom); + } + return parse_data(dom); +} + + +/** + * parse_json_file takes the json file to generate the property tree object + */ +inline gko::config::pnode parse_json_file(std::string filename) +{ + std::ifstream fstream(filename); + return parse_json(nlohmann::json::parse(fstream)); +} + + +} // namespace config +} // namespace ext +} // namespace gko + + +#endif // GKO_PUBLIC_EXTENSIONS_CONFIG_JSON_CONFIG_HPP_ diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt index 749d24b87bd..cfba7759170 100644 --- a/third_party/CMakeLists.txt +++ b/third_party/CMakeLists.txt @@ -11,6 +11,9 @@ if(GINKGO_BUILD_BENCHMARKS) if (NOT gflags_FOUND) add_subdirectory(gflags) endif() +endif() + +if(GINKGO_BUILD_TESTS OR GINKGO_BUILD_BENCHMARKS) if (NOT nlohmann_json_FOUND) add_subdirectory(nlohmann_json) endif()