tolower conflict with iostream?

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • David Rubin

    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.beg in(), 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::__no rmal_iterator<c har*, std::basic_stri ng<char,
    std::char_trait s<char>, std::allocator< char> > >,
    __gnu_cxx::__no rmal_iterator<c har*, std::basic_stri ng<char,
    std::char_trait s<char>, std::allocator< char> > >,
    __gnu_cxx::__no rmal_iterator<c har*, std::basic_stri ng<char,
    std::char_trait s<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
  • Howard Hinnant

    #2
    Re: tolower conflict with iostream?

    In article <3F8467DE.92C0F CD4@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.beg in(), 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::__no rmal_iterator<c har*, std::basic_stri ng<char,
    > std::char_trait s<char>, std::allocator< char> > >,
    > __gnu_cxx::__no rmal_iterator<c har*, std::basic_stri ng<char,
    > std::char_trait s<char>, std::allocator< char> > >,
    > __gnu_cxx::__no rmal_iterator<c har*, std::basic_stri ng<char,
    > std::char_trait s<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.beg in(), 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.beg in(), s.end(), s.begin(), (int (*)(int))tolowe r);

    I.e. cast tolower to the specific function pointer type that you're
    aiming for.

    -Howard

    Comment

    • Steven C.

      #3
      Re: tolower conflict with iostream?


      "David Rubin" <bogus_address@ nomail.com> wrote in message
      news:3F8467DE.9 2C0FCD4@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.beg in(), 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::__no rmal_iterator<c har*, std::basic_stri ng<char,
      std::char_trait s<char>, std::allocator< char> > >,
      __gnu_cxx::__no rmal_iterator<c har*, std::basic_stri ng<char,
      std::char_trait s<char>, std::allocator< char> > >,
      __gnu_cxx::__no rmal_iterator<c har*, std::basic_stri ng<char,
      std::char_trait s<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


      Comment

      • Dietmar Kuehl

        #4
        Re: tolower conflict with iostream?

        Howard Hinnant <hinnant@metrow erks.com> wrote:[color=blue]
        > You can correct it, portably, with the following incantation:
        >
        > transform(s.beg in(), s.end(), s.begin(), (int (*)(int))tolowe r);
        >
        > 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(i nt)' 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(st atic_cast<unsig ned 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.co m> <http://www.dietmar-kuehl.de/>
        Phaidros eaSE - Easy Software Engineering: <http://www.phaidros.co m/>

        Comment

        • tom_usenet

          #5
          Re: tolower conflict with iostream?

          On Wed, 08 Oct 2003 22:43:22 GMT, Howard Hinnant
          <hinnant@metrow erks.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.beg in(), 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.


          also has vague relevence.
          [color=blue]
          >You can correct it, portably, with the following incantation:
          >
          > transform(s.beg in(), s.end(), s.begin(), (int (*)(int))tolowe r);
          >
          >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

          Comment

          • J. Campbell

            #6
            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.beg in(), 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)).

            Comment

            • J. Campbell

              #7
              Re: tolower conflict with iostream?

              "Steven C." <nospam@xxx.com > wrote in message news:<KG6hb.376 3$Z86.179@twist er.socal.rr.com >...[color=blue]
              > "David Rubin" <bogus_address@ nomail.com> wrote in message
              > news:3F8467DE.9 2C0FCD4@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.beg in(), 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

              Comment

              • David Rubin

                #8
                Re: tolower conflict with iostream?

                "J. Campbell" wrote:
                [color=blue][color=green]
                > > using namespace std;
                > >
                > > string&
                > > lc(string& s)
                > > {
                > > transform(s.beg in(), 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

                Comment

                • Dietmar Kuehl

                  #9
                  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.co m> <http://www.dietmar-kuehl.de/>
                  Phaidros eaSE - Easy Software Engineering: <http://www.phaidros.co m/>

                  Comment

                  • Shane Beasley

                    #10
                    Re: tolower conflict with iostream?

                    Dietmar Kuehl <dietmar_kuehl@ yahoo.com> wrote in message news:<bm4ti8$ih p3a$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::toupp er>);
                    }

                    As a bonus, this also deals with the overloaded stuff in <locale> --
                    unless you actually wanted to call those. :)

                    - Shane

                    Comment

                    • Dietmar Kuehl

                      #11
                      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::toupp er>);[/color]

                      That's a nice one! :-)
                      --
                      <mailto:dietmar _kuehl@yahoo.co m> <http://www.dietmar-kuehl.de/>
                      Phaidros eaSE - Easy Software Engineering: <http://www.phaidros.co m/>

                      Comment

                      • tom_usenet

                        #12
                        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$ih p3a$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_func tion<char, char>
                        {
                        char operator()(char c) const {
                        return std::char_trait s<char>::to_cha r_type(
                        F(std::char_tra its<char>::to_i nt_type(c))
                        );
                        }
                        };

                        With luck, everything will be inlined since safe_to is a functor, and
                        F is a compile time constant.

                        Tom

                        Comment

                        • David Rubin

                          #13
                          Re: tolower conflict with iostream?

                          tom_usenet wrote:
                          [color=blue]
                          > #include <string>
                          > #include <functional>
                          >
                          > template <int (*F) (int)>
                          > struct safe_to: std::unary_func tion<char, char>
                          > {
                          > char operator()(char c) const {
                          > return std::char_trait s<char>::to_cha r_type(
                          > F(std::char_tra its<char>::to_i nt_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

                          Comment

                          • tom_usenet

                            #14
                            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_func tion<char, char>
                            >> {
                            >> char operator()(char c) const {
                            >> return std::char_trait s<char>::to_cha r_type(
                            >> F(std::char_tra its<char>::to_i nt_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<wch ar_t>::to_char_ type(mywint);

                            Tom

                            Comment

                            Working...