www.delorie.com/gnu/docs/smalltalk/gst_34.html   search  
 
Buy GNU books!


GNU Smalltalk User's Guide

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

3.3 The C data type manipulation system

CType is a class used to represent C data types themselves (no storage, just the type). There are subclasses called things like CmumbleCType. The instances can answer their size and alignment. Their valueType is the underlying type of data. It's either an integer, which is interpreted by the interpreter as the scalar type, or the underlying element type, which is another CType subclass instance.

To make life easier, there are global variables which hold onto instances of CScalarCType: they are called CmumbleType (like CIntType, not like CIntCType), and can be used wherever a C datatype is used. If you had an array of strings, the elements would be CStringType's (a specific instance of CScalarCType).

CObject is the base class of the instances of C data. It has a subclass called CScalar, which has subclasses called Cmumble. These subclasses can answer size and alignment information.

Instances of CObject holds a pointer to a C type variable. The variable have been allocated from Smalltalk by doing type new, where type is a CType subclass instance, or it may have been returned through the C callout mechanism as a return value. Thinking about this facet of the implementation (that CObject point to C objects) tends to confuse me when I'm thinking about having CObjects which are, say, of type long*... so I try to think of CObject as just representing a C data object and not thinking about the implementation.

The only place where this is apparent is in the availability of methods like + anInteger which returns a CObject which is higher in memory by anInteger times the size of each item. There is also - which acts like + if it is given an integer as its parameter. If a CObject is given, it returns the difference between the two pointers. incr, decr, incrBy:, decrBy: adjust the string either forward or backward, by either 1 or n characters. Only the pointer to the string is changed; the actual characters in the string remain untouched.

CObjects can be divided into two families, scalars and non-scalars, just like C data types. Scalars fetch a Smalltalk object when sent the value message, and change their value when sent the value: message. Non-scalars do not support these two messages.

replaceWith: aString replaces the string the instance points to with the new string. Actually, it copies the bytes from the Smalltalk String instance aString into the C string object, and null terminates. Be sure that the C string has enough room! You can also use a Smalltalk ByteArray as the data source.

Non-scalars include instances of CArray, CPtr and subclasses of CStruct and CUnion.

CPtrs and CArrays get their underlying element type through a CType subclass instance which is associated with the CArray or CPtr instance.

CPtr's also have value and value: which get or change the underlying value that's pointed to. CString is a subclass that answers a Smalltalk String when sent value, and automatically allocates storage to copy and null-terminate a Smalltalk String when sent value:.

Finally, there are CStruct and CUnion, which are abstract subclasses of CObject(13). In the following I will refer to CStruct, but the same considerations apply to CUnion as well, with the only difference that CUnions of course implement the semantics of a C union.

These classes provide direct access to C data structures including

Here is an example struct decl in C:
 
struct audio_prinfo {
    unsigned    channels;
    unsigned    precision;
    unsigned    encoding;
    unsigned    gain;
    unsigned    port;
    unsigned    _xxx[4];
    unsigned    samples;
    unsigned    eof;
    unsigned char       pause;
    unsigned char       error;
    unsigned char       waiting;
    unsigned char       _ccc[3];
    unsigned char       open;
    unsigned char       active;
};

struct audio_info {
    audio_prinfo_t      play;
    audio_prinfo_t      record;
    unsigned    monitor_gain;
    unsigned    _yyy[4];
};

And here is a Smalltalk equivalent decision:
 
CStruct subclass: #AudioPrinfo
        declaration: #( (sampleRate uLong)
                        (channels uLong)
                        (precision uLong)
                        (encoding uLong)
                        (gain uLong)
                        (port uLong)
                        (xxx (array uLong 4))
                        (samples uLong)
                        (eof uLong)
                        (pause uChar)
                        (error uChar)
                        (waiting uChar)
                        (ccc (array uChar 3))
                        (open uChar)
                        (active uChar))
        classVariableNames: ''
        poolDictionaries: ''
        category: 'C interface-Audio'
!

CStruct subclass: #AudioInfo
        declaration: #( (play #{AudioPrinfo} )
                        (record #{AudioPrinfo} )
                        (monitorGain uLong)
                        (yyy (array uLong 4)))
        classVariableNames: ''
        poolDictionaries: ''
        category: 'C interface-Audio'
!

This creates two new subclasses of CStruct called AudioPrinfo and AudioInfo, with the given fields. The syntax is the same as for creating standard subclasses, with the instanceVariableNames replaced by declaration(14). You can make C functions return CObjects that are instances of these classes by passing AudioPrinfo type as the parameter to the returning: keyword.

AudioPrinfo has methods defined on it like:
 
    #sampleRate
    #channels
    #precision
    #encoding

etc. These access the various data members. The array element accessors (xxx, ccc) just return a pointer to the array itself.

For simple scalar types, just list the type name after the variable. Here's the set of scalars names, as defined in `kernel/CStruct.st':
 
   #long                   CLong
   #uLong                  CULong
   #ulong                  CULong
   #byte                   CByte
   #char                   CChar
   #uChar                  CUChar
   #uchar                  CUChar
   #short                  CShort
   #uShort                 CUShort
   #ushort                 CUShort
   #int                    CInt
   #uInt                   CUInt
   #uint                   CUInt
   #float                  CFloat
   #double                 CDouble
   #string                 CString
   #smalltalk              CSmalltalk
   #{...}                  A given subclass of CObject

The #{...} syntax is not in the Blue Book, but it is present in GNU Smalltalk and other Smalltalks; it returns an Association object corresponding to a global variable.

To have a pointer to a type, use something like:
 
        (example (ptr long))

To have an array pointer of size size, use:
 
        (example (array string size))

Note that this maps to char *example[size] in C.

The objects returned by using the fields are CObjects; there is no implicit value fetching currently. For example, suppose you somehow got ahold of an instance of class AudioPrinfo as described above (the instance is a CObject subclass and points to a real C structure somewhere). Let's say you stored this object in variable audioInfo. To get the current gain value, do
 
    audioInfo gain value

to change the gain value in the structure, do
 
    audioInfo gain value: 255

The structure member message just answers a CObject instance, so you can hang onto it to directly refer to that structure member, or you can use the value or value: methods to access or change the value of the member.

Note that this is the same kind of access you get if you use the addressAt: method on CStrings or CArrays or CPtrs: they return a CObject which points to a C object of the right type and you need to use value and value: to access and modify the actual C variable.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

  webmaster     delorie software   privacy  
  Copyright 2003   by The Free Software Foundation     Updated Jun 2003