-
Notifications
You must be signed in to change notification settings - Fork 731
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Convert server plugin API to C #6145
Comments
In Rethinking the Computer Music Language: SuperCollider (2002) James McCartney defined it this way: "The unit generator applications programming interface (API) is a simple C interface." (p. 64) … and IIRC there was no discussion about whether to change it. It probably just happened? |
I doubt that this was ever really true... But people definitely added more and more C++ bits over time. |
Just a wild idea (not really thought out): would it be possible to implement a new API as pure C, then provide the existing C++ API in terms of the new C API (a kind of wrapper) so ultimately no-one is affected? |
The Server exposes the interface table and various structs. These need to be the same for all languages. A C++ wrapper cannot magically change the data layout or function parameters. Note that the changes outlined above mostly affect the ABI and only require compilation. There are just a few cases in 2. and 4. where maintaining source compatibility is a bit more tricky. As a side note: we already have a C++ wrapper over the (pseudo-)C API: |
Hmm - could we avoid breaking compatibility with existing plugins by:
There should be no danger of collisions between the |
There may be cases where struct layout differs between C and C++, but considering that this was at SOME point nominally a C ABI - it seems likely that everything would be equivalent? But I haven't thought the through the implications of this, maybe there are some other lurking problems... |
scsynth does not export any symbols, it passes the interface table to the plugin entry point function. The whole API is defined by structs containing data and function pointers. My point is that these structs - and all referenced functions - should strictly follow the C ABI. I don't see how a "C++ wrapper" could work in this case.
We need to bump the plugin API anyway in the near future, particulary if we want to implement #5347. I would just try to avoid bumping it several times in a row. For example, we could have a dedicated branch as a PR target for all plugin API changes and only merge it into main once all necessary PRs are done.
One problem are The general idea is that anyone can look at our structs and correctly deduce the data layout, so people can reimplement our plugin API in other languages (that can interop with C). |
Motivation
Currently, the server plugin API is actually a C++ API. This has the significant downside that you cannot write server plugins in other languages without at least some C++ glue code.
Since most languages can interface with C, a pure C API would make it easier to develop server plugins in other languages (C, Rust, Zig, Fortran, etc.)
There are two distinct, but related aspects:
languages that understand C headers (C, C++, Obj-C, D, Zig, etc.) can work directly with our header files
all other languages (e.g. Rust) can reimplement our plugin API by following the C ABI
Plan for Implementation
The required changes necessarily break the ABI, so we need to bump the plugin API version and recompile existing plugins. However, we should try to maintain source compatibility as much as possible, i.e. existing C++ plugins should still compile.
In general, these are the required changes:
replace
bool
data members and function arguments withint
Only requires recompilation.
replace reference functions parameters with pointers, e.g.
struct FifoMsg&
-->struct FifoMsg *
This would obviously break C++ source compatibility.
If we can guarantee that reference and pointer arguments are interchangable on the ABI level, we might do this conditionally for non-C++ code.
Otherwise we would have to break source compatibility for all API functions with reference parameters.
AFAICT, only the following functions would be affected:
Assuming that most people actually use the corresponding macros, we may get away with changing their definition.
For example:
#define SendMsgFromRT (*ft->fSendMsgFromRT)
would become
#define SendMsgFromRT(inWorld, inMsg) (*ft->fSendMsgFromRT(inWorld, &(inMsg))
The scope buffer functions don't have corresponding macros, but I guess they are also very rarely used.
If not compiled as C++, remove inline member functions from structs (
sc_msg_iter
,Complex
,FifoMsg
,RGen
, etc.).Each language wrapper can then provide their own helper functions for dealing with these structs.
This does not affect C++ plugins.
replace "real" C++ classes with C equivalent.
AFAICT, this only concerns
SCFFT_Allocator
(see SCFFT_Allocator not ABI compatible #4438) andBelaScope
(see Bela: convert BelaScope to a C API #5411)replace
nova
spinlocks inWorld
andSndBuf
for Supernova pluginsSince the spinlocks come from an external library, we cannot just remove the C++ parts (as in 3.)
For non-C++ plugins, we could replace the C++ classes with C structs of the same data layout, so other languages would just need to add appropriate helper functions.
However, these helper functions would have to match the implementation of the corresponding C++ member functions in the
nova
library! This is particularly important for the reader-writer spinlocks!The text was updated successfully, but these errors were encountered: