blogにコードを貼ったりするけど、実物のコピペじゃない方が多いんですよ。その場でちゃっちゃか「こんなだったよな」と思い出しながら、ほぼ同等のコードを書くことが多い。
で、enable_ifとかmplとかのコードを貼り付けたエントリで、やたらと書き直しが多発した。つまり、まだ全然慣れてないんだろうなあ。
まーでも、STLよりはだいぶ楽な気がするんだけど。それに何つーか、他にシンプルで効率のいい手が無いから仕方ない、って使い方だから納得しやすい。std::vectorなんかはやっぱりちょっと使いたい場面が無いしなあ。バウンダリチェックとか自動伸長とか要らないし。boost::arrayは使ってみたい気もするけど。つーかvectorよりもvalarrayよりも、boost::arrayみたいなのをまず標準に入れるべきだったんじゃなかろうか。もちろんvectorも入れた方がいいけど。あとvector<bool>は略。
まあ、iostreamとかもなあ…。しかも標準入出力だから、大抵最初の方で紹介されるんだよな。cout << "hello work" << endl;
とか書いて「これがC++っぽい書き方です」的に。そりゃ標準出力だから早めに紹介するよな。
演算子オーバーロードの曲芸は良くないよなあ(笑)。と言いつつもboost::formatとか超便利に使ってますが。
技術
型変換回避コードが無駄に肥大化する
enable_ifが使えて助かったー、と思ったんだが、long longに対応しなきゃならなくなった。やべえ。
type traitsには型のサイズとか見当たらないよな…。むーん。boost::mplには無いんかな。あった。sizeof_とかいう怪しげな物が。つーか何もかも全部が怪しげだ(笑)。
じゃ試しに判別用の関数を書くか。いやメタ関数か。
template <typename T> struct is_64bit : mpl::equal_to< mpl::sizeof_<T>, mpl::size_t<8> > {}; template <typename T> struct is_signed_64bit : mpl::and_< is_signed<T>, is_64bit<T> > {};
さあ、どんどん変な呪文っぽくなってきました。
どうせだからint8_tからuint64_tまで全部書いちゃおうぜー。で、unionのテンプレートメンバ関数で全部enable_ifで中身書き分けて、と…。
いや、完成はしたし、一見ちゃんと動いてるんですが。こういう時って大抵何か間違ってる気がするんだよなあ。妙に力が入ってる感じっつーか。視点を変えるともっとお気楽な手法で終わることが多い。まあ例外もあるけど。
でも、Boostも標準ライブラリもintrinsicsも無いとしたら、現状の他のコードも色々と変な呪文の塊になりそうだしなあ…。こんなもんなのかも。
まあ、一人でやるコードだから黒魔術でも何でもいいんだけど。むしろ、似たようなコードのコピペが大量に発生したんで気持ち悪い、つー話かも。でもunionで型変換避けるとどうしてもそうなる気がするんだよな。
ポインタ通して型を潰す奴は、サイズが違うと危ないしなあ…。まー少なくとも現状でも全く効率悪くないしいいんじゃね。コピペみたいなソースが数十行あるのがうざいけど。
jmp命令
普通にenable_if使えたし
普通に考えて使えそうだったしー。馬鹿じゃーん。
つーか、ちゃんとBoostの例文通りに書いたら通った。英文だから面倒で見てなかった。
いや、何でこんなん使いたかったかっつーと、スレッド間で受け渡すのは結局全部PODになってて、送受信の時に無理矢理型変換とかしてる訳ですよ。えらい低レベルになったような。shared_ptr何それ的な。
で、unsignedもsignedもfloatもdoubleもポインタもビット列変換無しに出し入れする関数が欲しくなった。
enable_if無しで失敗した時は、まあ明示的に型指定なり関数名で型を明示するなりして我慢しようと思ったのだが、異常に大量の型明示が発生した。その上、typedefで隠蔽されたAPI引数型とかを相手にしなきゃならなくなってきて、実際の型を確認して適合する関数を毎回指定するとかやってたら色々と本末転倒かつかなり問題があるんで、どうしてもオーバーロードなりテンプレートなりで解決したくなった訳だ。boost::anyはvptrとかRTTIとか噛んでそうなので無しで。
具体的には(実物とはだいぶ違うけど)、まずこんな感じに書いて失敗した。何度目だ。
void set ( int t ) { set_int(t); } void set ( unsigned int t ) { set_uint(t); } void set ( float t ); // 以下実装略 void set ( void* t );
これでfoo.set(uint32_data);
とか呼ぶと「引数tはunsigned intなんだけど、型変換すれば使える関数が三つもあるよ! どれ使えばいいの!」とかいつものクソバカなエラー吐くんで、
template <typename T> void set ( typename enable_if< is_signed<T>, T >::type t ) { set_int(t); } template <typename T> void set ( typename enable_if< is_unsigned<T>, T >::type t ) { set_uint(t); } // 以下略
こんなん書いたらコンパイル通った。読み出しもインライン最適化に期待してT&を受け取る気持ち悪いテンプレート関数にした。これで型の明示も関数名書き換えも無しに読み書き可能になった。万歳。asm見たらインラインになってなかったけど。いきなりjmpするPROCとかひどいのが出来てた(笑)。まあリンクタイムの最適化もあるしな。というよりそこまでシビアじゃないっつーに。
つーか、もっといい手は無いんだろうか。
C的に考えると、マクロか何かで(*((uint_32t*)&t))
こんなんに変換するとか。シンプルではあるなあ。オーバーロードも何も要らんですな。最適化も掛かりやすそうな。まあ今更か。不便で危険だけどシンプルで僅かに効率的かもしれない、ってことだしなあ。
あー、64-bit対応の時はuint64_t版にifdefとかで書き換えが発生したりするのか。まー大した手間でもない気はするが。
そういえばreinterpret_castもやったけど、static_castが出来る時はそっちでやれ、とコンパイラに怒られた。うぜえ(笑)。
異世界のコントが見たい
そういえばプログラマ二人の会話を横から聞いてたゲンガーの人が「日本語なのに意味が分からなくて異世界みたいで面白い」的なことを言ってたことを思い出した。
抽象化しまくる系だからかなあ。理系にありがちな。
で、適当な文章を自分で見直してみる。
適当な文章の例
オーバーロードの解決が曖昧なんですよ。カッとなってenable_ifを試したけど、なんかうまく通らないんでさっさと諦めた。まだ枯れるには程遠いし。
で、結局オーバーロードやめて型ごとに名前変えて終わり。名前ミスると終わるが仕方ない。
…プログラマ視点しか無いから、ちょっとどう見えるか分かりにくいな。用語を微妙に間違ったものに置き換えてみよう。
意味の通じない文章の例
クロードサイクルの解決が曖昧なんですよ。カッとなってcable_ifを試したけど、なんかうまく通らないんでさっさと諦めた。まだ枯れるには程遠いし。
で、結局クロードサイクルやめて型ごとに名前変えて終わり。名前ミスると終わるが仕方ない。
何言ってんだかわかんねえ(笑)。
でも別に面白くなくね。いや、意味が無い文章と分かってるから面白くないのか。そういえば「ちゃんと二人で通じてお互い笑ったりしてるから面白い」みたいに言ってたなあ。「分かりそうで分からない」とかも。