Skip to content

Instantly share code, notes, and snippets.

@qookei
Created April 26, 2020 22:50
Show Gist options
  • Save qookei/51af0fcbed9ebcc07d168b2a4adf494e to your computer and use it in GitHub Desktop.
Save qookei/51af0fcbed9ebcc07d168b2a4adf494e to your computer and use it in GitHub Desktop.
#include <iostream>
#include <tuple>
#include <array>
#include <cstring>
namespace detail {
template<typename ...Ts>
struct concat_size;
template<typename ...Ts>
inline constexpr size_t concat_size_v = concat_size<Ts...>::value;
template<typename T, typename ...Ts>
struct concat_size<T, Ts...>
: std::integral_constant<size_t, std::tuple_size_v<T> + concat_size_v<Ts...>> { };
template<>
struct concat_size<>
: std::integral_constant<size_t, 0> { };
template<typename X, size_t N>
constexpr void concat_insert(std::array<X, N> &, size_t) { }
template<typename X, size_t N, typename T, typename... Ts>
constexpr void concat_insert(std::array<X, N> &res, size_t at, const T &other, const Ts &...tail) {
size_t n = std::tuple_size_v<T>;
for(size_t i = 0; i < n; ++i)
res[at + i] = other[i];
concat_insert(res, at + n, tail...);
}
template<typename X, typename... Ts>
constexpr auto array_concat(const Ts &...arrays) {
std::array<X, concat_size_v<Ts...>> res{};
concat_insert(res, 0, arrays...);
return res;
}
} // namespace detail
enum class operation_mode {
in,
out,
both
};
struct operation {
uint8_t *ptr;
uint8_t val;
operation_mode mode;
};
template <typename ...Args>
auto send_bytes(Args &&...args) {
return std::array<operation, sizeof...(Args)>{operation{nullptr, static_cast<uint8_t>(args), operation_mode::out}...};
}
template <typename T, size_t ...p>
auto send_buffer_impl(T *buffer, std::index_sequence<p...>) {
return send_bytes(buffer[p]...);
}
template <size_t N, typename T>
auto send_buffer(T *buffer) {
return send_buffer_impl(buffer, std::make_index_sequence<N>{});
}
template <size_t N, typename T>
auto send_buffer(T (&buffer)[N]) {
return send_buffer_impl(buffer, std::make_index_sequence<N>{});
}
template <typename ...Args>
auto recv_bytes(Args &...args) {
return std::array<operation, sizeof...(Args)>{operation{&args, 0, operation_mode::in}...};
}
template <typename T, size_t ...p>
auto recv_buffer_impl(T *buffer, std::index_sequence<p...>) {
return recv_bytes(buffer[p]...);
}
template <size_t N, typename T>
auto recv_buffer(T *buffer) {
return recv_buffer_impl(buffer, std::make_index_sequence<N>{});
}
template <size_t N, typename T>
auto recv_buffer(T (&buffer)[N]) {
return recv_buffer_impl(buffer, std::make_index_sequence<N>{});
}
template <typename T, size_t ...p>
auto xfer_buffers_impl(T *out, T *in, std::index_sequence<p...>) {
return std::array<operation, sizeof...(p)>{operation{out + p, in[p], operation_mode::both}...};
}
template <size_t N, typename T>
auto xfer_buffers(T *out, T *in) {
return xfer_buffers_impl(out, in, std::make_index_sequence<N>{});
}
template <size_t N, typename T>
auto xfer_buffers(T (&out)[N], T (&in)[N]) {
return xfer_buffers_impl(out, in, std::make_index_sequence<N>{});
}
template <typename Transmitter, typename ...Args>
void do_transmission(Transmitter &transmitter, Args &&...args) {
auto items = detail::array_concat<operation>(std::forward<Args>(args)...);
size_t out_pos = 0, in_pos = 0;
transmitter.select();
while (out_pos < items.size() || in_pos < items.size()) {
while (out_pos < items.size() && transmitter.is_transmit_empty()) {
transmitter.send(items[out_pos++].val);
}
while (in_pos < items.size() && in_pos <= out_pos && transmitter.is_receive_not_empty()) {
auto p = items[in_pos++].ptr;
auto b = transmitter.recv(!p);
if (p) *p = b;
}
}
transmitter.deselect();
}
int main() {
struct dummy_transmitter {
void select() {}
void deselect() {}
bool is_transmit_empty() {
bool b = tx_free;
rx_free++; tx_free--;
return b;
}
bool is_receive_not_empty() {
bool b = rx_free;
rx_free--; tx_free++;
return b;
}
void send(uint8_t value) { printf("send %02x\n", value); n_tx++; }
uint8_t recv(bool discarded) { printf("recv%s\n", discarded ? " discarded" : ""); n_rx++; return 0xAA; }
size_t tx_free;
size_t rx_free;
size_t n_tx;
size_t n_rx;
} the_transmitter{8, 0};
uint8_t out_buf[4];
uint8_t in_buf[4];
memset(out_buf, 0xCC, 4);
uint8_t a, b, c, d;
do_transmission(the_transmitter,
send_bytes(0x11, 0x22, 0x33, 0x44, 0x55, 0x66),
send_buffer(out_buf),
recv_buffer(in_buf),
recv_bytes(a, b, c, d),
xfer_buffers(in_buf, out_buf)
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment