Использование -1 для инициализации без знака в {} инициализации структуры или массива

14

ДЕЙСТВИТЕЛЬНО 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.
  • 0
    Почему бы не использовать ~0ULL всегда?
  • 1
    -1U не является константой -1 с суффиксом U - это константа 1U как операнд унарного оператора отрицания - .
Показать ещё 8 комментариев
Теги:
c++11
initialization

6 ответов

6
Лучший ответ

Немного более удобная версия ответа @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.

  • 0
    +1 - «Немного более удобная для пользователя версия ...» - на самом деле выглядит как полностью улучшенная версия и решение проблемы ОП (хотя я и не тестировал ее, но я собираюсь вам здесь доверять). В конце концов, это не требует какой-либо модификации или отражения типа структуры, как того хотел ОП.
  • 0
    Кстати, constexpr не будет плохой идеей для этого, не так ли?
Показать ещё 10 комментариев
3

Как насчет этого? Он работает только для неподписанных типов, но в вопросе конкретно указано без знака. (см. Комментарии к 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.

  • 0
    all_bits_one<int>() не имеет всех битов один. Вы должны будете использовать -1 для подписанных через SFINAE. И сделайте функцию constexpr пока вы в ней ;-) .
  • 1
    @rubenvb Извините, вопрос конкретно говорит без подписи.
Показать ещё 5 комментариев
2

Почему бы не указать маску вместе с типом?

С

 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};
  • 2
    Для C не требуется никакого решения. В C U u = { -1 }; это просто хорошо.
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 };
  • 0
    +1 Точно также, хотя вам все еще нужно назвать поле (но хорошо, по крайней мере, не его тип).
1

Вдохновленный Али, но используя вычитание аргумента шаблона.

T all_bits_one(T& dummy) { return ~(T()); }
unsigned long u = all_bits_one(u);
  • 0
    Я прав в том, что это работает, потому что u уже есть действительное, но унифицированное значение при вызове all_bits_one ? Не могли бы вы указать на стандарт для этого?
  • 0
    Да, было бы неплохо какое-то объяснение, у меня трудности с пониманием этого подхода.
Показать ещё 4 комментария
1

Весь модный код шаблона в стороне, вот какой-то причудливый код С++ 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) для этого.

  • 0
    Конечно decltype , забыл об этом. Что ж, вам нужно знать название поля, но, по крайней мере, вы можете оставаться в неведении относительно его типа.
  • 1
    @Christian Кристиан, я не очень рекомендую это, это всего лишь дешевый обходной путь. Почему вы не устанавливаете это значение в конструкторе? Похоже, это немного больше, чем по умолчанию, для которого конструктор идеально подходит.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню