Getting Started | Documentation | Glish | Learn More | Programming | Contact Us |
Version 1.9 Build 1556 |
|
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).
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.
The Value class provides the following basic member functions:
TYPE_BOOL TYPE_BYTE TYPE_SHORT TYPE_INT TYPE_FLOAT TYPE_DOUBLE TYPE_COMPLEX TYPE_DCOMPLEX TYPE_RECORD TYPE_STRING TYPE_ERRORThese 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_FAILThese 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_FUNCPerhaps at some point these will be available to clients too, but presently neither of these types can be passed to/from a client.
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 ) constYou may have noted the absence of StringVal(). This is because StringVal() is different; see the next item.
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.
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;
The Value class provides the following member functions for manipulating the type of a Value object:
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.
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;
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:
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.
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.
Analogous functions are available for the other numeric types, as well as const char*, which copies the contents of the passed string.
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.
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:
If the called Value object is not a record then a fatal error is generated.
The name will have an embedded ``*" character indicating it is an internal name.
const Value* ExistingRecordElement( const Value* index ) constidentical 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.
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.
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.
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.
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
Attributes are also accessible from the Value class. They are accessed using the following functions:
With these functions, you can manage the attributes which are associated with a particular Value.