0

I want to implement a RAII mechanism for a net-snmp C library struct snmp_pdu object.

That object is created with a method called struct snmp_pdu* snmp_pdu_create(int type) and released with a method void snmp_free_pdu(struct snmp_pdu *obj).

What I have done is extend auto_ptr to call the method libname_free_obj in the destructor. Below is the code:

class auto_snmp_pdu : public std::auto_ptr<snmp_pdu>
{
  public:
   virtual ~auto_snmp_pdu()
   {
     snmp_free_pdu(get());
   }
};

Is the above correect?

EDIT

I cannot use unique_ptr since I am using an old version of g++ and I am not authorized to update it.

Dan
  • 2,452
  • 20
  • 45

2 Answers2

6

auto_ptr is deprecated and bug-prone. Use unique_ptr with a custom deleter. Here is one implementation:

#include <memory>
extern "C" {
    struct snmp_pdu;

    struct snmp_pdu* snmp_pdu_create(int type);
    void snmp_free_pdu(struct snmp_pdu *obj);
}

struct snmp_pdu_deleter
{
    void operator()(snmp_pdu* p) const noexcept {
        snmp_free_pdu(p);
    }
};
using snmp_pdu_ptr = std::unique_ptr<snmp_pdu, snmp_pdu_deleter>;

snmp_pdu_ptr create_snmp_pdu(int x) {
    return snmp_pdu_ptr(snmp_pdu_create(x));
}


int main()
{
    auto ptr = create_snmp_pdu(0);
}

but my compiler is pre-c++11

unique_ptr and move semantics are fairly easy to simulate:

#include <utility>
#include <iostream>
#include <stdlib.h>

extern "C" {
    struct snmp_pdu {};

    void foo(snmp_pdu*) { std::cout << "foo" << std::endl; }

    struct snmp_pdu* snmp_pdu_create(int type) {
        return (snmp_pdu*)malloc(sizeof(snmp_pdu));
    }
    void snmp_free_pdu(struct snmp_pdu *obj) {
        free(obj);
    }
}

struct snmp_pdu_proxy
{
    struct mover { 
        mover(snmp_pdu*& impl_ref) : impl_ref_(impl_ref) {}
        snmp_pdu*& impl_ref_; 
        };

    snmp_pdu_proxy(int code) 
    : impl_(snmp_pdu_create(code))
    {}

    snmp_pdu_proxy(mover m) 
    : impl_(0)
    {
        std::swap(impl_, m.impl_ref_);
    }

    mover move() {
        return mover ( impl_ );
    }

    snmp_pdu_proxy& operator=(mover m)
    {
        snmp_pdu_proxy tmp = move();
        std::swap(m.impl_ref_, impl_);
        return *this;
    }

    operator snmp_pdu* () const {
        return impl_;
    }

    ~snmp_pdu_proxy() {
        if(impl_) {
            snmp_free_pdu(impl_);
        }
    } 
private:
    snmp_pdu_proxy& operator=(const snmp_pdu_proxy&);
    snmp_pdu* impl_;
};


int main()
{
    snmp_pdu_proxy ptr = snmp_pdu_proxy(0);

    snmp_pdu_proxy p2 = ptr.move();

    ptr = p2.move();

    foo(ptr);
}
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • There's another way to accomplish this without naming a new deleter. If the deleter type is specified as `decltype(&snmp_free_pdu)`, then the function can be passed directly to the `unique_ptr` c'tor as a second parameter. – StoryTeller - Unslander Monica Aug 03 '17 at 08:33
  • @StoryTeller While true, IMHO it's a little more cryptic. There are many ways to skin this cat, and I have used them all. But in the end I find that creating a class for a specific job is easier to document and explain to colleagues. – Richard Hodges Aug 03 '17 at 08:35
  • I can see your point. Mine was more about avoiding incidental types. I suppose another big bonus of yours is the ability to specify `noexcept` – StoryTeller - Unslander Monica Aug 03 '17 at 08:38
  • @StoryTeller types are cheap. I try to use one type represent one state or job. – Richard Hodges Aug 03 '17 at 08:42
0

If you're using C++03, then you can't use unique_ptr for sure. However, this doesn't justify using auto_ptr. It's a horrible construct and is semantically full of problems. For example, it's copyable, and a copy operation moves the object under it. I can't even start to describe how many problems that will cause.

Just create your own class that will do the deallocation for you with RAII. It's much simpler than you think. Here are a few examples:

  1. On my blog I described a few ways to do this, even with C++03.
  2. This is a SmartHandle class that deallocates anything (unfortunately it's C++11). I use it for HDF5. You can change it to fit C++03 with a functor.
  3. This is a shared_ptr implementation that supports detaching/releasing an object (it's not thread-safe). I created this for a project that works with gcc 4.3.

Take a look at these, and you'll get the idea. You can modify these examples to match your needs.

Good luck!

The Quantum Physicist
  • 24,987
  • 19
  • 103
  • 189