Polymorphic list in C, kind of 
Author Message
 Polymorphic list in C, kind of



Quote:
>unsigned char *s,*st;
>s=new unsigned char[64];
>st=s;
>*s=(char) 'a';
>s++;
>*s=(int) 34;
>s+=2;
>*s=(int) 45;

Don't do that -- there is a related thread (in comp.lang.c)
about trying to coerce char* to int*.  Anyway, you'd need
to cast the lvalue, not the rvalue:
        *(int*)s=34;
But as I said, don't do that.  Use a stack that is an array
of a union type, where the members of the union are the
various data types.


Wed, 05 Jan 2000 03:00:00 GMT  
 Polymorphic list in C, kind of

I'm writing a simple VM for a even simpler language in C. When, for
example the VM will execute :

ipush 10
iadd

this will look like 0x34 (for ipush) 0x0000000A (for 10) and then 0x65
for iadd. Then, the first instruction is a char, then an long and then a
char again; therefor, the bytecode will consist of diffrent types at
unpredictable locations. I've came up with this idea:

#include <stdio.h>
void main(void)
{

unsigned char *s,*st;
s=new unsigned char[64];
st=s;
*s=(char) 'a';
s++;
*s=(int) 34;
s+=2;
*s=(int) 45;

printf("%c %d %d",*st,*(st+1),*(st+3));

Quote:
}

but, it doesn't look very nice...
Anyone got any better ideas for implementing this kind of polymorphic
list? Thanks in advance.




Wed, 05 Jan 2000 03:00:00 GMT  
 Polymorphic list in C, kind of

Quote:

> But as I said, don't do that.  Use a stack that is an array
> of a union type, where the members of the union are the
> various data types.

thanks! but... :-)

if i use something like

union eee
{
        char   b8;
        long   b32;
        double b64;

Quote:
} *rrr;

then a sizeof of eee will be 64 (the double). Every rrr++; will increase
the pointer with 4 bytes. When i use the char for a bytecode, i will
have 3 bytes of empty storage. How do I "pack" all these types, so I
can  eliminate the loss of space?




Wed, 05 Jan 2000 03:00:00 GMT  
 Polymorphic list in C, kind of



Quote:
>if i use something like
>union eee
>{
>    char   b8;
>    long   b32;
>    double b64;
>} *rrr;
>then a sizeof of eee will be 64 (the double). Every rrr++; will increase
>the pointer with 4 bytes. When i use the char for a bytecode, i will
>have 3 bytes of empty storage. How do I "pack" all these types, so I
>can  eliminate the loss of space?

If you want to use simple code (e.g. sp++->b32) to access the stack,
then you simply have to tolerate the padding, which in many cases is
necessary for alignment anyway (depends on architecture).  However,
if you're willing to do a bit more work for stack access, you can store
the data packed on the stack, and access it via functions that assemble/
disassemble longer objects one byte at a time.  The assembly is often
done using a union, something along these lines:
        #define MAX(a,b) ((a) > (b) ? (a) : (b))
        union { char a[MAX(sizeof(long),sizeof(double))]; long l; double d; } u;
        char stack[STACKSIZE], *sp = stack;
        void push_long( long val, char **spp ) {
                size_t n = sizeof(long);
                u.l = val;
                while ( n-- > 0 )
                        *(*spp)++ = u.a[n];     // next byte
        }
        long pop_long( char **spp ) {
                size_t n = sizeof(long);
                while ( n-- > 0 )
                        u.a[n] = *--(*spp);     // next byte
                return u.l;
        }
        ...
        // to increment value on top of stack:
        long i = pop_long( &sp );   // get long value from packed stack
        ++i;                                    // perform desired operation
        push long( i, &sp );                // put long result onto packed stack

Another common approach dispenses with the union:
        unsigned char stack[STACKSIZE], *sp = stack;
        void push_long( long val, unsigned char **spp ) {
                size_t n;
                for ( n = sizeof(long); n > 0; --n ) {
                        *(*spp)++ = val & (unsigned char)~0;        // next byte
                        val >>= CHAR_BITS;
                }
        }
        long pop_long( unsigned char **spp ) {
                size_t n;
                unsigned long val = 0;
                for ( n = sizeof(long); n > 0; --n )
                        val = (val << CHAR_BITS) | *--(*spp);     // next byte
                return (long)val;
        }

Probably this gives you enough of an idea to work out your own details.



Thu, 06 Jan 2000 03:00:00 GMT  
 Polymorphic list in C, kind of

Quote:

>I'm writing a simple VM for a even simpler language in C. When, for
>example the VM will execute :

>ipush 10
>iadd

>this will look like 0x34 (for ipush) 0x0000000A (for 10) and then 0x65
>for iadd. Then, the first instruction is a char, then an long and then a
>char again; therefor, the bytecode will consist of diffrent types at
>unpredictable locations. I've came up with this idea:

So you want something along the lines of x86 machine code, a byte array,
op codes which determine the length of the instruction and the placement
of operands in that instruction, however these instructions may be aligned
at any byte position.

The problem in C is that when you access an object with a particular type
(such as int) in memory that object has to be appropriately aligned. If
you have no alignment guarantees then you have to access your byte array
a byte at a time. One thing you haven't made clear is whether the size
and byte order of your operands is related to the C types for the particular
implementation you are compiling the code on, or whether it is defined in
a platform-independent way. The approach will be different depending on this.

Quote:
>#include <stdio.h>
>void main(void)

main returns int in C.

Quote:
>{

>unsigned char *s,*st;
>s=new unsigned char[64];

There is no new operator in C, either use malloc or be clear that you are
writing in C++ (which has its own newsgroup).

Quote:
>st=s;
>*s=(char) 'a';
>s++;

That's OK, you can write simply

 *s++='a';

Quote:
>*s=(int) 34;
>s+=2;

What this does is convert 34 to an int (it was already so the (int) cast has
no effect). It is then converted implicitly to an unsigned char and stored
in the unsigned char object that s points to. The overall effect is that
1 byte is written to.

This is a case that would benefit from the use of a macro e.g. if you defined
at the top of the code:

#define write_word(s, value) (*(s)++ = (unsigned)(value) & 0xff, \
                              *(s)++ = (unsigned)(value) >> 8)

you can then write in the code:

 write_word(s, 34);

Note that the macro is "unsafe" in the sense that it evaluates its arguments
more than once therefore you shouldn't use side-effects in the arguments. For
example

 write_word(s++, 34);

is not a good idea.

Quote:
>*s=(int) 45;

>printf("%c %d %d",*st,*(st+1),*(st+3));

*(st+1) and *(st+3) will again only read a single byte. Yoiu can define
another macro at the top:

#define get_word(s) (((s)[0] | ((unsigned)(s)[1] << 8)))

and write:

 printf("%c %u %u",*st,get_word(st+1),get_word(st+3));

Note that I've changed the type to unsigned since unsigned arithmetic is
better defined in this sort of situation.

Quote:
>}

--
-----------------------------------------


-----------------------------------------


Thu, 06 Jan 2000 03:00:00 GMT  
 Polymorphic list in C, kind of

Quote:


> > But as I said, don't do that.  Use a stack that is an array
> > of a union type, where the members of the union are the
> > various data types.

> thanks! but... :-)

> if i use something like

> union eee
> {
>         char   b8;
>         long   b32;
>         double b64;
> } *rrr;

> then a sizeof of eee will be 64 (the double). Every rrr++; will increase
> the pointer with 4 bytes. When i use the char for a bytecode, i will
> have 3 bytes of empty storage. How do I "pack" all these types, so I
> can  eliminate the loss of space?

What is the problem?  Incrementing rrr should add 4 bytes, of course,
why would you increment rrr?  I could understand if it were an array.
Depending on the last entry you should be doing one of the following:

*rrr->b8++
*rrr->b32++
*rrr->b64++

These will increment each individual amount.  Note: the union is
*always* the size of the largest component.

I might do the following though.

enum typeIdent {aChar, aLong, aDbl};

struct dataObject {
   typeIdent what_is_here;
   union {
        char b8;
        long b32;
        double b64;
   }

Quote:
} myObject;

This way you do not need to remember what you put in their last, just
check the typeIdent to find out what is there.

--
********************************************

********************************************
I've never had a humble opinion in my life.
If you're going to have one,
why bother to be humble about it?
                                Joan Baez



Thu, 06 Jan 2000 03:00:00 GMT  
 Polymorphic list in C, kind of

Quote:

> What is the problem?  Incrementing rrr should add 4 bytes, of course,
> why would you increment rrr?  I could understand if it were an array.
> Depending on the last entry you should be doing one of the following:

> *rrr->b8++
> *rrr->b32++
> *rrr->b64++

Yes, there is the problem. Since the "list" will be used for bytecode
for a VM, a jump in b8 would "un-syncronize" the bytecode's with it's
operands, and since it will be diffrent numbers of operands with
diffrent kinds of bytecodes, there will be trouble :-)

thanx anyway!




Fri, 07 Jan 2000 03:00:00 GMT  
 Polymorphic list in C, kind of



Quote:
>Note: the union is *always* the size of the largest component.

A compiler is permitted to pad it out to more than that. Consider for
example:

union {
    long l;
    char a[9];

Quote:
};

The largest component is 9 bytes. However say a long on the particular
platform has an alignment requirement of 4 bytes then the compiler will
probably pad the union out to 12 bytes.

--
-----------------------------------------


-----------------------------------------



Fri, 07 Jan 2000 03:00:00 GMT  
 Polymorphic list in C, kind of

Quote:
> I'm writing a simple VM for a even simpler language in C. When, for
> example the VM will execute :

> ipush 10
> iadd

> this will look like 0x34 (for ipush) 0x0000000A (for 10) and then 0x65
> for iadd. Then, the first instruction is a char, then an long and then a
> char again; therefor, the bytecode will consist of diffrent types at
> unpredictable locations. I've came up with this idea:
> #include <stdio.h>
> void main(void)

I presume you mean :

int main(void)

as we all know, having read the FAQ, right? ;-)

Quote:
> {

> unsigned char *s,*st;
> s=new unsigned char[64];
> st=s;
> *s=(char) 'a';
> s++;
> *s=(int) 34;
> s+=2;
> *s=(int) 45;

> printf("%c %d %d",*st,*(st+1),*(st+3));
> }

> but, it doesn't look very nice...
> Anyone got any better ideas for implementing this kind of polymorphic
> list? Thanks in advance.

My suggestion would be to use a union to store the opcodes. For example:

struct ByteCode {
  enum {CHAR, LONG} which;
  union {
    char cbyte;
    long lbyte;
    /* Whatever other definitions you need */
  } code;

Quote:
};

That way you can write a switch statement to print the value with
something like :

switch(bytecode[i].which) {
  case CHAR: printf("%c ", bytecode[i].code.cbyte); break;
  case LONG: printf("%d ", bytecode[i].code.lbyte); break;

Quote:
}

Admittedly it isn't tremendously elegant - this sort of thing is
a lot semantically neater in C++ - but it works.
--

"No one knew were were among you... until now."
[return address corrupted to remove spam. Remove leading asterisks]


Sun, 09 Jan 2000 03:00:00 GMT  
 
 [ 9 post ] 

 Relevant Pages 

1. looking for polymorphic lists

2. Newbie: separate big .cs file into small .cs files

3. porting STL to posix, what kind of header needed and is using std::list allowed

4. Difference in Polymorphic behavior of C++ and C#

5. Why non-polymorphic instance methods?

6. Difference in Polymorphic behavior of C++ and C#

7. Implementing a polymorphic collection

8. Polymorphic Recordset needed

9. Polymorphic Iterator

10. Polymorphic Views

11. How to show/call Form2.cs from Form1.cs ?

12. lists ....lists...lists...

 

 
Powered by phpBB® Forum Software