ДЕЙСТВИТЕЛЬНО КРАТКИЙ
Как создать константу без знака, в которой установлены все биты?
…что вы можете использовать для инициализации поля с {} s,
…это не получает предупреждение о сужении от GCC 4.7.2.
Следующее не является удовлетворительным:
struct U { unsigned ufield; };
struct Uc { unsigned char ufield; };
struct ULL { unsigned long long ufield; };
struct U32 { unsigned ufield; };
struct U64 { uint64_t ufield; }
typedef
//any of the above U Uc ULL U32 U64, we will arbitrarily choose:
U Ueg;// somewhere far away
Ueg u = {-1}; // well defined by C standard, GCC 4.7.2 -Wnarrowing warning
Ueg u = {~0U}; // finit width constant, warnings in some places, silent non-all-1s-mask others
Ueg u = {~0ULL}; // ditto
Ueg u = {-1ULL}; // ditto
По сути, пользователь, парень, пишущий инициализацию {},
не знает тип поля.
Он знает только, что это тип без знака, но не как широко.
Не совсем какой тип без знака это.
* Еще одна причина, почему я хочу максимально простой и элегантный синтаксис *
Я мог бы также упомянуть кое-что еще: «пользователь» здесь фактически не пишет программу на C или C ++.
Он редактирует файл конфигурации.
Программа, простой скрипт на Perl или Python, обрабатывает файл конфигурации и генерирует C-код.
Эта программа не очень сложна, и в данный момент проходит через куски текста, которые выглядят как
Foo: {-1,2,3};
чтобы генерировать
ЬурейеЕ
struct Some_Struct {unsigned a; без знака b, без знака c; }
Some_Struct = {-1,2,3}; // то же самое
По сути, я хочу иметь удобный синтаксис для литерала, который говорит
Msgstr «Все биты в этом значении без знака установлены».
Без необходимости знать, какой большой из неподписанных.
И без программы, которая обрабатывает файл конфигурации, становится слишком сложным.
Чтобы потенциальный поставщик ответов не пожаловался, что это новое ограничение, нереалистичное и т. Д.
У меня была точно такая же проблема с шаблонами.
То есть с типами шаблонов, где я хочу написать литерал, который «без знака любой ширины, все 1 с».
В шаблоне я мог бы быть более склонен к использованию некрасивого синтаксиса Ugly, UGLY.
что, очевидно, может сделать это:
но мне бы очень хотелось, чтобы был простой, элегантный синтаксис.
* Настоящий вопрос *
Вопрос: есть ли способ создать константу, для которой установлено «все 1», без предупреждения GCC 4.7.2?
КРАТКАЯ
Я наткнулся на программу, которая использовала буквальную константу -1 для инициализации поля структуры, например,
> cat ./-1u.cpp
#include <stdio.h>
struct U { unsigned ufield; } ustruct = { -1 };
int main(int argc, char** argv)
{
printf("ustruct.ufield = %08x\n",ustruct.ufield);
}
Хотя более ранние версии GCC приняли это без предупреждения,
относительно недавняя версия GCC 4.7.2 содержит предупреждение:
> /SOME-PATH/import/gcc/gcc-4.7.2.pkg/installdir/bin/g++ -Wall ./-1u.cpp
./-1u.cpp:3:46: warning: narrowing conversion of '-1' from 'int' to 'unsigned int' inside { } is ill-formed in C++11 [-Wnarrowing]
Примечание: это только предупреждение. Результат преобразования -1 в unsigned хорошо определен в стандартах C / C ++:
> ./a.out
ustruct.ufield = ffffffff
Мне не нравятся предупреждения, поэтому я хотел бы заставить замолчать это раздражающее предупреждение. Я предпочитаю не использовать #pragmas, которые применяются ко всему файлу, так как это может отключить предупреждение для реальных ошибок.
(Кстати, вы получаете это предупреждение только при инициализации поля. Не при инициализации неполя
unsigned u = -1; // no cmpiler warning.
дела
struct U { unsigned ufield; } ustruct = { ~0U };
заставляет замолчать ошибку.
Но было отмечено, что если тип поля не без знака, а вместо этого uint64_t,
тогда ~ 0U дает результат, отличный от -1: 0x00000000FFFFFFFF, а не 0xFFFFFFFFFFFFFFFF.
(Т.е. 32 бита 1 с, а не 64 бита 1 с.)
Структура U и код инициализации могут жить в совершенно разных местах,
и мы хотели бы иметь возможность увеличить размер поля, немного маскируя, не информируя пользователей.
И цель состоит в том, чтобы получить «маску всех 1» любого типа без знака.
так же
struct U { unsigned ufield; } ustruct = { -1u };
заставляет замолчать ошибку. (К моему удивлению — я не знал, что -1 может считаться незамеченным.)
Но также является постоянной конечной ширины.
ДЕТАЛЬ
Вот тестовая программа.
(Кстати, все, о чем я спрашиваю, это использование литеральной константы со знаком -1
инициализировать неподписанного члена. Другие предупреждения — просто тесты.
Вам не нужно объяснять мне, что 64-разрядное число не умещается в 32-разрядное.)
sh-3.2$ cat ./-1u.cpp
#include <stdio.h>
unsigned um1 = -1;
unsigned un0u = ~0u;
unsigned un0ull = ~0ull;
struct Foo {
unsigned um1;
unsigned un0u;
unsigned un0ull;
};
Foo foo = { -1, ~0u, ~0ull };int main(int argc, char** argv)
{
printf("um1 = %08x\n",um1);
printf("un0u = %08x\n",un0u);
printf("un0ull = %08x\n",un0ull);
printf("foo.um1 = %08x\n",foo.um1);
printf("foo.un0u = %08x\n",foo.un0u);
printf("foo.un0ull = %08x\n",foo.un0ull);
}
sh-3.2$ /mips/proj/performance/import/gcc/gcc-4.7.2.pkg/installdir/bin/gcc -Wall ./-1u.cpp
./-1u.cpp:7:20: warning: large integer implicitly truncated to unsigned type [-Woverflow]
./-1u.cpp:15:28: warning: narrowing conversion of '-1' from 'int' to 'unsigned int' inside { } is ill-formed in C++11 [-Wnarrowing]
./-1u.cpp:15:28: warning: narrowing conversion of '18446744073709551615ull' from 'long long unsigned int' to 'unsigned int' inside { } is ill-formed in C++11 [-Wnarrowing]
./-1u.cpp:15:28: warning: large integer implicitly truncated to unsigned type [-Woverflow]
sh-3.2$ /mips/proj/performance/import/gcc/gcc-4.7.2.pkg/installdir/bin/g++ -Wall ./-1u.cpp
./-1u.cpp:7:20: warning: large integer implicitly truncated to unsigned type [-Woverflow]
./-1u.cpp:15:35: warning: narrowing conversion of '-1' from 'int' to 'unsigned int' inside { } is ill-formed in C++11 [-Wnarrowing]
./-1u.cpp:15:35: warning: narrowing conversion of '18446744073709551615ull' from 'long long unsigned int' to 'unsigned int' inside { } is ill-formed in C++11 [-Wnarrowing]
./-1u.cpp:15:35: warning: large integer implicitly truncated to unsigned type [-Woverflow]
Не встречается в более раннем компиляторе:
sh-3.2$ /usr/bin/g++ -Wall ./-1u.cpp
./-1u.cpp:7: warning: large integer implicitly truncated to unsigned type
./-1u.cpp:15: warning: large integer implicitly truncated to unsigned type
/usr/bin/g++ --version
g++ (GCC) 4.1.2 20080704 (Red Hat 4.1.2-51)
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Чуть более удобная версия ответа @ Али:
#include <type_traits>
struct all_ones_type {
template <typename T,
typename = typename std::enable_if<std::is_unsigned<T>::value>::type>
constexpr operator T () const
{ return static_cast<T>(-1); }
} const all_ones;
#include <iostream>
struct X {
unsigned short a;
unsigned long b;
unsigned long long c;
};
int main() {
X x = { all_ones, all_ones, all_ones };
std::cout << x.a << "\n"<< x.b << "\n"<< x.c << std::endl;
}
В зависимости от того, что вы хотите сделать при попытке преобразования в подписанный тип, вы можете изменить enable_if
разрешить все целочисленные типы или добавить другую перегрузку с хорошим static_assert
,
Как насчет этого? Это работает только для неподписанных типов, но вопрос конкретно говорит без знака. (См. Комментарии rubenvb ниже.)
#include <cinttypes>
#include <iomanip>
#include <iostream>
#include <limits>
#include <type_traits>
template <typename T>
T all_bits_one() {
static_assert(std::is_unsigned<T>::value, "the type must be unsigned");
return std::numeric_limits<T>::max();
}
struct Ui {
typedef unsigned int the_type;
the_type ufield;
};
struct ULL {
typedef unsigned long long the_type;
the_type ufield;
};
struct U64 {
typedef uint64_t the_type;
the_type ufield;
};
int main() {
using namespace std;
Ui ui = { all_bits_one< Ui::the_type>() };
ULL ull = { all_bits_one<ULL::the_type>() };
U64 u64 = { all_bits_one<U64::the_type>() };
cout << hex;
cout << "unsigned int: " << ui.ufield << endl;
cout << "unsigned long long: " << ull.ufield << endl;
cout << "unsigned int 64: " << u64.ufield << endl;
//all_bits_one<int>(); // causes compile-time error if uncommented
return 0;
}
Пользователь не должен знать точный тип the_type
или количество битов, на которых оно представлено.
Существует некоторое дублирование кода, которое можно удалить, но для этого потребуется лучшее понимание вашего кода и проблемы, с которой вы сталкиваетесь.
Я думаю, вы упростили свой код перед публикацией. Как оно есть, ваш struct
не имеет смысла для меня, простой typedef
было бы достаточно.
Почему бы не предоставить маску вместе с типом?
C:
struct U { unsigned ufield; };
#define U_MASK (-1U)
// somewhere far away
U u = {U_MASK};
C ++:
struct U { unsigned ufield; static constexpr unsigned MASK = -1; };
// somewhere far away
U u = {U::MASK};
Если оставить в стороне весь модный код шаблона, вот немного модного кода C ++ 11:
struct Foo
{
unsigned a;
unsigned long b;
unsigned long long c;
};
Foo foo = { decltype(Foo::a)(-1), decltype(Foo::b)(-1), decltype(Foo::c)(-1) };
который подвержен ошибкам, но функционален.
Лучшее решение по-прежнему использовать (напечатано) enum (class)
за это.
По-другому
// C++03 and C++11
Ueg u = { (Ueg().ufield - 1) };
// C99 and C11 (works only inside of functions)
Ueg u = { (Ueg){0}.ufield - 1 };
Вдохновлен Али, но с использованием шаблона Аргумент вычитания.
T all_bits_one(T& dummy) { return ~(T()); }
unsigned long u = all_bits_one(u);
Может быть, то, что вы хотите, инициализируя uint -1, это то, что все биты установлены в «1»?
В таком случае:
typedef uint MyUIntType;
MyUIntType mID = (MyUIntType)~0;
~
применяет «1 дополнение» к 0
который эффективно переворачивает все свои биты.
Результатом будет наибольшее значение, которое может содержать ваш тип uint, что полезно в случаях, когда 0 является значимым значением, а переменные необходимо инициализировать как «что-то еще».