BLOP is written in C++. Its user interface is CINT (a C/C++ interpreter). So the syntax of the user's scripts is C++, in which all the classes and functions provided by the BLOP package are usable. Why have I chosen CINT?
Object-oriented design is a great help in software developement and maintenance. However, when the whole software is 'exported' through a C++ interpreter interface to the user, this can make its daily use awkward and difficult, and gets in conflict with the main goal, which is to provide an easy-to-use scripting language. For small, often changing tasks you don't want to type thousands of characters, writing a well-structured, object-oriented program. BLOP was therefore designed to satisfy these needs.
It's of no doubt, that a detailed documentation (which is easy to navigate) is necessary for any software. But a good design does not only mean, that everything is well structured and well documented: it must reflect the human way of thinking, and strict conventions must be followed throughout the whole software, such that if the user gets familiar with these concepts, he does not have to browse the documentation for a new command or solution, but he can figure it out from his previous experiences and from these concepts. This is why I attempt to summarize these concepts. And there is another reason: this software most probably is far from completely matching these concepts. This summary should also set an ideal niveau, to which I will hopefully frequently be constrained by the feedback of users.
Ok, so much about philosophy. Let me tell some examples (which are not necesserily existing in the software...!) If you find yourself having trouble with frequently typing linetype(...) instead of the existing linestyle(...) function,
In this section you will most probably find expressions, which are not self-explanatory. Read this section without worrying about that, and maybe return later, after getting familiar with these.
frame::current().xaxis()->range(0,10);Clear and nice, isn't it? But who the hell wants to have always a clear and nice, but so long script just to set the axis range, or to do some other minor tasks? Nobody could convince a user that he should write 100s of characters for the easiest task.
Therefore there are wrapper functions, which are intuitive, easy to use, and: SHORT. They are implemented as static member function of the set class, to have a syntax which mimics gnuplots syntax. Here only a few examples are provided, refer to the header file set.h
void set::x1range(double min, double max); // set range of x1 axis void set::x1title(const var &); // set axis title
mpad &p = mpad::mknew(2,2).gap(1*MM).fillcolor(red).bordercolor(blue);
arrow *a = new arrow; a->autodel = true; a->from(MM, CM); a->to(3*CM, 5*CM); a->linewidth(MM); a->headangle(20); pad::current()->add(a);Using these functions, however, life is easier:
arrow::pdraw(MM,CM,3*CM,5*CM).linewidth(MM).headangle(20);
pad &p = pad::mknew(0,0,0.5,0.5); ,or pad &p = pad::mknew(0,0,0.5,0.5).bordercolor(blue).borderwidth(MM);Otherwise, if mknew returned a pointer, one should write:
pad *p = pad::mknew(0,0,0.5,0.5); pad &p = pad::mknew(0,0,0.5,0.5)->bordercolor(blue).borderwidth(MM);Returning a reference also indicates, that these functions (in principle :-) always succeed (returning a 0-pointer could indicate failure).
frame::current().legend()->borderwidth(MM).bordercolor(red);(if there would be a . instead of the ->, the whold statement could be seen by a superficial reader as setting many properties of frame::current())
default_xyzw(...);function, which has the same argument. All object created after calling this function will be initialized with the new default value. Example:
dgraph::default_pointcolor(blue); plot("datafile").ds(points()); // this file is plotted with blue points
In the case of plotting or drawing objects, the guideline was (see mknew, fdraw, etc) that the functions creating/drawing an object only take the absolutely necessary parameters, create an object, attach it to the current pad, container or canvas, and return a reference to this object. Any further property of them (such as orientation, color, fontsize, etc) can be set on the returned reference. This could be done there, because all these object properties were not used at the time of the function call, but only later, when your picture is printed to a terminal.
In many other cases this can not be done. For example when fitting or histogramming, one needs to specify many options to a C-function, which will be used at that moment. In order to avoid the many-argument-problem, in these cases the option classes will be used (for example fitopt for fitting, or histopt for histogramming. These classes are simply the collection of a lot of properties, parameters, or whatever you call them. They have corresponding member access functions with a self-describing name (your code will be more readable), and these member access functions return references to the object itself. Therefore, they can be chained, as described above. Moreover, they will have the default_xxx functions as well, to set the default values of these properties. This way you can very cleanly set default arguments to these functions, which take option classes as arguments.
For example consider the fitting procedure (concentrate only on the
last argument):
fit
The statement fitopt() creates a (temporary) object of type
fitopt, on which you call several member-setting
functions. If you want to make a lot of calls to the function
fit, with the same options, you can set the default values,
and then you don't have to provide them each time:
fitopt::default_x(_1,_2); fitopt::default_y(_3); fitopt::default_verbose(2); fit(Graph, Model);