Thread: How to use templates on this.

  1. #1
    Registered User
    Join Date
    Dec 2008
    Location
    California
    Posts
    37

    How to use templates on this.

    I have a handler class that handles all the class data (ie. Cars, Motors, Planes)
    But I want to have a getData function which has a parameter that can identify which class to return.

    Code:
    class Handler
    {
    public:
    Car *cars;
    Motor *motors;
    Plane *planes
    
    Handler();
    
    void getData(int index) // When index = 0; cars. 1 will be motors.
    {
    
    if(index == 0)
    return cars;
    else if(index ==1)
    return motors;
    
    };
    So when I try to get the cars. I'll just call.

    Handler->getData(0)->ViewData();

    My problem is, how do I do that? I heard templates can do that but I don't know how to use it.
    Test

  2. #2
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Remember that templates are compile time polymorphic, so the type of the object must be know at compile time for a template to be used. In this case, that means the index must be known compile time.

    So basically what you are asking for is a way to map integers to a class type.

    Here's one solution involving a macro. You'd need to adapt it to work with the class, but this outlines the principle:
    Code:
    #include<iostream>
    
    int getData(int arr[][1]){
        return 0;
    }
    
    double getData(int arr[][2]){
        return 2.5;
    }
    
    #define getDataM(N)  getData((int (*)[N])NULL)
    
    int main(){
        std::cout << getDataM(1)  << std::endl;
        std::cout << getDataM(2 ) << std::endl;
        return 0;
    }
    I think it might also be possible to do something with class template specialization, but this came to me first.

    Is this the kind of thing you're looking for? Cause if not -- if index is not known at compile time, then you need to rely on conventional polymorphism, not templates or macros.
    Last edited by King Mir; 08-19-2009 at 07:04 AM. Reason: changed to std:cout
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  3. #3
    The larch
    Join Date
    May 2006
    Posts
    3,573
    I think it might also be possible to do something with class template specialization, but this came to me first.
    With C++0x you could replace the macro with:

    Code:
    template <int N>
    decltype(getData((int (*)[N])NULL)) getDataM()
    {
        return getData((int (*)[N])NULL);
    }
    
    int main(){
        printf("%i\n", (int) getDataM<1>() );
        printf("%f\n", (double) getDataM<2>() );
        return 0;
    }
    A solution within current standard would be interesting to see
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  4. #4
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    With some thoughtI came up with this:

    Code:
    #include<iostream>
    
    template <int I>
    class Mclass{};
    
    class Handler
    {
      public:
      
      double getData(Mclass<0>){
        return 2.5;
      }
      int getData(Mclass<1>){
        return 0;
      }
    };
    int main(){
        Handler obj;
        std::cout << (  obj.getData( Mclass<0>() )  ) << std::endl;
        std::cout << (  obj.getData( Mclass<1>() )  ) << std::endl;
    
        return 0;
    }
    All in all this is better then the first solution. It doesn't use a macro, and the number does not have to be a positive integer.
    Last edited by King Mir; 08-19-2009 at 07:47 AM.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  5. #5
    The larch
    Join Date
    May 2006
    Posts
    3,573
    Very nice. I got stuck with your previous idea.

    P.S It seems that you don't need the specialization?
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  6. #6
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Good catch on not needing the specialization. Editing now to avoid confusion.

    Really both options are the same thing: use a type has an integer parameter as an extra argument to overload on.
    Last edited by King Mir; 08-19-2009 at 07:50 AM.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  7. #7
    Registered User
    Join Date
    Apr 2008
    Posts
    890
    Why not just use polymorphism?

    (Uncompiled code follows)

    Code:
    class Viewable {
    public:
        virtual void viewData() = 0;
    };
    
    class Car : public Viewable {
    public:
        virtual void viewData() { // implement view data for Car }
    };
    
    ...
    
    class Handler {
    public:
        void viewer(const Viewable & v)
        {
            v.viewData();
        }
    };
    Last edited by medievalelks; 08-19-2009 at 08:32 AM.

  8. #8
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Because that involves runtime overhead. Don't do at runtime what you can at compile time.

    But for the OP, chances are that is what he's looking for.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  9. #9
    Registered User
    Join Date
    Apr 2008
    Posts
    890
    Quote Originally Posted by King Mir View Post
    Because that involves runtime overhead.
    I prefer the cleaner design over a perceived performance gain.

  10. #10
    Deprecated Dae's Avatar
    Join Date
    Oct 2004
    Location
    Canada
    Posts
    1,034
    Quote Originally Posted by medievalelks View Post
    I prefer the cleaner design over a perceived performance gain.
    I agree, but the performance gain is not just perceived.

    Elaborating on King Mir's example, if you wanted it to be legible, you would typedef the different choices, so it's cleaner. However, in that case you would just end up with this example.

    Code:
    #include <iostream>
    #include <string>
    
    class Car
    {
    public:
    	void ViewData()
    	{
    		std::cout << "<Car>" << std::endl;
    	}
    };
    
    class Motor {};
    class Plane {};
    
    class Handler
    {
    	Car car;
    	Motor motor;
    	Plane plane;
    
    public:
    	struct Type
    	{
            template<int I>
            class Mclass {};
    
    		typedef Mclass<0> car_type; //or u can use: class car_type {}
    		typedef Mclass<1> motor_type; //or u can use: class motor_type {}
    		typedef Mclass<2> plane_type; //or u can use: class plane_type {}
    
    		static const car_type Car;
    		static const motor_type Motor;
    		static const plane_type Plane;
    	};
    
    	Car& GetData(Type::car_type const& t)
    	{
    		return car;
    	}
    
    	Motor& GetData(Type::motor_type const& t)
    	{
    		return motor;
    	}
    
    	Plane& GetData(Type::plane_type const& t)
    	{
    		return plane;
    	}
    };
    
    const Handler::Type::car_type Handler::Type::Car = Handler::Type::car_type();
    const Handler::Type::motor_type Handler::Type::Motor = Handler::Type::motor_type();
    const Handler::Type::plane_type Handler::Type::Plane = Handler::Type::plane_type();
    
    int main()
    {
    	Handler handler;
    
    	handler.GetData(Handler::Type::Car).ViewData(); //output: <Car>
    }
    If you're going to do something illegible, or verbose, such as these examples, why don't you just do handler.GetCar().ViewData()? If you only have 1 instance of each class, as your first code shows, then the best solution would be to give them individual functions. If however you have multiple instances, then you would need an index based approach such as King Mir's, or medievalelks with an array:

    Code:
    #include <iostream>
    #include <string>
    #include <vector>
    
    class Viewable 
    {
    public:
    	virtual void ViewData() = 0;
    };
    
    class Car : public Viewable
    {
    public:
    	virtual void ViewData()
    	{
    		std::cout << "<Car>" << std::endl;
    	}
    };
    
    class Motor : public Viewable
    {
    public:
    	virtual void ViewData()
    	{
    		std::cout << "<Motor>" << std::endl;
    	}
    };
    
    class Plane : public Viewable
    {
    public:
    	virtual void ViewData()
    	{
    		std::cout << "<Plane>" << std::endl;
    	}
    };
    
    class Handler
    {
    	std::vector<Viewable*> data;
    
    public:
    	Viewable* GetData(int i)
    	{
    		return data[i];
    	}
    
    	void Add(Viewable* v)
    	{
    		data.push_back(v);
    	}
    
    	Viewable* operator[](int i)
    	{
    		return GetData(i);
    	}
    };
    
    
    int main()
    {
    	Handler* handler = new Handler;
    
    	handler->Add(new Car);
    	handler->Add(new Motor);
    	handler->Add(new Plane);
    
    	handler->GetData(0)->ViewData(); //same as handler[0]->ViewData(); with non-pointer
    }
    Personally if I were handling multiple instances of multiple unknown classes, I would do something along the lines of this...

    Code:
    #include <iostream>
    #include <string>
    #include <vector>
    #include <boost/any.hpp>
    
    class Car
    {
    public:
    	void ViewData()
    	{
    		std::cout << "<Car>" << std::endl;
    	}
    };
    
    class Motor {};
    
    class Plane {};
    
    class Handler
    {
    	std::vector<boost::any> data;
    
    public:
    	template<typename T>
    	T GetData(int i)
    	{
    		return boost::any_cast<T>(data[i]);
    	}
    
    	template<typename T>
    	void Add(T v)
    	{
    		data.push_back(boost::any(v));
    	}
    };
    
    
    int main()
    {
    	Handler handler;
    
    	handler.Add(Car());
    	handler.Add(Motor());
    	handler.Add(Plane());
    
    	handler.GetData<Car>(0).ViewData();
    }
    Unfortunately you have to cast the type, but C/C++ don't yet have a way of determining the type without help from the user (too bad we couldn't have an array of typedef's or something). Maybe C++0x, I don't know.

    That just looks like a generic array now though. What the heck is the purpose? Game asset handler? If that's the case, you might want to implement Provider's, and instead of using int's (0, 1, 2, etc) to access them, give them string names (use an unordered_map). ie. handler.GetProvider<Car>().GetAsset("ferrari") or handler.GetAsset<Car>("ferrari") or handler.GetProvider("car").GetAsset("ferrari"), etc. There's more about that on gamedev.
    Warning: Have doubt in anything I post.

    GCC 4.5, Boost 1.40, Code::Blocks 8.02, Ubuntu 9.10 010001000110000101100101

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Templates from DLL or static library problem
    By mikahell in forum C++ Programming
    Replies: 2
    Last Post: 01-01-2008, 01:49 AM
  2. Questions about Templates
    By Shamino in forum C++ Programming
    Replies: 4
    Last Post: 12-18-2005, 12:22 AM
  3. templates and inheritance problem
    By kuhnmi in forum C++ Programming
    Replies: 4
    Last Post: 06-14-2004, 02:46 AM
  4. When and when not to use templates
    By *ClownPimp* in forum C++ Programming
    Replies: 7
    Last Post: 07-20-2003, 09:36 AM