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 次手動編譯:

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

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

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

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

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/"

看到以下錯誤免著驚, 這是藥氣等待行, 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 編好了。

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 版本

2018年3月23日 星期五

用輕鬆的方式來學習編譯程式指令 (gcc/g++), 不一定要靠 IDE 來編譯程式。

最開始學習 C 語言時, 是用 turbo c 2.0, 記得只要按下 F9, 就會產生一個 dos 執行檔案。而裡頭的除錯環境也很好用, 對於剛學習 C 語言的初學者幫助很大, 只要專心在 c 語言的學習上就可以了, 不需要花太多時間和編譯工具奮鬥。

然而老師卻要我們用 unix 上的 cc 編譯器, 一下子難倒許多人, 在上大學之前, 不可能有人會有 unix c 語言開發經驗的。

turbo c 2.0 的開發者真是厲害, Anders Hejlsberg 開發了 turbo pascal, turbo c 不知道其是不是也有參與?

我現在的程度可以在 dos 上獨立開發出 turbo c 2.0 的開發工具嗎? 方便的 ide 除錯環境目前還難倒我。



如何看待「年轻人不要用 Visual Studio」的言论?

這篇在討論應該要用這種 ide 嗎? 我有自己的想法, 但我寫這篇文章不是要參加大辯論, 我想寫些東西, 讓願意不使用 ide 的初學者, 可以不要那麼痛苦。

以下介紹 linux c 開發環境, 使用 windows/vs 的朋友就抱歉了, vs 系列我連「略懂」都沒有。

寫程式最需要的是編譯指令, 以 gcc 來說, 就是使用 gcc 這個指令。
最簡單的用法就是:
gcc h.c
這時候會編出一個 a.out 的執行檔, 執行它就可以了。
如果你有 2 個 c 檔案:
gcc h1.c h2.c
這樣就可以, 應該沒難到記不住。

再來是 -o option,
gcc h.c -o h
這會輸出一個 h 的可執行檔。

再來是和 header files (.h) 有關:
如果你 include abc/1.h
很直覺的一個想法, 你應該告訴 gcc 去哪裡找是吧? 否則 gcc 怎麼知道要去那裡找 abc 目錄裡頭的 1.h。
這就有了 -I option,

如果 abc 是在 /abc
gcc h.c -I /abc
這樣就會告訴 gcc 去 /abc 找那個 1.h。

如果 abc 是在 /xyz/abc
gcc h.c -I /xyz/abc
這樣就會告訴 gcc 去 /xyz/abc 找那個 1.h。

你說那 stdlib.h 怎麼不用下 -I option, 那是因為 stdlib.h 是標準程式庫的一部份, gcc 知道 stdlib.h 在哪裡, 既然 gcc 知道在哪裡, 當然就不用我們加入 -I option。

再來是連接一個程式庫 libabcdef.a, 假設這個放在 /def/ 裡頭, 那麼
gcc h.c /def/libabcdef.a
就可以了, 很直覺, 不用解釋。

同理, c 程式如果呼叫了 printf, 就需要連結 libc.a, 為什麼我不用加入 /aaa/libc.a, 因為這是標準程式庫的一部份, gcc 知道 libc.a 在哪裡, 既然 gcc 知道在哪裡, 當然就不用我們寫。

以下的 option 可以不需要, 但因為很常見, 介紹一下, 覺得太複雜可以先不記。
-c option:
gcc -c h.c
會產生 h.o object file, 而不是可執行檔案。

再一次
gcc h.o
就會產生 a.out 可執行檔案。

gcc h.o -o h
就會產生 h 可執行檔案。

-l option, -L option:
這 2 個通常一起用,
gcc h.c /def/libabcdef.a
/def/libabcdef.a 可以換成
-L/def/ -labcdef
-L 告訴 gcc libabcdef.a 放在哪裡。
-l 可以不用寫出完整的 libabcdef.a 名稱。
麻煩多了吧! 所以有時候我也不用這 2 個 option。

只要先這樣, 就不需要靠 ide 完成編譯, 自己打指令就可以了。

這方式顯然比使用 ide 的 F9 痛苦了不只一點, 那有什麼好處嗎?
好處是你知道編譯的細節, 當你換了一個編譯器指令時, 知道要去找類似的 option 來使用。這個理由不太吸引人, 我知道。

debug 是一個問題, 不靠 IDE, 要學習 gdb 來 debug, 這就真的不容易。

而沒有 IDE 你很難作到:
function complete
看變數初始值
ifdef 是哪一段
macro 的值, 或是展開後的結果
...

一但 ide 裝好就會有的神奇功能, 使用原始開發工具的我都無法直接受惠。

我自己依然在使用 makefile + vim 的環境, 很久之後我才知道現在的 ide 和 turbo c 2.0 的 ide 已經大不相同, 提供了很多的功能, 但不用 ide 對我沒造成太大的困擾, 我一樣可以用 vim 來查看 linux source code, 寫 qt 的時候開啟 qt doc 查閱相關函式, 複製/貼上, 原始人的開發方式, 這也不算是什麼厲害的技巧, 就每個人習慣的工具不同罷了。

而事實上, 不用 ide 還得學習 makefile 的編寫, 這也是一個小門檻。

最基本 makefile 用法:
h.o: h.c
        gcc -c h.c
應該很直覺, h.o 要由 h.c 來產生, 用 gcc -c h.c 指令來產生 h.o。

h: h.o
        gcc -o h h.o
應該也很直覺, h 要由 h.o 來產生, 用 gcc -o h h.o 指令來產生 h。

makefile 我通常用 $@, $<, $^, 只要記住這 3 個, 負擔應該不大。如果覺得太複雜, 也可以不理這個。

h: h.o
        gcc -o $@ h.o

$@ 就是指 h。

h: h.o
        gcc -o h $<

$< 就是指 h.o。

h: h.o k.o
        gcc -o h $<

$< 還是指 h.o, 如果要把 k.o 也包含進來, 要用 $^。

h: h.o k.o
        gcc -o h $^

$^ 就是指 h.o k.o。

好了, 作為一份輕鬆的簡介, 這樣已經太複雜了。

這只是很簡單的介紹, 實際上要在學習多一點, 才會更好用, 但如果要嘗試看看不使用 ide 環境, 這樣就夠了。

2018年3月16日 星期五

[娃店] 偶夢之地

偶夢地址: 高雄市楠梓區惠豐街206巷27號2樓

20171210 我搭乘高雄捷運, 來到都會公園站, 由 4 號出口出站, 透過 google map 來到了偶夢之地。這是一間娃店, 販售的物品很多, 終於來到這家店了。

從都會公園站 4 號出口大概走路 5 分鐘左右即可到達。店在二樓, 得先打電話請二樓的店員到一樓來開門, 很奇怪對吧?

不過我運氣好, 一樓剛好有人, 他就幫我開門, 讓我進入店裡頭, 一樓是個咖啡店, 目前還在整理當中, 吸引我目光的是霹靂布袋戲的戲偶。



「哇賽!」原來布袋戲偶這麼大一隻, 我和一樓的小哥聊了好多布袋戲話題, 戲偶主人財力雄厚, 這只是其中部份蒐藏。

二樓的店家才是我這次主要的拜訪對象, 裡頭只有一位店員, 辛苦她了。

fig 1. 咖啡色涼鞋 520 nt

看到販售區的鞋子, 目光馬上被一雙咖啡色涼鞋吸引, 親切的店員說:「我們這裡的東西都是可以試穿的, 覺得適合再購買。」店家鼓勵娃友們把自己的娃娃帶來, 可以試試店中的每一件商品, 相當不錯的服務, 有些東西總是要試試才知道適不適合自己的娃, 這是實體店面帶來的服務, 網路店家絕對辦不到的服務。

而看到 fig 1. 的拍照區之後, 立刻換上新涼鞋來一張, 這是我第一次拿著娃到拍照區拍照, 店家配合 12/25 耶誕節的到來, 打造了應景的耶誕氛圍, 整體感覺相當不錯。



整間店面算是蠻大的, 又是在公共運輸工具容易抵達的地方, 除了地利, 裡頭販售的物品相對平價, 同樣的 3000 元, 在別家店可能買一樣東西, 在這裡可以買的滿滿一手。

 

店員說目前 正在開發 bjd 素體可以接上 DD 頭的方式, 這樣就可以免除素體染色的問題, DD 素體染色比較麻煩, 不好處理, bjd 在最差情況下, 可以用磨的方式, 來把被染色的部份磨掉。

fig 2. 購買的物品, 總共 2989 含 fig 1. 的涼鞋。

不過我能停留的時間實在太短,又花了不少時間和店員聊天, 剩下能逛的時間不多。趕緊匆匆的選購一些配件, 而且每一件都可以試穿, 雖然得花不少時間, 但可以看看自己的娃穿上後的效果, 免得買到不搭的配件, 這是很棒的服務, 來這裡請記得帶自己的娃來哦!

最後花了 2989 購買了 fig 2 的物品。

回來之後和娃友分享購買的物品, 發現很多人也入手了涼鞋, 看來是一款很受歡迎的鞋子。



fig 1. 的搖椅我很喜歡, 不過偶夢並沒有販售這個搖椅, 後來透過淘寶, 我購買了一個類似的椅子 (fig 3), 沒有偶夢的那張精緻, 但也很不錯, DD 3 分坐上去非常適合。


fig 3.
bjd娃用家具 bjd摇椅 大叔3分4分BJD娃娃SD娃用椅子 仓发
費用: 100+12 rmb, 2018-01-18 下標, 20180123 到集貨商, 1.42kg

本來有點擔心椅子大小不合, 不過看上去還蠻適合的, 坐在這張搖椅上, 看來別有一番滋味。

2018年3月9日 星期五

uefi programming: read a file

耳聞之不如目見之, 目見之不如足踐之
繼上次可以很單純的《不使用 edk2 來開發 uefi 程式》之後, 讓我對於 uefi 程式的撰寫有了動力, 畢竟要用 edk2 這種我不熟悉的開發工具, 實在是提不上勁。之後的範例沒特別說明的話, 都是以此方式來開發, 不再使用 edk2。

這次要練習使用 uefi api 來讀取檔案, 為什麼要練習這個, 因為 uefi os loader 應該會需要有讀檔案的能力, 我不打算自己撰寫磁碟驅動程式, 能先在 uefi 環境載入檔案在方便不過。

讀取檔案程式員應該不陌生, 不管是 fopen/fread, open/read 都是類似的概念, uefi 也是, 不過在這之前得先取得一個叫 protocol 的東西。這是很特別的概念, 我在其他開發工具沒看過類似的用法, 通常要讀寫檔案, 直接呼叫 read api 即可, 不過在 uefi 環境, 要先取得檔案相關的 protocol, 並用其 protocol 的功能來處理檔案。

取得 protocol 有好幾個不同的 api 可以使用, 我使用的是 LocateProtocol(), 你說我的怎麼是 locate_protocol, 這有點難解釋, 就不解釋了, 因為不是用 edk2, 這是自己宣告的函式名稱。

L16, 43, 就是在取得檔案的 protocol。L16 定義了一組數字, 這組數字就是對應到檔案相關的操作; 每個 protocol 都有定義一組數字, 圖形相關的 protocol 的數字則是

  define EFI_GOP_GUID EFI_GUID(0x9042a9de, 0x23dc, 0x4a38, 0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a)

所以要取得某個服務, 要知道其數字, edk2 沒問題, 這些都定義好了, 自己打造的環境就需要把這些數字自己定義上去。不難, 看 edk2 定義的數字, 依樣畫葫蘆即可。

L52, 66, 78 則是程式員比較習慣的檔案操作, open, read, 多了一個 open_volume (edk2 的名稱是 OpenVolume())。其參數 EFI_FILE_PROTOCOL 提供了相關的檔案操作, open, read ... 你可能發現和 edk2 文件不同, edk2 Open 我用的是 open, Read 我是用 read, 有點奇怪, 這是因為 uefi 程式是在被載入後才使用這些函式, 而不是在 link 階段就把這些函式都定位好, 所以才可以這麼做。

OpenVolume() Prototype
typedef
EFI_STATUS
(EFIAPI *EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_OPEN_VOLUME) (
  IN EFI_SIMPLE_FILE_SYSTEM PROTOCOL *This,
  OUT EFI_FILE_PROTOCOL **Root);
Parameters

Provides file based access to supported file systems.

Contents

 [hide]

read_file.c
  1 /*
  2  * Copyright (c) 2015 Google, Inc
  3  *
  4  * SPDX-License-Identifier: GPL-2.0+
  5  *
  6  * EFI information obtained here:
  7  * http://wiki.phoenix.com/wiki/index.php/EFI_BOOT_SERVICES
  8  *
  9  * Loads a payload (U-Boot) within the EFI environment. This is built as an
 10  * EFI application. It can be built either in 32-bit or 64-bit mode.
 11  */
 12 
 13 #include "efi.h"
 14 #include "efi_api.h"
 15 
 16 static efi_guid_t gEfiSimpleFileSystemProtocolGuid = EFI_GUID(0x964E5B22, 0x6459, 0x11D2,  0x8E, 0x39, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B);
 17 
 18 
 19 #if 0
 20 
 21 // File attributes
 22 //
 23 #define EFI_FILE_READ_ONLY  0x0000000000000001
 24 #define EFI_FILE_HIDDEN     0x0000000000000002
 25 #define EFI_FILE_SYSTEM     0x0000000000000004
 26 #define EFI_FILE_RESERVED   0x0000000000000008
 27 #define EFI_FILE_DIRECTORY  0x0000000000000010
 28 #define EFI_FILE_ARCHIVE    0x0000000000000020
 29 #define EFI_FILE_VALID_ATTR 0x0000000000000037
 30 #endif
 31 
 32 efi_status_t efi_main(efi_handle_t image, struct efi_system_table *sys_table)
 33 {
 34   efi_status_t ret;
 35 
 36   struct efi_simple_text_output_protocol *con = sys_table->con_out;
 37 
 38   con->output_string(con, L"22 test read file\n\r");
 39 
 40 
 41   struct efi_simple_file_system_protocol *sfs;
 42 
 43   ret = sys_table->boottime->locate_protocol(&gEfiSimpleFileSystemProtocolGuid, 0, (void **)&sfs);
 44 
 45   if (ret != EFI_SUCCESS) 
 46   {
 47     con->output_string(con, L"\r\nLocateProtocol failed\n\r");
 48   }
 49 
 50   struct efi_file_handle *root = 0;
 51 
 52   ret = sfs->open_volume(sfs, &root);
 53   if (ret != EFI_SUCCESS) 
 54   {
 55     con->output_string(con, L"\r\nsfs->open_volume failed\n\r");
 56   }
 57 
 58   struct efi_file_handle *fh = 0;
 59   u8 Buf[66]={0};
 60   u16 u16_buf[66]={0};
 61   u64 BufSize = 64;
 62   s16 fn[20]={'.', '\\', 't', '.', 't', 'x', 't', 0};
 63   con->output_string(con, L"open ");
 64   con->output_string(con, fn);
 65   con->output_string(con, L"\r\n");
 66   ret = root->open(root, &fh, L".\\t.txt", EFI_FILE_MODE_READ, 0);
 67 
 68   if(ret == EFI_NOT_FOUND)
 69   {
 70     con->output_string(con, L"\r\nroot->open not found\r\n");
 71   }
 72   if(ret != EFI_SUCCESS)
 73   {
 74     con->output_string(con, L"\r\nroot->open fail\r\n");
 75     return ret;
 76   }
 77 
 78   ret = fh->read(fh, &BufSize, Buf);
 79   if(ret != EFI_SUCCESS)
 80   {
 81     con->output_string(con, L"\r\nroot->read fail\r\n");
 82   }
 83 
 84 
 85   for (int i=0 ; i < BufSize ; ++i)
 86   {
 87     u16_buf[i] = Buf[i];    
 88   }
 89 
 90     if(ret == EFI_SUCCESS)
 91     {
 92       //Buf[BufSize] = 0;
 93 
 94       con->output_string(con, L"\r\nprint t.txt\n\r");
 95       #if 1
 96       con->output_string(con, L"\r\n");
 97       con->output_string(con, u16_buf);
 98       con->output_string(con, L"\r\n");
 99       #endif
100     }
101     con->output_string(con, L"\r\nprint t.txt end\n\r");
102 
103     ret = fh->close(fh);
104 
105 
106 
107   return 0;
108 }

read_file.c 只用到 open, read, close。程式看起來在簡單不過了, 但我卻踩到一個問題。我是使用 usb 隨身碟來載入 efi shell, 也就是說, notebook 內建硬碟已經有了一個 fat EFI parartion, 隨身碟也有一個 fat EFI parartion, 而這個 locate_protocol 找到的檔案操作只能針對內建硬碟的 fat EFI parartion (它只能找到第一個 protocol, 第一個 protocol 對應到 內建硬碟的 fat EFI parartion), 所以我在隨身碟的 fat EFI parartion 執行這個程式會找不到 t.txt, 花了好久時間我才知道有這問題, 所以這隻程式得在內建硬碟的 fat EFI parartion 才能執行成功。

印出來的部份不太正確, 不過不影響整個讀檔的正確性, 要印出來還得將字元轉成 uint16_t, 這是和 linux utf8 編碼的不同之處, 我也不太熟悉這樣的方式。

fig 1. 內建硬碟的 fat EFI parartion 執行成功的結果

2018年3月2日 星期五

volks DD 娃一體身, 牡羊套裝與皮衣套裝

一體身 (One-Piece Torso) 是一種 DD 身體零件, 沒有在胸部附近分成 2 塊零件, 據說穿比基尼會好看些, 但身體的可動性則會降低。



所有零件購於日本秋葉原 doll point, 我本來在想買身體 + ㄧ個 L 胸一體身, 但這樣會多出 2 個身體零件, 多了重複的零件有點困擾我, 最後還是選擇購入所有身體零件, 當然這樣會比買整個組好的身體貴了一點, 而且我沒有購買手型 (整個身體是有含手型)。其實應該可以考慮賣掉用不著的身體零件, 當初沒有想好, 不過自己組裝也是有獲得額外的經驗。

一開始遇到的問題是大腿骨與小腿的組裝, 如 fig 1. 一樣, 我不知道怎麼裝進去, 洞太小了, 後來是群組的人說明要用吹風機吹那個洞, 軟化之後, 就可以把小腿插進去, 我試了一下吹風機, 果然可行。

fig 1. 大腿骨與小腿的組裝

好不容易把大腿骨與小腿裝起來之後, 發現看起來怪怪的, 如 fig 2, 原來我把左大腿裝上右小腿, 又花費了好一番功夫拆掉, 一樣得用吹風機才拔的出來。

fig 2. 左右裝反
又要重裝一次, 覺得很煩, 但是沒辦法, 沒有經驗就是這樣, 買直接裝好的身體就簡單多了, 還不用被這樣折騰, 不過這是一次很好的經驗, 因為自己有裝過一次, 了解其中的一些組裝細節。

原來吹風機是要這麼用的, 我之前換手型的時候都沒有使用吹風機, 直接硬塞到手首中, 看起來有點暴力。

這大概就是最難的部份了, 其他的手臂, 直接插入肩膀即可, 腳掌也沒有什麼困難, 我大概花了半小時之後, 終於成功組裝起整個身體。

購買的一體身是 L 胸的一般胸, 不是集中胸, 一體身的可動性果然不佳, 無法作到側身旋轉身體, 沒什麼特別理由的話, 還是買非一體身的好。

一體身會包含一個骨架, 所以比較貴, 據說骨架也是可以抽換, 但可能比較難換些, 希望我不會遇到有需要抽換骨架的一天。

這陣子剛好買了不少配件, 噴了不少銀彈, 來試試這些裝備, fig 3 是這些配件的集合, 衣服則是第一套購買的那件, 已經很久沒再拿出來穿過, 喜新厭舊的速度令我驚訝, 其實還蠻好看的, 搭配新買的手勢, 造型更多樣化了。不過由於這件是用魔鬼氈, 一直會黏到其他的衣服, 不太適合這樣的組合, 沒多久我就又換了另外一個造型, 後文會介紹。

fig 5. 知好色而慕少艾

fig 5. 展示一體身與非一體身的差異, 在外觀美感上, 果然是有差別, 但一體身無法作到轉動上半身的動作, 也展示了內衣的不同, 粉紅色是 tcdoll 製作的, 1300nt + 70 購得, 很貴, 我覺得是一個很離譜的價錢 (盤子價), 而且只適合 M 胸, L 胸無法穿, 店家展示的 model 是 smart doll, 穿在 DD 身上, 有點不太合身; 白色是淘寶店家, 58rmb, 差價驚人, 但視覺效果也不同。

fig 3.
fig 6. 牡羊套裝 + 女僕盤子, 20171211 訂購於蝦皮, 20171214 收到, 2 項共 2850nt, 這 2 件從其他娃友手上購得。

牡羊套裝是 dollheart 出品, 編號 DM000024, 定價 $89.90 us, 可汀販售的, 定價好像是 3xxx, 我之前看到就很喜歡, 不過由於只能在 M 胸上穿著, 考慮之後, 沒有購入。

不過之後購入 M 胸身體之後 (請參考: [敗家] dd body III M 胸 - 普肌娃體), 已經可以穿上這套衣服, 我猶豫了一下之後, 就跟娃友買下了, 感謝他願意開蝦皮賣場讓我刷卡, 謝謝。

另外的女僕盤子原價 750, 還真的貴 (真的盤子價), 娃友賣 500, 也是不便宜, 但我看了喜歡就一起買下了。

牡羊套裝的馬甲蠻特別的, 竟然是穿在襯衫的外頭, 搭配的是短褲, 覺得有點可惜, 比較喜歡搭配裙子, 可惜我找來找去, 將我的收集都翻過一輪, 還是找不到和其搭配的裙子, 只好搭這件毛很多的短褲, 當然整體造型也是不錯。

馬甲的穿著比較麻煩, 得一個洞一個洞的把線串起來, 脫下來也要重複同一個步驟, 再換上之後, 我實在很懶得在脫下來, 所以這件衣服停留了很久一段的時間在 M 胸娃體上。

fig 6. 右: 女僕盤子, 左: 牡羊套裝



皮衣套裝比較複雜, 因為來自不同的地方, 全部配件如下:
黑色長靴含運 470nt, 20160707 收到, 娃友的二手品, 直到現在才派上用場。
帽子購於娃場, 350nt。
襯衫購於放課後 Tea Time & 貓草屋, 忘記多少錢了。
服裝本體從好幾個地方購得, 參考以下買賣紀錄。

一體身最後就是換上這個皮衣套裝, 還蠻帥氣有型。



订单号52992110636669095love A peace 经营BJD假发 衣服 娃鞋 眼珠等配件



128.00
103.61
1


订单号2595578696169095梦回童年娃衣店
黑色1/3
110.00
1
157.00
(含运费:¥12.00)
交易成功
黑色3分
35.00
1