本記事の信頼性
- リアルタイムシステムの研究歴12年.
- 東大教員の時に,英語でOS(Linuxカーネル)の授業.
- 2012年9月~2013年8月にアメリカのノースカロライナ大学チャペルヒル校(UNC)コンピュータサイエンス学部で客員研究員として勤務.C言語でリアルタイムLinuxの研究開発.
- プログラミング歴15年以上,習得している言語: C/C++,Python,Solidity/Vyper,Java,Ruby,Go,Rust,D,HTML/CSS/JS/PHP,MATLAB,Assembler (x64,ARM).
- 東大教員の時に,C++言語で開発した「LLVMコンパイラの拡張」,C言語で開発した独自のリアルタイムOS「Mcube Kernel」をGitHubにオープンソースとして公開.
- 2020年1月~現在はアメリカのノースカロライナ州チャペルヒルにあるGuarantee Happiness LLCのCTOとしてECサイト開発やWeb/SNSマーケティングの業務.2022年6月~現在はアメリカのノースカロライナ州チャペルヒルにあるJapanese Tar Heel, Inc.のCEO兼CTO.
- 最近は自然言語処理AIとイーサリアムに関する有益な情報発信に従事.
- (AI全般を含む)自然言語処理AIの論文の日本語訳や,AIチャットボット(ChatGPT,Auto-GPT,Gemini(旧Bard)など)の記事を50本以上執筆.アメリカのサンフランシスコ(広義のシリコンバレー)の会社でプロンプトエンジニア・マネージャー・Quality Assurance(QA)の業務委託の経験あり.
- (スマートコントラクトのプログラミングを含む)イーサリアムや仮想通貨全般の記事を200本以上執筆.イギリスのロンドンの会社で仮想通貨の英語の記事を日本語に翻訳する業務委託の経験あり.
こういった私から学べます.
C言語を独学で習得することは難しいです.
私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.
私のキャパシティもあり,一定数に達したら終了しますので,今すぐ追加しましょう!
独学が難しいあなたは,元東大教員がおすすめするC言語を学べるオンラインプログラミングスクール5社で自分に合うスクールを見つけましょう.後悔はさせません!
C言語でC++言語みたいにコードを書けたら便利だと思ったことはありませんか?
そんなあなたにC言語でC++言語の機能を模倣する方法を紹介します.
基本的にはGCC/Clangの拡張機能を利用しますので,Visual Studioでは動作しないことに注意して下さい.
目次
コンストラクタとデストラクタ
C++言語ではクラスを生成,破棄する際にコンストラクタとデストラクタが呼ばれます.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <iostream> class Hello { public: Hello() { std::cout << "constructor()" << std::endl; } ~Hello() { std::cout << "destructor()" << std::endl; } }; Hello hello; int main(void) { std::cout << "main()" << std::endl; return 0; } |
実行結果は以下になります.
1 2 3 4 5 |
$ g++ constructor_and_destructor.cpp $ a.out constructor() main() destructor() |
C言語でコンストラクタとデストラクタを模倣するためには,以下のGCC/Clangの拡張機能を利用します.
- コンストラクタ: __attribute__((constructor))
- デストラクタ:__attribute__((destructor))
C言語のコードは以下になります.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <stdio.h> __attribute__((constructor)) void constructor(void) { printf("constructor()\n"); } __attribute__((destructor)) void destructor(void) { printf("destructor()\n"); } int main(void) { printf("main()\n"); return 0; } |
実行結果は以下になります.
1 2 3 4 5 |
$ gcc constructor_and_destructor.c $ a.out constructor() main() destructor() |
関数のオーバーロード
C++ではクラスのメンバ関数のオーバーロードができます.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <iostream> class Hello { public: void print_hello(int i) { std::cout << "Hello " << i << "!" << std::endl; } void print_hello(double d) { std::cout << "Hello " << d << "!" << std::endl; } void print_hello(std::string s) { std::cout << "Hello " << s << "!" << std::endl; } void print_hello() { std::cout << "Hello World!" << std::endl; } }; Hello hello; int main(void) { hello.print_hello(123); hello.print_hello(3.14); hello.print_hello("abc"); hello.print_hello(); return 0; } |
実行結果は以下になります.
1 2 3 4 5 6 |
$ g++ overload.cpp $ a.out Hello 123! Hello 3.14! Hello abc! Hello World! |
C11規格の総称選択(Generic Selection)
C11規格の総称選択(Generic Selection)を利用すると,C++言語のオーバーロードを模倣することができます.
C++言語のオーバーロードとは異なり,C11規格の総称選択は引数の個数が同じでない(引数がない)場合は利用できないことに注意して下さい.
※引数の個数が可変の可変長引数を利用すれば,制限付きで可能になります.
まずは総称選択を知りたいあなたはこちらからどうぞ.
C11規格の総称選択を利用するコードは以下になります.
※print_hello関数の引数がない場合は削除しています.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <stdio.h> #define print_hello(n) _Generic((n), \ int: print_hello_i, \ double: print_hello_d, \ char *: print_hello_s \ )(n) void print_hello_i(int i) { printf("Hello %d!\n", i); } void print_hello_d(double d) { printf("Hello %lf!\n", d); } void print_hello_s(char *s) { printf("Hello %s!\n", s); } int main(void) { print_hello(123); print_hello(3.14); print_hello("abc"); return 0; } |
実行結果は以下になります.
1 2 3 4 5 |
$ gcc overload.c $ a.out Hello 123! Hello 3.140000! Hello abc! |
Clangの拡張機能「__attribute__((overloadable))」
Clangの拡張機能「__attribute__((overloadable))」を利用することで,C++言語のオーバーロードを模倣することができます.
C11規格の総称選択とは異なり,Clangの拡張機能「__attribute__((overloadable))」は引数の個数が異なる場合でも利用できます.
ただし,GCCでは「__attribute__((overloadable))」が機能せず,ビルドが失敗することに注意して下さい.
Clangの拡張機能「__attribute__((overloadable))」を利用するコードは以下になります.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <stdio.h> __attribute__((overloadable)) void print_hello(int i) { printf("Hello %d!\n", i); } __attribute__((overloadable)) void print_hello(double d) { printf("Hello %lf!\n", d); } __attribute__((overloadable)) void print_hello(char *s) { printf("Hello %s!\n", s); } __attribute__((overloadable)) void print_hello(void) { printf("Hello World!\n"); } int main(void) { print_hello(123); print_hello(3.14); print_hello("abc"); print_hello(); return 0; } |
実行結果は以下になります.
1 2 3 4 5 6 |
$ clang overload2.c $ a.out Hello 123! Hello 3.140000! Hello abc! Hello World! |
関数のオーバーライド
C++での関数のオーバーライドは,子クラスが親クラスのメンバ関数を上書きすることです.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <iostream> class Hello { public: void print_hello() { std::cout << "Hello!" << std::endl; } }; class World : public Hello { public: void print_hello() { std::cout << "Hello World!" << std::endl; } }; Hello hello; World world; int main(void) { hello.print_hello(); world.print_hello(); return 0; } |
実行結果は以下になります.
1 2 3 4 |
$ g++ override.cpp $ a.out Hello! Hello World! |
C言語で関数のオーバーライドをするためには,GCC/Clangの拡張機能の「__attribute__((weak))」を利用します.
__attribute__((weak)を利用する関数と同じ名前の関数は別ファイルで定義する必要があることに注意して下さい.
同じファイルで両方の関数を定義するとビルドエラーになります.
C言語で__attribute__((weak)を利用するコードは以下になります.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <stdio.h> __attribute__((weak)) void print_hello(void) { printf("Hello!\n"); } int main(void) { print_hello(); return 0; } |
1 2 3 4 5 6 7 8 9 10 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <stdio.h> void print_hello(void) { printf("Hello World!\n"); } |
実行結果は以下になります.
override.cのみをビルドして実行すると「Hello!」と表示されます.
これに対して,override.cとoverride2.cをビルドして実行すると「Hello World!」と表示されます.
この理由は,override2.cにあるprint_hello関数が,override.cにあるprint_hello関数をオーバーライドしたからです.
1 2 3 4 5 6 |
$ gcc override.c $ a.out Hello! $ gcc override.c override2.c $ a.out Hello World! |
まとめ
C言語でC++言語の機能を模倣する方法を紹介しました.
具体的には,コンストラクタとデストラクタ,関数のオーバーロード,関数のオーバーライドを解説しました.
C言語を独学で習得することは難しいです.
私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.
私のキャパシティもあり,一定数に達したら終了しますので,今すぐ追加しましょう!
独学が難しいあなたは,元東大教員がおすすめするC言語を学べるオンラインプログラミングスクール5社で自分に合うスクールを見つけましょう.後悔はさせません!