私とc++ in 例外安全day

44
私とC++ In 例外安全Day

Upload: tatsuya-ishikawa

Post on 22-Jul-2015

860 views

Category:

Documents


3 download

TRANSCRIPT

Page 1: 私とC++ in 例外安全day

私とC++In 例外安全Day

Page 2: 私とC++ in 例外安全day

自己紹介

石川達也

株式会社Codeer代表取締役

C, C++, C#, Java

Page 3: 私とC++ in 例外安全day

Exceptional C++ 読書会で

常識のある人間です。

Page 4: 私とC++ in 例外安全day

Exceptional C++ 読書会で

常識のある人間です。

・gotoを使わない。

Page 5: 私とC++ in 例外安全day

Exceptional C++ 読書会で

常識のある人間です。

・gotoを使わない。・Duff ‘s deviceなどもっての外。

Page 6: 私とC++ in 例外安全day

Exceptional C++ 読書会で

常識のある人間です。

・gotoを使わない。・Duff ‘s deviceなどもっての外。・VC++をDisらない。

Page 7: 私とC++ in 例外安全day

今日は、体験談を語らせてもらいます。

参考にするもよし、マサカリ投げるもよし。(優しく)

Page 8: 私とC++ in 例外安全day

アジェンダ

・例外

・テンプレート活用例・関数ポインタキャスト・アロケータ記憶・AOPもどきログ

Page 9: 私とC++ in 例外安全day

例外

・リソース系

・プログラムミス系

Page 10: 私とC++ in 例外安全day

注)OutOfMemory場合によって・・・

リソース系or

プログラムミス系

例外

Page 11: 私とC++ in 例外安全day

・リソース系FileIO他の機器との通信

例外安全に作ります。最終的には、catchして適切に処理。て言うか、例外とは思っていない。

例外に関する姿勢

Page 12: 私とC++ in 例外安全day

・プログラムミス(ASSERT含む)

問題はこれ。

例外に関する姿勢

Page 13: 私とC++ in 例外安全day

std::vector<int> buf;・・・buf[index] = 100; //範囲外とか。

例外(プログラムミス)

Page 14: 私とC++ in 例外安全day

swtich(val) {・・・default://予期せぬ値とか。

ASSERT(FALSE);break;

}

例外(プログラムミス)

Page 15: 私とC++ in 例外安全day

可能な限りの終了処理をしてプロセスを停止させる。

注)お客様との調整が必須。

例外(プログラムミス)

Page 16: 私とC++ in 例外安全day

堅牢性と正当性は相反する。

例外(プログラムミス)

Page 17: 私とC++ in 例外安全day

size_t index = ・・・;std::vector<int> buf;・・・ASSERT_DEAD(index < buf.size());buf[index] = 100;

例外(プログラムミス)

Page 18: 私とC++ in 例外安全day

swtich(val) {・・・default:

ASSERT_DEAD(FALSE);}

例外(プログラムミス)

Page 19: 私とC++ in 例外安全day

例外(プログラムミス)

そのまま動作させると・・・

・永続データを壊すかも。

・原因特定困難な不具合になる。

・使われない復帰コードでコードが腐敗し、さらなる不具合を招く。

Page 20: 私とC++ in 例外安全day

例外(プログラムミス)

潔く終了すると・・・。

・変なデータができない。

・不具合解析が楽。

・コードはスッキリ。↓

結果的にリリース品質が劇的にUP。

Page 21: 私とC++ in 例外安全day

例外(プログラムミス)

あ、

「この辺は無理に終了しなくても・・・」とかは無し。

その切り分けは難しく、だんだん、生き恥をさらす方向に行くので。

Page 22: 私とC++ in 例外安全day

Page 23: 私とC++ in 例外安全day

テンプレート活用例

①関数ポインタキャスト

Page 24: 私とC++ in 例外安全day

FARPROC GetProcAddress(

HMODULE hModule, LPCSTR lpProcName);

おなじみ、DLL関数取得。

①関数ポインタキャスト

Page 25: 私とC++ in 例外安全day

関数のロードが面倒・・・。

typedef void (__stdcall *FuncType)(int value);FuncType Func = NULL;

Func = (Func)GetProcAddress(h, “Func”);

①関数ポインタキャスト

Page 26: 私とC++ in 例外安全day

ちょっとしたユーティリティーを作っておく。

template<typename T>void GetEx(HMODULE hModule, LPCSTR name,

T& func) {

func = (T)GetProcAddress(h, name);}

#define GETPROC(h, f) GetEx(h, #f, f)

①関数ポインタキャスト

Page 27: 私とC++ in 例外安全day

スッキリ!

void (__stdcall *Func)(int value);

GETPROC(h, Func);

①関数ポインタキャスト

Page 28: 私とC++ in 例外安全day

①関数ポインタキャスト

ついこないだも、使っちゃいました。

→コードへ。http://www.codeer.co.jp/technical-notes/NativeAndNet

Page 29: 私とC++ in 例外安全day

②アロケータ記憶

DLLで、ランタイム異なることありますよね・・・?

(゚◇゚)共通にすれば済む話・・・。

Page 30: 私とC++ in 例外安全day

Exe dll

メモリ管理メモリ管理

そうすると、ヒープも違うんですよね。

②アロケータ記憶

混ぜるな危険

Page 31: 私とC++ in 例外安全day

②アロケータ記憶

でも、動的なコンテナ使いたいんです。

Page 32: 私とC++ in 例外安全day

②アロケータ記憶

で、コンテナ作りました。

Σ⊂( ̄□ ̄~j

Page 33: 私とC++ in 例外安全day

template<typename T>

class ArrayAllocator

{

public:

T* (__stdcall *New)(unsigned int size);

void (__stdcall *Delete)(T* ptr);

ArrayAllocator() : New(NewCore),Delete(DeleteCore){}

②アロケータ記憶

Page 34: 私とC++ in 例外安全day

private:

static T* __stdcall NewCore(unsigned int size)

{

return new T[size];

}

static void __stdcall DeleteCore(T* ptr)

{

if (ptr)

{

delete[] ptr;

}

}};

Page 35: 私とC++ in 例外安全day

//配列template<typename T>

class Array

{

ArrayAllocator<T> _heap;

・・・};

//文字列class WString

{

Array<WCHAR> _core;

・・・};

Page 36: 私とC++ in 例外安全day

struct Data

{

Array<int> ar;

WString str;

};

void _stdcall Func(Array<Data>& a)

{

//データを詰める}

入れ子もOK!

Page 37: 私とC++ in 例外安全day

{Array<Data> a;Func(a);

}抜けたら、それぞれのメモリ管理のdeleteが呼ばれる。

Void Func(Array<Data>& a){

a.resize(100);a[99].ar.resize(100);a[99].str = L“abc”;

②アロケータ記憶

Page 38: 私とC++ in 例外安全day

③AOPもどきでログを仕込む

関数の呼び出し順をログ出力したことありますよね?

Page 39: 私とC++ in 例外安全day

③AOPもどきでログを仕込む

全部に、いちいち仕込むのは面倒!

横断的(AOP)に処理したい!

Page 40: 私とC++ in 例外安全day

template <typename Ret, int fileNo, int lineNo>

struct AOPLog {

//DLL関数の型typedef Ret (__stdcall *FuncType)();

//ログstatic std::string& Log()

{ static std::string log; return log; }

//DLL関数static FuncType& Func()

{ static FuncType func; return func; }

//ログ出力と呼び出しstatic Ret __stdcall Invoke()

{ Print(Log()); return Func(); }

};

Page 41: 私とC++ in 例外安全day

//戻り値voidで特殊化template <int fileNo, int lineNo>

struct AOPLog<void, fileNo, lineNo> {

typedef void(__stdcall *FuncType)();

static std::string& Log()

{ static std::string log; return log; }

static FuncType& Func()

{ static FuncType func; return func; }

static void __stdcall Invoke()

{ Print(Log()); Func(); }

};

Page 42: 私とC++ in 例外安全day

//ログ入り関数ロードtemplate<int fileNo, int lineNo, typename Ret>

void MakeLog(HMODULE h,

Ret (__stdcall *&func)(), LPCSTR name)

{

typedef AOPLog<Ret, fileNo, lineNo> T;

if (s_logMode) {

T::Func() =

(T::FuncType)::GetProcAddress(h,name);

T::Log() = funcName;

func = T::Invoke;

} else {

func = (T::FuncType)::GetProcAddress(h,name);

}

Page 43: 私とC++ in 例外安全day

//今のを引数が必要分繰り返す。//BOOST_PPとか。

//でヘルパマクロ#define LOG_GETEX(fileNo, h, f) \

MakeLog<fileNo, __LINE__>(h, f, #f)

//使うところは、すっきり。void (__stdcall *Func)();

LOG_GETEX(0, h, Func);

Page 44: 私とC++ in 例外安全day

そろそろ時間!

ご清聴ありがとうございました。