From 6b82c108c01aa005a6e395214a261cc85efd5c90 Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Sat, 7 Feb 2026 02:50:28 +0100 Subject: [PATCH 01/13] Started developing comments --- include/rfl/Commented.hpp | 149 ++++++++++++++++++++++ include/rfl/parsing/Parser.hpp | 1 + include/rfl/parsing/Parser_commented.hpp | 67 ++++++++++ include/rfl/parsing/supports_comments.hpp | 38 ++++++ 4 files changed, 255 insertions(+) create mode 100644 include/rfl/Commented.hpp create mode 100644 include/rfl/parsing/Parser_commented.hpp create mode 100644 include/rfl/parsing/supports_comments.hpp diff --git a/include/rfl/Commented.hpp b/include/rfl/Commented.hpp new file mode 100644 index 00000000..4d4d59f1 --- /dev/null +++ b/include/rfl/Commented.hpp @@ -0,0 +1,149 @@ +#ifndef RFL_COMMENTED_HPP_ +#define RFL_COMMENTED_HPP_ + +#include +#include +#include +#include + +namespace rfl { + +template +class Commented { + public: + using Type = std::remove_cvref_t; + + Commented() : value_(Type()) {} + + Commented(const Type& _value) : value_(_value) {} + + Commented(Type&& _value) noexcept : value_(std::move(_value)) {} + + Commented(const Type& _value, std::optional _comment) + : comment_(std::move(_comment)), value_(_value) {} + + Commented(Type&& _value, std::optional _comment) noexcept + : comment_(std::move(_comment)), value_(std::move(_value)) {} + + Commented(Commented&& _commented) noexcept = default; + + Commented(const Commented& _commented) = default; + + template + Commented(const Commented& _commented) + : comment_(_commented.comment()), value_(_commented.get()) {} + + template + Commented(Commented&& _commented) noexcept( + noexcept(Type(std::move(_commented.value())))) + : comment_(std::move(_commented.comment())), + value_(std::move(_commented.value())) {} + + template + requires(std::is_convertible_v) + Commented(const U& _value) : value_(_value) {} + + template + requires(std::is_convertible_v) + Commented(U&& _value) noexcept : value_(std::forward(_value)) {} + + template + requires(std::is_convertible_v) + Commented(const Commented& _commented) : value_(_commented.value()) {} + + /// Assigns the underlying object to its default value. + template + requires(std::is_default_constructible_v) + Commented(const Default&) : value_(Type()) {} + + ~Commented() = default; + + /// Sets the comment associated with the field. + void add_comment(std::string _comment) { comment_ = std::move(_comment); } + + /// Returns the comment associated with the field, if any. + const std::optional& comment() const { return comment_; } + + /// Returns the underlying object. + Type& get() { return value_; } + + /// Returns the underlying object. + const Type& get() const { return value_; } + + /// Returns the underlying object. + Type& operator()() { return value_; } + + /// Returns the underlying object. + const Type& operator()() const { return value_; } + + /// Assigns the underlying object. + auto& operator=(const Type& _value) { + value_ = _value; + return *this; + } + + /// Assigns the underlying object. + auto& operator=(Type&& _value) noexcept { + value_ = std::move(_value); + return *this; + } + + /// Assigns the underlying object. + template + requires std::is_convertible_v + auto& operator=(const U& _value) { + value_ = _value; + return *this; + } + + /// Assigns the underlying object to its default value. + template + requires std::is_default_constructible_v + auto& operator=(const Default&) { + value_ = Type(); + return *this; + } + + /// Assigns the underlying object. + Commented& operator=(const Commented& _commented) = default; + + /// Assigns the underlying object. + Commented& operator=(Commented&& _commented) = default; + + /// Assigns the underlying object. + template + auto& operator=(const Commented& _commented) { + value_ = _commented.get(); + return *this; + } + + /// Assigns the underlying object. + template + auto& operator=(Commented&& _commented) { + value_ = std::forward(_commented.value_); + return *this; + } + + /// Assigns the underlying object. + void set(const Type& _value) { value_ = _value; } + + /// Assigns the underlying object. + void set(Type&& _value) { value_ = std::move(_value); } + + /// Returns the underlying object. + Type& value() { return value_; } + + /// Returns the underlying object. + const Type& value() const { return value_; } + + private: + /// The comment associated with the field. + std::optional comment_; + + /// The underlying value. + Type value_; +}; + +} // namespace rfl + +#endif diff --git a/include/rfl/parsing/Parser.hpp b/include/rfl/parsing/Parser.hpp index 4f1cb355..1d9e4d34 100644 --- a/include/rfl/parsing/Parser.hpp +++ b/include/rfl/parsing/Parser.hpp @@ -9,6 +9,7 @@ #include "Parser_box.hpp" #include "Parser_bytestring.hpp" #include "Parser_c_array.hpp" +#include "Parser_commented.hpp" #include "Parser_default.hpp" #include "Parser_default_val.hpp" #include "Parser_duration.hpp" diff --git a/include/rfl/parsing/Parser_commented.hpp b/include/rfl/parsing/Parser_commented.hpp new file mode 100644 index 00000000..dbf52114 --- /dev/null +++ b/include/rfl/parsing/Parser_commented.hpp @@ -0,0 +1,67 @@ +#ifndef RFL_PARSING_PARSER_COMMENTED_HPP_ +#define RFL_PARSING_PARSER_COMMENTED_HPP_ + +#include +#include +#include + +#include "../Commented.hpp" +#include "../Ref.hpp" +#include "../Result.hpp" +#include "../always_false.hpp" +#include "Parent.hpp" +#include "Parser_base.hpp" +#include "schema/Type.hpp" +#include "schemaful/IsSchemafulReader.hpp" +#include "schemaful/IsSchemafulWriter.hpp" +#include "schemaful/OptionalReader.hpp" +#include "supports_comments.hpp" + +namespace rfl::parsing { + +template + requires AreReaderAndWriter> +struct Parser, ProcessorsType> { + using InputVarType = typename R::InputVarType; + + using ParentType = Parent; + + static Result> read(const R& _r, + const InputVarType& _var) noexcept { + if constexpr (supports_comments) { + return Parser, ProcessorsType>::read(_r, + _var) + .transform([&](auto&& _t) { + return Commented(std::move(_t), _r.get_comment(_var)); + }); + } else { + return Parser, ProcessorsType>::read(_r, + _var) + .transform([](auto&& _t) { + return Commented(std::forward(_t)); + }); + } + } + + template + static void write(const W& _w, const Commented& _c, const P& _parent) { + if constexpr (supports_comments) { + if (_c.comment()) { + _w.add_comment(_parent, *_c.comment()); + } + } + Parser, ProcessorsType>::write(_w, _c.get(), + _parent); + } + + static schema::Type to_schema( + std::map* _definitions) { + using U = std::remove_cvref_t; + return schema::Type{schema::Type::Optional{Ref::make( + Parser::to_schema(_definitions))}}; + } +}; + +} // namespace rfl::parsing + +#endif diff --git a/include/rfl/parsing/supports_comments.hpp b/include/rfl/parsing/supports_comments.hpp new file mode 100644 index 00000000..0acc5033 --- /dev/null +++ b/include/rfl/parsing/supports_comments.hpp @@ -0,0 +1,38 @@ +#ifndef RFL_PARSING_SUPPORTSCOMMENTS_HPP_ +#define RFL_PARSING_SUPPORTSCOMMENTS_HPP_ + +#include +#include +#include +#include + +#include "../Result.hpp" +#include "IsReader.hpp" +#include "IsWriter.hpp" + +namespace rfl::parsing { + +/// Determines whether a reader supports comments. +template +concept reader_supports_comments = + requires(R r, typename R::InputVarType var, std::string_view comment) { + { r.get_comment(var) } -> std::same_as>; + }; + +/// Determines whether a writer supports comments. +template +concept writer_supports_comments = + requires(W w, Parent parent, std::string_view comment) { + { + w.add_comment(parent, comment) + } -> std::same_as; + }; + +template +concept supports_comments = + (IsReader && reader_supports_comments) || + (IsWriter && writer_supports_comments); + +} // namespace rfl::parsing + +#endif From f2f2f36b95020be883d1163d231d2a4a3c61d9ba Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Sat, 7 Feb 2026 11:24:42 +0100 Subject: [PATCH 02/13] Added comments to YAML --- include/rfl.hpp | 3 ++- include/rfl/Commented.hpp | 2 ++ include/rfl/parsing/Parent.hpp | 16 ++++++++++++++ include/rfl/parsing/Parser_commented.hpp | 23 ++++++------------- include/rfl/parsing/supports_comments.hpp | 25 +++++---------------- include/rfl/yaml/Writer.hpp | 18 ++++++++++----- src/rfl/yaml/Writer.cpp | 14 ++++++++++++ tests/yaml/test_comment.cpp | 27 +++++++++++++++++++++++ 8 files changed, 86 insertions(+), 42 deletions(-) create mode 100644 tests/yaml/test_comment.cpp diff --git a/include/rfl.hpp b/include/rfl.hpp index b57542b6..e8ba77f8 100644 --- a/include/rfl.hpp +++ b/include/rfl.hpp @@ -16,6 +16,8 @@ #include "rfl/Binary.hpp" #include "rfl/Box.hpp" #include "rfl/Bytestring.hpp" +#include "rfl/CamelCaseToSnakeCase.hpp" +#include "rfl/Commented.hpp" #include "rfl/DefaultIfMissing.hpp" #include "rfl/DefaultVal.hpp" #include "rfl/Description.hpp" @@ -41,7 +43,6 @@ #include "rfl/Skip.hpp" #include "rfl/SnakeCaseToCamelCase.hpp" #include "rfl/SnakeCaseToPascalCase.hpp" -#include "rfl/CamelCaseToSnakeCase.hpp" #include "rfl/TaggedUnion.hpp" #include "rfl/Timestamp.hpp" #include "rfl/UnderlyingEnums.hpp" diff --git a/include/rfl/Commented.hpp b/include/rfl/Commented.hpp index 4d4d59f1..8e24438b 100644 --- a/include/rfl/Commented.hpp +++ b/include/rfl/Commented.hpp @@ -6,6 +6,8 @@ #include #include +#include "default.hpp" + namespace rfl { template diff --git a/include/rfl/parsing/Parent.hpp b/include/rfl/parsing/Parent.hpp index c8d73be7..8ce99fa4 100644 --- a/include/rfl/parsing/Parent.hpp +++ b/include/rfl/parsing/Parent.hpp @@ -7,6 +7,7 @@ #include "../always_false.hpp" #include "schemaful/IsSchemafulWriter.hpp" #include "supports_attributes.hpp" +#include "supports_comments.hpp" namespace rfl::parsing { @@ -71,6 +72,21 @@ struct Parent { } } + /// Adds a comment to the parent element, if supported by the writer. + template + static void add_comment(const W& _w, std::string_view _comment, + const ParentType& _parent) { + using Type = std::remove_cvref_t; + if constexpr (supports_comments) { + if constexpr (std::is_same()) { + _w.add_comment_to_array(_comment, _parent.arr_); + + } else if constexpr (std::is_same()) { + _w.add_comment_to_object(_comment, _parent.obj_); + } + } + } + // For schemaful formats only. template static auto add_map(const W& _w, const size_t _size, diff --git a/include/rfl/parsing/Parser_commented.hpp b/include/rfl/parsing/Parser_commented.hpp index dbf52114..7a0471b0 100644 --- a/include/rfl/parsing/Parser_commented.hpp +++ b/include/rfl/parsing/Parser_commented.hpp @@ -28,30 +28,21 @@ struct Parser, ProcessorsType> { static Result> read(const R& _r, const InputVarType& _var) noexcept { - if constexpr (supports_comments) { - return Parser, ProcessorsType>::read(_r, - _var) - .transform([&](auto&& _t) { - return Commented(std::move(_t), _r.get_comment(_var)); - }); - } else { - return Parser, ProcessorsType>::read(_r, - _var) - .transform([](auto&& _t) { - return Commented(std::forward(_t)); - }); - } + return Parser, ProcessorsType>::read(_r, _var) + .transform([](auto&& _t) { + return Commented(std::forward(_t)); + }); } template static void write(const W& _w, const Commented& _c, const P& _parent) { + Parser, ProcessorsType>::write(_w, _c.get(), + _parent); if constexpr (supports_comments) { if (_c.comment()) { - _w.add_comment(_parent, *_c.comment()); + ParentType::add_comment(_w, *_c.comment(), _parent); } } - Parser, ProcessorsType>::write(_w, _c.get(), - _parent); } static schema::Type to_schema( diff --git a/include/rfl/parsing/supports_comments.hpp b/include/rfl/parsing/supports_comments.hpp index 0acc5033..a91332b3 100644 --- a/include/rfl/parsing/supports_comments.hpp +++ b/include/rfl/parsing/supports_comments.hpp @@ -7,31 +7,18 @@ #include #include "../Result.hpp" -#include "IsReader.hpp" -#include "IsWriter.hpp" namespace rfl::parsing { -/// Determines whether a reader supports comments. -template -concept reader_supports_comments = - requires(R r, typename R::InputVarType var, std::string_view comment) { - { r.get_comment(var) } -> std::same_as>; - }; - /// Determines whether a writer supports comments. template -concept writer_supports_comments = - requires(W w, Parent parent, std::string_view comment) { - { - w.add_comment(parent, comment) - } -> std::same_as; - }; - -template concept supports_comments = - (IsReader && reader_supports_comments) || - (IsWriter && writer_supports_comments); + requires(W w, typename W::OutputArrayType arr, + typename W::OutputObjectType obj, std::string_view comment) { + { w.add_comment_to_array(comment, &arr) } -> std::same_as; + + { w.add_comment_to_object(comment, &obj) } -> std::same_as; + }; } // namespace rfl::parsing diff --git a/include/rfl/yaml/Writer.hpp b/include/rfl/yaml/Writer.hpp index b5bf0616..943f3b65 100644 --- a/include/rfl/yaml/Writer.hpp +++ b/include/rfl/yaml/Writer.hpp @@ -11,8 +11,7 @@ #include "../always_false.hpp" #include "../common.hpp" -namespace rfl { -namespace yaml { +namespace rfl::yaml { class RFL_API Writer { public: @@ -42,11 +41,17 @@ class RFL_API Writer { } OutputArrayType add_array_to_array(const size_t _size, - OutputArrayType* _parent) const; + OutputArrayType* /*_parent*/) const; OutputArrayType add_array_to_object(const std::string_view& _name, const size_t _size, - OutputObjectType* _parent) const; + OutputObjectType* /*_parent*/) const; + + void add_comment_to_array(const std::string_view& _comment, + OutputArrayType* _parent) const; + + void add_comment_to_object(const std::string_view& _comment, + OutputObjectType* _parent) const; OutputObjectType add_object_to_array(const size_t _size, OutputArrayType* _parent) const; @@ -122,6 +127,8 @@ class RFL_API Writer { OutputArrayType new_array() const; + void new_comment(const std::string_view& _comment) const; + OutputObjectType new_object(const std::string_view& _name) const; OutputObjectType new_object() const; @@ -130,7 +137,6 @@ class RFL_API Writer { const Ref out_; }; -} // namespace yaml -} // namespace rfl +} // namespace rfl::yaml #endif diff --git a/src/rfl/yaml/Writer.cpp b/src/rfl/yaml/Writer.cpp index a45b144c..eae111e0 100644 --- a/src/rfl/yaml/Writer.cpp +++ b/src/rfl/yaml/Writer.cpp @@ -29,6 +29,16 @@ Writer::OutputArrayType Writer::add_array_to_object( return new_array(_name); } +void Writer::add_comment_to_array(const std::string_view& _comment, + OutputArrayType*) const { + new_comment(_comment); +} + +void Writer::add_comment_to_object(const std::string_view& _comment, + OutputObjectType*) const { + new_comment(_comment); +} + Writer::OutputObjectType Writer::add_object_to_array( const size_t /*_size*/, OutputArrayType* /*_parent*/) const { return new_object(); @@ -68,6 +78,10 @@ Writer::OutputArrayType Writer::new_array() const { return OutputArrayType{}; } +void Writer::new_comment(const std::string_view& _comment) const { + (*out_) << YAML::Comment(std::string(_comment)); +} + Writer::OutputObjectType Writer::new_object( const std::string_view& _name) const { (*out_) << YAML::Key << std::string(_name) << YAML::Value << YAML::BeginMap; diff --git a/tests/yaml/test_comment.cpp b/tests/yaml/test_comment.cpp new file mode 100644 index 00000000..f57ad7c6 --- /dev/null +++ b/tests/yaml/test_comment.cpp @@ -0,0 +1,27 @@ +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_comment { + +struct Person { + std::string first_name; + std::string last_name; + rfl::Commented town; +}; + +TEST(yaml, test_comment) { + const auto homer = Person{.first_name = "Homer", + .last_name = "Simpson", + .town = rfl::Commented( + "Springfield", "The town where Homer lives")}; + + const auto yaml_str = rfl::yaml::write(homer); + + EXPECT_EQ(yaml_str, R"(first_name: Homer +last_name: Simpson +town: Springfield # The town where Homer lives)"); +} +} // namespace test_comment From d04cd7c38dea8935b79f4b9c82c72dec1b696420 Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Sat, 7 Feb 2026 11:49:21 +0100 Subject: [PATCH 03/13] Added comments to XML --- include/rfl/xml/Writer.hpp | 14 +++++++++----- src/rfl/xml/Writer.cpp | 22 ++++++++++++++-------- tests/xml/test_comment.cpp | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 13 deletions(-) create mode 100644 tests/xml/test_comment.cpp diff --git a/include/rfl/xml/Writer.hpp b/include/rfl/xml/Writer.hpp index e68582ce..3426fc70 100644 --- a/include/rfl/xml/Writer.hpp +++ b/include/rfl/xml/Writer.hpp @@ -10,8 +10,7 @@ #include "../always_false.hpp" #include "../common.hpp" -namespace rfl { -namespace xml { +namespace rfl ::xml { struct RFL_API Writer { struct XMLOutputArray { @@ -59,6 +58,12 @@ struct RFL_API Writer { const size_t _size, OutputObjectType* _parent) const; + void add_comment_to_array(const std::string_view& _comment, + OutputArrayType* _parent) const; + + void add_comment_to_object(const std::string_view& _comment, + OutputObjectType* _parent) const; + OutputObjectType add_object_to_array(const size_t _size, OutputArrayType* _parent) const; @@ -95,7 +100,7 @@ struct RFL_API Writer { template decltype(auto) to_string(const T& _val) const { if constexpr (std::is_same, std::string>()) { - return _val; // Return reference to avoid expensive string copy. + return _val; // Return reference to avoid expensive string copy. } else if constexpr (std::is_same, bool>()) { return _val ? "true" : "false"; } else if constexpr (std::is_floating_point>() || @@ -121,7 +126,6 @@ struct RFL_API Writer { std::string root_name_; }; -} // namespace xml -} // namespace rfl +} // namespace rfl::xml #endif // RFL_XML_WRITER_HPP_ diff --git a/src/rfl/xml/Writer.cpp b/src/rfl/xml/Writer.cpp index 83be3cdc..d46a90e1 100644 --- a/src/rfl/xml/Writer.cpp +++ b/src/rfl/xml/Writer.cpp @@ -38,27 +38,23 @@ Writer::Writer(const Ref& _root, const std::string& _root_name) Writer::~Writer() = default; Writer::OutputArrayType Writer::array_as_root(const size_t /*_size*/) const { - auto node_child = - Ref::make(root_->append_child(root_name_)); + auto node_child = Ref::make(root_->append_child(root_name_)); return OutputArrayType(root_name_, node_child); } Writer::OutputObjectType Writer::object_as_root(const size_t /*_size*/) const { - auto node_child = - Ref::make(root_->append_child(root_name_)); + auto node_child = Ref::make(root_->append_child(root_name_)); return OutputObjectType(node_child); } Writer::OutputVarType Writer::null_as_root() const { - auto node_child = - Ref::make(root_->append_child(root_name_)); + auto node_child = Ref::make(root_->append_child(root_name_)); return OutputVarType(node_child); } Writer::OutputVarType Writer::value_as_root_impl( const std::string& _str) const { - auto node_child = - Ref::make(root_->append_child(root_name_)); + auto node_child = Ref::make(root_->append_child(root_name_)); node_child->append_child(pugi::node_pcdata).set_value(_str); return OutputVarType(node_child); } @@ -74,6 +70,16 @@ Writer::OutputArrayType Writer::add_array_to_object( return OutputArrayType(_name, _parent->node_); } +void Writer::add_comment_to_array(const std::string_view& _comment, + OutputArrayType* _parent) const { + _parent->node_->append_child(pugi::node_comment).set_value(_comment); +} + +void Writer::add_comment_to_object(const std::string_view& _comment, + OutputObjectType* _parent) const { + _parent->node_->append_child(pugi::node_comment).set_value(_comment); +} + Writer::OutputVarType Writer::add_value_to_array_impl( const std::string& _str, OutputArrayType* _parent) const { auto node_child = diff --git a/tests/xml/test_comment.cpp b/tests/xml/test_comment.cpp new file mode 100644 index 00000000..5416ad53 --- /dev/null +++ b/tests/xml/test_comment.cpp @@ -0,0 +1,32 @@ +#include +#include +#include + +#include "write_and_read.hpp" + +namespace test_comment { + +struct Person { + std::string first_name; + std::string last_name; + rfl::Commented town; +}; + +TEST(xml, test_comment) { + const auto homer = Person{.first_name = "Homer", + .last_name = "Simpson", + .town = rfl::Commented( + "Springfield", "The town where Homer lives")}; + + const auto xml_str = rfl::xml::write(homer); + + EXPECT_EQ(xml_str, R"( + + Homer + Simpson + Springfield + + +)"); +} +} // namespace test_comment From ef3336093e8fb80582fe7f88410a59a9f9f8b5ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dr=2E=20Patrick=20Urbanke=20=28=E5=8A=89=E8=87=AA=E6=88=90?= =?UTF-8?q?=29?= Date: Sun, 8 Feb 2026 07:08:52 +0800 Subject: [PATCH 04/13] Add documentation for rfl::Commented feature (#600) --- README.md | 1 + docs/commented.md | 81 +++++++++++++++++++++++++++++++++++++++++++++++ mkdocs.yaml | 1 + 3 files changed, 83 insertions(+) create mode 100644 docs/commented.md diff --git a/README.md b/README.md index f82b555b..e044bd53 100644 --- a/README.md +++ b/README.md @@ -571,6 +571,7 @@ In addition, it supports the following custom containers: - `rfl::Binary`: Used to express numbers in binary format. - `rfl::Box`: Similar to `std::unique_ptr`, but (almost) guaranteed to never be null. - `rfl::Bytestring`: An alias for `std::vector`. Supported by Avro, BSON, Cap'n Proto, CBOR, flexbuffers, msgpack and UBJSON. +- `rfl::Commented`: Allows you to add comments to fields (supported by YAML and XML). - `rfl::Generic`: A catch-all type that can represent (almost) anything. - `rfl::Hex`: Used to express numbers in hex format. - `rfl::Literal`: An explicitly enumerated string. diff --git a/docs/commented.md b/docs/commented.md new file mode 100644 index 00000000..494bceee --- /dev/null +++ b/docs/commented.md @@ -0,0 +1,81 @@ +# `rfl::Commented` + +The `rfl::Commented` wrapper allows you to add comments to fields in your structs. These comments are then serialized in formats that support them, such as YAML and XML. + +Note that `rfl::Commented` is currently unsupported by formats that do not have a standard way of representing comments, such as JSON. Also note that comments are **write-only**: they are ignored during deserialization. + +## Example (YAML) + +In YAML, the comments are added as line comments (`#`): + +```cpp +struct Person { + std::string first_name; + std::string last_name; + rfl::Commented town; +}; + +const auto homer = Person{.first_name = "Homer", + .last_name = "Simpson", + .town = rfl::Commented( + "Springfield", "The town where Homer lives")}; + +const auto yaml_str = rfl::yaml::write(homer); +``` + +This will result in the following YAML: + +```yaml +first_name: Homer +last_name: Simpson +town: Springfield # The town where Homer lives +``` + +## Example (XML) + +In XML, the comments are added as `` blocks after the field: + +```cpp +struct Person { + std::string first_name; + std::string last_name; + rfl::Commented town; +}; + +const auto homer = Person{.first_name = "Homer", + .last_name = "Simpson", + .town = rfl::Commented( + "Springfield", "The town where Homer lives")}; + +const auto xml_str = rfl::xml::write(homer); +``` + +This will result in the following XML: + +```xml + + + Homer + Simpson + Springfield + + +``` + +## API convenience + +`rfl::Commented` provides several ways to access and modify the underlying value and the comment: + +- `.get()`, `.value()`, `operator()()` — access the underlying value (const and non-const overloads). +- `.comment()` — returns an `std::optional` containing the comment. +- `.add_comment(std::string)` — sets or updates the comment. +- `.set(...)`, `operator=(...)` — assign the underlying value. + +Example: + +```cpp +Person p; +p.town = "Springfield"; +p.town.add_comment("The town where Homer lives"); +std::string s = p.town.value(); +``` diff --git a/mkdocs.yaml b/mkdocs.yaml index 988c1339..24e7e1fd 100644 --- a/mkdocs.yaml +++ b/mkdocs.yaml @@ -123,6 +123,7 @@ nav: - rfl::Box and rfl::Ref: rfl_ref.md - rfl::Timestamp: timestamps.md - rfl::Skip: rfl_skip.md + - rfl::Commented: commented.md - rfl::Result: result.md - Standard containers: standard_containers.md - C arrays and inheritance: c_arrays_and_inheritance.md From d8cf631f9c08b5a4450c1e60620b7150bf800ab0 Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Sun, 8 Feb 2026 01:26:46 +0100 Subject: [PATCH 05/13] Added comments to the docs README --- docs/docs-readme.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/docs-readme.md b/docs/docs-readme.md index 813753c0..7d869c3d 100644 --- a/docs/docs-readme.md +++ b/docs/docs-readme.md @@ -24,6 +24,8 @@ [rfl::Skip](rfl_skip.md) - For skipping fields during serialization and/or deserialization. +[rfl::Commented](commented.md) - For adding comments to your serialized format (only supported by some formats, such as YAML). + [rfl::Result](result.md) - For error handling without exceptions. [Standard containers](standard_containers.md) - Describes how reflect-cpp treats containers in the standard library. From 4629f9fa84b9d6c7246fb3e9a1c2bd30e15cf5ca Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Sun, 8 Feb 2026 02:31:32 +0100 Subject: [PATCH 06/13] Add operator* --- include/rfl/Commented.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/rfl/Commented.hpp b/include/rfl/Commented.hpp index 8e24438b..31b9bc96 100644 --- a/include/rfl/Commented.hpp +++ b/include/rfl/Commented.hpp @@ -78,6 +78,12 @@ class Commented { /// Returns the underlying object. const Type& operator()() const { return value_; } + /// Returns the underlying object. + Type& operator*() { return value_; } + + /// Returns the underlying object. + const Type& operator*() const { return value_; } + /// Assigns the underlying object. auto& operator=(const Type& _value) { value_ = _value; From c1e49cbf83d3ea432cc13314cd88179d232bbc1f Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Sun, 8 Feb 2026 02:33:25 +0100 Subject: [PATCH 07/13] class -> struct --- include/rfl/Commented.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/rfl/Commented.hpp b/include/rfl/Commented.hpp index 31b9bc96..59916994 100644 --- a/include/rfl/Commented.hpp +++ b/include/rfl/Commented.hpp @@ -11,7 +11,7 @@ namespace rfl { template -class Commented { +struct Commented { public: using Type = std::remove_cvref_t; @@ -144,7 +144,6 @@ class Commented { /// Returns the underlying object. const Type& value() const { return value_; } - private: /// The comment associated with the field. std::optional comment_; From 40f6e404ceef195215159e8098006bb78e96fd41 Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Sun, 8 Feb 2026 02:35:37 +0100 Subject: [PATCH 08/13] Fix operator= --- include/rfl/Commented.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/rfl/Commented.hpp b/include/rfl/Commented.hpp index 59916994..e60e0899 100644 --- a/include/rfl/Commented.hpp +++ b/include/rfl/Commented.hpp @@ -122,13 +122,15 @@ struct Commented { template auto& operator=(const Commented& _commented) { value_ = _commented.get(); + comment_ = _commented.comment(); return *this; } /// Assigns the underlying object. template auto& operator=(Commented&& _commented) { - value_ = std::forward(_commented.value_); + value_ = std::move(_commented.value_); + comment_ = std::move(_commented.comment_); return *this; } From 19adda8984f5001c0c8deffbcf6d77b3a81c4c0a Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Sun, 8 Feb 2026 02:36:18 +0100 Subject: [PATCH 09/13] More sensible schema definition --- include/rfl/parsing/Parser_commented.hpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/rfl/parsing/Parser_commented.hpp b/include/rfl/parsing/Parser_commented.hpp index 7a0471b0..4720a5d5 100644 --- a/include/rfl/parsing/Parser_commented.hpp +++ b/include/rfl/parsing/Parser_commented.hpp @@ -47,9 +47,8 @@ struct Parser, ProcessorsType> { static schema::Type to_schema( std::map* _definitions) { - using U = std::remove_cvref_t; - return schema::Type{schema::Type::Optional{Ref::make( - Parser::to_schema(_definitions))}}; + return Parser, ProcessorsType>::to_schema( + _definitions); } }; From 16470592c9e7a5f3c2e2270cfb3e025ef4a34aa6 Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Sun, 8 Feb 2026 02:37:03 +0100 Subject: [PATCH 10/13] Fixed typo --- include/rfl/xml/Writer.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/rfl/xml/Writer.hpp b/include/rfl/xml/Writer.hpp index 3426fc70..6b032ff3 100644 --- a/include/rfl/xml/Writer.hpp +++ b/include/rfl/xml/Writer.hpp @@ -10,7 +10,7 @@ #include "../always_false.hpp" #include "../common.hpp" -namespace rfl ::xml { +namespace rfl::xml { struct RFL_API Writer { struct XMLOutputArray { From 5368fce85c35f0849bf98999272c53727f91ff92 Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Sun, 8 Feb 2026 02:37:54 +0100 Subject: [PATCH 11/13] Added XML --- docs/docs-readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs-readme.md b/docs/docs-readme.md index 7d869c3d..c98e2466 100644 --- a/docs/docs-readme.md +++ b/docs/docs-readme.md @@ -24,7 +24,7 @@ [rfl::Skip](rfl_skip.md) - For skipping fields during serialization and/or deserialization. -[rfl::Commented](commented.md) - For adding comments to your serialized format (only supported by some formats, such as YAML). +[rfl::Commented](commented.md) - For adding comments to your serialized format (only supported by some formats, such as YAML or XML). [rfl::Result](result.md) - For error handling without exceptions. From 89eaa1470a433a6168f9f9a32a6017885851385b Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Sun, 8 Feb 2026 02:39:30 +0100 Subject: [PATCH 12/13] Resolved last issue --- include/rfl/Commented.hpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/rfl/Commented.hpp b/include/rfl/Commented.hpp index e60e0899..0a944a7e 100644 --- a/include/rfl/Commented.hpp +++ b/include/rfl/Commented.hpp @@ -43,15 +43,16 @@ struct Commented { template requires(std::is_convertible_v) - Commented(const U& _value) : value_(_value) {} + Commented(const Commented& _commented) + : comment_(commented.comment()), value_(_commented.value()) {} template requires(std::is_convertible_v) - Commented(U&& _value) noexcept : value_(std::forward(_value)) {} + Commented(const U& _value) : value_(_value) {} template requires(std::is_convertible_v) - Commented(const Commented& _commented) : value_(_commented.value()) {} + Commented(U&& _value) noexcept : value_(std::forward(_value)) {} /// Assigns the underlying object to its default value. template From 5409d78a23d7419cf6506c90dce248997264e886 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dr=2E=20Patrick=20Urbanke=20=28=E5=8A=89=E8=87=AA=E6=88=90?= =?UTF-8?q?=29?= Date: Sun, 8 Feb 2026 13:34:18 +0800 Subject: [PATCH 13/13] Fixed typo --- include/rfl/Commented.hpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/rfl/Commented.hpp b/include/rfl/Commented.hpp index 0a944a7e..156416dd 100644 --- a/include/rfl/Commented.hpp +++ b/include/rfl/Commented.hpp @@ -36,15 +36,14 @@ struct Commented { : comment_(_commented.comment()), value_(_commented.get()) {} template - Commented(Commented&& _commented) noexcept( - noexcept(Type(std::move(_commented.value())))) + Commented(Commented&& _commented) noexcept : comment_(std::move(_commented.comment())), value_(std::move(_commented.value())) {} template requires(std::is_convertible_v) Commented(const Commented& _commented) - : comment_(commented.comment()), value_(_commented.value()) {} + : comment_(_commented.comment()), value_(_commented.value()) {} template requires(std::is_convertible_v)