システムプログラミングでは依然として主流
豊富なライブラリ、ABI
「薄い/どこにでもある」ランタイム
on the bare metalなセマンティクス
抽象化機能が貧弱
本物のマクロで抽象化限界突破
(!cppの偽マクロ)
File | LOC |
---|---|
ls.h | 38 |
extern.h | 21 |
ls.c | 526 |
cmp.c | 73 |
print.c | 319 |
util.c | 164 |
計 | 1141 |
誰もが知ってるls(1)コマンド
ソースはFreeBSDから (simple, no-dependency)
素直で読みやすいコード
C言語ソースとしては優等
しかし…
3つの問題点
cmp.c
int namecmp(const FTSENT *a, const FTSENT *b) { return (strcoll(a->fts_name, b->fts_name)); } int revnamecmp(const FTSENT *a, const FTSENT *b) { return (strcoll(b->fts_name, a->fts_name)); }
util.c
while ((clen = mbrtowc(&wc, ...)) != 0) { if (clen == (size_t)-1) { ... } else if (clen == (size_t)-2)) { ... } if (iswprint(wc)) { ... } else { ... } }
int i; : for (i = 0; i < (int)clen; i++) putchar((unsigned char)s[i]);
for (int i = [0..clen]) { putchar(...) } とか書けたらなあ
FTSENT *p; : for (p = dp->list; p; p = p->fts_link) { : }
foreach (FTSENT *p in dp->list) { ... } とか書けたらなあ
ls.h
extern int f_accesstime; /* use time of last access */
ls.c (decl)
int f_accesstime; /* use time of last access */
ls.c (main)
case 'c': f_statustime = 1; f_accesstime = 0; case 'u': f_accesstime = 1; f_statustime = 0;
リフォームのポイント:構文なんて飾りです
int main(int argc, char *argv[]) { static char dot[] = ".", *dotav[] = {dot, NULL}; struct winsize win; int ch, fts_options, notused; char *p; (void)setlocale(LC_ALL, ""); if (isatty(STDOUT_FILENO)) { termwidth = 80; if ((p = getenv("COLUMNS")) != NULL && *p != '\0') termwidth = atoi(p); else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) != -1 && win.ws_col > 0) termwidth = win.ws_col; f_nonprint = 1;
(define-cfn main (argc::int argv::char**) ::int (setlocale LC_ALL "") (cond [(isatty STDOUT_FILENO) (= termwidth 80) (let* ([p::char* (getenv "COLUMNS")] [win::(struct winsize)]) (cond [(and p (!= (* p) #\null)) (= termwidth (atoi p))] [(and (!= (ioctl STDOUT_FILENO TIOCGWINSZ (& win)) -1) (> (ref win ws_col) 0)) (= termwidth (ref win ws_col))]) (= f_nonprint 1))]
構文木置換
(define-cise-stmt (when test . body) `(if ,test (begin ,@body))) : (when (is_foo x) (do_this) (do_that)) ↓ (if (is_foo x) (begin (do_this) (do_that))) ;; if (is_foo(x)) { do_this(); do_that(); }
パターンの抽出/隠蔽
(dotimes [i (strlen s)] (printf "%02x" (aref s i))) ↓ (let* ([i::int 0] [cise__213::int (strlen s)]) (for [() (< i cise__213) (inc! i)] (printf "%02x" (aref s i)))) ;; { ;; int i = 0; int cise__213 = strlen(s); ;; for (; i < cise__213; i++) { ;; printf("%02x", s[i]); ;; } ;; }
グローバルなコンパイル時計算
(define-enum FooMode (MODE_X MODE_Y)) : (gen-printer FooMode)
↓
enum FooMode { MODE_X, MODE_Y }; : void FooMode_print(enum FooMode m) { switch (m) { case MODE_X: puts("MODE_X"); break; case MODE_Y: puts("MODE_Y"); break; } }
cmp.sc
(define-cmpfn namecmp (return (strcoll (-> a fts_name) (-> b fts_name)))) (define-cmpfn-stat modcmp st_mtime) (define-cmpfn-stat acccmp st_atime) (define-cmpfn-stat statcmp st_ctime) (define-cmpfn-stat sizecmp st_size)
FTSENT *p; : for (p = dp->list; p; p = p->fts_link) { ... }
↓
(do-ftsent [p (-> dp list)] ...)
util.sc
(define-cfn prn_normal (s::(const char*)) ::int (let* ([n::int 0]) (make-printer (putchar (cast (unsigned char) (* s))) ; ilseq (clen == -1) (+= n (printf "%s" s)) ; incomplete (clen == -2) (default-print) ; nonprintable (begin (default-print) (+= n (wcwidth wc)))) ; printable (return n)))
元のC関数: 29LOC
util.sc
(define-cfn prn_octal (s::(const char*)) ::int (let* ([len::int 0] [esc::(.array (static const char) (())) "\\\\\"\"\aa\bb\ff\nn\rr\tt\vv"]) (make-printer (octal-print 1) ; ilseq (clen == -1) (octal-print (strlen s)) ; incomplete (clen == -2) (esc-print) ; nonprint (if (and (!= wc (cast wchar_t #\")) ;print (!= wc (cast wchar_t #\\))) (begin (default-print) (+= len (wcwidth wc))) (esc-print))) (return len)))
元のC関数: 50LOC
(define-flag int f_accesstime "u" "cU") ; use time of last access (define-flag int f_statustime "c" "uU") ; use time of last mode change (define-flag int f_longform "l" "1Cxm"); long listing format (define-flag int f_nonprint "q" "Bbw") ; show unprintables as ?
Before | After | |
---|---|---|
cmp.c | 73 | 15 |
ls.c | 526 | 277 |
print.c | 319 | 183 |
util.c | 164 | 73 |
計 | 1082 | 548 |
!!注意!!
``Macro Club has two rules, plus one exception''
Stuart Halloway ("Programming Clojure")
プログラマであるあなたは
万能の神
言語に使われるのではなく
言語を使い
言語と戯れよう