باستخدام CTTYPES Python لتمرير / قراءة المعلمة المعلمة باسم "BLUMNT_NAME *** PARAM_NAME"؟

StackOverflow https://stackoverflow.com/questions/383010

سؤال

أحاول استخدام مكتبة python ctypes للوصول إلى بعض الأساليب في مكتبة المسح عاقل. وبعد هذه هي تجربتي الأولى مع CTTYPES ولأي المرة الأولى التي اضطررت إلى التعامل معها مع البيانات الخاصة بك في أكثر من عام، لذا فهناك منحنى تعليمي عادل هنا، لكنني أعتقد حتى بدون هذا الإعلان بالذات سيكون مزعجا:

extern SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only);

بادئ ذي بدء، لقد تعاملت بنجاح SANE_Status (عداد) و SANE_Bool (typedef ل c_int). كانت تلك بسيطة. هذه المعلمة الأولى، من ناحية أخرى، تسبب لي كل أنواع الحزن. أنا غير مألوف مع "***"الترميز أن تبدأ وعصريات التتبع الخاصة بي حتى الآن لم تسفر عن معلومات أكثر من بيانات القمامة. كيف يمكنني تهيئة المدخلات لهذه الوظيفة بحيث يمكنني قراءة قائمة بتراجع كائنات بنية بيثون؟ للرجوع إليها، هي هيكل ج يجري المشار إليها هي:

typedef struct
{
    SANE_String_Const name; /* unique device name */
    SANE_String_Const vendor;   /* device vendor string */
    SANE_String_Const model;    /* device model name */
    SANE_String_Const type; /* device type (e.g., "flatbed scanner") */
 }
SANE_Device;

أين SANE_String_Const يعرف بأنه c_char_p.

إصدار Python / ctypes الخاص بي من هذا الكائن هو:

class SANE_Device(Structure):
    _fields_ = [
        ("name", c_char_p),
        ("vendor", c_char_p),
        ("model", c_char_p),
        ("type", c_char_p)]

اقتراحات حول ما يجب أن أذهب فيه بحيث يمكنني الحصول على السلوك المتوقع (قائمة من الكائنات الهيكلية) من هذا؟ جميع الاستجابات موضع تقدير.

التحديث 1:

باستخدام ما يلي، كنت قادرا على استرداد هيكل Python الصحيح Sane_Device الصحيح:

devices = pointer(pointer(pointer(SANE_Device())))
status = libsane.sane_get_devices(devices, c_int(0))
print status, devices, devices.contents.contents.contents.name

ومع ذلك، 1) Yuck و 2) يبدو أنه لن يعمل إلا إذا كانت هناك نتيجة واحدة. لا أستطيع لين () devices.contents.contents أو devices.contents.contents.contents. وبعد كيف يمكنني تحديد عدد النتائج؟ تحدد مستندات عاقلة أن "إذا تم تنفيذ الدالة بنجاح، فقد تخزن مؤشر إلى صفيف تم إنهاء Null من المؤشرات إلى Sane_Device هياكل في * device_list". اقتراحات؟

تحديث 2:

كنت قادرا على اجتياز مجموعة من عشرة العنصر ثم الوصول إلى العنصر الأول باستخدام:

devices = pointer(pointer(pointer((SANE_Device * 10)())))
status = libsane.sane_get_devices(devices, c_int(0))
print status, devices, devices.contents.contents.contents[0].name

ومع ذلك، من الواضح أن عشرة هو رقم تعسفي وليس لدي أي طريقة لتحديد العدد الفعلي للنتائج. محاولة الوصول إلى devices.contents.contents.contents[1].name عند توصيل جهاز واحد فقط يتسبب خطأ تجزئة. يجب أن تكون هناك طريقة مناسبة للتعامل مع البنيات ذات الطول المتغير مثل هذه في ctypes.

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

المحلول

أ const SANE_Device *** هو مؤشر ثلاثة مستويات: إنه مؤشر إلى مؤشر إلى مؤشر إلى Sane_Device ثابت. يمكنك استخدام البرنامج CDECL. إلى تعريف تعريفات C / C ++ معقدة.

بحسب ال الوثائق العاطل, SANE_get_devices() سيتم تخزين مؤشر إلى قائمة من المؤشرات غير ذات الإغراق بأجهزة عاقل إذا نجحت. وبالتالي، فإن الطريقة المناسبة للاتصال بها هي إعلان متغير من النوع const SANE_Device ** (أي مؤشر إلى مؤشر إلى ثابت `sane_device)، وتمرير عنوان هذا المؤشر:

const SANE_Device **device_list;
SANE_get_devices(&device_list, local_only);  // check return value
// Now, device_list[0] points to the first device,
// device_list[1] points to the second device, etc.
// Once you hit a NULL pointer, that's the end of the list:
int num_devices = 0;
while(device_list[num_devices] != NULL)
    num_devices++;
// num_devices now stores the total number of devices

الآن، هذه هي الطريقة التي ستدعوها من كود C. لقد قمت بإدارة الوثائق على CTTYPES، ويبدو أنك تريد استخدام byref وظيفة لتمرير الوسيطة بالرجوع إليها، وأن القيمة التي تمر بها يجب أن تكون مؤشر إلى مؤشر إلى Sane_Device. لاحظ التمييز بين pointer و POINTER: السابق يخلق مؤشر إلى نموذج, ، في حين أن الأخير يخلق مؤشر إلى اكتب. وبعد وبالتالي، أعتقد أن الشفرة التالية ستعمل:

// SANE_Device declared as you had it
devices = POINTER(POINTER(SANE_Device))()  // devices is a NULL pointer to a pointer to a SANE_Device
status = libsane.sane_get_devices(byref(devices), c_int(0))
if status != successful:   // replace this by whatever success is
    print error
else:
    num_devices = 0
    // Convert NULL-terminated C list into Python list
    device_list = []
    while devices[num_devices]:
        device_list.append(devices[num_devices].contents)  // use .contents here since each entry in the C list is itself a pointer
        num_devices += 1
    print device_list

تحرير] لقد اختبرت الرمز أعلاه باستخدام عنصر نائب بسيط للغاية SANE_get_devices, ، ويعمل.

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