Interfaces to string classes typically have of method named IsEmpty
(VCL) or empty
(STL). That's absolutely reasonable because it's a special case, but the code that uses these methods often has to negate this predicate, which leads to a "optical (and even psychological) overhead" (the exclamation mark is not very obvious, especially after an opening parenthesis). See for instance this (simplified) code:
/// format an optional time specification for output std::string fmtTime(const std::string& start, const std::string& end) { std::string time; if (!start.empty() || !end.empty()) { if (!start.empty() && !end.empty()) { time = "from "+start+" to "+end; } else { if (end.empty()) { time = "since "+start; } else { time = "until "+end; } } } return time; }
It has four negations, because the empty cases are those to be skipped. I often observe this kind of negation, also when designing interfaces, and it's not a big problem but it's annoying. I only wish to support writing understandable and easy-to-read code. I hope you'll understand my point.
Maybe I'm only struck with blindness: How would you solve the above problem?
Edit: After reading some comments, I think it's nessessary to say that the original code uses the class System::AnsiString
of the VCL. This class provides an IsEmpty
method, which is very readable:
if (text.IsEmpty()) { /* ... */ } // read: if text is empty ...
if not negated:
if (!text.IsEmpty()) { /* ... */} // read: if not text is empty ...
...instead of if text is not empty. I think the literal is
was better left to the reader's fantasy to let also the negation work well. Ok, maybe not a widespread problem...
isNotEmpty. Determines if a string is not empty. Its the exact opposite for isEmpty(String) .
isBlank() vs isEmpty() The difference between both methods is that isEmpty() method returns true if, and only if, string length is 0. isBlank() method only checks for non-whitespace characters. It does not check the string length.
The isEmpty() method checks whether a string is empty or not. This method returns true if the string is empty (length() is 0), and false if not.
Use the length property to check if a string is empty, e.g. if (str. length === 0) {} . If the string's length is equal to 0 , then it's empty, otherwise it isn't empty.
In most cases you can reverse the order of the if
and the else
to clean up the code:
const std::string fmtTime(const std::string& start, const std::string& end) { std::string time; if (start.empty() && end.empty()) { return time; } if (start.empty() || end.empty()) { if (end.empty()) { time = "since "+start; } else { time = "until "+end; } } else { time = "from "+start+" to "+end; } return time; }
Or even cleaner after some more refactoring:
std::string fmtTime(const std::string& start, const std::string& end) { if (start.empty() && end.empty()) { return std::string(); } if (start.empty()) { return "until "+end; } if (end.empty()) { return "since "+start; } return "from "+start+" to "+end; }
And for the ultimate compactness (although I prefer the previous version, for its readability):
std::string fmtTime(const std::string& start, const std::string& end) { return start.empty() && end.empty() ? std::string() : start.empty() ? "until "+end : end.empty() ? "since "+start : "from "+start+" to "+end; }
Another possibility is to create a helper function:
inline bool non_empty(const std::string &str) { return !str.empty(); } if (non_empty(start) || non_empty(end)) { ... }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With