سؤال

لدي ثلاثة ملفات رأس في مشروعي التي تصف الكائنات Rational, Complex, ، و RubyObject.الأولين عبارة عن قوالب.يمكن تحويلها جميعًا باستخدام مُنشئات النسخ، والتي تم تعريفها في ملفات الرأس - باستثناء تلك التي يتم إنشاؤها Rational و Complex من const RubyObject&س، والتي تم تعريفها في ملف مصدر.

ملحوظة: هذه التعريفات موجودة بالضرورة.اذا هم الجميع اذهب إلى الرؤوس، وستحصل على ذلك التبعية الدائرية.

منذ فترة، واجهت بعض أخطاء الرمز التي لم يتم حلها مع مُنشئي النسخ المحددين في الملف المصدر.تمكنت من تضمين الوظيفة التالية في الملف المصدر

void nm_init_data() {
    nm::RubyObject obj(INT2FIX(1));
    nm::Rational32 x(obj);
    nm::Rational64 y(obj);
    nm::Rational128 z(obj);
    volatile nm::Complex64 a(obj);
    volatile nm::Complex128 b(obj);
}

وثم يتصل nm_init_data() من نقطة دخول المكتبة في الملف المصدر الرئيسي.يؤدي القيام بذلك إلى إجبار هذه الرموز على الارتباط بشكل صحيح.

لسوء الحظ، قمت مؤخرًا بترقية نظام دول مجلس التعاون الخليجي وعادت الأخطاء مرة أخرى.في الواقع، يبدو أن الأمر يحدث في مكان مختلف قليلاً مع إصدار دول مجلس التعاون الخليجي 4.6 (على سبيل المثال، في Travis-CI).

لكنها ليست مشكلة خاصة بالإصدار (كما اعتقدت من قبل).نرى ذلك على نظام Travis CI المبني على Ubuntu, الذي يدير دول مجلس التعاون الخليجي 4.6.لكننا لا نراها على جهاز Ubuntu مع الإصدار 4.8.1 أو 4.8.2 من دول مجلس التعاون الخليجي.ولكننا يفعل شاهده على جهاز Mac OS X يعمل بالإصدار 4.8.2، وليس على نفس الجهاز الذي يعمل بالإصدار 4.7.2.لا يبدو أن إيقاف تشغيل التحسين يساعد أيضًا.

إذا ركضت nm في مكتبتي، الرمز غير محدد بالتأكيد:

$ nm tmp/x86_64-darwin13.0.0/nmatrix/2.0.0/nmatrix.bundle |grep RationalIsEC1ERKNS
                 U __ZN2nm8RationalIsEC1ERKNS_10RubyObjectE
00000000004ca460 D __ZZN2nm8RationalIsEC1ERKNS_10RubyObjectEE18rb_intern_id_cache
00000000004ca458 D __ZZN2nm8RationalIsEC1ERKNS_10RubyObjectEE18rb_intern_id_cache_0

لست متأكدًا من سبب وجود إدخالين محددين تابعين للرمز غير المحدد، لكنني أيضًا لا أعرف القدر الذي أريده من المترجمين.

يبدو أيضًا أن مُنشئ النسخ هو رمز غير محدد لكل إصدار من Rational نموذج:

__ZN2nm8RationalIiEC1ERKNS_10RubyObjectE
__ZN2nm8RationalIsEC1ERKNS_10RubyObjectE
__ZN2nm8RationalIxEC1ERKNS_10RubyObjectE

فكرت: "حسنًا، هذا غريب"."Complex64 و Complex128 وتسمى أيضا في ذلك nm_init_data وظيفة، ولكن كلاهما يتم حلهما بشكل صحيح — ولم يتم إدراجهما في ملف nm -u الإخراج." لذا حاولت الإضافة volatile قبل إنشاء النسخة العقلانية أيضًا، معتقدين أنه ربما كان المترجم يقوم بتحسين شيء لا نريد تحسينه.لكن هذا لم يحل المشكلة أيضًا، للأسف.وهذا ما حدث مع التحذير:

void nm_init_data() {
  volatile VALUE t = INT2FIX(1);
  volatile nm::RubyObject obj(t);
  volatile nm::Rational32 x(const_cast<nm::RubyObject&>(obj));
  volatile nm::Rational64 y(const_cast<nm::RubyObject&>(obj));
  volatile nm::Rational128 z(const_cast<nm::RubyObject&>(obj));
  volatile nm::Complex64 a(const_cast<nm::RubyObject&>(obj));
  volatile nm::Complex128 b(const_cast<nm::RubyObject&>(obj));
}

التحذير هو أنني الآن أتلقى نفس الخطأ بالضبط، ولكن بالنسبة للكائنات المعقدة بدلاً من ذلك.ارغ!

dyld: lazy symbol binding failed: Symbol not found: __ZN2nm7ComplexIdEC1ERKNS_10RubyObjectE
  Referenced from: /Users/jwoods/Projects/nmatrix/lib/nmatrix.bundle
  Expected in: flat namespace

dyld: Symbol not found: __ZN2nm7ComplexIdEC1ERKNS_10RubyObjectE
  Referenced from: /Users/jwoods/Projects/nmatrix/lib/nmatrix.bundle
  Expected in: flat namespace

هذا سخيف تماما.فيما يلي تعريفات هاتين الوظيفتين، في نفس الملف المصدر مثل ملف nm_init_data() وظيفة:

namespace nm {
  template <typename Type>
  Complex<Type>::Complex(const RubyObject& other) {
    // do some things
  }

  template <typename Type>
  Rational<Type>::Rational(const RubyObject& other) {
    // do some other things
  }
} // end of namespace nm

تَلمِيح: الشيء الوحيد الجدير بالذكر هو أن الخطأ لا يحدث متى nm_init_data() يتم الاتصال به (أي عند تحميل المكتبة).يحدث ذلك لاحقًا، أثناء استدعاء آخر لهذه الوظائف المزعجة.

كيف أصلح هذه المشكلة نهائيًا وتعجب الآخرين؟

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

المحلول

تدعي ما يلي، وهو ما أشك فيه.

هذه التعريفات موجودة بالضرورة. إذا ذهبوا جميعا في الرؤوس، فستحصل على تبعية دائرية.

في معظم الحالات، يمكنك حل مثل هذه الترطيبات الدائرية عن طريق فصل التعليمات البرمجية الخاصة بك إلى ملف إضافي .hpp، والتي يتم تضمينها مع تعريف الفصل الذي يحتوي على تعريفات القالب في أي مكان مطلوب.

إذا كان الكود الخاص بك يحتوي على تبعية دائرية حقيقية، فلا يمكن تجميعها. عادة ما يبدو أن تبعياتك تعميما، عليك أن تبدو أقرب وتذهب إلى مستوى الطريقة وتحقق من أي منها يتطلب كلا النوعين لتجميعها.

لذلك يمكن أن تستخدم أنواعك بعضها البعض، ثم ترجمة الكل في ملف .cpp واحد (E.G. عبر ثلاثة .hpp). أو لا يوجد فقط مؤشر إلى نوع آخر، ثم استخدم الإعلانات الأمامية للتأكد من أن جميع القوالب يتم حلها. أو ثالثا، لديك بعض الطريقة التي تعتمد على الأمام وبعضها يعتمد للخلف، ثم ضع الرقيقة في ملف واحد، والبعض الآخر في مكان آخر، وأنت بخير مرة أخرى.

بالإضافة إلى ذلك، يبدو أنه يجب عليك استخدام إعلان إلى الأمام لعناصرك المفقودة. أتوقع شيئا مثل ما يلي بعد تعريف الوظيفة. E.G.:

giveacodicetagpre.

نصائح أخرى

Rational, Complex...هي قوالب

نسخ المنشئين...يتم تعريفها في ملفات الرأس - باستثناء تلك التي يتم إنشاؤها Rational و Complex من const RubyObject&س، والتي تم تعريفها في الملف المصدر.

وهنا تكمن مشكلتك.منذ Rational و Complex هي قوالب، الجميع يجب أن تكون أساليبها متاحة في ملف الرأس الخاص بك.

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

ما عليك سوى نقل تعريفات Rational(const RubyObject&) و Complex(const RubyObject&) في الرؤوس المعنية وكل شيء يجب أن يعمل فقط.

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