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

Discussion in 'C++' started by nguillot, Mar 6, 2009.

  1. nguillot

    nguillot Guest

    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.
     
    nguillot, Mar 6, 2009
    #1
    1. Advertising

  2. nguillot

    SG Guest

    Re: r H2 deduce deduce template argument of a template classinheriting from a non template base?

    On 6 Mrz., 21:11, nguillot <> wrote:
    > 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
     
    SG, Mar 6, 2009
    #2
    1. Advertising

  3. nguillot

    nguillot Guest

    Re: r H2 deduce deduce template argument of a template classinheriting from a non template base?

    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.
     
    nguillot, Mar 7, 2009
    #3
  4. nguillot

    SG Guest

    Re: r H2 deduce deduce template argument of a template classinheriting from a non template base?

    On 7 Mrz., 09:28, nguillot <> wrote:
    > 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
     
    SG, Mar 7, 2009
    #4
  5. nguillot

    nguillot Guest

    Re: r H2 deduce deduce template argument of a template classinheriting from a non template base?

    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.
     
    nguillot, Mar 7, 2009
    #5
  6. nguillot

    Ivan Guest

    Re: r H2 deduce deduce template argument of a template classinheriting from a non template base?


    > 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--
     
    Ivan, Mar 8, 2009
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    7
    Views:
    559
  2. George2

    template function argument deduce

    George2, Mar 11, 2008, in forum: C Programming
    Replies:
    0
    Views:
    405
    George2
    Mar 11, 2008
  3. Ed
    Replies:
    1
    Views:
    408
    James Kanze
    Aug 14, 2008
  4. Hicham Mouline
    Replies:
    1
    Views:
    625
    Victor Bazarov
    Apr 20, 2009
  5. Thomas
    Replies:
    4
    Views:
    189
    Thomas
    Jun 7, 2005
Loading...

Share This Page