4 Objects in the Lely libraries are all defined and implemented according to the
5 pattern shown here. This pattern is designed to allow a header-only C++
6 interface for C objects which is as close as possible to a native C++ interface.
8 Example of a public C header (lely/lib/obj.h):
10 #ifndef LELY_LIB_OBJ_H_
11 #define LELY_LIB_OBJ_H_
13 // The obj_t typedef is only defined for C. This allows the C++ interface to
14 // define its own typedef but still call all C functions without requiring a
18 typedef struct __obj obj_t;
22 // Disable C++ name mangling and ensure the C calling convention is used.
26 // In C++, memory allocation and object initialization are separate steps. The
27 // C implementation of these steps is exposed so they can be used by the C++
28 // interface. Users of the C interface SHOULD not invoke these functions
29 // directly, but use obj_create() and obj_destroy() instead.
30 void *__obj_alloc(void);
31 void __obj_free(void *ptr);
32 struct __obj *__obj_init(struct __obj *obj, Args... args);
33 void __obj_fini(struct __obj *obj);
35 // Creates a new object instance. Internally, this function invokes
36 // __obj_alloc() followed by __obj_init().
37 obj_t *obj_create(Args... args);
39 // Destroys an object instance. Internally, this function invokes __obj_fini()
40 // followed by obj_free().
41 void obj_destroy(obj_t *obj);
43 // An example of an object method. The first parameter is always a pointer to
44 // the object (cf. "this" in C++).
45 int obj_method(obj_t *obj, Args... args);
51 #endif // !LELY_LIB_OBJ_H_
54 Example of the C implementation (obj.c):
56 #include <lely/util/errnum.h>
57 #include <lely/lib/obj.h>
69 void *ptr = malloc(sizeof(struct __obj));
71 // On POSIX platforms this is a no-op (errno = errno), but on
72 // Windows this converts errno into a system error code and
73 // invokes SetLastError().
74 set_errc(errno2c(errno));
85 __obj_init(struct __obj *obj, Args... args)
89 // Initialize all object members. If an error occurs, clean up and
97 __obj_fini(struct __obj *obj)
101 // Finalize all object members.
106 obj_create(Args... args)
110 obj_t *obj = __obj_alloc();
113 goto error_alloc_obj;
116 if (!__obj_init(obj, args...)) {
131 obj_destroy(obj_t *obj)
133 // obj_destroy() and obj_free() are the only methods which can be called
134 // with `obj == NULL`.
142 obj_method(obj_t *obj, Args... args)
146 // Do something with the object.
151 Example of a public C++ header (lely/lib/obj.hpp):
153 #ifndef LELY_LIB_OBJ_HPP_
154 #define LELY_LIB_OBJ_HPP_
157 #error "include <lely/lib/obj.h> for the C interface"
160 // Include the incomplete_c_type<> and c_type_traits<> templates.
161 #include <lely/util/c_type.hpp>
163 // Provide the obj_t typedef. This is necessary to include lely/lib/obj.h
165 namespace lely { class Obj; }
166 typedef lely::Obj obj_t;
168 #include <lely/lib/obj.h>
172 // The c_type_traits<> template specialization provides a uniform wrapper for
173 // the the alloc(), free(), init() and fini() methods from the C interface.
174 // These methods are invoked by incomplete_c_type<> from which the C++ interface
177 struct c_type_traits<__obj> {
178 typedef __obj value_type;
179 typedef value_type& reference;
180 typedef const value_type& const_reference;
181 typedef value_type* pointer;
182 typedef const value_type* const_pointer;
184 static void* alloc() noexcept { return __obj_alloc(); }
185 static void free(void* ptr) noexcept { __obj_free(ptr); }
188 init(pointer p, Args... args) noexcept
190 return __obj_init(p, args...);
193 static void fini(pointer p) noexcept { __obj_fini(p); }
196 class Obj: public incomplete_c_type<__obj> {
197 typedef incomplete_c_type<__obj> c_base;
199 // c_base calls the alloc() and init() methods of c_type_traits<__obj>
200 // that construct the object.
201 Obj(Args... args): c_base(args...) {}
204 method(Args... args) noexcept
206 // Due to the (re)definition of obj_t we can pass "this" as the
208 return obj_method(this, args...);
212 // The destructor is protected to prevent the creation of an instance on
213 // the heap. This would lead to errors, since the size of the object is
220 #endif // !LELY_LIB_OBJ_HPP_
223 Defining the destructor as `protected` prevents allocating objects on the stack.
224 Unfortunately, it also prevents invoking `delete` on heap-allocated objects.
225 Instead, objects must be destroy by invoking `destroy()`, either as a member
226 function (`obj->destroy()`) or as a global function (`destroy(obj)`).
228 From C++11 onwards, using raw pointers is discouraged. Shared or unique pointers
229 are preferred. lely/util/c_type.hpp provides the `make_shared_c()` and
230 `make_unique_c()` convenience functions for this purpose. They are equivalent to
231 the standard `make_shared()` and `make_unique()` functions, but specify
232 `destroy()` as the deleter.