Темная сторона силы - это путь к многим способностям, некоторые считаются неестественными.
- Дарт Сидиус
На основе 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 является прочной основой для написания надежных и поддерживаемых метапрограмм в чистом C99. Он реализован как интерпретированный язык FP на вершине препроцессорных макросов: просто #include <metalang99.h> и вы готовы к работе. Metalang99 имеет алгебраические типы данных, сопоставление рисунков, рекурсию, каррию и коллекции; Кроме того, он обеспечивает средства для отчетности по ошибкам и отладки. С помощью нашего встроенного синтаксического контроля макрос ошибки должны быть совершенно понятными, что позволяет вам для удобной разработки.
В настоящее время Metalang99 используется в OpenIPC в качестве косвенной зависимости DataType99 и интерфейса99; Это включает в себя реализацию RTSP 1.0, а также ~ 50K строки частного кода.
Макросы облегчают повторное использование кода, макросы-это строительный материал, который позволяет формировать язык в соответствии с решением проблемы, что приводит к более чистому и краткому коду. Тем не менее, метапрограммирование в C является совершенно кастрированным: мы даже не можем работать с управляющим потоком, целыми числами, неограниченными последовательностями и составными структурами данных, тем самым выбрасывая много гипотетически полезных метапрегром.
Чтобы решить проблему, я внедрил Metalang99. Имея в нашу функциональность в нашем распоряжении, становится возможным разработать даже довольно нетривиальные метапрограммы, такие как 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 ));
}В отличие от расплывчатых методов, таких как тегированные профсоюзы или таблицы виртуальных методов, вышеупомянутые метапрограммы используют безопасность типа типа, синтаксис -хростья и поддержание точного сбора памяти сгенерированного кода.
Выглядит интересно? Проверьте мотивационный пост для получения дополнительной информации.
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, оставила меня неудовлетворенным результатом. Фундаментальные ограничения Boost/Preprocessor сделали кодовую базу просто невозможности; К ним относятся рекурсивные макросовые вызовы (заблокированные препроцессором), которые сделали отладку полного кошмара, отсутствие частичного применения, которое сделало контекст, проходя совершенно неловкий, и каждая ошибка, которая привела к мегабайтам сообщений об ошибках компилятора.
Только тогда я понял, что вместо того, чтобы обогащать препроцессор различными специальными механизмами, мы должны действительно установить четкую парадигму, в которой можно структурировать метапрограммы. Имея в виду эти мысли, я начал внедрять Metalang99 ...
Короче говоря, потребовалось пол года тяжелой работы, чтобы выпустить V0.1.0 и почти год, чтобы сделать его стабильным. Как реальное применение Metalang99, я создал DataType99 точно такой же формы, в которой я хотел: реализация очень декларативна, синтаксис является изящным, а семантика четко определена.
Наконец, я хочу сказать, что Metalang99-это только синтаксические преобразования, а не о задачах, связанных с процессорами; Препроцессор слишком медленный и ограниченный для такого рода злоупотреблений.
ML99_assertIsTuple , ML99_assertIsNat и т. Д. Для лучших диагностических сообщений.## -оператор PASTEN-PASTING Внутри Metalang99-совместимых макросов вместо ML99_cat или его друзей, потому что аргументы, тем не менее, будут полностью расширены.ML99_todo и его друзей, чтобы указать невыполненную функциональность. См. CONTRIBUTING.md .
Смотрите ARCHITECTURE.md .
См. idioms.md .
См. optimization_tips.md .
A: 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 )В любом случае вы можете попытаться итеративно отладить свою метапрограмму. Исходя из моего опыта, 95% ошибок понятны - Metalang99 создан для людей, а не для макромонстра.
A: См. Главу «Тестирование, отладка и отчет об ошибках» .
A: Я использую VS -код для разработки. Это позволяет всплывать предложения по макрогенерированным конструкциям, но, конечно, не поддерживает макро-синтаксис.
A: Чтобы запустить тесты, выполнить ./scripts/bench.sh из корневого каталога.
A:
A: Смотрите сообщение в блоге "Какой смысл препроцессора C, на самом деле?"
A: Препроцессор C/C ++ способен итерации только до определенного предела. Для Metalang99 этот предел определяется с точки зрения этапов сокращения: после того, как фиксированное количество этапов сокращения исчерпано, ваша метапрограмма больше не сможет выполнять.
A: Metalang99 в первую очередь нацелен на Pure C, а C отсутствуют шаблоны. Но в любом случае, вы можете найти аргументацию для C ++ на веб -сайте Boost/Preprocessor.
A: Я против объединенных заголовков из -за бремени с обновлением. Вместо этого вы можете просто добавить Metalang99 в качестве подмодуля GIT и обновить его с помощью git submodule update --remote .
A: C99/C ++ 11 и далее.
A: Metalang99, как известно, работает над этими компиляторами: