Russian Qt Forum

Программирование => С/C++ => Тема начата: Igors от Октябрь 28, 2015, 09:27



Название: 2 мапы и вумные указатели
Отправлено: Igors от Октябрь 28, 2015, 09:27
Добрый день

Запутался с, казалось бы, простой задачей. Есть 2 мапы
Код
C++ (Qt)
std::map<Key1, Value *> map1;
std::map<Key2, Value *> map2;
 
Обе мапы хранят полный (и одинаковый) набор Value. В первой мапе эл-ты с разными ключами могут иметь одно и то же значение. Во второй все значения уникальны (а не только ключи). Др словами первая - чтобы собственно пользоваться, вторая - чтобы "валидировать".

Клиент, располагая 2-мя ключами, ищет 2 значения Value в map1 и map2. Если они ненулевые и совпали - все хорошо, возвращается найденный указатель. Иначе если эл-т не найден в map2 то новый Value должен быть создан и помещен в обе мапы. Если найден то замещает значение в map1[key1]. В любом случае если оказалось что старый (замещаемый) эл-т в map1 больше в ней никем не используется - значение должно быть удалено и вычеркнуто из map2. Др словами обе мапы все время должны хранить полный и одинаковый набор значений.

Как же здесь могут помочь вумные указатели?  :)

Спасибо


Название: Re: 2 мапы и вумные указатели
Отправлено: ssoft от Октябрь 28, 2015, 13:46
Можно хранить в map2 shared_ptr< Value >, а в map1 weak_ptr< Value >, тогда удаление Value из map2 приведет к обнулению указателя weak_ptr в map1.
Только это кажется нерациональным, так как пустые weak_ptr "зависнут" в map1, но все зависит от контекста решаемой задачи.


Название: Re: 2 мапы и вумные указатели
Отправлено: Igors от Октябрь 28, 2015, 14:51
Можно хранить в map2 shared_ptr< Value >, а в map1 weak_ptr< Value >, тогда удаление Value из map2 приведет к обнулению указателя weak_ptr в map1.
Только это кажется нерациональным, так как пустые weak_ptr "зависнут" в map1, но все зависит от контекста решаемой задачи.
Ну вот, есть же люди которые нормально отвечают, не ссылаясь на "плохая задача"! У меня удаление по Key1 (клиент отвалился), поэтому сделал наоборот, shared в первой, weak во второй (суть та же). А вот с удалением зависших ключей ничего не придумал :'( Не видно особой опасности, но как-то неаккуратно. Может в самом Value хранить итератор map2? Хорошего впечатления не производит... 


Название: Re: 2 мапы и вумные указатели
Отправлено: ssoft от Октябрь 28, 2015, 15:20

Может в самом Value хранить итератор map2? Хорошего впечатления не производит... 
[/quote]

После изменения состава map2, хранимый итератор может стать невалидным.


Название: Re: 2 мапы и вумные указатели
Отправлено: Igors от Октябрь 29, 2015, 06:36
После изменения состава map2, хранимый итератор может стать невалидным.
Для мапы остается валидным пока сам элемент не удален. Все равно коряво


Название: Re: 2 мапы и вумные указатели
Отправлено: Racheengel от Октябрь 29, 2015, 09:49
По моему, указатели тут не помогут. Лучше дедовским методом прибить значение в  map2 и не париться...


Название: Re: 2 мапы и вумные указатели
Отправлено: Old от Октябрь 29, 2015, 09:57
По моему, указатели тут не помогут. Лучше дедовским методом прибить значение в  map2 и не париться...
А если удаляемые элементы кому-то еще нужны, а мы их будем грохать?
Тут умные указатели как раз очень нужны.


Название: Re: 2 мапы и вумные указатели
Отправлено: Igors от Октябрь 29, 2015, 10:06
По моему, указатели тут не помогут. Лучше дедовским методом прибить значение в  map2 и не париться...
Т.е. пробегаться по map2 и мочить всех с пустыми значениями? А в какой момент это делать?


Название: Re: 2 мапы и вумные указатели
Отправлено: Old от Октябрь 29, 2015, 10:19
А в какой момент это делать?

У меня удаление по Key1 (клиент отвалился)

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


Название: Re: 2 мапы и вумные указатели
Отправлено: Racheengel от Октябрь 29, 2015, 11:29
По моему, указатели тут не помогут. Лучше дедовским методом прибить значение в  map2 и не париться...
А если удаляемые элементы кому-то еще нужны, а мы их будем грохать?
Тут умные указатели как раз очень нужны.

Я так понял, что если значения в мапе больше нет - его можно убить, т.к. оно никому не нужно.


Название: Re: 2 мапы и вумные указатели
Отправлено: Igors от Октябрь 29, 2015, 11:55
Я так понял, что если значения в мапе больше нет - его можно убить, т.к. оно никому не нужно.
И даже нужно. Но как и, главное, когда это делать? Удаление клиента по Key1 ни о чем не говорит, ведь могут быть другие, юзающие тот же Value


Название: Re: 2 мапы и вумные указатели
Отправлено: m_ax от Октябрь 29, 2015, 12:13
У shared_ptr есть метод unique: http://en.cppreference.com/w/cpp/memory/shared_ptr/unique (http://en.cppreference.com/w/cpp/memory/shared_ptr/unique)
а также use_count.


Название: Re: 2 мапы и вумные указатели
Отправлено: Igors от Октябрь 29, 2015, 14:10
У shared_ptr есть метод unique: http://en.cppreference.com/w/cpp/memory/shared_ptr/unique (http://en.cppreference.com/w/cpp/memory/shared_ptr/unique)
а также use_count.
И что, как я найду удаляемый в map2? На момент удаления имеется только Key1. Неиспользуемый может удаляться и в момент создания нового. Тогда есть оба ключа, но это не помогает - нашли Value по Key1, а по Key2 нет.

Смысл этой конструкции 2 мапов: есть кеши рисования (Value) которые могут шариться. Напр 2 рисуемых объекта шарят один кеш, 2 ключа (Key1) ссылаются на 1 Value. Теперь юзер что-то изменил в одном из рисуемых объектов, и теперь уже нужно 2 кеша. Вот для этого и нужна map2. Если по ключу Key2 (параметры влияющие на рисовние) ничего не найдено в map2, то новый кеш должен быть создан и мапы обновлены.


Название: Re: 2 мапы и вумные указатели
Отправлено: Old от Октябрь 29, 2015, 14:51
Смысл этой конструкции 2 мапов: есть кеши рисования (Value) которые могут шариться. Напр 2 рисуемых объекта шарят один кеш, 2 ключа (Key1) ссылаются на 1 Value. Теперь юзер что-то изменил в одном из рисуемых объектов, и теперь уже нужно 2 кеша. Вот для этого и нужна map2. Если по ключу Key2 (параметры влияющие на рисовние) ничего не найдено в map2, то новый кеш должен быть создан и мапы обновлены.
Как то это напоминает implicit sharing, но странно реализованным.


Название: Re: 2 мапы и вумные указатели
Отправлено: Авварон от Октябрь 29, 2015, 23:46
Плохо соображаю под вечер, но не мультииндекс ли это?


Название: Re: 2 мапы и вумные указатели
Отправлено: Igors от Октябрь 30, 2015, 07:43
Как то это напоминает implicit sharing, но странно реализованным.
Плохо соображаю под вечер, но не мультииндекс ли это?
Хмм.. не знаю. Задача простая

- клиент (объект с ключом Key1) хочет рисоваться. Он знает как себя сейчас рисовать (Key2). Надо найти подходящий кеш рисования по Key2 или создать новый. Разумеется неиспользуемые кеши должны удаляться. Как и в случае если клиент удален или выключен (в этот момент Key2 нет). Вот собственно и все.

Есть др решение, без мапов - пожалуйста, предлагайте.


Название: Re: 2 мапы и вумные указатели
Отправлено: Old от Октябрь 30, 2015, 08:39
Все равно не очень понятно назначение этих мапов.
Вот класс клиента, он содержит указатель на кеш. Если клонировать клиента, то они с клоном будут разделять кеш. Если один из них, захочет поменять кеш, он всегда сможет присвоить указателю новый объект и если кеш перестанет быть нужным он удалиться.
Код
C++ (Qt)
class Client
{
public:
   void draw()
   {
       if( !m_cache )
       {
           m_cache = make_shared<DrawCache>();
           forming( m_cache );
       }
       paint( m_cache );
   }
 
private:
   shared_ptr<DrawCache> m_cache;
};
 


Название: Re: 2 мапы и вумные указатели
Отправлено: Igors от Октябрь 30, 2015, 13:35
Все равно не очень понятно назначение этих мапов.
Вот класс клиента, он содержит указатель на кеш. Если клонировать клиента, то они с клоном будут разделять кеш. Если один из них, захочет поменять кеш, он всегда сможет присвоить указателю новый объект и если кеш перестанет быть нужным он удалиться.
Подходящий кеш может уже иметься на момент создания клиента или отрисовки
Он знает как себя сейчас рисовать (Key2). Надо найти подходящий кеш рисования по Key2 или создать новый


Название: Re: 2 мапы и вумные указатели
Отправлено: m_ax от Октябрь 30, 2015, 14:21
Может тогда одного мапа достаточно:
Код
C++ (Qt)
typedef implicit_sharing<cache> shared_cache;
typedef std::pair<shared_cache, shared_cache> pair_t;
 
std::map<key, pair_t> map;
 
shared_cache cacheA;
 
map[keyA] = make_pair(cacheA, cacheA);
...
 


Название: Re: 2 мапы и вумные указатели
Отправлено: Old от Октябрь 30, 2015, 14:31
Подходящий кеш может уже иметься на момент создания клиента или отрисовки
Т.е. это просто справочник известных кешей?

Вот набросал немного кода:
Код
C++ (Qt)
#include <memory>
#include <map>
#include <iostream>
 
using namespace std;
 
struct DrawCache
{
explicit DrawCache( int key2 ) : m_key2( key2 )
{
cout << "Contruct DrawCache = " << m_key2 << endl;
}
 
~DrawCache()
{
cout << "Destruct DrawCache = " << m_key2 << endl;
}
 
int m_key2;
};
 
typedef shared_ptr<DrawCache> DrawCachePtr;
 
class Client
{
public:
explicit Client( int key2 )
{
DrawCachePtr val = m_caches[ key2 ].lock();
if( !val )
{
val = make_shared<DrawCache>( key2 );
m_caches[ key2 ] = val;
}
setCache( val );
}
 
~Client()
{
setCache( DrawCachePtr() );
}
 
void changeCache( const Client &other )
{
setCache( other.m_cache );
}
 
void draw()
{
paint( m_cache );
}
 
static void dumpCaches()
{
cout << "-----------------------------------------------------" << endl;
for( const auto &entry : m_caches )
{
DrawCachePtr cache = entry.second.lock();
 
cout << "Cache key2 = " << entry.first << " -> value = " << cache << endl;
}
}
 
protected:
void setCache( const DrawCachePtr &cache )
{
// Если это последний указатель на данный кеш, удаляем его из справочника
if( m_cache && m_cache.unique() )
m_caches.erase( m_cache->m_key2 );
 
m_cache = cache;
}
 
void paint( const DrawCachePtr &/*cache*/ )
{
}
 
private:
DrawCachePtr m_cache;
 
static map<int, weak_ptr<DrawCache>> m_caches;
};
 
map<int, weak_ptr<DrawCache>> Client::m_caches;
 
int main( int, char ** )
{
{
Client::dumpCaches();
Client cli1( 1 );
Client::dumpCaches();
Client cli2( 1 );
Client::dumpCaches();
Client cli3( 2 );
Client::dumpCaches();
cli2.changeCache( cli3 );
Client::dumpCaches();
cli1.changeCache( cli3 );
Client::dumpCaches();
}
Client::dumpCaches();
 
return 0;
}
 

Цитировать
-----------------------------------------------------
Contruct DrawCache = 1
-----------------------------------------------------
Cache key2 = 1 -> value = 0x13bac70
-----------------------------------------------------
Cache key2 = 1 -> value = 0x13bac70
Contruct DrawCache = 2
-----------------------------------------------------
Cache key2 = 1 -> value = 0x13bac70
Cache key2 = 2 -> value = 0x13bacd0
-----------------------------------------------------
Cache key2 = 1 -> value = 0x13bac70
Cache key2 = 2 -> value = 0x13bacd0
Destruct DrawCache = 1
-----------------------------------------------------
Cache key2 = 2 -> value = 0x13bacd0
Destruct DrawCache = 2
-----------------------------------------------------


Название: Re: 2 мапы и вумные указатели
Отправлено: Igors от Октябрь 30, 2015, 16:42
Может тогда одного мапа достаточно:
Код
C++ (Qt)
typedef implicit_sharing<cache> shared_cache;
typedef std::pair<shared_cache, shared_cache> pair_t;
 
std::map<key, pair_t> map;
 
shared_cache cacheA;
 
map[keyA] = make_pair(cacheA, cacheA);
...
 

??? Ничего не понял  :)

Т.е. это просто справочник известных кешей?

Вот набросал немного кода:
Хранить шаред на кеш прямо в клиенте неудачно. Напр карта может не поддерживать кеши или потребовалось их все выключить, или один клиент может иметь 2 кеша. Вообще в подробности рисования клиента лучше не посвящать, пусть он только даст нужные рисованию данные. Вот и приходим к еще одной (первой) мапе, где Key1 - просто (неперемещаемый) адрес клиента (ну почти).


Название: Re: 2 мапы и вумные указатели
Отправлено: Old от Октябрь 30, 2015, 19:16
Хранить шаред на кеш прямо в клиенте неудачно.
Ну это спорно. ;)

Напр карта может не поддерживать кеши или потребовалось их все выключить, или один клиент может иметь 2 кеша.
Никаких проблем с этим нет. Можно объявить массив кешей, если он не нужен/не поддерживается не заполнять его. Можно сделать иерархию классов для разных клиентов: не поддерживает кеш, поддерживает кеш и т.д. Много всего можно придумать.

пусть он только даст нужные рисованию данные. Вот и приходим к еще одной (первой) мапе, где Key1 - просто (неперемещаемый) адрес клиента (ну почти).
Ok, давайте определимся, какие операции нужны с какими исходными данными?
DrawCachePtr Manager::newCache()
DrawCachePtr Manager::getCache( int keyClient )
void Manager::removeCache( ??? )


Название: Re: 2 мапы и вумные указатели
Отправлено: Igors от Октябрь 31, 2015, 02:55
Никаких проблем с этим нет. Можно объявить массив кешей, если он не нужен/не поддерживается не заполнять его. Можно сделать иерархию классов для разных клиентов: не поддерживает кеш, поддерживает кеш и т.д. Много всего можно придумать.
Конечно можно, но весь этот код начинает литься в клиента, а там он совершенно неуместен. Клиент не должен ничего знать от том кто и как его кеширует, это не его дело. Даже хранение Key2 в клиенте плохо, т.к. Key2 это большой набор данных с массой подробностей рисования. Именно на рисовании он и формируется - вот и пусть хранится только во второй мапе.

Говоря о др решениях я имел ввиду нечто принципиально другое чем "шаред + наблюдающий weak". А иначе нет смысла ломать логику классов из-за технической детали. Можно хотя бы просто при каждом получении кеша (и отключении клиента) тупо проходиться по второй мапе удаляя сдохших weak. В данном случае это приемлемо т.к. число эл-тов в ней невелико. Собственно никто не мешает записать что-то в кеш и в деструкторе кеша это использовать - но вот не пока соображу что и как.


Название: Re: 2 мапы и вумные указатели
Отправлено: Old от Октябрь 31, 2015, 07:54
Конечно можно, но весь этот код начинает литься в клиента, а там он совершенно неуместен. Клиент не должен ничего знать от том кто и как его кеширует, это не его дело. Даже хранение Key2 в клиенте плохо, т.к. Key2 это большой набор данных с массой подробностей рисования. Именно на рисовании он и формируется - вот и пусть хранится только во второй мапе.
Я не знаю подробностей вашей задачи. Считаете что не должен, пусть будет так.

Собственно никто не мешает записать что-то в кеш и в деструкторе кеша это использовать - но вот не пока соображу что и как.
Если вы напишите примерное api на эту систему кеширования (как я просил в прошлом сообщении), тогда можно будет что-то советовать.


Название: Re: 2 мапы и вумные указатели
Отправлено: Igors от Октябрь 31, 2015, 18:10
Если вы напишите примерное api на эту систему кеширования (как я просил в прошлом сообщении), тогда можно будет что-то советовать.
Для меня API - это вещь "архитектурная", поэтому любой набросок неизбежно потянет длинные пояснения специфики, что не было целью данной темы. Лучше сосредоточиться на деталях реализации, напр

- вот есть вполне нормальная схема: shared + weak, оба хранятся в мапах. Можем добавлять любые данные к ключам и значениям обеих мапов. Как лучше всего обеспечить удаление "отжившего" weak значения? Конечно "можно как-то обойтись без одной из мапов" - но за это придется заплатить чем-то другим. Зачем, разве удаление weak - нерешаемая задача?


Название: Re: 2 мапы и вумные указатели
Отправлено: Old от Октябрь 31, 2015, 18:17
Я спросил про API для того, что если у вас есть функция или метода типа freeCkient( int keyClient ), которая удаляет информацию о клиенте из первой map, то чистить указатель во второй map можно из него. Если его нет, то можно сделать специальный контейнер для кеша и чистить вторую map из его деструктора.


Название: Re: 2 мапы и вумные указатели
Отправлено: Old от Октябрь 31, 2015, 18:29
Специальный контейнер это не коллекция для объектов кеша, а класс обертка над указателем на данные кеша.


Название: Re: 2 мапы и вумные указатели
Отправлено: Igors от Ноябрь 01, 2015, 08:24
Я спросил про API для того, что если у вас есть функция или метода типа freeCkient( int keyClient ), которая удаляет информацию о клиенте из первой map, то чистить указатель во второй map можно из него.
Конечно такой метод есть, его просто не может не быть. Но это не единственное место удаления кеша. К тому же используется QSharedPointer который use_count не имеет, а С++11 в проекте, увы - пока не используется, что зависит не от меня. И надо где-то хранить Key2 (ключ-монстр). Словом - тут немалые трудности.

Если его нет, то можно сделать специальный контейнер для кеша и чистить вторую map из его деструктора.
Вот это интересно, в рамках 2 мапов (класса где они сидят) у меня никаких ограничений нет, он кешированию и посвящен.


Название: Re: 2 мапы и вумные указатели
Отправлено: Old от Ноябрь 01, 2015, 09:33
Конечно такой метод есть, его просто не может не быть. Но это не единственное место удаления кеша.
Нам нужно не само удаление кеша, а момент удаления клиента из map1. В этот момент может произойти удаление неиспользуемого кеша и необходимость почистить map2.

К тому же используется QSharedPointer который use_count не имеет
Печально. Будем ждать появления такого функционала где нибудь в Qt6. Они QEnableSharedFromThis умудрились сделать только в Qt5.4, хотя QSharedPointer у них появился еще в Qt4.5. :)

И надо где-то хранить Key2 (ключ-монстр). Словом - тут немалые трудности.
Без определения уникальности в этом нет смысла. Проще после каждого удаления клиента из map1 пробегаться по map2 и удалять опустевшие указатели.

Вот это интересно, в рамках 2 мапов (класса где они сидят) у меня никаких ограничений нет, он кешированию и посвящен.
Имелся ввиду контейнер для указателя на данные кеша. При удалении этого объекта (а это случается, когда из map1 удаляется последний клиент его использующий) он в деструкторе подчищал map2.
Но если у вас специальный метод, то проще все это сделать в нем.


Название: Re: 2 мапы и вумные указатели
Отправлено: _Bers от Ноябрь 04, 2015, 00:25
Добрый день

Запутался с, казалось бы, простой задачей. Есть 2 мапы
Код
C++ (Qt)
std::map<Key1, Value *> map1;
std::map<Key2, Value *> map2;
 
Обе мапы хранят полный (и одинаковый) набор Value. В первой мапе эл-ты с разными ключами могут иметь одно и то же значение. Во второй все значения уникальны (а не только ключи). Др словами первая - чтобы собственно пользоваться, вторая - чтобы "валидировать".

Клиент, располагая 2-мя ключами, ищет 2 значения Value в map1 и map2. Если они ненулевые и совпали - все хорошо, возвращается найденный указатель. Иначе если эл-т не найден в map2 то новый Value должен быть создан и помещен в обе мапы. Если найден то замещает значение в map1[key1]. В любом случае если оказалось что старый (замещаемый) эл-т в map1 больше в ней никем не используется - значение должно быть удалено и вычеркнуто из map2. Др словами обе мапы все время должны хранить полный и одинаковый набор значений.

Как же здесь могут помочь вумные указатели?  :)

Спасибо

std::set<значений>
и std::map<ключ, итератор значений>