Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!usc!apple!agate!shelby!unix!hplabs!hpfcso!cunniff From: cunniff@hpfcso.HP.COM (Ross Cunniff) Newsgroups: comp.lang.misc Subject: Re: Look, Ma, I can dynamically compose functions in C! Message-ID: <8960034@hpfcso.HP.COM> Date: 24 Jan 91 23:39:44 GMT References: <19468:Dec2100:23:0790@kramden.acf.nyu.edu> Organization: Hewlett-Packard, Fort Collins, CO, USA Lines: 145 In article <553@ssp9.idca.tds.philips.nl> dolf@idca.tds.PHILIPS.nl (Dolf Grunbauer) writes: >>In article <1990Dec19.222059.6878@mathrt0.math.chalmers.se> augustss@cs.chalmers.se (Lennart Augustsson) writes: >>> To see the difference just try to write a function >>> that does function composition. >>> I claim that this is impossible to do in a portable way. >> >>Okay. (What follows isn't tested but you'll get the idea.) >> >>[ idea, which probably will work, deleted ] >> >I think this is not what Lennart wanted. He wanted something like: > extern int f(), g(); > int (*fg)(); > ... > fg = f; > (*fg)(x); /* executes f(x) */ > fg = compose (f,g); > (*fg)(x); /* should execute f(g(x)) */ >That is: he just wants to use the result of compose just like any other >pointer to function, so one needs not to known whether it is a real function >or a composed one. In Dan's code one must made this disctinction. And now for something completely perverse. Of course, this violates the 'portability' constraint, but the function is short enought that it wouldn't be terribly hard to rewrite it for your target machine. Here goes: /* Assembly code we'll generate: * mov.l 4(%sp),-(%sp) * jsr b * mov.l %d0,(%sp) * jsr a * addq.w %4,%sp * rts */ /* Instructions used */ #define INSTR_GETX1 0x2f2f /* mov.l 4(%sp),-(%sp) */ #define INSTR_GETX2 0x0004 #define INSTR_JSR 0x4eb9 /* jsr ... */ #define INSTR_GETB 0x2e80 /* mov.l %d0,(%sp) */ #define INSTR_RET1 0x584f /* addq.w %4,%sp */ #define INSTR_RET2 0x4e75 /* rts */ #define COMPOSE_SIZE 22 /* Bytes */ typedef int (*func)( int ); /* Build a function that returns a(b(x)) */ func compose( func a, func b ) { unsigned short *Res, *Abits, *Bbits; void *malloc( unsigned ); Res = malloc( COMPOSE_SIZE ); if( !Res ) { return 0; } /* Get addresses */ Abits = (unsigned short *)&a; Bbits = (unsigned short *)&b; /* Call b */ Res[0] = INSTR_GETX1; Res[1] = INSTR_GETX2; Res[2] = INSTR_JSR; Res[3] = Bbits[0]; Res[4] = Bbits[1]; /* Call a, leaving result in d0 */ Res[5] = INSTR_GETB; Res[6] = INSTR_JSR; Res[7] = Abits[0]; Res[8] = Abits[1]; /* Pop args off stack and return */ Res[9] = INSTR_RET1; Res[10] = INSTR_RET2; /* Flush caches - this is very system specific, but necessary */ /* since separate I and D caches won't know about each other. */ flush_cache( Res, COMPOSE_SIZE ); /* Return result */ return (func)Res; } int a( int x ) { return x + 2; } int b( int x ) { return x * 2; } main( int argc, char **argv ) { int x, atoi( char * ); func f; x = atoi( argv[1] ); printf( "a(%d) = %d\n", x, a(x) ); printf( "b(%d) = %d\n", x, b(x) ); printf( "a(b(%d)) = %d\n", x, a(b(x)) ); f = compose( a, b ); printf( "f(%d) = %d\n", x, (*f)(x) ); return 0; } This printed: a(5) = 7 b(5) = 10 a(b(5)) = 12 f(5) = 12 on an HP9000 Series 300 workstation. How fun! Ross Cunniff Hewlett-Packard Colorado Language Lab cunniff@hpfcla.HP.COM