Thread: Templates that only accept certain types and class having a template as friend

  1. #1
    Registered User
    Join Date
    Mar 2002
    Posts
    125

    Templates that only accept certain types and class having a template as friend

    I have two questions:
    First, is it possible to make a template that only takes classes that inherited from a certain base class as it's template parameter? Particularly, I want to make a sort of smart pointer class template (a pointer that detects whether the thing it points to was deleted by something else) that can point to an object of class GameObject or a class that inherits from GameObject. The reason I don't want it to point to anything else is because some of the methods required to make the smart pointer tick depend on a member function GameObject. The reason I want to use a template is because I do not want to downcast the inherited class, then upcast it later (which would require dynamic casting, checking whether it's of the right class, etc) so I specifically want a class that keeps a pointer of type pointer-to-derived-object. If this is possible, how do I do it?

    Second, is it possible to have all classes spawned from a template as friend? Say a member function of the template needs to access a protected member of another class; how would I go about this?

    Any suggestions to getting this to work in an easier way not involving templates of pointer casts would be very welcome as well!

    Example header code I've already written:

    Code:
    class GameObject
    {
    	public:
    		unsigned int GetObjectNum();
    		virtual void Act() = 0;
    
    	// irrelevant stuff omitted
    };
    
    class FooBarrel : public GameObject
    {
    	public:
    		virtual void Act();
    		int FooLevel();
    	// stuff omitted
    };
    
    class ObjectList
    {
    	// stuff omitted
    	public:
    		friend ObjectPointer; // Question 2: does this work?
    	protected:
    		bool ObjectIsValid(unsigned int);
    	
    };
    
    extern ObjectList *grand_list;
    
    // Question 1: how to limit T to classes derived from GameObject, like Foobarrel?
    template <class T> 
    class ObjectPointer
    {
    	public:
    		bool IsValid() 
    		{
    			// This requires ObjectPointer<T> to be a friend of ObjectList
    			if (grand_list->ObjectIsvalid(objectnum) ) 
    				return true;
    			return false; 
    		}
    		T *operator->() 
    		{ 
    			return pointed_to; 
    		}
    		ObjectPointer operator=(T *in) 
    		{ 
    			pointed_to = in; 
    			objectnum = in->GetObjectNum(); // this requires T to be derived from GameObject
    			return *this;
    		}
    	protected:
    		T *pointed_to;
    		unsigned int objectnum;
    };
    
    ObjectPointer<FooBarrel> barrel_of_foo;
    Somewhere in a cpp file I'd call
    if (barrel_of_foo.IsValid)
    x=barrel_of_foo->FooLevel();

    I've searched several C++ tutorials and the archives of these forums for answers but couldn't find anything.
    Thanks in advance!
    Typing stuff in Code::Blocks 8.02, compiling stuff with MinGW 3.4.5.

  2. #2
    Code Goddess Prelude's Avatar
    Join Date
    Sep 2001
    Posts
    9,897
    >First, is it possible to make a template that only takes classes that inherited from a certain
    >base class as it's template parameter?
    No. Well, yes and no. You can use policies toward that effect, but templates don't support a constraint mechanism, so there's a certain amount of work required on the user code's part as well.

    >Second, is it possible to have all classes spawned from a template as friend?
    Ehh, going on your example, I would say yes. But you need to specify that ObjectPointer is a template class:
    Code:
    class ObjectList {
    public:
      template <class T>
      friend class ObjectPointer;
    protected:
      bool ObjectIsValid(unsigned int);
    };
    My best code is written with the delete key.

  3. #3
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    In answer to the first question, yes it is possible. Use inheritence traits. Have a look here for discussion by Herb Sutter.

  4. #4
    semi-colon generator ChaosEngine's Avatar
    Join Date
    Sep 2005
    Location
    Chch, NZ
    Posts
    597
    not only is it possible it's pretty much done for you with boost type traits.

    Code:
    #include <boost/type_traits/is_base_of.hpp> 
    
    class GameObject
    {
    };
    
    template <class T>
    class GOPointer
    {
    public:
    	~GOPointer()
    	{
    		// NOTE: the double brackets!! stops the macro interpreting the comma as
    		// 2 macro args
    		BOOST_STATIC_ASSERT((boost::is_base_of<GameObject, T>::value));
    	}
    };
    
    class Rock : public GameObject{};
    class Gun : public GameObject{};
    class Thing {};
    
    int main(int argc, char* argv[])
    {
    	// all good
    	GOPointer<Rock> pR; 
    	GOPointer<Gun> pG;
    
    	// compile error!!
    	GOPointer<Thing> pT;
    	return 0;
    }
    easy as...

    >Second, is it possible to have all classes spawned from a template as friend?
    not really sure what you mean by "spawned from a template"? do you mean instantiated? please show an example of what you want to do.
    Last edited by ChaosEngine; 07-22-2006 at 11:24 PM.
    "I saw a sign that said 'Drink Canada Dry', so I started"
    -- Brendan Behan

    Free Compiler: Visual C++ 2005 Express
    If you program in C++, you need Boost. You should also know how to use the Standard Library (STL). Want to make games? After reading this, I don't like WxWidgets anymore. Want to add some scripting to your App?

  5. #5
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    If I remember correctly, having a template (and thus all of its instantiations) as a friend is currently, for some unknown reason, forbidden by the standard.
    The C++0x standard will most likely remedy that, and the feature might also exist as an extension for various compilers.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  6. #6
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Quote Originally Posted by CornedBee
    If I remember correctly, having a template (and thus all of its instantiations) as a friend is currently, for some unknown reason, forbidden by the standard.
    AFAIK, this statement is incorrect and the following is legal;
    Code:
    template<class T> Foo
    {
         // whatever
    };
    
    class X
    {
         template<class T> friend class Foo<T>;
          // whatever
    };
    as this effectively declares all specialisations of Foo to be a friend of X.

    If you mean that this is illegal;
    Code:
    template<class T> Foo
    {
         // whatever
    };
    
    class X
    {
         friend class Foo;
          // whatever
    };
    then you are correct, although that's a side-effect of (IIRC) the friend declaration violating the ODR.

  7. #7
    Code Goddess Prelude's Avatar
    Join Date
    Sep 2001
    Posts
    9,897
    >If I remember correctly, having a template (and thus all of its instantiations) as a friend is
    >currently, for some unknown reason, forbidden by the standard.
    Section 14.5.4 of C++98 seems to disagree.
    My best code is written with the delete key.

  8. #8
    Registered User
    Join Date
    Mar 2002
    Posts
    125
    Thanks a lot for the insight guys!
    After reading through the GotW page grumpy posted, it seems to me the easiest way (without including an extra header) to make sure a template parameter is derived from another class would be
    Code:
    template <class T>
    class RequiresBaseObject
    {
    		bool CheckClass(BaseObject *) { return true; };
    		bool CheckClass( ... ) { BaseObject *a = static_cast<T*>(0); }; // error!
    	public:
    		~RequiresBaseObject()
    		{
    			assert ( CheckClass(static_cast<T*>(0)) );
    		}
    };
    So if T* can be converted implicitly to a BaseObject*, there's no problem, but if not, the compiler will give an error because I try to do so anyway in the generic CheckClass function (which gives a "Cannot convert X* to BaseObject*"), along with the line where I tried to instantiate the bad class.
    Typing stuff in Code::Blocks 8.02, compiling stuff with MinGW 3.4.5.

  9. #9
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Quote Originally Posted by Prelude
    >If I remember correctly, having a template (and thus all of its instantiations) as a friend is
    >currently, for some unknown reason, forbidden by the standard.
    Section 14.5.4 of C++98 seems to disagree.
    Indeed. I would have to search the boost mailing list for the particular issue I'm looking for. Might be one of the more specific cases, such as being unable to friend a partial specialization, or to define a friend template function inline.

    Quote Originally Posted by Boksha
    Thanks a lot for the insight guys!
    After reading through the GotW page grumpy posted, it seems to me the easiest way (without including an extra header) to make sure a template parameter is derived from another class would be
    Code:
    template <class T>
    class RequiresBaseObject
    {
    		bool CheckClass(BaseObject *) { return true; };
    		bool CheckClass( ... ) { BaseObject *a = static_cast<T*>(0); }; // error!
    	public:
    		~RequiresBaseObject()
    		{
    			assert ( CheckClass(static_cast<T*>(0)) );
    		}
    };
    So if T* can be converted implicitly to a BaseObject*, there's no problem, but if not, the compiler will give an error because I try to do so anyway in the generic CheckClass function (which gives a "Cannot convert X* to BaseObject*"), along with the line where I tried to instantiate the bad class.
    Yes, but you should add a return false to the failing implementation. Different compiler have different strategies, but a compiler might partially check the CheckClass(...) function and realize it's not returning a value, and give an error even if you never instantiate it.
    Last edited by CornedBee; 07-24-2006 at 08:16 AM.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Questions about Templates
    By Shamino in forum C++ Programming
    Replies: 4
    Last Post: 12-18-2005, 12:22 AM
  2. Dikumud
    By maxorator in forum C++ Programming
    Replies: 1
    Last Post: 10-01-2005, 06:39 AM
  3. Prime Number Generator... Help !?!!
    By Halo in forum C++ Programming
    Replies: 9
    Last Post: 10-20-2003, 07:26 PM
  4. Linked list with two class types within template.
    By SilasP in forum C++ Programming
    Replies: 3
    Last Post: 02-09-2002, 06:13 AM