I've read multiple posts about how to get the 'tolower' function to
work in gcc. From what I understand, the problem is that there are two
versions - you must cast to specify which version you want to use.
transform(tempString.begin(),tempString.end(),tempString.begin(),
(int(*)(int))std::tolower);
This works with gcc, but not with MS Visual Studio 2003. It only works
with MS Visual Studio 2003 if you remove the std::.
I'm not quite sure why this is, but I read that there are two versions
of tolower. One is the global namespace, and comes from ctype.h, and
the other one is in the std namespace. This link (http://
www.thescripts.com/forum/thread161158.html) states that std::tolower
takes two arguments, not one. This link (http://www.cplusplus.com/
reference/clibrary/cctype/tolower.html) shows that the tolower in
cctype.h takes a single argument. I am also aware that transform must
take a binary argument.
So, if the problem *is* because the std::tolower version takes 2
arguments (and not one), this lead me to ask why it works in gcc? The
only reason I can think of is maybe perhaps the gcc implementation of
std::tolower provides some parameter defaults, which the MS version
does not? This 'option of providing defaults' is explained at:
http://www.gotw.ca/gotw/064.htm:
"The standard library specification deliberately gives some leeway to
implementers when it comes to member functions. Specifically:
- A member function signature with default parameters may be replaced
by "two or more member function signatures with the equivalent
behavior."
- A member function signature may have additional defaulted
parameters.
"
Any comments as per my guesses - are they correct?
Taras
PS: I'm finding that writing portable code when using a STANDARD
library is much harder than I would think it is *sigh*
Forget the cast and specify ::tolower.
> This works with gcc, but not with MS Visual Studio 2003. It only works
> with MS Visual Studio 2003 if you remove the std::.
...
> So, if the problem *is* because the std::tolower version takes 2
> arguments (and not one), this lead me to ask why it works in gcc? The
> only reason I can think of is maybe perhaps the gcc implementation of
> std::tolower provides some parameter defaults, which the MS version
> does not?
Why not test your hypothesis? Try calling std::tolower with one argument.
> PS: I'm finding that writing portable code when using a STANDARD
> library is much harder than I would think it is *sigh*
What are you actually trying to do?
> I've read multiple posts about how to get the 'tolower' function to
> work in gcc. From what I understand, the problem is that there are two
> versions - you must cast to specify which version you want to use.
>
> transform(tempString.begin(),tempString.end(),tempString.begin(),
> (int(*)(int))std::tolower);
Thanks for following my request to change into a g++ group.
Here's my "threatened" information on why what you are attempting to
do probably isn't a good idea:
You are attempting to invoke the 1 argument verison of
std::tolower(). That's the version C++ inherits from C. As most
<cctype> functions, std::tolower() expects its argument to either have
the value -1 or a value in the range representable by unsigned chars,
i.e. 0..UCHAR_MAX; otherwise, the program has undefined behavior.
If your char type is signed, and tempString contains characters with
negative codes, the requirement imposed by std::tolower() will not be
met in the above call to std::transform() (resp. a working version
thereof).
If your program uses an ASCII based character encoding, such as an
ISO-8859 (or "ANSI") encoding, everyday characters such as הציאיס do
have negative codes.
> This works with gcc, but not with MS Visual Studio 2003. It only works
> with MS Visual Studio 2003 if you remove the std::.
"works" and "doesn't work" are very vague. What error messsage do you
get?
> I'm not quite sure why this is, but I read that there are two versions
> of tolower. One is the global namespace, and comes from ctype.h, and
> the other one is in the std namespace.
Unfortunately, the situation is more complicated:
- <cctype> declares the 1 argument version of std::tolower()
- <locale> declares the 2 argument version of std::tolower()
- in C++, <ctype.h> works like <cctype> but adds a using declaration
for std::tolower in the global namespace
And in reality, C++ implemetations often don't implement the 3rd item
correctly. So the best thing IMHO is to not #include <ctype.h> in new
code and rely on the tolower() overloads in namespace std.
> This link (http://www.thescripts.com/forum/thread161158.html) states
> that std::tolower takes two arguments, not one. This link
> (http://www.cplusplus.com/ reference/clibrary/cctype/tolower.html)
> shows that the tolower in cctype.h takes a single argument. I am
> also aware that transform must take a binary argument.
There is no such thing as <cctype.h> in C++. It's either <cctype> or
<ctype.h>. Both declare a 1 argument version of std::tolower().
> So, if the problem *is* because the std::tolower version takes 2
> arguments (and not one), this lead me to ask why it works in gcc?
Please post a minimal, complete program that demonstrates your
problem. Without seeing what headers you #include, your audience can
only make guesses.
I'm not trying to do anything in particular. I was just making a
remark that writing code that compiles across different compilers is a
lot harder than it should be. I've read numerous posts where the
posters have voiced this opinion, and it seems that there is a bit of
leeway in the implementation of the standard (which can't make the job
an easier), as implied by this link:
Your reply clarifying the the 'versions' of the tolower function did
clarify this issue quite a bit.
I tried a number of things with some test code. The test code is:
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
int main()
{
std::string tempString = "BLAH";
transform(tempString.begin(),tempString.end(),tempString.begin(),
(int(*)(int))std::tolower);
std::cout << tempString << std::endl;
}
I will alter the above code by:
1) Including <cctype>, then changing this to include <ctype.h>
2) Switching between std::tolower and tolower
1. As is:
--------------------
g++ compiled fine
MSV2008:tolower.cpp(10) : error C2039: 'tolower' : is not a member of
'std'
Where is 'tolower' being pulled in from for g++? I'm not including
cctype or ctype.h...
2. #include <cctype>, calling std::tolower
--------------------
g++ compiled fine
MSV2008 works fine
2. #include <cctype>, calling tolower
--------------------
g++ compiled fine
MSV2008 works fine
3. #include <ctype.h>, calling std::tolower
--------------------
g++ compiled fine
MSV2008: tolower.cpp(11) : error C2039: 'tolower' : is not a member of
'std'
It seems that including ctype.h is placing to lower into the global
namespace, but also hiding it from the std:: one?
4. #include <ctype.h>, calling tolower
--------------------
g++ compiled fine
MSV2008: works fine
As expected as ctype.h puts tolower into the global namespace
What are the reasons for these differences?
You've actually brought up a couple of other points I have questions
about. Firstly about the difference between <ctype.h> and <cctype>
The post at http://www.thescripts.com/forum/thread60652.html states
that:
"The problem is that most implementations of the standard C <ctype.h>
header define functions like toupper/tolower/etc as macros. To make it
work in STL algorithms, you have to include <cctype> header instead of
<ctype.h>. At least on my PC (Debian/gcc 3.3), <cctype> undefines all
tolower/etc macros and pulls ::tolower/::toupper/etc functions into
std namespace, so that your sample will work fine."
Is this accurate (in that it won't work because the functions are
macros)?
By the way, the function call was:
string foo = "Some Mixed Case Text";
transform(foo.begin(), foo.end(), foo.begin(), tolower);
Taras
Something similar was happening in another file. I was using the time
function, but not including <ctime> or <time.h> anyway. It would
compile on g++, but not on VS2008.
>
> I tried a number of things with some test code. The test code is:
>
> #include <iostream>
> #include <string>
> #include <algorithm>
> #include <vector>
>
> int main()
> {
> std::string tempString = "BLAH";
> transform(tempString.begin(),tempString.end(),tempString.begin(),
> (int(*)(int))std::tolower);
> std::cout << tempString << std::endl;
> }
>
> I will alter the above code by:
> 1) Including <cctype>, then changing this to include <ctype.h>
> 2) Switching between std::tolower and tolower
>
> 1. As is:
> --------------------
> g++ compiled fine
>
> MSV2008:tolower.cpp(10) : error C2039: 'tolower' : is not a member of
> 'std'
>
> Where is 'tolower' being pulled in from for g++? I'm not including
> cctype or ctype.h...
The ISO C++ Standard allows Standard Library headers to freely
#include other Standard Library headers. It seems that one of the
headers your code in turn #includes <cctype>, possibly indirectly. If
I were interested, I'd have a look at <iostram> and <string> first.
The behavior of both compilers is thus correct.
> 2. #include <cctype>, calling std::tolower
> --------------------
> g++ compiled fine
> MSV2008 works fine
Good.
> 2. #include <cctype>, calling tolower
> --------------------
> g++ compiled fine
> MSV2008 works fine
Not good.
> 3. #include <ctype.h>, calling std::tolower
> --------------------
> g++ compiled fine
> MSV2008: tolower.cpp(11) : error C2039: 'tolower' : is not a member of
> 'std'
>
> It seems that including ctype.h is placing to lower into the global
> namespace, but also hiding it from the std:: one?
I wouldn't call it hiding; the function is probably just not declared
in namespace std.
The 1998 Standard states (§ D.5):
Each C header, whose name has the form name.h, behaves as if each name
placed in the Standard library namespace by the corresponding cname
header is also placed withing the namespace scope of the namespace std
and is followed by an explicit using-declaration.
[Example: The header <cstdlib> provides its declarations and
definitions within the namespace std. The header <stdlib.h> makes these
available in the global name space, much as in the C Standard.]
It seems that Microsoft's <ctype.h> only fulfills the second part,
ignoring the part before "also".
That's an instance of what I meant when I wrote:
>> And in reality, C++ implemetations often don't implement the 3rd
>> item correctly.
in my previous post.
> 4. #include <ctype.h>, calling tolower
> --------------------
> g++ compiled fine
> MSV2008: works fine
Good. That's the C compatibility mode.
> What are the reasons for these differences?
See above. Partly the implementation's freedom of choice, and partly
implementation bugs.
Summing things up, you should be fine using option 2. or 4. I'd use
2. in new code; when using old C code, 4. comes to our rescue.