システムプログラミングでは依然として主流
豊富なライブラリ、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")
プログラマであるあなたは
万能の神
言語に使われるのではなく
言語を使い
言語と戯れよう