Pregunta

Tengo una pregunta similar a ¿Cómo administrar el tiempo de objeto con los consejos inteligentes de la biblioteca Boost? Pero, en mi caso, el "objeto" no es un objeto C ++ en absoluto, sino un tipo opaco devuelto/desmayado de una API C. El tipo no tiene semántica de puntero, es decir, no hay desferencias; Sin embargo, se pasa como un argumento a otras funciones en la API C. El tipo también tiene una definitiva close API que debe llamarse para limpiar los recursos internos.

Entonces, tengo una API C que es algo así como

opaque_legacy_type_t x;
XXopen(..., &x); // allocates/opens resource and fills out 'x' to be used later
XXdoSomethingWithResource(x, ...); // do something with resources related to 'x'
...more actions...
XXclose(x); // closes and cleans up resources related to 'x'

Por varias razones, en mi código C ++ me gustaría administrar "instancias" de OPAQUE_LEGACY_TYPE_T, al igual que administraría instancias de objetos asignadas por el montón, es decir, con semánticas compartidas similares como boost::shared_ptr<>. Parece que shared_ptr ofrece suficiente para que pueda manejar llamar XXclose al hacer esto:

opaque_legacy_type_t x;
XXopen(..., &x);
boost::shared_ptr<opaque_legacy_type_t> managed(x, XXclose);

Pero desde opaque_legacy_type_t no tiene semántica de puntero, el uso de managed es un poco torpe.

Lo que me gustaría hacer es tener algo como un managed_type que es similar a shared_ptr, y estoy buscando ideas que no requieran que lo escriba todo.

EDITAR: Correcté mi desorden original en el ejemplo. La API heredada toma el tipo opaco por valor en lugar de por puntero.

¿Fue útil?

Solución

Podrías usar Boost Smart Pointers junto con Pimpl Idom:

class shared_opaque_legacy_type_t {
    struct impl {
        opaque_legacy_type_t t;
        impl(...) { XXOpen(..., t); }
        ~impl(...) { XXClose(t); }
    }
    boost::shared_ptr<impl> _impl;
public:
    shared_opaque_lagacy_type_t(...) : _impl(new impl(...)) {}

    opaque_legacy_type_t* get() {
        return _impl->t;
    }
};


shared_opaque_legacy_type_t x(...);
XXdoSomethingWithResource(x.get(), ...);

El inconveniente es que aún podrías llamar XXclose(x.get()) e invalidar su objeto.

ACTUALIZAR: Arreglado. :-)

Otros consejos

Dado que toda la API heredada toma un puntero al tipo opaco, puede usar punteros compartidos directamente. La clave es que no declare la estructura original en la pila, sino que la asigne a través de new:

int main () {
    std::shared_ptr<opaque_legacy_type_t> x(new opaque_legacy_type_t,
        [](opaqeue_legacy_type_t* p) { XXClose(p); delete p; });
    XXopen(..., x.get());
    XXdoSomethingWithResource(x.get(), ...);
}


EDITAR: Si alguna API toma el tipo opaco por valor en lugar de puntero, pase el puntero desferenciado.

int main () {
    std::shared_ptr<opaque_legacy_type_t> x(new opaque_legacy_type_t,
        [](opaqeue_legacy_type_t* p) { XXClose(*p); delete p; });
    XXopen(..., x.get());
    XXdoSomethingWithResource(*x, ...);
}

Podrías escribir un envoltorio para usar con Boost que llamará al open() en el ctor y el close() en el dtor.

Voté por la respuesta de Rob que solo usa un shared_ptr Sin envoltura, pero si realmente quieres evitar la asignación dinámica, aquí hay un pequeño ejemplo simple de cómo hacerlo.

Es una plantilla que sostiene directamente el mango y no asigna la asignación. Pasas el constructor un functor que crea un objeto del tipo opaco y un perdedor para llamar cuando el tipo debe ser destruido. Es móvil y no cotizable, por lo que ahora se necesita un recuento de referencias compartido. Implementa operadores de conversión implícitos para que pueda usarlo donde utilizaría un valor del tipo retenido.

template<typename T,typename D>
class opaque_type_handle {
    T handle;
    D deleter;
    bool needs_delete;
public:
    template<typename F>
    opaque_type_handle(F f,D d) : handle(f()), deleter(d), needs_delete(true) {}

    opaque_type_handle(opaque_type_handle const &) = delete;
    opaque_type_handle &operator=(opaque_type_handle const &) = delete;

    opaque_type_handle(opaque_type_handle &&rhs) : handle(rhs.handle),deleter(rhs.deleter),needs_delete(true) {
        rhs.needs_delete = false;
    }
    opaque_type_handle &operator=(opaque_type_handle &&rhs) {
        handle = rhs.handle;
        deleter = rhs.deleter;
        needs_delete = true;
        rhs.needs_delete = false;
        returh *this;
    }

    ~opaque_type_handle() {
        if(needs_delete) {
            deleter(handle);
        }
    }

    operator T&() { return handle; }
    operator T() const { return handle; }
};

Úselo así:

// wrap up the code for creating an opaque_legacy_type_t handle
typedef opaque_type_handle<opaque_legacy_type_t,decltype(&XXclose)> legacy_handle;

legacy_handle make_legacy_handle(...) {
    return legacy_handle(
        [](){
            opaque_legacy_type_t tmp;
            XXopen(..., &tmp);
            return tmp;
        },
        &XXclose
    );
}

legacy_handle x = make_legacy_handle(...);
XXdoSomethingWithResource(x,...);
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top