سؤال

أرغب في استخدام حارس النطاق في C من أجل القيام بالتمييز.

أود أن أعرف مقدار الوقت الذي أقضيه في وظيفة. هذا ما أفعله:

int function() {

  tic();

  ... do stuff ...
  if (something)
  {
    toc();
    return 0;
   }

  toc();
  return 1;
}

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

شكرًا

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

المحلول

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

static inline int real_function() {
    // previous contents of function(), with no tic or toc
}

int function() {
    tic();
    int r = real_function();
    toc();
    return r;
}

كما يقول أي شخص آخر: استخدم Profiler ، فإنه سيوفر لك الكثير من الجهد على المدى الطويل. كما لا يقولون: إذا كان منصتك يحتوي على واحد.

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

أيضًا ، ضع في اعتبارك عندما تفعل أي شيء من هذا القبيل يمكن أن يفسدك المترجم الخاص بك. يمكنك كتابة هذا:

tic();
do_something();
int i = something_else();
toc();
return i;

إذا قرر المترجم أن شيئًا _else لا يحتوي على آثار جانبية ، فالأنه يستغرق وقتًا كبيرًا ، فقد يحول الرمز إلى هذا:

tic();
do_something();
toc();
return something_else();

وسوف تقلل بيانات ملف التعريف الخاص بك عن الوقت الذي تقضيه في وظيفتك. سبب آخر أنه من الجيد جدًا أن يكون لديك مستندقي حقيقي - يمكن أن يتعاون مع المترجم.

نصائح أخرى

يمكنك تحديد ماكرو مثل:

#define TOC_RETURN(x) \
    do { \
    toc(); \
    return x; \
    } while(0)

التي يجب أن تعمل في أي مكان تضعه. ثم يمكنك أتمتة الاستبدال return *; مع TOC_RETURN(*).

لماذا لا تستخدم أداة التنميط الفعلي ، مثل GPROF?

يمكنك فقط "إعادة تعريف"العودة عبر الماكرو: (يرجى الاطلاع على إخلاء المسؤولية)

#include <stdio.h>

void tic() { printf("tic\n"); }
void toc() { printf("toc\n"; }

#define return toc(); return
int foo() {
    tic();

    return 0;
}
#undef return

int main() {
    foo();
    return 0;
}

عدم اعطاء رأي: يمكن اعتبار هذا قبيحًا واختراقًا لأن:

  • لن يعمل مع وظائف الفراغ إلا إذا كنت تستخدم إرجاع؛-صياغات.
  • قد لا يكون محمولًا/قياسيًا ، على الرغم من أنه يعمل على MSVC8.
  • لا ينبغي للمرء أن يحدد الكلمات الرئيسية.

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

أليس من الأفضل أن تفعل على النحو التالي؟

tic();
call_function();
toc();

هذا يتعامل تلقائيًا "جميع نقاط الخروج" من الوظيفة.

ملاحظة: لماذا لا تستخدم profiler؟

لا يحتاج لك Profiler الحقيقي إلى تعديل الكود ، فقط لتجميعه مع تمكين التنميط.

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

void cleanup_toc(int *ignored __attribute__((__unused__))) { toc(); }

int function(void) {
    tic();
    int atexit __attribute__((__cleanup__(cleanup_toc))) = 0;

    //... do stuff ...
    if (something) {
        return 0;
    }

    return 1;
}

لا يستخدم هذا الحل وحدات الماكرو ، ولكن يمكنك بالطبع لف هذا الماكرو. علي سبيل المثال:

#define CONCATENATE_IMPL(x, y) x ## y
#define CONCATENATE(x, y) CONCATENATE_IMPL(x, y)
#define ATEXIT(f) int CONCATENATE(atexit, __LINE__) __attribute__((__cleanup__(f))) = 0

int function(void) {
    ATEXIT(cleanup1); // These are executed in reverse order, i.e.
    ATEXIT(cleanup2); // cleanup2 will run before cleanup1.
}

حسنًا ، ربما لف استدعاء الوظيفة في ماكرو (عائلة من وحدات الماكرو ، حقًا)؟ إليك واحدة لا تأخذ حججًا وتعيد Retval:

// define the wrapper for name
#define DEFTIMECALL0(Retval,name) \
    Retval timed##name() \
    { \
        Retval ret;
        tic(); \
        ret = name(); \
        toc(); \
        return ret; \
    }

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

تعديل ربما لا يوجد حتى نقطة في تحديد وظيفة التفاف ، ومن الأفضل أن يكون لديك عائلة من وحدات الماكرو (مرة أخرى ، لكل إصدارات نوع القوس ونوع الفراغ) التي تلتف دالة استدعاء في TIC/TOC مباشرة في المكالمات

لا تخف من صياغة الأدوات ، التي تفعل ذلك من أجلك بشكل أساسي.

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