首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >MathLink错误消息

MathLink错误消息
EN

Stack Overflow用户
提问于 2011-06-29 20:29:35
回答 3查看 3.2K关注 0票数 8

我想我开始了解如何将用C/C++编写的函数连接到Mathematica。我面临的问题是,我不知道如何将错误消息从我的C包装器发送到Mathematica。在谷歌搜索之后,我找到了这个MathLink教程

第1.7节给了我一个关于如何发送错误消息的洞察力,但是我得到了奇怪的结果。这是我正在使用的代码。

代码语言:javascript
复制
//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

H文件声明了类PointLine。我已经把这个类剥离到了最小值,除了我想在Mathematica中使用的函数。我想找出从一条线到另一条线的距离。这个函数是lineInt数学中的线框中的C版本。要在Mathematica中使用这个函数,我们需要一个包装器函数,它从Mathematica获得输入,并将输出发送回Mathematica。

代码语言:javascript
复制
//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获得输入。从包含两个列表的列表中获得一行。每个子列表是一个包含两个实数的列表(一个点)。要在Mathematica中尝试这个函数,我们需要更多的文件。

代码语言:javascript
复制
//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输入数学时函数的简要说明。当出现错误时,我希望使用另一个Evaluate (稍后详细介绍)。

代码语言:javascript
复制
#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。在这一点上,我们都设置了测试函数的数学。

我之前应该提到我使用的是Mac,我不知道这在Windows上会如何工作。在mlwrapper.cpp中,主函数需要更多的代码,您可以在Mathematica提供的一个示例中找到这些代码。

在航站楼,我知道这样做:

代码语言:javascript
复制
make > makelog.txt
make clean

这将使可执行文件mlwrapper。现在我们可以开始使用Mathematica了:

代码语言:javascript
复制
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"}

]

我们获得的输出如下:

只要输入正确的参数,一切都正常。也就是说,两个列表,每一个是由两个双倍的两个列表组成的列表。也许还有另一种获取输入的方法,如果你知道如何让我知道。如果我们坚持这种方法,我们所需要的只是一种方法,让Mathematica用户知道是否有任何错误。一个非常简单的输入是输入错误。让我说我输入如下:

代码语言:javascript
复制
LineDistance[{{0, 0}, {0}}, {{1, -1}, {1, 1}}]

输出为$Failed。以下几点如何?

代码语言:javascript
复制
LineDistance[{{1, -1}, {1, 1}}]

输出为LineDistance[{{1, -1}, {1, 1}}]。我猜这是因为我们在Pattern部分中描述了函数接受两个列表,而且由于我们只给出了一个列表,所以它与模式不匹配。这是真的吗?

无论如何,在我发现的教程之后,让我们按以下方式修改文件mlwrapper.cpp:

代码语言:javascript
复制
#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文件的末尾

代码语言:javascript
复制
: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吗?

更新:

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

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2011-06-29 21:22:07

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

代码语言:javascript
复制
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"

职能如下:

代码语言:javascript
复制
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");
    }
}

以下是使用的例子:

代码语言:javascript
复制
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数学函数封装到执行错误检查的另一个函数中。不过,我对Mathlink的经验非常有限,所以我在这里的看法可能不太重要。

编辑

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

代码语言:javascript
复制
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");
}

和模板

代码语言:javascript
复制
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"

示例:

代码语言:javascript
复制
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
票数 4
EN

Stack Overflow用户

发布于 2011-06-29 20:59:25

像这样的东西通常对我有用:

代码语言:javascript
复制
void putMessage(const char* messageSymbol, const char* messageTag, const char* messageParam)
{
    MLNewPacket(stdlink);
    MLPutFunction(stdlink, "EvaluatePacket", 1);

    MLPutFunction(stdlink, "Message", 2);
        MLPutFunction(stdlink, "MessageName", 2);
            MLPutSymbol(stdlink, messageSymbol);
            MLPutString(stdlink, messageTag);

        MLPutString(stdlink, messageParam);

    MLFlush(stdlink);
    MLNextPacket(stdlink);
    MLNewPacket(stdlink);
}

你仍然需要返回一个结果。

代码语言:javascript
复制
MLPutSymbol(stdlink, "$Failed");
票数 3
EN

Stack Overflow用户

发布于 2011-07-01 02:36:40

这篇文章是为任何对我如何写我的最后代码感兴趣的人写的。此代码基于与@Leonid的有益讨论。让我们从实用程序文件开始。

代码语言:javascript
复制
//MLErrors.h
#include <stdarg.h>
#include <vector>
#include <sstream>

#define CCHAR const char*
#define UINT unsigned int
class MLException {
public:
    CCHAR sym;
    CCHAR tag;
    std::vector<std::string> err;
    MLException(CCHAR msgSym, CCHAR msgTag, UINT n, ...): 
    sym(msgSym), tag(msgTag), err(n)
    {
        std::stringstream ss;
        va_list args;
        va_start(args, n);
        for (UINT i=0; i < n; ++i) {
            err[i] = va_arg(args, CCHAR);
            if (err[i][0] == '%') {
                switch (err[i][1]) {
                    case 'i':
                        ss << va_arg(args, int);
                        break;
                    case 'd':
                        ss << va_arg(args, double);
                        break;
                    default:
                        break;
                }
                err[i] = ss.str();
            }
        }
        va_end(args);
    }
};
#undef CCHAR
#undef UINT

void ML_SendMessage(const MLException& e){
    if (MLError(stdlink) != MLEOK) MLClearError(stdlink); 
    MLNewPacket(stdlink); 
    MLPutFunction(stdlink, "EvaluatePacket", 1);
    MLPutFunction(stdlink, "Message", e.err.size()+1);
    MLPutFunction(stdlink, "MessageName", 2);
    MLPutSymbol(stdlink, e.sym);
    MLPutString(stdlink, e.tag);
    for (int i=0; i < e.err.size(); ++i) {
        MLPutString(stdlink, e.err[i].c_str());
    }
    MLFlush(stdlink);
    MLNextPacket(stdlink);
    MLNewPacket(stdlink);
    MLPutSymbol(stdlink, "$Failed");
}

该文件包含MLException类和函数ML_SendMessage。以下是简单的解释。MLException类型的对象包含两个字符串和一个字符串向量。如果e是一个MLException,那么e.sym必须是一个有效的Mathematica符号,e.tag必须是一个有效的标记。我们在模板文件中定义这个Symbol::tag。如果Symbol::tag包含参数,那么它们将存储在e.err中。例如,假设您在模板文件中声明了以下内容:

代码语言:javascript
复制
:Evaluate: someSymbol::someTag = "Error, the program encountered: `1`, it needed `2`."

然后,如果出于某些原因,C函数中出现了错误,则可以通过抛出带有某种消息的异常来摆脱该错误。如下所示:

代码语言:javascript
复制
if(ERROR) throw MLException("someSymbol", "someTag", 2, "this", "that");

注意第三个参数是一个整数。这是将放置的消息数,而不是消息中的"1“和"2”。这意味着,您将在Mathematica中看到的信息是:“错误,遇到的程序:这个,它需要它。”如果您需要在消息中包含数字,那么我会写一个字符串,后面跟着一个数字。例如,如果您想要写数字100,然后再写一些其他消息,那么您可以抛出如下异常:

代码语言:javascript
复制
 if(ERROR) throw MLException("someSymbol", "someTag", 2, "%i", 100, "msg");

如果你想要包含一个双,然后使用"%d“代替。

ML_sendMessage函数接受异常,清除错误,向Mathematica发送一条消息,并放置$Failed

下面是我的模板文件:

代码语言:javascript
复制
//mlwrapper.tm
:Evaluate: BeginPackage["mlwrapper`"]

:Evaluate: EMPH[a_] := ToString[Style[a, "TI"], StandardForm]
:Evaluate: LINK[url_, label_ : Style["\[RightSkeleton]", "SR"]] := ToString[Hyperlink[label, url], StandardForm]
:Evaluate: LineDistance::usage = "LineDistance["<>EMPH["L"]<>", "<>EMPH["M"]<>"] gives the distance between two lines. "<>LINK["#"]

:Evaluate: mlwrapper::mlink = "There has been a low-level MathLink error. The message is: `1`"
:Evaluate: GetPoint::narg = "A point is a list of 2 numbers. A list of `1` elements was passed instead. "<>LINK["#"]
:Evaluate: GetLine::narg = "A line is a list of 2 points. A list of `1` elements was passed instead. "<>LINK["#"]

:Evaluate: EndPackage[]

:Evaluate: Begin["mlwrapper`Private`"]

void LineDistance P((void));
:Begin:
:Function: LineDistance
:Pattern: LineDistance[L_List, M_List]
:Arguments: {L, M}
:ArgumentTypes: {Manual}
:ReturnType: Manual
:End:

:Evaluate: End[]

我决定把这个做成一个包裹。我还声明了函数EMPHLINK。第一种强调单词,另一种则允许我们编写超链接。一旦我学习如何正确地文档,我将添加超链接到描述。

现在我们可以用数学的方法来描述错误

代码语言:javascript
复制
//mlwrapper.cpp
#include "mathlink.h"
#include "MLErrors.h"
#include <cmath>
#include "cppFunctions.h"

#define MLINKERROR MLException("mlwrapper", "mlink", 1, MLErrorMessage(stdlink))

void ML_GetPoint(Point &P){
    long n = 0;
    MLCheckFunction(stdlink, "List", &n);
    if (n != 2) throw MLException("GetPoint", "narg", 1, "%i", n);
    MLGetReal64(stdlink, &P.x);
    MLGetReal64(stdlink, &P.y);
    if (MLError(stdlink) != MLEOK) throw MLINKERROR;
}
void ML_GetLine(Line &L){
    long n = 0;
    MLCheckFunction(stdlink, "List", &n);
    if (n != 2) throw MLException("GetLine", "narg", 1, "%i", n);
    ML_GetPoint(L.p1);
    ML_GetPoint(L.p2);
}
void LineDistance(void) {
    Line L, M;
    try {
        ML_GetLine(L);
        ML_GetLine(M);
    }
    catch (MLException& e) {
        ML_SendMessage(e);
        return;
    }
    MLPutReal64(stdlink, L.distanceTo(M));
}
int main(int argc, char* argv[]) {
    return MLMain(argc, argv);
}

现在,由于我正在制作一个包,我们需要最后一个文件: mlwrapper.m。在这个文件中,我们添加了这一行:Install["mlwrapper"];。当然,我们假设可执行的mlwrapper位于同一个目录中。最后,我展示了结果的截图:

我写最后一条语句是为了了解异常的开销。我主要希望异常处理输入的获取,或者根据C/C++函数的返回语句处理其他一些错误。在任何情况下,毫无例外地编写包装器并不比例外情况好得多。

所以你就有了。另一个例子,如何从Mathematica调用C/C++函数。

我还要感谢@alexey给了我写EMPHLINK的想法。这让我头疼,找出如何格式化我的信息。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6526848

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档