gpt4 book ai didi

c++ - 数学 : MathLink error messages

转载 作者:可可西里 更新时间:2023-11-01 18:37:17 28 4
gpt4 key购买 nike

我想我开始了解如何将用 C/C++ 编写的函数链接到 Mathematica。我面临的问题是我不知道如何从我的 C 包装器向 Mathematica 发送错误消息。在谷歌搜索后,我找到了这个 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 声明了类 PointLine .除了我想在 Mathematica 中使用的函数之外,我已经将这个类剥离到最低限度。我想找到从一条线到另一条线的距离。这个函数是 lineInt的C版本在 wireframes in Mathematica .要在 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);
}

我创建了两个辅助函数: ML_GetPointML_GetLine帮助我从 Mathematica 获取输入。从包含两个列表的列表中获取一行。每个子列表都是 2 个实数(一个点)的列表。要在 Mathematica 中尝试此功能,我们需要更多文件。
//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 将手动获取参数,并将返回一个实数。最后两行很重要。第一 Evaluate声明 usage的功能。当 ?LineDistance 时,它会给出有关该功能的简短消息。被输入到 Mathematica。其他 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 中,main 函数需要更多代码,您可以在 Mathematica 提供的示例之一中找到这些代码。

在终端我知道这样做:
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"}

]

我们得到的输出如下:

Output

只要您输入正确的参数,一切都可以正常工作。也就是说,2 个列表,每个列表都是 2 个 double 列表的列表。也许有另一种获取输入的方法,如果您知道如何请告诉我。如果我们坚持这种方法,我们所需要的只是让 Mathematica 用户知道是否有任何错误的方法。一个非常简单的就是输入错误的输入。假设我输入这个:
LineDistance[{{0, 0}, {0}}, {{1, -1}, {1, 1}}]

输出为 $Failed .以下情况如何:
LineDistance[{{1, -1}, {1, 1}}]

输出为 LineDistance[{{1, -1}, {1, 1}}] .我猜这是因为我们在 Pattern 中描述过 .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 中犯一些错误。

我发布了输出内容的屏幕截图,而不是写下所有内容。

Output

注意我们在重复调用后如何得到不同的错误。似乎该函数在遇到错误后继续在该行。如果我不使用函数 ML_Attempt 中的任何其他 ML 函数而我只使用 MLEvaluate发送错误标签,然后 MathLink 损坏,我必须重新安装链接。有谁知道如何从 C 向 Mathematica 发送错误消息?

更新:

基于已经给出的答案和另一个有用的 document (第 8 章)我设法让它发挥作用。代码目前不太漂亮,但这让我问了以下问题。是否可以提前终止函数?在常规 C 程序中,如果我遇到错误,我会打印一条错误消息并使用 exit功能。我们可以做类似的事情吗?如果我们使用 exit功能链接将被破坏,我们将不得不重新安装该功能。取函数 ML_GetPointML_GetLine例如。如果此处发生错误,则在主函数 LineDistance 中进行计算没有意义。 .我们需要清除我们获得的任何错误,向 Mathematica 发送一条消息,指明错误,现在退出并等待下一次调用。

最佳答案

作为@ragfield 解决方案的替代方案,您可以将函数声明为 void并手动返回结果。这是基于 addTwo 的示例标准示例。这是模板:

void addtwo P(( int, int));

:Begin:
:Function: addtwo
:Pattern: AddTwo[i_Integer, j_Integer]
:Arguments: { i, j }
:ArgumentTypes: { Integer, Integer }
:ReturnType: Manual
:End:

:Evaluate: AddTwo::usage = "AddTwo[x, y] gives the sum of two machine
integers x and y."
:Evaluate: AddTwo::badargs = "Arguments are expected to be positive numbers"

和功能:
void addtwo( int i, int j) {
if(i>0&&j>0){
MLPutInteger(stdlink,i+j);
}else{
MLPutFunction(stdlink,"CompoundExpression",2);
MLPutFunction(stdlink,"Message",1);
MLPutFunction(stdlink,"MessageName",2);
MLPutSymbol(stdlink,"AddTwo");
MLPutString(stdlink,"badargs");
MLPutSymbol(stdlink,"$Failed");
}
}

以下是使用示例:
In[16]:= AddTwo[1,2]
Out[16]= 3

In[17]:= AddTwo[-1,2]
During evaluation of In[17]:= AddTwo::badargs: Arguments are expected
to be positive numbers

Out[17]= $Failed

这是一种更“高级”的方式来做到这一点,这样您就不必明确处理数据包。

但是,我觉得通过适当的模式在 Mathematica 端更好地执行输入参数的完整错误检查,以及为您的 C 代码中检测到的一些内部错误保存的错误消息选项(我实际上更进一步并返回到 Mathematica只是作为整数或字符串的错误代码,并让更高级别的 mma 包装器处理它们并发出消息)。您可以将这些模式放在您的模板文件中,或者您可以将您的 MathLink Mathematica 函数包装到另一个将执行错误检查的函数中。不过,我对 Mathlink 的经验非常有限,所以我在这里的意见可能不重要。

编辑

评论中的每个请求(虽然我不确定我是否正确理解了请求):
extern void addeight( void );
extern void addall(void);

static void putErrorMessageAndReturnFailure(const char *fname, const char *msgtag);

void addeight(void)
{
int i,j,k,l,i1,j1,k1,l1;
MLGetInteger(stdlink,&i);
MLGetInteger(stdlink,&j);
MLGetInteger(stdlink,&k);
MLGetInteger(stdlink,&l);
MLGetInteger(stdlink,&i1);
MLGetInteger(stdlink,&j1);
MLGetInteger(stdlink,&k1);
MLGetInteger(stdlink,&l1);

if(i<0||j<0||k<0||l<0||i1<0||j1<0||k1<0||l1<0){
putErrorMessageAndReturnFailure("AddEight","badargs");
}else{
MLPutFunction(stdlink,"List",2);
MLPutFunction(stdlink,"List",2);
MLPutInteger(stdlink,i+i1);
MLPutInteger(stdlink,j+j1);
MLPutFunction(stdlink,"List",2);
MLPutInteger(stdlink,k+k1);
MLPutInteger(stdlink,l+l1);
}
}

void addall(){
int *data, len, i = 0,sum = 0;
if(!MLGetIntegerList(stdlink, &data, &len)){
putErrorMessageAndReturnFailure("AddAll","interr");
return;
}
for(i=0;i<len;i++){
if(data[i]<0){
putErrorMessageAndReturnFailure("AddAll","badargs");
return;
}else{
sum+=data[i];
}
}
MLPutInteger(stdlink,sum);
MLReleaseInteger32List(stdlink, data, len);
}


static void putErrorMessageAndReturnFailure(const char *fname, const char *msgtag){
MLPutFunction(stdlink,"CompoundExpression",2);
MLPutFunction(stdlink,"Message",1);
MLPutFunction(stdlink,"MessageName",2);
MLPutSymbol(stdlink,fname);
MLPutString(stdlink,msgtag);
MLPutSymbol(stdlink,"$Failed");
}

和模板
void addeight P(( ));

:Begin:
:Function: addeight
:Pattern: AddEight[{{i_Integer, j_Integer},{k_Integer,l_Integer}},{{i1_Integer,j1_Integer},{k1_Integer,l1_Integer}}]
:Arguments: { i, j, k ,l, i1,j1,k1,l1 }
:ArgumentTypes: { Manual }
:ReturnType: Manual
:End:

:Evaluate: AddEight::usage = "AddEight[{{i_Integer, j_Integer},{k_Integer,l_Integer}}, {{i1_Integer, j1_Integer},{k1_Integer,l1_Integer}}] gives the sum as a list: {{i+i1,j+j1},{k+k1,l+l1}}."

:Evaluate: AddEight::badargs = "Arguments are expected to be positive numbers"


void addall P(( ));

:Begin:
:Function: addall
:Pattern: AddAll[fst:{{_Integer, _Integer},{_Integer,_Integer}},sec:{{_Integer, _Integer},{_Integer,_Integer}}]
:Arguments: { Flatten[{fst,sec}]}
:ArgumentTypes: { Manual }
:ReturnType: Manual
:End:

:Evaluate: AddAll::usage = "AddAll[{{i_Integer, j_Integer},{k_Integer,l_Integer}},{{i1_Integer, j1_Integer},{k1_Integer,l1_Integer}}] gives the total sum of elemens of the sub-lists."

:Evaluate: AddAll::badargs = "Arguments are expected to be positive numbers"

:Evaluate: AddAll::interr = "Internal error - error getting the data from Mathematica"

例子:
In[8]:= AddEight[{{1,2},{3,4}},{{5,6},{7,8}}]
Out[8]= {{6,8},{10,12}}

In[9]:= AddEight[{{-1,2},{3,4}},{{5,6},{7,8}}]
During evaluation of In[9]:= AddEight::badargs: Arguments are expected to be positive numbers

Out[9]= $Failed

In[10]:= AddAll[{{1,2},{3,4}},{{5,6},{7,8}}]
Out[10]= 36

In[11]:= AddAll[{{-1,2},{3,4}},{{5,6},{7,8}}]
During evaluation of In[11]:= AddAll::badargs: Arguments are expected to be positive numbers

Out[11]= $Failed

关于c++ - 数学 : MathLink error messages,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6526848/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com