Есть ли ортодоксальный способ избежать предупреждения компилятора C4309 - "усечение постоянного значения" с выходом двоичного файла?

Моя программа выполняет общую задачу записи двоичных данных в файл, соответствующих определенному формату нетекстового файла. Поскольку данные, которые я пишу, уже не существуют в существующих фрагментах, а вместо этого складываются по байтам во время выполнения, я использую std::ostream::put() вместо write(). Я предполагаю, что это обычная процедура.

Программа работает отлично. В качестве аргументов используются как std::stringstream::put(), так и std::ofstream::put() с двухзначными шестнадцатеричными целыми числами. Но я получаю предупреждение компилятора C4309: "усечение постоянного значения" (в VС++ 2010), когда аргумент put() больше 0x7f. Очевидно, что компилятор ожидает signed char, а константа вне диапазона. Но я не думаю, что любое усечение на самом деле происходит; байт записывается так, как предполагалось.

Предупреждения компилятора заставляют меня думать, что я не делаю ничего обычным, принятым способом. Ситуация, которую я описал, должна быть общей. Есть ли общий способ избежать предупреждения о компиляторе? Или это пример бессмысленного предупреждения компилятора, который следует просто игнорировать?

Я подумал о двух непростых способах избежать этого. Я могу использовать синтаксис типа mystream.put( char(0xa4) ) для каждого вызова. Или вместо std::stringstream я мог бы использовать std::basic_stringstream< unsigned char >, но я не думаю, что трюк будет работать с std::ofstream, который не является шаблоном. Я чувствую, что здесь должно быть лучшее решение, тем более что ofstream предназначен для записи двоичных файлов.

Ваши мысли?

- EDIT -

А, я ошибся в том, что std::ofstream не является шаблоном. Это фактически std::basic_ofstream<char>, но я пробовал этот метод, который и понял, что он не будет работать в любом случае из-за отсутствия определенных методов и полиморфной несовместимости с std::ostream.

Вот пример кода:

stringstream ss;
int a, b;
/* Do stuff */
ss.put( 0 );
ss.put( 0x90 | a ); // oddly, no warning here...
ss.put( b );        // ...or here
ss.put( 0xa4 );     // C4309

Ответы

Ответ 1

Я нашел решение, которым я доволен. Это более элегантно, чем явное приведение каждой константы к unsigned char. Это то, что у меня было:

ss.put( 0xa4 ); // C4309

Я думал, что "усечение" происходит при неявном нажатии unsigned char на char, но Конг Сю отметил, что целочисленные константы считаются подписанными, а любой, превышающий 0x7f, получает от char до int. Затем он должен быть усечен (сокращен до одного байта), если он передан на put(). Используя суффикс "u", я могу указать константу без знака, а если она не больше 0xff, это будет unsigned char. Это то, что у меня есть сейчас, без предупреждений компилятора:

ss.put( 0xa4u );

Ответ 2

std::stringstream ss;
ss.put(0x7f);
ss.put(0x80); //C4309

Как вы уже догадались, проблема в том, что ostream.put() ожидает char, но 0x7F является максимальным значением для char, и все большее значение повышается до int. Вы должны использовать для unsigned char ширину, равную char, поэтому она будет хранить что-либо char и безопасно, но также делает предупреждения об усечении законными:

ss.put(static_cast<unsigned char>(0x80)); // OK
ss.put(static_cast<unsigned char>(0xFFFF)); //C4309