Рефакторинг или массив указателей на члены
От: korzhik Россия  
Дата: 21.03.05 13:26
Оценка:
Здравствуйте,

очень не нравится такой код:
void Class::sort_recordset(int num)
{
  switch (num)
  {
  case 0:
    std::sort(m_recordset.begin(), m_recordset.end(), make_comparer(&Data::data_id));
    break;
  case 1:
    std::sort(m_recordset.begin(), m_recordset.end(), make_comparer(&Data::name));
    break;
  //....
  case 18:
    std::sort(m_recordset.begin(), m_recordset.end(), make_comparer(&Data::top));
    break;
  default: assert(!"wow!");
  }

make_comparer это производящая функция, которая возвращает функтор для сравнения членов структуры. data_id, name, top это константные функции ничего не принимающие и возвращающии поля структуры, естественно разного типа.

Ну в общем наверно все поняли что этот код сортировки контейнера структур Data по разным полям.

Хотелось бы чего нибудь такого:
void Class::sort_recordset(int num)
{
  std::sort(m_recordset.begin(), m_recordset.end(), make_comparer(member_table[num]));
}


Есть какие нибудь мысли?
Re: Рефакторинг или массив указателей на члены
От: Gorn  
Дата: 21.03.05 14:01
Оценка:
Здравствуйте, korzhik, Вы писали:

K>Хотелось бы чего нибудь такого:

K>
K>void Class::sort_recordset(int num)
K>{
K>  std::sort(m_recordset.begin(), m_recordset.end(), make_comparer(member_table[num]));
K>}
K>


Как вариант — можно реализовать тот же перебор внутри make_comparer.

Либо инкапсулировать этот код, сделав фабрику для создания объектов-функторов:

class ComparerFactory
{
public:
    static Functor Create (int num);
}


Тогда можно будет написать:

void Class::sort_recordset(int num)
{
  std::sort(m_recordset.begin(), m_recordset.end(), ComparerFactory.Create(num));
}
Re: Рефакторинг или массив указателей на члены
От: Анатолий Широков СССР  
Дата: 21.03.05 14:06
Оценка: 26 (2)
struct foo
{
public:
    int d;
    std::string s;
};

struct comparator;
struct comparator_proxy
{
    comparator const *ptr;
public:
    comparator_proxy(comparator const *ptr) : ptr(ptr) {}
    bool operator()(foo const &a, foo const &b) const;
};

struct comparator
{
    virtual ~comparator() {}
    virtual bool operator()(foo const &, foo const &) const = 0;
    comparator_proxy proxy() const
    {
        return comparator_proxy(this);
    }
};

bool comparator_proxy::operator()(foo const &a, foo const &b) const
{
    return (*ptr)(a, b);
}


template<typename T>
struct member_comparator : comparator
{
    T foo::*data;

    explicit member_comparator(T foo::*data) : data(data) {}
    
    bool operator()(foo const &a, foo const &b) const 
    {
        return a.*data < b.*data;      
    }
};


template<typename T>
std::auto_ptr<comparator> make_member_comparator(T foo::*ptr)
{
    return std::auto_ptr<comparator>(new member_comparator<T>(ptr));
}

struct boo
{
    std::vector<foo> arr;

    void sort_by(int num)
    {
        static std::auto_ptr<comparator> member[] = {
            make_member_comparator(&foo::d),
            make_member_comparator(&foo::s),
        };
        std::sort(arr.begin(), arr.end(), member[num]->proxy());
    }
};
Re: Рефакторинг или массив указателей на члены
От: ssm Россия  
Дата: 21.03.05 14:10
Оценка: 26 (2) +1
Здравствуйте, korzhik, Вы писали:



K>Есть какие нибудь мысли?


вектор функторов?


std::vector<boost::function<bool(Data &lh, Data &rh)> > comparers;
comparers.reserve(sz);

comparers.push_back(make_comparer(&Data::top));
comparers.push_back(make_comparer(&Data::data_id));
//...
Re[2]: Рефакторинг или массив указателей на члены
От: korzhik Россия  
Дата: 21.03.05 14:12
Оценка:
Здравствуйте, Gorn, Вы писали:

G>Как вариант — можно реализовать тот же перебор внутри make_comparer.

нельзя

G>Либо инкапсулировать этот код, сделав фабрику для создания объектов-функторов:


G>
G>class ComparerFactory
G>{
G>public:
G>    static Functor Create (int num);
G>}
G>


Такие варианты я рассматривал.
Все они сводятся к тому чтобы убрать этот здоровый switch куда подальше.
Я же хочу чтобы у меня был массив указателей на члены, чтобы по индексу их извлекать.

member member_table[] = 
{
    long        (Data::*i1)()const,
    std::string (Data::*i2)()const,
    float        (Data::*i3)()const,
    std::string (Data::*i4)()const,
    std::string (Data::*i5)()const,
    std::string (Data::*i6)()const,
      //...
};

в С++ это невозможно.
Сдаётся мне что от свитча не избавиться никак.
struct member_table 
{
    long        (Data::*i1)()const;
    std::string (Data::*i2)()const;
    float        (Data::*i3)()const;
    std::string (Data::*i4)()const;
    std::string (Data::*i5)()const;
    std::string (Data::*i6)()const;
      //...

      R operator[](size_t)  // что возвращать?
      {
        // здесь switch
      }
};


Ну и чёрт с ним. Всё работает как часики и слава богу.

Мне уже стала просто интересна задача создания такого массива. Или его подобия.
Re[2]: Рефакторинг или массив указателей на члены
От: ssm Россия  
Дата: 21.03.05 14:12
Оценка:
Здравствуйте, ssm, Вы писали:


std::vector<boost::function<bool(const Data &lh, const Data &rh) const> > comparers;
Re[2]: Рефакторинг или массив указателей на члены
От: korzhik Россия  
Дата: 21.03.05 15:02
Оценка:
Здравствуйте, Анатолий Широков, Вы писали:

// суперский код поскипан


Спасибо. Очень понравилось. Однозначно пойдёт в коллекцию фич.
Что интересно, Comeau компилит без проблем, а VC7.1 при инстанцировании шаблона member_comparator типом std::string foo::* начинает ругаться на оператор меньше для reverse_iterator
Пока не понял в чём дело, но думаю сам разберусь.
Re[2]: Рефакторинг или массив указателей на члены
От: korzhik Россия  
Дата: 21.03.05 15:05
Оценка:
Здравствуйте, ssm, Вы писали:

ssm>вектор функторов?


ssm>
ssm>std::vector<boost::function<bool(Data &lh, Data &rh)> > comparers;
ssm>comparers.reserve(sz);

ssm>comparers.push_back(make_comparer(&Data::top));
ssm>comparers.push_back(make_comparer(&Data::data_id));
ssm>//...
ssm>


Спасибо! В таких случаях остаётся только развести руками и сказать: "Как же я сам не догадался!?"
Я просто зациклился на массиве указателей на члены и поэтому про массив функторов даже не подумал.
Re: Рефакторинг или массив указателей на члены
От: Кодт Россия  
Дата: 21.03.05 19:23
Оценка:
Здравствуйте, korzhik, Вы писали:

boost::function< bool(*)(const Data&, const Data&) >
?
Перекуём баги на фичи!
Re[2]: Рефакторинг или массив указателей на члены
От: Warturtle  
Дата: 22.03.05 09:11
Оценка: +1
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, korzhik, Вы писали:


К>boost::function< bool(*)(const Data&, const Data&) >

К>?
Раз пошла такая тотальная сортировка, то есть boost::multi_index. Как говорится: "жизнь без геморроя".
Re[3]: Рефакторинг или массив указателей на члены
От: korzhik Россия  
Дата: 22.03.05 09:18
Оценка:
Здравствуйте, Warturtle, Вы писали:

W>Раз пошла такая тотальная сортировка, то есть boost::multi_index. Как говорится: "жизнь без геморроя".


Я его уже использую
Но мне не хочется хранить 18 индексов для сортировки. Я храню три индекса для фильтрации (equal_range), а по остальным полям сортирую сам.
Re: Рефакторинг или массив указателей на члены
От: sadomovalex Россия http://sadomovalex.blogspot.com
Дата: 22.03.05 12:04
Оценка: 1 (1)
Здравствуйте, korzhik, Вы писали:

K>Есть какие нибудь мысли?


Предложу еще одно решение:
struct data
{
    int data_id;
    double name;
};

#define MAKE_NAME(name, field) name##_##field
#define MAKE_COMPARER(name, field) bool MAKE_NAME(name, field)(const data& l, const data& r){ return (l.field < r.field);} 
MAKE_COMPARER(comp, data_id)
MAKE_COMPARER(comp, name)

class Class
{
    typedef bool(*comparerPtr)(const data& l, const data& r);
    std::vector<comparerPtr> m_comparers;
    std::vector<data> m_recordset;
public:
    Class()
    {
        m_comparers.push_back(MAKE_NAME(comp, data_id));
        m_comparers.push_back(MAKE_NAME(comp, name));
    }

    void sort_recordset(int num)
    {
        std::sort(m_recordset.begin(), m_recordset.end(), m_comparers[num]);
    }
};

//...
    Class a;
    a.sort_recordset(0);
    a.sort_recordset(1);
"Что не завершено, не сделано вовсе" Гаусс
Re[2]: Рефакторинг или массив указателей на члены
От: korzhik Россия  
Дата: 22.03.05 12:17
Оценка:
Здравствуйте, sadomovalex, Вы писали:

S>Предложу еще одно решение:


Спасибо за участие в дискуссии и за ваше решение.
В принципе это вариация на тему массива функторов.
Конкретно ваше решение мне не подходит потому что у меня функция make_comparer это не просто функция — это целое семейство функций — шаблонных и перегруженных, которые возвращает объект-функтор, сам который создаётся в зависимости от многих условий: константный член, не константный также есть возможность композиции функторов, то есть сортировать сразу по нескольким полям структуры и так далее. Поэтому мне нужен массив объектов-функторов, для этого подходит, как здесь уже советовали boost::function<bool (Data const&, Data const&)> table[];
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.