Follow Techotopia on Twitter

On-line Guides
All Guides
eBook Store
iOS / Android
Linux for Beginners
Office Productivity
Linux Installation
Linux Security
Linux Utilities
Linux Virtualization
Linux Kernel
System/Network Admin
Programming
Scripting Languages
Development Tools
Web Development
GUI Toolkits/Desktop
Databases
Mail Systems
openSolaris
Eclipse Documentation
Techotopia.com
Virtuatopia.com
Answertopia.com

How To Guides
Virtualization
General System Admin
Linux Security
Linux Filesystems
Web Servers
Graphics & Desktop
PC Hardware
Windows
Problem Solutions
Privacy Policy

  




 

 

Thinking in C++ Vol 2 - Practical Programming
Prev Home Next

Taking the address of a generated function template

In some situations you need to take the address of a function. For example, you may have a function that takes an argument of a pointer to another function. It s possible that this other function might be generated from a template function, so you need some way to take that kind of address:[55]

//: C05:TemplateFunctionAddress.cpp {-mwcc}
// Taking the address of a function generated
// from a template.
 
template<typename T> void f(T*) {}
 
void h(void (*pf)(int*)) {}
 
template<typename T> void g(void (*pf)(T*)) {}
 
int main() {
h(&f<int>); // Full type specification
h(&f); // Type deduction
g<int>(&f<int>); // Full type specification
g(&f<int>); // Type deduction
g<int>(&f); // Partial (but sufficient) specification
} ///:~
 

This example demonstrates a number of issues. First, even though you re using templates, the signatures must match. The function h( ) takes a pointer to a function that takes an int* and returns void, and that s what the template f( ) produces. Second, the function that wants the function pointer as an argument can itself be a template, as in the case of the template g( ).

In main( ) you can see that type deduction works here, too. The first call to h( ) explicitly gives the template argument for f( ), but since h( ) says that it will only take the address of a function that takes an int*, that part can be deduced by the compiler. With g( ) the situation is even more interesting because two templates are involved. The compiler cannot deduce the type with nothing to go on, but if either f( ) or g( ) is given int, the rest can be deduced.

An obscure issue arises when trying to pass the functions tolower or toupper, declared in <cctype>, as parameters. It is possible to use these, for example, with the transform algorithm (which is covered in detail in the next chapter) to convert a string to lower or upper case. You must be careful because there are multiple declarations for these functions. A naive approach would be something like this:

// The variable s is a std::string
transform(s.begin(), s.end(), s.begin(), tolower);
 

The transform algorithm applies its fourth parameter (tolower( ) in this case) to each character in the string s and places the result in s itself, thus overwriting each character in s with its lower-case equivalent. As it is written, this statement may or may not work! It fails in the following context:

//: C05:FailedTransform.cpp {-xo}
#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>
using namespace std;
 
int main() {
string s("LOWER");
transform(s.begin(), s.end(), s.begin(), tolower);
cout << s << endl;
} ///:~
 

Even if your compiler lets you get away with this, it is illegal. The reason is that the <iostream> header also makes available a two-argument version of tolower( ) and toupper( ):

template<class charT> charT toupper(charT c,
const locale& loc);
template<class charT> charT tolower(charT c,
const locale& loc);
 

These function templates take a second argument of type locale. The compiler has no way of knowing whether it should use the one-argument version of tolower( ) defined in <cctype> or the one mentioned above. You can solve this problem (almost!) with a cast in the call to transform, as follows:

transform(s.begin(),s.end(),s.begin()
static_cast<int (*)(int)>(tolower));
 

(Recall that tolower( ) and toupper( ) work with int instead of char.) The cast above makes clear that the single-argument version of tolower( ) is desired. This works with some compilers, but it is not required to. The reason, albeit obscure, is that a library implementation is allowed to give C linkage (meaning that the function name does not contain all the auxiliary information[56] that normal C++ functions do) to functions inherited from the C language. If this is the case, the cast fails because transform is a C++ function template and expects its fourth argument to have C++ linkage and a cast is not allowed to change the linkage. What a predicament!

The solution is to place tolower( ) calls in an unambiguous context. For example, you could write a function named strTolower( ) and place it in its own file without including <iostream>, like this:

//: C05:StrTolower.cpp {O} {-mwcc}
#include <algorithm>
#include <cctype>
#include <string>
using namespace std;
 
string strTolower(string s) {
transform(s.begin(), s.end(), s.begin(), tolower);
return s;
} ///:~
 

The header <iostream> is not involved here, and the compilers we use do not introduce the two-argument version of tolower( ) in this context,[57] so there s no problem. You can then use this function normally:

//: C05:Tolower.cpp {-mwcc}
//{L} StrTolower
#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>
using namespace std;
string strTolower(string);
 
int main() {
string s("LOWER");
cout << strTolower(s) << endl;
} ///:~
 

Another solution is to write a wrapper function template that calls the correct version of tolower( ) explicitly:

//: C05:ToLower2.cpp {-mwcc}
#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>
using namespace std;
 
template<class charT> charT strTolower(charT c) {
return tolower(c); // One-arg version called
}
 
int main() {
string s("LOWER");
transform(s.begin(),s.end(),s.begin(),&strTolower<char>);
cout << s << endl;
} ///:~
 

This version has the advantage that it can process both wide and narrow strings since the underlying character type is a template parameter. The C++ Standards Committee is working on modifying the language so that the first example (without the cast) will work, and some day these workarounds can be ignored.[58]

Thinking in C++ Vol 2 - Practical Programming
Prev Home Next

 
 
   Reproduced courtesy of Bruce Eckel, MindView, Inc. Design by Interspire