Используя следующий .thrift
файл
struct myElement {
1: required i32 num,
}
struct stuff {
1: optional map<i32,myElement> mymap,
}
Я получаю бережливый класс с картой STL. Экземпляр этого класса долгоживущий (я добавляю и удаляю из него, а также записываю на диск с помощью TSimpleFileTransport).
Я хотел бы расширить myElement
в C++, расширения не должны влиять на сериализованную версию этого объекта (и этот объект не используется ни в одном другом языке). Какой чистый способ добиться этого?
Я рассмотрел следующее, но они не казались чистыми:
- Make a second, non thrift map that is indexed with the same key
- keeping both in sync could prove to be a pain
- Модифицируйте сгенерированный код либо путем постобработки сгенерированного заголовка (включая взлом пропроцессора).
- Аналогично #2, но измените сторону генерации, чтобы включить следующее в сгенерированную структуру, а затем определите NAME_CXX_EXT в принудительно включенном заголовке
#ifdef NAME_CXX_EXT NAME_CXX_EXT ... #endif
Все вышеперечисленное кажется довольно неприятным
Решение, которое я сейчас выберу:
[Это все псевдокод, не проверял эту копию на компиляцию]
Следующий сгенерированный код, который я не могу изменить (хотя я могу изменить карту на набор)
class GeneratedElement {
public:
// ...
int32_t num;
// ...
};
class GeneratedMap {
public:
// ...
std::map<int32_t, GeneratedElement> myGeneratedMap;
// ...
};
// End of generated code
В другом месте приложения:
class Element {
public:
GeneratedElement* pGenerated; // <<== ptr into element of another std::map!
time_t lastAccessTime;
};
class MapWrapper {
private:
GeneratedMap theGenerated;
public:
// ...
std::map<int32_t, Element> myMap;
// ...
void doStuffWIthBoth(int32_t key)
{
// instead of
// theGenerated.myGeneratedMap[key].num++; [lookup in map #1]
// time(&myMap[key].lastAccessTime); [lookup in map #2]
Element& el=myMap[key];
el.pGenerated->num++;
time(&el.lastAccessTime);
}
};
Я хотел избежать двойного поиска карты для каждого доступа (хотя я знаю, что сложность остается прежней, это все еще два поиска).
Я решил, что могу гарантировать, что все вставки и удаления в/из theGenerated
) выполняются в одном месте, и в том же месте, где я заполняю/удаляю соответствующую запись в myMap
, я тогда смогу инициализировать Element:: pGenerated в соответствующий элемент в theGenerated.myGeneratedMap
Это не только позволит мне сэкономить половину времени поиска, я даже могу изменить myMap на лучший тип контейнера для моего типа ключа (скажем, hash_map или даже карту мультииндекса boost)
Сначала мне это показалось плохой идеей. С std::vector и std::dqueue я вижу, как это может быть проблемой, поскольку значения будут перемещаться, делая указатели недействительными. Учитывая, что std::map реализован с древовидной структурой, действительно ли есть время, когда элемент карты будет перемещен? (мои приведенные выше предположения были подтверждены обсуждением в введите здесь описание ссылки)
Хотя я, вероятно, не буду предоставлять метод доступа к каждому члену myElement
или любому синтаксическому сахару (например, перегрузка [] () и т. д.), это позволяет мне обрабатывать эти элементы почти согласованным образом. Единственный ключ в том, что (помимо вставки) я никогда не ищу членов mymap
напрямую.