Created
April 26, 2020 22:50
-
-
Save qookei/51af0fcbed9ebcc07d168b2a4adf494e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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