How to correctly use named args in C++ Fmt library
Asked Answered
E

1

5

I have the following bit of code, that uses a runtime determined width:

#include <string>
#include <vector>

#include <fmt/core.h>
#include <fmt/format.h>

int main()
{
   std::vector<std::string> lines =
   {
      "line 1 - xxxxxxxx",
      "line 2 - xxxxxxxx",
      "line 3 - xxxxxxxx",
      "line 4 - xxxxxxxx",
   };

   const std::size_t width = 3;

   for (std::size_t i = 0; i < lines.size(); ++i)
   {
      const auto& line = lines[i];
      fmt::println("{:0{width}} {}",
                   i, fmt::arg("width",width),
                   line);
   }

   return 0;
}

I expect it to output the lines as follows prefixing each line with a line number that is right-aligned and padded with "0"

000 line 1 - xxxxxxxx
001 line 2 - xxxxxxxx
002 line 3 - xxxxxxxx
003 line 4 - xxxxxxxx

However when I run it, I get the following runtime exception:

Program returned: 139
libc++abi: terminating due to uncaught exception of type fmt::v11::format_error: cannot switch from manual to automatic argument indexing
Program terminated with signal: SIGSEGV

I'm not sure where I'm going wrong here, I'm using the syntax detailed in the Argument-Determined Width section from here:

https://hackingcpp.com/cpp/libs/fmt.html


Godbolt link: https://godbolt.org/z/fzv66KGe1

Epaminondas answered 1/9 at 5:5 Comment(5)
Unrelated: Use #include <print> and #include <format> and std::printlnif you have c++23 support.Zestful
@Zestful Can't use the std format functions as the codebase is C++20, Also I don't think what is available in C++23 allow for runtime format specifiersEpaminondas
You should add tag c++20.Zestful
Side note : why aren't you refering to fmt's own documentation? (fmt.dev/11.0/api).Kennedy
@PennyDreudter C++20/23 does support runtime arguments. It just doesn't support named arguments, you need index-based(positional) arguments.Athena
D
6

I can't find it documented anywhere but it looks like you can't mix automatic arguments with named or indexed arguments, you can do either:

"{0:0{width}} {2}"

Or

"{:0{}} {}"

std::format does document this restriction:

specifies the index of the argument in args whose value is to be used for formatting; if it is omitted, the arguments are used in order.

The arg-id s in a format string must all be present or all be omitted. Mixing manual and automatic indexing is an error.

Disembowel answered 1/9 at 6:41 Comment(4)
You should add a live demo(godbolt) atleast since we don't have official source for this so if in future something changes, we'll have a link to the demo that shows this work.Zestful
Mixing manual and automatic indexing is only an error for the compile time version. For the runtime version, as in the OP's question, it's fine to mix them, with the constraint that you can't switch from manual to automatic. So in OP's case, {:0{width}} {2}", i, fmt::arg("width",width), line); works fine (where only the 2 needs to be specified, since it's preceded by a manual index with width). godbolt.org/z/cWrP7vfKj Not sure why there's a mismatch between runtime/compile versions (the runtime behavior seems sensible to me). And feel free to use the godbolt demo in the answer :)Ginglymus
the explanation makes sense, it's either all indexed or all args but no mixing is allowed if runtime checks are enabled.Epaminondas
@PennyDreudter No, it's - no mixing is allowed unless runtime checks are enabled. See the demo link in my previous comment.Ginglymus

© 2022 - 2024 — McMap. All rights reserved.