Dynamic Data structures in Clarion using DynaLib

Ok up till now we havnt had time or the knowledge of runtime data structures in the clarion runtime.

With a quite winter and summer we have had a little time to look at the work of the russian in the early days of clarion 5 and 6 and re compile in clarion 11.

We have a need for this stuff in an up and coming project and i thought let publish a little of this as we go along in the next part of this project.

This is the cleaned up version of creating a queue at runtime using the russian code with some rewriting.

As we get know this code we will post tid bits of hopefully creating file and views and queues…

The statements sent to this method in the library are just simple text declarations of a queue.

This is where the queue is created and you can see the Self.Q is simple an address to a string with some data types of long , pretty simple really…we have put a window in to show the data created just for this example.

RTLQueueCreateClass.QConstruct PROCEDURE

QueHdr               LIKE(TQueueHeader),AUTO
DeclareMem           LONG,AUTO
QueueDeclareBuffer   &STRING
QueueTQueueHeader    &STRING
fld                 any  


Window                          WINDOW('test Runtime Queue - from deep inside russia.'),AT(,,395,224),GRAY,FONT('Segoe UI',9)
                                    BUTTON('&OK'),AT(291,201,41,14),USE(?OkButton),DEFAULT
                                    BUTTON('&Cancel'),AT(340,201,42,14),USE(?CancelButton),STD(STD:Close)
                                    LIST,AT(15,19,357,157),USE(?LIST1),FROM(''),FORMAT('47L(2)|M20L' & |
                                        '(2)|M20L(2)|M')
                                END

  Code
  Clear(QueHdr)
 
  if ~Self.LinkMode = true 
     Self.Q &= Null

      ! Is A Queue created here?   
         
     gSize# = Self.FSize(0,True); if gSize# < 0 then Return;end 
     if ~gSize# then Size# = 1 else Size# = gSize#;end
     DeclareMem = RTL::NewMem(Size#); if ~DeclareMem then Return;end
     QueHdr.BufferPtr = DeclareMem
     QueHdr.BufferSize = gSize#

    ! Is GenerateDeclare   
        
     QueueDeclareBuffer &= Self.GenerateDeclare()
     if QueueDeclareBuffer &= Null then 
        RTL::FreeMem(DeclareMem);
        Return;
     end
        
     QueHdr.FieldsDefPtr = Address(QueueDeclareBuffer)
   End
   if ~Self.LinkMode >= true OR ~(Self.LinkG &= Null)
     if Self.LinkMode >= true
        RefGrp.GRef &= Self.LinkG
        QueHdr.BufferPtr = OvrGrp.GPtr.BufferPtr
        QueHdr.BufferSize = OvrGrp.GPtr.BufferSize
        QueHdr.FieldsDefPtr = OvrGrp.GPtr.FieldsDefPtr
        
        ! set up queue header addresses
                
     End
     QueueTQueueHeader &= New(STRING(Size(TQueueHeader)))
     if QueueTQueueHeader &= Null ! memory allocation failed
        RTL::FreeMem(DeclareMem)
        Dispose(QueueTQueueHeader)
     else ! Set up Queue address 
        QueueTQueueHeader = QueHdr
        Self.Q &= Address(QueueTQueueHeader)
            
        if ~(Self.Q &= Null)
             
            fld &= what(self.q,1)
            fld = 'data'
            add(self.Q)
             
            open(WINDOW)
            ?List1{prop:from} = self.Q
            
            ACCEPT
            END
            
            close(WINDOW)
            
           Free(Self.Q); Clear(Self.Q)
           if Self.Thread then Self._Thread(Self.Thread);end
  ;end  ;end  ;end

  QueueDeclareBuffer &= Null; QueueTQueueHeader &= Null
  if ~(Self.Q &= Null)
     if ~Self.Props.BStrPresent = true
        Self.Props.GrpClear &= NEW(STRING(SIZE(Self.Q)))
        if ~Self.LinkMode = TRUE
           Self.Props.GrpClear = Self.Q
        else
           QueueDeclareBuffer &= _NewStr(Self.Q)
           Clear(Self.Q)
           Self.Props.GrpClear = Self.Q
           Self.Q = QueueDeclareBuffer
           Dispose(QueueDeclareBuffer)
      end  ;end
     Self.Props.Created = True
   end

  Return

Looks like Dynalib code.

We are going through and rewriting sections of the Russian DLIB libraries from the russians. What we found interesting was that once you have the locally declared typed groups or structures clarion database, queues and views become dynamic.

it is if that is the russian code base. We wont be taking credit for it but the code is being re worked as some of it is very very hard to read and repeats itself.

There is for a long time also a free version of Dynalib and you can contact Oleg. See https://www.clarionlife.net/novaya-versiya-dynalib-4-0-4/

1 Like

Oops, didn’t know, I delete my message then.

From the help:

Freeware Restrictions

Freeware-version of the given library has some restrictions of functionality:

The maximal size of structure up to 128 bytes
A maximal quantity of fields in a group up to 15
A maximal quantiy of keys in a file up to 2
A maximal quantity of MEMOs and BLOBs in a file up to 1
A maximal quantity of JOIN-blocks in a VIEW up to 2
A maximal quantity of levels (nests of JOIN-blocks) up to 1

These restrictions are valid both for created, and for “linked” structures. Nevertheless, I think, that not looking at these restrictions, freeware-version have sufficient functionality for their use in more - less complex projects. But if you need more flexibility with data manipulating you can purchase Professional version of DynaLib on the ClarionShop.

I dont think you will find that the product is supported any longer by the authors. Much of the code needs reworking as it not really very clean.

As we uncover it structures we are rewriting the parts we are using into a clearer to read format.

It is a very extensive library of code with a lot of functionality.

The Ukraine and Russian appear to have found and exposed all the clarion data structures and come up with a solution to runtime create all data types without using any special code except standard clarion code.

Since no work has been done on this product for what appear to about a decade we decided it was a matter of diving in and hoping the internal clarion data structures had not changed.

Much to our surprise it appear to run in clarion 11 and if clarion goes 64 we will try to support that also.

If the product is still sold on clarion shop i doubt you will get any support from the authors although we stand to be corrected on that.

As we connect the code to runtime macros i will update this thread and prehaps post some example with calc2 runtime scripts on github later in the year for clarions developers to try.

Glad all of the Russians were in on this. :slight_smile:

Could you put some examples on GitHub? That work within the free limitation of 128 bytes.

I usually make LIST / QUEUE examples with DIRECTORY() of the Temp or Windows folder. You could take the FILE:QUEUE and copy it to a DynaLib Queue created at runtime with a STRING(32) file name. This Repo has the code to show the Temp folder in a List

1 Like

That’s pretty neat. Would be cool if there was a textbox to add our own filter criteria for the temp files for our apps that do a lot of temp files. One filter per line. And perhaps an “ignore” textbox that has a list of file masks to never make available for deletion…

e.g.

MySpecialPrefix*.tmp
OtherPrefix*.xxx
Yada*.*

Not something I’d use every day, but it would be useful when I needed it.

This seems to be the last web scrape by the wayback machine.
Aug 2020
Download (archive.org)

dLib Doc file
Wayback Machine (archive.org)

dynalib.narod.ru/freeware/dLib.zip is not in the wayback machine
dynalib.narod.ru/freeware/fScan.zip is also not in the wayback machine.

There is an extended evaluate link on the wayback machine site (last wayback machine webscrape)
“Utilities” (archive.org)

This link mentions dynalib and the TDynaFileClass class
Wayback Machine (archive.org)

The only copy of ExtEval.zip on the wayback machine
Wayback Machine (archive.org)

Found the Russian forum which looks very active still, like posts still being made today.
forum.clarionlife.net - Main page

Example of Creating a Dynamic Queue using a Proxy class.

include(‘Dynaproxycontainer.inc’),ONCE

MAP

   module('ddynartl.clw')
      CreateQueuestruct(string DataStructure,Dynaproxycontainer q),long 
  END
    
END

qproxy &Dynaproxycontainer

Qflds ANY
qaddress long

CODE
Qflds = ‘Stockprices QUEUE,PRE(TST);’ & |
‘Name STRING(@S100),ALIGN©,Name(’‘Name’’);’ & |
‘Code SHORT(@N_5),ALIGN®;’ & |
‘Price DECIMAL(@N13`2),ALIGN®;’ & |
‘Input DATE(@D6.B),ALIGN®;’ & |
‘Closed DATE(@D6.B),ALIGN®;’ & |
‘END;’

qproxy  &=  new(Dynaproxycontainer)    
qaddress = CreateQueuestruct(Qflds,qproxy)
if qaddress > FALSE
     
      qproxy.IDSet.DSet('Name','META STOCK')
     qproxy.IDSet.DSet('TST:Code','META')
     qproxy.IDSet.DSet('TST:Price',157)
      add(qproxy.q)
      
     message(qproxy.IDGet.DGet('TST:Price'))
      
     
END

Dynamically Creating a Queue and Tables from text files.

 module('ddynartl.clw')
      RegisterDrivers
      CreateQueuestruct(string DataStructure,Dynaproxycontainer q),long 
      CreateFilestruct(string DataStructure,Dynaproxycontainer q),long 
      CreateQueuestructDeclare(string QueueName,string DataStructureFile,Dynaproxycontainer q),long 
      CreateFilestructDeclare(string DBFileName,string DataStructureFile,Dynaproxycontainer q),long 

    END
    
END

qproxy &Dynaproxycontainer
Fproxy &Dynaproxycontainer
Qflds ANY
qaddress long

CODE
Qflds = ‘Stockprices QUEUE,PRE(TST);’ & |
‘Name STRING(@S100),ALIGN©,Name(’‘Name’’);’ & |
‘Code SHORT(@N_5),ALIGN®;’ & |
‘Price DECIMAL(@N13`2),ALIGN®;’ & |
‘Input DATE(@D6.B),ALIGN®;’ & |
‘Closed DATE(@D6.B),ALIGN®;’ & |
‘END;’

qproxy  &=  new(Dynaproxycontainer)    
qaddress = CreateQueuestruct(Qflds,qproxy)
if qaddress > FALSE
     
     qproxy.IDSet.DSet('Name','META STOCK')
     qproxy.IDSet.DSet('TST:Code','META')
     qproxy.IDSet.DSet('TST:Price',157)
      add(qproxy.q)
      
     message(qproxy.IDGet.DGet('TST:Price'))
END   
RegisterDrivers    

qflds = 'C:\win32clacode\Ddyna\DynaTableDatastructures.txt'    
Fproxy  &=  new(Dynaproxycontainer)    
qaddress = CreateFilestructDeclare('TPSFile',Qflds, Fproxy)   

Files can be stored in text files for runtime declarations.

#FILE(TPSFile)

TPSFile FILE,DRIVER(‘TOPSPEED’),PRE(TPS),CREATE,Bindable
Name_Key KEY(+TPS:Name,+TPS:Desc),DUP,OPT,NOCASE
Code_Key KEY(+TPS:Code),OPT,NOCASE
Ndx_Key KEY(+TPS:Index),OPT,NOCASE,PRIMARY
Record RECORD
Name STRING(30)
Desc STRING(110)
Code LONG
Index LONG
END
END

#ENDFILE