سؤال

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

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

لذلك لقد حصلت

CarTypes.hpp

enum CarTypes
{
   prius = 0,
   miata,
   hooptie,
   n_car_types
};

MyFactory.hpp

class CarCreator
{
public:
   virtual Car * create_a_car( CarType ) = 0;
   virtual std::list< CarTypes > list_cars_I_create() = 0;
};

class MyFactory // makes cars
{
public:
   Car * create_car( CarType type );
   void factory_register( CarCreator * )

   static MyFactory * get_instance(); // singleton
private:
   MyFactory();

   std::vector< CarCreator * > car_creator_map;
};

MyFactory.cpp

MyFactory:: MyFactory() : car_creator_map( n_car_types );

MyFactory * MyFactory::get_instance() {
   static MyFactory * instance( 0 ); /// Safe singleton
   if ( instance == 0 ) {
      instance = new MyFactory;
   }
   return instance;
}

void MyFactory::factory_register( CarCreator * creator )
{
   std::list< CarTypes > types = creator->list_cars_I_create();
   for ( std::list< CarTypes >::const_iteator iter = types.begin();
         iter != types.end(); ++iter ) {
      car_creator_map[ *iter ] = creator;
   }
}

Car * MyFactory::create_car( CarType type ) 
{
   if ( car_creator_map[ type ] == 0 ) { // SERIOUS ERROR!
      exit();
   }
   return car_creator_map[ type ]->create_a_car( type );
}

...

بعد ذلك سيكون لدي سيارات محددة ومنشئو سيارات محددون:

مياتا.cpp

class Miata : public Car {...};

class MiataCreator : public CarCreator {
public:
   virtual Car * create_a_car( CarType );
   virtual std::list< CarTypes > list_cars_I_create();
private:
   static bool register_with_factory();
   static bool registered;
};

bool MiataCreator::register_with_factory()
{
   MyFactory::get_instance()->factory_register( new MiataCreator );
   return true;
}

bool MiataCreator::registered( MiataCreator::register_with_factory() );

...

أن أكرر:من خلال ربط مكتباتي ديناميكيًا، ستتم تهيئة MiataCreator::registered، وربط مكتباتي بشكل ثابت، ولن تتم تهيئته.

مع البنية الثابتة، عندما يذهب شخص ما إلى المصنع ليطلب مياتا، فإن عنصر مياتا في car_creator_map سيشير إلى NULL وسيخرج البرنامج.

هل هناك أي شيء مميز بالنسبة لأعضاء البيانات المتكاملة الثابتة الخاصة حيث سيتم تخطي عملية التهيئة الخاصة بهم بطريقة أو بأخرى؟هل تتم تهيئة أعضاء البيانات الثابتة فقط في حالة استخدام الفصل؟لم يتم الإعلان عن فئات CarCreator الخاصة بي في أي ملف رأسي؛إنهم يعيشون بالكامل داخل ملف .cpp.هل من الممكن أن يقوم المترجم بتضمين وظيفة التهيئة ويتجنب بطريقة ما استدعاء MyFactory::factory_register?

هل هناك حل أفضل لمشكلة التسجيل هذه؟

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

...

فيما يلي بعض الردود التي أتوقعها ولكنها لا تعالج مشكلتي:

1) المصنع المفرد الخاص بك ليس آمنًا.أ) لا يهم، أنا أعمل مع موضوع واحد فقط.

2) قد يكون المصنع الفردي الخاص بك غير مهيأ عند تهيئة CarCreators (على سبيل المثال.لديك فشل تهيئة ثابت) أ) أنا أستخدم إصدارا آمنا من فئة المفرد عن طريق وضع مثيل المفرد في دالة.إذا كانت هذه مشكلة، فيجب أن أرى الإخراج إذا أضفت عبارة طباعة إلى ملف MiataCreator's::register_with_factory طريقة:أنا لا.

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

المحلول

أعتقد أن لديك فشلًا ذريعًا في أمر التهيئة الثابتة، ولكن ليس مع المصنع.

لا يعني ذلك أن العلم المسجل لم تتم تهيئته، بل لم تتم تهيئته في وقت قريب بما فيه الكفاية.

لا يمكنك الاعتماد على ترتيب التهيئة الثابت إلا بالقدر الذي:

  1. ستتم تهيئة المتغيرات الثابتة المحددة في نفس وحدة الترجمة (ملف .cpp) بالترتيب المذكور
  2. ستتم تهيئة المتغيرات الثابتة المحددة في وحدة الترجمة قبل استدعاء أي وظيفة أو طريقة في وحدة الترجمة هذه لأول مرة.

ما لك لا تستطيع الاعتماد عليه هو أنه سيتم تهيئة متغير ثابت قبل وظيفة أو طريقة في بعض الآخر يتم استدعاء وحدة الترجمة لأول مرة.

على وجه الخصوص، لا يمكنك الاعتماد على MiataCreator::registered (المحدد في Miata.cpp) لتتم تهيئته قبل استدعاء MyFactory::create_car (المحدد في MyFactory.cpp) لأول مرة.

مثل كل السلوكيات غير المحددة، أحيانًا ستحصل على ما تريد، وأحيانًا لن تحصل عليه، وأغرب الأشياء التي تبدو غير ذات صلة (مثل الارتباط الثابت مقابل الارتباط الديناميكي) يمكن أن تتغير سواء كانت تعمل بالطريقة التي تريدها أم لا.

ما عليك القيام به هو إنشاء طريقة وصول ثابتة للعلامة المسجلة التي تم تعريفها في Miata.cpp، وجعل مصنع MyFactory يحصل على القيمة من خلال هذا الموصل.نظرًا لأن الملحق موجود في نفس وحدة الترجمة مثل تعريف المتغير، فسيتم تهيئة المتغير بحلول الوقت الذي يتم فيه تشغيل الملحق.ستحتاج بعد ذلك إلى استدعاء هذا الملحق من مكان ما.

نصائح أخرى

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

تعتمد جميع تقنيات التسجيل التلقائي على كود وقت التحميل وطرقه لتجنب الفشل الثابت، مثل الوظيفة التي تنشئ الكائن وتعيده عند الطلب.

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

بشكل عام، مع المكتبات الثابتة، سيقوم الرابط فقط بسحب ملفات .o من تلك المكتبة التي يشير إليها البرنامج الرئيسي.نظرًا لأنك لا تشير إلى MiataCreator::مسجل أو أي شيء في Miata.cpp ولكنك تعتمد على التهيئة الثابتة، فلن يتضمن الرابط هذا الرمز في ملف exe الخاص بك إذا كان مرتبطًا من مكتبة ثابتة-

تحقق من الملف القابل للتنفيذ الناتج باستخدام nm أو objdump (أو dumpbin إذا كنت تستخدم نظام التشغيل Windows) ما إذا كان رمز MiataCreator::registered مضمنًا بالفعل في ملف exe عند الارتباط بشكل ثابت.

لا أعرف كيفية إجبار الرابط على تضمين كل أجزاء وأجزاء من المكتبة الثابتة ..

مع دول مجلس التعاون الخليجي، يمكنك إضافة -Wl,--whole-archive myLib.a --Wl,--no-whole-archive.سيؤدي هذا إلى إجبار الرابط على تضمين الكائنات حتى لو لم تتم الإشارة إليها.ومع ذلك، هذا ليس محمولا.

متى تتحقق مما إذا كان عنصر مياتا موجودًا داخل الخريطة؟هل هو قبل أو بعد الرئيسي؟
السبب الوحيد الذي يمكن أن أفكر فيه هو الوصول إلى عناصر الخريطة قبل main() (كما هو الحال في التهيئة العامة)، والتي قد تحدث قبل إنشاء MiataCreator::registered (إذا كان في وحدة ترجمة مختلفة)

أنا شخصياً أعتقد أنك مخطئ في الرابط.

لا يتم استخدام المتغيرات المنطقية 'bool MiataCreator::registered' في نظام التشغيل، حيث لا يسحبها الرابط من lib إلى الملف القابل للتنفيذ (تذكر أنه إذا لم يكن هناك إشارة إلى وظيفة/عامة في الملف القابل للتنفيذ، فلن يسحب الرابط الكائن منها) lib [يبحث فقط عن الكائنات غير المحددة حاليًا في الملف القابل للتنفيذ])

يمكنك إضافة بعض عبارات الطباعة في 'bool MiataCreator::register_with_factory()' لمعرفة ما إذا كان قد تم استدعاؤها أم لا.أو تحقق من الرموز الموجودة في ملفك القابل للتنفيذ للتأكد من وجوده.

بعض الأشياء التي سأفعلها:

// Return the factory by reference rather than pointer.
// If you return by pointer the user has to assume it could be NULL
// Also the way you were creating the factory the destructor was never
// being called (though not probably a big deal here) so there was no
// cleanup, which may be usefull in the future. And its just neater.
MyFactory& MyFactory::get_instance()
{
    static MyFactory   instance; /// Safe singleton 
    return instance;
}

بدلاً من تهيئة الكائن من خطوتين.الذي أظن أنه فشل بسبب الرابط.قم بإنشاء مثيل لمصنعك واطلب من المُنشئ تسجيله.

bool MiataCreator::register_with_factory()
{
     MyFactory::get_instance()->factory_register( new MiataCreator );
     return true;
}
//
// I would hope that the linker does not optimize this out (but you should check).
// But the linker only pulls from (or searches in) static libraries 
// for references that are explicitly not defined.
bool MiataCreator::registered( MiataCreator::register_with_factory() );

اريد ان افعل هذا:

MiataCreator::MiataCreator()
{
    // I would change factory_register to take a reference.
    // Though I would store it internall as a pointer in a vector.
    MyFactory::getInstance().factory_register(*this);
}

// In Cpp file.
static MiataCreator   factory;

يعرف الرابط عن كائنات ومنشئات C++ ويجب أن يسحب جميع المتغيرات العامة حيث من المحتمل أن يكون للمنشئات تأثيرات جانبية (أعلم أن المنطق الخاص بك يفعل ذلك أيضًا، لكن يمكنني رؤية بعض الروابط تعمل على تحسين ذلك).

على أي حال هذا هو قيمة 2C بلدي.

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