У меня есть код, который выглядит следующим образом:
char member_data[16];
template<typename T>
void set(void (T::*member)(void)) {
memcpy(member_data, (char*) &member, sizeof(member));
}
template<typename T>
void (T::*)(void) get() {
void (T::*member)(void);
memcpy((char*) &member, member_data, sizeof(member));
return member;
}
В полном контексте я могу быть уверен, что set
всегда использует тот же тип, что и следующий get
,
Можно ли это безопасно переписать для использования reinterpret_cast
?
Этот код делает то же самое, что и выше?
char member_data[16];
template<typename T>
using member_func = void (T::*)();
template<typename T>
void set(member_func<T> member) {
reinterpret_cast<member_func<T>&>(member_data) = member;
}
template<typename T>
member_func<T> get() {
return reinterpret_cast<member_func<T>&>(member_data));
}
Версия, которая у вас есть в отредактированной части, недействительна: вы не можете получить доступ к произвольный char
массив как любой другой тип. Может быть возможно достичь чего-то подобного правильным способом, используя std::aligned_storage<..>
вместо равнины char
массив.
Если member_data
объявлен как
std::aligned_storage<sizeof(member_func<T>), alignof(member_func<T>)>::type member_data;
или (по существу, эквивалентно)
alignas(member_func<T>) char member_data[sizeof(member_func<T>)];
тогда ваш reinterpret_cast<..>
подход должен на самом деле работать. Вместо зависимого параметра шаблона sizeof
а также alignof
выражения, вы можете попробовать использовать любой фиксированный member_func<some_class>
, Крайне маловероятно, что реализация имеет разные требования к размеру или выравниванию для указателей на функции-члены разных классов. Если вы хотите быть действительно безопасным, используйте статические подтверждения для проверки.
Можно ли это безопасно переписать для использования reinterpret_cast?
Кроме того, что описано в вашем редактировании, вы можете напрямую привести указатель на функцию-член, например reinterpret_cast<SomeType>(member)
, только если SomeType
также является указателем на тип члена. Так что вы Можно выберите один тип функции указателя на член в качестве «хранилища указателя на функцию универсального члена», если все, что вы делаете с этим значением, это конвертируете его обратно в исходный тип указателя на член.
Вы не можете конвертировать указатель на член в указатель на объект (или наоборот).
В обоих случаях ваш код небезопасен как есть, потому что он переполнит member_data
буфер, если sizeof (void (T::*)()) > 16
,
И, кстати: первый пример кода уже делает использование reinterpret_cast
: Старый стиль приведен к (char*)
от void (T::**)()
уже эквивалентно reinterpret_cast
😉
Других решений пока нет …