CS>Существует ли способ сменить класс объекта в p? CS>Я понимаю что стандартными способами — нет. CS>Но в принципе это задача смены одного указателя на vtbl у объекта. CS>Как бы это сделать путем "наименьшего хака"?
Здравствуйте, c-smile, Вы писали:
CS>Задача примерно такова: есть DOM tree
CS>
CS>namespace html
CS>{
CS> struct element
CS> {
CS> ... data ...
CS> void do_layout() = 0;
CS> };
CS> struct paragraph : public element
CS> {
CS> void do_layout() { layout as a text container };
CS> };
CS> struct div : public element
CS> {
CS> void do_layout() { layout as a block container };
CS> };
CS>}
CS>
CS>Каждый элемент в зависимости от CSS атрибута: CS>display-model: block-inside | inline-inside | table
CS>должен переключать способ do_layout и чертову силу других методов.
CS>Причем display-model может переключаться динамически (во всяком случае это не запрещено)
Здесь может очень хорошо подойти visitor. visitor позволяет "вынести" виртуальный метод (один или несколько) в отдельный внешний объект. Соответственно можно "переключать" поведение с помощью замены внешнего visitor. При этом сохраняется полная типобезопасность — никаких кастов, полный доступ к данным типа и достаточно стройная реализация.
Паттерн расширяем как пополненем новыми типами объектов (paragraph, div), так и пополнением новыми внешними алгоритмами (block_inside, inline_inside).
visitor сам по себе является "объектом-пустышкой", т.е. не содержит никаких данных. Поэтому его разрушение/создание практически бесплатно. Если есть желание, то можно и пресоздать всех visitor, тогда надо будет переключать только указатель на них.
Сами же объекты с данными рушить и вообще трогать при переключении не надо.
При необходимости можно добавить неограниченное кол-во наворотов сверху.
Можно сделать отдельно visitor и const_visitor для пущей стройности.
Можно добавить шаблонов, что бы не писать много скучных virtual void do_layout(element_visitor& e) {e.do_layout(*this);}
Можно добавить acyclic visitor для разрыва циклической зависимости.
И т.д.
Здравствуйте, remark, Вы писали:
R>Здравствуйте, c-smile, Вы писали:
CS>>Чем это лучше простой смены типа?
R>Ничем. Кому что нравится.
R>Вот ещё вариант:
А вот улучшенный вариант, с прямой поддержкой идиомы "переключение типа":
struct data
{
int i;
};
struct base
{
virtual void draw() = 0;
};
struct impl1 : base, virtual data
{
virtual void draw()
{
std::cout << "impl1 " << i << std::endl;
}
};
struct impl2 : base, virtual data
{
virtual void draw()
{
std::cout << "impl2 " << i << std::endl;
}
};
struct last : impl1, impl2
{
last() : self(static_cast<impl1*>(this)) {}
template<typename type>
void switch_to()
{
self = static_cast<type*>(this);
}
template<void (base::*f)()>
void exec()
{
(self->*f)();
}
base* self;
};
int main()
{
last l;
l.exec<&base::draw>();
l.switch_to<impl2>();
l.exec<&base::draw>();
}
Здравствуйте, c-smile, Вы писали:
КЛ>>А я не могу понять, почему ты так относишься к предложениям, которые выдвигаются по твоей собственной просьбе? Зачем все в штыки принимать?
CS>Мда, народ упорно не читает ТЗ. CS>Вопрос был такой "Существует ли способ сменить класс объекта в p?" CS>Все.
Просто все подумали, что ответ "нет" очевиден, и что он очевиден и тебе Поэтому додумали твою проблему, и предлагают решения. Вот.
З.ы. "хакерское" создание нового объекта на месте старого — не смена типа *объекта*
Здравствуйте, c-smile, Вы писали:
CS>Мне нужно переключить behavior класса в зависимости от некоего условия. CS>Операции конструирования и копирования объектов достаточно дорогие. CS>Поэтому все что я могу себе позволить это переключить vtbl
Может я и чего то не понимаю в этой куче решений, но по моему здесь усиленно изобретают термоядерный велосипед из шаблонов и посетителей.
Ведь эта задача решается элементарно средствами С — если программишь на MSVC(или даже G++, но только не Borland C++) — можно видеть однозначное (вроде даже задокументированное) соответсвие С-шных структур С++ классам (интерфейсам).
Вот, к примеру:
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: хочу странного: сменить класс объекта в runtime
Здравствуйте, StevenIvanov, Вы писали:
SI>Здравствуйте, c-smile, Вы писали:
CS>>...
SI>Может я и чего то не понимаю в этой куче решений, но по моему здесь усиленно изобретают термоядерный велосипед из шаблонов и посетителей.
Если на это положиться, то можно просто перезаписывать vptr, даже никакого С не надо:
struct base
{
int i;
virtual void draw() = 0;
};
struct d1 : base
{
virtual void draw()
{
std::cout << "d1 " << i << std::endl;
}
static d1 instance;
};
struct d2 : base
{
virtual void draw()
{
std::cout << "d2 " << i << std::endl;
}
static d2 instance;
};
d1 d1::instance;
d2 d2::instance;
int main()
{
d1 d;
base& b = d;
b.draw();
*reinterpret_cast<void**>(&b) = *reinterpret_cast<void**>(&d2::instance);
b.draw();
}
Тихо сам с собою...
CS>>Ты про это? E>Да.
Задумался я таки как лучше. ИМХО неочевидно.
Решил попробовать пописать примеры. Писал на псевдокоде, так как компилятора под рукой ночью не было
Для раздумий я выбрал такую модель.
Типа у нас есть программа, которая работает с каким-то данными о людях, представленными разнообразными классами. Например в нашей прогамме есть такой класс:
При этом таких классов много и много клиентского кода, который что-то делает с объектами таких классов.
Например такого:
void Persone* readPersone()
{
std::string name = readPersonalStirng();
std::string family = readPersonalStirng();
return new Persone( name, family );
}
void ForgetPersone( Persone* p )
{
delete p;
}
void output( Persone* p )
{
cout << p->GetFullName();
}
Теперь мы замечаем, что программа иногда может использоваться не только в Европе, но и в Японии. Для этого надо немного по-другому переписать несколько методов.
Так как нам "надо быстро", мы "сначала сделали как-то так":
// old version 1 (We are in the Europe)namespace InEurope {
class Persone {
public:
Persone( std::string name_, std::string family_ ) : name( name_ ), family( family_ ) {}
std::string GetName() const { return name; }
std::string GetFamily() const { return family; }
std::string GetFullName() const { return name + " " + family; }
std::string GetInitials() const { toInitials( name ) + toInitials( family ); }
private:
const std::string name;
const std::string family;
static std::string toInitials( const std::string& ); // где-то реализовано
};
} // namespace InEurope
// old version 2 (We are in the Japan)namespace InJapan {
class Persone {
public:
Persone( std::string name_, std::string family_ ) : name( name_ ), family( family_ ) {}
std::string GetName() const { return name; }
std::string GetFamily() const { return family; }
std::string GetFullName() const { return family + " " + name; }
std::string GetInitials() const { toInitials( family ) + toInitials( name ); }
private:
const std::string name;
const std::string family;
static std::string toInitials( const std::string& ); // где-то реализовано
};
} // namespace InJapantypedef InEurope::Persone Persone;
Ну а потом посмотрели на это всё и поняли, что так нам не нравится по многим причинам (главная -- неподдерживаемо) И мы хотим что-то теперь сделать так, чтобы это всё можно было переключать динамически, но редко.
При этом мы считаем, что можем проитерировать все объекты, поведением которых мы хотим управлять.
Кроме того, для простоты, я предположил, что
1) Переключаться между странами можно не очень быстро
2) Persone и другие объекты можно легко построить в "пустом состоянии" (то есть что даже если у объекта и нет конструктора по умолчанию, то его легко написать)
3) Для всех таких классов легко написать эффективный std::swap (либо вообще подходит дефолтная реализация)
Какие собственно проблемы?
1) Как-то не запутаться с тем какая реализация работает в какой версии
2) Как-то не делать это всё очень задорого.
Собственно я сначала решил, что хочу иметь объект, который умеет хранить в себе описание поведения всех типов с переключаемым поведением и объект такого типа может использоваться для переключения.
Итак мы хотим как-то переключать поведение наших классов к тому или иному ProcessorsCollection, формируя каждый экземпляр при помощи статических регистраторов.
Сразу просматривается два подхода.
1) Иметь в Persone указатель на состояние, которое собственно умеет выполнять переключаемую работу
2) применить хак с "переключением" типа объектов.
Итак, пробуем:
Сначала пробуем подход с просессорами (ясно, что всё можно немного доточить, но принципиально лучше не будет)
Сначала немного допишем к нашему фреймворку:
//------------------------------
// Вариант с "переключаемыми" типами
//-----------------------------namespace UseSwitchableDT {
class Persone : public ISwitchableDT { //1protected: //1
Persone() {} //1
Persone( std::string name_, std::string family_ ) : name( name_ ), family( family_ ) {}
public: //1static Persone* New() //1
{ return ISwitchable::New<Persone>(); }
static Persone New( std::string name_, std::string family_ ) //1
{ return return ISwitchable::New<Persone>( name_, family_ ); }
void SwitchTo( const ProcessorsCollection& patterns ) //1
{
ISwitchableDT::SwitchToImplementation( patterns, this );
// name.SwitchTo( patterns ); // Полю тоже может быть надо
// family.SwitchTo( patterns ); // Полю тоже может быть надо
}
std::string GetName() const { return name; }
std::string GetFamily() const { return family; }
virtual std::string GetFullName() const = 0; //1virtual std::string GetInitials() const = 0; //1protected:
/*const */std::string name; //1
/*const */std::string family; //1static std::string toInitials( const std::string& ); // где-то реализовано
};
// Реализация версий (какой-то cpp наверное):extern ProcessorsCollection InEurope;
class PersoneInEurope : public CSwitchebleDT< PersoneInEurope, Persone > {
virtual std::string GetFullName() const { return name + " " + family; }
virtual std::string GetInitials() const { toInitials( name ) + toInitials( family ); }
};
static const CProcessorRegistrar<Persone, PersoneInEurope> personeInEurope( InEurope );
extern ProcessorsCollection InJapan;
class PersoneInJapan : public CSwitchebleDT< PersoneInJapan, Persone > {
virtual std::string GetFullName() const { return family + " " + name; }
virtual std::string GetInitials() const { toInitials( family ) + toInitials( name ); }
};
static const CProcessorRegistrar<Persone, PersoneInJapan> personeInJapan( InJapan );
} // namespace UseSwitchableDT
метками //1 помечены места, которые потребовалось переписать.
При этом клиентский код почти не поменяется. (Поменяется только создание/разрушение объектов, да и то весьма формально):
void Persone* readPersone()
{
std::string name = readPersonalStirng();
std::string family = readPersonalStirng();
return Persone::New( name, family ); //1
}
void ForgetPersone( Persone* p )
{
Persone::Delete( p ); //1
}
void output( Persone* p )
{
cout << p->GetFullName();
}
При этом вроде бы все места, где нужна эта правка всплывут при компиляции...
Итак, какие выводы?
Выводы такие, что, ИМХО, примерно одинаково сложно выходит.
С одной стороны при переключении д.т.о. удобнее писать реализации (хотя есть нетривильные правки, например правка константности полей...), зато нужны правки в клиентском коде.
С другой стороны, при использовании процессоров сложнее писать реализации. Хотя не так чтобы как-то сверхрадикально сложнее. Зато клиентский код не меняется вовсе.
С тертьей стороны использование процессоров позволяет, в принципе, сделать поле proc статическим, что избавит от итерации всех объектов в случаее чего, а оставит только итерацию типов. А если скорость не так уж сверхкритична, то можно и вообще хранить где-то статический указатель на ProcessorsCollection, у которого proc получать при каждом вызове метода, тогда поведение будет переключаться вообще забесплатно.
Ну и ещё один момент. версия с процессорами позволяет легче перейти к конструкции, когда ProcessorsCollection будет параметром методов. То есть можно будето легко в одном куске смешивать такое и сякое поведение (например найти людей, инициалы которых не зависят от того в Японии их получили или в Европе )
Что лучше я так и не понял. Так что я бы наверное выбрал процессоры.
Но, возможно кто-то предложит более удачное решение?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: хочу странного: сменить класс объекта в runtime
Есть два класса-потомка (A и B) от общего базового абстракта P.
class P
{
int data;
virtual foo() = 0;
}
class A: public P
{
// no class specific data, only methodsvirtual foo() { ++data; }
}
class B: public P
{
// no class specific data, only methodsvirtual foo() { --data; }
}
Есть некий конкретный instance созданный как A.
P* p = new A;
Существует ли способ сменить класс объекта в p?
Я понимаю что стандартными способами — нет.
Но в принципе это задача смены одного указателя на vtbl у объекта.
Как бы это сделать путем "наименьшего хака"?
Вот такой вот вопрос.
Re: хочу странного: сменить класс объекта в runtime
Здравствуйте, c-smile, Вы писали:
CS>Есть два класса-потомка (A и B) от общего базового абстракта P.
CS>
CS>class P
CS>{
CS> int data;
CS> virtual foo() = 0;
CS>}
CS>class A: public P
CS>{
CS> // no class specific data, only methods
CS> virtual foo() { ++data; }
CS>}
CS>class B: public P
CS>{
CS> // no class specific data, only methods
CS> virtual foo() { --data; }
CS>}
CS>
CS>Есть некий конкретный instance созданный как A.
CS>
CS>P* p = new A;
CS>
CS>Существует ли способ сменить класс объекта в p? CS>Я понимаю что стандартными способами — нет. CS>Но в принципе это задача смены одного указателя на vtbl у объекта. CS>Как бы это сделать путем "наименьшего хака"?
CS>Вот такой вот вопрос.
P* p = new B;
или я чего то не понял?
Re[2]: хочу странного: сменить класс объекта в runtime
Здравствуйте, Awaken, Вы писали:
CS>>Существует ли способ сменить класс объекта в p? CS>>Я понимаю что стандартными способами — нет. CS>>Но в принципе это задача смены одного указателя на vtbl у объекта. CS>>Как бы это сделать путем "наименьшего хака"?
A>pimpl, handle/body, делегация?
+1. И всё это, взятое вместе, называется паттерном State.
Re[2]: хочу странного: сменить класс объекта в runtime
Здравствуйте, Awaken, Вы писали:
CS>>Существует ли способ сменить класс объекта в p? CS>>Я понимаю что стандартными способами — нет. CS>>Но в принципе это задача смены одного указателя на vtbl у объекта. CS>>Как бы это сделать путем "наименьшего хака"?
A>pimpl, handle/body, делегация?
Эти слова я знаю. Плюсы и минусы тоже знаю. Все эти подходы определяют новые сущности.
Мысли вслух:
Хочется простой вещи типа:
P* p = new A;
new(p) B;
Надо по-эксперементировать ...
Re[3]: хочу странного: сменить класс объекта в runtime
Здравствуйте, c-smile, Вы писали:
CS>Здравствуйте, Awaken, Вы писали:
CS>>>Существует ли способ сменить класс объекта в p? CS>>>Я понимаю что стандартными способами — нет. CS>>>Но в принципе это задача смены одного указателя на vtbl у объекта. CS>>>Как бы это сделать путем "наименьшего хака"?
A>>pimpl, handle/body, делегация?
CS>Эти слова я знаю. Плюсы и минусы тоже знаю. Все эти подходы определяют новые сущности.
CS>Мысли вслух:
CS>Хочется простой вещи типа:
CS>
CS>P* p = new A;
CS>new(p) B;
CS>
CS>Надо по-эксперементировать ...
Может проще будет создать 2 объекта сразу ?
P* f(bool need_a)
{
static A a;
static B b;
return need_a ? &a : &b;
}
Здравствуйте, c-smile, Вы писали:
CS>Существует ли способ сменить класс объекта в p? CS>Я понимаю что стандартными способами — нет. CS>Но в принципе это задача смены одного указателя на vtbl у объекта. CS>Как бы это сделать путем "наименьшего хака"? CS>Вот такой вот вопрос.
Вот эта вот кострукция компилируется на ура и делает то что надо:
#include"new.h"struct P
{
int data;
P() {}
virtual ~P() {}
virtual foo() = 0;
};
template <typename T>
inline P* ctor(int d) { P* t = new T; t->data = d; return t; }
template <typename T>
inline void turn_to(P* p) { ::new(p) T; }
struct A: public P
{
// no class specific data, only methodsvirtual foo() { ++data; }
};
struct B: public P
{
// no class specific data, only methodsvirtual foo() { --data; }
};
int main(int argc, char* argv[])
{
P *p = ctor<A>(0);
p->data = 0;
p->foo();
printf("step 1, p.data = %d\n", p->data);
turn_to<B>(p);
p->foo();
printf("step 2, p.data = %d\n", p->data);
return 0;
}
Проверяю дальше.
Re[2]: хочу странного: сменить класс объекта в runtime
template<class TFrom, class TTo> void SwitchTo( P* p )
E>{
E> static_assert( sizeof( TFrom ) >= sizeof( TTo ) );
E> assert( dynamic_cast<TFrom*>( p ) != 0 );
p->~P();
E> new( p ) TTo;
E> assert( dynamic_cast<TTo*>( p ) != 0 );
E>}
E>
Мне p->~P(); как раз не нужен.
Мне нужно переключить behavior класса в зависимости от некоего условия.
Операции конструирования и копирования объектов достаточно дорогие.
Поэтому все что я могу себе позволить это переключить vtbl
E>Вроде даже без UB всюду...
Не вижу я там UB. TFrom и TTo ничего не добавляют в instance.
Отличие этих классов сугубо в vtbl.
Re[3]: хочу странного: сменить класс объекта в runtime
namespace html
{
struct element
{
... data ...
void do_layout() = 0;
};
struct paragraph : public element
{
void do_layout() { layout as a text container };
};
struct div : public element
{
void do_layout() { layout as a block container };
};
}
Каждый элемент в зависимости от CSS атрибута:
display-model: block-inside | inline-inside | table
должен переключать способ do_layout и чертову силу других методов.
Причем display-model может переключаться динамически (во всяком случае это не запрещено)
Вот такие вот пироги. Два (5 на самом деле) объекта создавать само собой я даже и не знаю как.
[]
CS>Мне p->~P(); как раз не нужен.
CS>Мне нужно переключить behavior класса в зависимости от некоего условия. CS>Операции конструирования и копирования объектов достаточно дорогие. CS>Поэтому все что я могу себе позволить это переключить vtbl
Только зачем это делать, рискуя нарваться на неприятности с placement new?
Здравствуйте, c-smile, Вы писали:
CS>Есть два класса-потомка (A и B) от общего базового абстракта P.
ну первое это vtab вручную — поместить в класс указатели на методы.
Просто переписать vtab по адресу (viud *(&))(*this)
placement new — воспользоватся раздельной компиляцией и описать одноименный пустой класс без всяких data только обьявления виртуальных методов в томже порядке. И public на те классы где есть виртуальные методы. Тогда по идее структура vtabl сохранится, а побочного ффекта от конструкторов-деструкторов не будет
Описать сначала невиртуальные методы, потом перекрыть (продублировать) ихже виртуальными, тогда можно вернутся к той базе где виртуальных нет
Re[5]: хочу странного: сменить класс объекта в runtime
Здравствуйте, c-smile, Вы писали:
CS>должен переключать способ do_layout и чертову силу других методов.
CS>Причем display-model может переключаться динамически (во всяком случае это не запрещено)
Читать "Паттерны проектирования". Решение твоей проблемы — паттерн State, вероятнее всего тебе также понадобятся Factory method и Composite.
В vtbl лезть не стоит.
Re[6]: хочу странного: сменить класс объекта в runtime
Здравствуйте, gandjustas, Вы писали:
G>Здравствуйте, c-smile, Вы писали:
CS>>должен переключать способ do_layout и чертову силу других методов.
CS>>Причем display-model может переключаться динамически (во всяком случае это не запрещено)
G>Читать "Паттерны проектирования". Решение твоей проблемы — паттерн State, вероятнее всего тебе также понадобятся Factory method и Composite.
CS>Используются только документирванные фичи языка.
В таком простом случае можно было бы хранить тим в поле элемента, а в do_layout() использовать switch.
...А потом почитать Фаулера "Рефакторинг" и преобразовать это дело в State/Strategy.
Здравствуйте, korzh.pavel, Вы писали:
KP>Здравствуйте, c-smile, Вы писали:
CS>>Мне нужно переключить behavior класса в зависимости от некоего условия. CS>>Операции конструирования и копирования объектов достаточно дорогие. CS>>Поэтому все что я могу себе позволить это переключить vtbl
KP>http://rsdn.ru/forum/message/626591.aspx
CS>>Используются только документирванные фичи языка.
G>В таком простом случае можно было бы хранить тим в поле элемента, а в do_layout() использовать switch.
Извиняюсь, но это "индийский способ" решать проблемы. Это хак но архитектурный.
В реалии методов далеко за 20 и будет больше.
G>...А потом почитать Фаулера "Рефакторинг" и преобразовать это дело в State/Strategy.
Здравствуйте, Константин Л., Вы писали:
КЛ>Здравствуйте, c-smile, Вы писали:
КЛ>[]
CS>>Мне p->~P(); как раз не нужен.
CS>>Мне нужно переключить behavior класса в зависимости от некоего условия. CS>>Операции конструирования и копирования объектов достаточно дорогие. CS>>Поэтому все что я могу себе позволить это переключить vtbl
КЛ>Только зачем это делать, рискуя нарваться на неприятности с placement new?
1) "Зачем?" — потому что это максимально эффективный способ
2) "на неприятности с placement new" — какие такие неприятности?
placement new это базовая фича языка используемая повсеместно (например в stl и boost).
КЛ>здесь
Здравствуйте, Erop, Вы писали:
CS>>Есть два класса-потомка (A и B) от общего базового абстракта P. CS>>Как бы это сделать путем "наименьшего хака"? E>Тема раз: E>
Здравствуйте, c-smile, Вы писали:
CS>1) "Зачем?" — потому что это максимально эффективный способ
Не рановато ли вы оптимизацией занимаетесь? Даже при использовании State можно избежать затрат на конструирование объекта без хакерства.
CS>2) "на неприятности с placement new" — какие такие неприятности?
Лишаете себя автоматического деструктора, сильно урезая возможности ООП в C++.
CS>Этот метод может создать лишний redirection при вызове методов. Накладно.
По сравнению с new для каждого element второй вызов метода — мелочь.
C>Ага, конечно. Нет тут у тебя UB, панимаешь... А про выравниванием кто думать будет?
К твоему сведению, new возвращает память максимально выравненнуюю.
Как по твоему работает new int[256]?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
C>>Ага, конечно. Нет тут у тебя UB, панимаешь... А про выравниванием кто думать будет? E>К твоему сведению, new возвращает память максимально выравненнуюю.
AFAIR, только для нужного типа. Это malloc() возвращает максимально выравненую память.
E>Как по твоему работает new int[256]?
Так и работает. Но это не значит, что "*((double*)new int[2])=1.0d" будет работать так как надо.
Здравствуйте, c-smile, Вы писали:
CS>Мне p->~P(); как раз не нужен.
CS>Мне нужно переключить behavior класса в зависимости от некоего условия. CS>Операции конструирования и копирования объектов достаточно дорогие.
Тема 1:
Можно сделать данные разделяемыми несколькими "инстансами" объекта.
И использовать любой хак после этого.
CS>Поэтому все что я могу себе позволить это переключить vtbl
Тема 2:
Вообще говоря, обычно вред от виртуальности вызова происходит не из-за того, что вызов как-то нереально дорог, а из-за того, что виртуальный вызов не встраивается.
Так что обычно цена вопроса "один виртуальный вызов или два подряд" -- это несколько тактов. Так что, ИМХО твой выбор прост -- двойная деспечерезация
CS>Не вижу я там UB. TFrom и TTo ничего не добавляют в instance. CS>Отличие этих классов сугубо в vtbl.
Ну в стандарте никакого vtbl нету...
Представь себе реализацию, в которой виртуальность обеспечивается пйтём регистрации объектов в рантайме. Типа конструктор неявно регит в каком-то map'е в рантайме объект, потом при виртуальном вызове по этому map'у смотрят какого же он типа, на самом деле, и зовут что надо. Это ничем не противоречит стандарту...
Другое дело, что если тебя волнует вполне определённый набор компиляторов, то частенько на каждом из них конкретные случаи UB хорошо докуминтированы и могут быть использованы
Но в твоём случае, ИМХО, игра свеч не стоит.
Ну регай какой-нибудь объект-редиректор, который зовёт нужный метод из пяти, при случае.
Тем более, что ты можешь сделать и как-то так:
При этом CMyItemData может иметь реализации всех методов процессоров, а процессоры будут просто осуществлять перевызов.
Опять же, обращаю внимание, что при таком подходе методы MethodХХХ класса CMyItem нифига не виртуальные, так что даже цепочки из двух вызовов не получаеся.
Но в целом примерно это тебе тут уже советовали, но почему-то не прямо, а ссылаясь на книжки и прочее...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Cyberax, Вы писали:
E>>Как по твоему работает new int[256]? C>Так и работает. Но это не значит, что "*((double*)new int[2])=1.0d" будет работать так как надо.
Я два ответа послал, но второй что-то не появляется
Короче, общаая идея такая, что мне казалось, что есть гарантия, что new char[count] возвращает то же, что вернёт ::operator new( count ), но я её что-то не найду.
Так что наверное ты прав. Надо звать ::operator new/::operator delete непосредственно
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, gandjustas, Вы писали:
G>Здравствуйте, c-smile, Вы писали:
CS>>1) "Зачем?" — потому что это максимально эффективный способ G>Не рановато ли вы оптимизацией занимаетесь? Даже при использовании State можно избежать затрат на конструирование объекта без хакерства.
"Не рановато" — нет.
Речь идет о живом продукте. Вариации вот этого http://www.terrainformatica.com/htmlayout/
CS>>2) "на неприятности с placement new" — какие такие неприятности? G>Лишаете себя автоматического деструктора, сильно урезая возможности ООП в C++.
Где я себя лишаю? Эти объекты на стеке не создаются в принципе.
Re[2]: хочу странного: сменить класс объекта в runtime
Здравствуйте, c-smile, Вы писали:
CS>Здравствуйте, c-smile, Вы писали:
CS>>Существует ли способ сменить класс объекта в p? CS>>Я понимаю что стандартными способами — нет. CS>>Но в принципе это задача смены одного указателя на vtbl у объекта. CS>>Как бы это сделать путем "наименьшего хака"? CS>>Вот такой вот вопрос.
CS>Вот эта вот кострукция компилируется на ура и делает то что надо:
Здесь ты можешь поиметь проблемы при агрессивной оптимизации из-за alliasing.
Если хочешь быстро и легально то придется vtable ручками писать. Нет в C++ средств автоматизации для твоего случая.
Здравствуйте, Шахтер, Вы писали:
Ш>Здравствуйте, c-smile, Вы писали:
Ш>Сменить тип нельзя, но можно пересоздать объект на том же месте.
Ш>
Ш> template <class T>
Ш> typename Enable<T>::Ret // instead of P *
Ш> mutate()
Ш> {
Ш> P *ret=this;
Ш> D temp;
Ш> saveTo(&temp);
Ш> ret->>~P();
Ш> new(ret) T(&temp);
Ш> return ret;
Ш> }
Ш> };
Интересно, и это близко к тому что я написал.
Единственно я бы тогда уже сделал так:
[ccode]
struct P
{
template <typename T>
void mutate()
{
static_assert( sizeof(T) == sizeof(*this) );
T temp;
swap(temp);
this->~P();
new(this) T;
temp.swap(*this);
}
};
Это в принципе требует работы ctor/dtor дополнительных но зато
можно mutate звать без всяких переделок так как метод swap уже
определен у P и проверен в бою. И мне нужно смену секса по месту делать.
Есть внешние ссылки на объект.
Ш>Насколько я понимаю, слюдующий код легален (хотя на 200% не уверен). Впрочем, если ты допускаешь хаки, то должно быть всё в порядке.
Да вот я не вижу где тут хак а принципе.
Со swap кстати еще один плюс — пара
swap(temp);
temp.swap(*this);
гарантирует совместимость типов.
Re[3]: хочу странного: сменить класс объекта в runtime
Это менее эффективно. Ты здесь делаешь обмен состояниями с temp, а потом с новым объектом. Т.е. грубо говоря 6 копирований. Вместо 2.
CS>Это в принципе требует работы ctor/dtor дополнительных но зато CS>можно mutate звать без всяких переделок так как метод swap уже CS>определен у P и проверен в бою. И мне нужно смену секса по месту делать.
CS>Есть внешние ссылки на объект.
Это как раз не проблема в данном случае -- эти ссылки будут указывать на объект типа P, только новый.
Ш>>Насколько я понимаю, слюдующий код легален (хотя на 200% не уверен). Впрочем, если ты допускаешь хаки, то должно быть всё в порядке.
CS>Да вот я не вижу где тут хак а принципе.
CS>Со swap кстати еще один плюс — пара CS>
CS>swap(temp);
CS>temp.swap(*this);
CS>
CS>гарантирует совместимость типов.
Насчет совместимости типов -- можно добавить static_assert на то, что T -- производный от P.
Ш>Это менее эффективно. Ты здесь делаешь обмен состояниями с temp, а потом с новым объектом. Т.е. грубо говоря 6 копирований. Вместо 2.
Хм... давай смотреть вместе:
mutate()
{
P *ret=this;
D temp;
saveTo(&temp); // swap with temp, *this is an "empty" thing now.
ret->>~P(); // destroy this, already empty thing, harmlessnew(ret) T(&temp); // shall do swap from temp.return ret;
} // dtor of temp, it should be empty at this moment
Как ты видишь количество swap ровно такое же как у меня.
Или я чего не вижу?
Re: хочу странного: сменить класс объекта в runtime
c-smile пишет: > Существует ли способ сменить класс объекта в p? > Я понимаю что стандартными способами — нет.
Как это нет ? Очень даже можно, толко это будет не тот уже объект.
Сначала надо получить ссылку на старый объект,
потом на его основе создать объект нового типа, выделив
под него новую память (которая может быть меньше, равна или больше
по объему старому объекту), потом старый объект удалить.
Ну и вернуть наружу адрес уже нового объекта.
Posted via RSDN NNTP Server 2.1 beta
Re[3]: хочу странного: сменить класс объекта в runtime
R>При необходимости можно добавить неограниченное кол-во наворотов сверху. R>Можно сделать отдельно visitor и const_visitor для пущей стройности. R>Можно добавить шаблонов, что бы не писать много скучных virtual void do_layout(element_visitor& e) {e.do_layout(*this);} R>Можно добавить acyclic visitor для разрыва циклической зависимости. R>И т.д.
Чем это лучше простой смены типа?
Смена типа в данном случае это вызов одной функции в момент смены стиля
(что в быту происходит нечасто):
Здравствуйте, c-smile, Вы писали:
CS>2) нужно представлять во что выливается конструкция CS>
CS>(self->*f)();
CS>
CS>в рантайм.
Да ни во что она особое не выливается. Лишнее разыменование, которое хорошо конвейрезуется и парится.
Я тебе уже давно советую сделать такой рефакторинг, но ты чего-то не хочешь.
У тебя чего больше? Кода классов, которые ты хочешь "переключать", или коиентского кода?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, c-smile, Вы писали:
CS>1) Ты вводишь новую сущность — struct last т.е. весь существующий код CS>типа: CS>
CS> base* pb = ... ;
pb->>draw();
CS>
CS>надо злостно рефакторить.
Не, просто last -- это то, что теперь будет видеть, под видом base старый код
Смотри, как можно сделать:
// Было примерно так (схема упрощённая, так как у Base может быть много наследников...
// Ко всем классам, которые надо изменить припишу слово Oldclass OldBase {
public:
virtual void Method1() = 0; // На самом деле тут есть два варианта Impl1 и Impl2protected:
BaseData data;
};
class OldImpl1 : public OldBase {
public:
virtual void Method1() { data.Impl1(); }
};
class OldImpl2 : public OldBase {
public:
virtual void Method1() { data.Impl2(); }
};
void foo( OldBase* obj )
{
obj->Method1();
}
// а станет такstruct IBaseProcessor {
virtual void Method( BaseData& ) = 0;
};
class CBase {
public:
void Method1() { processor->Method( data ); }
protected:
IBaseProcessor* processor;
BaseData data;
};
struct CBasePocessorImpl1 : IBaseProcessor {
virtual void Method( BaseData& data ) { data.Impl1(); }
};
struct CBasePocessorImpl2 : IBaseProcessor {
virtual void Method( BaseData& data ) { data.Impl2(); }
};
foo( Base * base )
{
base->Method1(); // вообще ничего не меняется!!!
}
Соответсвенно, реальность, скорее всего хитрее.
То есть наверное у base есть несколько наследников. И у каждого есть своя переключаемая реализация.
Ну так и это не мешает
Просто надо привинтить какую-то регистрилку процессоров, по id наследника. И уметь получать из этой регистрилки процессор под нужный тип.
Тогда ты просто статическими регистраторами регишь новую имплементацию, получаешь статический же объект, выдающий процессоры. Корорый, при переключеннии имплементации отдаёшь во все свои объекты и объекты получают из этой регистрилки свои процессоры. Ну а потом всё работает как раньше.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
E>Я тебе уже давно советую сделать такой рефакторинг, но ты чего-то не хочешь.
Зачем?
В C++ объект это есть структура содержащая указатель на vtbl + данные инстанса.
По условию задачи мне нужно менять значение указателя vtbl
т.е. замена 1 (одного) указателя. Решается все 1 (одной) одной функцией из 5 (пяти) строк.
Я не могу понять — за ради каких вселенских принципов предлагается
1) зарефакторить 50тыс. строк кода 2) заведомо менее эффективным решением.
Здравствуйте, c-smile, Вы писали:
CS>Зачем?
Чтобы код был более поддерживаемым. Если у вас и так получается поддерживать 50 000 строк такого кода с хаками, ну значит и так у вас всё хорошо.
Хотя можно рефакторить по чуть-чуть. Типа переписывать классы по одному, например.
CS>В C++ объект это есть структура содержащая указатель на vtbl + данные инстанса. CS>По условию задачи мне нужно менять значение указателя vtbl
Это всё неправда, увы. Например при множественном наследовании...
Но пока не бахает, не бахает. Просто когда бахнет прийдётся нагнуться раком и всё как-то резко поменять
CS>т.е. замена 1 (одного) указателя. Решается все 1 (одной) одной функцией из 5 (пяти) строк.
Ну надо ещё иметь шизофриническую довольно структуру "параллельных" объектов, как-то поддерживать её параллельность и т. п.
Конечно 50 000 строк -- это немного совсем кода, но всё равно совсем вручную это поддерживать уже страшновато. Лично мне было бы, во всяком случае
CS>Я не могу понять — за ради каких вселенских принципов предлагается CS>1) зарефакторить 50тыс. строк кода 2) заведомо менее эффективным решением.
1) Потери эффективности нет
2) Надёжность и поддерживаемость. Откуда ты знаешь, что хотя бы сейчас у тебя вс работает совсем корректно?
3) Удобство отладки, в конце концов. Отладчику крышу не сносит часом?
4) Если ты займёшься каким-то более важным и нужным и новым и, скорее всего интересным, проектом, то поддержку надо будет передать кому-то ещё. ИМХО так нетрадиционно написанный код этот "кто-то ещё" будет поддерживать плохо и долго не въедет
Ну и вообще, я таки думаю, что у тебя вообще архитектура немного неудачная для твоей задачи.
Скорее всего нужен вообще заметный рефакторинг. Увы.
Но это вс вопрос бизнесстратегии. Грубо говоря сколько денег ты готов потратить на инфраструктурные инвестиции. Типа если проект будет жить ещё 10 лет и развиваться, то инфраструктурные инвестиции очень выгодны. Просто они долго очень отбиваются. А если два месяца -- то инфраструктурные инвестиции -- пустая трата сил
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, c-smile, Вы писали:
CS>Здравствуйте, remark, Вы писали:
CS>>>>Чем это лучше простой смены типа? R>>>Ничем. Кому что нравится.
CS>В нашем деле слово нравится есть эквивалент "технически грамотно".
"технически грамотных" может быть несколько вариантов, а дальше — кому что нравится
CS>1) Ты вводишь новую сущность — struct last т.е. весь существующий код
Можно держать указатель только на base:
last l;
base& b1 = static_cast<impl1&>(l);
base& b2 = static_cast<impl2&>(static_cast<last&>((b1));
При "переключении" можно прямо из "одного" base получить "второй" base.
Про last можно забыть до удаления, а можно и вообще, т.к. при удалении можно тоже по base восстановить last.
Здравствуйте, remark, Вы писали:
R>Если на это положиться, то можно просто перезаписывать vptr, даже никакого С не надо:
Всё равно отсанется необходимость в ручном поддержании "эквивалентности" различных реализаций
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, c-smile, Вы писали:
CS>Здравствуйте, Erop, Вы писали:
E>>Я тебе уже давно советую сделать такой рефакторинг, но ты чего-то не хочешь.
CS>Зачем?
CS>В C++ объект это есть структура содержащая указатель на vtbl + данные инстанса. CS>По условию задачи мне нужно менять значение указателя vtbl CS>т.е. замена 1 (одного) указателя. Решается все 1 (одной) одной функцией из 5 (пяти) строк.
ну так давно пора закрыть ветку, раз ты все для себя решил.
CS>Я не могу понять — за ради каких вселенских принципов предлагается CS>1) зарефакторить 50тыс. строк кода 2) заведомо менее эффективным решением.
А я не могу понять, почему ты так относишься к предложениям, которые выдвигаются по твоей собственной просьбе? Зачем все в штыки принимать?
Здравствуйте, Константин Л., Вы писали:
CS>>В C++ объект это есть структура содержащая указатель на vtbl + данные инстанса. CS>>По условию задачи мне нужно менять значение указателя vtbl CS>>т.е. замена 1 (одного) указателя. Решается все 1 (одной) одной функцией из 5 (пяти) строк.
КЛ>ну так давно пора закрыть ветку, раз ты все для себя решил.
Да пора.
CS>>Я не могу понять — за ради каких вселенских принципов предлагается CS>>1) зарефакторить 50тыс. строк кода 2) заведомо менее эффективным решением.
КЛ>А я не могу понять, почему ты так относишься к предложениям, которые выдвигаются по твоей собственной просьбе? Зачем все в штыки принимать?
Мда, народ упорно не читает ТЗ.
Вопрос был такой "Существует ли способ сменить класс объекта в p?"
Все.
Здравствуйте, c-smile, Вы писали:
CS>Мда, народ упорно не читает ТЗ. CS>Вопрос был такой "Существует ли способ сменить класс объекта в p?" CS>Все.
Ну тебе предлагали разные решения на эту тему.
Например, я
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: хочу странного: сменить класс объекта в runtime
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, remark, Вы писали:
R>>Если на это положиться, то можно просто перезаписывать vptr, даже никакого С не надо:
E>Всё равно отсанется необходимость в ручном поддержании "эквивалентности" различных реализаций
Что такое "ручное поддержании эквивалентности" в твоем понимании?
class P
{
virtual int foo() = 0;
}
class A
{
virtual int foo() { ... 1 }
}
class B
{
virtual int foo() { ... 2 }
}
Это вот "ручное" или нет?
Re[5]: хочу странного: сменить класс объекта в runtime
Здравствуйте, c-smile, Вы писали:
CS>Что такое "ручное поддержании эквивалентности" в твоем понимании?
CS>
CS>class P
CS>{
CS> virtual int foo() = 0;
CS>}
CS>class A
CS>{
CS> virtual int foo() { ... 1 }
CS>}
CS>class B
CS>{
CS> virtual int foo() { ... 2 }
CS>}
CS>
CS>Это вот "ручное" или нет?
Я так понимаю, что обычно жизнь немного не такая.
1) методов много
2) Есть данные. Они должны быть одинаковыми везде, вроде как. Или ты выводишь свои "стратегии" из реального класса с данными?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
[]
CS>Мда, народ упорно не читает ТЗ. CS>Вопрос был такой "Существует ли способ сменить класс объекта в p?" CS>Все.
не хочется флеймить, но ты упорно пытаешься "продвинуть" свой вариант с placement new, выдвигая аргумент "зачем", вместо того, чтобы рассмотреть все плюсы и минусы. Не безопасен он. Как только размеры объектов изменятся, ты получишь вылет в корку, а не compile-time error/warning.
Re[6]: хочу странного: сменить класс объекта в runtime
Здравствуйте, Erop, Вы писали:
CS>>Это вот "ручное" или нет?
E>Я так понимаю, что обычно жизнь немного не такая. E>1) методов много E>2) Есть данные. Они должны быть одинаковыми везде, вроде как. Или ты выводишь свои "стратегии" из реального класса с данными?
Цитата из начального сообщения:
class P
{
int data;
virtual foo() = 0;
}
class A: public P
{
// no class specific data, only methodsvirtual foo() { ++data; }
}
class B: public P
{
// no class specific data, only methodsvirtual foo() { --data; }
}
Обрати внимание на комментарии.
Re[7]: хочу странного: сменить класс объекта в runtime
Здравствуйте, c-smile, Вы писали:
CS>Цитата из начального сообщения: CS>Обрати внимание на комментарии.
Ну всё равно тонкости бывают. Ты же модель показываешь только. Мне твой подход всё равно кажется опасным.
Даже о том, что ты не все методы перекрыл тебя не предупредят.
Ну а в случае множественного наследования в базе вообще всё плохо будет
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Константин Л., Вы писали:
КЛ>Здравствуйте, c-smile, Вы писали:
КЛ>[]
CS>>Мда, народ упорно не читает ТЗ. CS>>Вопрос был такой "Существует ли способ сменить класс объекта в p?" CS>>Все.
КЛ>не хочется флеймить, но ты упорно пытаешься "продвинуть" свой вариант с placement new, выдвигая аргумент "зачем", вместо того, чтобы рассмотреть все плюсы и минусы. Не безопасен он. Как только размеры объектов изменятся, ты получишь вылет в корку, а не compile-time error/warning.
Таки да, народ упорно не читает ТЗ.
Написано же ведь изначально что A и B не изменяют размер instance — т.е. новые data members не добавляют.
Соответсвенно вопрос в этом контексте: чем конкретно вариант с placement new "Не безопасен он." ?
Здравствуйте, c-smile, Вы писали:
CS>Написано же ведь изначально что A и B не изменяют размер instance — т.е. новые data members не добавляют. CS>Соответсвенно вопрос в этом контексте: чем конкретно вариант с placement new "Не безопасен он." ?
Ты уж прости, но это ты ответы не читаешь
Собственно тебе предлагался вариант с new размещения, совсем безопасный. Но ты сказал, что жалко звать конструктор класса, так как там типа данные дорогие. Так кажется дело было?
Тогда начались тёрки как избежать ненужного копирования данных...
А что касается опасностей, то
1) Можно запутаться какой класс какой реализует и переключить что-нибудь несовместимо. При этом узнаешь ты об этом, только когда управление до этой несовместимости дойдёт. Не раньше
2) Если будет так:
struct i1 {
virtual void method1() = 0;
};
struct i2 {
virtual void method2() = 0;
};
class p : public i1, public i2 {
private:
myData data;
};
То твой хак уже не сработает корректно
3) А ещё бывают виртуальны деструкторы, например...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[8]: хочу странного: сменить класс объекта в runtime
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, c-smile, Вы писали:
CS>>Цитата из начального сообщения: CS>>Обрати внимание на комментарии.
E>Ну всё равно тонкости бывают. Ты же модель показываешь только. Мне твой подход всё равно кажется опасным.
"тонкости бывают", "кажется" — это из какой оперы?
E>Даже о том, что ты не все методы перекрыл тебя не предупредят.
Что ты имеешь ввиду? Кто не предупредит?
Или ты считаешь что
new(p) B;
скомпилируется если у тебя абстрактные методы не имплементированы в B?
E>Ну а в случае множественного наследования в базе вообще всё плохо будет
Да ну нет там множественного наследования! См. условия в начальном сообщении.
Здравствуйте, c-smile, Вы писали:
CS>Здравствуйте, remark, Вы писали:
R>>З.ы. "хакерское" создание нового объекта на месте старого — не смена типа *объекта*
CS>Чем конкретно оно "хакерское"?
Ну не знаю... для меня лично, это очевидно, я не знаю как это передать... "хакерское" — это скорее качественная характеристика, нежели количественная...
Ну вот например. Я отлаживаю код в отладчике. Мой объект запоминает указатель на другой объект. Я запоминаю адрес объекта и его динамический тип по таблице виртуальных функций. Далее через некотрое время я опять смотрю на свой объект. Хопа! Динамический тип изменился! Адрес остался прежним, а тип поменялся. Первый признак либо расстрела памяти, либо обращения к удалённому объекту. И понеслась! Кровопролитный дебаг на пару часов.
Ну ты должен знать слова, которыми вспоминают программиста, который такое написал, когда всё-таки понимают в чём дело Лучше тебе рядом не находится
Здравствуйте, remark, Вы писали:
R>>>З.ы. "хакерское" создание нового объекта на месте старого — не смена типа *объекта*
CS>>Чем конкретно оно "хакерское"?
R>Ну не знаю... для меня лично, это очевидно, я не знаю как это передать... "хакерское" — это скорее качественная характеристика, нежели количественная...
Хакерское решение это например когда используются недокументирванные фичи.
Здесь такого нет.
R>Ну вот например. Я отлаживаю код в отладчике. Мой объект запоминает указатель на другой объект. Я запоминаю адрес объекта и его динамический тип по таблице виртуальных функций. Далее через некотрое время я опять смотрю на свой объект. Хопа! Динамический тип изменился! Адрес остался прежним, а тип поменялся. Первый признак либо расстрела памяти, либо обращения к удалённому объекту. И понеслась! Кровопролитный дебаг на пару часов. R>Ну ты должен знать слова, которыми вспоминают программиста, который такое написал, когда всё-таки понимают в чём дело Лучше тебе рядом не находится
Лирика какая-то, право слово.
Обрати внимание на "покраснение" справа:
Re[9]: хочу странного: сменить класс объекта в runtime
Здравствуйте, c-smile, Вы писали:
CS>Или ты считаешь что CS>
CS>new(p) B;
CS>
CS>скомпилируется если у тебя абстрактные методы не имплементированы в B?
Я так и не понял, ты пересоздаёшь данные в конструкторе или нет.
Если нет, но я тебе предложил совсем корректное решение. Но ты сам вроде говорил, что оно тебя не устраивает, так как ты не хочешь звать конструкторы объектов A и B так как долго строятся данные...
CS>Да ну нет там множественного наследования! См. условия в начальном сообщении.
В начальном сообщении вообще нет наследования. P -- просто корневой абстрактный класс.
Но если всё так просто, то я вообще не понимю, зачем так хаккерить?
Чем тогда тебе State не подходит, либо решение от remark'a?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, c-smile, Вы писали:
CS>Здравствуйте, remark, Вы писали:
R>>>>З.ы. "хакерское" создание нового объекта на месте старого — не смена типа *объекта*
CS>>>Чем конкретно оно "хакерское"?
R>>Ну не знаю... для меня лично, это очевидно, я не знаю как это передать... "хакерское" — это скорее качественная характеристика, нежели количественная...
CS>Хакерское решение это например когда используются недокументирванные фичи.
Это уже не хакерское, это уже "работающее по счастливой случайности"
CS>Здесь такого нет.
Нет, ну выбирать тебе. Я думаю, ты сам прекрасно понимаешь, что я имею в виду.
R>>Ну вот например. Я отлаживаю код в отладчике. Мой объект запоминает указатель на другой объект. Я запоминаю адрес объекта и его динамический тип по таблице виртуальных функций. Далее через некотрое время я опять смотрю на свой объект. Хопа! Динамический тип изменился! Адрес остался прежним, а тип поменялся. Первый признак либо расстрела памяти, либо обращения к удалённому объекту. И понеслась! Кровопролитный дебаг на пару часов. R>>Ну ты должен знать слова, которыми вспоминают программиста, который такое написал, когда всё-таки понимают в чём дело Лучше тебе рядом не находится
CS>Лирика какая-то, право слово.
CS>Обрати внимание на "покраснение" справа:
Покраснение будет только при выполнении строчки, в которой это поменялось. Если разработчик нашёл и увидел эту строчку, то это уже половина дела. Речь о том, когда я ставлю точки останова только на моём, интересующим меня в данный момент, коде.
Здравствуйте, Erop, Вы писали:
E>Если нет, но я тебе предложил совсем корректное решение. Но ты сам вроде говорил, что оно тебя не устраивает, так как ты не хочешь звать конструкторы объектов A и B так как долго строятся данные...
Ты про это?
template<class T> P* CreateHackedObject()
{
void * buffer = new char[sizeof T];
return new( buffer ) T;
}
void DeleteHackedObject( P* p )
{
if( p == 0 )
return;
p->~P();
delete [] static_cast<char*>( (void*)p );
}
template<class TFrom, class TTo> void SwitchTo( P* p )
{
static_assert( sizeof( TFrom ) >= sizeof( TTo ) );
assert( dynamic_cast<TFrom*>( p ) != 0 );
p->~P();
new( p ) TTo;
assert( dynamic_cast<TTo*>( p ) != 0 );
}
Если да то я не понял твою мысль.
Вот это p->~P(); разрушает текущий instance, а new( p ) TTo; создает нечто пустое по месту. Это меняет условие задачи — данные instance должны сохраняться — их пересоздание — дорого.
И я не понял про CreateHackedObject() и DeleteHackedObject( P* p ) — зачем они? Почему не просто new A или new B и delete p?
Здравствуйте, c-smile, Вы писали:
CS>Ты про это?
Да.
CS>Если да то я не понял твою мысль.
Вот и надо было задавать вопросы, а не прикалываться
CS>Вот это p->~P(); разрушает текущий instance, а new( p ) TTo; создает нечто пустое по месту. Это меняет условие задачи — данные instance должны сохраняться — их пересоздание — дорого.
Обычно конструктор и деструктор симметричны. То есть то что один разрушает, то другой создаёт.
Это обозначает, что у тебя конструктор A будет создавать и P, значит будет создавать и все данные.
Что в свою очередь обозначает, что "предыдущее значение данных" может пострадать.
Смотри, например
struct A { std::string text; };
struct B { std::string text; };
A* a = new A; // зовутся конструкторы полей A, в том числе и для A::text зовётся конструктор std::string
a->text = "text A";
B* b = new( a ) B; // Тут на месте a->text зовётся конструктор B::text, то есть опять таки std::string.
// Старое тело скорее всего теряется
b->text = "text B";
delete b; // Всё нормально пока, правда ~A так никогда и не был позван :(
Соответсвенно тебе всё равно надо как-то решить проблему с пересозданием данных в конструкторах A и B. ИМХО, самый прямой путь -- хранить в P (а значит в A и B) указатель на данные. Но реально тут возможно много путей. Их тебе вроде показали отчасти.
Например, если данные недорого swap'ать и недорого создавать пустой P, то можно сделать так:
template<class TTo, class P> void SwitchTo( P* p )
{
static_assert( sizeof( P ) == sizeof( TTo ) );
P tmp;
std::swap( *p, tmp );
p->~P();
new( p ) TTo;
std::swap( *p, tmp );
assert( dynamic_cast<TTo*>( p ) != 0 );
}
CS>И я не понял про CreateHackedObject() и DeleteHackedObject( P* p ) — зачем они? Почему не просто new A или new B и delete p?
Ну, вообще-то говоря никто не обещал, что если ты сделаешь так:
P* p = new A;
static_cast<A*>( p )->~A();
new( p ) B;
delete static_cast<B*>( p );
То всё будет хорошо. В целом operator new/delete и деструктор часто в реализациях смешиваются. Неплохо бы как-то нежнее с компилятором обходиться.
Вот про new размещения вроде как гарантируется, что можно звать деструктор явно без непонятных последствий. Ну так явно и зовём:
template<class T> P* CreateHackedObject()
{
void * buffer = ::operator new ( sizeof T );
return new( buffer ) T;
}
void DeleteHackedObject( P* p )
{
if( p == 0 )
return;
p->~P();
::operator delete( p );
}
Ну правда. Перепиши в своём примере с P, A, B поля с int на что-нибудь более сложное. Скажем на std::string. Сразу начнёшь меня понимать
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[5]: хочу странного: сменить класс объекта в runtime
Ш>>Это менее эффективно. Ты здесь делаешь обмен состояниями с temp, а потом с новым объектом. Т.е. грубо говоря 6 копирований. Вместо 2.
CS>Хм... давай смотреть вместе:
CS>
CS>mutate()
CS> {
CS> P *ret=this;
CS> D temp;
CS> saveTo(&temp); // swap with temp, *this is an "empty" thing now.
ret->>>~P(); // destroy this, already empty thing, harmless
CS> new(ret) T(&temp); // shall do swap from temp.
CS> return ret;
CS> } // dtor of temp, it should be empty at this moment
CS>
CS>Как ты видишь количество swap ровно такое же как у меня.
CS>Или я чего не вижу?
Я не делаю swap, я копирую данные. Т.е. saveTo сохраняет состояние объекта. Это одно копирование.
Если у тебя состояние содержит хендлы, то их нужно будет ещё занулить, чтобы они в деструкторе не закрылись. Для них добавляется зануление.
Наконец, в конструкторе мы просто восстанавливаем состояние всех полей. Одно копирование. Идея подчерпнута из лекций Степанова. Можно, конечно и через swap сделать, но это менее эффективно.
Здравствуйте, Шахтер, Вы писали:
Ш>Я не делаю swap, я копирую данные. Т.е. saveTo сохраняет состояние объекта. Это одно копирование.
Вообще-то никто не мешает специфицировать std::swap для своего типа. Тогда всё будет эффективно.
Плюс метода состоит в том, что специфицировать надо только доля тех типов, для которых будут детектированы тормоза...
Правда и saveTo можно гаписать шаблонный...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Константин Л., Вы писали:
КЛ>Внимание! Нелюбитель шаблонов пишет шаблонный код! Перевосптался!
Я не любитель не шаблонов вообще, а шаблонов, которые не проектировались или проектировались плохо
Если тут делать всё стразу нормально, то никаких шаблнов вообще-то не надо, но я предположил, что ситуация изначально плохая...
Ну а так вообще-то есть какие-то предложения по существу?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: хочу странного: сменить класс объекта в runtime
конечно, все не так уж и гладко (особенно если вспомнить о виртуальных деструкторах, размещении информации о типе, переносимости, хороших манерах и проч. и т.п.), но вот этот ужасный код работает
#include <iostream>
using namespace std;
class Base
{
public:
Base()
{
}
virtual ~Base()
{}
public:
// methods to overridevirtual void foo() = 0;
virtual void doo() = 0;
};
struct Bar
: Base
{
void foo()
{
cout << "Bar::foo\n";
}
void doo()
{
cout << "Bar::doo\n";
}
};
struct Baz
: Base
{
void foo()
{
cout << "Baz::foo\n";
}
void doo()
{
cout << "Baz::doo\n";
}
};
void cast_spell( Base * from, Base * to )
{
struct VtblHolder
{
void * vtbl;
};
VtblHolder * vtbl_holder_1 = reinterpret_cast<VtblHolder *>( from );
VtblHolder * vtbl_holder_2 = reinterpret_cast<VtblHolder *>( to );
vtbl_holder_1->vtbl = vtbl_holder_2->vtbl;
}
void black_magic_test()
{
Base * pbar = new Bar();
Base * pbaz = new Baz();
cast_spell( pbar, pbaz );
pbar->foo();
pbar->doo();
delete pbar, pbaz;
}
int main( int argc, char * argv[] )
{
black_magic_test();
return 0;
}
Re[3]: хочу странного: сменить класс объекта в runtime
Здравствуйте, StevenIvanov, Вы писали:
SI>протестировано и работает на x86-windows-msvc8.0, x86-linux-g++, arm-linux-g++
Это скорее всего взорвётся не при смене платфолрмы, а при какой-то неожиданной модификации кода.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[5]: хочу странного: сменить класс объекта в runtime
Здравствуйте, Erop, Вы писали:
E>... E>Это скорее всего взорвётся не при смене платфолрмы, а при какой-то неожиданной модификации кода.
Можно заранее утверждать, что взорвется, поскольку компилятором такая возможность не предоставляется и даже минимальная защита от дурака отсутствует.
Если хочется быть уверенным на все 100 — необходимо писать объявления таких классов только на С (реализация функций может быть сделана уже на C++). И вообще, по-моему я нечто подобное уже видел (но не хочу утверждать) в ядре linux в разделе кода для управления файловыми системами. Так вот, там есть код создания управляющей структуры драйвера конкретной файловой системы, которая состоит из указателей на функции read, write, close... Эти функции в некоторых случаях могут быть заменены на другие (что-то вроде реализация перехвата операций чтения-записи).
OFF: А по поводу рассматриваемой задачи — честно говоря я так и не понял почему нельзя использовать visitor? Я читал все (ну почти) посты и так и не понял почему это решение не подходит для такой задачи.
Re[6]: хочу странного: сменить класс объекта в runtime
Здравствуйте, StevenIvanov, Вы писали:
SI>OFF: А по поводу рассматриваемой задачи — честно говоря я так и не понял почему нельзя использовать visitor? Я читал все (ну почти) посты и так и не понял почему это решение не подходит для такой задачи.
Да подходит оно, ну может немного переточенное.
Просто я так понял, что уже есть некий код, и вопрос состоит в том, как его подешевле зарефакторить. Хотя лучше обратиться за разъяснениями к топикстартеру
Я вот попробовал так и сяк прикинуть как будет. ИМХО не сложно это и там и сям выходит. Вернее сравнимо сложно
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[7]: хочу странного: сменить класс объекта в runtime
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, Шахтер, Вы писали:
Ш>>Я не делаю swap, я копирую данные. Т.е. saveTo сохраняет состояние объекта. Это одно копирование. E>Вообще-то никто не мешает специфицировать std::swap для своего типа. Тогда всё будет эффективно.
saveTo оптимален
E>Плюс метода состоит в том, что специфицировать надо только доля тех типов, для которых будут детектированы тормоза... E>Правда и saveTo можно гаписать шаблонный...
Конечно. Более того, его можно через через swap реализовать.
Здравствуйте, Шахтер, Вы писали:
Ш>saveTo оптимален
Я согласен, только мне больше нравится название MoveTo
Во всяком случае в моей любимой библиотеке используется MoveTo...
Но в жанном случае моей любимой библиотеки нет, а std::swap есть
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[6]: хочу странного: сменить класс объекта в runtime
Здравствуйте, StevenIvanov, Вы писали:
SI>Здравствуйте, Erop, Вы писали:
E>>... E>>Это скорее всего взорвётся не при смене платфолрмы, а при какой-то неожиданной модификации кода. SI>Можно заранее утверждать, что взорвется, поскольку компилятором такая возможность не предоставляется и даже минимальная защита от дурака отсутствует.
"защита от дурака отсутствует" не беспокит. Дураки к тому коду доступа не имеют.
SI>Если хочется быть уверенным на все 100 — необходимо писать объявления таких классов только на С (реализация функций может быть сделана уже на C++). И вообще, по-моему я нечто подобное уже видел (но не хочу утверждать) в ядре linux в разделе кода для управления файловыми системами. Так вот, там есть код создания управляющей структуры драйвера конкретной файловой системы, которая состоит из указателей на функции read, write, close... Эти функции в некоторых случаях могут быть заменены на другие (что-то вроде реализация перехвата операций чтения-записи).
Второй вариант выглядит как хак в том смысле что использует знание проекции C++ объектов на C структуры.
Что в принципе тоже safe до тех пор пока С++ поддерживает COM и Corba имплементации которые
тоже построены на этом эзотерическом знании.
Выбор между этими вариантами определяется имплементацией — насколько часто ноужно делать смену секса и
насколько lightweight конструкторы по умолчанию ( TO t; ) и transfer_data_to.
SI>OFF: А по поводу рассматриваемой задачи — честно говоря я так и не понял почему нельзя использовать visitor? Я читал все (ну почти) посты и так и не понял почему это решение не подходит для такой задачи.
Идиома visitor требует значительного refactoring что не рассматривается как опция.