C++ Algorithms, Boost and function currying 161
I’ve been experimenting with C++ using the Eclipse CDT and gcc 4.4. Since I’m a fan of boost, I’ve been using that as well. I finally got into I realistic use of boost::bind.
I converted this:int Dice::total() const {
int total = 0;
for(const_iterator current = dice.begin();
current != dice.end();
++current)
total += (*current)->faceValue();
return total;
}
int Dice::total() const {
return std::accumulate(
dice.begin(),
dice.end(),
0,
bind(std::plus<int>(), _1, bind(&Die::faceValue, _2))
);
}
To see how to go from the first version to the final version with lots of steps in between: http://schuchert.wikispaces.com/cpptraining.SummingAVector.
This is a first draft. I’ll be cleaning it up over the next few days. If you see typos, or if anything is not clear from the code, please let me know where. Also, if my interpretation of what boost is doing under the covers (there’s not much of that) is wrong, please correct me.
Thanks!
C++ shared_ptr and circular references, what's the practice? 33
I’m looking for comments on the practice of using shared pointers in C++. I’m not actively working on C++ projects these days and I wonder if you’d be willing to give your experience using shared pointers, if any.
I’m porting one of our classes to C++ from Java (it’s already in C#). So to remove memory issues, I decided to use boost::shared_ptr. It worked fine until I ran a few tests that resulted in a circular reference between objects.
- A book may have a receipt (this is a poor design, that’s part of the exercise).
- A receipt may have a book.
Both sides of the relationship are 0..1. After creating a receipt, I end up with a circular reference between Receipt and Book.
In the existing Java and C# implementations, there was no cleanup code in the test teardown to handle what happens when the receipt goes away. This was not a problem since C# and Java garbage collection algorithms easily handle this situation.
Shared pointers, however, do not handle this at all. They are good, sure, but not as good as a generation-scavenging garbage collector (or whatever algorithms are used these days – I know the JVM for 1.6 sometimes uses the stack for dynamic allocation based on JIT, so it’s much more sophisticated than a simple generation-scavenger, right?)
OK, so how to fix this problem? One way I could do is is manually break the circularity:boost::shared_ptr<Receipt> r = ...;
CHECK(xxx, yyy);
r.setCopy(boost::shared_ptr<Book>());
(I did not use these types like this. When I use templates, especially those in a namespace, I use typedefs and I even, gasp, use Hungarian-esque notation.)
That would work, though it is ugly. Also, it is error prone and will either require violating DRY or making an automatic variable a field.
I could have removed the back reference from the Receipt to the book. That’s OK, but is a redesign of a system deliberately written with problems (part of the assignment).
Maybe I could explicitly “return” the book, which could remove the receipt and the back-reference. That would make the test teardown a bit more complex (and sort of upgrade the test from a unit test to something closer to an integration test), but it makes some sense. The test validate borrowing a book, so to clean up, return the book.
Instead of any of these options, I decided to use a boost::weak_ptr on the Receipt side. (This is the “technology to the rescue solution”, thus my question, is this an OK approach.)
I did this since the lifetime of a book object is much longer than its receipt (you return your library books, right?). Also, the Receipt only exists on a book. But the book could exist indefinitely without a Receipt.
This fixed the problem right away. I got a clean run using CppUTest. All tests passed and no memory leaks.
Once I had the test working, I experimented. Why? The use of a weak_ptr exposes some underlying details that I didn’t like exposing. For example, this line of code:aReceipt->getBook()->getIsbn();
(Yes, violating Law of Demeter, get over it, the alternative would make a bloated API on the Book class.)
Became instead:aReceipt->getBook().lock()->getIsbn();
The lock() method promotes a weak_ptr to a shared_ptr for the life of the expression. In this case, it’s a temporary in that line of code.
This worked fine, but I decided to put that promotion into the Receipt class. So internally, the class stores weak_ptr, but when you ask the receipt for its book, it does the lock:boost::shared_ptr<Book> getBook() {
return book.lock();
}
On the one hand, anybody using the getBook() method is paying the price of the promotion. However, the weak_ptr doesn’t allow access to its payload without the promotion so it’s really required to be of any value. Or at least that’s my take on it.
Do you have different opinions?
Please keep in mind, this is example code we use in class to give students practice naming things like methods and variables and also practice cleaning up code by extracting methods and such.
Even so, what practice do you use, if any, when using shared_ptr? Do you use weak_ptr?
Thanks in advance for your comments. I’ll be reading and responding as they come up.