Макрос формата C++ / встраивает ostringstream

14
задан Jason Plank 1 November 2011 в 17:38
поделиться

7 ответов

Вы уже все в значительной степени закрепили это. Но это - немного оспаривания для следования. Таким образом позвольте мне попробовать суммировать то, что Вы сказали...

<час>

, Что трудности здесь состоят в том что:

  • Мы играем с временным файлом ostringstream объект, так взятие обращается, служится противопоказанием.

  • , поскольку это - временный файл, мы не можем тривиально преобразовать в ostream объект через кастинг.

  • И конструктор [очевидно], и str() является методами класса ostringstream. (Да, мы должны использовать .str(). Используя эти ostringstream объект непосредственно завершил бы вызов ios::operator void*(), возвратив подобное указателю хорошее/плохое значение и не строковый объект.)

  • operator<<(...) существует и как наследованный ostream методы и как глобальные функции. Во всех случаях это возвращается ostream& ссылка.

  • выбором здесь для [1 111] является унаследованный метод ostream::operator<<(void* ) и глобальная функция operator<<(ostream&,const char* ). Наследованный ostream::operator<<(void* ) побеждает, потому что мы не можем преобразовать в ostream ссылка на объект для вызова глобальной функции. [Благодарность [1 149] coppro!]

<час>

Так, для осуществления этого мы должны:

  • Выделяют временный файл ostringstream.
  • Преобразовывают его в ostream.
  • Добавляют данные.
  • Преобразовывают его назад в ostringstream.
  • И вызывают str().
<час>

Выделение: ostringstream().

Преобразование: существует несколько вариантов. Другие предложили:

  • ostringstream() << std::string() // Kudos to *David Norman*
  • ostringstream() << std::dec // Kudos to *cadabra*

Или мы могли использовать:

Мы не можем использовать:

  • <забастовка> operator<<( ostringstream(), "" )
  • <забастовка> (ostream &) ostringstream()

Добавление: Простой теперь.

Преобразование назад: Мы могли просто использовать (ostringstream&). Но dynamic_cast было бы более безопасным. В маловероятном случае dynamic_cast возвратился NULL (это не было должно), следующий .str() инициирует coredump.

Вызов str(): Предположение.

<час>

Соединение всего этого.

#define FORMAT(ITEMS)                                             \
  ( ( dynamic_cast<ostringstream &> (                             \
         ostringstream() . seekp( 0, ios_base::cur ) << ITEMS )   \
    ) . str() )
<час>

Ссылки:

.

19
ответ дан 1 December 2019 в 06:24
поделиться

Вот то, что я использую. Все это вписывается в одно опрятное определение класса в заголовочном файле.

обновление: основное улучшение кода благодаря litb.

// makestring.h:

class MakeString
{
    public:
        std::stringstream stream;
        operator std::string() const { return stream.str(); }

        template<class T>
        MakeString& operator<<(T const& VAR) { stream << VAR; return *this; }
};

Вот то, как это используется:

string myString = MakeString() << a << "b" << c << d;
21
ответ дан 1 December 2019 в 06:24
поделиться

Вот ответ как cadabra's, который не смешивает с состоянием ostream:

#define FORMAT(items)     static_cast<std::ostringstream &>((std::ostringstream() << std::string() << items)).str()

я полагаю, что первый абзац ответа coppro описывает, почему вещи ведут себя этот путь.

2
ответ дан 1 December 2019 в 06:24
поделиться

Когда я взял решение mrree (тот, отмеченный "предпочтенный", тот, красиво объясненный и тот, работающий отлично на G ++), я столкнулся с проблемами с MSVC ++: Все строки, созданные с этим макросом, закончились пустые.

Часы (и большое царапание моей головы и выяснение "перезагрузил" вопрос здесь) позже, я узнал, что seekp () вызов был преступником. Я не уверен, что MSVC ++ делает по-другому с этим, но заменой

ostringstream().seekp( 0, ios_base::cur )

с cadabra's

ostringstream() << std::dec

работы для MSVC ++, также.

1
ответ дан 1 December 2019 в 06:24
поделиться

Проблема, которую Вы имеете, связана с тем, которое operator << (ostream&, char*) не член ostream, и Ваш временный ostream экземпляр не может связать с не - const ссылка. Вместо этого это выбирает void* перегрузка, которая является членом ostream и таким образом не имеет того ограничения.

лучшее (но не самый легкий или самый изящный, любым фрагментом воображения!) должен был бы использовать Препроцессор Повышения для генерации большого количества функциональных перегрузок, каждый шаблонный на большом количестве объектов (включает, были опущены и принятие using namespace std;):

#define MAKE_OUTPUT(z, n, data) \
    BOOST_PP_TUPLE_ELEM(2, 0, data) << BOOST_PP_CAT(BOOST_PP_TUPLE_ELEM(2, 1, data), n);

#define MAKE_FORMAT(z, n, data) \
    template <BOOST_PP_ENUM_PARAMS_Z(z, BOOST_PP_INC(n), typename T)> \
    inline string format(BOOST_PP_ENUM_BINARY_PARAMS_Z(z, BOOST_PP_INC(n), T, p)) \
    { \
      ostringstream s; \
      BOOST_PP_REPEAT_##z(z, n, MAKE_OUTPUT, (s, p)); \
      return s.str(); \
    }

Это, как гарантируют, не будет работать точно (записал это, не тестируя), но это - в основном идея. Вы затем звоните BOOST_PP_REPEAT(N, MAKE_FORMAT, ()) для создания ряда взятия функций до параметров N, которые отформатируют строку, как Вы хотите к (замените N предпочтительным целым числом. Более высокие значения могут негативно влиять на время компиляции). Это должно быть достаточным, пока Вы не получаете компилятор с шаблонами variadic. Необходимо прочитать документацию препроцессора повышения, она имеет очень мощные функции для вещей как это. (Вы можете впоследствии #undef макросы после вызова BOOST_PP_REPEAT вызов для генерации функций)

4
ответ дан 1 December 2019 в 06:24
поделиться

Вот рабочее решение:

#define FORMAT(items)                                                   \
   ((std::ostringstream&)(std::ostringstream() << std::dec << items)).str()

я не вполне понимаю поведения первого аргумента.

1
ответ дан 1 December 2019 в 06:24
поделиться

Почему не только используют функцию вместо макроса?

0
ответ дан 1 December 2019 в 06:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: