编写 C++ API - 如何保持对 API 内部对象的外部引用?
Writing C++ API - how to keep external references to API internal objects?
所以我正在用 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 class 暴露给客户端。
编辑
这是一个突出当前问题的更新示例:
API 在内部对对象列表执行某些算法,其中一些算法需要对向量进行排序作为一个步骤。
所以我的 GUI 会做类似的事情:
myApiBeginCalculations();
然后在内部 API 会做这样的事情:
myApiBeginCalculations()
{
//Start algorithm
.......
Sort(vector);
//Continue with algorithm
}
然后假设该算法完成后,用户想要修改给定的 MyObject 实例并重新开始:
myApiBeginCalculations();
myApiModifyProperty(myHandler, "PROPERTY", "VALUE");
myApiBeginCalculations();
myApiDeleteObject(myHandler);
myAPiBeginCalculations();
在内部,myApi 将对 MyObject 实例做很多事情,我需要一种可靠的方法来跟踪客户端上的各个实例,即使它们四处乱窜。
您可以使用 std::map
代替 std::vector
。因此,您可以在需要时快速查找并删除对象。
std::map<int, Object> Object_directory
您需要根据对每个对象都是唯一的并且对每个对象保持不变的内容来使用 ID。显然,您不断重新排列的向量的索引不符合条件。
你没有描述对象的属性所以我不能说是否已经有适合这种用途的东西,但如果没有那么你可以添加一些东西。您可以在创建对象时为每个对象分配一个 ID,或者您可以在堆上分配对象,以便它们的地址在您进行排序时保持一致,例如,对 vector<unique_ptr<MyObject>>
.
进行排序
您必须考虑需要执行的每个操作并计算出必要的性能。例如,通过向量进行线性搜索以查找具有匹配 ID 的对象对于某些目的来说可能太慢了。在那种情况下,您将不得不弄清楚如何避免线性搜索,也许通过将地图放在一边或其他东西,代价是必须在其他操作期间更新地图。
我建议根本不要生成 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 以用于我将要编写的另一个 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 class 暴露给客户端。
编辑
这是一个突出当前问题的更新示例:
API 在内部对对象列表执行某些算法,其中一些算法需要对向量进行排序作为一个步骤。
所以我的 GUI 会做类似的事情:
myApiBeginCalculations();
然后在内部 API 会做这样的事情:
myApiBeginCalculations()
{
//Start algorithm
.......
Sort(vector);
//Continue with algorithm
}
然后假设该算法完成后,用户想要修改给定的 MyObject 实例并重新开始:
myApiBeginCalculations();
myApiModifyProperty(myHandler, "PROPERTY", "VALUE");
myApiBeginCalculations();
myApiDeleteObject(myHandler);
myAPiBeginCalculations();
在内部,myApi 将对 MyObject 实例做很多事情,我需要一种可靠的方法来跟踪客户端上的各个实例,即使它们四处乱窜。
您可以使用 std::map
代替 std::vector
。因此,您可以在需要时快速查找并删除对象。
std::map<int, Object> Object_directory
您需要根据对每个对象都是唯一的并且对每个对象保持不变的内容来使用 ID。显然,您不断重新排列的向量的索引不符合条件。
你没有描述对象的属性所以我不能说是否已经有适合这种用途的东西,但如果没有那么你可以添加一些东西。您可以在创建对象时为每个对象分配一个 ID,或者您可以在堆上分配对象,以便它们的地址在您进行排序时保持一致,例如,对 vector<unique_ptr<MyObject>>
.
您必须考虑需要执行的每个操作并计算出必要的性能。例如,通过向量进行线性搜索以查找具有匹配 ID 的对象对于某些目的来说可能太慢了。在那种情况下,您将不得不弄清楚如何避免线性搜索,也许通过将地图放在一边或其他东西,代价是必须在其他操作期间更新地图。
我建议根本不要生成 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);
}