I want to add debug printing to my project with a function having a type signature something like:
bool -> Printf.TextWriterFormat<'a> -> 'a
i.e. it should take a bool indicating whether or not we are in verbose mode, and use that to take the decision about whether to print or not.
For example, lets say dprint : bool -> Printf.TextWriterFormat<'a> -> 'a
then I would like this behaviour:
> dprint true "Hello I'm %d" 52;;
Hello I'm 52
val it : unit = ()
> dprint false "Hello I'm %d" 52;;
val it : unit = ()
The idea is that a command line flag can be used to avoid control this output. I also want to avoid a runtime cost in the "not verbose" case. It is possible to define a function that works like this using kprintf
:
let dprint (v: bool) (fmt: Printf.StringFormat<'a,unit>) =
let printVerbose (s: string) =
if v then System.Console.WriteLine(s)
fmt |> Printf.kprintf printVerbose
but printing/ignoring a sequence of numbers with List.iter (dprint b "%A") [1..10000]
(b \in {true,false}) takes amount 1.5s for both values of b on my machine.
I came up with another method using reflection that builds an appropriately typed function to discard the formatting arguments:
let dprint (v: bool) (fmt: Printf.TextWriterFormat<'a>) : 'a =
let rec mkKn (ty: System.Type) =
if FSharpType.IsFunction(ty) then
let _, ran = FSharpType.GetFunctionElements(ty)
FSharpValue.MakeFunction(ty,(fun _ -> mkKn ran))
else
box ()
if v then
printfn fmt
else
unbox<'a> (mkKn typeof<'a>)
but here the reflection seems too expensive (even more so than that done inside the standard libraries complicated definition of printf
sometimes).
I don't want to litter my code with things like:
if !Options.verbose then
printfn "Debug important value: %A" bigObject5
or closures:
dprint (fun () -> printfn "Debug important value: %A" bigObject5)
so, are there any other solutions?
The log allows our developers to inspect the internal workings of the PaperCut application and pinpoint the cause of problems. Enabling debug logging is only usually required or requested by the PaperCut Support team. The Print Provider is the component in the PaperCut n-tiered architecture that interfaces with the print queues.
How to debug printing problems. If you are experiencing a problem with printing, please take a look at the common bugs page before filing a bug. If the problem you are seeing is not listed there or none of the workarounds seem to help, please consider filing a bug report to help us make Fedora run better on your hardware.
Attach all created files to the bugzilla ticket if needed. When a print job is processed it is sent through a chain of filters to convert the file into a format the printer can understand, and then finally sent to a backend, a program which can transport the data to the printer.
So for getting debug logs there is a skeleton: where <backend> is the name of backend which supports your device in capitals (f.e. PIXMA, AIRSCAN, GENESYS, HPAIO). You can find which backend supports your device here.
I like your solution using reflection. How about caching it on the type level so that you pay the price of reflection only once per type? For example:
let rec mkKn (ty: System.Type) =
if Reflection.FSharpType.IsFunction(ty) then
let _, ran = Reflection.FSharpType.GetFunctionElements(ty)
// NOTICE: do not delay `mkKn` invocation until runtime
let f = mkKn ran
Reflection.FSharpValue.MakeFunction(ty, fun _ -> f)
else
box ()
[<Sealed>]
type Format<'T> private () =
static let instance : 'T =
unbox (mkKn typeof<'T>)
static member Instance = instance
let inline dprint verbose args =
if verbose then
printfn args
else
Format<_>.Instance
A pragmatist would just use the fast C# formatted printing machinery instead of this. I avoid Printf
functions in production code because of the overhead they have, as you point out. But then F# printing definitely feels nicer to use.
My #time
results for List.iter (dprint false "%A") [1..10000]
:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With