Re[2]: замена VPTR это "грязный" хак?
От: B0FEE664  
Дата: 06.04.11 08:54
Оценка: +1
Здравствуйте, ononim, Вы писали:

O>Хаком тут является отсутствие вызова деструктора и отсутствие проверки необходимого размера. Все. Для рассказывающих ужастики повторяю: ВСЕ.


В вот в том то и дело, что не всё ! Даже не рассматривая проблемы размера и деструктора в исходном примере есть хак.
Вот 4 варианта на псевдокоде:

Вариант N 0

class User {
public:

  Interface intf;
  template <class T> changeIntfRealization() {
    new (&intf)T();
  }  
};



Вариант N 1

class User {
public:

  char buf[достаточно большое число]; 
  Interface* intf;

  User() { intf = new (buf)T(); }
  
  template <class T> changeIntfRealization() {
    new (intf)T();
  }  
};


Вариант N 2

class User {
public:

  char buf[достаточно большое число]; 
  Interface* intf;

  User() { intf = new (buf)T(); }
  
  template <class T> changeIntfRealization() {
    intf = new (intf)T();
  }  
};


Вариант N 3

class User {
public:

  char buf[достаточно большое число]; 
  Interface* intf;

  User() { intf = new (buf)T(); }
  
  template <class T> changeIntfRealization() {
    intf = new (buf)T();
  }  
};


Разница в

0. new (&intf)T();
1. new (intf)T();
2. intf = new (intf)T();
3. intf = new (buf)T();

Каждый из вариантов при определённых обстоятельствах будет работать (без ошибок), но только последний вариант не является хаком.

0. new (&intf)T();
Нарушается пункт 3.8.7 стандарта (если T не совпадает с Interface)

1. new (intf)T();
Неявно предполагается, что (Interface*)pointer == (T*)pointer

2. intf = new (intf)T();
Ещё более неявно предполагается, что (Interface*)pointer == (T*)pointer. Если T имеет множественное наследование, то через некоторое количество таких перезаказов мы можем выйти за пределы буфера.

Возражения ?
И каждый день — без права на ошибку...
Re[15]: замена VPTR это "грязный" хак?
От: jazzer Россия Skype: enerjazzer
Дата: 06.04.11 09:11
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, k.o., Вы писали:


KO>>можно черновик C++0x посмотреть, там, это почти не изменилось: здесь, тоже 12.4/4.


E>Спасибо за идею.

E>Тут смотри что пишут:

A destructor is trivial if it is neither user-provided nor deleted and if:
E>— the destructor is not virtual,
E>— all of the direct base classes of its class have trivial destructors, and
E>— for all of the non-static data members of its class that are of class type (or array thereof), each such
E>class has a trivial destructor.
E>Otherwise, the destructor is non-trivial.

требование снижено до отсутствия виртуальности самого деструктора, по сравнению с тем, что я помню.
E>Интересно, как оно было раньше?

Ты пропустил выделенное
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[3]: замена VPTR это "грязный" хак?
От: ononim  
Дата: 06.04.11 09:16
Оценка:
BFE>1. new (intf)T();
BFE>Неявно предполагается, что (Interface*)pointer == (T*)pointer

BFE>2. intf = new (intf)T();

BFE>Ещё более неявно предполагается, что (Interface*)pointer == (T*)pointer. Если T имеет множественное наследование, то через некоторое количество таких перезаказов мы можем выйти за пределы буфера.

BFE>Возражения ?


С этис никаких. В этом плане я невнимательно изучил код ТС. Впрочем в предложенном мной варианте тут http://rsdn.ru/forum/cpp/4221579.1.aspx
Автор: ononim
Дата: 05.04.11
таких проблем нету
Как много веселых ребят, и все делают велосипед...
Re[3]: девиртуализация?
От: jazzer Россия Skype: enerjazzer
Дата: 06.04.11 09:20
Оценка: +1
Здравствуйте, jazzer, Вы писали:

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


A>>После уже второго такого обсуждения о хаке виртуальной таблицы стало интересно — а не наткнутся ли любители переписывать таблицы виртуальных методов на грабли в виде девиртуализации проведенной оптимизирующем компилятором?

J>+1.
J>Одна из форм UB.
J>Поэтому и нельзя реюзать указатели/ссылки на погибший объект, даже если ты там сконструировал что-то новое и вроде как ссылки/указатели остались валидными — компилятор знает, что они указывали на изначальный класс, и вправе генерить какой угодно код, исходя из этого предположения.
J>Помимо девиртуализации вызова, он может, например, просто закешировать указатель на vtbl при первой инициализации указателя, а не считывать его каждый раз при обращении по указателю (зачем делать два джампа, когда достаточно одного?) и таким образом продолжить обращаться к старой таблице даже после подмены, с сопутствующими фейерверками.

ononim, ты с чем конкретно не согласен-то?
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[14]: замена VPTR это "грязный" хак?
От: k.o. Россия  
Дата: 06.04.11 09:20
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, k.o., Вы писали:



E>>>Да, но гарантий того, что эти указатели совпадут таки нет...


KO>>Так нам их и не надо, смотри по ссылке, что я тебе давал. Там, конечно, много букв, но, от этого ещё меньше хочется всё заново повторять.


E>Как так не надо?

E>Мы же запоминаем указатель на базу первого объекта и хотим его потом использовать, как указатель на базу второго.
E>Насколько я понимаю, это надо для того, чтобы подменять объекты в каком-то дереве. Если нам не надо сохранять указатель, то можно не страдать, а просто завести пул объектов, например, и не париться из-за переаллокаций...

В исходном сообщении я никакого дерева не увидел, поэтому и не вижу необходимости запоминать указатель. Я, скорее, предполагал что-то вроде этого
Автор: Caracrist
Дата: 05.04.11
. По сути, там и используется пул на один объект.
Re[11]: замена VPTR это "грязный" хак?
От: k.o. Россия  
Дата: 06.04.11 09:20
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Здравствуйте, k.o., Вы писали:


KO>>И что такого волшебного в malloc? Можно использовать и new char[<size of storage for any implementation>] или вообще aligned_storage<(size of storage for any implementation)>::type storage;


BFE>Можно. В C++ вполне можно написать свой аллокатор и , вероятно, за счёт дополнительного знания об объектах он будет работать быстрее стандартного.


Честно, говоря, это никак не прояняет что ты имел в виду говря

Это отличается заказом памяти. Собственно разница состоит в "malloc(<size of storage for any implementation>);"


BFE>>>А занимаясь такими вещами, мы делаем шаг в сторону С-- (здесь) или же в сторону Майкросовтовского CoCreateInstance.


KO>>Какими такими?


BFE>Ну вот когда начинают думать об объектах не в абстрактных терминах , а в терминах объёма памяти и кусках адресных пространств, вот тогда и начинается... А на выходе получается (обычно) непереносимый код. Или же что-то вроде management С++, в котором простая операция доступа через указатель (вроде a->b) в шесь раз медленнее...


Если нас интересуют только "объекты в абстрактных терминах", возможно, использование C++ не самая лучшая идея. Проще уж тогда взять C#, Java, Scala, you name it, если нужен native код есть OCaml и Haskell. А вот кодга нас интересует объём памяти и скорость работы начинается C++. ИМХО, программист на C++ должен такие вещи знать и уметь эти знания применять, как раз чтобы не получился непереносимый код работающий в 6 раз медленнее.

BFE>>>Чем, собственно, не устраивает классический подход :


KO>>Позволю себе процитировать ТС:

KO>>

KO>>Хочу использовать реализации интерфейсов без динамической реаллокации объектов реализующих их


BFE>Вот собственно это и интересно. Чем вызвано такое требование ? Это ограничение связано с реал-тайм или с контролем памяти ? Или же тут что-то ещё ? Или стандартный аллокатор настолько плох ?


Это ты меня или ТС спрашиваешь? Впрочем, стандартный аллокатор скоростью работы действительно не блещет.
Re[15]: замена VPTR это "грязный" хак?
От: k.o. Россия  
Дата: 06.04.11 09:24
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, k.o., Вы писали:


KO>>можно черновик C++0x посмотреть, там, это почти не изменилось: здесь, тоже 12.4/4.


E>Спасибо за идею.

E>Тут смотри что пишут:

A destructor is trivial if it is neither user-provided nor deleted and if:
E>— the destructor is not virtual,
E>— all of the direct base classes of its class have trivial destructors, and
E>— for all of the non-static data members of its class that are of class type (or array thereof), each such
E>class has a trivial destructor.
E>Otherwise, the destructor is non-trivial.

требование снижено до отсутствия виртуальности самого деструктора, по сравнению с тем, что я помню.

E>Интересно, как оно было раньше?

Как было раньше я уже писал. На самом деле, я не понимаю, как деструктор может быть виртуальным, если выполняется "if it is neither user-provided" и "all of the direct base classes of its class have trivial destructors", т.е. в этом плане, видимо, ничего не изменилось по сравнению с предыдущим стандартом.
Re[16]: замена VPTR это "грязный" хак?
От: Erop Россия  
Дата: 06.04.11 09:25
Оценка:
Здравствуйте, jazzer, Вы писали:

E>>A destructor is trivial if it is neither user-provided nor deleted and if:

E>>— the destructor is not virtual,

J>Ты пропустил выделенное


Да, действительно. Какой такой виртуальный деструктор, если деструктор явно вообще не описан? Тут что-то не так...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[15]: замена VPTR это "грязный" хак?
От: Erop Россия  
Дата: 06.04.11 09:31
Оценка:
Здравствуйте, k.o., Вы писали:

KO>В исходном сообщении я никакого дерева не увидел, поэтому и не вижу необходимости запоминать указатель. Я, скорее, предполагал что-то вроде этого
Автор: Caracrist
Дата: 05.04.11
. По сути, там и используется пул на один объект.


В исходном сообщении вообще не указатель, а просто встроенное в другую структуру поле...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[16]: замена VPTR это "грязный" хак?
От: k.o. Россия  
Дата: 06.04.11 09:51
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, k.o., Вы писали:


KO>>В исходном сообщении я никакого дерева не увидел, поэтому и не вижу необходимости запоминать указатель. Я, скорее, предполагал что-то вроде этого
Автор: Caracrist
Дата: 05.04.11
. По сути, там и используется пул на один объект.


E>В исходном сообщении вообще не указатель, а просто встроенное в другую структуру поле...


Ну да, "поле" имеющее тип абстрактного класса...
Re[17]: замена VPTR это "грязный" хак?
От: Erop Россия  
Дата: 06.04.11 10:04
Оценка:
Здравствуйте, k.o., Вы писали:

E>>В исходном сообщении вообще не указатель, а просто встроенное в другую структуру поле...


KO>Ну да, "поле" имеющее тип абстрактного класса...


Угу. То, что пример некорректен очевидно. Не очевидна трактовка, что это типа просто new размещения в буфере...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[17]: замена VPTR это "грязный" хак?
От: jazzer Россия Skype: enerjazzer
Дата: 06.04.11 10:08
Оценка:
Здравствуйте, Erop, Вы писали:

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


E>>>A destructor is trivial if it is neither user-provided nor deleted and if:

E>>>— the destructor is not virtual,

J>>Ты пропустил выделенное


E>Да, действительно. Какой такой виртуальный деструктор, если деструктор явно вообще не описан? Тут что-то не так...


Сгенерированный компилятором, вестимо, если в базовом классе был виртуальный деструктор.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[18]: замена VPTR это "грязный" хак?
От: k.o. Россия  
Дата: 06.04.11 10:27
Оценка:
Здравствуйте, jazzer, Вы писали:

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


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


E>>>>A destructor is trivial if it is neither user-provided nor deleted and if:

E>>>>— the destructor is not virtual,

J>>>Ты пропустил выделенное


E>>Да, действительно. Какой такой виртуальный деструктор, если деструктор явно вообще не описан? Тут что-то не так...


J>Сгенерированный компилятором, вестимо, если в базовом классе был виртуальный деструктор.


Нет, тут уже срабатывает "all of the direct base classes of its class have trivial destructors".
Re[17]: замена VPTR это "грязный" хак?
От: gegMOPO4  
Дата: 06.04.11 10:39
Оценка:
Здравствуйте, Erop, Вы писали:
E>Да, действительно. Какой такой виртуальный деструктор, если деструктор явно вообще не описан? Тут что-то не так...

Сгенерированный. И унаследовавший виртуальность от предков (хотя бы одного из).
Re[18]: замена VPTR это "грязный" хак?
От: Erop Россия  
Дата: 06.04.11 10:42
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Сгенерированный компилятором, вестимо, если в базовом классе был виртуальный деструктор.


Э-э-э, тогда кто-то из баз с нетривиальным деструктором должен бы быть. И пункт как бы лишний выходит...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[18]: замена VPTR это "грязный" хак?
От: Erop Россия  
Дата: 06.04.11 10:44
Оценка:
Здравствуйте, gegMOPO4, Вы писали:

MOP>Сгенерированный. И унаследовавший виртуальность от предков (хотя бы одного из).


Тогда тот предок имеет нетривиальный деструктор и мы получаем, что пункт про виртуальный деструктор избыточен...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[6]: замена VPTR это "грязный" хак?
От: gegMOPO4  
Дата: 06.04.11 10:49
Оценка:
Здравствуйте, A13x, Вы писали:
A>Насчет переделки согласен, вопрос по большему счету философский. Кстати, в википедии вот пример тоже до подозрительности напоминает банальный pointer to implementation — http://en.wikipedia.org/wiki/Strategy_pattern

Стратегия и pImpl — это разные уровни. Стратегия — интерфейс, pImpl — реализация. Стратегия задаётся явно, поведение объекта может определяться несколькими стратегиями.
Re[19]: замена VPTR это "грязный" хак?
От: jazzer Россия Skype: enerjazzer
Дата: 06.04.11 10:53
Оценка: 8 (1) :)
Здравствуйте, Erop, Вы писали:

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


MOP>>Сгенерированный. И унаследовавший виртуальность от предков (хотя бы одного из).


E>Тогда тот предок имеет нетривиальный деструктор и мы получаем, что пункт про виртуальный деструктор избыточен...


ну в С++0х можно явно дефолтные версии генерить...
Я не уверен, но вполне возможно, что можно написать так:
struct A
{
  virtual ~A() = default;
};

тут он и виртуальный, и в то же время не определен пользователем
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re: замена VPTR это "грязный" хак?
От: Seigram  
Дата: 06.04.11 19:06
Оценка:
Здравствуйте, Аноним, Вы писали:
[skipped]

Вообще-то это я писал, забыл авторизироваться.
1. Не ожидал такого обилия ответов и советов
2. Многие просто не захотели заметить что я категорически НЕ могу шевелить кучу.
И плодить инстанции реализаций тоже не могу заранее.
Поэтому иду на это колдунство.
Собственно никаких конструкторов и деструкторов, тем более нетривиальных — нет.
Сейчас решение в упрощенном виде(если отбросить что все являются шаблонными классами)
приблизительно такое(собственно машина состояний и событий, но без монстроидальных switch's):


template <class T> class Interface
    : public T
{
public:
  template <class Q> void as() {
    new ((void*)this)Q();
  }
  template <class Q> void as() const {
    new ((void*)this)Q();
  }
  inline T* operator-> () { return this; }
  inline T const* operator-> () const { return this; }
};

template <class Q, class T> void as(T *ptr) {
  new ((void*)ptr)Q();
}

struct Intf { // на самом деле здесь template <int _код_группы>
  virtual R1 method1(T1, ...TN) {}
  ...
  virtual Z1 methodZ(Q1, ...QN) {}
};

struct Derived1: public Intf { // на самом деле здесь template <int _код_группы>
  // конкретная реализация
  ...
  ...
  X1 methodX(U1, ...UN) { 
    if (params_depended_condition) { // мутирую самого себя - самый грязный HACK   :) 
      as<DerivedK> (this); 
      return X1(code_X);
    }
  }
  ...
};
...прочие Derived

Где-то где нужно(в отдельной нитке): 
Interface<Intf> intf;
for (;;) {
 // прицеливаемся
 if (cond1) intf.as<Derived1> ();
 else ...
 else if (condN) intf.as<DerivedN> ();
 // используем реализацию
 intf->method<такой-то> (параметры такие-то)
}


Думаю понятно что такой подход мне понравился из-за отсутствия таблицы-таблиц вызовов в которой черт ногу сломит.
Но терзают сомнения — на всех ли компиляторах это прокатит ?..
Re[2]: замена VPTR это "грязный" хак?
От: gegMOPO4  
Дата: 06.04.11 20:11
Оценка: +1
Здравствуйте, Seigram, Вы писали:
S>1. Не ожидал такого обилия ответов и советов

А теперь их прочитайте. Причём особенно внимательно не те, что кричат «да, конечно можно!», а те, которые объясняют, почему так делать нельзя, чем это грозит и как сделать, чтобы было можно.

S>2. Многие просто не захотели заметить что я категорически НЕ могу шевелить кучу.


Решения без кучи предлагали. Заведите в мутирующем классе сырой буффер достаточного размера с запасом на выравнивание (char[...]), указатель на интерфейс и потом pimpl=new(area)T. Обязателен статический или динамический assert.

S> И плодить инстанции реализаций тоже не могу заранее.


Если у вас в производных классах добавляются новые члены-данные, то фейл очевиден. Если используются только данные из исходного мутирующего класса (или их вообще нет), то для чистых стратегий без данных достаточно одного инстанса на класс (и содержать он будет только VPTR). Код шаблона, хакающего VPTR, будет больше.

S>Но терзают сомнения — на всех ли компиляторах это прокатит ?..


Как уже говорили выше, могут быть проблемы при инициализации (указатели на разные классы могут не совпадать, выравнивание, размер), использовании (статическая девиртуализация или кеширование в рантайме), удалении (дефолный деструктор может делать неожиданные вещи). Поэтому не делайте так.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.