I am wrapping my head around on a relatively simple problem, but seemingly cannot find a way to resolve this issue - even a bit hackish ..., I just got stuck and could use some idea that seem not to pop up. The code sample shown below is NOT (fully) working and is rather there to show my problem.
I have a controller class with 3 methods init, process and dispose. The Controller basically is a wrapper for one or more so called components to be executed.
Furthermore there is a component_manager class to create component instances and manage their life cycle. The component manager is derived from an interface defining 4 virtual methods, such as init, prepare, recycle and dispose.
A component itself also has a well defined interface icomponent. All components derive from that interface and all components are default constructable (no arguments needed).
To show some code I've extracted the important parts from the code I am experimenting with, leaving out the `controller' wrapper class and all the noise around it.
namespace detail {
// Iterator for std::tuple
template<typename Tuple, typename F, std::size_t ...Indices>
constexpr void for_each_impl(Tuple&& tuple, F&& f, std::index_sequence<Indices...>)
{
using swallow = int[];
(void)swallow{1,
(f(std::get<Indices>(std::forward<Tuple>(tuple))), void(), int{})...
};
}
} // Namespace detail
template<typename Tuple, typename F>
constexpr void for_each(Tuple&& tuple, F&& f)
{
constexpr std::size_t N = std::tuple_size<std::remove_reference_t<Tuple>>::value;
detail::for_each_impl(std::forward<Tuple>(tuple), std::forward<F>(f),
std::make_index_sequence<N>{});
}
// A component
class icomponent {
public:
virtual ~icomponent() = default;
virtual std::string id() = 0;
};
// Sample components a, b and c
class component_a : public icomponent {
public:
virtual std::string id() override { return "component a"; }
};
class component_b : public icomponent {
public:
virtual std::string id() override { return "component b"; }
};
class component_c : public icomponent {
public:
virtual std::string id() override { return "component c"; }
};
// Interface defining a component manager
class iprocessing_component_manager
{
public:
virtual ~iprocessing_component_manager() = default;
virtual T* prepare() = 0;
// More members like init(), recycle() and dispose()
};
// An simple, possible implementation of component manager
class simple_processing_component_manager
: public iprocessing_component_manager
{
public:
virtual ~simple_processing_component_manager() = default;
// ... other implemented methods ...
virtual T* prepare()
{
// ... creation of component
}
};
// In the controller wrapper, the process method
template<typename ...T>
void process()
{
// This initialization is normally within the controller wrapper
std::unique_ptr<simple_processing_component_manager> component_mgr = std::make_unique<simple_processing_component_manager>();
// Convert to tuple
std::tuple<T...> components;
// Iterate over (component) types ...
for_each(components, [&](auto& comp) {
using component_t = typename std::remove_reference<decltype(comp)>::type;
// Steps would be ...
// Instantiate the component (doesn't work, just showing what I need to accomplish)
component_t* x = component_mgr->prepare<component_t>();
// ^^^^^^^^^^^^^
// Perform component execution ... here I just print the id of the component
std::cout << x->id() << "\n";
// After processing component_mgr recycle() and / or dispose() are called
delete x;
});
}
Putting it all together, a typical controller usage would look like this:
controller c(/* some initialization parameters */);
result = c->process<component_a, component_c>(/* some more parameters */);
That would be my preferred way of usage, passing the components as variadic template arguments. Within the controller process method, these component types are iterated and executed. I would really like to pass the components with the process method and not with the controller instantiation.
My basic problem relies in component managers prepare method. A simple implementation of a component_manager and its prepare method would look like this:
T* prepare(/*...*/)
{
return new T(); // Create component instance
}
However, I will have different implementations of component managers, for example, I would like to recycle and reuse the created component pointers by using an object pool. In such case I may have something like
return borrow_object<T>(/* some key */); // Get from pool, will be returned back to pool with `recycle`...
A the end I will have various distinct component_managers and each implementation of prepare and the other recycle/disposal methods will differ, however, for all cases I need the component type T. But ... a template method cannot be virtual. I am aware of this!
Is there any way to fix the above problem or perhaps a way better approach or design for this problem? I been sitting and staring at this too long, so please bear with me if I missed something really simple.
Thanks for your time!
I'm not an expert of inheritance and virtual methods... and I'm not sure to understand your exact requirements... anyway, as you said, a template method cannot be virtual.
So you have to take in count this.
But a template method can call a virtual method in a template class.
So I propose to make virtual prepare() in iprocessing_component_manager to return a icomponent pointer.
struct iprocessing_component_manager
{
virtual ~iprocessing_component_manager () = default;
virtual icomponent * prepare () = 0;
};
Next, to prepare a single type template derived class that implement prepare()
template <typename T>
struct type_processing_component_manager
: public iprocessing_component_manager
{
virtual ~type_processing_component_manager () = default;
virtual T* prepare () override
{ return new T; }
};
Observe that prepare() return a pointer to T, not a pointer to icomponent.
Now you can write a variadic template tuple_processing_component_manager, that inherit from all type_processing_component_manager<Ts>, with a template (not virtual, obviously) prepare() that call the right (type dependent) virtual inherited prepare() method.
template <typename ... Ts>
struct tuple_processing_component_manager
: public type_processing_component_manager<Ts>...
{
virtual ~tuple_processing_component_manager () = default;
template <typename T>
T * prepare ()
{ return type_processing_component_manager<T>::prepare(); }
};
In the main process() you can have a component_mgr based over the variadic processing component manager
auto component_mgr
{ std::make_unique<tuple_processing_component_manager<Ts...>>() };
and use it, in the lambda, as follows
using component_t
= typename std::remove_reference<decltype(comp)>::type;
std::unique_ptr<component_t> x
{ component_mgr->template prepare<component_t>() };
std::cout << x->id() << "\n";
The following is a full working example.
#include <tuple>
#include <memory>
#include <utility>
#include <iostream>
namespace detail
{
template <typename Tuple, typename F, std::size_t ... Is>
constexpr void for_each_impl (Tuple && tuple, F && f,
std::index_sequence<Is...>)
{
using swallow = int[];
(void)swallow { 1,
((void)f(std::get<Is>(std::forward<Tuple>(tuple))), 0)... };
}
}
template <typename Tuple, typename F>
constexpr void for_each (Tuple && tuple, F && f)
{
constexpr auto N
{ std::tuple_size<std::remove_reference_t<Tuple>>::value };
detail::for_each_impl(std::forward<Tuple>(tuple), std::forward<F>(f),
std::make_index_sequence<N>{});
}
struct icomponent
{
virtual ~icomponent () = default;
virtual std::string id () = 0;
};
struct component_a : public icomponent
{ virtual std::string id () override { return "component a"; } };
struct component_b : public icomponent
{ virtual std::string id () override { return "component b"; } };
struct component_c : public icomponent
{ virtual std::string id () override { return "component c"; } };
struct iprocessing_component_manager
{
virtual ~iprocessing_component_manager () = default;
virtual icomponent * prepare () = 0;
};
template <typename T>
struct type_processing_component_manager
: public iprocessing_component_manager
{
virtual ~type_processing_component_manager () = default;
virtual T* prepare () override
{ return new T; }
};
template <typename ... Ts>
struct tuple_processing_component_manager
: public type_processing_component_manager<Ts>...
{
virtual ~tuple_processing_component_manager () = default;
template <typename T>
T * prepare ()
{ return type_processing_component_manager<T>::prepare(); }
};
template <typename ... Ts>
void process ()
{
auto component_mgr
{ std::make_unique<tuple_processing_component_manager<Ts...>>() };
std::tuple<Ts...> components;
for_each(components, [&](auto& comp)
{
using component_t
= typename std::remove_reference<decltype(comp)>::type;
std::unique_ptr<component_t> x
{ component_mgr->template prepare<component_t>() };
std::cout << x->id() << "\n";
});
}
int main ()
{
process<component_a, component_b, component_c>();
}
If you're not interested in the std::tuple of components, and in for_each() processing, you can use directly the type_processing_component_manager struct in a recursive process()
// ground case
template <int = 0>
void process ()
{ }
// recursive case
template <typename T0, typename ... Ts>
void process ()
{
type_processing_component_manager<T0> component_mgr;
std::unique_ptr<T0> x { component_mgr.prepare() };
std::cout << x->id() << "\n";
// recursion
process<Ts...>();
}
Related
Currently, I store pointers of different types in a vector. To archive this, I implemented a class template "Store" which derives from a non-class template "IStore". My vector finally stores pointers to "IStore".
In code:
class IStore
{
public:
IStore() = default;
virtual ~IStore() = default;
virtual void call() = 0;
// ... other virtual methods
};
template<typename T>
class Store : public IStore
{
public:
Store() = default;
virtual ~Store() = default;
virtual void call() override;
// ... other virtual methods
private:
T* m_object = nullptr;
}
And in my main class which holds the vector:
class Main
{
public:
template<typename T>
void registerObject(T* ptr);
template<typename T>
void callObjects();
// ... other methods
private:
std::vector<IStore*> m_storedObjects;
};
So far the current class structure. To describe the problem I need to introduce the following three example structs:
struct A {}
struct B : public A {}
struct C : {}
Other classes should call the Main::registerObject method with pointers to objects of A, B or C types. This method will then create a new Store<A>, Store<B> resp. Store<C> template class object and inserts this objects pointer to m_storedObjects.
Now the tricky part starts: The method Main::callObjects should be called by other classes with a template argument, such as Main::callObjects<B>(). This should iterate though m_storedObjects and call the IStore::call method for each object, which is of type B or which type B is derived from.
For example:
Main::registerObject<A>(obj1);
Main::registerObject<B>(obj2);
Main::registerObject<C>(obj3);
Main::callObjects<B>();
Should call obj1 and obj2 but not obj3, because C isn't B and B isn't derived from C.
My approaches in Main::callObjects were:
1. Perform dynamic_cast and check against nullptr like:
for(auto store : m_storedObjects)
{
Store<T>* base = dynamic_cast<Store<T>*>(store);
if(base)
{
// ...
}
}
which will only work for the same classes, not derived classes, because Store<B> isn't derived from Store<A>.
2. To overwrite the cast operator in IStore and Store, such that I can specify Store should be castable when the template argument is castable. For example in Store:
template<typename C>
operator Store<C>*()
{
if(std::is_convertible<T, C>::value)
{
return this;
}
else
{
return nullptr;
}
}
But this method is never called.
Does anyone have a solution to this problem?
Sorry for the long post, but I thought more code would be better to understand the problem.
Thanks for your help anyway :)
After some thought, I realized that your type erasure, from assigning Store<T> objects to IStore* pointers, makes it impossible to use any compile-time type checking like std::is_base_of and the like. The next best option you have is run-time type information (dynamic_cast<>(), typeid()). As you observed, dynamic_cast<>() can't determine if an object's type is an ancestor of another type, only if an object's type is a descendant of another type known at compile time.
EDIT: With C++17 support, I can think of another way to solve your problem, based on the std::visit example here. If you change your Main interface...
#include <iostream>
#include <vector>
#include <variant>
template <typename T>
class Store {
public:
using value_type = T;
Store(T* object): m_object(object) {}
void call() { std::cout << "Hello from " << typeid(T).name() << '\n'; }
// ... other methods
private:
T* m_object = nullptr;
};
template <typename... Ts>
class Main {
private:
std::vector<std::variant<Store<Ts>...>> m_storedObjects;
public:
// replacement for registerObjects, if you can take all objects in at once
Main(Ts*... args): m_storedObjects({std::variant<Store<Ts>...>(Store<Ts>{args})...}) {}
template <typename U>
void callObjects() {
for (auto& variant : m_storedObjects) {
std::visit([](auto&& arg) {
using T = typename std::decay_t<decltype(arg)>::value_type;
if constexpr (std::is_base_of<T, U>::value) {
arg.call();
}
}, variant);
}
}
};
struct A {};
struct B : public A {};
struct C {};
int main() {
A a;
B b;
C c;
auto m = Main{&a, &b, &c};
m.callObjects<B>();
// > Hello from 1A
// > Hello from 1B
return 0;
}
I'm trying to make an interface an abstract class which would be implemented by derivated childs. One of methods in that class has to be variadic (children get one ore more QSharedPointer<QObject> depending on implementation)
The problem is that:
templated methods cannot be virtual
I cannot make a variadic method taking QSharedPointer<QObject>... args arguments because of error: expansion pattern ‘QSharedPointer<QObject>’ contains no argument packs.
Less words, more code:
class BaseClass {
public:
virtual void foo(QSharedPointer<QObject>... args) = 0;
}
class ChildClassA : public BaseClass {
public:
void foo(QSharedPointer<QObject> arg1);
}
class ChildClassB : public BaseClass {
public:
void foo(QSharedPointer<QObject> arg1, QSharedPointer<QObject> arg2);
}
I would like to use above classes to things like that:
template <class T = BaseClass>
class Controller<T>{
void callFoo(QSharedPointer<QObject>... args){
T* = new T();
T->foo(args);
}
}
As you can see the BaseClass is only to say: use one of my children as generic type.
How do I make such things work? Is it even possible in C++?
Since (per the comments) you already have separated the children by arity, and they're managed by separate instantiations of a Controller class template, there is no point in trying to force them all into the same hierarchy -- that only means workarounds and runtime overhead.
Instead, let's make BaseClass a template, and use its parmeter to generate foo's signature.
namespace detail {
template <std::size_t, class T>
using expandHook = T;
}
template <std::size_t N, class = std::make_index_sequence<N>>
struct BaseClass;
template <std::size_t N, std::size_t... Idx>
struct BaseClass<N, std::index_sequence<Idx...>> {
virtual void foo(detail::expandHook<Idx, QSharedPointer<QObject>>... args) = 0;
};
detail::expandHook is kind of a syntactic trick to repeat the QSharedPointer<QObject> type as many times as there are Idxes, which are 0 to N - 1 thanks to the work of std::index_sequence.
Then, children inherit from the corresponding BaseClass:
struct Child : BaseClass<2> {
void foo(QSharedPointer<QObject>, QSharedPointer<QObject>) override;
};
Bonus: a child can inherit from several BaseClass specializations if you want it to support several arities!
Finally, a controller will be parameterized by the adequate BaseClass type, and everything clicks in.
See it live on Coliru (with stubbed types, and main's content would be what's inside a Controller).
If you follow the guideline that public functions should be non-virtual and virtual functions should be private, then you can solve this problem by turning the base function into a variadic template function which delegates to a virtual private function taking a std::vector.
This also has the advantage that all the unpacking happens in one place and derived classes are easier to implement.
Here is an example (I've replaced QSharedPtr with std::shared_ptr and added dummy implementations for QObject so that the example can be compiled without 3rd-party stuff):
#include <memory>
#include <vector>
#include <iostream>
#include <cassert>
struct QObject { virtual void sayHi() const = 0; };
struct DerivedQObject1 : public QObject { void sayHi() const override { std::cout << "1\n"; } };
struct DerivedQObject2 : public QObject { void sayHi() const override { std::cout << "2\n"; } };
class BaseClass {
public:
template <class... Types>
void foo(std::shared_ptr<Types>... args)
{
std::vector<std::shared_ptr<QObject>> vector;
pushBack(vector, args...);
assert(!empty(vector));
doFoo(vector);
}
private:
virtual void doFoo(std::vector<std::shared_ptr<QObject>> const& args) = 0;
template<typename LastType>
static void pushBack(std::vector<std::shared_ptr<QObject>>& vector, LastType arg)
{
vector.push_back(arg);
};
template<typename FirstType, typename ...OtherTypes>
static void pushBack(std::vector<std::shared_ptr<QObject>>& vector, FirstType const& firstArg, OtherTypes... otherArgs)
{
vector.push_back(firstArg);
pushBack(vector, otherArgs...);
};
};
class ChildClassA : public BaseClass {
private:
void doFoo(std::vector<std::shared_ptr<QObject>> const& args) override;
};
void ChildClassA::doFoo(std::vector<std::shared_ptr<QObject>> const& args) {
for (auto const& arg : args) {
arg->sayHi();
}
}
int main() {
ChildClassA child;
auto obj1 = std::make_shared<DerivedQObject1>();
auto obj2 = std::make_shared<DerivedQObject2>();
child.foo(obj1, obj2);
}
I am reading a book on metaprogramming and there is secession on Trampolines:
struct generic_t
{
void* obj;
void(*del)(void*);
};
template <typename T> // outer template parameter
generic_t copy_to_generic(const T& value)
{
struct local_cast // local class
{
static void destroy(void* p) // void*-based interface
{
delete static_cast<T*>(p); // static type knowledge
}
};
generic_t p;
p.obj = new T(value); // information loss: copy T* to void*
p.del = &local_cast::destroy;
return p;
}
I totally understand how it works but I don't know what is the application of it! and where do you usually use this technique? dose anyone know about it? thanks :)
I use it in many places in my programs. One thing I like with this method is that you can hold a list of unrelated types. For example, I've seen a lot of code that looked like that:
struct Abstract { virtual ~Abstract() = default; };
template<typename P>
struct AbstractHandler : Abstract {
virtual void handle(P) = 0;
};
template<typename P, typename H>
struct Handler : AbstractHandler<P>, private H {
void handle(P p) override {
H::handle(p);
}
};
struct Test1 {};
struct Test1Handler {
void handle(Test1) {}
};
struct Test2 {};
struct Test2Handler {
void handle(Test2) {}
};
int main() {
std::vector<std::unique_ptr<Abstract>> handlers;
handlers.emplace_back(std::make_unique<Handler<Test1, Test1Handler>>());
handlers.emplace_back(std::make_unique<Handler<Test2, Test2Handler>>());
// some code later....
dynamic_cast<AbstractHandler<Test1>*>(handlers[0].get())->handle(Test1{});
dynamic_cast<AbstractHandler<Test2>*>(handlers[1].get())->handle(Test2{});
}
Dynamic casts add unnecessary overhead to the program. Instead, you could use type easure just like the one you've made to avoid this overhead.
Plus, there is no reason for Abstract to even exist. It's an interface that expose no useful function. The real need here is to hold a list of unrelated interfaces.
Let's say we ajust type easure to allow copy_to_generic to cast the instance to a parent class.
template <typename Parent, typename T>
generic_t to_generic(T&& value) // forward is better.
{
struct local_cast
{
static void destroy(void* p)
{
// we cast to the parent first, and then to the real type.
delete static_cast<std::decay_t<T>*>(static_cast<Parent*>(p));
}
};
generic_t p;
p.obj = static_cast<Parent*>(new std::decay_t<T>(std::forward<T>(value)));
p.del = &local_cast::destroy;
return p;
}
Look at this code with the type easure:
// No useless interface
template<typename P>
struct AbstractHandler {
// No virtual destructor needed, generic_t already has a virtual destructor via `del`
virtual void handle(P) = 0;
};
template<typename P, typename H>
struct Handler : private H {
void handle(P p) override {
H::handle(p);
}
};
struct Test1 {};
struct Test1Handler {
void handle(Test1) {}
};
struct Test2 {};
struct Test2Handler {
void handle(Test2) {}
};
int main() {
std::vector<generic_t> handlers;
handlers.emplace_back(
to_generic<AbstractHandler<Test1>>(Handler<Test1, Test1Handler>{})
);
handlers.emplace_back(
to_generic<AbstractHandler<Test2>>(Handler<Test2, Test2Handler>{})
);
// some code later....
static_cast<AbstractHandler<Test1>*>(handlers[0].obj)->handle(Test1{});
static_cast<AbstractHandler<Test2>*>(handlers[1].obj)->handle(Test2{});
}
No empty interface and no dynamic casts anymore! This code does the same thing as the other one, but faster.
Consider the below code, EventGeneratorBase is a helper class intended to provide the actual implementation for AddEventHandler() and I would like to use that implementation in the class RemoteControl instead of explicity defining it. I know it's not possible to instantiate RemoteControl without defining the method but is there a shortcut or an easy way to avoid manually defining the methods.
Note: The code in it's present form doesn't compile because RemoteControl can't be instantiated.
#include <iostream>
#include <vector>
#include <memory>
template<class TEventHandler> struct IEventGenerator {
virtual ~IEventGenerator() = default;
virtual void AddEventHandler(std::weak_ptr<TEventHandler> eventHandler) = 0;
};
template <class TEvents> struct EventGeneratorBase : IEventGenerator<TEvents> {
void AddEventHandler(std::weak_ptr<TEvents> target) {
_eventHandlers.push_back(target);
}
std::vector<std::weak_ptr<TEvents>> GetEventHandlers() {
return _eventHandlers;
}
private:
std::vector<std::weak_ptr<TEvents>> _eventHandlers;
};
struct IControlEvents {
virtual ~IControlEvents() = default;
virtual void PowerOn() = 0;
virtual void PowerOff() = 0;
};
struct IRemoteControl : IEventGenerator<IControlEvents> {
virtual ~IRemoteControl() = default;
virtual void Toggle() = 0;
};
struct RemoteControl : IRemoteControl, EventGeneratorBase<IControlEvents> {
// I don't want to define AddEventHandler() in this class and
// would like to inherit the implementation from EventGeneratorBase
void Toggle() {
for (auto tref : GetEventHandlers()) {
auto t = tref.lock();
if (t) {
t->PowerOn();
t->PowerOff();
}
}
}
};
struct Light : IControlEvents {
Light(std::string color) : _color(color) { }
void PowerOn() {
std::cout << _color << "::Light ON!" << std::endl;
}
void PowerOff() {
std::cout << _color << "::Light OFF!" << std::endl;
}
private:
std::string _color;
};
int main() {
std::shared_ptr<IRemoteControl> remote(new RemoteControl); // ERROR: Can't instantiate
std::shared_ptr<IControlEvents> light1(new Light("GREEN"));
std::shared_ptr<IControlEvents> light2(new Light("RED"));
remote->AddEventHandler(light1);
remote->AddEventHandler(light2);
remote->Toggle();
return 0;
}
Your problem is that you have two distinct sub-objects of type IEventGenerator<IControlEvents> within your RemoteControl object. One via EventGeneratorBase<IControlEvents> and one via IRemoteControl.
There are two ways to prevent you from having two distinct subobjects. The first is to inherit virtually from IEventGenerator<TEventHandler> in both spots. This has a modest run-time cost. Simply add virtual before every case of inheritance from IEventGenerator<?> and you are done.
A second method is to note that EventGeneratorBase is intended to help with implementing IEventGenerator.
template<class T> struct tag{using type=T;};
template<class Tag> using type_t=typename Tag::type;
template<class TEventHandler>
tag<TEventHandler> get_event_handler_type(
IEventGenerator<TEventHandler> const*
) { return {}; }
template<class X>
using event_handler_type = type_t< decltype( get_event_handler_type( (X*)nullptr ) ) >;
template <class Base, class TEvents = event_handler_type<Base>>
struct EventGeneratorHelper :
Base
{
void AddEventHandler(std::weak_ptr<TEvents> target) override {
_eventHandlers.push_back(target);
}
std::vector<std::weak_ptr<TEvents>> GetEventHandlers() {
return _eventHandlers;
}
private:
std::vector<std::weak_ptr<TEvents>> _eventHandlers;
};
now, go down to here:
struct RemoteControl :
EventGeneratorHelper<IRemoteControl>
{
and change how we inherit. We now interpose EventGeneratorHelper between us and IRemoteControl, so they now share the same common IEventGenerator.
This removes the need for virtual inheritance, but does up your compile time, and can cause some executable code bloat.
We can go a step further. Add this to EventGeneratorHelper:
template<class Action>
void FireEvents( Action&& action ) const {
for (auto tref : GetEventHandlers()) {
auto t = tref.lock();
if (t) {
action(t);
}
}
}
which reduces RemoteControl to:
struct RemoteControl :
EventGeneratorHelper<IRemoteControl>
{
void Toggle() {
this->FireEvents([](std::shared_ptr<IRemoteControl> const& ptr){
t->PowerOn();
t->PowerOff();
});
}
};
which I think is nice -- requiring clients to know the right way of iterating seems silly.
You have a problem in your inheritance hierarchy.
template <class TEvents> struct EventGeneratorBase :IEventGenerator<TEvents> {
[...]
};
struct IRemoteControl : IEventGenerator<IControlEvents> {
[...]
};
struct RemoteControl : IRemoteControl, EventGeneratorBase<IControlEvents> {
[...]
};
This is not doing what you might expect. Instead, your class RemoteControl inherits twice from IEventGenerator, once from IRemoteControl and once from EventGeneratorBase.
I'm trying to figure out a way to dynamically cast an instance of a child class to its parent in a somewhat difficult set of conditions.
Specifically, I have a an object hierarchy that looks something like (I've simplified a lot, so if something doesn't make sense, it might be due to the simplification):
class Object {
public:
virtual ~Object() {}
};
// shown just to give an idea of how Object is used
class IntObject: public Object {
protected:
int value;
public:
IntObject(int v) { value = v; }
int getValue() { return value; }
};
template <class T>
class ObjectProxy: public Object {
protected:
T *instance;
public:
ObjectProxy(T *instance): instance(instance) {}
T *getInstance() { return instance; }
};
The ObjectProxy class essentially acts as a wrapper to allow other types to be used in the Object hierarchy. Specifically, it allows pointers to class instances to be kept, and used later when invoking the instance's methods. For example, suppose I have:
class Parent {
protected:
int a;
public:
Parent(int v) { a = v; }
virtual ~Parent() {}
void setA(int v) { a = v; }
int getA() { return a; }
};
class Child: public Parent {
protected:
int b;
public:
Child(int v1, int v2): Parent(v1) { b = v2; }
void setA(int v) { b = v; }
int getB() { return b; }
};
I might use them in the following situation:
template <typename C>
void callFn(std::list<Object *> &stack, std::function<void (C*)> fn) {
Object *value = stack.front();
stack.pop_front();
ObjectProxy<C> *proxy = dynamic_cast<ObjectProxy<C> *>(value);
if (proxy == nullptr) {
throw std::runtime_error("dynamic cast failed");
}
fn(proxy->getInstance());
}
void doSomething(Parent *parent) {
std::cout << "got: " << parent->getA() << std::endl;
}
int main() {
std::list<Object *> stack;
// this works
stack.push_back(new ObjectProxy<Child>(new Child(1, 2)));
callFn<Child>(stack, doSomething);
// this will fail (can't dynamically cast ObjectProxy<Child> to ObjectProxy<Parent>)
stack.push_back(new ObjectProxy<Child>(new Child(1, 2)));
callFn<Parent>(stack, doSomething);
}
As noted in the above comments, this code fails for a known reason. In the sample code, it's easy to avoid invoking callFn<Parent>(stack, doSomething). However, in my real code, I am using the signature of the function to determine type, and if its a method for the parent class, that will automatically be used for the template parameter.
My question is if there is any way to achieve the dynamic cast from ObjectProxy from an object of type of ObjectProxy. Part of the complication comes from the fact that in the function callFn, you only have the Parent type and not the child type.
I looked into using type-erasure via boost::any (i.e. ObjectProxy stops being templated, and instead has boost::any instance), but still ran into problems when it came to dynamic-casting (boost::any_cast is static). I did find mention to a dynamic_any on SO, but have not gotten it to work properly yet.
Any help or insight into the problem is greatly appreciated.
The dynamic cast is failing because the classes that are instantiations of ObjectProxy do not share the same hierarchy as the types given in the parameterisation of ObjectProxy. I see two approaches that may help. One, you make the types given to ObjectProxy share a single common base class and move the dynamic cast away from ObjectProxy and onto the instances.
namespace approach2 {
struct object_t {
virtual ~object_t() { }
};
struct required_base_t {
virtual ~required_base_t() { }
};
class object_proxy_base_t : public object_t {
required_base_t* instance_;
public:
object_proxy_base_t(required_base_t* i) : instance_ (i) { }
template <class T>
T* cast_to() const
{
return dynamic_cast<T*>(instance_);
}
};
template <class value_t>
class object_proxy_t : public object_proxy_base_t {
value_t* instance_;
public:
object_proxy_t(value_t* i)
: object_proxy_base_t (i),
instance_ (i)
{
}
};
template <class value_t>
object_t* new_with_proxy(value_t const& value)
{
return new object_proxy_t<value_t>(new value_t(value));
}
struct parent_t : required_base_t {
virtual ~parent_t() { }
};
struct child_t : parent_t {
virtual ~child_t() { }
};
void f()
{
object_t* a = new_with_proxy(parent_t());
object_t* b = new_with_proxy(child_t());
std::cout
<< dynamic_cast<object_proxy_base_t*>(a)->cast_to<parent_t>() << '\n' // works
<< dynamic_cast<object_proxy_base_t*>(b)->cast_to<parent_t>() << '\n' // works
;
}
}
This approach is not possible if you cannot change the base classes of all types used by ObjectProxy. Which leads to the second solution where you make ObjectProxy instantiations have the same hierarchy as the types used to parameterise it.
namespace approach3 {
struct object_t {
virtual ~object_t() { }
};
struct empty_t {
template <class T>
empty_t(T*) { }
};
template <class value_t>
class object_proxy_t : public virtual object_t {
value_t* instance_;
public:
object_proxy_t(value_t* i) : instance_ (i) { }
};
template <class value_t, class base_t>
class object_proxy_sub_t :
public object_proxy_t<value_t>,
public base_t {
public:
object_proxy_sub_t(value_t* i)
: object_proxy_t<value_t>(i),
base_t (i)
{
}
};
template <class base_t, class value_t>
object_t* new_with_proxy(value_t const& value)
{
return new object_proxy_sub_t<value_t, base_t>(new value_t(value));
}
struct parent_t {
virtual ~parent_t() { }
};
struct child_t : parent_t {
virtual ~child_t() { }
};
void f()
{
object_t* a = new_with_proxy<empty_t>(parent_t());
object_t* b = new_with_proxy<object_proxy_t<parent_t> >(child_t());
std::cout
<< dynamic_cast<object_proxy_t<parent_t>*>(a) << '\n' // works
<< dynamic_cast<object_proxy_t<parent_t>*>(b) << '\n' // works
;
}
}
This approach places fewer requirements on the types involved but means more work to keep the hierarchies in sync.
Building off of Bowie Owen's first answer, I realized that while the types given would likely not be derived from the same class (it's a library), I could force that to occur:
struct ObjectProxyBaseType {
virtual ~ObjectProxyBaseType() {}
};
template <class T>
class ObjectProxyType: public ObjectProxyBaseType, public T {
public:
// allow construction via parameters
template <typename... Args>
ObjectProxyType(Args &&... args): T(std::move(args)...) {}
// or construction via copy constructor
ObjectProxyType(T *t): T(*t) {}
virtual ~ObjectProxyType() {}
};
Thus, if I have class Child, I can create an instance of ObjectProxyType<Child>, which causes it to also inherit ObjectProxyBaseType. The rest of the code follows Bowie's suggestion:
class ObjectProxy: public Object {
protected:
ObjectProxyBaseType *instance;
public:
template <typename T>
ObjectProxy(ObjectProxyType<T> *i) {
instance = i;
}
template <typename T>
ObjectProxy(T *value) {
instance = new ObjectProxyType<T>(value);
}
template <typename T>
T *castTo() const {
return dynamic_cast<T *>(instance);
}
};
And an example of code that works:
int main() {
std::list<Object *> stack;
stack.push_back(new ObjectProxy(new Child(1, 2)));
callFn<Child>(stack, doSomething);
stack.push_back(new ObjectProxy(new Child(5, 6)));
callFn<Parent>(stack, doSomething);
}
I've had to do something somewhat similar recently. I've used an approach which worked for me, but might not be appropriate in this case; use your discretion. This hinges on the fact that you (or the person extending this code, if any) have full knowledge of what hierarchies will be used as template parameters.
So let's say these hierarchies are the following:
class Parent1
class Child1: public Parent1
class Child11: public Child1
...
class Parent2
class Child2: public Parent2
...
Then you build a holder class. It is a bit complicated for a simple reason - my compiler doesn't support default template parameters on functions, so I am using helper structs to enable SFINAE.
This class needs to be able to hold objects belonging to all hierarchies (through a base class pointer).
class TypeHolder
{
template<class T, class E=void>
struct GetHelper
{
static T* Get(const TypeHolder* th) { return nullptr; }
//you can actually add code here to deal with non-polymorphic types through this class as well, if desirable
};
template<class T>
struct GetHelper<T, typename std::enable_if<std::is_polymorphic<T>::value, void>::type>
{
static T* Get(const TypeHolder* th)
{
switch(th->type)
{
case P1: return dynamic_cast<T*>(th->data.p1);
case P2: return dynamic_cast<T*>(th->data.p2);
//and so on...
default: return nullptr;
}
}
};
template<class T, class E=void>
struct SetHelper
{
static void Set(T*, TypeHolder* th) { th->type = EMPTY; }
};
template<class T>
struct SetHelper<T, typename std::enable_if<std::is_polymorphic<T>::value, void>::type>
{
static void Set(T* t, TypeHolder* th)
{
th->data.p1 = dynamic_cast<Parent1*>(t);
if(th->data.p1) { th->type = P1; return; }
th->data.p2 = dynamic_cast<Parent2*>(t);
if(th->data.p2) { th->type = P2; return; }
//...and so on
th->type = EMPTY;
}
};
public:
TypeHolder(): type(EMPTY) { }
template<class T>
T* GetInstance() const
{
return GetHelper<T>::Get(this);
}
template<class T>
void SetInstance(T* t)
{
SetHelper<T>::Set(t, this);
}
private:
union
{
Parent1* p1;
Parent2* p2;
//...and so on
} data;
enum
{
EMPTY,
P1,
P2
//...and so on
} type;
};
By the way, the reason we need the SFINAE trick is because of the dynamic_casts, which will not compile on non-polymorphic types.
Now all you need to do is modify your classes just a little bit :)
class ObjectProxyBase
{
public:
virtual const TypeHolder& GetTypeHolder() const = 0;
};
template<class T>
class ObjectProxy: public Object, public ObjectProxyBase
{
T* instance;
static TypeHolder th; //or you can store this somewhere else, or make it a normal (but probably mutable) member
public:
ObjectProxy(T* t): instance(t) { }
T* getInstance() const { return instance; }
const TypeHolder& GetTypeHolder() const { th.SetInstance(instance); return th; }
//... and the rest of the class
};
template<class T>
TypeHolder ObjectProxy<T>::th;
I hope this code is actually correct, since I mostly typed it into the browser window (mine used different names).
And now for the final piece: the function.
template <typename C>
void callFn(std::list<Object *> &stack, std::function<void (C*)> fn) {
Object *value = stack.front();
stack.pop_front();
ObjectProxyBase *proxy = dynamic_cast<ObjectProxyBase *>(value);
if (proxy == nullptr) {
throw std::runtime_error("dynamic cast failed");
}
C* heldobj = proxy->GetTypeHolder().GetInstance<C>(); //I used to have a dynamic_cast here but it was unnecessary
if (heldobj == nullptr) {
throw std::runtime_error("object type mismatch");
}
fn(heldobj);
}
You only need to use this approach for hierarchies, and can still use the dynamic_cast directly to ObjectProxy<C>* in other cases (essentially, you'll want to try both and see if one succeeds).
I hope this is at least a little bit helpful.