Getting Started Documentation Glish Learn More Programming Contact Us
Version 1.9 Build 1556
News FAQ
Search Home


next up previous contents index
Next: Available Glish Clients Up: The Glish Client Library Previous: The Proxy and ProxyStore Classes

Subsections



The Value Class

As noted above, the Value class is both rich and complex. It provides considerable functionality for manipulating Glish-style values. The following discussion is divided into constructing Value's, basic operations, type conversions, manipulating records, and accessing and assigning elements. This discussion does not include all of the member functions, as some of them are intended for use only by the Glish interpreter (which uses the Value class internally).


Constructing Value Objects

Value objects are constructed either from single scalars, in which case a one-element Value is created, or from arrays, in which case a multi-element Value is created.

To create a scalar Value, use one of the following:

    Value( glish_bool value )
    Value( byte value )
    Value( short value )
    Value( int value )
    Value( float value )
    Value( double value )
    Value( complex value )
    Value( dcomplex value )
    Value( const char* value )
These create single-element Value objects that correspond to the Glish types boolean, byte, short, integer, float, double, complex, dcomplex, and string. Note that in all cases (including the string constructor) the value used to initialize the object is copied.

The C++ glish_bool type is an enumerated type with two constants, glish_true (= 1) and glish_false (= 0). This enumeration is defined in Glish/glish.h. This header file is automatically included by Glish/Value.h.

The C++ byte type holds an unsigned byte. It is also defined in Glish/glish.h.

The C++ complex and dcomplex types define single- and double-precision complex types. The types are defined in the header Glish/Complex.h, which is automatically included by Glish/Value.h (see above).

The Value class also has a copy constructor:

    Value( const Value &v )
Glish has two systems of reference counting going on at the same time. One is an external sort of reference counting that is handled by the Ref() and Unref() functions (see § 15.1). This sort of reference counting manages a whole value. Internally, though, the Value class has a second type of reference counting. This copy constructor uses this sort of reference counting to avoid copying the underlying data. The reference for the underlying data is incremented and a copy is made only when a write access is made to the underlying memory. This is how copy-on-write is implemented in Glish. (See § 3.10.) In some of the following functions, there is a flag to indicate if the access is read only or modify.

To create a new, empty record use:

    Value* create_record()
The fields of the new record can then be set using the functions discussed in § 15.7.4.

To create a multi-element Value, use one of the following:

    Value( glish_bool value[], int num_elements,
           array_storage_type storage = TAKE_OVER_ARRAY )
    Value( byte value[], int num_elements,
           array_storage_type storage = TAKE_OVER_ARRAY )
    Value( short value[], int num_elements,
           array_storage_type storage = TAKE_OVER_ARRAY )
    Value( int value[], int num_elements,
           array_storage_type storage = TAKE_OVER_ARRAY )
    Value( float value[], int num_elements,
           array_storage_type storage = TAKE_OVER_ARRAY )
    Value( double value[], int num_elements,
           array_storage_type storage = TAKE_OVER_ARRAY )
    Value( complex value[], int num_elements,
           array_storage_type storage = TAKE_OVER_ARRAY )
    Value( dcomplex value[], int num_elements,
           array_storage_type storage = TAKE_OVER_ARRAY )
    Value( const char* value[], int num_elements,
           array_storage_type storage = TAKE_OVER_ARRAY )
Each of these constructors takes a pointer (array) of one of the types discussed above, the number of elements in the array, and an optional argument indicating to what degree that array now ``belongs" to the Value object.

This last argument defaults to TAKE_OVER_ARRAY, which informs the Value object it can do whatever it wants with the array, including resize it (via a call to realloc()) and delete it when done using it. Thus if TAKE_OVER_ARRAY is used the array must have been dynamically allocated. The following is erroneous:

    int foo[50];
    Value* v = new Value( foo, 50 );
because foo is not a dynamically allocated array. This, too, is illegal:
    int* foo = new int[50];
    Value* v = new Value( foo, 50 );
    delete foo;
because foo now belongs to v and should not be deleted by anyone else.

The storage argument can also be COPY_ARRAY, in which case the Value object uses a copy of the entire array instead of the original array (string Value's copy each string element of the string array, too), or PRESERVE_ARRAY, in which case the Value object uses the array as is but does not attempt to grow it or delete it. If the Value object needs to alter a PRESERVE_ARRAY array, it first copies it instead.

Presently, COPY_ARRAY and PRESERVE_ARRAY have the same effect, i.e. a copy is made immediately. The intended PRESERVE_ARRAY functionality was not pushed through when copy-on-write values were added. In the future, this may be corrected, but PRESERVE_ARRAY seems problematic. When using either COPY_ARRAY or PRESERVE_ARRAY, you must consider efficiency.


Basic Value Operations

The Value class provides the following basic member functions:

glish_type Type() const
returns the Value's type, which is generally one of:
    TYPE_BOOL           TYPE_BYTE               TYPE_SHORT
    TYPE_INT            TYPE_FLOAT              TYPE_DOUBLE
    TYPE_COMPLEX        TYPE_DCOMPLEX
    TYPE_RECORD         TYPE_STRING             TYPE_ERROR
These represent the basic Glish types. TYPE_ERROR indicates a problem. In addition to these types which are generally used, there are a couple of other types which could be encountered:
    TYPE_REF            TYPE_SUBVEC_REF         TYPE_FAIL
These types are generally only used by the Glish interpreter, but are accessible via the Value class member functions. The two remaining types in the glish_type enumeration are currently only available to the interpreter. They are:
    TYPE_AGENT          TYPE_FUNC
Perhaps at some point these will be available to clients too, but presently neither of these types can be passed to/from a client.

unsigned int Length() const
returns the number of elements in the Value (if an array), the number of fields (if a record), or 1 (if a reference, an agent, or a function).

int IsNumeric() const
returns true if the Value is numeric (boolean, byte, short, integer, float, or double), false otherwise.

int IntVal( int n = 1, Str &err = g_err ) const
treats the Value as an integer type and returns the n'th element converted to the C++ int type. n = 1 corresponds to the first element of the Value.

If the Value is a reference then it is first dereferenced.

If n is out-of-bounds (less than 1 or greater than the number of elements) then an error is generated and 0 returned. If the Value is not numeric then an error is generated and 0 returned.

If an error does occur, the error string is returned through the err parameter. It indicates what sort of error occurred. Typically the error parameter is most useful for the interpreter, but can also be used by clients. It is used as follows:

    Str err;
    int start = val->IntVal(1,err);
    if ( err.chars() )
        {
        ... handle error ...
        }

There are anaglous functions for each of the basic types, and each is very similar to IntVal():

    glish_bool BoolVal( int n = 1, Str &err = g_err ) const
    byte ByteVal( int n = 1, Str &err = g_err ) const
    short ShortVal( int n = 1, Str &err = g_err ) const
    float FloatVal( int n = 1, Str &err = g_err ) const
    double DoubleVal( int n = 1, Str &err = g_err ) const
    complex ComplexVal( int n = 1, Str &err = g_err ) const
    dcomplex DcomplexVal( int n = 1, Str &err = g_err ) const
You may have noted the absence of StringVal(). This is because StringVal() is different; see the next item.

char* StringVal(
char sep = ' ', unsigned int max_elements = 0, int use_attr = 0, Str &err = glish_errno ) const returns a string representation of the Value object regardless of the type of the object. The optional sep argument indicates what character should be used to separate adjacent elements. For non-string arrays of more than one element the result is wrapped in []'s. For example, an array of the first three positive integers results in a string of "[1 2 3]" being returned.

The optional useAttr argument indicates, if true, that the attributes (see § 3.5) of the value should be used in the formatting of the result. The most common example is the shape attribute which is used to format the value multi-dimensional array.

The optional max_elements argument limits the number of elements used to create the result. The 0 indicates that all of the elements should be used, but if this is set to 3 only the first three elements are used. This prevents generating huge strings.

The string returned is dynamically allocated and should be delete'd when done with.

int* IntPtr( int modify=1 ) const
returns a pointer to the underlying C++ int array of an integer Value object. This provides direct access to the underlying array. The modify argument indicates if the caller intends to modify the array. If modify is set to a non-zero value and this data is shared with other values (see § 15.7.1), then a copy is made. If modify is set to zero, no copy is made.

These elements can then be directly manipulated (there are, of course, Length() elements present). To modify the array though, modify must be non-zero. If the Value is not of type integer then a fatal error results. It is important to realize that for Values whose type is TYPE_SUBVEC_REF or TYPE_SUBVEC_CONST, the pointer returned by IntPtr() points to the Value underlying the ``sub-vector'' reference.

Analogous functions for numeric and string values are:

    glish_bool* BoolPtr( int modify=1 ) const;
    byte* BytePtr( int modify=1 ) const;
    short* ShortPtr( int modify=1 ) const;
    float* FloatPtr( int modify=1 ) const;
    double* DoublePtr( int modify=1 ) const;
    complex* ComplexPtr( int modify=1 ) const;
    double* DcomplexPtr( int modify=1 ) const;
    charptr* StringPtr( int modify=1 ) const;

int* IntPtr( int modify=1 )
is similar to the preceding IntPtr() member function except that if Value's type is not integer then it is first Polymorph()'d (see § 15.7.3, page [*], below) to integer. Analogous functions are provided for the other numeric and string types.

int IsRef() const
returns true if the Value is a reference (either ref or const), false otherwise.

int IsVecRef() const
returns true if the Value is a ``sub-vector'' reference (either ref or const), false otherwise.

int IsConst() const
returns true if the Value is a constant value, false otherwise.

Value* Deref()
dereferences the Value until it is no longer a reference.

const Value* Deref() const
is the same as the previous except it is a const member function returning a const Value pointer (here const refers to the C++ notion of ``constant pointer", not the Glish type of ``constant reference").

Value* VecRefDeref()
dereferences the Value until it is no longer a ``sub-vector'' reference.

const Value* VecRefDeref() const
is the same as the previous except it is a const member function returning a const Value pointer.


Type Conversions

The Value class provides the following member functions for manipulating the type of a Value object:

void Polymorph( glish_type new_type )
changes the Value from its present type to new_type, which is one of the types listed in the previous section for the Type() member function. For example,
    v->Polymorph( TYPE_INTEGER );
changes v from its present type to integer, coercing all of its elements to the C++ int type.

The Value's current and new types must be compatible. Presently this means that they either must be the same or both must be numeric. This restriction will be eased in the future.

int* CoerceToIntArray(
int& is_copy, int size, int* result = 0 ) const returns a C++ int pointer to an integer representation of the Value's elements.

If the Value's type is integer and size equals the number of elements in the Value, then the Value's underlying array is returned (as though IntPtr() had been called; see the preceding section) and is_copy will be false. In this case the returned pointer must not be delete'd.

If the type is a different numeric type, or size differs from the number of elements, then a copy of its first size elements, coerced to int, is returned, and is_copy will be true. In this case it is the caller's responsibility to delete the returned pointer when done with it.

If the type is non-numeric then a fatal error is generated.

If the Value has only 1 element and size is greater than 1, then size copies of that one element coerced to int.

If result is non-nil then the result is placed in result (as well as returned by the function), and is_copy will always be true.

Analogous functions for type conversions to numeric types are provided:

    glish_bool* CoerceToBoolArray( int& is_copy,
                int size, glish_bool* result = 0 ) const;
    byte* CoerceToByteArray( int& is_copy,
                int size, byte* result = 0 ) const;
    short* CoerceToShortArray( int& is_copy,
                int size, short* result = 0 ) const;
    float* CoerceToFloatArray( int& is_copy,
                int size, float* result = 0 ) const;
    double* CoerceToDoubleArray( int& is_copy,
                int size, double* result = 0 ) const;
    complex* CoerceToComplexArray( int& is_copy,
                int size, complex* result = 0 ) const;
    dcomplex* CoerceToDcomplexArray( int& is_copy,
                int size, dcomplex* result = 0 ) const;

const char* CoerceToStringArray(
int& is_copy, int size, const char* result = 0 ) const is similar to CoerceToIntArray() except that the Value's type must be string. This restriction will be eased in the future to allow numeric types, too.

Manipulating Records

Very often record's are used as Glish event values, so it's important that it be easy to manipulate them. The Value class provides a number of member functions for accessing and setting record fields. The most commonly used ones are listed first:

Value* Field( const char field[] )
returns the record field named ``field", or nil if either the field doesn't exist or the Value object is not a record.

Value* Field( const char field[], glish_type t )
is the same as the preceding Field() function except the field is polymorphed to type t. (See § 15.7.3, page [*], above.)

int* FieldIntPtr(
const char field[], int& len, int modify=1 ) returns a pointer to the underlying values of the given field, polymorphed to integer. The number of elements in the field is returned in len.

As with IntPtr() (see § 15.7.2), the modify flag must be set to a non-zero value if the returned array will be modified. Thus any necessary copies are made of the underlying data before any modifications are made, i.e. it does the copy-on-write mechanics.

Analogous functions are available for the other numeric types, as well as string:

  glish_bool* FieldBoolPtr( const char field[],
                            int& len, int modify=1 )
  byte* FieldBytePtr( const char field[],
                            int& len, int modify=1 )
  short* FieldShortPtr( const char field[],
                            int& len, int modify=1 )
  float* FieldFloatPtr( const char field[],
                            int& len, int modify=1 )
  double* FieldDoublePtr( const char field[],
                            int& len, int modify=1 )
  complex* FieldComplexPtr( const char field[],
                            int& len, int modify=1 )
  dcomplex* FieldDcomplexPtr( const char field[],
                            int& len, int modify=1 )
  const char* FieldStringPtr( const char field[],
                            int& len, int modify=1 )

These functions return a nil pointer if the Value object is not a record or doesn't contain the given field. In these cases, len is not modified.

int FieldVal(
const char field[], int& val, int n = 1, Str &err = g_err ) looks for a field with the given name. If present, returns true, and in the second argument (val) returns the scalar value corresponding to the n'th element of that field coerced to int. n=1 corresponds to the first element of the field.

If the field is not present, returns false, and val is unchanged.

As with IntVal() (see § 15.7.2), if an error occurs, it is returned through the optional err parameter.

Analogous functions are available for the other numeric types.

int FieldVal(
const char field[], char*& val ) is similar to the functions described in the previous item, but rather than just coercing one element of the field to string, it returns a string representation of the entire value (as described for the StringVal() member function in § 15.7.2, page [*], above). Thus the value returned in val is a newly allocated string which should be delete'd when done with.

void SetField(
const char field[], int value ) sets (or changes, if already present) the given field in a record to a scalar integer value.

Analogous functions are available for the other numeric types, as well as const char*, which copies the contents of the passed string.

For example, the following:

    Value* r = create_record();
    r->SetField( "x", 3 );
    r->SetField( "y", "hi there" );
    r->SetField( "z", glish_false );
is equivalent to the Glish statement:
    r := [x=3, y='hi there', z=F]

If the Value object is not a record then a fatal error is generated.

void SetField(
const char field[], int value[], int num_elements, array_storage_type storage = TAKE_OVER_ARRAY ) is a similar member function for adding a multi-element field to a record. The first argument names the field to be added, and the remaining arguments are identical to those for the integer array constructor discussed in § 15.7.1, page [*], above.

Exactly analogous member functions are available for creating arrays of the other numeric types, as well as string.

Again, if the Value object is not a record then a fatal error is generated.

In addition to these member functions, several others are available, primarily for when you want to deal with record fields as Value objects themselves:

Value* Field( const char field[] )
returns a pointer to the given field of a Value object. If the Value object is not a record, or does not contain the given field, the function returns a nil pointer instead.

Value* Field( const Value* index )
is similar to the preceding function except that index is itself a Value object (presumably string-valued).

Value* Field( const char field[], glish_type t )
is similar to the first member function above except a Glish type such as TYPE_FLOAT is specified as well, and the field if present is polymorphed to that type prior to return.

void SetField( const char field[], Value* value )
assigns the given field to the given Value object. The assigned Value object may or may not be copied by the member function, but in any case upon return it is safe for the caller to Unref() the assigned value (and the caller should do so if it will not be used further).

If the called Value object is not a record then a fatal error is generated.

Value* NthField( int n )
returns a pointer to the n'th field in the record, with the first field that was added to the record numbered 1. Returns a nil pointer if n is out of range or the Value object is not a record.

const Value* NthField( int n ) const
is a const version (in the C++ sense of the term) of the preceding member function.

const char* NthFieldName(
int n ) const returns a non-modifiable pointer to the n'th field's name. Returns a nil pointer under the same circumstances as NthField().

char* NewFieldName()
returns a copy of a unique field name; that is, a field name not already present in the given record. Returns a nil pointer if the object is not a record.

The name will have an embedded ``*" character indicating it is an internal name.

const Value*
ExistingRecordElement( const char field[] ) const is directly analogous to the first Field() function above except it works for const (in the C++ sense) Value objects and returns a const pointer. Also available is:
    const Value*
    ExistingRecordElement( const Value* index ) const
identical to the second form of Field().

If the field does not exist or the object is not a record then these functions return a fail value, see § 5.7.

Accessing and Assigning Elements

Usually the Value class is used to ``wrap" Glish values around C, C++, or FORTRAN data so those values may be communicated as events. As such, the emphasis in using the class is on convenient wrapping and unwrapping. The following discussion involves using the class to manipulate Value objects similar to how they can be manipulated in the Glish language. I'm interested in hearing from users who find they would like more such functionality.

Value* operator[]( const Value* index ) const
indexes the Value object with the given index, which is itself a Value object. The index should either be numeric (in which case it is treated as discussed in § 3.6, page [*], above) or string (in which case the called object should be a record, and is indexed as explained in § 3.4.3, page [*], above).

This function returns a newly created Value object representing the designated elements of the original object. The caller should Unref() this new object when done with it.

Any errors result in messages being written to stderr and a return value of a copy of the F constant.

void AssignElements(
const Value* index, Value* value ) assigns the elements designated by index to value. index is treated as discussed for the preceding member function.

AssignElements() will either take over (by Ref()'ing) or copying what it needs from value, so after the function returns the caller should discard value by Unref()'ing it when done with it.

For example, the sequence:

    int* x = new int[3];
    x[0] = 3; x[1] = 5; x[2] = 7;
    Value* xval = new Value( x, 3 );
    Value* index = new Value( 2 );
    Value* new_val = new Value( 10 );
    xval->AssignElements( index, value );
    Unref( new_val );
is equivalent to the Glish statement:
    x := [3, 5, 7]
    x[2] := 10

void TakeValue( Value* new_value )
discards a Value object's present value and instead uses new_value for its value. The caller should then Unref() new_value when otherwise done using it.

Accessing and Assigning Attributes

Attributes are also accessible from the Value class. They are accessed using the following functions:

const Value* HasAttribute( const char label[] ) const
returns a non-modifiable Value pointer which points to the exsiting attribute specified by label if it exists, otherwise it returns 0.

void AssignAttribute( const char* label, Value* value )
assigns the attribute specified by label to have the value value. Attribute label is created if it didn't already exist.

const Value *ExistingAttribute( const char label[] ) const
returns a non-modifiable Value pointer which points to the exsiting attribute specified by label. Returns false_value if attribute label does not exist.

const Value *ExistingAttribute( const Value* label ) const
returns a non-modifiable Value pointer which points to the exsiting attribute specified by label. Returns false_value if attribute label does not exist.

Value *GetOrCreateAttribute( const char label[] )
returns a modifiable Value pointer which points to the attribute specified by label. If the label attribute didn't previously exist, it is created.

Value *GetOrCreateAttribute( const Value *label )
returns a modifiable Value pointer which points to the attribute specified by label. If the label attribute didn't previously exist, it is created.

void DeleteAttribute( const char label[] )
deletes the attribute specified by label if it exists.

void DeleteAttribute( const Value* label )
deletes the attribute specified by label if it exists.

With these functions, you can manage the attributes which are associated with a particular Value.


next up previous contents index
Next: Available Glish Clients Up: The Glish Client Library Previous: The Proxy and ProxyStore Classes   Contents   Index
Please send questions or comments about AIPS++ to aips2-request@nrao.edu.
Copyright © 1995-2000 Associated Universities Inc., Washington, D.C.

Return to AIPS++ Home Page
2006-10-15