From 2553f20653ce45171647c21a8d6ecc91523dbe19 Mon Sep 17 00:00:00 2001 From: Perdixky <3293789706@qq.com> Date: Sun, 17 May 2026 22:25:47 +0800 Subject: [PATCH 1/2] feat: add C++20 module interfaces Add an opt-in REFLECTCPP_BUILD_MODULES path that registers CMake CXX_MODULES file sets for rfl, generic, cli, JSON, and each optional serialization format when its existing feature option is enabled. The module interfaces include the existing public headers in the global module fragment, then explicitly re-export reflect-cpp public names with using declarations. That keeps the include headers as the source of truth while avoiding attaching standard-library and C headers directly to named modules. Change namespace-scope public constants and module-visible helpers from internal-linkage static objects/functions to inline constexpr/inline forms. Exported templates can otherwise refer to entities owned by one module interface translation unit, which makes the imported API depend on unstable TU-local addresses and can diverge from include mode. Signed-off-by: Perdixky <3293789706@qq.com> Co-authored-by: Codex --- .gitignore | 1 + CMakeLists.txt | 89 ++++- include/rfl/capnproto/to_schema.hpp | 2 +- include/rfl/cli/Reader.hpp | 4 +- include/rfl/default.hpp | 7 +- include/rfl/internal/find_index.hpp | 4 +- include/rfl/internal/make_tag.hpp | 2 +- .../internal/variant/is_alternative_type.hpp | 2 +- .../internal/variant/is_convertible_to.hpp | 2 +- include/rfl/parsing/is_empty.hpp | 2 +- include/rfl/parsing/make_type_name.hpp | 2 +- include/rfl/thirdparty/ctre.hpp | 4 +- include/rfl/thirdparty/yyjson.h | 133 +++---- src/modules/rfl.avro.cppm | 26 ++ src/modules/rfl.boost_serialization.cppm | 18 + src/modules/rfl.bson.cppm | 19 + src/modules/rfl.capnproto.cppm | 30 ++ src/modules/rfl.cbor.cppm | 18 + src/modules/rfl.cereal.cppm | 17 + src/modules/rfl.cli.cppm | 23 ++ src/modules/rfl.cppm | 155 ++++++++ src/modules/rfl.csv.cppm | 15 + src/modules/rfl.flexbuf.cppm | 18 + src/modules/rfl.generic.cppm | 18 + src/modules/rfl.json.cppm | 27 ++ src/modules/rfl.msgpack.cppm | 18 + src/modules/rfl.parquet.cppm | 16 + src/modules/rfl.toml.cppm | 17 + src/modules/rfl.ubjson.cppm | 18 + src/modules/rfl.xml.cppm | 17 + src/modules/rfl.yaml.cppm | 17 + src/modules/rfl.yas.cppm | 20 + tests/CMakeLists.txt | 3 + tests/modules/CMakeLists.txt | 28 ++ tests/modules/test_modules.cpp | 357 ++++++++++++++++++ 35 files changed, 1069 insertions(+), 80 deletions(-) create mode 100644 src/modules/rfl.avro.cppm create mode 100644 src/modules/rfl.boost_serialization.cppm create mode 100644 src/modules/rfl.bson.cppm create mode 100644 src/modules/rfl.capnproto.cppm create mode 100644 src/modules/rfl.cbor.cppm create mode 100644 src/modules/rfl.cereal.cppm create mode 100644 src/modules/rfl.cli.cppm create mode 100644 src/modules/rfl.cppm create mode 100644 src/modules/rfl.csv.cppm create mode 100644 src/modules/rfl.flexbuf.cppm create mode 100644 src/modules/rfl.generic.cppm create mode 100644 src/modules/rfl.json.cppm create mode 100644 src/modules/rfl.msgpack.cppm create mode 100644 src/modules/rfl.parquet.cppm create mode 100644 src/modules/rfl.toml.cppm create mode 100644 src/modules/rfl.ubjson.cppm create mode 100644 src/modules/rfl.xml.cppm create mode 100644 src/modules/rfl.yaml.cppm create mode 100644 src/modules/rfl.yas.cppm create mode 100644 tests/modules/CMakeLists.txt create mode 100644 tests/modules/test_modules.cpp diff --git a/.gitignore b/.gitignore index cc3a2926..287ed2d6 100644 --- a/.gitignore +++ b/.gitignore @@ -78,6 +78,7 @@ __pycache__/ # Distribution / packaging .Python build/ +build-*/ develop-eggs/ dist/ downloads/ diff --git a/CMakeLists.txt b/CMakeLists.txt index fb1069ff..c2722454 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,7 @@ option(REFLECTCPP_BOOST_SERIALIZATION "Enable Boost.Serialization support" ${REF option(REFLECTCPP_BUILD_BENCHMARKS "Build benchmarks" OFF) option(REFLECTCPP_BUILD_TESTS "Build tests" OFF) option(REFLECTCPP_CHECK_HEADERS "Make sure that all headers are self-contained" OFF) +option(REFLECTCPP_BUILD_MODULES "Build experimental C++20 module interfaces" OFF) option(REFLECTCPP_USE_BUNDLED_DEPENDENCIES "Use the bundled dependencies" ON) @@ -460,6 +461,86 @@ set_target_properties(reflectcpp PROPERTIES LINKER_LANGUAGE CXX) target_sources(reflectcpp PRIVATE ${REFLECT_CPP_SOURCES}) target_precompile_headers(reflectcpp PRIVATE [["rfl.hpp"]] ) +if(REFLECTCPP_BUILD_MODULES) + if(CMAKE_VERSION VERSION_LESS 3.28) + message(FATAL_ERROR "REFLECTCPP_BUILD_MODULES requires CMake 3.28 or newer") + endif() + + if(CMAKE_GENERATOR MATCHES "Unix Makefiles") + message(FATAL_ERROR "REFLECTCPP_BUILD_MODULES requires a generator with C++ module dependency scanning support, such as Ninja") + endif() + + set(REFLECTCPP_MODULE_SOURCES + src/modules/rfl.cppm + src/modules/rfl.generic.cppm + src/modules/rfl.cli.cppm + ) + + if(REFLECTCPP_JSON) + list(APPEND REFLECTCPP_MODULE_SOURCES src/modules/rfl.json.cppm) + endif() + if(REFLECTCPP_AVRO) + list(APPEND REFLECTCPP_MODULE_SOURCES src/modules/rfl.avro.cppm) + endif() + if(REFLECTCPP_BSON) + list(APPEND REFLECTCPP_MODULE_SOURCES src/modules/rfl.bson.cppm) + endif() + if(REFLECTCPP_CAPNPROTO) + list(APPEND REFLECTCPP_MODULE_SOURCES src/modules/rfl.capnproto.cppm) + endif() + if(REFLECTCPP_CBOR) + list(APPEND REFLECTCPP_MODULE_SOURCES src/modules/rfl.cbor.cppm) + endif() + if(REFLECTCPP_CEREAL) + list(APPEND REFLECTCPP_MODULE_SOURCES src/modules/rfl.cereal.cppm) + endif() + if(REFLECTCPP_CSV) + list(APPEND REFLECTCPP_MODULE_SOURCES src/modules/rfl.csv.cppm) + endif() + if(REFLECTCPP_FLEXBUFFERS) + list(APPEND REFLECTCPP_MODULE_SOURCES src/modules/rfl.flexbuf.cppm) + endif() + if(REFLECTCPP_MSGPACK) + list(APPEND REFLECTCPP_MODULE_SOURCES src/modules/rfl.msgpack.cppm) + endif() + if(REFLECTCPP_PARQUET) + list(APPEND REFLECTCPP_MODULE_SOURCES src/modules/rfl.parquet.cppm) + endif() + if(REFLECTCPP_TOML) + list(APPEND REFLECTCPP_MODULE_SOURCES src/modules/rfl.toml.cppm) + endif() + if(REFLECTCPP_UBJSON) + list(APPEND REFLECTCPP_MODULE_SOURCES src/modules/rfl.ubjson.cppm) + endif() + if(REFLECTCPP_XML) + list(APPEND REFLECTCPP_MODULE_SOURCES src/modules/rfl.xml.cppm) + endif() + if(REFLECTCPP_YAML) + list(APPEND REFLECTCPP_MODULE_SOURCES src/modules/rfl.yaml.cppm) + endif() + if(REFLECTCPP_YAS) + list(APPEND REFLECTCPP_MODULE_SOURCES src/modules/rfl.yas.cppm) + endif() + if(REFLECTCPP_BOOST_SERIALIZATION) + list(APPEND REFLECTCPP_MODULE_SOURCES src/modules/rfl.boost_serialization.cppm) + endif() + + target_sources(reflectcpp + PUBLIC + FILE_SET reflectcpp_modules + TYPE CXX_MODULES + BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/src/modules + FILES ${REFLECTCPP_MODULE_SOURCES} + ) + set_source_files_properties(${REFLECTCPP_MODULE_SOURCES} + PROPERTIES SKIP_PRECOMPILE_HEADERS ON) + set_target_properties(reflectcpp PROPERTIES CXX_SCAN_FOR_MODULES ON) + + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(reflectcpp PUBLIC -fmodules-ts) + endif() +endif() + if (REFLECTCPP_BUILD_TESTS) add_library(reflectcpp_tests_crt INTERFACE) target_link_libraries(reflectcpp_tests_crt INTERFACE reflectcpp GTest::gtest_main) @@ -538,10 +619,17 @@ if (REFLECTCPP_INSTALL) FILES ${RFL_HEADERS} ) + if(REFLECTCPP_BUILD_MODULES) + set(_REFLECTCPP_MODULE_FILE_SET_INSTALL + FILE_SET reflectcpp_modules DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/reflectcpp/modules + ) + endif() + install( TARGETS reflectcpp EXPORT reflectcpp-exports FILE_SET reflectcpp_headers DESTINATION ${INCLUDE_INSTALL_DIR} + ${_REFLECTCPP_MODULE_FILE_SET_INSTALL} ) install( @@ -586,4 +674,3 @@ include(CPack) if (ament_cmake_FOUND) ament_package() endif() - diff --git a/include/rfl/capnproto/to_schema.hpp b/include/rfl/capnproto/to_schema.hpp index 2a6e96a2..525abd3e 100644 --- a/include/rfl/capnproto/to_schema.hpp +++ b/include/rfl/capnproto/to_schema.hpp @@ -38,7 +38,7 @@ struct SchemaHolder { }; template -static const SchemaHolder schema_holder = +inline const SchemaHolder schema_holder = SchemaHolder::make(); /// Returns the Cap'n Proto schema for a class. diff --git a/include/rfl/cli/Reader.hpp b/include/rfl/cli/Reader.hpp index 1d46e5d6..6d62f562 100644 --- a/include/rfl/cli/Reader.hpp +++ b/include/rfl/cli/Reader.hpp @@ -20,11 +20,11 @@ namespace rfl::cli { /// Character used to separate nested field names in CLI arguments. /// Example: `--database.host` for nested field `database::host`. -static constexpr char path_separator = '.'; +inline constexpr char path_separator = '.'; /// Character used to delimit array elements in CLI argument values. /// Example: `--ports=8080,8081,8082` for an array of ports. -static constexpr char array_delimiter = ','; +inline constexpr char array_delimiter = ','; /// Represents a CLI variable that can be a direct value or a path in the argument map. /// The `path` represents the hierarchical key (e.g., "database.host"). diff --git a/include/rfl/default.hpp b/include/rfl/default.hpp index 752d1edf..d9ab02d5 100644 --- a/include/rfl/default.hpp +++ b/include/rfl/default.hpp @@ -3,13 +3,14 @@ namespace rfl { -/// Helper class that can be passed to a field to trigger the default value of the type. -/// Used as a sentinel value to indicate that the default-constructed value should be used. +/// Helper class that can be passed to a field to trigger the default value of +/// the type. Used as a sentinel value to indicate that the default-constructed +/// value should be used. struct Default {}; /// Convenience constant for the Default type. /// Can be used like: MyField field = rfl::default_value; -inline static const auto default_value = Default{}; +inline constexpr auto default_value = Default{}; } // namespace rfl diff --git a/include/rfl/internal/find_index.hpp b/include/rfl/internal/find_index.hpp index f32bc968..c5590aac 100644 --- a/include/rfl/internal/find_index.hpp +++ b/include/rfl/internal/find_index.hpp @@ -36,7 +36,7 @@ constexpr auto wrap_fields(std::integer_sequence) { /// Finds the index of the field signified by _field_name template -constexpr static int find_index() { +inline constexpr int find_index() { constexpr int ix = wrap_fields<_field_name, Fields>( std::make_integer_sequence>()); static_assert(rfl::tuple_element_t::name_ == _field_name, @@ -46,7 +46,7 @@ constexpr static int find_index() { /// Finds the index of the field signified by _field_name or -1. template -constexpr static int find_index_or_minus_one() { +inline constexpr int find_index_or_minus_one() { if constexpr (rfl::tuple_size_v == 0) { return -1; } else { diff --git a/include/rfl/internal/make_tag.hpp b/include/rfl/internal/make_tag.hpp index 6c5d12dd..46e2027e 100644 --- a/include/rfl/internal/make_tag.hpp +++ b/include/rfl/internal/make_tag.hpp @@ -14,7 +14,7 @@ namespace rfl::internal { template -static inline auto make_tag(const T& _t) noexcept { +inline auto make_tag(const T& _t) noexcept { if constexpr (internal::has_reflection_type_v) { return make_tag<_discriminator>(_t.reflection()); } else if constexpr (named_tuple_t::Names::template contains< diff --git a/include/rfl/internal/variant/is_alternative_type.hpp b/include/rfl/internal/variant/is_alternative_type.hpp index 2342e211..1ee86b50 100644 --- a/include/rfl/internal/variant/is_alternative_type.hpp +++ b/include/rfl/internal/variant/is_alternative_type.hpp @@ -8,7 +8,7 @@ namespace rfl::internal::variant { template -static constexpr bool is_alternative_type() { +inline constexpr bool is_alternative_type() { return internal::element_index, std::remove_cvref_t...>() != -1; diff --git a/include/rfl/internal/variant/is_convertible_to.hpp b/include/rfl/internal/variant/is_convertible_to.hpp index 8121bf74..2fdb7585 100644 --- a/include/rfl/internal/variant/is_convertible_to.hpp +++ b/include/rfl/internal/variant/is_convertible_to.hpp @@ -8,7 +8,7 @@ namespace rfl::internal::variant { template -static constexpr bool is_convertible_to() { +inline constexpr bool is_convertible_to() { return std::disjunction_v...>; } diff --git a/include/rfl/parsing/is_empty.hpp b/include/rfl/parsing/is_empty.hpp index 41d5b851..d14d0ea3 100644 --- a/include/rfl/parsing/is_empty.hpp +++ b/include/rfl/parsing/is_empty.hpp @@ -21,7 +21,7 @@ namespace parsing { * @return True if the variable is empty, false otherwise. */ template -static bool is_empty(const T& _var) { +inline bool is_empty(const T& _var) { using Type = std::remove_cvref_t; if constexpr (std::is_pointer_v) { return !_var || is_empty(*_var); diff --git a/include/rfl/parsing/make_type_name.hpp b/include/rfl/parsing/make_type_name.hpp index 35e9a554..9fa62f7b 100644 --- a/include/rfl/parsing/make_type_name.hpp +++ b/include/rfl/parsing/make_type_name.hpp @@ -26,7 +26,7 @@ inline std::string replace_non_alphanumeric(std::string _str) { * @return The type name. */ template -static std::string make_type_name() { +inline std::string make_type_name() { if constexpr (is_tagged_union_wrapper_v) { return replace_non_alphanumeric(type_name_t().str() + "__tagged"); diff --git a/include/rfl/thirdparty/ctre.hpp b/include/rfl/thirdparty/ctre.hpp index 41796262..14eaed25 100644 --- a/include/rfl/thirdparty/ctre.hpp +++ b/include/rfl/thirdparty/ctre.hpp @@ -341,7 +341,7 @@ constexpr length_value_t length_and_value_of_utf16_code_point(uint16_t first_uni struct construct_from_pointer_t { }; -constexpr auto construct_from_pointer = construct_from_pointer_t{}; +inline constexpr auto construct_from_pointer = construct_from_pointer_t{}; CTLL_EXPORT template struct fixed_string { char32_t content[N] = {}; @@ -3577,7 +3577,7 @@ template concept capture_group = requires(const T & cap) { struct capture_not_exists_tag { }; -constexpr auto capture_not_exists = capture_not_exists_tag{}; +inline constexpr auto capture_not_exists = capture_not_exists_tag{}; template struct captures; diff --git a/include/rfl/thirdparty/yyjson.h b/include/rfl/thirdparty/yyjson.h index 55676ed0..c85a1f71 100644 --- a/include/rfl/thirdparty/yyjson.h +++ b/include/rfl/thirdparty/yyjson.h @@ -330,7 +330,12 @@ /** inline function export */ #ifndef yyjson_api_inline -#define yyjson_api_inline static yyjson_inline +#define yyjson_api_inline inline +#endif + +/** constant export */ +#ifndef yyjson_api_const +#define yyjson_api_const inline constexpr #endif /** stdint (C89 compatible) */ @@ -709,7 +714,7 @@ typedef uint32_t yyjson_read_flag; - Report error if double number is infinity. - Report error if string contains invalid UTF-8 character or BOM. - Report error on trailing commas, comments, inf and nan literals. */ -static const yyjson_read_flag YYJSON_READ_NOFLAG = 0; +yyjson_api_const yyjson_read_flag YYJSON_READ_NOFLAG = 0; /** Read the input data in-situ. This option allows the reader to modify and use input data to store string @@ -717,27 +722,27 @@ static const yyjson_read_flag YYJSON_READ_NOFLAG = 0; The caller should hold the input data before free the document. The input data must be padded by at least `YYJSON_PADDING_SIZE` bytes. For example: `[1,2]` should be `[1,2]\0\0\0\0`, input length should be 5. */ -static const yyjson_read_flag YYJSON_READ_INSITU = 1 << 0; +yyjson_api_const yyjson_read_flag YYJSON_READ_INSITU = 1 << 0; /** Stop when done instead of issuing an error if there's additional content after a JSON document. This option may be used to parse small pieces of JSON in larger data, such as `NDJSON`. */ -static const yyjson_read_flag YYJSON_READ_STOP_WHEN_DONE = 1 << 1; +yyjson_api_const yyjson_read_flag YYJSON_READ_STOP_WHEN_DONE = 1 << 1; /** Allow single trailing comma at the end of an object or array, such as `[1,2,3,]`, `{"a":1,"b":2,}` (non-standard). */ -static const yyjson_read_flag YYJSON_READ_ALLOW_TRAILING_COMMAS = 1 << 2; +yyjson_api_const yyjson_read_flag YYJSON_READ_ALLOW_TRAILING_COMMAS = 1 << 2; /** Allow C-style single-line and mult-line comments (non-standard). */ -static const yyjson_read_flag YYJSON_READ_ALLOW_COMMENTS = 1 << 3; +yyjson_api_const yyjson_read_flag YYJSON_READ_ALLOW_COMMENTS = 1 << 3; /** Allow inf/nan number and literal, case-insensitive, such as 1e999, NaN, inf, -Infinity (non-standard). */ -static const yyjson_read_flag YYJSON_READ_ALLOW_INF_AND_NAN = 1 << 4; +yyjson_api_const yyjson_read_flag YYJSON_READ_ALLOW_INF_AND_NAN = 1 << 4; /** Read all numbers as raw strings (value with `YYJSON_TYPE_RAW` type), inf/nan literal is also read as raw with `ALLOW_INF_AND_NAN` flag. */ -static const yyjson_read_flag YYJSON_READ_NUMBER_AS_RAW = 1 << 5; +yyjson_api_const yyjson_read_flag YYJSON_READ_NUMBER_AS_RAW = 1 << 5; /** Allow reading invalid unicode when parsing string values (non-standard). Invalid characters will be allowed to appear in the string values, but @@ -747,22 +752,22 @@ static const yyjson_read_flag YYJSON_READ_NUMBER_AS_RAW = 1 << 5; @warning Strings in JSON values may contain incorrect encoding when this option is used, you need to handle these strings carefully to avoid security risks. */ -static const yyjson_read_flag YYJSON_READ_ALLOW_INVALID_UNICODE = 1 << 6; +yyjson_api_const yyjson_read_flag YYJSON_READ_ALLOW_INVALID_UNICODE = 1 << 6; /** Read big numbers as raw strings. These big numbers include integers that cannot be represented by `int64_t` and `uint64_t`, and floating-point numbers that cannot be represented by finite `double`. The flag will be overridden by `YYJSON_READ_NUMBER_AS_RAW` flag. */ -static const yyjson_read_flag YYJSON_READ_BIGNUM_AS_RAW = 1 << 7; +yyjson_api_const yyjson_read_flag YYJSON_READ_BIGNUM_AS_RAW = 1 << 7; /** Allow UTF-8 BOM and skip it before parsing if any (non-standard). */ -static const yyjson_read_flag YYJSON_READ_ALLOW_BOM = 1 << 8; +yyjson_api_const yyjson_read_flag YYJSON_READ_ALLOW_BOM = 1 << 8; /** Allow extended number formats (non-standard): - Hexadecimal numbers, such as `0x7B`. - Numbers with leading or trailing decimal point, such as `.123`, `123.`. - Numbers with a leading plus sign, such as `+123`. */ -static const yyjson_read_flag YYJSON_READ_ALLOW_EXT_NUMBER = 1 << 9; +yyjson_api_const yyjson_read_flag YYJSON_READ_ALLOW_EXT_NUMBER = 1 << 9; /** Allow extended escape sequences in strings (non-standard): - Additional escapes: `\a`, `\e`, `\v`, ``\'``, `\?`, `\0`. @@ -771,7 +776,7 @@ static const yyjson_read_flag YYJSON_READ_ALLOW_EXT_NUMBER = 1 << 9; - Unknown escape: if backslash is followed by an unsupported character, the backslash will be removed and the character will be kept as-is. However, `\1`-`\9` will still trigger an error. */ -static const yyjson_read_flag YYJSON_READ_ALLOW_EXT_ESCAPE = 1 << 10; +yyjson_api_const yyjson_read_flag YYJSON_READ_ALLOW_EXT_ESCAPE = 1 << 10; /** Allow extended whitespace characters (non-standard): - Vertical tab `\v` and form feed `\f`. @@ -779,22 +784,22 @@ static const yyjson_read_flag YYJSON_READ_ALLOW_EXT_ESCAPE = 1 << 10; - Non-breaking space `\xA0`. - Byte order mark: `\uFEFF`. - Other Unicode characters in the Zs (Separator, space) category. */ -static const yyjson_read_flag YYJSON_READ_ALLOW_EXT_WHITESPACE = 1 << 11; +yyjson_api_const yyjson_read_flag YYJSON_READ_ALLOW_EXT_WHITESPACE = 1 << 11; /** Allow strings enclosed in single quotes (non-standard), such as ``'ab'``. */ -static const yyjson_read_flag YYJSON_READ_ALLOW_SINGLE_QUOTED_STR = 1 << 12; +yyjson_api_const yyjson_read_flag YYJSON_READ_ALLOW_SINGLE_QUOTED_STR = 1 << 12; /** Allow object keys without quotes (non-standard), such as `{a:1,b:2}`. This extends the ECMAScript IdentifierName rule by allowing any non-whitespace character with code point above `U+007F`. */ -static const yyjson_read_flag YYJSON_READ_ALLOW_UNQUOTED_KEY = 1 << 13; +yyjson_api_const yyjson_read_flag YYJSON_READ_ALLOW_UNQUOTED_KEY = 1 << 13; /** Allow JSON5 format, see: [https://json5.org]. This flag supports all JSON5 features with some additional extensions: - Accepts more escape sequences than JSON5 (e.g. `\a`, `\e`). - Unquoted keys are not limited to ECMAScript IdentifierName. - Allow case-insensitive `NaN`, `Inf` and `Infinity` literals. */ -static const yyjson_read_flag YYJSON_READ_JSON5 = +yyjson_api_const yyjson_read_flag YYJSON_READ_JSON5 = (1 << 2) | /* YYJSON_READ_ALLOW_TRAILING_COMMAS */ (1 << 3) | /* YYJSON_READ_ALLOW_COMMENTS */ (1 << 4) | /* YYJSON_READ_ALLOW_INF_AND_NAN */ @@ -808,49 +813,49 @@ static const yyjson_read_flag YYJSON_READ_JSON5 = typedef uint32_t yyjson_read_code; /** Success, no error. */ -static const yyjson_read_code YYJSON_READ_SUCCESS = 0; +yyjson_api_const yyjson_read_code YYJSON_READ_SUCCESS = 0; /** Invalid parameter, such as NULL input string or 0 input length. */ -static const yyjson_read_code YYJSON_READ_ERROR_INVALID_PARAMETER = 1; +yyjson_api_const yyjson_read_code YYJSON_READ_ERROR_INVALID_PARAMETER = 1; /** Memory allocation failed. */ -static const yyjson_read_code YYJSON_READ_ERROR_MEMORY_ALLOCATION = 2; +yyjson_api_const yyjson_read_code YYJSON_READ_ERROR_MEMORY_ALLOCATION = 2; /** Input JSON string is empty. */ -static const yyjson_read_code YYJSON_READ_ERROR_EMPTY_CONTENT = 3; +yyjson_api_const yyjson_read_code YYJSON_READ_ERROR_EMPTY_CONTENT = 3; /** Unexpected content after document, such as `[123]abc`. */ -static const yyjson_read_code YYJSON_READ_ERROR_UNEXPECTED_CONTENT = 4; +yyjson_api_const yyjson_read_code YYJSON_READ_ERROR_UNEXPECTED_CONTENT = 4; /** Unexpected end of input, the parsed part is valid, such as `[123`. */ -static const yyjson_read_code YYJSON_READ_ERROR_UNEXPECTED_END = 5; +yyjson_api_const yyjson_read_code YYJSON_READ_ERROR_UNEXPECTED_END = 5; /** Unexpected character inside the document, such as `[abc]`. */ -static const yyjson_read_code YYJSON_READ_ERROR_UNEXPECTED_CHARACTER = 6; +yyjson_api_const yyjson_read_code YYJSON_READ_ERROR_UNEXPECTED_CHARACTER = 6; /** Invalid JSON structure, such as `[1,]`. */ -static const yyjson_read_code YYJSON_READ_ERROR_JSON_STRUCTURE = 7; +yyjson_api_const yyjson_read_code YYJSON_READ_ERROR_JSON_STRUCTURE = 7; /** Invalid comment, deprecated, use `UNEXPECTED_END` for unclosed comment. */ -static const yyjson_read_code YYJSON_READ_ERROR_INVALID_COMMENT = 8; +yyjson_api_const yyjson_read_code YYJSON_READ_ERROR_INVALID_COMMENT = 8; /** Invalid number, such as `123.e12`, `000`. */ -static const yyjson_read_code YYJSON_READ_ERROR_INVALID_NUMBER = 9; +yyjson_api_const yyjson_read_code YYJSON_READ_ERROR_INVALID_NUMBER = 9; /** Invalid string, such as invalid escaped character inside a string. */ -static const yyjson_read_code YYJSON_READ_ERROR_INVALID_STRING = 10; +yyjson_api_const yyjson_read_code YYJSON_READ_ERROR_INVALID_STRING = 10; /** Invalid JSON literal, such as `truu`. */ -static const yyjson_read_code YYJSON_READ_ERROR_LITERAL = 11; +yyjson_api_const yyjson_read_code YYJSON_READ_ERROR_LITERAL = 11; /** Failed to open a file. */ -static const yyjson_read_code YYJSON_READ_ERROR_FILE_OPEN = 12; +yyjson_api_const yyjson_read_code YYJSON_READ_ERROR_FILE_OPEN = 12; /** Failed to read a file. */ -static const yyjson_read_code YYJSON_READ_ERROR_FILE_READ = 13; +yyjson_api_const yyjson_read_code YYJSON_READ_ERROR_FILE_READ = 13; /** Incomplete input during incremental parsing; parsing state is preserved. */ -static const yyjson_read_code YYJSON_READ_ERROR_MORE = 14; +yyjson_api_const yyjson_read_code YYJSON_READ_ERROR_MORE = 14; /** Error information for JSON reader. */ typedef struct yyjson_read_err { @@ -1125,38 +1130,38 @@ typedef uint32_t yyjson_write_flag; - Report error on inf or nan number. - Report error on invalid UTF-8 string. - Do not escape unicode or slash. */ -static const yyjson_write_flag YYJSON_WRITE_NOFLAG = 0; +yyjson_api_const yyjson_write_flag YYJSON_WRITE_NOFLAG = 0; /** Write JSON pretty with 4 space indent. */ -static const yyjson_write_flag YYJSON_WRITE_PRETTY = 1 << 0; +yyjson_api_const yyjson_write_flag YYJSON_WRITE_PRETTY = 1 << 0; /** Escape unicode as `uXXXX`, make the output ASCII only. */ -static const yyjson_write_flag YYJSON_WRITE_ESCAPE_UNICODE = 1 << 1; +yyjson_api_const yyjson_write_flag YYJSON_WRITE_ESCAPE_UNICODE = 1 << 1; /** Escape '/' as '\/'. */ -static const yyjson_write_flag YYJSON_WRITE_ESCAPE_SLASHES = 1 << 2; +yyjson_api_const yyjson_write_flag YYJSON_WRITE_ESCAPE_SLASHES = 1 << 2; /** Write inf and nan number as 'Infinity' and 'NaN' literal (non-standard). */ -static const yyjson_write_flag YYJSON_WRITE_ALLOW_INF_AND_NAN = 1 << 3; +yyjson_api_const yyjson_write_flag YYJSON_WRITE_ALLOW_INF_AND_NAN = 1 << 3; /** Write inf and nan number as null literal. This flag will override `YYJSON_WRITE_ALLOW_INF_AND_NAN` flag. */ -static const yyjson_write_flag YYJSON_WRITE_INF_AND_NAN_AS_NULL = 1 << 4; +yyjson_api_const yyjson_write_flag YYJSON_WRITE_INF_AND_NAN_AS_NULL = 1 << 4; /** Allow invalid unicode when encoding string values (non-standard). Invalid characters in string value will be copied byte by byte. If `YYJSON_WRITE_ESCAPE_UNICODE` flag is also set, invalid character will be escaped as `U+FFFD` (replacement character). This flag does not affect the performance of correctly encoded strings. */ -static const yyjson_write_flag YYJSON_WRITE_ALLOW_INVALID_UNICODE = 1 << 5; +yyjson_api_const yyjson_write_flag YYJSON_WRITE_ALLOW_INVALID_UNICODE = 1 << 5; /** Write JSON pretty with 2 space indent. This flag will override `YYJSON_WRITE_PRETTY` flag. */ -static const yyjson_write_flag YYJSON_WRITE_PRETTY_TWO_SPACES = 1 << 6; +yyjson_api_const yyjson_write_flag YYJSON_WRITE_PRETTY_TWO_SPACES = 1 << 6; /** Adds a newline character `\n` at the end of the JSON. This can be helpful for text editors or NDJSON. */ -static const yyjson_write_flag YYJSON_WRITE_NEWLINE_AT_END = 1 << 7; +yyjson_api_const yyjson_write_flag YYJSON_WRITE_NEWLINE_AT_END = 1 << 7; /** The highest 8 bits of `yyjson_write_flag` and real number value's `tag` are reserved for controlling the output format of floating-point numbers. */ @@ -1182,28 +1187,28 @@ static const yyjson_write_flag YYJSON_WRITE_NEWLINE_AT_END = 1 << 7; typedef uint32_t yyjson_write_code; /** Success, no error. */ -static const yyjson_write_code YYJSON_WRITE_SUCCESS = 0; +yyjson_api_const yyjson_write_code YYJSON_WRITE_SUCCESS = 0; /** Invalid parameter, such as NULL document. */ -static const yyjson_write_code YYJSON_WRITE_ERROR_INVALID_PARAMETER = 1; +yyjson_api_const yyjson_write_code YYJSON_WRITE_ERROR_INVALID_PARAMETER = 1; /** Memory allocation failure occurs. */ -static const yyjson_write_code YYJSON_WRITE_ERROR_MEMORY_ALLOCATION = 2; +yyjson_api_const yyjson_write_code YYJSON_WRITE_ERROR_MEMORY_ALLOCATION = 2; /** Invalid value type in JSON document. */ -static const yyjson_write_code YYJSON_WRITE_ERROR_INVALID_VALUE_TYPE = 3; +yyjson_api_const yyjson_write_code YYJSON_WRITE_ERROR_INVALID_VALUE_TYPE = 3; /** NaN or Infinity number occurs. */ -static const yyjson_write_code YYJSON_WRITE_ERROR_NAN_OR_INF = 4; +yyjson_api_const yyjson_write_code YYJSON_WRITE_ERROR_NAN_OR_INF = 4; /** Failed to open a file. */ -static const yyjson_write_code YYJSON_WRITE_ERROR_FILE_OPEN = 5; +yyjson_api_const yyjson_write_code YYJSON_WRITE_ERROR_FILE_OPEN = 5; /** Failed to write a file. */ -static const yyjson_write_code YYJSON_WRITE_ERROR_FILE_WRITE = 6; +yyjson_api_const yyjson_write_code YYJSON_WRITE_ERROR_FILE_WRITE = 6; /** Invalid unicode in string. */ -static const yyjson_write_code YYJSON_WRITE_ERROR_INVALID_STRING = 7; +yyjson_api_const yyjson_write_code YYJSON_WRITE_ERROR_INVALID_STRING = 7; /** Error information for JSON writer. */ typedef struct yyjson_write_err { @@ -3946,25 +3951,25 @@ yyjson_api_inline bool yyjson_mut_obj_rename_keyn(yyjson_mut_doc *doc, typedef uint32_t yyjson_ptr_code; /** No JSON pointer error. */ -static const yyjson_ptr_code YYJSON_PTR_ERR_NONE = 0; +yyjson_api_const yyjson_ptr_code YYJSON_PTR_ERR_NONE = 0; /** Invalid input parameter, such as NULL input. */ -static const yyjson_ptr_code YYJSON_PTR_ERR_PARAMETER = 1; +yyjson_api_const yyjson_ptr_code YYJSON_PTR_ERR_PARAMETER = 1; /** JSON pointer syntax error, such as invalid escape, token no prefix. */ -static const yyjson_ptr_code YYJSON_PTR_ERR_SYNTAX = 2; +yyjson_api_const yyjson_ptr_code YYJSON_PTR_ERR_SYNTAX = 2; /** JSON pointer resolve failed, such as index out of range, key not found. */ -static const yyjson_ptr_code YYJSON_PTR_ERR_RESOLVE = 3; +yyjson_api_const yyjson_ptr_code YYJSON_PTR_ERR_RESOLVE = 3; /** Document's root is NULL, but it is required for the function call. */ -static const yyjson_ptr_code YYJSON_PTR_ERR_NULL_ROOT = 4; +yyjson_api_const yyjson_ptr_code YYJSON_PTR_ERR_NULL_ROOT = 4; /** Cannot set root as the target is not a document. */ -static const yyjson_ptr_code YYJSON_PTR_ERR_SET_ROOT = 5; +yyjson_api_const yyjson_ptr_code YYJSON_PTR_ERR_SET_ROOT = 5; /** The memory allocation failed and a new value could not be created. */ -static const yyjson_ptr_code YYJSON_PTR_ERR_MEMORY_ALLOCATION = 6; +yyjson_api_const yyjson_ptr_code YYJSON_PTR_ERR_MEMORY_ALLOCATION = 6; /** Error information for JSON pointer. */ typedef struct yyjson_ptr_err { @@ -4515,28 +4520,28 @@ yyjson_api_inline bool yyjson_ptr_ctx_remove(yyjson_ptr_ctx *ctx); typedef uint32_t yyjson_patch_code; /** Success, no error. */ -static const yyjson_patch_code YYJSON_PATCH_SUCCESS = 0; +yyjson_api_const yyjson_patch_code YYJSON_PATCH_SUCCESS = 0; /** Invalid parameter, such as NULL input or non-array patch. */ -static const yyjson_patch_code YYJSON_PATCH_ERROR_INVALID_PARAMETER = 1; +yyjson_api_const yyjson_patch_code YYJSON_PATCH_ERROR_INVALID_PARAMETER = 1; /** Memory allocation failure occurs. */ -static const yyjson_patch_code YYJSON_PATCH_ERROR_MEMORY_ALLOCATION = 2; +yyjson_api_const yyjson_patch_code YYJSON_PATCH_ERROR_MEMORY_ALLOCATION = 2; /** JSON patch operation is not object type. */ -static const yyjson_patch_code YYJSON_PATCH_ERROR_INVALID_OPERATION = 3; +yyjson_api_const yyjson_patch_code YYJSON_PATCH_ERROR_INVALID_OPERATION = 3; /** JSON patch operation is missing a required key. */ -static const yyjson_patch_code YYJSON_PATCH_ERROR_MISSING_KEY = 4; +yyjson_api_const yyjson_patch_code YYJSON_PATCH_ERROR_MISSING_KEY = 4; /** JSON patch operation member is invalid. */ -static const yyjson_patch_code YYJSON_PATCH_ERROR_INVALID_MEMBER = 5; +yyjson_api_const yyjson_patch_code YYJSON_PATCH_ERROR_INVALID_MEMBER = 5; /** JSON patch operation `test` not equal. */ -static const yyjson_patch_code YYJSON_PATCH_ERROR_EQUAL = 6; +yyjson_api_const yyjson_patch_code YYJSON_PATCH_ERROR_EQUAL = 6; /** JSON patch operation failed on JSON pointer. */ -static const yyjson_patch_code YYJSON_PATCH_ERROR_POINTER = 7; +yyjson_api_const yyjson_patch_code YYJSON_PATCH_ERROR_POINTER = 7; /** Error information for JSON patch. */ typedef struct yyjson_patch_err { diff --git a/src/modules/rfl.avro.cppm b/src/modules/rfl.avro.cppm new file mode 100644 index 00000000..ca1f9dab --- /dev/null +++ b/src/modules/rfl.avro.cppm @@ -0,0 +1,26 @@ +module; + +#include "rfl/avro.hpp" + +export module rfl.avro; +export import rfl; + +export namespace rfl::avro { +using ::rfl::avro::InputObjectType; +using ::rfl::avro::InputVarType; +using ::rfl::avro::load; +using ::rfl::avro::Parser; +using ::rfl::avro::read; +using ::rfl::avro::Reader; +using ::rfl::avro::save; +using ::rfl::avro::Schema; +using ::rfl::avro::SchemaImpl; +using ::rfl::avro::to_json_representation; +using ::rfl::avro::to_schema; +using ::rfl::avro::write; +using ::rfl::avro::Writer; +} // namespace rfl::avro + +export namespace rfl::avro::schema { +using ::rfl::avro::schema::Type; +} diff --git a/src/modules/rfl.boost_serialization.cppm b/src/modules/rfl.boost_serialization.cppm new file mode 100644 index 00000000..24bedf7c --- /dev/null +++ b/src/modules/rfl.boost_serialization.cppm @@ -0,0 +1,18 @@ +module; + +#include "rfl/boost_serialization.hpp" + +export module rfl.boost_serialization; +export import rfl; + +export namespace rfl::boost_serialization { +using ::rfl::boost_serialization::load; +using ::rfl::boost_serialization::MemBuf; +using ::rfl::boost_serialization::Parser; +using ::rfl::boost_serialization::read; +using ::rfl::boost_serialization::read_from_archive; +using ::rfl::boost_serialization::Reader; +using ::rfl::boost_serialization::save; +using ::rfl::boost_serialization::write; +using ::rfl::boost_serialization::Writer; +} // namespace rfl::boost_serialization diff --git a/src/modules/rfl.bson.cppm b/src/modules/rfl.bson.cppm new file mode 100644 index 00000000..ecd11d4a --- /dev/null +++ b/src/modules/rfl.bson.cppm @@ -0,0 +1,19 @@ +module; + +#include "rfl/bson.hpp" + +export module rfl.bson; +export import rfl; + +export namespace rfl::bson { +using ::rfl::bson::InputObjectType; +using ::rfl::bson::InputVarType; +using ::rfl::bson::load; +using ::rfl::bson::Parser; +using ::rfl::bson::read; +using ::rfl::bson::Reader; +using ::rfl::bson::save; +using ::rfl::bson::to_buffer; +using ::rfl::bson::write; +using ::rfl::bson::Writer; +} // namespace rfl::bson diff --git a/src/modules/rfl.capnproto.cppm b/src/modules/rfl.capnproto.cppm new file mode 100644 index 00000000..c04df741 --- /dev/null +++ b/src/modules/rfl.capnproto.cppm @@ -0,0 +1,30 @@ +module; + +#include "rfl/capnproto.hpp" + +export module rfl.capnproto; +export import rfl; + +export namespace rfl::capnproto { +using ::rfl::capnproto::get_root_name; +using ::rfl::capnproto::InputObjectType; +using ::rfl::capnproto::InputVarType; +using ::rfl::capnproto::is_named_type; +using ::rfl::capnproto::load; +using ::rfl::capnproto::Parser; +using ::rfl::capnproto::read; +using ::rfl::capnproto::Reader; +using ::rfl::capnproto::save; +using ::rfl::capnproto::Schema; +using ::rfl::capnproto::SchemaHolder; +using ::rfl::capnproto::SchemaImpl; +using ::rfl::capnproto::to_schema; +using ::rfl::capnproto::to_string_representation; +using ::rfl::capnproto::write; +using ::rfl::capnproto::Writer; +} // namespace rfl::capnproto + +export namespace rfl::capnproto::schema { +using ::rfl::capnproto::schema::CapnProtoTypes; +using ::rfl::capnproto::schema::Type; +} // namespace rfl::capnproto::schema diff --git a/src/modules/rfl.cbor.cppm b/src/modules/rfl.cbor.cppm new file mode 100644 index 00000000..1980b500 --- /dev/null +++ b/src/modules/rfl.cbor.cppm @@ -0,0 +1,18 @@ +module; + +#include "rfl/cbor.hpp" + +export module rfl.cbor; +export import rfl; + +export namespace rfl::cbor { +using ::rfl::cbor::InputObjectType; +using ::rfl::cbor::InputVarType; +using ::rfl::cbor::load; +using ::rfl::cbor::Parser; +using ::rfl::cbor::read; +using ::rfl::cbor::Reader; +using ::rfl::cbor::save; +using ::rfl::cbor::write; +using ::rfl::cbor::Writer; +} // namespace rfl::cbor diff --git a/src/modules/rfl.cereal.cppm b/src/modules/rfl.cereal.cppm new file mode 100644 index 00000000..a355d93f --- /dev/null +++ b/src/modules/rfl.cereal.cppm @@ -0,0 +1,17 @@ +module; + +#include "rfl/cereal.hpp" + +export module rfl.cereal; +export import rfl; + +export namespace rfl::cereal { +using ::rfl::cereal::InputVarType; +using ::rfl::cereal::load; +using ::rfl::cereal::Parser; +using ::rfl::cereal::read; +using ::rfl::cereal::Reader; +using ::rfl::cereal::save; +using ::rfl::cereal::write; +using ::rfl::cereal::Writer; +} // namespace rfl::cereal diff --git a/src/modules/rfl.cli.cppm b/src/modules/rfl.cli.cppm new file mode 100644 index 00000000..6b3380fb --- /dev/null +++ b/src/modules/rfl.cli.cppm @@ -0,0 +1,23 @@ +module; + +#include "rfl/cli.hpp" + +export module rfl.cli; +export import rfl; +export import rfl.generic; + +export namespace rfl::cli { +using ::rfl::cli::array_delimiter; +using ::rfl::cli::CliArrayType; +using ::rfl::cli::CliObjectType; +using ::rfl::cli::CliVarType; +using ::rfl::cli::looks_like_option; +using ::rfl::cli::parse_argv; +using ::rfl::cli::parse_value; +using ::rfl::cli::ParsedArgs; +using ::rfl::cli::Parser; +using ::rfl::cli::path_separator; +using ::rfl::cli::read; +using ::rfl::cli::Reader; +using ::rfl::cli::resolve_args; +} // namespace rfl::cli diff --git a/src/modules/rfl.cppm b/src/modules/rfl.cppm new file mode 100644 index 00000000..651c4601 --- /dev/null +++ b/src/modules/rfl.cppm @@ -0,0 +1,155 @@ +module; + +#include "rfl.hpp" + +export module rfl; + +export namespace rfl { +using ::rfl::AddNamespacedTagsToVariants; +using ::rfl::AddStructName; +using ::rfl::AddTagsToVariants; +using ::rfl::AllOf; +using ::rfl::AllowRawPtrs; +using ::rfl::AlphaNumeric; +using ::rfl::always_false_v; +using ::rfl::AnyOf; +using ::rfl::apply; +using ::rfl::as; +using ::rfl::Attribute; +using ::rfl::Base64Encoded; +using ::rfl::Binary; +using ::rfl::Box; +using ::rfl::Bytestring; +using ::rfl::CamelCaseToSnakeCase; +using ::rfl::Commented; +using ::rfl::Copyability; +using ::rfl::CopyableBox; +using ::rfl::Default; +using ::rfl::default_value; +using ::rfl::DefaultIfMissing; +using ::rfl::DefaultVal; +using ::rfl::define_literal_t; +using ::rfl::define_named_tuple_t; +using ::rfl::define_tagged_union_t; +using ::rfl::define_variant_t; +using ::rfl::Description; +using ::rfl::Email; +using ::rfl::enum_to_string; +using ::rfl::Error; +using ::rfl::error; +using ::rfl::ExclusiveMaximum; +using ::rfl::ExclusiveMinimum; +using ::rfl::extract_discriminators_t; +using ::rfl::ExtraFields; +using ::rfl::Field; +using ::rfl::field_type_t; +using ::rfl::fields; +using ::rfl::Flatten; +using ::rfl::from_generic; +using ::rfl::from_named_tuple; +using ::rfl::Generic; +using ::rfl::get; +using ::rfl::get_enum_range; +using ::rfl::get_enumerator_array; +using ::rfl::get_enumerators; +using ::rfl::get_if; +using ::rfl::get_underlying_enumerator_array; +using ::rfl::get_underlying_enumerators; +using ::rfl::Hex; +using ::rfl::holds_alternative; +using ::rfl::Literal; +using ::rfl::LiteralHelper; +using ::rfl::make_box; +using ::rfl::make_copyable_box; +using ::rfl::make_field; +using ::rfl::make_from_tuple; +using ::rfl::make_named_tuple; +using ::rfl::make_ref; +using ::rfl::Maximum; +using ::rfl::Minimum; +using ::rfl::name_of; +using ::rfl::name_t; +using ::rfl::named_tuple_t; +using ::rfl::NamedTuple; +using ::rfl::NoExtraFields; +using ::rfl::NoFieldNames; +using ::rfl::NoOptionals; +using ::rfl::Nothing; +using ::rfl::Object; +using ::rfl::Oct; +using ::rfl::OneOf; +using ::rfl::Pattern; +using ::rfl::PatternValidator; +using ::rfl::Positional; +using ::rfl::PossibleTags; +using ::rfl::Processors; +using ::rfl::Ref; +using ::rfl::Rename; +using ::rfl::Result; +using ::rfl::Short; +using ::rfl::Size; +using ::rfl::Skip; +using ::rfl::SkipDeserialization; +using ::rfl::SkipSerialization; +using ::rfl::SnakeCaseToCamelCase; +using ::rfl::SnakeCaseToKebabCase; +using ::rfl::SnakeCaseToPascalCase; +using ::rfl::TaggedUnion; +using ::rfl::Timestamp; +using ::rfl::Tuple; +using ::rfl::UnderlyingEnums; +using ::rfl::Unexpected; +using ::rfl::UUIDv1; +using ::rfl::UUIDv2; +using ::rfl::UUIDv3; +using ::rfl::UUIDv4; +using ::rfl::Validator; +using ::rfl::Variant; +using ::rfl::Vectorstring; +using ::rfl::operator*; +using ::rfl::operator<<; +using ::rfl::operator<=>; +using ::rfl::possible_tags_t; +using ::rfl::remove_fields_t; +using ::rfl::replace; +using ::rfl::string_to_enum; +using ::rfl::to_array; +using ::rfl::to_bool; +using ::rfl::to_double; +using ::rfl::to_generic; +using ::rfl::to_int; +using ::rfl::to_int64; +using ::rfl::to_named_tuple; +using ::rfl::to_null; +using ::rfl::to_object; +using ::rfl::to_string; +using ::rfl::to_view; +using ::rfl::tuple_cat; +using ::rfl::tuple_element; +using ::rfl::tuple_element_t; +using ::rfl::tuple_size; +using ::rfl::tuple_size_v; +using ::rfl::type_name_t; +using ::rfl::value_of; +using ::rfl::variant_alternative; +using ::rfl::variant_alternative_t; +using ::rfl::variant_size; +using ::rfl::variant_size_v; +using ::rfl::view_t; +using ::rfl::visit; +} // namespace rfl + +export namespace rfl::atomic { +using ::rfl::atomic::is_atomic; +using ::rfl::atomic::is_atomic_v; +using ::rfl::atomic::remove_atomic_t; +using ::rfl::atomic::set_atomic; +} // namespace rfl::atomic + +export namespace rfl::concepts { +using ::rfl::concepts::BackInsertableByteContainer; +using ::rfl::concepts::ByteLike; +using ::rfl::concepts::ByteSpanLike; +using ::rfl::concepts::ContiguousByteContainer; +using ::rfl::concepts::MutableContiguousByteContainer; +} // namespace rfl::concepts diff --git a/src/modules/rfl.csv.cppm b/src/modules/rfl.csv.cppm new file mode 100644 index 00000000..0f4d2920 --- /dev/null +++ b/src/modules/rfl.csv.cppm @@ -0,0 +1,15 @@ +module; + +#include "rfl/csv.hpp" +#include "rfl/csv/Settings.hpp" + +export module rfl.csv; +export import rfl; + +export namespace rfl::csv { +using ::rfl::csv::load; +using ::rfl::csv::read; +using ::rfl::csv::save; +using ::rfl::csv::Settings; +using ::rfl::csv::write; +} // namespace rfl::csv diff --git a/src/modules/rfl.flexbuf.cppm b/src/modules/rfl.flexbuf.cppm new file mode 100644 index 00000000..3010c7ad --- /dev/null +++ b/src/modules/rfl.flexbuf.cppm @@ -0,0 +1,18 @@ +module; + +#include "rfl/flexbuf.hpp" + +export module rfl.flexbuf; +export import rfl; + +export namespace rfl::flexbuf { +using ::rfl::flexbuf::InputVarType; +using ::rfl::flexbuf::load; +using ::rfl::flexbuf::Parser; +using ::rfl::flexbuf::read; +using ::rfl::flexbuf::Reader; +using ::rfl::flexbuf::save; +using ::rfl::flexbuf::to_buffer; +using ::rfl::flexbuf::write; +using ::rfl::flexbuf::Writer; +} // namespace rfl::flexbuf diff --git a/src/modules/rfl.generic.cppm b/src/modules/rfl.generic.cppm new file mode 100644 index 00000000..39a5b7f0 --- /dev/null +++ b/src/modules/rfl.generic.cppm @@ -0,0 +1,18 @@ +module; + +#include "rfl/generic/Parser.hpp" +#include "rfl/generic/Reader.hpp" +#include "rfl/generic/Writer.hpp" +#include "rfl/generic/read.hpp" +#include "rfl/generic/write.hpp" + +export module rfl.generic; +export import rfl; + +export namespace rfl::generic { +using ::rfl::generic::Parser; +using ::rfl::generic::read; +using ::rfl::generic::Reader; +using ::rfl::generic::write; +using ::rfl::generic::Writer; +} // namespace rfl::generic diff --git a/src/modules/rfl.json.cppm b/src/modules/rfl.json.cppm new file mode 100644 index 00000000..fb2bc71e --- /dev/null +++ b/src/modules/rfl.json.cppm @@ -0,0 +1,27 @@ +module; + +#include "rfl/json.hpp" + +export module rfl.json; +export import rfl; + +export namespace rfl::json { +using ::rfl::json::InputObjectType; +using ::rfl::json::InputVarType; +using ::rfl::json::load; +using ::rfl::json::Parser; +using ::rfl::json::pretty; +using ::rfl::json::read; +using ::rfl::json::Reader; +using ::rfl::json::save; +using ::rfl::json::to_schema; +using ::rfl::json::to_schema_internal_schema; +using ::rfl::json::TypeHelper; +using ::rfl::json::write; +using ::rfl::json::Writer; +} // namespace rfl::json + +export namespace rfl::json::schema { +using ::rfl::json::schema::JSONSchema; +using ::rfl::json::schema::Type; +} // namespace rfl::json::schema diff --git a/src/modules/rfl.msgpack.cppm b/src/modules/rfl.msgpack.cppm new file mode 100644 index 00000000..9fd56489 --- /dev/null +++ b/src/modules/rfl.msgpack.cppm @@ -0,0 +1,18 @@ +module; + +#include "rfl/msgpack.hpp" + +export module rfl.msgpack; +export import rfl; + +export namespace rfl::msgpack { +using ::rfl::msgpack::InputObjectType; +using ::rfl::msgpack::InputVarType; +using ::rfl::msgpack::load; +using ::rfl::msgpack::Parser; +using ::rfl::msgpack::read; +using ::rfl::msgpack::Reader; +using ::rfl::msgpack::save; +using ::rfl::msgpack::write; +using ::rfl::msgpack::Writer; +} // namespace rfl::msgpack diff --git a/src/modules/rfl.parquet.cppm b/src/modules/rfl.parquet.cppm new file mode 100644 index 00000000..45c29d1b --- /dev/null +++ b/src/modules/rfl.parquet.cppm @@ -0,0 +1,16 @@ +module; + +#include "rfl/parquet.hpp" +#include "rfl/parquet/Settings.hpp" + +export module rfl.parquet; +export import rfl; + +export namespace rfl::parquet { +using ::rfl::parquet::Compression; +using ::rfl::parquet::load; +using ::rfl::parquet::read; +using ::rfl::parquet::save; +using ::rfl::parquet::Settings; +using ::rfl::parquet::write; +} // namespace rfl::parquet diff --git a/src/modules/rfl.toml.cppm b/src/modules/rfl.toml.cppm new file mode 100644 index 00000000..b00140f1 --- /dev/null +++ b/src/modules/rfl.toml.cppm @@ -0,0 +1,17 @@ +module; + +#include "rfl/toml.hpp" + +export module rfl.toml; +export import rfl; + +export namespace rfl::toml { +using ::rfl::toml::InputVarType; +using ::rfl::toml::load; +using ::rfl::toml::Parser; +using ::rfl::toml::read; +using ::rfl::toml::Reader; +using ::rfl::toml::save; +using ::rfl::toml::write; +using ::rfl::toml::Writer; +} // namespace rfl::toml diff --git a/src/modules/rfl.ubjson.cppm b/src/modules/rfl.ubjson.cppm new file mode 100644 index 00000000..3cf88c7e --- /dev/null +++ b/src/modules/rfl.ubjson.cppm @@ -0,0 +1,18 @@ +module; + +#include "rfl/ubjson.hpp" + +export module rfl.ubjson; +export import rfl; + +export namespace rfl::ubjson { +using ::rfl::ubjson::InputObjectType; +using ::rfl::ubjson::InputVarType; +using ::rfl::ubjson::load; +using ::rfl::ubjson::Parser; +using ::rfl::ubjson::read; +using ::rfl::ubjson::Reader; +using ::rfl::ubjson::save; +using ::rfl::ubjson::write; +using ::rfl::ubjson::Writer; +} // namespace rfl::ubjson diff --git a/src/modules/rfl.xml.cppm b/src/modules/rfl.xml.cppm new file mode 100644 index 00000000..64078dc6 --- /dev/null +++ b/src/modules/rfl.xml.cppm @@ -0,0 +1,17 @@ +module; + +#include "rfl/xml.hpp" + +export module rfl.xml; +export import rfl; + +export namespace rfl::xml { +using ::rfl::xml::InputVarType; +using ::rfl::xml::load; +using ::rfl::xml::Parser; +using ::rfl::xml::read; +using ::rfl::xml::Reader; +using ::rfl::xml::save; +using ::rfl::xml::write; +using ::rfl::xml::Writer; +} // namespace rfl::xml diff --git a/src/modules/rfl.yaml.cppm b/src/modules/rfl.yaml.cppm new file mode 100644 index 00000000..44e5e01a --- /dev/null +++ b/src/modules/rfl.yaml.cppm @@ -0,0 +1,17 @@ +module; + +#include "rfl/yaml.hpp" + +export module rfl.yaml; +export import rfl; + +export namespace rfl::yaml { +using ::rfl::yaml::InputVarType; +using ::rfl::yaml::load; +using ::rfl::yaml::Parser; +using ::rfl::yaml::read; +using ::rfl::yaml::Reader; +using ::rfl::yaml::save; +using ::rfl::yaml::write; +using ::rfl::yaml::Writer; +} // namespace rfl::yaml diff --git a/src/modules/rfl.yas.cppm b/src/modules/rfl.yas.cppm new file mode 100644 index 00000000..df4742e2 --- /dev/null +++ b/src/modules/rfl.yas.cppm @@ -0,0 +1,20 @@ +module; + +#include "rfl/yas.hpp" + +export module rfl.yas; +export import rfl; + +export namespace rfl::yas { +using ::rfl::yas::IArchive; +using ::rfl::yas::load; +using ::rfl::yas::OArchive; +using ::rfl::yas::Parser; +using ::rfl::yas::read; +using ::rfl::yas::read_from_archive; +using ::rfl::yas::Reader; +using ::rfl::yas::save; +using ::rfl::yas::write; +using ::rfl::yas::write_to_archive; +using ::rfl::yas::Writer; +} // namespace rfl::yas diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d68b3a0f..bc18574c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -15,6 +15,9 @@ if (REFLECTCPP_JSON) add_subdirectory(json) add_subdirectory(json_c_arrays_and_inheritance) add_subdirectory(cli) + if(REFLECTCPP_BUILD_MODULES) + add_subdirectory(modules) + endif() endif () if (REFLECTCPP_AVRO) diff --git a/tests/modules/CMakeLists.txt b/tests/modules/CMakeLists.txt new file mode 100644 index 00000000..40769d66 --- /dev/null +++ b/tests/modules/CMakeLists.txt @@ -0,0 +1,28 @@ +project(reflect-cpp-modules-tests) + +add_executable( + reflect-cpp-modules-tests + test_modules.cpp +) + +target_link_libraries(reflect-cpp-modules-tests PRIVATE reflectcpp) +target_compile_definitions(reflect-cpp-modules-tests PRIVATE + REFLECTCPP_MODULE_TEST_AVRO=$ + REFLECTCPP_MODULE_TEST_BOOST_SERIALIZATION=$ + REFLECTCPP_MODULE_TEST_BSON=$ + REFLECTCPP_MODULE_TEST_CAPNPROTO=$ + REFLECTCPP_MODULE_TEST_CBOR=$ + REFLECTCPP_MODULE_TEST_CEREAL=$ + REFLECTCPP_MODULE_TEST_CSV=$ + REFLECTCPP_MODULE_TEST_FLEXBUFFERS=$ + REFLECTCPP_MODULE_TEST_MSGPACK=$ + REFLECTCPP_MODULE_TEST_PARQUET=$ + REFLECTCPP_MODULE_TEST_TOML=$ + REFLECTCPP_MODULE_TEST_UBJSON=$ + REFLECTCPP_MODULE_TEST_XML=$ + REFLECTCPP_MODULE_TEST_YAML=$ + REFLECTCPP_MODULE_TEST_YAS=$ +) +set_target_properties(reflect-cpp-modules-tests PROPERTIES CXX_SCAN_FOR_MODULES ON) + +add_test(NAME modules.json_roundtrip COMMAND reflect-cpp-modules-tests) diff --git a/tests/modules/test_modules.cpp b/tests/modules/test_modules.cpp new file mode 100644 index 00000000..09fc5338 --- /dev/null +++ b/tests/modules/test_modules.cpp @@ -0,0 +1,357 @@ +#include +#include + +import rfl; +import rfl.cli; +import rfl.generic; +import rfl.json; + +#if REFLECTCPP_MODULE_TEST_AVRO +import rfl.avro; +#endif +#if REFLECTCPP_MODULE_TEST_BOOST_SERIALIZATION +import rfl.boost_serialization; +#endif +#if REFLECTCPP_MODULE_TEST_BSON +import rfl.bson; +#endif +#if REFLECTCPP_MODULE_TEST_CAPNPROTO +import rfl.capnproto; +#endif +#if REFLECTCPP_MODULE_TEST_CBOR +import rfl.cbor; +#endif +#if REFLECTCPP_MODULE_TEST_CEREAL +import rfl.cereal; +#endif +#if REFLECTCPP_MODULE_TEST_CSV +import rfl.csv; +#endif +#if REFLECTCPP_MODULE_TEST_FLEXBUFFERS +import rfl.flexbuf; +#endif +#if REFLECTCPP_MODULE_TEST_MSGPACK +import rfl.msgpack; +#endif +#if REFLECTCPP_MODULE_TEST_PARQUET +import rfl.parquet; +#endif +#if REFLECTCPP_MODULE_TEST_TOML +import rfl.toml; +#endif +#if REFLECTCPP_MODULE_TEST_UBJSON +import rfl.ubjson; +#endif +#if REFLECTCPP_MODULE_TEST_XML +import rfl.xml; +#endif +#if REFLECTCPP_MODULE_TEST_YAML +import rfl.yaml; +#endif +#if REFLECTCPP_MODULE_TEST_YAS +import rfl.yas; +#endif + +namespace { + +struct Person { + std::string first_name; + int age; +}; + +struct Config { + std::string host_name; + int port; +}; + +bool is_person(const Person& person, const std::string& first_name, + const int age) { + return person.first_name == first_name && person.age == age; +} + +int test_core_module() { + const auto field = rfl::make_field<"first_name">(std::string("Marge")); + const auto named_tuple = field * rfl::make_field<"age">(42); + const rfl::Result result = rfl::get<"age">(named_tuple); + + if (!result) { + return 10; + } + if (*result != 42) { + return 11; + } + if (rfl::get<"first_name">(named_tuple) != "Marge") { + return 12; + } + return 0; +} + +int test_json_module() { + const auto json = rfl::json::write(Person{.first_name = "Homer", .age = 45}); + const auto person = rfl::json::read(json); + + if (!person) { + return 20; + } + if (!is_person(*person, "Homer", 45)) { + return 21; + } + return 0; +} + +int test_generic_module() { + const auto generic = + rfl::generic::write(Person{.first_name = "Lisa", .age = 8}); + const auto person = rfl::generic::read(generic); + + if (!person) { + return 30; + } + if (!is_person(*person, "Lisa", 8)) { + return 31; + } + return 0; +} + +int test_cli_module() { + char arg0[] = "reflect-cpp"; + char arg1[] = "--host-name=springfield"; + char arg2[] = "--port=742"; + char* argv[] = {arg0, arg1, arg2}; + + const auto config = rfl::cli::read(3, argv); + if (!config) { + return 40; + } + if (config->host_name != "springfield") { + return 41; + } + if (config->port != 742) { + return 42; + } + return 0; +} + +#if REFLECTCPP_MODULE_TEST_AVRO +int test_avro_module() { + const auto bytes = rfl::avro::write(Person{.first_name = "Apu", .age = 36}); + const auto person = rfl::avro::read(bytes); + return person && is_person(*person, "Apu", 36) ? 0 : 50; +} +#endif + +#if REFLECTCPP_MODULE_TEST_BOOST_SERIALIZATION +int test_boost_serialization_module() { + const auto bytes = rfl::boost_serialization::write( + Person{.first_name = "Barney", .age = 40}); + const auto person = rfl::boost_serialization::read(bytes); + return person && is_person(*person, "Barney", 40) ? 0 : 60; +} +#endif + +#if REFLECTCPP_MODULE_TEST_BSON +int test_bson_module() { + const auto bytes = rfl::bson::write(Person{.first_name = "Moe", .age = 48}); + const auto person = rfl::bson::read(bytes); + return person && is_person(*person, "Moe", 48) ? 0 : 70; +} +#endif + +#if REFLECTCPP_MODULE_TEST_CAPNPROTO +int test_capnproto_module() { + const auto bytes = + rfl::capnproto::write(Person{.first_name = "Patty", .age = 44}); + const auto person = rfl::capnproto::read(bytes); + return person && is_person(*person, "Patty", 44) ? 0 : 80; +} +#endif + +#if REFLECTCPP_MODULE_TEST_CBOR +int test_cbor_module() { + const auto bytes = rfl::cbor::write(Person{.first_name = "Selma", .age = 44}); + const auto person = rfl::cbor::read(bytes); + return person && is_person(*person, "Selma", 44) ? 0 : 90; +} +#endif + +#if REFLECTCPP_MODULE_TEST_CEREAL +int test_cereal_module() { + const auto bytes = rfl::cereal::write(Person{.first_name = "Ned", .age = 60}); + const auto person = rfl::cereal::read(bytes); + return person && is_person(*person, "Ned", 60) ? 0 : 100; +} +#endif + +#if REFLECTCPP_MODULE_TEST_CSV +int test_csv_module() { + const auto csv = rfl::csv::write(std::vector{{"Lenny", 38}}); + const auto people = rfl::csv::read>(csv); + return people && people->size() == 1 && + is_person(people->front(), "Lenny", 38) + ? 0 + : 110; +} +#endif + +#if REFLECTCPP_MODULE_TEST_FLEXBUFFERS +int test_flexbuf_module() { + const auto bytes = + rfl::flexbuf::write(Person{.first_name = "Carl", .age = 39}); + const auto person = rfl::flexbuf::read(bytes); + return person && is_person(*person, "Carl", 39) ? 0 : 120; +} +#endif + +#if REFLECTCPP_MODULE_TEST_MSGPACK +int test_msgpack_module() { + const auto bytes = + rfl::msgpack::write(Person{.first_name = "Milhouse", .age = 10}); + const auto person = rfl::msgpack::read(bytes); + return person && is_person(*person, "Milhouse", 10) ? 0 : 130; +} +#endif + +#if REFLECTCPP_MODULE_TEST_PARQUET +int test_parquet_module() { + const auto bytes = rfl::parquet::write(std::vector{{"Ralph", 8}}); + const auto people = rfl::parquet::read>(bytes); + return people && people->size() == 1 && is_person(people->front(), "Ralph", 8) + ? 0 + : 140; +} +#endif + +#if REFLECTCPP_MODULE_TEST_TOML +int test_toml_module() { + const auto toml = + rfl::toml::write(Person{.first_name = "Skinner", .age = 45}); + const auto person = rfl::toml::read(toml); + return person && is_person(*person, "Skinner", 45) ? 0 : 150; +} +#endif + +#if REFLECTCPP_MODULE_TEST_UBJSON +int test_ubjson_module() { + const auto bytes = + rfl::ubjson::write(Person{.first_name = "Nelson", .age = 12}); + const auto person = rfl::ubjson::read(bytes); + return person && is_person(*person, "Nelson", 12) ? 0 : 160; +} +#endif + +#if REFLECTCPP_MODULE_TEST_XML +int test_xml_module() { + const auto xml = rfl::xml::write(Person{.first_name = "Edna", .age = 41}); + const auto person = rfl::xml::read(xml); + return person && is_person(*person, "Edna", 41) ? 0 : 170; +} +#endif + +#if REFLECTCPP_MODULE_TEST_YAML +int test_yaml_module() { + const auto yaml = rfl::yaml::write(Person{.first_name = "Agnes", .age = 75}); + const auto person = rfl::yaml::read(yaml); + return person && is_person(*person, "Agnes", 75) ? 0 : 180; +} +#endif + +#if REFLECTCPP_MODULE_TEST_YAS +int test_yas_module() { + const auto bytes = rfl::yas::write(Person{.first_name = "Otto", .age = 29}); + const auto person = rfl::yas::read(bytes); + return person && is_person(*person, "Otto", 29) ? 0 : 190; +} +#endif + +} // namespace + +int main() { + if (const auto rc = test_core_module(); rc != 0) { + return rc; + } + if (const auto rc = test_json_module(); rc != 0) { + return rc; + } + if (const auto rc = test_generic_module(); rc != 0) { + return rc; + } + if (const auto rc = test_cli_module(); rc != 0) { + return rc; + } +#if REFLECTCPP_MODULE_TEST_AVRO + if (const auto rc = test_avro_module(); rc != 0) { + return rc; + } +#endif +#if REFLECTCPP_MODULE_TEST_BOOST_SERIALIZATION + if (const auto rc = test_boost_serialization_module(); rc != 0) { + return rc; + } +#endif +#if REFLECTCPP_MODULE_TEST_BSON + if (const auto rc = test_bson_module(); rc != 0) { + return rc; + } +#endif +#if REFLECTCPP_MODULE_TEST_CAPNPROTO + if (const auto rc = test_capnproto_module(); rc != 0) { + return rc; + } +#endif +#if REFLECTCPP_MODULE_TEST_CBOR + if (const auto rc = test_cbor_module(); rc != 0) { + return rc; + } +#endif +#if REFLECTCPP_MODULE_TEST_CEREAL + if (const auto rc = test_cereal_module(); rc != 0) { + return rc; + } +#endif +#if REFLECTCPP_MODULE_TEST_CSV + if (const auto rc = test_csv_module(); rc != 0) { + return rc; + } +#endif +#if REFLECTCPP_MODULE_TEST_FLEXBUFFERS + if (const auto rc = test_flexbuf_module(); rc != 0) { + return rc; + } +#endif +#if REFLECTCPP_MODULE_TEST_MSGPACK + if (const auto rc = test_msgpack_module(); rc != 0) { + return rc; + } +#endif +#if REFLECTCPP_MODULE_TEST_PARQUET + if (const auto rc = test_parquet_module(); rc != 0) { + return rc; + } +#endif +#if REFLECTCPP_MODULE_TEST_TOML + if (const auto rc = test_toml_module(); rc != 0) { + return rc; + } +#endif +#if REFLECTCPP_MODULE_TEST_UBJSON + if (const auto rc = test_ubjson_module(); rc != 0) { + return rc; + } +#endif +#if REFLECTCPP_MODULE_TEST_XML + if (const auto rc = test_xml_module(); rc != 0) { + return rc; + } +#endif +#if REFLECTCPP_MODULE_TEST_YAML + if (const auto rc = test_yaml_module(); rc != 0) { + return rc; + } +#endif +#if REFLECTCPP_MODULE_TEST_YAS + if (const auto rc = test_yas_module(); rc != 0) { + return rc; + } +#endif + return 0; +} From 9f670f9713094250a99ca86caf3ac855f695b3d6 Mon Sep 17 00:00:00 2001 From: Perdixky <3293789706@qq.com> Date: Sun, 17 May 2026 22:28:37 +0800 Subject: [PATCH 2/2] style: format cli reader Apply clang-format to include/rfl/cli/Reader.hpp only. Signed-off-by: Perdixky <3293789706@qq.com> Co-authored-by: Codex --- include/rfl/cli/Reader.hpp | 151 +++++++++++++++++-------------------- 1 file changed, 71 insertions(+), 80 deletions(-) diff --git a/include/rfl/cli/Reader.hpp b/include/rfl/cli/Reader.hpp index 6d62f562..b10d47ed 100644 --- a/include/rfl/cli/Reader.hpp +++ b/include/rfl/cli/Reader.hpp @@ -2,8 +2,8 @@ #define RFL_CLI_READER_HPP_ #include -#include #include +#include #include #include #include @@ -26,9 +26,10 @@ inline constexpr char path_separator = '.'; /// Example: `--ports=8080,8081,8082` for an array of ports. inline constexpr char array_delimiter = ','; -/// Represents a CLI variable that can be a direct value or a path in the argument map. -/// The `path` represents the hierarchical key (e.g., "database.host"). -/// The `direct_value` is used when parsing array elements directly. +/// Represents a CLI variable that can be a direct value or a path in the +/// argument map. The `path` represents the hierarchical key (e.g., +/// "database.host"). The `direct_value` is used when parsing array elements +/// directly. struct CliVarType { const std::map* const args = nullptr; const std::string path; @@ -55,10 +56,10 @@ struct CliArrayType { /// @param _str The string to parse /// @param _path The CLI argument path (unused for strings) /// @return The string unchanged -template requires std::same_as -rfl::Result parse_value( - const std::string& _str, const std::string& -) noexcept { +template + requires std::same_as +rfl::Result parse_value(const std::string& _str, + const std::string&) noexcept { return _str; } @@ -68,22 +69,23 @@ rfl::Result parse_value( /// @param _str The string to parse /// @param _path The CLI argument path (for error messages) /// @return A Result containing the boolean value or an error -template requires std::same_as -rfl::Result parse_value( - const std::string& _str, const std::string& _path -) noexcept { +template + requires std::same_as +rfl::Result parse_value(const std::string& _str, + const std::string& _path) noexcept { if (_str.empty() || _str == "true" || _str == "1") { return true; } if (_str == "false" || _str == "0") { return false; } - return error( - "Could not cast '" + _str + "' to boolean for key '" + _path + "'."); + return error("Could not cast '" + _str + "' to boolean for key '" + _path + + "'."); } /// Parses a string value to a floating-point number. -/// Uses locale-independent parsing (C locale) to ensure "3.14" works consistently. +/// Uses locale-independent parsing (C locale) to ensure "3.14" works +/// consistently. /// @tparam T Must be a floating-point type (float, double, long double) /// @param _str The string to parse /// @param _path The CLI argument path (for error messages) @@ -92,10 +94,10 @@ rfl::Result parse_value( // std::strtod depends on the C locale (LC_NUMERIC), so "3.14" can fail // under locales that use comma as decimal separator. // Use strtod_l with an explicit "C" locale on all platforms for consistency. -template requires (std::is_floating_point_v) -rfl::Result parse_value( - const std::string& _str, const std::string& _path -) noexcept { +template + requires(std::is_floating_point_v) +rfl::Result parse_value(const std::string& _str, + const std::string& _path) noexcept { char* end = nullptr; #ifdef _WIN32 const auto c_locale = _create_locale(LC_NUMERIC, "C"); @@ -107,8 +109,8 @@ rfl::Result parse_value( freelocale(c_locale); #endif if (end != _str.c_str() + _str.size()) { - return error( - "Could not cast '" + _str + "' to floating point for key '" + _path + "'."); + return error("Could not cast '" + _str + "' to floating point for key '" + + _path + "'."); } return static_cast(value); } @@ -119,23 +121,24 @@ rfl::Result parse_value( /// @param _str The string to parse /// @param _path The CLI argument path (for error messages) /// @return A Result containing the parsed integer or an error -template requires (std::is_integral_v && !std::same_as) -rfl::Result parse_value( - const std::string& _str, const std::string& _path -) noexcept { +template + requires(std::is_integral_v && !std::same_as) +rfl::Result parse_value(const std::string& _str, + const std::string& _path) noexcept { T value; const auto [ptr, ec] = std::from_chars(_str.data(), _str.data() + _str.size(), value); if (ec != std::errc() || ptr != _str.data() + _str.size()) { - return error( - "Could not cast '" + _str + "' to integer for key '" + _path + "'."); + return error("Could not cast '" + _str + "' to integer for key '" + _path + + "'."); } return value; } /// Reader for command-line interface arguments. -/// Parses hierarchical key-value pairs from CLI arguments (e.g., --database.host=localhost). -/// Supports arrays through comma-delimited values (e.g., --ports=8080,8081). +/// Parses hierarchical key-value pairs from CLI arguments (e.g., +/// --database.host=localhost). Supports arrays through comma-delimited values +/// (e.g., --ports=8080,8081). struct Reader { using InputArrayType = CliArrayType; using InputObjectType = CliObjectType; @@ -147,31 +150,32 @@ struct Reader { /// Gets a specific element from a CLI array by index. /// @param _idx The index of the element to retrieve /// @param _arr The CLI array - /// @return A Result containing the element as a CliVarType or an error if out of bounds + /// @return A Result containing the element as a CliVarType or an error if out + /// of bounds rfl::Result get_field_from_array( const size_t _idx, const InputArrayType& _arr) const noexcept { if (_idx >= _arr.values.size()) { - return error( - std::string("Index ") + std::to_string(_idx) + " out of bounds."); + return error(std::string("Index ") + std::to_string(_idx) + + " out of bounds."); } return InputVarType{nullptr, "", _arr.values[_idx]}; } /// Gets a specific field from a CLI object by name. - /// Constructs a child path by appending the field name to the object's prefix. + /// Constructs a child path by appending the field name to the object's + /// prefix. /// @param _name The field name /// @param _obj The CLI object /// @return A Result containing a CliVarType for accessing the field rfl::Result get_field_from_object( const std::string& _name, const InputObjectType& _obj) const noexcept { - const auto child_path = _obj.prefix.empty() - ? _name - : _obj.prefix + _name; + const auto child_path = _obj.prefix.empty() ? _name : _obj.prefix + _name; return InputVarType{_obj.args, child_path, std::nullopt}; } /// Checks if a CLI variable is empty (has no value). - /// A variable is empty if there's no direct value and no matching key in the argument map. + /// A variable is empty if there's no direct value and no matching key in the + /// argument map. /// @param _var The CLI variable to check /// @return true if the variable is empty, false otherwise bool is_empty(const InputVarType& _var) const noexcept { @@ -186,23 +190,21 @@ struct Reader { } const auto prefix = _var.path + path_separator; const auto it = _var.args->lower_bound(prefix); - return it == _var.args->end() - || it->first.substr(0, prefix.size()) != prefix; + return it == _var.args->end() || + it->first.substr(0, prefix.size()) != prefix; } /// Reads all elements from a CLI array using the provided array reader. - /// @tparam ArrayReader The type of reader that processes individual array elements + /// @tparam ArrayReader The type of reader that processes individual array + /// elements /// @param _array_reader The reader object that processes each element /// @param _arr The CLI array to read from /// @return std::nullopt on success, or an Error if reading fails template - std::optional read_array( - const ArrayReader& _array_reader, - const InputArrayType& _arr - ) const noexcept { + std::optional read_array(const ArrayReader& _array_reader, + const InputArrayType& _arr) const noexcept { for (const auto& val : _arr.values) { - const auto err = _array_reader.read( - InputVarType{nullptr, "", val}); + const auto err = _array_reader.read(InputVarType{nullptr, "", val}); if (err) { return err; } @@ -211,36 +213,33 @@ struct Reader { } /// Reads all fields from a CLI object using the provided object reader. - /// Iterates through all arguments with the object's prefix and extracts child field names. - /// @tparam ObjectReader The type of reader that processes individual object fields + /// Iterates through all arguments with the object's prefix and extracts child + /// field names. + /// @tparam ObjectReader The type of reader that processes individual object + /// fields /// @param _object_reader The reader object that processes each field /// @param _obj The CLI object to read from /// @return std::nullopt on success, or an Error if reading fails template - std::optional read_object( - const ObjectReader& _object_reader, - const InputObjectType& _obj - ) const noexcept { + std::optional read_object(const ObjectReader& _object_reader, + const InputObjectType& _obj) const noexcept { std::set seen; - auto it = _obj.prefix.empty() - ? _obj.args->begin() - : _obj.args->lower_bound(_obj.prefix); + auto it = _obj.prefix.empty() ? _obj.args->begin() + : _obj.args->lower_bound(_obj.prefix); while (it != _obj.args->end()) { - if (!_obj.prefix.empty() - && it->first.substr(0, _obj.prefix.size()) != _obj.prefix) { + if (!_obj.prefix.empty() && + it->first.substr(0, _obj.prefix.size()) != _obj.prefix) { break; } const auto rest = std::string_view(it->first).substr(_obj.prefix.size()); const auto separator_pos = rest.find(path_separator); - const auto child = std::string( - separator_pos == std::string_view::npos - ? rest - : rest.substr(0, separator_pos)); + const auto child = std::string(separator_pos == std::string_view::npos + ? rest + : rest.substr(0, separator_pos)); if (!child.empty() && seen.insert(child).second) { const auto child_path = _obj.prefix + child; - _object_reader.read( - std::string_view(child), - InputVarType{_obj.args, child_path, std::nullopt}); + _object_reader.read(std::string_view(child), + InputVarType{_obj.args, child_path, std::nullopt}); } ++it; } @@ -279,14 +278,12 @@ struct Reader { rfl::Result to_object( const InputVarType& _var) const noexcept { if (!_var.args) { - return error("Cannot convert to object: no argument map available" - + (_var.path.empty() - ? std::string(".") - : " for key '" + _var.path + "'.")); + return error("Cannot convert to object: no argument map available" + + (_var.path.empty() ? std::string(".") + : " for key '" + _var.path + "'.")); } - const auto prefix = _var.path.empty() - ? std::string("") - : _var.path + path_separator; + const auto prefix = + _var.path.empty() ? std::string("") : _var.path + path_separator; return InputObjectType{_var.args, prefix}; } @@ -295,16 +292,13 @@ struct Reader { /// @param _var The CLI variable (unused) /// @return Always returns an error template - rfl::Result use_custom_constructor( - const InputVarType& - ) const noexcept { + rfl::Result use_custom_constructor(const InputVarType&) const noexcept { return error("Custom constructors are not supported for CLI parsing."); } private: static std::optional get_value( - const InputVarType& _var - ) noexcept { + const InputVarType& _var) noexcept { if (_var.direct_value) { return *_var.direct_value; } @@ -318,9 +312,7 @@ struct Reader { return it->second; } - static std::vector split( - const std::string& _str, char _delim - ) { + static std::vector split(const std::string& _str, char _delim) { std::vector result; if (_str.empty()) { return result; @@ -341,7 +333,6 @@ struct Reader { } return result; } - }; } // namespace rfl::cli