Sürekli numaralandırma C ++ 11


17

Bir enum sürekli olup olmadığını C ++ 11 denetlemek için bir yolu var mı ?

Olmayan bir enum değerleri vermek tamamen geçerlidir. Enum sürekli olup olmadığını kontrol etmek için C ++ 14, C ++ 17 veya belki C ++ 20'de bir tür özelliği gibi bir özellik var mı? Bu, static_assert içinde kullanılacak.

Küçük bir örnek şöyle:

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
Artan bir sıraya sahip olduğu veya her değer için sıfır ve sonra +1 ile başladığı anlamına mı geliyor?
RoQuOTriX

5
Numaralandırma etiketlerini numaralandırmanın bir yolu yoktur, bu yüzden bunu programın içinden yapmak mümkün değildir.
Bazı programcı ahbap

1
İlginç. Ben bir faktöriyel hesaplamak için nasıl bir derleyici alabilirsiniz hatları boyunca şablon programlama hatları boyunca düşünüyorum. İki A ve C sınırı ile işi başlatacaksınız ve şablon işlevleri SFINAE aracılığıyla, aralarındaki tüm değerlerin olup olmadığını kontrol eder enum. Ne yazık ki bir günlük işim var, bu yüzden bu yaklaşımı temel alan bir cevabı iptal edeceğim. Eminim @barry veya @sehe gibi biri bunu yapabilir.
Bathsheba

1
@RoQuOTriX Bir değeri bir etiketle nasıl eşleştirirsiniz? Ve etiketlerin sırasını nasıl kontrol edersiniz? Ve derleme zamanında nasıl yapılabilir (buna ihtiyaç vardır static_assert)? "Güzel bir çözüm" yapamasanız bile, yine de genel olarak nasıl yapılabileceğini merak ettiğim için bir cevap yazın.
Bazı programcı ahbap

1
@Someprogrammerdude "güzel" ya da iyi bir çözüm. Demek istediğim her enum ve tanrı için yeniden yazmak zorunda kalacak "kolay" kontrol çözümü, umarım kimse bunu yapmaz
RoQuOTriX

Yanıtlar:


7

Bir kaç enums için Magic Enum kütüphanesini kullanarak bunu hackleyebilirsiniz . Örneğin:

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

Bunun kütüphane adından da anlaşılacağı gibi, "sihirli" olduğuna dikkat edin - kütüphane, derleyiciye özgü bir dizi saldırı üzerinde çalışır. Bu yüzden gerçekten "saf C ++" gereksiniminizi karşılamıyor, ancak dilde yansıma olanaklarımız olana kadar alabileceğimiz kadar iyi.


Gerçekten sihir ama bu benim durumum en iyi paketi olacaktır.
Bart

7

Saf C ++ 'da bu mümkün değildir, çünkü enum değerlerini numaralandırmanın veya değerlerin sayısını ve minimum ve maksimum değerleri keşfetmenin bir yolu yoktur. Ancak istediğiniz şeye yakın bir şey uygulamak için derleyicinizin yardımını kullanmayı deneyebilirsiniz. Örneğin, gcc'de bir switchdeyim bir numaralandırmanın tüm değerlerini işlemezse bir derleme hatası uygulamak mümkündür :

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

Açıkçası, bu belirli bir numaralandırma için uzmanlaşmıştır, ancak bu tür işlevlerin tanımı önişlemci ile otomatikleştirilebilir.


Eğer doğru anlıyorsam, bu anahtar ve minmax için listede tüm numaralandırma değerlerini yazmayı gerektirir. Şu anda birden fazla numaram var, bu yüzden gerçekten mümkün ama durumum için tercih edilmiyor.
Bart

1

Bu konuda bir cevap görmek isterim. Ben de buna ihtiyacım var.

Ne yazık ki, bunun mevcut yardımcı programları kullanarak mümkün olduğunu düşünmüyorum. Bunun üzerine bir tür özelliği uygulamak istiyorsanız, derleyicinizden desteğe ihtiyacınız vardır, bu nedenle bunun için bir şablon yazmak kulağa mantıklı gelmez.

Zaten bitişik olduğunu göstermek için belirli bir etiketi ile numaralandırma zaten genişletilmiş ve hemen size boyutu verir: enum sınıfı yapıcı c ++, nasıl belirli bir değer geçmek için?

Alternatif olarak kendi özelliğinizi de yazabilirsiniz:

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

Bunu kullanmak istediğiniz bitişik bir enum tanımladığınızda bunun uzman olması gerekir. Ne yazık ki, enum değiştirilirse bu biraz bakım ve dikkat gerektirir.


1
Tür doğru ayarlanmışsa, derlerken kontrol eden bir kod denetleyicisi yazabilirsiniz
RoQuOTriX

Evet kesinlikle. Yazma yeteneğiniz varsa.
JVApen

1

Tüm numaralar süreklidir. 0'a her zaman izin verilir; izin verilen en yüksek değer, bir sonrakine yuvarlanan en yüksek numaralandırıcıdır 1<<N -1(tümü bir bit) ve aradaki tüm değerlere de izin verilir. ([dcl.enum] 9.7.1 / 5). Tanımlanmış negatif numaralandırıcılar varsa, izin verilen en düşük değer benzer şekilde en düşük numaralandırıcının yuvarlanmasıyla tanımlanır.

İçinde tanımlanan numaralandırıcılar enum, aralık ve doğru türde bir değere sahip sabit ifadelerdir, ancak dışında enumaynı özelliklere sahip ek sabitler tanımlayabilirsiniz :

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


2
Doğru olmanıza rağmen, OP'den bunu tanımlanmış değerler için bilmek istediğimiz açıktır. (Not: oylama benim değil)
JVApen

1
@JVApen: Kesinlikle sorun bu. "Tanımlanmış değerler", enum türünün kendisinin bir özelliği değildir. Standart, enum değerlerinin ne olduğunu açıktır.
MSalters
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.