Return Styles: Pseud0ch, Terminal, Valhalla, NES, Geocities, Blue Moon.

Pages: 1-

Currying in C

Name: Anonymous 2012-06-11 3:36

Hi /prog/, what's the standard technique to implement currying / bound function parameters in C (or i386 assembly)? I assume just allocate executable memory, copy in a stub and jmp into it? More elegant suggestions welcome

Are there any implementations of this technique for i386 / x86_64 / ARMv7? I've come across this paper [1] and this forum thread [2] but i havn't quite managed to hack anything together with a VirtualAlloc yet.

_________________________

1. http://asg.unige.ch/site/papers/Dami91a.pdf
2. http://arstechnica.com/civis/viewtopic.php?f=20&t=181415

Name: Cudder !MhMRSATORI!FBeUS42x4uM+kgp 2012-06-11 3:56

You could probably do this generically in C++ with a lot of operator overloading and templating. A curried function is just a function with a few of its parameters already given values; an object seems like a good place to store them.

It's not something C programmers would do regularly.

Name: Anonymous 2012-06-11 4:26

>>1

closures and higher order functions are more akin to objects than functions in c. You could think of a curried function as an instance of an object that has some data within it (the bindings of the variables) and a reference to another object (the function that has been curried). Then there is a method in the higher order object that takes some parameters and passes them along with data stored within the object to a subroutine of the referenced object. In a more primitive case, the referenced object can just be a function pointer, or hard coded to a particular function.


void print_bbcode_string(FILE* stream, int bold, int italics, char* string) {
  if(bold) fprintf(stream, "[b]");
  if(italics) fprintf(stream, "[i]");
  fprintf(stream, "%s", string);
  if(italics) fprintf(stream, "[/i]");
  if(bold) fprintf(stream, "[/b]");
}

struct string_printer {
  FILE* stream;
  int bold;
  int italics; 
};

void string_printer_call(struct string_printer* printer, char* string) {
  print_bbcode_string(printer->stream, printer->bold, printer->italics, string);
}


like in >>2, you just use objects to emulate them.

And yeah, you typically don't do this type of thing unless you have a good reason to. Making a closure and calling map on it isn't a good enough reason, unless you have a really awesome map implementation that forks a bunch of threads or something, you would just translate the code to a for loop, with the closure inlined in a hard coded fashon.

Name: Anonymous 2012-06-11 4:27

>>2
It's easy enough to do with an object, there's even std::bind1st in C++.

But i want something i can pass as a raw function pointer. It's not because i'm writing C, i want to use the technique in some outputted assembly.

Name: Anonymous 2012-06-11 4:36

>>4

So you are looking to generate assembly for a function that is equivalent to another function, except that certain values have been bound to a constant? This seems doable. In the most trivial implementation, it could be:


push bound_argument1
push bound_argument2
...
(maybe do some reordering of the arguments on the stack)
...
call bound_function_pointer
ret


You could also create a duplicate body of code for the orginal function, except that certain arguments are bound to constants. And then maybe apply constant folding within the function to optimize it a little.

Name: Anonymous 2012-06-11 4:45

>>5
That's right. It does seem doable, there's a few references to the topic but no solid examples. [2] in the OP is my best lead so far, although the assembly is using indirect jmps (i think absolute ones might be more reliable).

I'm working on a prototype for currying a C int (*fn)(int,int) to an int (*fn)(int), but havn't quite gotten it working yet. Once that's there, the assembly will be pretty general for all of a given calling convention (aiming for cdecl with straight stack parameters, no fpu registers).

Creating a duplicate body of code is probably a lot more work (and way more memory intensive) than an extra jmp. Constant folding is a lot of work to do at runtime, too. One less jmp to mispredict, but much less cache-friendly

Name: Anonymous 2012-06-11 4:48

>>6
yeah just creating some argument pushing code and a jmp (oh yeah, I didn't realize it's a tail call) would be a lot simpler and faster than recompiling the function body.

Name: Anonymous 2012-06-11 5:25

https://github.com/HackerFoo/curry.c/blob/master/curry.c

Roughly the same approach without using any inline assembly (copying a template function?). Will require further study to see if the technique is applicable in my situation

Don't change these.
Name: Email:
Entire Thread Thread List