Circular dependency and shared_ptr

C

Christopher Pisz

What are one's options when the code one is working on contains a
circular dependency and one is trying to update it to use a shared_ptr
vs a raw pointer?

I don't think I can easily get rid of the preexisting circular
dependency. The original author has created an inheritance where the
base needs the derived. We all know this is wrong, but it would take
quite a bit to analyze how it is used and be removed.

Am I out of luck?


Fictitious Example (best minimal example including the problems the
preexisting code has):

// File MasterClient.h
#include "BaseClient.h"
#include <set>

class MasterClient : BaseClient
{
public:

// Constructor adds this to the collection of clients
// It is later included in the processing of all clients
MasterClient();

// SNIP
private:
typedef std::set<Base *> Clients;
Clients clients_;

// Thread function that iterates over all clients
// including this instance and makes calls on them
void Process();
};

// file BaseClient.h
class MasterClient;

class BaseClient
{
public:

// Constructor
BaseClient(MasterClient * master);

// SNIP interface

private:
MasterClient * master_; // I really need this to be a shared_ptr

// Thread function that makes calls to the MasterClient
virtual void Process();
};
 
R

Richard Damon

What are one's options when the code one is working on contains a
circular dependency and one is trying to update it to use a shared_ptr
vs a raw pointer?

I don't think I can easily get rid of the preexisting circular
dependency. The original author has created an inheritance where the
base needs the derived. We all know this is wrong, but it would take
quite a bit to analyze how it is used and be removed.

Am I out of luck?


Fictitious Example (best minimal example including the problems the
preexisting code has):

// File MasterClient.h
#include "BaseClient.h"
#include <set>

class MasterClient : BaseClient
{
public:

// Constructor adds this to the collection of clients
// It is later included in the processing of all clients
MasterClient();

// SNIP
private:
typedef std::set<Base *> Clients;
Clients clients_;

// Thread function that iterates over all clients
// including this instance and makes calls on them
void Process();
};

// file BaseClient.h
class MasterClient;

class BaseClient
{
public:

// Constructor
BaseClient(MasterClient * master);

// SNIP interface

private:
MasterClient * master_; // I really need this to be a shared_ptr

// Thread function that makes calls to the MasterClient
virtual void Process();
};

To my knowledge, a shared pointer can be declared on a forward declaired
class, the restriction is that when you actually create the pointer you
need to see the definition of the class. Thus, you need to explicitly
write your constructors in a file (like BaseClient.cpp) that includes
MasterClient.h and not use an inline definition.

Note that you will also need to do this for the copy constructor,
probably you should also write operator=() and the destructor, and not
just use the default inline versions.
 
L

Larry Evans

What are one's options when the code one is working on contains a
circular dependency and one is trying to update it to use a shared_ptr
vs a raw pointer?

I don't think I can easily get rid of the preexisting circular
dependency. The original author has created an inheritance where the
base needs the derived. We all know this is wrong, but it would take
quite a bit to analyze how it is used and be removed.

Am I out of luck?
I thought weak_ptr was designed for this situation:

http://cboard.cprogramming.com/cplusplus-programming/109986-i-dont-get-weak_ptrs.html

HTH.

-Larry
 
R

Richard Damon


It doesn't look like he really has a "circular" dependency.

For objects, each Base has a SharedPointer to a Master, which has a
std:set of (normal) pointers to those Bases. The fact that Master uses
normal pointers to the Bases has broken the cycle here.

The problem he was expressing was an apparent cycle in deceleration
dependency which weak_ptr does nothing about. It is only apparent
because as I pointed out, BaseClient.cpp will depend on MasterClient.h
which will depend on BaseClient.h the key is that BaseClient.h can not
declare the implementation of certain member functions, including some
that will be generated by default. Making these explicitly generated in
a cpp file will break the cycle of dependency.
 
C

Christopher Pisz

It doesn't look like he really has a "circular" dependency.

For objects, each Base has a SharedPointer to a Master, which has a
std:set of (normal) pointers to those Bases. The fact that Master uses
normal pointers to the Bases has broken the cycle here.

The problem he was expressing was an apparent cycle in deceleration
dependency which weak_ptr does nothing about. It is only apparent
because as I pointed out, BaseClient.cpp will depend on MasterClient.h
which will depend on BaseClient.h the key is that BaseClient.h can not
declare the implementation of certain member functions, including some
that will be generated by default. Making these explicitly generated in
a cpp file will break the cycle of dependency.

I gave it my best shot and have 2 errors that I can't figure out:
C2664: BaseClient::BaseClient(boost::shared_ptr<T>) cannot convert
parameter 1 from boost::shared_ptr<T> to boost::shared_ptr<T>
C2385: ambiguous access of 'shared_from_this'

I have to use enable shared from this to be passing around the
shared_ptr from myself, no?

I believe both classes cannot inherit from it. I am not sure how to
change it up. any suggestions?

We are using VC2008, so I cannot use the standard shared_ptr yet, but I
believe the boost version is the same...

My attempt:

/*
* BaseClient.h
*/
#ifndef BASECLIENT_H
#define BASECLIENT_H

#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>

//-------------------------------
class MasterClient;

//-------------------------------
class BaseClient : public boost::enable_shared_from_this<BaseClient>
{
public:

typedef boost::shared_ptr<BaseClient> SharedPtr;

// Enforces that every instance must be a shared_ptr for
shared_from_this requirement
static BaseClient::SharedPtr Create(boost::shared_ptr<MasterClient>
master);

protected:

BaseClient(boost::shared_ptr<MasterClient> master);
virtual ~BaseClient();

boost::shared_ptr<MasterClient> master_;
};

#endif

/*
* BaseClient.cpp
*/

#include "BaseClient.h"
#include "MasterClient.h"

//-------------------------------
BaseClient::SharedPtr BaseClient::Create(boost::shared_ptr<MasterClient>
master)
{
return BaseClient::SharedPtr(new BaseClient(master));
}

//-------------------------------
BaseClient::BaseClient(boost::shared_ptr<MasterClient> master)
:
master_(master)
{
master->InsertClient(shared_from_this());
}

//-------------------------------
BaseClient::~BaseClient()
{
}

/*
* MasterClient.h
*/
#ifndef MASTERCLIENT_H
#define MASTERCLIENT_H

#include "BaseClient.h"

#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <set>

//-------------------------------
class MasterClient : public BaseClient , public
boost::enable_shared_from_this<MasterClient>
{
public:

typedef boost::shared_ptr<MasterClient> SharedPtr;

// Enforces that every instance must be a shared_ptr for
shared_from_this requirement
static MasterClient::SharedPtr Create();

protected:

typedef std::set<BaseClient::SharedPtr> Clients;
Clients clients_;

MasterClient();
~MasterClient();
};

#endif

/*
* MasterClient.cpp
*/

#include "MasterClient.h"

//-------------------------------
MasterClient::SharedPtr MasterClient::Create()
{
return MasterClient::SharedPtr(new MasterClient());
}

//-------------------------------
MasterClient::MasterClient()
:
BaseClient(MasterClient::shared_from_this())
{
}

//-------------------------------
MasterClient::~MasterClient()
{
}
 
C

Christopher Pisz



From boost docs:
Q. Can an object create a weak_ptr to itself in its constructor?

A. No. A weak_ptr can only be created from a shared_ptr, and at object
construction time no shared_ptr to the object exists yet.

This is my problem that I cannot overcome. It works with a raw pointer,
but then I cannot achieve my goal.

I have to supply a pointer from GlobalClient to BaseClient upon
construcion, because a BaseClient cannot be constructed without a
pointer to its owner.
 
R

Richard Damon

One thing that I note, is that it appears that you are pointing the
shared_ptr in the base of the MasterClient, to that MasterClient. Unless
you have something somewhere that will reset this shared_ptr, you are
basically negating the purpose of shared_ptr as the object will NEVER
get deleted as there will always be a shared_ptr referring to it, the
one that it holds to itself.

I think you need to think about WHY you want these to be shared
pointers, and what you want the various lifetimes of the objects to be.

Some how you have also decide that BaseClients are being managed by
shared_ptr also, why it this?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,969
Messages
2,570,161
Members
46,705
Latest member
Stefkari24

Latest Threads

Top