Всем привет. Давно тут не писал, так что немного стесняюсь...
Придумал я велосипед такой, хочу знать ващего мнения.
Есть класс, который наследуется от std::pair<key, указатель на самого себя>
ожидал я от него, что он будет сам себя в мапе хранить. О мапе своем все знать, и ключь свой при себе держать.
А лучше кодом напишу.
class Paired : public std::pair<int, Paired *>
{
std::map<int, Paired *> &owner_;
int &name_;
public:
Paired(int i, std::map<int, Paired *> &o)
: std::pair<int, Paired *>(i, this)
, owner_(o)
, name_(first)
{
}
std::string Name() const
{
if (name_ == 1) return"one";
if (name_ == 2) return"two";
if (name_ == 3) return"three";
return"none";
}
void Move()
{
owner_.erase(first);
name_ += 512;
owner_.insert(*this);
}
};
void test()
{
typedef std::map<int, Paired *> m_t;
// Paired<std::pair<int, void *> > p;
m_t m;
// 11111111111111
m.insert(Paired(1, m));
m.insert(Paired(2, m));
m.insert(Paired(3, m));
m.insert(Paired(4, m));
m.insert(Paired(5, m));
m.insert(Paired(6, m));
m.insert(Paired(7, m));
m.insert(Paired(8, m));
m.insert(Paired(9, m));
m.insert(Paired(10, m));
m.insert(Paired(11, m));
m.insert(Paired(12, m));
for (m_t::iterator i = m.begin(); i != m.end(); i++)
{
printf("%d %s\n", (*i).first, (*i).second->Name().c_str());
}
/// 222222222222222222
m_t::iterator mi = m.begin()
, mie = --m.end();
while (mi != mie)
{
m_t::iterator p = mi++;
(*p).second->Move();
}
(*mie).second->Move();
for (m_t::iterator i = m.begin(); i != m.end(); i++)
{
printf("%d %s\n", (*i).first, (*i).second->Name().c_str());
}
}
И самое удивительное что оно работает.
Эт только потом я вспомнил про слайсинг. Подумал о том что надо бы в мапе похорошему std::pair на мой класс заменить. но это не возможно.
Хотя с другой стороный если объекты будут хранится в другом каком-нить контейнере, а потом в мап попадать, не пофиг ли на слайсинг?
Ну и место 2, ваще уродство имхо. но ниче не поделаешь.
И что это за вилосипед я изобрел? И поедет ли он в дальний путь?
Здравствуйте, yatagarasu, Вы писали:
Y>Всем привет. Давно тут не писал, так что немного стесняюсь...
Y>Придумал я велосипед такой, хочу знать ващего мнения. Y>Есть класс, который наследуется от std::pair<key, указатель на самого себя> Y>ожидал я от него, что он будет сам себя в мапе хранить. О мапе своем все знать, и ключь свой при себе держать.
Посмотри на интрузивные контейнеры (boost::intrusive). Там ты как раз сможешь ноду для контейнера самодельную сделать.
плюсы:
— ключ дублироваться не будет
— объект к итератору кастуется
— содержащий контейнер можно получить по цепочке: to_iterator(obj) -> to_end_iterator(iterator) -> container_from_end(end_iterator)
Здравствуйте, yatagarasu, Вы писали:
Y>Придумал я велосипед такой, хочу знать ващего мнения. Y>Есть класс, который наследуется от std::pair<key, указатель на самого себя> Y>ожидал я от него, что он будет сам себя в мапе хранить. О мапе своем все знать, и ключь свой при себе держать. Y>И самое удивительное что оно работает.
Оно работает небось только под debug и то по счастливой случайности. Потому как при вставке происходит срезка объекта Paired до базового класса. Указатель при этом ссылается на созданный на стеке временный объект типа Paired. Так что это чистое везение что этот самый Paired не был переписан.
Здравствуйте, yatagarasu, Вы писали:
Y>Придумал я велосипед такой, хочу знать ващего мнения. Y>Есть класс, который наследуется от std::pair<key, указатель на самого себя> Y>ожидал я от него, что он будет сам себя в мапе хранить. О мапе своем все знать, и ключь свой при себе держать.
Изначально нерабочий подход. А если кто-то скажет тому map’у erase, а объекту об этом не скажет? А особенно в другой map пересадит?
Y> int &name_;
Не нужен. Раз мы наследуемся от std::pair, то first нам и так будет виден.
Создали временный объект, он запомнил указатель на себя; срезали его в std::pair в map’е, указатель скопировался; убили временный объект — указатель в pair остался указывать на труп. Итого, к этому моменту у нас есть map, содержащий 12 указателей на трупы.
Y> for (m_t::iterator i = m.begin(); i != m.end(); i++)
Y> {
Y> printf("%d %s\n", (*i).first, (*i).second->Name().c_str());
Y> }
А вот теперь начинается некромантия. «Взвод трупов, по порядку номеров — рассчитайсь!»
OK, если нам повезло, временные объекты создавались по разным адресам, а трупы ещё не успели разложиться. Первый. Второй. Третий. […] Двенадцатый, расчёт закончен.
Это всё, конечно, в предположении, что трупы каким-то неведомым образом продолжают не тухнуть. Потому что, будь я компилятор, я бы уже второго Paired положил на место уже мёртвого первого. И все временные переменные в методах erase и insert имеют полное право занять их место.
Y>И что это за вилосипед я изобрел? И поедет ли он в дальний путь?
Как только на кладбище построят что-нибудь полезное, а потом попытаются в очередной раз взводом зомби покомандовать, всё и упадёт.
Здравствуйте, sokel, Вы писали:
S>Здравствуйте, yatagarasu, Вы писали:
Y>>Всем привет. Давно тут не писал, так что немного стесняюсь...
Y>>Придумал я велосипед такой, хочу знать ващего мнения. Y>>Есть класс, который наследуется от std::pair<key, указатель на самого себя> Y>>ожидал я от него, что он будет сам себя в мапе хранить. О мапе своем все знать, и ключь свой при себе держать.
S>Посмотри на интрузивные контейнеры (boost::intrusive). Там ты как раз сможешь ноду для контейнера самодельную сделать. S>плюсы: S>- ключ дублироваться не будет S>- объект к итератору кастуется S>- содержащий контейнер можно получить по цепочке: to_iterator(obj) -> to_end_iterator(iterator) -> container_from_end(end_iterator)
S>Только вот аллокатор будет внешним.
Здравствуйте, Centaur, Вы писали:
C>Здравствуйте, yatagarasu, Вы писали:
Y>>Придумал я велосипед такой, хочу знать ващего мнения. Y>>Есть класс, который наследуется от std::pair<key, указатель на самого себя> Y>>ожидал я от него, что он будет сам себя в мапе хранить. О мапе своем все знать, и ключь свой при себе держать.
C>Изначально нерабочий подход. А если кто-то скажет тому map’у erase, а объекту об этом не скажет? А особенно в другой map пересадит?
тот сам себе будет злым буратиной. вообще этот велосипед такого неподразумевает. он еще недописан и там щиты чтобы стружка не вылетала и наклейки "отрубит пальцы если сунешь".
C>
Y>> int &name_;
C>
C>Не нужен. Раз мы наследуемся от std::pair, то first нам и так будет виден.
идея не в том чтобы наследоваться от паир, а в том чтобы использовать переменную класса как ключ.
как вариант мне потом посоветовали использовать std::set со своей функцией сравнения.
C>
C>Создали временный объект, он запомнил указатель на себя; срезали его в std::pair в map’е, указатель скопировался; убили временный объект — указатель в pair остался указывать на труп. Итого, к этому моменту у нас есть map, содержащий 12 указателей на трупы.
ох стар стал. не помню уже. не уверен доживут ли они до закрывающей скобки }. но это прототип.
C>
Y>> for (m_t::iterator i = m.begin(); i != m.end(); i++)
Y>> {
Y>> printf("%d %s\n", (*i).first, (*i).second->Name().c_str());
Y>> }
C>
C>А вот теперь начинается некромантия. «Взвод трупов, по порядку номеров — рассчитайсь!» C>OK, если нам повезло, временные объекты создавались по разным адресам, а трупы ещё не успели разложиться. Первый. Второй. Третий. […] Двенадцатый, расчёт закончен.
C>
C>Это всё, конечно, в предположении, что трупы каким-то неведомым образом продолжают не тухнуть. Потому что, будь я компилятор, я бы уже второго Paired положил на место уже мёртвого первого. C>И все временные переменные в методах erase и insert имеют полное право занять их место.
у вас ошибка. first и second будут перед owner_ и name_;
Y>>И что это за вилосипед я изобрел? И поедет ли он в дальний путь? C>Как только на кладбище построят что-нибудь полезное, а потом попытаются в очередной раз взводом зомби покомандовать, всё и упадёт.
не ответ на вопросы.
но кстате зомбями тож можно с пользой коммандовать. и важно об этом всегда помнить )
Здравствуйте, sokel, Вы писали:
S>Здравствуйте, yatagarasu, Вы писали:
Y>>Всем привет. Давно тут не писал, так что немного стесняюсь...
Y>>Придумал я велосипед такой, хочу знать ващего мнения. Y>>Есть класс, который наследуется от std::pair<key, указатель на самого себя> Y>>ожидал я от него, что он будет сам себя в мапе хранить. О мапе своем все знать, и ключь свой при себе держать.
S>Посмотри на интрузивные контейнеры (boost::intrusive). Там ты как раз сможешь ноду для контейнера самодельную сделать. S>плюсы: S>- ключ дублироваться не будет S>- объект к итератору кастуется S>- содержащий контейнер можно получить по цепочке: to_iterator(obj) -> to_end_iterator(iterator) -> container_from_end(end_iterator)
S>Только вот аллокатор будет внешним.
вот какого монстра я в итоге родил.
#include"fwd.h"#include <algorithm>
#include <boost/bind.hpp>
#include <boost/intrusive/set.hpp>
namespace test
{
template <typename V, typename P>
struct key_wrapper
{
V value_;
P &this_;
typename P::container_t &owner_;
key_wrapper(const V &v, P &p, typename P::container_t &c)
: value_(v)
, this_(p)
, owner_(c)
{
}
key_wrapper(const key_wrapper &c, P &p)
: value_(c.value_)
, this_(p)
, owner_(c.owner_)
{
}
key_wrapper &operator =(const key_wrapper &c)
{
value_ = c.value_;
return *this;
}
operator V() const
{
return value_;
}
V operator =(const V &v)
{
if (v != value_)
{
owner_.erase(this_);
value_ = v;
owner_.insert(this_);
}
return value_;
}
};
// template <template <> class C>class Paired : public boost::intrusive::set_base_hook<>
{
public:
// typedef C<Paired> container_t;typedef boost::intrusive::set<Paired> container_t;
private:
key_wrapper<int, Paired> name_;
public:
Paired(int i, container_t &o)
: name_(i, *this, o)
{
}
Paired(const Paired &c)
: name_(c.name_, *this)
{
log_print("copy.");
}
Paired &operator =(const Paired &c)
{
name_ = c.name_;
log_print("assign.");
return *this;
}
std::string Name() const
{
return Number(name_);
}
std::string Number(int name) const
{
if (name < 500)
{
if (name == 0) return"zero";
if (name == 1) return"one";
if (name == 2) return"two";
if (name == 3) return"three";
if (name == 4) return"four";
if (name == 5) return"five";
if (name == 6) return"six";
if (name == 7) return"seven";
if (name == 8) return"eight";
if (name == 9) return"nine";
if (name == 10) return"ten";
if (name == 11) return"eleven";
if (name == 12) return"twelve";
if (name == 13) return"thirteen";
if (name == 14) return"fourteen";
if (name == 15) return"fifteen";
if (name == 16) return"sixteen";
if (name == 17) return"seventeen";
if (name == 18) return"eighteen";
if (name == 19) return"nineteen";
}
else
{
return std::string("five hundred and ") + Number(name - 500);
}
return"none";
}
void Move()
{
name_ = name_ + 500;
}
friend bool operator< (const Paired &a, const Paired &b)
{
return a.name_ < b.name_;
}
};
Paired *get_pointer(const std::pair<int, Paired *> &p)
{
return p.second;
}
//[[[cog cog.outl("class %s" % test_name)]]]class Map
//[[[end]]]
{
public:
//[[[cog cog.outl("%s()" % test_name)]]]
Map()
//[[[end]]]
{
}
void test()
{
// typedef Paired<boost::intrusive::set> p_t;typedef Paired p_t;
typedef std::vector<p_t> v_t;
typedef boost::intrusive::set<p_t> m_t;
// p_t<std::pair<int, void *> > p;
v_t v;
m_t m;
v.push_back(p_t(1, m));
v.push_back(p_t(2, m));
v.push_back(p_t(3, m));
v.push_back(p_t(4, m));
v.push_back(p_t(5, m));
v.push_back(p_t(6, m));
v.push_back(p_t(7, m));
v.push_back(p_t(8, m));
v.push_back(p_t(9, m));
v.push_back(p_t(10, m));
v.push_back(p_t(11, m));
v.push_back(p_t(12, m));
log_print("Lets dance!\n");
for (v_t::iterator i = v.begin(); i != v.end(); i++)
{
m.insert(*i);
}
for (m_t::iterator i = m.begin(); i != m.end(); i++)
{
log_print("%s\n", (*i).Name().c_str());
}
// std::for_each(m.begin(), m.end()
// , boost::bind(&test::Paired::Move, _1));
// for (m_t::iterator i = m.begin(), ie = m.end(); i != ie;)
// {
// m_t::iterator p = i++;
// (*p).second->Move();
// }
m_t::iterator mi = m.begin()
, mie = --m.end();
while (mi != mie)
{
m_t::iterator p = mi++;
(*p).Move();
}
(*mie).Move();
for (m_t::iterator i = m.begin(); i != m.end(); i++)
{
log_print("%s\n", (*i).Name().c_str());
}
// const int i = 0;
// const int &ci = i;
// std::pair<const int &, int> cip(i, i);
// std::map<const int &, int> mip;
}
};
}
C>>Создали временный объект, он запомнил указатель на себя; срезали его в std::pair в map’е, указатель скопировался; убили временный объект — указатель в pair остался указывать на труп. Итого, к этому моменту у нас есть map, содержащий 12 указателей на трупы. Y>ох стар стал. не помню уже. не уверен доживут ли они до закрывающей скобки }. но это прототип.
Временные объекты живут до конца полного выражения. Не дольше.
C>>О ужас. Я это даже представить себе не могу, буду рисовать.
C>>// исходное положение
C>> 1 2 3 4 5 6 7 8 9 10 11 12
C>> x_x x_x x_x x_x x_x x_x x_x x_x x_x x_x x_x x_x
C>>^ |
C>>| | .------.
C>>| v v |
C>>| .-x_X----------------|------------.
C>>| | | | * | |
C>>| | owner_ | first | second | name_ |
C>>'----* | 1 | | * |
C>> '--------------------^--------|---'
C>> | |
C>> '--------'
C>>// остальных зомби разрисовывать не буду
C>>// порядок полей не соответствует реальному
[…] Y>у вас ошибка. first и second будут перед owner_ и name_;
Я где-то писал, что порядок полей реальный? По-моему, наоборот.