В каком контексте можно использовать указатель переворота? - PullRequest
0 голосов
/ 25 июня 2018

Я только что узнал о перестановке указателей и совершенно не уверен в ее фактическом использовании.

Например, допустим, у меня есть служба Windows, сериализующая объект, содержащий указатели, с использованием перестановки указателей, а затемдесериализация его в другом процессе.

Каковы предварительные условия для его работы?

Мне кажется, что он потерпит неудачу, потому что адреса, к которым пытаются получить указатели, находятся в чужой памяти процесса.пространство и ОС не позволят новому процессу получить к ним доступ.Сверкание указателя заставляет указатели пережить изменение контекста, но этого недостаточно, не так ли?Это также потребовало бы, прежде всего, наличия данных о некотором сегменте разделяемой памяти, не так ли?

Также мне очень любопытно увидеть реальные примеры, если вам случится знать что-либо в C ++ с какой-либо библиотекой вродеBoost или что-нибудь подобное.

1 Ответ

0 голосов
/ 25 июня 2018

Когда у вас есть закрученные указатели, вы не можете следовать за ними (эффективно), пока они не распутаются.

Представьте, что у вас есть несколько записей, каждая со ссылками (указателями) на другие записи в каком-то произвольном графике.

Один наивный способ разобраться с этим - взять двоичное значение указателя в качестве UID и сериализовать его.В то время как мы делаем это, мы также поддерживаем таблицу адресов для записи в порядке сериализации, и мы сериализуем эту последнюю.Назовите это таблицей Swizzle.

Когда мы десериализовываем, мы загружаем структуры данных и строим таблицу (порядок в сериализации) в (новый адрес записи в памяти).Затем мы загружаем таблицу swizzle, которая представляет собой карту из (старого адреса) в (порядок в сериализации).

Мы объединяем эти две таблицы и получаем (старый адрес) в (адрес новой записи в памяти)) table - таблица unswizzle.

Далее мы просматриваем наши десериализованные записи и для каждого указателя применяем эту карту.Старое двоичное значение каждого адреса хранится в некотором указателе;мы смотрим это в таблицу unswizzle, и заменяем это.Теперь каждый указатель указывает на адрес записи в новом адресном пространстве.

struct node {
  std::vector<node*> links;
  void write( OutArch& out ) const& {
    out.register_swizzle(this);
    out << links.size();
    for (node* n:links) {
      out << out.swizzle(n);
    }
  }
  static node* read( InArch& in ) {
    auto* r = new node;
    in.register_unswizzle( r );
    std::size_t n;
    in >> n;
    r->reserve(n);
    for (std::size_t i = 0; i<n; ++i) {
      std::intptr_t ptr;
      in >> ptr;
      r->links.push_back( reinterpret_cast<node*>(ptr) ); // danger
    }
    return r;
  }
  friend void do_unswizzle( InArch& in, node* n ) {
    for (node*& link : n->links ) {
      link = in.unswizzle(link);
    }
  }
};
struct OutArch {
  friend void operator<<( OutArch& arch, std::size_t count ); //TODO
  friend void operator<<( OutArch& arch, std::intptr_t ptr ); //TODO
  std::intptr_t swizzle( void* ptr ) {
    return reinterpret_cast<std::intptr_t>(ptr);
  }
  void register_swizzle( void* ptr ) {
    swizzle_table.insert( {(reinterpret_cast<std::intptr_t>(p), record_number} );
    ++record_number;
  }
private:
  // increased 
  std::size_t record_number = 0;
  std::map< std::intptr_t, std::size_t > swizzle_table;
};
struct InArch {
  friend void operator>>( InArch& arch, std::size_t& count ); //TODO
  friend void operator>>( InArch& arch, std::intptr_t& count ); //TODO
  template<class T>
  void register_unswizzle( T* t ) {
    unswizzle_table.insert( {record_number, t} );
    ++record_number;
    unswizzle_tasks.push_back([t](InArch* self){
      do_unswizzle( *self, t );
    });
  }
  struct unswizzler_t {
    void* ptr;
    template<class T>
    operator T*()&&{return static_cast<T*>(ptr);}
  };
  unswizzler_t unswizzle( void* ptr ) {
    auto p = reinterpret_cast<std::intptr_t>(ptr);
    auto it1 = swizzle_table.find(p);
    if (it1 == swizzle_table.end()) return {nullptr};
    auto it2 = unswizzle_table.find(it1->second);
    if (it2 == unswizzle_table.end()) return {nullptr};
    return { it2->second };
  }
  void load_swizzle_table(); //TODO
  void execute_unswizzle() {
    for (auto&& task: unswizzle_tasks) {
      task(this);
    }
  }
private:
  // increased 
  std::size_t record_number = 0;
  std::map< std::size_t, void* > unswizzle_table;
  std::map< std::intptr_t, std::size_t > swizzle_table;
  std::vector< std::function< void(InArch*) > > unswizzle_tasks;
};

Существует множество способов заклинания.Вместо сохранения двоичного значения указателя, вы можете сохранить порядок, в котором вы его сериализуете (например);но это требует некоторой тщательной предварительной обработки или путешествия во времени, поскольку у вас будут ссылки на структуры, которые вы еще не сериализовали.

Или вы можете сгенерировать guid, выписать guid для каждой записи и сохранитьтаблица swizzle от {запись адреса} до {guid} в старом процессе.Когда вы сохраняете записи, вы видите, есть ли указатели в вашей таблице Swizzle;если нет, добавьте их.Затем напишите гид вместо указателя.Не пишите таблицу Swizzle в этом случае;таблица unswizzle от {guid} до {адрес записи} может быть построена только из заголовка guid для каждой записи.Затем, используя эту таблицу восстановления, перестройте записи на стороне назначения.

...