From 058a4ea6845edd8947db85940bcfb756f385764f Mon Sep 17 00:00:00 2001 From: Jarle Aase Date: Sat, 18 Jan 2025 22:07:44 +0200 Subject: [PATCH] Fixing build errors --- CMakeLists.txt | 7 +- include/restc-cpp/boost_compatibility.h | 228 ++++++++++++++++++------ src/RequestImpl.cpp | 20 +-- 3 files changed, 177 insertions(+), 78 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b393e8..eb0bb6d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,8 @@ option(RESTC_CPP_AUTORUN_UNIT_TESTS "Run Unit Tests automatically after build" O option(RESTC_CPP_WITH_FUNCTIONALT_TESTS "Enable Functional Testing" ON) +option(RESTC_USE_LEGACY_BOOST_FIND "Use the old Boost find module" OFF) + set(GTEST_TAG "main" CACHE STRING "Gtest branch to use. Required on older Linux versions because newer gtest requure newer cmake!") set(LOGFAULT_TAG "master" CACHE STRING "Logfault branch to use. Required on older Linux versions because newer gtest requure newer cmake!") @@ -249,7 +251,10 @@ endif() if (NOT EMBEDDED_RESTC_CPP) - if(CMAKE_VERSION VERSION_GREATER "3.28") + if (RESTC_USE_LEGACY_BOOST_FIND) + unset(restc_cpp_boost_find_config) + message("Using legacy Boost find config") + elseif(CMAKE_VERSION VERSION_GREATER "3.28") set(restc_cpp_boost_find_config CONFIG) message("Using new Boost find config") endif() diff --git a/include/restc-cpp/boost_compatibility.h b/include/restc-cpp/boost_compatibility.h index f839746..dee72f0 100644 --- a/include/restc-cpp/boost_compatibility.h +++ b/include/restc-cpp/boost_compatibility.h @@ -4,31 +4,26 @@ #include #include -/* Boost keeps introducing breaking changes in the asio library. It seems like - * their window of backwards compatibility is about 5 years. +/** + * @file + * @brief Compatibility layer for handling breaking changes in Boost.Asio across versions. * - * This is a nightmare for library developers, if we want to maintain support - * for older versions of boost. I don't want the users of restc-cpp to be forced to - * refactor their code just because the boost library has been updated. Refactoring - * for this reason alone is just a waste of time and a huge cost for no gain. - * - * Here I try to handle the differences between the different versions of boost::asio - * in order to make it easier to maintain support for older versions of boost. - * - * So if restc-cpp is the only library that you are using that requires broken parts - * of boost, then you should be fine. - * - * I take full credits for whatever works well here. All blame goes to ChatGPT! ;) + * Boost frequently introduces breaking changes in its Asio library, with a backward + * compatibility window of about 5 years. This header helps maintain compatibility with + * multiple Boost versions, making it easier to support older versions without requiring + * extensive refactoring. */ #if BOOST_VERSION >= 107000 #include +/// Macro for catching exceptions in Boost Coroutine, ensuring required handling for `forced_unwind`. #define RESTC_CPP_IN_COROUTINE_CATCH_ALL \ catch (boost::coroutines::detail::forced_unwind const&) { \ throw; /* required for Boost Coroutine! */ \ } catch (...) #elif BOOST_VERSION >= 106000 #include +/// Macro for catching exceptions in Boost Coroutine, ensuring required handling for `forced_unwind`. #define RESTC_CPP_IN_COROUTINE_CATCH_ALL \ catch (boost::coroutines::detail::forced_unwind const&) { \ throw; /* required for Boost Coroutine! */ \ @@ -39,9 +34,9 @@ catch (...) #endif #if BOOST_VERSION >= 108100 -// They changed the function signature. In boost 1.86 it broke the build. +/// Macro for handling function signature changes in Boost 1.86 and later. #define RESTC_CPP_SPAWN_TRAILER \ -, boost::asio::detached + , boost::asio::detached #else #define RESTC_CPP_SPAWN_TRAILER #endif @@ -50,69 +45,184 @@ catch (...) namespace restc_cpp { #if BOOST_VERSION >= 107000 -using boost_const_buffer = boost::asio::const_buffer; -using boost_mutable_buffer = boost::asio::mutable_buffer; + /// Type alias for constant buffer in Boost 1.70 and later. + using boost_const_buffer = boost::asio::const_buffer; + /// Type alias for mutable buffer in Boost 1.70 and later. + using boost_mutable_buffer = boost::asio::mutable_buffer; #else -using boost_const_buffer = boost::asio::const_buffers_1; -using boost_mutable_buffer = boost::asio::mutable_buffers_1; + /// Type alias for constant buffer in Boost versions earlier than 1.70. + using boost_const_buffer = boost::asio::const_buffers_1; + /// Type alias for mutable buffer in Boost versions earlier than 1.70. + using boost_mutable_buffer = boost::asio::mutable_buffers_1; #endif - #if BOOST_VERSION >= 106600 -using boost_io_service = boost::asio::io_context; -using boost_work = boost::asio::executor_work_guard; - + /// Type alias for IO service in Boost 1.66 and later. + using boost_io_service = boost::asio::io_context; + /// Type alias for work guard in Boost 1.66 and later. + using boost_work = boost::asio::executor_work_guard; #else -using boost_io_service = boost::asio::io_service; -using boost_work = boost::asio::io_service::work; + /// Type alias for IO service in Boost versions earlier than 1.66. + using boost_io_service = boost::asio::io_service; + /// Type alias for work guard in Boost versions earlier than 1.66. + using boost_work = boost::asio::io_service::work; #endif -template -const char* boost_buffer_cast(const Buffer& buffer) { + /** + * @brief Extracts a const char pointer from a Boost buffer. + * + * @tparam Buffer The type of the buffer. + * @param buffer The buffer to extract the pointer from. + * @return A const char pointer to the data in the buffer. + */ + template + const char* boost_buffer_cast(const Buffer& buffer) { #if BOOST_VERSION >= 107000 - return static_cast(buffer.data()); + return static_cast(buffer.data()); #else - return boost::asio::buffer_cast(buffer); + return boost::asio::buffer_cast(buffer); #endif -} + } -template -std::size_t boost_buffer_size(const Buffer& buffer) { + /** + * @brief Retrieves the size of a Boost buffer. + * + * @tparam Buffer The type of the buffer. + * @param buffer The buffer to measure. + * @return The size of the buffer in bytes. + */ + template + std::size_t boost_buffer_size(const Buffer& buffer) { #if BOOST_VERSION >= 107000 - return buffer.size(); + return buffer.size(); #else - return boost::asio::buffer_size(buffer); + return boost::asio::buffer_size(buffer); #endif -} + } -template -void boost_dispatch(IOService& io_service, Handler&& handler) { + /** + * @brief Dispatches a handler to the IO service. + * + * @tparam IOService The type of the IO service. + * @tparam Handler The type of the handler. + * @param io_service The IO service to use. + * @param handler The handler to dispatch. + */ + template + void boost_dispatch(IOService& io_service, Handler&& handler) { #if BOOST_VERSION >= 106600 - // Determine if IOService is a pointer - if constexpr (std::is_pointer_v) { - io_service->get_executor().dispatch( - std::forward(handler), - std::allocator() // Default allocator - ); - } else { - io_service.get_executor().dispatch( - std::forward(handler), - std::allocator() // Default allocator - ); - } + if constexpr (std::is_pointer_v) { + io_service->get_executor().dispatch( + std::forward(handler), + std::allocator() // Default allocator + ); + } else { + io_service.get_executor().dispatch( + std::forward(handler), + std::allocator() // Default allocator + ); + } #else - if constexpr (std::is_pointer_v) { - io_service->dispatch(std::forward(handler)); - } else { - io_service.dispatch(std::forward(handler)); + if constexpr (std::is_pointer_v) { + io_service->dispatch(std::forward(handler)); + } else { + io_service.dispatch(std::forward(handler)); + } +#endif } + + /** + * @brief Wrapper for Boost resolver results for compatibility with older Boost versions. + * + * @tparam Iterator The type of the iterator used for results. + */ + template + class ResolverResultsWrapper { + public: + /** + * @brief Constructor. + * @param begin The beginning iterator of the results. + * @param end The end iterator of the results. + */ + explicit ResolverResultsWrapper(const Iterator& begin, const Iterator& end) + : begin_(begin), end_(end) {} + + /** + * @brief Returns the beginning iterator of the results. + * @return The beginning iterator. + */ + Iterator begin() const { return begin_; } + + /** + * @brief Returns the end iterator of the results. + * @return The end iterator. + */ + Iterator end() const { return end_; } + + private: + Iterator begin_; + Iterator end_; + }; + + template +#if BOOST_VERSION >= 106600 + /// Type alias for resolver results in Boost 1.66 and later. + using ResolverResults = boost::asio::ip::tcp::resolver::results_type; +#else + /// Type alias for resolver results in Boost versions earlier than 1.66. + using ResolverResults = ResolverResultsWrapper; #endif -} -boost::asio::ip::tcp::endpoint boost_create_endpoint(const std::string& ip_address, unsigned short port); -uint32_t boost_convert_ipv4_to_uint(const std::string& ip_address); -std::unique_ptr boost_make_work(boost_io_service& ioservice); + /** + * @brief Resolves a host and service to endpoints, with compatibility for multiple Boost versions. + * + * @tparam Resolver The type of the resolver. + * @tparam YieldContext The type of the yield context. + * @param resolver The resolver to use for the operation. + * @param host The host to resolve. + * @param service The service to resolve. + * @param yield The yield context for asynchronous operations. + * @return The resolver results, wrapped if necessary for older Boost versions. + */ + template + ResolverResults boost_resolve( + Resolver& resolver, + const std::string& host, + const std::string& service, + YieldContext yield) + { +#if BOOST_VERSION >= 107000 + return resolver.async_resolve(host, service, yield); +#elif BOOST_VERSION >= 106600 + return resolver.async_resolve(host, service, yield); +#else + boost::asio::ip::tcp::resolver::query query(host, service); + auto it = resolver.async_resolve(query, yield); + auto end = boost::asio::ip::tcp::resolver::iterator(); + return ResolverResultsWrapper(it, end); +#endif + } -} // ns + /** + * @brief Creates a Boost endpoint from an IP address and port. + * @param ip_address The IP address as a string. + * @param port The port number. + * @return A Boost TCP endpoint. + */ + boost::asio::ip::tcp::endpoint boost_create_endpoint(const std::string& ip_address, unsigned short port); + /** + * @brief Converts an IPv4 address from string format to a 32-bit unsigned integer. + * @param ip_address The IPv4 address as a string. + * @return The IPv4 address as a 32-bit unsigned integer. + */ + uint32_t boost_convert_ipv4_to_uint(const std::string& ip_address); + + /** + * @brief Creates a work guard for the given IO service. + * @param ioservice The IO service to manage. + * @return A unique pointer to the work guard. + */ + std::unique_ptr boost_make_work(boost_io_service& ioservice); +} // namespace restc_cpp diff --git a/src/RequestImpl.cpp b/src/RequestImpl.cpp index b31dd98..ebe1c40 100644 --- a/src/RequestImpl.cpp +++ b/src/RequestImpl.cpp @@ -631,17 +631,9 @@ class RequestImpl : public Request { return {protocol, static_cast(port_num)}; } - //boost::asio::ip::tcp::resolver::query const q{host, port}; boost::asio::ip::tcp::resolver resolver(owner_.GetIoService()); + auto results = boost_resolve(resolver, host, port, ctx.GetYield()); -#if BOOST_VERSION >= 107000 - // For Boost 1.70.0 and later - auto results = resolver.async_resolve(host, port, ctx.GetYield()); -#else - // For Boost versions earlier than 1.70.0 - boost::asio::ip::tcp::resolver::query query(host, port); - auto results = resolver.async_resolve(query, ctx.GetYield()); -#endif for (auto it = results.begin(); it != results.end(); ++it) { const auto endpoint = it->endpoint(); RESTC_CPP_LOG_TRACE_("ep=" << endpoint << ", protocol=" << endpoint.protocol().protocol()); @@ -697,15 +689,7 @@ class RequestImpl : public Request { const auto [host, service] = GetRequestEndpoint(); RESTC_CPP_LOG_TRACE_("Resolving " << host << ":" << service); - -#if BOOST_VERSION >= 107000 - // For Boost 1.70.0 and later - auto results = resolver.async_resolve(host, service, ctx.GetYield()); -#else - // For Boost versions earlier than 1.70.0 - boost::asio::ip::tcp::resolver::query query(host, service); - auto results = resolver.async_resolve(query, yield); -#endif + auto results = boost_resolve(resolver, host, service, ctx.GetYield()); for (auto it = results.begin(); it != results.end(); ++it) { const auto endpoint = it->endpoint();