Pebble Coding

ソフトウェアエンジニアによるIT技術、数学の備忘録

clang-format をいい感じにvimで使う

.vimrc設定

Bundle `cjuniet/clang-format.vim'
let g:clang_format_style="{BasedOnStyle: Google, IndentWidth: 4, Standard: C++11}"

以上です。依存するvimプラグインもなくいい感じです。
使い方は整形したい範囲を行選択してCTRL-Kを押すだけ。
シンプルイズベスト。
ルールをカスタマイズする場合はg:clang_format_styleを編集すれば良いです。

github.com


Googleスタイルの内容を知りたい場合は以下でダンプできるようです。
$ clang-format -style=google -dump-config > .clang-format

mac環境でclang-formatをインストールするには
$ brew install clang-format
を実行すればよいです。

C++ 例外クラス

クラスのコンストラクタで異常を上位に返したい場合、例外を用いる以外に術はない。
こんな時は、C++例外クラスをthrowするのが良い。
もちろん例外送出時にはリソースを全て開放しておこう。
C++例外クラスで使えそうなものを上げておく。
いずれも任意の文字列を引数に取れるので、エラー内容を表す適切な英文を設定しておけば良いだろう。

std::exception
ベースとなる例外、以下どれにも属さない場合はこれを使おう

std::logic_error
論理的な事前条件を満たしていないことを表す

std::invalid_argument
引数エラー
logic_errorの子

std::domain_error
演算が定義されている領域の外側のデータが与えられた時に使う
logic_errorの子

std::length_error
長さの制限を超過している
logic_errorの子

std::out_of_range
範囲外の要素にアクセスしようとしている
logic_errorの子

std::runtime_error
簡単には予測できないエラー
んー、どういうこと?

std::range_error
値域エラー
計算結果が型の範囲内で表現できない時、例えばint型で結果がNanになった場合など
runtime_errorの子

std::overflow_error
算術オーバーフロー
まあ使うことはなさそうだ
runtime_errorの子

std::underflow_error
算術アンダーフロー
これも使うことはなさそう
runtime_errorの子

C++11 シングルトン

class Hoge {
public:
    static Hoge& shared()
    {
        static Hoge instance;
        return instance;
    }
private:
    Hoge();
}

C++でシングルトンを作りたい場合は上記のようにすればよい。
C++11より前の仕様では、複数のスレッドからHoge::shared()を呼び出したときにstatic Hoge instanceの部分が2回実行されしまうというデータレースが発生していたが、C++11から1度しか実行されない仕様になった。

Concurrency in Action の第3章に書かれていました。

C++ Concurrency in Action

C++ Concurrency in Action

std::thread 練習帳その1

以下のコードはコンパイルに失敗し、Attempt to use a deleted function となる。

#include <thread>

void update(int& data) {
    data += 2;
}

int main(int argc, const char * argv[]) {

    int data = 11;
    std::thread t(update, data);
    t.join();
    
    printf("%d\n", data);

    return 0;
}


以下のコードはコンパイルできる。
std::refは引数の参照を返す関数テンプレートである。

void update(int& data) {
    data += 2;
}

int main(int argc, const char * argv[]) {

    int data = 11;
    std::thread t(update, std::ref(data));
    t.join();
    
    printf("%d\n", data);

    return 0;
}

macOSにて同一のシンボル名のダイナミックライブラリをロードしたらどうなるか

macOSにて同一のシンボル名を持つダイナミックライブラリをロードしたらどうなるかを検証します。
前提条件として、ダイナミックライブラリはインストールパスに@rpathが指定されていることとします。
絶対パスが指定されている場合は、全く異なる動作をしますが、今回は扱いません。

構成

GreatApplication.app/Contents/MacOS/GreatApplication
GreatApplication.app/Contents/Frameworks/blockA/libblock.dylib
GreatApplication.app/Contents/Frameworks/blockB/libblock.dylib

2つのlibblock.dylibが@rpath指定されているダイナミックライブラリです。
2つとも、-fvisibility=hidden, -fvisibility-inlines-hiddenが指定されてビルドされているとします。

ハンドル=dlopen(ダイナミックライブラリの絶対パス, RTLD_LAZY|RTLD_GLOBAL)
dlsym(ハンドル, "block")
を用います。

結果その1
ハンドルはA, Bで異なる値となります。
block関数は
__attribute__ ((visibility("default")))
指定をしておき、dysymでアドレス取得できるようにしておきます。
block関数のアドレスはA, Bで異なる値となります。
block関数から呼び出した同じ名前のC++クラス名の呼び出しはA, Bそれぞれのモジュール内のものが呼び出されます。

名前空間なしで定義したグローバル変数
int64_t g_variable;
はシンボル自体はローカルですが、A, Bそれぞれのモジュール内の異なるアドレスを指しています。

ちなみに、macOS環境ではRTLD_GLOBALがデフォルトで、linux環境ではRTLD_LOCALがデフォルトです。

ちょっと説明を読んでみましょう。

RTLD_GLOBAL Symbols exported from this image (dynamic library or bundle) will be available to any images build with -flat_names-
pace option to ld(1) or to calls to dlsym() when using a special handle.

RTLD_LOCAL Symbols exported from this image (dynamic library or bundle) are generally hidden and only availble to dlsym() when
directly using the handle returned by this call to dlopen().

RTLD_GLOBAL このダイナミックライブラリまたはバンドルイメージからエクスポートされたシンボルは(グローバル、ローカル共に)、-flat_names-paceオプションを与えられたld(1)でビルドされた全てのイメージまたは指定されたハンドルで使う時にdlsym()を呼び出すことにより利用できる。

RTLD_LOCAL このダイナミックライブラリまたはバンドルイメージからエクスポートされたシンボル(グローバル、ローカル共に)は一般的に隠され、dlopen()を呼び出して返ったハンドルを直接使用する場合のdysym()によってのみ利用できる。

なんだか良くわかりませんね。

試しにこの状態のまま-fvisibility-hidden, -fvisibility-inlines-hiddenを外してビルドしたらどうなるかを試してみましたが、
全く変化なしです。

結局分かったことは、
インストールパスが@rpathのダイナミックライブラリをdlopenした時は、RTLD_GLOBAL, RTLD_LOCALどちらを指定した時も、
同じシンボル名であっても関数のアドレスやグローバル変数のアドレスがコンフリクトすることはなく、それぞれのものが利用される。
ということです。
シンボル名のコンフリクトに関しては@rpath指定されていれば、オールOKという感じですね。

ちなみに、-fvisibility-hidden, -fvisibility-inlines-hiddenを指定しなかった場合は、
__attribute__ ((visibility("default")))を指定しなかった関数もdlsym()でアドレスを取得できるようになります。
意図せず呼び出されるリスクを減らしたいという場合は指定しておいた方が良さそうです。