0

I am wondering how to convert iterator of characters __gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char> > to string in C++?

#include <iostream>
#include <string>
#include <algorithm>
using std::string;

#define TO_UPPER(s) (transform((s).begin(), (s).end(), (s).begin(), ::toupper))
#define TO_LOWER(s) (transform((s).begin(), (s).end(), (s).begin(), ::tolower))


int main() {
    string str = "Hello world!";

    string result = TO_LOWER(str);

    std::cout << result << std::endl;
    return 0;
}

Error:

[1/2] Building CXX object CMakeFiles/untitled.dir/main.cpp.o
FAILED: CMakeFiles/untitled.dir/main.cpp.o 
/usr/bin/c++   -g -std=gnu++11 -MD -MT CMakeFiles/untitled.dir/main.cpp.o -MF CMakeFiles/untitled.dir/main.cpp.o.d -o CMakeFiles/untitled.dir/main.cpp.o -c /home/amir-pc/CLionProjects/untitled/main.cpp
/home/amir-pc/CLionProjects/untitled/main.cpp: In function ‘int main()’:
/home/amir-pc/CLionProjects/untitled/main.cpp:7:31: error: conversion from ‘__gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char> >’ to non-scalar type ‘std::string’ {aka ‘std::__cxx11::basic_string<char>’} requested
    7 | #define TO_LOWER(s) (transform((s).begin(), (s).end(), (s).begin(), ::tolower))
      |                     ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/amir-pc/CLionProjects/untitled/main.cpp:13:21: note: in expansion of macro ‘TO_LOWER’
   13 |     string result = TO_LOWER(str);
      |                     ^~~~~~~~
ninja: build stopped: subcommand failed.
Node.JS
  • 1,042
  • 6
  • 44
  • 114
  • 1
    You are successfully doing the conversion, however `std::transform` does not return a `std::string`. The transformation is done in-place. – Drew Dormann Mar 17 '22 at 18:28
  • 2
    This should be a function, not a macro. – chris Mar 17 '22 at 18:31
  • The error occurs because `std::transform` returns the "past-the-end-of-inserted-values" iterator. `std::string` doesn't support initialization from a single iterator (let alone an output iterator), and even if it did, it would be trying to initialize from the data *after* the data you inserted. – ShadowRanger Mar 17 '22 at 18:32

2 Answers2

6

std::transform modifies the container in place. You don't need its return value, you can just read the original container.

Also avoid using macros if possible. Prefer functions.

Also note that std::toupper causes undefined behavior if given a negative character code. You need to cast the parameter to unsigned char first to avoid this.

Here's a good implementation from cppreference:

std::string str_toupper(std::string s) {
    std::transform(s.begin(), s.end(), s.begin(), 
                // static_cast<int(*)(int)>(std::toupper)         // wrong
                // [](int c){ return std::toupper(c); }           // wrong
                // [](char c){ return std::toupper(c); }          // wrong
                   [](unsigned char c){ return std::toupper(c); } // correct
                  );
    return s;
}
HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
  • Names beginning with `str` are reserved for the C library (and I believe that restriction applies in C++ as well). – Jerry Coffin Mar 17 '22 at 18:38
  • @JerryCoffin Interesting, can you show where the standard says that? – HolyBlackCat Mar 17 '22 at 18:46
  • @HolyBlackCat: Rereading it, your name is actually all right. It's `str` followed by a lower case letter that's reserved, but followed by an underscore is all right (but it's §7.26.11, in the C standard, in case you still care). – Jerry Coffin Mar 17 '22 at 18:55
0

These macros change the string in place

#define TO_UPPER(s) (transform((s).begin(), (s).end(), (s).begin(), ::toupper))
#define TO_LOWER(s) (transform((s).begin(), (s).end(), (s).begin(), ::tolower))

So just write

TO_LOWER(str);
std::cout << str << std::endl;

Otherwise write

#include <iterator>

//...

string str = "Hello world!";

string result;
result.reserve( str.size() );

std::transform( str.begin(), str.end(), std::back_inserter( result ), ::tolower );

std::cout << result << std::endl;

Pay attention to that it is a bad idea to use such macros. At least you could declare inline functions.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • How to not change the string in place and return updated string? – Node.JS Mar 17 '22 at 18:28
  • 1
    @Node.JS: Simplest approach would be to do `string result = str;`, then `TO_LOWER(result);`. You've already got the tools written, why complicate things? – ShadowRanger Mar 17 '22 at 18:29
  • 2
    In any event, we've already got [a question for this](https://stackoverflow.com/q/313970/364696) (the answers provide both in place and not-in-place solutions). – ShadowRanger Mar 17 '22 at 18:30