Browse DevX
Sign up for e-mail newsletters from DevX


Using the Transform() Algorithm to Change a String's Case : Page 2

The task of converting strings to uppercase is just like many other programming tasks: everyone needs it and everyone does it. The problem is with how they do it. Often it is done the hard wayreimplementing the wheel or introducing unnecessary performance penalties, maintenance problems, and other complexities. This month's solution demonstrates how the transform() algorithm is a faster, cleaner and more flexible method.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

Converting the Hard Way
There are numerous ways to change the case of a string. A naive implementation might look like this:

#include <cctype> #include <string> using namespace std; int main() { string s="hello"; for (int j=0; j<s.length(); ++j) { s[j]=toupper(s[j]); } // s now contains "HELLO" }

Though functionally correct, this loop is a maintenance headache. To apply a different type of transformation to the string, say to convert it to lowercase or transliterate all characters to their Cyrillic equivalent, you'll have to rewrite the loop's body. To improve the design, separate the string transformation into two operations: one that iterates through the string's elements and one that actually transforms every element. You gain more flexibility by decoupling these operations and simplify future maintenance.

Step 1: Iteration
The transform() algorithm defined in <algorithm> is rather flexible. Not only does it separate between the iterations and transformation operations, it also allows you to transform only a portion of the string. In addition, you can store the result in a different destination, should you prefer to keep the original string intact. The transform() algorithm has two overloaded versions but we will use only the following one:

OutputIterator transform(InputIterator first, InputIterator last, OutputIterator result, UnaryOperation unary_op);

You can find an explanation about the different iterator categories here. The first and second arguments are iterators pointing to the beginning and the end of the sequence being transformed. The third argument is an iterator pointing to the beginning of the destination sequence. If you wish to overwrite the current string, result and first should have identical values.

Step 2: Transformation
The fourth argument is a unary operator. It can either be an address of a function that takes a single argument or a function object. STL algorithms don't really care whether a unary operator is a function object or an address because they merely append () to it and let the compiler takes care of the rest. This example uses the standard toupper() function declared in <cctype>:

#include <cctype> // for toupper #include <string> #include <algorithm> using namespace std; string s="hello"; transform(s.begin(), s.end(), s.begin(), toupper);

Alas, the program above will not compile because the name 'toupper' is ambiguous. It can refer either to:

int std::toupper(int); // from <cctype>


template <class chart> charT std::toupper(charT, const locale&);// from <locale>

Use an explicit cast to resolve the ambiguity:

std::transform(s.begin(), s.end(), s.begin(), (int(*)(int)) toupper);

This will instruct the compiler to choose the right toupper().

Thanks for your registration, follow us on our social networks to keep up-to-date