编写 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<unique_ptr<MyObject>>
进行排序。
您必须考虑需要执行的每个操作并确定必要的性能。例如,为了找到具有匹配 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 内部对象的外部引用?的主要内容,如果未能解决你的问题,请参考以下文章
nodejs v8 新特性——利用N-API编写c++ node扩展