byte-motion / byte-motion/RNL_RAPIDLibrary
Dynamic allocation standard
- Dominant language
- AMPL
- Stars
- 0
- Forks
- 1
- PR merge metrics
- No merged PRs in 30d
Description
Dynamic data allocation (or lack thereof) is becoming more and more of a thorn when designing structures for scalability and especially asynchronous operation across multiple machines that do not share memory, and which complex data structures consisting of chains of data of different types referring to each other.
specifically, dynamic data allocation is needed when structures that can not be generalized as strings due to the 80 character limit must be created in response to external, unpredictable events.
this is the case for:
- Event subscriptions
- Ocellus Items
- commands (external method calls)
- semi-value and non-value data that can not be converted into generic strings
The general consensus is that dynamic memory allocation is not possible in RAPID in an elegant fashion.
Seeing how to need is arising anyhow, I would like to propose the following standardized way of doing it:
Each data type that requires dynamic data allocation has its own module that contains:
- A list{x} of pre-allocated data of the given type
- A null value used to identify a in-use and not-in-use item in the list
- A set of standardized methods for interacting with lists (arrays) of that data type
- A NEW() and ERASE() method used to create a
see below for an example of the "num" datatype (note that nums typically do not require dynamic allocation as a num can fit inside a generic 80character string)
```
MODULE RNL__num
!Datatype: num
!Module with standard methods for the datatype
!RECORD num
!ENDRECORD
!***************************************************************************
! Data
!***************************************************************************
CONST num num_NULL:=9E9;
LOCAL CONST num NULL:=9E9;
LOCAL VAR num list{200};
!Initialization
LOCAL PROC init()
FOR i FROM 1 TO Dim(list,1) DO
list{i}:=NULL;
ENDFOR
ENDPROC
!###########################################################################
! NEW Constructor and ERASE Destructor
!###########################################################################
LOCAL PROC NEW(INOUT dataPointer pointer)
VAR num data;
pointer.type:=type(list);
LIST_num_append list,data\index:=pointer.id;
ENDPROC
LOCAL PROC NEW_RQM(INOUT dataPointer pointer,VAR rmqmessage message)
VAR num data;
pointer.type:=type(list);
RMQGetMsgData message,data;
LIST_num_append list,data\index:=pointer.id;
ENDPROC
LOCAL PROC ERASE(INOUT dataPointer pointer)
IF dataPointer_isReference(pointer) num_set pointer,NULL;
IF dataPointer_isIndex(pointer) LIST_num_del list,pointer.id;
pointer:=dataPointer_NULL;
ENDPROC
!%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
! Standardized Methods
!%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
!%%%% DataPointer methods %%%%
!Set a datapointers value
PROC num_set(INOUT dataPointer pointer,num value)
IF dataPointer_isReference(pointer) SetDataVal pointer.name,value;
IF dataPointer_isValue(pointer) pointer.value:=ValToStr(value);
IF dataPointer_isIndex(pointer) list{pointer.id}:=value;
!If the pointer is none of the above, create a new pointer
pointer := num_(value);
ENDPROC
!Get a datapointers value
FUNC num num_get(dataPointer pointer)
VAR num returnValue;
returnValue:=NULL;
IF dataPointer_isReference(pointer) GetDataVal pointer.name,returnValue;
IF dataPointer_isValue(pointer) errFromBool StrToVal(pointer.value,returnValue),ERR_SYM_ACCESS;
IF dataPointer_isIndex(pointer) returnValue:=returnValue;
RETURN returnValue;
ENDFUNC
!Create a value-datapointer from a value
FUNC dataPointer num_(num value \switch asReference)
VAR dataPointer pointer;
pointer.type:=Type(value);
IF Present(asReference) pointer.name := argName(value);
IF NOT Present(asReference) pointer.value:=ValToStr(value);
RETURN pointer;
ENDFUNC
! %%%% LIST methods %%%%
!Append a datapointer to the list
!The optional argument index will be set to the index that was used
PROC LIST_num_append(INOUT num list{*},num item\INOUT num index)
FOR i FROM 1 TO Dim(list,1) DO
IF list{i}=NULL THEN
list{i}:=item;
IF Present(index) index:=i;
RETURN ;
ENDIF
ENDFOR
ENDPROC
!Pop the last data pointer in the list
!Or if index is specified, pops the index
FUNC num LIST_num_pop(INOUT num list{*}\num index)
VAR num item;
!Pop at spesific index
IF Present(index) THEN
item:=list{index};
list{index}:=NULL;
LIST_num_del list,index;
RETURN item;
ENDIF
!Pop last index
FOR i FROM Dim(list,1) TO 1 DO
IF list{i}<>NULL THEN
item:=list{i};
list{i}:=NULL;
RETURN item;
ENDIF
ENDFOR
ENDFUNC
!Delete an item at a position of the list
PROC LIST_num_del(INOUT num list{*},num index)
list{index}:=NULL;
!Shift down the above items to close gap
FOR i FROM index TO Dim(list,1)-1 DO
list{index}:=list{index+1};
ENDFOR
ENDPROC
!Returns the number of items actively used in the list
FUNC num LIST_num_lenght(INOUT num list{*})
VAR num lenght;
lenght:=0;
FOR i FROM 1 TO Dim(list,1) DO
IF list{i}<>NULL THEN
lenght:=lenght+1;
ENDIF
ENDFOR
RETURN lenght;
ENDFUNC
ENDMODULE
```
I'll be implementing this approach for the version that will be in use on the Wiig project.
If you have comments or ideas for changes, then i'll be happy to adjust to comply, but dynamic data allocation is needed for the system to be scalable so dropping it is not an option.
i'd like @RobotSigmund and @AGus-RN and @RobotTore to do a once-over on this, and when we agree on a standard implementation I will set up a Pull-request on the wiki that formally describes and standardizes the method we agree on.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.