C++ Workshop - Functions, Parameters, & Scope (Ch. 5)

Started by
45 comments, last by me_minus 14 years, 7 months ago

Welcome to the GDNet C++ Workshop – Ch. 5

For a complete introduction to this workshop, please look here. Workshop Overview This workshop is designed to aid people in their journey to learn beginning C++. This workshop is targeted at highly motivated individuals who are interested in learning C++ or who have attempted to learn C++ in the past, but found that without sufficient support and mentoring they were unable to connect all the pieces of this highly complex but powerful programming language. This is a 'guided' self-teaching C++ workshop. Each student is responsible for taking the time to read the material and learn the information. The community and tutors that arise out of this workshop are here for making the learning process run more smoothly, but are not obligated to baby-sit a person's progress. Because everyone will be working from the same textbook (Teach Yourself C++ in 21 days 5th Ed.), students may find it easier to get answers to the specific questions they might have. There is no minimum age requirement, and there is no previous programming experience required. Additionally, this workshop does not attempt to defend C++ as a language, nor does it attempt to demonstrate that C++ is either more or less useful then other programming languages for any particular purpose. People who intend to start a discussion about the differences between C++ and ANY other languages (except as are relevant to a particular discussion), are encouraged to do so elsewhere. This workshop is for educational, not philosophical discussions. Quizzes & Exercises Each week will have quizzes and exercises posted in the weekly threads. Please try and answer them by yourself. As well, please DO NOT post the answers to Quizzes and Exercises within this thread. Once it becomes acceptable to post the answers to quizzes and exercises, an additional thread will be created each week specifically for the purpose of posting quiz answers. If you try with reasonable effort but are unable to answer the questions or complete the exercises, feel free to post a clarification question here on the thread. Tutors, myself, or others will do the best we can to point you in the right direction for finding the answer.

Chapter 5 – Organizing into Functions

Introduction Good morning all! This week we will be covering chapter 5, which is approximately 30 pages, not counting the review stuff at the end of the chapter. The topic for this week is functions. In a way, this week will explore the basis of the early procedural languages such as C. We'll take blocks of code which may need to be executed repeatedly and we'll move it out into functions. Additionally, we'll now be able to take overly complex ideas and break them up into smaller chunks of thought, and then separate them into their own procedures. Finally, when combined with previous weeks we can use functions as handy containers for expressions that take in input, perform calculations, and return an output, thus allowing us to make a library of more complicated mathematical functions. Functions by themselves are not a difficult concept, but the implementation of functions within a programming language does provide subtle complexity that is important to understand. Some of those include arguments, the difference between function definitions and declarations, scoping of variables, function overloading, inline functions, and recursion. Many of these topics do not even become relevant until we introduce the concept of functions or procedures. Roughly half way through the week myself, tutors, or anyone else simply wishing to challenge their teammates learning C++ can post review and quiz questions in this thread. Please do not post the answers in this thread however, as a new thread will be created for that purpose. Outline of the Reading
  1. What is a function?
  2. Return Values, Parameters, and Arguments
  3. Declaring and Defining functions
  4. Execution of Functions
  5. Determining Variable Scope
  6. Parameters Are Local Variables
  7. Considerations for Creating Function Statements
  8. More About Function Arguments
  9. More About Return Values
  10. Default Parameters
  11. Overloading Functions
  12. Special Topics About Functions
  13. How Functions Work - A Peek Under the Hood

Good Luck!

[Edited by - jwalsh on May 30, 2007 5:30:48 PM]
Jeromy Walsh
Sr. Tools & Engine Programmer | Software Engineer
Microsoft Windows Phone Team
Chronicles of Elyria (An In-development MMORPG)
GameDevelopedia.com - Blog & Tutorials
GDNet Mentoring: XNA Workshop | C# Workshop | C++ Workshop
"The question is not how far, the question is do you possess the constitution, the depth of faith, to go as far as is needed?" - Il Duche, Boondock Saints
Advertisement
Why is this posted in "For beginners"? Any particular reason, or just forgetfulness?
Quote:Original post by DivineGod
Why is this posted in "For beginners"? Any particular reason, or just forgetfulness?

It's posted in For Beginners for easy access by those who need it most. Each week we post a new chapter thread which is mirrored in FB and the C++ Workshop. Previous chapter threads are then removed from FB and archived in the C++ Workshop Forum. I'm just waiting for Fruny to swap out the stickied thread.

Had you read the first paragraph of the post, you would have known that. [wink]

Cheers!
Jeromy Walsh
Sr. Tools & Engine Programmer | Software Engineer
Microsoft Windows Phone Team
Chronicles of Elyria (An In-development MMORPG)
GameDevelopedia.com - Blog & Tutorials
GDNet Mentoring: XNA Workshop | C# Workshop | C++ Workshop
"The question is not how far, the question is do you possess the constitution, the depth of faith, to go as far as is needed?" - Il Duche, Boondock Saints
Oh.. sorry 'bout that, then.

Suppose I read too fast to notice everything in the first place.
A comment and a question about Listing 5.5.

I've found a couple of mistakes: a typo in line 7 and there should be <using std::endl;> in line 21.

I know that a function can be used to do something (such as "void myFunction();") or it may be used to return a value (such as "int myFunction (int a, int b);") and, in this case, there should be a "return x;" at the end of the function prior to going back to the main code. Is it possible to return two values from the function back to the main code? For instance, if (in line 23), there's an assignment setting x to be 23, can both x (23) and y (10) be returned to main so that these values are printed in lines 13 and 14?

I know that it would be possible to call a function twice, returning x (23) and y (10) separately, but is it possible to return both values from one execution of the function?
Quote:Original post by CondorMan
A comment and a question about Listing 5.5.

I've found a couple of mistakes: a typo in line 7 and there should be <using std::endl;> in line 21.

I know that a function can be used to do something (such as "void myFunction();") or it may be used to return a value (such as "int myFunction (int a, int b);") and, in this case, there should be a "return x;" at the end of the function prior to going back to the main code. Is it possible to return two values from the function back to the main code? For instance, if (in line 23), there's an assignment setting x to be 23, can both x (23) and y (10) be returned to main so that these values are printed in lines 13 and 14?

I know that it would be possible to call a function twice, returning x (23) and y (10) separately, but is it possible to return both values from one execution of the function?


You can't really return 2 values. The easiest way (and the most correct usually) would be to just call the function twice for each variable.

<ADVANCED>
You can also pass variables to a function by reference to achieve this. Usually a variable is passed by value, meaning only the value of the variable s sent to the function, so if you modify the vaiable in the function, its value outside will not change. Example:

// Passing by value#include <iostream>void Increment(int value){    value++;}int main(){    int value = 5;    std::cout << "orginal value: " << value << std::endl;    increment(value);    std::cout << "value after function: " << value << std::endl;    return 0;}


Output would be:
original value: 5value after function: 5


To pass a value by reference, you use the '&' operator in the parameter list of the function.

// Passing by reference#include <iostream>// notice the '&' operatorvoid Increment(int & value){    value++;}int main(){    int value = 5;    std::cout << "orginal value: " << value << std::endl;    increment(value);    std::cout << "value after function: " << value << std::endl;    return 0;}


Output would be:
original value: 5value after function: 6


Passing by reference can be nice for some things and some types of items (such as file stream objects) must be passed by reference to work properly. I only use one variable, but you can easily have multiple parameters defined to be passed by reference.

</ADVANCED>
Sean Henley [C++ Tutor]Rensselaer Polytechnic Institute
Thank you. I suspected the best way would be to call the function twice but just wondered if there was a way of doing it which fell into the "advanced" area.

The advanced comment about passing by reference is interesting. I noticed this when I flicked through the book so I look forward to getting my teeth into that.
Quote:Original post by CondorMan
I know that it would be possible to call a function twice, returning x (23) and y (10) separately, but is it possible to return both values from one execution of the function?

Not in C++, but it's possible in other languages.

[Advanced]

It's really not advanced if there's language support, but I'm going to talk about tuples. The word comes from a corruption, I believe, of "multiple," as that's exactly what a tuple is: a multiple-value value. The following is a Python example:

"""apply will apply (sorry) the function supplied as the parameter fn to each value in the tuple   t, collating the results into a tuple and returning it."""def apply(fn, t):  results = []           # create an empty list  for v in t:    results.append(fn(v))  return tuple(results)  # now convert the populated list into a tuple and return it"""mul2 multiplies its parameter by 2 and returns the result."""def mul2(n):  return 2 * n...# the following is a demonstration of the functions above as would occur from the # Python interactive command line>>> apply(mul2, (3, 4, 5, 6, 7))(6, 8, 10, 12, 14)


In fact, tuples are so inherent in Python that you can use them anywhere:
return 3, 4

The function apply is unnecessary because Python supplies map, which does exactly the same thing; apply was written for illustrative purposes. Similarly, mul2 is unnecessary and can be replaced with a lambda function... but we won't get into that today. [smile]

The point is, with built-in language support, tuples become extremely natural types to use, and they allow for some very elegant programming. That said, C++ classes can be written (and have been) support tuples, however they have limits on the number of elements they can store (typically about 10 for casual applications). They are written using templates to enable them handle any type, but unlike language-supported tuples (as in Python, where you can write return 4, "blah", SomeFunction), these C++ tuple implementations also have restrictions with regard to the types they can handle.

Of course, something should have popped out at you. A tuple is a sequence type, and it's very convenient, but C++ has a few other sequence types, like std::vector (admittedly, I doubt that has been introduced in your text yet). When you need to return several pieces of data, one option is to aggregate them into a container and return the container as a single object. If they are all of the same type, then you can return a std::vector. If they are of diverse types, but the operation is important enough, you can create a new data structure to hold the various fields, and then create an instance and populate that in your function, again returning a single value.

Hey, I said it was advanced! [smile] Don't worry too much about it now. Just know that, as time goes on, you'll be able to do far more complex things with C++ functions than the limitations you see now might suggest.

[Advanced]
<Advanced>

(Hey, if the previous two posts are advanced, then I figure this qualifies as well.)

C++ does sort of support tuples natively, because you can make and return structures, but they have to have a specific size, and a type for each returned value. You cannot return an array, and returning a pointer is generally speaking a bad idea (what are you going to point it at? Keep in mind that the function's local variables are dead when the function returns; 'normal' returns only work because a copy is made, at least conceptually.)

If you are returning several values of the same type, (or, with a bit more effort, where all of them are objects with a common base class), you can return a container of those objects such as a std::vector of them.

Otherwise... if you are lazy ;) or if the structure won't be used anywhere else, you can rely on the standard library. If you only need two return values, you can return a std::pair. This is a templated structure that has two members, 'first' and 'second'.

// Sample implementation: this is what std::pair MIGHT look like in your// library implementation.namespace std {  template <typename T1, typename T2>  struct pair {    T1 first;    T2 second;  };  template <typename T1, typename T2>  std::pair<T1, T2> make_pair(const T1& x, const T2& y) {    std::pair<T1, T2> result;    result.first = x;    result.second = y;    return result;  }}


See, the standard library already gives an example of the technique ;) (The reason std::make_pair() exists is so that you don't have to specify the template types when you create a std::pair object. Functions are able to infer template types from their arguments, but class constructors can't.)

"Unfortunately" the standard library doesn't provide a generic structure for three or more items. You *could*, however, abuse std::pair to do it:

typedef std::pair<std::pair<int, int>, int> triple;triple giveMeThreeInts() {  return std::make_pair(std::make_pair(1, 2), 3);}int main() {  triple t = giveMeThreeInts();  int x = t.first.first;  int y = t.first.second;  int z = t.second;}


The Boost library, among others, provides a templated tuple class holding up to some particular number of items, which is (AFAIK) basically a wrapper for this technique.


<OPINION>

But you're probably better off declaring a struct in that case.

Anyway, there are many ways to handle "returning multiple things", but as always, just because you CAN do something doesn't mean you SHOULD. Whenever it looks like a multiple return is required, always think carefully about your approach, and try to at least keep things organized.

Personally I think it's a bad idea to use the return value as a "real" return value if you will also use an out-parameter (i.e. passing something by reference and modifying it). Go for all-or-nothing; sometimes you will decide to use the return value for an error code, instead.

Sometimes, taking the "return error code and modify reference parameters" approach is a good idea even for only one return value. This is especially the case if it helps out with template parameter inference ;) But usually, you'll want to throw an exception to indicate an error anyway. As well, a function that returns a value can be used to initialize a variable, whereas if you want a variable's initial value to come from a function that uses an out-parameter, then you have to declare the variable first and then call the function with the uninitialized variable. This is quite ugly.

Oh, and about the vector returns: Some people will tell you never to return a standard library container, but instead always use out-parameters for them. This is an optimization technique (which already marks it as evil ;) ), and assumes that the calling code might already have a vector instance handy, into which the called code will dump its stuff. However, this complicates the interface, firstly because the calling code might have to declare a local vector that it wouldn't have anyway, and secondly because you have to decide whether the called code will clear out any existing items, blindly trust that there are none, or deliberately assume there may be some and append to the existing set. Plus, it's quite likely not to help in terms of optimization anyway. :)

</OPINION>
</ADVANCED>
Heya all,

I just wanted to post a quick, friendly reminder that we're in chapter 5 of the book, and have not yet covered pointers, templates, or classes. Just be mindful of the readers. [smile]

When you post an "advanced" section, it should be a more detailed discussion of the information currently being covered, and should try not to bring in concepts which have yet to be introduced. If your "advanced" post requires significant info about features we've not yet covered its a good indication the post is premature and should be saved for later.

Sorry for the interruption. Please continue. [wink]

Cheers!
Jeromy Walsh
Sr. Tools & Engine Programmer | Software Engineer
Microsoft Windows Phone Team
Chronicles of Elyria (An In-development MMORPG)
GameDevelopedia.com - Blog & Tutorials
GDNet Mentoring: XNA Workshop | C# Workshop | C++ Workshop
"The question is not how far, the question is do you possess the constitution, the depth of faith, to go as far as is needed?" - Il Duche, Boondock Saints

This topic is closed to new replies.

Advertisement