Через внешний полиморфизм в контейнер помещаются объекты разных типов. Необходимо обеспечить возможность удаления из контейнера, зная исходный объект. Пока единственное, что смог придумать — использовать void *. Нет ли элегантного решения?
Упрощенный пример выглядит так:
class pure1
{
public:
void foo1() { std::cout << "pure1::foo1()" << std::endl; }
};
class pure2
{
public:
void foo2() { std::cout << "pure2::foo2()" << std::endl; }
};
class polymorphy
{
public:
virtual void foo() = 0;
// virtual bool is_equal(void *pure_) = 0;
};
template <>
void polymorphy_foo(pure1 &t)
{
t.foo1();
}
template <>
void polymorphy_foo(pure2 &t)
{
t.foo2();
}
template <typename T>
class polymorphy_adapter : public polymorphy
{
private:
T * _pure;
public:
polymorphy_adapter(T &t) : _pure(&t) {}
virtual void foo()
{
polymorphy_foo<T>(*_pure);
}
// virtual bool is_equal(void *pure_)
// {
// return (_pure == pure_);
// }
};
void test()
{
pure1 p1;
pure2 p2;
typedef boost::shared_ptr<polymorphy> item_type;
std::vector<item_type> v;
v.push_back(item_type(new polymorphy_adapter<pure1>(p1)));
v.push_back(item_type(new polymorphy_adapter<pure2>(p2)));
v[0]->foo();
v[1]->foo();
}
Здравствуйте, <Аноним>, Вы писали:
А>Через внешний полиморфизм в контейнер помещаются объекты разных типов. Необходимо обеспечить возможность удаления из контейнера, зная исходный объект. Пока единственное, что смог придумать — использовать void *. Нет ли элегантного решения?
Что-то в твоём коде не видно void*.
Может быть, ты имел в виду vector< shared_ptr<void> > с очевидной проблемой — что вообще можно делать с такими указателями, кроме удаления и кастинга?
А так, в принципе, нормальное решение.
Единственный момент — это как организовывать интерфейс
— на абстрактном базовом классе и шаблонном наследнике (твой подход)
— на таблицах функций в каждом экземпляре
— — рукодельных
— — стандартных
// using namespace boost или std::tr1 - по вкусу
///////////////////////
// нам нужна только foo
// нет смысла делать шаблон со специализациями, достаточно перегрузить
function<void()> getfoo(pure1* p) { return bind(&pure1::foo1, p); }
function<void()> getfoo(pure2* p) { return bind(&pure2::foo2, p); }
pure1 v1;
pure2 v2;
pure3 v3;
vector<function<void()> doers;
doers.push_back(getfoo(&v1));
doers.push_back(getfoo(&v2));
doers.push_back(bind(&pure3::foo123, &v3, 1, 2, 3)); // можно и руками собрать
doers[2](); // v3.foo3(1,2,3);
//////////////////////////
// нам нужен букет функций
// структура с именованными полями
struct funs
{
function<void()> foo;
function<int(int)> bar;
function<int(int,int)> buz;
};
funs getfuns(pure1* p)
{
funs fs;
fs.foo = bind(&pure1::foo1, p);
fs.bar = bind(&pure1::bar1, p, _1);
fs.buz = bind(&pure1::buz1, p, _1, _2);
return fs;
}
vector<funs> doers;
doers.push_back(getfuns(&v1));
doers[0].buz(1,2); // v1.buz1(1,2)
// или кортеж
enum { FOO, BAR, BUZ };
typedef tuple< function<void()>, function<int(int)>, function<int(int,int)> > funset;
// если функций всего две, можно взять не tuple, а pair
funset getfunset(pure1* p)
{
return make_tuple(
bind(&pure1::foo1,p),
bind(&pure1::bar1,p,_1),
bind(&pure1::buz1,p,_1,_2)
);
}
vector<funset> doers;
doers.push_back(getfunset(&v1);
doers[0].get<BAR>()(1); // v1.bar1(1)
Кстати, вместо голых указателей можно использовать и умные. Для общности.
function<void()> getfoo(shared_ptr<pure1> p) { return bind(&pure1::foo1, p); }
void nodelete(void*) {}
pure1 v1;
function<void()> f1 = getfoo(shared_ptr<pure1>(v1,nodelete)); // с этой функцией надо быть аккуратнее - она валидна, пока жив v1
function<void()> f2 = getfoo(shared_ptr<pure1>(new pure1)); // а с этой можно делать что угодно
... << RSDN@Home 1.2.0 alpha 4 rev. 1237>>
Здравствуйте, Кодт, Вы писали:
А>>Через внешний полиморфизм в контейнер помещаются объекты разных типов. Необходимо обеспечить возможность удаления из контейнера, зная исходный объект. Пока единственное, что смог придумать — использовать void *. Нет ли элегантного решения?
К>Что-то в твоём коде не видно void*.
Эта часть кода закомментирована:
// virtual bool is_equal(void *pure_) = 0;
// virtual bool is_equal(void *pure_)
// {
// return (_pure == pure_);
// }
Проблема в том, как без void * найти элемент контейнера (для последующего удаления), соответствующий, например p1.
К>А так, в принципе, нормальное решение.
А мне вот void * спать спокойно не даёт
К>Единственный момент — это как организовывать интерфейс
К>- на абстрактном базовом классе и шаблонном наследнике (твой подход)
К>- на таблицах функций в каждом экземпляре
К>- — рукодельных
К>- — стандартных
Реальные методы, которые я упростил до foo1() и foo2(), имеют в качестве аргументов итераторы, т.е. шаблонные, поэтому мой вариант предпочтительнее.
Здравствуйте, Аноним 955, Вы писали:
К>>Что-то в твоём коде не видно void*.
А9>Эта часть кода закомментирована:
А9>А9>// virtual bool is_equal(void *pure_)
А9>// {
А9>// return (_pure == pure_);
А9>// }
А9>
То есть, сравниваешь адреса, но не содержимое?
Тогда не вижу никакой проблемы. Ну разве что с множественным наследованием нужно быть аккуратным.
А9>Проблема в том, как без void * найти элемент контейнера (для последующего удаления), соответствующий, например p1.
А9>Реальные методы, которые я упростил до foo1() и foo2(), имеют в качестве аргументов итераторы, т.е. шаблонные, поэтому мой вариант предпочтительнее.
Нужно было с самого начала сказать, что у тебя мультиметод (полиморфный по двум аргументам — элементу контейнера и искомому значению).
Понятно, что во время исполнения шаблоны уже не помогут. Значит, нужно смотреть, как ту же самую задачу решать без шаблонов. Способов много — хоть двойная диспетчеризация, хоть вообще вариантные типы (boost::any). Но это уже надо на суть методов fooNNN смотреть.