next up previous contents index
Next: Aliases of object types Up: Objects and structures Previous: Objects   Contents   Index


How to create and access objects

Object types are created in the initialization file (cf. 3.5). This allows objects to be created when the interpreter is running.

The object types that are available when the interpreter is running are contained in the array

obj_typ *Obj_typ;

If the section !def of the initialization file contains for example the following :

!def
; Objects of type 1 (comment line)
defobj1
-1
0
2
Objects of type 1 : 
ndim1_1
ndim1_2
nb1
; Objects of type 2 (comment line)
defobj2
0
2
3
Objects of type 2 : 
ndim2_1
ndim2_2
ndim2_3
nb2
.

2 object types will be created, Obj_typ[0] and Obj_typ[1].


The objects are contained in the array

obj **Obj;

Obj[i][j] will contain the j-th object of type i. The type 0 corresponds to the first type of objects created in the initialization file, the type 1 to the second type of objects created in the initialization file, and so on.

When the interpreter is running, objects are created using the corresponding command. For instance, in the preceeding example, the command

- interpcom -> defobj2 xxx

will create an object of the second type, whose name is xxx. Of course the dimensions of the arrays must have been defined previously. In the example the hidden variables ndim2_1, ndim2_2 and ndim2_3 (which are the names of the dimensions of the arrays) must have been filled with the appropriate dimensions. The way to do this will be explained later.

In the previous example the function

int obj_create(int argc, char *argv[]);

is the one that is called to create objects. This is the function that implements the command objdef. In the above example, defobj2 is in fact the following program

:defobj2
3
0
0
objdef 1 #1

The 1 after objdef means that the command defobj2 will create objects of type 1 (which is the second type of objects defined in the initialization file). When the initialization file is read, a program of this type is created for each object type. So the command

- interpcom -> defobj2 xxx

is equivalent to this one :

- interpcom -> objdef 1 xxx

but the first one is more explicit. It is possible to define many objects in one command, using arrays of objects. For instance the command

- interpcom -> defobj2 zz[3]

will define 3 objects, zz[1], zz[2] and zz[3]. Note that it is now possible to use the name zz alone for another object.

Now we show how to fix the dimensions of the arrays. This is done using the function

int S_convert_int(char *);

For instance, if ndim2_1 must be 100, one can do as follows :

    char h[100];
    memset(h, 0, 100);
    sprintf(h, "ndim2_1=%d", 100);
    S_convert_int(h, flow_interp);

Then the value of ndim2_1 will be $100$. This means that indices can be anything between $0$ and $100$ (including these values). Here flow_interp is a pointer to the flow_data structure used to describe the current thread (cf. 10.4).


The simplest case is when the objects of type defobj2 have always the same dimension. The variables ndim2_1, ndim2_2 and ndim2_3 are then filled once for all in the beginning of the execution of the interpreter (or when one enters a running mode), or by a command before the creation of the objects. One can also assign values to variables by using the section !var of the initialisation file (cf. 3.3). But in some cases it may be useful to be able to create objects with non constant dimensions. This can be done by creating an extra command of creation of objects. For instance suppose that I want to create the command Def_Obj2 that should be used in the following way :

- interpcom -> Def_Obj2 xxx nn1 nn2 nn3

should create an object of type defobj2 whose name is xxx, whith dimensions
ndim2_1=nn1, ndim2_2=nn2 and ndim2_3=nn3. Here nn1, nn2 and nn3 could be integers or numeric expressions that the expression evaluator can parse. The command is implemented as follows

int
Def_Obj2_cmd(int argc, char *argv[])}
{ 
    int     n1, n2, n3;}
    char    h[100], *k[4];}
    flow_data *flow_interp;

    flow_interp = (flow_data *) argv[-1];
    n1 = convert_int(argv[2], flow_interp);
    n2 = convert_int(argv[3], flow_interp);
    n3 = convert_int(argv[4], flow_interp);
    sprintf(ndim2_1=%d", n1);
    S_convert_int(h, flow_interp);
    memset(h, 0, 100);
    sprintf(ndim2_2=%d", n2);
    S_convert_int(h, flow_interp);
    memset(h, 0, 100);
    sprintf(ndim2_3=%d", n3);
    S_convert_int(h, flow_interp);
    k[0] = (char *) flow_interp;
    k[1] = ch_copy("objdef");
    k[2] = ch_copy("1");
    k[3] = ch_copy(argv[1]);
    obj_create(3, k + 1);
    free(k[1]);
    free(k[2]);
    free(k[3]);
    return 0;
}

Here the function obj_create is called. This means also that we emulate the command objdef described previously. Note that the address of the flow_data structure corresponding to the running thread is argv[-1] (cf. 5.2, 10.4). We need also to pass it to the command objdef that we emulate with the function obj_create.

Of course the function Def_Obj2_cmd could be improved. One should test for instance the positivity of n1, n2 and n3 before calling obj_create. One should also be sure that the name of the object (which is argv[1]) is not already used. This can be done with the function

int sketch_obj(char *, int *, flow_data *);

that will be described later. One could also verify that the creation of the object was successful. If it is not the case, the function obj_create returns -1. If there is a test of the value returned by obj_create, it is no longer necessary to verify if the name of the object has already been used. In the preceeding example the function convert_int is used to parse numeric arguments (cf. 6.3 and 12.2).


Now we show how to access objects which have been created. Suppose that we have a command, called Xcom1, that uses an object of type defobj2. So the command should be used as follows :

- interpcom -> Xcom1 xxx

where the argument xxx is supposed to be the name of an object of type defobj2. The function that implements this command could be as follows :

int
Xcom1_cmd(int argc, char *argv[])
{ 
    int iw, i0, *dim;
    double ***A;
    flow_data *flow_interp;

    flow_interp = (flow_data *) argv[-1];
    iw = sketch_obj_restr(argv[1], &i0, 2, flow_interp);
    if (iw != 2) {
        error_mess(3, flow_interp);
        return 1;
    }
    dim = Obj[1][i0].dim;
    A = (double ***) Obj[1][i0].adresse;
/*  Here should follow the useful part of the function */
    .
    .
    .
    return 0;
}

The function

int sketch_obj_restr(char *str, int *i0, int j, flow_data *flow_interp);

works as follows : if str contains the name of an object of type j-1 it returns j. Otherwise it returns 0. Recall that the first kind of objects defined in the initialization file has type 0, the second has type 1, and so on. If the function does not return 0, i0 will be the index of the object of type j-1 that has the given name. So the object will be Obj[j-1][i0]. This function can be used also with structures instead of objects; in this case negative values for j are used (cf. 6.10). In the above example we test if the string given as argument for the command Xcom1 is the name of an object defobj2 (i.e an object of type 1). If it is not the function prints an error message (cf. 3.9) and returns. If the name is correct, we know that this object is Obj[1][i0]. The member dim = Obj[1][i0].dim will give the dimensions of the corresponding array (a 3-dimensional array of double precision real numbers). The dimensions of this array are dim[0], dim[1] and dim[2]. The array is
A = (double ***) Obj[1][i0].adresse. It can then be used. Note that the function
sketch_obj_restr needs to know what is the running thread and how we can retrieve a pointer to the flow_data structure corresponding to this thread, as for the preceeding command Def_Obj2_cmd (cf. 10.4, 5.2).

Another function can be used to extract an object from its name.

int sketch_obj(char *str, int *i0, flow_data *flow_interp);

If str contains the name of an object of type j this function returns j+1, and the object is Obj[j][i0]. The function returns a negative value if str is the name of a structure, and 0 if the name has not been used at all. This function can be used if the type of the object is not known a priori. Otherwise, the function sketch_obj_restr is much faster (especially if many object types are defined).

It is possible to know the value of some element of an object from the interpreter. For instance, recall that objects of type defobj2 are 3-dimensional arrays of double precision real numbers. If xxx is one such object, &xxx(1,2,5) will be interpreted by the expression evaluator as the corresponding term of the array defined by xxx.


Example :


- interpcom -> defobj2 xxx
- interpcom -> const xxx 1
- interpcom -> &xxx(1,2,5)
                 1.000000
- interpcom -> i=2
                 2.000000}
- interpcom -> j=3
                 3.000000
- interpcom -> x=5.5
- interpcom -> x+&xxx(1,i,i+j)}
                 6.500000


So values of objects at some points can be passed as arguments to commands. For objects containing complex numbers (type 3,4,5 or 6 of variable), the operator & will return the real part. It is also possible to have the real or imaginary part by using the suffixes .r or .i respectively. For instance, if zzz is an object which is a 2 dimensional array of complex numbers, &zzz(1,2).r and &zzz(1,2).i are respectively the real part and the imaginary part of the term of indices (1,2) of zzz.


Two commands are available to fill objects.

With the first one, setobj, it is possible to impose a value to a given term of an object. For instance, if Obj is a real object (i.e. if it contains integers or real or double precision real numbers) with n dimensions, the instruction

setobj Obj x d1 ... dn

with give the value x to the term (d1,...,dn) of Obj. Here x can be a number or a numerical expression that will be sent to the expression evaluator.


Example :

- interpcom -> defobj2 xxx
- interpcom -> setobj xxx 1.5 2 3 4
- interpcom -> &xxx(2,3,4)
                 1.500000

For a complex object, the two components (real and imaginary, or modulus and phase according to the kind of object) must be given as third and fourth parameters of the command.


With the second command, fillobj, it is possible to fill all the object. For instance, if Obj is a real object (i.e. if it contains integers or real or double precision real numbers) with n dimensions, the instruction

fillobj Obj xxx

will fill the object according to the numerical expression xxx. Here xxx must be an expression depending on the variables xi, for i between 0 and n-1, representing the indices of the object. This expression will be sent to the expression evaluator and can also contain other variables or numerical functions recognized by it.


Example :

- interpcom -> defobj2 xxx
- interpcom -> dt=0.001
- interpcom -> fillobj xxx x0*dt*cos(x1*dt+x2*dt)

For complex objects two formulas must be given, one for each component of the terms of the objects.


next up previous contents index
Next: Aliases of object types Up: Objects and structures Previous: Objects   Contents   Index
2009-11-12