How to (correctly) implement it?

Programming language questions
Post Reply
dmaksimiuk
Posts: 28
Joined: Sun Jun 14, 2015 3:56 pm
Location: Delft, The Netherlands

How to (correctly) implement it?

Post by dmaksimiuk »

Hi All,
I have to interact with an external device through a DLL. In order to map arrays from C to BBoxCP I have to use the modifier "untagged" while declaring an array type on the CP part. One of the restrictions of the BBox
is that I cannot allocate such arrays dynamically (using NEW). The HW provides multiple data buffers with different lengths, and depends on my configuration at run-time, I would like to read my measurements out of one of these buffers.
This is my attempt to solve it using a pointer to an array of a certain type and assign an address of one of the buffers at run-time. A simplified version of the program is presented below:

Code: Select all

MODULE dmTestArrPtr;
  IMPORT Log := StdLog, S:=SYSTEM;

 CONST
    MSize = 5;
 
TYPE   tSArr1   = ARRAY MSize OF REAL; 
            tSArr2   = ARRAY 2 * MSize  OF REAL;        
            oPtrArr  = POINTER TO ARRAY OF REAL;

VAR 
         StArr10  : tSArr1;
         StArr20   :tSArr2;
          PtrArr: oPtrArr;
          i : INTEGER;

PROCEDURE Run*;
   BEGIN
   (* get an address of the array StArr10 and make a pointer  of out it *)
    Log.String("Arrays after  modification .."); Log.Ln();
    PtrArr := S.VAL(oPtrArr, S.ADR(StArr10));
    
     FOR i := 0 TO LEN(StArr10)  -1 DO
          PtrArr[i] :=  2.0 *  PtrArr[i] ;
    END; 

    PtrArr := S.VAL(oPtrArr, S.ADR(StArr20));

     FOR i := 0 TO LEN(StArr20)  -1 DO
          PtrArr[i] :=  0.5  * PtrArr [i] ;
    END; 
 
    FOR i := 0 TO LEN(StArr10)  -1 DO
      Log.String("Modified  Arr10=");Log.Real(StArr10[i]); Log.Ln();
    END; 
    Log.String("_______________xxx_____________"); Log.Ln();
    FOR i := 0 TO LEN(StArr20)  -1 DO
       Log.String("Modified  Arr20=");Log.Real(StArr20[i]); Log.Ln();
    END; 
    Log.String("_______________xxx_____________"); Log.Ln();
    Log.String("_______________xxx_____________"); Log.Ln();
END Run;

BEGIN
    Log.String("_______________________________"); Log.Ln();
    Log.String("_______________________________"); Log.Ln();
    Log.String("Arrays before modification .."); Log.Ln();
     (* initialise  StArr10 *)
    FOR i := 0 TO LEN(StArr10) -1 DO
       StArr10[i] := 1.0 + i;
       Log.String("Arr10=");Log.Real(StArr10[i]); Log.Ln();
    END;
    Log.String("_______________________________"); Log.Ln();
    (* initialise  StArr20 *)
    FOR i := 0 TO LEN(StArr20) -1 DO
       StArr20[i] := 2 * ( i  + 1);
       Log.String("Arr20=");Log.Real(StArr20[i]); Log.Ln();
    END;
   Log.String("_______________________________"); Log.Ln();
   Log.String("_______________________________"); Log.Ln();
END   dmTestArrPtr.

 dmTestArrPtr.Run

 DevDebug.UnloadThis dmTestArrPtr
So, I have declared two arrays of different length each, and I am accessing them via PtrArr. The value of PtrArr is assigned at run-time to either StArr10 or StArr20. The program compiles without any problems. The run-time behaviour is not what I have expected. The output generated by the program is:
_______________________________
_______________________________
Arrays before modification ..
Arr10= 1.0
Arr10= 2.0
Arr10= 3.0
Arr10= 4.0
Arr10= 5.0
_______________________________
Arr20= 2.0
Arr20= 4.0
Arr20= 6.0
Arr20= 8.0
Arr20= 10.0
Arr20= 12.0
Arr20= 14.0
Arr20= 16.0
Arr20= 18.0
Arr20= 20.0
_______________________________
_______________________________
Arrays after modification ..
Modified Arr10= 0.5
Modified Arr10= 1.0

Modified Arr10= 6.0
Modified Arr10= 8.0
Modified Arr10= 10.0
_______________xxx_____________
Modified Arr20= 2.0
Modified Arr20= 4.0

Modified Arr20= 3.0
Modified Arr20= 4.0
Modified Arr20= 5.0
Modified Arr20= 6.0
Modified Arr20= 7.0
Modified Arr20= 8.0
Modified Arr20= 9.0
Modified Arr20= 10.0
_______________xxx_____________
_______________xxx_____________

In the StArr10 each element is multiply by 2, and in the StArr20 each element is divided by 2. First two elements of StArr10 and StArr20 have wrong values!!

So, my question is:
How I should implement such a feature correctly?

There is a warning saying:
VAL, PUT, PUTREG, and MOVE may crash BlackBox and/or Windows when not used properly.
Never use VAL (or PUT or MOVE) to assign a value to a BlackBox pointer. Doing this would corrupt the garbage collector, with fatal consequences.
and I have experienced (hard way) when the result of VAL has been assigned to a BBox pointer.

Cheers,
Darek


P.S. I am using BBox CP version 1.6.
User avatar
Robert
Posts: 177
Joined: Sat Sep 28, 2013 11:04 am
Location: Edinburgh, Scotland

Re: How to (correctly) implement it?

Post by Robert »

Sorry - I have only had a quick look at this post and not read it carefully, so have probably misunderstood the question.

This reply might be relevant or helpful.

I have done something similar to address the ACML (Amd Core Maths Library), a FORTRAN DLL packaged by AMD.

I declare a normal Component Pascal "Vector", in module Vec, on the heap using NEW.

Code: Select all

TYPE
  Vector*     =  POINTER  TO  ARRAY  OF  REAL;

PROCEDURE  New* (size : INTEGER) : Vector;
  VAR
    vec  :  Vector;
  BEGIN
    IF  size  >  0  THEN  NEW (vec, size)
                    ELSE  ASSERT  (size = 0, 20)  END;
    RETURN  vec
  END  New;
The address I pass to the DLL is derived by this function:

Code: Select all

PROCEDURE  Vector (vec : Vec.Vector) : Dll.Vector;
  BEGIN
    RETURN  SYSTEM.VAL (Dll.Vector, SYSTEM.VAL (INTEGER, vec) + 16)
  END  Vector;
where the module Dll exports

Code: Select all

  Vector*   =  POINTER  TO  ARRAY [untagged]  OF  REAL
Works for me, goodness knows if it is "Correct".
manumart1
Posts: 67
Joined: Tue Sep 17, 2013 6:25 am

Re: How to (correctly) implement it?

Post by manumart1 »

I have noticed that after

Code: Select all

PtrArr := S.VAL(oPtrArr, S.ADR(StArr10))
this holds:

Code: Select all

PtrArr[0] = StArr10[2]
PtrArr[1] = StArr10[3]
PtrArr[2] = StArr10[4]
PtrArr[3] = 0
PtrArr[4] = 0
and after

Code: Select all

PtrArr := S.VAL(oPtrArr, S.ADR(StArr20))
this

Code: Select all

PtrArr[0] = StArr20[2]
PtrArr[1] = StArr20[3]
PtrArr[2] = StArr20[4]
..
PtrArr[7] = StArr20[9]
PtrArr[8] = 1 = StArr10[0]
PtrArr[9] = 2 = StArr10[1]
I do not know the reason, perhaps related to the internal representation of data types made by the compiler.
Making

Code: Select all

PtrArr := S.VAL(oPtrArr, S.ADR(StArr10) - 16);
PtrArr := S.VAL(oPtrArr, S.ADR(StArr20) - 16);
seems to work, but after some executions a trap happens.

Instead of having static arrays and trying to access to them via a pointer interface, it is much easier to have only dynamic arrays, this way:

Code: Select all

VAR a, b: POINTER TO ARRAY OF REAL;

NEW(a, MSize);
NEW(b, MSize * 2);

PtrArr := a;
FOR i := 0 TO LEN(PtrArr) - 1   DO
   PtrArr[i] :=  2.0 *  PtrArr[i] ;
END;
PtrArr := b;
FOR i := 0 TO LEN(PtrArr) - 1   DO
    PtrArr[i] :=  0.5 *  PtrArr[i] ;
END;
To interface the external program, following the example by Robert, I would try:

Code: Select all

PROCEDURE Vector (vec: Vec.Vector): Dll.Vector;
    VAR v: Dll.Vector;
BEGIN v := vec^;
    RETURN v;
END  Vector;
This way you avoid SYSTEM.VAL.
You allocate the dynamic array with NEW(a, 5) and pass a^ to the external program.
Post Reply