Loading the R library by hand versus using p/invoke

Oct 22, 2013 at 4:17 PM
Hey guys,

Thanks for the responses to the earlier questions. I am looking through the code a bit now to see if it might be possible to avoid the .dll issues. There is one design issue related to this in the R.NET that I don't quite understand, and was wondering what the explanation might be.

It seems that there are a number of complications due to the fact that R is manually replacing a lot of p/invoke functionality. For example, to load R and access the functions, the program

1- Loads the library loader library (dlopen)
2- Uses this to get pointers to R library (name depending on system)
3- Then uses the library to match a delegate with a function name each time, creates a function pointer, and then call that function.

This strikes me as duplicating a lot of the work already programmed in to the mono/.NET runtime. For instance, if rather than declaring function delegates that exactly match a function exposed by the library, like:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate IntPtr Rf_mkString(string s);
The function instead just directly used interop and did
[DllImport("R")]
static extern IntPtr Rf_mkString(string s)
Then the responsibility for finding this function, the marshalling, etc. would all be handled by the run time. I think this would allow removing the RDotNet.Native library entirely, avoid the issues with the having to have separate unix/windows flags, simplify how libraries are called (no delegate matching, function pointers, etc.), and perhaps be more performant as the runtime would just bind the functions once.

But am guessing there is probably something here I am missing, and am somewhat new to interop so wanted to check what the issue might be (perhaps the need to have multiple R instances running in the same process?).

Cheers,
Nigel
Oct 22, 2013 at 7:48 PM
Ah, I see at least that it can't totally be removed because of the need to set method pointers by overriding them:
         IntPtr suicidePointer = this.engine.DangerousGetHandle("ptr_R_Suicide");
         IntPtr newSuicide = Marshal.GetFunctionPointerForDelegate((ptr_R_Suicide)Suicide);
         Marshal.WriteIntPtr(suicidePointer, newSuicide);
And now I remember why I don't like C.
Oct 22, 2013 at 8:04 PM
Though it looks like it could still be loaded dynamically without two builds:

http://dimitry-i.blogspot.com/2013/01/mononet-how-to-dynamically-load-native.html

Though I think the only advantage to this is if the library could be loaded first and then referenced with DllInvoke, which would avoid doing the function look up by reflection on subsequent calls.

Will just add version to the GAC for now though.