Min function accepting varying number of arguments in C++17

Come across this problem once again in the book The Modern C++ Challenge (problem 18). Wonder how simple and elegant the implementation could be using C++17. Following is my solution. Ideas? ^_^

#include <algorithm>  template <typename Less, typename T, typename... Ts> constexpr const T& min(Less less, const T& a, const T& b, const Ts&... rems) {   if constexpr (sizeof...(rems)) {     return min(less, std::min(a, b, less), rems...);   }   else {     return std::min(a, b, less);   } } 

2D counterpart of std::array in C++17

I implemented a 2D counterpart of std::array named array2d in C++17. It is an aggregate like std::array, and provides similar interface. The goal is that if you know how to use std::array, then you will find yourself at home using array2d. Any comments are welcome 🙂 For better viewing experience with highlighting, you can refer to this GitHub page.

#include <cstddef> #include <array> #include <iterator>  template <typename T, std::size_t N0, std::size_t N1> struct array2d {   using row_t = std::array<T, N1>;   inline static constexpr std::array sizes{ N0, N1 };    static constexpr std::size_t size() noexcept { return N0 * N1; }   static constexpr bool empty() noexcept { return !size(); }    T& at(std::size_t i, std::size_t j) { return data_.at(i).at(j); }   const T& at(std::size_t i, std::size_t j) const { return data_.at(i).at(j); }    row_t& operator[](std::size_t i) noexcept { return data_[i]; }   const row_t& operator[](std::size_t i) const noexcept { return data_[i]; }    T& front() { return data_.front().front(); }   const T& front() const { return data_.front().front(); }    T& back() { return data_.back().back(); }   const T& back() const { return data_.back().back(); }    T* data() noexcept { return data_.data()->data(); }   const T* data() const noexcept { return data_.data()->data(); }    T* begin() noexcept { return data(); }   const T* begin() const noexcept { return data(); }    T* end() noexcept { return data() + size(); }   const T* end() const noexcept { return data() + size(); }    auto rbegin() noexcept { return std::make_reverse_iterator(end()); }   auto rbegin() const noexcept { return std::make_reverse_iterator(end()); }    auto rend() noexcept { return std::make_reverse_iterator(begin()); }   auto rend() const noexcept { return std::make_reverse_iterator(begin()); }    void fill(const T& v) {     for (auto& row : data_) {       row.fill(v);     }   }    friend void swap(array2d& a, array2d& b) { a.data_.swap(b.data_); }    std::array<row_t, N0> data_; }; 

A string_splitter using C++17

In this repo I’ve put together a header only string splitter, allowing for characters and string literals as delimiters.

The (little) library is strictly C++17.

I would like to ask for your comments.

As it appears to be mandatory to include at least 3 lines of code, here’s the code:

// MIT License // // Copyright (c) 2019 degski // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE.  #pragma once  #include <sax/iostream.hpp> #include <string> #include <string_view> #include <type_traits> #include <vector>   namespace sax::detail {  template<typename CharT> [[ nodiscard ]] constexpr std::basic_string_view<CharT> make_string_view ( std::basic_string_view<CharT> x ) noexcept {     return x; // guaranteed copy elision. } template<typename CharT> [[ nodiscard ]] constexpr std::basic_string_view<CharT> make_string_view ( CharT x ) noexcept {     return std::basic_string_view<CharT> ( std::addressof ( x ), 1 ); } template<typename CharT> [[ nodiscard ]] constexpr std::basic_string_view<CharT> make_string_view ( const CharT * x ) noexcept {     return std::basic_string_view<CharT> ( x ); }   template<typename CharT> constexpr void remove_prefix ( std::basic_string_view<CharT> & s, bool & removed, std::basic_string_view<CharT> x ) noexcept {     if ( s.size ( ) >= x.size ( ) and s.compare ( 0, x.size ( ), x ) == 0 ) {         s.remove_prefix ( x.size ( ) );         removed = removed or true;     }; } template<typename CharT> constexpr void remove_prefix ( std::basic_string_view<CharT> & s, bool & removed, CharT x ) noexcept {     if ( s.size ( ) >= 1 and s [ 0 ] == x ) {         s.remove_prefix ( 1 );         removed = removed or true;     }; } template<typename CharT> constexpr void remove_prefix ( std::basic_string_view<CharT> & s, bool & removed, const CharT * x ) noexcept {     remove_prefix ( s, removed, std::basic_string_view<CharT> ( x ) ); } template<typename CharT, typename ... Args> constexpr void remove_prefix ( std::basic_string_view<CharT> & s_, Args ... args_ ) noexcept {     bool removed = false;     do {         removed = false;         ( remove_prefix ( s_, removed, std::forward<Args> ( args_ ) ), ... );     } while ( removed ); // Keep removing untill nothing more can be removed. }   template<typename CharT> constexpr void remove_suffix ( std::basic_string_view<CharT> & s, bool & removed, std::basic_string_view<CharT> x ) noexcept {     if ( s.size ( ) >= x.size ( ) and s.compare ( s.size ( ) - x.size ( ), std::basic_string_view<CharT>::npos, x ) == 0 ) {         s.remove_suffix ( x.size ( ) );         removed = removed or true;     }; } template<typename CharT> constexpr void remove_suffix ( std::basic_string_view<CharT> & s, bool & removed, CharT x ) noexcept {     remove_suffix ( s, removed, std::basic_string_view<CharT> ( std::addressof ( x ), 1 ) ); } template<typename CharT> constexpr void remove_suffix ( std::basic_string_view<CharT> & s, bool & removed, const CharT * x ) noexcept {     remove_suffix ( s, removed, std::basic_string_view<CharT> ( x ) ); } template<typename CharT, typename ... Args> constexpr void remove_suffix ( std::basic_string_view<CharT> & s_, Args ... args_ ) noexcept {     bool removed = false;     do {         removed = false;         ( remove_suffix ( s_, removed, std::forward<Args> ( args_ ) ), ... );     } while ( removed ); // Keep removing untill nothing more can be removed. }   template<typename CharT, typename SizeT, typename StringyThing> constexpr void find ( std::basic_string_view<CharT> & s, SizeT & f_, StringyThing x_ ) noexcept {     f_ = std::min ( s.find ( make_string_view<CharT> ( x_ ) ), f_ ); } template<typename CharT, typename ... Args> [[ nodiscard ]] constexpr auto find ( std::basic_string_view<CharT> & s_, Args ... args_ ) noexcept {     auto found = std::basic_string_view<CharT>::npos;     ( find ( s_, found, std::forward<Args> ( args_ ) ), ... );     return found; }  }  namespace sax {  template<typename CharT, typename ... Delimiters> [[ nodiscard ]] std::vector<std::basic_string_view<CharT>> string_split ( const std::basic_string<CharT> & string_, Delimiters ... delimiters_ ) {     using size_type = typename std::basic_string_view<CharT>::size_type;     std::basic_string_view<CharT> string_view ( string_ );     std::vector<std::basic_string_view<CharT>> string_view_vector;     string_view_vector.reserve ( 4 ); // Avoid small size re-allocating, 0 > 1 > 2 > 3 > 4 > 6, now 4 > 6 > 9 etc.     // Remove trailing delimiters.     detail::remove_suffix ( string_view, std::forward<Delimiters> ( delimiters_ ) ... );     // Parse the string_view left to right.     while ( true ) {         detail::remove_prefix ( string_view, std::forward<Delimiters> ( delimiters_ ) ... );         const size_type pos = detail::find ( string_view, std::forward<Delimiters> ( delimiters_ ) ... );         if ( std::basic_string_view<CharT>::npos == pos ) {             string_view_vector.emplace_back ( std::move ( string_view ) );             break;         }         string_view_vector.emplace_back ( string_view.data ( ), pos );         string_view.remove_prefix ( pos );     }     return string_view_vector; }  } 

Map a set of types to unique IDs and runtime reinterpret back from ID and pointer in C++17

I wanted to create a relatively universal way of serialising an object, by doing a memcpy and generating a unique type ID. Stored together they can be used, for example, by another thread to restore a copy of the object for further processing.

This is used, for example, where one tread has a logging function requiring minimal overhead and converting the object to its logged state is considerably more expensive than making a raw copy.

Some other requirements / design choices:

  • IDs should be dense (no gaps)
  • IDs should fit the smallest unsigned possible
  • No RTTI allowed
  • A slight convenience overhead at the runtime restoring side is acceptable (and present in the form of the generated ‘if’ tree to match an ID to a type)
  • Both sides have access to the definition of the mapping
  • Handling of constructors with side-effects is left up to the user

Id’d love to hear any critiques or possible pitfalls! Below is the header pasted into a silly example to show the idea and interface:

// This keeps a variable in the final output: #define KEEP(x) volatile auto x __attribute__((unused))  #include <type_traits> #include <cstdint> #include <cstddef>  namespace type_list {         /**      * @brief Extract the N-th type of a set of template arguments      *      * @tparam N    Index of type to extract      * @tparam Ts   Arguments      */     template <std::size_t N, typename T, typename... Ts>     struct nth_type {         using type = typename nth_type<N-1, Ts...>::type;     };      template <typename T, typename... Ts>     struct nth_type<0, T, Ts...> {         using type = T;     };      /**      * @brief Extract the N-th type of a set of template arguments      *      * @tparam N    Index of type to extract      * @tparam Ts   Arguments      */     template <std::size_t N, typename... Ts>     using nth_type_t = typename nth_type<N, Ts...>::type;      /**      * @brief Find the index of the first matching type `IN` in a set of types.      *      * @tparam IN   Type to find      * @tparam T    First of type list      * @tparam Ts   Rest of type list      */     template <typename IN, typename T, typename... Ts>     struct index_of_type {         static_assert(sizeof...(Ts) != 0 || std::is_same_v<IN, T>, "No index for type found");         static constexpr const std::size_t value { 1 + index_of_type<IN, Ts...>::value };     };      template <typename IN, typename... Ts>     struct index_of_type<IN, IN, Ts...> {         static constexpr const std::size_t value { 0 };     };      /**      * @brief Find the index of the first matching type `IN` in a set of types.      *      * @tparam IN   Type to find      * @tparam Ts   Type list      */     template <typename IN, typename... Ts>     static constexpr const auto index_of_type_v { index_of_type<IN, Ts...>::value };      namespace {         static constexpr void noop(const std::size_t = 0) {}          template <size_t I, typename... Ts>         struct map_visit_impl {             template <typename F, typename E>             static constexpr decltype(auto) visit(const std::size_t id, const void *const ptr, F func, E on_error) {                 if (id == I - 1) {                     return func(*reinterpret_cast<const nth_type_t<I-1, Ts...> *const>(ptr));                 } else {                     return map_visit_impl<I - 1, Ts...>::visit(id, ptr, func, on_error);                 }             }              template <typename F, typename E>             static constexpr decltype(auto) visit(const std::size_t id, void *const ptr, F func, E on_error) {                 if (id == I - 1) {                     return func(*reinterpret_cast<nth_type_t<I-1, Ts...> *const>(ptr));                 } else {                     return map_visit_impl<I - 1, Ts...>::visit(id, ptr, func, on_error);                 }             }         };          template <typename... Ts>         struct map_visit_impl<0, Ts...> {             template <typename F, typename E>             static constexpr void visit(const std::size_t id, const void *const, F func, E on_error) {                 // If arrived here we have a invalid id                 on_error(id);             }              template <typename F, typename E>             static constexpr void visit(const std::size_t id, void *const, F func, E on_error) {                 // If arrived here we have a invalid id                 on_error(id);             }         };     }      /**      * @brief Create an ID map of a set of types.      *      * @tparam Ts Type list      */     template <typename... Ts>     struct map {         /**          * @brief Get the type with index `N`          *          * @tparam N Index of type to get          */         template <std::size_t N>         using type = type_list::nth_type_t<N, Ts...>;          /**          * @brief The ID number (index) of a given type `T`          *          * @tparam T          */         template <typename T>         static constexpr const std::size_t id { type_list::index_of_type_v<T, Ts...> };          /**          * @brief Number of types stored          */         static constexpr const std::size_t size { sizeof...(Ts) };          /**          * @brief Convert any given pointer to the type matching `id` and pass          * it to a function `func` as only argument using a `reinterpret_cast`.          *          * @tparam F    Function type          * @param id    id / index of type          * @param ptr   Storage location          * @param func  Handler function          * @return      Result of handler function          */         template <typename F, typename E = decltype(noop)>         static constexpr decltype(auto) parse(const std::size_t id, const void *const ptr, F func, E on_error = noop) {             return map_visit_impl<sizeof...(Ts), Ts...>::visit(id, ptr, func, on_error);         }          /**          * @brief Convert any given pointer to the type matching `id` and pass          * it to a function `func` as only argument using a `reinterpret_cast`.          *          * @tparam F    Function type          * @param id    id / index of type          * @param ptr   Storage location          * @param func  Handler function          * @return      Result of handler function          */         template <typename F, typename E = decltype(noop)>         static constexpr decltype(auto) parse(const std::size_t id, void *const ptr, F func, E on_error = noop) {             return map_visit_impl<sizeof...(Ts), Ts...>::visit(id, ptr, func, on_error);         }     }; }  // Generate unique types template <size_t N> struct c {};  // Demo set of types using map = type_list::map<     uint8_t, uint16_t, uint32_t, int8_t, int16_t, int32_t,     c<__COUNTER__>, c<__COUNTER__>, c<__COUNTER__>, c<__COUNTER__>,     c<__COUNTER__>, c<__COUNTER__>, c<__COUNTER__>, c<__COUNTER__>,     c<__COUNTER__>, c<__COUNTER__>, c<__COUNTER__>, c<__COUNTER__>,     c<__COUNTER__>, c<__COUNTER__>, c<__COUNTER__>, c<__COUNTER__>,     c<__COUNTER__>, c<__COUNTER__>, c<__COUNTER__>, c<__COUNTER__>,     c<__COUNTER__>, c<__COUNTER__>, c<__COUNTER__>, c<__COUNTER__>,     c<__COUNTER__>, c<__COUNTER__>, c<__COUNTER__>, c<__COUNTER__>,     c<__COUNTER__>, c<__COUNTER__>, c<__COUNTER__>, c<__COUNTER__>,     c<__COUNTER__>, c<__COUNTER__>, c<__COUNTER__>, c<__COUNTER__>,     c<__COUNTER__>, c<__COUNTER__>, c<__COUNTER__>, c<__COUNTER__> >;  // Just a quick hack to initialise some test data: char bytes[] = "Test string, bla bla";  uint64_t counter = 0;  void fn(const std::size_t n, const std::size_t i) {     __asm volatile("# LLVM-MCA-BEGIN type_map_overhead");     map::parse(n, &bytes[i], [&](auto& val) {         __asm volatile("# LLVM-MCA-END");         // Needed because the handler needs to apply to any type in the map:         if constexpr (std::is_integral_v<decltype(val)>) {             counter += val;         }     }); }  int main() {     KEEP(k1) = map::id<uint16_t>; // size_t => 1     KEEP(k2) = std::is_same_v<map::type<1>, uint16_t>; // bool => true      fn(0, 0);     fn(1, 1);     fn(2, 2);      KEEP(k4) = counter; } 

YA hash tuple in C++17

Is there something terribly wrong with this implementation?

template <class T> constexpr inline std::size_t hash_combine(T const& v,   std::size_t const seed = {}) noexcept {   return seed ^ (std::hash<T>()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2)); }  template <typename ...T> struct hash<std::tuple<T...>> {   template <typename A1, typename ...A, std::size_t ...I>   static auto apply_tuple(std::tuple<A1, A...> const& t,     std::index_sequence<I...>) noexcept   {     if constexpr(sizeof...(A))     {       return hash_combine(std::get<0>(t),         std::hash<std::tuple<A const&...>>()(           std::tuple<A const&...>{std::get<I + 1>(t)...}         )       );     }     else     {       return std::hash<std::remove_cv_t<std::remove_reference_t<A1>>>()(         std::get<0>(t));     }   }    auto operator()(std::tuple<T...> const& t) const noexcept   {     return apply_tuple(t,       std::make_index_sequence<sizeof...(T) - 1>()     );   } };