VOGONS


First post, by realnc

User metadata
Rank Oldbie
Rank
Oldbie

I tried using the C interface, but it's not very useful right now. The various callbacks of mt32emu_midi_receiver_i have no user data argument (void*), so they have no way of knowing where to send the data they get (like which mt32emu_context to use.)

All of these functions should have an extra "void *userData" argument. mt32emu_set_midi_receiver() should also take such an argument, which it then always forwards to the various callbacks. Setting a receiver would then look like this:

mt32emu_set_midi_receiver(myContext.d, myMidiReceiver, myUserData);

The callbacks would then always receive the 'myUserData' pointer.

Alternatively (or perhaps even better), mt32emu_midi_receiver_i_v0 should have a "void* userData" member, which the callbacks can then access easily through their 'instance' argument.

Reply 1 of 5, by sergm

User metadata
Rank Oldbie
Rank
Oldbie

The approach used when those interfaces were thought up was to make them binary compatible with the majority of C++ compilers (i.e. in the COM style). However, that actually breaks the portability paradigm of the project, so I changed my mind about that.
Still, I think having a multiple of callback functions with static type checking is good and better than a single callback with typeless arguments. But, as you noted, the instance pointer semantic should change.

Reply 2 of 5, by realnc

User metadata
Rank Oldbie
Rank
Oldbie

I'm not proposing a single callback though. Just a new void* argument to the existing callbacks, or a void* member in the mt32emu_midi_receiver_i_v0 struct.

This is pretty much the norm for any sort of C callback system. There's no other way to forward user-context to callback functions.

Reply 3 of 5, by sergm

User metadata
Rank Oldbie
Rank
Oldbie

Of course, there are conventional ways for callbacks in C. But I'm talking about implementations widely used in C++. Every single callback function takes the instance argument which can be used as a pointer to the object (agree, this is not obvious). Exactly the same way is used for mt32emu_context:

union mt32emu_context {
const mt32emu_service_i *i;
struct mt32emu_data *d;
};

You may also use a cast to get the same effect, as you would cast a void*. The only difference is that you add the pointer to the virtual table at the top of your data structure (as C++ usually does when constructing such polymorphic objects). I hope that explains the idea 😀

Anyway, I agree this way is pretty unnatural for C, and we cannot guarantee 100% binary compatibility with C++, so it should change. I only want to find a usable C++ wrapper for these callbacks, so the CPU won't do 3 indirect calls each time the callback is invoked.

Reply 5 of 5, by BootBoyer

User metadata
Rank Newbie
Rank
Newbie

it is looking that the approach used when those interfaces were thought up was to make them binary compatible with the majority of compilers.
Still, I think having a multiple of callback functions with static type checking is good and better than a single callback with typeless arguments. But, as you noted, the instance pointer semantic should change.

www.7pcb.com