#include <algorithm>
// using std::find
#include <initializer_list>
// using std::initializer_list
#include <cstdarg>
// using std::va_list, va_start, va_end
/* Some dummy code for use in the examples */
struct Foo
{
int x;
bool operator==(const Foo& o) const
{ return x == o.x; }
};
void bar(const Foo&);
/* Code smell no 1: Multiple copy-and-paste function calls */
void original_1()
{
Foo a, b, c;
bar(a);
bar(b);
bar(c);
}
/* Solution: Use a range-based loop with an initializer_list */
void refactored_1()
{
Foo a, b, c;
for(const Foo* i: {&a, &b, &c})
bar(*i);
}
/* Code smell no 2: variable argument lists in the 21st century
*
* Before anyone complains; yes there are still valid reasons for using them,
* however, here we have better alternatives
*/
Foo original_2(Foo searched, Foo def, int n, ...)
{
std::va_list ap;
va_start(ap, n);
for(int i = 0; i < n; ++i) {
Foo cur = va_arg(ap, Foo);
if(cur == searched) {
va_end(ap);
return cur;
}
}
va_end(ap);
return def;
}
/* Solution: initializer_list for arguments
*
* Obviously, this only works because we expect just a single type.
* Replacing printf-style functions is still a challenge
*/
Foo refactored_2(Foo searched, Foo def, const std::initializer_list<Foo>& lst)
{
std::initializer_list<Foo>::iterator found =
std::find(lst.begin(), lst.end(), searched);
return found == lst.end() ? def : *found;
}
/* Alternative solution: Pass iterators
*
* This is arguably more idiomatic, universal, and also works with obsolete
* compilers.
* On the other hand, it requires at least one more line of code to call it.
* It still profits from initializer_lists, as demonstrated below
*/
template<class Iterator>
Foo alternative_2(Foo searched, Foo def, Iterator first, Iterator last)
{
Iterator found = std::find(first, last, searched);
return found == last ? def : *found;
}
/* Demonstrates the usage of the various solutions for smell no. 2 */
void call_2()
{
Foo a, b, c, d, e;
Foo f = original_2(a, b, 3, c, d, e);
Foo g = refactored_2(a, b, {c, d, e});
const auto& lst = {c, d, e};
Foo h = alternative_2(a, b, lst.begin(), lst.end());
/* for completeness: alternative_2 with C++03 */
const Foo arr[] = {c, d, e};
Foo i = alternative_2(a, b, arr, arr + sizeof(arr)/ sizeof(*arr));
}
Comments