Skip to content

Latest commit

 

History

History
609 lines (509 loc) · 18.1 KB

212.md

File metadata and controls

609 lines (509 loc) · 18.1 KB
Info

Example

auto sum = [sums = 0](auto sum) mutable {
  return sums += sum;
};

int main() {
  std::cout << sum(1) << sum(2) << sum(3); // prints 1 3 6
}

https://godbolt.org/z/GEse7z

Puzzle

  • Can you implement a mutable lambda sum_prices which parses the input buffer and sums prices (price1,price2,...) for matching messages (dispatch based on header.id)?

    • Double points for the most generic solution
    • Triple points for the most efficient solution (Assembly)
struct [[gnu::packed]] header {
  short id;
};

struct [[gnu::packed]] add1 {
  header h;
  std::array<char, 1> price1;
};

struct [[gnu::packed]] add2 {
  header h;
  std::array<char, 2> price1;
};

struct [[gnu::packed]] add3 {
  header h;
  std::array<char, 5> price1;
  std::array<char, 6> price2;
};

template<auto Id, class T>
struct msg_traits {
  static constexpr auto id = Id;
  using type = T;
};

template<class...>
auto sum_prices = [](std::string_view buffer) {
  /*TODO*/return 0;
};

int main() {
  using namespace boost::ut;

  "should return 0 with empty buffer"_test = [] {
    expect(0_i == sum_prices<>(std::string_view{}));
  };

  "should return 0 with empty buffer and traits"_test = [] {
    expect(0_i == sum_prices<msg_traits<1, add1>>(std::string_view{}));
  };

  "should return 0 with not matchind id"_test = [] {
    add1 msg{ {.id = {}}, std::array{'9'} };
    expect(0_i == (sum_prices<msg_traits<7, add1>>(std::string_view{reinterpret_cast<const char*>(&msg), sizeof(msg)})));
  };

  "should return the price of matching message"_test = [] {
    add1 msg{ {.id = 42}, std::array{'9'} };
    expect(9_i == (sum_prices<msg_traits<42, add1>>(std::string_view{reinterpret_cast<const char*>(&msg), sizeof(msg)})));
  };

  "should return sum of prices for the same id"_test = [] {
    add1 msg1{ {.id = 42}, std::array{'2'} };
    expect(2_i == (sum_prices<msg_traits<0, add2>, msg_traits<42, add1>>(std::string_view{reinterpret_cast<const char*>(&msg1), sizeof(msg1)})));

    add1 msg2{ {.id = 42}, std::array{'3'} };
    expect(_i(2 + 3) == (sum_prices<msg_traits<0, add2>, msg_traits<42, add1>>(std::string_view{reinterpret_cast<const char*>(&msg2), sizeof(msg2)})));
  };

  "should return sum of prices for matching messages by id"_test = [] {
    add1 msg1{ {.id = 1}, std::array{'1'} };
    expect(1_i == (sum_prices<msg_traits<1, add1>, msg_traits<2, add2>>(std::string_view{reinterpret_cast<const char*>(&msg1), sizeof(msg1)})));

    add2 msg2{ {.id = 2}, std::array{'4', '2'} };
    expect(_i(1 + 42) == (sum_prices<msg_traits<1, add1>, msg_traits<2, add2>>(std::string_view{reinterpret_cast<const char*>(&msg2), sizeof(msg2)})));
  };

  "should return sum of prices for matching messages by id in different oder"_test = [] {
    add2 msg2{ {.id = 200}, std::array{'1', '1'} };
    expect(11_i == (sum_prices<msg_traits<200, add2>, msg_traits<100, add1>>(std::string_view{reinterpret_cast<const char*>(&msg2), sizeof(msg2)})));

    add1 msg1{ {.id = 100}, std::array{'7'} };
    expect(_i(11 + 7) == (sum_prices<msg_traits<200, add2>, msg_traits<100, add1>>(std::string_view{reinterpret_cast<const char*>(&msg1), sizeof(msg1)})));
  };

  "should return sum of prices for matching messages with spaces in prices "_test = [] {
    add1 msg1{ {.id = 100}, std::array{' '} };
    expect(0_i == (sum_prices<msg_traits<100, add1>, msg_traits<200, add2>, msg_traits<300, add3>>(std::string_view{reinterpret_cast<const char*>(&msg1), sizeof(msg1)})));

    add2 msg2{ {.id = 200}, std::array{' ', '9'} };
    expect(9_i == (sum_prices<msg_traits<100, add1>, msg_traits<200, add2>, msg_traits<300, add3>>(std::string_view{reinterpret_cast<const char*>(&msg2), sizeof(msg2)})));

    add3 msg3{ {.id = 300}, std::array{' ', ' ', '1', '2', '3'}, std::array{' ', '0', '8', '7', '0', '0'} };
    expect(_i(9 + 123 + 8700) == (sum_prices<msg_traits<100, add1>, msg_traits<200, add2>, msg_traits<300, add3>>(std::string_view{reinterpret_cast<const char*>(&msg3), sizeof(msg3)})));
  };
}

https://godbolt.org/z/n138a1

Solutions

constexpr auto integer_price = [](const auto price) {
    auto p = 0;
    for (auto i = 0u; i < std::size(price); ++i) {
      if (std::isdigit(price[i])) {
        p = (p * 10) + (price[i] - '0');
      }
    }
    return p;
};

template<class Traits>
auto sum_prices_impl = [](std::string_view buffer) {
  auto sum = 0;
  if (const auto* msg = reinterpret_cast<const typename Traits::type*>(std::data(buffer)); std::size(buffer) and Traits::id == msg->h.id) {
    if constexpr (requires {msg->price1;}) {
      sum += integer_price(msg->price1);
    }
    if constexpr (requires {msg->price2;}) {
      sum += integer_price(msg->price2);
    }
  }
  return sum;
};

template<class... Traits>
auto sum_prices = [total = 0](std::string_view buffer) mutable {
    return total += (0 + ... + sum_prices_impl<Traits>(buffer));
};

https://godbolt.org/z/dMhWxW

namespace detail {
template <class T, class... TArgs> decltype(void(T{std::declval<TArgs>()...}), std::true_type{}) test_is_braces_constructible(int);
template <class, class...> std::false_type test_is_braces_constructible(...);
template <class T, class... TArgs> using is_braces_constructible = decltype(test_is_braces_constructible<T, TArgs...>(0));
struct any_type { template<class T> constexpr explicit(false) operator T(); };

template<class T>
constexpr auto to_tuple(T&& object) noexcept {
  using type = std::decay_t<T>;
  if constexpr(is_braces_constructible<type, any_type, any_type, any_type>{}) {
    auto&& [p1, p2, p3] = std::forward<T>(object);
    return std::tuple(p1, p2, p3);
  } else if constexpr(is_braces_constructible<type, any_type, any_type>{}) {
    auto&& [p1, p2] = std::forward<T>(object);
    return std::tuple(p1, p2);
  } else if constexpr(is_braces_constructible<type, any_type>{}) {
    auto&& [p1] = std::forward<T>(object);
    return std::tuple(p1);
  } else {
    return std::tuple{};
  }
}

template<class R, R N>
static constexpr const R ascii_to_int[] = {
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 1 * R(std::pow(10, N)), 2* R(std::pow(10, N)), 3* R(std::pow(10, N)), 4* R(std::pow(10, N)), 5* R(std::pow(10, N)), 6* R(std::pow(10, N)), 7* R(std::pow(10, N)), 8* R(std::pow(10, N)), 9* R(std::pow(10, N)) };

template<class R, auto Size>
constexpr auto to_number = [](const auto& buffer) {
  return []<auto... Ns>(const auto& buffer, std::integer_sequence<R, Ns...>) {
    return ((ascii_to_int<R, sizeof...(Ns) - Ns - 1>[buffer[Ns]]) + ...);
  }(buffer, std::make_integer_sequence<R, Size>{});
};

template <class THeader, class... Ts>
auto sum_prices(const std::tuple<THeader, Ts...>& msg) {
  return (to_number<std::uint32_t, Ts{}.size()>(std::data(std::get<Ts>(msg))) + ...);
}

template <class... Ts>
constexpr auto sum_prices(const auto& buffer) {
  return ([&buffer] {
    const auto* msg = reinterpret_cast<const typename Ts::type*>(buffer);
    return msg->h.id == Ts::id ? sum_prices(to_tuple(*msg)) : 0;
  }() + ... + 0);
}
}  // namespace detail

template <class... Ts>
auto sum_prices = [prices = 0](std::string_view buffer) mutable {
  return std::size(buffer) ? prices += detail::sum_prices<Ts...>(std::data(buffer)) : prices;
};

https://godbolt.org/z/Y3cEn9 | https://godbolt.org/z/qKerKe | https://quick-bench.com/q/Pt3-lzZOU6FodyGLqAPjHHN48Fk

auto parse(const auto& arr) {
  auto it = std::find_if(std::cbegin(arr), std::cend(arr),
                         [](char c) { return c >= '0' && c <= '9'; });
  int i{};
  std::from_chars(it, std::cend(arr), i);
  return i;
};

auto msg_value(const auto& msg) {
  return parse(msg.price1);
}

auto msg_value(const add3& msg) {
  return parse(msg.price1) + parse(msg.price2);
}

template <class... TMsgTraits>
auto sum_prices = [sum = 0](std::string_view buffer) mutable {
  if (std::size(buffer) < sizeof(header)) {
    return 0;
  }
  const auto id = reinterpret_cast<const header*>(std::data(buffer))->id;
  ([&] <auto Id, class T> (msg_traits<Id, T>) {
    if (std::size(buffer) < sizeof(T)) {
      return false;
    }
    if (Id == id) {
      sum += msg_value(*reinterpret_cast<const T*>(std::data(buffer)));
      return true;
    }
    return false;
  }(TMsgTraits{}) or ...);
  return sum;
};

https://godbolt.org/z/a1hhG6

namespace detail {
static constexpr auto DEFAULT_RETURN = 0;

constexpr auto from_chars_wrapper(const auto iterable)
{
    int output {};
    const auto first_num = std::find(std::crbegin(iterable), std::crend(iterable), ' ').base();
    (void)std::from_chars(first_num, std::cend(iterable), output);
    return output;
}

template <class TMsgTraits>
constexpr auto calculate_price(const char* buffer)
{
    using message_t = TMsgTraits::type;

    if (const auto id = reinterpret_cast<const header*>(buffer)->id; id != TMsgTraits::id) {
        return DEFAULT_RETURN;
    }

    const message_t* message = reinterpret_cast<const message_t*>(buffer);

    int output {};
    if constexpr (requires { message_t::price1; }) {
        output += from_chars_wrapper(message->price1);
    }
    if constexpr (requires { message_t::price2; }) {
        output += from_chars_wrapper(message->price2);
    }
    return output;
};
}

template <class... TAllMsgTraits>
auto sum_prices = [sum = 0](std::string_view buffer) mutable {
    if (std::size(buffer) < sizeof(header)) {
        return detail::DEFAULT_RETURN;
    }

    sum += (detail::calculate_price<TAllMsgTraits>(std::data(buffer)) + ... + 0);
    return sum;
};

https://godbolt.org/z/75f6o9c

template <class F>
constexpr decltype(auto) apply_prices(F&& f, const add1& a) { return std::forward<F>(f)(a.price1); }
template <class F>
constexpr decltype(auto) apply_prices(F&& f, const add2& a) { return std::forward<F>(f)(a.price1); }
template <class F>
constexpr decltype(auto) apply_prices(F&& f, const add3& a) { return std::forward<F>(f)(a.price1, a.price2); }

template<class... Ts>
auto sum_prices = [sum = 0](const std::string_view& buffer) mutable {
  constexpr auto Ns = sizeof...(Ts);

  if constexpr (Ns > 0) {
    const auto id = reinterpret_cast<const short*>(buffer.data());

    if (sizeof(*id) <= buffer.size()) {
      constexpr std::array<std::size_t, Ns> ids{ Ts::id... };
      constexpr std::array<int(*)(const void*), Ns> acc{
        [](const void* ptr) {
          return apply_prices([](const auto&... prices) {
            return (... + std::accumulate(
              prices.begin(), prices.end(), 0, [](int s, char c) {
                if (c == ' ') return s;
                return s * 10 + (c - '0');
              })
            );
          }, *reinterpret_cast<const Ts::type*>(ptr));
        }...
      };

      const auto begin = ids.begin();
      const auto end   = ids.end();
      const auto it    = std::find(begin, end, *id);

      if (it != end) {
        sum += acc[it - begin](id);
      }
    }
  }

  return sum;
};

https://godbolt.org/z/drnqTa

auto array_to_price(const auto &array){
    auto text = std::string{std::data(array), std::size(array)};
    auto pos = text.find_first_not_of(' ');
    if(std::string::npos == pos){
        return 0;
    }

    return std::stoi(text.substr(pos));
}
template<class Trait>
auto sum_one_price = [](std::string_view buffer)  {
    using my_type = Trait::type;

    if (std::empty(buffer)) { return 0; }

    const auto& msg = *reinterpret_cast<const my_type*>(std::data(buffer));
    if (msg.h.id == Trait::id) {
        const auto price1 = array_to_price(msg.price1);
        if constexpr (requires{msg.price2;}){
            return price1 + array_to_price(msg.price2);
        }
        else{
            return price1;
        }
    }

    return 0;
};

template<class... Traits>
auto sum_prices =[sum = 0] (std::string_view buffer) mutable {
    if constexpr (sizeof...(Traits) ==0 )
        return 0;

    else {
        sum +=(sum_one_price<Traits>(buffer)+...);
        return sum;
    }
};

https://godbolt.org/z/7Wssz7

template<auto N>
int parse_number(const std::array<char, N>& arr) {
    int num = 0;
    for(uint32_t n=0; n<N; n++)
        if(arr[n] != ' ')
            num = 10*num + arr[n] - '0';
    return num;
};

template<class T> int totalPrices(const T& t) { return parse_number(t.price1); }
template<> int totalPrices<add3>(const add3& t) { return parse_number(t.price1) + parse_number(t.price2); }

template<class> struct process_msg;
template<auto Id, class T>
struct process_msg<msg_traits<Id, T>> {
    process_msg(std::string_view buffer) : buffer(buffer) {}
    int operator()() {
        const T* msg = reinterpret_cast<const T*>(buffer.data());
        if( msg->h.id == Id)
            return totalPrices(*msg);
        else
            return 0;
    }
    std::string_view buffer;
};

template<class...Msgs>
auto sum_prices = [sums = 0](std::string_view buffer) mutable  {
    if(buffer.size() ==0)
        return sums;
    if constexpr(sizeof...(Msgs) ==0) {
        return sums;
    } else {
        return sums += (process_msg<Msgs>(buffer)() + ...);
    }
};

https://godbolt.org/z/Yn3zP6

template<class...Ts>
auto sum_prices = [ price = 0](std::string_view buffer) mutable {
    if constexpr ( sizeof ...(Ts) == 0 ) return price;
    if ( buffer.size() >= sizeof(header)  )
    {
        header hd = *(header*)buffer.data();
        ([&](){ if( hd.id == Ts::id )
                {
                    std::stringstream ss;
                    ss << buffer;
                    ss.seekg(sizeof(header));
                    int p = 0;
                    while( ss.good() )
                    {
                        ss >> p;
                        price +=p;
                    }
                }
            }(),...);
    }
    return price;
};

https://godbolt.org/z/d1aezc

template<std::size_t N, typename Seq> struct offset_sequence;
template<std::size_t N, std::size_t... Ints>
struct offset_sequence<N, std::index_sequence<Ints...>> {
    using type = std::index_sequence<Ints + N...>;
};
template<std::size_t N, typename Seq>
using offset_sequence_t = typename offset_sequence<N, Seq>::type;

template<auto N>
int constexpr parse_number(const std::array<char, N>& arr) {
    int num = 0;
    for(uint32_t n=0; n<N; n++)
        if(arr[n] != ' ')
            num = 10*num + arr[n] - '0';
    return num;
};

template<class T> int constexpr totalPrices(const T& t) {
    auto tup = to_tuple(t);
    return [tup]<std::size_t... Is>(std::index_sequence<Is...>) { return (parse_number(std::get<Is>(tup)) + ...); }
    ( offset_sequence_t<1, std::make_index_sequence<std::tuple_size_v<decltype(tup)>-1>>{}   );
}


template<class> struct process_msg;
template<auto Id, class T>
struct process_msg<msg_traits<Id, T>> {
    process_msg(std::string_view buffer) : buffer(buffer) {}
    int operator()() {
        if(buffer.size() ==0)
            return 0;
        const T* msg = reinterpret_cast<const T*>(buffer.data());
        if( msg->h.id == Id)
            return totalPrices(*msg);
        else
            return 0;
    }
    std::string_view buffer;
};

template<class...Msgs>
auto sum_prices = [sums = 0](std::string_view buffer) mutable  {
    if constexpr(sizeof...(Msgs) ==0) {
        return sums;
    } else {
        return sums += (process_msg<Msgs>(buffer)() + ...);
    }
};

https://godbolt.org/z/c9Y5oM

static inline auto skip_spaces(auto & begin) {
    while(*begin == ' ')
        ++begin;
}

template<class TMsg>
auto dispatch(std::string_view buffer)
{
    auto id = reinterpret_cast<const short *>(buffer.data());
    bool match = TMsg::id == *id;
    if (match)
    {
        using msg_type = typename TMsg::type;
        auto msg = reinterpret_cast<const msg_type*>(buffer.data());
        auto sum = 0;
        if constexpr (requires { msg->price1; }) {
            auto begin = msg->price1.begin();
            skip_spaces(begin);
            if( begin != msg->price1.end())
                std::from_chars(begin, msg->price1.end(), sum);
        }
        if constexpr (requires { msg->price2; }) {
            int price2;
            auto begin = msg->price2.begin();
            skip_spaces(begin);
            if( begin != msg->price2.end())
            {
                std::from_chars(begin, msg->price2.end(), price2);
                sum += price2;
            }
        }
        return sum;
    }
    return 0;
}

template<class...TMsgTrait>
auto sum_prices = [sum = 0](std::string_view buffer) mutable {
    if (std::size(buffer) == 0)
        return 0;
    sum += (dispatch<TMsgTrait>(buffer) + ... + 0);
    return sum;
};

https://godbolt.org/z/64s4M3

constexpr auto value_of = [](const auto& data) {
    const auto start = std::find_if(std::cbegin(data), std::cend(data),
                                    [](const auto c) { return std::isdigit(static_cast<int>(c)); });
    std::size_t value{};
    // Assume well-formedness of input prices. In principle, should check
    // the error code returned, but handling the error case makes this more expensive
    // than it already is...
    std::from_chars(start, std::cend(data), value);
    return value;
};

template<class TMsg>
concept msg_with_price = requires(TMsg m) { m.price1; };

template<msg_with_price TMsg>
constexpr auto get_price(const TMsg& msg) {
    if constexpr (requires{ msg.price2; }) {
        return value_of(msg.price1) + value_of(msg.price2);
    }
    return value_of(msg.price1);
}

template<class... TMsgs>
auto sum_prices = [sum = std::size_t{}](std::string_view buffer) mutable -> std::size_t {
  if(std::size(buffer) < sizeof(header)) {
      return sum;
  }

  const auto id = reinterpret_cast<const header*>(std::data(buffer))->id;
  ([&]<auto Id, class T>(msg_traits<Id, T> msg) {
      if (std::size(buffer) < sizeof(T)) {
          return false;
      }

      if (Id == id) {
          sum += get_price(*reinterpret_cast<const T*>(std::data(buffer)));
          return true;
      }
    return false;
  }(TMsgs{}) or ...);

  return sum;
};

https://godbolt.org/z/5TYbq5