Допускает ли стандарт C копирование произвольной полиморфной структуры данных?

#c #memory #standards

#c #память #стандарты

Вопрос:

Я искал StackOverflow, но не смог найти вопрос, который напрямую затрагивает эту проблему.

Сначала некоторый контекст: я пытаюсь реализовать Either тип в C , который может обрабатывать полиморфные данные, подобно тому, как вы можете использовать std::runtime_error new ключевое слово — . Все отлично работает с примитивными типами, блоками и ссылками, но, учитывая, что мы не можем заранее знать размер полиморфной структуры данных, все становится сложнее. Затем я подумал о копировании структуры в необработанный буфер в куче, чтобы я мог передавать ее, как если бы она была в стеке.

Пример an Either<L, R> -type:

 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*>(amp;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>amp;> { using type = ConstBoxRefTag; };

template<typename T>
struct GetTag<Box<T>amp;amp;> { 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*>(amp;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*>(amp;buf));
  }

  Tamp; reference() {
    return *reinterpret_cast<T*>(amp;buf);
  }

  const Tamp; reference() const {
    return *static_cast<T*>(amp;buf);
  }

  ~Box() {
    if (buf != nullptr) {
      reference().~T();
      std::free(buf);
    }
  }

};
  

Комментарии:

1. Не могли бы вы показать реальный пример кода, пожалуйста?

2.Нам нужен код, чтобы ответить на ваш вопрос. Однако, похоже, вы ищете std::any en.cppreference.com/w/cpp/utility/any ?

3. Спасибо за комментарий. Обновит вопрос.

Ответ №1:

Действительно, стандарт недавно добавил понятие «тривиально копируемое», так что использование memcpy объекта, который не является тривиально копируемым, не приводит к созданию допустимого объекта. До того, как был введен термин «тривиально копируемый», это контролировалось POD-ness.

Чтобы создать копию объекта C , вам нужно вызвать его конструктор копирования. Стандартного полиморфного способа сделать это не существует, но некоторые иерархии классов предпочитают включать виртуальную clone() функцию (или аналогичную), которая отвечала бы вашим потребностям.

Другой вариант — найти способ полностью избежать копирования.

Комментарии:

1. Вы правы, возможно, было бы лучше просто отложить это до пользователя и потребовать реализации метода копирования. Думая об этом, это может быть не так уж плохо. Спасибо!