Bay 12 Games Forum

Please login or register.

Login with username, password and session length
Advanced search  

Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Messages - Stargrasper

Pages: [1] 2 3 ... 88
1
Life Advice / Re: Mom wants to buy a computer.
« on: October 06, 2013, 01:50:55 am »
Out of curiosity, why is the budget $200?  Even for used/refurbished, that's an extremely low value for a laptop.  In fact, I don't think I've ever seen a full blown laptop new for that price.  Netbooks and tablets maybe.  But not a laptop capable of actually doing much.

If you're willing to take a bit more of a risk in quality, ebay is showing promising results for names you're more likely to have heard of.  Craigslist sometimes has amazing deals, too.  Just don't skimp too much.  It sounds like it needs enough power to do some degree of multimedia.

2
General Discussion / Re: if self.isCoder(): post() #Programming Thread
« on: October 06, 2013, 01:38:55 am »
While I generally don't like having unnecessary variables, if I have reason to use embedded loops, my preferred means to break out is to use that extra variable.
Code: [Select]
var stop = 0
while(continuationConditionOuter && !stop)
 while(continuationConditionInner && !stop)
  if(stopCondition)
   stop = 1
Efficiency aside, sometimes embedded loops make the code easier to read or write.  It's generally alright to write inefficient code so long as you're clear enough about what you're doing that the compiler can figure out what you want and rewrite it to be efficient for you.

3
Curses / Re: L.C.S. - You're Playstyle
« on: July 21, 2013, 04:35:13 pm »
Lately I've been taking a more silent approach.  That is, founder is a brilliant genius chessmaster silently leading a massive sleeper network.

4
There's nothing wrong with using xml; especially if you already know it.  Plus the Java API supports it without external libraries.  Offhand, I'm not familiar with any json libraries for Java, but they're out there.  You could also devise your own format and write a parser for it.  Not actually all that hard.  I'm not familiar with your game, but why you want to externalize this data?  You could save yourself about a thousand times worth of work by hard coding that data.  Not saying you should do that, but make sure you have a good reason for doing whatever you choose to do.

5
Curses / Re: Making the LCS wiki an actual resource
« on: July 20, 2013, 01:08:12 am »
Something you learn in the business world.  Asking is the same as offering.

Seriously, though, your approach isn't a good one for convincing people to contribute more than they currently are.

6
Curses / Re: LCS Question
« on: July 20, 2013, 01:05:53 am »
Take a look at your first post.  Then take a look at the last.  It's oh so rare of a conservative automaton to enlighten so easily.  Glad you like the game!

7
Best not to compare C# to C/C++.  It turns out that it's the Microsoft equivalent of Java and should probably be treated as such.

8
I need to count how many times I said "method" versus how many times I said "function".
You said "method" about 52 times, give or take a few.

Also, thank goodness function declaration order doesn't matter in Java. :D

I just copied the entire post into a text file and ran the following two commands on it.  Ran word count on the whole thing, too.
Code: [Select]
stargrasper:~> grep -o method post.txt | wc
     49      49     343
stargrasper:~> grep -o function post.txt | wc
     93      93     837

stargrasper:~> wc post.txt
     356  4079 59138 post.txt

First number is newline count, second is word count, third is byte count.

The reason function order "doesn't matter" in Java is because the Java compiler actually reads your code several times.  C/C++ reads your code four times, I believe.  And each for very different reasons.  First for pre-processor commands, then for compilation, then linking, then assembly.

I got your back ;)

Good to have friends.

That code will work. Prototypes are made by only using the function header (see Lesson 3), followed by a semicolon. The function body is left off. This tells the compiler that the function does exist, but it is defined elsewhere. Even though b's function definition (which contains the function body) is after a's, its declaration (with the prototype) is still before a's definition, so it works.

Worth noting that frequently these get placed in a header file.

Also worth noting that the Wishful Thinking technique I described follows Mego's function rules if you approach it correctly.  Especially the naming, because you're writing the function calls before you're writing the function declarations or definitions.

9
You used the word "function" where you were supposed to write "method" 79 times. :P
Not including the times where you said, "function" in your code.

LMAO!  Really?  Wow...  And only half of those were intentional.  I considered doing a blanket substitution.  It's a really easy operation (%s/function/method/g).  I thought it'd be entertaining to leave them there.  That and I don't think anyone except Java calls them "methods".  If there's really that many, I should probably consider fixing them...the concepts are still correct, though, and that's what's important.

I need to count how many times I said "method" versus how many times I said "function".

10
The order in which you write function declaration matter in C/C++? O_o That sounds like an oversimplification...

That's intentional.  This is a Java oriented lesson.  I'm not particularly concerned with the specifics of other languages beyond generalized concepts.  I try to focus on conceptual detail to help the lessons translate to other languages through programming concepts.  But if I want to talk specifically about another language, that will have to be another lesson in itself.

11
Stargrasper's Java Basics Tutorial Series Whatever-I-Was-Calling-It-Like-A-Year-And-A-Half-Ago

I posted the last lesson on January 22, 2012.  A year-and-a-half later, I'm posting this one.  In that time, I've learned a few things about being a programmer.  Let's see if all I've learned reflects in this lesson!

Now, I really wanted to just dive into objects.  Reading the old lessons, I got really tired of treating them as magic.  So, we're not going to do that anymore...okay, yes we will.  But not as much.  Okay, we actually have to talk about one more thing first.  You need to understand methods before you can understand objects.

Lesson 0.6.whatever: Methods

Methods, known as functions or procedures in most other programming languages, allow you to encapsulate code in a manageable space that can be inserted into almost any part of your code just by calling the method.  Here's a secret; that thing called main?  Yeah, that's a method.  A method that's required by Java desktop applications.  It tells them where to start executing code.  If you're writing a Java applet, the rules are different.  Thank you Sun Microsystems, now owned by Oracle, for a consistent environment.

So lets look at a tiny bit of code, shall we?
Code: [Select]
public static void main(String args[]) {
System.out.println("Hello World!");
}

The line public static void main(String args[]) is the method signature.  This means the application tells the compiler what to expect for input and output for that function; as well as other information on how the method can be called.  The public keyword reflects visibility of the method.  More on that in the objects lesson.  static and void are two more keywords that I'll get to shortly.  What we really want to talk about is main.  This is the name of the method.  What's inside the parenthesis tell the method what input to expect.  In this case, a String array.  This array is actually for command line argument.  If you're using an IDE, then getting at the option to pass command line arguments is pretty tricky most of the time.  If you're actually running from the command line, then it's trivial, naturally.  We talked about Strings and arrays previously, so you can review previous lessons or ask questions if need be.  Everything between the opening curly brace ('{') and the closing curly brace ('}') is the method body.  The method body contains the code that will be run when a function is called.

Let's write a function!
Code: [Select]
public static void hello() {
System.out.println("Hello World!");
}

public static void main(String args[]) {
hello();
}

We have two methods here.  The hello and main methods.  As stated earlier, code execution begins at the beginning of the main method.  Other methods do not get called unless you or an object call them.  How do you call them?  You say their name, of course!  To call a function, you write the name of the function you wish to call followed by parenthesis.  If the function has any input, it will go inside the parenthesis.  Like this:
Code: [Select]
public static void hello(String motd) {
System.out.println(motd);
}

public static void main(String args[]) {
hello("Hello World!");
}

In the main method, you pass the text string "Hello World!" by placing it within the parenthesis of the function call.  In my teaching experience, this next part is a little confusing to people.  Maybe I'm bad at explaining it, but it's one of those things few people I tutored caught quickly.  Kind of like booleans.

The hello method takes in a string variable called motd as a parameter.  When calling the method, the value of the first and only parameter becomes the value of the variable that is the first parameter of the method.  So the value of motd becomes "Hello World!" in this example.  It is equivalent to writing:
Code: [Select]
String motd = "Hello World!";

Now more magic I've been glossing over.  That System.out.println?  That's a method, too.  It takes in a String object.  In our example, we take the String motd and pass in to println as a method parameter.  println is a part of the Java standard library, so we don't have to define it ourselves.  We don't even have to include any extra libraries.

Remember that void keyword we skipped a few minutes ago?  Methods are designed to perform some set of operations for you.  If you want it to just do it and be done, like if we want it to print something, we use void.  But what if we want a function to do something and then give us a result?  We have to have a return value.  In the method signature, we replace void with the data type we want to return.
Code: [Select]
public static void a() {
// do something...
}
public static int b() {
int result = 0;
// do something...
return result;
}
public static String c() {
String result;
// do something...
return result;
}

Here are three functions.  a does not return anything.  b returns an int.  c returns a String.  In order to do this, we simply use the return keyword.  Now, it is very important to remember, return ends the function.  void functions end when they reach the end of the function.  You are allowed to have multiple return statements.
Code: [Select]
public static int translate(String aString) {
switch(aString) {
case "a": return 0;
case "b": return 1;
case "c": return 2;
default:  return -1;
}
}

Do you remember how switch statements work?  The switch statement looks at the variable you pass it as a parameter and searches it's cases for that value.  If not found, it uses the default case.  A minor aside; as of Java 7, you can switch on integers, enumerations, and strings.  Do you see how there are four return statements?  If a return ends the function, does this method end after the return 0?  No.  Because if you fail that case statement, you'll never actually hit that particular return statement.  But as soon as you hit any of the return statements, it's over.  return that value from the function.  You can use return statements in void functions as well.  It's a useful way to terminate the function early.  For example:
Code: [Select]
public static void foo() {
if(aCondition) return;
// do something...
}

Look closely.  We used return without actually returning anything.  This works because the function expects us to return void, which as far as Java is concerned, should be nothing.  Not Object.  Not null.  Not anything.  In fact, trying is a compile-time error.

Let's go a little crazy with methods.
Code: [Select]

public static int add(int n1, int n2) {
int result = n1 + n2;
System.out.println(result);
return result;
}

public static int subtract(int n1, int n2) {
int result = n1 - n2;
System.out.println(result);
return result;
}

public static int multiply(int n1, int n2) {
int result = n1 * n2;
System.out.println(result);
return result;
}

public static int divide(int n1, int n2) {
int result = n1 / n2;
System.out.println(result);
return result;
}

public static int equation() {
int result = divide(multiply(add(5, 2), subtract(7, 3)), multiply(8, 2));
return result;
}

public static void basicMath() {
// I want to add 2 + 2
// I wish I had a function to add
// Oh wait, I DO
add(2, 2);

// I want to subtract 5 - 2
// I wish I had a function to subtract
// Oh wait, I DO
subtract(5, 2);

// I want to multiply 8 * 4
// I wish I had a function to multiply
// Oh wait, I DO
multiply(8, 4);

// I want to divide 9 / 3
// I wish I had a function to divide
// Oh wait, I DO
divide(9, 3);
}

public static void multiCalls() {
// 4 + 8 + 2 + 8 + 1 + 6 + 4 + 5
System.out.println(add(add(add(4, 8), add(2, 8)), add(add(1, 6), add(4, 5))));

// 6 + 4 - 4 * 8 / 2 + 4 / 9 - 4
// Note the integer division
// kekekekeke
System.out.println(subtract(add(subtract(add(6, 4), divide(multiply(4, 8), 2)), divide(4, 9)), 4));
}

public static void run() {
// I want a function that demonstrates
// basic mathematical operations
// using functions
// I wish I had a function to do that
// Oh wait, I DO
basicMath();

// I want a function that demonstrates
// calling functions multiple times
// I wish I had a function to do that
// Oh wait, I DO
multiCalls();

// I want to know (5 + 2) * (7 - 3) / (8 * 2)
// I wish I had a function to do that
// Oh wait, I DO
System.out.println(equation());
}

public static void main(String args[]) {
// I want the program to run
// I wish I had a function to run
// Oh wait, I DO
run();
}

Okay, that was a touch more than a little crazy.  But it was FunTM!  Because some of this is complicated, lets walk through it as painfully slowly as we possibly can.

Now, you can clearly see that there are a lot of functions.  We wrote nine functions for this application!  In Java, it turns out that it really does not matter what order you place the functions in, unlike other languages like C/C++.  Does that make it better or worse?  Not really.  There's benefits and drawbacks.  As far as order goes, I like to place the main method at the very bottom.  This is for the sole purpose of making it easy to find.  Sure, I could just use a search, but I hate wasting time.  For stylistic purposes, there is an order I like to use.  It will make more sense to discuss that in the objects lesson, however.  Still, style is a matter of personal preference and professional requirements.  If your employer says "Do it this way", you darn well better.

Remember that your code begins execution at the main method.  Comments get ignored.  As far as the compiler is concerned, there's only one line here.  A function call.  And what does it do?  It moves us up into the run method.

The run method has four function calls.  But wait, there's only three lines of actual code in there!  Well, look closely at the third one.  Do you see it?  We embedded the equation method call inside of the println method call.  But we're getting ahead of ourselves.  The first function call steps us into the basicMath function...which contains four more function calls.  All of which take two ints, perform an operation on them, then print and return the resultant int.  Let's just briefly step into the add function.  We get to the end of it.  There are no more method calls for us to follow.  Now what do we do?  Now we step back out to where the function in question was called.  At the end of that function, we step out to where basicMath was called and advance to the next function.

In multiCalls, for lack of a better name, we demonstrate that you can keep calling the same methods over and over again.  The input parameters take the values of whatever was passed in at that particular method call.  Furthermore, this demonstrates embedding functions.  Look closely.  The add function returns an int.  Now we could just say int foo = add(2, 2).  But because it returns an int, we can essentially treat it as though it is an int.  This lets us pass it as a parameter to the next call to add.  For example, add(add(2, 2), 2), adds (2 + 2) + 2.  After seven calls to add, we print the result.

Let's walk through the method calls quickly.  The first thing the compiler sees is the println.  But it can't tell what we want it to print until it calculates all other method calls.  The compiler next sees the far left add, but it cannot tell what it is adding until calculating other method calls.  Next add, and the compiler still cannot tell what it is adding without calculating more calls.  Finally, at the third add, the compiler has a function it can complete.  At the end of that call, the call is, effectively, replaced with it's return value.  The same is true with the next add.  With those two methods complete, the compiler backs off to the second add and completes it.  Then the compiler has to do it all over again for the last three adds.  When they've all completed, the first add can complete and the end value can be printed.

Now, the next one is a bit complex.  We can't just group the numbers into twos anymore because of order of operations.  I'll be honest, this one took me a bit of thinking.  Mostly because I would never, ever, actually do it this way.

Walking through this nightmare...you know what, let's just do an old fashion trace.
Code: [Select]
run println(subtract(add(subtract(add(6, 4), divide(multiply(4, 8), 2)), divide(4, 9)), 4))
run subtract(add(subtract(add(6, 4), divide(multiply(4, 8), 2)), divide(4, 9)), 4)
run add(subtract(add(6, 4), divide(multiply(4, 8), 2)), divide(4, 9))
run subtract(add(6, 4), divide(multiply(4, 8), 2))
run add(6, 4)
return add == (10)
run divide(multiply(4, 8), 2)
run multiply(4, 8)
return multiply == (32)
return divide == (32 / 2 = 16)
return subtract == (10 - 16 = -6)
run divide(4, 9)
return divide == (4 / 9 = 0)
return add == (-6 + 0 = -6)
return subtract == (-6 - 4 = -10)
return println(-10)

Woo!  That was kind of hard.  But if you don't understand what's happening in the code, tracing out what's happening can be a good, if somewhat slow, tool.  You could also make it trace for you by putting print statements at the beginning of every function call, but that does not force you to walk through your code and understand it as intimately.  Especially if you're using an IDE, you might also consider using the built-in debugger.  Somewhere out there is a version of GDB that works for Java.  You could use that as well; if you're coding on the command line.  Step into every function you wrote and step over everything else.  And watch the value of variables while you are doing that.

Do you understand embedding methods yet?  If not, come up with some random mathematical formula that utilizes only the basic four operations and try to trace that out.  You should be able to make a single line equation with function calls.

Moving on, we're still in the middle of the program.  We step into the equation function.  This is another, albeit much simpler, example embedding function calls inside other function calls.  Let's just look at it quickly.
Code: [Select]
add(5, 2) = 7
subtract(7, 3) = 4
multiply(7, 4) = 28
multiply(8, 2) = 16
divide(28, 16) = 1

Okay, that was fast, but by now you should be able to understand how that method works.  Be sure to ask if you're not following!

After printing the value of that function, the program ends.  If you run the complete application, you should get the following output:
Code: [Select]
4
3
32
3
12
10
22
7
9
16
38
38
10
32
16
-6
0
-6
-10
-10
7
4
28
16
1
1

Not much to look at, but this leads to a good exercise.  Trace through the code and work out where each of those values came from.

We talked about scoping, right?  Anything declared within a set of curly braces stops existing outside of those braces.  For purposes of scoping, input parameters are local to body of the method they are a part of.  This is why we can keep using the variable names n1 and n2 for so many different unique variables.

We need a slight break, so let's talk technique for a minute.  Look at the code.  Do you see how nearly everything is a function call?  And how the functions that are not calling other functions are just a few lines long?  I wrote this using one of my favorite programming techniques for large projects.  Wishful thinking.  In short, every time you want your code to do something, you make up a function call.  "I wish I had a function that does THIS.  Oh wait, I DO."  Eventually, you get to the end of your program and have a skeleton worked out.  At the end, start filling in functions the same way.  "I need a function that does THIS.  Oh wait, HERE'S one!"  At the end of the day, your code is a large number of function calls that eventually lead to a large number of functions that have only a few lines of code.  Let's walk through the example.

I'm in the main.  Well, it'd be nice if the main would run the application.  Let's write a function that does that.  End of main, end of application.  Great!  Let's start filling in our functions.  What do I want run to do?  Well, I want it to give examples of using function calls for basic mathematics for the purpose of demonstrating the basic manner of writing and calling functions.  Then I want it to demonstrate the more advanced calling of the same function many times and embedding functions.  Then I want one more simple embedded function example for good measure.  Three things that I want run to do.  Gee, I wish I had functions that did that.  Wait a second, I DO!  Okay, end of run.  Step out to main.  End of that.  End of application.  Time to fill in functions again.  basicMath.  I want it to add, subtract, multiply, and divide with functions; both to demonstrate calling functions and to demonstrate writing them.  I wish I had functions that performed each of those operations.  Wait, I DO!  Step out.  Step into multiCalls.  I just want it to string together a bunch of adds and then show a complicated equation, using functions.  I could, and with wishful thinking, probably should, make that two functions.  I elect not to do that because I can actually do both with a single line.  And not a stupid single line like in an earlier lesson where I made the entire program one line.  This is a single line that, as an experienced programmer, I would write in any of my actual applications.  So I'll let this function break the rules of Wishful Thinking and just call the mathematical operator functions.  Step out.  Step into equation.  Not unlike the second example in multiCalls, this is a single line of mathematical operator functions strung together.  Assign it to a variable and return it.

I want you to do something.  It doesn't make sense right now, but don't question it for the moment.  Just do it.
Code: [Select]
public Methods() {

}

public static void main(String args[]) {
new Methods();
}

Why are we doing this?  To get us out of the main and the requirement that everything it touches be static.  What we've just done is instantiated a Methods object, but objects are still a bit magical.  We'll talk about them in the next lesson.  If I ever finish this one.  From here, just pretend that Methods is the main function for the time being.

This is actually something I do a lot when I'm coding Java applications.  Frequently, if I don't have a driver class, I use the main object to contain the main method.  Java requires that if you are in a static function, you can only call other static functions.  This makes perfect sense once you understand the difference between static and non-static functions.  Unfortunately, we're only going to touch on that.  It will make more sense after we talk about objects.

We're now going to write a number of static methods.  Wait, what?  Yes, I want to show you how you would normally use static functions outside the main method.
Code: [Select]
public static int add(int n1, int n2) {
return n1 + n2;
}

public static int subtract(int n1, int n2) {
return n1 - n2;
}

public static int multiply(int n1, int n2) {
return n1 * n2;
}

public static int divide(int n1, int n2) {
return n1 / n2;
}

public Methods() {
Methods.add(1, 2);
Methods.subtract(3, 4);
Methods.multiply(5, 6);
Methods.divide(7, 8);
}

public static void main(String args[]) {
new Methods();
}

The static functions look a touch different than last time, but they actually work the same except they no longer print the result in the function.  The purpose of static functions is be able to call functions that are not attached to a specific instance of an object.  For instance, there's really no reason that the above functions need to be associated with an object.  They can stand up completely on their own.  We'll talk more on the differences when we talk about objects, but I wanted to at least touch on this here.  In order to call a static function from a non-static function, you need to say the name of the class that contains the function, a dot, and then the function name as normal.

I can't believe I'm about to touch on this, but as long as we're here, lets briefly look at recursion, confuse everyone to all Hell, and then move on and talk about it more some other time.  You know what they say about recursion.

"In order to understand recursion, you must first understand recursion."

Code: [Select]
public static int fibonacci(int n) {
if (n == 0)
return 0;
else if (n == 1)
return 1;
else
return fibonacci(n - 1) + fibonacci(n - 2);
}

public static void main(String[] args) {
for (int i = 0; i < 100; ++i) {
System.out.print(fibonacci(i) + " ");
}
}

Take a look at fibonacci.  In the else statement, the method calls itself.  Yes, this method is, in fact, calling itself.  This is recursion.  Anything you can do with loops, you can do with recursion.  And vise versa.  Usually one will be much cleaner than the other.  There's a lot of problems with recursively generating fibonacci numbers, but we'll ignore those for now.  If you are going to use recursion, you need to create what are called base cases.  They are what halt the recursion, similar to a loop's continuation conditions.  Remember how you can have an infinite loop?  Well, you can infinitely recurse as well.  Except when you infinitely recurse, your program crashes with a StackOverflowErrorIf you want to see that happen, run the following code.  I'm not responsible for any damage this causes.  In my eclipse, this instantly crashes with a StackOverflowErrorCan't imagine why. </sarcasm>
Code: [Select]
public static int overflow(int n) {
return overflow(n) + overflow(n) + overflow(n) + overflow(n)
+ overflow(n) + overflow(n) + overflow(n) + overflow(n)
+ overflow(n) + overflow(n) + overflow(n) + overflow(n)
+ overflow(n) + overflow(n) + overflow(n) + overflow(n)
+ overflow(n) + overflow(n) + overflow(n) + overflow(n)
+ overflow(n) + overflow(n) + overflow(n) + overflow(n)
+ overflow(n) + overflow(n) + overflow(n) + overflow(n)
+ overflow(n) + overflow(n) + overflow(n) + overflow(n)
+ overflow(n) + overflow(n) + overflow(n) + overflow(n)
+ overflow(n) + overflow(n) + overflow(n) + overflow(n)
+ overflow(n) + overflow(n) + overflow(n) + overflow(n);
}

public static void main(String[] args) {
overflow(Integer.MAX_VALUE);
}

You have to watch your base cases.  Usually these will be a few conditionals that stop you from recursing under certain circumstances.  Then just make sure that your recursion is always approaching your base cases.

It really is a good idea to split your code up into methods.  A single gigantic monolithic main tends to become unwieldy fairly quickly.  It also tends to lead to a very large about of duplicate code, which is typically a bad thing.  And there's other issues, but they will be easier to talk about after we discuss, you guessed it, objects.

* Somebody count how many times I wrote "function" when I was supposed to write "method".  Can tell I've been coding C/C++ and Python for the last year-and-a-half.

PS
I really wanted to talk about objects in this lesson.  But I just could not talk about objects without covering methods first.  And methods became a relatively lengthy lesson.  Then again, it's pretty much on par with my other lessons.

PPS
If anyone has any questions or comments, please feel free to express them.

PPPS
I think this might actually be one of my longest lessons.

12
...

Also, I should totally make a Java tutorial sometime.

I'll write more in the near-ish future...  I wanted to write something else before writing more Java.  If you also want to write Java lessons, then absolutely go for it.  I very strongly encourage you to do it.  But consider reading mine first and think how to differentiate yours.  Or don't read mine so you don't have any preconceived notion as to what you should do.  Either way would be productive.  Tutorials that say the same thing as has been said have limited usefulness, but a different point of view or focus can mean a lot.  For instance, a major focus for my tutorials has been conceptual detail.  But more importantly, don't just talk about doing it.  If you're interested in doing so, then actually do it.

...makefile tutorial...

That looks like a good overview of makefiles and reminds me again why I'm glad to use IDEs with compiler integration anytime I can.  From what I understand many large software development studios that deal with compiled languages have at least one person dedicated to makefile writing and maintenance.  I've seen some really nasty ones in my time, but fortunately haven't ever had to create or modify such a thing.

In the spirit of tutorials, I'm slightly tempted to try writing one that would help newcomers integrate V8 into C++ like I did last week.  The documentation I've found online was spread all over the place, was inconsistent and very confusing at the best of times.  If I knew I was doing things correctly I'd want to help alleviate that lack of knowledge on the internet, but I'm pretty sure I'm doing things redundantly and circuitiously in a few places.  And I haven't yet tried to implement the C++ side of object lifetime management when the JS garbage collector collects C++ linked objects.

Thanks for the compliment.  I just hope somebody here can actually use it.

If you want an exercise in makefiles, look at the makefile your IDE generates for you.  Being completely generated by the software, it's remarkably nightmarish.  I'm a little impressed they work.

If you think you can put knowledge into writing, try it.  Doing redundant or otherwise unusual things can be okay if it works.  If there's flaws, we'll likely see them and help correct them.

</kindof_pushy_encouragement>

13
Life Advice / Re: Considering anxiety medication
« on: July 12, 2013, 07:22:33 pm »
Modern mental health tends to be treated with drug therapy, with the average doctor appointments be 15 minutes to check and potentially change drugs.  But the first appointment isn't average.  It will probably be a longer appointment to talk about anxiety in general and get quite a bit of help.  Your doctor may refer you to a psychiatrist or other individual better trained to help you with this.  Oh, they'll probably give you drugs, but they'll tell you right off that drugs will do little to nothing on their own and behavioral therapy and exercises.

And do try to be careful about self-diagnoses.

14
Since I need a new excuse to procrastinate on my homework and to get myself into the practice of writing again, I thought I'd start with a long-promised brief lesson on makefiles.

Please be aware that you are expect to have a basic understanding of a *nix terminal for this lesson.

Stargrasper's Makefile Overview

Make is a Unix utility that was originally designed to streamline and automate the building process of applications.  We can also use it to automate an assortment of other tasks.  Commonly, these will be tasks such as cleaning up the workspace by deleting generated files or packaging the application for distribution.

In the simplest of circumstances, you can invoke Make by typing the following:
Code: [Select]
make

Make needs a set of rules to follow in order to understand what it is you are asking it to do.  The file that contains this information is known as a makefile.  When called with no other arguments, make will search for a file called GNUmakefile, makefile, or Makefile.  In that order.  Unless you tell it otherwise, if files by more than one of those names exists in the present working directory, make runs the first that it encounters.  So if makefile and Makefile are both in the present directory, make will run makefile, but not Makefile because it searches first for GNUmakefile, if not found, then makefile, if not found, then Makefile.

If, for whatever reason, you want make to run something other one of the default makefiles, you can do that.
Code: [Select]
make -f other_makefile

The -f argument tells make that you want to run a specific makefile; named whatever you want and located where you want.

Similar to a code file, a makefile can contains variables and commands.  But to know which commands to run when, the makefile has what are referred to as rules.  A rule consists of a rule name, a colon, and a space delimited list of prerequisites to run that rule.
Code: [Select]
rule_name : this is a list of prereqs

On the following line is the recipe, or command, that will be run when the rule is invoked.
Code: [Select]
application : main.c main.h
gcc -Wall -lm -o application main.c

Note that the command begins with a tab.  A lot of programmers use spaces instead of tabs while writing.  Make will not allow you to do this.  Thankfully, make will give a very clear error explaining what you've done wrong and how to fix it.  Also, be aware that you can run any command here that you can run in your terminal.  You only get one line, though.  You can run multiple commands.  There's a couple of ways.  You can have the rule run another rule through creative abuse of prerequisites.  You can also separate commands with a semicolon, just like you can in the terminal.
Code: [Select]
application : main.c main.h
cd src; make

And this is another trick you can use.  Running one makefile from another.  Just as you may split code into multiple files, you may split makefiles into multiple files by creatively running them all from a master makefile.  This has limited utility, but I've done it before.

You can also have rules call other rules.
Code: [Select]
application : main.o
gcc -Wall -lm -o application main.o

main.o : main.c main.h
gcc -c -Wall -lm -o main.o main.c

If multiple rules exist and you don't tell make otherwise, it runs the first rule it finds.  In this case, the application rule.  Then it looks at the dependencies and sees that it needs a file called main.o in order to run this rule.  Wait...what do you mean no file called main.o exists?  Well, I have a rule that creates such a file, so lets run that.  Simply put, if a rule calls for a file that doesn't exist, make looks for a rule that creates such a file.  If such a rule exists, that is invoked.  If such a rule does not exist, then make errors out.

You can also elect to run specific rules.  For example:
Code: [Select]
make main.o

This would invoke specifically the main.o rule.  I could also explicitly call the application rule, but because it is the default rule, this isn't necessary.

Now, lets look at this makefile.
Code: [Select]
application : util.o main.o
gcc -Wall -lm -o application util.o main.o

main.o : main.c main.h
gcc -c -Wall -o main.o main.c

util.o : util.c util.h
gcc -c -Wall -o main.o main.c

When make runs, it runs the default rule, application.  It sees that it needs util.o before it can run the rule, recognizes the file does not exist, and looks for a rule that creates the file.  Then it suspends running application to run util.o and checks that util.c and util.h exist.  It compiles the util.o object that returns to the application rule and sees that main.o doesn't exist.  So it suspends application again to run the main.o rule.  Then it finally is able to run the application rule.

Now, if you're familiar with gcc/g++, you've probably noticed my last couple examples have done all my linking in the default rule and splitting compiling in other rules.  Why would you want to do that?  Because make is smart.  If the file created by a rule exists when that rule is run, make checks the time stamp of the created file against the time stamps on the prerequisites.  If the created file's time stamp is newer, that is, none of the prerequisite files have changed since the created file was created, it will not run that rule.  So, if I compile the above with make and later change main.c, but nothing else, make will only recompile main.o and application.  Because the prerequisites for util.o have not changed since it was last compiled, it is unnecessary to recompile the file.  This means next to nothing in the baby programs you're probably writing.  It might mean something if your application has hundreds of source files, though.  This is one of the things that makes make really shine.

You can also touch a file.  Touch is a *nix command that creates a file if it does not exist or updates the timestamp if it does.  This has a handful of uses, depending what you want to touch.  It's a bit like updating a file without actually changing anything.

Now, we can create a rule that isn't compiling code.  Lets say we want to package our code.
Code: [Select]
app.tar : main.c main.h util.c util.h
tar -cvf app.tar main.c main.h util.c util.h

A tar file, sometimes called a tarball, or tape archive, places several files into one file originally intended for backup to a cassette tape.  Just as an aside, the reason you never see .tar files is that the .tar format does not compress data.  You frequently see either .tar.gz or .tar.bz or some other format appended to the end of files because they are compressed using some specific compression algorithm.

As you can see, we can create an archive file for distribution of our code.  Just like compiled files, if the prerequisites, anything in our code, have changed, the archive will be re-created when the rule is run.

Another creative technique we have is rules that perform an action that doesn't actually create anything.  Let's take a look.
Code: [Select]
.PHONY
clean :
\rm -rfv *.o application

A rule that does not create anything is called a phony rule.  Turns out, gnu make won't complain if you don't tell it a rule is a phony rule.  In this case, our clean rule deletes all of our object files and the application executable.  Why do this?  Before you distribute a new version of your program, traditionally, you whip out the object files and recompile it from scratch.  Why?  Sometimes weird things happen in files.  Something changes somewhere that affects a file in a manner you did not expect.  Recompiling completely can either eliminate or create the problem.  If the change is coming from a file you didn't expect, it likely isn't listed as a prerequisite for the first file and thus the first file isn't recompiled when the second is altered.  Most of the ways of avoiding this situation entirely are fairly advanced techniques.  For example, it's possible to ask gcc to work out and tell you all of the dependencies of a given code file.  We'll worry about doing that some other time.

For now, lets finally get to variables.  There are three types of variables that make will acknowledge.  User-defined variables types are recursively expanded variables and simply expanded variables and what are referred to as automatic variables.  Recursively expanded variables are expanded any time the variable is referenced using the variables that are referenced.  Every time that a recursively expanded variable is referenced, the variable is expanded again to find the current value of the variable at the time it is called.  Simply expanded variables are expanded once, meaning that they cannot actually contain any references to other variables.  At the time of definition, variables are expanded and the simply expanded variable contains the value of the variable referenced at the time it was referenced.  When I say "expanded", I mean, if the variable references another variable, it will resolve the value of that variable.  Recursively expanded variables are set with an '=' sign, while simply expanded variables are set with a ":=".

Automatic variables are set for you by make.  Most of these are extremely cryptic and I'm only going to cover a small number of them.
Code: [Select]
CC := gcc
CFLAGS := -c -Wall
LFLAGS := -Wall -lm
APP := application
SRC := *.c
SRCHDR = $(SRC):.c:.h)
OBJ := $(addprefix .obj/, $(SRC:.c:.o))

all : $(OBJ)
$(CC) $(LFLAGS) -o $(APP) $^

.obj/%.o : %.c %.h
$(CC) $(CFLAGS) -o $@ $^

app.tar : $(SRC) $(SRCHDR)
tar -cvf $@ $^

.PHONY
clean :
\rm -rfv *.o $(APP)

If THIS doesn't look cryptic to you, then you probably don't need to be reading this.  Lets just walk through this...

At the top are my variable declarations.  You'll notice I do a lot of simply expanded variables.  No particular reason than habit in this case.  SRCHDR and OBJ are my only variables that need to be expanded and because I never change SRC, which is what they depend on, the expansion never needs to change.

CC is a traditional variable for your C compiler.  The equivalent traditional C++ variable is CXX.  CFLAGS are your compile flags, while LFLAGS are your linker flags.  Remember that compilation is three steps; compile, link, assemble.  We're splitting them up a little here.  The equivalent C++ tradition is CXXFLAGS and LXXFLAGS.  These are fairly common variables and it makes life easier to make them variables for ease of access and alteration.  For instance, if took this makefile and tried to run it on Windows with cygwin or mingw, it'll spit an error in your face.  Well, with a variable, it's easy to fix.  Change gcc to cygwin/mingw/insert-compiler-here.  And it changes it everywhere.  Your compiler and linker flags can be just as easily modified.  Just don't remove the -c from your compiler flags unless you want an error during linking.

The APP variable is simply the name of the executable that will be compiled.  Now really, for what reason would the name of the final binary need to be a variable?  It's, in all likelihood, getting called twice.  During final linking and assembly...and during cleaning.  Well, there's an old saying: There is only one makefile.  Every other makefile ever to exist is a copy derived from the first makefile.  Nobody every writes these things from scratch.  Having such a variable will help re-usability when you want to copy the makefile to another project with a minimum of things that have to be changed.  Ah, the SRC variable.  The list of all source files.  If all of your source files are in the same directory as your makefile, this variable will scan them all for you.  Alternatively, if you want to put them somewhere else and don't want to fix the next two variables afterward, run this command in your terminal.
Code: [Select]
ls ./src/ | grep .c

That'll give you a list of all your .c files that you can copy and paste onto that line.  Not the most direct way of doing this, but probably the easiest.

The next variable, SRCHDR, is a little hack that takes everything in the SRC list and pastes it into the SRCHDR list, but replacing every .c with a .h.  This does assume that every .c file has a corresponding .h file and that no other .h files exist.  It can be modified accordingly if this isn't true.  For no reason, SRCHDR is also the only recursively expanded variable I have.  As stated earlier, in this example, nothing really needs to be recursively expanded and I'm trying to leave you with a makefile you can use, so I'm not adding too many theoretical examples.  Last variable is OBJ and boy is this magic.  We grab every element of the SRC list, pre-pend .obj/ to it, and then replace the .c with a .o.  This is an extremely useful technique that I strongly advise using because it allows a shortcut a little bit later.

You probably also noticed that you can't just use the name of a variable to directly call it.  To call a makefile variable, you use $(NAME) where NAME is the name of the variable in question.  As you can see, you also have limited ability to modify a variable within the call to it.

For the default rule, I usually just call it all because I usually use the default rule to say "build everything".  And since it depends on every .o, every .o needs to be compiled or recompiled.  The recipe line is a bunch of variable calls.  Back to that in a moment.

The second rule is where black magic is being used everywhere.  First, make allows different types of wildcards.  You saw above the * means "everything".  Or in the case we used it, "every that contains .c".  Technically, I guess we should have used '^*.c&'.  It's a regular expression thing.  I'll write a regex lesson some other time.  In reality, any wildcard that works in a bash shell, should work in a makefile.  Another type of wildcard make allows is %.  This is a special wildcard in that it can only be called on one thing at a time.  In our case, the all rule will fail when it finds a .o file that doesn't exist.  It sees a rule that can create one and replaces the % with the name of one of the .o files it needs.  Everywhere in that particular run of the rule, all of the % signs are replaced with the same thing.  And then come the next .o file, they're replaced with something else.  So this rule gets called for every .o file needed, and when it is, every % is replaced the same way.  So when we need main.o, every % is replaced with main, giving us main.o, main.c, and main.h.

On the next line, we call the compiler and compiler flags, but what are these weird signs at the end?  They're automatic variables, it turns out.  $@ is an extremely handy automatic variable that is equivalent to the name of the rule it is called in.  I usually name rules to be the file they output and then use $@ as the output name.  $^ is another handy rule that means all of the dependencies.  $+ is the same thing, but if dependencies are listed multiple times, these duplicates are preserved.  Sometimes useful for linking.  Whatever list you wrote on the dependencies above, that's what this is.  Similarly, $< is the first dependency listed.  Still similarly is $?, which is every dependency listed that is newer than the target.  I see limited use for this one.  There's more automatic variables, but these are the simpler ones.  $@ and $^ are the ones I tend to use all the time.

The default rule should make a bit more sense now. It says compile with the given linker flags a file called application using the following dependencies.  The tar rule should make as much sense as well.  Create a basic tar file called application.tar containing all the source and header files.  Clean should also pretty much make sense.  Remove all .o files and the executable.  The \ before rm means to run the base command, not your alias.  Mind, I usually alias rm to 'rm -rfv' anyway, but a lot of people will alias it, but in a different way.

Now that you understand the cryptic automatic variables and variable manipulation, this probably makes a lot more sense now.  And, while simple, this is very much a complete makefile.  I've used similar makefiles many a time.  In fact, this is exactly a makefile I've used a couple times except that I changed the SRC variable to be *.c instead of listing them explicitly, which probably is a good idea anyway.  In fact, do that instead.

Now, if you've ever used an IDE for a compiled language, like C/C++ or Java, you've noticed that they don't make you write makefiles or other build scripts.  They automatically generate them.  One of the ways this can be done is with autotools.  These include autoconf and automake.  For a bit more cross-platform solution, you could use cmake, which is capable of generating build scripts for many platforms, including VisualStudio and ordinary Unix makefiles.  For Java, there's ant and maven.  These sorts of tools become borderline necessary once your project balloons to a few dozen individual source files.  An explanation is probably beyond the scope of this overview.

Please be aware that while these are based on previous makefiles I've written, I didn't have enough spare code laying about to actually test everything.  Everything should work.  If for some reason you find something to not function as you would expect, please let me know.  If people want, I may expand on this at some point as it turns out it really is an overview that doesn't go that deeply into writing makefiles.  I encourage questions and comments and some form of acknowledgement that somebody besides me read this.

15
Which is one of my aspirations.  I'm a bit tired on studying right now, but eventually I want to go to grad school and eventually become a professor.  We'll see how motivated I get.

Also, I apparently haven't been posting in here enough.  Search is showing six pages of total posts by me.  It's a bit nostalgic.  It's also reminding me of how much of an idiot I can be.

Pages: [1] 2 3 ... 88