2013-08-31 9 views
12

Powiedzmy Mam zestaw flag i klasę takiego:Jak używać klasy wyliczeniowej jako zestawu flag?

/// <summary>Options controlling a search for files.</summary> 
enum class FindFilesOptions : unsigned char 
{ 
    LocalSearch = 0, 
    RecursiveSearch = 1, 
    IncludeDotDirectories = 2 
}; 

class FindFiles : boost::noncopyable 
{ 
    /* omitted */ 
public: 
    FindFiles(std::wstring const& pattern, FindFilesOptions options); 
    /* omitted */ 
} 

i chcę rozmówca, aby móc wybrać więcej niż jedną opcję:

FindFiles handle(Append(basicRootPath, L"*"), 
    FindFilesOptions::RecursiveSearch | FindFilesOptions::IncludeDotDirectories); 

Czy to możliwe, aby wspierać to w sposób mocno napisany przy pomocy C++ 11 enum class, czy też muszę powrócić do wyliczeń bez typów?

(wiem dzwoniący mógł static_cast do typu bazowego i static_cast z powrotem, ale nie chcę rozmówca musiał zrobić)

+0

Istnieje również ['std :: bitset'] (http://en.cppreference.com/w/cpp/utility/bitset) do przekazywania flag. – dyp

Odpowiedz

12

pewno jest to możliwe używaj enum class es dla bitmap. Niestety, jest to trochę bolesne: musisz zdefiniować niezbędne operacje bitowe na swoim typie. Poniżej znajduje się przykład, jak to może wyglądać. Byłoby miło, gdyby enum class es mógł pochodzić z innego typu, który mógłby żyć w odpowiedniej przestrzeni nazw definiującej niezbędny kod operatora.

#include <iostream> 
#include <type_traits> 

enum class bitmap: unsigned char 
{ 
    a = 0x01, 
    b = 0x02, 
    c = 0x04 
}; 

bitmap operator& (bitmap x, bitmap y) 
{ 
    typedef std::underlying_type<bitmap>::type uchar; 
    return bitmap(uchar(x) & uchar(y)); 
} 

bitmap operator| (bitmap x, bitmap y) 
{ 
    typedef std::underlying_type<bitmap>::type uchar; 
    return bitmap(uchar(x) | uchar(y)); 
} 

bitmap operator^ (bitmap x, bitmap y) 
{ 
    typedef std::underlying_type<bitmap>::type uchar; 
    return bitmap(uchar(x)^uchar(y)); 
} 

bool test(bitmap x) 
{ 
    return std::underlying_type<bitmap>::type(x); 
} 

int main() 
{ 
    bitmap v = bitmap::a | bitmap::b; 
    if (test(v & bitmap::a)) { 
     std::cout << "a "; 
    } 
    if (test(v & bitmap::b)) { 
     std::cout << "b "; 
    } 
    if (test(v & bitmap::c)) { 
     std::cout << "c "; 
    } 
    std::cout << '\n'; 
} 
+0

Właściwie to nie wydaje mi się, że jest to bolesne - chcę tylko pozwolić rozmówcom na OR razem, więc wszystko, czego bym potrzebował, to operator. +1 –

+0

@BillyONeal: Jestem przeciwny jakiemukolwiek powielaniu kodu. Wolałbym, gdyby operatorzy mogli zostać wywołani używając czegoś takiego jak 'enum class bitmap: bitmaps :: base {...};' z powyższymi operatorami zdefiniowanymi jako szablony w przestrzeni nazw 'bitmapy'. –

+0

zawsze można wyeliminować operatorów za pomocą makr. –

0

Problemem nie jest wyraźny typ enum ale zakres klasy.

z C++ 11, enum jako kompilacji stałą czasową uderzeniem duże zainteresowanie w porównaniu z grupą constexpr kiedy trzeba działać na wartości (praca bitowe, incrementation itp)

+1

Tworzenie wartości 'constexpr's pozwoliłoby komuś przekazać dowolny stary typ całkowy do' FindFiles', którego nie chcę dopuścić. –

+0

Byłem trochę szybki, chodzi o to, aby napisać małe opakowanie szablonu na typ skalarny, biorąc drugi typ klasy jako znacznik (jak iterator_category), aby zapobiec mieszaniu dwóch zestawów stałych. – galop1n

+0

To wymagałoby ode mnie, aby 'FindFiles :: FindFiles (std :: wstring const &, options)' w szablonie. –

0

Jeśli nie zależy Ci na wydajności, zmień opcje na set<FindFilesOptions>!

+0

To sprawia, że ​​osoba dzwoniąca jest zbyt skomplikowana. –

1

Jak zdefiniować FindFiles, aby uzyskać std::initializer_list z FindFilesOptions.

void FindFiles(std::wstring const& pattern, std::initializer_list<FindFilesOptions> options) 
{ 
    auto has_option = [&](FindFilesOptions const option) 
    { 
    return std::find(std::begin(options), std::end(options), option) != std::end(options); 
    }; 

    if (has_option(FindFilesOptions::LocalSearch)) 
    { 
    // ... 
    } 

    if (has_option(FindFilesOptions::RecursiveSearch)) 
    { 
    // ... 
    } 

    if (has_option(FindFilesOptions::IncludeDotDirectories)) 
    { 
    // ... 
    } 
} 

Następnie można nazwać tak:

FindFiles({}, {FindFilesOptions::RecursiveSearch, FindFilesOptions::IncludeDotDirectories}); 
+0

Interesujące. Teraz, jeśli używany kompilator obsługuje tylko 'initializer_list'. +1 –

+0

Następnie wystarczy użyć std :: vector. – Natok

+0

To sprawia, że ​​osoba dzwoniąca jest niepotrzebnie złożona. –

3

Szablony grać dobrze z enum class tak można zdefiniować zestawy operatorów, którzy pracują na zestawach podobnych typów wyliczeniowych. Kluczem jest użycie szablonu cech do określenia, które interfejsy (-y) każdego wyliczenia spełniają/subskrybują.

Na początek:

enum class mood_flag { 
    jumpy, 
    happy, 
    upset, 
    count // size of enumeration 
}; 

template<> 
struct enum_traits<mood_flag> { 
    static constexpr bool bit_index = true; 
}; 

template< typename t > 
struct flag_bits : std::bitset< static_cast<int>(t::count) > { 
    flag_bits(t bit) // implicit 
     { this->set(static_cast<int>(bit)); } 

    // Should be explicit but I'm lazy to type: 
    flag_bits(typename flag_bits::bitset set) 
     : flag_bits::bitset(set) {} 
}; 

template< typename e > 
typename std::enable_if< enum_traits<e>::bit_index, 
    flag_bits<e> >::type 
operator | (flag_bits<e> set, e next) 
    { return set | flag_bits<e>(next); } 

template< typename e > 
typename std::enable_if< enum_traits<e>::bit_index, 
    flag_bits<e> >::type 
operator | (e first, e next) 
    { return flag_bits<e>(first) | next; } 

http://ideone.com/kJ271Z

GCC 4.9 poinformował, że niektóre ukryte funkcje składowe były constexpr a ja coraz to skompilować, więc szablony powinny prawdopodobnie tak dobrze.

Powinno to również mieć wolną funkcję to_scalar lub coś, co zwraca liczbę całkowitą typu bez znaku, podaną jako pojedyncza flaga lub zestaw flag_bits.

Powiązane problemy