Skip to content

Commit 5658dae

Browse files
committed
Add an operator and basic types exercise for the first day.
This is an attempt at deepening the understanding of type promotions and operators for basic types. This should address most of the ideas in issue hsf-training#157. Fix hsf-training#157
1 parent 36caa05 commit 5658dae

File tree

5 files changed

+219
-0
lines changed

5 files changed

+219
-0
lines changed

exercises/basicTypes/CMakeLists.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Set up the project.
2+
cmake_minimum_required( VERSION 3.12 )
3+
project( basicTypes LANGUAGES CXX )
4+
5+
# Set up the compilation environment.
6+
include( "${CMAKE_CURRENT_SOURCE_DIR}/../common.cmake" )
7+
set( CMAKE_CXX_STANDARD 20 )
8+
9+
# Create the user's executable.
10+
add_executable( basicTypes PrintHelper.h basicTypes.cpp )
11+
12+
# Create the "solution executable".
13+
add_executable( basicTypes.sol EXCLUDE_FROM_ALL PrintHelper.h solution/basicTypes.sol.cpp )
14+
target_include_directories( basicTypes.sol PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} )
15+
add_dependencies( solution basicTypes.sol )

exercises/basicTypes/Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
all: basicTypes
2+
solution: basicTypes.sol
3+
4+
clean:
5+
rm -f *o *so basicTypes *~ basicTypes.sol
6+
7+
% : %.cpp PrintHelper.h
8+
$(CXX) -g -std=c++20 -Wall -Wextra -o $@ $<
9+
10+
%.sol : solution/%.sol.cpp PrintHelper.h
11+
$(CXX) -g -std=c++20 -Wall -Wextra -o $@ $< -I .

exercises/basicTypes/PrintHelper.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#pragma once
2+
3+
#include <bitset>
4+
#include <iostream>
5+
#include <iomanip>
6+
#include <string>
7+
8+
#ifdef _MSC_VER
9+
std::string demangle(std::string_view input) { return std::string{input}; }
10+
#else
11+
#include <cxxabi.h>
12+
std::string demangle(std::string_view input) {
13+
int status;
14+
return abi::__cxa_demangle(input.data(), NULL, NULL, &status);
15+
}
16+
#endif
17+
18+
// This helper prints type and value of an expression
19+
void printWithTypeInfo(std::string expression, auto const & t, bool useBitset = false) {
20+
const auto & ti = typeid(t);
21+
const std::string realname = demangle(ti.name());
22+
23+
std::cout << std::left << std::setw(30) << expression << " type=" << std::setw(20) << realname << "value=";
24+
if (useBitset) {
25+
std::cout << std::bitset<16>(t) << "\n";
26+
} else {
27+
std::cout << std::setprecision(25) << t << "\n";
28+
}
29+
}
30+
31+
// This macro both prints and evaluates an expression:
32+
#define print(A) printWithTypeInfo("Line " + std::to_string(__LINE__) + ": "#A, A);
33+
#define printBinary(A) printWithTypeInfo("Line " + std::to_string(__LINE__) + ": "#A, A, true);

exercises/basicTypes/basicTypes.cpp

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
#include "PrintHelper.h"
2+
3+
/* *************************************
4+
* * Fundamental types and expressions *
5+
* *************************************
6+
*
7+
* Tasks:
8+
* ------
9+
* - Compile the program and analyse the output of the different expressions
10+
* - Discuss with other students or your tutor in case the result of an expression is a surprise
11+
* - Fix the marked expressions by changing types such that they produce meaningful results
12+
* - Answer the questions in the code
13+
*/
14+
15+
int main() {
16+
std::cout << "Using literals of different number types:\n";
17+
print(5);
18+
print(5/2); //FIXME
19+
print(100/2ull);
20+
print(2 + 4ull);
21+
print(2.f + 4ull);
22+
print(0u - 1u); // FIXME
23+
print(1.0000000001f); // FIXME Why is this number not represented correctly?
24+
print(1. + 1.E-18); // FIXME
25+
26+
std::cout << "\nUsing increment and decrement operators:\n";
27+
int a = 1;
28+
int b;
29+
int c;
30+
print(b = a++); // Q: What is the difference between a++ and ++a?
31+
print(c = ++a);
32+
print(a);
33+
print(b);
34+
print(c);
35+
36+
std::cout << "\nCompound assignment operators:\n";
37+
int n = 1;
38+
print(n *= 2); // Q: Is there a difference between this and the next line?
39+
print(n *= 2.9);
40+
print(n -= 1.1f);
41+
print(n /= 4); // Q: Based on the results of these expressions, is there a better type to be used for n?
42+
43+
std::cout << "\nLogic expressions:\n";
44+
const bool alwaysTrue = true;
45+
bool condition1 = false;
46+
bool condition2 = true;
47+
print( alwaysTrue && condition1 && condition2 );
48+
print( alwaysTrue || condition1 && condition2 ); // Q: Why does operator precedence render this expression useless?
49+
print( alwaysTrue && condition1 || condition2 );
50+
print(condition1 != condition1); // Q: What is the difference between this and the following expression?
51+
print(condition2 = !condition2);
52+
print( alwaysTrue && condition1 && condition2 );
53+
print( alwaysTrue || condition1 && condition2 );
54+
print( alwaysTrue && condition1 || condition2 );
55+
56+
std::cout << '\n';
57+
print( false || 0b10 ); // Q: What is the difference between || and | ?
58+
print( false | 0b10 );
59+
printBinary( 0b1 & 0b10 );
60+
printBinary( 0b1 | 0b10 );
61+
printBinary( 0b1 && 0b10 ); // Q: Are the operators && and || appropriate for integer types?
62+
printBinary( 0b1 || 0b10 );
63+
64+
std::cout << "\nPlay with characters and strings:\n";
65+
print("a"); // Q: Why is this expression two bytes at run time, the next only one?
66+
print('a');
67+
68+
char charArray[20];
69+
char* charPtr = charArray;
70+
charArray[19] = 0; // Make sure that our string is terminated with the null byte
71+
72+
print(charArray);
73+
print(charArray[0] = 'a');
74+
print(charArray);
75+
print(charArray[1] = 98);
76+
print(charArray);
77+
print(charPtr);
78+
// FIXME: Ensure that no unexpected garbage is printed above
79+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#include "PrintHelper.h"
2+
3+
/* *************************************
4+
* * Fundamental types and expressions *
5+
* *************************************
6+
*
7+
* Tasks:
8+
* - Compile the program and analyse the output of the different expressions
9+
* - Discuss with other students or your tutor in case the result of an expression is a surprise
10+
* - Fix the marked expressions by changing types such that they produce meaningful results
11+
* - Answer the questions in the code
12+
*/
13+
14+
int main() {
15+
std::cout << "Using literals of different number types:\n";
16+
print(5);
17+
print(5/2.); //FIXME
18+
print(100/2ull);
19+
print(2 + 4ull);
20+
print(2.f + 4ull);
21+
print(0 - 1 ); // FIXME
22+
print(1.0000000001 ); // FIXME Why is this number not represented correctly?
23+
print(1.l+ 1.E-18); // FIXME
24+
25+
std::cout << "\nUsing increment and decrement operators:\n";
26+
int a = 1;
27+
int b;
28+
int c;
29+
print(b = a++); // Q: What is the difference between a++ and ++a?
30+
print(c = ++a); // A: Whether it returns the previous or new value
31+
print(a);
32+
print(b);
33+
print(c);
34+
35+
std::cout << "\nCompound assignment operators:\n";
36+
float n = 1;
37+
print(n *= 2); // Q: Is there a difference between this and the next line?
38+
print(n *= 2.9); // A: Yes, the computation runs in float and is converted back to int
39+
print(n -= 1.1f);
40+
print(n /= 4); // Q: Based on the results of these expressions, is there a better type to be used for n?
41+
// A: Probably yes, for example float
42+
43+
std::cout << "\nLogic expressions:\n";
44+
const bool alwaysTrue = true;
45+
bool condition1 = false;
46+
bool condition2 = true;
47+
print( alwaysTrue && condition1 && condition2 );
48+
print( alwaysTrue || condition1 && condition2 ); // Q: Why does operator precedence render this expression useless?
49+
print( alwaysTrue && condition1 || condition2 ); // A: "true || " is evaluated last. The expression therefore is always true.
50+
print(condition1 != condition1); // Q: What is the difference between this and the following expression?
51+
print(condition2 = !condition2); // A: The first is a comparison, the second a negation with subsequent assignment
52+
print( alwaysTrue && condition1 && condition2 );
53+
print( alwaysTrue || condition1 && condition2 );
54+
print( alwaysTrue && condition1 || condition2 );
55+
56+
std::cout << '\n';
57+
print( false || 0b10 ); // Q: What is the difference between || and | ?
58+
print( false | 0b10 ); // A: a boolean operation vs. a bit-wise boolean operation
59+
printBinary( 0b1 & 0b10 );
60+
printBinary( 0b1 | 0b10 );
61+
printBinary( 0b1 && 0b10 ); // Q: Are the operators && and || appropriate for integer types?
62+
printBinary( 0b1 || 0b10 ); // A: Most likely not, because the integers are first converted to boolean
63+
64+
std::cout << "\nPlay with characters and strings:\n";
65+
print("a"); // Q: Why is this expression two bytes at run time, the next only one?
66+
print('a'); // A: Because the first one is a string, which is 0-terminated
67+
68+
char charArray[20];
69+
// There are many ways to solve this, for example to use std::string and not manually manage the memory.
70+
// However, if one really desires to manage a char array, one should at least initialise it with the 0 byte:
71+
std::fill(std::begin(charArray), std::end(charArray), '\0');
72+
char* charPtr = charArray;
73+
74+
print(charArray);
75+
print(charArray[0] = 'a');
76+
print(charArray);
77+
print(charArray[1] = 98);
78+
print(charArray);
79+
print(charPtr);
80+
// FIXME: Ensure that no unexpected garbage is printed above
81+
}

0 commit comments

Comments
 (0)