Russian Qt Forum
Ноябрь 24, 2024, 03:30 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 [2] 3   Вниз
  Печать  
Автор Тема: тип size_t и сравнение указателей  (Прочитано 17429 раз)
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #15 : Март 19, 2011, 20:21 »

Вот скажет вам компилятор в один прекрасный день: "at line 123, error: сишный каст -- зло!!" и будет прав, а вам и сказать будет нечего.
Мне было "сказать нечего" не один раз, на Mac учат "любить свободу" очень быстро. Жили себе спокойно на Classic. Раз - PowerPC, переписывайте, "просто так" Ваш код не пойдет. Ладно, только улеглось - бац! Carbon - портируйте. Ладно, несколько лет ОС не беспокоил, потом опять: портируйте на Univrsal Binary. Сейчас (тактично) намекают что от Carbon'а надо (срочно) избавляться. А давеча (10.7) Розетту кинули (пользователи на понтах, есть старый но нужный софт)

Так что не верю я что можно написать "ну очень умный код" который уж наверняка будет совместим лет на 10 вперед. Если что случится - значит того не миновать, и нечего пытаться решать проблемы до их поступления
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #16 : Март 19, 2011, 20:26 »

Цитировать
можно уменьшить и убыстрить так:
1. заменить size_t на void *
2. убрать у функции address атрибут virtual и =0
3. в теле address в базовом классе вернуть this (reinterpret_cast не нужен для преобразования в void*)
4. убрать address из наследников
1. Хорошо
2-4. так не получится по той причине, что мне не интересен адрес самого x_connection: я сравниваю адреса receiver, который является внутренней переменной соединения и содержит адрес объекта receiverА.    

Цитировать
надо иметь ввиду, что вызов dynamic_cast по скорости близок к вызову виртуальной функции, поэтому заменяя dynamic_cast на виртуальную функцию много не выиграешь.
Если так, то тогда получается, что нет смысла в этой оптимизации.  Грустный Непонимающий

Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
brankovic
Гость
« Ответ #17 : Март 19, 2011, 20:41 »

1. Хорошо
2-4. так не получится по той причине, что мне не интересен адрес самого x_connection: я сравниваю адреса receiver, который является внутренней переменной соединения и содержит адрес объекта receiverА.    

да, в 2-4 я не прав.

Если так, то тогда получается, что нет смысла в этой оптимизации.  Грустный Непонимающий

Не знаю. Можно иметь в базовом классе void * addr; и его сравнивать. Но тогда в наследниках придётся колдовать с кастами, думаю эта возня не стоит результата. Стоит ли вообще оптимизировать операцию connect? Она ведь редко выполняется, гораздо реже вызова слота.

Вообще в C++0x предлагали ввести мультиметоды для ускорения подобных операций (не знаю, прошёл ли пропозал). Но до светлого будущего редко случается дожить..
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #18 : Март 19, 2011, 20:56 »

Цитировать
Не знаю. Можно иметь в базовом классе void * addr; и его сравнивать. Но тогда в наследниках придётся колдовать с кастами, думаю эта возня не стоит результата. Стоит ли вообще оптимизировать операцию connect? Она ведь редко выполняется, гораздо реже вызова слота.
Вот я тож об этом сейчас подумал.. Оно того стоит? Если по времени вызов виртуальной функции и вызов оператора dynamic_cast одного порядка то нефиг и суетится..

Цитировать
Вообще в C++0x предлагали ввести мультиметоды для ускорения подобных операций (не знаю, прошёл ли пропозал). Но до светлого будущего редко случается дожить..
Ну почему же) В gcc 4.5.1 уже практически полностью поддерживает стандарт C++0x на сколько мне известно) Гоаорят, что уже лямбды можно использовать)

Кстати, в libsig++ вообще не проверяется при создании нового соединения его наличие. Т.е. если подряд вызвать connect с одним и тем же слотом то слот будет повторно вызван  Грустный
« Последнее редактирование: Март 19, 2011, 21:05 от m_ax » Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
brankovic
Гость
« Ответ #19 : Март 19, 2011, 21:05 »

Так что не верю я что можно написать "ну очень умный код" который уж наверняка будет совместим лет на 10 вперед. Если что случится - значит того не миновать, и нечего пытаться решать проблемы до их поступления

Очень умный не получится, кто же спорит. Но дурные фичи лучше не использовать. Тем более, что касты не так часто нужны, и на 99% это static_cast и dynamic_cast.

Сишный каст зло, потому что:
1. не имеет почти никаких ограничений (reinterpret_cast гораздо слабее), можно кастить тип, а заодно убить константность
2. трудно находится поиском
3. затуманивает смысл (не понятно, какой именно каст хотел сделать автор)

Ну конечно в таком простом примере сишный каст не страшен, но написать static_cast 1 раз не очень сложно, мне кажется. В вашем примере тоже странно выглядит, кстати: (char *) a == (char *) b, читающий на секунду задумается, почему именно char*, может это сравнение строк каких-то? Может в этих классах строки вначале? Может у них operator char * есть? Надо использовать void * для таких вещей. Когда код формально верный, то читается легче, перестаёшь всё время подозревать, что автор -- идиот.
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #20 : Март 19, 2011, 21:10 »

Цитировать
Так что не верю я что можно написать "ну очень умный код" который уж наверняка будет совместим лет на 10 вперед. Если что случится - значит того не миновать, и нечего пытаться решать проблемы до их поступления

и

Цитировать
Сишный каст зло, потому что:
1. не имеет почти никаких ограничений (reinterpret_cast гораздо слабее), можно кастить тип, а заодно убить константность
2. трудно находится поиском
3. затуманивает смысл (не понятно, какой именно каст хотел сделать автор)

Ну конечно в таком простом примере сишный каст не страшен, но написать static_cast 1 раз не очень сложно, мне кажется. В вашем примере тоже странно выглядит, кстати: (char *) a == (char *) b, читающий на секунду задумается, почему именно char*, может это сравнение строк каких-то? Может в этих классах строки вначале? Может у них operator char * есть? Надо использовать void * для таких вещей. Когда код формально верный, то читается легче, перестаёшь всё время подозревать, что автор -- идиот.

== Мораль: Надо верить))
Хуже не будет)
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #21 : Март 20, 2011, 10:11 »

Сишный каст зло, потому что:
1. не имеет почти никаких ограничений (reinterpret_cast гораздо слабее), можно кастить тип, а заодно убить константность
2. трудно находится поиском
3. затуманивает смысл (не понятно, какой именно каст хотел сделать автор)

Ну конечно в таком простом примере сишный каст не страшен, но написать static_cast 1 раз не очень сложно, мне кажется. В вашем примере тоже странно выглядит, кстати: (char *) a == (char *) b, читающий на секунду задумается, почему именно char*, может это сравнение строк каких-то? Может в этих классах строки вначале? Может у них operator char * есть? Надо использовать void * для таких вещей. Когда код формально верный, то читается легче, перестаёшь всё время подозревать, что автор -- идиот.
1) Интересно посмотреть пример показывающий выгоды reinterpret_cast <char *> по сравнению с незатейливым (char *)

2) static_cast не приведет указатель к size_t

3) согласен, для "просто сравнения" (void *) приятнее, но (char *) позволит вычислить разницу между адресами что бывает нужно

4) Спорно/проблематично кто там больше "затуманивает". Часто текст превращается в демонстрацию знания C++ и автор озабочен не содержательной частью а лишь той самой "формальной грамотностью". Продравшись через все навороты обнаруживается, что дела-то "пшик", ф-циональность слаба. А бывает и наоборот

Вот пример совершенно "не кошерного" кода (доставшегося мне в наследство)
Код
C++ (Qt)
void * theNode;
..
if ((size_t) theNode & 1 != 0)  {     // взведен младший бит - это parent нод
Parent * theParent = (Parent *)((size_t) theNode & NODE_MASK);
...
}  
else {
Leaf * theLeaf = (Leaf *) theNode;  // это leaf нод
..
}
 
Автор весьма интенсивно пользует dynamic_cast в др. местах, но здесь решил сделать так - и у него были резоны.
Записан
brankovic
Гость
« Ответ #22 : Март 20, 2011, 11:29 »

1) Интересно посмотреть пример показывающий выгоды reinterpret_cast <char *> по сравнению с незатейливым (char *)

интересно посмотреть пример показывающий выгоды вытирания рта салфеткой по сравнению с простым рукавом рубашки..

4) Спорно/проблематично кто там больше "затуманивает". Часто текст превращается в демонстрацию знания C++ и автор озабочен не содержательной частью а лишь той самой "формальной грамотностью". Продравшись через все навороты обнаруживается, что дела-то "пшик", ф-циональность слаба. А бывает и наоборот

Я не к тому, что надо писать идеальный код, но туман на пустом месте не нужен. С формальностью тоже легко перебрать -- один раз видел в коде 50 строчек с шаблонами, нужных, чтобы убрать 1 ворнинг о сравнении unsigned >= 0. При прочих равных лучше кривой хак и строчка с комментом, чем формально правильное решение на страницу.

Вот пример совершенно "не кошерного" кода (доставшегося мне в наследство)
...
Автор весьма интенсивно пользует dynamic_cast в др. местах, но здесь решил сделать так - и у него были резоны.

Младший бит указателя всё равно пропадает, стандартная оптимизация, но исполнение спорное (надо задачу видеть, чтобы оценить). Как раз классический пример, когда const легко потерять.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #23 : Март 20, 2011, 12:48 »

1) Интересно посмотреть пример показывающий выгоды reinterpret_cast <char *> по сравнению с незатейливым (char *)
интересно посмотреть пример показывающий выгоды вытирания рта салфеткой по сравнению с простым рукавом рубашки..
Такие аргументы неэффективны  Улыбающийся  Конечно легко запомнить "юзай  reinterpret_cast, так в C++ положено". А вот привести примерчик где  reinterpret_cast сработает как надо а "просто приведение" нет - ну я например не могу (по крайней мере сходу). Так что вместо элегантной салфетки - большой подгузник  Улыбающийся
Записан
brankovic
Гость
« Ответ #24 : Март 20, 2011, 13:25 »

А вот привести примерчик где  reinterpret_cast сработает как надо а "просто приведение" нет

bool chek_set_true (void const *opts, std::string const &opt_name)
{
   cmd_line_options_map & cmd = *(cmd_line_options_map*) opts;
   return cmd [opt_name] == "true";
}

тип параметра opts передать уже нет возможности (ошибка проектирования, но неисправимая, пишется плагин). Автор кастит сишным кастом и теряет const. Вообще-то автор знал, что std::map::operator[] это не константная операция, но проспал этот момент. Если бы был использован reinterpret_cast, то компилятор бы не дал потерять const, ссылка стала бы const, и компилятор ругнулся бы на cmd [opt_name]. А так в мап добавляются новые параметры, на что код в другом месте реагирует презабавным образом.
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #25 : Март 20, 2011, 13:56 »

У меня тут ещё вопрос возник:
Корректно ли тогда будет такая конструкция работать:
Код
C++ (Qt)
class A
{
public:
   void func1() {}
   void func2() {}
};
 
void *addr1 = (void*)&A::func1;
void *addr2 = (void*)&A::func2;
 
if (addr1 != addr2)
...
elae
...
 
Непонимающий Или с указателями на функции такое уже не пройдёт?
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #26 : Март 20, 2011, 14:17 »

Проверил сейчас, что быстрее работает: вызов виртуальной функции или dynamic_cast:
Разницы практически никакой нет, едва заметный эффект начинает появлятся только после миллиона вызовов.. Так что смысла в этой оптимизации, реально 0.
 
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
brankovic
Гость
« Ответ #27 : Март 20, 2011, 14:34 »

У меня тут ещё вопрос возник:
Корректно ли тогда будет такая конструкция работать:
Код
C++ (Qt)
class A
{
public:
   void func1() {}
   void func2() {}
};
 
void *addr1 = (void*)&A::func1;
void *addr2 = (void*)&A::func2;
 
if (addr1 != addr2)
...
elae
...
 
Непонимающий Или с указателями на функции такое уже не пройдёт?

Не пройдёт. И с указателями на члены-данные не пройдёт. Проблема в том, что они могут быть размером в 2 указателя, для классов с виртуальными функциями. К тому же это не настоящие указатели. В общем не стоит их в воид запихивать, даже если сработает, то будет завязано на компилятор.

Проверил сейчас, что быстрее работает: вызов виртуальной функции или dynamic_cast:
Разницы практически никакой нет, едва заметный эффект начинает появлятся только после миллиона вызовов..

и кто побеждает-то?
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #28 : Март 20, 2011, 14:57 »

Цитировать
и кто побеждает-то?
Получается весьма забавный результат: побеждает dynamic_cast  Непонимающий
Вот сам тест:
Код
C++ (Qt)
#include <iostream>
#include <list>
#include <ctime>
#include <cstdlib>
#include <cstdio>
 
using namespace std;
 
enum type_derived { TYPE_INT, TYPE_F };
 
class Base
{
public:
   Base() {}
   virtual ~Base() {}
   virtual void print() = 0;
   virtual type_derived type() const = 0;
};
 
class DerivedInt : public Base
{
public:
   DerivedInt(int id = 0) : m_id(id) {}
   void print() { /*cout << "DerivedInt, id = " << m_id << endl;*/ }
   type_derived type() const { return TYPE_INT; }
   void myFunc() { /*cout << "DerivedInt, id = " << m_id << endl;*/ } // not virtual
private:
   int m_id;
};
 
class DerivedF : public Base
{
public:
   DerivedF(float id = 0.0f) : m_id(id) {}
   void print() { /*cout << "DerivedF, id = " << m_id << endl;*/ }
   type_derived type() const { return TYPE_F; }
   void myFunc() { /*cout << "DerivedF, id = " << m_id << endl;*/ } // not virtual
private:
   float m_id;
};
 
typedef list<Base*> ListBase;
typedef list<Base*>::iterator Iter;
 
void generate(ListBase &l, size_t N) {
   for (size_t i = 0; i < N; ++i) {
       if (rand() % 2)
           l.push_back(new DerivedInt(i));
       else
           l.push_back(new DerivedF(float(i)));
   }
}
 
void clearList(ListBase &l) {
   for (Iter it = l.begin(); it != l.end(); ++it) {
       delete *it;
   }
   l.clear();
}
 
void test_dynamic_cast(ListBase &l) {
   Iter it = l.begin();
   for(; it != l.end(); ++it) {
       DerivedF *obj = dynamic_cast<DerivedF*>(*it);
       if (obj)
           obj->myFunc();
   }
}
 
void test_virtual_fun(ListBase &l) {
   Iter it = l.begin();
   for(; it != l.end(); ++it) {
       if ((*it)->type() == TYPE_F) {
           DerivedF *obj = static_cast<DerivedF*>(*it);
           obj->myFunc();
       }
   }
}
 
int main()
{
   ListBase lb;
   size_t Num = 20000000;
   srand(time(0));
   generate(lb, Num);
 
   clock_t tStart = clock();
   test_dynamic_cast(lb);
   //test_virtual_fun(lb);
   cout << (float)(clock() - tStart) / CLOCKS_PER_SEC << endl;
 
   clearList(lb);
   return 0;
}
 
Суть эксперимента такова:
Есть базовый класс Base с чисто виртуальными двумя функциями print() и type().
От него наследуются два класса DerivedInt и DerivedF в которых данные функции переопределяются.
Но также в этих классах я ввожу не виртуальные методы myFunc().
Далее создаётся список, содержащий указатели на базовый класс Base:
Код
C++ (Qt)
typedef list<Base*> ListBase;
typedef list<Base*>::iterator Iter;
 
   
И заполняется объектами на DerivedInt и DerivedF (случайным образом):
Код
C++ (Qt)
void generate(ListBase &l, size_t N) {
   for (size_t i = 0; i < N; ++i) {
       if (rand() % 2)
           l.push_back(new DerivedInt(i));
       else
           l.push_back(new DerivedF(float(i)));
   }
}
 
Затем проверяется вызовы dynamic_cast виртуальных функций:
Код
C++ (Qt)
void test_dynamic_cast(ListBase &l) {
   Iter it = l.begin();
   for(; it != l.end(); ++it) {
       DerivedF *obj = dynamic_cast<DerivedF*>(*it); // Один вызов dynamic_cast
       if (obj)
           obj->myFunc(); // один вызов не виртуального метода!
   }
}
 
void test_virtual_fun(ListBase &l) {
   Iter it = l.begin();
   for(; it != l.end(); ++it) {
       if ((*it)->type() == TYPE_F) { // Один вызов виртуального метода
           DerivedF *obj = static_cast<DerivedF*>(*it); // Пренебрегаем издержками на вызов static_cast
           obj->myFunc(); // один вызов не виртуального метода!
       }
   }
}
 
Думаю, что тест не совсем чист, поскольку я не учитываю издержки связанные с оператором static_cast
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
brankovic
Гость
« Ответ #29 : Март 20, 2011, 16:44 »

Получается весьма забавный результат: побеждает dynamic_cast  Непонимающий

У меня тоже, интересный результат. Наверное из-за того, что функция type что-то делает (возвращает значение), а dynamic cast полностью инлайнится. Ещё, на static_cast нет никаких издержек, он влияет только на этапе проверки программы на ошибки.
Записан
Страниц: 1 [2] 3   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.193 секунд. Запросов: 22.