запретить создавать объекты на стеке
От: pastey  
Дата: 05.07.09 09:46
Оценка:
Здраствуйте, Товарищи.

Не подскажете, как сотворить запрет на создание объектов на стеке.
Уже не превый раз возникает такая необходимость.

Вот пример:
в нашей программе все андушные объекты хранятся в анду-манагере (указатели на них), и естественно просто так взять и грохнуть объект нельзя, можно только сказать ему Delete(), и он пометится в анду-манагере как удаленный, чтобы потом можно было анду-манагеру сказать Undo(), и только что замоченный объект — тут как тут. Если бы мы грохнули объект delete'ом, то к нему уже ясен пень проандушится было бы не возможно, и к тому же в стеке анду-транзакций останется куча ссылок на него, и выколупывать их никто не будет; да и вообще, какое тогда анду, если объект чья история у нас хранится взять и физически грохнуть?
Так вот, при чем тут стековые объекты,- мы-то временем их жизни не управляем — дошли до конца блока и он помер, поэтому наши андушные объекты нельзя создавать на стеке. Сам факт создания андушного объекта есть история анду, которая должна храниться, поэтому при создании такие объекты помещаются в анду-манагер. Анду-манагер сам знает, когда у него нет ссылок на какой-то объект и прибивает его оператором delete, и если подсунуть ему указатель на стековый объект, то хорошего случится мало.

Как запретить создавать объекты некого класса на стеке, я-то знаю — закрываем конструкор и делаем функцию static Object * Create();, которая будет создавать объекты в куче. Но вот проблема с анду — в том, что надо (очень хочется ) запреть в базовом классе создавать [b]наследников[b] на стеке. Наследников у нашего IUndoObject штук 100 не меньше, и всем закрывать конструктор и писать Create() не самый удачный выход, потому что это все равно не проверяется компилятором (ему-то гаду пофиг) — достаточно где-то завтыкать, или написать новый код, в котором забыть про Create, и все — упадешь и не заметишь. Да и вообще — это куча дублирования кода, и так сказать "мантра", которую надо помнить, знать и понимать.

Извините если сумбурно объяснил причины, надеюсь на помощь.
Re: запретить создавать объекты на стеке
От: Аноним  
Дата: 05.07.09 10:08
Оценка:
Здравствуйте, pastey, Вы писали:

class A 
{
public:
    A();
    virtual void Delete()
    {
        // do something
        delete this;
    }    
protected:
    ~A()
    {
        // do somesing
    }
};

class B : public A
{
public:
    B();
protected:
    ~B()
    {
        // do something
    }
};


void main()
{
    A a0; // <- compilation error
    A a = new A;
    B b = new B;
    delete a; // <- compilation error
    a->Delete(); // OK
    delete b; // <- compilation error
    b->Delete(); // OK

}
Re[2]: запретить создавать объекты на стеке
От: pastey  
Дата: 05.07.09 10:35
Оценка:
Здравствуйте, Аноним, Вы писали:

А>
А>class A 
А>{
А>public:
А>    A();
А>    virtual void Delete()
А>    {
А>        // do something
А>        delete this;
А>    }    
А>protected:
А>    ~A()
А>    {
А>        // do somesing
А>    }
А>};

А>class B : public A
А>{
А>public:
А>    B();
А>protected:
А>    ~B()
А>    {
А>        // do something
А>    }
А>};


А>void main()
А>{
А>    A a0; // <- compilation error
А>    A a = new A;
А>    B b = new B;
А>    delete a; // <- compilation error
    a->>Delete(); // OK
А>    delete b; // <- compilation error
    b->>Delete(); // OK

А>}
А>


спасибо за ответ, это конечно выход, но не без недостатков — в каждом наследнике нужно протектить деструктор, и об этом легко забыть (наследников много, и еще будут добавляться новые — писать гневный коментарий — "делайте детсруктор наследника протекченым" — не самый крутой способ защиты).
заприватить деструктор нельзя, т.к. тогда не скомпилится деструктор наследника, а дружить всех наследников с папашей — смешно.
но все равно — спасибо за ответ, вдруг натолкнет на мысль
Re: запретить создавать объекты на стеке
От: Аноним  
Дата: 05.07.09 11:02
Оценка:
Здравствуйте, pastey, Вы писали:

P>Здраствуйте, Товарищи.


P>Не подскажете, как сотворить запрет на создание объектов на стеке.

P>Уже не превый раз возникает такая необходимость.

P>Как запретить создавать объекты некого класса на стеке, я-то знаю — закрываем конструкор и делаем функцию static Object * Create();, которая будет создавать объекты в куче. Но вот проблема с анду — в том, что надо (очень хочется ) запреть в базовом классе создавать [b]наследников[b] на стеке. Наследников у нашего IUndoObject штук 100 не меньше, и всем закрывать конструктор и писать Create() не самый удачный выход, потому что это все равно не проверяется компилятором (ему-то гаду пофиг) — достаточно где-то завтыкать, или написать новый код, в котором забыть про Create, и все — упадешь и не заметишь. Да и вообще — это куча дублирования кода, и так сказать "мантра", которую надо помнить, знать и понимать.


Что мешает использовать templates?


class UndoMngr
{
//...
public:
template<typename T>
static T * Create()
{
T * result = new T();
// register Object...
return result;
}
//...
};
class IUndoObject
{
//...
friend class UndoMngr;
protected:
IUndoObject();
virtual ~IUndoObject();
//...
};
Re: запретить создавать объекты на стеке
От: Юрий Жмеренецкий ICQ 380412032
Дата: 05.07.09 11:08
Оценка:
Здравствуйте, pastey, Вы писали:

P>Здраствуйте, Товарищи.


P>Не подскажете, как сотворить запрет на создание объектов на стеке.

P>Уже не превый раз возникает такая необходимость.

P>Вот пример:

P>в нашей программе все андушные объекты хранятся в анду-манагере (указатели на них), и естественно просто так взять и грохнуть объект нельзя, можно только сказать ему Delete(), и он пометится в анду-манагере как удаленный, чтобы потом можно было анду-манагеру сказать Undo(), и только что замоченный объект — тут как тут. Если бы мы грохнули объект delete'ом, то к нему уже ясен пень проандушится было бы не возможно, и к тому же в стеке анду-транзакций останется куча ссылок на него, и выколупывать их никто не будет; да и вообще, какое тогда анду, если объект чья история у нас хранится взять и физически грохнуть?
P>Так вот, при чем тут стековые объекты,- мы-то временем их жизни не управляем — дошли до конца блока и он помер, поэтому наши андушные объекты нельзя создавать на стеке.

Имхо как-то странно. Обратное действие — может быть обычным действием. Т.е. например есть действия "удалить" и "создать". Каждое из них есть обартное действие для другого, поэтому здесь нет разделения 'undo'/'не undo'. Просто есть контекст в котором действие трактуется как 'обратное'.

В первом приближении:
class action
{
public:
  virtual void run() = 0;
  virtual smart_ptr<action> get_undo_action() = 0;
  //...
};

class action_add : public action
{
public:
   virtual smart_ptr<action> get_undo_action();
   //...
};

class action_remove : public action
{
public:
   virtual smart_ptr<action> get_undo_action();
   //...
};

smart_ptr<action> action_add::get_undo_action(){
  return smart_ptr<action>(new action_remove(some_args));
}

smart_ptr<action> action_remove::get_undo_action(){
  return smart_ptr<action>(new action_add(some_args));
}
//...

void run(smart_ptr<action> a)
{
  a->run();
  undo_stack.add(a->get_undo_action());
}

Понятно что могут быть ситуации в которых такой подход не может быть применим, но в любом случае сабжевой проблемы я здесь не вижу.
Re[2]: запретить создавать объекты на стеке
От: pastey  
Дата: 05.07.09 11:31
Оценка:
Здравствуйте, Юрий Жмеренецкий, Вы писали:

ЮЖ>Имхо как-то странно. Обратное действие — может быть обычным действием. Т.е. например есть действия "удалить" и "создать". Каждое из них есть обартное действие для другого, поэтому здесь нет разделения 'undo'/'не undo'. Просто есть контекст в котором действие трактуется как 'обратное'.


тот код, который ты привел — это командное анду — выполнить операцю такую-то, выполнить операцию обратную ей. наше анду основано на паттерне Memento — все данные объекта хранятся отдельно, и каждая операция с объектом создает новое хранилище данных, которое сохраняет его текущее состояние в стек анду. Вобщем-то к сабжу это действитеьно не имеет отношения.
Re: запретить создавать объекты на стеке
От: Аноним  
Дата: 05.07.09 11:53
Оценка: :)
под винду както так:
class foo
{
foo()
{
volatile int test = 0;
VirtualQuery(&test, ... &mbi1,...);
VirtualQuery(this, ... &mbi2,...);
assert (mbi1.AllocationBase!=mbi2. AllocationBase);
}
};


а вообще — shared_ptr в руки и все.
Re[2]: запретить создавать объекты на стеке
От: pastey  
Дата: 05.07.09 11:55
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Что мешает использовать templates?


А>

А>class UndoMngr
А>{
А>//...
А>public:
А>template<typename T>
А>static T * Create()
А>{
А>T * result = new T();
А>// register Object...
А>return result;
А>}
А>//...
А>};
А>class IUndoObject
А>{
А>//...
А>friend class UndoMngr;
А>protected:
А>IUndoObject();
А>virtual ~IUndoObject();
А>//...
А>};

А>


я вот тоже думаю о каком-то шаблоне. твой явно задачу не решает, т.к. ничего не мешает в наследнике IUndoObject сделать открытый конструктор и деструктор:

class CBadUndoObject : public IUndoObject
{
public:
  CBadUndoObject() { /* some code */ }
  ~CBadUndoObject() { /* some code */ }
};

/* где-то в коде */

{
  pUndoMgr->BeginGroup();

  CBadUndoObject aBadUndoObject( pUndoMgr ); /* тут он за счет конструктора базового анду-объекта регится в анду-манагере */

  pUndoMgr->EndGroup(); /* в конце транзакции создание объекта регистрируется и засовывается в стек анду */

} /* тут 'aBadUndoObject' доблестно погибает, и даже разрегестрируется из анду-манагера, но потроха его остаются в стеке анду */

... где-то дальше в коде

void OnUndo()
{
  pUndoMgr->Undo(); /* и тут мы полезем в стек анду, вытащим указатель на 'aBadUndoObject' и попытаемся вернуть ему его прошлое состояние (в этом примере - состояние несуществования :) ), и на этой попытке и помрем, т.к. обратимся к указателю на мертвый объект*/
}


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

забудьте о том, что тут было анду — проблема такая — "запретить создавать наследников некоего интерфеса на стеке средствами этого самого интерфеса ( или еще как-нибудь, может подмешивать еще какой-то базовый класс, я не знаю... )"
Re: Две возможные стратегии...
От: Erop Россия  
Дата: 05.07.09 12:03
Оценка:
Здравствуйте, pastey, Вы писали:

P>Извините если сумбурно объяснил причины, надеюсь на помощь.



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

1) В корневой класс ваших undo-объектов, добавляешь шаблон метода, который создаёт наследника.
2) В реальности эта штука должна создавать не заявленного наследника, а шаблонного наследника от него.
3) В корневом классе заводишь pure virtual метод, который сторонний код не может перекрыть (например там можно заюзать private типы).


То есть как-то так, примерно:
class CUndoObject {
    struct SecretSeed {};
    template<typename T> class Placeholder : public T { void checkSecretSeed( const SecretSeed& ) {} };
public:
    template<typename T>
    static T* CreateNew() { return new Placeholder<T>;
private:
    virtual void checkSecretSeed( const SecretSeed& ) = 0;
    
};


Вторая стратегия -- проверка в rt.
1) перекрываем у CUndoObject operator new
2) внутри него регим все аллокируемые блоки
3) в конструкторе кастимся по dynamic_cast к void* и смотрим есть ли такой блок в базе.
Если есть -- то вычёркиваем. Если нет -- ругаемся...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: запретить создавать объекты на стеке
От: Erop Россия  
Дата: 05.07.09 12:06
Оценка:
Здравствуйте, Аноним, Вы писали:

А>под винду както так:


Это не решит проблемы топикстартера.
Например, создавать объект, как поле другого объекта, тоже нельзя...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Две возможные стратегии...
От: pastey  
Дата: 05.07.09 12:17
Оценка:
Здравствуйте, Erop, Вы писали:

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


P>>Извините если сумбурно объяснил причины, надеюсь на помощь.



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


E>1) В корневой класс ваших undo-объектов, добавляешь шаблон метода, который создаёт наследника.

E>2) В реальности эта штука должна создавать не заявленного наследника, а шаблонного наследника от него.
E>3) В корневом классе заводишь pure virtual метод, который сторонний код не может перекрыть (например там можно заюзать private типы).


E>То есть как-то так, примерно:
E>class CUndoObject {
E>    struct SecretSeed {};
E>    template<typename T> class Placeholder : public T { void checkSecretSeed( const SecretSeed& ) {} };
E>public:
E>    template<typename T>
E>    static T* CreateNew() { return new Placeholder<T>;
E>private:
E>    virtual void checkSecretSeed( const SecretSeed& ) = 0;
    
E>};


E>Вторая стратегия -- проверка в rt.

E>1) перекрываем у CUndoObject operator new
E>2) внутри него регим все аллокируемые блоки
E>3) в конструкторе кастимся по dynamic_cast к void* и смотрим есть ли такой блок в базе.
E>Если есть -- то вычёркиваем. Если нет -- ругаемся...

Супер — спасибо большое — выглядит очень классно, особенно первое.
Второе тоже ничего, но на ран-тайм откладывать не хочется — пропустим какой-то редко используемы случай и кирдык.
Завтра испытаю — отпишусь, сейчас под рукой нет компилятора.

Еще раз спасибо большое.

И так — ради прикола — на днях посетила мысля, как на плюсах файнал класс сделать — приватим конструктор, открываем CreateNew() и готово — попробуй отнаследуйся; может и баян, но я сам придумал
Re[2]: запретить создавать объекты на стеке
От: pastey  
Дата: 05.07.09 12:23
Оценка:
Здравствуйте, Аноним, Вы писали:

А>под винду както так:

А>
А>class foo
А>{
А>foo()
А>{
А>volatile int test = 0;
А>VirtualQuery(&test, ... &mbi1,...);
А>VirtualQuery(this, ... &mbi2,...);
А>assert (mbi1.AllocationBase!=mbi2. AllocationBase);
А>}
А>};
А>


код должен быть кроссовый, но все равно спасибо — узнал про VirtualQuery — выглядит ужасно, но прикольно

А>а вообще — shared_ptr в руки и все.


shared_ptr тем более проблемы не решит. он мне не запретит создать отдельно объекты на стеке.
запретить дианамически грохать объекты кому-нипопадя кроме манагера я могу — закрыть delete анду-объекту и подружить его с манагером
Re[3]: Две возможные стратегии...
От: Erop Россия  
Дата: 05.07.09 12:42
Оценка:
Здравствуйте, pastey, Вы писали:

P>Завтра испытаю — отпишусь, сейчас под рукой нет компилятора.

У меня, к сожалению, прямо сейчас тоже нет...

P>Еще раз спасибо большое.

Для "спасибо" тут есть кнопки

P>И так — ради прикола — на днях посетила мысля, как на плюсах файнал класс сделать — приватим конструктор, открываем CreateNew() и готово — попробуй отнаследуйся; может и баян, но я сам придумал

Ну ещё можно через деструктор примерно так же читить...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: запретить создавать объекты на стеке
От: Аноним  
Дата: 05.07.09 13:34
Оценка:
P>shared_ptr тем более проблемы не решит. он мне не запретит создать отдельно объекты на стеке.
Нупо крайней мере такая декларация метода
void AddObject(std::shared_ptr<Object> &obj);

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

P>запретить дианамически грохать объекты кому-нипопадя кроме манагера я могу — закрыть delete анду-объекту и подружить его с манагером

дружба — это нехорошо
Re[3]: Вот, добрался до компилятора...
От: Erop Россия  
Дата: 05.07.09 19:58
Оценка: 6 (1) +1
Здравствуйте, pastey, Вы писали:

P>Супер — спасибо большое — выглядит очень классно, особенно первое.

P>Второе тоже ничего, но на ран-тайм откладывать не хочется — пропустим какой-то редко используемы случай и кирдык.
P>Завтра испытаю — отпишусь, сейчас под рукой нет компилятора.

//     Объект, который нельзя создать, иначе чем встроенной в него фабрикой...
class IDynCreatedObject {
public:
    virtual ~IDynCreatedObject() {}
    template<typename TDynCreatedObject> static TDynCreatedObject* CreateNew() 
        { return new objectPlaceholder<TDynCreatedObject>; }
    template<typename TDynCreatedObject> static TDynCreatedObject* CreateCopyOf( const TDynCreatedObject& toClone ) 
        { return new objectPlaceholder<TDynCreatedObject>( toClone ); }
    template<typename TDynCreatedObject> static TDynCreatedObject* CreateNew( TDynCreatedObject*& toInit ) 
        { return toInit = CreateNew<TDynCreatedObject>(); }

private:
    class secretSeed {};
    virtual secretSeed checkSecretSeed( secretSeed ) = 0;
    template<typename TDynCreatedObject>
    struct objectPlaceholder : TDynCreatedObject { 
        objectPlaceholder() {}
        objectPlaceholder( const TDynCreatedObject& other ) : TDynCreatedObject( other ) {}
        
        secretSeed checkSecretSeed( secretSeed ss ) { return ss; }
    };
};

#endif//!DynCreatedObject_h
это VC2008 типа.

пример использования:
struct testDCO : IDynCreatedObject {
//    secretSeed checkSecretSeed( secretSeed ss ) { return ss; }
};

struct testTestDCO {
//    testDCO forbiddenField;
};

void testIDynCreatedObject ()
{
//    delete new testDCO;
//    testDCO autoObj;
    testDCO* obj = testDCO::CreateNew(obj);    //    Тут нет UB, так как возврат из функции -- точка следования!
    delete obj->CreateCopyOf( *obj );
    delete obj;
}
закомментированные строчки не компилируются...

В принципе, возможно, будет удобно сразу скрестить это с этим
Автор: Erop
Дата: 26.04.08
...
Но это уже как приложится и что на самом деле нужно

Пиши в общем чего и в какой форме пригодилось...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: Вот, добрался до компилятора...
От: pastey  
Дата: 06.07.09 17:09
Оценка:
E>Пиши в общем чего и в какой форме пригодилось...

Привет. Я тоже испытал — работает на ура. Буду переxодить на secretSeed
Спасибо еще раз
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.