如何将C API封装到RAII C ++类中? [英] How to encapsulate a C API into RAII C++ classes?

查看:147
本文介绍了如何将C API封装到RAII C ++类中?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为控制拥有项目的会话的库提供了一个C API,将C API封装到RAII C ++类中的最佳设计是什么?



例如:

  HANDLE OpenSession(STRING sessionID); 
void CloseSession(HANDLE hSession);
HANDLE OpenItem(HANDLE hSession,STRING itemID);
void CloseItem(HANDLE hItem);

此外,对于其中一种类型(Session或Item)有用的其他函数,相关对象的C ++成员函数。但他们在这里不需要。我主要感兴趣的是这些对象的构造和销毁,使用RAII来管理这些类的正确打开和关闭。



我的第一个想法为我的类的设计,是纯粹并指向RAII。包含的类接受一个容器对象作为构造函数的参数。

  class Session {
HANDLE const m_hSession;
public:
Session(STRING sessionID):m_hSession(OpenSession(sessionID)){}
〜Session(){CloseSession(m_hSession); }
};
class Item {
HANDLE const m_hItem;
public:
Item(HANDLE hSession,STRING itemID):m_hItem(OpenItem(hSession,itemID)){}
〜Item(){CloseItem(m_hItem); }
};

这种设计的缺点是允许一个坏的行为:Session对象可以被销毁(和CloseSession函数调用)在它的所有Item对象被破坏之前。这是恼人,因为它不应该发生。即使这种错误的行为是可能的,因此无效,使用C API,我想通过在C ++ API的设计避免。



这就是为什么我我想知道如何使用以下设计,其中会话包含其项目(这显示了实际的关系),并且是唯一的类能够构建和销毁项目。

  class Item {
HANDLE const m_hItem;
项目(HANDLE hSession,STRING itemID):m_hItem(OpenItem(hSession,itemID){}
〜Item(){CloseItem(m_hItem);}
friend class Session;
public:
};
class Session {
HANDLE const m_hSession;
typedef vector< Item *> VecItem;
VecItem m_vecItem;
Session sessionID):m_hSession(OpenSession(sessionID)){}
〜Session(){
for(size_t n = 0; n m_vecItem.clear();
CloseSession(m_hSession);
}
public:
Item * OpenItem(STRING itemID){
Item * p = new Item(m_hSession,itemID);
m_vecItem.push_back(p);
return p;
}
void CloseItem(Item * item){
VecItem: :iterator it = find(m_vecItem.begin(),m_vecItem.end(),item);
if(it!= m_vecItem.end()){
Item * p = * it; m_vecItem。擦除(it);删除p;
}
}
};

我认为这是确保会话在项目关闭前未关闭的唯一方法:在设计中反映出Item对象是
Session的成员,因此将在会话被销毁之前被销毁。



但是,它看起来有点对我来说很奇怪,因为它在Session类的接口中保留了这些函数OpenItem和CloseItem。我在寻找更多的RAII行(对我来说,这意味着使用一个构造函数),但不能想象一种方式封装它,将确保正确的销毁顺序。



此外,使用指针,new和delete是太老的世纪C ++。应该可以使用Item的一个向量(而不是Item *),以正确定义类Item的移动语义的代价,但是它的代价是允许Item的默认构造函数创建未初始化的第二类

解决方案

通过创建一个公共项目对象。



<添加另一个层(并使你的RAII一点更明确),你可以得到一些很漂亮。默认的复制构造函数和会话和项目的分配做正确的事情。在所有项目的HANDLE关闭后,会话的HANDLE将关闭。没有必要保留儿童的向量,共享指针跟踪所有的你...所以我认为它应该做你需要的一切。

  class SessionHandle 
{
explicit SessionHandle(HANDLE in_h):h(in_h){}
HANDLE h;
〜SessionHandle(){if(h)CloseSession(h); }
};

class ItemHandle
{
explicit ItemHandle(HANDLE in_h):h(in_h){}
HANDLE h;
〜ItemHandle(){if(h)CloseItem(h); }
};

类会话
{
显式会话(STRING sessionID):session_handle(OpenSession(sessionID))
{
}
shared_ptr< SessionHandle> ; session_handle;
};

class Item
{
Item(Session& s,STRING itemID):
item_handle(OpenItem(s.session_handle.get(),itemID)
session_handle(s.session_handle)
{
}
shared_ptr< ItemHandle> item_handle;
shared_ptr< SessionHandle> session_handle;
};


Given a C API to a library controlling sessions that owns items, what is the best design to encapsulate the C API into RAII C++ classes?

The C API looks like:

HANDLE OpenSession(STRING sessionID);
void CloseSession(HANDLE hSession);
HANDLE OpenItem(HANDLE hSession, STRING itemID);
void CloseItem(HANDLE hItem);

Plus other functions that are useful for one of these types (Session, or Item) and map directly to C++ member functions of the relevant object. But they are not needed here. My main interest is in the construction and destruction of these objects, using RAII to manage a correct opening and closing of these classes.

My first idea for the design of my classes, is pure and direct RAII. The contained class accepts a container object as the constructor parameter.

class Session {
    HANDLE const m_hSession;
public:
    Session(STRING sessionID): m_hSession(OpenSession(sessionID)) {}
    ~Session() { CloseSession(m_hSession); }
};
class Item {
    HANDLE const m_hItem;
public:
    Item(HANDLE hSession, STRING itemID): m_hItem(OpenItem(hSession, itemID)) {}
    ~Item() { CloseItem(m_hItem); }
};

This design have the disadvantage of allowing a bad behavior: a Session object can be destructed (and CloseSession function called) before all of its Item objects have been destructed. This is annoying, because it shouldn't happened. Even if this erroneous behavior is possible, hence not valid, using the C API, I'd like it to be avoided by design in the C++ API.

That's why I am wondering about using the following design where the Session contains its Items (this shows the actual relationship), and is the sole class able to construct and destroy Items.

class Item {
    HANDLE const m_hItem;
    Item(HANDLE hSession, STRING itemID): m_hItem(OpenItem(hSession, itemID) {}
    ~Item() { CloseItem(m_hItem); }
    friend class Session;
public:
};
class Session {
    HANDLE const m_hSession;
    typedef vector<Item *> VecItem;
    VecItem m_vecItem;
    Session(STRING sessionID): m_hSession(OpenSession(sessionID)) {}
    ~Session() {
        for (size_t n = 0 ; n < m_vecItem.size() ; ++n) delete m_vecItem[n];
        m_vecItem.clear();
        CloseSession(m_hSession);
        }
public:
    Item * OpenItem(STRING itemID) {
        Item *p = new Item(m_hSession, itemID);
        m_vecItem.push_back(p);
        return p;
        }
    void CloseItem(Item * item) {
        VecItem::iterator it = find(m_vecItem.begin(), m_vecItem.end(), item);
        if (it != m_vecItem.end()) {
            Item *p = *it; m_vecItem.erase(it); delete p;
            }
        }
};

It looks to me as the only way to ensure a Session is not closed before its Items are closed: reflecting in the design that the Item objects are members of the Session, and therefore will be destructed before the Session is destroyed.

However, it looks a bit weird to me, as it leaves these functions OpenItem and CloseItem in the interface of the Session class. I was looking for something more in the line of RAII (for me, this means using a constructor for Item), but cannot imagine a way to encapsulate it that would ensure correct destruction order.

Furthermore, using pointers, new and delete is too much old century C++. It should be possible to use a vector of Item (instead of Item*), at the price of defining correctly the move semantics for class Item, but it would be at the price of allowing a default constructor for Item that would create uninitialized second class citizen Item objects.

Any better design ideas?

解决方案

By adding another layer (and making your RAII a little more explicit) you can get something pretty neat. Default copy constructors and assignment for Sessions and Items do the right thing. The HANDLE for the session will be closed after the HANDLE for all the items is closed. There's no need to keep vectors of children around, the shared pointers track all that for you ... So I think it should do everything you need.

class SessionHandle
{
   explicit SessionHandle( HANDLE in_h ) : h(in_h) {}
   HANDLE h;
   ~SessionHandle() { if(h) CloseSession(h); }
};

class ItemHandle
{
   explicit ItemHandle( HANDLE in_h ) : h(in_h) {}
   HANDLE h;
   ~ItemHandle() { if(h) CloseItem(h); }
};

class Session
{
   explicit Session( STRING sessionID ) : session_handle( OpenSession(sessionID) )
   {
   }
   shared_ptr<SessionHandle> session_handle;
};

class Item
{
   Item( Session & s, STRING itemID ) : 
     item_handle( OpenItem(s.session_handle.get(), itemID ) ), 
     session_handle( s.session_handle )
   {
   }
   shared_ptr<ItemHandle> item_handle;
   shared_ptr<SessionHandle> session_handle;
};

这篇关于如何将C API封装到RAII C ++类中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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