2010-07-31

決して呼ばれることのない変換関数

自分自身の型、基本クラスの型、void型、またはこれらの型のCV修飾子とリファレンス型への変換関数は、書くことができる。ただし、変換の際に呼ばれることはない。

struct Base { } ;

struct Derived : Base
{
    operator Base() ;
    operator Derived() ;
    operator void() ;
} ;

規格には、仮想変換関数を通して、呼ばれる可能性があると書かれているが、どういうコードを書けばそんな可能性が生まれるのだろう。試しにこんなコードを書いてみたが、

struct Base1 {  } ;

struct Base2
{
    virtual operator Base1 &()
    {
        static Base1 ret ;
        std::cout << "Base2::operator Base1 * is called" << std::endl ;
        return ret ;
    }
} ;

struct Derived : Base1, Base2
{
    virtual operator Derived &()
    {
        std::cout << "Derived::operator Derived & is called" << std::endl ;
        return *this ;        
    }
} ;

void f( Base2 & b2 )
{
    Base1 & b1 = b2 ;
}

int main()
{
    Derived d ;
    f(d) ;
}

うまくいかないようだ。

Pixivが小説機能を始めた

[pixiv] お知らせ

Pixivが小説機能を始めるようだ。

よく、Pixivを真に楽しむには、ROMではなく、自分で絵を描くべきだという意見を聞く。確かに、MixiのようなSocial Networking Serviceや、TwitterのようなMicro bloggingは、ROMに徹するだけでは、参加しているといえない。同じように、絵の投稿サービスを提供しているPixivも、参加していると言えるためには、自分で絵を描かなければならない。それは、一理ある。しかし問題は、絵を描くというのが、かなり個人差の激しい能力であることだ。

例えば私のような絵心のない人間が絵を書いても、棒人間より他に描くことができない。いや、それどころか、棒人間ですら、難しいのだ。例えば、xkcd: Froggerというサイトがある。これは棒人間のcartoonを掲載しているサイトであるが、この人の描く棒人間は、相当うまい。これは、私には描くことができない。

しかし、小説の投稿サイトなどというものが流行るであろうか。絵は、サムネイルを見ただけで、その巧拙がある程度は判断できる。しかし、文章には、サムネイルなどというものは生成できない。しかも、その文章の巧拙は、実際に読んでみなければ、判断できない。果たして、Pixivのような、多数のユーザーが作品を投稿するようなサイトで、小説などという機能が流行るだろうか。

一体どんな小説を想定しているのか。まず、これはPixivであるから、小説に絵をつけることができる。というより、絵に小説をつけると言ったほうが適切だろうか。

Pixiv内で、作品を閲覧するのには、主にランキング上位をみていくか、検索を行う。どちらも、多数の作品一覧から、興味をひく作品を探すという形になる。長文小説は、あまり好まれないだろう。もちろん、小説の面白さは、文章の長さで決まるわけではないから、これをもって批判するのはあたらない。また、サムネイルでは何も確認できないほどの長さの漫画が、巨大な一枚の画像として投稿されることもある。とすれば、必ずしも短くなければならないということもないだろう。

指定子の順番に意味はない

以下はすべて、まったく問題のないC++のコードである。

int typedef type ;
int f() { return 0 ; }
int extern f() ;

struct C
{
    int friend f() ;
    void virtual g() {}
} typedef * pointer ;

なぜなら、指定子には順番という概念がないからである。

ただし、指定子と宣言子の違いに注意しなければならない。

int typedef type ;

は問題ないが、

int * typedef type ;

これはエラーである。なぜならば、「*」は、指定子(specifier)ではなく、宣言子(declarator)だからである。

指定子と宣言子の区別は、通常のユーザーは知らなくてもいいことなので、普通の参考書は、typedef指定子を先頭に書いているのだろう。

これは、今、C++本の指定子について書いていて、thread_localとstaticの順番が関係ないので、そのことを解説しておこうと思って、気がついた。

void f()
{
    thread_local static int x ;
    static thread_local int y ;
}

そもそも、一般に、順番に意味のある指定子というものが見つからなかったので、わざわざthread_localとstaticについてだけ書くまでもないだろう。

宣言文を飛び越えること

gotoやswitchを使えば、変数の宣言文を飛び越えることが可能になる。

int main()
{
    goto label ;
    int x ; // 宣言文
label : ;
}

このコードは問題のないコードである。しかし、このようなコードを書いてはいけない。なぜなら、「問題のないコード」の定義が、非常に限定的だからである。いま執筆中の本では、この問題について、以下のように記述した。

まず、変数の型は、スカラー型か、trivialなデフォルトコンストラクターとtrivialなデストラクターを持つクラス型でなければならない。また、そのような型にCV修飾子を加えた型と、配列型でもよい。その上で、初期化子が存在していてはならない。

struct POD { } ;
// trivialではないコンストラクターを持つクラス
struct Object { Object() {} } ;

int main()
{
    // 変数の宣言文を飛び越えるgoto文
    goto label ;

    // エラー
    // 変数の型はスカラー型だが、初期化子がある。
    int value = 0 ;

    int scalar ; // OK

    // エラー
    // 変数のクラス型がtrivialではないコンストラクターを持っている
    Object object ;

    POD pod ; // OK

label : ;
}

2010-07-28

ひとつの宣言文で複数の変数を宣言するなかれ

C++では、ひとつの宣言文で、複数の変数を宣言できる。(宣言子は、厳密には、変数だけではないのだが)

int a, b, c, d ;

これは分かりやすい。同じ型の変数が複数欲しい場合は、重宝する。しかし、この文法は根本的に邪悪である。複数の変数を宣言してはならない。Bjarne Stroustrupも、D&Eで、「もし俺がCを土台としていなかったら、ひとつの宣言文で複数の変数を宣言できるようにはしていなかった」と書いている。

何故だめなのか。それには、例を示す方が分かりやすい。

int * a, b, c[5], (*d)(void) ;

このコードを全く躊躇せず、何も参照せずに、脳内で理解できるのならば、ひとつの宣言文で複数の変数を宣言してもよい。

oldnewthing: ウソッキーなフロッピー

Hardware backward compatibility: The finicky floppy drive - The Old New Thing - Site Home - MSDN Blogs

これは、ウソッキー(原語:Finicky、Floppyと音が似ている)というより、気難しいといったほうがいいのかもしれないが、まあ、ウソッキーの方が語呂がいい。

Windows 95の時代、僕はフロッピーディスクドライバーも担当している、ある人と話をしたところ、さる有名なマザーボードチップセットに必要な、ドライバーのハックを知った。

このチップセットに内蔵されているフロッピーディスクコントローラーは、何と言うか、会話の好みが激しいとでもいおうか。電源投入後、最初に受けるコマンドが読み込みの命令で、しかもディスクが挿入されていない場合、コントローラーチップは、完全にハングする。チップにリセットをかけても、全く応じない。完全に逝ってしまうのだ。コンピューターの電源を入れ直すしかなくなってしまう。

記事はここで終わっている。

2010-07-27

やはりクラスのattribute-specifierは最初の理解で正しかった

当初、私はクラスの宣言は、先頭にattribute-specifierを書けないものと考えていた。しかし、その後思い直して、以下のふたつのattribute-specifierは、クラスに属する(appertain)と考えていた。

class [[]] C {} ; // #1
[[]] class C {} ; // #2

しかし、これは違うのだ。#1はCに属するが、#2は違う。では何に属するか。

[[]] class C {} c ;

このattribute-specifierは、Cではなく、cに属する。クラスの定義というのは、結局、specifierに過ぎぬのだということを、すっかり忘れていた。危ないところだった。危うく参考書に嘘を書くところだった。ともかく、これでattribute-specifierは、ほぼ理解したはずだ。parametorへも指定できるか、これは普通のはずだ。

David Vandevoordeには、感謝している。

ちなみに、この変更についての理由は、Core issue 962に書いてある。

2010-07-20

attributeの書ける場所が間違っていた

この記事は間違っている。クラス宣言の先頭にattribute-specifierを指定しても、クラス定義には属さない。なぜならば、クラス定義は、class-specifierであるので、class C {} c ;のような記述ができる。したがって、クラスの場合は、class-keyとidentifierの間に書かなければならない。

以下、過去に書いた間違っている文章。

クラスの場合、以下の二行は、同じ意味である。

class [[ final ]] name ;
[[ final ]] class name ;

よく考えたら、class-specifierも、type-specifierに含まれるので、それはつまり、クラスの宣言と定義は、simple-declarationであるという単純なことを忘れていた。

さて、attributeを教える際には、attributeを指定する場所を、どのように教えるべきだろうか。初心者には、先頭に書く方が、分かりやすいと思う。しかし、私のように言語仕様的に考える人間にとっては、、変数や関数の場合は、識別子の後に書くのが自然であるし、クラスは、class-keyと識別子の間に書くのが自然だ。さてどうするか。

Class member name checking attributes

Class member name checking attributesというattributeがある。これは、override、hiding、base_checkからなるattributeである。これらは、ありがちなミスを防ぐために用意されている機能である。

overideの説明。例えば、以下のようなコードを考えてみる。

class Base
{
public :
    virtual void do_something() {}
} ;

class Derived : public Base
{
public :
    virtual void do_something() {}
} ;

ある名前の派生クラスの仮想関数に対して、同名の基本クラスの仮想関数がある時、派生クラスの仮想関数名は、基本クラスの仮想関数名をオーバーライドしていると言う。この場合では、Derived::do_somethingは、Base::do_somethingを、オーバーライドしている。では、Derivedが以下のように書かれていた場合はどうか。

class Derived : public Base
{
public :
    // typo
    virtual void do_somethig() {}
} ;

これをみると、Derivedの仮想関数が、do_somethigとなっている。これはtypoである。このコードを書いたプログラマの意図は、派生クラスのDerivedで、基本クラスの仮想関数、Base::do_somethingをオーバーライドしたいというものである。しかし、コンパイラーは、そんな人間側の都合など、理解できるはずがない。そこでこのコードは、全く問題なくコンパイルが通ってしまう。このバグは、大抵の場合、プログラムを実際に実行するまで発見出来ないのである。

あるいは、引数を間違えてしまうという場合もある。

class Base
{
public :
    virtual void do_something(int, int, int) {}
} ;

class Derived : public Base
{
public :
    virtual void do_something(int, int, short) {}
} ;

この場合も、やはりオーバーライドはされない。

このような場合、コンパイルエラーになって欲しい。ここで必要なのは、「このメンバー名は、必ずオーバーライドする。オーバーライドしない場合、それは誤りだ」という意味を表現できる文法である。overrideアトリビュートは、まさにその機能を提供する。Derivedの仮想関数は、以下のように書けばよい。

class Derived : public Base
{
public :
    // コンパイルエラーとなる
    virtual void do_somethig [[override]] () {}
    // 以下のように書くこともできる
    // 上と同じ意味となる。どちらを使っても良い。
    // [[override]] virtual void do_somethig  () {}
} ;

overrideアトリビュートの指定されたメンバーが、実際には、何もオーバーライドしていない場合、エラーとなる。これにより、プログラマの単純なミスを防ぐことができる。

hidingの説明。以下のような例を考える。

struct Base
{
    int value ;
} ;

struct Derived : Base
{
    float value ;
} ;

ある名前の派生クラスのメンバーに対して、同名の基本クラスのメンバーがある場合、派生クラスのメンバー名が、基本クラスのメンバー名を、隠す(hiding)と言う。

もし、Derivedが以下のように書かれていた場合、

struct Derived : Base
{
    // typo
    float valeu ;
} ;

この場合、Derived::valeuは、Base::valueを隠さない。しかし、これは問題なくコンパイルが通ってしまう。このバグは、大抵の場合、実行時に発見される。ここでも、コードの中で、このメンバー名は、基本クラスのメンバーを隠すのだという意味を、明示的に記述できれば、コンパイルエラーをだせる。hiding アトリビュートは、そのためにある。

struct Derived : Base
{
    // typo
    float valeu [[hiding]] ;
    // 以下のように書くこともできる
    // [[hiding]] float valeu ;
} ;

hidingアトリビュートが指定されたメンバー名が、同名の基本クラスのメンバーを隠していない場合、エラーとなる。

ところで、この「隠す」という概念には、注意が必要である。たとえば、

struct Base
{
    void f(int) {}
} ;

struct Derived : Base
{
    void f(double) {}
} ;

int main()
{
    Derived d ;
    d.f(0) ; // Derived::fが呼ばれる
}

この場合、Derived::fは、Base::fを隠してしまう。メンバーは隠されているので、オーバーロード解決の際に、考慮されない。従って、Derived::fには、hidingが使える。

struct Derived : Base
{
    void f(double) {}
} ;

しかし、C++では、using宣言で、基本クラスのメンバー名を引っ張り出すことができる。

struct Derived : Base
{
    using Base::f ;
    // エラー、Derived::fは何も隠してない
    void f [[hiding]] (double) {}
} ;

では、同名のusing宣言があれば、メンバー名は何も隠していないと言えるだろうか。実は、そうとも限らない。なぜなら、C++には、多重継承というものがある。

struct Base1
{
    void f(int) {}
} ;

struct Base2
{
    void f(short) {}
} ;

struct Derived : Base1, Base2
{
    using Base1::f ;
    void f [[hiding]] (double) {}
} ;

この場合、Derived::fは、Base1::fは隠していないものの、依然として、Base2::fは隠している。従って、hidingを指定しても、エラーにはならない。

隠しているかどうかというのは、using宣言にも左右されるので、注意しなければならない。

base_checkの説明。overrideとhidingは、つまらない間違いを、コンパイル時に未然に防ぐという、極めて便利な機能を提供している。しかし、実は名前を隠す意図がないのに、うっかりと隠してしまっていた場合は、検出できない。

// 同僚の大阪人によって書かれた基本クラス
struct Base
{
    // 何かユニークな名前が必要や。これならどやろ。
    // まさかこんな珍妙な名前を使う奴はおらんやろ
    int ケツの穴から手ぇ突っ込んで奥歯ガタガタいわしたろか ;
} ;

// 別の大阪人によって書かれた派生クラス
struct Derived : Base
{
    // Baseがどんな実装になってるかは知らんが、とりあえず衝突しない名前が必要や。
    // これならええやろ。まさかワシと同じ考えの奴はおるまい。
    int ケツの穴から手ぇ突っ込んで奥歯ガタガタいわしたろか ;
} ;

この例では、たまたま二人の大阪人のセンスが同じであったため、ユニークにしようと思ってつけた長い名前が、衝突してしまっている。これは、明示的な指定では、解決できない。

そこで、base_checkというアトリビュートが用意されている。これは、ある派生クラスに指定することで、overrideとhidingをしているメンバー名に対しては、明示的にアトリビュートを書かなければ、エラーとなる機能を持つ。

struct Base
{
    virtual void f(int) {}
    void g(int) {}
} ;

struct [[base_check]] Derived : Base
{
    // OK、overrideがある。
    virtual void f [[override]] (float) {}
    // エラー、overrideが必要なのに、指定されていない
    virtual void f (double) {}

    // OK、hidingがある
    void g [[hiding]] (short) {}
    // エラー、hidingが必要なのに、指定されていない
    viud g(double) {}

    // OK、overrideもhidingも必要ない
    void I_am_the_one() {}
} ;

注意:N3092のbase_checkのコード例は、アトリビュートの指定位置が間違っている。

これはすごい歴史の話

odokemono comments on Anyone know why /var and /etc weren't named something like /etc and /cfg?

このodokemonoという人のコメントが面白かった。特に二つの話が面白かったので、翻訳してみる。

これから話す話は、知識というよりも経験というべきだろう。私は、エンジニアではないし、特別にそういった訓練を受けていたわけでもない。私はベテランのシステム管理者だ。私はいくつかの記事を書き、またシステム管理者の教育もしている。私は80年代前半から、ほとんどUnix一本でやってきた。

Unixのすばらしいところは、25年前に書いた私のコードが、いまだに修正なしでコンパイルでき、しかも動くということだ。

それでは、「すばらしい話」のいくつかをしようと思う。

私は昔、顧客である大手の弁護士事務所の元に出向き、週末の間に、サーバーの大幅なアップグレード作業をしていた。私は相当大きな(60MBぐらいだったと思う)ディスクを、古いサーバーから取り外して、新しいサーバーに取り付けて、データをさらに容量の大きな新しいディスクにコピーしていた。ディスクというのは、5.25インチのMFMディスクであった。新しいサーバーが用意され、セットアップされた。私は古いディスクをつなぎ、コピーを始めた。コピーは、恐ろしく遅かった。10KBのファイルひとつコピーするのに、4,5分はかかった。私はすぐに、ddでセクターをひとつづつ読み書きするスクリプトを書き、経過を表示させた。27セクター読むごとに、読み込みが数分間ハングしていた。

そこで、私は事務所のシステム管理者の自宅に電話をかけ、バックアップがあるかどうかと、サーバーがおかしくなっていなかったかどうかを訊ねた。彼はバックアップを取っていなかった。また、サーバーの状態については、「そういや、この一週間というもの、極端に遅くなっていてね。だからアップグレードするってわけさ」と。私は電話を切った。

訳注:つまり、古いディスクは壊れているため、27セクター読むごとに数分ハングするのであった。

計算すると、このままの速度では、コピーには10日間かかる。顧客は月曜日の朝までにサーバーが用意されていることを期待している。これはまずい。

ヘッドのステッパーのモーターシャフトが、ディスクの横から突き出していた。私はシャフトに優しく指を当て、読み込み中に延々とシークばかりしている状態を感じ取れないものかどうか、試してみた。実際、その通りになっていた。しかしここで、私はあることに気がついた。私がシャフトにすこしだけ力を加えた場合、無駄なシークが止まったのだ。しかも、コンソールは正しいセクター番号を、ハングせずに表示しているではないか。

その後二十時間、私はシャフトに指を当て続けた。データの転送に成功し、顧客は満足した。そして私は、その後二日間、タイプできなくなった。

これも昔の話。私は顧客のもとに出向き、サーバーの交換とアップグレード作業をした。コンピューターは、有名なキャッシュレジスター会社によって作られた、NCRタワーであった。これは高さ約1.2メートル、幅1.8メートル、奥行き20センチメートルであり、136キログラムもの重量があった。アップグレードは事無く終り、その帰り際に、このすばらしい顧客が、ふとつぶやいた。「この古い機械は、一体どうすればいいんだろうねぇ」と。私は答えた「さあ」と。顧客は言った。「欲しいかい?」

その後しばらくの間、私はNCRタワー450(4MBのRAMに120MBのディスク、16本のシリアルポート、二つのシリアルターミナル)を、自宅で使うことになった。私は"Hello World!"プログラムを、たったの1.5分でコンパイルでき、冬でも部屋を暖房する必要がなくなった。このシステムは、当時としては、二万二千ドルもする代物であった。まだPC以前の時代にもかかわらず、私は自宅にUnixサーバーを持っていたのだ。私はこの上もなく幸せであった。ただし、部屋は相当狭くなってしまったが。

この他にも、まだまだ面白い話はあるのだが、まあ、このへんにしておこう。

2010-07-19

attributeの分かりにくい指定方法

attributeの指定方法はわかりにくい。attributeを書く事のできる場所というのは、attribute-specifierである。例えば、いまxという名前のint型の変数を宣言、定義する。

int x ;

これに対して、attribute-specifierは、二箇所存在する。

[[]] int x [[]] ;

前者は、simple-declarationのattribute-specifierであり、後者は、noptr-declaratorのattribute-specifierである。

今、xという変数に対して、アライメントを指定したいとする。一体、どちらのattribute-specifierを使えばいいのか。

答え:どちらでもよい。

まず、アライメントを指定するattributeであるalignは、変数に適用しなければならない。つまり、通常は、noptr-declaratorの方に指定する。

int x [[ align(16) ]] ;

では、simple-declarationの方のattribute-specifierは何か。ここに指定したattributeは、宣言の各entityに属すると定義されている。すなわち、

[[ align(16) ]] int x, y, z ;

という宣言は、

int x [[ align(16) ]], y [[ align(16) ]], z [[ align(16) ]] ;

と指定したものと、同じ意味になる。

と、ここまではいい。もっと複雑な例もある。const int 型の変数の宣言には、みっつのattribute-specifierを指定できる。

[[]] const [[/*中央*/]] int x [[]] = 0 ;

中央のattribute-specifierは、type-specifierに属する。これは、型に対してのattributeであって、entityに対するattributeではない。従って、変数というentityに指定するalignは、この中央に位置するattribute-specifierに指定することはできない。

おそらく、一般のユーザーからみると、alignというattributeは、宣言の最初に指定するか、変数名に指定するものだと見るだろう。実際のところ、実用上は、それでいいように思われる。

ところで、変数名に指定するというのが、また分かりにくいのだ。例えば、配列に対してalignを指定するのは、よくある話だと思う。では、その場合、どのように書けばよいのか。

int x [[align(16)]] [256] ;
[[align(16)]] int x [256] ;

上の二行は、完全に同じ意味である。どちらの文法を好むかは、人それぞれだと思う。私は、前者の方が明示的だと思うが、後者の方が、初心者には分かりやすいかもしれない。

実は、関数の定義も、二箇所指定できる。

void f [[noreturn]] () { while(true) ; }
[[noreturn]] void f() { while(true) ; }

これも、宣言の場合と同等で、先頭のattribute-specifierは、関数に適用したものとみなされる。

上記は、どちらも同じ意味である。これも、どちらを好むかは人それぞれだ。私は前者の方が、明示的で分かりやすいと思うのだが、後者のほうが、初心者受けはいいかもしれない。

ところで、複数書けるところでは、重複することが許されている。noreturnは、ひとつのattribute-specifierの中に、一回だけしか指定できないと定義されている。しかし、それはあくまでひとつattribute-specifierの話である。複数のattribute-specifierでは、それぞれにnoreturnを指定できる。

[[noreturn]] void f [[noreturn]] () { while(true) ; }

この、複数指定しても良いというのは、実は宣言も同じである。

残念ながらクラスには、初心者にも分かりやすいであろう指定方法がない。今、クラスの継承を禁止したいとする。

class [[ final ]] Do_not_derive_me { } ;

これはすこし、初心者には分かりにくいと思うが、残念ながら、こう書くより他に方法はない。

2010-07-16

今日爆笑した2chのレス

【話題】客のクレームに口答えしてはいけない? インターネット上で大激論「ごね得ニッポン、正直者がバカをみる国ニッポン」

85 名前:名無しさん@十周年[] 投稿日:2010/07/16(金) 16:43:25 ID:Pzlti40U0
どんな客も
胸のピースメーカーを見せると同情してくれるのかおとなしくなる

324 名前:名無しさん@十周年[] 投稿日:2010/07/16(金) 17:22:28 ID:mQOL1h5K0
>>85

ピースメーカーってコルトの?
そりゃ大人しくなるわ。

参考:ピースメーカーペースメーカー

新しいプログラミング雑誌の発行

[Grimoire]「プログラミングの魔導書」予約開始! - Faith and Brave - C++で遊ぼう
株式会社ロングゲート - 製品案内

新しいプログラミング雑誌が、とうとう発行された。PDF版は、いますぐ予約購入して、8月7日以降に読むことが出来る。物理的に印刷された雑誌は、予約販売という形をとっている。これは、7月末まで予約を受け付け、その後に印刷して、発送するという仕組みだ。印刷は、予約期間を逃すと、もう手に入れることはできないが、PDF版は、いつでも購入可能になっている。

このブログで何度か言及してきたが、私は、Bjarne Stroustrupへのインタビューをして、その翻訳と、C++の歴史について、二本の記事を書いた。いうまでもなくBjarne Stroustrupは、C++プログラマでその名前を知らなければモグリであるし、非常に興味深い内容となっている。全C++プログラマは、このインタビューを読むべきだ。C++の歴史は、D&E以降の歴史に注力している。exportにまつわる歴史や、STLの歴史などだ。また、EC++の末路についても、少し書いている。

Bjarne Stroustrupのインタビューは、私の翻訳を載せているが、もちろん本人とのやり取りは、英語で行われた。翻訳では、微妙なニュアンスが削ぎ落とされているという懸念があるだろう。なにを隠そう、私も翻訳は大嫌いだ。英語版は、Bjarne Stroustrup本人のサイトで、近々公開される見込みとなっている。

今回は、C++に集中した内容となったが、次回以降は、C++以外のトピックも取りあげる予定である。

さて、宣伝ばかりするのも何なので、批判もする。PDFだ。言うまでもなく、私はPDFが大嫌いだ。PDFというものは、印刷を主眼において設計されたフォーマットである。そんなものをぽんとダウンロード可能にして、「さあここに電子書籍があるぞ」などというのは、馬鹿げているにもほどがある。

実際に、この雑誌のPDFを読めば分かるが、横幅の実に三分の一が余白である。余白というのは、印刷上の必要性のために設けられているのである。私は、ディスプレイを最大限に有効活用して文字を読みたい。余白などは必要ない。フォントは固定。フォントサイズもあまり自由にはならない。なぜならば、ページという概念があって、拡大すると、ページのレイアウトごと拡大されてしまうからだ。ページという概念は、実に印刷の都合である。

もちろん、一行あたりの文字数があまりに長いのは読みにくいという人もいるだろう。それは当然だ。一行あたりの文字数をいくつにするかということは、読者側が設定できるべきだ。PDFで押し付けることではない。たとえば、あるWebサイトが横に長すぎると思えば、ブラウザのウインドウのサイズを変更するだろう。テキストエディターでも同じようにして、word wrapを適用するだろう。同様に、電子書籍もそうなってほしい。PDFは、これができない。また、フォントは読者の好きに設定できるべきである。PDFには、これができない。故に、PDFは地獄で我々読者を苦しめるために設計されたクソフォーマットである。世間一般には、この地獄をAdobeと呼んでいる。

電子書籍は、このブログのように、ブラウザーのウインドウのサイズに応じてスケールするようなフォーマットでなければならない。そして、幸いにも現在、これらの要求を満たすフォーマットで、しかもWindowsやLinux、Mac OS、ないしは携帯電話ですら閲覧できる、大変ポータブルなフォーマットがある。HTMLである。

私は、電子書籍というものは、XHTMLとCSSとSVG、それに加えて、画像音声動画などのコンテンツで提供されるべきだと信じている。そうすれば、読者はCSSを自分の好きに設定することで、余白だろうとフォントだろうと、好きに設定できる。とくに、この雑誌はプログラマー向けであるから、読者はCSSを知っている可能性が高い。たとえ知らなくても、ちょっと調べただけですぐに理解できる人種である。なおさら好都合だ。Webサイトというのは、すでにこれ電子書籍である。なんでわざわざ別のフォーマットを使う必要があるというのか。

ましてや、この雑誌のコンテンツは、ほとんどテキストである。漫画などは、デザインも問題になるが、ことプログラミングに関しては、文章とソースコードが重要で、それ以外は特に重要ではない。私は、プログラミングにおける図というのは、物事を分かった気にさせてくれるだけのごまかしのテクニックだと考えている。

私は再三このことを進言したにも関わらず、ロングゲート社は今回、PDFを使った。そして、「とりあえず最初に発行してみて、その後読者の反応をみてから」などという悠長なことを言っている。

そもそも、今時紙への印刷などが必要だろうか。マイコンBASICマガジンやC MAGAZINEは何故滅んだのか。もはや時代が紙の印刷を必要としなくなったからではないのか。特に、この種類の雑誌の読者は、プログラマーである。電子書籍への抵抗が最も少ない人種である。なんで印刷を提供する必要があるのか。

ベーマガやCマガが滅んだのは、時代の流れである。彼ら、出版を専門としているものですら、時代の流れには勝てなかったのに、なんでいまさら、出版や印刷にはド素人の我々が成功できるというのか。我々は紙の印刷への依存をやめなければならない。

もちろん、コレクターズアイテムとして、物理的なトークンが欲しいという人間もいる。それはありがたいことだ。ではなおさら、紙の印刷は限定的で良い。例えば、今回PDF版は千円で、書籍版は千五百円だ。実際、我々はまだ弱小であり、予想される販売部数は少ない。そのため、電子書籍に千円という価格設定は、致し方ないところがある。しかし、書籍版をこんなに安くする必要はない。コレクターズアイテムならば、もっと高くても人は買うはずである。例えば、書籍版を三千円とか五千円にして、それでも書籍が売れるならば、電子書籍は、もっと値段を下げられる。この限定版商法は、ゲーム業界がよく使う手である。

この限定版商法に、読者からの不満はないはずである。というのも、限定版が欲しい人にとっては、当然高くても限定版が提供されるべきであるし、その他多数の一般読者は、安価で便利な電子書籍を欲しているだろう。俗に言うWin-Winである。

明らかに、ロングゲートは時勢が分かっていない。電子書籍一辺倒こそ未来である。いまさら紙の出版という分野では、ベーマガやCマガには絶対に勝てないのに、どうしてベーマガやCマガですら失敗した土台で、新参者が勝てるというのか。

追記:PDF版はすぐに読むことが出来るのかと思ったら、紙の印刷の発送にあわせて、8月7日以降にダウンロード出来る仕組みになっているらしい。唖然とした。ロングゲート社は何を考えているんだ。なんで紙の印刷の都合に電子書籍を合わせなければならないのだ。遅れに遅れてこの体たらく。電子書籍は、紙の印刷から切り離さなければ、未来はない。

追記2: PDF版がまだ落とせない理由について。印刷を除けば、基本的に、もう雑誌は完成しているのだが、何人かに査読用のコピーを回すので、その時間を稼ぐため、8月7日ということらしい。

2010-07-15

Dvorakレイアウトについて

あのはまちちゃんがDvorakについて書いていたので、Drovakを覚えてみた。

何もない日にDvorak - ぼくはまちちゃん!(Hatena)

Drovakのレイアウトを覚えるために、Typex: typing exerciseを使った。これは、良く考えて作られた練習問題だと思う。

今、この文章も、Dvorakで書いているが、まだまだタイプが遅い。タイピングを覚えるのには、結構時間がかかるということを思い出した。

2010-07-14

Dark_Shikari、VP8について語る

Diary Of An x264 Developer » VP8: a retrospective

x264の開発者であり、ffmpegのVP8デコーダーの開発者でもあるDark_Shikariが、VP8の技術上の利点欠点について述べている。

僕はこの数週間、ffmpegのVP8デコーダーの開発を手伝ってた。On2のVP8動画フォーマットの、初の第三者による実装というわけだ。僕は数千行のアセンブリコードを書いて、さらに一部のCコードの最適化もした。そこで、VP8についていくつか意見を吐いたところ、それがネット上に広まっている。当時は正しかったが、今となっては変更されたこともある。

画質の比較という問題がある。まあこれに関しては、MSUの比較で、x264の圧勝ということが示されたわけだが、VP8の開発者はようやく、ピノキオの鼻を伸ばすのをやめることにしたらしい。まあ、x264が完膚無きにまで優れているのは当然のことなので、どうでもいいことだ。その点については、これ以上言及しない。技術的に一番でなければ役に立たないということはない。

あとちょっとのアセンブリの関数を書けば、ffmpegのVP8デコーダーが完成する。完成した暁には、libvpx(Googleに買収されたOn2自身による実装)との比較がしたいものだ。

1. 規格書、もとい、バイナリ列の説明書

Googleはようやく、規格書(spec)と呼ぶのをやめて、バイナリ列の説明書(bitstream guide)という言葉を使い始めた。これはどういうことか。自称規格書が公開されてからというもの、多くの批判がでた。そもそもが不完全な定義だらけなのだが、特にひどいのが、規格と公式のエンコーダー、デコーダーの実装が、異なっている箇所が、少なくとも数十ヶ所はあるからだ。The deblocking filter, motion vector clamping, probability tablesなどなど、多くの箇所が、規格と異なっている。幸いにして、ffmpegのVP8デコーダーの開発者の一人であるRonald Bultjeは、リバースエンジニアリングに長けているので、我々はとりあえず、実装の方を合わせることができたわけだ。

多くの差異は、それほど重要というわけではない。とくに、画質に影響を与えるものでもない。しかし、「動くVP8デコーダー」を実装するのが困難であるし、そもそも、「動く」ということに関する定義すら怪しいものになってしまう。例えば、Googleのデコーダーは、「ALTとGOLDENのリファレンスフレームを交換せよ」と命じられたならば、両方のフレームをGOLDENで上書きする。なぜならば、まず最初にGOLDEN = ALTと設定し、さらにALT = GOLDENと設定するからだ。これはバグなのか? それとも、こう動くべきなのか? 規格では何も言及していないため、よく分からん。Googleによれば、libvpxの動作はすべて正しいとしている。しかし、これは疑問だ。

僕は、規格が書かれることを望んでいる。Googleは自前のドキュメントすら揃わぬうちに早期リリースするものだから、こういう問題が生じることになる。外部の人間もGoogle社の開発者も皆、その被害をこうむることになるのだ。

訳注:以下、2.から5.まで、規格ではなく、VP8の動画圧縮の技術上の利点と欠点に対するコメントが続く。2, 4は、利点について、3, 5は欠点について述べている。

6. インターレースをサポートしない

キタコレwwwwwwwこれで勝つる!wwwwwwwwwwww

インターレースはH.264の暗黒面である。インターレースは規格のあらゆる部分で首をもたげてくる。結果として、デコーダーの実装は何千行も長くなってしまうのだ。H.264はさらにものすごく複雑で、効果的な、インターレース専用のコード方法を提供している。MBAFFだ。MBAFFの存在は、いまだにアナログ時代に住人である1080i、576i、480iとかのコンテンツを提供する奴らには利点があるが、すでに過去の遺物だ。

VP8はよくぞクソ地獄から逃れた。

もしどこかのアホが、インターレースのサポートなんぞをVP8のブランチに入れようとしたならば、現実世界に被害を及ぼさないうちに、拘束具をかませて精神病棟に送り込むべきである。

2010-07-13

今まで知らなかったswitch文の仕様

switch文に指定する文は、ブロックでなくてもよい。

int main()
{
    // OK
    switch(0) ;
    // OK
    switch(0)
        default : std::cout << "hello" << std::endl ;
}

もちろん、ラベル文がたったのひとつしか書けないので、実用上の意味はない。ブロックでなくてもいいとは知らなかった。てっきり、ブロックを指定するものだと思っていた。知らなかった。まあ、知っていても、全く役に立たない知識ではある。

追記:コメントを素直に実装するとこうなる

void f( int const value )
{
    int i = 0 ;
    switch( value )
        for ( ; i != 10 ; ++i )
        {
            case 1 : case 2 :
                std::cout << "value is 1 or 2" << std::endl ;
                continue ;

            default : break ;
        }
}

このコードは完璧にwell-formedなコードなので、当然だが、MSVCでもGCCでもコンパイルが通り、しかも意図通りに動作する。恐ろしい。そもそも、ループ文の外から、ループの内側のラベルに飛ぶというのは、邪悪すぎる。

x264の商用ライセンス

[x264-devel] Announcing commercial licensing for x264

x264はGPLライセンスの元に公開されているが、これは商用目的には使いづらい。というのも、GPLなコードを利用したプログラムは、GPLライセンスに感染してしまうからだ。どうやら、x264は、商用ライセンスを提供し始めるようだ。

商用ライセンスでは何が違うか。このライセンスでは、x264を自社製品に組み込んで好きなように使える。もちろん、ソースコードも提供されていて、変更することも可能だ。ただし、x264のソースコードを変更した場合、x264に対して、変更点を公開しなければならない。もしその変更が有意義なものであれば、公式にコミットされる可能性もある。つまり、全体の利益になるわけだ。

GPLの場合、製品そのもののコードも公開しなければならない。x264に対する変更点だけを公開すればいいというのは、商用利用の可能性を開くだろう。

どうやらこのライセンスの目的は、x264を商用目的で使えるようにし、なおかつ、非公開のforkを避けるということにあるらしい。というのも、非公開のforkは、外部の利益にはならないのだから、オープンソースの理念に反するというわけだ。

MPEG-LAの特許使用料の問題については、各自で解決しなければならない。これは、MPEG-LAの理念に基づくものである。今、甲という会社が、乙という会社の開発したH.264エンコーダーを使うとする。この場合、MPEG-LAの特許使用料は、甲にかかるということを、ほかならぬMPEG-LA自身が決めているからだ。そういうわけで、ソフトウェア特許の認められている国では、MPEG-LAに特許料を支払わなければならない。

因みに料金は、x264を利用する製品の1パッケージを1ユニットとして、1ユニットあたり1ドルである。ライセンスする最小ユニット数は、一万ユニットである。

商用ライセンスはLGPL互換であり、LGPLな製品であっても、問題なく使うことができる。

タイプライターを使用した音楽

YouTube - The Typewriter Leroy Anderson Martin Breinschmid with Strauß Festival Orchestra Vienna

世の中には、本物の大砲を使う音楽もあるが、まさかタイプライターとは。しかも、なかなか悪くない音楽だ。

やはり定数式は詳しく説明すべきだ

昨日一日、constant expressionとconstexpr specifierを眺めていたが、やはり、constant expressionは、詳しく説明する必要がある。なかなかまとめにくい概念なのだが。

2010-07-10

罵倒語に思うこと

「池沼キャラ」「原作レイプ」という言葉 - 萌え理論Blog

暴力性を内包した言葉を日常的に用いるのは、ネット外の常識からしておかしい。ネットでなければ、軽々しくそういう言葉を使わないはずだ。本当は注意が必要だと思う。

つーか逆だ逆。日本語には罵倒語にあたる言葉が足んねぇーんだよ。これは自然言語としてめっちゃ損だぜ。つーかお前、常識的に考えておかしいだろ。俺らは英語のfuckにあたる言葉ひとつ、満足に表現できねーんだぜ。ありえねーだろ。

俺が思うにはだな。まだ日本語は、口語と文語がありえねーほどかけ離れてんだよ。まともに批判しようとするなら、まずくだらない人名につける敬称問題から始まって、ですます調とである調の混同(お前、現実に口語で混同しない奴がいるか?)、正しい敬語(まず正しい敬語を定義せよ 10点)、方言の排除、等々、もう訳わからん。

ついでに、大和言葉じゃなくて、漢語を使わないと幼稚だと批判される。大和言葉で何が悪いんだよ。論点ずらしにもほどがあるだろボケ。左傳を丸暗記しろってのか今時。本居宣長ぐらい読め。

それもこれも、日本語は罵倒語が未発達だから起こるんだよ。

まあ、まだ救いはある。俺の見立てでは、最近敬語がだいぶ簡素化してきてる。最近の敬語は、みな、「~です」という形ですまそうとしている。これは、言語がより簡潔になるという点で、いいことだ。将来的には、敬語とか罵倒語などというくだらない境目などなくすべきだ。そうすりゃ、もっと簡単に会話ができるようになるし、バカバカしい論点ずらしの反論もなくなるってもんだ。

さて、現代仮名遣いにすら、未だに残っている表記と発音の不一致だが、こいつはどうすりゃいいのかね。表記を変えるには、相当強い強制力が必要だ。革命か戦争でも起こらない限り、この問題は解決できんな。

式最後の難関、定数式

定数式(Constant Expression)だ。これを真面目に説明すべきかどうかというのが、問題だ。今まで、定数式について真面目に説明している参考書をみたことがない。かくいう私も、定数式の具体的な定義については暗記しているわけではないし、そもそも、真面目に定数式の定義を読んだことがない。

そして、定数式の標準の定義は、ブラックリスト方式なのだ。この式はダメ、あの式はダメ。この式はこの場合ダメ。などという文面が延々と続いている。

一方で、constexprという新機能が導入された以上、定数式をいい加減に済ますこともできない。どうすべきか

narrowing conversionの適訳が思いつかない

narrowing conversionには、どうも対応する日本語の直訳がない。どうしたものか。

ナローじゃダメだろうか。ナローバンドという言葉は、一般に定着したように思われる。としたら、ナロー変換でもいいのではなかろうか。

Youtubeが4096pを公式にサポート

YouTube adds support for 4096p (not a typo!)

Youtubeが4096p(4096×3072)の解像度をサポートしたそうだ。とはいっても、Youtubeのビットレートとエンコードでは、あまり楽しめそうにないが。

そもそも、4096pもの解像度をネイティブに再生できる環境を持ったユーザーはどれほどいるのか。まず、30インチのディスプレイが四枚必要だ。グラフィックカードも数枚必要だろう。そして、あまりデコード速度と描画速度の速くないFlashを動かすための、高速なCPUとメモリ。

まあとにかく、二十年ぐらい前は、フルカラーで巨大な液晶パネルなど夢物語だと思われていたのだから、将来的には、あるいは普通になるのかもしれない。いい加減、現在のPPIでは、ディスプレイの大きさ的に無理がある。将来どんなにディスプレイが安価になろうとも、やはり使うのは人間なので、目の前に100インチのディスプレイを置きたいとは思わないだろう。将来、高PPIが普通になってくれるといいのだが。

少なくとも、3D(笑)とか言っているうちは駄目だ。

2010-07-09

現代の娯楽作品が理解出来ない

どうも二十歳を超えてからこの数年、現代の芸術が理解出来ない。芸術の定義は、なかなか人によって違い、現代の作品を芸術品とは認めない人種もいる。むしろ娯楽作品というべきだろうか。名称にはそれほど意味はないが、とにかく、理解出来ない。

例えば漫画だ。もう私には、最近の漫画の面白さが理解出来ない。無論、幼くなければ理解出来ない芸術も存在する。たとえば、のむらしんぼの「つるピカハゲ丸」や「かげきベイビー バーブー赤ちん」のような作品は、もう理解出来ない。しかしジャンプやマガジンも理解出来ないのは何故か。ジャンプやマガジンには、「週間少年」という枕詞がある。対象読者は少年であり、一般に、二十歳を過ぎた者を少年とは呼ばない。では、少年とは銘打たれていない漫画はどうか。これも、やはり理解できない。これはどうしたことか。

ではゲームはどうか。これも、まったく理解出来ない。描画性能はすばらしいほどに向上した。しかし、ゲームとしての面白さは、むしろ退化している。それに、どのゲームを見ても、過去の作品の続編ばかり。無論、過去のゲームで遊んだことのない現代の子供にとっては新鮮かもしれないが、すでに作品を知っている私にとっては、手抜きとしか思われない。グラフィックはすばらしい。だがそれだけだ。タッチパネルやWiiリモコンの類にしたって、大昔からあった。あの頃から比べれば、確かに品質は向上したが、格別に面白くなったとは思われない。

現代小説には、もはや読むべきものがない。今私が気分転換に読んでいるのは、今昔物語である。これなどは九百年ぐらい昔の作品だ。何故こんなに昔に娯楽を求めなければならないのか。

テレビ、音楽の類は、もとより子供の頃から理解ができなかったので、特に書く事もない。

ひょっとしたら、現代にまともな娯楽作品がないのではなく、単に私が現代の娯楽作品を理解出来ないだけなのかもしれない。そうだとすると、もはや私が時勢についていけないことを意味する。恐ろしいことだ。

2010-07-08

不等号と等号

よく考えたら、不等号演算子などという言葉はおかしい。号自体が記号を意味するのだから。しかし、関係演算子とか、等価演算子などと訳した場合と比べて、果たしてどちらの方が分かりやすいのか。

不等号といえば、<を「小なり」、>を「大なり」と呼ぶのは、一体どういういわれがあるのだろう。どうも私の言語感覚からすると、よく分からないのだが。たとえば、<は、英語では、less thanという。1 < 2 は、1 less than 2となるのは、言語感覚的にも分かりやすい。ところが、小なりとは一体。

追記、比較演算子の方がいいだろうか。しかし、==や!=も比較だ。

2010-07-07

ポインターの絡む演算は鬼門だな

ポインター同士を比較するというのも、意外と定義が難しい。

今一番悩んでいるのは、定数式をどうするかということだ。定数式の詳細な説明は、あまりに長すぎて、誰も読まないだろうし、ましてや現実のプログラミングで、ここまで詳細を気にして定数式を書く必要はない。

とはいえ、constexprという機能もあり、やはり悩むところだ。

今昔物語、震旦

今昔物語、震旦の部を読み終わった。読み終わって思うことは、実に宗教色が薄いことである。今昔物語では、各話の最後に取ってつけたように、神霊仏教の功徳無量なりなどと、説教めいたものを書いている。

例えば、欧州では、二千年前の大工の息子を、やたらに神格化した。イスラムは、千四百年前の商人が洞窟で受けたと称する啓示を信じた。ところが、震旦では、二千五百年前の羊飼いで諸国流浪のヒッピーだった孔子を、あまり呪術的な宗教とは結び付けなかった。もちろん、かなり後に生まれた朱子学はカルトめいた馬鹿げた学問であるが、やはりどうも、宗教とは言いがたい。これは考えて見れば不思議なことだ。

また興味深いのは、孔子の孔の読みが、呉音「ク」であることだ。まあ、個人的には、今昔物語は浅学の坊主の集団によって書かれたと思っているので、呉音は特に不思議でもないのだが。

孔子(クジ)(タフ )レ」という言葉は、今昔物語 巻十 孔子、為教盗跖行其家怖返語 第十五に書いてある。この諺は、いまはまったく聞きなれないが、弘法も筆の誤りとか、河童の川流れと似たような意味であろうと言われている。

ともかく、今昔物語によれば、孔子が盗跖に弁舌で負けて、ガタガタ打ち震えながら、尻尾を巻いて逃げ帰るときに、馬の轡を二度取り外し、鐙をしきりに踏み外した逸話から、「孔子倒れ」というらしい。

なお、宇治拾遺物語 巻十五ノ十二にも、盗跖と孔子の問答事と題して、まったく同じ逸話を挙げ、またこれも同じように、孔子倒れの語源としている。この話の直接の出典は、荘子らしい。荘子は、孔丘に相去ること百余歳、何ぞ孔子と盗跖の相論ずるを知らんや。と、三宝絵詞の為憲も言っている。

興味深い点では、今昔物語では、孔子をクジ、盗跖をトウシャクと読んでいるのに対して、宇治拾遺物語では、孔子はコウシ、盗跖はトウセキと読んでいる。

この諺は、当時よほど人口に膾炙せられていたとみえて、源氏物語 胡蝶にも、「右大将の、いとまめやかに、ことことしきさましたる人の、恋の山には孔子の倒ふれまねびつべきけしきに愁へたるも」などと書かれている。

ハードの互換性:わずかにまともではないファームウェア

Hardware backward compatibility: The firmware that missed one tiny detail - The Old New Thing - Site Home - MSDN Blogs

Windows 95のFDDのドライバーの責任者は、CD-ROMドライブのドライバーをも担当していた。(注意:これはCDFSファイルシステムとは違う。CDFSファイルシステムは、ファイルシステムチームの担当であって、ハードウェアドライバーの受け持ちではない)。私は、ある不思議なCD-ROMドライブの話を覚えている。

このドライブは、さる有名ブランド会社によって製造されていた。ドライブのパッケージには、これが完璧なIDE ATAPIドライブであることを宣伝していた。彼らは、いい仕事をしていた。このドライブは、すべてのATAPIコマンドを、ある僅かな点を除いて、完璧に実装していた。

彼らは、「お前はATAPIドライブか?」と質問するコマンドの実装を忘れていたのであった。

コメント欄によると、レイモンドはこの問題を、その後Windows 95でどのように解決したかまでは、把握していないようだ。結局Windowsは、そのベンダーのドライブを認識して良きに計らうハックをしていたというコメントもある。ちなみに、なぜ会社はそんな製品を製造、出荷して問題がなかったのかというと、その会社のドライバー(おそらくMSDOS用の)を使うと、ATAPIドライブとして認識されるからであった。

ちなみに、LG製のCD-ROMドライブが、ファームウェアのバグのため、とあるLinuxディストリのインストールでぶっ壊れるという事件もあったそうだ。

Slashdot | LG CD-ROMs Destroyed by Mandrake 9.2

ポインターと整数との加減算

ポインターと整数との加減算の、規格上の定義が、だいぶ抽象的すぎる。このまま説明していいものだろうか。

注釈で、別の方法による定義もある。つまり、ポインターの値をchar *型に変換して、オブジェクトのバイト数分足し引きをするという定義方法だ。両方説明するのも手だろうか。ちなみに、この注釈はC++規格のもので、Cの規格は、抽象的なもののみとなっている。

ところで、乗算、加減算は、じょうざん、かげんざんと読むのが普通らしい。何故かIMEで変換できないと思っていたら、一般には濁るとは驚きだ。私は今まで、算は清音で発音するものだとばかり思っていた。もちろん、演算のような単語は、私も濁音であると考えていたが。

欲しいC++0x機能

現在、主要コンパイラは、C++0xの新機能を実装しだしている。まだ、不完全な実装が多いが、autoやdecltypeは、もうかなり使えるようになっているし、lambda expressionも、だいぶ実用的な実装が出ている。list initializationや、variadic templatesも、gccは実装を初めている。しかし、未だに主要コンパイラが実装していない機能で、是非とも使いたい機能がある。また、別に実装しなくてもいいのではと思う機能もある。まだ、主要なコンパイラが実装していない機能で、欲しい機能といらない機能を、メモがわりに書いておく。

まずは、MUST-HAVE機能から。

新しいtypedef記法である、alias-declaration。

// typedef int (*type[5])(int, int)と同じ
using type = int (*[5])(int, int)

コンストラクターを継承できる、inheriting constructors。

struct Base
{
    Base() ; Base(int) ; Base(int, int) ; Base(float) ; Base(std::string) ; Base(std::vector) ;
} ;

struct Derived : Base
{
    using Base::Base ;
} ;

派生クラスのコンストラクターでは特にすることがない場合でも、単に値を基本クラスに渡すためだけに、対応するコンストラクターをすべて書かなければならない。これは労力の無駄である。プログラマはえてして怠惰なものあるから、そのような状況に直面した場合、常にコピペを使う。間違いのもとである。コンストラクターを継承できる機能は当然だ。

コンストラクターをデリゲートできる機能。規格に公式な名前なし。

// 分数クラス
struct Fraction
{
    int num ; int denom ;
    // 分子、分母の引数を取るコンストラクター
    Fraction( int num, int denom ) : num(num), denom(denom) {/*実装*/}
    // 整数を取るコンストラクター
    Fraction( int value ) : Fraction( value, 1 ) {/*何もしなくてよい*/}
    // コピーコンストラクター
    Fraction( Fraction & right ) : Fraction( right.num, right.denom) {/*何もしなくてよい*/}
} ;

この分数クラスのコンストラクターは、引数を受けて、メンバー変数などを初期化するであろう。しかるに、クラスを使いやすくするため、単に整数を受け取り、分母を1とみなすコンストラクターを書きたい。このため、コンストラクターのデリゲートはできてしかるべきである。これだけならば、デフォルト引数でも済むが、コピーコンストラクターの場合もある。

わざわざ名前がないのは、これぐらいできて当然だからである。

いらない機能、user defined literals。

まとめて圧縮すると著者推定できる話

Twitter / kiku: 「同じ著者の小説をつなげてzip圧縮したら、複数の著 ...

「同じ著者の小説をつなげてzip圧縮したら、複数の著者の小説をつなげて圧縮するよりも圧縮率がいいから著者推定に使える!」って論文が見つかった。キワモノかと思ったら精度いいし。論文探してるとしばしば「その発想はなかったわ」な物が見つかって面白いが俺は数日前にこれをやっとくべきだ。

どうやらこの論文らしい。

CiNii Article - 圧縮プログラムを応用した著者推定

なんだか論文の数字は高精度すぎて怪しいが、本当だろうか。

2010-07-06

ラムダキャプチャーにはparameter packが使える

ラムダキャプチャーにはfunction parameter packが使える。もちろん、コピーキャプチャーも参照キャプチャーもできる。

template < typename ... Types > void g( Types ... args ) ;

template < typename ... Types >
void f( Types ... args )
{
    // 明示的なキャプチャー
    [args...]{ g( args... ) ; } ;
    [&args...]{ g( args... ) ; } ;

    // 暗黙的なキャプチャー
    [=]{ g( args... ) ; } ;
    [&]{ g( args... ) ; } ;   
}

こんなことは、わざわざ説明するまでもないだろうから、いま執筆中の本では書かないつもりであったが、どうも、念のために書いておいた方がいいかもしれない。

邪悪なC形式のキャストにしかできないこと

注意:邪悪で汚らわしいC形式のキャストは、いやしくもC++プログラマたる者は、使うべからず

C++では、玉虫色のC形式のキャストの機能を、三つに分割した。static_cast、reinterpret_cast、const_castである。しかし、この三種のキャストでは、C形式のキャストを完全に代替できないという声をよく聞く。曰く、「どうしても書けないキャストがある」と。

それはよく聞く話だが、では実際にどのようなキャストなのかということは、誰も審らかにしない。誰も知らないキャストであれば、特に使えなくても問題ないはずだ。ただし、「C形式のキャストならばできるキャストが、新しいキャストを組み合わせてもできない。どんなキャストかは知らないが、とにかくできないと聞いている。故に新しいキャストはクソだ」などという論調で、C++の改良されたキャストを使わぬC畑の外道がしゃしゃり出てくるのも困る。そこで、ここでは、C形式のキャストでしかできないキャストを、余す所無く紹介しようと思う。

その前に、static_castとreinterpret_castの違いを説明しなければならない。

reinterpret_castは、愚直なキャストである。reinterpret_castは、値を保ったまま、型情報だけ変えることのできるキャストである。だから、int *からfloat *とか、some_class *からother_class *などといった、お互いに派生関係になく、ユーザー定義の型変換関数もない型のポインターやリファレンスにも変換できる。これは、変換するというより、型だけ変えるというべきである。値はそのまま保持される。

一方、static_castは、必要ならば値を変える。これは重要である。

reinterpret_castにできず、static_castにできることは、D&Eの331ページにも書かれているように、クラス階層のナビゲーションである。派生クラスへのポインターと基本クラスへのポインター同士の相互変換の際、そのポインターの内部的な値が、変わる可能性がある。これはつまり、ストレージ上のクラスのオブジェクトの中で、そのクラスのサブオブジェクトの場所が違うということである。従って、値を変更しなければ、正しい場所を参照しない。リファレンスも内部的にはポインターなので、問題は同じである。

static_castは、ポインターの値を変更することによって、クラス階層のナビゲーションを行える。reinterpret_castは、値を変更しないので、それができない。

さて、C形式のキャストは、

  1. const_cast
  2. static_cast
  3. static_castとconst_cast
  4. reinterpret_cast
  5. reinterpret_castとconst_cast

このような順番と組み合わせで表現できる。これらのキャストを上から順番に試していき、キャストが行えるところで、そのキャストを行う。

ただし、C形式のキャストでは、static_castが、以下の三つの追加的なキャストを行うことができる。

1. 派生クラスへのポインターやリファレンスから、基本クラスへのポインターやリファレンスに変換できる。文字通り変換できる。アクセス指定などは考慮されない。

struct Base { } ;
struct Derived : private Base { } ;

int main()
{
    Derived d ;

    Base & ref1 = (Base &) d ; // OK
    Base & ref2 = static_cast<Base &>(d) ; // ill-formed
}

このキャストは、reinterpret_castでもできる。ただし、reinterpret_castは、クラス階層のナビゲーションを行わないので、正しく動かない。C形式のキャストは、クラス階層のナビゲーションを行うので、正しく動く。

とはいえ、private継承している基本クラスへのポインターやリファレンスに変換する時点で、設計が多いに間違っている。それなら最初からpublic継承すべきなのだ。

2. 派生クラスのメンバーへのポインターから、曖昧ではない非virtualな基本クラスのメンバーへのポインターに変換できる。文字通り変換できる。アクセス指定などは考慮されない。

struct Base { } ;
struct Derived : private Base { int x ; } ;

int main()
{
    int Base::* ptr1 = (int Base::*) &Derived::x ; // OK
    int Base::* ptr2 = static_cast(&Derived::x) ; // ill-formed
}

これも、アクセス指定を無視できる。reinterpret_castでは、クラス階層のナビゲーションが正しく行われない。ただし、前述の理由で、このキャストを使用したいというのは、設計が間違っている証拠である。最初からpublic派生すべきなのだ。

曖昧ではなく非virtualな基本クラスのポインターやリファレンスあるいはメンバーへのポインターは、派生クラスのポインターやリファレンスあるいはメンバーへのポインターに変換できる。文字通り変換できる。アクセス指定などは考慮されない。

struct Base { int x ; } ;
struct Derived : private Base { } ;

int main()
{
    Derived d ;

    d.x = 0 ; // ill-formed. アクセス指定のため

    int Derived::* ptr = (int Derived::*) &Base::x ; // well-formed.
    d.*ptr = 0 ; // well-formed. C形式のキャストを使ったため、アクセス指定を無視できている
}

これも、アクセス指定がらみだ。これは、実装依存だが、reinterpret_castで代替できるかもしれない。というのも、reinterpret_castの挙動のほとんどが、実装依存だから、保証はできないのだ。

これはの三種のキャストは要するに、クラスのアクセス指定を無視でき、しかもクラス階層のナビゲーションをするキャストである。このようなキャストが必要となるコードは、現実に存在しないはずだ。const_castは、忌まわしく薄汚いCとの相互利用のために、仕方のない部分がある。dynamic_castやtypeidも、必要悪な機能である。しかし、アクセス指定の無視というのは、話にならない。まず、アクセス指定はC++から追加された機能であるので、互換性の問題はない。privateやprotectedなメンバーにアクセスしたいとすれば、最初からpublicにしていればいいのだ。土台、設計が間違っている。

もしこれを読んでもまだ、「C形式のキャストはぁ~、新しいキャストではぁ~、できないキャストができるからぁ~」などと世迷言を垂れ流す者は、全プログラマの為に害悪である。二度とコードを書かないでもらいたいのはもちろんのこと、およそソフトウェア業界には一切関わらないでお貰い申したい。

注意:邪悪で汚らわしいC形式のキャストは、使ってはならない。

2010-07-05

仮引数かパラメーター

どうも、parameterを仮引数、argumentを実引数とするより、parameterをパラメーター、argumentを引数と書いた方が、実用上、分かりやすい気がする。特にテンプレート周り。

alignof式とnoexcept式

alignof式とnoexcept式について書けることが少なすぎる。まあ、そもそもが単純な演算子であるし、互換性の問題もないので、汚い仕様にならなかったというべきか。

しかし、alignofは、具体的な値を示すことができないので、なんとも抽象的なサンプルコードになってしまう。

2010-07-03

newと初期化失敗

new式の評価において、ストレージの確保が成功したが、初期化中に、コンストラクターから例外が投げられた場合、確保されたストレージは、対応するdeallocation functionで解放される・・・かもしれない(MAY)。

少なくとも、MSVCとGCCは、この挙動を実装していなかった。

再び試したところ、MSVCとGCCではplacement deleteが規格通りに呼び出された。何か間違えていたらしい。

HDBaseTが発表された

HDMIが地獄の悪魔によって我々消費者を苦しめるために設計されたのは、周知の事実である。ディスプレイ用の端子とケーブルの規格は、実に悲惨である。ところで、面白い規格が出てきた。既存のイーサネットを使おうという規格である。

Goodbye HDMI, Hello HDBaseT

これはすばらしい。もう、無駄に一本何千円もするケーブルを買う必要はない。既存のLANケーブルが使える。

ところで、私が思うに、ディスプレイ用のケーブルは、もっともっと広帯域になるべきである。というのも、私は、もっと高PPIでフレームレートの高いディスプレイが使いたいからだ。無論、まだ私の理想とするスペックを満たすパネルは開発されていないが、私の生きている間に実現されるのではないかという希望がある。

私の理想では、最低でも、600PPI、毎秒120フレームで、物理的なサイズは24インチぐらいのディスプレイを使いたい。これぐらいのスペックがあれば、印刷と変わらぬ細かさで字が読め、しかも動画もヌルヌル再生されるであろう。

こんなパネルは、2010年の時点では、笑い話に過ぎない。パネルの話はともかく、ケーブルの話をしたい。現在のディスプレイ用のケーブルでは、帯域が全然足りないのだ。

たとえば、私は今、24インチで96PPI、毎秒60フレームのディスプレイを使っている。これが600PPIになるとする。PPIは約6倍だ。すると、ピクセル数は、36倍になる。さらに、秒間フレームも倍になっているので、現状の72倍の帯域が必要になる。

今、私はDVI-D端子をシングルリンクで使っている。これは、1920x1200のフレームを、秒間60枚送れる帯域ということになっている。ということは単純に計算しても、私の理想を実現するためには、今使っているこのDVI-D端子のケーブルが、72本必要である。

DVIのデュアルリンクでは、帯域は約2倍になっているので、ケーブルは36本ですむ。HDMI 1.4では、34本ぐらい必要になる。DisplayPort 1.2でも、やはり16本は必要になる。

というわけで、私の理想を実現できるディスプレイ用のケーブルは、まだこの世に存在しない。

2010-07-02

無能な働き者は処刑するしかない

軍人は4つに分類される。

有能な怠け者。これは前線指揮官に向いている。
理由は主に二通りあり、一つは怠け者であるために部下の力を遺憾なく発揮させるため。そして、どうすれば自分が、さらには部隊が楽に勝利できるかを考えるためである。
有能な働き者。これは参謀に向いている。
理由は、勤勉であるために自ら考え、また実行しようとするので、部下を率いるよりは参謀として司令官を補佐する方がよいからである。また、あらゆる下準備を施すためでもある。
無能な怠け者。これは総司令官または連絡将校に向いている、もしくは下級兵士。
理由は自ら考え動こうとしないので参謀や上官の命令どおりに動くためである。
無能な働き者。これは処刑するしかない。
理由は働き者ではあるが、無能であるために間違いに気づかず進んで実行していこうとし、さらなる間違いを引き起こすため。

デフォルトのoperator newの実装例

規格通りにデフォルトの挙動を実装してみた。

なんでget_new_hander()がないのだろう。

placement deleteの呼ばれる条件というのが、極めてレアだ。placement newに対応する同じ引数のplacement deleteは、ストレージの確保には成功したが、オブジェクトのコンストラクターが例外を投げた場合に呼ばれる。

結局、operator newというのは、new式のオーバーロードではなく、new式で使われるallocation functionのオーバーロードだというのが、問題をややこしくしている。

この際、new式の挙動を詳しく説明すべきだろう。

void * operator new ( std::size_t size ) throw( std::bad_alloc )
{
    if ( size == 0 ) size = 1 ;

    // Executes a loop: Within the loop,
    while( true )
    {
        // the function first attempts to allocate the requested storage.
        void * ptr = std::malloc( size ) ;

        // if the attempt is successful
        if ( ptr != nullptr )
        {// Returns a pointer to the allocated storage
            return ptr ;
        }

        // Otherwise, 
        std::new_handler handler = std::set_new_handler( nullptr ) ;
        std::set_new_handler( handler ) ;

        // if the argument in the most recent call to set_new_handler() was a null pointer,
        if ( handler == nullptr )
        {// throws bad_alloc.
            throw std::bad_alloc() ;
        }

        // Otherwise, the function calls the current new_handler function.
        handler() ;
        // If the called function returns, the loop repeats.
    }
}

void * operator new( std::size_t size, const std::nothrow_t & ) throw()
{
    try {
        // Calls operator new(size).
        // If the call returns normally, returns the result of that call. 
        return operator new( size ) ;
    } catch( ... )
    {
        // Otherwise, returns a null pointer.
        return nullptr ;
    }
}

void operator delete( void * ptr ) throw()
{
    // If ptr is null, does nothing.(std::free does nothing too)
    // Otherwise, reclaims the storage allocated by the earlier call to operator new.
    std::free( ptr ) ;
}

void operator delete(void * ptr, const std::nothrow_t & ) throw()
{
        // calls operator delete(ptr).
        operator delete( ptr ) ;
}

2010-07-01

bad_array_new_lengthにまつわる不思議な文面

5.3.4 New [expr.new] paragraph 7

the new-expression terminates by throwing an exception of a type that would match a handler (15.3) of type std::bad_array_new_length (18.6.2.2).

"an exception of a type that would match a handler (15.3) of type"という文面が気になる。なぜこんな回りくどい書き方をしているのか。"throws std::bad_array_new_length"ではだめなのか。

bad_array_new_lengthというからには、public継承しているstd::bad_allocや、そのベースクラスであるstd::exceptionのハンドラーもマッチするはずである。さらには、catch(...)も、当然マッチするはずである。なぜこんな書き方なのか。

思うに、std::bad_array_new_lengthを継承した実装依存のクラスを投げる実装を許可するためではないだろうか。たとえば、

namespace std {

class implementation_defined_special_exception : public bad_array_new_length {
public:
implementation_defined_special_exception() throw();
};

}

このクラスは、bad_array_new_lengthのハンドラーにもマッチする。これを許可するために、わざとこのような周りくどい書き方をしているのではなかろうか。

本では、これを詳しく書く必要はないだろう。

ちなみに、VC10の<new>ヘッダーには、bad_array_new_lengthがある。それではと、さっそく試してみたところ、

#include <new>
int main()
{
    try {
// 現状の大半のWindows環境では失敗するはず
        std::size_t n = std::numeric_limits<std::size_t>::max() ;
        new int[n] ;
    }
    catch( std::bad_array_new_length )
    { std::cout << "bad_array_new_length" << std::endl ; }
    catch( std::bad_alloc )
    { std::cout << "bad_alloc" << std::endl ; }
}

Debug buildでは、allocation functionの内部で使っているmalloc()のassertマクロに引っかかり、例外を投げるまでもなくプログラムが終了した。まあ、Debug buildだ。それでもいいだろう。ではRelease buildではどうか。bad_allocの方のハンドラーにマッチした。

どうやら、単にヘッダーで定義されているだけのようだ。意味がない。

ところで、このコードを書いていて気がついたのだが、以下のコードをコンパイルすると、

struct B {} ;
struct D : B {} ;
int main()
{
    try { }
    catch(B){}
    catch(D){}
}

MSVCとGCCの両方で、「実際にDの例外が飛んできても、Bのハンドラーの方が優先されて、Dのハンドラーではキャッチでけへんねやけどええんか」という警告を出した。久しぶりに役に立つコンパイラの警告を見た気がする。

newは鬼門だ

newは鬼門だ。せめて、オーバーロード可能でさえなければ、もう少し話は簡単になるのだが。