2017-09-07 8 views
4

私のC/C++関数にはargtypesを指定しなければならないことは知っています。私の呼び出しの中にはスタックの破損が発生するものがあるからです。実際にすべてのctypeにargtypeがあることを確認する方法はありますか?

myCfunc.argtypes = [ct.c_void_p, ct.POINTER(ct.c_void_p)] 
    myCfunc.errcheck = my_error_check 

、私は、約100の機能のいずれかのコールの関数プロトタイプ(argtypes/errcheck)を指定することを忘れていなかったことを確認したいと思います...

今私はちょうどを通してgrepします私のPythonファイルと視覚的にプロトタイプの定義を含む私のファイルと比較する。

すべての通話に対してargtypes/errcheckを定義したことを確認する良い方法はありますか?

class DummyFuncPtr(object): 
    restype = False 
    argtypes = False 
    errcheck = False 

    def __call__(self, *args, **kwargs): 
     assert self.restype 
     assert self.argtypes 
     assert self.errcheck 

    def __init__(self, *args): 
     pass 

    def __setattr__(self, key, value): 
     super(DummyFuncPtr, self).__setattr__(key, True) 

それはあなたのDLLのオブジェクトの_FuncPtrクラスを交換し、その後に各関数を呼び出して使用するには:ここで

+0

私は 'getattr 'argtypes'、' restype'、 'errcheck'の全てがデフォルト(None、c_long、None;それぞれ)。それらは有効な明示的な設定でもある可能性があるので、そのトリックは機能しませんでした。あなたのargtypesが決してデフォルトでない限り、それはそうです。 –

+1

方法論の問題であるようです。名前空間を使用する。各名前、プロトタイプ、およびオプションのエラーチェック機能を持つリストを使用して、関数ポインタを体系的に定義します。常に 'CDLL'や' WinDLL'インスタンスの代わりに構築された名前空間を使用してください。 – eryksun

答えて

1

@eryksunの名前空間については、明示的に注釈を付けた関数だけを公開するクラスでdllをラップすることができました。限りDLLが関数名(私はしなかった)「注釈」または「_error_check」を持っていないとして、次のようなアプローチは私のために働くようだ:

import ctypes as ct 

class MyWinDll: 
    def __init__(self, dll_filename): 
     self._dll = ct.WinDLL(dll_filename) 
     # Specify function prototypes using the annotate function 
     self.annotate(self._dll.myCfunc, [ct.POINTER(ct.c_void_p)], self._error_check) 
     self.annotate(self._dll.myCfunc2, [ct.c_void_p], self._error_check) 
     ... 

    def annotate(self, function, argtypes, errcheck): 
     # note that "annotate" may not be used as a function name in the dll... 
     function.argtypes = argtypes 
     function.errcheck = errcheck 
     setattr(self, function.__name__, function) 

    def _error_check(self, result, func, arguments): 
     if result != 0: 
      raise Exception 

if __name__ == '__main__': 
    dll = MyWinDll('myWinDll.dll') 
    handle = ct.c_void_p(None) 
    # Now call the dll functions using the wrapper object 
    dll.myCfunc(ct.byref(handle)) 
    dll.myCfunc2(handle) 

アップデート:@によってコメントeryksunは私がWinDLLコンストラクタのユーザーコントロールを与え、繰り返しコードを削減しようとすることで、コードを改善しようと作られた:

import ctypes as ct 

DEFAULT = object() 

def annotate(dll_object, function_name, argtypes, restype=DEFAULT, errcheck=DEFAULT): 
    function = getattr(dll_object._dll, function_name) 
    function.argtypes = argtypes 
    # restype and errcheck is optional in the function_prototypes list 
    if restype is DEFAULT: 
     restype = dll_object.default_restype 
    function.restype = restype 
    if errcheck is DEFAULT: 
     errcheck = dll_object.default_errcheck 
    function.errcheck = errcheck 
    setattr(dll_object, function_name, function) 


class MyDll: 
    def __init__(self, ct_dll, **function_prototypes): 
     self._dll = ct_dll 
     for name, prototype in function_prototypes.items(): 
      annotate(self, name, *prototype) 


class OneDll(MyDll): 
    def __init__(self, ct_dll): 
     # set default values for function_prototypes 
     self.default_restype = ct.c_int 
     self.default_errcheck = self._error_check 
     function_prototypes = { 
      'myCfunc': [[ct.POINTER(ct.c_void_p)]], 
      'myCfunc2': [[ct.c_void_p]], 
      # ... 
      'myCgetErrTxt': [[ct.c_int, ct.c_char_p, ct.c_size_t], DEFAULT, None] 
     } 
     super().__init__(ct_dll, **function_prototypes) 

    # My error check function actually calls the dll, so I keep it here... 
    def _error_check(self, result, func, arguments): 
     msg = ct.create_string_buffer(255) 
     if result != 0: 
      raise Exception(self.myCgetErrTxt(result, msg, ct.sizeof(msg))) 


if __name__ == '__main__': 
    ct_dll = ct.WinDLL('myWinDll.dll') 
    dll = OneDll(ct_dll) 
    handle = ct.c_void_p(None) 
    dll.myCfunc(ct.byref(handle)) 
    dll.myCfunc2(handle) 

元のコードを削除する必要がある場合(私は知らない、私は参照用に保管しました。 )

+1

'** kwds'を' WinDLL'コンストラクタに渡すことができます(例: 'use_last_error')。オプションの関数定義(名前、プロトタイプ、エラーチェック)のリストを表示して、関数ポインタを設定することもできます。これにより、コードの繰り返しがなくなり、大規模なプロジェクトでは一般化されます。一般に、 'annotate'関数とエラーチェック関数は、クラス自体で定義する必要はありません。 – eryksun

+0

@eryksun外部的に 'WinDLL'コンストラクタを呼び出すように更新しました。私の場合は、Windowsの.dllとLinuxの.soファイル(同じAPI)をラップしていますので、実際には 'ct_dll = load_dll(windows = 'myWinDll.dll'、linux = 'myNixSo.so')'となります。また、関数プロトタイプを辞書に定義して、繰り返しコードを排除しようとしました。コメントありがとう! – Amoork

0

は、属性を参照するには簡単なチェックでDLLオブジェクトの関数呼び出しを置き換えることができますダミークラスが定義されていますチェック、例えば実行します。

dll = ctypes.cdll.LoadLibrary(r'path/to/dll') 

# replace the DLL's function pointer 
# comment out this line to disable the dummy class 
dll._FuncPtr = DummyFuncPtr 

some_func = dll.someFunc 
some_func.restype = None 
some_func.argtypes = None 
some_func.errcheck = None 

another_func = dll.anotherFunc 
another_func.restype = None 
another_func.argtypes = None 

some_func()  # no error 
another_func() # Assertion error due to errcheck not defined 

をダミークラスは完全に今までもちろん呼び出されてから機能を防ぎ、これだけ戻って通常の動作に切り替えるために交換用の行をコメントアウト。

関数が呼び出されたときに各関数をチェックするだけであることに注意してください。これは、関数が呼び出されることが保証されているユニットテストファイルに最適です。

関連する問題