2

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

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
Sambatyon
  • 3,316
  • 10
  • 48
  • 65
  • 1
    By the way it compiles fine on VC++ – Brian R. Bondy Sep 09 '10 at 19:20
  • Before closing this thread, and for the sake of learning a little bit more, there are things I would like to discuss. The code certainly compiles in VC++, I tried without any problems, but it doesn't in GCC. The function isspace is declare in cctypes.h as __exctype (isspace); which is really extern int isspace(int) throw () – Sambatyon Sep 11 '10 at 08:16

3 Answers3

1

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
  • 496,577
  • 130
  • 894
  • 1,212
  • 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 Sep 12 '10 at 14:55
  • @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 Sep 12 '10 at 19:56
1

Try rtrimws(ws, ::isspace);.

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

Fozi
  • 4,973
  • 1
  • 32
  • 56
  • 1
    The cctype header isn't guaranteed to declare ::isspace, only std::isspace. The solution given here is fragile. –  Sep 12 '10 at 21:45
  • So then the only other solution would be not to use `using namespace std;`. – Fozi Sep 13 '10 at 14:13
  • Outside of function scope, "using namespace std" should be avoided as a matter of course. –  Oct 24 '10 at 11:11
0

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
  • 97,037
  • 24
  • 136
  • 212