Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

2013-04-29

例外中に例外を投げるとか二重例外はエラーという俗説について

いよいよC++の参考書の執筆も例外にまで到達した。例外は、規格の文面量だけで言えば短いが、詳細を解説するのは難しい。なにせ、まともに日本語で解説している本は皆無だからだ。ついでに、規格の文面のバグも発見した。これはすでに報告済みなので、次のC++規格では修正されるはずだ。

ちなみに、"C++ 例外"で検索して出てくる情報の大半が間違っているか、十分な詳細を解説していない。責任は規格違反な実装、特にMSVCにある。MSVCの挙動が全てだと信じる愚者が、規格を参照せずMSVCの挙動をもとに解説を書いているからだ。

たとえば、例外をハンドルしていない状態でオペランドのないthrow式を実行すると、std::terminateが呼ばれる。

int main()
{
    throw ; // std::terminateが呼ばれる
}

あるC++解説サイトでは、何故かstd::bad_exceptionが投げられると書いてある。これはおそらく、悪名高い不自由なC++コンパイラーであるMSVCの規格違反の実装をもとにしているのだろう。規格を参照せず、実装の挙動に頼るからこういう間違いを犯すのだ。コンパイラーはタイプミスなどの誤りを検出するためのものであって、規格検証のためのツールではない。ただし、Clangならだいぶいい線をいっているのだが。

俗に、例外中に例外を投げるとエラーとか、二重例外はエラーとか呼ばれていることについても誤解が多い。そこで、今回は二重例外について説明する。

まず、規格には「二重例外」なる言葉はない。「例外中に例外を投げる」というのはやや近いが、あまりに簡潔化し過ぎていて重要な意味が失われている。実は、現行のC++11の文面にはバグがある。そこで修正された最新のドラフトから引こう。ただし、これにもまだバグがある。

If the exception handling mechanism, after completing the initialization of the exception object but before activation of a handler for the exception, calls a function that exits via an exception, std::terminate is called

例外処理機構が、例外オブジェクトの初期化が完了した後、例外に対応するハンドラーが起動する前に、例外によって抜け出す関数を呼んだ場合、std::terminateが呼ばれる。

15.1 [except.throw] paragraph 7

1503. Exceptions during copy to exception object

まず、「エラー」の詳細であるが、「std::terminateが呼ばれる」ということだ。これは、実際のところ、C++におけるコンパイル時には検出できないコード上の誤りといってもいい。std::terminateは極めて限定的な条件でしか呼ばれず、その条件とは、ほとんどの場合コード上の誤りに起因するものだからだ。

さて、「例外中」の詳細は、具体的には、「例外オブジェクトの初期化が完了した後、例外に対応するハンドラーが起動する前」だ。問題となる条件は、「例外によって抜け出す関数を呼んだ場合」だ。これにより、例えば以下のようなコードは、std::terminateを呼び出す。

// デストラクターが例外を投げるクラス
struct C
{
    // デストラクターに明示的な例外指定がない場合、この文脈では暗黙にthrow()になるため
    // デストラクターの外に例外を投げるには例外指定が必要
    ~C() noexcept( false ) { throw 0 ; }
} ;

int main()
{
    try 
    {
        C c ;
        throw 0 ;
        // C型のオブジェクトcが破棄される
        // 例外中に例外が投げられたため、std::terminateが呼ばれる
    }
    catch( ... ){ }
}

デストラクターのnoexcept(false)に注意。C++11では、ユーザー定義のデストラクターに明示的な例外指定がない場合、暗黙の特別なメンバー関数に適用される暗黙の例外指定を受け継ぐ。これにより、デフォルトではthrow()となり、無例外関数だとみなされてしまうので、C++11でデストラクターの中から外に例外を投げたい場合は、明示的に例外指定を書かなければならない。

ただし、クラスCが以下のように書かれていた場合、std::terminateは呼ばれない。

struct C
{
    ~C()
    {
        try { throw 0 ; }
        catch ( ... ) { } // デストラクターの外に例外を投げない
    }
} ;

これは、「例外によって抜け出す関数を呼ぶ」という条件に合致しないからだ。

したがって、例外中に例外とか二重例外などという言い方は正しくない。例外が投げられ、まだハンドラーによってとらえられていないとしても、例外は問題なく使える。

初期化が完了した後という点にも注意。たとえば、以下のコードはstd::terminateを呼ばない。

struct X
{
    X() { throw 0 ; }
} ;

int main( )
{
    try
    {
        // OK、初期化式の評価中の例外
        // 例外オブジェクトの型はint
        throw X() ;
    }
    catch( X & exception ) { }
    catch( int exception ) { } // このハンドラーでとらえられる
}

これは、X型の例外オブジェクトの初期化中の例外なので、条件に当てはまらない。ただしこの場合、例外オブジェクトの型はXではなくintになる。

ただし、初期化が完了した後という点にも注意。たとえば、以下のコードでは、C++実装次第でstd::terminateが呼ばれる。

struct X
{
    X( X const & ) { throw 0 ; }
} ;

int main( )
{
    try
    {
        // 実装がコピーを省略しない場合、std::terminateが呼ばれる
        // コピーコンストラクターの実行は評価完了後
        throw X() ;
    }
    catch( ... ) { }
}

もし、C++11の実装が、例外オブジェクト構築の際に、Xのコピーコンストラクターを呼んだ場合、std::terminateが呼ばれる。賢い実装ならば、このような文脈ではコピーの省略ができるが、コピー、ムーブの省略は規格上許されているが保証されていないので、省略されない場合、std::terminateが呼ばれる。

ところで、冒頭で現在の規格の文面にはバグがあると書いた。この文面を厳密に解釈すると、以下のコードではstd::terminateが投げられる。

// 例外によって抜け出す関数
void f() { throw 0 ; }

struct C
{

    ~C()
    {
        // 例外によって抜け出す関数を呼ぶ
        try { f() ; }
        catch( ... ) { }
    }
} ;

int main()
{
    try 
    {
        C c ;
        throw 0 ;
    }
    catch( ... ){ }
}

このコードは、現行規格の文面を厳密に解釈すれば、std::terminateを呼び出すはずである。これはどう考えてもおかしいし、既存の実装もそういう実装にはなっていない。これは規格の文面の誤りであり、C++参考書を執筆している際に発見した。すでに報告してあるので、次の規格改定までには修正されるはずだ。

と、このように、規格の文面を一字一句、厳密に解釈して書かれたC++の参考書は、もうすぐ、例外まで書き終わる。プリプロセッサーは私の脳内規格ではobsoleteなので解説しないのは当然だから、もうすぐ公開できる。

それにしても長かった。参考書の執筆中に、いくつものGCCのバグや規格の文面のバグを見つけた。まだもう少しかかるが、この参考書を執筆することで、すでにC++にはだいぶ貢献したと思う。

2013-04-26

Mark Shuttleworth: スーパーカリフラジリスティックエクスピアリドーシャス・スコーピオンフィッシュだよね

Mark Shuttleworth » Blog Archive » The Supercalifragilisticexpialidocious Scorpionfish. Not.

Ubuntu 13.10のコードネームは、saucy salamanderに決定したようだ。

今日のUbuntu 13.04, Raring RingtailのリリースとUbuntuコミュニティの拡大に乾杯。この数ヶ月のフィードバックはすばらしいものだった。Rickの開発チーム率いるパフォーマンスと品質が世界的に認められ、多岐にわたるプラットフォームとコミュニティで賞賛を浴びている。

ローリングリリースの要望の声を抑えるため、我々は品質とパフォーマンスの自動化テストを大きく進歩させた。これからは、開発中のUbuntuをローリングリリースと呼んで差し支えない。これは日々の品質テストのすばらしい成果によるものだ。これこそ我々が受け入れた価値であり、プロジェクトはさらに素晴らしくなるだろう。

「リンリン」という音を13.04のコードネームにすり入れたことは、まあ、言語工学上の勝利と言えるだろうね。私もしばらくはついうかれてしまったよ。年二回恒例のお祭りの命名は、まあ、Rickのローリング・リリース・ロデオ[訳注: Rick’s Rolling Release Rodeo]には負けるだろうがね。この数ヶ月の旅行していたので、命名のための研究時間がなかなかとれなかった。私はいつも、数週間ほどの週末の夜に辞書を流し読みして過ごすのだが(なかなか圧倒的だよ。私は最近、rugose[1 しわのある[寄った];うねのある. 2 《植物》〈葉が〉しわの多い.]という言葉の意味を知ったよ。

そういうわけで、今日の私は命名の案がいささか不足している。もちろん、名前は決めてあるが、いつものような自己言及的な意味を含められなかった。

というわけで、そんなに深く考えたわけでもないけど、この次6ヶ月のマスコット君を紹介しようかな。saucy salamander[訳注: 生意気オオサンショウウオ]だよ。

オオサンショウウオはこの自然界におけるもっとも魔術的な生物だ。オオサンショウウオが生息しているという事は、未開の土地であることを意味する。これは今まさに新世界に興隆しつつあるUbuntu Touchの新しいアプリケーション、新しいSDK、豪華で綺麗なインターフェースを表現するのにぴったりだ。オオサンショウウオは透明で清潔な上流[訳注:川の上流を意味するupstreamと、ソフトウェア開発元を意味するupstreamをかけてある]で泳ぐ。これはUbuntuがモバイル環境で構築されつつあることにぴったりだ。これはつまり、多大なコミュニティが集まって、携帯からPCまで、ひとつの統一されたエクスペリエンスを提供し、世界中の開発者たちが、高速で新鮮で自由な多数のスタイリッシュなアプリを提供しようとしていることに、「ありがとう」と言っているのだ。我々は生意気でもある。人生は怠けたり呆けたりするにはあまりに短い。我々の仕事が我々の遊びだ。我々は多数の利用者のためにすばらしいことを成し遂げているのであり、我々はあらゆる環境に我々の方法で対応することができているのだ。

ハッピー・リリース・デイ エブリワン! これから生意気な開発サイクルが始まるよ。

相変わらずMark Shuttleworthの文章は読みづらいし、翻訳も難しい。何が難しいかというと、普段見慣れない珍しい言葉がたくさんでてくるのだ。今回、Mark Shuttleworthはコードネームを決定するために、辞書を流し読みすると書いてるが、見慣れない言葉がたくさんでてくるのはそのためだろうか。

ちなみに、Mark Shuttleworthのブログ記事のタイトルの元ネタは、メリー・ポピンズというミュージカル映画のなかの楽曲の名前である。

スーパーカリフラジリスティックエクスピアリドーシャス - Wikipedia

私の環境では、Ubuntu 13.04は再起動するとブート途中でカーネルパニックを起こすのだが、品質は一体どこに放り捨ててきたのだろうか。それから、ブート時間も少し伸びたような気がする。

Ubuntu 13.04の感想

Ubuntu 13.04の感想だが、12.10よりさらに悪くなっている。おそらくLTSの12.04にとどまっていたほうが賢明だと思われる。

なぜか再起動するとカーネルパニックを起こす。今のところ再現率100%だ。シャットダウンしてから起動すれば、問題なく起動する。

プロプラドライバーのnvidia-currentがnvidia-304を指している。しかし、どうもnvidia-310が推奨ドライバーのようだ。

日本語フォントより簡体字フォントが優先されるのは相変わらず。12.04で修正されたと思ったのに12.10で問題が復活してそのままだ。.fonts.confを設定して自力で直した。

追記:ウインドウをドラッグして動かすとスムーズに動かない。確か11.10がこんな挙動で、12.04あたりで直されたはずだが。

2013-04-25

DMCAは1972年2月15日以前に州法でライセンスされた歌には無効

Court denies Grooveshark DMCA protection for songs like “Johnny B. Goode” | Ars Technica

DMCAにより、WebサイトはいわゆるSafe Harborな地位を手に入れた。たとえばユーザーがアップロードする著作物を受け付けて公開するようなWebサイトでは、DMCA取り下げ要求を処理すれば、Webサイト自体が著作権侵害に問われることはない。これにより、動画サイトなどは、ユーザーがアップロードする動画を、著作権侵害の恐れなく公開できる。もし、ユーザーが著作者ではなく許諾も得ていない他人の著作物を侵害する動画を上げた場合(大量のユーザーが大量にアップロードするので、到底事前にチェックできない)でも、DMCAが送られ次第削除すればいいのだ。

ところが、アメリカ合衆国というのは州法と連邦法がごちゃごちゃで、どうも裁判の結果、DMCAには歴史的な経緯のみ解決問題が色々とあり、法解釈の結果、1972年2月15日以前に州法でライセンスされた歌は、DMCAの適用範囲外であるという。

つまり、昔の歌にはSafe Harborが働かないので、Webサイト自体が著作権侵害に問われる。

しかし、DMCAが適用されないという事は、DMCA取り下げ要求も無効という事になり、権利者としても正式な訴訟を起こさなければならなくなるだろう。

ちなみに、YouTubeなどの大手動画サイトには影響しない。なぜならば、このような大手Webサイトは、すでに音楽パブリッシャーと独自の同意をしていて、パブリッシャーの判断で勝手に動画を取り下げていいような仕組みを作っているからだ。

著作権法はどの国も歴史的経緯から無駄に複雑になっているものであるが、アメリカ合衆国は特にひどいと思う次第。

2013-04-24

マイクロソフトがClang開発者に求人中?

Clangの開発者メーリングリストに、Microsoftから依頼を受けたリクルーターが現れて求人広告を投下している。

The cfe-dev April 2013 Archive by date
[cfe-dev] Microsoft Job Opportunity

なんでも、MicrosoftがClang開発者を雇いたがっているらしい。要件はC++開発経験3年以上、Clang開発者、Clangの機能実装経験とClang AST経験。

雇用期間は6ヶ月で延長の可能性もありとなっている。

一体何をするつもりなのだろうか。

単にC++コンパイラー開発者が欲しいというわけではないだろう。そもそも、Microsoft WindowsにはろくなC++コンパイラーがない。おそらくClangを使って何かをやるに違いない。

ただし、雇用期間が6ヶ月というのも気になる。何か短期の実験的プロジェクトか、あるいはClangの社内教育などだろうか。

すでに、Visual StudioのIntelliSenseに使われているC++パーサーは、EDGのフロントエンドを使っていることがVisual C++ Teamブログで明らかにされている。

Rebuilding Intellisense - Visual C++ Team Blog - Site Home - MSDN Blogs

もはや、C++コンパイラーを自社開発することで技術的優位に立てる時代ではないのだ。とすれば、MSがClangを使うのはおかしくもない。というより、MSVCは悲惨すぎて、とてもではないがC++と呼べるシロモノではない。いっそのことClangにでも変えてしまったほうがいいのではないか。下位互換性はぶっ壊すが、まあ痛みなくして得るものなしだ。不自由ソフトウェアのつらいところよ。

まあ、主に不自由ソフトウェアと制限コンピューターを開発している会社のことはどうでもいいか。

2013-04-23

Bristol会議でC++14ドラフト入りが決まった提案一覧

C++14が来年に迫っている。先日行われたBristol会議の投票により、C++14のドラフトに正式に入ることが決まった提案の一覧を紹介する。ここで紹介されている提案は、すべてC++14入りが決定している。この先も細かい変更があるかもしれないが、基本的には変わらない。来年2014年に正式に発行される予定のC++14におそらく入る。

また、論文はすでに公開されている論文を、議論の結果修正したものなので、すでにこのブログでは全て解説しているはずだ。以下から探してくれたまえ。

本の虫: C++WG 2013-03-pre-Bristol mailingの簡易レビュー
本の虫: C++ 2013-01 mailingの簡易レビュー
本の虫: 2012-11 post-Portland mailingの簡易レビュー
本の虫: 2012-09 pre-Portland mailingのあまり簡易ではないレビュー
本の虫: 2012-02 post-Kona mailingの簡易レビュー
本の虫: 2011-01 pre-Kona 2012 mailingの簡易レビュー

参照している論文は、Post-Bristol mailingで公開される予定だ。最近C++WGの方針が変わり、このインターネット時代では、すでに書き終えた論文は一刻も早く公開すべきであるということになり、C++財団のWebサイトで公開されている。

[吐き気を催すPDF] N3651: Variable Templates (Revision 1)

変数の宣言もテンプレート宣言できるようにする提案。

今のC++では、型をパラメータ化した定数の宣言が難しい。これは、変数をテンプレート化できないためである。

constexpr double pi = 3.14 ;

この例では、piの型はdoubleである。したがって、piを使う文脈では、どこでもdouble型が使われてしまう。ではもし、floatとかintとかmy_floating_pointとかの型に対する円周率の定数を定義したければどうすればいいのだろうか。

クラステンプレートのメンバーを使うとか、constexpr関数テンプレートを使うとかいう手もある。

// 関数テンプレート
template < typename T >
constexpr T pi()
{
    return static_cast<T>(3.1415926535897932385) ;
}

// 明示的特殊化
template < >
constexpr my_floating_point pi< my_floating_point > ()
{
    return /* ユーザー定義クラスを返す実装 */ ;
}

関数テンプレートを定数として使う問題は、定数の利用には、関数呼び出しの文法が必要になることだ。

// 円の面積を求める
template<typename T>
T area_of_circle_with_radius(T r)
{
    return pi<T>() * r * r;
}

この()は冗長である。「定数とは無引数関数で表現できるんだ」などと主張したとしても、数学者ではないプログラマーには受け入れられない。この問題は、変数宣言にテンプレート宣言が許されていれば解決できる。

// 変数テンプレート
template < typename T >
constexpr T pi = static_cast<T>(3.1415926535897932385) ;

// 明示的特殊化
template < >
constexpr my_floating_point pi<my_floating_point> = /* 初期化 */

// 円の面積を求める
template<typename T>
T area_of_circle_with_radius(T r)
{
    // 変数テンプレートを実体化して使用
    return pi<T> * r * r;
}

とても斬新な機能だが、文面の変更としては極めて小さい。というのも、従来の変数宣言にはテンプレート宣言を使えなかった制限をとりはらうだけだからだ。

前回からの変更点は、constexprやconstではない変数テンプレートも許可すること。

// OK
template < typename T >
T x = 0 ; 

変数テンプレートのテンプレートテンプレートパラメーターは、熟考が必要だということで、C++14では禁止されることだ。

N3649: Wording for Auto Generic Lambda Proposal (Bristol 2013)

ジェネリックlambda。lambda式の仮引数の型をパラメーター化できる。

void f()
{
    auto l = []( auto x ) { } ;

    l( 0 ) ;
    l( 0.0 ) ;
    l( "hello" ) ;
}

もちろん、キャプチャーしないlambda式は、関数ポインターに変換可能だ。

void f()
{
    auto l = []( auto x ) { } ;

    auto (*p1)(int) -> void = l ;
    auto (*p2)(double) -> void = l ;
}

クラスで定義した関数オブジェクトならば、メンバーテンプレートを使うことで実現できていたジェネリックが、lambda式でも可能になる。

今回の論文は、前回からだいぶ変更点されている。

一番大きいのは、Variadic auto parametorsだろう。

void f()
{
    auto l = []( auto ... pack ) { }

    l( ) ;
    l( 1 ) ;
    l( 1, 2 ) ;
    l( 1, 2, 3, 4, 5 ) ;
}

C++14では、lambda式がますます使いやすくなるだろう。

N3668: exchange() utility function, revision 3

アトミック版ではない汎用的なexchange関数テンプレートの追加。実装は以下の通り。

template<typename T, typename U=T>
T exchange(T& obj, U&& new_val)
{
    T old_val = std::move(obj);
    obj = std::forward<U>(new_val);
    return old_val;
}

N3656: make_unique (Revision 1)

型のオブジェクトをnewしてunique_ptrにいれて返すmake_unique関数テンプレートの追加。make_sharedのunique_ptr版だ。これにより、new式を書かずにすむ。

#include <memory>

int main()
{
    std::unique_ptr< int > p1 = std::make_unique< int >( ) ;
    std::unique_ptr< int > p2 = std::make_unique< int >( 123 ) ;

    // int[10]
    std::unique_ptr < int [ ] > p3 = std::make_unique< int [ ] >( 10 ) ;
}

これは何をしているのか分かりやすくするために型を書いているが、現実にはautoを使うところだ。

#include <memory>

int main()
{
    auto p1 = std::make_unique< int >( ) ;
    auto p2 = std::make_unique< int >( 123 ) ;

    // int[10]
    auto p3 = std::make_unique< int [ ] >( 10 ) ;
}

重要なのは*p1で値を得られることであり、型ではないからだ。

あって当然の機能だ。これがない現行のC++11の標準ライブラリはクソである。

N3654: "quoted" proposal

空白付きの文字列をそのままストリームに出力し、また入力するマニピュレーター。

正直、ストリームライブラリは教科書のサンプル以上のことをしようとすると、とたんに厄介になるので、何も期待していない。

[無慈悲なPDF] N3642: User-defined Literals for Standard Library Types

標準ライブラリ用のユーザー定義リテラル。標準ライブラリの特権を行使して、アンダースコアからは始まらないリテラル識別子を使っている。うらやましい限りだ。

[C++WG論文をHTMLで記述すべき勧告が論文で出された] N3655: TransformationTraits Redux, v2

従来の標準ライブラリ内のメタ関数に対するエイリアステンプレートを提供する提案。metafunction<T>::typeが、metafunction_t<T>になる。ようするにこういうことだ。

template < typename T >
using add_pointer_t = add_pointer<T>::type ;

冗長な文法で書かなくてよくなる。

N3672: A proposal to add a utility class to represent optional objects (Revision 4)

標準ライブラリにoptionalを追加する。もはや有名すぎて解説するまでもあるまい。今時optionalを知らないのは、いまだにMSVCを使っているようなアホだけだ。

N3659: Shared Locking Revision 2

標準ライブラリにReader/Writer locksライブラリのShared lockingライブラリを追加する。

読む分にはいくらでも読んでいいが、書き込む際には排他的にロックしたいという場合に便利な同期ライブラリだ。

LLVMのFortranフロントエンド、FlangがGSoCに提案されている

[Phoronix] Fedora Is Testing Out Radeon, Nouveau, Intel Graphics

LLVM GSoC proposal.

LLVM用のFortranフロントエンドの実装、FlangがGSoCに提案されているようだ。

これは、12週間でFortran 77完全準拠、Fortran 90, 95に一部対応のLLVMフロントエンドを実装する計画らしい。はたしてFortranがたったの三ヶ月で実装可能なのだろうか。

過去にも、同名のLLVM用のFortranフロントエンドのプロジェクトがGithub上に存在したが、完成することなく打ち棄てられている。

isanbard/flang · GitHub

どうも私にはFortranの需要というのがよくわからない。とくに、今や科学技術計算の分野でも、PythonやJuliaといった言語が使われていることを考えると、なおさらわからない。

2013-04-22

自由ソフトウェア運動の思想に囚われた技術的に劣った選択

このところ、自由ソフトウェアのコピーレフトライセンスは方向性を間違えており、許諾的なオープンソースライセンスが流行っているとする主張がある。

漢(オトコ)のコンピュータ道: フリーソフトウェア運動は方向性を間違えてはいない

これについて思うことがあるので書く。その前に、まず自由ソフトウェアとは何かということから説明しなければならない。

もちろん、許諾的なオープンソースライセンスと呼ばれるもののほとんどは、ストールマンの自由四原則を満たす。

What is free software? - GNU Project - Free Software Foundation (FSF)

すなわち、

  • あらゆる目的でプログラムを実行する自由(自由0)
  • プログラムがいかにして動作するのかを検証し、変更して自分の意のままに動作させる自由(自由1)。ソースコードへのアクセスはこの自由への前提条件である。
  • 複製物を再配布し、隣人を助ける自由(自由2)
  • 改変版の複製物を他人に再配布する自由(自由3)。これにより、全体を自分の改変による恩恵にあずからせることができる。ソースコードへのアクセスはこの自由の前提条件である。

自由0はしばしば守られていない。たとえば、多くのソフトウェアは、ソフトウェアの利用方法に制限を設けている。犯罪行為に用いないと同意させるのは自由0ではないし、非商用であるとか、非組み込み用途に限るとか、非エンタープライズ用途に限るとか制限をつけるのも、自由0を満たさない。

自由1は当然である。ソフトウェアがいかにして動作するのかという事は、利用者が自ら検証可能であるべきだ。そのためには、ソースコードにアクセスできなければならない。

自由2と自由3が別々になっているのは不思議に思うかもしれない。しかし、世の中にはどうも、このうちのどちらか一方だけの自由は許すが、他方を許したくない人間が存在するのだ。

ストールマンは自由ソフトウェア運動として、強力なコピーレフトライセンスによる自由の保証を使った。従来の著作権の利用法というのは、著作権という独占的な権利が認められ、許諾を得なければ著作権侵害になるということを盾に、様々な利用上の制限を課しているというものだった。例えば、原則として複製物を再配布してはならないとか、改変してはならないとかいう類の権利だ。そういうことをしたければ、著作者からの明示的な許諾が必要になる。

ストールマンは著作権を利用して、自由を保証する許諾契約を作り上げた。著作物の複製物を再配布したり、改変版を再配布する許諾を得たければ、契約に同意する必要がある。その契約は、利用者に複製物や改変版複製物を提供する場合、その契約と同じ契約を提供しなければならないというものだ。これにより、派生物はすべて一つの契約で提供され、ソフトウェアの利用者の自由を保証できる。

なお、ソフトウェアの複製物を受け取る際には、この契約に同意する必要はない。たとえ複製物が契約違反な方法で再配布されていたとしても、受け取る事自体はあらかじめ契約に同意しなくても許諾されている。ただし、受け取った複製物を他人に再配布する際には、契約に同意しなければならない。その契約は、先程も説明したように、利用者の自由を保証する。

これがGPLというライセンスだ。ストールマンは従来の著作権、つまり複製の権利(コピーライト)という言葉に対して、コピーレフトなる造語を作り出した。

世の中には、GPLを汚染であるとか感染であるとかみなす向きがあるが、これは正しくない。むしろ、GPLは利用者を守るためにあるのだ。そもそも、著作権が明示的な許諾を必要とする以上、あらゆる著作物は汚染する。もし、他人の著作物を、ある限定された利用に対して許諾を得て使っているとしよう。その利用範囲を超えると、新たに許諾を得なければならない。

これは、合意した利用法を超えた範囲では、その著作物を使えないという事である。もし他人の著作物を含む自分の著作物がある場合、他人に著作物の利用方法を制限されてしまう。

これは作り手を保護するという意図がある。たとえば、前にテープという媒体に限定して提供する許諾契約を結んでいて、今新しく光学ディスクという媒体が出てきたが、これに対しては新しく契約を結び直すという具合に。

これは著作者の利益になるが、利用者の自由を制限する。今まで想定されていなかった利用法がでるたびに、許諾契約を取りなおさなければならない。

GPLは著作権をうまくハックして、利用者に自由を保証する許諾契約、つまり提供者に自由を保証させなければ著作権利用許諾を与えない許諾契約を作り出した。これはうまく動いた。

さて、ストールマンはMITのコンピューター更新の際、自由なOSが存在しないという現状に直面した。ソースコードも提供されないような不自由なOSを、さらに利用方法まで制限される契約に同意して、始めて利用を許されるようになるのだ。ストールマンは不自由なソフトウェアの利用を拒否した。MITを辞めて、自由なOSであるGNUを開発しだしたのだ。

GNUは、その本来の意図した完全なOSにはいまだに届いていないが、広く使われている。さらに、今はGPLv2という古い時代遅れのライセンスだが、一応は自由なソフトウェアのカーネルが存在するので、GNUにLinuxカーネルを加えて、GNU/Linuxというシステムとして使うことができる。ストールマンが単にLinuxとは呼ばず、GNU/Linuxと呼べと言っているのはこのためだ。

その後、この自由ソフトウェアの本質を理解しない浅はかな人間が、オープンソース運動を立ち上げた。彼ら利用者を制限することを恥としない人間は、提供者が利用者の自由を制限しても許される、むしろ自由の制限を許さないのは自由ではないと考えた。そこで、オープソースライセンスという劣ったライセンスを作り出した。

ほとんどのオープンソースライセンスは、ストールマンの自由原則を満たす。ただし、利用者の自由を保証しない。オープンソースライセンスは、「このソフトウェアのソースコードを提供してやるよ。複製物の再配布もいいよ。改変してもいいし、改変版の再配布もいいよ」とはいう。ただし、その自由の権利を利用者に保証させる義務がない。

つまり、ほとんどのオープンソースライセンスと呼ばれているライセンスで提供されているソフトウェアのソースコードを改変して、その改変版の複製物のバイナリのみを再配布することも許されるのだ。ソースコード提供の義務はない。しかも、利用方法を制限することも許される。非商用利用に限るとか、非組み込み、非エンタープライズ用途に限るなどといった邪悪な制限も許される。

これは利用者の自由を保証しない。

前置きの説明が非常に長くなった。本題の話をしよう。

GNUにはGCCというコンパイラーとその周辺ツール集のソフトウェアがある。これはGPLというコピーレフトなライセンスで提供されている。GCCは長らくコピーレフトを疎ましく思う連中からも使われていた。なぜならば、GCC以外の選択肢などないからだ。GCCはその独占的な技術的優位を利用して、コピーレフトを普及させた。

技術的優位に立つというのは非常に重要だ。いかに自由なソフトウェアといえども、技術的に劣ったソフトウェアは見向きもされない。人は全自由とひきかえに僅かな利便性を選択する生き物だからだ。そのため、自由なソフトウェアはブッチギリで優れたソフトウェアでなければならない。GCCはこの長年、そのような優れたソフトウェアであった。

ただし、GCCは自由ソフトウェアの思想に囚われた、技術上劣った選択をたびたびしてきた。たとえば、GCCは長年コンパイル済みヘッダーの実装を拒んできた。おそらく、コンパイル済みのヘッダーのみを配布されるのを恐れたからであろう。

GCCのソースコードも、モジュール化されておらず、個々の機能に分割して利用することができなかった。たとえば、C言語やC++のパーサーだけを取り出して使うとか、GCCに内部表現を渡して、その後からバイナリ生成までGCCに任せるだとか、そういう部分的な機能の利用が難しかった。そもそも、内部表現は規格化されておらず、互換性を考慮することなく変更されていった。そういうことがやりたければ、GCC全体に組み込むしかないのだ。

これも、GCCをバラバラにして一部分だけ不自由なソフトウェアに組み込みやすくするのを防ぐ意図があったのだろう。

さて、そうこうしているうちに、LLVMという全く新しいオープンソースのソフトウェアプロジェクトが立ち上がった。LLVMもGCCとおなじく、コンパイラーとその周辺ツール集である。LLVMは最初からモジュール化された設計をしており、機能毎に容易に分割できる。しかも、内部表現も規格化し、簡単に変更しない方針を取っている。

LLVMには、コピーレフトを汚染だとか感染だとか考える不遜な企業から大いに支援を受けた。その結果、すばらしい速度で機能が向上していった。現に今、LLVMのClangは、C++11を機能完全で実装するまでに至った。これはGCCもまだやり遂げていない。しかも、GCCの実装には、細かい規格違反のバグが多数ある。Clangにそのようなバグを探すのは難しい。もちろん、バグトラッカーを見れば、やはり多数発見されているのは確かだが。

問題はここだ。自由を保証するため、意図的に技術上優れた設計ではなく、不自由に転用しにくい設計にしてしまうこと。これをしてしまうと、技術的優位に立つことができなくなる。技術的優位に立てなければ、人には使われない。人に使われなければ意味がない。

もうひとつ設計上の話をしよう。カーネルの話だ。

GNUは本来、完全なOSを目指していた。OSにはカーネルも含まれる。現在、GNUではHurdというカーネルを開発中である。残念ながら、このカーネルは未だ実用には至っていない。機能的な優劣はもとより、まだまともに使える段階ですらない。そこで、いまはGNUとLinuxカーネルを組み合わせて使っている。

カーネルには、BSD系列もあり、こちらは共通の先祖を元にだいぶ分散しているが、どれも、少なくとも普通に使える程度には成熟している。Hurdはいまだに使えない。現時点でHurdがなんとか動く環境は、x86エミュレーター内である。実機で動く環境を用意することすら難しい。BSD系列は、ともかくも実機で動かすことはできるというのに。

なぜGNUはともかくも使えるカーネルを開発できなかったのか。十分な金がないのか? 有能なカーネル開発者がいないのか? そうではない。理由は、自由を保証するための設計上の選択だ。

GNUがカーネルを開発しようとしていた頃、まだLinuxカーネルはなかった。カーネルの開発は手間がかかる。もし既存の土台として利用できる自由なカーネルがあれば、それを利用したいものである。その当時、BSDならあった。BSDは色々とライセンス上の問題と訴訟を抱えていたが、最終的には解決されたし、それがカーネル選択の多大な悪影響を与えたわけではない。むしろ、GNUのメンバーには、BSDカーネルに精通しているものもおり、BSDカーネルの選択は妥当なものであると思われた。

もし、当時GNUがBSDカーネルを土台に選択していれば、いまごろは、BSD系列のひとつぐらいにはまともに動くカーネルが出来上がっていた可能性は大いにある。

しかし、ストールマンはGNU Hurdの土台としてMachというカーネルを選択した。Machカーネルはマイクロカーネルとして設計されていた。マクロカーネルとは、カーネル自体は非常に小さく、他の設計のOSならば通常なカーネルで行うような処理もほとんど、ユーザーランド実装で行われる設計だ。GCCはモジュール化を避けていたのを考えると不思議だが、これにも理由がある。

コンピューターの利用者がコンピューターの所有者であるとは限らない。利用者がコンピューターの管理者であるとは限らない。GNUがマイクロカーネルを選んだ理由は、権限を持たないコンピューター利用者にも、最大限の自由を保証したかったからだ。

マイクロカーネルならば、ほとんどの処理はユーザーランドで実装される。つまり、管理者権限を持たずとも、利用者ごとまったく異なるシステムを構築できるのだ。カーネルを変更する権限は必要ない。つまり、権限を持たない利用者でも、自分の環境は自由に設定でき、しかもその設定は他のユーザーに影響を及ぼさない。

GNU Hurdは、このような利用者の自由を保証するという思想上の設計から、マイクロカーネルを選んだ。その結果、いまだに実用に至っていない。

自由を保証する設計は、時として高くつく。例えば、GNU Hurdは設計上、forkをCopy-on-writeで実装できないそうだ。そのため、forkしてexecのようなコードのかわりに、posix_spawnの利用を推奨している。

もちろん、現状がこのままでいいわけがない。ソフトウェアの自由を深く考えないオープンソース(笑)は間違った考え方であり、目先の利便性だけを考えると長期的には不幸を招く。

たとえば、ソニーの邪悪な制限コンピューターPlaystation 4は、BSD系のOSを利用しているという話だし、コンパイラーにはLLVMを利用しているという話だ。しかし、利用者には自由原則はひとつとして保証されない。利用者はソニーがあらかじめ定めた方法でのみ、この忌まわしき制限PS4の操作が許される。ソースコードは公開されず、実装を検証することもできないし、複製物の再配布もできないし、改変版をインストールすることもできない。

これは、邪悪なPS4が利用するBSD系OSやLLVMが、許諾的なオープンソースライセンスで提供されているためである。許諾的なオープンソースライセンスは、ストールマン自由原則を満たすとはいえ、利用者の自由を保証しないので、とても弱いライセンスである。

スマートフォンに使われているAndroidというOSは、Linuxカーネルを用いている。残念ながら、Linuxカーネルは時代遅れのGPLv2を利用しているので、例えばソフトウェアを搭載して出荷されるコンピューターに、改変版を実行しないような制限装置がもうけられていたとしても、ライセンス上どうすることもできない。この制限コンピューターで有名なTivo社にちなんで、Tivoizationと呼ばれている。時代遅れのGPLv2のLinuxカーネルは、多くのAndroid搭載のスマートフォンが制限コンピューターに成り下がることを許している。多くのスマートフォンでは、コンピューター所有者の意に反して、OSの書き換えを制限しているのだ。もし読者がスマートフォンを購入しようと思い立ったならば、そのような制限スマートフォンは絶対に購入してはならない。制限コンピューターは利用者の自由を制限しているのだ。そのような制限コンピューターを所有したいとは思わないだろう。

利用者の自由を保証するため、Tivoizationの害悪から利用者を守るために、GPLv3は重要である。そのため、GPLv3のコンパイラーであるGCCや、GPLv3のOSであるGNU Hurdの存在は大きい。GCCはLLVMの脅威にさらされているとはいえ、まだ戦える。GNU Hurdはいつ戦いの土台に立てるのだろうか。

リチャード・ストールマン:子を持たないことの重要性について

Why it is important not to have children.

リチャード・ストールマンが、子を持たないことの重要性について書いている。

どうも文章が回りくどく翻訳が難しいので、原文もつけた。

Why it is important not to have children.

なぜ子を持たないことが重要なのか

-- Richard Stallman

-- リチャード・ストールマン

I decided not to have children. My family was full of tension and anger, and then I noticed that many others were too. Such a family life was in no way attractive. When older, often I saw parents rebuke their children for playing with me, or even in my vicinity, assuming it would bother me — without waiting to see if I objected. Rebuking those children had become an ingrained, automatic habit. To see this made me sad for them, but I knew I would be the same as a parent. I would not be able cope with a frequently crying baby without becoming upset and angry.

私を子を持たないことに決めた。私の家族はいらだちと怒りに満ちあふれていたし、他の多くの家族もそうであると後に知った。そんな家族生活に魅力などあるわけがない。歳を取った今、他所の子が私と遊んでいたり、あるいは私の近くによってきたりするだけで、その子の親は、私の意見を聞かずに、勝手に私の迷惑になると考え、子供を叱るようになる。子を叱るのが自動的な習慣になっている。そのような光景を見るのは哀しいことだが、もし私が親となっていれば、おそらく彼らと同じ事をしたであろう。私は頻繁に泣きわめく子に対して、怒りを交えずに接することなどできないだろうから。

Of course, many people tell themselves, "That happens to others, but I am better than they; I will get it right." Obviously, most of them are mistaken. I did not suppose that I would succeed in human relationships where most people fail.

もちろん、多くの人が、「そういうことは他人には起こるものだ。しかし、私は他人より優れている。私は正しく振る舞う」と自分自身に言い聞かせているが、ほとんどの者は間違えている。ほとんどの者が常に失敗するような人間関係を、私が正しくやり遂げられるとは思わない。

Most fathers in the US have to work very hard to get money for their children. I did not want a life of running on a treadmill, doing whatever people with money would tell me to do.

アメリカ合衆国のほとんどの父親は、子のために苦労して働いて金を稼いでいる。私はベルトコンベヤーの上で走って、金を持つ人間の命令に何でも従うような人生を送りたくない。

A large fraction of US fathers eventually get divorced, and subsequently rarely see the children for whom they are spending most of their time scrabbling for money. What a futile life! But even those who are not yet divorced see their children little, since they are so busy at work.

かなりの割合のアメリカ合衆国の父親は、最終的に離婚し、その結果として、多くの時間をかけて金を費やしたわが子に会うことがまれになる。なんという不毛な人生だ! まだ離婚していない者であっても、子供と会うことは少ない。なぜなら彼らは仕事で忙しいからだ。

I am convinced I made a wise personal decision in avoiding this. But I was not the only one that benefitted from it. Everyone did. Not having children is an important contribution to humanity. My decision probably reduced the 2050 population by 5 to 10 people.

この問題を避けた私の判断は、賢いものであった。しかし、これによって利益を被るのは独り私だけにとどまらない。皆が利益を被るのだ。子を持たないということは人類に対する重要な貢献である。私の決定により、2050年の人口は、おそらく5人から10人ほど減るだろう。

Overpopulation is a tremendous danger to civilization and the ecosphere. It makes every human-caused ecological problem bigger. Population growth has slowed but not stopped. The human population is expected to grow by 2 or 3 billion by 2050, and it is not clear how to find water and food for all those people. Population growth also increases the difficulty of curbing global heating.

人口過剰は文明と環境に対するとてつもない危機である。人口過剰は人為的な環境問題を拡大させる。人口の増加は遅くなってきているが、止まっていない。人類の人口は2050年には20億か30億ほど増加すると見込まれており、皆に水と食料を行き渡らせる方法は分かってない。人口増加は地球温暖化の阻止を困難にする。

Thus, the decision about having children is, for most people, the most important decision in their lives about how they will affect humanity's resource footprint in the future. (Nina Paley said it graphically.)

故に、子を持たないという選択は、ほとんどの人には、将来の人類の資源消費量への影響において、もっとも重要な選択である。(Nina Paleyはこの問題を図画で指摘した)

That decision enabled me to contribute something else: to launch GNU and the free software movement. Having no dependents, I could dedicate myself to what seemed right rather than to whatever someone with money wanted me to do. If you are reading this page, it is because that decision enabled me to make contributions to humanity that people appreciate.

この選択により、私は他のことに貢献できた。GNUと自由ソフトウェア運動を立ち上げたのだ。私に依存する者がいないため、私は自分が正しいと思うことのみに注力し、金を持つ人間の命令に従わずにすんだのだ。読者がこのページを読めるのは、それは私の選択の結果、人類への貢献して、その貢献に人々が感謝した結果だ。

I therefore urge you to do as I did, and have no children. I don't wish that nobody had any children; I don't want humanity to disappear. But there is no risk of that; for the numbers I could hope to influence, the influence is for the good.

私は故に、読者に対して私と同じように、子を持たない選択をするよう強く主張する。私は皆が一切子を持たないことを願っているのではない。私は人類に消えてほしくはないからだ。ただ、この読者が子を持たないということにリスクはないからだ。私が影響力を行使できるだけの数の人間では、影響は良い方に働く。

Some make the absurd argument that population decline is the real danger. In 50 years, they claim, everyone will have a comfortable life, so they may have few children (as tends to happen in developed societies today), and the human population could decline. If this went on for millenia, humanity might disappear. Is that a real possibility?

一部の者は、人口減少は深刻な危機であるという根も葉もない反論をする。彼らの主張するところによれば、50年後、と彼らは主張する、みなは快適な人生を全うしているだろうから、その後で子を少なく持つようになり(今日の発展途上社会ではよく起こることだ)、そうすれば人口は減少するだろう。もしこれが1000年も続けば、人類は絶滅してしまう。これは本当に起こりえるだろうか?

First of all, it disregards the tremendous disaster that global heating and destruction of the natural world are leading towards. 30 years from now, large parts of humanity will probably find it hard to get water or food, let alone contraception. It is unlikely we will provide most of humanity with a decent European-style life with the current world population. So there is little chance, in that world, of population decrease because everyone is comfortable.

まずはじめに、この主張は地球温暖化や環境破壊と進む一方の深刻な危機を無視している。今から30年後、人間の大多数は水と食料を得るのが困難になっているだろう、もはや避妊どころの話ではない。現在の世界人口では、人類の大半にまともな西洋風の人生をこの先提供することはまずありえない。つまり、このような世界において、皆が快適になるために人口を減少できる可能性は小さい。

Supposing we avoid the disaster and eliminate poverty, 50 years later we might reach a stage where everyone prefers a small family. However, 50 years after that we will probably have greatly extended the human life span. That means a much smaller number of births per adult per year would be enough to maintain a stable population. The danger of overpopulation might even return.

仮に災害を避け、貧困を解消できたとする。50年後、皆が小家庭を好むようになっているかもしれない。しかし、50年後、我々の寿命はとても伸びているだろう。つまり、大人一人あたりの出生数が少なくても、人口は安定してしまう。人口過剰の危機が再びやってくる。

The first hurdle is to avoid the disaster. Having no children will help, and it will free you to do something else that will help.

最初のハードルは災害を避けることだ。子を持たないことは貢献になり、しかも子に縛られることがなくなるので、何か別のことで人類に貢献できるようになる。

Copyright 2012 Richard Stallman Verbatim copying and redistribution of this entire page are permitted provided this notice is preserved.

2013-04-21

Bristol会議で採択されたもの

C++14のドラフトの採択を行うBristol会議が終わった。どうもwikiをみても会議の投票結果がよくわからない。多分Post-Bristol mailingのEditor's Reportを待つ必要がある。

とりあえず、もっともでかいものをHerb Sutterがまとめているのでこっちで紹介。

Trip Report: ISO C++ Spring 2013 Meeting : Standard C++

コア言語としては、ジェネリックLambda、Lambda汎用キャプチャー、変数テンプレート、動的配列が採用されたそうだ。ライブラリとしては、make_uniqueとoptionalが採択されたようだ。

基本的に、会議の結果提案を改良するので、Pre-Biristol論文は参考程度にしかならないのだが、とりあえず概要の確認は以下で。

本の虫: C++WG 2013-03-pre-Bristol mailingの簡易レビュー

とりあえずPost-Bristol mailing待ち。

GNU/Linuxを動かせる最低スペックはATmega

Linux on an 8-bit micro? - Dmitry Grinberg

8bitマイクロでARMエミュレーターを実装してGNU/Linuxを動かした顛末が書かれている。

イントロ

初心者がマイクロコントローラーのフォーラムで、可愛いちっぽけな8bitマイクロでLinuxを動かせるかどうか質問するのはよくあることだ。大方は笑われるだけだ。Linuxフォーラムでも、Linuxの最低スペックは何かという質問がなされる。一般的な回答は、32bitアーキテクチャとMMUと、少なくともカーネルを載せるための1メガバイトのRAMだ。このプロジェクトは、そのようなありきたりな回答者を黙らせるためのものだ。右にみえる基盤のはATmega1284pだ。同じ物をATmega644aでも作って成功している。この基盤はこれ以外にプロセッサーを持たず、Linux 2.6.34をブートする。実は、完全なUbuntuスタックを動かすことだって可能だ。XやgnomeだってOKだ。時間さえあればの話だが。

RAM

確かに、完全なLinuxのインストールにはメガバイト級のRAMと32bit CPUとMMUが必要なのは事実だ。このプロジェクトではそれをすべて用意する。まず、RAMを何とかしよう。画像で分かるように、この基板には大昔の30ピンSIMMメモリーモジュールが載せられている。これは80286ベースのPCで使われていたものだ。このメモリはATmegaと繋がれている。筆者はスペック通りにアクセスしてリフレッシュもするコードを書いた(SDRAMはデータを失わないために定期的なリフレッシュを必要とするのだ)。どのくらい速いかって? リフレッシュ割り込みは62ミリ秒毎に発生し、1.5ミリ秒ほどかかる。そのため、CPU時間を3%ほど食う。RAMアクセスは、プログラミングを簡単にするため、一度に1バイトづつ行われる。結果として、最大帯域は毎秒300キロバイトほどだ。

ストレージ

RAMについては解決した。あとふたつ問題が残っている。ストレージはそれほど難しい問題ではない。SPIを使えばSDカードを使うのはとても簡単で、このプロジェクトでも採用した1GBのSDカードで問題なく動く。今回のファイルシステム(Ubuntu Jaunty)に限って言えば、512MBでも十分だ。ATmegaはハードウェアSPIモジュールを搭載しているが、どういうわけか、うまく動かない。そこで、筆者自らインターフェースにビッグバンを起こした[訳注:自力で書いた]。結果は十分に速い。約毎秒200KBほどだ。また、これはすばらしいおまけもついている。十分なピン数さえあれば、どんなマイクロコントローラーでも使えるのだ。ハードウェアモジュールは必要ない。

CPU

あと残っているのは、32bit CPUとMMUの要件だ。残念ながらAVRにはMMUはなく、8bitだ。この障害を克服するため、筆者はARMエミュレーターを書いた。ARMは筆者が最も得意とするアーキテクチャであり、簡単でエミュレーターも書きやすい。なぜ既存のエミュレーターを移植せずに自力で書いたのか? それは、他人のコードを移植するのは面白くないこと、それと既存のエミュレーターに8bit機への移植を容易にするよう考慮されて書かれたものが存在しないことだ。たとえば、AVRコンパイラーはintが16bitであるので、"(1 << 20)" のような簡単なコードさえ通らない。かわりに、"(1UL << 20)"を使わなければならない。他人の不慣れなコードベースで、intが使われているあらゆる箇所をしらみつぶしに探して書き換えるのは悲惨だ。それに、筆者は常にモジュール化されたARMエミュレーターを書きたいと思っていたのだ。そこで、自力で書いた。

その他の機能

基盤と外の世界とのやり取りは、一本のシリアルポートを介して行われる。現在、シリアルポートはPCに接続されているが、キーボードとキャラクターLCDを基盤に接続することも可能で、完全に独立機としても動作する。基盤にはLEDがふたつある。これはSDカードアクセスを知らせるためのものだ。一つは読み込み、ひとつは書き込み。基盤にはボタンもひとつある。一秒間押された場合、エミュレートCPUの実行速度をシリアルポートに吐くようになっている。AVRは24MHzに動作する(標準の20MHzより若干オーバークロックしている)

どのくらい速いのか?

uARMは速度の鬼というわけではない。bashプロンプト("init=/bin/bash" カーネルコマンドライン)を表示するのに二時間かかる。そこからUbuntuをブートし終わる("exec init"してログインする)までにさらに4時間かかる。Xを起動するのはさらに時間がかかる。エミュレートされたCPUの実行速度は、6.5KHzといったところだ。8bitマイクロで32bit CPUとMMUをエミュレートするのだから、仕方がないことだ。興味深いことに、ブートさえしてしまえば、システムは何とか使えることだ。コマンドをタイプして結果を得るまでに一分しかかからない。これはつまり、実際に、使うことができる。例えば、筆者は今日、SDカードをフォーマットするのに使った。これは最速というわけではないが、おそらく最も安価で、最も遅く、最も単純な手で組み立てた、部品数の最も少ない、ローエストエンドLinux PCと言えるだろう。基盤は手でワイヤーを使ってハンダ付けされている。プリント基板すら必要ない。

このあと、ARMエミュレーターの解説に入り、ソースコードも公開されているが、残念ながら自由なソフトウェアではない。非商用利用限定だ。これは自由原則0を満たさない。

動画もある。

2013-04-20

Ubuntu 13.04の気になるパッケージのバージョン

Ubuntuのレポジトリにある、気になるいくつかソフトウェアパッケージのバージョンを、Ubuntu – Ubuntu Packages Searchから調べ、Ubuntu 12.10と比較してみた

Linuxカーネルは、3.5から3.8になった。カーネルの個々の変更にはうといので、とくに何の感情もない。3.8は現行の最新の安定版リリースだ。

gccは4.7.2から4.7.3になった。これはバグフィクスだけでエキサイティングな変更はない。残念だ。gcc 4.8はすでにリリースされており、C++11のコア言語対応が充実し、libstdc++もだいぶ改良されているのだが、ごく最近のことなのでまだ入らないのだろう。はやく3.8になってほしい。

clangは3.0から3.2になった。3.2は現行の最新版の安定リリースだ。Clangは3.3で大きく化ける。Linuxカーネルをビルドするための変更が公式に取り入れられたし、C++11のコア言語の機能を完全に実装している。もっとも、3.3はまだリリース日すら決定されていない状態で、GentooやArchのような人柱上等なディストロではない以上、現時点では仕方のないことだ。

libc++はいまだにパッケージが存在しない。libc++もC++11の標準ライブラリを完全に実装している。また、GNU/Linux環境でのビルドするためのcmakeスクリプトやドキュメントも整備されたし、そろそろいれてほしい。一応、Debianにはexperimentalに入っている。

Debian -- Package Search Results -- libc++

ibusは1.4.1から1.4.2になった。これも基本的にほとんど変わらない。最近のUbuntuのコミュニティ開発者では、ibus 1.5はイケてないとのもっぱらの噂だ。そこで現在fcitxをテスト中だという。

mozcは1.5から1.6になった。

もちろん、気にいらなければ自分でビルドすればいいだけの話だが、手動でのソフトウェアの管理は面倒だ。やはりメンテナーによってしっかりと管理されたパッケージマネージャーによる恩恵にあずかりたい。結局、自由ソフトウェアを手軽にビルドできる環境なのにもかかわらず、どうしてもひつようなclangとかlibc++以外はパッケージに頼っている。どうせ半年待てばいいだけなのだ。

GNU/Linuxでlibc++をビルドする方法

"libc++" C++ Standard Library

まず、libc++をチェックアウトする。

$ svn co http://llvm.org/svn/llvm-project/libcxx/trunk libcxx

libc++をビルドするにあたっては、使用するC++ABIライブラリを明示的に指定しなければならない。C++ABIライブラリは、例外やRTTIや名前マングリングといったC++の低級層のABIを提供するライブラリである。GNU/Linuxでは、デファクトスタンダードである Itanium C++ ABIが使われている。

C++ABIの実装は複数ある。そのため、どの実装を使うのか選ばなければならない。GCCによるC++ABI実装は、libsupc++だ。libsupc++を使うことの利点は、GCCとC++ABIの互換性を保てるという事だ。

LLVMでも、C++ABIの実装であるlibc++abiを提供している。これを使う手もある。

さらに、C++ABIの実装は他にもある。今回はGCCとの互換性を考え、libsupc++を利用することにする。

libsupc++を利用するには、libsupc++のヘッダーファイルがあるディレクトリを明示的に指定しなければならない。Ubuntuでは、/usr/include/c++/ と /usr/include/c++/<version>/<target-triple> だ。includeディレクトリは以下のようなコマンドを使えば確かめることができる。

$ echo | g++ -Wp,-v -x c++ - -fsyntax-only

Ubuntu 12.10では、/usr/include/c++/4.7 と /usr/include/c++/4.7/x86_64-linux-gnu になる。

libc++のビルドシステムにはcmakeが使われている。だいたい以下のような感じでビルドできる

$ svn co http://llvm.org/svn/llvm-project/libcxx/trunk libcxx
$ cd libcxx
$ mkdir build
$ cd build
$  CC=clang CXX=clang++ cmake -G "Unix Makefiles" -DLIBCXX_CXX_ABI=libsupc++ -DLIBCXX_LIBSUPCXX_INCLUDE_PATHS="/usr/include/c++/4.7/;/usr/include/c++/4.7/x86_64-linux-gnu/" -DCMAKE_BUILD_TYPE=Release ..
$ make

..はlibcxxのディレクトリへのパスだ。今回は親ディレクトリになる。ビルドが無事成功すれば、ビルドしたディレクトリの/libにlibcxx.soができる。

あとはmake installすればいいのだが、trunkを引っ張ってくるという事は、ちょくちょくビルドするだろうし、このまま使いたい。そのためには、libc++へincludeパスを指定し、libcxx.soへのライブラリのパスも追加しなければならない。

$ export LD_LIBRARY_PATH=<libcxx.soをビルドしたディレクトリへのパス>/lib
$ clang++ -std=c++11 -stdlib=libc++ -nostdinc++ -I<libcxxへのパス>/include -L<libcxx.soをビルドしたディレクトリへのパス>/lib test.cpp

とりあえず色々試してみたが、libc++のビルドさえやり遂げれば、あとは拍子抜けするほどあっさりと使える。libc++はC++11のライブラリを完全に実装している。clangもC++11のコア言語を完全に実装しているので、とうとうC++11の完全な実装が実現した。

本の虫: clangのビルドも参照のこと。

追記:

libc++をアップデートする方法であるが、単にsvn updateしてmakeすればいい。CMAKEから生成されたMakefileは、変更を検知して、自動的にcmakeを再実行してくれるので、再び空のビルド用ディレクトリでcmakeから始めてすべてをビルドする必要はない。

ClangがC++11を完全実装! 繰り返す、C++11を完全実装

[Phoronix] LLVM's Clang Compiler Is Now C++11 Feature Complete

C++11 support is now feature-complete. · e6e68b5 · llvm-mirror/clang · GitHub

3時間前のコミットにより、Clangは晴れてC++11規格を完全に実装した。その通り、完全にすべて実装した。

コミットは、Inheriting Constructorsとthread_localの機能を有効にする変更だ。

もちろん、まだ実装にバグは残っているだろうし、規格上のバグもあるので、開発に終わりはない。ただし、今日は記念すべき日である。

MultiPath TCPのLinuxカーネル実装

MultiPath TCP - Linux Kernel implementation : Main - Home Page browse

複数のIPアドレス、インターフェースをまとめてひとつにして扱えるTCP、MultiPath TCPのLinuxカーネルによる実装だそうだ。これにより、完全に別系統のネットワークをまとめて使うことができ、スループットと冗長性の向上が図れる。

デモでは、イーサネットとWifiと3GをひとまとめにしたMPTCP上にsshセッションでXを通している。冗長性があるので、複数のインターフェースの一部が切断されても、Xは問題なく使える。

FreeBSDでMultiPath TCPを実装するパッチもあるそうだ。

mptcp

このWebサイトは、Hacker Newsからリンクされたことにより、一時期閲覧者の増加により閲覧困難な状況に陥ったそうだ。「このサイトにもMultiPath TCPが必要なんじゃない?」と皮肉られている。

WindowsはP2Pソフトなので利用禁止

P2P通信用のAPIをデフォルトで搭載しており、切り離すことのできないWindows XP SP2とそれ以降のバージョンのWindowsは速やかにアンインストールするべきである。

Peer-to-Peer (Windows)

Windowsを用いる場合は、Windows XP SP1かそれ以前のバージョンを使うべきである。これはもはやサポートされておらず既知のセキュリティ上問題のある欠陥が山盛りであるが、恐ろしいP2P技術を避けるためには致し方ないことだろう。

古典的なネットワークプロトコルもP2P技術を利用している。例えば、IRCにはDCC(Direct Client-to-Client)というプロトコルがあり、IRCサーバーに接続するクライアント同士が、サーバーを介さずに直接接続できるプロトコルである。これは、一時期問題になった極悪なP2PソフトウェアであるNapsterやWinMXと同等機能であるから(NapsterやWinMXは中央管理サーバーでファイル情報を管理してファイルの送信はピア同士が直接行う)、DCCプロトコルを実装したIRCクライアントは、速やかにアンインストールするべきである。

Direct Client-to-Client - Wikipedia, the free encyclopedia

単なるプログラム言語や規約や解法を禁止する愚かさを理解できない人間がいるとは、それも教育機関であるとは、信じがたい事実である。

少なくとも、自由なOSであれば、自前でカーネルから特定の機能を削ってコンパイルし、また特定のライブラリやソフトウェアはインストールしないでシステムを構成することができる。そもそも、socketなんか提供しているからP2Pなどというけしからんソフトウェアが実装可能なのだから、socketなど削ってしまえばよい。カーネルの大幅な減量にもなるので一石二鳥だ。socketを削るとX11も動かないだろうが、悪用可能なP2P技術の芽を摘むためには致し方ないことだろう。

2013-04-19

Debian 7.0 Wheezyは5月初頭にリリースされる

[Phoronix] Debian 7.0 "Wheezy" To Release In Early May

FINAL release update

Debian 7.0 Wheezyが、5月の初頭にリリースされる見込みであることが発表された。

LLVM/Clangがぜってーサポートしねーと宣言しているLinuxカーネルに多用されているGCC拡張

[Phoronix] LLVM/Clang 3.3 Should Be Close To Building Linux Kernel

LLVMLinux

Bug 9254 – clang can't build iptables in Linux kernel: error: fields must have a constant size: 'variable length array in structure' extension will never be supported

ClangでLinuxカーネルをビルドできる状況は、多くの勢力に望まれてきた。理由は様々だ。Clangはビルド時間が短い。静的解析による警告も優れている。ソースコードも綺麗だ。複数の独立した実装でコンパイルできるコードは、誤りを静的に発見しやすい移植性にも優れる。より優れたコピーレフトなライセンスであるGPLv3を嫌う哀しい勢力の存在もある。

現在のところ、ClangでLinuxカーネルをコンパイルするには、ClangとLinuxカーネル両方にパッチを当てなければならない。LLVM 3.3では、パッチはすべて正式に取り込まれ、LLVM側では素のままでLinuxカーネル側だけにパッチを当てればコンパイルできるようになるそうだ。

問題は、Linuxカーネルだ。LinuxカーネルはGCC拡張を多用しており、今のところClangでは動かない。LLVM側で実装すればいいGCC拡張もあるが、LLVMとしては実装したくない汚いGCC拡張もある。その最大の汚らしいGCC拡張が、構造体内でのvariable length arrayだ。

variable length array自体は、GCCは大昔から拡張機能としてサポートしている。C99では正式に採用された。

void f( size_t size )
{
    char a[size] ; // sizeは実行するまで分からない
}

配列の長さを実行時に指定できる機能だ。

ただし、C99でも採用していない、醜悪なGCC拡張がある。GCC拡張では、variable length arrayを構造体内で宣言できるのだ。これはその頭文字をとってVLAIS(Variable length arrays in structs)と呼ばれている。

void f( size_t size )
{
    struct
    {
        char a[size] ; // GCC拡張
    } s ;
}

VLAISはC99でも違法である。Clang側としては、VLAISをサポートする気は一切ない。Clangのエラーメッセージにもその意思がはっきりと現れている。

$ cat vlais.c
int main()
{
    int size = 1 ;
    struct
    {
        char a[size] ;
    } s ;
    return 0 ;
}
$ clang vlais.c
vlais.c:6:14: error: fields must have a constant size: 'variable length array in structure' extension will never be supported
        char a[size] ;
             ^
1 error generated.

そのため、LLVMでLinuxをビルドするプロジェクトであるLLVMLinuxでは、LinuxカーネルのVLAIS利用を書き換えるパッチを作成して、Linuxカーネルに採用してもらうよう働きかけているそうだ。

ところで、C99の規格に、VLAISを禁止する文面が見当たらないのだが、どのへんに書かれているのだろうか。ご存じの方は教えてお貰い申したい。

2013-04-18

bitcoinを大量所有すると身バレする?

The Well Deserved Fortune of Satoshi Nakamoto, Bitcoin creator, Visionary and Genius | bitslog

bitcoinのパフォーマンスと匿名性の設計上のトレードオフにより、大量にbitcoinを所有している個人の匿名性は破綻するのだという話。

特に、初期に大量に確保されたbitcoinは、あのSatoshi Nakamotoによるものではないかと推測している。ということは、Satoshi Nakamotoが自分のbitcoinを使うと、身バレするのではないかとしている。

Satoshi Nakamotoと名乗る個人あるいは団体は、2008年に彗星のごとく現れて、bitcoinの理論を論文で発表し、最初のリファレンス実装を書き、姿を消した。いかにも日本人っぽい名前であり、本人も日本人を自称していたそうだが、日本語は一切使わなかったし、リファレンス実装のソースコードにも日本語は使われていない。Satoshi Nakamotoはbitcoinの発表にあたり、Torを経由して身元を隠していた。

リンク先のコメントでは、使うときに他の共有walletと混ぜてしまえば特定が難しくなるとか、自前で共有walletサイトを立ち上げるだとか。共有walletサイトへのアクセスにTorやVPNを使うなどすれば、身元の特定は困難になるなどと反論されている。

2013-04-17

xkcd: アドビのアップデート全般

xkcd: All Adobe Updates

このアップデートの目的:

  • アドビダウンロードマネージャー

このアップデートによりアドビダウンロードマネージャーの新しいアップデートのダウンロードが可能になります。

Adobeの不自由ソフトウェアの自動アップデートの意味不明さは有名だ。

xkcd: 認証

xkcd: Authorization

もしログインしたままの俺のラップトップが盗まれたら、泥棒は俺のメールを読めるし、俺の金を盗れるし、俺のふりをして俺の友人に接触できる。

まあでも幸い、俺の許可なくドライバーをインストールすることはできない。

cookieなどを使ってWebサイトのログイン状態を保つのは、コンピューターが盗難にあった場合、危険なことになる。

LLVMに電力効率を最適化するコンパイルオプションの議論

[Phoronix] Making A Code Compiler Energy-Aware

LLVMで、電力効率を最適化するコードを生成するコンパイルオプションの可否について議論されているようだ。

コードの最適化というのは、単に実行速度の最適化だけではない。、コードサイズや最適化もある。デバッグモードでのコンパイルも、デバッグに対する最適化と言える。その様々な目的に特化した最適化に、電力効率を加えようという提案だ。

電力効率最適化のコンパイルオプションを加えようという提案は、3年前に出されたが、当時は却下されている。

Bug 6210 – -O3, -Os, -Oe: optimize for running time, binary size, or energy

というのも、ほとんどの場合、電力効率が最大になるコードは、実行速度が最短となるコードである。処理を速く終えることができれば、それだけCPUがアイドル状態に落ちるのも速くできるからだ。したがって、電力効率最適化=実行速度最適化となる。

今回その議論が再燃しているというのは、一部の環境(特にARM SoC)では、メモリの利用の電力消費が相対的に大きく、そのような環境では、計算した値をメモリに記録しておくより、再計算したほうが電力効率が高くなるという状況があるからだという。

2013-04-16

LLVMによる自動C++11移行ツール

LLVM Project Blog: Status of the C++11 Migrator

先月のRSSフィードに一瞬だけ現れたのだが、すぐ消えてしまった記事が復活した。

cpp11-migrateは、LLVMのツールに含まれる、既存のコードをC++11に変換する移行ツールである。

現在のところ、四種類の変換が可能だ。

STLコンテナーや配列の要素をループでなめるコードをRange-based forに変換する。

こんないけてないコードが、

std::vector<int> myVec;
for (std::vector<int>::iterator I = myVec.begin(),
                                E = myVec.end();
     I != E; ++I)
  llvm::outs() << *I;

こんなに格好良くなる。

std::vector<int> myVec;
for (auto & elem : myVec)
  llvm::outs() << elem;

nullポインターの値としての0やNULLの利用を、nullポインターリテラルであるnullptrに変換。

こんないけてないコードが、

int * ptr1 = 0 ;
int * ptr2 = NULL ;

nullポインターリテラルのキーワードを利用したまともなコードに変わる。

int * ptr1 = nullptr ;
int * ptr2 = nullptr ;

auto指定子の変換。

auto指定子は、ほとんどの変数の宣言を置き換えることができるが、このツールでは、特殊な場合のみを置き換える。

STLコンテナーのイテレーター

こんなイケてないコードが、

void f( std::vector< std::string > & v )
{
    std::vector< std::string >::iterator iter = v.begin() ;
}

こんなに超絶カッチョイイコードに早変わり。

void f( std::vector< std::string > & v )
{
    auto iter = v.begin() ;
}

new式を受け取る。

こんな冗長で誤りの元なコードが、

MyType * ptr = new MyType() ;
MyType * const ptr = new MyType() ;

すっきりする。

auto * ptr = new MyType() ;
auto * const ptr = new MyType() ;

これで、変数の型名を間違えてつまらないコンパイルエラーになることはない。

overrideの付加

virtual関数がオーバーライドしている場合、overrideを自動的につけてくれる。

こんなコードでも

struct Base
{
    virtual void func() { }
} ;

struct Derived : Base
{
    virtual void func() { }
} ;

こうなる。

struct Base
{
    virtual void func() { }
} ;

struct Derived : Base
{
    virtual void func() override { }
} ;

関数名のtypoなどといったつまらない間違いをコンパイル時にエラーにできる。

予定されている将来の機能追加では、TR1のライブラリを正式な標準ライブラリに置き換えるだとか、auto_ptrの利用を置き換えるなどといった置換が提案されている。

LLVMの強力な解析とハックしやすいコードベースの力と言えるだろう。GCCの地位がますます脅かされる。

NTFSのAlternate Data Streamとrarアーカイブによるマルウェア隠し

Why questionable downloads use rar archives : Len Boyette

NTFSには、Alternate Data Streamという機能がある。これは、ファイルに複数のストリームを関連付ける機能で、主となるストリーム以外は、基本的にあまり表に出ない。Windowsはファイルのメタデータを保存するのにこの機能を使っている。例えば、インターネット上からダウンロードしたファイルに対して警告を発するのも、特別なAlternate Data Streamを付加しているからで、ファイルのプロパティからこれ以上警告しないという設定するのは、このストリームを削除するという事である。

Alternate Data Streamは、ファイル名の後ろにコロンをつけることで使える。例えば、"example.txt"という名前のファイルがある場合、"example.txt:secret"などというように。

Alternate Data Streamは、なかなか表に出ない。GUIのエクスプローラーでは直接は表示されない。コマンドdir /rで表示される位だ。しかも、Alternate Data Streamから実行されるプロセスは、タスクマネージャーにも現れない。これは、例えば表のストリームはファイルのAlternate Data Streamを実行するようなプログラムにしておけば、単に軽くファイルを検証しただけではマルウェアであることが分かりにくいということだ。

世の中の多くのアンチマルウェアソフトウェアは、Alternate Data Streamを調べない。

もちろん、普通のプログラムが扱うのは主のストリームであり、Alternate Data Streamはほとんど考慮されない。多くのアーカイバーソフトウェアも、Alternate Data Streamは無視する。従って、ネットワーク越しにファイルを送る場合、Alternate Data Streamは送られない。ただし、Alternate Data Streamにも対応している有名なアーカイバーがひとつだけある。rarだ。

WinRarは、Alternate Data Streamまで含めてアーカイブ、展開する機能を持っている。rarを使えば、Alternate Data Streamを含めてファイルをネットワーク越しに渡せるのだ。

市場の60%のアンチマルウェアソフトウェアは、このrarファイルや、Alternate Data Streamを使ったマルウェアを正しく検出できない。

元記事では、7zipなどのWindows用に設計された、よりより圧縮率、より良い暗号化方式、Unicodeサポート、並列処理などのモダンなアーカイブ形式があるにも関わらず、いまだにrarも使われているのは、ひょっとしたらこのためかと推測している。Windows用のマルウェアをAlternate Data Streamに隠して運ぶためではないのか。

2013-04-15

C++で物理アドレスの取得

GNU/Linuxのsysfsを使用した版。

#include <iostream>
#include <fstream>
#include <string>

int main()
{
    std::ifstream stream( "/sys/class/net/eth0/address", std::ios::in ) ;

    if ( !stream )
    {
        std::cout << "failed to open /sys/class/net/eth0/address" << std::endl ;
    }

    std::string mac_address ;
    stream >> mac_address ;

    std::cout << mac_address << std::endl ;
}

GNU/Linuxだとsysfsの/sys/class/net/eth0/addressを読み込むのが一般的なんですかね。

リファレンス元:C++ で物理アドレスの取得 - C++でゲームプログラミング

一応、getifaddrsを使うのがsysfsには頼らない方法だ。というより、manページにそのままズバリのサンプルコードが載っている。

getifaddrs(3) - Linux manual page

というより、なんでWIndowsはgetifaddrsじゃなくてGetAdaptersAddressesなんだろう。

DRMフリーを謳ってKickstarterで開発資金集めをしたゲーム、約束を破る

The DRM-Free Version Of Shadowrun Returns Sounds Kinda Lame

Shadowrunという不自由なソフトウェアのゲームが、最近はやりのCrowd FundingのKickstarterで資金を募った。謳い文句に、DRMフリーを掲げていた。

もちろん不自由なソフトウェアなので実行してならないが、DRMフリーを謳うのだけは、多少は評価できないこともない。多少は。

さて、ゲームは完成した。ただし、DRMフリーの約束は、部分的にしか守られなかった。Kickstarterで投資をした人間限定でDRMフリーなリリース版のコピーが配られる。

将来のアップデートパッチやDLCは、邪悪なDRMでガチガチに固められたゲーム流通プラットフォームのSteamで提供される。

DRMフリー版のコピーが得られるのは初期投資をした人間だけ、それもアップデートもDLCもないリリース当日のコピーが得られるだけ。これは詐欺だ。

開発社の言い訳としては、Microsoftとの契約で、ゲームとDLCはDRM付きで流通されなければならないからだということだ。理由はどうあれ、すでにDRMフリーを謳って資金を集めた以上、それを果たさないのは契約違反であると言える。Microsoftとの契約は、出資者との契約より重要らしい。

投資をした人間は、結果的にDRM付きの邪悪なソフトウェアを世に送り出した詐欺の片棒を担いだことになる。DRMフリーを謳うCrowd Fundingには手を出さないほうが懸命だ。

2013-04-14

最新のconstexpr

constexprは何度も解説しているが、だいぶ昔のドラフトを元にしていたので、正式な規格とはずれている。今書いている参考書はもうしばらく時間がかかるので、とりあえずその間をつなぐために、constexprについて解説する。

constexpr変数

constexpr指定子をつけてリテラル型の変数を宣言すると、その変数はコンパイル時定数になる。変数の初期化式はコンパイル時定数でなければならない。

void f()
{
    constexpr int x = 10 ; // OK、コンパイル時定数

    int r = 0 ;
    constexpr int error = r ; // エラー
}

constexpr指定子をつけて関数を宣言すると、constexpr関数になる。constexpr関数は、コンパイル時に評価される。ただし、constexpr関数にはとても厳しい制限がある。

まず、仮引数の型と戻り値の型は、リテラル型でなければならない。virtual関数であってはならない。関数の本体は、=defaultか、=deleteか、あるいは複合文で、しかもその中身が極端に制限される。

constexpr int f()
{
    ; // null文
    static_assert( true, "..." ) ; // static_assert宣言
    typedef int type1 ; // typedef宣言
    using type2 = int ; // エイリアス宣言
    using std::vector ; // using宣言
    using namespace std ; // using directive

    return 0 ; // 唯一のreturn文
}

これ以外の文を書くことはできない。これでもだいぶ制限が緩和されたのだ。昔のドラフトでは、return文ひとつの他には何も書けなかった。

もちろん、変数の宣言なんてできない。(x+y)(x+y)を返すconstexpr関数は以下のようになる。

// エラー
constexpr int error( int x, int y )
{
    constexpr int temp = x + y ;
    return temp * temp ;
}

// OK
constexpr int ok( int x, int y )
{
    return (x + y) * (x + y) ;
}

if文も使えないので、条件分岐は条件演算子や論理和や論理積を使うことになる。|x + y|を返すconstexpr関数は以下のようになる。

// エラー
constexpr int error( int x, int y )
{
    constexpr int temp = x + y ;
    if ( temp < 0 )
        return -temp ;
    else
        return temp ;
}

// OK
constexpr int ok( int x, int y )
{
    return (x + y) < 0 ? -(x + y) : (x + y) ;
}

もちろん、絶対値の計算を別のconstexpr関数で実装して丸投げすることは可能だ。

ループが使いたければ再帰だ。aのb乗(a,bは正の整数)を計算するconstexpr関数は以下のようになる。

// エラー
constexpr unsigned long error( unsigned long a, unsigned long b )
{
    unsigned long result = 1 ;

    for ( ; b != 0 ; --b )
    {
        result *= a ;
    }

    return result ;
}

// OK
constexpr unsigned long ok( unsigned long a, unsigned long b )
{
    return b == 0 ? 1 : a * ok( a, b-1 ) ;
}

この問題に関しては、中には再帰の方が分かりやすいと考える人もいるかもしれないが、あらゆるループを再帰で書くのは面倒だ。

現在、constexpr関数の制限を大幅に緩和して、普通に変数や任意の文を使えるようにすることが議論されている。

ちなみに、constexpr関数に対してコンパイル時定数以外を渡すと、実行時評価される。

constexpr int f( int x )
{
    return x ;
}
void g( int x )
{
    int result = f( x ) ; // 実行時評価
}

constexpr指定子をコンストラクターに指定すると、constexprコンストラクターとなる。constexprコンストラクターにはとても厳しい制限がある。constexpr関数の制限と似ているが、こちらはreturn文すら使えない。さらに、クラスのサブオブジェクトをすべて初期化しなければならない。この他にも厳しい制限が多数あるが、見事すべての制限を満たした暁には、なんとconstexprコンストラクターを持つクラスは、リテラル型になるのだ。

// クラスXはリテラル型
struct X
{
    int x ;
    int y ;

    constexpr X( int x = 0, int y = 0 )
        : x(x), y(y)
    { }
} ;

リテラル型ということは当然、constexpr変数の型や、constexpr関数の仮引数や戻り値の型として使えるという事だ。

struct Integer
{
    int member ;
    constexpr Integer( int value = 0 )
        : member( value )
    { }
    constexpr operator int() { return member ; }
} ;

constexpr Integer f( Integer i )
{
    return i + 1 ;
}


int main()
{
    constexpr Integer i = 10 ;
    constexpr Integer result = f( i ) ;

    std::cout << result << std::endl ;
}

つまり、constexprコンストラクターを持つクラスはコンパイル時定数として使えることを意味する。

とまあ、constexprは規格にして4ページ程度の小さな機能である。

2013-04-13

もし独裁国家が大真面目に健康を人民に強制したらどうなるのっと

The Perfect Health Regimen that Only an Absolute Dictator Could Impose

なかなか面白かった。長いので要約としてまとめる。

我々は、明らかに健康に良い悪いと分かっている習慣があっても、しばしば従わないものである。毎日、多種目の野菜やフルーツを摂取する。適度に運動する。十分に睡眠を取る。あるいは、歯磨き、フロス、ストレッチ、ストレスをためないなど、おそらく健康に良く長寿に関係するとされている習慣を行わない

これは由々しき事態である。もし、独裁国家が、現在、多くの論文で取り上げられていて相関性があると考えられている健康と長寿に貢献する条件を人民に強制するとしたならば、それはどのような世界になるのだろうか。

結論はディストピアだ。

食事は、味気のない植物性および動物性由来のタンパク質とウジ虫を真水で流しこむことになる。ただし、アルコールは一日5グラム(ワイン換算で80ミリリットル程度)ほど摂取できる。そして、退屈だが安全な運動を行う。これは序の口だ。

この新社会においては、全人民が可能な限り健康的な生活を健康的な場所で全うするのだ。

つまり、我々の居住区は高地に設けられた収容所になる。統計的に、標高の高い土地にすむ人間の方が寿命が長い。これは高地は低地に比べて酸素が欠乏しがちであり、我々の肉体は環境に適応するためより効率的に機能するようになるためだと考えられている。ただし、高地は夏暑く冬寒く、老人には辛い環境だ。そのため、我々は高地の集中出産所で産まれ、幼年時代を高地で過ごし、歳を取るに従って居住区を低地に移動していくことになるだろう。

基本的に、人民の旅行は禁止されている。人の移動は空気感染する感染症を拡大させてしまうからだ。しかも飛行機は被曝する。高度が高くなると、大気で阻害される低地に比べて宇宙線を受けやすくなるのだ。たまに移動する分にはそれほど影響がないだろう。ただし、パイロットと客室乗務員は、右翼労働組合の主張により、被曝する危険性のある労働者とみなされ、定期的な健康診断が義務付けられるだろう。太陽活動は11年に一度活発化するので、その時期には特別な注意が必要だ。

国外から入国してくる人間には、検疫が義務付けられるだろう。特にインフルエンザの季節などは、航空機による入国が制限され、国は事実上鎖国する。

ストレスは寿命に影響する。そのため、人民は幸福を義務付けられる。

多くの研究が、友人の数と寿命に長さには相関性があることを示している。多くの友人を持つことを国家が義務づけ、また保証しなければならない。

配偶者と子の存在は、いくつかの研究では、一見すれば長寿命に影響するようにみえる。しかし、多くの研究には欠陥がある。

多くの研究で、結婚は寿命を伸ばすと結論している。しかし、この寿命への影響というのは、多くの研究で、男にとっての影響である。女は結婚してもほとんど寿命に影響がない。いくつかの研究などでは、最初から男の寿命しか考慮していない。

多くの研究では、離婚者と未婚者を一緒くたに結婚していないグループに分類している。これは明らかに誤りである。離婚は明らかに寿命に影響することが示されている。離婚は多大なストレスを伴うものであり、当然の結果だろう。離婚したものと、一度も結婚していないものを分けて考えると、結婚はそれほど長寿化に寄与していない。一生結婚したものと、一生結婚しないものでは、それほど寿命に差がない。それでいて、結婚には離婚という低寿命化のリスクを含んでいる。

故に、健康を義務付ける独裁国家は一対の男女が生涯連れそう現在の結婚システムを禁止すべきである。それほど利点もなくリスクの多い欠陥システムに人民を晒すわけにはいかない。

我らの健康第一主義の独裁国家は、結婚の定義を、愛情によるものではなく、健康のための処方箋にするべきである。結婚はうまく行けば寿命を伸ばすが、素人が行うにはあまりにもリスクが大きい。結婚は個人が独断で行うのではなく、プロの手に任せるべきである。

現在、そのような適切な結婚システムに関するデータが不足している。そのため、政府は異なる結婚システムを実施して、その結果を比較した上で最適な結婚システムを決定すべきである。新しく設計された離婚リスクのない結婚システムは人民の長寿命化に貢献するだろう。

早期退職は寿命に悪影響を与えることが分かっている。ただし、これは仕事への意欲があり、しかも健康的な人間が早期に退職した場合である。仕事が嫌で病弱な人間には早期退職は長寿命化に貢献する。そのため、退職時期はフレキシブルにすべきである。

宗教は寿命の寄与することが知られている。この理由に関しては、いくつか挙げられている。宗教信者はそうでないものに比べて、相対的に健康に良い習慣を持っているのではないか。教会に集まるなどの定期的な会合が地域コミュニティによるお互いの健康確認を行わせているのではないか。信仰心によりストレスが下がるのではないか等だ。どんな理由にせよ、宗教は長寿命化に寄与している。独裁国家は宗教を義務付けるべきだ。宗教を固定する必要はないし、どんな宗教でもかまわない。空飛ぶスパゲティモンスター教でもいい。とにかく人民に宗教を義務付けなければならない。

さて、ここまであらゆる健康と長寿命に寄与する要素を独裁国家の強権により義務づけてきた。これらを施行した社会には、一体何が待ち受けているのか。後は何が長寿命化を妨げる要因になっているのであろうか。

答えは、男だ。

あらゆる社会で、女は男より長生きすることが統計的に明らかになっている。インドのような男女差が半年という国もあれば、男女差13年に及ぶ国もある。習慣を改善することで、寿命差を縮められるかもしれないが、もし、あらゆる努力をもってしても男女差が縮まらなければ、健康第一の独裁国家は、やはり決断しなければならない。男を減らすのだ。

もちろん、現在のところ、種を存続させるためには男が必要である。健康的な人口を維持できるだけの数の男は保ちつつ、男を減らすというのは難しい問題である。おそらく多数の実験が必要であるが、あまりいい実験ではないだろう。

ブラウザーのCanvas/WebGLのブラックリスト

各ブラウザーのCanvasやWebGLのGPUやドライバーによるブラックリストの具体的な内容がどこかに載っていないかと探したので、忘れないようにメモしておく。各ブラウザーといっても、自由なソフトウェア実装のブラウザーでCanvasやWebGLをサポートしていて主要なものは、FirefoxかChromiumしかないが。

まず、総合的な情報としては、khronosのWikiが便利だ。

BlacklistsAndWhitelists - WebGL Public Wiki

Firefoxでは、以下のページで詳細が公開されている。

Blocklisting/Blocked Graphics Drivers - MozillaWiki

GNU/Linux環境では、Cairo経由でXRenderを使う(Canvas 2Dはこれにあたるか?)。これにはブラックリストはない。WebGLはデフォルトで有効化されている。よほど古いMesaやプロプラドライバーな環境でなければブラックリストには引っかからない。GL layers accelerationは、今のところ無効化されている。

Chromiumは公式にそのようなドキュメントはないので、ソースを読むしかない。ブラックリストはJSONで定義されていて、以下から読める。

[chrome] Contents of /trunk/src/content/browser/gpu/software_rendering_list.json

GNU/Linuxでは、Canvas 2DのGPU利用は無条件で無効化されている。また、WebGLもデフォルトでソフトウェア実装のものが使われている。

2013-04-12

Fedora 19 Alphaリリースが一週間遅れるそうだ

[Phoronix] Fedora 19 Alpha Gets Its First Delay Due To UEFI

Releases/19/Schedule - FedoraProject

理由はUEFI絡みのバグに対処するためだそうだ。これにより、マイルストーンの予定も軒並み一週間ずれることになる。Fedora 19(シュレディンガーの猫)のリリースは6月25日から7月2日になった。

もっとも、延期がこれだけですめばの話だが。Fedora 18も真空引きした中にある球体の牛を仮定しなければならず遅れに遅れたので、中を観測するまで結果がわからない今回の猫も、やはりリリースされるまで日付がわからないのではないだろうか。

2013-04-10

様々なUNIX環境のecho.cの比較

UNIX V5, OpenBSD, Plan 9, FreeBSD, and GNU coreutils implementations of echo.c

UNIX Fifth Editionのecho.cは、以下のような実装になっている。

main(argc, argv)
int argc;
char *argv[];
{
 int i;
 
 argc--;
 for(i=1; i<=argc; i++)
  printf("%s%c", argv[i], i==argc? '\n': ' ');
}

いかにも昔のC言語らしいコードだ。ヘッダーの#includeはなく、関数の戻り値の型も指定されない。仮引数の型も、今となっては物珍しいだろうが後書きだ。

OpenBSDのコードは以下の通り。

/* $OpenBSD: echo.c,v 1.7 2009/10/27 23:59:21 deraadt Exp $ */
/* $NetBSD: echo.c,v 1.6 1995/03/21 09:04:27 cgd Exp $ */
 
/*
 * Copyright (c) 1989, 1993
 * The Regents of the University of California.  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 the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
/* ARGSUSED */
int
main(int argc, char *argv[])
{
 int nflag;
 
 /* This utility may NOT do getopt(3) option parsing. */
 if (*++argv && !strcmp(*argv, "-n")) {
  ++argv;
  nflag = 1;
 }
 else
  nflag = 0;
 
 while (*argv) {
  (void)fputs(*argv, stdout);
  if (*++argv)
   putchar(' ');
 }
 if (!nflag)
  putchar('\n');
 
 return 0;
}

OpenBSDもだいぶ簡単なコードだ。ただし、最初の引数が"-n"の場合は、その引数の出力は飛ばして、最後に改行を出力しないようになっている。また、printfのかわりにfputsを利用している。

Plan 9のコードは以下の通り。

#include <u.h>
#include <libc.h>
 
void
main(int argc, char *argv[])
{
 int nflag;
 int i, len;
 char *buf, *p;
 
 nflag = 0;
 if(argc > 1 && strcmp(argv[1], "-n") == 0)
  nflag = 1;
 
 len = 1;
 for(i = 1+nflag; i < argc; i++)
  len += strlen(argv[i])+1;
 
 buf = malloc(len);
 if(buf == 0)
  exits("no memory");
 
 p = buf;
 for(i = 1+nflag; i < argc; i++){
  strcpy(p, argv[i]);
  p += strlen(p);
  if(i < argc-1)
   *p++ = ' ';
 }
  
 if(!nflag)
  *p++ = '\n';
 
 if(write(1, buf, p-buf) < 0){
  fprint(2, "echo: write error: %r\n");
  exits("write error");
 }
 
 exits((char *)0);
}

-nオプションはOpenBSDと同じだ、実装はだいぶ違う。まず出力すべき文字数を計算した上でメモリを確保し、確保したメモリ上に文字列をすべてコピーしたうえで、writeを一回だけ呼び出して一発で出力している。

UNIX V5やOpenBSDに比べれば、Plan 9の実装はメモリー確保をする代わりにシステムコール呼び出しを一回に抑えるコードのように見える。ただし、printfやfputsを使うコードは、libc側でバッファをしているので、このような低級な処理をlibcにまかせているともいえる。そのためUNIX V5やOpenBSDにはメモリ確保失敗の恐れがないというわけではない。自動的なバッファよりも、明示的なバッファの方が当然効率がいい。ただし、果たして引数を出力するecho程度に、そのような効率化は必要だろうか。

FreeBSDのコード。

/*-
 * Copyright (c) 1989, 1993
 *      The Regents of the University of California.  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.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
 
#if 0
#ifndef lint
static char const copyright[] =
"@(#) Copyright (c) 1989, 1993\n\
        The Regents of the University of California.  All rights reserved.\n";
#endif /* not lint */
 
#ifndef lint
static char sccsid[] = "@(#)echo.c      8.1 (Berkeley) 5/31/93";
#endif /* not lint */
#endif
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
 
#include <sys/types.h>
#include <sys/uio.h>
 
#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
 
/*
 * Report an error and exit.
 * Use it instead of err(3) to avoid linking-in stdio.
 */
static __dead2 void
errexit(const char *prog, const char *reason)
{
        char *errstr = strerror(errno);
        write(STDERR_FILENO, prog, strlen(prog));
        write(STDERR_FILENO, ": ", 2);
        write(STDERR_FILENO, reason, strlen(reason));
        write(STDERR_FILENO, ": ", 2);
        write(STDERR_FILENO, errstr, strlen(errstr));
        write(STDERR_FILENO, "\n", 1);
        exit(1);
}
 
int
main(int argc, char *argv[])
{
        int nflag;      /* if not set, output a trailing newline. */
        int veclen;     /* number of writev arguments. */
        struct iovec *iov, *vp; /* Elements to write, current element. */
        char space[] = " ";
        char newline[] = "\n";
        char *progname = argv[0];
 
        /* This utility may NOT do getopt(3) option parsing. */
        if (*++argv && !strcmp(*argv, "-n")) {
                ++argv;
                --argc;
                nflag = 1;
        } else
                nflag = 0;
 
        veclen = (argc >= 2) ? (argc - 2) * 2 + 1 : 0;
 
        if ((vp = iov = malloc((veclen + 1) * sizeof(struct iovec))) == NULL)
                errexit(progname, "malloc");
 
        while (argv[0] != NULL) {
                size_t len;
 
                len = strlen(argv[0]);
 
                /*
                 * If the next argument is NULL then this is this
                 * the last argument, therefore we need to check
                 * for a trailing \c.
                 */
                if (argv[1] == NULL) {
                        /* is there room for a '\c' and is there one? */
                        if (len >= 2 &&
                            argv[0][len - 2] == '\\' &&
                            argv[0][len - 1] == 'c') {
                                /* chop it and set the no-newline flag. */
                                len -= 2;
                                nflag = 1;
                        }
                }
                vp->iov_base = *argv;
                vp++->iov_len = len;
                if (*++argv) {
                        vp->iov_base = space;
                        vp++->iov_len = 1;
                }
        }
        if (!nflag) {
                veclen++;
                vp->iov_base = newline;
                vp++->iov_len = 1;
        }
        /* assert(veclen == (vp - iov)); */
        while (veclen) {
                int nwrite;
 
                nwrite = (veclen > IOV_MAX) ? IOV_MAX : veclen;
                if (writev(STDOUT_FILENO, iov, nwrite) == -1)
                        errexit(progname, "write");
                iov += nwrite;
                veclen -= nwrite;
        }
        return 0;
}

FreeBSDのコードは、Plan 9よりもっと高度なことをしている。Plan 9は、出力すべき文字列が全部収まる量のメモリを確保して、整形を加えた上でメモリにコピーしていた。FreeBSDはそのコピーをしない。ポインターと文字数を格納するiovec構造体の配列を動的に確保し、argvをポインターとして格納し、さらに整形も間に挟む。そして、iovec構造体の配列を一括して出力するwritevを呼び出している。これにより、文字列のコピーを防いでいる。

ただ、所詮は引数として渡される程度の文字列なだけに、そこまでしてコピーを忌避するほどの価値があるかどうか。

ちなみに、Mac OS Xのecho.cも公開されている。これはFreeBSDそのままだ。

さて、とうとうGNU coreutilsの実装だ。

/* echo.c, derived from code echo.c in Bash.
   Copyright (C) 1987, 1989, 1991-1997, 1999-2005, 2007-2011 Free Software
   Foundation, Inc.
 
   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.
 
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
 
   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
#include <config.h>
#include <stdio.h>
#include <sys/types.h>
#include "system.h"
 
/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "echo"
 
#define AUTHORS \
  proper_name ("Brian Fox"), \
  proper_name ("Chet Ramey")
 
/* If true, interpret backslash escapes by default.  */
#ifndef DEFAULT_ECHO_TO_XPG
enum { DEFAULT_ECHO_TO_XPG = false };
#endif
 
void
usage (int status)
{
  if (status != EXIT_SUCCESS)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
             program_name);
  else
    {
      printf (_("\
Usage: %s [SHORT-OPTION]... [STRING]...\n\
  or:  %s LONG-OPTION\n\
"), program_name, program_name);
      fputs (_("\
Echo the STRING(s) to standard output.\n\
\n\
  -n             do not output the trailing newline\n\
"), stdout);
      fputs (_(DEFAULT_ECHO_TO_XPG
               ? N_("\
  -e             enable interpretation of backslash escapes (default)\n\
  -E             disable interpretation of backslash escapes\n")
               : N_("\
  -e             enable interpretation of backslash escapes\n\
  -E             disable interpretation of backslash escapes (default)\n")),
             stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
If -e is in effect, the following sequences are recognized:\n\
\n\
"), stdout);
      fputs (_("\
  \\\\      backslash\n\
  \\a      alert (BEL)\n\
  \\b      backspace\n\
  \\c      produce no further output\n\
  \\e      escape\n\
  \\f      form feed\n\
  \\n      new line\n\
  \\r      carriage return\n\
  \\t      horizontal tab\n\
  \\v      vertical tab\n\
"), stdout);
      fputs (_("\
  \\0NNN   byte with octal value NNN (1 to 3 digits)\n\
  \\xHH    byte with hexadecimal value HH (1 to 2 digits)\n\
"), stdout);
      printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
      emit_ancillary_info ();
    }
  exit (status);
}
 
/* Convert C from hexadecimal character to integer.  */
static int
hextobin (unsigned char c)
{
  switch (c)
    {
    default: return c - '0';
    case 'a': case 'A': return 10;
    case 'b': case 'B': return 11;
    case 'c': case 'C': return 12;
    case 'd': case 'D': return 13;
    case 'e': case 'E': return 14;
    case 'f': case 'F': return 15;
    }
}
 
/* Print the words in LIST to standard output.  If the first word is
   `-n', then don't print a trailing newline.  We also support the
   echo syntax from Version 9 unix systems. */
 
int
main (int argc, char **argv)
{
  bool display_return = true;
  bool allow_options =
    (! getenv ("POSIXLY_CORRECT")
     || (! DEFAULT_ECHO_TO_XPG && 1 < argc && STREQ (argv[1], "-n")));
 
  /* System V machines already have a /bin/sh with a v9 behavior.
     Use the identical behavior for these machines so that the
     existing system shell scripts won't barf.  */
  bool do_v9 = DEFAULT_ECHO_TO_XPG;
 
  initialize_main (&argc, &argv);
  set_program_name (argv[0]);
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);
 
  atexit (close_stdout);
 
  /* We directly parse options, rather than use parse_long_options, in
     order to avoid accepting abbreviations.  */
  if (allow_options && argc == 2)
    {
      if (STREQ (argv[1], "--help"))
        usage (EXIT_SUCCESS);
 
      if (STREQ (argv[1], "--version"))
        {
          version_etc (stdout, PROGRAM_NAME, PACKAGE_NAME, Version, AUTHORS,
                       (char *) NULL);
          exit (EXIT_SUCCESS);
        }
    }
 
  --argc;
  ++argv;
 
  if (allow_options)
    while (argc > 0 && *argv[0] == '-')
      {
        char const *temp = argv[0] + 1;
        size_t i;
 
        /* If it appears that we are handling options, then make sure that
           all of the options specified are actually valid.  Otherwise, the
           string should just be echoed.  */
 
        for (i = 0; temp[i]; i++)
          switch (temp[i])
            {
            case 'e': case 'E': case 'n':
              break;
            default:
              goto just_echo;
            }
 
        if (i == 0)
          goto just_echo;
 
        /* All of the options in TEMP are valid options to ECHO.
           Handle them. */
        while (*temp)
          switch (*temp++)
            {
            case 'e':
              do_v9 = true;
              break;
 
            case 'E':
              do_v9 = false;
              break;
 
            case 'n':
              display_return = false;
              break;
            }
 
        argc--;
        argv++;
      }
 
just_echo:
 
  if (do_v9)
    {
      while (argc > 0)
        {
          char const *s = argv[0];
          unsigned char c;
 
          while ((c = *s++))
            {
              if (c == '\\' && *s)
                {
                  switch (c = *s++)
                    {
                    case 'a': c = '\a'; break;
                    case 'b': c = '\b'; break;
                    case 'c': exit (EXIT_SUCCESS);
                    case 'e': c = '\x1B'; break;
                    case 'f': c = '\f'; break;
                    case 'n': c = '\n'; break;
                    case 'r': c = '\r'; break;
                    case 't': c = '\t'; break;
                    case 'v': c = '\v'; break;
                    case 'x':
                      {
                        unsigned char ch = *s;
                        if (! isxdigit (ch))
                          goto not_an_escape;
                        s++;
                        c = hextobin (ch);
                        ch = *s;
                        if (isxdigit (ch))
                          {
                            s++;
                            c = c * 16 + hextobin (ch);
                          }
                      }
                      break;
                    case '0':
                      c = 0;
                      if (! ('0' <= *s && *s <= '7'))
                        break;
                      c = *s++;
                      /* Fall through.  */
                    case '1': case '2': case '3':
                    case '4': case '5': case '6': case '7':
                      c -= '0';
                      if ('0' <= *s && *s <= '7')
                        c = c * 8 + (*s++ - '0');
                      if ('0' <= *s && *s <= '7')
                        c = c * 8 + (*s++ - '0');
                      break;
                    case '\\': break;
 
                    not_an_escape:
                    default:  putchar ('\\'); break;
                    }
                }
              putchar (c);
            }
          argc--;
          argv++;
          if (argc > 0)
            putchar (' ');
        }
    }
  else
    {
      while (argc > 0)
        {
          fputs (argv[0], stdout);
          argc--;
          argv++;
          if (argc > 0)
            putchar (' ');
        }
    }
 
  if (display_return)
    putchar ('\n');
  exit (EXIT_SUCCESS);
}

GNUの実装は、基本的にはOpenBSDのようにループを回してfputsで出力する。だかそれだけではない。

まず、とても便利なヘルプメッセージを表示する--helpがある。しかも、ただのヘルプメッセージではなく、ちゃんとgettextによる翻訳にも対応している。さらに、バックスラッシュによるエスケープ(\nを改行に置換したりhexを文字に変換するなどの)機能がデフォルトで有効になっている。これを無効にするための-Eオプションもある。

ただしGNU coreutilsのコードは汚い。関数を使わずループをネストさせ、あまつさえgotoを使ってネストしたループから脱出している。

伝統的に、GNUのツールはやたらと独自拡張を提供している。古き良きUNIXの理念に従えば、一つのことのみしっかりと行うツールを組み合わせるべきだが、実際のところ、誰も echo "hello\nworld" | エスケープ置換 | 末尾の改行削除 などと書くよりも、echo -n -e "hello\nworld" と書きたがった。そんなわけで、不自由な商用UNIXでも、GNU coreutilsだけはいれて使うような運用が流行ったのも、不思議ではない。

さて、実際のところ、このコードが使われることはまずない。なぜならば、ほとんどの環境で、echoはシェルの組み込みコマンドだからだ。

type -a echo

WaylandでIBusに対応する提案

[Phoronix] Wayland Support For IBus Proposed

[PATCH 00/18] Extend text protocol for IBus support

必要なプロトコルの変更をするパッチが投下されている。変更はコード行数にして1600行ほどらしい。

2013-04-09

Firefox 23では、デフォルトでSSLページ内で非SSLコンテンツの読み込みをブロックする

Site Compatibility for Firefox 23 | MDN

Firefox 23では、とうとうSSLページ内から非SSLコンテンツのロードをブロックするそうだ。

つまりとても簡単に言うと、httpsなページ内からhttpなURLで提供されるCSSやスクリプトやプラグインやiframeが読み込まれなくなる。しかし、Web FontやWebSocketにまで言及しているのに、何故か画像には言及していない。まさか画像だけは特別扱いするのだろうか。動画や音声も言及されていないがどうなのだろうか。

どうも、Nightlyの動作では、やはり画像だけは特別扱いのようだ。

ちなみに、Firefox 23は2013年の8月6日にリリースされる予定だ。

2013-04-08

LED可視光通信

世界初!LED可視光通信とは|超小型衛星「ShindaiSat」|信州大学

面白かったのでメモ代わりに。

衛星から使えるLEDによる可視光通信をまともに研究しているらしい。

電波は混戦したり、受信設備が大規模だったり、免許が必要になったりと、小規模で実験的な小型衛星の通信方法としては難しい。

じゃあ可視光でやればいいじゃんということで、そのためのLEDを開発したそうだ。

2013-04-07

フランスの国内情報中央局がWikipediaのsysopを脅迫して記事を削除させる

French homeland intelligence threatens a volunteer sysop to delete a Wikipedia Article | Autour de Wikipédia et des projets Wikimedia

フランスの国内情報中央局が、法的手続きを踏まずしてWikipediaの記事の削除権限をもつボランティアを呼びつけて脅迫し、記事の削除を強制させたそうだ。

[CC-BY-SA 2.0]

3月の初め、DCRI(フランスの国内情報中央局)がWikipediaをホストする非営利団体のWikimedia財団と連絡を取った。彼らはフランスの軍事基地関するあるフランス言語Wikipedia記事に軍事機密情報が含まれると主張し、即座に削除することを要求した。Wikimedia財団はこれを考慮し、彼らは十分な情報を提供していないと考え、彼らの要求を拒否した。 Wikimedia財団は国家組織に協調し法的な決定には従う。毎年数百件もの要求があり、十分な理由があれば、これに従っている。

財団の答えに不満足なDCRIは、4月4日にWikipediaのボランティアを彼らの事務所に呼びつけた。このページの削除が可能な権限を持つボランティアは、DCRIの事務所内で、従わない場合は拘束され検挙されると判断した上で、該当記事の削除を強制された。DCRIにWikipediaはそのように運営されるものではないと説明したものの、圧力の下、彼は記事を削除する以外の選択肢を持たなかった。彼は他のsysopに対し、記事の復帰はおそらく法的責任を負うだろうと警告した。

このボランティアはこれまで該当記事とか変わりがなく、DCRIの事務所に入るまでは、一度も同記事を編集したことがなく、また同記事の存在すら知らなかった。彼が選ばれて呼びつけられた理由は、彼がフランスにおけるWikipediaとWikimediaプロジェクトの啓蒙活動に頻繁に顔を出していたことにより、簡単に突き止められたからである。

Wikimediaフランスは、自由と知識を追求する人物に対し、脅迫と強制による手法を用いたことを理解しかねる。自由知識を支持するWikimediaフランスとして、フランス市民とWikipedia編集者に対しこのような検閲を行うことに抗議する。

Wikipediaを編集することがいつからフランス国内で法的に危険な行動となったのだ? DCRIは軍事機密の施行に、法的で、暴力的ではない方法を使えないのか?

同記事はその最後の数日を除いて長年、何の問題も起こさず存在してきた。脅迫は、フランスにおいて軍事機密を守るにあたって適切な方法ではなく、インターネットはそのような暴力的な方法で規制される場所ではない。DCRIは法律の施行にあたって、他の方法があったはずであると我々は信ずる。この問題を独立した調査により解決することを我々は望む。フランスは法治国家であり、国家安全をこのような方法で施行されるべき場所ではないのだ。

原文ではDCRIの事務所に呼びつけたのは、召喚(summon)となっているが、どうも記事の様子からすると、通常の召喚手続きを踏んで行われた法的拘束力のあるものではなさそうで、あるいはフランス語から英語に翻訳する際の誤りかもしれぬので、「呼びつけた」と訳しておいた。

どうやら、問題の記事はこれらしい。

Station hertzienne militaire de Pierre-sur-Haute - Wikipédia

同記事へのGoogle Translate経由のリンク

同記事に対応する英語版記事

ちなみに、記事は実際には消されていない。これはWikipediaは更新履歴をすべて保存しているし、管理者一人が消せる範囲は限られているためだ。

というわけで、記事は一時的に見えなくなっただけであり、すぐに復帰され、しかも今回のDCRIの検閲事実や、記事の内容のどこかに軍事機密を含むという記述まで追加されてしまった。さらに、記事に対応する他言語版が急速に増えつつある。多くの言語で、作成日時が、Wikimediaフランスが抗議声明を発表した以降の2013年4月6日以降の日付になっていることから、この事件を受けてから作成されたのだろう。

Redditで皮肉られているように、DCRIが次に規制すべき記事はストライサンド効果 - Wikipediaだろう。

追記:

French spy agency tries to pull 'classified' Wikipedia entry, only draws more attention to it | ZDNet

ZDNetの記事によれば、該当のWikipediaの記事は、公開されているフランス軍人へのインタビュー動画やその他の公開情報をソースにして作成されており、Wikipediaの記事は単に散らばっている情報を集めただけに過ぎないという。

2013-04-08追記:

ピエール・シュール・オート軍用無線局 - Wikipedia

日本語版の記事も追加された。どうやら英語版からの翻訳のようだ。

2013-04-06

C++11のfinalとoverride

C++11には、finalとoverrideがある。クラスにはfinalを指定できる。virtual関数にはfinalとoverrideを指定できる。

クラスにfinalを指定すると、そのクラスを基本クラスに指定できなくなる。

struct final_class final { } ;
// エラー、final_classは基本クラスに指定できない
struct error : final_class { } ;

virtual関数にfinalを指定すると、そのvirtual関数をそれ以上オーバーライドできなくなる。

struct base
{
    virtual void f() { } 
} ;

struct derived : base
{
    virtual void f() final { } 
} ;

struct error : derived
{
    // エラー
    virtual void f() { } 
} ;

virtual関数にoverrideを指定すると、そのvirtual関数がオーバーライドしていない場合、エラーとなる。

struct base
{
    virtual void virtual_function() { }
} ;

struct typo_error : base
{
    // エラー、オーバーライドしていない
    virtual void virtal_function () override { }
} ;
struct base
{
    void virtual_function() { }
} ;

struct non_virtual_error : base
{
    // エラー、オーバーライドしていない
    virtual void virtual_function () override { }
} ;

これにより、しょうもないtypoをコンパイル時に検出できる。

ドラフトではもうひとつ、基本クラスのスコープのメンバー名を派生先のクラスで隠している(hiding)ことを明示的に指定するための機能があったのだが、いろいろな理由で消された。これはメンバー関数以外のメンバーにも適用する必要があるため、文法の問題がややこしく、時間がなかったのだ。

AMIのソースコードと秘密鍵が公開FTP経由で流出?

Security Done Wrong: Leaky FTP Server - Adam Caudill

あるセキュリティ研究者によれば、AMIファームウェアのソースコードと秘密鍵が公開FTP経由で流出したそうだ。

ソースコードのコメントを読むと、少なくとも2012年に変更されたらしき後があり、かなり最近のソースコードである。

また、秘密鍵は、ファームウェアのアップデートを行う際のバイナリの認証鍵であるという。

これは、ファームウェアの書き換えによる攻撃を可能にする。

mjg59 | Leaked UEFI signing keys

Red HatのMatthew Garrettによれば、まあ、それほど心配することはないだろうとのことだ。

ファームウェアを書き換えれば、セキュアブートを無効化したり、あるいは攻撃者の公開鍵をひそかに付け加えたりできる。

しかし、いくらソースコードと秘密鍵が流出したとはいえ、ファームウェアはマザーボードごとにバイナリが異なるので、特定の個人を狙った攻撃ぐらいでしか役に立たないだろうとのことだ。

それに、ファームウェアの書き換えを許した時点で、相当に何でもできる状況になっている気もする。

Matthew Garrettは2004年に、secring.gpgという文字列を含むURLのページが、かなり高い確率で秘密鍵を含み、Googleで検索すればうっかり秘密鍵を公開してしまっているWebサイトがぞろぞろ出てくることを警告している。多くは公開FTPによるものだった。

An important lesson

それがもう2004年のことで、当時から状況が何も変わっていないことに驚いている。

更新:

秘密鍵はマザボベンダーで置き換えるようになっていたので、まともなマザボベンダーの製品なら、問題にならないそうだ。ただし、無頓着なマザボベンダーの存在が考えられる。

2013-04-05

x64について簡易的にまとめたIntelの記事

Introduction to x64 Assembly | Intel® Developer Zone

先月、Intelはx64について簡易的にまとめた記事を公開した。ただし、アセンブリプログラミングは不自由なWindowsにおける例を使う場合が多いようだ。

Ubuntu 13.04はLinuxカーネル 3.8、Python 2はまだ維持

RaringRingtail/TechnicalOverview - Ubuntu Wiki

Ubuntu 13.04(Raring Ringtail)は、Linuxカーネル 3.8.5をベースとしたカーネルを使う。

Pythonは、いずれPython 3のみをデフォルトでインストールするが、まだPython 2のデフォルトでのインストールを維持するらしい。Python 3に完全移行できるのはいつになるのだろうか。

Rackspaceが特許ゴロを訴えた次第

Why Rackspace Is Suing The Most Notorious Patent Troll In America - The Official Rackspace Blog

ホスティング会社のRackspaceが特許ゴロを訴えた次第が発表されている。なかなか興味深い。RackspaceとIPナビゲーショングループの最初の因縁を抄訳すると、

実際にはちょっと複雑だ。このゴロツキに因縁を付けられたのは2010年の12月にまでさかのぼる。現在パラレルアイロン(Parallel Iron)として知られているIPナビゲーショングループ(IP Navigation Group、以下IPナビ)は、秘密の特許保有者の代理人として、Rackspaceを特許侵害だと避難した。IPナビの言うには、侵害の詳細は、特許番号や特許保有者までひっくるめて一切明かすことはできないという。我々が「放棄同意」を締結するまでは。要するに、この同意は我々は相手を訴えないというものだ。IPナビは、我々が特許とそのクレームの内容を知れば、特許無効や非抵触を訴えることを心配していたのだ。我々にはそんな一方的な同意は受け入れがたい。

まるでヤクザのやり方だ。たとえばこんな。

おめぇさん、ヤキが回ったな。ある筋の特許侵害しているってぇのを小耳に挟んでね。いーや、どの特許を侵害してるなんてこたァ言えるわきゃぁねぇ。ある筋ってのも秘密だぜ。とにかくオレっちが侵害してるっていやぁしてんだ。早いとこ示談しねぇと後々大変なことになるぜ旦那。おっといや、勘違いしちゃぁいけねぇや。俺たちゃ何も敵じゃねぇ、むしろ味方だオトモダチ。今なら示談ですむよう取り計らってやるから。さぁさ、まずは契約をしねぇ契約を。ガタガタ抜かすと出るとこ出てもらおうか、ええ、旦那。

libc++のGNU/Linuxでのビルドが少し簡単になっている。

"libc++" C++ Standard Library

前回libc++のビルドに挑戦したときは、ドキュメントがなくビルドスクリプトもMac OS X用のmakefileが転がってるだけでさっぱりだったが、どうやら今は、少し状況が改善されているようだ。とりあえず簡易的ではあるがドキュメントが書かれ、さらにCMakeでビルドできるようになった。

clang側でも、-stdlib=libc++を指定すれば、gccのlibstdc++のかわりにLLVMのlibc++を使うようになる。

インストールせずにヘッダーとshared libraryのパスを指定して使うには、gccと同じく、-Iと-Lオプションでパスを指定すればいいようだ。

2013-04-04

LLVMのNightlyパッケージがDebianとUbuntu用に用意された

LLVM Project Blog: LLVM Debian/Ubuntu nightly packages

LLVMのDebian/Ubuntu用のNightlyパッケージのレポジトリが用意されたらしい。

DebianはWheezyとUnstable向けに、UbuntuはPrecise, Quantel, Raring向けに用意されている。アーキテクチャはamd64とi386だ。

いままでclangだけ自前でビルドして使ってきたのだが、これでだいぶ便利になる。

残念ながら、libc++やlibc++abiは含まれていないようだ。

GNU/LinuxのC++11でプログラミングの常識がひっくりかえった

C++11のコア言語を詳細に解説する本を書いている。ライブラリについては本書の範疇ではないし、どちらかと言えばコア言語の方が好きなのだが、ライブラリの解説だってやってやれないことはない。せっかくなのでC++11の範囲で非同期処理について書くことにしよう。

例えばあるファイルを全部メモリ上に読み込むような処理を考えよう。ファイルの読み込みは時間がかかる。残念ながら、まだmmapのようなmemory mapped fileは標準ライブラリにないし(あったとしてもこの問題には不適だし)、非同期I/Oもない。そのため、ファイルの読み込みは別のスレッドで行い、結果を元のスレッドに返したい。

これは非常に難しい問題である。別のスレッドを作ってその終了を待たなければならないどころか、その別のスレッドから何とかしてデータのやり取りをしなければならないのだ。それだけではなく、ファイルを読み込むという処理も書かなければならない。

幸い、C++11にはstd::asyncがある。これを使えば、なんとスレッドを作るとかデータの受け渡しをするとかいう誰でも書けるがゆえに難しい処理と、ファイルを読み込むという重要な処理を完全に分離できるのだ。

まず、ファイルを読み込む部分だけを関数として書こう。スレッドとかデータの受け渡しとか同期などは、一切考えなくてよい。重要なのは、別スレッドに渡したいデータは関数の戻り値で返すということだ。

// FILE *をfcloseするためのデリーター
struct FILE_ptr_deleter
{
    using pointer = FILE * ;
    void operator ()( pointer ptr ) const
    {
        std::fclose( ptr ) ;
    }
} ;

// ファイルの読み込み
std::vector<char> file_reader( std::string file_name  )
{
    // そもそもPOSIXってbを無視するんだよね。
    // いまどきbが意味を持つ古代の環境なんて一つしかないよね。どれとはあえていわないけど。
    std::unique_ptr<FILE, FILE_ptr_deleter> stream( std::fopen( file_name.c_str(), "rb" ) ) ;

    // ファイルが開けたかどうか確認
    if ( stream.get() == nullptr )
    { // エラー報告はもちろん例外を投げる
        throw std::runtime_error("fopen failed.") ;
    }

    // ファイルサイズを取得
    // いい加減C++にもまともなファイルシステムライブラリがほしい。
    std::fseek( stream.get(), 0L, SEEK_END ) ;
    long size = std::ftell( stream.get() ) ;
    std::rewind( stream.get() ) ;

    // あとは読むだけ
    std::vector<char> buf( size ) ;
    std::fread( buf.data(), size, 1, stream.get() ) ;

    return buf ;
}

まあ、こんな感じだろう。関数file_readerはファイル名を引数で受け取って読み込み、結果を戻り値として返す。この関数に、スレッドや、スレッドを超えたデータの受け渡しのためのコードは一切ない。そんな一般的な処理などわざわざユーザーが手を下すまでもないからだ。ファイルが開けない場合のエラー報告は、例外で行う。例外を使わない理由はない。

さて、この関数を別スレッドで実行して、さらに結果を安全に別のスレッドに返すのがstd::asyncの仕事だ。std::asyncの使い方は非常に簡単。

int main()
{
    try
    {
        // 別スレッドで読み込み開始
        auto result = std::async( std::launch::async, file_reader, std::string("example.txt") ) ;

        // その他の必要な処理

        // さて、ブロックされてもいいので結果を待とうか
        auto buf = result.get() ;

        // せっかくだから出力してみようか。
        for ( char c : buf )
        {
            std::cout << c ;
        }      


    } catch ( std::exception & exception )
    { // エラーはもちろん例外で受け取る。
        std::cout << exception.what() << std::endl ;
    }

}

このように非常に簡単に別スレッドをたちあげて結果を受け取ることができる。

std::asyncは、関数オブジェクトと、その関数オブジェクトに渡す実引数を受け取り、std::futureを戻り値として返す。この場合の戻り値の型は、std::future< std::vector<char> >である。と言っても、馬鹿正直に型名を書くのは面倒なのでauto指定子を使う。ポリシーとしてstd::launch::asyncを渡しているので、すぐさま別スレッドで実行が開始される。

さて、いよいよ結果が欲しくなった。ここでは、ブロックしてもかまわないので、結果が得られるまで待つfutureのメンバー関数getを使う。このgetを呼び出すことにより、別スレッドで実行される関数が終了して値を返すまで待ち、結果を別スレッドでも読めるように返してくれる。std::vectorを返しているが、C++11にはムーブがあるので、パフォーマンス上の問題はない。

さて、関数file_readerは別スレッドで実行されるが、エラー報告にはなんと例外を使っている。これは当然である。std::asyncは例外をスレッド間を超えて、正しくpropagateしてくれるのだ。例外のスレッドを超えたpropagateはC++11で新しく追加された機能であり、std::current_exceptionでstd::exception_ptrを取得して、それを別スレッドでstd::rethrow_exceptionを使って再びthrowすることにより、スレッド間を超えた例外のpropageteを実現できる。

最後に、蛇足的だが結果を標準出力に出力している。ここで使っているのはrange-based forだ。

このように、std::asyncを使えば、本当に重要な処理だけに注力することができる。スレッドを作るとかスレッド間の同期とかデータの受け渡しとか例外のpropageteとかは、わざわざ書くまでもないのだ。

残念ながら、C++11のasyncやfutureには極めて基本的な機能しかない。しかし安心して欲しい。次期C++に向けて現在提案中のN3558が採用された暁には、とても便利になる予定だ。

うん、GNU/Linuxは何の関係もなかった。ついでに、C++11ではすでに常識なので、とくにひっくりかえる常識もなかった。

追記:ファイルの読み込みをfstreamで実装してみたが、どうもあまり代わり映えしない。


std::vector<char> file_reader( std::string file_name )
{
    std::ifstream stream( file_name, std::ios::in | std::ios::binary ) ;
    if ( !stream )
    {
        
        throw std::runtime_error("failed to open a file.") ;
    }

    stream.seekg( 0, std::ios::end ) ;
    auto pos = stream.tellg() ;
    stream.seekg( 0, std::ios::beg ) ;

    std::vector<char> buf( pos ) ;
    stream.read( buf.data(), pos ) ;
    
    return buf ;
}

もう少しまともなI/Oライブラリが欲しい。

2013-04-03

Joel on Software: 特許みかじめ

The Patent Protection Racket - Joel on Software

Joel on SoftwareのJoel Spolskyが、特許ゴロを現代版のヤクザのみかじめに比している。

この低成長経済の中でも急速に発展しつつある米国の業界とは、特許ゴロみかじめ業界だろう。ソフトウェア特許に関する訴訟は、1999年に比べて3倍になった

とてもオイシイ商売だ。

一、ソフトウェア特許を買う。特許は何百万件とあり、どれも曖昧で理解不可能である。

二、注意深く細工された書面を、小規模ソフトウェア会社、iPhoneアプリ開発者、ネット業者に送る。ここが一番むずかしい。書面の受取り主には、その書面がカネを払わなければ訴訟を起こす脅しに見えなければならないが、一方法廷では、すばらしい新技術のライセンス契約のお誘いに見えなければならない。つまり、一方にのみ脅迫にみえる脅迫でなければならない。

三、数千もの小規模なソフトウェア会社が弁護士と相談している間、気長に待つ。彼らはいずれ、詐欺師にカネを払ったほうがマシだと気がつくだろう。なぜならば、法廷で争いをはじめるだけでも何百万ドルもかかるからだ。

四、儲け儲け

どうもこの手に見覚えがないか? そうだ。これは教科書通りのみかじめ料だ。これは組織的犯罪である、至極明白だ。これは法の悪用であり、特許の悪用であり、邪道である。

組織的犯罪にあたって、文明的な人間は決して支払わない。もし支払ったならば、それは犯罪者に資金提供をしたということであり、次の攻撃の手助けをしたことになる。まあ、気持ちは分かる。お前さんはアプリ内購入機能をもった小規模なiPhoneアプリを書いただけで、何も争いなんてしたくないんだろう。しかし、詐欺師にカネを払うということは、彼らに資金を与え、さらにまた別の開発者を脅すための種にしてしまう。お前は単に「都合よく」振舞っただけではない。お前もダークサイドに落ちるのだ。すまんね。人生は時に情け容赦ないが、時として、立って望まぬ戦いで争わなければならない時もあるのだ。

文明的な人間は決して支払わない。彼らは団結して、戦い、問題を取り除く。EFFは特許を改正するための運動を始めた。我々が支援するStack ExchangeのAsk Patentsで、いくつかのゴミ特許は未然に防げるだろう。

Application Developers Alliance(筆者は現在この団体の議長である)は、何度もDeveloper Patent Summitを世界の15都市ツアーで開き、特許ゴロに対して結束して戦うための土壌を形成している。住んでいる場所に来たのならば、会議に参加して--筆者は4月9日のサンフランシスコ会議にいく--何ができるかを学べ。

2013-04-02

現在のアセンブリの利用例

LEG/Engineering/OPTIM/Assembly - Linaro Wiki

Linux用のソフトウェアをARMアーキテクチャでサポートする目的で設立された非営利団体、Liaro.orgが、ソフトウェアパッケージのARM移植の調査のために、UbuntuとFedoraのレポジトリの全パッケージに検索をかけて、アセンブリの利用例を抽出した結果がまとめられている。

アセンブリの利用は、アセンブリのソースコードによくある拡張子と、その他のソースコードのインラインアセンブリを探すことで抽出された。さらに、抽出されたアセンブリ利用例に対して手動でその利用目的を調べてまとめている。

それによれば、アセンブリが使われているパッケージは1435件あったそうだ。

Ubuntuのパッケージだけで考えると、2万以上のソースパッケージの中の1200超のパッケージでアセンブリが使われており、利用率は約6%となる。

また、1435件のアセンブリを利用するパッケージについて、その利用目的を調べたところ、以下のように分類できた。もちろん、一つのパッケージで多目的に利用しているものもあるので、合計は100%以上になる。

  1. アトミック(10%) - メモリバリア、ロック、アトミックインクリメントやデクリメントなどの目的でアセンブリを使用。
  2. 埋め込みライブラリ由来(18.1%) - パッケージ自体に取り込んだライブラリ由来のアセンブリ(e.g. libjpegやgettext)
  3. 誤検出(11.1%) - スキャンでアセンブリだと誤って検出されたもの(.sで終わるファイル名)や、何らかの理由によりコメントアウトされたりなどして使われていないコード
  4. 低級(lowlevel)(38.1%) - 様々な低級(lowlevel)な目的のためのアセンブリコード、例えばレジスターやスタックやハードウェアポートなど、直接ハードウェアを操作するもの
  5. 他のOS用(9.3%) -  ホストであるFedoraやUbuntu以外の他のプラットフォームやOSをサポートするためのアセンブリで、移植作業には関係ないもの
  6. パフォーマンス(30.4%) - 何らかの方法でパフォーマンスの向上をはかる(実際に達成できているかどうかはともかく)とするアセンブリコード(e.g. マルチメディアのためのSIMDやループ内のコードのアセンブリによる置き換えなど)
  7. シンボルやセクション等(2.9%) - シンボルアクセスやELFセクションを直接いじるための"asm"の利用。

一部のパッケージは、ARMへの移植作業としては無視できるという。たとえば、アーキテクチャ独自のもの(ブートローダー)とか、一部のハードウェアを操作するもの(ディスプレイやグラフィックハードウェアの設定を行うもの)だ。

また、アセンブリの典型的な使われ方に着目したまとめもある。

バイトスワップやビット操作

多くのアセンブリが、単純なバイトスワップやビット操作を高速化するためにアセンブリを利用している。これは、今のコンパイラーならばたやすく最適化できるものである。このような目的にアセンブリが使われているのは、プログラマーの不理解や、当時の貧弱なコンパイラーへの対処のためである。もはやこの手のアセンブリ利用は必要なく、また時には、優秀なコンパイラーより遅くなることすらありえる。

ハードウェアの機能検出

例えばCPUIDなど、ハードウェアやそのサポートされている機能の検出目的でアセンブリが使われている。多くのソフトウェアパッケージが、独自にアセンブリを書いてハードウェアの検出を行なっている。これは恥ずべき現状である。このような処理には共通のライブラリが存在するべきだし、実際Linuxカーネルに検出機能があり、glibcを経由して提供されてもいるが、どういったわけか(移植性のためか?)、多くのソフトウェアパッケージは、共通のライブラリを使わずに自前実装している。

タイマーアクセス

RDTSCやHPETのような高精度のハードウェアタイマーにアクセスするためにアセンブリを使っている。主な利用はベンチマークだ。これもやはり自前でアセンブリを書くより、共通のライブラリにまかせるべき処理ではないかと思う。実際、POSIXにもハードウェアタイマーをラッパしたライブラリが提供されているのだが。

本の虫: 100ナノ秒ぐらいの分解能をもつクロック実装

SIMD

マルチメディア処理はパフォーマンスのためにSIMD演算を多用する。元記事では、compiler intrinsicを使うべきだとしているが、やはりcompiler intrinsicでカバーできるのは一部だけであり、動画屋は手動でカリカリに最適化したアセンブリを好むようだ。まあ、これはSIMDアーキテクチャとコンパイラー技術の改良が次第に状況を変えていくのではないかと思う。

atomic

多くのソフトウェアパッケージが、atomic操作のために自前でアセンブリを書いている。たとえ低級なatomic操作であっても、アセンブリを手書きするのではなく、compiler intrinsicやライブラリを使うべきである。

浮動小数点数の操作

驚くほど多くのパッケージが、浮動小数点数ユニットの演算精度や丸めモードなどを設定するために、x86アセンブリを直接手書きしている。これはx86以外のソフトウェアにはあまりみられない傾向だ。これこそライブラリで行うべき処理だ。C99にはFPUの挙動を設定するためのライブラリが追加されたが、どうやらC99にはなかなか移行できないようだ。

埋め込みライブラリ由来

ライブラリをライブラリとして参照するのではなく、ライブラリのソースコードを直接取り込むことによって、そのライブラリ由来のアセンブリコードまで取り込んだソフトウェアパッケージがかなりある。ライブラリのソースコードを取り込む理由は、

  1. パッケージをビルドしやすくする。
  2. ライブラリの特定のバージョンの依存したコード書いたり、ライブラリを改造して使う

といったところだ。一つ目の理由はそれほど重要ではない。大多数のユーザーは、ディストリビューターによってビルドされてパッケージ化されたものを使うからだ。ディストリビューターは、ライブラリの正しいビルド方法や依存関係の解決ができるだけのスキルを持っている。二つ目はそう簡単に解決できない。

ただし、ライブラリのコピーを内部で持つのは、様々な理由からおすすめできない。手動で本家のアップデートを追わなければならないのが一番の理由か。

もっとも頻繁に埋め込まれているライブラリもまとめている。

  • gnulib - これは誤検出ともいえる。本来ソースコードレベルでコピーして埋め込む設計のライブラリだから、設計通りと言える。その設計が正しいのかどうかはともかく。
  • gettext - これも基本的には誤検出だ。asmを使ってcygwin環境でシンボルのエクスポートを操作している
  • libgc - the Boehm garbage collection library
  • zlib - compression library
  • libjpeg - support for the JPEG graphics format

多くの例で、libgcはただ埋め込まれているだけではなく、目的に応じて改変されており、本家の最新版に追いついていくのを難しくしている。zlibはstableなABI/API互換を提供しているが、セキュリティホールの発見でたびたびアップデートされている。埋め込まれたzlibは正しくアップデートされにくい。

元記事では、最近の良い傾向として、多くの開発者がcompiler intrinsicなどの機能の存在を知っており、そのような機能を使おうとしていることを挙げている。これは移植を簡単にする。

悪い傾向としては、多くのパッケージで、本人が書いたのではなく、どこか他所からコピペしてきたらしきアセンブリのコードが散見されることだ。また、ライブラリの埋め込み利用も多数ある。これは移植を難しくする。

また、多くのアセンブリには、例えば以下のようなコメントが付属している。

「gcc 2.7のこのコードの最適化には誤りがある」

何十年も昔のコードならば許せもするが、適切な保守作業が行われていない証拠でもある。

10件のパッケージに、VAX用のアセンブリコードが発見された。いくつかは、どうやら当初VAXアセンブリで書かれて、その後、超最先端(new-fangled)のSunのワークステーションのためにCで移植されたらしい。1980年代のことだ。

多くのソフトウェアに、使われないまま眠っているアセンブリコードがある。書かれたものの正しく動かなかったのか、もはや必要がなくなったのか、いろいろな憶測が立つ。典型的な例は、あるIRCクライアントに含まれる、手で書かれてカリカリにチューニングされているが、全部コメントアウトされている、文字列処理用のアセンブリのサブルーチンだ。

その上で、ARMに移植するべきパッケージを優先度付けしている。

元記事はこの調査の上で、我々はアセンブリの悪用の例をドキュメント化し、まとめて、正しく教育する必要があるとしている。共通のライブラリがある場合はその存在を教育する。また、もっとコンパイラーを信頼するように教育しなければならない。近年のコンパイラーの技術の向上により、従来の多くのアセンブリ利用は、もはや不要になっているのだ。また、ソフトウェアの直接の開発者にも連絡して、不適切なアセンブリ利用をやめるよう説得するべきだ。

結論として、ほとんどのパッケージで、ARMのための移植作業は必要ない。Ubuntuのレポジトリで言えば、アセンブリを利用しているパッケージは、全体のたったの6%だからだ。アセンブリを利用している多くのパッケージも、そのまま動く。移植性の高い高級なコードでの実装とパフォーマンス目的のアセンブリ実装を切り替えるようになっていたりするからだ。また、ほとんどのアセンブリ利用は、それほど価値がない。本来アセンブリで書かなくてもいいような処理までアセンブリで書いていたりするからだ。

is_explicitly_convertibleが消された理由

Fixing is_constructible and is_explicitly_convertible

2010年のドラフトにあったis_explicitly_convertibleは、is_constructibleと完全に等しいということで、消された。

つまり、static_cast<T>(e) ;が可能かどうかと、T t(e) ;が可能かどうかは、全く同じだという事だ。

Implicitly converted vs contextually converted to bool

これをちょっと考えてみたのだが、Concept Liteで問題になりはしないだろうか。例えば以下のようなコード。

template < typename T >
void f( T & e )
{
    if ( e ) ; 
}

これをConcept Liteで以下のように書くと誤りである。

template < typename T >
requires std::is_convertible< T, bool >::value
void f( T & e )
{
    if ( e ) ;
}

以下のように書いても誤りである。


template < typename T >
constexpr bool is_implicitly_convertible_to_bool()
{
    return requires ( T e )
    {
        bool = { e } ;
    } ;
}

template < typename T >
requires is_implicitly_convertible_to_bool<T>()
void f( T & e )
{
    if ( e ) ;
}

これはどちらも、Tをboolへimplicitly convertibleかどうかを調べているためである。

このため、explicit operator bool()をメンバーに持つ型(e.g. std::unique_ptr)を、誤って弾いてしまう。

ifのような文脈では、contextually convertible to boolとも言うべき用語がでてくる。これは、宣言、bool t(e);が成り立つ式eのことである。

つまり、以下のように書くのが正しい。

template < typename T >
requires std::is_constructible< bool, T >::value
void f( T & e )
{
    if ( e ) ;
}

あるいは以下のように書く。

template < typename T >
constexpr bool is_contextually_convertible_to_bool()
{
    return requires ( T e )
    {
        { bool(e) } ;
    } ;
}

template < typename T >
requires is_contextually_convertible_to_bool<T>()
void f( T & e )
{
    if ( e ) ;
}

たかがifやforやwhileのごとき基本的なコア言語機能を使うテンプレートのConceptチェックに、implicitly convertedとcontextually converted to boolの違いを完璧に理解していなければならないのだろうか。

とはいえ、特にいい解決方法も思いつかないので、Concept利用者は当然、この程度のことは理解しているべきだと思う。しかし、これは分かっていても間違えそうだ。

ただし、explicit operator bool()を使うという事は、もともと結構面倒でもある。たとえば、

struct X
{
    explicit operator bool() ;
} ;

void f()
{
    X x ;
    bool good1(x) ; // OK
    bool bad = x ; // エラー
    bool good2 = bool(x) ; // OK

    if( x ) ; // OK
    if( x == true ) ; // エラー
    if( bool(x) == true ) ; // OK
}

こういうことになってしまうのだから。