Russian Qt Forum

Программирование => С/C++ => Тема начата: Igors от Сентябрь 13, 2012, 15:32



Название: Массив std::set
Отправлено: Igors от Сентябрь 13, 2012, 15:32
Добрый день

Нужен массив std::set (ну или порожденных классов, указателей) каждый из которых хранит указатели на один и тот же класс, но использует свой функтор. Пример

Код
C++ (Qt)
std::set <MyClass *, MyFunctor1> theSet1;
..
std::set <MyClass *, MyFunctor2> theSet2;
..
std::set <MyClass *, MyFunctor3> theSet3;
 
Так нет проблем но приходится перебирать theSetxx. А хотелось бы так
Код
C++ (Qt)
for (int i = 0; i < 3; ++i) {
MyClass * data = theSet[i].find(val);
if (data)...
 

Спасибо


Название: Re: Массив std::set
Отправлено: Igors от Сентябрь 14, 2012, 00:09
Код:
    set<int> s = *(set<int> *)array[i];
Так Вы копируете все данные set, наверное Вы имели ввиду
Код
C++ (Qt)
   set<int> * s = (set<int> *)array[i];
 
Но так и компилиться не должно если set <int> нигде не объявлен и значит не "инстанциирован". А если объявить и как-то заткнуть рот компилятору, то все равно неясно как же он найдет нужный функтор, ведь метод find не виртуальный.



Название: Re: Массив std::set
Отправлено: VPS от Сентябрь 14, 2012, 07:39
Код
C++ (Qt)
set<int> * s = (set<int> *)array[i];
 
Но так и компилиться не должно если set <int> нигде не объявлен и значит не "инстанциирован". А если объявить и как-то заткнуть рот компилятору, то все равно неясно как же он найдет нужный функтор, ведь метод find не виртуальный.

Компилятор не ругается ???, но и пример мой на самом деле не корректен...
Так как в нем постоянно определяется указатель на множество вида: set<int, less<int> >. Поэтому то и поиск во втором контейнере и не проходит...


Название: Re: Массив std::set
Отправлено: V1KT0P от Сентябрь 14, 2012, 08:10
Нужен массив std::set (ну или порожденных классов, указателей) каждый из которых хранит указатели на один и тот же класс, но использует свой функтор.
Правильно ли я тебя понял: у тебя три отдельных массива из-за того что используются три разных своих функтора, а ты хочешь эти три отдельных массива запихнуть в один массив?
Тогда почему ты не можешь сделать базовый функтор, от которого будут наследоваться остальные функторы. А в массиве будешь хранить только указатель на базовый. Даже если ты используешь не свои функторы и нет возможности их менять, то можно написать свой функтор обертку. В эту обертку заворачивать функторы. При чем если сделать на шаблонах, то вообще любой функтор можно будет завернуть.


Название: Re: Массив std::set
Отправлено: Igors от Сентябрь 14, 2012, 12:54
Правильно ли я тебя понял: у тебя три отдельных массива из-за того что используются три разных своих функтора, а ты хочешь эти три отдельных массива запихнуть в один массив?
У меня не 3 отдельных массива, а 3 (реально больше) std::set. Все они работают с типом MyClass * (первый template аргумент) но отличаются функторами (второй template аргумент), т.е. все они имеют разный тип. 

Тогда почему ты не можешь сделать базовый функтор, от которого будут наследоваться остальные функторы. А в массиве будешь хранить только указатель на базовый.
А как я могу "хранить функтор"? Прошу исполнить


Название: Re: Массив std::set
Отправлено: V1KT0P от Сентябрь 14, 2012, 21:00
У меня не 3 отдельных массива, а 3 (реально больше) std::set. Все они работают с типом MyClass * (первый template аргумент) но отличаются функторами (второй template аргумент), т.е. все они имеют разный тип. 
А как я могу "хранить функтор"? Прошу исполнить
Вот сразу два варианта:
1) Путем наследования от интерфейса, это если можно править классы функторов.
2) Путем шаблонного наследования от интерфеса, это если нельзя править классы функторов.
Код
C++ (Qt)
#include <iostream>
#include <vector>
 
using namespace std;
 
class MyFunctor1
{
public:
   void operator() ()
   {
       cout << "Call functor MyFunctor1" << endl;
   }
};
 
class MyFunctor2
{
public:
   void operator() ()
   {
       cout << "Call functor MyFunctor2" << endl;
   }
};
 
class MyFunctor3
{
public:
   void operator() ()
   {
       cout << "Call functor MyFunctor3" << endl;
   }
};
 
class IMultiFunctor
{
public:
   virtual ~IMultiFunctor(){}
   void operator() ()
   {
       callback();
   }
protected:
   virtual void callback() = 0;
};
 
class MyMultiFunctor1: public IMultiFunctor
{
private:
   void callback()
   {
       cout << "Call functor MyMultiFunctor1" << endl;
   }
};
 
class MyMultiFunctor2: public IMultiFunctor
{
private:
   void callback()
   {
       cout << "Call functor MyMultiFunctor2" << endl;
   }
};
 
class MyMultiFunctor3: public IMultiFunctor
{
private:
   void callback()
   {
       cout << "Call functor MyMultiFunctor3" << endl;
   }
};
 
class ITemplateFunctor
{
public:
   virtual ~ITemplateFunctor(){}
   void operator() ()
   {
       callback();
   }
protected:
   virtual void callback() = 0;
};
 
template <class T>
class MyTemplateFunctor: public ITemplateFunctor
{
public:
   MyTemplateFunctor(T *functor): m_functor( functor ){}
 
private:
   T *m_functor;
   void callback()
   {
       (*m_functor)();
   }
};
 
int main()
{
   std::vector<IMultiFunctor*> listOfInterfaceFunctor;
   std::vector<ITemplateFunctor*> listOfTemplateFunctor;
 
   IMultiFunctor* newMultiFunctor;
 
   newMultiFunctor = new MyMultiFunctor1;
   listOfInterfaceFunctor.push_back( newMultiFunctor );
   newMultiFunctor = new MyMultiFunctor2;
   listOfInterfaceFunctor.push_back( newMultiFunctor );
   newMultiFunctor = new MyMultiFunctor3;
   listOfInterfaceFunctor.push_back( newMultiFunctor );
 
   ITemplateFunctor *newTemplateFunctor;
 
   newTemplateFunctor = new MyTemplateFunctor<MyFunctor1>( new MyFunctor1 );
   listOfTemplateFunctor.push_back( newTemplateFunctor );
   newTemplateFunctor = new MyTemplateFunctor<MyFunctor2>( new MyFunctor2 );
   listOfTemplateFunctor.push_back( newTemplateFunctor );
   newTemplateFunctor = new MyTemplateFunctor<MyFunctor3>( new MyFunctor3 );
   listOfTemplateFunctor.push_back( newTemplateFunctor );
 
   for( size_t i = 0; i < listOfInterfaceFunctor.size(); ++i ){
       (*listOfInterfaceFunctor[i])();
   }
 
   for( size_t i = 0; i < listOfTemplateFunctor.size(); ++i ){
       (*listOfTemplateFunctor[i])();
   }
 
   for( size_t i = 0; i < listOfInterfaceFunctor.size(); ++i ){
       delete listOfInterfaceFunctor[i];
   }
   listOfInterfaceFunctor.clear();
 
   for( size_t i = 0; i < listOfTemplateFunctor.size(); ++i ){
       delete listOfTemplateFunctor[i];
   }
   listOfTemplateFunctor.clear();
   cin.get();
   return 0;
}
 


Название: Re: Массив std::set
Отправлено: Igors от Сентябрь 14, 2012, 21:32
Вот сразу два варианта:
1) Путем наследования от интерфейса, это если можно править классы функторов.
2) Путем шаблонного наследования от интерфеса, это если нельзя править классы функторов.
Все функторы в моих руках и, как обычно, они очень просты. Приведенный Вами код понятен - да, можно сделать функторам общую базу, а можно и обернуть. Указатели на созданные экземпляры классов поместить в контейнер - нема квешнзов. Ну а std::set то где? Напомню что он определяется как
Код
C++ (Qt)
std::set <MyClass *, MyFunctor> theSet;
 
Где MyFunctor - тип (а не экземпляр)


Название: Re: Массив std::set
Отправлено: V1KT0P от Сентябрь 14, 2012, 22:07
Код:
[quote author=Igors link=topic=23034.msg162748#msg162748 date=1347647552]
std::set <MyClass *, MyFunctor> theSet;
Где MyFunctor - тип (а не экземпляр)
[/quote]
Не использовал set, теперь понял что не про то подумал. Пока что в голову пришло только сделать для set-а шаблонный интерфейс, в который вынести нужные функции которые наследуемые классы переопределят и который будет знать только о типе хранимого объекта. А уже наследованные будут знать класс который сравнивает. Тогда просто объявишь три set-а, приведешь их к интерфейсу и запихнешь в один массив.


Название: Re: Массив std::set
Отправлено: VPS от Сентябрь 14, 2012, 23:27
Нашёл интересную статью "Неявное приведение между родственными шаблонами С++" (http://elwood.su/?p=181)
И на базе её попробовал сделать пример (http://www.prog.org.ru/index.php?action=dlattach;topic=23034.0;attach=5336), правда поиск производится методом algorithm::find и слишком много приведений к базовому типу... Но видимо без этого не обойтись, так как в общем контейнере надо хранить объекты разных типов, преобразованных к базовому.

П.С.: если использовать метод find шаблонного класса set, то ничего не находит...


Название: Re: Массив std::set
Отправлено: VPS от Сентябрь 17, 2012, 21:31
Немного изменил предыдущий пример. Теперь поиск set::find() работает.
Исходник, здесь (http://www.prog.org.ru/index.php?action=dlattach;topic=23034.0;attach=5340).


Название: Re: Массив std::set
Отправлено: V1KT0P от Сентябрь 17, 2012, 22:01
Немного изменил предыдущий пример. Теперь поиск set::find() работает.
Исходник, здесь (http://www.prog.org.ru/index.php?action=dlattach;topic=23034.0;attach=5340).
А теперь добавь в класс Less или Greater вот такую банальную строчку:
Код
C++ (Qt)
int x;
И получи чудеса.


Название: Re: Массив std::set
Отправлено: VPS от Сентябрь 18, 2012, 07:22
Немного изменил предыдущий пример. Теперь поиск set::find() работает.
Исходник, здесь (http://www.prog.org.ru/index.php?action=dlattach;topic=23034.0;attach=5340).
А теперь добавь в класс Less или Greater вот такую банальную строчку:
Код
C++ (Qt)
int x;
И получи чудеса.

Получил... ???
Направьте меня на тему, что про это почитать (если правильно понял, то что-то не так с преобразованием к нужному типу).


Название: Re: Массив std::set
Отправлено: V1KT0P от Сентябрь 18, 2012, 08:42
Получил... ???
Направьте меня на тему, что про это почитать (если правильно понял, то что-то не так с преобразованием к нужному типу).
Чего там читать, добавили одну переменную и адреса всех остальных сдвинулись. Добавим новую функцию и адреса остальных функций сдвинутся. В результате сперва адреса функций и переменных совпадали и можно было C style cast-ом(Именно это ты и делал, только замаскировал статик кастом а так получил тот-же C style cast) безнаказанно. В реальном же приложении если делают новый класс, то он чем-то отличается. Вот и получается что чуть сдвинулись переменные и все начало неправильно работать. Поэтому в C++ специально сделали безопасный статик_каст, а за C style cast надо по рукам бить. Сколько я уже наслышался проблем из-за него.


Название: Re: Массив std::set
Отправлено: VPS от Сентябрь 18, 2012, 08:49
Чего там читать, добавили одну переменную и адреса всех остальных сдвинулись. Добавим новую функцию и адреса остальных функций сдвинутся. В результате сперва адреса функций и переменных совпадали и можно было C style cast-ом(Именно это ты и делал, только замаскировал статик кастом а так получил тот-же C style cast) безнаказанно. В реальном же приложении если делают новый класс, то он чем-то отличается. Вот и получается что чуть сдвинулись переменные и все начало неправильно работать. Поэтому в C++ специально сделали безопасный статик_каст, а за C style cast надо по рукам бить. Сколько я уже наслышался проблем из-за него.
Спасибо за разъяснение!


Название: Re: Массив std::set
Отправлено: Igors от Сентябрь 18, 2012, 12:36
Чего там читать, добавили одну переменную и адреса всех остальных сдвинулись. Добавим новую функцию и адреса остальных функций сдвинутся.
Думаю что можно добавить сколько угодно ф-ций (виртуальных или нет) и ничего не сдвинется.  А вариант вполне рабочий, спасибо, VPS . На мой взгляд маскировать приведение ни к чему, проще сделать один static_cast на вставке в вектор. И провериться в конструкторе функтора, напр
Код
C++ (Qt)
template<class T>
class Greater : public BaseF<T>
{
 Greater( void )
 {
  assert(sizeof(*this) == sizeof(BaseF<T>));
 }
 ...
};
 
Пробовал разобраться почему "сдвигается"
Код:
        struct _Rb_tree_impl : public _Node_allocator
        {
 _Key_compare _M_key_compare;
 _Rb_tree_node_base _M_header;
 size_type _M_node_count; // Keeps track of size of tree.
Так понял что _Key_compare - это тот самый функтор, поэтому если он имеет др размер  :'(
Ну в принципе обеспечить функторы одного размера - не проблема.


Название: Re: Массив std::set
Отправлено: DmitryM от Сентябрь 18, 2012, 16:22
Вот сразу два варианта:
1) Путем наследования от интерфейса, это если можно править классы функторов.
2) Путем шаблонного наследования от интерфеса, это если нельзя править классы функторов.
Все функторы в моих руках и, как обычно, они очень просты. Приведенный Вами код понятен - да, можно сделать функторам общую базу, а можно и обернуть. Указатели на созданные экземпляры классов поместить в контейнер - нема квешнзов. Ну а std::set то где? Напомню что он определяется как
Код
C++ (Qt)
std::set <MyClass *, MyFunctor> theSet;
 
Где MyFunctor - тип (а не экземпляр)
В продолжение идеи  V1KT0P

Код
C++ (Qt)
#include <set>
#include <vector>
#include <cassert>
 
class MyClass
{
};
 
class IFunctor
{
   public:
   virtual ~IFunctor(){}
   virtual bool operator() (const MyClass* left, const MyClass* right) const =0;
};
 
class ConcreteFunctor1: public IFunctor
{
   public:
   virtual bool operator() (const MyClass* left, const MyClass* right) const
   {
       return false;
   };  
};
 
class ConcreteFunctor2: public IFunctor
{
   public:
   virtual bool operator() (const MyClass* left, const MyClass* right) const
   {
       return false;
   };  
};
 
 
class ProxyFunctor
{
   public:
   ProxyFunctor():funct_(0)
   {
   }
 
   ProxyFunctor(IFunctor* functor):funct_(functor)
   {
   }
 
   bool operator() (const MyClass* left, const MyClass* right) const
   {
       assert(funct_);
       return (*funct_)(left, right);
   };  
 
   private:
   const IFunctor* funct_;
};
 
int main()
{
   std::vector<std::set<MyClass*, ProxyFunctor> > vect;
 
   vect.push_back( std::set<MyClass*, ProxyFunctor>(ProxyFunctor(new ConcreteFunctor1())));
   vect.push_back( std::set<MyClass*, ProxyFunctor>(ProxyFunctor(new ConcreteFunctor2())));
 
 
   return 0;
}
 
Идея понятна?


Название: Re: Массив std::set
Отправлено: Igors от Сентябрь 18, 2012, 17:32
Во блин, тут же все просто, чего я ломился в открытую дверь? :) Есть же возможность передать функтор в конструторе

Код
C++ (Qt)
#include <set>
#include <vector>
 
bool Compare1( int v0, int v1 ) { return v0 < v1; }
bool Compare2( int v0, int v1 ) { return v0 > v1; }
 
typedef bool (*func) (int, int);
 
int main()
{
typedef std::set <int, func> TSet;
std::vector <TSet *> vec;
 
vec.push_back(new TSet(Compare1));
vec.push_back(new TSet(Compare2));
 
for (int i = 0; i < 4; ++i) {
vec[0]->insert(i);
vec[1]->insert(i);
}
 
for (size_t i = 0; i < vec.size(); ++i) {
printf("[%ld]: ", i);
for (TSet::iterator it = vec[i]->begin(); it != vec[i]->end(); ++it)
printf("%d, ", *it);
printf("\n");
}
 
return 0;
}
 
Ну ничего, подучил матчасть  :)