Boost Spirit Karma による printf 互換の浮動小数点出力、符号付き整数出力

ID: 27
creation date: 2013/06/04 23:50
modification date: 2013/06/06 09:58
owner: taiji
tags: C++, Boost, Spirit Karma, C99 printf

Boost Karma 事始め

Boost Spirit のパーサ Qi に対応するジェネレータ Karma を使うと、以下のように型安全な printf 風の出力が簡単に行える。

#include <iostream>
#include <cmath>
#include <boost/spirit/include/karma.hpp>

namespace karma = boost::spirit::karma;

int main()
{
  long double v[] = { std::exp(1), std::sqrt(2), };
  std::cout << karma::format(karma::long_double << ',' << karma::long_double, v[0], v[1]) << std::endl;
  return 0;
}
2.718,1.414

それどころか、様々なコンテナに対応しているので、std::vector に格納された要素の出力も極めて簡単である。

#include <iostream>
#include <cmath>
#include <vector>
#include <boost/assign.hpp>
#include <boost/spirit/include/karma.hpp>

namespace karma = boost::spirit::karma;

int main()
{
  std::vector<long double> v = boost::assign::list_of(std::exp(1))(std::sqrt(2));
  std::cout << karma::format(karma::long_double % ',', v) << std::endl;
  return 0;
}
2.718,1.414

Boost Karma による C99 printf double ステージ1

しかし、fprintf のような書式指定を行おうとすると少々厄介なので、ここでは浮動小数点に関して C99 printf 互換のジェネレータを用意してしまおう。

具体的には、以下のような C99 printf 書式指定のような浮動小数点の表示を、段階的に可能としていこう。

        scientific_real
%.3e    1.000e+00
%e      1.000000e+00
%+e     +1.000000e+00
% e      1.000000e+00

        fixed_real
%.3f    1.000
%f      1.000000
%+f     +1.000000
% f      1.000000

        general_real
%.3g    1
%g      1
%+g     +1
% g      1

        alt_general_real
%#.3g   1.00
%#g     1.00000
%#+g    +1.00000
%# g     1.00000

ちなみに、幅指定は最後の最後で対応する。

さて、Karma における浮動小数点の書式指定は real_policies<T> を継承したクラスにて行うものとなっているので、まずはこれらの「ポリシー」を用意しよう。

/*
  boost-spirit-karma-real_generators.hpp

  Copyright (C) 2013 Taiji Yamada <taiji@aihara.co.jp>

  Distributed under the Boost Software License, Version 1.0.
  (See http://www.boost.org/LICENSE_1_0.txt)
*/
#ifndef _boost_spirit_karma_real_generators_hpp_
#define _boost_spirit_karma_real_generators_hpp_

#include <boost/spirit/include/karma.hpp>

namespace boost { namespace spirit { namespace karma {

template <typename T>
struct scientific_real_policies : real_policies<T> {
  static int floatfield(T) { return real_policies<T>::fmtflags::scientific; }
};

template <typename T>
struct fixed_real_policies : scientific_real_policies<T> {
  static int floatfield(T) { return real_policies<T>::fmtflags::fixed; }
};

template <typename T>
struct alt_general_real_policies : fixed_real_policies<T> {
  static int floatfield(T n)
  {
    return real_policies<T>::floatfield(n);
  }
};

template <typename T>
struct general_real_policies : alt_general_real_policies<T> {
};

} } }

#endif

floatfield(T n) が常に real_policies<T>::fmtflags::scientific を返せば %e 相当、常に real_policies<T>::fmtflags::fixed を返せば %f 相当、既定の real_policies<T>::floatfield(n) に従えば %g 相当のようである。

例えばこれは、以下のように利用する。

#include <iostream>
#include <cmath>
#include <boost/spirit/include/karma.hpp>
#include "boost-spirit-karma-real_generators00.hpp"

namespace karma = boost::spirit::karma;
typedef karma::real_generator<long double, karma::scientific_real_policies<long double> > long_double_generator;

int main()
{
  long_double_generator long_double;
  long double v[] = { std::exp(1), std::sqrt(2), };
  std::cout << karma::format(long_double << ',' << long_double, v[0], v[1]) << std::endl;
  return 0;
}

この既定の段階では、以下のような表示形式になっている。

        scientific_real_policies<T>
%e      1.0e00                  T=double

        fixed_real_policies<T>
%f      1.0                     T=double

        general_real_policies<T>
%g      1.0                     T=double

まずは、精度を返す precision(T n) および、符号を正符号においても強制的に印字するか否かを返す force_sign(T n) で利用者がテンプレート引数で指定した属性を返すようにする。

--- boost-spirit-karma-real_generators00.hpp    2013-06-04 16:57:44.000000000 +0900
+++ boost-spirit-karma-real_generators01.hpp    2013-06-04 16:57:44.000000000 +0900
@@ -13,26 +13,28 @@
 
 namespace boost { namespace spirit { namespace karma {
 
-template <typename T>
+template <typename T, unsigned Precision = 6, bool ForceSign = false>
 struct scientific_real_policies : real_policies<T> {
+  static bool force_sign(T) { return ForceSign; }
   static int floatfield(T) { return real_policies<T>::fmtflags::scientific; }
+  static unsigned precision(T) { return Precision; }
 };
 
-template <typename T>
-struct fixed_real_policies : scientific_real_policies<T> {
+template <typename T, unsigned Precision = 6, bool ForceSign = false>
+struct fixed_real_policies : scientific_real_policies<T, Precision, ForceSign> {
   static int floatfield(T) { return real_policies<T>::fmtflags::fixed; }
 };
 
-template <typename T>
-struct alt_general_real_policies : fixed_real_policies<T> {
+template <typename T, unsigned Precision = 6, bool ForceSign = false>
+struct alt_general_real_policies : fixed_real_policies<T, Precision, ForceSign> {
   static int floatfield(T n)
   {
     return real_policies<T>::floatfield(n);
   }
 };
 
-template <typename T>
-struct general_real_policies : alt_general_real_policies<T> {
+template <typename T, unsigned Precision = 6, bool ForceSign = false>
+struct general_real_policies : alt_general_real_policies<T, Precision, ForceSign> {
 };
 
 }}}

Karma の既定の精度が 3 に対して、C99 printf の既定の精度は 6 なので、そのようにした。

この段階で以下のような表示形式になっている。

        scientific_real_policies<T, Precision=6, ForceSign=false>
%.3e    1.0e00                  T=double, Precision=3, ForceSign=false
%e      1.0e00                  T=double, Precision=6, ForceSign=false
%+e     +1.0e00                 T=double, Precision=6, ForceSign=true

        fixed_real_policies<T, Precision=6, ForceSign=false>
%.3f    1.0                     T=double, Precision=3, ForceSign=false
%f      1.0                     T=double, Precision=6, ForceSign=false
%+f     +1.0                    T=double, Precision=6, ForceSign=true

        general_real_policies<T, Precision=6, ForceSign=false>
%.3g    1.0                     T=double, Precision=3, ForceSign=false
%g      1.0                     T=double, Precision=6, ForceSign=false
%+g     +1.0                    T=double, Precision=6, ForceSign=true

キリの良い数字の場合、ゼロが切り詰められているのは、%g 以外では意図した書式ではないので、それに trailing_zeros(T n) で対応しよう。

--- boost-spirit-karma-real_generators01.hpp    2013-06-04 16:57:44.000000000 +0900
+++ boost-spirit-karma-real_generators02.hpp    2013-06-04 16:57:44.000000000 +0900
@@ -16,6 +16,7 @@
 template <typename T, unsigned Precision = 6, bool ForceSign = false>
 struct scientific_real_policies : real_policies<T> {
   static bool force_sign(T) { return ForceSign; }
+  static bool trailing_zeros(T) { return true; }
   static int floatfield(T) { return real_policies<T>::fmtflags::scientific; }
   static unsigned precision(T) { return Precision; }
 };
@@ -27,6 +28,7 @@
 
 template <typename T, unsigned Precision = 6, bool ForceSign = false>
 struct alt_general_real_policies : fixed_real_policies<T, Precision, ForceSign> {
+  static bool trailing_zeros(T) { return true; }
   static int floatfield(T n)
   {
     return real_policies<T>::floatfield(n);
@@ -35,6 +37,7 @@
 
 template <typename T, unsigned Precision = 6, bool ForceSign = false>
 struct general_real_policies : alt_general_real_policies<T, Precision, ForceSign> {
+  static bool trailing_zeros(T) { return false; }
 };
 
 }}}

この段階で以下のような表示形式になる。

        scientific_real_policies<T, Precision=6, ForceSign=false>
%.3e    1.000e00                T=double, Precision=3, ForceSign=false
%e      1.000000e00             T=double, Precision=6, ForceSign=false
%+e     +1.000000e00            T=double, Precision=6, ForceSign=true

        fixed_real_policies<T, Precision=6, ForceSign=false>
%.3f    1.000                   T=double, Precision=3, ForceSign=false
%f      1.000000                T=double, Precision=6, ForceSign=false
%+f     +1.000000               T=double, Precision=6, ForceSign=true

        general_real_policies<T, Precision=6, ForceSign=false>
%.3g    1.0                     T=double, Precision=3, ForceSign=false
%g      1.0                     T=double, Precision=6, ForceSign=false
%+g     +1.0                    T=double, Precision=6, ForceSign=true

        alt_general_real_policies<T, Precision=6, ForceSign=false>
%#.3g   1.000                   T=double, Precision=3, ForceSign=false
%#g     1.000000                T=double, Precision=6, ForceSign=false
%#+g    +1.000000               T=double, Precision=6, ForceSign=true

次に、%e の指数部の符号は常に表示するようにしよう。

--- boost-spirit-karma-real_generators02.hpp    2013-06-04 22:38:15.000000000 +0900
+++ boost-spirit-karma-real_generators03.hpp    2013-06-04 22:38:15.000000000 +0900
@@ -17,10 +17,21 @@
 struct scientific_real_policies : real_policies<T> {
   static bool force_sign(T) { return ForceSign; }
   static bool trailing_zeros(T) { return true; }
   static int floatfield(T) { return real_policies<T>::fmtflags::scientific; }
   static unsigned precision(T) { return Precision; }
+  template <typename CharEncoding, typename Tag, typename OutputIterator>
+  static bool exponent(OutputIterator &sink, long n)
+  {
+    long abs_n = traits::get_absolute_value(n);
+    bool r = char_inserter<CharEncoding, Tag>::call(sink, 'e') &&
+      sign_inserter::call(sink, /*traits::test_zero(n)*/false,
+                          traits::test_negative(n), /*false*/true);
+    if (r && abs_n < 10) // the C99 Standard requires at least two digits in the exponent
+      r = char_inserter<CharEncoding, Tag>::call(sink, '0');
+    return r && int_inserter<10>::call(sink, abs_n);
+  }
 };
 
 template <typename T, unsigned Precision = 6, bool ForceSign = false>
 struct fixed_real_policies : scientific_real_policies<T, Precision, ForceSign> {
   static int floatfield(T) { return real_policies<T>::fmtflags::fixed; }

すると以下のような表示形式になる。

        scientific_real_policies<T, Precision=6, ForceSign=false>
%.3e    1.000e+00               T=double, Precision=3, ForceSign=false
%e      1.000000e+00            T=double, Precision=6, ForceSign=false
%+e     +1.000000e+00           T=double, Precision=6, ForceSign=true

sign_inserter::call の第4引数が強制的に符号を印字するか否かを決定している。ちなみに、第2引数は、真のときに正符号の代わりに「空白」を印字してしまうので、ここでは偽を指定してそれを抑止している。

次に、正符号の代わりに「空白」を印字することを、利用者がテンプレート引数で選べるようにする。

--- boost-spirit-karma-real_generators03.hpp    2013-06-04 16:57:45.000000000 +0900
+++ boost-spirit-karma-real_generators04.hpp    2013-06-04 16:57:45.000000000 +0900
@@ -13,12 +13,18 @@
 
 namespace boost { namespace spirit { namespace karma {
 
-template <typename T, unsigned Precision = 6, bool ForceSign = false>
+template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false>
 struct scientific_real_policies : real_policies<T> {
   static bool force_sign(T) { return ForceSign; }
   static bool trailing_zeros(T) { return true; }
   static int floatfield(T) { return real_policies<T>::fmtflags::scientific; }
   static unsigned precision(T) { return Precision; }
+  template <typename OutputIterator>
+  static bool integer_part(OutputIterator &sink, T n, bool sign, bool force_sign)
+  {
+    return sign_inserter::call(sink, (force_sign && !sign && BlankSign) || traits::test_zero(n), sign, force_sign) &&
+      int_inserter<10>::call(sink, n);
+  }
   template <typename CharEncoding, typename Tag, typename OutputIterator>
   static bool exponent(OutputIterator &sink, long n)
   {
@@ -32,13 +38,18 @@
   }
 };
 
-template <typename T, unsigned Precision = 6, bool ForceSign = false>
-struct fixed_real_policies : scientific_real_policies<T, Precision, ForceSign> {
+template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false>
+struct fixed_real_policies : scientific_real_policies<T, Precision, ForceSign, BlankSign> {
   static int floatfield(T) { return real_policies<T>::fmtflags::fixed; }
+  template <typename OutputIterator>
+  static bool integer_part(OutputIterator &sink, T n, bool sign, bool force_sign)
+  {
+    return real_policies<T>::integer_part(sink, n, sign, force_sign);
+  }
 };
 
-template <typename T, unsigned Precision = 6, bool ForceSign = false>
-struct alt_general_real_policies : fixed_real_policies<T, Precision, ForceSign> {
+template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false>
+struct alt_general_real_policies : fixed_real_policies<T, Precision, ForceSign, BlankSign> {
   static bool trailing_zeros(T) { return true; }
   static int floatfield(T n)
   {
@@ -46,8 +57,8 @@
   }
 };
 
-template <typename T, unsigned Precision = 6, bool ForceSign = false>
-struct general_real_policies : alt_general_real_policies<T, Precision, ForceSign> {
+template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false>
+struct general_real_policies : alt_general_real_policies<T, Precision, ForceSign, BlankSign> {
   static bool trailing_zeros(T) { return false; }
 };
 

ここでは、scientific_real_policies<T> の仮数部を印字する integer_part 内の sign_inserter::call の第2引数は、真のときに正符号の代わりに「空白」を印字する条件を「正のとき空白としての符号が強制されるとき、または、仮数部がゼロのとき」として C99 printf 互換となるようにしている。

また、fixed_real_policies<T> の仮数部を印字する integer_part は、取り敢えず real_policies<T> のものを踏襲している。

この段階で以下のような表示形式になる。

        scientific_real_policies<T, Precision=6, ForceSign=false, BlankSign=false>
%.3e    1.000e+00               T=double, Precision=3, ForceSign=false, BlankSign=false
%e      1.000000e+00            T=double, Precision=6, ForceSign=false, BlankSign=false
%+e     +1.000000e+00           T=double, Precision=6, ForceSign=true, BlankSign=false
% e      1.000000e+00           T=double, Precision=6, ForceSign=true, BlankSign=true

        fixed_real_policies<T, Precision=6, ForceSign=false, BlankSign=false>
%.3f    1.000                   T=double, Precision=3, ForceSign=false, BlankSign=false
%f      1.000000                T=double, Precision=6, ForceSign=false, BlankSign=false
%+f     +1.000000               T=double, Precision=6, ForceSign=true, BlankSign=false
% f     +1.000000               T=double, Precision=6, ForceSign=true, BlankSign=true

        general_real_policies<T, Precision=6, ForceSign=false, BlankSign=false>
%.3g    1.0                     T=double, Precision=3, ForceSign=false, BlankSign=false
%g      1.0                     T=double, Precision=6, ForceSign=false, BlankSign=false
%+g     +1.0                    T=double, Precision=6, ForceSign=true, BlankSign=false
% g     +1.0                    T=double, Precision=6, ForceSign=true, BlankSign=true

        alt_general_real_policies<T, Precision=6, ForceSign=false, BlankSign=false>
%#.3g   1.000                   T=double, Precision=3, ForceSign=false, BlankSign=false
%#g     1.000000                T=double, Precision=6, ForceSign=false, BlankSign=false
%#+g    +1.000000               T=double, Precision=6, ForceSign=true, BlankSign=false
%# g    +1.000000               T=double, Precision=6, ForceSign=true, BlankSign=true

ここまでで %e については意図した書式になったが、まだまだ C99 printf 互換には程遠い。

次に % f に対応しよう。

--- boost-spirit-karma-real_generators04.hpp    2013-06-04 22:38:15.000000000 +0900
+++ boost-spirit-karma-real_generators05.hpp    2013-06-04 22:38:15.000000000 +0900
@@ -42,11 +42,12 @@
 struct fixed_real_policies : scientific_real_policies<T, Precision, ForceSign, BlankSign> {
   static int floatfield(T) { return real_policies<T>::fmtflags::fixed; }
   template <typename OutputIterator>
   static bool integer_part(OutputIterator &sink, T n, bool sign, bool force_sign)
   {
-    return real_policies<T>::integer_part(sink, n, sign, force_sign);
+    return sign_inserter::call(sink, force_sign && !sign && BlankSign, sign, force_sign) &&
+      int_inserter<10>::call(sink, n);
   }
 };
 
 template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false>
 struct alt_general_real_policies : fixed_real_policies<T, Precision, ForceSign, BlankSign> {

ここでは、fixed_real_policies<T> の仮数部を印字する integer_part 内の sign_inserter::call の第2引数は、真のときに正符号の代わりに「空白」を印字する条件を「正のとき空白としての符号が強制されるとき」として C99 printf 互換となるようにしている。

次に、%g では、キリの良い数字の場合、ゼロだけでなく小数点も不要だ。

--- boost-spirit-karma-real_generators05.hpp    2013-06-04 16:57:45.000000000 +0900
+++ boost-spirit-karma-real_generators06.hpp    2013-06-04 16:57:45.000000000 +0900
@@ -61,6 +61,26 @@
 template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false>
 struct general_real_policies : alt_general_real_policies<T, Precision, ForceSign, BlankSign> {
   static bool trailing_zeros(T) { return false; }
+  static int floatfield(T n)
+  {
+    if (traits::test_zero(n))
+      return real_policies<T>::fmtflags::fixed;
+    return alt_general_real_policies<T, Precision, ForceSign, BlankSign>::floatfield(n);
+  }
+  template <typename OutputIterator>
+  static bool dot(OutputIterator &sink, T n, unsigned precision)
+  {
+    if (traits::test_zero(n))
+      return true;
+    return alt_general_real_policies<T, Precision, ForceSign, BlankSign>::dot(sink, n, precision);
+  }
+  template <typename OutputIterator>
+  static bool fraction_part(OutputIterator &sink, T n, unsigned adjprec, unsigned precision)
+  {
+    if (traits::test_zero(n))
+      return true;
+    return fixed_real_policies<T, Precision, ForceSign, BlankSign>::fraction_part(sink, n, adjprec, precision);
+  }
 };
 
 }}}

小数がゼロの時は real_policies<T>::fmtflags::fixed で印字し、小数点や小数部は何もしない処理をしている。さもなくば、都合の良い継承しているメンバ関数を呼んでいる。

この段階で以下のような表示形式になる。

        scientific_real_policies<T, Precision=6, ForceSign=false, BlankSign=false>
%.3e    1.000e+00               T=double, Precision=3, ForceSign=false, BlankSign=false
%e      1.000000e+00            T=double, Precision=6, ForceSign=false, BlankSign=false
%+e     +1.000000e+00           T=double, Precision=6, ForceSign=true, BlankSign=false
% e      1.000000e+00           T=double, Precision=6, ForceSign=true, BlankSign=true

        fixed_real_policies<T, Precision=6, ForceSign=false, BlankSign=false>
%.3f    1.000                   T=double, Precision=3, ForceSign=false, BlankSign=false
%f      1.000000                T=double, Precision=6, ForceSign=false, BlankSign=false
%+f     +1.000000               T=double, Precision=6, ForceSign=true, BlankSign=false
% f      1.000000               T=double, Precision=6, ForceSign=true, BlankSign=true

        general_real_policies<T, Precision=6, ForceSign=false, BlankSign=false>
%.3g    1                       T=double, Precision=3, ForceSign=false, BlankSign=false
%g      1                       T=double, Precision=6, ForceSign=false, BlankSign=false
%+g     +1                      T=double, Precision=6, ForceSign=true, BlankSign=false
% g      1                      T=double, Precision=6, ForceSign=true, BlankSign=true

        alt_general_real_policies<T, Precision=6, ForceSign=false, BlankSign=false>
%#.3g   1.000                   T=double, Precision=3, ForceSign=false, BlankSign=false
%#g     1.000000                T=double, Precision=6, ForceSign=false, BlankSign=false
%#+g    +1.000000               T=double, Precision=6, ForceSign=true, BlankSign=false
%# g    +1.000000               T=double, Precision=6, ForceSign=true, BlankSign=true

相当よくなってきたが、致命的な問題が残っている。%#g の精度とは「有効数字」のことであり、他における「小数点以下の桁数」ではない。よって、続けて floatfield(T n)precision(T n) の2箇所修正してみよう。

--- boost-spirit-karma-real_generators06.hpp    2013-06-04 22:38:15.000000000 +0900
+++ boost-spirit-karma-real_generators07.hpp    2013-06-04 22:38:15.000000000 +0900
@@ -52,11 +52,14 @@
 template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false>
 struct alt_general_real_policies : fixed_real_policies<T, Precision, ForceSign, BlankSign> {
   static bool trailing_zeros(T) { return true; }
   static int floatfield(T n)
   {
-    return real_policies<T>::floatfield(n);
+    using namespace std;
+    T abs_n = traits::get_absolute_value(n);
+    return (!(floor(log10(abs_n)) < Precision) || abs_n < 1e-4) ?
+      real_policies<T>::fmtflags::scientific : real_policies<T>::fmtflags::fixed;
   }
 };
 
 template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false>
 struct general_real_policies : alt_general_real_policies<T, Precision, ForceSign, BlankSign> {
--- boost-spirit-karma-real_generators07.hpp    2013-06-04 16:57:45.000000000 +0900
+++ boost-spirit-karma-real_generators08.hpp    2013-06-04 16:57:45.000000000 +0900
@@ -59,6 +59,15 @@
     return (!(floor(log10(abs_n)) < Precision) || abs_n < 1e-4) ?
       real_policies<T>::fmtflags::scientific : real_policies<T>::fmtflags::fixed;
   }
+  static unsigned precision(T n)
+  {
+    using namespace std;
+    T abs_n = traits::get_absolute_value(n), l;
+    if ((l = floor(log10(1./abs_n))) > 1)
+      return Precision + (l - 1);
+    l = floor(log10(abs_n));
+    return Precision > l ? Precision - (l + 1) : Precision - 1;
+  }
 };
 
 template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false>

つまり、precision(T n) は有効桁 Precision から得られる小数点以下の桁数を返すようにし、他所との整合性をとった。

この段階で以下のような表示形式になる。

        scientific_real_policies<T, Precision=6, ForceSign=false, BlankSign=false>
%.3e    1.000e+00               T=double, Precision=3, ForceSign=false, BlankSign=false
%e      1.000000e+00            T=double, Precision=6, ForceSign=false, BlankSign=false
%+e     +1.000000e+00           T=double, Precision=6, ForceSign=true, BlankSign=false
% e      1.000000e+00           T=double, Precision=6, ForceSign=true, BlankSign=true

        fixed_real_policies<T, Precision=6, ForceSign=false, BlankSign=false>
%.3f    1.000                   T=double, Precision=3, ForceSign=false, BlankSign=false
%f      1.000000                T=double, Precision=6, ForceSign=false, BlankSign=false
%+f     +1.000000               T=double, Precision=6, ForceSign=true, BlankSign=false
% f      1.000000               T=double, Precision=6, ForceSign=true, BlankSign=true

        general_real_policies<T, Precision=6, ForceSign=false, BlankSign=false>
%.3g    1                       T=double, Precision=3, ForceSign=false, BlankSign=false
%g      1                       T=double, Precision=6, ForceSign=false, BlankSign=false
%+g     +1                      T=double, Precision=6, ForceSign=true, BlankSign=false
% g      1                      T=double, Precision=6, ForceSign=true, BlankSign=true

        alt_general_real_policies<T, Precision=6, ForceSign=false, BlankSign=false>
%#.3g   1.00                    T=double, Precision=3, ForceSign=false, BlankSign=false
%#g     1.00000                 T=double, Precision=6, ForceSign=false, BlankSign=false
%#+g    +1.00000                T=double, Precision=6, ForceSign=true, BlankSign=false
%# g     1.00000                T=double, Precision=6, ForceSign=true, BlankSign=true

ほぼ完璧と思ったら大間違い。精度が3で値が -0.0001 のように精度を超えた負値の場合、以下のように符号が消えてしまう。

        fixed_real_policies<T, Precision=6, ForceSign=false, BlankSign=false>
%.3f    0.000                   T=double, Precision=3, ForceSign=false, BlankSign=false
%#.3f   0.000                   T=double, Precision=3, ForceSign=false, BlankSign=false

いくら精度以下とはいえ、負のほぼ零と正のほぼ零は意味が全く異る。

        fixed_real_policies<T, Precision=6, ForceSign=false, BlankSign=false>
%.3f    -0.000                  T=double, Precision=3, ForceSign=false, BlankSign=false
%#.3f   -0.000                  T=double, Precision=3, ForceSign=false, BlankSign=false

これに対応するには、結局、オリジナルが処理していることと微妙に異なるコードを書く必要があった。

--- boost-spirit-karma-real_generators08.hpp    2013-06-04 16:57:46.000000000 +0900
+++ boost-spirit-karma-real_generators09.hpp    2013-06-04 16:57:46.000000000 +0900
@@ -13,8 +13,74 @@
 
 namespace boost { namespace spirit { namespace karma {
 
-template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false>
+template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false, typename CE = unused_type, typename CC = unused_type>
 struct scientific_real_policies : real_policies<T> {
+  typedef CE CharEncoding;
+  typedef CC Tag;
+  template <typename Inserter, typename OutputIterator, typename Policies>
+  static bool call(OutputIterator &sink, T n, Policies const &p)
+  {
+    bool force_sign = p.force_sign(n);
+    bool sign_val = false;
+    int flags = p.floatfield(n);
+    if (traits::test_negative(n)) {
+      n = -n;
+      sign_val = true;
+    }
+    unsigned precision = p.precision(n);
+    if (std::numeric_limits<T>::digits10) {
+      precision = (std::min)(precision,
+                             (unsigned)std::numeric_limits<T>::digits10 + 1);
+    }
+    using namespace std;
+    T dim = 0;
+    if (0 == (Policies::fmtflags::fixed & flags) && !traits::test_zero(n)) {
+      dim = log10(n);
+      if (dim > 0)
+        n /= spirit::traits::pow10<T>(traits::truncate_to_long::call(dim));
+      else if (n < 1.) {
+        long exp = traits::truncate_to_long::call(-dim);
+        if (exp != -dim)
+          ++exp;
+        dim = -exp;
+        n *= spirit::traits::pow10<T>(exp);
+      }
+    }
+    T integer_part;
+    T precexp = spirit::traits::pow10<T>(precision);
+    T fractional_part = modf(n, &integer_part);
+    fractional_part = floor(fractional_part * precexp + T(0.5));
+    if (fractional_part >= precexp) {
+      fractional_part = floor(fractional_part - precexp);
+      integer_part += 1;
+    }
+    T long_int_part = floor(integer_part);
+    T long_frac_part = fractional_part;
+    unsigned prec = precision;
+    if (!p.trailing_zeros(n)) {
+      T frac_part_floor = long_frac_part;
+      if (0 != long_frac_part) {
+        while (0 != prec &&
+               0 == traits::remainder<10>::call(long_frac_part)) {
+          long_frac_part = traits::divide<10>::call(long_frac_part);
+          --prec;
+        }
+      }
+      else
+        prec = 0;
+      if (precision != prec)
+        long_frac_part = frac_part_floor / spirit::traits::pow10<T>(precision-prec);
+    }
+    if (false &&
+        sign_val && traits::test_zero(long_int_part) && traits::test_zero(long_frac_part))
+      sign_val = false;
+    bool r = p.integer_part(sink, long_int_part, sign_val, force_sign);
+    r = r && p.dot(sink, long_frac_part, precision);
+    r = r && p.fraction_part(sink, long_frac_part, prec, precision);
+    if (r && 0 == (Policies::fmtflags::fixed & flags))
+      return p.template exponent<CharEncoding, Tag>(sink, traits::truncate_to_long::call(dim));
+    return r;
+  }
   static bool force_sign(T) { return ForceSign; }
   static bool trailing_zeros(T) { return true; }
   static int floatfield(T) { return real_policies<T>::fmtflags::scientific; }
@@ -38,8 +104,8 @@
   }
 };
 
-template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false>
-struct fixed_real_policies : scientific_real_policies<T, Precision, ForceSign, BlankSign> {
+template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false, typename CE = unused_type, typename CC = unused_type>
+struct fixed_real_policies : scientific_real_policies<T, Precision, ForceSign, BlankSign, CE, CC> {
   static int floatfield(T) { return real_policies<T>::fmtflags::fixed; }
   template <typename OutputIterator>
   static bool integer_part(OutputIterator &sink, T n, bool sign, bool force_sign)
@@ -49,8 +115,8 @@
   }
 };
 
-template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false>
-struct alt_general_real_policies : fixed_real_policies<T, Precision, ForceSign, BlankSign> {
+template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false, typename CE = unused_type, typename CC = unused_type>
+struct alt_general_real_policies : fixed_real_policies<T, Precision, ForceSign, BlankSign, CE, CC> {
   static bool trailing_zeros(T) { return true; }
   static int floatfield(T n)
   {
@@ -70,8 +136,8 @@
   }
 };
 
-template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false>
-struct general_real_policies : alt_general_real_policies<T, Precision, ForceSign, BlankSign> {
+template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false, typename CE = unused_type, typename CC = unused_type>
+struct general_real_policies : alt_general_real_policies<T, Precision, ForceSign, BlankSign, CE, CC> {
   static bool trailing_zeros(T) { return false; }
   static int floatfield(T n)
   {

call でやっていることは sign_val = false を抑制しているだけで、オリジナルとほぼ一緒。ついでに、大文字の %E, %G に対応させるための変更 typename CE = karma::char_encoding::ascii, typename CC = karma::tag::upper もやっているので注意。

Boost Karma による C99 printf double ステージ2

先の書式指定に加えて、以下のような幅指定及び各種フラグ -0 の追加に対応させよう。ひとつの値だけでも組み合わせはこれだけある。

%-12.3e         1.000e+00   
%-12e           1.000000e+00
%-+12e          +1.000000e+00
%- 12e           1.000000e+00
%-12.3f         1.000       
%-12f           1.000000    
%-+12f          +1.000000   
%- 12f           1.000000   
%-12.3g         1           
%-12g           1           
%-+12g          +1          
%- 12g           1          
%#-12.3g        1.00        
%#-12g          1.00000     
%#-+12g         +1.00000    
%#- 12g          1.00000    
%12.3e             1.000e+00
%12e            1.000000e+00
%+12e           +1.000000e+00
% 12e            1.000000e+00
%12.3f                 1.000
%12f                1.000000
%+12f              +1.000000
% 12f               1.000000
%12.3g                     1
%12g                       1
%+12g                     +1
% 12g                      1
%#12.3g                 1.00
%#12g                1.00000
%#+12g              +1.00000
%# 12g               1.00000
%12.3g                     1
%12g                       1
%+12g                     +1
% 12g                      1
%012.3e         0001.000e+00
%012e           1.000000e+00
%+012e          +1.000000e+00
% 012e           1.000000e+00
%012.3f         00000001.000
%012f           00001.000000
%+012f          +0001.000000
% 012f           0001.000000
%012.3g         000000000001
%012g           000000000001
%+012g          +00000000001
% 012g           00000000001
%#012.3g        000000001.00
%#012g          000001.00000
%#+012g         +00001.00000
%# 012g          00001.00000
%012.3g         000000000001
%012g           000000000001
%+012g          +00000000001
% 012g           00000000001

以上、C99 printf と一致していることは確認した。

--- boost-spirit-karma-real_generators09.hpp    2013-06-04 16:57:46.000000000 +0900
+++ boost-spirit-karma-real_generators10.hpp    2013-06-04 16:57:46.000000000 +0900
@@ -13,14 +13,18 @@
 
 namespace boost { namespace spirit { namespace karma {
 
-template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false, typename CE = unused_type, typename CC = unused_type>
+template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false,
+          int Width = 0, int Align = -1, char Pad = ' ',
+          typename CE = unused_type, typename CC = unused_type>
 struct scientific_real_policies : real_policies<T> {
+  typedef boost::mpl::int_<
+    karma::generator_properties::countingbuffer
+  > properties;
   typedef CE CharEncoding;
   typedef CC Tag;
   template <typename Inserter, typename OutputIterator, typename Policies>
-  static bool call(OutputIterator &sink, T n, Policies const &p)
+  static bool call_n(OutputIterator &sink, T n, Policies const &p, bool force_sign)
   {
-    bool force_sign = p.force_sign(n);
     bool sign_val = false;
     int flags = p.floatfield(n);
     if (traits::test_negative(n)) {
@@ -81,6 +85,43 @@
       return p.template exponent<CharEncoding, Tag>(sink, traits::truncate_to_long::call(dim));
     return r;
   }
+
+  template <typename Inserter, typename OutputIterator, typename Policies>
+  static bool call(OutputIterator &sink, T n, Policies const &p)
+  {
+    if (!Width)
+      return call_n<Inserter, OutputIterator, Policies>(sink, n, p, p.force_sign(n));
+    bool r = false;
+    if (Align < 0) { // left align
+      karma::detail::enable_counting<OutputIterator> counting(sink);
+      r = call_n<Inserter, OutputIterator, Policies>(sink, n, p, p.force_sign(n));
+      while (r && int(counting.count()) < Width) 
+        r = karma::generate(sink, ' ');
+    }
+    else { // right align
+      bool sign_val = false;
+      if (Pad == '0' && traits::test_negative(n)) {
+        n = -n;
+        sign_val = true;
+      }
+      karma::detail::enable_buffering<OutputIterator> buffering(sink, Width);
+      {
+        karma::detail::disable_counting<OutputIterator> nocounting(sink);
+        r = call_n<Inserter, OutputIterator, Policies>(sink, n, p, (Pad == '0' && p.force_sign(n)) ? false : p.force_sign(n));
+      }
+      buffering.disable();
+      karma::detail::enable_counting<OutputIterator> counting(sink, buffering.buffer_size());
+      if (sign_val || (Pad == '0' && p.force_sign(n)))
+        r = sign_inserter::call(sink, (force_sign && !sign_val && BlankSign) || traits::test_zero(n), sign_val, p.force_sign(n));
+      while (r && int(counting.count()) < Width) 
+        r = karma::generate(sink, Pad);
+      if (r) {
+        buffering.buffer_copy();
+      }
+    }
+    return r;
+  }
+
   static bool force_sign(T) { return ForceSign; }
   static bool trailing_zeros(T) { return true; }
   static int floatfield(T) { return real_policies<T>::fmtflags::scientific; }
@@ -104,8 +145,10 @@
   }
 };
 
-template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false, typename CE = unused_type, typename CC = unused_type>
-struct fixed_real_policies : scientific_real_policies<T, Precision, ForceSign, BlankSign, CE, CC> {
+template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false,
+          int Width = 0, int Align = -1, char Pad = ' ',
+          typename CE = unused_type, typename CC = unused_type>
+struct fixed_real_policies : scientific_real_policies<T, Precision, ForceSign, BlankSign, Width, Align, Pad, CE, CC> {
   static int floatfield(T) { return real_policies<T>::fmtflags::fixed; }
   template <typename OutputIterator>
   static bool integer_part(OutputIterator &sink, T n, bool sign, bool force_sign)
@@ -115,8 +158,10 @@
   }
 };
 
-template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false, typename CE = unused_type, typename CC = unused_type>
-struct alt_general_real_policies : fixed_real_policies<T, Precision, ForceSign, BlankSign, CE, CC> {
+template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false,
+          int Width = 0, int Align = -1, char Pad = ' ',
+          typename CE = unused_type, typename CC = unused_type>
+struct alt_general_real_policies : fixed_real_policies<T, Precision, ForceSign, BlankSign, Width, Align, Pad, CE, CC> {
   static bool trailing_zeros(T) { return true; }
   static int floatfield(T n)
   {
@@ -136,8 +181,10 @@
   }
 };
 
-template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false, typename CE = unused_type, typename CC = unused_type>
-struct general_real_policies : alt_general_real_policies<T, Precision, ForceSign, BlankSign, CE, CC> {
+template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false,
+          int Width = 0, int Align = -1, char Pad = ' ',
+          typename CE = unused_type, typename CC = unused_type>
+struct general_real_policies : alt_general_real_policies<T, Precision, ForceSign, BlankSign, Width, Align, Pad, CE, CC> {
   static bool trailing_zeros(T) { return false; }
   static int floatfield(T n)
   {

最後に全体像を示す。Boost Spirit Karma のサンプルは参考にしたが、実用的ではないので、以下のものでよいと思う。

/*
  boost-spirit-karma-real_generators.hpp

  Copyright (C) 2013 Taiji Yamada <taiji@aihara.co.jp>

  Distributed under the Boost Software License, Version 1.0.
  (See http://www.boost.org/LICENSE_1_0.txt)
*/
#ifndef _boost_spirit_karma_real_generators_hpp_
#define _boost_spirit_karma_real_generators_hpp_

#include <boost/spirit/include/karma.hpp>

namespace boost { namespace spirit { namespace karma {

template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false,
          int Width = 0, int Align = -1, char Pad = ' ',
          typename CE = unused_type, typename CC = unused_type>
struct scientific_real_policies : real_policies<T> {
  typedef boost::mpl::int_<
    karma::generator_properties::countingbuffer
  > properties;
  typedef CE CharEncoding;
  typedef CC Tag;
  template <typename Inserter, typename OutputIterator, typename Policies>
  static bool call_n(OutputIterator &sink, T n, Policies const &p, bool force_sign)
  {
    bool sign_val = false;
    int flags = p.floatfield(n);
    if (traits::test_negative(n)) {
      n = -n;
      sign_val = true;
    }
    unsigned precision = p.precision(n);
    if (std::numeric_limits<T>::digits10) {
      precision = (std::min)(precision,
                             (unsigned)std::numeric_limits<T>::digits10 + 1);
    }
    using namespace std;
    T dim = 0;
    if (0 == (Policies::fmtflags::fixed & flags) && !traits::test_zero(n)) {
      dim = log10(n);
      if (dim > 0)
        n /= spirit::traits::pow10<T>(traits::truncate_to_long::call(dim));
      else if (n < 1.) {
        long exp = traits::truncate_to_long::call(-dim);
        if (exp != -dim)
          ++exp;
        dim = -exp;
        n *= spirit::traits::pow10<T>(exp);
      }
    }
    T integer_part;
    T precexp = spirit::traits::pow10<T>(precision);
    T fractional_part = modf(n, &integer_part);
    fractional_part = floor(fractional_part * precexp + T(0.5));
    if (fractional_part >= precexp) {
      fractional_part = floor(fractional_part - precexp);
      integer_part += 1;
    }
    T long_int_part = floor(integer_part);
    T long_frac_part = fractional_part;
    unsigned prec = precision;
    if (!p.trailing_zeros(n)) {
      T frac_part_floor = long_frac_part;
      if (0 != long_frac_part) {
        while (0 != prec &&
               0 == traits::remainder<10>::call(long_frac_part)) {
          long_frac_part = traits::divide<10>::call(long_frac_part);
          --prec;
        }
      }
      else
        prec = 0;
      if (precision != prec)
        long_frac_part = frac_part_floor / spirit::traits::pow10<T>(precision-prec);
    }
    if (false &&
        sign_val && traits::test_zero(long_int_part) && traits::test_zero(long_frac_part))
      sign_val = false;
    bool r = p.integer_part(sink, long_int_part, sign_val, force_sign);
    r = r && p.dot(sink, long_frac_part, precision);
    r = r && p.fraction_part(sink, long_frac_part, prec, precision);
    if (r && 0 == (Policies::fmtflags::fixed & flags))
      return p.template exponent<CharEncoding, Tag>(sink, traits::truncate_to_long::call(dim));
    return r;
  }

  template <typename Inserter, typename OutputIterator, typename Policies>
  static bool call(OutputIterator &sink, T n, Policies const &p)
  {
    if (!Width)
      return call_n<Inserter, OutputIterator, Policies>(sink, n, p, p.force_sign(n));
    bool r = false;
    if (Align < 0) { // left align
      karma::detail::enable_counting<OutputIterator> counting(sink);
      r = call_n<Inserter, OutputIterator, Policies>(sink, n, p, p.force_sign(n));
      while (r && int(counting.count()) < Width) 
        r = karma::generate(sink, ' ');
    }
    else { // right align
      bool sign_val = false;
      if (Pad == '0' && traits::test_negative(n)) {
        n = -n;
        sign_val = true;
      }
      karma::detail::enable_buffering<OutputIterator> buffering(sink, Width);
      {
        karma::detail::disable_counting<OutputIterator> nocounting(sink);
        r = call_n<Inserter, OutputIterator, Policies>(sink, n, p, (Pad == '0' && p.force_sign(n)) ? false : p.force_sign(n));
      }
      buffering.disable();
      karma::detail::enable_counting<OutputIterator> counting(sink, buffering.buffer_size());
      if (sign_val || (Pad == '0' && p.force_sign(n)))
        r = sign_inserter::call(sink, (force_sign && !sign_val && BlankSign) || traits::test_zero(n), sign_val, p.force_sign(n));
      while (r && int(counting.count()) < Width) 
        r = karma::generate(sink, Pad);
      if (r) {
        buffering.buffer_copy();
      }
    }
    return r;
  }

  static bool force_sign(T) { return ForceSign; }
  static bool trailing_zeros(T) { return true; }
  static int floatfield(T) { return real_policies<T>::fmtflags::scientific; }
  static unsigned precision(T) { return Precision; }
  template <typename OutputIterator>
  static bool integer_part(OutputIterator &sink, T n, bool sign, bool force_sign)
  {
    return sign_inserter::call(sink, (force_sign && !sign && BlankSign) || traits::test_zero(n), sign, force_sign) &&
      int_inserter<10>::call(sink, n);
  }
  template <typename CharEncoding, typename Tag, typename OutputIterator>
  static bool exponent(OutputIterator &sink, long n)
  {
    long abs_n = traits::get_absolute_value(n);
    bool r = char_inserter<CharEncoding, Tag>::call(sink, 'e') &&
      sign_inserter::call(sink, /*traits::test_zero(n)*/false,
                          traits::test_negative(n), /*false*/true);
    if (r && abs_n < 10) // the C99 Standard requires at least two digits in the exponent
      r = char_inserter<CharEncoding, Tag>::call(sink, '0');
    return r && int_inserter<10>::call(sink, abs_n);
  }
};

template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false,
          int Width = 0, int Align = -1, char Pad = ' ',
          typename CE = unused_type, typename CC = unused_type>
struct fixed_real_policies : scientific_real_policies<T, Precision, ForceSign, BlankSign, Width, Align, Pad, CE, CC> {
  static int floatfield(T) { return real_policies<T>::fmtflags::fixed; }
  template <typename OutputIterator>
  static bool integer_part(OutputIterator &sink, T n, bool sign, bool force_sign)
  {
    return sign_inserter::call(sink, force_sign && !sign && BlankSign, sign, force_sign) &&
      int_inserter<10>::call(sink, n);
  }
};

template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false,
          int Width = 0, int Align = -1, char Pad = ' ',
          typename CE = unused_type, typename CC = unused_type>
struct alt_general_real_policies : fixed_real_policies<T, Precision, ForceSign, BlankSign, Width, Align, Pad, CE, CC> {
  static bool trailing_zeros(T) { return true; }
  static int floatfield(T n)
  {
    using namespace std;
    T abs_n = traits::get_absolute_value(n);
    return (!(floor(log10(abs_n)) < Precision) || abs_n < 1e-4) ?
      real_policies<T>::fmtflags::scientific : real_policies<T>::fmtflags::fixed;
  }
  static unsigned precision(T n)
  {
    using namespace std;
    T abs_n = traits::get_absolute_value(n), l;
    if ((l = floor(log10(1./abs_n))) > 1)
      return Precision + (l - 1);
    l = floor(log10(abs_n));
    return Precision > l ? Precision - (l + 1) : Precision - 1;
  }
};

template <typename T, unsigned Precision = 6, bool ForceSign = false, bool BlankSign = false,
          int Width = 0, int Align = -1, char Pad = ' ',
          typename CE = unused_type, typename CC = unused_type>
struct general_real_policies : alt_general_real_policies<T, Precision, ForceSign, BlankSign, Width, Align, Pad, CE, CC> {
  static bool trailing_zeros(T) { return false; }
  static int floatfield(T n)
  {
    if (traits::test_zero(n))
      return real_policies<T>::fmtflags::fixed;
    return alt_general_real_policies<T, Precision, ForceSign, BlankSign>::floatfield(n);
  }
  template <typename OutputIterator>
  static bool dot(OutputIterator &sink, T n, unsigned precision)
  {
    if (traits::test_zero(n))
      return true;
    return alt_general_real_policies<T, Precision, ForceSign, BlankSign>::dot(sink, n, precision);
  }
  template <typename OutputIterator>
  static bool fraction_part(OutputIterator &sink, T n, unsigned adjprec, unsigned precision)
  {
    if (traits::test_zero(n))
      return true;
    return fixed_real_policies<T, Precision, ForceSign, BlankSign>::fraction_part(sink, n, adjprec, precision);
  }
};

} } }
#endif

Boost Karma による C99 printf int

さて、Karma における符号付き整数のジェネレータである int_generator<T, Radix=10, force_sign=false> は、right_align(width, pad) ディレクティブとの組み合わせなどで、ほとんどの書式が可能であるが、以下のような書式の実現は困難である。

%04lld  0001
%+04lld +001
% 04lld  001

そして、int_policies のようなポリシークラスがあるわけではないので、書式を拡張するには int_generator そのものを継承する必要がある。

よって、C99 printf 書式指定のような符号付き整数の表示を可能とする c99_int_generator を用意してしまおう。これは以下のように使う。

#include <iostream>
#include <boost/range.hpp>
#include <boost/spirit/include/karma.hpp>
#include "boost-spirit-karma-int_generators.hpp"

namespace karma = boost::spirit::karma;
typedef karma::c99_int_generator<long long, 10, true, true, 6, 1, '0'> long_long_generator;

int main()
{
  long_long_generator long_long;
  long long v[] = { 0, 1, -100, };
  std::cout << karma::format(long_long % karma::lit(','),
                             boost::make_iterator_range(v, v+sizeof(v)/sizeof(v[0]))) << std::endl;
  return 0;
}

right_align(width, pad) を使うとどうしても以下のようになってしまう表示が、

# karma::right_align(6, '0')[karma::int_generator<long long, 10, true>()]
0000 0,0000+1,00-100

以下のように表示できる。

 00000, 00001,-00100

ここで使われているのは、以下の通りである。基本的には、浮動小数点でやったことと同様だ。

/*
  boost-spirit-karma-int_generators.hpp

  Copyright (C) 2013 Taiji Yamada <taiji@aihara.co.jp>

  Distributed under the Boost Software License, Version 1.0.
  (See http://www.boost.org/LICENSE_1_0.txt)
*/
#ifndef _boost_spirit_karma_int_generators_hpp_
#define _boost_spirit_karma_int_generators_hpp_

#include <boost/spirit/include/karma.hpp>

namespace boost { namespace spirit { namespace karma {

template <typename T, unsigned Radix = 10, bool ForceSign = false, bool BlankSign = false,
          int Width = 0, int Align = -1, char Pad = ' ',
          typename CharEncoding = unused_type, typename Tag = unused_type>
struct c99_int_generator : any_int_generator<T, CharEncoding, Tag, Radix, ForceSign> {
  typedef boost::mpl::int_<
    karma::generator_properties::countingbuffer
  > properties;

  template <typename OutputIterator, typename Attribute>
  static bool insert_int(OutputIterator &sink, Attribute const &attr, bool force_sign = ForceSign)
  {
    return sign_inserter::call(sink, (force_sign && !traits::test_negative(attr) && BlankSign),
                               traits::test_negative(attr), force_sign) &&
      int_inserter<Radix, CharEncoding, Tag>::call(sink, traits::get_absolute_value(attr));
  }

  template <typename OutputIterator, typename Context, typename Delimiter, typename Attribute>
  static bool generate(OutputIterator &sink, Context &context, Delimiter const &d, Attribute const &attr)
  {
    if (!traits::has_optional_value(attr))
      return false;
    if (!Width)
      return insert_int(sink, traits::extract_from<T>(attr, context), ForceSign) &&
        delimit_out(sink, d);
    bool r = false;
    if (Align < 0) { // left align
      karma::detail::enable_counting<OutputIterator> counting(sink);
      r = insert_int(sink, traits::extract_from<T>(attr, context), ForceSign) &&
        delimit_out(sink, d);
      while (r && int(counting.count()) < Width)
        r = karma::generate(sink, ' ');
    }
    else { // right align
      bool sign_val = false;
      Attribute n = attr;
      if (Pad == '0' && traits::test_negative(n)) {
        n = -n;
        sign_val = true;
      }
      karma::detail::enable_buffering<OutputIterator> buffering(sink, Width);
      {
        karma::detail::disable_counting<OutputIterator> nocounting(sink);
        r = insert_int(sink, traits::extract_from<T>(n, context), (Pad == '0' && ForceSign) ? false : ForceSign) &&
          delimit_out(sink, d);
      }
      buffering.disable();
      karma::detail::enable_counting<OutputIterator> counting(sink, buffering.buffer_size());
      if (sign_val || (Pad == '0' && ForceSign))
        r = sign_inserter::call(sink, (ForceSign && !sign_val && BlankSign), sign_val, ForceSign);
      while (r && int(counting.count()) < Width)
        r = karma::generate(sink, Pad);
      if (r) {
        buffering.buffer_copy();
      }
    }
    return r;
  }

};

} } }

#endif

符号無し整数ではこうした問題はない。

0 コメント
ゲストコメント認証用なぞなぞ:
キーボードのLから左に全部打って下さい。それを二回やって下さい。 ...