力の暗い側は多くの能力への経路であり、一部は不自然であると考えられています。
- ダース・シディアス
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言語として実装されています:Just #include <metalang99.h>そしてあなたは準備ができています。 Metalang99には、代数的データ型、パターンマッチング、再帰、カレー、コレクションがあります。さらに、コンパイル時間エラーの報告とデバッグの手段を提供します。組み込みの構文チェッカーを使用すると、マクロエラーは完全に理解できる必要があり、便利な開発を可能にします。
現在、Metalang99は、DataType99およびInterface99の間接依存性としてOpenIPCで使用されています。これには、RTSP 1.0の実装と、約5万行のプライベートコードが含まれます。
マクロはコードの再利用を促進します。マクロは、解決される問題に合わせて言語を形作ることができる建築材料であり、よりクリーンで簡潔なコードにつながります。ただし、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 ;
}またはinterface99:
#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には、このような欠点はありません。
ほぼ同じ構文。構文は通常のプリプロセッサコードとは重要ではないため、注文PPと比較してあまり異質に見えません。
部分アプリケーション。 Metalang99の部分アプリケーションでは、あちこちで補助引数を追跡する代わりに(Boost/Preprocessorで行われているように)、最初に一定の値を適用して環境をキャプチャできます。それに加えて、部分的なアプリケーションは、メタ機能のより良い再利用を促進します。 ML99_const 、 ML99_composeなどを参照してください。
デバッグとエラーの報告。マクロをML99_abortで便利にデバッグし、 ML99_fatalで回復不可能なエラーを報告できます。通訳はすぐに停止し、トリックを行います。私たちの知る限り、他のマクロフレームワークは、デバッグとエラーの報告のためのこのようなメカニズムを提供していません。
Boost/Preprocessorで実装された研究プログラミング言語であるPoicaに関する私の仕事は、結果に不満を抱いています。ブースト/プリプロセッサの基本的な制限により、コードベースは単純に維持できません。これらには、完全な悪夢のようにデバッグされた再帰マクロコール(プレプロセッサによってブロックされた)、コンテキストが完全に厄介に渡された部分的なアプリケーションの欠如、およびコンパイラエラーメッセージのメガバイトをもたらすすべての間違いが含まれます。
そうしてはじめに、さまざまなアドホックメカニズムでプリプロセッサを濃縮する代わりに、メタプログラムを構造化する明確なパラダイムを実際に確立する必要があることを理解しました。これらの考えを念頭に置いて、私はMetalang99を実装し始めました...
簡単に言えば、v0.1.0をリリースするのに半年の努力がかかり、それを安定させるのにほぼ1年かかりました。 Metalang99の実際のアプリケーションとして、私はそれを望んでいたのと同じ形式で正確にDatatype99を作成しました。実装は非常に宣言的であり、構文は気が狂い、セマンティクスは明確に定義されています。
最後に、Metalang99は構文変換に関するものであり、CPUに縛られたタスクに関するものではないと言いたいです。プリプロセッサは遅すぎて、このような虐待には制限されています。
ML99_assertIsTuple 、 ML99_assertIsNatなどを使用して、より良い診断メッセージを使用して、マクロパラメーターを整理様性についてアサイティングします。ML99_catまたはその友人の代わりに、Metalang99に準拠したマクロ内の##トークンパスティングオペレーターを好みます。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は主に純粋なCを対象としており、Cにはテンプレートがありません。とにかく、Boost/PreprocessorのWebサイトでC ++の議論を見つけることができます。
A:更新に負担がかかるため、私は融合したヘッダーに反対しています。代わりに、GitサブモジュールとしてMetalang99を追加して、 git submodule update --remoteで更新するだけです。
A:C99/C ++ 11以降。
A:Metalang99は、これらのコンパイラに取り組むことが知られています。