Sisi gelap kekuatan adalah jalur menuju banyak kemampuan, beberapa dianggap tidak wajar.
- Darth Sidious
Berdasarkan examples/demo.c :
| Manipulasi daftar waktu kompilasi |
// 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 )))),
}; |
| Rekursi Makro |
#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 )); |
| Kelebihan sejumlah argumen |
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.
} |
(Petunjuk: v(something) mengevaluasi something .)
Metalang99 adalah dasar yang kuat untuk menulis metaprogram yang andal dan dapat dipelihara di C99 murni. Ini diimplementasikan sebagai bahasa FP yang ditafsirkan di atas makro preprocessor: hanya #include <metalang99.h> dan Anda siap untuk pergi. Metalang99 fitur tipe data aljabar, pencocokan pola, rekursi, kari, dan koleksi; Selain itu, ia menyediakan sarana untuk pelaporan dan debugging kesalahan waktu kompilasi. Dengan pemeriksa sintaks bawaan kami, kesalahan makro harus dapat dipahami dengan sempurna, memungkinkan Anda untuk pengembangan yang nyaman.
Saat ini, Metalang99 digunakan di OpenIPC sebagai ketergantungan tidak langsung dari DataType99 dan Interface99; Ini termasuk implementasi RTSP 1.0 bersama dengan ~ 50 ribu baris kode pribadi.
Makro memfasilitasi penggunaan kembali kode, makro adalah bahan bangunan yang memungkinkan Anda membentuk bahasa yang sesuai dengan masalah yang diselesaikan, yang mengarah ke kode yang lebih bersih dan ringkas. Namun, metaprogramming dalam C benar -benar dikebiri: kami bahkan tidak dapat beroperasi dengan aliran kontrol, bilangan bulat, urutan yang tidak terbatas, dan struktur data majemuk, sehingga melemparkan banyak metaprogram yang bermanfaat secara hipotetis di luar ruang lingkup.
Untuk menyelesaikan masalah, saya telah menerapkan Metalang99. Memiliki fungsinya yang kami miliki, menjadi mungkin untuk mengembangkan metaprogram yang bahkan tidak sepele, seperti 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 ;
}Atau antarmuka99:
#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 ));
}Berbeda dengan teknik yang tidak jelas, seperti serikat pekerja yang ditandai atau tabel metode virtual, metaprogram di atas memanfaatkan keamanan jenis, keringkasan sintaks, dan mempertahankan tata letak memori yang tepat dari kode yang dihasilkan.
Terlihat menarik? Lihatlah posting motivasi untuk informasi lebih lanjut.
Metalang99 hanyalah satu set file header dan tidak ada yang lain. Untuk menggunakannya sebagai ketergantungan, Anda perlu:
metalang99/include untuk memasukkan direktori.-ftrack-macro-expansion=0 (GCC) atau -fmacro-backtrace-limit=1 (Dentang) untuk menghindari kesalahan ekspansi makro yang tidak berguna. Jika Anda menggunakan cmake, cara yang disarankan adalah 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 ()Secara opsional, Anda dapat mengukuhkan header dalam proyek Anda yang mengandalkan Metalang99. Ini akan mengurangi waktu kompilasi karena header tidak akan dikompilasi setiap kali dimasukkan.
Tutorial | Contoh | Dokumentasi pengguna
Selamat peretasan!
Rekursi Makro. Panggilan rekursif berperilaku seperti yang diharapkan. Secara khusus, untuk mengimplementasikan rekursi, meningkatkan/preprosesor hanya menyalin-pastes semua fungsi rekursif hingga batas tertentu dan kekuatan untuk melacak kedalaman rekursi atau mengandalkan pengurangan bawaan mereka. Menjadi seorang penerjemah, Metalang99 bebas dari kelemahan seperti itu.
Sintaks yang hampir sama. Metalang99 tidak terlihat terlalu asing dibandingkan dengan pesanan PP karena sintaksis berbeda secara tidak signifikan dari kode preprosesor biasa.
Aplikasi parsial. Alih -alih melacak argumen tambahan di sana -sini (seperti yang dilakukan dalam Boost/Preprocessor), aplikasi parsial Metalang99 memungkinkan untuk menangkap lingkungan dengan menerapkan nilai konstan terlebih dahulu. Selain itu, aplikasi parsial memfasilitasi penggunaan kembali metafunctions yang lebih baik; Lihat ML99_const , ML99_compose , dll.
Pelaporan debugging dan kesalahan. Anda dapat dengan mudah men -debug makro Anda dengan ML99_abort dan melaporkan kesalahan yang tidak dapat dipulihkan dengan ML99_fatal . Penerjemah akan segera berhenti dan melakukan trik. Sepengetahuan kami, tidak ada kerangka makro lain yang menyediakan mekanisme untuk debugging dan pelaporan kesalahan.
Pekerjaan saya di Poica, bahasa pemrograman penelitian yang diimplementasikan pada saat boost/preprocessor, telah membuat saya tidak puas dengan hasilnya. Keterbatasan mendasar dari Boost/Preprocessor telah membuat basis kode itu tidak dapat diketahui; Ini termasuk panggilan makro rekursif (diblokir oleh preprocessor), yang telah membuat debugging mimpi buruk yang lengkap, tidak adanya aplikasi parsial yang telah membuat konteks yang lewat benar -benar canggung, dan setiap kesalahan yang mengakibatkan megabyte pesan kesalahan kompiler.
Hanya dengan begitu saya mengerti bahwa alih-alih memperkaya preprocessor dengan berbagai mekanisme ad-hoc, kita harus benar-benar membangun paradigma yang jelas untuk menyusun metaprogram. Dengan pemikiran ini, saya mulai menerapkan Metalang99 ...
Singkat cerita, butuh setengah tahun kerja keras untuk merilis v0.1.0 dan hampir setahun untuk membuatnya stabil. Sebagai aplikasi dunia nyata dari Metalang99, saya membuat DataType99 persis dari bentuk yang sama yang saya inginkan: implementasinya sangat deklaratif, sintaksnya bagus, dan semantiknya terdefinisi dengan baik.
Akhirnya, saya ingin mengatakan bahwa metalang99 hanyalah tentang transformasi sintaksis dan bukan tentang tugas yang terikat CPU; Preprocessor terlalu lambat dan terbatas untuk pelecehan semacam itu.
ML99_assertIsTuple , ML99_assertIsNat , dll. Untuk pesan diagnostik yang lebih baik.## token-pasting di dalam makro yang sesuai dengan metalang99 daripada ML99_cat atau teman-temannya, karena argumen akan tetap diperluas sepenuhnya.ML99_todo dan teman -temannya untuk menunjukkan fungsionalitas yang tidak diimplementasikan. Lihat CONTRIBUTING.md .
Lihat ARCHITECTURE.md .
Lihat idioms.md .
Lihat optimization_tips.md .
A: Metalang99 adalah langkah besar menuju diagnostik kompiler yang dapat dimengerti. Ini memiliki pemeriksa sintaks bawaan yang menguji semua istilah yang masuk untuk validitas:
[ 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 bahkan dapat memeriksa prasyarat makro dan melaporkan kesalahan:
[ 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)))
| ^~~~~~~~~
Namun, jika Anda melakukan sesuatu yang canggung, kesalahan waktu kompilasi bisa menjadi sangat dikaburkan:
// 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 )Dalam kedua kasus tersebut, Anda dapat mencoba untuk men -debug metaprogram Anda secara iteratif. Dari pengalaman saya, 95% kesalahan dapat dipahami - Metalang99 dibangun untuk manusia, bukan untuk monster makro.
A: Lihat bab "Pengujian, Debugging, dan Pelaporan Kesalahan" .
A: Saya menggunakan kode vs untuk pengembangan. Ini memungkinkan saran pop-up dari konstruksi yang dihasilkan makro tetapi, tentu saja, tidak mendukung penyorotan sintaks makro.
A: Untuk menjalankan tolok ukur, jalankan ./scripts/bench.sh dari direktori root.
A:
A: Lihat posting blog "Apa gunanya preprocessor C, sebenarnya?"
A: Preprosesor C/C ++ mampu mengulangi hingga batas tertentu. Untuk Metalang99, batas ini didefinisikan dalam hal pengurangan langkah: Setelah jumlah langkah pengurangan tetap habis, metaprogram Anda tidak akan dapat mengeksekusi lagi.
A: Metalang99 terutama ditargetkan pada C murni, dan C tidak memiliki templat. Tapi bagaimanapun, Anda dapat menemukan argumentasi untuk C ++ di situs web Boost/Preprocessor.
A: Saya menentang header yang digabungkan karena beban dengan memperbarui. Sebagai gantinya, Anda bisa menambahkan Metalang99 sebagai submodule git dan memperbaruinya dengan git submodule update --remote .
A: C99/C ++ 11 dan dan seterusnya.
A: Metalang99 diketahui bekerja pada kompiler ini: