C++ - Smart Pointers - Casting smart pointers inside templates
I have a complex code base at work, and i created a small example to mimic the problem and here is the below code.
< Code below for reference> - This code is compilable if we have boost libraries and FastDelegate.h linked with the project. Please let me know if you need the full compilable example project, i can email you.
I 开发者_开发问答have two problems and need help resolving them.
- As seen below in the code, i have a class with argument type as template for another classes object. Now when i initialize the class below in UserClass's constructor (Line 107) i get error because mBaseAcceptor is a class with template argument of type base Class, but i need to do mbaseAcceptor(new derivedAcceptor_t). Casting problem how to fix this?
Error here is
./boost/smart_ptr/shared_ptr.hpp:387:9: error: comparison between distinct pointer types ‘Acceptor<DerivedClass>*’ and ‘Acceptor<BaseClass>*’ lacks a cast
Another problem is in line 108, even if i magically say resolve this by using another acceptor of derived class, this is where i use that mDerivedAcceptor, in Line 108 i do
mDerivedAcceptor->SetDelegate(fastdelegate::MakeDelegate(this, &UserClass::HandleDelegate));
then i get error saying
"error no matching function call for HandleDelegate(DerivedClass&, bool).
This make sense because HandleDelegate has argument of type BaseClass and by storing a delegate(which is a func. ptr) we have to call the function with appropriate argument. But how to fix this.
- If i cast Handler inside Acceptor class with derived class will it work when i only pass the baseClass pointer?
Code
/*
* smart_pointer_1.cpp
*
* Created on: Jul 26, 2011
* Author: balaji
*/
#include <algorithm>
#include <boost/foreach.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include "FastDelegate.h"
#include <iostream>
using namespace std;
template <class Handler>
class Acceptor {
public:
typedef fastdelegate::FastDelegate1<Handler &, bool> delegate_t;
Acceptor ();
void Initialize(Handler *&handle);
void SetDelegate(delegate_t delegate) { mDelegate = delegate; }
private:
int mValues[2];
delegate_t mDelegate;
};
template <class Handler>
Acceptor<Handler>::Acceptor()
{
std::cout << "In Constructor: " << __FUNCTION__ << std::endl;
mValues[0] = 1;
mValues[1] = 2;
}
template <class Handler>
void Acceptor<Handler>::Initialize(Handler *&handle){
if (!handle) {
std::cout << __FUNCTION__ << " : created" << std::endl;
handle = new Handler();
} else {
std::cout << __FUNCTION__ << " : Error exception" << std::endl;
}
if (mDelegate && mDelegate(*handle)) {
std::cout << "Ok Called Handle in " << __FUNCTION__ << std::endl;
} else {
std::cout << "Not Called Handle in " << __FUNCTION__ << std::endl;
}
handle->displayComputer();
}
class BaseClass {
std::string mComputer;
public:
BaseClass() {
std::cout << "In Base Constructor: " << __FUNCTION__ << std::endl;
mComputer = "Mac";
}
virtual void displayComputer() {
std::cout << "Computer type is " << mComputer << std::endl;
}
};
class DerivedClass : public BaseClass {
std::string mLanguage;
public:
DerivedClass() {
std::cout << "In Derived Constructor: " << __FUNCTION__ << std::endl;
mLanguage = "C++";
}
void displayComputer() {
std::cout << "Language is " << mLanguage << std::endl;
}
};
class UserClass {
public:
UserClass();
UserClass(bool);
typedef Acceptor<BaseClass> baseAcceptor_t;
typedef Acceptor<DerivedClass> derivedAcceptor_t;
typedef boost::shared_ptr<BaseClass> basePtr_t;
void CallDelegate(BaseClass&);
private:
boost::shared_ptr<baseAcceptor_t> mBaseAcceptor;
boost::shared_ptr<derivedAcceptor_t> mDerivedAcceptor;
BaseClass *mConnBasePtr;
bool HandleDelegate(BaseClass& baseDelegate);
};
UserClass::UserClass() : mBaseAcceptor(new baseAcceptor_t)
{
std::cout << "In Constructor: " << __FUNCTION__ << std::endl;
mBaseAcceptor->SetDelegate(fastdelegate::MakeDelegate(this, &UserClass::HandleDelegate));
mBaseAcceptor->Initialize(mConnBasePtr);
}
UserClass::UserClass(bool value)
{
std::cout << "In Constructor: " << __FUNCTION__ << std::endl;
mBaseAcceptor.reset(new derivedAcceptor_t); // <<========== Problem Here because of improper casting
mBaseAcceptor->SetDelegate(fastdelegate::MakeDelegate(this, &UserClass::HandleDelegate)); // <<=== Also here because of improper type passed to MakeDelegate function ptr. Please note HandleDelegate has an argument of type BaseClass, but Acceptor is derived class
mBaseAcceptor->Initialize(mConnBasePtr);
}
bool UserClass::HandleDelegate(BaseClass& baseDelegate)
{
std::cout << "In " << __FUNCTION__ << std::endl;
return true;
}
int main() {
std::cout << "In function: " << __FUNCTION__ << std::endl;
typedef boost::shared_ptr<UserClass> userPtr_t;
userPtr_t user(new UserClass(true));
std::cout << "In function: " << __FUNCTION__ << " at end "<< std::endl;
return 0;
}
Acceptor<DerivedClass>
is not derived from Acceptor<BaseClass>
(it doesn't matter that DerivedClass
is derived from BaseClass
or not) so the compiler can not cast one into the other.
I would get rid of the templatization of the acceptor, unless you have a good reason to keep it (which I don't see in your code) :
class Acceptor {
public:
typedef fastdelegate::FastDelegate1<BaseClass &, bool> delegate_t;
Acceptor ();
void Initialize(BaseClass *handle);
void SetDelegate(delegate_t delegate) { mDelegate = delegate; }
private:
int mValues[2];
delegate_t mDelegate;
};
void Acceptor::Initialize(BaseClass *handle){
if (!handle) {
std::cout << __FUNCTION__ << " : Error exception" << std::endl;
}
if (mDelegate && mDelegate(*handle)) {
std::cout << "Ok Called Handle in " << __FUNCTION__ << std::endl;
} else {
std::cout << "Not Called Handle in " << __FUNCTION__ << std::endl;
}
handle->displayComputer();
}
Then you don't need separate baseAcceptor_t
and derivedAcceptor_t
types as they both become simply Acceptor
, and you can do for example :
UserClass::UserClass() : mBaseAcceptor(new Acceptor(new BaseClass))
As far as I see the only thing you loose is the ability to pass a null pointer to the acceptor's constructor and have it create its handler itself. That's a very minor loss as the real decision (instantiate a base or a derived handler) is really taken when you instantiate the Acceptor
anyway (because you choose which of Acceptor<BaseClass>
or Acceptor<DerivedClass>
you want)
Define base class for the Acceptor template and another class that will be base to all Handler types. So your implementation will change to:
class IHandler {
};
class IAcceptor {
public:
virtual void Initialize(IHandler *) = 0;
virtual void SetDelegate(delegate_t delegate) = 0;
};
Your Acceptor template will change to:
template <class Handler>
class Acceptor : public IAcceptor {
public:
typedef fastdelegate::FastDelegate1<Handler &, bool> delegate_t;
Acceptor ();
void Initialize(IHandler *pVal);
void SetDelegate(delegate_t delegate) { mDelegate = delegate; }
private:
int mValues[2];
delegate_t mDelegate;
};
Your implementation for Initialize will change (Make sure you handle the dynamic_cast result correctly):
template <class Handler>
void Acceptor<Handler>::Initialize(IHandler *pVal){
Handler *pHandle = dynamic_cast<Handler>(pVal); //You will have to ofcourse ttake appropriate action if this cast fails.
if (!handle) {
std::cout << __FUNCTION__ << " : created" << std::endl;
handle = new Handler();
} else {
std::cout << __FUNCTION__ << " : Error exception" << std::endl;
}
if (mDelegate && mDelegate(*handle)) {
std::cout << "Ok Called Handle in " << __FUNCTION__ << std::endl;
} else {
std::cout << "Not Called Handle in " << __FUNCTION__ << std::endl;
}
handle->displayComputer();
}
Finally all classes that have to be used with the Acceptor will have to be derived from IHandler.
Now you can change your pointer declaration to shared_ptr< IAcceptor >.
EDIT:
Based on your comment for the second issue, I would pass the Handler object as a pointer instead of a reference and modify the UserClass::HandleDelegate method to accept a pointer to the BaseClass (or the IHandler class if you want to be even more generic.).
You can try to use boost::static_pointer_cast
, because even though
class Derived : public Base{};
it doesn't make boost::shared<Derived>
inherit from boost::shared_ptr<Base>
.
So you have to use explicit boost cast like boost::static_pointer_cast, boost::dynamic_pointer_cast
accordingly.
精彩评论