r H2 deduce deduce template argument of a template class inheritingfrom a non template base?

N

nguillot

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.boost.org/doc/libs/1_38_0/libs/ptr_container/test/tree_test.cpp
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.
 
S

SG

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

struct SData
{
    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::eek:stream&) const = 0;
};

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

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

You could override "print_on" here.

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;
    }

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 :

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

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: [...]

    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;
    }

(1) Specializing function templates is discouraged.
(2) Your syntax seems wrong: Shouldn't it be "print<int>" ?
(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
 
N

nguillot

Thanks for your answer.
(2) Your syntax seems wrong: Shouldn't it be "print<int>" ?

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:
(1) Specializing function templates is discouraged.
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.
 
S

SG

I don't think I want the virtual function you spoke about.

I think you do. Though, I'm not sure what you're really trying to do.
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.

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;
};
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.

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 what you want??
You said:> (1) Specializing function templates is discouraged.

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,
        ...
    };
};

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 what virtual functions are
for!
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.

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
 
N

nguillot

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.
 
I

Ivan

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);

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*$B!$(B 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
DataPrinter2.print(pDataInt);
DataPrinter2.print(pDataBool);
DataPrinter2.print(pDataDouble);

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--
 

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,766
Messages
2,569,569
Members
45,042
Latest member
icassiem

Latest Threads

Top