views:

144

answers:

3

I'm attempting to implement the atomic library from the C++0x draft. Specifically, I'm implementing §29.6/8, the store method:

template <typename T>
void atomic<T>::store(T pDesired, memory_order pOrder = memory_order_seq_cst);

The requirement states:

The order argument shall not be memory_order_consume, memory_order_acquire, nor memory_order_acq_rel.

I'm not sure what to do if it is one of these. Should I do nothing, throw an exception, get undefined behavior, or do something else?

P.S.: "C++0X" looks kinda like a dead fish :3

+7  A: 

Do what you want. It doesn't matter.

When ISO state that you "shall not do something", doing it is undefined behaviour. I would opt for whatever makes your implementation "better" (in your eyes, be that faster, more readable, subject to the principle of least astonishment, and so forth).

Myself, I'd go for readability (since I would have to maintain the thing) with speed taking a close second.

paxdiablo
A: 

I'd rather get vaguey sane behaviour that something crazy.

Well, as a potential consumer of your library, here's what I'd like: if there's no performance cost to the documented usage, then see if one of the memory_order values provides a functional superset of the others, particularly something corresponding to what a caller might naively expect the unsupported modes to do (if any sensible expectation can be formed). The caller may get the slowest, safest mode, but that's better than something functionally wrong. You minimise the client code's dependency on getting everything perfect for your code. The problem with this - compared to an assert/exception - is that it can go unnoticed in a test environment, so consider also writing an explanation to std::cerr, using a static variable to limit the messages to one per process run. That's a very useful diagnostic.

An exception, fatal assertion etc. might bring down a client application at a very inconvenient moment.... Seems a bit draconian, and not something I'd appreciate particularly. Another option is to have an environment variable control this behaviour.

(There's presumably a similar issue for values that aren't even in your current enumeration.)

Tony
If you rely on undefined behaviour, I'd rather my implementation reformat your hard disk (which is actually valid behaviour). That's because, while you're recovering it, you won't be pushing out buggy code :-) There can be _no_ functionally wrong behaviour if it's undefined, that's what "undefined" means.
paxdiablo
@paxdiablo. What a pointless statement. Undefined behaviour is typically accidentally invoked, hence the question in the first place. Grow up. It's called "defensive programming", and I'm sick of all the design-by-contract types acting like bad boys.
Tony
What you'd rather get is irrelevant. The standard states that you're not allowed to do it so the implementor is free to do whatever they want. They may choose to handle it gracefully or they may (and this is my preference) just ignore it and you can fix your own code. I'm not there to handhold you if you break the rules. Its no different to people moaning about the fact that `i = ++i + i++;` doesn't work as expected - the answer is _don't do it!_ And, if it was you that -1'ed my answer (assumption on my part but probably correct given your tone), I guess I'm "grown up" enough not to retaliate.
paxdiablo
As you say the implementers are free to do whatever they want. And what might that be? Evidently, to ask some C++ users for some ideas, to round out his own notions about what might be best. Good on him. I'd prefer not going to rehash the whole defensive vs DBC debate - been there and done that - here, just presenting the options for Joel's consideration. And I only rate answers on technical merits as I see them. Enjoy your tight-rope.
Tony
Re ++i + i++ - relevance? It's the way it is for performance reasons, and there is no "correct" (even if slow) behaviour defined to fall back on. I've never said there shouldn't be undefined behaviour, nor that an implementation's actual behaviour - where undefined in the Standard - should be documented or promoted as dependable in any way. Better analogy: a power supply - your device onlys promise to work within certain constraints, but - even if only in markets where they're cheap - why not have a fuse to handle lightning, short circuits? That's not bad design :-).
Tony
@Tony: html was design with the motto: "be liberal in what you accept, ..." it proved a very unfortunate decision. Better make it clear to the client that it'll never work rather than have them stretch the method to its limits and have it backfire when all stars are aligned.
Matthieu M.
@Matthieu: what I've said is more in line with your comment than what paxdiablo's said. I recommend actually checking and issuing a warning message. paxdiablo says ideally you wouldn't even check. Of course others will advocate aborting the process or an exception - that's a reasonable alternative, just not my preference.
Tony
@Tony: I understand that people are not necessarily happy about `abort`, personally I am in favor of `assert` and a comprehensive test suite. If you don't test your code, you don't know whether it works or not. You can also tweak the assert macro and have it throw an exception in release mode instead, to get a smoother error (with the possibility of recovery, important for servers).
Matthieu M.
@Matthieu: yes - that's best when you control the build/testing standards. For a low-level library implementer, you can bet some client code won't have dev vs release builds you can test for or depend on consistent correct deployment of. Then, back to abort vs throw vs log-and-continue vs ignore. Impractical to statistically sample C++ projects to guide choice. So, by my experience 1) do something sane if possible while logging to stderr 2) throw 3) abort the process 4) don't check + let it fall out whichever way it will. Performance and code maintenance concerns can change priorities.
Tony
@Tony: logging to stderr isn't practical for a library, since you don't know in which conditions it'll be used. I agree with the order of 2/3/4 for production code (when performance allows...)
Matthieu M.
A: 

I prefer a compile time error. If not that, then an assert() failure.

Assert is good because it compiles out of the release version and will not impact performance.

Compile time errors are even better because they provide more immediate feedback without waiting for the software to trip over the bug. Compile time error checks are a thing I love about C++ code over Python, Ruby, Perl code.

Zan Lynx