Подтвердить что ты не робот

Поток объекта непосредственно в std::string

Для некоторого типа, который доступен для потока:

struct X {
    int i;

    friend std::ostream& operator<<(std::ostream& os, X const& x) {
        return os << "X(" << x.i << ')';
    }
};

Я хочу добавить это на std::string. Я могу реализовать это как:

void append(std::string& s, X const& x) {
    std::ostringstream os;
    os << x;
    s.append(os.str());
}

Но это кажется хромым, так как я пишу данные в один поток, чтобы затем выделить новую строку только для того, чтобы добавить ее в другую. Есть ли более прямой маршрут?

4b9b3361

Ответ 1

Это можно решить с помощью нового типа streambuf (см. Стандартные IOStreams и локали С++: Расширенное руководство для программистов и Ссылка).

Вот эскиз того, как он может выглядеть:

#include <streambuf>

class existing_string_buf : public std::streambuf
{
public:
    // Store a pointer to to_append.
    explicit existing_string_buf(std::string &to_append); 

    virtual int_type overflow (int_type c) {
        // Push here to the string to_append.
    }
};

Как только вы подробно изложите здесь информацию, вы можете использовать его следующим образом:

#include <iostream>

std::string s;
// Create a streambuf of the string s
existing_string_buf b(s);
// Create an ostream with the streambuf
std::ostream o(&b);

Теперь вы просто пишете на o, и результат должен появиться как добавленный к s.

// This will append to s
o << 22;

Изменить

Как правильно отмечает @rustyx, для повышения производительности требуется переопределение xsputn.

Полный пример

Следующие отпечатки 22:

#include <streambuf>
#include <string>
#include <ostream> 
#include <iostream>

class existing_string_buf : public std::streambuf
{
public:
    // Somehow store a pointer to to_append.
    explicit existing_string_buf(std::string &to_append) : 
        m_to_append(&to_append){}

    virtual int_type overflow (int_type c) {
        if (c != EOF) {
            m_to_append->push_back(c);
        }
        return c;
    }

    virtual std::streamsize xsputn (const char* s, std::streamsize n) {
        m_to_append->insert(m_to_append->end(), s, s + n);                                                                                 
        return n;
    }

private:
    std::string *m_to_append;
};


int main()
{   
    std::string s;
    existing_string_buf b(s);
    std::ostream o(&b);

    o << 22; 

    std::cout << s << std::endl;
}   

Ответ 2

Вы можете написать оператор кастинга std::string:

struct X {
int i;

friend std::ostream& operator<<(std::ostream& os, X const& x) {
    os << "X(" << x.i << ')';
    return os;
}

operator std::string() {
    return std::string("X(") + std::to_string(x.i) + ")";
}

};

Затем вы можете просто добавить его к std::string:

X myX;
myX.i = 2;
std::string s("The value is ");
s.append(myX); //myX is cast into the string "X(2)"

Ответ 3

В этом конкретном случае я просто следую принципу KISS:

struct X {
    int i;

    std::string toString() const {
        return "X(" + std::to_string(i) + ")";
    }
};

Использование:

string += x.toString();
std::cout << x.toString();

An operator<<(std::ostream&, …) не подходит для общего преобразования строк, поэтому, если это то, что вы после этого, тип метода/бесплатной функции toString намного лучше. И если вы хотите std::cout << x, вы можете тривиально реализовать operator<<, чтобы просто вызвать toString.

Ответ 4

Так как исходная строка, вероятно, достаточно велика для существующего выделения, лучше всего вы можете надеяться, это отформатировать все, что вы хотите добавить один раз в потоке, а затем добавить результат, как у вас в вашем примере.

Если вы планируете часто выполнять эти добавления, я бы сказал, что std::string - неправильный тип проблемы. Я бы рекомендовал использовать std:: ostringtream непосредственно вместо std::string и только преобразовать в строку, когда вам нужен конечный результат.