r H2 deduce deduce template argument of a template class inheriting from a non template base?

r H2 deduce deduce template argument of a template class inheriting from a non template base?

Post by nguillo » Sun, 08 Mar 2009 05:11:21


Hello.

I'm not sure I can do what I will ask you...

Let's say I've got a template class inheriting from a non template
class:

struct SData
{
virtual ~SData() {}
};

template <class T>
struct SDataContainer : public SData
{
SDataContainer(const T& o) : m_data(o) {}
T m_data;
};

Then I have a class with methods receiving a SData as parameter. It
can be a SDataContainer<T>, or other class inheriting from SData.
In the following example, the method print will deduce the type of the
data thanks to dynamic_cast:

class SPrintDataContainer
{
public:
void print(SData* pData)
{
SDataContainer<int>* pi = dynamic_cast<SDataContainer<int>*>
(pData);
if (pi != 0)
{
cout << "printing integer " << pi->m_data << endl;
return;
}

SDataContainer<bool>* pb = dynamic_cast<SDataContainer<bool>*>
(pData);
if (pb != 0)
{
cout << "printing bool " << (pb->m_data ? "true" :
"false") << endl;
return;
}

cout << "data type not handled" << endl;
}
};

So I can use that as follow:

SData* pDataInt = new SDataContainer<int>(5);
SData* pDataBool = new SDataContainer<bool>(false);
SData* pDataDouble = new SDataContainer<double>(2);
SPrintDataContainer DataPrinter;

DataPrinter.print(pDataInt);
DataPrinter.print(pDataBool);
DataPrinter.print(pDataDouble);

It will output

printing integer 5
printing bool false
data type not handled

(and produce 3 memory leaks ;-) ).

Ok.

Now, the question: is there a way to avoid the dynamic_cast and deduce
the template argument thanks to template method specialization.

I would like to write something like that:

class SPrintDataContainer_withoutDynamicCast
{
public:
template <class T>
void print(SDataContainer<T>* pData)
{
cout << "data type not handled" << endl;
}

template <>
void print(SDataContainer<int>* pData)
{
cout << "printing integer " << pData->m_data << endl;
}

template <>
void print(SDataContainer<bool>* pData)
{
cout << "printing bool " << (pData->m_data ? "true" : "false")
<< endl;
}
};

But I can't use this class like that:

SPrintDataContainer_withoutDynamicCast DataPrinter2;
DataPrinter2.print(pDataInt);
DataPrinter2.print(pDataBool);
DataPrinter2.print(pDataDouble);

**First: Is me need clear?
There is a good example of the interest of this kind of design
here:
http://www.yqcomputer.com/
Here, we pass either a leaf<T> or an inner_node as a node.
And we can imagine a class that must print a tree, that receives
nodes, and according to the type (inner_node, leaf<int>,
leaf<double> ....) it must display something different.

**So: my question is: is there a way, a workaround of my sample, to
deduce the template argument of the template class when we receive an
object of the base class, in the way I suggest (template method
specialization). Maybe with proxy classes that would deduce the
template, I don't know.

Thanks in advance for you help, or critics.

Nicolas.
 
 
 

r H2 deduce deduce template argument of a template class inheriting from a non template base?

Post by SG » Sun, 08 Mar 2009 06:44:21


> virtual ~SData() {}> > };

Are you sure you don't need some more functions? Perhaps something
like this:

struct SData
{
virtual ~SData() {}
virtual void print_on(std::ostream&) const = 0;
};

That's what virtual functions are for. ;-)

> > templat< > > struct SDataContainer : public SData> > {> > SDataContainer(const T& o) : m_data(o) >}
> T m_d>ta;
> };

You could override "print_on" here.>

> void print(SData* pD>ta)
> SDataCont<>ner* pi = dyna<ic_cast*>
> (p>ata);
> if (>i != 0)> > {
<< cout << "printi<< >nteger " <<<pi->m>data << endl;
> }
>
< >SDataContainer*<pb = dynamic_>a&g<;t<>DataContain>r*>
> (pDat>);
> > if (pb != 0)<<> {
> <<t;out << "printing bo>l " << (pb->m_dat<<?>"true" :
> "fa>se") << endl;
> return;
> }

BTW: Is there a reason why you put this function inside a class or is
this only a habit you imported from Java / C# for no reason?

You're right. That looks ugly. You could avoid this with a virtual
function like SData::print_on :

voi> print(SData* pData)
{ > pData->print_on(cout);
}


> Now, the question: is there a way >o avoid the dynamic_cast and deduce
> the template argument thank> to template method specialization.
> I would like>to>write something lik<>th>t: [...]
>
> template > > voi> print(SDataContainer<<<* pData)
> {
> > lt;<>ut><< "data type>no> han<led" << endl;
> }
>
> {
><< gt;cout << "printing integer " << pData->m_data << endl;
> }

(1) Specializing function templates is discour&g<;ged.
(2) Your syntax seems wrong: Shouldn't it be "print" ?
(3) There's a big difference between the static type and the
runtime (most derived) type. The first one is known at
compile-time the 2nd one can only be determined at
run-time (in general). So, the compiler isn't able to select
the function with the right runtime type at compile-time.

That's what virtual functions are for.


Cheers!
SG

 
 
 

r H2 deduce deduce template argument of a template class inheriting from a non template base?

Post by nguillo » Sun, 08 Mar 2009 17:28:09

Thanks for your answer.


Yes indeed, that's what I meant.

I don't think I want the virtual function you spoke about.
It's a good idea, but I really want to receive a SData* as a parameter
in a function, or a method class:
this function or method will display for instance a dataGrid with a
cell dedicated for int in case of SDataContainer<int>, or a cell
dedicated to sting in case of SDataContainer<string>... and so on.
And another object would print those data in a html file for
instance... So I can't use an overridden virtual method: the
SDataContainer doesn't know how to print itself.

However indeed it seems I've got a design problem.
Maybe I could use the class inheritance like that in order to avoid
the dynamic_cast in the displayer object.

SData
/\
|| (public inheritance)
SDataContainer<T>
/\
|| (private inheritance)
HtmlDataDisplay<T> or GridDataDisplay<T>

and in HtmlDataDisplay<T> or GridDataDisplay<T> I would override the
virtual "print_on" of SData base class. But I would specialize those
class for each type (int, bool...) needed to be handled.

You said:
But if I don't use template specialization, I'll have to store the
data type in a way like that:

struct SData
{
enum eType
{
tBool,
tInt,
...
};
};

And store the data as a string inside SData, or inherit several
objects from SData: SDataBool, SDataInt and so on.
Here the template design is clearly more suitable.
 
 
 

r H2 deduce deduce template argument of a template class inheriting from a non template base?

Post by SG » Sun, 08 Mar 2009 19:40:07

n 7 Mrz., 09:28, nguillot < XXXX@XXXXX.COM > wrote:

I think you do. Though, I'm not sure what you're really trying to do.


You have to think about what common operations an object of the type
SDate should support. This could be a conversion to a string, for
example. This conversion could be a virtual function that makes SData
an abstract base class.

struct SData
{
virtual ~SData() {}
virtual string to_string() const = 0;
};

> > \
> | (public inhe>itance)
> DataC>n<>iner >> \
> | (priva>e inheritance)
> <t;tmlDataDisplay r >ri&<t;Da>aDisplay
>
> and in &<t;tmlDataDisplay or &<t;ridDataDisplay I wou>d override the
> virtual "print_on" of SData base class. But I would s>ecialize those
> class for each type (int, bool...) needed to be handled.

I don't think that's a good idea. This can only work if you know in
advance what kind of "print_on" behaviour you want. Also, when you
have a pointer do an SData object you really don't know what it's
going to do when you call print_on. Does it print a grid or HTML code?
So, not only the actual type is abstracted but also how your data is
printed. Is this wh>t you want>?

> You said:> (1) Specializing function templates >s >iscouraged.
>
> But if I don't use template specialization, I'll ha>e to store the
> data type in a>wa> like that:
> >> st>uct SData
> { >> >num eType
> > {
> > tBool, >> gt;tInt,
> ...
> };
> };

If you use templates and/or overloading for this the compiler can only
select the function according to the STATIC type. This is often not
what you want. If you pass a pointer of type SData* around (static
type) you can't expect the compiler to macigally select a function
based on the derived runtime type. This is w>at virtual functions are
for!

> And store the data as a string>inside SData, or inherit several
> objects from SDat>: SDataBool, SDataInt and so on.
> Here the template design is clearly more suitable.

As a rule of thumb: If you feel the need for something like this:

enum eType
{
tBool,
tInt,
...
};

it's very likely that a design with virtual functions is the better
one. The big disadvantage of your approach is that you have to check
the value of your eType variable possibly in many many places of your
code. This is a maintenance nightmare.

If you want dynamic polymorphism you can work with virtual functions
and to some extend with the typeid operator, though don't overuse the
typeid operator. Try to avoid it. I only used it once and threw it
away after a redesign.

Cheers!
SG
 
 
 

r H2 deduce deduce template argument of a template class inheriting from a non template base?

Post by nguillo » Mon, 09 Mar 2009 02:20:32

OK, I agree with all.
At the beginning, my design was almost what you've described.
But because of my display objects (html, grid...) I went to the point
described in my initial question.
After your last answer, I saw the MSVC ostream implementation and
indeed there is no template specialisation, but seveal operator<< for
each type.
I thought using template specialisation to provide a default uotput if
the type is not handled (like in my first post).
I'll work again and see if I manage to do my stuff with inheritance.

Thanks for expertise.
 
 
 

r H2 deduce deduce template argument of a template class inheriting from a non template base?

Post by Ivan » Tue, 10 Mar 2009 02:56:35


Your second program is correct I think, expect for:

SData* pDataInt = new SDataContainer<int>(5);
SData* pDataBool = new SDataContainer<bool>(false);
SData* pDataDouble = new SDataContainer<double>(2);
SPrintDataContainer DataPrinter;

Because these pDataInt(Bool, Boudle) are type of SDtata*, upcase to
base type.
So if compile meet:
DataPrinter2.print(pDataInt);

It will said an error existing, for that there is not ant print
function with parameter as SDtata* type.

change

to
DataPrinter2.print(new SDataContainer<int>(5));
DataPrinter2.print(new SDataContainer<bool>(true));
DataPrinter2.print(new SDataContainer<double>(1.0));

Should be OK. Since new SDataContainer<int> can be deduced its type as
int.etc..

Ivan--