Hatena::ブログ(Diary)

0xfeeb - endless loop

2009-10-18

[][]デフォルト引数で失敗した

2009-10-19 追記

コメントで指摘してもらっているようにデフォルト引数ではなくて、キーワード引数の問題だった。

よく分からないものを無理して使った上に、ブログに間違いを載せるとか、恥の上塗りでしかなかった。



最近は特に勉強もせず、ネトゲやったりしてダラダラ過ごしてしまっている。

そんな中、ちょっとしたミスからなかなかバグがとれなかったので、ここに晒して戒めとしよう。

それは似たようなコードをC++Pythonで使い回していて、次のような関数デフォルト引数を使ったコードで発生した。

# Python

def func(a = 10):
	print(a)

def main():
	a = 20

	func(a = 30)
	print(a)

if '__main__' == __name__:
	main()
// C++

#include <iostream>

using namespace std;

void func(int a = 10) {
	cout << a << endl;
}

int main() {
	int a = 20;

	func(a = 30);
	cout << a << endl;

	return 0;
}

出力は以下の通り。

Python
    30
    20

C++
    30
    30

このコードなら大した話ではないので間違いにはすぐ気がつくが、他のロジックに混ざっていると突然変数の中身が変化したような錯覚に。

根本的には、変数名の付け方が悪かったことに起因するんだろうなぁ。

2009-07-16

[]ctypesで関数ポインタを扱う

ctypesを使うとDLLが呼び出せることを前に書いた。

こんな感じで。

from ctypes import *

user32 = windll.user32
user32.MessageBoxW(0, u'Python ctypes test', 0, 0)

ctypesの便利さはこんなものじゃない。

Python関数関数ポインタのように扱って、callback関数にしたり、CreateThreadしたりできる。

from ctypes import *


user32 = windll.user32
kernel32 = windll.kernel32


def callbackFunction():
	user32.MessageBoxW(0, u'MessageBox from callback function', 0, 0)
	return 0


functionType = CFUNCTYPE(c_int)
functionPointer = functionType(callbackFunction)
threadId = c_ulong(0)

threadHandle = kernel32.CreateThread(0, 0, functionPointer, 0, 0, byref(threadId))
result = kernel32.WaitForSingleObject(threadHandle, 60*1000)

if result:
	print('Timeout')
else:
	print('Thread exited')

ctypesのおかげでWin32 API使ったコードでも、こんなに簡単にPythonだけで書くことが出来るようになっている。

2009-07-14

[]ctypesの三つのロード方法

ctypesではDLLのロード方法が三つある。

cdll、windll、oledllの三つだが、その違いはcallされた時のスタックの扱いと、戻り値だ。

ややっこしいのでまとめてみる。

cdllで呼び出すべき関数

  • cdecl呼び出し規約に従う
  • intを返す

windllで呼び出すべき関数

  • stdcall呼び出し規約に従う
  • intを返す

oledllで呼び出すべき関数

  • stdcall呼び出し規約に従う
  • HRESULTを返す

cdeclとstdcallの違いは、Wikipediaが詳しい。

要は、引数をスタックにpushして関数を呼び出すのは同じだが、そのスタックを元に戻す責任があるのが、呼び出された関数側なのか、呼び出した側なのかの違い。

2009-07-13

[]ctypesを使ってみる

ctypesを使うとPythonからDLLや、Shared libraryを呼び出すことが出来る。


ctypes_test.py

from ctypes import *

msvcrt = cdll.msvcrt
message = "Python ctypes test\n"
msvcrt.printf(message)

ポインタや構造体、ユニオンもちゃんと定義できる。

以下は型の対応表。


ctypes typeC typePython type
c_charchar1-character string
c_wcharwchar_t1-character unicode string
c_bytecharint/long
c_ubyteunsigned charint/long
c_shortshortint/long
c_ushortunsigned shortint/long
c_intintint/long
c_uintunsigned intint/long
c_longlongint/long
c_ulongunsigned longint/long
c_longlong__int64 or long longint/long
c_ulonglongunsigned __int64 or unsigned long longint/long
c_floatfloatfloat
c_doubledoublefloat
c_longdoublelong doublefloat
c_char_pchar * (NUL terminated)string or None
c_wchar_pwchar_t * (NUL terminated)unicode or None
c_void_pvoid *int/long or None