Макрос формата C ++ / встроенный ostringstream

Я пытаюсь написать макрос, который позволил бы мне сделать что-то вроде: FORMAT(a << "b" << c << d) , и результатом будет строка – то же самое, что и создание ostringstream, вставка a...d и возвращает .str() . Что-то вроде:

 string f(){ ostringstream o; o << a << "b" << c << d; return o.str() } 

По существу, FORMAT(a << "b" << c << d) == f() .

Во-первых, я попробовал:

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

Если первый элемент является строкой C ( const char * ), он будет печатать адрес строки в шестнадцатеричном виде, а следующие элементы будут печататься точно. Если самый первый элемент является std::string , он не сможет скомпилировать (без соответствующего оператора << ).

Эта:

 2: #define FORMAT(items) \ ((std::ostringstream&)(std::ostringstream() << 0 << '\b' << items)).str() 

дает то, что кажется правильным выходом, но 0 и \b присутствуют в строке, конечно.

Следующее, похоже, работает, но компилируется с предупреждениями (с указанием временного):

 3: #define FORMAT(items) \ ((std::ostringstream&)(*((std::ostream*)(&std::ostringstream())) << items)).str() 

Кто-нибудь знает, почему 1 печатает адрес c-строки и не скомпилируется с помощью std::string ? Не являются ли 1 и 3 по существу одинаковыми?

Я подозреваю, что вариативные шаблоны C ++ 0x позволят сделать format(a, "b", c, d) возможным. Но есть ли способ решить это сейчас?

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


Эти трудности заключаются в следующем:

  • Мы играем с временным объектом ostringstream , так что адреса ostringstream без указания.

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

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

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

  • Выбор здесь для ostringstream()<<"foo" - это унаследованный метод ostream::operator<<(void* ) и глобальный operator<<(ostream&,const char* ) функции operator<<(ostream&,const char* ) . Унаследованный ostream::operator<<(void* ) выигрывает, потому что мы не можем преобразовать ostream объект-объект для вызова глобальной функции. [Престижность к coppro !]


Итак, чтобы снять это, нам необходимо:

  • ostringstream временный ostringstream .
  • Преобразуйте его в ostream .
  • Добавить данные.
  • Преобразуйте его обратно в ostringstream .
  • И вызывается str() .

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

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

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

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

  • ostringstream() . seekp( 0, ios_base::cur )
  • ostringstream() . write( "", 0 )
  • ostringstream() . flush()
  • ostringstream() << flush
  • ostringstream() << nounitbuf
  • ostringstream() << unitbuf
  • ostringstream() << noshowpos
  • Или любой другой стандартный манипулятор. [ #include ] Ссылка: см. «Вставьте данные с форматом» 1/3 пути вниз на этой веб-странице.

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

  • operator<<( ostringstream(), "" )
  • (ostream &) ostringstream()

Добавление: прямо сейчас.

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

Вызов str() : Угадайте.


Все вместе.

 #define FORMAT(ITEMS) \ ( ( dynamic_cast ( \ ostringstream() . seekp( 0, ios_base::cur ) << ITEMS ) \ ) . str() ) 

Рекомендации:

  • Библиотека IOstream
  • ostringstream
  • ostream::operator<<()
  • Учебное пособие по литью
  • Wiki: Тип Литье

,

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

обновление: значительное улучшение кода благодаря подсветке .

 // makestring.h: class MakeString { public: std::stringstream stream; operator std::string() const { return stream.str(); } template MakeString& operator<<(T const& VAR) { stream << VAR; return *this; } }; 

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

 string myString = MakeString() << a << "b" << c << d; 

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

Лучшим (но не самым простым или самым изящным, каким-либо образом воображения!) Было бы использование препроцессора Boost для генерации большого числа перегрузок функций, каждый из которых был бы настроен на большое количество объектов (включая их опущены и предполагается 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  \ 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 целым выбором). Более высокие значения могут отрицательно повлиять на время компиляции). Этого достаточно, пока вы не получите компилятор с вариативными шаблонами. Вы должны прочитать документацию по препроцессору ускорения, у нее есть очень мощные функции для таких вещей. (впоследствии вы можете # #undef macros после вызова вызова BOOST_PP_REPEAT для генерации функций)

Вот ответ, подобный кадабре, который не воюет с состоянием ostream:

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

Я считаю, что первый параграф ответа Копро описывает, почему все так ведет себя.

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

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

Я не совсем понимаю поведение первого аргумента.

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

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

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

с кадаброй

 ostringstream() << std::dec 

работает и для MSVC ++.

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

Interesting Posts

Возможно ли импортировать файл реестра Windows без разрушения системы?

Как извлечь все внешние ссылки на веб-страницу и сохранить их в файле?

Как создать пользовательскую кнопку в Android с помощью стилей XML

Selenium WebDriver: Подождите, пока сложная страница с JavaScript для загрузки

Наиболее эффективный способ случайного выбора набора различных целых чисел

Получение этого простого регулярного выражения для соответствия в grep

Невозможно обработать проверку данных info.plist приложения в это время из-за общей ошибки (1095)

Как создать автоматический запуск скрипта без выбора Ubuntu?

Статический порядок инициализации Java

Что означает «#» в Scala?

Объявить массив const

«Неполный тип» в classе, который имеет один и тот же тип самого classа

Есть ли альтернатива использованию% (модуля) в C / C ++?

SSH-туннель в домашнюю сеть и доступ к веб-интерфейсу маршрутизатора

Получить текущее название маршрута в Ember

Давайте будем гением компьютера.