🔒 Locked
🎮 Unity
Single function friend access?
Advertisement
Original Post
Is there any way to define a single class that can use a single private function of another class? Short description: I want to allow ParticleStream::Update() to be called by, and only by, ParticleSystem::Update(). But I don't want to allow ParticleSystem to call any other restricted functions of ParticleStream. Robust description: I have a particle stream object. Many thousand particle stream objects may exist, and are owned by game objects. When a particle stream is triggered (and creates some particles) by it's owner, the stream object is inserted into a special active-list which is owned by a global particle system object. The particle stream object has a function, Update(), which should only ever be called when the stream is active (in the active list). I want to simply have the particle system call Update() on all of the streams that are in it's active list. But I want to make it obvious that the Update() function is not to be called from anywhere else. I recently nearly made the mistake of assuming the stream object needed updated by the owner, and luckily ran by my comment (// DO NOT CALL THIS EXTERNALLY) before wasting a bunch of time on it. Is this possible? Thanks for any information [smile]
Solution 1: trust yourself. If you control both ParticleStream and ParticleSystem, you may be able to make the latter a friend of the first.
Solution 2: create an ParticleStreamUpdateController class, that is a friend of ParticleStream. Then:
Solution 2: create an ParticleStreamUpdateController class, that is a friend of ParticleStream. Then:
class ParticleStreamUpdateController { friend void ParticleSystem::Update(); static void Update(ParticleStream& p) { p.Update(); } ParticleStreamUpdateController() { assert (false); }};
It looks like in this case you are trying to find some cleverness you can use to patch up a design problem. Could you instead come up with a design where the particle stream doesn't depend on its status in the active list?
This solution is pretty much what I originally hoped I wouldn't have to do. If it were really clever, I might feel better about using it. As for better designs, I'm open to suggestions. I throw the streams into an active list to avoid iterating through them until they're being used.
Well, the "OO" way is to provide a separate interface. I didn't test this:
The theory being, the inheritance causes the base only of the stream to be a friend of just the system, and the base only provides the stuff that the system is allowed to touch.
However, this might only work with virtuals (horrible news in a particle system), or even not at all in C++ (I really don't know the rules for friend access since I so rarely use or see it).
class UpdatableByParticleSystem { friend class ParticleSystem; void update() {}};class ParticleStream : public UpdatableByParticleSystem { void update() {}};The theory being, the inheritance causes the base only of the stream to be a friend of just the system, and the base only provides the stuff that the system is allowed to touch.
However, this might only work with virtuals (horrible news in a particle system), or even not at all in C++ (I really don't know the rules for friend access since I so rarely use or see it).
Well, Zahlman is right, however in C++ that would entail virtual functions. The only other solution I can think of is to give the ParticleSystem::Update call a function pointer to ParticleStream::Update. However that entails the same overhead as using virtual functions anyway and is the ugliest solution of all. So either trust yourself or go with ToohrVyk's solution. The thing is, I wouldn't worry about this as a design issue because C++ isn't a perfect OO language - a lot of the time you have to make a choice between code beauty and speed and some things you just can't really do neatly at all (I'm thinking templates here).
Giving full access of streams to the system isn't a big deal for me. They're both in the same module, and work extremely close together. I would rather expose all of the ParticleStream to ParticleSystem than leave ParticleStream::Update() dangling out in public as I once had it.
The update controller solution isn't bad. Just that it seems like one of those hacks that will later result in me chasing through my code, like following animal footprints backwards in the snow.
The update controller solution isn't bad. Just that it seems like one of those hacks that will later result in me chasing through my code, like following animal footprints backwards in the snow.
Quote:
Original post by Kest
Giving full access of streams to the system isn't a big deal for me. They're both in the same module, and work extremely close together. I would rather expose all of the ParticleStream to ParticleSystem than leave ParticleStream::Update() dangling out in public as I once had it.
Just out of interest... is there anything really bad about ParticleStream::Update being public? Having it public may allow you to implement unit testing, or enable some clever special effects. (Some particles updated more often than others, perhaps). Of course, if there are side effects, you may choose to continue to restrict it.
For a complicated situation, you might consider whether a Façade pattern is appropriate. By giving most users a limited interface to a given object, you can grant special access to other users by giving them the full interface (or a different interface). This is the general form of what Zahlman is showing.
This post is a few days old. I actually ended up completely removing Stream::Update() and worked the code that ran in it over into ParticleSystem.
edit: Only one day old? Must have drank a lot of coffee since then.
The system used to (when I posted this thread) update in this way:
The streams didn't update basic particle behavior, like velocity over time. Streams only needed to do special things with their own particles, like dynamically change gravity settings, perform collision detection, or morph one type of particle (as it dies) into another. Calling Stream::Update() outside of this would have just wasted time repeating the same tasks twice.
I originally added a Stream::Update because I expected to derive several other types of streams from a base stream. But after creating just three types of generic streams: flow/debris/frag, I haven't needed any others. The only difference between debris and frag is that frag performs ray collision tests with it's particle movement. So really, I've only needed two basic types. And the only thing that really seperates them is the fact that one is continuous (turned on and off) and the other is triggered. In other words, I realized I didn't need derived stream updating at all. Just a few properties given to stream objects, and a few ifs in the system updater.
Still, I appreciate the helpful replies.
edit: Only one day old? Must have drank a lot of coffee since then.
Quote:
Original post by SunTzu
Just out of interest... is there anything really bad about ParticleStream::Update being public?
The system used to (when I posted this thread) update in this way:
ParticleSystem::Update(){ for(int p=0; p<AllParticles; p++) Particle.Update(); for(int s=0; s<ActiveStreams; s++) Streams[s].Update(); RemoveDeadParticles();};The streams didn't update basic particle behavior, like velocity over time. Streams only needed to do special things with their own particles, like dynamically change gravity settings, perform collision detection, or morph one type of particle (as it dies) into another. Calling Stream::Update() outside of this would have just wasted time repeating the same tasks twice.
I originally added a Stream::Update because I expected to derive several other types of streams from a base stream. But after creating just three types of generic streams: flow/debris/frag, I haven't needed any others. The only difference between debris and frag is that frag performs ray collision tests with it's particle movement. So really, I've only needed two basic types. And the only thing that really seperates them is the fact that one is continuous (turned on and off) and the other is triggered. In other words, I realized I didn't need derived stream updating at all. Just a few properties given to stream objects, and a few ifs in the system updater.
Still, I appreciate the helpful replies.
Topic Locked
This topic has been locked by a moderator. New replies are not allowed.
Sign in to reply to this topic.
Advertisement