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

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

Страниц: 1 [2] 3 4 ... 6   Вниз
  Печать  
Автор Тема: оператор [] для union  (Прочитано 36134 раз)
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



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

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

Вот напр для моей допотопной структурки: 32 байта, malloc'ов нет. А тут... 
Вроде ж несложно посмотреть sizeof(std::variant<double, std::array<double, 3>, std::array<float,4>>).

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

Поэтому копирование вряд ли сведется к memmove, да и хранить он будет все "инстанциированные", т.е. все 3 варианта
Вы неправильно понимаете.
Записан

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

Сообщений: 11445


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

Никаких виртуалов там нет: визитёр раскручивается в компил тайм.  
Так-таки и нету? Улыбающийся Ну давайте откроем исходники https://www.boost.org/doc/libs/1_53_0/boost/any.hpp того же any, Вот я наблюдаю базовый класс placeholder с виртуалами, от него наследуется темплейтщина, все как обычно, другой-то дороги нет

Да и без исходников, здравый смысл никто не отменял
Код
C++ (Qt)
CData data(CData::type_Coord);
if (leftEggWants)
 data = CData(CData::type_Value);
double test = data[0];
 
Как компилятор может сгенерить "конкретный" код для присваивания? Да никак, ясно "разборка" сидит в самом операторе, а тогда нужен подскок с виртуала. Собсно это все чем рулит "магия" Улыбающийся

« Последнее редактирование: Май 02, 2018, 18:20 от Igors » Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



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

Ну давайте откроем исходники https://www.boost.org/doc/libs/1_53_0/boost/any.hpp того же any,

Отрывайте исходники variant. any - это другая штука, хоть и похожая.
Записан

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

Сообщений: 2094



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

Цитировать
Так-таки и нету?
Во-первых речь шла о визитёрах - оба варианта раскручиваются в компил тайме и там никакого полиморфизма нет.
Во-вторых по поводу variant.. Эта тема тоже уже ранее обсуждалась. Напомню, как там реализован холдер:
Код
C++ (Qt)
private:
   struct holder_base
   {
       holder_base(size_t _id) : id(_id) {}
       virtual ~holder_base() {}
       size_t id;
   };
 
   template <class T>
   struct holder : holder_base
   {
       holder(const T & _data) : holder_base(detail::type_id<T, Args...>::value), data(_data) {}
       T data;
   };
 
   std::shared_ptr<holder_base> _holder_ptr;
 


А так обеспечивается доступ к данным
Код
C++ (Qt)
template <class T>
   const T& get() const throw (std::bad_cast)
   {
       if (!is_type<T>())
           throw std::bad_cast();
 
       return std::static_pointer_cast<holder<T>>(_holder_ptr)->data;
   }
 
   template <class T>
   T& get() throw (std::bad_cast)
   {
       if (!is_type<T>())
           throw std::bad_cast();
 
       if (!_holder_ptr.unique())
           _holder_ptr = std::make_shared<holder<T>>(std::static_pointer_cast<holder<T>>(_holder_ptr)->data);
 
       return std::static_pointer_cast<holder<T>>(_holder_ptr)->data;
   }
 
   template <class T>
   variant& operator=(const T& val)
   {
       static_assert(detail::type_id<T, Args...>::value != detail::type_id<T, Args...>::bad_id, "variant::operator=(const T&): unknown type T");
 
       _holder_ptr = std::make_shared<holder<T>>(val);
       return *this;
   }
 
Да, здесь есть просадка из-за shared_pointer + make_shared - Но эта доморощенная реализация (см. аттач). Оригинальный boost::variant работает гораздо быстрее (не вникал особо, что у него там под капотом)

Цитировать
Как компилятор может сгенерить "конкретный" код для присваивания?

Я не совсем понимаю проблему? Если нужно безопасно получать и присваивать данные по индексу, то вот аналоги сеттера и геттера:
Код
C++ (Qt)
#include <iostream>
#include <array>
#include <exception>
#include <boost/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 getter_visitor : public boost::static_visitor<value_t>
{
public:
   getter_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;
};
 
class setter_visitor : public boost::static_visitor<void>
{
public:
   setter_visitor(const value_t & data, size_t index)
       : m_data(data), m_index(index)
   {}
 
   void operator()(value_t & x) const
   {
       if (m_index != 0) throw std::out_of_range("out of range!");
 
       x = m_data;
   }
 
   template <class T>
   void operator()(T & x) const
   {
       x.at(m_index) = m_data;
   }
 
private:
   const value_t & m_data;
   size_t m_index;
};
 
 
struct CData
{
   data_t data;
 
   value_t get(size_t index) const
   {
       return boost::apply_visitor(getter_visitor(index), data);
   }
 
   void set(const value_t & x, size_t index)
   {
       boost::apply_visitor(setter_visitor(x, index), data);
   }
};
 
 
int main()
{
   CData data1;
   CData data2;
 
   data1.data = 3.14;
 
   data1.set(2.71, 0);
 
   data2.data = color_t{.5f, .5f, .5f, .0f};
 
   data2.set(5.0, 1);
 
   try {
 
       std::cout << data1.get(0) << std::endl;
 
       std::cout << data2.get(1) << std::endl;
 
   } catch (const std::out_of_range & exc)
   {
       std::cout << exc.what() << std::endl;
   }
 
   return 0;
}
 

Давайте проверим, на сколько критично этот вариант будет медленнее?
Записан

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

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

Сообщений: 11445


Просмотр профиля
« Ответ #19 : Май 03, 2018, 05:24 »

Давайте проверим, на сколько критично этот вариант будет медленнее?
Давайте, только не "сию минуту", подготовлю примерчик в течение 2-3 дней
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #20 : Май 05, 2018, 07:41 »

Вот обещанный тест (аттач). Пояснения

Скорость: никто не пытается доказывать что "а велик быстрее !!!", если это компенсируется др выгодами, то есть смысл это др и юзать. Но все-таки должно быть "прилично" - напр в полтора раза медленнее, ну ладно, в два - но не на порядок же.

Удобства (использования и расширения): используется прямолинейный подход, нужен оператор - так напишем его. В моем реальном коде методов/операторов намного больше, нужна тригонометрия и.т.п. Понятно что в std есть подобное для массивов, но тогда уже не union, и хз что взамен. Да и лазание по справочнику утомляет.. Добавить пяток строчек при необходимости - мне кажется практичнее
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3258


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

Лучше void SetNth(double value, size_t index = 0) или добавить оверлоад с 1м параметром, если хочется индекс вначале - тогда не надо будет явно передавать 0 в случае type_Value. Ваш КО.
type_Value? сириусли? enum class до вашего компилятора еще не доехал?)
Вместо size_t удобнее ssize_t, это даже стдшники признают.
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



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



Мне одному кажется, что SSE тут циклами раскатали и свитчами разрубили?
« Последнее редактирование: Май 05, 2018, 13:50 от ViTech » Записан

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

Сообщений: 2094



Просмотр профиля
« Ответ #23 : Май 05, 2018, 19:34 »

Цитировать
Вот обещанный тест (аттач).
Спасибо. Начал было пиать свой аналог Вашего CData на variantе, но когда дошёл до реализации бинарных операторов немного выпал в осадок  Улыбающийся
Вот пример одного из них (из Ваших исходников):
Код
C++ (Qt)
friend CData operator * ( const CData & a, const CData & b )
{
CData dst(a);
 
for (size_t i = 0; i < dst.Size(); ++i)
dst.SetNth(i, dst.GetNth(i) * b.GetNth(i));
 
return dst;
}
 

А что произойдёт, если, например,  b.Size() < a.Size() ?  Вообще такие операции (бинарные) не корректны, как  с точки зрения здравого смысла, так и математики, если размерности объектов  не совпадают.  Или чётче формулируйте правила игры.
Какой результат мы ожидаем получить, если, например a имеет type_Color, а b type_Coord?



Записан

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

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

Сообщений: 4349



Просмотр профиля
« Ответ #24 : Май 05, 2018, 21:52 »

Спасибо. Начал было пиать свой аналог Вашего CData на variantе, но когда дошёл до реализации бинарных операторов немного выпал в осадок  Улыбающийся
Отвыкаем... выпадаем в осадок от такой ерунды "made in Igors". Улыбающийся
Вот жеж "решение":
Код
C++ (Qt)
friend CData operator * ( const CData & a, const CData & b )
{
Q_ASSERT(a.Size() == b.Size());
 
CData dst(a);
 
for (size_t i = 0; i < dst.Size(); ++i)
dst.SetNth(i, dst.GetNth(i) * b.GetNth(i));
 
return dst;
}
 


А если серьезно, то потребность в таких типах уже явный признак того, что нужно все переделывать. Странно, что в CData еще нет строки, даты, матрицы и геокоординаты? Для всего из перечисленного можно описать аналогичный оператор [] Улыбающийся
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2094



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

Цитировать
А если серьезно, то потребность в таких типах уже явный признак того, что нужно все переделывать.
Согласен, здесь явный архитектурный дефект) Который продуцирует подобные костыли.. Вообще, постоянно прослеживается какая то навязчивая идея супер-классов, оправдываемая антитезисом: "ну так же проще и понятней"..  Грустный 
Записан

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

Arch Linux Plasma 5
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #26 : Май 06, 2018, 10:28 »

Какой результат мы ожидаем получить, если, например a имеет type_Color, а b type_Coord?

Вот вы тоже удивительные вопросы задаёте. double же Улыбающийся.
Записан

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

Сообщений: 11445


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

Какой результат мы ожидаем получить, если, например a имеет type_Color, а b type_Coord?
Конечно exception (пропустил assert проверку). Все операторы работают только с CData одинакового типа.

Мне одному кажется, что SSE тут циклами раскатали и свитчами разрубили?
Ну для double AVX. Немного смотрел реализацию подобного в более простом случае (просто 3 float). Геморрой безумный, где-то глючит (отключаю этот #define - все норм), а вот выигрыша не ощутил.

Согласен, здесь явный архитектурный дефект) Который продуцирует подобные костыли.. Вообще, постоянно прослеживается какая то навязчивая идея супер-классов, оправдываемая антитезисом: "ну так же проще и понятней"..  Грустный 
А разве не так? Примитивно? Ужасно примитивно! (я этого и не скрывал). Но что Вы можете предложить взамен кроме "фырканья" (для которого много ума не надо)? Схему с визитером? У меня стойкое впечатление что это "не тот случай" - класс выполняет слишком низкоуровневую работу, накладные расходы слишком велики. Кстати, у Вас прекрасная возможность опровергнуть это результатами тестов.  Хотя, справедливости ради, Вы единственный кто хоть что-то предложил  Улыбающийся

Так может, если лучшего не видно, не стоит брезговать простым кодом - будет намного лучше чем городить "нечто" (только в угоду моде).     

Про архитектуру напишу отдельно  Улыбающийся
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


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

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

Есть контейнеры 3 (тех же) типов
Код
C++ (Qt)
Container<double> c1;
Container<coordinate> c2;
Container<color> c3;
Которые имеют массу общего (могут рисоваться в виде графиков, отображаться в таблицах и.т.п.). Очень скоро Вы убедитесь что работать с ними в таком виде нереально. В контейнер их указатели не положить, или напр хотим (всего-навсего) читать-писать эл-т контейнера. Если сделаем напр так
Код
C++ (Qt)
template<class T>
T Container<T>::GetElement( size_t index ) const;
То теперь все что зовет этот метод тоже обязано быть "инстанциируемым темплейтом", эта зараза моментально расползется. Но подавляющая масса кода без понятия чем же "инстанциировать", поэтому сделать все темплейтами не удастся. Очевидно надо создать базовый виртуальный класс СBaseContainer и от него наследовать темплейты.  Вот и выплывает "вариантный" класс CData
Код
C++ (Qt)
CData CBaseContainer::GetElement( size_t index ) const;
void CBaseContainer::SetElement( size_t index, const CData & data );
 
И, как бы Вы не критиковали, обойтись без него не удастся (да и не нужно)  Улыбающийся
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



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

Ну для double AVX. Немного смотрел реализацию подобного в более простом случае (просто 3 float). Геморрой безумный, где-то глючит (отключаю этот #define - все норм), а вот выигрыша не ощутил.

И не говорите, сложно до безумия.
Записан

Пока сам не сделаешь...
Страниц: 1 [2] 3 4 ... 6   Вверх
  Печать  
 
Перейти в:  


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