سؤال

إذا كنت تستخدم assert() ويفشل التأكيد بعد ذلك assert() سوف اتصل abort(), ، إنهاء البرنامج قيد التشغيل فجأة.لا أستطيع تحمل ذلك في رمز الإنتاج الخاص بي.هل هناك طريقة للتأكيد في وقت التشغيل مع القدرة على التقاط التأكيدات الفاشلة حتى تتاح لي الفرصة للتعامل معها بأمان؟

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

المحلول

نعم، في واقع الأمر هناك.ستحتاج إلى كتابة دالة تأكيد مخصصة بنفسك، مثل وظيفة C++ assert() هو بالضبط C assert(), ، مع ال abort() "الميزة" المجمعة.ولحسن الحظ، هذا أمر واضح ومباشر بشكل مدهش.

تأكيد.hh

template <typename X, typename A>
inline void Assert(A assertion)
{
    if( !assertion ) throw X();
}

ستطرح الوظيفة المذكورة أعلاه استثناءً إذا لم يتم الاحتفاظ بالمسند.سيكون لديك بعد ذلك فرصة للقبض على الاستثناء.إذا لم تتمكن من فهم الاستثناء، terminate() سيتم استدعاؤه، والذي سينهي البرنامج بالمثل abort().

قد تتساءل ماذا عن تحسين التأكيد عندما نبني من أجل الإنتاج.في هذه الحالة، يمكنك تحديد الثوابت التي ستدل على أنك تقوم بالبناء من أجل الإنتاج ثم الرجوع إلى الثابت عندما Assert().

debug.hh

#ifdef NDEBUG
    const bool CHECK_WRONG = false;
#else
    const bool CHECK_WRONG = true;
#endif

main.cc

#include<iostream>

struct Wrong { };

int main()
{
    try {
        Assert<Wrong>(!CHECK_WRONG || 2 + 2 == 5);
        std::cout << "I can go to sleep now.\n";
    }
    catch( Wrong e ) {
        std::cerr << "Someone is wrong on the internet!\n";
    }

    return 0;
}

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

ال Assert() الوظيفة تعادل الكتابة

if( !assertion ) throw X();

لكنه يشير بوضوح إلى نية المبرمج:تقديم تأكيد.من السهل أيضًا التعامل مع التأكيدات باستخدام هذا النهج، تمامًا مثل العادي assert()س.

لمزيد من التفاصيل حول هذه التقنية، راجع لغة برمجة C++‎ لـ Bjarne Stroustrup 3e، القسم 24.3.7.2.

نصائح أخرى

وظائف الإبلاغ عن الأخطاء في glib اتخاذ نهج الاستمرار بعد التأكيد.glib هي مكتبة استقلالية النظام الأساسي التي يستخدمها Gnome (عبر GTK).فيما يلي ماكرو يتحقق من شرط مسبق ويطبع تتبع المكدس في حالة فشل الشرط المسبق.

#define RETURN_IF_FAIL(expr)      do {                  \
 if (!(expr))                                           \
 {                                                      \
         fprintf(stderr,                                \
                "file %s: line %d (%s): precondition `%s' failed.", \
                __FILE__,                                           \
                __LINE__,                                           \
                __PRETTY_FUNCTION__,                                \
                #expr);                                             \
         print_stack_trace(2);                                      \
         return;                                                    \
 };               } while(0)
#define RETURN_VAL_IF_FAIL(expr, val)  do {                         \
 if (!(expr))                                                       \
 {                                                                  \
        fprintf(stderr,                                             \
                "file %s: line %d (%s): precondition `%s' failed.",     \
                __FILE__,                                               \
                __LINE__,                                               \
                __PRETTY_FUNCTION__,                                    \
                #expr);                                                 \
         print_stack_trace(2);                                          \
         return val;                                                    \
 };               } while(0)

إليك الوظيفة التي تطبع تتبع المكدس، المكتوب لبيئة تستخدم سلسلة أدوات gnu (gcc):

void print_stack_trace(int fd)
{
    void *array[256];
    size_t size;

    size = backtrace (array, 256);
    backtrace_symbols_fd(array, size, fd);
}

هذه هي الطريقة التي تستخدم بها وحدات الماكرو:

char *doSomething(char *ptr)
{
    RETURN_VAL_IF_FAIL(ptr != NULL, NULL);  // same as assert(ptr != NULL), but returns NULL if it fails.

    if( ptr != NULL )        // Necessary if you want to define the macro only for debug builds
    {
       ...
    }

    return ptr;
}

void doSomethingElse(char *ptr)
{
    RETURN_IF_FAIL(ptr != NULL);
}

تعمل التأكيدات في C/C++ فقط في إصدارات تصحيح الأخطاء.لذلك لن يحدث هذا في وقت التشغيل.بشكل عام، يجب أن تضع التأكيدات علامة على الأشياء التي تشير في حالة حدوثها إلى وجود خطأ، وإظهار الافتراضات بشكل عام في التعليمات البرمجية الخاصة بك وما إلى ذلك.

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

إليك ما لدي في "assert.h" (Mac OS 10.4):

#define assert(e) ((void) ((e) ? 0 : __assert (#e, __FILE__, __LINE__)))
#define __assert(e, file, line) ((void)printf ("%s:%u: failed assertion `%s'\n", file, line, e), abort(), 0)

بناءً على ذلك، استبدل استدعاء abort()‎ بـ throw( Exception ).وبدلاً من printf، يمكنك تنسيق السلسلة في رسالة خطأ الاستثناء.في النهاية تحصل على شيء مثل هذا:

#define assert(e) ((void) ((e) ? 0 : my_assert (#e, __FILE__, __LINE__)))
#define my_assert( e, file, line ) ( throw std::runtime_error(\
   std::string(file:)+boost::lexical_cast<std::string>(line)+": failed assertion "+e))

لم أحاول تجميعها، لكنك فهمت المعنى.

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

void my_assert( const char* e, const char* file, int line);

وقم بتنفيذه في مكان حيث يمكنك تضمين جميع الرؤوس التي تحتاجها بحرية.

قم بتغليفها في بعض #ifdef DEBUG إذا كنت في حاجة إليها، أو لا إذا كنت تريد دائمًا تشغيل عمليات التحقق هذه.

إذا كنت تريد إنشاء سلسلة أحرف تحتوي على معلومات حول التأكيد:http://xll8.codeplex.com/SourceControl/latest#xll/ensure.h

_set_error_mode(_OUT_TO_MSGBOX);

صدقوني، هذه الوظيفة يمكن أن تساعدك.

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