mardi 27 janvier 2015

Stuck with a double linked pattern update

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 As and Bs, 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 As, 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