This commit is contained in:
2025-04-09 10:22:44 +02:00
commit 96ff5392c8
330 changed files with 28300 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
---
BasedOnStyle: LLVM
---
Language: Cpp
AllowShortFunctionsOnASingleLine: Empty
DerivePointerAlignment: false
PointerAlignment: Left
ColumnLimit: 120
TabWidth: 4
IndentWidth: 2
...
+23
View File
@@ -0,0 +1,23 @@
# debug: clangd --check=modules/iue-io/ccsv.h
# debug: clangd --check=task1.hpp
# debug: clangd --check=task1.test.cpp
InlayHints:
Enabled: No
ParameterNames: No
DeducedTypes: No
---
CompileFlags:
Add:
- -Wall
- -Wno-unused-function
- -Wno-unused-variable
---
If:
PathMatch: [.*\.c, .*\.h]
CompileFlags:
Add: [-std=c11]
---
If:
PathMatch: [.*\.cpp, .*\.hpp]
CompileFlags:
Add: [-std=c++20]
+16
View File
@@ -0,0 +1,16 @@
## source: https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings
# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto
# Explicitly declare text files you want to always be normalized and converted
# to native line endings on checkout.
*.h text
*.hpp text
*.c text
*.cpp text
*.py text
*.ipynb text
*.md text
*.txt text
*.csv text
+358
View File
@@ -0,0 +1,358 @@
# custom
build
doc
.cache
.vscode
.idea
# https://github.com/github/gitignore/blob/main/CMake.gitignore
CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_deps
# ttps://github.com/github/gitignore/blob/main/C.gitignore
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
# https://github.com/github/gitignore/blob/main/C%2B%2B.gitignore
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
# source: https://github.com/github/gitignore/blob/main/Python.gitignore
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# jetbrain IDEs: https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
# VSCODE source: https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
+4
View File
@@ -0,0 +1,4 @@
[submodule "modules"]
path = modules
url = https://sgit.iue.tuwien.ac.at/360050/modules
branch = main
+30
View File
@@ -0,0 +1,30 @@
cmake_minimum_required(VERSION 3.20)
# define project metadata
project(lab3 LANGUAGES CXX
DESCRIPTION "lab3"
HOMEPAGE_URL "https://sgit.iue.tuwien.ac.at/360050/lab3")
# setting required language standards
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True)
set(CMAKE_CXX_EXTENSIONS OFF)
# misc settings
# generate a compile_commands.json
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# make all symbols visible on windows (which is default on unix)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
# global includes
include_directories(modules)
# include own targets
add_executable(taskA taskA.cpp)
add_executable(taskB taskB.cpp)
add_executable(taskC taskC.cpp)
+25
View File
@@ -0,0 +1,25 @@
# Labor III: Abgabe (30min)
**Dieser Teil wird über [TUWEL, Labor III: Abgabe (30min)](https://tuwel.tuwien.ac.at/course/view.php?id=62042#coursecontentcollapse9)** abgewickelt.
# Labor III: Praxisteil (120min)
- **Fragen Sie frühzeitig nach, falls Unklarheiten bestehen**.
- Fragen Sie alles, was Ihnen im Rahmen der Lehrveranstaltung wichtig erscheint.
- Es gibt keine Einschränkungen für die Zusammenarbeit zwischen Studierenden beim Bearbeiten der untenstehenden Aufgaben.
- Sie haben das Labor erfolgreich absolviert, wenn Sie alle drei Teilaufgaben bei einem Betreuer **demonstriert** haben.
- Melden Sie sich bei einem Betreuer, sobald Sie sich in der Lage sehen alle drei Aufgaben zu demonstrieren.
---
Im heutigen Labor sollen Sie die folgenden drei Aufgabengebiete bearbeiten.
### A. Klassen mit mehrere Konstruktoren
### B. Abgeleitete Klassen und dynamische Speicherverwaltung (*new/delete*)
### C. Generische Funktion/Klassen (*templates*)
---
Details zu den Aufgaben finden Sie in [`main.ipynb`](main.ipynb).
+1
View File
@@ -0,0 +1 @@
-Imodules
+112
View File
@@ -0,0 +1,112 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## A. Klassen mit mehrere Konstruktoren\n",
"\n",
"**Aufgabe:**\n",
"\n",
"- Implementieren Sie in eine Klasse `Vector` mit folgenden Eigenschaften (Details siehe [taskA.Vector.hpp](taskA.Vector.hpp)):\n",
" - Eine private Membervariable vom Typ `std::vector<double>`\n",
" - Drei Konstruktoren\n",
" - Eine `const` Memberfunktion `print()`.\n",
"- Nach der Implementierung von `Vector`: Aktivieren Sie in [taskA.cpp](taskA.cpp) alle gekennzeichneten Stellen (die jetzt kompilieren sollten).\n",
"\n",
"**Demonstration:** Kompilieren Sie Ihr Programm, führen Sie es aus, und erklären Sie in eigenen Worten den Vorteil mehrerer Konstruktoren."
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## B. Abgeleitete Klassen und dynamische Speicherverwaltung (*new/delete*)\n",
"\n",
"**Aufgabe:**\n",
"\n",
"- Gegeben ist eine abstrakte Schnittstellenklasse `Device` ([taskB.Device.hpp](taskB.Device.hpp))\n",
"- Ebenso gegeben sind folgende Klassen, die die Schnittstelle `Device` implementieren: \n",
" - `WashingMachine` ([taskB.WashingMachine.hpp](taskB.WashingMachine.hpp)) \n",
" - `Train` ([taskB.Train.hpp](taskB.Train.hpp))\n",
" \n",
"- Implementieren Sie eine weitere Klasse `Car` ([taskB.Car.hpp](taskB.Car.hpp)) die ebenfalls die Schnittstelle `Device` implementiert\n",
"- Erweitern Sie die `main`-Funktion in [taskB.cpp](taskB.cpp):\n",
" - Allozieren Sie mit `new` dynamische Instanzen Ihrer Klasse und fügen Sie die Zeiger auf diese Instanzen zum bereits vorhandenen Vektor hinzu.\n",
" - Deallozieren Sie am Ende alle dynamische allozieren Instanzen mit `delete`\n",
"- Speicherlecks:\n",
" - Überprüfen Sie ob Ihr Programmdurchlauf Speicherlecks aufweist, indem Sie mit `-fsanitize=address` kompilieren und das Programm ausführen.\n",
" - Sie können ein Speicherleck provozieren, indem Sie kein `delete` verwenden (also keine Deallokation durchführen).\n",
"\n",
"**Demonstration:** \n",
"- Erklären Sie in eigenen Worten, welchen Vorteil eine Schnittstellenklasse als Basisklasse bietet; welche Einschränkungen hatten Sie z.B. bei der Wahl der Membervariblen für Ihre Klasse `Car`?\n",
"- Kompilieren Sie Ihr Programm mit der Compiler-Option `-fsanitize=address` und führen Sie Ihr Programm aus; provozieren Sie ein Speicherleck und erklären Sie in eigenen Worten das Problem.\n",
"\n",
"**Wichtiger allgemeiner Hinweis:** Explizite dynamische Speicherverwaltung (`new`/`delete`) sollte **immer nur gekapselt** mit **klarer Regelung des Besitzes** der dynamischen Ressourcen erfolgen: also nur innerhalb einer Klasse/Datenstruktur."
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"**CMake:** Sie können (sofern Ihr Compiler dies unterstützt) die oben erwähnte Option auch an CMake übergeben:\n",
"\n",
"```shell\n",
"cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug -D CMAKE_CXX_FLAGS=\"-fsanitize=address\"\n",
"cmake --build build --target taskC\n",
"./build/taskC\n",
"```"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## C. Generische Funktion/Klassen (*templates*)\n",
"\n",
"In der Datei [taskC.one.hpp](taskC.one.hpp) sind im Namensraum `one` zwei Sätze von Funktionsüberladungen `add` und `sum` gegeben.\n",
"\n",
"**Aufgabe 1:**\n",
"\n",
"- Implementieren Sie in [taskC.two.hpp](taskC.two.hpp) im Namensraum `two` zwei Funktions-Templates `add` und `sum`, die Anstelle von `one::add` und `one::sum` verwendet werden können\n",
"- Nach der Implementierung: stellen Sie in [taskC.cpp](taskC.cpp) den verwendeten Namensraum von `one` auf `two` um, und überprüfen Sie die Funktionalität (die sich nicht geändert haben sollte).\n",
"\n",
"**Demonstration 1:** Kompilieren Sie Ihr Programm und führen Sie es aus. Könnten Sie mittels Ihrer zwei Funktions-Templates nun (neben `int` und `double`) beliebige andere Typen addieren und summieren? Welche Voraussetzungen gelten für die Template-Typen bei Ihren Funktionen?\n",
"\n",
"**Aufgabe 2:**\n",
"\n",
"- Kopieren Sie die Datei [taskA.Vector.hpp](taskA.Vector.hpp) als [taskA.VectorT.hpp](taskA.VectorT.hpp):\n",
" - Benennen Sie die Klasse in `VectorT` um\n",
" - **Entfernen Sie den dritten Konstruktor** (den mit dem `std::function<double()>` Parameter), da eine Umsetzung erweiterte C++-Kenntnisse erfordern wuerde. \n",
" - Verwandeln Sie die Klasse in ein Klassen-Template, indem Sie den im Vektor abgespeicherten Typ als Template-Typ verwenden:\n",
" ```cpp\n",
" template <typename T>\n",
" struct VectorT {\n",
" private:\n",
" std::vector<T> data;\n",
" ...\n",
" };\n",
" ```\n",
"\n",
"**Demonstration 2:** Erklären Sie, wie Sie bei der Umwandlung für von `Vector` zu `VectorT` vorgegangen sind."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.12.2"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
+11
View File
@@ -0,0 +1,11 @@
---
BasedOnStyle: LLVM
---
Language: Cpp
AllowShortFunctionsOnASingleLine: Empty
DerivePointerAlignment: false
PointerAlignment: Left
ColumnLimit: 120
TabWidth: 4
IndentWidth: 2
...
+20
View File
@@ -0,0 +1,20 @@
InlayHints:
Enabled: Yes
ParameterNames: Yes
DeducedTypes: No
---
CompileFlags:
Add:
- -Wall
- -Wno-unused-function
- -Wno-unused-variable
---
If:
PathMatch: [.*\.c, .*\.h]
CompileFlags:
Add: [-std=c11]
---
If:
PathMatch: [.*\.cpp, .*\.hpp]
CompileFlags:
Add: [-std=c++20]
+16
View File
@@ -0,0 +1,16 @@
## source: https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings
# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto
# Explicitly declare text files you want to always be normalized and converted
# to native line endings on checkout.
*.h text
*.hpp text
*.c text
*.cpp text
*.py text
*.ipynb text
*.md text
*.txt text
*.csv text
+358
View File
@@ -0,0 +1,358 @@
# custom
install
build
doc
.cache
.vscode
.idea
# https://github.com/github/gitignore/blob/main/CMake.gitignore
CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_deps
# ttps://github.com/github/gitignore/blob/main/C.gitignore
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
# https://github.com/github/gitignore/blob/main/C%2B%2B.gitignore
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
# source: https://github.com/github/gitignore/blob/main/Python.gitignore
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# jetbrain IDEs: https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
# VSCODE source: https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
+62
View File
@@ -0,0 +1,62 @@
cmake_minimum_required(VERSION 3.28)
# define project metadata
project(iue-modules LANGUAGES CXX C
DESCRIPTION "modules"
HOMEPAGE_URL "https://sgit.iue.tuwien.ac.at/360050/modules")
# setting required language standards
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED True)
set(CMAKE_C_EXTENSIONS OFF)
# misc settings
# avoid ctest-dashboard spcific targets
set_property(GLOBAL PROPERTY CTEST_TARGETS_ADDED 1)
# generate a compile_commands.json
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# make all symbols visible on windows (which is default on unix)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
# options
option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
option(BUILD_TESTING "enable testing with ctest" ON)
# external dependencies
#find_package(OpenMP COMPONENTS C CXX REQUIRED)
# testing
include(CTest)
# include modules in order of dependence
add_subdirectory(iue-other)
add_subdirectory(iue-io)
add_subdirectory(iue-po)
add_subdirectory(iue-num)
add_subdirectory(iue-rnd)
add_subdirectory(iue-svg)
# packaging for consuming an install-tree via find_package
include(CMakePackageConfigHelpers)
configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/iue-modules-config.in
${CMAKE_CURRENT_BINARY_DIR}/iue-modules-config.cmake
INSTALL_DESTINATION "./"
)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/iue-modules-config.cmake"
DESTINATION "./"
)
+30
View File
@@ -0,0 +1,30 @@
{
"version": 2,
"configurePresets": [
{
"name": "gcc-debug",
"displayName": "gcc-debug",
"generator": "Unix Makefiles",
"binaryDir": "${sourceDir}/build",
"hidden": false,
"cacheVariables": {
"CMAKE_C_FLAGS": "-g -O0 -fno-omit-frame-pointer -fsanitize=address",
"CMAKE_CXX_FLAGS": "-g -O0 -fno-omit-frame-pointer -fsanitize=address",
"CMAKE_EXE_LINKER_FLAGS": "-fsanitize=address",
"CMAKE_SHARED_LINKER_FLAGS": "-fsanitize=address"
}
},
{
"name": "msvc-debug",
"displayName": "msvc-debug",
"generator": "Visual Studio 17 2022",
"binaryDir": "${sourceDir}/build",
"hidden": false,
"cacheVariables": {
"CMAKE_C_FLAGS": "-W4 -WX",
"CMAKE_CXX_FLAGS": "-W4 -WX",
"CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS": "ON"
}
}
]
}
+94
View File
@@ -0,0 +1,94 @@
# Helper libraries
## Information for students
- you do **not** need to copy any files from this repository "by-hand"
- you do **not** need to clone this repo explicitly
- you will make use of functions provided by these libraries in the exercises
- these libraries will be configured as a *git submodule* in the respective exercises, this is why you need to clone with `git clone --recursive ...`
- tentative: we will use these libraries to showcase differences when implementing the same functionality in C and C++
## Overview
```
├── iue-io # reading and writing csv-files of numeric data, header-only
│   ├── ccsv.h
│   ├── ccsv.test.c
│   ├── csv.hpp
│   └── csv.test.cpp
├── iue-po # minimalistic support for parsing command line options, header-only
│   ├── cpo.h
│   ├── cpo.test.c
│   ├── po.hpp
│   └── po.test.cpp
├── iue-num # helper functions for numeric calculations with floating point values, header-only
│   ├── numerics.hpp
│   └── numerics.test.cpp
├── iue-rnd # helper functions for generating random instances of values and generic objects, header-only
│   ├── random.hpp
│   └── random.test.cpp
├── iue-svg # minimal svg-generator from geometric primitives (e.g. box/circle/triangle), header-only
│   ├── tags.hpp
│   ├── tags.test.cpp
│   ├── render.hpp
│   ├── render.test.cpp
│   ├── tree.hpp
│   └── tree.test.cpp
└── README.md
```
## config/build/test/install
```shell
# cmake -S . -B build
cmake --preset gcc-debug
cmake --build build
ctest --test-dir build --verbose
cmake --install build --prefix ./install
rm -rf build/ install/ # cleanup
```
## fresh integration into an exercise
```shell
git submodule add -b main https://sgit.iue.tuwien.ac.at/360050/modules ./modules
git add modules
git commit -m "added modules as git-submodule"
```
## pull for a submodule enabled exercise
```shell
cd exerciseX
git pull --recurse-submodules
```
## bump to latest modules commit for an exercise
```shell
cd exerciseX
git submodule update --remote --merge modules
git add modules
git commit -m "bump modules to latest commit"
```
## intended incorporation/usecase in an exercise
```cpp
// task1.main.cpp
#include "iue-io/csv.hpp"
//...
```
```cpp
// task1.main.c
#include "iue-io/ccsv.h"
//...
```
```
mkdir build
g++ -Imodules -std=c++20 task1.main.cpp -o build/task1_cpp && ./build/task1_cpp
gcc -Imodules -std=c11 task1.main.c -o build/task1_c && ./build/task1_c
```
+13
View File
@@ -0,0 +1,13 @@
@PACKAGE_INIT@
#include(CMakeFindDependencyMacro)
#find_dependency(OpenMP) # will use spec when from find_package used in this project
include("${CMAKE_CURRENT_LIST_DIR}/iue-other/iue-other-export.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/iue-io/iue-io-export.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/iue-po/iue-po-export.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/iue-num/iue-num-export.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/iue-rnd/iue-rnd-export.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/iue-svg/iue-svg-export.cmake")
check_required_components(iue-modules)
+1
View File
@@ -0,0 +1 @@
-Imodules
+52
View File
@@ -0,0 +1,52 @@
cmake_minimum_required(VERSION 3.28)
# define CXX interface library
# define target & alias (i.e. consuming projects can experience namespaces)
add_library(iue-io_csv INTERFACE)
target_link_libraries(iue-io_csv INTERFACE iue-other_honly)
#target_link_libraries(iue-io_csv INTERFACE OpenMP::OpenMP_CXX)
# define sources of target
target_sources(iue-io_csv
INTERFACE FILE_SET public_headers TYPE HEADERS BASE_DIRS ${PROJECT_SOURCE_DIR} FILES csv.hpp)
# define installation step
install(TARGETS iue-io_csv EXPORT iue-io-export
FILE_SET public_headers DESTINATION "./")
# define test
if(BUILD_TESTING AND PROJECT_IS_TOP_LEVEL)
add_executable(iue-io_csv.test csv.test.cpp)
target_link_libraries(iue-io_csv.test iue-io_csv)
add_test(NAME iue-io_csv.test COMMAND iue-io_csv.test)
endif()
# define C interface library
# define target & alias (i.e. consuming projects can experience namespaces)
add_library(iue-io_ccsv INTERFACE)
# define sources of target
target_sources(iue-io_ccsv
INTERFACE FILE_SET public_headers TYPE HEADERS BASE_DIRS ${PROJECT_SOURCE_DIR} FILES ccsv.h)
# define installation step
install(TARGETS iue-io_ccsv EXPORT iue-io-export
FILE_SET public_headers DESTINATION "./")
# define test
if(BUILD_TESTING AND PROJECT_IS_TOP_LEVEL)
add_executable(iue-io_ccsv.test ccsv.test.c)
target_link_libraries(iue-io_ccsv.test iue-io_ccsv)
add_test(NAME iue-io_ccsv.test COMMAND iue-io_ccsv.test)
endif()
# export targets for consuming cmake projects
# generate and install a .cmake file to allow dependend projects consuming the "install-tree" to import all targets
install(EXPORT iue-io-export DESTINATION "iue-io" )
# allow dependend projects consuming the "build-tree" to use all targets
export(EXPORT iue-io-export )
+143
View File
@@ -0,0 +1,143 @@
#pragma once
#include <assert.h> // assert
#include <math.h> // NAN
#include <stddef.h> // size_t
#include <stdio.h> // FILE|snprintf
#include <stdlib.h> // EXIT_FAILURE|EXIT_SUCCESS
#include <string.h> // strtok|strdup|memcpy
/// @brief Set of values in a row
struct Row {
double* values; ///< pointer to row value array
size_t n; ///< length of array 'value'
};
/// @brief Set of rows
struct Table {
struct Row* rows; ///< pointer to array of rows
size_t n; ///< length of array 'rows'
};
static void table_clear(struct Table* table) {
for (size_t i = 0; i != table->n; ++i) {
free(table->rows[i].values);
}
free(table->rows);
table->rows = NULL;
table->n = 0;
}
static void table_append_copy(struct Table* table, const double* data, size_t n) {
assert(table != NULL);
table->rows = realloc(table->rows, sizeof(struct Row) * ++table->n);
double* ptr = malloc(sizeof(double) * n);
memcpy(ptr, data, sizeof(double) * n);
table->rows[table->n - 1].values = ptr;
table->rows[table->n - 1].n = n;
}
static void table_append_move(struct Table* table, const struct Row* row) {
assert(table != NULL);
table->rows = realloc(table->rows, sizeof(struct Row) * ++table->n);
table->rows[table->n - 1] = *row;
}
/// @brief Deserialize a row from a string
static void row_serialize(const struct Row* row, FILE* stream, char del) {
for (size_t r = 0; r != row->n - 1; ++r) {
fprintf(stream, "%.18e%c", row->values[r], del);
}
fprintf(stream, "%.18e", row->values[row->n - 1]); // avoid trailing return
fprintf(stream, "\n");
}
/// @brief Writes a numeric values to a file using a csv-format
/// @param filepath file to be written including the desired extension
/// @param table table with the rows to be written
/// @param del delimiter between individual values
/// @param header informative comment in the output file
/// @param comments character signalling that a line is a comment (if used as first character of the line )
static int iueio_savetxt(const char* filepath, const struct Table* table, char del, const char* header, char comments) {
// check if data is present
if (table->n == 0)
return EXIT_SUCCESS;
// open file
FILE* stream = fopen(filepath, "w");
if (stream == NULL)
return EXIT_FAILURE;
// write header lines
if (header[0] != '\0') {
char* tmp = malloc((strlen(header) + 1) * sizeof(char));
strcpy(tmp, header);
char* pos = strtok(tmp, "\n");
while (pos != NULL) {
fprintf(stream, "%c %s%c\n", comments, pos, del);
pos = strtok(NULL, "\n");
}
free(tmp);
}
// write data
for (size_t i = 0; i != table->n; ++i) {
row_serialize(&table->rows[i], stream, del);
}
fclose(stream);
return EXIT_SUCCESS;
}
/// @brief Serializes a Row from a stream
static int row_deserialize(struct Row* row, const char* line, char del) {
assert(row->n == 0);
char format[32];
// snprintf(format, sizeof(format) / sizeof(char), "%%lf %c", del);
double value = NAN;
const char* iter = line;
int n = 0;
while (sscanf(iter, "%lf %n", &value, &n) != EOF) {
iter += n;
row->values = realloc(row->values, sizeof(double) * ++row->n);
row->values[row->n - 1] = value;
char format[16];
snprintf(format, sizeof(format) / sizeof(char), " %%*[%c] %%n", del);
sscanf(iter, format, &n);
iter += n;
}
return EXIT_SUCCESS;
}
/// @brief Read numeric values stored in a csv-format
/// @param filepath input file containing the numeric values
/// @param table table to be read into (must be empty)
/// @param del delimiter between individual values in a row
/// @return integral values signalling success or failure
static int iueio_loadtxt(const char* filepath, struct Table* table, char del, char comment) {
// check if table is empty
assert(table->n == 0);
// open file
FILE* stream = fopen(filepath, "r");
if (stream == NULL)
return EXIT_FAILURE;
// read from stream row-by-row (fixed size buffer)
static char line[1024];
// read each line into a row
while (fgets(&line[0], sizeof(line) / sizeof(char), stream)) {
if (line[0] == comment)
continue;
struct Row row = {NULL, 0};
int n = row_deserialize(&row, line, del);
table_append_move(table, &row);
}
fclose(stream);
return EXIT_SUCCESS;
}
+63
View File
@@ -0,0 +1,63 @@
#include "iue-io/ccsv.h"
#include <assert.h> // assert
#include <stddef.h> // size_t
int main(void) {
// prep input data
struct Table table1 = {NULL, 0};
{
double data[3] = {1, 2, 3};
table_append_copy(&table1, data, 3);
}
{
double data[4] = {10, 20, 30, 40};
table_append_copy(&table1, data, 4);
}
{
double data[4] = {1e3, 2e3, 3e3, 4e3};
table_append_copy(&table1, data, 4);
}
{ // write
int res = iueio_savetxt("ccsv_test.csv", &table1, ';', "this is a multiline \n header comment", '#');
if (res != EXIT_SUCCESS)
return EXIT_FAILURE;
}
struct Table table2 = {NULL, 0};
{ // read
int res = iueio_loadtxt("ccsv_test.csv", &table2, ';', '#');
if (res != EXIT_SUCCESS)
return EXIT_FAILURE;
for (size_t i = 0; i != table2.n; ++i) {
for (size_t j = 0; j != table2.rows[i].n; ++j)
printf("%lf ", table2.rows[i].values[j]);
printf("\n");
}
}
// compare
assert(table1.n == table2.n);
for (size_t r = 0; r != table1.n; ++r) {
assert(table1.rows[r].n == table2.rows[r].n);
for (size_t c = 0; c != table1.rows[r].n; ++c) {
assert(table1.rows[r].values[c] == table2.rows[r].values[c]);
}
}
table_clear(&table1);
table_clear(&table2);
return EXIT_SUCCESS;
}
+99
View File
@@ -0,0 +1,99 @@
#pragma once
#include <filesystem> // std::filesystem::path
#include <fstream> // std::ofstream|ifstream
#include <iomanip> // std::scientifc|setprecision
#include <iostream> // std::cout|endl
#include <limits> // std::numeric_limits
#include <sstream> // std::istringstream
#include <string> // std::string
#include <vector> // std::vector
namespace iue::io {
/// @brief Writes a numeric values to a file using a csv-format
/// @param filepath file to be written including the desired extension
/// @param table table with the rows to be written
/// @param del delimiter between individual values
/// @param header informative comment in the output file
/// @param comments character signalling that a line is a comment (if used as first character of the line )
inline void savetxt(std::filesystem::path filepath, const std::vector<std::vector<double>>& table, char del = ' ',
std::string header = "", char comments = '#') {
// setup row writer callable
auto serialize = [&del](const std::vector<double>& row) -> std::string {
std::ostringstream oss;
for (auto iter = row.begin(); iter != std::prev(row.end()); ++iter)
oss << std::scientific << std::setprecision(18) << *iter << del;
oss << std::scientific << std::setprecision(18) << *std::prev(row.end()); // avoid trailing return
oss << std::endl;
return oss.str();
};
// check if data is present
if (table.empty())
return;
// open file
std::ofstream stream;
stream.exceptions(std::ifstream::badbit);
stream.open(filepath);
// write header lines
if (!header.empty()) {
std::string line;
std::istringstream iss(header);
while (std::getline(iss, line)) {
stream << comments << ' ' << line << std::endl;
}
}
// write data
for (const auto& row : table)
stream << serialize(row);
}
/// @brief Read numeric values stored in a csv-format
/// @param filepath input file containing the numeric values
/// @param del delimiter between individual values
/// @param comments character signalling that a line is a comment (if used as first character of the line )
/// @return table which was read
inline std::vector<std::vector<double>> loadtxt(std::filesystem::path filepath, char del = ' ', char comments = '#') {
// setup row parser callable
auto deserialize = [&del](const std::string& line) -> std::vector<double> {
std::vector<double> res;
std::stringstream stream(line);
std::string item;
while (std::getline(stream, item, del)) {
std::stringstream ss(item);
ss.exceptions(std::ios::failbit);
auto value = std::numeric_limits<double>::quiet_NaN();
ss >> value;
res.push_back(value);
}
return res;
};
// setup table
std::vector<std::vector<double>> table;
// open file
std::ifstream stream;
stream.exceptions(std::ifstream::badbit);
stream.open(filepath);
// read from stream row-by-row
std::string line;
// read line to row vectors
while (std::getline(stream, line)) {
if (line.front() == comments)
continue;
table.push_back(deserialize(line));
}
return table;
}
} // namespace iue::io
+73
View File
@@ -0,0 +1,73 @@
#include "iue-io/csv.hpp"
#include <cassert> // assert
#include <cstddef> // std::size_t
#include <iostream> // std::cout|endl
#include <stdlib.h>
#include <vector> // std::vector
int main() {
// prep input data
std::vector<std::vector<double>> table1;
{
auto row = std::vector<double>{1, 2, 3};
table1.push_back(row);
}
{
auto row = std::vector<double>{10, 20, 30, 40};
table1.push_back(row);
}
{
auto row = std::vector<double>{1e3, 2e3, 3e3, 4e3};
table1.push_back(row);
}
// write
try {
for (const auto& row : table1) {
for (const auto& value : row)
std::cout << value << " ";
std::cout << std::endl;
}
iue::io::savetxt("csv_test.csv", table1, ';', "this is a multiline \n header comment");
} catch (const std::exception& e) {
std::cout << e.what();
}
std::vector<std::vector<double>> table2;
// read
try {
table2 = iue::io::loadtxt("csv_test.csv", ';');
for (const auto& row : table2) {
for (const auto& value : row)
std::cout << value << " ";
std::cout << std::endl;
}
} catch (const std::exception& e) {
std::cout << e.what();
}
// compare
assert(table1.size() == table2.size());
for (size_t r = 0; r != table1.size(); ++r) {
assert(table1[r].size() == table2[r].size());
for (size_t c = 0; c != table1[r].size(); ++c) {
assert(table1[r][c] == table2[r][c]);
}
}
return EXIT_SUCCESS;
}
+30
View File
@@ -0,0 +1,30 @@
cmake_minimum_required(VERSION 3.28)
# define CXX interface library
# define target & alias (for other projects consuming the "source-tree")
add_library(iue-num_num INTERFACE)
# define sources of target
target_sources(iue-num_num
INTERFACE FILE_SET public_headers TYPE HEADERS BASE_DIRS ${PROJECT_SOURCE_DIR} FILES numerics.hpp)
# define installation step
install(TARGETS iue-num_num EXPORT iue-num-export
FILE_SET public_headers DESTINATION "./")
# define test
if(BUILD_TESTING AND PROJECT_IS_TOP_LEVEL)
add_executable(iue-num_num.test numerics.test.cpp)
target_link_libraries(iue-num_num.test iue-num_num)
add_test(NAME iue-num_num.test COMMAND iue-num_num.test)
endif()
# export targets for consuming cmake projects
# generate and install a .cmake file to allow dependend projects consuming the "install-tree" to import all targets
install(EXPORT iue-num-export DESTINATION "iue-num" )
# allow dependend projects consuming the "build-tree" to use all targets
export(EXPORT iue-num-export)
+26
View File
@@ -0,0 +1,26 @@
/// @file
/// @brief Supporting functionality for numeric calculations with floating point values
#pragma once
#include <cmath> // std::abs, std::max, std::min
namespace iue::num {
/// @brief Checks if two floating point values are close to each other
/// @param a First value
/// @param b Second value
/// @param tabs Tolerance for the absolute difference:
/// abs(a - b) < tabs
/// @param trel Tolerance for the relative error (using the larger absolute value as reference):
/// abs(a b) < trel * max(abs(a),abs(b))
/// @return true if the stronger of the two tolerances is met, false otherwise
inline bool isclose(double a, double b, double tabs = 0.0, double trel = 1e-9) {
if (a == b) // for infinite values (which should be considered close to themselves)
return true;
auto abs_diff = std::abs(a - b); // absolte difference
auto trel_ref = std::max(std::abs(a), std::abs(b)); // reference for the relative threshold
return abs_diff < std::max(tabs, trel * trel_ref);
}
} // namespace iue::num
+69
View File
@@ -0,0 +1,69 @@
/// @file
/// @brief Test for iue::num::islcose
#include "iue-num/numerics.hpp" // iue::num::islcose
#include <cassert> // assert
#include <cstdlib> // EXIT_SUCCESS
#include <limits> // std::numeric_limits
int main() {
const double eps = std::numeric_limits<double>::epsilon();
const double inf = std::numeric_limits<double>::infinity();
const double nan = std::numeric_limits<double>::quiet_NaN();
{ // infinity
assert(iue::num::isclose(inf, inf, 0.0, 0.0));
assert(iue::num::isclose(-inf, -inf, 0.0, 0.0));
assert(!iue::num::isclose(-inf, inf, 0.0, 0.0));
assert(!iue::num::isclose(inf, -inf, 0.0, 0.0));
}
{ // nan
assert(!iue::num::isclose(nan, nan, inf, inf));
}
{ // around 1.0
double a = 1.0;
double b = 1.0 + eps;
// rel error
assert(iue::num::isclose(a, b, 0.0, 2 * eps));
assert(!iue::num::isclose(a, b, 0.0, 0.0));
// abs error
assert(iue::num::isclose(a, b, 2 * eps, 0.0));
assert(!iue::num::isclose(a, b, 0.0, 0.0));
}
{ // around 1e10
double a = 1.0 * 1e10;
double b = (1.0 + eps) * 1e10;
// rel error
assert(iue::num::isclose(a, b, 0.0, 2 * eps));
assert(!iue::num::isclose(a, b, 0.0, 0.0));
// abs error
assert(iue::num::isclose(a, b, 1e10 * 2 * eps, 0.0));
assert(!iue::num::isclose(a, b, 1e9 * 2 * eps, 0.0));
}
{ // around 1e-10
double a = 1.0 * 1e-10;
double b = (1.0 + eps) * 1e-10;
// rel error
assert(iue::num::isclose(a, b, 0.0, 2 * eps));
assert(!iue::num::isclose(a, b, 0.0, 0.0));
// abs error
assert(iue::num::isclose(a, b, 1e-10 * 2 * eps, 0.0));
assert(!iue::num::isclose(a, b, 1e-11 * 2 * eps, 0.0));
}
return EXIT_SUCCESS;
}
+53
View File
@@ -0,0 +1,53 @@
cmake_minimum_required(VERSION 3.28)
# define CXX interface library
# define target & alias (for other projects consuming the "source-tree")
add_library(iue-other_honly INTERFACE)
# define sources of target
target_sources(iue-other_honly
INTERFACE FILE_SET public_headers TYPE HEADERS BASE_DIRS ${PROJECT_SOURCE_DIR} FILES func.hpp func.detail.hpp)
# define installation step
install(TARGETS iue-other_honly EXPORT iue-other-export
FILE_SET public_headers DESTINATION "./")
# define test
if(BUILD_TESTING AND PROJECT_IS_TOP_LEVEL)
add_executable(iue-other_honly.test func.test.cpp)
target_link_libraries(iue-other_honly.test iue-other_honly)
add_test(NAME iue-other_honly.test COMMAND iue-other_honly.test)
endif()
# define CXX normal library
# define target & alias (for other projects consuming the "source-tree")
add_library(iue-other_lib)
# define sources of target
target_sources(iue-other_lib
PRIVATE library.cpp
PUBLIC FILE_SET public_headers TYPE HEADERS BASE_DIRS ${PROJECT_SOURCE_DIR} FILES library.hpp)
# define installation step
install(TARGETS iue-other_lib EXPORT iue-other-export
FILE_SET public_headers DESTINATION "./"
LIBRARY DESTINATION "./")
# define test
if(BUILD_TESTING AND PROJECT_IS_TOP_LEVEL)
add_executable(iue-other_lib.test library.test.cpp)
target_link_libraries(iue-other_lib.test iue-other_lib)
add_test(NAME iue-other_lib.test COMMAND iue-other_lib.test)
endif()
# export targets for consuming cmake projects
# generate and install a .cmake file to allow dependend projects consuming the "install-tree" to import all targets
install(EXPORT iue-other-export DESTINATION "iue-other" )
# allow dependend projects consuming the "build-tree" to use all targets
export(EXPORT iue-other-export)
+7
View File
@@ -0,0 +1,7 @@
#pragma once
namespace iue::other::detail {
inline int calc(int a) { return a * 2; }
} // namespace iue::other::detail
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "func.detail.hpp"
namespace iue::other {
inline int header_only(int a) { return detail::calc(a); }
} // namespace iue::other
+9
View File
@@ -0,0 +1,9 @@
#include "iue-other/func.hpp"
#include <cassert> // assert
int main() {
assert(iue::other::header_only(5) == 10);
return 0;
}
+9
View File
@@ -0,0 +1,9 @@
#include "library.hpp"
namespace iue::other {
int Widget::calc() const { return 5; }
int calc(const Widget& w) { return w.calc(); }
} // namespace iue::other
+11
View File
@@ -0,0 +1,11 @@
#pragma once
namespace iue::other {
struct Widget {
int calc() const;
};
int calc(const Widget& w);
} // namespace iue::other
+12
View File
@@ -0,0 +1,12 @@
#include "iue-other/library.hpp"
#include <cassert> // assert
int main() {
auto w = iue::other::Widget();
assert(w.calc() == 5);
assert(iue::other::calc(w) == 5);
return 0;
}
+51
View File
@@ -0,0 +1,51 @@
cmake_minimum_required(VERSION 3.28)
# define CXX interface library
# define target & alias (for other projects consuming the "source-tree")
add_library(iue-po_po INTERFACE)
# define sources of target
target_sources(iue-po_po
INTERFACE FILE_SET public_headers TYPE HEADERS BASE_DIRS ${PROJECT_SOURCE_DIR} FILES po.hpp)
# define installation step
install(TARGETS iue-po_po EXPORT iue-po-export
FILE_SET public_headers DESTINATION "./")
# define test
if(BUILD_TESTING AND PROJECT_IS_TOP_LEVEL)
add_executable(iue-po_po.test po.test.cpp)
target_link_libraries(iue-po_po.test iue-po_po)
add_test(NAME iue-po_po.test COMMAND iue-po_po.test --option1 100 -option2 o2)
endif()
# define C interface library
# define target & alias (for other projects consuming the "source-tree")
add_library(iue-po_cpo INTERFACE)
# define sources of target
target_sources(iue-po_cpo
INTERFACE FILE_SET public_headers TYPE HEADERS BASE_DIRS ${PROJECT_SOURCE_DIR} FILES cpo.h)
# define installation step
install(TARGETS iue-po_cpo EXPORT iue-po-export
FILE_SET public_headers DESTINATION "./")
# define test
if(BUILD_TESTING AND PROJECT_IS_TOP_LEVEL)
add_executable(iue-po_cpo.test cpo.test.c)
target_link_libraries(iue-po_cpo.test iue-po_cpo)
add_test(NAME iue-po_cpo.test COMMAND iue-po_cpo.test --option1 100 -option2 o2)
endif()
# export targets for consuming cmake projects
# generate and install a .cmake file to allow dependend projects consuming the "install-tree" to import all targets
install(EXPORT iue-po-export DESTINATION "iue-po" )
# allow dependend projects consuming the "build-tree" to use all targets
export(EXPORT iue-po-export)
+104
View File
@@ -0,0 +1,104 @@
#pragma once
#include <stdbool.h> // bool
#include <stdio.h> // snprintf|stderr
#include <stdlib.h> // EXIT_FAILURE|EXIT_SUCCESS
#include <string.h> // strncmp|strlen
/// @brief internal function
static bool iuepo_find_option(const char* option, const char* arg) {
char buffer[256];
snprintf(buffer, sizeof(buffer) / sizeof(char), "%s%s", "-", option);
if (strncmp(buffer, arg, strlen(buffer)) == 0)
return true;
snprintf(buffer, sizeof(buffer) / sizeof(char), "%s%s", "--", option);
if (strncmp(buffer, arg, strlen(buffer)) == 0)
return true;
return false;
}
/// @brief internal function
static int iuepo_count_if(const char* option, char** begin, char** end) {
int res = 0;
for (char** iter = begin; iter != end; ++iter) {
if (iuepo_find_option(option, *iter))
++res;
}
return res;
}
/// @brief internal function
static char** iuepo_find_if(const char* option, char** begin, char** end) {
int res = 0;
for (char** iter = begin; iter != end; ++iter) {
if (iuepo_find_option(option, *iter))
return iter;
}
return end;
}
/// @todo Improve documentation of this enum
enum iuepo_option { IUEPO_REQUIRED = 0, IUEPO_OPTIONAL };
/// @todo Improve documentation of this function
/// @brief Optain a string option from command line arguments
static int iuepo_get_string(int argc, char* argv[], const char* option, char* default_value, int N,
enum iuepo_option op) {
int count = iuepo_count_if(option, argv + 1, argv + argc);
if (count == 0) {
if (op == IUEPO_REQUIRED) {
fprintf(stderr, "required option not provided: -/-- %s\n", option);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
if (count > 1) {
fprintf(stderr, "option not unique: -/-- %s (count = %i )\n", option, count);
return EXIT_FAILURE;
}
char** iter = iuepo_find_if(option, argv + 1, argv + argc);
++iter;
if (iter == argv + argc) {
fprintf(stderr, "option not followed by value: -/-- %s\n", option);
return EXIT_FAILURE;
}
if (strncmp("-", *iter, 1) == 0) {
fprintf(stderr, "option -/-- %s not followed by value but another option -/-- %s\n", option, *iter);
return EXIT_FAILURE;
}
if (snprintf(default_value, N, "%s", *iter) >= N) {
fprintf(stderr, "provided buffer size %i for option -/-- %s not sufficient\n", N, option);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
/// @todo Improve documentation of this function
/// @brief Wrapping iuepo_get_string with a subsequent conversion
static int iuepo_get_int(int argc, char* argv[], const char* option, int* default_value, enum iuepo_option op) {
char default_value_string[256] = "\0";
snprintf(default_value_string, sizeof(default_value_string), "%i", *default_value);
if (iuepo_get_string(argc, argv, option, default_value_string, sizeof(default_value_string), op) == EXIT_FAILURE) {
return EXIT_FAILURE;
}
int res = sscanf(default_value_string, "%i", default_value);
if (res != 1) {
fprintf(stderr, "value for option -/-- %s could not be converted\n", option);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
+32
View File
@@ -0,0 +1,32 @@
#include "iue-po/cpo.h"
#include <assert.h> // assert
#include <stdio.h> // printf
#include <stdlib.h>
#include <string.h> // strcmp
int main(int argc, char* argv[]) {
int value1 = 1;
if (iuepo_get_int(argc, argv, "option1", &value1, IUEPO_REQUIRED) != EXIT_SUCCESS)
return EXIT_FAILURE;
char value2[256] = "default2";
if (iuepo_get_string(argc, argv, "option2", value2, sizeof(value2), IUEPO_REQUIRED) != EXIT_SUCCESS)
return EXIT_FAILURE;
char value3[256] = "default3";
if (iuepo_get_string(argc, argv, "option3", value3, sizeof(value3), IUEPO_OPTIONAL) != EXIT_SUCCESS)
return EXIT_FAILURE;
printf("'%i'\n", value1);
printf("'%s'\n", value2);
printf("'%s'\n", value3);
assert(value1 == 100);
assert(strcmp(value2, "o2") == 0);
assert(strcmp(value3, "default3") == 0);
return EXIT_SUCCESS;
}
+60
View File
@@ -0,0 +1,60 @@
#pragma once
#include <algorithm>
#include <sstream>
#include <string>
#include <vector>
namespace iue::po {
inline std::vector<std::string> init(int argc, char* argv[]) {
return {argv + 1, argv + argc};
}
enum struct option { optional, required };
template <typename T>
inline T get(std::vector<std::string> args, std::string option, T default_value, enum option op = option::optional) {
auto find_option = [&option](const std::string& arg) -> bool {
return arg.starts_with("-" + option) || arg.starts_with("--" + option);
};
const auto count = std::ranges::count_if(args, find_option);
if (count == 0) {
if (op == option::required) {
throw std::runtime_error("required option not provided: -/--" + option);
}
return default_value;
}
if (count > 1) {
throw std::runtime_error("option not unique: -/--" + option + " (count = " + std::to_string(count) + ")");
}
auto it = std::ranges::find_if(args, find_option);
auto value = std::next(it);
if (value == args.end()) {
throw std::runtime_error("option not followed by value: -/--" + option);
}
if (value->starts_with('-')) {
throw std::runtime_error("option -/--" + option + " not followed by value but another option -/--" + *value);
}
std::istringstream ss(*value);
ss.exceptions(std::ios::failbit);
T num;
try {
ss >> num;
} catch (std::exception& e) {
throw std::runtime_error("value for option -/--" + option + " could not be converted");
}
return num;
}
} // namespace iue::po
+23
View File
@@ -0,0 +1,23 @@
#include "iue-po/po.hpp"
#include <cassert> // assert
#include <iostream> // std::cout|endl
int main(int argc, char* argv[]) {
auto args = iue::po::init(argc, argv);
auto value1 = iue::po::get(args, "option1", 1, iue::po::option::required);
auto value2 = iue::po::get(args, "option2", std::string("default2"), iue::po::option::required);
auto value3 = iue::po::get(args, "option3", std::string("default3"));
std::cout << value1 << std::endl;
std::cout << value2 << std::endl;
std::cout << value3 << std::endl;
assert(value1 == 100);
assert(value2 == "o2");
assert(value3 == "default3");
return 0;
}
+30
View File
@@ -0,0 +1,30 @@
cmake_minimum_required(VERSION 3.28)
# define CXX interface library
# define target & alias (for other projects consuming the "source-tree")
add_library(iue-rnd_rnd INTERFACE)
# define sources of target
target_sources(iue-rnd_rnd
INTERFACE FILE_SET public_headers TYPE HEADERS BASE_DIRS ${PROJECT_SOURCE_DIR} FILES random.hpp)
# define installation step
install(TARGETS iue-rnd_rnd EXPORT iue-rnd-export
FILE_SET public_headers DESTINATION "./")
# define test
if(BUILD_TESTING AND PROJECT_IS_TOP_LEVEL)
add_executable(iue-rnd_rnd.test random.test.cpp)
target_link_libraries(iue-rnd_rnd.test iue-rnd_rnd)
add_test(NAME iue-rnd_rnd.test COMMAND iue-rnd_rnd.test)
endif()
# export targets for consuming cmake projects
# generate and install a .cmake file to allow dependend projects consuming the "install-tree" to import all targets
install(EXPORT iue-rnd-export DESTINATION "iue-rnd" )
# allow dependend projects consuming the "build-tree" to use all targets
export(EXPORT iue-rnd-export)
+128
View File
@@ -0,0 +1,128 @@
/// @file
/// @brief Generators for uniform distributions of Values, Circles and Triangles
#pragma once
#include <array> // std::array
#include <cmath> // std::sqrt, std::sin, std::cos
#include <numbers> // std::numbers::pi
#include <random> // std::mt19937, std::uniform_real_distribution
#include <tuple> // std::tuple
namespace iue::rnd {
using Vec2d = std::array<double, 2>;
using Circle = std::tuple<Vec2d, double>;
using Triangle = std::array<Vec2d, 3>;
///@brief Uniformly distributed speudorandom floating point values
struct UniformValue {
std::mt19937 algo; ///< Random number generator
std::uniform_real_distribution<double> dist; ///< Interval and distribution type
/// @brief Constructs an instance producing random values uniformly distributed over an interval
/// @param min Lower bound of the interval
/// @param max Upper bound of the interval
/// @param seed Seed used to initialize the generator algorithm
UniformValue(const double min, const double max, const std::size_t seed = 1) : algo(seed), dist(min, max) {}
/// @brief Obtains the next random value using current state of the generator
/// @return Random value
double operator()() { return dist(algo); }
};
///@brief Uniformly distributed circles
struct UniformCircle {
std::mt19937 algo; ///< Random number generator
std::uniform_real_distribution<double> x; ///< Interval and distribution type for the x-coordinate
std::uniform_real_distribution<double> y; ///< Interval and distribution type for the y-coordinate
std::uniform_real_distribution<double> r; ///< Interval and distribution type for the r-coordinate
/// @brief Constructs an instance producing random circles uniformly distributed (center and radius)
/// @param c_min Lower bound of the coordinate of the center
/// @param c_max Upper bound of the coordinate of the center
/// @param r_min Lower bound of the radius
/// @param r_max Upper ound of the radius
/// @param seed Seed used to initialize the generator algorithm
UniformCircle(const Vec2d& c_min, const Vec2d& c_max, double r_min, double r_max, const std::size_t seed = 1)
: algo(seed), x(c_min[0], c_max[0]), y(c_min[1], c_max[1]), r(r_min, r_max) {}
/// @brief Obtains the next random circle using current state of the generator
/// @return Random circle
std::tuple<std::array<double, 2>, double> operator()() {
// random center
Vec2d center = {x(algo), y(algo)};
// random raidus
double radius = r(algo);
// return final configuration
return {center, radius};
}
};
///@brief Uniformly distributed triangles
struct UniformTriangle {
std::mt19937 algo; ///< Random number generators
std::uniform_real_distribution<double> c_x; ///< distribution for the x-coordinate of the circumcircle
std::uniform_real_distribution<double> c_y; ///< distribution for the y-coordinate of the circumcircle
std::uniform_real_distribution<double> c_r; ///< distribution for the radius of the circumcircle
std::uniform_real_distribution<double> angle120; ///< distribution for the angles of the corner points
std::uniform_real_distribution<double> angle360; ///< distribution for the random rotation of the triangle
/// @brief Constructs an instance producing random triangles with
/// - uniformly distributed circumcircles, and
/// - uniformly distributed corner points w.r.t. three fixed non-overlapping 120-degree segments on the unit circle
/// @param c_min Lower bound of the coordinate of the circumcirle
/// @param c_max Upper bound of the coordinate of the circumcirle
/// @param r_min Lower bound of the radius of the circle
/// @param r_max Upper ound of the radius of the circle
/// @param seed Seed used to initialize the generator algorithm
UniformTriangle(const Vec2d& c_min, const Vec2d& c_max, double r_min, double r_max, const std::size_t seed = 1)
: algo(seed), c_x(c_min[0], c_max[0]), c_y(c_min[1], c_max[1]), c_r(r_min, r_max),
angle120(0, std::numbers::pi * 2 / 3), angle360(0, std::numbers::pi * 2) {}
/// @brief Obtains the next random triangle using current state of the generator
/// @return Random circle
Triangle operator()() {
// three points on the unit circle constraint to three 120deg-segments
// these will be the points of the triangle
double seg = angle120.max();
double alpha = seg * 0 + angle120(algo);
double beta = seg * 1 + angle120(algo);
double gamma = seg * 2 + angle120(algo);
Vec2d a = {std::sin(alpha), std::cos(alpha)};
Vec2d b = {std::sin(beta), std::cos(beta)};
Vec2d c = {std::sin(gamma), std::cos(gamma)};
Triangle abc = {a, b, c};
// random circumcircle center
Vec2d center = {c_x(algo), c_y(algo)};
// random circumcircle radius
double r = c_r(algo);
// random rotation
double rot = angle360(algo);
auto rotate = [rot](const Vec2d& p) -> const Vec2d {
return {std::cos(rot) * p[0] + (-std::sin(rot)) * p[1], std::sin(rot) * p[0] + std::cos(rot) * p[1]};
};
auto shift = [center](const Vec2d& p) -> const Vec2d { return {p[0] + center[0], p[1] + center[1]}; };
auto scale = [r](const Vec2d& p) -> const Vec2d { return {p[0] * r, p[1] * r}; };
// rotate/scale/shift to final configuration
for (auto& coord : abc) {
coord = rotate(coord);
coord = scale(coord);
coord = shift(coord);
}
// return final configuration
return {abc};
}
};
} // namespace iue::rnd
+93
View File
@@ -0,0 +1,93 @@
/// @file
/// @brief Test for iue::rnd::UniformValue, iue::rnd::UniformTriangle, and iue::rnd::UniformCircle
#include <cassert> // assert
#include <numeric> // std::accumulate
#include <vector> // std::vector
#include "iue-rnd/random.hpp" // iue::rnd::UniformValue, iue::rnd::UniformTriangle, iue::rnd::UniformCircle
#include <iue-num/numerics.hpp> // iue::num::isclose
int main() {
{ // random value
std::vector<double> items;
auto gen = iue::rnd::UniformValue(1.0, 2.0);
size_t N = 1e5;
for (int n = 0; n != N; ++n)
items.push_back({gen()});
// mean
auto get_value = [](double sum, const double& value) { return sum + value; };
auto mean = std::accumulate(items.begin(), items.end(), 0.0, get_value) / items.size();
assert(iue::num::isclose(mean, 1.5, 1e3 / N));
}
{ // random circle
std::vector<iue::rnd::Circle> items;
// generate random circles
iue::rnd::Vec2d min = {1, 1};
iue::rnd::Vec2d max = {2, 2};
auto gen = iue::rnd::UniformCircle(min, max, 1, 2);
size_t N = 1e5;
for (int n = 0; n != N; ++n)
items.push_back({gen()});
// r_mean
auto get_r = [](double sum, const iue::rnd::Circle& circ) {
const auto& [c, r] = circ;
return sum + r;
};
auto r_mean = std::accumulate(items.begin(), items.end(), 0.0, get_r) / items.size();
assert(iue::num::isclose(r_mean, 1.5, 1e3 / N));
// cx_mean
auto get_cx = [](double sum, const iue::rnd::Circle& circ) {
const auto& [c, r] = circ;
return sum + c[0];
};
auto cx_mean = std::accumulate(items.begin(), items.end(), 0.0, get_cx) / items.size();
assert(iue::num::isclose(cx_mean, 1.5, 1e3 / N));
// cy_mean
auto get_cy = [](double sum, const iue::rnd::Circle& circ) {
const auto& [c, r] = circ;
return sum + c[1];
};
auto cy_mean = std::accumulate(items.begin(), items.end(), 0.0, get_cy) / items.size();
assert(iue::num::isclose(cy_mean, 1.5, 1e3 / N));
}
{ // random triangle
std::vector<iue::rnd::Triangle> items;
// generate random circles
iue::rnd::Vec2d min = {1, 1};
iue::rnd::Vec2d max = {2, 2};
auto gen = iue::rnd::UniformTriangle(min, max, 1, 2);
size_t N = 1e5;
for (int n = 0; n != N; ++n)
items.push_back({gen()});
// abcx_mean
auto get_x = [](double sum, const iue::rnd::Triangle& tri) {
const auto& [a, b, c] = tri;
return sum + a[0] + b[0] + c[0];
};
auto abcx_mean = std::accumulate(items.begin(), items.end(), 0.0, get_x) / items.size() / 3.0;
assert(iue::num::isclose(abcx_mean, 1.5, 1e3 / N));
// abcx_mean
auto get_y = [](double sum, const iue::rnd::Triangle& tri) {
const auto& [a, b, c] = tri;
return sum + a[1] + b[1] + c[1];
};
auto abcy_mean = std::accumulate(items.begin(), items.end(), 0.0, get_y) / items.size() / 3.0;
assert(iue::num::isclose(abcy_mean, 1.5, 1e3 / N));
}
return 0;
}
+39
View File
@@ -0,0 +1,39 @@
cmake_minimum_required(VERSION 3.28)
# define CXX interface library
# define target & alias (for other projects consuming the "source-tree")
add_library(iue-svg_svg INTERFACE)
# define sources of target
target_sources(iue-svg_svg
INTERFACE FILE_SET public_headers TYPE HEADERS BASE_DIRS ${PROJECT_SOURCE_DIR} FILES tags.hpp tree.hpp render.hpp)
# define installation step
install(TARGETS iue-svg_svg EXPORT iue-svg-export
FILE_SET public_headers DESTINATION "./")
# define test
if(BUILD_TESTING AND PROJECT_IS_TOP_LEVEL)
add_executable(iue-svg_tree.test tree.test.cpp)
target_link_libraries(iue-svg_tree.test iue-svg_svg)
add_test(NAME iue-svg_tree.test COMMAND iue-svg_tree.test)
add_executable(iue-svg_tags.test tags.test.cpp)
target_link_libraries(iue-svg_tags.test iue-svg_svg)
add_test(NAME iue-svg_tags.test COMMAND iue-svg_tags.test)
add_executable(iue-svg_render.test render.test.cpp)
target_link_libraries(iue-svg_render.test iue-svg_svg)
add_test(NAME iue-svg_render.test COMMAND iue-svg_render.test)
endif()
# export targets for consuming cmake projects
# generate and install a .cmake file to allow dependend projects consuming the "install-tree" to import all targets
install(EXPORT iue-svg-export DESTINATION "iue-svg" )
# allow dependend projects consuming the "build-tree" to use all targets
export(EXPORT iue-svg-export)
+157
View File
@@ -0,0 +1,157 @@
/// @file
/// @brief Educational implementation of a SVG-Writer
#pragma once
#include "iue-svg/tags.hpp" // iue::svg:::XMLTag, iue::svg::svg
#include "iue-svg/tree.hpp" // iue::svg::Tree
#include <cassert> // assert
#include <filesystem> // std::filesystem::path
#include <fstream> // std::ofstream
#include <sstream> // std::stringstream
#include <string> // std::string
#include <vector> // std::vector
namespace iue::svg {
/// @brief Generate a SVG file from sequences of compatible primitives
/// @return Enclosing bounding box (original unscaled coordinates)
/// Imporant notes:
/// 1. The outline of the svg will be the bounding box enclosing all occuring geometric primitives (based on coordinate
/// based)
/// - the detail of the extend of an object (textlength, fontsize, linewidth, ...) is **not** considered
/// 2. The coordinates are **scaled/rotated/translated before serializing**
/// - this is not so nice, but a scaling is required to improve the viewing experience for small coordinate spans
/// 3. All primitives are rendered with black lines and no fill
/// - the linewidth is choosen relative the the dimension of the enclosing bounding box
/// - the font-size for text is relative to the linewith
inline iue::svg::BBox render(std::filesystem::path filepath, const std::vector<iue::svg::BBox>& bboxes,
const std::vector<iue::svg::Circle>& circles, const std::vector<iue::svg::Triangle>& triangles) {
using XMLTree = Tree<XMLTag>;
assert(bboxes.size() != 0 || circles.size() != 0 || triangles.size() != 0);
// calculate maximum extends
Vec2d bbmin = {+std::numeric_limits<double>::max(), +std::numeric_limits<double>::max()};
Vec2d bbmax = {-std::numeric_limits<double>::max(), -std::numeric_limits<double>::max()};
// update minmax from single coordinate
auto minmax = [&bbmin, &bbmax](const Vec2d& p) {
bbmin[0] = bbmin[0] < p[0] ? bbmin[0] : p[0];
bbmin[1] = bbmin[1] < p[1] ? bbmin[1] : p[1];
bbmax[0] = bbmax[0] > p[0] ? bbmax[0] : p[0];
bbmax[1] = bbmax[1] > p[1] ? bbmax[1] : p[1];
};
for (const auto& circle : circles) {
const auto& [c, r] = circle;
minmax({c[0] - r, c[1] - r});
minmax({c[0] + r, c[1] + r});
}
for (const auto& bbox : bboxes) {
const auto& [min, max] = bbox;
minmax(min);
minmax(max);
}
for (const auto& triangle : triangles) {
for (const auto& p : triangle)
minmax(p);
}
// scaling
double resolution;
{
auto const& [xmin, ymin] = bbmin;
auto const& [xmax, ymax] = bbmax;
double width = std::abs(xmax - xmin);
double height = std::abs(ymax - ymin);
resolution = std::max(width, height);
}
double unit = resolution / 800; // stroke referens is "thin" for a reference resolution
double scale = 1 / unit;
// scaling to have a unit stroke of 1
auto sc = [scale](const Vec2d& p) -> Vec2d { return {p[0] * scale, p[1] * scale}; };
auto scd = [scale](const double& val) -> double { return val * scale; };
// transform to have bottom left origin (svg default is top left origin)
auto tf = [xmin = bbmin[0], ymax = bbmax[1]](const Vec2d& p) -> Vec2d { return {(p[0] - xmin), (-p[1] + ymax)}; };
// create tree with svg
XMLTree dom{svg(sc(bbmin), sc(bbmax))};
dom.org.nodes.push_back({svg::group()});
auto& g = dom.org.nodes.back();
{ // plot all triangles
for (const auto& triangle : triangles) {
const auto& [a, b, c] = triangle;
g.nodes.push_back({svg::polygon({sc(tf(a)), sc(tf(b)), sc(tf(c))})});
}
}
{ // plot all BBoxes
for (const auto& bbox : bboxes) {
const auto& [min, max] = bbox;
const auto& [xmin, ymin] = min;
const auto& [xmax, ymax] = max;
std::vector<Vec2d> coords;
coords.push_back(sc(tf({xmin, ymin})));
coords.push_back(sc(tf({xmax, ymin})));
coords.push_back(sc(tf({xmax, ymax})));
coords.push_back(sc(tf({xmin, ymax})));
g.nodes.push_back({svg::polygon(coords)});
}
}
{ // plot all circles
for (const auto& circle : circles) {
const auto& [c, r] = circle;
g.nodes.push_back({svg::circle({sc(tf(c)), scd(r)})});
}
}
{ // plot bounding box of viewport
const auto& [xmin, ymin] = bbmin;
const auto& [xmax, ymax] = bbmax;
std::vector<Vec2d> limits;
limits.push_back(sc(tf({xmin, ymin})));
limits.push_back(sc(tf({xmax, ymin})));
limits.push_back(sc(tf({xmax, ymax})));
limits.push_back(sc(tf({xmin, ymax})));
g.nodes.push_back({svg::polygon(limits)});
}
// serialize tree to string
std::stringstream stream;
{
auto down = [&stream](const XMLTag& value, std::size_t level) -> void {
stream << std::string(2 * level, ' ') << value.open() << std::endl;
auto text = value.text();
if (!text.empty())
stream << std::string(2 * level, ' ') << value.text() << std::endl;
};
auto up = [&stream](const XMLTag& value, std::size_t level) -> void {
auto close = value.close();
if (!close.empty())
stream << std::string(2 * level, ' ') << value.close() << std::endl;
};
XMLTree::preorder(dom.org, down, up);
}
// save as file
std::ofstream fs;
fs.exceptions(std::ifstream::badbit);
fs.open(filepath);
fs << stream.str();
return {bbmin, bbmax};
}
} // namespace iue::svg
+37
View File
@@ -0,0 +1,37 @@
/// @file
/// @brief Test for 'iue::svg::render'
#include "iue-svg/render.hpp" // iue::svg::render
#include "iue-num/numerics.hpp" // iue::num::isclose
#include "iue-svg/tags.hpp" // iue::svg::Circle, iue::svg::Triangle, iue::svg::BBox
int main() {
std::vector<iue::svg::Circle> circles;
circles.push_back(iue::svg::Circle{{1, 1}, 1});
circles.push_back(iue::svg::Circle{{2, 2}, 2});
circles.push_back(iue::svg::Circle{{3, 3}, 3});
circles.push_back(iue::svg::Circle{{10, 10}, 10});
std::vector<iue::svg::Triangle> triangles;
triangles.push_back(iue::svg::Triangle{{{50, 50}, {49, 50}, {50, 49}}});
triangles.push_back(iue::svg::Triangle{{{50, 50}, {48, 50}, {50, 48}}});
triangles.push_back(iue::svg::Triangle{{{50, 50}, {47, 50}, {50, 47}}});
triangles.push_back(iue::svg::Triangle{{{50, 50}, {40, 50}, {50, 40}}});
std::vector<iue::svg::BBox> bboxes;
bboxes.push_back(iue::svg::BBox{{0, 49}, {1, 50}});
bboxes.push_back(iue::svg::BBox{{0, 48}, {2, 50}});
bboxes.push_back(iue::svg::BBox{{0, 47}, {4, 50}});
bboxes.push_back(iue::svg::BBox{{0, 40}, {10, 50}});
auto [bbmin, bbmax] = iue::svg::render("render.test.svg", bboxes, circles, triangles);
assert(iue::num::isclose(bbmin[0], 0));
assert(iue::num::isclose(bbmin[1], 0));
assert(iue::num::isclose(bbmax[0], 50));
assert(iue::num::isclose(bbmax[1], 50));
return 0;
}
+151
View File
@@ -0,0 +1,151 @@
/// @file
/// @brief Implementation of SVG-Tags for the SVG-writer in render.hpp
#pragma once
#include <array> // std::array
#include <sstream> // std::stringstream
#include <string> // std::string
#include <unordered_map> // std::unordered_map
#include <vector> // std::vector
namespace iue::svg {
/// @brief convenience aliases for short notations
using Vec2d = std::array<double, 2>;
using Circle = std::tuple<Vec2d, double>;
using Polygon = std::vector<Vec2d>;
using Triangle = std::array<Vec2d, 3>;
using BBox = std::tuple<Vec2d, Vec2d>;
using Text = std::tuple<Vec2d, std::string>;
/// @brief Map for storing an open set of attribute/value pairs
using Attributes = std::unordered_map<std::string, std::string>;
/// @brief Requirements towards an XMLTag
template <typename T>
concept Tag = requires(const T& t) {
{ t.open() } -> std::same_as<std::string>;
{ t.text() } -> std::same_as<std::string>;
{ t.close() } -> std::same_as<std::string>;
};
/// @brief Implementation of an SVG XML-tag
struct XMLTag {
std::string name;
Attributes attributes;
std::string content;
bool self_closing;
std::string text() const { return this->content; }
std::string open() const {
std::stringstream res;
res << "<" << name;
for (auto const& [key, val] : attributes)
res << " " << key << "='" << val << "'";
if (self_closing)
res << "/>";
else
res << ">";
return res.str();
}
std::string close() const {
std::stringstream res;
if (!self_closing)
res << "</" << name << ">";
return res.str();
}
};
/// @brief Check if Tag requirements are fullfilled
static_assert(Tag<XMLTag>);
/// @brief Root SVG Element
/// <svg preserveAspectRatio='xMidYMid meet' viewbox='0 0 w h' height='w' width='h'></svg>
inline XMLTag svg(const Vec2d& bbmin, const Vec2d& bbmax) {
Attributes attributes;
// add data attributes
auto const& [xmin, ymin] = bbmin;
auto const& [xmax, ymax] = bbmax;
double width = std::abs(xmax - xmin);
double height = std::abs(ymax - ymin);
attributes.insert({"width", std::to_string(width)});
attributes.insert({"height", std::to_string(height)});
std::stringstream viewbox;
for (const auto& val : {xmin, ymin, width, height})
viewbox << val << " ";
attributes.insert({"viewbox", viewbox.str()});
// add default attributes
attributes.insert({"xmlns", "http://www.w3.org/2000/svg"});
attributes.insert({"version", "1.1"});
attributes.insert({"preserveAspectRatio", "xMidYMid meet"});
// final assembly
return {"svg", attributes, std::string{}, false};
}
/// @brief Group SVG Element
/// <g></g>
inline XMLTag group(const Attributes& custom_attributes = {}) {
Attributes attributes;
// add custom attributes
attributes.insert(custom_attributes.begin(), custom_attributes.end());
// final assembly
return {"g", attributes, std::string{}, false};
}
/// @brief Circle SVG Element
/// <circle r='r0' cx='cx' cy='cy' stroke-width='1' fill='none' stroke='black'/>
inline XMLTag circle(const Circle& circle, const Attributes& custom_attributes = {}) {
Attributes attributes;
// add data attributes
const auto& [center, radius] = circle;
const auto& [cx, cy] = center;
attributes.insert({"cx", std::to_string(cx)});
attributes.insert({"cy", std::to_string(cy)});
attributes.insert({"r", std::to_string(radius)});
// add custom attributes
attributes.insert(custom_attributes.begin(), custom_attributes.end());
// add default attributes
attributes.insert({"stroke-width", std::to_string(1)});
attributes.insert({"stroke", "black"});
attributes.insert({"fill", "none"});
// final assembly
return {"circle", attributes, std::string{}, true};
}
/// @brief Polygon SVG Element
/// <polygon fill='none' stroke='black' stroke-width='1' points=' x1,y1 x2,y2, x3,y3 ... '/>
inline XMLTag polygon(const Polygon& polygon, const Attributes& custom_attributes = {}) {
Attributes attributes;
// add data attributes
std::stringstream points;
for (const auto& [x, y] : polygon)
points << x << "," << y << " ";
attributes.insert({"points", points.str()});
// add custom attributes
attributes.insert(custom_attributes.begin(), custom_attributes.end());
// add default attributes
attributes.insert({"stroke-width", std::to_string(1)});
attributes.insert({"stroke", "black"});
attributes.insert({"fill", "none"});
// final assembly
return {"polygon", attributes, std::string{}, true};
}
/// @brief Text SVG Element
/// <text font-size='10' text-anchor='middle' x='x' y='y' > content </text>
inline XMLTag text(const Text& text, const Attributes& custom_attributes = {}) {
Attributes attributes;
// add data attributes
const auto& [anchor, content] = text;
const auto& [x, y] = anchor;
attributes.insert({"x", std::to_string(x)});
attributes.insert({"y", std::to_string(y)});
// add custom attributes
attributes.insert(custom_attributes.begin(), custom_attributes.end());
// add default attributes
attributes.insert({"font-size", std::to_string(10)});
attributes.insert({"text-anchor", "middle"});
return {"text", attributes, content, false};
}
} // namespace iue::svg
+62
View File
@@ -0,0 +1,62 @@
/// @file
/// @brief Test for iue::svg::XMLTag
#include "iue-svg/tags.hpp" // iue::svg::XMLTag, iue::svg::svg, iue::svg::circle, iue::svg::polygon, iue::svg::, iue::svg::text
#include <cstdlib> // EXIT_SUCCESS
#include <iostream> // std::cout, std::endl
#include <string> // std::string
struct Value {
int a = 1;
};
int main() {
{ // w/ default attributes
auto svg = iue::svg::svg({0, 0}, {100, 100});
auto group = iue::svg::group();
auto text = iue::svg::text({{50, 50}, "text"});
auto circle = iue::svg::circle({{50, 50}, 0.5});
auto polygon = iue::svg::polygon({{50, 50}, {52, 51}, {51, 52}, {78, 100}});
std::cout << svg.open() << std::endl;
std::cout << group.open() << std::endl;
std::cout << text.open() << std::endl;
std::cout << text.text() << std::endl;
std::cout << text.close() << std::endl;
std::cout << circle.open() << std::endl;
std::cout << circle.close() << std::endl;
std::cout << polygon.open() << std::endl;
std::cout << polygon.close() << std::endl;
std::cout << group.close() << std::endl;
std::cout << svg.close() << std::endl;
}
{ // w/ custom attributes
iue::svg::Attributes attributes;
attributes.insert({"stroke-width", std::to_string(10)});
attributes.insert({"stroke", "green"});
attributes.insert({"fill", "blue"});
attributes.insert({"invalid-attribute", "no check is performed"});
auto svg = iue::svg::svg({0, 0}, {100, 100});
auto group = iue::svg::group(attributes);
auto text = iue::svg::text({{50, 50}, "text"}, attributes);
auto circle = iue::svg::circle({{50, 50}, 0.5}, attributes);
auto polygon = iue::svg::polygon({{50, 50}, {52, 51}, {51, 52}, {78, 100}}, attributes);
std::cout << svg.open() << std::endl;
std::cout << group.open() << std::endl;
std::cout << text.open() << std::endl;
std::cout << text.text() << std::endl;
std::cout << text.close() << std::endl;
std::cout << circle.open() << std::endl;
std::cout << circle.close() << std::endl;
std::cout << polygon.open() << std::endl;
std::cout << polygon.close() << std::endl;
std::cout << group.close() << std::endl;
std::cout << svg.close() << std::endl;
}
return EXIT_SUCCESS;
}
+56
View File
@@ -0,0 +1,56 @@
/// @file
/// @brief Educational implementation of a tree data structure with value semantics
/// @note The implementation relies on the "minimal incomplete type support" incomplete type support for std::list
/// - https://en.cppreference.com/w/cpp/feature_test#cpp_lib_incomplete_container_elements
#pragma once
#include <cstddef> // std::size_t
#include <functional> // std::function
#include <list> // std::string
namespace iue::svg {
template <typename T>
concept Value = requires(const T& a, T& b) {
T(a); ///< copy constructor (std::copy_constructible)
b = a; ///< copy assignment (std::assignable_from)
};
/// @brief Generic tree (tree with a dynamic unbound number of child nodes)
template <Value Value> struct Tree {
/// @brief Node in the tree
struct Node {
Value value; ///< Stored value
std::list<Node> nodes; ///< List of child nodes
};
using ValueVisitor = std::function<void(const Value& value, std::size_t level)>;
using NodeVisitor = std::function<void(const Node& node, std::size_t level)>;
Node org; ///< Root node of the tree
/// @brief Constructs a tree
/// @param Value held by the root node of the tree
/// @note This constructor avoids that the value type needs to be default-constructible
Tree(const Value& value) : org{value} {}
/// @brief Visit all values in the nodes in preorder using two visitors for down and up path
static void preorder(Node& root, const ValueVisitor& down, const ValueVisitor& up, std::size_t level = 0) {
down(root.value, level);
for (auto& child : root.nodes)
preorder(child, down, up, level + 1);
up(root.value, level);
}
/// @brief Visit all nodes in preorder using two visitors for down and up path
static void preorder(Node& root, const NodeVisitor& down, const NodeVisitor& up, std::size_t level = 0) {
down(root, level);
for (auto& child : root.nodes)
preorder(child, down, up, level + 1);
up(root, level);
}
};
} // namespace iue::svg
+60
View File
@@ -0,0 +1,60 @@
/// @file
/// @brief Test for iue::svg::Tree
#include "iue-svg/tree.hpp" // iue::svg::Tree
#include <cassert> // assert
#include <cstddef> // std::size_t
#include <cstdlib> // EXIT_SUCCESS
#include <iostream> // std::cout, std::endl
#include <sstream> // std::stringstream
#include <string> // std::string
struct Value {
int a = 1;
};
int main() {
using Tree = iue::svg::Tree<Value>;
using Node = Tree::Node;
Tree tree = {Value{0}};
auto& l0 = tree.org;
l0.nodes.push_back(Node{Value{1}});
l0.nodes.push_back(Node{Value{1}});
l0.nodes.push_back(Node{Value{1}});
for (auto& l1 : l0.nodes) {
l1.nodes.push_back(Node{Value{2}});
l1.nodes.push_back(Node{Value{2}});
l1.nodes.push_back(Node{Value{2}});
for (auto& l1 : l1.nodes) {
l1.nodes.push_back(Node{Value{3}});
l1.nodes.push_back(Node{Value{3}});
}
}
auto noop = [](const Value& value, std::size_t level) -> void {};
{ // print tree
std::stringstream stream;
auto print = [&stream](const Value& value, std::size_t level) -> void {
stream << std::string(2 * level, ' ') << value.a << std::endl;
};
tree.preorder(l0, print, noop);
std::cout << stream.str() << std::endl;
}
{ // sum values in tree
int sum = 0;
auto count = [&sum](const Value& value, std::size_t level) -> void { sum += value.a; };
tree.preorder(l0, count, noop);
assert(sum == 1 * 0 + (1 * 3 * 1) + (1 * 3 * 3 * 2) + (1 * 3 * 3 * 2 * 3));
}
return EXIT_SUCCESS;
}
+47
View File
@@ -0,0 +1,47 @@
/// @file
/// @brief task A: Class Vector
#pragma once
#include <functional> // std::function
#include <iostream> // std::cout, std::endl
#include <vector> // std::vector
/// @todo implement a class 'Vector' according to the following specification:
/// - use a *private* member variable of type std::vector<double>
/// - provide 3 constructors:
/// 1. passing a std::vector<double>
/// - the values will be copied over to the private member
/// 2. passing a count 'N' and an interval specified by a 'start', and 'end' value
/// - fill the private member with 'N' equidistant samples from the interval
/// 3. passing a count 'N' and a generator function in form of a callable std::function<double()>
/// - fill the private member with 'N' samples obtained from the generator/callable
/// - member function print():
/// - prints all elements in the private member to the console
class Vector {
std::vector<double> data_;
public:
Vector(std::vector<double> data) : data_(data) {}
Vector(double start, double end, unsigned int N) {
double step = (end - start) / (N - 1);
for (unsigned int i = 0; i < N; ++i) {
data_.push_back(start + i * step);
}
}
Vector(unsigned int N, std::function<double()> generator) {
for (unsigned int i = 0; i < N; ++i) {
data_.push_back(generator());
}
}
void print() {
std::cout << "[";
for (unsigned int i = 0; i < data_.size(); ++i) {
std::cout << data_[i];
if (i < data_.size() - 1) {
std::cout << ", ";
}
}
std::cout << "]" << std::endl;
}
};
+37
View File
@@ -0,0 +1,37 @@
/// @file
/// @brief task C: transform class Vector from task A to a class template
#pragma once
#include <vector>
#include <functional>
#include <iostream>
/// @todo copy Vector from taskA.Vector.hpp and adapt according to task C
template <typename T>
class Vector {
std::vector<T> data_;
public:
Vector(std::vector<T> data) : data_(data) {}
Vector(T start, T end, unsigned int N) {
T step = (end - start) / (N - 1);
for (unsigned int i = 0; i < N; ++i) {
data_.push_back(start + i * step);
}
}
Vector(unsigned int N, std::function<T()> generator) {
for (unsigned int i = 0; i < N; ++i) {
data_.push_back(generator());
}
}
void print() {
std::cout << "[";
for (unsigned int i = 0; i < data_.size(); ++i) {
std::cout << data_[i];
if (i < data_.size() - 1) {
std::cout << ", ";
}
}
std::cout << "]" << std::endl;
}
};
+34
View File
@@ -0,0 +1,34 @@
/// @file
/// @brief task A
/// g++ -std=c++20 taskA.cpp -Imodules -o taskA && ./taskA
#include <functional>
#include <iostream>
#include <vector>
#include "iue-rnd/random.hpp" // iue::rnd::UniformValue
#include "taskA.Vector.hpp" // todo: implement Vector
int main() {
// first constructor
std::vector<double> data = {2, 4, 6, 8};
// Vector v1(data); // @todo: uncomment
// second constructor
double start = 0;
double end = 5;
unsigned int N = 6;
// Vector v2(start, end, N); // @todo: uncomment
// third constructor
auto callable = iue::rnd::UniformValue(0.0, 1.0);
// Vector v3(N, callable); // @todo: uncomment
// v1.print(); // @todo: uncomment [2, 4, 6, 8]
// v2.print(); // @todo: uncomment [0, 1, 2, 3, 4, 5]
// v3.print(); // @todo: uncomment [6 random values between 0..1]
return 0;
}
+26
View File
@@ -0,0 +1,26 @@
/// @file
/// @brief task B: Car class
#pragma once
#include "taskB.Device.hpp" // Device
/// @todo Implement a class `Car` that publicly derives from 'Device'
class Car : public Device {
private:
std::string name_;
std::string model_;
double price_;
double power_;
public:
Car(std::string name, std::string model, double price, double power)
: name_(name), model_(model), price_(price), power_(power) {}
void print_info() const final override {
std::cout << name_ << ": " << model_ << std::endl;
std::cout << " power: " << power_ << " Kilowatts" << std::endl;
std::cout << " price: " << price_ << " EUR" << std::endl;
}
};
+13
View File
@@ -0,0 +1,13 @@
/// @file
/// @brief task B: Abstract interface class
#pragma once
#include <iostream>
#include <string>
/// @brief Abstract interface class to a generic "device"
struct Device {
virtual void print_info() const = 0;
virtual ~Device() {}
};
+25
View File
@@ -0,0 +1,25 @@
/// @file
/// @brief task B: Train class
#pragma once
#include "taskB.Device.hpp"
/// @todo Example class `Train` that publicly derives from 'Device'
struct Train : Device {
private:
std::string name_;
std::string description_;
size_t capacity_;
double length_;
public:
Train(std::string name, std::string description, size_t capacity, double length)
: name_(name), description_(description), capacity_(capacity), length_(length) {}
void print_info() const final override {
std::cout << name_ << ": " << description_ << std::endl;
std::cout << " length: " << length_ << " Meters" << std::endl;
std::cout << " capacity: " << capacity_ << " Passangers" << std::endl;
}
};
+25
View File
@@ -0,0 +1,25 @@
/// @file
/// @brief task B: WashingMachine class
#pragma once
#include "taskB.Device.hpp"
/// @todo Example class `WashingMachine` that publicly derives from 'Device'
struct WashingMachine : Device {
private:
std::string name_;
std::string description_;
double price_;
double power_;
public:
WashingMachine(std::string name, std::string description, double price, double power)
: name_(name), description_(description), price_(price), power_(power) {}
void print_info() const final override {
std::cout << name_ << ": " << description_ << std::endl;
std::cout << " power: " << power_ << " Kilowatts" << std::endl;
std::cout << " price: " << price_ << " EUR" << std::endl;
}
};
+35
View File
@@ -0,0 +1,35 @@
/// @file
/// @brief task B
/// g++ -g -fsanitize=address -std=c++20 taskB.cpp -o taskB && ./taskB
#include <vector>
#include "taskB.Device.hpp" // Device
#include "taskB.Train.hpp" // Train : Device
#include "taskB.WashingMachine.hpp" // WashingMachine : Device
#include "taskB.Car.hpp" // todo: implement Car : Device
int main() {
std::vector<Device*> devices; ///< contains pointers to dynamically allocated objects
devices.push_back(new WashingMachine("Miele W1", "will last forever (if you maintain it well)", 1500, 1200));
devices.push_back(new WashingMachine("Constructa XZY", "will also last forever (if you maintain it well)", 500, 800));
devices.push_back(new Train("ICE 4", "solid and fast train", 820, 350));
devices.push_back(new Train("Railjet I", "one of the world fastest trains with a discrete locomotive", 400, 200));
/// @todo: create dynamically allocated instances of your Car/Train classes and push them to 'devices'
/// loop over the vector and making use of the interface function 'print_info'
for (const auto& d : devices) {
d->print_info();
}
/// @todo: delete all dynamically allocated objects stored in 'devices' using 'delete' (use a loop)
return 0;
}
+27
View File
@@ -0,0 +1,27 @@
/// @file
/// @brief task C
/// g++ -g -std=c++20 taskC.cpp -o taskC && ./taskC
#include "taskC.one.hpp" // regular functions: one::add, one::sum
#include "taskC.two.hpp" // todo: implement function templates two::add, two::sum
int main() {
using namespace one; // todo: switch to namespace 'two' after implementing the function templates, details see taskC.two.hpp
int x = 5;
int y = 6;
double u = 1.2;
double v = 3.3;
int z = add(x, y);
double w = add(u, v);
std::vector<int> xV = {1, 2, 3};
std::vector<double> yV = {5.1, 4.9, 10.0};
int sumI = sum(xV);
double sumD = sum(yV);
return 0;
}
+31
View File
@@ -0,0 +1,31 @@
/// @file
/// @brief task C: regular functions with overloads (no templates)
#pragma once
#include <vector>
namespace one {
inline int add(const int& a, const int& b) {
return a + b;
}
inline double add(const double& a, const double& b) {
return a + b;
}
inline int sum(const std::vector<int>& vec) {
int res = 0;
for (int x : vec) {
res += x;
}
return res;
}
inline double sum(const std::vector<double>& vec) {
double res = 0;
for (double x : vec) {
res += x;
}
return res;
}
} // namespace one
+27
View File
@@ -0,0 +1,27 @@
/// @file
/// @brief task C: function templates
#pragma once
#include <vector>
namespace two {
/// @todo: implement ONE function template covering the function overloads 'one::add'
/// @todo: implement ONE function template covering the function overloads 'one::sum'
template <typename T>
T add(const T& a, const T& b) {
return a + b;
}
template <typename T>
T sum(const std::vector<T>& vec) {
T res = 0;
for (T x : vec) {
res += x;
}
return res;
} // namespace two