可移植的虛函數(shù)表
C++并非一門合適的模塊——接口語言,為了支持ABI/OAB(Application Binary Interface/Objects Across Borders)需要付出相當(dāng)程度的努力,并且使用者還必須受到嚴(yán)格的約束。下面介紹一種得到廣泛運(yùn)用的跨邊界對象技術(shù),如果你熟悉Win32 COM,肯定立馬心領(lǐng)神會。
接口代碼:
// 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 *);
服務(wù)端代碼:
// 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關(guān)鍵字
Java中有synchronized關(guān)鍵字,.NET中有l(wèi)ock關(guān)鍵字,它們可以被用于守護(hù)臨界區(qū)。而C++標(biāo)準(zhǔn)對線程幾乎只字未提,自然也沒有相關(guān)設(shè)施。而然借助一點(diǎn)點(diǎn)宏,我們就能輕松實(shí)現(xiàn)同樣的效果:(lock_scope請參考筆記(一)中的域守衛(wèi)類)
#define SYNCHRONIZED_BEGIN(T, v) \
{ \
lock_scope<T> __lock__(v);
#define SYNCHROIZED_END() \
}
如果你和我一樣不喜歡SYNCHROIZE_END()部分,畢竟和synchronized或lock比起來似乎不那么優(yōu)雅,那么可以用點(diǎn)小技巧,像這樣定義宏:
#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)
{
... // 臨界區(qū)代碼
}
... // 非臨界區(qū)代碼
synchronized(Object, obj)
{
... // 臨界區(qū)代碼
}
上述代碼,熟悉JAVA的朋友是不是很親切?不過還是有個小小的遺憾,那就是對象的類型不能被自動推導(dǎo)出來,導(dǎo)致我們必須手工提供兩個參數(shù)。
這里還有個問題要注意,如果同一個作用域中出現(xiàn)了兩個同步段,某些老式編譯器(如大名鼎鼎的VC6.0)對for循環(huán)處理的不那么“標(biāo)準(zhǔn)”,就會報(bào)出“重新定義__lock__”的錯誤。所以作者對此提供了一個可移植的解決方案,來確保每個“__lock__”都是不一樣的:
#define concat__(x, y) x ## y
#define concat_(x, y) concat__(x, y) // 用兩層宏來取__LINE__的實(shí)際值
#define synchronized(T, v) \
for (synchronized_lock<lock_scope<T> > concat_(__lock__,__LINE__)(v); \
concat_(__lock__,__LINE__); concat_(__lock__,__LINE__).end_loop())
但是,作者似乎忘了,某些老式編譯器(如VC6.0)對循環(huán)變量的釋放,會延遲到整個作用域結(jié)束時,而不是臨界代碼段的右括號處。所以該解決方案是錯誤的!
局部靜態(tài)對象
函數(shù)局部靜態(tài)對象的初始化存在的固有的競爭條件是非常重要的問題。因此不能被忽略。然而,它同時也是非常罕見的情況。我們可以利用其罕見性,給出一個極其優(yōu)雅的解決方案,該解決方案基于自旋互斥體。自旋互斥體本身依賴于原子操作,它的開銷是非常低的。
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函數(shù)seched_yield()將執(zhí)行機(jī)會讓給其他線程
}
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進(jìn)行自旋
lock_scope<spin_mutex> lock(smx); // 使用smx來加鎖
static Local local;
return local;
}