For the set of features you have, with most calling conventions on most platforms you don't need to synthesize all that. You could simply have your apply function be written in assembly and handle setting up the parameters fetching them from memory.
That also isn't higher-order programming as such. Can you create new functions from doing that? There are ways to compose functions in C by hand, but the distinction is that it cannot be done elegantly. (One could always just do higher-order programming in C by constructing an abstract syntax tree, after all.)
A good first effort in that you did correctly handle calling functions without requiring them to fetch their arguments in an unusual way, which is what most people would try at first. With a varargs apply function, you can handle the case of passing values larger than a register and passing pointers without having to cast them to a list, too. Consider the case of an apply function which takes an unsigned integer as the size of the following argument. If it's zero, then the argument list is finished. Otherwise, it then fetches an argument of that size from the va_list and goes on to the next one. So then your apply function is slightly uglier (int j = 42; apply(printf, sizeof (const char ), "Hello, %s! %u\n", sizeof (const char ), "world"), sizeof j, j, 0) but perhaps more flexible, and at least avoids relying on a few nasty implicit casts.
Also, function pointers should not necessarily be cast implicitly to void *, but on only a few architectures are function pointers so strange that you'll have any trouble with your approach.
As it is, you could even avoid writing or generating any assembly by just having the apply routine be a static inline function that does a switch on the argument count and then casts the function pointer to a function pointer that takes the right number of arguments. The compiler could do constant propagation for the number of arguments and you'd have little overhead in your function calls. You couldn't handle arbitrarily-large argument lists, but it would work pretty well.
For the set of features you have, with most calling conventions on most platforms you don't need to synthesize all that. You could simply have your apply function be written in assembly and handle setting up the parameters fetching them from memory.
+-----------------------------------------------------------+
| |
| C has support for dynamic typing (in the form of void*) |
| |
+-----------------------------------------------------------+
Haha I can't stop cracking up! Although it reminds me of when I wrote a ray-tracer in high school. Every reflection distribution function took some kind of void* structure and did something unique with it.
I don't get the point. Why not use the generic function type?
void *(*routine)(void *)
If you have a function already, just create a simple wrapper once. In the code shown, the wrapping has to be done by the caller of apply().
And this apply() doesn't work on all functions anyway. Casting an array of long into an array of void* makes quite a few assumptions and is very limited. What would you do if your original function takes a struct as an argument?
I'd quite like to use bind() in C / nasm for my current project. I think you can use a similar approach - create an executable area on the heap that pushes an extra parameter and jmps to the target function, then just return a function pointer to it.
EDIT: there might be a problem with arranging the stack parameters - if you use call on the target, then you're pushing a return address onto the stack, which messes up the arguments. But if you simply jmp, then neither the caller or callee know exactly how to clean up the stack when the function returns... hmm. I guess you could permanently reserve a register for this shimming but that's hardly an elegant solution.
The ability to cast a function pointer to a regular pointer is an implementation behavior (supported on every machine you'd want to use, but nonetheless it's not standard C)
That's undefined behavior meaning that anything might happen, including appearing to work [most of the time]. C allows casts between two data pointers and between two function pointers, but not cast that cross the data/function boundary.
Any cast that crosses function/data boundary must go through suitably large integer type; UB results if you cast a pointer to a too-small integer type. See my other comment in this thread for a reference to the standard.
The blog's github interface is annoying: in order to save-to-disk more than just the opening paragraphs in Firefox, I had to bring up Web Developer's View Generated Source function and save the result of that. Not cool for anybody who prefers to study offline.
Strange. I had zero trouble saving the blog to disk with Firefox on a Windows PC.
- it works with both an old generation of Firefox (3.x) and a post 3.x version.
- it correctly opens with the code, in both FF versions as well as IE.
- NoScript also correctly blocks and unblocks the code depending on what access you give to github.com and tumblr.com.
So, not sure what you are seeing, but I am unable to replicate it. As you can probably guess, I am just as sensitive to these saving glitches when they occur as you are too... :)
This is not undefined behavior. It's implementation specific behavior -- there's a huge difference. If you specify the calling convention for your functions, this is 100% deterministic and predictable. Nothing at all wrong with this approach.
[+] [-] caladri|13 years ago|reply
That also isn't higher-order programming as such. Can you create new functions from doing that? There are ways to compose functions in C by hand, but the distinction is that it cannot be done elegantly. (One could always just do higher-order programming in C by constructing an abstract syntax tree, after all.)
A good first effort in that you did correctly handle calling functions without requiring them to fetch their arguments in an unusual way, which is what most people would try at first. With a varargs apply function, you can handle the case of passing values larger than a register and passing pointers without having to cast them to a list, too. Consider the case of an apply function which takes an unsigned integer as the size of the following argument. If it's zero, then the argument list is finished. Otherwise, it then fetches an argument of that size from the va_list and goes on to the next one. So then your apply function is slightly uglier (int j = 42; apply(printf, sizeof (const char ), "Hello, %s! %u\n", sizeof (const char ), "world"), sizeof j, j, 0) but perhaps more flexible, and at least avoids relying on a few nasty implicit casts.
Also, function pointers should not necessarily be cast implicitly to void *, but on only a few architectures are function pointers so strange that you'll have any trouble with your approach.
As it is, you could even avoid writing or generating any assembly by just having the apply routine be a static inline function that does a switch on the argument count and then casts the function pointer to a function pointer that takes the right number of arguments. The compiler could do constant propagation for the number of arguments and you'd have little overhead in your function calls. You couldn't handle arbitrarily-large argument lists, but it would work pretty well.
Keep at it!
[+] [-] davvid|13 years ago|reply
Like this? https://github.com/davvid/libapply
unittest.c shows how it can be used: https://github.com/davvid/libapply/blob/master/unittest.c
[+] [-] huhtenberg|13 years ago|reply
[+] [-] chrisohara|13 years ago|reply
[+] [-] Xcelerate|13 years ago|reply
[+] [-] praptak|13 years ago|reply
[+] [-] ogoffart|13 years ago|reply
[+] [-] ori_b|13 years ago|reply
[+] [-] tspiteri|13 years ago|reply
And this apply() doesn't work on all functions anyway. Casting an array of long into an array of void* makes quite a few assumptions and is very limited. What would you do if your original function takes a struct as an argument?
[+] [-] mappu|13 years ago|reply
EDIT: there might be a problem with arranging the stack parameters - if you use call on the target, then you're pushing a return address onto the stack, which messes up the arguments. But if you simply jmp, then neither the caller or callee know exactly how to clean up the stack when the function returns... hmm. I guess you could permanently reserve a register for this shimming but that's hardly an elegant solution.
[+] [-] vedantk|13 years ago|reply
This might not help, but it's an interesting article on stack frames for all interested: http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-o...
[+] [-] forgotusername|13 years ago|reply
[+] [-] zvrba|13 years ago|reply
Any cast that crosses function/data boundary must go through suitably large integer type; UB results if you cast a pointer to a too-small integer type. See my other comment in this thread for a reference to the standard.
[+] [-] MichaelGG|13 years ago|reply
[+] [-] Daniel_Newby|13 years ago|reply
[+] [-] crb3|13 years ago|reply
[+] [-] drucken|13 years ago|reply
- it works with both an old generation of Firefox (3.x) and a post 3.x version.
- it correctly opens with the code, in both FF versions as well as IE.
- NoScript also correctly blocks and unblocks the code depending on what access you give to github.com and tumblr.com.
So, not sure what you are seeing, but I am unable to replicate it. As you can probably guess, I am just as sensitive to these saving glitches when they occur as you are too... :)
[+] [-] dbaupp|13 years ago|reply
http://www.instapaper.com/text?u=http://vedantk.tumblr.com/p...
[+] [-] zvrba|13 years ago|reply
[+] [-] daeken|13 years ago|reply
[+] [-] comex|13 years ago|reply
[+] [-] dchest|13 years ago|reply
[+] [-] maybird|13 years ago|reply
[+] [-] buzzert|13 years ago|reply
[+] [-] aklofas|13 years ago|reply
Go down this hole with caution.
[+] [-] bowyakka|13 years ago|reply