Skip to content

Add an exercise on basic types for the first day #518

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/build-exercises.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ jobs:
EXERCISE:
- NAME: "asan"
- NAME: "atomic"
- NAME: "basicTypes"
- NAME: "callgrind"
- NAME: "concepts"
- NAME: "condition_variable"
Expand Down
1 change: 1 addition & 0 deletions exercises/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ endif()
add_subdirectory( hello )
add_subdirectory( asan )
add_subdirectory( atomic )
add_subdirectory( basicTypes )
add_subdirectory( callgrind )
add_subdirectory( condition_variable )
add_subdirectory( constness )
Expand Down
2 changes: 2 additions & 0 deletions exercises/ExerciseSchedule_EssentialCourse.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ Day 1 - Basics Exercises

### Hello World (directory: [`hello`](hello), [CheatSheet](ExercisesCheatSheet.md#hello-world-directory-hello))

### Basic types (directory: [`basicTypes`](basicTypes), [CheatSheet](ExercisesCheatSheet.md#basic-types-directory-basictypes))

### Functions (directory: [`functions`](functions), [CheatSheet](ExercisesCheatSheet.md#functions-directory-functions))

### Control Structures (directory: [`control`](control), [CheatSheet](ExercisesCheatSheet.md#control-structures-directory-control))
Expand Down
4 changes: 4 additions & 0 deletions exercises/ExercisesCheatSheet.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ Basics Exercises

Just try to compile and run `./hello` to make sure that everything is set up correctly.

### Basic types (directory: [`basicTypes`](basicTypes))

The goal is to observe the behaviour of a few basic types, correctly employ integer and floating-point literals, and to be aware of conversions and the effect of operators in expressions with such types.

### Functions (directory: [`functions`](functions))

pass by copy / pass by reference
Expand Down
2 changes: 1 addition & 1 deletion exercises/Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
TESTDIRS = callgrind cppcheck header_units control hello modules move python smartPointers templates virtual_inheritance \
debug helgrind memcheck polymorphism race stl valgrind
debug helgrind memcheck polymorphism race stl valgrind basicTypes
NOCOMPILETESTDIRS = constness

solution:
Expand Down
15 changes: 15 additions & 0 deletions exercises/basicTypes/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Set up the project.
cmake_minimum_required( VERSION 3.12 )
project( basicTypes LANGUAGES CXX )

# Set up the compilation environment.
include( "${CMAKE_CURRENT_SOURCE_DIR}/../common.cmake" )
set( CMAKE_CXX_STANDARD 20 )

# Create the user's executable.
add_executable( basicTypes PrintHelper.h basicTypes.cpp )

# Create the "solution executable".
add_executable( basicTypes.sol EXCLUDE_FROM_ALL PrintHelper.h solution/basicTypes.sol.cpp )
target_include_directories( basicTypes.sol PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} )
add_dependencies( solution basicTypes.sol )
11 changes: 11 additions & 0 deletions exercises/basicTypes/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
all: basicTypes
solution: basicTypes.sol

clean:
rm -f *o *so basicTypes *~ basicTypes.sol

% : %.cpp PrintHelper.h
$(CXX) -g -std=c++20 -Wall -Wextra -o $@ $<

%.sol : solution/%.sol.cpp PrintHelper.h
$(CXX) -g -std=c++20 -Wall -Wextra -o $@ $< -I .
33 changes: 33 additions & 0 deletions exercises/basicTypes/PrintHelper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#pragma once

#include <bitset>
#include <iostream>
#include <iomanip>
#include <string>

#ifdef _MSC_VER
std::string demangle(std::string_view input) { return std::string{input}; }
#else
#include <cxxabi.h>
std::string demangle(std::string_view input) {
int status;
return abi::__cxa_demangle(input.data(), NULL, NULL, &status);
}
#endif

// This helper prints type and value of an expression
void printWithTypeInfo(std::string expression, auto const & t, bool useBitset = false) {
const auto & ti = typeid(t);
const std::string realname = demangle(ti.name());

std::cout << std::left << std::setw(30) << expression << " type=" << std::setw(20) << realname << "value=";
if (useBitset) {
std::cout << std::bitset<16>(t) << "\n";
} else {
std::cout << std::setprecision(25) << t << "\n";
}
}

// This macro both prints and evaluates an expression:
#define print(A) printWithTypeInfo("Line " + std::to_string(__LINE__) + ": "#A, A);
#define printBinary(A) printWithTypeInfo("Line " + std::to_string(__LINE__) + ": "#A, A, true);
79 changes: 79 additions & 0 deletions exercises/basicTypes/basicTypes.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#include "PrintHelper.h"

/* *************************************
* * Fundamental types and expressions *
* *************************************
*
* Tasks:
* ------
* - Compile the program and analyse the output of the different expressions
* - Discuss with other students or your tutor in case the result of an expression is a surprise
* - Fix the marked expressions by changing types such that they produce meaningful results
* - Answer the questions in the code
*/

int main() {
std::cout << "Using literals of different number types:\n";
print(5);
print(5/2); //FIXME
print(100/2ull);
print(2 + 4ull);
print(2.f + 4ull);
print(0u - 1u); // FIXME
print(1.0000000001f); // FIXME Why is this number not represented correctly?
print(1. + 1.E-18); // FIXME

std::cout << "\nUsing increment and decrement operators:\n";
int a = 1;
int b;
int c;
print(b = a++); // Q: What is the difference between a++ and ++a?
print(c = ++a);
print(a);
print(b);
print(c);

std::cout << "\nCompound assignment operators:\n";
int n = 1;
print(n *= 2); // Q: Is there a difference between this and the next line?
print(n *= 2.9);
print(n -= 1.1f);
print(n /= 4); // Q: Based on the results of these expressions, is there a better type to be used for n?

std::cout << "\nLogic expressions:\n";
const bool alwaysTrue = true;
bool condition1 = false;
bool condition2 = true;
print( alwaysTrue && condition1 && condition2 );
print( alwaysTrue || condition1 && condition2 ); // Q: Why does operator precedence render this expression useless?
print( alwaysTrue && condition1 || condition2 );
print(condition1 != condition1); // Q: What is the difference between this and the following expression?
print(condition2 = !condition2);
print( alwaysTrue && condition1 && condition2 );
print( alwaysTrue || condition1 && condition2 );
print( alwaysTrue && condition1 || condition2 );

std::cout << '\n';
print( false || 0b10 ); // Q: What is the difference between || and | ?
print( false | 0b10 );
printBinary( 0b1 & 0b10 );
printBinary( 0b1 | 0b10 );
printBinary( 0b1 && 0b10 ); // Q: Are the operators && and || appropriate for integer types?
printBinary( 0b1 || 0b10 );

std::cout << "\nPlay with characters and strings:\n";
print("a"); // Q: Why is this expression two bytes at run time, the next only one?
print('a');

char charArray[20];
char* charPtr = charArray;
charArray[19] = 0; // Make sure that our string is terminated with the null byte

print(charArray);
print(charArray[0] = 'a');
print(charArray);
print(charArray[1] = 98);
print(charArray);
print(charPtr);
// FIXME: Ensure that no unexpected garbage is printed above
}
81 changes: 81 additions & 0 deletions exercises/basicTypes/solution/basicTypes.sol.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#include "PrintHelper.h"

/* *************************************
* * Fundamental types and expressions *
* *************************************
*
* Tasks:
* - Compile the program and analyse the output of the different expressions
* - Discuss with other students or your tutor in case the result of an expression is a surprise
* - Fix the marked expressions by changing types such that they produce meaningful results
* - Answer the questions in the code
*/

int main() {
std::cout << "Using literals of different number types:\n";
print(5);
print(5/2.); //FIXME
print(100/2ull);
print(2 + 4ull);
print(2.f + 4ull);
print(0 - 1 ); // FIXME
print(1.0000000001 ); // FIXME Why is this number not represented correctly?
print(1.l+ 1.E-18); // FIXME

std::cout << "\nUsing increment and decrement operators:\n";
int a = 1;
int b;
int c;
print(b = a++); // Q: What is the difference between a++ and ++a?
print(c = ++a); // A: Whether it returns the previous or new value
print(a);
print(b);
print(c);

std::cout << "\nCompound assignment operators:\n";
float n = 1;
print(n *= 2); // Q: Is there a difference between this and the next line?
print(n *= 2.9); // A: Yes, the computation runs in float and is converted back to int
print(n -= 1.1f);
print(n /= 4); // Q: Based on the results of these expressions, is there a better type to be used for n?
// A: Probably yes, for example float

std::cout << "\nLogic expressions:\n";
const bool alwaysTrue = true;
bool condition1 = false;
bool condition2 = true;
print( alwaysTrue && condition1 && condition2 );
print( alwaysTrue || condition1 && condition2 ); // Q: Why does operator precedence render this expression useless?
print( alwaysTrue && condition1 || condition2 ); // A: "true || " is evaluated last. The expression therefore is always true.
print(condition1 != condition1); // Q: What is the difference between this and the following expression?
print(condition2 = !condition2); // A: The first is a comparison, the second a negation with subsequent assignment
print( alwaysTrue && condition1 && condition2 );
print( alwaysTrue || condition1 && condition2 );
print( alwaysTrue && condition1 || condition2 );

std::cout << '\n';
print( false || 0b10 ); // Q: What is the difference between || and | ?
print( false | 0b10 ); // A: a boolean operation vs. a bit-wise boolean operation
printBinary( 0b1 & 0b10 );
printBinary( 0b1 | 0b10 );
printBinary( 0b1 && 0b10 ); // Q: Are the operators && and || appropriate for integer types?
printBinary( 0b1 || 0b10 ); // A: Most likely not, because the integers are first converted to boolean

std::cout << "\nPlay with characters and strings:\n";
print("a"); // Q: Why is this expression two bytes at run time, the next only one?
print('a'); // A: Because the first one is a string, which is 0-terminated

char charArray[20];
// There are many ways to solve this, for example to use std::string and not manually manage the memory.
// However, if one really desires to manage a char array, one should at least initialise it with the 0 byte:
std::fill(std::begin(charArray), std::end(charArray), '\0');
char* charPtr = charArray;

print(charArray);
print(charArray[0] = 'a');
print(charArray);
print(charArray[1] = 98);
print(charArray);
print(charPtr);
// FIXME: Ensure that no unexpected garbage is printed above
}