힘의 어두운면은 많은 능력의 통로이며, 일부는 부자연스럽지 않은 것으로 간주됩니다.
-Darth Sidious
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에서 신뢰할 수 있고 유지 관리 가능한 메타 프로 그램을 작성하기위한 확고한 기반입니다. 전처리 매크로의 #include <metalang99.h> 에 해석 된 FP 언어로 구현됩니다. MetalAng99는 대수 데이터 유형, 패턴 매칭, 재귀, 카레 및 컬렉션을 특징으로합니다. 또한 컴파일 타임 오류보고 및 디버깅을위한 수단을 제공합니다. 내장 구문 검사기를 사용하면 매크로 오류가 완벽하게 이해할 수 있어야하므로 편리한 개발이 가능합니다.
현재, MetalAng99는 OpenIPC에서 DataType99 및 Interface99의 간접 종속성으로 사용됩니다. 여기에는 ~ 50k 라인의 개인 코드와 함께 RTSP 1.0 구현이 포함됩니다.
매크로는 코드 재사용을 용이하게하고, 매크로는 해결중인 문제에 맞게 언어를 형성 할 수있는 건축 자재이며, 더 깨끗하고 간결한 코드를 이끌어냅니다. 그러나 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는 그러한 단점이 없습니다.
거의 같은 구문. Syntax는 일반적인 사전 처리기 코드와 무의미하게 다르기 때문에 Metalang99는 Order PP와 비교할 때 너무 외계인으로 보이지 않습니다.
부분 적용. MetalAng99의 부분 응용 프로그램은 여기저기서 보조 인수를 여기저기서 추적하는 대신 먼저 일정한 값을 적용하여 환경을 캡처 할 수 있습니다. 그 외에도, 부분 적용은 더 나은 메타지 재사용을 용이하게한다. ML99_const , ML99_compose 등을 참조하십시오.
디버깅 및 오류보고. ML99_abort 로 매크로를 편리하게 디버깅하고 ML99_fatal 으로 복귀 할 수없는 오류를보고 할 수 있습니다. 통역사는 즉시 중지하고 트릭을 수행합니다. 우리가 아는 한, 다른 매크로 프레임 워크는 디버깅 및 오류보고를위한 메커니즘을 제공하지 않습니다.
Boost/Preprocessor에 구현 된 연구 프로그래밍 언어 인 Poica에 대한 저의 작업은 그 결과에 만족하지 못했습니다. Boost/Preprocessor의 기본 제한은 코드베이스를 간단하게 유지할 수 없게 만들었습니다. 여기에는 재귀 매크로 통화 (사전 프로세서가 차단)가 포함되어 있으며, 이는 완전히 악몽을 끄는 데 디버깅을했으며, 컨텍스트를 완전히 어색하게 통과시키는 부분 적용이없고, 모든 실수로 인해 컴파일러 오류 메시지의 거대로 발생했습니다.
그래야만 다양한 임시 메커니즘으로 사전 처리기를 풍부하게하는 대신 메타 프로 그램을 구조화 할 분명한 패러다임을 확립해야한다는 것을 이해했습니다. 이러한 생각을 염두에두고 Metalang99를 구현하기 시작했습니다 ...
간단히 말해서, V0.1.0을 석방하는 데 1 년의 노력이 걸렸고 거의 1 년이 걸렸습니다. MetalAng99의 실제 응용 프로그램으로서, 나는 원했던 것과 동일한 형태의 DataType99를 만들었습니다. 구현은 선언적이며 구문은 멋지고 의미론은 잘 정의되어 있습니다.
마지막으로, 나는 Metalang99가 CPU 결합 작업에 관한 것이 아니라 구문 변환에 관한 것이라고 말하고 싶습니다. 사전 처리기는 그러한 종류의 학대에 비해 너무 느리고 제한적입니다.
ML99_assertIsTuple , ML99_assertIsNat 등을 사용하여 잘 형성되는 매크로 매개 변수를 주장하십시오.ML99_cat -Compliant Macros 내부에서 ## 토큰 페이스트 연산자를 선호하십시오.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 : 개발에 대 코드를 사용합니다. 거시적 생성 된 구성의 팝업 제안을 가능하게하지만 물론 매크로 구문 강조를 지원하지는 않습니다.
A : 벤치 마크를 실행하려면 루트 디렉토리에서 ./scripts/bench.sh 실행하십시오.
에이:
A : 블로그 게시물을보십시오 . "실제로 C 전 처리기의 요점은 무엇입니까?"
A : C/C ++ 사전 처리기는 특정 한도까지만 반복 할 수 있습니다. MetalAng99의 경우,이 한계는 감소 단계 측면에서 정의됩니다. 일단 고정 된 양의 감소 단계가 소진되면 메타 프로 그램은 더 이상 실행할 수 없습니다.
A : MetalAng99는 주로 순수한 C를 대상으로하며 C는 템플릿이 부족합니다. 그러나 어쨌든 Boost/Preprocessor 웹 사이트에서 C ++에 대한 논쟁을 찾을 수 있습니다.
A : 업데이트에 대한 부담으로 인해 합병 된 헤더에 반대합니다. 대신, MetalAng99를 GIT 하위 모듈로 추가하고 git submodule update --remote 로 업데이트 할 수 있습니다.
A : C99/C ++ 11 이상.
A : Metalang99는 이러한 컴파일러에서 작동하는 것으로 알려져 있습니다.