[Contents] [TitleIndex] [WordIndex

Emulate Object Oriented in Scilab - part 2

Abstract

The current version of the Scilab language is procedural. The goal of this page is to explore ways to emulate OO in the Scilab language.

Introduction

The goal of this page is to show how to make a OO-like system in Scilab, based on a C++ hash map.

See in attachment tbxoo-v0.2.zip for a .zip which contains the full toolbox, validated on Windows.

On the use of hash maps

I make the hypothesis that I have a C++ class, "MyClass", which has 3 members : type, a, b and a "print" method. The goal is to be able to write in the Scilab console :

vu1 = myclass_new("Normale",1.0,0.5);
myclass_print(vu1);
myclass_destroy(vu1);

The method is based on the creation of an intermediate layer, base on a C++ hash map, which allow to associate an integer (the "token") with the C++ object. The map is managed by the file myclass_map.cpp, which contains :

typedef map<int , MyClass *> MyClass_map_type;
MyClass_map_type MyClass_map;
int MyClassCounter = 0;

The constructor is associated with MyClass_map_new, which takes as input arguments the creation parameters, and returns an integer :

int MyClass_map_new (char * type, double a, double b) {
MyClass * rv;
int token;
string name(type);
rv = new MyClass(name, a, b);
token = MyClassCounter;
MyClassCounter = MyClassCounter + 1;
MyClass_map[token] = rv;
return token;
}

The destructor takes as input argument the integer token and free the C++ object :

void MyClass_map_free ( int token ) {
MyClass_map_type::iterator it;
MyClass * rv = MyClass_map[token];
free(rv);
it = MyClass_map.find (token);
MyClass_map.erase(it);
}

The print method takes the token as input argument and triggers the corresponding method of the object :

void MyClass_map_Print ( int token ) {
MyClass * rv = MyClass_map[token];
return rv->Print();
}

One can easily implement the static methods (class methods) "size" and "tokens" which returns respectively the number of current objects and a Scilab array which contains the current tokens. The following script shows how to use these methods from the Scilab language :

nbvu = myclass_size(); // nbvu is 0
vulist = myclass_tokens(); // vulist is []
vu1 = myclass_new("Normale",1.0,0.5);
vu2 = myclass_new("Uniforme",1.0,2.5);
nbvu = myclass_size(); // nbvu is 2
vulist = myclass_tokens(); // vulist is [0 1]
myclass_destroy(vu1);
myclass_destroy(vu2);
nbvu = myclass_size(); // nbvu is 0
vulist = myclass_tokens(); // vulist is []

There is no difficulty to create the gateways.

The following is a list of advantages / drawbacks.

Advantages

the class "myclass".

Drawbacks

One gateway for several methods, designated by strings

Another possibility is to manage a single entry point (i.e. a single gateway), say "myclass" for example, which takes as first argument a string which represents the action to perform : "new", "destroy", "print". This method is used for example in grand.

vu1 = myclass("new","Normale",1.0,0.5);
myclass("print",vu1);
myclass("destroy",vu1);

Advantages

The method has the advantage of requiring only one gateway, which seems to be simpler. There is only one file to manage.

As we are going to see, this is the only advantage, against many drawbacks.

Drawbacks

This method has the following drawbacks.

tbx_completion.png

In short, this way of developping the gateway does not "scale" well with the number of methods.

Indeed, the gateway must contain the following sequence of if/then/else :

if ( action == "new" ) {
  // Source code for "new"
} elseif if ( action == "print" ) {
  // Source code for "print"
} elseif if ( action == "destroy" ) {
  // Source code for "destroy"
}

In practice, this leads to very long gateways, which are much more difficult to maintain. The source code in the gateway is typically of length n * 100, where n is the number of methods (i.e. "actions"). For example, the grand gateway contains 1000 source code lines. This is much more complicated to maintain than 10 gateways with 100 lines by gateway.

In practice, this also leads to some confusion in the design of the gateway. Indeed, the same gateway serves different purposes. In the end, the exact role of the gateway is complicated to understand. For example, grand allows to generate random numbers. But it also allows to initialize the seed for some of the random number generators. All generators do not require the same number of seeds, which leads to a complicated management of the input arguments.

The unit tests of such gateways are also more difficult to write. This is because updating the source code for one method may lead to a modification of the behaviour of another method, because the source code is the same. This might lead to increasing testing times.

Instead of doing this, a much simpler approach is to create one function by method (i.e. one function by "action"). In the following session, we use a class "myclass" which is associated to 3 methods.

vu1 = myclass_new("Normale",1.0,0.5);
myclass_print(vu1);
myclass_destroy(vu1);

This way of developing leads to separate gateways, separate helps and separate unit tests. It is much simpler to develop, maintain and understand and is much closer to an 00 methodology.

Conclusion

In practice, the method based on hash maps performs quite well. But some improvments may be necessary / useful, for example :


2022-09-08 09:27