Broadcasting is an example how powerful method delegation can be. It allows to write elegant code in common situations, where you otherwise would need a for-loop. Objects can define a forward handler. This handler then has the possibility to forward a message to more than one object. An example of such an object is the Set container from /obclib. It can be configured to forward all messages, it doesn't know to all the objects it contains. Here is the same vehicle example using broadcasting instead of for-loops.
main.c
#include "../obc/obc.h"
#include "../obclib/obclib.h"
#include "vehicleI.h"
#include "car.h"
#include "tunedcar.h"
#include "boat.h"
int main(){
Set allVehicles = set_create();
set_config(allVehicles, SET_ISOWNER | SET_DOFORWARD);
/* create objects */
set_add(allVehicles, car_create());
set_add(allVehicles, tunedcar_create());
set_add(allVehicles, boat_create());
/* use objects */
vehicle_accelerate(allVehicles);
obj_print(allVehicles, stdout);
/* free objects */
obj_destroy(allVehicles);
return 0;
}
Since allVehicles doesn't understand the message vehicle_accelerate(), it is forwarded to car, tunedcar and boat. Similar things happen with obj_print(). Because we configured allVehicles to be owner of its elements, obj_destroy() does free them as well.
Set has a build in forward handling. How can you implement this for your own object? Here is the same example using a pointer string. A pointer string is simply a NULL terminated pointer array. The method obj_setForwardHandler() is used to register pstr_forwardHandler() to the pointer string object pstr.
main.c
#include "../obc/obc.h"
#include "vehicleI.h"
#include "car.h"
#include "tunedcar.h"
#include "boat.h"
static Obj pstr_forwardHandler(void** self, ForwardIterator it) {
return self[it->counter++];
}
int main(){
Obj pstr[4];
obj_setForwardHandler(pstr, (ObjForwardHandler)pstr_forwardHandler);
obj_config(pstr, "Pstr", 0);
/* create objects */
pstr[0] = car_create();
pstr[1] = tunedcar_create();
pstr[2] = boat_create();
pstr[3] = NULL;
/* use objects */
vehicle_accelerate(pstr);
for(i=0; i < 3; i++) {
obj_print(pstr[i], stdout);
}
/* free objects */
obj_destroy(pstr);
obj_destroyInfo(pstr);
return 0;
}
The methods vehicle_accelerate() and obj_destroy() are now forwarded
to all objects returned by
pstr_forwardHandler(). But obj_print() isn't because obj_print(), like
obj_getHashCode() and obj_compare(), does implement a default behavior for
each object. If you need to delegate this methods, you must implement this
behavior explicitly (like Set does for obj_print).
ATTENTION: don't forget to call obj_destroyInfo(pstr) at the end to free
the object resources for pstr!
The forward handler itself is easily implemented. It is called as long as it
doesn't return NULL and must return objects where the method is forwarded
to. The ForwardIterator offers the following fields to help you iterating:
methodName, counter, counterPtr, data, dataPtr