Reputation: 191069
I like the feature in Python that can return None when it doesn't find the correct return value. For example:
def get(self, key):
if key in self.db:
return self.db[key]
return None
I need to implement the same feature in C++. I think about some possibilities.
bool get(string key, int& result)
{
if (in(key, db)) {
result = db[key];
return true;
}
return false;
}
int get(string key) throw (int)
{
if (in(key, db)) {
result = db[key];
return result;
}
throw 0;
}
try {
....
}
catch (int n)
{
cout << "None";
}
pair<bool, int> getp(int i)
{
if (...) {
return pair<bool, int>(true, 10);
}
return pair<bool,int>(false, 20);
}
pair<bool, int> res = getp(10);
if (res.first) {
cout << res.second;
}
Which one is normally used in C++? Are there any other ways to do it in C++?
Upvotes: 13
Views: 17884
Reputation: 191069
When returning pointer, I can use reinterpret_cast for NULL return.
class A
{
};
A* a(int i)
{
if (i == 0) return new A();
return reinterpret_cast<A*>(NULL);
}
int main(int argc, char *argv[]) {
A* result = a(1); // result is NULL
if (result == NULL) {
cout << "NULL returned";
}
result = a(0);
if (result != NULL) {
cout << "NON NULL returned";
}
}
Upvotes: 1
Reputation: 425
I achieved the good/bad return value using a small custom Checked
templated class.
My actual class was a little more comprehensive, including assignment operators, error reason strings and specialisations for reference types, et cetera, which is why I didn't use boost::optional<T>
. I could publish the full class if there is interest.
The general gist of the class is this:
static const class Bad {} None;
template<typename ValueType>
class Checked
{
public:
// Constructor for good value.
Checked(ValueType x)
: value(x), valid(true)
{}
// Constructor for bad value.
Checked(Bad)
: value(), valid(false)
{}
operator ValueType(void) const
{
if (!valid)
;//assert or throw...
return value;
}
ValueType value;
bool valid;
};
This can be used like so:
Checked<int> Divide(int numerator, int denominator)
{
if (denominator == 0)
return Bad(); // or None;
return numerator / denominator; // Automatically uses the "good value" constructor
}
or:
Checked<int> result = Divide(4, 5);
if (result.valid)
std::cout << result; // or result.value
else
std::cout << "Bad!";
This approach is often more efficient than the reference approach because of return value optimisation.
Upvotes: 3
Reputation: 18572
I would say that those three methods are all very common in C++.
It goes without saying that if the return type already can have some sort of "invalid" or "zombie" state (e.g., like a NULL
pointer or a NaN
number), then that might just be the easiest thing to use.
The "take output-parameter by reference and return an error-code" is the more traditional C-style way of doing things, which is, of course, very common. The tradition is to return 0 on success and some error-code on failure (any non-zero value).
The "throw an exception if you can't return a value" generally makes sense if you adopt exceptions in your code. This is very common, but not universally accepted (not everyone likes or uses exceptions for the same purposes).
Those first two options are in a never-ending feud (i.e., error-codes vs. exceptions), and it really depends on which side you pick. So, I would refer you to that debate (which is too subjective for StackOverflow, of course).
The "return a pair of bool
and value" is, I would say, less common, but still I've seen this many times. With the adoption of tuples (boost::tuple
or std::tuple
(C++11)) and with the use of tiers (boost::tie
or std::tie
(C++11)), the whole idea of returning multiple values from a function (like many languages allow) is ever more attractive and used in practice.
Among other options, you have boost::optional<T>
, whose name is pretty self-explanatory (basically, the third option (pair) wrapped in a prettier package). And you might also have Alexandrescu's Expected template, which gives you a hybrid of all three options such that you get a return value bundled with a flag to know if it is valid or not, and bundled with an exception describing why it couldn't produce the value, which would be automatically thrown if you attempt to read the invalid value. However, the template requires C++11 features to work.
Upvotes: 1
Reputation: 6026
I think different projects in C++ use different standards, but Return true/false you mentioned could be the most common way in C++, although some people prefer to return false on success while the others return true on success. In other cases, if the value you would like to get is a pointer, then returning null is another common way in C++.
For example, if you're working on Microsoft related projects, then the most common way is to return HRESULT, which is a return type introduced by Microsoft.
In linux, functions usually return 0 on success, and non-zero value indicates error code. (you may find this discussion helpful).
Upvotes: 1
Reputation: 96291
The normal C++ way to do this (note: C++ is not Python) is to return iterators from such functions and return end()
when the item can't be found.
If you wish to use non-iterator return values however, use boost::optional
and return boost::none
when you would return Python's None
.
Definitely don't use throw
unless you expect to never have the error case during normal execution.
Upvotes: 17