Here's a direct translation. You could make it look a little nicer, but it's never going to look as clean as F#.

1
2
3
4
5
6
7
    var name = "Bontrager";
    var years = 15;

    var s = ExtraTopLevelOperators.PrintFormatToString(new PrintfFormat<FSharpFunc<string, FSharpFunc<int, string>>, Unit, string, string>("he rode a %s for %d years")).Invoke(name).Invoke(years);
    System.Console.WriteLine(s);
    System.Console.ReadKey(true);
By on 6/22/2011 5:53 PM ()

Hi,

What's wrong with writing this ?

1
2
3
var name = "Bontrager";
var years = 15;
System.Console.WriteLine("he rode a {0} for {1} years", name, years);

Cordially !

By on 6/22/2011 8:51 PM ()

Sehnsucht, I have 14091 strings in a C++/CLI application I maintain that use the printf format. kvb, thanks! That really helped me understand. I need to be able to pass in the format dynamically. It is possible to define functions for each of the input parameter types.

1
2
3
4
5
6
7
8
9
10
11
let sprintf_s fmt a =
  let pf = Printf.StringFormat<string -> string, string> fmt
  sprintf pf a
 
let sprintf_ss fmt a b =
  let pf = Printf.StringFormat<string -> string -> string, string> fmt
  sprintf pf a b
 
let sprintf_sd fmt a b =
  let pf = Printf.StringFormat<string -> int -> string, string> fmt
  sprintf pf a b

Type safety remains for the parameters, but the type safety on the format is gone.
I can now do:

1
2
3
4
5
6
7
let f = "he rode a %s for %d years"
let s = sprintf_sd f name years
What I really REALLY want is a way to skip the type safety and just use the underlying
printf formatter with an object array.
let sprintf_array (fmt:string) (args:obj[]) : string = "help me code this please"
let args = [| name :> obj; years :> obj |]
let s = sprintf_array f args

Any idea how to implement that?

By on 6/23/2011 10:05 AM ()

Here's an attempt at sprintf_arr:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
type Helper =
    static member MakePrintFn<'t> s =
        sprintf (Printf.StringFormat<'t>(s))

let sprintf_arr (fmt:string) (arr:_[]) =
    let rec exec (retTy:System.Type) k = function
    | 0 -> typeof<Helper>.GetMethod("MakePrintFn").MakeGenericMethod(retTy).Invoke(null, [| fmt |]) |> k
    | n -> 
        let argTy = arr.[n-1].GetType()
        let fnTy = typedefof<_->_>.MakeGenericType(argTy, retTy)
        let k' fn =
            fnTy.GetMethod("Invoke").Invoke(fn, [|arr.[n-1]|]) |> k
        exec fnTy k' (n-1)
    exec typeof<string> string (arr.Length)
By on 6/23/2011 7:55 PM ()

It sounds like doing something like pre-processing the format string from

1
    "foo %s bar %d baz"

into

1
    "foo {0} bar {1} baz"

and then using String.Format may be the most expedient (and performant) thing to do. The value of F# printf is the type safety; it you don't need that, and you want 'dynamic' format strings (F# format strings must be string literals), then I think I would just 'wash' the formats back into StringFormat style.

By on 6/23/2011 11:07 AM ()

Brian, I thought about that, but I need the formatting to behave the same as C-style like the Core.Printf module does. I don't know what the equivelant of this is using String.Format:

1
2
3
printf "number: %.4g" 411.12341234

number: 411.1

For now, I'm just going forward with defining a function for each of the input parameter type combinations. There are not too many in our code base. But if you can think of a way to implement that sprintf_array function, please do let me know.

Cheers,

Cameron

By on 6/23/2011 3:14 PM ()

I see, yeah, check out e.g. [link:msdn.microsoft.com] and try stuff like

1
System.String.Format("--{0:G5}--", 123.4567)

So yeah, it's non-trivial to convert complex format strings, I guess.

Note that the C++ printf logic may be different from the F# printf logic may be different from the String.Format logic (I dunno, maybe they do different things on NaN, for example, so be careful).

By on 6/23/2011 3:49 PM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us
Built with WebSharper