Sign in to follow this  
Noods

"loose" variables...

Recommended Posts

I have quite a few variables that store information about the state of my program and the image that is being displayed. These variables exist within the scope of my WinProc. I am thinking about encapsulating this information in a "ApplicationData" class. However, these vaiables are passed all over the place. Frames per second is important in my application and I am wondering if the overhead of accessor functions would be worth it. How do you all approach encapsulating "loose" variables in your programs?

Share this post


Link to post
Share on other sites
I'm confused... what's the difference between a "loose variable" as you're describing it, and a global variable? What are some examples of loose variables?

As for your question about accessor functions, accessor functions are generally inlined and have zero computational overhead (as long as they aren't actually computing things).

Share this post


Link to post
Share on other sites
refactor your code till they are no longer necessary.
Quote:
I am thinking about encapsulating this information in a "ApplicationData" class.
why? what difference will it make if your gloabals are all wrapped up in a class they are still globals.

Share this post


Link to post
Share on other sites
Quote:
Original post by stonemetal
refactor your code till they are no longer necessary.

Erm, really? Can you give an example of how declaring hInstance as a global variable, for instance, would cause problems that would be solved by refactoring to remove the global?

Share this post


Link to post
Share on other sites
Quote:
Original post by Noods
When I say loose, I just mean not part of any object.

I don't think you do. If you have a for-loop, with a variable int i used as the counter, do you consider i to be a "loose variable"?

Share this post


Link to post
Share on other sites
Creating the main window is a standard approach used in every win32 application and it is in my oppinion comparable to the 'main' function in console apps. So I think you should identify the bare minimum needed to create the window, i.e. hinstance...
Then place all that into one .h file. Define entry functions such as Init, Update, Dispose and call them appropriately (passing parameters if needed). Then implement these functions in a separate .cpp file. From here on you should have no global variables, except perhaps one instantiation of a Main class.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sneftel
Quote:
Original post by Noods
When I say loose, I just mean not part of any object.

I don't think you do. If you have a for-loop, with a variable int i used as the counter, do you consider i to be a "loose variable"?


I have no globals. The reason I bring all this up is that I may end up moving to a state pattern and it might make sense to encapsulate this data so it will be easier to point to from my state objects.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sneftel
Erm, really? Can you give an example of how declaring hInstance as a global variable, for instance, would cause problems that would be solved by refactoring to remove the global?


Yeah, really. It doesn't really matter if it causes problems or not, sloppy code is sloppy code. Globals show a lack of thought and discipline.

Share this post


Link to post
Share on other sites
Quote:
Original post by stonemetal
Sloppy code is sloppy code. Globals show a lack of thought and discipline.

Sloppy code is sloppy code. Variables beginning with the letter "Q" show a lack of thought and discipline.

Share this post


Link to post
Share on other sites
Quote:
Original post by Noods
and I am wondering if the overhead of accessor functions would be worth it.


You can't always have clean, organized code and high performing code. Many times you are going to have to sacrifice performance for maintainability, and vise versa. Unless performance of stuff as simple and small as the overhead of accessors/mutators is actually dragging you down, don't worry about it.

If you do want clean and maintainable code, which I recommend you always aim for unless you have a VERY good reason otherwise, don't worry about the performance until you have it implemented. At that point, profile your code and find out what is actually slowing things down. Also, the biggest performance gains usually come from the algorithm, not how tightly you can pack instructions. For example, if your bottleneck is from a huge list being sorted, would your answer to the problem be, "Oh, guess we have to rewrite this part in hand-written assembly" instead of "what algorithm would work best for this data set"? I sure as hell hope not.

So pick either micro-optimizations or maintainability. Mixing both usually just results in crap in both categories. If your project is going to take longer than a few hours, going to be used in the future, or if you plan on releasing it to anyone else, pick maintainability.

Also, don't anyone get the idea that I am saying maintainable code always performs worse. It is easy to write horribly unoptimized algorithms if your code is sloppy. But if you are worrying about the performance of something as small as a global vs an accessor, just slap yourself until you don't. I don't care if it takes all night.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sneftel
Quote:
Original post by stonemetal
refactor your code till they are no longer necessary.

Erm, really? Can you give an example of how declaring hInstance as a global variable, for instance, would cause problems that would be solved by refactoring to remove the global?

I can [lol].

Share this post


Link to post
Share on other sites
Quote:
Original post by Sneftel
Quote:
Original post by stonemetal
refactor your code till they are no longer necessary.

Erm, really? Can you give an example of how declaring hInstance as a global variable, for instance, would cause problems that would be solved by refactoring to remove the global?


I can't think anything for hInstance. But if, for example, you declare a connection to the database as a global variable, it could cause some problems.

For example, if you suddenly got garbage in some table X, and you wanted to investigate the source of the trouble, the whole program would be suspect, because the whole program has access to the connection. But if you refactor and pass the connection only to the code that needs it, then the 'suspects' would be limited and debugging and maintenance would be easier.

Share this post


Link to post
Share on other sites
Quote:
Original post by stonemetal
It doesn't really matter if it causes problems or not /.../


Yes it does.

There should be a valid objective reason for something specific to be bad practice in order for it to actualy be bad practice.

Quote:
Original post by stonemetal
/.../ sloppy code is sloppy code.


Subjective reasoning applied to objective realities is still subjective reasoning.

Quote:
Original post by stonemetal
Globals show a lack of thought and discipline.


The blanket-application of rules to every possible condition shows no-thought-at-all.

Your 'discipline' angle is just a red-herring that has no application to his question.


Share this post


Link to post
Share on other sites
Quote:
Original post by mikeman
For example, if you suddenly got garbage in some table X, and you wanted to investigate the source of the trouble, the whole program would be suspect, because the whole program has access to the connection. But if you refactor and pass the connection only to the code that needs it, then the 'suspects' would be limited and debugging and maintenance would be easier.


You are advocating for global state here. See, if you have sloppy global state (that is, without any kind of restriction on who uses what part of that state) then you are bound to get into trouble because you can never know who can change a given piece.

The three major programming paradigms (procedural, object-oriented and functional) each solve the above problem in a certain way:
  • Procedural programming wraps global state in modules which reduce the brain overhead of handling dependencies. This makes answering the question of "who uses who" very easy, because you're handling dependencies between dozens of large systems instead of thousands of functions and variables.
  • Object-oriented programing uses no global variables. This leads to more work in situations where an "use a global variable now, refactor later" approach would have been easier, but otherwise eliminates the problem entirely.
  • Functional programming doesn't allow state changes, so the problem is totally irrelevant. On the other hand, solving problems without mutability decreases performance and requires even more brain power at design time.


However, object-oriented programming makes debugging harder (although it does make unit testing easier). The reason for this is that a typical object-oriented program will have a large number of interacting instances floating around at any point in time (whereas in a procedural program, you only have a finite set of modules defined at compile time). So, when you debug an object-oriented program, you have a lot more entities to consider, most of which are dynamic and created at run-time, so they risk not being repeatable and take longer to identify.

For instance, in the example of a database connection, in a procedural program you have a compile-time known graph of dependencies between the module that handles that connection and several other modules. The structure of the program is described by the declarations. So, you can follow them by hand and find out. In an object-oriented program, you have to actually run the program to find out what the objects look like. The structure of the program is described by the code. Object-Oriented programs are easier to extend because, to a small extent, the code controls the structure, but this also makes human analysis of the structure harder. As a whole, the easier it is to write (or change, design, build) the harder it is to read (or understand, study, analyze).

Share this post


Link to post
Share on other sites
Quote:
Original post by ToohrVyk
Quote:
Original post by mikeman
For example, if you suddenly got garbage in some table X, and you wanted to investigate the source of the trouble, the whole program would be suspect, because the whole program has access to the connection. But if you refactor and pass the connection only to the code that needs it, then the 'suspects' would be limited and debugging and maintenance would be easier.


You are advocating for global state here. See, if you have sloppy global state (that is, without any kind of restriction on who uses what part of that state) then you are bound to get into trouble because you can never know who can change a given piece.


Can you explain that a little? How am I advocating *for* global state when I say that, in this example, you can refactor and give access to the connection only to the modules that need it? Isn't that a restriction on who uses what part of the state? Or do you mean something else? How would that problem be solved without 'global state'?

Share this post


Link to post
Share on other sites
Quote:
Original post by mikeman
Can you explain that a little? How am I advocating *for* global state when I say that, in this example, you can refactor and give access to the connection only to the modules that need it? Isn't that a restriction on who uses what part of the state? Or do you mean something else? How would that problem be solved without 'global state'?


Sure.

When you provide a value as a function argument, you allow any function within that function's call tree to manipulate and store references to that value. In an object-oriented program and with the existence of polymorphism, if you pass a value as an argument to a function of any object, then that value is potentially available to the entire graph of objects containing that object. Of course, it's probably going to be only a small part of that graph, but since the exact nature, contents and layout of the graph are determined at runtime, reasoning about them at compile-time (and even when debugging) is hard. So, the bottom line is that you can't know without great effort who is manipulating that piece of state you just passed as an argument.

On the other hand, if pieces of state are never passed around as arguments but instead encapsulated as globals in a module, then determining who can alter a piece of global state is pretty simple: only the direct or indirect callers of global functions of that module can manipulate the state. And since the graph of modules is fully defined at compile time (and can be fully documented, along with all dependencies) the analysis is even easier.

In short, if I ask you, "can this piece of code alter value X?", you'll have an easier time if the program follows a procedural module-based design. So, if you're looking for control and knowledge over who can change state, procedural modules with global states are superior to object-oriented designs.

Share this post


Link to post
Share on other sites
Quote:
Original post by ToohrVyk
Quote:
Original post by mikeman
Can you explain that a little? How am I advocating *for* global state when I say that, in this example, you can refactor and give access to the connection only to the modules that need it? Isn't that a restriction on who uses what part of the state? Or do you mean something else? How would that problem be solved without 'global state'?


Sure.

When you provide a value as a function argument, you allow any function within that function's call tree to manipulate and store references to that value. In an object-oriented program and with the existence of polymorphism, if you pass a value as an argument to a function of any object, then that value is potentially available to the entire graph of objects containing that object. Of course, it's probably going to be only a small part of that graph, but since the exact nature, contents and layout of the graph are determined at runtime, reasoning about them at compile-time (and even when debugging) is hard. So, the bottom line is that you can't know without great effort who is manipulating that piece of state you just passed as an argument.

On the other hand, if pieces of state are never passed around as arguments but instead encapsulated as globals in a module, then determining who can alter a piece of global state is pretty simple: only the direct or indirect callers of global functions of that module can manipulate the state. And since the graph of modules is fully defined at compile time (and can be fully documented, along with all dependencies) the analysis is even easier.

In short, if I ask you, "can this piece of code alter value X?", you'll have an easier time if the program follows a procedural module-based design. So, if you're looking for control and knowledge over who can change state, procedural modules with global states are superior to object-oriented designs.


Ok, got it. Thanks. So if I want to use object-oriented approach but still want to easily analyze 'who uses what', what is my best bet? Use a hybrid approach organizing my classes into modules? Or is there no good solution to this problem using OO?

Share this post


Link to post
Share on other sites
Quote:
Original post by mikeman
Ok, got it. Thanks. So if I want to use object-oriented approach but still want to easily analyze 'who uses what', what is my best bet? Use a hybrid approach organizing my classes into modules? Or is there no good solution to this problem using OO?
This has been solved a long time ago by functional programming: if values cannot be changed, then "who can change this value?" is an easy question. Of course, you don't have to go all the way to immutable values, but you can still go some distance.

The key is that in object-oriented programming you have two "safe" areas where you fully control who modifies that. These are the local scope and the private scope. When a reference to a value leaves local scope (for instance as an argument to a function), you lose control. When a reference to a value leaves private scope (for instance as an argument or return value), you lose control. There is no problem with letting immutable references escape (because then, the value cannot be modified, so you don't care), but mutable references should stay where they are (unless, of course, the precise point of the value is to be externally modified and listened to).

This is a concept that could be reasonably called "write ownership":
  1. You cannot alter a value without having write ownership of that value.
  2. You have write ownership of any values you create.
  3. Whenever you grant write ownership of a value to someone else, you lose write ownership of that value (there is only one owner per value).
  4. The ownership of a value should be obvious from usage. Use 'const' wherever applicable, for instance.
  5. You can always read from a value. If you don't have write ownership, your code should assume that the value may change at any point.


This helps manage complexity (you only have to know who owns a value, which has now become a static property instead of a dynamic one) and also helps with concurrent programming.

Share this post


Link to post
Share on other sites
Quote:
Original post by ToohrVyk
Quote:
Original post by mikeman
Ok, got it. Thanks. So if I want to use object-oriented approach but still want to easily analyze 'who uses what', what is my best bet? Use a hybrid approach organizing my classes into modules? Or is there no good solution to this problem using OO?
This has been solved a long time ago by functional programming: if values cannot be changed, then "who can change this value?" is an easy question. Of course, you don't have to go all the way to immutable values, but you can still go some distance.


See, this is something I never quite got, no matter how much I tried. How would
functional programming solve the problem of edit/writing a database? It would create a new database each time I would like to make a change to it?

Share this post


Link to post
Share on other sites
Quote:
Original post by mikeman
See, this is something I never quite got, no matter how much I tried. How would
functional programming solve the problem of edit/writing a database? It would create a new database each time I would like to make a change to it?


Yes, it would create a new one. Of course, because everything is immutable, it's quite possible that after optimizing things out, 99% of the data in the new database is in fact just a few bytes' and nanoseconds' worth of references to the data in an existing database (or, depending on your approach, that the old database is now just a diff between what it used to be and the new database).

Share this post


Link to post
Share on other sites
Quote:
Original post by ToohrVyk
When you provide a value as a function argument, you allow any function within that function's call tree to manipulate and store references to that value. In an object-oriented program and with the existence of polymorphism, if you pass a value as an argument to a function of any object, then that value is potentially available to the entire graph of objects containing that object. Of course, it's probably going to be only a small part of that graph, but since the exact nature, contents and layout of the graph are determined at runtime, reasoning about them at compile-time (and even when debugging) is hard. So, the bottom line is that you can't know without great effort who is manipulating that piece of state you just passed as an argument.

On the other hand, if pieces of state are never passed around as arguments but instead encapsulated as globals in a module, then determining who can alter a piece of global state is pretty simple: only the direct or indirect callers of global functions of that module can manipulate the state. And since the graph of modules is fully defined at compile time (and can be fully documented, along with all dependencies) the analysis is even easier.


It's not often I read a post of yours I disagree with ToohrVyk, but I have to say that sounds like a load of rubbish. Let's say we have two versions of a program, one in which there is some piece of global state and the other in which that state is not global and is passed to whatever needs to modify it. How is it any easier searching through the code for direct and indirect users of that state's interface in the global state version? You'll find references to StateClass::someModifier() in the same places in both versions of the program, albeit with one version using globalState.someModifier() and the other using stateReference.someModifier(). You say that the difficulty is when trying to find code that indirectly modifies state when it's not global, because you then have to consider the tree of calls around that code, but isn't that exactly the case when the state is global too?

Whatever the case, passing the state around rather than making it global gives you more control over what is allowed to modify that state and makes the dependency more obvious when coding.

Maybe what you say is 'correct in theory', but I know from countless hours spent debugging that it's a damn sight easier to find what's modifying data when that data isn't global (assuming a debugger and data breakpoints aren't available).

Share this post


Link to post
Share on other sites
Quote:
Original post by Rockoon1
Quote:
Original post by stonemetal
It doesn't really matter if it causes problems or not /.../


Yes it does.

There should be a valid objective reason for something specific to be bad practice in order for it to actualy be bad practice.




/*some includes here*/
int main()
{
int i = 0;
for(;i < 100;++i)
{
std::cout << i<<'\n';
}
std::cout << "wow I can count to " << i << std::endl;
return 0;
}


Now, should i be a global variable? No, it doesn't correctly define the scope of thought and incorrectly introduces a variable where none is needed. Does it cause a problem with the correct function of the application, no it doesn't. Having i be a global shows confusion about the use of i and a lack of thought about the proper use of the code written. No specific bug is necessary to make poor code incorrect otherwise we would start all of our variables out as globals then move them into lower scopes as we fixed bugs in the pile of crap we called our code base.

Share this post


Link to post
Share on other sites
Quote:
Original post by hymerman
It's not often I read a post of yours I disagree with ToohrVyk, but I have to say that sounds like a load of rubbish. Let's say we have two versions of a program, one in which there is some piece of global state and the other in which that state is not global and is passed to whatever needs to modify it. How is it any easier searching through the code for direct and indirect users of that state's interface in the global state version? You'll find references to StateClass::someModifier() in the same places in both versions of the program, albeit with one version using globalState.someModifier() and the other using stateReference.someModifier(). You say that the difficulty is when trying to find code that indirectly modifies state when it's not global, because you then have to consider the tree of calls around that code, but isn't that exactly the case when the state is global too?
The difference here is that globalState.someModifier() tells me that the global state is being modified, whereas stateReference.someModifier() only tells me that one of the many instances of StateClass is being modified. So, if I care about a specific instance (say, a database connection to database #42) I won't know whether a given stateReference.someModifier() call affects database #42 or database #13 (or any other database that my program might have accessed at that point in time).

Quote:
Whatever the case, passing the state around rather than making it global gives you more control over what is allowed to modify that state and makes the dependency more obvious when coding.
I don't find the dependency obvious. When I look at procedural code, I know that module X uses module Y because it calls a function from that module, no ifs or buts or anything. It's so simple that even a C++ IDE can tell me that.

In an object-oriented program if I want to know whether object X uses object Y, I have to follow in my head the entire run-time object graph construction in order to determine whether object X references object Y instead of some other object Z of the same class as Y, which is not only an order of magnitude greater, but beyond the grasp of static analysis tools (because it's undecidable).

Consider the following code:

class Foo { Whizzbang(); };

void Frobnicate(Foo foo) { foo.Whizzbang(); }

int main()
{
Foo foo;
Foo bar;

// Do some stuff with 'foo' and 'bar'.
}


Can you tell me, at a glance, whether Frobnicate will depend on 'foo' or on 'bar' ? You cannot, and you have to explore what the "do some stuff" ellipsis actually does in order to determine that. By contrast, in a procedural programming approach:

namespace Foo { void Whizzbang(); }
namespace Bar { void Whizzbang(); }

void Frobnicate() { Foo::Whizzbang(); }


It is now perfectly obvious that Frobnicate depends on 'Foo' but not on 'Bar'. Of course, this imposes an obvious limit on the fact that you can't create new modules on the fly like you could with instances in the previous example. That is the advantage of object-oriented programming : extensibility. But with greater extensibility comes an increased difficulty in debugging: polymorphic code with side-effects (Object-Oriented & Imperative) is harder to debug than both polymorphic code without side-effects (Functional) and monomorphic code with side-effects (Procedural).

Quote:
Maybe what you say is 'correct in theory', but I know from countless hours spent debugging that it's a damn sight easier to find what's modifying data when that data isn't global (assuming a debugger and data breakpoints aren't available).
Programmers have been working with global data for decades, so they've come up with techniques and strategies for dealing with global data without having to spend countless hours debugging. One of these is the plain old procedural technique of grouping global state into modules.

If you wonder "what code could be altering the state of this module?" in a procedural-style C program, you usually just have to run makedepend (or some similar tool) to find out what the dependencies between your modules are, and place the appropriate breakpoints there. If you're using namespaces to represent your modules, it's even easier.

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