Good method for implementing Meta-Data in C++

Started by
10 comments, last by Name_Unknown 18 years, 8 months ago
Hello, I am looking for insight into how to apply an interface to a meta-data abstraction. If that sounds like gobble-gook talk I'll explain in more detail. I have the need to store 'meta-data' onto a class. It is meta-data, it is data which other classes might want to store on the object (with a key-value pair) and the class may not have any idea it is even tagged. That is meta-data as far as I know how to define it. The problem is that the meta-data can be many different kinds of things, and I want a nice interface to abstract it. I am looking for some generic/template approach if possible. I tried the 'everything is a string' approach.. but it is rather slow, and if I ca find a generic design that will make it faster and safer (and also manage it's own destruction if possible) I would be much happier ;-) Thanks
"It's such a useful tool for living in the city!"
Advertisement
I'm not really certain how you could accomplish this without modifying the original class. You could create a class that can be tagged then derive your classes from that, but that may not be what you're looking for. Another way would be to use a std::map that utilizes the address of the object instance as the key, then store some sort of meta data in that. The downside would be that copying the instance will not copy the metadata, moving the instance will lose the metadata, and deleting the instance will not remove the meta data from the map.
Can you provide an example of what you use this for, and the code of the currently unacceptable string version [for reference]?

Depending on the use there might be various solutions.
If you cannot/want not modify the original class, you have to use the address
of the instances, and a map, but you need to take extra care for construction,
copying and destruction.

I would instead implement the interface defined by that 'not modifyable' class,
and delegate it to the original instance, plus adding then what you call properties.

For storing the property itself, you might want to look at the boost library, namely boost::variant, which might be a good generic container.
Well, I already have it implemented ;-) Yep, using a std::map... however, I just found something that is exactly what I am trying to accomplish:

http://www.vollmann.com/en/pubs/meta/meta/meta.html

Right now, I have this... goofy class ... but it is very awkward and not very safe.. and it doesn't even know what itself is. So .. I need a better approach.

It is like this:

struct Attribute{   void * p;   int    type;};


As you see.. that is evil. But it works. And it is much faster than storing everything in string.. but it is evil.

Originally I had:

struct Attribute{   std::string val;   int type;   float ToFloat(...) ...};


but converting from/to strings can be very slow.

But that article is exactly what I want, and it also explains MOPs which is something I have been interested in but unable to find out how it works.

Thanks
"It's such a useful tool for living in the city!"
Having used that very type/void * sort of setup for years, let me caution you against such a setup. Except for serialization, it is unwieldy, and not nearly as useful as it seems. It requires a lot of "if type == " junk which is nearly the anti-thesis of code re-use.

As mr. mole suggests, boost::variant or ::any are likely far better solutions than type/void* ever will be. And even then, I question the wisdom of such a design which requires data to be tied to a class.
This is something I've wondered about also but I've never put fingers to keys on it. I'm pulling this off the top of my head, but how about something like this:
class AbstractAttribute{public:  int toInt() const = 0;  string toString() const = 0;  ...};template< class TAttribyteType >class ConcreteAttribute : public AbstractAttribute{  TAttributeType val;public:  ...};string ConcreteAttribute< int >::toString() const {  return IntToString( val );}etc


And use list< AbstractAttribute* > to store them?
Or create a NamedAttribute class and put it in a map?
Quote:Original post by Telastyn
Having used that very type/void * sort of setup for years


Yes, I am not unfamiliar with it. The problem
is that when it comes to templates I can't find a very useful way to employ them because of the C++ type system.

A string is an ideal container but it is slow, but I figured out a solution.
Boost::variant is sort of overkill for this.

Quote:
I question the wisdom of such a design which requires data to be tied to a class.


I don't really follow what you are implying.

This sort of thing has been common in languages like Smalltalk, Lisp and Scheme for many years.
"It's such a useful tool for living in the city!"
Quote:Original post by Name_Unknown
Quote:Original post by Telastyn
Having used that very type/void * sort of setup for years


Yes, I am not unfamiliar with it. The problem
is that when it comes to templates I can't find a very useful way to employ them because of the C++ type system.


Well, how would you be looking to use the meta-data? I'm sure some of the very bright people here could offer alternatives which might be more elegant than the type/void* pair.

Quote:
A string is an ideal container but it is slow, but I figured out a solution.
Boost::variant is sort of overkill for this.


Well, why is it slow? Are you really going to be accessing meta-data that often? I don't really know, because you've not given any context. Why is overkill bad? From what I've seen of the boost classes, which I've not been able to use myself, they seem tailor made for this sort of need.

Quote:
Quote:
I question the wisdom of such a design which requires data to be tied to a class.


I don't really follow what you are implying.


In my experience, which is admittedly limited, C++ classes tend to be best used as functional implimentations. That is, the data contained within them is enough to provide whatever functional interface the class provides. Most of the scenarios I can think of where a truly generic meta-data system across class types is needed is when the classes are being used as data stores, not functional implimentations....

For example:

If you have classes set up like:
unit- tank- infantry- cavalrybuilding- factory- hospital


This is using the specific type to simply store data. [imo] this is poor class design, since you're then including information in the type [or in the inherited functions]. Unfortunately, almost every class tutorial in the world does this.

Functional implimentation design would make the type of building or unit a parameter of the class, though unit and building would likely be abstracted into something more functional like 'mobile' or 'workplace'.

In my experience, designing classes around an interface like that makes them much more versatile, and leads to far fewer problems with C++'s types. It allows the properties of objects to better fit one interface than a whole group of classes. Like this metadata requirement would be it's own independant interface which could be combined with others, rather than trying to fit it over a bunch of disparate groups.

Quote:
This sort of thing has been common in languages like Smalltalk, Lisp and Scheme for many years.


Which, if this is a primary requirement, might be better tools for the job. C++ does not handle this sort of design as elegantly as I imagine other languages might. I can certainly see where the meta-data style design would fit better in a language with more malleable typing.
Quote:Original post by Telastyn
Well, how would you be looking to use the meta-data?


Mainly the meta-data is for scripting. It's mostly a way to attach some additional information to the class in a way that is not really practical to design into it (I don't really know what it might be). That information is specific to a particular object. If I designed it into the C++ classes it would become unwieldly like you had diagrammed with your classes. I like this design approach much better.

I have done the void* thing too, and I know it sucks, that is why I brought this topic up ;-)

Boost::variant looks like it might work I will give it a try.

Strings are the best choice because I want it to be language neutral and if possible, machine neutral (no little/big endian issues).

Thanks


"It's such a useful tool for living in the city!"

This topic is closed to new replies.

Advertisement