Я реализую распределенную карту в C ++ и ищу хороший дизайн API.
Первый и простой вариант — сделать его в точности как std :: map. Проблема с итератором.
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
}
Проблема в том, что проверка 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;
}
Итак, что вы думаете, лучший подход?
Знаете ли вы хороший API, похожий на требования моей распределенной карте?
Исходя из вашего термина «распределенная карта», я делаю следующие предположения:
Если это так, то итераторы — это не то, что вам нужно, и вам не нужна модель контейнера STL. Концепция итератора C ++ требует, чтобы вы реализовали предварительное увеличение (++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);
}
Я бы сказал, что вариант 3 лучше. Вы можете просто эмулировать его, используя один из стандартных типов интеллектуальных указателей, представленных в C ++ 11, поэтому вы по-прежнему создаете указатель, но пользователю не нужно его освобождать. Так что-то вроде:
std::unqiue_ptr<Person> person = map.get("sample");
if(person) {
person->makeMeASandwitch();
}