From c1e7e3511e166cf2a06ee07ebdf8bc4630ed3c11 Mon Sep 17 00:00:00 2001 From: Emil Dotchevski Date: Mon, 12 Jan 2026 00:14:24 -0500 Subject: [PATCH] Serialization --- .github/workflows/ci.yml | 19 ++ .gitignore | 1 + .vscode/tasks.json | 101 +++++++++ .../exception/detail/error_info_impl.hpp | 4 + .../boost/exception/detail/requires_cxx11.hpp | 2 +- include/boost/exception/detail/type_info.hpp | 212 +++++++++++++++++- include/boost/exception/detail/writer.hpp | 114 ++++++++++ .../exception/diagnostic_information.hpp | 59 +++++ include/boost/exception/info.hpp | 51 +++++ .../serialization/nlohmann_writer.hpp | 43 ++++ scripts/download_nlohmann_json.py | 33 +++ test/Jamfile.v2 | 1 + test/diagnostic_information_test.cpp | 54 +++-- test/nlohmann_test.cpp | 148 ++++++++++++ 14 files changed, 821 insertions(+), 21 deletions(-) create mode 100644 .vscode/tasks.json create mode 100644 include/boost/exception/detail/writer.hpp create mode 100644 include/boost/exception/serialization/nlohmann_writer.hpp create mode 100644 scripts/download_nlohmann_json.py create mode 100644 test/nlohmann_test.cpp diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7383a16..60df085 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -219,6 +219,10 @@ jobs: cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY git submodule update --init tools/boostdep python3 tools/boostdep/depinst/depinst.py --git_args "--jobs 3" $LIBRARY + cd libs/throw_exception + git fetch origin feature/serialization + git checkout feature/serialization + cd ../.. ./bootstrap.sh ./b2 -d0 headers @@ -227,6 +231,11 @@ jobs: run: | echo "using ${{matrix.toolset}} : : ${{matrix.compiler}} ;" > ~/user-config.jam + - name: Download nlohmann/json + run: | + cd ../boost-root/libs/$LIBRARY + python3 scripts/download_nlohmann_json.py + - name: Run tests run: | cd ../boost-root @@ -282,9 +291,19 @@ jobs: xcopy /s /e /q %GITHUB_WORKSPACE% libs\%LIBRARY%\ git submodule update --init tools/boostdep python tools/boostdep/depinst/depinst.py --git_args "--jobs 3" %LIBRARY% + cd libs\throw_exception + git fetch origin feature/serialization + git checkout feature/serialization + cd ..\.. cmd /c bootstrap b2 -d0 headers + - name: Download nlohmann/json + shell: cmd + run: | + cd ../boost-root/libs/%LIBRARY% + python3 scripts/download_nlohmann_json.py + - name: Run tests shell: cmd run: | diff --git a/.gitignore b/.gitignore index cbe87fe..9c8e405 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /.vscode/ipch/* /.vscode/settings.json .DS_Store +/test/nlohmann/ \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..e67eb6a --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,101 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Download nlohmann/json.hpp", + "type": "shell", + "command": "python scripts/download_nlohmann_json.py", + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": [] + }, + { + "label": "b2: Run all tests (default)", + "type": "shell", + "dependsOn": ["Download nlohmann/json.hpp"], + "command": "../../b2 test", + "options": { + "cwd": "${workspaceFolder}" + }, + "group": "test", + "problemMatcher": { + "base": "$gcc", + "fileLocation": ["relative", "${workspaceFolder}"] + }, + "windows": { + "command": "..\\..\\b2 test", + "problemMatcher": { + "base": "$msCompile", + "fileLocation": ["relative", "${workspaceFolder}"] + } + } + }, + { + "label": "b2: Run all tests (release)", + "type": "shell", + "dependsOn": ["Download nlohmann/json.hpp"], + "command": "../../b2 test variant=release", + "options": { + "cwd": "${workspaceFolder}" + }, + "group": "test", + "problemMatcher": { + "base": "$gcc", + "fileLocation": ["relative", "${workspaceFolder}"] + }, + "windows": { + "command": "..\\..\\b2 test variant=release", + "problemMatcher": { + "base": "$msCompile", + "fileLocation": ["relative", "${workspaceFolder}"] + } + } + }, + { + "label": "b2: Run all tests (all configs)", + "type": "shell", + "dependsOn": ["Download nlohmann/json.hpp"], + "command": "../../b2 test exception-handling=on,off rtti=on,off variant=debug,release link=static,shared", + "options": { + "cwd": "${workspaceFolder}" + }, + "group": "test", + "problemMatcher": { + "base": "$gcc", + "fileLocation": ["relative", "${workspaceFolder}"] + }, + "windows": { + "command": "..\\..\\b2 test exception-handling=on,off rtti=on,off variant=debug,release link=static,shared", + "problemMatcher": { + "base": "$msCompile", + "fileLocation": ["relative", "${workspaceFolder}"] + } + } + }, + { + "label": "b2: Run test for current editor file", + "type": "shell", + "dependsOn": ["Download nlohmann/json.hpp"], + "command": "../../b2 test ${fileBasenameNoExtension}.test", + "options": { + "cwd": "${workspaceFolder}" + }, + "group": { + "kind": "test", + "isDefault": true + }, + "problemMatcher": { + "base": "$gcc", + "fileLocation": ["relative", "${workspaceFolder}"] + }, + "windows": { + "command": "..\\..\\b2 test ${fileBasenameNoExtension}.test", + "problemMatcher": { + "base": "$msCompile", + "fileLocation": ["relative", "${workspaceFolder}"] + } + } + } + ] +} diff --git a/include/boost/exception/detail/error_info_impl.hpp b/include/boost/exception/detail/error_info_impl.hpp index d97c6f3..176aed0 100644 --- a/include/boost/exception/detail/error_info_impl.hpp +++ b/include/boost/exception/detail/error_info_impl.hpp @@ -13,6 +13,8 @@ #include #include +namespace boost { namespace exception_detail { class writer; } } + #ifndef BOOST_EXCEPTION_ENABLE_WARNINGS #if defined(__GNUC__) && __GNUC__*100+__GNUC_MINOR__>301 #pragma GCC system_header @@ -38,6 +40,7 @@ boost virtual std::string name_value_string() const = 0; virtual error_info_base * clone() const = 0; + virtual void write_to(writer &) const = 0; virtual ~error_info_base() BOOST_NOEXCEPT_OR_NOTHROW @@ -97,6 +100,7 @@ boost error_info & operator=( error_info && x ); #endif std::string name_value_string() const; + void write_to(exception_detail::writer &) const; value_type v_; }; } diff --git a/include/boost/exception/detail/requires_cxx11.hpp b/include/boost/exception/detail/requires_cxx11.hpp index 85ab2a3..914abde 100644 --- a/include/boost/exception/detail/requires_cxx11.hpp +++ b/include/boost/exception/detail/requires_cxx11.hpp @@ -21,4 +21,4 @@ BOOST_PRAGMA_MESSAGE("C++03 support was deprecated in Boost.Exception 1.85 and w #endif -#endif \ No newline at end of file +#endif diff --git a/include/boost/exception/detail/type_info.hpp b/include/boost/exception/detail/type_info.hpp index 72716b5..766b62a 100644 --- a/include/boost/exception/detail/type_info.hpp +++ b/include/boost/exception/detail/type_info.hpp @@ -11,7 +11,17 @@ #include #include #include -#include +#include +#include +#include + +#ifndef BOOST_EXCEPTION_PRETTY_FUNCTION +# if defined(_MSC_VER) && !defined(__clang__) && !defined(__GNUC__) +# define BOOST_EXCEPTION_PRETTY_FUNCTION __FUNCSIG__ +# else +# define BOOST_EXCEPTION_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# endif +#endif #ifndef BOOST_EXCEPTION_ENABLE_WARNINGS #if defined(__GNUC__) && __GNUC__*100+__GNUC_MINOR__>301 @@ -73,6 +83,206 @@ boost return a.type_!=b.type_ && strcmp(a.type_->name(), b.type_->name()) < 0; } }; + + template = S2)> + struct + cpp11_prefix + { + BOOST_FORCEINLINE static constexpr + bool + check(char const (&)[S1], char const (&)[S2]) noexcept + { + return false; + } + }; + + template + struct + cpp11_prefix + { + BOOST_FORCEINLINE static constexpr + bool + check(char const (&str)[S1], char const (&prefix)[S2]) noexcept + { + return str[I] == prefix[I] && cpp11_prefix::check(str, prefix); + } + }; + + template + struct + cpp11_prefix + { + BOOST_FORCEINLINE static constexpr + bool + check(char const (&str)[S1], char const (&prefix)[S2]) noexcept + { + return str[0] == prefix[0]; + } + }; + + template + BOOST_FORCEINLINE constexpr + int + check_prefix(char const (&str)[S1], char const (&prefix)[S2]) noexcept + { + return cpp11_prefix::check(str, prefix) ? S2 - 1 : 0; + } + + template = S2)> + struct + cpp11_suffix + { + BOOST_FORCEINLINE static constexpr + bool + check(char const (&)[S1], char const (&)[S2]) noexcept + { + return false; + } + }; + + template + struct + cpp11_suffix + { + BOOST_FORCEINLINE static constexpr + bool + check(char const (&str)[S1], char const (&suffix)[S2]) noexcept + { + return str[I1] == suffix[I2] && cpp11_suffix::check(str, suffix); + } + }; + + template + struct + cpp11_suffix + { + BOOST_FORCEINLINE static constexpr + bool + check(char const (&str)[S1], char const (&suffix)[S2]) noexcept + { + return str[I1] == suffix[0]; + } + }; + + template + BOOST_FORCEINLINE constexpr + int + check_suffix(char const (&str)[S1], char const (&suffix)[S2]) noexcept + { + return cpp11_suffix::check(str, suffix) ? S1 - S2 : 0; + } + + } + + namespace + n + { + struct + r + { + char const * name_not_zero_terminated_at_length; + std::size_t length; + }; + +#ifdef _MSC_VER +# define BOOST_EXCEPTION_CDECL __cdecl +#else +# define BOOST_EXCEPTION_CDECL +#endif + + template + BOOST_FORCEINLINE + r + BOOST_EXCEPTION_CDECL + p() + { +#define BOOST_EXCEPTION_P(P) (sizeof(char[1 + exception_detail::check_prefix(BOOST_EXCEPTION_PRETTY_FUNCTION, P)]) - 1) + // clang style: + std::size_t const p01 = BOOST_EXCEPTION_P("r boost::n::p() [T = "); + std::size_t const p02 = BOOST_EXCEPTION_P("r __cdecl boost::n::p(void) [T = "); + // old clang style: + std::size_t const p03 = BOOST_EXCEPTION_P("boost::n::r boost::n::p() [T = "); + std::size_t const p04 = BOOST_EXCEPTION_P("boost::n::r __cdecl boost::n::p(void) [T = "); + // gcc style: + std::size_t const p05 = BOOST_EXCEPTION_P("boost::n::r boost::n::p() [with T = "); + std::size_t const p06 = BOOST_EXCEPTION_P("boost::n::r __cdecl boost::n::p() [with T = "); + // msvc style, struct: + std::size_t const p07 = BOOST_EXCEPTION_P("struct boost::n::r __cdecl boost::n::p(void)"); +#undef BOOST_EXCEPTION_S + + char static_assert_unrecognized_pretty_function_format_please_file_github_issue[sizeof( + char[ + (s01 && (1 == (!!p01 + !!p02 + !!p03 + !!p04 + !!p05 + !!p06))) + || + (s02 && (1 == (!!p07 + !!p08 + !!p09))) + || + (s02 && !!p10) + ] + ) * 2 - 1]; + (void) static_assert_unrecognized_pretty_function_format_please_file_github_issue; + + if( std::size_t const p = sizeof(char[1 + !!s01 * (p01 + p02 + p03 + p04 + p05 + p06)]) - 1 ) + return { BOOST_EXCEPTION_PRETTY_FUNCTION + p, s01 - p }; + + if( std::size_t const p = sizeof(char[1 + !!s02 * (p07 + p08 + p09)]) - 1 ) + return { BOOST_EXCEPTION_PRETTY_FUNCTION + p, s02 - p }; + + std::size_t const p = sizeof(char[1 + !!s02 * p10]) - 1; + return { BOOST_EXCEPTION_PRETTY_FUNCTION + p, s02 - p }; + } + +#undef BOOST_EXCEPTION_CDECL + } + + namespace + exception_detail + { + struct + pretty_type_name + { + char const * name_not_zero_terminated_at_length; + std::size_t length; + + template + friend + std::basic_ostream & + operator<<(std::basic_ostream & os, pretty_type_name const & x) + { + return os.write(x.name_not_zero_terminated_at_length, x.length); + } + + template + friend + char * + to_zstr(char (&zstr)[S], pretty_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 + pretty_type_name + get_pretty_tag_type_name() + { + n::r parsed = n::p(); + return { parsed.name_not_zero_terminated_at_length, parsed.length }; + } } } diff --git a/include/boost/exception/detail/writer.hpp b/include/boost/exception/detail/writer.hpp new file mode 100644 index 0000000..d6f7893 --- /dev/null +++ b/include/boost/exception/detail/writer.hpp @@ -0,0 +1,114 @@ +//Copyright (c) 2006-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) + +#ifndef BOOST_EXCEPTION_DETAIL_WRITER_HPP_INCLUDED +#define BOOST_EXCEPTION_DETAIL_WRITER_HPP_INCLUDED + +#include +#include +#include + +namespace +boost + { + template class error_info; + + namespace + exception_serialization + { + struct writer_adl {}; + } + + namespace + exception_detail + { + template + struct first_arg; + + template + struct + first_arg + { + using type = A1; + }; + + template + struct + first_arg + { + using type = A1; + }; + + class + writer: + exception_serialization::writer_adl + { + writer(writer const &) = delete; + writer & operator=(writer const &) = delete; + + core::typeinfo const * type_; + void * w_; + + bool + dispatch_() + { + return false; + } + + template + bool + dispatch_(F1 && f1, Fn && ... fn) + { + using writer_type = typename std::decay::type::operator())>::type>::type; + if (writer_type * w = get()) + { + std::forward(f1)(*w); + return true; + } + return dispatch_(std::forward(fn)...); + } + + protected: + + template + explicit + writer(Writer * w) noexcept: + type_(&BOOST_CORE_TYPEID(Writer)), + w_(w) + { + } + + public: + + template + Writer * + get() noexcept + { + return *type_ == BOOST_CORE_TYPEID(Writer) ? static_cast(w_) : nullptr; + } + + template + bool + dispatch(Fn && ... fn) + { + return dispatch_(std::forward(fn)...); + } + }; + + template + struct + writer_adaptor: + writer + { + explicit + writer_adaptor(Writer & w) noexcept: + writer(&w) + { + } + }; + } + } + +#endif diff --git a/include/boost/exception/diagnostic_information.hpp b/include/boost/exception/diagnostic_information.hpp index b26c502..1465394 100644 --- a/include/boost/exception/diagnostic_information.hpp +++ b/include/boost/exception/diagnostic_information.hpp @@ -205,6 +205,65 @@ boost #endif return w; } + + namespace + exception_detail + { + template + void + write_diagnostic_information_to_impl_( boost::exception const * be, std::exception const * se, Writer & w ) + { + if( !be && !se ) + return; +#ifndef BOOST_NO_RTTI + if( !be ) + be=dynamic_cast(se); + if( !se ) + se=dynamic_cast(be); +#endif + if( be ) + { + if( char const * const * f=get_error_info(*be) ) + write_nested(w, *f, "throw_file"); + if( int const * l=get_error_info(*be) ) + write_nested(w, *l, "throw_line"); + if( char const * const * fn=get_error_info(*be) ) + write_nested(w, *fn, "throw_function"); + } +#ifndef BOOST_NO_RTTI + if( be || se ) + write_nested(w, core::demangle((be?(BOOST_EXCEPTION_DYNAMIC_TYPEID(*be)):(BOOST_EXCEPTION_DYNAMIC_TYPEID(*se))).type_->name()).c_str(), "dynamic_exception_type"); +#endif + if( se ) + if( char const * wh = se->what() ) + write_nested(w, wh, "std::exception::what"); + if( be ) + if( error_info_container * c = be->data_.get() ) + { + writer_adaptor wa(w); + c->write_to(wa); + } + } + } + + template + void + write_diagnostic_information_to( T const & e, Writer & w ) + { + exception_detail::write_diagnostic_information_to_impl_(exception_detail::get_boost_exception(&e),exception_detail::get_std_exception(&e),w); + } + +#ifndef BOOST_NO_EXCEPTIONS + template + void + write_current_exception_diagnostic_information_to( Writer & w ) + { + boost::exception const * be=current_exception_cast(); + std::exception const * se=current_exception_cast(); + if( be || se ) + exception_detail::write_diagnostic_information_to_impl_(be,se,w); + } +#endif } #if defined(_MSC_VER) && !defined(BOOST_EXCEPTION_ENABLE_WARNINGS) diff --git a/include/boost/exception/info.hpp b/include/boost/exception/info.hpp index f6a350f..b1c8249 100644 --- a/include/boost/exception/info.hpp +++ b/include/boost/exception/info.hpp @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include #ifndef BOOST_EXCEPTION_ENABLE_WARNINGS #if defined(__GNUC__) && __GNUC__*100+__GNUC_MINOR__>301 @@ -28,6 +30,36 @@ namespace boost { + namespace + exception_serialization + { + template + typename std::enable_if::value>::type + serialize(Writer &, T const &, char const *, Unused && ...) + { + } + + template + void + write(Writer & w, error_info const & e) + { + write(w, e.value()); + } + } + + namespace + exception_detail + { + template + void + serialize_(writer & w, error_info const & x) + { + using namespace boost::exception_serialization; + char buf[256]; + serialize(w, x.value(), to_zstr(buf, get_pretty_tag_type_name())); + } + } + template inline std::string @@ -53,6 +85,15 @@ boost return to_string_stub(*this); } + template + inline + void + error_info:: + write_to(exception_detail::writer & w) const + { + exception_detail::serialize_(w, *this); + } + namespace exception_detail { @@ -108,6 +149,16 @@ boost return diagnostic_info_str_.c_str(); } + void + write_to( writer & w ) const + { + for( error_info_map::const_iterator i=info_.begin(),end=info_.end(); i!=end; ++i ) + { + error_info_base const & x = *i->second; + x.write_to(w); + } + } + private: friend class boost::exception; diff --git a/include/boost/exception/serialization/nlohmann_writer.hpp b/include/boost/exception/serialization/nlohmann_writer.hpp new file mode 100644 index 0000000..3861449 --- /dev/null +++ b/include/boost/exception/serialization/nlohmann_writer.hpp @@ -0,0 +1,43 @@ +//Copyright (c) 2006-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) + +#ifndef BOOST_EXCEPTION_SERIALIZATION_NLOHMANN_WRITER_HPP_INCLUDED +#define BOOST_EXCEPTION_SERIALIZATION_NLOHMANN_WRITER_HPP_INCLUDED + +#include + +namespace +boost + { + namespace + exception_serialization + { + template + struct + nlohmann_writer + { + Json & j_; + + template + friend + auto + write(nlohmann_writer & w, T const & x) -> decltype(to_json(std::declval(), x)) + { + to_json(w.j_, x); + } + + template + friend + void + write_nested(nlohmann_writer & w, T const & x, char const * name) + { + nlohmann_writer nested{w.j_[name]}; + write(nested, x); + } + }; + } + } + +#endif diff --git a/scripts/download_nlohmann_json.py b/scripts/download_nlohmann_json.py new file mode 100644 index 0000000..7579587 --- /dev/null +++ b/scripts/download_nlohmann_json.py @@ -0,0 +1,33 @@ +""" + + 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) + + 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/test/Jamfile.v2 b/test/Jamfile.v2 index a0d29f2..bacdc83 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -41,6 +41,7 @@ run refcount_ptr_test.cpp ; run current_exception_cast_test.cpp : : : on ; run no_exceptions_test.cpp : : : off ; run errinfos_test.cpp : : : on ; +run nlohmann_test.cpp : : : on ; run exception_ptr_test.cpp/BOOST_ENABLE_NON_INTRUSIVE_EXCEPTION_PTR ../../thread/src/tss_null.cpp /boost/exception//boost_exception /boost/thread//boost_thread : : : multi on : non_intrusive_exception_ptr_test ; run exception_ptr_test.cpp ../../thread/src/tss_null.cpp /boost/thread//boost_thread : : : multi on ; run exception_ptr_test2.cpp ; diff --git a/test/diagnostic_information_test.cpp b/test/diagnostic_information_test.cpp index 1047129..f532ed5 100644 --- a/test/diagnostic_information_test.cpp +++ b/test/diagnostic_information_test.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #if BOOST_WORKAROUND(BOOST_CODEGEARC, BOOST_TESTED_AT(0x610)) struct test_tag1 {}; @@ -141,9 +142,11 @@ main() catch( error1 & x ) { - std::string di1=boost::diagnostic_information(x); + std::string di1 = diagnostic_information(x); + std::cout << __LINE__ << " ------------------\n" << di1; x << tagged_int1(2) << tagged_int2(2); std::string di2 = diagnostic_information(x); + std::cout << "\n" << di2 << std::endl; test1(di1,di2); } try @@ -155,9 +158,11 @@ main() catch( error1 & x ) { - std::string di1=boost::current_exception_diagnostic_information(); + std::string di1 = current_exception_diagnostic_information(); + std::cout << __LINE__ << " ------------------\n" << di1; x << tagged_int1(2) << tagged_int2(2); std::string di2 = current_exception_diagnostic_information(); + std::cout << "\n" << di2 << std::endl; test1(di1,di2); } try @@ -170,8 +175,10 @@ main() error2 & x ) { std::string di1 = diagnostic_information(x); + std::cout << __LINE__ << " ------------------\n" << di1; x << tagged_int1(2) << tagged_int2(2); std::string di2 = diagnostic_information(x); + std::cout << "\n" << di2 << std::endl; test2(di1,di2); } try @@ -184,23 +191,27 @@ main() error2 & x ) { std::string di1 = current_exception_diagnostic_information(); - BOOST_TEST(di1==boost::diagnostic_information_what(x)); + std::cout << __LINE__ << " ------------------\n" << di1; + BOOST_TEST(di1 == diagnostic_information_what(x)); x << tagged_int1(2) << tagged_int2(2); std::string di2 = current_exception_diagnostic_information(); - BOOST_TEST(di2==boost::diagnostic_information_what(x)); + std::cout << "\n" << di2 << std::endl; + BOOST_TEST(di2 == diagnostic_information_what(x)); test2(di1,di2); } try { error3 x; - std::string di=diagnostic_information(x); + std::string di = diagnostic_information(x); + std::cout << __LINE__ << " ------------------\n" << di << std::endl; test3(di); throw x; } catch( ... ) { - std::string di=current_exception_diagnostic_information(); + std::string di = current_exception_diagnostic_information(); + std::cout << __LINE__ << " ------------------\n" << di << std::endl; test3(di); } try @@ -210,9 +221,10 @@ main() catch( error4 & x ) { - std::string di1=boost::diagnostic_information(x); - std::string wh1=x.what(); - BOOST_TEST(wh1==di1); + std::string di1 = diagnostic_information(x); + std::cout << __LINE__ << " ------------------\n" << di1 << std::endl; + std::string wh1 = x.what(); + BOOST_TEST(wh1 == di1); } try { @@ -222,13 +234,15 @@ main() catch( error4 & x ) { - std::string di1=boost::diagnostic_information(x); - std::string wh1=x.what(); - BOOST_TEST(wh1==di1); + std::string di1 = diagnostic_information(x); + std::cout << __LINE__ << " ------------------\n" << di1; + std::string wh1 = x.what(); + BOOST_TEST(wh1 == di1); x << tagged_int1(2) << tagged_int2(2); std::string di2 = diagnostic_information(x); - std::string wh2=x.what(); - BOOST_TEST(wh2==di2); + std::cout << "\n" << di2 << std::endl; + std::string wh2 = x.what(); + BOOST_TEST(wh2 == di2); test4(di1,di2); } try @@ -239,13 +253,15 @@ main() catch( error4 & x ) { - std::string di1=boost::current_exception_diagnostic_information(); - std::string wh1=x.what(); - BOOST_TEST(wh1==di1); + std::string di1 = current_exception_diagnostic_information(); + std::cout << __LINE__ << " ------------------\n" << di1; + std::string wh1 = x.what(); + BOOST_TEST(wh1 == di1); x << tagged_int1(2) << tagged_int2(2); std::string di2 = current_exception_diagnostic_information(); - std::string wh2=x.what(); - BOOST_TEST(wh2==di2); + std::cout << "\n" << di2 << std::endl; + std::string wh2 = x.what(); + BOOST_TEST(wh2 == di2); test4(di1,di2); } return boost::report_errors(); diff --git a/test/nlohmann_test.cpp b/test/nlohmann_test.cpp new file mode 100644 index 0000000..f09d5e2 --- /dev/null +++ b/test/nlohmann_test.cpp @@ -0,0 +1,148 @@ +//Copyright (c) 2006-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 + +#if defined( BOOST_NO_EXCEPTIONS ) +# error This program requires exception handling. +#endif + +#include +#include +#include + +#include "nlohmann/json.hpp" +#include +#include +#include +#include + +using output_writer = boost::exception_serialization::nlohmann_writer; + +namespace boost { namespace exception_serialization { + +template +void +serialize(Handle & h, E const & e, char const * name) + { + h.dispatch( + [&](nlohmann_writer & w) { write_nested(w, e, name); }, + [&](nlohmann_writer & w) { write_nested(w, e, name); } + ); + } + +} } + +struct my_error_tag1; +struct my_error_tag2; + +typedef boost::error_info my_error1; +typedef boost::error_info my_error2; + +struct +my_info + { + int code; + char const * message; + + template + friend + void + to_json(Json & j, my_info const & e) + { + j["code"] = e.code; + j["message"] = e.message; + } + }; + +struct my_info_tag; +typedef boost::error_info my_error3; + +struct +test_exception: + virtual boost::exception, + virtual std::exception + { + char const * + what() const noexcept override + { + return "test_exception::what"; + } + }; + +void +check_output(nlohmann::ordered_json const & j, bool has_source_location) + { + if( has_source_location ) + { + BOOST_TEST(j.contains("throw_file")); + BOOST_TEST(j.contains("throw_line")); + BOOST_TEST(j.contains("throw_function")); + } +#ifndef BOOST_NO_RTTI + BOOST_TEST(j.contains("dynamic_exception_type")); +#endif + BOOST_TEST(j.contains("std::exception::what")); + BOOST_TEST_EQ(j["std::exception::what"].get(), "test_exception::what"); + + BOOST_TEST(j.contains("my_error_tag1")); + BOOST_TEST_EQ(j["my_error_tag1"].get(), 42); + + BOOST_TEST(j.contains("my_error_tag2")); + BOOST_TEST_EQ(j["my_error_tag2"].get(), "hello"); + + BOOST_TEST(j.contains("my_info_tag")); + auto const & mij = j["my_info_tag"]; + BOOST_TEST_EQ(mij["code"].get(), 1); + BOOST_TEST_EQ(mij["message"].get(), "error one"); + } + +int +main() + { + { + std::cout << "Testing write_diagnostic_information_to:\n"; + nlohmann::ordered_json j; + try + { + test_exception e; + e << + my_error1(42) << + my_error2("hello") << + my_error3({1, "error one"}); + BOOST_THROW_EXCEPTION(e); + } + catch( test_exception & e ) + { + output_writer w{j}; + boost::write_diagnostic_information_to(e, w); + } + std::cout << std::setw(2) << j << std::endl; + check_output(j, true); + } + + { + std::cout << "\nTesting write_current_exception_diagnostic_information_to:\n"; + nlohmann::ordered_json j; + try + { + test_exception e; + e << + my_error1(42) << + my_error2("hello") << + my_error3({1, "error one"}); + BOOST_THROW_EXCEPTION(e); + } + catch( ... ) + { + output_writer w{j}; + boost::write_current_exception_diagnostic_information_to(w); + } + std::cout << std::setw(2) << j << std::endl; + check_output(j, true); + } + + return boost::report_errors(); + }