Die dunkle Seite der Kraft ist ein Weg zu vielen Fähigkeiten, von denen einige als unnatürlich angesehen werden.
- Darth Sidious
Basierend auf examples/demo.c :
| Kompilierungs-Zeit-Listenmanipulation |
// 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 )))),
}; |
| Makro -Rekursion |
#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 )); |
| Überlastung einer Reihe von Argumenten |
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.
} |
(Hinweis: v(something) bewertet something .)
Metalang99 ist eine feste Grundlage für das Schreiben von zuverlässigen und wartbaren Metaprogrammen in reinem C99. Es wird als interpretierte FP -Sprache auf dem Vorprozessor -Makros implementiert: Just #include <metalang99.h> Und Sie sind bereit zu gehen. Metalang99 Features Algebraic -Datentypen, Musteranpassung, Rekursion, Currying und Sammlungen; Darüber hinaus bietet es Mittel zur Berichterstattung über Kompilierungszeitfehler und -Debuggen. Mit unserem integrierten Syntax-Checker sollten Makrofehler vollkommen verständlich sein, sodass Sie eine bequeme Entwicklung ermöglichen.
Derzeit wird Metalang99 bei OpenIPC als indirekte Abhängigkeit von DataType99 und Schnittstelle99 verwendet. Dies beinhaltet eine RTSP 1.0 -Implementierung sowie ~ 50.000 Zeilen privater Code.
Makros erleichtern die Wiederverwendung von Code, Makros sind das Baustoff, mit dem Sie die Sprache so gestalten können, dass sie zu dem Problem gelöst wird, was zu sauberer und prägnanterer Code führt. Die Metaprogrammierung in C ist jedoch völlig kastriert: Wir können nicht einmal mit Kontrollfluss, Ganzzahlen, unbegrenzten Sequenzen und zusammengesetzten Datenstrukturen arbeiten, wodurch viele hypothetisch nützliche Metaprogramme aus Geltungsbereich herausgeworfen werden.
Um das Problem zu lösen, habe ich Metalang99 implementiert. Mit seiner Funktionalität ist es möglich, selbst relativ nicht triviale Metaprogramme wie DataType99 zu entwickeln:
#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 ;
}Oder Schnittstelle99:
#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 ));
}Im Gegensatz zu den vagen Techniken wie markierten Gewerkschaften oder virtuellen Methodentabellen nutzen die oben genannten Metaprogramme die Sicherheitstypsicherheit, die Syntax -Übersicht und führen das genaue Speicherlayout von generiertem Code bei.
Sieht interessant aus? Weitere Informationen finden Sie im Motivationsbeitrag.
Metalang99 ist nur eine Reihe von Header -Dateien und sonst nichts. Um es als Abhängigkeit zu verwenden, müssen Sie:
metalang99/include Sie Verzeichnisse.-ftrack-macro-expansion=0 (gcc) oder -fmacro-backtrace-limit=1 (klang) an, um nutzlose Makroerweiterungsfehler zu vermeiden. Wenn Sie CMAKE verwenden, ist der empfohlene Weg 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 ()Optional können Sie die Header in Ihrem Projekt vorkompilieren, die auf Metalang99 beruhen. Dies verringert die Kompilierungszeit, da die Header nicht jedes Mal zusammengestellt werden, wenn sie eingeschlossen sind.
Tutorial | Beispiele | Benutzerdokumentation
Frohe Hacking!
Makro -Rekursion. Rekursive Anrufe verhalten sich wie erwartet. Insbesondere zur Umsetzung von Rekursion, Boost/Preprozessor, kopieren Sie alle rekursiven Funktionen bis zu einer bestimmten Grenze und zwingen die Kräfte, entweder die Rekursionstiefe im Auge zu behalten oder auf ihren integrierten Abzug zu stützen. Als Dolmetscher ist Metalang99 frei von solchen Nachteilen.
Fast die gleiche Syntax. Metalang99 sieht im Vergleich zu Order PP nicht zu fremd aus, da sich die Syntax unbedeutend vom üblichen Vorprozessorcode unterscheidet.
Teilweise Anwendung. Anstatt hier und da Hilfsargumente zu verfolgen (wie in Boost/Preprozessor), ermöglicht die teilweise Anwendung von Metalang99, eine Umgebung zu erfassen, indem zuerst konstante Werte angewendet werden. Abgesehen davon erleichtert die teilweise Anwendung eine bessere Wiederverwendung von Metafunktionen; Siehe ML99_const , ML99_compose usw.
Debugging und Fehlerberichterstattung. Sie können Ihre Makros bequem mit ML99_abort debuggen und nicht wiederherstellbare Fehler mit ML99_fatal melden. Der Dolmetscher wird sofort anhalten und den Trick machen. Nach unserem Kenntnisstand bietet kein anderer Makro -Framework einen solchen Mechanismus für Debugging und Fehlerberichterstattung.
Meine Arbeit über Poica, eine Forschungsprogrammiersprache, die nach Boost/Preprozessor implementiert ist, hat mich mit dem Ergebnis unzufrieden gemacht. Die grundlegenden Einschränkungen des Boost/Preprozessors haben die Codebasis einfach unbetentbar gemacht; Dazu gehören rekursive Makroanrufe (blockiert vom Präprozessor), die das Debuggen zu einem vollständigen Albtraum gemacht haben, das Fehlen einer teilweisen Anwendung, die den Kontext völlig unangenehm gemacht hat, und jeden einzelnen Fehler, der zu Megabyten von Compiler -Fehlermeldungen führte.
Erst dann habe ich verstanden, dass wir anstatt den Präprozessor mit verschiedenen AD-hoc-Mechanismen zu bereichern, wirklich ein klares Paradigma für die Strukturierung von Metaprogrammen herstellen sollten. Vor diesem Hintergrund begann ich Metalang99 im Umsetzung ...
Kurz gesagt, kurzer Sinn, es dauerte eine halbe Jahr der harten Arbeit, um V0.1.0 und fast ein Jahr zu veröffentlichen, um es stabil zu machen. Als reale Anwendung von Metalang99 habe ich DataType99 genau aus derselben Form erstellt, die ich wollte: Die Implementierung ist sehr deklarativ, die Syntax ist raffiniert und die Semantik ist gut definiert.
Schließlich möchte ich sagen, dass Metalang99 nur um Syntax-Transformationen und nicht um CPU-gebundene Aufgaben geht. Der Präprozessor ist für einen solchen Missbrauch einfach zu langsam und begrenzt.
ML99_assertIsTuple , ML99_assertIsNat usw. für bessere diagnostische Nachrichten.## Token-Pasting-Operator in Metalang99-konformen Makros anstelle von ML99_cat oder seinen Freunden, da Argumente dennoch vollständig erweitert werden.ML99_todo und seine Freunde, um eine unimplementierte Funktionalität anzuzeigen. Siehe CONTRIBUTING.md .
Siehe ARCHITECTURE.md .
Siehe idioms.md .
Siehe optimization_tips.md .
A: Metalang99 ist ein großer Schritt in Richtung verständlicher Compiler -Diagnostik. Es verfügt über einen integrierten Syntax-Checker, der alle eingehenden Begriffe für die Gültigkeit testet:
[ 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 kann sogar nach Makro -Voraussetzungen prüfen und einen Fehler melden:
[ 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)))
| ^~~~~~~~~
Wenn Sie jedoch etwas Unbeholfenes tun, können Kompilierungs-Zeitfehler ziemlich verdeckt werden:
// 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 )In beiden Fällen können Sie versuchen, Ihr Metaprogramm iterativ zu debuggen. Nach meiner Erfahrung sind 95% der Fehler verständlich - Metalang99 ist für Menschen gebaut, nicht für Makro -Monster.
A: Siehe Kapitel "Testen, Debuggen und Fehlerberichterstattung" .
A: Ich verwende VS -Code für die Entwicklung. Es ermöglicht Pop-up-Vorschläge für makrogenerierte Konstruktionen, unterstützt jedoch natürlich keine Makrosyntax-Hervorhebung.
A: Um die Benchmarks auszuführen, werden ./scripts/bench.sh aus dem Stammverzeichnis ausführen.
A:
A: Siehe den Blog -Beitrag "Was ist der Sinn des C -Präprozessors eigentlich?"
A: Der C/C ++ - Präprozessor ist in der Lage, nur bis zu einer bestimmten Grenze zu iterieren. Für Metalang99 wird diese Grenze in Bezug auf Reduktionsschritte definiert: Sobald eine feste Menge an Reduktionsschritten erschöpft ist, kann Ihr Metaprogramm nicht mehr ausgeführt werden.
A: Metalang99 ist hauptsächlich auf reines C abzielt, und C fehlen Vorlagen. Auf der Website von Boost/Preprozessor finden Sie trotzdem die Argumentation für C ++.
A: Ich bin wegen der Aktualisierung gegen die Verschmelzung von Kopfzeilen. Stattdessen können Sie einfach Metalang99 als Git -Submodul hinzufügen und es mit git submodule update --remote aktualisieren.
A: C99/C ++ 11 und weiter.
A: Metalang99 arbeitet bekannt an diesen Compilern: