Running native code on a Mac in RhinoWIP

Dear Rhino Forum,

This is on a mac using RhinoWIP:

Trying to find a solution for loading compiled, native C/C++ code, I have turned to ctypes. In the beginning of a Python script, I enter the following

import ctypes as ct

and get an exception with a message (see below) that informs me that there was an error in libdl.so. Looking a bit closer, it seems the dlopen function fails. So, basically … no dynamic loading. I would be grateful for suggestions as to how the issue might be resolved. Is it reproducible?

Best
Andreas


Message: libdl.so

Traceback:
line 353, in init, "/Applications/RhinoWIP.app/Contents/Resources/ManagedPlugIns/RhinoDLR_Python.rhp/Lib/ctypes/init.py"
line 441, in , "/Applications/RhinoWIP.app/Contents/Resources/ManagedPlugIns/RhinoDLR_Python.rhp/Lib/ctypes/init.py"
line 9, in , “/Users/jab/Misc/src/Rhino-Python/curves.py”

Here is an example of how to call native C++ code from a RhinoCommon plug-in running on Rhino for Mac.

The sample shows how to use p/invoke to call “C” functions from a dylib.

Perhaps this technique works with IronPython?

Thanks, I think I will try using Xamarin - as you suggest - sometime soon to create a wrapper for native code.

I have concluded that for the time being, ctypes from IronPython on a Mac just does not work. I am pretty sure about that, but hope to be contradicted or maybe things will change soon.

I investigated the ctypes problem a bit more, and the exception is due to the fact that it cannot load the library (libdl.so). This can be circumvented by placing a symbolic link to the actual library in a place IronPython expects.

However, this only leads to the next problem where a function that apparently exists only on Windows is called and then the next exception occurs. Possibly this can also be fixed, but - as far as I can tell after much Googling - ctypes has not really been made to work on OS X from IronPython.

I have experimented also with a stand alone version of IronPython. The issue is not specific to the IronPython in Rhino.

Dear Forum,

Soldiering on … I realized that before calling native code, I should master calling C#.

It seems (on Mac) there is a difference between IronPython as stand alone and inside Rhino. So this Python code:


import sys
sys.path.append("/Users/jab/Misc/Xamarin/CsharpFun/CsharpFun/bin/Debug")
import clr
clr.AddReference(“CsharpFun”)
from CsharpFun import MyClass
c = MyClass()
print c.fun()


works perfectly when executed by IronPython from the command line. When invoked in RhinoWIP, I get:


Message: ‘MyClass’ object has no attribute ‘fun’

Traceback:
line 7, in , “/Users/jab/Desktop/IronPython-2.7.5/ironfun.py”


that is surprising to me. It seems that MyClass is loaded on line 6 and everything goes well, but the instance is stripped of the fun method.

Is that something I should have expected?

Cheers
Andreas

It is not something to expect. Is your .NET dll compiled as Any Cpu? That is the only thing that I can think of at the moment that would be causing problems.

The problem was banal: I simply had to restart RhinoWIP. Apparently, Rhino’s IronPython does not realize that a .net assembly has changed before it is restarted? This problem could also be in regular IronPython since that is restarted every time I try.

Now, calling native also works. I just have to call a C# function which then calls the native code.

Ah yes that make sense. IronPython and all .NET assemblies are loaded once and only once into the process.

Ok. Thanks for the help on this thread!

Calling native code from a Python script executed in RhinoWIP on a Mac seems to work quite well if you jump through the additional hoop of wrapping the native (C/C++) code in a .Net assembly that you can conveniently create using Xamarin. I just wanted to post a summary since I did not find all of these steps mentioned online.

It seems that the steps are:

  • Create a 64 bit dynamic library, say mylib, where the entry point functions that you wish to call from Rhino are declared like this:

extern “C” attribute ((visibility (“default”)))
int fun(int);

  • I use Xcode to create the dylib, but that is probably not important.

  • Create a .Net assembly which contains a class that declares this function as an extern function. Probably this must be done with Xamarin, but this IDE is free for the OS X platform at least. So go ahead and create a .Net library with something similar to this:

namespace ForRhino
{
public class Blob
{
public Blob ()
{
}
[DllImport(“mylib.dll”, CallingConvention = CallingConvention.Cdecl)]
static extern public int fun(int x);
}
}

  • Here it is important that you have a DLLMAP that maps mylib.dll to the name of the actual Mac OS X dylib. DLLMaps are well documented.

  • Now, from IronPython in Rhino it is possible to call this function fun using the clr module like shown below:

import sys
sys.path.append(“path/to/the/assembly/ForRhino/bin/Debug”)
import clr
clr.AddReference(“ForRhino”)
from ForRhino import Blob
b = Blob()
print "output ", b.fun(1)

  • One final gotcha that I encountered was the fact that it seems the dylib must be 32 bit when using the standalone IronPython and must be 64 bit when using the IronPython inside RhinoWIP.
  • as mentioned above in the thread, RhinoWIP must be restarted when recompiling.
1 Like