9

私は、C++ で記述されたライブラリとのインターフェイスを試みるために、Cython を使用してきました。これまでのところ順調に進んでおり、ライブラリ内で MOST 関数を効果的に使用できます。私の唯一の問題は、コールバックの実装にあります。ライブラリには、次のような 4 つの関数定義があります。

typedef void (*Function1)(const uint16_t *data, 
                         unsigned width, unsigned height);
void SetCallBack(Function1);

したがって、それらを実装するには、cython で次のようなことを行うと考えました。

ctypedef void (*Function1)(unsigned short *data, 
                         unsigned width, unsigned height);
cdef extern from "lib.hpp":
    void SetCallBack(Function1)

これは実際には正しくコンパイルされますが、コールバックが機能するように実際にそれを実装する方法を考えることができません。最初に、他の関数の場合と同様に、それを呼び出すだけの関数を作成しようとしましたが、次のようになりました。

def PySetCallBack(Func):
    SetCallBack(Func)

しかし、それは私に(予測可能な)エラーを与えます:

「Python オブジェクトを 'Function1' に変換できません」

そうそう、それが私がいるところです。Cython でコールバックを設定した経験のある方がいらっしゃいましたら、よろしくお願いいたします。ありがとう。

編集:あなたのアドバイスに従って、次のようなcdefを使用して中間関数を作成しました:

cdef void cSetCallBack(Function1 function):
    SetCallBack(function)

これは私を手に入れたようです...もっと近くに?少なくとも今は別のエラーが発生しています:

error: invalid conversion from ‘void (*)(short unsigned int*, unsigned int, unsigned int)’ to ‘void (*)(const uint16_t*, unsigned int, unsigned int)’

今、私が知る限り、これらのタイプは同一であるため、何が起こっているのかわかりません.

Edit2 : 新しい typedef を宣言することでその問題を修正しました:

ctypedef unsigned short uint16_t

それを呼び出す引数として使用しますが、実際にはそれ以上近づいていないようですが、その関数を呼び出そうとすると、同じ「Pythonオブジェクトを「Function1」に変換できません"エラーが再び発生しました。

というわけで、ほぼスタート地点に戻ってきました。私が今できることは、そのような ac 関数として入ってくる python オブジェクトを明示的にキャストすることだけですが、正直なところ、どうやってそれを行うのかわかりません。

3番目を編集してください:さて、あなたの答えを分析した後、私はついにそれを手に入れました、そしてそれはうまくいきました。私がやったことは、次のような関数を作成することでした:

cdef void cSetCallback(Function1 function):
    SetCallback(function)
cdef void callcallback(const_ushort *data, unsigned width, unsigned height):
    global callbackfunc
    callbackfunc(data,width,height)
cSetCallback(callcallback)
def PySetCallback(callbackFunc):
    global callbackfunc
    callbackfunc = callbackFunc

したがって、唯一の問題は const_ushort *data を python オブジェクトに変換できないことですが、それはまったく別の問題であるため、これは解決されたと思います。どうもありがとうございました。

4

2 に答える 2

9

私は最近、イベント/コールバックを集中的に使用して、Cython を使用して既存の C++ ライブラリを Python とインターフェースしなければならない状況にありました。これに関するソースを見つけるのはそれほど簡単ではありませんでした。ここにすべてをまとめたいと思います。

まず第一に、ラッピング C++ コールバック クラス ('double ( METHOD)(void )' プロトタイプに基づいていますが、Cython はテンプレートを処理できるため、テンプレート化されている可能性があります):

ALabCallBack.h :

#ifndef ALABCALLBACK_H_
#define ALABCALLBACK_H_


#include <iostream>

using namespace std;

namespace elps {

//template < typename ReturnType, typename Parameter >
class ALabCallBack {
public:

    typedef double (*Method)(void *param, void *user_data);

    ALabCallBack();
    ALabCallBack(Method method, void *user_data);
    virtual ~ALabCallBack();

    double cy_execute(void *parameter);

    bool IsCythonCall()
    {
        return is_cy_call;
    }

protected:

    bool is_cy_call;

private:

    //void *_param;
    Method _method;
    void *_user_data;

};


} /* namespace elps */
#endif /* ALABCALLBACK_H_ */

ALabCallBack.cpp :

#include "ALabCallBack.h"

namespace elps {


ALabCallBack::ALabCallBack() {
    is_cy_call = true;
};

ALabCallBack::~ALabCallBack() {
};

ALabCallBack::ALabCallBack(Method method, void *user_data) {
    is_cy_call = true;
    _method = method;
    _user_data = user_data;
};

double ALabCallBack::cy_execute(void *parameter)
{
    return _method(parameter, _user_data);
};


} /* namespace elps */

どこ :

  • 'callback' :: C 型情報から Python (=Method) オブジェクト メソッドを起動するパターン/コンバーター メソッド

  • 'method' :: Python ユーザーから渡された有効なメソッド (=user_data)

  • 'parameter' :: 'method' に渡されるパラメーター

次に、.pyx ファイルを実装する必要があります...

私たちの基本プロトタイプ:

ctypedef double (*Method)(void *param, void *user_data)

次に、C++ クラスの Cython ラッパーを提供します。

cdef extern from "../inc/ALabCallBack.h" namespace "elps" :
    cdef cppclass ALabCallBack:
        ALabCallBack(Method method, void *user_data)
        double cy_execute(void *parameter)

C 型のプロトタイプを Python オブジェクト呼び出しに変換するために使用されるパターン/コンバーター メソッド:

cdef double callback(void *parameter, void *method):
    return (<object>method)(<object>parameter)

次に、この機能を Cython クラスに埋め込みましょう:

cdef class PyLabCallBack:
    cdef ALabCallBack* thisptr

    def __cinit__(self, method):
        # 'callback' :: The pattern/converter method to fire a Python 
        #               object method from C typed infos
        # 'method'   :: The effective method passed by the Python user 
       self.thisptr = new ALabCallBack(callback, <void*>method)

    def __dealloc__(self):
       if self.thisptr:
           del self.thisptr

    cpdef double execute(self, parameter):
        # 'parameter' :: The parameter to be passed to the 'method'
        return self.thisptr.cy_execute(<void*>parameter)

編集:実行機能のより良い入力:def execute => cpdef double

それでおしまい。そのようなことをするように呼び出します:

def func(obj):
    print obj 
    obj.Test()     # Call to a specific method from class 'PyLabNode'
    return obj.d_prop

n = PyLabNode()    # Custom class of my own
cb = PyLabCallBack(func)
print cb.execute(n)

Python は暗黙的に型指定されているため、コールバックを起動するときに、引数として渡されたオブジェクトのクラスに関連する「obj」オブジェクトのプロパティにアクセスできます。

純粋な C 実装に非常に簡単に適応させることができます。これに対する何らかの機能強化が見られるかどうか教えてください (私の場合、イベントが集中的に発生するため、パフォーマンスは非常に重要です)。

于 2012-10-09T22:12:43.067 に答える
5

ライブラリを変更して定義できる場合:

typedef void (*Function1)(const uint16_t *data, 
                          unsigned width, unsigned height,
                          void *user_data);
void SetCallBack(Function1, void*);

代わりに、あなたは運が悪いのではないかと心配しています。がある場合は、void*正しい引数とSetCallBackこの関数と python 呼び出し可能オブジェクトを使用して、python 呼び出し可能オブジェクトを呼び出す関数を定義します。

できないが、コールバックがグローバルである場合 (そのようです)、Python オブジェクトを格納するグローバル変数を作成できます。Python オブジェクトを呼び出してそれを渡す関数を再度作成するよりもSetCallBackPySetCallbackグローバルを設定し、適切な関数が登録されていることを確認します。

コールバックがコンテキスト固有であるが、それを「ユーザー データ」ポインターに渡す方法がない場合、ここで運が悪いのではないかと心配しています。

Python と C++ は知っていますが、cython は知らないので、関数を cython で作成できるかどうか、または C++ で作成する必要があるかどうかはわかりません。

于 2011-03-09T07:10:43.893 に答える