برنامهنویسی خودآرا
برنامهنویسی خودآرا یک فن برنامهنویسی تواناست که در آن الگوریتمها توسط یک همگردان(Compiler) به منظور به وجود آوردن یک کد موقتی به کار گرفته میشود، که توسط همگردان با بقیه کد ترکیب و سپس اجرا میشود.
خروجی این الگوریتمها شامل ثابتهای زمان اجرا، ساختارهای داده وتوابع کامل میباشد. بهکارگیری الگوریتمها میتواند مانند زمان همگردانی اجرا تصور شود. این فن در تعدادی از زبانها استفاده میشود که بهترین ومعروفترین آنهاC++ و همچنین Curl، XL و D هستند.
قالب برنامهنویسی خودآرا بهطور تصادفی کشف شده بود:تاریخچه آن را ببینید.
بعضی دیگر از زبانها شبیه به هم پشتیبانی میکنند. اگر چه از لحاظ تسهیلات زمان اجرا خیلی قدرتمند نیستند (به عنوان مثال lips macro اما بیرون از حوزهٔ این مقاله هستند.
اجزا
به کارگیری الگوریتمها به عنوان یک فن برنامهنویسی خودآرا مستلزم دو عملگر بارز میباشد: یک الگوریتم باید تعریف شود ویک الگوریتم تعریف شده باید اعلان گردد. تعریف الگوریتم فرم کلی منبع کد تولید شده را شرح میدهد ونوسازی سبب تولید یک مجموعه کد خاص از شکل کلی در الگوریتم میشود.
برنامهنویسی خودآرا یک تورینگ کامل است. یعنی اینکه هر محاسبه قابل بیانی میتواند توسط یک برنامه کامپیوتر در برخی اشکال با یک الگوریتم برنامهنویسی محاسبه گردد.
الگوها با ماکروها متفاوتند، یک ماکرو(Macro )هم وسیلهای است برای زمان اجرا زبان وهم کد را به صورت کلی در یک خط اجرا وتعویض میکند. سیستمهای ماکرو اغلب زمان اجرای پردازش را طبق قابلیتها محدود کردهاند و معمولاً فاقد اطلاعات معنی ونوع سیستم زبان همراهشان میباشند.(باید یک مورد استثنا با لیسپ ماکروها ساخته شود که در خود لیسپ نوشته میشوند و دستکاری وتعویض کد لیسپ را درگیر کند)
برنامهنویسی خود آرا هیچ متغیر نا پایداری ندارد یعنی اینکه هیچ متغیری نمیتواند مقدارش را در حالی که مقدار دهی شدهاست تغییر دهد؛ بنابراین برنامهنویسی خود آرا میتواند به عنوان شکل برنامه نویسی تابعی تلقی شود. _در واقع الگوی بیشتر فعالیتها تنها در طول بازگشت، جریا ن کنترل را انجام میدهند. به عنوان مثال در نمونه زیر بینید.
کاربرد
اگر چه دستور زبان برنامه نویسی خود آرا با زبان برنامهنویسی که با آن نوشته میشود، بسیار متفاوت است اما استفادههای کاربردی دارد. برخی از علل معمول برای کاربرد الگوها سبب اجرای برنامهٔ اصلی یا بهینهسازی زمان اجرا است و مثل اجرای چیزی بهطور یک مرتبه در زمان بهتر از هر زمانی است که برنامه ادامه مییابد.
زمان اجرای کلاس تولید
برنامهنویسی در زمان اجرا دقیقاً چه معنی میشود، با یک مثال تابع فاکتوریل میتواند روشن شود که در هیچیک از الگوهای C++نمیتواند مانند چیزی که درذیل آمدهاست، نوشته شود.
unsigned int factorial(unsigned int n) {
return n == 0 ? 1: n * factorial(n - 1);
}
// Usage examples:
// factorial(0) would yield 1;
// factorial(4) would yield 24.
کد بالا در حین اجرا برای تعیین فاکتوریل مقدارهای ۰و۴ اجرا خواهد شد. با استفادهٔ برنامهنویسی خودآرا والگوی خاص شرط نهایی برای بازگشت فراهم میشود. فاکتوریلهای استفاده شده در برنامه میتواند توسط کد زیر در حین اجرا محاسبه گردد.
template <int n>
struct factorial {
enum { value = n * factorial<n - 1>::value };
};
template <>
struct factorial<0> {
enum { value = ۱ };
};
// Usage examples:
// factorial<0>::value would yield 1;
// factorial<4>::value would yield 24.
کد بالا مقدار فاکتوریل اعداد ۰و۴را حین اجرا محاسبه میکند ونتیجههایی راکه از قبل به عنوان ثابت محاسبه شده بودند، استفاده میکند. به منظور امکان استفادهٔ الگوها در این روش باید کامپایلر (همگردان) مقدار متغیرهایش را حین اجرا بشناسد. چیزی که شرط اولیه وطبیعی را دارد، همان فاکتوریل X::اگر و تنها اگر Xدر حین اجراشناس باشد، به کار گرفته میشود. به بیان دیگر xباید یک حرف یا یک ثابت باشد.
درC++11یک روش به منظور اجازه دادن به کامپایلر (همگردان) برای انجام عبارتهای ساده وثابت، اضافه شده بود. کاربرد CONSTEXPR، هر کس میتواند از تعریف متداول وبازگشتی فاکتوریل استفاده کند.
بهینهسازی کد زمان اجرا
مثال فاکتوریل بالا نمونهای از بهینهسازی کد زمان اجراست که در آن همهٔ فاکتوریلهای به کار رفته توسط برنامه ازقبل اجرا شده و به عنوان ثابتهای شمارهای در اجرا ی برنامه تزریق میشود.
بهطور کل صرفه جویی هم در حافظه وهم در زمان، بهینهسازی کمتری به دست میدهد. برای اهمیت بیشتر مثال دیگری از زمان اجرای حلقهٔ غیر بازگشتی آوردهایم. برنامهنویسی خودآرا میتواند برای ایجاد کلاسهای متغیر به طول nاستفاده شود.(جایی که nدر زمان اجراشناس باشد) مزیت یک متغیر بیشتر مرسوم این است که حلقهها میتواند باز شود و در بهینهسازی کد بسیار مؤثر است.
به عنوان مثال، افزایش عملگر را ملاحظه کنید. افزایش یک متغیر به طول nممکن بود اینگونه نوشته شود.
template <int length>
Vector<length>& Vector<length>::operator+=(const Vector<length>& rhs)
{
for (int i = ۰; i <length; ++i)
value[i] += rhs.value[i];
return *this;
}
-وقتی کامپایلر (همگردان) الگوی تابع تعریف شده بالا را معرفی کند، کد زیر ممکن است آماده شود
template <>
Vector<2>& Vector<2>::operator+=(const Vector<2>& rhs)
{
value[0] += rhs.value[0];
value[1] += rhs.value[1];
return *this;
}
بهینهسازی کامپایلر باید قادر باشد حلقه را باز کند چون قالب طول متغیر در زمان اجرا یک ثابت محسوب میشود، هر چند اینگونه عمل کردن ممکن است باعث شودکد باز شدهٔ جدا برای هر n که شما تعریف میکنید ایجاد شود.
چند ریختی ایستا
چند ریختی یک ابزار برنامهنویسی استاندارد ورایج است، جایی که اشیا مشتق میتواند به عنوان نمونه شی اصلی استفاده شود، اما روش شی مشتق در کجا لازم میشود. به عنوان نمونه در کد زیر:
class Base
{
public:
virtual void method() { std::cout <<"Base"; }
virtual ~Base() {}
};
class Derived: public Base
{
public:
virtual void method() { std::cout <<"Derived"; }
};
int main()
{
Base *pBase = new Derived;
pBase->method(); //outputs "Derived"
delete pBase;
return 0;
}
جایی که همهٔ ملزومات روشهای مجازی با بیشترین کلاس مشتق را خواهد داشت. این رفتار چندریختی پویا با ایجاد جدولهای مراجعهای مجازی برای کلاسهایی با شیوههای مجازی، رمز نگاری شدهاست.
جدولهایی که در حین اجرا برای تشخیص روش به منظور فراخوانی، پیموده میشوند. بدین سان چند ریختی زمان اجرا لزوماً اجرا را متوقف میکند.(اگر چه در معماریهای جدید اندک است) به هر حال در بسیاری از موارد رفتار چند ریختی در صورت لازم ثابت میشود و میتواند در زمان اجرا مصمم شود.
سپس الگوی بازگشتی عجیب CRTP میتواند به منظور دستیابی به چند ریختی ثابت استفاده شود؛ که تقلیدی از چند ریختی در کد برنامهنویسی است. اما آن دوباره در حین اجرا حل میشود وهم چنین با جدولهای مراجعهای ومجازی زمان اجرا برطرف میشود. به عنوان مثال:
template <class Derived>
struct base
{
void interface()
{
// ...
static_cast<Derived*>(this)->implementation();
// ...
}
};
struct derived: base<derived>
{
void implementation()
{
// ...
}
};
در اینجا اساس الگوی کلاس مزیت واقعی را به دست میدهد؛ که بدنههای تابع عضو تا قبل از اعلانشان معرفی نمیشوند و آن عضوهای کلاسهای مشتق را در طول تابعهای عضو خودش توسط یک الگوی ثابت به کار خواهد گرفت، هم چنین در اجرای تولید ترکیب یک شی با کاراکترهای چند ریختی.
برای مثال یک کاربرد در جهان واقعی ،CRTP در کتابخانهٔ گردش قیمت به کار گرفته میشود. حیلهٔ بارتون-ناکمن [Barton-Nackman] کاربرد مشابه دیگری است، که گاهی به الگوی عبارت محصور ارجاع داده میشود.
مکانی که یک تابع رایج میتواند در کلاس اصلی جای بگیرد به عنوان یک قرارداد؛ استفاده نمیشود. تنها به عنوان یک ابزار لازم برای اجرای تابع هنگام کاهش کد زاید، رفتار میکند.
مزیتها ومعایب برنامهنویسی خود آرا
سبک و سنگین کردن زمان همگردانی در برابر زمان اجرا
-اگر مقدار زیادی از یک برنامهنویسی خودآرا به کار گرفته شود، همگردانی (اجرا) ممکن است کند شود. قسمت ۱۴٫۷٫۱ از استاندارد رایج، توضیحات تحتالگوهای ضمنی و تعریف شده را شرح میدهد.
تعریف یک الگو دلیل بر این نمیشود که آن الگو اعلان خواهد شد واعلان کردن الگوی یک کلاس باعث نمیشود تعریفهای عضوش اعلان شوند. به روش استفاده بستگی دارد. الگوها ممکن است سریع تر یا کند تر از کد بازگشتی دستی اجرا شوند.
تولید برنامه، برنامهنویسی خودآرا به برنامهنویس اجازه میدهد روی معماری تمرکز کند. وبه همگردان وکالت میدهد که تولید هر اجرا با کد ارجاع درخواست شود. به هر حال، برنامهنویسی خودآرا میتواند کد کلی را بهطور صحیح اجرا کند. سهولت در کوچک کردن ونگهداری بهتر.
خوانایی با مراجعه بهC++ دستور واصطلاحات برنامهنویسی خودآرا در مقایسه با قواید C++ مبهم هستند وبرنامهنویسی خودآرا میتواند برای فهم بسیار مشکل باشد. برنامهنویسی خودآرا برای نگهداری توسط برنامه نویسان ماهر در برنامهنویسی خودآرا نیز میتواند مشکل باشد.(هر چند این ممکن است با زبان اجرا ی دستور برنامه نویسیهای خودآرا فرق داشته باشد)>
منابع
- Eisenecker, Ulrich W. Generative Programming: Methods, Tools, and Applications. Addison-Wesley. ISBN 0-201-30977-7.
- Alexandrescu, Andrei. Modern C++ Design: Generic Programming and Design Patterns Applied. Addison-Wesley. ISBN 3-8266-1347-3.
- Abrahams, David; Gurtovoy, Aleksey. C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost and Beyond. Addison-Wesley. ISBN 0-321-22725-5.
- Vandervoorde, David; Josuttis, Nicolai M. (2003). C++ Templates: The Complete Guide. Addison-Wesley. ISBN 0-201-73484-2.
- Clavel, Manuel. Reflection in Rewriting Logic: Metalogical Foundations and Metaprogramming Applications. ISBN 1-57586-238-7.