A little light code review requested

L

luserXtrog

Me again. Back in black. This is a little experiment in
using stdarg to simulate functions that both receive and
return variable numbers of variables (determined by the
opcode). It seems to be working but strikes me as sufficiently
bizarre that I'd greatly appreciate even one other set of
eyes to glance over it.
It is halfway sensible and confusing due to the nature of the
problem? Or is it totally wacko-jacko?

575(1)09:40 PM:xpost 0> echo '//@@@@snip here@@@@'|cat oper.c -
object.h
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include "object.h"

Object *Iadd(Object oa[]) {
oa[0].u.i += oa[1].u.i;
return oa;
}

Object *moveto(Object oa[]) {
printf("%d %d moveto\n", oa[0].u.i, oa[1].u.i);
return oa;
}

Object *stroke(Object oa[]) {
printf("stroke\n");
return oa;
}

#define OPERATORS \
X(Iadd, 2,1) \
X(moveto,2,0) \
X(stroke,0,0) \

#define X(op,in,out) OP_ ## op,
enum e_opnames { OPERATORS };
#undef X

struct s_operator {
Object * (*fp)(Object []);
int in, out;
} oplist[] =
#define X(op,in,out) { op, in, out },
{ OPERATORS };
#undef X
typedef struct s_operator Oper;

void opcall (int opcode, ...) {
Oper oper;
int n;

oper = oplist[opcode];
n = oper.out>oper.in? oper.out: oper.in;
{
va_list ap;
Object oa[n];
Object *op;
int i;

va_start(ap, opcode);
for (i=0; i<oper.in; i++)
oa = * va_arg(ap, Object *);
va_end(ap);

oper.fp(oa);

va_start(ap,opcode);
for (i=0; i<oper.out; i++) {
op = va_arg(ap, Object *);
*op = oa;
}
va_end(ap);
}
return ;
}

int main() {
Object x = { .type = integertype, .u.i = 72 };
Object y = { .type = integertype, .u.i = 72 };
opcall(OP_Iadd, &x, &y);
opcall(OP_moveto, &x, &y);
opcall(OP_stroke);
return 0;
}

//eof
//@@@@snip here@@@@

#define LIT 1<<7

//composite attributes
#define READ 1
#define WRITE 2
#define EXEC 4

#define Types \
X( mark, int m) \
X( null, int u) \
X( integer, int i) \
X( real, double r) \
X( boolean, unsigned char b) \
X( array, size_t a) \
X(packedarray, size_t pa) \
X( string, size_t s) \
X( name, size_t n) \
X( dict, size_t d) \
X( operator, int o) \
X( file, int * f) \
X( save, Save * v) \
X( font, int * t)

#define X(a,b) a ## type,
enum e_types{ Types };
#undef X

typedef struct s_array Array;
struct s_array {
unsigned char attr;
size_t a;
size_t n;
};

typedef struct s_string String;
struct s_string {
unsigned char attr;
size_t s;
size_t n;
};

typedef struct s_dict Dict;
struct s_dict {
unsigned char attr;
size_t d;
size_t n;
};

typedef struct s_save Save;
struct s_save {
unsigned char *vm;
unsigned int level;
size_t sz;
size_t pointer;
};

#define X(a,b) b;
typedef struct s_object Object;
struct s_object {
unsigned char type;
union { Types } u;
};
#undef X

Object integer (int i);
Object real (double r);
Object boolean (unsigned char b);
Object name (Object s);
extern Object mark;
extern Object null;
576(1)09:54 PM:xpost 0> !gc
gcc -g -o oper oper.c
577(1)09:55 PM:xpost 0> gcc -Wall -o oper oper.c
578(1)09:55 PM:xpost 0> oper
144 72 moveto
stroke
579(1)09:55 PM:xpost 0>

tia
Semolina Sensemilla
 
E

Eric Sosman

Me again. Back in black. This is a little experiment in
using stdarg to simulate functions that both receive and
return variable numbers of variables (determined by the
opcode). [...]

The crux seems to be (and I've only skimmed the rest):
void opcall (int opcode, ...) {
Oper oper;
int n;

oper = oplist[opcode];
n = oper.out>oper.in? oper.out: oper.in;
{
va_list ap;
Object oa[n];
Object *op;
int i;

va_start(ap, opcode);
for (i=0; i<oper.in; i++)
oa = * va_arg(ap, Object *);
va_end(ap);

oper.fp(oa);

va_start(ap,opcode);
for (i=0; i<oper.out; i++) {
op = va_arg(ap, Object *);
*op = oa;
}
va_end(ap);
}
return ;
}


As far as I can see this will work, provided a copy of an Object
is as good as the original (see `FILE' for a counter-example). An
Object struct with a flexible array member would be troublesome, and
a call like `opcall(OP_Iadd, &obj, &obj)' would yield results that
might not be what you desire.

However, it strikes me as not terribly useful. Consider: To make
use of this wrapper, the caller already needs to know how many operands
the `oper' requires and how many results it returns (and in what order).
That is, the caller knows so much about `oper' that it's hard to see
why it would bother going through all this hocus-pocus rather than just
calling the target function directly. Human-written code, anyhow, would
seem to have little use for this. Machine-generated code, perhaps, but
if you're "compiling" a language or framework into C source, wouldn't
it make more sense to adopt a less convoluted infrastructure?
 
L

luserXtrog

Me again. Back in black. This is a little experiment in
using stdarg to simulate functions that both receive and
return variable numbers of variables (determined by the
opcode). [...]

     The crux seems to be (and I've only skimmed the rest):


void opcall (int opcode, ...) {
     Oper oper;
     int n;
     oper = oplist[opcode];
     n = oper.out>oper.in? oper.out: oper.in;
     {
         va_list ap;
         Object oa[n];
         Object *op;
         int i;
         va_start(ap, opcode);
         for (i=0; i<oper.in; i++)
             oa = * va_arg(ap, Object *);
         va_end(ap);

         oper.fp(oa);
         va_start(ap,opcode);
         for (i=0; i<oper.out; i++) {
             op = va_arg(ap, Object *);
             *op = oa;
         }
         va_end(ap);
     }
     return ;
}


     As far as I can see this will work, provided a copy of an Object
is as good as the original (see `FILE' for a counter-example).  An
Object struct with a flexible array member would be troublesome, and
a call like `opcall(OP_Iadd, &obj, &obj)' would yield results that
might not be what you desire.

     However, it strikes me as not terribly useful.  Consider: To make
use of this wrapper, the caller already needs to know how many operands
the `oper' requires and how many results it returns (and in what order).
That is, the caller knows so much about `oper' that it's hard to see
why it would bother going through all this hocus-pocus rather than just
calling the target function directly.  Human-written code, anyhow, would
seem to have little use for this.  Machine-generated code, perhaps, but
if you're "compiling" a language or framework into C source, wouldn't
it make more sense to adopt a less convoluted infrastructure?


Yes. It's mostly just a testing function but I may have need of it
later. This is a postscript interpreter, by the way.

My idea here is that the payload of an operator object is just an
integer index into the function array (this allows functions to
operate on functions). I plan on a slightly different handler for
normal execution which will pass a pointer from the global operand
stack and then readjust the stack pointer to account for the
difference. But this opcall function will (I hope) allow more
flexibility for operator functions to make use of each other
independently of the operand stack.

I was a little concerned about using stdarg and VLAs in the same
function; but it certainly seems to work. It actually compiled
clean the very first time. So I dared to run it and received the
to-be-expected segmentation violation. It was hilarious.

looza lychee
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,767
Messages
2,569,571
Members
45,045
Latest member
DRCM

Latest Threads

Top