On Sat, 30 May 2015 01:22:27 -0400, Raimond Dragomir
<
raimond....@gmail.com> wrote:
>> > C structs are powerful, the fields are associated with the object.
>>
>> You must specify the struct name and field in C to access the field.
>> That's true with both of the component selection operators in C:
>> '.' and '->'
>>
>> So, what do you mean by "the fields are associated with the object?"
>
> No you don't specify the struct name, you specify the object name which
> happens to have the type of that struct.
>
I understand what you're saying.
FYI, I refer to mystruct1, mystruct2 as the type of the struct, whereas
obj1, obj2 are the names of the structs.
> struct mystruct1 { int x; int y; }
> struct mystruct2 { int y; int x; }
> struct mystruct1 obj1;
> struct mystruct2 obj2;
> obj1.x
> obj2.x
>
> Tell me how can you do that in Forth.
You're familiar with the offsetof() macro for C, yes?
I.e., every C compiler must have one or it's equivalent.
It's usually coded similarly to this:
#define offsetof(s,e) ((int)((char *)&((s *)0)->e-(char *)0))
That casts NULL, represented syntactically as '0' here, to a
pointer to the start of 's' which is used to index 'e'. The
address of s->e is taken which is relative to NULL and subtracts
NULL the start of the struct to provide the offset of 'e'
relative to the start of the struct.
AIUI, Forth usually creates a bunch of custom words for each
array for each offset. Those are like offsetof() which have
hardcoded offset value for 'e'. In C, all objects are mapped
onto an array of bytes. So, we can use arrays for structs
in Forth, like C, and build up C like functionality from there.
E.g., these take an address for the start of the struct:
: OBJ1 ( a -- a+n ) 0 CELLS + ;
: OBJ2 ( a -- a+n ) 1 CELLS + ;
CREATE MYSTRUCT1 2 CELLS ALLOT
CREATE MYSTRUCT2 2 CELLS ALLOT
MYSTRUCT1 OBJ1 @
MYSTRUCT1 OBJ2 @
MYSTRUCT2 OBJ1 @
MYSTRUCT2 OBJ2 @
Of course, you could put the @ in offset definitions:
: OBJ1 ( a -- i ) 0 CELLS + @ ;
: OBJ2 ( a -- i ) 1 CELLS + @ ;
CREATE MYSTRUCT1 2 CELLS ALLOT
CREATE MYSTRUCT2 2 CELLS ALLOT
MYSTRUCT1 OBJ1
MYSTRUCT1 OBJ2
MYSTRUCT2 OBJ1
MYSTRUCT2 OBJ2
Of course, you can define those to be their own words:
: MYSTRUCTS1.OBJ1 MYSTRUCTS1 OBJ1 ;
: MYSTRUCTS1.OBJ2 MYSTRUCTS1 OBJ2 ;
: MYSTRUCTS2.OBJ1 MYSTRUCTS2 OBJ1 ;
: MYSTRUCTS2.OBJ2 MYSTRUCTS2 OBJ2 ;
MYSTRUCTS1.OBJ1
MYSTRUCTS1.OBJ2
MYSTRUCTS2.OBJ1
MYSTRUCTS2.OBJ2
Creating a more generic version in Forth might go something
like:
: OBJ1 0 ;
: OBJ2 1 ;
: OFFSETOF ( a n -- i ) CELLS + @ ;
MYSTRUCTS1 OBJ1 OFFSETOF
MYSTRUCTS1 OBJ2 OFFSETOF
MYSTRUCTS2 OBJ1 OFFSETOF
MYSTRUCTS2 OBJ2 OFFSETOF
Or, if we rename OFFSETOF to '->' to be more C like:
: -> ( a n -- i ) CELLS + @ ;
MYSTRUCTS1 OBJ1 ->
MYSTRUCTS1 OBJ2 ->
MYSTRUCTS2 OBJ1 ->
MYSTRUCTS2 OBJ2 ->
Of course, those only work for CELL sized objects. You'd
need to simulate C's sizeof() functionality in Forth to fix
that. Forth doesn't have a typesystem, so that information
must be hardcoded by you ...
E.g.,
: OBJ1 0 1 ; \ cell size offset 0
: OBJ2 1 1 ; \ cell size offset 1
\ let's do some CHAR's too ...
: OBJC1 1 0 ; \ char size offset of 1
: OBJC2 3 0 ; \ char size offset of 3
The second value is a flag to indicate CELL sized object.
: -> ( a n x -- i ) IF CELLS THEN + @ ;
Of course, that doesn't know if the size of the object
to be fetched is a CELL or a CHAR, and so fetches a CELL
which we probably don't want for a byte sized object,
assuming it's a CHAR. Technically, Forth doesn't have
type information, but it has two data types: CELL and CHAR.
CELL serves as a large integer and as address pointers.
Strings are passed as addresses. So, we can assume if
not CELL then CHAR.
: -> ( a n x -- i ) IF CELLS + @ ELSE + C@ THEN ;
In full,
: -> ( a n x -- i ) IF CELLS + @ ELSE + C@ THEN ;
CREATE MYSTRUCT1 2 CELLS ALLOT
CREATE MYSTRUCT2 2 CELLS ALLOT
: OBJ1 0 1 ; \ cell size offset 0
: OBJ2 1 1 ; \ cell size offset 1
: OBJC1 1 0 ; \ byte size offset of 1
: OBJC3 3 0 ; \ byte size offset of 3
: MYSTRUCTS1.OBJ1 MYSTRUCTS1 OBJ1 -> ;
: MYSTRUCTS1.OBJ2 MYSTRUCTS1 OBJ2 -> ;
: MYSTRUCTS2.OBJ1 MYSTRUCTS2 OBJ1 -> ;
: MYSTRUCTS2.OBJ2 MYSTRUCTS2 OBJ2 -> ;
: MYSTRUCTS1.OBJC1 MYSTRUCTS1 OBJC1 -> ; \ char
: MYSTRUCTS1.OBJC3 MYSTRUCTS1 OBJC3 -> ; \ char
: MYSTRUCTS2.OBJC1 MYSTRUCTS2 OBJC1 -> ; \ char
: MYSTRUCTS2.OBJC3 MYSTRUCTS2 OBJC3 -> ; \ char
MYSTRUCTS1.OBJ1
MYSTRUCTS1.OBJ2
MYSTRUCTS2.OBJ1
MYSTRUCTS2.OBJ2
MYSTRUCTS1.OBJC1
MYSTRUCTS1.OBJC3
MYSTRUCTS2.OBJC1
MYSTRUCTS2.OBJC3
If you throw CREATE .. DOES> into the mix, you might be
able to automate the process of creating all or many of
those pre-liminary definitions. Hopefully, that's easy
to do for someone here who is skilled in Forth.
I.e., you could implement structs in Forth much like
rudimentary arrays with only two possible types for
each item, either CELL or CHAR . This is what they
are in C but with some sizeof() and offsetof()
information used by the compiler and additional types
thrown in to the mix.
http://www.forth.org/svfig/Len/arrays.htm
http://www.forth.com/starting-forth/sf8/sf8.html
> This is a price that Forth has to pay for it's simplicity.
Agreed.
As to whatever was going on in the rest of the thread with
Hugh, I haven't really read that.