Che classe wrapper in C ++ è necessario utilizzare per la gestione delle risorse automatizzata?

StackOverflow https://stackoverflow.com/questions/2433240

  •  19-09-2019
  •  | 
  •  

Domanda

Sono un ++ amatoriale C. Sto scrivendo un codice API Win32 e ci sono maniglie e oggetti stranamente modo composto assegnati in abbondanza. Quindi mi chiedevo -? C'è qualche classe wrapper che renderebbe più facile la gestione delle risorse

Per esempio, quando voglio caricare alcuni dati apro un file con CreateFile() e ottenere un HANDLE. Quando ho finito con esso, dovrei chiamare CloseHandle() su di esso. Ma per qualsiasi funzione di caricamento ragionevolmente complesso ci saranno decine di possibili punti di uscita, per non parlare di eccezioni.

Quindi sarebbe bello se potessi avvolgere il manico in una sorta di classe wrapper che chiamare automaticamente una volta CloseHandle() esecuzione ha lasciato il campo di applicazione. Ancora meglio -. Si potrebbe fare qualche riferimento a contare in modo da poter passare in giro dentro e fuori di altre funzioni, e ha voluto rilasciare la risorsa solo quando la portata sinistra ultimo riferimento

Il concetto è semplice - ma c'è qualcosa di simile nella libreria standard? Sto usando Visual Studio 2008, tra l'altro, e non voglio attaccare un quadro 3a parte come Boost o qualcosa del genere.

È stato utile?

Soluzione

Scrivi la tua. E 'solo un paio di righe di codice. E 'solo un compito così semplice che non è vale la pena per fornire una versione generica riutilizzabile.

struct FileWrapper {
  FileWrapper(...) : h(CreateFile(...)) {}
  ~FileWrapper() { CloseHandle(h); }

private:
  HANDLE h;
};

Pensate a quello che una versione generica avrebbe dovuto fare: Sarebbe dovuto essere parametrizzabile in modo da poter specificare qualsiasi coppia di funzioni, e qualsiasi numero di argomenti per loro. Basta un'istanza come un oggetto sarebbe probabilmente prendere come molte righe di codice come la definizione della classe di cui sopra.

Naturalmente, C ++ 0x potrebbe pendere la bilancia un po 'con l'aggiunta di espressioni lambda. Due espressioni lambda potrebbero essere facilmente passare a una classe wrapper generico, quindi una volta C ++ 0x supporta viene intorno, abbiamo potrebbe vedere una tale classe RAII generica aggiunto per aumentare o qualcosa del genere.

Ma in questo momento, è più facile a rotolare solo il proprio quando ne hai bisogno.

Per quanto riguarda l'aggiunta di conteggio dei riferimenti, vorrei consigliare contro di essa. il conteggio dei riferimenti è costoso (improvvisamente il vostro maniglia deve essere assegnato in modo dinamico, e contatori di riferimento devono essere mantenuti su ogni incarico), e molto difficile da ottenere. Si tratta di una zona appena di rottura con condizioni di gara sottili in un ambiente threaded.

Se do bisogno di conteggio dei riferimenti, basta fare qualcosa di simile boost::shared_ptr<FileWrapper>:. Avvolgere le classi RAII ad hoc personalizzati in un shared_ptr

Altri suggerimenti

In sostanza, è un buon fstream C ++ involucro per handle di file. È parte dello standard che significa che è portatile, ben collaudato ed estendibile in modo orientato agli oggetti. Per le risorse di file, è una grande idea.

Tuttavia, fstream funziona solo per i file, non per maniglie generici, cioè fili, processi, oggetti di sincronizzazione, file mappati in memoria, ecc.

Questi involucri sono chiamati ATL.

Se il manico è un evento o simili, utilizzare chandle classe.

Se il manico è un file, utilizzare CAtlFile derivato uno, avvolge le API come CreateFile e ReadFile.

Ci sono altri involucri utili in ATL, CAtlFileMapping<T> è un wrapper RAII su file mappati in memoria, CPath avvolge le API Shell32 per percorso di movimentazione, e così via.

ATL è grande biblioteca, ma le cose di basso livello come i file, le stringhe e le collezioni sono isolati. Si possono utilizzare in tutte le applicazioni Win32. è solo intestazione, non c'è bisogno di collegare con qualsiasi cosa, o distribuire le DLL extra come MFC o CRT, il codice viene compilato in chiamate WinAPI e solo funziona.

Sono stati divisi da MFC in VS2003 o 2005, non ricordo, vale a dire Visual Studio 2008 li ha sicuramente. C'è un avvertimento tuttavia, se si sta utilizzando una versione freeware di VS, deve essere 2015 o più recente.

Ecco uno in base al largo il codice ENSURECLEANUP da 'Windows tramite C / C ++': http://www.codeproject.com/KB/cpp/template2003.aspx

MFC ha alcune primitive adatte (vedi CFile , per esempio), ma non la libreria standard.

Visual C ++ 2008 supporta TR1 attraverso il Feature Pack, e TR1 include shared_ptr. Vorrei usare questo -. È un potente classe di puntatore intelligente e può essere generalizzato a fare il tipo di gestione delle risorse si sta chiedendo

TR1 è effettivamente un'estensione allo standard. Credo che sia ancora ufficialmente "pre-standard", ma in modo efficace si può considerare bloccato.

Non credo che ci sia qualcosa nella libreria standard, e ho anche dubbi sul fatto che i puntatori (come in boost) condiviso può essere utilizzato (dal momento che quelli che ci si aspetterebbe puntatore da gestire, non gestisce).

Non dovrebbe essere difficile da scrivere uno voi stessi, seguendo le href="http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Scope_Guard" linguaggio (e facendo uso di modelli / puntatori a funzione, ecc, se lo desiderano).

template <typename Traits>
class unique_handle
{
    using pointer = typename Traits::pointer;

    pointer m_value;

    auto close() throw() -> void
    {
        if (*this)
        {
            Traits::close(m_value);
        }
    }

public:

    unique_handle(unique_handle const &) = delete;
    auto operator=(unique_handle const &)->unique_handle & = delete;

    explicit unique_handle(pointer value = Traits::invalid()) throw() :
        m_value{ value }
    {
    }

    unique_handle(unique_handle && other) throw() :
        m_value{ other.release() }
    {
    }

    auto operator=(unique_handle && other) throw() -> unique_handle &
    {
        if (this != &other)
        {
            reset(other.release());
        }

        return *this;
    }

    ~unique_handle() throw()
    {
        close();
    }

    explicit operator bool() const throw()
    {
        return m_value != Traits::invalid();
    }

    auto get() const throw() -> pointer
    {
        return m_value;
    }

    auto get_address_of() throw() -> pointer *
    {
        ASSERT(!*this);
        return &m_value;
    }

    auto release() throw() -> pointer
    {
        auto value = m_value;
        m_value = Traits::invalid();
        return value;
    }

    auto reset(pointer value = Traits::invalid()) throw() -> bool
    {
        if (m_value != value)
        {
            close();
            m_value = value;
        }

        return static_cast<bool>(*this);
    }

    auto swap(unique_handle<Traits> & other) throw() -> void
    {
        std::swap(m_value, other.m_value);
    }
};

template <typename Traits>
auto swap(unique_handle<Traits> & left,
    unique_handle<Traits> & right) throw() -> void
{
    left.swap(right);
}

template <typename Traits>
auto operator==(unique_handle<Traits> const & left,
    unique_handle<Traits> const & right) throw() -> bool
{
    return left.get() == right.get();
}

template <typename Traits>
auto operator!=(unique_handle<Traits> const & left,
    unique_handle<Traits> const & right) throw() -> bool
{
    return left.get() != right.get();
}

template <typename Traits>
auto operator<(unique_handle<Traits> const & left,
    unique_handle<Traits> const & right) throw() -> bool
{
    return left.get() < right.get();
}

template <typename Traits>
auto operator>=(unique_handle<Traits> const & left,
    unique_handle<Traits> const & right) throw() -> bool
{
    return left.get() >= right.get();
}

template <typename Traits>
auto operator>(unique_handle<Traits> const & left,
    unique_handle<Traits> const & right) throw() -> bool
{
    return left.get() > right.get();
}

template <typename Traits>
auto operator<=(unique_handle<Traits> const & left,
    unique_handle<Traits> const & right) throw() -> bool
{
    return left.get() <= right.get();
}

struct null_handle_traits
{
    using pointer = HANDLE;

    static auto invalid() throw() -> pointer
    {
        return nullptr;
    }

    static auto close(pointer value) throw() -> void
    {
        VERIFY(CloseHandle(value));
    }
};

struct invalid_handle_traits
{
    using pointer = HANDLE;

    static auto invalid() throw() -> pointer
    {
        return INVALID_HANDLE_VALUE;
    }

    static auto close(pointer value) throw() -> void
    {
        VERIFY(CloseHandle(value));
    }
};

using null_handle = unique_handle<null_handle_traits>;
using invalid_handle = unique_handle<invalid_handle_traits>;
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top