Re: хочу странного: сменить класс объекта в runtime
От: Awaken Украина  
Дата: 31.08.07 16:56
Оценка: 1 (1) +2
CS>Существует ли способ сменить класс объекта в p?
CS>Я понимаю что стандартными способами — нет.
CS>Но в принципе это задача смены одного указателя на vtbl у объекта.
CS>Как бы это сделать путем "наименьшего хака"?

pimpl, handle/body, делегация?
Re[4]: Visitor
От: c-smile Канада http://terrainformatica.com
Дата: 03.09.07 21:11
Оценка: +2 :)
Здравствуйте, Константин Л., Вы писали:

CS>>Чем это лучше простой смены типа?


КЛ>сон спокойнее


Сон спокойнее если ты знаешь что твои конкуренты вместо головы
на подушку кладут книжку GoF.
Re: Visitor
От: remark Россия http://www.1024cores.net/
Дата: 03.09.07 18:53
Оценка: 21 (1) +1
Здравствуйте, 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, тогда надо будет переключать только указатель на них.
Сами же объекты с данными рушить и вообще трогать при переключении не надо.

Вот базовая схема реализации:

namespace html
{
    struct paragraph;
    struct div;

    struct element_visitor
    {
        virtual void do_layout(paragraph&) = 0;
        virtual void do_layout(div&) = 0;
    };

    struct element
    {
        int data;
        virtual void do_layout(element_visitor&) = 0;
    };

    struct paragraph : element
    {
        virtual void do_layout(element_visitor& e)  {e.do_layout(*this);};
    };

    struct div : element
    {
        virtual void do_layout(element_visitor& e)  {e.do_layout(*this);};
    };

    struct block_inside_visitor : element_visitor
    {
        virtual void do_layout(paragraph& e)
        {
            std::cout << "block_inside: paragraph " << e.data << std::endl;
        }

        virtual void do_layout(div& e)
        {
            std::cout << "block_inside: div " << e.data << std::endl;
        }
    };

    struct inline_inside_visitor : element_visitor
    {
        virtual void do_layout(paragraph& e)
        {
            std::cout << "inline_inside: paragraph " << e.data << std::endl;
        }

        virtual void do_layout(div& e)
        {
            std::cout << "inline_inside: div " << e.data << std::endl;
        }
    };
}

int main()
{
    html::paragraph p; p.data = 1;
    html::div d; d.data = 2;

    html::element& e1 = p;
    html::element& e2 = d;

    html::block_inside_visitor v1;
    e1.do_layout(v1);
    e2.do_layout(v1);

    html::inline_inside_visitor v2;
    e1.do_layout(v2);
    e2.do_layout(v2);

    // dynamic switch
    html::element_visitor* v = 0;
    if (rand())
        v = &v1;
    else
        v = &v2;
    e1.do_layout(*v);
    e2.do_layout(*v);
}


При необходимости можно добавить неограниченное кол-во наворотов сверху.
Можно сделать отдельно visitor и const_visitor для пущей стройности.
Можно добавить шаблонов, что бы не писать много скучных virtual void do_layout(element_visitor& e) {e.do_layout(*this);}
Можно добавить acyclic visitor для разрыва циклической зависимости.
И т.д.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[4]: Visitor
От: remark Россия http://www.1024cores.net/
Дата: 03.09.07 21:07
Оценка: 16 (2)
Здравствуйте, 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>();
}



R>


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[2]: хочу странного: сменить класс объекта в runtime
От: c-smile Канада http://terrainformatica.com
Дата: 31.08.07 20:12
Оценка: :))
Здравствуйте, korzh.pavel, Вы писали:

KP>или я чего то не понял?


Угу

"Ну как ты мог обо мне так плохо подумать?"
Re[3]: Visitor
От: Константин Л. Франция  
Дата: 03.09.07 20:25
Оценка: :))
Здравствуйте, c-smile, Вы писали:

[]

CS>Чем это лучше простой смены типа?


сон спокойнее

[]
Re[10]: Visitor
От: remark Россия http://www.1024cores.net/
Дата: 04.09.07 16:42
Оценка: +2
Здравствуйте, c-smile, Вы писали:

КЛ>>А я не могу понять, почему ты так относишься к предложениям, которые выдвигаются по твоей собственной просьбе? Зачем все в штыки принимать?


CS>Мда, народ упорно не читает ТЗ.

CS>Вопрос был такой "Существует ли способ сменить класс объекта в p?"
CS>Все.

Просто все подумали, что ответ "нет" очевиден, и что он очевиден и тебе Поэтому додумали твою проблему, и предлагают решения. Вот.


З.ы. "хакерское" создание нового объекта на месте старого — не смена типа *объекта*


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[3]: Два пути в ад uB :)
От: korzh.pavel Россия  
Дата: 01.09.07 16:59
Оценка: 30 (1)
Здравствуйте, c-smile, Вы писали:

CS>Мне нужно переключить behavior класса в зависимости от некоего условия.

CS>Операции конструирования и копирования объектов достаточно дорогие.
CS>Поэтому все что я могу себе позволить это переключить vtbl

http://rsdn.ru/forum/message/626591.aspx
Автор: alnsn
Дата: 02.05.04


?
Re: хочу странного: сменить класс объекта в runtime
От: Шахтер Интернет  
Дата: 02.09.07 01:11
Оценка: 30 (1)
Здравствуйте, c-smile, Вы писали:

Сменить тип нельзя, но можно пересоздать объект на том же месте.

Насколько я понимаю, слюдующий код легален (хотя на 200% не уверен). Впрочем, если ты допускаешь хаки, то должно быть всё в порядке.

/* main.cpp */ 

#include <stdio.h>

#include <new>

/* -------------- */ 

template <bool Cond,class T> struct EnableIf;
 
template <class T> struct EnableIf<true,T> { typedef T Ret; };
 
template <class T> struct EnableIf<false,T> {};
 
/* -------------- */ 

struct D
 {
  int data;
  
  D() : data(0) { printf("D::D()\n"); }
 };
 
class P : protected D
 {
  public:
  
   P() : D() { printf("P::P()\n"); }
   
   virtual ~P() { printf("P::~P() : data=%d\n",data); }
   
   explicit P(D *from) : D(*from) { printf("P::P(D *) : data=%d\n",data); }
   
   void saveTo(D *to) { *to=*this; }
   
   virtual void fun()=0;
   
   template <class T> struct Enable;
   
   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;
    }
 }; 
 
template <class T> struct P::Enable : public EnableIf<(sizeof (T)==sizeof (P)),P *> {};
   
class A : public P
 {
  public:
  
   A() : P() { printf("A::A()\n"); }
   
   A(::D *from) : P(from) { printf("A::A(D *) : data=%d\n",data); }
   
   ~A() { printf("A::~A()\n"); }
   
   virtual void fun() { data++; }
 }; 
 
class B : public P
 {
  public:
  
   B() : P() { printf("B::B()\n"); }
   
   B(::D *from) : P(from) { printf("B::B(D *)\n"); }
   
   ~B() { printf("B::~B()\n"); }
   
   virtual void fun() { data--; }
 }; 
 
/* main() */   

int main()
 {
  P *p=new A();
  
  p->fun();
  p->fun();
  
  p=p->mutate<B>();
  
  p->fun();
  
  delete p;
  
  return 0;
 }
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re: хочу странного: сменить класс объекта в runtime
От: StevenIvanov США  
Дата: 04.09.07 07:56
Оценка: 28 (1)
Здравствуйте, c-smile, Вы писали:

CS>...


Может я и чего то не понимаю в этой куче решений, но по моему здесь усиленно изобретают термоядерный велосипед из шаблонов и посетителей.
Ведь эта задача решается элементарно средствами С — если программишь на MSVC(или даже G++, но только не Borland C++) — можно видеть однозначное (вроде даже задокументированное) соответсвие С-шных структур С++ классам (интерфейсам).
Вот, к примеру:
#if defined(__cplusplus) && !defined(CINTERFACE)
    IXMLElement : public IDispatch
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE get_tagName( BSTR *p) = 0;
    // ...
    };
    
#else     /* C style interface */

    typedef struct IXMLElement2Vtbl
    {
        BEGIN_INTERFACE
        /* IUnknown + IDispatch vtbl skipped*/
        

        HRESULT ( STDMETHODCALLTYPE *get_tagName )( IXMLElement2 * This, BSTR *p);
        
        /* ... */
        
        END_INTERFACE
    } IXMLElement2Vtbl;

    interface IXMLElement2
    {
        CONST_VTBL struct IXMLElement2Vtbl *lpVtbl;
    };
#endif

Если у тебя подобные классы, то проблем с заменой vtbl не должно быть никаких, а вот если у тебя виртуальное наследование со всякими рюшками то тогда
Re: Два пути в ад uB :)
От: Erop Россия  
Дата: 31.08.07 21:17
Оценка: 14 (1)
Здравствуйте, c-smile, Вы писали:

CS>Есть два класса-потомка (A и B) от общего базового абстракта P.

CS>Как бы это сделать путем "наименьшего хака"?

Тема раз:
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 );
}


Вроде даже без UB всюду...

Тема два (хмурое хаккерство, но...)
template<class TFrom, class TTo> void UnsafeSwitchTo( P* p )
{
    static_assert( sizeof( TFrom ) == sizeof( TTo ) );
    assert( dynamic_cast<TFrom*>( p ) != 0 );
    {
        TTo tmp;
        memcpy( p, &tmp, sizeof( tmp ) );
    }
    assert( dynamic_cast<TTo*>( p ) != 0 );
}
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: хочу странного: сменить класс объекта в runtime
От: remark Россия http://www.1024cores.net/
Дата: 04.09.07 08:14
Оценка: 14 (1)
Здравствуйте, 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();
}




1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[12]: Решил я таки попробовать пописать ... :)
От: Erop Россия  
Дата: 05.09.07 08:53
Оценка: 2 (1)
Тихо сам с собою...

CS>>Ты про это?

E>Да.


Задумался я таки как лучше. ИМХО неочевидно.
Решил попробовать пописать примеры. Писал на псевдокоде, так как компилятора под рукой ночью не было

Для раздумий я выбрал такую модель.
Типа у нас есть программа, которая работает с каким-то данными о людях, представленными разнообразными классами. Например в нашей прогамме есть такой класс:
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& ); // где-то реализовано
};

При этом таких классов много и много клиентского кода, который что-то делает с объектами таких классов.
Например такого:
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 InJapan

typedef InEurope::Persone Persone;

Ну а потом посмотрели на это всё и поняли, что так нам не нравится по многим причинам (главная -- неподдерживаемо) И мы хотим что-то теперь сделать так, чтобы это всё можно было переключать динамически, но редко.

При этом мы считаем, что можем проитерировать все объекты, поведением которых мы хотим управлять.
Кроме того, для простоты, я предположил, что
1) Переключаться между странами можно не очень быстро
2) Persone и другие объекты можно легко построить в "пустом состоянии" (то есть что даже если у объекта и нет конструктора по умолчанию, то его легко написать)
3) Для всех таких классов легко написать эффективный std::swap (либо вообще подходит дефолтная реализация)


Какие собственно проблемы?
1) Как-то не запутаться с тем какая реализация работает в какой версии
2) Как-то не делать это всё очень задорого.
Собственно я сначала решил, что хочу иметь объект, который умеет хранить в себе описание поведения всех типов с переключаемым поведением и объект такого типа может использоваться для переключения.

Получилось как-то так:
template<typename TSeed>
class TypeIdManager {
public:
    typedef int Id_t;
    static template<typename T> Id_t Get() { return CTypeId<T>::Id; }
    static int CurIdCount() { return seed_accessor::CurIdCount(); }

private:
    static int seed;
    struct seed_accessor;    
    friend seed_accessor;
    struct seed_accessor; {
        static int NextId() { return ++TypeIdManager::seed; }
        static int CurIdCount() { return 1 + TypeIdManager::seed; }
    };
    template<typename T> struct CTypeId : seed_accessor { static const int Id = NextId(); }

    TypeIdManager(); TypeIdManager( const TypeIdManager& ); ~TypeIdManager(); // тел нет
    
};

template<typename TSeed> int TypeIdManager::seed = 0;


class ProcessorsCollection {
public:
    
    template<typename I> void Set( I* data ) { body[getId<I>()] = data; }
    template<typename I> void Get( I*& dst ) const { dst = static_cast<I*>( body[getId<I>()] ); }

    bool IsValid() const { return is_compleet(); }

private:
    mutable std::vector<void*> body;

    bool is_compleet() const
    {
        assert( body.size() <= TypeIdManager<ProcessorsCollection>::CurIdCount() );
        if( body.size() != TypeIdManager<ProcessorsCollection>::CurIdCount() )
            return false;
        for( int i = 0; i < TypeIdManager<ProcessorsCollection>::CurIdCount(); i++ )
            if( body[i] == 0 )
                return false;
        return true;
    }
    int regId( int id ) const
    {
        if( id >= body.size() )
             body.set_size( TypeIdManager<ProcessorsCollection>::CurIdCount() );
        assert( 0 <= id && id < body.size() );
        return id;
    }
    
    template<typename T> int getId() const
    {
        return regId( TypeIdManager<ProcessorsCollection>::Get<T>() - 1 );
    }
};

template<typename I, typename TProcessor>
class CProcessorRegistrar {
public:
    CProcessorRegistrar( ProcessorsCollection& storage_ ) : storage( storage_ )
    { 
        assert( getProcessor() == 0 );
        setProcessor( &theProcessor );
    }
    ~CProcessorRegistrar() 
    { 
        assert( getProcessor() == &theProcessor );
        setProcessor( 0 );
    }
    
private:
    ProcessorsCollection& storage;
    TProcessor theProcessor;

    I* getProcessor() const 
    { 
        I* tmp;
        storage.Get( tmp );
        return tmp;
    }
    void setProcessor( I* i ) { storage.Set( i ); }

    CProcessorRegistrar( const CProcessorRegistrar& ); void operator = ( const CProcessorRegistrar& ); // тел нет
};

Итак мы хотим как-то переключать поведение наших классов к тому или иному ProcessorsCollection, формируя каждый экземпляр при помощи статических регистраторов.

Сразу просматривается два подхода.
1) Иметь в Persone указатель на состояние, которое собственно умеет выполнять переключаемую работу
2) применить хак с "переключением" типа объектов.
Итак, пробуем:


Сначала пробуем подход с просессорами (ясно, что всё можно немного доточить, но принципиально лучше не будет)
Сначала немного допишем к нашему фреймворку:
namespace UseProcessors {
extern const ProcessorsCollection& TheDefaultProcessors;
template<class TProcessor> class ProcHolder {
public:
    ProcHolder() { SwitchTo( TheDefaultProcessors ); }
    TProcessor* operator->() const { return proc; }
    void SwitchTo( const ProcessorsCollection& implementation )
    {
        implementation.Get( proc );
        assert( proc != 0 );
    }
private:
    mutable TProcessor* proc;
};
}   //  namespace UseProcessors


Теперь можем переписать класс Persone:
//------------------------------
// Вариант с просессорами
//-----------------------------

namespace UseProcessors {
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 proc->GetFullName( this ); } //2
    std::string GetInitials() const { proc->GetInitials( this ); } //2

    struct IProcessor { //2
        std::string GetFullName( const Persone* theObj ) const = 0
        std::string GetInitials( const Persone* theObj ) const = 0
    };

private:
    ProcHolder<IProcessor> proc; //2
    
    const std::string name;
    const std::string family;
public: // для процессоров //2
    static std::string toInitials( const std::string& ); // где-то реализовано
};

// Реализация версий (какой-то cpp наверное):
extern ProcessorsCollection InEurope;
class PersoneProcessorInEurope : public Persone::IProcessor {
    virtual std::string GetFullName( const Persone* theObj ) const { return theObj->Family() + " " + theObj->Name(); }
    virtual std::string GetInitials( const Persone* theObj ) const 
       { theObj->toInitials( theObj->Family() ) + theObj->toInitials( theObj->Name() ); }
};
static const CProcessorRegistrar<Persone::IProcessor, PersoneProcessorInEurope> personeInEurope( InEurope );

extern ProcessorsCollection InJapan;
class PersoneProcessorInJapan : public Persone::IProcessor {
    virtual std::string GetFullName( const Persone* theObj ) const { return theObj->Family() + " " + theObj->Name(); }
    virtual std::string GetInitials( const Persone* theObj ) const 
       { theObj->toInitials( theObj->Family() ) + theObj->toInitials( theObj->Name() ); }
};
static const CProcessorRegistrar<Persone::IProcessor, PersoneProcessorInJapan> personeInJapan( InJapan );

extern const ProcessorsCollection& TheDefaultProcessors = InEurope;


}   //  namespace UseProcessors
(меткой //2 помечны правки, которые пришлось изменить.

При этом наш клиентский код обойдётся вообще без изменений:
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();
}





Теперь попробуем применть конструкцию со сменой динамического типа. Сначала тоже допишем библиотеку:
//---------------------------------
//  Внимание!!! Злобный хак!!! Наследников ISwitchableDT нельзя создавать где-либо кроме кучи!!!
//---------------------------------
class ISwitchableDT {
public:
    template<typename T> static T* New() 
        { return new( ::operator new( sizeof( T ) ) ) T; }
    template<typename T, typename T1> static T* New( T1 a1 ) 
        { return new( ::operator new( sizeof( T ) ) ) T( a1 ); }
    template<typename T, typename T1, typename T2> static T* New( T1 a1, T2 a2 ) 
        { return new( ::operator new( sizeof( T ) ) ) T( a1, a2 ); }
    template<typename T, typename T1, typename T2, typename T3> static T* New( T1 a1, T2 a2, T3 a3 ) 
        { return new( ::operator new( sizeof( T ) ) ) T( a1, a2, a3 ); }
    // и т. д.
    template<typename T> static void Delete( T* obj )
    {
        if( obj == 0 )
            return;
        obj->~T();
        ::operator delete( obj );
    }


protected:
    virtula ISwitchableDT() = 0 {}
    virtual SwitchToMyDT( ISwitchableDT* dst ) = 0;

    template<typename TBase> void static SwitchToImplementation( const ProcessorsCollection& patterns, TBase* obj )
    {
        TBase* pattern = 0;
        patterns.Get( pattern );
        assert( pattern != 0 );
        static_cast<ISwitchableDT*>( pattern )->SwitchToMyDT( obj );
    }
    
    template<typename TTo, typename TBase> static void SwitchToImplementation( TBase* obj )
    {
        static_assert( sizeof( *obj ) == sizeof( TTo ) );
        if( obj == 0 )
            return;
        TBase tmp;
        std::swap( *obj, tmp );
        obj->~TBase();
        new( obj ) TTo;
        std::swap( *obj, tmp );
        assert( dynamic_cast<TTo*>( obj ) == obj ); 
    } 

private:
    void* operator new( size_t ); void operator delete( void* ); // нет тел, пользуйтесь ISwitchableDT::New/Delete
    void* operator new( size_t, void* buffer ) { return buffer; }
    
};

template< class TImplementation, class TBase >
class CSwitchebleDT : public TBase {
    virtual SwitchToMyDT( ISwitchableDT* dst_ )
    {
        TBase* dst = dynamic_cast<TBase*>( dst_ );
        assert( dst != 0 );
        ISwitchableDT::SwitchToImplementation<TImplementation>( dst );
    }
};


Теперь перепишем класс Persone:
//------------------------------
// Вариант с "переключаемыми" типами
//-----------------------------
namespace UseSwitchableDT {
class Persone : public ISwitchableDT { //1
protected: //1
    Persone() {} //1
    Persone( std::string name_, std::string family_ ) : name( name_ ), family( family_ ) {}
    
public: //1
    static 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; //1
    virtual std::string GetInitials() const = 0; //1

protected:
    /*const */std::string name; //1
    /*const */std::string family; //1
    static 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
От: Аноним  
Дата: 01.09.07 08:30
Оценка: +1
Здравствуйте, c-smile, Вы писали:

CS>Это собственно и есть то что строит compiler унутре.

CS>Т.е. зачем мне изобретать велосипед по новой?

Потому что ты не должен даже догадываться о существовании этих велосипедов.
Дело то внутреннее...

Нормальные решения тебе уже в общем-то подсказали.
Непонянтно почему ты их игнорируешь...
хочу странного: сменить класс объекта в runtime
От: c-smile Канада http://terrainformatica.com
Дата: 31.08.07 16:40
Оценка:
Есть два класса-потомка (A и B) от общего базового абстракта P.

class P
{
   int data;
   virtual foo() = 0;
}

class A: public P
{
   // no class specific data, only methods
   virtual foo() { ++data; }
}

class B: public P
{
   // no class specific data, only methods
   virtual foo() { --data; }
}


Есть некий конкретный instance созданный как A.

P* p = new A;


Существует ли способ сменить класс объекта в p?
Я понимаю что стандартными способами — нет.
Но в принципе это задача смены одного указателя на vtbl у объекта.
Как бы это сделать путем "наименьшего хака"?

Вот такой вот вопрос.
Re: хочу странного: сменить класс объекта в runtime
От: korzh.pavel Россия  
Дата: 31.08.07 16:50
Оценка:
Здравствуйте, 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
От: Centaur Россия  
Дата: 31.08.07 17:07
Оценка:
Здравствуйте, Awaken, Вы писали:

CS>>Существует ли способ сменить класс объекта в p?

CS>>Я понимаю что стандартными способами — нет.
CS>>Но в принципе это задача смены одного указателя на vtbl у объекта.
CS>>Как бы это сделать путем "наименьшего хака"?

A>pimpl, handle/body, делегация?


+1. И всё это, взятое вместе, называется паттерном State.
Re[2]: хочу странного: сменить класс объекта в runtime
От: c-smile Канада http://terrainformatica.com
Дата: 31.08.07 18:13
Оценка:
Здравствуйте, Awaken, Вы писали:

CS>>Существует ли способ сменить класс объекта в p?

CS>>Я понимаю что стандартными способами — нет.
CS>>Но в принципе это задача смены одного указателя на vtbl у объекта.
CS>>Как бы это сделать путем "наименьшего хака"?

A>pimpl, handle/body, делегация?


Эти слова я знаю. Плюсы и минусы тоже знаю. Все эти подходы определяют новые сущности.

Мысли вслух:

Хочется простой вещи типа:

P* p = new A;
new(p) B;


Надо по-эксперементировать ...
Re[3]: хочу странного: сменить класс объекта в runtime
От: _nn_ www.nemerleweb.com
Дата: 31.08.07 18:34
Оценка:
Здравствуйте, 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;
}
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[3]: хочу странного: сменить класс объекта в runtime
От: Went  
Дата: 31.08.07 18:34
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Хочется простой вещи типа:


CS>
CS>P* p = new A;
CS>new(p) B;
CS>


CS>Надо по-эксперементировать ...


А если что-то вроде:
P* p = new A;
//Do something
p = new B(*p);


Хотя, в этом случае, однозначно рулит компонентность, добавить компоненту, отвечающую за это действие.
Re: хочу странного: сменить класс объекта в runtime
От: c-smile Канада http://terrainformatica.com
Дата: 31.08.07 18:59
Оценка:
Здравствуйте, 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 methods
   virtual foo() { ++data; }
};

struct B: public P
{
   // no class specific data, only methods
   virtual 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
От: Константин Л. Франция  
Дата: 31.08.07 20:13
Оценка:
Здравствуйте, c-smile, Вы писали:

[]

не красиво это как-то,

может лучше так?




class Implementation
{
     virtual void f() = 0;
}

struct P
{
   int data;
   P() {}

   void SetImpl( Implementation* impl );

   virtual ~P() {}
   virtual foo() = 0;
};
Re[2]: Два пути в ад uB :)
От: c-smile Канада http://terrainformatica.com
Дата: 31.08.07 22:06
Оценка:
Здравствуйте, Erop, Вы писали:

E>
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
От: c-smile Канада http://terrainformatica.com
Дата: 31.08.07 22:13
Оценка:
Здравствуйте, Константин Л., Вы писали:

КЛ>может лучше так?


КЛ>

КЛ>class Implementation
КЛ>{
КЛ>     virtual void f() = 0;
КЛ>}

КЛ>struct P
КЛ>{
КЛ>   int data;
КЛ>   P() {}

КЛ>   void SetImpl( Implementation* impl );

КЛ>   virtual ~P() {}
КЛ>   virtual foo() = 0;
КЛ>};


КЛ>


Я не понял если честно.

Что-бы это имело смысл то это должно выглядеть так:

class Implementation
{
   virtual void f(P* p) {...}
}

struct P
{
   Implementation* pimpl;
   int data;

   P() {}
   void SetImpl( Implementation* impl ) { pimpl = impl; }

   ~P() {}
   void foo() { pimpl->foo(this); };
};


Это собственно и есть то что строит compiler унутре.
Т.е. зачем мне изобретать велосипед по новой?
Re[4]: хочу странного: сменить класс объекта в runtime
От: c-smile Канада http://terrainformatica.com
Дата: 01.09.07 02:23
Оценка:
Здравствуйте, _nn_, Вы писали:

__>Может проще будет создать 2 объекта сразу ?

__>
__>P* f(bool need_a)
__>{
__> static A a;
__> static B b;

__> return need_a ? &a : &b;
__>}
__>


"Не канает".

Задача примерно такова: есть DOM tree

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 на самом деле) объекта создавать само собой я даже и не знаю как.
Re[3]: Два пути в ад uB :)
От: Константин Л. Франция  
Дата: 01.09.07 10:57
Оценка:
Здравствуйте, c-smile, Вы писали:

[]

CS>Мне p->~P(); как раз не нужен.


CS>Мне нужно переключить behavior класса в зависимости от некоего условия.

CS>Операции конструирования и копирования объектов достаточно дорогие.
CS>Поэтому все что я могу себе позволить это переключить vtbl

Только зачем это делать, рискуя нарваться на неприятности с placement new?

здесь
Автор: Константин Л.
Дата: 01.09.07
я показал как примерно это лучше сделать (с твоими дополнениями конечно)

[]
Re: хочу странного: сменить класс объекта в runtime
От: Programador  
Дата: 01.09.07 12:19
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Есть два класса-потомка (A и B) от общего базового абстракта P.

ну первое это vtab вручную — поместить в класс указатели на методы.

Просто переписать vtab по адресу (viud *(&))(*this)

placement new — воспользоватся раздельной компиляцией и описать одноименный пустой класс без всяких data только обьявления виртуальных методов в томже порядке. И public на те классы где есть виртуальные методы. Тогда по идее структура vtabl сохранится, а побочного ффекта от конструкторов-деструкторов не будет

Описать сначала невиртуальные методы, потом перекрыть (продублировать) ихже виртуальными, тогда можно вернутся к той базе где виртуальных нет
Re[5]: хочу странного: сменить класс объекта в runtime
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 01.09.07 17:16
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>должен переключать способ do_layout и чертову силу других методов.


CS>Причем display-model может переключаться динамически (во всяком случае это не запрещено)


Читать "Паттерны проектирования". Решение твоей проблемы — паттерн State, вероятнее всего тебе также понадобятся Factory method и Composite.

В vtbl лезть не стоит.
Re[6]: хочу странного: сменить класс объекта в runtime
От: c-smile Канада http://terrainformatica.com
Дата: 01.09.07 17:30
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, c-smile, Вы писали:


CS>>должен переключать способ do_layout и чертову силу других методов.


CS>>Причем display-model может переключаться динамически (во всяком случае это не запрещено)


G>Читать "Паттерны проектирования". Решение твоей проблемы — паттерн State, вероятнее всего тебе также понадобятся Factory method и Composite.


Спасибо.

G>В vtbl лезть не стоит.


Я не лезу.

Вот пример того как это можно сделать:
http://www.rsdn.ru/forum/message/2641817.1.aspx
Автор: c-smile
Дата: 31.08.07

Используются только документирванные фичи языка.
Re[7]: хочу странного: сменить класс объекта в runtime
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 01.09.07 17:42
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Вот пример того как это можно сделать:

CS>http://www.rsdn.ru/forum/message/2641817.1.aspx
Автор: c-smile
Дата: 31.08.07

CS>Используются только документирванные фичи языка.

В таком простом случае можно было бы хранить тим в поле элемента, а в do_layout() использовать switch.
...А потом почитать Фаулера "Рефакторинг" и преобразовать это дело в State/Strategy.
Re[4]: Два пути в ад uB :)
От: c-smile Канада http://terrainformatica.com
Дата: 01.09.07 17:50
Оценка:
Здравствуйте, korzh.pavel, Вы писали:

KP>Здравствуйте, c-smile, Вы писали:


CS>>Мне нужно переключить behavior класса в зависимости от некоего условия.

CS>>Операции конструирования и копирования объектов достаточно дорогие.
CS>>Поэтому все что я могу себе позволить это переключить vtbl

KP>http://rsdn.ru/forum/message/626591.aspx
Автор: alnsn
Дата: 02.05.04


О! Спасибо Паша, про Сhristopher Diggins и его interfaces в heron language
я и подзабыл. Это он там с Абрахамсом обсуждает это дело.

Это несколько другое — но близко.

Еще раз: технически задача состоит в переключении конкретного instance
с класса A на гомоморфный класс B — сиречь смена instance контроллера.

placement new в C++ позволяет эту задачу решить.
Re[8]: хочу странного: сменить класс объекта в runtime
От: c-smile Канада http://terrainformatica.com
Дата: 01.09.07 17:58
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, c-smile, Вы писали:


CS>>Вот пример того как это можно сделать:

CS>>http://www.rsdn.ru/forum/message/2641817.1.aspx
Автор: c-smile
Дата: 31.08.07

CS>>Используются только документирванные фичи языка.

G>В таком простом случае можно было бы хранить тим в поле элемента, а в do_layout() использовать switch.


Извиняюсь, но это "индийский способ" решать проблемы. Это хак но архитектурный.

В реалии методов далеко за 20 и будет больше.

G>...А потом почитать Фаулера "Рефакторинг" и преобразовать это дело в State/Strategy.


Спасибо.
Re[4]: Два пути в ад uB :)
От: c-smile Канада http://terrainformatica.com
Дата: 01.09.07 18:22
Оценка:
Здравствуйте, Константин Л., Вы писали:

КЛ>Здравствуйте, c-smile, Вы писали:


КЛ>[]


CS>>Мне p->~P(); как раз не нужен.


CS>>Мне нужно переключить behavior класса в зависимости от некоего условия.

CS>>Операции конструирования и копирования объектов достаточно дорогие.
CS>>Поэтому все что я могу себе позволить это переключить vtbl

КЛ>Только зачем это делать, рискуя нарваться на неприятности с placement new?


1) "Зачем?" — потому что это максимально эффективный способ
2) "на неприятности с placement new" — какие такие неприятности?
placement new это базовая фича языка используемая повсеместно (например в stl и boost).

КЛ>здесь
Автор: Константин Л.
Дата: 01.09.07
я показал как примерно это лучше сделать (с твоими

дополнениями конечно)

Этот метод может создать лишний redirection при вызове методов. Накладно.
Re[2]: Два пути в ад uB :)
От: Cyberax Марс  
Дата: 01.09.07 18:26
Оценка:
Здравствуйте, Erop, Вы писали:

CS>>Есть два класса-потомка (A и B) от общего базового абстракта P.

CS>>Как бы это сделать путем "наименьшего хака"?
E>Тема раз:
E>
E>template<class T> P* CreateHackedObject()
E>{
E>    void * buffer = new char[sizeof T];
E>    return new( buffer ) T;
E>}
E>

Ага, конечно. Нет тут у тебя UB, панимаешь... А про выравниванием кто думать будет?

Нужно что-то типа такого:
template<class T>
struct aligned_union
{
    union type
    {
        typename boost::type_with_alignment<boost::alignment_of<T>::value>::type aligner;
        char char_arr[sizeof(T)];
    };
};

...
void *buffer=(new aligned_union())->char_arr;
...
Sapienti sat!
Re[5]: Два пути в ад uB :)
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 01.09.07 18:33
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>1) "Зачем?" — потому что это максимально эффективный способ

Не рановато ли вы оптимизацией занимаетесь? Даже при использовании State можно избежать затрат на конструирование объекта без хакерства.

CS>2) "на неприятности с placement new" — какие такие неприятности?

Лишаете себя автоматического деструктора, сильно урезая возможности ООП в C++.

CS>Этот метод может создать лишний redirection при вызове методов. Накладно.

По сравнению с new для каждого element второй вызов метода — мелочь.
Re[3]: Два пути в ад uB :)
От: Erop Россия  
Дата: 01.09.07 18:43
Оценка:
Здравствуйте, Cyberax, Вы писали:

E>>
E>>template<class T> P* CreateHackedObject()
E>>{
E>>    void * buffer = new char[sizeof T];
E>>    return new( buffer ) T;
E>>}
E>>

C>Ага, конечно. Нет тут у тебя UB, панимаешь... А про выравниванием кто думать будет?

К твоему сведению, new возвращает память максимально выравненнуюю.
Как по твоему работает new int[256]?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: Два пути в ад uB :)
От: Cyberax Марс  
Дата: 01.09.07 19:07
Оценка:
Здравствуйте, Erop, Вы писали:

C>>Ага, конечно. Нет тут у тебя UB, панимаешь... А про выравниванием кто думать будет?

E>К твоему сведению, new возвращает память максимально выравненнуюю.
AFAIR, только для нужного типа. Это malloc() возвращает максимально выравненую память.

E>Как по твоему работает new int[256]?

Так и работает. Но это не значит, что "*((double*)new int[2])=1.0d" будет работать так как надо.
Sapienti sat!
Re[3]: Два пути в ад uB :)
От: Erop Россия  
Дата: 01.09.07 19:30
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Мне p->~P(); как раз не нужен.


CS>Мне нужно переключить behavior класса в зависимости от некоего условия.

CS>Операции конструирования и копирования объектов достаточно дорогие.

Тема 1:
Можно сделать данные разделяемыми несколькими "инстансами" объекта.
И использовать любой хак после этого.

CS>Поэтому все что я могу себе позволить это переключить vtbl

Тема 2:
Вообще говоря, обычно вред от виртуальности вызова происходит не из-за того, что вызов как-то нереально дорог, а из-за того, что виртуальный вызов не встраивается.
Так что обычно цена вопроса "один виртуальный вызов или два подряд" -- это несколько тактов. Так что, ИМХО твой выбор прост -- двойная деспечерезация

CS>Не вижу я там UB. TFrom и TTo ничего не добавляют в instance.

CS>Отличие этих классов сугубо в vtbl.

Ну в стандарте никакого vtbl нету...
Представь себе реализацию, в которой виртуальность обеспечивается пйтём регистрации объектов в рантайме. Типа конструктор неявно регит в каком-то map'е в рантайме объект, потом при виртуальном вызове по этому map'у смотрят какого же он типа, на самом деле, и зовут что надо. Это ничем не противоречит стандарту...

Другое дело, что если тебя волнует вполне определённый набор компиляторов, то частенько на каждом из них конкретные случаи UB хорошо докуминтированы и могут быть использованы
Но в твоём случае, ИМХО, игра свеч не стоит.
Ну регай какой-нибудь объект-редиректор, который зовёт нужный метод из пяти, при случае.
Тем более, что ты можешь сделать и как-то так:

class CMyItemData;
class IMyItemProcessor {
public:
    virtual ~IMyItemProcessor();
    virtual void Method1( CMyItemData& ) = 0;
    virtual void Method2( CMyItemData& ) = 0;
    virtual void Method3( CMyItemData& ) = 0;
};

class CMyItem {
public:
    CMyItem( IMyItemProcessor*, const CMyItemData& );
    void Method1() { processor->Method1( data ); }
    void Method2() { processor->Method2( data ); }
    void Method3() { processor->Method3( data ); }

    SetProcessor( IMyItemProcessor* );

private:
    IMyItemProcessor* processor;
    CMyItemData data;

};


При этом CMyItemData может иметь реализации всех методов процессоров, а процессоры будут просто осуществлять перевызов.
Опять же, обращаю внимание, что при таком подходе методы MethodХХХ класса CMyItem нифига не виртуальные, так что даже цепочки из двух вызовов не получаеся.

Но в целом примерно это тебе тут уже советовали, но почему-то не прямо, а ссылаясь на книжки и прочее...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[5]: Два пути в ад uB :)
От: Erop Россия  
Дата: 01.09.07 19:34
Оценка:
Здравствуйте, Cyberax, Вы писали:

E>>Как по твоему работает new int[256]?

C>Так и работает. Но это не значит, что "*((double*)new int[2])=1.0d" будет работать так как надо.

Я два ответа послал, но второй что-то не появляется
Короче, общаая идея такая, что мне казалось, что есть гарантия, что new char[count] возвращает то же, что вернёт ::operator new( count ), но я её что-то не найду.
Так что наверное ты прав. Надо звать ::operator new/::operator delete непосредственно
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[6]: Два пути в ад uB :)
От: c-smile Канада http://terrainformatica.com
Дата: 01.09.07 20:54
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, c-smile, Вы писали:


CS>>1) "Зачем?" — потому что это максимально эффективный способ

G>Не рановато ли вы оптимизацией занимаетесь? Даже при использовании State можно избежать затрат на конструирование объекта без хакерства.

"Не рановато" — нет.
Речь идет о живом продукте. Вариации вот этого http://www.terrainformatica.com/htmlayout/

CS>>2) "на неприятности с placement new" — какие такие неприятности?

G>Лишаете себя автоматического деструктора, сильно урезая возможности ООП в C++.

Где я себя лишаю? Эти объекты на стеке не создаются в принципе.
Re[2]: хочу странного: сменить класс объекта в runtime
От: Шахтер Интернет  
Дата: 02.09.07 01:24
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Здравствуйте, c-smile, Вы писали:


CS>>Существует ли способ сменить класс объекта в p?

CS>>Я понимаю что стандартными способами — нет.
CS>>Но в принципе это задача смены одного указателя на vtbl у объекта.
CS>>Как бы это сделать путем "наименьшего хака"?
CS>>Вот такой вот вопрос.

CS>Вот эта вот кострукция компилируется на ура и делает то что надо:


Здесь ты можешь поиметь проблемы при агрессивной оптимизации из-за alliasing.
Если хочешь быстро и легально то придется vtable ручками писать. Нет в C++ средств автоматизации для твоего случая.
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[2]: хочу странного: сменить класс объекта в runtime
От: c-smile Канада http://terrainformatica.com
Дата: 02.09.07 02:00
Оценка:
Здравствуйте, Шахтер, Вы писали:

Ш>Здравствуйте, 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
От: Шахтер Интернет  
Дата: 02.09.07 02:42
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Единственно я бы тогда уже сделал так:


CS>
CS>struct P
CS>{
CS>template <typename T>
CS>  void mutate()
CS>{
CS>   static_assert( sizeof(T) == sizeof(*this) );
CS>   T temp;
CS>   swap(temp);
CS>   this->>~P();
CS>   new(this) T;
CS>   temp.swap(*this);
CS>}
CS>};
CS>


Это менее эффективно. Ты здесь делаешь обмен состояниями с 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.
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[4]: хочу странного: сменить класс объекта в runtime
От: c-smile Канада http://terrainformatica.com
Дата: 02.09.07 04:06
Оценка:
Здравствуйте, Шахтер, Вы писали:

CS>>Единственно я бы тогда уже сделал так:


CS>>
CS>>struct P
CS>>{
CS>>template <typename T>
CS>>  void mutate()
CS>>{
CS>>   static_assert( sizeof(T) == sizeof(*this) );
CS>>   T temp;
CS>>   swap(temp);
CS>>   this->>~P();
CS>>   new(this) T;
CS>>   temp.swap(*this);
CS>>}
CS>>};
CS>>


Ш>Это менее эффективно. Ты здесь делаешь обмен состояниями с 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, harmless
     new(ret) T(&temp); // shall do swap from temp.
     return ret;
   } // dtor of temp, it should be empty at this moment


Как ты видишь количество swap ровно такое же как у меня.

Или я чего не вижу?
Re: хочу странного: сменить класс объекта в runtime
От: MasterZiv СССР  
Дата: 02.09.07 08:34
Оценка:
c-smile пишет:
> Существует ли способ сменить класс объекта в p?
> Я понимаю что стандартными способами — нет.

Как это нет ? Очень даже можно, толко это будет не тот уже объект.

Сначала надо получить ссылку на старый объект,
потом на его основе создать объект нового типа, выделив
под него новую память (которая может быть меньше, равна или больше
по объему старому объекту), потом старый объект удалить.
Ну и вернуть наружу адрес уже нового объекта.
Posted via RSDN NNTP Server 2.1 beta
Re[3]: хочу странного: сменить класс объекта в runtime
От: MasterZiv СССР  
Дата: 02.09.07 08:39
Оценка:
c-smile пишет:
> Хочется простой вещи типа:
>
> P* p = new A;
> new(p) B;

Такое возможно, если sizeof(A) >= sizeof(B)

P* p = new A;
if( sizeof(A) >= sizeof(B) )
pB = new(p) B;
else
pB = new B(A); // или pB = new ( malloc( sizeof(B) ) ) B;

Но выделять и удалять память надо уже наверное руками, не через new.
Posted via RSDN NNTP Server 2.1 beta
Re[2]: Visitor
От: c-smile Канада http://terrainformatica.com
Дата: 03.09.07 19:44
Оценка:
Здравствуйте, remark, Вы писали:

R>Здравствуйте, c-smile, Вы писали:


R>Вот базовая схема реализации:


R>
R>int main()
R>{
R>    html::paragraph p; p.data = 1;
R>    html::div d; d.data = 2;

R>    html::element& e1 = p;
R>    html::element& e2 = d;

R>    html::block_inside_visitor v1;
R>    e1.do_layout(v1);
R>    e2.do_layout(v1);

R>    html::inline_inside_visitor v2;
R>    e1.do_layout(v2);
R>    e2.do_layout(v2);

R>    // dynamic switch
R>    html::element_visitor* v = 0;
R>    if (rand())
R>        v = &v1;
R>    else
R>        v = &v2;
R>    e1.do_layout(*v);
R>    e2.do_layout(*v);
R>}
R>


R>При необходимости можно добавить неограниченное кол-во наворотов сверху.

R>Можно сделать отдельно visitor и const_visitor для пущей стройности.
R>Можно добавить шаблонов, что бы не писать много скучных virtual void do_layout(element_visitor& e) {e.do_layout(*this);}
R>Можно добавить acyclic visitor для разрыва циклической зависимости.
R>И т.д.

Чем это лучше простой смены типа?

Смена типа в данном случае это вызов одной функции в момент смены стиля
(что в быту происходит нечасто):

template <typename T>
  void mutate(P& obj)
{
   static_assert( sizeof(T) == sizeof(obj) );
   T temp;
   obj.swap(temp);
   obj.~P();
   new(&obj) T;
   temp.swap(obj);
}
Re[3]: Visitor
От: remark Россия http://www.1024cores.net/
Дата: 03.09.07 20:59
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Чем это лучше простой смены типа?


Ничем. Кому что нравится.

Вот ещё вариант:

Тут похоже на твой вариант, но не надо создавать и рушить объект — он сам сразу "2 в 1" — его можно переключать.

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
{
};

int main()
{
    last l;
    base& b1 = static_cast<impl1&>(l);
    b1.draw();
    base& b2 = static_cast<impl2&>(l);
    b2.draw();
}




1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[5]: Visitor
От: c-smile Канада http://terrainformatica.com
Дата: 03.09.07 21:37
Оценка:
Здравствуйте, remark, Вы писали:

CS>>>Чем это лучше простой смены типа?

R>>Ничем. Кому что нравится.

В нашем деле слово нравится есть эквивалент "технически грамотно".

R>>Вот ещё вариант:

...

1) Ты вводишь новую сущность — struct last т.е. весь существующий код
типа:
  base* pb = ... ; 
  pb->draw();

надо злостно рефакторить.

2) нужно представлять во что выливается конструкция
(self->*f)();

в рантайм.
Re[6]: Visitor
От: Erop Россия  
Дата: 04.09.07 05:55
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>2) нужно представлять во что выливается конструкция

CS>
CS>(self->*f)();
CS>

CS>в рантайм.

Да ни во что она особое не выливается. Лишнее разыменование, которое хорошо конвейрезуется и парится.

Я тебе уже давно советую сделать такой рефакторинг, но ты чего-то не хочешь.

У тебя чего больше? Кода классов, которые ты хочешь "переключать", или коиентского кода?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[6]: Visitor
От: Erop Россия  
Дата: 04.09.07 06:12
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>1) Ты вводишь новую сущность — struct last т.е. весь существующий код

CS>типа:
CS>
CS>  base* pb = ... ; 
  pb->>draw();
CS>

CS>надо злостно рефакторить.

Не, просто last -- это то, что теперь будет видеть, под видом base старый код

Смотри, как можно сделать:
// Было примерно так (схема упрощённая, так как у Base может быть много наследников...
// Ко всем классам, которые надо изменить припишу слово Old
class OldBase {
public:
    virtual void Method1() = 0; // На самом деле тут есть два варианта Impl1 и Impl2

protected:
    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 наследника. И уметь получать из этой регистрилки процессор под нужный тип.
Тогда ты просто статическими регистраторами регишь новую имплементацию, получаешь статический же объект, выдающий процессоры. Корорый, при переключеннии имплементации отдаёшь во все свои объекты и объекты получают из этой регистрилки свои процессоры. Ну а потом всё работает как раньше.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[7]: Visitor
От: c-smile Канада http://terrainformatica.com
Дата: 04.09.07 06:49
Оценка:
Здравствуйте, Erop, Вы писали:

E>Я тебе уже давно советую сделать такой рефакторинг, но ты чего-то не хочешь.


Зачем?

В C++ объект это есть структура содержащая указатель на vtbl + данные инстанса.
По условию задачи мне нужно менять значение указателя vtbl
т.е. замена 1 (одного) указателя. Решается все 1 (одной) одной функцией из 5 (пяти) строк.

Я не могу понять — за ради каких вселенских принципов предлагается
1) зарефакторить 50тыс. строк кода 2) заведомо менее эффективным решением.
Re[8]: Visitor
От: Erop Россия  
Дата: 04.09.07 07:00
Оценка:
Здравствуйте, 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 лет и развиваться, то инфраструктурные инвестиции очень выгодны. Просто они долго очень отбиваются. А если два месяца -- то инфраструктурные инвестиции -- пустая трата сил
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[6]: Visitor
От: remark Россия http://www.1024cores.net/
Дата: 04.09.07 07:49
Оценка:
Здравствуйте, 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.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[3]: хочу странного: сменить класс объекта в runtime
От: Erop Россия  
Дата: 04.09.07 08:21
Оценка:
Здравствуйте, remark, Вы писали:

R>Если на это положиться, то можно просто перезаписывать vptr, даже никакого С не надо:


Всё равно отсанется необходимость в ручном поддержании "эквивалентности" различных реализаций
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[8]: Visitor
От: Константин Л. Франция  
Дата: 04.09.07 09:36
Оценка:
Здравствуйте, c-smile, Вы писали:

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


E>>Я тебе уже давно советую сделать такой рефакторинг, но ты чего-то не хочешь.


CS>Зачем?


CS>В C++ объект это есть структура содержащая указатель на vtbl + данные инстанса.

CS>По условию задачи мне нужно менять значение указателя vtbl
CS>т.е. замена 1 (одного) указателя. Решается все 1 (одной) одной функцией из 5 (пяти) строк.

ну так давно пора закрыть ветку, раз ты все для себя решил.

CS>Я не могу понять — за ради каких вселенских принципов предлагается

CS>1) зарефакторить 50тыс. строк кода 2) заведомо менее эффективным решением.

А я не могу понять, почему ты так относишься к предложениям, которые выдвигаются по твоей собственной просьбе? Зачем все в штыки принимать?
Re[9]: Visitor
От: c-smile Канада http://terrainformatica.com
Дата: 04.09.07 16:14
Оценка:
Здравствуйте, Константин Л., Вы писали:

CS>>В C++ объект это есть структура содержащая указатель на vtbl + данные инстанса.

CS>>По условию задачи мне нужно менять значение указателя vtbl
CS>>т.е. замена 1 (одного) указателя. Решается все 1 (одной) одной функцией из 5 (пяти) строк.

КЛ>ну так давно пора закрыть ветку, раз ты все для себя решил.


Да пора.

CS>>Я не могу понять — за ради каких вселенских принципов предлагается

CS>>1) зарефакторить 50тыс. строк кода 2) заведомо менее эффективным решением.

КЛ>А я не могу понять, почему ты так относишься к предложениям, которые выдвигаются по твоей собственной просьбе? Зачем все в штыки принимать?


Мда, народ упорно не читает ТЗ.
Вопрос был такой "Существует ли способ сменить класс объекта в p?"
Все.
Re[10]: Visitor
От: Erop Россия  
Дата: 04.09.07 16:17
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Мда, народ упорно не читает ТЗ.

CS>Вопрос был такой "Существует ли способ сменить класс объекта в p?"
CS>Все.
Ну тебе предлагали разные решения на эту тему.
Например, я
Автор: Erop
Дата: 01.09.07
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: хочу странного: сменить класс объекта в runtime
От: c-smile Канада http://terrainformatica.com
Дата: 04.09.07 16:37
Оценка:
Здравствуйте, 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
От: Erop Россия  
Дата: 04.09.07 16:40
Оценка:
Здравствуйте, 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) Есть данные. Они должны быть одинаковыми везде, вроде как. Или ты выводишь свои "стратегии" из реального класса с данными?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[10]: Visitor
От: Константин Л. Франция  
Дата: 04.09.07 17:07
Оценка:
Здравствуйте, c-smile, Вы писали:

[]

CS>Мда, народ упорно не читает ТЗ.

CS>Вопрос был такой "Существует ли способ сменить класс объекта в p?"
CS>Все.

не хочется флеймить, но ты упорно пытаешься "продвинуть" свой вариант с placement new, выдвигая аргумент "зачем", вместо того, чтобы рассмотреть все плюсы и минусы. Не безопасен он. Как только размеры объектов изменятся, ты получишь вылет в корку, а не compile-time error/warning.
Re[6]: хочу странного: сменить класс объекта в runtime
От: c-smile Канада http://terrainformatica.com
Дата: 04.09.07 17:41
Оценка:
Здравствуйте, Erop, Вы писали:

CS>>Это вот "ручное" или нет?


E>Я так понимаю, что обычно жизнь немного не такая.

E>1) методов много
E>2) Есть данные. Они должны быть одинаковыми везде, вроде как. Или ты выводишь свои "стратегии" из реального класса с данными?

Цитата из начального сообщения:

class P
{
   int data;
   virtual foo() = 0;
}

class A: public P
{
   // no class specific data, only methods
   virtual foo() { ++data; }
}

class B: public P
{
   // no class specific data, only methods
   virtual foo() { --data; }
}


Обрати внимание на комментарии.
Re[7]: хочу странного: сменить класс объекта в runtime
От: Erop Россия  
Дата: 04.09.07 17:46
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Цитата из начального сообщения:

CS>Обрати внимание на комментарии.
Ну всё равно тонкости бывают. Ты же модель показываешь только. Мне твой подход всё равно кажется опасным.
Даже о том, что ты не все методы перекрыл тебя не предупредят.
Ну а в случае множественного наследования в базе вообще всё плохо будет
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[11]: Visitor
От: c-smile Канада http://terrainformatica.com
Дата: 04.09.07 18:50
Оценка:
Здравствуйте, Константин Л., Вы писали:

КЛ>Здравствуйте, c-smile, Вы писали:


КЛ>[]


CS>>Мда, народ упорно не читает ТЗ.

CS>>Вопрос был такой "Существует ли способ сменить класс объекта в p?"
CS>>Все.

КЛ>не хочется флеймить, но ты упорно пытаешься "продвинуть" свой вариант с placement new, выдвигая аргумент "зачем", вместо того, чтобы рассмотреть все плюсы и минусы. Не безопасен он. Как только размеры объектов изменятся, ты получишь вылет в корку, а не compile-time error/warning.


Таки да, народ упорно не читает ТЗ.

Написано же ведь изначально что A и B не изменяют размер instance — т.е. новые data members не добавляют.
Соответсвенно вопрос в этом контексте: чем конкретно вариант с placement new "Не безопасен он." ?
Re[11]: Visitor
От: c-smile Канада http://terrainformatica.com
Дата: 04.09.07 18:52
Оценка:
Здравствуйте, remark, Вы писали:

R>З.ы. "хакерское" создание нового объекта на месте старого — не смена типа *объекта*


Чем конкретно оно "хакерское"?
Re[12]: Visitor
От: Erop Россия  
Дата: 04.09.07 18:55
Оценка:
Здравствуйте, 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
От: c-smile Канада http://terrainformatica.com
Дата: 04.09.07 19:00
Оценка:
Здравствуйте, Erop, Вы писали:

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


CS>>Цитата из начального сообщения:

CS>>Обрати внимание на комментарии.

E>Ну всё равно тонкости бывают. Ты же модель показываешь только. Мне твой подход всё равно кажется опасным.


"тонкости бывают", "кажется" — это из какой оперы?

E>Даже о том, что ты не все методы перекрыл тебя не предупредят.


Что ты имеешь ввиду? Кто не предупредит?
Или ты считаешь что
new(p) B;

скомпилируется если у тебя абстрактные методы не имплементированы в B?

E>Ну а в случае множественного наследования в базе вообще всё плохо будет


Да ну нет там множественного наследования! См. условия в начальном сообщении.
Re[12]: Visitor
От: remark Россия http://www.1024cores.net/
Дата: 04.09.07 19:11
Оценка:
Здравствуйте, c-smile, Вы писали:

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


R>>З.ы. "хакерское" создание нового объекта на месте старого — не смена типа *объекта*


CS>Чем конкретно оно "хакерское"?


Ну не знаю... для меня лично, это очевидно, я не знаю как это передать... "хакерское" — это скорее качественная характеристика, нежели количественная...

Ну вот например. Я отлаживаю код в отладчике. Мой объект запоминает указатель на другой объект. Я запоминаю адрес объекта и его динамический тип по таблице виртуальных функций. Далее через некотрое время я опять смотрю на свой объект. Хопа! Динамический тип изменился! Адрес остался прежним, а тип поменялся. Первый признак либо расстрела памяти, либо обращения к удалённому объекту. И понеслась! Кровопролитный дебаг на пару часов.
Ну ты должен знать слова, которыми вспоминают программиста, который такое написал, когда всё-таки понимают в чём дело Лучше тебе рядом не находится


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[13]: Visitor
От: c-smile Канада http://terrainformatica.com
Дата: 04.09.07 19:25
Оценка:
Здравствуйте, remark, Вы писали:

R>>>З.ы. "хакерское" создание нового объекта на месте старого — не смена типа *объекта*


CS>>Чем конкретно оно "хакерское"?


R>Ну не знаю... для меня лично, это очевидно, я не знаю как это передать... "хакерское" — это скорее качественная характеристика, нежели количественная...


Хакерское решение это например когда используются недокументирванные фичи.
Здесь такого нет.

R>Ну вот например. Я отлаживаю код в отладчике. Мой объект запоминает указатель на другой объект. Я запоминаю адрес объекта и его динамический тип по таблице виртуальных функций. Далее через некотрое время я опять смотрю на свой объект. Хопа! Динамический тип изменился! Адрес остался прежним, а тип поменялся. Первый признак либо расстрела памяти, либо обращения к удалённому объекту. И понеслась! Кровопролитный дебаг на пару часов.

R>Ну ты должен знать слова, которыми вспоминают программиста, который такое написал, когда всё-таки понимают в чём дело Лучше тебе рядом не находится

Лирика какая-то, право слово.

Обрати внимание на "покраснение" справа:
Re[9]: хочу странного: сменить класс объекта в runtime
От: Erop Россия  
Дата: 04.09.07 19:46
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Или ты считаешь что

CS>
CS>new(p) B;
CS>

CS>скомпилируется если у тебя абстрактные методы не имплементированы в B?
Я так и не понял, ты пересоздаёшь данные в конструкторе или нет.
Если нет, но я тебе предложил совсем корректное решение. Но ты сам вроде говорил, что оно тебя не устраивает, так как ты не хочешь звать конструкторы объектов A и B так как долго строятся данные...

CS>Да ну нет там множественного наследования! См. условия в начальном сообщении.

В начальном сообщении вообще нет наследования. P -- просто корневой абстрактный класс.
Но если всё так просто, то я вообще не понимю, зачем так хаккерить?
Чем тогда тебе State не подходит, либо решение от remark'a?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[14]: Visitor
От: remark Россия http://www.1024cores.net/
Дата: 04.09.07 19:58
Оценка:
Здравствуйте, c-smile, Вы писали:

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


R>>>>З.ы. "хакерское" создание нового объекта на месте старого — не смена типа *объекта*


CS>>>Чем конкретно оно "хакерское"?


R>>Ну не знаю... для меня лично, это очевидно, я не знаю как это передать... "хакерское" — это скорее качественная характеристика, нежели количественная...


CS>Хакерское решение это например когда используются недокументирванные фичи.


Это уже не хакерское, это уже "работающее по счастливой случайности"

CS>Здесь такого нет.


Нет, ну выбирать тебе. Я думаю, ты сам прекрасно понимаешь, что я имею в виду.


R>>Ну вот например. Я отлаживаю код в отладчике. Мой объект запоминает указатель на другой объект. Я запоминаю адрес объекта и его динамический тип по таблице виртуальных функций. Далее через некотрое время я опять смотрю на свой объект. Хопа! Динамический тип изменился! Адрес остался прежним, а тип поменялся. Первый признак либо расстрела памяти, либо обращения к удалённому объекту. И понеслась! Кровопролитный дебаг на пару часов.

R>>Ну ты должен знать слова, которыми вспоминают программиста, который такое написал, когда всё-таки понимают в чём дело Лучше тебе рядом не находится

CS>Лирика какая-то, право слово.


CS>Обрати внимание на "покраснение" справа:


Покраснение будет только при выполнении строчки, в которой это поменялось. Если разработчик нашёл и увидел эту строчку, то это уже половина дела. Речь о том, когда я ставлю точки останова только на моём, интересующим меня в данный момент, коде.



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[10]: хочу странного: сменить класс объекта в runtime
От: c-smile Канада http://terrainformatica.com
Дата: 04.09.07 20:38
Оценка:
Здравствуйте, 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?
Re[11]: Эх, ещё раз попробую объяснить...
От: Erop Россия  
Дата: 04.09.07 21:23
Оценка:
Здравствуйте, 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
От: Шахтер Интернет  
Дата: 04.09.07 23:29
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Здравствуйте, Шахтер, Вы писали:


CS>>>Единственно я бы тогда уже сделал так:


CS>>>
CS>>>struct P
CS>>>{
CS>>>template <typename T>
CS>>>  void mutate()
CS>>>{
CS>>>   static_assert( sizeof(T) == sizeof(*this) );
CS>>>   T temp;
CS>>>   swap(temp);
CS>>>   this->>~P();
CS>>>   new(this) T;
CS>>>   temp.swap(*this);
CS>>>}
CS>>>};
CS>>>


Ш>>Это менее эффективно. Ты здесь делаешь обмен состояниями с 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 сделать, но это менее эффективно.
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[6]: хочу странного: сменить класс объекта в runtime
От: Erop Россия  
Дата: 05.09.07 08:06
Оценка:
Здравствуйте, Шахтер, Вы писали:

Ш>Я не делаю swap, я копирую данные. Т.е. saveTo сохраняет состояние объекта. Это одно копирование.

Вообще-то никто не мешает специфицировать std::swap для своего типа. Тогда всё будет эффективно.
Плюс метода состоит в том, что специфицировать надо только доля тех типов, для которых будут детектированы тормоза...
Правда и saveTo можно гаписать шаблонный...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[13]: Решил я таки попробовать пописать ... :)
От: Константин Л. Франция  
Дата: 05.09.07 09:44
Оценка:
Здравствуйте, Erop, Вы писали:

[]

Внимание! Нелюбитель шаблонов пишет шаблонный код! Перевосптался!
Re[14]: Решил я таки попробовать пописать ... :)
От: Erop Россия  
Дата: 05.09.07 09:54
Оценка:
Здравствуйте, Константин Л., Вы писали:

КЛ>Внимание! Нелюбитель шаблонов пишет шаблонный код! Перевосптался!

Я не любитель не шаблонов вообще, а шаблонов, которые не проектировались или проектировались плохо
Если тут делать всё стразу нормально, то никаких шаблнов вообще-то не надо, но я предположил, что ситуация изначально плохая...

Ну а так вообще-то есть какие-то предложения по существу?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: хочу странного: сменить класс объекта в runtime
От: StevenIvanov США  
Дата: 05.09.07 10:31
Оценка:
Здравствуйте, StevenIvanov, Вы писали:

SI> ...


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


#include <iostream>
using namespace std;

class Base
{
public:
    Base()
    {
    }

    virtual ~Base()
    {}

public:
    // methods to override
    virtual 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 США  
Дата: 05.09.07 10:36
Оценка:
Здравствуйте, StevenIvanov, Вы писали:

SI> ...


Совсем забыл уточнить:
протестировано и работает на x86-windows-msvc8.0, x86-linux-g++, arm-linux-g++
Re[4]: хочу странного: сменить класс объекта в runtime
От: Erop Россия  
Дата: 05.09.07 10:46
Оценка:
Здравствуйте, StevenIvanov, Вы писали:

SI>протестировано и работает на x86-windows-msvc8.0, x86-linux-g++, arm-linux-g++

Это скорее всего взорвётся не при смене платфолрмы, а при какой-то неожиданной модификации кода.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[5]: хочу странного: сменить класс объекта в runtime
От: StevenIvanov США  
Дата: 05.09.07 11:23
Оценка:
Здравствуйте, Erop, Вы писали:

E>...

E>Это скорее всего взорвётся не при смене платфолрмы, а при какой-то неожиданной модификации кода.
Можно заранее утверждать, что взорвется, поскольку компилятором такая возможность не предоставляется и даже минимальная защита от дурака отсутствует.

Если хочется быть уверенным на все 100 — необходимо писать объявления таких классов только на С (реализация функций может быть сделана уже на C++). И вообще, по-моему я нечто подобное уже видел (но не хочу утверждать) в ядре linux в разделе кода для управления файловыми системами. Так вот, там есть код создания управляющей структуры драйвера конкретной файловой системы, которая состоит из указателей на функции read, write, close... Эти функции в некоторых случаях могут быть заменены на другие (что-то вроде реализация перехвата операций чтения-записи).

OFF: А по поводу рассматриваемой задачи — честно говоря я так и не понял почему нельзя использовать visitor? Я читал все (ну почти) посты и так и не понял почему это решение не подходит для такой задачи.
Re[6]: хочу странного: сменить класс объекта в runtime
От: Erop Россия  
Дата: 05.09.07 12:13
Оценка:
Здравствуйте, StevenIvanov, Вы писали:

SI>OFF: А по поводу рассматриваемой задачи — честно говоря я так и не понял почему нельзя использовать visitor? Я читал все (ну почти) посты и так и не понял почему это решение не подходит для такой задачи.

Да подходит оно, ну может немного переточенное.
Просто я так понял, что уже есть некий код, и вопрос состоит в том, как его подешевле зарефакторить. Хотя лучше обратиться за разъяснениями к топикстартеру

Я вот попробовал так и сяк прикинуть как будет. ИМХО не сложно это и там и сям выходит. Вернее сравнимо сложно
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[7]: хочу странного: сменить класс объекта в runtime
От: Шахтер Интернет  
Дата: 06.09.07 23:11
Оценка:
Здравствуйте, Erop, Вы писали:

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


Ш>>Я не делаю swap, я копирую данные. Т.е. saveTo сохраняет состояние объекта. Это одно копирование.

E>Вообще-то никто не мешает специфицировать std::swap для своего типа. Тогда всё будет эффективно.

saveTo оптимален

E>Плюс метода состоит в том, что специфицировать надо только доля тех типов, для которых будут детектированы тормоза...

E>Правда и saveTo можно гаписать шаблонный...

Конечно. Более того, его можно через через swap реализовать.
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[8]: хочу странного: сменить класс объекта в runtime
От: Erop Россия  
Дата: 07.09.07 06:41
Оценка:
Здравствуйте, Шахтер, Вы писали:

Ш>saveTo оптимален

Я согласен, только мне больше нравится название MoveTo
Во всяком случае в моей любимой библиотеке используется MoveTo...

Но в жанном случае моей любимой библиотеки нет, а std::swap есть
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[6]: хочу странного: сменить класс объекта в runtime
От: c-smile Канада http://terrainformatica.com
Дата: 08.09.07 22:52
Оценка:
Здравствуйте, StevenIvanov, Вы писали:

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


E>>...

E>>Это скорее всего взорвётся не при смене платфолрмы, а при какой-то неожиданной модификации кода.
SI>Можно заранее утверждать, что взорвется, поскольку компилятором такая возможность не предоставляется и даже минимальная защита от дурака отсутствует.

"защита от дурака отсутствует" не беспокит. Дураки к тому коду доступа не имеют.

SI>Если хочется быть уверенным на все 100 — необходимо писать объявления таких классов только на С (реализация функций может быть сделана уже на C++). И вообще, по-моему я нечто подобное уже видел (но не хочу утверждать) в ядре linux в разделе кода для управления файловыми системами. Так вот, там есть код создания управляющей структуры драйвера конкретной файловой системы, которая состоит из указателей на функции read, write, close... Эти функции в некоторых случаях могут быть заменены на другие (что-то вроде реализация перехвата операций чтения-записи).


Для решения данной задачи есть два варианта:

Первый:

turn_to(object* p, typename TO)
{
  TO t; 
  p->transfer_data_to(t); // aka directional swap()
  new(p) TO;
  t.transfer_data_to(*p); // aka directional swap()
}


Второй:

turn_to(object* p, typename TO)
{
  static TO t;
  p->_pvtbl = t._pvtbl;
}


Второй вариант выглядит как хак в том смысле что использует знание проекции C++ объектов на C структуры.
Что в принципе тоже safe до тех пор пока С++ поддерживает COM и Corba имплементации которые
тоже построены на этом эзотерическом знании.

Выбор между этими вариантами определяется имплементацией — насколько часто ноужно делать смену секса и
насколько lightweight конструкторы по умолчанию ( TO t; ) и transfer_data_to.

SI>OFF: А по поводу рассматриваемой задачи — честно говоря я так и не понял почему нельзя использовать visitor? Я читал все (ну почти) посты и так и не понял почему это решение не подходит для такой задачи.


Идиома visitor требует значительного refactoring что не рассматривается как опция.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.