qmailとsudoの相性の問題?
先日、学科のメインサーバのアップデートで、sudoなども更新された。 これでMovableTypeの記事をメールで書き込む mail2entry.pl の動きがおかしくなった。
具体的には、今までは、sudo を使って www-data 権限を得て 記事を書き込んでいた。
(( .qmail-XXXX )) | /usr/bin/sudo -u www-data /usr/lib/cgi-bin/movabletype/mail2entry.pl
しかし、sudo-1.8.3p2 では、記事書き込み終了後に、mail2entry.pl が ZOMBIE になって処理が終わらず、上位プロセス sudo , qmail-local が残って しまう。このため、一定数以上のmovabletype記事書き込みが溜まると、 メールシステム全体がメール配達が止まったり遅配となっていた。
どうも、sudo の問題みたいなので、sudo を使わずに処理を行うように、 設定を変更した。具体的には、/var/qmail/users/assign によって、 www-data 権限を与えて動かすようにした。
(( /var/qmail/users/assign )) +mt-XXXX:www-data:33:33:/var/qmail/alias:-:mt-XXXX: (( /var/qmail/alias/.qmail-mt-XXXX )) | /usr/lib/cgi-bin/movabletype/mail2entry.pl (( 設定 )) # chmod 644 /var/qmail/alias/.qmail-mt-XXXX # qmail-newu # /etc/init.d/qmail restart
純粋仮想基底クラスと関数ポインタ
先週の仮想関数の説明の応用ということで、 コンテナクラスなどの話につなげるための純粋仮想基底クラスの説明を行う。 その前に、仮想関数の実装の元となっている関数ポインタを簡単に紹介する。
関数ポインタ
関数ポインタとは、名前の通りで、動作説明の簡単なプログラムを紹介。
int add( int x , int y ) { // 加算関数 return x + y ; } int mul( int x , int y ) { // 乗算関数 return x * y ; } void main() { int (*f)(int,int) ; // int×2引数、返り値intの関数へのポインタ f = add ; printf( "%d" , (*f)( 2 , 3 ) ) ; // 5を表示 f = mul ; printf( "%d" , (*f)( 2 , 3 ) ) ; // 6を表示 }
関数ポインタを利用すれば、異なるデータに対する処理を、 汎用性高く作ることも可能となる。
int intcmp( int* x , int* y ) { // 整数比較関数 if ( *x > *y ) return 1 ; else if ( *x < *y ) return -1 ; else return 0 ; } int vmax( void* array , // 配列先頭アドレス int size , // 配列データ件数 int sizeofdata , // 1件あたりのbyte数 int(*f)( void*,void* ) ) { // 比較関数 int i , max = 0 ; for( i = 0 ; i < size ; i++ ) if ( (*f)( array + max * sizeofdata , array + i * sizeofdata ) ) max = i ; return max ; } int idata[ 4 ] = { 11 , 33 , 22 , 44 } ; char sdata[ 4 ][ 4 ] = { "ab" , "bc" , "aa" , "c" } ; void main() { int m ; // intcmp関数を使って、idata から最大値を探す m = vmax( idata , 4 , sizeof(int) , intcmp ) ; printf( "%d" , idata[ m ] ) ; // strcmp関数を使って、sdata から最大値を探す m = vmax( sdata , 4 , sizeof(sdata[0]) , strcmp ) ; printf( "%s" , sdata[ m ] ) ; }
純粋仮想基底クラス
これらの仮想基底クラスの考え方をもっと利用すると、 1つの配列に、異なる型の派生クラスを保存することも可能となる。
// 純粋仮想基底クラス class Object { public: virtual void print() = 0 ; } ; // 整数クラス class IntObject : public Object { private: int data ; public: IntObject( int x ) { data = x ; } virtual void print() { printf( "%d" , data ) ; } } ; // 実数クラス class DoubleObject : public Object { private: double data ; public: DoubleObject( double x ) { data = x ; } virtual void print() { printf( "%f" , data ) ; } } ; // 文字列クラス class StringObject : public Object { private: char* data ; public: StringObject( char* x ) { data = x ; } virtual void print() { printf( "%s" , data ) ; } } ; void main() { Object* a[ 3 ] ; // 配列に整数・実数・文字列が混在できる。 a[0] = new IntObject( 123 ) ; a[1] = new DoubleObject( 1.23 ) ; a[2] = new StringObject( "abc" ) ; // 混在したデータでも、正しく全要素をprint()できる for( int i = 0 ; i < 3 ; i++ ) a[ i ]->print() ; }
2012年5月20日(第269回)
- 通学について
- 体育祭の裏話
- クラブ紹介 硬式テニス部
担当:前田(3EI)、松島(1C)、山野(1C)、西(教員)
前川先生のエコ実践
前川先生は、卒業研究でも太陽電池パネルの太陽光追尾システムを構築し、 常時動かしている電波実験設備の給電にも利用している。
機械工業会さんとの連携事業でエコシステム構築で、追尾システムの お話を聞いていたら、太陽電池をさらに興味深いエコ利用を 試されているみたい。


太陽光発電で自宅菜園の貯水システム
休耕田の自宅菜園の散水用の水を、太陽光発電で貯水する実験をしているとのこ とであった。


(前川先生): 休耕田で野菜を作っています。 夏場の水やりのために自動車用バッテリーとインバータ でバスポンプを動かして、田んぼの中に置いた水槽に水をためています。
バッテリーにソーラーパネルをつないで充電をしています。 田んぼの中に置いてある水槽は、80リッターのもので1980円でした。 バスポンプは、直接12Vで動かすことも出来、約1A電流が流れます。 1000円ほどのポンプで1分8リットルから10リットルの水を吸い上げられます。 2000円だとその倍の水が汲めます。 5000円も出すとさらに大きいものがあります。
放っておくと水があふれるので、タイマーを付けようと考えています。 今のところ水槽とホースを3組用意しています。
Arduino+Xbee Shield/Pr…(05/18)
- 05/18 Arduino+Xbee Shield/Processing+XBee Explorer USB #fnct http://kousaku-kousaku.blogspot.jp/2008/…
- 05/18 XBeeエクスプローラUSB http://www.switch-science.com/products/d… #fnct
この記事は、 の @TohruSaitohに掲載した #fnct タグ付き記事を、まとめたものです。
60%の人間はプログラミングの素質がない…
個人的には、なかなか、的を得た数字のように思うな…
「ふたこぶラクダ」という名前の有名な論文に書かれているんだってさ。
引用:プログラミングの素質は、構築したメンタルモデルを、 ブレずに一貫して適用できるかどうかにかかっているようだ。
それぞれのプログラミング言語の都合に合わせて、動くようにプログラムを書くのだから、 わけのわからない言語のルールに、ブレずにしたがって頭の中で動く様をシミュレートできるか…って感じかな。
引用:一貫したグループにプログラミングを教育するのは、はるかに簡単である。 このグループは、観測的に、さらにふたつに分かれるようだ。 ひとつは、プログラミングを非常に簡単に感じ、プログラミングを楽しみ、 その後も成長してソフトウェアを書く良いプログラマーになるグループ。 もうひとつのグループは、プログラミングはできるものの、 それ自体には楽しみを見出さず、管理職になってUML図に溺れるグループ(やれやれ)。
爆笑….
ヒープメモリの演習
先週のmalloc+freeの説明後ということで、今日は演習の時間とした。
ヒープメモリ演習テーマ
大きさの解らないデータを入力し、何らかの処理を加えて出力する。 例えば、きわめて長い名前の人がいるかもしれないクラスの名簿を読み込む。 名簿の人数は、プログラム作成時は解らないものとする。 もしくは、成績表の集計を行うプログラムを作る。ただし、科目数や対象人数は変更できること。 演習の自信の無い人は、プログラムを実行し処理を始める前に、 データ件数を入力などを入力するという方法でもよい。
途中で配列サイズを変えるテクニック
最初にデータ件数などを入力するのは、現実の処理ではあまりない。 プログラミングでよく使われるテクニックは、 あらかじめ適当な大きさで配列を作り、その中にデータを保存していく。 途中で配列サイズからあふれる場合は、新しく2倍の大きさの配列を確保し、 新しい配列に、元の配列の中身をすべてコピーする。 そのあと、配列を指すポインタを、2倍の配列側に切り替え、元々の配列はfreeで廃棄する。
C++でのnewとdelete
前回の授業では、malloc+freeを用いたけど、最近はオブジェクト指向のC++を使うことが多い。 簡単に、new-deleteの使い方を説明する。
// C言語であれば int size = 適当な大きさ ; int *p ; if ( (p = (int*)malloc( sizeof( int ) * size )) != NULL ) { pを配列として使った処理... ; free( p ) ; } // C++であれば、 p = new int[ size ] ; // C++ではメモリ確保失敗は例外処理で対応 pを使った処理... ; delete [] p ; // [] は配列をdeleteする場合のみ
deleteの[]をつける意味が分かりにくいので、普通の使い方の例を、おまけで書いておく。
struct A { なんらかの要素 ; } *p ; // C言語の場合 if ( (p = (struct A*)malloc( sizeof(struct A) )) != NULL ) { pの構造体を使った処理 ; free( p ) ; } // C++の場合 p = new A ; pの構造体を使った処理 ; delete p ;
ポインタ処理と演習
前回のN進数変換のプログラム演習で、 まだまだ完成していない人が多いようなので、前半で説明・後半を演習とした。
ポインタ演算の説明として、以下のような例をしめす。 関数とのポインタ渡しなどは説明済み。
int a[ 4 ] = { 11 , 22 , 33 , 44 } ; int*p = a ; printf( "%d" , *p + 1 ) ; // 11 + 1 = 12 printf( "%d" , *(p + 1) ) ; // ひとつ後ろの場所22 p += 2 ; printf( "%d" , *p ) ; // 参照場所を2つ後ろに移動33 printf( "%d %d" , *(p + 1) , p[ 1 ] ) ; // 44,44 ポインタ記述 printf( "%d %d" , *(p - 1) , p[ -1 ] ) ; // 22,22 と配列記述
また、ポインタを動かしながら参照などの理解をするために、 前置インクリメント、後置インクリメントの違いを説明。
// ポストインクリメント int x = 1 ; printf( "%d" , x++ ) ; // printfの後に増やす。 printf( "%d" , x++ ) ; // 1 2 を表示 // プレインクリメント int x = 1 ; printf( "%d" , ++x ) ; // printfの前に増やす。 printf( "%d" , ++x ) ; // 2 3 を表示
以上のネタが解かった所で、配列加算とstrcpyの例を示す。
// 配列で0までの数値を加算する int a[ 4 ] = { 12 , 23 , 34 , 0 } ; int s = 0 ; for( i = 0 ; a[i] != 0 ; i++ ) s += a[i] ; // forの部分をポインタ移動に書き換え int *p = a ; while( *p != 0 ) s += *p++ ;
C言語の標準関数のstrcpyの中身を色々な書き方で示す。返り値はひとまずvoidにて… これにより、ポインタの移動処理と、条件判定が0/0以外であることを説明する。
void strcpy( char*t , char*s ) { for( int i = 0 ; s[i] != '¥0' ; i++ ) t[i] = s[i] ; } // forの部分を次々と書き変える for( ; *s != '¥0' ; s++ , t++ ) // コンマ演算子にも注意 *t = *s ; while( *s != '¥0' ) // ポストインクリメントを使って *t++ = *s++ ; while( (*t++ = *s++) != '¥0' ) ; // 条件内で代入 while( *t++ = *s++ ) ; // 偽=0,真=0以外
継承と仮想関数
オブジェクト指向の授業での、前回までのカプセル化の次の段階として、 継承について説明を行う。
継承(派生)
データを扱っている際に、基本的な部分では共通性があるけど、 様々なバリエーションが存在するデータには、継承(派生)が使われる。
例として人の情報に、親子関係を表すデータを追加する場合を考える。 これを構造体で実装すると、追加されたデータ側で、同じような処理を 沢山書く必要がでてくる。
// C言語ベースで破綻する例 // 元となるデータ構造 struct Person { char name[ 10 ] ; int age ; } ; void setPerson( struct Person* p , char* s, int a ) { strcpy( p->name , s ) ; p->age = a ; } void printPerson( struct Person* p ) { printf( "%s %d" , p->name , p->age ) ; } // 子どもの情報をもつ拡張したデータ struct Parent { struct Person base ; struct Person* child ; } ; void setParent( struct Parent* p , char* s , int a , struct Person* c ) { setPerson( p , s , a ) ; child->child = c ; } void main() { struct Person mitsuki ; setPerson( &mitsuki , "mitsuki" , 12 ) ; struct Parent tohru ; setParent( &tohru , "tohru" , 47 , &mitsuki ) ; printPerson( &mitsuki ) ; printParent( &tohru ) ; // printParent() を書く必要あり。 // 名前と年齢を表示したいだけなのに... }
これと同じようなプログラムは、C++であれば派生を使って簡単にかける。
class Person { // 基底クラス private: char name[ 10 ] ; int age ; public: Person( char*s , int a ) { strcpy( name , s ) ; age = a ; } void print() { printf( "%s %d" , name , age ) ; } } ; class Parent : public Person { // 派生クラス private: Person* child ; public: Parent( char* s , int a , Person* c ) : Person( s , a ) // 基底クラスのコンストラクタの呼び出し { child = c ; } } ; void main() { Person mitsuki( "mitsuki" , 12 ) ; Parent tohru( "tohru" , 47 , &mitsuki ) ; mitsuki.print() ; tohru.print() ; // 継承により"tohru 47"が表示できる }
上記のtohru.print() の様に、基底クラスの同名メソッドを流用できることを、 継承と呼ぶ。
仮想関数
上記の派生クラスで、Parentを表示する時に、子どもも一緒に表示させたければ、 以下の様に派生クラスでprint()を再定義しても良い。
class Parent : public Person { : public: void print() { Person::print() ; // 基底クラスメソッド呼び出し child->print() ; } } ; void main() { : tohru.print() ; // "tohru 47 mitsuki 12" の表示。
しかし、親子関係がもう1世代加わるとどうなるか?
void main() { Person mitsuki( "mitsuki" , 12 ) ; Parent tohru( "tohru" , 47 , &mitsuki ) ; Parent kinmatsu( "kinmatsu" , 78 , &tohru ) ; // 型の不一致? }
本来なら、&tohru は、Parent*型。 Parent::Parent(char*,int,Person*)コンストラクタ第3引数は、Person*型で型が違う。 しかし、オブジェクト指向では、 派生クラスのポインタは、安全に基底クラスのポインタに暗黙の変換が可能なので、 問題は無い。
一方で、以下の命令を実行した場合、何が表示されるのか?
kinmatsu.print() ; // kinmatsu 78 tohru 47 までしか表示されない。
これは、Parent型のchildは、Person型であり、tohruを表示しようとしても、 すでに"Person"の子供なしと思われてしまっている。 ここで、tohru は、元々Parent型であり、孫のmitsuki まで表示したい場合は、 どうすべきか?データ自信が、自分は"Person"なのか"Parent"なのか知っていれば、都合がよい。こういう場合は、仮想関数にて宣言する。
class Person { : private: virtual void print() { printf( "%s %d" , name , age ) ; } } ; class Parent : public Person { : private: virtual void print() { Person::print() ; child->print() ; // 仮想関数呼び出し } } ;
この例では、kinmatsu.print() を実行する時に、child->print() を実行する場合には、 tohruは、Parent型なので、Parent::print() を呼び出し、tohru.print()の時には、 mitsukiは、Person型なので、Person::print() を呼び出してくれる。
"virtual"を使うと、データ構造の中に、型情報が自動的に埋め込まれるため、 このような、データに応じた処理の呼び分けが可能となる。
質問で、virtual のキーワードを一方にしか付けなかったらどうなるの? ということで、調べてみた。 基底クラスで virtual 付き、派生クラスで virtual 無しだと、両方に付けた時と同じ動きとなった。資料をみると、派生クラスでは virtual は書かなくてもいいとのことであった。 一方、基底クラスで virtual 無し、派生クラスで virtual 有りだと、 処理の呼び分けができなかった。 うーん昔、書き間違えてvirtual忘れた時には、エラーが出た記憶があるのだが…