[…] returns a string containing the result of formatting the arguments.
But how can that function know how to format the arguments if it is not given a formatter argument? Indeed, it needs to know the margins (and current state) of the formatter where it would output the string in order to format it accordingly.
To give a small example:
let () = Format.set_margin 3;;
let () = Format.printf "a@ a@ a";;
let () = let s = Format.sprintf "a@ a@ a" in Format.printf "%s" s;;
outputs two different answers: in the last line, Format.sprintf does not “know” that I will want to use that string s to print in a formatter with margin 3, so it does not break the line.
What can I do to obtain a string that is formatted as if it were to be printed in a given formatter?
In my usecase, I need to obtain such a string because I need to “post-process” it before effectively printing it in my formatter. Is it a case where I have to use Format.str_formatter (or Format.formatter_of_buffer with another buffer than Format.stdbuf)? There are also some related comments in the documentation for Format.dprintf, but since that function does not produce a string, I think that does not answer my particular problem.
Side remark: should the documentation of Format.(a)sprintf be made more precise by writing something like
[…] returns a string containing the result of formatting the arguments as if it were output on [std_formatter] in a fresh state.
Format.asprintf (the non-deprecated version of Format.sprintf) uses the str_formatter as a formatter. Thus, what you wanted to write was
let () =
Format.pp_set_geometry ~margin:3 ~max_indent:2 Format.str_formatter;
let s = Format.asprintf "a@ a@ a" in Format.printf "%s" s
Note however that using a string as a partially formatted message is generally a bad idea because rendering to string removes too much contextual information.
If you don’t need to inspect the contents of the string Format.dprintf is generally a better choice. Otherwise, you may need to post-process either the format string or use an intermediary representation like format-doc.
Format.sprintf appears not to be deprecated as of 5.4, see OCaml library : Format (although it looks indeed completely subsumed by Format.asprintf; same with Format.k(a)sprintf).
Exactly what I needed, thanks! That sounds obvious once you know it, but that is exactly what is missing from OCaml library : Format.
Well, I should have checked the solution before replying: at least on OCaml 4.14.2 toplevel, it does not seem to work:
$ ocaml
OCaml version 4.14.2
[...]
# let () =
Format.pp_set_geometry ~margin:3 ~max_indent:2 Format.str_formatter;
let s = Format.asprintf "a@ a@ a@ a@ a" in Format.printf "%s@." s;;
a a a a
a
# let _ =
Format.pp_set_geometry ~margin:3 ~max_indent:2 Format.str_formatter;
let s = Format.asprintf "a@ a@ a@ a@ a" in s;;
- : string = "a a a a\na"
#
Unless I’m misunderstanding, the intent of the above pp_set_geometry is that s would have a \n after the second a?
You are right, the str_formatter is not connected to the sprintf function, I should have checked.
It is thus probably better to create your own asprintf function:
let asprintf' ~margin ~max_indent fmt =
let b = Buffer.create 17 in
let ppf = Format.formatter_of_buffer b in
Format.pp_set_geometry ~margin ~max_indent ppf;
Format.kfprintf (fun ppf ->
Format.pp_print_flush ppf ();
Buffer.contents b
) ppf fmt