c – Mathematica:MathLink错误消息

我想我开始理解如何将用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声明了Point和Line类.除了我想在Mathematica中使用的函数之外,我已经将这个类剥离到了minium.我想找到从一条线到另一条线的距离.此函数是wireframes in Mathematica中lineInt的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,&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_GetPoint和ML_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声明了函数的用法.当在Lineheistance输入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,{p3,p4}]
  }}],{{p1,{-1,1}},Locator,Appearance -> "L1"},{{p2,{2,Appearance -> "L2"},{{p3,-2}},Appearance -> "M1"},{{p4,3}},Appearance -> "M2"}

]

我们获得的输出如下:

只要输入正确的参数,一切正常.也就是说,2个列表,每个列表是2个双打的2个列表.如果你知道怎么请让我知道,也许还有另一种获取输入的方法.如果我们坚持使用这种方法,我们需要的是让Mathematica用户知道是否有任何错误的方法.一个非常简单的输入是错误的输入.让我们说我输入:

LineDistance[{{0,0},{0}},{{1,-1},{1,1}}]

输出是$Failed.以下怎么样:

LineDistance[{{1,1}}]

输出是LineDistance [{{1,1}}].我猜这是因为我们在.tm的Pattern部分描述了函数接受两个列表,因为我们只给了一个它与模式不匹配.这是真的?

无论如何,按照我发现的教程,我们可以修改文件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,&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,"LineDistance::mlink1"))return;
    ML_GetPoint(L.p1);
    ML_GetPoint(L.p2);
}
double LineDistance(void) {
    Line L,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_Attempt中的任何其他ML函数,并且我只使用MLEvaluate发送错误标记,那么MathLink就会被破坏,我必须重新安装链接.有谁知道如何从C向Mathematica发送错误消息?

更新:

根据已经给出的答案和另一个有用的document(第8章),我设法使其工作.目前代码不是那么漂亮,但这让我问下面的问题.是否可以提前终止功能?在常规C程序中,如果遇到错误,我会打印错误消息并使用退出功能.我们可以做类似的事吗?如果我们使用exit函数,链接将被破坏,我们将不得不重新安装该函数.以ML_GetPoint和ML_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,2);
                MLPutInteger(stdlink,i+i1);
                MLPutInteger(stdlink,j+j1);
            MLPutFunction(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,2);
        MLPutFunction(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,l1 }
:ArgumentTypes:  { Manual }
:ReturnType:     Manual
:End:

:Evaluate: AddEight::usage = "AddEight[{{i_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}}]
:Arguments:      { Flatten[{fst,sec}]}
:ArgumentTypes:  { Manual }
:ReturnType:     Manual
:End:

:Evaluate: AddAll::usage = "AddAll[{{i_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,8}}]
During evaluation of In[9]:= AddEight::badargs: Arguments are expected to be positive numbers

Out[9]= $Failed

In[10]:= AddAll[{{1,8}}]
Out[10]= 36

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

Out[11]= $Failed

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


一.C语言中的static关键字 在C语言中,static可以用来修饰局部变量,全局变量以及函数。在不同的情况下static的作用不尽相同。 (1)修饰局部变量 一般情况下,对于局部变量是存放在栈区的,并且局部变量的生命周期在该语句块执行结束时便结束了。但是如果用static进行修饰的话,该变量便存
浅谈C/C++中的指针和数组(二) 前面已经讨论了指针和数组的一些区别,然而在某些情况下,指针和数组是等同的,下面讨论一下什么时候指针和数组是相同的。C语言标准对此作了说明:规则1:表达式中的数组名被编译器当做一个指向该数组第一个元素的指针; 注:下面几种情况例外 1)数组名作为sizeof的操作数
浅谈C/C++中的指针和数组(一)指针是C/C++的精华,而指针和数组又是一对欢喜冤家,很多时候我们并不能很好的区分指针和数组,对于刚毕业的计算机系的本科生很少有人能够熟练掌握指针以及数组的用法和区别。造成这种原因可能跟现在大学教学以及现在市面上流行的很多C或者C++教程有关,这些教程虽然通俗易懂,
从两个例子分析C语言的声明 在读《C专家编程》一书的第三章时,书中谈到C语言的声明问题,《C专家编程》这本书只有两百多页,却花了一章的内容去阐述这个问题,足以看出这个问题的重要性,要想透彻理解C语言的声明问题仅仅看书是远远不够的,需要平时多实践并大量阅读别人写的代码。下面借鉴《C专家编程》书中的两个
C语言文件操作解析(一)在讨论C语言文件操作之前,先了解一下与文件相关的东西。一.文本文件和二进制文件 文本文件的定义:由若干行字符构成的计算机文件,存在于计算机系统中。文本文件只能存储文件中的有效字符信息,不能存储图像、声音等信息。狭义上的二进制文件则指除开文本文件之外的文件,如图片、DOC文档。
C语言文件操作解析(三) 在前面已经讨论了文件打开操作,下面说一下文件的读写操作。文件的读写操作主要有4种,字符读写、字符串读写、块读写以及格式化读写。一.字符读写 字符读写主要使用两个函数fputc和fgetc,两个函数的原型是: int fputc(int ch,FILE *fp);若写入成功则
浅谈C语言中的位段 位段(bit-field)是以位为单位来定义结构体(或联合体)中的成员变量所占的空间。含有位段的结构体(联合体)称为位段结构。采用位段结构既能够节省空间,又方便于操作。 位段的定义格式为: type [var]:digits 其中type只能为int,unsigned int,s
C语言文件操作解析(五)之EOF解析 在C语言中,有个符号大家都应该很熟悉,那就是EOF(End of File),即文件结束符。但是很多时候对这个理解并不是很清楚,导致在写代码的时候经常出错,特别是在判断文件是否到达文件末尾时,常常出错。1.EOF是什么? 在VC中查看EOF的定义可知: #def
关于VC+ʶ.0中getline函数的一个bug 最近在调试程序时,发现getline函数在VC+ʶ.0和其他编译器上运行结果不一样,比如有如下这段程序:#include &lt;iostream&gt;#include &lt;string&gt;using namespace std;int
C/C++浮点数在内存中的存储方式 任何数据在内存中都是以二进制的形式存储的,例如一个short型数据1156,其二进制表示形式为00000100 10000100。则在Intel CPU架构的系统中,存放方式为 10000100(低地址单元) 00000100(高地址单元),因为Intel CPU
浅析C/C++中的switch/case陷阱 先看下面一段代码: 文件main.cpp#includeusing namespace std;int main(int argc, char *argv[]){ int a =0; switch(a) { case ...
浅谈C/C++中的typedef和#define 在C/C++中,我们平时写程序可能经常会用到typedef关键字和#define宏定义命令,在某些情况下使用它们会达到相同的效果,但是它们是有实质性的区别,一个是C/C++的关键字,一个是C/C++的宏定义命令,typedef用来为一个已有的数据类型
看下面一道面试题:#include&lt;stdio.h&gt;#include&lt;stdlib.h&gt;int main(void) { int a[5]={1,2,3,4,5}; int *ptr=(int *)(&amp;aʱ); printf(&quot;%d,%d&quot;,*(
联合体union 当多个数据需要共享内存或者多个数据每次只取其一时,可以利用联合体(union)。在C Programming Language 一书中对于联合体是这么描述的: 1)联合体是一个结构; 2)它的所有成员相对于基地址的偏移量都为0; 3)此结构空间要大到足够容纳最&quot;宽&quo
从一个程序的Bug解析C语言的类型转换 先看下面一段程序,这段程序摘自《C 专家编程》:#include&lt;stdio.h&gt;int array[]={23,34,12,17,204,99,16};#define TOTAL_ELEMENTS (sizeof(array)/sizeof(ar
大端和小端 嵌入式开发者应该对大端和小端很熟悉。在内存单元中数据是以字节为存储单位的,对于多字节数据,在小端模式中,低字节数据存放在低地址单元,而在大端模式中,低字节数据存放在高地址单元。比如一个定义一个short型的变量a,赋值为1,由于short型数据占2字节。在小端模式中,其存放方式为0X40
位运算和sizeof运算符 C语言中提供了一些运算符可以直接操作整数的位,称为位运算,因此位运算中的操作数都必须是整型的。位运算的效率是比较高的,而且位运算运用好的话会达到意想不到的效果。位运算主要有6种:与(&amp;),或(|),取反(~),异或(^),左移(&gt;)。1.位运算中的类型转换位
C语言文件操作解析(四)在文件操作中除了打开操作以及读写操作,还有几种比较常见的操作。下面介绍一下这些操作中涉及到的函数。一.移动位置指针的函数 rewind函数和fseek函数,这两个函数的原型是:void rewind(FILE *fp); 将位置指针移动到文件首 int fseek(FILE
结构体字节对齐 在用sizeof运算符求算某结构体所占空间时,并不是简单地将结构体中所有元素各自占的空间相加,这里涉及到内存字节对齐的问题。从理论上讲,对于任何变量的访问都可以从任何地址开始访问,但是事实上不是如此,实际上访问特定类型的变量只能在特定的地址访问,这就需要各个变量在空间上按一定的规则排
C语言文件操作解析(二)C语言中对文件进行操作必须首先打开文件,打开文件主要涉及到fopen函数。fopen函数的原型为 FILE* fopen(const char *path,const char *mode) 其中path为文件路径,mode为打开方式 1)对于文件路径,只需注意若未明确给出绝