Постійний перерахунок C ++ 11


17

Чи є спосіб перевірити в C ++ 11, якщо перерахунок є безперервним ?

Цілком справедливо давати значення перерахунків, яких немає. Чи може бути така функція, як ознака типу в C ++ 14, C ++ 17 або, можливо, C ++ 20, щоб перевірити, чи перераховується перерахунок? Це потрібно використовувати в static_assert.

Невеликий приклад:

enum class Types_Discontinuous {
  A = 10,
  B = 1,
  C = 100
};

enum class Types_Continuous {
  A = 0,
  B = 1,
  C = 2
};

static_assert(SOME_TEST<Types_Discontinuous>::value, "Enum should be continuous"); // Fails
static_assert(SOME_TEST<Types_Continuous>::value, "Enum should be continuous");    // Passes

1
Чи означає, що він має порядок зростання або означає, що починається з нуля, а потім +1 для кожного значення?
RoQuOTriX

5
Неможливо перерахувати мітки перерахування, тому це неможливо зробити всередині самої програми.
Якийсь програміст чувак

1
Цікаво. Я думаю, що в рамках рядків програмування шаблонів уздовж рядків того, як можна отримати компілятор для обчислення факторіалу. Ви відкинете річ двома межами A і C, а функції шаблону перевіряють через SFINAE на наявність чи в іншому випадку всіх значень між ними в enum. На жаль, у мене щоденна робота, тому не можу спробувати це виписати, хоча я буду підтримувати відповідь, виходячи з цього підходу. Я впевнений, що хтось, як @barry або @sehe міг це зробити.
Вірсавія

1
@RoQuOTriX Як би ви співставили значення з міткою? І як би ви перевірили порядок етикетки? І як це можна зробити в час компіляції (що потрібно static_assert)? Навіть якщо ви не можете зробити "красиве рішення", будь-ласка, напишіть відповідь все одно, оскільки мені дуже цікаво, як це можна зробити загальним способом.
Якийсь програміст чувак

1
@Someprogrammerdude, що ви описали, це "красиве" або хороше рішення. Що я мав на увазі, це "легке" рішення для перевірки, яке вам доведеться переписати для кожного перерахунку, і Бог благословить, я сподіваюся, що ніхто цього не робить
RoQuOTriX

Відповіді:


7

Протягом декількох enums ви, мабуть, можете зламати свій шлях за допомогою бібліотеки Magic Enum . Наприклад:

#include "magic_enum.hpp"

template <typename Enum>
constexpr bool is_continuous(Enum = Enum{}) {
    // make sure we're actually testing an enum
    if constexpr (!std::is_enum_v<Enum>)
        return false;
    else {
        // get a sorted list of values in the enum
        const auto values = magic_enum::enum_values<Enum>();
        if (std::size(values) == 0)
            return true;

        // for every value, either it's the same as the last one or it's one larger
        auto prev = values[0];
        for (auto x : values) {
            auto next = static_cast<Enum>(magic_enum::enum_integer(prev) + 1);
            if (x != prev && x != next)
                return false;
            else
                prev = x;
        }
        return true;
    }
}

Зауважте, що це дійсно, як випливає з назви бібліотеки, "магія" - бібліотека функціонує на ряді хак-компіляторів. Як такий, він насправді не відповідає вашій вимозі "чистого C ++", але, ймовірно, настільки ж хороший, як ми можемо отримати, доки у нас не з’явиться засоби для відображення мови.


Це дійсно магія, але це найкраще підійде до моєї ситуації.
Барт

7

У чистому С ++ це неможливо, оскільки немає можливості перерахувати значення перерахунків або виявити кількість значень та мінімальні та максимальні значення. Але ви можете спробувати скористатися допомогою вашого компілятора, щоб реалізувати щось близьке до того, що ви хочете. Наприклад, у gcc можна застосувати помилку компіляції, якщо switchоператор не обробляє всі значення перерахунку:

enum class my_enum {
    A = 0,
    B = 1,
    C = 2
};

#pragma GCC diagnostic push
#if __GNUC__ < 5
#pragma GCC diagnostic error "-Wswitch"
#else
#pragma GCC diagnostic error "-Wswitch-enum"
#endif

constexpr bool is_my_enum_continuous(my_enum t = my_enum())
{
    // Check that we know all enum values. Effectively works as a static assert.
    switch (t)
    {
    // Intentionally no default case.
    // The compiler will give an error if not all enum values are listed below.
    case my_enum::A:
    case my_enum::B:
    case my_enum::C:
        break;
    }

    // Check that the enum is continuous
    auto [min, max] = std::minmax({my_enum::A, my_enum::B, my_enum::C});
    return static_cast< int >(min) == 0 && static_cast< int >(max) == 2;
}

#pragma GCC diagnostic pop

Очевидно, що це спеціалізується для заданого перерахунку, але визначення таких функцій може бути автоматизовано за допомогою препроцесора.


Якщо я правильно розумію, все одно знадобиться виписати всі значення перерахунку в перемикачі та списку для minmax. Наразі у мене є декілька перерахунків, тому це справді можливо, але не бажано для моєї ситуації.
Барт

1

Я хотів би побачити відповідь на це. Я також потребував цього.

На жаль, я не думаю, що це можливо за допомогою наявних утиліт. Якщо ви хочете реалізувати для цього ознаку типу, вам потрібна підтримка вашого компілятора, тому написання шаблону для цього не здається можливим.

Я вже розширив перерахування певним тегом, щоб вказати, що він є суміжним, і одразу дає вам розмір: конструктор класу enum c ++, як передавати конкретне значення?

Крім того, ви можете написати власну ознаку:

 template<T> struct IsContiguous : std::false_type {};

Це потрібно спеціалізувати, коли ви визначаєте суміжний перелік, де ви хочете це використовувати. На жаль, це вимагає певного обслуговування та уваги, якщо переживання змінюються.


1
Ви можете написати перевірку коду, яка перевіряє під час компіляції, якщо тип встановлений правильним
RoQuOTriX

Так, справді. Якщо у вас є можливість написати це.
JVApen

1

Усі перерахунки безперервні. 0 завжди дозволено; найвище дозволене значення - це найбільше число, округлене до наступного 1<<N -1(усі біти один), і всі значення між ними також дозволені. ([dcl.enum] 9.7.1 / 5). Якщо визначено від'ємні нумератори, найменше допустиме значення аналогічно визначається шляхом округлення нижнього нумератора.

Перелічувачі, визначені в enumпостійних виразах, мають значення в діапазоні та правильний тип, але ви можете визначити додаткові константи, поза enumякими мають однакові властивості:

constexpr enum class Types_Discontinuous = static_cast<Types_Discontinuous>(2)


2
Хоча ви праві, з ОП зрозуміло, що ми хочемо це знати для визначених значень. (PS: голосування вниз - не моє)
JVApen

1
@JVApen: Це саме проблема. "Визначені значення" не є властивістю типу enum. Стандарт явно визначає значення перерахунків.
MSalters
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.