I think I'm starting to understand how to associate functions written in C / C ++ with Mathematica. The problem I am facing is that I do not know how to send error messages from my C shell to Mathematica. After searching google, I found this MathLink Tutorial .
Section 1.7 gave me an idea of ββhow to send error messages, but I get strange results. Here is the code I'm working with.
//File cppFunctions.h #ifndef CPPFUNCTIONS_H #define CPPFUNCTIONS_H class Point { public: double x, y; Point(){ x=y=0.0;} Point(double a, double b): x(a), y(b) {} }; class Line { public: Point p1, p2; Line(void) {} Line(const Point &P, const Point &Q): p1(P), p2(Q) {} double distanceTo(const Line &M, const double &EPS = 0.000001){ double x21 = p2.x - p1.x; double y21 = p2.y - p1.y; double x43 = M.p2.x - M.p1.x; double y43 = M.p2.y - M.p1.y; double x13 = p1.x - M.p1.x; double y13 = p1.y - M.p1.y; double den = y43*x21 - x43*y21; if (den*den < EPS) return -INFINITY; double numL = (x43*y13 - y43*x13)/den; double numM = (x21*y13 - y21*x13)/den; if (numM < 0 || numM > 1) return -INFINITY; return numL; } };
The cppFunctions.h file declares the Point and Line classes. I split this class into bare minium, with the exception of the function that I want to use in Mathematica. I want to find the distance from one line to another. This function is a version of C lineInt in lineInt in Mathematica . To use this function in Mathematica, we need a wrapper function that receives input from Mathematica and sends the result back to Mathematica.
//mlwrapper.cpp
I created two helper functions: ML_GetPoint and ML_GetLine to help me get data from Mathematica. The string is obtained from a list containing two lists. Each sublist is a list of 2 real numbers (dots). To try this feature in Mathematica, we need more files.
//mlwrapper.tm double LineDistance P((void)); :Begin: :Function: LineDistance :Pattern: LineDistance[L_List, M_List] :Arguments: {L, M} :ArgumentTypes: {Manual} :ReturnType: Real :End: :Evaluate: LineDistance::usage = "LineDistance[{{x1,y1}, {x2,y2}}, {{x3,y3}, {x4,y4}}] gives the distance between two lines." :Evaluate: LineDistance::mlink = "There has been a low-level MathLink error. The message is: `1`"
This file indicates that the LineDistance function will receive the arguments manually and return a real number. The last two lines are important. The first Evaluate declares usage functions. It gives a brief message about the function when ?LineDistance is introduced in Mathematica. Another Evaluate is the one I want to use when there is an error (more on this later).
#Makefile VERSION=8.0 MLINKDIR = . SYS = MacOSX-x86-64 CADDSDIR = /Applications/Mathematica.app/SystemFiles/Links/MathLink/DeveloperKit/CompilerAdditions INCDIR = ${CADDSDIR} LIBDIR = ${CADDSDIR} MPREP = ${CADDSDIR}/mprep RM = rm CXX = g++ BINARIES = mlwrapper all : $(BINARIES) mlwrapper : mlwrappertm.o mlwrapper.o ${CXX} -I${INCDIR} mlwrappertm.o mlwrapper.o -L${LIBDIR} -lMLi3 -lstdc++ -framework Foundation -o $@ .cpp.o : ${CXX} -c -I${INCDIR} $< mlwrappertm.cpp : mlwrapper.tm ${MPREP} $? -o $@ clean : @ ${RM} -rf *.o *tm.c mlwrappertm.cpp
The last file is the Makefile. At this point, we are all set to test functions in Mathematica.
I should have mentioned that I use Mac OS X, I'm not sure how this will work on Windows. In mlwrapper.cpp, the main function requires much more code, which you can find in one of the examples provided by Mathematica.
In the terminal, I know this:
make > makelog.txt make clean
This will make the mlwrapper . Now we can start using Mathematica:
SetDirectory[NotebookDirectory[]]; link = Install["mlwrapper"]; ?LineDistance Manipulate[ Grid[{{ Graphics[{ Line[{p1, p2}, VertexColors -> {Red, Red}], Line[{p3, p4}] }, PlotRange -> 3, Axes -> True], LineDistance[{p1, p2}, {p3, p4}] }}], {{p1, {-1, 1}}, Locator, Appearance -> "L1"}, {{p2, {2, 1}}, Locator, Appearance -> "L2"}, {{p3, {2, -2}}, Locator, Appearance -> "M1"}, {{p4, {2, 3}}, Locator, Appearance -> "M2"}
]
The result obtained:

Everything works fine as long as you enter the correct arguments. That is, 2 lists, each of which is a list of 2 lists of 2 two-local. Maybe there is another way to get input, if you know how to please let me know. If we stick with this method, all we need is a way to let the Mathematica user know if there are any errors. Very simple wrong input. Let's say I entered this:
LineDistance[{{0, 0}, {0}}, {{1, -1}, {1, 1}}]
The output is $Failed . How about the following:
LineDistance[{{1, -1}, {1, 1}}]
Output signal LineDistance[{{1, -1}, {1, 1}}] . I assume this happens because we described in the Pattern .tm section that the function accepts two lists, and since we gave only one, it does not match the pattern. It's true?
In any case, following the tutorial below, you can modify the mlwrapper.cpp file as follows:
#include "mathlink.h" #include <math.h> #include <string> #include "cppFunctions.h" bool ML_Attempt(int func, const char* err_tag){ if (!func) { char err_msg[100]; sprintf(err_msg, "Message[%s,\"%.76s\"]", err_tag, MLErrorMessage(stdlink)); MLClearError(stdlink); MLNewPacket(stdlink); MLEvaluate(stdlink, err_msg); MLNextPacket(stdlink); MLNewPacket(stdlink); MLPutSymbol(stdlink, "$Failed"); return false; } return true; } void ML_GetPoint(Point &P){ long n; if(!ML_Attempt(MLCheckFunction(stdlink, "List", &n), "LineDistance::mlink2"))return; if(!ML_Attempt(MLGetReal64(stdlink, &P.x), "LineDistance::mlink3")) return; if(!ML_Attempt(MLGetReal64(stdlink, &P.y), "LineDistance::mlink4")) return; } void ML_GetLine(Line &L){ long n; if(!ML_Attempt(MLCheckFunction(stdlink, "List", &n), "LineDistance::mlink1"))return; ML_GetPoint(L.p1); ML_GetPoint(L.p2); } double LineDistance(void) { Line L, M; ML_GetLine(L); ML_GetLine(M); return L.distanceTo(M); } int main(int argc, char* argv[]) { return MLMain(argc, argv); }
And add the following to the end of the mlwrapper.tm file
:Evaluate: LineDistance::mlink1 = "There has been a low-level MathLink error. The message is: `1`" :Evaluate: LineDistance::mlink2 = "There has been a low-level MathLink error. The message is: `1`" :Evaluate: LineDistance::mlink3 = "There has been a low-level MathLink error. The message is: `1`" :Evaluate: LineDistance::mlink4 = "There has been a low-level MathLink error. The message is: `1`"
Now let's use make and try to make some mistakes in Mathematica.
I post a screenshot of what displays instead of writing everything.

Notice how we get different errors after repeating the call. It seems that the function continues on the line after an error occurs. If I do not use any of the other ML functions, as in the ML_Attempt function, and I use only MLEvaluate to send the error tag, then MathLink is broken and I need to reset the link. Does anyone know how to send error messages to Mathematica from C?
UPDATE:
Based on the answers received and another useful document (chapter 8), I managed to get it to work. The code is not very good at the moment, but it made me ask the following question. Is it possible to terminate the function earlier? In a regular C program, if I encounter an error, I would print an error message and use the exit function. Can we do something like this? If we use the exit function, the connection will be broken, and we will have to reinstall this function. Take, for example, the ML_GetPoint and ML_GetLine . If an error occurs here, then it makes no sense to process the calculations in the main LineDistance function. We need to clear all the errors that we received, send a Mathematica message indicating the error, exit now and wait for the next call.