Я искал StackOverflow, но не смог найти вопрос, который напрямую решает эту проблему.
Сначала немного контекста: я пытаюсь реализовать тип Either
в C ++, который может обрабатывать полиморфизмданные, так же, как вы можете бросить std::runtime_error
без new
ключа.Все отлично работает с примитивными типами, POD и ссылками, но, учитывая, что мы не можем заранее знать размер полиморфной структуры данных, все становится сложнее.Затем я подумал о копировании структуры в необработанный буфер в куче, чтобы я мог передать ее вокруг , как если бы это было в стеке.
Пример Either<L, R>
-типа:
Either<std::runtime_error, int> doSomeStuff() {
if (err) {
return left(std::runtime_error("Not right!"));
}
return right(42);
}
Я экспериментировал с такими вещами, как std::memcpy(buf, reinterpret_cast<char*>(static_cast<T*>(&value)), sizeof(T))
, но продолжаю получать ошибки SIGSEGV.Это потому, что, как я подозреваю, полиморфные структуры содержат дополнительную бухгалтерию, которая становится поврежденной при копировании?Есть ли способ хранить произвольную полиморфную структуру T
в куче, чтобы я мог передать ее, как если бы это был обычный объект, выделенный стеком?Или в современных стандартах C ++ такая вещь «неопределена»?
Обновление: вот код, который у меня есть.Это не красиво, но это лучшее, что у меня есть.
struct ConstBoxRefTag { };
struct BoxMoveTag { };
struct PlainValueTag { };
// struct BoxValueTag { };
template<typename T>
struct GetTag { using type = PlainValueTag; };
template<typename T>
struct GetTag<const Box<T>&> { using type = ConstBoxRefTag; };
template<typename T>
struct GetTag<Box<T>&&> { using type = BoxMoveTag; };
template<typename T>
struct GetTag<Box<T>> { using type = ConstBoxRefTag; };
template<typename T>
class Box<T, typename std::enable_if<std::is_polymorphic<T>::value>::type> {
void* buf;
size_t sz;
template<typename R, typename Enabler>
friend class Box;
public:
using Type = T;
template<typename R>
Box(R val): Box(typename box::GetTag<R>::type {}, val) {}
template<typename R>
Box(ConstBoxRefTag, R oth): buf(std::malloc(oth.sz)), sz(oth.sz) {
std::memcpy(buf, oth.buf, oth.sz);
}
template<typename R>
Box(BoxMoveTag, R oth): buf(std::move(oth.buf)), sz(std::move(oth.sz)) {
oth.buf = nullptr;
};
template<typename R>
Box(PlainValueTag, R val): buf(std::malloc(sizeof(R))), sz(sizeof(R)) {
std::memcpy(buf, reinterpret_cast<void*>(static_cast<T*>(&val)), sizeof(R));
}
template<typename R>
R as() const {
static_assert(std::is_base_of<T, R>::value, "Class is not a subtype of base class");
return *static_cast<const R*>(reinterpret_cast<const T*>(&buf));
}
T& reference() {
return *reinterpret_cast<T*>(&buf);
}
const T& reference() const {
return *static_cast<T*>(&buf);
}
~Box() {
if (buf != nullptr) {
reference().~T();
std::free(buf);
}
}
};