Thread: Global variable vs parameter 'sending'.

  1. #1
    the Wizard
    Join Date
    Aug 2004
    Posts
    109

    Global variable vs parameter 'sending'.

    Hey everyone,

    I've read a bit on this forum about global variables and see that you don't recommend em.

    I want to be able to use a vector of Fish objects in a boids algorithm. Here's the declaration of the vector in main.cpp:
    Code:
    static vector<Fish*> fishVector;
    If I need to send it to boids.cpp where it's needed, then I need to give it to every fish object as a parameter on creation, which then needs to give it to the boids algorithm as parameter when calling its functions.
    So the fishVector will be sent like this: main.cpp -> fish.cpp -> boids.cpp.. This annoys me since fish.cpp has nothing to do with the fishVector vector at all.

    Are there another way to get it to boids.cpp without having to send it though fish.cpp and without using global variables??

    If you have any question or something that needs clarification, please write

    Thanks in advance.
    Mipzhap
    -//Marc Poulsen -//MipZhaP

    He sat down, he programmed, he got an error...

  2. #2
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by MipZhaP View Post
    So the fishVector will be sent like this: main.cpp -> fish.cpp -> boids.cpp.. This annoys me since fish.cpp has nothing to do with the fishVector vector at all.
    Then why is fish.cpp the thing that calls boids.cpp? You're right to be concerned about weird data flows. In this case it's revealing an underlying logical problem. If fish.cpp shouldn't care about this vector, but boids.cpp does care about it, then fish.cpp should not be calling boids.cpp.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  3. #3
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by MipZhaP
    If I need to send it to boids.cpp where it's needed, then I need to give it to every fish object as a parameter on creation, which then needs to give it to the boids algorithm as parameter when calling its functions.
    So the fishVector will be sent like this: main.cpp -> fish.cpp -> boids.cpp.. This annoys me since fish.cpp has nothing to do with the fishVector vector at all.
    It seems to me that indeed a fish object should not need to be aware of a container of (pointers to) fish objects. At the same time, it seems to me that this boids algorithm operates on just such a container, not only on a fish object. As such, perhaps it makes more sense to directly provide the fish container to the boids algorithm, along with some way of identifying a particular fish object contained (e.g., using an index).
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  4. #4
    C++ Junkie Mozza314's Avatar
    Join Date
    Jan 2011
    Location
    Australia
    Posts
    174
    Using global variables makes it hard to reuse your code; so it makes it less general. If your algorithm is to be used in a larger application, and you used global variables, then the entire application is exposed to this data, but it should be hidden. For this reason, the use of global variables is not a legitimate alternative to passing your data around where it's needed. Part of the desire to use globals can be alleviated by good use of class member variables, as methods have access to the member variables of their class without them being passed in.

    PS: Global variables have bug/maintainability problems too.

  5. #5
    the Wizard
    Join Date
    Aug 2004
    Posts
    109
    Okey, my fault for not being explicit in explaining the structure.

    The main.cpp file is responsible for creating (using OpenGL - you don't need to care about that) an "aquarium" and making a list/vector of Fish objects so it can put them inside the aquarium.
    This means that main.cpp definately needs to have access to the list of Fish.

    Next up we have Fish.cpp. It has 2 important functions. preframe(time) and draw().
    The preframe() functions is responsible for calculating, at a given time, the position and the heading (position and velocity vector (vector as in a vector in 3D space with an x,y and z component) ) of the given fish.
    And the draw() function draws the fish at the position and heading that preframe() calculated.
    The preframe function uses an algorithm called boids (look furthere here if you're interested: Boids Pseudocode) to make the fishes act like a flock (correlated but still as individuals).

    The boids.cpp file contains 3 functions to model this flock behavior. The functions take as parameter a Fish object and then does some calculations that take its surrounding fishes (within a certain radius) into account. And here's the "problem", for boids.cpp to be able to do it's calculations it needs the fish list to be able to get the average position and heading of the surrounding fishes.

    The absolutely easiest (but ugliest) solution is to make the fish list global so main.cpp and boids.cpp can access it.

    Another solution is to (from main.cpp) give a pointer to the list as a parameter to every fish so they can have a reference to it and then give that reference to the boids.cpp algorithm when it needs to use it. But this is stupid since the refence needs to go through fish.cpp - which really doesn't need that reference for itself at all.

    Is there another solution that I haven't though of - as the C++ rookie I am? :P

    Thanks in advance for any comments.
    -//Marc Poulsen -//MipZhaP

    He sat down, he programmed, he got an error...

  6. #6
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Completely rejecting globals -- and excessively contorting your code to accommodate that -- is almost as stupid as abusing them excessively.

    Quote Originally Posted by Mozza314 View Post
    Using global variables makes it hard to reuse your code;

    PS: Global variables have bug/maintainability problems too.
    It depends how they are used -- having a function work on a global can make the function more portable, so long as the user understands this. A well chosen global can make code easier to understand and maintain. If your parameter lists are 7 args long, and the last 3 are always the same because you are constantly passing the same three things around everywhere, those 3 things are better off as globals.

    Defines, functions, class definitions, etc, are all (generally) globals, and these can be almost as problematic as simple variables. Method encapsulation via. OOP adds a level of control to the issue. You can something similar via file scope with static, and using global struct instantiations which group variables (but these things are still considered "globals").

    IMO this is particularly applicable to openGL programming since it is easy to end up to end up with oddles of entities that are used everywhere. In fact, it is unavoidable via. GLUT (and I think SDL...) since many of the callbacks do not allow for user parameters (you must register a function with a specific prototype, and there are no "user" pointers, etc). Many (probably: most) API's which make serious use of callbacks and event loops are like this.
    Last edited by MK27; 04-02-2011 at 09:48 AM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  7. #7
    Registered User C_ntua's Avatar
    Join Date
    Jun 2008
    Posts
    1,853
    The concept of global variables are somehow more C-like, than C++-like. In most OOP languages you use a variable in a certain scope. You have classes and namespaces to take care of this.
    Does fishVector need to be accessible by everything? If the scenario is that you might expand it in the future then who knows? Not having a global variable is mostly being pro-active or conservative, correct or wrong is irrelevant if you don't know the final scope of the application.

    In your case, though, I would say that fishVector can be a static variable inside Fish class. That makes the most sense for me. Use namespaces if you want to limit the scope even more.

    You should stick with the right logic, that is the best practice. Consider this, just as an example simplifying Boid.cpp as just having a function Boid():

    Code:
    vector<Fish*> fishVector;
    
    int main()
    {
         Fish *a = new Fish();
         fishVector.pushBack(a);
         Fish *b = new Fish();
         fishVector.pushBack(b);
         Boid();
    }
    When you see this code you think "why not put fishVector in main()?". Since it is a vector where you collect some fishes and then you can pass it where you want.
    But if the purpose is that fishVector is not just a vector to collect some fishes. It is the vector that collects all fishes. Then the code should look like this
    Code:
    int main()
    {
         Fish *a = new Fish();
         Fish *b = new Fish();
         Boid();
    }
    and the vector stores the object inside the constructor. Behind the scene. In this case it is stupid to say that fishVector should be inside main. You actually don't have to mention fishVector at all.

    So the point is more on the design, what is fishVector role. For example, what if you want to call Boid() but not include all the fishes? In this case you can have a vector<Fish*> inside main, but retain fishVector as the vector that contains all fishes.

    In other words use more a higher level logic on your design, do not think of how they will be accessed or how many parameters you pass etc. All of this thinking requires that you actually have the whole picture in your head, when that is hardly ever the case.

    Note finally that extending the scope is easier than limiting it. If you keep fishVector limited and you want to expand it later on, maybe you can simply copy/past it out of the Fish class. Or even have a global pointer point at it. Where the opposite will require you to search everywhere that is used.

  8. #8
    Registered User
    Join Date
    Aug 2010
    Location
    Poland
    Posts
    733
    Quote Originally Posted by Mozza314 View Post
    PS: Global variables have bug/maintainability problems too.
    I do not really understand what 'bug problems' are.

    Quote Originally Posted by MK27 View Post
    If your parameter lists are 7 args long, and the last 3 are always the same because you are constantly passing the same three things around everywhere, those 3 things are better off as globals.
    Both ways are bad and you should find an alternative.

    Quote Originally Posted by MK27 View Post
    using global struct instantiations which group variables (but these things are still considered "globals")
    It is one of the worst possible solutions. It does not improve code in any way, just obfuscates it.

    Quote Originally Posted by MK27 View Post
    IMO this is particularly applicable to openGL programming since it is easy to end up to end up with oddles of entities that are used everywhere. In fact, it is unavoidable via. GLUT (and I think SDL...) since many of the callbacks do not allow for user parameters (you must register a function with a specific prototype, and there are no "user" pointers, etc). Many (probably: most) API's which make serious use of callbacks and event loops are like this.
    For this kind of problem, I would separate this part of library-dependent code as much as possible (creating some kind of wrapper).

    Quote Originally Posted by MK27 View Post
    Completely rejecting globals -- and excessively contorting your code to accommodate that -- is almost as stupid as abusing them excessively.
    I would agree here, but it depends what you consider globals. For example: constants, which are usually global (at least in some scope), may not be really constant (const std::string for example) and may kill your multithreaded application.

  9. #9
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by kmdv View Post
    For this kind of problem, I would separate this part of library-dependent code as much as possible (creating some kind of wrapper).
    You do not understand the nature of callbacks; you cannot wrap them.

    A "callback" is submitted to an event driven library via a function pointer. The library then calls this function when an event occurs (eg, a keypress event). The API dictates the prototype for the callback. Some API's do include a "user pointer" and you can hardcode that value when you register the callback. If not, you will have no way to pass any variable at all into the callback function -- you must access a global, or retrieve it via some other function call (which is a 6 vs 2*3 methodology).

    Quote Originally Posted by kmdv View Post
    It is one of the worst possible solutions. It does not improve code in any way, just obfuscates it.
    Again, I think you have missed the bus and just want to repeat platitudes.
    Last edited by MK27; 04-02-2011 at 01:47 PM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  10. #10
    Registered User
    Join Date
    Aug 2010
    Location
    Poland
    Posts
    733
    Quote Originally Posted by MK27 View Post
    You do not understand the nature of callbacks; you cannot wrap them.
    I know what callbacks are. I meant some kind of wrapper, that is: when I am forced to use global functions (whenever it is DLL or API) I create a singleton with all the stuff. Of course I am forced to create a global instance of it, which is used by all these global functions. Such a wrapped code is IMO easier to maintain (the code is still kept in OOP).
    Last edited by kmdv; 04-02-2011 at 02:10 PM.

  11. #11
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by kmdv View Post
    I know what callbacks are. I meant some kind of wrapper, that is: when I am forced to use global functions (whenever it is DLL or API) I create a singleton with all the stuff.
    YOU CANNOT WRAP A CALLBACK REGISTRATION. If you can demonstrate a concrete example of such, I'll admit my ignorance and thank you (but I am sure this will not be happening )
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  12. #12
    Registered User
    Join Date
    Aug 2010
    Location
    Poland
    Posts
    733
    I just meant separating this code, creating a singleton. I do not know what is so special and impossible. I did not mean a typical wrapper class, just another layer of abstraction (that's probably a better word for it):
    callback -> singleton <-> OOP.

  13. #13
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by kmdv View Post
    I just meant separating this code, creating a singleton. I do not know what is so special and impossible. I did not mean a typical wrapper class, just another layer of abstraction (that's probably a better word for it):
    callback -> singleton <-> OOP.
    Fair enough. You do avoid globals that way; instead what you have is one gigantic object definition with 150 members -- is that any better? It ain't IMO.

    You could nest objects to tidy it up, but then you end with stuff like this inside the callback:

    Code:
    Window1->treeviewA->row3->column2->setvalue(
            transform2(Window2->entryB->getvalue)
    );
    Which my original point was: if eliminating a global means increasing the codebase by 25% to access the variable everywhere it's needed, maybe that is a global worth keeping.

    Guidelines are guidelines -- I'm not recommending everyone eschew parameter lists and encapsulation, just use a little common sense instead of blind conformance.
    Last edited by MK27; 04-02-2011 at 02:59 PM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  14. #14
    Registered User
    Join Date
    Aug 2010
    Location
    Poland
    Posts
    733
    Quote Originally Posted by MK27 View Post
    Fair enough. You do avoid globals that way; instead what you have is one gigantic object definition with 150 members -- is that any better? It ain't IMO.

    You could nest objects to tidy it up, but then you end with stuff like this inside the callback:

    Code:
    Window1->treeviewA->row3->column2->setvalue(
            transform2(Window2->entryB->getvalue)
    );
    Which my original point was: if eliminating a global means increasing the codebase by 25% to access the variable everywhere it's needed, maybe that is a global worth keeping.

    Guidelines are guidelines -- I'm not recommending everyone eschew parameter lists and encapsulation, just use a little common sense instead of blind conformance.
    Ok, now I got your point. I am always trying to find an alternative. And obviously, it depends on your implementation and design whether you will end up like that or not.

    Actually I had in mind an example like this:

    Code:
    1. KeyboardListener class.
    2. Console class.
    3. Low level keyboard hook.
    
    Keyboard hook => KeyboardListener global instance
    Console class <==> (some) KeyboardListener instance
    Whatever happens to your hook, implementation of KeyboardListener and Console won't change. Console instances will always use the listener you pass (you can always pass the global one). What would happen if you hadn't created KeyboardListener... definately, code size would not grow that much.

  15. #15
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Quote Originally Posted by MipZhaP View Post
    Is there another solution that I haven't though of - as the C++ rookie I am? :P
    You actually need to think about what is being represented.

    Generally speaking, a Fish does not need to know everything about every other Fish. The Fish, however, may be in an Environment (for example, an ocean, a fishbowl) and that Environment may contain multiple Fish. Each fish obtains information about other fish (e.g. in a school) through the environment.

    If you decide that each Fish moves in response to a stimulus from the Environment (eg if you drop a boulder into the ocean, each fish in the school will behave in some way, as part of the school), then your dependency might be main.cpp depends on Environment.cpp. Then Environment.cpp depends on Fish.cpp and Boid.cpp.

    The environment may stimulate each fish in turn. In deciding how to stimulate each fish, the environment may get information from the Boid.cpp (eg limits on behaviour determined by being in the school).

    With this representation, there does not need to be dependencies (either way) between Fish.cpp and Boid.cpp. If you want to change how a school behaves, change Boid.cpp. If you want to change environmental constraints (eg water depth) do that through the Environment (which will, in turn, enforce constraints on where individual fish or a school of fish can go). If you want to allow individual fish behaviour (for example, one fish in the school behaves differently from the school as a whole) do that through Fish.cpp.

    I'm not saying the above represents a perfect solution - the art of modelling is to find a representation that provides sufficient flexibility for the task at hand. That model then needs to be reflected in structure of source files (whereas, you seem to be using source structure to impose limits on the model).

    The basic idea, however, is that your model needs to make sense, and not impose artificial constraints and dependencies (as those make things hard to understand, hard to get right, hard to maintain).
    Last edited by grumpy; 04-02-2011 at 03:43 PM.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Quick: Alternative to using global variable
    By abrownin in forum C++ Programming
    Replies: 1
    Last Post: 05-02-2010, 04:25 AM
  2. Replies: 22
    Last Post: 11-13-2009, 05:51 PM
  3. scope of global variable vs. static
    By chiefmonkey in forum C++ Programming
    Replies: 4
    Last Post: 06-21-2009, 12:23 PM
  4. global struct variable best use
    By Kempelen in forum C Programming
    Replies: 2
    Last Post: 06-05-2009, 05:08 AM
  5. Global variable in shared library
    By krock923 in forum C Programming
    Replies: 5
    Last Post: 01-11-2008, 04:56 PM