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

استفسار عن ال Partail Template Specialization

بدأه ASDen في 25 أغسطس 2009 · 8 رد · 1,903 مشاهدة · في الأسئلة المجابة
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

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

المشكله كالتالى ,, لدينا Template class نريد ان نصنع منه Specialization لاى سبب من الاسباب المتعدده لذلك

جيد ,, المشكله الان انه ليس كل دوال ال class تحتاج / تستفيد من ذلك انما بعضها فقط

هنا نأتى الى مشكله انه من غير المنطقى لاسباب كثيره معروفه ان اعيد كتابه الكود هنا وهناك ,, اريد ان اكتبه مره واحده فقط ويستخدمه الاثنان

المشكله على ما يبدو ان هذا لا يعمل مع ال Partial Specialization

الكود التالى لا يعمل :

 


[color= #0000ff;]template [color= #000000;]<[color= #0000ff;]class T1,[color= #0000ff;]class T2[color= #000000;]>

[color= #0000ff;]class A

[color= #000000;]{

[color= #0000ff;]public[color= #000000;]:

    [color= #0000ff;]virtual [color= #0000ff;]void max[color= #000000;](T1 a,T2 b[color= #000000;])

    [color= #000000;]{

        cout[color= #000000;]<<[color= #A31515;]"max called T1,T2 ,b="[color= #000000;]<<b[color= #000000;]<<endl;

    [color= #000000;]}

    [color= #0000ff;]void mmax[color= #000000;](T1 a,T2 b[color= #000000;])

    [color= #000000;]{

        cout[color= #000000;]<<[color= #A31515;]"mmax called b="[color= #000000;]<<b[color= #000000;]<<endl;

    [color= #000000;]}

[color= #000000;]};

 

[color= #0000ff;]template [color= #000000;]<[color= #0000ff;]class T1[color= #000000;]>

[color= #0000ff;]class A[color= #000000;]<T1,[color= #0000ff;]float[color= #000000;]>

[color= #000000;]{

[color= #0000ff;]public[color= #000000;]:

    [color= #0000ff;]void max[color= #000000;](T1 a,[color= #0000ff;]float b[color= #000000;])

    [color= #000000;]{

        cout[color= #000000;]<<[color= #A31515;]"max called T1,T2 ,b="[color= #000000;]<<b[color= #000000;]<<endl;

    [color= #000000;]}

    [color= #0000ff;]void mmax[color= #000000;](T1 a,T2 b[color= #000000;]); [color= #007f00;]// whether this line is here or deleted

[color= #000000;]};

[color= #0000ff;]int _tmain[color= #000000;]([color= #0000ff;]int argc, _TCHAR[color= #000000;]* argv[color= #000000;][[color= #000000;]][color= #000000;])

[color= #000000;]{

    A[color= #000000;]<[color= #0000ff;]int,[color= #0000ff;]float[color= #000000;]> asd;

    asd.[color= #808000;]max[color= #000000;]([color= #ff0000;]1,[color= #ff0000;]2.5[color= #000000;]);

    asd.[color= #808000;]mmax[color= #000000;]([color= #ff0000;]2,[color= #ff0000;]3.25[color= #000000;]);[color= #007f00;]//it fails here

    [color= #0000ff;]return [color= #ff0000;]0;

[color= #000000;]}

الطريقه الوحيده التى عملت معى هى ادخال ال Inheritance فى الموضوع

كالتالى :

 


[color= #0000ff;]template [color= #000000;]<[color= #0000ff;]class T1,[color= #0000ff;]class T2[color= #000000;]>

[color= #0000ff;]class A

[color= #000000;]{

[color= #0000ff;]public[color= #000000;]:

    [color= #0000ff;]virtual [color= #0000ff;]void max[color= #000000;](T1 a,T2 b[color= #000000;])

    [color= #000000;]{

        cout[color= #000000;]<<[color= #A31515;]"max called T1,T2 ,b="[color= #000000;]<<b[color= #000000;]<<endl;

    [color= #000000;]}

    [color= #0000ff;]void mmax[color= #000000;](T1 a,T2 b[color= #000000;])

    [color= #000000;]{

        cout[color= #000000;]<<[color= #A31515;]"mmax called b="[color= #000000;]<<b[color= #000000;]<<endl;

    [color= #000000;]}

[color= #000000;]};

 

[color= #0000ff;]template [color= #000000;]<[color= #0000ff;]class T1[color= #000000;]>

[color= #0000ff;]class dA [color= #000000;]: [color= #0000ff;]public A[color= #000000;]<T1,[color= #0000ff;]float[color= #000000;]>

[color= #000000;]{

[color= #0000ff;]public[color= #000000;]:

    [color= #0000ff;]void max[color= #000000;](T1 a,[color= #0000ff;]float b[color= #000000;])

    [color= #000000;]{

        cout[color= #000000;]<<[color= #A31515;]"derived max called T1,T2 ,b="[color= #000000;]<<b[color= #000000;]<<endl;

    [color= #000000;]}

[color= #000000;]};

 

[color= #0000ff;]int _tmain[color= #000000;]([color= #0000ff;]int argc, _TCHAR[color= #000000;]* argv[color= #000000;][[color= #000000;]][color= #000000;])

[color= #000000;]{

    dA[color= #000000;]<[color= #0000ff;]int[color= #000000;]> asd;

    asd.[color= #808000;]max[color= #000000;]([color= #ff0000;]1,[color= #ff0000;]2.5[color= #000000;]); [color= #007f00;]//derived max called

    asd.[color= #808000;]mmax[color= #000000;]([color= #ff0000;]2,[color= #ff0000;]3.25[color= #000000;]);[color= #007f00;]//mmax called

    [color= #0000ff;]return [color= #ff0000;]0;

[color= #000000;]}

هل هذه الطريقه هى ما يستخدم عاده ؟ هل هناك طرق اخرى ؟ اى مشاكل معروفه فى ال Performance ؟

شكراً

#2

السلام عليكم ,,

تعديل بسيط فقط,

template <class T1,class T2>
class A
{
public:
	void max(T1 a,T2 b)
	{
		cout<<"max called T1,T2 ,b="<<b<<endl;
	}
	void mmax(T1 a,T2 b)
	{
		cout<<"mmax called b="<<b<<endl;
	}
};

لاحظ أنني أزلت virtual من تعريف max.

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

لكي نقوم باستدعاء الدالة المناسبة عن طريق مؤشر الأب polymorphism. و لاحاجة لذلك في المثال الذي وضعته.

في مثالك الأول:

asd.mmax(2,3.25);//it fails here

السبب ببساطة لأن T2 ليس له وجود في الـ specialized class.

void mmax(T1 a,T2 b); // whether this line is here or deleted

لاحظ أنك عندما تقوم بعمل specialization فإن الصنف الـ specialized لا يمت بصلة للصنف الأصلي. لايوجد هناك وراثة.

عموماً اسلوب الـ specialization لا يستخدم بهذه الطريقة عادة, و لكن عندما يكون هناك implementation مختلف للصنف في حالة كان أحد الـ parameters من نوع معين.

فمثلاً,

std::vector<bool>

عبارة عن specialization, و يتم استخدام bit واحد فقط لكل عنصر بدلاً من bool. لاحظ أنك كمستخدم لـ vector لن تلاحظ الفرق,

لذلك إعادة استخدام الكود اسلوب يعارض الفكرة الأساسية من وراء الـ specialization.

موضوع الـ templates في ++C من مواضيع العيار الثقيل جداً, و ليس المهم فهم الـ syntax, عليك بـ Modern Cpp Design لمخترع الـ templates نفسه إذا أردت فهم متى تستخدم هذه التقنيات.

تحياتي ...

تم تعديل هذه المشاركة بواسطة Khaled.Alshaya في 25 أغسطس 2009 في 21:24

#3

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

الموضوع جميل و حرام أن نتركه يذهب أدراج الرياح :)

اقتباس
المشكله كالتالى ,, لدينا Template class نريد ان نصنع منه Specialization لاى سبب من الاسباب المتعدده لذلك

السبب الرئيسي الذي يجعلنا نقوم بعمل specialization لـ class هو سبب واحد في أغلب الأحيان. عندما نقوم بكتابة template class فإنه قد يظهر لنا أنه في حالات خاصة, يمكن زيادة كفاءة الـ generic class دون المساس بواجهته,

بالتالي فإن مستخدم الـ generic class لن يرى أي فرق, و سيقوم المترجم باختيار النسخة الأفضل, كما في حالة الـ vector على سبيل المثال عندما نقوم بتمرير bool كـ template parameter.

الـ template specialization بالمناسبة هو مايجعل الـ templates في ++C لغة برمجة functional. هذا يسمى الـ pattern matching. و هذا هو الاستخدام المتقدم جداً للـ templates في ++C.

سأبتعد قليلاً عن مثالك, و أعطيك مثالاً, تصور أننا نريد طباعة اسم النوع الذي نمرره للـ template parameter. حسناً لاحظ التالي: أريد أن أقوم بكتابة كود يطبع اسم النوع إذا كان النوع int على سبيل المثال:

template <class Type>
struct printer
{
	void print()
	{
		if(nameof(Type) == int)
			std::cout << "WoW it is int!";
	}
};

الكود الذي في الأعلى من وحي الخيال و ليس من ++C بشيء, السبب أن if يقوم المترجم بتحويلها إلى كود للآلة و لا يقوم "هو" بتنفيذها. أحد الحلول هو أن نقوم بالتالي:

template <class Type>
struct isInteger
{
	static const bool v = false;
};

template <>
struct isInteger<int>
{
	static const bool v = true;
};

template <class Type>
struct printer
{
	void print()
	{
		if(isInteger<Type>::v == true)
			std::cout << "WoW it is int!";
	}
};

لاحظ كيف أن isInteger عبارة عن struct عادي, و لكننا قمنا باستعمال specialization كـ if statement لحظة الترجمة.

عموماً, عندما تريد فعل شيء متقدم جداً كهذا metaprogramming, فإنه من الضروري أن لا تعبث على الإطلاق مع الـ templates مباشرة, و إنما من الأفضل أن تقوم باستخدام MPL من boost, و ستجد شيئاً اسمه enableif كأنه دالة عادية :)

اقتباس
هنا نأتى الى مشكله انه من غير المنطقى لاسباب كثيره معروفه ان اعيد كتابه الكود هنا وهناك ,, اريد ان اكتبه مره واحده فقط ويستخدمه الاثنان

عندما يكون هناك مشاركة في الكود, فإن لديك هرماً كائنياً على الأغلب. ربما لو كان لديك فكرة معينة تريد مناقشتها أنا متحمس للموضوع :)

#4

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

اقتباس
لاحظ أنني أزلت virtual من تعريف max.

امم فى حاله المثال الاول فأنا اعتذر كان هذا خطأ سببه انى نسخت الكود بعد التعديل (بعدما عدلت عليه ليكون الثانى :P )

اما فى المثال الثانى فأختلف معك هى صحيه ومفيده على المدى البعيد

اقتباس
السبب ببساطة لأن T2 ليس له وجود في الـ specialized class.

كان وضع T2 مجرد محاوله كيشوتيه

المشكله من الاصل هى معامله ال Specialized class ك independent class وبالتالى كما ذكرت ازاله السكر او وضعه لا يعطى لها ال Compiler بالا

اقتباس
عموماً اسلوب الـ specialization لا يستخدم بهذه الطريقة عادة, و لكن عندما يكون هناك implementation مختلف للصنف في حالة كان أحد الـ parameters من نوع معين.

فمثلاً,

امم اختلف معك هنا

اى نعم ال Specialization فائدته هى تقديم Implementation محدده لنوع معين

ولكن من فضلك لا تقل لى Implementation مختلف تماما لا يوجد شىء مثل هذا ,, دائما هناك عناصر هى فعلا Generic واخرى نحتاج ان نقدم فيها type dependent implementation

وهنا استخدام ال Specialization كما هى يؤدى الى عمليه ال Copy & Paste -- > شىء مقيت لابد من التخلص منه بأى شكل كان

بعد اشارتك لل vector<bool> حاولت ان اطلع ال source code له ,, طبعا كود عجيب ولكن ما فهمته - ان كان ما فهمته صحيحا واشك فى هذا :P -

فى ال vector<bool> هرب من تكرار الكود والتزامه بالحفاظ على ال interface بأن انشأ interface مشابهه لل vector<_Typ> ثم استعمل متغير داخلى من نوع vector<int>

استخدمه فى ان ينادى ال methods الاصليه المقابله فى ال vector<_Typ> (و الاداره بشكل عام طبعا(

وفى بعض ال methods الاخرى فوجئت بلجوئه لل Copy&Paste

-----

المهم الحمد لله بعد بحث وصلت لما ابحث عنه تماما : انظر هنــــا :

أسلوب جميل ومنطقى والاجمل انه يستخدم فكرتى :P فى ادخال ال inheritance ولكن بشكل انظف بكثير طبعا

ووجدته ايضا للكتاب الذى ذكرت يا اخ خالد Modern C++ Design

وجدت فيه مناقشه اعمق لهذه ال Pattern ( ال Policy pattern (

دون دخول فى تفاصيل كثيره هى كاتالى (Ch1 من الكتاب فى تفاصيل اعمق)

يكون عندك class يوضع فيه كل ال generic code ,, ثم هذا ال class يرث ال specializations من classes اخرى

فيها يتم كتابه ال type dependent code أو لنقل الذى يعتمد على فعل خاصيه معينه ليفعل فعل معين

عودا على المثال الاول لو طبقنا ال policies يكون بالشكل التالى

 


[color= #0000ff;]template [color= #000000;]<[color= #0000ff;]class T[color= #000000;]>

[color= #0000ff;]class A[color= #000000;]: [color= #0000ff;]public T

[color= #000000;]{

[color= #0000ff;]public[color= #000000;]:

    [color= #0000ff;]void mmax[color= #000000;]([color= #000000;])

    [color= #000000;]{

        cout[color= #000000;]<<[color= #A31515;]"mmax called "[color= #000000;]<<endl;

    [color= #000000;]}

[color= #000000;]};

 

[color= #0000ff;]template [color= #000000;]<[color= #0000ff;]class T[color= #000000;]>

[color= #0000ff;]class maxA

[color= #000000;]{

[color= #0000ff;]public[color= #000000;]:

    [color= #0000ff;]void max[color= #000000;](T a,[color= #0000ff;]int b[color= #000000;])

    [color= #000000;]{

        cout[color= #000000;]<<[color= #A31515;]"maxA called ,b="[color= #000000;]<<b[color= #000000;]<<endl;

    [color= #000000;]}

[color= #000000;]};

 

[color= #0000ff;]template [color= #000000;]<[color= #0000ff;]class T[color= #000000;]>

[color= #0000ff;]class maxB

[color= #000000;]{

[color= #0000ff;]public[color= #000000;]:

    [color= #0000ff;]void max[color= #000000;](T a,[color= #0000ff;]float b[color= #000000;])

    [color= #000000;]{

        cout[color= #000000;]<<[color= #A31515;]"maxB called ,b="[color= #000000;]<<b[color= #000000;]<<endl;

    [color= #000000;]}

[color= #000000;]};

 

[color= #0000ff;]int _tmain[color= #000000;]([color= #0000ff;]int argc, _TCHAR[color= #000000;]* argv[color= #000000;][[color= #000000;]][color= #000000;])

[color= #000000;]{

    [color= #0000ff;]typedef A[color= #000000;]<maxA[color= #000000;]<[color= #0000ff;]int[color= #000000;]>> MyMaxA;

    [color= #0000ff;]typedef A[color= #000000;]<maxB[color= #000000;]<[color= #0000ff;]int[color= #000000;]>> MyMaxB;

 

    MyMaxA ma;MyMaxB mb;

    ma.[color= #808000;]max[color= #000000;]([color= #ff0000;]10,[color= #ff0000;]10[color= #000000;]);[color= #007f00;]//specilised for policy A

    ma.[color= #808000;]mmax[color= #000000;]([color= #000000;]);[color= #007f00;]//generic code

    mb.[color= #808000;]max[color= #000000;]([color= #ff0000;]20,[color= #ff0000;]20.5[color= #000000;]);[color= #007f00;]//specilised for policy B

    mb.[color= #808000;]mmax[color= #000000;]([color= #000000;]);[color= #007f00;]//generic code

 

    [color= #0000ff;]return [color= #ff0000;]0;

[color= #000000;]}

---

تجدوا الكتاب الجميل هنا : http://rs358.rapidshare.com/files/89441641...lied__2001_.rar

---

اقتباس
عندما يكون هناك مشاركة في الكود, فإن لديك هرماً كائنياً على الأغلب. ربما لو كان لديك فكرة معينة تريد مناقشتها أنا متحمس للموضوع

عندى صراحه ,, فالموضوع جزء من مشروع اكثر منه تعليم مطلق

مازال التصميم يحتاج تخيل اكثر لان الخيارات ضخمه ,, باذن الله استقر على prototype واضعه هنا للمناقشه اليوم او غدا ان شاء الله

#5

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

الموضوع الذي أشرت إليه أجاب عليه litb الألماني, هذا الشاب بالفعل معجزة برمجية, أعتقد أنه يحفظ الـ standards الخاصة بـ ++C :lol:

هو بالمناسبة يجيب باستمرار عن الأسئلة حول الـ templates في SO.....

نعود لموضوعنا....

عندما تقوم بإنشاء vector من النوع bool, فهذا عبارة عن specialization و لايهم كيف يقوم بعمل implementation سواء كان الـ implementation يستخدم vector من نوع int أو كان يقوم بإنشاء المصفوفة يدوياً أو غير ذلك.

المهم أنه يوفر نفس الـ interface للـ generic vector, لأن مستخدم الـ vector يجب أن لا يحس بأي فرق عند استخدام vector سواء كان نوعه bool أو غير ذلك....

للأسف أختلف معك في حكاية الوراثة, السبب أن الـ specialization هو توفير واجهة موحدة لـ implementation مختلف.... و عندما يكون هناك مشاركة في فكرة الـ implementation بين الـ generic و بين الـ specialized ففي الغالب, الـ specialization ليس هو الحل, بل على العكس ربما يعقد الحل.

الـ policy-based design من أقوى أفكار ++C, و صدقني بمجرد أن تتعرف على هذه الفكرة العبقرية لـ Alexandrescu, ستغير نظرتك لـ ++C تماماً...

دعني أحاول مناقشة المثال في الموضوع الذي أشرت إليه بمثال من عندي.

تصور أننا نريد بناء stack, بسيطة جداً, عبارة عن push و pop و top, و isempty و size! لا أكثر و لا أقل, أعتقد أنك تستطيع و أستطيع بناء stack بسيط في نصف ساعة.

و لكن هناك نقطة بسيطة نود مناقشتها, و هي ماهو الـ implementation الذي تريد استخدامه للـ stack ؟!

هل تريد أن تستخدم dynamic array أو linked list ؟!

حسناً ربما تقوم بصناعة stack يستخدم dynamic array و stack آخر يستخدم الـ linked list. كما ترى أن الواجهة واحدة, و لكن الـ implementation مختلف.

و لكن الـ modern Cpp ترفض هذا التصميم الضعيف, و تقترح policy-based stack...... بحيث أننا نقوم بتمرير الـ implementation كـ template parameter للـ stack class.

بالطبع دون كود لن تتضح الفكرة:

template <class Type, class Container>
class stack : private Container
{
public:

	stack() { };

	bool IsEmpty()
	{
		return empty();
	}

	std::size_t Size()
	{
		return size();
	}

	Type& Top()
	{
		return back();
	}

	void Push(const Type& value)
	{
		push_back(value);
	}

	void Pop()
	{
		pop_back();
	}
};

الآن, يمكنك استخدام أي Container أو أي صنف تريده, مادام يوفر:

empty()
size()
back()
push_back(value)
pop_back()

على سبيل المثال, يمكنني استخدام الصنف الذي قمت بكتابته في ثلاث دقائق كالتالي:

stack<int, std::vector<int> > arrayStack;
stack<int, std::list<int> > listStack;

هذا هو الـ policy-based design في أبسط اشكاله :)

أنا استخدمت الوراثة, و لكن كما ترى ليس لإعادة استخدام الكود. و إنما فرضت policy على جميع الـ containers بأن توفر الدوال المطلوبة و استخدمها دون معرفة حتى نوع الـ container الذي سيوفرها.

لاحظ أن litb في مثاله الذي أشرت إليه, فعل نفس الشيء, و لكنه نسي أن يرث private أو protected و أعتقد أنه سها عن ذلك لا أكثر,

لأن tcp أو udp ليس بينهما و بين service علاقة وراثة من أب لإبن parent/child على الإطلاق, كل ما في العملية أننا نقوم بتركيب الـ classes مع بعضها البعض في عملية تشبه تركيب المكعبات لنحصل على الـ class النهائي في النهاية.

في الحقيقة يمكنك الاستغناء عن الوراثة بشكل كامل, و إعادة كتابة المثال على الشكل التالي بعمل composition بدلاً من وراثة protected أو private...

template<typename Transport>
class service
{
public:
	typedef Transport transport_type;

	// common code
	void do_something() { 
		t.send(....);
	}
private:
	transport_type t;
};

مثال الـ stack ليس من عندي, فهكذا هو الـ stack الموجود أصلاً في STL و لكن قمت بعرضه بشكل مبسط جداً و بأخطاء فظيعة فقط لتوضيح الفكرة... أردت إلقاء الضوء على هذا الـ design pattern مع الوراثة في ++C :)

لأن الوراثة في ++C ليس من الضروري أن تكون علاقة is-a, بل من الممكن أن تكون has-a إذا كانت private أو protected بكل بساطة كما رأيت...

أعتقد أن الموضوع جميل لكي نتعلم منه, و أكرر إن كان لديك فكرة معينة فلا تبخل علينا :P

تحياتي....

تم تعديل هذه المشاركة بواسطة Khaled.Alshaya في 27 أغسطس 2009 في 03:57

#6

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

اقتباس
اقتباس
المشكله كالتالى ,, لدينا Template class نريد ان نصنع منه Specialization لاى سبب من الاسباب المتعدده لذلك

السبب الرئيسي الذي يجعلنا نقوم بعمل specialization لـ class هو سبب واحد في أغلب الأحيان. عندما نقوم بكتابة template class فإنه قد يظهر لنا أنه في حالات خاصة, يمكن زيادة كفاءة الـ generic class دون المساس بواجهته,

هناك حالات أخرى يجب عمل الSpecialization والا فلن يعمل البرنامج بشكل صحيح ،، مثلا برمجنا كلاس يمثل لنا مصفوفه ببعدين كما يوضح الinterface التالي :

template <typename T, int WIDTH, int HEIGHT>
class Grid {
 public:
		void setElementAt(int x, int y, const T& inElem);
		T& getElementAt(int x, int y);
		const T& getElementAt(int x, int y) const;
		int getHeight() const { return HEIGHT; }
		int getWidth() const { return WIDTH; }
 protected:
		T mCells[WIDTH][HEIGHT];
};

الأن دوال Copy Constructor والAssignment Operator تعمل بالشكل الصحيح في الوضع الطبيعي (تمرير كائن Grid بالقيمة أو من خلال اسناد Grid لأخر ) حيث يتم النسخ بشكل مباشر Shallow Copy ..

لكن في حال أردنا أن ننشئ الGrid من خلال مصفوفه من الأحرف Pointer to Char هنا سيختلف الأمر وسيتوجب أن نتعامل مع دوال الctor وdtor (اختصار لConstructor و Destructor) وأيضا الCopy ctor وال Assignment Operator بشكل أخر (عن طريق الDeep Copy) .

لذلك الحل الأفضل في تلك الحالة هو عمل Specialization من الGrid لكي يتعامل مع الPointers كما في Interface التالي:

template <>
class Grid<char*>
{
 public:
		Grid(int inWidth = kDefaultWidth, int inHeight = kDefaultHeight);
		Grid(const Grid<char*>& src);
		~Grid();
		Grid<char*>& operator=(const Grid<char*>& rhs);

		void setElementAt(int x, int y, const char* inElem);
		char* getElementAt(int x, int y) const;
		int getHeight() const { return mHeight; }
		int getWidth() const { return mWidth; }

		static const int kDefaultWidth = 10;
		static const int kDefaultHeight = 10;
 protected:
		void copyFrom(const Grid<char*>& src);
		char*** mCells;
		int mWidth, mHeight;
};

هكذا سنضمن تماما عمل الكلاس سواء باستخدام متغيرات عادية أو مؤشرات .. وسيتم استدعاء الكلاس الأصلي أو المخصص على حسب النوع الممرر والأهم من ذلك كما ذكر خالد هو أن هذه العملية مخفية تماما عن مستخدم الكلاس :

// main
Grid<int> myIntGrid; // Uses original Grid template
Grid<char*> stringGrid1(2, 2); // Uses char* specialization

على ما أعتقد في مثل تلك الحالات يفضل عمل Full class template Specialization لأنه سيكون هناك تغييرات كثيره عن الكلاس الأصلي ،،

ويمكن أن يكون الSpecialization ليس فقط لمؤشر من نوع معين بل لأي نوع وهنا يكون الكلاس الجديد الصغير أكثر مرونه :

template <typename T>
class Grid<T*>
{
 public:
		Grid(int inWidth = kDefaultWidth, int inHeight = kDefaultHeight);
		Grid(const Grid<T*>& src);
		~Grid();
		Grid<T*>& operator=(const Grid<T*>& rhs);
		void setElementAt(int x, int y, const T* inElem);
		T* getElementAt(int x, int y) const;
		int getHeight() const { return mHeight; }
		int getWidth() const { return mWidth; }
		static const int kDefaultWidth = 10;
		static const int kDefaultHeight = 10;
protected:
		  void copyFrom(const Grid<T*>& src);
		  T** mCells;
		  int mWidth, mHeight;
};

هذه تقريبا من أشهر حالات عمل الspecialization ، أما بالنسبة للوراثه template inheritance فلم أجرب مثال عملي على ذلك ولكن الفكره نظريا بأنها كالوراثه العادية تماما (مع أن طريقة العمل مختلفة قليلا حيث سيتم انشاء instantiate الكلاس الأب بالنوع الذي سيتم تحديده عند انشاء الإبن) وتستخدم عند ارادة اضافة المزيد من الوظائف extending implementations وعند ادخال مفهوم الPolymorphims .

خلاصه :

اقتباس
Use inheritance for extending implementations and for polymorphism. Use specialization

for customizing implementations for particular types.

وشكرا أخواني على نقاشكم الرائع ،،

بالتوفيق :) .

http://informatic-ar.com منصة تعليمية عربية في علوم الحاسب والبرمجة

https://moalfat.com  للكتب الالكترونية والكورسات التعليمية

Everything we see now is just an engineering solution based on old science

#7
اقتباس
الـ policy-based design من أقوى أفكار ++C, و صدقني بمجرد أن تتعرف على هذه الفكرة العبقرية لـ Alexandrescu, ستغير نظرتك لـ ++C تماماً...

حسب ما أعرفه أن الهدف هو تطبيق مفهوم static polymorphism بأفضل طريقة .. وهو مافعله Alexandrescu .. بمعنى أن static polymorphism موجودة قبل policy-based design .. هل هذا صحيح ؟

بالنسبة لمفهوم Specialization الذي ذكره الاخ ASD في أول الموضوع :

سبق أن واجهت نفس المشكلة تقريبا .. مع كلاس String ( يونيكود و اسكي ) ..وحاولت حلها باستخدام مفهوم "التخصيص" .. لكن وجدت نفسي أنسخ وألصق كما ذكر ASD .. لا أشعر حينها أني مبرمج .. لم أفكر بالـ static polymorphism .. ولم أكن أعرف عن policy based design .. أي مشكلة أواجها .. حلها الوحيد هو الحل الذي تنهجه مكاتب الجافا .. هذا ما ترسّخ في رأسي :D

خرجنا بفائدة من هذا الموضوع .. جزيتم خيراً

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#8
اقتباس
عندما تقوم بإنشاء vector من النوع bool, فهذا عبارة عن specialization و لايهم كيف يقوم بعمل implementation سواء كان الـ implementation يستخدم vector من نوع int أو كان يقوم بإنشاء المصفوفة يدوياً أو غير ذلك.

المهم أنه يوفر نفس الـ interface للـ generic vector, لأن مستخدم الـ vector يجب أن لا يحس بأي فرق عند استخدام vector سواء كان نوعه bool أو غير ذلك....

ليس هذه النقطه ما اعنى

بحثت فى ال Code الخاص بال vector<bool> لافهم كيف تغلب على المشكله التى واجهتنى ,, ولكن كما قلت لم يعجبنى الاسلوب والطريقه -هذا كما قلت ان كنت فعلا فهمت الكود :P -

اقتباس
للأسف أختلف معك في حكاية الوراثة, السبب أن الـ specialization هو توفير واجهة موحدة لـ implementation مختلف.... و عندما يكون هناك مشاركة في فكرة الـ implementation بين الـ generic و بين الـ specialized ففي الغالب, الـ specialization ليس هو الحل, بل على العكس ربما يعقد الحل.

اذا يا اخى كيف توفر generic code وفى نفس الوقت تستخدم Specialization للجزء الاخر من ال Class

بالتاكيد هذه الحاله موجوده ان لم تكن هى الاعم

هنا يأتى دور Pattern مثل ال Policy فكرتها -كما ذكر مؤلفها - هى دمج ال

دمجهما للحصول على مزايا كل منهما كما يقول

اقتباس
أنا استخدمت الوراثة, و لكن كما ترى ليس لإعادة استخدام الكود. و إنما فرضت policy على جميع الـ containers بأن توفر الدوال المطلوبة و استخدمها دون معرفة حتى نوع الـ container الذي سيوفرها

.

بالضبط ,, الوراثه وضعت Compile-time constraints على ال Policy

ولكن الميزه المهمه الاخرى - Code reuse - اتت من وجود derived class ثابت يتم تركيب المزايا القابله للاختلاف فيه عن طريق ال Policies الخارجيه

وهو ماكان ينقص ال Templates وامكن وضعه بدمجها بالوراثه دمج ال General و ال Specialised فى مكان واحد وواجهه واحده

راجع امثله الكتاب تجد الكاتب كثيرا ما يضع generic functions فى ال derived classes

مثلا عمليه ال -> لل SmartPtr

هى عمليه عامه تعتمد على ال Specialized Implementations دون ان تحددهم

template
<
   class T,
   template <class> class CheckingPolicy,
   template <class> class ThreadingModel
>
class SmartPtr
   : public CheckingPolicy<T>
   , public ThreadingModel<SmartPtr>
{
   ...
   T* operator->()
   {
	  typename ThreadingModel<SmartPtr>::Lock guard(*this);
	  CheckingPolicy<T>::Check(pointee_);
	  return pointee_;
   }
private:
   T* pointee_;
};

ايضا انظر الى اجابه عمو litb

محدده وواضحه تحدد مكان ال common code و ال Special code اجابه على سؤال واحد عانى من نفس المشكله سابقه الذكر

اقتباس
أكرر إن كان لديك فكرة معينة فلا تبخل علينا

قريبا ان شاء الله ,, المهم فى الاخر لاتطلع حاجه لا علاقه لها بكل هذا :P

---

اقتباس
Use inheritance for extending implementations and for polymorphism. Use specialization

for customizing implementations for particular types

بالتأكيد يا اخ وجدى

وفكره Alex كما ذكرها فى كتابه هى دمجهما معا للحصول على مزايهما معا

#9
اقتباس
اذا يا اخى كيف توفر generic code وفى نفس الوقت تستخدم Specialization للجزء الاخر من ال Class

بالتاكيد هذه الحاله موجوده ان لم تكن هى الاعم

أنا متأكد أني لم أفهم المطلوب, لذلك حبة حبة علينا حتى نفهم بعض :)

ليس هناك علاقة على الإطلاق بين الـ generic class و بين الـ partial specialized أو full specialized بالنسبة لكاتب تلك الـ classes. مستخدم تلك الـ classes يجب أن لا يشعر بوجود الـ specialized class أصلاً.

هناك تأتي النقطة التي ذكرتها, و هي ماذا لو كان هناك كود مشترك بين الـ generic class و بين الـ partial specialized أو full specialized ؟

حسناً في هذه الحالة من الخطأ أن يرث الـ specialized من الـ generic, و لكن كلاهما يستعملان كود موجود في helper class.... مثلاً:

template <class T1, class T2>
class helper
{
///
};

template <class T1, class T2>
class genericClass : private helper<T1, T2>
{
///
};

template <class T1>
class genericClass<T1, float> : private helper<T1, float>
{
///
};

هذا ما أعتقد أنك تريد فعله, و هو طريقتي في الموضوع :)

رغم أني لست مقتنعاً بحالة فعلية واحدة تستدعي فعل هذا الأمر, لأنه في النهاية أنت تقوم بعمل Specialization لتغيير الـ implementation...

لاحظ أنك لا تحتاج الوراثة من الأساس لفعل هذا الشيء! يمكنك الاستغناء بعمل Composition بدلاً من ذلك. الوراثة هنا ليست علاقة is-a.

اقتباس
هنا يأتى دور Pattern مثل ال Policy فكرتها -كما ذكر مؤلفها - هى دمج ال

دمجهما للحصول على مزايا كل منهما كما يقول

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

فالصنف الذي يحتوي على الـ generic functions كما تسميهم في قاع الهرم الوراثي و ليس في قمته :)

النقطة التي لم أفهمها حتى الآن, هي ما علاقة الـ specialization بالـ policy-based design ؟

في مثال الـ SmartPtr الذي وضعته لا يوجد Specialization على الإطلاق! في الحقيقة أن STL و Boost من بابها لمحرابها مبنية على هذا التصميم... خذ مثلاً vector.

template<class Type, class Allocator = std::allocator<Type> >
class vector
{
///
};

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

يمكنك تمرير allocator لكي يقوم بالحجز على الـ stack, أو في الـ heap أو يمكنك تخصيصه كما تحب. أي أننا وضعنا policy في vector بأن أي allocator يجب أن يوفر مجموعة من الـ functions دون أن نعرف من هو الـ allocator المستخدم أصلاً.

اقتباس
ايضا انظر الى اجابه عمو litb

محدده وواضحه تحدد مكان ال common code و ال Special code اجابه على سؤال واحد عانى من نفس المشكله سابقه الذكر

هذا ما قلته من البداية. لا علاقة للـ specialization بالموضوع. تقوم ببناء service ثم تقوم بتمرير قطع أخرى من الكود udp أو tcp عن طريق الـ template parameters. لكي يتصرف service بشكل مختلف داخلياً دون تغيير واجهته على الإطلاق.

ربما بمثال بعيداً عن الـ templates يتضح الموضوع, تصور أنك تريد القيام بتصميم policy-based service باستخدام OOP كما في Java أو #C. سنقوم بكتابة التالي بكل بساطة:

class Transport
{
public:
	virtual void send(....) = 0;
};

class tcp : public Transport
{
	virtual void send(....)
	{
	// implementation!
	}
};

class udp : public Transport
{
	virtual void send(....)
	{
	// implementation!
	}
};

class service
{
	service(Transport* p) : proto(p)
	{

	}

	void do_something()
	{
		proto->send(....);
	}

private:
	Transport* proto;
};

لماذا إذا لا يتم استعمال OOP بدلاً من الـ templates؟

ببساطة ليس هناك type-saftey, و لا efficiency و لا يمكن للمترجم التحقق من أي شيء, و لا يمكنه عمل optimization على الإطلاق!

بينما في حالة الـ templates فإن المترجم سيقوم, ببناء class اسمها service و يقوم بالتحقق وقت الترجمة من صلاحية أي Transport ثم سيقوم بدمج كود الـ Transport بكود الـ service و يقوم بعمل optimization للـ service لأن كل متغيرات service معلومة وقت الترجمة!

أتمنى أن لا أكون أتكلم في وادي و أنت في وادي آخر :)

تحياتي ...

تم تعديل هذه المشاركة بواسطة Khaled.Alshaya في 27 أغسطس 2009 في 19:22

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