OpenGATE Contents | GATE development concepts: Delegates
Problem description
Calling functions entry points from different languages has become a major challenge. Classic C function pointers are not comfortable enough to serve in high level codes like in C++. And C++ templates are too generic to be mapped back to simple C code.
Individual patch code is needed to translate function callbacks from one language layer too another for each component implementation which leads to multiple slightly different solutions for the same problem.
Solution
dotNet introduced Delegates
as one defined interface to encapsulate
callbacks and function or method pointers.
The GATE framework takes the dotNet delegate concept as a model for its own
implementation.
Definition
A gate_delegate_t
instance provides storage for a native function, method
and object pointer and a pointer to a dispatcher function.
The dispatcher function takes a pointer to its gate_delegate_t
instance
and a va_list
reference that gives access to any kind of variadic function
arguments.
Each kind of final delegate for a typed callback function or method is required
to provide a dispatcher that translates the contents of a va_list
into
the native function call.
Initialization of a gate_delegate_t
instance stores the native pointers to
the callback function or method and/or an associated object pointer and the
required dispatcher function.
When the global function gate_delegate_invoke()
is called with a pointer to
the initialized delegate and additional variadic arguments, it uses the
delegate’s dispatcher function that extracts the variadic arguments and
converts them into natively typed arguments and invokes the target function
or method endpoint.
To ensure compatibility between all kinds of languages, following rules for delegate functions must be fulfilled:
- A target function can use only primitive data types and pointers (or pointer-equivalents) as function arguments. C++ references are translated into Pointers when they are passed through delegate layers. Using whole object as by-value arguments is forbidden.
- A target function is only allowed to return a
gate_result_t
type to indicate the proper execution of the function. It is a simplified kind of exception notification.
Other kinds of status or return values need to be communicated via output-pointers as function arguments. - It is forbidden to raise exceptions or to initiate any other kind
of asynchronous code execution from within the delegate’s target code.
Exceptional conditions need to be cought through the native featureset
of the used language and must be translated into a
gate_result_t
indicator. - Delegates are treated as
const
objects after their initialization. It is forbidden to change their contents afterwards. - The usage scope of a delegate is releated to the life-time of the native code it is dispatching to. Object methods are only allowed to be called as long as the target object is allocated and fully functional.
- Delegates are weak references. They are not expected to retain objects and extend their life-time.
Implementation details
C Macros like the GATE_DELEGATE_DECLARE_
family generate a C-style callback
function typedef
for plain C functions and object-oriented methods including
an additional this
pointer.
Initialization functions like GATE_DELEGATE_INIT_FUNC
and
GATE_DELEGATE_INIT_OBJ
help to distinguish between function and object
bindings and choose the appropriate dispatcher function.
The C++ implementation uses template type deduction technologies to automatically bind a delegate to correct internal dispatcher. Therefore no additional macros are required.
C Example
1#include <gate/delegates.h> 2 3gate_result_t my_callback_function(int x, int y) 4{ 5 /* do something with "x" and "y" */ 6 return GATE_RESULT_OK; 7} 8 9GATE_DELEGATE_DECLARE_2(my_callback_delegate, int, int); 10 11int main() 12{ 13 gate_result_t result; 14 gate_delegate_t my_delegate_instance; 15 16 GATE_DELEGATE_INIT_FUNC( 17 my_callback_delegate, &my_delegate_instance, 18 &my_callback_function, NULL); 19 20 result = gate_delegate_invoke(&my_delegate_instance, 12, 34); 21 22 return 0; 23}
C++ Example
1#include <gate/delegates.hpp> 2 3class Target 4{ 5public: 6 void method(int x, int y) 7 { 8 // do something 9 10 } 11}; 12 13int main() 14{ 15 Target target_instance; 16 17 Delegate2<int, int> my_delegate_instance( 18 &Target::method, &target_instance); 19 20 my_delegate_instance(12 ,34); 21 // equal to: 22 // gate_result_t result = gate_delegate_invoke( 23 // my_delegate_instance.c_impl(), 12, 34); 24 25 return 0; 26}