123
$\begingroup$

Update: While at the time of writing the question loading DLLs with .NET/Link seemed easier, now I always use LibraryLink, which I recommend to anyone with a similar problem!


As of Mathematica 8, what is the minimal effort way to integrate an existing C++ function into Mathematica?

I think we have these:

  • MathLink (it was quite long ago I used it last time)
  • communication through pipes/files (Import is slow, ReadList not so much)
  • LibraryLink (??)
  • Mathematica 8's C code generation features (??) (apparently this is not relevant)

The keyword here is minimal effort. Which is the most convenient way, including the learning curve for the particular method?

I'm mainly interested in doing it on Windows. My particular function computes a long list of real numbers. The function input consists only of a few scalars (int & double).

An answer of the type "You'll likely spend the least time if you use technology X" is useful. A concrete example of how to do it using a small function is even better.


Answers

Each and every answer I got is great, and each demonstrates a different technology. It's impossible to choose a definitive one. Here's a small "table of contents" for them:

A presentation about the topic from the Wolfram Technology Conference is here (CDF file).


Experiences

I used two of these methods: the .NETLink method and Library Link. These are my personal experiences:

  • .NETLink This was easy to set up, however, I'd recommend it only for when you already have a compiled DLL from which you need to load functions (e.g. do something like this). If you compile your own DLL: once the DLL is loaded, it is locked and cannot be overwritten. A quick and dirty way to allow overwriting it is realoding NETLink with ReinstallNET[].

    Advantages: Very quick and easy, only Mathematica code is needed if the functions are already compiled into a DLL.

    Disadvantages: Windows-only, does not parallelize from within Mathematica, and the calculation will not be interruptible.

  • Library Link It's much easier to use this than what it looks like at first. Less setup is needed than in the case of MathLink, and compilation is automated from within Mathematica.

    Advantages: Also quite easy, but both Mathematica and C code are needed. It is parallelizable, and easily made interruptible. The library can be unloaded (for recompilation) using LibraryUnload.

$\endgroup$
6
  • $\begingroup$ Is that a List of scalars, or a finite number of parameters? $\endgroup$
    – rcollyer
    Commented Nov 15, 2011 at 18:04
  • $\begingroup$ @rcollyer By "scalars" I meant that I have a finite number of parameters, each of which is a number. I wanted to point out that I don't need to pass variable (or fixed...) length vectors/list $\endgroup$
    – Szabolcs
    Commented Nov 15, 2011 at 18:06
  • $\begingroup$ That's what I thought you meant, I was just trying to be clear. $\endgroup$
    – rcollyer
    Commented Nov 15, 2011 at 18:11
  • $\begingroup$ CCompilerDriver/tutorial/Compilation#472267182 in the Mathematica documentation is probably a good starting point. $\endgroup$ Commented Nov 15, 2011 at 18:22
  • 4
    $\begingroup$ The C code generation features are about converting Compile expressions to C code for speed or use them outside Mathematica, rather than accessing existing C code from the kernel. $\endgroup$
    – Joel Klein
    Commented Nov 16, 2011 at 15:45

7 Answers 7

70
$\begingroup$

Update: I posted a tutorial series on LibraryLink at Wolfram Community.

Update: LibraryLink example code is also available on this GitHub repository.


Here is my pitch to use LibraryLink, which is a really nice new technology in version 8. I am not going to pretend this is easy by any stretch of the imagination, because it involves a decent amount of knowledge of both Mathematica and C compilers. In this particular case I am using Visual Studio C++ Express plus the Microsoft Windows SDK 7.1. For the record, I had quite a bit of help from Joel Klein with this answer.

LibraryLink is set up to find most compilers on your system, but in my case I had to restart after installing the above tools (although looking back I think restarting my open frontend might have done the trick as well).

There are several examples of how to use LibraryLink in the documentation, but this example I wrote from scratch. All LibraryLink C code is compiled with the CreateLibrary function (which is located in the CCompilerDriver package.

Mathematica side

I'll skip the source for now, and focus on the Mathematica commands. First we loads the package to run the library link utility functions:

Needs["CCompilerDriver`"]

Next, we load the source file and create a library (.dll) from it:

myLibrary = 
  CreateLibrary[{"c:\\users\\arnoudb\\myLibrary.c"}, "myLibrary", "Debug" -> False];

Next, we load the one function defined in this library:

myFunction = LibraryFunctionLoad[myLibrary, "myFunction", {{Real, 2}}, {Real, 2}];

Next we use this function:

myFunction[{{1, 2, 3, 4}, {5, 6, 7, 8}}]

which returns the square of every matrix entry: {{1., 4., 9., 16.}, {25., 36., 49., 64.}}.

Finally, we unload the library (needed if we make changes to the source file and reload everything above):

LibraryUnload[myLibrary];

C side

There is a lot of boiler plate magic, to make things 'work'. These first four line need to always be included:

#include "WolframLibrary.h"
DLLEXPORT mint WolframLibrary_getVersion(){return WolframLibraryVersion;}
DLLEXPORT int WolframLibrary_initialize( WolframLibraryData libData) {return 0;}
DLLEXPORT void WolframLibrary_uninitialize( WolframLibraryData libData) {}

This is the actual function that you write. The function header is always the same, you get the actual function arguments from the MArgument_* api functions:

DLLEXPORT int myFunction(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res){

 int err; // error code

 MTensor m1; // input tensor
 MTensor m2; // output tensor

 mint const* dims; // dimensions of the tensor

 mreal *data1; // actual data of the input tensor
 mreal *data2; // data for the output tensor

 mint i; // bean counters
 mint j;

This gets the input tensor and dimensions and sets up the output tensor and actual data:

 m1 = MArgument_getMTensor(Args[0]);
 dims = libData->MTensor_getDimensions(m1);
 err = libData->MTensor_new(MType_Real, 2, dims,&m2);
 data1 = libData->MTensor_getRealData(m1);
 data2 = libData->MTensor_getRealData(m2);

The actual interesting stuff, this squares each element:

 for(i = 0; i < dims[0]; i++) {
  for(j = 0; j < dims[1]; j++) {
   data2[i*dims[1]+j] = data1[i*dims[1]+j]*data1[i*dims[1]+j];
  }
 }

This set the return value (and yes, you don't want to return the 'err' value here):

 MArgument_setMTensor(Res, m2);
 return LIBRARY_NO_ERROR;
}
$\endgroup$
13
  • 2
    $\begingroup$ For me, the case was made with the function CreateLibrary, +1. $\endgroup$
    – rcollyer
    Commented Nov 16, 2011 at 3:14
  • $\begingroup$ There are plenty of examples in the documentation, I guess it's easier to learn by examples, I tested a dummy example once in VisualC++ and it worked (don't forget "extern C" sometimes) here are some links: reference.wolfram.com/mathematica/LibraryLink/tutorial/… reference.wolfram.com/mathematica/TetGenLink/tutorial/… reference.wolfram.com/mathematica/LibraryLink/tutorial/… reference.wolfram.com/mathematica/CCodeGenerator/tutorial/… $\endgroup$
    – faysou
    Commented Nov 16, 2011 at 21:54
  • 1
    $\begingroup$ It looks like I'll go with this approach after all! But there's a little problem in the code: after macro expansion, MArgument_setMTensor(Res, m2) will look like (*(Res.tensor)) = m2, i.e. err = MArgument_setMTensor(Res, m2); will eventually "evaluate" to err = m2, which is incorrect and will also prevent the code from being compiled as C++. Could you correct this? Also, if I got it right, one must add EXTERN_C in front of all the DLLEXPORTs if the code is C++. $\endgroup$
    – Szabolcs
    Commented Nov 27, 2011 at 12:16
  • $\begingroup$ Another important point when using C++ with the MinGW compiler: it's necessary to pass "Libraries" -> "-lstdc++" to CreateLibrary[], otherwise there will be linking errors for any non-trivial C++ program. Is this a compiler driver bug by any chance? I would have expected this to happen automatically when using "Language" -> "C++" (since the "Libraries" option will be different for different compilers -- so the Mathematica side of my code is now not completely portable) $\endgroup$
    – Szabolcs
    Commented Nov 27, 2011 at 13:39
  • 1
    $\begingroup$ @Szabolcs Hi, Szabolcs, I didn't make a post because I figure out the solution. String is also working for C++. But we have to set gcc to treat .c file as c++, this can be done by compile option -x c++. In this way, we don't need "Libraries" -> "-lstdc++" or even "Language" -> "C++". We createlibrary using this CreateLibrary[libnewsource, "func", "CompileOptions" -> "-x c++"] $\endgroup$
    – matheorem
    Commented Jan 12, 2016 at 1:46
49
$\begingroup$

On Windows, C/C++ functions that have been compiled into DLLs can be accessed reasonably easily using NETLink. Let's say we have the following C++ DLL definition:

#include "stdafx.h"

BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID reserved) {
    return TRUE;
}

extern "C" __declspec(dllexport)
void helloMma(double a, double b, int n, double m[]) {
    for (int i = 0; i < n ; ++i) {
        m[i] = a * i + b;
    }
}

We can import this function into Mathematica as follows:

Needs["NETLink`"]

$dllPath = "C:\\some\\path\\to\\hellomma.dll";

helloMma = DefineDLLFunction[
  "helloMma", $dllPath, "void", {"double", "double", "int", "double[]"}
]

Alas the calling sequence for this function is complicated slightly by the fact that it returns its result by destructively overwriting a statically allocated array of doubles (not an uncommon occurrence). To do this from Mathematica, we must pre-allocate the result vector using NETNew:

In[23]:= NETBlock@Module[{n, result}
         , n = 10
         ; result = NETNew["System.Double[]", n]
         ; helloMma[3, 5, n, result]
         ; NETObjectToExpression[result]
         ]
Out[23]= {5., 8., 11., 14., 17., 20., 23., 26., 29., 32.}

Note that all the usual Bad Things would happen if the pre-allocated buffer were overrun. NETBlock is used to ensure that the storage allocated for the buffer is released when we are done with it.

I will point out a couple of "gotchas". Make sure that the DLL is compiled as 32-bit or 64-bit to match the version of Mathematica that you are running. Also, note that the exported DLL function in the example is declared as extern "C". This prevents the C++ compiler from "mangling" the name and makes it easier to reference in the DefineDLLFunction declaration (in this case the Visual Studio C++ mangled name was ?helloMma@@YAXNNHQEAN@Z).

$\endgroup$
7
  • $\begingroup$ I'm accepting this as a minimal effort solution that worked for me on Windows, but all the answers were great and very useful! $\endgroup$
    – Szabolcs
    Commented Nov 25, 2011 at 16:19
  • $\begingroup$ Is it possible to unload the DLL so I can recompile the DLL without quitting the kernel? $\endgroup$
    – Szabolcs
    Commented Nov 25, 2011 at 16:28
  • $\begingroup$ Unloading the DLL might be tricky. If Mma is using .NET interop to load the DLL, then unloading might not be practical -- one would have to load the DLL into a separate application domain and unload that domain. I'm not sure whether Mma would give us control over application domains. If Mma is not using .NET interop, then calls to the standard Win32 API functions GetModuleHandle and FreeLibrary through NETLink might do the trick. This is all speculation on my part at this point -- I have not tried either approach myself. $\endgroup$
    – WReach
    Commented Nov 25, 2011 at 17:54
  • 1
    $\begingroup$ For practical purposes, ReinstallNET[] works great for "unloading". Of course it destroys everything else .NET related too, but all I wanted was to recompile my DLL without quitting the Mathematica kernel (and I wasn't using .NET for anything else). I should update the post eventually with my experiences with this method and Library Link. The two significant disdvantages of the .NETLink method that remain are that 1. it's Windows only 2. it's not parallelizable from within Mathematica (think ParallelTable). Correct me if I am wrong. $\endgroup$
    – Szabolcs
    Commented Dec 21, 2011 at 17:15
  • 1
    $\begingroup$ @Szabolcs, in the above example, the use of NETBlock would allow you to recompile your dll file without UninstallNET[] or ReinstallNET[] $\endgroup$
    – unstable
    Commented May 18, 2012 at 11:37
40
$\begingroup$

Presuming that your c++ code is already written, then I don't know how the code generation feature would be helpful. That said, in order of simplicity, I would have Get, ReadList, Import, and both LibraryLink and MathLink.


Get and ReadList are by far the simplest. Provided that your c++ program outputs to stdout (std::cout), then it is simply

val = (<<"!command")

or,

ReadList["!command", Number (*or some other specifier*)]

If you need to control your external program more directly, or pass additional data to it (like run your function multiple times), then this method is more difficult and may require you to open pipes to communicate through (1, 2).


Import would require you to conform to some data format, which while very doable, is not as simple as just putting numbers onto stdout.


I have never used LibraryLink, but a quick perusal of the docs implies that it is a simplification of the MathLink interface. More specifically, it is easier to create a function that can communicate with Mathematica, but the intricacies of sending data back and forth remain. To return a List, though, is not to bad, and may well be worth it. The catch, of course, is that you need to provide a c interface to your function, which differs from the MathLink form, but is still necessary. So, I'm not sure there is an advantage here for one or the other.

$\endgroup$
5
  • 3
    $\begingroup$ To the downvoter, I am interested in what was missing from my answer. Did I give to little detail? Or, was it considered incorrect in some way? Either way, would you please elaborate. I'm not trying to complain here, I'm just curious what I could have done instead. $\endgroup$
    – rcollyer
    Commented Nov 15, 2011 at 21:31
  • $\begingroup$ It's possibly from all the pedestrian traffic in the C++ tag. Surely not a downvote from a Mathematica user. An upvote from me to get you closer to a silver :) $\endgroup$
    – yoda
    Commented Nov 16, 2011 at 14:44
  • $\begingroup$ @yoda, you're probably right. They, most likely, got "mad" when I didn't post much code. Oh well. Any way, thanks for the upvote, although I'm still over a hundred shy of the silver. And, I've got to run over Heike first ... :D $\endgroup$
    – rcollyer
    Commented Nov 16, 2011 at 14:57
  • $\begingroup$ This sort of downvotes seems to just be a fact of life, a drive-by shooting of sorts. I personally think that it is a moral obligation of the downvoter to explain the reasons (I don't downvote myself), but obviously not everyone thinks the same. In any case, here is another one towards your silver - +1 :) $\endgroup$ Commented Nov 16, 2011 at 15:41
  • $\begingroup$ @LeonidShifrin, belisarius and I had a long conversation about downvotes and associated messages. For the most part, he doesn't think the messages influence the people who need it, and the people who don't, aren't likely to be downvoted. In this case, I was honestly curious what they figured was needed, especially considering I do pay attention. But, alas, it was not to be. :P (I downvote rarely. Reserving it for incorrect, or misleading, answers. And, usually, it is outside of mathematica.) $\endgroup$
    – rcollyer
    Commented Nov 16, 2011 at 15:56
29
$\begingroup$

I suggest to use MathLink, which you can automate using the CCompilerDriver`. This is a safe alternative, since you won't crash the kernel if your code crashes. Once tested, this should not be hard to convert to library link. As an explicit example, consider a function which receives a list of integers and squares it. First, here is a function to create the boilerplate code:

makeMLinkCodeF = 
  StringJoin[
     "#include \"mathlink.h\"", "\n", ##, "\n",
     "
     #if WINDOWS_MATHLINK

     #if __BORLANDC__
     #pragma argsused
     #endif

     int PASCAL WinMain( HINSTANCE hinstCurrent, HINSTANCE 
           \ hinstPrevious, LPSTR lpszCmdLine, int nCmdShow)
     {
     \tchar  buff[512];
     \tchar FAR * buff_start = buff;
     \tchar FAR * argv[32];
     \tchar FAR * FAR * argv_end = argv + 32;

     \thinstPrevious = hinstPrevious; /* suppress warning */

     \tif( !MLInitializeIcon( hinstCurrent, nCmdShow)) return 1;
     \tMLScanString( argv, &argv_end, &lpszCmdLine, &buff_start);
     \treturn MLMain( (int)(argv_end - argv), argv);
     }

     #else

     int main(int argc, char* argv[])
     {
     \treturn MLMain(argc, argv);
     }

     #endif"] &;

It is pretty ugly, but you only need to define it once. Perhaps, a better way would be to rewrite it using Symbolic C, but I had no time to do it. Now, here is the code for our function:

code = 
"
 extern void squareList(int * data, long len);

 void squareList(int * data, long len){
    int *d = data;
    MLPutFunction(stdlink,\"List\",len);
    while(d-data<len){
        MLPutInteger(stdlink,(*d) * (*d));
        d++;
    }
 }
";

And here is the template:

template = 
StringReplace[#,"\n"~~Whitespace:>"\n"]&@
"
void squareList P((int *, long));

:Begin:
:Function:       squareList
:Pattern:        squareList[data_List]
:Arguments:      { data }
:ArgumentTypes:  { IntegerList }
:ReturnType:     Manual
:End:

";

(StringReplace was needed since otherwise the template would not copy-paste correctly from SO - it would contain spaces on the left, which would prevent us from creating an executable. You don't have to do this in the interactive session).

We must now load the compiler driver:

Needs["CCompilerDriver`"];

The following code prepares our stuff:

fullCCode = makeMLinkCodeF[code];
projectDir = "C:\\Temp\\MLProject";
If[! FileExistsQ[projectDir], CreateDirectory[projectDir]]
pname = "squareList";
files = 
 MapThread[
    Export[FileNameJoin[{projectDir, pname <> #2}], #1, "String"] &, 
    {{fullCCode, template}, {".c", ".tm"}}];

Now we attempt to create an executable:

In[19]:= exe=CreateExecutable[files,pname]
Out[19]= C:\Users\Archie\AppData\Roaming\Mathematica\SystemFiles\LibraryResources
  \Windows-x86-64\squareList.exe

We now install it:

In[20]:= Install[exe]
Out[20]= LinkObject["C:\Users\Archie\AppData\Roaming\Mathematica\SystemFiles\
LibraryResources\Windows-x86-64\squareList.exe",10,8]

And finally use it:

In[21]:= squareList[Range[5]]
Out[21]= {1,4,9,16,25}


In[23]:= Uninstall[exe]
Out[23]= C:\Users\Archie\AppData\Roaming\Mathematica\SystemFiles\LibraryResources
\Windows-x86-64\squareList.exe

There were a number of steps here, but all of them are within Mathematica, and most of them are common for all functions and can be factored away. If you end up doing lots of coding, I'd create a Mathematica-based DSL which would generate Symbolic C for your functions and also your templates. If / when time permits, I intend to improve this answer by automating the procedure more, and by replacing the fragile string-based pieces with functions to generate them.

$\endgroup$
2
  • $\begingroup$ Would be very interesting to see the automation stuff :). $\endgroup$
    – nixeagle
    Commented Dec 19, 2011 at 22:39
  • $\begingroup$ @nixeagle Yes, I also would like to do this, since the string-based manipulations are fragile. This is not really difficult, just need some free time :). $\endgroup$ Commented Dec 20, 2011 at 16:41
19
+500
$\begingroup$

In terms of loading C++ functions, I would strongly suggest LibraryLink. It is a great tool except that it requires you to write sometimes intimidating C code.

To make LibraryLink easier to use, I have developed a package called wll-interface, available in this github repository. It is a header-only library written in C++, doing only one thing—automatically generate the code that interact with LibraryLink.


A simple example

Here I give an example of how wll-interface simplifies the process of loading a C++ function; this example can also be found here.

Let's say that we have a C++ function multiply and we want to load it into Mathematica.

double multiply(double x, int y)
{
    return x * y;
}

Instead of calling LibraryLink APIs, all we need to do with the help of wll-interface is 1) include the header file, and 2) use DEFINE_WLL_FUNCTION macro. This is what the source code would look like:

src = "
#include \"wll_interface.h\"  // include the header file

double multiply(double x, int y)
{
    return x * y;
}

DEFINE_WLL_FUNCTION(multiply)  // defines wll_multiply
";

Then we create a shared library from the code. You need to replace <path-to-wll_interface.h> below with the directory that contains the header file wll_interface.h so that the compiler can find it.

Needs["CCompilerDriver`"];
mylib = CreateLibrary[src, "wll_multiply"(* name of the library, can be anything *), 
  "IncludeDirectories" -> {"<path-to-wll_interface.h>"}, 
  Language -> "C++", "CompileOptions" -> "-std=c++17"]

Finally we load the function from the shared library we just created. Note that we are loading wll_multiply—a function that is automatically generated.

multiply = LibraryFunctionLoad[mylib, "wll_multiply", {Real, Integer}, Real];

Now multiply can be used just like a normal function!

multiply[2.33, 5]

Manipulating arrays (MTensor)

The template class wll::tensor<type, rank> encapsulates LibraryLink MTensor, making operations on arrays much easier. In this example, I will show you how to pass a reference to an array through LibraryLink and sort the array.

wll::tensor has member functions begin and end to get the iterators, similar to STL containers. Declaring the type of x to be wll::tensor<double, 1>& means that this list of doubles will be passed by reference and no copy will be performed.

src = "
#include <algorithm>
#include \"wll_interface.h\"

void sort(wll::tensor<double, 1>& x)
{
    std::sort(x.begin(), x.end());
}
DEFINE_WLL_FUNCTION(sort)
";

Now we compile the library as before and load the library function wll_sort

Needs["CCompilerDriver`"];
mylib = CreateLibrary[src, "wll_sort",
  "IncludeDirectories" -> {"<path-to-wll_interface.h>"}, 
  Language -> "C++", "CompileOptions" -> "-std=c++17"]
sort = LibraryFunctionLoad[mylib, "wll_sort", {{Real, 1, "Shared"}}, "Void"];

Note that the type of argument is specified as {Real, 1, "Shared"}, where "Shared" means pass by reference here.

This library function works by directly modifies values of the variable in the Wolfram Kernel:

a = RandomReal[1., 10];
sort[a]   (* sort the array; there is no return value *)
a         (* the array has been sorted! *)

Sparse arrays (MSparseArray)

In this example, we will use wll-interface to help us generate identity matrices as sparse arrays. The template class wll::sparse_array<type, rank> corresponds to MSparseArray in LibraryLink. Its member function operator() can be used to extract the elements (wll::tensor also have this function).

src = "
#include \"wll_interface.h\"

wll::sparse_array<double, 2> sparse_identity(size_t size)
{
    wll::sparse_array<double, 2> identity({size, size});
    for (size_t i = 0; i < size; ++i)
        identity(i, i) = 1.0;
    return identity;
}
DEFINE_WLL_FUNCTION(sparse_identity)
";

Here identity(i, i) points to an element on the main diagonal. Following the convention of C/C++, indices in wll-interface are zero-based.

Compile and load the function:

Needs["CCompilerDriver`"];
mylib = CreateLibrary[src, "wll_sparse_identity",
  "IncludeDirectories" -> {"<path-to-wll_interface.h>"}, 
  Language -> "C++", "CompileOptions" -> "-std=c++17"]
identity = LibraryFunctionLoad[mylib, "wll_sparse_identity", {Integer}, LibraryDataType[SparseArray]];
identity[10]  (* gives a 10x10 idensity matrix *)
$\endgroup$
5
  • 1
    $\begingroup$ I think this is great work and will have great benefit for people who cares a lot about performance! Many times, I find it easier to write a C++ function and get better performance than to deal with all kinds of surprises in optimizing Mathematica code. I think it will be also more attractive to people, if you could add some examples about matrices and sparse array. $\endgroup$ Commented Feb 23, 2020 at 22:20
  • 1
    $\begingroup$ @xslittlegrass Thanks! I've added two examples of manipulating arrays. $\endgroup$ Commented Feb 24, 2020 at 21:46
  • $\begingroup$ When compiling "mylib = CreateLibrary[src, "wll_sparse_identity", "IncludeDirectories" -> {"<path-to-wll_interface.h>"}, Language -> "C++", "CompileOptions" -> "-std=c++17"]", I got this error message "CreateLibrary::cmperr: Compile error: C:\Temp\wll_interface.h(2012): error C2429: language feature 'structured bindings' requires compiler flag '/std:c++17'". Can you tell me why? Thanks. $\endgroup$
    – xpaul
    Commented Mar 22 at 20:13
  • $\begingroup$ @xpaul I guess you are using MSVC to compile the code--you can try specifying "CompileOptions" -> "/std:c++17". $\endgroup$ Commented Apr 11 at 3:47
  • $\begingroup$ Yes, I am using MSVC. I will try. Thank you very much! $\endgroup$
    – xpaul
    Commented Apr 11 at 21:16
16
$\begingroup$

There is a package called LTemplate that automates writing some of the boilerplate code for LibraryLink:

I consider this less effort than writing standard LibraryLink code. In this sense it is a fitting answer for this question.

However, I do recommend familiarizing yourself with the standard way of using LibraryLink before trying LTemplate.

Disclosure: I am the author of LTemplate.

$\endgroup$
14
$\begingroup$

You may want to look at this presentation: Integrating C and Mathematica.

In the past, I have found using .NET/Link to be the easiest. You can call C DLL's very easily on Windows, without the need for templates as in MathLink.

$\endgroup$
0

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.