init
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
---
|
||||
BasedOnStyle: LLVM
|
||||
---
|
||||
Language: Cpp
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
DerivePointerAlignment: false
|
||||
PointerAlignment: Left
|
||||
ColumnLimit: 120
|
||||
TabWidth: 4
|
||||
IndentWidth: 2
|
||||
...
|
||||
@@ -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]
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 "./"
|
||||
)
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
```
|
||||
@@ -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)
|
||||
@@ -0,0 +1 @@
|
||||
-Imodules
|
||||
@@ -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 )
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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)
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
namespace iue::other::detail {
|
||||
|
||||
inline int calc(int a) { return a * 2; }
|
||||
|
||||
} // namespace iue::other::detail
|
||||
@@ -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
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
#include "iue-other/func.hpp"
|
||||
|
||||
#include <cassert> // assert
|
||||
|
||||
int main() {
|
||||
assert(iue::other::header_only(5) == 10);
|
||||
return 0;
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
namespace iue::other {
|
||||
|
||||
struct Widget {
|
||||
int calc() const;
|
||||
};
|
||||
|
||||
int calc(const Widget& w);
|
||||
|
||||
} // namespace iue::other
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user