Russian Qt Forum
Ноябрь 25, 2024, 15:01 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2 3 ... 6   Вниз
  Печать  
Автор Тема: оператор [] для union  (Прочитано 40621 раз)
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« : Апрель 27, 2018, 11:01 »

Добрый день

Есть такая структурка
Код
C++ (Qt)
struct CData {
enum Type { type_Value, type_Coord, type_Color };
Type m_type;
union {
  double m_value;
  double m_coord[3];
  float m_color[4];
};
};
Требуется оператор [] который как минимум проверял бы индекс, напр если type_Value, то data[1] должно выбросить исключение. Ну а возврат по ссылке вообще было бы идеально. Как это сделать без унылых свитчей? Сменить union на что-то другое - пожалуйста, но сам класс CData не должен быть template

Спасибо
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #1 : Апрель 27, 2018, 11:17 »

std::variant.
Записан

Пока сам не сделаешь...
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #2 : Апрель 27, 2018, 12:15 »

std::variant.
Пример
Код
C++ (Qt)
void DoSomething( const CData & src, CData & dst, int index )
{
...
 
// хотелось бы записать так
dst.Set(index, src[index]);
 
// а еще лучше так
dst[index] = src[index];
}
 
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #3 : Апрель 27, 2018, 12:47 »

Чем не устраивает:
Код
C++ (Qt)
void DoSomething( const CData & src, CData & dst, int index)
{
   ...
   dst = src;
}

Есть  метод std::variant::index(). по нему можно проверки сделать, если надо.
Записан

Пока сам не сделаешь...
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #4 : Апрель 27, 2018, 15:22 »

Чем не устраивает:
Код
C++ (Qt)
void DoSomething( const CData & src, CData & dst, int index)
{
   ...
   dst = src;
}
Тем что это присвоит все компоненты, а нужно индексированную
Есть  метод std::variant::index(). по нему можно проверки сделать, если надо.
Это индекс текущего "типа" (а не индекса в массиве). И если делать проверки - то где "выйгрыш"? Записать то же но в стандартном стиле - ну может смысл и есть, но к теме не относится
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #5 : Апрель 27, 2018, 16:32 »

Так речь про индекс в массиве, хотя в union может и не массив совсем будет храниться во время выполнения программы. Интересно Улыбающийся.

Допустим добавите Вы оператор:
Код
C++ (Qt)
???? operator[](int index){...}
 
он какой тип возвращать должен: double или float? Как компилятор это должен определить в compile-time, если m_type хранимого значения будет известен только в run-time?

Связались с variant - кушайте свитчи. Они всё равно, в том или ином виде, будут присутствовать при работе с variant.
Записан

Пока сам не сделаешь...
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #6 : Апрель 27, 2018, 16:36 »


он какой тип возвращать должен: double или float? Как компилятор это должен определить в compile-time, если m_type хранимого значения будет известен только в run-time?


ну? например, std::any
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #7 : Апрель 27, 2018, 16:44 »

ну? например, std::any

Действительно. Теперь всё пойдёт как по маслу Улыбающийся.
Записан

Пока сам не сделаешь...
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #8 : Апрель 28, 2018, 11:30 »

он какой тип возвращать должен: double или float?
Такие вопросы удивляют Улыбающийся. Ну конечно double (старший). Никаких сверхзадач типа "хранить и оперировать со всем на свете" не ставится, речь идет о конкретном, узком наборе типов перечисленном в стартовом посте.

ну? например, std::any
Баранки гну. На хрена он мне сдался, тот any чтобы потом разбираться что в нем сидит. Я хочу удобно (и с контролем) читать/писать эл-т массива, а для какого типа - отвечает вызывающий.   
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #9 : Апрель 28, 2018, 13:46 »

Такие вопросы удивляют Улыбающийся. Ну конечно double (старший). Никаких сверхзадач типа "хранить и оперировать со всем на свете" не ставится, речь идет о конкретном, узком наборе типов перечисленном в стартовом посте.

Вот значит что у Вас удивление вызывает Улыбающийся. В стартовом посте Вы хотели, чтобы в идеале возврат был по ссылке, значит и 'float &' можно возвращать как 'double &'? И чем не устраивает  унылый свитч для конкретного узкого набора типов?

Баранки гну. На хрена он мне сдался, тот any чтобы потом разбираться что в нем сидит. Я хочу удобно (и с контролем) читать/писать эл-т массива, а для какого типа - отвечает вызывающий.   

Т.е. когда Вы решили использовать variant, такое же возмущение Вас не охватило Улыбающийся? И с variant получается "удобно (и с контролем) читать/писать эл-т массива"?
Записан

Пока сам не сделаешь...
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #10 : Апрель 28, 2018, 16:40 »

Как уже предлогали, здесь напрашивается variant, а в качестве свича использовать визитёра:

Код
C++ (Qt)
#include <iostream>
#include <array>
#include <exception>
#include <variant.hpp>
 
typedef double value_t;
typedef std::array<double, 3> coord_t;
typedef std::array<float, 4> color_t;
typedef boost::variant<value_t, coord_t, color_t> data_t;
 
 
class my_visitor : public boost::static_visitor<value_t>
{
public:
   my_visitor(size_t index)
       : m_index(index)
   {}
 
   value_t operator()(const value_t & x) const
   {
       if (m_index != 0) throw std::out_of_range("out of range!");
 
       return x;
   }
 
   template <class T>
   value_t operator()(const T & x) const
   {
       return x.at(m_index);
   }
 
private:
   size_t m_index;
};
 
 
struct CData
{
   data_t data;
 
   value_t operator[] (size_t index) const
   {
       return boost::apply_visitor(my_visitor(index), data);
   }
};
 
int main()
{
   CData data1;
   CData data2;
 
   data1.data = 3.14;
 
   data2.data = color_t{.5f, .5f, .5f, .0f};
 
   try {
 
       std::cout << data1[0] << std::endl;
 
       std::cout <<data2[3] << std::endl;
 
   } catch (const std::out_of_range & exc)
   {
       std::cout << exc.what() << std::endl;
   }
 
   return 0;
}
 
 

Да, кстатии, такой же финт можно реализовать не только с variant, но и, например, с boost::any. Эта идея уже обсуждалась ранее здесь: http://www.prog.org.ru/topic_29268_0.html
Всё, примерно аналогично:
Код
C++ (Qt)
#include <iostream>
#include <array>
#include <exception>
#include <boost/any.hpp>
#include <specmath/any_visitor/any_visitor.h>
 
typedef double value_t;
typedef std::array<double, 3> coord_t;
typedef std::array<float, 4> color_t;
 
class my_any_visitor : public any_visitor<value_t, type_list<value_t, coord_t, color_t>>
{
public:
   my_any_visitor(size_t index)
       : m_index(index)
   {}
 
   value_t operator()(const value_t & x) const
   {
       if (m_index != 0) throw std::out_of_range("out of range!");
       return x;
   }
 
   template <class T>
   value_t operator()(const T & x) const
   {
       return x.at(m_index);
   }
 
private:
   size_t m_index;
};
 
 
struct CData
{
 
   boost::any data;
 
   value_t operator[] (size_t index) const
   {
       return apply_any_visitor(my_any_visitor(index), data);
   }
};
 
int main()
{
   CData data1;
   CData data2;
 
   data1.data = 3.14;
 
   data2.data = color_t{.5f, .5f, .5f, .0f};
 
   try {
 
       std::cout << data1[0] << std::endl;
 
       std::cout << data2[1] << std::endl;
 
   } catch (const std::out_of_range & exc)
   {
       std::cout << exc.what() << std::endl;
   }
 
}
 


Приаттачу на всякий случай более свежие исходники any_visitor.h
Меташаблонная магия рулит  Подмигивающий
« Последнее редактирование: Апрель 29, 2018, 01:13 от m_ax » Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #11 : Май 01, 2018, 16:32 »

значит и 'float &' можно возвращать как 'double &'?
Кстати - можно, неск лет назад тут человек показывал, но у меня все не укладывается в голове  Улыбающийся
И чем не устраивает  унылый свитч для конкретного узкого набора типов?
Да собсно всем устраивает. Вот "цена вопроса"
Код
C++ (Qt)
double CData::GetNth( size_t index ) const
{
 Q_ASSERT(index < size());
 
 switch (m_type) {
 
  case type_Value:
    Q_ASSERT(index == 0);
    return m_value;
 
   case type_Coord:
    return m_coord[index];
 
   case type_Color:
    return m_color[index];
 
   default:
     Q_ASSERT(0);
 }
 return 0.0;
}
Ну и SetNth в том же духе. Пока не уверен нужен ли оператор []. "Чем не устраивает" - ну выглядит уж очень старенько, а на дворе уже 21-й век.  Может есть какие "фишки", поспрашиваю у молодых...

Как уже предлогали, здесь напрашивается variant, а в качестве свича использовать визитёра:
...
Меташаблонная магия рулит  Подмигивающий
Ну вообще-то это класс типа QVector3D, т.е. для интенсивнейшего возврата по значению. В связи с этим вопрос - а сколько памяти сожрет такой boost::variant и сколько у него malloc'ов ?

А так.. ну да, понятно с визитером, но нет легкости, во все надо вникать, напрягаться... А стоит ли? Ведь конечный полезный эффект не так уж велик (если вообще имеется)
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #12 : Май 02, 2018, 11:47 »

значит и 'float &' можно возвращать как 'double &'?
Кстати - можно, неск лет назад тут человек показывал, но у меня все не укладывается в голове  Улыбающийся
Что-то сомнительно для нешаблонной функции. Всё равно это костыль, достаточно дождаться какого-нибудь int среди double/float в варианте.

Ну и SetNth в том же духе. Пока не уверен нужен ли оператор []. "Чем не устраивает" - ну выглядит уж очень старенько, а на дворе уже 21-й век.  Может есть какие "фишки", поспрашиваю у молодых...
Нормально выглядит, если это надо один раз для конкретного узкого набора типов.

Ну вообще-то это класс типа QVector3D, т.е. для интенсивнейшего возврата по значению. В связи с этим вопрос - а сколько памяти сожрет такой boost::variant и сколько у него malloc'ов ?

А так.. ну да, понятно с визитером, но нет легкости, во все надо вникать, напрягаться... А стоит ли? Ведь конечный полезный эффект не так уж велик (если вообще имеется)

Если нормально визитёра написать, то никакого оверхеда быть не должно. В конечном итоге, применение визитёра к варианту развернётся примерно в тот же свитч, что Вы руками написали. Только в свитче выбор определяется по индексу типа (Type), а в визитёре по типу данных (double, double[3], float[4]). Плюс визитёра в том, что его можно применять для разных вариантов, и не нужно для каждого похожий свитч писать.
Записан

Пока сам не сделаешь...
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #13 : Май 02, 2018, 15:29 »

Задаю простой, естественный вопрос, типа "сколько весит".
Если нормально визитёра написать, то никакого оверхеда быть не должно.
Почему не вижу столь же простого ответа? Вот напр для моей допотопной структурки: 32 байта, malloc'ов нет. А тут... 

В конечном итоге, применение визитёра к варианту развернётся примерно в тот же свитч, что Вы руками написали.
Насколько я понимаю, никаких "разверток" там нет. Объявляется набор классов с базовым виртуальным методом для каждого "общего" действия. При инстанциации создаются порожденные классы для каждого темплейт типа которые зовут оте внешние огрызки кода. А при обращении зовется базовый виртуал. 

Поэтому копирование вряд ли сведется к memmove, да и хранить он будет все "инстанциированные", т.е. все 3 варианта
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #14 : Май 02, 2018, 16:04 »

Цитировать
Насколько я понимаю, никаких "разверток" там нет. Объявляется набор классов с базовым виртуальным методом для каждого "общего" действия.
Никаких виртуалов там нет: визитёр раскручивается в компил тайм. 

Цитировать
Задаю простой, естественный вопрос, типа "сколько весит".
Давайте померим и по производительности сравним.

Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Страниц: [1] 2 3 ... 6   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.262 секунд. Запросов: 22.