Difference between revisions of "V4 Design: epicsTypes"

From EPICSWIKI
(Major changes to EpicsArray)
Line 134: Line 134:
</center>
</center>


<tt>epicsBuffer.h</tt> defines a generic interface <tt>EpicsBuffer</tt> to a buffer of octet data, and a buffer factory <tt>EpicsBufferFactory</tt> that provides a central registry of different implementations of <tt>EpicsBuffer</tt> and uses this to create instances of a particular buffer type on demand.
<tt>epicsBuffer.h</tt> defines a generic interface <tt>EpicsBuffer</tt> to a buffer of octet data, and  
 
A buffer factory <tt>EpicsBufferFactory</tt> provides a central registry of <tt>EpicsBufferCreator</tt> objects and uses these to create instances of a particular buffer type on demand.


     class EpicsBuffer { // interface
     class EpicsBuffer { // interface
    protected:
        virtual ~EpicsBuffer() = 0;
     public:
     public:
        virtual EpicsBufferCreator *creator() const = 0;
         virtual void reserve(epicsInt32 capacity) = 0;
         virtual void reserve(epicsInt32 capacity) = 0;
         virtual epicsInt32 capacity() const = 0;
         virtual epicsInt32 capacity() const = 0;
Line 143: Line 148:
         virtual epicsInt32 size() const = 0;
         virtual epicsInt32 size() const = 0;
         virtual epicsInt32 maxSize() const = 0;
         virtual epicsInt32 maxSize() const = 0;
        virtual void destruct() = 0;
         virtual bool mutable() const = 0;
         virtual bool mutable() const = 0;
         virtual bool isEqual(const EpicsBuffer &cmp) const = 0;
         virtual bool isEqual(const EpicsBuffer &cmp) const = 0;
Line 152: Line 156:
         virtual void expose(epicsInt32 offset, epicsInt32 &len,
         virtual void expose(epicsInt32 offset, epicsInt32 &len,
                             const epicsOctet *&pdata) const = 0;
                             const epicsOctet *&pdata) const = 0;
   
    friend class EpicsBufferCreator;
     };
     };


Line 160: Line 166:
* readonly - A contiguous array that already contains the desired data is provided at buffer initialization time, and cannot be modified. This buffer is for use with literal character strings, and probably won't be registered with the <tt>EpicsBufferFactory</tt>.
* readonly - A contiguous array that already contains the desired data is provided at buffer initialization time, and cannot be modified. This buffer is for use with literal character strings, and probably won't be registered with the <tt>EpicsBufferFactory</tt>.
* contiguous - The data is stored in a contiguous array of octets.
* contiguous - The data is stored in a contiguous array of octets.
* segmented - The data is stored in fixed-size chunks. This form should be used for buffers that are frequently allocated and released, or which often need to change in size.
* segmented - The data is stored in fixed-size chunks. This form should be used for buffers that are frequently created and released, or which often need to change in size.


Additional implementations may be provided for other purposes such as managing network buffers. There might be a need for a contiguous zero-terminated buffer type for code that regularly talks to C string handling routines.
Additional implementations may be provided for other purposes such as managing network buffers. There might be a need for a contiguous zero-terminated buffer type for code that regularly talks to C string handling routines.
Line 173: Line 179:
Implementations of <tt>EpicsBuffer</tt> must provide the following methods, the names of which have been designed (where possible) to match the names of the equivalent methods in the C++ standard template library's container classes:
Implementations of <tt>EpicsBuffer</tt> must provide the following methods, the names of which have been designed (where possible) to match the names of the equivalent methods in the C++ standard template library's container classes:


;<tt>~EpicsBuffer()</tt>
: All data storage allocated for the buffer is reclaimed by the destructor.
;<tt>EpicsBufferCreator *creator() const</tt>
: An <tt>EpicsBuffer</tt> must know its own creator.
;<tt>void reserve(epicsInt32 capacity)</tt>
;<tt>void reserve(epicsInt32 capacity)</tt>
: Allocate space to store <tt>capacity</tt> octets. Some implementations may impose a <tt>max_size()</tt> smaller than the limits of memory. If either limit is exceeded by the request, an exception will be thrown. This method is used to request an increase or decrease in the amount of storage alloted for the buffer and may cause the data to move in memory, but does not change the data stored or its size. In particular it cannot reduce the capacity below the amount of data currently stored in the buffer, so to release all the data storage an application would call <tt>resize(0)</tt> then <tt>reserve(0)</tt>.
: Allocate space to store <tt>capacity</tt> octets. Some implementations may impose a <tt>max_size()</tt> smaller than the limits of memory. If either limit is exceeded by the request, an exception will be thrown. This method is used to request an increase or decrease in the amount of storage alloted for the buffer and may cause the data to move in memory, but does not change the data stored or its size. In particular it cannot reduce the capacity below the amount of data currently stored in the buffer, so to release all the data storage an application would call <tt>resize(0)</tt> then <tt>reserve(0)</tt>.
Line 183: Line 193:
;<tt>epicsInt32 maxSize() const</tt>
;<tt>epicsInt32 maxSize() const</tt>
: Returns the size of the largest buffer that may be allocated. An attempt to allocate more than this amount of space will fail for certain, but there is no guarantee that this amount of memory is available. This method provides a way for application code to discover any limits that may be imposed by the particular buffer type.
: Returns the size of the largest buffer that may be allocated. An attempt to allocate more than this amount of space will fail for certain, but there is no guarantee that this amount of memory is available. This method provides a way for application code to discover any limits that may be imposed by the particular buffer type.
;<tt>void destruct()</tt>
: All storage for both the data and the buffer class itself is released.
;<tt>bool mutable() const</tt>
;<tt>bool mutable() const</tt>
: Returns true if the buffer data can be modified, i.e. are puts allowed.
: Returns true if the buffer data can be modified, i.e. are puts allowed.
Line 201: Line 209:
* Must call <tt>resize(n)</tt> if the data length is to be changed.
* Must call <tt>resize(n)</tt> if the data length is to be changed.
* Must not access storage outside the length returned by expose. The caller may have to make multiple expose calls to read or write a complete string.
* Must not access storage outside the length returned by expose. The caller may have to make multiple expose calls to read or write a complete string.
Class methods that allocate or deallocate memory must be thread-safe; buffers could be resized, created or destroyed at any time from any thread.  However synchronizing access to the data in the buffer is not the responsibility of this class, thus a buffer should probably not contain a mutex/lock, but its creator might.
=== EpicsBufferCreator ===
    class EpicsBufferCreator { // interface
    public:
        virtual const char *bufferType() const = 0;
        virtual EpicsBuffer *create(epicsInt32 elementSize = 1) = 0;
        virtual void destroy(EpicsBuffer *pbuffer) = 0;
    };
An <tt>EpicsBufferCreator</tt> is an interface implemented by something that knows how to create a particular type of <tt>EpicsBuffer</tt>. How the creator manages the memory involved is up to the implementation - buffers and their data segments may be stored on a freelist by some implementations, and obtained from system memory by others.  Three methods must be implemented:
;<tt>const char *bufferType() const</tt>
: Returns some well-known name of the buffer type created.
;<tt>EpicsBuffer *create(epicsInt32 elementSize = 1)</tt>
: Instanciates an <tt>EpicsBuffer</tt> which is appropriate for storing arrays of size <tt>elementSize</tt>. Array elements can never cross a segment boundary, thus a buffer segment can never be smaller than an individual element. This method may throw an exception if it is unable to create the buffer.
;<tt>void destroy(EpicsBuffer *pbuffer)</tt>
: Executes <tt>pbuffer</tt>'s destructor to reclaim the buffer storage, and also reclaims the <tt>pbuffer</tt> object itself.
The way to deallocate an <tt>EpicsBuffer *pbuffer</tt> is like this:
    pbuffer->creator()->destroy(pbuffer);
    pbuffer = NULL;
Implementations of this class must be thread-safe; buffers could be created or destroyed at any time from any thread.


=== EpicsBufferFactory ===
=== EpicsBufferFactory ===


    typedef EpicsBuffer *(EpicsBufferAllocator)();
   
     class EpicsBufferFactory {
     class EpicsBufferFactory {
     public:
     public:
         static EpicsBuffer *allocate(const char *type);
         static void register(EpicsBufferCreator *creator);
         static epicsInt16 typeToTypeID(const char *type);
         static EpicsBufferCreator *creator(const char *type);
         static EpicsBuffer *allocate(epicsInt16 typeId);
         static EpicsBuffer *create(const char *type,
        static void register(const char *type,
                                    epicsInt32 elementSize = 1);
                          EpicsBufferAllocator allocator);
     };
     };


This class provides a central registry of different <tt>EpicsBuffer</tt> implementations and is also responsible for creating an <tt>EpicsBuffer</tt> of any of these types on demand. Note that creating a buffer is not the same as allocating storage for the data that is to be stored in that buffer. The former is performed by <tt>EpicsBufferFactory</tt>, while the latter is the responsibilty of the <tt>EpicsBuffer</tt> implementation.
The <tt>EpicsBufferFactory</tt> provides a registry of <tt>EpicsBuffer</tt> implementations, each defined by an <tt>EpicsBufferCreator</tt>.  While each <tt>EpicsBufferCreator</tt> is ultimately responsible for creating all <tt>EpicsBuffer</tt>s of its particular type, the <tt>EpicsBufferFactory</tt> provides a convenience function that looks up an <tt>EpicsBufferCreator</tt> by name and calls its <tt>create()</tt> function directly. Use of the factory to create buffers is optional, since the registry can also be queried to find an <tt>EpicsBufferCreator</tt> by name.
 
It should be clear that creating a buffer is not the same as allocating storage for the data held in that buffer. The former is an <tt>EpicsBufferCreator</tt>'s job, while the latter is the responsibility of the <tt>EpicsBuffer</tt> itself. However an <tt>EpicsBufferCreator</tt> may provide additional services to its <tt>EpicsBuffer</tt> instances such as free lists.




Line 224: Line 257:
<tt>epicsString.h</tt> defines a string class that uses an <tt>EpicsBuffer</tt> to hold its data.
<tt>epicsString.h</tt> defines a string class that uses an <tt>EpicsBuffer</tt> to hold its data.


     class EpicsString : public EpicsBuffer {
     class EpicsString {
     public:
     public:
         EpicsString();
         EpicsString();
         EpicsString(const char *literal);
         EpicsString(const char *literal);
         EpicsString(const char *bufferType, epicsInt32 capacity);
         EpicsString(const char *bufferType, epicsInt32 capacity);
         EpicsString(epicsInt16 bufferTypeId, epicsInt32 capacity);
         EpicsString(EpicsBufferCreator *creator, epicsInt32 capacity);
         virtual ~EpicsString();
         virtual ~EpicsString();
         EpicsString& operator=(const EpicsString &rhs);
         EpicsString& operator=(const EpicsString &rhs);
         void useBuffer(const char *bufferType, epicsInt32 capacity = 0)
         void useBuffer(const char *bufferType, epicsInt32 capacity = 0)
         void useBuffer(epicsInt16 bufferTypeId, epicsInt32 capacity = 0);
         void useBuffer(EpicsBufferCreator *creator, epicsInt32 capacity = 0);
        void destroy();
         epicsInt32 get(epicsInt32 offset, epicsInt32 len,
         epicsInt32 get(epicsInt32 offset, epicsInt32 len,
                         epicsOctet *pto) const;
                         epicsOctet *pto) const;
Line 241: Line 275:
          
          
         // These routines are as described for EpicsBuffer
         // These routines are as described for EpicsBuffer
        EpicsBufferCreator *creator() const;
         void reserve(epicsInt32 capacity);
         void reserve(epicsInt32 capacity);
         epicsInt32 capacity() const;
         epicsInt32 capacity() const;
Line 246: Line 281:
         epicsInt32 size() const;
         epicsInt32 size() const;
         epicsInt32 maxSize() const;
         epicsInt32 maxSize() const;
        void destruct();
         bool mutable() const;
         bool mutable() const;
         bool isEqual(const EpicsBuffer &cmp) const;
         bool isEqual(const EpicsBuffer &cmp) const;
Line 282: Line 316:
: Here a buffer type is specified, either by name or by identifier, so the selected buffer type will be created using the <tt>EpicsBufferFactory</tt>, and if <tt>capacity</tt> is non-zero the space will be reserved for at least that number of octets of data.
: Here a buffer type is specified, either by name or by identifier, so the selected buffer type will be created using the <tt>EpicsBufferFactory</tt>, and if <tt>capacity</tt> is non-zero the space will be reserved for at least that number of octets of data.
;<tt>~EpicsString()</tt>
;<tt>~EpicsString()</tt>
: The destructor will <tt>destruct()</tt> the <tt>EpicsBuffer</tt> if one has been allocated.
: The destructor will <tt>destroy()</tt> the <tt>EpicsBuffer</tt> if one has been created.
;<tt>void useBuffer(const char *bufferType, epicsInt32 capacity)</tt>
;<tt>void useBuffer(const char *bufferType, epicsInt32 capacity)</tt>
;<tt>void useBuffer(epicsInt16 bufferTypeId, epicsInt32 capacity)</tt>
;<tt>void useBuffer(EpicsBufferCreator *creator, epicsInt32 capacity)</tt>
: These methods create an <tt>EpicsBuffer</tt> to hold the string data, and if <tt>capacity</tt> is non-zero the space will be reserved for at least that number of octets of data.
: These methods create an <tt>EpicsBuffer</tt> to hold the string data, and if <tt>capacity</tt> is non-zero the space will be reserved for at least that number of octets of data.
:: '''What happens if a buffer type has already been selected for the string? Throw an exception, or copy the old data to the new buffer and destroy the old buffer? Start with the exception, switch if we need to.'''
:: '''What happens if a buffer type has already been selected for the string? Throw an exception, or create the new one, copy the data and destroy the old buffer? Start with the exception, switch if we need to.'''
;<tt>void destroy()</tt>
: Causes the underlying buffer storage to be completely released, after which one of the <tt>useBuffer()</tt> methods must be used before data can be stored in the string again.
;<tt>epicsInt32 get(epicsInt32 offset, epicsInt32 len, epicsOctet *pto)</tt>
;<tt>epicsInt32 get(epicsInt32 offset, epicsInt32 len, epicsOctet *pto)</tt>
: Copies up to <tt>len</tt> octets starting at <tt>offset</tt> from the string buffer to <tt>pto</tt>, and returns the number of octets transfered. The return value will be less than <tt>len</tt> if <tt>offset+len > size()</tt>.
: Copies up to <tt>len</tt> octets starting at <tt>offset</tt> from the string buffer to <tt>pto</tt>, and returns the number of octets transfered. The return value will be less than <tt>len</tt> if <tt>offset+len > size()</tt>.
Line 294: Line 330:
: Calculates an n-bit hash of the octets stored in the string buffer.
: Calculates an n-bit hash of the octets stored in the string buffer.


<tt>EpicsString</tt> also implements all of the routines in the <tt>EpicsBuffer</tt> interface. If an <tt>EpicsBuffer</tt> has been allocated these calls are forwarded to the underlying  <tt>EpicsBuffer</tt> method. If an <tt>EpicsBuffer</tt> has not been allocated however, all methods except for <tt>useBuffer()</tt> will throw an exception. The <tt>destruct()</tt> causes the underlying buffer storage to be completely released, after which one of the <tt>useBuffer()</tt> methods must be used before data can be stored in the string again.
<tt>EpicsString</tt> also implements all of the routines in the <tt>EpicsBuffer</tt> interface. If an <tt>EpicsBuffer</tt> has been created these calls are forwarded to the underlying  <tt>EpicsBuffer</tt> method. If an <tt>EpicsBuffer</tt> has not been created however, all methods except for <tt>useBuffer()</tt> will throw an exception.


----
----
Line 308: Line 344:
         EpicsArray(const char *bufferType, epicsInt32 capacity,
         EpicsArray(const char *bufferType, epicsInt32 capacity,
                     epicsInt32 elementSize = 0);
                     epicsInt32 elementSize = 0);
         EpicsArray(epicsInt16 bufferTypeId, epicsInt32 capacity,
         EpicsArray(EpicsBufferCreator *creator, epicsInt32 capacity,
                     epicsInt32 elementSize = 0);
                     epicsInt32 elementSize = 0);
         virtual ~EpicsArray();
         virtual ~EpicsArray();
Line 314: Line 350:
         epicsInt32 getElementSize() const;
         epicsInt32 getElementSize() const;
         void useBuffer(const char *bufferType, epicsInt32 capacity = 0);
         void useBuffer(const char *bufferType, epicsInt32 capacity = 0);
         void useBuffer(epicsInt16 bufferTypeId, epicsInt32 capacity = 0);
         void useBuffer(EpicsBufferCreator *creator, epicsInt32 capacity = 0);
        void destroy();
         epicsOctet * element(epicsInt32 index);
         epicsOctet * element(epicsInt32 index);
         const epicsOctet * element(epicsInt32 index) const;
         const epicsOctet * element(epicsInt32 index) const;
Line 323: Line 360:
          
          
         // These are similar to EpicsBuffer, measured in elements not octets
         // These are similar to EpicsBuffer, measured in elements not octets
        EpicsBufferCreator *creator() const;
         void reserve(epicsInt32 capacity);
         void reserve(epicsInt32 capacity);
         epicsInt32 capacity() const;
         epicsInt32 capacity() const;
Line 328: Line 366:
         epicsInt32 size() const;
         epicsInt32 size() const;
         epicsInt32 maxSize() const;
         epicsInt32 maxSize() const;
        void destruct();
         bool mutable() const;
         bool mutable() const;
         void expose(epicsInt32 offset, epicsInt32 &len,
         void expose(epicsInt32 offset, epicsInt32 &len,
Line 342: Line 379:
     };
     };


An <tt>EpicsArray</tt> is a datatype represented by the epicsType enumeration value <tt>epicsTypeArray</tt>.
An <tt>EpicsArray</tt> is a datatype represented by the epicsType enumeration value <tt>epicsTypeArray</tt>. It holds an array of elements of constant size.
It holds an array of elements of constant size.
: '''How do we cope with padding?'''


EpicsArray has the following methods:
EpicsArray has the following methods:
Line 350: Line 385:
: If an array is default constructed, one of the <tt>useBuffer()</tt> methods must be called to set the underlying buffer type before any storage can be reserved for the array elements. The array must also be told its element size before buffer space can be reserved.
: If an array is default constructed, one of the <tt>useBuffer()</tt> methods must be called to set the underlying buffer type before any storage can be reserved for the array elements. The array must also be told its element size before buffer space can be reserved.
;<tt>EpicsArray(const char *bufferType, epicsInt32 capacity, epicsInt32 elementSize = 0)</tt>
;<tt>EpicsArray(const char *bufferType, epicsInt32 capacity, epicsInt32 elementSize = 0)</tt>
;<tt>EpicsArray(epicsInt16 bufferTypeId, epicsInt32 capacity, epicsInt32 elementSize = 0)</tt>
;<tt>EpicsArray(EpicsBufferCreator *creator, epicsInt32 capacity, epicsInt32 elementSize = 0)</tt>
: Here a buffer type is specified, either by name or by identifier, so the selected butter type will be created using the <tt>EpicsBufferFactory</tt>, and if both <tt>capacity</tt> and <tt>elementSize</tt> are non-zero the space will be reserved for at least that number of array elements.
: Here a buffer type is specified, either by name or by identifier, so the selected butter type will be created using the <tt>EpicsBufferFactory</tt>, and if both <tt>capacity</tt> and <tt>elementSize</tt> are non-zero the space will be reserved for at least that number of array elements.
;<tt>~EpicsArray</tt>
;<tt>~EpicsArray</tt>
: The destructor will <tt>destruct()</tt> the <tt>EpicsBuffer</tt> if one has been allocated.
: The destructor will <tt>destroy()</tt> the <tt>EpicsBuffer</tt> if one has been created.
;<tt>void setElementSize(epicsInt32 elementSize)</tt>
;<tt>void setElementSize(epicsInt32 elementSize)</tt>
: This sets the size (in octets) of the element type to be stored in the array. If a buffer has already been allocated for previous array data, this will be released and the buffer capacity set to zero.
: This sets the size (in octets) of the element type to be stored in the array. If a buffer has already been used for previous array data, this will be released and the buffer capacity set to zero.
;<tt>epicsInt32 getElementSize()</tt>
;<tt>epicsInt32 getElementSize()</tt>
: This returns the element size currently stored.
: This returns the element size currently stored.
;<tt>void useBuffer</tt>
;<tt>void useBuffer</tt>
: These methods create an <tt>EpicsBuffer</tt> to hold the array data, and if <tt>capacity</tt> and <tt>elementSize</tt> are non-zero the space will be reserved for at least that number of array elements.
: These methods create an <tt>EpicsBuffer</tt> to hold the array data, and if <tt>capacity</tt> and <tt>elementSize</tt> are non-zero the space will be reserved for at least that number of array elements.
:: '''What happens if a buffer type has already been selected for the array? Throw an exception, or destroy the old buffer and create the new one? Start with the exception, switch if we need to.'''
:: '''What happens if a buffer type has already been selected for the array? Throw an exception, or create the new one, copy the data and destroy the old buffer? Start with the exception, switch if we need to.'''
;<tt>destroy()</tt>
: Causes the underlying buffer storage to be completely released, after which one of the <tt>useBuffer()</tt> methods must be used before data can be stored in the array again.
;<tt>epicsOctet * element(epicsInt32 index)</tt>
;<tt>epicsOctet * element(epicsInt32 index)</tt>
: Returns a pointer giving direct access to the <tt>index</tt>'th element of the array, allowing this element to be modified. The pointer returned will have to be cast to an appropriate element type before use. Access to either adjacent array element ''must not'' be made by adjusting the pointer since the underlying buffer storage may be segmented - instead call <tt>element()</tt> again with the appropriately modified index.
: Returns a pointer giving direct access to the <tt>index</tt>'th element of the array, allowing this element to be modified. The pointer returned will have to be cast to an appropriate element type before use. Access to either adjacent array element ''must not'' be made by adjusting the pointer since the underlying buffer storage may be segmented - instead call <tt>element()</tt> again with the appropriately modified index.
Line 371: Line 408:
;<tt></tt>
;<tt></tt>


<tt>EpicsArray</tt> also implements all of the routines in the EpicsBuffer interface, translating all capacity, size and offset values into element counts before passing them to the underlying <tt>EpicsBuffer</tt>. If an <tt>EpicsBuffer</tt> has not been created however, all these methods will throw an exception.


<tt>EpicsArray</tt> also implements all of the routines in the EpicsBuffer interface, translating all capacity, size and offset values into element counts before passing them to the underlying <tt>EpicsBuffer</tt>. If an <tt>EpicsBuffer</tt> has not been allocated however, all these methods will throw an exception. The <tt>destruct()</tt> causes the underlying buffer storage to be completely released, after which one of the <tt>useBuffer()</tt> methods must be used before data can be stored in the array again.
----


----
'''Stuff below here still needs work - ANJ'''


<center>
<center>
Line 380: Line 418:
</center>
</center>


An <tt>EpicsMDArray</tt> is an instance of an <tt>epicsTypeMDArray</tt>
An <tt>EpicsMDArray</tt> is a datatype represented by the epicsType enumeration value <tt>epicsTypeMDArray</tt>. It holds a multi-dimensional array of elements of constant size, using an <tt>EpicsBuffer</tt> to hold its data.
It specifies the element type, has the address of the array description,
type and has an associated buffer
that holds the storage for the array.


epicsTypeMDArray, i.e. multidimensional array data, is a supported type,
<tt>epicsMDArray.h</tt> contains the following:
because collection and display of two and three dimensional images is a common
requirement.


An <tt>EpicsMDArray</tt> can only hold primitive or string data.
    class EpicsMDArraySlice;
Thus it can be any of the types <tt>epicsTypeBoolean</tt>,
..., <tt>epicsTypeString</tt> but it can not be type <tt>epicsTypeUnknown</tt>,
<tt>epicsTypeArray</tt>, <tt>epicsTypeStruct</tt>, or <tt>epicsTypeMDArray</tt>.
 
 
<tt>epicsMDArray.h</tt> contains the following:
      
      
    class EpicsMDArraySlice;
     class EpicsMDArray : public EpicsArrayBuffer {
     class EpicsMDArray : public EpicsArrayBuffer {
         EpicsMDArray(epicsType type,epicsInt16 ndim);
         EpicsMDArray(epicsType type,epicsInt16 ndim);
Line 417: Line 442:
         epicsInt16 getElementSize();
         epicsInt16 getElementSize();
     protected:
     protected:
        epicsType type;
        epicsType type;
        epicsInt16 elementSize;
        epicsInt16 elementSize;
        EpicsMDArraySlice *pslice;
        EpicsMDArraySlice *pslice;
        EpicsMDArrayBuffer *pbuffer;
        EpicsMDArrayBuffer *pbuffer;
     private:
     private:
        EpicsMDArray(); // no default constructor
        EpicsMDArray(); // no default constructor
         EpicsMDArray(const EpicsMDArray &); // copy not allowed
         EpicsMDArray(const EpicsMDArray &); // copy not allowed
         EpicsMDArray(const EpicsMDArray *); // copy not allowed
         EpicsMDArray(const EpicsMDArray *); // copy not allowed

Revision as of 23:23, 7 June 2005

EPICS: epicsTypes

June 7, 2005

Overview

This document describes the C++ definitions for storing data that can be accessed without pre-complied code, i.e. it has self describing features.


epicsTypes can be used by any code that stores data accessable via V4 Channel Access. Some examples are:

  1. IOC records - Most data accessable from outside record support is stored as an epicsType.
  2. Channel Access Gateway - Can be used to store and transfer data.
  3. Channel Access Clients - Can be used to get/receive data from CA server.

Standard support can be provided to access, via dataAccess, epicsType data. For example standard support can be provided to move IOC record data between record instances and a Channel Access server.

epicsType is an enum that defines the following:

  • epicsTypeUnknown - Type is unknown
  • epicsTypeBoolean - true or false
  • epicsTypeOctet - An eight bit byte. It is NOT an integer type
  • epicsTypeInt16 - 16 bit signed integer
  • epicsTypeInt32 - 32 bit signed integer
  • epicsTypeInt64 - 64 bit signed integer
  • epicsTypeFloat32 - 32 bit IEEE float
  • epicsTypeFloat64 - 64 bit IEEE float
  • epicsTypeString - A UTF-8 Encoded Character String
  • epicsTypeArray - One dimensional array of any epicsType
  • epicsTypeMDArray - Multidimensional array of type epicsTypeBoolean,..epicsTypeFloat64, or epicsTypeString.
  • epicsTypeEnum - An index and associated set of choice strings.
  • epicsTypeStruct - A structure with fields each of which is any epicsType


The actual types associated with the epicsTypes are:

    typedef bool               epicsBoolean;
    typedef char               epicsOctet;
    typedef short              epicsInt16;
    typedef int                epicsInt32;
    typedef long long          epicsInt64;
    typedef float              epicsFloat32;
    typedef double             epicsFloat64;
    //EpicsString holds UTF-8 characters
    class EpicsString; // see below for details;
    class EpicsArray; // see below for details;
    class EpicsMDArray; // see below for details;
    class EpicsEnum; // see below for details;
    class EpicsStruct; // see below for details;

NOTE: In this document primitive type means one of epicsBoolean, epicsOctet, epicsInt16, epicsInt32, epicsInt64, epicsFloat32, or epicsFloat64.

epicsTypes

epicsTypes.h contains the following:

    /* The following may require OSD definitions*/
    typedef bool               epicsBoolean;
    typedef char               epicsOctet;
    typedef short              epicsInt16;
    typedef int                epicsInt32;
    typedef long long          epicsInt64;
    typedef float              epicsFloat32;
    typedef double             epicsFloat64;
   
    enum epicsType {
        epicsTypeUnknown,
        epicsTypeBoolean,
        epicsTypeOctet,
        epicsTypeInt16,
        epicsTypeInt32,
        epicsTypeInt64,
        epicsTypeFloat32,
        epicsTypeFloat64,
        epicsTypeString,
        epicsTypeArray,
        epicsTypeMDArray,
        epicsTypeEnum,
        epicsTypeBitmask,
        epicsTypeStruct
    };


epicsTypes provides classes for describing data that can be introspected and can be passed between different platforms. All data that is sent to or received from EPICS records will be composed of epicsTypes.

The types epicsBoolean, ..., epicsFloat64 all map to a C++ standard type. It may be necessary to provide operating system dependent definitions for some of the types. For example on some architectures an epicsInt64 may have to be defined as a long rather than a long long.

epicsTypeUnknown is for anything that is not one of the other epicsTypes.

The non-primitive type, except, epicsTypeUnknown are designed so that the data they contain can be described, introspected, and passed over a network. Each is described in the following sections.

Locking Issues

epicsTypes does not provide any facilities for preventing simultaneous access by multiple threads. For example the class definitions for non-primitive types, i.e. epicsString, epicsArray, epicsStruct, and epicsMDArray, all provide a method expose which returns the address of data. In order to make expose safe, some rules must be established.

As an example of rules, a lock can be associated with each object that supports expose. Code can only call expose and access the exposed data while it holds the lock.

The rules are not specified by epicsTypes since they are application dependent.


EpicsBuffer

epicsBuffer.h defines a generic interface EpicsBuffer to a buffer of octet data, and

A buffer factory EpicsBufferFactory provides a central registry of EpicsBufferCreator objects and uses these to create instances of a particular buffer type on demand.

    class EpicsBuffer { // interface
    protected:
        virtual ~EpicsBuffer() = 0;
    public:
        virtual EpicsBufferCreator *creator() const = 0;
        virtual void reserve(epicsInt32 capacity) = 0;
        virtual epicsInt32 capacity() const = 0;
        virtual void resize(epicsInt32 newsize) = 0;
        virtual epicsInt32 size() const = 0;
        virtual epicsInt32 maxSize() const = 0;
        virtual bool mutable() const = 0;
        virtual bool isEqual(const EpicsBuffer &cmp) const = 0;
        virtual bool isEqual(epicsInt32 offset, epicsInt32 len,
                             const epicsOctet *pdata) const = 0;
        virtual void expose(epicsInt32 offset, epicsInt32 &len,
                            epicsOctet *&pdata) = 0;
        virtual void expose(epicsInt32 offset, epicsInt32 &len,
                            const epicsOctet *&pdata) const = 0;
    
    friend class EpicsBufferCreator;
    };

An EpicsBuffer is a container for epicsOctet data values, and may be used to store things such as character strings containing UTF-8 characters or arrays of some other data type.

Multiple implementations of EpicsBuffer are needed with different characteristics, but all buffers will be accessed using the same EpicsBuffer interface. At least the following buffer types will be provided:

  • readonly - A contiguous array that already contains the desired data is provided at buffer initialization time, and cannot be modified. This buffer is for use with literal character strings, and probably won't be registered with the EpicsBufferFactory.
  • contiguous - The data is stored in a contiguous array of octets.
  • segmented - The data is stored in fixed-size chunks. This form should be used for buffers that are frequently created and released, or which often need to change in size.

Additional implementations may be provided for other purposes such as managing network buffers. There might be a need for a contiguous zero-terminated buffer type for code that regularly talks to C string handling routines.

In addition to storing the data, the buffer keeps the following information:

capacity
The number of octets of storage allocated for the buffer.
size
The number of octets of data currently held in the buffer.

Implementations of EpicsBuffer must provide the following methods, the names of which have been designed (where possible) to match the names of the equivalent methods in the C++ standard template library's container classes:

~EpicsBuffer()
All data storage allocated for the buffer is reclaimed by the destructor.
EpicsBufferCreator *creator() const
An EpicsBuffer must know its own creator.
void reserve(epicsInt32 capacity)
Allocate space to store capacity octets. Some implementations may impose a max_size() smaller than the limits of memory. If either limit is exceeded by the request, an exception will be thrown. This method is used to request an increase or decrease in the amount of storage alloted for the buffer and may cause the data to move in memory, but does not change the data stored or its size. In particular it cannot reduce the capacity below the amount of data currently stored in the buffer, so to release all the data storage an application would call resize(0) then reserve(0).
epicsInt32 capacity() const
Returns the allocated capacity of the buffer.
void resize(epicsInt32 newsize)
Sets the number of octets currently stored, up to the current capacity.
epicsInt32 size() const
Returns the number of octets currently stored in the buffer.
epicsInt32 maxSize() const
Returns the size of the largest buffer that may be allocated. An attempt to allocate more than this amount of space will fail for certain, but there is no guarantee that this amount of memory is available. This method provides a way for application code to discover any limits that may be imposed by the particular buffer type.
bool mutable() const
Returns true if the buffer data can be modified, i.e. are puts allowed.
isEqual(const EpicsBuffer &cmp)
Compares the contents of this buffer with the contents of the buffer cmp, and returns true if the data is identical.
isEqual(epicsInt32 offset, epicsInt32 len, const epicsOctet *pdata)
Compares len octets of data starting offset octets into this buffer with the octet array pdata supplied by the caller. This method is mainly provided to simplify the implementation of the other isEqual method.
void expose(epicsInt32 offset, epicsInt32 &len, epicsOctet *&pdata)
A request for the address of len octets of buffer data starting offset octets into the buffer. If the buffer implementation uses segmented memory the maximum number of contiguous octets exposed is the segment size, which may be less than the length requested. In this case the value of len will be reduced before the method returns. The caller must process the data provided and call expose again with an increased offset to retrieve or modify subsequent segments.

Two versions of the expose method are provided, one for use when modifying the buffer and one where the buffer (and the returned data pointer) is const.

  • The const expose() method will only present data up to the current limit as reported by the size() method.
  • The non-const expose() method will present data up to the current capacity of the buffer, so application code that is reading as well as modifying the buffer's contents must keep track of the current value of size() and stop reading when this is reached (or use the both methods, the const one for reading and the other for writing).
  • The non-const version will throw an exception if called for a buffer that is immutable.

The design of the expose method is intended to increase the efficiency of data access. The caller must follow these conventions:

  • Must call resize(n) if the data length is to be changed.
  • Must not access storage outside the length returned by expose. The caller may have to make multiple expose calls to read or write a complete string.

Class methods that allocate or deallocate memory must be thread-safe; buffers could be resized, created or destroyed at any time from any thread. However synchronizing access to the data in the buffer is not the responsibility of this class, thus a buffer should probably not contain a mutex/lock, but its creator might.

EpicsBufferCreator

    class EpicsBufferCreator { // interface
    public:
        virtual const char *bufferType() const = 0;
        virtual EpicsBuffer *create(epicsInt32 elementSize = 1) = 0;
        virtual void destroy(EpicsBuffer *pbuffer) = 0;
    };

An EpicsBufferCreator is an interface implemented by something that knows how to create a particular type of EpicsBuffer. How the creator manages the memory involved is up to the implementation - buffers and their data segments may be stored on a freelist by some implementations, and obtained from system memory by others. Three methods must be implemented:

const char *bufferType() const
Returns some well-known name of the buffer type created.
EpicsBuffer *create(epicsInt32 elementSize = 1)
Instanciates an EpicsBuffer which is appropriate for storing arrays of size elementSize. Array elements can never cross a segment boundary, thus a buffer segment can never be smaller than an individual element. This method may throw an exception if it is unable to create the buffer.
void destroy(EpicsBuffer *pbuffer)
Executes pbuffer's destructor to reclaim the buffer storage, and also reclaims the pbuffer object itself.

The way to deallocate an EpicsBuffer *pbuffer is like this:

    pbuffer->creator()->destroy(pbuffer);
    pbuffer = NULL;

Implementations of this class must be thread-safe; buffers could be created or destroyed at any time from any thread.

EpicsBufferFactory

    class EpicsBufferFactory {
    public:
        static void register(EpicsBufferCreator *creator);
        static EpicsBufferCreator *creator(const char *type);
        static EpicsBuffer *create(const char *type,
                                   epicsInt32 elementSize = 1);
    };

The EpicsBufferFactory provides a registry of EpicsBuffer implementations, each defined by an EpicsBufferCreator. While each EpicsBufferCreator is ultimately responsible for creating all EpicsBuffers of its particular type, the EpicsBufferFactory provides a convenience function that looks up an EpicsBufferCreator by name and calls its create() function directly. Use of the factory to create buffers is optional, since the registry can also be queried to find an EpicsBufferCreator by name.

It should be clear that creating a buffer is not the same as allocating storage for the data held in that buffer. The former is an EpicsBufferCreator's job, while the latter is the responsibility of the EpicsBuffer itself. However an EpicsBufferCreator may provide additional services to its EpicsBuffer instances such as free lists.


epicsString

epicsString.h defines a string class that uses an EpicsBuffer to hold its data.

    class EpicsString {
    public:
        EpicsString();
        EpicsString(const char *literal);
        EpicsString(const char *bufferType, epicsInt32 capacity);
        EpicsString(EpicsBufferCreator *creator, epicsInt32 capacity);
        virtual ~EpicsString();
        EpicsString& operator=(const EpicsString &rhs);
        void useBuffer(const char *bufferType, epicsInt32 capacity = 0)
        void useBuffer(EpicsBufferCreator *creator, epicsInt32 capacity = 0);
        void destroy();
        epicsInt32 get(epicsInt32 offset, epicsInt32 len,
                       epicsOctet *pto) const;
        epicsInt32 put(epicsInt32 offset, epicsInt32 len,
                       const epicsOctet *pfrom);
        epicsInt32 hash(epicsInt16 nBitsHashIndex) const;
        
        // These routines are as described for EpicsBuffer
        EpicsBufferCreator *creator() const;
        void reserve(epicsInt32 capacity);
        epicsInt32 capacity() const;
        void resize(epicsInt32 newsize);
        epicsInt32 size() const;
        epicsInt32 maxSize() const;
        bool mutable() const;
        bool isEqual(const EpicsBuffer &cmp) const;
        bool isEqual(epicsInt32 offset, epicsInt32 len,
                     const epicsOctet *pdata) const;
        bool expose(epicsInt32 offset, epicsInt32 &len,
                    epicsOctet *&pdata);
        bool expose(epicsInt32 offset, epicsInt32 &len,
                    const epicsOctet *&pdata) const;
    protected:
        EpicsBuffer *pbuffer;
    private:
        EpicsString(const EpicsString &str);   // No copy constructor
    };
    
    epicsBoolean operator==(const EpicsString &lhs, const EpicsString &rhs);
    epicsBoolean operator!=(const EpicsString &lhs, const EpicsString &rhs);

An EpicsString is a datatype represented by the epicsType enumeration value epicsTypeString. It holds a UTF-8 encoded string, i.e. multiple octets may be needed to store a single character. As long as it does not assume that each character is a single byte, most code can just ignore the fact that the string is UTF-8 encoded. If the local computer has been internationalized for the particular UTF-8 encoding then a UTF-8 string can be printed via the printf family of methods.

The EpicsString data is not guaranteed to be null-terminated and in most cases will not be, so applications using the string must take precautions when interfacing with routines that expect the string to end with a zero byte. We may provide a type of contiguous EpicsBuffer that does guarantee a null terminator.

EpicsString provides the following methods:

EpicsString()
If a string is default constructed, one of the useBuffer() methods must be called to set the underlying buffer type before any data can be stored.
EpicsString(const char *literal)
Use of this constructor causes the string to have a readonly buffer that holds just the literal string given, without copying. This is intended to be efficient as it is likely to be commonly used.
EpicsString(const char *bufferType, epicsInt32 capacity)
EpicsString(picsInt16 bufferTypeId, epicsInt32 capacity)
Here a buffer type is specified, either by name or by identifier, so the selected buffer type will be created using the EpicsBufferFactory, and if capacity is non-zero the space will be reserved for at least that number of octets of data.
~EpicsString()
The destructor will destroy() the EpicsBuffer if one has been created.
void useBuffer(const char *bufferType, epicsInt32 capacity)
void useBuffer(EpicsBufferCreator *creator, epicsInt32 capacity)
These methods create an EpicsBuffer to hold the string data, and if capacity is non-zero the space will be reserved for at least that number of octets of data.
What happens if a buffer type has already been selected for the string? Throw an exception, or create the new one, copy the data and destroy the old buffer? Start with the exception, switch if we need to.
void destroy()
Causes the underlying buffer storage to be completely released, after which one of the useBuffer() methods must be used before data can be stored in the string again.
epicsInt32 get(epicsInt32 offset, epicsInt32 len, epicsOctet *pto)
Copies up to len octets starting at offset from the string buffer to pto, and returns the number of octets transfered. The return value will be less than len if offset+len > size().
epicsInt32 put(epicsInt32 offset, epicsInt32 len, const epicsOctet *pfrom)
Copies up to len octets from pfrom into the buffer starting offset octets from the beginning, and returns the number of octets copied. The return value will be less than len if offset+len > capacity(). The string size will be updated if this call extends the string beyond its original size. However on entry offset must not be greater than size() or an exception will be thrown.
epicsInt32 hash(epicsInt16 nBitsHashIndex) const
Calculates an n-bit hash of the octets stored in the string buffer.

EpicsString also implements all of the routines in the EpicsBuffer interface. If an EpicsBuffer has been created these calls are forwarded to the underlying EpicsBuffer method. If an EpicsBuffer has not been created however, all methods except for useBuffer() will throw an exception.


epicsArray

epicsArray.h defines an array class that uses an EpicsBuffer to hold its data.

    class EpicsArray {
        EpicsArray(epicsInt32 elementSize = 0);
        EpicsArray(const char *bufferType, epicsInt32 capacity,
                   epicsInt32 elementSize = 0);
        EpicsArray(EpicsBufferCreator *creator, epicsInt32 capacity,
                   epicsInt32 elementSize = 0);
        virtual ~EpicsArray();
        void setElementSize(epicsInt32 elementSize);
        epicsInt32 getElementSize() const;
        void useBuffer(const char *bufferType, epicsInt32 capacity = 0);
        void useBuffer(EpicsBufferCreator *creator, epicsInt32 capacity = 0);
        void destroy();
        epicsOctet * element(epicsInt32 index);
        const epicsOctet * element(epicsInt32 index) const;
        epicsInt32 get(epicsInt32 offset, epicsInt32 len,
                       epicsOctet *pto) const;
        epicsInt32 put(epicsInt32 offset, epicsInt32 len,
                       const epicsOctet *pfrom);
        
        // These are similar to EpicsBuffer, measured in elements not octets
        EpicsBufferCreator *creator() const;
        void reserve(epicsInt32 capacity);
        epicsInt32 capacity() const;
        void resize(epicsInt32 newsize);
        epicsInt32 size() const;
        epicsInt32 maxSize() const;
        bool mutable() const;
        void expose(epicsInt32 offset, epicsInt32 &len,
                    epicsOctet *&pdata);
        void expose(epicsInt32 offset, epicsInt32 &len,
                    const epicsOctet *&pdata) const;
    protected:
        epicsInt32 elementSize;
        EpicsBuffer *pbuffer;
    private:
        EpicsArray(const EpicsArray &);            // No copy constructor
        EpicsArray& operator=(const EpicsArray &); // No assignment operator
    };

An EpicsArray is a datatype represented by the epicsType enumeration value epicsTypeArray. It holds an array of elements of constant size.

EpicsArray has the following methods:

EpicsArray(epicsInt32 elementSize = 0)
If an array is default constructed, one of the useBuffer() methods must be called to set the underlying buffer type before any storage can be reserved for the array elements. The array must also be told its element size before buffer space can be reserved.
EpicsArray(const char *bufferType, epicsInt32 capacity, epicsInt32 elementSize = 0)
EpicsArray(EpicsBufferCreator *creator, epicsInt32 capacity, epicsInt32 elementSize = 0)
Here a buffer type is specified, either by name or by identifier, so the selected butter type will be created using the EpicsBufferFactory, and if both capacity and elementSize are non-zero the space will be reserved for at least that number of array elements.
~EpicsArray
The destructor will destroy() the EpicsBuffer if one has been created.
void setElementSize(epicsInt32 elementSize)
This sets the size (in octets) of the element type to be stored in the array. If a buffer has already been used for previous array data, this will be released and the buffer capacity set to zero.
epicsInt32 getElementSize()
This returns the element size currently stored.
void useBuffer
These methods create an EpicsBuffer to hold the array data, and if capacity and elementSize are non-zero the space will be reserved for at least that number of array elements.
What happens if a buffer type has already been selected for the array? Throw an exception, or create the new one, copy the data and destroy the old buffer? Start with the exception, switch if we need to.
destroy()
Causes the underlying buffer storage to be completely released, after which one of the useBuffer() methods must be used before data can be stored in the array again.
epicsOctet * element(epicsInt32 index)
Returns a pointer giving direct access to the index'th element of the array, allowing this element to be modified. The pointer returned will have to be cast to an appropriate element type before use. Access to either adjacent array element must not be made by adjusting the pointer since the underlying buffer storage may be segmented - instead call element() again with the appropriately modified index.
const epicsOctet * element(epicsInt32 index) const
Returns a pointer giving const (read-only) access to the index'th element of the array, allowing this element to be read but not modified. If the underlying buffer type is not mutable, this method will throw an exception. The pointer returned will have to be cast to an appropriate element type before use. Access to either adjacent array element must not be made by adjusting the pointer since the underlying buffer storage may be segmented - instead call element() again with the appropriately modified index.
epicsInt32 get(epicsInt32 offset, epicsInt32 len, epicsOctet *pto)
Copies up to len elements starting at offset from the array buffer to pto, and returns the number of elements transfered. The return value will be less than len if offset+len > size().
epicsInt32 put(epicsInt32 offset, epicsInt32 len, const epicsOctet *pfrom)
Copies up to len elements from pfrom into the buffer starting offset elements from the beginning, and returns the number of elements copied. The return value will be less than len if offset+len > capacity(). The array size will be updated if this call extends the array beyond its original size. However on entry offset must not be greater than size() or an exception will be thrown.

EpicsArray also implements all of the routines in the EpicsBuffer interface, translating all capacity, size and offset values into element counts before passing them to the underlying EpicsBuffer. If an EpicsBuffer has not been created however, all these methods will throw an exception.


Stuff below here still needs work - ANJ

epicsMDArray

An EpicsMDArray is a datatype represented by the epicsType enumeration value epicsTypeMDArray. It holds a multi-dimensional array of elements of constant size, using an EpicsBuffer to hold its data.

epicsMDArray.h contains the following:

    class EpicsMDArraySlice;
    
    class EpicsMDArray : public EpicsArrayBuffer {
        EpicsMDArray(epicsType type,epicsInt16 ndim);
        virtual ~EpicsMDArray();
        EpicsMDArray(epicsType type,epicsInt16 ndim,const char *bufferType);
        EpicsMDArray(epicsType type,epicsInt16 ndim,epicsInt16 bufferTypeId);
        EpicsMDArray(epicsType type,epicsInt16 ndim,
                     const char *bufferType,,epicsInt32 capacity);
        EpicsMDArray(epicsType type,epicsInt16 ndim,
                     epicsInt16 bufferTypeId,,epicsInt32 capacity);
        void allocateBuffer(const char *bufferType);
        void allocateBuffer(epicsInt16 bufferTypeId);
        void allocateBuffer(const char *bufferType,
                    epicsInt16 elementSize, epicsInt32 capacity)
        void allocateBuffer(epicsInt16 bufferTypeId,
                    epicsInt16 elementSize, epicsInt32 capacity)
        epicsType getType();
        epicsInt16 getElementSize();
    protected:
        epicsType type;
        epicsInt16 elementSize;
        EpicsMDArraySlice *pslice;
        EpicsMDArrayBuffer *pbuffer;
    private:
        EpicsMDArray(); // no default constructor
        EpicsMDArray(const EpicsMDArray &); // copy not allowed
        EpicsMDArray(const EpicsMDArray *); // copy not allowed
    };
    class EpicsMDArrayIndex {
    public
        epicsInt32 low;
        epicsInt32 high;
    };
    class EpicsMDArraySlice {
    public:
        epicsType   type; 
        epicsInt16  ndim;      // number of dimensions
        EpicsMDArrayIndex *paindex; // addr of array of EpicsMDArrayIndex
    };

EpicsMDArray

An EpicsMDArray is an instance of an epicsTypeMDArray. It can contain an array of any epicsType.

EpicsMDArray has the following methods:

EpicsMDArray
Several constructors are available. All require an epicsType and the number ofdimensions. If no buffer type is specified then, one of the allocate methods must be called. If a buffer type is specified then EpicsArrayBufferFactory:allocate is called to initialize pbuffer. If capacity is also specified EpicsArrayBuffer:allocate is called to allocate storage for data.
~EpicsMDArray
The descructor will release the EpicsArrayBuffer if it has been allocated.
void allocateBuffer
These methods allocate an EpicsArrayBuffer and if capacity is specified call EpicsArrayBuffer:allocate.

EpicsMDArray implements interface EpicsArrayBuffer. If an EpicsArrayBuffer has not been allocated all methods raise an exceptioon. If an EpicsArrayBuffer has been allocated it calls the associated EpicsArrayBuffer method.


EpicsArrayBuffer Notes

EpicsMDArray uses the same buffer interface as EpicsArray. Code that accesses a multidimensional array should be prepared to access both contiguous and segmented buffer implementations. Circular buffer implementations may not make sense.

EpicsMDArraySlice

EpicsMDArraySlice has the following fields:

type
The element type which must be one of bool,...,string
ndim
The number of dimensions
paindex
addr of array(ndim) of EpicsMDArrayIndex

EpicsMDArrayIndex has the fields:

low
high

The definitions describe a "slice" of a multi-dimensional array.



EpicsEnum

An EpicsEnum is a 16-bit index value, with an interface to convert between choice strings and their index values.

class EpicsEnumChoices {
public:
    virtual EpicsEnumChoices *duplicate() const = 0;
    virtual void release() = 0;
    virtual epicsInt16 nChoices() const = 0;
    virtual epicsInt16 index(const EpicsString &choice) const = 0;
    virtual void choice(epicsInt16 index, EpicsString &choice) const = 0;
};

class EpicsEnum {
public:
    enum {invalid = -1};
    
    EpicsEnum();
    EpicsEnum(const EpicsEnum &e);
    EpicsEnum(EpicsEnumChoices *choices);
    EpicsEnum(EpicsEnumChoices *choices, epicsInt16 index);
    virtual ~EpicsEnum();
    EpicsEnum& operator=(const EpicsEnum &rhs);
    
    virtual void choices(EpicsEnumChoices *pchoices);
    EpicsEnumChoices *choices() const { return pchoices; }
    epicsInt16 nChoices() const { return pchoices ? pchoices->nChoices() : 0; }
    epicsInt16 get() const { return index; }
    virtual void get(EpicsString &choice) const;
    virtual void put(epicsInt16 index);
    virtual void put(const EpicsString &choice);
protected:
    EpicsEnumChoices *pchoices;
    epicsInt16 index;
};

epicsStruct

epicsStruct.h contains the following:

    class EpicsStructDescription;
    class EpicsStructManager;
    class EpicsStruct{
        EpicsStruct(EpicsStructDescription &structDef);
        ~EpicsStruct();
    protected:
        EpicsStructDescription *pstructDef;
        void    *pstorage;
    private:
        EpicsStruct(); // no default cobstructor
    };
    struct EpicsStringDefaults { // defaults for string
        const char * bufferType;
        epicsInt32 capacity;
    };
    struct EpicsArrayDefaults { // defaults for array
        epicsType  type;
        const char * bufferType;
        epicsInt32 capacity;
        epicsInt32 nelements;
    };
    struct EpicsEnumDefaults { // defaults for enum
         ???
    };
    struct EpicsBitMaskDefaults { // defaults for enum
         ???
    };
    struct EpicsMDArrayDefaults { // defaults for mdarray
        epicsType  type;
        epicsInt16 ndim
        const char *bufferType;
        epicsInt32 capacity;
    };
    struct EpicsStructDefaults { // defaults for struct
        EpicsStructDescription *pdescription;
    };
    class EpicsStructField {
    public:
        EpicsString name;
        epicsType   type;
        union {
            EpicsStringDefaults  *pstring;
            EpicsArrayDefaults   *parray;
            EpicsMDArrayDefaults *pmdarray;
            EpicsEnumDefaults    *penum;
            EpicsBitmaskDefaults *pbitmask;
            EpicsStructDefaults  *pstruct;
        } defaults;
    };
   
    class EpicsStructDescription{
    public:
        EpicsString         name;
        EpicsStructManager *plifetime;
        epicsInt16          nfields;
        EpicsStructField    *pafield;//ptr to array of EpicsStructField
    };
    class EpicsStructManager {
    public:
        virtual void allocate(EpicsStruct &struct) = 0;
        virtual void destroy(EpicsStruct &struct) = 0;
        virtual void *exposeField(EpicsStruct &struct, epicsInt16 index) = 0;
    };

An epicsStruct is a container with fields each of which is any epicsType including unknown.

The following classes are involved with an epicsStruct:

  • EpicsStruct - An instance of an epicsStruct
  • EpicsStructDescription - Describes an epicsStruct
  • EpicsStructField - Describes a field of an epicsStruct
  • EpicsStructDefaults - Provides default values for non-primitive fields. This is intended for code that initializes fields.
  • EpicsStructManager - An interface that manages storage for an epicsStruct.

It is expected that each of these classes are extended. In fact if any field of an epicsStruct has type epicsTypeUnknown then it is likely that most will be extended.

epicsTypes does not specify how to locate an EpicsStructManager, i.e. the mechanism is application dependent.

An EpicsStruct contains two fields:

pstructDef
Address of a EpicsStructDescription that describes the structure.
pstorage
Address of storage for the data contained in the structure.

EpicsStructDescription

EpicsStructDescription has the fields:

name
The structure name.
plifetime
Address of a EpicsStructManager interface. See below.
nfields
The number of fields in the structure.
pafield
addresss of an array of EpicsStructField, one for each field.

EpicsStructField

EpicsStructField has the fields:

name
The field name.
type
The field type, which can be any epicsType.
defaults
For each of the non-primitive epicsTypes, default values can be specified. This is for use by code that must initialize field instances.

EpicsStructManager

EpicsStructManager is an interface that has three methods:

allocate
This sets pstructDef to the definition for the associated structure and sets pstorage to the address of storage for the fields in the structure.
destroy
releases the storage for the structure and sets pstructDef null.
exposeField
This returns the address of the storage for the data associated with the field.