1 #pragma once
2 
3 #include <cassert>
4 #include <cstdint>
5 #include <stdexcept>
6 #include <utility>
7 
8 #include <hdf5.h>
9 
10 namespace ont { namespace hdf5 {
11 
12 /// Thrown when something goes wrong internally in HDF5.
13 class Exception : public std::runtime_error
14 {
15 public:
Exception()16     Exception()
17         : std::runtime_error("HDF5 exception")
18     {
19     }
20 };
21 
22 /// An HDF5 identifier.
23 ///
24 /// IdRef should be used by anything that wants to keep a reference to a HDF5
25 /// object.
26 using Id = hid_t;
27 
28 /// Maintains a reference to an HDF5 identifier.
29 ///
30 /// When you first create the identifier, use IdRef::claim() to grab it and make
31 /// sure it will be closed.
32 class IdRef final
33 {
34 public:
35     /// Create an IdRef that takes ownership of an existing reference.
36     ///
37     /// This is intended for use when you receive an ID from the HDF5 library.
38     ///
39     /// The reference counter will be decremented on destruction, but will not
40     /// be incremented.
41     static IdRef claim(Id id);
42 
43     /// Create an IdRef that takes a new reference to the ID.
44     ///
45     /// The reference counter will be incremented on creation, and decremented
46     /// on destruction.
47     static IdRef ref(Id id);
48 
49     /// Create an IdRef that refers to a global ID.
50     ///
51     /// No reference counting will be done.
global(Id id)52     static IdRef global(Id id)
53     {
54         return IdRef(id, true);
55     }
56 
57     /// Create an IdRef that refers to a global ID. This call
58     /// takes a non-global id and makes it global.
59     ///
60     /// No reference counting will be done.
61     static IdRef global_ref(Id id);
62 
63     /// Create an invalid IdRef.
64     ///
65     /// The only use for the resulting object is to copy or move into it.
66     IdRef() = default;
67 
68     IdRef(IdRef const& other);
69     IdRef& operator=(IdRef const&);
70 
IdRef(IdRef && other)71     IdRef(IdRef && other)
72         : m_id(other.m_id)
73         , m_is_global_constant(other.m_is_global_constant)
74     {
75         other.m_id = -1;
76         other.m_is_global_constant = false;
77     }
78     IdRef& operator=(IdRef && other)
79     {
80         std::swap(m_id, other.m_id);
81         std::swap(m_is_global_constant, other.m_is_global_constant);
82         return *this;
83     }
84 
85     ~IdRef();
86 
swap(IdRef & other)87     void swap(IdRef & other) {
88         std::swap(m_id, other.m_id);
89         std::swap(m_is_global_constant, other.m_is_global_constant);
90     }
91 
92     /// Take ownership of the ID.
93     ///
94     /// This object will no longer hold a reference to the ID. It is up to the
95     /// called to deref the ID.
release()96     Id release() {
97         Id id = m_id;
98         m_id = -1;
99         m_is_global_constant = false;
100         return id;
101     }
102 
103     /// Get the ID.
104     ///
105     /// The reference count will not be changed. This is mostly for passing into
106     /// HDF5 function calls.
get()107     Id get() const
108     {
109         assert(m_id >= 0);
110         return m_id;
111     }
112 
113     /// Get the reference count of the ID.
114     int ref_count() const;
115 
116     /// Check whether this IdRef contains an ID.
117     ///
118     /// Note that it does not check whether HDF5 thinks the ID is valid.
119     explicit operator bool() const { return m_id >= 0; }
120 
121 private:
122     // use claim() or ref()
IdRef(Id id)123     explicit IdRef(Id id) : m_id(id), m_is_global_constant(false) {}
IdRef(Id id,bool is_global)124     explicit IdRef(Id id, bool is_global)
125         : m_id(id)
126         , m_is_global_constant(is_global)
127     {}
128 
129     Id m_id = -1;
130     bool m_is_global_constant = false;
131 };
132 
133 }}