Function overloading isn't possible in C, you have to give them all different names. (HandleAIMessage1i, HandleAIMessage2i, HandleAIMessage1i1f, etc.)
Also a design where you pass arbitrary data with arbitrary messages is very bad, because it is totally not self documenting: You have to know beforehand that only HandleAIMessage1i will receive the ENEMY_LEFT message for example, and if you typo'ed it to HandleAIMessage2i you wouldn't receive any error at all, the message would just be ignored because it's passed to the other overload. Also suppose a new parameter would be added to a message. Then suddenly a different function gets called and the same thing happens: no warnings, no errors, no whatever, the message is silently ignored.
In other words, make sure the interface makes good use of compile time (type) checking whenever possible, and use runtime checks otherwise. Also make the code as self documenting as possible (also aids because one can use intellisense then), so no one ever has to figure the code out, but everyone can just read it.
Another point in the thread I don't see is, why decide to use arrays instead of structs because structs are presumably less portable (not, if you ask me), and then put the arrays in a struct after all ??? That's combining the disadvantages to get an even bigger disadvantage: less portability and unreadable code.
Just K.I.S.S., and make one function for each message, or make a single HandleAIMessage function that takes a void* that should be casted to a structure with identical name to name of event (plus/minus some prefix/suffix). Then have the event arguments sit in the structure.
The latter, my solution has the following advantages:
- parameters can be added without even breaking ABI compatibility, by just putting a new member in the struct (on the end).
- There is only one "semantic cast"* on the AI side ((void*) -> BlahblahEventArgs) and one on the Spring side. (compare with arrays where every access is a "semantic cast".
- It is well known design, at least to C programmers.
For the AI -> engine direction I'm a bit more in doubt but if I had to choose I'd go for a C style vtable approach. It's very easy to map this to an IAICallback implementation, it's portable ABI (as much as structs and function calls are portable) and C style vtables are also a well known design.
The other approach for AI->engine I see is making a bunch of public functions in spring.exe, and then linking AIs to spring.exe during build (or some library resulting from spring build). Problem with this however is that, AFAIK, it differs a lot per platform on how to do this, and it isn't always as easy to figure out either. Hence I prefer passing function pointers into the AI, so it can then call the functions indirectly.
*semantic cast: figured that's more or less the best term for this: not necessarily type casting, but "casting" the meaning of a variable (meaning defined by it's name). For example,
int xposition = yposition; would be a "semantic cast". Same for
int unitid = param1;, which is what I'm targetting.
EDIT: oh and indeed AF is right about varargs, varargs calling conventions are even more clumsy then normal calling conventions, so we'd only run into trouble with them. Let alone the incredible high chance for errors when using them (no compile nor runtime checking again..)