├── LICENSE ├── MetaEnumerator.hpp ├── README.md └── samples ├── basic.cpp └── extending.cpp /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Elia Sarti 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MetaEnumerator.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ELIGT_METAENUMERATOR_H 2 | #define ELIGT_METAENUMERATOR_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | #ifdef METAENUMERATOR_NAMESPACE 12 | namespace METAENUMERATOR_NAMESPACE 13 | { 14 | #endif 15 | 16 | template 17 | class EnumeratorDataContainer 18 | { 19 | public: 20 | template 21 | inline constexpr static bool isBigEndian() 22 | { 23 | static_assert(sizeof(IntType) > sizeof(ByteType), "isBigEndian() : IntType must be of size greater than ByteType."); 24 | 25 | IntType number = 0x1; 26 | ByteType *numPtr = (ByteType*)&number; 27 | return (numPtr[0] != 1); 28 | } 29 | 30 | public: 31 | using MemoryType = unsigned char; 32 | using OperandType = unsigned int; 33 | static constexpr const size_t MEMORY_BITS = sizeof(MemoryType) * CHAR_BIT; 34 | static constexpr const MemoryType MEMORY_MASK = std::numeric_limits::max(); 35 | static constexpr const size_t OPERAND_BITS = sizeof(OperandType) * CHAR_BIT; 36 | static constexpr const size_t OPERAND_COUNT = (BitLength / OPERAND_BITS) + (BitLength % OPERAND_BITS != 0 ? 1 : 0); 37 | static constexpr const size_t ROUNDED_BITLENGTH = OPERAND_COUNT * OPERAND_BITS; 38 | static constexpr const size_t ARRAY_SIZE = (ROUNDED_BITLENGTH / MEMORY_BITS) + (ROUNDED_BITLENGTH % MEMORY_BITS != 0 ? 1 : 0); 39 | 40 | static inline constexpr EnumeratorDataContainer max() 41 | { 42 | EnumeratorDataContainer c; 43 | 44 | for (size_t i = 0; i < OPERAND_COUNT; ++i) 45 | { 46 | c._data[i] = std::numeric_limits::max(); 47 | } 48 | 49 | return c; 50 | } 51 | 52 | public: 53 | inline constexpr EnumeratorDataContainer() noexcept = default; 54 | inline constexpr EnumeratorDataContainer(const EnumeratorDataContainer& other) noexcept = default; 55 | inline constexpr EnumeratorDataContainer(OperandType value) noexcept : _data{value} { } 56 | 57 | inline explicit constexpr operator bool() const 58 | { 59 | return !equals(0); 60 | } 61 | 62 | inline constexpr bool equals(const EnumeratorDataContainer& other) const 63 | { 64 | for (size_t i = 0; i < OPERAND_COUNT; ++i) 65 | { 66 | if (_data[i] != other._data[i]) 67 | return false; 68 | } 69 | 70 | return true; 71 | } 72 | 73 | inline constexpr bool equals(OperandType other) const 74 | { 75 | if (_data[0] != other) 76 | return false; 77 | 78 | for (size_t i = 0; i < OPERAND_COUNT; ++i) 79 | { 80 | if (_data[i] != 0) 81 | return false; 82 | } 83 | 84 | return true; 85 | } 86 | 87 | inline constexpr bool operator==(const EnumeratorDataContainer& other) const 88 | { 89 | return equals(other); 90 | } 91 | 92 | inline constexpr bool operator==(OperandType other) const 93 | { 94 | return equals(other); 95 | } 96 | 97 | inline constexpr bool operator!=(const EnumeratorDataContainer& other) const 98 | { 99 | return !equals(other); 100 | } 101 | 102 | inline constexpr bool operator!=(OperandType other) const 103 | { 104 | return !equals(other); 105 | } 106 | 107 | inline constexpr bool has_bit(size_t bit) const 108 | { 109 | const size_t offset = bit / OPERAND_BITS; 110 | const size_t rest = bit % OPERAND_BITS; 111 | 112 | if (offset < OPERAND_COUNT) 113 | return _data[offset] & (static_cast(1) << rest); 114 | 115 | return false; 116 | } 117 | 118 | inline constexpr EnumeratorDataContainer operator&(const EnumeratorDataContainer& other) const 119 | { 120 | EnumeratorDataContainer r{}; 121 | 122 | for (size_t i = 0; i < OPERAND_COUNT; ++i) 123 | { 124 | r._data[i] = _data[i] & other._data[i]; 125 | } 126 | 127 | return r; 128 | } 129 | 130 | inline constexpr EnumeratorDataContainer operator&(OperandType other) const 131 | { 132 | EnumeratorDataContainer r{}; 133 | 134 | r._data[0] = _data[0] & other; 135 | 136 | for (size_t i = 1; i < OPERAND_COUNT; ++i) 137 | { 138 | r._data[i] = 0; 139 | } 140 | 141 | return r; 142 | } 143 | 144 | inline constexpr EnumeratorDataContainer operator&=(const EnumeratorDataContainer& other) 145 | { 146 | for (size_t i = 0; i < OPERAND_COUNT; ++i) 147 | { 148 | _data[i] = _data[i] & other._data[i]; 149 | } 150 | 151 | return *this; 152 | } 153 | 154 | inline constexpr EnumeratorDataContainer operator&=(OperandType other) 155 | { 156 | _data[0] = _data[0] & other; 157 | 158 | for (size_t i = 1; i < OPERAND_COUNT; ++i) 159 | { 160 | _data[i] = 0; 161 | } 162 | 163 | return *this; 164 | } 165 | 166 | inline constexpr EnumeratorDataContainer operator|(const EnumeratorDataContainer& other) const 167 | { 168 | EnumeratorDataContainer r{}; 169 | 170 | for (size_t i = 0; i < OPERAND_COUNT; ++i) 171 | { 172 | r._data[i] = _data[i] | other._data[i]; 173 | } 174 | 175 | return r; 176 | } 177 | 178 | inline constexpr EnumeratorDataContainer operator|(OperandType other) const 179 | { 180 | EnumeratorDataContainer r{}; 181 | 182 | r._data[0] = _data[0] | other; 183 | 184 | for (size_t i = 1; i < OPERAND_COUNT; ++i) 185 | { 186 | r._data[i] = _data[i]; 187 | } 188 | 189 | return r; 190 | } 191 | 192 | inline constexpr EnumeratorDataContainer operator|=(const EnumeratorDataContainer& other) 193 | { 194 | for (size_t i = 0; i < OPERAND_COUNT; ++i) 195 | { 196 | _data[i] = _data[i] | other._data[i]; 197 | } 198 | 199 | return *this; 200 | } 201 | 202 | inline constexpr EnumeratorDataContainer operator|=(OperandType other) 203 | { 204 | _data[0] = _data[0] | other; 205 | 206 | return *this; 207 | } 208 | 209 | inline constexpr EnumeratorDataContainer operator^(const EnumeratorDataContainer& other) const 210 | { 211 | EnumeratorDataContainer r{}; 212 | 213 | for (size_t i = 0; i < OPERAND_COUNT; ++i) 214 | { 215 | r._data[i] = _data[i] ^ other._data[i]; 216 | } 217 | 218 | return r; 219 | } 220 | 221 | inline constexpr EnumeratorDataContainer operator^(OperandType other) const 222 | { 223 | EnumeratorDataContainer r{}; 224 | 225 | r._data[0] = _data[0] ^ other; 226 | 227 | for (size_t i = 1; i < OPERAND_COUNT; ++i) 228 | { 229 | r._data[i] = _data[i] ^ 0; 230 | } 231 | 232 | return r; 233 | } 234 | 235 | inline constexpr EnumeratorDataContainer operator^=(const EnumeratorDataContainer& other) 236 | { 237 | for (size_t i = 0; i < OPERAND_COUNT; ++i) 238 | { 239 | _data[i] = _data[i] ^ other._data[i]; 240 | } 241 | 242 | return *this; 243 | } 244 | 245 | inline constexpr EnumeratorDataContainer operator^=(OperandType other) 246 | { 247 | _data[0] = _data[0] ^ other; 248 | 249 | for (size_t i = 1; i < OPERAND_COUNT; ++i) 250 | { 251 | _data[i] = _data[i] ^ 0; 252 | } 253 | 254 | return *this; 255 | } 256 | 257 | inline constexpr EnumeratorDataContainer operator~() const 258 | { 259 | EnumeratorDataContainer r{}; 260 | 261 | for (size_t i = 0; i < OPERAND_COUNT; ++i) 262 | { 263 | r._data[i] = ~_data[i]; 264 | } 265 | 266 | return r; 267 | } 268 | 269 | inline constexpr EnumeratorDataContainer operator<<(size_t bits) const 270 | { 271 | if (bits == 0) 272 | return *this; 273 | 274 | if (bits == OPERAND_BITS * OPERAND_COUNT) 275 | return {}; 276 | 277 | EnumeratorDataContainer ret{}; 278 | const size_t offset = bits / OPERAND_BITS; 279 | const size_t rest = (bits % OPERAND_BITS); 280 | 281 | if (rest > 0) 282 | { 283 | OperandType carry = 0; 284 | 285 | for (size_t indexRead = 0, indexWrite = offset; indexRead < OPERAND_COUNT - offset; ++indexRead, ++indexWrite) 286 | { 287 | OperandType value = _data[indexRead]; 288 | ret._data[indexWrite] = (value << rest) | carry; 289 | carry = value >> (OPERAND_BITS - rest); 290 | } 291 | } 292 | else 293 | { 294 | for (size_t indexRead = 0, indexWrite = offset; indexRead < OPERAND_COUNT - offset; ++indexRead, ++indexWrite) 295 | ret._data[indexWrite] = _data[indexRead]; 296 | } 297 | 298 | return ret; 299 | } 300 | 301 | inline constexpr EnumeratorDataContainer operator>>(size_t bits) const 302 | { 303 | if (bits == 0) 304 | return *this; 305 | 306 | if (bits == OPERAND_BITS * OPERAND_COUNT) 307 | return {}; 308 | 309 | EnumeratorDataContainer ret{}; 310 | const int offset = bits / OPERAND_BITS; 311 | const int rest = (bits % OPERAND_BITS); 312 | 313 | if (rest > 0) 314 | { 315 | OperandType carry = 0; 316 | 317 | for (int indexRead = OPERAND_COUNT - 1, indexWrite = indexRead - offset; indexRead >= offset; --indexRead, --indexWrite) 318 | { 319 | OperandType value = _data[indexRead]; 320 | ret._data[indexWrite] = (value >> rest) | carry; 321 | carry = value << (OPERAND_BITS - rest); 322 | } 323 | } 324 | else 325 | { 326 | for (int indexRead = OPERAND_COUNT - 1, indexWrite = indexRead - offset; indexRead >= offset; --indexRead, --indexWrite) 327 | ret._data[indexWrite] = _data[indexRead]; 328 | } 329 | 330 | return ret; 331 | } 332 | 333 | public: 334 | OperandType _data[OPERAND_COUNT]; 335 | }; 336 | 337 | 338 | template 339 | class EnumeratorConverter 340 | { 341 | public: 342 | static inline constexpr DataType get_data(EnumType value) 343 | { 344 | auto data_value = static_cast::type>(value); 345 | 346 | if (data_value > bit_length) 347 | return static_cast(0); 348 | 349 | if (data_value == 0) 350 | return 0; 351 | 352 | return static_cast(1) << (data_value - 1); 353 | } 354 | 355 | static inline constexpr EnumType get_value(DataType data) 356 | { 357 | if (data == 0) 358 | return static_cast(0); 359 | 360 | DataType i=0; 361 | while( !((data >> i++) & 0x01) ) { ; } 362 | return static_cast(i); 363 | } 364 | 365 | static inline constexpr EnumType get_bit(size_t index) 366 | { 367 | return static_cast(index); 368 | } 369 | }; 370 | 371 | template 372 | class EnumeratorConverter 373 | { 374 | public: 375 | static inline constexpr DataType get_data(EnumType value) 376 | { 377 | static_cast(value); 378 | } 379 | 380 | static inline constexpr EnumType get_value(DataType data) 381 | { 382 | static_cast(data); 383 | } 384 | 385 | static inline constexpr EnumType get_bit(size_t index) 386 | { 387 | return static_cast(1 << index); 388 | } 389 | }; 390 | 391 | template 392 | class EnumeratorMeta 393 | { 394 | }; 395 | 396 | template 397 | class EnumeratorInherited 398 | { 399 | public: 400 | using InheritedType = EnumType; 401 | }; 402 | 403 | template 404 | struct enumerator_has_extension_meta : std::false_type { }; 405 | template 406 | struct enumerator_has_extension_meta::enum_extension, void())> : std::true_type { }; 407 | 408 | template 409 | struct enumerator_has_extension_value : std::false_type { }; 410 | template 411 | struct enumerator_has_extension_value : std::true_type { }; 412 | 413 | template 414 | struct enumerator_has_extension : std::integral_constant::value || enumerator_has_extension_value::value> { }; 415 | 416 | template 417 | struct enumerator_has_inheritance_meta : std::false_type { }; 418 | template 419 | struct enumerator_has_inheritance_meta::enum_inheritance, void())> : std::true_type { }; 420 | 421 | template 422 | struct enumerator_has_inheritance_value : std::false_type { }; 423 | template 424 | struct enumerator_has_inheritance_value : std::true_type { }; 425 | 426 | template 427 | struct enumerator_has_inheritance : std::integral_constant::value || enumerator_has_inheritance_value::value> { }; 428 | 429 | template 430 | struct enumerator_has_base_type : std::false_type { }; 431 | template 432 | struct enumerator_has_base_type::BaseEnumType(), void())> : std::true_type { }; 433 | 434 | 435 | template 436 | class EnumeratorInfo 437 | { 438 | template 439 | friend class EnumeratorInfo; 440 | 441 | protected: 442 | using Meta = EnumeratorMeta; 443 | using DataType = typename Meta::DataType; 444 | using Extender = typename Meta::Extender; 445 | using Inheritor = typename Meta::Inheritor; 446 | 447 | public: 448 | using EntryType = typename std::remove_const< typename std::remove_reference< typename std::remove_pointer< decltype(Meta::enum_entries[0]) >::type >::type >::type; 449 | static constexpr const size_t ENTRY_COUNT = sizeof(EnumeratorMeta::enum_entries) / sizeof(EntryType); 450 | 451 | struct Result 452 | { 453 | friend class EnumeratorInfo; 454 | 455 | public: 456 | constexpr Result() = default; 457 | 458 | constexpr explicit operator bool() const { return _ptr != nullptr; } 459 | constexpr const EntryType& operator*() const { return *_ptr; } 460 | constexpr const EntryType* operator->() { return _ptr; } 461 | constexpr const EntryType* pointer() { return _ptr; } 462 | 463 | protected: 464 | constexpr Result(const EntryType* entry) : _ptr{entry} {} 465 | 466 | protected: 467 | const EntryType* _ptr{}; 468 | }; 469 | 470 | struct Iterator 471 | { 472 | public: 473 | using iterator_category = std::input_iterator_tag; 474 | using difference_type = std::ptrdiff_t; 475 | using value_type = EntryType; 476 | using pointer = const EntryType*; 477 | using reference = const EntryType&; 478 | 479 | public: 480 | constexpr Iterator(pointer ptr) : _ptr{ptr} { } 481 | 482 | reference operator*() const { return *_ptr; } 483 | pointer operator->() { return _ptr; } 484 | 485 | Iterator& operator++() { _ptr++; return *this; } 486 | Iterator operator++(int) { Iterator tmp = *this; ++(*this); return tmp; } 487 | 488 | friend bool operator== (const Iterator& a, const Iterator& b) { return a._ptr == b._ptr; }; 489 | friend bool operator!= (const Iterator& a, const Iterator& b) { return a._ptr != b._ptr; }; 490 | 491 | private: 492 | pointer _ptr; 493 | }; 494 | 495 | static constexpr Iterator cbegin() { return Iterator(EnumeratorMeta::enum_entries); } 496 | static constexpr Iterator cend() { return Iterator(EnumeratorMeta::enum_entries + ENTRY_COUNT);} 497 | 498 | public: 499 | static constexpr Result find(EnumType value) 500 | { 501 | auto result = findQuick(value); 502 | if (result) 503 | return result; 504 | 505 | return findSlow(value); 506 | } 507 | 508 | static constexpr Result find(const char* name) 509 | { 510 | Result result = findSelf(name); 511 | if (result) 512 | return result; 513 | result = findInherited(name); 514 | if (result) 515 | return result; 516 | result = findExtended(name); 517 | if (result) 518 | return result; 519 | 520 | return {}; 521 | } 522 | 523 | protected: 524 | template ::value, int>::type = 0> 525 | static constexpr size_t getQuickIndex(EnumType value) 526 | { 527 | using BaseMeta = EnumeratorMeta::BaseEnumType>; 528 | using BaseInheritor = typename BaseMeta::Inheritor; 529 | auto dataValue = static_cast(value); 530 | auto dataMin = static_cast(BaseInheritor::get_inheritance()); 531 | auto index = static_cast(dataValue - dataMin) + 1; 532 | 533 | return index; 534 | } 535 | 536 | template ::value, int>::type = 0> 537 | static constexpr size_t getQuickIndex(EnumType value) 538 | { 539 | auto dataValue = static_cast(value); 540 | auto dataMin = static_cast(Meta::MIN_VALUE); 541 | auto index = static_cast(dataValue - dataMin); 542 | 543 | return index; 544 | } 545 | 546 | static constexpr Result findQuickSelf(EnumType value) 547 | { 548 | auto index = getQuickIndex(value); 549 | 550 | if (index < ENTRY_COUNT) 551 | { 552 | auto&& entry = EnumeratorMeta::enum_entries[index]; 553 | if (entry.get_value() == value) 554 | return {&entry}; 555 | } 556 | 557 | // Special case if enum_entries skips the first 0-valued entry 558 | if (index > 0 && index <= ENTRY_COUNT) 559 | { 560 | auto&& entry = EnumeratorMeta::enum_entries[index - 1]; 561 | if (entry.get_value() == value) 562 | return {&entry}; 563 | } 564 | 565 | return {}; 566 | } 567 | 568 | template ::value && !std::is_same::InheritedType>::value, int>::type = 0> 569 | static constexpr Result findQuickInherited(EnumType value) 570 | { 571 | using InheritedType = typename EnumeratorInherited::InheritedType; 572 | using InheritedInfo = EnumeratorInfo; 573 | 574 | auto inheritance = Inheritor::get_inheritance(); 575 | auto dataValue = static_cast(value); 576 | auto dataInheritance = static_cast(inheritance); 577 | 578 | if (dataValue >= dataInheritance) 579 | { 580 | return { InheritedInfo::find(static_cast(value)).pointer() }; 581 | } 582 | 583 | return {}; 584 | } 585 | 586 | template ::value || std::is_same::InheritedType>::value, int>::type = 0> 587 | static constexpr Result findQuickInherited(EnumType) 588 | { 589 | return {}; 590 | } 591 | 592 | template ::value, int>::type = 0> 593 | static constexpr Result findQuickExtended(EnumType value) 594 | { 595 | auto extension = Extender::get_extension(); 596 | auto dataValue = static_cast(value); 597 | auto dataExtension = static_cast(extension); 598 | 599 | if (dataValue >= dataExtension) 600 | { 601 | auto index = static_cast(dataValue - dataExtension); 602 | auto&& container = Extender::get_container(); 603 | 604 | // Note: because of how enum extensions work, we don't need an equivalent findSlow() for extensions 605 | if (index < container.ENTRY_COUNT) 606 | { 607 | auto&& entry = container.enum_entries[index]; 608 | if (entry.get_value() == value) 609 | return {&entry}; 610 | } 611 | } 612 | 613 | return {}; 614 | } 615 | 616 | template ::value, int>::type = 0> 617 | static constexpr Result findQuickExtended(EnumType) 618 | { 619 | return {}; 620 | } 621 | 622 | static constexpr Result findQuick(EnumType value) 623 | { 624 | // Return if the enum values aren't sequential, i.e. their values are already bitflags (isFlags parameter passed to EnumeratorMetaDefault was true) 625 | if (!EnumeratorMeta::bitwise_conversion) 626 | return {}; 627 | 628 | Result result = findQuickSelf(value); 629 | if (result) 630 | return result; 631 | result = findQuickInherited(value); 632 | if (result) 633 | return result; 634 | result = findQuickExtended(value); 635 | if (result) 636 | return result; 637 | 638 | return {}; 639 | } 640 | 641 | static constexpr Result findSlow(EnumType value) 642 | { 643 | auto it = cbegin(); 644 | auto end = cend(); 645 | 646 | for (; it != end; ++it) 647 | { 648 | auto&& entry = *it; 649 | 650 | if (entry.get_value() == value) 651 | return {&entry}; 652 | } 653 | 654 | return {}; 655 | } 656 | 657 | static constexpr bool matchEntry(const EntryType& entry, const char* name) 658 | { 659 | if (entry.get_name() == name) 660 | return true; 661 | else 662 | { 663 | if (name != nullptr && entry.get_name() != nullptr) 664 | { 665 | auto nameP = name; 666 | auto itName = entry.get_name(); 667 | 668 | while (*itName != '\0' && *nameP != '\0' && *itName == *nameP) 669 | { 670 | itName++; 671 | nameP++; 672 | } 673 | 674 | if (*itName == '\0' && *nameP == '\0') 675 | return true; 676 | } 677 | } 678 | 679 | return false; 680 | } 681 | 682 | static constexpr Result findSelf(const char* name) 683 | { 684 | auto it = cbegin(); 685 | auto end = cend(); 686 | 687 | for (; it != end; ++it) 688 | { 689 | auto&& entry = *it; 690 | 691 | if (matchEntry(entry, name)) 692 | return {&entry}; 693 | } 694 | 695 | return {}; 696 | } 697 | 698 | template ::value && !std::is_same::InheritedType>::value, int>::type = 0> 699 | static constexpr Result findInherited(const char* name) 700 | { 701 | using InheritedType = typename EnumeratorInherited::InheritedType; 702 | using InheritedInfo = EnumeratorInfo; 703 | 704 | return { InheritedInfo::find(name).pointer() }; 705 | } 706 | 707 | template ::value || std::is_same::InheritedType>::value, int>::type = 0> 708 | static constexpr Result findInherited(const char*) 709 | { 710 | return {}; 711 | } 712 | 713 | template ::value, int>::type = 0> 714 | static constexpr Result findExtended(const char* name) 715 | { 716 | auto&& container = Extender::get_container(); 717 | Iterator it(container.enum_entries); 718 | Iterator end(container.enum_entries + container.ENTRY_COUNT); 719 | 720 | for (; it != end; ++it) 721 | { 722 | auto&& entry = *it; 723 | 724 | if (matchEntry(entry, name)) 725 | return {&entry}; 726 | } 727 | 728 | return {}; 729 | } 730 | 731 | template ::value, int>::type = 0> 732 | static constexpr Result findExtended(const char*) 733 | { 734 | return {}; 735 | } 736 | }; 737 | 738 | template 739 | class EnumeratorSerializer 740 | { 741 | using Info = EnumeratorInfo; 742 | using EntryType = typename Info::EntryType; 743 | 744 | public: 745 | static constexpr decltype(std::declval().get_name()) get_name(EnumType value) 746 | { 747 | auto it = Info::find(value); 748 | 749 | if (it) 750 | return it->get_name(); 751 | 752 | return nullptr; 753 | } 754 | 755 | static constexpr decltype(std::declval().get_label()) get_label(EnumType value) 756 | { 757 | auto it = Info::find(value); 758 | 759 | if (it) 760 | return it->get_label(); 761 | 762 | return nullptr; 763 | } 764 | 765 | static constexpr EnumType get_value(const char* name) 766 | { 767 | auto it = Info::find(name); 768 | 769 | if (it) 770 | return it->get_value(); 771 | 772 | return EnumeratorMeta::Converter::get_value(0); 773 | } 774 | }; 775 | 776 | template 777 | class EnumeratorExtender 778 | { 779 | protected: 780 | using Meta = EnumeratorMeta; 781 | using Info = EnumeratorInfo; 782 | using EntryType = typename Info::EntryType; 783 | using DataType = typename Meta::DataType; 784 | static constexpr const DataType max_enum_value = static_cast(Meta::MAX_VALUE); 785 | 786 | public: 787 | template ::value, int>::type = 0> 788 | static constexpr bool has_extension() 789 | { 790 | return true; 791 | } 792 | 793 | template ::value, int>::type = 0> 794 | static constexpr bool has_extension() 795 | { 796 | return false; 797 | } 798 | 799 | template ::value, int>::type = 0> 800 | static constexpr T get_extension() 801 | { 802 | return static_cast(Meta::enum_extension); 803 | } 804 | 805 | template ::value && !enumerator_has_extension_meta::value, int>::type = 0> 806 | static constexpr T get_extension() 807 | { 808 | return static_cast(EnumType::EXTENSION); 809 | } 810 | 811 | template ::value, int>::type = 0> 812 | static constexpr EnumType get_extension() 813 | { 814 | static_assert(sizeof(T) == 0, "EnumeratorExtender requires the EnumeratorMeta to define an enum_extension variable or the enum to contain an EXTENSION value."); 815 | } 816 | 817 | template 818 | struct Container 819 | { 820 | static constexpr const size_t ENTRY_COUNT = max_enum_value - static_cast(extension); 821 | 822 | EntryType enum_entries[ENTRY_COUNT]{}; 823 | EnumType enum_extension = extension; 824 | }; 825 | 826 | // Making this a templated function is an unfortunately required hack to build on MSVC 827 | template 828 | static Container& get_container() 829 | { 830 | static_assert (extension == get_extension(), "Do not pass a template parameter to this function, read comment above."); 831 | static Container container{}; 832 | return container; 833 | } 834 | 835 | template 836 | static EnumType extend(Types... entries) 837 | { 838 | auto&& container = get_container(); 839 | 840 | if (static_cast(container.enum_extension) <= max_enum_value) 841 | { 842 | auto return_enum = container.enum_extension; 843 | auto entry_index = static_cast(return_enum) - static_cast(get_extension()); 844 | container.enum_entries[entry_index] = EntryType{return_enum, entries...}; 845 | 846 | container.enum_extension = static_cast(static_cast(return_enum) + 1); 847 | 848 | return return_enum; 849 | } 850 | 851 | // This is not ideal, if extensions overflow we should throw? or at least assert? 852 | return static_cast(0); 853 | } 854 | }; 855 | 856 | template 857 | class EnumeratorInheritor 858 | { 859 | protected: 860 | using Meta = EnumeratorMeta; 861 | using DataType = typename Meta::DataType; 862 | static constexpr const DataType max_enum_value = static_cast(Meta::MAX_VALUE); 863 | 864 | public: 865 | template ::value, int>::type = 0> 866 | static constexpr bool has_inheritance() 867 | { 868 | return true; 869 | } 870 | 871 | template ::value, int>::type = 0> 872 | static constexpr bool has_inheritance() 873 | { 874 | return false; 875 | } 876 | 877 | template ::value, int>::type = 0> 878 | static constexpr T get_inheritance() 879 | { 880 | return static_cast(Meta::enum_inheritance); 881 | } 882 | 883 | template ::value && !enumerator_has_inheritance_meta::value, int>::type = 0> 884 | static constexpr T get_inheritance() 885 | { 886 | return static_cast(EnumType::INHERITANCE); 887 | } 888 | 889 | template ::value, int>::type = 0> 890 | static constexpr EnumType get_enum_inheritance() 891 | { 892 | static_assert(sizeof(T) == 0, "EnumeratorInheritor requires the EnumeratorMeta to define an enum_inheritance variable or the enum to contain an INHERITANCE value."); 893 | } 894 | 895 | static constexpr DataType inherit() 896 | { 897 | return static_cast(get_inheritance()); 898 | } 899 | 900 | static constexpr DataType inheritMaximum() 901 | { 902 | return max_enum_value; 903 | } 904 | 905 | template ::value, int>::type = 0> 906 | static constexpr DataType inheritExtension() 907 | { 908 | return static_cast(Meta::Extender::get_extension()); 909 | } 910 | 911 | template ::value, int>::type = 0> 912 | static constexpr DataType inheritExtension() 913 | { 914 | return inheritMaximum(); 915 | } 916 | }; 917 | 918 | template 919 | class EnumeratorSpecializer 920 | { 921 | protected: 922 | using Meta = EnumeratorMeta; 923 | using DataType = typename Meta::DataType; 924 | 925 | public: 926 | template ::value, int>::type = 0> 927 | static constexpr bool has_base() 928 | { 929 | return true; 930 | } 931 | 932 | template ::value, int>::type = 0> 933 | static constexpr bool has_base() 934 | { 935 | return false; 936 | } 937 | }; 938 | 939 | template 940 | class SmartEnumerator 941 | { 942 | protected: 943 | using Meta = EnumeratorMeta; 944 | 945 | public: 946 | using EnumType = EnumType_; 947 | using DataType = typename Meta::DataType; 948 | 949 | public: 950 | constexpr SmartEnumerator(EnumType value) : m_data(value) { } 951 | 952 | template::value && (std::is_same::value || std::is_same::BaseEnumType>::value), int>::type = 0> 953 | constexpr SmartEnumerator(EnumT value) : SmartEnumerator(static_cast(value)) { } 954 | 955 | protected: 956 | constexpr SmartEnumerator(DataType data) : m_data(static_cast(data)) { } 957 | 958 | public: 959 | constexpr DataType data() const 960 | { 961 | return m_data; 962 | } 963 | 964 | constexpr bool operator==(SmartEnumerator a) const 965 | { 966 | return m_data == a.m_data; 967 | } 968 | 969 | constexpr bool operator==(EnumType a) const 970 | { 971 | return m_data == Meta::Converter::get_data(a); 972 | } 973 | 974 | constexpr bool operator!=(SmartEnumerator a) const 975 | { 976 | return m_data != a.m_data; 977 | } 978 | 979 | constexpr bool operator!=(EnumType a) const 980 | { 981 | return m_data != Meta::Converter::get_data(a); 982 | } 983 | 984 | constexpr operator bool() const 985 | { 986 | return m_data != 0; 987 | } 988 | 989 | protected: 990 | EnumType m_data; 991 | }; 992 | 993 | template ::type>::digits> 994 | class EnumeratorMask 995 | { 996 | protected: 997 | using Meta = EnumeratorMeta; 998 | 999 | public: 1000 | using InnerType = EnumType; 1001 | static constexpr const size_t BIT_LENGTH = bit_length; 1002 | using DataType = typename std::conditional< 1003 | bit_length <= std::numeric_limits::digits, unsigned char, 1004 | typename std::conditional< 1005 | bit_length <= std::numeric_limits::digits, unsigned short int, 1006 | typename std::conditional< 1007 | bit_length <= std::numeric_limits::digits, unsigned int, 1008 | typename std::conditional::digits, unsigned long long int, EnumeratorDataContainer>::type 1009 | >::type 1010 | >::type 1011 | >::type; 1012 | 1013 | struct Iterator 1014 | { 1015 | public: 1016 | using iterator_category = std::input_iterator_tag; 1017 | using difference_type = size_t; 1018 | using value_type = EnumType; 1019 | using pointer = const EnumType*; 1020 | using reference = EnumType; 1021 | 1022 | public: 1023 | constexpr Iterator(const EnumeratorMask* mask, size_t current = 1) : _mask{mask}, _current{findValue(current)} 1024 | { } 1025 | 1026 | constexpr reference operator*() const { return Meta::MaskConverter::get_bit(_current); } 1027 | 1028 | constexpr Iterator& operator++() { _current = findValue(_current + 1); return *this; } 1029 | constexpr Iterator operator++(int) { Iterator tmp = *this; ++(*this); return tmp; } 1030 | 1031 | constexpr friend bool operator== (const Iterator& a, const Iterator& b) { return a._mask == b._mask && a._current == b._current; } 1032 | constexpr friend bool operator!= (const Iterator& a, const Iterator& b) { return !(a == b); } 1033 | 1034 | protected: 1035 | constexpr size_t findValue(size_t current) const 1036 | { 1037 | while (!_mask->has_bit(current) && current < BIT_LENGTH) 1038 | { 1039 | ++current; 1040 | } 1041 | 1042 | return current; 1043 | } 1044 | 1045 | private: 1046 | const EnumeratorMask* _mask; 1047 | size_t _current; 1048 | }; 1049 | 1050 | struct ReverseIterator 1051 | { 1052 | public: 1053 | using iterator_category = std::input_iterator_tag; 1054 | using difference_type = size_t; 1055 | using value_type = EnumType; 1056 | using pointer = const EnumType*; 1057 | using reference = EnumType; 1058 | 1059 | public: 1060 | constexpr ReverseIterator(const EnumeratorMask* mask, size_t current = BIT_LENGTH) : _mask{mask}, _current{findValue(current)} 1061 | { } 1062 | 1063 | constexpr reference operator*() const { return Meta::MaskConverter::get_bit(_current); } 1064 | 1065 | constexpr ReverseIterator& operator++() { _current = findValue(_current - 1); return *this; } 1066 | constexpr ReverseIterator operator++(int) { Iterator tmp = *this; ++(*this); return tmp; } 1067 | 1068 | constexpr friend bool operator== (const ReverseIterator& a, const ReverseIterator& b) { return a._mask == b._mask && a._current == b._current; } 1069 | constexpr friend bool operator!= (const ReverseIterator& a, const ReverseIterator& b) { return !(a == b); } 1070 | 1071 | protected: 1072 | constexpr size_t findValue(size_t current) const 1073 | { 1074 | while (!_mask->has_bit(current) && current > 0) 1075 | { 1076 | --current; 1077 | } 1078 | 1079 | return current; 1080 | } 1081 | 1082 | private: 1083 | const EnumeratorMask* _mask; 1084 | size_t _current; 1085 | }; 1086 | 1087 | template ::value, int>::type = 0> 1088 | static inline constexpr EnumeratorMask all() 1089 | { 1090 | static_assert(sizeof(T) <= sizeof(DataType), "EnumeratorMask: T must fit inside DataType"); 1091 | return EnumeratorMask(std::numeric_limits::max()); 1092 | } 1093 | 1094 | template ::value, int>::type = 0> 1095 | static inline constexpr EnumeratorMask all() 1096 | { 1097 | static_assert(sizeof(T) <= sizeof(DataType), "EnumeratorMask: T must fit inside DataType"); 1098 | return EnumeratorMask(T::max()); 1099 | } 1100 | 1101 | public: 1102 | constexpr EnumeratorMask() noexcept : m_data{} 1103 | { } 1104 | constexpr EnumeratorMask(const EnumeratorMask& other) noexcept = default; 1105 | constexpr EnumeratorMask(EnumType value) noexcept : m_data(Meta::MaskConverter::get_data(value)) 1106 | { 1107 | static_assert( 1108 | !Meta::bitwise_conversion || ((size_t)Meta::MAX_VALUE) <= bit_length, 1109 | "EnumeratorMask bit_length has to be large enough to contain enum MAX_VALUE" 1110 | ); 1111 | } 1112 | constexpr explicit EnumeratorMask(DataType data) noexcept : m_data(data) 1113 | { } 1114 | 1115 | public: 1116 | constexpr DataType data() const 1117 | { 1118 | return m_data; 1119 | } 1120 | 1121 | constexpr bool operator==(const EnumeratorMask& a) const 1122 | { 1123 | return m_data == a.m_data; 1124 | } 1125 | 1126 | constexpr bool operator==(EnumType a) const 1127 | { 1128 | return m_data == Meta::MaskConverter::get_data(a); 1129 | } 1130 | 1131 | constexpr bool operator!=(const EnumeratorMask& a) const 1132 | { 1133 | return m_data != a.m_data; 1134 | } 1135 | 1136 | constexpr bool operator!=(EnumType a) const 1137 | { 1138 | return m_data != Meta::MaskConverter::get_data(a); 1139 | } 1140 | 1141 | constexpr bool is_empty() const 1142 | { 1143 | return m_data == 0; 1144 | } 1145 | 1146 | constexpr bool has(EnumType bit) const 1147 | { 1148 | static_assert( 1149 | !Meta::bitwise_conversion || ((size_t)Meta::MAX_VALUE) <= bit_length, 1150 | "EnumeratorMask bit_length has to be large enough to contain enum MAX_VALUE" 1151 | ); 1152 | 1153 | auto val = Meta::MaskConverter::get_data(bit); 1154 | return (m_data & val) == val; 1155 | } 1156 | 1157 | constexpr bool has(EnumeratorMask mask) const 1158 | { 1159 | return (m_data & mask.m_data) == mask.m_data; 1160 | } 1161 | 1162 | constexpr bool has(DataType data) const 1163 | { 1164 | return (m_data & data) == data; 1165 | } 1166 | 1167 | constexpr bool has_any(EnumeratorMask mask) const 1168 | { 1169 | return (m_data & mask.m_data) != 0; 1170 | } 1171 | 1172 | constexpr bool has_any(DataType data) const 1173 | { 1174 | return (m_data & data) != 0; 1175 | } 1176 | 1177 | constexpr void set(EnumType bit, bool value) 1178 | { 1179 | static_assert( 1180 | !Meta::bitwise_conversion || ((size_t)Meta::MAX_VALUE) <= bit_length, 1181 | "EnumeratorMask bit_length has to be large enough to contain enum MAX_VALUE" 1182 | ); 1183 | 1184 | auto val = Meta::MaskConverter::get_data(bit); 1185 | if (value) 1186 | m_data |= val; 1187 | else 1188 | m_data &= ~val; 1189 | } 1190 | 1191 | constexpr void set(EnumeratorMask mask, bool value) 1192 | { 1193 | if (value) 1194 | m_data |= mask.m_data; 1195 | else 1196 | m_data &= ~mask.m_data; 1197 | } 1198 | 1199 | constexpr void set(DataType data, bool value) 1200 | { 1201 | if (value) 1202 | m_data |= data; 1203 | else 1204 | m_data &= ~data; 1205 | } 1206 | 1207 | constexpr void clear() 1208 | { 1209 | m_data = 0; 1210 | } 1211 | 1212 | constexpr EnumeratorMask operator|(EnumType a) const 1213 | { 1214 | static_assert( 1215 | !Meta::bitwise_conversion || ((size_t)Meta::MAX_VALUE) <= bit_length, 1216 | "EnumeratorMask bit_length has to be large enough to contain enum MAX_VALUE" 1217 | ); 1218 | 1219 | auto data = Meta::MaskConverter::get_data(a); 1220 | EnumeratorMask m(m_data | data); 1221 | return m; 1222 | } 1223 | 1224 | constexpr EnumeratorMask operator|(EnumeratorMask a) const 1225 | { 1226 | EnumeratorMask m(m_data | a.m_data); 1227 | return m; 1228 | } 1229 | 1230 | constexpr EnumeratorMask operator|=(EnumType a) 1231 | { 1232 | static_assert( 1233 | !Meta::bitwise_conversion || ((size_t)Meta::MAX_VALUE) <= bit_length, 1234 | "EnumeratorMask bit_length has to be large enough to contain enum MAX_VALUE" 1235 | ); 1236 | 1237 | m_data = m_data | Meta::MaskConverter::get_data(a); 1238 | return *this; 1239 | } 1240 | 1241 | constexpr EnumeratorMask operator|=(EnumeratorMask a) 1242 | { 1243 | m_data = m_data | a.m_data; 1244 | return *this; 1245 | } 1246 | 1247 | constexpr EnumeratorMask operator&(EnumType a) const 1248 | { 1249 | EnumeratorMask m(m_data & Meta::MaskConverter::get_data(a)); 1250 | return m; 1251 | } 1252 | 1253 | constexpr EnumeratorMask operator&(EnumeratorMask a) const 1254 | { 1255 | EnumeratorMask m(m_data & a.m_data); 1256 | return m; 1257 | } 1258 | 1259 | constexpr EnumeratorMask operator&=(EnumType a) 1260 | { 1261 | m_data = m_data & Meta::MaskConverter::get_data(a); 1262 | return *this; 1263 | } 1264 | 1265 | constexpr EnumeratorMask operator&=(EnumeratorMask a) 1266 | { 1267 | m_data = m_data & a.m_data; 1268 | return *this; 1269 | } 1270 | 1271 | constexpr EnumeratorMask operator^(EnumType a) const 1272 | { 1273 | EnumeratorMask m(m_data ^ Meta::MaskConverter::get_data(a)); 1274 | return m; 1275 | } 1276 | 1277 | constexpr EnumeratorMask operator^(EnumeratorMask a) const 1278 | { 1279 | EnumeratorMask m(m_data ^ a.m_data); 1280 | return m; 1281 | } 1282 | 1283 | constexpr EnumeratorMask operator^=(EnumType a) 1284 | { 1285 | m_data = m_data ^ Meta::MaskConverter::get_data(a); 1286 | return *this; 1287 | } 1288 | 1289 | constexpr EnumeratorMask operator^=(EnumeratorMask a) 1290 | { 1291 | m_data = m_data ^ a.m_data; 1292 | return *this; 1293 | } 1294 | 1295 | constexpr EnumeratorMask operator~() const 1296 | { 1297 | EnumeratorMask m(~m_data); 1298 | return m; 1299 | } 1300 | 1301 | constexpr Iterator begin() const 1302 | { 1303 | return Iterator(this); 1304 | } 1305 | 1306 | constexpr Iterator end() const 1307 | { 1308 | return Iterator(this, BIT_LENGTH); 1309 | } 1310 | 1311 | constexpr Iterator cbegin() const 1312 | { 1313 | return Iterator(this); 1314 | } 1315 | 1316 | constexpr Iterator cend() const 1317 | { 1318 | return Iterator(this, BIT_LENGTH); 1319 | } 1320 | 1321 | constexpr ReverseIterator rbegin() const 1322 | { 1323 | return ReverseIterator(this); 1324 | } 1325 | 1326 | constexpr ReverseIterator rend() const 1327 | { 1328 | return ReverseIterator(this, 0); 1329 | } 1330 | 1331 | constexpr ReverseIterator crbegin() const 1332 | { 1333 | return ReverseIterator(this); 1334 | } 1335 | 1336 | constexpr ReverseIterator crend() const 1337 | { 1338 | return ReverseIterator(this, 0); 1339 | } 1340 | 1341 | constexpr EnumType at(size_t index) const 1342 | { 1343 | auto data = m_data; 1344 | 1345 | while (data != 0) 1346 | { 1347 | auto value = data & 1; 1348 | 1349 | if (value) 1350 | { 1351 | if (index == 0) 1352 | return value; 1353 | 1354 | index--; 1355 | } 1356 | 1357 | data >> 1; 1358 | } 1359 | 1360 | return {}; 1361 | } 1362 | 1363 | std::string toString() const 1364 | { 1365 | std::stringstream ss; 1366 | ss << *this; 1367 | 1368 | return ss.str(); 1369 | } 1370 | 1371 | protected: 1372 | template>::value, int>::type = 0> 1373 | constexpr bool has_bit(size_t bit) const 1374 | { 1375 | if (bit == 0) 1376 | return false; 1377 | 1378 | return m_data.has_bit(bit - 1); 1379 | } 1380 | template>::value, int>::type = 0> 1381 | constexpr bool has_bit(size_t bit) const 1382 | { 1383 | if (bit == 0) 1384 | return false; 1385 | 1386 | auto val = static_cast(1) << (bit - 1); 1387 | return (m_data & val) == val; 1388 | } 1389 | 1390 | protected: 1391 | DataType m_data; 1392 | }; 1393 | 1394 | 1395 | template::value && EnumeratorMeta::Specializer::has_base(), int>::type = 0> 1396 | inline constexpr typename EnumeratorMeta::BaseEnumType operator+(EnumType value) 1397 | { 1398 | using BaseType = typename EnumeratorMeta::BaseEnumType; 1399 | return static_cast(value); 1400 | } 1401 | 1402 | template::value && EnumeratorMeta::Specializer::has_base(), int>::type = 0> 1403 | inline constexpr bool operator==(EnumType value, typename EnumeratorMeta::BaseEnumType other) 1404 | { 1405 | using BaseType = typename EnumeratorMeta::BaseEnumType; 1406 | return static_cast(value) == other; 1407 | } 1408 | 1409 | template::value && EnumeratorMeta::Specializer::has_base(), int>::type = 0> 1410 | inline constexpr bool operator==(typename EnumeratorMeta::BaseEnumType other, EnumType value) 1411 | { 1412 | using BaseType = typename EnumeratorMeta::BaseEnumType; 1413 | return static_cast(value) == other; 1414 | } 1415 | 1416 | template ::value && EnumeratorMeta::math_operators, int>::type = 0> 1417 | inline constexpr EnumType operator+(EnumType a, EnumType b) 1418 | { 1419 | using DataType = typename EnumeratorMeta::DataType; 1420 | return static_cast(static_cast(a) + static_cast(b)); 1421 | } 1422 | 1423 | template ::DataType, typename std::enable_if::value && EnumeratorMeta::math_operators, int>::type = 0> 1424 | inline constexpr EnumType operator+(EnumType a, DataType b) 1425 | { 1426 | return static_cast(static_cast(a) + b); 1427 | } 1428 | 1429 | template ::DataType, typename std::enable_if::value && EnumeratorMeta::math_operators, int>::type = 0> 1430 | inline constexpr DataType operator+(DataType a, EnumType b) 1431 | { 1432 | return (a + static_cast(b)); 1433 | } 1434 | 1435 | template ::DataType, typename std::enable_if::value && EnumeratorMeta::math_operators, int>::type = 0> 1436 | inline constexpr EnumType& operator+=(EnumType& a, EnumType b) 1437 | { 1438 | a = static_cast(static_cast(a) + static_cast(b)); 1439 | return a; 1440 | } 1441 | 1442 | template ::DataType, typename std::enable_if::value && EnumeratorMeta::math_operators, int>::type = 0> 1443 | inline constexpr EnumType& operator+=(EnumType& a, DataType b) 1444 | { 1445 | a = static_cast(static_cast(a) + b); 1446 | return a; 1447 | } 1448 | 1449 | template ::DataType, typename std::enable_if::value && EnumeratorMeta::math_operators, int>::type = 0> 1450 | inline constexpr DataType& operator+=(DataType& a, EnumType b) 1451 | { 1452 | return (a += static_cast(b)); 1453 | } 1454 | 1455 | template ::DataType, typename std::enable_if::value && EnumeratorMeta::math_operators, int>::type = 0> 1456 | inline constexpr EnumType& operator++(EnumType& a) 1457 | { 1458 | a = static_cast(static_cast(a) + 1); 1459 | return a; 1460 | } 1461 | 1462 | template ::DataType, typename std::enable_if::value && EnumeratorMeta::math_operators, int>::type = 0> 1463 | inline constexpr DataType operator++(EnumType& a, int) 1464 | { 1465 | EnumType ret = a; 1466 | a = static_cast(static_cast(a) + 1); 1467 | return ret; 1468 | } 1469 | 1470 | template ::DataType, typename std::enable_if::value && EnumeratorMeta::math_operators, int>::type = 0> 1471 | inline constexpr EnumType operator-(EnumType a, EnumType b) 1472 | { 1473 | return static_cast(static_cast(a) - static_cast(b)); 1474 | } 1475 | 1476 | template ::DataType, typename std::enable_if::value && EnumeratorMeta::math_operators, int>::type = 0> 1477 | inline constexpr EnumType operator-(EnumType a, DataType b) 1478 | { 1479 | return static_cast(static_cast(a) - b); 1480 | } 1481 | 1482 | template ::DataType, typename std::enable_if::value && EnumeratorMeta::math_operators, int>::type = 0> 1483 | inline constexpr DataType operator-(DataType a, EnumType b) 1484 | { 1485 | return (a - static_cast(b)); 1486 | } 1487 | 1488 | template ::DataType, typename std::enable_if::value && EnumeratorMeta::math_operators, int>::type = 0> 1489 | inline constexpr EnumType& operator-=(EnumType& a, EnumType b) 1490 | { 1491 | a = static_cast(static_cast(a) - static_cast(b)); 1492 | return a; 1493 | } 1494 | 1495 | template ::DataType, typename std::enable_if::value && EnumeratorMeta::math_operators, int>::type = 0> 1496 | inline constexpr EnumType& operator-=(EnumType& a, DataType b) 1497 | { 1498 | a = static_cast(static_cast(a) - b); 1499 | return a; 1500 | } 1501 | 1502 | template ::DataType, typename std::enable_if::value && EnumeratorMeta::math_operators, int>::type = 0> 1503 | inline constexpr DataType& operator-=(DataType& a, EnumType b) 1504 | { 1505 | return (a -= static_cast(b)); 1506 | } 1507 | 1508 | template ::DataType, typename std::enable_if::value && EnumeratorMeta::math_operators, int>::type = 0> 1509 | inline constexpr EnumType& operator--(EnumType& a) 1510 | { 1511 | a = static_cast(static_cast(a) - 1); 1512 | return a; 1513 | } 1514 | 1515 | template ::DataType, typename std::enable_if::value && EnumeratorMeta::math_operators, int>::type = 0> 1516 | inline constexpr DataType operator--(EnumType& a, int) 1517 | { 1518 | EnumType ret = a; 1519 | a = static_cast(static_cast(a) - 1); 1520 | return ret; 1521 | } 1522 | 1523 | template ::DataType, typename std::enable_if::value && EnumeratorMeta::math_operators, int>::type = 0> 1524 | inline constexpr EnumType operator<<(EnumType a, EnumType b) 1525 | { 1526 | return static_cast(static_cast(a) << static_cast(b)); 1527 | } 1528 | 1529 | template ::DataType, 1530 | typename std::enable_if::value && EnumeratorMeta::math_operators && std::is_integral::value, int>::type = 0> 1531 | inline constexpr DataType operator<<(DataType a, EnumType b) 1532 | { 1533 | return (a << static_cast(b)); 1534 | } 1535 | 1536 | template ::DataType, typename std::enable_if::value && EnumeratorMeta::math_operators && std::is_integral::value, int>::type = 0> 1537 | inline constexpr EnumType operator<<(EnumType a, DataType b) 1538 | { 1539 | return static_cast(static_cast(a) << b); 1540 | } 1541 | 1542 | template ::DataType, typename std::enable_if::value && EnumeratorMeta::math_operators, int>::type = 0> 1543 | inline constexpr EnumType operator>>(EnumType a, EnumType b) 1544 | { 1545 | return static_cast(static_cast(a) >> static_cast(b)); 1546 | } 1547 | 1548 | template ::DataType, typename std::enable_if::value && EnumeratorMeta::math_operators && std::is_integral::value, int>::type = 0> 1549 | inline constexpr DataType operator>>(DataType a, EnumType b) 1550 | { 1551 | return (a >> static_cast(b)); 1552 | } 1553 | 1554 | template ::DataType, typename std::enable_if::value && EnumeratorMeta::math_operators && std::is_integral::value, int>::type = 0> 1555 | inline constexpr EnumType operator>>(EnumType a, DataType b) 1556 | { 1557 | return static_cast(static_cast(a) >> b); 1558 | } 1559 | 1560 | template ::value && EnumeratorMeta::logic_operators, int>::type = 0> 1561 | constexpr typename EnumeratorMeta::MaskType operator|(EnumType a, EnumType b) 1562 | { 1563 | return (typename EnumeratorMeta::MaskType(a) | b); 1564 | } 1565 | 1566 | template ::value && EnumeratorMeta::logic_operators, int>::type = 0> 1567 | constexpr typename EnumeratorMeta::MaskType operator&(EnumType a, EnumType b) 1568 | { 1569 | return (typename EnumeratorMeta::MaskType(a) & b); 1570 | } 1571 | 1572 | template ::value && EnumeratorMeta::logic_operators, int>::type = 0> 1573 | constexpr typename EnumeratorMeta::MaskType operator^(EnumType a, EnumType b) 1574 | { 1575 | return (typename EnumeratorMeta::MaskType(a) ^ b); 1576 | } 1577 | 1578 | template ::value && EnumeratorMeta::logic_operators, int>::type = 0> 1579 | constexpr typename EnumeratorMeta::MaskType operator~(EnumType a) 1580 | { 1581 | return (~typename EnumeratorMeta::MaskType(a)); 1582 | } 1583 | 1584 | template ::value && EnumeratorMeta::string_operators, int>::type = 0> 1585 | inline std::ostream& operator<<(std::ostream& os, EnumType value) 1586 | { 1587 | auto name = EnumeratorSerializer::get_name(value); 1588 | 1589 | if (name != nullptr) 1590 | return os << name; 1591 | 1592 | return os; 1593 | } 1594 | 1595 | template ::value && EnumeratorMeta::string_operators, int>::type = 0> 1596 | inline std::ostream& operator<<(std::ostream&os, const EnumeratorMask& value) 1597 | { 1598 | using Meta = EnumeratorMeta; 1599 | using Converter = typename Meta::MaskConverter; 1600 | auto data = value.data(); 1601 | size_t index = 1; 1602 | 1603 | typename Meta::MaskDataType val = 0; 1604 | 1605 | while(data != 0) 1606 | { 1607 | if (val) 1608 | os << ", "; 1609 | 1610 | val = (data & 0x01); 1611 | 1612 | if (val) 1613 | { 1614 | EnumType v = Converter::get_bit(index); 1615 | os << v; 1616 | } 1617 | 1618 | data = data >> 1; 1619 | index++; 1620 | } 1621 | 1622 | return os; 1623 | } 1624 | 1625 | template ::type>::digits> 1626 | class EnumeratorMetaDefault 1627 | { 1628 | public: 1629 | using EnumType = EnumType_; 1630 | using DataType = typename std::underlying_type::type; 1631 | using Converter = EnumeratorConverter; 1632 | using Extender = EnumeratorExtender; 1633 | using Inheritor = EnumeratorInheritor; 1634 | using Specializer = EnumeratorSpecializer; 1635 | using MaskType = EnumeratorMask; 1636 | using MaskDataType = typename MaskType::DataType; 1637 | using MaskConverter = EnumeratorConverter; 1638 | static constexpr const bool bitwise_conversion = !isFlags; 1639 | static constexpr const EnumType MIN_VALUE = static_cast(0); 1640 | 1641 | struct EnumEntry 1642 | { 1643 | EnumType value{}; 1644 | const char* name{}; 1645 | const char* label{}; 1646 | 1647 | constexpr EnumEntry() = default; 1648 | constexpr EnumEntry(EnumType val, const char* name_) : value(val), name(name_), label("") { } 1649 | constexpr EnumEntry(EnumType val, const char* name_, const char* label_) : value(val), name(name_), label(label_) { } 1650 | 1651 | constexpr EnumType get_value() const { return value; } 1652 | constexpr const char* get_name() const { return name; } 1653 | constexpr const char* get_label() const { return label; } 1654 | }; 1655 | }; 1656 | 1657 | #ifdef METAENUMERATOR_NAMESPACE 1658 | } 1659 | #endif 1660 | 1661 | 1662 | #endif 1663 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Meta Enumerator 2 | Meta Enumerator is a modern, single-header C++14 library to enhance enumerator capabilities and ease enum operations. The primary feature is enabling automatic generation of arbitrarily large (bitwise) masks based off of regular, sequential enum values - useful when you want to be able to enumerate values while also using them as flags. 3 | 4 | The enum masks are statically checked, type safe and statically allocated for better performance. Also, when using `enum classes`, Mask type can be forward declared (like the `enum class` itself) and therefore used in function parameters before the enum is fully defined. 5 | 6 | - [Features](#Features) 7 | - [Missing Features](#Missing-Features) 8 | - [Installation](#Installation) 9 | - [Usage](#Usage) 10 | - [Forward Declaration](#Forward-Declaration) 11 | - [Arbitrarily Large Masks](#Arbitrarily-Large-Masks) 12 | 13 | 14 | ## Features 15 | 16 | - Automatic generation of masks based off enums 17 | - Mask is held in a custom class, guaranteeing type safety 18 | - Mask can be arbitrarily large, to store any amount of flags 19 | - Mask storage is always allocated statically, using templates 20 | - Static checks ensure Mask will always be large enough to contain all enum values, else a compile error is raised 21 | - Mask can detect if all enum flags will fit in a built-in type and use that 22 | - If using built-in type, all bitwise operations on the mask will collapse to built-in bitwise operators 23 | - If using `enum class`, Mask type can be forward declared and used in function parameters before the enum is defined 24 | - Opted-in conversion to and from string (you need to specify the strings though, see [Usage](#usage)) 25 | - Opted-in support for automatic mathematical and logical operations on the enum values 26 | - Ability to customize implementation to attach arbitrary meta-data to each enum value 27 | - Enum inheritance, i.e. static extension of an enum with another enum at compile time 28 | - Enum extensions, i.e. dynamic extension of an enum with new values at run time 29 | - **NO MACROS!** I really dislike how many C++ enum utilities make heavy usage of macros, which is another reason why I wrote this library 30 | 31 | ## Missing Features 32 | 33 | Things I'd like to implement at some point but are not there yet. 34 | 35 | - Endianness awareness; make the library work on Big Endian systems 36 | - Tests! Right now there are none, although I've tested this quite a bit as I actively use it in my game 37 | - Samples and Documentation on how to modify some of the behaviors 38 | 39 | 40 | ## Installation 41 | 42 | Just copy `MetaEnumerator.hpp` to your project and include it like the following: 43 | 44 | ```cpp 45 | #define METAENUMERATOR_NAMESPACE MyRootNamespace 46 | #include "MetaEnumerator.hpp" 47 | ``` 48 | 49 | `MyRootNamespace` is the root namespace in your project. Define this for quality of life, otherwise you'd be required to close/open namespaces every time you need to define the template specializations for your own enum types. If you don't use a namespace (i.e. your code lives in the global namespace) then you can remove the `#define METAENUMERATOR_NAMESPACE MyRootNamespace` line entirely. 50 | 51 | 52 | ## Usage 53 | 54 | This is how you define the meta information on your enum: 55 | 56 | ```cpp 57 | #define METAENUMERATOR_NAMESPACE MyRootNamespace 58 | #include "MetaEnumerator.hpp" 59 | 60 | namespace MyRootNamespace 61 | { 62 | 63 | enum class TargetType 64 | { 65 | NONE = 0, 66 | ENEMY_ALIVE, 67 | ENEMY_CORPSE, 68 | ENEMY_SPOT, 69 | ALLY_ALIVE, 70 | ALLY_CORPSE, 71 | ALLY_SPOT, 72 | MAX = ALLY_SPOT 73 | }; 74 | 75 | template <> 76 | class EnumeratorMeta : public EnumeratorMetaDefault 77 | { 78 | public: 79 | static constexpr const bool math_operators = true; 80 | static constexpr const bool logic_operators = true; 81 | static constexpr const bool string_operators = true; 82 | static constexpr const TargetType MAX_VALUE = TargetType::MAX; 83 | 84 | static constexpr const EnumEntry enum_entries[]{ 85 | { TargetType::ENEMY_ALIVE, "ENEMY_ALIVE" }, 86 | { TargetType::ENEMY_CORPSE, "ENEMY_CORPSE" }, 87 | { TargetType::ENEMY_SPOT, "ENEMY_SPOT" }, 88 | { TargetType::ALLY_ALIVE, "ALLY_ALIVE" }, 89 | { TargetType::ALLY_CORPSE, "ALLY_CORPSE" }, 90 | { TargetType::ALLY_SPOT, "ALLY_SPOT" } 91 | }; 92 | }; 93 | 94 | } 95 | ``` 96 | 97 | You also need to add this somewhere in your `.cpp` file to ensure you don't get linking errors: 98 | 99 | ```cpp 100 | constexpr EnumeratorMeta::EnumEntry EnumeratorMeta::enum_entries[]; 101 | ``` 102 | 103 | This is how you'd create a mask: 104 | ```cpp 105 | // creates a mask named accepted_targets 106 | auto accepted_targets = TargetType::ENEMY_ALIVE | TargetType::ALLY_ALIVE; 107 | std::cout << "Accepted Targets: " << accepted_targets << "\n"; 108 | // prints: Accepted Targets: ENEMY_ALIVE, ALLY_ALIVE 109 | ``` 110 | 111 | **Note**: `accepted_targets` above is an instance of a custom (templated) class and therefore type-safe. In fact, if you don't want to use `auto` you can actually specify the type, e.g. 112 | ```cpp 113 | // creates a mask named accepted_targets 114 | EnumeratorMask accepted_targets = TargetType::ENEMY_ALIVE | TargetType::ALLY_ALIVE; 115 | std::cout << "Accepted Targets: " << accepted_targets << "\n"; 116 | // prints: Accepted Targets: ENEMY_ALIVE, ALLY_ALIVE 117 | ``` 118 | 119 | This is how you'd check a mask against a flag: 120 | ```cpp 121 | // creates a mask named accepted_targets 122 | auto accepted_targets = TargetType::ENEMY_ALIVE | TargetType::ALLY_ALIVE; 123 | if (accepted_targets.has(TargetType::ENEMY_ALIVE)) 124 | std::cout << "Looking for a live enemy...\n"; 125 | // prints: Looking for a live enemy... 126 | ``` 127 | 128 | This is how you obtain the string representation for an enum value: 129 | 130 | ```cpp 131 | const char* targetStr = EnumeratorSerializer::get_name(TargetType::ALLY_CORPSE); 132 | std::cout << "We got this target: " << targetStr; 133 | // prints: We got this target: ALLY_CORPSE 134 | ``` 135 | 136 | *Note*: the library supports `ostream operator<<` directly so if you want to print out the value you can simply do: 137 | ```cpp 138 | auto target = TargetType::ALLY_CORPSE; 139 | std::cout << "We got this target: " << target; 140 | // prints: We got this target: ALLY_CORPSE 141 | ``` 142 | 143 | This is how you obtain an enum value from its string representation: 144 | 145 | ```cpp 146 | TargetType target = EnumeratorSerializer::get_value("ALLY_CORPSE"); 147 | if (target == TargetType::ALLY_CORPSE) 148 | std::cout << "We got an ally's corpse!"; 149 | // prints: We got an ally's corpse! 150 | ``` 151 | 152 | ### Forward Declaration 153 | 154 | Another useful feature of the library is allowing forward declaring the Mask type for `enum classes`, for instance: 155 | 156 | ```cpp 157 | enum class TargetType; 158 | using TargetTypeMask = EnumeratorMask; 159 | ``` 160 | 161 | This makes it possible to specify `TargetTypeMask` as an argument type in function declarations, and then only `#include` the `enum class` definition in the `.cpp` file that contains the function body. This means adding entries to the enum class will require less re-compilation. 162 | 163 | ### Arbitrarily Large Masks 164 | 165 | The usual way to use enums for flags in a mask in C++ is to define the enum so that each value in it is a power of 2. In addition to the issue of making the masks not type safe, this also limits how large the mask can be to whatever built-in integer type is largest. In other words, assuming your code has to be cross-platform, you will be limited to a maximum of 64 flags. 166 | 167 | The Mask type instead has no such limitation as it can store any amount of flags. The Mask will try to find the largest built-in type available to store the flags and if none is available it will use its own large integer representation. 168 | 169 | Keep in mind that of course, when using this large integer storage, performance will be lower than using a built-in type, because the operations have to be abstracted. This is however unavoidable because representing such values would always require a custom type. 170 | 171 | Also, because it's a goal of the implementation for all storage to be statically allocated, you have to specify how many flags, or bits of storage, you need for the mask by passing an extra parameter to the template, like below: 172 | 173 | ```cpp 174 | enum class TargetType; 175 | using TargetTypeMask = EnumeratorMask; // store up to 100 different flags in this mask 176 | ``` 177 | 178 | You also need to specify this when adding the meta information in the specialization: 179 | 180 | ```cpp 181 | #define METAENUMERATOR_NAMESPACE MyRootNamespace 182 | #include "MetaEnumerator.hpp" 183 | 184 | namespace MyRootNamespace 185 | { 186 | 187 | enum class TargetType 188 | { 189 | NONE = 0, 190 | ENEMY_ALIVE, 191 | ENEMY_CORPSE, 192 | ENEMY_SPOT, 193 | ALLY_ALIVE, 194 | ALLY_CORPSE, 195 | ALLY_SPOT, 196 | MAX = ALLY_SPOT 197 | }; 198 | 199 | template <> 200 | class EnumeratorMeta : public EnumeratorMetaDefault // up to 100 different values 201 | { 202 | public: 203 | static constexpr const bool math_operators = true; 204 | static constexpr const bool logic_operators = true; 205 | static constexpr const bool string_operators = true; 206 | static constexpr const TargetType MAX_VALUE = TargetType::MAX; 207 | 208 | static constexpr const EnumEntry enum_entries[]{ 209 | { TargetType::ENEMY_ALIVE, "ENEMY_ALIVE" }, 210 | { TargetType::ENEMY_CORPSE, "ENEMY_CORPSE" }, 211 | { TargetType::ENEMY_SPOT, "ENEMY_SPOT" }, 212 | { TargetType::ALLY_ALIVE, "ALLY_ALIVE" }, 213 | { TargetType::ALLY_CORPSE, "ALLY_CORPSE" }, 214 | { TargetType::ALLY_SPOT, "ALLY_SPOT" } 215 | }; 216 | }; 217 | 218 | } 219 | ``` 220 | 221 | This is one of the reasons you have to specify the `MAX_VALUE` constant in the specialization - this way, if you keep adding values to the enum and forget to make the mask large enough, the system can check (at compile time) and ensure that your maximum value can fit inside the mask. 222 | 223 | There's no limit to the amount of flags, other than of course the available RAM. If you request 100 bits, that would require 13 bytes of memory, but right now storage is aligned to an integer so, the Mask will occupy 16 bytes of memory. 224 | 225 | 226 | ### Inheritance and Extension 227 | 228 | Inheriting from an enum requires either defining a specially named value in your parent enum or a special field in its `EnumeratorMeta` specialization. Then, in the child enum, you set the first value to the value returned by the `Inheritor` type from the `EnumeratorMeta` (which corresponds to `EnumeratorInheritor`). You can then cast any of the child enum values to the parent enum using the `unary +` operator. 229 | 230 | You can better see the requirements for this in the sample under [samples/extending.cpp](samples/extending.cpp) file. 231 | -------------------------------------------------------------------------------- /samples/basic.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #define METAENUMERATOR_NAMESPACE MyRootNamespace 3 | #include "MetaEnumerator.hpp" 4 | 5 | 6 | namespace MyRootNamespace 7 | { 8 | 9 | enum class TargetType 10 | { 11 | NONE = 0, 12 | ENEMY_ALIVE, 13 | ENEMY_CORPSE, 14 | ENEMY_SPOT, 15 | ALLY_ALIVE, 16 | ALLY_CORPSE, 17 | ALLY_SPOT, 18 | MAX = ALLY_SPOT 19 | }; 20 | 21 | template <> 22 | class EnumeratorMeta : public EnumeratorMetaDefault 23 | { 24 | public: 25 | static constexpr const bool logic_operators = true; 26 | static constexpr const bool string_operators = true; 27 | static constexpr const TargetType MAX_VALUE = TargetType::MAX; 28 | 29 | static constexpr const EnumEntry enum_entries[]{ 30 | { TargetType::ENEMY_ALIVE, "ENEMY_ALIVE" }, 31 | { TargetType::ENEMY_CORPSE, "ENEMY_CORPSE" }, 32 | { TargetType::ENEMY_SPOT, "ENEMY_SPOT" }, 33 | { TargetType::ALLY_ALIVE, "ALLY_ALIVE" }, 34 | { TargetType::ALLY_CORPSE, "ALLY_CORPSE" }, 35 | { TargetType::ALLY_SPOT, "ALLY_SPOT" } 36 | }; 37 | }; 38 | 39 | constexpr EnumeratorMeta::EnumEntry EnumeratorMeta::enum_entries[]; 40 | 41 | } 42 | 43 | 44 | int main(int argc, char *argv[]) 45 | { 46 | using namespace MyRootNamespace; 47 | 48 | { 49 | // creates a mask named accepted_targets 50 | auto accepted_targets = TargetType::ENEMY_ALIVE | TargetType::ALLY_ALIVE; 51 | std::cout << "Accepted Targets: " << accepted_targets << "\n"; 52 | // prints: Accepted Targets: ENEMY_ALIVE, ALLY_ALIVE 53 | } 54 | 55 | { 56 | // creates a mask named accepted_targets 57 | auto accepted_targets = TargetType::ENEMY_ALIVE | TargetType::ALLY_ALIVE; 58 | if (accepted_targets.has(TargetType::ENEMY_ALIVE)) 59 | std::cout << "Looking for a live enemy...\n"; 60 | // prints: Looking for a live enemy... 61 | } 62 | 63 | { 64 | // support for ostream operator<< 65 | auto target = TargetType::ALLY_CORPSE; 66 | std::cout << "We got this target: " << target; 67 | // prints: We got this target: ALLY_CORPSE 68 | } 69 | 70 | std::cout << "\n"; 71 | } -------------------------------------------------------------------------------- /samples/extending.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #define METAENUMERATOR_NAMESPACE MyRootNamespace 3 | #include "MetaEnumerator.hpp" 4 | 5 | 6 | namespace MyRootNamespace 7 | { 8 | 9 | // Forward declare enum types and a function which uses them (could be in a separate header) 10 | enum class DocumentType; 11 | using DocumentTypeMask = EnumeratorMask; 12 | 13 | bool support_document_type(DocumentTypeMask documentTypes, DocumentType documentType); 14 | 15 | 16 | enum class DocumentType 17 | { 18 | NONE = 0, 19 | TEXT, 20 | IMAGE, 21 | WAVE, 22 | INHERITANCE, 23 | EXTENSION = 128, // reserve the first 127 values to built-in and inherited 24 | MAX = 255 // support at maximum 255 document types 25 | }; 26 | 27 | template <> 28 | class EnumeratorMeta : public EnumeratorMetaDefault 29 | { 30 | public: 31 | static constexpr const bool logic_operators = true; 32 | static constexpr const bool string_operators = true; 33 | static constexpr const DocumentType MAX_VALUE = DocumentType::MAX; 34 | 35 | static constexpr const EnumEntry enum_entries[]{ 36 | { DocumentType::TEXT, "TEXT" }, 37 | { DocumentType::IMAGE, "IMAGE" }, 38 | { DocumentType::WAVE, "WAVE" } 39 | }; 40 | }; 41 | 42 | constexpr EnumeratorMeta::EnumEntry EnumeratorMeta::enum_entries[]; 43 | 44 | 45 | bool support_document_type(DocumentTypeMask documentTypes, DocumentType documentType) 46 | { 47 | return documentTypes.has(documentType); 48 | } 49 | 50 | 51 | enum class DocumentType_Extended 52 | { 53 | NONE = 0, 54 | RICHTEXT = EnumeratorMeta::Inheritor::inherit(), 55 | SPREADSHEET, 56 | VIDEO, 57 | MAX = EnumeratorMeta::Inheritor::inheritMaximum() 58 | }; 59 | 60 | template <> 61 | class EnumeratorMeta : public EnumeratorMetaDefault 62 | { 63 | public: 64 | using BaseEnumType = DocumentType; 65 | using EnumEntry = EnumeratorMeta::EnumEntry; 66 | 67 | static constexpr const bool logic_operators = true; 68 | static constexpr const bool string_operators = true; 69 | static constexpr const DocumentType_Extended MAX_VALUE = DocumentType_Extended::MAX; 70 | 71 | static constexpr const EnumEntry enum_entries[]{ 72 | { +DocumentType_Extended::RICHTEXT, "RICHTEXT" }, 73 | { +DocumentType_Extended::SPREADSHEET, "SPREADSHEET" }, 74 | { +DocumentType_Extended::VIDEO, "VIDEO" } 75 | }; 76 | }; 77 | 78 | template <> 79 | class EnumeratorInherited 80 | { 81 | public: 82 | using InheritedType = DocumentType_Extended; 83 | }; 84 | 85 | constexpr EnumeratorMeta::EnumEntry EnumeratorMeta::enum_entries[]; 86 | 87 | } 88 | 89 | 90 | int main(int argc, char *argv[]) 91 | { 92 | using namespace MyRootNamespace; 93 | 94 | DocumentTypeMask textTypes = DocumentType::TEXT | +DocumentType_Extended::RICHTEXT; 95 | 96 | std::cout << "Support for " << DocumentType_Extended::VIDEO << " document enabled: " << support_document_type(textTypes, +DocumentType_Extended::VIDEO) << "\n"; 97 | // prints: Support for VIDEO document enabled: 0 98 | 99 | std::cout << "Support for " << DocumentType_Extended::RICHTEXT << " document enabled: " << support_document_type(textTypes, +DocumentType_Extended::RICHTEXT) << "\n"; 100 | // prints: Support for VIDEO document enabled: 1 101 | 102 | // Extending the enum at run-time, to record dynamic document types (for instance, added by plugins or external modules) 103 | using Extender = EnumeratorMeta::Extender; 104 | auto documentType1 = Extender::extend("documentType1"); 105 | auto documentType2 = Extender::extend("documentType2"); 106 | DocumentTypeMask pluginTypes = documentType1 | documentType2; 107 | std::cout << "Is " << DocumentType_Extended::RICHTEXT << " registered by plugin: " << pluginTypes.has(+DocumentType_Extended::RICHTEXT) << "\n"; 108 | // prints: Is RICHTEXT registered by plugin: 0 109 | std::cout << "Is " << documentType1 << " registered by plugin: " << pluginTypes.has(documentType1) << "\n"; 110 | // prints: Is documentType1 registered by plugin: 1 111 | 112 | using Serializer = EnumeratorSerializer; 113 | std::cout << "Unserialized values " << (int)Serializer::get_value("documentType1") << ":" << Serializer::get_value("documentType1") << ", " << (int)Serializer::get_value("RICHTEXT") << ":" << Serializer::get_value("RICHTEXT") << ", " << (int)Serializer::get_value("WAVE") << ":" << Serializer::get_value("WAVE"); 114 | // prints: Unserialized values 128:documentType1, 4:RICHTEXT, 3:WAVE 115 | 116 | std::cout << "\n"; 117 | } --------------------------------------------------------------------------------