你見過哪些很贊的宏定義?

rt


出自gcc源碼的stdio.h


題乾沒有限制必須是 C/C++ 唉!define-syntax 也是宏對不對?

(define-syntax
(syntax-rules ()
(( x)
(lambda (action)
(case action
((ref) x)
((set) (lambda (new-val) (set! x new-val))))))))

我在做 Youki 的時候寫過一個奇葩的宏,它會換掉某塊代碼里「直接出現的文字」的定義。可以說沒這個宏的話 typeof.net 根本寫不出來:

[define-macro [html-section $block] [cond
[$block/definingScope
[setf s [derive $block/definingScope]]
[setpart s ".lit" dothtml/dotlit] ; 轉義尖括弧和
[setpart s ".codespan" dothtml/dotcodespan] ; 給 `xxx` 加 &
[setpart s ".p" dothtml/dotp] ; 給段落包 &

元素
[setpart s ".li" dothtml/dotli]
[setpart s ".ul" dothtml/dotul]
[setpart s ".ol" dothtml/dotol]
[setpart s ".inline**" dothtml/dotstrong]
[setpart s ".inline*" dothtml/dotem]
[evaluate-in-scope s $block]
]
[true [throw "Unsupported Type"]]
]]

另外……cf. @李阿玲


關門,上Gobject!滿足你對宏的慾望!

以下代碼拷自Gobject自帶的教程。

如果你想定義一個類,名字叫bar,前綴(或名字空間)叫maman,你得首先按照「慣例」手寫這一坨宏:

#define MAMAN_TYPE_BAR (maman_bar_get_type ())
#define MAMAN_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_TYPE_BAR, MamanBar))
#define MAMAN_IS_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_TYPE_BAR))
#define MAMAN_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MAMAN_TYPE_BAR, MamanBarClass))
#define MAMAN_IS_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MAMAN_TYPE_BAR))
#define MAMAN_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MAMAN_TYPE_BAR, MamanBarClass))

typedef struct _MamanBar MamanBar;
typedef struct _MamanBarClass MamanBarClass;

每當我看這個教程的時候,我都想蹲在廁所唱「美麗滴天使啊在遠方召喚你」,不知道為什麼……


我不太喜歡宏,宏定義使得代碼難以理解,也難以調試。絕大多數的宏,在C/C++中有其它方式替代。但有時不用宏也不成。

見劉未鵬的文章,C++11(及現代C++風格)和快速迭代式開發,錯誤處理部分,介紹的ENSURE宏,就很贊。

使用很簡單

ENSURE(0 &<= index index &< v.size())(index)(v.size());

條件失敗的時候,會產生異常,並附帶信息 index 和 v.size() 中的值。

Failed: 0 &<= index index &< v.size() File: xxx.cpp Line: 123 Context Variables: index = 12345 v.size() = 100

文章並沒有給出源碼。我簡單仿寫了一個。用法跟原來的ENSURE類似。

CLOVER_ASSERT(0 &<= index index &< v.size())(index)(v.size());

我不喜歡異常,只作為debug版本調試使用。在release版本,上面語句會直接略過。這個宏相當於assert的加強版。

這個宏實現,精美之處是宏的循環遞歸展開。

#define __CLOVER_ASSERT_A(x) __CLOVER_ASSERT_OP(x, B)
#define __CLOVER_ASSERT_B(x) __CLOVER_ASSERT_OP(x, A)
#define __CLOVER_ASSERT_OP(x, next) printValue(#x, (x)).__CLOVER_ASSERT_##next

__CLOVER_ASSERT_A展開成__CLOVER_ASSERT_B,__CLOVER_ASSERT_B再次展開成__CLOVER_ASSERT_A,就可以在斷言失敗時任意列印出多個參數值。

class NullAssert
{
public:
char __CLOVER_ASSERT_A;
char __CLOVER_ASSERT_B;
};

類中有兩個變數跟宏的名字一樣,就是循環展開的結束標記。

我習慣在輔助的宏前面加上兩橫線,避免IDE的智能提示列出一堆我不關心的東西。


樓上有人提到用C的宏實現狀態機,正好我看過一篇論文《Automata via Macros》用Scheme 的宏實現狀態機。

(define-syntax process-state
(syntax-rules (accept →)
[(_accept)
(lambda (stream)
(cond
[(empty? stream) true]
[else false]))]
[(_(label → target) · · ·)
(lambda (stream)
(cond
[(empty? stream) false ]
[else
(case (first stream)
[(label) (target (rest stream))]
· · ·
[else false])]))]))

(define-syntax automaton
(syntax-rules (:)
[(_init-state
(state : response · · ·)
···)
(let-syntax
([process-state
?
(syntax-rules (accept →)
[(_accept)
(lambda (stream)
(cond
[(empty? stream) true]
[else false]))]
[(_(label → target) (··· ···))
(lambda (stream)
(cond
[(empty? stream) false]
[else
(case (first stream)
[(label) (target
(rest stream))]
(··· ···)
[else false])]))])])
(letrec ([state
(process-state
response · · ·)]
···)
init-state))]))

然後這樣寫狀態機

(define m2
(automaton init
[init : (c → more)]
[more : (a → more)
(d → more)
(r → end)]
[end : accept]))


#define if(...) if(!(__VA_ARGS__))


#define TRUE FALSE

// Happy debugging suckers


#define FOREACH(FOO)
FOO(MARCO1)
FOO(MARCO2)
FOO(MARCO3)

#define GENERATE_ENUM(ENUM) ENUM,
#define GENERATE_STRING(STRING) #STRING,

enum ENUM {
FOREACH(GENERATE_ENUM)
};

static const char * STRING[] = {
FOREACH(GENERATE_STRING)
};

用法自行腦補……


這些都是小兒科,febird.dataio中的一個宏:

#define DATA_IO_LOAD_SAVE_E(Class, Members)
struct Class##_fdio : public Class
{
template& void load(DataIO aDataIO) { aDataIO Members; }
template& void save(DataIO aDataIO) const { aDataIO Members; }
DATA_IO_GEN_DUMP_TYPE_TRAITS(Class, Members)
DATA_IO_OPTIMIZE_ELEMEN_LOAD(Class, Members)
DATA_IO_OPTIMIZE_ELEMEN_SAVE(Class, Members)
DATA_IO_OPTIMIZE_VECTOR_LOAD(Class, Members)
DATA_IO_OPTIMIZE_VECTOR_SAVE(Class, Members)
DATA_IO_OPTIMIZE_ARRAY__LOAD(Class, Members)
DATA_IO_OPTIMIZE_ARRAY__SAVE(Class, Members)
};
DATA_IO_GEN_DUMP_TYPE_TRAITS_REG(inline, Class##_fdio, Class)
DATA_IO_OPTIMIZE_ELEMEN_LOAD_REG(inline, static_cast&< Class##_fdio&>, Class)
DATA_IO_OPTIMIZE_ELEMEN_SAVE_REG(inline, static_cast&, Class)
DATA_IO_OPTIMIZE_VECTOR_LOAD_REG(inline, Class##_fdio, Class)
DATA_IO_OPTIMIZE_VECTOR_SAVE_REG(inline, Class##_fdio, Class)
DATA_IO_OPTIMIZE_ARRAY__LOAD_REG(inline, Class##_fdio, Class)
DATA_IO_OPTIMIZE_ARRAY__SAVE_REG(inline, Class##_fdio, Class)
template&
void DataIO_loadObject(DataIO dio, Class x)
{
static_cast&(x).load(dio);
}
template&
void DataIO_saveObject(DataIOdio,const Classx)
{
static_cast&(x).save(dio);
}

這個宏有什麼功能呢?當你定義了這樣的對象時,可以自動推斷出它的對象布局和序列化形式是否相同,相同時可以直接 memcpy ,或者 fread/fwrite:

struct A { int a, b, c, d; };
struct B { long x, y; };
struct C { A a; B b; };
DATA_IO_LOAD_SAVE_E(A, abcd) // 可以 memcpy
DATA_IO_LOAD_SAVE_E(B, xy) // 可以 memcpy
DATA_IO_LOAD_SAVE_E(C, ab) // 可以 memcpy

但是:

using namespace std;
struct D { set&&> d; map&&> m; };
DATA_IO_LOAD_SAVE_E(D, d) // 不能 memcpy, 必須老老實實地去逐個序列化
// 但是碰到 vector& 時,可以一次性對整個數組 fread/fwrite

性能呢,比起 boost.serialization,是幾十倍,幾百倍,甚至一千多倍的性能差異: febird.dataio vs boost.serialization 運行性能對比


覺得應該是assert的宏實現

以及foreach的C++實現

以及用宏實現的隊列和湊整

手機占坑,回家上圖


#define mian main


有個很簡單東西,不知道記錄在哪裡,寫這裡吧

#define __STR(x) #x
#define _STR(x) __STR(x)

第一個__STR(x)一般簡單的用來展開一個字元串 比如

char *a = __STR(name)

會生成

char *a = "name"

但是,他有個缺點,遇到這樣的,就不行了

#define LEN 3
char *a = __STR(LEN)

如果它會展開成

char *a = "LEN"

怎麼展開成你想要的東西呢?

char *a = _STR(LEN)

逐層展開,第一層:

char *a = __STR(3)

第二層:

char *a = "3"

所以_STR(x)可以接受宏(單層展開的宏,多層的話還是有問題)


為了避免代碼風格大戰……(親身經歷過的,可以看看我的回答)

#define function(return_type, name, arg_list)
return_type name arg_list {
#define end_function }

#include &

function(int, main, (void))
printf("Hello, world!");
return 0;
end_function

另外一個原創,專門用來測試代碼速度

#include &
#include &
#define TEST_FUNC_SPEED(f,n)
do {
clock_t _0Ts;
double _0Tt;
int _0Ti, _0Tc;
_0Ti = _0Tc = (n);
_0Ts = clock();
while (_0Ti-- &> 0) (f);
_0Tt = (clock() - _0Ts) / (double)CLOCKS_PER_SEC;
printf("%s %d times: %g second%s
", #f, _0Tc, _0Tt, _0Tt &> 1 ? "s": "");
} while (0)

#include &
int main(void) {
TEST_FUNC_SPEED(strlen("Hello, world!"), 10000000);
return 0;
}

另外&裡面的offsetof

#define offsetof(type, member) ((size_t)((type *)0)-&>member)


算不上贊,但我自己常用,特別是重複很多次的時候:

#define RETURN_IF_FAILED(b)
do{
if(!(b))
{
return false;
}
}while(0)

//demo
bool add(object *obj)
{
RETURN_IF_FAILED(obj);
//todo something
return true;
}

試想下如果不用此宏,每個函數都寫一個if得多醜。。。

………………………………………………………………

相應也有異常版:

void check_null(T t)
{
if(t==null)
throw new arg_null_exception();
}

//demo
void add(T t)
{
check_null(t);
//todo something
}


神奇的XX:

libuv, http_parser等好多開源項目都有。

比如libuv/uv.h at master · joyent/libuv · GitHub里的errno的定義方式

/* Expand this list if necessary. */
#define UV_ERRNO_MAP(XX)
XX(E2BIG, "argument list too long")
XX(EACCES, "permission denied")
XX(EADDRINUSE, "address already in use")
XX(EADDRNOTAVAIL, "address not available")
XX(EAFNOSUPPORT, "address family not supported")

這裡只取了一部分,當需要定義errno時

typedef enum {
#define XX(code, _) UV_ ## code = UV__ ## code,
UV_ERRNO_MAP(XX)
#undef XX
UV_ERRNO_MAX = UV__EOF - 1
} uv_errno_t;

當需要errno的描述時

#define UV_ERR_NAME_GEN(name, _) case UV_ ## name: return #name;
const char* uv_err_name(int err) {
switch (err) {
UV_ERRNO_MAP(UV_ERR_NAME_GEN)
default:
assert(0);
return NULL;
}
}
#undef UV_ERR_NAME_GEN

也就是說你可以根據需要自己定義其他的宏來用他,而當你需要增加一項的時候只要在UV_ERRNO_MAP里加一行就搞定了。


#define TRUE (__LINE__ % 2


Data.Lens


我實在不能理解 C++ 程序員們動不動就弄一個新的 class 以及拋異常的習慣。

異常比 macro 難調試得多好嘛。

@黃兢成 那一大堆和 Learn C the Hard Way Exercise 20: Zed"s Awesome Debug Macros 這個沒差多少好吧。

回答樓主的問題,極少有任何 C 代碼運行的次數比 Linux Kernel 還多,Linux Kernel 源碼裡面有很多乾貨:

Linux/include/linux/compiler.h

Linux/include/linux/kernel.h

比如說很有名的 container_of

/*** container_of - cast a member of a structure out to the containing structure* @ptr: the pointer to the member.* @type: the type of the container struct this is embedded in.* @member: the name of the member within the struct.**/
#define container_of(ptr, type, member) ({
const typeof( ((type *)0)-&>member ) *__mptr = (ptr);
(type *)( (char *)__mptr - offsetof(type,member) );})


#define LOVE_FOREVER


#define ARRAY_SIZE_UNSAFE(a) (sizeof(a)/sizeof(a[0]))

用於獲取數組的大小,未做安全檢查


推薦閱讀:

如何利用樹莓派學習Linux及Python?
編程好學嗎?
你們都通過閱讀開源的代碼獲得了哪些跟操作系統和語言無關的技術知識?
如何快速吃透別人的代碼?
如何在 Github 上做一個規範的開源項目?

TAG:編程 | C編程語言 | 程序 | C |