Reputation: 2863
Say I have this function:
void doThings(uint8_t index) {
if (an index is given) { ... }
}
Usually, an invalid index is -1, so that if statement would be if (index != -1)
. What if I'm using an unsigned integer to represent the index? Would it be silly to change the function definition to a signed int, just so I can test for -1
? Is there a universally accepted number representing 'no index' for unsigned ints?
Upvotes: 1
Views: 307
Reputation: 17415
Ask yourself what values are valid for the index. Typically, you have an array and the length of the array defines the valid range. If that array happens to be 256 elements long, you use every possible value of a uint8_t
as valid index, which means that you need more to represent an "invalid index". If the array is smaller, any index out of range of that array is invalid, customarily the highest value would be used for that (i.e. static_cast<uint8_t>(-1)
or using functions from the <limits>
header).
There are a bunch of approaches here already, like e.g. an additional flag, using optional<uint8_t>
(which you should remember, as it is applicable in any place where you have similar requirements, not just for indices) or using an overload (which is probably not the way to go since this requires a compile-time decision).
Instead, I'd use a larger index type. The usual type used to represent an index is size_t
, which is an unsigned integral type that typically has the size of a pointer (i.e. 32 or 64 bit on common computers). If you switch to that, you will be able to address even the largest of arrays that you will ever have in memory (not e.g. on disk!). With that, you can also use static_cast<size_t>(-1)
as signal value to represent "invalid index".
Upvotes: 0
Reputation: 27577
Simply overload doThings
, something like this:
void doThings(uint8_t index) {
// do things for a given index
}
void doThings() {
// do things for no index
}
Or, if you're simply passing the results of a function, say findElement
use a std::pair
something like:
std::pair<std::uint8_t, bool> findElement(...);
void doThings(std::pair<std::uint8_t, bool>& arg) {
if (arg.second) {
// do things for given element arg.first
and call it with:
doThings(findElement(...));
Upvotes: 3
Reputation: 9406
I would go with a Maybe type.
// include some optional type, e.g. experimental/optional or boost/optional.hpp
using maybe_index = optional<std::uint8_t>;
void doThings(maybe_index index) {
if (index) { ... }
else { ... }
}
if I needed to be able to represent an index plus a special invalid state. GCC has an implementation of the proposed std::optional
in std::experimental
, and a Boost version is available.
Upvotes: 0
Reputation: 3112
It's not silly to change the function definition to use a signed integer so you can check against -1. This is a common practice.
However, if the function is part of a well-defined and documented API that is used by others, then you may not want to change the function to used a signed int. Instead, I would suggest using MAX_INT (or 0xFF for a uint8_t) as a flag for an invalid index.
Upvotes: 0
Reputation: 22624
If you must take into account the two situations in the same function, a better option may be to just provide a second parameter.
void doThings(uint8_t index, bool indexGiven) {
if (indexGiven) { ... }
}
However, using two entirely different functions, one for when the index is given and one for when it is not, may lead to a cleaner design.
Upvotes: 2