「偽札対応済み」って書いてある両替機。
自信を持ってるというのは貴重な才能なのかもしれない
ある程度能力を持ってるなら、成果を残せるか残せないかは自信を持ってるか持ってないかの違いだけかもしれないというような話。
最近思うのだけど、使えない奴(例えば、僕のような)の特徴として、「どうせ自分が何をどうやっても、何もどうにもならない」的な思考があると思う。
例えば、上の一行で言うと、「使えない奴(例えば僕」とか、そういったような思考。
こういった思考は、いったん根付いてしまうと、循環するので、なかなかやめられない。「どうせ自分は何もできないんだ→何もできないから何もやらない→何も成果を残せない→どうせ自分は何もできないんだ」とかいった感じで。
矯正しようと思っても、「いや、こういう性格だから、矯正は無理だし。」と、いう思考になってしまい、なかなかなおらない。
(余談として、さらにキモいことに、こういった思考の持ち主は「自分は何もしないのは、何もできないからである。何もできないのは、そういう性格だからである。そういう性格なのは、生まれ育った環境が大きく影響しており、もはや本人の努力ではどうしようも無い決定事項だった」とかいうような感じで、自分のキモさを社会の責任にしている部分があるのでキモい)
こういう人間は、本人がどんなに能力があっても、社会では成果を残せないような気がする。社会というのは、危険なチャレンジでもしないかぎり、特殊な能力が求められることなんて無いからだ。
世間では、「〜できる奴がすごい」とか、「〜の能力は必須だ」とか、そういう煽りをしょっちゅうみかける、が、しかし、そういうような能力なんてなくても、別に問題無く暮らしていける。
例えば、英語を話せる人よりも、ふつーに結婚して、子供生んで、等身大の幸せを掴んでいる人のほうが多い、というようなところから考えても、通常の生活を送るには、能力っていうのは、あってもなくてもあんまり変わらないと、いうのがいえると思う。
たしかに、なかには、能力が無いと先へ進まないとか、作業が滞るとかいうような場面はあるけど、世の中、生きているのは自分ひとりだけということはなく、大抵の場合、適度に誰かが助けられたり、そうでなくても誰かが少々の被害に遭う程度で済んでしまったりする。
実際にどうにもならないような場面なんていうのは存在してなくて、なんとかなってしまう。
つまり、「世の中は、能力なんてなくてもどうにかなる程度には最適化されてる」ということである。んで、そのことは、特殊な能力だけがあっても、微妙に使える場面が無い、ということにつながってくるような気がしないでもない。
えーと、なんか話がまとまってしまった。そうじゃなくて…
世の中で成果を残してる人っていうのは、自信に満ち溢れている感じがする。ていうか、まあ、世の中には、能力の高い人は結構ゴロゴロしてて、じゃあ、なんで、成果を出せる人ってゴロゴロしてないかというと、結局、「俺なら他人がやるよりもうまくやってやるよ」感?とか、そんな感じ?なんか適当になってきたけど。
自信を持ってる人は、負け組思考を持ってる奴(僕のような奴←こういうような奴)と反対の正のフィードバックというか、「俺ならやってやるぜ→チャレンジ→成果を残す→俺ならできるぜ」的な感じがあって、多分、そういうのが、いいんだろうなー、とか思う。
ぐだぐだだな!!そう、つまり、もっと自信を持とうということなんだ!!(最初からそう書けよ)
ある程度能力を獲得した、と思ったら、次に進むべきステップは、「どういう場面で、この能力は有効活用できるか?」という方向であるべきなのだ。
けして「さらに深い能力を獲得しよう」などと思ってはいけない。いったんそういう方向へ進んでしまうと、どんどんと深みへ行ってしまって、なかなか戻ってこれなくなる。そうすると、「高い能力を持ってるような気がするけど…持ってないような気もする?」ていうような感じになって、なんというか、持て余すのである。多分。ていうか今の僕がそんな感じ。どうしたらいいかわからんのだよなー。
だから、そう、つまり、技術を獲得しようとするときは、一緒に実績の残しかたとかも考えないといけないということだ。実績というのは、つまるところ、「自分ならできる」という自信ということになってくるだろう。(まあ、自信は測定できないから、結局実績は経験年数で表現するしかないんだけど)
とは言っても、なかなか、これは難しい。負け組思考はもはや深く根をはっており、どれだけ、「明日からは自信を持てるように生きていこう」と思っても、明日の朝には、あるべき姿に戻っており、「いや、やっぱどうでもいいや」とかいうような感じで安定してるだろうことは目に見えている!!
しかし、そこを、踏んばって、なんとか、「俺ならできる、今日できなくても明日やる!!」という思考を維持し続けなければならない。一体どうやって!
そこで、まあ、夢とか理想とかになってくるのである。夢とか理想とかではおなかは膨れないけど、しかし、それでも、大きな夢さえあれば、「もうどうでもいいや」という思考を「ほんの少しだけ」抑えることができるのである。
そのほんの少しは、本当にほんの少しだろうか?それは意味のないことだろうか?それはやってみないとわからない。
さて…あなたは、夢を持っているだろうか?僕は…?
僕の夢は、ああ、あれか。
主に、僕より頭良くて、お金持ちの女子に養われたり飼われたりしつつ、仕事しないで暮らしていけたらいいな、とか、そういうのを希望してます。
Cでクロージャ
int retfunc( int x ) { int a = 20; int hogehoge( ) { return a; } return hogehoge(); }
GCC拡張で、ネストした関数は、外側のスコープの変数を参照できる、というのは有名な話かと思う。
これの実装は結構面白くて、スタックの上に、フレームをロードしたあと、関数を呼び出すプログラムを実行時に生成して、それを関数ポインタとする、というようになっている。
(どっか解説有るかなーっと思ったけど見つからないな…google:gcc trampolineとかを漁ってみるとよいかもしれない)
gcc4.1、x86だと、
mov $フレームのアドレス, %ecx jmp 関数のアドレス
こういうプログラムをスタックの上に生成して、それを関数ポインタとするのである。(exec-shieldがあるともうちょっと色々あるらしいけど)
ちなみに、これは、
typedef int (*func_t)( int ); func_t dump_closure( func_t f) { int i; unsigned char *p = (unsigned char*)f; for ( i=0; i<10; i++ ) { putchar( p[i] ); } return f; } func_t retfunc( int x ) { int a = 4; int hogehoge( int b ) { return b + a + x; } dump_closure( hogehoge ); }
こういう感じのプログラムを書いて、
$ ./a.out > out $ objdump -m i386 -b binary -D out out: ファイル形式 binary セクション .data の逆アセンブル: 00000000 <.data>: 0: b9 08 f6 93 bf mov $0xbf93f608,%ecx ; これがフレームのアドレスね 5: e9 f0 8d 70 48 jmp 0x48708dfa ; これが関数へのジャンプ
こうやれば見られる。
で、ここまでが前提知識。続いて、クロージャを実装する。
GCC3.4とかでは無理だったのだけど、GCC4.xだと、このトランポリンを作るときのスタック構造が、
| | +--------+ | local | <- mov xxx, %ecx で %ecxに入れられる値 +--------+ | local | +--------+ | .... | . . +--------+ |mov | <- mov $0xbf93f608, %ecx + + | 0x08 | + + | 0xf6 | + + | 0x93 | + + | 0xbf | +--------+ |jmp | <- jmp 0x48708dfa + + | 0xf0 | + + | 0x8d | + + | 0x70 | + + | 0x48 | +--------+ . ..... . | ..... | <- %ebp +--------+
こんな感じになってるのである。(必ずこうなるのかどうかは調べてない)
これがあれば、ネストされた関数が使うフレームの大きさがわかる。
ここまでくれば、答えは見えたも同じだ。
struct trampoline_code /* トランポリンのコード */ { char mov_ecx; unsigned char *frame_start; /* フレームのアドレスはここに */ char jmp_func; unsigned char *func_offset; /* ジャンプ先のアドレスはここに */ } __attribute__((packed)) ; func_t make_closure( func_t f ) /* f がトランポリンの先頭アドレス */ { struct trampoline_code *src = (struct trampoline_code*)f; unsigned char *tramp_start = (unsigned char*)src; unsigned char *frame_start = src->frame_start; /* フレームは、フレームの先頭とトランポリンの先頭の間 */ unsigned int frame_size = tramp_start-frame_start; unsigned char *off; unsigned char *frame = malloc( frame_size ); struct trampoline_code *dst = malloc( sizeof(struct trampoline_code) ); /* フレームの値をコピー */ memcpy( frame, frame_start, frame_size ); /* トランポリンのコピー */ dst->mov_ecx = -71; dst->jmp_func = -23; dst->frame_start = frame; off = (unsigned int)src->func_offset + (unsigned char*)src; dst->func_offset = off - (unsigned int)dst; return (func_t)dst; }
これで、クロージャができる!!
#include <stdio.h> #include <stdlib.h> #include <string.h> typedef int (*func_t)( ); struct trampoline_code { char mov_ecx; unsigned char *frame_start; char jmp_func; unsigned char *func_offset; } __attribute__((packed)) ; func_t make_closure( func_t f ) { struct trampoline_code *src = (struct trampoline_code*)f; unsigned char *tramp_start = (unsigned char*)src; unsigned char *frame_start = src->frame_start; unsigned int frame_size = tramp_start-frame_start; unsigned char *off; unsigned char *frame = malloc( frame_size ); struct trampoline_code *dst = malloc( sizeof(struct trampoline_code) ); memcpy( frame, frame_start, frame_size ); dst->mov_ecx = -71; dst->jmp_func = -23; dst->frame_start = frame; off = (unsigned int)src->func_offset + (unsigned char*)src; dst->func_offset = off - (unsigned int)dst; return (func_t)dst; } void dump_result(void) { func_t func[10]; int i; for ( i=0; i<10; i++ ) { int n = i*i; int hogehoge( ) { return n + 30; } func[i] = make_closure( hogehoge ); } for ( i=0; i<10; i++ ) { printf("%d\n",func[i]()); } } int main() { dump_result(); return 0; }
だからどうした!!
TODO: 引数に対応する。(しません)
堕落したCプログラマのレベル10
元ネタ→http://d.hatena.ne.jp/ytqwerty/20060807#p1
レベル1: Cの暗黙の型変換、符号拡張のルールを暗唱できる。ANSI規格で未定義となっている部分を熟知し、かつ、各メーカーのコンパイラがその部分にどう対応しているかを把握しており、最低限のifdefでパフォーマンスを犠牲にすることなく、ポータビリティーの非常に高いプログラムを書く。
レベル2: sizeof(int) == sizeof(void*)ぐらいは許容できる。若干移植性を犠牲にしつつも、ポインタや整数の変換を駆使して、メモリを1byteも無駄にしないよう心掛ける。
レベル3: 結構どのコンパイラでも対応してるようなので、ちょっとやる気が出ないときは、//でコメントを書いている。
レベル4: GCC拡張やC99の機能を使ってみたら便利だったので、気が付くと普通に使っている。マクロよりもinlineを好む。allocaよりも可変長配列を使う。stdint.hはむしろ無いほうがおかしいと思う。
レベル5: プログラムを書こうと思ったが、いい加減文字列の操作が嫌になってきたので、文字列ライブラリを車輪。しかし、よく見ると、メモリリーク、メモリ破壊を起こしている。
レベル6: プログラムを書こうと思ったが、ハッシュテーブルとか二分探索木を書くところから始めないといけなくて、やめたくなる。しかし、無いものはしかたないので、とりあえずハッシュテーブルを実装したが、そこで飽きた。
レベル7: Boehm GCを使っている
レベル8: ちょっと息抜きにRubyをさわってみたら、便利すぎて、気が付くとちょっとしたツールは全部Rubyで書いている
レベル9: ほとんどのプログラムをRubyかPythonで書いてしまう。手元にあるCプログラムはPerlスクリプトで生成されたもの。
レベル10: Javaの素晴らしさを理解する
堕落したCプログラマのレベル -10
レベル-1: 関数は、ただのポインタとして持ち運べるのを見てそのシンプルさと柔軟さに感動する。
レベル-2: リンカの動作を説明できる。externを付ける必要性を理解する。
レベル-3: __attribute__((ctor))や、 __attribute__((dtor))の動きを説明できる。
レベル-4: __attribute__((weak))が利用できる場面を思い付く。
レベル-5: オブジェクトファイルと機械語とCプログラムの区別が無くなってくる。全部バイト列に見える
レベル-6: Cは、カリー化したり、クロージャ作ったりできることに気付く
レベル-7: Cコンパイラの都合によって生成されるコードが毎回変わるので困る。
レベル-8: むしろ関数のほとんどがインラインアセンブラで書いてある
レベル-9: それでもやっぱり-fomit-frame-pointerとかされると生成されるコードが変わるので困る。だんだんプログラムがmainと配列一個だけになってくる。
char prog[] = { 0x68, 0x6f, 0x2e, 0x0a, 0x00, 0x68, 0x48, 0x65, 0x6c, 0x6c, 0x89, 0xe1, 0xb8, 0x04, 0x00, 0x00, 0x00, 0xbb, 0x01, 0x00, 0x00, 0x00, 0xba, 0x07, 0x00, 0x00, 0x00, 0xcd, 0x80, 0x83, 0xc4, 0x08, 0xc3 }; typedef void (*func_t)(); int main() { ((func_t)prog)(); return 0; }
レベル-10: むしろnasmとかみたいなちゃんとしたアセンブラを使ったほうが楽だったことに気付く。
それは無い。