最終更新日時(UTC):
が更新

履歴 編集

function
<format>

std::vformat_to(C++20)

namespace std {
  template<class Out>
  Out vformat_to(Out out, string_view fmt, format_args args); // (1)

  template<class Out>
  Out vformat_to(Out out, wstring_view fmt, wformat_args args); // (2)

  template<class Out>
  Out vformat_to(Out out, const locale& loc, string_view fmt, format_args args); // (3)

  template<class Out>
  Out vformat_to(Out out, const locale& loc, wstring_view fmt, wformat_args args); // (4)
}

概要

書式文字列fmtに従ったフォーマットでargsの文字列表現を出力イテレータoutに出力する。

  • (1): マルチバイト文字列版
  • (2): ワイド文字列版
  • (3): マルチバイト文字列版 (ロケール指定あり)
  • (4): ワイド文字列版 (ロケール指定あり)

format_toのフォーマット引数を型消去したバージョンであり、内部的に使用される。文字列をフォーマットする目的で直接利用する必要はない。 ただし、format_toのような関数を自作する場合は、vformat_toを使って実装すると便利である。

テンプレートパラメータ制約

Outは以下の制約を満たす。

  • (1),(3): OutputIterator<const char&>
  • (2),(4): OutputIterator<const wchar_t&>

事前条件

outは以下の制約を満たす型の有効なオブジェクトである。

  • (1),(3): OutputIterator<const char&>
  • (2),(4): OutputIterator<const wchar_t&>

効果

書式文字列fmtに従ったフォーマットでargsの文字列表現を出力イテレータout[out, out + N)イテレータ範囲に出力する。ロケールlocが指定された場合は、ロケール依存のフォーマットにおいて使用される。 (ただし、N=formatted_size(fmt, args...) または formatted_size(loc, fmt, args...))

戻り値

out + N (ただし、N=formatted_size(fmt, args...) または formatted_size(loc, fmt, args...))

例外

書式文字列が正しくなかったり、フォーマット実行時に失敗したりした場合、format_errorを投げる。

実装例

template<class ParseContext, class FormatContext>
struct ArgVisitor {
  ParseContext& pctx;
  FormatContext& fctx;

  void operator()(std::monostate) {
  }

  using handle = std::basic_format_arg<FormatContext>::handle;
  void operator()(const handle& handle) {
    handle.format(pctx, fctx);
  }

  template<class T>
  void operator()(const T& arg) {
    using Formatter = FormatContext::template formatter_type<T>;
    Formatter formatter;
    pctx.advance_to(formatter.parse(pctx));
    fctx.advance_to(formatter.format(arg, fctx));
  }
};

template<std::output_iterator<char> Out, class Context = std::basic_format_context<Out, char>>
Out vformat_to(Out out, std::string_view fmt, std::basic_format_args<Context> args) {
  using ParseContext = std::basic_format_parse_context<decltype(fmt)::value_type>;
  ParseContext pctx{fmt};
  Context fctx{out, args}; // このコンストラクタの存在は未規定
  ArgVisitor<ParseContext, Context> visitor{pctx, fctx};

  size_t next_arg_index = 0;
  while (!std::ranges::empty(pctx)) {
    auto it = pctx.begin();
    if (*it == '{') {
      ++it;
      if (it == pctx.end()) {
        throw std::format_error("invalid format");
      } else if (*it != '{') {
        // インデックスを解析する
        size_t index;
        if (auto [ptr, ec] = std::from_chars(it, pctx.end(), index); ec == std::errc{}) {
          it += (ptr - std::to_address(it));
          pctx.check_arg_id(index);
        } else {
          index = next_arg_index;
          pctx.next_arg_id();
          ++next_arg_index;
        }
        // オプション開始マークを解析する
        if (it == pctx.end()) {
          throw std::format_error("invalid format");
        } else if (*it == ':') {
          ++it;
        } else if (*it != '}') {
          throw std::format_error("invalid format");
        }
        pctx.advance_to(it);
        // フォーマッターを呼び出す
        std::visit_format_arg(visitor, args.get(index));
        // 置換フィールドの終端を解析する
        if (it == pctx.end() || *it != '}') {
          throw std::format_error("invalid format");
        }
        pctx.advance_to(++it);
        continue;
      }
    } else if (*it == '}') {
      ++it;
      if (it == pctx.end() || *it != '}') {
        throw std::format_error("invalid format");
      }
    }
    *out = *it;
    pctx.advance_to(++it);
    fctx.advance_to(++out);
  }
  return out;
}

バージョン

言語

  • C++20

処理系

参照