使用 std::vector<double>访问由 std::unique_ptr<double[2]> 管理的数据 [英] Use std::vector&lt;double&gt; to access data managed by std::unique_ptr&lt;double[2]&gt;

查看:32
本文介绍了使用 std::vector<double>访问由 std::unique_ptr<double[2]> 管理的数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个复杂的类,它包含由智能指针管理的一大块 double[2] 类型的数据,例如:std::unique_ptrm_data; 我不能改变数据结构的类型.

I have a complex class, that holds a big block of double[2]-type data managed by a smart pointer like: std::unique_ptr<double[2]> m_data; I cannot change the type of the data structure.

我正在使用一个库,它为我提供了一个具有以下签名的函数:boolfunc_in_lib(std::vector& data, double& res).我无法更改此函数的签名.

I am using a library that gives me a function with the following signature: bool func_in_lib(std::vector<double>& data, double& res). I cannot change the signature of this function.

我想将 unique_ptr 管理的数据传递给需要 vector& 的函数,而不会中断与我的复杂类的连接.我希望该函数直接在我的 m_data 上工作,而不是将数据复制到 std::vector 并将其复制回我的复杂类,因为我必须多次这样做.

I want to pass the data managed by the unique_ptr to the function expecting a vector<double>& without breaking the connection to my complex class. I want the function to work directly on my m_data and not copy the data into a std::vector<double> and the copy it back into my complex class, because I have to do this a lot of times.

有没有办法做到这一点?

Is there any way to do this?

这是一些涵盖我想要的语义的代码.我关心的代码行是

Here is some code that covers the semantic I want to have. The code line of my concern is

vectoraccess_vec =/* 通过向量接口访问 my_data */;

#include <iostream>
#include <memory>
#include <vector>

using namespace std;

//--------------------------------------------------------------------------//
//--- This function is given, I cannot change its signature.
bool
func_in_lib(std::vector<double>& data, double& res) {
  //--- check some properties of the vector
  if (data.size() < 10)
    return false;
  //--- do something magical with the data
  for (auto& d : data)
    d *= 2.0;
  res = 42.0;
  return true;
}

//--------------------------------------------------------------------------//
struct DataType {
  double a = 1.0;
  double b = 2.0;
  double c = 3.0;
};

//--------------------------------------------------------------------------//
ostream&
operator<<(ostream& out, const DataType& d) {
  out << d.a << " " << d.b << " " << d.c << endl;
  return out;
}

//--------------------------------------------------------------------------//
int
main(int argc, char const* argv[]) {
  int count = 20;
  //--- init and print my data
  unique_ptr<DataType[]> my_data = make_unique<DataType[]>(count);
  for (int i = 0; i < count; ++i)
    cout << my_data.get()[i];
  //---
  double         result     = 0.0;
  vector<double> access_vec = /* give access to my_data via vector interface */;
  func_in_lib(access_vec, result);

  return 0;
}

推荐答案

我和我的一位同事找到了两个解决方案,解决了我的问题.

With a colleague of mine I found two solutions, that solve my problem.

这个想法是使用 std::vector 的底层实现的结构,它由 在我的例子中 3 个成员组成,其中包含 3 个指向向量的数据.

The idea is to use the structure of the underlying implementation of the std::vector<double>, which consists in my case of 3 members containing 3 pointers to the data of the vector.

  1. 数据段的起始地址
  2. 数据段的结束地址
  3. 当前数据段最大容量的地址

所以我构建了一个包含这三个地址的 struct 并使用 reinterpret_caststd::vector.这适用于我机器上 std::vector 的当前实现.此实现可能会有所不同,具体取决于所安装的 STL 版本.

So I build a struct containing these three addresses and use a reinterpret_cast to a std::vector. This works with the current implementation of std::vector on my machine. This implementation can vary, depending on the installed version of the STL.

这里的好处是,我可以使用 std::vector 的接口而无需创建它.我也不必将数据复制到 std::vector 中.我还可以从存储在复杂类中的初始数据中提取一部分.我可以通过发送给结构体的指针来控制被操作的部分.

The nice thing here is, that I can use the interface of std::vector without creating it. I also do not have to copy the data into a std::vector. I could also take a just part from the initial data stored in my complex class. I can control the manipulated part, by the pointers I send to the struct.

这解决了我的问题,但它是一个黑客.我可以使用它,因为代码只与我自己相关.我仍然发布它,因为它可能会引起其他人的兴趣.

This solves my problem, but it is a hack. I can use it, because the code is only relevant for myself. I still post it, because it could be of interest for others.

#include <iostream>
#include <memory>
#include <vector>

using namespace std;

//--------------------------------------------------------------------------//
//--- This function is given, I cannot change its signature.
bool
func_in_lib(std::vector<double>& data, double& res) {
  //--- check some properties of the vector
  if (data.size() < 10)
    return false;
  //--- do something magical with the data
  for (auto& d : data)
    d *= 2.0;

  res = 42.0;
  return true;
}

//--------------------------------------------------------------------------//
struct DataType {
  double a = 1.0;
  double b = 2.0;
  double c = 3.0;
};

//--------------------------------------------------------------------------//
ostream&
operator<<(ostream& out, const DataType& d) {
  out << d.a << " " << d.b << " " << d.c << endl;
  return out;
}

//--------------------------------------------------------------------------//
int
main(int argc, char const* argv[]) {
  int count = 20;
  //--- init and print my data
  unique_ptr<DataType[]> my_data = make_unique<DataType[]>(count);
  for (int i = 0; i < count; ++i)
    cout << my_data.get()[i];
  
  //--------------------------------------------------------------------------//
  // HERE STARTS THE UGLY HACK, THAT CAN BE ERROR-PRONE BECAUSE IT DEPENDS ON
  // THE UNDERLYING IMPLEMENTATION OF std::vector<T>
  //--------------------------------------------------------------------------//
  struct VecAccess {
    double* start = nullptr; // address to the start of the data
    double* stop0 = nullptr; // address to the end of the data
    double* stop1 = nullptr; // address to the capacity of the vector
  };

  //---
  DataType*       p_data = my_data.get();
  VecAccess       va{ &(p_data[0].a),                //points at the 'front' of the vector
                      &(p_data[count - 1].c) + 1,    //points at the 'end' of the vector
                      &(p_data[count - 1].c) + 1 };
  vector<double>* p_vec_access = reinterpret_cast<vector<double>*>(&va);
  //--------------------------------------------------------------------------//
  // HERE ENDS THE UGLY HACK.
  //--------------------------------------------------------------------------//

  //---
  double dummy = 0.0;   // this is only relevant for the code used as minimum example
  func_in_lib(*p_vec_access, dummy);

  //--- print the modified data
  for (int i = 0; i < count; ++i)
    cout << my_data.get()[i];

  return 0;
}


更新: 分析第二个解决方案的汇编代码表明,即使没有调用数据对象的复制构造函数,也会执行内容的复制.复制过程发生在机器代码级别.


Update: Analyzing the assembler code of the second solution shows, that a copy of the content is performed, even though the copy-constructor of the data objects is not called. The copy process happens at machine code level.

对于这个解决方案,我必须用 noexcept 标记 DataType 的移动构造函数.关键思想是不要DataType[]数组视为std::vector.相反,我们将 std::vector 视为 std::vector.我们可以然后将数据移动到这个向量中(不复制),将它发送给函数,然后将其移回.

For this solution I have to mark the Move-Constructor of DataType with noexcept. The key idea is not to treat the DataType[] array as a std::vector<double>. Instead we treat the std::vector<double> as a std::vector<DataType>. We can then move the data into this vector (without copying), send it to the function, and move it back afterwards.

数据不是复制而是移动std::vector,速度更快.同样与我的情况相关,我可以再次从存储在复杂类中的初始数据中提取一部分.此解决方案的缺点是我必须为以正确的大小移动数据.

The data is not copied but moved std::vector, which is faster. Also relevant for my case I can again take a just part from the initial data stored in my complex class. Drawback with this solution I have to create an additional storage for the moved data with the correct size.

#include <iostream>
#include <memory>
#include <utility>
#include <vector>

using namespace std;

//--------------------------------------------------------------------------//
//--- This function is given, I cannot change its signature.
bool
func_in_lib(std::vector<double>& data, double& res) {
  //--- check some properties of the vector
  if (data.size() < 10)
    return false;
  //--- do something magical with the data
  for (auto& d : data)
    d *= 2.0;

  res = 42.0;
  return true;
}

//--------------------------------------------------------------------------//
class DataType {
public:
  double a = 1.0;
  double b = 2.0;
  double c = 3.0;

  // clang-format off
  DataType() = default;
  DataType(DataType const&) = default;
  DataType(DataType&&) noexcept = default;
  DataType& operator=(DataType const&) = default;
  DataType& operator=(DataType&&) noexcept  = default;
  ~DataType()  = default;
  // clang-format on
};

//--------------------------------------------------------------------------//
ostream&
operator<<(ostream& out, const DataType& d) {
  out << d.a << " " << d.b << " " << d.c << endl;
  return out;
}

//--------------------------------------------------------------------------//
int
main(int argc, char const* argv[]) {
  int count = 20;
  //--- init and print my data
  unique_ptr<DataType[]> my_data = make_unique<DataType[]>(count);
  for (int i = 0; i < count; ++i)
    cout << my_data.get()[i];
  //---
  vector<double> double_vec;
  double_vec.reserve(count * 3);
  //--- here starts the magic stuff
  auto& vec_as_datatype = *reinterpret_cast<vector<DataType>*>(&double_vec);
  auto* start_mv        = &(my_data.get()[0]);
  auto* stop_mv         = &(my_data.get()[count]) + 1;
  //--- move the content to the vec
  move(start_mv, stop_mv, back_inserter(vec_as_datatype));
  //--- call the external func in the lib
  double dummy = 0.0; // is only needed for the code of the example
  func_in_lib(double_vec, dummy);
  //--- move the content to back
  move(begin(vec_as_datatype), end(vec_as_datatype), start_mv);
  //--- print modified the data
  for (int i = 0; i < count; ++i)
    cout << my_data.get()[i];
}

这篇关于使用 std::vector<double>访问由 std::unique_ptr<double[2]> 管理的数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆