Why you must get it
Organism that transforms coffee and pizza into lines of code
Mentally-diseased organism that transforms lines of code into time to go for a coffee
cmake --build .
class ArrayView
{
ArrayView(QByteArray& array);
ArrayView(const ArrayView& source,
std::size_t begin, std::size_t end);
~ArrayView() = default; // Does nothing
Byte operator[](std::size_t i) const;
Byte& operator[](std::size_t i);
ArrayView operator()(std::size_t begin,
std::size_t end) const;
template<typename T>
T read() const; // Magic goes here
template<typename T>
void write(const T& value); // More magic
private:
QByteArray* _array;
std::size_t _begin, _end;
};
Non-owning view of a slice of an existing array
class ArrayView
{
public:
template<typename T>
T read() const {
QByteArray slice = _array->slice(_begin, _end);
QDataStream stream{_&slice};
T result;
stream >> result;
return result;
}
};
Transparent serialization by calling read()/write()
QByteArray array = readBytes();
ArrayView frame{array};
assert_equals(frame(0,2).read<quint16>(),
FRAME_HEADER);
ArrayView payload = frame(4, 32);
appLayer(payload);
class ArrayView
{
public:
ArrayView(const ArrayView& source,
std::size_t begin, std::size_t end)
: _array{source._array},
_begin{source._begin + begin},
_end{source._begin + end}
{}
ArrayView operator()(std::size_t begin, std::size_t end) const
{
return {*this, begin, end};
}
};
class ArrayView {
...
public:
template<typename T>
class ReaderWriter {
const T& operator=(const T& value) {
_view->write(value);
return value;
}
ArrayView* _view;
};
template<typename T>
ReaderWriter<T> readerWriter()
{
return {this};
}
};
class ViewableArray : public QByteArray, public ArrayView {
public:
ViewableArray(QByteArray& array);
...
};
class Frame : public ViewableArray {
public:
using ViewableArray::ViewableArray;
ReaderWriter<quint16> header()
ReaderWriter<quin32> source_address();
...
};
// Input
Frame frame = readBytes();
assert_equals(frame.header() == FRAME_HEADER);
// Output
Frame frame = buildRequest();
frame.source_address() = Me();
sendBytes(frame); Note sendBytes takes a QByteArray!
auto tuple = make_tuple("pi"_, 3.141592654,
"e"_, 2.71828182846);
std::cout << "Pi: " << tuple["pi"_];
Use strings to name tuple elements
// Jonathan Müller, the constexpr hashing guy
namespace foonathan {
namespace string_id {
namespace detail {
using hash_type = std::uint64_t;
constexpr hash_type fnv_basis = 14695981039346656037ull;
constexpr hash_type fnv_prime = 109951162821ull;
// FNV-1a 64 bit hash
constexpr hash_type sid_hash(const char *str, hash_type hash = fnv_basis) noexcept
{
return *str ? sid_hash(str + 1, (hash ^ *str) * fnv_prime) : hash;
}
}
}
} // foonathan::string_id::detail
Just write a constexpr UDL that computes the hash of the literal
template<const char* str>
using hash = std::integral_constant<
foonathan::string_id::detail::hash_type,
foonathan::string_id::detail::sid_hash(str)
>;
constepr auto operator"" _(const char* str, std::size_t length)
{
return hash<str>{};
}
Just write a constexpr UDL that computes the hash of the literal
#define h(x) std::integral_constant< \
foonathan::string_id::detail::hash_type, \
foonathan::string_id::detail::sid_hash(x) \
>{}
int main()
{
auto t = make_tuple(h("pi"), 3.141592654,
h("e"), 2.71828182846);
std::cout << TUPLE_GET("e")(t) << std::endl;
std::cout << TUPLE_GET("pi")(t) << std::endl;
}
// Not mine, Louis Dionne deserves all merit
namespace detail {
template <typename x>
struct no_decay { using type = x; };
template <typename key, typename value>
struct pair { };
template <typename ...xs>
struct inherit : xs... { };
template <typename key, typename value>
static no_decay<value> lookup(pair<key, value>*);
template <typename key, typename ...pairs>
using at_key = typename
decltype(lookup<key>((inherit<pairs...>*)nullptr))
::type;
}
Use function lookup rules to map from input types to "return" types
template<typename Indices, typename Types>
struct tuple;
template<typename... Indices, typename... Types>
struct tuple<std::tuple<Indices...>, std::tuple<Types...>> :
public std::tuple<Types...>
{
using tuple_t = std::tuple<Types...>;
template<typename key>
using element_index = detail::at_key<key, Indices...>;
template<typename Hash>
const auto& get() const {
return std::get<
element_index<Hash>::value
>(static_cast<const tuple_t&>(*this));
}
template<typename Hash>
const auto& operator[](Hash) const {
return get<Hash>();
}
template<typename... Ts>
tuple(Ts&&... elems) :
tuple_t{ std::forward<Ts>(elems)... } {}
};
Take types and indices, map access to std::tuple
template<typename Hash, typename Type>
struct entry
{
using hash = Hash;
using type = Type;
};
template<typename... Entries>
struct tuple_builder
{
template<typename Seq>
struct build;
template<typename T, T... Seq>
struct build<std::integer_sequence<T, Seq...>>
{
using type = tuple<
std::tuple<detail::pair<
typename Entries::hash,
std::integral_constant<T,Seq>>...
>,
std::tuple<typename Entries::type...>
>;
};
using type = typename build<std::index_sequence_for<Entries...>>::type;
};
template<typename... Entries>
using build_tuple = typename tuple_builder<Entries...>::type;
What's next?