Imperfect C++読書ノート(二)
12830 ワード
移植可能な虚関数テーブル
C++は適切なモジュールではありません.インタフェース言語では、ABI/OAB(ApplicationBinary Interface/Objects Across Borders)をサポートするためにかなりの努力が必要であり、使用者は厳しい制約を受けなければなりません.Win 32 COMに詳しい場合は、すぐに心を奪われることになります.
インタフェースコード:
サービス側コード:
クライアントコード:
synchronizedキーワード
Javaにsynchronizedキーワードがあります.NETにはロックキーがあり、臨界領域を守るために使用できます.C++標準はスレッドについてほとんど一言も話さず、自然に関連施設もない.少しのマクロでも簡単に同じ効果が得られます.(lock_scopeはノート(一)のドメインガードクラスを参照してください)
もしあなたが私と同じようにSYNHROIZEが好きでないならEND()セクションは、synchronizedやlockに比べてそれほど優雅ではないようですが、マクロを定義するには、小さなテクニックを使用します.
顧客コード:
上記のコードは、JAVAに詳しい方は親切ではないでしょうか.しかし、オブジェクトのタイプが自動的に導出されず、2つのパラメータを手動で提供しなければならないという小さな残念な点があります.
ここでは、同じ役割ドメインに2つの同期セグメントが存在する場合、forループ処理が有名なVC 6.0のような古いコンパイラの「標準」ではない場合、「再定義__lock_」が報告されることに注意してください.に表示されます.したがって、著者らは、各「_lock_」を確実にするための移植可能なソリューションを提供する.すべてが違います.
しかし、著者らは、VC 6.0のような古いコンパイラのループ変数の解放が、臨界コードセグメントの右括弧ではなく、役割ドメイン全体の終了時に遅延することを忘れているようだ.そのため、この解決策は間違っている.
ローカル静的オブジェクト
関数局所静的オブジェクトの初期化に存在する固有の競合条件は非常に重要な問題である.だから無視されてはいけない.しかし、それは非常に珍しい状況でもある.その希少性を利用して,スピン反発体に基づいた極めて優雅な解を与えることができる.スピン反発体自体は原子操作に依存し,そのオーバーヘッドは非常に低い.
C++は適切なモジュールではありません.インタフェース言語では、ABI/OAB(ApplicationBinary Interface/Objects Across Borders)をサポートするためにかなりの努力が必要であり、使用者は厳しい制約を受けなければなりません.Win 32 COMに詳しい場合は、すぐに心を奪われることになります.
インタフェースコード:
// Interface.h
//
struct IObject;
struct IObjectVTable
{
void (*SetName)(struct IObject * obj, char const * s);
char const * (*GetName)(struct IObject const * obj);
};
struct IObject
{
struct IObjectVTable * const vtable;
#ifdef __cplusplus
protected:
IObject(struct IObjectVTable * vt)
: vtable(vt)
{}
~IObject()
{}
public:
inline void SetName(char const * s)
{
assert(NULL != vtable);
vtable->SetName(this, s);
}
inline char const * GetName() const
{
assert(NULL != vtable);
return vtable->GetName(this);
}
private:
IObject(IObject const & rhs);
IObject & operator=(IObject const & rhs);
#endif /* __cplusplus */
};
extern "C" IObject * createObject();
extern "C" void destroyObject(IObject *);
サービス側コード:
// Server.cpp
//
class Object : public IObject
{
public:
virtual void SetName(char const * s)
{
m_name = s;
}
virtual char const * GetName() const
{
return m_name.c_str();
}
#ifndef POAB_COMPILER_HAS_COMPATIBLE_VTABLES
public:
Object()
: IObject(GetVTable())
{}
Object(Object const & rhs)
: IObject(GetVTable())
, m_name(rhs.m_name)
{}
private:
static void SetName_(IObject * this_, char const * s)
{
static_cast<Object*>(this_)->SetName(s);
}
static char const * GetName_(IObject const * this_)
{
return static_cast<Object const *>(this_)->GetName();
}
static IObjectVTable * GetVTable()
{
static IObjectVTable s_vt = MakeVTable();
return &s_vt;
}
static IObjectVTable MakeVTable()
{
IObjectVTable vt = { SetName_, GetName_ };
return vt;
}
#endif /* !POAB_COMPILER_HAS_COMPATIBLE_VTABLES */
private:
std::string m_name;
};
クライアントコード:
// Client.cpp
//
int _tmain(int argc, _TCHAR* argv[])
{
IObject * pObj = createObject();
pObj->SetName("Matthew Wilson");
std::cout << "name: " << pObj->GetName() << std::endl;
destroyObject(pObj);
return 0;
}
synchronizedキーワード
Javaにsynchronizedキーワードがあります.NETにはロックキーがあり、臨界領域を守るために使用できます.C++標準はスレッドについてほとんど一言も話さず、自然に関連施設もない.少しのマクロでも簡単に同じ効果が得られます.(lock_scopeはノート(一)のドメインガードクラスを参照してください)
#define SYNCHRONIZED_BEGIN(T, v) \
{ \
lock_scope<T> __lock__(v);
#define SYNCHROIZED_END() \
}
もしあなたが私と同じようにSYNHROIZEが好きでないならEND()セクションは、synchronizedやlockに比べてそれほど優雅ではないようですが、マクロを定義するには、小さなテクニックを使用します.
#define synchronized(T, v) \
for (synchronized_lock<lock_scope<T> > __lock__(v); \
__lock__; __lock__.end_loop())
template<typename T>
struct synchronized_lock : public T
{
public:
template<typename U>
synchronized_lock(U & u)
: T(u)
, m_bEnded(false)
{}
operator bool () const
{
return !m_bEnded;
}
void end_loop()
{
m_bEnded = true;
}
private:
bool m_bEnded;
};
顧客コード:
synchronized(Object, obj)
{
... //
}
... //
synchronized(Object, obj)
{
... //
}
上記のコードは、JAVAに詳しい方は親切ではないでしょうか.しかし、オブジェクトのタイプが自動的に導出されず、2つのパラメータを手動で提供しなければならないという小さな残念な点があります.
ここでは、同じ役割ドメインに2つの同期セグメントが存在する場合、forループ処理が有名なVC 6.0のような古いコンパイラの「標準」ではない場合、「再定義__lock_」が報告されることに注意してください.に表示されます.したがって、著者らは、各「_lock_」を確実にするための移植可能なソリューションを提供する.すべてが違います.
#define concat__(x, y) x ## y
#define concat_(x, y) concat__(x, y) // __LINE__
#define synchronized(T, v) \
for (synchronized_lock<lock_scope<T> > concat_(__lock__,__LINE__)(v); \
concat_(__lock__,__LINE__); concat_(__lock__,__LINE__).end_loop())
しかし、著者らは、VC 6.0のような古いコンパイラのループ変数の解放が、臨界コードセグメントの右括弧ではなく、役割ドメイン全体の終了時に遅延することを忘れているようだ.そのため、この解決策は間違っている.
ローカル静的オブジェクト
関数局所静的オブジェクトの初期化に存在する固有の競合条件は非常に重要な問題である.だから無視されてはいけない.しかし、それは非常に珍しい状況でもある.その希少性を利用して,スピン反発体に基づいた極めて優雅な解を与えることができる.スピン反発体自体は原子操作に依存し,そのオーバーヘッドは非常に低い.
class spin_mutex
{
public:
explicit spin_mutex(int * p = NULL)
: m_spinCount((NULL != p) ? p : &m_internalCount)
, m_internalCount(0)
{}
void lock()
{
for (; 0 != atomic_write(m_spinCount, 1); sched_yield())
{} // PTHREAD seched_yield()
}
void unlock()
{
atomic_set(m_spinCount, 0);
}
private:
int * m_spinCount;
int m_internalCount;
private:
spin_mutex(spin_mutex const & rhs);
spin_mutex & operator=(spin_mutex const & rhs);
};
Local & GetLocal()
{
static int guard; // 0
spin_mutex smx(&guard); // guard
lock_scope<spin_mutex> lock(smx); // smx
static Local local;
return local;
}