I am having the following scenario:
Object A
has a map
of Object B
's pointers. When I assign an object B
to A
's map
, A
and B
will decrease a counter, and when I remove B
from A
's map, those counters will increase.
A( free_space = 12 );
B_1( alloc_space = 8 );
B_2( alloc_space = 3 );
A.alloc( B_1, 5 );
//A.free_space -= 5; A.free_space == 7
//B_1.alloc_space -= 5; B_1.alloc_space == 3
A.alloc( B_2, 3 );
//A.free_space -= 3; A.free_space == 4
//B_2.alloc_space -= 3; B_2.alloc_space == 0
A.dealloc( B_1 );
//A.free_space += 5; A.free_space == 9
//B_1.alloc_space += 5; B_1.alloc_space == 8
I should be able to have multiple A
s and B
s, and a single B
could be allocated in more than one A, though only one allocation per A
(hence the map
collection).
Now, since a B
could be in multiple A
s, I was trying to come up with a secure way of implementing these A
's allocations when B is going to be deleted (in order to restore the original A.free_space
value).
The same should happen to B in case I delete an 'A'.
I tried to come up with an std::shared_ptr
and std::weak_ptr
approach to this scenario, but my main problem here is the lack of a smart pointer of B
during B
's destructor (since I am using std::weak_ptr<B>
as the key
to the map
in A
).
Should I just use regular pointers for this problem? Isn't there a better approach to this? Or one where I can use smart pointers?
Anyway, here is a demo code with what I'm trying to accomplish (A
is called Servo
and B
is called Subassembly
in this demo).
#include <map>
#include <set>
#include <iterator>
#include <memory>
#include <iostream>
#include <string>
//------------------------------------------------------------------------------
class Servo;
typedef std::shared_ptr<Servo> PtrServo;
typedef std::weak_ptr<Servo> WeakServo;
class Subassembly {
public:
std::string name;
int alloc_space;
std::set<WeakServo, std::owner_less<WeakServo> > alloc_servos;
Subassembly( std::string a_name, int a_alloc_space ) : name(a_name), alloc_space(a_alloc_space), alloc_servos() {}
~Subassembly();
};
typedef std::shared_ptr<Subassembly> PtrSubassembly;
typedef std::weak_ptr<Subassembly> WeakSubassembly;
typedef std::map< WeakSubassembly, int, std::owner_less<WeakSubassembly> > base_map;
class my_map : protected base_map {
public:
using base_map::base_map;
//using base_map::operator [];
using base_map::begin;
using base_map::end;
Servo& owner;
my_map( Servo& a_owner ) : owner(a_owner), base_map() {}
void add( PtrSubassembly a_subassembly_ptr, int a_value );
void remove( PtrSubassembly a_subassembly_ptr );
};
class Servo : public std::enable_shared_from_this<Servo> {
public:
std::string name;
int free_space;
my_map allocs;
Servo( std::string a_name, int a_free_space ) : name(a_name), free_space(a_free_space), allocs(*this) {}
~Servo() {
for( auto l_alloc : allocs ) {
auto l_ptr = l_alloc.first.lock();
allocs.remove( l_ptr );
}
}
};
void my_map::add( PtrSubassembly a_subassembly_ptr, int a_value ) {
if( !emplace( a_subassembly_ptr, a_value ).second ) {
remove( a_subassembly_ptr );
add( a_subassembly_ptr, a_value );
return;
} else {
owner.free_space -= a_value;
a_subassembly_ptr->alloc_space -= a_value;
a_subassembly_ptr->alloc_servos.insert( owner.shared_from_this() );
}
std::cout << "+\t"
<< owner.name << "[" << owner.free_space << "]\t&&\t"
<< a_subassembly_ptr->name << "[" << a_subassembly_ptr->alloc_space << "] -= " << a_value << "\n";
}
void my_map::remove( PtrSubassembly a_subassembly_ptr ) {
auto l_it = find( a_subassembly_ptr );
if( l_it == end() ) {
return;
}
int l_value = l_it->second;
owner.free_space += l_value;
a_subassembly_ptr->alloc_space += l_value;
a_subassembly_ptr->alloc_servos.erase( owner.shared_from_this() );
erase( l_it );
std::cout << "-\t"
<< owner.name << "[" << owner.free_space << "]\t&&\t"
<< a_subassembly_ptr->name << "[" << a_subassembly_ptr->alloc_space << "] += " << l_value << "\n";
}
Subassembly::~Subassembly() {
for( WeakServo l_servo_wek : alloc_servos ) {
PtrServo l_servo_ptr = l_servo_wek.lock();
if( !l_servo_ptr ) {
continue;
}
for( std::pair<WeakSubassembly,int> l_sub_wek : l_servo_ptr->allocs ) {
PtrSubassembly l_sub_ptr = l_sub_wek.first.lock();
if( !l_sub_ptr ) {
std::cout << "can't lock subassembly weak ptr\n";
continue;
}
if( l_sub_ptr.get() == this ) {
std::cout << "removing subassembly weak ptr\n";
l_servo_ptr->allocs.remove( l_sub_ptr );
} else {
std::cout << "pointer not equal\n";
}
}
//std::cout << "got a valid lock\n";
//l_servo_ptr->remove()
}
}
//------------------------------------------------------------------------------
int main()
{
PtrServo l_head( new Servo { "head", 3 } );
PtrServo l_torso( new Servo { "torso", 8 } );
PtrSubassembly l_sensor_ptr = std::make_shared<Subassembly>( "sensor", 4 );
PtrSubassembly l_weapon_ptr = std::make_shared<Subassembly>( "weapon", 8 );
l_head->allocs.add( l_sensor_ptr, 4 );
l_head->allocs.add( l_sensor_ptr, 1 );
l_torso->allocs.add( l_sensor_ptr, 3 );
l_torso->allocs.add( l_weapon_ptr, 4 );
//l_torso.reset();
std::set<PtrServo> l_servos = { l_head, l_torso };
for( auto l_servo : l_servos ) {
if( !l_servo ) {
continue;
}
for( auto l_alloc : l_servo->allocs ) {
PtrSubassembly l_sub_ptr = l_alloc.first.lock();
std::cout << l_servo->name << "[" << l_servo->free_space << "]" << "\t" << l_sub_ptr->name << "\t" << l_alloc.second << "\n";
}
}
return 0;
}
And yes, in case you might be wondering, these are references to the Mekton Zeta
RPG.
Aucun commentaire:
Enregistrer un commentaire