Sign in to follow this  

template classes and inheritance

This topic is 4859 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hey folks, I'm still a real newbie when it comes to mixing templates and class inheritance, so I'm trying to get advice on the best way to proceed with my class heirarchy design (that is likely to cause the least amount of problems down the track when it comes to using the classes... because I won't be the one using them...). Basically, I want to derive from a base template class (the base class is a storage class and I'm adding some functionality for a particular application). I expect that this is a fairly trivial problem, so I assume I could start with something like
template <typename T>
class BaseStorageClass
{...};

template <typename T>
class DerivedStorageClass : public BaseStorageClass<T>
{...};




But is this the best way to do this? Now, what if I want to store objects from another template class in my DerivedStorageClass... let's say objects like
template <typename V, V value>
class MyObjectClass
{...};




Would the following be a correct instantiation?
typedef MyObjectClass<int,5> Int5ObjectType
typedef DerivedStorageClass<Int5ObjectType> Container;

Int5ObjectType An_Object;
Container  A_Container;

A_Container.insert(An_Object);




(assuming the existance of the method 'insert' of course) Would there be any particular issues I need to be aware of if I wanted to create all of my objects at run-time (i.e, dynamically)? My second question relates particularly to my derived class. The derived class needs to know something about the type of attributes of objects being stored... in this case, the tempalte parameter V. What is the best way to access this information? Use something like...
template <typename V, V value>
class MyObjectClass
{
public:
typedef V value_type;
};

template <typename T>
class DerivedStorageClass : public BaseStorageClass<T>
{
public:
  typedef typename T::value_type ValueType;
};




Now, if the above is correct, is there any problem in using ValueType as a return type of member functions of DerivedStorageClass? If the above is incorrect, how do I use 'V' as a return type in DerivedStorageClass if objects are created dynamically? Finally, is there any way to use a BaseStorageClass version of that member function (with return type ValueType) or do I have to override that function in DerivedStorageClass since the BaseStorageClass won't know about the return type... or should I simply add the typedef typename T::value_type ValueType statement to the base class instead of the derived class. If I do this, how do I access it in the derived class and what would the return type look like in the derived class if I did want to override the base class version of the function? Any and all help is greatly appreciated... treat this as a learning excercise for me please so feel free to educate me... just don't criticise my OOP newbie-ness please! ;) Cheers, Timkin

Share this post


Link to post
Share on other sites
Quote:
Original post by Timkin
Hey folks, I'm still a real newbie when it comes to mixing templates and class inheritance, so I'm trying to get advice on the best way to proceed with my class heirarchy design (that is likely to cause the least amount of problems down the track when it comes to using the classes... because I won't be the one using them...).

Basically, I want to derive from a base template class (the base class is a storage class and I'm adding some functionality for a particular application). I expect that this is a fairly trivial problem, so I assume I could start with something like

*** Source Snippet Removed ***

But is this the best way to do this?


Hi it depends on what your trying to do, could please explain the purpose of these three types so then it is easy to see if this or an alternative is better.

Share this post


Link to post
Share on other sites
Okay i read it abit more carefully now.

Quote:
Original post by Timkin

template <typename T>
class BaseStorageClass
{...};

template <typename T>
class DerivedStorageClass : public BaseStorageClass<T>
{...};


But is this the best way to do this?


There could be one problem with that if BaseStorageClass doesn't have virtual deconstructor then you will have problems if you try delete an instance of DerivedStorageClass that is being refered to by a pointer of type BaseStorageClass.

Secondly if BaseStorageClass is anything like STL containers where the deconstructor is not virtual and there not mean't to be derived.

If that is the case then probably better solution would be to make an adaptor type like STL's stack type e.g.:


template <typename T>
class BaseStorageClass
{...};

template < typename Type, template < typename Contained > class Container = BaseStorageClass >
class StorageAdaptor {
public:

typedef Type value_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef value_type& referece;
typedef const value_type& const_reference;

typedef Container< value_type > container_type;

typedef typename container_type::iterator iterator;
typedef typename container_type::const_iterator const_iterator;

private:

container_type c;

public:
//....
//....
};







Quote:
Original post by Timkin
Now, what if I want to store objects from another template class in my DerivedStorageClass... let's say objects like
*** Source Snippet Removed ***
Would the following be a correct instantiation?

*** Source Snippet Removed ***
(assuming the existance of the method 'insert' of course)


seems to be correct.

Quote:
Original post by Timkin
Would there be any particular issues I need to be aware of if I wanted to create all of my objects at run-time (i.e, dynamically)?

My second question relates particularly to my derived class. The derived class needs to know something about the type of attributes of objects being stored... in this case, the tempalte parameter V. What is the best way to access this information? Use something like...
*** Source Snippet Removed ***

Now, if the above is correct


yep, as long as the type given to the type parameter supports value_type typedef. Alternative is to not be so strict on the type T to types that only support value_type by using template template parameters e.g.


template <typename V, V value>
struct MyObjectClass {

typedef V value_type;

};

template < typename T, V val, template< typename U, U value > class W >
class DerivedStorageClass : public BaseStorageClass<T>
{
public:
typedef typename W< T, val >::value_type value_type;
};
//.....
DerivedStorageClass< int, 5, MyObjectClass > m;




Quote:
Original post by Timkin
is there any problem in using ValueType as a return type of member functions of DerivedStorageClass? If the above is incorrect, how do I use 'V' as a return type in DerivedStorageClass if objects are created dynamically?


If i understood you correct there shouldn't be any problems.

Quote:
Original post by Timkin
Finally, is there any way to use a BaseStorageClass version of that member function (with return type ValueType) or do I have to override that function in DerivedStorageClass since the BaseStorageClass won't know about the return type... or should I simply add the typedef typename T::value_type ValueType statement to the base class instead of the derived class. If I do this, how do I access it in the derived class and what would the return type look like in the derived class if I did want to override the base class version of the function?


if you wont to access the base's typedefs just do this:


template< typename T >
struct base { typedef T value_type ; };

template< typename T >
struct derived : base<T> {

typedef typename base<T>::value_type value_type;

};


[Edited by - snk_kid on August 26, 2004 3:23:13 AM]

Share this post


Link to post
Share on other sites
Thanks for the quick reply snk_kid and taking the time to go through my code...

Quote:
Original post by snk_kid
There could be one problem with that if BaseStorageClass doesn't have virtual deconstructor


Sorry, I thought that would have been obvious, hence I didn't bother putting in such things in the class definitions... I was more interested in the affects of the templating on the inheritance.

Quote:
Original post by snk_kid
Secondly if BaseStorageClass is anything like STL containers where the deconstructor is not virtual and there not mean't to be derived.


Well that's a pain (didn't know STL doesn't have virtual destructors). I don't generally use the STL (although I know I should use it more than I do). I have my own base class but I thought I'd just plug in an STL container for now... but I guess not.

Quote:
Original post by snk_kid
If that is the case then probably better solution would be to make an adaptor type like STL's stack type e.g.:


template < typename Type, template < typename Contained > class Container = BaseStorageClass >
class StorageAdaptor {...};


But don't you then have the problem of needing to write your own methods to gain access to the BaseStorageClass methods, since the BaseStorageClass is now an attribute of the DerivedClass.

That's sort of the way I had originally designed the system (using template template parameters) but it got really convoluted (and I doubt it was anywhere near as clear as your example!) and so I thought a little inheritance was in order! ;)

Quote:

Alternative is to not be so strict on the type T to types that only support value_type by using template template parameters
e.g.


It's probably that I don't really understand template template parameters so well, but why is it BaseStorageClass<T>. (Shouldn't it be BaseStorageClass<W>?)

Quote:

if you wont to access the base's typedefs just do this:


Ah, not quite what I was asking, sorry if I wasn't clear...

What I was asking was more along the lines of


template <typename V, V value>
class MyObjectClass
{
public:
typedef V value_type;
};

template <typename T>
class BaseStorageClass
{
public:
typedef typename T::value_type ValueType;
};



and then being able to access ValueType from DerivedStorageClass. Why did you use structs in your example. Are these in addition to the class definitions (to fool the compiler) or was it that you were just using structs instead of classes because of the simplicity of the example? In other words, I don't really understand your solution... could you explain a little more please?


Finally, I have one additional question I was saving from earlier (because there were already so many)! How does any of this change (if at all) if I don't know the type and value of the MyObjectClass parameters until runtime? How is the compiler going to be able to handle a non-const value as a template parameter?

If it can't, then I suppose that I need to pass 'value' into the MyObjectClass constructor as a variable (instead of having it as a template parameter) and deal with it that way. Any other way?

Again, thanks for the help.

Timkin

Share this post


Link to post
Share on other sites
Quote:
Original post by Timkin
Well that's a pain (didn't know STL doesn't have virtual destructors). I don't generally use the STL (although I know I should use it more than I do). I have my own base class but I thought I'd just plug in an STL container for now... but I guess not.


There are good reason why mainly for speed, probably all STL free functions, member functions & functors are inlineable.

It is still acceptable to sub-type from STL containers then have an instance of that sub-type refered to by a pointer to STL container it just that your sub-type's deconstructor wont be invoked so as long as you don't need to free any resources there it should be ok e.g.


#include <list>
#include <iostream>

template<
typename Type,
template< typename U > class Container = std::list
>
struct PolyCont : public Container< Type > {

typedef Container< Type > Base;

};

int main() {

std::list<int>* l = new PolyCont<int>();

l->push_back(4);

delete l;

}




just remember thou the went designed for this in mind.

Quote:
Original post by Timkin
But don't you then have the problem of needing to write your own methods to gain access to the BaseStorageClass methods, since the BaseStorageClass is now an attribute of the DerivedClass.


unfortunately, you could do it like STL adaptors do they just provide a new interface that is abit more restricted for instance stack has methods push & pop only for adding & getting an element.

Also if you wonted to do what you where doing before you could combine the approach so you can handle both containers with virtual deconstructors & STL containers. To handle the case of STL containers make a polymorphic adaptor type that contains it and has a virtual deconstructor e.g.


#include <list>

template <

typename Type,
template< typename Contained > class Container = std::list

>
class base_stl_container {

public:

typedef Type value_type;
typedef Container< value_type > container_type;

private:

container_type c;

public:

virtual void push_back(const value_type& v) {
c.push_back(v);
}

virtual ~base_stl_container() {}

};


template<

typename Type,
template < typename V > class Base

>
struct derived_container : public Base< Type > {


typedef Base< Type > base_type;

};

int main() {

base_stl_container<int >* l = new derived_container< int, base_stl_container >();

l->push_back(3);

delete l;

return 0;

}



Quote:
Original post by Timkin
It's probably that I don't really understand template template parameters so well


I just noticed i made a very very slight mistake in that example it should have been


template <typename V, V value>
struct MyObjectClass {

typedef V value_type;

};

template < typename T, T val, template< typename U, U value > class W >
class DerivedStorageClass : public BaseStorageClass<T>
{
public:
typedef typename W< T, val >::value_type value_type;
};
//.....
DerivedStorageClass< int, 5, MyObjectClass > m;



Quote:
Original post by Timkin
but why is it BaseStorageClass<T>. (Shouldn't it be BaseStorageClass<W>?)


Sorry yes your wright then if W is mean't to be the type contained in the base then it would be:


template <typename V, V value>
struct MyObjectClass {

typedef V value_type;

};

template < typename T, T val, template< typename U, U value > class W >
class DerivedStorageClass : public BaseStorageClass< W< T, val > > {
public:
typedef typename W< T, val >::value_type value_type;
};
//.....
DerivedStorageClass< int, 5, MyObjectClass > m;



Quote:
Original post by Timkin
Ah, not quite what I was asking, sorry if I wasn't clear...

What I was asking was more along the lines of

*** Source Snippet Removed ***
and then being able to access ValueType from DerivedStorageClass. Why did you use structs in your example. Are these in addition to the class definitions (to fool the compiler) or was it that you were just using structs instead of classes because of the simplicity of the example?


Yeah i was using structs to just save abit of typing, in c++ structures & classess are the same but different default access permissions, structures default to public access & inheritance where as classes default to both private.

Quote:
Original post by Timkin
In other words, I don't really understand your solution... could you explain a little more please?


Sure i'll try which bit didn't you understand?

Quote:
Original post by Timkin
Finally, I have one additional question I was saving from earlier (because there were already so many)! How does any of this change (if at all) if I don't know the type and value of the MyObjectClass parameters until runtime? How is the compiler going to be able to handle a non-const value as a template parameter?

If it can't, then I suppose that I need to pass 'value' into the MyObjectClass constructor as a variable (instead of having it as a template parameter) and deal with it that way. Any other way?

Again, thanks for the help.

Timkin


Ah i think i see what your trying to do now well yes all of this wont be that much use if the type of objects going to be contained are not known until run-time. I think there is a much simplier solution and the key here is dynamic polymorphism or late binding. Are the objects going to be loaded from a file?

If thats the case then start off having a base type with virtual methods and sub-type all the types that are going to be loaded at run-time from the file.

Have some STL container contain pointers to base type, then you'll need a some kind of factory or abstract factory type that instantiates the correct sub-type on the heap from the file and builds it and then just add pointer to the container of pointers to base. If you wont a pseudocode code for this just let me know

Share this post


Link to post
Share on other sites
Quote:
Original post by snk_kid
There are good reason why mainly for speed, probably all STL free functions, member functions & functors are inlineable.


I had originally balked at using STL containers, mainly for the storage overhead when you have many containers holding only a few things each. I had written my own base container type that was basically a dynamic array type with additional stack functionality. In hindsight though, RAM is cheap, time isn't; so I'm leaning now toward using an STL container as the base type.

Quote:
Original post by snk_kid
It is still acceptable to sub-type from STL containers then have an instance of that sub-type refered to by a pointer to STL


...if I understand the affect of this is that the pointer to STL type will be castable to pointer to base type but when I call member functions through that pointer, I'll get base type functions. Correct?


Quote:
Original post by snk_kid
Also if you wonted to do what you where doing before you could combine the approach so you can handle both containers with virtual deconstructors & STL containers.
*** Source Snippet Removed ***


I'm really going to have to sit down over the weekend and read through that to make sure I understand it!

Quote:
Original post by snk_kid
Sure i'll try which bit didn't you understand?


'Tis okay, I understand now!

Quote:
Original post by snk_kid
Ah i think i see what your trying to do now...


Hehe... and I thought I was being deliberately obtuse! I didn't want to cloud the basic C++ issues with application issues, since this isn't a 'hunt the bug' thread... and I wanted to learn the principles first before tailoring them to the app. To give you some idea of what I am doing though...

I'm writing a class library for data types used in image segmentation problems. These types are to be used in conjunction with a funky Tree class I wrote that handles all different sorts of decompositions within a simple interface. Images are read from netcdf files from which things like the dimensionality of the image and the data value type are determined. A voxel data object is created (and initialised) for each image voxel and then the voxels are stored in a single image object (which is my derived container). This container has methods to perform certain basic computations on the voxel data within the image. The tree class accepts a single image into its constructor and then recursively builds a decomposition of the image by subsequent calls to the Tree's constructor for left and right branches, which are passed subsets of the image passed in (there's a bit more to the constructor than this, but that's the gist of it). When complete, the tree stores images in its leaf nodes, with each image being a subset of the original image. The union of the data in the leaves is the original image. This tree is then used for further analysis, visualisation, etc.

The stuff we've been discussing in this thread is just related to the voxel and image classes and not the tree class. I'm reasonably confident I have that class sorted out, although I'm sure I'll review that once I have finalised the image class.

I'll read up on the Factory info you posted this weekend and will holler if I need any more of your great help!

Thanks heaps (once again!)

Timkin

Share this post


Link to post
Share on other sites
Quote:
Original post by Timkin
Quote:
Original post by snk_kid
There are good reason why mainly for speed, probably all STL free functions, member functions & functors are inlineable.


I had originally balked at using STL containers, mainly for the storage overhead when you have many containers holding only a few things each. I had written my own base container type that was basically a dynamic array type with additional stack functionality. In hindsight though, RAM is cheap, time isn't; so I'm leaning now toward using an STL container as the base type.


std::vector should consume 4 pointer's worth of space. The bigger problem is "lots of containers", that's where the inefficeny is if you want to do something about it (e.g. index buffers are an optimization so you only need one array of vertices, less containers to manipulate).

Quote:
Original post by Timkin
Quote:
Original post by snk_kid
It is still acceptable to sub-type from STL containers then have an instance of that sub-type refered to by a pointer to STL


...if I understand the affect of this is that the pointer to STL type will be castable to pointer to base type but when I call member functions through that pointer, I'll get base type functions. Correct?

[/quote]

Yes, it's closely related to something called slicing, and should be avoided.

One thing to note about template base-classes is that they are not the same for different types. Often this is what you want, but not always. That is DerivedTemplate<int> and DerivedTemplate<float> do not have a common ancestor.
You have to make an additional abstract base class if that's what you want (design by contract/interface, then build concrete classes using templates).

Rather than derived from the base container class, it should probably be aggregated. I've been lazy a few times though and used inheritence to tack on a couple of virtual functions to a vector or list. I haven't settled on a good way to share and iterate containers in C++. Using templates in public interfaces isn't always a good idea, but without that you have to build an OO interface, like COM's IEnumXXXXX.

What you have is quite complicated and the classes are heavily intertwined. Why is the V type so important? This mean you can't use this container with anything other than MyObjects, so why it is a template?

Maybe what you want is a template-temaplte parameter?

Heh, found a bug in MSVC 7.1 :) This works with gcc though.

template<template<class V, V val> ObjectType, typename Type1, typenmae Type1>
struct Container
{
typedef ObjectType<Type1, Type1()> MyObject1;
typedef ObjectType<Type2, Type2()> MyObject2;
};

...
Quote:
Original post by snk_kid
Also if you wonted to do what you where doing before you could combine the approach so you can handle both containers with virtual deconstructors & STL containers.
*** Source Snippet Removed ***

Destructor not deconstructor :P

Share this post


Link to post
Share on other sites
Quote:
Original post by Magmai Kai Holmlor
std::vector should consume 4 pointer's worth of space.


When I first tackled this problem for my employer, I didn't like the overhead of 40,000 pointers just to store 10,000 things. Now I'm not so concerned because I'm writing this for myself (not based on their code, so a complete redesign... and the algorithms were mine/public domain anyway) and hey, RAM is cheap)!

Quote:
Original post by Magmai Kai Holmlor
e.g. index buffers are an optimization so you only need one array of vertices, less containers to manipulate.


I'm not certain that I know what index buffers are (I could guess) but that doesn't sound like a viable approach, since it would require me to write accessor functions to pull out and manipulate sub images via the index buffer, rather than just pass around image objects.


Quote:
Original post by Magmai Kai Holmlor
One thing to note about template base-classes is that they are not the same for different types.


Expected that. Wanted that. Might not be the best approach... but that's what I'm here for... trying to learn what is.

Quote:
Original post by Magmai Kai Holmlor
Rather than derived from the base container class, it should probably be aggregated.


Since, as I said earlier, I'm not a big user of the STL, then I've found that aggregating container classes makes for messy code. I guess it's time I started using the STL and iterators more!

Quote:
Original post by Magmai Kai Holmlor
What you have is quite complicated and the classes are heavily intertwined.


Yes, they are. But given this structure it's also much easier for the programmer using the class library to define objects like images and voxels and then manipulate them with other classes like my Tree class. Of course, I could write the whole thing in C and ignore OOP... but this is as much another learning experience for me, a chance to learn some valuable OOP skills and an opportunity to develop something that is lacking in this field. Sure, mostly it will be used by just myself and a few people, but I would like to make it the backbone of a GUI based image segmentation program.

Quote:
Original post by Magmai Kai Holmlor
Why is the V type so important?


Because it determines critically the segmentation algorithm that must be applied and the results that are obtained from the segmentation. It's a probability/coding theory thing.

Quote:
Original post by Magmai Kai Holmlor
This mean you can't use this container with anything other than MyObjects, so why it is a template?


Because I didn't want to write specialisations of the container class for each possible data type V. Call me lazy.

Quote:
Original post by Magmai Kai Holmlor
Maybe what you want is a template-temaplte parameter?


I've been down this road and I was looking for a different path... I didn't like the bumps. ;)

Quote:
Original post by Magmai Kai Holmlor
Heh, found a bug in MSVC 7.1 :) This works with gcc though.


I hate that MSVC doesn't permit exporting templates... 8(


Thanks for the input,

Timkin

Share this post


Link to post
Share on other sites

This topic is 4859 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this