Jump to content


Template Fun


  • You cannot reply to this topic
11 replies to this topic

#1 TheNut

    Senior Member

  • Moderators
  • 1473 posts
  • LocationThornhill, ON

Posted 20 February 2007 - 02:22 AM

I'm trying to add custom user variable support into my signal/slot system to simplify the process where I want to extend an already built-in signal with an extra user variable. Everything works fine except when I'm using references as a template parameter. Allow me to show you an example:


template<class param1>

class A

{

     param1 mParam1;  // Store User Variable


     A ()

     {

     }

};


int main (int argc, char **argv)

{

     A<ClassB &> a;     // compile error: mParam1 must be defined

     A<ClassB *> a;     // Ok

     A<ClassB> a;       // Ok


     return 0;

}


Of course the quick fix is to just use pointers and not references, but I'd like to know if there's any trick I could do to support both types without having to write two separate classes. Any thoughts?
http://www.nutty.ca - Being a nut has its advantages.

#2 eddie

    Senior Member

  • Members
  • PipPipPipPip
  • 751 posts

Posted 20 February 2007 - 02:29 AM

class ClassB {};

template<class param1>
class A
{
public:
     param1 mParam1;  // Store User Variable

     A (param1 p)
     : mParam1(p)
     {
     }
};

int main (int argc, char **argv)
{
    ClassB classb;

    A<ClassB &> a(classb);
    A<ClassB *> b(&classb);
    A<ClassB> c(classb);

    return 0;
}


The problem is you need to assign to the reference, and you can only do that once: at construction time. So pass it in as a variable, and things should work.

#3 TheNut

    Senior Member

  • Moderators
  • 1473 posts
  • LocationThornhill, ON

Posted 20 February 2007 - 03:13 AM

Looking back, perhaps that example was a little to simple =) Allow me to complicate it further :lol:

This is an excerpt from my slot system. It supports up to 5 customizable signal/slot systems without having to write 5 separate classes. I'm adding in user variables by trying to reuse one of the extra template types. EventParam::null is just an empty class so that you don't have to declare all 5 template types if you don't want to.

template <class Class, class param1 = EventParam::null,
				class param2 = EventParam::null,
				class param3 = EventParam::null,
				class param4 = EventParam::null,
				class param5 = EventParam::null>
class CSlot : public CEvent<param1, param2, param3, param4, param5>
/*
	Typedefs
*/
typedef void (Class::*Method)  ();
typedef void (Class::*Method1) (param1);
...

/*
	Aliases
*/
Class		*mClass;
Method		mMethod;
Method1		mMethod1;
...

/*
	User Variables
*/
bool		mUser;
param1		mParam1;
param2		mParam2;
...

/*
	Attach
		@desc: To assign the slot to a method pointer
*/
void attach (Class *classPtr, Method method)  { mClass = classPtr; mMethod  = method; }
void attach (Class *classPtr, Method1 method) { mClass = classPtr; mMethod1 = method; }
...

/*
	Attach + User Variable
		@desc: To assign the slot to a method pointer with a defined user variable
*/
void attach (Class *classPtr, Method1 method, param1 user) { mClass = classPtr; mMethod1 = method; mParam1 = user; mUser = true; }
void attach (Class *classPtr, Method2 method, param2 user) { mClass = classPtr; mMethod2 = method; mParam2 = user; mUser = true; }
...

In my engine, I have signals such as CSignal<CMouseEvent &> that would get called whenever a GUI object receives a mouse event. In my code, I need to have just one method, but maybe I would like to add a pointer to the object evoking the signal so I can perform some functionality.

void mySlot (CMouseEvent &, CGUIObject *objEvent)
{
	// Do something with objEvent
}

void main ()
{
	...
	CSlot<CMouseEvent &, CGUIObject *> slot;
	guiObject->OnMouseEvent.connect(slot, guiObject); // <- Special case, adding user variable
}

Because I'm using a reference to CMouseEvent as one of the types, the compiler will fail because mParam1 must be defined, but in this case mParam1 isn't going to be used as the user variable, mParam2 will. So unless there's a hack/trick to assign garbage to those mParam members to satisfy the compiler, I may need to write special classes to support user variable defined slots (which I'm reluctant to do) or I can remove any use of referencing in my engine to satisfy the compiler.
http://www.nutty.ca - Being a nut has its advantages.

#4 TheNut

    Senior Member

  • Moderators
  • 1473 posts
  • LocationThornhill, ON

Posted 20 February 2007 - 04:00 AM

Well, despite my impatience, I decided to go ahead and write the extra classes for this special support (where the parameter is defined in the constructor). It definitely bloated my code a bit (the word reuse has no place here), but thankfully I have a slot manager object to do all the dirty work.
http://www.nutty.ca - Being a nut has its advantages.

#5 eddie

    Senior Member

  • Members
  • PipPipPipPip
  • 751 posts

Posted 20 February 2007 - 04:39 AM

Right. So yeah, i fyou're doing any sort of assignment inside of your code, references will fail.

You could always write a wrapper class that houses references themselves, but by it's very nature is copy-by-value-ish. I think Boost had something like that.

Not sure if that helps, now that you've done two implementations. ;)

#6 dave_

    Senior Member

  • Members
  • PipPipPipPip
  • 584 posts

Posted 20 February 2007 - 08:53 AM

Is this useful? Boost Remove reference Its a simple template specialisation

So rather than storing the reference, you store the value


template<class param1>
class A
{
public:
     boost::remove_reference<param1>::type mParam1;  // Store user variable by value

}

Edit: if you need to store the reference, specialise your templates to store the pointer for the case of references, but remove_reference is still a good starting point.

#7 TheNut

    Senior Member

  • Moderators
  • 1473 posts
  • LocationThornhill, ON

Posted 20 February 2007 - 12:58 PM

That's pretty interesting, learn something new every day. I did some further research on this and came up with: http://msdn2.microso...96f(VS.80).aspx

Very interesting read. For my case though, there is far less code by just adding in the special user slot classes than specializing the core slot class for various cases. Still though, this technique could come in handy in the future. Thanks for the heads up.
http://www.nutty.ca - Being a nut has its advantages.

#8 .oisyn

    DevMaster Staff

  • Moderators
  • 1822 posts

Posted 20 February 2007 - 11:23 PM

eddie said:

You could always write a wrapper class that houses references themselves, but by it's very nature is copy-by-value-ish. I think Boost had something like that.
boost::ref ;). It provides a class that behaves as the reference to a variable, but can be copied as a pointer.
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#9 dave_

    Senior Member

  • Members
  • PipPipPipPip
  • 584 posts

Posted 21 February 2007 - 10:38 AM

TheNut said:

Very interesting read. For my case though, there is far less code by just adding in the special user slot classes than specializing the core slot class for various cases. Still though, this technique could come in handy in the future. Thanks for the heads up.
Maybe.. what happens when you've got a const? Will you have to specialise for that too?

Also check this out: http://en.wikipedia....metaprogramming

#10 SmokingRope

    Valued Member

  • Members
  • PipPipPip
  • 210 posts

Posted 22 February 2007 - 12:51 AM

If you are using this in a limited environment you could always make an assumption about the parameters being passed.

template <typename tParam>
class Handler
{
public:
    void signal( const tParam & ){};
};

class Dummy {};

Handler<Dummy> l_1; // void Handler::signal( const Dummy & )
Handler<Dummy*> l_2; // void Handler::signal( Dummy * const& )

There is a similarity between a reference to a const pointer and the (non-reference) alternative. In a lot of cases i think we should probably be writing our functions to take a const pointer anyway.

void func1( Dummy * pData){}; // Can wreak havoc on the pointer address
void func2( Dummy *const pData ){}; // Can't modify the pointer address
void func3( Dummy *const& pData){}; // Can't modify the pointer address

This similarity is only accurate when these types are parameters. There can be a huge difference between using a pointer and using a reference to a const pointer in other circumstances.

#11 .oisyn

    DevMaster Staff

  • Moderators
  • 1822 posts

Posted 22 February 2007 - 02:12 AM

Note that func1 and func2 have the same signature, the only difference is that the actual implementation of func1 is allowed to alter it's own local copy of the parameter, while func2 isn't. But the outside world doesn't care either way (which is why the signatures are equivalent). I don't really see what func2 has got to do with the point you're trying to make ;)
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#12 TheNut

    Senior Member

  • Moderators
  • 1473 posts
  • LocationThornhill, ON

Posted 22 February 2007 - 05:27 AM

Heh, I have a printout of that Fibonacci metaprogramming code on my desk. It's a thing of beauty =)

"Maybe.. what happens when you've got a const? Will you have to specialise for that too?"
I don't know for sure, but if that were the case it could account for a lot of code bloat. Of course you could always put a comment in there "Please be careful not to modify important memory". I've seen that once in someone's engine :lol:

Smoke, I do try to minimize obfuscating my code =)
http://www.nutty.ca - Being a nut has its advantages.





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users