Design and simplicity would be the reason to use raw function pointers.
Doesn't matter how complex it is inside, as long as it looks simple from outside and your cpu is happy with it, its all good. Using raw function pointers is same like .. well calling a function like you would. And I mean function not inline/class function or any other macro.
I came up with something. It doesn't look pretty but it does the job.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
// gonna make it visibly more acceptable
#define funcPtr( ret, ... ) (ret(*)(__VA_ARGS__))
// only modifying call function from my code above
void call(int params, ...){
va_list vl;
va_start(vl, params);
switch(params){
case 0:
for(auto i : functions) (funcPtr( void, void )i)();
break;
case 1:
for(auto i : functions) (funcPtr( void, void *)i)( va_arg(vl, void *) );
break;
case 2:
for(auto i : functions) (funcPtr( void, void *, void *)i)( va_arg(vl, void *), va_arg(vl, void *) );
break;
// and so on ... until its enough
}
va_end(vl);
}
| |
As long as we keep forward function parameters as pointers and feed right amount of parameters, we'll be alright, by theory.
Pointer is just an variable, depending on system environment, for 64bit program, we'll be having 64bit variables as a pointers.
Passing everything as pointers offers a lot more freedom to API anyways because who ever are going to receive the callback, can also change the values of parameters for who ever is going to get a callback next.
let me show you more in code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
|
// our callback function A
void funcA( char *name ){
cout << "My name is " << name << endl;
}
struct simple{
int day;
char month[32];
};
// our callback function B
void funcB( int *year, simple *restOfTheDate ){
cout << "Today's date is:" << *restOfTheDate.month << "/" << *restOfTheDate.day << "/" << *year << endl;
*year = 2019;
}
// our callback function C
void funcC( long long age ){
cout << "Earth is " << age << " years old." << endl;
}
forward A, B, C;
// let's imagine that I already added my functions into those forwards
// something like that: A.addFunction(funcA);
int main(){
// lets just focus on calling them
A.call(1, "Fadey");
int year = 2018; simple rdate = { 18, "April" }
B.call(2, &year, date);
cout << "The year is now:" << year << endl;
long long ageOfEarth = 4540000000LL;
C.call(1, ageOfEarth);
}
| |
When running this on 64bit the output would be:
My name is Fadey
Today's date is:April/18/2018
The year is now:2019
Earth is 4540000000 years old.
|
but if we were to call this on 32bit we would .. well program would simply crash
because of calling C. Because pointers are now 32bit and we're writing 64bit variable long long into it, program would simply write this long long, 32bit of it into right place and the rest of the 32bit into where function's instructions are located. I think that memory part should be read only as well since it was not meant to be changed over the course of run time so that would trigger the crash, not the confusion of one of the instructions being change to do something .. else.