9
$\begingroup$

I am trying to wrap a sophisticated C/C++ API in Mathematica using WSTP, but can't work out how to return a pointer to a C/C++ data structure. This object was created on the C/C++ side of the wrapper, and I want to be able to pass it back to the API as a parameter later on.

I know how to wrap basic functions that return simple Mathematica objects like numbers and strings. In a perfect world, I would simply convert C++ objects into Mathematica expressions in my wrapper code. But this API's state cannot be represented as Mathematica expressions, so the objects need to remain on the C/C++ side. I need some way to wrap this state in a Mathematica expression. In other words, I would like to treat these structures as Abstract Data Types.

To make my question concrete, consider a silly example. Suppose I have an API written in C for controlling battle robots, and that I want to use Mathematica to guide a robot army to victory over the bad guys. Let's also assume that the RobotLink structures are complicated and must remain in memory on the C side, and that there are multiple robots, so a singleton object won't do.

typedef struct { ... } RobotLink;
RobotLink *CreateRobotLink(int robotId);
void MarchTo(RobotLink *robot, char **targetLocation);
void ShootAt(RobotLink *robot, char **target);
void FreeRobotLink(RobotLink *robot);

How can I wrap this API to appear to Mathematica users as follows?

Needs["RobotCommsAPI`"]
robotLinks = createRobotLink /@ {1,2,3};
marchTo(#, "battle ground") & /@ robotLinks;
shootAt(#, "bad guys") & /@ robotLinks;
freeRobotLink /@ robotLinks;

Also, is there a way to have Mathematica call freeRobotLink automatically, when the object is no longer needed?

Thanks very much in advance.

$\endgroup$
  • $\begingroup$ If you can use LibraryLink instead of MathLink, first read up on "managed library expressions" (available in Mathematica 10+, see documentation). Then to simplify working with them you may also be interested in this package I wrote: mathematica.stackexchange.com/a/96128/12 $\endgroup$ – Szabolcs Dec 12 '15 at 12:41
  • $\begingroup$ @Szabolcs - Thanks for the link. $\endgroup$ – kuperov Dec 20 '15 at 1:24
2
$\begingroup$

You can use LibraryLink instead of MathLink/WSTP and use Managed Library Expressions. This will solve all the problems you describe.

You can have an integer handle to a data structure that was allocated in C (and is never converted to a Mathematica expressions).

When the handle is no longer referenced in Mathematica, the C-side data structure will be automatically freed using the function you provide during library setup.

See the demo_managed.cxx example that comes with Mathematica.


For examples of Mathematica packages that use managed library expressions, see TriangleLink and TetGenLink. Both ship with Mathematica.


The LTemplate package was made to simplify working with Managed Library Expressions, but it requires using (basic) C++ instead of C.

Setting up everything for managed library expressions is boring, and takes quite a bit of time. For example, you need to create a data structure which maps arbitrary integer IDs to objects, you need to provide creation and freeing functions that interoperate with your data structure, etc. LTemplate will do all of this for you, and makes it possible to set up a library literally in under 2 minutes.

Your example would look like this:

class RobotLink {
    // your data here
public:
    RobotLink() { /* set up a link, no need to manage ID manually */ }
    ~RobotLink() { /* free link */ }

    // just use member functions, don't take ID as parameter manually
    void marchTo(const char *targetLocation) { /* ... */ }
    void shootAt(const char *target) { /* ... */ }
};

Then in Mathematica, specify an interface:

template = LClass["RobotLink",
   {
    LFun["moveTo", {"UTF8String"}, "Void"],
    LFun["shootAt", {"UTF8String"}, "Void"]
    }
   ];

Compile and load with CompileTemplate and LoadTemplate.

Then create a RobotLink (or as many as you wish) using

robots = Table[Make[RobotLink], {3}]
(* {RobotLink[1], RobotLink[2], RobotLink[3]} *)

#@"marchTo"["battle ground"] & /@ robots
#@"shootAt"["bad guys"] & /@ robots

robot = First[robots]

robot@"marchTo"["home"]

There is no need to free the RobotLinks manually. As soon as there is no reference to them (e.g. Clear[robots, robot] and assuming Out didn't keep a reference) they will be freed. A typical usage pattern is

Module[{robot = Make[RobotLink]},
    robot@"doSomething"[]
]

where robot is auto-freed at the exit of the Module.

Of course you can free the object manually, if you judge that there is a need to do so. You would need to use the LibraryLink API for this, as at this moment LTemplate doesn't automatically expose this functionality to Mathematica (but there's an example in the documentation showing how to do it).

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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