Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CRITICAL_SECTION and avoiding including windows.h?

I have an interface declared like this:

    #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;
    };

The problem is, I need to include windows.h for CRITICAL_SECTION definition;

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#undef WIN32_LEAN_AND_MEAN

but dosn't this cause possible header include problems with others using the interface?

How can I declare my typedef without having to include the whole windows.h?

Thanks

like image 677
KaiserJohaan Avatar asked Sep 12 '12 20:09

KaiserJohaan


1 Answers

The typical way to prevent the implementation details from leaking into other code is to use the Pimpl Idiom. The idea is to make your class simply contain a pointer to the real implementation. Since the real implementation lives in a .cpp file, it can include whatever it needs without polluting the namespace of the users of the class.

In the header file:

#include <memory>  // for std::unique_ptr

class Mutex {
  public:
    Mutex();
    ~Mutex();
    void Lock();
    // ...
  private:
    class Impl;
    std::unique_ptr<Impl> m_pimpl;
};

Then in the implementation (.cpp) class:

#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(); }

You could put the whole implementation into #ifdef blocks, or in separate .cpp files (e.g., mutex_win.cpp, mutex_posix.cpp, etc.) and just use the correct one for your type of build.

In general, the Pimpl Idiom requires extra pointer dereferencing, but so does your virtual method solution.

like image 117
Adrian McCarthy Avatar answered Sep 30 '22 13:09

Adrian McCarthy