diff --git a/lib/base/visit.hpp b/lib/base/visit.hpp new file mode 100644 index 00000000000..b8dc9a05a33 --- /dev/null +++ b/lib/base/visit.hpp @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: 2026 Icinga GmbH +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +namespace icinga { + +namespace detail{ +template +struct Overloaded : Ts... { using Ts::operator()...; }; +template +Overloaded(Ts...) -> Overloaded...>; +} // namespace detail + +/** + * A more convenient wrapper around std::visit. + * + * Instead of allowing to dispatch a single functor on an arbitrary number of variant objects, + * this allows to dispatch any number of overloads on a single variant object, which more closely + * fits the way this project uses @c std::variant. + * + * @param variant The variant to dispatch on + * @param overloads A set of overloads to handle each member of the variant + * + * @return The value std::visit() returns. + */ +template +auto Visit(Variant&& variant, Overloads&&... overloads) +{ + return std::visit(detail::Overloaded{std::forward(overloads)...}, std::forward(variant)); +} + +} // namespace icinga diff --git a/lib/perfdata/perfdatawriterconnection.cpp b/lib/perfdata/perfdatawriterconnection.cpp index f8807c9ce03..0061fd1e42f 100644 --- a/lib/perfdata/perfdatawriterconnection.cpp +++ b/lib/perfdata/perfdatawriterconnection.cpp @@ -3,6 +3,7 @@ #include "perfdata/perfdatawriterconnection.hpp" #include "base/tcpsocket.hpp" +#include "base/visit.hpp" #include #include #include @@ -75,14 +76,11 @@ void PerfdataWriterConnection::Disconnect() * result in exceptions thrown by the yield_context, even if its already queued for * completion. */ - std::visit( - [](const auto& stream) { - if (stream->lowest_layer().is_open()) { - stream->lowest_layer().cancel(); - } - }, - m_Stream - ); + Visit(m_Stream, [](const auto& stream) { + if (stream->lowest_layer().is_open()) { + stream->lowest_layer().cancel(); + } + }); m_ReconnectTimer.cancel(); Disconnect(std::move(yc)); @@ -128,27 +126,26 @@ void PerfdataWriterConnection::EnsureConnected(const boost::asio::yield_context& return; } - std::visit( - [&](auto& stream) { + Visit( + m_Stream, + [&](const Shared::Ptr& stream) { ::Connect(stream->lowest_layer(), m_Host, m_Port, yc); }, + [&](const Shared::Ptr& stream) { ::Connect(stream->lowest_layer(), m_Host, m_Port, yc); - if constexpr (std::is_same_v, Shared::Ptr>) { - using type = boost::asio::ssl::stream_base::handshake_type; + using type = boost::asio::ssl::stream_base::handshake_type; - stream->next_layer().async_handshake(type::client, yc); + stream->next_layer().async_handshake(type::client, yc); - if (m_VerifyPeerCertificate) { - if (!stream->next_layer().IsVerifyOK()) { - BOOST_THROW_EXCEPTION( - std::runtime_error{ - "TLS certificate validation failed: " + stream->next_layer().GetVerifyError() - } - ); - } + if (m_VerifyPeerCertificate) { + if (!stream->next_layer().IsVerifyOK()) { + BOOST_THROW_EXCEPTION( + std::runtime_error{ + "TLS certificate validation failed: " + stream->next_layer().GetVerifyError() + } + ); } } - }, - m_Stream + } ); m_Connected = true; @@ -160,16 +157,13 @@ void PerfdataWriterConnection::Disconnect(boost::asio::yield_context yc) return; } - std::visit( - [&](auto& stream) { - if constexpr (std::is_same_v, Shared::Ptr>) { - stream->GracefulDisconnect(m_Strand, yc); - } else { - stream->lowest_layer().shutdown(boost::asio::socket_base::shutdown_both); - stream->lowest_layer().close(); - } - }, - m_Stream + Visit( + m_Stream, + [&](Shared::Ptr& stream) { stream->GracefulDisconnect(m_Strand, yc); }, + [&](Shared::Ptr& stream) { + stream->lowest_layer().shutdown(boost::asio::socket_base::shutdown_both); + stream->lowest_layer().close(); + } ); m_Stream = MakeStream(); @@ -177,29 +171,23 @@ void PerfdataWriterConnection::Disconnect(boost::asio::yield_context yc) void PerfdataWriterConnection::WriteMessage(boost::asio::const_buffer buf, const boost::asio::yield_context& yc) { - std::visit( - [&](auto& stream) { - boost::asio::async_write(*stream, buf, yc); - stream->async_flush(yc); - }, - m_Stream - ); + Visit(m_Stream, [&](auto& stream) { + boost::asio::async_write(*stream, buf, yc); + stream->async_flush(yc); + }); } HttpResponse PerfdataWriterConnection::WriteMessage(const HttpRequest& request, const boost::asio::yield_context& yc) { boost::beast::http::response response; - std::visit( - [&](auto& stream) { - boost::beast::http::request_serializer sr{request}; - boost::beast::http::async_write(*stream, sr, yc); - stream->async_flush(yc); - - boost::beast::flat_buffer buf; - boost::beast::http::async_read(*stream, buf, response, yc); - }, - m_Stream - ); + Visit(m_Stream, [&](auto& stream) { + boost::beast::http::request_serializer sr{request}; + boost::beast::http::async_write(*stream, sr, yc); + stream->async_flush(yc); + + boost::beast::flat_buffer buf; + boost::beast::http::async_read(*stream, buf, response, yc); + }); if (!response.keep_alive()) { Disconnect(yc);