Здравствуйте, c-smile, Вы писали:
CS>Скажем есть указатель на уже выделенную память под объект. CS>Как вызвать конструктор по этому месту?
CS>Вариант с placement new я знаю:
CS>
Здравствуйте, jazzer, Вы писали:
J>Да, это именно он и есть, а чем он не устраивает?
Мне нужно решить на самом деле обратную задачу
Вызвать placement new без вызова конструктора.
Для классов с виртуальными функциями new(oldobj) просто меняет (инициализирует)
vtbl. А конструктор инициализирует поля. Мне надо вызвать new(place) но не конструктор.
Здравствуйте, c-smile, Вы писали:
CS>Мне нужно решить на самом деле обратную задачу CS>Вызвать placement new без вызова конструктора.
CS>Для классов с виртуальными функциями new(oldobj) просто меняет (инициализирует) CS>vtbl. А конструктор инициализирует поля. Мне надо вызвать new(place) но не конструктор.
Здравствуйте, night beast, Вы писали:
NB>дык это, может мемкопи первых нескольких байт?
a. Хотелось бы переносимое решение. Это компилируется в MSVC, Apple XCode и GCC.
b. Если с "to" можно еще как-то разобраться, то что делать с from? Откуда копировать vtbl ?
Re[5]: С++, как вызвать конструктор объекта по месту?
Здравствуйте, c-smile, Вы писали:
NB>>дык это, может мемкопи первых нескольких байт?
CS>a. Хотелось бы переносимое решение. Это компилируется в MSVC, Apple XCode и GCC.
общая пустая база есть?
CS>b. Если с "to" можно еще как-то разобраться, то что делать с from? Откуда копировать vtbl ?
не подумал
Re[5]: С++, как вызвать конструктор объекта по месту?
Здравствуйте, c-smile, Вы писали:
NB>>дык это, может мемкопи первых нескольких байт?
CS>a. Хотелось бы переносимое решение. Это компилируется в MSVC, Apple XCode и GCC. CS>b. Если с "to" можно еще как-то разобраться, то что делать с from? Откуда копировать vtbl ?
существующий код менять нельзя?
может что-то вроде этого
Я солидарен с кодт: "Сделали всё то же, что делает компилятор, только руками."
Как-то не радует руками прописывать vtbl. В реальности там что-то под 50 методов. Да еще и super calls используются.
Re[3]: С++, как вызвать конструктор объекта по месту?
Здравствуйте, c-smile, Вы писали:
CS>Здравствуйте, jazzer, Вы писали:
J>>Да, это именно он и есть, а чем он не устраивает?
CS>Мне нужно решить на самом деле обратную задачу CS>Вызвать placement new без вызова конструктора.
Для произвольного левого класса это невозможно сделать, если ты хочешь, чтоб код оставался соответствующим Стандарту. Если классы все под твоим контролем — юзай указатель pimpl, в него ты можешь засунуть все, что угодно (в т.ч. std::string), и не инициализировать, если он уже есть (ну или передавать указатель в конструктор, чтоб уж совсем не зависеть от его точного расположения в классе).
Ну и по-хорошему ты еще должен корректно прибить тот объект, который там лежал, вызвав его деструктор (хотя с точки зрения lifetime это и не обязательно — достаточно факта переиспользования памяти).
struct Thing
{
struct ThingImpl;
typedef std::unique_ptr<ThingImpl> Pimpl;
Thing(Pimpl p) : pimpl_(p) {}
// другие нормальные инициализирующие pimpl_ конструкторыvirtual ~Thing() {} // pimpl_ будет прибит тут автоматомtemplate<class T>
T* mutate() {
Pimpl tmp( pimpl_ ); // теперь this своим pimpl-ом не владеет
~Thing(); // естественно, деструктор должен быть виртуальнымreturn new(this) T(tmp);
}
protected: // наследникам нужен доступ к потрохам
Pimpl pimpl_;
};
подойдет?
CS>Я солидарен с кодт: "Сделали всё то же, что делает компилятор, только руками."
все так. но к этой vtbl ты имеешь доступ.
CS>Как-то не радует руками прописывать vtbl. В реальности там что-то под 50 методов. Да еще и super calls используются.
может есть возможность отделить данные от интерфейса. через предложенный Pimp или что-то вроде optional'а сделать:
struct data {};
struct test {
optional< data > m_data;
test ( NO_INIT ) { m_data.is_valid = true; }
};
Re[4]: С++, как вызвать конструктор объекта по месту?
Здравствуйте, jazzer, Вы писали:
J>Здравствуйте, c-smile, Вы писали:
CS>>Здравствуйте, jazzer, Вы писали:
J>>>Да, это именно он и есть, а чем он не устраивает?
CS>>Мне нужно решить на самом деле обратную задачу CS>>Вызвать placement new без вызова конструктора.
J>placement new без вызова конструктора называется reinterpret_cast
Ну нет конечно. placement new как минимум инициализирует vtbl если она есть. reinterpret_cast он ничего не делает. Или я не понял сермяжность.
CS>>Для классов с виртуальными функциями new(oldobj) просто меняет (инициализирует) CS>>vtbl. А конструктор инициализирует поля. Мне надо вызвать new(place) но не конструктор.
CS>>Контекст проблемы: http://stackoverflow.com/questions/21212379/changing-vtbl-of-existing-object-on-the-fly-dynamic-subclassing
J>Для произвольного левого класса это невозможно сделать, если ты хочешь, чтоб код оставался соответствующим Стандарту. Если классы все под твоим контролем — юзай указатель pimpl, в него ты можешь засунуть все, что угодно (в т.ч. std::string), и не инициализировать, если он уже есть (ну или передавать указатель в конструктор, чтоб уж совсем не зависеть от его точного расположения в классе).
pimpl это a) лишний redirection level и б) потребность указания одних и тех же функций два раза.
Собственно vtbl и есть pimpl если смотреть на абстрактный класс и его специализацию/имплементацию.
J>Ну и по-хорошему ты еще должен корректно прибить тот объект, который там лежал, вызвав его деструктор (хотя с точки зрения lifetime это и не обязательно — достаточно факта переиспользования памяти).
Не надо там ничего прибивать. Ни до ни после turn_thing_to.
Поля остаются теми же и по тому же offsetof.
C++ ведь разрешает работать с конструкциями:
union {
Thing t1;
OtherThing t2;
}
мой вариант это то же самое только несколько иначе описанный.
Re[5]: С++, как вызвать конструктор объекта по месту?
Здравствуйте, c-smile, Вы писали:
CS>>>Вызвать placement new без вызова конструктора.
J>>placement new без вызова конструктора называется reinterpret_cast
CS>Ну нет конечно. placement new как минимум инициализирует vtbl если она есть. reinterpret_cast он ничего не делает. Или я не понял сермяжность.
Это делает конструктор. Вернее, стандарт не специфицирует vtbl вообще, но по сути этим занимается конструктор в качестве первого шага, так как указатель на vtbl — это просто скрытый член, который надо проинициализировать.
Так что сермяжность очень простая: new выделяет память, вызывает конструктор, возвращает указатель нужного типа.
placement new убирает первый шаг, оставляя вызов конструктора и возврат указателя нужного типа.
Если ты убираешь и конструктор, остается просто возврат указателя — это reinterpret_cast.
CS>>>Для классов с виртуальными функциями new(oldobj) просто меняет (инициализирует) CS>>>vtbl. А конструктор инициализирует поля. Мне надо вызвать new(place) но не конструктор.
CS>>>Контекст проблемы: http://stackoverflow.com/questions/21212379/changing-vtbl-of-existing-object-on-the-fly-dynamic-subclassing
J>>Для произвольного левого класса это невозможно сделать, если ты хочешь, чтоб код оставался соответствующим Стандарту. Если классы все под твоим контролем — юзай указатель pimpl, в него ты можешь засунуть все, что угодно (в т.ч. std::string), и не инициализировать, если он уже есть (ну или передавать указатель в конструктор, чтоб уж совсем не зависеть от его точного расположения в классе).
CS>pimpl это a) лишний redirection level и б) потребность указания одних и тех же функций два раза. CS>Собственно vtbl и есть pimpl если смотреть на абстрактный класс и его специализацию/имплементацию.
Да, это pimpl таблицы методов. Но тебе ж еще и данные нужны
J>>Ну и по-хорошему ты еще должен корректно прибить тот объект, который там лежал, вызвав его деструктор (хотя с точки зрения lifetime это и не обязательно — достаточно факта переиспользования памяти).
CS>Не надо там ничего прибивать. Ни до ни после turn_thing_to. CS>Поля остаются теми же и по тому же offsetof.
Ты можешь это гарантировать? И в случае множественного наследования тоже?
Для начала, размещение полей вообще никак не специфицировано, если тип не standard layout (а это как раз твой случай).
Так что ты уж определись — либо ты хочешь иметь код, соответствующий стандарту (как, например, код, который я привел), либо код, который "работает на доступных мне компиляторах".
Если второе — да, можешь предполагать что угодно, и проще всего вообще будет тупо вломиться в объект и поменять его внутренний указатель на vtbl, нужную тебе, и не заморачиваться с конструкторами, placement new и прочим — дешево и сердито.
CS>C++ ведь разрешает работать с конструкциями: CS>
CS>мой вариант это то же самое только несколько иначе описанный.
Очень ограниченно разрешает. Ты не можешь записать в t1, а потом прочитать из t2. Либо одно, либо другое. Хочешь поменять — прибей одно, создай другое, читай [class.union]:
4 [ Note: In general, one must use explicit destructor calls and placement new operators to change the active
member of a union. —end note ] [ Example: Consider an object u of a union type U having non-static data
members m of type M and n of type N. If M has a non-trivial destructor and N has a non-trivial constructor
(for instance, if they declare or inherit virtual functions), the active member of u can be safely switched
from m to n using the destructor and placement new operator as follows:
u.m.~M();
new (&u.n) N;
—end example ]
С объединениями из standard layout типов правила немного слабее, но это не твой случай из-за виртуальностей.
Если ты собираешься писать standard-compliant код, конечно же.
Если хочешь — сделай явный указатель на таблицу методов, сохраняя свои классы standard layout, и меняй потом эту таблицу руками как обычный член — с этим проблем нет.
Но если ты хочешь пользоваться встроенной системой типов С++ в соответствии со стандартом — придется соответствовать, не предполагая, в частности, никаких vtbl и прочего.
Здравствуйте, jazzer, Вы писали:
J>Если второе — да, можешь предполагать что угодно, и проще всего вообще будет тупо вломиться в объект и поменять его внутренний указатель на vtbl, нужную тебе, и не заморачиваться с конструкторами, placement new и прочим — дешево и сердито.
Только нет гарантий, что это будет работать всегда. Например, при оптимизации компилятор может в каком-то месте определить, что тип объекта известен, и просто принлайнить вызовы некоторых виртуальных функций. И придется долго вылавливать почему на некоторых сборках с оптимизацией что-то работает не так.
Re[3]: С++, как вызвать конструктор объекта по месту?
Здравствуйте, c-smile, Вы писали:
CS>Здравствуйте, jazzer, Вы писали:
J>>Да, это именно он и есть, а чем он не устраивает?
CS>Мне нужно решить на самом деле обратную задачу CS>Вызвать placement new без вызова конструктора.
CS>Для классов с виртуальными функциями new(oldobj) просто меняет (инициализирует) CS>vtbl. А конструктор инициализирует поля. Мне надо вызвать new(place) но не конструктор.
CS>Контекст проблемы: http://stackoverflow.com/questions/21212379/changing-vtbl-of-existing-object-on-the-fly-dynamic-subclassing
Может имеет смысл вынести данные из Thing?
struct Data
{
int a;
};
struct Storage
{
// здесь, возможно, будет лучше использовать aligned_storage для Data и Thing/OtherThing/YetAnotherThing.
Data data;
Thing thing;
};
Если создавать Thing только как часть Storage, вполне можно будет вычислять смещение Data относительно Thing::this в compile-time, т.е. можно будет сделать что-то вроде
class Thing
{
...
Data *data()
{
return ((char *)this) + offset;
}
...
virtual int value() { return data()->a; }
};
Тогда при пересоздании Thing, данные, гарантировано не изменятся и скорость работы с ними должна быть такой же, как если бы они лежали в Thing.
Re[7]: С++, как вызвать конструктор объекта по месту?
Сишный способ даёт на одну косвенность меньше. А если в интерфейсе только одна функция, то можно вообще сразу эту функцию присваивать (предельный случай идиомы NVI).
Ну и наконец, маленький, но странный хак. Срезка наооборот.
#include <cstdio>
#include <memory>
struct Base
{
int x, y, z;
Base(int i) : x(i), y(i+i), z(i*i) {}
virtual void whoami() { printf("%p base %d %d %d\n", this, x, y, z); }
};
struct Derived : Base
{
Derived(Base&& b) : Base(b) {}
virtual void whoami() { printf("%p derived %d %d %d\n", this, x, y, z); }
};
int main()
{
Base b(3);
Base* p = &b;
b.whoami(); // 000000000022fd50 base 3 6 9
p->whoami(); // 000000000022fd50 base 3 6 9
Base t(std::move(b));
Derived* d = new(&b)Derived(std::move(t));
printf("-----\n");
b.whoami(); // 000000000022fd50 base 3 6 9
p->whoami(); // 000000000022fd50 derived 3 6 9
d->whoami(); // 000000000022fd50 derived 3 6 9
};
Обрати внимание, что компилятор не ждёт подлянки с подменой типа и немножко оптимизирует.
Перекуём баги на фичи!
Re[3]: С++, как вызвать конструктор объекта по месту?