-
Notifications
You must be signed in to change notification settings - Fork 68
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 #157. Fix #157
- Loading branch information
Showing
5 changed files
with
220 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 ) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 . |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |