18
$\begingroup$

Mathematica 12 has a new function FunctionCompileExportLibrary, which can export a pure function as a .dll file like this:

FunctionCompileExportLibrary["function.dll", Function[
   Typed[arg, "MachineInteger"], arg + 1]]

"...\\function.dll"

But how to call it in an external program (e.g., visual studio)? I have no .h file, I have no .lib file. I even don't know the function name that I want to call... Can anyone tell me how to use it?

The documentation of FunctionCompileExportLibrary states that

The library generated by FunctionCompileExportLibrary is suitable for linking into external programs. It can also be loaded into the Wolfram System using LibraryFunctionLoad.

$\endgroup$
2
  • $\begingroup$ Could you try something like what is done here? $\endgroup$ Commented Apr 17, 2019 at 21:02
  • $\begingroup$ @SjoerdC.deVries I have look your link, but I'm not good at it. Could you please give an example to use it? $\endgroup$
    – yode
    Commented Apr 18, 2019 at 5:42

3 Answers 3

20
$\begingroup$

This is a quick example of calling the exported compiled function from a standalone executable on Windows.

A few things to note which are currently undocumented and that can definitely be subject to change (and improvement) in the future:

  • the function is named Main by default and the example uses an explicit prototype rather than including a header file;

  • the Wolfram Runtime Library is linked statically for convenience (of course, the dynamic WolframRTL.dll could be used instead provided the executable can find it);

  • CreateExecutable is also used for convenience here, however the test executable could be compiled in any other way;

  • while the Windows import library (function.lib) is created, currently it is not placed next to the DLL. I expect this will be corrected in a paclet update.

    lib = FunctionCompileExportLibrary["function.dll",
              Function[Typed[arg, "MachineInteger"], arg + 1], 
               "CompilerOptions" -> "CreateLibraryOptions" -> 
                  {"SystemLibraries" -> {"WolframRTL_Static_Minimal"}}];
    
    Needs["CCompilerDriver`"]
    
    test = CreateExecutable["
       #include <stdio.h>
       #include <inttypes.h>
       int64_t Main(int64_t);
       int main() {
          printf(\"%\" PRId64, Main(10));
       }", 
       "test", "Libraries" -> {FileNameJoin[{$CCompilerDefaultDirectory, "function.lib"}]}];
    
    RunProcess[test, "StandardOutput"]
    
    (* "11" *)
    

Regarding the follow-up question in the edit: I think the linker is looking for the C++ mangled name. Try declaring Main with C linkage, i.e. extern "C". Also make sure the prototype is using the correct integer type.

$\endgroup$
11
  • $\begingroup$ Fun, would mind to tell me how to know the name is 'Main'? It's seem not very explicit... $\endgroup$
    – yode
    Commented Apr 19, 2019 at 3:36
  • $\begingroup$ One way is to look at e.g. LibraryFunctionLoad[lib] // InputForm $\endgroup$
    – ilian
    Commented Apr 19, 2019 at 3:53
  • 1
    $\begingroup$ it is also discoverable by using nm -gU library.dylib under macOS. $\endgroup$
    – sunt05
    Commented Apr 19, 2019 at 6:15
  • 1
    $\begingroup$ This is quite promising! I am curious about other valid settings in "CompilerOptions", which seem to make this function more powerful! However, this is heavily undocumented... $\endgroup$
    – sunt05
    Commented Apr 19, 2019 at 6:16
  • 1
    $\begingroup$ This method does not work on osx, got dyld: Library not loaded: @rpath/function.dylib Referenced from: ... Reason: image not found Abort trap: 6 on the generated executable. $\endgroup$
    – vapor
    Commented Apr 19, 2019 at 10:19
5
$\begingroup$

As the ilian's answer, I make a note for Visual Studio user here to supplement:

source.cpp file

#include<iostream>
using namespace std;
extern "C" int Main(int);
int main() {
    cout << Main(3) << endl;
    return 0;
}

configure Visual Studio

  1. In Configuration Properties/Debugging/Environment add

PATH=C:\Program Files\Wolfram Research\Mathematica\12.0en\SystemFiles\Kernel\Binaries\Windows-x86-64;%appdata%\Mathematica\SystemFiles\LibraryResources\Windows-x86-64;C:\Program Files\Wolfram Research\Mathematica\12.0en\SystemFiles\Libraries\Windows-x86-64

The first directory has some necessary .dll file. The second and the three directory have function.dll and WolframRTL.dll respectively. Of course, your can don't set the PATH evironment if you copy all those .dll file into the direcotry with source.cpp or the Configuration Properties/Output Directory.

  1. In Configuration Properties/VC++ Directories/Library Directories add

%appdata%\Mathematica\SystemFiles\LibraryResources\Windows-x86-64

This directory include function.lib

  1. In Configuration Properties/Linker/Input/Additional Dependencies add

function.lib

Then you can run the c++ project now. I think this is a small step for Wolframer, but a giant leap for those men who hope to use Mathematica in his work.

$\endgroup$
4
$\begingroup$

This is a follow-up of @ilian's answer, which doesn't work under macOS but provides a promising basis.

The error reported by @happy fish that dyld: Library not loaded: @rpath/function.dylib Referenced from: ... Reason: image not found Abort trap: 6 suggests the linking was not correct. Then I tried to correct the @rpath issue (which seems to be a bug but I'm not quite sure; this needs further investigation) and still got other linking issues which clearly indicated libWolframRTL_Static_Minimal.a was not statically linked (which is a well-known pain on macOS).

So below I provide a workaround for macOS, which used the FunctionCompileExport instead of FunctionCompileExportLibrary due to possible linking bug in it:

pathStaticLib="/Applications/Mathematica.app/Contents/SystemFiles/Libraries/MacOSX-x86-64/libWolframRTL_Static_Minimal.a";
lib=FunctionCompileExport["function.o",Function[Typed[arg,"MachineInteger"],arg+1];

Needs["CCompilerDriver`"]

test=CreateExecutable["
   #include <stdio.h>
   #include <inttypes.h>
   int64_t Main(int64_t);
   int main() {
      printf(\"%\" PRId64, Main(10));
   }","test","CompileOptions"->pathStaticLib,"ExtraObjectFiles"->{lib}];

RunProcess[test]

(*<|"ExitCode"->0,"StandardOutput"->"11","StandardError"->""|>*)
$\endgroup$
1
  • $\begingroup$ This doesn't compile as soon as you try to use a Kernel function. $\endgroup$
    – Mauro Lacy
    Commented Mar 22, 2020 at 12:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.