/ 最近 .rdf 追記 設定 本棚

脳log[C++: 2013-06-24~]



2013年06月24日 (月)

最終更新: 2013-07-14T00:41+0900

[C++][SakuraEditor] Sakura Editor / PatchUnicode / #618 Singletonの実装変更

静的変数の初期化とマルチスレッド安全性。C++11より前と VCに関してはお寒い状況らしいが、そもそものコードが素朴なものだった。

もともとマルチスレッド対応ではないので静的変数で実装するのは大差ないとして(マルチスレッド対応コードを自分自身で書く機会を放棄することにはなる?)、唯一性の保証を TSingleton利用者に期待するというなら TSingletonはただグローバルアクセサを定義する手段になってしまう。この assertで従来通りの保証ができるかなと思ったんだけど

TSingleton(){ assert(this == static_cast<TSingleton<T>*>(T::getInstance())); }

static変数の初期化にロックがかかってたらこれはデッドロックを引き起こすんでない?ロックせず素通りしてしまうと未初期化オブジェクトを使用してしまうおそれがあるよね? C++11に対応した gccはどうやってるんだろ。

パッチの目的はデストラクタが呼ばれるようになることにあるらしいが、一方で、任意のタイミングでオブジェクトを破棄することはできなくなる。それはシングルトンオブジェクトにはそぐわない扱いかもしれないけど。

本当に stackoverflowのコードを利用するなら継承をやめたらいい。そうすれば TSingletonの利用者などというものは存在せずシングルトンクラスの実装者しかいなくなる。実装を提供するだけならマクロでもできるし、あえて継承にする理由は一元的にインスタンス作成を補足することではないかと。偽りの名前を与えられた中途半端な実装を別ファイルに隔離するのはどうかと思う。


3回ぐらいコメントの下書きをしてるけどぐるぐるしてまとまらなくてこの日記になる。


 @2013-06-25

インスタンス変数を関数内staticではなくクラス内staticにするとアドレスを取得するのに getInstanceを呼ぶ必要がなくなって、getInstanceとコンストラクタ呼び出しがネストしなくなってロックの可能性(そんなものが実在するかは gccがないので知らない)が消える、と思ったんだけど、すべてのシングルトンオブジェクトの初期化がプログラム起動時に走ってしまいそうだ。これを避けるとインスタンスをポインタで保持することになって、これは現在のコードだ。インスタンスを関数内staticで保持したまま、getInstanceでそのアドレスだけをクラス内static変数にコピーする、とかいうのはトリッキーなわりに利がなくて目的を見失ってる感。

クラスで実装するシングルトンってなんのためにあるんだろうね。アプリケーションクラスだけがシングルトンでそれ以外はそのメンバでいいやん。Javaとか C#の Main関数みたいな居心地の悪さがあるかしらんけどさ。


 @2013-07-12 クリティカルセクションは他スレッドを閉め出すもの。

同じスレッドであれば同じCRITICAL_SECTIONを引数にして何度でも EnterCriticalSectionできるらしい(同じ回数の LeaveCriticalSectionが必要)。

各スレッドはクリティカルセクションの所有権を取得した後は、自らの実行をブロックすることなく、EnterCriticalSection または TryEnterCriticalSection 関数を追加で呼び出すことができます。この結果、スレッドが既に自ら所有しているクリティカルセクションを待機しようとしてデッドロックに陥ることを防止できます。

というわけで、コンパイラが静的変数の初期化を実際どう実装するかは知らんけど、あまり気にせず上の方に書いた assertを使っていいんじゃないかな。

イベントを使ったのはこのとき(20130416)が初めてで、クリティカルセクションはまだ使ったことがないのでした。たぶんその存在はペゾルドさんの本で読んだんだろうなあ。9年前


2010年05月29日 (土) NHK夕方のスターウォーズにヒナギクさんがいた。

最終更新: 2010-05-29T22:53+0900

[C++] これは真実?

仮想関数は、仮想関数テーブルに関数ポインタを保持するため、仮想関数の定義分サイズが増えることに注意。仮想関数を増やせば増やしただけ、オブジェクトサイズが増大する。

太るのは vtblで、これはクラスにつき一つしか存在しないんじゃ。「オブジェクト」=インスタンスではなくて「オブジェクト」=*.objという話ならわからないけど。


2009年11月29日 (日) 最先端からは何周遅れかな~。

最終更新: 2010-01-15T21:20+0900

[C++] ミックスイン

読みやすくも正確にも書けないのでリンクだけ。

サクラエディタの肥大化する一方の CEditView(描画、ダイアログの表示、文字列検索、単語判定、URL判定、ドラッグアンドドロップ対応、などなどなんでもござれ)を機能ごとに分割したいなあ、と考えていて、モデルとして想定していたのが Rubyの Enumerableモジュールのミックスイン。Enumerableを ミックスイン(include)するクラスが eachメソッドを提供するだけで、およそシーケンシャルアクセス可能なコレクションに対して行いたい操作(max, min, find, map,...)のすべてが可能になるという仕組み。

C++でやるにはどうするのかな、と想像していたときに「これってあれじゃね?」と結びついたのが「奇妙に再帰したテンプレートパターン(CRTP)」。このパターンは「[単行本(ソフトカバー)] ビョルン・カールソン【Boost C++をチューンアップする最先端ライブラリ】 ピアソンエデュケーション」で仕入れた。

仮に Enumerableモジュールのようなものをこのパターンで実現できたとして、その Enumerableテンプレート(eachメソッドが定義されたクラスをテンプレートパラメータとして受け取る)が配列っぽいあれやこれやのクラスにミックスイン(継承)されたらオブジェクトファイルがすごく肥大化しそうだという気がする。

純粋仮想関数にした eachに依存する形で Enumerableの各メソッドを実装すれば、テンプレートを使ったときと違って実装の共有ができるだろうか。

というようなことを想像してるだけ。


eachが yieldする型は何になるんだ?(C++で yieldなんてできないという話はおいておいて) 結局、型安全性を求めたらテンプレートになって、型ごとに実装(テンプレートのインスタンス)が作られるんじゃないか。

と、ここで boost::anyを使ってはどうか。Enumerableのメソッドの実装は要素の型に依存しないから anyのまま扱う。Enumerableのメソッドを呼び出す側が要素の型を知っているから anyから正しい型のオブジェクトを取り出せる。

と思ったら、「Enumerableのメソッドの実装は要素の型に依存しない」は嘘だ。比較できないと最大値や最小値が求まらん。Enumerableがコレクションに対して求めてるのは eachだけだけど、その要素に対しては比較できることや加算できることなど、いろいろ要求している。これじゃあテンプレートでないとやってられない。

いやいやいや、min, maxをはじめ Enumerableのほとんどのメソッドは要素を引数にとるブロックを受け付けて、その結果を基に処理を行っている。ブロックのコンテキストは Enumerableのメソッドを呼び出す側だから要素の型を知っている。やっぱり Enumerableの実装は要素の型を知らなくてもいい。

ブロックの代わりになるのは関数オブジェクトか traitsかな。


とまあ、かように面倒くさそうなことになるから CEditViewクラスになんでも突っ込む結果になるのも当然。言語の不備だ、とか言ってみる。

あるクラス(CEditDoc, CEditView)にいろいろな特性(検索可能性、ドラッグアンドドロップ可能性、範囲選択可能性、……)を、簡単にクラス外から付加する確立した手順が必要。……と、あくまで想像するだけ。

それに分割してしまうと機能間での連携(依存ともいう)に苦労することになるのが目に見えている。ANSI版の CEditViewはなんでも一カ所につっこめる限界点を超えているとは思うが。


mix-inや traitsは、使われる場所でいろいろ意味が違うみたい。自分は mix-inといえば Rubyのしか知らないし、traitsといえば std::stringの char_traitsしか知らない(それすら知っているとはいえない)。


2009年11月23日 (月)

最終更新: 2015-11-26T01:15+0900

[C++] これって合法?

class MyClass
{
public:
  MyClass() : hidden_variable_data(0), open_const_data( hidden_variable_data ) {}
  const int& open_const_data;
private:
  int hidden_variable_data;
};

「Microsoft (R) C/C++ Optimizing Compiler Version 14.00.50727.42 for x64」では期待通りにコンパイルしてくれて、期待通り( open_const_dataは書き換えられず、hidden_variable_dataを書き換えると open_const_dataも変更された )に動作した。

でも sizeof(MyClass)が 16(bytes)になる。これは int 4個分のサイズ。const int&を const intにするだけで、サイズは int 2個分と妥当なサイズになる。いったい何をしてるんでしょうね。


 サイズに関して@2009-11-25

x86 向けにコンパイルした結果と比較すると、参照のサイズはポインタのサイズらしい。それはわかるんだけど、64ビット(ポインタ 1つ) + 32ビット(int 1つ) = 12(bytes) とならないで 16になったのはなんでだろう、と。アラインメントってやつなのでしょうか。


/Zp[n] pack structs on n-byte boundary

試しに /Zp4 オプションをつけてみたら sizeof(MyClass)が 12(bytes)になった。やっぱりアラインメントだったのね。

ところで、合法なんでしょうか? (見たことがないんだけど)

だとしても () を省くためだけに、ポインタ分のサイズ増加と間接アクセスによる実行コスト増加を受け入れられるかといえば、やっぱり割に合わないか。


 @2009-11-28 n-byte boundary

「n-byte boundary」の byteが単数形なのは nが 1かもしれないからではなくて、n-byteが形容詞みたいになっているからだとか。退屈な例を出すと 10-year-old boyとか。64-bit OS もそうだな。たぶん塾で最初に聞いた。

なんで書いたかといえば、byteの複数形って bytesでいいのかなあ、と書いてから疑問に思ったから。「情報の単位」には「1MB = 1,000KB = (10^3)^2 = 10^6 = 1,000,000 Byte」というように書いてあって不安にさせられたが、「Byte - Wikipédia, a enciclopédia livre」には「Megabyte (MB) / 1 024 KB / 1 048 576 (2^20) Bytes / 8 388 608 Bits」と、Bytesが使われている。日本語ソースよりは ptと略される言語を信用する。ポーチュギーズ?


日本人が桁を 3つずつ区切るのが全く理解できない。

10,000,000 bytesを ten milion bytesだとか ten mega bytesだとか読むのであれば理解できる。でもたいていの場合、単位は円だ。4桁で区切れよ。そうすればカンマが万、億、兆だ。円以外の日常的な単位は n,µ,m,d,h,kを使って単位が 0を内包するから桁区切りなんて不要だし 3桁 4桁区切りが両方必要(円だけ例外扱い)になったりはしない。さすがに $は 3桁で区切ったほうがよさそうだけど、俺の日常に $という単位はない。

そんなわけで極力、桁を区切らないで済ますことにしてる。あんなもんは飾りだ。そして、飾りは不要だ(後半に異論はあるでしょう)。


2009年09月03日 (木) 不用意に出すと払われたりすくわれたりします。「社交辞令というものを知らず、飲み会などに誘っても絶対来ない」 誘いが本当に社交辞令だったのなら、断るのが正解では?(上司の社交辞令) それとも誘いに乗るふりでその場を収めて、あとで理由を付けて断るの?(部下の社交辞令) どちらも発言者の意図ではないだろうけど。

最終更新: 2010-06-22T11:46+0900

[C++] 型。var。

初歩の初歩ですよ。

vector<int> v(99);
for(int i = 0; i != (int)v.size(); ++i) {
}

みたいなのがあって(俺が書いたんじゃないよ)、ひょっとしたらキャストがなくても問題ないのかもしれないし、それがないと警告(符号付きと符号なしの比較がうんたらかんたら)が出るのかもしれないけど、(int)って書きたくないよね。static_cast<int>()にしろっていう問題でもなくて。iの型を unsignedにするのも若干のアドホック感がある(なんのための typedef)。かといって v.size()の戻り値の型(vector<int>::size_type?)をコピってくるのも嫌だね。たとえば(そんなキーワードはないけど) varを使って

vector<int> v(99);
for(var end = v.size(), i = 0; i != end; ++i) {
}

みたいに書きたいし、コンテナの型を何度も書く代わりにそこにある、型付けされた変数を使ってこう書きたい

list<int> l(99);
for(type(l)::iterator it = l.begin(); it != l.end(); ++it) {
}

C++のことだし方法はあるはずだけど……。(typedef list<int> hoge; はコンテナの型(hoge)を見つけてこないといけないのは同じだし、俺俺タイプをいちいち命名したくもないし)


 @2009-09-08 varではなく auto

■_ をち

2chにはせいぜいautoマンセーと0b論争がお似合い

  1. auto! まさか……この響きは……(C++0xの文脈で自動変数のわけはないし)。
  2. 検索で最初に見つかった > C++0x - auto - Faith and Brave - C++で遊ぼう
  3. 日付が 2007年。そんなに前から。全然 C++のこと知らないね。

 @2010-05-12 type(l)::iteratorの方も……

N2971: Core issue 743: decltype(...) name qualifiers

delctypeをnested-name-specifierで使えるようにする変更。簡単に言うと、delctype(T)::typeということができるようになる。
これは、日本から送った意見だ。だからどうということはないのだが。何を隠そう、信仰と勇気で有名なあの人が発見した問題だったはずだ。

「信仰と勇気で有名なあの人」って、すぐ上で autoに関してリンクしたとこの中の人でしょう。decltypeが、varに対する autoのように自分の希望をかなえてくれる本物のキーワードだってことは C++0xに関する記述を断片的に目にするにつれ知っていたけど、最初から名前を修飾する目的に使用できたわけではないとは知らなかった。行動を起こした人がいるのだ。これはもう足を向けて寝られない。


2009年07月23日 (木)

最終更新: 2010-01-15T21:38+0900

[Ruby][C++] 釣られますた

C/C++ではもちろん0==false はtrueなわけですが・・・ もしかして、boolean型を整数型とキャスト比較しない仕様なんでしょうか?

この辺はC/C++も難しくて 本質的には 2==trueも本当は真なんですが偽になったりもします。

Rubyでビットフラグをチェックするときは 0との比較を忘れてはいけない。

if(flag & mask)
  # (意図に反して)必ず実行される
end
if(0 != flag & mask)
  # 期待通り
end

というのはさておき、2==true に関して、「本質的には」という言葉が胡散臭い。その「本質」を表すコードは「(2!=false) == (true!=false)」であって、2==true は関係ないのでは? ==は真偽値を返す演算子ではあっても、真偽値のみをオペランドにとる演算子ではないから、2==true も 1==true も -1==true までもが暗黙にそのように解釈されて真になったりしたら、むしろ気持ち悪い。それは PHPの世界だ。(偏見)

Cと逆だな

Rubyでは非0が偽だと言いたげですね。


 @2009-11-06 まさしく偏見でした。

Safe Bool (ja.wikibooks.org)という C++のイディオムがあって、つまり裏を返すと、危険な Boolean変換が C++には存在するということだ。やーめーてー。

C++09(予定)では explicitなコンストラクタみたいに、明示的なキャストを必要とする変換を定義できるようになるらしい。