كيف يمكنني إجراء تهيئة ما قبل الرائد في C / C ++ مع AVR-GCC؟

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

سؤال

من أجل التأكد من أن بعض رمز التهيئة يعمل من قبل main (باستخدام Arduino / AVR-GCC) لدي رمز مثل ما يلي:

class Init {
public:
    Init() { initialize(); }
};

Init init;

من الناحية المثالية، أود أن أكون قادرا على الكتابة ببساطة:

initialize();

ولكن هذا لا يجمع ...

هل هناك طريقة أقل سرعة لتحقيق نفس التأثير؟

ملحوظة: الرمز هو جزء من رسم Arduino حتى main يتم إنشاء الدالة تلقائيا ولا يمكن تعديلها (على سبيل المثال للاتصال initialize قبل أي رمز آخر).

تحديث: من الناحية المثالية سيتم تنفيذ التهيئة في setup وظيفة، ولكن في هذه الحالة هناك رمز آخر اعتمادا عليه والتي تحدث من قبل main.

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

المحلول

يمكنك استخدام دول مجلس التعاون الخليجي constructor ينسب للتأكد من أنها تسمى من قبل main():

void Init(void) __attribute__((constructor));
void Init(void) { /* code */ }  // This will always run before main()

نصائح أخرى

يمكنك جعل ما ورد أعلاه أقصر قليلا من خلال إعطاء "تهيئة" نوع الإرجاع، واستخدام ذلك لتهيئة متغير عالمي:

int initialize();
int dummy = initialize();

ومع ذلك، عليك أن تكون حذرا مع هذا، لا يضمن المعيار أن التهيئة المذكورة أعلاه (أو واحد لكائن INITE الخاص بك) يحدث قبل تشغيل الصفحة الرئيسية (3.6.2 / 3):

يتم تحديده حول التنفيذ ما إذا كان التهيئة الديناميكية (8.5 و 9.4 و 12.1 و 12.6.1) من كائن من نطاق مساحة الاسم تتم قبل بيان رئيسي الرئيسي.

الشيء الوحيد المضمون هو أن التهيئة ستعقد قبل استخدام "دمية" على الإطلاق.

قد يكون خيار أكثر تدخلا (إذا كان ذلك ممكنا) هو استخدام "-d الرئيسي = AVR_MAIN" في Makefile. يمكنك بعد ذلك إضافة الرئيسية الخاصة بك كما يلي:

// Add a declaration for the main declared by the avr compiler.
int avr_main (int argc, const char * argv[]);  // Needs to match exactly

#undef main
int main (int argc, const char * argv[])
{
  initialize ();
  return avr_main (argc, argv);
}

على الأقل هنا أنت مضمون أن يتم التهيئة عندما تتوقع.

إليك طريقة شريرة إلى حد ما لتحقيق هذا:

#include <stdio.h>

static int bar = 0;

int __real_main(int argc, char **argv);

int __wrap_main(int argc, char **argv)
{
    bar = 1;
    return __real_main(argc, argv);
}

int main(int argc, char **argv)
{
    printf("bar %d\n",bar);
    return 0;
}

أضف ما يلي إلى إشارات الرابط: --wrap main

على سبيل المثال

gcc -Xlinker --wrap -Xlinker main a.c

سوف يحل المركز أن يحل محل جميع المكالمات main مع المكالمات إلى __wrap_main, ، انظر ld man page. على --wrap

الحل الخاص بك في بسيطة ونظيفة. ما يمكنك بالإضافة إلى ذلك هو وضع التعليمات البرمجية الخاصة بك في مساحة الاسم المجهولة. لا أرى أي حاجة لجعلها أفضل من ذلك :)

إذا كنت تستخدم بيئة Arduino، فهل هناك أي سبب لا يمكنك وضعه في طريقة الإعداد?

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

تحديث:

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

يمكنك دائما إجراء ماكرو Preprocessor منه:

#define RUN_EARLY(code) \
namespace { \
    class Init { \
        Init() { code; } \
    }; \
    Init init; \
}

الآن يجب أن يعمل هذا:

RUN_EARLY(initialize())

لكنها ليست حقا تجعل الأمور أقصر، فقط تحريك الرمز المطلي حولها.

يمكنك استخدام أقسام ".init *" لإضافة رمز C ليتم تشغيله قبل التشغيل () (وحتى وقت التشغيل C). ترتبط هذه الأقسام بالتنفيذ في النهاية وتسميتها في وقت محدد أثناء تهيئة البرنامج. يمكنك الحصول على القائمة هنا:

http://www.nongnu.org/avr-libc/user-manual/mem_sections.html.

.init1 على سبيل المثال، مرتبط ضعيف ب __init ()، لذلك إذا حددت __init ()، فسيتم ربطه ويسمى أول شيء. ومع ذلك، لم يتم إعداد المكدس، لذلك عليك أن تكون حذرا في ما تفعله (فقط استخدم متغير التسجيل 8_t، وليس استدعاء أي وظائف).

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

هنا هو مثالك

class Init {
private:
    // Made the constructor private, so to avoid calling it in other situation
    // than for the initialization of the static member.
    Init() { initialize(); }

private:
    static Init INIT;
};


Init Init::INIT;

بالتأكيد، قمت بوضع هذا في أحد ملفات رأسك، قل preinit.h:

class Init { public: Init() { initialize(); } }; Init init;

وبعد ذلك، في واحد لوحدات التجميع الخاصة بك، وضعت:

void initialize(void) {
    // weave your magic here.
}
#include "preinit.h"

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

يجب أن تكون حريصا أيضا بما في ذلك أكثر من واحدة من وظائف التهيئة هذه لأنني لا أصدق أن C ++ تملي الطلب - قد يكون عشوائيا.

لست متأكدا من هذا "الرسم" الذي تتحدثه ولكنه سيكون من الممكن تحويل وحدة الترجمة الرئيسية باستخدام برنامج نصي قبل أن يتم تمريره إلى المحول البرمجي، شيء مثل:

awk '{print;if (substr($0,0,11) == "int main (") {print "initialize();"};}'

يمكنك أن ترى كيف سيؤثر ذلك على برنامجك لأن:

echo '#include <stdio.h>
int main (void) {
    int x = 1;
    return 0;
}' | awk '{
    print;
    if (substr($0,0,11) == "int main (") {
        print "    initialize();"
    }
}'

يولد ما يلي مع initialize() وأضاف المكالمة:

#include <stdio.h>
int main (void) {
    initialize();
    int x = 1;
    return 0;
}

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

هناك كيف يمكنني أداء الترميز الرئيسي الرئيسي. هناك أقسام إيطارية تنفذ قبل الرئيس، يشير إلى http://www.nongnu.org/avr-libc/user-manual/mem_sections.html. أقسام البداية.

على أي حال، هذا يعمل فقط على -O0 التحسين لسبب ما. ما زلت أحاول معرفة أي خيار "الأمثل" رمز التجميع قبل الرئيسي.

static void
__attribute__ ((naked))
__attribute__ ((section (".init8")))    /* run this right before main */
__attribute__ ((unused))    /* Kill the unused function warning */
stack_init(void) {assembly stuff}

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

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