Complex type problem

Started by
4 comments, last by Lennings 17 years, 4 months ago
Hi. I have some problem to get a type to work. What I want is to a good container for my 2d animations. I what to use a container like: std::map<std::string, std::vector<Image*> > Animations; Where string is a name like “run” and the vector is all the animation images for that animation. After that I think I what to put this map into another map tax. m_animations[“Player”][“Run”] I have some test code to show I think it can look like. Strings instead of my image class. #include <iostream> #include <map> #include <string> #include <vector> using namespace std; typedef std::map<std::string, std::vector<string>* > myType; int main() { myType xxx; xxx["1"] = new std::vector<string>; xxx["2"] = new std::vector<string>; xxx["3"] = new std::vector<string>; xxx["1"]->push_back("A"); xxx["2"]->push_back("B"); xxx["3"]->push_back("C"); std::cout << xxx.find("1")[0] << std::endl; std::cout << xxx.find("2")[0] << std::endl; std::cout << xxx.find("3")[0] << std::endl; std::cout << xxx["1"][0] << std::endl; std::cout << xxx["2"][0] << std::endl; std::cout << xxx["3"][0] << std::endl; for(myType::const_iterator itr=xxx.begin(); itr != xxx.end(); itr++) { for(unsigned int i=0; i < itr->second.size(); i++) { std::cout << itr->first << "\t" << itr->second << std::endl; } } return 0; } Wonder what the different is between const_iterator and iterator? Hope you have some idea. Thanks all.
Advertisement
A const_iterator is a read only iterator, you can not modify its contents. On a normal iterator, you can.
0) Avoid storing pointers into standard library containers. Especially pointers to dynamic allocations. You are fighting the container that way; it wants to do the memory management for you. Just use vector<string> as the value type here. It will do what you want. The code provided will not, because the map lookup will return a *pointer* to a vector, and applying subscript [0] to a pointer-to-vector will simply yield the pointed-at vector (it treats the pointer-to-vector as if it were an array of vectors, meaning that subscripts *other* than [0] will do horrible things indeed.).

1) Not to mention, you can't subscript the result of the 'find', because the result of the 'find' is an iterator-over-maps, not a map value. The map subscript operator wraps up a bunch of other nonsense that you have to do with the 'find' value. But you *should* learn how to do it, because the operator[] has another quirk that is often not what you want: it will silently add values to the map if they weren't there before. It does this because, well, it has to return something; the alternative would be to throw an exception, which is considerably less friendly. The operator[] behaviour is sometimes what you want and sometimes not, so you must Think(TM).

2) For declaring complex types, 'typedef' is your friend. In particular, it avoids this irritating problem:

typedef std::map<std::string, std::vector<string>> myType; //wrong


The '>>' is interpreted as a "right shift" operator, which causes the compiler to choke and die. A space is mandatory. I see you're using the one typedef already, but more of them would be quite useful, because (a) it avoids that problem (you can typedef the value type, and then your typedef name doesn't have any angle brackets in it, so you're OK); (b) you then have a simple name for the value type which will be more useful later; and (c)...

3) If you expect to look up by one key, and then another key, then you're going to have to have a map of maps, right? :) (I'm not sure what you mean by "map tax", though. Is 'tax' supposed to be short for 'taxonomy' or something?)

#include <map>#include <vector>#include <string>#include <exception>#include "Image.h" // might as well show a "real" example; never mind that// we can't "output" Images, but we can at least show the *expressions* we// will need to work with.using namespace std;typedef std::vector<Image> ImageSequence;// Again, there is probably no reason to make that a vector of Image*. If you// think you have a reason, please explain it and let me see about it :)typedef std::map<std::string, ImageSequence> ObjectAnimations;typedef std::map<std::string, ObjectAnimations> AllAnimations;// Get 'which' frame of 'what' animation for 'who'.// Note how we can return a pointer although we didn't store pointers; but// this pointer will be invalidated the next time a change is made to the map,// so we better act quickly.// Also note how 'find' is used to avoid modifying anything, and how// const_iterators are used - since we're not modifying anything, we use them// so that the compiler checks that we didn't mess up and modify anything, and// so that the code *documents* the intent not to modify anything.const Image* getFramePtr(const AllAnimations& aa, const std::string& who, const std::string& what, int which) {  AllAnimations::const_iterator aa_iter = aa.find(who);  if (aa_iter == aa.end()) { return 0; }  // Here's how we use the AA iterator to get at the OA:  ObjectAnimations::const_iterator oa_iter = aa_iter->second.find(what);  if (oa_iter == oa.end()) { return 0; }  ImageSequence& is = oa_iter->second;  if (which < 0 || which >= is.size()) { return 0; } // out of range  return &(is[which]); // pointer to that element. Note that operator[] for  // a *vector* *can* be a const operation.}// And here's a wrapper that allows the client code to not worry about pointers,// and which makes more sense for situations where the frame is *expected* to// exist.const Image& getFrame(const AllAnimations& aa, const std::string& who, const std::string& what, int which) {  Image* result = getFramePtr(aa, who, what, which);  if (!result) { throw std::exception("frame not found"); }  return *result;}// Now you have the choice whether to check returned pointers for NULL, or// to catch and handle exceptions, according to what is best for your situation.// Depending on your unique circumstances, it might be more appropriate to// use an assertion in the getFrame() version rather than throwing an exception.int main() {  AllAnimations aa;  // The "auto-vivifying" behaviour of the map operator[] can be "chained",  // since it just default-constructs values:  aa["Player"]["Run"].push_back(Image());  // We "know" this one is here:  getFrame(aa, "Player", "Run", 0).doWhateverItIsThatImagesDo(); }
Thanks all.

And thanks Zahlman for the good information. The “tax”, it was not supposed to be there.
It was as you mention a map in another map.

you don’t know any good book on a little more advanced c++. I have only a basic c++ book?

I will see if I now can get these to work.
Thanks another time. :-)
I forgot to say that why I use Image* is that many I want many object to chare image. But that maybe is not necessary.

Hmmm have to test that… good that you mention that.
*NEW*

I forgot to say that why I use Image*, it is sow many object to chare image. But that maybe is not necessary.
Hmmm have to test that… good that you mention that.

This topic is closed to new replies.

Advertisement