Size: a a a

2020 November 26

O

Oleksandr in pro.cxx
Правильно ли я понимаю что в плюсах нельзя стирать тип функции/метода (не функтора!) через каст к void*?
источник

O

Oleksandr in pro.cxx
Т.е. подобный код не может и не будет компилироваться

#include <iostream>

int main() {
   /// A
   int a = 42;
   int* pa = static_cast<int*>(static_cast<void*>(&a));
   std::cout << *pa << std::endl;

   /// B
   int (*b)() = +[]() { return 42; };
   int (*pb)() = static_cast<int (*)()>(static_cast<void*>(b));
   std::cout << pb() << std::endl;

   return 0;
}
источник

O

Oleksandr in pro.cxx
Можно ли это как-то обойти без UB?
источник

IZ

Ilia Zviagin in pro.cxx
Эдвард Даньковский
Ребят, че-то не могу понять одну штуку связанную с LifeTime объектов. В стандрате сказано, что lifetime заканчивается в начале вызова деструктора объекта с non-trivial destructor (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf 3.8 1.3). Так же в стандарте сказано, что вызов  non-static member function объекта с завершенным Lifetime - это UB.
Получается, что из деструктора объекта с non-trivial destructor нельзя вызывать non-static member function этого объекта?
Что я упускаю?
Всё-таки наверное в КОНЦЕ работы деструктора.
Ты видимо перепутал
источник

ЭД

Эдвард Даньковский... in pro.cxx
Ilia Zviagin
Всё-таки наверное в КОНЦЕ работы деструктора.
Ты видимо перепутал
Возможно, я как-то неправильно трактую, но там написано
The lifetime of an object of type T ends when if T is a class type with a non-trivial destructor (12.4), the destructor call starts
or
(1.4) — the storage which the object occupies is reused or released
источник

AT

Alexander Tulikov in pro.cxx
Эдвард Даньковский
Ребят, че-то не могу понять одну штуку связанную с LifeTime объектов. В стандрате сказано, что lifetime заканчивается в начале вызова деструктора объекта с non-trivial destructor (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf 3.8 1.3). Так же в стандарте сказано, что вызов  non-static member function объекта с завершенным Lifetime - это UB.
Получается, что из деструктора объекта с non-trivial destructor нельзя вызывать non-static member function этого объекта?
Что я упускаю?
Всё правильно, время жизни объекта с конца конструктора до начала деструктора.
источник

ЭД

Эдвард Даньковский... in pro.cxx
Alexander Tulikov
Всё правильно, время жизни объекта с конца конструктора до начала деструктора.
Тогда, правильно ли я понимаю, что вызов членов-функций из деструктора - UB?
источник

ЭД

Эдвард Даньковский... in pro.cxx
Делаю такой вывод из трактовки [basic.life] 5.2 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf
источник

AT

Alexander Tulikov in pro.cxx
Эдвард Даньковский
Тогда, правильно ли я понимаю, что вызов членов-функций из деструктора - UB?
источник

IZ

Ilia Zviagin in pro.cxx
Oleksandr
Т.е. подобный код не может и не будет компилироваться

#include <iostream>

int main() {
   /// A
   int a = 42;
   int* pa = static_cast<int*>(static_cast<void*>(&a));
   std::cout << *pa << std::endl;

   /// B
   int (*b)() = +[]() { return 42; };
   int (*pb)() = static_cast<int (*)()>(static_cast<void*>(b));
   std::cout << pb() << std::endl;

   return 0;
}
А зачем это? Приводи указатель на функцию к void*, этого достаточно для хранения.
Но даже это лишнее, потому что ты не можешь вызвать функцию с одной сигнатурой через другую сигнатуру, то есть просто сигнатуру надо хранить ту же что у оригинальной функции.
Иначе ты не сможешь эту функцию вызвать.

Так что ub там или нет - я не знаю, но так просто нет смысла делать, поскольку ты функцию не вызовишь .
Именно поэтому так никто и не делает.
источник

ЭД

Эдвард Даньковский... in pro.cxx
Большое спасибо!
источник

IZ

Ilia Zviagin in pro.cxx
Эдвард Даньковский
Тогда, правильно ли я понимаю, что вызов членов-функций из деструктора - UB?
Нет. Не UB.
источник

O

Oleksandr in pro.cxx
Ilia Zviagin
А зачем это? Приводи указатель на функцию к void*, этого достаточно для хранения.
Но даже это лишнее, потому что ты не можешь вызвать функцию с одной сигнатурой через другую сигнатуру, то есть просто сигнатуру надо хранить ту же что у оригинальной функции.
Иначе ты не сможешь эту функцию вызвать.

Так что ub там или нет - я не знаю, но так просто нет смысла делать, поскольку ты функцию не вызовишь .
Именно поэтому так никто и не делает.
- Приводи указатель на функцию к void*, этого достаточно для хранения.
Я так и пытаюсь делать, но похоже что это запрещено стандартом:
https://godbolt.org/z/nG3Moq

- А зачем это?
Экспериментирую. Хочу получить что-то вроде полностью безтипового function_view. Который превращал бы функции A -> B в функции std::any -> std::any. Вот пример (который не компилируется):
https://godbolt.org/z/16sz5Y

- Просто сигнатуру надо хранить ту же что у оригинальной функции
Моя идея - хранить указатель на шаблонный "инвоукер" для каждой сигнатуры функции, который сам будет кастить перед вызовом. По аналогии с некоторыми реализациями std::any
источник

АК

Александр Караев... in pro.cxx
Oleksandr
- Приводи указатель на функцию к void*, этого достаточно для хранения.
Я так и пытаюсь делать, но похоже что это запрещено стандартом:
https://godbolt.org/z/nG3Moq

- А зачем это?
Экспериментирую. Хочу получить что-то вроде полностью безтипового function_view. Который превращал бы функции A -> B в функции std::any -> std::any. Вот пример (который не компилируется):
https://godbolt.org/z/16sz5Y

- Просто сигнатуру надо хранить ту же что у оригинальной функции
Моя идея - хранить указатель на шаблонный "инвоукер" для каждой сигнатуры функции, который сам будет кастить перед вызовом. По аналогии с некоторыми реализациями std::any
источник

IZ

Ilia Zviagin in pro.cxx
Oleksandr
- Приводи указатель на функцию к void*, этого достаточно для хранения.
Я так и пытаюсь делать, но похоже что это запрещено стандартом:
https://godbolt.org/z/nG3Moq

- А зачем это?
Экспериментирую. Хочу получить что-то вроде полностью безтипового function_view. Который превращал бы функции A -> B в функции std::any -> std::any. Вот пример (который не компилируется):
https://godbolt.org/z/16sz5Y

- Просто сигнатуру надо хранить ту же что у оригинальной функции
Моя идея - хранить указатель на шаблонный "инвоукер" для каждой сигнатуры функции, который сам будет кастить перед вызовом. По аналогии с некоторыми реализациями std::any
Формально - я не знаю, надо смотреть в стандарт.
Фактически - я тебе написал
источник

O

Oleksandr in pro.cxx
Спасибо, как раз эту статью вчера читал :)
Дело в том, что там тип не полностью стирается, а каст к воиду происходит только для функторов. Но функции хранятся без стирания типа.
источник

IZ

Ilia Zviagin in pro.cxx
Oleksandr
- Приводи указатель на функцию к void*, этого достаточно для хранения.
Я так и пытаюсь делать, но похоже что это запрещено стандартом:
https://godbolt.org/z/nG3Moq

- А зачем это?
Экспериментирую. Хочу получить что-то вроде полностью безтипового function_view. Который превращал бы функции A -> B в функции std::any -> std::any. Вот пример (который не компилируется):
https://godbolt.org/z/16sz5Y

- Просто сигнатуру надо хранить ту же что у оригинальной функции
Моя идея - хранить указатель на шаблонный "инвоукер" для каждой сигнатуры функции, который сам будет кастить перед вызовом. По аналогии с некоторыми реализациями std::any
(2)-ое не бывает.

(3)-ие - так шаблон всегда тебе тип функции, сигнатуру, сохранять будет .
источник

АК

Александр Караев... in pro.cxx
Oleksandr
Т.е. подобный код не может и не будет компилироваться

#include <iostream>

int main() {
   /// A
   int a = 42;
   int* pa = static_cast<int*>(static_cast<void*>(&a));
   std::cout << *pa << std::endl;

   /// B
   int (*b)() = +[]() { return 42; };
   int (*pb)() = static_cast<int (*)()>(static_cast<void*>(b));
   std::cout << pb() << std::endl;

   return 0;
}
вообще, быстрое гугление показало, что каст указателя на функцию к void* может быть невалиден.

а вот стирание типа через каст в void(*)() должно прокатить:
https://eel.is/c++draft/expr.compound#expr.reinterpret.cast-6
источник

O

Oleksandr in pro.cxx
Ilia Zviagin
(2)-ое не бывает.

(3)-ие - так шаблон всегда тебе тип функции, сигнатуру, сохранять будет .
(2) - не совсем понял что не бывает :)

(3) - шаблонная функция будет "хранить" сигнатуру имплементации, но сигнатура самой шаблонной функции не зависит от параметров шаблона, она одинакова для любых A и B. Поэтому ее саму можно хранить через указатель, без всяких сигнатур имплементации
источник

D

Danya in pro.cxx
Oleksandr
(2) - не совсем понял что не бывает :)

(3) - шаблонная функция будет "хранить" сигнатуру имплементации, но сигнатура самой шаблонной функции не зависит от параметров шаблона, она одинакова для любых A и B. Поэтому ее саму можно хранить через указатель, без всяких сигнатур имплементации
Извиняюсь, что я врываюсь без учета предыдущих обсуждений
Но шаблон функции не является функцией
Следовательно на неё нельзя взять указатель
источник