顯示具有 cpp 標籤的文章。 顯示所有文章
顯示具有 cpp 標籤的文章。 顯示所有文章

2025年2月13日 星期四

[books] C++ Move Semantics 簡體中文版

telegram 上看到 C++ Move Semantics, 竟然是簡體中文版的翻譯, 也驚訝 C++ Move Semantics 竟然有一本書的份量, 真的有人能精通 c++ 嗎?

翻譯的文件是 latex 檔案, 真是太酷了。花了一些功夫使用 xelatex 編譯起來, xelatex C++-Move-Semantics.tex, 遇到什麼找不到的東西, 就把 texlive 相關套件裝一下, 應該不難搞定。

編譯好的簡體中文 pdf 放在: C++-Move-Semantics.pdf

有嘗試改成繁體中文, 不過會遇到有些繁體字找不到字型的問題, 懶得改了。

轉 epub 檔案: pandoc C++-Move-Semantics.tex -o C++-Move-Semantics.epub

繁體中文 epub: C++-Move-Semantics.epub

epub 版本繁體中文正常顯示, 無缺字, 用 e-ink 閱讀器看比較方便, 也可以用語音聽。但聽程式碼還真的有點難。

2024年5月16日 星期四

c++20 moudles support by g++

這是一個我很想用的特性, 沒有 header files 還能編譯, 真的厲害, 不知道怎麼辦到的, c++ 的魔法越來越多了。

主要以 chatgpt 為主, 再搭配 google 找資料, 終於知道怎麼編譯 module。
descent@deb64:cpp_module$ g++ --version
g++ (Debian 13.2.0-12) 13.2.0
main.cpp
 1 import functions;
 2 
 3 #include <iostream>
 4 #include <cstdio>
 5 
 6 int main() {
 7 #if 0
 8     int x = 10;
 9     int y = 5;
10 
11     std::cout << "x + y = " << add(x, y) << std::endl;
12     std::cout << "x - y = " << subtract(x, y) << std::endl;
13     std::cout << "x + y = " << add(10.5, 1.2) << std::endl;
14     //std::cout << "x * y = " << multiply(x, y) << std::endl;
15 #endif
16     Point p1 = createPoint(3, 4);
17     Point p2 = {1, 2};
18 
19     std::cout << "p1.x = " << p1.x << ", p1.y = " << p1.y << std::endl;
20     std::cout << "p2.x = " << p2.x << ", p2.y = " << p2.y << std::endl;
21     Line line{1,2};
22     line.print();
23     printMessage();
24     print_msg();
25     return 0;
26 }
math_functions.cpp
 1 export module functions;
 2 
 3 import <iostream>;
 4 
 5 export int int_add(int a, int b) {
 6     return a + b;
 7 }
 8 
 9 #if 0
10 export int subtract(int a, int b) {
11     return a - b;
12 }
13 
14 export int multiply(int a, int b) {
15     return a * b;
16 }
17 #endif
18 
19 export template<typename T>
20 T add(T a, T b) {
21     return a + b;
22 }
23 
24 export template<typename T>
25 T subtract(T a, T b) {
26     return a - b;
27 }
28 
29 // export module math_functions;
30 
31 export struct Point 
32 {
33     int x;
34     int y;
35 };
36 
37 export Point createPoint(int x, int y) 
38 {
39     return {x, y};
40 }
41 
42 export 
43 class Line
44 {
45   public:
46     Line(int x, int y):x_(x), y_(y)
47     {
48     }
49     void print()
50     {
51       printf("x_: %d, y_: %d\n", x_, y_);
52       std::cout << "x_: " << x_ << std::endl;
53     }
54   private:
55     int x_;
56     int y_;
57 };
58 
59 
60 
61 export void printMessage() 
62 {
63   std::cout << "Hello again from math_functions.cpp by iostream" << std::endl;
64 }
65 
66 export void print_msg() 
67 {
68   std::printf("Hello again from math_functions.cpp by printf!\n");
69 }
list 1 編譯指令
g++ -std=c++23 -fmodules-ts main.cpp math_functions.cpp -o math_program -x c++-system-header iostream -x c++-system-header cstdio

編譯之後會產生 gcm.cache 目錄。另外也有可能第一次編譯有錯, 再一次編譯就正常的情形, 所以有時候我搞不清楚是我寫錯還是編譯的問題。

測試了:
  1. class
  2. function
  3. template function
  4. 使用標準程式庫
這樣大概就可以來用 module 了, 不過這樣就不能從 header files 知道有哪些 function, class, class member function 可用, 一定得寫文件了吧! 原本的 include 還是可以用。

c++20_module
 1 g++ -std=c++23 -fmodules-ts main.cpp math_functions.cpp -o math_program -x c++-system-header iostream -x c++-system-header cstdio
 2 In module imported at main.cpp:1:1:
 3 functions: error: failed to read compiled module: No such file or directory
 4 functions: note: compiled module file is ‘gcm.cache/functions.gcm’
 5 functions: note: imports must be built before being imported
 6 functions: fatal error: returning to the gate for a mechanical issue
 7 compilation terminated.
 8 In module imported at math_functions.cpp:3:1:
 9 /usr/include/c++/13/iostream: error: failed to read compiled module: No such file or directory
10 /usr/include/c++/13/iostream: note: compiled module file is ‘gcm.cache/./usr/include/c++/13/iostream.gcm’
11 /usr/include/c++/13/iostream: note: imports must be built before being imported
12 /usr/include/c++/13/iostream: fatal error: returning to the gate for a mechanical issue
13 compilation terminated.
14 make: *** [makefile:2: math_program] Error 1
15 descent@debian-vm:cpp20_module$ make clean
16 rm math_program
17 rm: cannot remove 'math_program': No such file or directory
18 make: *** [makefile:4: clean] Error 1
19 descent@debian-vm:cpp20_module$ ls
20 gcm.cache  main.cpp  makefile  math_functions.cpp
21 descent@debian-vm:cpp20_module$ rm -rf gcm.cache/
22 descent@debian-vm:cpp20_module$ make
23 g++ -std=c++23 -fmodules-ts main.cpp math_functions.cpp -o math_program -x c++-system-header iostream -x c++-system-header cstdio
24 In module imported at main.cpp:1:1:
25 functions: error: failed to read compiled module: No such file or directory
26 functions: note: compiled module file is ‘gcm.cache/functions.gcm’
27 functions: note: imports must be built before being imported
28 functions: fatal error: returning to the gate for a mechanical issue
29 compilation terminated.
30 In module imported at math_functions.cpp:3:1:
31 /usr/include/c++/13/iostream: error: failed to read compiled module: No such file or directory
32 /usr/include/c++/13/iostream: note: compiled module file is ‘gcm.cache/./usr/include/c++/13/iostream.gcm’
33 /usr/include/c++/13/iostream: note: imports must be built before being imported
34 /usr/include/c++/13/iostream: fatal error: returning to the gate for a mechanical issue
35 compilation terminated.
36 make: *** [makefile:2: math_program] Error 1
37 descent@debian-vm:cpp20_module$ make
38 g++ -std=c++23 -fmodules-ts main.cpp math_functions.cpp -o math_program -x c++-system-header iostream -x c++-system-header cstdio
39 In module imported at main.cpp:1:1:
40 functions: error: failed to read compiled module: No such file or directory
41 functions: note: compiled module file is ‘gcm.cache/functions.gcm’
42 functions: note: imports must be built before being imported
43 functions: fatal error: returning to the gate for a mechanical issue
44 compilation terminated.
45 make: *** [makefile:2: math_program] Error 1
46 descent@debian-vm:cpp20_module$ make
47 g++ -std=c++23 -fmodules-ts main.cpp math_functions.cpp -o math_program -x c++-system-header iostream -x c++-system-header cstdio
48 In module imported at main.cpp:1:1:
49 functions: error: import ‘/usr/include/c++/13/iostream’ has CRC mismatch
50 main.cpp: In substitution of ‘template<class _CharT, class _Traits> std::basic_ostream<_CharT, _Traits>& std::operator<<(basic_ostream<_CharT, _Traits>&, const _CharT*) [with _CharT = char; _Traits = std::char_traits<char>]’:
51 main.cpp:19:18:   required from here
52 functions: error: failed to read compiled module cluster 15: Bad file data
53 functions: note: compiled module file is ‘gcm.cache/functions.gcm’
54 main.cpp:19:18: fatal error: failed to load pendings for ‘std::operator<<’
55    19 |     std::cout << "p1.x = " << p1.x << ", p1.y = " << p1.y << std::endl;
56       |                  ^~~~~~~~~
57 compilation terminated.
58 make: *** [makefile:2: math_program] Error 1
59 descent@debian-vm:cpp20_module$ make
60 g++ -std=c++23 -fmodules-ts main.cpp math_functions.cpp -o math_program -x c++-system-header iostream -x c++-system-header cstdio
61 descent@debian-vm:cpp20_module$ ls
62 gcm.cache  main.cpp  makefile  math_functions.cpp  math_program
63 descent@debian-vm:cpp20_module$ ./math_program 
ref:
C++20 Modules - 讓編譯加速吧 | C++ · 傳統與革新的空間

2022年8月5日 星期五

union and std::string

這個其實是我在用 bison 和 c++ 時遇到的問題, 我不想用 char*, std::string *, 想直接用 std::string, 參考 u.cpp L7, 所以才碰到這個問題。

bison 的解法是改用輸出 c++ 版本的 parser, 紀錄在「yacc/bison 系列 (3) bison 與 c++」

u.cpp
 1 #include <cstdio>
 2 #include <string>
 3 using namespace std;
 4 
 5 union YYSTYPE
 6 {
 7   string id;
 8   int num;
 9   #if 0
10   YYSTYPE(){};
11   ~YYSTYPE(){};
12   YYSTYPE operator=(const YYSTYPE&){}
13   #endif
14 };
15 
16 YYSTYPE yylval;
17 
18 int main(int argc, char *argv[])
19 {
20 
21   return 0;
22 }


list 1. error message
g++ -std=c++17 u.cpp

1 u.cpp:16:9: error: use of deleted function ‘YYSTYPE::YYSTYPE()’
2    16 | YYSTYPE yylval;
3       |         ^~~~~~
4 u.cpp:5:7: note: ‘YYSTYPE::YYSTYPE()’ is implicitly deleted because the default definition would be ill-formed:
5     5 | union YYSTYPE


這個情境在 c++ primer the 5th 中文版「19.6 union:節省空間的類別」有提到, 有點複雜, 摘錄其部份內容。

具有類別型別的成員的 union (p848)

在早期版本的C++ 底下,union 不能有成員是定義了自己的建構器或拷貝控制成員的類別型 別。在新標準之下,這項限制鬆綁了。然而,具有的成員定義了它們自己的建構器或拷貝控 制成員的 union 使用起來會比成員是內建型別的 union 還要複雜。

當一個union 具有內建型別的成員,我們可以使用一般的指定來改變那個 union 所存放的值。 而成員不是簡單類別型別的 union 就不是這樣了,當我們將 union 的值切換至或切換自類別型別的一個成員,我們就必須建構或摧毀那個成員:當我們將 union 切換至類別型別的一個成員,我們就必須執行那個成員的型別的一個建構器;當我們切換自那個成員,就必須執行它的解構器。

當一個 union 有內建型別的成員,編譯器會合成逐個成員(memberwise)版的預設建構器 或拷貝控制成員,但對成員是定義有自己的預設建構器或一或多個拷貝控制成員的 union 來 我,就不是如此了。如果一個union 的成員的型別定義了這些成員其中之一,那麼編譯器就會把union 對應的成員合成為 deleted(§13.1.6)。

舉例來說, string 類別定義了所有的五個拷貝控制成員,以及預設建構器,如果一個 union 含有一個 string, 而且並沒有定義自己的預設建構器或其中一個拷貝控制成員, 那麼編譯器就會合成那個缺少的成員為 deleted, 如果一個類別有一個 union 成員具有一個 deleted 的拷貝控制成員,那麼該類別本身對應的拷貝控制運算也會是 deleted 的。

2022年7月30日 星期六

install c++ library man page

在 linux 上一開始寫程式就是使用 c++, man 3 printf 就可以查詢 printf 用法真的很方便, 但是 c++ 標準程式庫卻沒有 man page 可以用, 一直很想要 man std::vector 也可以看到 vector 的用法, 每次不是查網路就是要翻開我那本 c++ 標準程式庫手冊。今天心血來潮找了一下, 原來有人有做了 c++ man page, 從 cppreference 轉來的。

在 https://github.com/jeaye/stdman, 從 source code 編譯, 不會太難。
./configure 
make
sudo make install
sudo mandb
/usr/local/share/man/man3

man std::vector 就可以看到 vector 用法, 爽啊!

2022年1月13日 星期四

Thriving in a Crowded and Changing World: C++ 2006–2020 簡體中文版本

在拥挤和变化的世界中茁壮成长:C++ 2006–2020

原文版本在此: https://www.stroustrup.com/hopl20main-p5-p-bfc9cd4--final.pdf

這篇論文在介紹 C++ 2006–2020 的語言發展歷史, 和 The Design and Evolution of C++類似, 我很喜歡電腦語言的發展歷史內容, 拜簡體中文版本, 終於可以一窺其貌。

我整理成 epub, 轉成繁體中文, 修改某些術語為台灣術語, 這部份沒有改的很好, 還是會有一些是中國用語, 有電子閱讀器 e-ink 讀起來會很舒服。

https://github.com/descent/Cxx_HOPL4_zh/tree/epub/zh_tw
直接下載 cxx.epub

第 3 章介紹了 c++ 標準委員會的組織架構, 還蠻龐大的。

4.1.1 介紹了 c++11 記憶體模型

第六章 - 概念 (concept) 的說明非常精彩, 從有 template 開始, Bjarne 就有考慮到這個問題, 沒想到一直到等到 c++20 才有這個功能, 這章說明了其中的心路歷程, 經過了什麼樣的討論, 最後被退回, 再次的努力, 終於在 c++20 有了 concept, 背後的辛苦難為人知。

Bjarne 說不要區分 template function, 讓 template function 看起來就樣一般 function 一樣, 而委員會有蠻多人持反對意見, 本來我也覺得讓 template function 看起來不要像一般 function 比較好, 但好像也沒必要去區分是吧?

c++17 可以這樣寫 pair p2 ("Hi!"s,129); 好威, 就不需要寫出角括弧 pair<string:int> p2; 語法看起來簡潔很多。

cfront source code:
http://www.softwarepreservation.org/projects/c_plus_plus/
http://www.softwarepreservation.org/projects/c_plus_plus/cfront/release_e/cfront1985.zip

2020年12月10日 星期四

gcc tail call (tail recursion) optimize

fig 1. C++ 函數式編程
有一陣時間, 沒有去研究一個技術主題之後, 我又回到了 revursive 上, 這是在讀 SICP 之後才注意到的技術。

但是 revursive 實在太難, 我還沒能掌握這個思考方式。

甚至連 tail call (tail recursive) 都搞不清楚, 後來在閱讀了「C++ 函數式編程 (Functional Programming in C++: How to improve your C++ programs using functional techniques)」(fig 1) 之後, 才知道就是字面上的意思。

而只有 tail call 才能讓編譯器有機會轉成 loop, 避免使用 stack。

一般看到的文章都說編譯器會把 tail call 轉成 loop, 而不使用 stack 空間, 避免 stack 爆掉, 這是真的嗎?

要怎麼確認呢?

就像 inline function, 我要怎麼確認真的有 inline 呢?

如果編譯器沒有給出明確訊息, 似乎也只能看編譯器輸出的組合語言了。

我用了 sum.c 來測試, 這個程式就是把 arr[0] ~ arr[4] 印出來, 只是我用了 recursive function 印出, 而不是用 for loop。

這個程式符合 tail call, call 自己的那行程式是在程式的最尾端, 所以應該可以被編譯器最佳化為 loop 才是。但如果沒下什麼編譯選項的話, 是看不到這樣的結果的。

list 1. sum.c
 1 #include <stdio.h>
 2 
 3 void print(int *array, int len, int n)
 4 {
 5   if (n >= len)
 6   {
 7     return;
 8   }
 9   else
10   {
11     printf("n: %d, %d\n", n, array[n]);
12     return print(array, len, n+1);
13   }
14 }
15 
16 int main(int argc, char *argv[])
17 {
18   int arr[] = {1,2,3,4,5,};
19   print(arr, 5, 0);
20   return 0;
21 }


而和 tail recursive calls 有關的編譯選項是 -foptimize-sibling-calls。

-foptimize-sibling-calls
    Optimize sibling and tail recursive calls.

    Enabled at levels -O2, -O3, -Os.


但是很奇怪, 如果使用 -foptimize-sibling-calls 反而不會輸出 Optimize tail recursive calls 的組合語言, 需要用 -Os 才會將 tail recursive calls 轉成 loop。

gcc  -no-pie -m32 sum.c -g -Os -o sum.tc.opt
gcc  -no-pie -m32 sum.c -g -foptimize-sibling-calls -o sum.no.tc.opt


list 2 L37, 在 print() 裡頭還是會 call print()

list 2. sum.no.tc.opt.txt
 3 
 4 sum.no.tc.opt:     file format elf32-i386
 5 
 6 
 7 
 8 08049162 <print>:
 9  8049162:	55                   	push   %ebp
10  8049163:	89 e5                	mov    %esp,%ebp
11  8049165:	53                   	push   %ebx
12  8049166:	83 ec 04             	sub    $0x4,%esp
13  8049169:	e8 b4 00 00 00       	call   8049222 <__x86.get_pc_thunk.ax>
14  804916e:	05 92 2e 00 00       	add    $0x2e92,%eax
15  8049173:	8b 55 10             	mov    0x10(%ebp),%edx
16  8049176:	3b 55 0c             	cmp    0xc(%ebp),%edx
17  8049179:	7d 43                	jge    80491be <print+0x5c>
18  804917b:	8b 55 10             	mov    0x10(%ebp),%edx
19  804917e:	8d 0c 95 00 00 00 00 	lea    0x0(,%edx,4),%ecx
20  8049185:	8b 55 08             	mov    0x8(%ebp),%edx
21  8049188:	01 ca                	add    %ecx,%edx
22  804918a:	8b 12                	mov    (%edx),%edx
23  804918c:	83 ec 04             	sub    $0x4,%esp
24  804918f:	52                   	push   %edx
25  8049190:	ff 75 10             	pushl  0x10(%ebp)
26  8049193:	8d 90 08 e0 ff ff    	lea    -0x1ff8(%eax),%edx
27  8049199:	52                   	push   %edx
28  804919a:	89 c3                	mov    %eax,%ebx
29  804919c:	e8 8f fe ff ff       	call   8049030 <printf@plt>
30  80491a1:	83 c4 10             	add    $0x10,%esp
31  80491a4:	8b 45 10             	mov    0x10(%ebp),%eax
32  80491a7:	83 c0 01             	add    $0x1,%eax
33  80491aa:	83 ec 04             	sub    $0x4,%esp
34  80491ad:	50                   	push   %eax
35  80491ae:	ff 75 0c             	pushl  0xc(%ebp)
36  80491b1:	ff 75 08             	pushl  0x8(%ebp)
37  80491b4:	e8 a9 ff ff ff       	call   8049162 <print>
38  80491b9:	83 c4 10             	add    $0x10,%esp
39  80491bc:	eb 01                	jmp    80491bf <print+0x5d>
40  80491be:	90                   	nop
41  80491bf:	8b 5d fc             	mov    -0x4(%ebp),%ebx
42  80491c2:	c9                   	leave  
43  80491c3:	c3                   	ret    
44 
45 080491c4 <main>:
46  80491c4:	8d 4c 24 04          	lea    0x4(%esp),%ecx
47  80491c8:	83 e4 f0             	and    $0xfffffff0,%esp
48  80491cb:	ff 71 fc             	pushl  -0x4(%ecx)
49  80491ce:	55                   	push   %ebp
50  80491cf:	89 e5                	mov    %esp,%ebp
51  80491d1:	51                   	push   %ecx
52  80491d2:	83 ec 24             	sub    $0x24,%esp
53  80491d5:	e8 48 00 00 00       	call   8049222 <__x86.get_pc_thunk.ax>
54  80491da:	05 26 2e 00 00       	add    $0x2e26,%eax
55  80491df:	c7 45 e4 01 00 00 00 	movl   $0x1,-0x1c(%ebp)
56  80491e6:	c7 45 e8 02 00 00 00 	movl   $0x2,-0x18(%ebp)
57  80491ed:	c7 45 ec 03 00 00 00 	movl   $0x3,-0x14(%ebp)
58  80491f4:	c7 45 f0 04 00 00 00 	movl   $0x4,-0x10(%ebp)
59  80491fb:	c7 45 f4 05 00 00 00 	movl   $0x5,-0xc(%ebp)
60  8049202:	83 ec 04             	sub    $0x4,%esp
61  8049205:	6a 00                	push   $0x0
62  8049207:	6a 05                	push   $0x5
63  8049209:	8d 45 e4             	lea    -0x1c(%ebp),%eax
64  804920c:	50                   	push   %eax
65  804920d:	e8 50 ff ff ff       	call   8049162 <print>
66  8049212:	83 c4 10             	add    $0x10,%esp
67  8049215:	b8 00 00 00 00       	mov    $0x0,%eax
68  804921a:	8b 4d fc             	mov    -0x4(%ebp),%ecx
69  804921d:	c9                   	leave  
70  804921e:	8d 61 fc             	lea    -0x4(%ecx),%esp
71  8049221:	c3                   	ret    


list 3 L65, 在 print() 是用 jmp 回到前面一個點 (L55), 就是 loop 的動作。

list 3. sum.tc.opt.txt
 1 
 2 sum.tc.opt:     file format elf32-i386
 3 
 4 
 5 
 6 Disassembly of section .text:
 7 
 8 08049050 <main>:
 9  8049050:	e8 9b 01 00 00       	call   80491f0 <__x86.get_pc_thunk.ax>
10  8049055:	05 ab 2f 00 00       	add    $0x2fab,%eax
11  804905a:	8d 4c 24 04          	lea    0x4(%esp),%ecx
12  804905e:	83 e4 f0             	and    $0xfffffff0,%esp
13  8049061:	ff 71 fc             	pushl  -0x4(%ecx)
14  8049064:	55                   	push   %ebp
15  8049065:	89 e5                	mov    %esp,%ebp
16  8049067:	57                   	push   %edi
17  8049068:	56                   	push   %esi
18  8049069:	8d b0 14 e0 ff ff    	lea    -0x1fec(%eax),%esi
19  804906f:	8d 45 d4             	lea    -0x2c(%ebp),%eax
20  8049072:	51                   	push   %ecx
21  8049073:	8d 7d d4             	lea    -0x2c(%ebp),%edi
22  8049076:	b9 05 00 00 00       	mov    $0x5,%ecx
23  804907b:	83 ec 30             	sub    $0x30,%esp
24  804907e:	f3 a5                	rep movsl %ds:(%esi),%es:(%edi)
25  8049080:	6a 00                	push   $0x0
26  8049082:	6a 05                	push   $0x5
27  8049084:	50                   	push   %eax
28  8049085:	e8 28 01 00 00       	call   80491b2 <print>
29  804908a:	8d 65 f4             	lea    -0xc(%ebp),%esp
30  804908d:	31 c0                	xor    %eax,%eax
31  804908f:	59                   	pop    %ecx
32  8049090:	5e                   	pop    %esi
33  8049091:	5f                   	pop    %edi
34  8049092:	5d                   	pop    %ebp
35  8049093:	8d 61 fc             	lea    -0x4(%ecx),%esp
36  8049096:	c3                   	ret    
37  8049097:	66 90                	xchg   %ax,%ax
38  8049099:	66 90                	xchg   %ax,%ax
39  804909b:	66 90                	xchg   %ax,%ax
40  804909d:	66 90                	xchg   %ax,%ax
41  804909f:	90                   	nop
42 
43 
44 080491b2 <print>:
45  80491b2:	55                   	push   %ebp
46  80491b3:	89 e5                	mov    %esp,%ebp
47  80491b5:	57                   	push   %edi
48  80491b6:	56                   	push   %esi
49  80491b7:	53                   	push   %ebx
50  80491b8:	e8 33 ff ff ff       	call   80490f0 <__x86.get_pc_thunk.bx>
51  80491bd:	81 c3 43 2e 00 00    	add    $0x2e43,%ebx
52  80491c3:	83 ec 0c             	sub    $0xc,%esp
53  80491c6:	8b 75 10             	mov    0x10(%ebp),%esi
54  80491c9:	8d bb 08 e0 ff ff    	lea    -0x1ff8(%ebx),%edi
55  80491cf:	3b 75 0c             	cmp    0xc(%ebp),%esi
56  80491d2:	7d 14                	jge    80491e8 <print+0x36>
57  80491d4:	50                   	push   %eax
58  80491d5:	8b 45 08             	mov    0x8(%ebp),%eax
59  80491d8:	ff 34 b0             	pushl  (%eax,%esi,4)
60  80491db:	56                   	push   %esi
61  80491dc:	46                   	inc    %esi
62  80491dd:	57                   	push   %edi
63  80491de:	e8 4d fe ff ff       	call   8049030 <printf@plt>
64  80491e3:	83 c4 10             	add    $0x10,%esp
65  80491e6:	eb e7                	jmp    80491cf <print+0x1d>
66  80491e8:	8d 65 f4             	lea    -0xc(%ebp),%esp
67  80491eb:	5b                   	pop    %ebx
68  80491ec:	5e                   	pop    %esi
69  80491ed:	5f                   	pop    %edi
70  80491ee:	5d                   	pop    %ebp
71  80491ef:	c3                   	ret    


看到這樣的結果就安心了, 真的是像文章上說的一樣, 有做 tail recursive calls Optimization。

ref:

2020年1月17日 星期五

c++ vector 呼叫容器元素的解構函式

constructor 我喜歡翻譯成建構函式, 因為它本質的確是一個函式; destructor 我喜歡的翻譯是解構函式。當我寫 c 的時候, init() 我會取名 ctor(); destory() 我會取名為 dtor()。

在使用 std::vector 的時候, 會因為 vector 的容量變化而呼叫元素的解構函式, 這個負擔大不大呢?

vector 提供的 reserve 有沒有幫助?

我之前沒有細想這個問題。

v.push_back.cpp
 1 #include <vector>
 2 #include <stdio.h>
 3 #include <iostream>
 4 
 5 using namespace std;
 6 
 7 class MyClass
 8 {
 9   public:
10     MyClass()
11     {
12       index_ = num_;
13       ++num_;
14       printf("ctor index_: %d, num_: %d\n", index_, num_);
15     }
16     MyClass(const MyClass &&rhs)
17     {
18       index_ = rhs.index_;
19       printf("move ctor index_: %d, num_: %d\n", index_, num_);
20     }
21     MyClass(const MyClass &rhs)
22     {
23       index_ = rhs.index_;
24       ++num_;
25       printf("copy ctor index_: %d,num_: %d\n", index_, num_);
26     }
27     ~MyClass()
28     {
29       --num_;
30       printf("dtor, index_: %d, num_: %d\n", index_, num_);
31     }
32     static int num_;
33     int index() const {return index_;}
34   private:
35     int  index_;
36 };
37 
38 int MyClass::num_=0;
39 
40 int main(int argc, char *argv[])
41 {
42   {
43     vector<MyClass> my_class;
44     //my_class.reserve(100);
45     cout << "capacity(): " << my_class.capacity() << endl;
46     MyClass c1,c2;
47     cout << "push c1" << endl;
48     my_class.push_back(c1);
49 
50     cout << "my_class[0].index(): " << my_class[0].index() << endl;
51 
52     cout << "c1 capacity(): " << my_class.capacity() << endl;
53 
54     cout << "push c2" << endl;
55     my_class.push_back(c2);
56 
57     cout << "my_class[1].index(): " << my_class[1].index() << endl;
58 
67   }
68   printf("end\n");
69   return 0;
70 }

由於我沒有執行 reserve, capacity() 一開始是 0, push c1 之後, vector 先配置 1 個元素的記憶體空間, copy ctor 發動, 複製 c1 到 vector[0], 目前總共有 c1, c2, vector[0] 總共 3 個 MyClass, capacity() 為 1。

push c2 之後, capacity() 空間不夠, 先配置 2 個元素的記憶體空間, 呼叫 copy ctor 複製 c2 以及在原本的 vector[0], 這時候共有 5 個 MyClass。

再來發動「原本的 vector[0]」 dtor, 現在的 MyClass 變為 4 個, 原本的有 1 個元素的記憶體空間被歸還。

在 vector 解構後, 會執行 4 次 MyClass dtor。

result 1. 總開銷
ctor: 2
copy ctor: 3
dtor: 5

嚴格來說最開始的 ctor:2, 和最後的 dtor:2 不能算在 vector 頭上, 重新計算之後。

result 2. 總開銷
ctor: 0
copy ctor: 3
dtor: 3

以 result 1 來看, 老實說我有點驚訝這個頻繁的次數, 以 result 2 結果來看就還好。

但如果用 v-1.push_back.cpp 的寫法, 因為 push_back 無法寫 my_class.push_back(); 得寫成 my_class.push_back(MyClass{});, 這寫法所有的開銷就得算在 vector 頭上了。

總開銷:
ctor: 2
copy ctor: 3
dtor: 5

和 result 1 一樣。

v-1.push_back.cpp
 1 #include <vector>
 2 #include <stdio.h>
 3 #include <iostream>
 4 
 5 using namespace std;
 6 
 7 class MyClass
 8 {
 9   public:
10     MyClass()
11     {
12       index_ = num_;
13       ++num_;
14       printf("ctor index_: %d, num_: %d\n", index_, num_);
15     }
23     MyClass(const MyClass &rhs)
24     {
25       index_ = rhs.index_;
26       ++num_;
27       printf("copy ctor index_: %d, num_: %d\n", index_, num_);
28     }
29     ~MyClass()
30     {
31       --num_;
32       printf("dtor, index_: %d, num_: %d\n", index_, num_);
33     }
34     static int num_;
35     int index() const {return index_;}
36     int  index_;
37   private:
38 };
39 
40 int MyClass::num_=0;
41 
42 int main(int argc, char *argv[])
43 {
44   {
45     vector<MyClass> my_class;
46     //my_class.reserve(100);
47     cout << "capacity(): " << my_class.capacity() << endl;
48     //MyClass c1,c2,c3;
49     cout << "push c1" << endl;
50     my_class.push_back(MyClass{});
51 
52     cout << "my_class[0].index(): " << my_class[0].index() << endl;
53 
54     my_class[0].index_ = 99;
55 
56     cout << "xx my_class[0].index(): " << my_class[0].index() << endl;
57 
58     cout << "c1 capacity(): " << my_class.capacity() << endl;
59 
60     cout << "push c2" << endl;
61     my_class.push_back(MyClass{});
62 
63     cout << "my_class[1].index(): " << my_class[1].index() << endl;
73   }
74   printf("end\n");
75   return 0;
76 }

list 3. v-1 執行結果
 1 capacity(): 0
 2 push c1
 3 ctor index_: 0, num_: 1
 4 copy ctor index_: 0, num_: 2
 5 dtor, index_: 0, num_: 1
 6 my_class[0].index(): 0
 7 xx my_class[0].index(): 99
 8 c1 capacity(): 1
 9 push c2
10 ctor index_: 1, num_: 2
11 copy ctor index_: 1, num_: 3
12 copy ctor index_: 99, num_: 4
13 dtor, index_: 99, num_: 3
14 dtor, index_: 1, num_: 2
15 my_class[1].index(): 1
16 dtor, index_: 99, num_: 1
17 dtor, index_: 1, num_: 0
18 end

list 1 push_back 執行結果
 1 capacity(): 0
 2 ctor index_: 0, num_: 1
 3 ctor index_: 1, num_: 2
 5 push c1
 6 copy ctor index_: 0,num_: 3
 7 my_class[0].index(): 0
 8 c1 capacity(): 1
 9 push c2
10 copy ctor index_: 1,num_: 4
11 copy ctor index_: 0,num_: 5
12 dtor, index_: 0, num_: 4
13 my_class[1].index(): 1
15 dtor, index_: 1, num_: 3
16 dtor, index_: 0, num_: 2
17 dtor, index_: 0, num_: 1
18 dtor, index_: 1, num_: 0
19 end

如果不想付出這麼頻繁的代價, 可以使用 vector<MyClass*> my_class; 指標的版本, 或是使用 reserve, reserve 可以減少發動 ctor, dtor 的次數。

c++11 之後有了 emplace_back(), 來看看這個新東西所帶來的效率改善。

直接寫 my_class.emplace_back() 就可在 vector[0] 插入一個 MyClass 物件。

push c1 時, 發動一次 ctor, push c2 時, c2 發動一次 ctor, 原本的 vector[0] 發動一次 copy ctor, 這時候總共有 3 個 MyClass, 之後 vector[0] 發動一次 dtor, 現在的 MyClass 總數為 2 個。

當 vector 解構之後, 發動 2 次 dtor。

總開銷:
ctor: 2
copy ctor : 1
dtor: 3

和 result 2. 的開銷一樣, 不過使用 emplace_back() 可是貨真價實的省下 2 個 ctor, 這使用了 variadic template 的技術, 我總算找到使用 variadic template 的原因了。

v.emplace_back.cpp
 1 #include <vector>
 2 #include <stdio.h>
 3 #include <iostream>
 4 
 5 using namespace std;
 6 
 7 class MyClass
 8 {
 9   public:
10     MyClass()
11     {
12       index_ = num_;
13       ++num_;
14       printf("ctor index_: %d, num_: %d\n", index_, num_);
15     }
16     MyClass(const MyClass &&rhs)
17     {
18       index_ = rhs.index_;
19       printf("move ctor index_: %d, num_: %d\n", index_, num_);
20     }
21     MyClass(const MyClass &rhs)
22     {
23       index_ = rhs.index_;
24       ++num_;
25       printf("copy ctor index_: %d,num_: %d\n", index_, num_);
26     }
27     ~MyClass()
28     {
29       --num_;
30       printf("dtor, index_: %d, num_: %d\n", index_, num_);
31     }
32     static int num_;
33     int index() const {return index_;}
34   private:
35     int  index_;
36 };
37 
38 int MyClass::num_=0;
39 
40 int main(int argc, char *argv[])
41 {
42   {
43     vector<MyClass> my_class;
45     cout << "capacity(): " << my_class.capacity() << endl;
48     cout << "push c1" << endl;
51     my_class.emplace_back();
52 
53     cout << "my_class[0].index(): " << my_class[0].index() << endl;
54 
55     cout << "c1 capacity(): " << my_class.capacity() << endl;
56 
57     cout << "push c2" << endl;
59     my_class.emplace_back();
60 
61     cout << "my_class[1].index(): " << my_class[1].index() << endl;
62 
71   }
72   printf("end\n");
73   return 0;
74 }

編譯: g++ -std=c++2a v.emplace_back.cpp -o v.emplace_back

list 2. emplace_back
 1 capacity(): 0
 2 push c1
 3 ctor index_: 0, num_: 1
 4 my_class[0].index(): 0
 5 c1 capacity(): 1
 6 push c2
 7 ctor index_: 1, num_: 2
 8 copy ctor index_: 0,num_: 3
 9 dtor, index_: 0, num_: 2
10 my_class[1].index(): 1
11 dtor, index_: 0, num_: 1
12 dtor, index_: 1, num_: 0
13 end

emplace_back:

2019年12月20日 星期五

c++ inline function 裡頭 static object 會有幾份?

inline function 在 c (c99 之後), c++ 都可以使用, 只要在 function 前面加上 inline 即可。

C11 standard (ISO/IEC 9899:2011):
6.7.4 Function specifiers (p: 125-127)
C99 standard (ISO/IEC 9899:1999):
6.7.4 Function specifiers (p: 112-113)

而 c, c++ 的 inline 有點不同,

list 1. no_static_inline.err
1 /usr/bin/x86_64-linux-gnu-ld: /tmp/ccqLHrwj.o: in function `main':
2 x.c:(.text+0x15): undefined reference to `f1'
3 /usr/bin/x86_64-linux-gnu-ld: /tmp/cctzg49W.o: in function `call_f1':
4 y.c:(.text+0xa): undefined reference to `f1'
5 collect2: error: ld returned 1 exit status

c 的 inline 如果定義在 .h, 一定要加上 static, 否則會有 list 1 的錯誤。
c++ 的 inline 不需要加上 static, 但如果加上 static 也是可以, 但是加上了 static inline 的 c++ inline function, 對於 inline function 裡頭的 static ojbect, 會有不一樣的行為。

參考: static variables in an inlined function

沒有 static 的 inline function, function 中的 static object 只會有一個, 但是加上了 static 的 inline function, inline function 被包含了幾次, 就會有幾個 static object。

source code:
https://github.com/descent/progs/tree/master/inline_function_with_static_obj

2019年5月24日 星期五

implement itoa()

itoa() 並不是 c 標準, 有些平台有支援, 但 gnu c 並沒有支援這個。

而在 bare-metal 環境會很需要把整數轉成字串, 搭配 print 之類的函式來除錯; itoa 實作並不難, 這裡有個範例可以抄。

http://www.retro11.de/ouxr/43bsd/usr/ingres/source/gutil/itoa.c.html

不過有個討厭的數學問題:
1 *j-- = i % 10 + '0';
2 i /= 10;

i%10 如果 i 是負數會怎麼樣, 以下的 c++ 程式碼會印出什麼?

cout << (-8) % 16 << endl;

在我的平台是 -8, 但他不是一個固定答案, 根據不同語言的實作有不同的結果。而如果是 -8 的話, 會造成取到錯誤的 array index, 程式不至於會當掉, 但會得到錯誤結果。

ref:
负数究竟是如何取模的?

很久之前就知道這問題, 不過當時這並不是太重要的問題, 我一直沒理他, 現在學習告個段落, 該是好好正視這問題。

所以改善的作法是不要用負數取餘數, 改用正整數來取餘數。

目標是這樣:
我希望傳入 -1 時, 可以得到 -1 的字串; 傳入 0x80000000 可以得到 80000000 的字串。

itoa test
183   //int v = 0x80000000;
184   int v = -1;
185   char str[32];
186   itoa_32(v, str, 16);
187   printf("str: %s\n", str);
188
189   itoa_32(v, str, 10);
190   printf("str: %s\n", str);
191
192   return 0;

數字: -1
str: FFFFFFFF (16 進位)
str: -1       (10 進位)

數字: 0x80000000
str: 80000000    (16 進位)
str: -2147483648 (10 進位)

以下是最後修改的版本:
itoa.c
 2 char* itoa_32(int n, char* str, int radix)
 3 {
 4   char digit[]="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
 5   char* p=str;
 6   char* head=str;
 7   unsigned int val;

12   if (n==0)
13   {
14     *p++='0';
15     *p=0;
16     return str;
17   }
18   if (radix == 10 && n < 0)
19   {
20     val = (-n);
21   }
22   else
23     val = n;
24 
25   while(val)
26   {
27     *p++=digit[val%radix];
28     val/=radix;
29   }
30 
31   if (radix == 10 && n < 0)
32     *p++='-';
33 
34   *p=0;
36   for (--p; head < p ; ++head, --p)
37   {
38     char temp=*head;
39     *head=*p;
40     *p=temp;
41   }
43   return str;
44 }
45 

itoa.c L27 一律用 val 這個 unsigned int 來取餘數。

這個 itoa 還有其他問題, 例如沒有檢查 str 的大小, 是有可能超過邊界的, 不過由於知道 int 最大是 11 個位數 (包涵負號), 把 str array 設定大一點, 勉強可以避開這問題。

2018年4月27日 星期五

Multi-dimensional Arrays in C - c 的多維陣列

寫俄羅斯方塊時, 搞反了 2 維陣列的維度, 逼自己好好反省一下。

m_array.c
 1 #include <stdio.h>
 2 
 3 int main(int argc, char *argv[])
 4 {
 4.5   // int daytab[3][13] 每 row 有 13 個元素
 5   unsigned char a[5][4][6][8];
 6 
 7   printf("a: %p\n", a);
 8   printf("&a[3][3][2][7]: %p\n", &a[3][3][2][7]);
 9   printf("offset: %d\n", (&a[3][3][2][7]) - (&a[0][0][0][0]));
10   return 0;
11 }

a: 0x7fff98a74220
&a[3][3][2][7]: 0x7fff98a74507
offset: 743


m_array.c L4.5 宣告了 2 維陣列, 誰是 3, 誰是 13 呢? 老是搞不清楚, 以 row, column 來說, row 是 13, column 是 3。

4 維空間, 厄 ... 是 4 維陣列無法用 row, column 來說明, 所以遇到 unsigned char a[5][4][6][8] 該怎麼想呢? 長的像以下的圖, 下圖只到 [4][6][8], 有 5 個以下的圖, 就是 [5][4][6][8] 了。

a[3][2][5][7] 的位址該怎麼計算呢?

a
[3] - a3
[2] - b2
[5] - c5
[7] - d7

在 d7 的那個紅色位置。

1


1 2 3 4 5 6 7 8

2







3







4







5







6







2

1







2







3







4







5 , 1 2 3 4 5 6 7 8

6







3























































4
























































2 [4][6][8]

a3
1


1 2 3 4 5 6 7 8

2







3







4







5







6







b2

1







2







3







4







c5 , 1 2 3 4 5 6 d7 8

6







3























































4
























































4 [4][6][8]

5 [4][6][8]

2018年4月12日 星期四

c++ virtual function 的實作 by cfront

c++ virtual function 被很多人拿來研究, 由於沒有 cfront, 大部分的人都是從反組譯來觀察, 太苦了。

目前我已經把 cfront 建構出來, 可以使用 cfront 來看看 virtual function 轉出的 c code 長什麼樣, 破解 c++ virtual function。

hello_a.C
 1 #include <stream.h>
 2 
 3 class A
 4 {
 5   public:
 6     virtual void foo(int a = 0)
 7     {
 8       printf("A %d\n", a);
 9     }
10     virtual void va(int a)
11     {
12       printf("va: A %d\n", a);
13     }
14 };
15 
16 class B : public A
17 {
18   public:
19     virtual void foo(int a = 1)
20     {
21       printf("B a: %d\n", a);
22     }
23 };
24 
25 
26 main()
27 {
28   A *p = new A();
29   p->foo();
30   p->va(25);
31 }

hello.a.c 是 cfont 轉出來的 c code, 31 行的 c++ 程式碼透過 cfront 轉出 773 行 c code, 先來看看 class A, class B 被轉成什麼? hello.a.c L633, L643 就是對應的 class A, class B, 被轉成 strcut:
633 struct A { /* sizeof A == 8 */
636   struct __mptr *__vptr__1A ;
637 };
639 
643 struct B { /* sizeof B == 8 */
646   struct __mptr *__vptr__1A ;
647 };
裡頭有一個 __vptr__1A:
646 struct __mptr *__vptr__1A ;
struct __mptr 定義在 L17。
17 struct __mptr {short d; short i; __vptp f; };
注意其中的 __vptp f 即可, 這個就是用來儲存 virtual function, 其實 virtual function 就是一般的 c function, 所以也只是把 virtual function 的位址存起來。

再來是 L766 的 __ptbl_vec__hello_C_, 這邊建構了__vtbl__1A__hello_C
766 struct __mptr* __ptbl_vec__hello_C_[] = {
767 __vtbl__1A__hello_C,
768 
769 };
__vtbl__1A__hello_C 在 L699 定義:
699 struct __mptr __vtbl__1A__hello_C[] = {0,0,0,
700 0,0,(__vptp)foo__1AFi ,
701 0,0,(__vptp)va__1AFi ,
702 0,0,0};
__vtbl__1A__hello_C[0]: 0,0,0
__vtbl__1A__hello_C[1]: 0,0, foo__1AFi 就是 class A 的 virtual void foo(int a = 0)
__vtbl__1A__hello_C[2]: 0,0, va__1AFi 就是 class A virtual void va(int a)
A *p = new A(); 會用 new (__nw__FUl 就是 new) 建構 struct A, 把 p->__vptr__1A = __ptbl_vec__hello_C_[0]
對應到 hello.a.c L668
668 __1p = ( (__0__X52 = 0 ), ( ((__0__X52 || (__0__X52 = (struct A *)__nw__FUl ( (unsigned long )(sizeof (struct A))) ))?(__0__X52 ->
669 #line 28 "hello.C"
670 __vptr__1A = (struct __mptr *) __ptbl_vec__hello_C_[0]):0 ), __0__X52 ) ) ;
p->foo(); 就是執行 p->__vptr__1A[1].f

對應到 hello.a.c L671
671 ((*(((void (*)(struct A *__0this , int __2a ))(__1p -> __vptr__1A [1]).f))))( ((struct A *)((((char *)__1p ))+ (__1p -> __vptr__1A [1]).d)), 0 ) ;
p->va(25); 就是執行 p->__vptr__1A[2].f
對應到 hello.a.c L672
672 ((*(((void (*)(struct A *__0this , int __2a ))(__1p -> __vptr__1A [2]).f))))( ((struct A *)((((char *)__1p ))+ (__1p -> __vptr__1A [2]).d)), 25 ) ;
這些很恐怖的轉型/設定程式碼就是在做這些事情 (花點時間應該可以看懂), 也就是為什麼 class B 可以呼叫 class A 的 virtaul function va 的祕密。

hello.a.c
  1 #line 1 "hello.C"
  2 
  3 /* <<AT&T C++ Language System <3.0.3> 05/05/94>> */
  4 char __cfront_version_303_xxxxxxxx;
  5 /* < hello.C > */
  6 
  7 #pragma lib "ape/libap.a"
  8 
  9 #pragma lib "c++/libC.a"
 10 
 11 #line 1 "hello.C"
 12 void *__vec_new (void *, int , int , void *);
 13 
 14 #line 1 "hello.C"
 15 void __vec_delete (void *, int , int , void *, int , int );
 16 typedef int (*__vptp)(void);
 17 struct __mptr {short d; short i; __vptp f; };
 18 
 19 #line 1 "hello.C"
 20 extern struct __mptr* __ptbl_vec__hello_C_[];
 21 
632 #line 4 "hello.C"
633 struct A { /* sizeof A == 8 */
634 
635 #line 14 "hello.C"
636 struct __mptr *__vptr__1A ;
637 };
638 struct B;
639 
640 #line 14 "hello.C"
641 
642 #line 17 "hello.C"
643 struct B { /* sizeof B == 8 */
644 
645 #line 14 "hello.C"
646 struct __mptr *__vptr__1A ;
647 };
648 
649 #line 14 "hello.C"
650 
651 #line 6 "hello.C"
652 static void foo__1AFi (struct A *__0this , int __2a );
653 
654 #line 10 "hello.C"
655 static void va__1AFi (struct A *__0this , int __2a );
656 
657 #line 26 "hello.C"
658 int main (void ){ _main(); 
659 #line 27 "hello.C"
660 { 
661 #line 28 "hello.C"
662 struct A *__1p ;
663 
664 #line 29 "hello.C"
665 struct A *__0__X52 ;
666 
667 #line 28 "hello.C"
668 __1p = ( (__0__X52 = 0 ), ( ((__0__X52 || (__0__X52 = (struct A *)__nw__FUl ( (unsigned long )(sizeof (struct A))) ))?(__0__X52 ->
669 #line 28 "hello.C"
670 __vptr__1A = (struct __mptr *) __ptbl_vec__hello_C_[0]):0 ), __0__X52 ) ) ;
671 ((*(((void (*)(struct A *__0this , int __2a ))(__1p -> __vptr__1A [1]).f))))( ((struct A *)((((char *)__1p ))+ (__1p -> __vptr__1A [1]).d)), 0 ) ;
672 ((*(((void (*)(struct A *__0this , int __2a ))(__1p -> __vptr__1A [2]).f))))( ((struct A *)((((char *)__1p ))+ (__1p -> __vptr__1A [2]).d)), 25 ) ;
673 }
674 } 
675 #line 31 "hello.C"
676 void __sti__hello_C_main_ (void )
677 #line 664 "incl-master/incl-linux32/iostream.h"
678 { __ct__13Iostream_initFv ( & iostream_init ) ;
679 
680 #line 664 "incl-master/incl-linux32/iostream.h"
681 }
682 
683 #line 31 "hello.C"
684 void __std__hello_C_main_ (void )
685 #line 664 "incl-master/incl-linux32/iostream.h"
686 { __dt__13Iostream_initFv ( & iostream_init , 2) ;
687 
688 #line 664 "incl-master/incl-linux32/iostream.h"
689 }
690 static void foo__1BFi (
691 #line 19 "hello.C"
692 struct B *__0this , 
693 #line 19 "hello.C"
694 int __2a );
695 struct __mptr __vtbl__1B__hello_C[] = {0,0,0,
696 0,0,(__vptp)foo__1BFi ,
697 0,0,(__vptp)va__1AFi ,
698 0,0,0};
699 struct __mptr __vtbl__1A__hello_C[] = {0,0,0,
700 0,0,(__vptp)foo__1AFi ,
701 0,0,(__vptp)va__1AFi ,
702 0,0,0};
703 static void foo__1BFi (struct B *__0this , 
704 #line 19 "hello.C"
705 int __2a )
706 #line 20 "hello.C"
707 { 
708 #line 21 "hello.C"
709 printf ( (const char *)"B a: %d\n",
710 #line 21 "hello.C"
711 __2a ) ;
712 }
713 
714 #line 10 "hello.C"
715 static void va__1AFi (struct A *__0this , 
716 #line 10 "hello.C"
717 int __2a )
718 #line 11 "hello.C"
719 { 
720 #line 12 "hello.C"
721 printf ( (const char *)"va: A %d\n",
722 #line 12 "hello.C"
723 __2a ) ;
724 }
725 
726 #line 6 "hello.C"
727 static void foo__1AFi (struct A *__0this , 
728 #line 6 "hello.C"
729 int __2a )
730 #line 7 "hello.C"
731 { 
732 #line 8 "hello.C"
733 printf ( (const char *)"A %d\n",
734 #line 8 "hello.C"
735 __2a ) ;
736 }
766 struct __mptr* __ptbl_vec__hello_C_[] = {
767 __vtbl__1A__hello_C,
768 
769 };
770 
771 #line 31 "hello.C"
772 
773 /* the end */

hello_b.C 和 hello_a.C 不同之處在於 new B(), 對於整個轉出來的程式當中, 只有一點點的不同:

hello_b.C
 1 #include <stream.h>
 2 
 3 class A
 4 {
 5   public:
 6     virtual void foo(int a = 0)
 7     {
 8       printf("A %d\n", a);
 9     }
10     virtual void va(int a)
11     {
12       printf("va: A %d\n", a);
13     }
14 };
15 
16 class B : public A
17 {
18   public:
19     virtual void foo(int a = 1)
20     {
21       printf("B a: %d\n", a);
22     }
23 };
24 
25 
26 main()
27 {
28   A *p = new B();
29   p->foo();
30   p->va(25);
31 }

hello.b.c 733

773 struct __mptr* __ptbl_vec__hello_C_[] = {
774 __vtbl__1A__hello_C,
775 __vtbl__1B__hello_C,
776 
777 };

多建構了一個 __vtbl__1B__hello_C
695 struct __mptr __vtbl__1B__hello_C[] = {0,0,0,
696 0,0,(__vptp)foo__1BFi ,
697 0,0,(__vptp)va__1AFi ,
698 0,0,0};

__vtbl__1B__hello_C[0]: 0,0,0
__vtbl__1B__hello_C[1]: 0,0, foo__1AFi 就是 class B 的 virtual void foo(int a = 1)
__vtbl__1B__hello_C[2]: 0,0, va__1AFi 就是 class A 的 virtual void va(int a)

A *p = new B(); 會用 new 建構 struct B, 把 p->__vptr__1A = __ptbl_vec__hello_C_[1]
對應到 hello.b.c L671

671 __1p = (struct A *)( (__0__X52 = 0 ), ( ((__0__X52 || (__0__X52 = (struct B *)__nw__FUl ( (unsigned long )(sizeof (struct B)))
672 #line 28 "hello.C"
673 ))?( (__0__X52 = (struct B *)( (__0__X51 = (((struct A *)__0__X52 ))), ( ((__0__X51 || (__0__X51 = (struct A *)__nw__FUl ( (unsigned long
674 #line 28 "hello.C"
675 )(sizeof (struct A))) ))?(__0__X51 -> __vptr__1A = (struct __mptr *) __ptbl_vec__hello_C_[0]):0 ), __0__X51 ) ) ), (__0__X52 -> __vptr__1A = (struct __mptr *) __ptbl_vec__hello_C_[1])) :0 ),
676 #line 28 "hello.C"
677 __0__X52 ) ) ;
p->foo(); 對應到 hello.b.c L678
678 ((*(((void (*)(struct A *__0this , int __2a ))(__1p -> __vptr__1A [1]).f))))( ((struct A *)((((char *)__1p ))+ (__1p -> __vptr__1A [1]).d)), 0 ) ;
p->va(25); 對應到 hello.b.c L679
679 ((*(((void (*)(struct A *__0this , int __2a ))(__1p -> __vptr__1A [2]).f))))( ((struct A *)((((char *)__1p ))+ (__1p -> __vptr__1A [2]).d)), 25 ) ;
這些很恐怖的轉型/設定程式碼就是在做這些事情, 而呼叫 virtual function 的程式碼是相同的, 只有在設定 __vptr__1A 有所不同而已。 看懂這些資料結構之後, virtaul function 就沒有那麼神秘了。 至於
17 struct __mptr {short d; short i; __vptp f; };
神秘的 d, i, 應該是用在繼承上的, 可能是其他的繼承方式, 有興趣的朋友可以繼續追蹤下去。
hello.b.c
632 #line 4 "hello.C"
633 struct A { /* sizeof A == 8 */
634 
635 #line 14 "hello.C"
636 struct __mptr *__vptr__1A ;
637 };
638 struct B;
639 
640 #line 14 "hello.C"
641 
642 #line 17 "hello.C"
643 struct B { /* sizeof B == 8 */
644 
645 #line 14 "hello.C"
646 struct __mptr *__vptr__1A ;
647 };
648 
649 #line 23 "hello.C"
650 
651 #line 6 "hello.C"
652 static void foo__1AFi (struct A *__0this , int __2a );
653 
654 #line 10 "hello.C"
655 static void va__1AFi (struct A *__0this , int __2a );
656 
657 #line 26 "hello.C"
658 int main (void ){ _main(); 
659 #line 27 "hello.C"
660 { 
661 #line 28 "hello.C"
662 struct A *__1p ;
663 
664 #line 29 "hello.C"
665 struct B *__0__X52 ;
666 
667 #line 29 "hello.C"
668 struct A *__0__X51 ;
669 
670 #line 28 "hello.C"
671 __1p = (struct A *)( (__0__X52 = 0 ), ( ((__0__X52 || (__0__X52 = (struct B *)__nw__FUl ( (unsigned long )(sizeof (struct B)))
672 #line 28 "hello.C"
673 ))?( (__0__X52 = (struct B *)( (__0__X51 = (((struct A *)__0__X52 ))), ( ((__0__X51 || (__0__X51 = (struct A *)__nw__FUl ( (unsigned long
674 #line 28 "hello.C"
675 )(sizeof (struct A))) ))?(__0__X51 -> __vptr__1A = (struct __mptr *) __ptbl_vec__hello_C_[0]):0 ), __0__X51 ) ) ), (__0__X52 -> __vptr__1A = (struct __mptr *) __ptbl_vec__hello_C_[1])) :0 ),
676 #line 28 "hello.C"
677 __0__X52 ) ) ;
678 ((*(((void (*)(struct A *__0this , int __2a ))(__1p -> __vptr__1A [1]).f))))( ((struct A *)((((char *)__1p ))+ (__1p -> __vptr__1A [1]).d)), 0 ) ;
679 ((*(((void (*)(struct A *__0this , int __2a ))(__1p -> __vptr__1A [2]).f))))( ((struct A *)((((char *)__1p ))+ (__1p -> __vptr__1A [2]).d)), 25 ) ;
680 }
681 } 
682 #line 31 "hello.C"
683 void __sti__hello_C_main_ (void )
684 #line 664 "incl-master/incl-linux32/iostream.h"
685 { __ct__13Iostream_initFv ( & iostream_init ) ;
686 
687 #line 664 "incl-master/incl-linux32/iostream.h"
688 }
689 
690 #line 31 "hello.C"
691 void __std__hello_C_main_ (void )
692 #line 664 "incl-master/incl-linux32/iostream.h"
693 { __dt__13Iostream_initFv ( & iostream_init , 2) ;
694 
695 #line 664 "incl-master/incl-linux32/iostream.h"
696 }
697 static void foo__1BFi (
698 #line 19 "hello.C"
699 struct B *__0this , 
700 #line 19 "hello.C"
701 int __2a );
702 struct __mptr __vtbl__1B__hello_C[] = {0,0,0,
703 0,0,(__vptp)foo__1BFi ,
704 0,0,(__vptp)va__1AFi ,
705 0,0,0};
706 struct __mptr __vtbl__1A__hello_C[] = {0,0,0,
707 0,0,(__vptp)foo__1AFi ,
708 0,0,(__vptp)va__1AFi ,
709 0,0,0};
710 static void foo__1BFi (struct B *__0this , 
711 #line 19 "hello.C"
712 int __2a )
713 #line 20 "hello.C"
714 { 
715 #line 21 "hello.C"
716 printf ( (const char *)"B a: %d\n",
717 #line 21 "hello.C"
718 __2a ) ;
719 }
720 
721 #line 10 "hello.C"
722 static void va__1AFi (struct A *__0this , 
723 #line 10 "hello.C"
724 int __2a )
725 #line 11 "hello.C"
726 { 
727 #line 12 "hello.C"
728 printf ( (const char *)"va: A %d\n",
729 #line 12 "hello.C"
730 __2a ) ;
731 }
732 
733 #line 6 "hello.C"
734 static void foo__1AFi (struct A *__0this , 
735 #line 6 "hello.C"
736 int __2a )
737 #line 7 "hello.C"
738 { 
739 #line 8 "hello.C"
740 printf ( (const char *)"A %d\n",
741 #line 8 "hello.C"
742 __2a ) ;
743 }
744 
773 struct __mptr* __ptbl_vec__hello_C_[] = {
774 __vtbl__1A__hello_C,
775 __vtbl__1B__hello_C,
776 
777 };
778 
779 #line 31 "hello.C"
780 
781 /* the end */

2018年3月29日 星期四

[懷舊] 向 cfront 致敬, 使用 cfront 來編譯 c++ 程式

華麗的 c++ 與華麗的娃衣
cfront 是幹麻的? c++ 程式員應該不陌生, 他是把 c++ 程式轉成 c, 再用 c 編譯器編譯出最後執行檔的轉譯程式。知道的 c++ 開發者應該很多, 但看過、甚至執行過 cfront 的人應該就很少了。

我一直都在找 cfront 的 source code, 希望可以編譯/執行起來看看, 這樣可以很清楚的知道, virtual function, 繼承的 class, template 到底是怎麼轉成 c code 的, cfront 不支援 exception, 因為這功能複雜到超過 cfront 的能力了。

Cfront
Cfront 4.0 was abandoned in 1993 after a failed attempt to add exception support.[1] The C++ language had grown beyond its capabilities

Comeau C/C++ 也可以做到類似的事情, 不過要錢, 沒免費版可以試用。

這個 https://github.com/seyko2/cfront-3 就是 cfront source code 了, 我想在 linux 下編譯, 之前嘗試過一次, 失敗了, 這次有新版再來挑戰一次, 向 bjarne stroustrup 博士致敬, 感謝他發明出 c++ 這麼複雜的語言, 整死開發 c++ 編譯器的開發人員, 當然還有學習 c++ 的程式員。

我修改過的版本在 https://github.com/descent/cfront-3, 切到 can_build_in_linux branch 即可, 你得先會用 git 才行, 好吧! git checkout can_build_in_linux 就可以了。

Makefile
 1 #Makefile for the CC translator
 2 # BSD 4.2 or later should first run bsd.sed in the scratch directory
 3 # and set CCFLAGS=-DBSD
 4 # also set BSD=1
 5 
 6 CCFLAGS=-Os
 7 scratchCC ?= gcc
 8 
 9 BSD=
10 PATH:=$(CURDIR):$(PATH)
11 
12 #For first make (bootstrap):
13 # make scratch  #on system V, BSD 4.1 or earlier
14 #Otherwise:
15 # make
16 #
17 
18 CC = CC
19 
20 all: libC.a munch cfront
21  :
22 
23 libC.a: always
24  cd lib/mk; $(MAKE) CC=$(CC) CCFLAGS="$(CCFLAGS)" BSD=$(BSD)
25  mv lib/mk/libC.a .
26 
27 munch: _munch/munch.c
28  cc -o munch _munch/munch.c
29 
30 cfront: always
31  cd src; $(MAKE) CXX=$(CC) CCFLAGS="$(CCFLAGS)"
32  mv src/cfront cfront
33  
34 scratch: always
35  cd scratch; $(MAKE) CC=$(scratchCC) BSD=$(BSD) CCFLAGS="$(CCFLAGS)"
36 
37 #This target will populate the scratch directories with good-old-c
38 #files.  This is used to port to another machine.
39 
40 fillscratch:
41  make -C src szal.result y.tab.C yystype.h
42  cp src/_stdio.c scratch/src/
43  cd scratch/src; $(CC) -I../../src         -I../../incl -Fc -..c ../../src/*.C;
44  cd scratch/lib; $(CC) -I../../lib/complex -I../../incl -Fc -..c ../../lib/new/*.C
45  cd scratch/lib; $(CC) -I../../lib/complex -I../../incl -Fc -..c ../../lib/static/*.C
46  cp _munch/*.c scratch/mnch/
47 
48 always: 

Makefile L20
all: libC.a munch cfront

總共有 3 個部份要編譯, libC.a, munch, cfront 我分 3 次手動編譯, 為了避免奇怪的錯誤, 請按以下次序編譯。

[cfront]
編譯 cfront 會用到 yacc, can_build_in_linux branch 除了修改程式碼與編譯參數, 還有 yacc 需要改為使用 byacc (Berkeley YACC, 修改 Makefile), 使用 apt-get 來安裝 byacc。

apt-get install byacc

用 bison 會有以下錯誤訊息:

bison.err
1 gram.y:2340.31-32: error: $$ for the midrule at $3 of ‘statement’ has no declared type
2         |  ID COLON { $$ = $1; stmt_seen=1; } caselab_stmt
3                                ^^
4 gram.y:2344.34-35: error: $$ for the midrule at $3 of ‘statement’ has no declared type
5         |  TNAME COLON { $$ = new name($<pn>1->string); stmt_seen=1; } caselab_stmt
6                                   ^^

編譯指令:
cd src
make CC=gcc CCFLAGS="-Os -fpermissive -I../incl-master/incl-linux32/"

很奇怪, 在 gcc 9 之下, 不能使用 -Os, 會有 list 1 的錯誤。

list 1 gcc9 error
1 "t.h", line 22: internal <<AT&T C++ Language System <3.0.3> 05/05/94>> error: bus error (or something nasty like that)

看到以下錯誤免著驚, 這是藥氣等待行, cfront 其實已經編譯出來了。

g++ -o cfront Bits.o alloc.o block.o dcl.o dcl2.o dcl3.o dcl4.o del.o discrim.o error.o expand.o expr.o expr2.o expr3.o find.o hash.o lalex.o lex.o main.o norm.o norm2.o print.o print2.o repr.o simpl.o simpl2.o size.o table.o template.o tree_copy.o tree_walk.o typ.o typ2.o y.tab.o _stdio.o
Makefile:23: recipe for target 'cfront' failed
make: [cfront] Error 1 (ignored)
cp cfront ..

cfront 編好了。

[munch]
使用 cfront 編譯時, 需要用到的一個叫 munch 的工具程式。
gcc -o munch _munch/munch.c
這麼簡單, 不需要說明, 這是 .c 不用出動 cfront。

[libC.a]
cd lib/mk
make CC=gcc CCFLAGS="-Os -I../../incl-master/incl-linux32/"

不建議用 gcc 編譯 libC.a。 建議用 cfront 編譯, 不過先要編出 cfront 才行, CC3 就會使用 cfront 來編譯。後面會提到怎麼使用 cfront 編譯出一個可以執行的檔案, 這邊的 libC.a 就需要用 cfront 來編譯, 否則會有奇怪的 link 錯誤。

編出 cfront 之後 (需要複製到 cfront-3 目錄下), 在 cfront-3 目錄下打 make libC.a 即可。

建議使用 cfront 編譯 libC, 避免出現奇怪的錯誤 (因為這樣, 出現很多靈異現象讓我找了老半天)。

cd pt
make CC=gcc

pt 目錄有一些工具程式是編譯用到 string class 的時候會用到的。
ref: cfront-3/demo/string/
string class 我沒成功編譯出來。

也可以用 cfront 編譯 cfront 自己, 不過先要編出 cfront 才行, CC3 就會使用 cfront 來編譯, 不過有些問題, 得手動修改, 我懶的改, 沒成功用 cfront 編譯 cfront 自己。

終於 build 出來了, 執行一下, 感動阿!

run cfront
1 descent@debian64:cfront-3$ ./cfront
2 # 1 ""
3
4 /* <<AT&T C++ Language System <3.0.3> 05/05/94>> */
5 char __cfront_version_303_xxxxxxxx;

用 cfont 編譯 demo/hello/hello.C

demo/hello/hello.C
1 #include <stream.h>
2 
3 main()
4 {
5     cout << "Hello, World!\n";
6 }

有點難度, 步驟如下:
cp hello.C incl-master/incl-linux32
gcc -I. -E hello.C > hh.c
../../cfront < hh.c > h3.c

有沒很感動, 輸出一個 c 的 c++ 程式, bjarne 有你的。

h3.c
  1 # 1 ""
  2 
  3 /* <<AT&T C++ Language System <3.0.3> 05/05/94>> */
  4 char __cfront_version_303_xxxxxxxx;
  5 
  6 #pragma lib "ape/libap.a"
  7 
  8 #pragma lib "c++/libC.a"
  9 
 10 # 1 ""
 11 char *__vec_new ();
 12 
 13 # 1 ""
 14 char __vec_delete ();
 15 typedef int (*__vptp)();
 16 struct __mptr {short d; short i; __vptp f; };
 17 
 18 # 1 ""
 19 extern struct __mptr* __ptbl_vec___[];
 20 
 21 # 1 ""
 22 
 23 # 49 "./iostream.h"
 24 typedef long streampos ;
 25 typedef long streamoff ;
 26 enum __Q2_3ios8io_state { goodbit__Q2_3ios8io_state = 0, eofbit__Q2_3ios8io_state = 1, failbit__Q2_3ios8io_state = 2, badbit__Q2_3ios8io_state = 4, hardfail__Q2_3ios8io_state = 128} ;
 27 enum __Q2_3ios9open_mode { in__Q2_3ios9open_mode = 1, out__Q2_3ios9open_mode = 2, ate__Q2_3ios9open_mode = 4, app__Q2_3ios9open_mode = 8, trunc__Q2_3ios9open_mode = 16, nocreate__Q2_3ios9open_mode = 32, noreplace__Q2_3ios9open_mode = 64} ;
 28 enum __Q2_3ios8seek_dir { beg__Q2_3ios8seek_dir = 0, cur__Q2_3ios8seek_dir = 1, end__Q2_3ios8seek_dir = 2} ;
 29 enum __Q2_3ios4__E1 { skipws__Q2_3ios4__E1 = 1, left__Q2_3ios4__E1 = 2, right__Q2_3ios4__E1 = 4, internal__Q2_3ios4__E1 = 8, dec__Q2_3ios4__E1 = 16, oct__Q2_3ios4__E1 = 32, hex__Q2_3ios4__E1 = 64, showbase__Q2_3ios4__E1 = 128, showpoint__Q2_3ios4__E1 = 256, uppercase__Q2_3ios4__E1 = 512, showpos__Q2_3ios4__E1 = 1024, scientific__Q2_3ios4__E1 = 2048, fixed__Q2_3ios4__E1 = 4096, unitbuf__Q2_3ios4__E1 = 8192,
 30 # 54 "./iostream.h"
 31 stdio__Q2_3ios4__E1 = 16384} ;
 32 enum __Q2_3ios4__E2 { skipping__Q2_3ios4__E2 = 512, tied__Q2_3ios4__E2 = 1024} ;
 33 struct ios { /* sizeof ios == 88 */
 34 
 35 # 153 "./iostream.h"
 36 int nuser__3ios ;
 37 
 38 # 155 "./iostream.h"
 39 union ios_user_union *x_user__3ios ;
 40 
 41 # 162 "./iostream.h"
 42 struct streambuf *bp__3ios ;
 43 
 44 # 168 "./iostream.h"
 45 int state__3ios ;
 46 int ispecial__3ios ;
 47 int ospecial__3ios ;
 48 int isfx_special__3ios ;
 49 int osfx_special__3ios ;
 50 int delbuf__3ios ;
 51 struct ostream *x_tie__3ios ;
 52 long x_flags__3ios ;
 53 short x_precision__3ios ;
 54 char x_fill__3ios ;
 55 short x_width__3ios ;
 56 
 57 # 186 "./iostream.h"
 58 int assign_private__3ios ;
 59 
 60 # 193 "./iostream.h"
 61 struct __mptr *__vptr__3ios ;
 62 };
 63 
 64 # 84 "./iostream.h"
 65 extern long basefield__3ios ;
 66 
 67 # 86 "./iostream.h"
 68 extern long adjustfield__3ios ;
 69 
 70 # 88 "./iostream.h"
 71 extern long floatfield__3ios ;
 72 
 73 # 150 "./iostream.h"
 74 extern long nextbit__3ios ;
 75 extern long nextword__3ios ;
 76 
 77 # 180 "./iostream.h"
 78 extern char (*stdioflush__3ios )();
 79 
 80 # 195 "./iostream.h"
 81 struct streambuf { /* sizeof streambuf == 80 */
 82 short alloc__9streambuf ;
 83 short x_unbuf__9streambuf ;
 84 char *x_base__9streambuf ;
 85 char *x_pbase__9streambuf ;
 86 char *x_pptr__9streambuf ;
 87 char *x_epptr__9streambuf ;
 88 char *x_gptr__9streambuf ;
 89 char *x_egptr__9streambuf ;
 90 char *x_eback__9streambuf ;
 91 int x_blen__9streambuf ;
 92 
 93 # 370 "./iostream.h"
 94 struct __mptr *__vptr__9streambuf ;
 95 };
 96 
 97 # 1 ""
 98 extern char __dl__FPv ();
 99 
100 # 246 "./iostream.h"
101 
102 # 255 "./iostream.h"
103 int doallocate__9streambufFv ();
104 
105 # 258 "./iostream.h"
106 int underflow__9streambufFv ();
107 
108 # 369 "./iostream.h"
109 int x_snextc__9streambufFv ();
110 
111 # 259 "./iostream.h"
112 int pbackfail__9streambufFi ();
113 
114 # 257 "./iostream.h"
115 int overflow__9streambufFi ();
116 
117 # 24 "./string.h"
118 extern char *memcpy ();
119 
120 # 229 "./iostream.h"
121 
122 # 265 "./iostream.h"
123 int xsputn__9streambufFPCci ();
124 
125 # 234 "./iostream.h"
126 
127 # 266 "./iostream.h"
128 int xsgetn__9streambufFPci ();
129 
130 # 372 "./iostream.h"
131 struct istream { /* sizeof istream == 112 */
132 
133 # 493 "./iostream.h"
134 int x_gcount__7istream ;
135 
136 # 503 "./iostream.h"
137 struct __mptr *__vptr__7istream ;
138 struct ios *Pios;
139 struct ios Oios;
140 };
141 
142 # 489 "./iostream.h"
143 int do_ipfx__7istreamFi ();
144 
145 # 298 "./iostream.h"
146 
147 # 302 "./iostream.h"
148 
149 # 418 "./iostream.h"
150 struct istream *rs_complicated__7istreamFRUc ();
151 
152 # 298 "./iostream.h"
153 
154 # 302 "./iostream.h"
155 
156 # 419 "./iostream.h"
157 struct istream *rs_complicated__7istreamFRc ();
158 
159 # 429 "./iostream.h"
160 struct istream *get__7istreamFPcic ();
161 
162 # 298 "./iostream.h"
163 
164 # 292 "./iostream.h"
165 
166 # 436 "./iostream.h"
167 struct istream *get_complicated__7istreamFRUc ();
168 
169 # 298 "./iostream.h"
170 
171 # 292 "./iostream.h"
172 
173 # 437 "./iostream.h"
174 struct istream *get_complicated__7istreamFRc ();
175 
176 # 377 "./iostream.h"
177 
178 # 292 "./iostream.h"
179 
180 # 163 "./iostream.h"
181 
182 # 377 "./iostream.h"
183 
184 # 281 "./iostream.h"
185 
186 # 480 "./iostream.h"
187 struct istream *read__7istreamFPci ();
188 
189 # 260 "./iostream.h"
190 int sync__9streambufFv ();
191 
192 # 505 "./iostream.h"
193 struct ostream { /* sizeof ostream == 104 */
194 
195 # 610 "./iostream.h"
196 struct __mptr *__vptr__7ostream ;
197 struct ios *Pios;
198 struct ios Oios;
199 };
200 
201 # 601 "./iostream.h"
202 int do_opfx__7ostreamFv ();
203 char do_osfx__7ostreamFv ();
204 
205 # 538 "./iostream.h"
206 struct ostream *complicated_put__7ostreamFc ();
207 
208 # 324 "./iostream.h"
209 
210 # 163 "./iostream.h"
211 
212 # 568 "./iostream.h"
213 struct ostream *ls_complicated__7ostreamFc ();
214 
215 # 324 "./iostream.h"
216 
217 # 163 "./iostream.h"
218 
219 # 569 "./iostream.h"
220 struct ostream *ls_complicated__7ostreamFUc ();
221 
222 # 324 "./iostream.h"
223 
224 # 163 "./iostream.h"
225 
226 # 572 "./iostream.h"
227 struct ostream *__ls__7ostreamFi ();
228 
229 # 329 "./iostream.h"
230 
231 # 163 "./iostream.h"
232 
233 # 589 "./iostream.h"
234 
235 # 612 "./iostream.h"
236 struct iostream { /* sizeof iostream == 128 */
237 
238 # 493 "./iostream.h"
239 int x_gcount__7istream ;
240 
241 # 503 "./iostream.h"
242 struct __mptr *__vptr__7istream ;
243 struct ios *Pios;
244 struct ostream Oostream;
245 };
246 
247 # 620 "./iostream.h"
248 struct istream_withassign { /* sizeof istream_withassign == 112 */
249 
250 # 493 "./iostream.h"
251 int x_gcount__7istream ;
252 
253 # 503 "./iostream.h"
254 struct __mptr *__vptr__7istream ;
255 struct ios *Pios;
256 struct ios Oios;
257 };
258 
259 # 644 "./iostream.h"
260 extern struct istream_withassign cin ;
261 
262 # 628 "./iostream.h"
263 struct ostream_withassign { /* sizeof ostream_withassign == 104 */
264 
265 # 610 "./iostream.h"
266 struct __mptr *__vptr__7ostream ;
267 struct ios *Pios;
268 struct ios Oios;
269 };
270 
271 # 645 "./iostream.h"
272 extern struct ostream_withassign cout ;
273 extern struct ostream_withassign cerr ;
274 extern struct ostream_withassign clog ;
275 
276 # 657 "./iostream.h"
277 struct Iostream_init { /* sizeof Iostream_init == 1 */
278 
279 # 657 "./iostream.h"
280 char __W23__13Iostream_init ;
281 };
282 
283 # 658 "./iostream.h"
284 extern int stdstatus__13Iostream_init ;
285 extern int initcount__13Iostream_init ;
286 
287 # 663 "./iostream.h"
288 char __dt__13Iostream_initFv ();
289 
290 # 662 "./iostream.h"
291 struct Iostream_init *__ct__13Iostream_initFv ();
292 
293 # 664 "./iostream.h"
294 static struct Iostream_init iostream_init ;
295 
296 #pragma lib "ape/libap.a"
297 
298 #pragma lib "c++/libC.a"
299 
300 #pragma lib "ape/libap.a"
301 
302 #pragma lib "c++/libC.a"
303 
304 # 20 "./generic.h"
305 typedef int (*GPT )();
306 
307 # 112 "./iomanip.h"
308 struct smanip_int { /* sizeof smanip_int == 16 */
309 
310 # 112 "./iomanip.h"
311 struct ios *(*fct__10smanip_int )();
312 
313 # 112 "./iomanip.h"
314 int arg__10smanip_int ;
315 };
316 
317 # 1 ""
318 extern char *__nw__FUl ();
319 
320 # 112 "./iomanip.h"
321 struct sapply_int { /* sizeof sapply_int == 8 */
322 
323 # 112 "./iomanip.h"
324 struct ios *(*fct__10sapply_int )();
325 };
326 
327 # 112 "./iomanip.h"
328 struct imanip_int { /* sizeof imanip_int == 16 */
329 
330 # 112 "./iomanip.h"
331 struct istream *(*fct__10imanip_int )();
332 
333 # 112 "./iomanip.h"
334 int arg__10imanip_int ;
335 };
336 
337 # 112 "./iomanip.h"
338 struct iapply_int { /* sizeof iapply_int == 8 */
339 
340 # 112 "./iomanip.h"
341 struct istream *(*fct__10iapply_int )();
342 };
343 
344 # 112 "./iomanip.h"
345 struct omanip_int { /* sizeof omanip_int == 16 */
346 
347 # 112 "./iomanip.h"
348 struct ostream *(*fct__10omanip_int )();
349 
350 # 112 "./iomanip.h"
351 int arg__10omanip_int ;
352 };
353 
354 # 112 "./iomanip.h"
355 struct oapply_int { /* sizeof oapply_int == 8 */
356 
357 # 112 "./iomanip.h"
358 struct ostream *(*fct__10oapply_int )();
359 };
360 
361 # 112 "./iomanip.h"
362 struct iomanip_int { /* sizeof iomanip_int == 16 */
363 
364 # 112 "./iomanip.h"
365 struct iostream *(*fct__11iomanip_int )();
366 
367 # 112 "./iomanip.h"
368 int arg__11iomanip_int ;
369 };
370 
371 # 112 "./iomanip.h"
372 struct ioapply_int { /* sizeof ioapply_int == 8 */
373 
374 # 112 "./iomanip.h"
375 struct iostream *(*fct__11ioapply_int )();
376 };
377 
378 # 112 "./iomanip.h"
379 
380 # 113 "./iomanip.h"
381 struct smanip_long { /* sizeof smanip_long == 16 */
382 
383 # 113 "./iomanip.h"
384 struct ios *(*fct__11smanip_long )();
385 
386 # 113 "./iomanip.h"
387 long arg__11smanip_long ;
388 };
389 
390 # 113 "./iomanip.h"
391 struct sapply_long { /* sizeof sapply_long == 8 */
392 
393 # 113 "./iomanip.h"
394 struct ios *(*fct__11sapply_long )();
395 };
396 
397 # 113 "./iomanip.h"
398 struct imanip_long { /* sizeof imanip_long == 16 */
399 
400 # 113 "./iomanip.h"
401 struct istream *(*fct__11imanip_long )();
402 
403 # 113 "./iomanip.h"
404 long arg__11imanip_long ;
405 };
406 
407 # 113 "./iomanip.h"
408 struct iapply_long { /* sizeof iapply_long == 8 */
409 
410 # 113 "./iomanip.h"
411 struct istream *(*fct__11iapply_long )();
412 };
413 
414 # 113 "./iomanip.h"
415 struct omanip_long { /* sizeof omanip_long == 16 */
416 
417 # 113 "./iomanip.h"
418 struct ostream *(*fct__11omanip_long )();
419 
420 # 113 "./iomanip.h"
421 long arg__11omanip_long ;
422 };
423 
424 # 113 "./iomanip.h"
425 struct oapply_long { /* sizeof oapply_long == 8 */
426 
427 # 113 "./iomanip.h"
428 struct ostream *(*fct__11oapply_long )();
429 };
430 
431 # 113 "./iomanip.h"
432 struct iomanip_long { /* sizeof iomanip_long == 16 */
433 
434 # 113 "./iomanip.h"
435 struct iostream *(*fct__12iomanip_long )();
436 
437 # 113 "./iomanip.h"
438 long arg__12iomanip_long ;
439 };
440 
441 # 113 "./iomanip.h"
442 struct ioapply_long { /* sizeof ioapply_long == 8 */
443 
444 # 113 "./iomanip.h"
445 struct iostream *(*fct__12ioapply_long )();
446 };
447 
448 # 113 "./iomanip.h"
449 
450 # 10 "./stdio.h"
451 typedef char *va_list ;
452 
453 # 118 "./stdio.h"
454 extern char *sys_errlist [];
455 extern int sys_nerr ;
456 extern unsigned char *_bufendtab [];
457 
458 # 22 "./stdiostream.h"
459 struct stdiobuf { /* sizeof stdiobuf == 96 */
460 
461 # 196 "./iostream.h"
462 short alloc__9streambuf ;
463 short x_unbuf__9streambuf ;
464 char *x_base__9streambuf ;
465 char *x_pbase__9streambuf ;
466 char *x_pptr__9streambuf ;
467 char *x_epptr__9streambuf ;
468 char *x_gptr__9streambuf ;
469 char *x_egptr__9streambuf ;
470 char *x_eback__9streambuf ;
471 int x_blen__9streambuf ;
472 
473 # 370 "./iostream.h"
474 struct __mptr *__vptr__9streambuf ;
475 
476 # 36 "./stdiostream.h"
477 struct _iobuf *fp__8stdiobuf ;
478 int last_op__8stdiobuf ;
479 char buf__8stdiobuf [2];
480 };
481 
482 # 21 "./fstream.h"
483 struct filebuf { /* sizeof filebuf == 120 */
484 
485 # 196 "./iostream.h"
486 short alloc__9streambuf ;
487 short x_unbuf__9streambuf ;
488 char *x_base__9streambuf ;
489 char *x_pbase__9streambuf ;
490 char *x_pptr__9streambuf ;
491 char *x_epptr__9streambuf ;
492 char *x_gptr__9streambuf ;
493 char *x_egptr__9streambuf ;
494 char *x_eback__9streambuf ;
495 int x_blen__9streambuf ;
496 
497 # 370 "./iostream.h"
498 struct __mptr *__vptr__9streambuf ;
499 
500 # 45 "./fstream.h"
501 int xfd__7filebuf ;
502 int mode__7filebuf ;
503 char opened__7filebuf ;
504 streampos last_seek__7filebuf ;
505 char *in_start__7filebuf ;
506 
507 # 51 "./fstream.h"
508 char lahead__7filebuf [2];
509 };
510 
511 # 23 "./fstream.h"
512 extern int openprot__7filebuf ;
513 
514 # 54 "./fstream.h"
515 struct fstreambase { /* sizeof fstreambase == 224 */
516 
517 # 72 "./fstream.h"
518 struct filebuf buf__11fstreambase ;
519 
520 # 75 "./fstream.h"
521 struct __mptr *__vptr__11fstreambase ;
522 struct ios *Pios;
523 struct ios Oios;
524 };
525 
526 # 87 "./fstream.h"
527 
528 # 70 "./fstream.h"
529 
530 # 141 "./iostream.h"
531 
532 # 70 "./fstream.h"
533 
534 # 77 "./fstream.h"
535 struct ifstream { /* sizeof ifstream == 248 */
536 
537 # 72 "./fstream.h"
538 struct filebuf buf__11fstreambase ;
539 
540 # 75 "./fstream.h"
541 struct __mptr *__vptr__11fstreambase ;
542 struct ios *Pios;
543 struct istream Oistream;
544 };
545 
546 # 102 "./fstream.h"
547 
548 # 70 "./fstream.h"
549 
550 # 141 "./iostream.h"
551 
552 # 70 "./fstream.h"
553 
554 # 92 "./fstream.h"
555 struct ofstream { /* sizeof ofstream == 240 */
556 
557 # 72 "./fstream.h"
558 struct filebuf buf__11fstreambase ;
559 
560 # 75 "./fstream.h"
561 struct __mptr *__vptr__11fstreambase ;
562 struct ios *Pios;
563 struct ostream Oostream;
564 };
565 
566 # 117 "./fstream.h"
567 
568 # 70 "./fstream.h"
569 
570 # 141 "./iostream.h"
571 
572 # 70 "./fstream.h"
573 
574 # 53 "./stream.h"
575 
576 # 54 "./stream.h"
577 
578 # 55 "./stream.h"
579 
580 # 56 "./stream.h"
581 
582 # 57 "./stream.h"
583 
584 # 58 "./stream.h"
585 
586 # 59 "./stream.h"
587 
588 # 60 "./stream.h"
589 
590 # 62 "./stream.h"
591 typedef int state_value ;
592 
593 # 571 "./iostream.h"
594 struct ostream *__ls__7ostreamFPCc ();
595 
596 # 3 "hello.C"
597 int main (){ _main(); 
598 # 4 "hello.C"
599 { 
600 # 5 "hello.C"
601 __ls__7ostreamFPCc ( (struct ostream *)(& cout ), (char *)"Hello, World!\n") ;
602 }
603 } 
604 # 6 "hello.C"
605 char __sti___main_ ()
606 # 664 "./iostream.h"
607 { __ct__13Iostream_initFv ( & iostream_init ) ;
608 
609 # 664 "./iostream.h"
610 }
611 
612 # 6 "hello.C"
613 char __std___main_ ()
614 # 664 "./iostream.h"
615 { __dt__13Iostream_initFv ( & iostream_init , 2) ;
616 
617 # 664 "./iostream.h"
618 }
619 
620 # 23 "./fstream.h"
621 int openprot__7filebuf ;
622 
623 # 56 "./iostream.h"
624 
625 # 659 "./iostream.h"
626 int initcount__13Iostream_init ;
627 
628 # 658 "./iostream.h"
629 int stdstatus__13Iostream_init ;
630 
631 # 180 "./iostream.h"
632 char (*stdioflush__3ios )();
633 
634 # 151 "./iostream.h"
635 long nextword__3ios ;
636 
637 # 150 "./iostream.h"
638 long nextbit__3ios ;
639 
640 # 88 "./iostream.h"
641 long floatfield__3ios ;
642 
643 # 86 "./iostream.h"
644 long adjustfield__3ios ;
645 
646 # 84 "./iostream.h"
647 long basefield__3ios ;
648 
649 # 6 "hello.C"
650 
651 /* the end */

只有這樣還不能滿足吧, 希望可以讓這個 c 檔案透過 gcc 編譯出可執行檔案, 這就會需要那個 libC.a 了, cout 在 libC.a 中。

上面那個是我自己亂試的, 後來我參考了 cfront-3/demo/hello/hello.sh, 知道整個使用 cfront 的編譯步驟。

cfront-3/demo/hello/hello.sh
 1 #!/bin/sh
 2
 3 # manual steps to compile a program
 4 # http://lists.nongnu.org/archive/html/tinycc-devel/2014-12/binCQHJXV7ywM.bin
 5 #
 6 # The CC script now work.
 7 # Anyway, here is how to compile a C++ file manually
 8
 9
10 D=../..
11
12 if [ ! -x ${D}/cfront ]; then
13     echo "error: cfront compiler not found"
14     exit
15 fi
16
17 cpp -I${D}/incl hello.C > hello.i                  # run preprocessor
18 ${D}/cfront +a1 +L +fhello.C < hello.i > hello..c       # run cfront
19 cc hello..c ${D}/libC.a -o hello.tmp                  # compile and link plain C
20
21 # For static con/destructors, the nm/munch thingy is needed
22
23 nm hello.tmp | ${D}/munch > hello.cdts..c  # run mn against linked binary and filter
24 cc hello..c hello.cdts..c ${D}/libC.a -o hello        # compile and link again
25
26 ./hello

我們來看看怎麼編譯在平常不過的 hello world c++ 程式。

hello.C
1 #include <stream.h>
2
3 main()
4 {
5     cout << "Hello, World!\n";
6 }

在 cfront-3 目錄中執行:
cpp -Iincl-master/incl-linux32/ hello.C > hello.i # 處理 header files
./cfront +a1 +L +fhello.C < hello.i > hello..c # 將 cpp 檔 (.C) 轉成 .c 檔
gcc hello..c ./libC.a -o hello.tmp # 不知道
nm hello.tmp | ./munch > hello.cdts..c # 不知道
gcc hello..c hello.cdts..c ./libC.a -o hello # 輸出可執行檔
./hello # 執行

看到印出來的 Hello, World 真是感動。

後來我測試了, template function, class template 都沒有問題。

用 cfront 編譯 cfront 時, alloc.C 遇到的問題:
CC3 L608
eval 'gcc -Du3b -U__GNUC__ -D__cfront__ -D__cplusplus=1 -D__signed__= -D__null=0 -E' ' -I../incl-master/incl-linux32/ -D__CFRONT_ANSI_OPT'  '-Dc_plusplus=1' '-D__cplusplus=1' '' ' -I/media/work/git/cfront-3/incl' 'alloc.C' \>/media/work/git/cfront-3/tmpdir/CC.25638/cpptmp

用 llvm 也可以作到 c++ 轉 c code, 參考 Can I use LLVM to convert C++ code to C code? 不過要 3.1 版才能用, 我的系統最低是 3.6。

不過 llvm 用的方式是先轉成 .bc 再將 .bc 轉成 c, 其實和 cfront 不太一樣。

ref:
[LLVMdev] How to enable cbe as a supported target?

至於很常用到的 vector, 這個版本付的 vector 用法和 std::vector 的用法很不同:

demo/generic/Vector.C
vectordeclare(int)
vectordeclare(float)

vectorimplement(int)
vectorimplement(float)
vector(int)     iv(VectorSize);

看起來很類似 macro 的用法, 而不是 class template 的用法, 後來將我那個 myvec.h 修改一下, 可以用在 cfront。

v.C
 1 #include <stream.h>
 2 #include "myvec.h"
 3 
 4 main()
 5 {
 6   vector<int> v;
 7   v.push_back(1);
 8   v.push_back(2);
 9   cout << v[0] << endl;
10   cout << v[1] << endl;
11 }

是可以用的。

ref:
另外一個 cfront 版本
release 1.0:
http://www.softwarepreservation.org/projects/c_plus_plus/cfront/release_1.0/src/cfront.tar.gz/view