Как реализовать API для распределенной карты в C ++?

Я реализую распределенную карту в 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, похожий на требования моей распределенной карте?

3

Решение

Исходя из вашего термина «распределенная карта», я делаю следующие предположения:

  • Подмножество данных доступно локально, и для набора данных, который не является некоторой удаленной выборкой, нужно будет выполнить.
  • Записи в возвращаемый объект не должны автоматически сохраняться в хранилище данных. Вместо этого следует сделать явный запрос на обновление.

Если это так, то итераторы — это не то, что вам нужно, и вам не нужна модель контейнера 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);
}
1

Другие решения

Я бы сказал, что вариант 3 лучше. Вы можете просто эмулировать его, используя один из стандартных типов интеллектуальных указателей, представленных в C ++ 11, поэтому вы по-прежнему создаете указатель, но пользователю не нужно его освобождать. Так что-то вроде:

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

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector