我正在低级别接收托管数据{ void * data; uint 步幅,计数; }
格式。我可以读取、写入和交换数据项,但不能添加或删除/调整大小/重新分配。有足够的信息用于迭代和随机访问,但没有用于取消引用的类型信息。所有不透明的项目都可以轻松复制和移动。
在现代 C++ 中是否有可能促进这种数据格式的 std 兼容性,省略解引用接口(interface),并将评估委托(delegate)给外部上下文?像这样的东西:
std::sort(OpaqueFirst, OpaqueLast, [](Opaque a, Opaque b){ return handle(a, b); });
最佳答案
基本思想是创建一个引用您的子数组之一的类型并定义 swap
实际交换它们的内容:
struct buffer {std::span<std::byte> s;}; // but see below
void swap(const buffer &a,const buffer &b) {
assert(a.s.size()==b.s.size());
auto i=b.s.begin();
for(auto &x : a.s) std::swap(x,*i++);
}
然后我们只需要创建此类对象的惰性范围,这很容易使用 std::views::transform
缠绕std::views::iota
,并根据问题写出简单的比较器:
struct array { void * data; uint stride, count; };
void sort(const array &a,bool lt(const void*,const void*)) {
std::ranges::sort
(std::views::transform
(std::views::iota(uint(),a.count),
[&](uint i)
{return buffer{{static_cast<std::byte*>(a.data)+a.stride*i,a.stride}};}),
[&](const buffer &x,const buffer &y) {return lt(x.s.data(),y.s.data());});
}
不幸的是,sort
要求能够将范围元素移动到临时变量中,而不仅仅是交换它们。这需要一个相当复杂的包装类型,它实际上可以暂时拥有数据,这反过来又需要动态分配,因为 array::stride
不是一个常数:
struct buffer {
std::unique_ptr<std::byte[]> u;
std::span<std::byte> s;
buffer(std::span<std::byte> s) : s(s) {}
buffer(buffer &&b) noexcept {
if(b.u) u=std::move(b.u);
else {
u=std::make_unique<std::byte[]>(b.s.size());
std::ranges::copy(b.s,u.get());
s={u.get(),b.s.size()};
}
}
void operator=(const buffer &b) const noexcept;
buffer& operator=(const buffer &b) noexcept {
assert(s.size()==b.s.size());
std::ranges::copy(b.s,s.begin());
return *this;
}
};
保留我们的习惯swap
避免对每个元素交换使用此类分配,因此性能并不糟糕。未实现的operator=(…) const
有必要让图书馆相信 buffer
是一种代理类型,应该支持通过纯右值排序。
一个complete example与完整buffer
类型存在于编译器资源管理器中。
关于c++ - 通过外部评估调度将标准兼容性与不透明数据连接起来,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/77774335/