2013年12月19日木曜日

is_enumは概ね実装できていた / gcc2.7.2の特徴

昨日のコンパイルエラーは結構特殊な状況下でしか出ないことがわかった。
回避策もあったのでよしとしよう。

気を良くして昨日のコードを説明。

■状況
今はVxWorks5.4で動くコードを書くのが仕事である。で、それで動く最新のコンパイラがgcc2.7.2なのである。
もしかしたらWindRiverに言えば高いお金を払ってバージョンアップできるのかもしれない。あるいは自分でクロスコンパイラをビルドできるのかもしれない。でも情報が少ないので諦めている。
clangか何かでC++14コードをC89コードに変換できないかなぁ。

■gcc2.7.2(というかVxWorks5.4 + g++での開発環境)の特徴
この環境ではC++98規格には準拠していない。コンパイラのリリースが1989?だし・・・。

・namespaceが使えない
どうもまったく完全に実装されてないわけではないらしい。というのは、std名前空間にあるクラスを派生させるとICE(Internal Compiler Error)になるケースがあるからである。例えばstd::exceptionとか、自作std::integral_constantとか。しかし自前ではnamespaceを定義できない。

・整数定数がたまに使えない
どういう条件でダメになるのかわからないが、
class Hoge
{
    static bool const value = false;
};
とかがダメなケースがある(ICE発生)。なのでenumで代用することになる。
class Hoge
{
    enum
    {
        value = false
    };
};

・テンプレートは使える
以下に述べる通りバグは多数だが、必要最小限には使える。部分特殊化や整数引数も使えるので、ちょっとしたTMPも可能。placeholderなしのstd::function / std::bindっぽい機能も実装できた。

・メンバ関数テンプレートでは特殊化のために型を指定できない
以下のようなコードはNG。
struct Hoge
{
    template <typename T>
    void temp();
};

void doSomething()
{
    Hoge h;
    h.temp<int>();
}
これはstaticメンバであってもダメ。グローバル関数テンプレートならOK。
template <typename T>
void temp();

void doSomething()
{
    temp<int>();
}

・インナークラステンプレートはNG
以下は両方ともダメ。
template <typename T>
struct Outer
{
    template <typename U>
    struct Inner{};
};

struct Outer
{
    template <typename T>
    struct Inner{};
};
あとアウターがテンプレートのとき、インナーでそのテンプレート仮引数シンボルを解釈できないことがある。そのときはアウター側でtypedefすれば回避できる。
これがダメなのでstd::allocatorのrebindがダメになる。というかstd::allocatorの実装がヘン。よって組み込み向けのコンテナや文字列クラスなど、メモリ確保をプールやスタックにやらせる場合はすべてコンテナごと自作となる。まあstd::mapとか実装されてないし・・・。

・可変個数引数(...)とテンプレートを組み合わせると壮大にICEする
しかもコンパイルエラーになる箇所が意味不明なことが多い。あるいはコンパイルエラーにならずにバグったコードを吐くこともある。
# 可変個数引数とか言ってすみません。MSのsprintf_s()の代替とか要望されたのです。

・SFINAE使えない
まあ期待するほうが無理ってものだが、type_traits系を実装するのが大変すぎる(というか一部は無理ぽ・・・)。

・STL使えない
なんか色々と残念な感じ。iterator_categoryはOK。mapは使えない、listも標準に準拠しない動作がある。まあallocatorがダメな時点でSTLはほとんど全部書き直すのだけど。

・friendがおかしいことがある
どうおかしいのか詳細を忘れたが、とにかくなんかおかしいことがあった。大抵は期待通りになる。

・演算子オーバーロードの適用でICEになることがある
特にテンプレートが絡むとかなりの確率でICEになる。まったく無関係になりそうなものでも巻き込まれる。なのでVariant的な型を作るとき大変危険、というか作らないほうがよい。

■is_enumの実装
必要としていたのは完全版じゃなくて、

  • プリミティブ型
  • enum
  • クラス/構造体
が識別できればよかったので、そのように実装してみた。大変なのはenumとクラスの識別で、「メンバを保持する」的なSFINAEの書き下しが使えない以上、それ以外の手段で識別するしかない。

そこで「intに暗黙の型変換可能かどうか」で識別してみた。これだとoperator int()があるクラスは引っかかってしまうが、そこはもう諦めた。内輪の事情でoperator int()を実装することがほぼないとわかっていたので。

0 件のコメント:

コメントを投稿