Design and implementation issues (templates, inheritance)

S

scorp007

Hi,

My problem is as follows. I am trying to have each of my classes in the
hierarchy have different behaviours which are set at compile time. I
also want to be able to treat the classes in the hierarchy uniformly
using polymorphism and this is where I run into problems. I'm not sure
how to go about declaring a pointer to the base type so that this
becomes possible.

I dynamically create objects that fall into the types A,B or C. The
types might be instantiated something like:
A<TypeOne> a;
B<TypeTwo> b;
C<TypeOne> c;

What then should the Base pointer type look like? Perhaps my design
isn't all that good but I couldn't really see another nice way to do
what I want. Inheritance wouldn't be very manageable although fairly
easy to implement.

The class hierarchy looks something like this:

template<class Type>
class Base
{
void function()
{
Type aType;
aType.doSomething()
}
};

template<class Type>
class A: public Base<Type>
{
};

template<class Type>
class B: public Base<Type>
{
};

template<class Type>
class C: public Base<Type>
{
};

class TypeOne
{
public:
void doSomething()
{
cout << "this is type one\n";
}
};

class TypeTwo
{
void doSomething()
{
cout << "this is type two\n";
}
};


Thanks for any help.
 
O

Ondra Holub

scorp007 napsal:
Hi,

My problem is as follows. I am trying to have each of my classes in the
hierarchy have different behaviours which are set at compile time. I
also want to be able to treat the classes in the hierarchy uniformly
using polymorphism and this is where I run into problems. I'm not sure
how to go about declaring a pointer to the base type so that this
becomes possible.

I dynamically create objects that fall into the types A,B or C. The
types might be instantiated something like:
A<TypeOne> a;
B<TypeTwo> b;
C<TypeOne> c;

What then should the Base pointer type look like? Perhaps my design
isn't all that good but I couldn't really see another nice way to do
what I want. Inheritance wouldn't be very manageable although fairly
easy to implement.

The class hierarchy looks something like this:

template<class Type>
class Base
{
void function()
{
Type aType;
aType.doSomething()
}
};

template<class Type>
class A: public Base<Type>
{
};

template<class Type>
class B: public Base<Type>
{
};

template<class Type>
class C: public Base<Type>
{
};

class TypeOne
{
public:
void doSomething()
{
cout << "this is type one\n";
}
};

class TypeTwo
{
void doSomething()
{
cout << "this is type two\n";
}
};


Thanks for any help.

Your classes do NOT have any common base type, because Base<int> is
different from Base<double>. You need inherit class Base<something>
from some common base type:

class ReallyBaseClass
{
// Some code will be here
};

template<class Type>
class Base: public ReallyBaseClass
{
// etc.
 
S

scorp007

Your classes do NOT have any common base type, because Base<int> is
different from Base<double>. You need inherit class Base<something>
from some common base type:

class ReallyBaseClass
{
// Some code will be here

};
template<class Type>
class Base: public ReallyBaseClass
{
// etc.

I see. How then do I implement
void function()
{
Type aType;
aType.doSomething()
}

In such a base class? If I remove the template argument from my current
base class, I would then have to implement the function() in each of
the subclasses?
 
O

Ondra Holub

scorp007 napsal:
I see. How then do I implement
void function()
{
Type aType;
aType.doSomething()
}

In such a base class? If I remove the template argument from my current
base class, I would then have to implement the function() in each of
the subclasses?

class ReallyBaseClass
{
public:
// Constructors and other stuff not here for simplicity

virtual function() = 0;
};

And in templated class Base you can let it as it was:
template<class Type>
class Base: public ReallyBaseClass
{
virtual void function()
{
Type aType;
aType.doSomething()
}

};
 
S

scorp007

class ReallyBaseClass
{
public:
// Constructors and other stuff not here for simplicity

virtual function() = 0;

};And in templated class Base you can let it as it was:
template<class Type>
class Base: public ReallyBaseClass
{
virtual void function()
{
Type aType;
aType.doSomething()
}

};

I now have an issue with calling functions that are defined only in
BaseClass and not ReallyBaseClass. If I have pointers to
ReallyBaseClass I still have to perform a cast to BaseClass to perform
those function calls. How then do I cast to have the correct Type
associated with it?
 
O

Ondra Holub

I now have an issue with calling functions that are defined only in
BaseClass and not ReallyBaseClass. If I have pointers to
ReallyBaseClass I still have to perform a cast to BaseClass to perform
those function calls. How then do I cast to have the correct Type
associated with it?

If you want to call method (let's say named method()), you can either
(a) define such function as virtual in ReallyBaseClass
(b) if you know, that certain pointer to ReallyBaseClass points to
instance of Base<SomeType>, you can typecast it this way:

ReallyBaseClass* p = // get it from somewhere
Base<SomeType>* pp = dynamic_cast<Base<SomeType>*>(p);
if (pp == 0)
{
// p was not pointer to instance of Base<SomeType> (or class
derived from it)
}
else
{
// call method of Base<SomeType>
pp->method();
}

I prefer variant (a), because I do not need to perform ugly typecasts.
In ReallyBaseClass I usualy define method() to always fail to be able
to detect, that it was called for class, where it is nonsense:

class ReallyBaseClass
{
public:
// ... constructors and more methods

// If you are using exceptions
virtual void method()
{
throw SomeGeneralException;
}

// If you do not use exceptions
virtual void method()
{
std::cerr << "ReallyBaseClass::method() called\n";
exit(1);
}
};
 
S

scorp007

(a) define such function as virtual in ReallyBaseClass
(b) if you know, that certain pointer to ReallyBaseClass points to
instance of Base<SomeType>, you can typecast it this way:

ReallyBaseClass* p = // get it from somewhere
Base<SomeType>* pp = dynamic_cast<Base<SomeType>*>(p);
if (pp == 0)
{
// p was not pointer to instance of Base<SomeType> (or class
derived from it)}else
{
// call method of Base<SomeType>
pp->method();

}I prefer variant (a), because I do not need to perform ugly typecasts.
In ReallyBaseClass I usualy define method() to always fail to be able
to detect, that it was called for class, where it is nonsense:

I think my problem is how do I actually know what the correct SomeType
is?
All I have are pointers to already created objects which I then want to
cast to the BaseClass so I can call some methods.
 
O

Ondra Holub

scorp007 napsal:
I think my problem is how do I actually know what the correct SomeType
is?
All I have are pointers to already created objects which I then want to
cast to the BaseClass so I can call some methods.

If you cannot know the real type of instance, you should write the
method as virtual method acording to (a) mentioned above. It is most
simple and costs almost no work (less than typecasting).

Otherwise you would have to provide your own RTTI
(run-time-type-identification) mechanism. Something like:

class ReallyBaseClass
{
public:
ReallyBaseClass(int type): type_(type) { }

int GetType() const { return type_; }

static int GenerateID()
{
static int id = 0;
return ++id;
}

private:
int type_;
};

class Derived: public ReallyBaseClass
{
public:
Derived(): ReallyBaseClass(ReallyBaseClass::ID) { }

static const int ID; // This must be different for every type
};

const int Derived::ID = ReallyBaseClass::GenerateID();

// Then if you have instance of ReallyBaseClass, you can do:

ReallyBaseClass* p = // Somehow get it
switch (p->GetType())
{
case Derived::ID:
// Do something

default:
};

But this is really very ugly and I do not recommend it. Variant (a)
with virtual method is better and simpler.
 
S

scorp007

If you cannot know the real type of instance, you should write the
method as virtual method acording to (a) mentioned above. It is most
simple and costs almost no work (less than typecasting).

Otherwise you would have to provide your own RTTI
(run-time-type-identification) mechanism. Something like:

class ReallyBaseClass
{
public:
ReallyBaseClass(int type): type_(type) { }

int GetType() const { return type_; }

static int GenerateID()
{
static int id = 0;
return ++id;
}

private:
int type_;

};class Derived: public ReallyBaseClass
{
public:
Derived(): ReallyBaseClass(ReallyBaseClass::ID) { }

static const int ID; // This must be different for every type

};const int Derived::ID = ReallyBaseClass::GenerateID();

// Then if you have instance of ReallyBaseClass, you can do:

ReallyBaseClass* p = // Somehow get it
switch (p->GetType())
{
case Derived::ID:
// Do something

default:

};But this is really very ugly and I do not recommend it. Variant (a)
with virtual method is better and simpler.

Is there really no other way? Both those methods don't look very
appealing to me. On one hand I would have to include functionality in
classes that has no real reason to be there, on the other I would have
to do a lot of work which shouldn't need to be done with
object-oriented code. I'm quite willing to rethink my design if it is
seriously bad but I need some pointers as to the correct way to lay
things out.

Thanks for the help so far.
 
B

Bo Yang

scorp007 :
Is there really no other way? Both those methods don't look very
appealing to me. On one hand I would have to include functionality in
classes that has no real reason to be there, on the other I would have
to do a lot of work which shouldn't need to be done with
object-oriented code. I'm quite willing to rethink my design if it is
seriously bad but I need some pointers as to the correct way to lay
things out.

Thanks for the help so far.
But, doesn't Ondra's method work?
 

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

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,013
Latest member
KatriceSwa

Latest Threads

Top