Требуются пояснения
От: vdimas Россия  
Дата: 06.12.24 21:56
Оценка: :)
Всем привет, коллега попросил спросить:
https://godbolt.org/z/xhP1b4cn6

  сырцы
#include <iostream>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <map>

using namespace std;

namespace umba {
namespace enum_helpers {

// template<typename Integer,         std::enable_if_t<std::is_integral<Integer>::value, bool> = true>
//     T(Integer) : type(int_t) {}

//----------------------------------------------------------------------------
//! Конвертирует enum в подлежащий тип. Версия для 'честных' enum'ов.
template< typename EnumType, typename std::enable_if< std::is_enum<EnumType>::value, bool>::type = true > inline
typename std::underlying_type< EnumType >::type toUnderlyingType( EnumType flagsVal )
{
    typedef typename std::underlying_type< EnumType >::type    EnumUnderlyingType;
    return (EnumUnderlyingType)flagsVal;
}

//----------------------------------------------------------------------------
//! Конвертирует enum в подлежащий тип. Версия для интегральных типов.
/*! Часто неохота разбираться, является ли значение int'ом, unsigned'ом, или другим интегральным типом,
    или же является enum'ом.
 */
template< typename EnumType, typename std::enable_if< (!std::is_enum<EnumType>::value
                                                    &&  std::is_integral<EnumType>::value
                                                      )
                                                    , bool>::type = true
                                                    > inline
EnumType toUnderlyingType( EnumType flagsVal )
{
    return flagsVal;
}

//----------------------------------------------------------------------------
//! Конвертирует в enum из подлежащего типа. Версия для 'честных' enum'ов.
template< typename EnumType, typename std::enable_if< std::is_enum<EnumType>::value, bool
                                                    >::type = true > inline
EnumType fromUnderlyingType( typename std::underlying_type< EnumType >::type flagsVal )
{
    return (EnumType)flagsVal;
}

//----------------------------------------------------------------------------
//! Конвертирует в enum из подлежащего типа. Версия для интегральных типов.
/*! Часто неохота разбираться, является ли значение int'ом, unsigned'ом, или другим интегральным типом,
    или же является enum'ом.
 */
template< typename EnumType, typename std::enable_if< (!std::is_enum<EnumType>::value 
                                                    &&  std::is_integral< EnumType >::value
                                                      )
                                                    , bool>::type = true 
                                                    > inline
EnumType fromUnderlyingType( EnumType flagsVal )
{
    return flagsVal;
}

//----------------------------------------------------------------------------
template< typename EnumType > inline
bool enumLessImpl(EnumType e1, EnumType e2)
{
    typedef typename std::underlying_type< EnumType >::type    EnumUnderlyingType;
    return (EnumUnderlyingType)e1 < (EnumUnderlyingType)e2;
}

//----------------------------------------------------------------------------
template< typename EnumType > inline
bool enumLessEqualImpl(EnumType e1, EnumType e2)
{
    typedef typename std::underlying_type< EnumType >::type    EnumUnderlyingType;
    return (EnumUnderlyingType)e1 <= (EnumUnderlyingType)e2;
}

//----------------------------------------------------------------------------
template< typename EnumType > inline
bool enumGreaterImpl(EnumType e1, EnumType e2)
{
    typedef typename std::underlying_type< EnumType >::type    EnumUnderlyingType;
    return (EnumUnderlyingType)e1 > (EnumUnderlyingType)e2;
}

//----------------------------------------------------------------------------
template< typename EnumType > inline
bool enumGreaterEqualImpl(EnumType e1, EnumType e2)
{
    typedef typename std::underlying_type< EnumType >::type    EnumUnderlyingType;
    return (EnumUnderlyingType)e1 >= (EnumUnderlyingType)e2;
}


} // namespace enum_helpers
} // namespace umba

//----------------------------------------------------------------------------



//----------------------------------------------------------------------------
#define UMBA_ENUM_CLASS_IMPLEMENT_ENUM_UNARY_OPERATOR_IMPL(EnumType, operatorSign)                              \
            inline                                                                                              \
            EnumType operator operatorSign( EnumType e )                                                       \
            {                                                                                                   \
                return umba::enum_helpers::fromUnderlyingType<EnumType>(                                        \
                                           operatorSign                                                         \
                                           umba::enum_helpers::toUnderlyingType<EnumType>(e)                    \
                );                                                                                              \
            }

//------------------------------
#define UMBA_ENUM_CLASS_IMPLEMENT_ENUM_BINARY_OPERATOR_IMPL(EnumType, operatorSign)                             \
            inline                                                                                              \
            EnumType operator operatorSign( EnumType e1, EnumType e2)                                          \
            {                                                                                                   \
                return umba::enum_helpers::fromUnderlyingType<EnumType>(                                        \
                                           umba::enum_helpers::toUnderlyingType<EnumType>(e1)                   \
                                           operatorSign                                                         \
                                           umba::enum_helpers::toUnderlyingType<EnumType>(e2)                   \
                );                                                                                              \
            }                                                                                                   \
            inline                                                                                              \
            EnumType& operator operatorSign##=( EnumType &e1, EnumType e2)                                     \
            {                                                                                                   \
                e1 = e1 operatorSign e2;                                                                        \
                return e1;                                                                                      \
            }

//------------------------------
#define UMBA_ENUM_CLASS_IMPLEMENT_ENUM_UNDERLYING_TYPE_BINARY_OPERATOR_IMPL(EnumType, operatorSign)             \
            inline                                                                                              \
            EnumType operator operatorSign( EnumType e1, typename std::underlying_type< EnumType >::type e2)   \
            {                                                                                                   \
                return umba::enum_helpers::fromUnderlyingType<EnumType>(                                        \
                                           umba::enum_helpers::toUnderlyingType<EnumType>(e1)                   \
                                           operatorSign                                                         \
                                           e2                                                                   \
                );                                                                                              \
            }                                                                                                   \
            inline                                                                                              \
            EnumType operator operatorSign( typename std::underlying_type< EnumType >::type e1, EnumType e2)   \
            {                                                                                                   \
                return umba::enum_helpers::fromUnderlyingType<EnumType>(                                        \
                                           e1                                                                   \
                                           operatorSign                                                         \
                                           umba::enum_helpers::toUnderlyingType<EnumType>(e2)                   \
                );                                                                                              \
            }                                                                                                   \
            inline                                                                                              \
            EnumType& operator operatorSign##=( EnumType &e1, typename std::underlying_type< EnumType >::type e2) \
            {                                                                                                   \
                e1 = e1 operatorSign e2;                                                                        \
                return e1;                                                                                      \
            }

//------------------------------
#define UMBA_ENUM_CLASS_IMPLEMENT_ENUM_SHIFT_OPERATOR_IMPL(EnumType, operatorSign)                              \
            inline                                                                                              \
            EnumType operator operatorSign( EnumType e, unsigned sh )                                          \
            {                                                                                                   \
                return umba::enum_helpers::fromUnderlyingType<EnumType>(                                        \
                                           umba::enum_helpers::toUnderlyingType<EnumType>(e)                    \
                                           operatorSign                                                         \
                                           sh                                                                   \
                );                                                                                              \
            }                                                                                                   \
            inline                                                                                              \
            EnumType& operator operatorSign##=( EnumType &e, unsigned sh )                                     \
            {                                                                                                   \
                e = e operatorSign sh;                                                                          \
                return e;                                                                                       \
            }

//----------------------------------------------------------------------------




//----------------------------------------------------------------------------
//! Реализует битовые операции для enum-типа
#define UMBA_ENUM_CLASS_IMPLEMENT_BIT_OPERATORS( EnumType )                                                     \
             UMBA_ENUM_CLASS_IMPLEMENT_ENUM_UNARY_OPERATOR_IMPL (EnumType,~)                                    \
             UMBA_ENUM_CLASS_IMPLEMENT_ENUM_BINARY_OPERATOR_IMPL(EnumType,|)                                    \
             UMBA_ENUM_CLASS_IMPLEMENT_ENUM_BINARY_OPERATOR_IMPL(EnumType,&)                                    \
             UMBA_ENUM_CLASS_IMPLEMENT_ENUM_BINARY_OPERATOR_IMPL(EnumType,^)                                    \
             UMBA_ENUM_CLASS_IMPLEMENT_ENUM_SHIFT_OPERATOR_IMPL (EnumType,<<)                                   \
             UMBA_ENUM_CLASS_IMPLEMENT_ENUM_SHIFT_OPERATOR_IMPL (EnumType,>>)


//----------------------------------------------------------------------------
//! Реализует битовые операции для enum-типа и подлежащего типа
#define UMBA_ENUM_CLASS_IMPLEMENT_UNDERLYING_TYPE_BIT_OPERATORS( EnumType )                                      \
             UMBA_ENUM_CLASS_IMPLEMENT_ENUM_UNDERLYING_TYPE_BINARY_OPERATOR_IMPL(EnumType,|)                     \
             UMBA_ENUM_CLASS_IMPLEMENT_ENUM_UNDERLYING_TYPE_BINARY_OPERATOR_IMPL(EnumType,&)                     \
             UMBA_ENUM_CLASS_IMPLEMENT_ENUM_UNDERLYING_TYPE_BINARY_OPERATOR_IMPL(EnumType,^)


//----------------------------------------------------------------------------
//! Реализует арифметические операции для enum-типа
#define UMBA_ENUM_CLASS_IMPLEMENT_ARITHMETIC_OPERATORS( EnumType )                                              \
             UMBA_ENUM_CLASS_IMPLEMENT_ENUM_UNARY_OPERATOR_IMPL (EnumType,+)                                    \
             UMBA_ENUM_CLASS_IMPLEMENT_ENUM_UNARY_OPERATOR_IMPL (EnumType,-)                                    \
             UMBA_ENUM_CLASS_IMPLEMENT_ENUM_BINARY_OPERATOR_IMPL(EnumType,+)                                    \
             UMBA_ENUM_CLASS_IMPLEMENT_ENUM_BINARY_OPERATOR_IMPL(EnumType,-)                                    \
             UMBA_ENUM_CLASS_IMPLEMENT_ENUM_BINARY_OPERATOR_IMPL(EnumType,*)                                    \
             UMBA_ENUM_CLASS_IMPLEMENT_ENUM_BINARY_OPERATOR_IMPL(EnumType,%)                                    \
             UMBA_ENUM_CLASS_IMPLEMENT_ENUM_BINARY_OPERATOR_IMPL(EnumType,/)


//----------------------------------------------------------------------------
//! Реализует арифметические операции для enum-типа и подлежащего типа
#define UMBA_ENUM_CLASS_IMPLEMENT_UNDERLYING_TYPE_ARITHMETIC_OPERATORS( EnumType )                               \
             UMBA_ENUM_CLASS_IMPLEMENT_ENUM_UNDERLYING_TYPE_BINARY_OPERATOR_IMPL(EnumType,+)                     \
             UMBA_ENUM_CLASS_IMPLEMENT_ENUM_UNDERLYING_TYPE_BINARY_OPERATOR_IMPL(EnumType,-)                     \
             UMBA_ENUM_CLASS_IMPLEMENT_ENUM_UNDERLYING_TYPE_BINARY_OPERATOR_IMPL(EnumType,*)                     \
             UMBA_ENUM_CLASS_IMPLEMENT_ENUM_UNDERLYING_TYPE_BINARY_OPERATOR_IMPL(EnumType,/)                     \
             UMBA_ENUM_CLASS_IMPLEMENT_ENUM_UNDERLYING_TYPE_BINARY_OPERATOR_IMPL(EnumType,%)


//----------------------------------------------------------------------------
//! Реализует операции сравнения больше/меньше для enum-типа
#define UMBA_ENUM_CLASS_IMPLEMENT_RELATION_OPERATORS( EnumType )                                                            \
                                                                                                                            \
             inline bool operator< (EnumType e1, EnumType e2) { return umba::enum_helpers::enumLessImpl        (e1, e2); }  \
             inline bool operator<=(EnumType e1, EnumType e2) { return umba::enum_helpers::enumLessEqualImpl   (e1, e2); }  \
             inline bool operator> (EnumType e1, EnumType e2) { return umba::enum_helpers::enumGreaterImpl     (e1, e2); }  \
             inline bool operator>=(EnumType e1, EnumType e2) { return umba::enum_helpers::enumGreaterEqualImpl(e1, e2); }


//----------------------------------------------------------------------------
//! Реализует операции сравнения больше/меньше для enum-типа и подлежащего типа
#define UMBA_ENUM_CLASS_IMPLEMENT_UNDERLYING_TYPE_RELATION_OPERATORS( EnumType )                                             \
                                                                                                                            \
             inline bool operator< (EnumType e1, typename std::underlying_type< EnumType >::type e2) { return umba::enum_helpers::enumLessImpl        (e1, (EnumType)e2); } \
             inline bool operator<=(EnumType e1, typename std::underlying_type< EnumType >::type e2) { return umba::enum_helpers::enumLessEqualImpl   (e1, (EnumType)e2); } \
             inline bool operator> (EnumType e1, typename std::underlying_type< EnumType >::type e2) { return umba::enum_helpers::enumGreaterImpl     (e1, (EnumType)e2); } \
             inline bool operator>=(EnumType e1, typename std::underlying_type< EnumType >::type e2) { return umba::enum_helpers::enumGreaterEqualImpl(e1, (EnumType)e2); } \
                                                                                                                            \
             inline bool operator< (typename std::underlying_type< EnumType >::type e1, EnumType e2) { return umba::enum_helpers::enumLessImpl        ((EnumType)e1, e2); } \
             inline bool operator<=(typename std::underlying_type< EnumType >::type e1, EnumType e2) { return umba::enum_helpers::enumLessEqualImpl   ((EnumType)e1, e2); } \
             inline bool operator> (typename std::underlying_type< EnumType >::type e1, EnumType e2) { return umba::enum_helpers::enumGreaterImpl     ((EnumType)e1, e2); } \
             inline bool operator>=(typename std::underlying_type< EnumType >::type e1, EnumType e2) { return umba::enum_helpers::enumGreaterEqualImpl((EnumType)e1, e2); }


//----------------------------------------------------------------------------
#define UMBA_ENUM_CLASS_IMPLEMENT_UNDERLYING_TYPE_EQUAL_OPERATORS( EnumType )                                   \
                                                                                                                \
             template<typename EnumType, typename IntType> inline                                               \
             bool operator==(EnumType e, IntType i)                                                             \
             {                                                                                                  \
                 return umba::enum_helpers::toUnderlyingType(e)==umba::enum_helpers::toUnderlyingType(i);       \
             }                                                                                                  \
                                                                                                                \
             template<typename EnumType, typename IntType> inline                                               \
             bool operator!=(EnumType e, IntType i)                                                             \
             {                                                                                                  \
                 return umba::enum_helpers::toUnderlyingType(e)!=umba::enum_helpers::toUnderlyingType(i);       \
             }                                                                                                  \
                                                                                                                \
             inline                                                                                             \
             bool operator!(EnumType e)                                                                         \
             {                                                                                                  \
                 return e==0;                                                                                   \
             }

/*
             template<typename EnumType, typename IntType> inline                                               \
             bool operator==(IntType i, EnumType e)                                                             \
             {                                                                                                  \
                 return umba::enum_helpers::toUnderlyingType(e)==umba::enum_helpers::toUnderlyingType(i);       \
             }                                                                                                  \
                                                                                                                \
             template<typename EnumType, typename IntType> inline                                               \
             bool operator!=(IntType i, EnumType e)                                                             \
             {                                                                                                  \
                 return umba::enum_helpers::toUnderlyingType(e)!=umba::enum_helpers::toUnderlyingType(i);       \
             }                                                                                                  \

*/




enum EEE
{
    zero,
    one,
    two

};

enum class CEEE
{
    zero,
    one,
    two

};

enum class MoveFileFlags
{
    copyAllowed     = 1, //!< If the file is to be moved to a different volume, the function simulates the move by using the CopyFile and DeleteFile functions.
    replaceExisting = 2, //!< If a file named lpNewFileName exists, the function replaces its contents with the contents of the lpExistingFileName file
    overwrite       = 2, //!< Same as replaceExisting
    writeThrough    = 4  //!< The function does not return until the file is actually moved on the disk. Setting this value guarantees that a move performed as a copy and delete operation is flushed to disk before the function returns. The flush occurs at the end of the copy operation.
};


UMBA_ENUM_CLASS_IMPLEMENT_BIT_OPERATORS(MoveFileFlags)
UMBA_ENUM_CLASS_IMPLEMENT_UNDERLYING_TYPE_BIT_OPERATORS(MoveFileFlags)
UMBA_ENUM_CLASS_IMPLEMENT_UNDERLYING_TYPE_EQUAL_OPERATORS(MoveFileFlags)

inline
bool isKindOfName(const std::unordered_set<std::string>& s, const std::string &name)
{
// строка 321     //return s.find(name)!=s.end();
    return true;
}



int main()
{
    using namespace umba::enum_helpers;
    cout << "val of EEE::one: " << toUnderlyingType(one) << "\n";
    cout << "val of EEE::two: " << toUnderlyingType(two) << "\n";

    cout << "val of CEEE::one: " << toUnderlyingType(CEEE::one) << "\n";
    cout << "val of CEEE::two: " << toUnderlyingType(CEEE::two) << "\n";

    cout << "val of 1: " << toUnderlyingType(1) << "\n";
    cout << "val of 2: " << toUnderlyingType(2) << "\n";

    cout << "val of copyAllowed    : " << toUnderlyingType(MoveFileFlags::copyAllowed) << "\n";
    cout << "val of replaceExisting: " << toUnderlyingType(MoveFileFlags::replaceExisting) << "\n";

    cout << "MoveFileFlags::replaceExisting==1: " << (MoveFileFlags::replaceExisting==1 ? "true" : "false") << "\n";

    MoveFileFlags flags = MoveFileFlags::replaceExisting;

}


строка 321 — работает
если её раскоментировать, и закоментировать следующую — гцц не компилит

После поверхностного вникания я предложил добавить недостающий вариант

template< typename EnumType, typename std::enable_if< (!std::is_enum<EnumType>::value
                                                    &&  !std::is_integral<EnumType>::value
                                                      )
                                                    , bool>::type = true
                                                    > inline
int toUnderlyingType( EnumType flagsVal )
{
    return 0;
}


И оно ожидаемо сработало.
код подцепляет этот вариант, можно добавить в его тело static_assert
template< typename EnumType, typename std::enable_if< (!std::is_enum<EnumType>::value
                                                    &&  !std::is_integral<EnumType>::value
                                                      )
                                                    , bool>::type bb = false
                                                    > inline
int toUnderlyingType( EnumType flagsVal )
{
    static_assert(bb);
    return 0;
}


Подцепляет, ИМХО, из этих строчек
template<typename EnumType, typename IntType> inline                                               \            
bool operator==(EnumType e, IntType i)
Отредактировано 06.12.2024 22:27 vdimas . Предыдущая версия . Еще …
Отредактировано 06.12.2024 22:17 vdimas . Предыдущая версия .
Отредактировано 06.12.2024 22:16 vdimas . Предыдущая версия .
Re: Требуются пояснения
От: Великий Реверс google
Дата: 06.12.24 22:38
Оценка: +1
а что пояснять?
он глобально перегрузил
template<typename EnumType, typename IntType> inline                                               \
bool operator!=(EnumType e, IntType i)

где IntType у него свободный на выбор
вон и матчиться на !=

return s.find(name)!=s.end();

Re: Требуются пояснения
От: andrey.desman  
Дата: 06.12.24 22:44
Оценка: +1 -1 :)
Здравствуйте, vdimas, Вы писали:

V>Подцепляет, ИМХО, из этих строчек

V>
V>template<typename EnumType, typename IntType> inline                                               \            
V>bool operator==(EnumType e, IntType i)
V>


Ну да, оператор сравнения для любых произвольных типов. Зачем тут шаблон если он в макрос тип передает? Сделать как и остальные без шаблона.
Вообще, страшная штука. Лучше не надо.
Re: Требуются пояснения
От: cserg  
Дата: 06.12.24 22:50
Оценка: +2
Здравствуйте, vdimas, Вы писали:

V>Подцепляет, ИМХО, из этих строчек

V>
V>template<typename EnumType, typename IntType> inline                                               \            
V>bool operator==(EnumType e, IntType i)
V>

EnumType параметр макроса. Зачем нужен EnumType в параметрах шаблона?
Отредактировано 06.12.2024 23:12 cserg . Предыдущая версия . Еще …
Отредактировано 06.12.2024 22:55 cserg . Предыдущая версия .
Re[2]: Требуются пояснения
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 08.12.24 16:01
Оценка:
Здравствуйте, andrey.desman, Вы писали:

AD>Ну да, оператор сравнения для любых произвольных типов. Зачем тут шаблон если он в макрос тип передает? Сделать как и остальные без шаблона.


Да, это явно тупой проджоб


AD>Вообще, страшная штука. Лучше не надо.


Что именно не так с кодом? Ну, если считать, что шаблонный тип EnumType — это проджоб?


ЗЫ Политексный и сюда пробрался минусики ставить. Ты недавно в политике ничего не писал?
Маньяк Робокряк колесит по городу
Отредактировано 08.12.2024 16:03 Marty . Предыдущая версия .
Re[3]: Требуются пояснения
От: vdimas Россия  
Дата: 08.12.24 16:36
Оценка:
Здравствуйте, Marty, Вы писали:

AD>>Вообще, страшная штука. Лучше не надо.

M>Что именно не так с кодом?

Всё так. Иногда типизированные операторы для енумов банально удобны, как оно есть в шарпе, к примеру.
Re[3]: Требуются пояснения
От: andrey.desman  
Дата: 08.12.24 16:41
Оценка: -1 :))
Здравствуйте, Marty, Вы писали:

AD>>Вообще, страшная штука. Лучше не надо.

M>Что именно не так с кодом? Ну, если считать, что шаблонный тип EnumType — это проджоб?

Было похожее в твоей теме. Не нравится, что макросы и глобальное пространство.
Предпочитаю явный враппер
Автор: andrey.desman
Дата: 05.10.22
.

M>ЗЫ Политексный и сюда пробрался минусики ставить. Ты недавно в политике ничего не писал?


Да, так и есть.
Re[2]: Требуются пояснения
От: vdimas Россия  
Дата: 08.12.24 20:26
Оценка:
Здравствуйте, Великий Реверс, Вы писали:

ВР>а что пояснять?

ВР>он глобально перегрузил
ВР>
ВР>template<typename EnumType, typename IntType> inline                                               \
ВР>bool operator!=(EnumType e, IntType i)   
ВР>

ВР>где IntType у него свободный на выбор
ВР>вон и матчиться на !=
ВР>

ВР>return s.find(name)!=s.end();


Да, тоже указал на такой способ переопределения операторов...
Re: Требуются пояснения
От: B0FEE664  
Дата: 09.12.24 14:39
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Всем привет, коллега попросил спросить:

V>https://godbolt.org/z/xhP1b4cn6

перечисление — это не набор констант, это не набор флагов, перечисление — это набор идентификаторов. Если это понять, то программировать станет намного легче:
namespace EEEconstants
{
  inline constexpr int zero = 0;
  inline constexpr int one  = 1;
  inline constexpr int two  = 2;
};

struct CEEEconstant
{
  inline constexpr static int zero = 0;
  inline constexpr static int one  = 1;
  inline constexpr static int two  = 2; 

  inline constexpr static std::array all = {zero, one, two};
};

static_assert(EEEconstants::zero == CEEEconstant::zero);
static_assert(EEEconstants::one < CEEEconstant::two);

struct MoveFileFlags_
{
  inline constexpr static unsigned int copyAllowed     = 1;
  inline constexpr static unsigned int replaceExisting = 2;
  inline constexpr static unsigned int overwrite       = 2; //!< Same as replaceExisting
  inline constexpr static unsigned int writeThrough    = 4;

  inline constexpr static unsigned int validMask       = copyAllowed | replaceExisting | overwrite | writeThrough;
};
И каждый день — без права на ошибку...
Re[2]: Требуются пояснения
От: vdimas Россия  
Дата: 11.12.24 00:20
Оценка: :)
Здравствуйте, B0FEE664, Вы писали:

BFE>перечисление — это не набор констант, это не набор флагов, перечисление — это набор идентификаторов.


Составляющих отдельную семантическую группу — перечислимый тип.


BFE>Если это понять, то программировать станет намного легче:


У тебя получилось наоборот — проще совершать ошибки, т.к. можно запросто перепутать константы из разных групп. ))

Отказываться от типизации в языках, эту типизацию предоставляющую — такое себе...
Отредактировано 11.12.2024 0:52 vdimas . Предыдущая версия .
Re[3]: Требуются пояснения
От: B0FEE664  
Дата: 11.12.24 10:05
Оценка:
Здравствуйте, vdimas, Вы писали:

BFE>>перечисление — это не набор констант, это не набор флагов, перечисление — это набор идентификаторов.

V>Составляющих отдельную семантическую группу — перечислимый тип.
Рассмотрим структуру struct ABC { int a, b, c; }. В этой структуре перечислены поля a, b, c. Составляет ли поля struct ABC отдельную семантическую группу? Думаю — да. Означает ли это, что struct ABC — это перечислимый тип?

V>У тебя получилось наоборот — проще совершать ошибки, т.к. можно запросто перепутать константы из разных групп. ))

V>Отказываться от типизации в языках, эту типизацию предоставляющую — такое себе...
Я так понял, что это и есть цель обсуждаемого кода:
UMBA_ENUM_CLASS_IMPLEMENT_UNDERLYING_TYPE_RELATION_OPERATORS(CEEE)
CEEE::zero < EEE::one;




PS А вы в курсе про потенциальное теоретическое UB при выходе за пределы диапазона значений enum class?
И каждый день — без права на ошибку...
Re[4]: Требуются пояснения
От: rg45 СССР  
Дата: 11.12.24 14:19
Оценка: +1
Здравствуйте, B0FEE664, Вы писали:

BFE>
BFE>UMBA_ENUM_CLASS_IMPLEMENT_UNDERLYING_TYPE_RELATION_OPERATORS(CEEE)
BFE>CEEE::zero < EEE::one;
BFE>

BFE>

Ну так у него это задано явно и намеренно. А в твоем подходе элементы разных перечислений сходу являются величинами совместимых типов, над которыми можно выполнять любые арифметические операции. Теряется типовая надежность.


BFE>PS А вы в курсе про потенциальное теоретическое UB при выходе за пределы диапазона значений enum class?


А эту проблему можно отправить отдыхать, явным заданием подходящего underlying type. А если к тому же в качестве допустимых операций рассматриваются только сравнение и битовые операции, то переполнению и вовсе неоткуда взяться.
--
Справедливость выше закона. А человечность выше справедливости.
Re[4]: Требуются пояснения
От: vdimas Россия  
Дата: 11.12.24 14:42
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>>>перечисление — это не набор констант, это не набор флагов, перечисление — это набор идентификаторов.

V>>Составляющих отдельную семантическую группу — перечислимый тип.
BFE>Рассмотрим структуру struct ABC { int a, b, c; }. В этой структуре перечислены поля a, b, c. Составляет ли поля struct ABC отдельную семантическую группу? Думаю — да. Означает ли это, что struct ABC — это перечислимый тип?

Типы образуются не только перечислением.


V>>У тебя получилось наоборот — проще совершать ошибки, т.к. можно запросто перепутать константы из разных групп. ))

V>>Отказываться от типизации в языках, эту типизацию предоставляющую — такое себе...
BFE>Я так понял, что это и есть цель обсуждаемого кода:
BFE>
BFE>UMBA_ENUM_CLASS_IMPLEMENT_UNDERLYING_TYPE_RELATION_OPERATORS(CEEE)
BFE>CEEE::zero < EEE::one;
BFE>

BFE>

Не так.
Там битовые и арифметические унарные и бинарные операторы, которые сохраняют тип енумов.

А доп.операторы сравнения определены для енума и целых, имеющих тип underlying type.


BFE>PS А вы в курсе про потенциальное теоретическое UB при выходе за пределы диапазона значений enum class?


Однако, в случае битовых флагов этой практике столько же лет, сколько языку Си — ведь перечисляются отдельные флаги, а не все их возможные комбинаторные варианты.

Плюс еще есть трюк по использованию енумов как различимых (т.е. несовместимых) по типам целочисленных значений. Такой енум может не содержать при объявлении значений вовсе.

Ну и, в показанном коде макры независимы, т.е. для одних энумов можно определить только битовые операторы, для других только арифметические и т.д.
Re[5]: Требуются пояснения
От: B0FEE664  
Дата: 11.12.24 17:23
Оценка:
Здравствуйте, rg45, Вы писали:

BFE>>
BFE>>UMBA_ENUM_CLASS_IMPLEMENT_UNDERLYING_TYPE_RELATION_OPERATORS(CEEE)
BFE>>CEEE::zero < EEE::one;
BFE>>

BFE>>

R>Ну так у него это задано явно и намеренно. А в твоем подходе элементы разных перечислений сходу являются величинами совместимых типов, над которыми можно выполнять любые арифметические операции. Теряется типовая надежность.


Нет при таком подходе никакой типовой надёжности! Вот такое скомпилируется:
UMBA_ENUM_CLASS_IMPLEMENT_UNDERLYING_TYPE_ARITHMETIC_OPERATORS(CEEE)
    CEEE x = CEEE::two + 1;

при том, что в CEEE:
enum class CEEE
{
    zero,
    one,
    two
};

нет значения three. Значит в x лежит невалидное значение. (Помимо формального UB см. ниже) это означает, что в коде нигде нельзя положиться на пришедшее значение CEEE. CEEE перестало отличаться от int. Считать, что x имеет тип CEEE — это обманывать себя и читателя.

BFE>>PS А вы в курсе про потенциальное теоретическое UB при выходе за пределы диапазона значений enum class?

R>А эту проблему можно отправить отдыхать, явным заданием подходящего underlying type. А если к тому же в качестве допустимых операций рассматриваются только сравнение и битовые операции, то переполнению и вовсе неоткуда взяться.
Ээээ.... Вообще-то речь не про переполнение.
Если бы CEEE был объявлен как enum class CEEE : int... то тогда — да, UB нет, а вот без задания базового типа компилятор может полагаться на то, что есть только три значения. Да, я понимаю, что сами писатели стандарта пишут одно, а подразумевают другое, но формально это неопределённое поведение.
И каждый день — без права на ошибку...
Re[5]: Требуются пояснения
От: B0FEE664  
Дата: 11.12.24 17:54
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Там битовые и арифметические унарные и бинарные операторы, которые сохраняют тип енумов.

Нет, не сохраняют. В перечислении есть только те имена и значения, которые в нём заданы, а в результате этого кода перечисления становятся неотличимы от целых типов.

V>А доп.операторы сравнения определены для енума и целых, имеющих тип underlying type.

Не представляю зачем это может понадобиться.

BFE>>PS А вы в курсе про потенциальное теоретическое UB при выходе за пределы диапазона значений enum class?

V>Однако, в случае битовых флагов этой практике столько же лет, сколько языку Си — ведь перечисляются отдельные флаги, а не все их возможные комбинаторные варианты.
Да, я давно призываю отказаться от этой порочной практики.

V>Плюс еще есть трюк по использованию енумов как различимых (т.е. несовместимых) по типам целочисленных значений. Такой енум может не содержать при объявлении значений вовсе.

Ну, да. Как std::byte. Кто-то им пользуется?

V>Ну и, в показанном коде макры независимы, т.е. для одних энумов можно определить только битовые операторы, для других только арифметические и т.д.

Определить-то можно, только это усложняет код неимоверно, если это делать как в вышеприведённом коде: при каждом изменении перечисления придётся проверять глазами все использования переменных этого типа.
Но главное даже не это. Главное, что такие операции не имеют смысла. Это всё равно, что считать, что если к яблоку прибавить 1, то получится апельсин, а если два — то груша.
Если вам нужно использовать отдельный арифметический тип, ну так используйте его:
struct CEEEconstant
{
  inline constexpr static int zero = 0;
  inline constexpr static int one  = 1;
  inline constexpr static int two  = 2; 

  int value = zero;
};

Определите для него требуемые операции и будет понятно. А так, как в коде — это очень странно и нелогично.
И каждый день — без права на ошибку...
Re[6]: Требуются пояснения
От: rg45 СССР  
Дата: 11.12.24 18:00
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Нет при таком подходе никакой типовой надёжности! Вот такое скомпилируется:

BFE>
BFE>UMBA_ENUM_CLASS_IMPLEMENT_UNDERLYING_TYPE_ARITHMETIC_OPERATORS(CEEE)
BFE>    CEEE x = CEEE::two + 1;   
BFE>

BFE>при том, что в CEEE:
BFE>
BFE>enum class CEEE
BFE>{
BFE>    zero,
BFE>    one,
BFE>    two
BFE>};
BFE>


Что-то удивительное ты пишешь. Вот такое НЕ компилируется:

http://coliru.stacked-crooked.com/a/e56da567f0d45c7c

enum class CEEE
{
    zero,
    one,
    two
};

int main()
{
    const CEEE eee = CEEE::two + 1; // error: no match for 'operator+' (operand types are 'CEEE' and 'int')
}


В устройство макроса я не вникал, если что. Я исходил из предположения, что он реализован через enum class.
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 11.12.2024 19:45 rg45 . Предыдущая версия . Еще …
Отредактировано 11.12.2024 19:44 rg45 . Предыдущая версия .
Отредактировано 11.12.2024 18:05 rg45 . Предыдущая версия .
Отредактировано 11.12.2024 18:02 rg45 . Предыдущая версия .
Re[6]: Требуются пояснения
От: vdimas Россия  
Дата: 12.12.24 01:01
Оценка:
Здравствуйте, B0FEE664, Вы писали:

V>>Там битовые и арифметические унарные и бинарные операторы, которые сохраняют тип енумов.

BFE>Нет, не сохраняют. В перечислении есть только те имена и значения, которые в нём заданы, а в результате этого кода перечисления становятся неотличимы от целых типов.

Верно, в этом и цель, если ты про набор операций.

А если про типизацию, то разница большая, конечно.
Бери условную сигнатуру:
ResultEnum someOp(Flags f, Ops o, Mode m);

В случае простых целых ты не защищён от того, чтобы подать флаги в опции, а режимы во флаги.


V>>А доп.операторы сравнения определены для енума и целых, имеющих тип underlying type.

BFE>Не представляю зачем это может понадобиться.

Скорее всего, чтобы не вчитываться в такие сообщения компилятора:
<source>:15:31: error: no match for 'operator<' (operand types are 'SomeEnum' and 'int')
   15 |     bool r = SomeEnum::Value2 < 0;
      |              ~~~~~~~~~~~~~~~~ ^ ~
      |                        |        |
      |                        SomeEnum int



BFE>>>PS А вы в курсе про потенциальное теоретическое UB при выходе за пределы диапазона значений enum class?

V>>Однако, в случае битовых флагов этой практике столько же лет, сколько языку Си — ведь перечисляются отдельные флаги, а не все их возможные комбинаторные варианты.
BFE>Да, я давно призываю отказаться от этой порочной практики.

Рядом тебе уже сказали, что в битовых операциях над флагами одного енума не может быть переполнения даже теоретически.


V>>Плюс еще есть трюк по использованию енумов как различимых (т.е. несовместимых) по типам целочисленных значений. Такой енум может не содержать при объявлении значений вовсе.

BFE>Ну, да. Как std::byte. Кто-то им пользуется?

Таким трюком я иногда пользуюсь.
Это примерно как в первом сниппете, чтобы не оперировать "бестиповыми" интами.


V>>Ну и, в показанном коде макры независимы, т.е. для одних энумов можно определить только битовые операторы, для других только арифметические и т.д.

BFE>Определить-то можно, только это усложняет код неимоверно, если это делать как в вышеприведённом коде: при каждом изменении перечисления придётся проверять глазами все использования переменных этого типа.

С какой целью проверять?
Что ты будешь при этом искать?


BFE>Но главное даже не это. Главное, что такие операции не имеют смысла. Это всё равно, что считать, что если к яблоку прибавить 1, то получится апельсин, а если два — то груша.


Почему нет? ))
На последовательном расположении значений енумов порой удобно выполнять диспетчеризацию в линейных массивах/таблицах.


BFE>Если вам нужно использовать отдельный арифметический тип, ну так используйте его:

BFE>
BFE>struct CEEEconstant
BFE>{
BFE>  inline constexpr static int zero = 0;
BFE>  inline constexpr static int one  = 1;
BFE>  inline constexpr static int two  = 2; 

BFE>  int value = zero;
BFE>};
BFE>

BFE>Определите для него требуемые операции и будет понятно. А так, как в коде — это очень странно и нелогично.

Тоже вариант, и тоже иногда использовался.

Но не всё было гладко.
Не исследовал, как оно сейчас, но в прошлом обёртка-структура над числами давали худший бинарный код, чем непосредственное использование типов-чисел.
Зато вариант с енумами давал идентичный код.

Плюс, надо не забыть прописать выравнивание структуры, чтобы работали правила упаковки полей С/С++, когда эти структуры используются как поля объектов.
Re[6]: Требуются пояснения
От: vdimas Россия  
Дата: 12.12.24 01:18
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>нет значения three. Значит в x лежит невалидное значение.


Скорее всего, это хелпер, чтобы каждый раз не писать ручками так:
SomeEnum value = static_cast<SomeEnum>(static_cast<int>(SomeEnum::Value2) + 1);

(если в реальном коде такое применяется.)

Тогда, с точки зрения UB, разницы нет — расписываются ли приведения типов каждый раз, или единожды для данного enum


BFE>это означает, что в коде нигде нельзя положиться на пришедшее значение CEEE.


Это и сейчас так.
Компилятор ведь никак не гарантирует попадание значений enum в перечисленные оные при объявлении.
Для этого отродясь в некоторых местах ставились проверки, сколько себя помню. ))


BFE>CEEE перестало отличаться от int. Считать, что x имеет тип CEEE — это обманывать себя и читателя.


С этим, как раз, не спорят.
Если бы в языке был такой алиас, который порождал бы новый тип — эта задача решалась бы в одну строчку.

Есть языки, где можно написать условно так:
type volume = Float64;
type velocity = Float64;

И не складывать случайно в исходнике амперы с кельвинами.


R>>А если к тому же в качестве допустимых операций рассматриваются только сравнение и битовые операции, то переполнению и вовсе неоткуда взяться.

BFE>Ээээ.... Вообще-то речь не про переполнение.
BFE>Если бы CEEE был объявлен как enum class CEEE : int... то тогда — да, UB нет, а вот без задания базового типа компилятор может полагаться на то, что есть только три значения. Да, я понимаю, что сами писатели стандарта пишут одно, а подразумевают другое, но формально это неопределённое поведение.

Эти рассуждения понятны, но похоже, что ты не прочитал аргумент внимательно — в случае битовых операций ширина результата не меняется.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.