الشبكة العربية لمطوري الألعاب

مبتدئ  محمد علاء الدين عبد العزيز مشاركة 1

السلام عليكم و رحمة الله و بركاته

فى الماضى عندما يحدث خطأ داخل احد برامجى كنت استخدم الـ try .. catch بالإضافه للماكرو __FILE__ و __FUNCTION__ و __LINE__ لمعرفة اسم الملف الذى حدث به الخطأ و اسم الداله و رقم السطر.

ففكرت بصنع class لتمثل الـ exception و وضع الماكرو بداخلها و بالتالى سيكون شكل الـ class كالتالى




#ifndef EXCEPTION_H
#define EXCEPTION_H

#include 

using namespace std;

class Exception
{
private:
    string m_fileName;
    string m_functionName;
    int    m_lineNumber;

protected:
    string m_msg;

public:
     Exception(string msg);
     virtual ~Exception();

     string getMessage();     
     string getFileName();
     string getFunctionName();
     int    getLineNumber();
};

#endif  // EXCEPTION_H

بالنسبه للدوال التى تبدأ بـ get فهى Accessor function و الكود الذى يهمنا هو كود المشيد حيث سيكون كالتالى:



Exception::Exception(string msg)
{
   m_msg          = msg;
   m_fileName     = __FILE__;
   m_functionName = __FUNCTION__;
   m_LineNumber   = __LINE__;
}


طبعا هذه الـ class لن تعمل كما هو متوقع منها لإنى نسيت شئ هام جدا و هو ان الماكرو سيتم عمل إحلال لإسمه بالقيمه الخاصه به و التى فى حالتنا هذه يأخذها من الـ preprocessor و تكون على التوالى اسم الملف و اسم الداله و رقم السطر و سيكونوا اسم الملف الخاص بهذه الـ class و الداله ستكون Exception::Exception اما رقم السطر سيكون على حسب كتابتى للكود.

بخلاف السابق الـ class ستعمل و لكن ما الهدف منها مادام الأمر يقتصر على متغير string يمكن الإستغناء عنها.

و سؤالى هل يمكن معرفة اسم الملف و الداله و رقم السطر الذى حدث بهم الخطأ اثناء runtime?


و الله ولى التوفيق

خبير  أحمد عبد الغني مشاركة 2

ممكن تعطي مثال عن كيف تستخدم هذا الكلاس حالياً في الكود؟

اللهم انصر أهلنا في فلسطين وآجرنا أن نكون عوناً لهم

مبتدئ  محمد علاء الدين عبد العزيز مشاركة 3

#include 
#include "Exception.h"

using namespace std;

double divide(double x, double y);

void main{}
{
  double resault=0;
  try
  {
    resault = divide(5.0 , 0.0);
  }
  catch (Exception& ex)
  {
    cout << "an error ocuured:" << '\n';
    cout << "Message: " << ex.getMessage();
    cout << "FileName: " << ex.getFileName();
    cout << "FunctionName: " << ex.getFunctionName();
    cout << "LineNumber: " << ex.getLineNumber();
  }
}


double divide(double x, double y)
{
   if ( y==0 )
      throw Exception("Divide by Zero.");

   return x/y;
}

خبير مدير وسام البهنسي مشاركة 4

ما رأيك باستخدام ماكرو إضافي يقوم بتعبئة المعلومات بشكل خفي:
 
#define At(txt) (txt),__LINE__,__FILE__
 
ومن ثم يتم تعديل الكلاس ليأخذ البارمترات بشكل مباشر:

Exception::Exception(string msg, const char* file, const char* function, int line)
{
   m_msg          = msg;
   m_fileName     = file;
   m_functionName = __FUNCTION__;
   m_LineNumber   = __LINE__;
}

 الآن يصبح كود رمي الاستثناء كالآتي:
 

throw Exception(At("Divide by Zero."));
 
كما أنك تستطيع ضم كل من Exception و At في ماكرو واحد:


#define ExceptionAt(txt) Exception((txt),__LINE__,__FILE__)
 
.
.
.
 
throw ExceptionAt("Divide by Zero.");
 



وفي 28/رمضان/1429 09:48 ص، قال محمد علاء الدين عبد العزيز متحمساً:

سؤالى هل يمكن معرفة اسم الملف و الداله و رقم السطر الذى حدث بهم الخطأ اثناء runtime?

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

وسام البهنسي
مبرمج في إنفيديا وإنفريمز