就像筆者常講的,現在軟體工程師看 code 的時間要比 coding 多的多。看 C code 時最怕的就是 code 裡面一堆 #if...#elif...#else:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | /* copy from LwIP 1.4.1 */ int lwip_sendto( int s, const void *data, size_t size, int flags, const struct sockaddr *to, socklen_t tolen) { struct lwip_sock *sock; err_t err; u16_t short_size; const struct sockaddr_in *to_in; u16_t remote_port; #if !LWIP_TCPIP_CORE_LOCKING struct netbuf buf; #endif sock = get_socket(s); if (!sock) { return -1; } if (sock->conn->type == NETCONN_TCP) { #if LWIP_TCP return lwip_send(s, data, size, flags); #else /* LWIP_TCP */ LWIP_UNUSED_ARG(flags); sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1; #endif /* LWIP_TCP */ } /* @todo: split into multiple sendto's? */ LWIP_ASSERT( "lwip_sendto: size must fit in u16_t" , size <= 0xffff); short_size = (u16_t)size; LWIP_ERROR( "lwip_sendto: invalid address" , (((to == NULL) && (tolen == 0)) || ((tolen == sizeof ( struct sockaddr_in)) && ((to->sa_family) == AF_INET) && ((((mem_ptr_t)to) % 4) == 0))), sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); to_in = ( const struct sockaddr_in *)( void *)to; #if LWIP_TCPIP_CORE_LOCKING /* Should only be consider like a sample or a simple way to experiment this option (no check of "to" field...) */ { struct pbuf* p; ip_addr_t *remote_addr; #if LWIP_NETIF_TX_SINGLE_PBUF p = pbuf_alloc(PBUF_TRANSPORT, short_size, PBUF_RAM); if (p != NULL) { #if LWIP_CHECKSUM_ON_COPY u16_t chksum = 0; if (sock->conn->type != NETCONN_RAW) { chksum = LWIP_CHKSUM_COPY(p->payload, data, short_size); } else #endif /* LWIP_CHECKSUM_ON_COPY */ MEMCPY(p->payload, data, size); #else /* LWIP_NETIF_TX_SINGLE_PBUF */ p = pbuf_alloc(PBUF_TRANSPORT, short_size, PBUF_REF); if (p != NULL) { p->payload = ( void *)data; #endif /* LWIP_NETIF_TX_SINGLE_PBUF */ if (to_in != NULL) { inet_addr_to_ipaddr_p(remote_addr, &to_in->sin_addr); remote_port = ntohs(to_in->sin_port); } else { remote_addr = &sock->conn->pcb.ip->remote_ip; #if LWIP_UDP if (NETCONNTYPE_GROUP(sock->conn->type) == NETCONN_UDP) { remote_port = sock->conn->pcb.udp->remote_port; } else #endif /* LWIP_UDP */ { remote_port = 0; } } LOCK_TCPIP_CORE(); if (netconn_type(sock->conn) == NETCONN_RAW) { #if LWIP_RAW err = sock->conn->last_err = raw_sendto(sock->conn->pcb.raw, p, remote_addr); #else /* LWIP_RAW */ err = ERR_ARG; #endif /* LWIP_RAW */ } #if LWIP_UDP && LWIP_RAW else #endif /* LWIP_UDP && LWIP_RAW */ { #if LWIP_UDP #if LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF err = sock->conn->last_err = udp_sendto_chksum(sock->conn->pcb.udp, p, remote_addr, remote_port, 1, chksum); #else /* LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF */ err = sock->conn->last_err = udp_sendto(sock->conn->pcb.udp, p, remote_addr, remote_port); #endif /* LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF */ #else /* LWIP_UDP */ err = ERR_ARG; #endif /* LWIP_UDP */ } UNLOCK_TCPIP_CORE(); pbuf_free(p); } else { err = ERR_MEM; } } #else /* LWIP_TCPIP_CORE_LOCKING */ /* initialize a buffer */ buf.p = buf.ptr = NULL; #if LWIP_CHECKSUM_ON_COPY buf.flags = 0; #endif /* LWIP_CHECKSUM_ON_COPY */ if (to) { inet_addr_to_ipaddr(&buf.addr, &to_in->sin_addr); remote_port = ntohs(to_in->sin_port); netbuf_fromport(&buf) = remote_port; } else { remote_port = 0; ip_addr_set_any(&buf.addr); netbuf_fromport(&buf) = 0; } LWIP_DEBUGF(SOCKETS_DEBUG, ( "lwip_sendto(%d, data=%p, short_size=%" U16_F ", flags=0x%x to=" , s, data, short_size, flags)); ip_addr_debug_print(SOCKETS_DEBUG, &buf.addr); LWIP_DEBUGF(SOCKETS_DEBUG, ( " port=%" U16_F "\n" , remote_port)); /* make the buffer point to the data that should be sent */ #if LWIP_NETIF_TX_SINGLE_PBUF /* Allocate a new netbuf and copy the data into it. */ if (netbuf_alloc(&buf, short_size) == NULL) { err = ERR_MEM; } else { #if LWIP_CHECKSUM_ON_COPY if (sock->conn->type != NETCONN_RAW) { u16_t chksum = LWIP_CHKSUM_COPY(buf.p->payload, data, short_size); netbuf_set_chksum(&buf, chksum); err = ERR_OK; } else #endif /* LWIP_CHECKSUM_ON_COPY */ { err = netbuf_take(&buf, data, short_size); } } #else /* LWIP_NETIF_TX_SINGLE_PBUF */ err = netbuf_ref(&buf, data, short_size); #endif /* LWIP_NETIF_TX_SINGLE_PBUF */ if (err == ERR_OK) { /* send the data */ err = netconn_send(sock->conn, &buf); } /* deallocated the buffer */ netbuf_free(&buf); #endif /* LWIP_TCPIP_CORE_LOCKING */ sock_set_errno(sock, err_to_errno(err)); return (err == ERR_OK ? short_size : -1); } |
重點是既然查得出 symbol,自然就可以自動化刪除編譯後不存在的 #if...#elif #else 區塊。
直覺上這可以請 C/C++ compiler 代勞,很不幸的,compiler 會連同 header file 等一同展開,結果本想減輕讀 code 負擔變成加重負擔...冏(如果您覺得這樣比較好用也請留言分享,感謝!)
我們需要一個輕量級小工具,可以餵給他一些 symbol 定義幫我們刪掉不需要的分支就好,其他的地方不要亂動!這就是今天要介紹的 - sunifdef。sunifdef 是 son of unifdef 的意思,unifdef 源自 FreeBSD,可見歷史久遠,不過 Google 發現居然沒有人寫中文教學,所以寫這篇跟大家分享一下。
sunifdef 參數很多,不過以下範例應該可以解決大部分問題:
1 | sunifdef.exe --file argfile --constant eval,del xyz.c |
1 2 3 4 5 6 7 | -D IP_FRAG_USES_STATIC_BUF=0 -D LWIP_NETIF_TX_SINGLE_PBUF=0 -D IP_FRAG=1 -D IP_REASSEMBLY=1 -D LWIP_TCPIP_CORE_LOCKING=0 -D LWIP_TCP=1 -D LWIP_CHECKSUM_ON_COPY=0 |
1 2 3 4 5 6 7 8 9 10 | /* #define IP_FRAG_USES_STATIC_BUF 0 #define IP_REASSEMBLY 1 */ void foo( void ) { #if (IP_FRAG_USES_STATIC_BUF==1) && (IP_REASSEMBLY==1) /* 這裡會被清空...*/ #endif } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | int lwip_sendto( int s, const void *data, size_t size, int flags, const struct sockaddr *to, socklen_t tolen) { struct lwip_sock *sock; err_t err; u16_t short_size; const struct sockaddr_in *to_in; u16_t remote_port; struct netbuf buf; sock = get_socket(s); if (!sock) { return -1; } if (sock->conn->type == NETCONN_TCP) { return lwip_send(s, data, size, flags); } /* @todo: split into multiple sendto's? */ LWIP_ASSERT( "lwip_sendto: size must fit in u16_t" , size <= 0xffff); short_size = (u16_t)size; LWIP_ERROR( "lwip_sendto: invalid address" , (((to == NULL) && (tolen == 0)) || ((tolen == sizeof ( struct sockaddr_in)) && ((to->sa_family) == AF_INET) && ((((mem_ptr_t)to) % 4) == 0))), sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); to_in = ( const struct sockaddr_in *)( void *)to; /* initialize a buffer */ buf.p = buf.ptr = NULL; if (to) { inet_addr_to_ipaddr(&buf.addr, &to_in->sin_addr); remote_port = ntohs(to_in->sin_port); netbuf_fromport(&buf) = remote_port; } else { remote_port = 0; ip_addr_set_any(&buf.addr); netbuf_fromport(&buf) = 0; } LWIP_DEBUGF(SOCKETS_DEBUG, ( "lwip_sendto(%d, data=%p, short_size=%" U16_F ", flags=0x%x to=" , s, data, short_size, flags)); ip_addr_debug_print(SOCKETS_DEBUG, &buf.addr); LWIP_DEBUGF(SOCKETS_DEBUG, ( " port=%" U16_F "\n" , remote_port)); /* make the buffer point to the data that should be sent */ err = netbuf_ref(&buf, data, short_size); if (err == ERR_OK) { /* send the data */ err = netconn_send(sock->conn, &buf); } /* deallocated the buffer */ netbuf_free(&buf); sock_set_errno(sock, err_to_errno(err)); return (err == ERR_OK ? short_size : -1); } |
補充說明
win32 sunifdef 有個地方很兩光,就是 argfile 不會去搜尋環境變數 PATH,補救方法是使用一門快要失傳的手藝 - batch file,這也是跟強者我朋友學的,他是我見過最會寫 batch file 的人。範例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | @echo off SET arg_filename=lwip.arg FOR /F "tokens=* USEBACKQ" %%F IN (`where %arg_filename%`) DO ( SET arg_filefullpath=%%F goto _break0 ) :_break0 For %%A in ("%arg_filefullpath%") do ( Set arg_folder=%%~dpA goto _break1 ) :_break1 SET infile=%1% SET tmpfile=%infile%.tmp copy %infile% %infile%.org sunifdef.exe -f %arg_folder%%arg_filename% --constant eval,del %infile% > %tmpfile% type %tmpfile% > %infile% del /F %tmpfile% |
已加書籤。感謝分享!我們部門開發的韌體,裡面就有大量的#ifdef用來隔開不同專案的特殊處理需求。這樣寫真的很亂... :(
回覆刪除要謝就謝強者我朋友吧!
刪除很多 open source 也這樣寫,習慣就好,也可以參考小弟其他幾篇文章,說不定雞尾酒療法效果更佳
https://sourceforge.net/projects/coan2/files/latest/download
回覆刪除這個好像是新版的
在 stackoverflow 看過,不過我沒用過
刪除