编写 C++ API - 如何保持对 API 内部对象的外部引用?

Posted

技术标签:

【中文标题】编写 C++ API - 如何保持对 API 内部对象的外部引用?【英文标题】:Writing C++ API - how to keep external references to API internal objects? 【发布时间】:2015-05-26 20:25:17 【问题描述】:

所以我正在用 C++ 编写一个 API,以便在我将要编写的另一个 GUI 应用程序中使用。 API 将允许用户创建“MyObject”的实例并修改该对象的属性,但对象本身不会暴露给客户端,只有该对象的 ID。比如:

Object_ID identifier = myApiCreateObject();
myApiModifyProperty(identifier, "PROPERTY_NAME", "value");

因此,标识符充当特定 MyObject 实例的外部处理程序。

截至目前,Object_ID 定义如下:

typedef int Object_ID;

目前所有 MyObject 实例都存储在我的 API 中的 std::vector 中。 Object_ID 只是所需实例所在的向量中的索引。

这种方法的问题是我不知道如何处理从向量中删除 MyObject 的实例。例如,假设我创建了 10 个 MyObject 实例,我想删除索引 5 处的实例,我想做如下操作:

myApiDeleteObject(handlerForIndex5);

通过这样做,我的 API 在内部会从 std::vector 中删除该对象,然后必须转移索引 > 5 处的所有对象。这将导致我的外部处理程序不再引用正确的对象

所以仅使用数组的索引本身是不够的,但我不知道有更好的选择,而不必将 MyObject 类公开给客户端。

编辑

这是一个突出当前问题的更新示例:

API 在内部对对象列表执行某些算法,其中一些算法需要对向量进行排序作为一个步骤。

所以我的 GUI 会做类似的事情:

myApiBeginCalculations(); 

然后在内部 API 会做这样的事情:

myApiBeginCalculations()
 
     //Start algorithm
     .......
     Sort(vector);
    //Continue with algorithm

那么假设算法完成后,用户想要修改给定的 MyObject 实例并重新开始:

myApiBeginCalculations();
myApiModifyProperty(myHandler, "PROPERTY", "VALUE");
myApiBeginCalculations();
myApiDeleteObject(myHandler); 
myAPiBeginCalculations();

在内部,myApi 将对 MyObject 实例做很多事情,我需要一种可靠的方法来跟踪客户端上的各个实例,即使它们被打乱。

【问题讨论】:

原版Game Programming Gems 的早期章节对使用句柄来引用库中的对象(资源)进行了很好的讨论。有不同级别的复杂性和功能(如可调试性)的各种实现。 【参考方案1】:

您可以使用std::map 代替std::vector。因此,您可以在需要时快速查找并删除对象。

std::map<int, Object> Object_directory

【讨论】:

【参考方案2】:

您需要使用基于对每个对象都唯一且对每个对象保持不变的东西的 ID。显然,您不断重新排列的向量中的索引不符合条件。

你还没有描述对象的属性,所以我不能说是否已经有适合这种用途的东西,但如果没有,那么你可以添加一些东西。您可以在创建每个对象时为其分配一个 ID,或者您可以在堆上分配对象,以便它们的地址保持一致,例如,对 vector&lt;unique_ptr&lt;MyObject&gt;&gt; 进行排序。

您必须考虑需要执行的每个操作并确定必要的性能。例如,为了找到具有匹配 ID 的对象而对向量进行线性搜索可能对于某些目的来说太慢了。在这种情况下,您必须弄清楚如何避免这种线性搜索,可能是通过将地图放在一边或其他方式,代价是必须在其他操作期间保持地图更新。

【讨论】:

Unique_ptrs 实际上看起来是个不错的主意。通过将地址存储在处理程序中,我可以在恒定时间内修改属性(我认为)。谢谢【参考方案3】:

我建议根本不生成 ID 号。只需使用指向实际 Object 实例的真实指针即可。要对客户端隐藏它,您可以使用void*uintptr_t,并在需要时让您的API 函数将该值类型转换为Object* 指针。您仍然可以在std::vector 中跟踪Object 实例,以便您可以对对象执行算法,但std:vector 的顺序对客户来说并不重要,删除任何给定的Object 不会使其他对象 ID 无效。

typedef uintptr_t Object_ID;

typedef std::vector<Object*> ObjectVector;
typedef ObjectVector::iterator ObjectVectorIter;

ObjectVector objVec;

Object_ID myApiCreateObject()

    try
    
        std::auto_ptr<Object> obj(new Object);
        objVec.push_back(obj.get());
        return reinterpret_cast<Object_ID>(obj.release());
    
    catch (const std::exception&)
    
        return 0;
    


ObjectVectorIter myApiFindObject(Object_ID identifier)

    Object *obj = reinterpret_cast<Object*>(identifier);
    return std::find(objVec.begin(), objVec.end(), obj);


void myApiModifyProperty(Object_ID identifier, const char* propName, const char* propValue)

    ObjectVectorIter iter = myApiFindObject(identifier);
    if (iter != objVec.end())
        iter->property[propName] = propValue;


void myApiDeleteObject(Object_ID identifier)

    ObjectVectorIter iter = myApiFindObject(identifer);
    if (iter != objVec.end())
    
        Object* obj = *iter;
        objVec.erase(iter);
        delete obj;
    

或者,如果您使用的是 C++11:

typedef uintptr_t Object_ID;

typedef std::shared_ptr<Object> ObjectPtr;
typedef std::vector<ObjectPtr> ObjectVector;
typedef ObjectVector::iterator ObjectVectorIter;

ObjectVector objVec;

Object_ID myApiCreateObject()

    try
    
        ObjectPtr obj = std::make_shared<Object>();
        objVec.push_back(obj);
        return reinterpret_cast<Object_ID>(obj.get());
    
    catch (const std::exception&)
    
        return 0;
    


ObjectVectorIter myApiFindObject(Object_ID identifier)

    Object *obj = reinterpret_cast<Object*>(identifier);
    return std::find_if(objVec.begin(), objVec.end(), [obj](const ObjectPtr &p) return p.get() == obj; );


void myApiModifyProperty(Object_ID identifier, const char* propName, const char* propValue)

    ObjectVectorIter iter = myApiFindObject(identifier);
    if (iter != objVec.end())
        (*iter)->property[propName] = propValue;


void myApiDeleteObject(Object_ID identifier)

    ObjectVectorIter iter = myApiFindObject(identifier);
    if (iter != vec.end())
        objVec.erase(iter);

【讨论】:

也感谢您的回答

以上是关于编写 C++ API - 如何保持对 API 内部对象的外部引用?的主要内容,如果未能解决你的问题,请参考以下文章

用windows API 编写一个C++窗口

如何制作一个 PInvoke 友好的原生 API?

F28335第十六篇——内部Flash操作

nodejs v8 新特性——利用N-API编写c++ node扩展

如何使用 Windows 运行时使用 C# 实现 C++ API?

优雅对API进行内部升级改造