crossplatform.ru

Здравствуйте, гость ( Вход | Регистрация )

alexy
  опции профиля:
сообщение 15.4.2015, 0:02
Сообщение #1


Студент
*

Группа: Участник
Сообщений: 44
Регистрация: 4.8.2010
Пользователь №: 1931

Спасибо сказали: 0 раз(а)




Репутация:   0  


shared_ptr нельзя создать в конструкторе...
у меня сейчас в многопоточном коде часто появляются двухэтапные конструкторы. снчала создаешь shared_ptr потом вызываешь какой-нибудь registr или еще что-нибудь, куча отложенных созданий объектов, когда я бы мог создать его в конструкторе ,но создаю в фукнции ,которой он нужен, из-за этого появляются дополнительные проверки "не создал ли я его уже?", которых можно было бы избежать, если бы...
к тому же мне не нравится shared_from_this с точки зрения ООП, это как бы деталь реализации, закравшаяся в интерфейс.

может спасти intrusive_ptr, но у него нет weak_ptr и значит проблема циклических ссылок будет.

вобщем сегодня я накатал такой код. не уверен в правильности использвания std::atomic. выбрал shared_ptr для быстроты реализации. потом хочу сделать структуру, одно поле будет счетчиком ссылок для объекта, другое для weak_ptr'а.

выкладываю: может кому интересно или у кого-то уже есть свои проверенные наработки, лучше чем у меня, поделитесь пожалуйста.

зы: а что, нельзя загружать файлы hpp ??
Прикрепленные файлы
Прикрепленный файл  intrusive_weak_ptr.hpp.txt ( 3.67 килобайт ) Кол-во скачиваний: 420
 
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
2 страниц V   1 2 >  
Начать новую тему
Ответов (1 - 16)
lanz
  опции профиля:
сообщение 15.4.2015, 0:53
Сообщение #2


Старейший участник
****

Группа: Участник
Сообщений: 690
Регистрация: 28.12.2012
Пользователь №: 3660

Спасибо сказали: 113 раз(а)




Репутация:   8  


Цитата
shared_ptr нельзя создать в конструкторе...

А зачем? Чтобы иметь член класса-указатель на себя самого? Или чтобы вызывать глобальные статические функции?
В любом случае, лучше использовать factory[method].
class MyAwesomeObj {
  ...
}
class MyAwesomeFactory {
  virtual std::shared_ptr<MyAwesomeObj> create() {
    std::shared_ptr<MyAwesomeObj> obj = std::shared_ptr<MyAwesomeObj>( new MyAwesomeObj );
    MyAwesomeSingletonRegistry::Instance().registerAwesome( obj );
    return obj;
  }
}
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
alexy
  опции профиля:
сообщение 15.4.2015, 14:19
Сообщение #3


Студент
*

Группа: Участник
Сообщений: 44
Регистрация: 4.8.2010
Пользователь №: 1931

Спасибо сказали: 0 раз(а)




Репутация:   0  


Цитата(lanz @ 15.4.2015, 1:53) *
Или чтобы вызывать глобальные статические функции?

ага, для нескольких типов у меня есть реестр.
также вревовидные структуры.. некоторым нужно обязательно создать определенных детей. то есть например класс дерево обязательно должен содержать один лист и один орех, другое по желанию :)

Цитата(lanz @ 15.4.2015, 1:53) *
В любом случае, лучше использовать factory[method].


а чем лучше?
нужно создавать паралельную эрархию классов. а если я хочу указатель на созданный тип, а не на базовый? с фабрикой останется только dynamic_cast.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
lanz
  опции профиля:
сообщение 15.4.2015, 14:58
Сообщение #4


Старейший участник
****

Группа: Участник
Сообщений: 690
Регистрация: 28.12.2012
Пользователь №: 3660

Спасибо сказали: 113 раз(а)




Репутация:   8  


Цитата
а чем лучше?

Separation of concerns, разделяете тип хранения (shared_ptr), реестры и ваш класс. Тип хранения это деталь реализации.
Вот например сейчас у вас один реестр, а захотите например 2, из которых один записывает в БД. Добавляете новую фабрику, с 2 реестрами и все. Хотите создаете с одним реестром, хотите с двумя. А если у вас занесение в реестр в конструкторе как вы это реализуете? параметр передавать в конструктор? if-then-else?
Цитата
а если я хочу указатель на созданный тип, а не на базовый?

Interface segregation principle, значит у базового типа плохой интерфейс, возможно стоит сделать еще один? :lol:
Если не хочется много писать, сделайте factory template:
template <typename T>
class MySharedFactory {
  std::shared_ptr<T> create() { return std::shared_ptr<T>(new T); }
}
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
alexy
  опции профиля:
сообщение 15.4.2015, 15:48
Сообщение #5


Студент
*

Группа: Участник
Сообщений: 44
Регистрация: 4.8.2010
Пользователь №: 1931

Спасибо сказали: 0 раз(а)




Репутация:   0  


Цитата(lanz @ 15.4.2015, 15:58) *
Separation of concerns, разделяете тип хранения
...
Interface segregation principle, значит у базового типа плохой интерфейс, возможно стоит сделать еще один? :lol:


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

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

ЗЫ: да, мои гуглинья наводят меня на мысль, что это действительно никому не нужно и люди юзают другие способы. выходит что обычно такое рашется именно фабрикой?
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
lanz
  опции профиля:
сообщение 15.4.2015, 16:11
Сообщение #6


Старейший участник
****

Группа: Участник
Сообщений: 690
Регистрация: 28.12.2012
Пользователь №: 3660

Спасибо сказали: 113 раз(а)




Репутация:   8  


Цитата
еще проблема с параметрами для инициализации. на одном форуме нашел, что это не возможно. там чел потом сделал политику фкнциионирования, которую и передавал в качестве единственного параметра в фабрику.

Почему же невозможно, можно в метод create передавать все нужные параметры.

Цитата
ЗЫ: да, мои гуглинья наводят меня на мысль, что это действительно никому не нужно и люди юзают другие способы. выходит что обычно такое рашется именно фабрикой?

Просто делать это в конструкторе - плодить лишние зависимости.

Тут надо подумать, кто является владельцем сессии и на него возложить заботу о создании, хранении и т.п. Самой сессии знать о центральном реестре и других сессиях на мой взгляд излишне.

Сообщение отредактировал lanz - 15.4.2015, 16:11
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Iron Bug
  опции профиля:
сообщение 16.4.2015, 9:17
Сообщение #7


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


может, я не совсем понимаю идею, но чем не устраивает make_shared, например?
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
lanz
  опции профиля:
сообщение 16.4.2015, 9:33
Сообщение #8


Старейший участник
****

Группа: Участник
Сообщений: 690
Регистрация: 28.12.2012
Пользователь №: 3660

Спасибо сказали: 113 раз(а)




Репутация:   8  


Цитата
может, я не совсем понимаю идею, но чем не устраивает make_shared, например?

После создания хочется выполнять еще какие-то обязательные действия, не только завернуть указатель.

Цитата
иначе, если этого не сделать, то выходит, что сессия будет какое-то время с незалогиненым пользователем.

Это как раз не страшно. Введите состояния сессии и отслеживайте их.
Просто если логинится в конструкторе, то как обрабатывать ошибки?
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Iron Bug
  опции профиля:
сообщение 16.4.2015, 13:20
Сообщение #9


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


один фиг непонятно, чего хотели получить в итоге. а можно пример использования класса и что конкретно не получается?
можно создавать shared_ptr и использовать инициализацию в конструкторе, при этом можно в конструктор передавать shared_ptr'ы на другие объекты. можно через enable_shared_from_this создавать weak_ptr на себя и потом возвращать shared_ptr, полученный из this, через какой-то метод.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
alexy
  опции профиля:
сообщение 16.4.2015, 13:44
Сообщение #10


Студент
*

Группа: Участник
Сообщений: 44
Регистрация: 4.8.2010
Пользователь №: 1931

Спасибо сказали: 0 раз(а)




Репутация:   0  


Цитата(lanz @ 16.4.2015, 10:33) *
Просто если логинится в конструкторе, то как обрабатывать ошибки?

ну, я ввел понятие токена. сессия без логина, не принимает никаких параметоров, сессия с логином, принимает строку токена, ну и не создается (throw login_error) если токен не верен.

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

я просто делаю клиента html, сделал виджетами. один может содержать другой, но есть root - это как бы <html></html> и у него несколько виджетов - <head> <body> и пара других. изначально сделал widget_ptr, и метод parent возращает widget_ptr, но оказалось, что так не получится если я хочу создать в конструкторе виджеты, то есть если он составной.
мне кажется как-то не правильно создавать в фабричном методе внутренние детали виджета.

Цитата(Iron Bug @ 16.4.2015, 14:20) *
один фиг непонятно, чего хотели получить в итоге. а можно пример использования класса и что конкретно не получается?
можно создавать shared_ptr и использовать инициализацию в конструкторе, при этом можно в конструктор передавать shared_ptr'ы на другие объекты. можно через enable_shared_from_this создавать weak_ptr на себя и потом возвращать shared_ptr, полученный из this, через какой-то метод.

да, я об этом писал в первом посте. мне не нравится shared_from_this() то есть если он public, то выходит что это реализация в интерфейсе. я делаю его protected. но в конструкторе все равно не получается. то есть получится, если сделать какой-нибудь метод, get_shared, тогда выйдет
auto sptr_ = new foo();
std::shared_ptr<foo> sptr = sptr_->get_shared();

а сам get_shared, после первого использования должен сделать shared_to_this.release(); или что-нибудь в этом духе, чтобы остался только weak_to_this...

по мне лучше что-нибудь в таком духе (в конструкторе)
// в заголовочном файле
// typedef boost::intrusive_ptr<widget_base> widget_ptr;

root_widget::root_widget() 
    : root_html_(new with_contenet()), body_(new with_content()), head_(new with_content())
{
    *root_html_ << *head_ << *body_;
    // shared_ptr здесь не передать
    //root_html _.parent(/*shared_from_*/this);

    // только пока с этим тоже облом - все равно будет вызванно intrusive_ptr_release
    // это баг в intrusive_ptr?
    boost::intrusive_ptr<widget_base> this_ptr (this, false);

    root_html_->parent(this_ptr); // здесь он преобразуется в weak_ptr
}

// в другом файле, где определяется with_content
widget_ptr with_content::parent() const
{
    return parent_.lock();
}


Сообщение отредактировал alexy - 16.4.2015, 13:46
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
lanz
  опции профиля:
сообщение 16.4.2015, 14:25
Сообщение #11


Старейший участник
****

Группа: Участник
Сообщений: 690
Регистрация: 28.12.2012
Пользователь №: 3660

Спасибо сказали: 113 раз(а)




Репутация:   8  


shared_ptr подразумевает некую семантику - когда shared_ptr является членом класса, это значит что класс совместно владеет этим объектом вместе с кем-то. Когда у вас указатель parent shared_ptr, значит потомок владеет своим родителем.
Вот если родитель владеет(shared_ptr а то и unique_ptr) своим потомком, а потомок ссылается (raw pointer) на своего родителя, то нет никаких противоречий.
Единственное чтобы время жизни родителя перекрывало время жизни ребенка.

Цитата
ну, я ввел понятие токена. сессия без логина, не принимает никаких параметоров, сессия с логином, принимает строку токена, ну и не создается (throw login_error) если токен не верен.

В случае исключения никогда не вызовется деструктор сессии.




И насчет возврата из фабрики
http://herbsutter.com/2013/05/30/gotw-90-solution-factories/

Сообщение отредактировал lanz - 16.4.2015, 14:10
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Iron Bug
  опции профиля:
сообщение 16.4.2015, 14:29
Сообщение #12


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


а зачем делать
auto sptr_ = new foo();
std::shared_ptr<foo> sptr = sptr_->get_shared();


если нужно делать сразу
std::shared_ptr<foo> sptr = shared_ptr<foo>(new foo());


проблема в том, что смешивать обычные указатели и shared_ptr хотя и можно чисто технически, но сильно не рекомендуется. ибо тогда вообще теряется смысл shared_ptr'а.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
alexy
  опции профиля:
сообщение 16.4.2015, 15:04
Сообщение #13


Студент
*

Группа: Участник
Сообщений: 44
Регистрация: 4.8.2010
Пользователь №: 1931

Спасибо сказали: 0 раз(а)




Репутация:   0  


Цитата(Iron Bug @ 16.4.2015, 15:29) *
если нужно делать сразу
std::shared_ptr<foo> sptr = shared_ptr<foo>(new foo());


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


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


тут проблема в моногопоточности.
например есть какой-то поток, который генерирует очень полезные цифры. он должен показывать их на клиенте (постоянное дуплексное соединение). у него есть слабый казатель на виджет, на какой-нибудь widget::label . когда у него появляется новое число, он берет сильный указатель, проверяет не здох ли виджет (смотри ли этот пользователь или уже закрыл все и свалил) и обновляет данные. в этот момент, пока обновляет, даже если пользователь закроет, виджет никуда не денется, т.к. указатель сильный. потом сильный казатель уходит и вижет может исчезнуть за время, пока его не нужно будет обновлять.

нет, дети не "владеют" родителем. они имееют слабый казатель на родителя. тут такое дело - если родителя прикончат в одном потоке, а другой поток в этот мемент работает с каким-нибудь ребенком и если ему нужно вызвать несколько методов... то у него могут прямо под носом убить объект с которым он работает и мутексы помочь не смогут, так как он берет указатель на объект и..
// плохо, если some_spin - raw pointer
int val = some_spin->value();
// тут другой поток убивает родителя...
if(!do_somthis(val)) some_spin->value(0);

а weak_ptr'а нет, если иметь дело с raw pointer
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
lanz
  опции профиля:
сообщение 16.4.2015, 15:15
Сообщение #14


Старейший участник
****

Группа: Участник
Сообщений: 690
Регистрация: 28.12.2012
Пользователь №: 3660

Спасибо сказали: 113 раз(а)




Репутация:   8  


1. При такой архитектуре вы можете удалить виджет из другого потока, создав его в одном. Фактически его удалит тот поток, который будет держать последний shared_ptr.

2. Обновлять данные из другого потока на виджете не безопасно. Вы уверенны что не придет событие отрисовки в середине вашего обновления?

Для многопоточной работы лучше исползуйте систему сообщений. Т.е. другой поток постит сообщение для отображения, а виджет смотрит - есть у него такой потомок или нет. Если нет, ничего страшного, если есть - информация безопасно обновляется. Разделяемые между потоками данные - это большая мина, с которой надо очень аккуратно обходится.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
alexy
  опции профиля:
сообщение 16.4.2015, 15:46
Сообщение #15


Студент
*

Группа: Участник
Сообщений: 44
Регистрация: 4.8.2010
Пользователь №: 1931

Спасибо сказали: 0 раз(а)




Репутация:   0  


Цитата(lanz @ 16.4.2015, 16:15) *
1. При такой архитектуре вы можете удалить виджет из другого потока, создав его в одном. Фактически его удалит тот поток, который будет держать последний shared_ptr.

2. Обновлять данные из другого потока на виджете не безопасно. Вы уверенны что не придет событие отрисовки в середине вашего обновления?

Для многопоточной работы лучше исползуйте систему сообщений. Т.е. другой поток постит сообщение для отображения, а виджет смотрит - есть у него такой потомок или нет. Если нет, ничего страшного, если есть - информация безопасно обновляется. Разделяемые между потоками данные - это большая мина, с которой надо очень аккуратно обходится.


1. ну да.. а что в этом такого?
#include <thread>
int main() {
    int * i = new int(0);
    std::thread th([i](){delete i;});
    th.join();
}


2. сам виджет thradsafe. то есть даже если придет, ничего страшного, максимум данные потеряются. ну, вообще, это сам пользователь если ступит. то есть виджет же не рисуется прямо на мониторе, он передается по сети, там, у клиента, только один поток. так что если он исправляет значение виджета, не дожидается ответа сервера и начинает что-то еще править, то.. ну это как нажать кнопку сохранить, потом еще текст начать править.. он ведь не сохранится, если не нажать еще раз.

мне кажется с очередью сообщений труднее. сейчас у меня виджет содержит данные и мутекс, также функционал от widget_base - слабый казатель на родителя, который и так thread_safe
сообщения они передают клиенту по thradsafe каналу, то есть там мутекс перед тем как оправить данные, так что пользователю каша не придет.


кстати, не понял что-то функционала boost::intrusive_ptr если в конструкторе ему передать (some_raw_pointer, false). это же вызовет ошибку в деструкторе его. или intrusive_ptr_release нужно делать if(counter && --counter == 0) delete p;
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
lanz
  опции профиля:
сообщение 16.4.2015, 18:17
Сообщение #16


Старейший участник
****

Группа: Участник
Сообщений: 690
Регистрация: 28.12.2012
Пользователь №: 3660

Спасибо сказали: 113 раз(а)




Репутация:   8  


Цитата
1. ну да.. а что в этом такого?

Ничего, если нет несинхронизированных сторонних эффектов.
Цитата
2. сам виджет thradsafe.

Ну это меняет все дело :lol:
Только нужно следить чтобы deadlockов не было. С очередью попроще.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Iron Bug
  опции профиля:
сообщение 17.4.2015, 11:24
Сообщение #17


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


обычная схема перерисовки данных - это чтение данных по сигналу обновления виджета. поток, получающий данные, складывает их в память. а поток, который отрисовывает виджет, берёт их оттуда. и не нужно никаких сильных-слабых указателей и прочих велосипедов.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение

2 страниц V   1 2 >
Быстрый ответОтветить в данную темуНачать новую тему
Теги
Нет тегов для показа


1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0


RSS Рейтинг@Mail.ru Текстовая версия Сейчас: 25.5.2025, 14:29