Contents Previous Next Index
14 Advanced Connectivity
This chapter describes how to connect Maple to other applications. Maple can be connected as the main interface (for example, to a database), as a hidden engine (for example, as part of a Microsoft® Excel® plug-in), or side-by-side with another application (for example, a CAD application). You can also use Maple to generate code.
14.1 In This Chapter
Connecting to the Maple engine
Embedding external libraries in Maple
Connecting Maple to another program
Code generation
Connecting to the Maple Engine
There are several ways to use the Maple computation engine in other applications. For example, you can create a financial application that runs calculations using the MapleNet web service API; create a plug-in for Microsoft Excel in both Visual Basic and C++ to perform Maple computations in a spreadsheet; and create an engineering process to generate and batch process scripts using the Maple command-line interface. These are examples of real situations where you can use Maple as a calculation engine embedded in an external interface.
For more information, see MapleNet, OpenMaple, and The Maple Command-line Interface.
Using External Libraries in Maple
Most dynamic link-libraries (.dlls) that contain mathematical functions written in another programming language can be linked directly in Maple. Using the commands in these libraries usually requires you to translate the Maple data into a format that the external library can interpret. Maple provides an extensive API for data conversions, evaluation, and process control. You can, therefore, use a custom library of functions in Maple as if it were a regular Maple package. You can also use the Maple external API to connect Maple to a hardware device for data acquisition, link with an open source suite of utilities, or to avoid rewriting legacy code.
For more information, see External Calling: Using Compiled Code in Maple.
Connecting Maple to Another Program
You can set up Maple to communicate with another program by using the Maple external API. For example, by using the CAD package, you can set up Maple to communicate with CAD applications. Other methods are available, such as setting up a communication channel to the other program using the Sockets package.
For more information, see CAD Connectivity, Maple Plug-in for Excel, Connecting MATLAB® and Maple, and Accessing Data over a Network with TCP/IP Sockets.
Code Generation
The Maple programming language and the worksheet environment are ideal for creating prototypes. They are also ideal for error-free algebraic manipulations and long calculations. When you create a prototype that includes various formulas, you can easily write that program in the native language that is used by your application. The generated code can then be compiled and embedded directly in your application.
The CodeGeneration package provides commands for translating Maple code into other programming languages such as C, Visual C#, Fortran, Java, MATLAB®, and Visual Basic. The resulting code can be compiled and used in applications other than Maple.
For more information, see Code Generation.
14.2 MapleNet
MapleNet provides online viewing and execution of Maple documents and access to a Maple compute programming interface. Maple worksheets and workbooks can be viewed in web browsers and embedded components in those documents are interactive.
In addition, Maple help databases can be hosted to allow web-based navigation of Maple help content. The compute endpoint allows programmatic access to the Maple computation engine. Third party applications that require complex mathematical computations can send compute requests to MapleNet via standard HTTP POST request.
Computation on Demand
The MapleNet Compute Service allows for applications to send a Maple computation to MapleNet and receive the result of executing that computation. This service is an application programming interface, not an end user interface.
For more details about the Compute Service see https://www.maplesoft.com/documentation_center/MapleNet2019/MapleNetComputeAPI.pdf.
14.3 OpenMaple
OpenMaple is an interface that lets you access the Maple computation engine by referencing its dynamic-link library (.dll) file.
Note: The OpenMaple interface is available on all platforms supported by Maple. The convention in this guide is to use the terminology related to .dll files, in place of .so or .dylib on other systems.
You can use this interface to embed calls to Maple in other applications.
Interfaces to access the OpenMaple API are provided for use with C, C++, Java, Fortran, C#, and Visual Basic. All of these interfaces are built on the C API, so they all reference the primary library, maplec.dll, which is located in your Maple binary directory. This library can be accessed from other languages by following the protocol established in the maplec.dll file.
Complete example programs are available in the samples/OpenMaple subdirectory of your Maple installation. In conjunction with reading this section, you may want to try extending one or more of those examples before creating your own programs.
Application-specific header files can be found in the extern/include subdirectory of your Maple installation. If you are developing a Java application, you can find the jopenmaple.jar file in the java subdirectory of your Maple installation.
Runtime Environment Prerequisites
To run your application, two paths must be set up in your local environment.
the path to the maplec.dll file
the path to the top-level directory in which Maple is installed
In Windows, depending on the source programming language, calls to initialize the OpenMaple interface will locate these paths automatically so that the Maple commands will work without additional configuration steps.
Note: If your application does not initialize, set your Windows %PATH% environment variable to include the Maple bin.win or bin.X86_64_WINDOWS directory. To find out which path to use, run the kernelopts(bindir) command in Maple.
In Linux and macOS, the MAPLE, and LD_LIBRARY_PATH or DYLD_LIBRARY_PATH environment variables must be set before starting your application. To set these environment variables, add the following lines to the start-up script of your calling application, where $MAPLE is your Maple installation directory.
#!/bin/sh export MAPLE="/usr/local/maple" . $MAPLE/bin/maple -norun myapp $*
These commands run the maple launch script to configure your environment without starting Maple. The period (.) prefix in a Bourne shell causes the commands to be sourced, thus, applying the settings to future sessions. Starting the application would be done via the above script rather than calling the executable directly.
Interface Overview
Each OpenMaple application contains the following components:
a text callback to display or hide the output generated by Maple when an expression is evaluated
commands to initialize the Maple engine
calls to API commands to compute with Maple
The examples in this section show how to run a Maple computation using the OpenMaple API. Each example evaluates an expression and then exits. The examples in this section are intended to help you get started using the API. For detailed examples and descriptions, refer to the OpenMaple help page.
Text Callbacks
In each example, a text callback is defined. Output that would normally be displayed after an expression is evaluated in a Maple session is routed through callbacks. This output includes the following:
results from evaluated commands that are terminated with a semicolon
output from the print command, the printf command, and other display-related commands
userinfo and warning messages
diagnostic output from the debugger, high settings from printlevel, and trace output
error messages if no error callback is defined
resource status messages if no status callback is defined
displayed help pages
The text callback is not the only way to control output in your OpenMaple application. For more control, individual results can be converted to strings, which can be displayed in text boxes or directed in any way you want. When using this method of controlling output, the text callback can be defined as a procedure that does not perform any operations (that is, does not direct the output anywhere). Note that the Java example below uses the predefined EngineCallBacksDefault class, which configures a method to print output using the System.out method. In general, if the text callback is left undefined by setting it to 0 or null, the output is directed to the console.
Initializing the Maple Engine
You can initialize the Maple engine by calling the StartMaple function in most versions of the API, or by creating an Engine class in the Java version of the API. In all cases, the initialization process loads the maplec.dll file and sets up the initial state so that the OpenMaple interface can evaluate commands. Despite the name StartMaple, this is only an initialization step; no separate Maple process is started.
The initialization process follows standard Maple start-up steps, including reading and running initialization files, setting library paths, and setting default security options. The startup state can be controlled by using the first parameter passed to the StartMaple function. This parameter is an array of strings that specify options accepted by the command-line interface. For more information about these options, refer to the maple help page.
Calling API Commands to Compute with Maple
When the OpenMaple interface is initialized, a kernel vector handle (or engine class), can be used to access all of the other methods in the API. The most common method lets you parse and evaluate arbitrary Maple commands. If a command is terminated with a semicolon, the display output is directed to your defined callbacks. This command also returns the result as a Maple data structure. The return value can be passed to other API commands for further analysis.
The OpenMaple interface manages Maple internal data structures and performs garbage collection. The data structures that are returned by an API function are automatically protected from garbage collection. The Maple command unprotect:-gc must be called to clean the memory reserved for these tasks. The OpenMaple Java interface is the only exception to this rule. Because the OpenMaple Java interface implements Maple structures as native objects, it manages object references by using a weak hash map, and therefore Maple data does not need to be unprotected.
Maple data structures are all declared as a single black box ALGEB, IntPtr, or similar type. Methods for inspecting and manipulating these data structures are provided. The API methods should be used, rather than dereferencing them directly in the data.
C/C++ Example
#include <stdio.h> #include <stdlib.h> #include "maplec.h" /* callback used for directing result output */ static void M_DECL textCallBack( void *data, int tag, const char *output ) { printf("%s\n",output); } int main( int argc, char *argv[] ) { char err[2048]; /* command input and error string buffers */ MKernelVector kv; /* Maple kernel handle */ MCallBackVectorDesc cb = { textCallBack, 0, /* errorCallBack not used */ 0, /* statusCallBack not used */ 0, /* readLineCallBack not used */ 0, /* redirectCallBack not used */ 0, /* streamCallBack not used */ 0, /* queryInterrupt not used */ 0 /* callBackCallBack not used */ }; ALGEB r; /* Maple data-structures */ /* initialize Maple */ if( (kv=StartMaple(argc,argv,&cb,NULL,NULL,err)) == NULL ) { printf("Fatal error, %s\n",err); return( 1 ); } r = EvalMapleStatement(kv,"int(x,x);"); StopMaple(kv); return( 0 ); }
Additional examples are available in the samples/OpenMaple directory of your Maple installation.
The method used to build this program depends on which compiler you are using. The following command is specific to the GCC compiler on a 64-bit version of Linux; it is useful as a reference for other platforms.
gcc -I $MAPLE/extern/include test.c -L $MAPLE/bin.X86_64_LINUX -lmaplec -lmaple -lhf -lprocessor64
In this example, $MAPLE is your Maple installation directory. Note that the C header files can be found in the $MAPLE/extern/include directory and the library files can be found in the $MAPLE/bin.$SYS directory. In this case,$SYS is X86_64_LINUX; check the library path you need to specify by running the kernelopts(bindir) command in Maple. The remaining -l options specify which libraries need to be linked. In Windows, you only need to link to the maplec.lib library. Other platforms may require several libraries to be linked, including libmaplec.so, libmaple.so, and libhf.so. If you do not specify a library as required, the compiler returns a message indicating that undefined references to functions exist, or a dependent library cannot be found.
When this example is built, a file called test.exe is created. Note: The file might be called a.out or another name, depending on your compiler. Before this executable file can be run, you must specify the path of the Maple dynamic libraries. For more information, see Runtime Environment Prerequisites.
After setting up your environment, run the binary file as you would run any other executable file. For example, create a shortcut icon and double-click it, or enter the file name at a command prompt.
test.exe
The following output is displayed.
1/2*x^2
C# Example
using System; using System.Text; using System.Runtime.InteropServices; class MainApp { // When evaluating an expression, Maple sends all of the displayed // output through this function. public static void cbText( IntPtr data, int tag, String output ) { Console.WriteLine(output); } public static void Main(string[] args) { MapleEngine.MapleCallbacks cb; byte[] err = new byte[2048]; IntPtr kv; // pass -A2 which sets kernelopts(assertlevel=2) just to show // how in this example. The corresponding argc parameter // (the first argument to StartMaple) should then be 2 // argv[0] should always be filled in with a value. String[] argv = new String[2]; argv[0] = "maple"; argv[1] = "-A2"; // assign callbacks cb.textCallBack = cbText; cb.errorCallBack = null; cb.statusCallBack = null; cb.readlineCallBack = null; cb.redirectCallBack = null; cb.streamCallBack = null; cb.queryInterrupt = null; cb.callbackCallBack = null; try { kv = MapleEngine.StartMaple(2,argv,ref cb,IntPtr.Zero,IntPtr.Zero,err); } catch(DllNotFoundException e) { Console.WriteLine(e.ToString()); return; } catch(EntryPointNotFoundException e) { Console.WriteLine(e.ToString()); return; } // make sure we have a good kernel vector handle back if( kv.ToInt64() == 0 ) { // If Maple does not start properly, the "err" parameter will be filled // in with the reason why (usually a license error). // Note that since we passed in a byte[] array, we need to remove // the characters past \0 during conversion to string Console.WriteLine("Fatal Error, could not start Maple: " + System.Text.Encoding.ASCII.GetString(err,0,Array.IndexOf(err,(byte)0)) ); return; } MapleEngine.EvalMapleStatement(kv,"int(x,x);"); MapleEngine.StopMaple(kv); } }
To build this example, you can open a Microsoft .NET Framework SDK Command Prompt. Browse to the directory that contains the test.cs file and enter the following command.
csc test.cs $MAPLE\\extern\\include\\maple.cs
$MAPLE is the directory in which Maple is installed. The maple.cs file contains the MapleEngine class definition. and defines an interface to the maplec.dll file.
When this example is built, a file called test.exe is created. This file can usually be run without additional environment settings. For more information, see Runtime Environment Prerequisites.
Run the binary file as you would run any other executable file. For example, create a shortcut icon and double-click it, or enter the file name at a command prompt.
Java Example
import com.maplesoft.openmaple.*; import com.maplesoft.externalcall.MapleException; class test { public static void main( String args[] ) { String a[]; Engine t; int i; a = new String[1]; a[0] = "java"; try { t = new Engine( a, new EngineCallBacksDefault(), null, null ); t.evaluate( "int( x,x );" ); } catch ( MapleException e ) { System.out.println( "An exception occurred" ); return; } System.out.println( "Done" ); } }
This example and others are available in the samples/OpenMaple/Java/simple subdirectory of your Maple installation.
To build this program, enter the following at a command prompt, where $JDKBINDIR is the directory in which your Java development tools are installed, and $MAPLE is the directory in which Maple is installed.
$JDKBINDIR/javac -classpath "$MAPLE/java/externalcall.jar;$MAPLE/java/jopenmaple.jar" test.java
Note: The same command can be used to build the example in Macintosh; however, use a colon (:) to separate the directories in the classpath instead of a semicolon.
When this example is built, a test.class file is created in the current directory. Before this file can be run, the path of the Java OpenMaple native library must be specified for your Java Virtual Machine. For more information, see Runtime Environment Prerequisites. In Windows, Java OpenMaple applications also require the %PATH% environment variable to be set.
You can use the Java Virtual Machine to run the generated class file by entering the following command. Note that the third entry in the classpath is a period (.) indicating the current directory.
$JDKBINDIR/java -classpath "$MAPLE/java/externalcall.jar;$MAPLE/java/jopenmaple.jar;." test
1/2*x^2 Done
Visual Basic 6 Example
Public kv As Long Public cb As MapleCallBack Public Sub TextCallBack(data As Long, ByVal tag As Integer, ByVal output As Long) Dim OutputString As String OutputString = MaplePointerToString(output) MainForm.OutputText.Text = MainForm.OutputText.Text + vbCrLf + OutputString End Sub Private Sub Form_Load() Dim error As String Dim args(0 To 1) As String 'init callbacks cb.lpTextCallBack = GetProc(AddressOf TextCallBack) cb.lpErrorCallBack = 0 cb.lpStatusCallBack = 0 cb.lpReadLineCallBack = 0 cb.lpRedirectCallBack = 0 cb.lpQueryInterrupt = 0 cb.lpCallBackCallBack = 0 ' start Maple kv = StartMaple(0, args, cb, 0, error) If kv = 0 Then MsgBox "Error starting Maple: " + StrConv(error, vbUnicode), vbCritical, "" Unload Me End End If dim result as Long = EvalMapleStatement(kv, "int(x,x);" ) End Sub
Other examples are available in the samples/OpenMaple/msvb directory of your Maple installation.
To build this example, create a new project, and add both the test.bas and $MAPLE/extern/include/maple.bas files to your project. $MAPLE is the directory in which Maple is installed. Create a form called "MainForm" and add a text box named "OutputText" to the form.
Build and run this example by pressing F5. When this example is built, a form that shows a text box filled with the value 1/2*x^2 is displayed.
Visual Basic .NET Example
Friend Class MainForm Inherits System.Windows.Forms.Form Public kv As IntPtr Public cb As MapleCallBack Public Sub MyTextCallBack(ByRef data As Integer, ByVal tag As Short, ByVal output As String) tbOutput.Text = tbOutput.Text & vbCrLf & " (" & tag & ") " & output End Sub Public Sub MyErrorCallBack(ByRef data As Integer, ByVal Offset As Short, ByVal output As String) MsgBox(" at offset " & Str(Offset) & " " & output, MsgBoxStyle.Information, "") End Sub Private Sub MainForm_Load(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles MyBase.Load Dim args(1) As String 'init callbacks cb.lpTextCallBack = AddressOf MyTextCallBack cb.lpErrorCallBack = AddressOf MyErrorCallBack cb.lpStatusCallBack = 0 cb.lpReadLineCallBack = 0 cb.lpRedirectCallBack = 0 cb.lpQueryInterrupt = 0 cb.lpCallBackCallBack = 0 ' start Maple Try args(1) = "-A2" kv = StartMaple(1, args, cb, 0) Catch e As StartMapleException MsgBox("Error starting Maple: " & e.Message, "") Me.Close() End Try Dim result As IntPtr result = EvalMapleStatement(kv, "int( x,x );" ) If result = 0 Then tbOutput.Text = "invalid expression" Else tbOutput.Text = MapleToString(kv, MapleALGEB_SPrintf1(kv, "%a", result)) End If End Sub End Class
To build this example, create a new project, and add both the test.bas and $MAPLE/extern/include/maple.vb files to your project. $MAPLE is the directory in which Maple is installed. Create a form called "MainForm" and add a text box named "tbOutput" to the form.
Build and run the example by pressing F5. When this example is built, a form that shows a text box filled with the value 1/2*x^2 is displayed.
Memory Usage
Maple allocates memory from the operating system in large portions. On most platforms, this process is performed by the C malloc function; 64KB of memory is allocated during a Maple session. This memory is not returned to the operating system (that is, by free) until the Maple session ends.
When Maple no longer requires a block of memory, that memory block is added to an internal list of free storage. Maple has several storage lists for different sizes of memory blocks so that it can quickly find blocks of the required size. For example, Maple uses three-word blocks, so it maintains a list of blocks of that size that are free. Maple allocates additional memory from the operating system only when it cannot respond to a request using its storage lists.
The more memory Maple is allocated by the operating system, the less it is allocated in the future because it reuses memory. For most applications, 4MB of memory must be available for allocation.
14.4 The Maple Command-line Interface
When considering how to use the Maple engine as part of another application, interface, or automatic process, several options are available. One of the simplest options is to use the Maple command-line interface.
The command-line version of Maple is a simple interface that, when used interactively, displays an input prompt (>), runs commands, and displays the output as text-based results. You can use this interface in batch mode to direct input to an application, specify a text file to run, or evaluate a command using the -c option.
In Windows, the command-line interface is called cmaple.exe. You can run this file from either the bin.win or bin.X86_64_WINDOWS directory of your Maple installation, depending on your platform. On other platforms, you can start the command-line interface by running the maple script located in the bin directory of your Maple installation.
Starting the Maple command-line interface, automatically executing a command file, and stopping the Maple session can take about one tenth of a second, depending on which commands are run and the speed of your system. The quick start-up time and the minimal amount of processing required make the Maple command-line interface suitable to be called from other applications, even for quick calculations.
Batch Files
A batch file is a Maple script that can run in the Maple command-line interface, run statements, and exit. The results can be displayed or directed to a file.
One method of using the command-line interface to solve a problem is to create an .mpl script, which includes the input data. If this script is called solve.mpl, you could run the script as follows.
cmaple solve.mpl > solve.output
In this example, the output is redirected to a file named solve.output. You can configure an application to read this output file to capture the result.
Note: .mpl is the standard file extension for a Maple language file, which is a text file that can contain Maple statements. For more information, refer to the file help page.
You can use the -q option to hide extra output that interferes with parsing results automatically. For more options on the Maple command-line interface, refer to the maple help page.
Directing Input to a Pipeline
To avoid using the file system, input to the command-line interface can be directed to a pipeline. The following example shows how to perform this task at a command prompt.
echo "int(x,x);" | cmaple
Specifying Start-up Commands
You can use the -c option to specify start-up commands to be run by the Maple command-line interface. Although the -c option can be followed by any valid Maple statement, the syntax must be carefully quoted to avoid being interpreted by the shell of the calling system. For example, the following syntax can be entered at a Windows command prompt.
cmaple -c "datafile := `c:/temp/12345.data`" -c N:=5;
The equivalent command in a UNIX shell requires different quoting.
/usr/local/maple/bin/maple -c 'datafile:="/tmp/12345.data";' -c N:=1;
Statements that do not use characters that are special to the system interpreter can be left unquoted, as with the case of -c N:=1; above. This statement does not use spaces, pipe characters, redirect symbols, quotes, or other characters that have meaning to the interpreter.
14.5 External Calling: Using Compiled Code in Maple
In Maple, you can load a dynamic-link library (.dll) file that contains functions written in a programming language other than Maple, and then use those functions in Maple as you would use any other commands.
External functions that accept and return basic types (for example, integers and floats) can be called directly in Maple after defining the calling sequence of the external function. Alternatively, if you want to call functions that use complicated types, or if you require more control over the conversion of data structures you want to access, you can use the Maple external function interface to create and compile a wrapper file.
Calling a Function in a Dynamic-link Library
Most external functions that are compiled in a .dll file use standard hardware types such as integers, floating-point numbers, strings, pointers (to strings, integers, and floating-point numbers), matrices, and vectors. Maple can translate the hardware representation of these external functions so that the external functions are recognized in Maple. This method is efficient and easy to use because it does not require the use of a compiler. This method of directly calling the external code allows you to use an external library without modifying the Maple library.
To understand the Maple external calling facility, consider the following C code, which adds two numbers and returns the result.
int add( int num1, int num2 ) { return num1+num2; }
Three basic steps are required to call a function in a .dll library.
Create or obtain a .dll file
Create a function specification in Maple
Call the external function from within Maple
Create or Obtain a .dll file
The external functions that you want to call from Maple must be compiled in a .dll file. You can either create the code and compile the .dll file yourself or obtain an existing .dll file that contains the functions you want to use in Maple.
The external library functions in a .dll file must have been compiled using the _stdcall calling convention, which is the default convention used by most compilers in Macintosh and 64-bit Windows, but must be specified when using most 32-bit Windows compilers. Options are also available for calling .dll files created by Fortran compilers and classes created in Java.
Create a Function Specification
Before using an external function in Maple, you must provide a description (or function specification), which includes the following information.
Name of the function in the .dll file. In the example above, the name is add.
Type of parameters the function passes and returns. In the example above, all of the parameters are of type int.
Name of the .dll file that contains the function. In the example above, assume that the C code has been compiled into a .dll file called mylib.dll.
A function specification translates the external function into a form that can be recognized and interpreted by Maple.
At a Maple prompt, you can define the function specification by calling define_external as follows.
myAdd := define_external( 'add', 'num1'::integer[4], 'num2'::integer[4], 'RETURN'::integer[4], 'LIB'="mylib.dll" );
Examine this function and note the following characteristics.
The first argument of the define_external function (in this example, add) is the name of the external function as exported by the .dll file. In the C code, the function is called add. However, because the define_external function is assigned to the name myAdd above, the Maple procedure that will be generated after you define the function specification will be called myAdd. This is the command that you will use to call the external function within Maple.
If Java or Fortran was used to create the external function, you must specify JAVA or FORTRAN as the second argument to indicate the programming language. The default language is C, so this parameter does not need to be specified in the example above since the add function was written in C.
The parameters num1 and num2, which are both of type int, describe the arguments of the function to be called. These values should be specified in the order in which they appear in your documentation or source code for the external function, regardless of issues such as the passing order (left to right versus right to left). By doing so, the Maple procedure returned by the define_external function has the same calling sequence as the external function when it is used in the language for which it was written. The only exception is that one argument can be assigned the name RETURN. This argument specifies the type returned by the function rather than a parameter passed to the function. In the example above, the return type does not have a name, so the keyword RETURN is used.
For information on specifying parameter types, see Specifying Parameter Types for Function Specifications.
Specifying the parameter types is independent of the compiler. The specification is always defined in the same way, regardless of the method used to compile the .dll file. The example above uses the C type int, which is specified as integer[4] in Maple. The 4 in the square brackets indicates the number of bytes used to represent the integer. Some C compilers use 4-byte (32-bit) long data types, but other compilers use 8-bytes (64-bit) for the same data structure. If you are using the long data type, the specification in Maple will need to be either integer[4] or integer[8], depending on the way your .dll file was built. For more information about common type relations, see Table 14.2.
The name of the .dll file containing the external function is specified by defining the LIB parameter. In the example above, mylib.dll specifies the file name of the library in which the function is located. The format of this name is system-dependent and certain systems require a full path to the file. In general, the name should be in the same format as you would specify for a compiler on the same system. If you are calling a Java method, dllName is the name of the class containing the method.
Important: Specify the function exactly and make sure that the arguments are in the correct order. Failure to do this will not cause any problems when you are defining the specification; however, unexpected results may be produced or your program may stop responding when the external function is called within Maple.
Calling the External Function
Calling the define_external function for myAdd returns a Maple procedure that translates the Maple types to hardware types that can work with an external function. This procedure can be used in the same way as other Maple commands.
myAdd(1,2);
3
a := 33:
b := 22:
myAdd(a,b);
55
r:= myAdd(a,11);
r ≔ 44
Specifying Parameter Types for Function Specifications
Maple uses its own notation to provide a generic well-defined interface for calling compiled code in any language. The format of each type descriptor parameter is as follows.
argumentIdentifier :: dataDescriptor
The return value description is also defined by using a data descriptor, with the name RETURN as the argumentIdentifier. If the function returns no value, no RETURN parameter is specified. Also, if no parameters are passed, no argument identifiers are required.
Scalar Data Formats
External libraries generally handle scalar data formats that are supported by your platform. All array, string, and structured formats are created from these. The data descriptors used to represent scalar formats usually contain a type name and size. The size represents the number of bytes needed to represent the given hardware type. Table 14.1 lists the basic type translations for standard C, Fortran, and Java compilers.
Maple Data Descriptor
C Type
Fortran Type
Java Type
integer[1]
char
BYTE
byte
integer[2]
short
INTEGER2
integer[4]
int or long^1
INTEGER or INTEGER4
int
integer[8]
long^1 or long long
INTEGER8
long
float[4]
float
REAL or REAL4
float[8]
double
DOUBLE PRECISION or REAL8
char[1]
CHARACTER
boolean[1]
LOGICAL1
boolean
boolean[2]
LOGICAL2
boolean[4]
LOGICAL or LOGICAL4
boolean[8]
LOGICAL8
Note: The C type long is typically 4 bytes on 32-bit systems and 4 or 8 bytes on 64-bit systems. Use the sizeof operator or consult your compiler documentation to verify sizeof(long).
Structured Data Formats
In addition to the basic types listed in Table 14.1, Maple also recognizes certain compound types that can be derived from the basic types, such as arrays and pointers. These compound types are listed in Table 14.2. For a complete list and a detailed specification, refer to the define_external,types help page.
ARRAY( datatype = float[8], ... )
type A
type[] A
string[n]
char x[n]
CHARACTER2
string
complex[4]
struct{ float re, im; }
COMPLEX or COMPLEX8
NA
complex[8]
struct{ double re, im; }
DOUBLE COMPLEX or COMPLEX16
REF(typename)
TYPENAME
External Function Interface
Alternatively, you may want to call a .dll file that directly manipulates Maple data structures, rather than converting them automatically to standard data types. By doing so, you can either write custom applications that are integrated with Maple or provide custom conversions for data passed to prebuilt .dll files. Maple provides an API for directly managing Maple data structures and operations performed on them.
This API, or external function interface, is a subset of the API provided by the OpenMaple interface. Unlike the OpenMaple interface, you do not need to define stream callbacks because Maple is the primary interface. Also, the kernel-vector handle returned from a call to the StartMaple function in the OpenMaple API is, instead, passed as an argument to the external function defined in your .dll file.
Currently, the API is defined for C/C++ and Fortran, and certain portions of the API can be used for external functions written in Java. Other languages such as Visual C# and Visual Basic can interface through a small C++ layer.
The API function prototypes for manipulating Maple data structures are located in the $MAPLE/extern/include directory where $MAPLE is the directory in which Maple is installed. The header file maplec.h must be included when writing custom C wrappers. One of the header files, maplefortran.hf or maplefortran64bit.hf, must be included when writing custom Fortran wrappers. Other header files, mplshlib.h, and mpltable.h contain macros, types, and data structures that are needed for direct manipulation of Maple data structures.
In your C code, Maple uses the following lines as an entry point to call the external function directly with no argument conversion.
ALGEB myExternalFunction( MKernelVector kv, ALGEB args );
Two parameters are in the external function declaration. The first is a handle that will be required to call any Maple API function. The second is a Maple expression sequence of all the arguments passed when the external function is called. The API function MapleNumArgs can be used to determine the number of elements in the expression sequence. This variable can be treated as an array of DAGs starting at index 1 (not 0). Therefore, args[1] is the first parameter passed to the external function.
myFunc := define_external('myExternalFunction', 'MAPLE', 'LIB'= "myStuff.dll"):
When using the define_external function to declare an interface to an external function that directly manipulates Maple structures, you do not need to provide a description of the arguments and their types. Instead, add the keyword option, MAPLE.
Again, consider the simple example that adds two numbers passed by Maple. This time, with explicit data type conversions using the API, and defining the external function prototype, as described above, the C function appears as follows.
/* Program to add two numbers from Maple */ #include <stdio.h> #include <stdlib.h> #include <maplec.h> ALGEB myAdd( MKernelVector kv, ALGEB args ) { int a1, a2, r; if( MapleNumArgs(kv,args) != 2 ) MapleRaiseError(kv,"Incorrect number of arguments"); a1 = MapleToInteger32(kv,((ALGEB*)args)[1]); a2 = MapleToInteger32(kv,((ALGEB*)args)[2]); r = a1 + a2; return( ToMapleInteger(kv,(M_INT) r) ); }
This program first checks if the Maple function call passes exactly two arguments. It then converts the two arguments to hardware integers and adds them. The result is converted to a Maple integer and returned.
This program can be compiled into a .dll file using a C compiler of your choice. Ensure that you link with the Maple API .dll file. The .dll file can be placed in the Maple binary directory, as given by kernelopts(bindir), or a subdirectory within the directory specified by the PATH environment variable. If you are using .dll files outside of the Maple binary directory, you may need to specify the full path to the .dll file in the LIB argument to the define_external function.
To complete the example, the myAdd function can be linked in Maple and used as any other Maple procedure.
myAdd := define_external('myAdd', 'MAPLE', 'LIB'= "myAdd.dll"):
myAdd(2,3);
5
myAdd(2.2,1);
Error, (in myAdd) integer expected for integer[4] parameter
myAdd(2^80,2^70);
Error, (in myAdd) integer too large in context
The equivalent Fortran wrapper is as follows.
Program to add two numbers from Maple INTEGER FUNCTION myAdd(kv, args) INCLUDE "maplefortran.hf" INTEGER kv INTEGER args INTEGER arg INTEGER a1, a2, r CHARACTER ERRMSG*20 INTEGER ERRMSGLEN ERRMSGLEN = 20 IF ( maple_num_args(kv, args) .NE. 2 ) THEN ERRMSG = 'Incorrect number of arguments' CALL maple_raise_error( kv, ERRMSG, ERRMSGLEN ) myAdd = to_maple_null( kv ) RETURN ENDIF arg = maple_extract_arg( kv, args, 1 ) a1 = maple_to_integer32(kv, arg) arg = maple_extract_arg( kv, args, 2 ) a2 = maple_to_integer32(kv, arg) r = a1 + a2 myAdd = to_maple_integer( kv, r ) END
Once compiled into a .dll file, the same syntax can be used in Maple to access the function. The only difference is the additional keyword 'FORTRAN' in the define_external call.
myAdd := define_external('myAdd','MAPLE','FORTRAN','LIB'= "myAdd.dll"):
For more examples, refer to the define_external,CustomWrapper help page.
Specifying Parameter Passing Conventions
Each programming language uses a specific convention for parameter passing. For example, C uses the pass-by-value convention; passing parameters by reference must be performed explicitly by passing an address. Fortran uses the pass-by-reference convention. Pascal uses either, depending on how the parameter was declared.
The Maple external calling mechanism supports C, Fortran, and Java calling conventions. There is an external API for writing custom wrappers for C and Fortran, but not for Java. The default convention used is C. To use Fortran calling conventions, specify the name FORTRAN as a parameter to the define_external function.
f := define_external(`my_func`,`FORTRAN`, ...);
To use Java calling conventions, specify the name JAVA as a parameter to the define_external command. Also, specify the CLASSPATH= option to point to the classes used.
f := define_external(`my_func`,`JAVA`, CLASSPATH="...", ...);
Some other compiler implementations, such as Pascal and C++, can work with C external calling by using the correct definitions and order of passed parameters.
Generating Wrappers Automatically
When you specify the keyword WRAPPER in the call to the define_external function, Maple generates code for data translations. Maple compiles this code into a .dll file and dynamically links to the new library. Subsequently invoking the procedure that is returned by the define_external function calls the newly generated conversion command before calling the external function in the library you provided.
The C code generated by Maple wraps the Maple data structures by translating them to hardware equivalent types. Therefore, the code file is called the wrapper, and the library generated by this code is called the wrapper library.
Generating wrappers can provide an easy way to start writing custom code that references the Maple external function interface. The term wrapper also refers to the code you write to communicate with existing .dll files, as it does for the code Maple generates for the same reason. Your code is sometimes called a custom wrapper.
Consider the original add function that was introduced at the beginning of this chapter. In the following example, the WRAPPER option is used to generate a wrapper using the define_external function. As shown, you can also use the NO_COMPILE option to prevent the generated wrapper from compiling. The name of the generated file is returned instead.
myAdd := define_external( 'add', 'WRAPPER', 'NO_COMPILE', 'num1'::integer[4], 'num2'::integer[4], 'RETURN'::integer[4] );
myAdd := "mwrap_add.c"
The file mwrap_add.c resembles the following.
/* MWRAP_add Wrapper Generated automatically by Maple Do not edit this file. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <mplshlib.h> #include <maplec.h> MKernelVector mapleKernelVec; typedef void *MaplePointer; ALGEB *args; /* main - MWRAP_add */ ALGEB MWRAP_add( MKernelVector kv, INTEGER32 (*fn) ( INTEGER32 a1, INTEGER32 a2 ), ALGEB fn_args ) { INTEGER32 a1; INTEGER32 a2; INTEGER32 r; ALGEB mr; int i; mapleKernelVec = kv; args = (ALGEB*) fn_args; if( MapleNumArgs(mapleKernelVec,(ALGEB)args) != 2 ) MapleRaiseError(mapleKernelVec,"Incorrect number of arguments"); /* integer[4] */ a1 = MapleToInteger32(mapleKernelVec,args[1]); /* integer[4] */ a2 = MapleToInteger32(mapleKernelVec,args[2]); r = (*fn)(a1, a2); mr = ToMapleInteger(mapleKernelVec,(long) r); return( mr ); }
The generated wrapper is a good starting point for writing your own code. Some extra variables and declarations may be used because the wrapper generation is generic. For example, the use of args rather than fn_args avoids the need for a cast with args[1], but it is also a static global variable which is useful when working with callbacks that need access to the argument sequence outside the main entry point.
Passing Arguments by Reference
External functions follow normal Maple evaluation rules in that the arguments are evaluated during a function call. It therefore may be necessary to enclose assigned names in right single quotes (unevaluation quotes) when passing arguments by reference. For example, consider the following function that multiplies a number by two in-place.
void double\_it( int *i ) { if( i == NULL ) return; *i *= 2; }
In Maple, the definition of this function could appear as follows.
double_it := define_external('double_it', i::REF(integer[4]), LIB="libtest.dll");
When running this function, the argument 'i' is converted from the Maple internal representation of an integer to a 4-byte hardware integer. A pointer to the hardware integer is then passed to the external function, 'double_it'. Although 'i' is declared as a pointer to an integer, you can call 'double_it' with non-pointer input.
double_it(3);
In this case, a pointer to the hardware integer 3 is sent to 'double_it'. The modified value is not accessible from Maple. To access the modified value, the parameter must be assigned to a name. The name must be enclosed in unevaluation quotes to prevent evaluation.
n:=3;
double_it(n); # n is evaluated first, so 3 is passed
n;
double_it('n'); # use unevaluation quotes to pass 'n'
6
For numeric data, the string "NULL" can be passed as a parameter to represent the address 0 (the C NULL). For strings, because "NULL" is a valid string, the integer 0 represents the address 0.
double_it("NULL"); concat := define_external('concat', RETURN::string, a::string, b::string, LIB="libtest.dll"): concat("NULL","x");
NULLx
concat(0,0);
0
In the concat example, the C code might look like the following. Note that this function does not clean the memory as it should.
char * concat( char* a, char *b ) { char *r; if( !a \\ !b ) return( NULL ); r = (char*)malloc((strlen(a)+strlen(b)+1)*sizeof(char)); strcpy(r,a); strcat(r,b); return( r ); }
External API
An external API is provided if you want to expand existing wrappers or write custom wrappers. Because this API is the same as that of OpenMaple, most of the internal documentation is referenced in the OpenMaple help pages. In particular, refer to the OpenMaple,C,API and OpenMaple,VB,API help pages and related pages.
Additional examples can be found in the examples,ExternalCalling page. Sample code is provided in the samples/ExternalCall directory of your Maple installation. In particular, all of the external calling sample code provided in the individual help pages in OpenMaple,C,API can be found in the samples/ExternalCall/HelpExamples directory. This code is precompiled in the HelpExamples.dll file provided with Maple so that you can run the examples in your Maple session.
System Integrity
The Maple kernel cannot control the quality or reliability of external functions. If an external function performs an illegal operation, such as accessing memory outside of its address space, that operation may result in a segmentation fault or system error. The external function may stop responding and cause Maple to stop responding as well.
If an external function accesses memory outside of its address space but within the Maple address space, the external function will likely not stop responding, but certain parts of Maple may not function correctly, resulting in unexpected behavior or a crash later in the Maple session. Similarly, an external function that directly manipulates Maple data structures can produce unexpected results by misusing the data structure manipulation facilities.
Therefore, use external calling at your own risk. Whether an external function is one that you have written, or supplied by a third party to which you have declared an interface (that is, by using the define_external function), Maple must rely on the integrity of the external function when it is called.
14.6 Accessing Data over a Network with TCP/IP Sockets
The Sockets package allows Maple to communicate with data sources over the Internet, such as web sites and remote Maple sessions running on a network.
You can create a Maple server in a Maple session and configure the server to send a message to that session.
Socket Server
A socket server can be a public web service such as a stock quote service. The following example shows how to create a Maple procedure that acts as a service that listens for a socket connection and sends a message when a connection is found.
The server action is defined in the following procedure.
myServer := proc( sid ) uses Sockets; Write( sid, sprintf( "Hello %s on port %d, from %s\r\n", GetPeerHost( sid ), GetPeerPort( sid ), GetHostName() ) ) end proc:
The following commands cause the Maple session in which they are called to start the servicing requests. This call is not returned. To run the code, enter the procedure definition above and the Serve command below in a Maple worksheet. (Remove the comment character (#) before running the code.)
#Sockets:-Serve( 2525, myServer );
Socket Client
To write a simple client, the socket must first be opened. To do so, specify the name of the host and the port number of the host to which you want to connect.
sid := Sockets:- Open ( "localhost", 2525 );
To get information from the server, the socket must be read.
Sockets:-Read(sid);
When you are finished, close the socket.
Sockets:-Close(sid);
14.7 Code Generation
In Maple, code generation is one of several powerful tools for deploying results to other systems. Maple can translate formulas, numerical procedures, data sets, and matrices to compiled languages. Maple supports translation to C, C#, MATLAB®, Java, JavaScript, Perl, Python, R, Visual Basic, and Fortran.
with( CodeGeneration );
C,CSharp,Fortran,IntermediateCode,Java,JavaScript,Julia,LanguageDefinition,Matlab,Names,Perl,Python,R,Save,Swift,Translate,VisualBasic
Calling CodeGeneration Commands
You can call the CodeGeneration commands using the following syntax, where L is one of the supported languages, for example, C.
CodeGeneration[*L*]( *expression*, *options* )
The expression can take one of the following forms.
A single algebraic expression: Maple generates a statement in the target language assigning this expression to a variable.
A list of equations of the form *name*=*expression*: Maple interprets this list as a sequence of assignment statements and generates the equivalent sequence of assignment statements in the target language.
A list, array, or rtable: Maple generates a statement or sequence of statements assigning the elements to an array in the target language.
A Maple procedure or module: Maple generates an equivalent structure in the target language. For example, to translate a procedure to C, Maple generates a function along with any necessary directives for library inclusion. To translate a module to Java, Maple generates a Java class declaration with exports translated to public static methods and module locals translated to private static methods. For more information on translating code to a specific language, refer to the CodeGeneration help page and browse to the help page for the target programming language that you want to use.
You can use many options with the CodeGeneration commands. For more information, refer to the CodeGenerationOptions help page. Some of the commonly used options are listed below.
optimize=value: This option specifies whether the Maple code is optimized before it is translated. The default value is false. When this option is set to true, the codegen[optimize] function is used to optimize the Maple code before it is translated.
output=value: This option specifies the form of the output. By default, the formatted output is printed to the console. If a name (different from the name string) or a string is specified as the value, the result is appended to a file of that name. If the value is the name string, a string containing the result is returned. This string can then be assigned and manipulated.
declare=[declaration(s)]: This option specifies the types of variables. Each declaration has the form varname::vartype where varname is a variable name and vartype is one of the Maple type names recognized by the CodeGeneration package, as described in the TranslationDetails help page. Declarations specified using this option override any other type declarations in the input code.
Notes on Code Translation
Because the Maple programming language differs from the target languages supported by the CodeGeneration package, the generated output may not be completely equivalent to the input code. The CodeGeneration/Details help page provides more information on the translation process and hints on how to take full advantage of the facilities. In addition, some help pages contain notes that are relevant to specific languages. For more information, refer to the help pages for the corresponding target language, for example, CodeGeneration/General/CDetails.
Translation Process
The CodeGeneration commands recognize a subset of the Maple types, which are listed in the CodeGeneration/Details help page. The Maple types are translated to appropriate types in the target language. Compatibility of types is checked before operations are translated, and type coercions are performed if necessary. The CodeGeneration commands attempt to determine the type of any untyped variables. You can control type analysis and deduction by using the coercetypes, declare, deducetypes, and defaulttype options. For more information, refer to the CodeGenerationOptions help page.
The CodeGeneration commands can translate a subset of the Maple commands, which are listed on the CodeGeneration/Details help page. Some commands are translated only to certain target languages. For more information about a specific language, refer to its detailed help page, for example, CodeGeneration/General/CDetails.
The return type of a procedure is determined automatically if you do not declare it. If more than one return statement is specified, the types of all objects returned must be compatible in the target language. If a return statement contains a sequence of objects, the sequence is translated into an array. Implicit returns are recognized in some cases, but translations to explicit returns can be suppressed by using the deducereturn=false option. When necessary, an automatically generated return variable is used to store a return value.