473,385 Members | 1,317 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,385 software developers and data experts.

Templates and Inheritance

Hello all,
I got some legacy code where a template implements a non-templated
interface. These classes are really HUGE, but it boils down to the
following structure:

class BaseClass
{
public:
virtual ~BaseClass() {}
virtual bool Create(size_t size) = 0;
/* lots of other stuff here */
protected:
void* m_pData;
};

template <class T>
class Wrapper : public BaseClass
{
public:
virtual ~Wrapper() { delete (T*) m_pData; }
virtual bool Create(size_t size)
{
m_pData = new T[size];
return (m_pData != 0);
}
T GetElement(int i) { return *((T*)m_pData+i); }
};

The purpose of this is to have collections of Wrappers with different types:

Wrapper<int>* intwrapper = new Wrapper<int>;
Wrapper<float>* floatwrapper = new Wrapper<float>;
intwrapper->Create(10);
floatwrapper->Create(5);

std::vector<BaseClass*> vec;
vec.push_back(intwrapper);
vec.push_back(floatwrapper);

for ( std::vector<BaseClass*>::iterator it = vec.begin();
it != vec.end(); ++it )
{
DoSomething( (*it) );
}
Now what do to with such a pointer? I can't do anything with it!
For example, I want to access the third element in each array and say
something like:

void DoSomething(BaseClass* any)
{
std::cout < "Element 3: " << any->GetElement(3);
}

But this obviously does not work. GetElement can't be virtual, because
BaseClass does not know which type it should return. Making it a
template does not help either, because this means giving up the nice
inhomogeneous container.
Is there some template magic to solve this problem? I know that it's all
screwed up, but there are thousands of LOC using this class.

Thanks in advance,
Juergen

Jul 22 '05 #1
7 2033
"Jürgen Kaminski" <jk*******@freenet.de> wrote in message
news:cj*************@news.t-online.com...
Hello all,
I got some legacy code where a template implements a non-templated
interface. These classes are really HUGE, but it boils down to the
following structure:

class BaseClass
{
public:
virtual ~BaseClass() {}
virtual bool Create(size_t size) = 0;
/* lots of other stuff here */
protected:
void* m_pData;
};

template <class T>
class Wrapper : public BaseClass
{
public:
virtual ~Wrapper() { delete (T*) m_pData; }
virtual bool Create(size_t size)
{
m_pData = new T[size];
return (m_pData != 0);
}
T GetElement(int i) { return *((T*)m_pData+i); }
};

The purpose of this is to have collections of Wrappers with different types:
Wrapper<int>* intwrapper = new Wrapper<int>;
Wrapper<float>* floatwrapper = new Wrapper<float>;
intwrapper->Create(10);
floatwrapper->Create(5);

std::vector<BaseClass*> vec;
vec.push_back(intwrapper);
vec.push_back(floatwrapper);

for ( std::vector<BaseClass*>::iterator it = vec.begin();
it != vec.end(); ++it )
{
DoSomething( (*it) );
}
Now what do to with such a pointer? I can't do anything with it!
For example, I want to access the third element in each array and say
something like:

void DoSomething(BaseClass* any)
{
std::cout < "Element 3: " << any->GetElement(3);
}

But this obviously does not work. GetElement can't be virtual, because
BaseClass does not know which type it should return. Making it a
template does not help either, because this means giving up the nice
inhomogeneous container.
Is there some template magic to solve this problem? I know that it's all
screwed up, but there are thousands of LOC using this class.

Thanks in advance,
Juergen


You have several alternatives, none of which is very good. You could
dynamic_cast your BaseClass to its actual type, if you know it somehow, and
then call GetElement. If you don't know it, you will have to try all
possible types. But of course, if you want the framework to be extensible
you cannot enumerate all possible types.

In the interest of maintainablity I would certainly avoid the construct
std::vector<BaseClass*>. This indicates you want to make an array of arrays
of unknown type. To me this implies your design centers around data instead
of the problem domain. In other words, sound object oriented designs don't
suffer from this problem.

BTW, what does LOC mean? Learners Of C++? Leering Obnoxious Cretins? Lamers
On Crack? Just wondering.

--
Cy
http://home.rochester.rr.com/cyhome/
Jul 22 '05 #2
Cy Edmunds wrote:
"Jürgen Kaminski" <jk*******@freenet.de> wrote in message
news:cj*************@news.t-online.com...
Hello all,
I got some legacy code where a template implements a non-templated
interface. These classes are really HUGE, but it boils down to the
following structure:

class BaseClass
{
public:
virtual ~BaseClass() {}
virtual bool Create(size_t size) = 0;
/* lots of other stuff here */
protected:
void* m_pData;
};

template <class T>
class Wrapper : public BaseClass
{
public:
virtual ~Wrapper() { delete (T*) m_pData; }
virtual bool Create(size_t size)
{
m_pData = new T[size];
return (m_pData != 0);
}
T GetElement(int i) { return *((T*)m_pData+i); }
};

The purpose of this is to have collections of Wrappers with different
types:
Wrapper<int>* intwrapper = new Wrapper<int>;
Wrapper<float>* floatwrapper = new Wrapper<float>;
intwrapper->Create(10);
floatwrapper->Create(5);

std::vector<BaseClass*> vec;
vec.push_back(intwrapper);
vec.push_back(floatwrapper);

for ( std::vector<BaseClass*>::iterator it = vec.begin();
it != vec.end(); ++it )
{
DoSomething( (*it) );
}
Now what do to with such a pointer? I can't do anything with it!
For example, I want to access the third element in each array and say
something like:

void DoSomething(BaseClass* any)
{
std::cout < "Element 3: " << any->GetElement(3);
}

But this obviously does not work. GetElement can't be virtual, because
BaseClass does not know which type it should return. Making it a
template does not help either, because this means giving up the nice
inhomogeneous container.
Is there some template magic to solve this problem? I know that it's all
screwed up, but there are thousands of LOC using this class.

Thanks in advance,
Juergen

You have several alternatives, none of which is very good. You could
dynamic_cast your BaseClass to its actual type, if you know it somehow, and
then call GetElement. If you don't know it, you will have to try all
possible types. But of course, if you want the framework to be extensible
you cannot enumerate all possible types.


Yes, I thought of this already. The Problem is that I don't know the
actual type, and I want to omit trying all possibilities.
In the interest of maintainablity I would certainly avoid the construct
std::vector<BaseClass*>. This indicates you want to make an array of arrays
of unknown type. To me this implies your design centers around data instead
of the problem domain. In other words, sound object oriented designs don't
suffer from this problem.
It is not really a std::vector, I used this only for explanation. In
real life it's an image processing application where a stack of 2D
images is stored in an array. The images can have any type, including
integer, float, RGB or even vector. How would a "sound object oriented
design" look like? I would really appreciate your help, even if
redesigning the whole stuff is pointless at the moment.
BTW, what does LOC mean? Learners Of C++? Leering Obnoxious Cretins? Lamers
On Crack? Just wondering.


*LOL* It's Lines Of Code - I thought everyone knows this abbreviation
(sorry).

Jul 22 '05 #3
[snip]

It is not really a std::vector, I used this only for explanation. In
real life it's an image processing application where a stack of 2D
images is stored in an array. The images can have any type, including
integer, float, RGB or even vector. How would a "sound object oriented
design" look like? I would really appreciate your help, even if
redesigning the whole stuff is pointless at the moment.


OK, image processing applications I understand. Object orientation may not
be the way to go here. Image processing is a large collection of algorithms
operating on data stored in a lot of different ways. If you look at the
standard library, which generally speaking isn't object oriented at all,
they attack this problem by keeping the algorithm separate from the the data
using iterators. Consider:

// untested code
template <typename IN, typename OUT>
IN
copy(IN first, IN last, OUT out)
{
while (first != last)
*out++ = *first++;
return first;
}

This is an algorithm (albeit a very simple one) which works with an
incredible variety of container types and just about any data type. So why
not write image processing algorithms the same way? I have in fact done so
in code I cannot share with you, but I'll bet Google can find a bunch of
public domain versions already out there. Then your containers need only
provide the iterators. In my experience integer and floating point types may
require separate algorithms because of differences in rounding. You should
also be aware that you may run into some performance penalties if you make
things too general.

If you prefer a more object oriented approach, you could try something along
the lines of what I once wrote about in CUJ for color space transformations:

ftp://ftp.cuj.com/pub/1998/cujjul98.zip

Here each object is a color and each transform is embedded in the object.
Thus every pixel in a calibrated RGB image would know how to convert itself
to XYZ, for instance. All data types and the calibrations are implemented
with templates. The code as shown assumes an integer data type, but floating
point types can be accommodated by specializations.

--
Cy
http://home.rochester.rr.com/cyhome/
Jul 22 '05 #4
Jürgen Kaminski <jk*******@freenet.de> wrote in message news:<cj*************@news.t-online.com>...
Hello all,
I got some legacy code where a template implements a non-templated
interface. These classes are really HUGE, but it boils down to the
following structure:

class BaseClass
{
public:
virtual ~BaseClass() {}
virtual bool Create(size_t size) = 0;
/* lots of other stuff here */
protected:
void* m_pData;
};

template <class T>
class Wrapper : public BaseClass
{
public:
virtual ~Wrapper() { delete (T*) m_pData; }
virtual bool Create(size_t size)
{
m_pData = new T[size];
return (m_pData != 0);
}
T GetElement(int i) { return *((T*)m_pData+i); }
};

The purpose of this is to have collections of Wrappers with different types:

Wrapper<int>* intwrapper = new Wrapper<int>;
Wrapper<float>* floatwrapper = new Wrapper<float>;
intwrapper->Create(10);
floatwrapper->Create(5);

std::vector<BaseClass*> vec;
vec.push_back(intwrapper);
vec.push_back(floatwrapper);

for ( std::vector<BaseClass*>::iterator it = vec.begin();
it != vec.end(); ++it )
{
DoSomething( (*it) );
}
Now what do to with such a pointer? I can't do anything with it!
For example, I want to access the third element in each array and say
something like:

void DoSomething(BaseClass* any)
{
std::cout < "Element 3: " << any->GetElement(3);
}

But this obviously does not work. GetElement can't be virtual, because
BaseClass does not know which type it should return. Making it a
template does not help either, because this means giving up the nice
inhomogeneous container.
Is there some template magic to solve this problem? I know that it's all
screwed up, but there are thousands of LOC using this class.

You could do something like following the code I will post . But you
would need to create your own container and if you wanted to use
iterators , you'd also need to create them special. It's no trivial
task.
You'd also need to predefine the list of types that it could handle,
this example only handles 4 different types, just extend the Traits
List template for more.
Code:....
#include <iostream>

template<class T1, class T2, class T3, class T4>
class Traits{
private:
struct Empty{};
template<class H,class T>struct LN{
typedef H head;
typedef T tail;
};
template<class t1,class t2,class t3,class t4>struct List{
typedef LN<t1,LN<t2,LN<t3,LN<t4,Empty> > > > type;
};
public:
typedef typename List<T1,T2,T3,T4>::type type_list;
};

template<class _Traits>
class bData{
public:
typedef typename _Traits::type_list::head t1;
typedef typename _Traits::type_list::tail node2;
typedef typename node2::tail node3;
typedef typename node3::tail node4;
typedef typename node2::head t2;
typedef typename node3::head t3;
typedef typename node4::head t4;
//the following wouldn't work
//typedef typename _Traits::type_list::tail::head t2;
//I thought it might have done, dunno why.
virtual bData& operator=(const t1&){return *this;}
virtual bData& operator=(const t2&){return *this;}
virtual bData& operator=(const t3&){return *this;}
virtual bData& operator=(const t4&){return *this;}
};

template<class T,class _Traits=Traits<T1,T2,T3,T4> >
class Data:public bData<_Traits>{
public:
Data(){}
//contructor should use traits to make sure T is a memer of type_list.
//If not it can't work.
Data& operator=(const T& rhs){itsData=rhs; std::cout<<"in here\n";
return *this;}
private:
T itsData;
};
int main(){
typedef Traits<int,char,bool,double> type_list;

bData<type_list>* p1 = new Data<int, type_list>;
bData<type_list>* p2 = new Data<double, type_list>;

*p1 = 5;
*p2 = 5; // virtaul method doesn't bubble.
*p2 = 5.5; //virtual method bubbles ok.

return 0;
}

If a non-convertable operation is aplied to an object then it won't
bubble through the virtual table.

HTH Paul.
Jul 22 '05 #5
Cy Edmunds wrote:
[snip]
OK, image processing applications I understand. Object orientation may not
be the way to go here. Image processing is a large collection of algorithms
operating on data stored in a lot of different ways. If you look at the
standard library, which generally speaking isn't object oriented at all,
they attack this problem by keeping the algorithm separate from the the data
using iterators. Consider:

// untested code
template <typename IN, typename OUT>
IN
copy(IN first, IN last, OUT out)
{
while (first != last)
*out++ = *first++;
return first;
}

This is an algorithm (albeit a very simple one) which works with an
incredible variety of container types and just about any data type. So why
not write image processing algorithms the same way? I have in fact done so
in code I cannot share with you, but I'll bet Google can find a bunch of
public domain versions already out there. Then your containers need only
provide the iterators. In my experience integer and floating point types may
require separate algorithms because of differences in rounding. You should
also be aware that you may run into some performance penalties if you make
things too general.

If you prefer a more object oriented approach, you could try something along
the lines of what I once wrote about in CUJ for color space transformations:

ftp://ftp.cuj.com/pub/1998/cujjul98.zip

Here each object is a color and each transform is embedded in the object.
Thus every pixel in a calibrated RGB image would know how to convert itself
to XYZ, for instance. All data types and the calibrations are implemented
with templates. The code as shown assumes an integer data type, but floating
point types can be accommodated by specializations.


Thanks for your help.
Yes, I think this would be the right way to go. In fact I stumbled
already on a library called "vigra" (no pun intended) which can be found
at http://kogs-www.informatik.uni-hambu...~koethe/vigra/. I think
it's pretty nice, but too difficult for me to use out of the box at the
moment. Again, thanks for your help.
Jul 22 '05 #6
Paul wrote:
Code:....
#include <iostream>

template<class T1, class T2, class T3, class T4>
class Traits{
private:
struct Empty{};
template<class H,class T>struct LN{
typedef H head;
typedef T tail;
};
template<class t1,class t2,class t3,class t4>struct List{
typedef LN<t1,LN<t2,LN<t3,LN<t4,Empty> > > > type;
};
public:
typedef typename List<T1,T2,T3,T4>::type type_list;
};

template<class _Traits>
class bData{
public:
typedef typename _Traits::type_list::head t1;
typedef typename _Traits::type_list::tail node2;
typedef typename node2::tail node3;
typedef typename node3::tail node4;
typedef typename node2::head t2;
typedef typename node3::head t3;
typedef typename node4::head t4;
//the following wouldn't work
//typedef typename _Traits::type_list::tail::head t2;
//I thought it might have done, dunno why.
virtual bData& operator=(const t1&){return *this;}
virtual bData& operator=(const t2&){return *this;}
virtual bData& operator=(const t3&){return *this;}
virtual bData& operator=(const t4&){return *this;}
};

template<class T,class _Traits=Traits<T1,T2,T3,T4> >
class Data:public bData<_Traits>{
public:
Data(){}
//contructor should use traits to make sure T is a memer of type_list.
//If not it can't work.
Data& operator=(const T& rhs){itsData=rhs; std::cout<<"in here\n";
return *this;}
private:
T itsData;
};
int main(){
typedef Traits<int,char,bool,double> type_list;

bData<type_list>* p1 = new Data<int, type_list>;
bData<type_list>* p2 = new Data<double, type_list>;

*p1 = 5;
*p2 = 5; // virtaul method doesn't bubble.
*p2 = 5.5; //virtual method bubbles ok.

return 0;
}

If a non-convertable operation is aplied to an object then it won't
bubble through the virtual table.

HTH Paul.

Thanks for your help. If I understand this correctly (and I'm not sure
if I do) then I will need in the base class a stub for each type and
each operation (?) This does not work for virtual functions that return
any of t1,t2,t3,t4, does it?
Jul 22 '05 #7
Jürgen Kaminski <jk*******@freenet.de> wrote in message news:<cja14v$m4u$03[snip]

Thanks for your help. If I understand this correctly (and I'm not sure
if I do) then I will need in the base class a stub for each type and
each operation (?) This does not work for virtual functions that return
any of t1,t2,t3,t4, does it?


What you say is true but you can indirectly work around this.
My intention was not to use class Data<> directly but to create a
container of them e.g:
template<class _Traits=_Traits<T1,T2,T3,T4> >
class DataContainer{
//handle allocation and assignments to pointers of type
bData<_Traits>;
};

But that gets pretty complicated and the allocation is the main reason
for a traits class. But not getting into the implications of a
container class I will explain how to indirectly return diferent types
of objects.

Amend the previously posted code to include conversion and assigment
operators:
template<class _Traits>
class bData{
public:
bData(){}
virtual ~bData(){}
typedef typename _Traits::type_list::head t1;
typedef typename _Traits::type_list::tail node2;
typedef typename node2::tail node3;
typedef typename node3::tail node4;
typedef typename node2::head t2;
typedef typename node3::head t3;
typedef typename node4::head t4;
virtual bData& operator=(const t1&){return *this;}
virtual bData& operator=(const t2&){return *this;}
virtual bData& operator=(const t3&){return *this;}
virtual bData& operator=(const t4&){return *this;}
virtual operator t1(){return t1();}
virtual operator t2(){return t2();}
virtual operator t3(){return t3();}
virtual operator t4(){return t4();}
};

template<class T,class _Traits=Traits<T1,T2,T3,T4> >
class Data:public bData<_Traits>{
public:
Data(){}
~Data(){}
Data(const Data& rhs){itsData = rhs.itsData;}
Data(T& d){itsData = d;}
Data& operator=(const T& rhs){itsData=rhs; return *this;}
operator T(){return itsData;}
private:
T itsData;
};

Although you cannot directly call for an objects data you can extract
a copy now.
Using a simple test class like the following here is an example:

class A{
public:
A():x(0){std::cout<<"A()\n";}
A(int a):x(a){std::cout<<"A(int)\n";}
~A(){std::cout<<"~A()\n";}
A(const A& rhs){x=rhs.x; std::cout<<"A(const A&)\n";}
operator int(){return x;}
private:
int x;
};

int main(){
typedef Traits<int,A,bool,double> type_traits;

bData<type_traits>* p = new Data<A, type_traits>;
A a=5;
*p = a; //Assigment example
A b = *p;
std::cout<< b << std::endl;
delete p;

p = new Data<double, type_traits>;
double c = *p = 6.6;
int x = *p = 7; //Unhandled Error, 7 is an int thus x is undefined.
double d = *p;
std::cout<< d << std::endl;

delete p;
return 0;
}

As you can see a copy of the object(no matter what type it is) can be
created. Then you can do what you want with the copy then reapply it
to the original if you wanted to.
you can create traits like is_convertable_to_int<T> then apply this to
a type_list to create direct conversion for example:

bData<type_traits>* p = new Data<A, type_traits>;
*p = 5;

Although A can convert to an int it won't bubble as A is not an int
type but you can , from within the virtual stub, ask the object if it
can convert. By using traits and type_lists the object can check to
see if it is a member of ,in this case int_types(a type_list of all
int types), and if it is a memeber assign the integer directly to
itself. It should only be a member of int_types if it can convert from
int.
Not sure if I explained the above well but if you understand it you
probably ask but what if it can't convert. This is a design issue and
perhaps you want to treat this as an error or let it go as in the
example where int type was assigned to a double.
you may wish to let it go unnoticed so that you could do something
like this:

for(int i=0 ; i<containerlenght; i++){
dataContainer[i] = "A string";
}

Then all types that were in type_list string_types would be assigned
to but all the non-convertable_to_string types would be left
untouched.

The problem I found with creating this type of container is with these
kind of design choices.

HTH
Paul.
Jul 22 '05 #8

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

1
by: Markus Seeger | last post by:
Hi, I'm writing a commandline argument parser that can handle switches by using some techniques similar to the Qt slot mechanism. example: // inheritance method (what I'm trying at the...
3
by: darkstorm | last post by:
I have a doubt regarding inheritance involving templates Consider this: ///////////////////////////////////// template<typename T> class A { private: T m_a;
16
by: WittyGuy | last post by:
Hi, What is the major difference between function overloading and function templates? Thanks! http://www.gotw.ca/resources/clcm.htm for info about ]
10
by: makc.the.great | last post by:
now that I am looking at templates, there's the question. why same effect couldn't/shouldn't be achieved with inheritance? provided the difference of the two, when and where do I use templates...
11
by: Peter Oliphant | last post by:
Is there any plan to support templates with managed code in the (near) future? For instance, VS.NET 2005... : )
4
by: qning88 | last post by:
I would like to find out how I can effectively make use of templates in theexample below. In Class A, I have 3 overloaded member functions "send" for handling the different messages. Although...
6
by: Ravi Rao | last post by:
Hi, This is about "templates vs inheritance" (please, before you flame me, I do understand that they cover largely non-intersecting domains). Apart from D&E*, I've found either online...
3
by: Paulo Matos | last post by:
Hi all, I'm curious if I can say in C++ that a certain template can only be instantiated (template-wise) by a class implementing a certain interface (inheriting from a given abstract class). ...
5
by: Lars Hillebrand | last post by:
Hello, i discovered a weird behaviour if i use templates together with virtual inheritance and method over. I managed to reproduce my problem with a small example: // *********** <code...
2
by: Isaac Gelado | last post by:
Hi, I am having problems with inheritance in templates classes. Say I have the following classes: class A {}; class B: public A {}; template<typename Tclass C {}; Now in my code I have...
0
by: Faith0G | last post by:
I am starting a new it consulting business and it's been a while since I setup a new website. Is wordpress still the best web based software for hosting a 5 page website? The webpages will be...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 3 Apr 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome former...
0
by: ryjfgjl | last post by:
In our work, we often need to import Excel data into databases (such as MySQL, SQL Server, Oracle) for data analysis and processing. Usually, we use database tools like Navicat or the Excel import...
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.