clang でコーディング規約を調べられるようだ2017年12月04日 14時00分11秒

ClangFormatにてコーディング規約を調べられるようだ。
ClangFormat describes a set of tools that are built on top of LibFormat. It can support your workflow in a variety of ways including a standalone tool and editor integrations.

clang-format is located in clang/tools/clang-format and can be used to format C/C++/Java/JavaScript/Objective-C/Protobuf code.

取り敢えず、メモ。

テンプレート基底クラスのエラーを修正2017年04月21日 13時41分32秒

C++ でテンプレートクラスを次の様に基底クラスにするとエラーがでる。
% cat template_base_class.cpp
#include <list>

template<typename T> class Vector : public std::vector<T>
{
public:
  int At(int i)
  {
    return at(i);
  }
};

gcc だと、 error: there are no arguments to `at' that depend on a template parameter, so a declaration of `at' must be available の様なエラーがでて、-fpermissive を渡すと、無視させて進めることが出来る。

llvm だと

template_base_class.cpp:8:14: error: use of undeclared identifier 'at'
      return at(i);
等となる。

なお、Solaris と AIX のコンパイラは大丈夫なようだ。

C++ もテンプレートなどが発展して、名前解決の規則が厳しくなり、しっかりと分かりやすくテンプレート基底クラスから、at が使われると明示してやる必要がある。

修正の仕方は三つ。

  1. this->at(i) として、メンバ関数を呼び出していると明示する。
  2. std::vector<T>::at(i) として、基底クラスから呼び出しているのを明示する。
  3. using std::vector<T>::at(i) をいれて、at は vector クラスのメンバ関数と明示する。
後の二つの方法だと、仮想関数を呼び出したい時には、基底クラスを呼び出すとしている為、うまく動作しない。今回は、std::vector を基底クラスにしたので、at 関数に virtual を付けて仮想関数にすることは出来ないので問題はないが。そこらも踏まえて修正するのであれば、this を用いた方法が最適だろう。

C++ の宣言で = delete2017年02月16日 22時20分58秒

C++11 での新しい構文。関数宣言で「= delete」とすることで、明示的に、操作を禁止することが出来る。
class C
{
    C& operator=( const C& ) = delete;
    C( const C& ) = delete;
};
これはクラスインスタンスの複製を禁止する。

以前は、宣言のみをしてコンパイラの自動生成を抑制し、実装を行わずに、リンクエラーを起こさせて禁止事項だと伝えるのが精いっぱいだった。そのため、宣言の周辺にはコメントとして、意図的に実装していない故が、記述されていた。

class C
{
    C& operator=( const C& ); // no-copy
    C( const C& ); // no-copy
};

自動生成される C++ の関数 - C++98/032016年12月03日 16時00分28秒

C++98/03 では、以下の四つの関数は、宣言されていない時に、コンパイラが自動生成する。
  1. コンストラクタが宣言されていない場合の、デフォルトコンストラクタ
  2. コピーコンストラクタ
  3. コピー代入演算子
  4. デストラクタ

make_shared の方が効率が良いようだ2016年10月25日 17時50分37秒

#include <memory> にある shared_ptr と make_shared。make_shared は shared_ptr を生成する。下記の二つのメモリの割当てた後の動作は同じ。make_shared を使うと、shared_ptr と対象のオブジェクトが同一メモリに生成されるので二回メモリの割り当てを行う形式よりも若干効率が良い。
std::shared_ptr<int> ptr1 = std::make_shared( 3 );
std::shared_ptr<int> ptr2( new int( 4 ) );
また、new での結果を直接渡す上記の表記だと、shared_ptr の生成に失敗するとメモリリークを起こす。一旦ポインタ型の変数に代入してからなら、メモリりークは回避できるが、大幅な記述量の増加になる。やはり、make_shared の方が便利だ。

std::bad_alloc を捕まえる2016年01月14日 13時51分08秒

bad_alloc 例外を捕まえたら「bad allocation」が表示された。
catch( std::bad_alloc& ex )
{
    std::cout << ex.what() << std::endl;
}

Solaris C++ コンパイラがメモリ不足で落ちる時のエラー2015年11月05日 13時06分18秒

Solaris C++ コンパイラがメモリ不足で落ちる時のエラー。
>> Assertion:  DBGGEN ERROR: FILE="../src/dbg_alloc.c", LINE=%23, Ran out of
memory [DBG_GEN 5.3.3] (../lnk/dbg_dbggen.cc, line 4518)
    while processing .cpp at line 0.

Solaris のコンパイラは、コードの不正を見付けて終了する時には、オブジェクトファイルをしっかりと削除して終了してくれるが、このエラーの時は、コンパイラ自体が異常終了するため、オブジェクトファイルが消されない。そのため、make を使うと、二度目はファイルが存在するため通過してしまうが、リンカーが失敗する。

前回

static 関数や static 変数をヘッダーに2015年09月19日 12時40分29秒

何故だか知らないが、チョコチョコ見付ける摩訶不思議な関数と変数。static 関数や static 変数が .h のヘッダファイルに書かれているのを良く見付ける。あちこちで見たことがあるし、消しても消しても誰かが復活させる。

static 関数や static 変数をヘッダファイルに書くのは大間違い。

C++ ではあれこれと新しい意味も増えた。基本の C 言語由来の static はこうだ。static 修飾子を付けると、関数や変数がコンパイル単位内でしか、見えなくなるのだ。extern を付けて、広域で使い回すのと逆になる。

さて、問題はここからだ。ヘッダーファイルのコンパイル単位はそれを #include する各々のファイルだ。何が起きるのかというと、ヘッダーファイルに書かれた static 関数と static 変数は各々のオブジェクトファイルに生成される。

つまり、ヘッダーファイルに書かれた static 関数と static 変数はそれを読み込むファイルの数だけ生成される事になる。同じ関数がいくつも定義されているのに何故エラーにならないかと言うと static 関数だからだ。staic 関数なので、各々の .c や .cpp を越えてリンカーが参照させることは無い。そのため、シンボルの重複が起きないのだ。ヘッダファイルの内容と .c や .cpp ファイルの内容はプリプロセッサに処理されてからコンパイラに渡される。そのため、コンパイラが見るのは大きなファイルが一つだけ。プリプロセッサにどのファイルからどこの行が読まれたかの情報は組み込まれているが、それらを元に、static 関数がヘッダーファイルにあるのを警告するコンパイラは、知る限りでは無い。

static 関数や static 変数をヘッダファイルに見付けたら、即刻修正の対象だ。厄介なことに、コンパイラやリンカーはこの問題を検出しない。また、間違ってはいても、問題の無い動作をすることが多いのも特徴だ。具体的な問題としては、コンパイルに掛かる時間が増える。リンカーも時間が掛かる。実行形式のファイルの大きさが増える。実行時のメモリの利用量も増えるなど多岐にわたる。

Solaris C++ コンパイラがメモリ不足で落ちる2015年08月25日 12時44分42秒

この度、Solaris の C++ コンパイラがメモリ不足で落ちる自体になった。ほぼ 2GB のメモリを割り当てた後に、コンパイラが異常終了してしまう。

コンパイラの動作と、コードを見ていると、テンプレートの処理に大量のメモリが必要になるようだ。

あまり作法良く書かれたコードでは無いので、インライン関数やテンプレートが乱用されている。インライン関数自体は害では無いのだが、インラインにするために大量のヘッダーを #include しているため、どのヘッダーを読み込んでも大量のインライン関数に遭遇し、それに加えて大量のテンプレートが散りばめられている。

ヘッダーはフィルから綺麗に書き直すのが正攻法なのだが、時間と手間的にも無理。目の前のコンパイルできないソースから処理していくしかない。単純だが、最初に行なうのは cpp ファイルの分割。なるべく、必要なヘッダー毎に組になるように分ける。その後に、徐々に分割するしか無い。

Purify でプログラムが起動しない2015年08月04日 11時10分12秒

Purify はオブジェクトファイルに検査の為のコードを埋め込み、実行時の各種問題の検査をしてくれる。プログラムのリンクの時間とプログラムの実行時間がとてつもなく遅いのが珠に傷なのだが、その検知力は他を追随しない。ゆっくりと時間を掛けてプログラムを生成し、時間に余裕をもってテストを行なう。

さて、久方に、Purify を使った。しかし、Purify を組み込んだプログラムが起動しない。エラーメッセージがいかの様に出る。

the two libraries might interfere with each other, leading to unpredictable results. This happens occasionally when you share the cache between multiple machines running the same version of the OS.
要約すると、「プログラムが同名の複数の実装のライブラリを組み込んでいる。」となる。

Purify を用いないとプログラムは起動する。Purify だと駄目。

ldd などを用いて詳しく調べると、libm が SunStudio とシステムの両方に参照されていた。プログラムは起動こそするものの、どちらの実装が使われるのかはそれこそ運次第。libm は数学系のライブラリなので枯れてはいるので、おそらく問題が表面化しないのだろう。

もし上記のエラーで困ったのだったら、-L にて指定されているライブラリのパスを調べたら良いだろう。