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

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

Страниц: [1] 2 3 ... 5   Вниз
  Печать  
Автор Тема: (С++11) Variant - простой аналог boost::any  (Прочитано 38556 раз)
navrocky
Гипер активный житель
*****
Offline Offline

Сообщений: 817


Погроммист


Просмотр профиля
« : Февраль 11, 2015, 13:18 »

Предлагаю вашему вниманию простой аналог boost::any. Иногда случается, что QVariant неудобен или проект не основан на Qt, а boost тащить не особо хочется.

В моём случае это был C++ проект под мобилку. Простых и понятных реализаций я навскидку не нашел, поэтому свелосипедил свою.

Пример использования:
Код
C++ (Qt)
Variant v;
v = 10;
if (v.isType<int>())
   cout << v.value<int>() << endl;

Плюсы:
  • всё реализовано в одном заголовочном файле
  • поддерживается сравнение по значению, если сравнение поддерживается хранимым типом
  • зависит только от стандартной библиотеки C++ 11

Минусы:
  • зависит от RTTI
  • использует динамическую память под хранение значения
  • C++03 не поддерживается

По умолчанию при кастовании происходит сравнение строк с названиями типов хранимых значений. Можно включить оптимизацию -DVARIANT_CAST_OPTIMIZATION=1, которая приведет к сравнению типов по указателю, но при этом теряется возможность использовать Variant в интерфейсе динамических библиотек (т.к. таблицы метаданных разные).

Актуальный исходник здесь: https://github.com/navrocky/Variant
« Последнее редактирование: Февраль 12, 2015, 19:21 от navrocky » Записан

Гугль в помощь
panAlexey
Гипер активный житель
*****
Offline Offline

Сообщений: 864

Акцио ЗАРПЛАТА!!!!! :(


Просмотр профиля
« Ответ #1 : Февраль 11, 2015, 16:57 »

Спасибо!
Записан

Win Xp SP-2, Qt4.3.4/MinGW. http://trdm.1gb.ru/
vregess
Гость
« Ответ #2 : Февраль 11, 2015, 20:57 »

А еще есть cdiggins::any
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #3 : Февраль 12, 2015, 12:15 »

А можно ли оперировать выражениями, было бы круто напр
Код
C++ (Qt)
Variant a, b, c;
..
Variant result = (a > 0) ? b : c;
Или это неизбежно сваливается в дебри свитчей?
Записан
navrocky
Гипер активный житель
*****
Offline Offline

Сообщений: 817


Погроммист


Просмотр профиля
« Ответ #4 : Февраль 12, 2015, 16:36 »

А можно ли оперировать выражениями

Технически такое сделать вполне возможно. Только сравнивать можно одинаковые типы. Не прокатит конвертация.. Хотя если запилить аналог lexical_cast, то на его базе можно и сравнение разных типов сделать.   Сейчас пробую.
« Последнее редактирование: Февраль 12, 2015, 16:54 от navrocky » Записан

Гугль в помощь
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #5 : Февраль 12, 2015, 17:43 »

У меня пара комментариев по реализации:

1) Всё же, сравнение типов как строк (и их хранение в HolderBase) не очень оптимальное решение.. Почему бы вместо этого не использовать std::dynamic_pointer_cast в методе isType():
Код
C++ (Qt)
template <typename T>
   bool isType() const
   {
       return std::dynamic_pointer_cast<Holder<T>>(holder_) != 0;
   }
 
 
Не вижу к каким проблемам это может привести?

2) Наверное логичнее/нагляднее кидать std::bad_cast вместо std::runtime_error?
Код
C++ (Qt)
template <typename T>
   const T& value() const throw (std::bad_cast)
   {
       if (!isType<T>())
           throw std::bad_cast();
       return std::static_pointer_cast<Holder<T>>(holder_)->value;
   }
 

Записан

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

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

Сообщений: 817


Погроммист


Просмотр профиля
« Ответ #6 : Февраль 12, 2015, 19:00 »

1) Всё же, сравнение типов как строк (и их хранение в HolderBase) не очень оптимальное решение.. Почему бы вместо этого не использовать std::dynamic_pointer_cast в методе isType():
Не вижу к каким проблемам это может привести?

dynamic_cast не работает в динамических библиотеках. Соответственно если либа выдаст вам такой Variant, в основном коде уже ничего не сможете из него вытащить. Хотя согласен, что сравнение указателя эффективнее чем сравнение строки.
Может быть сделать эту оптимизацию через DEFINE?

Кстати сейчас вспомнил, что реализация dynamic_cast в студийном компиляторе сравнивает строки.

2) Наверное логичнее/нагляднее кидать std::bad_cast вместо std::runtime_error?

С этим согласен, подправлю.
« Последнее редактирование: Февраль 12, 2015, 19:02 от navrocky » Записан

Гугль в помощь
navrocky
Гипер активный житель
*****
Offline Offline

Сообщений: 817


Погроммист


Просмотр профиля
« Ответ #7 : Февраль 12, 2015, 19:16 »

Выхожил код на GitHub, чтобы легче было дописывать. ссылку положил в шапку.

  • по просьбе Igors добавил возможность сравнивать Variant, теперь их можно размещать в std::map и прочие упорядоченные контейнеры
  • также добавил возможность включения оптимизации кастования, которая сравнивает только указатели
  • добавил тесты
Записан

Гугль в помощь
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #8 : Февраль 12, 2015, 19:46 »

Цитировать
dynamic_cast не работает в динамических библиотеках. Соответственно если либа выдаст вам такой Variant, в основном коде уже ничего не сможете из него вытащить.
Да, погуглил сейчас на эту тему.. Как я понял, это особенность реализации rtti..

Однако, накидал тестовый пример, где у меня (gcc 4.8.2) всё работает.. (аттач)
Или я что то неправильно понял?



Записан

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

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

Сообщений: 2095



Просмотр профиля
« Ответ #9 : Февраль 12, 2015, 20:00 »

Цитировать
по просьбе Igors добавил возможность сравнивать Variant, теперь их можно размещать в std::map и прочие упорядоченные контейнеры
Имхо, плохая идея.. Я бы не стал раздувать из variant супер-класс с операторами сравнения и т.д..
Завтра igorsу приспичит не только сравнивать но и складывать, умножать, инкрементировать/декрементировать  и т.д.. И что же? Во что ваш variant превратиться?) 
Записан

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

Arch Linux Plasma 5
Bepec
Гость
« Ответ #10 : Февраль 12, 2015, 23:34 »

в буст Веселый
Записан
vbv
Чайник
*
Offline Offline

Сообщений: 59


Просмотр профиля
« Ответ #11 : Февраль 13, 2015, 02:10 »

А взять и сделать вытяжку из boost только его(boost::any) одного и использовать (хоть статически хоть динамически), не плодя велосипеды?
Не, если задача чисто академическая... Тогда успехов.
Записан
navrocky
Гипер активный житель
*****
Offline Offline

Сообщений: 817


Погроммист


Просмотр профиля
« Ответ #12 : Февраль 13, 2015, 10:17 »

А взять и сделать вытяжку из boost только его(boost::any) одного и использовать (хоть статически хоть динамически), не плодя велосипеды?
Не, если задача чисто академическая... Тогда успехов.
Я об этом думал, но глядя на запутанные исходники мне становилось грустно.
Записан

Гугль в помощь
navrocky
Гипер активный житель
*****
Offline Offline

Сообщений: 817


Погроммист


Просмотр профиля
« Ответ #13 : Февраль 13, 2015, 10:33 »

Однако, накидал тестовый пример, где у меня (gcc 4.8.2) всё работает.. (аттач)
Или я что то неправильно понял?
Да, действительно. Надо бы поглядеть реализацию на асме...
Записан

Гугль в помощь
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #14 : Февраль 13, 2015, 12:25 »

Код
C++ (Qt)
template <typename Type>
struct HasEqualOperator
{
typedef char yes[1];
typedef char no[2];
template <std::size_t N>
struct SFINAE {};
template <typename T>
static yes& isEqualComparable(SFINAE<sizeof(*static_cast<T*>(0) == *static_cast<T*>(0))>* = 0);
 
Оказывается уже и так можно! (заглянул в справочник Улыбающийся). Однако, если я правильно понял, экземпляр типа может сравниваться только с точно таким же (напр сравнение float с double поведет к runtime error).

Я не сторонник таких вещей. "Секрет успеха С++ в его совместимости с С". Переменная int - это 4 байта в памяти - и ВСЕ, никаких доп данных нет. Это нормально и хорошо. Но энтузиастам неймется, поэтому начинается выкручивание рук с гребаными темплейтами. Т.е. вообще-то тип не хранится, но когда объявляем переменную - компилятор его знает, вот на этом будем что-то варить... На мой взгляд это попытки придать языку нечто несвойственное, не лучше ли выскочить в тот же Пытон где все это натурально.

Однако привлекает простота с которой Вы это сбацали. Хранить в контейнере "всякую всячину" - реально usable. По поводу развития этого мини-проекта: лучше оставить его "lite", не пытаться накручивать операторов и.т.п. - по-моему это оказалось неудачным, потерялась "легкость".  Вместо этого организовать хранение простых типов (до 8 байт) "на месте" вместо кучи, а то сейчас реализация "жирная"

Спасибо

Записан
Страниц: [1] 2 3 ... 5   Вверх
  Печать  
 
Перейти в:  


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