1

After installing Fsharp and Mono in Ubuntu server 14.04, I am trying to play around with NCurses. I let myself get inspired by several C# projects. And I got stuck on the following issue.

The printw function in /usr/include/ncurses.h is defined as follows:

extern NCURSES_EXPORT(int) printw (const char *,...)

The goal is that I can call this with:

printw("Hello world")

So how can I create an extern binding for this in fsharp? Note that it is the "..." which gives me the problems..

I already tried some stuff which did not work:

[<DllImport ("libncurses.so.5")>]
extern int printw(string s, [<ParamArray>] obj[] args)

It compiles, but a test results in:

bindings.printw("Hello world!")
----------------^^^^^^^^^^^^^^

/home/12345/fcurses/stdin(11,17): error FS0001: This expression was expected to have type
    string * obj []
but here has type
    string

According to section Parameter Arrays, only methods can have parameter arrays. And if I split the extern into a static extern with an accompaning method (inspired by the answer of this post), then extern is still used as a value, rather than a method, so no parameter array here as well.

Any ideas?

Community
  • 1
  • 1
Frank Joppe
  • 141
  • 7

1 Answers1

0

One admittedly ugly way of doing it is to create a binding for every combination of parameters you intend to pass into the function. For example, with printf, you could do:

open System
open System.Runtime.InteropServices

[<DllImport ("msvcrt40.dll", EntryPoint = "printf")>]
extern Int32 printfInt32 (String format, Int32 arg1)

[<DllImport ("msvcrt40.dll", EntryPoint = "printf")>]
extern Int32 printfString (String format, [<MarshalAs (UnmanagedType.LPStr)>] String arg1)

[<DllImport ("msvcrt40.dll", EntryPoint = "printf")>]
extern Int32 printfInt32String (String format, Int32 arg1, [<MarshalAs (UnmanagedType.LPStr)>] String arg2)


printfInt32 ("%d\n", 42) |> ignore
printfString ("%s\n", "Testing, testing...") |> ignore
printfInt32String ("Number is %d and string is \"%s\"!", 167, "SPARTA") |> ignore

So in your case it could look like (untested):

[<DllImport ("libncurses.so.5", EntryPoint = "printw")>]
extern Int32 printwInt32(String format, Int32 arg1)

printwInt32 ("%d", 102) |> ignore

A real solution would use the mkrefany IL instruction (see III.4.19 mkrefany), which is not supported by the F# compiler* and only supported via a hidden keyword in the C# compiler.

More information on Bart de Smet's blog.

* you might be able to use inline IL code to get the F# compiler to emit mkrefany, somehow...

Community
  • 1
  • 1
Nikon the Third
  • 2,665
  • 19
  • 33
  • I can confirm that the printwInt32 example above does work. Seems like getting the "..." bound into fsharp is a project in itself. Thanks for the answer though. I'll probably go for mapping it only on the first parameter, and fill that using the fsharp function "sprintf". Or CSharp. But that does not solve this technical issue, which you get back when you want to map the "scanf" function from ncurses, which has about the same signature. Anyway, for those reading this. Solving this problem with fsharp quotations also did not work, because it seems that Expr.Call checks the argument count. – Frank Joppe Mar 10 '15 at 21:25