问题
I'd like to convert a base class pointer to a derived class pointer as a function argument without using dynamic_pointer_cast
class Base
{
public:
typedef std::shared_ptr < Base > Ptr;
virtual ~Base ( );
...
};
class Derive : public Base
{
public:
typedef std::shared_ptr < Derive > Ptr;
...
};
void foo( Derive::Ptr ptr )
{
...
}
Base::Ptr ptr1 ( new Derive );
foo( ptr1 );
The above code will give an error while calling foo. This can be avoided by typecasting ptr1 into a Dervie pointer using std::dynamic_pointer_cast.
Base::Ptr ptr1 ( new Derive );
foo ( std::dynamic_pointer_cast < Derive > ( ptr1 ) );
I want to create a map of callback functions that will automatically typecast a base class pointer and call the corresponding function based on the type of derived class.
回答1:
This works for me.
#include <iostream>
#include <set>
#include <memory>
class Base
{
public:
typedef std::shared_ptr < Base > Ptr;
virtual ~Base () {}
};
// Dispatch mechanism to dispatch derived class specific
// functions for foo.
// Base class.
struct FooDispatcher
{
static int registerDispatcher(FooDispatcher* dispatcher)
{
dispatcherSet.insert(dispatcher);;
return 0;
}
static void dispatch(Base::Ptr ptr)
{
std::set<FooDispatcher*>::iterator iter = dispatcherSet.begin();
std::set<FooDispatcher*>::iterator end = dispatcherSet.end();
for ( ; iter != end; ++ iter )
{
if ( (*iter)->canProcess(ptr) )
{
(*iter)->process(ptr);
return;
}
}
}
virtual bool canProcess(Base::Ptr ptr) = 0;
virtual void process(Base::Ptr ptr) = 0;
static std::set<FooDispatcher*> dispatcherSet;
};
std::set<FooDispatcher*> FooDispatcher::dispatcherSet;
void foo( Base::Ptr ptr )
{
FooDispatcher::dispatch(ptr);
}
// A class template for derived classes to use when they
// have an implementation of foo.
template <typename T>
struct FooDerivedDispatcher : public FooDispatcher
{
virtual bool canProcess(Base::Ptr ptr)
{
return (std::dynamic_pointer_cast<T>(ptr) != nullptr);
}
virtual void process(Base::Ptr ptr)
{
fooImpl(std::dynamic_pointer_cast<T>(ptr));
}
};
// Derived1 and its implementation of foo.
class Derived1 : public Base
{
public:
typedef std::shared_ptr < Derived1 > Ptr;
};
void fooImpl( Derived1::Ptr ptr )
{
std::cout << "Came to fooImpl(Derived1::Ptr).\n";
}
// Register the FooDispatcher for Derived1
int dummy1 = FooDispatcher::registerDispatcher(new FooDerivedDispatcher<Derived1>());
// Derived2 and its implementation of foo.
class Derived2 : public Base
{
public:
typedef std::shared_ptr < Derived2 > Ptr;
};
void fooImpl( Derived2::Ptr ptr )
{
std::cout << "Came to fooImpl(Derived2::Ptr).\n";
}
// Register the FooDispatcher for Derived1
int dummy2 = FooDispatcher::registerDispatcher(new FooDerivedDispatcher<Derived2>());
// Test...
int main()
{
Base::Ptr ptr(new Derived1);
foo(ptr);
ptr = Base::Ptr(new Derived2);
foo(ptr);
}
Output:
Came to fooImpl(Derived1::Ptr). Came to fooImpl(Derived2::Ptr).
Update
A simpler dispatch mechanism... Thanks to @MooingDuck.
#include <iostream>
#include <vector>
#include <memory>
class Base
{
public:
typedef std::shared_ptr < Base > Ptr;
virtual ~Base () {}
};
struct FooDispatcher {
template<class Derived, class Func>
bool registerFunction(Func func) {
auto lambda = [=](std::shared_ptr<Base>& ptr)->void {
std::shared_ptr<Derived> d = std::dynamic_pointer_cast<Derived>(ptr);
if (d)
func(std::move(d));
};
functions.push_back(lambda);
return true;
}
void dispatch(std::shared_ptr<Base>& ptr)
{
for(auto& func : functions)
func(ptr);
}
private:
std::vector<std::function<void(std::shared_ptr<Base>&)>> functions;
};
FooDispatcher dispatcher;
void foo( Base::Ptr ptr )
{
dispatcher.dispatch(ptr);
}
class Derived1 : public Base
{
public:
typedef std::shared_ptr < Derived1 > Ptr;
};
void fooImpl1( Derived1::Ptr ptr )
{
std::cout << "Came to fooImpl(Derived1::Ptr).\n";
}
class Derived2 : public Base
{
public:
typedef std::shared_ptr < Derived2 > Ptr;
};
void fooImpl2( Derived2::Ptr ptr )
{
std::cout << "Came to fooImpl(Derived2::Ptr).\n";
}
int main()
{
dispatcher.registerFunction<Derived1>(fooImpl1);
dispatcher.registerFunction<Derived2>(fooImpl2);
Base::Ptr ptr(new Derived1);
foo(ptr);
ptr = Base::Ptr(new Derived2);
foo(ptr);
}
来源:https://stackoverflow.com/questions/23896308/dynamic-pointer-cast