strbuf
,
ptr
, and ref
Prev | Index | Next |
OK. So now that I whetted your appetite, there are some things you should know about libasync. Quoting from Using libasync:
One complication of programming with callbacks is dealing with freeing dynamically allocated memory. This is particularly true of strings. If a function takes an argument of type
char *
and does something asynchronously using the string, the caller may not be allowed to free the string until later. Keeping track of who is responsible for what strings and for how long is tricky and error-prone....
Strings are not the only data structure for which deallocation becomes tricky in asynchronous programming. Libasync therefore provides a generic mechanism for reference-counted deallocation of arbitrary dynamically allocated data structures. Refcounting is a simple form of automatic garbage collection: each time a refcounted object is referenced or goes out of scope its reference count is incremented or decremented, respectively. If the reference count of an object ever reaches zero, the object's destructor is called and it is deleted. Note that unlike real garbage collection in languages like Java, you cannot use reference-counted deallocation on data structures with pointer cycles.
Libasync provides you with strbuf
objects, automatic
reference counting and garbage collection. We'll treat these one by
one.
strbuf
Strbuf
allows you to build up a string by appending to
it using the <<
operator.
1: #include "async.h" 2: 3: int 4: main(int argc, char *argv[]) 5: { 6: async_init(); 7: strbuf foo; 8: foo << "Hello, "; 9: foo << "World!\n"; 10: warn << foo; 11: }
This example speaks for itself. One situation where you'll find
strbuf
most useful is when iteratively reading chunks of
data from a file descriptor.
New
Libasync provides C++ support for debugging malloc. You should use
the New
operator where you would otherwise use
new
. New
is a wrapper around
new
that includes line information, making it easy to
debug memory leaks, etc. (Freely quoted from Using
libasync.)
1: #include "async.h" 2: 3: typedef struct { 4: int r; 5: int t; 6: int m; 7: } rtm; 8: 9: int 10: main(int argc, char *argv[]) 11: { 12: async_init(); 13: rtm *foo = New rtm(); 14: foo->r = 1; 15: foo->t = 2; 16: foo->m = 3; 17: 18: delete foo; 19: }
ref
You can create reference counted object with the
refcounted
template. Refcounted
can turn
any ordinary C++ type into a reference counted type. It's pretty
cool. Here's an example that allocates a new (reference counted)
foo
object, and passes it to a function using a
delaycb
.
1: #include "async.h" 2: 3: typedef struct { 4: int x, y, z; 5: } foo; 6: 7: 8: void 9: printfoo(ref<foo> p) 10: { 11: warn << "x : " << p->x << "\n"; 12: warn << "y : " << p->y << "\n"; 13: warn << "z : " << p->z << "\n"; 14: exit(0); 15: } 16: 17: 18: int 19: main(int argc, char *argv[]) 20: { 21: async_init(); 22: // ordinary C++ type 23: ref<int> tmg = New refcounted<int>; 24: *tmg = 3; 25: 26: // fancy type 27: ref<foo> bar = New refcounted<foo>; 28: bar->x = 1; 29: bar->y = 2; 30: bar->z = 3; 31: 32: delaycb(1, 0, wrap(printfoo, bar)); 33: 34: amain(); 35: }
Notice that you can use a ref<foo>
object, e.g.
bar
, as if it was a plain old C pointer. There's one
thing you can never do, though: you cannot set a
ref<...>
object to zero. That's where
ptr<...>
enters the picture.
ptr
The only difference between ref
and ptr
is that ptr
's can be zero, and ref
's cannot.
Here's a dead simple example that core dumps.
1: #include "async.h" 2: 3: typedef struct { 4: int x, y, z; 5: } foo; 6: 7: 8: int 9: main(int argc, char *argv[]) 10: { 11: async_init(); 12: ref<foo> bar = New refcounted<foo>; 13: ptr<foo> rab = New refcounted<foo>; 14: 15: rab = 0; // fine! 16: 17: // wouldn't compile! 18: // bar = 0; 19: 20: // core dumps 21: bar = rab; 22: }
You should be using ref
's, unless you have a good
excuse to use ptr
's.
Please refer to Using libasync for the full story on reference counted objects.
Prev | Index | Next |