Higher-order programming in C
vedantk.tumblr.com
vedantk.tumblr.com
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!
Like this? https://github.com/davvid/libapply
unittest.c shows how it can be used: https://github.com/davvid/libapply/blob/master/unittest.c
+-----------------------------------------------------------+
| |
| C has support for dynamic typing (in the form of void*) |
| |
+-----------------------------------------------------------+
A quotable thinker.I find very wise to avoid it when it is not necessary or when there is an equivalent safer alternative (e.g C++ templates if available and applicable).
In this case however, is not only completely fine but I don't think there is a better way to do it.
BTW, my favourite phrase of the article:
"“… Why do this?” Because I can, of course."
The truly spirit of a hacker :)
typedef void* (*f)();
extern void* apply(void* func, void** args, size_t argc) {
switch (argc) {
case 0: return ((f)(func))();
case 1: return ((f)(func))(args[0]);
case 2: return ((f)(func))(args[0], args[1]);
case 3: return ((f)(func))(args[0], args[1], args[2]);
case 4: return ((f)(func))(args[0], args[1], args[2], args[3]);
case 5: return ((f)(func))(args[0], args[1], args[2], args[3], args[4]);
case 6: return ((f)(func))(args[0], args[1], args[2], args[3], args[4], args[5]);
case 7: return ((f)(func))(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
case 8: return ((f)(func))(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
case 9: return ((f)(func))(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]);
/*...*/
}
} 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?
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.
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...
I think you can do bound functions if you do callee-cleanup, then your intermediate bound trampolines can just jmp and you don't end up with (too many) weird problems. But it means that the final return address ends up at the bottom of the stack, rather than at the top, like it would with a conventional push/call system.
In any case it looks like cdecl is out of the question.
Some further discussion in a slightly different context, if you're interested: http://stackoverflow.com/questions/11271848/implementing-bou...
On the other hand, some of the the popular PIC microcontrollers have an instruction pointer that is wider than the data registers. Good luck doing functional programming on that!
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.
http://www.instapaper.com/text?u=http://vedantk.tumblr.com/p...
- 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... :)
No, I'm not. Casting between data and function pointers (or even a function pointer to a function pointer of different signature!) is undefined behavior. Look up the C spec.
6.5.9.6 confirms that two function pointers that compare equal point to the same function.
From my careful study of the code, I have a nagging sensation that this might not quite be the end of the story as far as portability goes...
6.3.2.3.5 and .6 allow for abritrary (implementation-defined) conversions between integer types and pointer types, so any conversion between function and data pointers must go through a suitably large integer type if such exists (converting a function pointer to a too small integer type is UB).
I remain unconvinced that this is a serious issue, all things considered. The odd feeling that there could be other roadblocks to a fully portable implementation still remains. Nevertheless, it appears that this particular part could actually be done portably.
Seriously, though, is there a reason the Objective C blocks wouldn't work in C, just substituting functions for methods?
Go down this hole with caution.