What would be the elegant and simple way (if exists) to implement a storage of generic objects (all other objects inherit from base). Once the object is stored, use a string handle to retrieve object or copy into another.
class Object{
public:
Object(){};
~Object(){};
};
class ObjectHandler{
public:
ObjectHandler(){};
~ObjectHandler(){};
void InsertObject(std::string handle, std::shared_ptr<Object> obj){
// some things happen before inserting
_obj.insert(std::make_pair(handle,obj));
}
std::shared_ptr<Object> RetrieveObject(std::string handle){
// some things happen before retrieving
return _obj[handle];
}
private:
std::map<std::string,std::shared_ptr<Object>> _obj;
}
For example, user defined classes are
class Dog : public Object{
public:
Dog(){};
Dog(std::string name){dogName=name};
~Dog(){};
std::string dogName;
//...
}
class Cat : public Object{
public:
Cat(){};
Cat(std::string name){catName=name};
~Cat(){};
std::string catName;
//...
}
And the following code is executed
void main(){
ObjectHandler oh;
Cat c("kitten"), cc;
Dog d("doggy"), dd;
oh.InsertObject("cat#1",c);
oh.InsertObject("dog#1",d);
cc = oh.RetrieveObject("cat#1");
dd = oh.RetrieveObject("dog#1");
std::cout << cc.catName << std::endl; // expect to print 'kitten'
std::cout << dd.dogName << std::endl; // expect to print 'doggy'
}
I believe there should be some well established idea (pattern) to make this working right.
I also suspect std::shared_ptr might be useful here.
Thanks,
I would exercise caution here, in your example you're storing your objects as Object strictly (on the stack), since that would only allocate enough space for something of type Object, should you insert something that inherits from the type, it would have the part that describes the subclass sliced.
Good examples of the problem at hand:
http://www.geeksforgeeks.org/object-slicing-in-c/
What is object slicing?
One way to get around the problem is to handle pointers to the objects in your ObjectHandler instead, the objects themselves allocted on the heap.
If I'm just misinterpreting your post, then I apologise.
But if you as you said, will store smart pointers to the object instead, making a pair should look something like this:
std::map<std::string,std::shared_ptr<Object>> _obj;;
std::string handle = "hello"; //Or whatever the actual handle is.
std::shared_ptr<Object> keyvalue(new Object());
objects[handle] = std::shared_ptr<Object>(keyvalue); //alternative to std::make_pair
objects.insert(std::make_pair(handle, std::shared_ptr<Object>(keyvalue))); //this also works
Depending on at what point you want to start handling objects with smart pointers, insertion might look like:
void InsertObject(std::string handle, Object* obj){
_obj.insert(std::make_pair(handle,std::shared_ptr<Object>(obj)));
}
std::string key("hi");
InsertObject(key, new Object());
or alternatively just:
void InsertObject(std::string handle, std::shared_ptr<Object> obj){
_obj.insert(std::make_pair(handle, obj));
}
Also note that std::map's indexing operator[] overwrites the old value if it exists, while the insert you're already using will only insert if the old one doesn't exist.
Related
Note: Apologies if the title is unclear, I don't quite know how to express the issue in proper terms (improvement suggestions are very welcome).
Code, onlinegdb example of the working version and example of the non-working one first to simplify the explanation:
#include <iostream>
#include <vector>
#include <memory>
class A {
public:
int v = 0;
};
void some_library_function(const std::vector<A*>& objects)
{
// do something to each object without taking ownership
for(auto p : objects)
{
p->v = 42;
}
}
class B
{
public:
std::vector<std::shared_ptr<A>> m_objects; // this is a private field in my actual code
B():m_objects{std::make_shared<A>()}{};
void use_library()
{
std::vector<A*> observer_vector(m_objects.size());
for(int i=0; i<m_objects.size(); i++)
{
observer_vector[i] = m_objects[i].get(); // fails here if I use unique_ptr
}
some_library_function(observer_vector);
}
};
int main()
{
B b;
b.use_library();
std::cout << b.m_objects[0]->v;
return 0;
}
I have a library function that operates on a series of objects of class A passed in via std::vector<A*>. These objects are stored in a field of class B that owns the objects. I would like to model the "owns" part via a vector of std::vector<unique_ptr<A>>, but this makes it impossible to pass the objects down to the library function.
using shared_ptrs works, but I'm worried this is not as expressive as the unique_ptrs with regards to object ownership.
Is there a way to use unique_ptrs in the vector and still be able to use the library function?
You're already doing the right thing. A raw pointer is a perfectly reasonable way to model something with unknown or lacking ownership. You're not storing the vector anywhere, so there is no confusion and no risk.
The only problem here really is that you've had to regenerate the entire vector, which seems like a bit of a waste. Ultimately, if you're set on a vector<unique_ptr<A>> at the source, and you're stuck with vector<A*> at the destination, then there's nothing you can do about that. If the vector is small it doesn't really matter though.
observer_vector[i] = m_objects[i].get(); // fails if with unique_ptr because of operator= being deleted
No, that should be valid. You're just assigning a raw pointer.
I have simplified the code as much as possible.
So I have two class:
class EntityManager
{
public:
std::shared_ptr<std::vector<Entity> > getEntities()
{
return std::make_shared<std::vector<Entity> >(m_entities);
}
private:
std::vector<Entity> m_entities{};
};
and
class System
{
public:
void loadEntities(std::shared_ptr<std::vector<Entity> > entities)
{
m_entities = entities;
}
private:
std::shared_ptr<std::vector<Entity> > m_entities;
};
Now basically I want the m_entities of System to point to the m_entities of EntityManager.
I did this:
system = System();
system.loadEntities(m_entityManager.getEntities());
But then I pushed back an element into the m_entities vector of EntityManager and this element wasn't added in the m_entities vector of System, which means my pointer doesn't point.
Where is my mistake?
Thanks!
Your problem is this line: return std::make_shared<std::vector<Entity> >(m_entities);
What is happening is that the shared_ptr manages a new std::vectory<Entity> container which is initialized as a copy of m_entities. Therefore, modifying the instance in the shared_ptr doesn't modify the data member in the EntityManager class and of course the shared_ptr won't see changes made to EntityManager::m_entities.
std::make_shared doesn't "make this thing shared"; it "makes a thing that will be shared".
So, you can't just make a shared pointer out of nowhere that points to something that already exists.
Your code dynamically allocates a std::vector, copy constructed from m_entities and managed by a std::shared_ptr. It's shorthand for this:
std::vector<Entity>* ptr_to_copy = new std::vector<Entity>(m_entities);
return std::shared_ptr(ptr_to_copy);
It's not clear what you're trying to do, from the code that (by your own admission) does not achieve that goal. But it seems unlikely that std::shared_ptr is appropriate here.
If it is, then make the vector dynamically-allocated and shared from the start; otherwise, just return a reference to the vector as it is.
Hack example of a pointer-free solution.
#include <string>
#include <iostream>
#include <vector>
//Hack-sample Entity class
class Entity
{
public:
Entity(const char * name): m_name(name)
{
}
void print() // this is stupid in real life. Prefer a << overload
{
std::cout << "Hi! I'm " << m_name << "!\n";
}
private:
std::string m_name;
};
class EntityManager
{
private:
std::vector<Entity> m_entities;
public:
// hide the fact that a vector is being used to store the entities.
// you can now swap out the vector for most standard containers without
// changing any code other than the using and the declaration of m_entities
using iterator = std::vector<Entity>::iterator;
EntityManager(): m_entities({"bob", "bill"})
// just pre-loading a few test entities
{
// RAII says you should load the entities from their source here
}
// get the first entity.
iterator begin()
{
return m_entities.begin();
}
// get the end of the entity list
iterator end()
{
return m_entities.end();
}
// adds an entity
void addEntity(const Entity & entity)
{
m_entities.push_back(entity);
}
// removes an entity
iterator removeEntity(iterator rem)
{
return m_entities.erase(rem);
}
};
class System
{
public:
// example method to show System working with EntityManager by printing all of the Entities
void printEntities()
{
for (EntityManager::iterator it = m_entityManager.begin();
it != m_entityManager.end();
++it)
{
it->print();
}
}
// example method to show System working with EntityManager by adding Entities
void addMoreEntities()
{
m_entityManager.addEntity(Entity("Ted \"Theodore\" Logan"));
m_entityManager.addEntity(Entity("Excellent!!!"));
}
private:
EntityManager m_entityManager ;
};
// sample test
int main()
{
System test;
test.printEntities();
test.addMoreEntities();
test.printEntities();
}
THIS HAS BEEN A HACK. THIS HAS ONLY BEEN A HACK.
If you want to do EntityManager right, see Writing your own STL Container for hints. If you want all of the bells and whistles, the job is fairly complicated. Depending on how you are using EntityManager and the complexity of the Entity management logic, you may be better off discarding EntityManager and just using the plain, old std::vector.
Addendum: What is meant by Resource Acquisition is Initialization (RAII)?
In my class implementation, I have something like this:
base class
class swcWidget :
public swcRectangle
{
public:
swcWidget();
virtual ~swcWidget();
void update(float dt);
protected:
inline virtual void oPaintOnTop() { }
private:
};
derived class
class swcButton :
public swcWidget
,public swcText
{
public:
swcButton();
virtual ~swcButton();
static const int DEFAULT_SIZE = 20;
protected:
private:
void oPaintOnTop();
};
class swcApplication
{
public:
swcApplication(int argc, char *argv[]);
virtual ~swcApplication();
int run();
struct Controls
{
typedef std::vector<swcWidget*> vWidgets; //option 1
~Controls();
/////////////////////////////////
// M A I N P R O B L E M //
/////////////////////////////////
void add(swcWidget &&widgets); //most preferred option
//but gets demoted to base class.
void add(swcWidget *widgets); //second choice
//but should I make a copy of it?
//or just make a reference to it?
//and this one does what I wanted to.
//but still unsure on other things I don't know
void add(swcWidget *&&widgets); //this compiles fine (?)
//I don't know what kind of disaster I can make into this, but still does not do what I wanted.
inline vWidgets &getWidgets() {
return widgets;
}
private:
vWidgets widgets;
};
Controls controls;
};
I know some working option like this:
making the
swcApplication::Controls::widgets
as type of
std::vector<std::shared_ptr<swcWidget>>
but my code will bind into std::shared_ptr and I cannot make simple syntax like this:
swcButton btn;
app.controls.add(std::move(btn));
Example usage:
main.cpp
int main(int argc, char *argv[])
{
swcApplication app(argc, argv);
app.windows.create(640, 480);
if (font->load("fonts\\georgia.fnt") != BMfont_Status::BMF_NO_ERROR)
{
puts("failed to load \"georgia.fnt\"");
}
{
swcButton btn;
btn.setPosition(100, 100);
btn.setFont(font);
btn.text = "Ey!";
app.controls.add(std::move(&btn));
// btn.text = "Oy!";
}
return app.run();
}
Update:
Here's the temporary definition of swcApplication::Controls::add() although it may still vary
void swcApplication::Controls::add(swcWidget &&widget)
{
widgets.push_back(std::move(widget));
}
If a class is moveable, then it will in turn move it's members one by one. For this to be efficient, these members must either be small POD's or must be allocated on the heap. You must add this functionality, not forget to move any member, and object slicing is a concern to watch out for.
Given the class is non-trivial, you have the most efficient move construct available when you just use a pointer directly (at the cost of heap allocation time of course). No slicing is possible, and no member can be forgotten to be moved, since you move the whole object in one go. The one hurdle to watch out for is to keep track of who owns the pointers - you'd better set it in stone, but if that's done then there are no issues anymore.
The move semantics are wonderful, but if your classes are somewhat involved I think pointers in this case are easier / more efficient to work with. I'd thus stick with the pointer variant, and make sure your collection will own the pointers (and release them again via RAII) - make liberal use of comment in your public interface saying so. You can do this by storing some form of smart pointer (hint: be careful with unique_ptr's!), or (less safe) make and always use a Clear() member that delete's all pointers before clear()'ing the collection.
EDIT
Whet you define your widgets member to be of type vector, then example code could be:
To class swcApplication add:
void swcApplication::Controls::ClearWidgets() {
for (auto& nextWidget: widgets) {
delete nextWidget;
}
widgets.clear();
}
Don't forget to call ClearWidgets at the appropriate times (like in your destructor).
Adding widgets can be done with:
// Note: any passed widget will not be owned by you anymore!
template <typename Widget>
void swcApplication::Controls::add(Widget*& widget) {
widgets.push_back(widget);
widget = nullptr;
}
From now on you can add widgets like
swcButton* btn = new swcButton;
app.controls.add(btn);
// btn is now owned by app.controls, and should be set
// to nullptr for you to prevent misuse like deleting it
Using a smart pointer here should make it more safe, though storing unique_ptr's makes accessing them a bit error-prone (watch out for grabbing ownership back from the container when accessing them), and a shared_ptr gives overhead which might be unneeded here.
I'm quite new to C++ and I am trying to store objects inside a std::vector like this:
Event.h:
//event.h
class Event
{
public:
Event();
Event(std::string name);
~Event();
void addVisitor(Visitor visitor);
private:
std::vector<Visitor> m_visitors;
};
Event.cpp:
//event.cpp
Event::Event() :
m_name("Unnamed Event")
{
}
Event::Event(std::string name) :
m_name(name)
{
}
void Event::addVisitor(Visitor visitor)
{
this->m_visitors.push_back(visitor);
}
void Event::listVisitors()
{
std::vector<Visitor>::iterator it;
for(it = this->m_visitors.begin();it != this->m_visitors.end(); ++it)
{
std::cout << it->getName() << std::endl;
}
}
Visitor.h:
//visitor.h
class Visitor
{
public:
Visitor();
Visitor(std::string name);
~Visitor();
std::string getName() const;
void listVisitors();
private:
std::string m_name;
};
Visitor.cpp:
//visitor.cpp
Visitor::Visitor() :
m_name("John Doe")
{
}
Visitor::Visitor(std::string name) :
m_name(name)
{
}
std::string Visitor::getName() const
{
return m_name;
}
main.cpp:
//main.cpp
int main()
{
Event *e1 = new Event("Whatever");
Visitor *v1 = new Visitor("Dummy1");
Visitor *v2 = new Visitor("Dummy2");
e1->addVisitor(*v1);
e1->addVisitor(*v2);
}
If I do it like this I would have to add a copy constructor which would make a deep copy so the object gets copied properly into the vector. I'm looking for a way around it by only storing pointers to the objects in a vector.
I already tried it with std::vector<std::unique_ptr<Visitor> > m_visitors, but then I got some errors when calling addVisitor in main.cpp. Of course I changed the declaration of the class members accordingly.
How would an appropriate declaration of the members and the member function look like to make it work?
Stylistically, if you are passing pointers, just accept pointers as the function arguments.
What's happening in the example code above is that the visitors are getting copied to become function arguments and the pointers you had are unreferenced by anything outside of the main function.
I can't speak to what the errors are that you're seeing as you didn't describe them but it probably has to do with incompatible types.
Just get rid of the news because for these data structures they're unnecessary.
int main()
{
Event e1("Whatever");
Visitor v1("Dummy1");
Visitor v2("Dummy2");
e1.addVisitor(v1);
e1.addVisitor(v2);
}
I would suggest that if you don't know how to use pointers you couldn't possibly want to store them instead (they're a hassle IMO to store in the vector when copying by value works just fine).
The compiler generated copy constructor should work just fine.
No manual deep copy required, because you are quite correctly using std::string, which supports RAII.
However, your main function has three memory leaks — there is no need to use new there anyway, so simply don't.
General rule of thumb:
If, at any time T, you're thinking of introducing more pointers into your code, then you're probably going in the wrong direction.
I know I can use:
MyGame game; // the game object
//
ofstream out("mygame.bin", ios::binary);
out.write((char *)&game, sizeof(MyGame));
to save and load the game, but what if I have pointers inside MyGame structure? will the pointers just be saved but not the data it points to?
and: how to solve this?
You can't just write pointers to a stream and expect it to be magically done. You need to implement save/load methods in your objects. E.g:
class Serializable
{
virtual void save(std::ofstream& _out) const = 0;
virtual void load(std::ifstream& _in) = 0;
}; // eo class Serializable
// some game object
class MyObject : public Serializable
{
int myInt;
std::string myString;
virtual void save(std::ofstream& _out) const
{
_out << myInt << myString;
}; // eo save
virtual void load(std::ifstream& _in)
{
_in >> myInt >> myString;
}; // eo load
}; // eo class SomeObject
class MyGame : public Serializable
{
MyObject a;
MyObject b;
virtual void save(std::ofstream& _out) const
{
a.save(_out);
b.save(_out);
}; // eo save
virtual void load(std::ifstream& _in)
{
a.load(_in);
b.load(_in);
}; // eo load
}; // eo class MyGame
Assuming you have not overridden char * cast, yes this will most probably save only pointer and not data.
What you need is Serialization of your object. You can provide a method to marshal the state of object in a bit stream and write that out. And you also need to have method to restore the state back.
You may read more about serialization on wikipedia
Boost has a serialization library, with built in support for deep pointer save and restore, and proper serialization of pointers to shared data.
It's a rather extensive library, but you don't need to write that much code to start using it in your own projects. Well worth the learning effort for anything but the simplest serialization requirements in my opinion.
You could overload the stream out operator (<<) and stream out each individual field (and vice versa)
EDIT: here is a complete example...
#include <iostream>
#include <fstream>
#include <map>
using namespace std;
template <typename T>
void serialize(ostream& str, const T& field)
{
str.rdbuf()->sputn(reinterpret_cast<const char*>(&field), sizeof(T));
}
template <typename T>
void deserialize(istream& str, T& field)
{
str.rdbuf()->sgetn(reinterpret_cast<char*>(&field), sizeof(T));
}
class MyGame
{
public:
MyGame() : a(), b() {}
MyGame(int av, int bv) : a(av), b(bv) {}
friend ostream& operator<<(ostream& str, MyGame const& game);
friend istream& operator>>(istream& str, MyGame& game);
int getA() const { return a; }
int getB() const { return b; }
private:
int a;
int b;
};
ostream& operator<<(ostream& str, MyGame const& game)
{
serialize(str, game.a);
serialize(str, game.b);
return str;
}
istream& operator>>(istream& str, MyGame& game)
{
deserialize(str, game.a);
deserialize(str, game.b);
return str;
}
int main(void)
{
{
ofstream fout("test.bin", ios::binary);
MyGame game(10, 11);
fout << game;
}
{
ifstream fin("test.bin", ios::binary);
MyGame game;
fin >> game;
cout << "game.a: " << game.getA() << ", game.b: " << game.getB() << endl;
}
return 0;
}
You must understand the issues with this approach though, such as the resulting file will be platform specific (i.e. non-portable) etc.
Try game.serialize(out);. In your serialize member function call serialize of your pointer members.
Make a serializing function per type that needs to be persistent.
Call this for each member.
It is actually similar to serializing over network or visualizing for debug-purposes.
boost.serialize can help you.
"Naive" serialization that just dumps the value of pointers is never going to work because, when deserializing, those pointers will be invalid.
The general approach to this kind of problem would go like this:
Have each object that can be serialized in your game implement a serialize() virtual function (I am assuming all such objects will ultimately derive from the same base class).
Have the base class implement a get_serialized_id() public function. This function will use a static auto-incremented variable to generate a unique id for each object instance, but only the first time it is called (subsequent calls will just return the existing value).
Now, when serializing:
Start with a std::map<int, YourBaseClass*>. Add your game object to this map, using the value returned by get_serialized_id() for the key.
While the map contains objects that have not been serialized yet:
Take the first such object.
Serialize its get_serialized_id().
Serialize it by calling its implementation for serialize(). Have it persist its primitive data as usual. For data available through pointers, call get_serialized_id() on each object pointed to and just serialize the number returned from it. Also, add that object to the map.
This will result in a bunch of objects being serialized (in a "random" order) along with each one's "random" id.
When deserializing:
Start with a std::map<int, YourBaseClass*>. Read the first item in your saved file.
For each object pointed to by this first object, you know a unique id (this is what you serialized instead of a pointer). Fetch the item with this id from the saved file and deserialize it.
Recursively do this until all items have been fetched and deserialized from the saved file.
As each item has all its dependences deserialized in step 3 above, instantiate it as an object and add it to the map.
This will enable you to grab a pointer from the map given an item's id, which you can now use to set the pointer members of objects dependent on this item.
When the recursion ends, the last object in the map will be your main "game" object with all its pointers ready to go.
What you did is shallow copy, if you have pointers in your MyGame class, then a deep copy is a MUST!.
I suggest implementing a function or a set of functiions inside MyGame that will take care of saving its own data to a file,and you will only need to call it.
Thanks everyone for the fast and good answers, but a buddy of mine (who is helping me on this) told me we should do it in another way.
I just save the basics of the object and we recreate the rest in a function.
It's a card-game and to save the stack of cards we'll be saving the ID of the card only (not the objects) and just re-initializing each card when we read in the ID from the file.