tags:

views:

172

answers:

3

Hi guys, I am following the book C++ Cookbook from O'Reilly and I try one of the examples, here is the code:

#include <string>
#include <iostream>
#include <cctype>
#include <cwctype>

using namespace std;

template<typename T, typename F>
void rtrimws(basic_string<T>& s, F f){
    if(s.empty())
        return;

    typename basic_string<T>::iterator p;
    for(p = s.end(); p != s.begin() && f(*--p););

    if(!f(*p))
        p++;

    s.erase(p, s.end());
}

void rtrimws(string& ws){
    rtrimws(ws, isspace);
}

void rtrimws(wstring& ws){
    rtrimws(ws, iswspace);
}

int main(){
    string s = "zing            ";
    wstring ws = L"zonh     ";

    rtrimws(s);
    rtrimws(ws);

    cout << s << "|\n";
    wcout << ws << "|\n";
}

When I try to compile it, I get the following error

trim.cpp: In function ‘void rtrimws(std::string&)’:
trim.cpp:22: error: too many arguments to function ‘void rtrimws(std::string&)’
trim.cpp:23: error: at this point in file

and I don't understand what's wrong. If I don't use the char version (string) but the wchar_t version only, everything runs smooth.

By the way, I am using g++ 4.4.3 in an ubuntu machine 64 bits

A: 

That's because isspace is a template function in c++. It cannot deduce F. If you want to use C variant of isspace you could fully qualify its name as follows:

void rtrimws(string& ws){
    rtrimws(ws, ::isspace); // this will use isspace from global namespace
                            // C++ version belongs to the namespace `std`
}

This is one more good sample why you shouldn't use using namespace std.

Kirill V. Lyadvinsky
Note that when you use `::isspace` you then need to include `ctype.h`. It's unspecified whether global names are declared when using a C++ header like `cctype`.
Johannes Schaub - litb
Agree. Actually no need to pass `isspace` as an argument. OP can use templated C++ version of the `isspace` function right in the `rtrimws` template.
Kirill V. Lyadvinsky
@Kirill good point xD
Johannes Schaub - litb
Except that I would lost this opportunity to learn something new :(
Chubsdad
+1  A: 

isspace is also a template in C++ which accepts a templated character and also a locale with which it uses the facet std::ctype<T> to classify the given character (so it can't make up its mind what version to take, and as such ignores the template).

Try specifying that you mean the C compatibility version: static_cast<int(*)(int)>(isspace). The differences between the compilers could come from the inconsistent handling of deduction from an overloaded function name among the compilers - see this clang PR. See the second case in Faisal's first set of testcases for an analogous case.


Someone pointed out on IRC that this code would call isspace using a char - but isspace takes int and requires the value given to be in the range of unsigned char values or EOF. Now in case that char is signed on your PC and stores a negative non-EOF value, this will yield to undefined behavior.

I recommend to do it like @Kirill says in a comment and just use the templated std::isspace - then you can get rid of the function object argument too.

Johannes Schaub - litb
I tried to look for the definition of isspace and it doesn't look like a template. it is actually defined with the marco __exctype which expands to "extern int isspace(int) throw ()"
Sambatyon
@Sambatyon well you are looking in the wrong file then. In any case, you have accepted a try/guess-answer: "Try xyz maybe it works. But don't wask why!!". These answers that don't give a rationale should be avoided. Because next time you have this problem you try his work around and maybe it won't work and then you can't fix it yourself because you don't know *why* the problem arises in the first place.
Johannes Schaub - litb
+1  A: 

Try rtrimws(ws, ::isspace);.

Also, just as a note, you should be using the reverse iterator.

Fozi
The cctype header isn't guaranteed to declare ::isspace, only std::isspace. The solution given here is fragile.
Roger Pate
So then the only other solution would be not to use `using namespace std;`.
Fozi
@Fozi: Outside of function scope, "using namespace std" should be avoided as a matter of course.
Roger Pate