From d5374e4540c290e3c3fbecc84a37bf3fe7fffe94 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Sun, 6 Dec 2020 12:21:37 +0100 Subject: [PATCH] :boom: Rework Vst3MessageHandler - Now allows direct deserialization into existing objects. This will be necessary for our VST3 implementations since the interface instances we'll deserialize into will not be trivially constructable because they have to be able to do callbacks. - `ControlResponse` and `CallbackResponse` were dropped. These response enums are not necessary because of the `T::Response` associated type and returning the types directly makes the direct deserialization possible. --- src/common/communication/vst3.h | 53 +++++++++++++++------------------ src/common/serialization/vst3.h | 37 ++--------------------- src/plugin/bridges/vst3.cpp | 2 +- 3 files changed, 27 insertions(+), 65 deletions(-) diff --git a/src/common/communication/vst3.h b/src/common/communication/vst3.h index 9643ab13..d60c199f 100644 --- a/src/common/communication/vst3.h +++ b/src/common/communication/vst3.h @@ -16,7 +16,7 @@ #pragma once -#include +#include #include "../logging/vst3.h" #include "../serialization/vst3.h" @@ -38,10 +38,8 @@ * @tparam Thread The thread implementation to use. On the Linux side this * should be `std::jthread` and on the Wine side this should be `Win32Thread`. * @tparam Request Either `ControlRequest` or `CallbackRequest`. - * @tparam Response Either `ControlResponse` or `CallbackResponse`, depending on - * the type of `Request`. */ -template +template class Vst3MessageHandler : public AdHocSocketHandler { public: /** @@ -86,7 +84,9 @@ class Vst3MessageHandler : public AdHocSocketHandler { const T& object, std::optional> logging) { typename T::Response response_object; - return send_message(object, response_object, logging); + send_message(object, response_object, logging); + + return response_object; } /** @@ -112,23 +112,20 @@ class Vst3MessageHandler : public AdHocSocketHandler { // 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. - const TResponse response = this->template send( + this->template send( [&](boost::asio::local::stream_protocol::socket& socket) { write_object(socket, Request(object)); - const auto response = - read_object(socket, response_object); - - // If the other side handled the request correctly, the Response - // variant should now contain an object of type `T::Response` - return std::get(response); + read_object(socket, response_object); + // 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{}; }); if (logging) { auto [logger, is_host_vst] = *logging; - logger.log_response(!is_host_vst, response); + logger.log_response(!is_host_vst, response_object); } - - return response; } /** @@ -137,8 +134,8 @@ class Vst3MessageHandler : public AdHocSocketHandler { * `socket`. * * The specified function receives a `Request` variant object containing an - * object of type `T`, and it should return the corresponding `Response` of - * type `T::Response`. + * object of type `T`, and it should then return the corresponding + * `T::Response`. * * @param logging A pair containing a logger instance and whether or not * this is for sending host -> plugin control messages. If set to false, @@ -148,7 +145,11 @@ class Vst3MessageHandler : public AdHocSocketHandler { * @param callback The function used to generate a response out of the * request. See the definition of `F` for more information. * - * @tparam F A function type in the form of `Reponse(Request)`. + * @tparam F A function type in the form of `T::Response(Request(T))`. C++ + * doesn't have syntax for this, but the function receives a `Request` + * variant containing a `T`, and the function should return a `T::Reponse` + * object. This way we can directly deserialize into a `T::Reponse` on the + * side that called `send_object(T)`. * * @relates Vst3MessageHandler::send_event */ @@ -170,14 +171,10 @@ class Vst3MessageHandler : public AdHocSocketHandler { request); } - Response response = callback(request); + const auto response = callback(request); if (logging) { - std::visit( - [&](const auto& object) { - auto [logger, is_host_vst] = *logging; - logger.log_response(!is_host_vst, object); - }, - response); + auto [logger, is_host_vst] = *logging; + logger.log_response(!is_host_vst, response); } write_object(socket, response); @@ -260,14 +257,12 @@ class Vst3Sockets : public Sockets { * This will be listened on by the Wine plugin host when it calls * `receive_multi()`. */ - Vst3MessageHandler - host_vst_control; + Vst3MessageHandler host_vst_control; /** * For sending callbacks from the plugin back to the host. After we have a * better idea of what our communication model looks like we'll probably * want to provide an abstraction similar to `EventHandler`. */ - Vst3MessageHandler - vst_host_callback; + Vst3MessageHandler vst_host_callback; }; diff --git a/src/common/serialization/vst3.h b/src/common/serialization/vst3.h index 27dfb589..3dacf9ab 100644 --- a/src/common/serialization/vst3.h +++ b/src/common/serialization/vst3.h @@ -48,10 +48,7 @@ struct WantsConfiguration { /** * When we send a control message from the plugin to the Wine VST host, this * encodes the information we request or the operation we want to perform. A - * request of type `T` should send back a `CallbackResponse` containing - * `T::Reponse`. - * - * @relates ControlResponse + * request of type `ControlRequest(T)` should send back a `T::Reponse`. */ using ControlRequest = std::variant<>; @@ -60,26 +57,10 @@ void serialize(S& s, ControlRequest& payload) { s.ext(payload, bitsery::ext::StdVariant{}); } -/** - * A response to a control message. Tee `T::Reponse` for the correct type to - * return here. - * - * @relates ControlRrequest - */ -using ControlResponse = std::variant<>; - -// TODO: Uncomment when this is no longer empty -// template -// void serialize(S& s, ControlResponse& payload) { -// s.ext(payload, bitsery::ext::StdVariant{}); -// } - /** * When we do a callback from the Wine VST host to the plugin, this encodes the * information we want or the operation we want to perform. A request of type - * `T` should send back a `CallbackResponse` containing `T::Reponse`. - * - * @relates CallbackResponse + * `CallbackRequest(T)` should send back a `T::Reponse`. */ using CallbackRequest = std::variant; @@ -87,17 +68,3 @@ template void serialize(S& s, CallbackRequest& payload) { s.ext(payload, bitsery::ext::StdVariant{[](S&, WantsConfiguration&) {}}); } - -/** - * A response to a callback. Tee `T::Reponse` for the correct type to return - * here. - * - * @relates CallbackRrequest - */ -using CallbackResponse = std::variant; - -template -void serialize(S& s, CallbackResponse& payload) { - s.ext(payload, bitsery::ext::StdVariant{ - [](S& s, Configuration& config) { s.object(config); }}); -} diff --git a/src/plugin/bridges/vst3.cpp b/src/plugin/bridges/vst3.cpp index c8755d55..0d6165bb 100644 --- a/src/plugin/bridges/vst3.cpp +++ b/src/plugin/bridges/vst3.cpp @@ -42,7 +42,7 @@ Vst3PluginBridge::Vst3PluginBridge() host_callback_handler = std::jthread([&]() { sockets.vst_host_callback.receive_messages( std::pair(logger, false), - [&](CallbackRequest request) -> CallbackResponse { + [&](CallbackRequest request) -> auto { return std::visit(overload{[&](const WantsConfiguration&) -> WantsConfiguration::Response { return config;