Archived

This topic is now archived and is closed to further replies.

Mezz

Restricted templates

Recommended Posts

Hi, I''ve got a query regarding templated programming. Is it possible to be restrictive with the template''s parameter. As in, can I specify it to be of a derivation of a particular type? I mean, this sounds quite odd but what I''m trying to achieve is the ability to generically handle a set of derived classes. The issue is that I need to allocate an array of the parameter''s type (say, DATATYPE), on initialisation. After that I only need to access one member of the type (present in the base structure) just to search on. To try and clarify: Can I cast some DATATYPE pointer to the base class and then access the string from this safely? e.g. BaseType *base = (BaseType *)datatypeptr; then use base->string for whatever I need... Alternatively, when I specify the in the declarations can I make it so that DATATYPE is only of BaseType or it''s derivitives (but then would I call new on it in the same manner when I allocate the array?). Finally, if this is complete trash and there is a better way please tell me, because I''m fairly new to templates and I might not be using the design like I should. Thanks, -Mezz

Share this post


Link to post
Share on other sites
quote:
Original post by Mezz
Hi,

I've got a query regarding templated programming.

Is it possible to be restrictive with the template's parameter. As in, can I specify it to be of a derivation of a particular type?
I mean, this sounds quite odd but what I'm trying to achieve is the ability to generically handle a set of derived classes.


It doesn't sound that odd. It's called "bounded parametric polymorphism", and C++ provides no direct support. However, that doesn't mean you can't use some combination of other mechanisms to simulate what you want to do.

quote:

The issue is that I need to allocate an array of the parameter's type (say, DATATYPE), on initialisation. After that I only need to access one member of the type (present in the base structure) just to search on.

Err.. I don't understand. I get the point that you want to parameterise on some type, which will be the element type of the array, but where does the type restriction come into it?

quote:

To try and clarify:

Can I cast some DATATYPE pointer to the base class and then access the string from this safely?

Woah... I'm a bit uncomfortable with the idea that you want to cast between types. Templates are one of the mechanisms that help us avoid the abomination of typecasting.

Perhaps you can explain what you are trying to do in another way, possibly with code samples to illustrate? I might be being dumb here, but I don't "get" what it is you are trying to do.

--
The placement of a donkey's eyes in its head enables it to see all four feet at all times.

Edited by - SabreMan on February 22, 2002 11:25:52 AM

Share this post


Link to post
Share on other sites
OK, I'll try and set it out...



// a base class

class BaseData
{
public:
char a_string[16];
};

// a derived class

class DerivedData : public BaseData
{
public:
int anothermember;
};



OK, I realise these are trivial examples but hopefully it will make sense a bit further down...

This is the class I am templating...



// there is a typename here, but the board doesn't show it
// inside the angle brackets
template
class DataManager
{

DATATYPE *the_array;

public:

int Initialise(int num)
{
// the array allocated on startup I was on about...
the_array = new DATATYPE[num];
};

int Search(char *cmp)
{
// assume other stuff in here
// ... looping down whatever

strcmp(the_array[loop].a_string, cmp);

}

};



I'm sorry those seem such horrible examples but hopefully you can see that I *know* DATATYPE will always be BaseData or a derivitive, now, in the Search() routine I don't need to access anything apart from the string member of BaseData - so how can I do this and still have the array allocated in Initialise() be of type DATATYPE (which is one of the derived classes).
Obviously, I've cut down a lot on the functions for simplicity, I know they'd never be used in that state in reality.

Does that make more sense?

Edits: the spacing has gone to hell, sorry...

Edited by - Mezz on February 22, 2002 11:48:47 AM

Share this post


Link to post
Share on other sites
You can do this through specialisation of the template. If you declare the template like so:

  
// forward declaration of templated class:

template<typename T> class DataManager;



And then create specialisations for each type that you want to be able to instantiate DataManager with:

       
template <>
class DataManager<BaseData>
{
DATATYPE *the_array;
public:
int Initialise(int num)
{
the_array = new DATATYPE[num];
return 0;
}

int Search(char *cmp)
{
strcmp(the_array->a_string, cmp);
return 0;
}
};

template<>
class DataManager<DerivedData>
{
//...

};


You will find that instantiations of the template will only succeed where a defined specialisation exists. That's the simplest mechanism, which would be OK if you only have two classes. If you are planning on building a hierarchy of classes for which you will need many specialisations, it is not hard to see how you will end up duplicating a lot of common code, which just seems daft.

In the spirit of the saying "you can solve any programming problem via a level of indirection", we can indirect the instantiation of the type you are interested in, and factor out the common code. One way of doing this would be to use "traits classes", where you would create a small templated traits class for each data type you want to specialise on, and pass that traits class into the common code. I don't have time to write an example right now, so I'll leave you to mull it over. I might try and post back at the weekend if I can get around to it...

--

The placement of a donkey's eyes in its head enables it to see all four feet at all times.


Edit: fix bad formatting and add one code comment.

Edited by - SabreMan on February 22, 2002 12:18:33 PM

Share this post


Link to post
Share on other sites
Thanks for the ideas Sabre... I''ll think them over and try to understand them...

I might try implementing a few tests and see what happens.

-Mezz

Share this post


Link to post
Share on other sites
If you have some time on your hands look up info on "Compile time assertions" and "template metaprogramming". A good starting point would be here. About half way down is a way of detecting (at compile time) whether one class can be converted to another. You can use those tests in a compile time assert, to cause template instantiation failure if the class isn''t derived from your base class. I''ll warn you now though, the syntax is pretty evil . If you''ve only got a couple of classes, it might be better to find a simpler way.

Share this post


Link to post
Share on other sites
I quite like Krunk''s line of thinking here. Revisiting the actual problem, you want to write a templated class and say to the compiler and future users of your code: "I only want this templated class to be instantiated for a particular range of types". This is what I said is known as bounded parametric polymorphism. Since C++ does not directly support this concept, you need some mechanism that allows you to do this.

The first solution I thought of is template specialisation, which I showed earlier in this thread. The trouble with that approach is that you end up replicating the same code for every specialisation. Possibly more serious than that, it also does not express the intent of what you are doing to anyone reading your code. What is there in the code to suggest you have placed a restriction on the allowable types?

I then thought of an idea using policy classes as a mix-in, where you would create a small policy for each allowable type. This partially addresses the code replication problem, but it still does not express the intention of what you want to do.

And so to compile-time assertions. This strikes me as being the best way to solve the problem and to express the intent. The only issue here is that the underlying machinery is rather complex. However, life is made easier because this machinery has already been written by top-notch experts. The Boost library (www.boost.org) provides the static_assert facility, and this will help solve the problem. The trouble is, I could not get it to work under MS v6 (which is a sack of crap when it comes to templates). The other problem is that I think you still need more mechanisms to do exactly what you want. Ideally, you could either build a typelist of the allowable types and assert if there is an attempted instantiation with a type not present in the typelist. Or, you could supply a base class and assert if the instantiated type is not the base class or a derivative. All of these require additional mechanisms which can be found in Alexandrescu''s Loki library (which also doesn''t work well with MS v6).

It''s difficult to demonstrate all of these possibilities in a forum posting, as there''s a lot going on and my time is limited (err.. I''m sure I have some "real" work to do). It''s fairly advanced stuff so, if you can''t manage to grok this stuff yourself, you might want to stick with the simplest solution that fulfils your need. Having said that, I''ve actually been threatening to write an article or two for various sources, and this strikes me as good material. If I can get around to it, I''ll be sure to let you know.

--

The placement of a donkey''s eyes in its head enables it to see all four feet at all times.

Share this post


Link to post
Share on other sites
Can''t seem to leave this topic alone...

I''ve been fiddling with MS''s compiler and have come up with a fairly simple solution to what you initially asked.

  
#include <cstddef>

template <class base, class der>
struct is_subclass_of
{
is_subclass_of()
{
static const base* const subclass_check = (const der*)0;
static const size_t void_check = sizeof(base);
}
};

class A
{
};

class B : public A
{
};

int main()
{
is_subclass_of<A,B> s1; // B is subclass of A - OK

is_subclass_of<B,A> s2; // fails to compile

is_subclass_of<void,B> s3; // fails to compile

}


This provides a facility to check that one class is a subclass of another. MS didn''t allow me to write the template how I would prefer, but this should be sufficient for your initial problem. Now, in your templated class, you should be able to utilise this check as required. I''m pretty damn sure I could write a completely static version of this with a bit more effort (and with a compiler other than MSVC++6), but I could go on ad-infinitum. I suggest that, once you are comfortable with templates, you read the book Modern C++ Design.

--

When horses lift their heads up high to look at something, they''re looking far into the distance. To see things that are closer, they lower their heads.

Share this post


Link to post
Share on other sites