EAWebKit勉強会というところに行ってきました。

EAWebKitは、Electronic Arts (以下EA)がオープンソースで公開しているライブラリです。SafariやGoogle Chromeで使われているWebKitをEAが改造?移植?したものということでこの名前のようです。注目すべきはWebKit以外にも様々なライブラリが含まれており、特にあのEASTLも含まれているというのです。

EASTLはC++標準化委員会でのpaper、N2271 EASTLで明らかにされた、EAによるSTL風のライブラリです(N2271の時点では実装は公開されていなかった)。

勉強会の午後の時間は、各々がやりたいことをやりましょうということだったので、私は黙々とEASTLのvector.hを読んでいました。


というわけで、vector.hから4つ取り上げます。

まず1つ目、ファイル冒頭です。「結局EASTLのライセンスって何よ?」という話がありましたが、EASTLでは各ファイル先頭にライセンスが書かれてあります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Copyright (C) 2009 Electronic Arts, Inc.  All rights reserved.
 
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
 
1.  Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.
2.  Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.
3.  Neither the name of Electronic Arts, Inc. ("EA") nor the names of
    its contributors may be used to endorse or promote products derived
    from this software without specific prior written permission.
(以下省略)

3条項版BSDライセンスです。BSDらしいという話があったので、家に帰ってから探してみました。OSIのThe BSD 3-Clause Licenseのテンプレートにピッタリはまります。他のEASTLの各ファイルも同様に3条項BSDライセンスですが、LGPLであるWebKitを同梱しているEAWebKit全体としてはLGPLなのでは、という懸念が拭えないのが私の気にするところです。


2つ目、ライセンスの次に現れるeast::vectorについての文章です。

///////////////////////////////////////////////////////////////////////////////
// This file implements a vector (array-like container), much like the C++
// std::vector class.
// The primary distinctions between this vector and std::vector are:
// – vector has a couple extension functions that increase performance.
// – vector can contain objects with alignment requirements. std::vector
// cannot do so without a bit of tedious non-portable effort.
// – vector supports debug memory naming natively.
// – vector is easier to read, debug, and visualize.
// – vector is savvy to an environment that doesn’t have exception handling,
// as is sometimes the case with console or embedded environments.
// – vector has less deeply nested function calls and allows the user to
// enable forced inlining in debug builds in order to reduce bloat.
// – vector<bool> is a vector of boolean values and not a bit vector.
// – vector guarantees that memory is contiguous and that vector::iterator
// is nothing more than a pointer to T.
// – vector has an explicit data() method for obtaining a pointer to storage
// which is safe to call even if the block is empty. This avoids the
// common &v[0], &v.front(), and &*v.begin() constructs that trigger false
// asserts in STL debugging modes.
// – vector::size_type is defined as eastl_size_t instead of size_t in order to
// save memory and run faster on 64 bit systems.
// – vector data is guaranteed to be contiguous.
// – vector has a set_capacity() function which frees excess capacity.
// The only way to do this with std::vector is via the cryptic non-obvious
// trick of using: vector<SomeClass>(x).swap(x);
///////////////////////////////////////////////////////////////////////////////

おそらく、こんな感じでしょうか。

このファイルはC++のstd::vectorによく似たvector(配列風のコンテナ)を実装している。std::vectorと比べ、主に次のような相違がある。

  • 性能向上のため、対の拡張関数がある(訳注:resizeの2番目の引数があるものとないものを用意していることでしょうか?)。
  • アラインメント要求のあるオブジェクトを保持できる。std::vectorで同様のことをやるには、移植性のない手法を使わないと実現できない。
  • デバッグ用としてメモリに名前を付けられる機能をネイティブに持っている。
  • 読み取り、デバッグ、可視化が容易。
  • コンソールや組込など例外処理のない環境を考慮。
  • 深くネストした関数呼出を避けている。このため、デバッグビルドでもインライン化(によるコードの膨張を抑えること)が可能。
  • vector<bool>はboolのvector。変な小細工はしていない。
  • 要素を保持するメモリの連続性はあるうえ、イテレータはただのTへのポインタ。
  • 先頭要素へのポインタを返すdata()メンバ関数がある。&v[0], &v.front(), &*v.begin()などと違って要素が空でも安全。
  • size_typeは、size_tではなくeastl_size_tのtypedef。メモリの節約かつ64ビット環境での高速化に繋がる(訳注:east_size_tは32ビット符号なし整数型)。
  • 要素の連続性の保証。
  • 余分なcapacityを減らすためのset_capacity()メンバ関数がある。std::vectorではvector<SomeClass>(x).swap(x);というわけわからないコードを書く必要がしかなかった。

例外処理やアラインメント、size_typeなどはなるほど納得です。一方、今となっては微妙なものもちらほらとあります。

  • dataメンバ関数とshrink_to_fitメンバ関数(vector<SomeClass>(x).swap(x);相当)がC++0xで追加されます(VC++ 2010などが対応済み)。N2271でC++標準化委員会へEASTLが紹介されたことも遠因にあるのでしょうか。
  • ネストした関数呼出をしないというのは、最適化能力の低いコンパイラにも有効でしょうが、正直PC向けならあまり気にする必要がないと思いました。
  • 要素の連続を謳っているのは、std::vectorにその保証がなかったC++98を意識しているように見受けられます(C++03でstd::vectorでも同じことが規定されました)。

なお、アラインメント対応やメモリへの名前付けなのはアロケータの役割なので、vector.hだけではあまりその周りの実装は分かりません。


3つ目、VectorBaseクラステンプレート(vectorの基底クラス)とvectorクラステンプレートのクラス定義です。

ここでの基底クラスの意義は、テンプレートでのコード膨張を抑制するためかなと思ったら違いました。答えはVectorBase手前のコメントに書いてあり、メモリ確保(VectorBaseコンストラクタ)と初期化(vectorコンストラクタ)を分離したいため、とのことです。

メモリ確保に成功したけど初期化に失敗した場合、vectorのコンストラクタで例外を投げることになります。この場合、VectorBaseはコンストラクトが完了しているのでVectorBaseのデストラクタが走り、メモリが解放されます。これをVectorBaseなしで実現するにはtry ‐ catchを書く必要があるので、例外無し環境で使えないコードになってしまうということだそうです。ようするに、try ‐ catchを書かずに済ませることで例外無し環境への対応が楽にになるというわけです(これにはちょっとしたオチがありますがそれは次回以降のエントリで改めて)。

VectorBaseクラス定義はtypedefやメンバ関数とメンバ変数の宣言など、特に変哲はありません(なお、EASTLのソースはメンバ関数の定義をその場に書かない派です)。だだし、以下のprotectedメンバに注目です。

176
177
178
179
180
protected:
    T*              mpBegin;
    T*              mpEnd;
    T*              mpCapacity;
    allocator_type  mAllocator;  // To do: Use base class optimization to make this go away.
192
193
194
195
protected:
    T*        DoAllocate(size_type n);
    void      DoFree(T* p, size_type n);
    size_type GetNewCapacity(size_type currentCapacity);

次はvectorクラスの定義です。これもstd::vector互換のtypedefとメンバ関数の並ぶ(一部eastl固有のものあり)特徴のないものです。と、言いたいところですが、typedefに混じって「publicな領域」にこんな宣言があります。

226
227
228
229
230
231
232
233
    using base_type::mpBegin;
    using base_type::mpEnd;
    using base_type::mpCapacity;
    using base_type::mAllocator;
    using base_type::npos;
    using base_type::GetNewCapacity;
    using base_type::DoAllocate;
    using base_type::DoFree;

基底クラスのメンバをusing宣言です。久々に見ましたこの構文、というところではなく、堂々とpublicにしているところが驚くべきところです。この「必要とあらばvectorの中身を自由にいじれるように公開している」という点がstd::vectorにはない(std::vectorはまねしない・できない)特徴だと思います。


4つ目、swapメンバ関数です。ほとんど関数の実装は素直なものですが、ところどころ特徴的なものがあります。その代表例としてswapを取り上げます。

1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
template &lt;typename T, typename Allocator&gt;
inline void vector&lt;T, Allocator&gt;::swap(this_type&amp; x)
{
    if(mAllocator == x.mAllocator) // If allocators are equivalent...
    {
        // We leave mAllocator and as-is.
        eastl::swap(mpBegin,     x.mpBegin);
        eastl::swap(mpEnd,       x.mpEnd);
        eastl::swap(mpCapacity,  x.mpCapacity);
    }
    else // else swap the contents.
    {
        const this_type temp(*this); // Can't call eastl::swap because that would
        *this = x;                   // itself call this member swap function.
        x     = temp;
    }
}

EASTLでは、アロケータxで確保したメモリがyで解放できるときx == yだそうです。コンテナのswapではアロケータは交換しないという方針だそうで、その場合は中身をコピーしてswapする実装です。


本当は、EAWebKit勉強会の全体的な感想なども書きたいのですが、これだけで分量がいっぱいなのでこのエントリではこれまでとします。


スポンサード リンク

この記事のカテゴリ

  • ⇒ EAWebKit勉強会に行ってきた ‐ vector.h