REBOL Technologies

Uncovering the Hidden DLL Function Callback Feature

Carl Sassenrath, CTO
REBOL Technologies
16-Mar-2005 23:05 GMT

Article #0141
Main page || Index || Prior Article [0140] || Next Article [0142] || Post Comments || Send feedback

I took a little break today and dropped-in on the REBOL3 AltME world to find an interesting question being asked by Cyphre and a few other users: The word CALLBACK appears in the REBOL binary, but what does it do? Here is some little known information about this feature.

Callbacks in DLL Functions (Routines)

When using the External Library Interface (DLLs), you can pass a REBOL function to be called back from within a DLL function. REBOL will deal with the argument conversions in both directions, but you still have to write it with great care, because interfacing in this way to DLL code is tricky business.

Example of a Callback

Here is an example written by Cyphre that helps show the way a callback function works. In REBOL you would write a routine (a DLL interface function) such as:

test: make routine! [
    a [int]
    b [int]
    c [callback [int int return: [int]]]
    return: [int]
] test-lib "test"

Here the c argument is a callback function interface specification that takes two integers and returns an integer result. Note that the argument names are not provided, only their datatypes.

Then, in the test.dll code you might write the something like:

extern "C"
MYDLL_API int test(int a, int b, int (*pFunc)(int, int))
{
   int result = pFunc(a, b);
   return result;
}

And finally, try it out, you would write the actual callback function such as:

add-it: func [a b][return a + b]

and give it a try with:

>> test 1 2 :add-it
== 3

Here the TEST routine is called and control is passed to its C DLL function along with a pointer to a special "trampoline" function (here the pointer is referenced with pFunc) that marshals the arguments and passes them to the callback function that is interpreted within REBOL.

Special Notes

There are a few important notes to keep in mind:

  1. You can only have 16 callbacks active at one time. This is a restriction in the way the callback function argument frames are constructed by the C compiler.
  2. It is optional to specify the RETURN value for the CALLBACK in its spec block.
  3. Callbacks can return only integer, decimal, and string (memory) datatypes (in addition to returning nothing at all, as stated above).
  4. If your REBOL callback function does not return the correct datatype, for protection the system will return a null or zero value to the caller of the correct datatype.
  5. A routine can include multiple callback function references.
  6. Callbacks work in Win32 and most Unix/Linux implementations of REBOL including OpenBSD, but they are not yet enabled in systems like OS X.
  7. There may be other issues related to the use of callback functions that are not known at this time. Use this feature with care.

I would like to thank Cyphre, Jaime Vargas, Romano, and Alphe Salas-Schuman for their help and participation in this project.

14-Sep-2005: Additional limitation:

It has just been reported that if you are using a lot of callbacks in your code you may see a crash message "root block overflow". This is due to an allocation shortage for callbacks. In other words, you may be getting less than the 16 callbacks mentioned above.

Keep in mind that callbacks are pretty much an "alpha" test feature. They need more testing and feedback from you. If the above case is affecting your usage or testing of this feature, please contact us to let us know about it. Thanks.

Dynamic Allocation on Callbacks: CHAR-ARRAY

The CHAR-ARRAY word can be used in the place of CHAR* to make a copy of a C string that is being passed to REBOL. This allows the string to exist within REBOL's memory space and not be affected by changes that might occur in the C space (such as volatile reuse of memory).

For example:

dup: make routine! [
    string [char*]
    action [callback [char-array 2000] return: [char*]]
] test-lib "dup"

Each time the ACTION callback function is called (from C), its argument will be dynamically allocated as string of length 2000. If the C code provided a pointer to memory, the contents of that memory will be copied into the newly allocated memory. This will happen each time the callback function is invoked.

Post Comments

Updated 22-Nov-2017   -   Copyright Carl Sassenrath   -   WWW.REBOL.COM   -   Edit   -   Blogger Source Code