Skip to content

Commit

Permalink
Merge branch 'master' of github.com:motis-project/utl
Browse files Browse the repository at this point in the history
  • Loading branch information
felixguendling committed Mar 1, 2025
2 parents 1d9cc46 + 659d088 commit afa4b17
Show file tree
Hide file tree
Showing 14 changed files with 341 additions and 69 deletions.
36 changes: 34 additions & 2 deletions .github/workflows/unix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
formatting:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4

- name: Install clang-format
env:
Expand Down Expand Up @@ -82,7 +82,7 @@ jobs:
DEBIAN_FRONTEND: noninteractive
UBSAN_OPTIONS: halt_on_error=1:abort_on_error=1
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4

- uses: seanmiddleditch/gha-setup-ninja@master
if: matrix.config.os == 'macos-latest'
Expand Down Expand Up @@ -135,3 +135,35 @@ jobs:
- name: Run Tests Valgrind
if: matrix.config.mode == 'Debug' && matrix.config.cc == 'gcc-12'
run: valgrind --error-exitcode=1 --show-reachable=yes --leak-check=full ./build/utl-test

doc:
runs-on: ubuntu-latest
steps:
- name: Checkout 🛎️
uses: actions/checkout@v4

- name: Set up Python 3.13 🔧
uses: actions/setup-python@v5
with:
python-version: 3.13

- name: Install doxygen ⬇️
uses: ssciwr/doxygen-install@v1
with:
version: "1.13.2"

- name: Install ninja ⚙️ ⬇️
uses: seanmiddleditch/gha-setup-ninja@master

- name: Invoke CMake to install deps ⚙️
run: cmake -G Ninja -S . -B build

- name: Generate HTML documentation 🏗️
run: deps/docs/build_docs.sh

- name: Deploy documentation onto GitHub Pages 🚀
if: github.ref == 'refs/heads/master'
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: public/
9 changes: 8 additions & 1 deletion .github/workflows/windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,15 @@ jobs:
cmake-opt: -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4

- uses: seanmiddleditch/gha-setup-ninja@master

- name: Install doxygen 🔧
uses: ssciwr/doxygen-install@v1
with:
version: "1.13.2"

- uses: ilammy/msvc-dev-cmd@v1

- name: Build
Expand Down
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,11 @@
*.sublime-*
/.idea
/deps
/docs/conf.py
/docs/Doxyfile
/docs/html
/docs/xml
/public
/.vscode
_build/
.clang-tidy
6 changes: 5 additions & 1 deletion .pkg
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
[docs]
url=git@github.com:motis-project/docs.git
branch=main
commit=75dc89a53e9c2d78574fc0ffda698e69f1682ed2
[googletest]
url=git@github.com:motis-project/googletest.git
branch=master
commit=34a46558609e05865c197f0260ab36daa7cbbb6e
[fmt]
url=git@github.com:motis-project/fmt.git
branch=master
commit=edb385ac526c24bc917ec4a41bb0edb28f0ca59e
commit=dc10f83be70ac2873d5f8d1ce317596f1fd318a2
[cista]
url=git@github.com:felixguendling/cista.git
branch=master
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
![Linux Build](https://github.com/motis-project/utl/workflows/Linux%20Build/badge.svg)
![Unix Build](https://github.com/motis-project/utl/workflows/Unix%20Build/badge.svg)
![Windows Build](https://github.com/motis-project/utl/workflows/Windows%20Build/badge.svg)


This repository contains universally useful C++ utilities.

Documentation: https://motis-project.github.io/utl/
72 changes: 72 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# MOTIS utl module documentation
MOTIS is an open-source software platform for efficient planning and routing in multi-modal transportation systems.
GitHub main repository: https://github.com/motis-project/motis

This is the documentation for the **utl** (utility) module.

:::{toctree}
:maxdepth: 2
:caption: Contents:
:::

## Logging
The simplest way to produce log lines is to use the `utl:log()` function,
or the wrapping functions for the various log levels,
`utl::log_debug()`, `utl::log_info()` and `utl::log_error()`:
```c++
#include "utl/logging.h"

utl::log_info("MyCtx", "Simple message");
```
The first parameter is the **context**, that provides an information of the origin of the log line inside MOTIS code.
The following log levels are supported:
debug
: Messages that contain information only useful when debugging MOTIS
info
: Important information about a normal behavior of the program
error
: Details on an abnormal behavior of the application
### Advanced usage
You can insert variables in the message by using `{}` and passing them as extra arguments
(formatting is performed by the [fmt](https://fmt.dev>) library):
```c++
utl::log_info("MyCtx", "String={} Int={}", "Hello", 42);
```

You can specify **metadata** using `.attrs()`:
```c++
utl::log_info("MyCtx", "Message").attrs({{"key1", "value1"}, {"key2", "value2"}});
```
### API details
:::{doxygenstruct} utl::log
:no-link:
:members:
:::
:::{doxygenstruct} utl::log_debug
:no-link:
:members:
:::
:::{doxygenstruct} utl::log_info
:no-link:
:members:
:::
:::{doxygenstruct} utl::log_error
:no-link:
:members:
:::
:::{note}
Those logging function are an exception to the rule that, in MOTIS,
we use [Aggregate Initialization](https://en.cppreference.com/w/cpp/language/aggregate_initialization) wherever possible,
but here we do not want to use `utl::log_info{...}`.
:::
149 changes: 101 additions & 48 deletions include/utl/logging.h
Original file line number Diff line number Diff line change
@@ -1,70 +1,123 @@
#pragma once

#ifdef LOGGING_HEADER
#include LOGGING_HEADER
#else

#include <chrono>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <mutex>
#include <source_location>
#include <sstream>
#include <string>

#ifdef _MSC_VER
#define gmt(a, b) gmtime_s(b, a)
#else
#define gmt(a, b) gmtime_r(a, b)
#endif
#include "fmt/core.h"
#include "fmt/ostream.h"

#define FILE_NAME \
(strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
namespace utl {

#define uLOG(lvl) \
utl::log() << "[" << utl::log::str[lvl] << "]" \
<< "[" << utl::time() << "]" \
<< "[" << FILE_NAME << ":" << __LINE__ << "]" \
<< " "
/// Log level
enum class log_level { debug, info, error };

namespace utl {
constexpr char const* to_str(log_level const level) {
switch (level) {
case log_level::debug: return "debug";
case log_level::info: return "info";
case log_level::error: return "error";
}
return "";
}

struct log {
log() = default;
extern log_level log_verbosity;

log(log const&) = delete;
log& operator=(log const&) = delete;
inline std::string now() {
using clock = std::chrono::system_clock;
auto const now = clock::to_time_t(clock::now());
struct tm tmp {};
#if _MSC_VER >= 1400
gmtime_s(&tmp, &now);
#else
gmtime_r(&now, &tmp);
#endif

log(log&&) = default;
log& operator=(log&&) = default;
std::stringstream ss;
ss << std::put_time(&tmp, "%FT%TZ");
return ss.str();
}

template <typename T>
friend log&& operator<<(log&& l, T&& t) {
std::clog << std::forward<T&&>(t);
return std::move(l);
/// Produce a new log line at the given `level`.
template <log_level LogLevel, typename... Args>
struct log {
log(const char* ctx, fmt::format_string<Args...> fmt_str, Args&&... args,
std::source_location const& loc = std::source_location::current())
: loc_{loc},
ctx_{ctx},
msg_{fmt::format(fmt_str, std::forward<Args>(args)...)} {}

~log() {
if (LogLevel >= log_verbosity) {
#if defined(_WIN32)
auto const base_file_name = strrchr(loc_.file_name(), '\\')
? strrchr(loc_.file_name(), '\\') + 1
: loc_.file_name();
#else
// On MacOS, due to a bug with Clang 15, the wrong filename
// is retrieved (logging.h instead of the calling file):
// https://github.com/llvm/llvm-project/issues/56379
auto const base_file_name = strrchr(loc_.file_name(), '/')
? strrchr(loc_.file_name(), '/') + 1
: loc_.file_name();
#endif
fmt::print(std::clog, "{time} [{level}] [{file}:{line}] [{ctx}] {msg}\n",
fmt::arg("time", now()), fmt::arg("level", to_str(LogLevel)),
fmt::arg("file", base_file_name),
fmt::arg("line", loc_.line()), fmt::arg("ctx", ctx_),
fmt::arg("msg", msg_));
}
}

~log() { std::clog << std::endl; }
/// Add key-values metadata
void attrs(
std::initializer_list<std::pair<std::string_view, std::string_view> >&&
attrs) {
attrs_ = std::move(attrs);
}

static constexpr const char* const str[]{"emrg", "alrt", "crit", "erro",
"warn", "note", "info", "debg"};
log_level level_;
std::source_location loc_;
char const* ctx_;
std::string msg_;
std::initializer_list<std::pair<std::string_view, std::string_view> > attrs_;
};

enum log_level { emrg, alrt, crit, err, warn, notice, info, debug };
/// Produce a new DEBUG log line
template <typename... Args>
struct log_debug : public log<log_level::debug, Args...> {
using log<log_level::debug, Args...>::log;
};

inline std::string time(time_t const t) {
char buf[sizeof "2011-10-08t07:07:09z-0430"];
struct tm result {};
gmt(&t, &result);
strftime(buf, sizeof buf, "%FT%TZ%z", &result);
return buf;
}
/// Produce a new INFO log line
template <typename... Args>
struct log_info : public log<log_level::info, Args...> {
using log<log_level::info, Args...>::log;
};

inline std::string time() {
time_t now;
std::time(&now);
return time(now);
}
/// Produce a new ERROR log line
template <typename... Args>
struct log_error : public log<log_level::error, Args...> {
using log<log_level::error, Args...>::log;
};

} // namespace utl
// Template deduction guides, to help the compiler distinguish between
// the variadic template Args... and the next argument std::source_location
// which has a default value:

#endif
template <typename... Args>
log_debug(const char* ctx, fmt::format_string<Args...>,
Args&&... args) -> log_debug<Args...>;

template <typename... Args>
log_info(const char* ctx, fmt::format_string<Args...>,
Args&&... args) -> log_info<Args...>;

template <typename... Args>
log_error(const char* ctx, fmt::format_string<Args...>,
Args&&... args) -> log_error<Args...>;

} // namespace utl
2 changes: 1 addition & 1 deletion include/utl/parallel_for.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ inline errors_t parallel_for(
jobs.size(),
[&](auto const idx) {
if (idx % mod == 0) {
uLOG(info) << desc << " " << idx << "/" << jobs.size();
utl::log_info("parallel_for", "{} {}/{}", desc, idx, jobs.size());
}
func(jobs[idx]);
},
Expand Down
10 changes: 6 additions & 4 deletions include/utl/sorted_diff.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ constexpr std::string_view to_str(op const o) {
std::unreachable();
}

template <typename It, typename Lt, typename Eq, typename Fn>
void sorted_diff(It a, It const a_end, It b, It const b_end, Lt&& cmp,
template <typename It1, typename It1End, typename It2, typename It2End,
typename Lt, typename Eq, typename Fn>
void sorted_diff(It1 a, It1End const a_end, It2 b, It2End const b_end, Lt&& cmp,
Eq&& deep_eq, Fn&& fn) {
while (a != a_end || b != b_end) {
if (a == a_end) {
Expand All @@ -41,8 +42,9 @@ void sorted_diff(It a, It const a_end, It b, It const b_end, Lt&& cmp,
}
}

template <typename Collection, typename Lt, typename Eq, typename Fn>
void sorted_diff(Collection const& a, Collection const& b, Lt&& cmp,
template <typename Collection1, typename Collection2, typename Lt, typename Eq,
typename Fn>
void sorted_diff(Collection1 const& a, Collection2 const& b, Lt&& cmp,
Eq&& deep_eq, Fn&& fn) {
sorted_diff(begin(a), end(a), begin(b), end(b), std::forward<Lt>(cmp),
std::forward<Eq>(deep_eq), std::forward<Fn>(fn));
Expand Down
Loading

0 comments on commit afa4b17

Please sign in to comment.