5

我有一个这样声明的接口:

    #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?

谢谢

4

4 回答 4

8

防止实现细节泄漏到其他代码中的典型方法是使用 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 需要额外的指针解引用,但您的虚拟方法解决方案也是如此。

于 2012-09-13T00:50:20.163 回答
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 实现,您甚至可以同时拥有两者。

于 2012-09-12T21:00:10.077 回答
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;
}
于 2012-09-14T04:04:32.830 回答
2

我认为根本问题是您将特定于操作系统的类型暴露给可能是非特定于操作系统的代码。

根据您的需要,一种解决方案可能是GetMutexHandle从接口中排除该功能,IMutex但将其包含在两个子接口中,IMutexWin32然后IMutexPosix说。您可以IMutexWin32仅为特别请求它的代码声明接口,并要求此类代码包含Windows.h. 无论如何GetMutexHandle,将需要在 Windows 上实际使用的任何代码。Windows.h

或者,您可以GetMutexHandle使用特定于操作系统的子接口将返回值设为单独的接口类。

同一主题的一个次要变体是检测是否已包含 Windows.h ( #ifdef _WINDOWS_) 并使用它来决定是否声明特定于 Windows 的子接口。

最简单(但最丑陋)的解决方案是GetMutexHandle返回一个指向 void 的指针并相信调用者正确地转换它。

于 2012-09-12T21:39:49.980 回答