Diese GitLab Instanz ist nicht für den produktiven Einsatz gedacht!
Bitte https://gitlab.ost.ch benutzen!
Diese Instanz ist nur nuch zur Datenmigration online.

Vielen Dank für euer Verständnis.

Commit a5751081 authored by Thomas Corbat's avatar Thomas Corbat
Browse files

update w14

parent b8a67553
No preview for this file type
# No Week 14 in FS20!
# Exercise 14 - Build Systems
In this exercise...
* ... you will set up CMake to be used on your system
* ... write a `Makefile` for your `BoundedQueue`
* ... create a *CMake* setup for your `BoundedQueue`
* ... adjust your project layout to follow the best-practices shown in the lecture
# 1. Setting up CMake
Depending on your OS, install *CMake* on your system:
## Installation on Windows
Download the *CMake* installer from the [CMake website](https://cmake.org/download/). During installation, make sure to select the option to add *CMake* to your PATH!
## Installation on macOS
You should be able to install *CMake* via Homebrew.
## Installation on Linux
Install *CMake* using your distribution's package manger.
## Testing your installation
After installing *CMake*, test that your setup worked using the `w14_03_my_app_cmake` project from the `lecture_templates` folder:
1. Using your terminal, change the working directory to your copy of the example.
2. Create a new directory called `build` inside the example directory
3. Enter this new directory.
4. Invoke `cmake ..` to create a new set of build scripts for the project. ***NOTE:*** On Windows, you will have to add the command line parameter `-G"MinGW Makefiles"` to ensure *CMake* picks up the correct compiler!
5. Invoke `cmake --build .` to build the project.
If everything worked successfully, you should be left with an application called `my_app` (or `my_app.exe` if you are on Windows) that should produce the message "This is my awesome app!" when run.
# 2. A Makefile for `BoundedQueue`
The directory `w12_template_02_BoundedQueue` contains a flattened version of the test suite you used when building your `BoundedQueue`. Your task is to write a Makefile to build an executable called `BoundedQueueTests` from the provided sources. Use an approach similar to the one shown in the lecture when we discussed *Pattern Rules*. Specifically:
* Use a variable called `OBJECTS` containing all the object files you need to produce the executable
* Use a pattern rule to generalize the compiler invocation required.
* Remember:
* We are using C++17 features as well as threading. Make sure your compiler invocations use the correct flags!
* The CUTE headers are in the subdirectory `cute`. Make sure to instruct your compiler to look for headers files in this directory!
Afterwards, build your project using `make`.
## 2++ (Optional/Advanced)
Specifying the list object files manually is rather cumbersome. Can you figure out a way to have `make` find the sources/determine the object files for you? (Hint: the `make` functions `wildcard` and `patsubst` are your friends!)
# 3 CMake for `BoundedQueue`
Use the same sources as in the previous exercise, but this time around, your goal is to provide a *CMake* configuration for your project. Create a file called `CMakeLists.txt` in the root directory of your source code containing a configuration that covers the following points:
* Make sure that C++ is the only enabled language for your project.
* Again, we are using C++17. Set the correct *CMake* variable to the appropriate value
* We want to use standard C++. Make sure that non-standard extensions are disabled
* Remember to tell *CMake* that the `cute` directory contains header files required by our source code.
* Add an executable called `BoundedQueueTests` that is build using all `.cpp` files in the project.
* Ensure that your executable is linked against the `pthread` library.
Build your project using the commands discussed in the lecture.
## 3++ (Optional)
In the lecture, you have seen that *CMake* also ships with *CTest*. Extend your `CMakeLists.txt` so that you can use the `ctest` command to run your tests.
# 4 Project Structure
Having all files in a single directory gets messy fast. Adjust the directory hierarchy of your project according the guidelines presented in the lecture. Here are some things to keep in mind:
* You will need to adjust your `CMakeLists.txt` accordingly to reflect the new paths your resources live at.
* Headers, sources and third party libraries only used for tests should live in the according subdirectories inside the `test` folder.
Make sure your project still builds successfully.
Use the [forum](https://moodle.hsr.ch/mod/forum/view.php?id=151989) on Moodle for questions.
\ No newline at end of file
BoundedBuffer.h
BoundedQueue.h
*.o
BoundedQueueTests
BoundedQueueTests.xml
OBJECTS = bounded_queue_heap_memory_suite.o bounded_queue_student_suite.o bounded_queue_single_threaded_lock_suite.o bounded_queue_semantic_suite.o bounded_queue_multi_threaded_suite.o bounded_queue_default_behavior_suite.o Test.o bounded_queue_signatures_suite.o bounded_queue_content_suite.o bounded_queue_non_default_constructible_element_type_suite.o
all: BoundedQueueTests
BoundedQueueTests: $(OBJECTS)
g++ -o BoundedQueueTests $^ -lpthread
%.o: %.cpp
g++ -Icute -std=c++17 -c $<
SOURCES = $(wildcard *.cpp)
OBJECTS = $(patsubst %.cpp,%.o,$(SOURCES))
all: BoundedQueueTests
BoundedQueueTests: $(OBJECTS)
g++ -o BoundedQueueTests $^ -lpthread
%.o: %.cpp
g++ -Icute -std=c++17 -c $<
#ifndef MEMORYOPERATIONCOUNTER_H_
#define MEMORYOPERATIONCOUNTER_H_
struct MemoryOperationCounter {
MemoryOperationCounter() = default;
MemoryOperationCounter(unsigned moves, unsigned copies, bool known_state) :
moves { moves }, copies { copies }, known_state { known_state } {
}
MemoryOperationCounter(MemoryOperationCounter && source) noexcept :
moves { std::move(source.moves) }, copies { std::move(source.copies) }, known_state { std::move(source.known_state) } {
moves++;
source.known_state = false;
}
MemoryOperationCounter & operator=(MemoryOperationCounter && source) & noexcept {
copyFrom(source);
moves++;
source.known_state = false;
return *this;
}
MemoryOperationCounter(MemoryOperationCounter const & source) :
moves { std::move(source.moves) }, copies { std::move(source.copies) }, known_state { std::move(source.known_state) } {
copies++;
}
MemoryOperationCounter & operator=(MemoryOperationCounter const & source) & noexcept {
copyFrom(source);
copies++;
return *this;
}
bool operator==(MemoryOperationCounter const & other) const {
return (moves == other.moves) && (copies == other.copies) == (known_state == other.known_state);
}
void print(std::ostream & os) const {
os << "MemoryOperationsCounter{moves: " << moves << ", copies: " << copies << ", known_state: " << known_state << "}\n";
}
private:
unsigned moves { 0 };
unsigned copies { 0 };
bool known_state { true };
void copyFrom(MemoryOperationCounter const & source) {
moves = source.moves;
copies = source.copies;
known_state = source.known_state;
}
};
inline std::ostream & operator <<(std::ostream & os, MemoryOperationCounter const & counter) {
counter.print(os);
return os;
}
#endif /* MEMORYOPERATIONCOUNTER_H_ */
#include "cute.h"
#include "ide_listener.h"
#include "xml_listener.h"
#include "cute_runner.h"
#include "bounded_queue_signatures_suite.h"
#include "bounded_queue_default_behavior_suite.h"
#include "bounded_queue_content_suite.h"
#include "bounded_queue_semantic_suite.h"
#include "bounded_queue_student_suite.h"
#include "bounded_queue_heap_memory_suite.h"
#include "bounded_queue_non_default_constructible_element_type_suite.h"
#include "bounded_queue_single_threaded_lock_suite.h"
#include "bounded_queue_multi_threaded_suite.h"
bool runAllTests(int argc, char const *argv[]){
//TODO add your test here
cute::xml_file_opener xmlfile(argc,argv);
cute::xml_listener<cute::ide_listener<> > lis(xmlfile.out);
bool success = true;
success &= cute::makeRunner(lis,argc,argv)(make_suite_bounded_queue_signatures_suite(), "BoundedQueue API Tests");
success &= cute::makeRunner(lis,argc,argv)(make_suite_bounded_queue_default_behavior_suite(), "BoundedQueue Default Behavior Tests");
success &= cute::makeRunner(lis,argc,argv)(make_suite_bounded_queue_content_suite(), "BoundedQueue Content Tests");
success &= cute::makeRunner(lis,argc,argv)(make_suite_bounded_queue_semantic_suite(), "BoundedQueue Semantic Tests");
success &= cute::makeRunner(lis,argc,argv)(make_suite_bounded_queue_student_suite(), "BoundedQueue Student Tests");
success &= cute::makeRunner(lis,argc,argv)(make_suite_bounded_queue_heap_memory_suite(), "BoundedQueue Heap Memory Tests");
success &= cute::makeRunner(lis,argc,argv)(make_suite_bounded_queue_non_default_constructible_element_type_suite(), "BoundedQueue Non-Default-Constructible Element Type Tests");
success &= cute::makeRunner(lis,argc,argv)(make_suite_bounded_queue_single_threaded_lock_suite(), "BoundedQueue Single Threaded Lock Tests");
success &= cute::makeRunner(lis,argc,argv)(make_suite_bounded_queue_multi_threaded_suite(), "BoundedQueue Multi-Threaded Tests");
return success;
}
int main(int argc, char const *argv[]){
return !runAllTests(argc,argv);
}
#include "bounded_queue_content_suite.h"
#include "cute.h"
#include "BoundedQueue.h"
#include "times_literal.hpp"
#include <stdexcept>
using namespace times::literal;
void test_queue_is_not_empty_after_push_rvalue() {
BoundedQueue<int> queue { 5 };
queue.push(5);
ASSERTM("Queue should not be empty after push", !queue.empty());
}
void test_queue_is_not_empty_after_push_lvalue() {
BoundedQueue<int> queue { 5 };
int const lvalue { 5 };
queue.push(lvalue);
ASSERTM("Queue should not be empty after push", !queue.empty());
}
void test_queue_is_empty_after_one_push_and_pop() {
BoundedQueue<int> queue { 5 };
queue.push(5);
queue.pop();
ASSERTM("Queue should be empty after one push and pop", queue.empty());
}
void test_queue_of_size_two_is_full_after_two_pushs() {
BoundedQueue<int> queue { 2 };
2_times([&]() {queue.push(5);});
ASSERTM("Queue of size two should be full after two pushs", queue.full());
}
void test_queue_size_is_one_after_push() {
BoundedQueue<int> queue { 2 };
queue.push(1);
ASSERT_EQUAL(1, queue.size());
}
void test_queue_size_is_fifty_after_fifty_pushs() {
BoundedQueue<int> queue { 75 };
50_times([&]() {queue.push(5);});
ASSERT_EQUAL(50, queue.size());
}
void test_queue_pop_gets_first_element_of_pushs() {
BoundedQueue<int> queue { 5 };
queue.push(1);
queue.push(2);
ASSERT_EQUAL(1, queue.pop());
}
void test_queue_pop_in_sequence_of_push() {
BoundedQueue<int> queue { 5 };
std::vector<int> frontValues { }, expectedValues { 1, 2, 3, 4, 5 };
queue.push(1);
queue.push(2);
frontValues.push_back(queue.pop());
queue.push(3);
queue.push(4);
frontValues.push_back(queue.pop());
frontValues.push_back(queue.pop());
queue.push(5);
frontValues.push_back(queue.pop());
frontValues.push_back(queue.pop());
ASSERT_EQUAL(expectedValues, frontValues);
}
void test_queue_wrap_around_behavior_pop() {
BoundedQueue<int> queue { 3 };
std::vector<int> frontValues { }, expectedValues { 1, 2, 3, 4, 5, 6, 7 };
queue.push(1);
queue.push(2);
frontValues.push_back(queue.pop());
frontValues.push_back(queue.pop());
queue.push(3);
queue.push(4);
queue.push(5);
frontValues.push_back(queue.pop());
frontValues.push_back(queue.pop());
frontValues.push_back(queue.pop());
queue.push(6);
queue.push(7);
frontValues.push_back(queue.pop());
frontValues.push_back(queue.pop());
ASSERT_EQUAL(expectedValues, frontValues);
}
void test_queue_after_swap_this_has_argument_content() {
std::vector<int> frontValues { }, expectedValues { 1, 2, 3 };
BoundedQueue<int> queue { 5 }, otherQueue { 5 };
queue.push(1);
queue.push(2);
queue.push(3);
otherQueue.swap(queue);
3_times([&]() {frontValues.push_back(otherQueue.pop());});
ASSERT_EQUAL(expectedValues, frontValues);
}
void test_queue_after_swap_argument_has_this_content() {
std::vector<int> frontValues { }, expectedValues { 1, 2, 3 };
BoundedQueue<int> queue { 5 }, otherQueue { 5 };
queue.push(1);
queue.push(2);
queue.push(3);
queue.swap(otherQueue);
3_times([&]() {frontValues.push_back(otherQueue.pop()); });
ASSERT_EQUAL(expectedValues, frontValues);
}
cute::suite make_suite_bounded_queue_content_suite() {
cute::suite s;
s.push_back(CUTE(test_queue_is_not_empty_after_push_rvalue));
s.push_back(CUTE(test_queue_is_not_empty_after_push_lvalue));
s.push_back(CUTE(test_queue_is_empty_after_one_push_and_pop));
s.push_back(CUTE(test_queue_of_size_two_is_full_after_two_pushs));
s.push_back(CUTE(test_queue_size_is_one_after_push));
s.push_back(CUTE(test_queue_size_is_fifty_after_fifty_pushs));
s.push_back(CUTE(test_queue_pop_gets_first_element_of_pushs));
s.push_back(CUTE(test_queue_pop_in_sequence_of_push));
s.push_back(CUTE(test_queue_wrap_around_behavior_pop));
s.push_back(CUTE(test_queue_after_swap_this_has_argument_content));
s.push_back(CUTE(test_queue_after_swap_argument_has_this_content));
return s;
}
#ifndef BOUNDED_QUEUE_CONTENT_SUITE_H_
#define BOUNDED_QUEUE_CONTENT_SUITE_H_
#include "cute_suite.h"
extern cute::suite make_suite_bounded_queue_content_suite();
#endif
#include "bounded_queue_default_behavior_suite.h"
#include "cute.h"
#include "BoundedQueue.h"
#include <boost/type_index.hpp>
void test_queue_constructor_for_capacity_zero_throws() {
ASSERT_THROWS(BoundedQueue<int> queue{0}, std::invalid_argument);
}
void test_int_queue_of_capacity_thousand_is_empty() {
BoundedQueue<int> const queue{1000};
ASSERTM("New queue should be empty", queue.empty());
}
void test_const_int_queue_of_capacity_thousand_is_not_full() {
BoundedQueue<int> const queue{1000};
ASSERTM("New queue should not be full", !queue.full());
}
void test_int_queue_of_capacity_thousand_has_size_zero() {
BoundedQueue<int> const queue{1000};
ASSERT_EQUALM("New queue should be empty", 0, queue.size());
}
void test_empty_bounded_queue_returns_false_on_try_pop() {
BoundedQueue<int> queue{23};
int val{};
ASSERT(!queue.try_pop(val));
}
void test_full_bounded_queue_returns_false_on_try_push_const_lvalue() {
BoundedQueue<int> queue{1};
int const lvalue{23};
queue.push(lvalue);
ASSERT(!queue.try_push(lvalue));
}
void test_full_bounded_queue_returns_false_on_try_push_rvalue() {
BoundedQueue<int> queue{1};
int lvalue{23};
queue.push(lvalue);
ASSERT(!queue.try_push(std::move(lvalue)));
}
using namespace std::chrono_literals;
void test_empty_bounded_queue_returns_false_on_try_pop_for() {
BoundedQueue<int> queue{23};
int val{};
ASSERT(!queue.try_pop_for(val, 1ns));
}
void test_full_bounded_queue_returns_false_on_try_push_for_const_lvalue() {
BoundedQueue<int> queue{1};
int const lvalue{23};
queue.push(lvalue);
ASSERT(!queue.try_push_for(lvalue, 1ns));
}
cute::suite make_suite_bounded_queue_default_behavior_suite(){
cute::suite s{};
s.push_back(CUTE(test_int_queue_of_capacity_thousand_is_empty));
s.push_back(CUTE(test_queue_constructor_for_capacity_zero_throws));
s.push_back(CUTE(test_const_int_queue_of_capacity_thousand_is_not_full));
s.push_back(CUTE(test_int_queue_of_capacity_thousand_has_size_zero));
s.push_back(CUTE(test_empty_bounded_queue_returns_false_on_try_pop));
s.push_back(CUTE(test_full_bounded_queue_returns_false_on_try_push_const_lvalue));
s.push_back(CUTE(test_full_bounded_queue_returns_false_on_try_push_rvalue));
s.push_back(CUTE(test_empty_bounded_queue_returns_false_on_try_pop_for));
s.push_back(CUTE(test_full_bounded_queue_returns_false_on_try_push_for_const_lvalue));
return s;
}
#ifndef BOUNDED_QUEUE_DEFAULT_BEHAVIOR_SUITE_H_
#define BOUNDED_QUEUE_DEFAULT_BEHAVIOR_SUITE_H_
#include "cute_suite.h"
extern cute::suite make_suite_bounded_queue_default_behavior_suite();
#endif
#include "bounded_queue_heap_memory_suite.h"
#include "cute.h"
#include "BoundedQueue.h"
#include "times_literal.hpp"
#include <cstddef>
#include <ios>
#include <stdexcept>
#include <utility>
#include <vector>
struct AllocationTracker {
static void* operator new(std::size_t sz) {
AllocationTracker * ptr = static_cast<AllocationTracker*>(::operator new(sz));
allocatedSingleObjects.push_back(ptr);
return ptr;
}
static void* operator new[](std::size_t sz) {
AllocationTracker * ptr = static_cast<AllocationTracker*>(::operator new[](sz));
allocatedArrays.push_back(ptr);
return ptr;
}
static void operator delete(void* ptr) {
deallocatedSingleObjects.push_back(static_cast<AllocationTracker*>(ptr));
::operator delete(ptr);
}
static void operator delete[](void* ptr) {
deallocatedArrays.push_back(static_cast<AllocationTracker*>(ptr));
::operator delete[](ptr);
}
static void* operator new ( std::size_t count, void* ptr ) {
return ::operator new(count, ptr);
}
static std::vector<AllocationTracker*> allocatedSingleObjects;
static std::vector<AllocationTracker*> allocatedArrays;
static std::vector<AllocationTracker*> deallocatedSingleObjects;
static std::vector<AllocationTracker*> deallocatedArrays;
};
std::vector<AllocationTracker*> AllocationTracker::allocatedSingleObjects;
std::vector<AllocationTracker*> AllocationTracker::allocatedArrays;
std::vector<AllocationTracker*> AllocationTracker::deallocatedSingleObjects;
std::vector<AllocationTracker*> AllocationTracker::deallocatedArrays;
std::ostream & operator <<(std::ostream& out, AllocationTracker const * const ptr) {
out << "0x" << std::hex << (unsigned long long)ptr << std::dec;
return out;
}
void resetAllocationCounters() {
AllocationTracker::allocatedSingleObjects.clear();
AllocationTracker::allocatedArrays.clear();
AllocationTracker::deallocatedSingleObjects.clear();
AllocationTracker::deallocatedArrays.clear();
}
void test_allocation_of_default_bounded_queue() {
resetAllocationCounters();
{
BoundedQueue<AllocationTracker> queue { 2 };
}
ASSERT_EQUAL(0, AllocationTracker::allocatedArrays.size());
}
void test_deallocation_of_default_bounded_queue() {
resetAllocationCounters();
{
BoundedQueue<AllocationTracker> queue { 2 };
}
ASSERT_EQUAL(0, AllocationTracker::deallocatedArrays.size());
}
void test_no_undeleted_allocation_on_exception() {
resetAllocationCounters();
try {
BoundedQueue<AllocationTracker> queue { 0 };
FAILM("The tests expects the BoundedQueue not being constructible with size 0.");
} catch (std::invalid_argument const & e) {
ASSERT_EQUAL(AllocationTracker::deallocatedArrays, AllocationTracker::allocatedArrays);
}
}
void test_copy_constructor_allocates_a_new_queue() {
resetAllocationCounters();
BoundedQueue<AllocationTracker> queue { 15 };
BoundedQueue<AllocationTracker> copy { queue };
ASSERT_EQUAL(0, AllocationTracker::allocatedArrays.size());
}
void test_move_constructor_does_not_allocate_a_new_queue() {
resetAllocationCounters();
BoundedQueue<AllocationTracker> queue { 15 };
BoundedQueue<AllocationTracker> moved { std::move(queue) };
ASSERT_EQUAL(0, AllocationTracker::allocatedArrays.size());
}
void test_copy_assignment_one_additional_allocation() {
resetAllocationCounters();
BoundedQueue<AllocationTracker> queue { 3 }, copy { 2 };
queue.push(AllocationTracker{});
queue.push(AllocationTracker{});
copy = queue;
ASSERT_EQUAL(0, AllocationTracker::allocatedArrays.size());
}
void test_move_assignment_no_additional_allocation() {
resetAllocationCounters();
BoundedQueue<AllocationTracker> queue { 3 }, move { 2 };
queue.push(AllocationTracker{});
queue.push(AllocationTracker{});
move = std::move(queue);
ASSERT_EQUAL(0, AllocationTracker::allocatedArrays.size());