diff --git a/include/boost/http_proto/file.hpp b/include/boost/http_proto/file.hpp index beea183b..c03df376 100644 --- a/include/boost/http_proto/file.hpp +++ b/include/boost/http_proto/file.hpp @@ -12,6 +12,7 @@ #define BOOST_HTTP_PROTO_FILE_HPP #include +#include #include #include #include @@ -27,19 +28,20 @@ namespace http_proto { file_source to enable streaming HTTP message bodies to and from files. - @par Example + @par Example 1 @code - file f; - system::error_code ec; - - f.open("example.zip", file_mode::write_new, ec); - if(ec.failed()) - throw system::system_error(ec); + file f("example.zip", file_mode::scan); + response.set_payload_size(f.size()); + serializer.start(response, std::move(f)); + @endcode - parser.set_body(std::move(f)); + @par Example 2 + @code + parser.set_body("example.zip", file_mode::write_new); @endcode @see + @ref file_mode, @ref file_sink, @ref file_source. */ @@ -63,9 +65,34 @@ class file using native_handle_type = impl_type::native_handle_type; /** Constructor. + + There is no open file initially. */ file() = default; + /** Constructor. + + Open a file at the given path with the specified mode. + + @par Exception Safety + Exception thrown if operation fails. + + @throw system_error + Operation fails. + + @param path The UTF-8 encoded path to the file. + + @param mode The file mode to use. + + @see + @ref file_mode, + @ref open. + */ + file(char const* path, file_mode mode) + { + open(path, mode); + } + /** Constructor. The moved-from object behaves as if default-constructed. @@ -80,6 +107,12 @@ class file operator=( file&& other) noexcept = default; + /** Destructor + + If the file is open it is first closed. + */ + ~file() = default; + /** Returns the native handle associated with the file. */ native_handle_type @@ -110,6 +143,9 @@ class file /** Close the file if open. + Note that, The descriptor is closed even if the function + reports an error. + @param ec Set to the error, if any occurred. */ void @@ -118,6 +154,26 @@ class file impl_.close(ec); } + /** Close the file if open. + + Note that, The descriptor is closed even if the function + reports an error. + + @par Exception Safety + Exception thrown if operation fails. + + @throw system_error + Operation fails. + */ + void + close() + { + system::error_code ec; + impl_.close(ec); + if(ec.failed()) + detail::throw_system_error(ec); + } + /** Open a file at the given path with the specified mode. @param path The UTF-8 encoded path to the file. @@ -125,6 +181,9 @@ class file @param mode The file mode to use. @param ec Set to the error, if any occurred. + + @see + @ref file_mode. */ void open(char const* path, file_mode mode, system::error_code& ec) @@ -132,6 +191,30 @@ class file impl_.open(path, mode, ec); } + /** Open a file at the given path with the specified mode. + + @param path The UTF-8 encoded path to the file. + + @param mode The file mode to use. + + @par Exception Safety + Exception thrown if operation fails. + + @throw system_error + Operation fails. + + @see + @ref file_mode. + */ + void + open(char const* path, file_mode mode) + { + system::error_code ec; + impl_.open(path, mode, ec); + if(ec.failed()) + detail::throw_system_error(ec); + } + /** Return the size of the open file in bytes. @param ec Set to the error, if any occurred. @@ -142,6 +225,24 @@ class file return impl_.size(ec); } + /** Return the size of the open file in bytes. + + @par Exception Safety + Exception thrown if operation fails. + + @throw system_error + Operation fails. + */ + std::uint64_t + size() const + { + system::error_code ec; + auto r = impl_.size(ec); + if(ec.failed()) + detail::throw_system_error(ec); + return r; + } + /** Return the current position in the file, in bytes from the beginning. @param ec Set to the error, if any occurred. @@ -152,6 +253,24 @@ class file return impl_.pos(ec); } + /** Return the current position in the file, in bytes from the beginning. + + @par Exception Safety + Exception thrown if operation fails. + + @throw system_error + Operation fails. + */ + std::uint64_t + pos() const + { + system::error_code ec; + auto r = impl_.pos(ec); + if(ec.failed()) + detail::throw_system_error(ec); + return r; + } + /** Set the current position in the file. @param offset The byte offset from the beginning of the file. @@ -164,6 +283,25 @@ class file impl_.seek(offset, ec); } + /** Set the current position in the file. + + @par Exception Safety + Exception thrown if operation fails. + + @throw system_error + Operation fails. + + @param offset The byte offset from the beginning of the file. + */ + void + seek(std::uint64_t offset) + { + system::error_code ec; + impl_.seek(offset, ec); + if(ec.failed()) + detail::throw_system_error(ec); + } + /** Read data from the file. @return The number of bytes read. Returns @@ -182,6 +320,31 @@ class file return impl_.read(buffer, n, ec); } + /** Read data from the file. + + @par Exception Safety + Exception thrown if operation fails. + + @throw system_error + Operation fails. + + @return The number of bytes read. Returns + 0 on end-of-file. + + @param buffer The buffer to store the read data. + + @param n The number of bytes to read. + */ + std::size_t + read(void* buffer, std::size_t n) + { + system::error_code ec; + auto r = impl_.read(buffer, n, ec); + if(ec.failed()) + detail::throw_system_error(ec); + return r; + } + /** Write data to the file. @return The number of bytes written. @@ -199,6 +362,30 @@ class file { return impl_.write(buffer, n, ec); } + + /** Write data to the file. + + @par Exception Safety + Exception thrown if operation fails. + + @throw system_error + Operation fails. + + @return The number of bytes written. + + @param buffer The buffer containing the data to write. + + @param n The number of bytes to write. + */ + std::size_t + write(void const* buffer, std::size_t n) + { + system::error_code ec; + auto r = impl_.write(buffer, n, ec); + if(ec.failed()) + detail::throw_system_error(ec); + return r; + } }; } // http_proto diff --git a/include/boost/http_proto/file_mode.hpp b/include/boost/http_proto/file_mode.hpp index b946cd10..266a7298 100644 --- a/include/boost/http_proto/file_mode.hpp +++ b/include/boost/http_proto/file_mode.hpp @@ -20,7 +20,7 @@ namespace http_proto { instances of the @ref file. @code - file_mode acesss sharing seeking file std mode + file_mode access sharing seeking file std mode -------------------------------------------------------------------------------------- read read-only shared random must exist "rb" scan read-only shared sequential must exist "rbS" diff --git a/include/boost/http_proto/file_sink.hpp b/include/boost/http_proto/file_sink.hpp index 2db830fe..574e70fc 100644 --- a/include/boost/http_proto/file_sink.hpp +++ b/include/boost/http_proto/file_sink.hpp @@ -27,15 +27,11 @@ namespace http_proto { @par Example @code - file f; - system::error_code ec; - f.open("example.zip", file_mode::write_new, ec); - if(ec.failed()) - throw system::system_error(ec); - parser.set_body(std::move(f)); + parser.set_body("example.zip", file_mode::write_new); @endcode @see + @ref file_source, @ref file, @ref parser, @ref sink. diff --git a/include/boost/http_proto/file_source.hpp b/include/boost/http_proto/file_source.hpp index 309989d6..88d98a21 100644 --- a/include/boost/http_proto/file_source.hpp +++ b/include/boost/http_proto/file_source.hpp @@ -27,15 +27,13 @@ namespace http_proto { @par Example @code - file f; - system::error_code ec; - f.open("example.zip", file_mode::scan, ec); - if(ec.failed()) - throw system::system_error(ec); + file f("example.zip", file_mode::scan); + response.set_payload_size(f.size()); serializer.start(response, std::move(f)); @endcode @see + @ref file_sink, @ref file, @ref serializer, @ref source. diff --git a/include/boost/http_proto/message_base.hpp b/include/boost/http_proto/message_base.hpp index c01b5041..c46c3fdc 100644 --- a/include/boost/http_proto/message_base.hpp +++ b/include/boost/http_proto/message_base.hpp @@ -19,7 +19,7 @@ namespace boost { namespace http_proto { -/** Mixin for modifiing common metadata +/** Mixin for modifing common metadata in HTTP request and response messages. This type is useful for modifying common diff --git a/include/boost/http_proto/parser.hpp b/include/boost/http_proto/parser.hpp index 230a9c5a..81ba94a9 100644 --- a/include/boost/http_proto/parser.hpp +++ b/include/boost/http_proto/parser.hpp @@ -462,13 +462,7 @@ class parser read_header(stream, pr); - http_proto::file file; - system::error_code ec; - file.open("./index.html", file_mode::write_new, ec); - if(ec.failed()) - return ec; - - pr.set_body(std::move(file)); + pr.set_body("example.zip", file_mode::write_new); read(stream, pr); @endcode @@ -494,9 +488,11 @@ class parser @param args Arguments to be passed to the `Sink` constructor. - @return A reference to the costructed Sink object. + @return A reference to the constructed Sink object. @see + @ref sink, + @ref file_sink, @ref parse. */ template< @@ -650,7 +646,7 @@ class parser BOOST_HTTP_PROTO_DECL parser( - rts::context&, + const rts::context&, detail::kind); BOOST_HTTP_PROTO_DECL @@ -697,7 +693,7 @@ class parser elastic, }; - rts::context& ctx_; + const rts::context& ctx_; detail::parser_service& svc_; detail::workspace ws_; diff --git a/include/boost/http_proto/request.hpp b/include/boost/http_proto/request.hpp index 4e4e8cf1..d9c43d93 100644 --- a/include/boost/http_proto/request.hpp +++ b/include/boost/http_proto/request.hpp @@ -15,7 +15,7 @@ namespace boost { namespace http_proto { -/** A modfiable container for HTTP requests. +/** A modifiable container for HTTP requests. This container owns a request, represented by a buffer which is managed by performing diff --git a/include/boost/http_proto/request_base.hpp b/include/boost/http_proto/request_base.hpp index 5f40b057..d7587fb8 100644 --- a/include/boost/http_proto/request_base.hpp +++ b/include/boost/http_proto/request_base.hpp @@ -18,7 +18,7 @@ namespace boost { namespace http_proto { -/** Mixin for modifiing HTTP requests. +/** Mixin for modifing HTTP requests. @see @ref message_base, diff --git a/include/boost/http_proto/request_parser.hpp b/include/boost/http_proto/request_parser.hpp index 2c2bf1e6..ff8296c0 100644 --- a/include/boost/http_proto/request_parser.hpp +++ b/include/boost/http_proto/request_parser.hpp @@ -84,7 +84,7 @@ class request_parser */ BOOST_HTTP_PROTO_DECL explicit - request_parser(rts::context& ctx); + request_parser(const rts::context& ctx); /// @copydoc parser ~request_parser() = default; diff --git a/include/boost/http_proto/response.hpp b/include/boost/http_proto/response.hpp index a10f3538..71dc6dd1 100644 --- a/include/boost/http_proto/response.hpp +++ b/include/boost/http_proto/response.hpp @@ -17,7 +17,7 @@ namespace boost { namespace http_proto { -/** A modfiable container for HTTP responses. +/** A modifiable container for HTTP responses. This container owns a response, represented by a buffer which is managed by performing diff --git a/include/boost/http_proto/response_base.hpp b/include/boost/http_proto/response_base.hpp index 9d0decda..8bc18211 100644 --- a/include/boost/http_proto/response_base.hpp +++ b/include/boost/http_proto/response_base.hpp @@ -20,7 +20,7 @@ namespace boost { namespace http_proto { -/** Mixin for modifiing HTTP responses. +/** Mixin for modifing HTTP responses. @see @ref message_base, diff --git a/include/boost/http_proto/response_parser.hpp b/include/boost/http_proto/response_parser.hpp index 3f25c179..db228362 100644 --- a/include/boost/http_proto/response_parser.hpp +++ b/include/boost/http_proto/response_parser.hpp @@ -84,7 +84,7 @@ class response_parser */ BOOST_HTTP_PROTO_DECL explicit - response_parser(rts::context& ctx); + response_parser(const rts::context& ctx); /// @copydoc parser::~parser ~response_parser() = default; diff --git a/include/boost/http_proto/serializer.hpp b/include/boost/http_proto/serializer.hpp index 6b4d3323..25a95f18 100644 --- a/include/boost/http_proto/serializer.hpp +++ b/include/boost/http_proto/serializer.hpp @@ -313,12 +313,9 @@ class serializer @par Example @code - file f; - system::error_code ec; - f.open("example.zip", file_mode::scan, ec); - if(ec.failed()) - throw system::system_error(ec); - serializer.start(response, std::move(file)); + file f("example.zip", file_mode::scan); + response.set_payload_size(f.size()); + serializer.start(response, std::move(f)); @endcode @par Preconditions @@ -352,7 +349,7 @@ class serializer @param args Arguments to be passed to the `Source` constructor. - @return A reference to the costructed Source object. + @return A reference to the constructed Source object. @see @ref source, @@ -910,7 +907,7 @@ class serializer::stream /** Commit data to the serializer. - Makes `n` bytes avialable to the serializer. + Makes `n` bytes available to the serializer. All buffer sequences previously obtained using @ref prepare are invalidated. diff --git a/include/boost/http_proto/sink.hpp b/include/boost/http_proto/sink.hpp index 8aba8cfb..01ba5bba 100644 --- a/include/boost/http_proto/sink.hpp +++ b/include/boost/http_proto/sink.hpp @@ -56,6 +56,20 @@ struct BOOST_SYMBOL_VISIBLE results& operator+=( results const& rv) noexcept; + + #ifdef BOOST_HTTP_PROTO_AGGREGATE_WORKAROUND + constexpr + results() = default; + + constexpr + results( + system::error_code ec_, + std::size_t bytes_) noexcept + : ec(ec_) + , bytes(bytes_) + { + } + #endif }; /** Consume data. @@ -124,7 +138,7 @@ struct BOOST_SYMBOL_VISIBLE @return The result of the operation. - @param b The buffer to cosume. + @param b The buffer to consume. The result must indicate that the buffer was consumed completely, or that an error occurred. diff --git a/include/boost/http_proto/source.hpp b/include/boost/http_proto/source.hpp index eb405e05..853ffebf 100644 --- a/include/boost/http_proto/source.hpp +++ b/include/boost/http_proto/source.hpp @@ -60,6 +60,22 @@ struct BOOST_SYMBOL_VISIBLE results& operator+=( results const& rv) noexcept; + + #ifdef BOOST_HTTP_PROTO_AGGREGATE_WORKAROUND + constexpr + results() = default; + + constexpr + results( + system::error_code ec_, + std::size_t bytes_, + bool finished_) noexcept + : ec(ec_) + , bytes(bytes_) + , finished(finished_) + { + } + #endif }; /** Produce data. @@ -116,7 +132,7 @@ struct BOOST_SYMBOL_VISIBLE not there is more data remaining in the source. The implementation must fill the - provided buffer completly, report + provided buffer completely, report an error or set `rv.finished` to true. @par Preconditions diff --git a/include/boost/http_proto/static_request.hpp b/include/boost/http_proto/static_request.hpp index d57e18d9..366f148f 100644 --- a/include/boost/http_proto/static_request.hpp +++ b/include/boost/http_proto/static_request.hpp @@ -15,7 +15,7 @@ namespace boost { namespace http_proto { -/** A modfiable static container for HTTP requests. +/** A modifiable static container for HTTP requests. This container owns a request, represented by an inline buffer with fixed capacity. diff --git a/include/boost/http_proto/static_response.hpp b/include/boost/http_proto/static_response.hpp index 3de907df..4efee020 100644 --- a/include/boost/http_proto/static_response.hpp +++ b/include/boost/http_proto/static_response.hpp @@ -15,7 +15,7 @@ namespace boost { namespace http_proto { -/** A modfiable static container for HTTP responses. +/** A modifiable static container for HTTP responses. This container owns a response, represented by an inline buffer with fixed capacity. diff --git a/include/boost/http_proto/status.hpp b/include/boost/http_proto/status.hpp index c53b4539..bff36fa1 100644 --- a/include/boost/http_proto/status.hpp +++ b/include/boost/http_proto/status.hpp @@ -25,7 +25,7 @@ enum class status : unsigned short This value indicates that the value for the status code is not in the list of commonly recognized status codes. - Callers interested in the exactl value should use the + Callers interested in the exact value should use the interface which provides the raw integer. */ unknown = 0, diff --git a/src/parser.cpp b/src/parser.cpp index f785045f..2ad6ce49 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -304,7 +304,7 @@ class zlib_filter public: zlib_filter( - rts::context& ctx, + const rts::context& ctx, http_proto::detail::workspace& ws, int window_bits) : zlib_filter_base(ws) @@ -354,7 +354,7 @@ class brotli_filter public: brotli_filter( - rts::context& ctx, + const rts::context& ctx, http_proto::detail::workspace&) : svc_(ctx.get_service()) { @@ -420,7 +420,7 @@ class parser_service std::size_t max_codec = 0; parser_service( - rts::context&, + const rts::context&, parser::config_base const& cfg_) : cfg(cfg_) { @@ -519,7 +519,7 @@ install_parser_service( //------------------------------------------------ parser:: -parser(rts::context& ctx, detail::kind k) +parser(const rts::context& ctx, detail::kind k) : ctx_(ctx) , svc_(ctx.get_service()) , ws_(svc_.space_needed) diff --git a/src/request_parser.cpp b/src/request_parser.cpp index 6333ede9..65e9a194 100644 --- a/src/request_parser.cpp +++ b/src/request_parser.cpp @@ -14,7 +14,7 @@ namespace http_proto { request_parser:: request_parser( - rts::context& ctx) + const rts::context& ctx) : parser( ctx, detail::kind::request) diff --git a/src/response_parser.cpp b/src/response_parser.cpp index 70772510..85cc126f 100644 --- a/src/response_parser.cpp +++ b/src/response_parser.cpp @@ -14,7 +14,7 @@ namespace http_proto { response_parser:: response_parser( - rts::context& ctx) + const rts::context& ctx) : parser( ctx, detail::kind::response) diff --git a/src/serializer.cpp b/src/serializer.cpp index 31bfabcc..1ae378a8 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -94,13 +94,13 @@ class zlib_filter public: zlib_filter( - const rts::context* ctx, + const rts::context& ctx, http_proto::detail::workspace& ws, int comp_level, int window_bits, int mem_level) : zlib_filter_base(ws) - , svc_(ctx->get_service()) + , svc_(ctx.get_service()) { system::error_code ec = static_cast(svc_.init2( strm_, @@ -160,11 +160,11 @@ class brotli_filter public: brotli_filter( - const rts::context* ctx, + const rts::context& ctx, http_proto::detail::workspace&, std::uint32_t comp_quality, std::uint32_t comp_window) - : svc_(ctx->get_service()) + : svc_(ctx.get_service()) { // TODO: use custom allocator state_ = svc_.create_instance(nullptr, nullptr, nullptr); @@ -242,7 +242,7 @@ class serializer_service std::size_t space_needed = 0; serializer_service( - rts::context&, + const rts::context&, serializer::config const& cfg_) : cfg(cfg_) { @@ -719,7 +719,7 @@ start_init( if(!svc_->cfg.apply_deflate_encoder) goto no_filter; filter_ = &ws_.emplace( - ctx_, + *ctx_, ws_, svc_->cfg.zlib_comp_level, svc_->cfg.zlib_window_bits, @@ -731,7 +731,7 @@ start_init( if(!svc_->cfg.apply_gzip_encoder) goto no_filter; filter_ = &ws_.emplace( - ctx_, + *ctx_, ws_, svc_->cfg.zlib_comp_level, svc_->cfg.zlib_window_bits + 16, @@ -743,7 +743,7 @@ start_init( if(!svc_->cfg.apply_brotli_encoder) goto no_filter; filter_ = &ws_.emplace( - ctx_, + *ctx_, ws_, svc_->cfg.brotli_comp_quality, svc_->cfg.brotli_comp_window); diff --git a/test/unit/Jamfile b/test/unit/Jamfile index bc0779ee..cedad320 100644 --- a/test/unit/Jamfile +++ b/test/unit/Jamfile @@ -39,7 +39,6 @@ local SOURCES = fields_view.cpp fields.cpp file_mode.cpp - file.cpp header_limits.cpp http_proto.cpp message_base.cpp @@ -81,6 +80,7 @@ for local f in $(SOURCES) # file tests local FILE_TESTS = + file.cpp file_sink.cpp file_source.cpp detail/file_posix.cpp diff --git a/test/unit/detail/file_posix.cpp b/test/unit/detail/file_posix.cpp index d77adfa0..5b9791f8 100644 --- a/test/unit/detail/file_posix.cpp +++ b/test/unit/detail/file_posix.cpp @@ -13,7 +13,6 @@ #if BOOST_HTTP_PROTO_USE_POSIX_FILE #include "file_test.hpp" -#include "test_suite.hpp" namespace boost { namespace http_proto { @@ -31,7 +30,7 @@ class file_posix_test TEST_SUITE( file_posix_test, - "boost.http_proto.file_posix"); + "boost.http_proto.detail.file_posix"); } // detail } // http_proto diff --git a/test/unit/detail/file_stdio.cpp b/test/unit/detail/file_stdio.cpp index fdf00819..9e09d672 100644 --- a/test/unit/detail/file_stdio.cpp +++ b/test/unit/detail/file_stdio.cpp @@ -11,7 +11,6 @@ #include #include "file_test.hpp" -#include "test_suite.hpp" namespace boost { namespace http_proto { @@ -33,7 +32,7 @@ class file_stdio_test TEST_SUITE( file_stdio_test, - "boost.http_proto.file_stdio"); + "boost.http_proto.detail.file_stdio"); } // detail } // http_proto diff --git a/test/unit/detail/file_win32.cpp b/test/unit/detail/file_win32.cpp index c8ff2469..7be66613 100644 --- a/test/unit/detail/file_win32.cpp +++ b/test/unit/detail/file_win32.cpp @@ -13,7 +13,6 @@ #if BOOST_HTTP_PROTO_USE_WIN32_FILE #include "file_test.hpp" -#include "test_suite.hpp" namespace boost { namespace http_proto { @@ -31,7 +30,7 @@ class file_win32_test TEST_SUITE( file_win32_test, - "boost.http_proto.file_win32"); + "boost.http_proto.detail.file_win32"); } // detail } // http_proto diff --git a/test/unit/file.cpp b/test/unit/file.cpp index c401bdba..703c66fd 100644 --- a/test/unit/file.cpp +++ b/test/unit/file.cpp @@ -1,5 +1,6 @@ // // Copyright (c) 2022 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2025 Mohammad Nejati // // 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) @@ -9,3 +10,60 @@ // Test that header file is self-contained. #include + +#include +#include "file_test.hpp" + +namespace boost { +namespace http_proto { + +struct file_test +{ + void + testThrowingOverloads() + { + // constructor + BOOST_TEST_THROWS( + file("missing.txt", file_mode::scan), + system::system_error); + + file f; + char buf[1]; + + BOOST_TEST_THROWS( + f.open("missing.txt", file_mode::scan), + system::system_error); + // BOOST_TEST_THROWS( + // f.close(), + // system::system_error); + BOOST_TEST_THROWS( + f.size(), + system::system_error); + BOOST_TEST_THROWS( + f.pos(), + system::system_error); + BOOST_TEST_THROWS( + f.seek(1), + system::system_error); + BOOST_TEST_THROWS( + f.read(buf, 1), + system::system_error); + BOOST_TEST_THROWS( + f.write(buf, 1), + system::system_error); + } + + void + run() + { + test_file(); + testThrowingOverloads(); + } +}; + +TEST_SUITE( + file_test, + "boost.http_proto.file"); + +} // http_proto +} // boost diff --git a/test/unit/detail/file_test.hpp b/test/unit/file_test.hpp similarity index 100% rename from test/unit/detail/file_test.hpp rename to test/unit/file_test.hpp diff --git a/test/unit/sink.cpp b/test/unit/sink.cpp index 98f0e2a5..b0e21f63 100644 --- a/test/unit/sink.cpp +++ b/test/unit/sink.cpp @@ -112,6 +112,12 @@ struct sink_test BOOST_TEST(! rv.ec.failed()); BOOST_TEST_EQ(rv.bytes, 0); } + + // sink::results aggregate workaround + { + sink::results rs{ {}, 0 }; + (void)rs; + } } void diff --git a/test/unit/source.cpp b/test/unit/source.cpp index e0feddad..449ce7b6 100644 --- a/test/unit/source.cpp +++ b/test/unit/source.cpp @@ -106,6 +106,12 @@ struct source_test BOOST_TEST(! rv.ec.failed()); BOOST_TEST_EQ(rv.bytes, 0); } + + // source::results aggregate workaround + { + source::results rs{ {}, 0, false }; + (void)rs; + } } void