LINUX.ORG.RU

C++ - хочу прочитать содержимое std::string не копируя его куда-либо через интерфейс operator>>.


0

3

Хочу создать некий итератороподобный объект, который бы являлся istream (реализовывал интерфейс operator>>), из которого бы лилось содержимое этого std::string. Убил объект, создал новый - начал сливать строку сначала. Не хочется копировать строку в std::stringstream... Спасибо.

реализуй через c_str().

DELIRIUM ☆☆☆☆☆
()

Что-нибудь вроде

#include <streambuf>
#include <istream>
#include <string>

class string_istream : public std::istream {

    struct string_buf : public std::streambuf {
        string_buf(std::string &s) { setg(&*s.begin(), &*s.begin(), &*s.end()); }
    };

    string_buf sb;

public:

    string_istream(std::string &s) : std::istream(&sb), sb(s) {}

};

#include <cstdio>
#include <sstream>

int main()
{
    std::string s = "12345";
#ifdef SS
    std::istringstream ss(s);
#else
    string_istream ss(s);
#endif
    while (ss.good()) {
        char c;
        ss >> c;
        printf("%c\n", c);
    }
}

/*
➜  ~  g++ str.cc
➜  ~  valgrind ./a.out
==2533== Memcheck, a memory error detector
==2533== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==2533== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==2533== Command: ./a.out
==2533== 
1
2
3
4
5
5
==2533== 
==2533== HEAP SUMMARY:
==2533==     in use at exit: 0 bytes in 0 blocks
==2533==   total heap usage: 1 allocs, 1 frees, 30 bytes allocated
==2533== 
==2533== All heap blocks were freed -- no leaks are possible
==2533== 
==2533== For counts of detected and suppressed errors, rerun with: -v
==2533== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
➜  ~  g++ -DSS str.cc 
➜  ~  valgrind ./a.out
==2544== Memcheck, a memory error detector
==2544== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==2544== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==2544== Command: ./a.out
==2544== 
1
2
3
4
5
5
==2544== 
==2544== HEAP SUMMARY:
==2544==     in use at exit: 0 bytes in 0 blocks
==2544==   total heap usage: 2 allocs, 2 frees, 60 bytes allocated
==2544== 
==2544== All heap blocks were freed -- no leaks are possible
==2544== 
==2544== For counts of detected and suppressed errors, rerun with: -v
==2544== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
*/

возможно с дописыванием string_istream и string_buf — http://www.mr-edd.co.uk/blog/beginners_guide_streambuf.

quasimoto ★★★★
()
Ответ на: комментарий от PatrickKilpatrick

c_str() и data() возвращают _const_ char*, streambuf хочет просто char*, поэтому &*s.begin() и &*s.end() (то есть iterator -> value reference -> address).

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

21.4.1 basic_string general requirements

The char-like objects in a basic_string object shall be stored contiguously. That is, for any basic_string object s, the identity &*(s.begin() + n) == &*s.begin() + n shall hold for all values of n such that 0 <= n < s.size().

string_buf(std::string &s) { setg(&*s.begin(), &*s.begin(), &*s.begin() + s.size()); }
quasimoto ★★★★
()
Ответ на: комментарий от x0r

Что лучше — получить сразу char* (&*s.begin(), &s[0]) или сначала const char* (s.c_str(), s.data()) и потом char* кастом (const_cast<char*>(s.c_str()), const_cast<char*>(s.data()))?

const_cast это, в общем случае, небезопасное преобразование — если без него можно обойтись, то так и нужно делать.

А вообще тут проблема в том, что const у data и c_str не без причины — при изменении строки методами вроде append оно переаллоцируется по разным адресам:

#include <streambuf>
#include <istream>
#include <string>

class string_istream : public std::istream {

    struct string_buf : public std::streambuf {

        string_buf(std::string &s) {
            char *cs = &s[0];
                    // &*s.begin();
                    // const_cast<char*>(s.data());
            setg(cs, cs, cs + s.size());
        }

    };

    string_buf sb;

public:

    string_istream(std::string &s) : std::istream(&sb), sb(s) {}

};

#include <cstdio>

int main()
{
    std::string s = "0";
    string_istream ss(s);

    for (int i = 0; i < 10; ++i) {
        printf("%p %c\n", &s[0], ss.peek());
        s.append("1");
    }

}

/*
==2376== Memcheck, a memory error detector
==2376== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==2376== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==2376== Command: ./a.out
==2376== 
0x59f9058 0
==2376== Invalid read of size 1
==2376==    at 0x4EB7352: std::istream::peek() (in /usr/lib64/gcc/x86_64-pc-linux-gnu/4.7.3/libstdc++.so.6.0.17)
==2376==    by 0x401782: main (in /ht/a.out)
==2376==  Address 0x59f9058 is 24 bytes inside a block of size 26 free'd
==2376==    at 0x4C2AF3C: operator delete(void*) (vg_replace_malloc.c:480)
==2376==    by 0x4EF4607: std::string::reserve(unsigned long) (in /usr/lib64/gcc/x86_64-pc-linux-gnu/4.7.3/libstdc++.so.6.0.17)
==2376==    by 0x4EF4894: std::string::append(char const*, unsigned long) (in /usr/lib64/gcc/x86_64-pc-linux-gnu/4.7.3/libstdc++.so.6.0.17)
==2376==    by 0x4017BA: main (in /ht/a.out)
==2376== 
0x59f90b8 0
0x59f9118 0
0x59f9118 0
0x59f9178 0
0x59f9178 0
0x59f9178 0
0x59f9178 0
0x59f91e8 0
0x59f91e8 0
==2376== 
==2376== HEAP SUMMARY:
==2376==     in use at exit: 0 bytes in 0 blocks
==2376==   total heap usage: 5 allocs, 5 frees, 156 bytes allocated
==2376== 
==2376== All heap blocks were freed -- no leaks are possible
==2376== 
==2376== For counts of detected and suppressed errors, rerun with: -v
==2376== ERROR SUMMARY: 9 errors from 1 contexts (suppressed: 2 from 2)
*/

так что уже без разницы — что сразу char*, что data и const_cast — всё UB.

То есть в общем случае нужно копирование (std::istringstream тоже не просто так его делает, как и после s.c_str тоже принято делать std::copy), либо исходим из того, что после конструирования string_istream строка не изменяется и добываем её массив с помощью &s[0] (как самый простой вариант — явно проще const_cast<char*>(s.data())).

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

пытаться передать кудато const char* где явно требуется char* это уже плохой ход, тут любая пляка не особо безопасная. интерфейс std::string сделан для дурака и призван избежать таких вещей, как rw доступ к внутренним буферам.

по сабжу, тебе нужно копировать без копирования? std::move не пройдет? может вообще std::shared_ptr и копировать сколько угодно?

x0r ★★★★★
()
Ответ на: комментарий от x0r

по сабжу, тебе нужно копировать без копирования?

Сабж в ОП :) Нужно сделать

std::string s(...);
istringstream ss(s);

где istringstream должен уметь всё что умеет std::istringstream (например, operator>>), то есть тоже наследовать от std::istream, но без копирования, да. std::istringstream копирует строку, поэтому безопасен, выше я привёл решение которое не копирует, то есть конструирует std::streambuf (который нужен единственному protected конструктору std::istream) прямо над внутренним буфером std::string — работает как должно до тех пор пока исходная строка не изменится (и буфер не уедет — объект istringstream не побьётся).

Другого решения я не вижу — только писать свои классы string и string_stream (: std::istream) во friend отношении.

quasimoto ★★★★
()
Ответ на: комментарий от x0r

интерфейс std::string сделан для дурака и призван избежать таких вещей, как rw доступ к внутренним буферам

Значит он не справляется с этой задачей — пока есть итераторы и operator[] дающие non-const reference и, соответсвенно, указатель в буфере с доступом на запись. Причём, стандарт говорит, что этот буфер непрерывен в памяти — пока строка не меняется имеем полное право делать RW прямо на нём.

quasimoto ★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.