如何为共享内存转发声明自定义 unique_ptr
How to forward declare custom unique_ptr for shared memory
我从 Rian Quinn 博士的书中 "Hands-On System Programming with C/C++" 中提取了下面的例子,只是稍作修改。它用 unique_ptr{} 包装 mmap。它几乎就像我需要的那样工作。我想转发声明指向映射内存的指针,以便我可以将它用作私有 class 成员。我的应用程序是多任务的,每个都是单线程的,硬实时的,带有用于跨任务通信的共享内存。 Quinn 博士有第二个共享内存示例,比这里显示的稍长,但这个示例说明了问题。 shm_open/mmap 在时间上相对昂贵。我需要在安装过程中完成一次,但不知道如何去做。我知道如何使用原始指针来做到这一点。我正在使用 g++ 4.8.5.
我试过:
std::unique_ptr<myStruct,mmap_deleter> ptr;
这导致:
/usr/include/c++/4.8.2/tuple:132:22: error: no matching function for call to ‘mmap_deleter::mmap_deleter()’
: _M_head_impl() { }
#include <memory>
#include <iostream>
#include <string.h>
#include <sys/mman.h>
constexpr auto PROT_RW = PROT_READ | PROT_WRITE;
constexpr auto MAP_ALLOC = MAP_PRIVATE | MAP_ANONYMOUS;
class mmap_deleter
{
std::size_t m_size;
public:
mmap_deleter(std::size_t size) :
m_size{size}
{ }
void operator()(void *ptr) const
{
munmap(ptr, m_size);
}
};
template<typename T>
auto mmap_unique()
{
if (auto ptr = mmap(0, sizeof(T), PROT_RW, MAP_ALLOC, -1, 0)) {
auto obj = new (ptr) T(args...);
auto del = mmap_deleter(sizeof(T));
return std::unique_ptr<T, mmap_deleter>(obj, del);
}
throw std::bad_alloc();
}
struct myStruct{
double foo;
double bar;
};
// Forward declare pointer, neither compiles
std::unique_ptr<myStruct> ptr;
// or
// std::unique_ptr<myStruct,mmap_deleter> ptr;
int main()
{
ptr = mmap_unique<myStruct>();
ptr->foo = 55.;
std::cout << ptr->foo << '\n';
}
这是一个可以编译的愚蠢示例,运行 使用原始指针来说明我想用智能指针做什么。
// myClass.h
class myClass
{
public:
myClass();
~myClass();
int get_i();
int get_j();
private:
void myFunc1();
void myFunc2();
void myFunc3();
struct myStruct{
int i;
int j;
};
// FORWARD Declaration of ptr here!!!!!!!!!!!!!!!!!!!!!
// I would like to use a smart pointer
myStruct* ptr_myStruct{nullptr};
};
#include <sys/mman.h>
#include <iostream>
#include <string.h>
#include <cerrno>
//#include "myClass.h"
myClass::myClass(){
// Set the pointer to the mmap'ed address
ptr_myStruct = (myStruct*)mmap(NULL,sizeof(myStruct),PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS, -1, 0);
memset(ptr_myStruct,0,sizeof(myStruct));
// The three member functions use ptr_myStruct
myFunc1();
myFunc2();
myFunc3();
}
myClass::~myClass(){
munmap(ptr_myStruct,sizeof(myStruct));
ptr_myStruct = nullptr;
}
void myClass::myFunc1(){
ptr_myStruct->i++;
}
void myClass::myFunc2(){
ptr_myStruct->j++;
}
void myClass::myFunc3(){
ptr_myStruct->i++;
}
int myClass::get_i(){return ptr_myStruct->i;}
int myClass::get_j(){return ptr_myStruct->j;}
int main(){
myClass a;
std::cout<< a.get_i()<<" "<<a.get_j()<<"\n";
}
经过一些修改后,您的代码确实可以编译和工作(我添加了一些调试打印来演示它),请参阅 live example。 (另外,请注意 mmap_deleter
不会调用 obj
的析构函数,但它应该调用)。
鉴于您使用的是 C++14,我建议删除 mmap_deleter
并简化代码如下:
template <typename T>
using unique_mapped_ptr = std::unique_ptr<T, void(*)(T*)>;
template<typename T, typename ...Args>
unique_mapped_ptr<T> mmap_unique(Args... args)
{
constexpr auto PROT_RW = PROT_READ | PROT_WRITE;
constexpr auto MAP_ALLOC = MAP_PRIVATE | MAP_ANONYMOUS;
if (auto ptr = mmap(0, sizeof(T), PROT_RW, MAP_ALLOC, -1, 0)) {
return {
new (ptr) T{args...},
[](T*p) {
p->~T();
munmap(p, sizeof(T));
}
};
}
throw std::bad_alloc();
}
然后您可以使用 unique_mapped_ptr<myStruct>
定义指向映射对象的指针。
行
std::unique_ptr<myStruct,mmap_deleter> ptr;
尝试默认构造一个mmap_deleter
,因为那是一个对象的定义。您的 size_t
构造函数会抑制编译器生成的默认值(此处对您来说是 good)。
您可以改为将其作为模板参数提供,这还允许您根据需要将 ~T
应用于您的对象。
template <typename T>
struct mmap_deleter
{
void operator()(T* ptr) const
{
ptr->~T();
munmap(ptr, sizeof(T));
}
};
template<typename T, typename... Args>
std::unique_ptr<T, mmap_deleter<T>> mmap_unique(Args&&... args)
{
if (auto ptr = mmap(0, sizeof(T), PROT_RW, MAP_ALLOC, -1, 0)) {
return { new (ptr) T(std::forward<Args>(args)...) };
}
throw std::bad_alloc();
}
我从 Rian Quinn 博士的书中 "Hands-On System Programming with C/C++" 中提取了下面的例子,只是稍作修改。它用 unique_ptr{} 包装 mmap。它几乎就像我需要的那样工作。我想转发声明指向映射内存的指针,以便我可以将它用作私有 class 成员。我的应用程序是多任务的,每个都是单线程的,硬实时的,带有用于跨任务通信的共享内存。 Quinn 博士有第二个共享内存示例,比这里显示的稍长,但这个示例说明了问题。 shm_open/mmap 在时间上相对昂贵。我需要在安装过程中完成一次,但不知道如何去做。我知道如何使用原始指针来做到这一点。我正在使用 g++ 4.8.5.
我试过:
std::unique_ptr<myStruct,mmap_deleter> ptr;
这导致:
/usr/include/c++/4.8.2/tuple:132:22: error: no matching function for call to ‘mmap_deleter::mmap_deleter()’ : _M_head_impl() { }
#include <memory>
#include <iostream>
#include <string.h>
#include <sys/mman.h>
constexpr auto PROT_RW = PROT_READ | PROT_WRITE;
constexpr auto MAP_ALLOC = MAP_PRIVATE | MAP_ANONYMOUS;
class mmap_deleter
{
std::size_t m_size;
public:
mmap_deleter(std::size_t size) :
m_size{size}
{ }
void operator()(void *ptr) const
{
munmap(ptr, m_size);
}
};
template<typename T>
auto mmap_unique()
{
if (auto ptr = mmap(0, sizeof(T), PROT_RW, MAP_ALLOC, -1, 0)) {
auto obj = new (ptr) T(args...);
auto del = mmap_deleter(sizeof(T));
return std::unique_ptr<T, mmap_deleter>(obj, del);
}
throw std::bad_alloc();
}
struct myStruct{
double foo;
double bar;
};
// Forward declare pointer, neither compiles
std::unique_ptr<myStruct> ptr;
// or
// std::unique_ptr<myStruct,mmap_deleter> ptr;
int main()
{
ptr = mmap_unique<myStruct>();
ptr->foo = 55.;
std::cout << ptr->foo << '\n';
}
这是一个可以编译的愚蠢示例,运行 使用原始指针来说明我想用智能指针做什么。
// myClass.h
class myClass
{
public:
myClass();
~myClass();
int get_i();
int get_j();
private:
void myFunc1();
void myFunc2();
void myFunc3();
struct myStruct{
int i;
int j;
};
// FORWARD Declaration of ptr here!!!!!!!!!!!!!!!!!!!!!
// I would like to use a smart pointer
myStruct* ptr_myStruct{nullptr};
};
#include <sys/mman.h>
#include <iostream>
#include <string.h>
#include <cerrno>
//#include "myClass.h"
myClass::myClass(){
// Set the pointer to the mmap'ed address
ptr_myStruct = (myStruct*)mmap(NULL,sizeof(myStruct),PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS, -1, 0);
memset(ptr_myStruct,0,sizeof(myStruct));
// The three member functions use ptr_myStruct
myFunc1();
myFunc2();
myFunc3();
}
myClass::~myClass(){
munmap(ptr_myStruct,sizeof(myStruct));
ptr_myStruct = nullptr;
}
void myClass::myFunc1(){
ptr_myStruct->i++;
}
void myClass::myFunc2(){
ptr_myStruct->j++;
}
void myClass::myFunc3(){
ptr_myStruct->i++;
}
int myClass::get_i(){return ptr_myStruct->i;}
int myClass::get_j(){return ptr_myStruct->j;}
int main(){
myClass a;
std::cout<< a.get_i()<<" "<<a.get_j()<<"\n";
}
经过一些修改后,您的代码确实可以编译和工作(我添加了一些调试打印来演示它),请参阅 live example。 (另外,请注意 mmap_deleter
不会调用 obj
的析构函数,但它应该调用)。
鉴于您使用的是 C++14,我建议删除 mmap_deleter
并简化代码如下:
template <typename T>
using unique_mapped_ptr = std::unique_ptr<T, void(*)(T*)>;
template<typename T, typename ...Args>
unique_mapped_ptr<T> mmap_unique(Args... args)
{
constexpr auto PROT_RW = PROT_READ | PROT_WRITE;
constexpr auto MAP_ALLOC = MAP_PRIVATE | MAP_ANONYMOUS;
if (auto ptr = mmap(0, sizeof(T), PROT_RW, MAP_ALLOC, -1, 0)) {
return {
new (ptr) T{args...},
[](T*p) {
p->~T();
munmap(p, sizeof(T));
}
};
}
throw std::bad_alloc();
}
然后您可以使用 unique_mapped_ptr<myStruct>
定义指向映射对象的指针。
行
std::unique_ptr<myStruct,mmap_deleter> ptr;
尝试默认构造一个mmap_deleter
,因为那是一个对象的定义。您的 size_t
构造函数会抑制编译器生成的默认值(此处对您来说是 good)。
您可以改为将其作为模板参数提供,这还允许您根据需要将 ~T
应用于您的对象。
template <typename T>
struct mmap_deleter
{
void operator()(T* ptr) const
{
ptr->~T();
munmap(ptr, sizeof(T));
}
};
template<typename T, typename... Args>
std::unique_ptr<T, mmap_deleter<T>> mmap_unique(Args&&... args)
{
if (auto ptr = mmap(0, sizeof(T), PROT_RW, MAP_ALLOC, -1, 0)) {
return { new (ptr) T(std::forward<Args>(args)...) };
}
throw std::bad_alloc();
}