lisp,Rust,Scala中的宏与C/C++的宏有什么异同?
3 个回答
6/18 15:14 经群友提醒做改动
c和c++是对token做替换,但不提供模式匹配。
racket的宏是对ast(抽象语法树)做替换,顺带拓展出了(貌似)模式匹配。
rust的宏则因为ast没有稳定下来,所以只提供了对token的匹配和替换功能。
Scala我没用过,不清楚。
吐个槽,第一门语言应该是Scala
没用过Scala,因此只对后几门语言的宏进行比较
从一般角度讲
宏的强大程度: Lisp\gg Rust\gt C/C++
但是这并不意味着C的宏就不能搞一些奇技淫巧了,例如下面这个例子
#define OFPACTS \
/* Output. */ \
OFPACT(OUTPUT, ofpact_output, ofpact, "output") \
OFPACT(GROUP, ofpact_group, ofpact, "group") \
OFPACT(CONTROLLER, ofpact_controller, ofpact, "controller") \
OFPACT(ENQUEUE, ofpact_enqueue, ofpact, "enqueue") \
OFPACT(OUTPUT_REG, ofpact_output_reg, ofpact, "output_reg") \
OFPACT(BUNDLE, ofpact_bundle, slaves, "bundle") \
/* Header changes. */ \
OFPACT(SET_FIELD, ofpact_set_field, ofpact, "set_field") \
OFPACT(SET_VLAN_VID, ofpact_vlan_vid, ofpact, "set_vlan_vid") \
OFPACT(SET_VLAN_PCP, ofpact_vlan_pcp, ofpact, "set_vlan_pcp") \
OFPACT(STRIP_VLAN, ofpact_null, ofpact, "strip_vlan") \
OFPACT(PUSH_VLAN, ofpact_null, ofpact, "push_vlan") \
OFPACT(SET_ETH_SRC, ofpact_mac, ofpact, "mod_dl_src") \
OFPACT(SET_ETH_DST, ofpact_mac, ofpact, "mod_dl_dst") \
OFPACT(SET_IPV4_SRC, ofpact_ipv4, ofpact, "mod_nw_src") \
OFPACT(SET_IPV4_DST, ofpact_ipv4, ofpact, "mod_nw_dst") \
OFPACT(SET_IP_DSCP, ofpact_dscp, ofpact, "mod_nw_tos") \
OFPACT(SET_IP_ECN, ofpact_ecn, ofpact, "mod_nw_ecn") \
OFPACT(SET_IP_TTL, ofpact_ip_ttl, ofpact, "mod_nw_ttl") \
OFPACT(SET_L4_SRC_PORT, ofpact_l4_port, ofpact, "mod_tp_src") \
OFPACT(SET_L4_DST_PORT, ofpact_l4_port, ofpact, "mod_tp_dst") \
OFPACT(REG_MOVE, ofpact_reg_move, ofpact, "move") \
OFPACT(STACK_PUSH, ofpact_stack, ofpact, "push") \
OFPACT(STACK_POP, ofpact_stack, ofpact, "pop") \
OFPACT(DEC_TTL, ofpact_cnt_ids, cnt_ids, "dec_ttl") \
OFPACT(SET_MPLS_LABEL, ofpact_mpls_label, ofpact, "set_mpls_label") \
OFPACT(SET_MPLS_TC, ofpact_mpls_tc, ofpact, "set_mpls_tc") \
OFPACT(SET_MPLS_TTL, ofpact_mpls_ttl, ofpact, "set_mpls_ttl") \
OFPACT(DEC_MPLS_TTL, ofpact_null, ofpact, "dec_mpls_ttl") \
OFPACT(PUSH_MPLS, ofpact_push_mpls, ofpact, "push_mpls") \
OFPACT(POP_MPLS, ofpact_pop_mpls, ofpact, "pop_mpls") \
/* Metadata. */ \
OFPACT(SET_TUNNEL, ofpact_tunnel, ofpact, "set_tunnel") \
OFPACT(SET_QUEUE, ofpact_queue, ofpact, "set_queue") \
OFPACT(POP_QUEUE, ofpact_null, ofpact, "pop_queue") \
OFPACT(FIN_TIMEOUT, ofpact_fin_timeout, ofpact, "fin_timeout") \
/* Flow table interaction. */ \
OFPACT(RESUBMIT, ofpact_resubmit, ofpact, "resubmit") \
OFPACT(LEARN, ofpact_learn, specs, "learn") \
/* Arithmetic. */ \
OFPACT(MULTIPATH, ofpact_multipath, ofpact, "multipath") \
/* Other. */ \
OFPACT(NOTE, ofpact_note, data, "note") \
OFPACT(EXIT, ofpact_null, ofpact, "exit") \
OFPACT(SAMPLE, ofpact_sample, ofpact, "sample") \
/* Instructions. */ \
OFPACT(METER, ofpact_meter, ofpact, "meter") \
OFPACT(CLEAR_ACTIONS, ofpact_null, ofpact, "clear_actions") \
OFPACT(WRITE_ACTIONS, ofpact_nest, ofpact, "write_actions") \
OFPACT(WRITE_METADATA, ofpact_metadata, ofpact, "write_metadata") \
OFPACT(GOTO_TABLE, ofpact_goto_table, ofpact, "goto_table")
#define OFPACT(ENUM, STRUCT, MEMBER, NAME) \
BUILD_ASSERT_DECL(offsetof(struct STRUCT, ofpact) == 0); \
enum { OFPACT_##ENUM##_RAW_SIZE \
= (offsetof(struct STRUCT, MEMBER) \
? offsetof(struct STRUCT, MEMBER) \
: sizeof(struct STRUCT)) }; \
enum { OFPACT_##ENUM##_SIZE \
= ROUND_UP(OFPACT_##ENUM##_RAW_SIZE, OFPACT_ALIGNTO) }; \
static inline struct STRUCT * \
ofpact_get_##ENUM(const struct ofpact *ofpact) \
{ \
ovs_assert(ofpact->type == OFPACT_##ENUM); \
return ALIGNED_CAST(struct STRUCT *, ofpact); \
} \
static inline struct STRUCT * \
ofpact_put_##ENUM(struct ofpbuf *ofpacts) \
{ \
return ofpact_put(ofpacts, OFPACT_##ENUM, \
OFPACT_##ENUM##_RAW_SIZE); \
} \
static inline void \
ofpact_init_##ENUM(struct STRUCT *ofpact) \
{ \
ofpact_init(&ofpact->ofpact, OFPACT_##ENUM, \
OFPACT_##ENUM##_RAW_SIZE); \
OFPACTS
#undef OFPACT
这个C的宏可以批量生成函数与enum。。。源程序
不过大部分人写的C宏应该是这个亚子
#define max(a,b) ((a)>(b)?(a):(b))
至于Lisp的宏,那是出了名的强大,举个经典的例子
最近大热的python有一个功能叫做列表推导,语法大致如下
x = [i for i in range(10) if i % 2 == 0]
这个特性最初并未在python中实现,要想实现相同的功能还得多写一点代码,但在Lisp中,你完全可以通过宏编写自己的语法规则
(defmacro lcomp (expression for var in list conditional conditional-test)
;; create a unique variable name for the result
(let ((result (gensym)))
;; the arguments are really code so we can substitute them
;; store nil in the unique variable name generated above
`(let ((,result nil))
;; var is a variable name
;; list is the list literal we are suppose to iterate over
(loop for ,var in ,list
;; conditional is if or unless
;; conditioanl-test is (= (mod x 2) 0) in our examples
,conditional ,conditional-test
;; and this is the action from the earlier lisp example
;; result = result + [x] in python
do (setq ,result (append ,result (list ,expression))))
;; return the result
,result)))
于是你就可以在Lisp写出下面这样的列表推导式
(lcomp x for x in (range 10) if (= (mod x 2) 0))
另一个Lisp经典的宏应该就是once-only宏了
(defmacro once-only ((&rest names) &body body)
(let ((gensyms (loop for n in names collect (gensym))))
`(let (,@(loop for g in gensyms collect `(,g (gensym))))
`(let (,,@(loop for g in gensyms for n in names collect ``(,,g ,,n)))
,(let (,@(loop for n in names for g in gensyms collect `(,n ,g)))
,@body)))))
(至今未看懂QAQ
Rust中的宏本来也是十分强大的,即所谓的编译器扩展,但是这个的API好像还在设计中,还未稳定,不过仅仅是Rust的过程宏,估计也够大部分人喝一壶的了
use std::collections::HashMap;
macro_rules! hashmap {
($( $key: expr => $val: expr),*) {{
let mut map = HaskMap::new();
$( map.insert($key, $val);)*
通过这个宏,我们就可以像使用
vec!
一样的使用
hashmap!
简易的创建一个HashMap了
但是, 我大C++可是宇宙第一复杂的语言,比复杂性就没输过!,如果把C宏的究极进化版——模板算上的话(严格来讲这两个还是有很大不同的,宏替换是在预处理阶段,模板则是在编译期),那妥妥的应该是
C++\gt Lisp \gg Rust
毕竟
C + + Templates are Turing Complete
举个欺负编译器系列的栗子
template <unsigned N>
auto factorial()
if constexpr (N == 0)
return 1;
return N * factorial<N - 1>();
最后放个雷,编译器爆炸系列
template <int i> struct D { D(void*); operator int(); };
template <int p, int i> struct is_prime {
enum { prim = (p%i) && is_prime<(i > 2 ? p : 0), i -1> :: prim };
template < int i > struct Prime_print {
Prime_print<i-1> a;
enum { prim = is_prime<i, i-1>::prim };
void f() { D<i> d = prim; }
struct is_prime<0,0> { enum {prim=1}; };
struct is_prime<0,1> { enum {prim=1}; };
struct Prime_print<2> { enum {prim = 1}; void f() { D<2> d = prim; } };