замена VPTR это "грязный" хак?
От: Аноним  
Дата: 03.04.11 20:33
Оценка: :))
Хочу использовать реализации интерфейсов без динамической реаллокации объектов реализующих их,
способ примерно такой:

class Interface {
public:
  virtual void someFun() =0;
};
class Derived1: public Interface {
public:
  void someFun() {
    //реализация...
  }
};
class Derived2: public Interface {
public:
  void someFun() {
    //реализация...
  }
};

class User { // потребитель
public:
  Interface intf;
  template <class T> changeIntfRealization() {
    // возможные проверки на происхождение T от Interface на манер: static_assert(is_base_of<Interface, T>::value, "Ouch");
    new (&intf)T(); // происходит подмена VPTR
  }  
};

int main() {
  User user;
  user.changeIntfRealization<Derived1> ();
  user.intf.someFun(); // вызовется реализация Derived1

  user.changeIntfRealization<Derived2> ();
  user.intf.someFun(); // вызовется реализация Derived2
}


Собственно вопрос в топике.
Re: замена VPTR это "грязный" хак?
От: c-smile Канада http://terrainformatica.com
Дата: 03.04.11 21:50
Оценка: +2 -2
Здравствуйте, Аноним, Вы писали:

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

А>способ примерно такой:

Это стандартная фича языка. Поэтому хаком это является в том случае если этот момент у тебя не задокументирован должным образом.

А вообще вот:
http://www.terrainformatica.com/2010/08/cpp-how-to-change-class-of-object-in-runtime/
обрати внимание на comments.
Re[2]: замена VPTR это "грязный" хак?
От: zaufi Земля  
Дата: 03.04.11 22:35
Оценка: 1 (1) +6 -1
Здравствуйте, c-smile, Вы писали:

CS>Здравствуйте, Аноним, Вы писали:


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

А>>способ примерно такой:

CS>Это стандартная фича языка.

да?? а ссылочку на section в стандарте можно в студию?? %)

CS>Поэтому хаком это является в том случае если этот момент у тебя не задокументирован должным образом.


это хак в любом случае, в зависимости от того документированный он или нет можно только подставлять разные эпитеты... типа "грязный хак", например
ради чего делать себе жизнь сложнее и тем кому неповезет сопровождать этот код, когда можно легко обойтись "стандартным" (Behaviour) паттерном?
сложнее в том плане, что нужно очень аккуратно контролировать размер объектов, типы полей, заботиться о правильной переинициализации, аккуратно писать конструкторы внутренних классов, и все такое прочее... все это очень жестко завязано на конкрентую задачу и layout классов изменяющих поведение, и шаг вправо\лево -- расстрел памяти... ради чего? какой profit??
Re: замена VPTR это "грязный" хак?
От: A13x США  
Дата: 04.04.11 07:42
Оценка: +1
Здравствуйте, Аноним, Вы писали:

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

А>...

ужос.

Нафига вообще менять VPTR??

Здесь же просится паттерн стратегия:



// класс у которого хочется круто менять поведение
class YourFlexibleObject {
 private:
  Strategy * strategy_;

 public:
  // вот этот метод меняет стратегию
  void setStrategy(Strategy * strategy) { this->strategy_ = strategy; }

  // интерфейсная часть, которую хочется менять:
  int getSmth(Arg1 arg1, Arg2 arg2) {
    ASSERT_VALID(strategy_);
    return strategy_->getSmth(this, arg1, arg2);
  }

  void doSmth(Arg3 arg3, Arg4 arg4) {
    ASSERT_VALID(strategy_);
    return strategy_->getSmth(this, arg3, arg4);
  }

  // ...
};


Strategy — интерфейс, который реализуют нужные тебе конкретные воплощения стратегии.
Эти экземпляры можно выделить один раз и переиспользовать без реаллокации — voila!
Re: замена VPTR это "грязный" хак?
От: Тот кто сидит в пруду Россия  
Дата: 04.04.11 09:06
Оценка:
Здравствуйте, Аноним, Вы писали:

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

А>способ примерно такой:

А>class User { // потребитель

А>public:
А> Interface intf;
А> template <class T> changeIntfRealization() {
А> // возможные проверки на происхождение T от Interface на манер: static_assert(is_base_of<Interface, T>::value, "Ouch");
А> new (&intf)T(); // происходит подмена VPTR
А> }
А>};

А>Собственно вопрос в топике.


UB. Вообще, хотя бы деструктор предыдущей реализации надо вызвать перед placement new, ну и позаботиться о том, чтобы хватило памяти (sizeof(intf) >= sizeof любой из реализаций) под все реализации. И это все равно не гарантирует отсутствия проблем при оптимизации.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[2]: замена VPTR это "грязный" хак?
От: k.o. Россия  
Дата: 04.04.11 09:41
Оценка:
Здравствуйте, Тот кто сидит в пруду, Вы писали:

ТКС>Здравствуйте, Аноним, Вы писали:


ТКС>UB. Вообще, хотя бы деструктор предыдущей реализации надо вызвать перед placement new, ну и позаботиться о том, чтобы хватило памяти (sizeof(intf) >= sizeof любой из реализаций) под все реализации. И это все равно не гарантирует отсутствия проблем при оптимизации.


Деструктор, если он тривиальный, вызывать необязательно, UB будет только если размеры или выравние создаваемых объектов не соответсвуют используемой памяти.
Re[3]: замена VPTR это "грязный" хак?
От: k.o. Россия  
Дата: 04.04.11 09:58
Оценка: -1
Здравствуйте, zaufi, Вы писали:

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


CS>>Здравствуйте, Аноним, Вы писали:


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

А>>>способ примерно такой:

CS>>Это стандартная фича языка.

Z>да?? а ссылочку на section в стандарте можно в студию?? %)

3.8 Object lifetime. Единственная, трудность может быть с обеспечением правильного выравнивания, но, если размеры объектов совпадают и память выделяется динамически, корректность гарантируется стандартом.

CS>>Поэтому хаком это является в том случае если этот момент у тебя не задокументирован должным образом.


Z>это хак в любом случае, в зависимости от того документированный он или нет можно только подставлять разные эпитеты... типа "грязный хак", например

Z>ради чего делать себе жизнь сложнее и тем кому неповезет сопровождать этот код, когда можно легко обойтись "стандартным" (Behaviour) паттерном?
Z>сложнее в том плане, что нужно очень аккуратно контролировать размер объектов, типы полей, заботиться о правильной переинициализации, аккуратно писать конструкторы внутренних классов, и все такое прочее... все это очень жестко завязано на конкрентую задачу и layout классов изменяющих поведение, и шаг вправо\лево -- расстрел памяти... ради чего? какой profit??

+100500
Re[3]: замена VPTR это "грязный" хак?
От: Тот кто сидит в пруду Россия  
Дата: 04.04.11 10:49
Оценка:
Здравствуйте, k.o., Вы писали:

ТКС>>UB. Вообще, хотя бы деструктор предыдущей реализации надо вызвать перед placement new, ну и позаботиться о том, чтобы хватило памяти (sizeof(intf) >= sizeof любой из реализаций) под все реализации. И это все равно не гарантирует отсутствия проблем при оптимизации.


KO>Деструктор, если он тривиальный, вызывать необязательно


Так вроде никто тривиальных деструкторов не обещал
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[4]: замена VPTR это "грязный" хак?
От: k.o. Россия  
Дата: 04.04.11 12:15
Оценка:
Здравствуйте, Тот кто сидит в пруду, Вы писали:

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


ТКС>>>UB. Вообще, хотя бы деструктор предыдущей реализации надо вызвать перед placement new, ну и позаботиться о том, чтобы хватило памяти (sizeof(intf) >= sizeof любой из реализаций) под все реализации. И это все равно не гарантирует отсутствия проблем при оптимизации.


KO>>Деструктор, если он тривиальный, вызывать необязательно


ТКС>Так вроде никто тривиальных деструкторов не обещал


В исходном сообщении они тривиальные. А если деструктор сам не вызывается, то можно и на не-тривиальные забивать, если, конечно, не нужно то, что они делают.
Re: замена VPTR это "грязный" хак?
От: ononim  
Дата: 04.04.11 12:43
Оценка: 3 (1) +1 -1
class Yanus
{
    Interface *_if1, *_if2;
    
public:
    Yanus(Interface *if1, Interface *if2)
        :_if1(if1), _if2(if2){}

    ~Yanus()
    {
        delete _if1;
        delete _if2;
    }

    Interface *operator->() { return _if1; }
    const Interface *operator->() const { return _if1; }
    void SwitchFace() { std::swap(_if1, _if2); }
};
Как много веселых ребят, и все делают велосипед...
Re[2]: замена VPTR это "грязный" хак?
От: k.o. Россия  
Дата: 04.04.11 17:48
Оценка:
Здравствуйте, ononim, Вы писали:

Минус за то, что этот код не отвечает на вопрос ТС о грязности хака и не решает проблему смены реализации без переаллокации.
Re[2]: замена VPTR это "грязный" хак?
От: Vain Россия google.ru
Дата: 04.04.11 19:37
Оценка:
Здравствуйте, A13x, Вы писали:

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

А>>...
A>ужос.
A>Нафига вообще менять VPTR??
A>Здесь же просится паттерн стратегия:
Что-то ваш паттерн стратегия напоминает паттерн Pimpl
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[3]: замена VPTR это "грязный" хак?
От: ononim  
Дата: 04.04.11 20:25
Оценка:
KO>Минус за то, что этот код не отвечает на вопрос ТС о грязности хака и не решает проблему смены реализации без переаллокации.
А где вы тут видите "переаллокацию" при смене реализации? При смене там тока std::swap вызывается, который ниче не "переаллоцирует"
А на вопрос о "грязности" тут все ответили — добавлять особо нечего, а энтропию плодить, пересказывая уже сказанное — не хочу.
Как много веселых ребят, и все делают велосипед...
Re[4]: замена VPTR это "грязный" хак?
От: k.o. Россия  
Дата: 04.04.11 20:32
Оценка:
Здравствуйте, ononim, Вы писали:

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

O>А где вы тут видите "переаллокацию" при смене реализации? При смене там тока std::swap вызывается, который ниче не "переаллоцирует"

Ну, хорошо, ты решил проблему переаллокации, заранее выделив память для всех используемых реализаций. Ты действительно считаешь это хорошим решением? Кстати, интересно было бы увидеть как это будет выглядеть не для 2-х реализаций, а, хотя бы для 10.
Re[5]: замена VPTR это "грязный" хак?
От: ononim  
Дата: 04.04.11 23:38
Оценка: 6 (1)
KO>>>Минус за то, что этот код не отвечает на вопрос ТС о грязности хака и не решает проблему смены реализации без переаллокации.
O>>А где вы тут видите "переаллокацию" при смене реализации? При смене там тока std::swap вызывается, который ниче не "переаллоцирует"
KO>Ну, хорошо, ты решил проблему переаллокации, заранее выделив память для всех используемых реализаций. Ты действительно считаешь это хорошим решением? Кстати, интересно было бы увидеть как это будет выглядеть не для 2-х реализаций, а, хотя бы для 10.
Ну так а давайте писать код согласно требованиям, а не фантазиям. ТС хотел отсутствие реаллокации при смене реализации — он его получил. Если хочется минимизировать размер занятой памяти, — это уже совершенно иное требование, и решение будет тоже иное. К примеру, такое:


class Interface 
{
public:
    virtual void foo() = 0;
    virtual ~Interface() = 0 {};

};

class InterfaceImpl1 : public Interface
{
public:
    virtual void foo()
    {
        printf("InterfaceImpl1 this=0x%x\n", this);
    }
};

class InterfaceImpl2 : public Interface
{
public:
    virtual void foo()
    {
        printf("InterfaceImpl2 this=0x%x\n", this);
    }
};

class Yanus
{
    void *p;
    Interface *i;
    size_t l;

public:
    Yanus(size_t prealloc_size = 0) 
        :p(0), i(0), l(0)
    {
        if (prealloc_size)
        {
            p = malloc(prealloc_size);
            if (p) l = prealloc_size;
        }
    }

    ~Yanus()
    {
        if (i)i->~Interface();
    }

    Interface *operator->() { return i; }
    const Interface *operator->() const { return i; }
    template<class IFIMPL>
    void ChangeFace() throw(std::bad_alloc)
    { 
        if (i) 
        {
            i->~Interface();
            i = 0;
        }

        if (sizeof(IFIMPL)>l)
        {
            p = realloc(p, sizeof(IFIMPL));
            if (!p)
            {
                l = 0;
                throw std::bad_alloc();
            }
            l = sizeof(IFIMPL);
        }

        i = new (p) IFIMPL ();
    }
};

void test_yanus()
{
    Yanus y;
    y.ChangeFace<InterfaceImpl1>();
    y->foo();
    y.ChangeFace<InterfaceImpl2>();
    y->foo();
}


ну или хотябы такое:
template <class IMPL1, class IMPL2>
class Yanus
{
    char buf[max(sizeof(IMPL1), sizeof(IMPL2))];
    Interface *i;

public:
    Yanus() : i(0)
    {
    }

    ~Yanus()
    {
        if (i)i->~Interface();
    }

    Interface *operator->() { return i; }
    const Interface *operator->() const { return i; }
    
    void ChangeFace(bool second)
    { 
        if (i) 
        {
            i->~Interface();
            i = 0;
        }

        i = second ?  
            (Interface *)new (&buf[0]) IMPL1 () 
            : (Interface *)new (&buf[0]) IMPL2 ();
    }
};
Как много веселых ребят, и все делают велосипед...
Re[3]: замена VPTR это "грязный" хак?
От: c-smile Канада http://terrainformatica.com
Дата: 05.04.11 02:15
Оценка: -1 :))
Здравствуйте, zaufi, Вы писали:

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


CS>>Здравствуйте, Аноним, Вы писали:


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

А>>>способ примерно такой:

CS>>Это стандартная фича языка.

Z>да?? а ссылочку на section в стандарте можно в студию?? %)

А "ссылочку" на что собственно?
На то как работает placement new? Или на то что в C++ нет implicit инициализаций полей структур и классов как в Java например.
Это все имхо учебников начального уровня требует ибо основы.

CS>>Поэтому хаком это является в том случае если этот момент у тебя не задокументирован должным образом.


Z>это хак в любом случае, в зависимости от того документированный он или нет можно только подставлять разные эпитеты... типа "грязный хак", например


Я знаю use cases где это решение приносит значительный профит.

Z>ради чего делать себе жизнь сложнее и тем кому неповезет сопровождать этот код, когда можно легко обойтись "стандартным" (Behaviour) паттерном?


Что есть такое "стандартный (Behaviour) паттерн" в данном контексте?

Z>сложнее в том плане, что нужно очень аккуратно контролировать размер объектов, типы полей, заботиться о правильной переинициализации, аккуратно писать конструкторы внутренних классов, и все такое прочее... все это очень жестко завязано на конкрентую задачу и layout классов изменяющих поведение, и шаг вправо\лево -- расстрел памяти... ради чего? какой profit??


Детский сад право слово. Не ходите дети в лес — там волки.

Еще раз: есть области где именно аккуратное и эффективное прописывание всего и вся есть mission critical.
Я подозреваю что для C++ uses cases это как раз типично. Иначе рекомендовано использовать Java и .NET. Там низкий порог вхождения.
Re[6]: замена VPTR это "грязный" хак?
От: k.o. Россия  
Дата: 05.04.11 07:20
Оценка:
Здравствуйте, ononim, Вы писали:

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

O>>>А где вы тут видите "переаллокацию" при смене реализации? При смене там тока std::swap вызывается, который ниче не "переаллоцирует"
KO>>Ну, хорошо, ты решил проблему переаллокации, заранее выделив память для всех используемых реализаций. Ты действительно считаешь это хорошим решением? Кстати, интересно было бы увидеть как это будет выглядеть не для 2-х реализаций, а, хотя бы для 10.
O>Ну так а давайте писать код согласно требованиям, а не фантазиям. ТС хотел отсутствие реаллокации при смене реализации — он его получил. Если хочется минимизировать размер занятой памяти, — это уже совершенно иное требование, и решение будет тоже иное. К примеру, такое:

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

  Скрытый текст
O>class Interface 
O>{
O>public:
O>    virtual void foo() = 0;
O>    virtual ~Interface() = 0 {};

O>};

O>class InterfaceImpl1 : public Interface
O>{
O>public:
O>    virtual void foo()
O>    {
O>        printf("InterfaceImpl1 this=0x%x\n", this);
O>    }
O>};

O>class InterfaceImpl2 : public Interface
O>{
O>public:
O>    virtual void foo()
O>    {
O>        printf("InterfaceImpl2 this=0x%x\n", this);
O>    }
O>};

O>class Yanus
O>{
O>    void *p;
O>    Interface *i;
O>    size_t l;

O>public:
O>    Yanus(size_t prealloc_size = 0) 
O>        :p(0), i(0), l(0)
O>    {
O>        if (prealloc_size)
O>        {
O>            p = malloc(prealloc_size);
O>            if (p) l = prealloc_size;
O>        }
O>    }

O>    ~Yanus()
O>    {
O>        if (i)i->~Interface();
O>    }

O>    Interface *operator->() { return i; }
O>    const Interface *operator->() const { return i; }
O>    template<class IFIMPL>
O>    void ChangeFace() throw(std::bad_alloc)
O>    { 
O>        if (i) 
O>        {
            i->~Interface();
O>            i = 0;
O>        }

O>        if (sizeof(IFIMPL)>l)
O>        {
O>            p = realloc(p, sizeof(IFIMPL));
O>            if (!p)
O>            {
O>                l = 0;
O>                throw std::bad_alloc();
O>            }
O>            l = sizeof(IFIMPL);
O>        }

O>        i = new (p) IFIMPL ();
O>    }
O>};

O>void test_yanus()
O>{
O>    Yanus y;
O>    y.ChangeFace<InterfaceImpl1>();
    y->foo();
O>    y.ChangeFace<InterfaceImpl2>();
    y->foo();
O>}

А за это спасибо, сам хотел предложить ТС что-то подобное, но лень было.
Re[4]: замена VPTR это "грязный" хак?
От: Erop Россия  
Дата: 05.04.11 07:41
Оценка: 1 (1)
Здравствуйте, c-smile, Вы писали:

Z>>это хак в любом случае, в зависимости от того документированный он или нет можно только подставлять разные эпитеты... типа "грязный хак", например

CS>Я знаю use cases где это решение приносит значительный профит.

Поподробнее не расскажешь?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: замена VPTR это "грязный" хак?
От: Caracrist https://1pwd.org/
Дата: 05.04.11 08:05
Оценка:
Здравствуйте, Аноним, Вы писали:

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


А>Собственно вопрос в топике.


class Interface {
public:
  virtual void someFun() =0;
  virtual ~Interface(){}
};
class Derived1: public Interface {
public:
  void someFun() {
    //реализация...
  }
};
class Derived2: public Interface {
public:
  void someFun() {
    //реализация...
  }
};

class User { // потребитель
public:
  char buff[sizeof(Interface)]; // at least sizeof(Interface)
  Interface *intf;
  User() : intf(0){
  }
  ~User(){
    Release(); 
  } 
  template <class T> 
  void changeIntfRealization() {
    static_assert(size_of(T) <= size_of(buff), "Ouch");
    Release();
    intf =  new (&buff) T();   
  }  
private:
  void Release() {
    if (intf) intf->~Interface(); 
    intf = 0;
  }
};

int main() {
  User user;
  if (!user.intf)
    cout << "not initialized yet!" << endl;

  user.changeIntfRealization<Derived1> ();
  user.intf->someFun(); // вызовется реализация Derived1

  user.changeIntfRealization<Derived2> ();
  user.intf->someFun(); // вызовется реализация Derived2
}

~~~~~
~lol~~
~~~ Single Password Solution
Re[4]: замена VPTR это "грязный" хак?
От: B0FEE664  
Дата: 05.04.11 08:17
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>>>Это стандартная фича языка.

Z>>да?? а ссылочку на section в стандарте можно в студию?? %)

CS>А "ссылочку" на что собственно?


На то, что реализация обязана использовать таблицу виртуальных методов. Откуда это следует?
А если нет , то тогда откуда следует, что можно безопасно размещать один объект по/в памяти другого объекта?

ЗЫ Приведённый в первом сообщении пример не скомпилируется.
И каждый день — без права на ошибку...
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.