This is the mail archive of the guile@cygnus.com mailing list for the guile project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: structs, records, SMOBs etc.


> Since Guile is foremost an embedded language intended for extension of
> an application, it is important to provide good support for
> manipulation of application data.

Agreed.

The current limitations that I'm experiencing are
[1] I'm writing a lot of scm->C and C->scm translations into everything,
    they don't seem to make much difference to the speed (though they
    can't be helping) but they do obfuscate the code and forgetting to
    include a translation (especially C->scm) is usually instant segmentation
    violation signals.
[2] I'm writing a lot of scheme entry points that are essentially the same
    as C entry points but they check the types and do translations scm->C
    then call the C entry point. OK, I accept that the entry point has to
    be there but this feeling od deja-vu keeps coming to me.

> Therefore I'd like if the new record type could be "mapped" over an
> application struct and if it would enable Scheme level access to
> binary data.

Having a way of systematically mapping function calls over application
functions would also be nice, could the two mechanisms be combined?

> Maybe it should also be possible to put a pointer to a record field
> spec array inside another record field spec array to support the
> situation when a struct contains another struct, or when it contains a
> pointer to another struct.

>From the point of view of usability, I prefer a variable length argument
list. I also like the idea of storing fields together with their offsets
into the structure and forgetting about `opaque' fields completely --
just don't bother mapping them. Here's my little demo program:

#include <stdio.h>
#include <stdarg.h>

typedef unsigned long SCM;
typedef unsigned long special_t;

/*
 * NOTES:
 *
 * The LEN field is only used for array length.
 * Opaque members of the struct are not mapped at all.
 * SCM is a type like any other.
 */
typedef enum
{
    SCM_MRT_LENMASK    = 0x0000ffff,
    SCM_MRT_TYPEMASK   = 0x00ff0000,
    SCM_MRT_FLAGMASK   = 0x0f000000,
    SCM_MRT_CHAR       = 0x00010000,
    SCM_MRT_UCHAR      = 0x00020000,
    SCM_MRT_SHORT      = 0x00030000,
    SCM_MRT_USHORT     = 0x00040000,
    SCM_MRT_INT        = 0x00050000,
    SCM_MRT_UINT       = 0x00060000,
    SCM_MRT_LONG       = 0x00070000,
    SCM_MRT_ULONG      = 0x00080000,
    SCM_MRT_FLOAT      = 0x00090000,
    SCM_MRT_DOUBLE     = 0x000A0000,
    SCM_MRT_COMPLEX    = 0x000B0000,
    SCM_MRT_SCM        = 0x000C0000,
    SCM_MRT_READONLY   = 0x01000000
} scm_record_field_flags_t;

void scm_make_record_type( char *name, void *base,
                           SCM ( *gc_mark_hook )( void *base ), ... )
{
    va_list ap;
    char *fieldname;
    int offset;
    unsigned long typestuff;

    printf( "structure = %s\n", name );
    va_start( ap, gc_mark_hook );
    while( fieldname = va_arg( ap, char * ))
    {
        offset = va_arg( ap, void * ) - base;
        typestuff = va_arg( ap, unsigned long );
        printf( "  %s at %ld type %08X\n", fieldname, offset, typestuff );
    }
    va_end( ap );
}

/* ---------------------------------------------------------------------- */

struct demo_S
{
    double x, y;
    int colour;
    SCM attribute_list;
    unsigned long flags;
    special_t *special;
    unsigned char sequence[ 23 ];
};

void init_demo_struct( void )
{
    struct demo_S p;

    scm_make_record_type( "demo",              &p,       0,
                          "x",                 &p.x,
                          SCM_MRT_DOUBLE | SCM_MRT_READONLY,
                          "y",                 &p.y,
                          SCM_MRT_DOUBLE | SCM_MRT_READONLY,
                          "colour",            &p.colour,
                          SCM_MRT_INT,
                          "attribute_list",    &p.attribute_list,
                          SCM_MRT_SCM,
                          "flags",             &p.flags,
                          SCM_MRT_ULONG,
                          "sequence",          &p.sequence,
                          SCM_MRT_UCHAR | 23,
                          0 );
}

main()
{
    init_demo_struct();
}


> If the gc_hook is NULL, a standard function will be used (which looks
> after SCM_FT_SCM_GC fields).  If the user wants special treatment he
> can supply his own gc routine.

The default function can be a `conservative' scan of the struct looking
for SCM objects. This should be reasonably reliable and fast and will be
easy for people who just want to get something working (i.e. everyone).

> The record field spec would be "compiled" by make_record_type into a
> format which is maximally efficient for the garbage collector.  This
> compiled spec would be stored in the "type object".

May I point out that only SCM objects need garbage collection.
If a user is keeping doubles in a structure then those doubles have
a known home and whenever the structure is valid, so are the doubles.

Marking an object as type SCM_MRT_SCM is enough to say that it needs
garbage collection (or rather, that it needs to be considered by the
mark phase).

Two more points that need consideration, structs may point to dynamically
allocated memory that in turn may contain SCM objects that need marking
but how much memory is allocated will vary. We need a way for the
mark phase to recognise pointers to sub-structures (which will often
be NULL pointers). If you do need a special marking routine to do
this, better to pass it the pointer to the struct rather than a SCM type.

The other point is that removing the struct may (will often) involve
deallocation of resources, depending on how the struct gets deallocated
it may need a deallocation routing (at which point it is pretty much
an SMOB).

	- Tel