> <\body> glue mechanism> New functionality can be added to using the ``glue'' mechanism. There are two typical types of glue: <\enumerate> The user has written a new C++ class or a new C++ template class, and would like to use this class from withing the interpreter. The user has written an interface to an external software which comes with its own language and typesystem. In this case, one would like to have an automatic mechanism for making the functionality of the external software available from within . In both cases, the user has to write a ``glue definition routine'', which declares the functionality provided by the glue, using a standard API. This routine can then be called from some central point (at startup or when loading a dynamically linked glue library) in order to make the glue functionality available from within the current evaluator. The glue mechanism comes in two layers. On the one hand hand, we provide a low-level C++ API (see section below), which allows you to manually add new types, routines and converters. On top of this layer, the language also provides the keyword , which allows for ahigher level description of the glue, which can also be used by the compiler. This second preferred mechanism is not yet documented. Let us first consider writing a glue definition routine for a simple C++ class named . Assume also that the class comes with two routines which we wish to export to : <\cpp-fragment> color named_color (const string& name); color invert_color (const color& c); Then the glue definition routine for typically looks as follows: <\cpp-fragment> void define_color () { \ \ define_type\color\ ("Color"); \ \ define ("named_color", named_color); \ \ define ("invert", invert_color); } The instruction exports the type to under the name . The two next lines export the routines and under the same names. The type of the routines and is automatically inferred by the gluing mechanism. The template parameter is not really important for our simple example; below, we will see how to use it for the conditional definition of glue in the case of template types. Whenever a new type is exported to , a few standard routines are required to be implemented for this type. In the case of our class , the following routines must be provided: <\cpp-fragment> nat hash (const color& c); generic flatten (const color& c); bool operator == (const color& c1, const color& c2); bool operator != (const color& c1, const color& c2); The first routine computes a hash value for , where stands for . The second flattening routine converts to a generic expression; this routine will be used when printing a color. The last two routines implement equality testing. Notice that there are several types of equality in . In addition to the and operators, you may wish to define <\cpp-fragment> bool hard_eq (const color& c1, const color& c2); bool hard_neq (const color& c1, const color& c2); bool eq (const color& c1, const color& c2); bool neq (const color& c1, const color& c2); The routine is a fast test whether and are represented in the same way in memory. In the case of pointer objects like vectors, one typically tests whether the pointers match; in particular, two identical vectors which are stored at different locations would not be ``hard equal''. The routine tests whether and are syntactically equal. Typically, the rational number might be equal to the integer , without being syntactically equal. The main routines for defining new types and routines are specified in . Here follows a short description: <\explain|<\cpp> void \C\ (const generic& name) > This routine declares a new C++ type and exports as . Whenever a new type is defined, a few other derived types are defined as well (see section below). <\explain|<\cpp> void \C\ (const generic& name, const C& x) > This routine exports the constant as . <\explain|<\cpp> void \C\ (generic (*fun) (const C&)) > This routine exports a constructor for instances of type . <\explain|<\cpp> void \D\ (const generic& name, D (*fun) ()) void \D,S1\ (const generic& name, D (*fun) (const S1&)) void \D,S1,S2\ (const generic& name, D (*fun) (const S1&, const S2&)) ... > This routine exports a function as . <\explain|<\cpp> void \D,S\ (const generic& name, D (*fun) (const S&), nat pen) > This routine exports a type conversion routine D> (which defaults to the default converter from to ) with a given penalty . In cases of ambiguity, conversion chains with the least penalty are preferred. The of the converter should be one among , and , depending on the nature and transitivity properties of the converter. Upgraders are used for automatic constructors (such as rational>) and downgraders for type inheritance (such as shape>). Plain converters cannot be composed with other converters. <\explain|<\cpp> void \Cond\ (const generic& name, generic (*f) (const generic&)) > This routine exports a language primitive . The argument to the primitive is a tuple which is not evaluated before is called. The primitive should take care of the possible evaluation of its arguments itself. Whenever a new type is defined by the user, a few other related types are added automatically. More precisely, will automatically define the following types: <\explain|<\cpp> alias\C\ > This type is used for aliases to instances of type (see ). The C\> type plays a similar role as the C++ reference type , but there are some subtle differences. In , aliases are much slower, but more functional and powerful in nature. More precisely, C\> is an abstract class with two promises for read-access and write-access. In particular, an alias need not correspond to a physical location in memory. To understand one major difference, consider the following session: <\session|mathemagix|default> <\unfolded-io|> v: Vector Generic := vector (1, 2, 3, 4, 5) <|unfolded-io> 1,2,3,4,5> <\input|> x: Alias Generic == v[4]; <\unfolded-io|> v := vector (6, 7, 8, 9, 10) <|unfolded-io> 6,7,8,9,10> <\unfolded-io|> x <|unfolded-io> In , the second line would be incorrect. However, in , a read-access for is only performed at the last line, when computing . Notice also that performs only a read-access for , whereas would typically perform a write-access (see remark below). <\explain|<\cpp> tuple\C\ > This type stands for a tuple of elements of type . By defining a vector constructor using <\cpp-fragment> define\Cond\ ("vector", make_vector\C\); where <\cpp-fragment> template\typename C\ vector\C\ make_vector (const tuple\C\& t); this will allow you to enter vectors in the interpreter using <\mmx-fragment> vector(a,b,c,d,e,f) <\explain|<\cpp> alias\tuple\C\ \ > This type is also added, for coherence. <\remark> Concerning references types, one should notice another subtlety when writing accessors for compound data types. For instance, consider the methods <\cpp-fragment> template\typename C,typename T\ C table\C,T\::operator [] (const T& key) const; template\typename C,typename T\ C& table\C,T\::operator [] (const T& key); The overloading will allow for both read-access and write-access using the same syntax. However, it sometimes happens that you have a (non constant) table which corresponds to a global environment. In that case, any access to this table will be a write-access independently if you really perform some modifications of the table. This may lead to subtle errors if you really wanted to perform a read-access, since a write-access might actually modify the table (allocating a non existant key-value pair, for instance). This subtlety does not occur for the C\> type. The routine allow the user to define automatic converters between different types. This facility is quite powerful, but has to be used with care: since automatic converters are applied in a very systematic way, they may even be applied in sitations which the user did not foresee. First of all, the user has to carefully select between the three different types of converters: upgraders, downgraders and plain converters. These types differ in the way they may be composed: plain converters are neither left nor right composable, upgraders are left composable, and downgraders are right composable. Given a left composable converter C> and an arbitrary converter B>, automatically adds a converter C> (which is left composable if B> is left composable and right composable if C> is right composable). Similarly, if B> is right composable and C> is arbitrary, then we add a converter C>. Typically, upgraders correspond to type constructors, such as Rational> or > Matrix(C)>. Similarly, downgraders correspond to type inheritance, such as Shape>, Series(C)>, etc. Plain converters are often used for converters which may involve some loss of data, such as Double>. A second important property of a converter is the correponding penalty: when several conversion schemes can be used in order to apply a function to some arguments, the scheme with the lowest penalty will be preferred (here we notice that the penalty of a conversion (C,D)> is the maximum of the penalties of the conversions C> and D>). Among all possible schemes with the lowest penalty, the most specialized function will be chosen (a type is strictly more specialized than if there exists a converter U>> but no converter T>). If no conversion schemes can be found to apply the function, then it will be applied symbolically. Currently, the following penalties are provided: <\description> >This corresponds to an exact match. >This corresponds to the penalty for automatic language-related conversions, such as C>. >This penalty should be used for conversions U>, where may be viewed as a mathematical subset of . Example: Rational>. This penalty is the default one for conversions U> when is different from . >This penalty should be used for conversions U> which can be viewed as mathematical homomorphisms, but not as inclusions. Examples: Int> or, more generally, Modular(p)>. >Sometimes, two distinct libraries implement the same or a similar type. In that case, it may be interesting to provide automatic converters between these types (in both ways). Using the higher penalty for this kind of conversions will ensure that operations are performed in the library of the types of the arguments, unless an implementation is only available in the other library. >This penalty should be used for all conversions which, even though convenient for the user, entail some loss of information. Example: Double>. >For any type , this is the penalty of the conversion Generic>. Hence, if the user provides a generic fall back implementation of an operation, then this will be the penalty for the application of the fall back method. >Many composite types, such as , come with an inclusion Complexify(C)>. Although it is generally correct to use for the corresponding penalty, many unwanted conversions may arise when . For this reason, the default penaly for conversions of the kind T> is the maximal penalty . Some common sources of bugs when using overloading and automatic conversions are the following: <\enumerate> You specified an upgrader Polynomial(Generic)>, but addition on generic polynomials can not be applied so as to add one to a polynomial. The point here is that the penalty of the conversion Polynomial(Generic)> should be the maximal penalty , which is larger than the penalty for the symbolic addition of expressions. The solution to this problem is to implement the following two specialized additions: <\verbatim> \ \ \ \ +: (Polynomial (Generic), Generic) -\ Polynomial (Generic) \ \ \ \ +: (Generic, Polynomial (Generic)) -\ Polynomial (Generic) Actually, these operations may be useful for other coefficient types than , since they can usually be implemented in a particularly efficient way. You forgot to define a generic fall back method for some operation. Sometimes, your implementation may rely on the assumption that a given operation has no implementation, so that it will be applied symbolically. When providing an implementation of for some type, this assumption may suddenly be violated and provoke infinite loops or incorrect results. In that case, you should provide a default symbolic implementation for . A package with a collection of types, routines and glue definition routines should be compiled into a dynamic library, which can then be loaded on the fly into the interpreter. Names of glue libraries should be of the form >.la> or >.so> and the principal glue definition routine for the library should carry the name >>. Notice that names are mangled, so you may need to declare >> as a . In the subdirectories and , you may find two simple examples on how to glue new code to . For larger projects, we refer to the documentation on coding conventions and how to add new packages. Assume that we want to add a routine for computing Fibonacci numbers to the glue. The file with the routine and the corresponding glue definition routine would typically look at follows: <\cpp-fragment> #include "glue.hpp" using namespace mmx; \; int fibonacci (const int& n) { \ \ if (n \= 1) return 1; \ \ return fibonacci (n-1) + fibonacci (n-2); } \; void define_standard_fibonacci () { \ \ define\always\ ("fibonacci", fibonacci); } \; void (*define_fibonacci) () = define_standard_fibonacci; The very last line is added to prevent name mangling of . We may now compile the file into a shared library : <\shell-fragment> g++ --shared `basix-config --cppflags --libs` \ \ \ \ fibonacci.cpp -o libmmxfibonacci.so After putting the directory which contains in your , you may now use the library from within : <\session|mathemagix|default> <\input|> use "fibonacci" <\unfolded-io|> fibonacci(37) <|unfolded-io> To be completed. It is possible to catch exceptions occurring in glued C++ routines within the interpreter. In order to make this work, you should first configure using the (default) option . Next, your glue code may raise exceptions of the type . In fact, it is better not to directly raise exceptions by yourself, but rather use the convenience macros defined in . Indeed, distinguishes two main kind of exceptions, whose default behaviour can be configured: <\itemize> Normal exceptions are the typical exceptions that you want to make visible within the interpreter. Since the interpreter is much slower than the code anyway, it is a good practice to heavily test for erroneous exceptional cases in the glue routines. For instance, when making an array access, one should typically test the bounds. Normal exceptions are enabled using the default configuration option. When disabled, they will be replaced by assertions inside the code. Low level exceptions are additional checks that you may wish to add in critical parts of the code. For instance, low level array access is performance-critical, since it might be used intensively by other routines. When using the configuration option, you may make add additional checks for such low level routines. Since this may slow down the global performance of the system, this option is disabled by default, and should mainly be used for debugging purposes. Instead of directly gluing low level routines to the editor, we rather recommend to write a small wrapper with the necessary checks for the routine. The two above types of exceptions both come with their corresponding macros. The following macros should be used for raising exceptions: <\explain|<\cpp> (message) > Raises the error message . <\explain|<\cpp> (condition,message) > Raises the error message if the is not satisfied. <\explain|<\cpp> (condition,message) > When compiling using , this macro raises the error message if the is not satisfied. module system> Still to be written and documented. . If you don't have this file, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.>