When I have a method that calls a set of methods that offer strong guarantee, I often have a problem on rolling back changes in order to also have a strong guarantee method too. Let's use an example:
// Would like this to offer strong guarantee
void MacroMethod() throw(...)
{
int i = 0;
try
{
for(i = 0; i < 100; ++i)
SetMethod(i); // this might throw
}
catch(const std::exception& _e)
{
// Undo changes that were done
for(int j = i; j >= 0; --j)
UnsetMethod(j); // this might throw
throw;
}
}
// Offers strong guarantee
void SetMethod(int i) throw(...)
{
// Does a change on member i
}
// Offers strong guarantee
void UnsetMethod() throw(...)
{
// Undoes a change on member i
}
Obviously, the UnsetMethod could throw. In which case, my MacroMathod() only offers basic guarantee. Yet, i did all I could to offer a strong guarantee, but I can't be absolutly sure my UnsetMethod() will not throw. Here's my questions:
Should I even try to offer a strong guarantee in this case?
Should I document my MacroMethod() as having a basic or strong guarantee? Even if it is very unlikely UnsetMethod will throw?
Can you see a way to make this method truly offer a strong guarantee?
I should probably put the call to UnsetMethod() in a try, but that feels rather heavy, and what should I do in the catch?
Thanks!
A good pattern to try to achieve this is to make your method work on a copy of the object that you want to modify. When all modifications are done, you swap the objects (swap should be guaranteed not to throw). This only makes sense if copy and swap can be implemented efficiently.
This method has the advantage that you do not need any try...catch-blocks in your code, and also no cleanup-code. If an exception is thrown, the modified copy gets discarded during stack unwinding, and the original was not modified at all.
No, you can't in general with the code you gave. However, depending on your problem, maybe you can make a copy of the data, perform SetMethod on that copy and then swap the representation. This provides strong guarantee, but again depends on the problem.
You can document: Strong guarantee if UnsetMethod doesn't throw, basic otherwise. Actually this explains why it is said that destructors should not throw. Actually any undo operations should not throw.
Yes, see 1.
No, it makes no sense.
If a strong exception guarantee is important to MacroMethod(), I would redesign UnsetMethod() to not throw anything at all, if you can. Of course how this can be done depends on what you're doing.
You're using UnsetMethod() to clean up after a failure from SetMethod(). If UnsetMethod() fails to clean up, what can you do about that? It's the same reason why throwing exceptions from destructors is extremely dangerous.
Fundamentally, what you need to do in functions that call throwing functions is work on a temporary copy of the data until all of the possibly-throwing sub functions have finished, then swap (which must not throw) the temporary values in to the 'real' structure.
Some illustrative psudocode:
void MacroMethod() throw(...)
{
int i = 0;
MyValues working_copy[100] = *this; //obviously this is psudocode as I don't know your real code
for(i = 0; i < 100; ++i)
working_vopy[i].SetMethod(i); // if this throws the exception will propogate out, and no changes will be made
swap( *this, working_copy); // this must not throw
}
Related
This:
void foo(int &&r) {
std::cout << r << std::endl;
}
int main() {
int i = 2;
foo(std::move(i));
i = 3; //no warning. any way to get some warnings here?
return 0;
}
Is there no way to tell the compiler to give me an error (or warning) if I accidentally use the variable after I have moved it? I think this would be very convenient. A lot of times I find myself moving variables elsewhere like that, but then I manually have to be VERY CAREFUL that I don't use them afterwards. Now this hasn't caused any problems yet, but who knows down the line... better be safe!
Maybe there are some preprocessor trickery (or pretty widely available compiler extensions) that exists to do this stuff?
More realistic example:
struct HugeStorage {
std::vector<double> m_vec;
HugeStorage(std::vector<double> vec) : m_vec(std::move(vec)) { }
};
struct SmallStorage {
std::vector<double> m_vec;
SmallStorage(std::vector<double> vec) : m_vec(std::move(vec)) { }
};
std::vector<double> vec_from_data_source() {
return std::vector<double>(); //only example!!
}
int main() {
std::vector<double> vec = vec_from_data_source();
if (vec.size() > 10000)
{
HugeStorage storage(std::move(vec));
//do some things, but I gotta be careful I don't do anything to vec
}
else
{
SmallStorage storage(std::move(vec));
//do some things, but I gotta be careful I don't do anything to vec
}
return 0;
}
Is there no way to tell the compiler to give me an error (or warning) if I accidentally use the variable after I have moved it?
The answer is "no, there is no way" (to the best of my knowledge at least, no currently available compiler offers such an option, and for a good reason - see below).
Even if that was possible at all, why would you expect a warning, or even worse an error, to be given in this case? First of all, moving from an integer is not any different than copying it.
Secondly, for most types, assigning a moved-from object of that type is a perfectly legal operation; this is always true of fundamental types like int, and it is definitely true of std::vector, although it might not be true of other types.
In general, whether or not assigning a moved-from object is legal depends on the particular post-conditions of the move operation for that type and on the preconditions of the assignment operator (the assignment operator for types of the Standard Library have no preconditions on the left-hand side argument). This is something a compiler cannot check in the general case.
Therefore, if you were to:
Move from an object for which the move assignment or move constructor places the moved-from object in an unspecified state (that's the case for std::vector), and then;
Invoke any function with preconditions on the state of that object (and that's not the case for the assignment to an std::vector);
That would certainly be bad. On the other hand, the compiler does not have a way to perform a semantic analysis of your program and find out whether this is the case:
A x, y;
...
if (complicatedCondition())
{
y = move(x);
}
foo(x); // Did I move from x? And if so, is it safe to call foo()?
Moreover, don't forget that the philosophy of C++ is to give you power and (most often) design guidelines, but "lets you shoot your feet" if you are really trying to do that.
There are dangerous, even meaningless things that you can do in C++ (will your compiler give you a warning, or an error, if you try to delete the same pointer twice?), but the language itself won't prevent you from doing them, under the assumption that you really, really know what you are doing.
//do some things, but I gotta be careful I don't do anything to vec
Clarification: You need to be careful that you don't do anything to vec that requires a precondition. You can do anything with vec that does not require any preconditions. For example you can assign vec a new value. You can call vec.clear(). You can call vec.size(). But do not call vec.pop_back() because that member function has a precondition.
If a type has a swap function which cannot fail, this can make it easier for other functions to provide the strong exception safety guarantee. This is because we can first do all of the function's work which may fail "off to the side", and then commit the work using non-throwing swaps.
However, are there any other benefits of guaranteeing that swap will never fail?
For example, is there a situation in which the presence of a no-fail swap makes it easier for another function to provide the basic guarantee?
Let's say I do this:
class C {
T a, b; // invariant: a > b
void swap(C& other);
};
It seems that there's no way to implement C::swap() with basic guarantee if T::swap(T&) might throw. I'd have to add a level of indirection and store T* instead of T.
The general guideline when writing classes (using the copy-and-swap idiom) is to provide a non throwing swap member function. (Effective C++, 3rd edition, Item 25 and other resources)
However, what if I cannot provide the nothrow guarantee because my class uses a 3rd party class member that doesn't provide a swap operation?
// Warning: Toy code !!!
class NumberBuffer {
public:
...
void swap(NumberBuffer& rhs);
public:
float* m_data;
size_t m_n;
CString m_desc;
};
void swap(NumberBuffer& lhs, NumberBuffer& rhs) {
lhs.swap(rhs);
}
void NumberBuffer::swap(NumberBuffer& rhs) {
using std::swap;
swap(m_data, rhs.m_data);
swap(m_n, rhs.m_n);
swap(m_desc, rhs.m_desc); // could throw if CString IsLocked and out-of-mem
}
CString swap cannot be made no-throw, so there's the off chance the swap could fail.
Note: For rare 3rd party classes, using a smart ptr (pimpl) would be an option, but --
Note: CString is a good example as noone in his right mind (?) would start holding all members of a conceptually simple and ubiquitous class like CString via pimpl (smart ptr) because that would really look horrible -- and on the other hand, there's no (short to mid-term) chance to get the CString modified to allow fully no-throw swap.
So, is it OK to have a potentially throwing swap member function if you can't help it? (Or do you know ways around this conundrum?)
Edit: And: Can a throwing swap member be used with the copy-and-swap idiom to provide the basic guarantee if not the strong guarantee?
So, is it OK to have a potentially throwing swap member function if you can't help it? (Or do you know ways around this conundrum?)
There is nothing inherently wrong with having a swap function that can potentially throw, but beware that without the strong exception guarantee in swap, it cannot possibly be used to provide exception safety, that is, it can only be used as swap (that is, forget about the copy-and-swap idiom for that particular class as a way of providing the strong exception guarantee... but you can still use it to reduce the amount of code --and document that it is not exception safe)
Alternatively, you can move the CString into a smart pointer that offers a no-throw swap (or at the very least the strong exception guarantee), not a nice solution, but it will at least be exception safe. Lastly, you can move away from CString altogether by using any other string library that provides whatever you need and offers a no-throw swap operation.
There's nothing inherently wrong with a throwing swap, it is just less useful than a no-throw version.
The copy and swap idiom doesn't need swap to be no-throw in order to provide the strong exception guarantee. swap needs only to provide the strong exception guarantee.
The difficulty is that if the no-throw guarantee cannot be provided, it is also likely that the strong exception guarantee cannot be provided. Naive swapping using a temporary and three copies only provides the basic guarantee unless the copying operation provides the no-throw guarantee, in which case swap is also no-throw.
You can easily make it nothrow:
void NumberBuffer::swap(NumberBuffer& rhs) throw()
{
try
{
std::swap(m_desc, rhs.m_desc); //could throw
std::swap(m_data, rhs.m_data);
std::swap(m_n, rhs.m_n);
}
catch(...)
{
}
}
Of course, this is no real solution to the problem, but now you at least got your non-throwing swap ;)
I'm refreshing my C++ knowledge after not having used it in anger for a number of years. In writing some code to implement some data structure for practice, I wanted to make sure that my code was exception safe. So I've tried to use std::auto_ptrs in what I think is an appropriate way. Simplifying somewhat, this is what I have:
class Tree
{
public:
~Tree() { /* delete all Node*s in the tree */ }
void insert(const string& to_insert);
...
private:
struct Node {
...
vector<Node*> m_children;
};
Node* m_root;
};
template<T>
void push_back(vector<T*>& v, auto_ptr<T> x)
{
v.push_back(x.get());
x.release();
}
void Tree::insert(const string& to_insert)
{
Node* n = ...; // find where to insert the new node
...
push_back(n->m_children, auto_ptr<Node>(new Node(to_insert));
...
}
So I'm wrapping the function that would put the pointer into the container, vector::push_back, and relying on the by-value auto_ptr argument to
ensure that the Node* is deleted if the vector resize fails.
Is this an idiomatic use of auto_ptr to save a bit of boilerplate in my
Tree::insert? Any improvements you can suggest? Otherwise I'd have to have
something like:
Node* n = ...; // find where to insert the new node
auto_ptr<Node> new_node(new Node(to_insert));
n->m_children.push_back(new_node.get());
new_node.release();
which kind of clutters up what would have been a single line of code if I wasn't
worrying about exception safety and a memory leak.
(Actually I was wondering if I could post my whole code sample (about 300 lines) and ask people to critique it for idiomatic C++ usage in general, but I'm not sure whether that kind of question is appropriate on stackoverflow.)
It is not idiomatic to write your own container: it is rather exceptional, and for the most part useful only for learning how to write containers. At any rate, it is most certainly not idiomatic to use std::autp_ptr with standard containers. In fact, it's wrong, because copies of std::auto_ptr aren't equivalent: only one auto_ptr owns a pointee at any given time.
As for idiomatic use of std::auto_ptr, you should always name your auto_ptr on construction:
int wtv() { /* ... */ }
void trp(std::auto_ptr<int> p, int i) { /* ... */ }
void safe() {
std::auto_ptr<int> p(new int(12));
trp(p, wtv());
}
void danger() {
trp(std::auto_ptr<int>(new int(12)), wtv());
}
Because the C++ standard allows arguments to evaluate in any arbitrary order, the call to danger() is unsafe. In the call to trp() in danger(), the compiler may allocate the integer, then create the auto_ptr, and finally call wtv(). Or, the compiler may allocate a new integer, call wtv(), and finally create the auto_ptr. If wtv() throws an exception then danger() may or may not leak.
In the case of safe(), however, because the auto_ptr is constructed a-priori, RAII guarantees it will clean up properly whether or not wtv() throws an exception.
Yes it is.
For example, see the interface of the Boost.Pointer Container library. The various pointer containers all feature an insert function taking an auto_ptr which semantically guarantees that they take ownership (they also have the raw pointer version but hey :p).
There are however other ways to achieve what you're doing with regards to exception safety because it's only internal here. To understand it, you need to understand what could go wrong (ie throw) and then reorder your instructions so that the operations that may throw are done with before the side-effects occur.
For example, taking from your post:
auto_ptr<Node> new_node(new Node(to_insert)); // 1
n->m_children.push_back(new_node.get()); // 2
new_node.release(); // 3
Let's check each line,
The constructor may throw (for example if the CopyConstructor of the type throws), in this case however you are guaranteed that new will perform the cleanup for you
The call to push_back may throw a std::bad_alloc if the memory is exhausted. That's the only error possible as copying a pointer is a no-throw operation
This is guaranteed not to throw
If you look closely, you'll remark that you would not have to worry if you could somehow have 2 being executed before 1. It is in fact possible:
n->m_children.reserve(n->m_children.size() + 1);
n->m_children.push_back(new Node(to_insert));
The call to reserve may throw (bad_alloc), but if it completes normally you are then guaranteed that no reallocation will occur until size becomes equal to capacity and you try another insertion.
The call to new may fall if the constructor throw, in which case new will perform the cleanup, if it completes you're left with a pointer that is immediately inserted in the vector... which is guaranteed not to throw because of the line above.
Thus, the use of auto_ptr may be replaced here. It was nonetheless a good idea, though as has been noted you should refrain from executing RAII initialization within a function evaluation.
I like the idea of declaring the ownership of pointer. This is one of the great features in c++0x, std::unique_ptrs. However std::auto_ptr is so hard to understand and lethal is even slightly misued I would suggest avoiding it entirely.
http://www.gotw.ca/publications/using_auto_ptr_effectively.htm
I have code that controls a mutex lock/unlock based on scope:
void PerformLogin()
{
ScopeLock < Lock > LoginLock( &m_LoginLock );
doLoginCommand();
ScopeLock < SharedMemoryBase > MemoryLock( &m_SharedMemory );
doStoreLogin();
...
}
Can I guarantee that MemoryLock will be destructed before LoginLock?
Yes, it is. In any particular scope local objects are destroyed in the reverse order that they were constructed.
Yes, destructors are called in the reverse order of construction.
Adding on to Neil's answer.
Consider if the opposite was true, that is that you couldn't predict the order of destructors for stack declared variables. That would make it nearly impossible to use dependent value types on the stack. Consider
void Foo() {
Type1 t1;
Type2 t2(&t1);
...
}
If C++ did not guarantee destructor ordering, straight forward code like this would be incredibly unsafe because it would be possible for t1 to be destroyed before t2's destructor ran. Hence you could not guarantee t2's destructor ran with a valid t1 value.
The question was answered already, but I'd like to add that I typically have a habit of writing something like this:
void PerformLogin()
{
ScopeLock < Lock > LoginLock( &m_LoginLock );
doLoginCommand();
{
ScopeLock < SharedMemoryBase > MemoryLock( &m_SharedMemory );
doStoreLogin();
...
}
}
In my opinion, this makes the intent clearer (*). That might be relevant if your code really is relying on the specific order. I find that this makes it less likely that someone accidentally changes the order, and causes a hard-to-find bug. (Well, that is of course a non-issue, since we all have tests in place, don't we?)
I always write the redundant parentheses in something like (a && b) || c too, and I find this matter quite similar.
(*): Of course, you could use a comment as well.
Yes, destructors are the reverse of constructors. Because destructors are used to delete the objects which are no longer required and constructor are use to create the objects.