/ 最近 .rdf 追記 設定 本棚

脳log[C++: 2017-09-08~]



2017年09月08日 (金) [SakuraEditor] ツールチップがおかしいよね。選択した単語のヘルプがマウスカーソルのそばに出る。そこじゃないよ。■メニュー>設定>キーワードヘルプ自動表示するを選ぶと(※この設定をソースコードで見つけて、どこで設定できるのか探した。iniには書き込まれない揮発性の設定みたいだ)、選択しなくてもキャレット付近の単語の説明が出るんだけど、ポインタの先の単語じゃないんだよね。ポインタのそばに説明が出るのに……。■ポインタがビューの上にないときには表示しない処理もあるんだよね。表示するのはポインタではなくキャレットのそばの単語の説明なのに。■誰かのこだわりの結果なのかなんなのか。普通じゃないね。■2.3.1.0で補完をすると、例えば サクラ を入力してから補完ウィンドウを出して サクラエディタ を選ぶと、サクラサクラエディタ みたいに補完されるのだけど、trunkをデバッグモードでコンパイルしたものがたまたまあるので試してみてもそんなことは起こらないから、何かの設定のせいなのか、よくわからないうちに直ったのか。■単語検索といえば昔のはてなダイアリーは豪快だった。サクラエディタも Trieとか接尾辞配列とかを導入したら、予め決めた単語境界に基づいて抽出した単語を登録済みキーワードと比較するのではなく、もっとアグレッシブに編集中のテキストから登録済みのキーワードを見つけ出せるのに。……とかいって俺には難しすぎるデータ構造だから他人事(ひとごと)だよ。手を動かして理解する手もある?

最終更新: 2017-11-04T18:59+0900

[C++] オブジェクトメンバーの持ち方。初期化と再初期化。参照とポインタ。

クラスを書くときの理想として、コンストラクタでメンバーを初期化して以後は変更したくないというのがある。メンバーの状態は自身の行動の前提条件であり、そこが流動的ではあらゆるメソッドが事前に前提を確認してからでないと本題に入れなくなる。コンストラクタで準備の完了と条件の確認をすべて済ませておきたい。そこが動く、動いたなら、それはパラメータを変えた別のインスタンスに任せたい。

上で登場した AsWithHelpTooltip というオブジェクトは CTipWnd というオブジェクトをメンバーとして持っており、寿命を共にする。CTipWnd のサイズ(を始めとするその他諸々)を隠したいならポインタ(か参照)一択だが、そういう制約がないなら new CTipWnd の手間を避けて、AsWithHelpTooltip のサイズに CTipWnd のサイズを丸々含めるような持ち方をする。

初期化である。AsWithHelpTooltip とそのメンバーである CTipWnd は As~を継承する CEditView の初期化(ウィンドウ作成など)を待たないと初期化が完了しないので、As~のコンストラクタでは呼ばれるのが早すぎる。また、CEditView はコンストラクタとは別に Create という名前の初期化(リセット)関数を持っている。であれば As~にも Create という名前の初期化関数を生やして CEditView の Create から呼んでもらうというのが自然な流れ。As~はそれでいい。そのメンバーである CTipWnd にも Create の連鎖を波及させるべきだろうか。CTipWnd は小さな部品であり、そこそこ汎用性もある。そこに、外部の都合で、リセット関数という全く不要で全ての前提を覆す邪悪なメンバーを生やしてもいいものだろうか。

代入演算子を定義しようか。しかし代入は邪悪で避けるべきものだ。なにより外部のリソースを作成するときに使用したパラメータは、コンストラクタの中で使用するのみで以後は不要なので保存していない。代入なんかのためだけに余計な荷物をしょいこむなんてまっぴらごめんである。

ではやはりポインタだろうか。Create 関数が呼ばれるたびに new CTipWnd でメンバーのインスタンスを作り直そうか。こういうとき参照は使えない。参照はコンストラクタで初期化するしかできないし、初期化しなければいけない。そこが参照のポインタとは違う参照たる所以であるので不便でも不満でもない。しかしポインタには不満がある。ポインタは無効な状態(nullptr)を許容する所が参照とは違う点で、今回の As~と CTipWnd の関係ではそういう無効な状態の入り込む余地などないのだから、CTipWnd をポインタで持つというのは意図や前提の過不足のない表現として不適切で不満がある。

結局 As~の Create 関数の中でこうやって CTipWndを再作成したんですよ。

m_cTipWnd.~CTipWnd();
new(&m_cTipWnd) CTipWnd( G_AppInstance(), asView(this)->GetHwnd() );

代入なし、(CTipWndに)リセット関数なし、new なし、ポインタなしで満足してるんだけど、何かの本で読んだテクニックというわけではなくて、どんな落とし穴があるのか戦々恐々としてる。普通の new ですら使うのが怖くてできるだけ見えないように(STLを使ったり)するのに、placement new なんてとてもとても。

 @2017-09-28 placement new より swap

picojsonというパーサを読んでいます。分からないのは、以下に該当するコードです。deleteしていないのに、配置newしたオブジェクトがメモリリークしないのはなぜなのでしょうか。

inline value& value::operator= ( const value& x ) {
    if( this != &x ){
        this->~value();
        new (this) value(x); // thisの寿命はどうなる?
    }
    return *this;
}

ほぼ同じ2行だ。そして picojson は知っているし信頼できるソースだ。だけど検索したら現在の picojson にはその2行がない。こういうことらしい。

make operator= safe when part of LHS is being assigned, as well as ex… · kazuho/picojson@96f6c81

exception-safe はわかる。破壊と構築がアトミックでないから、構築し損ねた荒れ地にさらにデストラクタが走る可能性がある。

でも when part of LHS is being assigned っていうのがわからない。さへんのいちぶぶんにだいにゅうされているとき?

 @2017-10-30 placement new より swap (続き)

c++ - Can I use placement new(this) in operator=? - Stack Overflow

ある回答者によれば『Exceptional C++』で言及されているらしい。もう一度読もうか。

その回答に補足して、継承とスライシングに関わる問題が placement new を使った手法にはあるとも指摘されている。picojson の主な理由もそれだったのかもしれないけど、よくわからん。

ある型の代入演算子の中で自身のデストラクタを呼ぶとき、それが仮想であるならば呼ばれるデストラクタは派生クラスのものであるかもしれず、placement new で新しいインスタンスで上書きするときに派生部分の初期化が行われない、と。そのようにコードを呼んだ側も呼ばれた側も意図するところは「ある型」部分に限った代入なので、仮想のデストラクタを呼んだのが間違い。

『[単行本(ソフトカバー)] Jaroslav Tulach【APIデザインの極意 Java/NetBeansアーキテクト探究ノート】 インプレス』を読んでその苦労を垣間見るけど、派生クラスは難しい。派生が可能なクラスを公開して、それを互換性を保ったまま改善するというのは、ほとんど不可能なのではないかと思えるほど。どれだけ注意を払っても余分なコードで対策しても、得られるのは「ソースコード上の」とか「バイナリの」とかいう条件付きの互換性なんだからやってられない。

 @2017-11-02 placement new より swap (続きの続き)

『Exceptional C++』の項目41がそのものずばりでアンチイディオムとして取り上げており、すでに述べた2つ以外にもこれでもかこれでもかと否定の論拠を挙げていた。C++怖い。そして、「何かの本で読んだテクニックというわけではなくて」とか書いていたのは誰だっけ?

実際のところ、a.patch のケースでは例外安全性以外の問題はないと思うんだよね。Exceptional C++ のケースも picojson のケースも、継承を伴う代入演算子の話だし。問題があるのはたしかで万事うまくいく解決策もあるのに、あえて危険な手段をとったのが知識と思慮の不足ゆえなのは否定しようがないけど。


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なコンストラクタみたいに、明示的なキャストを必要とする変換を定義できるようになるらしい。