الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

template function pointer

بدأه C++er في 10 مايو 2010 · 6 رد · 782 مشاهدة · في لغة C و ++C
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

اثناء عملى فى احد البرامج احتجت لعمل template function pointer و لكن بالطبع اللغه لا تدعم هذا النوع من المؤشرات و لا اعلم لماذا ؟؟؟

template <class T>
struct IObject
{
   virtual T    getValue() = 0;
   virtual void setValue( T value ) = 0;
};

template <class T>
typedef void (*OnObjectChanged) (IObject<T>* obj);

قمت بالتحايل على هذه المشكله بإستخدام void*، و سؤالى الأن هل يوجد بديل للـ template function pointer (ليس فى هذا المثال و لكن عموما)؟؟

تم تعديل هذه المشاركة بواسطة Muhammad alaa في 10 مايو 2010 في 20:49

مدونتي: C++ Tips and Tricks

#2

السلام عليكم ...

لا أدري إن كان التالي هو ما تريده, و لكن لو وضعت مثالاً توضح فيه كيفية استخدامك من الممكن أن نصل إلى شيء أفضل:

template <class T>
struct IObject
{
	virtual T    getValue() = 0;
	virtual void setValue( T value ) = 0;
};

struct Number : public IObject<int>
{
	Number() : n(0)				{ }
	int  getValue()				{ return n; }
	void setValue( int value )	{ n = value; }
private:
	int n;
};

template <class T>
struct factory
{
	typedef void (*OnObjectChanged) (IObject<T>* obj);
};

void print_value(IObject<int>* o)
{
	std::cout << o->getValue();
}

int main()
{
	IObject<int>* o = new Number;
	factory<int>::OnObjectChanged fun = print_value;

	o->setValue(101);
	fun(o);
}

كل ما قمت به هو كتابة factory لإنتاج مؤشرات الدوال حسب الـ T التي تحددها.

تحياتي...

2
#3

شكرا لك اخى خالد هذا هو المطلوب

بالنسبه للمثال الذى وضعته انت فهو مثال جيد و لكن بالنسبه للـ factory<int>::OnObjectChanged فأعتقد انه المفترض ان تكون داخل الفئه Iobject حيث انها ستكون مشتركه بين جميع الفئات التى سترثها، بمعنى اعتقد ان الفئه Iobject من المفترض أن تبدو كذلك:

template <class T> class IObject;

template <class T>
struct factory
{
        typedef void (*OnObjectChanged) (IObject<T>* obj);
};

template <class T>
struct IObject
{
	IObject() : _valueChanged(0) {}
        virtual ~IObject() {}

        virtual T    getValue() = 0;
        virtual void setValue( T value ) = 0;

        void setOnObjectChanged(factory<T>::OnObjectChanged callback) { _valueChanged = callback; }
        bool IsOnObjectChangedPresent() { return _valueChanged != 0; }

protected:
        factory<T>::OnObjectChanged _valueChanged;
};

struct Number : public IObject<int>
{
        Number() : IObject<int>(), n(0) { }
        int  getValue()                 { return n; }
        void setValue( int value )      { n = value; }
private:
        int n;
};


void print_value(IObject<int>* o)
{
        std::cout << o->getValue();
}

int main()
{
        IObject<int>* o = new Number;
        o->setOnObjectChanged(print_value)
        o->setValue(101);
}

الكود لا يعمل معى و يقول لى ان تعريف المعامل OnObjectChanged للداله setOnObjectChanged غير معرف؟ لازلت ادرس الـ template و مستواى بها ليس جيدا اطلاقا لذا لا اعرف ما المشكله ؟

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

مدونتي: C++ Tips and Tricks

#4

السلام عليكم أخي محمد,

اقتباس
الكود لا يعمل معى و يقول لى ان تعريف المعامل OnObjectChanged للداله setOnObjectChanged غير معرف؟ لازلت ادرس الـ template و مستواى بها ليس جيدا اطلاقا لذا لا اعرف ما المشكله ؟

و من قال لك أني أنا الحريف في الـ templates :P

سأعطيك ما عندي باختصار,

المشكلة هي أن المترجم في السطرين التاليين:

void setOnObjectChanged(factory<T>::OnObjectChanged callback)
...
factory<T>::OnObjectChanged _valueChanged;

لا يمكنه معرفة أن:

factory<T>::OnObjectChanged

عبارة عن نوع, و ليس قيمة static كـ object مثلاً, و هذا أحد الأسباب التي تجعل قواعد ++C من نوع Context-Sensitive! إذا أردت البحث أكثر عنها يمكنك البحث عن Dependent Type Names. للأسف لا أعرف الكثير عن قواعدها و لا أريد المعرفة لأنها وجع رأس, حاولت أكثر من مرة و لكن لم أستطع الإلمام بها :(

انظر كيف يمكن أن تقول للمترجم بأن ما نشيره إليه هو نوع, و ليس قيمة static:

#include <iostream>
using namespace std;

template <class T> struct IObject; // IObject is struct!

template <class T>
struct factory
{
	typedef void (*OnObjectChanged) (IObject<T>* obj);
};

template <class T>
struct IObject
{
	IObject() : _valueChanged(0) {}
	virtual ~IObject() {}

	virtual T    getValue() = 0;
	virtual void setValue( T value ) = 0;
	// precede it with typename
	void setOnObjectChanged(typename factory<T>::OnObjectChanged callback) { _valueChanged = callback; }
	bool IsOnObjectChangedPresent() { return _valueChanged != 0; }

protected:
	// here is another situation!
	typename factory<T>::OnObjectChanged _valueChanged;
};

struct Number : public IObject<int>
{
	Number() : IObject<int>(), n(0) { }
	int  getValue()                 { return n; }
	void setValue( int value )      { n = value; }
private:
	int n;
};


void print_value(IObject<int>* o)
{
	std::cout << o->getValue();
}

int main()
{
	IObject<int>* o = new Number;
	o->setOnObjectChanged(print_value); // don't forget the semi-colon ;)
		o->setValue(101);
}

إضافة بسيطة للفائدة سأحاول فيها توضيح متى استخدام typename, و ربما طرأ السؤال في ذهنك و هو أن الكود الذي وضعته أنا في البداية يعمل دون الحاجة إلى typename. كيف تعرف أنك تحتاجها؟

ببساطة, عندما تقوم باستخدام نوع معرف داخل فئة ما, فإنه لابد من استخدام typename إذا قمت بتمرير أحد الـ Template Parameters الممررة إلى فئتك أصلاً!!!!

مثال بسيط:

template <class T>
struct factory
{
	typedef T inner_type;
};

template <class T>
struct user
{
	factory<int>::inner_type value;
};

لم نستخدم typename لأننا لم نمرر T إلى factory. بينما:

template <class T>
struct factory
{
	typedef T inner_type;
};

template <class T>
struct user
{
	typename factory<T>::inner_type value;
};

نحتاج typename في هذه الحالة, لأننا قمنا بتمرير T إلى factory.

ربما تتسائل عن السبب؟! و لكن ما أفهمه, هو أنه في الحالة الأولى عندما يرى المترجم:

factory<int>::inner_type value;

فإنه يستطيع الوصول إلى inner_type لأنه يعلم بالضبط ماهو النوع, بينما في الحالة الثانية لم يتم تحديد النوع, و لا تنخدع بتمرير T, لأن المترجم لا يعلم حتى الآن ماهو النوع الفعلي, فعلى سبيل المثال, يمكن أن يكون هناك Specializations تستخدم الاسم inner_type على أنه نوع, و أخرى تستخدم inner_type على أنها قيمة static من نوع ما. و طبعاً إن لم توضح أنه نوع, فإن اللغة تصميمها الأصلي يفترض أن ما بعد :: عبارة عن static member! و لهذا استحدثت typename!

===================================================================

الآن بما أنك وضحت لنا طريقة استخدامك, سأطرح طريقة أبسط:

template <class T>
struct IObject
{
	typedef void (*Callback) (IObject<T>*);

	IObject() : _valueChanged(0) {}
	virtual ~IObject() {}

	virtual T    getValue() = 0;
	virtual void setValue( T value ) = 0;
	// precede it with typename
	void setOnObjectChanged(Callback callback) { _valueChanged = callback; }
	bool IsOnObjectChangedPresent() { return _valueChanged != 0; }

protected:
	// here is another situation!
	Callback _valueChanged;
};

ما رأيك بأن نلقي نظرة على Boost.Signal أيضاً :cool:

تم تعديل هذه المشاركة بواسطة Khaled.Alshaya في 11 مايو 2010 في 21:35

2
#5

ممتاز اخى خالد بارك الله فيك.

اقتباس
// IObject is struct!

خطأ كتابى شكرا لتصحيحه، الـ templates على وشك انها تجننى :wacko: .

اقتباس
بينما في الحالة الثانية لم يتم تحديد النوع, و لا تنخدع بتمرير T, لأن المترجم لا يعلم حتى الآن ماهو النوع الفعلي

كيف لا يعلم المترجم النوع الفعلى، أليس T هو بحد ذاته نوع؟

ايضا ما الفرق فى هذه الحاله بين استخدام typename و class كنوع لمعامل الـ template.

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

مدونتي: C++ Tips and Tricks

#6

أهلاً أخ محمد,

كيف لا يعلم المترجم النوع الفعلى، أليس T هو بحد ذاته نوع؟

صحيح, و لكن لغة ++C قواعدها Context Sensitive بمعنى آخر, تفسير ما تكتبه يعتمد على السياق بجانب الـ Syntax و ليس على الـ Syntax لوحده. فلو نظرنا إلى السطر التالي:

factory<int>::OnObjectChanged fun = print_value;

فإن المترجم, يمكنه أن يفسر OnObjectChanged بطريقتين, الأولى أنها static member, و هذا هو الـ Default Context و في الحالة الثانية, يمكنه أن يفسرها على أنها نوع داخي. الذي يجعله يفسرها على أنها نوع داخلي, هو أن المترجم يملك جميع المعلومات اللازمة حيث سيقوم بعمل Instantiation للـ Template المسمى factory و بالتالي, استطاع أن يحدد المطلوب. بينما في الحالة الثانية:

typename factory<T>::OnObjectChanged _valueChanged;

فإن المترجم لا يملك أي معلومات لأنه لم يقم بعمل Instantiation, بالتالي فإنه سيفترض أن المراد هو static member. بالطبع هذه هي قواعد اللغة نفسه, و المترجم يطبقها لا أكثر. و كما قلت لك, الموضوع وجع رأس أصلاً :P

ربما في الأيام القادمة نلقي نظرة على Boost.Function و Boost.Signal,

تحياتي....

1
#7

جزاك الله كل خير اخى خالد

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

مدونتي: C++ Tips and Tricks

مواضيع مشابهة