开发者

Extending a thrift generated object in C++

Using the following .thrift file

struct myElement {
  1: required i32 num,
}

struct stuff {
  1: optional map<i32,myElement> mymap,
}

I get thrift-generated class with an STL map. The instance of this class is long-lived (I append and remove from it as well as write it to disk using TSimpleFileTransport).

I would like to extend myElement in C++, the extenstions should not affect the serialized version of this object (and this object is not used in any other language). Whats a clean way to acomplish that?

I contemplated the following, but they didn't seem clean:

  1. Make a second, non thrift map that is indexed with the same key
    • keeping both in sync could prove to be a pain
  2. Modify the generated code either by post-processing of the generated header (incl. proprocessor hackery).
  3. Similar to #2, but modify the generation side to include the following in the generated struct and then define NAME_CXX_EXT in a forced-included header
      #ifdef NAME_CXX_EXT
      NAME_CXX_EXT ...
      #endif
    

All of the above seem rather nasty


The solution I am going to go with for now:

[This is all pseudo code, didn't check this copy for compilation]

The following generated code, which I cannot modify (though I can change the map to a set)

class GeneratedElement {
 public:
   // ...
   int32_t num;
   // ...
};


class GeneratedMap {
 public:
   // ...
   std::map<int32_t, GeneratedElement>  myGeneratedMap;
   // ...
};

// End of generated code

Elsewhere in the app:

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);
   }
};

I wanted to avoid the double m开发者_开发百科ap lookup for every access (though I know that the complexity remains the same, it is still two lookups ).

I figured I can guarantee that all insertions and removals to/from the theGenerated) are done in a single spot, and in that same spot is where I populate/remove the corresponding entry in myMap, I would then be able to initialize Element::pGenerated to its corresponding element in theGenerated.myGeneratedMap

Not only will this let me save half of the lookup time, I may even change myMap to a better container type for my keytype (say a hash_map or even a boost multi index map)

At first this sounded to me like a bad idea. With std::vector and std::dqueue I can see how this can be a problem as the values will be moved around, invalidating the pointers. Given that std::map is implemented with a tree structure, is there really a time where a map element will be relocated? (my above assumptions were confirmed by the discussion in enter link description here)

While I probably won't provide an access method to each member of myElement or any syntactic sugar (like overloading [] () etc), this lets me treat these elements almost a consistent manner. The only key is that (aside for insertion) I never look for members of mymap directly.


Have you considered just using simple containership?

You're using C++, so you can just wrap the struct(s) in some class or other struct, and provide wrapper methods to do whatever you want.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜