SCREEN メモ (0.0.1)
スクリーンへの描画関係を担当。
まだ全然プロトタイプ。
大まかなアルゴリズムの確認で行き当たりばったりで組んだので、全体的に絡みがひどい、見通しの悪い、わかりずらい仕組みになってる。
もっと単純・簡単な景色へと書き直さないとしないと、難しくて使えない…使いたくない(orz
具体的には今後、STRING型をCHAR型で書き直して、そのSTRING型でLINE_LIST型を書き直して、そのLINE_LIST型でSCREEN型を書き直したらどうかなぁ…と考えてます。
まだ全然プロトタイプ。
大まかなアルゴリズムの確認で行き当たりばったりで組んだので、全体的に絡みがひどい、見通しの悪い、わかりずらい仕組みになってる。
もっと単純・簡単な景色へと書き直さないとしないと、難しくて使えない…使いたくない(orz
具体的には今後、STRING型をCHAR型で書き直して、そのSTRING型でLINE_LIST型を書き直して、そのLINE_LIST型でSCREEN型を書き直したらどうかなぁ…と考えてます。
| SCREEN.h (0.0.1) |
| /* SCREEN SCREENにはスクリーンへの文字バッファの描画を担当する。 ユーザーはSCREENの文字バッファに文字列を登録して、SCREENに描画を実行させる。 文字バッファには文字情報が格納されてるが、ここはあくまで描画バッファなので、ここからデータを『取り出す』ような使い方はしてはいけない。 あくまでSCREENの仕事は与えられたバッファをスクリーンに描画するのみである。 ここを文字情報の格納場所として考えたような使い方は『しないように!!』。(これは各モジュール間の依存関係を極力なくすため作戦です) SCREENの構造の説明 sl はポインタの配列である。ここにはデータの大本となる str 配列へのポインタが格納される。 sl の配列サイズは screen_h である。これは改行に依存せずに、純粋に行数で考えた場合を扱うために用意してある。 _bufは screen_w * screen_h の長さの配列である。 rb はポインタの配列である。これは_bufを2次元配列として扱い易くするために、_bufの各行の先頭にあたるアドレスを保存する。 これを用いて _buf にアクセスすれば計算量が減るし、扱いも簡単である。 rl はROW_LINK型の配列である。サイズはscreen_h ROW_LINKの内容は to_line_link に rb の添え字番号、line_separete_index には行の分割番目を記録する。 これらを用いての、一連の使用法の例を説明する。 まず SCREEN に表示させたい文字列 str を用意する。この str の文字列長は、いくら長くてもよい。 次に str を sl にリンクする。 これは、SCREEN の仮想行(文字列がscreen_wを超えた場合の改行を考慮しない、純粋な行数)で考えた場合の、表示させたい行数である。 たとえば、元のテキストの10行目を仮想行の1行目に表示させたいなら sl[0] である。3行目なら sl[2] という要領である。 sl には「文字列を screen_w の幅に収めて、あまった分は改行して…」という概念が無い。純粋な『行』という概念で考えてよい。 つぎに、slの指し示す内容を、 _buf に実際に書き込む。 直接 _buf を操作すると煩雑になるので、_buf にリンクされた rb を用いる。 rb に行を書き込む操作は、常に他の行への影響の可能性があるので、常に全体での一連の操作が必要になる。 つまり、たとえば rb の10行目だけを書き換えることはできない。10行目だけで十分な場合でも rb[0] から screen_h 分の更新が必要である。 まず最初に、rb[0] に sl[0] の内容を書き込むことから始まる。 具体的には sl[0] の指し示す str の(長さ / screen_w)が x > 0 ならば複数行という意味であり、書き込みループ回数を表し、 この rb[0] の( 書き込みループ回数 * screen_w )が、そのループ回での書き込みに用いる str の先頭アドレスであり、 これらを用いて rb[0],rb[1],rb[2],,, と書き込んでいく。その際に用いたslのindexが、各行のto_line_linkであり、indexが link_separete_index である。 この一連の処理によって sl[0] を _buf の(指定行〜必要行分)だけ書き込んだことになる。 たとえば例として sl[0] にリンクされた str の文字列長が 200 文字、screen_w が 80 ならば、rb の内容は、 rb[0] = sl[0] + 0-1 〜 sl[0] + 80-1 rb[1] = sl[0] + 80-1 〜 sl[0] + 160-1 rb[2] = sl[0] + 160-1 〜 sl[0] + 200-160-1 となり、 rl[0〜2].to_line_link = 0 rl[0〜2].line_separate_index = 80, 80, 40 となる。 この要領で、rb[screen_h-1]まで処理を繰り返すことにより、_bufへの登録が完了する。 以上。 ROW_LINK について: なぜこれが用意されてるかというと、スクリーン位置と、文字列の仮想行上での位置の対応を、簡単に計算するためである。 つまり、スクリーンカーソル位置を仮想行上での位置に簡単に変換するためである。また、その逆も。 */ #ifndef _SCREEN_H_ #define _SCREEN_H_ #include <stdio.h> #include "CURSOR_CONTROL.h" #define DEF_SCREEN_W 132 #define DEF_SCREEN_H 43 typedef struct tagROW_LINK { int to_line_link; int line_separete_index; } ROW_LINK; typedef struct tagSCREEN { int screen_w; int screen_h; ROW_LINK* rl; // 各行が、実際の行数の何行目に対応しているかのリンクを指す構造体の、配列 char* _buf; // w * h の長さの1次元配列 char** rb; // _bufの各行の先頭アドレスを指す、ポインタの配列。(いちいち h * screen_w + w しなくても簡単にアクセスできるように) char** sl; // 大本の文字列strへのリンク } SCREEN; extern void initSCREEN( SCREEN* scr ); extern void resizeSCREEN( SCREEN* scr, int w, int h ); // 大本の行 sl の index に登録する。 // あくまでリンクにすぎない(実際の文字データのコピーは行われない。実際の_buf へのコピーは refresh_bufSCREEN()で行われる。) // index が範囲外の場合は何もせずにリターンする。 extern void reg_strSCREEN( SCREEN* scr, int index, char* str ); // sl にリンクされた文字列の内容で _buf を更新する。 extern void refresh_bufSCREEN( SCREEN* scr ); // _bufの内容を画面に出力する extern void printSCREEN( SCREEN* scr, CURSOR_CONTROL* cc ); #endif // _SCREEN_H_ |
| SCREEN.c (0.0.1) |
| #include <stdio.h> #include <stdlib.h> #include <string.h> #include "SCREEN.h" #include "CURSOR_CONTROL.h" extern void __link_buf2rbSCREEN( SCREEN* scr ); extern void __initSCREEN( SCREEN* scr ); // buf と rb を結びつける void __link_buf2rbSCREEN( SCREEN* scr ) { int j; for ( j=0; j<scr->screen_h; j++ ) { scr->rb[j] = &scr->_buf[scr->screen_w * j]; } } // 文字列バッファを初期値 ' ' で埋める void __initSCREEN( SCREEN* scr ) { int i; for(i=0;i<scr->screen_h*scr->screen_w;i++){ scr->_buf[i]=' '; } } void initSCREEN( SCREEN* scr ) { scr->screen_w = DEF_SCREEN_W; scr->screen_h = DEF_SCREEN_H; scr->_buf = (char*)malloc( sizeof(char) * scr->screen_w * scr->screen_h ); scr->rl = (ROW_LINK*)malloc( sizeof(ROW_LINK) * scr->screen_h ); scr->rb = (char**)malloc( sizeof(char*) * scr->screen_h ); scr->sl = (char**)malloc( sizeof(char*) * scr->screen_h ); __initSCREEN( scr ); __link_buf2rbSCREEN( scr ); } void resizeSCREEN( SCREEN* scr, int w, int h ) { scr->screen_w = w; scr->screen_h = h; scr->_buf = (char*)realloc( scr->_buf, sizeof(char) * scr->screen_w * scr->screen_h ); scr->rl = (ROW_LINK*)realloc( scr->rl, sizeof(ROW_LINK) * scr->screen_h ); scr->rb = (char**)realloc( scr->rb, sizeof(char*) * scr->screen_h ); scr->sl = (char**)realloc( scr->sl, sizeof(char*) * scr->screen_h ); __initSCREEN( scr ); __link_buf2rbSCREEN( scr ); } // 大本の行 sl の index に登録する。 // あくまでリンクにすぎない(実際の文字データのコピーは行われない。実際の_buf へのコピーは refresh_bufSCREEN()で行われる。) // index が範囲外の場合は何もせずにリターンする。 // // 注!!:引数で渡すstrは、文字の最後に必ず ¥0 が付加されてること!! void reg_strSCREEN( SCREEN* scr, int index, char* str ) { if( index >= scr->screen_h || index < 0 ) { return; } scr->sl[index] = str; } // sl にリンクされた文字列の内容で _buf を更新する。 void refresh_bufSCREEN( SCREEN* scr ) { int j,jj; int cur_sh; char* src = scr->sl[0]; char* cur_src_line_top; int cur_src_line_len; char* dst = scr->rb[0]; char* cur_dst_line_top; cur_sh=0; // 現在のスクリーンの書き込み行 for(j=0;j<scr->screen_h;j++){ // 書き込み元の、最大書き込み可能行数の高さ分(スクリーンの高さと同じ) src = scr->sl[j]; // 書き込み元アドレスを sl から、現在の書き込み行番目を得る //printf("sl=%s",src); cur_src_line_len = ((int)strlen(src))/scr->screen_w; // 現在の書き込み行の、スクリーン書き込み用の仮想行数。 //printf("cur_src_line_len=%d ",cur_src_line_len); for(jj=0; jj<=cur_src_line_len; jj++){ // 仮想行数分だけ繰り返す(cur_src_line_lenは1行の時は0なので < ではなく <= で結んでる) if (cur_sh >= scr->screen_h ) { //printf("¥n単行複数行化ありcur_sh=%d¥n ",cur_sh ); return; // スクリーン書き込み行が、スクリーン高さに達したらリターンして終了 } //printf("cur_sh=%d ",cur_sh); cur_src_line_top = &(src[scr->screen_w * jj]); // 書き込み元の先頭アドレスを、仮想行の先頭に設定する(仮想行xスクリーン幅) //printf("cur_src_line_top=%d ",(int)cur_src_line_top); cur_dst_line_top = scr->rb[cur_sh]; // 書き込み先の先頭アドレス。こちらは文字位置ではなく、行そのものの移動。この違いに注意。 //printf("scr->rb[cur_sh]=%d¥n ",(int)scr->rb[cur_sh]); if(jj <= cur_src_line_len){ // 仮想的に分割した行の最後でなければ int len = scr->screen_w; strncpy( cur_dst_line_top, cur_src_line_top, len ); //(int)strlen(cur_src_line_top) ); // 実際に文字列をコピー cur_dst_line_top[len]='¥0'; //printf( "%s¥n",cur_dst_line_top); //printf(" screen_w = %d ; strlen = %d ",scr->screen_w, strlen(cur_dst_line_top) ); //printf("%d strcpy=%s¥n ",(int)cur_dst_line_top, cur_dst_line_top ); } else { //最後ならば int len = (int)strlen(cur_src_line_top); strncpy( cur_dst_line_top, cur_src_line_top, len ); cur_dst_line_top[len]='¥0'; //printf( "%s¥n",cur_dst_line_top); } cur_sh++; // 現在のスクリーンの書き込み位置を次に進める } } //printf("¥ncur_sh=%d¥n ",cur_sh ); } // _bufの内容を画面に出力する void printSCREEN( SCREEN* scr, CURSOR_CONTROL* cc ) { // clear_screen(); int row,colum; for( row=0; row<scr->screen_h; row++ ) { for( colum=0; colum<scr->screen_w; colum++ ) { __setposCURSOR_CONTROL( colum, row ); char c = scr->rb[row][colum]; if(c=='¥0' || c=='¥n') { break; } else { putchar(c); } } } __setposCURSOR_CONTROL( cc->pos_x, cc->pos_y ); } |
ETC_MATH メモ (0.0.6)
その他、雑多な関数。
int pow( int a, int inv )
ainv 、ただしinvは正のみ
int _dig( int a, int cardinal )
a の桁数を求める。 cardeinal には 10 と書けばいい。
aもcardinalも正のみ。
void itos( char* dst, int src )
整数型 src から 文字列型 dst への変換。
src は正の数字のみ。
dst の長さは不定。十分に長いサイズのバッファを与えること。
int pow( int a, int inv )
ainv 、ただしinvは正のみ
int _dig( int a, int cardinal )
a の桁数を求める。 cardeinal には 10 と書けばいい。
aもcardinalも正のみ。
void itos( char* dst, int src )
整数型 src から 文字列型 dst への変換。
src は正の数字のみ。
dst の長さは不定。十分に長いサイズのバッファを与えること。
| ETC_MATH.h (0.0.6) |
| #ifndef _ETC_MATH_H_ #define _ETC_MATH_H_ // a^inv extern int pow(int a, int inv ); // 基数による桁数 extern int _dig(int a, int cardinal ); // 数値型から文字列型 extern void itos( char* dst, int src ); #endif // _ETC_MATH_H_ |
| ETC_MATH.c (0.0.6) |
| #include <stdio.h> #include <stdlib.h> #include "ETC_MATH.h" // a^inv int pow(int a, int inv ) { int r=a; if(inv==0) r=1; if(inv==1) r=a; int i; for(i=2;i<=inv;i++){r*=a;} return(r); } // 基数による桁数 int _dig(int a, int cardinal ) { if( cardinal ==0 ){printf("err: dig() cardinal¥n");exit(1);} int count=0; while(a>0){ count++; a/=cardinal; if( a <= 0 ) { break; } } return(count); } // 数値型から文字列型 void itos( char* dst, int src ) { if(src > 0){ int p; int unit; int sub=0; int dig=_dig(src,10); int i; for(i=0;i<dig;i++){ p=pow(10,i+1); sub+=((src%p)-sub); unit=sub / pow(10, i); switch(unit){ case 0:dst[dig-1-i]='0';break; case 1:dst[dig-1-i]='1';break; case 2:dst[dig-1-i]='2';break; case 3:dst[dig-1-i]='3';break; case 4:dst[dig-1-i]='4';break; case 5:dst[dig-1-i]='5';break; case 6:dst[dig-1-i]='6';break; case 7:dst[dig-1-i]='7';break; case 8:dst[dig-1-i]='8';break; case 9:dst[dig-1-i]='9';break; default:printf("err: itos() dst[%d]¥n",dig-1-i ); exit(1); break; } } dst[dig]='¥0'; } else { dst[0]='0'; dst[1]='¥0'; } } |
CURSOR_CONTROL(0.0.6) メモ
windows の getch() に相当する、get_key() を用意しようと思った。
つまり、いちいちエンターを押さなくても、キーを押した瞬間に読み取ってくれるような関数。
windowsとlinuxでは、初期設定の手続きが違うからマクロで書きわけてます。
visual c++ だと、コンパイル時に普通は標準だと自動的に #define WIN32 が付加されるので、それを頼りに #ifdef WIN32 (←WIN32 が定義されてたら?という分岐)で処理を分岐させてます。
linuxだと、プログラムの終了時に、設定の復帰処理を行わないといけないので、main() の最後に必ず endCURSOR_CONTROL(void) を書くように。 windowsでコンパイルした場合、単純にこの関数は空になります。
初期設定は、得に意識して行う必要はありません。 CURSOR_CONTROL型の変数を初期化する関数 initCURSOR_CONTROL() を実行すると、(呼び出しがプログラム中の最初の一回目なら)自動的に初期化が行われます。
CURSOR_CONTROL 型の変数はいくらでも作れますが、initCURSOR_CONTROL() を実行した際に、初期設定を行うのは、プログラム中全体での最初の1個目の初期化時だけです。
void setposCURSOR_CONTROL( CURSOR_CONTROL* cc, int pos_x, int ppos_y )
void __setposCURSOR_CONTROL( int pos_x, int ppos_y )
どちらもカーソルの位置をセットする関数。
この位置に、カーソルが移動する。(文字の描画位置)
__が付いた方は、現在位置を cc に保存する必要が無い場合などに使う。
通常版( setposCURSOR() )だと、実行毎に cc に現在位置が保存される。(あと細かな範囲補正と)
つまり、いちいちエンターを押さなくても、キーを押した瞬間に読み取ってくれるような関数。
windowsとlinuxでは、初期設定の手続きが違うからマクロで書きわけてます。
visual c++ だと、コンパイル時に普通は標準だと自動的に #define WIN32 が付加されるので、それを頼りに #ifdef WIN32 (←WIN32 が定義されてたら?という分岐)で処理を分岐させてます。
linuxだと、プログラムの終了時に、設定の復帰処理を行わないといけないので、main() の最後に必ず endCURSOR_CONTROL(void) を書くように。 windowsでコンパイルした場合、単純にこの関数は空になります。
初期設定は、得に意識して行う必要はありません。 CURSOR_CONTROL型の変数を初期化する関数 initCURSOR_CONTROL() を実行すると、(呼び出しがプログラム中の最初の一回目なら)自動的に初期化が行われます。
CURSOR_CONTROL 型の変数はいくらでも作れますが、initCURSOR_CONTROL() を実行した際に、初期設定を行うのは、プログラム中全体での最初の1個目の初期化時だけです。
| CURSOR_CONTROL型 □ | → 初期化 | initCURSOR_CONTROL()で初期化 | |
| ↓ すでに以前に、 他のCURSOR_CONTROL型の 初期化を行ったことがある? | |||
| ↓ まだない。初 ( flg_init_termios == 0 ) | |||
| termios の初期設定を行う flg_init_termios フラグを1に |
void setposCURSOR_CONTROL( CURSOR_CONTROL* cc, int pos_x, int ppos_y )
void __setposCURSOR_CONTROL( int pos_x, int ppos_y )
どちらもカーソルの位置をセットする関数。
この位置に、カーソルが移動する。(文字の描画位置)
__が付いた方は、現在位置を cc に保存する必要が無い場合などに使う。
通常版( setposCURSOR() )だと、実行毎に cc に現在位置が保存される。(あと細かな範囲補正と)
| setposCURSOR_CONTROL() | __setposCURSOR_CONTROL() |
| 指定位置へとカーソルを移動する screenのサイズと比較し、指定位置がscreen範囲を飛び出してる場合は補正する。 その後、内部的に __setposCURSOR_CONTROL()を呼び出す。 | 指定位置へとカーソルを移動する |
| CURSOR_CONTROL.h (0.0.6) |
| #ifndef _CURSOR_CONTROL_H_ #define _CURSOR_CONTROL_H_ #ifdef WIN32 #include <windows.h> #else #include <unistd.h> #include <termios.h> #include <sys/time.h> #endif // WIN32 #ifdef WIN32 #else extern struct termios save_term; extern struct termios temp_term; #endif // WIN32 typedef struct tagCOUSOR_CONTROL { int pos_x; // カーソルの現在位置 int pos_y; int scr_w; // スクリーンの幅・高さ int scr_h; } CURSOR_CONTROL; extern void initCURSOR_CONTROL( CURSOR_CONTROL* cc ); // CURSOR_CONTROLのシステムの初期化も兼ねる。最初に必ず一回以上実行すること extern void endCURSOR_CONTROL( void ); // CURSOR_CONTROLのシステムの終了処理。mainの最後で必ず一回実効すること。 // 単純にカーソル位置を移動するのみ。内部処理的にプリント位置を調整する目的の時はこちら extern void __setposCURSOR_CONTROL( int x, int y ); // cc にカーソル位置情報を保存しつつ移動する。通常のカーソル移動はこちら extern void setposCURSOR_CONTROL( CURSOR_CONTROL* cc, int x, int y ); // カーソルを1マス移動する(画面の外周外に出た場合は、外周位置に留まる) extern void move_rightCURSOR_CONTROL( CURSOR_CONTROL* cc ); extern void move_leftCURSOR_CONTROL( CURSOR_CONTROL* cc ); extern void move_upCURSOR_CONTROL( CURSOR_CONTROL* cc ); extern void move_downCURSOR_CONTROL( CURSOR_CONTROL* cc ); // キー入力を得る extern char get_key(void); // 画面をクリアーする extern void clear_screen(void); #endif // _CURSOR_CONTROL_H_ |
| CURSOR_CONTROL.c (0.0.6) |
| #include <stdio.h> #include <stdlib.h> #include <string.h> #include "SCREEN.h" #include "ETC_MATH.h" #include "CURSOR_CONTROL.h" #ifdef WIN32 #include <windows.h> #else #include <unistd.h> #include <termios.h> #include <sys/time.h> #endif // WIN32 #ifdef WIN32 HANDLE hConsole; static int flg_init_hConsole = 0; #else struct termios save_term; struct termios temp_term; static int flg_init_termios = 0; #endif // WIN32 void initCURSOR_CONTROL( CURSOR_CONTROL* cc ) { cc->pos_x = 0; cc->pos_y = 0; cc->scr_w = DEF_SCREEN_W; cc->scr_h = DEF_SCREEN_H; #ifdef WIN32 if( flg_init_hConsore == 0 ) { hConsole = GetStdHandle( STD_OUTPUT_HANDLE ); flg_init_hConsore = 1; } #else if( flg_init_termios == 0 ) { tcgetattr(fileno(stdin), &save_term); temp_term = save_term; temp_term.c_iflag &= IGNCR; temp_term.c_lflag &= ~ICANON; temp_term.c_lflag &= ~ECHO; temp_term.c_lflag &= ~ISIG; temp_term.c_cc[VMIN]=1; temp_term.c_cc[VTIME]=0; tcsetattr(fileno(stdin), TCSANOW, &temp_term); flg_init_termios = 1; } #endif //WIN32 } void endCURSOR_CONTROL(void) { #ifdef WIN32 if( flg_init_hConsore == 1 ) { flg_init_hConsore = 0; } #else if( flg_init_termios == 1 ) { tcsetattr(fileno(stdin), TCSANOW, &save_term ); flg_init_termios = 0; } #endif //WIN32 } void setposCURSOR_CONTROL( CURSOR_CONTROL* cc, int x, int y ) { cc->pos_x = (x<0)?0:x; cc->pos_y = (y<0)?0:y; if(cc->pos_x >= cc->scr_w){cc->pos_x = cc->scr_w-1;} if(cc->pos_y >= cc->scr_h){cc->pos_y = cc->scr_h-1;} __setposCURSOR_CONTROL( cc->pos_x, cc->pos_y ); } void clear_screen(void) { #ifdef WIN32 #else printf("%c[2J",0x1B ); #endif // WIN32 } char get_key(void) { #ifdef WIN32 return( (char)getch() ); #else return( getchar() ); #endif // WIN32 } // カーソルを1マス移動する(画面の外周外に出た場合は、外周位置に留まる) void move_rightCURSOR_CONTROL( CURSOR_CONTROL* cc ) { cc->pos_x++; if ( cc->pos_x >= cc->scr_w ) cc->pos_x = cc->scr_w - 1; setposCURSOR_CONTROL( cc, cc->pos_x, cc->pos_y ); } void move_leftCURSOR_CONTROL( CURSOR_CONTROL* cc ) { cc->pos_x--; if ( cc->pos_x < 0 ) cc->pos_x = 0; setposCURSOR_CONTROL( cc, cc->pos_x, cc->pos_y ); } void move_upCURSOR_CONTROL( CURSOR_CONTROL* cc ) { cc->pos_y++; if ( cc->pos_y >= cc->scr_h ) cc->pos_y = cc->scr_h - 1; setposCURSOR_CONTROL( cc, cc->pos_x, cc->pos_y ); } void move_downCURSOR_CONTROL( CURSOR_CONTROL* cc ) { cc->pos_y--; if ( cc->pos_y < 0 ) cc->pos_y = 0; setposCURSOR_CONTROL( cc, cc->pos_x, cc->pos_y ); } #ifdef WIN32 void __setposCURSOR_CONTROL( int x, int y ) { COORD Pos; Pos.X = x; Pos.Y = y; SetConsoleCursorPosition(hConsole, Pos); } #endif // WIN32 #ifndef WIN32 void __setposCURSOR_CONTROL( int x, int y ) { x+=1; y+=1; char str_x[0x0F]; char str_y[0x0F]; itos( str_x, x ); itos( str_y, y ); //printf("str_x=%s, str_y=%s\n",str_x, str_y ); int str_x_len = (int)strlen(str_x); int str_y_len = (int)strlen(str_y); char command[0x1F]; command[0]=0x1b; command[1]='['; int i; char* cp; cp = &command[2]; for(i=0;i<str_y_len;i++){ *cp=str_y[i]; cp++; } command[2 + str_y_len] =';'; cp = &command[2+str_y_len+1]; for(i=0;i<str_x_len;i++){ *cp=str_x[i]; cp++; } command[2 + str_y_len + 1 + str_x_len + 0] ='H'; command[2 + str_y_len + 1 + str_x_len + 1] ='\0'; // puts(command); for(i=0; i<2 + str_y_len + 1 + str_x_len + 2; i++){ putchar(command[i]);//putchar(' '); } // // printf("%c[%d;%df",0x1b,x,y); } #endif // WIN32 |
CHAR(0.0.6) メモ
CHAR に、putchar に相当する関数を用意しようと思った。
int printCHAR( CHAR* ch, CURSOR_CONTROL* cc )
引数は CHAR と カーソルのコントローラー
このカーソル位置に、CHAR の文字を出力する。
ASCII の場合は半角、その他の場合は全角。
(半角カナは考慮してません。←そのうち追加します。)
戻り値は、画面の表示ブロックの消費量。ASCIIなら1、全角なら2。
int printCHAR( CHAR* ch, CURSOR_CONTROL* cc )
引数は CHAR と カーソルのコントローラー
このカーソル位置に、CHAR の文字を出力する。
ASCII の場合は半角、その他の場合は全角。
(半角カナは考慮してません。←そのうち追加します。)
戻り値は、画面の表示ブロックの消費量。ASCIIなら1、全角なら2。
| CHAR.h (0.0.6) |
| #ifndef _CHAR_H_ #define _CHAR_H_ #include "CURSOR_CONTROL.h" typedef struct tagCHAR { unsigned char c[4]; int mode; } CHAR; // char[4] を読み込み UTF-8 に変換して格納する。 // リターンは使用してるバイト数。判別不能の時は0を返す。 extern int writeCHAR( CHAR* dst, unsigned char* src ); // char[4] に UTF-8 の先頭バイトから順に格納する。 // ただし、使用されるバイト数は、モードの必要分”だけ”であり、 // それ以降のバイトには一切触れない(値を変えない・アクセスすらしない) // 戻り値には、使用しているバイト数が返される。0の場合はほぼエラーを意味する。 extern int readCHAR( CHAR* src, unsigned char* dst ); // 指定したカーソル位置に一文字表示する // 戻り値は、文字の表示に使用したブロック数。ASCII(半角文字)の場合は1ブロック、非ASCII(全角文字)の場合は2ブロック // カーソル位置は、表示後も変わらない。(自動では進まない) extern int printCHAR( CHAR* ch, CURSOR_CONTROL* cc ); #endif // _CHAR_H_ |
| CHAR.c (0.0.6) |
| #include <stdio.h> #include "CHAR.h" #include "CURSOR_CONTROL.h" // char[4] を読み込み UTF-8 に変換して格納する。 // リターンは使用してるバイト数。判別不能の時は0を返す。 int writeCHAR( CHAR* dst, unsigned char* src ) { dst->mode = 0; unsigned char c0 = src[0]; unsigned char c1; unsigned char c2; unsigned char c3; // 先頭bitが1ならマルチバイト文字、0ならASCII文字 if( ( c0 & 0x80 ) == 0 ) { dst->c[0] = c0; dst->c[1] = 0; dst->c[2] = 0; dst->c[3] = 0; dst->mode = 1; } else { // 第2bitが0なら先頭以外の文字、1なら先頭文字 if ( ( c0 & 0x40 ) == 0 ) { } else { dst->c[0] = c0; // 第3bitが0なら2バイト文字、1なら3バイト以上の文字 if ( ( c0 & 0x20 ) == 0 ) { dst->c[1] = src[1]; dst->mode = 2; dst->c[2] = 0; dst->c[3] = 0; } else { // ...かつ、第4bitが0なら3バイト文字、1なら4バイト文字 if ( ( c0 & 0x10 ) == 0 ) { dst->c[1] = src[1]; dst->c[2] = src[2]; dst->mode = 3; dst->c[3] = 0; } else { dst->c[1] = src[1]; dst->c[2] = src[2]; dst->c[3] = src[3]; dst->mode = 4; } } } } return( dst->mode ); } // char[4] に UTF-8 の先頭バイトから順に格納する。 // ただし、使用されるバイト数は、モードの必要分”だけ”であり、 // それ以降のバイトには一切触れない(値を変えない・アクセスすらしない) // 戻り値には、使用しているバイト数が返される。0の場合はほぼエラーを意味する。 int readCHAR( CHAR* src, unsigned char* dst ) { switch(src->mode){ case 1: dst[0] = src->c[0]; return(1); case 2: dst[0] = src->c[0]; dst[1] = src->c[1]; return(2); case 3: dst[0] = src->c[0]; dst[1] = src->c[1]; dst[2] = src->c[2]; return(3); case 4: dst[0] = src->c[0]; dst[1] = src->c[1]; dst[2] = src->c[2]; dst[3] = src->c[3]; return(4); default: return(0); } } // 指定したカーソル位置に一文字表示する // 戻り値は、文字の表示に使用したブロック数。ASCII(半角文字)の場合は1ブロック、非ASCII(全角文字)の場合は2ブロック // カーソル位置は、表示後も変わらない。(自動では進まない) int printCHAR( CHAR* ch, CURSOR_CONTROL* cc ) { unsigned char c[4]; readCHAR( ch, c ); int step; int i; __setposCURSOR_CONTROL( cc->pos_x, cc->pos_y); // カーソル位置をセット for( i=0; i < ch->mode; i++ ) { putchar( c[i] ); } __setposCURSOR_CONTROL( cc->pos_x, cc->pos_y ); // putchar() の実行で変わったであろうカーソル位置を、元にもどす(自動での移動はさせない) switch(ch->mode){ case 0: step = 0; break; case 1: step = 1; break; // ASCII 半角文字なら case 2: step = 2; break; // 非ASCIIは全て全角文字として扱う。(半角カナなどは考慮しない) case 3: step = 2; break; case 4: step = 2; break; default: step = 0; break; } return( step ); } |
CHAR メモ
「そもそも char型とは何か?」
という、疑問。
本来の char の意味を考えたら、char を補完する新たな CHAR 型の必要性を感じた。そしてそれを基本型として組むべきではないのか?と。
もともと、C の char型とは、『キャラクタ型』という意味。 つまり、1つで1文字を表せる。そういう変数。
変数とは、中に数をメモしとくための箱。 そもそも、なぜ変数で文字を表せるのか?
実際のところ、char 自体に文字画像が入ってるワケじゃなくて、char に記録された数字は、あれは、単なる文字一覧表の中での、文字に対応した番号にすぎない。
ASCIIコード対応表
この表の文字の数は、全部で合計は 16 * 8 で 128個。
つまり0〜127までの数字を表せる入れ物なら、十分。
この箱の1つ1つが、それぞれ、0か1かのどちらかを記録できる。
この箱1つのことを1ビットと呼ぶ。
で、この箱を8個まとめたものが1バイト。
1メガバイトってのは、この棒が100万本使える状態。1ギガバイトってのは10億本使える状態。
この棒の、箱1つ1つは、0か1のどちらかしか記録できない。だから、箱1つに3とか5みたいな数字は記録できない。
数字は、この棒に2進数で記録する。
10進数だと、0、1、2、3、、、8、9 と来て、繰り上がって10になる。 で、11、12、13、、、と続いてく。
この10で繰り上がるってのが、2進数の場合2で繰り上がる。
つまり、0、1 と来たら、繰り上がって 10 となる。 で、11 で、また繰り上がって 100で、101、
また繰り上がって 110、で111、また繰り上がって 1000、で1001、また繰り上がって1010、で1011、でまた繰り上がって 1100、で1101、でまた繰り上がって1110、で1111。
わかりやすくイメージ描くと、こんな感じ
LSB ってのは、一番下の桁って意味。 たとえば十進数で例えると 1234 の 4。 一の位 がLSB。
HSBってのは、一番上の桁って意味。 たとえば十進数で例えると 1234 の1。 千の位がHSB。
上の例だと4つの箱、つまり4ビットによる数です。4ビットだと0〜15までの数字を表せる。
この0〜15までを、一の位として扱えるのが16進数。 これは0〜9、そしてA、B、C、D、E、Fと続く。
Aは10という意味、Bは11という意味、Cは12という意味、Dは13という意味、Eは14、Fは15。
プログラムで書くときは頭に接頭詞で0xって書く。 0x0F と書いたら、これは 00001111 であり、15である。
話を char に戻すと、
char ってのは要するに ASCII コードの対応表を保存するに十分なサイズ。っていう目的の数で、
だから 8bit という大きさに決められたんだと思う。 (8bitは0〜255までの数字をあらわせる)
だから昔は 『char 型 = 文字を表せる』 の図式が成立してたんだろうけど、それは ASCII コードしか使わない国の人たちだけの話。
一方、日本だとひらがな、カタカナ、そして漢字があるからこまる。なにせ漢字は種類が多い。 とてもじゃないが 0 〜 255の範囲になんて収まらない。
日本語を表すための表として shift-jis を用意したものの、char の1バイトでは足りなかったのである。
だから、w_char 型なんてのを使ったりしてた。このw_charってのは、ようするにchar[2]と同じで、2バイトのサイズである。
2バイトあれば、2の16乗個まで表せる。つまり大体64キロ。6万4千くらい。厳密には65536種類。
もちろん、LSB側の7bitはASCIIと互換で、同じコード変換表になっているので、その兼ね合いで、実際には64000種類も対応は書けないのだけど、それでも char よりは、全然多くの文字を『指し示せる』
最近だと UTF-8というエンコードをよくみかける。
これはユニコードというコード体系の一種で、いわく「地球上の全ての言語の文字を表す」っていう理念のもとに作りはじめられた対応表。
さすがに、すべての言語っていうと、使用するバイト数も多そうなもんだけど、当初は2バイトでこと足りると思ってたらしい。
…西洋人の感覚だと 「せいぜい2、3万種類くらいじゃね? 地球上の文字って」って感覚だったのだろう…
で、とうぜん「あれ?っていうか足りなくね?」となり、結局一文字を3バイトというルールに変更し、その後、結局4バイトになった。
一文字4バイトだと、ASCII文字でこと足りてたラテン文字文化圏の人たちはたまらない。1バイトで記録できるものを、わざわざ4バイト。
これではこまるので、UTF-8は、使用するバイト数が可変長になっている。文字の種類によって、使用するバイト数が変わる。
これが、妙なとっつきにくさの原因なのだろうけど、実際、その判別方法は全然簡単である。フラグを見てくだけ。
UTF-8でも、先頭1バイトの7bit分は、ASCIIコードとまったく同じである。ASCIIは0〜127の数字を文字の対応表に使用してた。つまりHSBの 1bit が余ってる状態。これをフラグにして判断していくだけ。
具体的にはこんな感じ。ぜんぜん簡単である。なにも難しいことなんて無い。
<UTF-8の読み方>
で、本題のもどると、
基本型を char じゃなくて、このUTF-8型に決め打ちした CHAR 型にして作り直した方が良いような気がした。
…ので、CHAR 型を作ることにしました。
という、疑問。
本来の char の意味を考えたら、char を補完する新たな CHAR 型の必要性を感じた。そしてそれを基本型として組むべきではないのか?と。
もともと、C の char型とは、『キャラクタ型』という意味。 つまり、1つで1文字を表せる。そういう変数。
変数とは、中に数をメモしとくための箱。 そもそも、なぜ変数で文字を表せるのか?
実際のところ、char 自体に文字画像が入ってるワケじゃなくて、char に記録された数字は、あれは、単なる文字一覧表の中での、文字に対応した番号にすぎない。
ASCIIコード対応表
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
| 0 | NUL | DLE | SP | 0 | @ | P | ` | p |
| 1 | SOH | DC1 | ! | 1 | A | Q | a | q |
| 2 | STX | DC2 | " | 2 | B | R | b | r |
| 3 | ETX | DC3 | # | 3 | C | S | c | s |
| 4 | EOT | DC4 | $ | 4 | D | T | d | t |
| 5 | ENQ | NAK | % | 5 | E | U | e | u |
| 6 | ACK | SYN | & | 6 | F | V | f | v |
| 7 | BEL | ETB | ' | 7 | G | W | g | w |
| 8 | BS | CAN | ( | 8 | H | X | h | x |
| 9 | HT | EM | ) | 9 | I | Y | i | y |
| 10 | LF | SUB | * | : | J | Z | j | z |
| 11 | VT | ESC | + | ; | K | [ | k | { |
| 12 | FF | FS | , | < | L | \ | l | | |
| 13 | CR | GS | - | = | M | ] | m | } |
| 14 | SO | RS | . | > | N | ^ | n | ~ |
| 15 | SI | US | / | ? | O | _ | o | DEL |
この表の文字の数は、全部で合計は 16 * 8 で 128個。
つまり0〜127までの数字を表せる入れ物なら、十分。
| HSB | LSB | |
| □□□□□□□□ |
この箱の1つ1つが、それぞれ、0か1かのどちらかを記録できる。
この箱1つのことを1ビットと呼ぶ。
で、この箱を8個まとめたものが1バイト。
1メガバイトってのは、この棒が100万本使える状態。1ギガバイトってのは10億本使える状態。
この棒の、箱1つ1つは、0か1のどちらかしか記録できない。だから、箱1つに3とか5みたいな数字は記録できない。
数字は、この棒に2進数で記録する。
10進数だと、0、1、2、3、、、8、9 と来て、繰り上がって10になる。 で、11、12、13、、、と続いてく。
この10で繰り上がるってのが、2進数の場合2で繰り上がる。
つまり、0、1 と来たら、繰り上がって 10 となる。 で、11 で、また繰り上がって 100で、101、
また繰り上がって 110、で111、また繰り上がって 1000、で1001、また繰り上がって1010、で1011、でまた繰り上がって 1100、で1101、でまた繰り上がって1110、で1111。
わかりやすくイメージ描くと、こんな感じ
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
| LSB | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 |
| 1 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | |
| 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | |
| HSB | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
HSBってのは、一番上の桁って意味。 たとえば十進数で例えると 1234 の1。 千の位がHSB。
上の例だと4つの箱、つまり4ビットによる数です。4ビットだと0〜15までの数字を表せる。
この0〜15までを、一の位として扱えるのが16進数。 これは0〜9、そしてA、B、C、D、E、Fと続く。
Aは10という意味、Bは11という意味、Cは12という意味、Dは13という意味、Eは14、Fは15。
プログラムで書くときは頭に接頭詞で0xって書く。 0x0F と書いたら、これは 00001111 であり、15である。
話を char に戻すと、
char ってのは要するに ASCII コードの対応表を保存するに十分なサイズ。っていう目的の数で、
だから 8bit という大きさに決められたんだと思う。 (8bitは0〜255までの数字をあらわせる)
だから昔は 『char 型 = 文字を表せる』 の図式が成立してたんだろうけど、それは ASCII コードしか使わない国の人たちだけの話。
一方、日本だとひらがな、カタカナ、そして漢字があるからこまる。なにせ漢字は種類が多い。 とてもじゃないが 0 〜 255の範囲になんて収まらない。
日本語を表すための表として shift-jis を用意したものの、char の1バイトでは足りなかったのである。
だから、w_char 型なんてのを使ったりしてた。このw_charってのは、ようするにchar[2]と同じで、2バイトのサイズである。
2バイトあれば、2の16乗個まで表せる。つまり大体64キロ。6万4千くらい。厳密には65536種類。
もちろん、LSB側の7bitはASCIIと互換で、同じコード変換表になっているので、その兼ね合いで、実際には64000種類も対応は書けないのだけど、それでも char よりは、全然多くの文字を『指し示せる』
最近だと UTF-8というエンコードをよくみかける。
これはユニコードというコード体系の一種で、いわく「地球上の全ての言語の文字を表す」っていう理念のもとに作りはじめられた対応表。
さすがに、すべての言語っていうと、使用するバイト数も多そうなもんだけど、当初は2バイトでこと足りると思ってたらしい。
…西洋人の感覚だと 「せいぜい2、3万種類くらいじゃね? 地球上の文字って」って感覚だったのだろう…
で、とうぜん「あれ?っていうか足りなくね?」となり、結局一文字を3バイトというルールに変更し、その後、結局4バイトになった。
一文字4バイトだと、ASCII文字でこと足りてたラテン文字文化圏の人たちはたまらない。1バイトで記録できるものを、わざわざ4バイト。
これではこまるので、UTF-8は、使用するバイト数が可変長になっている。文字の種類によって、使用するバイト数が変わる。
これが、妙なとっつきにくさの原因なのだろうけど、実際、その判別方法は全然簡単である。フラグを見てくだけ。
UTF-8でも、先頭1バイトの7bit分は、ASCIIコードとまったく同じである。ASCIIは0〜127の数字を文字の対応表に使用してた。つまりHSBの 1bit が余ってる状態。これをフラグにして判断していくだけ。
具体的にはこんな感じ。ぜんぜん簡単である。なにも難しいことなんて無い。
<UTF-8の読み方>
| 先頭バイトの HSB側から1ビット目が 0なら1バイト文字(ASCII文字)、 1ならマルチバイト文字 | → 0だった | 1バイト文字 (ASCII文字) |
↓ 1だった | ||
| 先頭バイトの HSB側から2ビット目が 1なら、先頭以外のバイト 0なら、このバイトが、マルチバイト文字の先頭バイト | → 1だった | なんだか よくわかんねぇ 状態 |
↓ 0だった | ||
| 先頭バイトの HSB側から3ビット目が 0なら、このバイトと、次のバイトによる2バイト文字 1なら、3バイト以上の文字 | → 0だった | 2バイト文字 |
↓ 1だった | ||
| 先頭バイトの HSB側から4ビット目が 0なら、このバイトと、次のバイトと、次々のバイトの3バイト文字 1なら、このバイトから、次々々バイトまでの4バイト文字 | → 0だった | 3バイト文字 |
↓ 1だった | ||
4バイト文字 | ||
で、本題のもどると、
基本型を char じゃなくて、このUTF-8型に決め打ちした CHAR 型にして作り直した方が良いような気がした。
…ので、CHAR 型を作ることにしました。
| CHAR.h (0.0.5) |
| #ifndef _CHAR_H_ #define _CHAR_H_ typedef struct tagCHAR { unsigned char c[4]; int mode; } CHAR; // char[4] を読み込み UTF-8 に変換して格納する。 // リターンは使用してるバイト数。判別不能の時は0を返す。 extern int writeCHAR( CHAR* dst, unsigned char* src ); // char[4] に UTF-8 の先頭バイトから順に格納する。 // ただし、使用されるバイト数は、モードの必要分”だけ”であり、 // それ以降のバイトには一切触れない(値を変えない・アクセスすらしない) // 戻り値には、使用しているバイト数が返される。0の場合はほぼエラーを意味する。 extern int readCHAR( CHAR* src, unsigned char* dst ); #endif // _CHAR_H_ |
| CHAR.cpp (0.0.5) |
| #include <stdio.h> #include "CHAR.h" // char[4] を読み込み UTF-8 に変換して格納する。 // リターンは使用してるバイト数。判別不能の時は0を返す。 int writeCHAR( CHAR* dst, unsigned char* src ) { dst->mode = 0; unsigned char c0 = src[0]; unsigned char c1; unsigned char c2; unsigned char c3; // 先頭bitが1ならマルチバイト文字、0ならASCII文字 if( ( c0 & 0x80 ) == 0 ) { dst->c[0] = c0; dst->c[1] = 0; dst->c[2] = 0; dst->c[3] = 0; dst->mode = 1; } else { // 第2bitが0なら先頭以外の文字、1なら先頭文字 if ( ( c0 & 0x40 ) == 0 ) { } else { dst->c[0] = c0; // 第3bitが0なら2バイト文字、1なら3バイト以上の文字 if ( ( c0 & 0x20 ) == 0 ) { dst->c[1] = src[1]; dst->mode = 2; dst->c[2] = 0; dst->c[3] = 0; } else { // ...かつ、第4bitが0なら3バイト文字、1なら4バイト文字 if ( ( c0 & 0x10 ) == 0 ) { dst->c[1] = src[1]; dst->c[2] = src[2]; dst->mode = 3; dst->c[3] = 0; } else { dst->c[1] = src[1]; dst->c[2] = src[2]; dst->c[3] = src[3]; dst->mode = 4; } } } } return( dst->mode ); } // char[4] に UTF-8 の先頭バイトから順に格納する。 // ただし、使用されるバイト数は、モードの必要分”だけ”であり、 // それ以降のバイトには一切触れない(値を変えない・アクセスすらしない) // 戻り値には、使用しているバイト数が返される。0の場合はほぼエラーを意味する。 int readCHAR( CHAR* src, unsigned char* dst ) { switch(src->mode){ case 1: dst[0] = src->c[0]; return(1); case 2: dst[0] = src->c[0]; dst[1] = src->c[1]; return(2); case 3: dst[0] = src->c[0]; dst[1] = src->c[1]; dst[2] = src->c[2]; return(3); case 4: dst[0] = src->c[0]; dst[1] = src->c[1]; dst[2] = src->c[2]; dst[3] = src->c[3]; return(4); default: return(0); } } |
STRING メモ
char* 型での文字列操作だと、なんとなく微妙な部分で「どっちだっけか?」ってなったりしてややこしいので、STRING型として操作をまとめてしまおうと思った。
STRING は文字列を一行格納するためのもの。
STRINGに対しての行える操作は
write_char は STRING の x 文字目に1文字書き込むが、
この際の x が STRING長より長かった場合は、以下のような処理になる。
↓
*□には文字としてスペースが書き込まれる
このように不足分の領域が自動的に再確保され、
STRING の最大長が伸ばされる。(メモリの再確保が行われる)
その際、新たに確保した分のスキマはスペース(空白文字)で埋められる。
書き込んだ1文字以外の文字列は、全て保持される。(破壊されない)
write_string は、単純に char* 型による文字列を STRING にコピーするだけ。
その際に、書き込み位置は選べない。書き込み位置は常に先頭0番目から。
書き込み文字列は、char* と、その length で表現する。
STRING長よりも length が長い場合は、STRING長が伸ばされる(新たにメモリが確保される)
注:STRING.c には、文字列の最後に ¥0 をとくに書き込むような『気の使い方』は一切していないので、
直接読み取りをする場合は、そこを意識して注意するように。
具体的には strlen が望みどうりの値を返さないケースが起きたりする。
(なるべく read_ 系の関数を介して読み取る方がいい。その方が簡単だし安全)
↑
これは実際、どう組むべきか迷ってます。
へたに生データでの ¥0 を保証しちゃうと、関数を介さないでアクセスするクセがついてしまうのが怖い。
¥0 が保証されないのであれば、面倒だし、関数を介するだろう…と。
もしも関数を介さずにアクセスした際に、len を無視して文字列を伸ばしたり縮めたりされると、read_、write_ともに、関数の動作に支障がでてくる。
とにかく、STRING の生データには直接触らないっていう方針で使ってれば問題無い。
常に関数を介するように。
生データに直接書き込みするのは、なるべく避けるように。
read_char は、STRING の任意の位置から1文字読み込む。
戻り値として char
位置が 0 より小さい(マイナス)場合は0、位置がSTRING長以上の場合は STRING長 - 1 の位置から読み込む。
↓
read_str は STRING から char* に文字列を読み込みます。
読み込みは常に先頭0文字目からです。
読み込む文字列長は STRING の len 依存です。
つまり、char* には十分なメモリ領域を確保しとく必要があります。
(汎用的に使うバッファ変数を用いて読み込むケースがほとんどでしょうから、いっそのこと1メガくらい確保しといても良いかもしれません。最近のPCはメモリが有り余ってますし。)
あと、この関数で読み込んだ文字列には、文字列の最後に必ず \0 が付加されます。(\0 保証)
つまり、この関数を介してで読み込んだ文字列なら、 strlen() が必ず動作します。(正常に動作します)
注意: char* のメモリが STRING長に満たない場合の動作は不定です。 とにかく char* には十分すぎるメモリを確保しといてください。
read_str ボツ案1:
必要に応じて read_str 関数内部で malloc して、ポインタをリターンする方法なら、メモリを動的に確保できるので、メモリ領域の問題は解決するのですが、その方法だと、結果的に read_str が一種の malloc として動作するのと同じ意味です。
これだと、malloc に関わる free の問題があらわれてしまいます。
常にシステムでほとんど最初から最後まで存在し続けることを望まれるメモリならば、とくに開放に神経質になる必要はそれほどありませんが、read_str の場合、おそらく、読み込んだ文字列の用途は一時バッファ的な使い方がほぼ大部分のケースだと予想されます。
この手のメモリーってのは、どんどん無駄に溜まるので、使用後の開放が必須ですが、手動の開放作業ってのは、往々にしてを忘れてしまうことが多い。
なぜなら、開放してもしなくても、これといってエラーが発生するワケでもないし、コンパイルも当然通る。
そこが怖い。(この手のエラーは、ものすごく見付けづらい、直しづらい)
だから、read_str を malloc の一種として実装する案はボツにしました。
read_str ボツ案2:
STRING のプログラムブロックに、大域変数として char* 型の string_buf を用意する。
これをヘッダファイルで外部に公開して使えるようにする。
read_str は。この string_buf へとリード結果を書き込む。
そして、ユーザーは string_buf を読めば、常に、直近に実行した read_str の結果を得ることができる。
この方法だと、read_str というユニークなバッファを用意することで、両方の問題を解決できる。
現在案の、『メモリーを余分に確保しとかなくてはならない』という問題。
また、ボツ案1の、ゴミメモリー領域を、うっかり発生させてしまいかねない、という問題。
どちらも解決できる。
string_buf は、read_str関数が。完全に把握してコントロールすることができるバッファ。
だから動的にメモリーを確保してやれば、読み込みの問題は解決する。 うっかり忘れのゴミメモリ問題も無い。
でもこれは、デメリットとして、大域変数の問題があるのでボツにした。
つまり、関数内で完結させたい処理であっても、それが不可能である。というコーディング上の不安定要素が入り込んでしまう恐れがあるからボツ。
つまり、関数Aで使った結果が、関数Bで読むときにも影響する。 忘れたままBで『期待値』を頼りに string_buf を読むと、まったくおかしな値が入ってた…というケースが必ず起こりうる。
これを防止するためには、read_str 関数と、その結果の string_buf に対して行う作業を、必ずペアにして、それらの処理の間に、さらに再帰的に read_str 関数を使うなどの処理を挟み込まない。
この、『read_str と string_buf は、ペアで直後に使用すること』という制約が、意外とキツい。なぜなら再帰、もしくは再帰的なループ系の処理が、のきなみ、ほぼ全てアウトだから。再帰を書くためには、それなりのトリックなり用意が必要になってくる。
こういう複雑性を持ち込むのは、本末転倒だと思う。(元々、プログラムを簡単にするために STRING を用意してるのだからw)
なので、この大域変数でバッファを用意する案もボツにしました。
結局、現在の、『無駄に大きめのバッファを用意して使ってね』 というパラダイムが一番マシな気がしたので、これを採用してます。
これが一番、『妙な作法的なもの』を意識せずに使えて、簡単だと思ったからです。
…もっと良い方法があれば、それに書き換えるつもりですが、ボツ案1が本当は理想なんですが、これを安全に実装するには、やはりガベージコレクションが保証されてる言語じゃないと不安です…
(Cでボツ案1を組むと、いわば手動ガベージコレクションを人間に要求することと同じ意味。 でも、人間は間違えるし、うっかり忘れもする。)
STRING は文字列を一行格納するためのもの。
STRINGに対しての行える操作は
- 1文字書き込む : write_charSTRING()
- 1文字読み込む : read_charSTRING()
- 1行書き込む : write_strSTRING()
- 1行読み込む : read_strSTRING()
write_char は STRING の x 文字目に1文字書き込むが、
この際の x が STRING長より長かった場合は、以下のような処理になる。
| □ | |||||||||
| ↓ | |||||||||
| □ | □ | □ | □ | □ | □ | □ | □ | □ | □ |
| 0 | 1 | 2 | 3 | 4 | ... | ... | ... | ... | x |
| STRING長 = 5 | |||||||||
↓
| □ | □ | □ | □ | □ | □ | □ | □ | □ | □ |
| 0 | 1 | 2 | 3 | 4 | ... | ... | ... | ... | x |
| string長 = x + 1 | |||||||||
このように不足分の領域が自動的に再確保され、
STRING の最大長が伸ばされる。(メモリの再確保が行われる)
その際、新たに確保した分のスキマはスペース(空白文字)で埋められる。
書き込んだ1文字以外の文字列は、全て保持される。(破壊されない)
write_string は、単純に char* 型による文字列を STRING にコピーするだけ。
その際に、書き込み位置は選べない。書き込み位置は常に先頭0番目から。
書き込み文字列は、char* と、その length で表現する。
STRING長よりも length が長い場合は、STRING長が伸ばされる(新たにメモリが確保される)
注:STRING.c には、文字列の最後に ¥0 をとくに書き込むような『気の使い方』は一切していないので、
直接読み取りをする場合は、そこを意識して注意するように。
具体的には strlen が望みどうりの値を返さないケースが起きたりする。
(なるべく read_ 系の関数を介して読み取る方がいい。その方が簡単だし安全)
↑
これは実際、どう組むべきか迷ってます。
へたに生データでの ¥0 を保証しちゃうと、関数を介さないでアクセスするクセがついてしまうのが怖い。
¥0 が保証されないのであれば、面倒だし、関数を介するだろう…と。
もしも関数を介さずにアクセスした際に、len を無視して文字列を伸ばしたり縮めたりされると、read_、write_ともに、関数の動作に支障がでてくる。
とにかく、STRING の生データには直接触らないっていう方針で使ってれば問題無い。
常に関数を介するように。
生データに直接書き込みするのは、なるべく避けるように。
read_char は、STRING の任意の位置から1文字読み込む。
戻り値として char
位置が 0 より小さい(マイナス)場合は0、位置がSTRING長以上の場合は STRING長 - 1 の位置から読み込む。
| □ | |||||||||
| ↑ | |||||||||
| □ | □ | □ | □ | □ | □ | □ | □ | □ | □ |
| 0 | 1 | 2 | 3 | 4 | ... | ... | ... | ... | x |
| STRING長 = 5 | |||||||||
↓
| □ | |||||||||
| ↑ | |||||||||
| □ | □ | □ | □ | □ | □ | □ | □ | □ | □ |
| 0 | 1 | 2 | 3 | 4 | ... | ... | ... | ... | x |
| STRING長 = 5 | |||||||||
read_str は STRING から char* に文字列を読み込みます。
読み込みは常に先頭0文字目からです。
読み込む文字列長は STRING の len 依存です。
つまり、char* には十分なメモリ領域を確保しとく必要があります。
(汎用的に使うバッファ変数を用いて読み込むケースがほとんどでしょうから、いっそのこと1メガくらい確保しといても良いかもしれません。最近のPCはメモリが有り余ってますし。)
あと、この関数で読み込んだ文字列には、文字列の最後に必ず \0 が付加されます。(\0 保証)
つまり、この関数を介してで読み込んだ文字列なら、 strlen() が必ず動作します。(正常に動作します)
注意: char* のメモリが STRING長に満たない場合の動作は不定です。 とにかく char* には十分すぎるメモリを確保しといてください。
read_str ボツ案1:
必要に応じて read_str 関数内部で malloc して、ポインタをリターンする方法なら、メモリを動的に確保できるので、メモリ領域の問題は解決するのですが、その方法だと、結果的に read_str が一種の malloc として動作するのと同じ意味です。
これだと、malloc に関わる free の問題があらわれてしまいます。
常にシステムでほとんど最初から最後まで存在し続けることを望まれるメモリならば、とくに開放に神経質になる必要はそれほどありませんが、read_str の場合、おそらく、読み込んだ文字列の用途は一時バッファ的な使い方がほぼ大部分のケースだと予想されます。
この手のメモリーってのは、どんどん無駄に溜まるので、使用後の開放が必須ですが、手動の開放作業ってのは、往々にしてを忘れてしまうことが多い。
なぜなら、開放してもしなくても、これといってエラーが発生するワケでもないし、コンパイルも当然通る。
そこが怖い。(この手のエラーは、ものすごく見付けづらい、直しづらい)
だから、read_str を malloc の一種として実装する案はボツにしました。
read_str ボツ案2:
STRING のプログラムブロックに、大域変数として char* 型の string_buf を用意する。
これをヘッダファイルで外部に公開して使えるようにする。
read_str は。この string_buf へとリード結果を書き込む。
そして、ユーザーは string_buf を読めば、常に、直近に実行した read_str の結果を得ることができる。
この方法だと、read_str というユニークなバッファを用意することで、両方の問題を解決できる。
現在案の、『メモリーを余分に確保しとかなくてはならない』という問題。
また、ボツ案1の、ゴミメモリー領域を、うっかり発生させてしまいかねない、という問題。
どちらも解決できる。
string_buf は、read_str関数が。完全に把握してコントロールすることができるバッファ。
だから動的にメモリーを確保してやれば、読み込みの問題は解決する。 うっかり忘れのゴミメモリ問題も無い。
でもこれは、デメリットとして、大域変数の問題があるのでボツにした。
つまり、関数内で完結させたい処理であっても、それが不可能である。というコーディング上の不安定要素が入り込んでしまう恐れがあるからボツ。
つまり、関数Aで使った結果が、関数Bで読むときにも影響する。 忘れたままBで『期待値』を頼りに string_buf を読むと、まったくおかしな値が入ってた…というケースが必ず起こりうる。
これを防止するためには、read_str 関数と、その結果の string_buf に対して行う作業を、必ずペアにして、それらの処理の間に、さらに再帰的に read_str 関数を使うなどの処理を挟み込まない。
この、『read_str と string_buf は、ペアで直後に使用すること』という制約が、意外とキツい。なぜなら再帰、もしくは再帰的なループ系の処理が、のきなみ、ほぼ全てアウトだから。再帰を書くためには、それなりのトリックなり用意が必要になってくる。
こういう複雑性を持ち込むのは、本末転倒だと思う。(元々、プログラムを簡単にするために STRING を用意してるのだからw)
なので、この大域変数でバッファを用意する案もボツにしました。
結局、現在の、『無駄に大きめのバッファを用意して使ってね』 というパラダイムが一番マシな気がしたので、これを採用してます。
これが一番、『妙な作法的なもの』を意識せずに使えて、簡単だと思ったからです。
…もっと良い方法があれば、それに書き換えるつもりですが、ボツ案1が本当は理想なんですが、これを安全に実装するには、やはりガベージコレクションが保証されてる言語じゃないと不安です…
(Cでボツ案1を組むと、いわば手動ガベージコレクションを人間に要求することと同じ意味。 でも、人間は間違えるし、うっかり忘れもする。)
| STRING.h (0.0.5) |
| #ifndef _STRING_H_ #define _STRING_H_ typedef struct tagSTRING { char* c; int len; } STRING; #include <stdio.h> #include <stdlib.h> #include <string.h> #include "STRING.h" // 初期化 // インスタンスの作成直後に必ず最初にこの関数を通して初期化する必要がある。 extern void initSTRING( STRING* str ); // char 型の文字列を len 文字分、STRINGに書き込む。この際、古いデータは破壊され、上書きによって置き換わる。(つまり感覚としては char -> STRINGへのコピーに近い) // STRING のメモリーが足りない場合は自動的に確保される extern void write_strSTRING( STRING* dst, char* src, int len ); // char 1文字を dst の index 番目に書き込む。 // STRING のメモリーが足りない場合は自動的に確保される。確保された分はスペースで初期化される。 extern void write_charSTRING( STRING* dst, int index, char c ); // src の文字列を dst に書き込む。 // STRING のメモリーが足りない場合は自動的に確保される extern void write_stringSTRING( STRING* dst, STRING* src ); // char 1文字を src の index 番めから読み込む。 // index が 0 以下の場合は 0 として読み込まれる。 index が STRING長 - 1 以上の場合、STRING長 - 1 として読み込まれる extern char read_charSTRING( STRING* src, int index ); // char 型の文字列 dst へ、STRING型 src から全文字を読み込む。 読み込んだ文字列の最後には必ず '¥0' が付加される。 // // 注意:dst は十分すぎるほど十分な長さを確保しておくように。 //(Cにはガベージコレクションが無いので、使いやすくするために、あえて固定長配列の dst を引数にしてます。 // 開放忘れによるメモリの行方不明を防ぐため、char* を返す関数として内部で malloc するような作りにはあえてしてません。 // だからdstは十分な長さを確保して渡してください。 // 0xFFFFFF (16メガw)くらいあれば、まぁ、絶対大丈夫だと思います。(一行の長さが1600万文字のテキストファイルなんて、めったに存在しませんもんね...super_piの出力くらい?) // 0xFFFF (64キロ)くらいでも、実用、十分かと思いますが。(一行の長さが6万4千文字のテキストファイル… まぁこれでも十分ですよね… 普通、一行は100文字程度でしょうし。) extern void read_strSTRING( STRING* src, char* dst ); #endif // _STRING_H_ |
| STRING.cpp (0.0.5) |
| #include <stdio.h> #include <stdlib.h> #include <string.h> #include "STRING.h" // 初期化 // インスタンスの作成直後に必ず最初にこの関数を通して初期化する必要がある。 void initSTRING( STRING* str ) { str->c = (char*)malloc(sizeof(char)); str->len = 0; } // char 型の文字列を len 文字分、STRINGに書き込む。この際、古いデータは破壊され、上書きによって置き換わる。(つまり感覚としては char -> STRINGへのコピーに近い) // STRING のメモリーが足りない場合は自動的に確保される void write_strSTRING( STRING* dst, char* src, int len ) { if( len <= 0 ) { len = 0; } // len の範囲丸め // メモリが足りなかったら確保する if( dst->len < len ) { dst->len = len; free( (void*)(dst->c) ); dst->c = (char*)malloc( sizeof(char) * dst->len ); } int i; char* d = dst->c; char* s = src; for(i=0;i<len;i++){ *d++ = *s++; } dst->len = len; } // char 1文字を dst の index 番目に書き込む。 // STRING のメモリーが足りない場合は自動的に確保される。確保された分はスペースで初期化される。 void write_charSTRING( STRING* dst, int index, char c ) { if( index <= 0 ) { index = 0; } // index の範囲丸め // メモリが足りなかったら確保する // index の場合は、たとえば len が10の配列では、実際に使用できるindexの最大値は9までなので、( dst->len - 1 ) と比較している if( dst->len - 1 < index ) { int old_len = dst->len; dst->len = index + 1; // len を index の値とした配列だと、index - 1 番目までしか書き込めないので、それだと1足りないので ( index + 1 ) としている。 dst->c = (char*)realloc( dst->c, sizeof(char) * dst->len ); int i; char* d = dst->c; for(i=old_len; i<dst->len; i++ ) { *d++ = ' '; // 新規確保したメモリーはスペースで初期化する } } dst->c[index] = c; } // src の文字列を dst に書き込む。 // STRING のメモリーが足りない場合は自動的に確保される void write_stringSTRING( STRING* dst, STRING* src ) { write_strSTRING( dst, src->c, src->len ); } // char 1文字を src の index 番めから読み込む。 // index が 0 以下の場合は 0 として読み込まれる。 index が STRING長 - 1 以上の場合、STRING長 - 1 として読み込まれる char read_charSTRING( STRING* src, int index ) { if( index <= 0 ) { index = 0; } else if( index >= src->len - 1 ) { index = src->len - 1; } // index の範囲丸め return( src->c[index] ); } // char 型の文字列 dst へ、STRING型 src から全文字を読み込む。 読み込んだ文字列の最後には必ず '¥0' が付加される。 // // 注意:dst は十分すぎるほど十分な長さを確保しておくように。 //(Cにはガベージコレクションが無いので、使いやすくするために、あえて固定長配列の dst を引数にしてます。 // 開放忘れによるメモリの行方不明を防ぐため、char* を返す関数として内部で malloc するような作りにはあえてしてません。 // だからdstは十分な長さを確保して渡してください。 // 0xFFFFFF (16メガw)くらいあれば、まぁ、絶対大丈夫だと思います。(一行の長さが1600万文字のテキストファイルなんて、めったに存在しませんもんね...super_piの出力くらい?) // 0xFFFF (64キロ)くらいでも、実用、十分かと思いますが。(一行の長さが6万4千文字のテキストファイル… まぁこれでも十分ですよね… 普通、一行は100文字程度でしょうし。) void read_strSTRING( STRING* src, char* dst ) { char* d = dst; char* s = src->c; int len = src->len; int i; for(i=0;i<len;i++){ *d++ = *s++; } switch( dst[len-1] ) { case '\0':break; default: dst[len] = '\0'; } } |
CHAR_LIST メモ
char型の変数1つを格納するための箱
0 <-> back□next <-> back□next <-> back□next <-> ... <-> back□next <-> 0
こういうイメージ。
四角い箱と、左右のback,next が1セット。これが1文字を格納するための入れ物。
next には、次の箱の位置を表す住所。 back には、手前の箱の位置を表す住所。
一番最初の箱には、それよりも手前に箱が無いので、backに書く住所が無いので、 back には 0 と書く。
back が 0 なら先頭の箱という意味。そういうルールに決めた。
同様に、一番最後の箱には、next に 0 と書く。
next が 0 なら最後の箱という意味。そういうルールに決めた。
この箱1個を表す構造が、CHAR_SEED。
next は、次の CHAR_SEED の住所をメモするため。
back には手前の CHAR_SEED の住所をメモするため。
c には、記録したい文字。1文字分のメモリー。
このリスト全体の長さと、リストで扱う箱の位置を記録しとく場所が CHAR_LIST
cs_top は、先頭の0番目の箱の住所をメモしておくために用意した変数。
cs_top
↓
0 <-> back□next <-> back□next <-> back□next <-> ... <-> back□next <-> 0
0番目 1番目 2番目 n番目
先頭の箱の住所が分かれば、先頭からx番目の住所も分かる。
それぞれの next の場所へと、順々に数を数えながら飛んでいけば分かる。
たとえば3番目の箱の文字を見たければ、3番目の箱の位置を知るために、next を順番にたどってけばよい。
cs_top = 0番目の場所 / 今は0番目 (0文字目)
↓
0番目のnext = 1番目の場所 / 今は1番目 (1文字目)
↓
1番目のnext = 2番目の場所 / 今は2番目 (2文字目)
↓
2番目のnext = 3番目の場所 / 今は3番目 (3文字目)
↓
3番目の c = 格納されてる1文字
これで詠んだり書いたりすればいい。
…以上。そういう仕組みで動かそうとイメージして書いてます。
あと、まだコードに書いてないけど、
1文字を、リストのどこかに挿入する仕組みも書きたい。
0 <-> back□next <-> back□next <-> ... <-> back□next <-> 0
↓
back□next
↑ ↑
↓ ↓
0 <-> back□next back□next <-> ... <-> back□next <-> 0
↓
0 <-> back□next <-> back□next <-> back□next <-> ... <-> back□next <-> 0
普通に配列として文字列を扱う方法だと、この挿入のコストが、文字列長に比例して増えてしまう。
たとえば、100文字の文字列があったとして、3文字目に新しく文字を追加したい場合、配列を使ってるケースだと、3文字目以降を全て次の箱に書く(移動)する必要が出てくる。
10文字程度の短い文字列なら気にならないが、100文字、1000文字になってくると、無視できない処理の重さになってくる。
一方、各箱に next と back を覚えさせる方法なら、100文字の文字列の3文字目に1文字追加する場合でも、必要な操作は2文字目、新3文字目、新4文字目(旧3文字目)の、この3つの箱のリンクの繋ぎ直しだけで済む。
その代償として、文字列として出力する際に、
単純な配列なら、単純に配列を順番に読み込むだけで済むところを、いちいち一文字一文字リンクを調べながら読む必要が出てくる。
でも、これは微々たるコストだと思う。 得られるメリットから考えれば。
あと、文字の挿入に関して、文字列長を大きく越えた場所にも挿入できるようにしたい。
これはもぅコードに書いた。(けど、まだ仕組みとして荒削りなのでもっと練る必要があるかも…)
たとえば、長さが100文字の文字列があって、104文字目に文字を挿入したい場合
back□next <-> 0
100文字目
↓
back□next <-> back□next <-> back□next <-> back□next <-> back□next <-> 0
100文字目 101文字目 102文字目 103文字目 104文字目
100文字目の next には 101番目をリンクしたい。
104文字目の back には103文字目をリンクしたい。という状態になりますが、
しかし、その箱は存在しません。(文字列の箱は、もともと100文字までしか用意されてなかったから)
そういう場合は、101〜103番目の箱を、新たにメモリーから確保して用意します。
そして、その新たな箱の中には、文字として空白文字(スペース)を記録します。
0 <-> back□next <-> back□next <-> back□next <-> ... <-> back□next <-> 0
こういうイメージ。
四角い箱と、左右のback,next が1セット。これが1文字を格納するための入れ物。
next には、次の箱の位置を表す住所。 back には、手前の箱の位置を表す住所。
一番最初の箱には、それよりも手前に箱が無いので、backに書く住所が無いので、 back には 0 と書く。
back が 0 なら先頭の箱という意味。そういうルールに決めた。
同様に、一番最後の箱には、next に 0 と書く。
next が 0 なら最後の箱という意味。そういうルールに決めた。
この箱1個を表す構造が、CHAR_SEED。
next は、次の CHAR_SEED の住所をメモするため。
back には手前の CHAR_SEED の住所をメモするため。
c には、記録したい文字。1文字分のメモリー。
このリスト全体の長さと、リストで扱う箱の位置を記録しとく場所が CHAR_LIST
cs_top は、先頭の0番目の箱の住所をメモしておくために用意した変数。
cs_top
↓
0 <-> back□next <-> back□next <-> back□next <-> ... <-> back□next <-> 0
0番目 1番目 2番目 n番目
先頭の箱の住所が分かれば、先頭からx番目の住所も分かる。
それぞれの next の場所へと、順々に数を数えながら飛んでいけば分かる。
たとえば3番目の箱の文字を見たければ、3番目の箱の位置を知るために、next を順番にたどってけばよい。
cs_top = 0番目の場所 / 今は0番目 (0文字目)
↓
0番目のnext = 1番目の場所 / 今は1番目 (1文字目)
↓
1番目のnext = 2番目の場所 / 今は2番目 (2文字目)
↓
2番目のnext = 3番目の場所 / 今は3番目 (3文字目)
↓
3番目の c = 格納されてる1文字
これで詠んだり書いたりすればいい。
…以上。そういう仕組みで動かそうとイメージして書いてます。
あと、まだコードに書いてないけど、
1文字を、リストのどこかに挿入する仕組みも書きたい。
0 <-> back□next <-> back□next <-> ... <-> back□next <-> 0
↓
back□next
↑ ↑
↓ ↓
0 <-> back□next back□next <-> ... <-> back□next <-> 0
↓
0 <-> back□next <-> back□next <-> back□next <-> ... <-> back□next <-> 0
普通に配列として文字列を扱う方法だと、この挿入のコストが、文字列長に比例して増えてしまう。
たとえば、100文字の文字列があったとして、3文字目に新しく文字を追加したい場合、配列を使ってるケースだと、3文字目以降を全て次の箱に書く(移動)する必要が出てくる。
10文字程度の短い文字列なら気にならないが、100文字、1000文字になってくると、無視できない処理の重さになってくる。
一方、各箱に next と back を覚えさせる方法なら、100文字の文字列の3文字目に1文字追加する場合でも、必要な操作は2文字目、新3文字目、新4文字目(旧3文字目)の、この3つの箱のリンクの繋ぎ直しだけで済む。
その代償として、文字列として出力する際に、
単純な配列なら、単純に配列を順番に読み込むだけで済むところを、いちいち一文字一文字リンクを調べながら読む必要が出てくる。
でも、これは微々たるコストだと思う。 得られるメリットから考えれば。
あと、文字の挿入に関して、文字列長を大きく越えた場所にも挿入できるようにしたい。
これはもぅコードに書いた。(けど、まだ仕組みとして荒削りなのでもっと練る必要があるかも…)
たとえば、長さが100文字の文字列があって、104文字目に文字を挿入したい場合
back□next <-> 0
100文字目
↓
back□next <-> back□next <-> back□next <-> back□next <-> back□next <-> 0
100文字目 101文字目 102文字目 103文字目 104文字目
100文字目の next には 101番目をリンクしたい。
104文字目の back には103文字目をリンクしたい。という状態になりますが、
しかし、その箱は存在しません。(文字列の箱は、もともと100文字までしか用意されてなかったから)
そういう場合は、101〜103番目の箱を、新たにメモリーから確保して用意します。
そして、その新たな箱の中には、文字として空白文字(スペース)を記録します。
| CHAR_LIST.h (0.0.4) |
| #ifndef _CHAR_LIST_H_ #define _CHAR_LIST_H_ typedef struct tagCHAR_SEED { char c; struct tagCHAR_SEED* next; struct tagCHAR_SEED* back; } CHAR_SEED; typedef struct tagCHAR_LIST{ CHAR_SEED* cs_top; int list_len; } CHAR_LIST; extern void initCHAR_LIST( CHAR_LIST* cl, int len ); // LISTを配列としてイメージした場合の、配列の添え字を意味する。 0番からはじまる。 // // 配列のLIST長よりも添え字が大きかった場合( next = 0 の状態)の場合、最後の要素を返す。 // また、インデックスがマイナスの場合は、最初の要素を返す。 extern CHAR_SEED* seekCHAR_LIST( CHAR_LIST* cl, int index ); // cs_top を先頭と考えて、index 番目に一文字書き込み // CHAR_LISTのメモリー領域が足りない場合は、自動的に確保される。 extern void writeCHAR_LIST( CHAR_LIST* cl, int index, char c ); // cs_top を先頭と考えて、index 番目から一文字読み込み extern char readCHAR_LIST( CHAR_LIST* cl, int index ); // メモリー確保領域を伸ばす // 最後尾に len 分だけメモリーを追加する // 確保した領域の空白部はスペースで初期化される extern void stretchCHAR_LIST( CHAR_LIST* cl, int len ); // cs_top を先頭と考えて、index 番目から文字列を len 文字書き込み。 // CHAR_LISTのメモリー領域が足りない場合は、自動的に確保される。 extern void write_strCHAR_LIST( CHAR_LIST* cl, int index, char* str, int len ); // cs_top を先頭と考えて、index 番目から文字列を len 文字読み込み // CHAR_LIST の最後尾よりも以降を読み込む指定がされた場合は、最後尾までを読み込んで終わりにする。 extern void read_strCHAR_LIST( CHAR_LIST* cl, int index, char* str, int len ); #endif // _CHAR_LIST_H_ |
| CHAR_LIST.cpp (0.0.4) |
| #include <stdio.h> #include <stdlib.h> #include "CHAR_LIST.h" void initCHAR_LIST( CHAR_LIST* cl, int len ) { if (len < 1 ) { printf("err initCHAR_LIST() len¥n"); exit(1); } cl->cs_top = (CHAR_SEED*)malloc( sizeof(CHAR_SEED) ); cl->cs_top->back = 0; cl->cs_top->next = 0; cl->cs_top->c = ' '; cl->list_len = 1; stretchCHAR_LIST( cl, len ); } // メモリー確保領域を伸ばす // 最後尾に len 分だけメモリーを追加する // 確保した領域の空白部はスペースで初期化される void stretchCHAR_LIST( CHAR_LIST* cl, int len ) { size_t size = sizeof(CHAR_SEED) * len; CHAR_SEED* cur = seekCHAR_LIST( cl, cl->list_len - 1 ); CHAR_SEED* next; int i; for ( i = 0; i < len; i++ ) { cur->next = (CHAR_SEED*)malloc(size); cur->next->c = ' '; next = cur->next; next->back = cur; cur = next; cl->list_len++; } cur->next = 0; } // LISTを配列としてイメージした場合の、配列の添え字を意味する。 0番からはじまる。 // // 配列のLIST長よりも添え字が大きかった場合( next = 0 の状態)の場合、最後の要素を返す。 // また、インデックスがマイナスの場合は、最初の要素を返す。 CHAR_SEED* seekCHAR_LIST( CHAR_LIST* cl, int index ) { int i; CHAR_SEED* cur = cl->cs_top; if (index < 0 ) { return( cur ); } for(i=0;i<index;i++){ if( cur->next != 0 ) { cur = cur->next; } else { i = index; } } return( cur ); } // cs_top を先頭と考えて、index 番目に一文字書き込み // CHAR_LISTのメモリー領域が足りない場合は、自動的に確保される。 void writeCHAR_LIST( CHAR_LIST* cl, int index, char c ) { int dif = index - (cl->list_len - 1); if ( dif > 0 ) { stretchCHAR_LIST( cl, dif ); } CHAR_SEED* cs = seekCHAR_LIST( cl, index ); cs->c = c; } // cs_top を先頭と考えて、index 番目から一文字読み込み char readCHAR_LIST( CHAR_LIST* cl, int index ) { CHAR_SEED* cs = seekCHAR_LIST( cl, index ); return( cs->c ); } // cs_top を先頭と考えて、index 番目から文字列を len 文字書き込み // CHAR_LISTのメモリー領域が足りない場合は、自動的に確保される。 void write_strCHAR_LIST( CHAR_LIST* cl, int index, char* str, int len ) { if( ( index + len ) > ( cl->list_len - 1 ) ) { stretchCHAR_LIST( cl, ( index + len ) - cl->list_len ); } CHAR_SEED* cur = seekCHAR_LIST( cl, index ); int i; for(i = 0; i < len; i++ ) { cur->c = str[i]; cur = cur->next; } } // cs_top を先頭と考えて、index 番目から文字列を len 文字読み込み void read_strCHAR_LIST( CHAR_LIST* cl, int index, char* str, int len ) { CHAR_SEED* cur = seekCHAR_LIST( cl, index ); int dif = ( index + len ) - cl->list_len; if ( dif > 0 ) { len -= dif; } int i; for(i = 0; i < len; i++ ) { str[i] = cur->c; cur = cur->next; } str[len] = '¥0'; } |
R(仮)さんのアサシンなおんなのひとうろおぼえ

何がありがたいって、絵チャで描いてると度々ピクシブのアドレス聞かれることと、
無いって言うと誘ってくれる方がいてくれるという、この、なんていうか、やっぱありがたいなぁ・・・とつくづく感じる。理屈抜きで。直感的に。
ありがたい。
こぅ、ほんと、ほんの数人居るか居ないかだろうけど、ボクの絵を見て「わーい♪」って思ってくれる人が、ボクのほかにも居てくれるってことは、ほんとに、ほんとに、ありがたい。
こんなに嬉しいことは他に無い。
そういう人にもっと喜んでもらえるように、もっともっと上手くなりたいって、心のそこから思う。
こういう感覚って、大切にしないとなぁ…って思う。
最下層なんか見てるとえっぢさんとか、ほんと、見てるこっちも心がすさむ。
あんな場所とはやっぱり離れた方がいい。
何年か見てて、すっかりアノ空気を自然だと勘違いしてたけど、やっぱりあそこは異常だ。
本当に絵が好きな人たちの集まってる場所は、やっぱり全然違う。前向きだし、お互いが奉仕精神にあふれてるし、競争はしてもけしてドロドロとはしてない。
いい絵にはいいって素直に感想言うし、それが普通なんだ。
絵は楽しい。
というか、絵描きと遊ぶのは楽しい。
あらためて思った。刺激の量がけた違い。

何がありがたいって、絵チャで描いてると度々ピクシブのアドレス聞かれることと、
無いって言うと誘ってくれる方がいてくれるという、この、なんていうか、やっぱありがたいなぁ・・・とつくづく感じる。理屈抜きで。直感的に。
ありがたい。
こぅ、ほんと、ほんの数人居るか居ないかだろうけど、ボクの絵を見て「わーい♪」って思ってくれる人が、ボクのほかにも居てくれるってことは、ほんとに、ほんとに、ありがたい。
こんなに嬉しいことは他に無い。
そういう人にもっと喜んでもらえるように、もっともっと上手くなりたいって、心のそこから思う。
こういう感覚って、大切にしないとなぁ…って思う。
最下層なんか見てるとえっぢさんとか、ほんと、見てるこっちも心がすさむ。
あんな場所とはやっぱり離れた方がいい。
何年か見てて、すっかりアノ空気を自然だと勘違いしてたけど、やっぱりあそこは異常だ。
本当に絵が好きな人たちの集まってる場所は、やっぱり全然違う。前向きだし、お互いが奉仕精神にあふれてるし、競争はしてもけしてドロドロとはしてない。
いい絵にはいいって素直に感想言うし、それが普通なんだ。
絵は楽しい。
というか、絵描きと遊ぶのは楽しい。
あらためて思った。刺激の量がけた違い。
らくがき
私的Emacs使い方メモ(覚え書き)
ウインドウズのメモ帳に対応した操作方法
| ファイル操作関連 | |
| Ctrlを押したまま x f | ファイルを開く |
| Ctrlを押したまま x w | 名前をつけて保存 |
| Ctrlを押したまま x s | 上書き保存 |
| カット&ペースト関連 | |
| Ctrlを押したまま @ | 範囲選択開始 |
| Ctrlを押したまま w | カット |
| Ctrlを押したまま y | ペースト |
| 取り消し関連 | |
| Ctrlを押したまま u | アンドゥ |
| Ctrlを押したまま g | ESCキー的な意味合い (コマンドを入力途中でキャンセルしたい時など) |
| 文字の検索関連 | |
| CtrlとAltを押したまま s → 検索したい文字 | 文字の検索 |
| 文字の検索中に Ctrl押したまま s | 次の検索文字の場所へ (下方向) |
| 文字の検索中に Ctrl押したまま r | 次の検索文字の場所へ (上方向) |
| 文字の置きかえ関連 | |
| Altを押したまま x → query-replace-regexp と入力 → 検索したい文字 → 置き換える文字 | 文字の置換 (正規表現が使える) |
AltとShiftを押したまま% | 文字の置換 (正規表現が使えない) |
| 文字の置換中に y | 文字を置換して 次の検索文字の場所へ (下方向) |
| 文字の置換中に n | 文字を置換しない 次の検索文字の場所へ (下方向) |
| 文字の置換中に ^ | 一つ手前の検索位置に戻る (上方向) |
| 文字の置換中に ! | いっきに全部置換 |
| 検索と置換には正規表現が使えます | |
| . | 改行文字以外の、任意の1文字を表します。 |
| * | 前にある文字の繰り返しを意味します。 |
| ? | 前の文字を1回繰り返します。 |
| [] | []内に書かれた文字に対応します。 例 [ABCD] ←AかBかCかDを表す |
| [^] | []内に書かれた文字以外に対応します。 例 [^ABCD] ←AかBかCかD以外を表す |
| ^ | 行の先頭を意味する 例 ^ABCD ←行の先頭にあるABCDを検索しろという意味 |
| $ | 行の最後を意味する 例 ABCD$ ←行の最後にあるABCDを検索しろという意味 |
| \ | 特殊文字を文字そのものとして扱う。 c言語のprintfの¥と同じ意味 (場所はキーボードの『ろ』の位置です) |
bmp2sr-0.2.0 ばぐ
PIXEL.h の PIXEL4クラスの
deq メソッド。 a /= a じゃなくて this->a /= a に直しといてください。
あと BITMAP.cpp の open_new_file メソッド内の、BITMAP_INFO_HEADER か BITMAP_FILE_HEADER 、どっちか忘れたけど、なんたらSIZE みたいなメンバ変数。 =0になってるから、そこ、全体サイズ - ヘッダサイズした数値入れるように書きなおしといてください。
そのうち直したら上げときます…w
deq メソッド。 a /= a じゃなくて this->a /= a に直しといてください。
あと BITMAP.cpp の open_new_file メソッド内の、BITMAP_INFO_HEADER か BITMAP_FILE_HEADER 、どっちか忘れたけど、なんたらSIZE みたいなメンバ変数。 =0になってるから、そこ、全体サイズ - ヘッダサイズした数値入れるように書きなおしといてください。
そのうち直したら上げときます…w
bmp2sr-0.2.x
C++で全面的に書き直しました。
効率は一切無視して、あえて馬鹿正直にオブジェクト指向で組んでます。ピクセル回りの扱いを。
はっきり言って、Cで行き当たりばったりに書いてた旧バージョンの方が何倍も速く動作します…w
使い方:
bmp2sr 変換元ファイル名.bmp 変換後ファイル名.bmp
(変換できるのは標準的な24bitビットマップ or 32bitビットマップのみです。ファイルの書き順間違えると、ファイルが消滅しちゃうんで注意してください。(バックアップしといた方が安全です))
バイナリ:
windows用:bmp2sr-020win.txt
(exe形式をブログにアップロードできないので、txtと書き換えてあります。.exeと書き直して使ってね。)
linux用:ソースからコンパイルしてください。
ソース:bmp2sr-020.txt
(zip形式をブログにアップロードできないので、txtと書き換えてあります。.zipと書き直して使ってね。)
ソースからのコンパイル方法:
linuxの場合: gcc -o bmp2sr -c PIXEL.c PIXEL_LINE.c BITMAP_INFO_HEADER.c BITMAP_FILE_HEADER.c BITMAP.c -lstdc++
windowsの場合:visualStudioのコンソールプロジェクトにソースを読み込んでコンパイルボタンを押す。(fopen関数でワーニングがでますけど通ります。気になる人はfopen_s関数に置き換えて書き直せばワーニングは出なくなります)
余談:
Cの時は一つのソースファイルにまとめても手に負える程度の、短いコードでしたが、C++でバカ正直に多態性を意識して作ったら、同じようなことやるのにアホみたいに膨大なコード量になってしまって、ほとほと呆れました。(orz
C++で書いてると、いつも思うんですけど、そもそも簡単にするのが目的なのに、そのために膨大さという複雑性を持ち込んでしまってるかのような、ある種の本末転倒っぷりを感じることが多いです。ボクは設計がヘタで抽象度が低いおかげでこれでもまだ短いコードに落ち着いてますが、たとえば基本型を包括するvariable型みたいなのを導入しようとしたならば、それこそ、さらに膨大に…(苦笑。 最初、variable型を作るところからやるつもりだったんですが、(演算子の定義からすべてやり直して)なんだかバカバカしくなってそれは断念しました(笑) …魔が差したらやるかもしれません(やめとけって…www
というか、やっぱりC++はどうにも好きになれないです。いや、おもしろいとは思いますけど。
C++で描いてると、どうしても多態性によって実現する方の設計を選択してしまって、どうにも本末転倒な複雑さと冗長性が入り込んできてしまって、ボクみたいに素人が遊びで書く分にはパズル的でおもしろくはあるのですが、もしこれを仕事にしてる人だったら、やっぱCで書くだろうなぁ〜…って気がする。
実際、本当に多態性を意識して描くと、コードがいちいち大げさになるし、「想定外の場合」ってのに対して必要以上に気を遣う感じで、本筋以外にやることが増えてしまって、どうにも進みが悪いような気がする。 それを嫌って、C++で書いてるにも関わらず、単なるベターCとして使ってる人って意外に多いのではないだろうか…たぶん。
かと言って、C++の言語機能をトリッキーな歪曲した使用法でC的コーディングに含ませたりすると、すぐカオスな依存関係でめちゃくちゃになるし(フレンドみたいな抜け道がいくらでも用意されてるから、いくらでも誤用(オブジェクト指向的ではない使い方)できてしまう)だったら、なまじC++なんか使わないで無難にCで書いた方がマシにすら思えたりもする…
コード解説:
PIXEL.cpp / PIXEL.h
ピクセルには32bitと24bitの2種類を扱い分けるのですが、ここをオブジェクト指向の多態性で共通化してしまおうという魂胆で、PIXELを純粋仮想クラスとして定義し、その継承として実際の具象クラス PIXEL3, PIXEL4 を定義してます。
ピクセルに対しての定義は「操作はリードとライト、そして四則演算が可能」という点のみです。
PIXELから派生して作られたクラスには、すべてこの規則が適用されます。またこれら派生クラスのインスタンスは全て、例外なくPIXEL型のポインタとすることができ、当然メソッドは共通してるので方を意識せずに実行時に動的に型による分岐を”自動的”に行わせられます。簡単にいうと、これが多態性です。(…だと理解してます。ボクは。 専門家じゃないので間違っててもすみませんw)
要するに、実を言うと、switch文で必要に応じて処理を分岐させるようなコードを人間が書けばいいところを、かっこつけて自動的にやらせるような仕組みに仕立て上げるのが多態性の肝どころだと思います。こんなの、ほんとは人間が分岐を書いてやればいいだけのことだと思う。 静的な型結合で十分だと思う。というか、むしろ逆にボクには実行時型結合で”なければならない”というケースがどうにも思いつかないです。 分岐書かなくていいからラクだ、コードもすっきりする、ってことくらい? いや、ほんとに。思いつかない。
PIXEL_LINE.cpp / PIXEL_LINE.h
PIXELを一次元配列として扱うためのクラスです。たぶん、こういうのをコンテナって言うのかもしれません、実はコンテナの定義ってよく知らないんですが…w
pixel は、PIXEL型のポインタを一次元配列にした状態です。ほんとはこれすらもヒープから持ってくるべきでしょうけど、スタックに置いてあります。ボクが単にPIXELポインタ型の配列をnewする”文法を知らなくて書けなかった”だけです。(あざ笑っていいよ
PIXEL型のポインタの配列があって、その一つ一つにコンストラクタ時にヒープから一個一個インスタンスを吊り下げてあります。配列の実態がメモリ的に連続してるとは限りませんし、連続してないような拡張も大丈夫だと思います。(いや、実際は連続してるのでしょうけど…w)
pixel_bufは、内部処理用に1ピクセルだけ格納できるバッファーです。どうしてこんなモノを用意してあるかというと、
read_pixel と write_pixel() との間に、バッファーを介することを人間に強制したかったからです。
PIXEL に演算子の = を再定義して代入可能にして、read_pixel(void) という設計にすることも可能ですが、これだと read_pixel 内でリードした結果を新たにコピーコントラスタで用意して返す必要がでてくると思いました(なぜなら、リードした値は操作するのだろうし、そうさするならポインタで連動しててはまずいから、あらたな実体を別に生成する必要があるけど、その生成のための型情報は多態性のため不明(常にPIXEL*であることしか分からない、つまり具象であるPIXEL3かPIXEL4かは不明。)なので、read_pixel() 内でにて PIXEL* に対して新たにバッファを作るための型情報を得られなくて難儀します。それをバッファーなしで、直接PIXEL*型をリターンする形で read_pixel()を定義したのなら、渡されたread_pixel内で、渡されたPIXEL*へぶら下げられたインスタンスのメモリを、最終的に関数内で開放する必要があります(でなければ、使われないゴミメモリがどんどん増えてく)。しかし、write_pixelには、当然、開放してほしくないPIXEL型も渡されますが、この『開放してほしい、ほしくない』の判断をする方法がありません。 あるのは、常に開放するか、常に開放しないか。だと思ったので、なら開放しない設計にしよう。と。その場合、汎用のPIXEL型のバッファーが一つあるだけで解決できるな…という理由で、pixel_bufが用意されてます。
これも、本当はC的に、単純にフラグなどで泥臭く人間的にやれば、こんなのは必要無いのだろうな…と思います。
ただ、多態性が実現されてるクラスを、フラグによって分岐させるような使い方を一ヶ所してしまうと、もぅ、そこが原因で多態性の破れ(多態性を信用しきれない箇所、少なくともここ以降は確実にそうなる)が起こってしまうので、むやみにフラグ化できないという悩みもあるとおもいます。
だから多態性を維持するために、いろいろ仕組みを練り出すのですが、それが返ってプログラムを難解にしてしまったりするわけで、多態性のそもそもの目的である『プログラムを簡単にする、読みやすくする』為という目的が本末転倒的になったりして、まぁ、世の中「こっちを立てれば、あっちが立たず」的な、そういう側面って何にでもあるのだなぁ…とw 賢者の石みたいな万能薬って、そんなうまい話はなかなか無いもんだなぁ…とか、しみじみ思ってしまいます。
BITMAP_INFO_HEADER.c / BITMAP_INFO_HEADER.h
BITMAP_FILE_HEADER.c / BITMAP_FILE_HEADER.h
BMPのヘッダを読んで格納しとくだけです。どこにも依存したくなかったので、処理は完結してます。だから、わざわざファイルを開いて、また自分で閉じてます。
BITMAP.cpp BITMAP.h
ビットマップファイルから任意の1行読む、1行描く、任意の位置を読む、位置に描く、画像に対してやれることはPIXEL_LINEクラスのやれることと同じです。 『PIXEL_LINEにファイルを扱う機能を付加したモノ』という捉え方をした方が良かったかもしれません。もしそういう設計にするならば、まずPIXEL_LINEの集合としてのPIXEL_MATRIXクラスを定義して、それとは別にファイル読み込み、書き込み操作のためのクラスFILEなどを定義して、これらを多重継承したクラスとしてBITMAPを定義するべきだったかもしれません。(こんどその設計で書き直してみようと思います…(苦笑
今回はBITMAPとPIXEL_LINEとの関係をhas a関係ではなくhave a関係だと考えたので、継承ではなく所有として設計しました。ただ、書いてる途中で「あ、have a関係で捉えたこの設計は失敗だったかもな…」と思いました。どうにもコードが論理的には分離しきれてないような気がします…orz
(まちがいえた、is a関係でした…うろおぼえで書くから〜…www)
main.cpp
メイン関数です。
人間に説明するとき的な論理で書けてるので、とりあえずクラスしては、考え方としてはこれでいいのだろうな…方向性は…と。
オブジェクト指向ってのは、要するにこういう main()関数みたいなのを簡単に書けるようにするための苦労なんだと思うw
たしかに、使うだけなら、オブジェクト指向で作ったクラスとメソッドのセットってのはめちゃくちゃ簡単だと思います。ただ使うだけなら… ほんとに『ただ使うだけ』なら…(orz
効率は一切無視して、あえて馬鹿正直にオブジェクト指向で組んでます。ピクセル回りの扱いを。
はっきり言って、Cで行き当たりばったりに書いてた旧バージョンの方が何倍も速く動作します…w
使い方:
bmp2sr 変換元ファイル名.bmp 変換後ファイル名.bmp
(変換できるのは標準的な24bitビットマップ or 32bitビットマップのみです。ファイルの書き順間違えると、ファイルが消滅しちゃうんで注意してください。(バックアップしといた方が安全です))
バイナリ:
windows用:bmp2sr-020win.txt
(exe形式をブログにアップロードできないので、txtと書き換えてあります。.exeと書き直して使ってね。)
linux用:ソースからコンパイルしてください。
ソース:bmp2sr-020.txt
(zip形式をブログにアップロードできないので、txtと書き換えてあります。.zipと書き直して使ってね。)
ソースからのコンパイル方法:
linuxの場合: gcc -o bmp2sr -c PIXEL.c PIXEL_LINE.c BITMAP_INFO_HEADER.c BITMAP_FILE_HEADER.c BITMAP.c -lstdc++
windowsの場合:visualStudioのコンソールプロジェクトにソースを読み込んでコンパイルボタンを押す。(fopen関数でワーニングがでますけど通ります。気になる人はfopen_s関数に置き換えて書き直せばワーニングは出なくなります)
余談:
Cの時は一つのソースファイルにまとめても手に負える程度の、短いコードでしたが、C++でバカ正直に多態性を意識して作ったら、同じようなことやるのにアホみたいに膨大なコード量になってしまって、ほとほと呆れました。(orz
C++で書いてると、いつも思うんですけど、そもそも簡単にするのが目的なのに、そのために膨大さという複雑性を持ち込んでしまってるかのような、ある種の本末転倒っぷりを感じることが多いです。ボクは設計がヘタで抽象度が低いおかげでこれでもまだ短いコードに落ち着いてますが、たとえば基本型を包括するvariable型みたいなのを導入しようとしたならば、それこそ、さらに膨大に…(苦笑。 最初、variable型を作るところからやるつもりだったんですが、(演算子の定義からすべてやり直して)なんだかバカバカしくなってそれは断念しました(笑) …魔が差したらやるかもしれません(やめとけって…www
というか、やっぱりC++はどうにも好きになれないです。いや、おもしろいとは思いますけど。
C++で描いてると、どうしても多態性によって実現する方の設計を選択してしまって、どうにも本末転倒な複雑さと冗長性が入り込んできてしまって、ボクみたいに素人が遊びで書く分にはパズル的でおもしろくはあるのですが、もしこれを仕事にしてる人だったら、やっぱCで書くだろうなぁ〜…って気がする。
実際、本当に多態性を意識して描くと、コードがいちいち大げさになるし、「想定外の場合」ってのに対して必要以上に気を遣う感じで、本筋以外にやることが増えてしまって、どうにも進みが悪いような気がする。 それを嫌って、C++で書いてるにも関わらず、単なるベターCとして使ってる人って意外に多いのではないだろうか…たぶん。
かと言って、C++の言語機能をトリッキーな歪曲した使用法でC的コーディングに含ませたりすると、すぐカオスな依存関係でめちゃくちゃになるし(フレンドみたいな抜け道がいくらでも用意されてるから、いくらでも誤用(オブジェクト指向的ではない使い方)できてしまう)だったら、なまじC++なんか使わないで無難にCで書いた方がマシにすら思えたりもする…
コード解説:
PIXEL.cpp / PIXEL.h
ピクセルには32bitと24bitの2種類を扱い分けるのですが、ここをオブジェクト指向の多態性で共通化してしまおうという魂胆で、PIXELを純粋仮想クラスとして定義し、その継承として実際の具象クラス PIXEL3, PIXEL4 を定義してます。
ピクセルに対しての定義は「操作はリードとライト、そして四則演算が可能」という点のみです。
PIXELから派生して作られたクラスには、すべてこの規則が適用されます。またこれら派生クラスのインスタンスは全て、例外なくPIXEL型のポインタとすることができ、当然メソッドは共通してるので方を意識せずに実行時に動的に型による分岐を”自動的”に行わせられます。簡単にいうと、これが多態性です。(…だと理解してます。ボクは。 専門家じゃないので間違っててもすみませんw)
要するに、実を言うと、switch文で必要に応じて処理を分岐させるようなコードを人間が書けばいいところを、かっこつけて自動的にやらせるような仕組みに仕立て上げるのが多態性の肝どころだと思います。こんなの、ほんとは人間が分岐を書いてやればいいだけのことだと思う。 静的な型結合で十分だと思う。というか、むしろ逆にボクには実行時型結合で”なければならない”というケースがどうにも思いつかないです。 分岐書かなくていいからラクだ、コードもすっきりする、ってことくらい? いや、ほんとに。思いつかない。
PIXEL_LINE.cpp / PIXEL_LINE.h
PIXELを一次元配列として扱うためのクラスです。たぶん、こういうのをコンテナって言うのかもしれません、実はコンテナの定義ってよく知らないんですが…w
pixel は、PIXEL型のポインタを一次元配列にした状態です。ほんとはこれすらもヒープから持ってくるべきでしょうけど、スタックに置いてあります。ボクが単にPIXELポインタ型の配列をnewする”文法を知らなくて書けなかった”だけです。(あざ笑っていいよ
PIXEL型のポインタの配列があって、その一つ一つにコンストラクタ時にヒープから一個一個インスタンスを吊り下げてあります。配列の実態がメモリ的に連続してるとは限りませんし、連続してないような拡張も大丈夫だと思います。(いや、実際は連続してるのでしょうけど…w)
pixel_bufは、内部処理用に1ピクセルだけ格納できるバッファーです。どうしてこんなモノを用意してあるかというと、
read_pixel と write_pixel() との間に、バッファーを介することを人間に強制したかったからです。
PIXEL に演算子の = を再定義して代入可能にして、read_pixel(void) という設計にすることも可能ですが、これだと read_pixel 内でリードした結果を新たにコピーコントラスタで用意して返す必要がでてくると思いました(なぜなら、リードした値は操作するのだろうし、そうさするならポインタで連動しててはまずいから、あらたな実体を別に生成する必要があるけど、その生成のための型情報は多態性のため不明(常にPIXEL*であることしか分からない、つまり具象であるPIXEL3かPIXEL4かは不明。)なので、read_pixel() 内でにて PIXEL* に対して新たにバッファを作るための型情報を得られなくて難儀します。それをバッファーなしで、直接PIXEL*型をリターンする形で read_pixel()を定義したのなら、渡されたread_pixel内で、渡されたPIXEL*へぶら下げられたインスタンスのメモリを、最終的に関数内で開放する必要があります(でなければ、使われないゴミメモリがどんどん増えてく)。しかし、write_pixelには、当然、開放してほしくないPIXEL型も渡されますが、この『開放してほしい、ほしくない』の判断をする方法がありません。 あるのは、常に開放するか、常に開放しないか。だと思ったので、なら開放しない設計にしよう。と。その場合、汎用のPIXEL型のバッファーが一つあるだけで解決できるな…という理由で、pixel_bufが用意されてます。
これも、本当はC的に、単純にフラグなどで泥臭く人間的にやれば、こんなのは必要無いのだろうな…と思います。
ただ、多態性が実現されてるクラスを、フラグによって分岐させるような使い方を一ヶ所してしまうと、もぅ、そこが原因で多態性の破れ(多態性を信用しきれない箇所、少なくともここ以降は確実にそうなる)が起こってしまうので、むやみにフラグ化できないという悩みもあるとおもいます。
だから多態性を維持するために、いろいろ仕組みを練り出すのですが、それが返ってプログラムを難解にしてしまったりするわけで、多態性のそもそもの目的である『プログラムを簡単にする、読みやすくする』為という目的が本末転倒的になったりして、まぁ、世の中「こっちを立てれば、あっちが立たず」的な、そういう側面って何にでもあるのだなぁ…とw 賢者の石みたいな万能薬って、そんなうまい話はなかなか無いもんだなぁ…とか、しみじみ思ってしまいます。
BITMAP_INFO_HEADER.c / BITMAP_INFO_HEADER.h
BITMAP_FILE_HEADER.c / BITMAP_FILE_HEADER.h
BMPのヘッダを読んで格納しとくだけです。どこにも依存したくなかったので、処理は完結してます。だから、わざわざファイルを開いて、また自分で閉じてます。
BITMAP.cpp BITMAP.h
ビットマップファイルから任意の1行読む、1行描く、任意の位置を読む、位置に描く、画像に対してやれることはPIXEL_LINEクラスのやれることと同じです。 『PIXEL_LINEにファイルを扱う機能を付加したモノ』という捉え方をした方が良かったかもしれません。もしそういう設計にするならば、まずPIXEL_LINEの集合としてのPIXEL_MATRIXクラスを定義して、それとは別にファイル読み込み、書き込み操作のためのクラスFILEなどを定義して、これらを多重継承したクラスとしてBITMAPを定義するべきだったかもしれません。(こんどその設計で書き直してみようと思います…(苦笑
今回はBITMAPとPIXEL_LINEとの関係をhas a関係ではなくhave a関係だと考えたので、継承ではなく所有として設計しました。ただ、書いてる途中で「あ、have a関係で捉えたこの設計は失敗だったかもな…」と思いました。どうにもコードが論理的には分離しきれてないような気がします…orz
(まちがいえた、is a関係でした…うろおぼえで書くから〜…www)
main.cpp
メイン関数です。
人間に説明するとき的な論理で書けてるので、とりあえずクラスしては、考え方としてはこれでいいのだろうな…方向性は…と。
オブジェクト指向ってのは、要するにこういう main()関数みたいなのを簡単に書けるようにするための苦労なんだと思うw
たしかに、使うだけなら、オブジェクト指向で作ったクラスとメソッドのセットってのはめちゃくちゃ簡単だと思います。ただ使うだけなら… ほんとに『ただ使うだけ』なら…(orz
画像に対してサブピクセルレンダリング
更新:2009.10.31:
ファイルサイズが変換後も元サイズと同じままだったバグを修正しました linux版 ver0.1.4
windowsの方も同様のバグがあります。こっちはまだ直してません。(ペコリ
windowsでもlinuxのソースのままコンパイルできるとおもいます。一応セキュリティ関係で警告が出ますが一応動くのが吐き出されるはずです・・・すいません、なおします(w
BMP形式の画像ファイルを、サブピクセルレンダリングして1/3に縮小するプログラムです。
コマンドラインから使います。
bmp2sr ファイル名
です。
こんな感じになります。

(余談:ubuntu9.10のx86-64版入れました♪)
(A4 300dpi くらいの画像、つまり3500x2500くらいの画像に対して掛けると、ちょうどいいみたい。 あんまり小さい画像だと、色ずれが目立っちゃう・・・もっと綺麗にする方法は無いだろうか〜・・・と考えて遊んでみたりしてます。近況)
ソースファイル(txt形式しかアップできないんで拡張子変えてあります。本当は .c です)
linux 用 http://blog-imgs-34.fc2.com/k/e/m/kemeconoajito/bmp2sr-014.txt
windows 用 http://blog-imgs-34.fc2.com/k/e/m/kemeconoajito/bmp2sr_win-013.txt
コンパイルはubuntuなら
gcc bmp2sr.c -o bmp2sr
windowsならVisualStudioの空のコンソールプロジェクトにこのソースを読み込んでメイクすればたぶん大丈夫です。
(コンパイル設定で、スタックメモリを1メガくらい確保しないとダメかも。たぶん)
使い方:
ABCD.bmp を変換する場合
ubuntuの場合:
./bmp2sr ABCD.bmp
windowsの場合
bmp2sr.exe ABCD.bmp
サブピクセルレンダリングがどういうものなのか、実は全然知らないので想像だけで「たぶんこんな感じでやってんだろうな・・・」的な、予想実装なので実は全然トンチンカンなことやってる可能性が高いですが、まぁいいや。
具体的には単体のRGBピクセルベクトル3個を3x3の正方行列に並べて、そこの対角要素を取り出してるだけです。めちゃくちゃ単純です。
要するに簡単にいうと、1、2、3って3個の点を、1個の点に合成するときに、Rは1個目のR、Gは2個目のG、Bは3個目のBからそれぞれ取ってきて、1個の点にしてます。それだけです。単純。
というのも、なんでこういう取り方をするか? っていう根拠が自分なりにあって、
もともと考えてる大本のスタートとしては
白 白 黒 白 白 白
っていう点があったとして、全部で6ピクセルですが、これを1/3の2ピクセルに縮小した場合は
灰 白
ってなるだろうけど、灰色の点を構成する画素は
R G B
って並んでて、RGBで考えれば 灰・白 は
R G B R G B
となるわけで、元々の配置と対応させると
R G B R G B
白 白 黒 白 白 白
となるわけで、Bの明るさを0にすれば、その部分に黒を作れるだろうな。 ・・・と考えたわけです。
つまり、灰のピクセルは、80,80,80 みたいな灰ではなく、255,255,0 にするという意味です。
でも、これだと、このピクセル単体の色は黄色になってしまいます。が、このピクセルのさらに手前(左側)のピクセルのRGBのBが255ならば、BRGの配置で考えれば 255,255,255 で白になります。
つまり、この灰のピクセル”付近”は、さらに手前のBが255ならば、黄色にした方が、より黒を『輝度の値的な意味』では、より正確に保存できる方法なわけです。・・・と妄想してみました。
・・・と、まぁ、頭の中で勝手に空想してた理屈なですが、
「まぁ、実際にやったら、そんな都合よく見えないだろうな・・・」と半信半疑で試してみたら、どうも、意外と思ってるような効果がでる場合もあって、これはもっと掘り下げたらおもしろいかもなぁ・・・と思いました。
なんか専門的には、おそらく何かもぅ、『こうやります』的な答え的な数式があって、それ何も考えずにマネすればできちゃう、そういう式があるんだろうけど、そういうの見ないであえて自分であれこれ回り道しながら遊んでみようと思う。
どうせ考えて遊ぶのが目的なんだしw
こういうのはあれこれアイディアを考えてる時と、それを実際に組んでみて思ったとうりに効果あったときとが楽しい。べつに成果としての完成品には実はあんまし興味なかったりする。こういうのは過程が楽しい。成果物には実はあんまり興味ないもんだったりするw(意味無いじゃん・・・w
ファイルサイズが変換後も元サイズと同じままだったバグを修正しました linux版 ver0.1.4
windowsの方も同様のバグがあります。こっちはまだ直してません。(ペコリ
windowsでもlinuxのソースのままコンパイルできるとおもいます。一応セキュリティ関係で警告が出ますが一応動くのが吐き出されるはずです・・・すいません、なおします(w
BMP形式の画像ファイルを、サブピクセルレンダリングして1/3に縮小するプログラムです。
コマンドラインから使います。
bmp2sr ファイル名
です。
こんな感じになります。

(余談:ubuntu9.10のx86-64版入れました♪)
(A4 300dpi くらいの画像、つまり3500x2500くらいの画像に対して掛けると、ちょうどいいみたい。 あんまり小さい画像だと、色ずれが目立っちゃう・・・もっと綺麗にする方法は無いだろうか〜・・・と考えて遊んでみたりしてます。近況)
ソースファイル(txt形式しかアップできないんで拡張子変えてあります。本当は .c です)
linux 用 http://blog-imgs-34.fc2.com/k/e/m/kemeconoajito/bmp2sr-014.txt
windows 用 http://blog-imgs-34.fc2.com/k/e/m/kemeconoajito/bmp2sr_win-013.txt
コンパイルはubuntuなら
gcc bmp2sr.c -o bmp2sr
windowsならVisualStudioの空のコンソールプロジェクトにこのソースを読み込んでメイクすればたぶん大丈夫です。
(コンパイル設定で、スタックメモリを1メガくらい確保しないとダメかも。たぶん)
使い方:
ABCD.bmp を変換する場合
ubuntuの場合:
./bmp2sr ABCD.bmp
windowsの場合
bmp2sr.exe ABCD.bmp
サブピクセルレンダリングがどういうものなのか、実は全然知らないので想像だけで「たぶんこんな感じでやってんだろうな・・・」的な、予想実装なので実は全然トンチンカンなことやってる可能性が高いですが、まぁいいや。
具体的には単体のRGBピクセルベクトル3個を3x3の正方行列に並べて、そこの対角要素を取り出してるだけです。めちゃくちゃ単純です。
要するに簡単にいうと、1、2、3って3個の点を、1個の点に合成するときに、Rは1個目のR、Gは2個目のG、Bは3個目のBからそれぞれ取ってきて、1個の点にしてます。それだけです。単純。
というのも、なんでこういう取り方をするか? っていう根拠が自分なりにあって、
もともと考えてる大本のスタートとしては
白 白 黒 白 白 白
っていう点があったとして、全部で6ピクセルですが、これを1/3の2ピクセルに縮小した場合は
灰 白
ってなるだろうけど、灰色の点を構成する画素は
R G B
って並んでて、RGBで考えれば 灰・白 は
R G B R G B
となるわけで、元々の配置と対応させると
R G B R G B
白 白 黒 白 白 白
となるわけで、Bの明るさを0にすれば、その部分に黒を作れるだろうな。 ・・・と考えたわけです。
つまり、灰のピクセルは、80,80,80 みたいな灰ではなく、255,255,0 にするという意味です。
でも、これだと、このピクセル単体の色は黄色になってしまいます。が、このピクセルのさらに手前(左側)のピクセルのRGBのBが255ならば、BRGの配置で考えれば 255,255,255 で白になります。
つまり、この灰のピクセル”付近”は、さらに手前のBが255ならば、黄色にした方が、より黒を『輝度の値的な意味』では、より正確に保存できる方法なわけです。・・・と妄想してみました。
・・・と、まぁ、頭の中で勝手に空想してた理屈なですが、
「まぁ、実際にやったら、そんな都合よく見えないだろうな・・・」と半信半疑で試してみたら、どうも、意外と思ってるような効果がでる場合もあって、これはもっと掘り下げたらおもしろいかもなぁ・・・と思いました。
なんか専門的には、おそらく何かもぅ、『こうやります』的な答え的な数式があって、それ何も考えずにマネすればできちゃう、そういう式があるんだろうけど、そういうの見ないであえて自分であれこれ回り道しながら遊んでみようと思う。
どうせ考えて遊ぶのが目的なんだしw
こういうのはあれこれアイディアを考えてる時と、それを実際に組んでみて思ったとうりに効果あったときとが楽しい。べつに成果としての完成品には実はあんまし興味なかったりする。こういうのは過程が楽しい。成果物には実はあんまり興味ないもんだったりするw(意味無いじゃん・・・w
フォトショップのカラーピッカープラグイン作りました。
動作確認
Elements1で動作しました。http://kemeconoajito.dokkoisho.com/zip/el1.png
Elements7(体験版)で動作しました。http://kemeconoajito.dokkoisho.com/zip/el7.png
SSE2の動くCPU用(Pentium4、Athlon64、PentiumM、Phenom、CORE、等)
http://kemeconoajito.dokkoisho.com/zip/NearestBase_sse2.zip
SSE2の動かないCPU用(AthlonXP、PentiumIII、K6、Pentium、486、等)
http://kemeconoajito.dokkoisho.com/zip/NearestBase_x87.zip
ソースファイル
http://kemeconoajito.dokkoisho.com/zip/colol2_sorce.zip
(とりあえず動けばいいや・・・の精神で、いきあたりばったりコードです。読みづらいです)
(めんどくさいのでSDKのNearestBaseのソースをベースにして書いてます。だからCopyrightとか機能と関係無い部分がadobeのままです。そのうち書き直します。めんどくさい)
インストール方法:
1.zipを解凍する。
2.NearestBase.8bc をフォトショップのプラグインフォルダーに置く。
3.フォトショップを起動して『環境設定』で、使用する『カラーピッカー』 を 『Adobe → NearestBase』に変更します。
起動方法:
通常のカラーピッカーと同じです。(ツールボックスの色のところをクリックすると起動します)
使い方:
A.ペインターのカラーピッカーに似せてあります。 リングで色相を選択、三角で彩度と明度を選択します。
B.カーソルがウインドウの外に出ると自動的に消えます。(OKとかキャンセルとかを押す必要がありません)
C.表示中にキーボードの『ESCキー』を押すと、通常のAdobe標準のカラーピッカーが起動します。(起動するはず・・・)
備考:
もしかしたら、なんらかのバグによって、フォトショップが突然終了してしまうかもしれません。
普段絵を描く時は『Adobe』の標準のカラーピッカーに設定しておくことをすすめます。
フリーズ:
カラーピッカー起動中に、別のソフトにウインドウを切り替えて、再びフォトショップに切り替えると、フリーズに似た状態になります。(マウス入力を受け付けない状態)
このときはESCキーを押せば強制的にカラーピッカーを終了できます。
Elements1で動作しました。http://kemeconoajito.dokkoisho.com/zip/el1.png
Elements7(体験版)で動作しました。http://kemeconoajito.dokkoisho.com/zip/el7.png
SSE2の動くCPU用(Pentium4、Athlon64、PentiumM、Phenom、CORE、等)
http://kemeconoajito.dokkoisho.com/zip/NearestBase_sse2.zip
SSE2の動かないCPU用(AthlonXP、PentiumIII、K6、Pentium、486、等)
http://kemeconoajito.dokkoisho.com/zip/NearestBase_x87.zip
ソースファイル
http://kemeconoajito.dokkoisho.com/zip/colol2_sorce.zip
(とりあえず動けばいいや・・・の精神で、いきあたりばったりコードです。読みづらいです)
(めんどくさいのでSDKのNearestBaseのソースをベースにして書いてます。だからCopyrightとか機能と関係無い部分がadobeのままです。そのうち書き直します。めんどくさい)
インストール方法:
1.zipを解凍する。
2.NearestBase.8bc をフォトショップのプラグインフォルダーに置く。
3.フォトショップを起動して『環境設定』で、使用する『カラーピッカー』 を 『Adobe → NearestBase』に変更します。
起動方法:
通常のカラーピッカーと同じです。(ツールボックスの色のところをクリックすると起動します)
使い方:
A.ペインターのカラーピッカーに似せてあります。 リングで色相を選択、三角で彩度と明度を選択します。
B.カーソルがウインドウの外に出ると自動的に消えます。(OKとかキャンセルとかを押す必要がありません)
C.表示中にキーボードの『ESCキー』を押すと、通常のAdobe標準のカラーピッカーが起動します。(起動するはず・・・)
備考:
もしかしたら、なんらかのバグによって、フォトショップが突然終了してしまうかもしれません。
普段絵を描く時は『Adobe』の標準のカラーピッカーに設定しておくことをすすめます。
フリーズ:
カラーピッカー起動中に、別のソフトにウインドウを切り替えて、再びフォトショップに切り替えると、フリーズに似た状態になります。(マウス入力を受け付けない状態)
このときはESCキーを押せば強制的にカラーピッカーを終了できます。
Windowsに後ろ髪引かれる最大の理由は、Photoshop/Painter/Cubase/VisualStudioの代わりがLinuxに無いから
機能の搭載数はやたら多いけど、操作性が最悪なソフト。
機能の搭載数は少ないけど、そのどれもが洗練された直感的な操作で使えるソフト。
どちらが好まれるか。
先日ubuntu9.10のrcがアップロードされてたのでインストールしました。
あいかわらずlinuxにはGimpを超える画像編集ソフトは見当たりません。
にもかかわらず、そのGimpさえも、絵を描く上での細かい部分での些細な、しかし重要な振る舞いの多くで、ことごとくPhotoshopよりも劣っています。
平たく言うと「操作がイラつく」。 「直感的じゃない」。
一例を。
たとえばGimpではCtrl-Cでコピーしてペーストすると、フローティング画像としてレイヤーに追加されます。
ここまではphotoshopと同じですが、問題はこの先。
Gimpの場合、このフローティング画像を実際に編集可能な状態にするためには、レイヤーの新規作成ボタンを押す必要がある。
これで新規レイヤーにフローテイィング画像が貼り付けられ、『やっと』編集可能になります。 photoshopでは、このあたりは全て自動的に処理されます。 つまりそういう違いです。
ここからして全然ちがう。インターフェースの洗練度合いが雲泥の差なんです。
もぅひとつ例。
GimpにもPainter同様のカラーピッカーが搭載されてます。 リングで色相を選んで、三角で明度と彩度を決定する有名なアレです。
Painterのアレは、大変優れた発明で、おそらくペイントソフトのカラー選択方法としては究極だと思います。
しかし、これもGimp版は恐ろしく使いづらい。最悪のカラーピッカーへと成り下がっています。なぜか?
Gimpだと、三角がクルクル回るんです。 色相リングの選択部分にあわせて。
だから、たとえば「赤の時は、上にいくほど暗い」、「青の時は下にいくほど暗い」、「緑の時は右にいくほど暗い」、という具合に、方向がコロコロ変わります。
これが、実際に使ってみるとわかるのですが、じつに使いづらい。 なぜなら直感的に使えないからです。いちいち左脳で考えさせられる。これがダメなんです。
こういう『三角を色相にあわせてクルクル回す』という発想は、絵描きの発想ではありません。あの発想はプログラマーの浅知恵です。実に邪魔な機能です。
Gimpの操作には、そういう『直感的じゃない』、『いちいちワンステップ余計に左脳を使わせる』という、微妙な使いづらさが全体にちりばめられてます。
たしかに機能面だけで純粋に『できること』だけで比較すれば、GimpはPhotoshopと同等です。でも、それを操作するインターフェースの洗練度合いでは、photoshopの方が遥かに未来を行っています。
windowsのソフトは全般的にどれもすごく使いやすい。VisualStudioとEmacs、どちらが使いやすいか?w
そしてmacは、そのwindowsよりも、さらに簡単です。
だからすごいんです。
『簡単に使える』ってのは、実は、すごいことなんです。プログラム的にも高度です。
しかし実際には『簡単に使えるソフトは知的なレベルが低い』と勘違いしてる人間が多い。それもことプログラマーのような、「わりとよく知ってる人」に多い。
全然逆です。
googleやChromeのインターフェースの洗練は、甚だしく知的な『削る作業』の産物なのだと思います。ゴテゴテしたyahooと、シンプルなgoogle。どちらが知的か。
GNU/linuxのソフトは『できること』それ自体はwinやmacと大差ありません。しかし遥かに使いづらい。煩雑で不親切で『操作そのもの』に使用者の左脳を使わせてしまう。
たとえばphotoshopとGimpの違い、もしくはVisualStudioとEmacsの違いのようなもので、どちらも『できること』には、実はそれほど大差ない。でもwinのソフトの方が遥かに洗練されてます。
それは操作性の面での差が大きい。
昨今のUbuntu台頭の理由は『使いやすさ』です。
ViやEmacsが普及しない最大の理由は『使いいづらさ』です。 みなさんGEditの方が好みでしょ?
『機能が多ければ良い』という時代はとっくの昔に終わってて、いまは、『同じ機能でも、それをいかに使いやすくデザインできてるか?』が問われる時代なのだと思います。
そして、この『使いやすさ』という分野を最優先に据えてるのがmacです。
GNU/linuxは無料にしてすら普及しませんが、
macのOSXが無料になれば、おそらく一瞬でwindowsと半々くらいにまで普及することでしょう。それだけ好まれるでしょうということです。なぜか?何が違うのか?
使いやすさです。けして機能じゃありません。
そういう意味で、ubuntuの方向性は正しいです。実際、彼らの思惑とうり、あっというまに普及しました。
今後も、更に簡単にしていくべきです。 viやEmacsやLaTexを好んで使う昔ながらの人々には嘲笑われるでしょうけど。
機能の搭載数は少ないけど、そのどれもが洗練された直感的な操作で使えるソフト。
どちらが好まれるか。
先日ubuntu9.10のrcがアップロードされてたのでインストールしました。
あいかわらずlinuxにはGimpを超える画像編集ソフトは見当たりません。
にもかかわらず、そのGimpさえも、絵を描く上での細かい部分での些細な、しかし重要な振る舞いの多くで、ことごとくPhotoshopよりも劣っています。
平たく言うと「操作がイラつく」。 「直感的じゃない」。
一例を。
たとえばGimpではCtrl-Cでコピーしてペーストすると、フローティング画像としてレイヤーに追加されます。
ここまではphotoshopと同じですが、問題はこの先。
Gimpの場合、このフローティング画像を実際に編集可能な状態にするためには、レイヤーの新規作成ボタンを押す必要がある。
これで新規レイヤーにフローテイィング画像が貼り付けられ、『やっと』編集可能になります。 photoshopでは、このあたりは全て自動的に処理されます。 つまりそういう違いです。
ここからして全然ちがう。インターフェースの洗練度合いが雲泥の差なんです。
もぅひとつ例。
GimpにもPainter同様のカラーピッカーが搭載されてます。 リングで色相を選んで、三角で明度と彩度を決定する有名なアレです。
Painterのアレは、大変優れた発明で、おそらくペイントソフトのカラー選択方法としては究極だと思います。
しかし、これもGimp版は恐ろしく使いづらい。最悪のカラーピッカーへと成り下がっています。なぜか?
Gimpだと、三角がクルクル回るんです。 色相リングの選択部分にあわせて。
だから、たとえば「赤の時は、上にいくほど暗い」、「青の時は下にいくほど暗い」、「緑の時は右にいくほど暗い」、という具合に、方向がコロコロ変わります。
これが、実際に使ってみるとわかるのですが、じつに使いづらい。 なぜなら直感的に使えないからです。いちいち左脳で考えさせられる。これがダメなんです。
こういう『三角を色相にあわせてクルクル回す』という発想は、絵描きの発想ではありません。あの発想はプログラマーの浅知恵です。実に邪魔な機能です。
Gimpの操作には、そういう『直感的じゃない』、『いちいちワンステップ余計に左脳を使わせる』という、微妙な使いづらさが全体にちりばめられてます。
たしかに機能面だけで純粋に『できること』だけで比較すれば、GimpはPhotoshopと同等です。でも、それを操作するインターフェースの洗練度合いでは、photoshopの方が遥かに未来を行っています。
windowsのソフトは全般的にどれもすごく使いやすい。VisualStudioとEmacs、どちらが使いやすいか?w
そしてmacは、そのwindowsよりも、さらに簡単です。
だからすごいんです。
『簡単に使える』ってのは、実は、すごいことなんです。プログラム的にも高度です。
しかし実際には『簡単に使えるソフトは知的なレベルが低い』と勘違いしてる人間が多い。それもことプログラマーのような、「わりとよく知ってる人」に多い。
全然逆です。
googleやChromeのインターフェースの洗練は、甚だしく知的な『削る作業』の産物なのだと思います。ゴテゴテしたyahooと、シンプルなgoogle。どちらが知的か。
GNU/linuxのソフトは『できること』それ自体はwinやmacと大差ありません。しかし遥かに使いづらい。煩雑で不親切で『操作そのもの』に使用者の左脳を使わせてしまう。
たとえばphotoshopとGimpの違い、もしくはVisualStudioとEmacsの違いのようなもので、どちらも『できること』には、実はそれほど大差ない。でもwinのソフトの方が遥かに洗練されてます。
それは操作性の面での差が大きい。
昨今のUbuntu台頭の理由は『使いやすさ』です。
ViやEmacsが普及しない最大の理由は『使いいづらさ』です。 みなさんGEditの方が好みでしょ?
『機能が多ければ良い』という時代はとっくの昔に終わってて、いまは、『同じ機能でも、それをいかに使いやすくデザインできてるか?』が問われる時代なのだと思います。
そして、この『使いやすさ』という分野を最優先に据えてるのがmacです。
GNU/linuxは無料にしてすら普及しませんが、
macのOSXが無料になれば、おそらく一瞬でwindowsと半々くらいにまで普及することでしょう。それだけ好まれるでしょうということです。なぜか?何が違うのか?
使いやすさです。けして機能じゃありません。
そういう意味で、ubuntuの方向性は正しいです。実際、彼らの思惑とうり、あっというまに普及しました。
今後も、更に簡単にしていくべきです。 viやEmacsやLaTexを好んで使う昔ながらの人々には嘲笑われるでしょうけど。
cp ってのはAからBにファイルをコピーするコマンドらしいけど
どうやらDOSのcopyコマンドみたいなもんだろうな…と。たぶん。
で、インターネットのURLを直接指定して、ネットから直にコピーできないかなぁ〜…って思って
試しに
cp http://ftp.gnu.org/gnu/bash/bash-4.0.tar.gz $HOME/tmp/
みたいにしたら、ネットからコピーできるかも〜? って思って試してみたけど、できませんでした。(orz
「これができれば、今までの更新、全部バッチファイルに書いて、自動化できるのになぁ〜…」
と。
「なんか、簡単にやるツールないかなぁ〜…」って調べてたらこんなの見つけた。
http://tech.bayashi.net/svr/doc/wget.html
wgetっていうツールだかコマンドだか使えばいいみたい。書式はcpと同じ。
wget http://ftp.gnu.org/gnu/bash/bash-4.0.tar.gz $HOME/tmp/
これでやったら、ちゃんとダウンロードできた。
どうも表示の感じが見覚えある。 どうやら ubuntu にお手軽更新みたいなのやってもらってるときに、コンソールに表示されるのがこれに似てる。
たぶんこれでやってたのだろうな… なるほろ〜〜と勝手に納得。
まぁ、とりあえず、wget ってのでダウンロードしてくるようにバッチファイル書けば、『ダウンロード、コンパイル、インストール』の一連の処理が自動かできそう。
いつか訪れるであろう、「システム壊れて再インストールしなきゃ〜〜!」の時にも安心だ。バッチファイルで自動化しとけばコマンド一行打つだけだし♪
…そのうち自分用にバッチファイル書こう〜…と。
あ、あと、なんかどうやらLinuxだとバッチファイルって言わないで、シェルスクリプトって言うらしい。
まぁ、名前なんかどうでもいいや。
拡張子が.bat じゃなくて .shってするのが作法(?)なのかしら、たぶん。よく知らないけど。
余談:
どうやらubuntuにはwgetが元々入ってるみたいだけど、例によってi486用にコンパイルされてるクサいので、ちゃんとcore2用にしとこう。あと古いし。
GNUのwgetがあるから、ソースダウンロードしてきてコンパイル・インストールしようと思う。
全部コマンドラインでやってみよう。
cd $HOME/tmp/
wget ftp://ftp.gnu.org/gnu/wget/wget-1.12.tar.gz
tar xvf wget-1.12.tar.gz
cd wget-1.12
sudo ./configure CFLAGS="-O4 -march=core2 -msse3"
sudo make
sudo make install
やった〜〜♪(^_^v
wget げっとーー
で、インターネットのURLを直接指定して、ネットから直にコピーできないかなぁ〜…って思って
試しに
cp http://ftp.gnu.org/gnu/bash/bash-4.0.tar.gz $HOME/tmp/
みたいにしたら、ネットからコピーできるかも〜? って思って試してみたけど、できませんでした。(orz
「これができれば、今までの更新、全部バッチファイルに書いて、自動化できるのになぁ〜…」
と。
「なんか、簡単にやるツールないかなぁ〜…」って調べてたらこんなの見つけた。
http://tech.bayashi.net/svr/doc/wget.html
wgetっていうツールだかコマンドだか使えばいいみたい。書式はcpと同じ。
wget http://ftp.gnu.org/gnu/bash/bash-4.0.tar.gz $HOME/tmp/
これでやったら、ちゃんとダウンロードできた。
どうも表示の感じが見覚えある。 どうやら ubuntu にお手軽更新みたいなのやってもらってるときに、コンソールに表示されるのがこれに似てる。
たぶんこれでやってたのだろうな… なるほろ〜〜と勝手に納得。
まぁ、とりあえず、wget ってのでダウンロードしてくるようにバッチファイル書けば、『ダウンロード、コンパイル、インストール』の一連の処理が自動かできそう。
いつか訪れるであろう、「システム壊れて再インストールしなきゃ〜〜!」の時にも安心だ。バッチファイルで自動化しとけばコマンド一行打つだけだし♪
…そのうち自分用にバッチファイル書こう〜…と。
あ、あと、なんかどうやらLinuxだとバッチファイルって言わないで、シェルスクリプトって言うらしい。
まぁ、名前なんかどうでもいいや。
拡張子が.bat じゃなくて .shってするのが作法(?)なのかしら、たぶん。よく知らないけど。
余談:
どうやらubuntuにはwgetが元々入ってるみたいだけど、例によってi486用にコンパイルされてるクサいので、ちゃんとcore2用にしとこう。あと古いし。
GNUのwgetがあるから、ソースダウンロードしてきてコンパイル・インストールしようと思う。
全部コマンドラインでやってみよう。
cd $HOME/tmp/
wget ftp://ftp.gnu.org/gnu/wget/wget-1.12.tar.gz
tar xvf wget-1.12.tar.gz
cd wget-1.12
sudo ./configure CFLAGS="-O4 -march=core2 -msse3"
sudo make
sudo make install
やった〜〜♪(^_^v
wget げっとーー
端末(bash)のバージョン表示見たら「このプログラムはi486向けにコンパイルされてます」的な表示出て
え〜〜…(・_;
って思ったので、ソースダウンロードしてきてcore2専用にコンパイルしちゃおう〜って思った。
あとついでに、ほかの細かいGNUツールもいくつかコンパイルしとこう…
気がつく度にちょくちょくメモして落としてコンパイルしてこうと思う。量が膨大過ぎて果てしないけど…
ソースURL
bash
http://ftp.gnu.org/gnu/bash/bash-4.0.tar.gz
grep
http://ftp.gnu.org/gnu/grep/grep-2.5.4.tar.gz
bison
http://ftp.gnu.org/gnu/bison/bison-2.4.tar.gz
makeinfo
http://ftp.gnu.org/gnu/texinfo/texinfo-4.13a.tar.gz
コンパイル・リンク・インストール(どれも同じ)
1.
解凍先のディレクトリに入る
2.
sudo ./configure CFLAGS="-O4 -march=core2 -msse3"
sudo make
sudo make install
プログラム名 --version
ってやれば、大抵どれでもバージョンが見れる。 たぶんそういう様式みたい。
ちゃん変わってるか一応確認しとく。
って思ったので、ソースダウンロードしてきてcore2専用にコンパイルしちゃおう〜って思った。
あとついでに、ほかの細かいGNUツールもいくつかコンパイルしとこう…
気がつく度にちょくちょくメモして落としてコンパイルしてこうと思う。量が膨大過ぎて果てしないけど…
ソースURL
bash
http://ftp.gnu.org/gnu/bash/bash-4.0.tar.gz
grep
http://ftp.gnu.org/gnu/grep/grep-2.5.4.tar.gz
bison
http://ftp.gnu.org/gnu/bison/bison-2.4.tar.gz
makeinfo
http://ftp.gnu.org/gnu/texinfo/texinfo-4.13a.tar.gz
コンパイル・リンク・インストール(どれも同じ)
1.
解凍先のディレクトリに入る
2.
sudo ./configure CFLAGS="-O4 -march=core2 -msse3"
sudo make
sudo make install
プログラム名 --version
ってやれば、大抵どれでもバージョンが見れる。 たぶんそういう様式みたい。
ちゃん変わってるか一応確認しとく。
ubuntu に Mercurial をいれて、firefoxの3.5系〜3.7系のソースコードをダウンロードしてみよう
前回のCVSで、3.0.16pre までしか落とせないことが判明したので、仕方なくMercurialってのを導入してみようと思いました。
当初「新しいから難しそうだなぁ〜… めんどくさいなぁ〜…」と先入観だったんですが、実際に使ってみるとCVSよりずっと分かり易くて簡単で使いやすいと思いました。おすすめ。
オチから言うと、結局3.7の最新コードは落とせたのですが、開発中なのでコンパイルがまともに通りませんでした。
結局3.6b2pre が、コンパイルできるコードの中では最新みたいです。
Mercurialのインストール
1.ソースファイルを落としてきます
http://mercurial.selenic.com/release/mercurial-1.3.tar.gz
2.依存ライブラリとかツールとか入れます
sudo apt-get install build-essential gcc python-dev
sudo apt-get install asciidoc xmlto
3.コンパイルします
参考:http://mercurial.selenic.com/wiki/UnixInstall
3ー1.
sudo make install-home
3ー2.
$HOME/.bashrcに以下を書き加える
export PYTHONPATH=${HOME}/lib/python # bash syntax, ymmv
export PATH=${HOME}/bin:$PATH
3ー3.
bash再起動
sudo make install
これでコンソールでhgと打って、hgのヘルプが表示されればインストール成功です。
firefoxのソースをダウンロードする。
参考:https://developer.mozilla.org/ja/Mozilla_Source_Code_%28Mercurial%29
1.
hg clone http://hg.mozilla.org/mozilla-central/ src
これだけ。
これで $HOME/src に mozilla-central(メイン開発ツリー)のソースコードがダウンロードされます。
あとはいつもどうり src に .mozcnfig を書いて、依存ライブラリーを落として、
srcディレクトリ内で
sudo ./configure
sudo make -f client.mk build
するだけです。
この辺の手順はいつもと同じです。
で、結局 mozilla-central (つまり3.7?)のコードはコンパイル通らないので、3.6b2pri など落としてみる。
参考:https://developer.mozilla.org/ja/Mozilla_Source_Code_%28Mercurial%29
hg clone http://hg.mozilla.org/releases/mozilla-1.9.2/ 192src
これで $HOME/192src/ に 3.6b2pre のコードがダウンロードされる。 あとはいつもの方法で。
これはコンパイル通ってインストールできるみたいです。
補足:
コードの新たな追加・更新分を落とす場合は
src のディレクトリ内で
hg pull http://hg.mozilla.org/mozilla-central/
hg update
これだけです。
(src は今回の例でのダウンロード先ディレクトリのこと。)
余談:
3.7のエラーの内容見て、エラー出してるソースの場所が分かったので「ちょっと自分でなおしちゃおうか…」って思って眺めてみたんだけど、
エラーが出た .h の行あたりを眺めてみたんだけど、C++の文法的にはなにもおかしいところが無いような気がした…はて?
エラー出力
WebGLContext.h:59 から include されたファイル中,
WebGLContext.cpp:2 から:
nsGLPbuffer.h:62:20: error: GL/glx.h: No such file or directory
In file included from WebGLContext.h:59,
from WebGLContext.cpp:2:
nsGLPbuffer.h:184: error: ISO C++ forbids declaration of ‘Display’ with no type
nsGLPbuffer.h:184: error: expected ‘;’ before ‘*’ token
nsGLPbuffer.h:185: error: ‘GLXFBConfig’ does not name a type
nsGLPbuffer.h:186: error: ‘GLXPbuffer’ does not name a type
nsGLPbuffer.h:187: error: ‘GLXContext’ does not name a type
make[6]: *** [WebGLContext.o] エラー 1
ようするに、クラス内でメンバ変数として宣言してる型が、なんか見つからないだかで(?)参照がうまくいってないってことなんでしょうか? (ぜんぜん的外れかもw)
…にしても、直すにしたって、あまりにもコードが膨大すぎて、なにがどこにどう影響したり依存してるのかがさっぱり…
こんなの直せる人が世の中にはいるんだもんなぁ… すごいなぁ…(しみじみ)
どういう整理のしかたで理解してるんだろうか… と思った(とおいめ)
Booch法の本で昔読んだけど、曰く、人間が、同時に把握・理解できる事の数は、5±2だって書いてあったなぁ…と。
頭いい人でも7、ボクみたいなのでも3程度ってこと。 このくらいの構造数にまで抽象化して理解しないと、構造を理解なんてできないんだ…という話だったなぁ、たしか。 で、オブジェクト指向しろ…とw
…でもおれ、オブジェクト指向きらい。 考え方は単純で簡単で好きなんだけど、実際にコーディングしようとすると途端に難解になるから、意味ないじゃん…と。 簡単に理解するためにオブジェクト指向導入してるはずなのに、なぜかコードは逆に難解になっちゃうという感じが、なんだか本末転倒だなぁ〜…と思うから。
…っていうか、ボクは頭わるいから3だから、7の人みたいにC++を読めてないから難解に感じるだけなのかもしれませんけどね…
ただのCの方がいいや… それも、ごくごく当たり前〜な書き方だけで構成されたCコード。
構造体でデータまとめて、それ関数で扱って、結果をど〜たらして… 的な。 単純〜なのがいいや…
っていうか、頭わるいから、そういう行き当たりバッタリ的なコードの方がむしろ読みやすいですわ…はは… 当方、どうせ理解できる範囲はその程度ですし…w (orz
当初「新しいから難しそうだなぁ〜… めんどくさいなぁ〜…」と先入観だったんですが、実際に使ってみるとCVSよりずっと分かり易くて簡単で使いやすいと思いました。おすすめ。
オチから言うと、結局3.7の最新コードは落とせたのですが、開発中なのでコンパイルがまともに通りませんでした。
結局3.6b2pre が、コンパイルできるコードの中では最新みたいです。
Mercurialのインストール
1.ソースファイルを落としてきます
http://mercurial.selenic.com/release/mercurial-1.3.tar.gz
2.依存ライブラリとかツールとか入れます
sudo apt-get install build-essential gcc python-dev
sudo apt-get install asciidoc xmlto
3.コンパイルします
参考:http://mercurial.selenic.com/wiki/UnixInstall
3ー1.
sudo make install-home
3ー2.
$HOME/.bashrcに以下を書き加える
export PYTHONPATH=${HOME}/lib/python # bash syntax, ymmv
export PATH=${HOME}/bin:$PATH
3ー3.
bash再起動
sudo make install
これでコンソールでhgと打って、hgのヘルプが表示されればインストール成功です。
firefoxのソースをダウンロードする。
参考:https://developer.mozilla.org/ja/Mozilla_Source_Code_%28Mercurial%29
1.
hg clone http://hg.mozilla.org/mozilla-central/ src
これだけ。
これで $HOME/src に mozilla-central(メイン開発ツリー)のソースコードがダウンロードされます。
あとはいつもどうり src に .mozcnfig を書いて、依存ライブラリーを落として、
srcディレクトリ内で
sudo ./configure
sudo make -f client.mk build
するだけです。
この辺の手順はいつもと同じです。
で、結局 mozilla-central (つまり3.7?)のコードはコンパイル通らないので、3.6b2pri など落としてみる。
参考:https://developer.mozilla.org/ja/Mozilla_Source_Code_%28Mercurial%29
hg clone http://hg.mozilla.org/releases/mozilla-1.9.2/ 192src
これで $HOME/192src/ に 3.6b2pre のコードがダウンロードされる。 あとはいつもの方法で。
これはコンパイル通ってインストールできるみたいです。
補足:
コードの新たな追加・更新分を落とす場合は
src のディレクトリ内で
hg pull http://hg.mozilla.org/mozilla-central/
hg update
これだけです。
(src は今回の例でのダウンロード先ディレクトリのこと。)
余談:
3.7のエラーの内容見て、エラー出してるソースの場所が分かったので「ちょっと自分でなおしちゃおうか…」って思って眺めてみたんだけど、
エラーが出た .h の行あたりを眺めてみたんだけど、C++の文法的にはなにもおかしいところが無いような気がした…はて?
エラー出力
WebGLContext.h:59 から include されたファイル中,
WebGLContext.cpp:2 から:
nsGLPbuffer.h:62:20: error: GL/glx.h: No such file or directory
In file included from WebGLContext.h:59,
from WebGLContext.cpp:2:
nsGLPbuffer.h:184: error: ISO C++ forbids declaration of ‘Display’ with no type
nsGLPbuffer.h:184: error: expected ‘;’ before ‘*’ token
nsGLPbuffer.h:185: error: ‘GLXFBConfig’ does not name a type
nsGLPbuffer.h:186: error: ‘GLXPbuffer’ does not name a type
nsGLPbuffer.h:187: error: ‘GLXContext’ does not name a type
make[6]: *** [WebGLContext.o] エラー 1
ようするに、クラス内でメンバ変数として宣言してる型が、なんか見つからないだかで(?)参照がうまくいってないってことなんでしょうか? (ぜんぜん的外れかもw)
…にしても、直すにしたって、あまりにもコードが膨大すぎて、なにがどこにどう影響したり依存してるのかがさっぱり…
こんなの直せる人が世の中にはいるんだもんなぁ… すごいなぁ…(しみじみ)
どういう整理のしかたで理解してるんだろうか… と思った(とおいめ)
Booch法の本で昔読んだけど、曰く、人間が、同時に把握・理解できる事の数は、5±2だって書いてあったなぁ…と。
頭いい人でも7、ボクみたいなのでも3程度ってこと。 このくらいの構造数にまで抽象化して理解しないと、構造を理解なんてできないんだ…という話だったなぁ、たしか。 で、オブジェクト指向しろ…とw
…でもおれ、オブジェクト指向きらい。 考え方は単純で簡単で好きなんだけど、実際にコーディングしようとすると途端に難解になるから、意味ないじゃん…と。 簡単に理解するためにオブジェクト指向導入してるはずなのに、なぜかコードは逆に難解になっちゃうという感じが、なんだか本末転倒だなぁ〜…と思うから。
…っていうか、ボクは頭わるいから3だから、7の人みたいにC++を読めてないから難解に感じるだけなのかもしれませんけどね…
ただのCの方がいいや… それも、ごくごく当たり前〜な書き方だけで構成されたCコード。
構造体でデータまとめて、それ関数で扱って、結果をど〜たらして… 的な。 単純〜なのがいいや…
っていうか、頭わるいから、そういう行き当たりバッタリ的なコードの方がむしろ読みやすいですわ…はは… 当方、どうせ理解できる範囲はその程度ですし…w (orz
Firefox3.0.16preのコンパイル・インストール(CVSによるコード取得)
Firefox3.0.16pre系のソースを入手するためにCVSをインストールする方法
Flex
sudo apt-get install flex
bison
sudo apt-get install bison
audoconf
sudo apt-get install autoconf2.13
ライブIDLのソース
http://ftp.gnome.org/pub/gnome/sources/libIDL/0.8/libIDL-0.8.13.tar.gz
sudo ./configure
sudo make
sudo make install
CVSのソース
http://ftp.gnu.org/non-gnu/cvs/source/stable/1.11.23/cvs-1.11.23.tar.gz
sudo ./configure
sudo make
sudo make install
以下、ここに描いてあるとうりにマネする
http://www.limy.org/program/linux/lfs/mozilla_install.html
参考 https://developer.mozilla.org/ja/Mozilla_Source_Code_Via_CVS
cvs -d :pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot login
anonymous
cvs -d :pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot co mozilla/client.mk
cd mozilla
make -f client.mk checkout MOZ_CO_PROJECT=browser
touch .mozconfig
sudo gedit .mozconfig
内容($BASEDIR の部分は client.mk のあるディレクトリ)
mk_add_options MOZ_OBJDIR=$BASEDIR/browser
. $BASEDIR/browser/config/mozconfig
依存ライブラリーを入れる
sudo apt-get install libgtk2.0-dev libtiff4-dev libjpeg-dev libgif-dev libXpm-dev librsvg2-dev libgpm-dev libdbus-1-dev libm17n-dev libotf-dev
sudo apt-get install libdbus-glib-1-dev libnotify-dev libasound2-dev libcurl4-openssl-dev libiDL-dev libiw-dev
sudo apt-get install build-essential linux-headers-`uname -r` gcc-3.4 libgtk2.0-dev
sudo apt-get install libgtk2.0-dev libdbus-glib-1-dev libidl-dev libxt-dev
コンパイル・リンク
sudo make -f client.mk build
インストール
sudo make -f client.mk install
備考:
インストールされる実行firefoxの場所のパターンは
/usr/lib/firefoxバージョン名/firefox
っぽい
プラグイン関係は、その実行ファイルが存在するディレクトリにある./plugin/ディレクトリから
○○.soが見える状態にすれば使える。(リンクでも、直接本物おいても、どっちでもいい)
余談:
もともと「最新の3.7系のコードをダウンロードしてきちゃうもんね♪」って思ってCVSを試してみたんですけど、どうやら開発のバージョン管理にCVSが使われてたのは3.0.16preまでだったみたい。
どうやらCVSでは落とせるバージョンもここまでみたいです。
これよりも新しいソースをダウンロードする場合は Mercurial ってのを使うらしいです。
Flex
sudo apt-get install flex
bison
sudo apt-get install bison
audoconf
sudo apt-get install autoconf2.13
ライブIDLのソース
http://ftp.gnome.org/pub/gnome/sources/libIDL/0.8/libIDL-0.8.13.tar.gz
sudo ./configure
sudo make
sudo make install
CVSのソース
http://ftp.gnu.org/non-gnu/cvs/source/stable/1.11.23/cvs-1.11.23.tar.gz
sudo ./configure
sudo make
sudo make install
以下、ここに描いてあるとうりにマネする
http://www.limy.org/program/linux/lfs/mozilla_install.html
参考 https://developer.mozilla.org/ja/Mozilla_Source_Code_Via_CVS
cvs -d :pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot login
anonymous
cvs -d :pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot co mozilla/client.mk
cd mozilla
make -f client.mk checkout MOZ_CO_PROJECT=browser
touch .mozconfig
sudo gedit .mozconfig
内容($BASEDIR の部分は client.mk のあるディレクトリ)
mk_add_options MOZ_OBJDIR=$BASEDIR/browser
. $BASEDIR/browser/config/mozconfig
依存ライブラリーを入れる
sudo apt-get install libgtk2.0-dev libtiff4-dev libjpeg-dev libgif-dev libXpm-dev librsvg2-dev libgpm-dev libdbus-1-dev libm17n-dev libotf-dev
sudo apt-get install libdbus-glib-1-dev libnotify-dev libasound2-dev libcurl4-openssl-dev libiDL-dev libiw-dev
sudo apt-get install build-essential linux-headers-`uname -r` gcc-3.4 libgtk2.0-dev
sudo apt-get install libgtk2.0-dev libdbus-glib-1-dev libidl-dev libxt-dev
コンパイル・リンク
sudo make -f client.mk build
インストール
sudo make -f client.mk install
備考:
インストールされる実行firefoxの場所のパターンは
/usr/lib/firefoxバージョン名/firefox
っぽい
プラグイン関係は、その実行ファイルが存在するディレクトリにある./plugin/ディレクトリから
○○.soが見える状態にすれば使える。(リンクでも、直接本物おいても、どっちでもいい)
余談:
もともと「最新の3.7系のコードをダウンロードしてきちゃうもんね♪」って思ってCVSを試してみたんですけど、どうやら開発のバージョン管理にCVSが使われてたのは3.0.16preまでだったみたい。
どうやらCVSでは落とせるバージョンもここまでみたいです。
これよりも新しいソースをダウンロードする場合は Mercurial ってのを使うらしいです。
Firefox3.6b をコンパイル・インストールする方法
ソースから自前でコンパイルすれば、自分のCPUに最適化されたFirefoxを使えるので、いろいろ得です。
1.まず、Firefoxのホームページからソースを入手します。
ftp://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/3.6b1-candidates/build1/source/firefox-3.6b1.source.tar.bz2
2.解凍します。
tar xvf firefox-3.6b1.source.tar.bz2
3.解凍先のディレクトリに移動します。
cd mozilla-1.9.2
ここに【.mozconfig】というテキストファイルを作成します。(解凍先の先頭ディレクトリ内に作る)
.mozconfig
です。 ファイル名の先頭はピリオドです。
.mozconfig の内容には以下のテキストを書きます。
ac_add_options --enable-application=browser
ac_add_options --enable-optimize="-O4 -march=core2 -msse3"
ac_add_options --disable-debug
4.Firefox3.6bが依存してるライブラリーを調べてインストールします。
sudo apt-get install libgtk2.0-dev libtiff4-dev libjpeg-dev libgif-dev libXpm-dev librsvg2-dev libgpm-dev libdbus-1-dev libm17n-dev libotf-dev
sudo apt-get install libdbus-glib-1-dev libnotify-dev libasound2-dev libcurl4-openssl-dev libiDL-dev libiw-dev
5.Makefile (client.mk) を作成します。
sudo ./configure
最後あたりの行に error 系を匂わすメッセージがなければ次に進めます。
エラーがあった場合は、依存ライブラリーのインストールが何か不足してる可能性があります。メッセージから推測して調べてインストールしてください。
6.コンパイル&リンクします。 (core2で30分ほど)
sudo make -f client.mk build
7.ビルド結果の確認をします。
もしビルドに成功していれば ./dist/bin/firefox で起動します。
バージョン情報を確認してください。
8.インストールします。
sudo make client.mk install
以上です。
元々のファイアーフォックスのショートカットがそのまま機能すると思います。たぶん。
コンソールから起動する場合は firefox です。(/usr/bin/firefox)
余談:
アイコンが火狐じゃなくて地球になってるのは、権利関係の影響らしいです。狐アイコンが使えるのはmozillaの純正ビルドだけという決まりらしいです。よくしりませんが。
Javascript 実行速度の比較。(SunSpider)
Total2898.8ms
Firefox3.0 通常版
Total972.0ms
Firefox3.6b1 最適化コンパイル版(core2、sse3専用)
テストCPU: Pentium Dual-Core E5300 2.6GHz (45nm Wolfdaleコア)
1.まず、Firefoxのホームページからソースを入手します。
ftp://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/3.6b1-candidates/build1/source/firefox-3.6b1.source.tar.bz2
2.解凍します。
tar xvf firefox-3.6b1.source.tar.bz2
3.解凍先のディレクトリに移動します。
cd mozilla-1.9.2
ここに【.mozconfig】というテキストファイルを作成します。(解凍先の先頭ディレクトリ内に作る)
.mozconfig
です。 ファイル名の先頭はピリオドです。
.mozconfig の内容には以下のテキストを書きます。
ac_add_options --enable-application=browser
ac_add_options --enable-optimize="-O4 -march=core2 -msse3"
ac_add_options --disable-debug
4.Firefox3.6bが依存してるライブラリーを調べてインストールします。
sudo apt-get install libgtk2.0-dev libtiff4-dev libjpeg-dev libgif-dev libXpm-dev librsvg2-dev libgpm-dev libdbus-1-dev libm17n-dev libotf-dev
sudo apt-get install libdbus-glib-1-dev libnotify-dev libasound2-dev libcurl4-openssl-dev libiDL-dev libiw-dev
5.Makefile (client.mk) を作成します。
sudo ./configure
最後あたりの行に error 系を匂わすメッセージがなければ次に進めます。
エラーがあった場合は、依存ライブラリーのインストールが何か不足してる可能性があります。メッセージから推測して調べてインストールしてください。
6.コンパイル&リンクします。 (core2で30分ほど)
sudo make -f client.mk build
7.ビルド結果の確認をします。
もしビルドに成功していれば ./dist/bin/firefox で起動します。
バージョン情報を確認してください。
8.インストールします。
sudo make client.mk install
以上です。
元々のファイアーフォックスのショートカットがそのまま機能すると思います。たぶん。
コンソールから起動する場合は firefox です。(/usr/bin/firefox)
余談:
アイコンが火狐じゃなくて地球になってるのは、権利関係の影響らしいです。狐アイコンが使えるのはmozillaの純正ビルドだけという決まりらしいです。よくしりませんが。
Javascript 実行速度の比較。(SunSpider)
Total2898.8ms
Firefox3.0 通常版
Total972.0ms
Firefox3.6b1 最適化コンパイル版(core2、sse3専用)
テストCPU: Pentium Dual-Core E5300 2.6GHz (45nm Wolfdaleコア)
ubuntu9.04インストール直後にとりあえずやっとくこと(主に日本語関係)
【システム → システム管理 → 日本語環境セットアップヘルパ】
を実行して、とりあえずフォントとか一通りちゃんとしたの入れる。
【システム → システム管理 → Synapticパッケージ・マネージャー】
を実行して ubuntu-desktop-ja を検索してインストールする。(同時に自動で選択される依存ファイルもすべて)
↑
日本語のIMEの動作があやしくなったり問題が起きた時も、とりあえずこれを入れ直せば治るケースが多い。
あと、日本語関係ないけど、grub (ブートローダー)の起動時に表示される一覧の順序が
.Ubuntu
.Memtest86
...
.Windows
みたいに、Winの順番は最後にされちゃってる。
普段Winを起動することの方が多いならば、表の先頭をWinに書き換えた方がいい。(自動選択されるのは毎回、表の先頭のOSだし)
sudo gedit /boot/grub/menu.lst
でエディタを起動して編集する。
## ## End Default Options ##
title Ubuntu 9.04, kernel 2.6.28-15-generic
uuid 090a0f73-66c1-44ee-b0d2-c40ac1264a0e
kernel /boot/vmlinuz-2.6.28-15-generic root=UUID=090a0f73-66c1-44ee-b0d2-c40ac1264a0e ro quiet splash
initrd /boot/initrd.img-2.6.28-15-generic
quiet
title Ubuntu 9.04, kernel 2.6.28-15-generic (recovery mode)
uuid 090a0f73-66c1-44ee-b0d2-c40ac1264a0e
kernel /boot/vmlinuz-2.6.28-15-generic root=UUID=090a0f73-66c1-44ee-b0d2-c40ac1264a0e ro single
initrd /boot/initrd.img-2.6.28-15-generic
title Ubuntu 9.04, kernel 2.6.28-11-generic
uuid 090a0f73-66c1-44ee-b0d2-c40ac1264a0e
kernel /boot/vmlinuz-2.6.28-11-generic root=UUID=090a0f73-66c1-44ee-b0d2-c40ac1264a0e ro quiet splash
initrd /boot/initrd.img-2.6.28-11-generic
quiet
title Ubuntu 9.04, kernel 2.6.28-11-generic (recovery mode)
uuid 090a0f73-66c1-44ee-b0d2-c40ac1264a0e
kernel /boot/vmlinuz-2.6.28-11-generic root=UUID=090a0f73-66c1-44ee-b0d2-c40ac1264a0e ro single
initrd /boot/initrd.img-2.6.28-11-generic
title Ubuntu 9.04, memtest86+
uuid 090a0f73-66c1-44ee-b0d2-c40ac1264a0e
kernel /boot/memtest86+.bin
quiet
### END DEBIAN AUTOMAGIC KERNELS LIST
# This is a divider, added to separate the menu items below from the Debian
# ones.
title Other operating systems:
root
# This entry automatically added by the Debian installer for a non-linux OS
# on /dev/sda1
title Microsoft Windows 2000 Professional
rootnoverify (hd0,0)
savedefault
makeactive
chainloader +1
Windowsの項目の順番が最後になってるので、これを最初になるように先頭辺りにコピペするだけ。
注:間違えておかしなことすると、システムが起動しなくなる。
その時は、インストール用のUbuntuのDVDから直接起動して、もう一度、この場所をエディタで正しく訂正すればいいだけ。
を実行して、とりあえずフォントとか一通りちゃんとしたの入れる。
【システム → システム管理 → Synapticパッケージ・マネージャー】
を実行して ubuntu-desktop-ja を検索してインストールする。(同時に自動で選択される依存ファイルもすべて)
↑
日本語のIMEの動作があやしくなったり問題が起きた時も、とりあえずこれを入れ直せば治るケースが多い。
あと、日本語関係ないけど、grub (ブートローダー)の起動時に表示される一覧の順序が
.Ubuntu
.Memtest86
...
.Windows
みたいに、Winの順番は最後にされちゃってる。
普段Winを起動することの方が多いならば、表の先頭をWinに書き換えた方がいい。(自動選択されるのは毎回、表の先頭のOSだし)
sudo gedit /boot/grub/menu.lst
でエディタを起動して編集する。
## ## End Default Options ##
title Ubuntu 9.04, kernel 2.6.28-15-generic
uuid 090a0f73-66c1-44ee-b0d2-c40ac1264a0e
kernel /boot/vmlinuz-2.6.28-15-generic root=UUID=090a0f73-66c1-44ee-b0d2-c40ac1264a0e ro quiet splash
initrd /boot/initrd.img-2.6.28-15-generic
quiet
title Ubuntu 9.04, kernel 2.6.28-15-generic (recovery mode)
uuid 090a0f73-66c1-44ee-b0d2-c40ac1264a0e
kernel /boot/vmlinuz-2.6.28-15-generic root=UUID=090a0f73-66c1-44ee-b0d2-c40ac1264a0e ro single
initrd /boot/initrd.img-2.6.28-15-generic
title Ubuntu 9.04, kernel 2.6.28-11-generic
uuid 090a0f73-66c1-44ee-b0d2-c40ac1264a0e
kernel /boot/vmlinuz-2.6.28-11-generic root=UUID=090a0f73-66c1-44ee-b0d2-c40ac1264a0e ro quiet splash
initrd /boot/initrd.img-2.6.28-11-generic
quiet
title Ubuntu 9.04, kernel 2.6.28-11-generic (recovery mode)
uuid 090a0f73-66c1-44ee-b0d2-c40ac1264a0e
kernel /boot/vmlinuz-2.6.28-11-generic root=UUID=090a0f73-66c1-44ee-b0d2-c40ac1264a0e ro single
initrd /boot/initrd.img-2.6.28-11-generic
title Ubuntu 9.04, memtest86+
uuid 090a0f73-66c1-44ee-b0d2-c40ac1264a0e
kernel /boot/memtest86+.bin
quiet
### END DEBIAN AUTOMAGIC KERNELS LIST
# This is a divider, added to separate the menu items below from the Debian
# ones.
title Other operating systems:
root
# This entry automatically added by the Debian installer for a non-linux OS
# on /dev/sda1
title Microsoft Windows 2000 Professional
rootnoverify (hd0,0)
savedefault
makeactive
chainloader +1
Windowsの項目の順番が最後になってるので、これを最初になるように先頭辺りにコピペするだけ。
注:間違えておかしなことすると、システムが起動しなくなる。
その時は、インストール用のUbuntuのDVDから直接起動して、もう一度、この場所をエディタで正しく訂正すればいいだけ。
Emenese (MSNメッセンジャーのクローン) のver1.51のインストール方法
Python言語によって書かれてるので、コンパイルの必要はない。(Python2.x系)
システムにPython2.6あたりが入ってる必要がある。
Pythonの3.x系では動かないと思う。(試してないけど。おそらく。ほとんど別言語なので)
SourceForgeから Emesene1.51 のソースをダウンロードしてくる。
http://sourceforge.net/projects/emesene/files/emesene/emesene-1.5.1/emesene-1.5.1.tar.gz/download
ダウンロード先のディレクトリに移動し、gzip で解凍し、tarball を展開する。
gzip -dc emesene-1.5.1.tar.gz | tar xvf -
あとは Python の引数として Emesene を渡せば起動する。
python ./emesene-1.5.1/emesene
余談:
あとは、好きな場所に置いといて、適当にコマンドラインのショートカットを適当に作ればよい。(作らなくてもいい)
とくに決まってはいないが、Emesene のインストール先のディレクトリは/usr/share/あたりに作ればいいと思う。
sudo mkdir /usr/share/emesene/
展開したファイルを、ディレクトリ構造ごと、すべて /usr/share/emesene/ コピーする。
sudo cp -r ./emesene-1.5.1/* /usr/share/emesene/
gnome のメニューにショートカットを設定する。
プログラムの起動パスは
python /usr/share/emesene/emesene
アイコンのパスは
/usr/share/emesene/emesene-logo.png
システムにPython2.6あたりが入ってる必要がある。
Pythonの3.x系では動かないと思う。(試してないけど。おそらく。ほとんど別言語なので)
SourceForgeから Emesene1.51 のソースをダウンロードしてくる。
http://sourceforge.net/projects/emesene/files/emesene/emesene-1.5.1/emesene-1.5.1.tar.gz/download
ダウンロード先のディレクトリに移動し、gzip で解凍し、tarball を展開する。
gzip -dc emesene-1.5.1.tar.gz | tar xvf -
あとは Python の引数として Emesene を渡せば起動する。
python ./emesene-1.5.1/emesene
余談:
あとは、好きな場所に置いといて、適当にコマンドラインのショートカットを適当に作ればよい。(作らなくてもいい)
とくに決まってはいないが、Emesene のインストール先のディレクトリは/usr/share/あたりに作ればいいと思う。
sudo mkdir /usr/share/emesene/
展開したファイルを、ディレクトリ構造ごと、すべて /usr/share/emesene/ コピーする。
sudo cp -r ./emesene-1.5.1/* /usr/share/emesene/
gnome のメニューにショートカットを設定する。
プログラムの起動パスは
python /usr/share/emesene/emesene
アイコンのパスは
/usr/share/emesene/emesene-logo.png
OpenJDK をFireFox3.xで使えるようにする設定
Javaのオープンソース版である OpenJDK を FireFox3.x で使うには、普通にインストールしただけでは使えない。
インストール後に、FireFoxのプラグインディレクトリに手動でリンクを張ればいい。
1.まず普通にインストールする。
sudo apt-get install openjdk-6-jdk
sudo apt-get install openjdk-6-jre
sudo apt-get install icedtea6-plugin
2.FireFoxのプラグインディレクトリに移動する。
cd /usr/lib/firefox/plugins/
3.IcedTeaPlugin.so へのリンクをここに置く。
sudo ln -s /usr/lib/jvm/java-6-openjdk/jre/lib/i386/IcedTeaPlugin.so
これで FireFox3.x でJAVAアプレットが動くはず。
インストール後に、FireFoxのプラグインディレクトリに手動でリンクを張ればいい。
1.まず普通にインストールする。
sudo apt-get install openjdk-6-jdk
sudo apt-get install openjdk-6-jre
sudo apt-get install icedtea6-plugin
2.FireFoxのプラグインディレクトリに移動する。
cd /usr/lib/firefox/plugins/
3.IcedTeaPlugin.so へのリンクをここに置く。
sudo ln -s /usr/lib/jvm/java-6-openjdk/jre/lib/i386/IcedTeaPlugin.so
これで FireFox3.x でJAVAアプレットが動くはず。
emacs23.1.1のコンパイル・インストール (ubuntu9.04)
GNUからソースをダウンロード
http://core.ring.gr.jp/pub/GNU/emacs/emacs-23.1.tar.gz
emacs23の依存関係をDebianやUbuntuなどのページで調べたり、以降の./configureの結果から推測したりして調べる
Ubuntu : http://packages.ubuntu.com/ja/hardy/emacs22
Debian : http://packages.debian.org/ja/sid/editors/emacs23
それらを参考に、必要なライブラリーをダウンロードする
sudo apt-get install libgtk2.0-dev libtiff4-dev libjpeg-dev libgif-dev libXpm-dev librsvg2-dev libgpm-dev libdbus-1-dev libm17n-dev libotf-dev libncurses5-dev
makeinfo がインストール時に必要になるので、あらかじめインストールしておく
sudo apt-get install texinfo
emacs23のダウンロード先のディレクトリに移動し
gzipで解凍し、tarballを展開する
gzip -dc emacs-23.1.tar.gz | tar xvf -
解凍先のディレクトリに移動
cd emacs-23.1
管理者権限でビルドする
Makefileの作成(最適化, core2専用, sse3専用に設定)
sudo ./configure CFLAGS="-O4 -march=core2 -msse3"
コンパイル&リンク
sudo make
無事コンパイルに成功したなら、以下のコマンドで起動するはずなので
確認してみる
./src/emacs
完成したバイナリをシステムにインストール
sudo make install
後はコマンドラインから emacs で起動して使える
余談:
gnomeのメニューには自動では登録してくれないみたいなので、自分で設定する
プログラムの場所は /usr/local/bin/emacs23.1
アイコンの場所は /usr/local/share/emacs/23.1/etc/images/icons/hicolor/scalable/apps/emacs.svg
http://core.ring.gr.jp/pub/GNU/emacs/emacs-23.1.tar.gz
emacs23の依存関係をDebianやUbuntuなどのページで調べたり、以降の./configureの結果から推測したりして調べる
Ubuntu : http://packages.ubuntu.com/ja/hardy/emacs22
Debian : http://packages.debian.org/ja/sid/editors/emacs23
それらを参考に、必要なライブラリーをダウンロードする
sudo apt-get install libgtk2.0-dev libtiff4-dev libjpeg-dev libgif-dev libXpm-dev librsvg2-dev libgpm-dev libdbus-1-dev libm17n-dev libotf-dev libncurses5-dev
makeinfo がインストール時に必要になるので、あらかじめインストールしておく
sudo apt-get install texinfo
emacs23のダウンロード先のディレクトリに移動し
gzipで解凍し、tarballを展開する
gzip -dc emacs-23.1.tar.gz | tar xvf -
解凍先のディレクトリに移動
cd emacs-23.1
管理者権限でビルドする
Makefileの作成(最適化, core2専用, sse3専用に設定)
sudo ./configure CFLAGS="-O4 -march=core2 -msse3"
コンパイル&リンク
sudo make
無事コンパイルに成功したなら、以下のコマンドで起動するはずなので
確認してみる
./src/emacs
完成したバイナリをシステムにインストール
sudo make install
後はコマンドラインから emacs で起動して使える
余談:
gnomeのメニューには自動では登録してくれないみたいなので、自分で設定する
プログラムの場所は /usr/local/bin/emacs23.1
アイコンの場所は /usr/local/share/emacs/23.1/etc/images/icons/hicolor/scalable/apps/emacs.svg




































