LINUX.ORG.RU

[знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...


0

0

... что:

class A {int a;};
class B: public A {int b;};
B object;

Странный вопрос? А вот я у Страуструпа (вроде) читал, что реализация класса В, выбранная разработчиком компилятора, может быть вовсе не обязательно struct B1 { int a; int b; };

Может быть выбрана такая реализация: struct __B2 {int b;}; struct B2 { int a; __B2* __b2; } и для этой реализации write запишет на диск адрес __b2, а потом, при следующем запуске программы, read прочтет уже недействительный адрес в поле __b2.

Где в стандарте написано что-то на эту тему?

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

>struct __B2 {int b;}; struct B2 { int a; __B2* __b2; }

Полнейшая чушь, компилятор не имеет права выбирать такую реализацию. Оба класса - POD-типы. write/read законны и правильны. В стандарте лазить лень.

JackYF ★★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

s/B object;/B obj;/

www_linux_org_ru ★★★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

Раздел 3.9 [basic.types], пункт 2.

Там сказано, что POD-типы ("plain old data") можно так копировать, в массив char [sizeof(T)] и обратно. Про не-POD стандарт, скорее всего, просто не даёт никаких гарантий (то есть если прошерстить весь стандарт от корки до корки, то ничего найти про это не удастся).

anonymous ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

> Где в стандарте написано что-то на эту тему?

1.8.5 An object of POD type (basic.types) shall occupy contiguous bytes of storage.

Но вопрос хороший. Если полиморфный класс может занимать не неразрывный кусок памяти, то как там рассчитывается sizeof? В общем, it ruines my world.

dilmah ★★★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

Although it is sometimes thought that "Plain Old Data" is only an informal term that should be avoided in general communications, a POD notion is defined in ISO/IEC standard 14882 for the C++ programming language. A POD type in the standard is either a scalar type or a POD class. POD class has no user-defined copy assignment operator, no user-defined destructor, and no non-static data members that are not themselves PODs. Moreover, POD class must be an aggregate, meaning it has no user-declared constructors, no private nor protected non-static data, no bases and ... <-------- базы у меня есть.

www_linux_org_ru ★★★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

> В общем, it ruines my world.

Потому и спрашиваю. Похоже, это не POD!

www_linux_org_ru ★★★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

>> Где в стандарте написано что-то на эту тему?

>>1.8.5 An object of POD type (basic.types) shall occupy contiguous bytes of storage.

>Но вопрос хороший. Если полиморфный класс может занимать не неразрывный кусок памяти, то как там рассчитывается sizeof? В общем, it ruines my world.

2 my mind такой реализации не будет никогда.

Absurd ★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

> Полнейшая чушь, компилятор не имеет права выбирать такую реализацию. Оба класса - POD-типы. write/read законны и правильны. В стандарте лазить лень.

Нет, class B - не POD, потому что у него есть базовый класс. Согласно 8.5.1 [dcl.init.aggr], такие классы не являются aggregate. По определению в 9 [class]: "A POD-struct is an aggregate class". Не aggregate - значит не POD. Поэтому пример в исходном посте правильный.

anonymous ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

> 2 my mind такой реализации не будет никогда.

ну vtable таки лежит в другом месте. Просто это не влияет на состояние объекта.

dilmah ★★★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

> Если полиморфный класс может занимать не неразрывный кусок памяти, то как там рассчитывается sizeof? В общем, it ruines my world.

А зачем тебе его sizeof? В стандарте определены какие-то смутные правила на этот счёт (5.3.3 [expr.sizeof), но из них можно только сделать вывод, что sizeof в этом случае практически бесполезен.

anonymous ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

> Если полиморфный класс может занимать не неразрывный кусок памяти, то как там рассчитывается sizeof? В общем, it ruines my world.

А зачем тебе его sizeof? В стандарте определены какие-то смутные правила на этот счёт (5.3.3 [expr.sizeof), но из них можно только сделать вывод, что sizeof в этом случае практически бесполезен.

anonymous ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

>> 2 my mind такой реализации не будет никогда.

>ну vtable таки лежит в другом месте. Просто это не влияет на состояние объекта.

Ну и как тогда будет выглядеть скомпилированный код - при апкастинге пробегать всю иерархию до верха? Куча кода написана с расчетом на то при что одиночном наследовании адрес начала базового объекта совпадает с адресом начала производного объекта и static_cast<> не делает ничего кроме локального отключения контроля типов.

Absurd ★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

> 2 my mind такой реализации не будет никогда.

(почти) такая реализация имела бы свои удобства.

www_linux_org_ru ★★★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

> static_cast<> не делает ничего кроме локального отключения контроля типов.

если базовых классов несколько, то static_cast сдвигает указатель..

dilmah ★★★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

самое близкое:

3.9.4 The object representation of an object of type T is the sequence of N unsigned char objects taken up by the object of type T, where N equals sizeof(T). The value representation of an object is the set of bits that hold the value of type T.

dilmah ★★★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

> Куча кода написана с расчетом на то при что одиночном наследовании адрес начала базового объекта совпадает с адресом начала производного объекта

Что-то не могу придумать пример, как написать так, чтобы при "нелогичной" реализации одиночного наследования код ломался.

anonymous ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

> Ну и как тогда будет выглядеть скомпилированный код - при апкастинге пробегать всю иерархию до верха?

Не нужно. (хотя вопрос не про это)

www_linux_org_ru ★★★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

> 3.9.4 The object representation of an object of type T

Это просто определения. Из этого же пункта видно, что value representation полностью содержится в object representation только для POD-типов. Видимо, подразумевается, что для не-под value representation может быть разбросано по разным местам.

anonymous ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

>> static_cast<> не делает ничего кроме локального отключения контроля типов.

>если базовых классов несколько, то static_cast сдвигает указатель..

В Loki::Tuple множественное наследование применяется для генерации структуры из списка типов - структура рекурсивно наследуется по одному разу от каждого Holder<тип> в списке типов. Александреску утверждает что элементы в таких структурах располагаются в пямяти последовательно. Я уверен что это уже перекочевало в буст и ломать это дело никто не будет.

Absurd ★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

>> Куча кода написана с расчетом на то при что одиночном наследовании адрес начала базового объекта совпадает с адресом начала производного объекта

>Что-то не могу придумать пример, как написать так, чтобы при "нелогичной" реализации одиночного наследования код ломался.

Derived*->void*->Base*

Absurd ★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

Пройдя по пунктам стандарта, указанным анонимусом, я вроде понял...

An aggregate is an array or a class (clause 9) with no user-declared constructors (12.1), no private or protected non-static data members (clause 11), no base classes (clause 10), and no virtual functions (10.3).

Так что В -- не агрегат. Дальше имеем:

A POD class is a class that is either a POD-struct or a POD-union.

POD-union отпадает. Остается POD-struct:

A POD-struct is an aggregate class that has no non-static data members of type non-POD-struct, non-POD-union (or array of such types) or reference, and has no user-defined copy assignment operator and no user-defined destructor.

Так что В -- не POD class.

www_linux_org_ru ★★★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

> Derived*->void*->Base*

ССЗБ

www_linux_org_ru ★★★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

> Если полиморфный класс может занимать не неразрывный кусок памяти, то как там рассчитывается sizeof?

Похоже что не-полиморфный класс может занимать не неразрывный кусок памяти.

www_linux_org_ru ★★★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

>> Derived*->void*->Base*

>ССЗБ

Любая интеракция с Си-кодом содержит такие конверсии.

Наример, при создании треда функции-точке входа передается указатель на инстанс класса Thread и она делает static_cast<Thread*>(handback)->run().

Absurd ★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

run_thread(static_cast<Thread*>(thread));

void run_thread(void* obj) {
    Thread *thread = reinterpret_cast<Thread*>(obj);
    thread->run();
}

Какие тут проблемы?

Legioner ★★★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

>> Что-то не могу придумать пример, как написать так, чтобы при "нелогичной" реализации одиночного наследования код ломался. > > Derived*->void*->Base*

Нет, это просто стандартная ошибка при использовании C++, которая имеет тенденцию ускользать на большинстве реализаций, когда наследование не множественное, и немедленно вылезает повреждениями памяти, как только добавляется ещё один базовый класс. Сам на такие грабли наступал и на gcc, и на Microsoft Visual C++.

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

anonymous ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

> Derived*->void*->Base*

Такая ошибка даже называется своим именем... сорри, забыл как.

www_linux_org_ru ★★★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

> run_thread(static_cast<Thread*>(thread));
>

> void run_thread(void* obj) {

> Thread *thread = reinterpret_cast<Thread*>(obj);

> thread->run();

> }

>

> Какие тут проблемы?


я бы как раз использовал static_cast оба раза, потому что про это в стандарте явно
написно, что будет работать (5.2.9, пункт 10: "A value of type pointer to object converted to
“pointer to cv void” and back to the original pointer type will have its original value.")

а вот если сначала reinterpret_, а потом static_, то это уже как-то сложновато смотрится.
reinterpret_cast по семантике вообще тут неуместно.

anonymous ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

> Александреску утверждает что элементы в таких структурах располагаются в пямяти последовательно.

И видимо так и есть в большинстве компиляторов.

Однако вопрос не в этом, а в том, использует ли он эту укладку в памяти? Т.е. адресную арифметику или void* вместо стандартных плюсовых неявных преобразований?

www_linux_org_ru ★★★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

>>> Что-то не могу придумать пример, как написать так, чтобы при "нелогичной" реализации одиночного наследования код ломался.

>> Derived*->void*->Base*

>Нет, это просто стандартная ошибка при использовании C++, которая имеет тенденцию ускользать на большинстве реализаций, когда наследование не множественное, и немедленно вылезает повреждениями памяти, как только добавляется ещё один базовый класс.

Однако кастовать указатель на объект к void* стандарт позволяет. И обратно тоже.

>И не надо надеяться на то, что разработчики компиляторов заботятся об ошибках такого рода. Их библия - стандарт, и ничего более.

У MS? библия - стандарт? Они ИМХО до сих пор используют C-style cast повсеместно. GCC тоже не будут изменять топологию размещения элементов в классах, поскольку подавляющее большинство молодых FOSS-проектов написано через Ж и напичкано хаками.

Absurd ★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

> я бы как раз использовал static_cast оба раза, потому что про это в стандарте явно написно, что будет работать (5.2.9, пункт 10: "A value of type pointer to object converted to “pointer to cv void” and back to the original pointer type will have its original value.")

да, точно.

dilmah ★★★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

> Они ИМХО до сих пор используют C-style cast повсеместно.

у C-style cast четко описанное в стандарте значение. Какой из const_cast, static_cast, *_cast он означает в какой ситуации

dilmah ★★★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

> у C-style cast четко описанное в стандарте значение. Какой из const_cast, static_cast, *_cast он означает в какой ситуации

поэтому часто лучше использовать как раз C-style cast, чтобы компилятор автоматически вывел какой *_cast использовать, чтобы не было таких ошибок как выше использовали reinterpret_cast вместо static_cast.

dilmah ★★★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

>> у C-style cast четко описанное в стандарте значение. Какой из const_cast, static_cast, *_cast он означает в какой ситуации

>поэтому часто лучше использовать как раз C-style cast, чтобы компилятор автоматически вывел какой *_cast использовать, чтобы не было таких ошибок как выше использовали reinterpret_cast вместо static_cast.

AFAIK C-style cast - это reinterpret_cast + const_cast.

Absurd ★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

> Однако кастовать указатель на объект к void* стандарт позволяет.

Да -- неявно: void* memory="abcd";

> И обратно тоже.

А обратно -- только явно, через (void*), чтобы тебя заставить задуматься.

Кстати: в этом "заставить задуматься" есть дыра -- именно эта пара write-read. По-хорошему там должна учиваться (не)константность -- тогда дыру можно прикрыть.

www_linux_org_ru ★★★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

> AFAIK C-style cast - это reinterpret_cast + const_cast.

нет. Он может означать любой *_cast кроме dynamic_cast.

5.4 - Explicit type conversion (cast notation) [expr.cast]

dilmah ★★★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

>> Однако кастовать указатель на объект к void* стандарт позволяет.

>Да -- неявно: void* memory="abcd";

Есть еще dynamic_cast<void*>(..) - он позволяет найти начало объекта в памяти если наследование множественное.

Absurd ★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

Конечно же const void* memory="abcd";

www_linux_org_ru ★★★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

void* memory="abcd"; не пройдет

www_linux_org_ru ★★★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

> AFAIK C-style cast - это reinterpret_cast + const_cast.

вынужден опять отвечать чётким "нет". C-style-cast отображается в C++-касты в следующем порядке (используется первый подходящий вариант), 5.4, пункт 5:

— a const_cast (5.2.11), — a static_cast (5.2.9), — a static_cast followed by a const_cast, — a reinterpret_cast (5.2.10), or — a reinterpret_cast followed by a const_cast,

Про Александреску скажу, что он реализация Loki::Tuple выполнена в полном соответствии со стандартом, а про размещение в памяти он говорил, вероятно, только чтобы обосновать, что производительность будет адекватной практически во всех случаях. Это аргумент за то, что выгоды от использования tuple перевешивают риск падения скорости при использовании "необычного" компилятора. Так что как пример к этому обсуждению это не годится.

anonymous ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

>> AFAIK C-style cast - это reinterpret_cast + const_cast.

>нет. Он может означать любой *_cast кроме dynamic_cast.

Конверсии типа void (*)(void*) -> void (*)(const CSomeType&) с помощью C-Style cast я тоже у них видел.

Absurd ★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

> Конверсии типа void (*)(void*) -> void (*)(const CSomeType&) с помощью C-Style cast я тоже у них видел.

Это подразумевает reinterpret_cast и разрешено при условии, что void (*) (void*) был получен через каст от void (*) const CSomeType&) (5.2.10, пункт 6).

Absurd, может хватит "примеров"? :)

anonymous ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

>> Конверсии типа void (*)(void*) -> void (*)(const CSomeType&) с помощью C-Style cast я тоже у них видел.

>Это подразумевает reinterpret_cast и разрешено при условии, что void (*) (void*) был получен через каст от void (*) const CSomeType&) (5.2.10, пункт 6).

AFAIK в том месте Win32 С API в этот калбэк передавало LPSOMETYPE который видимо был бинарно совместим с бинарным форматом CSomeType.

Absurd ★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

То есть конверсия была обратной, наглючил - void (*)(const CSomeType&) -> void (*)(void*)

Absurd ★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

> AFAIK в том месте Win32 С API в этот калбэк передавало LPSOMETYPE который видимо был бинарно совместим с бинарным форматом CSomeType.

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

dilmah ★★★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

>>Что-то не могу придумать пример, как написать так, чтобы при "нелогичной" реализации одиночного наследования код ломался.

> Derived*->void*->Base*

В2 от такого НЕ сломается. Потому что все эти адреса будут (побитово) одинаковы. (А методы класса В2 будут лезть к b через указатель __b, и это можно оптимизировать легко, так как он не меняется).

www_linux_org_ru ★★★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

> В2 от такого НЕ сломается.

Ну и что, а если такая реализация, то сломается: class B { A* parent; int b; };
Неправильно через void* к базе приводить, и баста. Ты же сам уже говорил об этом.

anonymous ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

> Ну и что, а если такая реализация, то сломается: class B { A* parent; int b; };

А я такую не предлагал :-) А вот Абсурд сказал что моя реализация В2 сломается -- я ему и говорю, что НЕ сломается

> Неправильно через void* к базе приводить, и баста. Ты же сам уже говорил об этом.

Неправильно, я не спорю. Как эта ошибка называется?

www_linux_org_ru ★★★★★ ()

Re: [знатокам стандарта С++] законно ли write(fd, &obj, sizeof(obj)) <перезапуск> read(fd, &obj, sizeof(obj)) при условии что...

Зачем я все это затеял -- хотел удостоверится, что альтернативные реализации объектной модели плюсов возможны (например В2). После этого имеет смысл различать саму объектную модель и ее реализацию и ДАЖЕ задуматься о смене реализации модели.

www_linux_org_ru ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.