We sadly cannot call `shared_library()` and `executable()` in these
subdirectories while still maintaining the same `build/` directory
structure, but this is still much cleaner. All of the other build
artifacts are now also gone from the root of `build/` so it's cleaner
overall.
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 is in some cases needed to get decent performance in REAPER, as
REAPER seems to query this information (which cannot change without the
plugin requesting a restart) four times per second.
This prevents REAPER from crashing when removing the last instance of a
plugin and then readding it. REAPER doesn't unload the module even after
it removes its last plugin factory instance. This means that before this
the plugin factory would be freed but we still had a seemingly valid
pointer to it that we would try to access.
This was a bit of a tricky one because it requires simulating mutual
recursion, but it's needed for REAPER as otherwide calls to
`IPlugFrame::resizeView()` and `IContextMenu::popup()` might cause
REAPER to segfault because its GUI is not thread safe.
Instead of serializing the actual `YaMessage`, for the reasons mentioned
in the comments. This was needed to stop iZotope VocalSynth 2 in Ardour
from segfaulting when editing parameters, because that plugin is
apparently being very naughty.
This way every relevant object instance will get its own thread for
handling these calls. The alternative would be creating a full fat
Vst3MessageHandler pair for all object instances, but that would be a
huge waste.
As it turns out there are only two or three functions where we can do
this. It also breaks logging, and this function will probably only be
called once anyways. More consistency is always better.
We can now use implement all VST3 plugin interfaces through this class,
check whether the object from the plugin also supports these classes,
and then conditionally allow casting to the supported classes. This
should give us a one-to-one proxy of the original object.
Directly serializing and deserializing into objects was and more
boilerplate heavy (since we now need two implementations even though we
only use one), and also much less flexible because we can't wrap
payloads in structs or provide optional values that way.