Обертки над const и не const объктом
От: enji  
Дата: 13.12.11 11:10
Оценка:
Есть объект, он может быть как константным, так и нет. Нужно сделать обертку (через которую будет работать клиентский код) для обоих вариантов.

Пока получилось вот так:
class TDBAddressConst
{
  const byte *buf_;
public:
  TDBAddressConst(const byte* buf);
  bool operator==(TDBAddressConst b) const;
  bool operator!=(TDBAddressConst b) const { return !(*this == b); }
  const byte* buf() const { return buf_; }  
  uint_fast8_t copyTo(void *trg) const;      
  // другие методы, не меняющие исходный объект
};

class TDBAddress : public TDBAddressConst
{
public:
  TDBAddress(byte* buf) : TDBAddressConst(buf) {}  
  byte* buf() const { return const_cast<byte*>(TDBAddressConst::buf()); }
  const TDBAddress& clear() const;  
  // другие методы, модифицирующие исходный объект
};


Не нравится:
— const_cast
— дублирование конструкторов
— две сущности вместо одной

Нельзя ли как-нибудь проще сделать?
Re: Обертки над const и не const объктом
От: alexnekipelov  
Дата: 13.12.11 11:24
Оценка:
Здравствуйте, enji, Вы писали:

E>Есть объект, он может быть как константным, так и нет. Нужно сделать обертку (через которую будет работать клиентский код) для обоих вариантов.


Нафига? Константность, это свойство конкретного объекта, а не класса.
Re[2]: Обертки над const и не const объктом
От: enji  
Дата: 13.12.11 12:17
Оценка:
Здравствуйте, alexnekipelov, Вы писали:

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


E>>Есть объект, он может быть как константным, так и нет. Нужно сделать обертку (через которую будет работать клиентский код) для обоих вариантов.


A>Нафига? Константность, это свойство конкретного объекта, а не класса.


Поясните пожалуйста, как это утверждение связано с обертками?
Re: Обертки над const и не const объктом
От: Chorkov Россия  
Дата: 13.12.11 12:42
Оценка: 4 (1)
Здравствуйте, enji, Вы писали:

E>Есть объект, он может быть как константным, так и нет. Нужно сделать обертку (через которую будет работать клиентский код) для обоих вариантов.


E>Нельзя ли как-нибудь проще сделать?


Можно например:
1)
Пусть константные/не константные методы класса, соотвсетуют константным/не константным методам обертки.
Изготавливать обертку двуия фабриками:
shared_ptr<Foo>       make_foo(byte*);
shared_ptr<const Foo> make_foo(const byte*);

Недостаток: закладываемся на определенную политику владения объектами.

2)
Сделать шабонный класс обертки, параметризуемый "константностью".
  Скрытый текст
template<bool const_> class TDBAddress;

template<typename internalData> 
class TDBAddressBase
{
protected:
   internalData* buf_;
public:
   // общие методы
};

template<> 
class TDBAddress<true> : public TDBAddressBase<const byte>
{
  // декларация константных методов. Возмождно их нет.
};

template<> 
class TDBAddress<false> : public TDBAddressBase<byte> 
               // можно : public TDBAddress<true>, но не желательно. Получим все недостатки твоей реализации.
{
  // декларация не константных методов.
};

// методы имеющие одинаковую реализацию, можно реализовать один раз
template<bool const_> 
TDBAddress<const_>::foo(...)
{ 
...
}

Недостаток: нельзя понизить уровень доступа (с неконстантного на константный),
разве только отдельным методом.
Дублирование конструкторов.
Сущностей стало много.

3)
Сделать шаблонный класс, реализуя некостантные методы только тольк в одной из реализаций случае.
template<typename Stored>
class TDBAddress
{
   Stored* buf_;
   typedef TDBAddress<Stored> Self;
public:
   TDBAddress(Stored*buf) : buf_(buf) {}

   Stored buf() const { ... }
   
   typename boost::enable_if_c< ! boost::is_const<Stored>::value, const Self& >::type
   clear() const // только не-конст. версия
   {   
       // В принципе, можно и прямую ошибку компиляции вместо SFINAE
       // BOOST_STATIC_ASSERT( ! boost::is_const<Stored>::value );
       ...
   } 

   void foo() { ... }
};

template<typename Stored_>
void TDBAddress<const Stored_>::foo()
{
  /// если нужно специализировать реализацию отдельных методов ....
}
Re[3]: Обертки над const и не const объктом
От: alexnekipelov  
Дата: 13.12.11 12:54
Оценка:
Здравствуйте, enji, Вы писали:

E>Поясните пожалуйста, как это утверждение связано с обертками?


А так, что обертки не только лишние сущности, но и ломают систему безопасности типов. Ваш же код:

byte* buf() const { return const_cast<byte*>(TDBAddressConst::buf()); }


Если вам нужен константный и не константный метод buf, то делается два метода, а не две сущности:

class TDBAddressConst
{
  /*const*/ byte *buf_;
public:
  TDBAddressConst(/*const*/ byte* buf);
  bool operator==(TDBAddressConst b) const;
  bool operator!=(TDBAddressConst b) const { return !(*this == b); }
  const byte* buf() const { return buf_; }  // Константный метод
  byte* buf() { return buf_; }              // Не константный метод
  const TDBAddress& clear() /* const */ ;   // Судя по названию, метод должен модифицировать объект,
                                            // поэтому он не может быть константным.

  uint_fast8_t copyTo(void *trg) const;      
};


По аналогии и другие методы, модифицирующие или не модифицирующие объект.
Re[4]: Обертки над const и не const объктом
От: enji  
Дата: 13.12.11 13:07
Оценка:
Здравствуйте, alexnekipelov, Вы писали:

A>Если вам нужен константный и не константный метод buf, то делается два метода, а не две сущности:


A>
A>class TDBAddressConst
A>{
A>  /*const*/ byte *buf_;
A>public:
A>  TDBAddressConst(/*const*/ byte* buf);
A>  bool operator==(TDBAddressConst b) const;
A>  bool operator!=(TDBAddressConst b) const { return !(*this == b); }
A>  const byte* buf() const { return buf_; }  // Константный метод
A>  byte* buf() { return buf_; }              // Не константный метод
A>  const TDBAddress& clear() /* const */ ;   // Судя по названию, метод должен модифицировать объект,
A>                                            // поэтому он не может быть константным.

A>  uint_fast8_t copyTo(void *trg) const;      
A>};
A>


A>По аналогии и другие методы, модифицирующие или не модифицирующие объект.


Да, я думал так сделать. Однако дальше возникает две проблемы:
// 1. const_cast переезжает в другие места и размножается:
const TDBAddress someMethod(const byte *buf) { return TDBAdress(const_cast<byte*>(buf); }

// 2. как запретить вызов неконстантных методов у объекта TDBAddress?
const byte *buf = ...;
TDBAddress  a = someMethod(buf); // присвоили константный объект неконстантному
a.clear(); // !!! ахтунг, модифицировали константный объект
Re: Обертки над const и не const объктом
От: MescalitoPeyot Украина  
Дата: 13.12.11 13:09
Оценка:
Здравствуйте, enji, Вы писали:

E>- const_cast


Это нормально

E>- дублирование конструкторов

E>- две сущности вместо одной

.
— Доктор у меня тут вот хрустит, когда я вот так делаю.
— А зачем вы так делаете?


В смысле, если вам не нравятся две сущности, зачем вы их вводите? Почему не последовать совету автора из соседней ветки и не сделать вот так:

  const byte* buf() const { return buf_; }
  byte* buf() { return buf_; }
Re: Обертки над const и не const объктом
От: uzhas Ниоткуда  
Дата: 13.12.11 13:29
Оценка: 4 (1) +1
Здравствуйте, enji, Вы писали:

E>Нельзя ли как-нибудь проще сделать?

тут несколько вариантов есть и все зависит от специфики:

для устранения дублирования конст\не-конст методов типа buf() я ничего посоветовать не могу, все методы, которые я видел. довольно страшные. лучше руками дублировать в этом языке
кстати, у компилятора никаких ambiguity нет при вызове метода buf() ?
Re[5]: Обертки над const и не const объктом
От: alexnekipelov  
Дата: 13.12.11 13:34
Оценка: -1
Здравствуйте, enji, Вы писали:

E>Да, я думал так сделать. Однако дальше возникает две проблемы:

E>
E>// 1. const_cast переезжает в другие места и размножается:
E>const TDBAddress someMethod(const byte *buf) { return TDBAdress(const_cast<byte*>(buf); }

Это не очень приятно. Легче было бы принимать не константный аргумент. Но если такое не возможно, то конечно же без const_cast никуда. Ну а если при этом существует вероятность, что buf может находится в памяти с пометкой "read only", то я бы предпочел сделать шаблонный класс, как уже предложили, чтобы не напороться на AV.

E>const byte *buf = ...;
E>TDBAddress  a = someMethod(buf); // присвоили константный объект неконстантному
E>a.clear(); // !!! ахтунг, модифицировали константный объект

Изменить код вот так:

[ccode]
const byte *buf = ...;
const TDBAddress &someMethod(const byte *buf) { return TDBAdress(const_cast<byte*>(buf); }
const TDBAddress &a = someMethod(buf);
a.clear(); // нельзя, метод не константный


При этом запретив создание копий и присваивание. Стандарт гарантирует, что такая ссылка будет жить столько, сколько нужно. Если же копирование и присваивание нужно, то см. выше про шаблонный класс.
Re[2]: Обертки над const и не const объктом
От: enji  
Дата: 13.12.11 13:35
Оценка:
Здравствуйте, MescalitoPeyot, Вы писали:

MP>В смысле, если вам не нравятся две сущности, зачем вы их вводите? Почему не последовать совету автора из соседней ветки и не сделать вот так:


MP>
MP>  const byte* buf() const { return buf_; }
MP>  byte* buf() { return buf_; }
MP>


объяснил тут http://rsdn.ru/forum/cpp/4540540.1.aspx
Автор: enji
Дата: 13.12.11
Re[2]: Обертки над const и не const объктом
От: enji  
Дата: 13.12.11 13:48
Оценка:
Здравствуйте, uzhas, Вы писали:

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


E>>Нельзя ли как-нибудь проще сделать?

U>тут несколько вариантов есть и все зависит от специфики:

U>если ваша сущность должна существовать быть поверх рид-онли сырого буфера, то запись туда должнабыть запрещена:

U> 1) либо вы не предоставляете неконстантных методов клиенту (то есть имеем две сущности: Const + NonConst)
собственно мой вариант и есть

U> 2) либо в рантайме рапортуете ошибку: то есть ваш get_buf, который возвращает неконстантный указатель, должен вернуть 0 (либо иным способом сообщить ошибку), этого можно добиться, сделать лишь один класс NonConst и создав дополнительную сущность Buffer, которая и будет в зависимости от константности char* возвращать данные или нули\ошибки)

по возможности хотелось бы иметь компайл-тайм ошибки

U>вы можете попробовать оторвать вашу сущность от сырого буфера и сделать ее мутабельной (внутри не-const мемберы)\немутабельной (внутри const, любая модификация выплевывает новый объект)

U>при этом вам могут понадобиться методы сериализации\десериализации из сырого буфера (ведь он тут неслучайно, как я понимаю)

Задача собственно такая — есть некоторое длинное сообщение, хочется им удобно манипулировать. При этом памяти в притык, сериализации\десериализации, кучи и т.д. надо по возможности избегать. Сообщение имеет фиксированный формат, в нем много разных полей. Некоторые поля имеют переменную длину, некоторые имеют не бинарный формат — наложить сообщение на структуру C не получится.

Вариантов использования два:
1. Клиент попросил создать новое сообщение и получил в ответ обертку для записи
2. Пришло новое сообщение извне, клиент уведомился об этом и получил неизменямую обертку для чтения

В случае 2 уведомляться могут много клиентов, не хотелось бы, что-бы кто-то из них по ошибке перетер сообщение.

U>кстати, у компилятора никаких ambiguity нет при вызове метода buf() ?

Нет, а с чего вдруг? Это просто замещение метода предка.
Re[6]: Обертки над const и не const объктом
От: enji  
Дата: 13.12.11 13:57
Оценка:
Здравствуйте, alexnekipelov, Вы писали:

Копирование нужно. Вариант с шаблонным классом привели выше. Но он смотрится не лучше моего исходного кода, к сожалению
Re[6]: Обертки над const и не const объктом
От: k.o. Россия  
Дата: 13.12.11 14:16
Оценка: +1
Здравствуйте, alexnekipelov, Вы писали:

A>Изменить код вот так:


A>
A>const byte *buf = ...;
A>const TDBAddress &someMethod(const byte *buf) { return TDBAdress(const_cast<byte*>(buf); }
A>const TDBAddress &a = someMethod(buf);
A>a.clear(); // нельзя, метод не константный
A>


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


В данном случае, стандарт такого не гарантирует. Впрочем, даже если бы он это гарантировал, это было бы бесполезно, поскольку при использовании, пожалуй, любого из современных компиляторов объект будет удален при выходе из функции, в чём можно легко убедиться запустив соответствующий тест.
Re[3]: Обертки над const и не const объктом
От: uzhas Ниоткуда  
Дата: 13.12.11 14:29
Оценка:
Здравствуйте, enji, Вы писали:


U>> 1) либо вы не предоставляете неконстантных методов клиенту (то есть имеем две сущности: Const + NonConst)

E>собственно мой вариант и есть
да, только мне, как и вам, не нравится const-cast =) его можно избежать, если держать указатель на буфер в обоих классах

U>> 2) либо в рантайме рапортуете ошибку: то есть ваш get_buf, который возвращает неконстантный указатель, должен вернуть 0 (либо иным способом сообщить ошибку), этого можно добиться, сделать лишь один класс NonConst и создав дополнительную сущность Buffer, которая и будет в зависимости от константности char* возвращать данные или нули\ошибки)

E>по возможности хотелось бы иметь компайл-тайм ошибки
с идеологической точки зрения мне нравится вариант с двумя классами: нет метода — нет проблем
иначе (когда класс один) мы приходим к единой сущности, методы которой должны (имхо) возвращать ошибки\нули

E>Вариантов использования два:

E>1. Клиент попросил создать новое сообщение и получил в ответ обертку для записи
E>2. Пришло новое сообщение извне, клиент уведомился об этом и получил неизменямую обертку для чтения
спасибо, немного яснее стало

E>В случае 2 уведомляться могут много клиентов, не хотелось бы, что-бы кто-то из них по ошибке перетер сообщение.


ну тут опять же либо вы даете ссылку на константный объект (тогда без хаков клиенты не смогут вызвать неконстантные методы), либо даете им лишь константный интерфейс
выбор за вами
1) OnNewMessage(const TDBAddress& newAddress);
2) OnNewMessage(const ConstTDBAddress& newAddress);

выигрыша второго метода именно тут никакого (масло маслянное), он может быть в других случаях, надо понять все ваши текущие и будущие случаи

U>>кстати, у компилятора никаких ambiguity нет при вызове метода buf() ?

E>Нет, а с чего вдруг? Это просто замещение метода предка.
да, спасибо, лично я избегаю сокрытие имен, поэтому немного насторожился)
Re[7]: Обертки над const и не const объктом
От: alexnekipelov  
Дата: 13.12.11 15:19
Оценка: -1
Здравствуйте, k.o., Вы писали:

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


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


Советую запустить таки запустить этот тест и убедится в том, что объект будет жить.

12.2/5 "Temporary objects":

The temporary to which the reference is bound or the temporary that is the complete object to a subobject of which the temporary is bound persists for the lifetime of the reference except as specified below. A temporary bound to a reference member in a constructor’s ctor-initializer (12.6.2) persists until the constructor exits. A temporary bound to a reference parameter in a function call (5.2.2) persists until the completion of the full expression containing the call.

Re[8]: Обертки над const и не const объктом
От: alexnekipelov  
Дата: 13.12.11 15:24
Оценка:
Здравствуйте, alexnekipelov, Вы писали:

A>Советую запустить таки запустить этот тест и убедится в том, что объект будет жить.


запустить таки запустить


Тьфу, все моя торопливость
Re[8]: Обертки над const и не const объктом
От: uzhas Ниоткуда  
Дата: 13.12.11 15:39
Оценка: +1
Здравствуйте, alexnekipelov, Вы писали:

A>

A>12.2/5 "Temporary objects":

A>The temporary to which the reference is bound or the temporary that is the complete object to a subobject of which the temporary is bound persists for the lifetime of the reference except as specified below. A temporary bound to a reference member in a constructor’s ctor-initializer (12.6.2) persists until the constructor exits. A temporary bound to a reference parameter in a function call (5.2.2) persists until the completion of the full expression containing the call.


вы неправильно понимаете фразу "the temporary to which the reference is bound"

вот пример1 (нерабочий, потому что объект используют после его смерти, даже ворнинг компилятора есть):
http://ideone.com/74mer

вот пример2, где действительно константная ссылка bound к временному объекту и тем самым, согласно процитированному вами стандарту, продлевает жизнь объекта
http://ideone.com/z7sek
Re[9]: Обертки над const и не const объктом
От: alexnekipelov  
Дата: 13.12.11 15:46
Оценка:
Здравствуйте, uzhas, Вы писали:

U>вот пример1 (нерабочий, потому что объект используют после его смерти, даже ворнинг компилятора есть):

U>http://ideone.com/74mer

U>вот пример2, где действительно константная ссылка bound к временному объекту и тем самым, согласно процитированному вами стандарту, продлевает жизнь объекта

U>http://ideone.com/z7sek

Тьфу, я ведь сам написал, что ссылку возвращаю. Правильно, позор мне
Re[2]: Обертки над const и не const объктом
От: rg45 СССР  
Дата: 13.12.11 16:10
Оценка: +1
Здравствуйте, alexnekipelov, Вы писали:

E>>Есть объект, он может быть как константным, так и нет. Нужно сделать обертку (через которую будет работать клиентский код) для обоих вариантов.


A>Нафига? Константность, это свойство конкретного объекта, а не класса.


Тем не менее, итераторы контейнеров стандартной библиотеки, как правило, представлены двумя типами: iterator и const_iterator. Нафига?
--
Re: Обертки над const и не const объктом
От: rg45 СССР  
Дата: 13.12.11 16:50
Оценка:
Здравствуйте, enji, Вы писали:

E>Есть объект, он может быть как константным, так и нет. Нужно сделать обертку (через которую будет работать клиентский код) для обоих вариантов.


E>Пока получилось вот так:

E>...

E>Не нравится:

E>- const_cast
E>- дублирование конструкторов
E>- две сущности вместо одной

Думаю, две сущности вместо одной — семантически вполне оправданное решение в данном случае. Дублирование конструкторов, тоже беда не большая. А вот const_cast — действительно некрасиво, я бы сделал как-то так:
class TDBAddressConst
{
public:
  const byte* buf() const { return buf_; }

protected:
  byte *buf_;
};

class TDBAddress : public TDBAddressConst
{
public:
  byte* buf() const { return buf_; }
};
--
Re[2]: Обертки над const и не const объктом
От: night beast СССР  
Дата: 13.12.11 17:20
Оценка: +1
Здравствуйте, rg45, Вы писали:

R>Думаю, две сущности вместо одной — семантически вполне оправданное решение в данном случае. Дублирование конструкторов, тоже беда не большая. А вот const_cast — действительно некрасиво, я бы сделал как-то так:


не подойдет. у него конструктор постоянную строку принимает...
думаю самое простое решение -- передавать type_trait шаблонным параметром.
Re: Обертки над const и не const объктом
От: cppnick  
Дата: 13.12.11 19:55
Оценка:
Здравствуйте, enji, Вы писали:

E>Нельзя ли как-нибудь проще сделать?

Как вариант, можно сделать константность шаблонным параметром. Тем не менее, в Boost.Asio схожие концепты реализованы полностью раздельно.
Re[2]: Обертки над const и не const объктом
От: Кодт Россия  
Дата: 14.12.11 10:48
Оценка: 6 (2)
Здравствуйте, Chorkov, Вы писали:

C>Пусть константные/не константные методы класса, соотвсетуют константным/не константным методам обертки.

C>Изготавливать обертку двуия фабриками:
C>
C>shared_ptr<Foo>       make_foo(byte*);
C>shared_ptr<const Foo> make_foo(const byte*);
C>

C>Недостаток: закладываемся на определенную политику владения объектами.

Можем сделать другую политику
template<class T> class itself_ptr
{
  mutable T data_;
public:
  explicit itself_ptr(const T& src) : data_(src) {}

  T* operator->() const { return const_cast<T*>(&data_); }
  T& operator* () const { return const_cast<T&>( data_); }
};

здесь mutable и const_cast рассчитаны на то, что T — это обёртка, которую можно безболезненно копировать, как угодно.

Выбирая между itself_ptr<Wrapper> и itself_ptr<const Wrapper>, получаем желаемое.
Перекуём баги на фичи!
Re[3]: Обертки над const и не const объктом
От: uzhas Ниоткуда  
Дата: 14.12.11 11:52
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Можем сделать другую политику

имхо и без const_cast все шоколадно
http://ideone.com/5MnRf
http://ideone.com/xfenR
Re[4]: Обертки над const и не const объктом
От: Кодт Россия  
Дата: 14.12.11 12:27
Оценка:
Здравствуйте, uzhas, Вы писали:

К>>Можем сделать другую политику

U>имхо и без const_cast все шоколадно

да это я сперва наконсткастил, а потом про мутабле вспомнил
Перекуём баги на фичи!
Re: Резюме
От: enji  
Дата: 15.12.11 07:14
Оценка:
В общем, как я понял, самое прямое решение из предложенных вот такое:

template<class T> class itself_ptr
{
  mutable T data_;
public:
  explicit itself_ptr(const T& src) : data_(src) {}
 
  T* operator->() const { return &data_; }
  T& operator* () const { return data_; }
};

namespace detail {

struct TDBAddressImpl
{
  static int long_getter(const byte *buf);
  static void long_setter(byte *buf, double v);
};

}

template<class T>
class TDBAddress_
{
  T *buf_;
public:
  TDBAddress_(T *buf) : buf_(buf) {}
  
  byte getter() const { return buf_[105]; }
  void setter(byte v) { buf_[106] = v; }
  
  int long_getter() const { return detail::TDBAddressImpl::long_getter(buf_); }
};

typedef itself_ptr<TDBAddress_<byte> > TDBAddress;
typedef itself_ptr<TDBAddress_<const byte> > TDBAddressConst;

inline TDBAddress make_dbaddress(byte *buf) { return TDBAddress(TDBAddress_<byte>(buf)); }
inline TDBAddressConst make_dbaddress(const byte *buf) { return TDBAddressConst(TDBAddress_<const byte>(buf)); }


Его плюсы — нет const_cast и дублирования методов.
Его минусы
— нужны дополнительные заморочки, чтобы можно было преобразовать TDBAddress в TDBAddressConst
— itself_ptr можно вынести в библиотеку (если таких оберток будет много), но все равно много мусора — тайпдефы, make_dbaddress, отдельный struct для тяжелых функций, реализацию которых не охота светить в хидере, переадресация вызовов для таких функций.

ИМХО, исходное решение, хотя и не слишком красиво смотрится, вполне имеет право на жизнь
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.