This question already has answers here:
How can I pass an F# delegate to a P/Invoke method expecting a function pointer?
(2 answers)
Closed 8 years ago.
I am trying to call EnumWindows from F# and got following exception:
System.Runtime.InteropServices.MarshalDirectiveException: Cannot marshal 'parameter #1': Generic types cannot be marshaled.
Code I used:
module Win32 =
open System
open System.Runtime.InteropServices
type EnumWindowsProc = delegate of (IntPtr * IntPtr) -> bool
[<DllImport("user32.dll")>]
extern bool EnumWindows(EnumWindowsProc callback, IntPtr lParam)
let EnumTopWindows() =
let callback = new EnumWindowsProc(fun (hwnd, lparam) -> true)
EnumWindows(callback, IntPtr.Zero)
module Test =
printfn "%A" (Win32.EnumTopWindows())
This is a bit subtle, but when you use parentheses in the delegate definition, you are explicitly telling the compiler to create a delegate taking tuple - and then the interop fails because it cannot handle tuples. Without parentheses, the delegate is created as ordinary .NET delegate with two parameters:
type EnumWindowsProc = delegate of IntPtr * IntPtr -> bool
Then you also have to change how you use it (because it is now treated as two-parameter function):
let EnumTopWindows() =
let callback = new EnumWindowsProc(fun hwnd lparam -> true)
EnumWindows(callback, IntPtr.Zero)
Related
I'm adding a static builder method to a record type like this:
type ThingConfig = { url: string; token : string; } with
static member FromSettings (getSetting : (string -> string)) : ThingConfig =
{
url = getSetting "apiUrl";
token = getSetting "apiToken";
}
I can call it like this:
let config = ThingConfig.FromSettings mySettingsAccessor
Now the tricky part: I'd like to add a second overloaded builder for use from C# (ignore the duplicated implementation for now):
static member FromSettings (getSetting : System.Func<string,string>) : ThingConfig =
{
url = getSetting.Invoke "apiUrl";
token = getSetting.Invoke "apiToken";
}
This works for C#, but breaks my earlier F# call with
error FS0041: A unique overload for method 'FromSettings' could not be determined based on type information prior to this program point. A type annotation may be needed. Candidates: static member ThingConfig.FromSettings : getSetting:(string -> string) -> ThingConfig, static member ThingConfig.FromSettings : getSetting:Func -> ThingConfig
Why can't F# figure out which one to call?
What would that type annotation look like? (Can I annotate the parameter type from the call site?)
Is there a better pattern for this kind of interop? (overloads accepting lambdas from both C# and F#)
Why can't F# figure out which one to call?
Overload resolution in F# is generally more limited than C#. The F# compiler will often, in the interest of safety, reject overloads that C# compiler sees as valid.
However, this specific case is a genuine ambiguity. In the interest of .NET interop, F# compiler has a special provision for lambda expressions: regularly, a lambda expression will be compiled to an F# function, but if the expected type is known to be Func<_,_>, the compiler will convert the lambda to a .NET delegate. This allows us to use .NET APIs built on higher-order functions, such as IEnumerable<_> (aka LINQ), without manually converting every single lambda.
So in your case, the compiler is genuinely confused: did you mean to keep the lambda expression as an F# function and call your F# overload, or did you mean to convert it to Func<_,_> and call the C# overload?
What would the type annotation look like?
To help the compiler out, you can explicitly state the type of the lambda expression to be string -> string, like so:
let cfg = ThingConfig.FromSettings( (fun s -> foo) : string -> string )
A slightly nicer approach would be to define the function outside of the FromSettings call:
let getSetting s = foo
let cfg = ThingConfig.FromSettings( getSetting )
This works fine, because automatic conversion to Func<_,_> only applies to lambda expressions written inline. The compiler will not convert just any function to a .NET delegate. Therefore, declaring getSetting outside of the FromSettings call makes its type unambiguously string -> string, and the overload resolution works.
EDIT: it turns out that the above no longer actually works. The current F# compiler will convert any function to a .NET delegate automatically, so even specifying the type as string -> string doesn't remove the ambiguity. Read on for other options.
Speaking of type annotations - you can choose the other overload in a similar way:
let cfg = ThingConfig.FromSettings( (fun s -> foo) : Func<_,_> )
Or using the Func constructor:
let cfg = ThingConfig.FromSettings( Func<_,_>(fun s -> foo) )
In both cases, the compiler knows that the type of the parameter is Func<_,_>, and so can choose the overload.
Is there a better pattern?
Overloads are generally bad. They, to some extent, obscure what is happening, making for programs that are harder to debug. I've lost count of bugs where C# overload resolution was picking IEnumerable instead of IQueryable, thus pulling the whole database to the .NET side.
What I usually do in these cases, I declare two methods with different names, then use CompiledNameAttribute to give them alternative names when viewed from C#. For example:
type ThingConfig = ...
[<CompiledName "FromSettingsFSharp">]
static member FromSettings (getSetting : (string -> string)) = ...
[<CompiledName "FromSettings">]
static member FromSettingsCSharp (getSetting : Func<string, string>) = ...
This way, the F# code will see two methods, FromSettings and FromSettingsCSharp, while C# code will see the same two methods, but named FromSettingsFSharp and FromSettings respectively. The intellisense experience will be a bit ugly (yet easily understandable!), but the finished code will look exactly the same in both languages.
Easier alternative: idiomatic naming
In F#, it is idiomatic to name functions with first character in the lower case. See the standard library for examples - Seq.empty, String.concat, etc. So what I would actually do in your situation, I would create two methods, one for F# named fromSettings, the other for C# named FromSettings:
type ThingConfig = ...
static member fromSettings (getSetting : string -> string) =
...
static member FromSettings (getSetting : Func<string,string>) =
ThingConfig.fromSettings getSetting.Invoke
(note also that the second method can be implemented in terms of the first one; you don't have to copy&paste the implementation)
Overload resolution is buggy in F#.
I filed already some cases, like this where it is obviously contradicting the spec.
As a workaround you can define the C# overload as an extension method:
module A =
type ThingConfig = { url: string; token : string; } with
static member FromSettings (getSetting : (string -> string)) : ThingConfig =
printfn "F#ish"
{
url = getSetting "apiUrl";
token = getSetting "apiToken";
}
module B =
open A
type ThingConfig with
static member FromSettings (getSetting : System.Func<string,string>) : ThingConfig =
printfn "C#ish"
{
url = getSetting.Invoke "apiUrl";
token = getSetting.Invoke "apiToken";
}
open A
open B
let mySettingsAccessor = fun (x:string) -> x
let mySettingsAccessorAsFunc = System.Func<_,_> (fun (x:string) -> x)
let configA = ThingConfig.FromSettings mySettingsAccessor // prints F#ish
let configB = ThingConfig.FromSettings mySettingsAccessorAsFunc // prints C#ish
I have a native C library and I want do some F# coding with it. The thing is I get exception:
System.TypeLoadException: Cannot marshal field 'log' of type
'LoggingModel': There is no marshaling support for this type.
at
System.StubHelpers.ValueClassMarshaler.ConvertToNative(IntPtr dst,
IntPtr src, IntPtr pMT, CleanupWorkList& pCleanupWorkList)
at
FSI_0009.Initialize(ComponentOverrideFlags flags, LoggingModel&
loggingModel, ThreadingModel& threadingModel, SchedulingModel&
schedulingModel, IntPtr memoryModel)
at
.$FSI_0011.main#()
in
D:\dev_p\f#\FunBindings\FunExample\Environment.fs:line 16 Stopped due
to error
Here the code:
module Interop
[<CLSCompliant(true); Flags>]
type LogTarget =
| None = 0
| Console = 1
| Trace = 2
| Custom = 4
[<UnmanagedFunctionPointer(CallingConvention.Cdecl)>]
type LogCallback = delegate of LogTarget * string * string * nativeint -> unit
[<UnmanagedFunctionPointer(CallingConvention.Cdecl)>]
type ReleaseCallback = delegate of nativeint -> unit
[<Struct>]
type LoggingModel =
val mutable targets : LogTarget
val mutable log : LogCallback
val mutable deleteModel : ReleaseCallback
val mutable userparam : IntPtr
[<DllImport("CLIBRARY.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "txInitialize")>]
[<MethodImpl(MethodImplOptions.ForwardRef)>]
extern int Initialize(ComponentOverrideFlags flags, LoggingModel& loggingModel, ThreadingModel& threadingModel, SchedulingModel& schedulingModel, IntPtr memoryModel)
module Environment
let initialize =
let mutable loggingModel = new LoggingModel()
let mutable threadingModel = new ThreadingModel()
let mutable schedulingModel = new SchedulingModel()
Initialize(ComponentOverrideFlags.None, &loggingModel, &threadingModel, &schedulingModel, IntPtr.Zero)
Basically, I get the aforementioned error when I try to execute "initialize" function in interactive.
I would really appreciate any help.
Update: I've checked the code a bit more and noticed that outside of the interactive console it seems to be working, without failing with exceptions. I need to provide a bit more coverage for CLibrary to be sure. Meanwhile, if there anybody who knows what could cause this exception and how it could be prevented, I would really appreciate the answer.
I think the problem is that delegate of LogTarget * string * string * nativeint -> unit declares a delegate where the arguments are curried. (This doesn't really make sense to me either since a * b normally represents a tuple.)
The subtly different delegate of (LogTarget * string * string * nativeint) -> unit declares a delegate with tupled arguments which would be compatible with a native function.
You can see this difference if you try and assign a .NET method to two different delegate types:
type Curried = delegate of int * int -> int
type Tupled = delegate of (int * int) -> int
//let a = new Curried (Math.Max) // doesn't compile
let b = new Tupled (Math.Max) // works
Have you tried adding [<MarshalAsAttribute(UnmanagedType.FunctionPtr)>] to LoggingModel?
[<Struct>]
type LoggingModel =
val mutable targets : LogTarget
[<MarshalAsAttribute(UnmanagedType.FunctionPtr)>]
val mutable log : LogCallback
[<MarshalAsAttribute(UnmanagedType.FunctionPtr)>]
val mutable deleteModel : ReleaseCallback
val mutable userparam : IntPtr
IL code without this attribute is:
// Fields
.field public class Interop.LogCallback log
but with this attribute is:
// Fields
.field public marshal(Func) class Interop.LogCallback log
Without marshal(Func)/MarshalAs attribute the delegate cannot be marshalled even with the UnmanagedFunctionPointer attribute. Cannot test it with a native library though.
I am porting a VB.NET application to F# as an experiment. The VB program uses SQLDriverConnect, so I need to call it from F#. I cannot get the pinvoke/extern declaration to work properly. The call to SQLDriver connect always returns -2, SQL_INVALID_HANDLE, instead of prompting for a connection as expected.
Anybody know how to get this to work?
open System
open System.Runtime.InteropServices
open System.Text
[<DllImport("odbc32.dll")>]
extern Int16 SQLAllocEnv(IntPtr& EnvironmentHandle);
[<DllImport("odbc32.dll")>]
extern Int16 SQLDriverConnect(IntPtr hdbc, IntPtr hwnd, string szConnStrIn,
Int16 cbConnStrIn, StringBuilder szConnStrOut,
Int16 cbConnStrOutMax, Int16& pcbConnStrOut,
UInt16 fDriverCompletion)
let getConnectionString () =
let SQL_DRIVER_PROMPT = 2us
let mutable henv = IntPtr(0)
let mutable csLen = 0s
let rc1 = SQLAllocEnv &henv
assert (rc1 = 0s)
let csOut = new StringBuilder(1024)
let rc2 = SQLDriverConnect(henv, IntPtr.Zero, "", 0s, csOut, 1024s, &csLen, SQL_DRIVER_PROMPT)
assert (rc2 = 0s)
csOut.ToString()
[<EntryPoint>]
let main argv =
printfn "Connection string: %s" (getConnectionString())
0 // return an integer exit code
I don't do F# but in C you call SQLAllocEnv (or SQLAllocHandle) to create an environment handle, then you call SQLSetEnvAttr to set the version of ODBC you want, then SQLAllocConnect (or SQLAllocHandle) to allocate a connection handle and lastly call SQLDriverConnect with the connection handle. Your code looks to be passing an environment handle to SQLDriverConnect but SQLDriverConnect needs a connection handle hence SQL_INVALID_HANDLE.
This question already has answers here:
Call F# code from C#
(4 answers)
Closed 8 years ago.
I am SQL developer and am really new to both F# and C#. I need help on how to pass a string to f# function below and to return the result from F# to C#.
Description of project:
I am using stanford postagger to tag a sentence with the parts of speech.
Reference link from where i copied this code.
(http://sergey-tihon.github.io/Stanford.NLP.NET/StanfordPOSTagger.html)
module File1
open java.io
open java.util
open edu.stanford.nlp.ling
open edu.stanford.nlp.tagger.maxent
// Path to the folder with models
let modelsDirectry =
__SOURCE_DIRECTORY__ + #'..\stanford-postagger-2013-06-20\models\'
// Loading POS Tagger
let tagger = MaxentTagger(modelsDirectry + 'wsj-0-18-bidirectional-nodistsim.tagger')
let tagTexrFromReader (reader:Reader) =
let sentances = MaxentTagger.tokenizeText(reader).toArray()
sentances |> Seq.iter (fun sentence ->
let taggedSentence = tagger.tagSentence(sentence :?> ArrayList)
printfn "%O" (Sentence.listToString(taggedSentence, false))
)
// Text for tagging
let text = System.Console.ReadLine();
tagTexrFromReader <| new StringReader(text)
it won't matter if C# or F# - do make a function that gets a string and returns ... let
s say an int, you just need something like this (put it in some MyModule.fs):
namespace MyNamespace
module MyModule =
// this is your function with one argument (a string named input) and result of int
let myFun (input : string) : int =
// do whatever you have to
5 // the value of the last line will be your result - in this case a integer 5
call it in from C#/.net with
int result = MyNamespace.MyModule.myFun ("Hallo");
I hope this helps you out a bit
For your example this would be:
let myFun (text : string) =
use reader = new StringReader(text)
tagTexrFromReader reader
as you'll have this in the module File1 you can just call it with var res = Fiel1.myFun(text);
BTW: use is in there because StringReader is IDisposable and using use F# will dispose the object when you exit the scope.
PS: is tagTexrFromReader a typo?
I'm trying to pass delegate to outer managed API
the delegate function is:
type HookProc = delegate of int * nativeint * nativeint -> nativeint
function for delegate:
let HookCallback(nCode:int,wParam:System.IntPtr,lParam:System.IntPtr) =
let t = (int)wParam
if t = WM_KEYUP then
let vkCode:int = Marshal.ReadInt32(lParam)
printfn "%A The Pressed key code is : " vkCode
CallNextHookEx(_hookID, nCode, wParam, lParam)
the problem I had that when i'm creating delegate
let HookProcF = new HookProc(HookCallback)
get this error
Error 1 Type mismatch. Expecting a int -> nativeint -> nativeint -> nativeint
but given a int * System.IntPtr * nativeint -> System.IntPtr
The type 'int' does not match the type 'int * System.IntPtr * nativeint'
I have asked question related
here
The answer is in the error message - when it says int <> int->nativeint ....
You need to use the curried form for the function like so
let HookCallback (nCode:int) (wParam:System.IntPtr) (lParam:System.IntPtr) =