Skip to content

Commit

Permalink
Create RAII wrappers for C libraries
Browse files Browse the repository at this point in the history
    * CLibrarySPtr: A smart pointer class for ref-counting C objects
      (cairo_t, cairo_surface_t, GObject, poppler stuff...)
    * CairoSaveGuard: RAII wrapper for cairo_save/cairo_restore.
  • Loading branch information
Benjamin Hennion authored and Febbe committed Jun 27, 2022
1 parent faa0888 commit 05f1c17
Show file tree
Hide file tree
Showing 5 changed files with 499 additions and 0 deletions.
100 changes: 100 additions & 0 deletions src/util/include/util/raii/CLibrariesSPtr.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Xournal++
*
* RAII smart pointers for C library classes
*
* @author Xournal++ Team
* https://github.com/xournalpp/xournalpp
*
* @license GNU GPLv2 or later
*/

#pragma once

#include <utility>

namespace xoj::util {
/**
* @brief Simple template class for RAII smart pointer (aimed at Cairo/GTK/Poppler ref-counting classes)
* @param T The wrapper will store a pointer of type T
* @param H Handler class containing at least
* static T* ref(T*):
* static void unref(T*);
* static T* adopt(T*); // What to do to a pointer when we adopt an instance (typically the identity)
* and optionally (for floating refs)
* static T* ref_sink(T*);
*/
template <typename T, class H>
class CLibrariesSPtr {
public:
CLibrariesSPtr() = default;

using handler_type = H;

private:
static auto safeRef(T* ptr) -> T* { return ptr ? H::ref(ptr) : ptr; }
static auto safeUnref(T* ptr) -> void {
if (ptr) {
H::unref(ptr);
}
}
static auto safeSinkRef(T* ptr) -> T* { return ptr ? H::sink_ref(ptr) : ptr; }
static auto safeReset(T*& ptr, T* val) -> void { safeUnref(std::exchange(ptr, val)); }

public:
~CLibrariesSPtr() { safeUnref(p); }

CLibrariesSPtr(const CLibrariesSPtr& other) { p = safeRef(other.p); }
CLibrariesSPtr(CLibrariesSPtr&& other) { p = std::exchange(other.p, nullptr); }

CLibrariesSPtr& operator=(const CLibrariesSPtr& other) {
if (this != &other) {
safeReset(p, safeRef(other.p));
}
return *this;
}
CLibrariesSPtr& operator=(CLibrariesSPtr&& other) {
if (this != &other) {
safeReset(p, std::exchange(other.p, nullptr));
}
return *this;
}

constexpr static struct Adopt {
} adopt = Adopt();
constexpr static struct Ref {
} ref = Ref();
constexpr static struct RefSink {
} refsink = RefSink();

CLibrariesSPtr(T* p, Adopt = adopt): p(H::adopt(p)) {}
CLibrariesSPtr(T* p, Ref): p(safeRef(p)) {}
CLibrariesSPtr(T* p, RefSink): p(safeSinkRef(p)) {}

void reset(T* other = nullptr, Adopt = adopt) { safeReset(p, other); }
void reset(T* other, Ref) { safeReset(p, safeRef(other)); }
void reset(T* other, RefSink) { safeReset(p, safeRefSink(other)); }

operator bool() const { return p != nullptr; }

T* get() { return p; }
const T* get() const { return p; }

T* release() { return std::exchange(p, nullptr); }

T* operator->() { return p; }
const T* operator->() const { return p; }

void swap(CLibrariesSPtr& other) { std::swap(p, other.p); }

private:
T* p = nullptr;
};
}; // namespace xoj::util

namespace std {
template <typename T, class H>
void swap(xoj::util::CLibrariesSPtr<T, H>& first, xoj::util::CLibrariesSPtr<T, H>& second) {
first.swap(second);
}
}; // namespace std
65 changes: 65 additions & 0 deletions src/util/include/util/raii/CairoWrappers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Xournal++
*
* RAII wrappers for C library classes
*
* @author Xournal++ Team
* https://github.com/xournalpp/xournalpp
*
* @license GNU GPLv2 or later
*/

#pragma once

#include <utility>

#include <cairo.h>

#include "CLibrariesSPtr.h"

namespace xoj::util {

inline namespace raii {
namespace specialization {

template <typename T> // Todo(cpp20): replace with std:identity()
constexpr auto identity = [](T* p) { return p; };

class CairoHandler {
public:
constexpr static auto ref = cairo_reference;
constexpr static auto unref = cairo_destroy;
constexpr static auto adopt = identity<cairo_t>;
};

class CairoSurfaceHandler {
public:
constexpr static auto ref = cairo_surface_reference;
constexpr static auto unref = cairo_surface_destroy;
constexpr static auto adopt = identity<cairo_surface_t>;
};
}; // namespace specialization

using CairoSPtr = CLibrariesSPtr<cairo_t, raii::specialization::CairoHandler>;
using CairoSurfaceSPtr = CLibrariesSPtr<cairo_surface_t, raii::specialization::CairoSurfaceHandler>;

/**
* @brief cairo_save(cr)/cairo_restore(cr) RAII implementation
*/
class CairoSaveGuard {
public:
CairoSaveGuard() = delete;
CairoSaveGuard(cairo_t* cr): cr(cr) { cairo_save(cr); }
~CairoSaveGuard() { cairo_restore(cr); }

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

private:
cairo_t* cr;
};

}; // namespace raii
}; // namespace xoj::util
44 changes: 44 additions & 0 deletions src/util/include/util/raii/GObjectSPtr.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Xournal++
*
* RAII wrappers for C library classes
*
* @author Xournal++ Team
* https://github.com/xournalpp/xournalpp
*
* @license GNU GPLv2 or later
*/

#pragma once

#include <utility>

#include <glib-object.h>
#include <gtk/gtk.h>

#include "CLibrariesSPtr.h"

namespace xoj::util {

inline namespace raii {
namespace specialization {

template <class object_type>
class GObjectHandler {
public:
static object_type* ref(object_type* p) { return static_cast<object_type*>(g_object_ref(p)); }
constexpr static auto unref = g_object_unref;
static object_type* ref_sink(object_type* p) { return static_cast<object_type*>(g_object_ref_sink(p)); }
static object_type* adopt(object_type* p) {
#if (GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 70))
return static_cast<object_type*>(g_object_take_ref(p));
#else
return g_object_is_floating(p) ? static_cast<object_type*>(g_object_ref_sink(p)) : p;
#endif
}
};
}; // namespace specialization

using WidgetSPtr = CLibrariesSPtr<GtkWidget, raii::specialization::GObjectHandler<GtkWidget>>;
}; // namespace raii
}; // namespace xoj::util
Loading

0 comments on commit 05f1c17

Please sign in to comment.