Skip to content
Snippets Groups Projects
Commit 116decdd authored by Sandipan Mohanty's avatar Sandipan Mohanty
Browse files

Add first day notebooks

parent 8e76643f
No related branches found
No related tags found
No related merge requests found
%% Cell type:code id:f9bbdd7a-2dc4-4f0a-b4b6-2eeaecbfec05 tags:
``` C++
#pragma cling add_include_path("/p/project/training2312/local/include")
#include <iostream>
#include <string>
#include <cmath>
using namespace std;
```
%% Cell type:markdown id:international-german tags:
## Blocks
We can group a number of statements into a block by simply enclosing them inside a pair of braces `{` and `}`. Blocks are convenient to group statements together. For instance,
```c++
if (x > 1) {
x = sqrt(log(x));
std::cout << "x decreased to " << x << "\n";
} else {
x = 1;
std::cout << "x clamped to 1\n";
}
```
We wanted to execute two statements when a condition is true and 2 different statements when it is false. Note how the statements are grouped together using the braces. Although frequently C++ programmers use indentation to make such groupings easy to see, the **indentation has no syntactic meaning** in C++. The start and end of blocks is marked by `{` and `}` alone. Blocks can be created inside other blocks. But blocks may not overlap partially. The usual rules of opening and closing of nested parentheses apply.
The top level block is always the body of a function.
Although blocks are frequently used to organize code around `if` statements, loops etc., blocks can stand on their own. Anywhere in a function,
One of the most interesting properties of blocks in C++ is that variables declared inside a block expire at the closing `}` of the block. This means, the any name-object link we made inside a block is completely invisible outside it, as if it never existed.
%% Cell type:code id:brown-coalition tags:
``` C++
{
string ship{"Rocinante"}, destination{"Tycho"};
cout << ship << " is approaching " << destination << "\n";
}
// First run this cell as it is. Then uncomment just the next line and try again.
// cout << ship << "\n";
// You will see errors reported by the compiler, because the variable ship is not defined outside the block where it was introduced.
// Now, re-insert the comment markers // and uncomment the following two lines instead
int ship{4}, destination{11};
std::cout << ship << "\t" << destination << "\n";
// Since the variable names ship and destinations were not defined outside the block at the top, we were free to define and use
// them for any purpose we need! Here we created integers with those names.
```
%% Cell type:markdown id:honey-aspect tags:
```c++
if (argc > 1) {
auto N { stoi(argv[1]) }; // N is an integer
} else {
string N { "Navoo" }; // N is a string
}
cout << N << "\n"; // Error! N is not defined.
```
You can define any variables inside a block with any name which is not used to define another variable in that block. But that definition has no bearing on what goes on outside that block. Try the above!
%% Cell type:code id:military-danger tags:
``` C++
{
auto argc = 2;
if (argc > 1) {
auto N { stoi("224") }; // N is an integer
} else {
string N { "Navoo" }; // N is a string
}
// cout << N << "\n"; // Error! N is not defined.
// What (declarations) happens in a block, stays in that block
}
```
%% Cell type:markdown id:floating-sociology tags:
## Scope of a declaration
Variables can have a finite well regulated lifetime, after which the name of the variable can be reused for something else, e.g., another variable of any type. A **scope** is
- for a variable declared in a block, the scope begins with its declaration and ends at the closing `}` of the block
- for a variable declared in the header of a for loop, its scope is the loop body
- for variables declared along with the condition in an `if` statement: both `if` and `else` part of the conditional branch
- for variables declared in the function header, the scope is the function body (later!)
A variable declared in a scope exists from the point of declaration till the end of the scope. If the program exits the scope, the variable expires, and its name may be reused.
%% Cell type:code id:humanitarian-carter tags:
``` C++
int i = 0, j=1;
while (j < 1000) {
// if (i > 0) cout << "old value of sum = " << sum << "\n";
// If you uncomment the above, it will be an error
auto sum = i + j; // scope of sum begins here
i = j;
j = sum;
cout << i << "\n";
} // scope of sum ends here. Even when we jump back into the loop, sum would be a free name
```
%% Cell type:markdown id:sixth-intention tags:
```c++
for (int i = 0; i < N; ++i) {
//counter i defined only in this "for" loop.
}
double newval=0;
for (int i = 0; i < N; ++i) {
// The counter i here is a different entity
if (newval < 5) { // same newval as 3 lines earlier
string fl{"small.dat"};
// do something
} // fl dies here!
newval=...;
cout << fl << ’\n; // Error! The name fl is not defined here!
}
int fl=42; // ok
if (auto f=filename; newval < 5) {// C++17
// f is available here
} else {
// f is also available here
}
```
Variables declared outside any limited scope are called "global" variables, and they live until the program itself ends execution.
%% Cell type:markdown id:stretch-bearing tags:
### Name visibility in nested blocks
Any names visible in the program at the point we start a new block are visible throughout that block, unless *shadowed* by declarations in that block.
%% Cell type:code id:reduced-warehouse tags:
``` C++
int s1{1}, s2{2}, s3{3};
if (s1 < s3) {
// s1, s2 and s3 are visible here, because they were visible at the opening { of this block
cout << "line 3: " << s1 << "\t" << s2 << "\t" << s3 << "\n";
std::string s3{"umbrella"}; // A new block-local s3 comes in and "shadows" the outside s3
cout << "line 5: " << s1 << "\t" << s2 << "\t" << s3 << "\n"; // s1, s2 from before, s3 from the block
cout << "line 6: " << "s3 has size " << s3.size() << "\n"; // every time you use s3 till the closing } of the block, you will see the new, block local s3
} // The s3 declared inside the block dies here. Shadow lifts!
cout << "line 8: " << s1 << "\t" << s2 << "\t" << s3 << "\n";
```
%% Cell type:markdown id:focal-material tags:
## Namespaces
So far, in some of our introductory notebooks, we have been able to print things by simply writing `cout << x`. This is because of the statement `using namespace std;` in the "you will understand later" cell at the start. Usually you would need to write `std::cout << x`. In order to understand how that works, we have to understand the concept of a `namespace` in C++.
The following is a conversation I never had, but I could have had!
### Imaginary conversation...
A: "Thomas Müller is in this building."
B: "The football player ?"
A: "Yes, I guess, he plays football. Sometimes. Quite possibly."
B: "In the German national team ?"
A: "No, not that one. I meant the scientist working in the computational science (CS) division at the Jülich Supercomputing Centre (JSC)."
B: "So, you meant, JSC :: CS :: Thomas Müller !"
- A **namespace** is a named context in which variables, functions etc. may be defined
- `::` is called the **scope resolution operator**
- The statement `using namespace blah` imports all the names in the namespace `blas` into the current scope
```c++
// Somewhere in the header iostream
namespace std {
ostream cout; // Definition of a variable called cout with the useful property that lets us write things on the terminal
// << is an operator in C++, like +, -, *, /
// When we use << with a variable of type ostream on the left and something else on the right,
// that other thing gets written to the screen. ostream is a library provided class, and you will
// see, we can control the behaviour of operators when class types are involved. Later!
// The definition of the variable cout sits inside the namespace std
}
// In your program ...
#include <iostream>
// A variable called std::cout is now visible
auto main() -> int
{
{
using namespace std;
// All names defined in the namespace std are visible. Therefore std::cout can now be
// addressed by its plain name, cout
cout << __func__ << "\n";
} // Visibility of the names imported by the using namespace declaration ends here!
int cout = 0; // This cout is just an integer
for (cout = 0; cout < 5; ++cout)
std::cout << "Counter = " << cout << ’\n;
// Above, plain cout is an integer,
// but std::cout is an output stream
// The syntax to refer to a name
// defined inside a namespace is:
// namespace_name::identifier_name
}
```
%% Cell type:markdown id:stupid-madonna tags:
Let's explore namespaces by creating some on our own.
%% Cell type:code id:retained-difference tags:
``` C++
#include <iostream>
#include <string>
namespace UnitedKingdom {
std::string London{"Big city"};
}
namespace UnitedStates {
std::string London{"Small town in Kentucky"};
}
using namespace UnitedKingdom;
// You can not rerun this cell without restarting this kernel. That's why we isolated
// the namespace part to its own notebook.
```
%% Cell type:markdown id:liable-sierra tags:
The reason why repeated execution of the above cells causes issues in the interpreter is that all definitions in a namespace are "global". Namespaces can not be declared in block scope. In a real C++ header or source file, you would put namespace definitions outside function bodies. Namespaces can also be reopened to add new definitions. That's why, once we execute the above cell, variables like `London` get defined, and those definitions last for the duration the kernel in this interpreter stays alive. When running it a second time, `cling` notices that we are trying to define, for example, another variable called `London` in the same namespace. Namespace variables being global, the previous definition is still active, and can not be replaced. And hence the error.
%% Cell type:code id:educational-effort tags:
``` C++
std::cout << London << '\n';
std::cout << UnitedStates::London << '\n';
```
%% Cell type:markdown id:italian-theology tags:
Let's reopen the `UnitedKingdom` namespace and add a new variable to it.
%% Cell type:code id:meaning-toronto tags:
``` C++
namespace UnitedKingdom {
std::string Cambridge{"University town"};
}
```
%% Cell type:code id:addressed-correspondence tags:
``` C++
std::cout << UnitedKingdom::Cambridge << "\n" << UnitedKingdom::London << "\n" << UnitedStates::London << "\n";
```
%% Cell type:markdown id:fresh-preserve tags:
The same name can appear in different namespaces without a "name clash". Only if we try to import multiple namespaces each defining the same symbol do we have a name clash:
%% Cell type:code id:heavy-frame tags:
``` C++
// If you uncomment the following lines and run this cell, you would have to restart the kernel
//using namespace UnitedStates;
//std::cout << London << "\n";
```
%% Cell type:markdown id:coupled-radical tags:
Namespaces can be nested to arbitrary depth.
%% Cell type:code id:compatible-bobby tags:
``` C++
namespace UnitedStates {
namespace KY {
std::string London{" in Kentucky"};
}
namespace OH {
std::string London{" in Ohio"};
}
}
```
%% Cell type:code id:deluxe-night tags:
``` C++
std::cout << UnitedStates::OH::London << "\n";
```
%% Cell type:markdown id:adaptive-interstate tags:
Specifying multiple nested namespaces can get tidious, with long names followed by :: and more names. But it is possible to create shorthand aliases for long nested namespace names.
%% Cell type:code id:danish-cabin tags:
``` C++
#include <iostream>
namespace USOH = UnitedStates::OH;
std::cout << "London is " << USOH::London << '\n';
// This is valid code, has been valid since C++11. But the cling interpreter does not support this.
// The error message you get is also meaningless. Observe the syntax above and use it.
```
%% Cell type:code id:linear-sheet tags:
``` C++
```
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment