💥 Redo all higher order template functions

This does what we did for a few functions in the last few commits for
every function. We now use either the `std::invocable` concept or our
own `invocable_returning` concept wherever possible to make sure we pass
function types to these template functions, since constraint errors are
a lot more readable than template deduction errors. And instead of
having to specify the return type as a template argument, we now just
use `std::invoke_result_t<F>` instead. The VST3 message handling
functions are still using the good old `typename F` since those are
overloaded polymorphic functions. This was also a good moment to modify
`AdHocSocketHandler::send()` to allow functions returning void (this got
rid of an old fixme where we had to return some dummy value from a
function instead of just not returning anything).
This commit is contained in:
Robbert van der Helm
2021-05-20 00:53:48 +02:00
parent 6c58f4e305
commit e4ca520b64
11 changed files with 91 additions and 93 deletions
+37 -30
View File
@@ -435,8 +435,8 @@ class SocketHandler {
* @see read_object
* @see SocketHandler::receive_single
*/
template <typename T, typename F>
void receive_multi(F callback) {
template <typename T, std::invocable<T, std::vector<uint8_t>&> F>
void receive_multi(F&& callback) {
std::vector<uint8_t> buffer{};
while (true) {
try {
@@ -566,13 +566,17 @@ class AdHocSocketHandler {
* @param callback A function that will be called with a reference to a
* socket. This is either the primary `socket`, or a new ad hock socket if
* this function is currently being called from another thread.
*
* @tparam T The return value of F.
* @tparam F A function in the form of
* `T(boost::asio::local::stream_protocol::socket&)`.
*/
template <typename T, typename F>
T send(F callback) {
template <std::invocable<boost::asio::local::stream_protocol::socket&> F>
std::invoke_result_t<F, boost::asio::local::stream_protocol::socket&> send(
F&& callback) {
// A bit of template and constexpr nastiness to allow us to either
// return a value from the callback (for when writing the response to a
// new object) or to return void (when we deserialize into an existing
// object)
constexpr bool returns_void = std::is_void_v<std::invoke_result_t<
F, boost::asio::local::stream_protocol::socket&>>;
// XXX: Maybe at some point we should benchmark how often this
// ad hoc socket spawning mechanism gets used. If some hosts
// for instance consistently and repeatedly trigger this then
@@ -582,10 +586,15 @@ class AdHocSocketHandler {
// This was used to always block when sending the first message,
// because the other side may not be listening for additional
// connections yet
auto result = callback(socket);
sent_first_event = true;
if constexpr (returns_void) {
callback(socket);
sent_first_event = true;
} else {
auto result = callback(socket);
sent_first_event = true;
return result;
return result;
}
} else {
try {
boost::asio::local::stream_protocol::socket secondary_socket(
@@ -609,10 +618,15 @@ class AdHocSocketHandler {
if (!sent_first_event) {
std::lock_guard lock(write_mutex);
auto result = callback(socket);
sent_first_event = true;
if constexpr (returns_void) {
callback(socket);
sent_first_event = true;
} else {
auto result = callback(socket);
sent_first_event = true;
return result;
return result;
}
} else {
// Rethrow the exception if the sockets we're not
// handling the specific case described above
@@ -636,15 +650,12 @@ class AdHocSocketHandler {
* an incoming connection on a secondary socket. This would often do the
* same thing as `primary_callback`, but secondary sockets may need some
* different handling.
*
* @tparam F A function type in the form of
* `void(boost::asio::local::stream_protocol::socket&)`.
* @tparam G The same as `F`.
*/
template <typename F, typename G>
template <std::invocable<boost::asio::local::stream_protocol::socket&> F,
std::invocable<boost::asio::local::stream_protocol::socket&> G>
void receive_multi(std::optional<std::reference_wrapper<Logger>> logger,
F primary_callback,
G secondary_callback) {
F&& primary_callback,
G&& secondary_callback) {
// We use this flag to have the `close()` function wait for the this
// function to exit, to prevent use-after-frees when destroying this
// object from another thread.
@@ -726,10 +737,10 @@ class AdHocSocketHandler {
*
* @overload
*/
template <typename F>
template <std::invocable<boost::asio::local::stream_protocol::socket&> F>
void receive_multi(std::optional<std::reference_wrapper<Logger>> logger,
F callback) {
receive_multi(logger, callback, callback);
F&& callback) {
receive_multi(logger, callback, std::forward<F>(callback));
}
private:
@@ -742,16 +753,12 @@ class AdHocSocketHandler {
* @param logger A logger instance for logging connection errors. This
* should only be passed on the plugin side.
* @param callback A function that handles the new socket connection.
*
* @tparam F A function in the form
* `void(boost::asio::local::stream_protocol::socket)` to handle a new
* incoming connection.
*/
template <typename F>
template <std::invocable<boost::asio::local::stream_protocol::socket> F>
void accept_requests(
boost::asio::local::stream_protocol::acceptor& acceptor,
std::optional<std::reference_wrapper<Logger>> logger,
F callback) {
F&& callback) {
acceptor.async_accept(
[&, logger, callback](
const boost::system::error_code& error,
+7 -12
View File
@@ -20,6 +20,7 @@
#include "../logging/vst2.h"
#include "../serialization/vst2.h"
#include "../utils.h"
#include "common.h"
/**
@@ -184,7 +185,7 @@ class EventHandler : public AdHocSocketHandler<Thread> {
// messages from arriving out of order. `AdHocSocketHandler::send()`
// will either use a long-living primary socket, or if that's currently
// in use it will spawn a new socket for us.
EventResult response = this->template send<EventResult>(
const EventResult response = this->send(
[&](boost::asio::local::stream_protocol::socket& socket) {
write_object(socket, event);
return read_object<EventResult>(socket);
@@ -219,16 +220,12 @@ class EventHandler : public AdHocSocketHandler<Thread> {
* @param callback The function used to generate a response out of an event.
* See the definition of `F` for more information.
*
* @tparam F A function type in the form of `EventResponse(Event, bool)`.
* The boolean flag is `true` when this event was received on the main
* socket, and `false` otherwise.
*
* @relates EventHandler::send_event
* @relates passthrough_event
*/
template <typename F>
template <invocable_returning<EventResult, Event&, bool> F>
void receive_events(std::optional<std::pair<Vst2Logger&, bool>> logging,
F callback) {
F&& callback) {
// Reading, processing, and writing back event data from the sockets
// works in the same way regardless of which socket we're using
const auto process_event =
@@ -384,16 +381,14 @@ class Vst2Sockets : public Sockets {
* @param callback The function to call with the arguments received from the
* socket, either `AEffect::dispatcher()` or `audioMasterCallback()`.
*
* @tparam F A function with the same signature as `AEffect::dispatcher` or
* `audioMasterCallback`.
*
* @return The result of the operation. If necessary the `DataConverter` will
* unmarshall the payload again and write it back.
*
* @relates EventHandler::receive_events
*/
template <typename F>
EventResult passthrough_event(AEffect* plugin, F callback, Event& event) {
template <
invocable_returning<intptr_t, AEffect*, int, int, intptr_t, void*, float> F>
EventResult passthrough_event(AEffect* plugin, F&& callback, Event& event) {
// This buffer is used to write strings and small objects to. We'll
// initialize the beginning with null values to both prevent it from being
// read as some arbitrary C-style string, and to make sure that
+11 -12
View File
@@ -138,15 +138,10 @@ class Vst3MessageHandler : public AdHocSocketHandler<Thread> {
// messages from arriving out of order. `AdHocSocketHandler::send()`
// will either use a long-living primary socket, or if that's currently
// in use it will spawn a new socket for us.
this->template send<std::monostate>(
[&](boost::asio::local::stream_protocol::socket& socket) {
write_object(socket, Request(object), buffer);
read_object<TResponse>(socket, response_object, buffer);
// FIXME: We have to return something here, and ML was not yet
// invented when they came up with C++ so void is not
// valid here
return std::monostate{};
});
this->send([&](boost::asio::local::stream_protocol::socket& socket) {
write_object(socket, Request(object), buffer);
read_object<TResponse>(socket, response_object, buffer);
});
if (should_log_response) {
auto [logger, is_host_vst] = *logging;
@@ -205,7 +200,7 @@ class Vst3MessageHandler : public AdHocSocketHandler<Thread> {
*/
template <bool persistent_buffers = false, typename F>
void receive_messages(std::optional<std::pair<Vst3Logger&, bool>> logging,
F callback) {
F&& callback) {
// Reading, processing, and writing back the response for the requests
// we receive works in the same way regardless of which socket we're
// using
@@ -377,12 +372,15 @@ class Vst3Sockets : public Sockets {
* Wine plugin host is even listening on it.
* @param cb An overloaded function that can take every type `T` in the
* `AudioProcessorRequest` variant and then returns `T::Response`.
*
* @tparam F A function type in the form of `T::Response(T)` for every `T`
* in `AudioProcessorRequest::Payload`.
*/
template <typename F>
void add_audio_processor_and_listen(
size_t instance_id,
std::promise<void>& socket_listening_latch,
F&& cb) {
F&& callback) {
{
std::lock_guard lock(audio_processor_sockets_mutex);
audio_processor_sockets.try_emplace(
@@ -400,7 +398,8 @@ class Vst3Sockets : public Sockets {
// receiving buffers for all calls. This slightly reduces the amount of
// allocations in the audio processing loop.
audio_processor_sockets.at(instance_id)
.template receive_messages<true>(std::nullopt, std::forward<F>(cb));
.template receive_messages<true>(std::nullopt,
std::forward<F>(callback));
}
/**