Russian Qt Forum

Программирование => С/C++ => Тема начата: Igors от Декабрь 05, 2020, 10:52



Название: Ассоциации (?)
Отправлено: Igors от Декабрь 05, 2020, 10:52
Добрый день

Есть такие данные

Код
C++ (Qt)
enum {
 param_Amount = 0,
 param_Velocity = 1,
 param_..,
 ...
};
 
std::vector<QString> paramNames = {
 "Amount",
 "Velocity",
 ...
};
Как я могу быть уверен что enum соответствует строке имени? При большом числе данных (и/или частых изменениях) это оказывается совсем не простым

Спвсибо


Название: Re: Ассоциации (?)
Отправлено: kambala от Декабрь 05, 2020, 14:11
в данном случае — никак. а в Qt для этого есть QMetaEnum.


Название: Re: Ассоциации (?)
Отправлено: Авварон от Декабрь 05, 2020, 14:48
можно поменять вектор на пару энум-строка и использовать богомерзкие макросы для описания элементов.
приправить static_assert для проверки валидности массива


Название: Re: Ассоциации (?)
Отправлено: RedDog от Декабрь 05, 2020, 17:43
Можно мапу/хеш завести


Название: Re: Ассоциации (?)
Отправлено: Igors от Декабрь 06, 2020, 12:13
в данном случае — никак. а в Qt для этого есть QMetaEnum.
"A можно пример?"  :)

можно поменять вектор на пару энум-строка и использовать богомерзкие макросы для описания элементов.
приправить static_assert для проверки валидности массива
Но сначала придется все равно объявить enum чтобы юзать его в паре
Можно мапу/хеш завести
Да, часто достаточно и вектора пар. но тогда потребуется find. С хешем выглядит примерно так
Код
C++ (Qt)
QString Param2Name( TParam param )
{
 static QHash<TParam, QString> names;
 if (!names.size()) {
   names[param_Amount] = "Amount";
   names[param_Velocity] = "Velocity";
   ...
 }
 return names.value(param);
}
Терпимо, но элегантностью не блещет, да и нет гарантии (компилятора) что все enum'ы будут в хеше


Название: Re: Ассоциации (?)
Отправлено: kambala от Декабрь 06, 2020, 15:06
Код
C++ (Qt)
class ItemQuality
{
   Q_GADGET
   Q_ENUMS(ItemQualityEnum)
 
public:
   enum ItemQualityEnum
   {
       LowQuality = 1,
       Normal,
       HighQuality,
       Magic,
       Set,
       Rare,
       Unique,
       Crafted,
       Honorific
   };
};
 
const char *quality = metaEnumFromName<Enums::ItemQuality>("ItemQualityEnum").valueToKey(item->quality);

код писался еще под 4.8.7 и до С++11, может щас можно как-то получше.


Название: Re: Ассоциации (?)
Отправлено: Igors от Декабрь 07, 2020, 12:17
Код
C++ (Qt)
const char *quality = metaEnumFromName<Enums::ItemQuality>("ItemQualityEnum").valueToKey(item->quality);
Ну как-то это выглядит монструозно или неадекватно для скромной цели стартового поста :) Возможно там задумка печатать такие enum'ы автоматом

код писался еще под 4.8.7 и до С++11, может щас можно как-то получше.
Да, я тоже рассчитывал на какую-нить "фишку" из новых стандартов. Хотя сам дальше С++ 11 пока не уполз  :)

В любом случае спасибо за пример


Название: Re: Ассоциации (?)
Отправлено: AkonResumed от Апрель 03, 2021, 08:09
Хз. Год прошел. Но если прям нужна уверенность, то, навскидку, как-то так:
Код:
#include <iostream>

enum Param {
First,
Second,
Third
};

template <Param> const char* ParamName = "Not associated!";
template <> const char* ParamName<First> = "First parameter name";
template <> const char* ParamName<Second> = "Second parameter name";

// Для третьего параметра "забыли" специализацию, поэтому будет "Not associated!".

int main()
{
std::cout << ParamName<First> << std::endl;
std::cout << ParamName<Second> << std::endl;
std::cout << ParamName<Third> << std::endl;  // Not associated!

return 0;
}

Или еще лучше - с компайл-тайм детектион:
Код:
template <Param> const char* ParamName = "Not associated!";
заменить на
template <Param> const char* ParamName = [](){ static_assert(0, "Not associated!"); return nullptr; }();
В этом случае строка
std::cout << ParamName<Third> << std::endl;  // Not associated!
просто будет давать ошибку компиляции.

Здесь С++17 (template variables/constants, invokable lambda), но в С++11 это тоже принципиально достижимо.

З.Ы. Сам всегда делал (и делаю) перечисление + массив строк (как у вас). Да, при изменении нужна скрупулезность. Зато достоинство - простота и читаемость кода. Да и часто для строк нужно делать перевод на разные языки, а это уже что-то вроде
QCoreApplication::translate("context", QT_TRANSLATE_NOOP("context", "First parameter name")
и только рантайм.


Название: Re: Ассоциации (?)
Отправлено: kambala от Апрель 27, 2021, 22:18
https://github.com/Neargye/magic_enum


Название: Re: Ассоциации (?)
Отправлено: Igors от Апрель 28, 2021, 11:31
https://github.com/Neargye/magic_enum
Да, вроде "в тему", спасибо. Правда сейчас навалилась др работа..


Название: Re: Ассоциации (?)
Отправлено: m_ax от Апрель 28, 2021, 15:56
https://github.com/Neargye/magic_enum
Красивое решение)


Название: Re: Ассоциации (?)
Отправлено: ssoft от Апрель 28, 2021, 17:27
Если нужен свой велик для С++98, то можно что-то типа этого использовать

Код
C++ (Qt)
template < typename _Type, int >
struct EnumName { static const char * name () { return "<Unknown>"; } };
 
#define ENUM_NAME( Enum, param_value, param_name ) \
   template <>  struct EnumName< Enum, param_value > { static const char * name () { return param_name; } }; \
 
template < typename _Enum, int _value >
struct EnumNameHelper
{
   static const char * name ( int value )
   {
       if ( _value == value )
           return EnumName< _Enum, _value >::name();
       else if ( _value > value )
           return EnumNameHelper< _Enum, _value - 1 >::name( value );
       else
           return "<Incorrect>";
   }
};
 
template < typename _Enum >
struct EnumNameHelper< _Enum, -1 >
{
   static const char * name ( int ) { return "<Corrupted>"; }
};
 
template < typename _Enum >
const char * enumValueName ( _Enum value )
{
   return EnumNameHelper< _Enum, param_Count >::name( value );
}
 

Код
C++ (Qt)
enum MyEnum
{
 param_Amount,
 param_Velocity,
 param_Color,
 param_Coordinate,
 param_Count
};
 
// или так писать
template <> struct EnumName< MyEnum, param_Amount > { static const char * name () { return "Amount"; } };
// или так
ENUM_NAME( MyEnum, param_Velocity, "Velocity" )
ENUM_NAME( MyEnum, param_Color, "Color" )
ENUM_NAME( MyEnum, param_Coordinate, "Coordinate" )
 
 
#include <iostream>
 
int main ( int narg, char ** vargs )
{
   // В runtime тоже работает
   for ( size_t i = 0; i < param_Count; ++i )
       ::std::cout << enumValueName< MyEnum >( i ) << ::std::endl;
   return 0;
}