Forcing format_to_n to use terminating zero

Viewed 526

Beside most common (format) function C++20 also comes with format_to_n that takes output iterator and count.

What I am looking for is the way to make sure that in case I ran out of space that my string is still zero terminated. For example I want the following program to output 4 instead of 42.

#include<string>
#include<iostream>
#define FMT_HEADER_ONLY 
#include <fmt/format.h>
void f(char* in){
    fmt::format_to_n(in, 2,"{}{}", 42,'\0');
    std::cout << in;
}

int main(){
    char arr[]= "ABI";
    f(arr);
}

Is this possible without me manually doing the comparison of number of written chars and max len I provided to function?

If you are wondering why I use '\0' as an argument:

I have no idea how to put terminating char in format string.

note: I know that for one argument I can specify max len with :. but I would like a solution that works for multiple arguments.

2 Answers

format_to_n returns a result. You can use that struct:

void f(char* in){
    auto [out, size] = fmt::format_to_n(in, 2, "{}", 42);
    *out = '\0';
    std::cout << in;
}

Note that this might write "42\0" into in, so adjust your capacity as appropriate (2 for a buffer of size 3 is correct).

format_to_n returns a struct containing, among other things, the iterator past the last character written. So it's quite easy to simply check the difference between that iterator and the original iterator against the maximum number of characters, and insert a \0 where appropriate:

void f(char* in)
{
  const max_chars = 2;
  auto fmt_ret = fmt::format_to_n(in, max_chars,"{}", 42);
  char *last = fmt_ret.out;
  if(last - in == max_chars)
    --last;
  *last = '\0';

  std::cout << in;
}

Note that this assumes that the array only holds exactly the number of characters (including the NUL terminator) as the number you attempted to pass to format_to_n. The above code will therefore overwrite the last character written with a NUL terminator, essentially doing further truncation.

If instead you pass to format_to_n the number of characters in the array - 1, then you can simply always write the NUL terminator to fmt_ret.out itself.

Related