diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f466d77..48a0449 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -261,7 +261,8 @@ jobs: - name: Generate headers run: | cd ../boost-root/libs/leaf - python3 gen/generate_single_header.py -i include/boost/leaf/detail/all.hpp -p include -o test/leaf.hpp boost/leaf + python3 scripts/generate_single_header.py -i include/boost/leaf/detail/all.hpp -p include -o test/leaf.hpp boost/leaf + python3 scripts/download_nlohmann_json.py - name: Run tests run: | @@ -329,7 +330,8 @@ jobs: - name: Generate headers run: | cd ../boost-root/libs/leaf - python3 gen/generate_single_header.py -i include/boost/leaf/detail/all.hpp -p include -o test/leaf.hpp boost/leaf + python3 scripts/generate_single_header.py -i include/boost/leaf/detail/all.hpp -p include -o test/leaf.hpp boost/leaf + python3 scripts/download_nlohmann_json.py - name: Run tests shell: cmd diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 2744801..0622f37 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -42,7 +42,7 @@ jobs: run: | REF=$(git show-ref $GITHUB_REF --hash) cd ../boost-root/libs/leaf - python gen/generate_single_header.py -i include/boost/leaf/detail/all.hpp -p include -o doc/html/leaf.hpp boost/leaf --hash "$REF" + python scripts/generate_single_header.py -i include/boost/leaf/detail/all.hpp -p include -o doc/html/leaf.hpp boost/leaf --hash "$REF" - name: Deploy to GitHub Pages uses: JamesIves/github-pages-deploy-action@4.0.0 diff --git a/.gitignore b/.gitignore index 84d0e3d..f4b99b7 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,5 @@ subprojects/*/ .vscode/settings.json .vscode/c_cpp_properties.json test/leaf.hpp +test/nlohmann/* doc/leaf.html diff --git a/.vscode/tasks.json b/.vscode/tasks.json index d366525..a096f76 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -48,7 +48,13 @@ { "label": "Generate leaf.hpp", "type": "shell", - "command": "cd ${workspaceRoot} && python gen/generate_single_header.py -i include/boost/leaf/detail/all.hpp -p ${workspaceRoot}/include -o ${workspaceRoot}/test/leaf.hpp boost/leaf", + "command": "cd ${workspaceRoot} && python scripts/generate_single_header.py -i include/boost/leaf/detail/all.hpp -p ${workspaceRoot}/include -o ${workspaceRoot}/test/leaf.hpp boost/leaf", + "problemMatcher": [] + }, + { + "label": "Download nlohmann/json.hpp", + "type": "shell", + "command": "cd ${workspaceRoot} && python scripts/download_nlohmann_json.py", "problemMatcher": [] }, { @@ -81,7 +87,8 @@ "label": "Run all unit tests (debug)", "type": "shell", "dependsOn": [ - "Generate leaf.hpp" + "Generate leaf.hpp", + "Download nlohmann/json.hpp" ], "command": "cd ${workspaceRoot}/_bld/debug && ninja && meson test && cd ${workspaceRoot}/_bld/debug && ninja && meson test", "problemMatcher": { @@ -106,7 +113,8 @@ "label": "Run all unit tests (release)", "type": "shell", "dependsOn": [ - "Generate leaf.hpp" + "Generate leaf.hpp", + "Download nlohmann/json.hpp" ], "command": "cd ${workspaceRoot}/_bld/release && ninja && meson test && cd ${workspaceRoot}/_bld/release_single_header && ninja && meson test", "problemMatcher": { @@ -131,7 +139,8 @@ "label": "Run all unit tests (b2, all configurations)", "type": "shell", "dependsOn": [ - "Generate leaf.hpp" + "Generate leaf.hpp", + "Download nlohmann/json.hpp" ], "command": "../../b2 --abbreviate-paths test link=shared,static variant=debug,release,leaf_debug_diag0,leaf_release_diag0,leaf_debug_single_header,leaf_release_single_header,leaf_debug_embedded,leaf_release_embedded exception-handling=off rtti=off cxxstd=11,14,1z,17 && ../../b2 --abbreviate-paths test link=shared,static variant=debug,release,leaf_debug_diag0,leaf_release_diag0,leaf_debug_single_header,leaf_release_single_header exception-handling=on,off cxxstd=11,14,1z,17", "problemMatcher": { diff --git a/doc/leaf.adoc b/doc/leaf.adoc index c01f463..7c42d46 100644 --- a/doc/leaf.adoc +++ b/doc/leaf.adoc @@ -1730,6 +1730,187 @@ TIP: The automatically generated diagnostic messages are developer-friendly, but ''' +[[tutorial-serialization]] +=== Serialization + +LEAF provides a serialization API that enables exporting error information into different formats, such as JSON. This is useful for structured logging, remote debugging, or integrating with monitoring systems. To serialize error information, use the `write_to` member function available on the following types: + +* <> +* <> +* <> +* <> + +==== Custom Writers + +To support different serialization formats, users can define custom writer types that derive from `leaf::serialization::writer`: + +[source,c++] +---- +#include + +class my_writer: public leaf::serialization::writer +{ +public: + + my_writer() noexcept: + writer(this) + { + } + + template + void write(E const & e) + { + .... // Serialize e + } +}; +---- + +LEAF does not define a specific API for user-defined writer types; for example, a writer could provide a `write` member function as shown above. + +To enable serialization of error objects to custom writer types, define a `serialize` function template in the `boost::leaf::serialization` namespace: + +[source,c++] +---- +namespace boost { namespace leaf { + +namespace serialization { + +template +void serialize(writer & w, E const & e) +{ + if( my_writer * mw = w.get() ) + mw->write(e); +} + +} + +} } +---- + +The `serialize` function receives a polymorphic `writer &` reference. Use the `get()` member function to check for a specific writer type. If the cast succeeds (returns non-null), call member functions as needed (e.g. `write`) to serialize the error object. To support multiple output formats, this user-defined `serialize` function should check for each writer type accordingly. + +==== JSON Serialization + +LEAF provides `json_writer`, a writer class template for JSON serialization: + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + +namespace serialization { + + template + class json_writer: public writer + { + public: + + explicit json_writer(Json &) noexcept; + + template + void write(E const &); + }; + +} + +} } +---- + +The `json_writer::write` member function serializes error objects by calling `to_json(Json &, E const &)`, found via ADL. These overloads use `operator[](char const *)` and `to_json` to serialize member values. + +LEAF provides `to_json` overloads for: + +* `error_id` +* `e_source_location` +* `e_errno` +* `std::error_code` +* `std::error_condition` +* `std::exception` +* `std::exception_ptr` +* any type with a `.value` member for which `to_json` can be found via ADL + +This interface is compatible with https://github.com/nlohmann/json[nlohmann/json]: + +[source,c++] +---- +#include +#include +#include "nlohmann/json.hpp" + +namespace leaf = boost::leaf; + +using nlohmann_writer = leaf::serialization::json_writer; + +namespace boost { namespace leaf { + +namespace serialization { + +template +void serialize(writer & w, E const & e) +{ + if( nlohmann_writer * nw = w.get() ) + nw->write(e); +} + +} + +} } + +struct e_api_response +{ + int status; + std::string message; + + template + friend void to_json(Json & j, e_api_response const & e) + { + j["status"] = e.status; + j["message"] = e.message; + } +}; + +struct e_request_url +{ + std::string value; +}; + +.... + +leaf::try_handle_all( + []() -> leaf::result + { + .... + return leaf::new_error(e_api_response{403, "Access denied"}, e_request_url{"/api/admin/settings"}); + }, + [](leaf::diagnostic_details const & dd) + { + nlohmann::json j; + nlohmann_writer w(j); + dd.write_to(w); + std::cout << j.dump(2) << std::endl; + } +); +---- + +.Output: +[source,json] +---- +{ + "e_api_response": { + "status": 403, + "message": "Access denied" + }, + "e_request_url": { + "value": "/api/admin/settings" + } +} +---- + +[.text-right] +<> | <> | <> + +''' + [[tutorial-std_error_code]] === Working with `std::error_code`, `std::error_condition` @@ -2345,6 +2526,9 @@ namespace boost { namespace leaf { void unload(); + template + void write_to( Writer & w ) const; + template friend std::ostream & operator<<( std::basic_ostream &, result const & ); }; @@ -2391,6 +2575,9 @@ namespace boost { namespace leaf { void unload(); + template + void write_to( Writer & w ) const; + template friend std::ostream & operator<<( std::basic_ostream &, result const &); }; @@ -2675,6 +2862,156 @@ namespace boost { namespace leaf { Reference: <> ==== +''' + +[[synopsis-serialization]] + +=== Serialization + +[[type_name.hpp]] +==== `type_name.hpp` + +==== +.#include +[source,c++] +---- +namespace boost { namespace leaf { + +namespace serialization +{ + + struct type_name + { + char const * name_not_zero_terminated_at_length; + std::size_t length; + std::size_t hash; + + friend bool operator==( type_name const &, type_name const & ) noexcept; + friend bool operator!=( type_name const &, type_name const & ) noexcept; + friend bool operator<( type_name const &, type_name const & ) noexcept; + + template + friend std::ostream & operator<<( std::basic_ostream &, type_name const & ); + +#if __cplusplus >= 201703L + friend std::string_view to_string_view( type_name const & ) noexcept; +#endif + +#if BOOST_LEAF_CFG_STD_STRING + friend std::string to_string( type_name const & ); +#endif + }; + + template + type_name get_type_name() noexcept; + +} // namespace serialization + +} } + +namespace std +{ + template <> struct hash; +} +---- + +[.text-right] +Reference: <> | <> +==== + +[[writer.hpp]] +==== `writer.hpp` + +==== +.#include +[source,c++] +---- +namespace boost { namespace leaf { + +namespace serialization +{ + + class writer + { + protected: + + template + explicit writer( Derived * ) noexcept; + + ~writer() noexcept; + + public: + + template + Derived * get() noexcept; + }; + +} // namespace serialization + +} } +---- + +[.text-right] +Reference: <> | <> +==== + +[[json_writer.hpp]] +==== `json_writer.hpp` + +==== +.#include +[source,c++] +---- +namespace boost { namespace leaf { + +namespace serialization +{ + template + void to_json( Json &, error_id ); + + template + void to_json( Json &, e_source_location const & ); + + template + void to_json( Json &, e_errno const & ); + + template + void to_json( Json &, std::error_code const & ); + + template + void to_json( Json &, std::error_condition const & ); + + template + void to_json( Json &, std::exception const & ); + + template + void to_json( Json &, std::exception_ptr const & ); + + // SFINAE: any type E with a .value member for which to_json can be bound via ADL. + template + auto to_json( Json &, E const & ) -> + decltype(to_json(std::declval(), std::declval().value), void()); + + template + class json_writer: public writer + { + public: + + explicit json_writer( Json & ) noexcept; + + template + void write( E const & ); + }; + +} + +} } +---- + +[.text-right] +Reference: <> | <> +==== + [[functions]] == Reference: Functions @@ -2859,6 +3196,27 @@ TIP: See also <> from the tutorial. ''' +[[get_type_name]] +=== `get_type_name` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + +namespace serialization +{ + template + type_name get_type_name() noexcept; +} + +} } +---- + +Returns a <> object representing the type `T`. The type name is extracted automatically in constant time. + +''' + [[make_context]] === `make_context` @@ -2969,6 +3327,27 @@ TIP: See <> from the Tutorial. ''' +[[serialize]] +=== `serialize` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + +namespace serialization +{ + template + void serialize( writer &, E const & ); +} + +} } +---- + +The `serialize` function template is a user-defined customization point. If defined, it is called by the serialization system to serialize error objects to a writer; see <>. + +''' + [[throw_exception]] === `throw_exception` @@ -3045,6 +3424,52 @@ NOTE: To automatically capture `pass:[__FILE__]`, `pass:[__LINE__]` and `pass:[_ ''' +[[to_json]] +=== `to_json` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + +namespace serialization +{ + template + void to_json( Json &, error_id ); + + template + void to_json( Json &, e_source_location const & ); + + template + void to_json( Json &, e_errno const & ); + + template + void to_json( Json &, std::error_code const & ); + + template + void to_json( Json &, std::error_condition const & ); + + template + void to_json( Json &, std::exception const & ); + + template + void to_json( Json &, std::exception_ptr const & ); + + // SFINAE: any type E with a .value member for which to_json can be bound via ADL. + template + auto to_json( Json &, E const & ) -> + decltype(to_json(std::declval(), std::declval().value), void()); +} + +} } +---- + +The `to_json` function is used by <> to serialize error objects to a JSON object. Users can define `to_json` overloads for their own error types, which will be found via ADL. These overloads should use `operator[]` to create JSON object fields and call `to_json` to serialize member values. + +NOTE: This interface is compatible with https://github.com/nlohmann/json[nlohmann/json]. + +''' + [[to_variant]] === `to_variant` @@ -3653,10 +4078,15 @@ For automatic deduction of `Ctx`, use <>. ---- namespace boost { namespace leaf { - class diagnostic_details: public error_info + class diagnostic_details: public diagnostic_info { //Constructors unspecified + public: + + template + void write_to( Writer & w ) const; + template friend std::ostream & operator<<( std::basic_ostream &, diagnostic_details const & ); }; @@ -3670,6 +4100,8 @@ The message printed by `operator<<` includes the message printed by `error_info` The additional information includes the types and the values of all such error objects (but see <>). +The `write_to` member function is used with the serialization system; see <>. + [NOTE] -- The behavior of `diagnostic_details` (and <>) is affected by the value of the macro `BOOST_LEAF_CFG_DIAGNOSTICS`: @@ -3694,6 +4126,11 @@ namespace boost { namespace leaf { { //Constructors unspecified + public: + + template + void write_to( Writer & w ) const; + template friend std::ostream & operator<<( std::basic_ostream &, diagnostic_info const & ); }; @@ -3707,6 +4144,8 @@ The message printed by `operator<<` includes the message printed by `error_info` The additional information is limited to the type name of the first such error object, as well as their total count. +The `write_to` member function is used with the serialization system; see <>. + [NOTE] -- The behavior of `diagnostic_info` (and <>) is affected by the value of the macro `BOOST_LEAF_CFG_DIAGNOSTICS`: @@ -4158,6 +4597,9 @@ namespace boost { namespace leaf { bool exception_caught() const noexcept; std::exception const * exception() const noexcept; + template + void write_to( Writer & w ) const; + template friend std::ostream & operator<<( std::basic_ostream &, error_info const & ); }; @@ -4177,6 +4619,41 @@ WARNING: It is illegal to call the `exception` member function unless `exception The `operator<<` overload prints diagnostic information about each error object currently stored in the <> local to the <>, <> or <> scope that invoked the handler, but only if it is associated with the <> returned by `error()`. +The `write_to` member function is used with the serialization system; see <>. + +''' + +[[json_writer]] +=== `json_writer` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + +namespace serialization +{ + template + class json_writer: public writer + { + public: + + explicit json_writer( Json & ) noexcept; + + template + void write( E const & ); + }; +} + +} } +---- + +The `json_writer` class template is used to serialize error objects to a JSON object. The `Json` template parameter is the type of the JSON object, for example https://github.com/nlohmann/json[nlohmann/json]. + +The constructor takes a reference to a JSON object, which will be populated with serialized error data. + +The `write` member function serializes an error object by calling `<>(json, e)`, found via ADL; see <>. + ''' [[result]] @@ -4250,6 +4727,9 @@ namespace boost { namespace leaf { void unload(); + template + void write_to( Writer & w ) const; + template friend std::ostream & operator<<( std::basic_ostream &, result const & ); }; @@ -4296,6 +4776,9 @@ namespace boost { namespace leaf { void unload(); + template + void write_to( Writer & w ) const; + template friend std::ostream & operator<<( std::basic_ostream &, result const &); }; @@ -4310,7 +4793,7 @@ namespace boost { namespace leaf { } } ---- [.text-right] -<> | <> | <> | <> | <> | <> | <> | <> | <> | <> +<> | <> | <> | <> | <> | <> | <> | <> | <> | <> | <> The `result` type can be returned by functions which produce a value of type `T` but may fail doing so. @@ -4433,6 +4916,42 @@ WARNING: The returned proxy object refers to `*this`; avoid holding on to it. ''' +[[result::has_error]] +==== `has_error` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + template + bool result::has_error() const noexcept; + +} } +---- + +Returns: :: If `*this` is in <>, returns `false`, otherwise returns `true`. + +''' + +[[result::has_value]] +==== `has_value` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + template + bool result::has_value() const noexcept; + +} } +---- + +Returns: :: If `*this` is in <>, returns `true`, otherwise returns `false`. + +''' + [[result::load]] ==== `load` @@ -4456,6 +4975,47 @@ Returns: :: `*this`. ''' +[[result::operator_bool]] +==== `operator bool` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + template + result::operator bool() const noexcept; + +} } +---- + +Returns: :: If `*this` is in <>, returns `true`, otherwise returns `false`. + +''' + +[[result::operator_deref]] +==== `operator*` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + + template + T const & result::operator*() const noexcept; + + template + T & result::operator*() noexcept; + +} } +---- + +Requires: :: `*this` must be in <>. + +Returns :: a reference to the stored value. + +''' + [[result::operator_eq]] ==== `operator=` @@ -4478,8 +5038,8 @@ Effects: :: Destroys `*this`, then re-initializes it as if using the appropriate ''' -[[result::has_value]] -==== `has_value` +[[result::operator_ptr]] +==== `operatorpass:[->]` .#include [source,c++] @@ -4487,48 +5047,15 @@ Effects: :: Destroys `*this`, then re-initializes it as if using the appropriate namespace boost { namespace leaf { template - bool result::has_value() const noexcept; - -} } ----- - -Returns: :: If `*this` is in <>, returns `true`, otherwise returns `false`. - -''' - -[[result::has_error]] -==== `has_error` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { + T const * result::operator->() const noexcept; template - bool result::has_error() const noexcept; + T * result::operator->() noexcept; } } ---- -Returns: :: If `*this` is in <>, returns `false`, otherwise returns `true`. - -''' - -[[result::operator_bool]] -==== `operator bool` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - result::operator bool() const noexcept; - -} } ----- - -Returns: :: If `*this` is in <>, returns `true`, otherwise returns `false`. +Returns :: If `*this` is in <>, returns a pointer to the stored value; otherwise returns `nullptr`. ''' @@ -4570,13 +5097,8 @@ A member type of `result`, defined as a synonym for `T`. ''' -[[result::bad_result]] -Effects: :: If `*this` is in <>, returns a reference to the stored value, otherwise throws `bad_result`. - -''' - -[[result::operator_ptr]] -==== `operatorpass:[->]` +[[result::write_to]] +==== `write_to` .#include [source,c++] @@ -4584,38 +5106,17 @@ Effects: :: If `*this` is in <>, returns a reference to the namespace boost { namespace leaf { template - T const * result::operator->() const noexcept; - - template - T * result::operator->() noexcept; + template + void result::write_to( Writer & w ) const; } } ---- -Returns :: If `*this` is in <>, returns a pointer to the stored value; otherwise returns 0. +The `write_to` member function is used with the serialization system; see <>. -''' +If the result is in <>, outputs the value. If it is in <>, outputs the <>. If the result holds <>, outputs them as well. -[[result::operator_deref]] -==== `operator*` - -.#include -[source,c++] ----- -namespace boost { namespace leaf { - - template - T const & result::operator*() const noexcept; - - template - T & result::operator*() noexcept; - -} } ----- - -Requires: :: `*this` must be in <>. - -Returns :: a reference to the stored value. +NOTE: Result objects carry error objects only when in <>. Otherwise, to output error objects, use `write_to` on <> in an error handling scope. ''' @@ -4654,6 +5155,106 @@ namespace boost { namespace leaf { } } ---- +''' + +[[type_name]] +=== `type_name` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + +namespace serialization +{ + + struct type_name + { + char const * name_not_zero_terminated_at_length; + std::size_t length; + std::size_t hash; + + friend bool operator==( type_name const &, type_name const & ) noexcept; + friend bool operator!=( type_name const &, type_name const & ) noexcept; + friend bool operator<( type_name const &, type_name const & ) noexcept; + + template + friend std::ostream & operator<<( std::basic_ostream &, type_name const & ); + +#if __cplusplus >= 201703L + friend std::string_view to_string_view( type_name const & ) noexcept; +#endif + +#if BOOST_LEAF_CFG_STD_STRING + friend std::string to_string( type_name const & ); +#endif + }; + +} // namespace serialization + +} } + +namespace std +{ + template <> struct hash; +} +---- + +The `type_name` struct represents a type name, extracted automatically in constant time. It contains a pointer to the type name string, its length, and a precomputed hash for efficient comparison. + +Two `type_name` objects compare equal if they represent the same type. The `operator<` provides alphabetical ordering. A `std::hash` specialization is provided for use with `std::unordered_map`. See <>. + +The type names are not mangled. + +.Example: +[source,c++] +---- +namespace app +{ + struct my_error { }; +} + +using leaf::serialization::get_type_name; + +assert(to_string_view(get_type_name()) == "int"); +assert(to_string_view(get_type_name()) == "app::my_error"); +---- + +''' + +[[writer]] +=== `writer` + +.#include +[source,c++] +---- +namespace boost { namespace leaf { + +namespace serialization +{ + class writer + { + protected: + + template + explicit writer( Derived * ) noexcept; + + ~writer() noexcept; + + public: + + template + Derived * get() noexcept; + }; +} + +} } +---- + +The `writer` class is the base class for custom serialization writers. The constructor takes a pointer to the derived class, which is used by the `get` member function to recognize writer types at runtime. + +The `get` member function returns a pointer to the derived class if the type matches, or `nullptr` otherwise. This allows the <> function to dispatch to the correct writer type; see <>. + [[predicates]] == Reference: Predicates diff --git a/include/boost/leaf/common.hpp b/include/boost/leaf/common.hpp index 4eac22e..0706385 100644 --- a/include/boost/leaf/common.hpp +++ b/include/boost/leaf/common.hpp @@ -6,7 +6,6 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include -#include #include #include diff --git a/include/boost/leaf/config.hpp b/include/boost/leaf/config.hpp index 87a4e12..d02e795 100644 --- a/include/boost/leaf/config.hpp +++ b/include/boost/leaf/config.hpp @@ -161,7 +161,6 @@ # define BOOST_LEAF_NO_EXCEPTIONS # endif # endif - #endif // #ifndef BOOST_LEAF_NO_EXCEPTIONS //////////////////////////////////////// @@ -248,22 +247,34 @@ namespace boost { - [[noreturn]] void throw_exception( std::exception const & ); // user defined -} -namespace boost { namespace leaf { - -template -[[noreturn]] void throw_exception_( T && e ) -{ #ifdef BOOST_LEAF_NO_EXCEPTIONS - ::boost::throw_exception(std::move(e)); -#else - throw std::move(e); -#endif + +[[noreturn]] void throw_exception( std::exception const & ); // user defined + +namespace leaf +{ + template + [[noreturn]] void throw_exception_( T && e ) + { + ::boost::throw_exception(std::move(e)); + } } -} } +#else + +namespace leaf +{ + template + [[noreturn]] void throw_exception_( T && e ) + { + throw std::move(e); + } +} + +#endif + +} // namespace boost //////////////////////////////////////// diff --git a/include/boost/leaf/config/tls.hpp b/include/boost/leaf/config/tls.hpp index a495383..9370bdf 100644 --- a/include/boost/leaf/config/tls.hpp +++ b/include/boost/leaf/config/tls.hpp @@ -87,4 +87,4 @@ namespace tls # include #endif -#endif // #ifndef BOOST_LEAF_CONFIG_TLS_HPP_INCLUDED \ No newline at end of file +#endif // #ifndef BOOST_LEAF_CONFIG_TLS_HPP_INCLUDED diff --git a/include/boost/leaf/config/tls_win32.hpp b/include/boost/leaf/config/tls_win32.hpp index cfa69ea..cee6ed3 100644 --- a/include/boost/leaf/config/tls_win32.hpp +++ b/include/boost/leaf/config/tls_win32.hpp @@ -196,11 +196,11 @@ namespace detail public: BOOST_LEAF_ALWAYS_INLINE tls_slot_index(): - idx_(TlsAlloc()) + idx_(TlsAlloc()) { if (idx_ == TLS_OUT_OF_INDEXES) throw_exception_(win32_tls_error("TLS_OUT_OF_INDEXES")); - } + } BOOST_LEAF_ALWAYS_INLINE ~tls_slot_index() noexcept { diff --git a/include/boost/leaf/context.hpp b/include/boost/leaf/context.hpp index 63518f3..d14ee73 100644 --- a/include/boost/leaf/context.hpp +++ b/include/boost/leaf/context.hpp @@ -6,6 +6,7 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include +#include #include #if !defined(BOOST_LEAF_NO_THREADS) && !defined(NDEBUG) @@ -203,12 +204,11 @@ namespace detail tuple_for_each::unload(tup, err_id); } - template - static void print(std::basic_ostream & os, void const * tup, error_id to_print, char const * & prefix) + static void write_to(serialization::writer & w, void const * tup, error_id id) { BOOST_LEAF_ASSERT(tup != nullptr); - tuple_for_each::print(os, tup, to_print, prefix); - std::get(*static_cast(tup)).print(os, to_print, prefix); + tuple_for_each::write_to(w, tup, id); + std::get(*static_cast(tup)).write_to(w, id); } }; @@ -218,14 +218,13 @@ namespace detail BOOST_LEAF_CONSTEXPR static void activate( Tup & ) noexcept { } BOOST_LEAF_CONSTEXPR static void deactivate( Tup & ) noexcept { } BOOST_LEAF_CONSTEXPR static void unload( Tup &, int ) noexcept { } - template - BOOST_LEAF_CONSTEXPR static void print(std::basic_ostream &, void const *, error_id, char const * &) { } + BOOST_LEAF_CONSTEXPR static void write_to(serialization::writer &, void const *, error_id) { } }; - template - BOOST_LEAF_CONSTEXPR void print_tuple_contents(std::basic_ostream & os, void const * tup, error_id to_print, char const * & prefix) + template + BOOST_LEAF_CONSTEXPR void serialize_tuple_contents(serialization::writer & w, void const * tup, error_id id) { - tuple_for_each::value, Tup>::print(os, tup, to_print, prefix); + tuple_for_each::value, Tup>::write_to(w, tup, id); } } // namespace detail @@ -369,18 +368,18 @@ public: return is_active_; } - template - void print( std::basic_ostream & os ) const + void write_to( serialization::writer & w ) const { - char const * prefix = "Contents:"; - detail::print_tuple_contents(os, &tup_, error_id(), prefix); + detail::serialize_tuple_contents(w, &tup_, error_id()); } template friend std::ostream & operator<<( std::basic_ostream & os, context const & ctx ) { - ctx.print(os); - return os << '\n'; + serialization::diagnostics_writer w(os); + w.set_prefix("Contents:"); + ctx.write_to(w); + return os; } template diff --git a/include/boost/leaf/detail/all.hpp b/include/boost/leaf/detail/all.hpp index 62030d5..521a724 100644 --- a/include/boost/leaf/detail/all.hpp +++ b/include/boost/leaf/detail/all.hpp @@ -2,7 +2,6 @@ // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -#include #include #include #include @@ -12,4 +11,7 @@ #include #include #include +#include +#include +#include #include diff --git a/include/boost/leaf/detail/capture_list.hpp b/include/boost/leaf/detail/capture_list.hpp index 9b51ed6..1e57b18 100644 --- a/include/boost/leaf/detail/capture_list.hpp +++ b/include/boost/leaf/detail/capture_list.hpp @@ -6,14 +6,14 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include -#include #if BOOST_LEAF_CFG_CAPTURE -#include - namespace boost { namespace leaf { +class error_id; +namespace serialization { class writer; } + namespace detail { @@ -29,9 +29,7 @@ namespace detail friend class capture_list; virtual void unload( int err_id ) = 0; -#if BOOST_LEAF_CFG_DIAGNOSTICS - virtual void print(std::ostream &, error_id const & to_print, char const * & prefix) const = 0; -#endif + virtual void write_to(serialization::writer &, error_id const &) const = 0; protected: @@ -92,23 +90,16 @@ namespace detail } ); } - template - void print(std::basic_ostream & os, error_id const & to_print, char const * & prefix) const + void write_to(serialization::writer & w, error_id const & id) const { -#if BOOST_LEAF_CFG_DIAGNOSTICS if( first_ ) { for_each( - [&os, &to_print, &prefix]( node const & n ) + [&w, &id]( node const & n ) { - n.print(os, to_print, prefix); + n.write_to(w, id); } ); } -#else - (void) os; - (void) prefix; - (void) to_print; -#endif } }; // class capture_list diff --git a/include/boost/leaf/detail/demangle.hpp b/include/boost/leaf/detail/demangle.hpp index e6003fa..d63fbe1 100644 --- a/include/boost/leaf/detail/demangle.hpp +++ b/include/boost/leaf/detail/demangle.hpp @@ -1,7 +1,7 @@ #ifndef BOOST_LEAF_DETAIL_DEMANGLE_HPP_INCLUDED #define BOOST_LEAF_DETAIL_DEMANGLE_HPP_INCLUDED -// Copyright 2018-2025 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2026 Emil Dotchevski and Reverge Studios, Inc. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -17,8 +17,7 @@ #include #include #include - -#if BOOST_LEAF_CFG_DIAGNOSTICS +#include // __has_include is currently supported by GCC and Clang. However GCC 4.9 may have issues and // returns 1 for 'defined( __has_include )', while '__has_include' is actually not supported: @@ -41,8 +40,6 @@ # endif #endif -#endif // #if BOOST_LEAF_CFG_DIAGNOSTICS - namespace boost { namespace leaf { namespace detail @@ -111,28 +108,36 @@ namespace detail { return cpp11_suffix::check(str, suffix) ? S1 - S2 : 0; } + + //////////////////////////////////////// + + template + BOOST_LEAF_ALWAYS_INLINE std::size_t compute_hash(char const (&str)[S], std::size_t begin, std::size_t end) noexcept + { + std::size_t h = 2166136261u; + for( std::size_t i = begin; i != end; ++i ) + h = (h ^ static_cast(str[i])) * 16777619u; + return h; + } } // namespace detail namespace n { struct r { - char const * name; - int len; - r(char const * name, int len) noexcept: - name(name), - len(len) - { - } - template - friend std::ostream & operator<<(std::basic_ostream & os, r const & pn) - { - return os.write(pn.name, pn.len); - } + char const * name_not_zero_terminated_at_length; + std::size_t length; + std::size_t hash; }; +#ifdef _MSC_VER +# define BOOST_LEAF_CDECL __cdecl +#else +# define BOOST_LEAF_CDECL +#endif + template - BOOST_LEAF_ALWAYS_INLINE r p() + BOOST_LEAF_ALWAYS_INLINE r BOOST_LEAF_CDECL p() { // C++11 compile-time parsing of __PRETTY_FUNCTION__/__FUNCSIG__. The sizeof hacks are a // workaround for older GCC versions, where __PRETTY_FUNCTION__ is not constexpr, which triggers @@ -146,75 +151,56 @@ namespace n #define BOOST_LEAF_P(P) (sizeof(char[1 + detail::check_prefix(BOOST_LEAF_PRETTY_FUNCTION, P)]) - 1) // clang style: - int const p01 = BOOST_LEAF_P("r boost::leaf::n::p() [T = "); - int const p02 = BOOST_LEAF_P("r __cdecl boost::leaf::n::p(void) [T = "); - int const p03 = BOOST_LEAF_P("r __stdcall boost::leaf::n::p(void) [T = "); - int const p04 = BOOST_LEAF_P("r __fastcall boost::leaf::n::p(void) [T = "); + std::size_t const p01 = BOOST_LEAF_P("r boost::leaf::n::p() [T = "); + std::size_t const p02 = BOOST_LEAF_P("r __cdecl boost::leaf::n::p(void) [T = "); // old clang style: - int const p05 = BOOST_LEAF_P("boost::leaf::n::r boost::leaf::n::p() [T = "); - int const p06 = BOOST_LEAF_P("boost::leaf::n::r __cdecl boost::leaf::n::p(void) [T = "); - int const p07 = BOOST_LEAF_P("boost::leaf::n::r __stdcall boost::leaf::n::p(void) [T = "); - int const p08 = BOOST_LEAF_P("boost::leaf::n::r __fastcall boost::leaf::n::p(void) [T = "); + std::size_t const p03 = BOOST_LEAF_P("boost::leaf::n::r boost::leaf::n::p() [T = "); + std::size_t const p04 = BOOST_LEAF_P("boost::leaf::n::r __cdecl boost::leaf::n::p(void) [T = "); // gcc style: - int const p09 = BOOST_LEAF_P("boost::leaf::n::r boost::leaf::n::p() [with T = "); - int const p10 = BOOST_LEAF_P("boost::leaf::n::r __cdecl boost::leaf::n::p() [with T = "); - int const p11 = BOOST_LEAF_P("boost::leaf::n::r __stdcall boost::leaf::n::p() [with T = "); - int const p12 = BOOST_LEAF_P("boost::leaf::n::r __fastcall boost::leaf::n::p() [with T = "); + std::size_t const p05 = BOOST_LEAF_P("boost::leaf::n::r boost::leaf::n::p() [with T = "); + std::size_t const p06 = BOOST_LEAF_P("boost::leaf::n::r __cdecl boost::leaf::n::p() [with T = "); // msvc style, struct: - int const p13 = BOOST_LEAF_P("struct boost::leaf::n::r __cdecl boost::leaf::n::p(void)"); + std::size_t const s02 = BOOST_LEAF_S(">(void)"); #undef BOOST_LEAF_S char static_assert_unrecognized_pretty_function_format_please_file_github_issue[sizeof( char[ - (s01 && (1 == (!!p01 + !!p02 + !!p03 + !!p04 + !!p05 + !!p06 + !!p07 + !!p08 + !!p09 + !!p10 + !!p11 + !!p12))) + (s01 && (1 == (!!p01 + !!p02 + !!p03 + !!p04 + !!p05 + !!p06))) || - (s02 && (1 == (!!p13 + !!p14 + !!p15 + !!p16 + !!p17 + !!p18 + !!p19 + !!p20 + !!p21))) + (s02 && (1 == (!!p07 + !!p08 + !!p09))) || - (s02 && (1 == (!!p22 + !!p23 + !!p24))) + (s02 && !!p10) ] ) * 2 - 1]; (void) static_assert_unrecognized_pretty_function_format_please_file_github_issue; - if( int const p = sizeof(char[1 + !!s01 * (p01 + p02 + p03 + p04 + p05 + p06 + p07 + p08 + p09 + p10 + p11 + p12)]) - 1 ) - return { BOOST_LEAF_PRETTY_FUNCTION + p, s01 - p }; + if( std::size_t const p = sizeof(char[1 + !!s01 * (p01 + p02 + p03 + p04 + p05 + p06)]) - 1 ) + return { BOOST_LEAF_PRETTY_FUNCTION + p, s01 - p, detail::compute_hash(BOOST_LEAF_PRETTY_FUNCTION, p, s01) }; - if( int const p = sizeof(char[1 + !!s02 * (p13 + p14 + p15 + p16 + p17 + p18 + p19 + p20 + p21)]) - 1 ) - return { BOOST_LEAF_PRETTY_FUNCTION + p, s02 - p }; + if( std::size_t const p = sizeof(char[1 + !!s02 * (p07 + p08 + p09)]) - 1 ) + return { BOOST_LEAF_PRETTY_FUNCTION + p, s02 - p, detail::compute_hash(BOOST_LEAF_PRETTY_FUNCTION, p, s02) }; - int const p = sizeof(char[1 + !!s02 * (p22 + p23 + p24)]) - 1; // p is not zero, we've static asserted the hell out of it - return { BOOST_LEAF_PRETTY_FUNCTION + p, s02 - p }; + std::size_t const p = sizeof(char[1 + !!s02 * p10]) - 1; + return { BOOST_LEAF_PRETTY_FUNCTION + p, s02 - p, detail::compute_hash(BOOST_LEAF_PRETTY_FUNCTION, p, s02) }; } + +#undef BOOST_LEAF_CDECL + } // namespace n -using parsed = n::r; - -template -parsed parse() -{ - return n::p(); -} - } } // namespace boost::leaf //////////////////////////////////////// @@ -223,29 +209,41 @@ namespace boost { namespace leaf { namespace detail { - template - std::ostream & demangle_and_print(std::basic_ostream & os, char const * mangled_name) + class demangler { - BOOST_LEAF_ASSERT(mangled_name); -#if defined(BOOST_LEAF_CFG_DIAGNOSTICS) && defined(BOOST_LEAF_HAS_CXXABI_H) - struct raii + char const * mangled_name_; +#ifdef BOOST_LEAF_HAS_CXXABI_H + char * demangled_name_ = nullptr; +#endif + + public: + + explicit demangler(char const * mangled_name) noexcept: + mangled_name_(mangled_name) { - char * demangled_name; - raii(char const * mangled_name) noexcept - { - int status = 0; - demangled_name = abi::__cxa_demangle(mangled_name, nullptr, nullptr, &status); - } - ~raii() noexcept - { - std::free(demangled_name); - } - } d(mangled_name); - if( d.demangled_name ) - return os << d.demangled_name; -#endif // #if defined(BOOST_LEAF_CFG_DIAGNOSTICS) && defined(BOOST_LEAF_HAS_CXXABI_H) - return os << mangled_name; - } + BOOST_LEAF_ASSERT(mangled_name_); +#ifdef BOOST_LEAF_HAS_CXXABI_H + int status = 0; + demangled_name_ = abi::__cxa_demangle(mangled_name_, nullptr, nullptr, &status); +#endif + } + + ~demangler() noexcept + { +#ifdef BOOST_LEAF_HAS_CXXABI_H + std::free(demangled_name_); +#endif + } + + char const * get() const noexcept + { +#ifdef BOOST_LEAF_HAS_CXXABI_H + if( demangled_name_ ) + return demangled_name_; +#endif + return mangled_name_; + } + }; } // namespace detail } } // namespace boost::leaf diff --git a/include/boost/leaf/detail/exception_base.hpp b/include/boost/leaf/detail/exception_base.hpp new file mode 100644 index 0000000..34da460 --- /dev/null +++ b/include/boost/leaf/detail/exception_base.hpp @@ -0,0 +1,30 @@ +#ifndef BOOST_LEAF_DETAIL_EXCEPTION_BASE_HPP_INCLUDED +#define BOOST_LEAF_DETAIL_EXCEPTION_BASE_HPP_INCLUDED + +// Copyright 2018-2026 Emil Dotchevski and Reverge Studios, Inc. +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +namespace boost { namespace leaf { + +class error_id; + +namespace detail +{ + class exception_base + { + public: + virtual error_id get_error_id() const noexcept = 0; + virtual serialization::type_name get_type_name() const = 0; + protected: + exception_base() noexcept { } + ~exception_base() noexcept { } + }; +} + +} } + +#endif // #ifndef BOOST_LEAF_DETAIL_EXCEPTION_BASE_HPP_INCLUDED diff --git a/include/boost/leaf/detail/function_traits.hpp b/include/boost/leaf/detail/function_traits.hpp index e8ccb11..522e188 100644 --- a/include/boost/leaf/detail/function_traits.hpp +++ b/include/boost/leaf/detail/function_traits.hpp @@ -5,6 +5,7 @@ // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +#include #include #include diff --git a/include/boost/leaf/detail/print.hpp b/include/boost/leaf/detail/print.hpp deleted file mode 100644 index 4299de6..0000000 --- a/include/boost/leaf/detail/print.hpp +++ /dev/null @@ -1,160 +0,0 @@ -#ifndef BOOST_LEAF_DETAIL_PRINT_HPP_INCLUDED -#define BOOST_LEAF_DETAIL_PRINT_HPP_INCLUDED - -// Copyright 2018-2025 Emil Dotchevski and Reverge Studios, Inc. -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#include -#include - -#include -#include - -namespace boost { namespace leaf { - -template -struct show_in_diagnostics: std::true_type -{ -}; - -namespace detail -{ - template - struct is_printable: std::false_type - { - }; - - template - struct is_printable()<(), void())>: show_in_diagnostics - { - }; - - //////////////////////////////////////// - - template - struct has_printable_member_value: std::false_type - { - }; - - template - struct has_printable_member_value()<().value, void())>: show_in_diagnostics - { - }; - - //////////////////////////////////////// - - template - void print_name(std::basic_ostream & os, char const * & prefix, char const * delimiter) - { - static_assert(show_in_diagnostics::value, "show_in_diagnostics violation"); - BOOST_LEAF_ASSERT(delimiter); - char const * p = prefix; - prefix = nullptr; - os << (p ? p : delimiter) << parse(); - } - - template - bool print_impl(std::basic_ostream & os, char const * & prefix, char const * delimiter, char const * mid, PrintableInfo const & x) - { - print_name(os, prefix, delimiter); - if( mid ) - os << mid << x; - return true; - } - - template - bool print_impl(std::basic_ostream & os, char const * & prefix, char const * delimiter, char const * mid, PrintableInfo const * x) - { - print_name(os, prefix, delimiter); - if( mid ) - { - os << mid; - if( x ) - os << x; - else - os << ""; - } - return true; - } - - //////////////////////////////////////// - - template < - class Wrapper, - bool ShowInDiagnostics = show_in_diagnostics::value, - bool WrapperPrintable = is_printable::value, - bool ValuePrintable = has_printable_member_value::value, - bool IsException = std::is_base_of::value, - bool IsEnum = std::is_enum::value> - struct diagnostic; - - template - struct diagnostic - { - template - static bool print(std::basic_ostream &, char const * &, char const *, Wrapper const & x) noexcept - { - return false; - } - }; - - template - struct diagnostic - { - template - static bool print(std::basic_ostream & os, char const * & prefix, char const * delimiter, Wrapper const & x) - { - return print_impl(os, prefix, delimiter, ": ", x); - } - }; - - template - struct diagnostic - { - template - static bool print(std::basic_ostream & os, char const * & prefix, char const * delimiter, Wrapper const & x) - { - return print_impl(os, prefix, delimiter, ": ", x.value); - } - }; - - template - struct diagnostic - { - template - static bool print(std::basic_ostream & os, char const * & prefix, char const * delimiter, Exception const & ex) - { - if( print_impl(os, prefix, delimiter, ": \"", static_cast(ex).what()) ) - { - os << '"'; - return true; - } - return false; - } - }; - - template - struct diagnostic - { - template - static bool print(std::basic_ostream & os, char const * & prefix, char const * delimiter, Wrapper const &) - { - return print_impl(os, prefix, delimiter, nullptr, 0); - } - }; - - template - struct diagnostic - { - template - static bool print(std::basic_ostream & os, char const * & prefix, char const * delimiter, Enum const & enum_) - { - return print_impl(os, prefix, delimiter, ": ", static_cast::type>(enum_)); - } - }; -} // namespace detail - -} } // namespace boost::leaf - -#endif // #ifndef BOOST_LEAF_DETAIL_PRINT_HPP_INCLUDED diff --git a/include/boost/leaf/diagnostics.hpp b/include/boost/leaf/diagnostics.hpp index 28449d6..cc05237 100644 --- a/include/boost/leaf/diagnostics.hpp +++ b/include/boost/leaf/diagnostics.hpp @@ -11,12 +11,10 @@ namespace boost { namespace leaf { -#if BOOST_LEAF_CFG_DIAGNOSTICS - class diagnostic_info: public error_info { void const * tup_; - void (*print_tuple_contents_)(std::ostream &, void const * tup, error_id to_print, char const * & prefix); + void (*serialize_tuple_contents_)(serialization::writer &, void const *, error_id); protected: @@ -26,23 +24,35 @@ protected: BOOST_LEAF_CONSTEXPR diagnostic_info( error_info const & ei, Tup const & tup ) noexcept: error_info(ei), tup_(&tup), - print_tuple_contents_(&detail::print_tuple_contents) + serialize_tuple_contents_(&detail::serialize_tuple_contents) { } - template - void print_diagnostic_info(std::basic_ostream & os) const + template + void write_to_(Writer & w) const { - print_error_info(os); - char const * prefix = exception() ? nullptr : "\nCaught:" BOOST_LEAF_CFG_DIAGNOSTICS_FIRST_DELIMITER; - print_tuple_contents_(os, tup_, error(), prefix); + serialize_tuple_contents_(w, tup_, error()); + } + +public: + + template + void write_to(Writer & w) const + { + error_info::write_to(w); + write_to_(w); } template friend std::ostream & operator<<( std::basic_ostream & os, diagnostic_info const & x ) { - x.print_diagnostic_info(os); - return os << '\n'; + serialization::diagnostics_writer w(os, x.error(), x.source_location(), x.exception()); +#if BOOST_LEAF_CFG_DIAGNOSTICS + x.write_to_(w); +#else + os << "\nboost::leaf::diagnostic_info N/A due to BOOST_LEAF_CFG_DIAGNOSTICS=0"; +#endif + return os; } }; // class diagnostic_info @@ -68,61 +78,8 @@ namespace detail }; } -#else // #if BOOST_LEAF_CFG_DIAGNOSTICS - -class diagnostic_info: public error_info -{ -protected: - - diagnostic_info( diagnostic_info const & ) noexcept = default; - - BOOST_LEAF_CONSTEXPR diagnostic_info( error_info const & ei ) noexcept: - error_info(ei) - { - } - - template - void print_diagnostic_info( std::basic_ostream & os ) const - { - print_error_info(os); - os << "\nboost::leaf::diagnostic_info N/A due to BOOST_LEAF_CFG_DIAGNOSTICS=0"; - } - - template - friend std::ostream & operator<<( std::basic_ostream & os, diagnostic_info const & x ) - { - x.print_diagnostic_info(os); - return os << "\n"; - } -}; // class diagnostic_info - -namespace detail -{ - struct diagnostic_info_: diagnostic_info - { - BOOST_LEAF_CONSTEXPR diagnostic_info_( error_info const & ei ) noexcept: - diagnostic_info(ei) - { - } - }; - - template <> - struct handler_argument_traits: handler_argument_always_available - { - template - BOOST_LEAF_CONSTEXPR static diagnostic_info_ get( Tup const &, error_info const & ei ) noexcept - { - return diagnostic_info_(ei); - } - }; -} - -#endif // #else (#if BOOST_LEAF_CFG_DIAGNOSTICS) - //////////////////////////////////////// -#if BOOST_LEAF_CFG_DIAGNOSTICS - #if BOOST_LEAF_CFG_CAPTURE class diagnostic_details: public diagnostic_info @@ -140,22 +97,34 @@ protected: { } - template - void print_diagnostic_details( std::basic_ostream & os) const + template + void write_to_(Writer & w) const { - print_diagnostic_info(os); if( da_ ) - { - char const * prefix = "\nDiagnostic details:" BOOST_LEAF_CFG_DIAGNOSTICS_FIRST_DELIMITER; - da_->print(os, error(), prefix); - } + da_->write_to(w, error()); + } + +public: + + template + void write_to(Writer & w) const + { + diagnostic_info::write_to(w); + write_to_(w); } template friend std::ostream & operator<<( std::basic_ostream & os, diagnostic_details const & x ) { - x.print_diagnostic_details(os); - return os << '\n'; + serialization::diagnostics_writer w(os, x.error(), x.source_location(), x.exception()); +#if BOOST_LEAF_CFG_DIAGNOSTICS + x.diagnostic_info::write_to_(w); + w.set_prefix("\nDiagnostic details:" BOOST_LEAF_CFG_DIAGNOSTICS_FIRST_DELIMITER); + x.write_to_(w); +#else + os << "\nboost::leaf::diagnostic_details N/A due to BOOST_LEAF_CFG_DIAGNOSTICS=0"; +#endif + return os; } }; // class diagnostic_details @@ -196,18 +165,25 @@ protected: { } - template - void print_diagnostic_details( std::basic_ostream & os ) const +public: + + template + void write_to(Writer & w) const { - print_diagnostic_info(os); - os << "\nboost::leaf::diagnostic_details N/A due to BOOST_LEAF_CFG_CAPTURE=0"; + diagnostic_info::write_to(w); } template friend std::ostream & operator<<( std::basic_ostream & os, diagnostic_details const & x ) { - x.print_diagnostic_details(os); - return os << "\n"; + serialization::diagnostics_writer w(os, x.error(), x.source_location(), x.exception()); +#if BOOST_LEAF_CFG_DIAGNOSTICS + x.diagnostic_info::write_to_(w); + os << "\nboost::leaf::diagnostic_details N/A due to BOOST_LEAF_CFG_CAPTURE=0"; +#else + os << "\nboost::leaf::diagnostic_details N/A due to BOOST_LEAF_CFG_DIAGNOSTICS=0"; +#endif + return os; } }; // class diagnostic_details @@ -235,57 +211,6 @@ namespace detail #endif // #else (#if BOOST_LEAF_CFG_CAPTURE) -#else // #if BOOST_LEAF_CFG_DIAGNOSTICS - -class diagnostic_details: public diagnostic_info -{ -protected: - - diagnostic_details( diagnostic_details const & ) noexcept = default; - - BOOST_LEAF_CONSTEXPR diagnostic_details( error_info const & ei ) noexcept: - diagnostic_info(ei) - { - } - - template - void print_diagnostic_details( std::basic_ostream & os ) const - { - print_error_info(os); - os << "\nboost::leaf::diagnostic_details N/A due to BOOST_LEAF_CFG_DIAGNOSTICS=0"; - } - - template - friend std::ostream & operator<<( std::basic_ostream & os, diagnostic_details const & x ) - { - x.print_diagnostic_details(os); - return os << "\n"; - } -}; - -namespace detail -{ - struct diagnostic_details_: diagnostic_details - { - BOOST_LEAF_CONSTEXPR diagnostic_details_( error_info const & ei ) noexcept: - diagnostic_details(ei) - { - } - }; - - template <> - struct handler_argument_traits: handler_argument_always_available - { - template - BOOST_LEAF_CONSTEXPR static diagnostic_details_ get( Tup const &, error_info const & ei ) noexcept - { - return diagnostic_details_(ei); - } - }; -} - -#endif // #else (#if BOOST_LEAF_CFG_DIAGNOSTICS) - using verbose_diagnostic_info = diagnostic_details; } } // namespace boost::leaf diff --git a/include/boost/leaf/error.hpp b/include/boost/leaf/error.hpp index 7e02d67..e5a15f7 100644 --- a/include/boost/leaf/error.hpp +++ b/include/boost/leaf/error.hpp @@ -9,10 +9,7 @@ #include #include #include - -#if BOOST_LEAF_CFG_DIAGNOSTICS -# include -#endif +#include #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR # include @@ -84,17 +81,15 @@ struct show_in_diagnostics: std::false_type namespace detail { - class exception_base + template + void serialize_(Writer & w, E const & e) { - public: - virtual error_id get_error_id() const noexcept = 0; -#if BOOST_LEAF_CFG_DIAGNOSTICS && !defined(BOOST_LEAF_NO_EXCEPTIONS) - virtual void print_type_name(std::ostream &) const = 0; -#endif - protected: - exception_base() noexcept { } - ~exception_base() noexcept { } - }; + using namespace serialization; + typename dependent_writer::type & wr = w; + serialize(wr, e); + if( diagnostics_writer * dw = w.template get() ) + dw->write(e); + } } //////////////////////////////////////// @@ -153,15 +148,14 @@ namespace detail void unload( int err_id ) noexcept(!BOOST_LEAF_CFG_CAPTURE); - template - void print(std::basic_ostream & os, ErrorID to_print, char const * & prefix) const + template + void write_to(serialization::writer & w, ErrorID id) const { if( int k = this->key() ) { - if( to_print && to_print.value() != k ) + if( id && id.value() != k ) return; - if( diagnostic::print(os, prefix, BOOST_LEAF_CFG_DIAGNOSTICS_DELIMITER, value(k)) && !to_print ) - os << '(' << k/4 << ')'; + serialize_(w, value(k)); } } @@ -223,12 +217,10 @@ namespace detail { impl::unload(err_id); } -#if BOOST_LEAF_CFG_DIAGNOSTICS - void print(std::ostream & os, error_id const & to_print, char const * & prefix) const override + void write_to(serialization::writer & w, error_id const & id) const override { - impl::print(os, to_print, prefix); + impl::write_to(w, id); } -#endif public: BOOST_LEAF_CONSTEXPR explicit capturing_slot_node( capture_list::node * * & last ): capturing_node(last) @@ -260,11 +252,9 @@ namespace detail { std::rethrow_exception(ex_); } -#if BOOST_LEAF_CFG_DIAGNOSTICS - void print(std::ostream &, error_id const &, char const * &) const override + void write_to(serialization::writer &, error_id const &) const override { } -#endif std::exception_ptr const ex_; public: capturing_exception_node( capture_list::node * * & last, std::exception_ptr && ex ) noexcept: @@ -362,7 +352,7 @@ namespace detail } using capture_list::unload; - using capture_list::print; + using capture_list::write_to; }; // class dynamic_allocator template @@ -437,12 +427,10 @@ namespace detail da_.unload(err_id); } -#if BOOST_LEAF_CFG_DIAGNOSTICS - template - void print(std::basic_ostream &, ErrorID, char const * &) const + template + void write_to(serialization::writer &, ErrorID) const { } -#endif }; // slot specialization for dynamic_allocator } // namespace detail diff --git a/include/boost/leaf/exception.hpp b/include/boost/leaf/exception.hpp index 36fb6fd..5e8e4fb 100644 --- a/include/boost/leaf/exception.hpp +++ b/include/boost/leaf/exception.hpp @@ -1,13 +1,13 @@ #ifndef BOOST_LEAF_EXCEPTION_HPP_INCLUDED #define BOOST_LEAF_EXCEPTION_HPP_INCLUDED -// Copyright 2018-2025 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2026 Emil Dotchevski and Reverge Studios, Inc. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include #include -#include +#include //////////////////////////////////////// @@ -57,12 +57,10 @@ namespace detail return *this; } -#if BOOST_LEAF_CFG_DIAGNOSTICS && !defined(BOOST_LEAF_NO_EXCEPTIONS) - void print_type_name(std::ostream & os) const override + serialization::type_name get_type_name() const override { - detail::demangle_and_print(os, typeid(Ex).name()); + return serialization::get_type_name(); } -#endif public: diff --git a/include/boost/leaf/handle_errors.hpp b/include/boost/leaf/handle_errors.hpp index 6552e12..e3aa39b 100644 --- a/include/boost/leaf/handle_errors.hpp +++ b/include/boost/leaf/handle_errors.hpp @@ -7,7 +7,7 @@ #include #include -#include +#include namespace boost { namespace leaf { @@ -76,32 +76,21 @@ public: return loc_; } - template - void print_error_info(std::basic_ostream & os) const + template + void write_to(Writer & w) const { - os << "Error with serial #" << err_id_; - if( loc_ ) - os << " reported at " << *loc_; + detail::serialize_(w, err_id_); #ifndef BOOST_LEAF_NO_EXCEPTIONS if( ex_ ) - { - os << "\nCaught:" BOOST_LEAF_CFG_DIAGNOSTICS_FIRST_DELIMITER; -#if BOOST_LEAF_CFG_DIAGNOSTICS - if( auto eb = dynamic_cast(ex_) ) - eb->print_type_name(os); - else + detail::serialize_(w, *ex_); #endif - detail::demangle_and_print(os, typeid(*ex_).name()); - os << ": \"" << ex_->what() << '"'; - } -#endif // #ifndef BOOST_LEAF_NO_EXCEPTIONS } template friend std::ostream & operator<<(std::basic_ostream & os, error_info const & x) { - x.print_error_info(os); - return os << '\n'; + serialization::diagnostics_writer w(os, x.error(), x.source_location(), x.exception()); + return os; } }; // class error_info @@ -232,7 +221,7 @@ namespace detail { return nullptr; } - + template BOOST_LEAF_CONSTEXPR static E * peek( SlotsTuple &, error_id const & ) noexcept { diff --git a/include/boost/leaf/result.hpp b/include/boost/leaf/result.hpp index 20d7cac..ccb8113 100644 --- a/include/boost/leaf/result.hpp +++ b/include/boost/leaf/result.hpp @@ -6,9 +6,9 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include -#include -#include #include +#include +#include #include @@ -27,40 +27,6 @@ class bad_result: //////////////////////////////////////// -namespace detail -{ - template ::value> - struct result_value_printer; - - template - struct result_value_printer - { - template - static void print( std::basic_ostream & s, T const & x ) - { - (void) (s << x); - } - }; - - template - struct result_value_printer - { - template - static void print( std::basic_ostream & s, T const & ) - { - (void) (s << "{not printable}"); - } - }; - - template - void print_result_value( std::basic_ostream & s, T const & x ) - { - result_value_printer::print(s, x); - } -} // namespace detail - -//////////////////////////////////////// - namespace detail { template @@ -330,25 +296,38 @@ protected: what_ = move_from(std::move(x)); } - template - void print_error_result(std::basic_ostream & os) const + template + error_id write_error_to(Writer & w) const { result_discriminant const what = what_; BOOST_LEAF_ASSERT(what.kind() != result_discriminant::val); error_id const err_id = what.get_error_id(); - os << "Error serial #" << err_id; - if( what.kind() == result_discriminant::err_id_capture_list ) + detail::serialize_(w, err_id); + return err_id; + } + + template + void write_capture_to(Writer & w, error_id err_id) const + { + if( what_.kind() == result_discriminant::err_id_capture_list ) { #if BOOST_LEAF_CFG_CAPTURE - char const * prefix = "\nCaptured:"; - cap_.print(os, err_id, prefix); - os << "\n"; + cap_.write_to(w, err_id); #else BOOST_LEAF_ASSERT(0); // Possible ODR violation. #endif } } + template + void print_error( Writer & w ) const + { + error_id err_id = write_error_to(w); + w.set_prefix(", captured -> "); + w.set_delimiter(", "); + write_capture_to(w, err_id); + } + public: using value_type = T; @@ -594,13 +573,30 @@ public: #endif } + template + void write_to(Writer & w) const + { + if( what_.kind() == result_discriminant::val ) + detail::serialize_(w, value()); + else + write_capture_to(w, write_error_to(w)); + } + template friend std::ostream & operator<<( std::basic_ostream & os, result const & r ) { - if( r.what_.kind() == result_discriminant::val ) - detail::print_result_value(os, r.value()); + serialization::diagnostics_writer w(os); + w.set_prefix(": "); + if( r ) + { + os << "Success"; + detail::serialize_(w, r.value()); + } else - r.print_error_result(os); + { + os << "Failure"; + r.print_error(w); + } return os; } }; // template result @@ -695,13 +691,25 @@ public: BOOST_LEAF_ASSERT(has_value()); } + template + void write_to(Writer & w) const + { + if( !*this ) + write_error_to(w); + } + template friend std::ostream & operator<<( std::basic_ostream & os, result const & r ) { if( r ) - os << "No error"; + os << "Success"; else - r.print_error_result(os); + { + serialization::diagnostics_writer w(os); + w.set_prefix(": "); + os << "Failure"; + r.print_error(w); + } return os; } diff --git a/include/boost/leaf/serialization/diagnostics_writer.hpp b/include/boost/leaf/serialization/diagnostics_writer.hpp new file mode 100644 index 0000000..9763d69 --- /dev/null +++ b/include/boost/leaf/serialization/diagnostics_writer.hpp @@ -0,0 +1,252 @@ +#ifndef BOOST_LEAF_SERIALIZATION_DIAGNOSTICS_WRITER_HPP_INCLUDED +#define BOOST_LEAF_SERIALIZATION_DIAGNOSTICS_WRITER_HPP_INCLUDED + +// Copyright 2018-2026 Emil Dotchevski and Reverge Studios, Inc. +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +#include + +#if BOOST_LEAF_CFG_DIAGNOSTICS +# include +#else +# include +#endif + +#ifndef BOOST_LEAF_NO_EXCEPTIONS +# include +#endif + +namespace boost { namespace leaf { + +template +struct show_in_diagnostics: std::integral_constant +{ +}; + +namespace serialization +{ + template + struct is_printable: std::false_type + { + }; + + template + struct is_printable()<(), void())>: std::true_type + { + }; + + template + struct has_printable_member_value: std::false_type + { + }; + + template + struct has_printable_member_value()<().value, void())>: std::true_type + { + }; + + template + struct has_member_value: std::false_type + { + }; + + template + struct has_member_value().value)>: std::true_type + { + }; + + //////////////////////////////////////// + + class diagnostics_writer: public writer + { + diagnostics_writer(diagnostics_writer const &) = delete; + diagnostics_writer & operator=(diagnostics_writer const &) = delete; + + std::ostream & os_; + char const * prefix_; + char const * delimiter_; + void (* const print_suffix_)(std::ostream &); + + template + static void print_name(std::basic_ostream & os, char const * & prefix, char const * delimiter) + { + static_assert(show_in_diagnostics::value, "show_in_diagnostics violation"); + BOOST_LEAF_ASSERT(delimiter); + char const * p = prefix; + prefix = nullptr; + os << (p ? p : delimiter) << get_type_name(); + } + + template + static bool print_impl(std::basic_ostream & os, char const * & prefix, char const * delimiter, char const * mid, PrintableInfo const & x) + { + print_name(os, prefix, delimiter); + if( mid ) + os << mid << x; + return true; + } + + template + static bool print_impl(std::basic_ostream & os, char const * & prefix, char const * delimiter, char const * mid, PrintableInfo const * x) + { + print_name(os, prefix, delimiter); + if( mid ) + { + os << mid; + if( x ) + os << x; + else + os << ""; + } + return true; + } + + template < + class Wrapper, + bool ShowInDiagnostics = show_in_diagnostics::value, + bool WrapperPrintable = is_printable::value, + bool ValuePrintable = has_printable_member_value::value, + bool IsException = std::is_base_of::value, + bool IsEnum = std::is_enum::value> + struct diagnostic; + + public: + + template + explicit diagnostics_writer(std::basic_ostream & os) noexcept: + writer(this), + os_(os), + prefix_(BOOST_LEAF_CFG_DIAGNOSTICS_FIRST_DELIMITER), + delimiter_(BOOST_LEAF_CFG_DIAGNOSTICS_DELIMITER), + print_suffix_([](std::basic_ostream &) { }) + { + } + + template + diagnostics_writer(std::basic_ostream & os, error_id const & id, e_source_location const * loc, std::exception const * ex) noexcept: + writer(this), + os_(os), + prefix_(BOOST_LEAF_CFG_DIAGNOSTICS_FIRST_DELIMITER), + delimiter_(BOOST_LEAF_CFG_DIAGNOSTICS_DELIMITER), + print_suffix_([](std::basic_ostream & os) { os << '\n'; }) + { + os << "Error with serial #" << id; + if( loc ) + os << " reported at " << *loc; +#ifndef BOOST_LEAF_NO_EXCEPTIONS + if( ex ) + { + os << "\nCaught:" BOOST_LEAF_CFG_DIAGNOSTICS_FIRST_DELIMITER; + if( auto eb = dynamic_cast(ex) ) + os << eb->get_type_name(); + else + os << detail::demangler(typeid(*ex).name()).get(); + os << ": \"" << ex->what() << '"'; + } + else +#endif + { + prefix_ = "\nCaught:" BOOST_LEAF_CFG_DIAGNOSTICS_FIRST_DELIMITER; + } + (void) ex; + } + + ~diagnostics_writer() noexcept + { + print_suffix_(os_); + } + + void set_prefix(char const * prefix) noexcept + { + prefix_ = prefix; + } + + void set_delimiter(char const * delimiter) noexcept + { + delimiter_ = delimiter; + } + + template + void write(E const & e) + { + diagnostic::print(os_, prefix_, delimiter_, e); + } + }; // class diagnostics_writer + + //////////////////////////////////////// + + template + struct diagnostics_writer::diagnostic + { + template + static bool print(std::basic_ostream &, char const * &, char const *, Wrapper const &) noexcept + { + return false; + } + }; + + template + struct diagnostics_writer::diagnostic + { + template + static bool print(std::basic_ostream & os, char const * & prefix, char const * delimiter, Wrapper const & x) + { + return print_impl(os, prefix, delimiter, ": ", x); + } + }; + + template + struct diagnostics_writer::diagnostic + { + template + static bool print(std::basic_ostream & os, char const * & prefix, char const * delimiter, Wrapper const & x) + { + return print_impl(os, prefix, delimiter, ": ", x.value); + } + }; + + template + struct diagnostics_writer::diagnostic + { + template + static bool print(std::basic_ostream & os, char const * & prefix, char const * delimiter, Exception const & ex) + { + if( print_impl(os, prefix, delimiter, ": \"", static_cast(ex).what()) ) + { + os << '"'; + return true; + } + return false; + } + }; + + template + struct diagnostics_writer::diagnostic + { + template + static bool print(std::basic_ostream & os, char const * & prefix, char const * delimiter, Wrapper const &) + { + return print_impl(os, prefix, delimiter, nullptr, 0); + } + }; + + template + struct diagnostics_writer::diagnostic + { + template + static bool print(std::basic_ostream & os, char const * & prefix, char const * delimiter, Enum const & enum_) + { + return print_impl(os, prefix, delimiter, ": ", static_cast::type>(enum_)); + } + }; + +} // namespace serialization + +} } // namespace boost::leaf + +#endif // #ifndef BOOST_LEAF_SERIALIZATION_DIAGNOSTICS_WRITER_HPP_INCLUDED diff --git a/include/boost/leaf/serialization/json_writer.hpp b/include/boost/leaf/serialization/json_writer.hpp new file mode 100644 index 0000000..bdc3765 --- /dev/null +++ b/include/boost/leaf/serialization/json_writer.hpp @@ -0,0 +1,160 @@ +#ifndef BOOST_LEAF_SERIALIZATION_JSON_WRITER_HPP_INCLUDED +#define BOOST_LEAF_SERIALIZATION_JSON_WRITER_HPP_INCLUDED + +// Copyright 2018-2026 Emil Dotchevski and Reverge Studios, Inc. +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include + +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR +# include +#endif + +#ifndef BOOST_LEAF_NO_EXCEPTIONS +# include +#endif + +namespace boost { namespace leaf { + +namespace serialization +{ + template + void to_json(Json & j, error_id x) + { + to_json(j, x.value() / 4); + } + + template + void to_json(Json & j, e_source_location const & x) + { + to_json(j["file"], x.file); + to_json(j["line"], x.line); + to_json(j["function"], x.function); + } + + template + void to_json(Json & j, e_errno const & e) + { + to_json(j["value"], e.value); + to_json(j["message"], std::strerror(e.value)); + } + +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR + template + void to_json(Json & j, std::error_code const & ec) + { + to_json(j["category"], ec.category().name()); + to_json(j["value"], ec.value()); + to_json(j["message"], ec.message()); + } + + template + void to_json(Json & j, std::error_condition const & ec) + { + to_json(j["category"], ec.category().name()); + to_json(j["value"], ec.value()); + to_json(j["message"], ec.message()); + } +#endif + + template + void to_json(Json & j, detail::exception_base const & eb) + { + char zstr[1024]; + to_json(j["type"], to_zstr(zstr, eb.get_type_name())); + char const * what = "N/A"; +#ifndef BOOST_LEAF_NO_EXCEPTIONS + if( std::exception const * ex = dynamic_cast(&eb) ) + what = ex->what(); +#endif + to_json(j["what"], what ? what : "<>"); + } + +#ifndef BOOST_LEAF_NO_EXCEPTIONS + template + void to_json(Json & j, std::exception const & ex) + { + if( detail::exception_base const * eb = dynamic_cast(&ex) ) + { + char zstr[1024]; + to_json(j["type"], to_zstr(zstr, eb->get_type_name())); + } + else + to_json(j["type"], detail::demangler(typeid(ex).name()).get()); + if( char const * w = ex.what() ) + to_json(j["what"], w); + else + to_json(j["what"], "<>"); + } +#endif + + template + void to_json(Json & j, std::exception_ptr const & ep) + { + if( ep ) + { +#ifndef BOOST_LEAF_NO_EXCEPTIONS + try + { + std::rethrow_exception(ep); + } + catch( detail::exception_base const & eb ) + { + to_json(j, eb); + return; + } + catch( std::exception const & ex ) + { + to_json(j, ex); + return; + } + catch( ... ) + { + } +#endif + to_json(j["type"], "<>"); + } + else + to_json(j["type"], "<>"); + to_json(j["what"], "N/A"); + } + + template + auto to_json(Json & j, E const & e) -> decltype(to_json(j, e.value), void()) + { + to_json(j["value"], e.value); + } + + //////////////////////////////////////// + + template + class json_writer: public writer + { + Json & j_; + + public: + + explicit json_writer(Json & j) noexcept: + writer(this), + j_(j) + { + } + + template + void write(E const & e) + { + char zstr[1024]; + to_json(j_[to_zstr(zstr, get_type_name())], e); + } + }; + +} // namespace serialization + +} } // namespace boost::leaf + +#endif // #ifndef BOOST_LEAF_SERIALIZATION_JSON_WRITER_HPP_INCLUDED diff --git a/include/boost/leaf/serialization/type_name.hpp b/include/boost/leaf/serialization/type_name.hpp new file mode 100644 index 0000000..f56a015 --- /dev/null +++ b/include/boost/leaf/serialization/type_name.hpp @@ -0,0 +1,102 @@ +#ifndef BOOST_LEAF_SERIALIZATION_TYPE_NAME_HPP_INCLUDED +#define BOOST_LEAF_SERIALIZATION_TYPE_NAME_HPP_INCLUDED + +// Copyright 2018-2026 Emil Dotchevski and Reverge Studios, Inc. +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include + +#if BOOST_LEAF_CFG_STD_STRING +# include +#endif + +#if __cplusplus >= 201703L +# include +#endif + +namespace boost { namespace leaf { + +namespace serialization +{ + +struct type_name +{ + char const * name_not_zero_terminated_at_length; + std::size_t length; + std::size_t hash; + + friend bool operator==(type_name const & a, type_name const & b) noexcept + { + BOOST_LEAF_ASSERT((a.hash == b.hash) == (a.length == b.length && std::memcmp(a.name_not_zero_terminated_at_length, b.name_not_zero_terminated_at_length, a.length) == 0)); + return a.hash == b.hash; + } + + friend bool operator!=(type_name const & a, type_name const & b) noexcept + { + return !(a == b); + } + + friend bool operator<(type_name const & a, type_name const & b) noexcept + { + if( int cmp = std::memcmp(a.name_not_zero_terminated_at_length, b.name_not_zero_terminated_at_length, a.length < b.length ? a.length : b.length) ) + return cmp < 0; + return a.length < b.length; + } + + template + friend std::ostream & operator<<(std::basic_ostream & os, type_name const & x) + { + return os.write(x.name_not_zero_terminated_at_length, x.length); + } + +#if __cplusplus >= 201703L + friend std::string_view to_string_view(type_name const & x) noexcept + { + return std::string_view(x.name_not_zero_terminated_at_length, x.length); + } +#endif + +#if BOOST_LEAF_CFG_STD_STRING + friend std::string to_string(type_name const & x) + { + return std::string(x.name_not_zero_terminated_at_length, x.length); + } +#endif + + template + friend char * to_zstr(char (&zstr)[S], type_name const & x) noexcept + { + std::size_t n = x.length < S - 1 ? x.length : S - 1; + std::memcpy(zstr, x.name_not_zero_terminated_at_length, n); + zstr[n] = 0; + return zstr; + } +}; + +template +type_name get_type_name() +{ + n::r parsed = n::p(); + return { parsed.name_not_zero_terminated_at_length, parsed.length, parsed.hash }; +} + +} // namespace serialization + +} } // namespace boost::leaf + +namespace std +{ + template <> + struct hash + { + std::size_t operator()(boost::leaf::serialization::type_name const & x) const noexcept + { + return x.hash; + } + }; +} // namespace std + +#endif // #ifndef BOOST_LEAF_SERIALIZATION_TYPE_NAME_HPP_INCLUDED diff --git a/include/boost/leaf/serialization/writer.hpp b/include/boost/leaf/serialization/writer.hpp new file mode 100644 index 0000000..393cf36 --- /dev/null +++ b/include/boost/leaf/serialization/writer.hpp @@ -0,0 +1,61 @@ +#ifndef BOOST_LEAF_SERIALIZATION_WRITER_HPP_INCLUDED +#define BOOST_LEAF_SERIALIZATION_WRITER_HPP_INCLUDED + +// Copyright 2018-2026 Emil Dotchevski and Reverge Studios, Inc. +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +#include + +namespace boost { namespace leaf { + +namespace serialization +{ +class writer +{ + type_name const type_; + +protected: + + template + explicit writer(Derived * d) noexcept: + type_(get_type_name()) + { + BOOST_LEAF_ASSERT(d == this), (void) d; + } + + ~writer() noexcept + { + } + +public: + + template + Derived * get() noexcept + { + return type_ == get_type_name::type>() ? static_cast(this) : nullptr; + } +}; + + template + typename std::enable_if::value>::type + serialize(W &, E const &) + { + } +} + +namespace detail +{ + template + struct dependent_writer + { + using type = serialization::writer; + }; +} + +} } // namespace boost::leaf + +#endif // #ifndef BOOST_LEAF_SERIALIZATION_WRITER_HPP_INCLUDED diff --git a/meson.build b/meson.build index b0250da..2e1da94 100644 --- a/meson.build +++ b/meson.build @@ -141,6 +141,7 @@ if option_enable_unit_tests 'handle_basic_test', 'handle_some_other_result_test', 'handle_some_test', + 'nlohmann_test', 'match_member_test', 'match_test', 'match_value_test', @@ -172,7 +173,7 @@ if option_enable_unit_tests 'on_error_preload_nested_success_exception_test', 'on_error_preload_nested_success_result_test', 'optional_test', - 'parse_name_test', + 'type_name_test', 'print_test', 'result_bad_result_test', 'result_implicit_conversion_test', @@ -223,6 +224,10 @@ if option_enable_unit_tests '_hpp_on_error_test', '_hpp_pred_test', '_hpp_result_test', + '_hpp_serialization_diagnostics_writer_test', + '_hpp_serialization_json_writer_test', + '_hpp_serialization_writer_test', + '_hpp_serialization_type_name_test', '_hpp_to_variant_test', ] foreach t : header_tests diff --git a/scripts/download_nlohmann_json.py b/scripts/download_nlohmann_json.py new file mode 100644 index 0000000..0b53ae0 --- /dev/null +++ b/scripts/download_nlohmann_json.py @@ -0,0 +1,33 @@ +""" + + Copyright 2018-2025 Emil Dotchevski and Reverge Studios, Inc. + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + This program downloads the nlohmann/json single header distribution. + + Usage: + + python3 download_nlohmann_json.py + +""" + +import urllib.request +import os + +url = "https://github.com/nlohmann/json/releases/download/v3.11.3/json.hpp" +output_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "test", "nlohmann") +output_file = os.path.join(output_dir, "json.hpp") + +def _main(): + if os.path.exists(output_file): + print(f"{output_file} already exists, skipping download") + return + os.makedirs(output_dir, exist_ok=True) + print(f"Downloading {url}...") + urllib.request.urlretrieve(url, output_file) + print(f"Saved to {output_file}") + +if __name__ == "__main__": + _main() diff --git a/gen/generate_single_header.py b/scripts/generate_single_header.py similarity index 100% rename from gen/generate_single_header.py rename to scripts/generate_single_header.py diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index cff2af9..4fd1079 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -44,6 +44,10 @@ compile _hpp_leaf_test.cpp ; compile _hpp_on_error_test.cpp ; compile _hpp_pred_test.cpp ; compile _hpp_result_test.cpp ; +compile _hpp_serialization_diagnostics_writer_test.cpp ; +compile _hpp_serialization_json_writer_test.cpp ; +compile _hpp_serialization_writer_test.cpp ; +compile _hpp_serialization_type_name_test.cpp ; compile _hpp_to_variant_test.cpp ; run boost_exception_test.cpp ; @@ -83,6 +87,7 @@ run handle_all_test.cpp ; run handle_basic_test.cpp ; run handle_some_other_result_test.cpp ; run handle_some_test.cpp ; +run nlohmann_test.cpp : : : clang,linux,2b:no clang,linux,23:no ; run match_member_test.cpp ; run match_test.cpp ; run match_value_test.cpp ; @@ -114,7 +119,7 @@ run on_error_preload_nested_new_error_result_test.cpp ; run on_error_preload_nested_success_exception_test.cpp ; run on_error_preload_nested_success_result_test.cpp ; run optional_test.cpp ; -run parse_name_test.cpp ; +run type_name_test.cpp ; run print_test.cpp ; run result_bad_result_test.cpp ; run result_implicit_conversion_test.cpp ; @@ -161,6 +166,7 @@ compile-fail _compile-fail-result_1.cpp ; compile-fail _compile-fail-result_2.cpp ; compile-fail _compile-fail-result_3.cpp ; compile-fail _compile-fail-result_4.cpp ; +compile-fail _compile-fail-to_json.cpp ; exe try_capture_all_exceptions : ../example/try_capture_all_exceptions.cpp : single:no off:no leaf_debug_capture0:no leaf_release_capture0:no ; exe try_capture_all_result : ../example/try_capture_all_result.cpp : single:no leaf_debug_capture0:no leaf_release_capture0:no leaf_debug_embedded:no leaf_release_embedded:no ; diff --git a/test/_compile-fail-to_json.cpp b/test/_compile-fail-to_json.cpp new file mode 100644 index 0000000..b275696 --- /dev/null +++ b/test/_compile-fail-to_json.cpp @@ -0,0 +1,17 @@ +// Copyright 2018-2025 Emil Dotchevski and Reverge Studios, Inc. +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include "nlohmann/json.hpp" + +struct no_to_json {}; + +struct e_no_to_json +{ + no_to_json value; +}; + +nlohmann::json j; +e_no_to_json e; +auto x = (boost::leaf::serialization::to_json(j, e), 0); diff --git a/test/_hpp_serialization_diagnostics_writer_test.cpp b/test/_hpp_serialization_diagnostics_writer_test.cpp new file mode 100644 index 0000000..94efd58 --- /dev/null +++ b/test/_hpp_serialization_diagnostics_writer_test.cpp @@ -0,0 +1,7 @@ +// Copyright 2018-2025 Emil Dotchevski and Reverge Studios, Inc. +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +int main() { return 0; } diff --git a/test/_hpp_serialization_json_writer_test.cpp b/test/_hpp_serialization_json_writer_test.cpp new file mode 100644 index 0000000..71ba7e7 --- /dev/null +++ b/test/_hpp_serialization_json_writer_test.cpp @@ -0,0 +1,7 @@ +// Copyright 2018-2025 Emil Dotchevski and Reverge Studios, Inc. +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +int main() { return 0; } diff --git a/test/_hpp_serialization_type_name_test.cpp b/test/_hpp_serialization_type_name_test.cpp new file mode 100644 index 0000000..a52444a --- /dev/null +++ b/test/_hpp_serialization_type_name_test.cpp @@ -0,0 +1,7 @@ +// Copyright 2018-2026 Emil Dotchevski and Reverge Studios, Inc. +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +int main() { return 0; } diff --git a/test/_hpp_capture_test.cpp b/test/_hpp_serialization_writer_test.cpp similarity index 53% rename from test/_hpp_capture_test.cpp rename to test/_hpp_serialization_writer_test.cpp index 99bd00e..222da67 100644 --- a/test/_hpp_capture_test.cpp +++ b/test/_hpp_serialization_writer_test.cpp @@ -1,7 +1,7 @@ -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. +// Copyright 2018-2025 Emil Dotchevski and Reverge Studios, Inc. // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -#include -#include +#include +#include int main() { return 0; } diff --git a/test/context_deduction_test.cpp b/test/context_deduction_test.cpp index f9908c1..693286e 100644 --- a/test/context_deduction_test.cpp +++ b/test/context_deduction_test.cpp @@ -137,7 +137,7 @@ void not_called_on_purpose() test, info<2>, info<3>>( expd([]( info<1> const, info<2> ){ }, []( info<1> const *, info<3> ){ }) ); test, info<2>, leaf::e_source_location>( expd([]( info<1>, info<2>, leaf::diagnostic_info const & ){ }, []( info<1>, info<2> ){ }) ); -#if BOOST_LEAF_CFG_DIAGNOSTICS && BOOST_LEAF_CFG_CAPTURE +#if BOOST_LEAF_CFG_CAPTURE test, info<2>, leaf::e_source_location, leaf::detail::dynamic_allocator>( expd([]( info<1>, info<2>, leaf::diagnostic_details const & ){ }, []( info<1>, info<2> ){ }) ); #else test, info<2>, leaf::e_source_location>( expd([]( info<1>, info<2>, leaf::diagnostic_details const & ){ }, []( info<1>, info<2> ){ }) ); diff --git a/test/diagnostics_test5.cpp b/test/diagnostics_test5.cpp index 2f55412..1326a64 100644 --- a/test/diagnostics_test5.cpp +++ b/test/diagnostics_test5.cpp @@ -93,20 +93,23 @@ int main() st << di; std::string s = st.str(); std::cout << s << std::endl; - if( BOOST_LEAF_CFG_DIAGNOSTICS && BOOST_LEAF_CFG_CAPTURE ) + if( BOOST_LEAF_CFG_CAPTURE ) { - auto const n1 = s.find("info<1>: acc=0"); - auto const n2 = s.find("info<2>: acc=0"); - auto const n3 = s.find("info<3>: acc=0"); - auto const n4 = s.find("info<4>: acc=2"); - BOOST_TEST_NE(n1, s.npos); - BOOST_TEST_NE(n2, s.npos); - BOOST_TEST_NE(n3, s.npos); - BOOST_TEST_NE(n4, s.npos); + if( BOOST_LEAF_CFG_DIAGNOSTICS ) + { + auto const n1 = s.find("info<1>: acc=0"); + auto const n2 = s.find("info<2>: acc=0"); + auto const n3 = s.find("info<3>: acc=0"); + auto const n4 = s.find("info<4>: acc=2"); + BOOST_TEST_NE(n1, s.npos); + BOOST_TEST_NE(n2, s.npos); + BOOST_TEST_NE(n3, s.npos); + BOOST_TEST_NE(n4, s.npos); + } BOOST_TEST_EQ(counter, 4); } else - BOOST_TEST_EQ(counter, 1); + BOOST_TEST_EQ(counter, 1); #endif }, [] diff --git a/test/diagnostics_test6.cpp b/test/diagnostics_test6.cpp index c32759d..8d22fd8 100644 --- a/test/diagnostics_test6.cpp +++ b/test/diagnostics_test6.cpp @@ -7,7 +7,6 @@ #else # include # include -# include # include #endif diff --git a/test/error_code_test.cpp b/test/error_code_test.cpp index 162ea5d..81212b9 100644 --- a/test/error_code_test.cpp +++ b/test/error_code_test.cpp @@ -19,7 +19,6 @@ int main() #ifdef BOOST_LEAF_TEST_SINGLE_HEADER # include "leaf.hpp" #else -# include # include # include #endif diff --git a/test/exception_test.cpp b/test/exception_test.cpp index f915863..1f6f386 100644 --- a/test/exception_test.cpp +++ b/test/exception_test.cpp @@ -19,7 +19,6 @@ int main() #ifdef BOOST_LEAF_TEST_SINGLE_HEADER # include "leaf.hpp" #else -# include # include # include # include diff --git a/test/handle_some_test.cpp b/test/handle_some_test.cpp index 051d6d5..144c11b 100644 --- a/test/handle_some_test.cpp +++ b/test/handle_some_test.cpp @@ -1478,7 +1478,7 @@ int main() #ifndef BOOST_LEAF_NO_EXCEPTIONS // exception caught, error not handled - { + { int handle_some_handler_called = 0; int r = leaf::try_handle_all( [&] diff --git a/test/multiple_errors_test.cpp b/test/multiple_errors_test.cpp index ece87b5..8f9ffa9 100644 --- a/test/multiple_errors_test.cpp +++ b/test/multiple_errors_test.cpp @@ -2,13 +2,11 @@ // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -#include -#include -#include - #ifdef BOOST_LEAF_TEST_SINGLE_HEADER # include "leaf.hpp" #else +# include +# include # include #endif diff --git a/test/nlohmann_test.cpp b/test/nlohmann_test.cpp new file mode 100644 index 0000000..aa43ba1 --- /dev/null +++ b/test/nlohmann_test.cpp @@ -0,0 +1,399 @@ +// Copyright 2018-2026 Emil Dotchevski and Reverge Studios, Inc. +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include +# include +# include +# include +# include +# include +#endif + +#include "nlohmann/json.hpp" +#include +#include +#include +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR +# include +#endif + +#include "lightweight_test.hpp" + +namespace leaf = boost::leaf; + +using nlohmann_writer = leaf::serialization::json_writer; + +namespace boost { namespace leaf { + +namespace serialization { + +template +void serialize(writer & w, E const & e) +{ + if( nlohmann_writer * nw = w.get() ) + nw->write(e); +} + +} + +} } + +struct my_exception { }; + +struct my_exception_ptr +{ + std::exception_ptr value; +}; + +template +struct my_error +{ + int code; + char const * message; + + template + friend void to_json(Json & j, my_error const & e) + { + j["code"] = e.code; + j["message"] = e.message; + } + + friend std::ostream & operator<<(std::ostream & os, my_error const & e) + { + return os << "code=" << e.code << ", message=" << e.message; + } +}; + +leaf::result fail() +{ + return BOOST_LEAF_NEW_ERROR( +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR + std::make_error_code(std::errc::invalid_argument), + std::make_error_condition(std::errc::io_error), +#endif + 42, + my_error<1>{1, "error one"}, + my_error<2>{2, "error two"}, + leaf::e_errno{ENOENT}, + leaf::e_api_function{"my_api_function"} ); +} + +#ifndef BOOST_LEAF_NO_EXCEPTIONS +void leaf_throw() +{ + BOOST_LEAF_THROW_EXCEPTION( +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR + std::make_error_code(std::errc::invalid_argument), + std::make_error_condition(std::errc::io_error), +#endif + 42, + my_error<1>{1, "error one"}, + my_error<2>{2, "error two"}, + leaf::e_errno{ENOENT}, + leaf::e_api_function{"my_api_function"} ); +} + +void throw_() +{ + auto load = leaf::on_error( +#if BOOST_LEAF_CFG_STD_SYSTEM_ERROR + std::make_error_code(std::errc::invalid_argument), + std::make_error_condition(std::errc::io_error), +#endif + 42, + my_error<1>{1, "error one"}, + my_error<2>{2, "error two"}, + leaf::e_errno{ENOENT}, + leaf::e_api_function{"my_api_function"} ); + throw my_exception{}; +} +#endif + +void check_diagnostic_info(nlohmann::ordered_json const & j, bool has_source_location) +{ + BOOST_TEST(j["boost::leaf::error_id"].get() > 0); + + auto const & e1j = j["my_error<1>"]; + BOOST_TEST_EQ(e1j["code"].get(), 1); + BOOST_TEST_EQ(e1j["message"].get(), "error one"); + + if( has_source_location ) + { + auto const & loc = j["boost::leaf::e_source_location"]; + BOOST_TEST(!loc["file"].get().empty()); + BOOST_TEST(loc["line"].get() > 0); + BOOST_TEST(!loc["function"].get().empty()); + } + + BOOST_TEST(!j.contains("my_error<2>")); +} + +void check_diagnostic_details(nlohmann::ordered_json const & j, bool has_source_location) +{ + BOOST_TEST(j["boost::leaf::error_id"].get() > 0); + + auto const & e1j = j["my_error<1>"]; + BOOST_TEST_EQ(e1j["code"].get(), 1); + BOOST_TEST_EQ(e1j["message"].get(), "error one"); + + if( has_source_location ) + { + auto const & loc = j["boost::leaf::e_source_location"]; + BOOST_TEST(!loc["file"].get().empty()); + BOOST_TEST(loc["line"].get() > 0); + BOOST_TEST(!loc["function"].get().empty()); + } + + if( BOOST_LEAF_CFG_CAPTURE ) + { + BOOST_TEST_EQ(j["int"].get(), 42); + + auto const & e2j = j["my_error<2>"]; + BOOST_TEST_EQ(e2j["code"].get(), 2); + BOOST_TEST_EQ(e2j["message"].get(), "error two"); + + auto const & ej = j["boost::leaf::e_errno"]; + BOOST_TEST_EQ(ej["value"].get(), ENOENT); + BOOST_TEST(!ej["message"].get().empty()); + + BOOST_TEST_EQ(j["boost::leaf::e_api_function"]["value"].get(), "my_api_function"); + + if( BOOST_LEAF_CFG_STD_SYSTEM_ERROR ) + { + auto const & ecj = j["std::error_code"]; + BOOST_TEST_EQ(ecj["value"].get(), static_cast(std::errc::invalid_argument)); + BOOST_TEST(!ecj["category"].get().empty()); + BOOST_TEST(!ecj["message"].get().empty()); + + auto const & econdj = j["std::error_condition"]; + BOOST_TEST_EQ(econdj["value"].get(), static_cast(std::errc::io_error)); + BOOST_TEST(!econdj["category"].get().empty()); + BOOST_TEST(!econdj["message"].get().empty()); + } + } + else + { + BOOST_TEST(!j.contains("int")); + BOOST_TEST(!j.contains("my_error<2>")); + BOOST_TEST(!j.contains("boost::leaf::e_errno")); + BOOST_TEST(!j.contains("boost::leaf::e_api_function")); + BOOST_TEST(!j.contains("std::error_code")); + BOOST_TEST(!j.contains("std::error_condition")); + } +} + +#ifndef BOOST_LEAF_NO_EXCEPTIONS +void check_exception(nlohmann::ordered_json const & j) +{ + auto const & exj = j["std::exception"]; + BOOST_TEST(!exj["type"].get().empty()); + BOOST_TEST(!exj["what"].get().empty()); +} +#endif + +int main() +{ + { + nlohmann::ordered_json j; + leaf::try_handle_all( + [] + { + return fail(); + }, + [&j](leaf::diagnostic_info const & di, my_error<1> const * e1) + { + BOOST_TEST(e1 != nullptr); + nlohmann_writer w(j); + di.write_to(w); + } + ); + std::cout << __LINE__ << " diagnostic_info JSON output:\n" << std::setw(2) << j << std::endl; + check_diagnostic_info(j, true); + } + + { + nlohmann::ordered_json j; + leaf::try_handle_all( + [] + { + return fail(); + }, + [&j](leaf::diagnostic_details const & dd, my_error<1> const * e1) + { + BOOST_TEST(e1 != nullptr); + nlohmann_writer w(j); + dd.write_to(w); + } + ); + std::cout << __LINE__ << " diagnostic_details JSON output:\n" << std::setw(2) << j << std::endl; + check_diagnostic_details(j, true); + } + +#ifndef BOOST_LEAF_NO_EXCEPTIONS + { + nlohmann::ordered_json j; + leaf::try_catch( + [] + { + leaf_throw(); + }, + [&j](leaf::diagnostic_info const & di, my_error<1> const * e1) + { + BOOST_TEST(e1 != nullptr); + nlohmann_writer w(j); + di.write_to(w); + } + ); + std::cout << __LINE__ << " leaf_throw diagnostic_info JSON output:\n" << std::setw(2) << j << std::endl; + check_diagnostic_info(j, true); + check_exception(j); + } + + { + nlohmann::ordered_json j; + leaf::try_catch( + [] + { + leaf_throw(); + }, + [&j](leaf::diagnostic_details const & dd, my_error<1> const * e1) + { + BOOST_TEST(e1 != nullptr); + nlohmann_writer w(j); + dd.write_to(w); + } + ); + std::cout << __LINE__ << " leaf_throw diagnostic_details JSON output:\n" << std::setw(2) << j << std::endl; + check_diagnostic_details(j, true); + check_exception(j); + } + + { + nlohmann::ordered_json j; + leaf::try_catch( + [] + { + throw_(); + }, + [&j](leaf::diagnostic_info const & di, my_error<1> const * e1) + { + BOOST_TEST(e1 != nullptr); + nlohmann_writer w(j); + di.write_to(w); + } + ); + std::cout << __LINE__ << " throw_ diagnostic_info JSON output:\n" << std::setw(2) << j << std::endl; + check_diagnostic_info(j, false); + } + + { + nlohmann::ordered_json j; + leaf::try_catch( + [] + { + throw_(); + }, + [&j](leaf::diagnostic_details const & dd, my_error<1> const * e1) + { + BOOST_TEST(e1 != nullptr); + nlohmann_writer w(j); + dd.write_to(w); + } + ); + std::cout << __LINE__ << " throw_ diagnostic_details JSON output:\n" << std::setw(2) << j << std::endl; + check_diagnostic_details(j, false); + } + + { + nlohmann::ordered_json j; + leaf::try_handle_all( + []() -> leaf::result + { + return leaf::new_error(my_exception_ptr{std::make_exception_ptr(std::runtime_error("test exception"))}); + }, + [&j](leaf::diagnostic_details const & dd, my_exception_ptr *) + { + nlohmann_writer w(j); + dd.write_to(w); + } + ); + std::cout << __LINE__ << " std::exception_ptr JSON output:\n" << std::setw(2) << j << std::endl; + + auto const & ep = j["my_exception_ptr"]["value"]; + std::string type = ep["type"].get(); + std::string what = ep["what"].get(); + BOOST_TEST(type.find("std::runtime_error") != std::string::npos); + BOOST_TEST_EQ(what, "test exception"); + } + + { + nlohmann::ordered_json j; + leaf::try_handle_all( + []() -> leaf::result + { + return leaf::new_error(my_exception_ptr{std::make_exception_ptr(42)}); + }, + [&j](leaf::diagnostic_details const & dd, my_exception_ptr *) + { + nlohmann_writer w(j); + dd.write_to(w); + } + ); + std::cout << __LINE__ << " non-std::exception_ptr JSON output:\n" << std::setw(2) << j << std::endl; + + auto const & ep = j["my_exception_ptr"]["value"]; + std::string type = ep["type"].get(); + std::string what = ep["what"].get(); + BOOST_TEST_EQ(type, "<>"); + BOOST_TEST_EQ(what, "N/A"); + } +#endif + + { + nlohmann::ordered_json j; + leaf::result r = 42; + BOOST_TEST(r); + nlohmann_writer w(j); + r.write_to(w); + std::cout << __LINE__ << " result success JSON output:\n" << std::setw(2) << j << std::endl; + BOOST_TEST_EQ(j["int"].get(), 42); + } + + { + nlohmann::ordered_json j; + leaf::result r = leaf::new_error(); + BOOST_TEST(!r); + nlohmann_writer w(j); + r.write_to(w); + std::cout << __LINE__ << " result error JSON output:\n" << std::setw(2) << j << std::endl; + BOOST_TEST(j["boost::leaf::error_id"].get() > 0); + } + +#if BOOST_LEAF_CFG_CAPTURE + { + nlohmann::ordered_json j; + leaf::result r = leaf::try_capture_all( + []() -> leaf::result + { + return leaf::new_error(my_error<1>{1, "error one"}, my_error<2>{2, "error two"}); + } ); + BOOST_TEST(!r); + nlohmann_writer w(j); + r.write_to(w); + std::cout << __LINE__ << " result captured error JSON output:\n" << std::setw(2) << j << std::endl; + BOOST_TEST(j["boost::leaf::error_id"].get() > 0); + auto const & e1j = j["my_error<1>"]; + BOOST_TEST_EQ(e1j["code"].get(), 1); + BOOST_TEST_EQ(e1j["message"].get(), "error one"); + auto const & e2j = j["my_error<2>"]; + BOOST_TEST_EQ(e2j["code"].get(), 2); + BOOST_TEST_EQ(e2j["message"].get(), "error two"); + } +#endif + + return boost::report_errors(); +} diff --git a/test/on_error_dynamic_reserve_test2.cpp b/test/on_error_dynamic_reserve_test2.cpp index 4d1dd30..cb7f787 100644 --- a/test/on_error_dynamic_reserve_test2.cpp +++ b/test/on_error_dynamic_reserve_test2.cpp @@ -30,7 +30,7 @@ leaf::result f() BOOST_TEST_EQ(leaf::tls::read_ptr>>(), nullptr); auto e = leaf::new_error(info<43>{}); BOOST_TEST( - (BOOST_LEAF_CFG_CAPTURE == 0 || BOOST_LEAF_CFG_DIAGNOSTICS == 0) + (BOOST_LEAF_CFG_CAPTURE == 0) == (leaf::tls::read_ptr>>() == nullptr)); return e; diff --git a/test/parse_name_test.cpp b/test/parse_name_test.cpp deleted file mode 100644 index 3c786ac..0000000 --- a/test/parse_name_test.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc. -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#ifdef BOOST_LEAF_TEST_SINGLE_HEADER -# include "leaf.hpp" -#else -# include -#endif - -#include -#include - -namespace leaf = boost::leaf; - -#include "lightweight_test.hpp" - -namespace leaf_test -{ - class class_ { }; - struct struct_ { }; - enum enum_ { }; - template class class_template1 { }; - template struct struct_template1 { }; - template class class_template2 { }; - template struct struct_template2 { }; -} - -namespace boost { namespace leaf { - struct in_namespace_boost_leaf { }; -} } - -class class_ { }; -struct struct_ { }; -enum enum_ { }; -template class class_template1 { }; -template struct struct_template1 { }; -template class class_template2 { }; -template struct struct_template2 { }; - -bool test(leaf::parsed const & pn, char const * correct) -{ - return - std::strlen(correct) == pn.len && - std::memcmp(correct, pn.name, pn.len) == 0; -} - -int main() -{ - using leaf::parse; - - BOOST_TEST(test(parse(), "boost::leaf::in_namespace_boost_leaf")); - - BOOST_TEST(test(parse(), "int")); - - BOOST_TEST(test(parse(), "leaf_test::class_")); - BOOST_TEST(test(parse(), "leaf_test::struct_")); - BOOST_TEST(test(parse(), "leaf_test::enum_")); - BOOST_TEST(test(parse>(), "leaf_test::class_template1<42>")); - BOOST_TEST(test(parse>(), "leaf_test::struct_template1<42>")); - BOOST_TEST(test(parse>(), "leaf_test::class_template2")); - BOOST_TEST(test(parse>(), "leaf_test::struct_template2")); - - BOOST_TEST(test(parse(), "class_")); - BOOST_TEST(test(parse(), "struct_")); - BOOST_TEST(test(parse(), "enum_")); - BOOST_TEST(test(parse>(), "class_template1<42>")); - BOOST_TEST(test(parse>(), "struct_template1<42>")); - BOOST_TEST(test(parse>(), "class_template2")); - BOOST_TEST(test(parse>(), "struct_template2")); - - return boost::report_errors(); -} diff --git a/test/print_test.cpp b/test/print_test.cpp index 9ddfc4d..425fd0d 100644 --- a/test/print_test.cpp +++ b/test/print_test.cpp @@ -19,7 +19,7 @@ int main() #ifdef BOOST_LEAF_TEST_SINGLE_HEADER # include "leaf.hpp" #else -# include +# include #endif #include @@ -71,10 +71,12 @@ struct c4 template std::string print(T const & x, char const * prefix, char const * delimiter) { - using namespace leaf::detail; std::ostringstream s; - diagnostic::print(s, prefix, delimiter, x); - diagnostic::print(s, prefix, delimiter, x); + leaf::serialization::diagnostics_writer w(s); + w.set_prefix(prefix); + w.set_delimiter(delimiter); + w.write(x); + w.write(x); std::string q = s.str(); std::cout << "[LINE " << Line << "] " << q << '\n'; return q; diff --git a/test/result_print_test.cpp b/test/result_print_test.cpp index 2f0a040..f234a52 100644 --- a/test/result_print_test.cpp +++ b/test/result_print_test.cpp @@ -22,13 +22,10 @@ struct non_printable_value { }; +template struct e_err { - template - friend std::ostream & operator<<( std::basic_ostream & os, e_err const & ) - { - return os << "e_err"; - } + int value; }; int main() @@ -42,7 +39,9 @@ int main() std::string s = ss.str(); std::cout << s << std::endl; if( BOOST_LEAF_CFG_DIAGNOSTICS ) - BOOST_TEST_EQ(s, "42"); + BOOST_TEST_EQ(s, "Success: int: 42"); + else + BOOST_TEST_EQ(s, "Success"); #endif } @@ -55,7 +54,9 @@ int main() std::string s = ss.str(); std::cout << s << std::endl; if( BOOST_LEAF_CFG_DIAGNOSTICS ) - BOOST_TEST_EQ(s, "{not printable}"); + BOOST_TEST_EQ(s, "Success: non_printable_value"); + else + BOOST_TEST_EQ(s, "Success"); #endif } @@ -67,22 +68,25 @@ int main() ss << r; std::string s = ss.str(); std::cout << s << std::endl; - if( BOOST_LEAF_CFG_DIAGNOSTICS ) - BOOST_TEST_EQ(s, "No error"); + BOOST_TEST_EQ(s, "Success"); #endif } { - leaf::result r = leaf::new_error(e_err{ }); + leaf::result r = leaf::new_error(); BOOST_TEST(!r); #if BOOST_LEAF_CFG_STD_STRING std::ostringstream ss; ss << r; std::string s = ss.str(); std::cout << s << std::endl; - leaf::error_id err = r.error(); if( BOOST_LEAF_CFG_DIAGNOSTICS ) - BOOST_TEST_EQ(s, "Error serial #" + std::to_string(err.value()/4)); + { + leaf::error_id err = r.error(); + BOOST_TEST_EQ(s, "Failure: boost::leaf::error_id: " + std::to_string(err.value()/4)); + } + else + BOOST_TEST_EQ(s, "Failure"); #endif } @@ -91,20 +95,23 @@ int main() leaf::result r = leaf::try_capture_all( []() -> leaf::result { - return leaf::new_error(e_err{ }); + return leaf::new_error(e_err<1>{1}, e_err<2>{2}); } ); #if BOOST_LEAF_CFG_STD_STRING std::ostringstream ss; ss << r; std::string s = ss.str(); std::cout << s << std::endl; - leaf::error_id err = r.error(); - BOOST_TEST_NE(s.find("Error serial #" + std::to_string(err.value()/4)), s.npos); if( BOOST_LEAF_CFG_DIAGNOSTICS ) { - BOOST_TEST_NE(s.find("Captured:"), s.npos); - BOOST_TEST_NE(s.find("e_err: e_err"), s.npos); + leaf::error_id err = r.error(); + BOOST_TEST_NE(s.find("Failure: boost::leaf::error_id: " + std::to_string(err.value()/4)), s.npos); + BOOST_TEST_NE(s.find(", captured -> "), s.npos); + BOOST_TEST_NE(s.find("e_err<1>: 1"), s.npos); + BOOST_TEST_NE(s.find("e_err<2>: 2"), s.npos); } + else + BOOST_TEST_EQ(s, "Failure"); #endif } #endif diff --git a/test/result_ref_test.cpp b/test/result_ref_test.cpp index ac17cdb..b8d3760 100644 --- a/test/result_ref_test.cpp +++ b/test/result_ref_test.cpp @@ -6,7 +6,6 @@ # include "leaf.hpp" #else # include -# include #endif #include "lightweight_test.hpp" diff --git a/test/type_name_test.cpp b/test/type_name_test.cpp new file mode 100644 index 0000000..a8ee2e9 --- /dev/null +++ b/test/type_name_test.cpp @@ -0,0 +1,121 @@ +// Copyright 2018-2026 Emil Dotchevski and Reverge Studios, Inc. +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifdef BOOST_LEAF_TEST_SINGLE_HEADER +# include "leaf.hpp" +#else +# include +#endif + +#include +#include +#include + +#if __cplusplus >= 201703L +# include +#endif + +namespace leaf = boost::leaf; +namespace serialization = boost::leaf::serialization; + +#include "lightweight_test.hpp" + +namespace leaf_test +{ + class class_ { }; + struct struct_ { }; + enum enum_ { }; + template class class_template1 { }; + template struct struct_template1 { }; + template class class_template2 { }; + template struct struct_template2 { }; +} + +namespace boost { namespace leaf { + struct in_namespace_boost_leaf { }; +} } + +class class_ { }; +struct struct_ { }; +enum enum_ { }; +template class class_template1 { }; +template struct struct_template1 { }; +template class class_template2 { }; +template struct struct_template2 { }; + +bool test(serialization::type_name const & tn, char const * correct) +{ + return + std::strlen(correct) == tn.length && + std::memcmp(correct, tn.name_not_zero_terminated_at_length, tn.length) == 0; +} + +int main() +{ + using serialization::get_type_name; + + BOOST_TEST(test(get_type_name(), "boost::leaf::in_namespace_boost_leaf")); + + BOOST_TEST(test(get_type_name(), "int")); + + BOOST_TEST(test(get_type_name(), "leaf_test::class_")); + BOOST_TEST(test(get_type_name(), "leaf_test::struct_")); + BOOST_TEST(test(get_type_name(), "leaf_test::enum_")); + BOOST_TEST(test(get_type_name>(), "leaf_test::class_template1<42>")); + BOOST_TEST(test(get_type_name>(), "leaf_test::struct_template1<42>")); + BOOST_TEST(test(get_type_name>(), "leaf_test::class_template2")); + BOOST_TEST(test(get_type_name>(), "leaf_test::struct_template2")); + + BOOST_TEST(test(get_type_name(), "class_")); + BOOST_TEST(test(get_type_name(), "struct_")); + BOOST_TEST(test(get_type_name(), "enum_")); + BOOST_TEST(test(get_type_name>(), "class_template1<42>")); + BOOST_TEST(test(get_type_name>(), "struct_template1<42>")); + BOOST_TEST(test(get_type_name>(), "class_template2")); + BOOST_TEST(test(get_type_name>(), "struct_template2")); + + BOOST_TEST(get_type_name() == get_type_name()); + BOOST_TEST(get_type_name() == get_type_name()); + BOOST_TEST(!(get_type_name() == get_type_name())); + BOOST_TEST(!(get_type_name() == get_type_name())); + + BOOST_TEST(!(get_type_name() != get_type_name())); + BOOST_TEST(get_type_name() != get_type_name()); + BOOST_TEST(get_type_name() != get_type_name()); + + BOOST_TEST(get_type_name() < get_type_name()); // "class_" < "struct_" + BOOST_TEST(!(get_type_name() < get_type_name())); + BOOST_TEST(!(get_type_name() < get_type_name())); // not less than itself + BOOST_TEST(get_type_name() < get_type_name()); // "int" < "long" + +#if __cplusplus >= 201703L + { + auto sv = to_string_view(get_type_name()); + BOOST_TEST(sv == "int"); + } + { + auto sv = to_string_view(get_type_name()); + BOOST_TEST(sv == "class_"); + } + { + auto sv = to_string_view(get_type_name()); + BOOST_TEST(sv == "leaf_test::struct_"); + } +#endif + + BOOST_TEST(std::hash()(get_type_name()) == get_type_name().hash); + BOOST_TEST(std::hash()(get_type_name()) == std::hash()(get_type_name())); + { + std::unordered_set s; + s.insert(get_type_name()); + s.insert(get_type_name()); + s.insert(get_type_name()); // duplicate + BOOST_TEST(s.size() == 2); + BOOST_TEST(s.count(get_type_name()) == 1); + BOOST_TEST(s.count(get_type_name()) == 1); + BOOST_TEST(s.count(get_type_name()) == 0); + } + + return boost::report_errors(); +}