I want to create a C++ objects factory that will create some objects by id. Each object must have a references counter. If an object with the same id is requested again, the same object must be returned if it's still in memory.
While there is something keeping a pointer to an object, this object will not be deleted. When there is no pointers to the object but pointer in factory cache, this object is placed in QCache and will be deleted if it will not be requested again for some time.
What's the best way to implement this?
Here is how I would do it.
First of all, the factory class would only hold observing pointers to the objects it instantiates. This way, objects will be immediately deleted when no owning references to them exist, without putting them in a queue.
Then, the factory class would return shared pointers to the objects it instantiates, and those shared pointers would specify a custom deleter to unregister the deleted object from the factory's map upon destruction.
Assuming that the objects you want to instantiate have a constructor which accept their ID as an argument, and a function get_id() to return their ID, here is the code for the factory class:
#include <memory>
#include <unordered_map>
#include <functional>
using namespace std;
using namespace std::placeholders;
template<typename T>
class factory
{
public:
shared_ptr<T> get_instance(int id)
{
auto i = m_map.find(id);
if (i == m_map.end())
{
return create(id);
}
else
{
shared_ptr<T> p = i->second.lock();
if (p == nullptr)
{
p = create(id);
}
return p;
}
}
shared_ptr<T> create_instance()
{
shared_ptr<T> p = create(nextId);
nextId++;
return p;
}
void unregister(T* p)
{
int id = p->get_id();
m_map.erase(id);
delete p;
}
private:
shared_ptr<T> create(int id)
{
shared_ptr<T> p(new T(id), bind(&factory::unregister, this, _1));
m_map[id] = p;
return p;
}
unordered_map<int, weak_ptr<T>> m_map;
int nextId = 0;
};
And that's how you would use it:
struct A
{
A(int id) : _id(id) { }
int get_id() const { return _id; }
int _id;
};
int main()
{
factory<A> f;
{
shared_ptr<A> pA = f.get_instance(5);
shared_ptr<A> pB = pA;
// ...
// The object with ID 5 will go out of scope and get unregistered
}
shared_ptr<A> pA = f.get_instance(3);
shared_ptr<A> pB = f.get_instance(3); // This will return the existing object
//
// Instance with ID 3 will go out of scope and get unregistered
}
Related
I'm testing custom deleters for unique_ptr.
Strangely, only deleters that are function objects work fine.
If I replace them with functions or lambdas, program doesn't compile.
What am I doing wrong?
Here is my complete test program
#include <iostream>
#include <memory>
using namespace std;
class Vehicle {
public:
Vehicle(){ cout<<"Vehicle constructor..."<<endl;}
virtual ~Vehicle(){cout<<"~Vehicle destructor..."<<endl;}
virtual void go()=0;
};
class Car:public Vehicle {
public:
void go() override {
cout<<"Going by car..."<<endl;
}
};
class Bicycle:public Vehicle {
public:
void go() override {
cout<<"Going by bicycle..."<<endl;
}
};
// Custom deleters
auto CustomLambdaDeleter = [](Vehicle* v){
cout<<"Custom lambda deleter called..."<<endl;
delete v;
};
void CustomFunctionDeleter(Vehicle* v){
cout<<"Custom function deleter called..."<<endl;
delete v;
}
struct CustomFunctorDeleter
{
void operator()(Vehicle* v ) const {
cout<<"Custom functor deleter called..."<<endl;
delete v;
}
};
// Doesn't compile
//using VehiclePtr = unique_ptr<Vehicle, decltype(CustomLambdaDeleter)>;
// Doesn't compile
//using VehiclePtr = unique_ptr<Vehicle, decltype(&CustomFunctionDeleter)>;
// Works ok
using VehiclePtr = unique_ptr<Vehicle, CustomFunctorDeleter>;
class VehicleFactory {
public:
static VehiclePtr createVehicle(string type){
VehiclePtr vptr;
if("bicycle"==type) {
vptr.reset(new Bicycle());
// This also works
// vptr= (VehiclePtr) new Bicycle();
return vptr;
}
else if("car"==type) {
vptr.reset( new Car());
return vptr;
}
return nullptr;
}
};
void vehicleFactoryTest(){
cout<<"* Starting vehicleFactoryTest()..."<<endl;
auto firstVehicle = VehicleFactory::createVehicle("bicycle");
firstVehicle->go();
auto newCar = VehicleFactory::createVehicle("car");
newCar->go();
}
int main(int, char **)
{
vehicleFactoryTest();
return 0;
}
The problem isn't with either of
using VehiclePtr = unique_ptr<Vehicle, decltype(CustomLambdaDeleter)>;
or
using VehiclePtr = unique_ptr<Vehicle, CustomFunctorDeleter>;
Those two compile by themselves. The problem lies with the following line within createVehicle
VehiclePtr vptr;
Here, you're default constructing a unique_ptr, which will not compile in case of the lambda deleter because lambdas are not default constructible. So you need
VehiclePtr vptr{nullptr, CustomLambdaDeleter};
In the case where you're using a function pointer, you're attempting to default construct the unique_ptr with a nullptr deleter, which is not allowed. The fix is similar, you need to pass a pointer to the function in that case.
VehiclePtr vptr{nullptr, CustomFunctionDeleter};
You're also making a similar error in the final return statement in createVehicle. Change that line to
return vptr;
I am creating a Lua api for my program.
I want to be able to do the following:
void* CreateA()
{
A* a = new A();
PointerTypes[a] = A;
return reinterpret_cast<void*>(a);
}
void* CreateB()
{
B* b = new B();
PointerTypes[b] = B;
return reinterpret_cast<void*(b);
}
void DeleteThing( void* p )
{
typename type = PointerTypes[p];
type* t = reinterpret_cast< type >( p );
delete t;
}
Is there any straightforward way to do this?
PS: My application already uses RTTI so it can be used here too.
Instead of saving the type in a map (which is not possible because types aren't first class objects in C++) you could store a deleter function in a map. Your factory function then becomes:
void* CreateA()
{
A *a = new A();
PointerDeleters[a] = [](void *obj) { delete static_cast<A*>(obj); };
return a;
}
or with a function template:
template<typename T>
void* Create() // maybe you want to forward c'tor args using variadic template
{
T *a = new T();
PointerDeleters[t] = [](void *obj) { delete static_cast<T*>(obj); };
return t;
}
And then invoke it to trigger the deletion of the object p with unknown type:
void DeleteThing(void* p)
{
PointerDeleters[p]();
}
The map PointerDeleters should then have the value type std::function<void(void*)>.
A better solution would be (if your design allows it) to use a base class with a virtual destructor; then you can simply delete a pointer to that class without storing any additional information:
template<typename T>
BaseClass* Create()
{
return new T();
}
void DeleteThing(BaseClass* p)
{
delete p;
}
I have a class that stores weak_ptrs in a container and later does something if the weak_ptr is not expired:
class Example
{
public:
void fill(std::shared_ptr<int> thing)
{
member.push_back(thing);
}
void dosomething() const
{
for (const auto& i : member)
if (!i.expired())
;// do something. the weak_ptr will not be locked
}
private:
std::vector<std::weak_ptr<int>> member;
};
If Example is an object that lives forever and fill is used regularily, the vector allocates memory for elements continously, but they are never removed after they expired.
Is there any automatic C++ way to get rid of the expired weak_ptrs in the container or is there a better way to store a variable number of them?
My naive way would be to iterate over the container each time fill is called and remove all the expired weak_ptrs. In scenarios where Example has many elements in the container and fill is frequently called this seems to be very inefficient.
Since you clarified that you are actually using a std::map and not a std::vector, it might be easiest to remove the expired elements on-the-fly in doSomething(). Switch back from a range-based for loop to a normal iterator based design:
void dosomething() const
{
auto i = member.begin();
while( i != member.end() ) {
if( i->expired() ) { i = member.erase( i ); continue; }
;// do something. the weak_ptr will not be locked
++i;
}
}
Does the shared_ptr<int> have to be a shared_ptr<int>?
How about a shared_ptr<IntWrapper>?
#include <iostream>
#include <forward_list>
using namespace std;
class IntWrapper {
public:
int i;
static forward_list<IntWrapper*>& all() {
static forward_list<IntWrapper*> intWrappers;
return intWrappers;
}
IntWrapper(int i) : i(i) {
all().push_front(this);
}
~IntWrapper() {
all().remove(this);
}
};
void DoSomething() {
for(auto iw : IntWrapper::all()) {
cout << iw->i << endl;
}
}
int main(int argc, char *argv[]) {
shared_ptr<IntWrapper> a = make_shared<IntWrapper>(1);
shared_ptr<IntWrapper> b = make_shared<IntWrapper>(2);
shared_ptr<IntWrapper> c = make_shared<IntWrapper>(3);
DoSomething();
return 0;
}
I would rather use a custom deleter for the shared_ptr. But this implies here to change the interface of the Example class. The advantage using custom deleter is that there is no need to check for expired objects in the collection. The collection is directly maintained by the custom deleter.
Quick implementation :
#include <memory>
#include <iostream>
#include <set>
template <typename Container>
// requires Container to be an associative container type with key type
// a raw pointer type
class Deleter {
Container* c;
public:
Deleter(Container& c) : c(&c) {}
using key_type = typename Container::key_type;
void operator()(key_type ptr) {
c->erase(ptr);
delete ptr;
}
};
class Example {
public:
// cannot change the custom deleter of an existing shared_ptr
// so i changed the interface here to take a unique_ptr instead
std::shared_ptr<int> fill(std::unique_ptr<int> thing) {
std::shared_ptr<int> managed_thing(thing.release(), Deleter<containter_type>(member));
member.insert(managed_thing.get());
return managed_thing;
}
void dosomething() const {
// we don't need to check for expired pointers
for (const auto & i : member)
std::cout << *i << ", ";
std::cout << std::endl;
}
using containter_type = std::set<int*>;
private:
containter_type member;
};
int main()
{
Example example;
auto one = example.fill(std::unique_ptr<int>(new int(1)));
auto two = example.fill(std::unique_ptr<int>(new int(2)));
auto three = example.fill(std::unique_ptr<int>(new int(3)));
example.dosomething();
three.reset();
example.dosomething();
}
I have a map of addresses that allows me to store arbitrary data with objects. Basically, a library I'm writing has a templated function that winds up storing arbitrary data with objects.
std::map<void *, MyUserData>
This works, until the object passed in is destroyed, leaving its user data in the map. I want the associated user data to be removed as well, so I need to somehow listen for the destructor of the passed in object,
Some example code that illustrates the problem:
#include <map>
#include <memory>
struct MyUserData
{
int someNum;
};
std::map<void *, MyUserData> myMap;
template <typename T>
registerObject<T>(const std::shared_ptr<T> & _object)
{
static inc = 0;
myMap[(void *)&_object->get()].someNum = inc++;
}
struct MyObject
{
int asdf;
};
int main(int _argc, char ** _argv)
{
auto obj = std::make_shared<MyObject>();
obj->asdf = 5;
registerObject(obj);
obj = 0;
//The user data is still there. I want it to be removed at this point.
}
My current solution is to set a custom deleter on the shared_ptr. This signals me for when the object's destructor is called, and tells me when to remove the associated user data. Unfortunately, this requires my library to create the shared_ptr, as there is no "set_deleter" function. It must be initialized in the constructor.
mylib::make_shared<T>(); //Annoying!
I could also have the user manually remove their objects:
mylib::unregister<T>(); //Equally annoying!
My goal is to be able to lazily add objects without any prior-registration.
In a grand summary, I want to detect when the object is deleted, and know when to remove its counterpart from the std::map.
Any suggestions?
P.S. Should I even worry about leaving the user data in the map? What are the chances that an object is allocated with the same address as a previously deleted object? (It would end up receiving the same user data as far as my lib is concerned.)
EDIT: I don't think I expressed my problem very well initially. Rewritten.
From you code example, it looks like the external interface is
template <typename T>
registerObject<T>(const std::shared_ptr<T> & _object);
I assume there is a get-style API somewhere. Let's call this getRegisteredData. (It could be internal.)
Within the confines of the question, I'd use std::weak_ptr<void> instead of void*, as std::weak_ptr<T> can tell when there are no more "strong references" to the object around, but won't prevent the object from being deleted by maintaining a reference.
std::map<std::weak_ptr<void>, MyUserData> myMap;
template <typename T>
registerObject<T>(const std::shared_ptr<T> & _object)
{
static inc = 0;
Internal_RemoveDeadObjects();
myMap[std::weak_ptr<void>(_object)].someNum = inc++;
}
template <typename T>
MyUserData getRegisteredData(const std::shared_ptr<T> & _object)
{
Internal_RemoveDeadObjects();
return myMap[std::weak_ptr<void>(_object)];
}
void Internal_RemoveDeadObjects()
{
auto iter = myMap.cbegin();
while (iter != myMap.cend())
{
auto& weakPtr = (*iter).first;
const bool needsRemoval = !(weakPtr.expired());
if (needsRemoval)
{
auto itemToRemove = iter;
++iter;
myMap.erase(itemToRemove);
}
else
{
++iter;
}
}
}
Basically, std::weak_ptr and std::shared_ptr collaborate and std::weak_ptr can detect when there are no more std::shared_ptr references to the object in question. Once that is the case, we can remove the ancillary data from myMap. I'm using the two interfaces to myMap, your registerObject and my getRegisteredData as convenient places to call Internal_RemoveDeadObjects to perform the clean up.
Yes, this walks the entirety of myMap every time a new object is registered or the registered data is requested. Modify as you see fit or try a different design.
You ask "Should I even worry about leaving the user data in the map? What are the chances that an object is allocated with the same address as a previously deleted object?" In my experience, decidedly non-zero, so don't do this. :-)
I'd add a deregister method, and make the user deregister their objects. With the interface as given, where you're stripping the type away, I can't see a way to check for the ref-count, and C++ doesn't provide a way to check whether memory has been deleted or not.
I thought about it for a while and this is as far as I got:
#include <memory>
#include <map>
#include <iostream>
#include <cassert>
using namespace std;
struct MyUserData
{
int someNum;
};
map<void *, MyUserData> myMap;
template<class T>
class my_shared_ptr : public shared_ptr<T>
{
public:
my_shared_ptr() { }
my_shared_ptr(const shared_ptr<T>& s) : shared_ptr<T>(s) { }
my_shared_ptr(T* t) : shared_ptr<T>(t) { }
~my_shared_ptr()
{
if (unique())
{
myMap.erase(get());
}
}
};
template <typename T>
void registerObject(const my_shared_ptr<T> & _object)
{
static int inc = 0;
myMap[(void *)_object.get()].someNum = inc++;
}
struct MyObject
{
int asdf;
};
int main()
{
{
my_shared_ptr<MyObject> obj2;
{
my_shared_ptr<MyObject> obj = make_shared<MyObject>();
obj->asdf = 5;
registerObject(obj);
obj2 = obj;
assert(myMap.size() == 1);
}
/* obj is destroyed, but obj2 still points to the data */
assert(myMap.size() == 1);
}
/* obj2 is destroyed, nobody points to the data */
assert(myMap.size() == 0);
}
Note however that it wouldn't work if you wrote obj = nullptr; , or obj.reset(), since the object isn't destroyed in those cases (no destructor called). Also, you can't use auto with this solution.
Also, be careful not to call (void *)&_object.get() like you were doing. If I'm not terribly wrong, by that statement you're actually taking the address of the temporary that _object.get() returns, and casting it to void. That address, however, becomes invalid instantly after.
This sounds like a job for... boost::intrusive (http://www.boost.org/doc/libs/1_53_0/doc/html/intrusive.html)! I don't think the current interface will work exactly as it stands though. I'll try to work out a few more details a little later as I get a chance.
You can just do
map.erase(map.find(obj));
delete obj;
obj = 0;
this will call the destructor for your user data and remove it from the map.
Or you could make your own manager:
class Pointer;
extern std::map<Pointer,UserData> data;
class Pointer
{
private:
void * pointer;
public:
//operator ()
void * operator()()
{
return pointer;
}
//operator =
Pointer& operator= (void * ptr)
{
if(ptr == 0)
{
data.erase(data.find(pointer));
pointer = 0;
}
else
pointer = ptr;
return *this;
}
Pointer(void * ptr)
{
pointer = ptr;
}
Pointer()
{
pointer = 0;
}
~Pointer(){}
};
struct UserData
{
static int whatever;
UserData(){}
};
std::map<Pointer,UserData> data;
int main()
{
data[Pointer(new UserData())].whatever++;
data[Pointer(new UserData())].whatever++;
data[Pointer(new UserData())].whatever++;
data[Pointer(new UserData())].whatever++;
Pointer x(new UserData());
data[x].whatever;
x = 0;
return 0;
}
What I want to do is to write a small Manager/Handler class. A Manager distributes and manages Handles. Such a handle could be for example a simple filehandle.
If a consumer wants to get a handle which already exists, the manager simply returns a shared_ptr. If the handle does not exist, the manager creates a new handle and then returns the shared_ptr.
Inside the Manager, those shared_ptr's are stored in a simple STL-Map.
If the last shared_ptr, which was assigned gets deleted, I want my manager to remove the related map-element, so that the handler object automatically gets destructed.
This sounds a bit like garbage-collection(e.g. worker thread, which checks the usage count of the pointers), but I am sure it can be done more elegantly.
How do I pass a reference of the manager instance to the handler object? (e.g. sth. like passing a unique_ptr(this) to the constructor of a new handler)
#include <memory>
#include <iostream>
#include <map>
using namespace std;
/*
* Simple handler class, that actually does nothing.
* This could be e.g. a Filehandler class or sth. like that
*/
class Handler {
private:
int i;
public:
Handler(int i) :i(i) {}
~Handler() {}
// Say who you are.
void print(void) { cout << "I am handler # " << i << endl; }
};
/*
* This is the "manager" class, that manages all handles. A handle is identified
* by an integer value. If a handle already exists, the Manager returns a shared_ptr,
* if it does not exist, the manager creates a new handle.
*/
class Manager {
private:
map<int, shared_ptr<Handler> > handles;
public:
Manager() {}
~Manager() {}
shared_ptr<Handler> get_handler(int identifier) {
shared_ptr<Handler> retval;
auto it = handles.find(identifier);
if(it != handles.end() ) {
retval = it->second;
} else {
retval = shared_ptr<Handler>(new Handler(identifier));
handles.insert( pair<int, shared_ptr<Handler>>(identifier, retval) );
}
return retval;
}
};
int main(int argc, char** argv) {
Manager m;
// Handler 13 doesn't exist, so it gets allocated
auto h = m.get_handler(13);
// Manager knows about handler 13, so it returns the already existing shared_ptr
auto i = m.get_handler(13);
h.reset(); // Well... Let's assume we don't need h any more...
// do some stuff...
i->print();
// ...
i.reset(); // We also loose i. This is exactly the point where i want the manager to forget about the handle 13
return 0;
}
You may want to hold non-owning pointers in your manager to keep track of existing handles, and give away owning shared_ptr with a custom deleter. The custom deleter would make sure the corresponding observing pointer in the manager is removed when the object eventually gets destroyed.
I called this pattern Tracking Factory, and here is how it works. Given an object class (would be Handler in your case):
class object
{
public:
size_t get_id() const
{
return _id;
}
private:
friend class tracking_factory;
object(size_t id) : _id(id) { }
size_t _id = static_cast<size_t>(-1);
};
I define a class which creates instances of object and stores non-owning references (weak_ptrs) to them. This class is the only class through which instances of object can be created - this is why the constructor of object is private, and tracking_factory is declared as friend in order to be able to access it:
class tracking_factory
{
public:
std::shared_ptr<object> get_object(size_t id,
bool createIfNotFound = true)
{
auto i = std::find_if(
begin(_objects),
end(_objects),
[id] (std::pair<size_t const, std::weak_ptr<object>> const& p)
-> bool
{
return (p.first == id);
});
if (i != end(_objects))
{
return i->second.lock();
}
else if (createIfNotFound)
{
return make_object(id);
}
else
{
return std::shared_ptr<object>();
}
}
size_t count_instances() const
{
return _objects.size();
}
private:
std::shared_ptr<object> make_object(size_t id)
{
std::shared_ptr<object> sp(
new object(id),
[this, id] (object* p)
{
_objects.erase(id);
delete p;
});
_objects[id] = sp;
return sp;
}
std::map<size_t, std::weak_ptr<object>> _objects;
};
Then, the rest of program will obtain shared_ptrs to objects through the object_factory: if an object with the desired characteristics (an id member here) has been created already, a shared_ptr to it will be returned without a new object being instantiated. Here is some code to test the functionality:
#include <iostream>
int main()
{
tracking_factory f;
auto print_object_count = [&f] ()
{
std::cout << "Number of objects: " << f.count_instances() << std::endl;
};
print_object_count();
auto p1 = f.get_object(42);
print_object_count();
{
auto p2 = f.get_object(42);
print_object_count();
p1 = f.get_object(0);
print_object_count();
}
print_object_count();
p1.reset();
print_object_count();
}
Finally, here is a live example.
Store std::weak_ptr objects in the map; they don't retain ownership, so when the last std::shared_ptr object goes away the resource will be destroyed. But they do keep track of whether there are any remaining std::shared_ptr objects that point to the original object, so putting them in the map lets you check later whether there is still a resource there.