برمجة القوالب الوصفية
البرمجة الوصفية باستخدام القوالب ( TMP ) هي تقنية برمجة وصفية تستخدم فيها القوالب من قِبل المُصرّف لإنشاء شفرة مصدرية مؤقتة ، يقوم المُصرّف بدمجها مع بقية الشفرة المصدرية ثم يُجري عملية التجميع. يمكن أن تتضمن مخرجات هذه القوالب ثوابت وقت التجميع ، وهياكل بيانات ، ودوال كاملة . يُمكن اعتبار استخدام القوالب بمثابة تعدد أشكال وقت التجميع . تُستخدم هذه التقنية في عدد من اللغات، أشهرها C++ ، بالإضافة إلى Curl و D و Nim و XL .
تم اكتشاف برمجة القوالب الوصفية، بمعنى ما، عن طريق الصدفة. [ 1 ] [ 2 ]
تدعم بعض اللغات الأخرى إمكانيات وقت الترجمة المماثلة، إن لم تكن أقوى (مثل وحدات الماكرو في لغة Lisp ).
مكونات برمجة القوالب الوصفية
يتطلب استخدام القوالب كتقنية برمجة فوقية عمليتين متميزتين: تعريف القالب، وإنشاء نسخة منه . يُوصف الشكل العام لشفرة المصدر المُولَّدة في تعريف القالب، وعند إنشاء نسخة منه، يُستخدم هذا الشكل العام لتوليد مجموعة محددة من شفرة المصدر.
تُعتبر برمجة القوالب الوصفية كاملة تورينج ، مما يعني أنه يمكن حساب أي عملية حسابية يمكن التعبير عنها بواسطة برنامج حاسوبي، بشكل أو بآخر، بواسطة برنامج وصفي قالبي. [ 3 ]
تختلف القوالب عن وحدات الماكرو . وحدة الماكرو عبارة عن جزء من التعليمات البرمجية يُنفذ أثناء عملية الترجمة، ويقوم إما بمعالجة نصية للتعليمات البرمجية المراد ترجمتها (مثل وحدات ماكرو C++ )، أو بمعالجة شجرة بناء الجملة المجردة التي ينتجها المترجم (مثل وحدات ماكرو Rust أو Lisp ). وتتميز وحدات الماكرو النصية باستقلالها عن بناء جملة اللغة التي تُعالج، إذ أنها تُغير فقط النص الموجود في الذاكرة للتعليمات البرمجية المصدرية قبل الترجمة مباشرةً.
لا تحتوي البرامج الوصفية القائمة على القوالب على متغيرات قابلة للتغيير ، أي لا يمكن لأي متغير تغيير قيمته بعد تهيئته، ولذلك يمكن اعتبار البرمجة الوصفية القائمة على القوالب شكلاً من أشكال البرمجة الوظيفية [ 4 ] . في الواقع، تعتمد العديد من تطبيقات القوالب على التحكم في التدفق من خلال الاستدعاء الذاتي فقط ، كما هو موضح في المثال أدناه.
استخدام قالب البرمجة الفوقية
على الرغم من أن بنية البرمجة الوصفية للقوالب تختلف عادةً اختلافًا كبيرًا عن لغة البرمجة المستخدمة معها، إلا أنها ذات فوائد عملية. من الأسباب الشائعة لاستخدام القوالب تطبيق البرمجة العامة (تجنب أجزاء التعليمات البرمجية المتشابهة باستثناء بعض الاختلافات الطفيفة) أو إجراء تحسين تلقائي أثناء الترجمة، مثل تنفيذ إجراء ما مرة واحدة فقط أثناء الترجمة بدلاً من تنفيذه في كل مرة يُشغّل فيها البرنامج - على سبيل المثال، من خلال جعل المُترجم يفكّ حلقات التكرار للتخلص من القفزات وتقليل عدد مرات تكرار الحلقات عند تنفيذ البرنامج.
توليد الفئات في وقت الترجمة
يمكن توضيح معنى "البرمجة في وقت الترجمة" بالضبط من خلال مثال على دالة المضروب، والتي يمكن كتابتها في لغة C++ غير القائمة على القوالب باستخدام الاستدعاء الذاتي على النحو التالي:
uint32_t factorial ( uint32_t n ) { return n == 0 ? 1 : n * factorial ( n - 1 ); }// أمثلة على الاستخدام: // دالة المضروب (0) ستنتج 1؛ // دالة المضروب (4) ستنتج 24.سيتم تنفيذ الكود أعلاه أثناء وقت التشغيل لتحديد قيمة مضروب القيمتين الحرفيتين 0و 4. باستخدام البرمجة الوصفية للقوالب وتخصيص القوالب لتوفير شرط إنهاء الاستدعاء الذاتي، يمكن حساب المضروب المستخدم في البرنامج - مع تجاهل أي مضروب غير مستخدم - أثناء وقت الترجمة بواسطة هذا الكود:
template < uint32_t N > struct Factorial { static constexpr uint32_t VALUE = N * Factorial < N - 1 >:: VALUE ; };template <> struct Factorial < 0 > { static constexpr uint32_t VALUE = 1 ; };// أمثلة على الاستخدام: // دالة Factorial<0>::value ستنتج 1؛ // دالة Factorial<4>::value ستنتج 24.يحسب الكود أعلاه مضروب القيم الحرفية 0و 4أثناء الترجمة، ويستخدم النتائج كما لو كانت ثوابت مُحسوبة مسبقًا. لاستخدام القوالب بهذه الطريقة، يجب أن يعرف المُترجم قيمة مُعاملاته أثناء الترجمة، وهو ما يتطلب شرطًا أساسيًا طبيعيًا، وهو Factorial<X>::valueأن تكون قيمة حرفية ثابتة أو تعبيرًا ثابتًا X.X
في لغتي C++11 و C++20 ، تم تقديم constexpr و consteval لتمكين المُصرّف من تنفيذ التعليمات البرمجية. باستخدام constexprهاتين الدالتين consteval، يمكن استخدام تعريف المضروب التكراري المعتاد مع الصيغة غير المُنمّطة. [ 5 ]
تحسين الكود في وقت الترجمة
يُعد مثال المضروب أعلاه أحد أمثلة تحسين الكود أثناء الترجمة، حيث يتم تجميع جميع المضروبات المستخدمة في البرنامج مسبقًا وإدخالها كثوابت عددية أثناء الترجمة، مما يوفر وقت التشغيل ويقلل من استهلاك الذاكرة . ومع ذلك، يُعتبر هذا تحسينًا بسيطًا نسبيًا.
كمثال آخر وأكثر أهمية على فك حلقات التكرار في وقت الترجمة ، يمكن استخدام البرمجة الوصفية للقوالب لإنشاء فئات متجهات بطول n (حيث n معروف في وقت الترجمة). وتكمن ميزة هذه الطريقة مقارنةً بالمتجهات التقليدية ذات الطول n في إمكانية فك حلقات التكرار، مما ينتج عنه كود مُحسَّن للغاية. على سبيل المثال، لنأخذ عامل الجمع. يمكن كتابة عملية جمع متجه بطول n على النحو التالي:
template < int Length > ColumnVector < Length >& ColumnVector < Length >:: operator += ( const Vector < Length >& rhs ) { for ( int i = 0 ; i < Length ; ++ i ) { value [ i ] += rhs . value [ i ]; } return * this ; }عندما يقوم المترجم بإنشاء قالب الدالة المحدد أعلاه، قد يتم إنتاج الكود التالي:
template < > ColumnVector <2> & ColumnVector <2> :: operator + = ( const ColumnVector <2> & rhs ) { value [ 0 ] + = rhs.value [ 0 ] ; value [ 1 ] + = rhs.value [ 1 ] ; return * this ; }ينبغي أن يكون مُحسِّن المُصرِّف قادرًا على فك forالحلقة لأن مُعامل القالبLength ثابت في وقت التجميع.
ومع ذلك، يجب توخي الحذر لأن هذا قد يتسبب في تضخم الكود، حيث سيتم إنشاء كود غير ملفوف منفصل لكل 'N' (حجم المتجه) يتم إنشاء القالب به.
التعدد الشكلي الثابت
تعد خاصية تعدد الأشكال إحدى ميزات البرمجة القياسية الشائعة، حيث يمكن استخدام الكائنات المشتقة كنسخ من الكائن الأساسي، ولكن يتم استدعاء أساليب الكائنات المشتقة، كما هو الحال في هذا الكود.
class Base { public : virtual void method () { std :: println ( "Base" ); }virtual ~ Base () {} };class Derived : public Base { public : virtual void method () { std :: println ( "Derived" ); } };int main () { Base * b = new Derived ; b -> method (); // يُخرج "Derived" delete b ; return 0 ; }حيث ستكون جميع استدعاءات virtualالدوال تابعة للفئة الأكثر اشتقاقًا. ويتحقق هذا السلوك الديناميكي متعدد الأشكال (عادةً) من خلال إنشاء جداول بحث افتراضية للفئات ذات الدوال الافتراضية، وهي جداول يتم استعراضها أثناء التشغيل لتحديد الدالة المراد استدعاؤها. وبالتالي، فإن تعدد الأشكال أثناء التشغيل يستلزم بالضرورة زيادة في تكلفة التنفيذ (مع أن هذه الزيادة ضئيلة في البنى الحديثة).
مع ذلك، في كثير من الحالات، يكون السلوك متعدد الأشكال المطلوب ثابتًا ويمكن تحديده أثناء الترجمة. عندئذٍ، يمكن استخدام نمط القالب المتكرر بشكل غريب (CRTP) لتحقيق تعدد الأشكال الثابت ، وهو محاكاة لتعدد الأشكال في كود البرمجة، ولكنه يُحل أثناء الترجمة، وبالتالي يُغني عن عمليات البحث في الجداول الافتراضية أثناء التشغيل. على سبيل المثال:
template < typename Child > struct Base { void myMethod () { // ... static_cast < Child *> ( this ) -> myImplementation (); // ... } };struct Derived : Base < Derived > { void myImplementation () { // ... } };هنا، يستفيد قالب الفئة الأساسية من حقيقة أن دوال الأعضاء لا تُنشأ إلا بعد تعريفها، ويستخدم أعضاء الفئة المشتقة ضمن دوال أعضائه الخاصة، عبر استخدام static_cast، وبالتالي يُولّد عند الترجمة تركيبًا للكائنات بخصائص متعددة الأشكال. كمثال على الاستخدام العملي، يُستخدم CRTP في مكتبة Boost iterator . [ 6 ]
وهناك استخدام مشابه آخر هو " حيلة بارتون-ناكمان "، والتي يشار إليها أحيانًا باسم "توسيع القالب المقيد"، حيث يمكن وضع الوظائف الشائعة في فئة أساسية لا تستخدم كعقد ولكن كمكون ضروري لفرض السلوك المتوافق مع تقليل تكرار التعليمات البرمجية.
إنشاء الجداول الثابتة
تكمن فائدة الجداول الثابتة في استبدال العمليات الحسابية "المكلفة" بعملية فهرسة بسيطة للمصفوفات (للاطلاع على أمثلة، انظر جدول البحث ). في لغة C++، توجد أكثر من طريقة لإنشاء جدول ثابت أثناء الترجمة. يوضح المثال التالي كيفية إنشاء جدول بسيط جدًا باستخدام هياكل البيانات المتكررة والقوالب المتغيرة الوسائط . حجم الجدول عشرة عناصر، وكل قيمة فيه هي مربع الفهرس.
استيراد std ؛باستخدام std :: array ;constexpr int TABLE_SIZE = 10 ;/** * قالب متغير الوسائط لبنية مساعدة متكررة. */ template < int Index = 0 , int ... D > struct Helper : Helper < Index + 1 , D ..., Index * Index > { };/** * تخصيص القالب لإنهاء الاستدعاء الذاتي عندما يصل حجم الجدول إلى TABLE_SIZE. */ template < int ... D > struct Helper < TABLE_SIZE , D ... > { static constexpr array < int , TABLE_SIZE > TABLE = { D ... }; } ;constexpr array < int , TABLE_SIZE > TABLE = Helper <>:: TABLE ;enum class Numbers { FOUR = TABLE [ 2 ] // استخدام وقت الترجمة };int main () { for ( int i : TABLE ) { std :: println ( "{}" , i ); // استخدام وقت التشغيل } std :: println ( "FOUR: {}" , Numbers :: FOUR ); }تعتمد الفكرة على أن مُساعد البنية يرث بشكل متكرر من بنية تحتوي على وسيط قالب إضافي (يُحسب في هذا المثال كـ Index * Index) حتى يُنهي تخصيص القالب التكرار عند حجم 10 عناصر. يستخدم التخصيص ببساطة قائمة الوسائط المتغيرة كعناصر للمصفوفة. سيُنتج المُصرّف شيفرة مشابهة لما يلي (مأخوذة من clang المُستدعى باستخدام -Xclang -ast-print -fsyntax-only ).
استيراد std ؛باستخدام std :: array ;template < int Index = 0 , int ... D > struct Helper : Helper < Index + 1 , D ..., Index * Index > {}; template <> struct Helper < 0 , <>> : Helper < 0 + 1 , 0 * 0 > {}; template <> struct Helper < 1 , < 0 >> : Helper < 1 + 1 , 0 , 1 * 1 > {}; template <> struct Helper < 2 , < 0 , 1 >> : Helper < 2 + 1 , 0 , 1 , 2 * 2 > {}; template <> struct Helper < 3 , < 0 , 1 , 4 >> : Helper < 3 + 1 , 0 , 1 , 4 , 3 * 3 > {}; template <> struct Helper < 4 , < 0 , 1 , 4 , 9 >> : Helper < 4 + 1 , 0 , 1 , 4 , 9 , 4 * 4 > {}; template <> struct Helper < 5 , < 0 , 1 , 4 , 9 , 16 >> : Helper < 5 + 1 , 0 , 1 ,4 ، 9, 16 , 5 * 5 > {}; template <> struct Helper < 6 , < 0 , 1 , 4 , 9 , 16 , 25 >> : Helper < 6 + 1 , 0 , 1 , 4 , 9 , 16 , 25 , 6 * 6 > {}; template <> struct Helper < 7 , < 0 , 1 , 4 , 9 , 16 , 25 , 36 >> : Helper < 7 + 1 , 0 , 1 , 4 , 9 , 16 , 25 , 36 , 7 * 7 > {}; قالب <> بنية Helper < 8 , < 0 , 1 , 4 , 9 , 16 , 25 , 36 , 49 >> : Helper < 8 + 1 , 0 , 1 , 4 , 9 , 16 , 25 , 36 , 49 , 8 * 8 > {}; قالب <> بنية Helper < 9 , < 0 , 1 , 4 , 9 , 16 , 25 , 36 , 49 , 64 >> : Helper < 9 + 1 , 0 , 1 , 4 , 9 , 16 , 25 , 36 , 49 , 64 , 9* 9 > {}; template <> struct Helper < 10 , < 0 , 1 , 4 , 9 , 16 , 25 , 36 , 49 , 64 , 81 >> { static constexpr array < int , TABLE_SIZE > TABLE = { 0 , 1 , 4 , 9 , 16 , 25 , 36 , 49 , 64 , 81 }; };منذ إصدار C++17، يمكن كتابة هذا بشكل أكثر وضوحًا على النحو التالي:
استيراد std ؛باستخدام std :: array ;constexpr int TABLE_SIZE = 10 ;constexpr array < int , TABLE_SIZE > TABLE = []() -> array < int , TABLE_SIZE > { array < int , TABLE_SIZE > a = {}; for ( size_t i = 0 ; i < TABLE_SIZE ; i ++ ) { a [ i ] = i * i ; } return a ; }();enum class Numbers { FOUR = TABLE [ 2 ] // استخدام وقت الترجمة };int main () { for ( int i : TABLE ) { std :: println ( "{}" , i ); // استخدام وقت التشغيل } std :: println ( "FOUR: {}" , Numbers :: FOUR ); }ولإظهار مثال أكثر تعقيدًا، تم توسيع الكود في القائمة التالية ليحتوي على مساعد لحساب القيمة (استعدادًا لعمليات حسابية أكثر تعقيدًا)، وإزاحة خاصة بالجدول، ووسيطة قالب لنوع قيم الجدول (على سبيل المثال uint8_t، uint16_t...).
استيراد std ؛باستخدام std :: array ;constexpr int TABLE_SIZE = 20 ; constexpr int OFFSET = 12 ;/** * قالب لحساب مدخل جدول واحد */ template < typename ValueType , ValueType Offset , ValueType Index > struct ValueHelper { static constexpr ValueType VALUE = Offset + Index * Index ; };/** * قالب متغير الوسائط لبنية مساعدة تكرارية. */ template < typename ValueType , ValueType Offset , int N = 0 , ValueType ... D > struct Helper : Helper < ValueType , Offset , N + 1 , D ..., ValueHelper < ValueType , Offset , N >:: VALUE > { };/** * تخصيص القالب لإنهاء التكرار عندما يصل حجم الجدول إلى TABLE_SIZE. */ template < typename ValueType , ValueType Offset , ValueType ... D > struct Helper < ValueType , Offset , TABLE_SIZE , D ... > { static constexpr array < ValueType , TABLE_SIZE > TABLE = { D ... }; };constexpr array < uint16_t , TABLE_SIZE > TABLE = Helper < uint16_t , OFFSET >:: TABLE ;int main () { for ( int i : TABLE ) { std :: println ( "{}" , i ); } }والتي يمكن كتابتها على النحو التالي باستخدام لغة C++17:
استيراد std ؛باستخدام std :: array ;constexpr int TABLE_SIZE = 20 ; constexpr int OFFSET = 12 ;template < typename ValueType , int Offset > constexpr array < ValueType , TABLE_SIZE > TABLE = []() -> array < ValueType , TABLE_SIZE > { array < ValueType , TABLE_SIZE > A = {}; for ( size_t i = 0 ; i < TABLE_SIZE ; i ++ ) { a [ i ] = Offset + i * i ; } return A ; }();int main () { for ( int i : TABLE < uint16_t , OFFSET > ) { std :: pritnln ( "{}" , i ); } }المفاهيم
قدّم معيار C++20 لمبرمجي لغة C++ أداة جديدة لبرمجة القوالب الوصفية، والمفاهيم ذات الصلة. [ 7 ]
تُمكّن المفاهيم المبرمجين من تحديد متطلبات النوع، مما يُتيح إنشاء نماذج القوالب. يبحث المُصرّف عن قالب يحتوي على المفهوم الذي يُلبي أعلى المتطلبات.
إليكم مثال على مشكلة Fizz buzz الشهيرة التي تم حلها باستخدام برمجة القوالب الوصفية.
import std ; import boost.type_index ; // لعرض أنواع البيانات بشكل مُنسقباستخدام std :: tuple ؛ باستخدام boost :: typeindex :: type_id ؛/** * تمثيل نوعي للكلمات المراد طباعتها */ struct Fizz {}; struct Buzz {}; struct FizzBuzz {}; template < size_t M > struct Number { constexpr static size_t N = M ; };/** * المفاهيم المستخدمة لتحديد شروط التخصيص */ template < typename A > concept HasN = requires { requires A :: N - A :: N == 0 ; };template < typename A > concept FizzConc = HasN < A > && requires { requires A :: N % 3 == 0 ; };template < typename A > concept BuzzConc = HasN < A > && requires { requires A :: N % 5 == 0 ; };template < typename A > concept FizzBuzzC = FizzConc < A > && BuzzConc < A > ;/** * من خلال تخصيص بنية `res`، مع متطلبات المفاهيم، يتم تنفيذ الإنشاء الصحيح */ template < typename X > struct Res ;template < FizzBuzzConc X > struct Res < X > { using Result = FizzBuzz ; };template < FizzConc X > struct Res < X > { using Result = Fizz ; };template < BuzzConc X > struct Res < X > { using Result = Buzz ; };template < HasN X > struct Res < X > { using Result = X ; };/** * تعريف مسبق لدالة الربط */ template < size_t Concat , typename ... Args > struct Concatenator ;/** * طريقة تكرارية لدمج الأنواع التالية */ template < size_t Concat , typename ... Args > struct Concatenator < Concat , tuple < Args ... >> { using Type = typename Concatenator < Concat - 1 , tuple < typename Res < Number < Concat >>:: Result , Args ... >>:: Type ; };/** * الحالة الأساسية */ template < typename ... Args > struct Concatenator < 0 , tuple < Args ... >> { using Type = tuple < Args ... > ; };/** * دالة جلب النتيجة النهائية */ template < size_t Amount > using FizzBuzzFull = typename Concatenator < Amount - 1 , tuple < typename Res < Number < Amount >>:: Result >>:: Type ;int main ( int argc , char * argv []) { // طباعة النتيجة باستخدام boost.type_index، لتكون واضحة std :: println ( "{}" , type_id < fizz_buzz_full_template < 100 >> (). pretty_name ()); } /* النتيجة: std::tuple<Number<1ul>, Number<2ul>, Fizz, Number<4ul>, Buzz, Fizz, Number<7ul>, Number<8ul>, Fizz, Buzz, Number<11ul>, Fizz, Number<13ul>, Number<14ul>, FizzBuzz, Number<16ul>, Number<17ul>, Fizz, Number<19ul>, Buzz, Fizz, Number<22ul>, Number<23ul>, Fizz, Buzz, Number<26ul>, Fizz, Number<28ul>, Number<29ul>, FizzBuzz, Number<31ul>, Number<32ul>, Fizz, Number<34ul>, Buzz, Fizz, Number<37ul>, Number<38ul>, Fizz, Buzz, Number<41ul>, Fizz, Number<43ul>, Number<44ul>, فيز باز، رقم 46، رقم 47، فيز، رقم 49، باز، فيز، رقم 52، رقم 53، فيز، باز، رقم 56، فيز، رقم 58، رقم 59، فيز باز، رقم 61، رقم 62، فيز، رقم 64، باز، فيز، رقم 67، رقم 68، فيز، باز، رقم 71، فيز، رقم 73، رقم 74، فيز باز، رقم 76، رقم 77، فيز، رقم 79، باز، فيز، رقم 82، رقم 83، فيز، باز، رقم 86، فيز، رقم 88، رقم 89، فيز باز رقم<91ul>، رقم<92ul>، فيز، رقم<94ul>، باز، فيز، رقم<97ul>، رقم<98ul>، فيز، باز> */مزايا وعيوب برمجة القوالب الوصفية
تظهر المفاضلات بين وقت الترجمة ووقت التنفيذ إذا تم استخدام قدر كبير من البرمجة الوصفية للقوالب.
- تتيح برمجة القوالب للمبرمج التركيز على بنية النظام وتفويض مهمة إنشاء أي تنفيذ مطلوب من قبل كود العميل إلى المُصرّف. وبالتالي، تُمكن برمجة القوالب من كتابة كود عام تمامًا ، مما يُسهّل تقليل حجم الكود وتحسين قابليته للصيانة .
- فيما يتعلق بلغة C++ قبل الإصدار C++11 ، كانت قواعد وأساليب برمجة القوالب الوصفية غامضة مقارنةً ببرمجة C++ التقليدية، وكان فهمها صعبًا للغاية. [ 8 ] [ 9 ] ولكن بدءًا من C++11، أصبحت قواعد برمجة حساب القيم الوصفية أقرب فأقرب إلى لغة C++ "العادية"، مع تقليل تأثير ذلك على سهولة القراءة.
انظر أيضاً
مراجع
- ↑ سكوت مايرز (12 مايو 2005). لغة C++ الفعالة: 55 طريقة محددة لتحسين برامجك وتصاميمك . بيرسون للتعليم. ISBN 978-0-13-270206-5.
- ↑ انظر تاريخ TMP على ويكي الكتب
- ^ فيلدهويزن، تود ل. (2003). “قوالب C++ مكتملة”. سيتيسيركس 10.1.1.14.3670 .
- ↑ بوركولاب، ز. (2010). "البرمجة الوظيفية باستخدام البرامج الوصفية لقوالب لغة C++". في: ز. هورفاث، ر. بلاسميير، و ف. زوك (محررون)، مدرسة أوروبا الوسطى للبرمجة الوظيفية (CEFP 2009) ، سلسلة محاضرات في علوم الحاسوب، المجلد 6299. برلين وهايدلبرغ: سبرينغر، الصفحات 90-135. doi:10.1007/978-3-642-17685-2_9.
- ↑ "Constexpr - التعبيرات الثابتة المعممة في C++11 - Cprogramming.com" . www.cprogramming.com .
- ↑ "واجهة التكرار - 1.79.0" .
- ↑ "القيود والمفاهيم (منذ C++20) - cppreference.com" . en.cppreference.com .
- ↑ تشارنيكي، ك.؛ أودونيل، ج.؛ ستريغنيتز، ج.؛ طه، وليد محمد (2004). "تنفيذ لغة المجال المحددة في ميتا كاميل، وهاسكل القالب، وسي++" (ملف PDF) . جامعة واترلو، جامعة غلاسكو، مركز جوليش للأبحاث، جامعة رايس.
يعاني برمجة القوالب الوصفية في سي++ من عدد من القيود، بما في ذلك مشاكل قابلية النقل بسبب قيود المُصرّف (على الرغم من تحسن هذا الأمر بشكل ملحوظ في السنوات القليلة الماضية)، ونقص دعم تصحيح الأخطاء أو الإدخال/الإخراج أثناء إنشاء القوالب، وأوقات تجميع طويلة، وأخطاء طويلة وغير مفهومة، وضعف قابلية قراءة الكود، وضعف الإبلاغ عن الأخطاء.
- ↑ شيرد، تيم؛ جونز، سيمون بيتون (2002). "البرمجة الوصفية للقوالب في هاسكل" (ملف PDF) . ACM 1-58113-415-0/01/0009.
تُشير ورقة روبنسون المثيرة للجدل إلى أن قوالب لغة C++ تُعدّ نجاحًا كبيرًا، وإن كان غير مقصود، لتصميم لغة C++. فعلى الرغم من الطبيعة المعقدة للغاية للبرمجة الوصفية للقوالب، تُستخدم القوالب بطرق رائعة تتجاوز أكثر تخيلات مصممي اللغة جموحًا. ولعلّ من المفاجئ، بالنظر إلى أن القوالب برامج وظيفية، أن المبرمجين الوظيفيين كانوا بطيئين في الاستفادة من نجاح C++.
- إيزينيكر، أولريش دبليو. (2000). البرمجة التوليدية: الأساليب والأدوات والتطبيقات . أديسون-ويسلي. ISBN 0-201-30977-7.
- ألكسندريسكو، أندريه (2003). تصميم لغة C++ الحديثة: البرمجة العامة وتطبيقات أنماط التصميم . أديسون-ويسلي. ISBN 3-8266-1347-3.
- أبراهامز، ديفيد ؛ غورتوفوي، أليكسي (يناير 2005). برمجة القوالب في لغة C++: مفاهيم وأدوات وتقنيات من Boost وما بعدها . أديسون-ويسلي. ISBN 0-321-22725-5.
- الأماكن القريبة : جوسوتيس، نيكولاي م. (2003). قوالب C++: الدليل الكامل أديسون ويسلي. رقم ISBN 0-201-73484-2.
- كلافيل، مانويل (16 أكتوبر 2000). الانعكاس في إعادة كتابة المنطق: الأسس الميتافيزيقية وتطبيقات البرمجة الميتافيزيقية . مطبعة جامعة كامبريدج. ISBN 1-57586-238-7.
روابط خارجية
- "مكتبة البرمجة الوصفية Boost (Boost MPL)" .
- "مكتبة الأرواح" .(تم بناؤه باستخدام البرمجة الوصفية للقوالب)
- "مكتبة Boost Lambda" .(استخدام خوارزميات STL بسهولة)
- فيلدهويزن، تود (مايو 1995). "استخدام البرامج الوصفية للقوالب في لغة C++" . تقرير C++ . 7 (4): 36-43 . مؤرشف من الأصل في 4 مارس 2009.
- "Template Haskell" .(البرمجة الوصفية الآمنة من حيث النوع في لغة هاسكل)
- برايت، والتر . "إعادة النظر في القوالب" .(البرمجة الوصفية باستخدام القوالب في لغة البرمجة D )
- كوسكينين، يوهانس . "البرمجة الوصفية في لغة C++" (ملف PDF) . مؤرشف من النسخة الأصلية (PDF) بتاريخ 28 أغسطس 2014. تم الاطلاع عليه بتاريخ 20 يونيو 2014 .
- أتاردي، جوزيبي؛ سيسترنينو، أنطونيو. "دعم الانعكاس باستخدام برمجة القوالب الوصفية" (ملف PDF) . مؤرشف من الأصل (ملف PDF) بتاريخ 3 مارس 2016. تم الاطلاع عليه بتاريخ 24 أكتوبر 2008 .
- بيرتون، مايكل سي؛ غريسولد، ويليام جي؛ ماكولوتش، أندرو دي؛ هوبر، غاري إيه (2002). "هياكل البيانات الثابتة". CiteSeerX 10.1.1.14.5881 .
- أمجد، زيشان (13 أغسطس 2007). "برمجة القوالب الميتا ونظرية الأعداد" .
- أمجد، زيشان (24 أغسطس 2007). "برمجة القوالب الميتا ونظرية الأعداد: الجزء 2" .
- "مكتبة لبرمجة نمط LISP في لغة C++" .
- البرمجة الوصفية
- لغة سي++
