الجانب المظلم من القوة هو طريق إلى العديد من القدرات ، بعضها يعتبر غير طبيعي.
- دارث سيديوس
بناءً على examples/demo.c :
| ترجمة معالجة قائمة الوقت |
// 3, 3, 3, 3, 3
static int five_threes [] = {
ML99_LIST_EVAL_COMMA_SEP ( ML99_listReplicate ( v ( 5 ), v ( 3 ))),
};
// 5, 4, 3, 2, 1
static int from_5_to_1 [] = {
ML99_LIST_EVAL_COMMA_SEP ( ML99_listReverse ( ML99_list ( v ( 1 , 2 , 3 , 4 , 5 )))),
};
// 9, 2, 5
static int lesser_than_10 [] = {
ML99_LIST_EVAL_COMMA_SEP (
ML99_listFilter ( ML99_appl ( v ( ML99_greater ), v ( 10 )), ML99_list ( v ( 9 , 2 , 11 , 13 , 5 )))),
}; |
| عودة الماكرو |
#define factorial ( n ) ML99_natMatch(n, v(factorial_))
#define factorial_Z_IMPL (...) v(1)
#define factorial_S_IMPL ( n ) ML99_mul(ML99_inc(v(n)), factorial(v(n)))
ML99_ASSERT_EQ ( factorial ( v ( 4 )), v ( 24 )); |
| التحميل الزائد على عدد من الحجج |
typedef struct {
double width , height ;
} Rect ;
#define Rect_new (...) ML99_OVERLOAD(Rect_new_, __VA_ARGS__)
#define Rect_new_1 ( x )
{ x, x }
#define Rect_new_2 ( x , y )
{ x, y }
static Rect _7x8 = Rect_new ( 7 , 8 ), _10x10 = Rect_new ( 10 );
// ... and more!
int main ( void ) {
// Yeah. All is done at compile time.
} |
(تلميح: v(something) يقييم something .)
Metalang99 هو أساس ثابت لكتابة metaprograms الموثوقة والقابلة للصيانة في C99 النقي. يتم تنفيذها كلغة FP المفسرة على قمة وحدات الماكرو قبل المعالج: فقط #include <metalang99.h> أنت مستعد للذهاب. يتميز Metalang99 بأنواع البيانات الجبرية ، ومطابقة الأنماط ، والتكرار ، والكاري ، والمجموعات ؛ بالإضافة إلى ذلك ، فإنه يوفر وسائل لتقارير خطأ في وقت التجميع وتصحيح الأخطاء. من خلال مدقق بناء الجملة المدمج الخاص بنا ، يجب أن تكون أخطاء الماكرو مفهومة تمامًا ، مما يمكّنك من التطوير المريح.
حاليًا ، يتم استخدام Metalang99 في OpenIPC كاعتماد غير مباشر لنمذجة البيانات 99 و interface99 ؛ ويشمل ذلك تطبيق RTSP 1.0 جنبا إلى جنب مع حوالي 50 كيلو خط من الكود الخاص.
تسهل وحدات الماكرو إعادة استخدام الكود ، وحدات الماكرو هي مادة البناء التي تتيح لك تشكيل اللغة لتناسب المشكلة التي يتم حلها ، مما يؤدي إلى رمز أكثر نظافة وموجزة. ومع ذلك ، فإن metaprogramming في C مخصصة تمامًا: لا يمكننا حتى العمل مع تدفق التحكم ، والأعداد الصحيحة ، والتسلسلات غير المحدودة ، وهياكل البيانات المركبة ، وبالتالي رمي الكثير من metaprograms مفيدة افتراضيًا خارج النطاق.
لحل المشكلة ، قمت بتطبيق Metalang99. وجود وظائفها تحت تصرفنا ، يصبح من الممكن تطوير حتى metaprograms غير التافهة إلى حد ما ، مثل DataType99:
#include <datatype99.h>
datatype (
BinaryTree ,
( Leaf , int ),
( Node , BinaryTree * , int , BinaryTree * )
);
int sum ( const BinaryTree * tree ) {
match ( * tree ) {
of ( Leaf , x ) return * x ;
of ( Node , lhs , x , rhs ) return sum ( * lhs ) + * x + sum ( * rhs );
}
return -1 ;
}أو الواجهة 99:
#include <interface99.h>
#include <stdio.h>
#define Shape_IFACE
vfunc( int, perim, const VSelf)
vfunc(void, scale, VSelf, int factor)
interface ( Shape );
typedef struct {
int a , b ;
} Rectangle ;
int Rectangle_perim ( const VSelf ) { /* ... */ }
void Rectangle_scale ( VSelf , int factor ) { /* ... */ }
impl ( Shape , Rectangle );
typedef struct {
int a , b , c ;
} Triangle ;
int Triangle_perim ( const VSelf ) { /* ... */ }
void Triangle_scale ( VSelf , int factor ) { /* ... */ }
impl ( Shape , Triangle );
void test ( Shape shape ) {
printf ( "perim = %dn" , VCALL ( shape , perim ));
VCALL ( shape , scale , 5 );
printf ( "perim = %dn" , VCALL ( shape , perim ));
}على عكس التقنيات الغامضة ، مثل النقابات الموسومة أو جداول الأسلوب الافتراضي ، فإن Metaprograms أعلاه تستفيد من السلامة ، وإيجاز بناء الجملة ، والحفاظ على تخطيط الذاكرة الدقيق للرمز الذي تم إنشاؤه.
تبدو مثيرة للاهتمام؟ تحقق من المنشور التحفيزي لمزيد من المعلومات.
Metalang99 هو مجرد مجموعة من ملفات الرأس ولا شيء آخر. لاستخدامه كاعتماد ، تحتاج إلى:
metalang99/include لتشمل الدلائل.-ftrack-macro-expansion=0 (GCC) أو -fmacro-backtrace-limit=1 (clang) لتجنب أخطاء التوسع في الماكرو غير المجدية. إذا كنت تستخدم Cmake ، فإن الطريقة الموصى بها هي FetchContent :
include (FetchContent)
FetchContent_Declare(
metalang99
URL https://github.com/hirrolot/metalang99/archive/refs/tags/v1.2.3.tar.gz # v1.2.3
)
FetchContent_MakeAvailable(metalang99)
target_link_libraries (MyProject metalang99)
# Disable full macro expansion backtraces for Metalang99.
if (CMAKE_C_COMPILER_ID STREQUAL "Clang" )
target_compile_options (MyProject PRIVATE -fmacro-backtrace-limit=1)
elseif (CMAKE_C_COMPILER_ID STREQUAL "GNU" )
target_compile_options (MyProject PRIVATE -ftrack-macro-expansion=0)
endif ()اختياريا ، يمكنك تجميع الرؤوس في مشروعك تعتمد على Metalang99. سيؤدي ذلك إلى تقليل وقت التجميع لأن الرؤوس لن يتم تجميعها في كل مرة يتم تضمينها.
البرنامج التعليمي | أمثلة | وثائق المستخدم
قرصنة سعيدة!
عودة الماكرو. المكالمات العودية تتصرف كما هو متوقع. على وجه الخصوص ، لتنفيذ العودية ، يعزز/ما قبل المعالج فقط جميع الوظائف المتكررة حتى حد معين وإما قوى تتبع عمق العودية أو الاعتماد على خصمها المدمج. كونه مترجمًا ، فإن Metalang99 خالية من مثل هذه العيوب.
تقريبا نفس بناء الجملة. لا يبدو Metalang99 غريبًا جدًا مقارنةً بـ PP لأن بناء الجملة يختلف بشكل غير ملحوظ عن رمز المعالجة المعتادة.
التطبيق الجزئي. بدلاً من تتبع الحجج الإضافية هنا وهناك (كما هو الحال في Boost/Preprocessor) ، يسمح تطبيق Metalang99 الجزئي بتقاط البيئة عن طريق تطبيق قيم ثابتة أولاً. إلى جانب ذلك ، يسهل التطبيق الجزئي إعادة استخدام وظائف أفضل ؛ انظر ML99_const ، ML99_compose ، إلخ.
تصحيح الأخطاء والإبلاغ عن الخطأ. يمكنك تصحيح وحدات الماكرو الخاصة بك بسهولة مع ML99_abort والإبلاغ عن أخطاء غير قابلة للاسترداد مع ML99_fatal . سوف يوقف المترجم الفوري على الفور ويقوم بالخدعة. على حد علمنا ، لا يوفر أي إطار عمل ماكرو آخر آلية لتصحيح الأخطاء والإبلاغ عن الأخطاء.
لقد تركني عملي في Poica ، وهي لغة برمجة بحثية تم تنفيذها على Boost/Preprocessor ، غير راضية عن النتيجة. لقد جعلت القيود الأساسية للزيادة/المعالج المسبق قاعدة الكود ببساطة غير قابلة للحمل ؛ وتشمل هذه المكالمات الكلية العودية (التي تم حظرها من قبل المعالج المسبق) ، والتي جعلت تصحيح الأخطاء كابوسًا كاملاً ، وغياب التطبيق الجزئي الذي جعل السياق يمر محرجًا تمامًا ، وكل خطأ أدى إلى Megabytes من رسائل خطأ المترجم.
عندها فقط فهمت أنه بدلاً من إثراء المعالج المسبق مع مختلف الآليات المخصصة ، يجب أن نضع نموذجًا واضحًا لتنظيم metaprograms. مع وضع هذه الأفكار في الاعتبار ، بدأت في تنفيذ Metalang99 ...
قصة قصيرة طويلة ، استغرق الأمر نصف عام من العمل الشاق لإصدار V0.1.0 وما يقرب من عام لجعلها مستقرة. كتطبيق حقيقي في العالم لـ Metalang99 ، قمت بإنشاء نوع Datatype99 تمامًا من نفس النموذج الذي أردت أن يكون عليه: التنفيذ تعليمي للغاية ، وبناء الجملة أنيق ، والدلالات محددة جيدًا.
أخيرًا ، أود أن أقول إن Metalang99 يدور فقط حول تحويلات بناء الجملة وليس حول مهام وحدة المعالجة المركزية ؛ المعالج المسبق هو بطيء للغاية ومحدود لمثل هذا النوع من الإساءة.
ML99_assertIsTuple ، ML99_assertIsNat ، وما إلى ذلك للحصول على رسائل تشخيصية أفضل.## الرمز المميز داخل وحدات الماكرو المتوافقة مع Metalang99 بدلاً من ML99_cat أو أصدقائه ، لأنه سيتم توسيع الحجج بالكامل.ML99_todo وأصدقائها للإشارة إلى وظائف لا تنفذ. انظر CONTRIBUTING.md .
انظر ARCHITECTURE.md .
انظر idioms.md .
انظر optimization_tips.md .
ج: Metalang99 هي خطوة كبيرة نحو تشخيصات البرمجيات المفهومة. يحتوي على مدقق بناء جملة مدمج يختبر جميع المصطلحات الواردة للصلاحية:
[ playground.c ]
ML99_EVAL ( 123 )
ML99_EVAL ( x , y , z )
ML99_EVAL ( v ( Billie ) v ( Jean )) [ /bin/sh ]
$ gcc playground.c -Imetalang99/include -ftrack-macro-expansion=0
playground.c:3:1: error: static assertion failed: "invalid term `123`"
3 | ML99_EVAL(123)
| ^~~~~~~~~
playground.c:4:1: error: static assertion failed: "invalid term `x`"
4 | ML99_EVAL(x, y, z)
| ^~~~~~~~~
playground.c:5:1: error: static assertion failed: "invalid term `(0v, Billie) (0v, Jean)`, did you miss a comma?"
5 | ML99_EVAL(v(Billie) v(Jean))
| ^~~~~~~~~
يمكن لـ Metalang99 حتى التحقق من الشروط المسبقة الماكرو والإبلاغ عن خطأ:
[ playground.c ]
ML99_EVAL ( ML99_listHead ( ML99_nil ()))
ML99_EVAL ( ML99_unwrapLeft ( ML99_right ( v ( 123 ))))
ML99_EVAL ( ML99_div ( v ( 18 ), v ( 4 ))) [ /bin/sh ]
$ gcc playground.c -Imetalang99/include -ftrack-macro-expansion=0
playground.c:3:1: error: static assertion failed: "ML99_listHead: expected a non-empty list"
3 | ML99_EVAL(ML99_listHead(ML99_nil()))
| ^~~~~~~~~
playground.c:4:1: error: static assertion failed: "ML99_unwrapLeft: expected ML99_left but found ML99_right"
4 | ML99_EVAL(ML99_unwrapLeft(ML99_right(v(123))))
| ^~~~~~~~~
playground.c:5:1: error: static assertion failed: "ML99_div: 18 is not divisible by 4"
5 | ML99_EVAL(ML99_div(v(18), v(4)))
| ^~~~~~~~~
ومع ذلك ، إذا قمت بشيء محرج ، يمكن أن تصبح أخطاء وقت الترجمة غامضة تمامًا:
// ML99_PRIV_REC_NEXT_ML99_PRIV_IF_0 blah(ML99_PRIV_SYNTAX_CHECKER_EMIT_ERROR, ML99_PRIV_TERM_MATCH) ((~, ~, ~) blah, ML99_PRIV_EVAL_)(ML99_PRIV_REC_STOP, (~), 0fspace, (, ), ((0end, ~), ~), ~, ~ blah)(0)()
ML99_EVAL ((~, ~, ~) blah )في كلتا الحالتين ، يمكنك محاولة تصحيح تصحيح metaprogram الخاص بك بشكل متكرر. من تجربتي ، فإن 95 ٪ من الأخطاء مفهومة - تم تصميم Metalang99 للبشر ، وليس للوحوش الكلية.
ج: راجع الفصل "الاختبار والتصحيح والإبلاغ عن الخطأ" .
ج: أنا أستخدم الكود مقابل التنمية. إنها تتيح اقتراحات منبثقة للإنشاءات التي تم إنشاؤها بواسطة الماكرو ، ولكنها بالطبع لا تدعم تسليط الضوء على بناء جملة الماكرو.
ج: لتشغيل المعايير ، تنفيذ ./scripts/bench.sh من دليل الجذر.
ج:
ج: شاهد منشور المدونة "ما الهدف من المعالج المسبق C ، في الواقع؟"
ج: المعالج المسبق C/C ++ قادر على التكرار فقط إلى حد معين. بالنسبة إلى Metalang99 ، يتم تعريف هذا الحد من حيث خطوات التخفيضات: بمجرد استنفاد كمية ثابتة من خطوات التخفيض ، لن يتمكن metaprogram من التنفيذ بعد الآن.
ج: يستهدف Metalang99 في المقام الأول نقي C ، و C يفتقر إلى القوالب. ولكن على أي حال ، يمكنك العثور على الحجة لـ C ++ في موقع الويب الخاص بـ Boost/Preprocessor.
ج: أنا ضد الرؤوس المدمجة بسبب عبء التحديث. بدلاً من ذلك ، يمكنك فقط إضافة metalang99 كوحدة فرعية git وتحديثها باستخدام git submodule update --remote .
A: C99/C ++ 11 وما بعده.
ج: من المعروف أن Metalang99 يعمل على هؤلاء المترجمين: