I'm building a c++ framework that can be extended by adding new class
I would like to find a way for simplifying the extension of new classes.
my current code look like:
class Base {
public:
virtual void doxxx() {...}
};
class X: public Base {
public:
static bool isMe(int i) { return i == 1; }
};
class Y: public Base {
public:
static bool isMe(int i) { return i == 2; }
};
class Factory {
public:
static std::unique_ptr<Base> getObject(int i) {
if (X::isMe(i)) return std::make_unique<X>();
if (Y::isMe(i)) return std::make_unique<Y>();
throw ....
}
};
Also for every new class a new if-statement must be added.
Now I would like to find a way to rewrite my Factory class (using meta programming) that adding a new class can be done by calling an add method
and the factory class looks like following pseudo code:
class Factory
{
public:
static std::unique_ptr<Base> getObject(int i) {
for X in classNames:
if (X::isMe(i)) return std::make_unique<X>();
throw ....
}
static void add() {...}
static classNames[];...
};
Factory::add(X);
Factory::add(Y);
.
.
is something like that possible?
Many thanks in advance
You might do something like the following:
template <typename ... Ts>
class Factory {
public:
static std::unique_ptr<Base> createObject(int i) {
if (i < sizeof...(Ts)) {
static const std::function<std::unique_ptr<Base>()> fs[] = {
[](){ return std::make_unique<Ts>();}...
};
return fs[i]();
}
throw std::runtime_error("Invalid arg");
}
};
Usage would be:
using MyFactory = Factory<X, Y /*, ...*/>;
auto x = MyFactory::createObject(0);
auto y = MyFactory::createObject(1);
If you want runtime registration, you might do instead:
class Factory {
public:
static std::unique_ptr<Base> createObject(int i) {
auto it = builders.find(i);
if (it == builders.end()) {
throw std::runtime_error("Invalid arg");
}
return it->second();
}
template <typename T>
void Register()
{
builders.emplace(T::Id, [](){ return std::make_unique<T>();});
}
private:
std::map<int, std::function<std::unique_ptr<Base>()>> builders;
};
Usage would be:
class X: public Base {
public:
static constexpr int id = 1;
};
class Y: public Base {
public:
static constexpr int id = 2;
};
and
Factory factory;
factory.register<X>();
factory.register<Y>();
auto x = factory.createObject(1);
auto y = factory.createObject(Y::id);
You can just have a template function:
#include <memory>
class Base {
public:
virtual void doxxx() { /* ... */ };
};
template<int i>
std::unique_ptr<Base> getObject();
#define REGISTER_CLASS(CLS, ID) \
template<> \
std::unique_ptr<Base> getObject<ID>() { return std::unique_ptr<Base>(new CLS()); }
class X1: public Base {
public:
// ...
};
REGISTER_CLASS(X1, 1)
class X2: public Base {
public:
// ...
};
REGISTER_CLASS(X2, 2)
int main()
{
auto obj1 = getObject<1>(); // Makes X1
auto obj2 = getObject<2>(); // Makes X2
return 0;
}
However this only allows for class id values known at compile time.
You could do something like this:
#include <type_traits>
#include <utility>
#include <memory>
#include <functional>
#include <stdexcept>
class I {
public:
virtual ~I(); // Always give a polymorphic class a virtual dtor!
virtual void doxxx();
};
enum class I_Key {
X = 1,
Y
/*...*/
};
struct I_Key_Order {
bool operator()(I_Key k1, I_Key k2) const
{
using U = std::underlying_type_t<I_Key>;
return static_cast<U>(k1) < static_cast<U>(k2);
}
};
class I_Factory {
public:
using Generator = std::function<std::unique_ptr<I>()>;
static std::unique_ptr<I> getObject(I_Key key) const;
static void add(I_Key key, Generator gen);
template <class T>
class AutoRegister {
public:
AutoRegister() {
auto generator = []() -> std::unique_ptr<I>
{ return std::make_unique<T>(); };
add(T::class_key, std::move(generator));
}
AutoRegister(const AutoRegister&) = delete;
AutoRegister& operator=(const AutoRegister&) = delete;
};
private:
using GenMapT = std::map<I_Key, Generator, I_Key_Order>;
static GenMapT& m_generators();
};
inline std::unique_ptr<I> I_Factory::getObject(I_Key key)
{
auto& gen_map = m_generators();
auto iter = gen_map.find(key);
if (iter != gen_map.end())
return iter->second();
throw std::invalid_argument("unknown I_Factory key");
}
inline void I_Factory::add(I_Key key, Generator gen)
{
m_generators().emplace(key, std::move(gen));
}
I_Factory::GenMapT& I_Factory::m_generators()
{
static GenMapT generators;
return generators;
}
class X : public I {
public:
static constexpr I_Key class_key = I_Key::X;
static const I_Factory::AutoRegister<X> _reg;
};
class Y : public I {
public:
static constexpr I_Key class_key = I_Key::Y;
static const I_Factory::AutoRegister<Y> _reg;
};
Note in addition to an I_Factory::add() function, I've set up a second often easier way to have a generator for class C added: define an object of type I_Factory::AutoRegister<C>, as long as C::class_key is a valid enumerator. If the object is a static class member, the adding will happen essentially at the start of the program. This will only work if the class has a public default constructor, so the add function might still be used if something different is necessary.
Here is one version with an add function and a map to store the different types.
class Base {
public:
virtual void doxxx() {...}
};
class X: public Base {
public:
static int isMe = 1;
};
class Y: public Base {
public:
static int isMe = 2;
};
class Factory {
public:
static std::unique_ptr<Base> getObject(int i) {
if (classID.find(i) != classID.end())
return classID[i]();
throw ....
}
template <typename C>
static void add() {
classID[C::isMe] = [](){ return std::make_unique<C>(); };
}
static std::map<int, std::function<std::unique_ptr<Base>()>> classID;
};
// Called like this
Factory::add<X>();
Factory::add<Y>();
A C++14 solution.
Required headers:
#include <memory>
#include <iostream>
A class to throw when the index cannot be found
class ClassNotIndexed{};
A helper class to help create a binding between the index and the requested type:
template< typename ClassType, typename IndexType, IndexType idx >
class ClassIndexer
{};
The primary template for out factory class
template <typename... >
class ClassSelector;
Base case for factory class. When index is found then create the unique_pointer, otherwise throw an exception:
template<typename BaseClass,
typename ClassType, typename IndexType, IndexType idx,
template<typename,typename,IndexType> typename T>
class ClassSelector<BaseClass, T<ClassType,IndexType, idx>>
{
public:
constexpr static
std::unique_ptr<BaseClass> getObject(IndexType i)
{
if (i == idx)
return std::make_unique<ClassType>();
else
throw ClassNotIndexed();
}
};
Variadic template which takes many Indexed classes.
If the index of the first class is a match, then make_unique_pointer otherwise check the next indexed class:
template< typename BaseClass,
typename ClassType, typename IndexType, IndexType idx,
template< typename , typename, IndexType > typename T,
typename... Ts >
class ClassSelector<BaseClass, T<ClassType,IndexType, idx>, Ts... > : ClassSelector< BaseClass, Ts ... >
{
using Base = ClassSelector< BaseClass, Ts ... >;
public:
constexpr static
std::unique_ptr<BaseClass> getObject(IndexType i)
{
if (i == idx)
return std::make_unique<ClassType>();
else
return Base::getObject( i );
}
};
Example:
class Base
{
public:
virtual void doxxx() {}
};
class X : public Base
{
public:
//static bool isMe( int i ) { return i == 1; }
void doxxx() override {std::cout << "I'm X !!\n";}
};
class Y : public Base
{
public:
//static bool isMe( int i ) { return i == 2; }
void doxxx() override {std::cout << "I'm Y !!\n";}
};
int main()
{
using Factory = ClassSelector<
Base,
ClassIndexer< X, int, 1 >,
ClassIndexer< Y, int, 2 >
>;
Factory::getObject( 1 )->doxxx();
Factory::getObject( 2 )->doxxx();
int choose;
std::cin >> choose;
Factory::getObject( choose )->doxxx();
return 0;
}
Related
How can one implement a method which calls another method recursively for all base classes? Conceptually:
class C {
public:
magical void recursiveWork() {
thisClass::doWork();
if (baseClassExists) {
baseClass::recursiveWork();
}
}
void doWork() {
printf("bar");
}
}
class D : public C {
public:
void doWork() {
printf("foo");
}
}
D d;
d.recursiveWork(); // prints "foobar"
You could use something to indicate the base class, like base_type below, and then recursively check if DoIt exists. (in this case I checked if base_type is different from self, in case of the most base class). Of course, it won't work with multiple inheritance (it should have something to identify all base_type's), and be careful with object slicing when going up on base classes.
#include <iostream>
#include <utility>
class A1
{
public:
using base_type = A1;
virtual ~A1(){}
virtual void DoIt(){ std::cout << "A1\n"; }
};
class A : public A1
{
public:
using base_type = A1;
virtual void DoIt(){
std::cout << "A\n";
}
};
class B : public A
{
public:
using base_type = A;
virtual void DoIt(){
std::cout << "B\n";
}
};
template<class...> using void_t = void;
template<class, class = void>
struct has_do_it : std::false_type { constexpr static bool value = false; };
template<class T>
struct has_do_it<T, void_t<decltype(std::declval<T&>().DoIt())>> : std::true_type { constexpr static bool value = true; };
template<typename T>
void DoItRec(T t)
{
if (has_do_it<T>::value) {
t.DoIt();
if(!std::is_same<T, typename T::base_type>::value)
{
typename T::base_type& base = t; // get base part (object slicing)
DoItRec(base);
}
}
}
int main()
{
B b;
DoItRec(b);
return 0;
}
Live example
I've tried to use c++ properties and now I'm stuck with this:
class a {
protected:
// wikipedia https://en.wikipedia.org/wiki/Property_(programming)#C.2B.2B
template<class s, typename t>
class getonly {
protected:
friend s;
t value;
t set(); // operator =...
public:
t get(); // t operator...
};
};
class b : public a {
public:
getonly<b, int> id;
};
What I want is something like this, where getonly is parametrized by only the typename (int), and not the class (b):
class b : public a {
public:
getonly<int> id;
};
Is this possible? Can anyone help me?
It appears that you want a data member that can be read by any code, but that can only be changed by the containing class.
The attempt at providing friend-ship via template would not have compiled prior to C++11. It means that this code can not be easily modified to work with a C++03 compiler (with C++03 one could express the access restriction on modification via e.g. an owner credential argument to a setter). However, that concern becomes less significant every day.
Here's your code modified to compile, and with the inheritance changed from public to private (it doesn't make much sense to say that b "is-an" a):
class a {
protected:
// wikipedia https://en.wikipedia.org/wiki/Property_(programming)#C.2B.2B
template<class s, typename t>
class getonly {
protected:
friend s;
t value_;
public:
auto get() const -> t const& { return value_; }
operator t const& () const { return value_; }
getonly( t v ): value_( v ) {}
};
};
class b : private a {
public:
getonly<b, int> id;
void set_id( int x ) { id.value_ = 2*x; }
b(): id( 42 ) {}
};
#include <iostream>
auto main() -> int
{
using namespace std;
b obj;
cout << obj.id << endl;
obj.set_id( 333 );
cout << obj.id << endl;
}
So first off, this can be done with CRTP
template<typename s>
class a {
protected:
// wikipedia https://en.wikipedia.org/wiki/Property_(programming)#C.2B.2B
template<typename t>
class getonly {
protected:
friend s;
t value;
t set(); // operator =...
public:
t get(); // t operator...
};
};
class b : public a<b> {
public:
getonly<int> id;
};
But since you mentioned inheritance, are you aware that friend declarations are not inherited? If that is your goal, then there is a more elegant solution - do not use the property as a base class but as traits:
template<typename T, typename F>
class property_impl
{
friend F;
private:
T value_;
protected:
void set(T value)
{
value_ = value;
}
public:
T get()
{
return value_;
}
};
template<typename F>
class property_traits
{
template<typename T> using property = property_impl < T, F > ;
};
class foo : public property_traits < foo >
{
public:
property<int> id_;
};
class bar : public foo, public property_traits < bar >
{
public:
property<int> another_id_;
void doStuff(foo f)
{
auto value = f.id_.get();
// f.id_.set(5); <-- fails since friend is not inherited
}
};
int main(int argc, char** argv)
{
foo f;
auto value = f.id_.get();
bar b;
auto another_value = b.another_id_.get();
b.doStuff(f);
return 0;
}
This will give you the ability to inherit and specialize on each class that needs a property while retianing the fact, that only the class type used in specialization is able to modify the value.
Again, maybe you want that, I am not sure.
Consider
struct AbstractClass {};
struct Derived1 : AbstractClass {
using type = int;
};
struct Derived2 : AbstractClass {
using type = char;
};
struct Derived3 : AbstractClass {
using type = bool;
};
int main() {
AbstractClass* a[] = {new Derived1, new Derived2, new Derived3};
}
How to get type from a[0], a[1], a[2]?
Here is what I'm trying to accomplish. I guess there is no other way to do this than this?
#include <iostream>
struct Object { virtual ~Object() = default; };
struct A : Object {};
struct B : Object {};
struct C : Object {};
struct AbstractClass {
virtual void take (Object*) {
std::cout << "Accepted.\n";
}
};
template <typename...> struct ObjectTypes;
template <typename First, typename... Rest>
struct ObjectTypes<First, Rest...> : ObjectTypes<Rest...> {
bool operator()(Object* o) const {
if (dynamic_cast<First*>(o))
return true;
return ObjectTypes<Rest...>::operator()(o);
}
};
template <>
struct ObjectTypes<> {
bool operator()(Object*) const {return false;}
};
struct Derived1 : AbstractClass {
using type = ObjectTypes<A,B>;
virtual void take (Object* o) override {
if (type()(o)) // This is why I want to use type from AbstractClass.
return AbstractClass::take(o);
std::cout << "Rejected.\n";
}
};
struct Derived2 : AbstractClass {
using type = ObjectTypes<A,C>;
virtual void take (Object* o) override {
if (type()(o))
return AbstractClass::take(o);
std::cout << "Rejected.\n";
}
};
struct Derived3 : AbstractClass {
using type = ObjectTypes<B,C>;
virtual void take (Object* o) override {
if (type()(o))
return AbstractClass::take(o);
std::cout << "Rejected.\n";
}
};
int main() {
AbstractClass* abs[] = {new Derived1, new Derived2, new Derived3};
A* a = new A;
B* b = new B;
C* c = new C;
for (AbstractClass* x : abs) {
x->take(a);
x->take(b);
x->take(c);
std::cout << "------------\n";
}
}
Notice the repetitions in Derived1, Derived2, Derived3, etc...? Hence my original goal was to have it all done in the base class AbstractClass.
I rather thought about sth like:
#include <iostream>
struct Object { virtual ~Object() = default; };
struct A : Object {};
struct B : Object {};
struct C : Object {};
struct SuperObjectType {
public:
virtual bool operator()(Object *o) const = 0;
};
template <typename First, typename... Rest>
struct ObjectTypes: SuperObjectType {
bool operator()(Object* o) const override {
if (dynamic_cast<First*>(o))
return true;
return ObjectTypes<Rest...>()(o);
}
};
template <typename OnlyOne>
struct ObjectTypes<OnlyOne>: SuperObjectType {
bool operator()(Object* o) const override {
return dynamic_cast<OnlyOne*>(o);
}
};
struct AbstractClass {
SuperObjectType *sot;
void take (Object* o) {
if ((*sot)(o)) {
std::cout << "ACCEPTED" << std::endl;
} else {
std::cout << "REJECTED" << std::endl;
}
}
};
struct Derived1 : AbstractClass {
Derived1() {
sot = new ObjectTypes<A,B>;
}
};
struct Derived2 : AbstractClass {
Derived2() {
sot = new ObjectTypes<A,C>;
}
};
struct Derived3 : AbstractClass {
Derived3() {
sot = new ObjectTypes<B,C>;
}
};
int main() {
AbstractClass* abs[] = {new Derived1, new Derived2, new Derived3};
A* a = new A;
B* b = new B;
C* c = new C;
for (AbstractClass* x : abs) {
x->take(a);
x->take(b);
x->take(c);
std::cout << "------------\n";
}
}
But I'm happy I've inspired you ;)
Minor alternative to (perhaps) speed up the performance:
template <typename First, typename... Rest>
struct ObjectTypes: ObjectTypes<Rest...> { // Derive from ObjectTypes<Rest...> instead.
virtual bool operator()(Object* o) const override {
if (dynamic_cast<First*>(o))
return true;
return ObjectTypes<Rest...>::operator()(o);
// So now ObjectTypes<Rest...>::operator() can be used, and thus avoid instantiation.
}
};
template <typename T>
struct ObjectTypes<T>: SuperObjectType {
virtual bool operator()(Object* o) const override {
return dynamic_cast<T*>(o);
}
};
Because ObjectTypes<T> is derived from SuperObjectType for any type T, then ObjectTypes<First, Rest...> is derived from SuperObjectType as well (by being derived from ObjectTypes<Last>, were Last is the last type in Rest...). So everything still works (tested).
Solution inspired by Wojciech Frohmberg's suggestion, though I'm not sure this is what he meant. I actually don't know what he meant and I think he meant something else, and would like to see his solution and find out what he was actually saying.
#include <iostream>
struct Object { virtual ~Object() = default; };
struct A : Object {};
struct B : Object {};
struct C : Object {};
template <typename...> struct ObjectTypes;
template <typename First, typename... Rest>
struct ObjectTypes<First, Rest...> : ObjectTypes<Rest...> {
bool operator()(Object* o) const {
if (dynamic_cast<First*>(o))
return true;
return ObjectTypes<Rest...>::operator()(o);
}
};
template <>
struct ObjectTypes<> {
bool operator()(Object*) const {return false;}
};
struct AbstractClass {
virtual void take (Object*) = 0;
template <typename... Ts> void takeHelper (Object* o, ObjectTypes<Ts...>&& types) {
if (types(o)) std::cout << "Accepted.\n"; // And now do whatever with o.
else std::cout << "Rejected.\n";
}
};
template <typename Derived>
struct AbstractClassCRTP : AbstractClass {
virtual void take (Object* o) override {
takeHelper(o, typename Derived::type{});
}
};
struct Derived1 : AbstractClassCRTP<Derived1> {
using type = ObjectTypes<A,B>;
};
struct Derived2 : AbstractClassCRTP<Derived2> {
using type = ObjectTypes<A,C>;
};
struct Derived3 : AbstractClassCRTP<Derived3> {
using type = ObjectTypes<B,C>;
};
int main() {
AbstractClass* abs[] = {new Derived1, new Derived2, new Derived3};
A* a = new A;
B* b = new B;
C* c = new C;
for (AbstractClass* x : abs) {
x->take(a);
x->take(b);
x->take(c);
std::cout << "------------\n";
}
}
Output:
Accepted.
Accepted.
Rejected.
------------
Accepted.
Rejected.
Accepted.
------------
Rejected.
Accepted.
Accepted.
Following does not work:
std::vector<IRule*> vec;
RuleRangeDouble *rule = new RuleRangeDouble(0, 100);
vec.push_back(rule);
Now how can a make a vector of different rules? I know, I have to use pointers... But what else do I have to do to get this working? How can I change my base structure to get this work?
I use an Interface like the following:
// Interface
template <typename T>
class IRule
{
public:
virtual bool isValid(T value) = 0;
};
And my example class looks like this:
class RuleRangeDouble : public IRule<double>
{
private:
double min;
double max;
public:
bool isValid(double value)
{
....
};
};
The vector needs to be a vector of an actual type, for example std::vector<IRule<double>*>. Irule on its own is not a type, it is a class template. So you would need
std::vector<IRule<double>*> vec;
RuleRangeDouble *rule = new RuleRangeDouble(0, 100);
vec.push_back(rule);
If the template parameter is not part of the interface, you can introduce a common base class. Don't forget to give it a virtual destructor:
class IRule
{
public:
virtual bool isValid(T value) = 0;
virtual ~IRule() {}
};
template <typename T>
class Rule : public IRule
{
.....
};
class RuleRangeDouble : public Rule<double>
{
....
};
Then your original use case sample would work:
std::vector<IRule*> vec; // IRule really is a type now
RuleRangeDouble *rule = new RuleRangeDouble(0, 100);
vec.push_back(rule);
You can implement something like getBestValidValue()in several ways. One is to define a special general (but non-template) return type to be used for getBestValidValue() and also as argument type for isValid(value) (juanchopanza's answer was faulty here):
class ValueType
{
enum { is_int, is_double } my_type; // add more if you want
union { my_int; my_double; }; // union only works for simple types
public:
// implicit construction from allowed types
ValueType(int x) : my_type(is_int), my_int(x) {}
ValueType(double x) : my_type(is_double), my_double(x) {}
// use SFINAE for is<type>() function
template<typename T>
typename std::enable_if<std::is_same<T,int>::value,bool>::type
is() const { return my_type==is_int; }
template<typename T>
typename std::enable_if<std::is_same<T,double>::value,bool>::type
is() const { return my_type==is_double; }
// implicit type conversion to allowed types
// note: does not assert(is<T>())
operator int() { return my_int; }
operator double() { return my_double; }
};
class IRule
{
public:
virtual bool isValid(ValueType const&) const = 0; // fixed bug in juanchopanza's answer
virtual ValueType getBestValidValue() const = 0; // return any type of value
virtual ~IRule() {}
};
template<typename T>
class Rule : public IRule
{
protected:
virtual bool valid(T) const = 0;
virtual T bestValidValue() const = 0;
public:
bool isValid(ValueType const&value) const
{
return value.is<T>()
&& valid(value); // implicit type conversion to T
}
ValueType getBestValidValue() const
{
return bestValidValue(); // implicit construction of ValueType
}
....
}
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.