为什么 mapped_file::data returns char* 而不是 void*
Why mapped_file::data returns char* instead of void*
或者更好的模板 <T*>
?
如果内存映射文件包含一个 32 位整数序列,如果 data()
returned a void*
,我们可以静态转换为 std::uint32_t
直接。
为什么 boost 作者选择 return char*
?
编辑:如前所述,如果可移植性成为问题,则需要进行翻译。但是说一个文件(或者在这种情况下是一块内存)是一个字节流而不是一个比特流,或者 IEEE754 双精度数,或者复杂的数据结构,在我看来是一个非常宽泛的陈述,需要一些更多解释。
即使必须处理字节顺序,能够直接映射到 be_uint32_t
的矢量(如建议的那样(并在此处实现)将使代码更具可读性:
struct be_uint32_t {
std::uint32_t raw;
operator std::uint32_t() { return ntohl(raw); }
};
static_assert(sizeof(be_uint32_t)==4, "POD failed");
是否 allowed/advised 转换为 be_uint32_t*
?为什么,或者为什么不?
应该使用哪种演员表?
EDIT2:因为似乎很难直截了当而不是讨论天气,所以 elaborator 的内存模型是由位、字节或单词组成的,我将重新举个例子:
#include <cstdint>
#include <memory>
#include <vector>
#include <iostream>
#include <boost/iostreams/device/mapped_file.hpp>
struct entry {
std::uint32_t a;
std::uint64_t b;
} __attribute__((packed)); /* compiler specific, but supported
in other ways by all major compilers */
static_assert(sizeof(entry) == 12, "entry: Struct size mismatch");
static_assert(offsetof(entry, a) == 0, "entry: Invalid offset for a");
static_assert(offsetof(entry, b) == 4, "entry: Invalid offset for b");
int main(void) {
boost::iostreams::mapped_file_source mmap("map");
assert(mmap.is_open());
const entry* data_begin = reinterpret_cast<const entry*>(mmap.data());
const entry* data_end = data_begin + mmap.size()/sizeof(entry);
for(const entry* ii=data_begin; ii!=data_end; ++ii)
std::cout << std::hex << ii->a << " " << ii->b << std::endl;
return 0;
}
鉴于 map
文件包含正确顺序的预期位,是否有任何其他原因可以避免使用 reinterpret_cast 来使用我的虚拟内存而不先复制它?
如果没有,为什么要强制用户通过 return 键入指针来执行 reinterpret_cast?
请回答所有问题以获得奖励积分:)
char*
表示原始字节数组,这就是 mapped_file::data 在大多数情况下的含义。
void*
会产生误导,因为它提供的有关包含类型的信息较少,并且需要更多设置才能使用 char*
- 我们知道文件内容是一些字节,char*
代表。
Template return 类型需要在库内部执行到该类型的转换,而在调用方执行此操作更有意义(因为库只提供原始文件内容的接口,而来电者明确知道这些内容是什么)。
In case the memory mapped file contains a sequence of 32 bit integers, if data()
returned a void*
, we could be able to static cast to std::uint32_t
directly.
不,不是真的。您仍然必须考虑(如果没有别的)字节序。 "one step conversion" 这个想法会让你产生一种错误的安全感。您忘记了文件中的字节与您想要进入程序的 32 位整数之间的整个转换层。即使该翻译恰好在您当前的系统和给定文件上是空操作,它仍然是一个翻译步骤。
获得一个字节数组(字面意思是 char*
指向的内容!)要好得多,然后您知道您必须做一些思考以确保您的指针转换有效并且您正在执行所需的任何其他工作。
返回 char *
似乎只是 boost::iostreams
实现的一个(特殊)设计决策。
其他 API,例如boost interprocess return void*
。
根据 sehe 的观察,UNIX mmap
specification (and malloc
) 也使用 void*
。
有点重复void* or char* for generic buffer representation?
请注意,当内存从一种架构写入并在不同的架构上读取时,可能需要 Lightness 在另一个答案中提到的翻译层。使用转换类型很容易解决字节序问题,但还需要考虑对齐。
关于静态转换:http://en.cppreference.com/w/cpp/language/static_cast 提及:
A prvalue of type pointer to void (possibly cv-qualified) can be
converted to pointer to any type. If the value of the original pointer
satisfies the alignment requirement of the target type, then the
resulting pointer value is unchanged, otherwise it is unspecified.
Conversion of any pointer to pointer to void and back to pointer to
the original (or more cv-qualified) type preserves its original value.
因此,如果要进行内存映射的文件是在具有不同对齐方式的不同体系结构上创建的,加载可能会失败(例如使用 SIGBUS),具体取决于体系结构和 OS。
或者更好的模板 <T*>
?
如果内存映射文件包含一个 32 位整数序列,如果 data()
returned a void*
,我们可以静态转换为 std::uint32_t
直接。
为什么 boost 作者选择 return char*
?
编辑:如前所述,如果可移植性成为问题,则需要进行翻译。但是说一个文件(或者在这种情况下是一块内存)是一个字节流而不是一个比特流,或者 IEEE754 双精度数,或者复杂的数据结构,在我看来是一个非常宽泛的陈述,需要一些更多解释。
即使必须处理字节顺序,能够直接映射到 be_uint32_t
的矢量(如建议的那样(并在此处实现)将使代码更具可读性:
struct be_uint32_t {
std::uint32_t raw;
operator std::uint32_t() { return ntohl(raw); }
};
static_assert(sizeof(be_uint32_t)==4, "POD failed");
是否 allowed/advised 转换为 be_uint32_t*
?为什么,或者为什么不?
应该使用哪种演员表?
EDIT2:因为似乎很难直截了当而不是讨论天气,所以 elaborator 的内存模型是由位、字节或单词组成的,我将重新举个例子:
#include <cstdint>
#include <memory>
#include <vector>
#include <iostream>
#include <boost/iostreams/device/mapped_file.hpp>
struct entry {
std::uint32_t a;
std::uint64_t b;
} __attribute__((packed)); /* compiler specific, but supported
in other ways by all major compilers */
static_assert(sizeof(entry) == 12, "entry: Struct size mismatch");
static_assert(offsetof(entry, a) == 0, "entry: Invalid offset for a");
static_assert(offsetof(entry, b) == 4, "entry: Invalid offset for b");
int main(void) {
boost::iostreams::mapped_file_source mmap("map");
assert(mmap.is_open());
const entry* data_begin = reinterpret_cast<const entry*>(mmap.data());
const entry* data_end = data_begin + mmap.size()/sizeof(entry);
for(const entry* ii=data_begin; ii!=data_end; ++ii)
std::cout << std::hex << ii->a << " " << ii->b << std::endl;
return 0;
}
鉴于 map
文件包含正确顺序的预期位,是否有任何其他原因可以避免使用 reinterpret_cast 来使用我的虚拟内存而不先复制它?
如果没有,为什么要强制用户通过 return 键入指针来执行 reinterpret_cast?
请回答所有问题以获得奖励积分:)
char*
表示原始字节数组,这就是 mapped_file::data 在大多数情况下的含义。
void*
会产生误导,因为它提供的有关包含类型的信息较少,并且需要更多设置才能使用 char*
- 我们知道文件内容是一些字节,char*
代表。
Template return 类型需要在库内部执行到该类型的转换,而在调用方执行此操作更有意义(因为库只提供原始文件内容的接口,而来电者明确知道这些内容是什么)。
In case the memory mapped file contains a sequence of 32 bit integers, if
data()
returned avoid*
, we could be able to static cast tostd::uint32_t
directly.
不,不是真的。您仍然必须考虑(如果没有别的)字节序。 "one step conversion" 这个想法会让你产生一种错误的安全感。您忘记了文件中的字节与您想要进入程序的 32 位整数之间的整个转换层。即使该翻译恰好在您当前的系统和给定文件上是空操作,它仍然是一个翻译步骤。
获得一个字节数组(字面意思是 char*
指向的内容!)要好得多,然后您知道您必须做一些思考以确保您的指针转换有效并且您正在执行所需的任何其他工作。
返回 char *
似乎只是 boost::iostreams
实现的一个(特殊)设计决策。
其他 API,例如boost interprocess return void*
。
根据 sehe 的观察,UNIX mmap
specification (and malloc
) 也使用 void*
。
有点重复void* or char* for generic buffer representation?
请注意,当内存从一种架构写入并在不同的架构上读取时,可能需要 Lightness 在另一个答案中提到的翻译层。使用转换类型很容易解决字节序问题,但还需要考虑对齐。
关于静态转换:http://en.cppreference.com/w/cpp/language/static_cast 提及:
A prvalue of type pointer to void (possibly cv-qualified) can be converted to pointer to any type. If the value of the original pointer satisfies the alignment requirement of the target type, then the resulting pointer value is unchanged, otherwise it is unspecified. Conversion of any pointer to pointer to void and back to pointer to the original (or more cv-qualified) type preserves its original value.
因此,如果要进行内存映射的文件是在具有不同对齐方式的不同体系结构上创建的,加载可能会失败(例如使用 SIGBUS),具体取决于体系结构和 OS。