Callbacks

BBC BASIC for Windows supports callbacks but BBC BASIC for SDL 2.0 doesn't. This is largely because of the number of different ABIs that would need to be supported, and the difficulty (maybe impossibility) of achieving it in C.

Is there any way, in the C programming language, of calling a function whose signature (number and types of parameters) can be specified at runtime (e.g. in a variable of some sort) rather than explicitly (i.e. known at compile time)?

Is there perhaps some way in which the varargs feature of C could be leveraged to achieve this?

Comments

  • I really do not know. Varargs has always bent my brain into uncomfortable shapes, so I can't say for certain whether it would help.
  • Soruk wrote: »
    I really do not know. Varargs has always bent my brain into uncomfortable shapes, so I can't say for certain whether it would help.
    OK. I suppose some light might be shone on this by knowing - which I don't - what is exported in the object file for a varargs function, and what the linker does with that information.

    Does the linker know that it's a varargs function and somehow arrange to call it differently, or is it just like any other exported function as far as the linker is concerned? If the linker doesn't know, it suggests that a callback could successfully be made to that function.

    One feature of varargs functions is that they must be able to determine how many parameters there are, either by passing that information explicitly, or deducing it (which is how printf and scanf do it, because the number of parameters is implied by the format string).

    That's not in itself a barrier to using a varargs function as a callback, because one would make the number of parameters something specified when the callback is declared in BASIC.
  • I think the problem there is that with varargs the number of parameters is known at compile time, whereas using it as a callback in BASIC is that value is unknown at compile time.

    Could this be implemented by defining a parameter block so that at the C layer all that's being passed is a memory location, then the BASIC implementation can examine that block as it will know the structure expected even if it is not known to the C layer?
  • Soruk wrote: »
    I think the problem there is that with varargs the number of parameters is known at compile time, whereas using it as a callback in BASIC is that value is unknown at compile time.
    Whilst that is true, it doesn't follow that the count must be known at compile time. If you look at the way the parameters are extracted, the count is something you pass to the va_start macro at run-time:
        va_start(ap, count); /* Before C23: Requires the last fixed parameter (to get the address) */
        for (j = 0; j < count; j++) {
            sum += va_arg(ap, int); /* Increments ap to the next argument. */
        }
        va_end(ap);
    
    That being the case I don't believe it does need to be known at compile time.
    Could this be implemented by defining a parameter block so that at the C layer all that's being passed is a memory location, then the BASIC implementation can examine that block as it will know the structure expected even if it is not known to the C layer?
    I'm not following what you mean here. One would probably have the choice of coding va_start, va_arg and va_end in BASIC rather than C, although it might be easier to wrap those macros in functions and call them from BASIC with SYS.
  • That being the case I don't believe it does need to be known at compile time.
    Indeed the example at Wikipedia, from which the code I listed was extracted, explicitly does not know the count at compile time: it's passed as a parameter! You can use different counts in the same program:
    a = average(2, b, c);
    d = average(3, e, f, g);
    h = average(4, i, j, k, l);
    
    So your assertion that "with varargs the number of parameters is known at compile time" is not (always) correct.
  • Fair enough - I stand corrected.
  • Hated_moron
    edited October 29
    the difficulty (maybe impossibility) of achieving it in C.
    It turns out that the premise of the question is flawed. If you want to support an arbitrary number of callbacks, which is desirable, you must implement them in assembly language, not in C.

    The reason is easy to see: the only way of distinguishing between two or more different callbacks is by the memory address being called so an arbitrary number of callbacks means an arbitrary number of addresses, and you can't do that in compiled C. You must generate the code of the callback, or at least its entry point, at run time.

    One small caveat, you could in principle check the return address and attempt to determine which callback it is from that. But there's no guarantee that the same callback function won't be called from two or more places, nor that multiple callback functions aren't called from the same place (e.g. via a dispatch mechanism).

    So it simply isn't possible to implement (an arbitrary number of) callbacks in versions of BBCSDL in which the assembler is unusable (e.g. iOS and 'Apple Silicon' MacOS). On other versions a callback library would be possible, using the same approach as the BB4W library.