العثور على سبب لكل شيء ، وسوف تفهم الكثير.
في الآونة الأخيرة ، بالنظر إلى رمز U-Boot في جزء من تطبيق SPI ، صادفت ماكرو لتجاوز قائمة الأجهزة المتاحة ، والتي بعد عدة انتقالات أعادت تعييني على ماكرو _ container_of. كان النص الكلي نفسه موجودًا ، وبدهشة طفيفة رأيت أنه يختلف بعض الشيء عن الإصدار الذي رأيته سابقًا. تم إجراء تحقيق صغير ، مما أدى إلى نتائج مثيرة للاهتمام.
لقد كان الماكرو نفسه معروفًا منذ وقت طويل ويحل مشكلة غريبة إلى حد ما: لدينا مؤشر لحقل لهيكل ما (ptr) ، ونعرف نوع الهيكل (النوع) واسم الحقل (عضو) ، ونحن بحاجة إلى الحصول على مؤشر للهيكل ككل. لا أفهم حقًا كيف يمكن أن تظهر هذه المهمة ، ربما كان المؤلفون "يريدون شيئًا غريبًا" ، ولكن بمجرد تعيين المهمة ، يجب حلها. الحل معروف جيدا:
#define container_of(ptr, type, member) \ ((type *)((char *)(ptr)-offsetof(type,member)))
كل شيء شفاف وواضح ، لكن التنفيذ المكتشف كان أكثر تعقيدًا إلى حد ما:
#define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );})
يتطابق السطر الثاني تقريبًا مع الحل الأول ، ولكن ما الذي يفعله السطر الأول من الماكرو؟ لا ، ما تقوم به هو مفهوم فقط: فهو ينشئ مؤشرًا محليًا ثابتًا لحقل الهيكل ويعينه قيمة معلمة الماكرو - مؤشر إلى الحقل. ولكن لماذا يتم ذلك غير واضح.
أحد الاعتبارات الواضحة فيما يتعلق بالغرض من السطر الأول هو التحقق من المعلمة الأولى للماكرو وهي بالفعل مؤشر لحقل بنية في النمط:
int *x = (X)
والتي في هذه الحالة تأخذ شكلاً صعبًا إلى حد ما:
typeof( ((type *)0)->member ) *__mptr = (ptr)
منذ النوع المطلوب للتحقق يجب أن تبنى على الطاير.
حسناً ، دعنا نتفق ، الشيء مفيد ، رغم أنني اضطررت إلى استخدام المؤشر في السطر الثاني لتجنب تحذيرات برنامج التحويل البرمجي.
ولكن لماذا معدل الثبات - يجب أن يعمل الماكرو بشكل جيد حتى بدون هذه الإضافة (حاولت على الفور وعملت). الكتاب لا يمكن وضعه عن طريق الصدفة.
ليس من الضروري أن ننظر هناكيجب أن أعترف بأنهم دفعوا لي بالإجابة ، وأنا شخصياً لم أخمن.
اتضح أن هذا المعدل ضروري ببساطة إذا حاولنا معرفة عنوان البنية الثابتة ، لأنه في هذه الحالة سيحتاج المحول البرمجي إلى
invalid conversion from 'const xxx*' to `xxx*'
.
من المثير للاهتمام أن ثبات الحقل نفسه داخل بنية عادية لا يتطلب الإكراه في ماكرو فحسب ، بل يزيل أيضًا حاجته إلى بنية ثابتة ، أي تعبير مثل:
struct s1 { int data; const int next; }; const struct s1 ss1 = {314,0}; container_of(&(ss1.next),struct s1,next);
يترجم دون أخطاء ودون معدّل في نص الماكرو ، والتعبير:
struct s1 { int data; int next; }; const struct s1 ss1 = {314,0}; container_of(&(ss1.next),struct s1,next);
هو مطلوب.
بالنسبة لقراء هابر الذين يعرفون مستوى اللغة جيدًا ، ستبدو اكتشافاتي سخيفة في أسلوب "شكرًا لك يا كابتن" ، لكن بالنسبة للباقي فإنها قد تكون ممتعة.