Skip to content

Commit

Permalink
Add an operator and basic types exercise for the first day.
Browse files Browse the repository at this point in the history
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 #157.

Fix #157
  • Loading branch information
hageboeck committed Apr 2, 2024
1 parent 36caa05 commit ceb9746
Show file tree
Hide file tree
Showing 5 changed files with 220 additions and 0 deletions.
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 .
34 changes: 34 additions & 0 deletions exercises/basicTypes/PrintHelper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#pragma once

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

#ifdef __MSVC__
std::string demangle(std::string_view input) { return 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
}

0 comments on commit ceb9746

Please sign in to comment.