Снова конструкторы и POD-типы
От: McSeem2 США http://www.antigrain.com
Дата: 25.07.05 23:48
Оценка: 1 (1)
Понимаю, что вопрос уже навяз в зубах, но поиск ничего конкретного не дал.
Вот полное описание объекта:
struct point
{
   int x,y;
   point() {}
   point(int x_, int y_) : x(x_), y(y_) {}
};


Вопрос первый — является ли данный объект POD-типом? Согласно стандарту — не является, поскольку имеет явные конструкторы.
Вопрос второй — допустимо ли копировать данный объект целиком при помощи memcpy (хранить его в специализированном контейнере для POD-типов)?
Вопрос третий. Если не допустимо, то каково обоснование этому? Класс не содержит и не может содержать никаких неявных данных, типа VMT. Так же не имеет права иметь явный деструктор. Он не может наследоваться от чего-либо и не может иметь производных классов. Имеется так же запрет на какие-либо исключения. В нем даже нет никаких указателей — только базовые типы (это все — начальные условия). И только лишь от наличия одного конструктора, имеющего чисто утилитарное назначение (как синтаксический сахар) объект нельзя копировать при помощи memcpy? Это же маразм!
Вопрос четвертый. Где-либо, когда-либо, кто-либо сталкивался с реальными проблемами на практике, при "безбашенном" копировании подобных объектов при помощи memcpy (или memmove)? По-моему, таких проблем никогда не было и не будет.
Вопрос пятый — как следствие из четвертого. Возможна ли в принципе такая ситуация (при оговоренных выше ограничениях), что добавление простого конструктора изменит sizeof объекта?

То есть, я не вижу ни малейшего rationale подобных запретов. Да, как только в конструкторе аллокируется какая-либо память (а в деструкторе освобождается), объект сразу перестает быть POD-типом. Это совершенно логично и не вызывает ни малейших возражений. Но просто запрет каких бы то ни было конструкторов — это уже какой-то параноидальный объектно-ориентированный шовинизм
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re: Снова конструкторы и POD-типы
От: Павел Кузнецов  
Дата: 26.07.05 00:58
Оценка: 3 (1)
McSeem2,

>
> struct point
> {
>    int x,y;
>    point() {}
>    point(int x_, int y_) : x(x_), y(y_) {}
> };
>

> Вопрос первый — является ли данный объект POD-типом? Согласно стандарту — не является, поскольку имеет явные конструкторы.

Да, не является.

> Вопрос второй — допустимо ли копировать данный объект целиком при помощи memcpy (хранить его в специализированном контейнере для POD-типов)?


С точки зрения стандарта — нет.

> Вопрос третий. Если не допустимо, то каково обоснование этому?


Как это ни забавно, экономическое: ресурсы комитета очень ограничены. Если бы стандарт говорил, что такой класс можно копировать с помощью memcpy, то в стандарте нужно было бы указать: 1) что такое "такой класс"; 2) что именно произойдет, если его скопировать при помощи memcpy. Уже даже ответы на эти вопросы не так тривиальны, как может показаться на первый взгляд. Дальше — хуже. Фактически, нужно вводить особую разновидность объектов для поддержания memcpy для них. Когда начинается и заканчивается lifetime такого объекта? Определение POD не подходит, определение "обычных" объектов с нетривиальным конструктором — тоже. И т.п.

Если хочешь поддержки стандартом, пользуйся чуть-чуть другой стратегией создания объектов:
struct point
{
    int x,y;
};

inline
point make_point(int x_, int y_) { point pt = { x_, y_}; return pt; }

И стандарт будет счастлив, и твоя совесть спокойна

> <...> только лишь от наличия одного конструктора, имеющего чисто утилитарное назначение (как синтаксический сахар) объект нельзя копировать при помощи memcpy? Это же маразм!


Проблема в выработке точной спецификации, что такое "конструктор, имеющий чисто утилитарное назначение". А учитывая наличие простого workaround, игра просто-напросто не стоит свеч.

> Вопрос четвертый. Где-либо, когда-либо, кто-либо сталкивался с реальными проблемами на практике, при "безбашенном" копировании подобных объектов при помощи memcpy (или memmove)? По-моему, таких проблем никогда не было и не будет.


Они могут появиться, если, скажем, в отладочной версии, компилятор добавит проверки, что объект был сконструирован, прежде чем разрешать им пользоваться, если класс имеет нетривиальный конструктор.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[2]: Снова конструкторы и POD-типы
От: Павел Кузнецов  
Дата: 26.07.05 01:17
Оценка:
P.S.

> Как это ни забавно, экономическое: ресурсы комитета очень ограничены. <...>


На самом деле, все еще проще. Если ты считаешь, что эта функциональность нужна, ты можешь выработать formal proposal, включая точные изменения в тексте стандарта, и комитетчики обязаны будут его рассмотреть. Вопрос: тебе оно настолько нужно?
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[2]: Снова конструкторы и POD-типы
От: McSeem2 США http://www.antigrain.com
Дата: 26.07.05 01:25
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>struct point
ПК>{
ПК>    int x,y;
ПК>};

ПК>inline
ПК>point make_point(int x_, int y_) { point pt = { x_, y_}; return pt; }

ПК>И стандарт будет счастлив, и твоя совесть спокойна

Очень не хочется возвращать значение... Это у меня такая фобия

А так можно?
struct point
{
    int x,y;
    static point make(int x_, int y_) { point pt = { x_, y_}; return pt; }
};



А так?
struct point
{
    int x,y;
    point clone(int x_, int y_) { point pt; memcpy(pt, this, sizeof(pt)); return pt; }
};


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

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


Хм. А где-нибудь такой отладчик в реальности существует? В общем, я пока что рискну считать, что при данных условиях копировать можно. Есть в этом некий риск, но IMO, он на практике стремится к нулю.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re[3]: Снова конструкторы и POD-типы
От: Шахтер Интернет  
Дата: 26.07.05 01:39
Оценка:
Здравствуйте, McSeem2, Вы писали:

Можно так делать

struct Point
 {
  int x,y;
 };
 
struct SetPoint : Point
 {
  SetPoint() : Point() {}
  
  SetPoint(int x_,int y_)
   {
    x=x_;
    y=y_;
   }
 };

/* main() */ 

int main()
 {
  Point p=SetPoint();
  Point q=SetPoint(1,2);
 
  return 0;
 }
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[2]: Снова конструкторы и POD-типы
От: Шахтер Интернет  
Дата: 26.07.05 01:45
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>Как это ни забавно, экономическое: ресурсы комитета очень ограничены.


А эти экономические причины не помешали включить STL в стандарт языка?
Или это тоже делалось по экономическим причинам (только другого сорта)?

Мне, например, непонятно, почему ряд простейших вопросов (типа itoa), комитет урегулировать не может и эти мелочи годами отравляют жизнь.
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[3]: Снова конструкторы и POD-типы
От: McSeem2 США http://www.antigrain.com
Дата: 26.07.05 01:48
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>На самом деле, все еще проще. Если ты считаешь, что эта функциональность нужна, ты можешь выработать formal proposal, включая точные изменения в тексте стандарта, и комитетчики обязаны будут его рассмотреть. Вопрос: тебе оно настолько нужно?


В данном случае, весь formal proposal заключается в том, чтобы не доводить до абсурда. И просто исключить наличие конструктора из формальных признаков не-POD типа. В самом деле — в структуре может быть некий указатель, при этом она остается POD. Наличие простых (невиртуальных) функций-членов допустимо в POD (или я ошибаюсь?). Но я могу явно написать my_ctor() и my_dtor(), которые буду вызывать вручную. И если они будут аллокировать и освобождать память, то структура сразу перестает быть POD, хотя формально она так ей и остается. С этой точки зрения, конструктор ничем не отличается от функции-члена и не может являться никаким формальным признаком. А вот деструктор — уже может. Ибо в POD-структуре по определению никогда ничего не требуется уничтожать. А вот инициализировать при создании — требуется.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re[4]: Снова конструкторы и POD-типы
От: McSeem2 США http://www.antigrain.com
Дата: 26.07.05 01:54
Оценка:
Здравствуйте, Шахтер, Вы писали:
Ш>Можно так делать
Ш>[ccode]
Ш>struct Point
Ш> {
Ш> int x,y;
Ш> };

Ш>struct SetPoint : Point

Ш> {
Ш> SetPoint() : Point() {}

Ш> SetPoint(int x_,int y_)

Ш> {
Ш> x=x_;
Ш> y=y_;
Ш> }
Ш> };

Это тоже не фонтан из за необходимости использовать явные присвоения заместо списка инициализации. Кстати, явный оператор присвоения в POD-типах разрешен?
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re[3]: Снова конструкторы и POD-типы
От: Павел Кузнецов  
Дата: 26.07.05 02:24
Оценка:
Шахтер,

ПК>> Как это ни забавно, экономическое: ресурсы комитета очень ограничены.


Ш> А эти экономические причины не помешали включить STL в стандарт языка?


Как видишь, не помешали

Ш> Мне, например, непонятно, почему ряд простейших вопросов (типа itoa),

Ш> комитет урегулировать не может и эти мелочи годами отравляют жизнь.

Дык, все просто: volunteer. Только данная функция относится к стандарту C,
соответственно, нужно участвовать в работе комитета по стандартизации C.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[4]: Снова конструкторы и POD-типы
От: Павел Кузнецов  
Дата: 26.07.05 02:42
Оценка: 1 (1)
McSeem2,

M> В данном случае, весь formal proposal заключается в том, чтобы не

M> доводить до абсурда. И просто исключить наличие конструктора из
M> формальных признаков не-POD типа.

Так работать не будет. Конструктор по умолчанию включен в определение агрегатных
типов. POD-типы это свойство "наследуют" от агрегатных. Если к агрегатным типам
причислить типы, имеющие конструкторы по умолчанию, то "поплывут" многие места в
стандарте. Если изменить определение POD-типов, тоже много чего "поплывет". Нужно
очень аккуратно проходиться по тексту всего стандарта.

В общем, хочешь что-то в этом изменить -- volunteer.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[3]: Снова конструкторы и POD-типы
От: Павел Кузнецов  
Дата: 26.07.05 02:56
Оценка:
McSeem2,

M> Но вообще-то говоря, мне хочется иметь некий тип, свободно копируемый

M> при помощи memcpy

А зачем? Чем std::copy не устраивает?

M> чисто сишная структура с инициализацией


Тут два противоречивых положения: сишные структуры не имеют "автоматической"
инициализации по определению.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re: Снова конструкторы и POD-типы
От: Павел Кузнецов  
Дата: 26.07.05 03:03
Оценка:
McSeem2,

M> То есть, я не вижу ни малейшего rationale подобных запретов.


См. тему с участием комитетчиков, там они прямым текстом дают rationale:
http://groups-beta.google.com/group/comp.std.c++/browse_frm/thread/56961680e16b8bdf
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[4]: Снова конструкторы и POD-типы
От: McSeem2 США http://www.antigrain.com
Дата: 26.07.05 04:02
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>А зачем? Чем std::copy не устраивает?


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

M>> чисто сишная структура с инициализацией


ПК>Тут два противоречивых положения: сишные структуры не имеют "автоматической"

ПК>инициализации по определению.

Ну хорошо, пусть они не будут POD. Тогда можно ввести понятие "bitwise copyable". То есть, такой объект, который 100%-тно сохраняет валидность при побитовом копировании. Тем более, что на практике, так оно и есть для объектов, описанных мной в начальном сообщении. Надо это всего лишь узаконить.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re[2]: Снова конструкторы и POD-типы
От: McSeem2 США http://www.antigrain.com
Дата: 26.07.05 04:12
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>См. тему с участием комитетчиков, там они прямым текстом дают rationale:

ПК>http://groups-beta.google.com/group/comp.std.c++/browse_frm/thread/56961680e16b8bdf

Это все понятно, но это никак не объясняет, почему подобные объекты (POD с конструктором и принципиально прзиционно независимые) нельзя копировать напрямую.
Еще раз. Пусть они не будут POD, и их нельзя инициализировать через "{}". Но пусть они будут bitwise copyable, причем официально. Тем более, что на практике они так copyable.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re[5]: Снова конструкторы и POD-типы
От: CrystaX Россия https://crystax.me/
Дата: 26.07.05 05:17
Оценка:
Здравствуйте, McSeem2, Вы писали:

MS>А она умеет яростно копировать большие массивы, так же эффективно, как и memcpy?

MS>Что-то мне подсказывает, что во многих случаях будут мощные накладные расходы, особенно, если структура содержит множество других структур. Да, в определенных частных случаях скорость та же, но полагаться на это нельзя — в других случаях (другая платформа, другой компилятор) — разница в разы. А мне хочется именно гарантии и стабильности.

Если речь идет о STLport — то умеет. Там есть copy helper, принимающий доп. параметр. В стандартной copy вызывается copy helper и передается ему кроме прочих параметров is_pod_type. Выбор происходит, естественно, в compile-time.
... << RSDN@Home 1.1.4 stable rev. 510>>
Re[6]: Снова конструкторы и POD-типы
От: McSeem2 США http://www.antigrain.com
Дата: 26.07.05 05:29
Оценка:
Здравствуйте, CrystaX, Вы писали:

CX>Если речь идет о STLport — то умеет. Там есть copy helper, принимающий доп. параметр. В стандартной copy вызывается copy helper и передается ему кроме прочих параметров is_pod_type. Выбор происходит, естественно, в compile-time.


Спасибо, но на это не подходит. Я не имею права полагаться на какую-либо конкретную имплементацию. Можно полагаться лишь на просто стандартизованную STL, а какая конкретно будет использоваться — мне не ведомо. А memcpy есть везде.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re[5]: Снова конструкторы и POD-типы
От: Bell Россия  
Дата: 26.07.05 06:54
Оценка: +1
Здравствуйте, McSeem2, Вы писали:

MS>Здравствуйте, Павел Кузнецов, Вы писали:


ПК>>А зачем? Чем std::copy не устраивает?


MS>А она умеет яростно копировать большие массивы, так же эффективно, как и memcpy?

MS>Что-то мне подсказывает, что во многих случаях будут мощные накладные расходы, особенно, если структура содержит множество других структур. Да, в определенных частных случаях скорость та же, но полагаться на это нельзя — в других случаях (другая платформа, другой компилятор) — разница в разы. А мне хочется именно гарантии и стабильности.

Совсем не факт, что использование memcpy решит все твои проблемы.
Посмотри например здесь
Автор: Анатолий Широков
Дата: 05.06.03
и здесь
Любите книгу — источник знаний (с) М.Горький
Re[5]: Снова конструкторы и POD-типы
От: Анатолий Широков СССР  
Дата: 26.07.05 07:33
Оценка:
Для таких примитивных типов можно использовать и подобного рода инициализацию:

struct point
{
   int x, y;
};

...
point pt = {0, 0};


И наглядно и эффективно.
Re[5]: Снова конструкторы и POD-типы
От: Cyberax Марс  
Дата: 26.07.05 10:39
Оценка: 11 (2)
McSeem2 wrote:

> ПК>А зачем? Чем std::copy не устраивает?

> А она умеет яростно копировать большие массивы, так же эффективно, как
> и memcpy?

Делай спецификацию для нужных типов, чтобы она копировала с помощью memcpy.

Вот кусок из моей библиотеки контейнеров (будет время — напишу о ней
подробнее):
///////////////////////////////////////////////////////////////////////////////////////
////// Move traits
///////////////////////////////////////////////////////////////////////////////////////
//General fall-back implementation of move using copy-constructors.
template<class T> struct safe_move_traits
{
    static void destroy_range(T* _first, T* _last)
    {
        for (T* f=_first; f!=_last; f++)
            f->~T();
    }

    static void move(T* _first, T* _last, void *_dest)
    {
        copy(_first,_last,_dest);
        destroy_range(_first,_last);
    }

    static void copy(const T* _first, const T* _last, void *_dest)
    {
        T* tdest = static_cast<T*>(_dest);
        std::uninitialized_copy(_first,_last,_dest);
    }
};

//Fast memmove-backed mover
template<class T> struct memmove_traits
{
    enum{bitwise_moveable=1};

    static void destroy_range(T* _first, T* _last)
    {
        for (T* f=_first; f!=_last; f++)
            f->~T();
    }

    static void move(T* _first, T* _last, void *_dest)
    {
        copy(_first,_last,_dest);
        destroy_range(_first,_last);
    }

    static void copy(const T* _first, const T* _last, void *_dest)
    {
        memmove(_dest,_first,(_last-_first)*sizeof(T));
    }
};

///////////////////////////////////////////////////////////////////////////////////////
////// Automatic moveфв selectors
///////////////////////////////////////////////////////////////////////////////////////

template <class T> struct fast_move_flag
{
    enum{can_move=0};
};

#define ENABLE_BITWISE_MOVE(clazz) template<> struct 
fast_move_flag<clazz>{enum{can_move=1};};

//All non-classes or types with fast_move_flag!=0 can be moved using 
memmove.
template <class T> struct move_traits:
    public boost::mpl::if_c<boost::is_class<T>::value == 0 || 
fast_move_flag<T>::can_move,
        memmove_traits<T>, safe_move_traits<T>
    >::type
{
};

В этом коде можно многое улучшить, но идея должна быть понятна.

Более того, если у тебя есть свой класс, который может быть "почти
скопирован" с помощью memcpy (допустим, требуются небольшие изменения) —
делаешь спецификацию шаблона move_traits и наслаждаешься.

--
С уважением,
Alex Besogonov (alexy@izh.com)
Posted via RSDN NNTP Server 1.9
Sapienti sat!
Re[6]: Снова конструкторы и POD-типы
От: McSeem2 США http://www.antigrain.com
Дата: 26.07.05 14:14
Оценка:
Здравствуйте, Bell, Вы писали:

B>Совсем не факт, что использование memcpy решит все твои проблемы.

B>Посмотри например здесь
Автор: Анатолий Широков
Дата: 05.06.03
и здесь


Спасибо за ссылки. Во-первых, насчет производительности. Я знаю, что можно копировать быстрее, чем memcpy. Даже простой цикл оптимизатор может векторизовать и задействовать 64-битовые операции MMX. Но здесь нет никаких гарантий. Пусть (даже в большинстве случаев) memcpy работает медленнее на несколько процентов. Но она обладает очень важным свойством — предсказуемостью производительности.

Хорошо, раскрываю карты. Дело даже не в производительности. Как мне при поможи std::copy() корректно "вытащить" данные из памяти, находящиеся по невыровненному адресу? По-моему никак. На всяких SGI сразу получаем автобусную ошибку. Для чего это надо? А вот для чего. У нас есть линейный упакованный поток данных в памяти:
"char cmd; int x; int y".
Этих данных может быть многие мегабайты. Если мы представим их в виде структуры:
struct point
{
   char cmd;
   int x,y;
};

Мы неизбежно получаем полуторный перерасход по памяти из за выравнивания. Если же мы отделим cmd от x,y, то получаем упакованные и невыровненные данные. "Легализовать" невыровненные данные можно только простым побитовым копированием. Разделить на два потока в данном случае можно, но в реальности, структура этого потока значительно сложнее — что-то типа бинарного XML. И там иногда попадаются достаточно сложные структуры. Конечно же, в этих структурах могут быть и "дырки" от выравнивания, но их процент исчезающе мал, основная масса данных является упакованной. Сложные структуры всегда восстанавливаются корректно, поскольку были записаны ровно тем же путем. Получается, что C++ в чистом виде не справляется с решением подобной задачи. И только лишь из за того, что запретили побитовое копирование объектов, имеющих конструктор. Классическое решение — сделать "нормальные" классы с методами "serialize/deserialize". Но во-первых, от этого появляется много мусорного кода (внутри мне все равно придется использовать побайтное копирование), а во-вторых, это не всегда возможно. Я все-таки склонен взять на себя риск нарушить этот запрет.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.