C/C++ で記述された関数をMathematicaにリンクする方法を理解し始めていると思います。私が直面している問題は、C ラッパーから Mathematica にエラー メッセージを送信する方法がわからないことです。Google で検索した後、このMathLink Tutorialを見つけました。
セクション 1.7 で、エラー メッセージを送信する方法についての洞察が得られましたが、奇妙な結果が得られています。ここに私が取り組んでいるコードがあります。
//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;
}
};
#endif
ファイル cppFunctions.h は、クラスPoint
とLine
. Mathematicaで使用したい関数を除いて、このクラスを最小限に抑えました。ある線から別の線までの距離を見つけたい。この関数は Mathematica のlineInt
in wireframesの C バージョンです。この関数をMathematicaで使用するには、 Mathematicaから入力を取得し、出力をMathematicaに送り返すラッパー関数が必要です。
//mlwrapper.cpp
#include "mathlink.h"
#include <math.h>
#include "cppFunctions.h"
void ML_GetPoint(Point &P){
long n;
MLCheckFunction(stdlink, "List", &n);
MLGetReal64(stdlink, &P.x);
MLGetReal64(stdlink, &P.y);
}
void ML_GetLine(Line &L){
long n;
MLCheckFunction(stdlink, "List", &n);
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);
}
Mathematicaからの入力を取得するために、ML_GetPoint
との2 つのヘルパー関数を作成しました。2 つのリストを含むリストから行を取得します。各サブリストは、2 つの実数 (点) のリストです。Mathematica でこの関数を試すには、さらにファイルが必要です。ML_GetLine
//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`"
このファイルには、関数 LineDistance が引数を手動で取得し、実数を返すことが記載されています。最後の 2 行が重要です。1 つ目は、関数の をEvaluate
宣言しusage
ます。をMathematica?LineDistance
に入力すると、関数に関する簡単なメッセージが表示されます。もう1 つは、エラーが発生したときに使用したいものです (これについては後で詳しく説明します)。Evaluate
#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
最後のファイルは Makefile です。この時点で、Mathematica で関数をテストする準備が整いました。
Mac OS X を使用していることを前に述べておく必要がありましたが、これが Windows でどのように機能するかはわかりません。mlwrapper.cpp では、メイン関数にさらに多くのコードが必要です。これはMathematicaが提供する例の 1 つに見られます。
私が知っている端末でこれを行う:
make > makelog.txt
make clean
これにより、実行可能ファイルが作成されますmlwrapper
。これで 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"}
]
得られる出力は次のとおりです。
正しい引数を入力する限り、すべて正常に機能します。つまり、2 つのリストがあり、それぞれが 2 つの double の 2 つのリストのリストです。入力を取得する別の方法があるかもしれません。方法を知っている場合は、私に知らせてください。この方法に固執する場合、必要なのはMathematicaユーザにエラーがあるかどうかを知らせる方法だけです。非常に単純なものは、間違った入力を入力することです。これを入力するとしましょう:
LineDistance[{{0, 0}, {0}}, {{1, -1}, {1, 1}}]
出力は$Failed
です。以下はどうでしょう。
LineDistance[{{1, -1}, {1, 1}}]
出力はLineDistance[{{1, -1}, {1, 1}}]
です。Pattern
のセクションで、関数が 2 つのリストを受け入れることを説明したため、これが発生したと推測し.tm
ています。これは本当ですか?
いずれにせよ、私が見つけたチュートリアルに従って、ファイル mlwrapper.cpp を次のように変更します。
#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);
}
mlwrapper.tm ファイルの末尾に次を追加します。
: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`"
make を使って、Mathematica で間違いを犯してみましょう。
すべてを書くのではなく、どの出力のスクリーンショットを投稿しています。
呼び出しを繰り返すと、さまざまなエラーが発生することに注意してください。エラーが発生した次の行で機能が継続しているようです。関数のような他の ML 関数を使用せず、エラー タグを送信するためML_Attempt
だけに使用するMLEvaluate
場合、MathLink が壊れているため、リンクを再インストールする必要があります。CからMathematicaにエラー メッセージを送信する方法を知っている人はいますか?
アップデート:
与えられた回答と別の有用なドキュメント(第 8 章) に基づいて、なんとか機能させることができました。現時点ではコードはそれほどきれいではありませんが、これにより、次の質問をするようになりました。機能を早期に終了することはできますか? 通常の C プログラムでは、エラーが発生した場合、エラー メッセージを出力してexit
関数を使用します。似たようなことはできますか?この機能を使用するexit
と、リンクが壊れ、機能を再インストールする必要があります。たとえば、関数ML_GetPoint
を取り上げます。ML_GetLine
ここでエラーが発生した場合、main 関数で計算を実行しても意味がありませんLineDistance
。取得したエラーをすべてクリアし、エラーを指定して Mathematica にメッセージを送信し、いったん終了して次の呼び出しを待つ必要があります。