二重振り子のシミュレーション

C++から動画を直接出力できる自作ライブラリ「Dolly」を用いて、二重振り子のシミュレーション動画を作ってみました。

10分耐久・たのしい二重振り子

ソースコードはこちらにあります:

二重振り子はどこにいる?

さらに、振り子の通ったところをずっと重ねるように描いた動画も作りました。

すごいランダムな動きに見えますが、振り子のある場所はやっぱり下側が多い?

自作ライブラリでマンデルブローを描画した

C++から動画を直接出力する自作ライブラリDollyを使って、マンデルブローを拡大する動画を作りました。

この部分のソースはこちらにあります。

紙ジェクションマッピングしてみた

あけましておめでとうございます!今年もよろしくおねがいします。去年はあまりいろいろ出来なかったので、今年は少しずつでも、何かできればなと思っています。せっかく転学部までしたので物理をちゃんと身につけたい1ですし、大学再受験も本腰いれてやってきたいです。

さて、新年早速何かアウトプットしよう、ということで、去年作って放置していた「紙ジェクションマッピング」というネタを投稿します。

紙ジェクションマッピングしてみた

プロジェクションマッピングってご存知でしょうか。

20140102_07
東京駅が動き出す!? この冬見られる話題のプロジェクションマッピングまとめ

こういう感じで立体物に映像を投影するアートで、最近いろいろなところで行われているようです。

これっぽい事がしたいのですが、うちにはプロジェクタのような高いものはありません。ので、代わりに1枚50円で印刷できるコンビニのプリンタで何か似たようなことが出来ないかと模索した結果、こんな感じになりました。

仕組みは非常に単純で、ある角度から眺めると立体的に見えるように紙に印刷してあります。

20140102_01作り方ですが、まずこのマーカー画像をセブンネットプリントで印刷して(宣伝)、

20140102_03こんな感じで撮影します。このとき、カメラのファインダから眺めた時に台形が真四角に見えるようにするのがポイントです。

20140102_02この状態で撮った画像がこちら

20140102_04これを作ったソフトで変形させて適当にホワイトバランスを調整。

20140102_05これを紙に印刷して、マーカー画像を印刷した紙の代わりにこの紙をおきます。

20140102_01この状態で動画を撮ったのが、先ほどのニコ動の動画です。

20140102_06実態としては、台形に変形させただけの簡単画像加工をしただけということになりますw

今後の課題

今回は一番簡単な平面でしたが、もっといろいろなものに紙ジェクションしたいですね。たとえば、ティッシュの箱に貼り付けて、ある角度から見ると立体的に見える…とか。今位置合わせが非常にしんどいのですが、ARマーカーでなんとかできるといいんじゃないかなーとかも考えてます。3D力が問われる!!

ただ、結局プリンタの実力があまり高くないせいで、紙に写した方がそんなにリアルに見えないので限界かなという気がしています。っていうかそれがお蔵入りしてた原因です…(◞‸◟ )

  1. 物理とか数学って何やるにしても役に立つと思います []

クリスマスはNoiz2saをAndroidで!

おひさしぶりです。21日のニコニコ学会βは良かったですね。特に良かったのは研究100連発で、本職の人のすごさを見ると、私も何かつくりたいな〜って気分になります。私はボランティア(登壇者へのカンペ出し)として参加しました。凄い人を間近に見れるという特典付き(?)の嬉しいけど責任重大なお仕事です。イベント運営って大変ですね…。あと野糞の人は本気でマッドだ(褒め言葉)。

Noiz2saをAndroidに移植してみた!

Noiz2saはABA Gamesさんの開発した、アブストラクトな感じのシューティングゲームです。私はこのゲームがSTGに入り始めたきっかけだけあって、大好きです。

去年はWebに移植したNoiz2saを公開しましたが、今年はAndroidに移植したものを公開します。

クリスマス、そして年末年始といえばシューティングゲーム!ぜひお楽しみ下さい。敵の弾を避けて撃つ、指一本で遊べるシンプルなゲームです。

USBのゲームパッドをつなぐと、PC版と同様な操作で遊ぶことも可能です。

ダウンロードはこちらから

バッドノウハウFAQ

移植時のバッドノウハウを書きます

Q.リソースのファイルの拡張子がamrとかなのはなぜですか

このように置かないと、リソースが圧縮される時に圧縮されてしまい、AssetManagerからランダムリードすることが出来なくなってしまうからです。

あっコーナー分けた割に一個しか思い浮かばなかった…質問は随時受け付けています。

HDDを壊さないためにACアダプタにシール貼った

区別のつかないACアダプタ

ACアダプタ、好きですか。私はだいきらい!です(何

ハードウェアごとに別々のACアダプタが必要で、使いたいのはハードウェアなのに大きなACアダプタばかりが部屋を占拠していってしまいますし、何より種類が無数にあるACアダプタの区別が全然付きません。

パナソニックやソニーのような大企業(?)の作っているデバイスでは、ACアダプタを見れば何のハードウェア用なのかすぐに分かるのです。たぶん、このACアダプタならPSPの充電用でしょう、とかね。

しかし、独自のラベルを付けたりせず、ACアダプタのメーカが製造したものをそのまま同封している場合、本当に区別がつかなくなります。このACアダプタは何用のACアダプタでしょう。電圧と電流は分かるのですが…。

20130710_1

こたえ:HDDケース。

…わからないですよね!

ACアダプタ間違えてHDDが壊れた

単純に区別がつかないだけなら、仮にどのデバイスがどのACアダプタか区別が付かなくなっても、トライ&エラーで動くまで試せばよいのですが、それをやると壊れてしまうことがあります…。

今回、HDDのケースに、誤って正しい12vのアダプタではなく、間違った24vのACアダプタを接続してしまったところ、HDDケースは無事だったのですが、HDD本体が壊れてしまいました。

ううう…。保護回路とかは流石に無いんだろうなぁ…。

原始的解決法:シール

一回失敗したら、それはもう仕方がないので「次がない」ように対策しましょう。やはりココは原始的にシール…で…。

メモックロールテープというのがたまたま手元にあったので、これを使いました。テプラとか普通のセロハンテープとかとは違って、べとつかずに綺麗に剥がせるので精神衛生によい気がします、が、もしかするとすぐ取れちゃうかも…(この辺は要検討)

20130710_220130710_3

なんか他にいい方法あったら教えてね!!

二条項BSDとのデュアルライセンス。さきゅばす2.0b4を公開しました

久しぶりの更新です。。。。

久しぶりにさきゅばすのアップデートを行いました。またマイナー更新ですけど…。

2.0b4のダウンロードはこちらから!

リリースノート

デュアルライセンスになりました

今回のver 2.0b4のソースから、

  • GPL v3 or later
  • 2-clause BSD License

のデュアルライセンスになりました。派生物のライセンスはこのどちらか一方か、両方(デュアルライセンスのまま)を選ぶことができます。また、改造されたffmpegに関しては、勝手にBSDにすることはできないので、従来どおりGPL v3のままとなります。

また、GPL v3のライブラリがリンクされているので、配布バイナリも従来どおりGPL v3のままです。

ライブラリの更新とバグの修正を行いました

その他の更新はこんな感じです。

  • 2chのスレで見つかってたいくつかのバグを修正しました。
  • x264とかffmpeg本体のライブラリの更新を行いました。
  • ビルドシステムがcmakeからwafになりました。pythonで書きやすい。
    • このためにwafにバグ修正パッチまで投げました…(1.7.10でaccepted)
  • 未踏ソフトウェア事業で開発していたもののうち、基礎的な部分をライブラリ「しなもん」として切り出し、そちらを利用することにしました。
  • 上記の影響でlibxml2への依存が消えています。icuにも陽には依存しなくなりました(しなもんを通して陰に依存はしています)。

追記

2ちゃんで報告されてた「【第10回MMD杯本選】騒がしいゆーじょー【超遅刻カオス】 」の音ズレ問題ですが、たぶんacodecがaac(ffmpegの内蔵aacエンコーダ)が問題を引き起こしてるのではないかと思います1。外部ライブラリであるlibvo_aacencにすると解決するようです。

今までは変換レシピでaacを使うものがいくつかありましたが、今回のバージョンからlibvo_aacencだけにしました。

  1. スレに書こうかと思ったらまた規制掛かっててファッ!? []

2012年度未踏事業でスーパークリエイター認定されました

1月まで独立行政法人 情報処理推進機構(IPA)未踏IT人材発掘・育成事業の支援で「プログラミング言語ど~なっつ」と、それを用いた「ファミコンを題材にした電子教材のような何か」を作っておりましたが、本事業でスーパークリエータに認定されました。わーいわーい(謎

5/29にはスーパークリエータ認定授与式があり、そこで再度プレゼンテーションを行います。でも平日だよ!

今後の展開なのですが、とりあえず時間操作ができるプログラミング言語、ど~なっつの改善をしたいな~と思っておりまして、

とかやりたいです。特に最後ですが、これが出来ればニワン語の/seekとかにも対応できるし、現状抽象構文木をそのまま評価している「ねこまた」をど~なっつVMへのトランスレータにすることが出来れば、リアルタイム再生も夢でないくらいには高速化できる…かも??

本業(学生)の方があるので支援期間中ほどの速度は出せませんが、ぼちぼち書けていければな~と思います。進学する学科、間違えちゃったかなあ…。

エラー処理の地学史、もしくはあなたがMaybeモナドを使うべき理由。

(以下、カノッサの屈辱(テレビ番組)のノリでお願いします)

コンピュータの理論を確立したチューリング。彼は、無限のテープの長さを持ち、単なる計算を行うだけのチューリングマシンを夢想した。

しかし、我々の生きる現実世界におけるコンピュータでのプログラムに於いては、計算の失敗=エラーが発生するのは避ける事ができない宿命である。ネットワーク接続失敗、ファイルが見つからない、メモリが確保出来なかった…等々。すべてのエラーを書き出すには、それこそ無限の長さの紙が必要であろう。プログラム進化の歴史は例外との戦いであると言っても過言ではない。今回の講義では、プログラミング言語の様々な進化のうちの「エラー処理」に着目し、その長い戦いの歴史を概観する。

まずは、いにしえの先エクセプション紀におけるエラー処理を見てみよう。

先エクセプション紀:エラーコード-1の時代

エクセプション(exception)と呼ばれる隕石が衝突する以前の時代は先エクセプション紀と呼ばれている。この時代での例外処理の特徴は、エラーコードという概念の存在である。数学の関数を模したプログラム言語の「関数」とよばれる生物は、値を一つだけ戻すことができる。当時地球を支配していたAPI関数たちはこの「戻り値」を用いてエラーが発生した旨を伝えることで、上位のプログラマにその処理を託した。

//int readNumberFromFile(const char* filename);
// ファイルを読んで、そこに書かれた数字を返す。失敗した場合は-1を返す。
// ex) number.txtに"12"と書かれていれば、12を返す。
int status = readNumberFromFile("number.txt");

これがこの時代における典型的な「失敗する可能性のある関数」の姿である。戻り値として計算が失敗した場合に特別な値を返すことでその旨を表現し、多くの場合、-1などマイナスの値が好んで使われた。これを「エラーコード」と呼ぶ。

しかし、上記の例を見れば分かる通り、正常な結果と失敗した結果を同じint型の変数一つで表してしまうことで、いくつか問題が発生した。例えば、上記のプログラムでは、もしもファイル中に「-1」と書かれていた場合、このAPI関数は「正常な結果として」-1を返してしまうかもしれない。プログラマにはそれを区別する事ができない。必ず非負の数値が書かれていることを要請し、マイナスの場合はすべてエラーとすれば問題は解決するが、APIに要求とされる機能によってはそれは許されないかもしれない。そのような場合、このように苦肉の策として追加のポインタを使うケースがあった。

// ファイルを読んで、そこに書かれた数字を返す。
// 失敗したか否かの情報がstatus内に格納され、失敗したら-1が格納される。
// int readNumberFromFile(const char*filename, int* status);
int status = 0;
int result = readNumberFromFile("number.txt", &status);
if(status == -1)....

このポインタを用いて間接的に値を返す手法によって、プログラマに結果の値を一つしか伝えることができない、という関数の問題を回避することができた。しかし、この方法には明らかに欠陥がある。「処理が失敗したか否か」も、「成功した場合のその結果」もどちらも「関数から返ってくる値」のはずであるが、この方法ではその対称性が崩れてしまっており、あたかもstatusは入力値のようにも見えてしまっている。また、どちらをポインタとして渡して格納させるかはライブラリごと、関数ごとに違い、勘違いして双方を入れ替えてしまう悲劇も発生していた。また、Javaなどのように「参照型」を渡すことができない言語においては、この方法を(直接は)使うことができない。

Windowsの一部のAPIやOpenGLなどでは、別の関数を呼びエラーの発生を確認するという方式も考えだされた。

// int readNumberFromFile(const char* filename);
int result = readNumberFromFile("number.txt");
int status = getError();
if(status == -1) ....

この方法では、失敗する可能性のあるAPI関数の呼び出しの直後に特定の関数を呼ぶことでエラーの発生を確認した。当然、エラーを確認するまえに別のAPI関数を呼び出してしまうと、エラーを正しく処理することができない。

// int readNumberFromFile(const char* filename);
int result = readNumberFromFile("number.txt");
int result2 = anotherFunction(); /* !ここでもエラーが発生するかもしれない ! */
// readNumberFromFileのエラーをもはや反映しない
int status = getError(); 
if(status == -1) ....

保守や機能拡張時などにおいては、その事をすっかり忘れてしまい、明後日なエラーメッセージを前に途方に暮れるという事態も珍しいことではなかった。

また、別の関数をわざわざ呼ばなければならない点はやはり手間が大きく、この方式では輪をかけてエラーが無視されることとなった。

エラーコード-1の亜種として、nullを返すAPIが棲息する地域もあった。Java大陸や、C++海溝の一部などに見られる化石などから明らかになっている。このような関数呼び出しの場合、一見エラーを返さないような計算に見える:

String data = client.readDataFromFile();

dataがnullであれば失敗である事を示し、そうでない場合は成功した結果を表すという方式である。

この方式も、エラーコードと同様、正しい結果としてnullが返ってくる可能性がある場合にうまく対処できない。さらに「null」というただひとつの値しか返すことができないため、結局失敗した理由を他の方法を用いてAPI使用者に伝えなければならないという問題があった。多くのAPIでは実装工数がないの魔法の一声で、その方法は提供しないこともあった。

エラーコードの体系や伝え方(nullを返す・関数を呼ぶ・エラーコードを使う・ポインタを渡す)は当然、APIの創造主や、APIごとにまちまちであり、APIを消費するプログラマはそれらを個別に理解し、正しく対処することが求められた。そしてもちろん、そこには言語処理系のサポートは一切ない。

結果、そのエラー判定コードが正しいことをコンパイラで事前に判定することができないため、プログラマのその日の体調や気分によってはバグが発生することとなった。

エクセプション隕石の衝突

地球上でエラーコードを持つ多様な生物が棲息していたある日、ついに事件は起こった。エクセプション隕石の衝突がそれである。エクセプション隕石は地球上の多種多様な生物に深刻なダメージを与えたが、特に構造化の三要素へのダメージは深刻であった。

当時のプログラミング言語の構文細胞において、順次・反復・分岐の三生物が共生し、細胞小器官となる進化が発生していた。この三つの器官をまとめて「構造化の三要素」と呼ぶ。

/* 順次(上から下に実行) */
int a=10;
int b=a*10;

/* 反復(繰り返し) */
int sum = 0;
for(int i=0;i<100;++i){
    sum += i;
}

/* 分岐 */
if( isOdd(x) ){
    printf("odd number!\n");
}

しかし、一部の言語はそれよりも原始的なgotoと呼ばれる器官を退化させることなく持ち続け、APIを消費する一部の生物種は、例外の処理に好んでgotoを用いた。

if( mayFail() == ERROR_FAIL ){
    goto fail1;
}
...
fail1:
    /* 失敗時の処理 */

エクセプション隕石は、この原始的なgotoと構造化の三要素との悪魔融合合体進化を発生させた。なぜこのような奇妙な進化が起こったのかは、未だに議論のあるところである。この合体進化した新たな器官は、隕石の名前にちなんで「エクセプション」と呼ばれた。

try {
    int result = mayFail();
    int result2 = mayFail2();
    ....
} catch (Error const& e) {
    /* 例外時の処理 */
}

エクセプションは構造化の三要素のような姿を持ちつつ、goto文のような文脈を跳躍する特性も有する。APIを消費する上位のプログラマは、エラーの発生があたかも無いかのように処理を記述しつつ、最後にエラーに纏めて対処する事となった。

構造化の三要素とエラーコードを用いたAPI消費の多くのケースでは、ひとつでもエラーが発生した場合、次の処理を行うことは通常不可能であったため、多くのプログラムではエラーが発生したら、すぐにgoto文で失敗時の処理に移るということが見られた。

if( mayFail() == ERROR ){
    goro error;
}
if( mayFail2() == ERROR ){
    goro error;
}
if( mayFail3() == ERROR ){
    goro error;
}
error:
return -1;

エクセプションでは、これらをcatch節 という仕組みを使うことで共通化した。すなわち、エラーが発生したら移行の処理は全てスキップし、catch節の中でそのエラーに纏めて対処するということである。

try {
    int result = mayFail();
    int result2 = mayFail2();
    int result3 = mayFail3();
   ....
} catch (Error const& e) {
    /* 例外時の処理 */
}

エクセプションを持つ言語は地球上で広く繁栄することとなった。エクセプション器官を持つプログラミング言語は、オブジェクト指向と呼ばれる骨格も同時に持つことが多く、オブジェクト指向とエクセプションには関係があると考えられているが、先述の議論から構造化プログラミングとの関係の方が深いという異論も存在する。

しかし、このエクセプションは悪魔融合合体であるがゆえに、黒魔術的弱点を有する。

多くのプログラムでは、構造化の三要素が守られる事を暗黙の前提としてソフトウェアが構築されていくが、エクセプションの仕組みではエラーが発生するとそれがgoto由来の文脈跳躍生によってほぼ無化されてしまう。

またエクセプションの仕組みは処理の流れを変えることに依存しているため、処理の流れが複数存在するスレッド環境などではさらに技巧を用いることが必要とされ、多くの場合それはうまく動作することは無かった。

これらの事に端を発した多臓器不全で亡くなる生物は少なくなく、「More Effective C++」などの当時を研究する古医学書では1章をこのエクセプション由来の疾患に割かれている。

さらに、「で、結局纏めて対処しろって言われてもどうすれば良いんだ…」との声も根強く、catch節には何も書かれない事態も珍しい物では無くなった。これを「例外潰し」と呼ぶ。

エラーコードの復権:地殻内空洞での”数学”との遭逢、そしてMaybe種とEither種

エクセプション隕石はエラーコード種を概ね絶滅させたが、地下3000mではほそぼそと生きながらえる事となった。地下3000mには酸素がほぼ存在しないため、好気性生物であったエラーコード種には進化が求められた。そのための遺伝子を提供したのが、地殻内空洞に棲息する「数学」と呼ばれる、一連の生物群である。

この生物種がエラーコード種に提供したのは、「モナド」と呼ばれる強力な遺伝子である。この特徴を詳しく見てみよう。

エラーコード種に起こった問題である「同じ型で成功と失敗を表されるので、正しい結果の”-1″と失敗を表す”-1″の区別がつかない」という欠点を、値の上位に成功か失敗かを表す仕組み1である「Maybe」や「Either」を加えることで表現。さらに、nullの持つ「失敗としてただひとつの値しか返すことができない」という問題も、失敗時の型にその情報を付加できるようにすることで解決することとなった。

-- Leftが失敗、Rightが成功
data Either a b = Left a | Right b
--引数に応じて処理、失敗するかもしれない
mayFail :: ArgType -> Either String Result
-- ----------------
case mayFail arg of
    Left a -> ... --aにはエラーの理由が
    Right b -> ... --bには成功した結果が

思わぬ副作用として、エラーを無視することが出来なくなった。戻り値のMaybe/Eitherから結果を取り出すには、必ずエラーか否かをチェックする必要がある2

これは今までのプログラミング言語種でも可能であった。Cなら構造体と共用体で、C++ならクラスの仕組みを用いることで実現できる。しかし、それだけではエラーコードかどうかをチェックする今までの方法とあまり変わらず、煩雑なエラーチェックが必要になる。

すなわち、MaybeやEitherの導入だけでは、次のような非常に煩雑な記述が必要になってしまう、ということである。

--mayFailとmayFail2とmayFail3を使った処理
--この三つの関数は、どれも失敗するかもしれないのでEitherを返す。
func = case mayFail arg of
	Left err -> return (Left "error on maybeFail")
	Right ok -> case (mayFail2 ok arg2) of
		Left err2 -> return (Left "error on maybeFail2")
		Right ok2 -> case (mayFail3 ok2 arg3) of
			Left err3 -> return (Left "error on maybeFail3")
			Right ok3 -> return (Right ok3)

この状況に力を与えたのが、かの有名な「モナド」遺伝子である。

モナド遺伝子のbindの力とMaybeやEitherが共生することで、上のような煩雑なエラー処理は次のように至極単純化された。

func = do
    ok <- mayFail arg
    ok2 <- mayFail2 ok arg2
    ok3 <- mayFail3 ok2 arg3
    return (Just ok3)

表層を見ると、エラー処理が全て消え去ってしまっている。エクセプションではエラーを処理するために存在するcatch節が、完全に消滅している。しかし、エラーを「潰した」りはされない。もし、この関数を実行した際にmaybeFail2でエラーが発生した場合、その下位関数のエラー値がそのまま返され、続くmaybeFail3はスキップされる。

-- 途中でエラーが発生すれば、その結果が返される。潰れされない。
func -- -> Left "error occur on maybeFail2"
-- 最後まで成功すれば、最後にreturnした値が返される
func -- -> Right result

エクセプションにあった、構造化の三要素が無化されてしまう事によって生じうる問題はここでもまだ残っているが、MaybeやEitherは制御構造ではなく、単なる値であるため、エクセプションに比べて柔軟である。例えば、エクセプションを用いたプログラミング言語種では「何度か実行し、成功を試行する」といった仕組みを作るのは一般的に面倒であるが、Maybeを使えばこのように簡単である。

-- 渡された関数を何度か実行し、一度でも成功すればその結果を、
-- n回実行しても失敗するならNothingを返す
tryFunc :: Int -> ( Int -> Maybe Result ) -> Maybe Result
tryFunc n thunk =
    if n <= 0 then Nothing
    else
        case (thunk n) of
            Nothing -> tryFunc (n-1) thunk
            Just x -> Just x

この関数を用いてラップすることで、既存のMaybeを返す関数も簡単に「N回試行」させることができるようになる。

func = do
    ok1 <- mayFail
    -- maybeFail2だけは10回だけ試行する。一回でも成功すればよい。
    ok2 <- tryFunc 10 mayFail2
    ....
    return (ok3)

このように、「失敗するかもしれない」処理の内側と外側の行き来が非常に簡単になったことで、エクセプションの問題も対処しやすくなっている。

まとめ

  • エラーコードでは…
    • 正しい結果の-1と、エラーコードの-1を区別するのが困難
    • エラーコードのチェックが面倒くさい
    • 大体エラーコードのチェックなんてしなくなってくる
  • JavaやC++の例外機構では…
    • 例外が発生すると以降の処理が全部スキップされてしまうことに起因するバグ
    • 例外は潰されるもの
  • MaybeやEitherでは…
    • エラーを無視できない
    • 下位で発生したエラーをそのまま上位に渡すのが簡単
    • エラーが発生する処理とその周囲との行き来が簡単

私が現在理解したところによるMaybe/Either/Optionalの利点はこんな感じです。あと文中のHaskellがIOがどこでも起こせるかのような書き方ですが、IOと組み合わせると論点がぼやけそうなんです、ごめんなさい。ツッコミ、お待ちしています

ちなみに

JavaでもC++でも、匿名クラスやlambda式を多用することでMaybe/Eitherモナドは使うことができます。C++だとこんな感じで実装できます。Javaでもバイトで書いたことがあるのですが、バイトで書いたので手元にコードはありません(ぉ

 

  1. 多くの場合、型によって提供された []
  2. Just x <- maybeFailは邪悪なので無視 []

More Effective C++ ー 読むならC++を何万行も書く前に読みましょう。

有名な「Effective C++」の続編の本です。未踏の期間が終わったこともあり、読む時間を作ることができました。

この本は英語版で、一応日本語版もあるのですが、訳の評判はあまり良くありません。英語版の方も非常に読みやすい英語で、JavaDocレベルの英語が読めればなんとかなると思います。気のきいたジョークに使われる日常単語がいまいち分からなくて全部調べてたので、いまいち笑えませんでしたが…(ーー;

 Effective C++に比べて枝葉の話が多い

続編ということもあり、前編であるEffective C++では言語の根本的な部分のと比較して、Moreの方では細かい話が多いです。

前著の方では、例えば

  • コピーコンストラクタやデストラクタ、operator =を定義しないと勝手にコンパイラが定義してしまいますよ
  • 値渡しよりconst参照渡しを考えた方がいいですよ
  • newしたものはdelete、new [] したものはdelete[]、と対応させないといけません。
  • private継承や多重継承は注意深く使ったほうがいいですよ

といった内容が多く、他言語を使っていたプログラマがC++の世界を理解するには丁度良かったと思うのですが、今回のMoreの方では、

  • 関数の引数で暗黙的な変換が起こるのは値渡しかconst参照渡しの時だけで、しかも変換は一度しか起きません
  • 参照カウントなスマートポインタ
  • デストラクタは例外が起こってもちゃんと実行されるのでリソースリークを避けるのに使えます。
  • 多重継承・virtual継承する時のオブジェクトのデータフォーマットはこうなっている(事が多い)ですよ

といった内容です。最後の項目はvtableやvptrの実態がよくわかったので参考になったのですが、その他の項目は今ではインターネット上のC++に関する文章や、STLをある程度使っていると「そらそうよ…」「せやな…」と思ってしまう内容が多く、Effective C++を読んだ時に感じた、あの驚きをもう感じることはできませんでした。Effective C++を読んで少しC++を書き始めた時期に読めばもっと参考になったのでしょうが、数万行書いた後となると…。

改訂が繰り返されているEffective C++と違い、Moreは95年から一度も改訂されていません。STLは出たばかりの時期だったようです。

本を読むよりソースを読んで書いてWebのリソース読むのが好きな人は、まあ読まなくても…。

この本の中で特に一番大きく扱われているのは、参照カウント・スマートポインタです。sharedされている値そのものが参照カウント数を管理する、boostでいうところのintrusive_ptr相当のものをじっくり実装したあと、階層を一個上げてshared_ptr相当のものを実装します。が、相当のものがある事からも分かる通り、どちらも既に有名なもので、いまいち読んでも驚きがありません。しかもweak_ptrを扱っていませんし、参照カウントで問題になる「自分へのshared_ptrを基本的に持てない」という問題も扱っていません。

もしも読むなら、Effective C++を読んでちょっとC++を書いたらすぐに読むことをお勧めします!