Function Pointers

When compiling with the Island backend, is it possible for Mercury to cast raw function pointers to delegates and vice versa (create a pointer to a delegate)? I’m currently unable to test since I last used the trial almost two years ago, but I am coming back around to the platform.

Hw would this work? Under the hood, a block/delegate is a combination of a function (method) address and a self pointer that will be passed as first parameter to the method. A raw function pointer is just an address.

Can you give me a concrete example of what you are trying to solve/achieve?


For instance, in the case of using a dynamic loaded library. I would want to use the GetProcAddress method to obtain the function. I know .NET had functions to marshal function pointers to delegates and the reverse manner as well. Mainly I would be using this for interacting with libraries. I would think you can extend the Ptr class to force a plain function pointer to be obtained from a sub/function to pass to an library. Of course a function pointer should be able to be casted to a delegate.

Some googling shows that i thinkl ylu will want to use the <UnmanagedFunctionPointer(CallingConvention.???)]> attribute on your delegate declaration.

I’ve taken the code from this example Functionpointer in C# · GitHub and converted it to Mercury, and it compiles, so it should be fine. Of course I don’t have an actual native .dll handy to test with.

Imports System.Runtime.InteropServices

Module Program

  Shared Sub Main(args As String())
    Dim pDll As IntPtr = NativeMethods.LoadLibrary(”PathToYourDll.DLL”)
    ' oh dear, error handling here
    ' if (pDll == IntPtr.Zero)
    Dim pAddressOfFunctionToCall As IntPtr = NativeMethods.GetProcAddress(pDll, “MultiplyByTen”)
    ' oh dear, error handling here
    ' if(pAddressOfFunctionToCall == IntPtr.Zero)
    Dim multiplyByTen As MultiplyByTen = TryCast(Marshal.GetDelegateForFunctionPointer(pAddressOfFunctionToCall, GetType(MultiplyByTen)), MultiplyByTen)
    Dim theResult As Integer = multiplyByTen(10)
    ' remaining code here
  End Sub

  Delegate Function MultiplyByTen(numberToMultiply As Integer) As Integer

End Module

Shared Class NativeMethods

  Public Declare Function LoadLibrary(dllToLoad As String) As IntPtr

  Public Declare Function GetProcAddress(hModule As IntPtr, procedureName As String) As IntPtr

  Public Declare Function FreeLibrary(hModule As IntPtr) As Boolean

End Class

Oh wait, you’re on Island. even easier, just declare a function pointer using the <FunctionPointer> on a block/delegate declaration, and you’re good to go.

Public Delegate Function SomeFunction(Parameter As String) As Int

See FunctionPointer.

Okay that’s good to know. Now if I need to create a function pointer in Mercury to use with external code, will the standard AddressOf work?

It should, yes

I’ve been testing shared libraries and while it seems to work fine on Island/Windows, on Linux it’s continuing to throw segfaults for both debug and release mode when opening a library. Also for some reason, DLLImport or the .fx fails to load the symbol when compiling for Linux. I’ve uploaded both the library project and test console app as VS solutions. (2.1 MB) (11.7 KB)

The following is the error received when attempting to use the .fx to link the library. There’s no way to define the types without being in a namespace. Forcing the struct definitions into a similar global namespace to match the defined one in the FX also fails.

Imports asm
'Imports  RemObjects.Elements.System
Imports Library2

Namespace Global.Test

    Structure test
	     Dim x,y,z As Double
    End Structure

    Structure test2
	     Dim v(2) As test
	     Dim t As Byte
    End Structure

End Namespace

Module Program

/*<External, DllImport(""), SymbolName("testFunc")>
Sub testFunc (t As test2) 
End Sub*/

'Delegate Sub def_testFunc(t2 As test2)

  Sub Main(args as String())	
    Dim t As Test.test2 ', testFunc As def_testFunc
    Dim r As RemObjects.Elements.RTL.Random = New RemObjects.Elements.RTL.Random()
    'Dim l = Process.LoadLibrary("./")
    'testFunc = TryCast(Process.GetProcAddress(l, "testFunc"), def_testFunc)

    For i = 0 To 2 
	     t.v(i).x = r.NextDouble()
         t.v(i).y = r.NextDouble()
         t.v(i).z = r.NextDouble()
    t.t = r.NextInt()
    Dim a As Integer = Math.Abs(r.NextInt()) >> 8, b As Integer = Math.Abs(r.NextInt()) >> 8
    writeLn($"A: {a} B: {b}")
    writeLn($"Add: {testAsm(a, b)}")
  End Sub

End Module

Error||(E486) Parameter 1 is Test.test2, should be Test.test2, in call to Test.testFunc(t: Test.test2)|ConsoleApplication3|Z:\source\repos\ConsoleApplication3\ConsoleApplication3\Program.vb|44||

Here’s the working Windows project testing the Mercury compiled library in C++. (422.6 KB) (433.9 KB)

It seems that when passing the struct as a pointer to the library, it reads properly. However the segfault occurs when indexing the array. Also, the parameter was not declared as a pointer in the library code but Island/Linux seems to ignore this.

Both the projects you sent compile fine for me. what could I be missing? I’m not really set up to test on Linux.

This error occurs when referencing the .fx file that was generated for the library instead of dynamically linking the function in code.

Can you send me a testcase for that? (2.1 MB)

Note you’ll need to reference to the Library2.fx file in the respective project. Either debug or release version.

Also, the main issue of this code failing to work on Linux is related to my post This is the same error GDB is throwing regarding the GC bounds check. It doesn’t matter if I make the array reference a pointer inside the structure and pass the entire structure to the function as a pointer, the same error is thrown.

This is starting to get a bit confusing to keep track of. can we start individual new threads for separate issues, and not mix them together?

Looking at this now.

Thisnojnly only has the console projhect.

Can I get one test case that has everything together as needed in one place?

I’m not asking for this because I’m too lazy to out it together myself, but because in my experience, whenever I end up having to do this, there’s some step that was missing and I end up not reproducing the actual problem. thanx!

I will open two additional threads to work from.

1 Like