力的黑暗面是通往许多能力的途径,有些人认为是不自然的。
- 达斯·西迪斯(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中编写可靠且可维护的元图的坚定基础。它被实现为预处理宏的解释性FP语言:Just #include <metalang99.h> ,您已经准备好了。 Metalang99具有代数数据类型,模式匹配,递归,咖喱和集合;此外,它提供了用于编译时间错误报告和调试的手段。借助我们内置的语法检查器,宏错误应该是完全可理解的,使您可以方便地开发。
当前,MetalAng99在OpenIPC上用作Datatype99和Interface99的间接依赖性;这包括RTSP 1.0实现以及〜50K的私有代码行。
宏可促进代码重复使用,宏是使您塑造语言以适合解决问题的建筑材料,从而导致更加干净和简洁的代码。但是,C中的元编程被完全cast割:我们甚至无法使用控制流,整数,无界序列和复合数据结构进行操作,从而从范围中抛出了许多假设有用的元图。
为了解决问题,我实施了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没有此类缺点。
几乎相同的语法。与PP顺序相比,Metalang99看起来不太陌生,因为语法与通常的预处理器代码无关。
部分申请。 MetalAng99的部分应用程序无需在这里和那里跟踪辅助参数(如在Boost/Preadotersor中完成),可以首先应用常数值来捕获环境。除此之外,部分应用还有助于更好地重用元功能。请参阅ML99_const , ML99_compose等。
调试和错误报告。您可以使用ML99_abort方便地调试宏,并通过ML99_fatal报告无法恢复的错误。口译员将立即停止并实现困难。据我们所知,没有其他宏观框架为调试和错误报告提供了这种机制。
我在Poica上的工作是在BOOST/PEDORACERSOR上实施的研究计划语言,使我对结果不满意。 Boost/预处理器的基本局限性使代码库完全无法实现。其中包括递归宏通话(被预处理器阻止),这使调试一场噩梦,缺乏部分应用程序,使上下文完全尴尬,以及导致编译器错误消息的兆字节。
只有那时,我已经理解,我们不应该真正建立一个清晰的范式来构造元图的清晰范式。考虑到这些想法,我开始实施Metalang99 ...
长话短说,释放v0.1.0的艰苦努力花费了一半的努力,并使其稳定了近一年。作为MetalAng99的真实应用程序,我完全以相同的形式创建了DataType99:实现是高度声明性的,语法很漂亮,语义是明确的。
最后,我想说的是,Metalang99仅与语法转换有关,而不是关于CPU结合的任务。预处理器太慢且有限,无法进行这种虐待。
ML99_assertIsTuple , ML99_assertIsNat等主张宏参数,以进行良好的形式,以提供更好的诊断消息。ML99_cat或其朋友,更喜欢##令牌运算符,因为尽管如此,争论仍会得到充分扩展。ML99_todo及其朋友指示未完成功能。 参见CONTRIBUTING.md 。
请参阅ARCHITECTURE.md 。
参见idioms.md 。
请参阅optimization_tips.md 。
答: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是为人类而不是用于宏观怪物的。
答:请参阅“测试,调试和错误报告”一章。
答:我使用VS代码进行开发。它可以实现宏观生成构造的弹出建议,但当然,它不支持宏观语法突出显示。
答:要运行基准,请从根目录中执行./scripts/bench.sh 。
一个:
答:请参阅博客文章“实际上,C预处理器的重点是什么?”
答:C/C ++预处理器能够仅迭代一定限制。对于MetalAng99,该限制是根据减排步骤定义的:一旦固定数量的减少步骤耗尽,您的元数据将无法再执行。
答:Metalang99主要针对纯C,C缺少模板。但是无论如何,您可以在BOOST/Preprocessor的网站上找到C ++的论点。
答:由于更新的负担,我反对合并的标题。取而代之的是,您只需添加Metalang99作为GIT subsodule,然后使用git submodule update --remote进行更新。
答:C99/C ++ 11及以后。
答:已知Metalang99可以在这些编译器上使用: