How WolframLibraryData::Message gets the library name

Posted 5 months ago
1152 Views
|
7 Replies
|
2 Total Likes
|
 I was following the LibraryLink documentation. There are two ways to send an error from inside the C library. one using WolframLibraryData::Message which takes "tag" as an input. The second way is to send the following expression over MLink. Message[MessageName[MyFunction, "tag"], e1, e2, ...] Now This MyFunction is the Mathematica name assigned for the Library possibly with LibraryFunctionLoad. MyFunction = LibraryFunctionLoad[lib,fun, ...] The library lib is apparently unaware about the user given name MyFunction. So how will the library use the name MyFunction while constructing the MessageName expression ?On the other hand the WolframLibraryData::Message function does not require the function name to be passed. It some how magically figures this out. Although it has it's own problem (Inability of passing parameters to the error messages).There are two possibilities. the user given name is supplied to the library through some hidden attribute in the LibraryData. There is a special symbol like $CurrentLibraryFunction that can be used as a placeholder for MyFunction which will be resolved latter. Are any of these two possibilities true ? Is there any hackish way to know the name of the Library Function ? Answer 7 Replies Sort By: Posted 5 months ago  On the other hand the WolframLibraryData::Message function does not require the function name to be passed. It some how magically figures this. I don't think this is the case, when you call libData -> Message(), the messages are always issued with the generic LibraryFunction head, see for examplehttps://mathematica.stackexchange.com/questions/85374/how-to-properly-generate-messages-from-librarylink-function Answer Posted 5 months ago  When you use the WolframLibraryData::Message method with a "tag", the message issued uses LibraryFunction as the function; it calls Message[ MessageName[ LibraryFunction, "tag"]]. So it's less versatile than the math link version. src = " #include \"WolframLibrary.h\" DLLEXPORT int issueMessages(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res) { libData->Message(\"rnoset\"); return LIBRARY_RANK_ERROR; } "; << CCompilerDriver lib = CreateLibrary[src, "messageLibrary"]; messageFunc = LibraryFunctionLoad[lib, "issueMessages", {}, "Void"]; This function generates two messages, one from the libData->Message(\"rnoset\"); call and one from returning a certain value. In[5]:= messageFunc[] During evaluation of In[5]:= LibraryFunction::rnoset: The function was executed but it failed to set a value for the result. During evaluation of In[5]:= LibraryFunction::rnkerr: An error caused by inconsistent tensor rank was encountered evaluating the function issueMessages. Out[5]= LibraryFunctionError["LIBRARY_RANK_ERROR", 2] So in this example, nowhere does the library does not know the symbol name messageFunc - in your example the library never has access to the symbol name MyFunction. Answer Posted 5 months ago  Thanks. So it seems, there is no way of getting the Library Function name from inside the the library. Actually I expected that Message looks up for the appropriate function name in the hierarchy by matching the tag. However that is non-trivial and ambiguous too.Is there any hackish way of getting the LibraryFunction name ? may be like going upward in the stack. Or checking the parent context unless the LibraryFunction lives in the Global context. Answer Posted 5 months ago  There cannot possibly be a way of getting the LibraryFunction name. fun1 = LibraryFunctionLoad[...] fun2 = fun1 What is the correct name now? fun1 or fun2?I suggest handling high-level things like issuing messages with the correct head in Mathematica code, not in C. But even in Mathematica code, the same problem persists. Suppose we have a function that calls a subroutine that is used by multiple user-facing functions and can fail. How does this subroutine know which head to attach the message to? It does not which function it was called by.There are multiple solutions to this problem, but none that I know is perfect. One is to Throw a Failure-like object, catch it in the topmost function, attach the correct head to the message there, and issue it. A more common one is to pass down the correct head to the subroutine as an extra argument. You can use this with LibraryLink too.What you could also do in LibraryLink if you really want to use the correct message is this:When a failure occurs in your function, save the text of the corresponding message to a global variable and return with an error code. The LibraryFunction will them return LibraryFunctionError with the same code. On the Mathematica side, detect this, and retrieve the message text (or list of message texts) through another library function. Then finally issue the Message with the correct text within Mathematica.This would work for error messages that are issued only when your function fails, and needs to exit immediately. It would not work if your function wants to continue after it issued the message. Answer Posted 5 months ago  Here's a little demo of the concept. I did it with LTemplate for convenience, but of course you do not need LTemplate for this. In[1]:= << LTemplate In[2]:= SetDirectory@CreateDirectory[]; In[3]:= tem = LClass["Test", {LFun["messageQ", {}, True | False], LFun["getMessage", {}, "UTF8String"], LFun["sqrt", {Real}, Real]} ]; In[4]:= code = " #include class Test { const char *message_text; void setMessage(const char *msg) { message_text = msg; } public: bool messageQ() const { return message_text != nullptr; } const char *getMessage() { const char *tmp = message_text; message_text = nullptr; return tmp; } double sqrt(double x) { if (x < 0) { setMessage(\"Argument must be non-negative.\"); throw mma::LibraryError(); } return std::sqrt(x); } }; "; Export["Test.h", code, "String"]; In[6]:= CompileTemplate[tem] During evaluation of In[6]:= Current directory is: /private/var/folders/31/l_62jfs110lf0dh7k5n_y2th0000gq/T/m000003189471 During evaluation of In[6]:= Unloading library Test ... During evaluation of In[6]:= Generating library code ... During evaluation of In[6]:= Compiling library code ... Out[6]= "/Users/szhorvat/Library/Mathematica/SystemFiles/\ LibraryResources/MacOSX-x86-64/Test.dylib" In[7]:= LoadTemplate[tem] In[8]:= obj = Make["Test"]; In[9]:= General::fail = "Library failed with: "; In[10]:= check[head_][_LibraryFunctionError] := (Message[ head::fail, If[obj@"messageQ"[], obj@"getMessage"[], "Unknown error"] ];$Failed) check[head_][x_] := x In[12]:= sqrt[x_?InternalRealValuedNumericQ] :=check[sqrt]@obj@"sqrt"[x] In[13]:= sqrt[4] Out[13]= 2. In[14]:= sqrt[-1] During evaluation of In[14]:= sqrt::fail: Library failed with: Argument must be non-negative. Out[14]= \$Failed 
 Yes you can always reassign the Mathematica accessor. I just though may be there is something to keep track of the run time environment. Actually I was modeling Message in Mathematica++. This is what I came up with. I just pushed the changes that I would like to share. The custom messages are first declared. namespace messages{ struct mymsg: library_message{ static constexpr const char* tag = "mymsg"; static constexpr const char* detail = "Hallo I am a message with one placeholder 1"; }; } Then they are passed as shell << messages::mymsg() % std::string("Hallo World"); This shell can be glued with a library name at the constructor. The operator<< glues that. with Message The MessageName can be declared inside a package that loads the shared object. Optionally I have an initializer class that can be used to declare the MessageName at the time of initialization. EXTERN_C DLLEXPORT mint WolframLibrary_initialize(WolframLibraryData libData){ mathematica::initializer init(libData, "LibraryName"); init.declare(messages::mymsg()); return 0; } `I have not used exceptions for sending messages because there I should be able to send multiple messages from inside the library without aborting the program. However the exception class take a message in its constructor that are caught and sent to the frontend.