From 42deef206d20aea82250d077ea5951f1a31e3a57 Mon Sep 17 00:00:00 2001 From: Jakob Gamper <97gamjak@gmail.com> Date: Fri, 14 Aug 2026 22:06:11 +0200 Subject: [PATCH 1/7] add strong type definition template for convenience in multiple libs --- CHANGELOG.md | 4 + include/mstd/enum.hpp | 25 +- include/mstd/types.hpp | 6 + include/mstd/types/strong_type.hpp | 158 +++++++++++ include/mstd/types/strong_type.tpp | 439 +++++++++++++++++++++++++++++ 5 files changed, 629 insertions(+), 3 deletions(-) create mode 100644 include/mstd/types.hpp create mode 100644 include/mstd/types/strong_type.hpp create mode 100644 include/mstd/types/strong_type.tpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 7623e28..7a3d806 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. ## Next Release +### Types + +- add convenient template for creating strong types easily + ## [0.1.5](https://github.com/repo/owner/releases/tag/0.1.5) - 2026-08-13 diff --git a/include/mstd/enum.hpp b/include/mstd/enum.hpp index a0b71ee..f88a059 100644 --- a/include/mstd/enum.hpp +++ b/include/mstd/enum.hpp @@ -139,11 +139,30 @@ return lhs; \ } \ \ - inline EnumName operator&(EnumName lhs, EnumName rhs) \ + struct EnumName##FlagTest \ { \ - return static_cast( \ + Underlying value; \ + constexpr operator EnumName() const noexcept \ + { \ + return static_cast(value); \ + } \ + \ + constexpr explicit operator bool() const noexcept \ + { \ + return value != Underlying{0}; \ + } \ + }; \ + \ + inline EnumName##FlagTest operator&(EnumName lhs, EnumName rhs) \ + { \ + return EnumName##FlagTest{static_cast( \ static_cast(lhs) & static_cast(rhs) \ - ); \ + )}; \ + } \ + \ + inline bool operator!(EnumName lhs) \ + { \ + return !static_cast(lhs); \ } #endif // __MSTD__ENUM_HPP__ diff --git a/include/mstd/types.hpp b/include/mstd/types.hpp new file mode 100644 index 0000000..3743310 --- /dev/null +++ b/include/mstd/types.hpp @@ -0,0 +1,6 @@ +#ifndef __MSTD__TYPES_HPP__ +#define __MSTD__TYPES_HPP__ + +#include "types/strong_type.hpp" // IWYU pragma: export + +#endif // __MSTD__TYPES_HPP__ diff --git a/include/mstd/types/strong_type.hpp b/include/mstd/types/strong_type.hpp new file mode 100644 index 0000000..588ea87 --- /dev/null +++ b/include/mstd/types/strong_type.hpp @@ -0,0 +1,158 @@ +#ifndef __MSTD__TYPES__STRONG_TYPE_HPP__ +#define __MSTD__TYPES__STRONG_TYPE_HPP__ + +#include +#include +#include +#include + +namespace mstd +{ + +#define STRONG_TYPE_TRAIT_LIST(X) \ + X(NONE, 0) \ + X(ORDERED, 1u << 0) \ + X(ARITHMETIC, 1u << 1) \ + X(INCREMENT, 1u << 2) \ + X(HASHABLE, 1u << 3) \ + X(STREAMABLE, 1u << 4) \ + X(BOOLEAN, 1u << 5) + + MSTD_ENUM_BITFLAG(StrongTypeTrait, unsigned, STRONG_TYPE_TRAIT_LIST); + + /** + * @brief A generalized strong type wrapper. + * + * @tparam T underlying value type + * @tparam Tag a unique tag type used only for disambiguation, e.g. `struct + * EnergyTag;` + * @tparam Traits bitwise-OR of StrongTypeTrait flags enabling specific + * operations + * + * Usage: + * struct EnergyTag{}; + * using Energy = StrongType; + */ + template < + typename T, + typename Tag, + StrongTypeTrait Traits = StrongTypeTrait::NONE> + class StrongType + { + public: + using ValueType = T; + using TagType = Tag; + static constexpr auto traitFlags = Traits; + + constexpr StrongType() = default; + + constexpr explicit StrongType( + const T &value + ) noexcept(std::is_nothrow_copy_constructible_v); + + constexpr explicit StrongType( + T &&value + ) noexcept(std::is_nothrow_move_constructible_v); + + [[nodiscard]] constexpr const T &get() const noexcept; + [[nodiscard]] constexpr T &get() noexcept; + + [[nodiscard]] constexpr explicit operator const T &() const noexcept; + + // + // boolean and ordering + // + + [[nodiscard]] constexpr explicit operator bool() const noexcept + requires( + (Traits & StrongTypeTrait::BOOLEAN) && std::convertible_to + ); + + [[nodiscard]] + constexpr auto operator<=>(const StrongType &) const + requires(Traits &StrongTypeTrait::ORDERED && + std::three_way_comparable) + = default; + + [[nodiscard]] + constexpr bool operator==(const StrongType &) const + requires(Traits &StrongTypeTrait::ORDERED && + std::equality_comparable) + = default; + + // + // Arithmetic + // + + constexpr StrongType &operator+=(const StrongType &other) + requires(Traits &StrongTypeTrait::ARITHMETIC && true); + + constexpr StrongType &operator-=(const StrongType &other) + requires(Traits &StrongTypeTrait::ARITHMETIC && true); + + constexpr StrongType &operator*=(const T &scalar) + requires(Traits &StrongTypeTrait::ARITHMETIC && true); + + constexpr StrongType &operator/=(const T &scalar) + requires(Traits &StrongTypeTrait::ARITHMETIC && true); + + [[nodiscard]] + constexpr StrongType operator+(const StrongType &other) const + requires(Traits &StrongTypeTrait::ARITHMETIC && true); + + [[nodiscard]] + constexpr StrongType operator-(const StrongType &other) const + requires(Traits &StrongTypeTrait::ARITHMETIC && true); + + [[nodiscard]] + constexpr StrongType operator*(const T &scalar) const + requires(Traits &StrongTypeTrait::ARITHMETIC && true); + + [[nodiscard]] + constexpr StrongType operator/(const T &scalar) const + requires(Traits &StrongTypeTrait::ARITHMETIC && true); + + [[nodiscard]] + constexpr StrongType operator-() const + requires(Traits &StrongTypeTrait::ARITHMETIC && true); + + // + // Increment / decrement + // + + constexpr StrongType &operator++() + requires(Traits &StrongTypeTrait::INCREMENT && true); + + constexpr StrongType operator++(int) + requires(Traits &StrongTypeTrait::INCREMENT && true); + + constexpr StrongType &operator--() + requires(Traits &StrongTypeTrait::INCREMENT && true); + + constexpr StrongType operator--(int) + requires(Traits &StrongTypeTrait::INCREMENT && true); + + private: + T _value{}; + }; + +} // namespace mstd + +#ifndef __MSTD__TYPES__STRONG_TYPE_TPP__ +#include "strong_type.tpp" // IWYU pragma: export +#endif // __MSTD__TYPES__STRONG_TYPE_TPP__ + +// Streaming - free function, only valid to call if STREAMABLE trait set +template +std::ostream &operator<<( + std::ostream &os, + const mstd::StrongType &strongType +) +requires(Traits &mstd::StrongTypeTrait::STREAMABLE && true) +{ + return os << strongType.get(); +} + +#endif // __MSTD__TYPES__STRONG_TYPE_HPP__ diff --git a/include/mstd/types/strong_type.tpp b/include/mstd/types/strong_type.tpp new file mode 100644 index 0000000..1190cf8 --- /dev/null +++ b/include/mstd/types/strong_type.tpp @@ -0,0 +1,439 @@ +#ifndef __MSTD__TYPES__STRONG_TYPE_TPP__ +#define __MSTD__TYPES__STRONG_TYPE_TPP__ + +#include "strong_type.hpp" + +namespace mstd +{ + /** + * @brief StrongType constructor from const reference. + * + * @tparam T underlying value type + * @tparam Tag a unique tag type used only for disambiguation, e.g. `struct + * EnergyTag;` + * @tparam Traits bitwise-OR of StrongTypeTrait flags enabling specific + * operations + * + * @param value the underlying value to wrap + */ + template + constexpr StrongType::StrongType( + const T &value + ) noexcept(std::is_nothrow_copy_constructible_v) + : _value(value) + { + } + + /** + * @brief StrongType constructor from rvalue reference. + * + * @tparam T underlying value type + * @tparam Tag a unique tag type used only for disambiguation, e.g. `struct + * EnergyTag;` + * @tparam Traits bitwise-OR of StrongTypeTrait flags enabling specific + * operations + * + * @param value the underlying value to wrap + */ + template + constexpr StrongType::StrongType( + T &&value + ) noexcept(std::is_nothrow_move_constructible_v) + : _value(std::move(value)) + { + } + + /** + * @brief Get the underlying value. + * + * @tparam T underlying value type + * @tparam Tag a unique tag type used only for disambiguation, e.g. `struct + * EnergyTag;` + * @tparam Traits bitwise-OR of StrongTypeTrait flags enabling specific + * operations + * + * @return const T& reference to the underlying value + */ + template + constexpr const T &StrongType::get() const noexcept + { + return _value; + } + + /** + * @brief Get the underlying value. + * + * @tparam T underlying value type + * @tparam Tag a unique tag type used only for disambiguation, e.g. `struct + * EnergyTag;` + * @tparam Traits bitwise-OR of StrongTypeTrait flags enabling specific + * operations + * + * @return T& reference to the underlying value + */ + template + constexpr T &StrongType::get() noexcept + { + return _value; + } + + /** + * @brief Explicit conversion operator to const T&. + * + * This operator allows explicit conversion from StrongType to the + * underlying type T. It is only enabled if the + * STRONG_TYPE_TRAIT::EXPLICIT_CONVERSION trait is set. + * + * @tparam T underlying value type + * @tparam Tag a unique tag type used only for disambiguation, e.g. `struct + * EnergyTag;` + * @tparam Traits bitwise-OR of StrongTypeTrait flags enabling specific + * operations + * + * @return const T& reference to the underlying value + */ + template + constexpr StrongType::operator const T &() const noexcept + { + return _value; + } + + /** + * @brief Explicit conversion operator to bool. + * + * This operator allows explicit conversion from StrongType to bool. It is + * only enabled if the STRONG_TYPE_TRAIT::BOOLEAN trait is set and T is + * convertible to bool. + * + * @tparam T underlying value type + * @tparam Tag a unique tag type used only for disambiguation, e.g. `struct + * EnergyTag;` + * @tparam Traits bitwise-OR of StrongTypeTrait flags enabling specific + * operations + * + * @return true if the underlying value is truthy, false otherwise + */ + template + constexpr StrongType::operator bool() const noexcept + requires( + (Traits & StrongTypeTrait::BOOLEAN) && std::convertible_to + ) + { + return static_cast(_value); + } + + /** + * @brief Compound assignment operator for addition. + * + * This operator allows adding another StrongType of the same type to this + * StrongType. It is only enabled if the STRONG_TYPE_TRAIT::ARITHMETIC trait + * is set. + * + * @tparam T underlying value type + * @tparam Tag a unique tag type used only for disambiguation, e.g. `struct + * EnergyTag;` + * @tparam Traits bitwise-OR of StrongTypeTrait flags enabling specific + * operations + * + * @param other the other StrongType to add + * @return reference to this StrongType after addition + */ + template + constexpr StrongType &StrongType:: + operator+=(const StrongType &other) + requires(Traits &StrongTypeTrait::ARITHMETIC && true) + { + _value += other._value; + return *this; + } + + /** + * @brief Compound assignment operator for subtraction. + * + * This operator allows subtracting another StrongType of the same type from + * this StrongType. It is only enabled if the STRONG_TYPE_TRAIT::ARITHMETIC + * trait is set. + * + * @tparam T underlying value type + * @tparam Tag a unique tag type used only for disambiguation, e.g. `struct + * EnergyTag;` + * @tparam Traits bitwise-OR of StrongTypeTrait flags enabling specific + * operations + * + * @param other the other StrongType to subtract + * @return reference to this StrongType after subtraction + */ + template + constexpr StrongType &StrongType:: + operator-=(const StrongType &other) + requires(Traits &StrongTypeTrait::ARITHMETIC && true) + { + _value -= other._value; + return *this; + } + + /** + * @brief Compound assignment operator for multiplication by a scalar. + * + * This operator allows multiplying this StrongType by a scalar of type T. + * It is only enabled if the STRONG_TYPE_TRAIT::ARITHMETIC trait is set. + * + * @tparam T underlying value type + * @tparam Tag a unique tag type used only for disambiguation, e.g. `struct + * EnergyTag;` + * @tparam Traits bitwise-OR of StrongTypeTrait flags enabling specific + * operations + * + * @param scalar the scalar value to multiply by + * @return reference to this StrongType after multiplication + */ + template + constexpr StrongType &StrongType:: + operator*=(const T &scalar) + requires(Traits &StrongTypeTrait::ARITHMETIC && true) + { + _value *= scalar; + return *this; + } + + /** + * @brief Compound assignment operator for division by a scalar. + * + * This operator allows dividing this StrongType by a scalar of type T. It + * is only enabled if the STRONG_TYPE_TRAIT::ARITHMETIC trait is set. + * + * @tparam T underlying value type + * @tparam Tag a unique tag type used only for disambiguation, e.g. `struct + * EnergyTag;` + * @tparam Traits bitwise-OR of StrongTypeTrait flags enabling specific + * operations + * + * @param scalar the scalar value to divide by + * @return reference to this StrongType after division + */ + template + constexpr StrongType &StrongType:: + operator/=(const T &scalar) + requires(Traits &StrongTypeTrait::ARITHMETIC && true) + { + _value /= scalar; + return *this; + } + + /** + * @brief Addition operator for StrongType. + * + * This operator allows adding two StrongType objects of the same type. It + * is only enabled if the STRONG_TYPE_TRAIT::ARITHMETIC trait is set. + * + * @tparam T underlying value type + * @tparam Tag a unique tag type used only for disambiguation, e.g. `struct + * EnergyTag;` + * @tparam Traits bitwise-OR of StrongTypeTrait flags enabling specific + * operations + * + * @param other the other StrongType to add + * @return a new StrongType representing the sum + */ + template + constexpr StrongType StrongType::operator+( + const StrongType &other + ) const + requires(Traits &StrongTypeTrait::ARITHMETIC && true) + { + return StrongType{_value + other._value}; + } + + /** + * @brief Subtraction operator for StrongType. + * + * This operator allows subtracting one StrongType from another of the same + * type. It is only enabled if the STRONG_TYPE_TRAIT::ARITHMETIC trait is + * set. + * + * @tparam T underlying value type + * @tparam Tag a unique tag type used only for disambiguation, e.g. `struct + * EnergyTag;` + * @tparam Traits bitwise-OR of StrongTypeTrait flags enabling specific + * operations + * + * @param other the other StrongType to subtract + * @return a new StrongType representing the difference + */ + template + constexpr StrongType StrongType::operator-( + const StrongType &other + ) const + requires(Traits &StrongTypeTrait::ARITHMETIC && true) + { + return StrongType{_value - other._value}; + } + + /** + * @brief Multiplication operator for StrongType by a scalar. + * + * This operator allows multiplying a StrongType by a scalar of type T. It + * is only enabled if the STRONG_TYPE_TRAIT::ARITHMETIC trait is set. + * + * @tparam T underlying value type + * @tparam Tag a unique tag type used only for disambiguation, e.g. `struct + * EnergyTag;` + * @tparam Traits bitwise-OR of StrongTypeTrait flags enabling specific + * operations + * + * @param scalar the scalar value to multiply by + * @return a new StrongType representing the product + */ + template + constexpr StrongType StrongType::operator*( + const T &scalar + ) const + requires(Traits &StrongTypeTrait::ARITHMETIC && true) + { + return StrongType{_value * scalar}; + } + + /** + * @brief Division operator for StrongType by a scalar. + * + * This operator allows dividing a StrongType by a scalar of type T. It is + * only enabled if the STRONG_TYPE_TRAIT::ARITHMETIC trait is set. + * + * @tparam T underlying value type + * @tparam Tag a unique tag type used only for disambiguation, e.g. `struct + * EnergyTag;` + * @tparam Traits bitwise-OR of StrongTypeTrait flags enabling specific + * operations + * + * @param scalar the scalar value to divide by + * @return a new StrongType representing the quotient + */ + template + constexpr StrongType StrongType::operator/( + const T &scalar + ) const + requires(Traits &StrongTypeTrait::ARITHMETIC && true) + { + return StrongType{_value / scalar}; + } + + /** + * @brief Unary negation operator for StrongType. + * + * This operator allows negating a StrongType. It is only enabled if the + * STRONG_TYPE_TRAIT::ARITHMETIC trait is set. + * + * @tparam T underlying value type + * @tparam Tag a unique tag type used only for disambiguation, e.g. `struct + * EnergyTag;` + * @tparam Traits bitwise-OR of StrongTypeTrait flags enabling specific + * operations + * + * @return a new StrongType representing the negated value + */ + template + constexpr StrongType StrongType::operator-( + ) const + requires(Traits &StrongTypeTrait::ARITHMETIC && true) + { + return StrongType{-_value}; + } + + /** + * @brief Pre-increment operator for StrongType. + * + * This operator allows pre-incrementing a StrongType. It is only enabled if + * the STRONG_TYPE_TRAIT::INCREMENT trait is set. + * + * @tparam T underlying value type + * @tparam Tag a unique tag type used only for disambiguation, e.g. `struct + * EnergyTag;` + * @tparam Traits bitwise-OR of StrongTypeTrait flags enabling specific + * operations + * + * @return reference to this StrongType after incrementing + */ + template + constexpr StrongType &StrongType:: + operator++() + requires(Traits &StrongTypeTrait::INCREMENT && true) + { + ++_value; + return *this; + } + + /** + * @brief Post-increment operator for StrongType. + * + * This operator allows post-incrementing a StrongType. It is only enabled + * if the STRONG_TYPE_TRAIT::INCREMENT trait is set. + * + * @tparam T underlying value type + * @tparam Tag a unique tag type used only for disambiguation, e.g. `struct + * EnergyTag;` + * @tparam Traits bitwise-OR of StrongTypeTrait flags enabling specific + * operations + * + * @return a new StrongType representing the value before incrementing + */ + template + constexpr StrongType StrongType::operator++( + int + ) + requires(Traits &StrongTypeTrait::INCREMENT && true) + { + auto tmp = *this; + ++_value; + return tmp; + } + + /** + * @brief Pre-decrement operator for StrongType. + * + * This operator allows pre-decrementing a StrongType. It is only enabled if + * the STRONG_TYPE_TRAIT::INCREMENT trait is set. + * + * @tparam T underlying value type + * @tparam Tag a unique tag type used only for disambiguation, e.g. `struct + * EnergyTag;` + * @tparam Traits bitwise-OR of StrongTypeTrait flags enabling specific + * operations + * + * @return reference to this StrongType after decrementing + */ + template + constexpr StrongType &StrongType:: + operator--() + requires(Traits &StrongTypeTrait::INCREMENT && true) + { + --_value; + return *this; + } + + /** + * @brief Post-decrement operator for StrongType. + * + * This operator allows post-decrementing a StrongType. It is only enabled + * if the STRONG_TYPE_TRAIT::INCREMENT trait is set. + * + * @tparam T underlying value type + * @tparam Tag a unique tag type used only for disambiguation, e.g. `struct + * EnergyTag;` + * @tparam Traits bitwise-OR of StrongTypeTrait flags enabling specific + * operations + * + * @return a new StrongType representing the value before decrementing + */ + template + constexpr StrongType StrongType::operator--( + int + ) + requires(Traits &StrongTypeTrait::INCREMENT && true) + { + auto tmp = *this; + --_value; + return tmp; + } +} // namespace mstd + +#endif // __MSTD__TYPES__STRONG_TYPE_TPP__ From 4b78e5da8aa1e7a2b2ae56d1d8e690baa58349c2 Mon Sep 17 00:00:00 2001 From: Jakob Gamper <97gamjak@gmail.com> Date: Fri, 14 Aug 2026 22:15:55 +0200 Subject: [PATCH 2/7] try to fix cppcheck error with reformatting --- include/mstd/types/strong_type.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/mstd/types/strong_type.hpp b/include/mstd/types/strong_type.hpp index 588ea87..de8bff2 100644 --- a/include/mstd/types/strong_type.hpp +++ b/include/mstd/types/strong_type.hpp @@ -65,7 +65,8 @@ namespace mstd // boolean and ordering // - [[nodiscard]] constexpr explicit operator bool() const noexcept + [[nodiscard]] + constexpr explicit operator bool() const noexcept requires( (Traits & StrongTypeTrait::BOOLEAN) && std::convertible_to ); From 9588b3ea3caccb8a8e0415cfbe9761585255aed2 Mon Sep 17 00:00:00 2001 From: Jakob Gamper <97gamjak@gmail.com> Date: Fri, 14 Aug 2026 22:31:28 +0200 Subject: [PATCH 3/7] add tasks.json and license headers --- .vscode/tasks.json | 108 +++++++++++++++++++++++++++++ include/mstd/types.hpp | 22 ++++++ include/mstd/types/strong_type.hpp | 22 ++++++ 3 files changed, 152 insertions(+) create mode 100644 .vscode/tasks.json diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..d306176 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,108 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Cppcheck", + "type": "shell", + "command": "cppcheck", + "args": [ + "--enable=all", + "--enable=performance", + "--enable=style", + "--enable=information", + "--enable=portability", + "--error-exitcode=1", + "--suppressions-list=.cppcheck.suppress", + "--suppress=missingIncludeSystem", + "--inline-suppr", + "--inconclusive", + "-I", + "include", + "include", + "test" + ], + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": { + "owner": "cppcheck", + "fileLocation": [ + "relative", + "${workspaceFolder}" + ], + "pattern": { + "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error|style|performance|portability|information):\\s+(.*)\\s+\\[(\\w+)\\]$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5, + "code": 6 + } + }, + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated", + "clear": true + } + }, + { + "label": "Clang-Tidy (cpp_checks)", + "type": "shell", + "command": "cpp_checks", + "args": [ + "--dirs", + "include", + "--dirs", + "test" + ], + "options": { + "cwd": "${workspaceFolder}" + }, + "problemMatcher": { + "owner": "clang-tidy", + "fileLocation": [ + "relative", + "${workspaceFolder}" + ], + "pattern": { + "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)\\s+\\[([\\w,-]+)\\]$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5, + "code": 6 + } + }, + "group": "build", + "presentation": { + "reveal": "always", + "panel": "dedicated", + "clear": true + } + }, + { + "label": "Static Analysis: All", + "dependsOn": [ + "Cppcheck", + "Clang-Tidy (cpp_checks)" + ], + "dependsOrder": "sequence", + "problemMatcher": [], + "group": "build" + }, + { + "type": "cmake", + "label": "CMake: build", + "command": "build", + "targets": [ + "all" + ], + "group": "build", + "problemMatcher": [], + "detail": "CMake template build task" + } + ] +} diff --git a/include/mstd/types.hpp b/include/mstd/types.hpp index 3743310..1d3e0c1 100644 --- a/include/mstd/types.hpp +++ b/include/mstd/types.hpp @@ -1,3 +1,25 @@ +/***************************************************************************** + + + mstd library + Copyright (C) 2025-now Jakob Gamper + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + +******************************************************************************/ + #ifndef __MSTD__TYPES_HPP__ #define __MSTD__TYPES_HPP__ diff --git a/include/mstd/types/strong_type.hpp b/include/mstd/types/strong_type.hpp index de8bff2..6b9130e 100644 --- a/include/mstd/types/strong_type.hpp +++ b/include/mstd/types/strong_type.hpp @@ -1,3 +1,25 @@ +/***************************************************************************** + + + mstd library + Copyright (C) 2025-now Jakob Gamper + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + +******************************************************************************/ + #ifndef __MSTD__TYPES__STRONG_TYPE_HPP__ #define __MSTD__TYPES__STRONG_TYPE_HPP__ From dac91c1835609c51c0c147332a5b9b1b07d7f342 Mon Sep 17 00:00:00 2001 From: Jakob Gamper <97gamjak@gmail.com> Date: Fri, 14 Aug 2026 23:22:27 +0200 Subject: [PATCH 4/7] fix bugs and update lint ci to use newer cppcheck --- .github/workflows/static-analysis.yml | 10 ++-- .vscode/tasks.json | 1 + include/mstd/enum.hpp | 78 +++++++++++++-------------- include/mstd/types/strong_type.hpp | 29 +++++----- include/mstd/types/strong_type.tpp | 26 ++++----- 5 files changed, 75 insertions(+), 69 deletions(-) diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 20707ca..b34c4f8 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -17,10 +17,14 @@ jobs: with: submodules: recursive + - uses: actions/setup-python@v5 + with: + python-version: 3.13 + cache: 'pip' + - name: Install cppcheck run: | - sudo apt update - sudo apt install -y cppcheck + pip install --break-system-packages cppcheck - name: Run cppcheck run: | @@ -57,4 +61,4 @@ jobs: - name: Install clang-tidy and dependencies run: | - cpp_checks --dirs include --dirs test \ No newline at end of file + cpp_checks --dirs include --dirs test diff --git a/.vscode/tasks.json b/.vscode/tasks.json index d306176..a9f3f2e 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -16,6 +16,7 @@ "--suppress=missingIncludeSystem", "--inline-suppr", "--inconclusive", + "--std=c++23", "-I", "include", "include", diff --git a/include/mstd/enum.hpp b/include/mstd/enum.hpp index f88a059..0ace569 100644 --- a/include/mstd/enum.hpp +++ b/include/mstd/enum.hpp @@ -124,45 +124,45 @@ #define MSTD_ENUM(EnumName, Underlying, LIST) enum class EnumName : Underlying; #endif -#define MSTD_ENUM_BITFLAG(EnumName, Underlying, LIST) \ - MSTD_ENUM(EnumName, Underlying, LIST) \ - \ - inline EnumName operator|(EnumName lhs, EnumName rhs) \ - { \ - return static_cast( \ - static_cast(lhs) | static_cast(rhs) \ - ); \ - } \ - inline EnumName& operator|=(EnumName& lhs, EnumName rhs) \ - { \ - lhs = lhs | rhs; \ - return lhs; \ - } \ - \ - struct EnumName##FlagTest \ - { \ - Underlying value; \ - constexpr operator EnumName() const noexcept \ - { \ - return static_cast(value); \ - } \ - \ - constexpr explicit operator bool() const noexcept \ - { \ - return value != Underlying{0}; \ - } \ - }; \ - \ - inline EnumName##FlagTest operator&(EnumName lhs, EnumName rhs) \ - { \ - return EnumName##FlagTest{static_cast( \ - static_cast(lhs) & static_cast(rhs) \ - )}; \ - } \ - \ - inline bool operator!(EnumName lhs) \ - { \ - return !static_cast(lhs); \ +#define MSTD_ENUM_BITFLAG(EnumName, Underlying, LIST) \ + MSTD_ENUM(EnumName, Underlying, LIST) \ + \ + inline constexpr EnumName operator|(EnumName lhs, EnumName rhs) \ + { \ + return static_cast( \ + static_cast(lhs) | static_cast(rhs) \ + ); \ + } \ + inline constexpr EnumName& operator|=(EnumName& lhs, EnumName rhs) \ + { \ + lhs = lhs | rhs; \ + return lhs; \ + } \ + \ + struct EnumName##FlagTest \ + { \ + Underlying value; \ + constexpr operator EnumName() const noexcept \ + { \ + return static_cast(value); \ + } \ + \ + constexpr explicit operator bool() const noexcept \ + { \ + return value != Underlying{0}; \ + } \ + }; \ + \ + inline constexpr EnumName##FlagTest operator&(EnumName lhs, EnumName rhs) \ + { \ + return EnumName##FlagTest{static_cast( \ + static_cast(lhs) & static_cast(rhs) \ + )}; \ + } \ + \ + inline constexpr bool operator!(EnumName lhs) \ + { \ + return !static_cast(lhs); \ } #endif // __MSTD__ENUM_HPP__ diff --git a/include/mstd/types/strong_type.hpp b/include/mstd/types/strong_type.hpp index 6b9130e..6c2f0a8 100644 --- a/include/mstd/types/strong_type.hpp +++ b/include/mstd/types/strong_type.hpp @@ -25,9 +25,10 @@ #include #include -#include #include +#include "mstd/enum.hpp" + namespace mstd { @@ -110,52 +111,52 @@ namespace mstd // constexpr StrongType &operator+=(const StrongType &other) - requires(Traits &StrongTypeTrait::ARITHMETIC && true); + requires(bool(Traits &StrongTypeTrait::ARITHMETIC)); constexpr StrongType &operator-=(const StrongType &other) - requires(Traits &StrongTypeTrait::ARITHMETIC && true); + requires(bool(Traits &StrongTypeTrait::ARITHMETIC)); constexpr StrongType &operator*=(const T &scalar) - requires(Traits &StrongTypeTrait::ARITHMETIC && true); + requires(bool(Traits &StrongTypeTrait::ARITHMETIC)); constexpr StrongType &operator/=(const T &scalar) - requires(Traits &StrongTypeTrait::ARITHMETIC && true); + requires(bool(Traits &StrongTypeTrait::ARITHMETIC)); [[nodiscard]] constexpr StrongType operator+(const StrongType &other) const - requires(Traits &StrongTypeTrait::ARITHMETIC && true); + requires(bool(Traits &StrongTypeTrait::ARITHMETIC)); [[nodiscard]] constexpr StrongType operator-(const StrongType &other) const - requires(Traits &StrongTypeTrait::ARITHMETIC && true); + requires(bool(Traits &StrongTypeTrait::ARITHMETIC)); [[nodiscard]] constexpr StrongType operator*(const T &scalar) const - requires(Traits &StrongTypeTrait::ARITHMETIC && true); + requires(bool(Traits &StrongTypeTrait::ARITHMETIC)); [[nodiscard]] constexpr StrongType operator/(const T &scalar) const - requires(Traits &StrongTypeTrait::ARITHMETIC && true); + requires(bool(Traits &StrongTypeTrait::ARITHMETIC)); [[nodiscard]] constexpr StrongType operator-() const - requires(Traits &StrongTypeTrait::ARITHMETIC && true); + requires(bool(Traits &StrongTypeTrait::ARITHMETIC)); // // Increment / decrement // constexpr StrongType &operator++() - requires(Traits &StrongTypeTrait::INCREMENT && true); + requires(bool(Traits &StrongTypeTrait::INCREMENT)); constexpr StrongType operator++(int) - requires(Traits &StrongTypeTrait::INCREMENT && true); + requires(bool(Traits &StrongTypeTrait::INCREMENT)); constexpr StrongType &operator--() - requires(Traits &StrongTypeTrait::INCREMENT && true); + requires(bool(Traits &StrongTypeTrait::INCREMENT)); constexpr StrongType operator--(int) - requires(Traits &StrongTypeTrait::INCREMENT && true); + requires(bool(Traits &StrongTypeTrait::INCREMENT)); private: T _value{}; diff --git a/include/mstd/types/strong_type.tpp b/include/mstd/types/strong_type.tpp index 1190cf8..c7a45fd 100644 --- a/include/mstd/types/strong_type.tpp +++ b/include/mstd/types/strong_type.tpp @@ -141,7 +141,7 @@ namespace mstd template constexpr StrongType &StrongType:: operator+=(const StrongType &other) - requires(Traits &StrongTypeTrait::ARITHMETIC && true) + requires(bool(Traits &StrongTypeTrait::ARITHMETIC)) { _value += other._value; return *this; @@ -166,7 +166,7 @@ namespace mstd template constexpr StrongType &StrongType:: operator-=(const StrongType &other) - requires(Traits &StrongTypeTrait::ARITHMETIC && true) + requires(bool(Traits &StrongTypeTrait::ARITHMETIC)) { _value -= other._value; return *this; @@ -190,7 +190,7 @@ namespace mstd template constexpr StrongType &StrongType:: operator*=(const T &scalar) - requires(Traits &StrongTypeTrait::ARITHMETIC && true) + requires(bool(Traits &StrongTypeTrait::ARITHMETIC)) { _value *= scalar; return *this; @@ -214,7 +214,7 @@ namespace mstd template constexpr StrongType &StrongType:: operator/=(const T &scalar) - requires(Traits &StrongTypeTrait::ARITHMETIC && true) + requires(bool(Traits &StrongTypeTrait::ARITHMETIC)) { _value /= scalar; return *this; @@ -239,7 +239,7 @@ namespace mstd constexpr StrongType StrongType::operator+( const StrongType &other ) const - requires(Traits &StrongTypeTrait::ARITHMETIC && true) + requires(bool(Traits &StrongTypeTrait::ARITHMETIC)) { return StrongType{_value + other._value}; } @@ -264,7 +264,7 @@ namespace mstd constexpr StrongType StrongType::operator-( const StrongType &other ) const - requires(Traits &StrongTypeTrait::ARITHMETIC && true) + requires(bool(Traits &StrongTypeTrait::ARITHMETIC)) { return StrongType{_value - other._value}; } @@ -288,7 +288,7 @@ namespace mstd constexpr StrongType StrongType::operator*( const T &scalar ) const - requires(Traits &StrongTypeTrait::ARITHMETIC && true) + requires(bool(Traits &StrongTypeTrait::ARITHMETIC)) { return StrongType{_value * scalar}; } @@ -312,7 +312,7 @@ namespace mstd constexpr StrongType StrongType::operator/( const T &scalar ) const - requires(Traits &StrongTypeTrait::ARITHMETIC && true) + requires(bool(Traits &StrongTypeTrait::ARITHMETIC)) { return StrongType{_value / scalar}; } @@ -334,7 +334,7 @@ namespace mstd template constexpr StrongType StrongType::operator-( ) const - requires(Traits &StrongTypeTrait::ARITHMETIC && true) + requires(bool(Traits &StrongTypeTrait::ARITHMETIC)) { return StrongType{-_value}; } @@ -356,7 +356,7 @@ namespace mstd template constexpr StrongType &StrongType:: operator++() - requires(Traits &StrongTypeTrait::INCREMENT && true) + requires(bool(Traits &StrongTypeTrait::INCREMENT)) { ++_value; return *this; @@ -380,7 +380,7 @@ namespace mstd constexpr StrongType StrongType::operator++( int ) - requires(Traits &StrongTypeTrait::INCREMENT && true) + requires(bool(Traits &StrongTypeTrait::INCREMENT)) { auto tmp = *this; ++_value; @@ -404,7 +404,7 @@ namespace mstd template constexpr StrongType &StrongType:: operator--() - requires(Traits &StrongTypeTrait::INCREMENT && true) + requires(bool(Traits &StrongTypeTrait::INCREMENT)) { --_value; return *this; @@ -428,7 +428,7 @@ namespace mstd constexpr StrongType StrongType::operator--( int ) - requires(Traits &StrongTypeTrait::INCREMENT && true) + requires(bool(Traits &StrongTypeTrait::INCREMENT)) { auto tmp = *this; --_value; From 51e479e5f8d26375a3fcbac14cd916581d99fc3d Mon Sep 17 00:00:00 2001 From: Jakob Gamper <97gamjak@gmail.com> Date: Sat, 15 Aug 2026 18:31:50 +0200 Subject: [PATCH 5/7] add toString method --- include/mstd/types/strong_type.hpp | 7 +++++-- include/mstd/types/strong_type.tpp | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/include/mstd/types/strong_type.hpp b/include/mstd/types/strong_type.hpp index 6c2f0a8..91a10cc 100644 --- a/include/mstd/types/strong_type.hpp +++ b/include/mstd/types/strong_type.hpp @@ -64,6 +64,9 @@ namespace mstd StrongTypeTrait Traits = StrongTypeTrait::NONE> class StrongType { + private: + T _value{}; + public: using ValueType = T; using TagType = Tag; @@ -158,8 +161,8 @@ namespace mstd constexpr StrongType operator--(int) requires(bool(Traits &StrongTypeTrait::INCREMENT)); - private: - T _value{}; + std::string toString() const + requires(Tag::toString(T{})); }; } // namespace mstd diff --git a/include/mstd/types/strong_type.tpp b/include/mstd/types/strong_type.tpp index c7a45fd..1181295 100644 --- a/include/mstd/types/strong_type.tpp +++ b/include/mstd/types/strong_type.tpp @@ -434,6 +434,28 @@ namespace mstd --_value; return tmp; } + + /** + * @brief Convert StrongType to string. + * + * This function converts the StrongType to a string representation. It is + * only enabled if the Tag type provides a static toString function that + * accepts the underlying type T. + * + * @tparam T underlying value type + * @tparam Tag a unique tag type used only for disambiguation, e.g. `struct + * EnergyTag;` + * @tparam Traits bitwise-OR of StrongTypeTrait flags enabling specific + * operations + * + * @return a string representation of the StrongType + */ + template + std::string StrongType::toString() const + requires(Tag::toString(T{})) + { + return Tag::toString(_value); + } } // namespace mstd #endif // __MSTD__TYPES__STRONG_TYPE_TPP__ From 6144f43910a73f06126960735d461dfb0eed2cf8 Mon Sep 17 00:00:00 2001 From: Jakob Gamper <97gamjak@gmail.com> Date: Sat, 15 Aug 2026 18:43:14 +0200 Subject: [PATCH 6/7] introduce new concept toString --- CHANGELOG.md | 4 ++++ include/mstd/type_traits.hpp | 3 ++- include/mstd/type_traits/string.hpp | 22 ++++++++++++++++++++++ include/mstd/types/strong_type.hpp | 3 ++- include/mstd/types/strong_type.tpp | 2 +- 5 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 include/mstd/type_traits/string.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a3d806..2fe311a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ All notable changes to this project will be documented in this file. - add convenient template for creating strong types easily +### Type Traits + +- Introduce new concept `HasToString` + ## [0.1.5](https://github.com/repo/owner/releases/tag/0.1.5) - 2026-08-13 diff --git a/include/mstd/type_traits.hpp b/include/mstd/type_traits.hpp index dc30437..8a37714 100644 --- a/include/mstd/type_traits.hpp +++ b/include/mstd/type_traits.hpp @@ -29,5 +29,6 @@ #include "type_traits/quantity_traits.hpp" // IWYU pragma: export #include "type_traits/ranges_traits.hpp" // IWYU pragma: export #include "type_traits/ratio_traits.hpp" // IWYU pragma: export +#include "type_traits/string.hpp" // IWYU pragma: export -#endif // __MSTD__TYPE_TRAITS_HPP__ \ No newline at end of file +#endif // __MSTD__TYPE_TRAITS_HPP__ diff --git a/include/mstd/type_traits/string.hpp b/include/mstd/type_traits/string.hpp new file mode 100644 index 0000000..6aa02c8 --- /dev/null +++ b/include/mstd/type_traits/string.hpp @@ -0,0 +1,22 @@ +#ifndef __MSTD__TYPE_TRAITS__STRING_HPP__ +#define __MSTD__TYPE_TRAITS__STRING_HPP__ + +#include +#include + +namespace mstd +{ + /** + * @brief Concept checking whether Tag::toString(const T&) is a valid + * expression that returns something convertible to std::string. + * + * @tparam Tag the tag type expected to provide a static toString member + * @tparam T the value type passed to Tag::toString + */ + template + concept HasToString = requires(const T &value) { + { Tag::toString(value) } -> std::convertible_to; + }; +} // namespace mstd + +#endif // __MSTD__TYPE_TRAITS__STRING_HPP__ diff --git a/include/mstd/types/strong_type.hpp b/include/mstd/types/strong_type.hpp index 91a10cc..304d199 100644 --- a/include/mstd/types/strong_type.hpp +++ b/include/mstd/types/strong_type.hpp @@ -28,6 +28,7 @@ #include #include "mstd/enum.hpp" +#include "mstd/type_traits/string.hpp" namespace mstd { @@ -162,7 +163,7 @@ namespace mstd requires(bool(Traits &StrongTypeTrait::INCREMENT)); std::string toString() const - requires(Tag::toString(T{})); + requires HasToString; }; } // namespace mstd diff --git a/include/mstd/types/strong_type.tpp b/include/mstd/types/strong_type.tpp index 1181295..8842ac1 100644 --- a/include/mstd/types/strong_type.tpp +++ b/include/mstd/types/strong_type.tpp @@ -452,7 +452,7 @@ namespace mstd */ template std::string StrongType::toString() const - requires(Tag::toString(T{})) + requires HasToString { return Tag::toString(_value); } From a4c87ccb299cc2883a5eb4aab80860c7b98ec15f Mon Sep 17 00:00:00 2001 From: Jakob Gamper <97gamjak@gmail.com> Date: Sat, 15 Aug 2026 19:45:53 +0200 Subject: [PATCH 7/7] add missing license header --- include/mstd/type_traits/string.hpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/include/mstd/type_traits/string.hpp b/include/mstd/type_traits/string.hpp index 6aa02c8..953092b 100644 --- a/include/mstd/type_traits/string.hpp +++ b/include/mstd/type_traits/string.hpp @@ -1,3 +1,25 @@ +/***************************************************************************** + + + mstd library + Copyright (C) 2025-now Jakob Gamper + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + +******************************************************************************/ + #ifndef __MSTD__TYPE_TRAITS__STRING_HPP__ #define __MSTD__TYPE_TRAITS__STRING_HPP__