• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
Soundstone

Multiple types in a map container C++

12 posts in this topic

Hi everyone, 

 

not sure if this is the proper thread to post this question to, if not I apologize and will gladly post it where it needs to be. I am having an issue with understanding how to use multiple data types in one map container. For example my code looks like this:

 

 

struct A
{
    string mName;
    int    mNumber;
};
 
struct B
{
    string mName;
    float  mFloater;
    bool   mFlag;
};
 
 
class C
{
     C();
     ~C();
 
//basically I want this map to hold a list by reference int for the entire collection of both objects from A and B.
map<int, A/B> myMultiMap;
 
}
 
so that in main..
 
int main()
{
     C* myC;
     A* myA;
     B* myB;
 
    myC->myMultiMap[0] = myA;
    myC->myMultiMap[1] = myB;
}
 
is there any easy way to do this. I haven't had much luck with getting the boost libraries to load correctly, I've only been at this about a year, and don't completely know how to do some of the things. I appreciate any help that is offered, even ifs its just a link or something. Thanks for reading. 
 
Regards,
Soundstone
0

Share this post


Link to post
Share on other sites
Here's one option:
struct Base
{
    virtual ~Base() { }
};

struct Alpha : public Base
{
    int Foo;
};

struct Beta : public Base
{
    std::string Bar;
};

int main()
{
    Alpha* someAlpha = new Alpha;
    someAlpha->Foo = 42;

    Beta* someBeta = new Beta;
    someBeta->Bar = "Test";

    std::map<int, Base*> someMap;
    someMap[0] = someAlpha;
    someMap[1] = someBeta;
}
A better design would be to use std::shared_ptr (or boost::shared_ptr if your compiler doesn't support the C++11 standard) instead of raw pointers.
0

Share this post


Link to post
Share on other sites

You still need some way to know what type of object it actually is though... you can use dynamic_cast for that if you want.

 

Can you only have one of type A or type B corresponding to a particular int key? Because if that is not the case you are probably better off having 2 maps, one holding A's and the other holding B's.

0

Share this post


Link to post
Share on other sites

Yeah, I agree, but with the empty base class implementation it would be required.

 

If we knew more about what the OP wanted we could probably suggest a better design I feel.

0

Share this post


Link to post
Share on other sites

Some other options that I don't see being mentioned here, if in fact A and B should not inherit from the same base class:

 

  • use "void *" as the value type for your map (though this gets tricky, because you need to know the type when you pull the object and dynamic_cast it)
  • use "boost::any" as the value type for your map (this is a little better but can cause problems if you have exceptions off because boost throws an execption if boost::any_cast fails)
Edited by metsfan
0

Share this post


Link to post
Share on other sites

A better design would be to use std::shared_ptr (or boost::shared_ptr if your compiler doesn't support the C++11 standard) instead of raw pointers.

 

As for this point...wouldn't you say this advice is a bit misleading?  Yes, shared pointers have their place, but shouldn't the train of thought be "by default, try to make sure a pointer is owned by only one object, and if that is impossible or breaks the design of the application, then use a shared pointer"?  

0

Share this post


Link to post
Share on other sites
Then use a std::unique_ptr, problem solved.

My point wasn't to make a blanket rule for all pointer usage (so I'm not sure why you read that into my post) but rather to point the OP in the direction of smart pointers as a preference over raw pointers.
2

Share this post


Link to post
Share on other sites

Thanks for advice guys, Ill try and make it more clear what I am trying to accomplish.

 

In visualization terms think of a box, this box can hold exactly 9 objects in it,each of the 9 spots is a numbered index 0-8, but only objects of either type A or type B. I would like a associative container that can keep track of which everything in the box, including which type is located at which box index. I tried doing this through inheritance and downcasting but I feel like there should be a better way. I have data members in A and B that extend a baseclass. The classes look similar to this:

Class BaseClass
{
public:
	BaseClass(string, int, string, int, int, int);
	~BaseClass();
	void SetName(string value) 	{name = value;}
	void SetType(int value)		{type = value;}
	void SetFlavor(string value)	{flavor = value;}
	void SetNum1(int value)		{num1 = value;}
	void SetNum2(int value)		{num2 = value;}
	void SetNum3(int value)		{num3 = value;}
	const string GetName()		{return name;}
	const int  	GetType()	{return type;}
	const string	GetFlavor()	{return flavor;}
	Const int	GetNum1()	{return num1;}
	const int	GetNum2()	{return num2;}
	const int	GetNum3()	{return num3;}
	
protected:
	string  name; //name of object
	int	type; //flagged 0 for type a and 1 for type B
	string  flavor; //flavor text about object
	int     num1; 
	int 	num2;
	int 	num3;
}

Class A : public BaseClass
{
public:
	A(string name, int type, string flavor, int num1, int num2, int num3, int num4
              int num5, float speed)
          :BaseClass(name, type, flavor, num1, num2, num3);
	~A();
	//getters and setters for speed, num4 and num5...

private:
	float speed;
	int 	num4;
	int 	num5;
}

Class B : public BaseClass
{
public:
	B(string name, int type, string flavor, int num1, int num2, int num3 int bInt)
          :BaseClass(name, type, flavor, num1, num2, num3);
	~B();
	void SetBInt(int value)		{bInt = value;}
	const int GetBInt()		{return bInt;}
private:
	int bInt;
}

so that when in class box it would look like this:

 

//Box.h

#include "A.h"
#include "B.h"
#include "BaseClass.h"
Class Box
{
public:
	Box();
	~Box();
	void AddObjectTypeA(A* objectA);
	void AddObjectTypeB(B* objectB);
private:
	map<int, BaseClass*> myBoxMap;
}


//Box.cpp

void Box::AddObjectTypeA(A* objectA)
{
	bool full = false;
	for(int i = 0; i < myBoxMap.size(); i++)
	{
		if(i == myBoxMap.size())
		{
			//map is full - only want 9 objects inside no more
			full = true;
			break;
		}
		if(mFoundOffensiveMap[i]->GetOffCellName() != "") //preloaded during box construction with 9 objects inside of type Basecell name = ""
		{
			//bad choice cell full already
			continue;
		}
		else if(mFoundOffensiveMap[i]->GetOffCellName() == "")
		{
			//good cell choice - it's empty currently
			myBoxMap[i]->SetName(cell->GetName());
			myBoxMap[i]->SetType(cell->GetType());
			myBoxMap[i]->Setnum1(cell->Getnum1());
			myBoxMap[i]->Setnum2(cell->Getnum2());
			myBoxMap[i]->Setnum3(cell->Getnum3());
			myBoxMap[i]->Setnum4(cell->GetNum4());
			myBoxMap[i]->setSpeed(cell->GetSpeed());
			myBoxMap[i]->setnum5(cell->Getnum5());
			
			
			break;
		}
	}
	return full;

}

finally in main.cpp

 

 


 

//main.cpp

int main()
{
	A* myObjA = new A("Steve", 0, "is a man", 4, 6, 7, 17, 8, 3.4f);
	Box* myBox;

	myBox->AddObjectA(myObjA);
				 
	/* during the above call I would like to set myObjA into the Box 
	   at the first available index without another object. It tests
	   this in the function by checking if name = "". */
}

I hope that better explains it. As it currently stand I'm getting access violation errors when in the function to add. Those errors are keeping me from knowing if this set up will actually work for what I want. Thank you again, for reading into this. I truly appreciate the quick responses you have offered.

 

Regards,
Soundstone

Edited by Soundstone
0

Share this post


Link to post
Share on other sites

The access violations would be because you use a pointer to a Box (Box* myBox) but you don't create an object of type B by doing

 

Box* myBox = new Box();

 

But you are probably better off just having a Box local variable (or member variable of a class) so you don't have to create it with operator new in the first place.

Edited by Paradigm Shifter
0

Share this post


Link to post
Share on other sites

For this kind of problem I use [url=http://libclaw.sourceforge.net/multi_type_map.html]claw::multi_type_map[/url] :

For example :

typedef claw::meta::type_list_maker<A*, B*>::result my_type_list;
claw::multi_type_map<int, my_type_list> my_multi_map;

my_multi_map.set<0>( myA );
my_multi_map.set<1>( myB );

Then I use a [url=http://libclaw.sourceforge.net/multi_type_map.html#mtm-examples-visitor]visitor[/url] to explore the map.

With this solution, A and B do not need to share a common base class, nor to have any virtual stuff. There is also no dynamic_cast.

0

Share this post


Link to post
Share on other sites

I'm confused. You say it's _exactly_ 9 slots and they are _consecutively_ numbered 0-8? At which point did that make you think "map<int, ptr>" instead of "array<ptr, 9>"?

 

Why fill it with dummy instances and loop through all the pointers and call functions on them, instead of just checking them for 0?

 

And why even do that, instead of just adding a simple "numItems" variable to box that keeps track of the next free slot?

 

 

class Box
{
public:
    Box() : numItems(0) {}
 
    void addItem(shared_ptr<Item> item)
    {
         if (numItems < 9)
         {
              items[numItems++] = item;
         }
    }
 
private:
   std::array<shared_ptr<Type>, 9>;
};

 

Why pass a shared_ptr? Because in your code you never check if the item was successfully added. You ownership is completely unknown at this point. If you delete it at the calling site and it was added -> access violation. If you don't delete it and it wasn't added -> memory leak.

 

Why a shared_ptr? Without knowing the usage of that box, it is safer to allow code that is currently using an item from the box to keep it alive, just in case it gets removed from the box in between. Using a smart pointer also means there is no need to explicitly delete an object in the box before replacing it (if that is even a use case).

 

 

Your main function (as explained above) also has a big issue.

 

 

int main()
{
     Box myBox; //This has no business being a pointer in the first place
     myBox.addItem(make_shared<ItemA>("Steve", 0, "is a man", 4, 6, 7, 17, 8, 3.4f);
}

 

 

The real problem is of course trying to stuff two completely unrelated types into the same container. Why? It will just result in a huge mess of if/else and dynamic_casts or getType(). Put them in two different arrays and let the box class hide that fact. Of course if you ever want to access an item in the box, you're back to the same problem. What type should that function return?

 

Generally you want to hide all members and use a _common_ interface for A and B. Depending on what they are supposed to do, try to abstract that. You want them to print their data? Have a virtual function "print" in both of them (and the base class). Don't even think about placing that code outside the classes and have it rip the data from their guts.

Edited by Trienco
2

Share this post


Link to post
Share on other sites

Hi Trienco, Thanks for the help and the advice, what you present seems to be a good solution to go off of. I think my originial intention with the map container over the array was that I wanted to have a string key that I could easily reference the items by. The items in the container are similar and inherit from an abstract base item class. The end game was to use this as part of an inventory system for a game. Not necessarily what the player is carrying but what they are storing into an external container in game. This container has actions that it takes much like an AI player. I was really tied to the idea of item lookup through string key. Thank you all for your comments and help. I'll be thinking through this much more in depth.

0

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0