8
$\begingroup$

I have a dynamic library which I want to load into Mathematica using LibraryLink. I don't have access to the source code of the library, so I need a C wrapper to do the job. In the C wrapper, I would recieve arguments from Mathematica using the MArgument_get* functions and call my library function to do the computation. Then, the results would be sent back to Mathematica using the MArgument_set* functions.

Since the data type I receive from Mathematica is a Wolfram Library type such as mint, double, MTensor, etc., and the data types my external library function expects to receive are standard C types like int or float, my question is: how do we convert between these two different data types in the wrapper so that Mathematica and my external library functions can understand each other?

Update:

Here is an very simple example, following example here.

I have defined a external function which add two vectors, in fortran:

!addvec.f90

subroutine addvec(a,b)
implicit none
integer,parameter::N=3   
integer a(N),b(N)
a(:)=a(:)+b(:)
return
end subroutine addvec

The C wrapper is

//wrapper.cc

#include "WolframLibrary.h"
#include "WolframCompileLibrary.h"

DLLEXPORT mint WolframLibrary_getVersion(){
  return WolframLibraryVersion;
}
DLLEXPORT int WolframLibrary_initialize(WolframLibraryData libData){
  return 0;
}

extern "C" {
  void addvec_(int a[], int b[]);
} 

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

  MTensor ta;
  MTensor tb;

  ta=MArgument_getMTensor(Args[0]);
  tb=MArgument_getMTensor(Args[1]);

  addvec_((int *)((*ta).data),(int *)((*tb).data));

  MArgument_setMTensor(Res,ta);
  return LIBRARY_NO_ERROR;  
}

Mathematica code

Needs["CCompilerDriver`"]
CreateLibrary[{"wrapper.cc", "addvec.o"}, "myadd", "Debug" -> True, "TargetDirectory" -> "."]
addvec = LibraryFunctionLoad["./myadd", "addvec", {{Integer, 1}, {Integer, 1}}, {Integer, 1}]
addvec[{1, 2, 3}, {4, 5, 6}]

The Mathematica Kernel will crash soon after execute the third line.

Update 2

In Mathematica version 9 the above code seems partially work:

addvec[{1, 2, 3}, {4, 5, 6}]
(*{5, 7, 3}*)
addvec[{1, 2, 3}, {4, 5, 6}]
(*{6, 8, 4}*)

The third number in the list is calculated wrong.

$\endgroup$
1
  • $\begingroup$ The third number is calculated incorrectly because your integers in Fortran are probably only 4 bytes. Change them to 8 byte integers. $\endgroup$
    – bdforbes
    Dec 29, 2014 at 4:15

2 Answers 2

4
$\begingroup$

The data types defined for LibraryLink are just simple typedefs for standard data types.

--> see WolframLibrary.h

typedef int mint; /* 32-bit architecture */
typedef long long mint; /* 64-bit architecture */

typedef double mreal;

The MArgument_setter and MArgument_getter are just simple #defines for accessing the union MArgument:

typedef union {
    mbool *boolean;
    mint *integer;
    mreal *real;
    mcomplex *cmplex;
    MTensor *tensor;
    char **utf8string;
} MArgument;

for instance MArgument_getInteger:

#define MArgument_getInteger(marg) (*((marg).integer))

You can use the LibraryLink data types as you would use the standard ones.

Since, if you call a function which expects an integer and you assign an mint as an argument it's just a typedef for that standard type (this is therefore not introducing a new type, it's just another name for exactly the same type).

The MTensor data type is somewhat more complex, but if you look at it is again just a typedef for some data, its properties and its length.

--> see WolframCompileLibrary.h

struct M_TENSOR_STRUCT
{
    void *data;
    TensorProperty properties;
    mint flattened_length;
};

TensorProperty is again a structure with definitions for the precision, its dimension etc. Luckily you don't have to deal with this structure directly because there are again #defines in order to access the MTensor structure data type.

Now let's deal with the mreal data-type.

As I've mentioned above that mreal is just a simple typedef for the standard data-type double, converting from double to float is somewhat a mystery. Why is that ok?

float and double don't store decimal places. They store binary places: float is (assuming IEEE 754) 24 significant bits and double is 53 significant bits.

So. Converting from double to float will give you the closest possible float.

The point why this is ok is, floating point numbers are assumed to be 'double' in C unless they have a suffix (F).

Hope that helps.

$\endgroup$
17
  • $\begingroup$ Thanks for the answer, but I still can't get it to work. Could you be more specific about the data type conversion? For example, I have a MTensor ta which contains a one dimensional array of mint, how can I convert it to a which is a int array? I tried something like a=(*ta).data but it dones't work. $\endgroup$ Apr 5, 2013 at 15:02
  • $\begingroup$ i don't get it. is ta now a pointer to a MTensor struct or not? what is wrong with a = ta.data? (as i said before, there is no type conversion needed. the types are identical.) $\endgroup$
    – Stefan
    Apr 5, 2013 at 16:07
  • $\begingroup$ when I do this, I get compile error: MMA.cc: In function 'int add(st_WolframLibraryData*, mint, MArgument*, MArgument)': MMA.cc:35: error: invalid use of incomplete type 'struct M_TENSOR_STRUCT' WolframLibrary.h:46: error: forward declaration of 'struct M_TENSOR_STRUCT' MMA.cc:35: error: invalid use of incomplete type 'struct M_TENSOR_STRUCT' WolframLibrary.h:46: error: forward declaration of 'struct M_TENSOR_STRUCT' Beside data is a void* type, is it OK to assign it to int [] type? Thanks $\endgroup$ Apr 5, 2013 at 16:20
  • $\begingroup$ ok. at that it'd be better you post your actual code. it's obvious that you declared your MTensor struct but did not initialized it. $\endgroup$
    – Stefan
    Apr 5, 2013 at 18:04
  • 1
    $\begingroup$ The struct M_TENSOR_STRUCT error is because I forgot to include the "WolframCompileLibrary.h" file. After fix that the compilation is OK. But I still can't get the answer right. I've add the code I use to the questions. Could you have a look? Thank you very much. $\endgroup$ Apr 5, 2013 at 19:25
6
$\begingroup$

There are a lot of def functions in the header file "WolframCompileLibrary.h", which make the type conversion very easy and straight forward. I'm using Mathematica version 8.

For example, the following functions can be used to get data from the MTensor variables, they will return pointers to the basic m types such as mint, mreal, or mcomplex from the MTensor variables. After get the basic m types, you can directly use them as the C standard types, however corresponding size must be carefully considered.

MTensor_getDimensionsMacro(mt)          //return mint* dimension
MTensor_getIntegerDataMacro(mt)         //return mint* data
MTensor_getRealDataMacro(mt)            //return mreal* data
MTensor_getComplexDataMacro(mt)         //return mcomplex* data

Here is a simple example modified from the question:

External Subroutine

!addvec.f90

subroutine addVec(a,b,n)
implicit none
integer n
integer,dimension(n)::a,b

a(:)=a(:)+b(:)

return
end subroutine addVec

C Wrapper

//MMA.cc

#include "WolframLibrary.h"
#include "WolframCompileLibrary.h" 

DLLEXPORT mint WolframLibrary_getVersion(){
  return WolframLibraryVersion;
}
DLLEXPORT int WolframLibrary_initialize(WolframLibraryData libData){
  return 0;
}
extern "C" {
  void addvec_(mint a[], mint b[], mint* n);
} 


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

  MTensor ta;
  MTensor tb;

  ta=MArgument_getMTensor(Args[0]);
  tb=MArgument_getMTensor(Args[1]); 
  addvec_(MTensor_getIntegerDataMacro(ta),MTensor_getIntegerDataMacro(tb),MTensor_getDimensionsMacro(ta));

  MArgument_setMTensor(Res,ta);
  return LIBRARY_NO_ERROR;
}

Mathematica

Needs["CCompilerDriver`"]
CreateLibrary[{"MMA.cc", "addvec.o"}, "myadd", "Debug" -> True, "TargetDirectory" -> "."]
addVec = LibraryFunctionLoad["./myadd", "addvec", {{Integer, 1}, {Integer, 1}}, {Integer, 1}]

addVec[{1, 2, 3}, {5, 6, 7}]
addVec[{1, 2, 3, 2}, {4, 2, 3, 5}]
(*{6, 8, 10}*)
(*{5, 4, 6, 7}*)
$\endgroup$

Your Answer

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

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