ラベル C99 の投稿を表示しています。 すべての投稿を表示
ラベル C99 の投稿を表示しています。 すべての投稿を表示

2011年12月28日水曜日

【C++11】 Designated Initializer 風な何か Boost.Fusion 対応

はい、という訳で当初目標であった、Designated Initializer 風な何かの Boost.Fusion 対応ができました。実際のコードは https://github.com/yak1ex/cpp_stuff にあります。GCC 4.7 専用です。書いた後で言うのもなんですが、まぁ余程でかい構造体でなければ全部初期化子書いた方が早いだろ、という気はします。あと最初っからやる気があるなら Boost.Parameter 使えばいいんじゃね?、みたいな。配列の場合は意味があるケースはあるかもしれません。特定の添字だけじゃなくて range<5, 12>() = 1 と書くと 5 ~ 11 が 1 で初期化とかだとより意味があるケースが出てくるかも。

テストコード

#include <vector>
#include <iostream>

#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/define_struct.hpp>
#include <boost/array.hpp>
#include <boost/fusion/include/boost_array.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/fusion/include/boost_tuple.hpp>
#include <boost/fusion/include/adapt_assoc_struct.hpp>

#include <boost/fusion/include/out.hpp>

#include "dinit.hpp"

// Helper function

template<typename T>
void output(const T& v)
{
 for(auto val : v) {
  std::cout << ' ' << val;
 }
 std::cout << std::endl;
}

// Type definition and adapter

// Don't work for BOOST_FUSION_DEFINE_STRUCT becaues conflict with constructor

struct employee
{
 std::string name;
 int age;
 double rate;
};

BOOST_FUSION_ADAPT_STRUCT(
 employee,
 (std::string, name)
 (int, age)
 (double, rate)
)

namespace keys {
 struct name;
 struct age;
 struct rate;
}

struct employee_
{
 std::string name;
 int age;
 double rate;
};


BOOST_FUSION_ADAPT_ASSOC_STRUCT(
 employee_,
 (std::string, name, keys::name)
 (int, age, keys::age)
 (double, rate, keys::rate)
)

// Don't work for plain array because brace-enclosed initializer is required.

int main(void)
{
 using yak::util::dinit::di;
 using yak::util::dinit::idx;
 using yak::util::dinit::idx_;
 using yak::util::dinit::key;
 using yak::util::dinit::key_;

// For sequence with list constructor

 std::vector<int> v = di(idx<10>() = 1, idx<0>() = 2, idx<2>() = 3, idx<3>() = 4);
 output(v);

 std::vector<int> v2 = di(idx_<10>(1), idx_<0>(2), idx_<2>(3), idx_<3>(4));
 output(v2);

// For Fusion sequence with idx

 employee e = di(idx<2>() = 3.3, idx<1>() = 33);
 boost::fusion::out(std::cout, e); std::cout << std::endl;

 boost::array<int, 5> ar2 = di(idx<3>() = 4);
 output(ar2);

 boost::tuple<std::string, int, double> t = di(idx<1>() = 5);
 boost::fusion::out(std::cout, t); std::cout << std::endl;

// For Fusion sequence with tag

 employee_ e2 = di(key<keys::rate>() = 3.3, key<keys::age>() = 33);
 boost::fusion::out(std::cout, e2); std::cout << std::endl;

 employee_ e3 = di(key_<keys::rate>(3.3), key_<keys::age>(33));
 boost::fusion::out(std::cout, e3); std::cout << std::endl;

 return 0;
}

出力

 2 0 3 4 0 0 0 0 0 0 1
 2 0 3 4 0 0 0 0 0 0 1
( 33 3.3)
 0 0 0 4 0
( 5 0)
( 33 3.3)
( 33 3.3)

2011年12月25日日曜日

【C++11】 Designated Initializer 風な何か シーケンス用

元々、【C++11 Advent Calendar 2011】17日目 小ネタ集 後編用にネタコード作ろうと思ってちょこちょことやっていた訳ですが間に合わなさそうだったので、16進浮動小数点定数やってみたら割とさくっとできたのでそっちだけ記事に書いたわけですが。とりあえずそれっぽいものができたので公開してみます。https://github.com/yak1ex/cpp_stuff の、dinit.hpp, index_tuple.hpp, dinit.cpp になります。とりあえず GCC 4.7 -std=c++0x 限定です。自分が書いてるコードが悪いような気がしますが。後どこかで dangling reference ってる気もします。

#include <vector>
#include <iostream>

#include "dinit.hpp"

int main(void)
{
 using yak::util::dinit::di;
 using yak::util::dinit::idx;
 using yak::util::dinit::idx_;

 std::vector<int> v = di(idx<10>() = 1, idx<0>() = 2, idx<2>() = 3, idx<3>() = 4);
 for(auto val : v) {
  std::cout << val << std::endl;
 }
 std::vector<int> v2 = di(idx_<10>(1), idx_<0>(2), idx_<2>(3), idx_<3>(4));
 for(auto val : v2) {
  std::cout << val << std::endl;
 }
 return 0;
}

出力

2
0
3
4
0
0
0
0
0
0
1
2
0
3
4
0
0
0
0
0
0
1

見ての通り、di() の中で idx<添字>() = 値、あるいは idx_<添字>(値) を並べるとそれに沿って初期化されます。それに沿って初期化されたものによってコピーないしムーブ初期化されます、の方が正確かもしれません。ただ -O2 の段階でも main() 関数内に初期値(2,0,3,4,0,...,0,1)の push が展開されていたのでほぼ初期化相当と見ていいんじゃないでしょうか。↑は vector ですが、initializer-list constructor 持ってるやつ(deque, list, forward_list)なら動いているようです、多分。配列や array にも使えないという残念仕様ですが。また、普通同じ添字のものがあったら最後が有効になると思うでしょうが、最初のものが有効になります。これは直そうと思えばすぐ直せそうですが。

コード的には di() でテンプレート変換演算子を持っているクラスの一時オブジェクトを作成、テンプレート変換演算子中で list initialization している形になります。できればせめて di{} にしたかったのですが早々に諦めました。イメージ的には↓のような感じでこれに実際に実現するためのあれやこれやをつけた感じです。

template<typename Tuple, std::size_t ... idxN>
struct Initer
{
 constexpr Initer(const Tuple& t) : args(t) {}
 template<typename U>
 constexpr operator U()
 {
  return { /* ... */ };
 }
 Tuple args;
};

template<typename ... T>
constexpr const auto di(T&& ... t) -> /* Initer<std::tuple<...>, ...> */;

2011年12月17日土曜日

【C++11 Advent Calendar 2011】17日目 小ネタ集 後編

本記事は C++11 Advent Calendar 17日目の記事です。前編だけだとネタが薄いんじゃないかと思って簡単に調べたネタになります。
さて、Advent Calendar やられるくらい C++11 さんは期待されているわけですが、一方の C99 さんのネグレクトされ具合は割とひどいです。が、大丈夫。C++11 では C99(というより次期規格の C1X)との互換性が向上しています。この後編では C++11 に取り込まれた C99 の機能を簡単に眺めてみましょう。後編においても他人のふんどしで相撲をとるべく seclan@dll.jp さんによるプログラミング言語 C の新機能の構成に合わせて比較していきます。なお、基本的に俺調べとなりますので信頼度は低いです。
  • 1 概略
    • 1.2 新しい予約語 (n3291 2.12)
      inline は既存、restrict は無し、_Bool, _Complex は代替が既に存在するのでなし、_Imagnary はな し
    • 1.3 新しいヘッダファイル (n3291 17.6.1.2)
      <choge> 形式を加えて C99 で追加されているものは全て追加されています。なお、<cstdalign>、<cuchar> は C1X との同期です(<cuchar> は C Unicode TR こと ISO/IEC TR 19769:2004 が参照文書になっています)。
  • 2 C プリプロセッサ
    • 2.1 新しいコメント表記方法
      // のことなので既存です。
    • 2.2 空引数を許された関数型マクロ呼出し (n3291 16.3/4)
      マクロで a(3,,5) とかが書けます。追加されました。
    • 2.3 可変個数引数を持つマクロ定義 (n3291 16.3/4,5, 16.3.1)
      __VA_ARGS__ のことです。DigitalGhost さんの記事を参照。
    • 2.4 あらかじめ定義されているマクロ名 (n3291 16.8)
      • __STDC_VERSION__
        C 言語仕様のバージョンですが定義の有無から実装依存です。
      • __STDC_ISO_10646__
        定義されている場合には wchar_t がいつまでの ISO/IEC 10646 (Unicode 相当)に従っているかを表す日付になります。
      • __STDC_HOSTED__
        C99 にもあるので参照元ページでの説明漏れだと思われます。hosted environment かどうかを示します。
      • __STDC_MB_MIGHT_NEQ_WC__
        C1X からです。C++11 では 1 固定。1 だと wchar_t のエンコーディングについて basic charcter set 範囲が char での表現と違っているかもしれないという意味です。
      • __STDC_IEC_559__
        なし?ただし以前より numeric_limits に is_iec559 があります。
      • __STDC_IEC_559_COMPLEX__
        なし?
      • C++11 で追加された定義済みマクロは他にもありますが C との比較の観点なので省略します。
    • 2.5 プログラミング言語 C 標準プラグマ
      一部 #pragma を標準化しようというもので #pragma STDC hogehoge という形式になります。C++11 にはなさそげです。浮動小数点絡みはなくてもいいんでしょうか……
    • 2.6 単項演算子 _Pragma (n3291 16.9)
      追加されました。Flast さんの記事を参照ください。
    • 2.7 プリプロセッサ式における整数型 (n3291 16.1/4)
      intmax_t 型、uintmax_t 型で計算されます。C99 に合わせて変更されています。
    • 2.8 文字列定数とワイド文字列定数の結合 (n3291 2.14.5/13)
      例えば "foo" L"bar" が L"foobar" になります。C99 に合わせて変更されていますが、C++11 では文字列の種類が増えたので拡張されています。
      • 両方が同じ prefix → その prefix のまま
      • 片方が prefix ありで、他方が prefix なし → prefix あり
      • u8 と L が隣接 → ill-formed
      • 他は実装依存
  • 3 字句
  • 4 配列
    • 4.1 可変長配列
      int a[n]; (n は非定数式)とか書けるやつですが C++11 には有りません。
    • 4.2 構造体中の 0 長配列メンバ
      構造体末尾に int a[] とか置いておき malloc() 等でメモリを確保する際に余分に取って a[5] とかでアクセスできるものですが、C++11 にはなさそうです。
    • 4.3 配列要素中の記憶/型修飾子
      int n[static 5] とか書けるそうです。C++11 にはありません。構文的には属性を使って int n[5][[static]]; みたいに書ける処理系があってもいいかもしれません。
  • 5 整数型
    • 5.1 Bool 型
      bool があるので無用です
    • 5.2 long long int 型 (n3291 3.9.1/2,3)
      追加されました。
    • 5.3 long long int 型の定数 (n3291 2.14.2)
      LL とか ULL サフィクスです。追加されました。
    • 5.4 整数除算 (n3291 5.6/4)
      C99 と同じくいずれかのオペランドが負の場合でも 0 方向に切り捨てるよう規定が出来ました。5 / -2 は -2 になります。
  • 6 複素数型
    complex があるので基本省略、……なのですが C99 の hoge_Complex 型とメモリ配置の互換性をとるためでしょう、
    1. z を std::complex<T> として reinterpret_cast<cv T(&)[2]>(z)[0] が実部、reinterpret_cast<cv T(&)[2]>(z)[1] が虚部になること
    2. a を cv std::complex<T>* として、reinterpret_cast<cv T*>(a)[2*i] が a[i] の実部を、reinterpret_cast<cv T*>(a)[2*i + 1] が a[i] の虚部となること
    という規定が <complex> に追加されています。
  • 7 文法一般
  • 8. 標準ライブラリ(拡張)
    C99 とは別の話で余談になりますが、Concurrency 関係でデータ競合についての補足が結構追加されていたりします。
    • 8.1 ctype.h (n3291 21.7)
      C99 同様に isblank() が追加されています。
    • 8.2 float.h (n3291 18.3/3)
      C99 同様に DECIMAL_DIG と FLT_EVAL_METHOD が追加されています。
    • 8.3 math.h (n3291 26.8)
      多いので詳細は省略しますが 
      • C99 で追加されたマクロは同様に追加されています。ただし C99 で追加された関数型マクロは C++11 ではオーバーロード関数になっています。
      • 標準プラグマは前述の通りありません。
      • hogef()、hogel() に相当する hoge() のオーバーロードが(C++98から)あります。
    • 8.4 stdarg.h (n3291 18.10)
      C99 同様 va_copy が追加されています。
    • 8.5 stdio.h (n3291 27.9.2)
      C99 で追加されたものは書式文字列を含めて全て追加されています。snprintf(),vsnprintf(), vscanf() ファミリーも同様です。
    • 8.6 stdlib.h (n3291 18.5, 18.10, 20.6.13, 21.7, 25.5, 26.8)
      C99 同様 _Exit(), strtof(), strtold(), strtoll(), strtoull(), atoll(), llabs(), lldiv(), ldiv_t 型, lldiv_t 型 が追加されています。abs(), div() は long int, long long int に対するオーバーロードも追加されています。
    • 8.7 wchar.h (n3291 21.7)
      C99 同様 vwscanf(), vswscanf(), vfwscanf(), wcstof(), wcstold(), wcstoll(), wcstoull() が追加されています。
    • 8.8 wctype.h (n3291 21.7)
      C99 同様 iswblank() が追加されています。
  • 9. 標準ライブラリ(新規)
    基本的に追加分のヘッダは全て存在しますが、一部変更があります。
    • 9.1 complex.h: 複素数 (n3291 26.4.10)
      単純に <complex> を #include するだけになっています。
    • 9.2 fenv.h: 浮動小数点環境 (n3291 26.3)
      スレッド毎に環境が用意されること、スレッド生成時には親スレッドの環境を引き継ぐことが追加で規定されています。
    • 9.3 inttypes.h (n3291 27.9.2/3,4)
      書式指定用マクロ(PRIdFAST8 とか)は__STDC_FORMAT_MACROSの有無に関わらず常に定義されるようになっています。また、abs(), div() が intmax_t に対してもオーバーロードされます。
    • 9.4 stdbool.h (n3291 18.10/8)
      マクロ bool, true, false は定義されません。__bool_true_false_are_defined だけが定義されます。
    • 9.5 stdint.h (n3291 18.4)
      __STDC_CONSTANT_MACROS, __STD_LIMIT_MACROS に関わらずマクロも定義されます。ちなみに <atomic> で <cstdint> にある全ての型について atomic<T> が特殊化されていることが規定されています。
    • 9.6 tgmath.h: 型総称数学関数 (n3291 26.8/1,2)
      C言語的には驚きの tgmath.h さん(擬似的に数学関数のオーバーロードを実現します)ですが、C++ 的にはオーバーロードがあるので <ccomplex> と <cmath> を単純に #include するだけです。
    • stdalign.h (n3291 18.10/7)
      C1X との同期です。n3291 だと alignas は定義されず __alignas_is_defined だけが定義されるように読めるのですが、alignof はどこに行ったのでしょうか?C1X DIS だと alignof 系もあるようなのですが……
    • uchar.h (n3291 21.7)
      C1X にもありますが規格上は C Unicode TR ISO/IEC TR 19769:2004 を参照文献としています。char16_t, char32_t は C++11 言語側にあるので __STDC_UTF_16__, __STDC_UTF_32__, mbrtoc16(), c16rtomb(), mbrtoc32(), c32rtomb() が定義されています。
  • A. 付録
    • A.1 strftime 書式指定子 (n3291 20.11.8)
      <ctime> は C の time.h と同じだと書いてありますので C99 と同様に拡張されています。"strftime supports the C conversion specifiers C, D, e, F, g, G, h, r, R, t, T, u, V, and z, and the modifiers E and O." と注釈もついています。
    • A.2 printf の書式 (n3291 27.9.2)
      特に規定がないので C99 と同じだと思われます。例えば %lf さんが公式に許可されるほか、std::size_t に対する z (%zu みたいに使う)、std::ptrdiff_t に対する t (%td みたいに使う)、そして定数としては使えないのに16進浮動小数点数としての出力 %a ないし %A などが追加されます。
お疲れ様でした。どうでしょうか。「C に合わせた変更も結構多いな」という印象ではないでしょうか。とりあえず C にあって未だ C++ に無い主なものとしては
  • restrict
  • 標準プラグマ
  • 浮動小数点定数の 16 進数表記
  • 可変長配列
  • 指示付きの初期化子 (Designated Initializer)
辺りでしょうか。ないなら作れ、「『欠・即・産』。これが、Boost に魅入られた C++er が共有した真の正義だったはず」ってことで、浮動小数点定数の 16 進数表記、指示付きの初期化子 (Designated Initializer)辺りはライブラリである程度補完できるかもしれません。Designated Initializer は相当妥協や無理矢理をやらないと厳しそうですが「浮動小数点定数の 16 進数表記」は割とすんなり近いものは書けそうということで簡単にでっちあげてみました。オーバーフロー、エラーチェック、型の考慮、型サフィクス非対応等適当ですが大体できそうというのは見て取れるのではないかと思います。ただし __builtin_scalbn() を使って手を抜いているので GCC 専用です。g++ 4.7.0 20111203 だと user-defined literal で、g++ 4.5/4.6 だと user-defined literal の呼び出しができないのでそこだけ手書きした状態で通ります。例えば下記コードで 31.25 と表示されます。
// for g++ 4.7
#include <iostream>
#include "hexfloat.hpp"

int main(void)
{
    using yak::util::hexfloat::operator "" _hf;

    std::cout << 0xF.ap+1_hf << std::endl;

    return 0;
}
C++11 Advent Calendar 17日目はこれで終了です。明日 18 日目は id:ignisan(@ignis_fatuus) さんです。

追記(2011/12/28)

Fusion さんの助けを借りつつ無理矢理 Designated Initializer っぽいものもでっちあげてみました。http://yak-ex.blogspot.com/2011/12/c11-designated-initializer-boostfusion.html