سؤال

تحديث: حل المشكلة! انظر أسفل المنشور

أحتاج إلى السماح لمطوري Python بتمرير مجموعة من البيانات المعبأة (في هذه الحالة) في واجهة برمجة التطبيقات الخاصة بي ، وهي سلسلة من واجهات C ++ المكشوفة يدويًا من خلال Python 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);

ولف غلاف الثعبان يبدو مثل هذا:

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/C ++ المكتوبة ، وأنا سعيد تمامًا بها (ابتعدت عن Swig حتى أتمكن من الحصول على مزيد من المرونة). لا أرغب في إعادة ترميزها ، لذلك أفضل حلًا يعمل مع API C أصلاً. ثانياً ، أعتزم أن يتم تعريف بنية قمة الرأس مسبقًا في رمز C ++ الخاص بي ، لذلك أفضل ألا يكون لدى المستخدم إعادة تعريفه في الثعبان (يقلل من الأخطاء بهذه الطريقة) ، لكنني أنا لست متأكدًا من كيفية فضح بنية متجاورة من هذا القبيل. ثالثًا ، ليس لدي أي سبب لتجربة بنية Ctypes بصرف النظر عن عدم معرفة طريقة أخرى للقيام بذلك. أي اقتراحات مرحب بها. أخيرًا ، نظرًا لأن هذا (كما قد تكون قد خمنت) لتطبيق الرسومات ، فإنني أفضل طريقة أسرع على طريقة مريحة ، حتى لو كانت الطريقة الأسرع تستغرق المزيد من العمل.

شكرا على اي مساعدة! ما زلت أشعر بملحقات بيثون ، لذلك فهي مساعدة رائعة للحصول على مدخلات المجتمع في بعض الأجزاء الأكثر إلحاحًا.

المحلول

أولاً ، بفضل كل من نصب في أفكارهم. لقد كان الكثير من الحكايات الصغيرة التي أضافت إلى الإجابة النهائية. في النهاية هنا ما وجدته: انتهى اقتراح سام باستخدام struct.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)

مع تحليل بلدي tuple الآن يبدو هكذا:

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 متغير في هذا المثال (على الرغم من أنني سأفعل في المنتج النهائي) ، فأنا بحاجة إلى تحليل tuple باستخدام "y#" بدلاً من مجرد "y" وإلا فإنه سيتوقف عند الفارغ الأول (وفقًا للوثائق). أيضا أن يتم النظر فيها: الفراغ* يلقي مثل هذا أمر خطير للغاية ، لذلك يرجى القيام بالأمواج أكثر فحص الأخطاء أكثر مما أظهر هنا!

لذا ، أيها الوظيفة أحسنت ، يوم سعيد ، حزم والعودة إلى المنزل ، نعم؟

انتظر! ليس بهذه السرعة! هناك المزيد!

لقد شعرت بالرضا عن كيفية عمل كل ذلك ، قررت ، على نزوة ، لمعرفة ما إذا كانت محاولتي السابقة لا تزال تنفجر علي وعادت إلى المقتطف الأول من بيثون في هذا المنشور. (باستخدام رمز C الجديد ، بالطبع) و ... لقد عملت! وكانت النتائج مماثلة لإصدار struct.pack! رائع!

وهذا يعني أن المستخدمين لديك خيار في كيفية توفير هذا النوع من البيانات ، ويمكن أن يتعامل الكود إما بدون تغييرات. أنا شخصياً سأشجع طريقة ctype.structure ، لأنني أعتقد أنها تجعل قابلية القراءة أسهل ، ولكن في الحقيقة كل ما يرتاحه المستخدم. (هيك ، يمكنهم كتابة سلسلة من البايتات يدويًا إذا أرادوا ذلك. إنه يعمل. لقد حاولت.)

بصراحة أعتقد أن هذه هي أفضل نتيجة ممكنة ، لذلك أنا نشوة. شكرا لكم جميعًا مرة أخرى ، ونتمنى لك التوفيق لأي شخص آخر يركض في هذه المشكلة!

هل كانت مفيدة؟

المحلول

لم يتم اختباره ولكن يجب أن تجرب هذا الأمر وإخبارنا إذا كان سريعًا بما يكفي لاحتياجاتك.

على جانب 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

على غلاف المكتبة/Python ، قم بتعديل pyargs_parsetuple لاستخدام سلسلة التنسيق "si". سيؤدي ذلك إلى تحويل سلسلة Python الخاصة بك إلى سلسلة C (Char*) والتي يمكنك بعد ذلك typecast كمؤشر إلى بنية المتجه الخاصة بك. عند هذه النقطة ، تكون السلسلة C عبارة عن تيار من البايت/الكلمات/العوامات ويجب أن تكون ما تبحث عنه.

حظا طيبا وفقك الله!

نصائح أخرى

أسهل شيء يمكنني رؤيته هو تجنب المشكلة تمامًا وفضح جهاز _readvertex الذي يأخذ في x و y و z و u و v واللون كوسائط. هذا له عيوب واضحة ، مثل جعل مبرمجي Python يطعمها رؤوسها واحدة تلو الأخرى.

إذا لم يكن ذلك جيدًا بما يكفي (يبدو أنه ليس كذلك) ، فيمكنك محاولة تحديد نوع Python جديد كما هو موضح هنا. إنه رمز أكثر قليلاً ، لكنني أعتقد أن هذه هي طريقة "الصوت الأكثر معماريًا" ، لأنك تضمن أن مطوري Python الخاص بك يستخدمون نفس تعريف النوع كما أنت في رمز C. كما أنه يتيح مرونة أكثر قليلاً من البنية البسيطة (إنه حقًا فئة ، مع إمكانية إضافة طرق ، إلخ) ، والتي لست متأكدًا من أنك تحتاجها بالفعل ولكنها قد تكون مفيدة لاحقًا.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top