ДЕЙСТВИТЕЛЬНО BRIEF
Как создать неподписанную константу со всеми установленными битами?
..., который вы можете использовать для инициализации поля с помощью {} 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
В принципе, пользователь, парень, пишущий {} инициализацию, не знает тип ufield. Он знает только, что это неподписанный тип, но не такой широкий. Не совсем какой тип без знака.
* Еще одна причина Почему я хочу как можно более простой и элегантный синтаксис *
Я мог бы также упомянуть что-то еще: "пользователь" здесь фактически не пишет программу на C или С++. Он редактирует файл конфигурации. Программа, простой Perl или Python script, обрабатывает конфигурационный файл и генерирует код C. Эта программа не очень сложна и в данный момент проходит через куски текста, которые выглядят как
Foo: {-1,2,3};
для генерации ЬурейеЕ struct Some_Struct {unsigned a; unsigned b, unsigned c; } Some_Struct = {-1,2,3};//ditto
В принципе, я хочу иметь удобный синтаксис для простого литерала, который гласит: Msgstr "Все биты в этом значении без знака установлены". Не зная, насколько велика неподписанная. И без программы, которая обрабатывает конфигурационный файл, становясь слишком сложным.
Чтобы потенциальный поставщик ответов не жаловался, что это новое ограничение, не реалистичное и т.д.:
У меня была такая же проблема с шаблонами.
То есть с типами шаблонов, где я хочу написать литерал, который "без знака любой ширины, все 1s".
В шаблоне я мог бы быть более готов сделать некоторые из уродливых, Ugly, UGLY синтаксиса
что, очевидно, способно это сделать:
но я действительно хотел, чтобы был простой, элегантный, синтаксис.
* Реальный вопрос *
Q: существует ли способ создать константу, которая является "все 1s set" без запуска предупреждения 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/С++:
> ./a.out
ustruct.ufield = ffffffff
Мне не нравятся предупреждения, поэтому я хотел бы заставить замолчать это раздражающее предупреждение. Я предпочитаю не использовать #pragmas, которые применяются ко всему файлу, поскольку это может отключить предупреждение для реальных ошибок.
(Кстати, вы получаете это предупреждение только при инициализации поля. Не при инициализации не-поля
unsigned u = -1; // no cmpiler warning.
Выполнение
struct U { unsigned ufield; } ustruct = { ~0U };
заглушает ошибку.
Но было указано, что если тип поля не является беззнаковым, но вместо этого uint64_t, то ~ 0U дает другой результат, чем -1: 0x00000000FFFFFFFF, а не 0xFFFFFFFFFFFFFFFF. (I.e 32 бита 1s, а не 64 бит 1s.)
Структура U и код инициализации могут жить в совершенно разных местах, и мы хотели бы иметь возможность увеличить размер поля, битмаску, не сообщая пользователям. И цель состоит в том, чтобы получить "маску всех 1s" любого беззнакового типа.
Аналогично
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.
Немного более удобная версия ответа @Ali:
#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
, чтобы разрешить все интегральные типы, или добавить еще одну перегрузку с помощью nice static_assert
.
constexpr
не будет плохой идеей для этого, не так ли?
Как насчет этого? Он работает только для неподписанных типов, но в вопросе конкретно указано без знака. (см. Комментарии к 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
.
all_bits_one<int>()
не имеет всех битов один. Вы должны будете использовать -1
для подписанных через SFINAE. И сделайте функцию constexpr
пока вы в ней ;-)
.
Почему бы не указать маску вместе с типом?
С
struct U { unsigned ufield; };
#define U_MASK (-1U)
// somewhere far away
U u = {U_MASK};
С++:
struct U { unsigned ufield; static constexpr unsigned MASK = -1; };
// somewhere far away
U u = {U::MASK};
U u = { -1 };
это просто хорошо.
Другой способ
// 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);
u
уже есть действительное, но унифицированное значение при вызове all_bits_one
? Не могли бы вы указать на стандарт для этого?
Весь модный код шаблона в стороне, вот какой-то причудливый код С++ 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) };
который является подверженным ошибкам, но функциональным.
Лучшим решением по-прежнему является использование (tped) enum (class)
для этого.
decltype
, забыл об этом. Что ж, вам нужно знать название поля, но, по крайней мере, вы можете оставаться в неведении относительно его типа.
~0ULL
всегда?-1U
не является константой-1
с суффиксомU
- это константа1U
как операнд унарного оператора отрицания-
.