C_Cpp
Last updated
Last updated
新しくC/C++を使って書くならRustで書くことを検討したほうがいい
理由
モダン
安全
周辺ツールが標準で組み込まれている
コンパイルエラーが人にやさしい
Effective C++
awesome cpp
typedef static定数 static変数 static関数 メンバ変数 コンストラクタ デフォルトコンストラクタ コピーコンストラクタ その他のコンストラクタ デストラクタ メンバ関数 内部クラス・構造体
Configファイルをインストールに含める
INSTALL(DIRECTORY "config" DESTINATION ${INSTALL_CONFIG} COMPONENT "configurations")
デフォルトのインストール先を変更する
cscope -R -b -P pwd
/
:set csre # -Pのパスを読み込む設定
:cscope add cscope.out
Vimでは、:cscope find search type search string という形式でCscopeの検索コマンドを実行する(cscope findの部分は:cs fで代用可)。search type(検索タイプ)には、以下のものがある。
symbol or s -- すべてのシンボル参照を検索
global or g -- グローバル定義を検索
calls or c -- 指定した関数の呼び出し箇所を検索
called or d -- 指定した関数が呼び出す関数を検索
text or t -- テキスト検索を実行
file or f -- ファイルを開く
include or i -- 指定ファイルをインクルードしているファイルを検索
コマンドまとめ
マクロを参照する
info macro
変数の定義を知りたい
whatis
ptype
structの場合は
ptype struct xxx
とする必要がある。
TUIモード
入る C-x C-a、<
出る C-x a
トグル C-x A
長いメッセージが...になる
set print elements 0
Missing separate debuginfos, use: debuginfo-installが出る
sudo yum install -y yum-utils
sudo debuginfo-install --nogpgcheck --enablerepo debug xxx
ソースコードが表示できない
show directories
でソースコードの格納ディレクトリがないことを確認する
directory ディレクトリ名
で追加する。
単数形になっているところに注意する。
リモートデバッグ
直接ポートが解放できない環境でリモートデバッグする
install gdb gdb-gdbserver
gdbserver --multi localhost:9999 <progname>
ローカルフォワードする ssh -L 9999:localhost:9999 remote-server
gdb
(gdb) target remote localhost:9999
デバッグオプションがついているか調べる
objdump --syms
シンボルが表示されるかどうか。No symbolsだとついていない
ただしgdb上で確認したほうが確実(Readができない場合があるため) (gdb) info sharedlibrary
or
エラーメッセージの見方
EProfiler
GProf 最適化オフ(-O0)らないと正しく表示されない
OProfile
Valgrind(callgrind) 最適化オフ(-O0)らないと正しく表示されない
Very Sleepy
gperftools
perf
関数の呼出しグラフを可視化
メモリ関連のチェックツール
clang-format -i -style="{BasedOnStyle: Google, IndentWidth: 4, Standard: C++11}" source-file
ディレクトリ以下をFormat
find . -regex '.*\.\(cpp\|hpp\|cc\|cxx\)' -exec clang-format -style=file -i {} \;
オプション設定
Complexity 複雑度(McCabeのサイクロマチック数)
10 以下であればよい構造
30 を越える場合,構造に疑問
50 を越える場合,テストが不可能
75 を越える場合,いかなる変更も誤修正を生む原因を作る
cccc 実行コマンド
cccc $(find . -name "*.c" -o -name "*.h" -o -name "*.*pp")
コンパイルオプションを変更
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -Wall --coverage")
プロジェクトルートにファイルを持ってこないとうまくいかなかった
find build -type f \( -iname \*.gcno -or -iname \*.gcda \) -exec cp {} . \;
gcovr -r .
readelf --relocs foo.o | egrep '(GOT|PLT|JU?MP_SLOT)'
コストを気にするならshared_ptrで返したほうがいいが、moveセマンティクスもあるためそのまま返したほうが読みやすいのではと思っている
JavaやC#と違ってnamespaceを使うのが一般的な模様。
できない
typeidを使う
その1
その2
各gccのバージョンでのstd=c++の対応状況
共有ライブラリから別の共有ライブラリをリンクすり
メンバ変数をコールバックで呼び替える
引数argc, argvを作る
共有ライブラリのパスを取得する
gcc --print-search-dirs | ruby -ne 'puts $_.split(/:|=/).select{|e| e =~/^//}.map{|e| File.expand_path(e) }' | sort
デフォルトのインクルードパスを調べる
gcc ~~xc -E -v ~~
コピーコンストラクタとoperator=の重複コードを減らす
-gオプションが付いているか確認する
readelf --debug-dump=line XXX
なにか出力される
objdump -W XXX
Contents of the .debug_aranges section:
Contents of the .debug_pubnames section:
Contents of the .debug_info section:
がある
nm -a XXX
< 0000000000000000 N .debug_abbrev
< 0000000000000000 N .debug_aranges
< 0000000000000000 N .debug_info
< 0000000000000000 N .debug_line
< 0000000000000000 N .debug_loc
< 0000000000000000 N .debug_pubnames
< 0000000000000000 N .debug_pubtypes
< 0000000000000000 N .debug_st
メモリ使用量を計測する
GNUのtimeコマンドを利用する
フルパスで
/usr/bin/time -f "%M KB" ls
すべて計測
/usr/bin/time -f "%Uuser %Ssystem %Eelapsed %PCPU (%Xtext+%Ddata %Mmax)k %Iinputs+%Ooutputs (%Fmajor+%Rminor)pagefaults %Wswaps"
ライブラリが32bitか64bitか調べる
共有ライブラリ
file libhoge.so
スタティックライブラリ
objdump -f libfoo.a | grep ^architecture
i386 ->32bit
i386:x86-64 ->64bit
gccで不要な関数をリンク時に自動的に除外
-Wl,--gc-sections
gccでlibがプレフィックスになっていない静的ライブラリにリンクする方法
cspice.aなどのスタティックライブラリにリンクする
gcc main.o -L/path/to/foo -l:foo.a
2016年、C言語はどう書くべきか
巨大な配列を使うには
1.static宣言する
2.ヒープ領域を用いる配列
void ポインタ
void ポインタはキャストしないのがK&Rに記載されている言語使用である。(voidポインタは定義された際に暗黙でキャストされるため)
gccのバージョン向上によりvoidをcharとして扱うようになってしまった。そのため,(int*)malloc(sizeof(int))とするような記述が増えてきている。
実際には(int*)は不要である。
return exitの違い
atexitの動作が異なってくる
returnの場合mainが終了しているので、static変数も開放される。exitの場合mainが終了していないため変数にアクセスできる
ツール名
説明
cccc
メトリクス計測
cppcheck
静的解析
ユニットテストツール
gprof
プロファイリング
doxygen
ドキュメントツール
以下の部分利便性の観点からgoogle C++スタイルと異なるようにする
定数:すべて大文字_つなぎ →Cの命名規則と似ていて、目立つため
thisを必ずつけること →補完が効きかつ、名前の衝突が発生しないため。可読性が下がるが慣れれば大丈夫
staticメンバ変数の場合はクラス名をつける。
static const(final)の場合は付けなくてもよい。
log4jのstatic変数loggerも例外とする。
メンバ関数名:小文字で処理名のちキャメル →Qtで見やすかったから
コメントはDoxygenQtスタイルで
static変数は必ず定義時に初期化する
複数値が入るものは複数形にすること
以下のようなスペースのスタイルとする
~~~
void□write(int□type,□int□mode)□{
if□(type□==□TYPE_NORMAL)□{
git.insert("help").delete("/")
.trim();
}
else□{
} }
int i_len=strlen(szp_del); if( i_len > l_max ){ i_len = l_max; }
// xxx / xxx */ </code>
// void getCurrentTime( char szp_time, int i_option ){ time_t timer=time(NULL); // 暦変換 struct tm st_lct=localtime(&timer); // 地方時変換
// 引数チェック if(szp_time==NULL){ errlog; return; }
// strftime(szp_time,D_TIME_LENGTH,"%Y%m%d%H%M%S",st_lct); # <- 一時的なコメントアウトの例 //あとで変更すること! # <- テンポラリなコメント strftime(szp_time,D_TIME_LENGTH,"%Y%m%d",st_lct); return; }
</code>
const int & MyClass::getFoo() { return m_foo; } void MyClass::setFoo(const int & foo) { m_foo = foo; }
int MyClass::getFoo() { return m_foo; } // Removed 'const' and '&' void MyClass::setFoo(const int foo) { m_foo = foo; } // Removed '&'
if (fabs(a - b) < DBL_EPSILON) { }
int main() { int a = 10; double b = static_cast<double>(static_cast(&a));
std::cout << *b << std::endl; }
int x[7][6]; int n1 = sizeof(x) / sizeof(x[0]); / 行数すなわち7が得られる / int n2 = sizeof(x[0]) / sizeof(x[0][0]); / 列数すなわち6が得られる /
bool MyClass::operator<(const MyClass &rhs) { return a < rhs.a; }
std::sort(vec.begin(), vec.end()); Second option:
bool CompareMyClass(const MyClass &lhs, const MyClass &rhs) { return lhs.a < rhs.a; // this function will need to be declared friend if a is private }
std::sort(vec.begin(), vec.end(), CompareMyClass); Third option:
struct MyFunctor { bool operator()(const MyClass &lhs, const MyClass &rhs) const { return lhs.a < rhs.a; } };
~~~
std::sort(vec.begin(), vec.end(), MyFunctor());
デフォルトコンストラクター:X()
コピーコンストラクター:X(const X &)
コピー代入:X &operator=(const X &)
ムーブコンストラクター:X(X &&)
ムーブ代入:X &operator=(X &&)
デストラクター:~X()
プログラマーがこれらの関数を宣言すると、コンパイラーは次の規則に基づいて生成しない。
プログラマーがコンストラクターを一つでも宣言すれば、コンパイラーはデフォルトコンストラクターを生成しない。
プログラマーがコピー関数、ムーブ関数、デストラクターの何れか一つでも宣言すれば、コンパイラーはコピー関数、ムーブ関数、デストラクターの何れも生成しない。
連想配列がない(マップかenumで代用)
コンストラクタでエラー処理しづらい
STLの頭が弱い
初期化がコンストラクタのみ
enumにアクセス指定子が指定できない(C11からOK)
POD以外をstatic変数にできない(開放処理が不定をなるため)
STLが直感的でない
mapの要素にアクセスするときにconstでとれない。(C11からOK)
vectorの初期化が{0,1}とかじゃできない(C11からはOK)
同名、違戻り値の関数を作るのが大変
メンバ関数をコールバック関数にできない(staticならできる。C11からはstd::function,std::bindでできる)
const char constにすると大丈夫な模様。
一つ目のポインタの値の書き換えはガードされるけど、ポインタのポインタの値の書き換えがガードできないからエラーと理解。
/usr/bin/ld: /usr/local/lib/libboost_regex.a(regex.o): relocation R_X86_64_PC32 against undefined hidden symbol
_ZTCN5boost10wrapexceptISt13runtime_errorEE0_NS_16exception_detail10clone_implINS3_19error_info_injectorIS1_EEEE' can not be used when making a shared object`
boostもclangでコンパイルするとうまくリンクできた
/etcか/usr/local/etc
class Car : private Engine
テンプレートメンバ関数をpublicで作った際に起こりがち
undefined reference to
解決するには、
実装をヘッダに書く
ソースににインスタンス生成だけする
template int func <int> (int);
付けられない。仕様書で決まっている。
ctags -R --links=no xxx
namespaceがよい。メソッド名でコンフリクトしないため。
From a code readability standpoint, it is probably better in my opinion to use the #2 method for this reason:
You can be using multiple namespaces at a time, and any object or function written below that line can belong to any of those namespaces (barring naming conflicts). Wrapping the whole file in a namespace block is more explicit, and allows you to declare new functions and variables that belong to that namespace within the .cpp file as well
参考:
基本的には"Google C++スタイル": を守る
clangが悪いみたい。2020/03/10現在まだ解決してなさげ。