Commit b8a67553 authored by Felix Morgner's avatar Felix Morgner
Browse files

week13: add resources

parent 2154dca4
# Exercises 13 - Hourglass Interfaces
In this exercise you will apply the hourglass interface approach to a given library.
## 1. Fancy Lib
In the exercise directory you will find the following projects already prepared:
* Fancy (Shared Library) - Which contains the implementation of the `cppug::be_fancy` function. It is implemented in the `fancy_lib.hpp` and `fancy_lib.cpp` files.
```cpp
namespace cppug {
void be_fancy(std::string const & ent, std::ostream & out) {
out << "I am a fancy " << ent << '\n';
}
}
```
* Doge (C++ Executable Application) - An executable project that accesses the `be_fancy` function through an overload version of `be_fancy`.
* Walrus (C Executable Application) - A C executable project that accesses the `be_fancy` function through the C API `cppug_be_fancy_on_stdout(char const * const entity)`, which you will need to implement.
* Python (A Python Script) - `fancy_python.py` loads a shared library and calls the `cppug_be_fancy_on_stdout` function in the shared library, wrapped in its own `be_fancy` function.
### Your Task
* Implement the hourglass interface in the Fancy (Shared Library).
* Add a `fancy.h` header that offers the function `cppug_be_fancy_on_stdout(char const * const entity)`.
* Implement the function in the `fancy.cpp` source file.
* Try to use the library in the different environments (C++, C and Python)
***Note:***
* The shared library has to be compiled with the flags `-fPIC` and `-fvisibility=hidden` (The visibility won't affect libraries on Windows).
* If you start the executables from Cevelop they need to be able to find the shared library. You can either copy the compiled library into the `Debug` directory of the executables or adapt the the run configuration.
* In the `Environment` tab click `Select...` and tick the `PATH` variable. Append the location of the shared library. e.g. `;${workspace_loc:/w13_template_01_FancyLibHourglass/Debug};`
* On Linux you need to set the `LD_LIBRARY_PATH` instead of the `PATH` variable.
* You might need to adapt the library name in the Python script.
\ No newline at end of file
# Exercise 13 - Build Systems
In this exercise...
* ... you will set up CMake to be used on your system
* ... 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.
If you are going to Cevelop for the exercises, also install the *Ninja* build tool.
## 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!
Follow [these instructions](https://github.com/rwols/CMakeBuilder/wiki/Ninja-for-Windows-Installation-Instructions) to make sure that Ninja is in your environments path.
## Installation on macOS
You should be able to install *CMake* and *Ninja* via Homebrew.
## Installation on Linux
Install *CMake* and *Ninja* using your distribution's package manger.
## Testing your installation
After installing *CMake*, test that your setup worked using the `w14_01_my_app_cmake` project from the `lecture_templates` folder:
To test on the Terminal:
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 you prefer to work with an IDE, check you IDE's instructions (refer to the lecture if you are working with Cevelop) on how to import a CMake 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 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* target compile features.
* 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.
## 2++ (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.
# 3 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.
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>w13_solution_01_FancyLibHourglass</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
<triggers>clean,full,incremental,</triggers>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
<triggers>full,incremental,</triggers>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.cdt.core.cnature</nature>
<nature>org.eclipse.cdt.core.ccnature</nature>
<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
</natures>
</projectDescription>
extern "C" {
#include "fancy.h"
}
#include "fancy_lib.hpp"
void cppug_be_fancy_on_stdout(char const * const entity) {
cppug::be_fancy(entity);
}
#ifndef FANCY
#define FANCY
__attribute__((visibility("default")))
void cppug_be_fancy_on_stdout(char const * const entity);
#endif
#ifndef FANCYPP
#define FANCYPP
extern "C" {
#include "fancy.h"
}
#include <string>
namespace cppug {
inline void be_fancy(std::string entity) {
cppug_be_fancy_on_stdout(entity.c_str());
}
}
#endif
#include "fancy_lib.hpp"
namespace cppug {
void be_fancy(std::string const & ent, std::ostream & out) {
out << "I am a fancy " << ent << '\n';
}
}
#ifndef FANCY_LIB
#define FANCY_LIB
#include <string>
#include <iostream>
namespace cppug {
void be_fancy(std::string const &, std::ostream & = std::cout);
}
#endif
cmake_minimum_required(VERSION "3.12.0")
project("BoundedQueueTests" LANGUAGES CXX)
add_executable("BoundedQueueTests"
"bounded_queue_content_suite.cpp"
"bounded_queue_default_behavior_suite.cpp"
"bounded_queue_heap_memory_suite.cpp"
"bounded_queue_multi_threaded_suite.cpp"
"bounded_queue_non_default_constructible_element_type_suite.cpp"
"bounded_queue_semantic_suite.cpp"
"bounded_queue_signatures_suite.cpp"
"bounded_queue_single_threaded_lock_suite.cpp"
"bounded_queue_student_suite.cpp"
"Test.cpp"
)
target_compile_features("BoundedQueueTests" PRIVATE
"cxx_std_17"
)
target_include_directories("BoundedQueueTests" PRIVATE
"cute"
)
target_link_libraries("BoundedQueueTests" PRIVATE "pthread")
cmake_minimum_required(VERSION "3.12.0")
project("BoundedQueueTests" LANGUAGES CXX)
enable_testing()
add_executable("BoundedQueueTests"
"bounded_queue_content_suite.cpp"
"bounded_queue_default_behavior_suite.cpp"
"bounded_queue_heap_memory_suite.cpp"
"bounded_queue_multi_threaded_suite.cpp"
"bounded_queue_non_default_constructible_element_type_suite.cpp"
"bounded_queue_semantic_suite.cpp"
"bounded_queue_signatures_suite.cpp"
"bounded_queue_single_threaded_lock_suite.cpp"
"bounded_queue_student_suite.cpp"
"Test.cpp"
)
target_compile_features("BoundedQueueTests" PRIVATE
"cxx_std_17"
)
target_include_directories("BoundedQueueTests" PRIVATE
"cute"
)
target_link_libraries("BoundedQueueTests" PRIVATE "pthread")
add_test("Tests" "BoundedQueueTests")
#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;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment