LinuxQuestions.org
Register a domain and help support LQ
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices

Reply
 
Search this Thread
Old 09-17-2004, 02:05 PM   #1
deiussum
Member
 
Registered: Aug 2003
Location: Santa Clara, CA
Distribution: Slackware
Posts: 895

Rep: Reputation: 31
C++ Template question


I had a question about templates in C++ that I'm hoping someone might be able to help with. I've programmed with C++ for about 15 years or so now, but I have never delved too deeply into the world of template classes.

Right now, I'm considering ways to have a std::list or std::vector object that can contain multiple types. Generally, I've done this by using a class heirarchy with a base class and having the list store pointers to the base class. I started thinking that maybe there is someway to do something like this with templates, though.

Right now, I'm running into a couple of situations where it would be really nice to be able to store a list of ints and strings in the same list. I've run into 2 situations where I thought this might be useful. One such situation involves storing field values returned from a database query, where the types of each field could be any number of different types. Another situation is to store a list of parameter values to pass to a function that I wrote to make a SOAP call...

My initial thought was to try something as follows, which doesn't work:

Code:
#include <iostream>
#include <list>

using namespace std;

template<typename T>
class DataColumn
{
    public:
        DataColumn(const T &value) : m_value(value) {}
        T GetValue() const { return m_value; }
    private:
        T m_value;
};
    

int main()
{
    // This line obviously doesn't work because DataColumn<T> doesn't
    // create a template specialization, because obviously T isn't a concrete type...
    list<DataColumn<T> > oList;

    return 0;
}
Then I started to think that maybe I could create a base class, and derive the template class from that. But then, that doesn't seem to gain me that much over my usual base object hierarchy type of thing. Not only that, but there is no way I know of to have some sort of virtual function in the base class that returns the value for whatever type the template class's value member ends up being.

I'm starting to think that maybe there isn't really a good way to do what I want with just templates, but thought I'd pose the question here before I give up for now and just go back to my normal ways.
 
Old 09-17-2004, 07:54 PM   #2
kamransoomro84
Member
 
Registered: Feb 2004
Location: Pakistan
Distribution: OpenSUSE 10.2
Posts: 241

Rep: Reputation: 30
Ok. Interesting question. Now, I have never tried what I am about to tell you, so please excuse me if it doesn't work. You're right, the problem is that in the main function, T does not exist as a typename.

However, why don't you create a structure that stores a void* and an int that describes what type of class the pointer points to.

This way you can simply check the value of the int and cast the pointer to that class type. You would still be using a template class for the data column since it's more efficient than having seperate classes for every data type.

Code:
include <iostream>
#include <list>

using namespace std;

template<typename T>
class DataColumn
{
    public:
        DataColumn(const T &value) : m_value(value) {}
        T GetValue() const { return m_value; }
        int GetType() { /* Check type and return code */ }
    private:
        T m_value;
};

struct ClassInfo
{
  void *pDC;
  int type;
};  
    

int main()
{
   list<ClassInfo*> oList;

    return 0;
}
Now, when you store the pointer to the class in the structure, you can check the type of the T variable with typid operator and determine what to store in the int. Simiilarly you can check the int later and determine what type of DataColumn to cast the void* to, like DataColumn<int>*, DataColumn<long>*, etc.

Please let me know if this helps. I'm really curious.

Last edited by kamransoomro84; 09-17-2004 at 07:56 PM.
 
Old 09-17-2004, 08:21 PM   #3
redjokerx
Member
 
Registered: Aug 2004
Location: San Diego
Distribution: Slackware
Posts: 303

Rep: Reputation: 31
I've only used templated classes, never made one myself. From my experience, it's
Code:
templatedClass<int> tCi;  
templatedClass<double> tCd;
templatedClass<otherClass> tCc;
if you want, you can overload [] and then you can access the elements by tCi[x]; or something. I don't know what you should do though if a certain type you want to use needed some special functions.

Last edited by redjokerx; 09-17-2004 at 08:23 PM.
 
Old 09-18-2004, 10:42 AM   #4
deiussum
Member
 
Registered: Aug 2003
Location: Santa Clara, CA
Distribution: Slackware
Posts: 895

Original Poster
Rep: Reputation: 31
Thanks for the suggestion, kamransoomro84. That's similar to what I've usually done for keeping lists of different types. I was also thinking something like the following, which is kind of like what you suggested.

Code:
#include <iostream>
#include <list>
#include <string>

using namespace std;

class DataColumnBase
{
    public:
        DataColumnBase() {}
};

template<typename T>
class DataColumn : public DataColumnBase
{
    public:
        DataColumn(const T &value) : m_value(value) {}
        T GetValue() const { return m_value; }
    private:
        T m_value;
};

int main()
{
    list<DataColumnBase*> oList;
    list<DataColumnBase*>::iterator itr;

    oList.push_back(new DataColumn<int>(10));
    oList.push_back(new DataColumn<string>("Hello"));

    for(itr=oList.begin();itr!=oList.end();itr++)
    {
        // Now you can cast (*itr) to the appropriate type...
    }

    return 0;
}
Ultimately, what I was thinking is that it would be nice to be able to use the above format for adding items to the list. It would be ideal to be able to then use what's stored in the list w/o having to know it's underlying type, but I guess you can't always have everything you want.

I like the typeid idea, though. It could probably be implemented as a GetType method in my base class, and overridden in my template type. It might be a bit cleaner solution than my usual inheritence methods...

Last edited by deiussum; 09-18-2004 at 10:47 AM.
 
Old 09-18-2004, 03:04 PM   #5
kamransoomro84
Member
 
Registered: Feb 2004
Location: Pakistan
Distribution: OpenSUSE 10.2
Posts: 241

Rep: Reputation: 30
I pretty much figured out what you were doing from your earlier post. Anyway, I still think it's easier to impement it as a template class since it saves a lot of coding time. Oh what the heck, it's your choice. Whatever works, right .
 
Old 09-18-2004, 05:18 PM   #6
deiussum
Member
 
Registered: Aug 2003
Location: Santa Clara, CA
Distribution: Slackware
Posts: 895

Original Poster
Rep: Reputation: 31
Yeah, using the template class would make it so that you wouldn't need to code as many classes. Since what I have so far requires actually knowing what type of template specialization you have to do the casting right, there will be a bit more code for that part, though.

I ran into one other slight problem with trying to use typeid to get the type_info passed to the template. I tried to do the following with the classes:

Code:
class DataColumnBase
{
    public:
        DataColumnBase() {}
        virtual type_info  GetTypeID() = 0;
};

template<typename T>
class DataColumn : public DataColumnBase
{
    public:
        DataColumn(const T &value) : m_value(value) {}
        T GetValue() const { return m_value; }
        type_info GetTypeID() { return typeid(T); }
    private:
        T m_value;
};
The only problem with that is that it won't compile because the constructor for the std::type_info is private, so it can't create an instance of that class as a return value.

So... now my test program looks like so:

Code:
#include <iostream>
#include <list>
#include <string>

using namespace std;

class DataColumnBase
{
    public:
        DataColumnBase() {}

        // Doesn't work, std::type_info::type_info() is private
        //virtual type_info  GetTypeID() = 0;

        // NOTE: Need at least 1 virtual function for RTTI 
        // to be generated.
        virtual void Blah() {}
};

template<typename T>
class DataColumn : public DataColumnBase
{
    public:
        DataColumn(const T &value) : m_value(value) {}
        T GetValue() const { return m_value; }
        //type_info GetTypeID() { return typeid(T); }
    private:
        T m_value;
};

int main()
{
    list<DataColumnBase*> oList;
    list<DataColumnBase*>::iterator itr;

    oList.push_back(new DataColumn<int>(10));
    oList.push_back(new DataColumn<string>("Hello"));

    for(itr=oList.begin();itr!=oList.end();itr++)
    {
        if (typeid(**itr) == typeid(DataColumn<int>))
        {
            cout << (dynamic_cast<DataColumn<int>*>(*itr))->GetValue() << endl;
        }
        else if (typeid(**itr) == typeid(DataColumn<string>))
        {
            cout << (dynamic_cast<DataColumn<string>*>(*itr))->GetValue() << endl;
        }
        else
        {
            cout << "Unknown type: " << typeid(*itr).name() << endl;
        }

        delete *itr;
    }

    return 0;
}
The casting is a bit ugly in the for loop, and it only handles the 2 specializations I have defined so far, but adding another specialization would only require another if else in there...

I might be able to clean that up a bit by doing the typeid check in the constructor of the template class and having my GetType function return an enum of supported types, though... Actually, now that I think about that, it might work pretty well. I could use it a switch/case statement in my for loop then...

Ok, enough thinking out loud, I'm gonna go mess with that a bit more now.

Last edited by deiussum; 09-18-2004 at 05:25 PM.
 
Old 09-21-2004, 10:29 PM   #7
kamransoomro84
Member
 
Registered: Feb 2004
Location: Pakistan
Distribution: OpenSUSE 10.2
Posts: 241

Rep: Reputation: 30
You know, while thinking up your suggestion, I faced the same problem . I came up with two solutions for it. One is yours, to check the typeid in the GetType function and return the code straight away. Second is, you can declare your function like this,

const typeinfo & GetTypeID() { return typied(T); }

Get my drift? by returning by reference, your not creating a copy and thus you don't need the constructor outside of the class.
 
Old 09-22-2004, 04:24 AM   #8
dakensta
Member
 
Registered: Jun 2003
Location: SEUK
Distribution: Debian & OS X
Posts: 194

Rep: Reputation: 35
Why not use something that already exists?

http://www.boost.org/doc/html/any.html

The link at the bottom of that page, "Valued Conversions", a pdf file, gives some well annotated source code and decent discussion of the issues you are currently working on and the boost::any source code is only a hundred or so lines ( http://www.boost.org/boost/any.hpp ) and pretty easy to understand, in this case at least
 
Old 09-22-2004, 08:23 AM   #9
deiussum
Member
 
Registered: Aug 2003
Location: Santa Clara, CA
Distribution: Slackware
Posts: 895

Original Poster
Rep: Reputation: 31
kamransoomro84, I had thought briefly about returning the type_info by reference, but by doing that you are returning a reference to a temporary variable, which seems kind of dangerous to me. A quick test shows that it works w/o any segmentation faults, but still...

dakensta, thanks for the info. I'll take a look. I've got what I needed working reasonably well, but looking at how they do it might give me some additional ideas.
 
Old 09-22-2004, 09:15 AM   #10
deiussum
Member
 
Registered: Aug 2003
Location: Santa Clara, CA
Distribution: Slackware
Posts: 895

Original Poster
Rep: Reputation: 31
I browsed through the boost::any code a bit, and noticed that it is surprisingly similar to the design I came up with, only with an additional layer to hide the underlying base and template classes. They also use the trick of returning a const reference to a temporary, so maybe that's not as bad as I would think it is...
 
Old 09-24-2004, 05:53 AM   #11
kamransoomro84
Member
 
Registered: Feb 2004
Location: Pakistan
Distribution: OpenSUSE 10.2
Posts: 241

Rep: Reputation: 30
Actually, it's not temporary as you think. If you look at the documentation for the typeid operator, you'll see that it returns reference to a typeinfo object. This implies that the object is actually created somewhere else, possibly even when the type of class that it refers to is created. Thus, when you return a reference to the typeinfo, you are merely forwarding it to the point where you're function is being called. Thus, the typeinfo object is not a temporary variable within the function at all. Tell me if this helps.

P.S. I'll definitely check out that link. Thanks.
 
Old 09-24-2004, 08:26 AM   #12
deiussum
Member
 
Registered: Aug 2003
Location: Santa Clara, CA
Distribution: Slackware
Posts: 895

Original Poster
Rep: Reputation: 31
Ahhh, I totally overlooked that typeid itself returns a reference. It all makes sense now.
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Daemon Template thekonqueror Programming 0 08-15-2005 02:07 PM
Template class with a template member... Nicholas Bishop Programming 3 02-21-2005 08:27 PM
user template bogus__13 Linux - General 5 01-21-2005 01:25 AM
template class ckcheung0927 Programming 3 11-28-2004 03:59 PM
c++ template help ashirazi Programming 4 11-16-2004 08:52 AM


All times are GMT -5. The time now is 04:58 AM.

Main Menu
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration