【追記:問題解決!投稿の下部を参照]
Python 開発者がパックされたデータ (この場合は頂点) の配列を API に渡せるようにする必要があります。これは、Python C API を介して手動で公開された一連の C++ インターフェイスです。これに関する私の最初の印象は、ctypes Structure クラスを使用して、次のようなインターフェイスを可能にすることです。
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 でネイティブに動作するソリューションを希望します。次に、Vertex 構造体を C++ コードで事前に定義するつもりなので、ユーザーが Python で再定義する必要がないようにしたいと思います (その方法でエラーを減らします)。そのような連続した構造を公開する方法がわかりません。第三に、ctypes 構造を試す理由が他にありません。別の方法を知りません。どんな提案でも大歓迎です。最後に、これは (ご想像のとおり) グラフィックス アプリ用であるため、高速な方法が少し手間がかかるとしても、便利な方法よりも高速な方法をお勧めします。
助けてくれてありがとう!Python 拡張機能についてはまだ手探りの状態なので、重要な部分についてコミュニティの意見を聞くことは大きな助けになります。
[解決]
まず最初に、アイデアを提案してくれたすべての人に感謝します。最終的な答えにつながったのは、多くの小さな情報でした。最後に、私が見つけたものは次のとおりです。struct.packを使用するというSamの提案は、最終的にはお金に見合ったものになりました。私は 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#' を使用してタプルを解析する必要があることに注意してください。そうしないと、最初の NULL (ドキュメントによると)。また、考慮すべきこと: このような void* キャストは非常に危険です。そのため、ここで示すよりも多くのエラー チェックを実行してください。
それで、仕事はよくやった、幸せな一日、荷物をまとめて家に帰りますか?
待って!そんなに早くない!もっとあります!
すべてがうまくいったことに満足していたので、気まぐれで、前回の試みがまだうまくいかないかどうかを確認し、この投稿の python の最初のスニペットに戻ったかどうかを確認することにしました。(もちろん、新しい C コードを使用して) そして... うまくいきました! 結果は struct.pack バージョンと同じでした! わお!
したがって、これは、ユーザーがこの種のデータを提供する方法を選択できることを意味し、コードは変更なしでどちらも処理できます。個人的には、読みやすくするために ctype.Structure メソッドをお勧めしますが、実際には、ユーザーが快適に使用できる方法です。(ほら、彼らが望むなら、16 進数でバイトの文字列を手動で入力することができます。それは動作します。私は試しました。)
正直なところ、これが可能な限り最高の結果だと思うので、私は有頂天です。この問題に遭遇した他のすべての人に幸運を祈ります。