Skip to content

Commit

Permalink
Fixing build errors
Browse files Browse the repository at this point in the history
  • Loading branch information
jgaa committed Jan 18, 2025
1 parent 8aa13de commit 058a4ea
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 78 deletions.
7 changes: 6 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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!")

Expand Down Expand Up @@ -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()
Expand Down
228 changes: 169 additions & 59 deletions include/restc-cpp/boost_compatibility.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,26 @@
#include <boost/version.hpp>
#include <boost/asio/ip/address_v4.hpp>

/* 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 <boost/coroutine/exceptions.hpp>
/// 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 <boost/coroutine2/detail/forced_unwind.hpp>
/// 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! */ \
Expand All @@ -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
Expand All @@ -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<boost::asio::io_context::executor_type>;

/// 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<boost::asio::io_context::executor_type>;
#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 <typename Buffer>
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 <typename Buffer>
const char* boost_buffer_cast(const Buffer& buffer) {
#if BOOST_VERSION >= 107000
return static_cast<const char*>(buffer.data());
return static_cast<const char*>(buffer.data());
#else
return boost::asio::buffer_cast<const char*>(buffer);
return boost::asio::buffer_cast<const char*>(buffer);
#endif
}
}

template <typename Buffer>
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 <typename Buffer>
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 <typename IOService, typename Handler>
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 <typename IOService, typename Handler>
void boost_dispatch(IOService& io_service, Handler&& handler) {
#if BOOST_VERSION >= 106600
// Determine if IOService is a pointer
if constexpr (std::is_pointer_v<IOService>) {
io_service->get_executor().dispatch(
std::forward<Handler>(handler),
std::allocator<void>() // Default allocator
);
} else {
io_service.get_executor().dispatch(
std::forward<Handler>(handler),
std::allocator<void>() // Default allocator
);
}
if constexpr (std::is_pointer_v<IOService>) {
io_service->get_executor().dispatch(
std::forward<Handler>(handler),
std::allocator<void>() // Default allocator
);
} else {
io_service.get_executor().dispatch(
std::forward<Handler>(handler),
std::allocator<void>() // Default allocator
);
}
#else
if constexpr (std::is_pointer_v<IOService>) {
io_service->dispatch(std::forward<Handler>(handler));
} else {
io_service.dispatch(std::forward<Handler>(handler));
if constexpr (std::is_pointer_v<IOService>) {
io_service->dispatch(std::forward<Handler>(handler));
} else {
io_service.dispatch(std::forward<Handler>(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 <typename Iterator>
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 <typename Resolver, typename YieldContext>
#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<boost::asio::ip::tcp::resolver::iterator>;
#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_work> 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 <typename Resolver, typename YieldContext>
ResolverResults<Resolver, YieldContext> 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_work> boost_make_work(boost_io_service& ioservice);

} // namespace restc_cpp
20 changes: 2 additions & 18 deletions src/RequestImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -631,17 +631,9 @@ class RequestImpl : public Request {
return {protocol, static_cast<uint16_t>(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());
Expand Down Expand Up @@ -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();
Expand Down

0 comments on commit 058a4ea

Please sign in to comment.