[ACCEPTED]-What you think about throwing an exception for not found in C++?-usagepatterns
The STL deals with this situation by using 5 iterators. For example, the std::map class 4 has a similar function:
iterator find( const key_type& key );
If the key isn't 3 found, it returns 'end()'. You may want 2 to use this iterator approach, or to use 1 some sort of wrapper for your return value.
The correct answer (according to Alexandrescu) is:
Optional
and Enforce
First 9 of all, do use the Accessor, but in a safer 8 way without inventing the wheel:
boost::optional<X> get_X_if_possible();
Then create 7 an enforce
helper:
template <class T, class E>
T& enforce(boost::optional<T>& opt, E e = std::runtime_error("enforce failed"))
{
if(!opt)
{
throw e;
}
return *opt;
}
// and an overload for T const &
This way, depending on what might 6 the absence of the value mean, you either 5 check explicitly:
if(boost::optional<X> maybe_x = get_X_if_possible())
{
X& x = *maybe_x;
// use x
}
else
{
oops("Hey, we got no x again!");
}
or implicitly:
X& x = enforce(get_X_if_possible());
// use x
You use the 4 first way when you’re concerned about efficiency, or 3 when you want to handle the failure right 2 where it occurs. The second way is for all 1 other cases.
Don't use an exception in such a case. C++ has 47 a nontrivial performance overhead for such 46 exceptions, even if no exception is thrown, and it additially makes reasoning 45 about the code much harder (cf. exception 44 safety).
Best-practice in C++ is one of the 43 two following ways. Both get used in the 42 STL:
- As Martin pointed out, return an iterator. Actually, your iterator can well be a
typedef
for a simple pointer, there's nothing speaking against it; in fact, since this is consistent with the STL, you could even argue that this way is superior to returning a reference. - Return a
std::pair<bool, yourvalue>
. This makes it impossible to modify the value, though, since a copycon of thepair
is called which doesn't work with referende members.
/EDIT:
This answer has spawned quite some controversy, visible 41 from the comments and not so visible from 40 the many downvotes it got. I've found this 39 rather surprising.
This answer was never 38 meant as the ultimate point of reference. The 37 “correct” answer had already been given 36 by Martin: execeptions reflect the behaviour 35 in this case rather poorly. It's semantically 34 more meaningful to use some other signalling 33 mechanism than exceptions.
Fine. I completely 32 endorse this view. No need to mention it 31 once again. Instead, I wanted to give an 30 additional facet to the answers. While minor 29 speed boosts should never be the first rationale 28 for any decision-making, they can provide 27 further arguments and in some (few) cases, they 26 may even be crucial.
Actually, I've mentioned 25 two facets: performance and exception safety. I 24 believe the latter to be rather uncontroversial. While 23 it's extremely hard to give strong exceptions 22 guarantees (the strongest, of course, being 21 “nothrow”), I believe it's essential: any 20 code that is guaranteed to not throw exceptions 19 makes the whole program easier to reason 18 about. Many C++ experts emphasize this (e.g. Scott 17 Meyers in item 29 of “Effective C++”).
About 16 speed. Martin York has pointed out that 15 this no longer applies in modern compilers. I 14 respectfully disagree. The C++ language 13 makes it necessary for the environment to 12 keep track, at runtime, of code paths that 11 may be unwound in the case of an exception. Now, this 10 overhead isn't really all that big (and 9 it's quite easy to verify this). “nontrivial” in 8 my above text may have been too strong.
However, I 7 find it important to draw the distinction 6 between languages like C++ and many modern, “managed” languages 5 like C#. The latter has no additional overhead 4 as long as no exception is thrown because 3 the information necessary to unwind the 2 stack is kept anyway. By and large, stand 1 by my choice of words.
The problem with exists() is that you'll 11 end up searching twice for things that do 10 exist (first check if it's in there, then 9 find it again). This is inefficient, particularly 8 if (as its name of "list" suggests) your 7 container is one where searching is O(n).
Sure, you 6 could do some internal caching to avoid 5 the double search, but then your implementation 4 gets messier, your class becomes less general 3 (since you've optimised for a particular 2 case), and it probably won't be exception-safe 1 or thread-safe.
STL Iterators?
The "iterator" idea proposed before 94 me is interesting, but the real point of 93 iterators is navigation through a container. Not 92 as an simple accessor.
If you're accessor 91 is one among many, then iterators are the 90 way to go, because you will be able to use 89 them to move in the container. But if your 88 accessor is a simple getter, able to return 87 either the value or the fact there is no value, then your 86 iterator is perhaps only a glorified pointer...
Which 85 leads us to...
Smart pointers?
The point of smart pointers 84 is to simplify pointer ownership. With a 83 shared pointer, you'll get a ressource (memory) which 82 will be shared, at the cost of an overhead 81 (shared pointers needs to allocate an integer 80 as a reference counter...).
You have to choose: Either 79 your Value is already inside a shared pointer, and 78 then, you can return this shared pointer 77 (or a weak pointer). Or Your value is inside 76 a raw pointer. Then you can return the row 75 pointer. You don't want to return a shared pointer if your ressource is not already inside a shared pointer: A World of funny things will happen 74 when your shared pointer will get out of 73 scope an delete your Value without telling 72 you...
:-p
Pointers?
If your interface is clear about 71 its ownership of its ressources, and by 70 the fact the returned value can be NULL, then 69 you could return a simple, raw pointer. If 68 the user of your code is dumb enough ignore 67 the interface contract of your object, or 66 to play arithmetics or whatever with your 65 pointer, then he/she will be dumb enough 64 to break any other way you'll choose to 63 return the value, so don't bother with the 62 mentally challenged...
Undefined Value
Unless your Value 61 type really has already some kind of "undefined" value, and 60 the user knows that, and will accept to 59 handle that, it is a possible solution, similar 58 to the pointer or iterator solution.
But 57 do not add a "undefined" value 56 to your Value class because of the problem you 55 asked: You'll end up raising the "references 54 vs. pointer" war to another level of 53 insanity. Code users want the objects you 52 give them to either be Ok, or to not exist. Having 51 to test every other line of code this object 50 is still valid is a pain, and will complexify 49 uselessly the user code, by your fault.
Exceptions
Exceptions 48 are usually not as costly as some people 47 would like them to be. But for a simple 46 accessor, the cost could be not trivial, if 45 your accessor is used often.
For example, the 44 STL std::vector has two accessors to its 43 value through an index:
T & std::vector::operator[]( /* index */ )
and:
T & std::vector::at( /* index */ )
The difference 42 being that the []
is non-throwing . So, if you access 41 outside the range of the vector, you're 40 on your own, probably risking memory corruption, and 39 a crash sooner or later. So, you should 38 really be sure you verified the code using 37 it.
On the other hand, at
is throwing. This means that 36 if you access outside the range of the vector, then 35 you'll get a clean exception. This method 34 is better if you want to delegate to another 33 code the processing of an error.
I use personnaly 32 the []
when I'm accessing the values inside 31 a loop, or something similar. I use at
when 30 I feel an exception is the good way to return 29 the current code (or the calling code) the 28 fact something went wrong.
So what?
In your case, you 27 must choose:
If you really need a lightning-fast 26 access, then the throwing accessor could 25 be a problem. But this means you already 24 used a profiler on your code to determinate 23 this is a bottleneck, doesn't it?
;-)
If you 22 know that not having a value can happen 21 often, and/or you want your client to propagate 20 a possible null/invalid/whatever semantic 19 pointer to the value accessed, then return 18 a pointer (if your value is inside a simple 17 pointer) or a weak/shared pointer (if your 16 value is owned by a shared pointer).
But 15 if you believe the client won't propagate 14 this "null" value, or that they 13 should not propagate a NULL pointer (or 12 smart pointer) in their code, then use the 11 reference protected by the exception. Add 10 a "hasValue" method returning 9 a boolean, and add a throw should the user 8 try the get the value even if there is none.
Last 7 but not least, consider the code that will 6 be used by the user of your object:
// If you want your user to have this kind of code, then choose either
// pointer or smart pointer solution
void doSomething(MyClass & p_oMyClass)
{
MyValue * pValue = p_oMyClass.getValue() ;
if(pValue != NULL)
{
// Etc.
}
}
MyValue * doSomethingElseAndReturnValue(MyClass & p_oMyClass)
{
MyValue * pValue = p_oMyClass.getValue() ;
if(pValue != NULL)
{
// Etc.
}
return pValue ;
}
// ==========================================================
// If you want your user to have this kind of code, then choose the
// throwing reference solution
void doSomething(MyClass & p_oMyClass)
{
if(p_oMyClass.hasValue())
{
MyValue & oValue = p_oMyClass.getValue() ;
}
}
So, if 5 your main problem is choosing between the 4 two user codes above, your problem is not 3 about performance, but "code ergonomy". Thus, the 2 exception solution should not be put aside 1 because of potential performance issues.
:-)
Accessor?
The "iterator" idea proposed before me is 10 interesting, but the real point of iterators 9 is navigation through a container. Not as 8 an simple accessor.
I agree with paercebal, an iterator 7 is to iterate. I don't like the way STL does. But 6 the idea of an accessor seems more appealing. So 5 what we need? A container like class that 4 feels like a boolean for testing but behaves 3 like the original return type. That would 2 be feasible with cast operators.
template <T> class Accessor { public: Accessor(): _value(NULL) {} Accessor(T &value): _value(&value) {} operator T &() const { if (!_value) throw Exception("that is a problem and you made a mistake somewhere."); else return *_value; } operator bool () const { return _value != NULL; } private: T *_value; };
Now, any 1 foreseeable problem? An example usage:
Accessor <type> value = list.get(key); if (value) { type &v = value; v.doSomething(); }
How about returning a shared_ptr as the 3 result. This can be null if the item wasn't 2 found. It works like a pointer, but it will 1 take care of releasing the object for you.
(I realize this is not always the right answer, and my tone a bit strong, but you should consider this question before deciding for other more complex alternatives):
So, what's wrong with returning a pointer?
I've 9 seen this one many times in SQL, where people 8 will do their earnest to never deal with 7 NULL columns, like they have some contagious 6 decease or something. Instead, they cleverly 5 come up with a "blank" or "not-there" artificial 4 value like -1, 9999 or even something like 3 '@X-EMPTY-X@'.
My answer: the language already 2 has a construct for "not there"; go ahead, don't 1 be afraid to use it.
what I prefer doing in situations like this 7 is having a throwing "get" and for those 6 circumstances where performance matter or 5 failiure is common have a "tryGet" function 4 along the lines of "bool tryGet(type key, value 3 **pp)" whoose contract is that if true is 2 returned then *pp == a valid pointer to 1 some object else *pp is null.
@aradtke, you said.
I agree with paercebal, an 32 iterator is to iterate. I don't like the 31 way STL does. But the idea of an accessor seems 30 more appealing. So what we need? A container 29 like class that feels like a boolean for 28 testing but behaves like the original 27 return type. That would be feasible with 26 cast operators. [..] Now, any foreseeable 25 problem?
First, YOU DO NOT WANT OPERATOR 24 bool. See Safe Bool idiom for more info. But about your 23 question...
Here's the problem, users need 22 to now explict cast in cases. Pointer-like-proxies (such 21 as iterators, ref-counted-ptrs, and raw 20 pointers) have a concise 'get' syntax. Providing 19 a conversion operator is not very useful 18 if callers have to invoke it with extra 17 code.
Starting with your refence like example, the 16 most concise way to write it:
// 'reference' style, check before use
if (Accessor<type> value = list.get(key)) {
type &v = value;
v.doSomething();
}
// or
if (Accessor<type> value = list.get(key)) {
static_cast<type&>(value).doSomething();
}
This is okay, don't 15 get me wrong, but it's more verbose than 14 it has to be. now consider if we know, for 13 some reason, that list.get will succeed. Then:
// 'reference' style, skip check
type &v = list.get(key);
v.doSomething();
// or
static_cast<type&>(list.get(key)).doSomething();
Now 12 lets go back to iterator/pointer behavior:
// 'pointer' style, check before use
if (Accessor<type> value = list.get(key)) {
value->doSomething();
}
// 'pointer' style, skip check
list.get(key)->doSomething();
Both 11 are pretty good, but pointer/iterator syntax 10 is just a bit shorter. You could give 'reference' style 9 a member function 'get()'... but that's 8 already what operator*() and operator->() are 7 for.
The 'pointer' style Accessor now has 6 operator 'unspecified bool', operator*, and 5 operator->.
And guess what... raw pointer 4 meets these requirements, so for prototyping, list.get() returns 3 T* instead of Accessor. Then when the design 2 of list is stable, you can come back and 1 write the Accessor, a pointer-like Proxy type.
More Related questions
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.