Pythonからcへの構造体の配列をパスします
-
22-09-2019 - |
質問
更新:問題が解決しました!投稿の底を参照
Python開発者が、Python C APIを介して手動で露出した一連のC ++インターフェイスである一連のパックデータ(この場合は頂点)をAPIに渡すことを許可する必要があります。これでの私の最初の印象は、CTypes構造クラスを使用して次のようなインターフェイスを可能にすることです。
class Vertex(Structure):
_fields_ = [
('x', c_float),
('y', c_float),
('z', c_float),
('u', c_float),
('v', c_float),
('color', c_int)
]
verts = (Vertex * 3)()
verts[0] = Vertex(0.0, 0.5, 0.0, 0.0, 0.5, 0xFF0000FF)
verts[1] = Vertex(0.5, -0.5, 0.0, 0.5, -0.5, 0x00FF00FF)
verts[2] = Vertex(-0.5, -0.5, 0.0, -0.5, -0.5, 0x0000FFFF)
device.ReadVertices(verts, 3) # This is the interfaces to the C++ object
私が渡そうとしている関数に次の署名があります:
void Device::ReadVertices(Vertex* verts, int count);
そして、Pythonラッパーは次のように見えます:
static PyObject* Device_ReadVertices(Py_Device* self, PyObject* args)
{
PyObject* py_verts;
int count;
if(!PyArg_ParseTuple(args, "Oi", &py_verts, &count))
return NULL;
// This Doesn't Work!
Vertex* verts = static_cast<Vertex*>(PyCObject_AsVoidPtr(py_verts));
self->device->ReadVertices(verts, count);
Py_RETURN_NONE;
}
もちろん、私が抱えている最大の問題は次のとおりです。私は構造体のpyobjectを取得できますが、正しいタイプにどのようにキャストするかはわかりません。上記のコードは惨めに失敗します。では、ユーザーがPythonからこの種のデータを渡すことができるようにするには、どのように正確に進むのでしょうか。
さて、考慮すべきことのいくつかは、最初に私はすでに私のpython/c ++層をかなり書いており、それに完全に満足しているということです(私はSwigから離れて柔軟性を高めることができます)。私はそれを再コードしたくないので、C APIでネイティブに動作するソリューションを好むでしょう。第二に、頂点構造をC ++コードで事前に定義するつもりなので、ユーザーにPythonで再定義する必要がないことを望みます(その方法でエラーを切り取ります)が、そのような隣接する構造を公開する方法がわからない。第三に、別の方法を知らないことを除いて、CTYPES構造を試す理由はありません。どんな提案でも大歓迎です。最後に、これは(ご想像のとおり)グラフィックスアプリの場合は(ご想像のとおり)、便利な方法よりも高速な方法よりも高速な方法を好むでしょう。
助けてくれてありがとう!私はまだPython拡張機能の周りの方法を感じているので、いくつかの粘着性の部分でコミュニティのインプットを取得するのは大きな助けです。
解決
まず第一に、自分のアイデアを売り込んだすべての人に感謝します。最終的な答えに合わせたのは、たくさんの小さな情報でした。最後に、私が見つけたものがあります。Scrive.Packを使用するというサムの提案は、お金にぴったりでした。私がPython 3を使用しているのを見て、私はそれを少し微調整する必要がありましたが、すべてが言われて行われたとき、これは実際に私の画面に三角形が現れました:
verts = bytes()
verts += struct.pack("fffffI", 0.0, 0.5, 0.0, 0.0, 0.5, 0xFF0000FF)
verts += struct.pack("fffffI", 0.5, -0.5, 0.0, 0.5, -0.5, 0x00FF00FF)
verts += struct.pack("fffffI", -0.5, -0.5, 0.0, -0.5, -0.5, 0x0000FFFF)
device.ReadVertices(verts, 3)
私のタプルの解析が今次のように見えます:
static PyObject* Device_ReadVertices(Py_Device* self, PyObject* args)
{
void* py_verts;
int len, count;
if(!PyArg_ParseTuple(args, "y#i", &py_verts, &len, &count))
return NULL;
// Works now!
Vertex* verts = static_cast<Vertex*>(py_verts);
self->device->ReadVertices(verts, count);
Py_RETURN_NONE;
}
使用していなくても len
この例では(最終製品ではそうしますが)、「Y」の代わりに「Y#」を使用してタプルを解析する必要があります。そうしないと、最初のヌルで停止します(ドキュメントに従って)。また、考慮すべき:このようなvoid*キャストは非常に危険なので、ここで示すよりも多くのエラーチェックをロードしてください!
それで、仕事がよくやった、幸せな日、荷物をまとめて家に帰る、はい?
待って!そんなに早くない!もっとあります!
すべてがどのようにうまくいったのか、気まぐれに、以前の試みがまだ私に爆発し、この投稿でPythonの最初のスニペットに戻ったかどうかを確認することにしました。 (もちろん、新しいCコードを使用して)そして...それは機能しました!結果はstruct.packバージョンと同じでした!わお!
したがって、これは、ユーザーがこの種のデータをどのように提供するかを選択できることを意味し、コードは変更なしで処理できます。個人的には、CTYPE.Structureメソッドを奨励します。これは、読みやすさが容易になると思うからですが、実際にユーザーが快適なものは何でも。 (ちなみに、彼らは望むなら、彼らはヘックスの一連のバイトを手動で入力することができました。それはうまくいきました。私は試しました。)
正直なところ、これは可能な限り最高の結果だと思うので、私はecとしています。もう一度ありがとう、そしてこの問題に遭遇した他の人に幸運を!
解決
テストされていませんが、これを試してみて、あなたのニーズに合った速さであるかどうかをお知らせください。
Python側では、頂点をオブジェクトの代わりに文字列に詰めます。
str = "" # byte stream for encoding data
str += struct.pack("5f i", vert1.x, vert1.y, vert1.z, vert1.u, vert1.v, vert1.color) # 5 floats and an int
# same for other vertices
device. ReadVertices( verts, 3) # send vertices to C library
Cライブラリ/Pythonラッパーで、pyargs_parsetupleを変更してフォーマット文字列を使用します "si"
. 。これにより、Python StringをC String(char*)に変換し、ベクトル構造へのポインターとしてタイプキャストできます。この時点で、Cストリングはバイト/単語/フロートのストリームであり、探しているものである必要があります。
幸運を!
他のヒント
私ができる最も簡単なことは、問題を完全に回避し、X、Y、Z、U、V、および色を引用として取得するデバイス_READVERTEXを公開することです。これには、Pythonプログラマーに1つずつ頂点に供給させるなど、明らかな欠点があります。
それが十分ではない場合(おそらくそうではないようです)、説明されているように新しいPythonタイプを定義してみることができます ここ. 。もう少しコードですが、Python開発者がCコードと同じタイプ定義を使用していることを確認するため、これは「よりアーキテクチャリーサウンド」メソッドだと思います。また、単純な構造体(実際にはクラスであり、メソッドを追加する可能性など)よりも少し柔軟性が可能になります。