Sign in to follow this  
Soaps79

[C++] Problems with templates and friends

Recommended Posts

I have an idea for a Tween class that looks great on paper, but I am running into trouble trying to implement it. I want to be able to create Tween objects that take in a variable, and adjust it in different ways across a given time span. I then want to be able to have objects instantiate Tweens which will run automated. Essentially I want to be able to:
[source cpp]
// give object a Tween* vector
vector<Tween*> tweenVec;

// somewhere within the Object's code
// to tween position from its current (1, 1) to (100, 100) over 1000ms
Tween<float>* tempTween = new Tween<float>( &m_position.X, 100, 1000 )
tweenVec.add( tempTween );
tempTween = new Tween<float>( &m_position.Y, 100, 1000 )
tweenVec.add( tempTween );

// then in the Object's update code:
if ( !tweenVec.empty() )
{
for each tween t
t->update( currentTime )
if t->isFinished
// delete the object and remove the pointer from the vector
}
[/source]

I thought, through my limited understanding of friends, that if I specified the Tween class as a friend in the Object's declaration code that it could access protected members. I tried making a Tween<T> a friend, but then it wanted me to also make Object a template, which is understandable but very undesirable.

How would someone go about implementing this so object could have a vector of Tween pointers that have access to its protected data? Would knowing which data types it will work on be helpful? As in, explicitly making it friends with Tween<int>, Tween<float> etc? Also, will all of this be undone when I make derived Tweens with specific agorithms?

Share this post


Link to post
Share on other sites
Note that Tween<float> is a completely different type than Tween<int> so you can't have Tween* without the template type. You must always specify the type. What you can do is having a abstract base class that all your Tween<T> inherit from.
Something like this:[code]
class TweenBase
{
public:
virtual void update(int time) = 0;
};

template <typename T>
class Tween : public TweenBase
{
public:
virtual void update(int time) { .. };
};[/code]
Then you can have an std::vector<TweenBase*> to store the pointers in.

I don't understand exactly why you want to use a friend. What protected data are you talking about?

Share this post


Link to post
Share on other sites
The protected data that I was talking about would be members of an Object or Sprite class, like its (protected) position or scale variables. I was looking to give the Tween a reference to a member variable at the Tween's instantiation, and just let it run until it dies. Then on each render cycle, the object would be drawn using its member variables, which have been manipulated by the Tween in its Update() function.

The idea of using the abstract base class is something I will explore when I go back to it tonight, thanks for your help.

Share this post


Link to post
Share on other sites
The friend keyword allows class A to access class B's private members [b]by going through class B[/b].

That is:
[code]
class A
{
private:
int myPrivInt;

// allow A to see my internals
friend class B;
};
class B
{
A myA;

void SomeFunc()
{
// I can reach myPrivInt through A, because it declared me as a friend
myA.myPrivInt = 5;
}
}
[/code]


That's not what you're doing though. You're binding a pointer to m_position.Y. This allows anyone owning that pointer to modify m_position.Y without going through A. You don't have to do anything with friends. If you have a pointer to something, you can change it, even if it's a private member within a class.

Note that this breaks encapsulation and is typically seen as bad OO programming. You're bypassing the very reason for making m_position private in the first place. That doesn't mean it should [b]always [/b]be avoided. We often write code like this because it's more efficient than alternatives that are more OO.

In this case, I think you're safe as long as you make sure that Object maintains strict ownership over the Tween objects. I recommend you enforce this by making tweenVec a boost::ptr_vector, but in the absence of boost I'd just make sure you're deleting each tweenVec object in the Object destructor.

I'm having trouble thinking of [b]good[/b] ways of doing this without breaking encapsulation. You could do something with shared_ptrs and boost::functions to retrieve and set the current value, surely, but I don't think it would end up being very nice.

Share this post


Link to post
Share on other sites
Great explanation of friend classes thank you. That is exactly what I thought they did, but I did not realize that you could get around variables being private through pointer use (as bad practice as it may be).

[quote name='A Brain in a Vat' timestamp='1313357150' post='4849109']
In this case, I think you're safe as long as you make sure that Object maintains strict ownership over the Tween objects. I recommend you enforce this by making tweenVec a boost::ptr_vector, but in the absence of boost I'd just make sure you're deleting each tweenVec object in the Object destructor.
[/quote]
Yes, the object itself will instantiate the Tween objects, add them into its vector, and delete them when they finish (and delete any remaining when the object's destructor is called). No other objects will have access to this vector or the individual Tweens.

[quote name='A Brain in a Vat' timestamp='1313357150' post='4849109']
I'm having trouble thinking of [b]good[/b] ways of doing this without breaking encapsulation. You could do something with shared_ptrs and boost::functions to retrieve and set the current value, surely, but I don't think it would end up being very nice.[/quote]
This was somewhat like an approach my partner and I discussed, where upon Tween instantiation you would pass it a function pointer that would later be used to pass the tween'd value back to the object every Update(). This approach would have worked, but I am trying hard to maintain the simple/clean interface for the Tween class, and felt that the function pointers made it feel sloppy and unnecessarily abstract.

Thanks again for the assistance.

Also, is this concept something that is done regularly but I just never ran across? It seems like a real handy class to have around, but I have never seen it before. Edited by Soaps79

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this