tolower conflict with iostream? | | |
I get an error when I compile the following code:
#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>
using namespace std;
string&
lc(string& s)
{
transform(s.begin(), s.end(), s.begin(), tolower);
return s;
}
int
main()
{
string name = "DAVID";
cout << name << " " << lc(name) << endl;
return 0;
}
; g++ lc.cc
lc.cc: In function `std::string& lc(std::string&)':
lc.cc:11: error: no matching function for call to `transform(
__gnu_cxx::__normal_iterator<char*, std::basic_string<char,
std::char_traits<char>, std::allocator<char> > >,
__gnu_cxx::__normal_iterator<char*, std::basic_string<char,
std::char_traits<char>, std::allocator<char> > >,
__gnu_cxx::__normal_iterator<char*, std::basic_string<char,
std::char_traits<char>, std::allocator<char> > >, <unknown type>)'
If I do not include iostream, or if I use a different function
(e.g., int id(int i){return i;}), I do not get any errors. Am I doing
something wrong?
/david
--
Andre, a simple peasant, had only one thing on his mind as he crept
along the East wall: 'Andre, creep... Andre, creep... Andre, creep.'
-- unknown | | | | re: tolower conflict with iostream?
In article <3F8467DE.92C0FCD4@nomail.com>,
David Rubin <bogus_address@nomail.com> wrote:
[color=blue]
> I get an error when I compile the following code:
>
> #include <algorithm>
> #include <cctype>
> #include <iostream>
> #include <string>
>
> using namespace std;
>
> string&
> lc(string& s)
> {
> transform(s.begin(), s.end(), s.begin(), tolower);
> return s;
> }
>
> int
> main()
> {
> string name = "DAVID";
>
> cout << name << " " << lc(name) << endl;
> return 0;
> }
>
> ; g++ lc.cc
> lc.cc: In function `std::string& lc(std::string&)':
> lc.cc:11: error: no matching function for call to `transform(
> __gnu_cxx::__normal_iterator<char*, std::basic_string<char,
> std::char_traits<char>, std::allocator<char> > >,
> __gnu_cxx::__normal_iterator<char*, std::basic_string<char,
> std::char_traits<char>, std::allocator<char> > >,
> __gnu_cxx::__normal_iterator<char*, std::basic_string<char,
> std::char_traits<char>, std::allocator<char> > >, <unknown type>)'
>
> If I do not include iostream, or if I use a different function
> (e.g., int id(int i){return i;}), I do not get any errors. Am I doing
> something wrong?[/color]
Yes, but you're in good company. This one gets almost everyone at least
once.
There are two kickers (if not more) in this one:
1. tolower is (also) a template function prototyped in <locale>:
template <class charT> charT tolower(charT c, const locale& loc);
2. Any standard C++ header is allowed to include any other standard C++
header as an implementation detail (reference 17.4.4.1/1).
So when you say:
transform(s.begin(), s.end(), s.begin(), tolower);
and if the templated tolower is in scope, then the compiler can't figure
out what template parameters to try out for tolower<charT>, even if the
non-templated tolower is also in scope. And apparently gcc's <iostream>
brings tolower<charT> into scope as an implementation detail. Thus the
error.
This is a sneaky one in that it may well compile and do what you want
with another compiler. For example it works just fine on Metrowerks,
unless <locale> is explicitly included, and then you get a similar error.
You can correct it, portably, with the following incantation:
transform(s.begin(), s.end(), s.begin(), (int (*)(int))tolower);
I.e. cast tolower to the specific function pointer type that you're
aiming for.
-Howard | | | | re: tolower conflict with iostream?
"David Rubin" <bogus_address@nomail.com> wrote in message
news:3F8467DE.92C0FCD4@nomail.com...
I get an error when I compile the following code:
#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>
using namespace std;
string&
lc(string& s)
{
transform(s.begin(), s.end(), s.begin(), tolower);
return s;
}
int
main()
{
string name = "DAVID";
cout << name << " " << lc(name) << endl;
return 0;
}
; g++ lc.cc
lc.cc: In function `std::string& lc(std::string&)':
lc.cc:11: error: no matching function for call to `transform(
__gnu_cxx::__normal_iterator<char*, std::basic_string<char,
std::char_traits<char>, std::allocator<char> > >,
__gnu_cxx::__normal_iterator<char*, std::basic_string<char,
std::char_traits<char>, std::allocator<char> > >,
__gnu_cxx::__normal_iterator<char*, std::basic_string<char,
std::char_traits<char>, std::allocator<char> > >, <unknown type>)'
If I do not include iostream, or if I use a different function
(e.g., int id(int i){return i;}), I do not get any errors. Am I doing
something wrong?
/david
--
Andre, a simple peasant, had only one thing on his mind as he crept
along the East wall: 'Andre, creep... Andre, creep... Andre, creep.'
-- unknown
__________________________________________________ ___________
the way I fixed it is by doing
::tolower | | | | re: tolower conflict with iostream?
Howard Hinnant <hinnant@metrowerks.com> wrote:[color=blue]
> You can correct it, portably, with the following incantation:
>
> transform(s.begin(), s.end(), s.begin(), (int (*)(int))tolower);
>
> I.e. cast tolower to the specific function pointer type that you're
> aiming for.[/color]
See, even standard library implementers get it wrong :-) This one is,
however, even more subtle: 'std::tolower(int)' can only be called with
values which can be represented by an 'unsigned char' and EOF. However,
on systems where 'char' is a signed entity lots of possible 'char' values,
promoted to 'int', cannot be represented as 'unsigned char'. Although the
above code would be portably compilable, it won't run portably. For a
truely portable solution, you would need an auxiliary function:
char mytolower(char c) {
return std::tolower(static_cast<unsigned char>(c));
}
This will convert the negative 'char' to some positive value first which
stays the same value when converted to 'int'. Promoting a negative 'char'
directly to 'int' and then converting this value to an unsigned type
would yield a value which is [normally (*)] bigger than any value which
can be hold by an 'unsigned char'.
(*): if the number of bits used for 'int' and 'unsigned char' is identical,
it would work...
--
<mailto:dietmar_kuehl@yahoo.com> <http://www.dietmar-kuehl.de/>
Phaidros eaSE - Easy Software Engineering: <http://www.phaidros.com/> | | | | re: tolower conflict with iostream?
On Wed, 08 Oct 2003 22:43:22 GMT, Howard Hinnant
<hinnant@metrowerks.com> wrote:
[color=blue]
>Yes, but you're in good company. This one gets almost everyone at least
>once.
>
>There are two kickers (if not more) in this one:
>
>1. tolower is (also) a template function prototyped in <locale>:
>
>template <class charT> charT tolower(charT c, const locale& loc);
>
>2. Any standard C++ header is allowed to include any other standard C++
>header as an implementation detail (reference 17.4.4.1/1).
>
>So when you say:
>
> transform(s.begin(), s.end(), s.begin(), tolower);
>
>and if the templated tolower is in scope, then the compiler can't figure
>out what template parameters to try out for tolower<charT>, even if the
>non-templated tolower is also in scope.[/color]
It's not that clear in the standard that the non-template tolower
shouldn't be chosen. For the templated tolower, argument deduction
would fail, so only the non-template version would be left. In theory
this could be chosen unambiguously.
And apparently gcc's <iostream>[color=blue]
>brings tolower<charT> into scope as an implementation detail. Thus the
>error.
>
>This is a sneaky one in that it may well compile and do what you want
>with another compiler. For example it works just fine on Metrowerks,
>unless <locale> is explicitly included, and then you get a similar error.[/color]
Comeau compiles it fine with both <locale> and <cctype> included.
13.4/2 suggests that it might be ok, but it certainly isn't clear
either way. http://std.dkuug.dk/jtc1/sc22/wg21/d...ctive.html#115
also has vague relevence.
[color=blue]
>You can correct it, portably, with the following incantation:
>
> transform(s.begin(), s.end(), s.begin(), (int (*)(int))tolower);
>
>I.e. cast tolower to the specific function pointer type that you're
>aiming for.[/color]
However, this still has the problem with the domain to tolower being
EOF + the range of unsigned char.
Tom | | | | re: tolower conflict with iostream?
David Rubin <bogus_address@nomail.com> wrote in message news:<3F8467DE.92C0FCD4@nomail.com>...[color=blue]
> I get an error when I compile the following code:
>
> #include <algorithm>
> #include <cctype>
> #include <iostream>
> #include <string>
>
> using namespace std;
>
> string&
> lc(string& s)
> {
> transform(s.begin(), s.end(), s.begin(), tolower);
> return s;
> }
>
> int
> main()
> {
> string name = "DAVID";
>
> cout << name << " " << lc(name) << endl;
> return 0;
> }[/color]
[color=blue]
> /david[/color]
david, I don't know that much about c++, but you can do what you want
by flipping the bit in the 32's place. If you know that your string
contains letters, then you can use these 3 functions:
#include <iostream>
#include <string>
using namespace std;
string lcase(string stringin){
string stringout;
for(int i = 0; i < stringin.size(); ++i)
stringout += (stringin[i] | 32);
return stringout;
}
string ucase(string stringin){
string stringout;
for(int i = 0; i < stringin.size(); ++i)
stringout += (stringin[i] & (223));
return stringout;
}
string flipcase(string stringin){
string stringout;
for(int i = 0; i < stringin.size(); ++i)
stringout += (stringin[i] ^ 32);
return stringout;
}
int main(){
cout << "Enter text";
string test;
getline(cin, test);
cout << "Original " << test << endl
<< "All Caps " << ucase(test)<< endl
<< "Lower Case " << lcase(test) << endl
<< "Flip Case " << flipcase(test) << endl;
}
Of course, you will prob. want to clean up the function masks so that
they only effect characters. (eg only effect chars (65 - 90) and (97 -
122)). | | | | re: tolower conflict with iostream?
"Steven C." <nospam@xxx.com> wrote in message news:<KG6hb.3763$Z86.179@twister.socal.rr.com>...[color=blue]
> "David Rubin" <bogus_address@nomail.com> wrote in message
> news:3F8467DE.92C0FCD4@nomail.com...
> I get an error when I compile the following code:
>
> #include <algorithm>
> #include <cctype>
> #include <iostream>
> #include <string>
>
> using namespace std;
>
> string&
> lc(string& s)
> {
> transform(s.begin(), s.end(), s.begin(), tolower);
> return s;
> }
>
> int
> main()
> {
> string name = "DAVID";
>
> cout << name << " " << lc(name) << endl;
> return 0;
> }[/color]
I'm sure your aren't interested, but...I improved the functions I just
wrote so that they now ignore all non alphebetic characters. You can
easily change the function to pass a string reference if you want the
change to affect the original string. Anyway...these functions should
work with any string object.
#include <iostream>
#include <string>
using namespace std;
string lcase(string in){
string stringout;
for(int i = 0; i < in.size(); ++i)
if(!(in[i] & 128) & ((in[i] & 95) > 64) & ((in[i] & 31) <= 26))
stringout += (in[i] | 32);
else stringout += in[i]; //character wasn't a letter...dont
change
return stringout;
}
string ucase(string in){
string stringout;
for(int i = 0; i < in.size(); ++i)
if(!(in[i] & 128) & ((in[i] & 95) > 64) & ((in[i] & 31) <= 26))
stringout += (in[i] & (223));
else stringout += in[i]; //character wasn't a letter...dont
change
return stringout;
}
string flipcase(string in){
string stringout;
for(int i = 0; i < in.size(); ++i)
if(!(in[i] & 128) & ((in[i] & 95) > 64) & ((in[i] & 31) <= 26))
stringout += (in[i] ^ 32);
else stringout += in[i]; //character wasn't a letter...dont
change
return stringout;
}
int main(){
cout << "Enter text\n";
string test;
getline(cin, test);
cout << "Original " << test << endl
<< "All Caps " << ucase(test)<< endl
<< "Lower Case " << lcase(test) << endl
<< "Flip Case " << flipcase(test) << endl;
}
in case you are wondering...
if(!(in[i] & 128) & ((in[i] & 95) > 64) & ((in[i] & 31) <= 26))
------------- ------------------ -------------------
0XXX XXXX X10X XXXX 000X XXXX <= 26
000X XXXX > 0 | | | | re: tolower conflict with iostream?
"J. Campbell" wrote:
[color=blue][color=green]
> > using namespace std;
> >
> > string&
> > lc(string& s)
> > {
> > transform(s.begin(), s.end(), s.begin(), tolower);
> > return s;
> > }[/color][/color]
[snip][color=blue]
> david, I don't know that much about c++, but you can do what you want
> by flipping the bit in the 32's place. If you know that your string
> contains letters, then you can use these 3 functions:[/color]
[snip - code]
Thanks for the code. However, you are assuming an ASCII character set
and a particular locale. The ::tolower solution seems to work well. I
suppose you can extend this to implement Flipcase as a functor (or
simply as a function) using
s[i] = (::isupper(s[i]) ? ::tolower(s[i]) : ::toupper(s[i]));
BTW, for those in the know who are still reading this thread, is* and
to* are known to take an int argument to account for EOF. However, is it
safe to assume that an explicit cast is unnecessary when applying these
functions to a string? Presumably, a string will not contain EOF.
/david
--
Andre, a simple peasant, had only one thing on his mind as he crept
along the East wall: 'Andre, creep... Andre, creep... Andre, creep.'
-- unknown | | | | re: tolower conflict with iostream?
David Rubin wrote:[color=blue]
> s[i] = (::isupper(s[i]) ? ::tolower(s[i]) : ::toupper(s[i]));
>
> BTW, for those in the know who are still reading this thread, is* and
> to* are known to take an int argument to account for EOF. However, is it
> safe to assume that an explicit cast is unnecessary when applying these
> functions to a string? Presumably, a string will not contain EOF.[/color]
Although the 'is*()' and 'to*()' account for EOF, they do not account
for negative values of 'char' on platforms where 'char' is a signed
entity (at least, they are not required to; I would expect that library
implementers actually do account for this case). Thus, you have to cast
a 'char' to 'unsigned char' before passing them into these functions.
--
<mailto:dietmar_kuehl@yahoo.com> <http://www.dietmar-kuehl.de/>
Phaidros eaSE - Easy Software Engineering: <http://www.phaidros.com/> | | | | re: tolower conflict with iostream?
Dietmar Kuehl <dietmar_kuehl@yahoo.com> wrote in message news:<bm4ti8$ihp3a$1@ID-86292.news.uni-berlin.de>...
[color=blue]
> Although the 'is*()' and 'to*()' account for EOF, they do not account
> for negative values of 'char' on platforms where 'char' is a signed
> entity (at least, they are not required to; I would expect that library
> implementers actually do account for this case). Thus, you have to cast
> a 'char' to 'unsigned char' before passing them into these functions.[/color]
#include <algorithm>
#include <string>
#include <cctype>
template <int (*F) (int)>
int safe (unsigned char c) { return F(c); }
int main () {
std::string s;
std::transform(s.begin(), s.end(), s.begin(), safe<std::toupper>);
}
As a bonus, this also deals with the overloaded stuff in <locale> --
unless you actually wanted to call those. :)
- Shane | | | | re: tolower conflict with iostream? sbeasley@cs.uic.edu (Shane Beasley) wrote:[color=blue]
> template <int (*F) (int)>
> int safe (unsigned char c) { return F(c); }[/color]
[...][color=blue]
> std::transform(s.begin(), s.end(), s.begin(), safe<std::toupper>);[/color]
That's a nice one! :-)
--
<mailto:dietmar_kuehl@yahoo.com> <http://www.dietmar-kuehl.de/>
Phaidros eaSE - Easy Software Engineering: <http://www.phaidros.com/> | | | | re: tolower conflict with iostream?
On 10 Oct 2003 04:29:29 -0700, sbeasley@cs.uic.edu (Shane Beasley)
wrote:
[color=blue]
>Dietmar Kuehl <dietmar_kuehl@yahoo.com> wrote in message news:<bm4ti8$ihp3a$1@ID-86292.news.uni-berlin.de>...
>[color=green]
>> Although the 'is*()' and 'to*()' account for EOF, they do not account
>> for negative values of 'char' on platforms where 'char' is a signed
>> entity (at least, they are not required to; I would expect that library
>> implementers actually do account for this case). Thus, you have to cast
>> a 'char' to 'unsigned char' before passing them into these functions.[/color]
>
> #include <algorithm>
> #include <string>
> #include <cctype>
>
> template <int (*F) (int)>
> int safe (unsigned char c) { return F(c); }[/color]
Nice idea, but how about :
#include <string>
#include <functional>
template <int (*F) (int)>
struct safe_to: std::unary_function<char, char>
{
char operator()(char c) const {
return std::char_traits<char>::to_char_type(
F(std::char_traits<char>::to_int_type(c))
);
}
};
With luck, everything will be inlined since safe_to is a functor, and
F is a compile time constant.
Tom | | | | re: tolower conflict with iostream?
tom_usenet wrote:
[color=blue]
> #include <string>
> #include <functional>
>
> template <int (*F) (int)>
> struct safe_to: std::unary_function<char, char>
> {
> char operator()(char c) const {
> return std::char_traits<char>::to_char_type(
> F(std::char_traits<char>::to_int_type(c))[/color]
I'm not sure what to_int_type does, but you don't want to promote a
negative char value to a negative int value. You want to promote a
negative char value to a non-negative (e.g., unsigned char) value, and
*then* to an int value (implicit in the call to std::tolower).
[color=blue]
> );
> }
> };[/color]
/david
--
Andre, a simple peasant, had only one thing on his mind as he crept
along the East wall: 'Andre, creep... Andre, creep... Andre, creep.'
-- unknown | | | | re: tolower conflict with iostream?
On Mon, 13 Oct 2003 14:14:53 -0400, David Rubin
<bogus_address@nomail.com> wrote:
[color=blue]
>tom_usenet wrote:
>[color=green]
>> #include <string>
>> #include <functional>
>>
>> template <int (*F) (int)>
>> struct safe_to: std::unary_function<char, char>
>> {
>> char operator()(char c) const {
>> return std::char_traits<char>::to_char_type(
>> F(std::char_traits<char>::to_int_type(c))[/color]
>
>I'm not sure what to_int_type does, but you don't want to promote a
>negative char value to a negative int value. You want to promote a
>negative char value to a non-negative (e.g., unsigned char) value, and
>*then* to an int value (implicit in the call to std::tolower).[/color]
to_int_type and to_char_type are for correctly converting between char
and int in the streams sense (e.g. map char to unsigned char values).
It saves on ugly casts (although looks pretty ugly itself). It also
readily converts between wchar_t and wint_t -
char_traits<wchar_t>::to_char_type(mywint);
Tom |  | | | | /bytes/about
We are a network of experts and professionals in IT and software development that help one another with answers to tough questions and share insights.
Get the best answers to your questions from over 226,510 network members.
|