2013-08-21 12 views
3

我正在C++中实现分布式映射并寻找一个好的API设计。 首先和直接的选择是使它完全像std :: map。问题在于迭代器。如何在C++中实现分布式映射的API?

IMap<std::string,Person>::iterator it; 
it = map.find("sample"); 
if(it == map.end()){ 
    //NULL 
} 
for(it = map.begin(); it != map.end(); it++){ 
    //iterate 
} 

在分布式环境中(至少在我正在实现的环境中),没有地图的开始和结束。无论如何,它并没有被排序,所以返回一个迭代器看起来不像一个选项。

第二种选择是复制像下面的返回值类:

Person emptyPerson; 
Person person = map.get("sample"); 
if(person == emptyPerson){ 
    //NULL 
} 

问题是与空检查看起来很奇怪。您可以首先询问它是否可用,然后获取对象,但要求是这些操作必须是原子的。

第三个选项是返回指针:

Person* person = map.get("sample"); 
if(person == NULL){ 
    //NULL 
} 

我不想做这种方式,因为它是很容易出错。用户需要删除我在内部创建的指针。

我想返回一个类,像包装用户对象:

value_reference<std::map, Person> person = map.get("sample"); 
if(value_reference.hasValue()){ 
    Person p = value_reference; 
} 

那么你认为什么是最好的办法是什么?

你知道任何类似于我的分布式映射的需求吗?

+0

在C++中,大多数更新操作使迭代器失效。所以,如果你获得一个迭代器,我认为你可能会随时使迭代器失效(如果你没有相应处理它)。另一方面,你存储一个Person,而不是Person指针,所以你的方法不应该返回Person *。实际上,Person对象上的更新可能不会影响相应地图上的原始对象。将Person封装到另一个容器类中并使用它来访问原始对象可能会更好。它可能会将访问包含许多数据的有效数据的问题分开。 – phoad

+0

即你可以保留一个像RemotePerson这样的类,并且你的容器可以包含这个类的对象。所以你可能有方法来检查这个单个对象的可用性等等。因此,地图可能只能处理其包含这些对象的工作,并保持对人的原始位置的引用。 RemotePerson的工作可能是检查Person是否仍然存在等。您可以将RemotePerson自动转换为Person,如共享指针。 – phoad

回答

1

根据您的术语“分布图”我做出如下假设:

  • 数据的一个子集是本地可用,而对于这组数据是不需要执行一些远程获取。
  • 写入返回的对象不应该自动保留在数据存储区中。应该做一个明确的更新请求。

如果这是真的,那么迭代器不是你想要的,你也不想要STL容器模型。 C++ Iterator概念要求你实现预增(++i)操作符,如果你的数据是无序的并且分布在多个节点上,那么请求“给我下一个条目”是没有意义的。

,如果你想模仿STL容器和迭代器的互操作性考虑,您可以创建一个可怕的杂牌组装电脑:有地图的end()方法返回一个定点迭代器实例,有operator++()你的迭代器返回此相同的哨兵。实际上,每个迭代器都会指向“地图中的最后一个元素”。我强烈建议不要采取这种方法,除非有必要,我认为不会。

这听起来像你想要的是一个简单的CRUD模型,其中更新必须明确要求。在这种情况下,你的API看起来是这样的:

template <typename TKey, typename TValue> 
class IMap<TKey, TValue> 
{ 
public: 
    void create(TKey const & key, TValue const & value) = 0; 
    std::unique_ptr<TValue> retrieve(TKey const & key) = 0; 
    bool update(TKey const & key, TValue const & value) = 0; 
    bool remove(TKey const & key) = 0; 
}; 

在检索的情况下,你会简单地返回一个空指针如你所说。 std::unique_ptr<>将确保调用者将删除已分配的对象或明确拥有它。

“返回指向新分配对象的指针”情况的替代方法是让调用方传入引用,如果在地图中找到该值,该方法将返回true。例如,这将使调用者直接将对象检索到数组槽或其他本地结构中,而不需要中介堆分配。

bool retrieve(TKey const & key, TValue & value) = 0; 

使用这种方法看起来是这样的:

Person person; 

if (map.retrieve("sample", person)) { 
    std::cout << "Found person: " << person << std::endl; 
} else { 
    std::cout << "Did not find person." << std::endl; 
} 

你可以同时提供过载过,和一个返回一个指针可以在其他方面默认实现:

template <typename TKey, typename TValue> 
std::unique_ptr<TValue> IMap<TKey, TValue>::retrieve(TKey const & key) 
{ 
    TValue v; 
    return std::unique_ptr<TValue>(retrieve(key, v) ? new TValue(v) : nullptr); 
} 
+0

我几乎同意所有的东西,但我更喜欢MJD答案中的return-by-unique_ptr。如果STL算法无用,则不要实现STL接口。 – DanielKO

+0

@DanielKO是的,这是一个权衡。没有什么能够阻止接口指定两个重载,然后调用者可以使用最有意义的接口。返回指针的那个默认情况下可以很容易地用另一个来实现。 ('std :: unique_ptr retrieve(TKey const&key){TValue v; return retrieve(key,v)?std :: unique_ptr (new TValue(v)):std :: unique_ptr (nullptr);} ) – cdhowie

+0

我看到解决方案需要C++ 11 std :: unique_ptr。我不知道它是否是一种正确的方式,但我不想强迫我的api用户使用C++ 11,因为每个人都需要花时间切换到C++ 11。 – sancar

1

我会说选项3是最好的。您可以使用C++ 11中引入的标准智能指针类型之一来模拟它,因此您仍然创建一个指针,但用户不必释放它。因此,像:

std::unqiue_ptr<Person> person = map.get("sample"); 
if(person) { 
     person->makeMeASandwitch(); 
} 
+0

我看到解决方案需要C++ 11 std :: unique_ptr。我不知道这是否是正确的方式,但我不想强迫我的api用户使用C++ 11,因为它需要时间让大家切换到C++ 11 – sancar