1.Foreign
imports and exports
Mathemagix both allows the user to import
functionality from C++ template languages and to export Mathemagix
functions and classes back to C++. The syntax for importating and
exporting functionality is as follows:
foreign cpp import import_body;
foreign cpp export export_body; |
The import and export bodies essentially contain dictionaries between
C++ names and Mathemagix names of various
classes, functions and other variables or constants.
In the case of foreign imports, it is usually necessary to specify
options which should be passed to the compiler and to the linker, as
well as some include statements or macro definitions which are
necessary in order to compile the imported code. This can be done
using the following keywords:
cpp_flags |
: |
flags that should be passed to the C++ compiler
|
cpp_libs |
: |
flags that should be passed to the C++ linker
|
cpp_include |
: |
C++ files that should be included in order to compile the
imported code
|
cpp_preamble |
: |
macro definitions which are necessary to compile the imported
code
|
A typical example of the use of these keywords occurs at the start of
the file basix/int.mmx:
foreign cpp import {
cpp_flags "`basix-config --cppflags`";
cpp_libs "`basix-config --libs`";
cpp_include "basix/int.hpp";
cpp_preamble "#define int_literal(x) as_int (as_string (x))";
…
} |
The C++ compiler and linker flags are taken to be the results of the
shell command basix-config which outputs the
appropriate flags as a function of the user's environment. The header
file basix/int.hpp contains various utility functions
for machine integers which are imported into Mathemagix.
The additional macro int_literal
is also imported as the constructor of machine integers from literal
integers.
2.Importing classes
from C++
Inside the body of a foreign cpp
import statement, the C++
class my_class can be
imported into Mathemagix under the name My_Class using
class My_Class == my_class; |
For instance, we may import the class integer
as Integer using
class Integer == integer; |
Any imported class my_class
is required to provide the following standard routines:
bool operator == (const my_class&, const my_class&);
// equality predicate
bool operator != (const my_class&, const my_class&);
// inequality predicate
bool exact_eq (const my_class&, const my_class&);
// predicate for syntactic equality
bool exact_neq (const my_class&, const my_class&);
// predicate for syntactic equality
bool hard_eq (const my_class&, const my_class&);
// predicate for hard equality (of pointers for reference counted objects)
bool hard_neq (const my_class&, const my_class&);
// predicate for hard inequality (of pointers for reference counted objects)
nat hash (const my_class&);
// a hash code compatible with operator ==
nat exact_hash (const my_class&);
// a hash code compatible with exact_eq
nat hard_hash (const my_class&);
// a hash code compatible with hard_eq
syntactic flatten (const my_class&);
// a flattener for objects of type my_class |
In practice, some of these routines can usually be derived from the
others. In the file
one may find various macros to this effect, such as TRUE_TO_EXACT_IDENTITY_SUGAR.
3.Importing containers
from C++ and exportation of categories
In a similar way, a C++ container class my_container<param_1,
…, param_n> can be imported as a Mathemagix container class with name My_Container using
class My_Container (P_1: Cat_1, …, P_n: Cat_n) ==
my_container (P_1, …, P_n); |
The parameters of imported C++ containers are necessarily type
parameters, and it is required to specify the types of these
parameters, which are categories. For instance, in numerix/complex.mmx, the container class
complex<R> of
complex numbers is imported using
class Complex (R: Ring) == complex R; |
In the case when the categories of the parameters are non trivial
(e.g. in the above example where Ring
is the category of the parameter R),
it is necessary to specify a dictionary between the members of the
category and their corresponding names in C++.
Indeed, in the C++ implementation of an addition on complex numbers,
we add the real and imaginary parts using the C++ operator +. At some place, we have to specify that
this C++ operator +
corresponds to the Mathemagix operator infix +. This can be done
by exporting the category Ring
to C++:
foreign cpp export {
category Ring == {
convert: Int -> This == keyword constructor;
prefix -: This -> This == prefix -;
infix +: (This, This) -> This == infix +;
infix -: (This, This) -> This == infix -;
infix *: (This, This) -> This == infix *;
}
} |
This example illustrates the general syntax for the exportation of
categories to C++: every field
mmx_function: (S_1, …, S_n) -> D; |
in the definition of the category is replaced by a declaration
mmx_function: (S_1, …, S_n) -> D == cpp_function; |
where cpp_function is
the C++ name corresponding to the function mmx_function.
4.Importing variables
and functions from C++
Inside the body of a foreign cpp
import statement, one may
import a C++ function or variable with name cpp_name
into Mathemagix under the name mmx_name using the syntax
mmx_name: Type == cpp_name; |
The type Type of the
imported function or variable should be specified on the Mathemagix
side. For instance, some of the basic operations on strings can be
imported using
foreign cpp import {
prefix #: String -> Int == N;
postfix []: (String, Int, Int) -> String == postfix ();
infix *: (String, String) -> String == infix *;
infix ><: (String, String) -> String == infix *;
infix <<: (Alias String, String) -> Alias String == infix <<;
} |
The standard ports mmin,
mmout and mmerr for input, output and errors are
imported using
foreign cpp import {
mmin : Port == mmin;
mmout: Port == mmout;
mmerr: Port == mmerr;
} |
5.Importing template
functions from C++
Template functions can be imported from C++ into Mathemagix
using the same syntax as for the importation of ordinary functions, by
putting all declarations inside a forall block. For instance, the following basic imported functions
on vectors were extracted from basix/vector.mmx:
foreign cpp import {
forall (C: Type) {
prefix #: Vector C -> Int == N;
postfix []: (Vector C, Int) -> C == postfix [];
postfix []: (Alias Vector C, Int) -> Alias C == postfix [];
postfix []: (Vector C, Int, Int) -> Vector C == range;
reverse: Vector C -> Vector C == reverse;
infix ><: (Vector C, Vector C) -> Vector C == append;
}
} |
Inside the forall block
it is possible to impose additional constraints on the parameters
using the assume
statement. For instance, we may extend the imported functions on
vectors as follows:
foreign cpp import {
forall (C: Type) {
prefix #: Vector C -> Int == N;
postfix []: (Vector C, Int) -> C == postfix [];
postfix []: (Alias Vector C, Int) -> Alias C == postfix [];
postfix []: (Vector C, Int, Int) -> Vector C == range;
reverse: Vector C -> Vector C == reverse;
infix ><: (Vector C, Vector C) -> Vector C == append;
assume (C: Abelian_Group) {
prefix -: Vector C -> Vector C == prefix -;
infix +: (Vector C, Vector C) -> Vector C == infix +;
infix -: (Vector C, Vector C) -> Vector C == infix -;
}
}
} |
Here Abelian_Group stands for the following category:
foreign cpp export {
category Abelian_Group == {
convert: Int -> This == keyword constructor;
prefix -: This -> This == prefix -;
infix +: (This, This) -> This == infix +;
infix -: (This, This) -> This == infix -;
}
} |
6.Exporting to C++
Classes, functions and variables can be exported back to C++ using the
same syntax as for importations. For instance, the class Point from the chapter
about user defined classes can be exported together with some
basic routines as follows
foreign cpp export {
class Point == point;
point: (Double, Double) -> Point == keyword constructor;
postfix .x: Point -> Double == get_x;
postfix .y: Point -> Double == get_y;
} |
In order to use C++ template functions with Point
as a parameter, the required operations from the category of that
parameter should be exported to C++ as well. For instance, assume that
we wish to use the operations and from the previous section on vectors of points. Then we
first have to define the abelian group operations on Point:
convert (i: Int): Point == point (as_double i, as_double i);
prefix - (p: Point): Point == point (-p.x, -p.y);
infix + (p: Point, q: Point): Point == point (p.x + q.x, p.y + q.y);
infix - (p: Point, q: Point): Point == point (p.x - q.x, p.y - q.y); |
We next have to export these operations to C++, while making sure that
the C++ names of the operations correspond to the exported names of
the operations of the category Abelian_Group:
foreign cpp export {
convert: Int -> Point == keyword constructor;
prefix -: Point -> Point == prefix -;
infix +: (Point, Point) -> Point == infix +;
infix -: (Point, Point) -> Point == infix -;
} |
Remark 1. In the current
implementation of the compiler, the mandatory operations operator ==, operator
!=, exact_eq,
exact_neq, hard_eq, hard_neq,
hash, exact_hash, hard_hash
and flatten of Mathemagix-compatible C++
types are not exported to C++. For instance, assuming that
the user redefined the default flattening routine for Point, this routine should be exported
explicitly to C++ if we want vectors of points to be printed
correctly.
© 2012 Joris van der Hoeven
Permission is granted to copy, distribute and/or modify this document
under the terms of the
GNU General Public License. If you
don't have this file, write to the Free Software Foundation, Inc., 59
Temple Place - Suite 330, Boston, MA 02111-1307, USA.