Skip to content

Instantly share code, notes, and snippets.

@bwoods
Last active September 19, 2015 22:48
Show Gist options
  • Save bwoods/aaa53f135ad0cbd3bf7f to your computer and use it in GitHub Desktop.
Save bwoods/aaa53f135ad0cbd3bf7f to your computer and use it in GitHub Desktop.
#pragma once
#include <type_traits>
namespace raii {
template <class T>
class pointer {
template <typename D>
struct owner {
operator T* () const { return p; }
T* operator->() { return p; }
T** operator&() { return &p; }
T* release() { T* tmp = nullptr; std::swap(tmp, p); return tmp; };
void reset(T* val = nullptr) { if (p) deleter(p); p = val; }
T* get() { return p; };
T* operator= (T* val) { reset(val); return val; }
owner(T* p, D deleter) : p(p), deleter(deleter) { }
owner(owner&& that) : p(that.release()), deleter(that.deleter) { }
~owner() { reset(); }
T* p; D deleter;
};
public:
template <typename D>
static inline auto with_destructor(D deleter) {
return pointer<T>::owner<D>{ nullptr, deleter };
}
};
template <class T>
class structure {
template <typename D>
struct owner {
T* operator->() { return &s; }
T* operator&() { return &s; }
~owner() { deleter(&s); }
T s; D deleter;
};
public:
template <typename D>
static inline auto with_destructor(D deleter) -> structure<T>::owner<D> {
return { .deleter = deleter };
}
};
}

RAII for C APIs

An alternative to wrapping every C API you may need in custom classes for the convienence and correctness that Resource Acquisition Is Initialization gives. The idea is similar to std::unique_ptr but is less opinionated about where the memory comes from.

Some examples:

Typical usage with an API that expects to allocate pointer for you.

auto db = raii::pointer<sqlite3>::with_destructor(sqlite3_close_v2);
sqlite3_open_v2(path, &db, SQLITE_OPEN_CREATE+SQLITE_OPEN_READWRITE, nullptr);

Some APIs need custom destruction for stack allocated container classes.

auto remotes = raii::structure<git_strarray>::with_destructor(git_strarray_free);
git_remote_list(&remotes, repository);

If destruction requires more than a single function, a lambda should be used.

auto stream = raii::pointer<std::iterator_traits<CFReadStreamRef>::value_type>::with_destructor([] (CFReadStreamRef p) { CFReadStreamClose(p); CFRelease(p); });
stream = CFReadStreamCreateWithFile(kCFAllocatorDefault, url);
CFReadStreamOpen(stream);

Core Foundation can be a particularly… persnickety… case. The non-mutable classes require a const in the template and the type being pointed to is private. Which leads to this:

int64_t number = …
auto id = raii::pointer<const std::iterator_traits<CFNumberRef>::value_type>::with_destructor(CFRelease);
id = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &number);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment