我有一个声明如下的接口:
#if defined _WIN32 || _WIN64
typedef CRITICAL_SECTION MutexHandle;
#else
typedef pthread_mutex_t MutexHandle;
#endif
class IMutex
{
public:
enum MutexState
{
UNLOCKED = 0,
LOCKED
};
virtual ~IMutex() { }
virtual int32_t Lock() = 0;
virtual int32_t Unlock() = 0;
virtual const MutexState& GetMutexState() const = 0;
virtual MutexHandle& GetMutexHandle() = 0;
};
问题是,我需要包含用于CRITICAL_SECTION定义的windows.h;
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#undef WIN32_LEAN_AND_MEAN
但是这不会导致可能的标题包含使用界面的其他人的问题吗?
如何在不必包含整个windows.h的情况下声明我的typedef?
由于
答案 0 :(得分:7)
防止实现细节泄漏到其他代码的典型方法是使用Pimpl Idiom。这个想法是让你的类只包含一个指向真实实现的指针。由于真正的实现存在于.cpp文件中,因此它可以包含它所需的任何内容,而不会污染类的用户的命名空间。
在头文件中:
#include <memory> // for std::unique_ptr
class Mutex {
public:
Mutex();
~Mutex();
void Lock();
// ...
private:
class Impl;
std::unique_ptr<Impl> m_pimpl;
};
然后在实现(.cpp)类中:
#include <windows.h> // nobody else sees this
class Mutex::Impl {
public:
Impl() {
::InitializeCriticalSection(&m_cs);
}
~Impl() {
::DeleteCriticalSection(&m_cs);
}
void Lock() {
::EnterCriticalSection(&m_cs);
}
// etc.
private:
CRITICAL_SECTION m_cs;
};
// This maps the externally visible Mutex methods to the
// ones in the Implementation.
Mutex::Mutex() : m_pimpl(new Mutex::Impl()) {}
Mutex::~Mutex() {}
void Mutex::Lock() { m_pimpl->Lock(); }
您可以将整个实现放入#ifdef块或单独的.cpp文件(例如,mutex_win.cpp,mutex_posix.cpp等)中,只需使用正确的构建类型即可。
一般来说,Pimpl Idiom需要额外的指针解除引用,但虚拟方法解决方案也是如此。
答案 1 :(得分:4)
我更喜欢稍微粗略地分离代码路径,可能是这样的:
struct WinMutex;
struct PTMutex;
#ifdef WIN32
typedef WinMutex Mutex;
#include <windows.h> // or whatever
struct WinMutex
{
CRITICAL_SECTION cs;
void lock() { /* lock it */ }
void unlock() { /* unlock it */ }
};
#else
typedef PTMutex Mutex;
#include <pthread.h>
struct PTMutex
{
pthread_mutex_t m;
PTMutex() { pthread_mutex_init(&m); }
~PTMutex() { pthread_mutex_destroy(&m); }
void lock() { pthread_mutex_lock(&m); }
void unlock() { pthread_mutex_unlock(&m); }
}
#endif
这样,每个单独的类都很容易检查和重构,并且您仍然可以获得与平台相关的实现。或者,如果您发现Windows的pthreads实现,您甚至可以同时使用它们。
答案 2 :(得分:2)
我认为潜在的问题是您将特定于操作系统的类型暴露给可能是非特定操作的代码。
根据您的需要,一种解决方案可能是从GetMutexHandle
接口中排除IMutex
函数,但将其包含在两个子接口中,IMutexWin32
和IMutexPosix
。您可以仅为特定请求的代码声明IMutexWin32
接口,并要求此类代码包含Windows.h
。无论如何,任何在Windows上实际使用GetMutexHandle
的代码都需要Windows.h
。
或者,您可以使GetMutexHandle
返回一个单独的接口类,并使用特定于操作系统的子接口。
同一主题的次要变体是检测是否已包含Windows.h(#ifdef _WINDOWS_
)并使用它来决定是否声明特定于Windows的子接口。
最简单(但最丑陋)的解决方案是让GetMutexHandle
返回一个指向void的指针,并信任调用者正确地投射它。
答案 3 :(得分:2)
如果要完全避免对windows.h的头依赖,则需要转发声明CRITICAL_SECTION
,以便匹配windows.h中的现有声明。您需要确保它适用于您正在使用的windows.h版本,但这应该有效:
extern "C"
{
typedef struct _RTL_CRITICAL_SECTION RTL_CRITICAL_SECTION;
typedef RTL_CRITICAL_SECTION CRITICAL_SECTION;
}