如何转发声明共享内存的自定义unique_ptr

时间:2019-01-02 23:29:07

标签: c++ unique-ptr forward-declaration

我已经从Rian Quinn博士的著作“使用C / C ++进行动手系统编程”中进行了以下修改。它用一个unique_ptr {}包装mmap。它几乎就像我需要的那样工作。我想向前声明指向映射内存的指针,以便可以将其用作私有类成员。我的应用程序是多任务,每个单线程,具有共享内存的硬实时,用于跨任务通信。 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";
}

2 个答案:

答案 0 :(得分:0)

在对您的代码进行了一些汇编和编译之后(我添加了一些调试打印文件进行演示),请参阅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>来定义指向映射对象的指针。

请参见the example with myClass

答案 1 :(得分:0)

std::unique_ptr<myStruct,mmap_deleter> ptr;

尝试使用默认构造 mmap_deleter,因为这是对象的定义。您的size_t构造函数禁止编译器生成默认值(在这里对您来说)。

您可以改为将其作为模板参数提供,该参数还允许您根据需要将~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();
}