Skip to main content

ログ取得ツール

Category: プログラミング

gmpxxメモ

gmpのC++バインディングだが、mpf_class(というかgmpのmpf_tの)デフォルトの有効桁は64桁。デフォルトを変更するにはmpf_set_default_prec(unsigned long)で有効桁を変更する必要がある。しかしC++でmpf_classのコンストラクタが呼ばれた後だとデフォルトを変更しても駄目なので、

int foo(){
  mpf_set_default_prec(1000);
  mpf_class A(10);
}

みたいな順番で書く必要がある。まあ、当然っちゃ当然。でも、C++はちゃんとコンストラクタを後に呼ぶんだな。変数が1つだけとかなら、

  mpf_class A(10);
  A.set_prec(1000);

でも問題はない。 gmpには実数(分数)型ってないのかな。無限桁の分数型があれば誤差ゼロの演算が可能になるのだが。…なくても、mpzを2つ使ってすぐに書けそうだけど。 (追記) 2004-06-15 10:23 あれ、分数だと実数じゃなくて有理数って言うんだっけ? 無理数は計算機で表現できるのだろうか。 (追記) 2004-06-15 21:41 非常に手抜きですが分数型を書いてみました

# g++ -D__MAIN__ -g -o fractint fractint.C -lgmpxx
# ./fractint 1020/234 222/555
fr1 = 170/39
fr2 = 2/5
+: 928/195
-: 772/195
/: 425/39
*: 68/39

C++もたまにはけっこう楽できるな、と思える。

未熟にもしてやられた

よくマクロでdo{ ... }while(0);という書き方がされている。単にスコープを作るだけなら{ ... }のほうが見やすいと思う。しかし、do{ ... }while(0);{ ... }は違う。 何が違うって、do{ ... }while(0);はあくまでもループなので、continue;break;の抜け先が異なるのだ。 展開された状態で

  for(i=0; i<100; i++){
    do{
      printf("Hello, World %d\n", i);
      if(i==10) break;
    }while(0);
  }

は100回ループを回るが、

  for(i=0; i<100; i++){
    {
      printf("Hello, World %d\n", i);
      if(i==10) break;
    }
  }

は10までしか表示されない。どちらが望ましいかはケースによって異なる。この違いを認識しないでいろいろなマクロを書いていると、わけがわからないバグとしていつか表面に現れるだろう…っていうか、実際に(もうちょっと複雑なケースで)バグったから書いてるんだけど(笑)。 実際にバグったのを説明すると、リストをたどる用のマクロ(foreach〜forendで囲むタイプのもの)というのを使ってて、do{ ... }while(0);じゃなくて{ ... }を使っていた。で、次の要素に移るとか、forendのほうでもちょっと処理していた。で、ループの中でcontinueを書いてもcontinueになってなかったのだ。forend側の}の前に書いてあった、いわゆるi=i->next;の処理が実行されない。do{ ... }while(0); i=i->next;のようにcontinueで飛びたい部分を囲むとよい。ただこれじゃ、breakで抜けられなくなっちゃうのだけどね。要はcontinueとbreakのどちらを取るかって話だが、for文を使ってi=i->nextな部分をforに押し込めるのが正しいと思う。あるいはbreakとcontinueだとbreakのほうが使うから、continueを禁止するか。

Pythonの連想配列のfor

Pythonでいつも忘れて変なことになるのでメモしておく。 連想配列(辞書)をforで受けるとkeyしか出てこない、という話。

a={"key1":"val1", "key2":"val2"}
for i,j in a:
  print i, j

と書くとエラー。fori,jを受け取れない。Perlから来た人は、ここでもうブチ切れですよ(うそ)。しょうがないから

a={"key1":"val1", "key2":"val2"}
for i in a:
  print i, a[i]

などと書くと素人なのだ(私は思い出せなくていつもこうなってしまう)。正しくは、

a={"key1":"val1", "key2":"val2"}
for i,j in a.items():
  print i, j

である。items()を忘れるな、ということだ。おもむろにdir({1:2})とやってメソッドのリストを眺めて思い出そう。iteritems()でイテレータがもらえる。

SO_REUSEADDR

いつまで待ったらSO_REUSEADDRがデフォルトで有効になるのか。このフラグを立てずにサーバを書いている人がいるとは思えない。何度Address already in useを見てうんざりしたことか。 Pythonの場合、$pythonlib/SocketServer.pyにあるSocketServer.TCPServerallow_reuse_addressをTrue(1)に設定すればよい(デフォルトはFalse(0)だ)。これはクラス変数であるから、派生クラスで値をTrueに設定すればsocket()bind()の間で

socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

を呼んでくれる。つまり、例えばSimpleXMLRPCServerだと、

import SimpleXMLRPCServer
SimpleXMLRPCServer.SimpleXMLRPCServer.allow_reuse_address=True
sv=SimpleXMLRPCServer.SimpleXMLRPCServer(("localhost", 9999))

という感じになる。BaseHTTPServer.pyのHTTPServerではクラス定義の部分でデフォルトでTrueに設定している。ここは、あえてTCPServerのレベルでデフォルトをTrueに設定すべきと思う。 …まあ、こんなこと書くのは、書き忘れて使えるようになるのを待ってるからなんですけどね(笑) (追記) 2004-06-08 08:50 いまさら補足。後で使うほうのプログラムでTIME_WAIT状態のソケットを破棄してアドレスを再使用するというものなので、後で起動したほうのプログラムがSO_REUSEADDRに設定すればよい。本来は待たなくてもよい状況なのです。 そういう性格のものなので、setsockopt()をsocket()とbind()の間で呼ばなければならず、後で呼んでも効果はない。最初はTCPServer(すでにlisten中)のインスタンスに対してsetsockopt()を呼んでしまって効果のなさに戸惑う。 というわけで、Pythonの実装がこれをクラス変数にしたというのは当然なのである(ついでに言えば、Pythonではクラス変数とインスタンス変数に区別はないはず)。

だ、だまされた!?

header("Last-Modified: ほげほげ");を投げとけばPHP(というかApache HTTP Serverか?)が勝手にユーザのIf-Modified-Sinceを処理して304にするか200にするかを判断してくれると思っていた私が間違いだった。 自力でユーザの投げたヘッダを解析して304か否かを判断せねばならないらしい。そりゃないぜ、と思う。それって、あっちゃいけないんじゃ? とりあえずこのページは暫定対処してみたが、こんな処理を毎回書くことが正しいとは思えない。PHPのモジュールのコードを読んでみようとひそかに思った。PHPのインタプリタがheader("Last-Modified: ほげほげ");を検出した時点で判断して、古ければ勝手に304を返してexit、というのがユーザの想定する処理だろうと思う。

gcc 3.3以降の文字列の扱い

改行を含んだ文字列の扱いが変わったみたい。 gcc-3.2.xでは

printf("test
this is a
TEST\n");

で問題なかったが、gcc-3.3とかgcc-3.4では

printf("test"
this is a
TEST\n");

に変換されて(最初の行に余計な**"**がついてしまうので文の意味を成さなくなる)しまう。余計なことを! と私は思うけども、Cの標準がそうなのかなぁ。よくわかんないけど、けっこう迷惑。

printf("test\n"
"this is a\n"
"TEST\n");

と書けと? (追記) 2004-04-15 23:42 gccというよりも、cppの段階で変換されてしまうのだ。

Pythonメモ:distutils.sysconfig

# python -c 'from distutils.sysconfig import *;print get_python_inc()'
# python -c 'from distutils.sysconfig import *;print get_python_lib()'

まあ、そんだけなんだけど、書いとかないと覚えてらんないでしょ。

名前の問題

swigメモ。いつか問題が解決するだろうと信じている。 ライブラリって、swigでPythonからも使えるようにするじゃん。このとき、小さいライブラリだと、「ライブラリの名前=モジュールの名前=唯一の構造体の名前」っていうことも珍しくない。例えば"name"だとすると、C側の関数名もname_func1()にするでしょ。こうしたときに、ややこしいから整理すると、

typedef struct name{
  int member;
}name;

name *name_init(int id);
void name_exit(name *arg);
int name_func1(name *arg);
int name_func2(name *arg, int arg2);

がname.hで定義されているとする。これをswigのソースname.iで

// swig -python name.i
// gcc -c name_wrap.c -I/usr/include/python2.2 -I.
// gcc -shared name_wrap.o -lname -L. -o _name.so
%module name
%{
#include "name.h"
%}
%include "name.h"

とするとする。使っている側のココロとしては、

if a=name.init(100):
  a.func1()
  a.func2(1)

だと思う。しかし実際できあがってみると、

if a=name.name_init(100):
  name.name_func1(a)
  name.name_func2(a, 1)

という悲しい状況になる。これを回避するための方法は%renameを使う方法である。name.iで

%rename(init) name_init;
%rename(func1) name_func1;

などとしておくと、

if a=name.init(100):
  name.func1(a)
  name.func2(a, 1)

まではとりあえずあっさりスッキリシャッキリするわけだ。しかしaというオブジェクト、おまえはどういうつもりなのか。C struct以上の機能はないのか。そこで、name.iにこう書き加える(name構造体がPythonのクラスになったものにメンバを追加する)。

節穴で真実を見る

セキュアなコードの書き方 - セキュアな設計が最も重要(mycom.co.jp)より、MSの人が見抜けなかったバグ。

1:template 
2:
3:static T MaxInt() {
4: if( IsSigned() ) {
5:  return (T)~( 1 << ( BitCount()-1 ) );
6: }
7: //else
8: return (T)(~(T)0);
9:}

たしかにわからん。 正解は、5行目の左側の1をTにキャストしてからシフトしないと普通のintのシフトになってビットが消えちゃう、というものらしい。なるほどねぇ。