Continuous enum C ++ 11

17

Apakah ada cara untuk memeriksa C ++ 11 jika enum kontinu ?

Ini sepenuhnya valid untuk memberikan nilai enum yang bukan. Apakah mungkin ada fitur seperti jenis sifat di C ++ 14, C ++ 17 atau mungkin C ++ 20 untuk memeriksa apakah enum kontinu? Ini untuk digunakan dalam static_assert.

Contoh kecil berikut:

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
Bart
sumber
1
Berarti berlanjut, bahwa ia memiliki urutan naik atau artinya dimulai dengan nol lalu +1 untuk setiap nilai?
RoQuOTriX
5
Tidak ada cara untuk menghitung label enumerasi sehingga tidak mungkin melakukannya dari dalam program itu sendiri.
Beberapa programmer Bung
1
Menarik. Saya sedang berpikir di sepanjang baris pemrograman template di sepanjang baris tentang bagaimana Anda bisa mendapatkan kompiler untuk menghitung faktorial. Anda akan memulai masalah dengan dua batas A dan C, dan fungsi template memeriksa melalui SFINAE untuk mengetahui ada atau tidaknya semua nilai di antara mereka di enum. Sayangnya saya memiliki pekerjaan harian sehingga tidak dapat mencoba untuk menulis ini, walaupun saya akan menjawab dengan suara keras berdasarkan pendekatan ini. Saya cukup yakin seseorang seperti @barry atau @sehe bisa melakukannya.
Batsyeba
1
@RoQuOTriX Bagaimana Anda mencocokkan nilai dengan label? Dan bagaimana Anda memeriksa urutan label? Dan bagaimana itu bisa dilakukan pada waktu kompilasi (yang diperlukan untuk static_assert)? Bahkan jika Anda tidak dapat membuat "solusi yang indah", tolong tetap tuliskan jawaban karena saya sangat ingin tahu bagaimana hal itu dapat dilakukan dengan cara yang umum.
Beberapa programmer Bung
1
@Someprogrammerdude apa yang Anda gambarkan adalah solusi "cantik" atau bagus. Yang saya maksudkan adalah solusi pemeriksaan "mudah", yang harus Anda tulis ulang untuk setiap enum dan tuhan memberkati, saya harap tidak ada yang melakukan itu
RoQuOTriX

Jawaban:

7

Untuk beberapa enums Anda mungkin bisa meretas jalan Anda melalui ini menggunakan perpustakaan Magic Enum . Sebagai contoh:

#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;
    }
}

Perhatikan bahwa ini memang, seperti namanya perpustakaan, "ajaib" - pustaka berfungsi pada sejumlah peretas khusus kompiler. Karena itu tidak benar-benar memenuhi persyaratan Anda "pure C ++", tetapi mungkin sebaik yang kami bisa dapatkan sampai kami memiliki fasilitas refleksi dalam bahasa tersebut.

N. Shead
sumber
Memang ajaib tapi ini akan sesuai dengan situasi saya yang terbaik.
Bart
7

Ini tidak mungkin dalam C ++ murni, karena tidak ada cara untuk menyebutkan nilai enum, atau menemukan jumlah nilai dan nilai minimum dan maksimum. Tetapi Anda dapat mencoba menggunakan bantuan kompiler Anda untuk mengimplementasikan sesuatu yang mendekati apa yang Anda inginkan. Sebagai contoh, di gcc dimungkinkan untuk menegakkan kesalahan kompilasi jika switchpernyataan tidak menangani semua nilai enum:

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

Jelas, ini khusus untuk enum yang diberikan, tetapi definisi fungsi tersebut dapat diotomatisasi dengan preprosesor.

Andrey Semashev
sumber
Jika saya mengerti dengan benar, ini masih membutuhkan penulisan semua nilai enum di sakelar dan daftar untuk minmax. Saat ini saya memiliki beberapa enum sehingga memang mungkin tetapi tidak disukai untuk situasi saya.
Bart
1

Saya ingin melihat jawabannya. Saya membutuhkannya juga.

Sayangnya, saya tidak berpikir ini mungkin menggunakan utilitas yang ada. Jika Anda ingin menerapkan sifat jenis ini, Anda memerlukan dukungan dari kompiler Anda, jadi menulis templat untuk itu kedengarannya tidak layak.

Saya sudah memperpanjang enumerasi dengan tag tertentu untuk menunjukkan bahwa itu bersebelahan dan segera memberi Anda ukuran: enum class constructor c ++, bagaimana cara memberikan nilai spesifik?

Atau, Anda dapat menulis sifat Anda sendiri:

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

Ini perlu dikhususkan setiap kali Anda mendefinisikan enum yang berdekatan di mana Anda ingin menggunakan ini. Sayangnya, ini membutuhkan beberapa pemeliharaan dan perhatian jika enum diubah.

JVApen
sumber
1
Anda dapat menulis pemeriksa kode, yang memeriksa saat kompilasi, jika jenisnya disetel dengan benar
RoQuOTriX
Ya memang. Jika Anda memiliki kemampuan untuk menulisnya.
JVApen
1

Semua enum bersifat kontinu. 0 selalu diizinkan; nilai tertinggi yang diizinkan adalah enumerator tertinggi yang dibulatkan ke yang berikutnya 1<<N -1(semua bit satu), dan semua nilai di antaranya diizinkan juga. ([dcl.enum] 9.7.1 / 5). Jika ada enumerator negatif yang ditentukan, nilai terendah yang diizinkan juga didefinisikan dengan membulatkan ke bawah enumerator terendah.

Enumerator yang didefinisikan dalam enumadalah ekspresi konstan dengan nilai dalam rentang dan tipe yang benar, tetapi Anda dapat menentukan konstanta tambahan di luar enumyang memiliki properti yang sama:

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

MSalters
sumber
2
Meskipun Anda benar, dari OP jelas bahwa kami ingin mengetahui ini untuk nilai yang ditentukan. (PS: suara turun bukan milikku)
JVApen
1
@ JVApen: Itulah masalah sebenarnya. "Nilai yang ditentukan" bukan properti dari tipe enum itu sendiri. Standar ini eksplisit apa nilai enum itu.
MSalters