c++ - How to make this "template / constexpr" construct more elegant / less verbose? -
i have pseudo bitfield implementation:
class field { public: constexpr field(int i, int s) : index(i), size(s) {} constexpr field(const field & prev, int s) : index(prev.index + prev.size), size(s) {} int index, size; }; #define field(name, i, s) constexpr static const field name = {i, s}; template<typename t = quint32> class flags { public: flags(t d = 0) : data(d) {} inline t readfield(const field & f) { return (data & getmask(f.index, f.size)) >> f.index; } inline void writefield(const field & f, t val) { data = (data & setmask(f.index, f.size)) | (val << f.index); } private: static constexpr t getmask(int i, int size) { return ((1 << size) - 1) << i; } static constexpr t setmask(int pos, int size) { return ~getmask(pos, size); } t data; };
however, quite verbose use in present form:
struct test { flags<> flags; field(one, 0, 1) field(two, one, 2) }; test t; t.flags.readfield(t.one); t.flags.writefield(t.one, 1);
i make more elegant, instead of syntax above can this:
t.one.read(); t.one.write(1);
the way tried have flags &
each field
, implement read()
, write()
methods use flags
targets internally.
this requires field
made template well, increased verbosity further, t
has specified fields well.
i tried having t
specified implicitly using flags<t>::makefield()
became mess of incompatibility between constexprt
, static
, regular members , methods, auto
, whatnot, after going in circles decided seek advice people more experience.
naturally, there requirement fields
not take runtime storage , many possible expressions resolved during compile.
having absolutely no idea intent is, first suggestion use bitfield. it's thousand times simpler/faster/etc.
struct test { unsigned long long 1 : 1; unsigned long long 1 : 2; };
however, if want class, made fieldreference class appears vaguely match you're doing.
class:
#include <cassert> #include <type_traits> #include <cstddef> template<class t, size_t offset_, size_t size_> struct fieldreference { static const size_t offset = offset_; static const size_t size = size_; static const size_t mask = ~t(((~0)<<offset<<size)|((1<<offset)-1)); explicit fieldreference(t& f) :flags(&f) {} operator t() const {return (flags[0]&mask)>>offset;} fieldreference& operator=(t v) { assert((v&~(mask>>offset))==0); flags[0] &= ~mask; flags[0] |= (v<<offset); return *this; } private: t* flags; }; #define firstfield(flags,name,size) \ auto name() -> fieldreference<decltype(flags),0,size> {return fieldreference<decltype(flags),0,size>(flags);} \ fieldreference<std::add_const<decltype(flags)>::type,0,size> name() const {return fieldreference<std::add_const<decltype(flags)>::type,0,size>(flags);} #define field(flags,name,previous,size) \ auto name() -> fieldreference<decltype(flags),decltype(previous())::offset,size> {return fieldreference<decltype(flags),decltype(previous())::offset,size>(flags);} \ auto name() const -> fieldreference<std::add_const<decltype(flags)>::type,decltype(this->previous())::offset,size> {return fieldreference<std::add_const<decltype(flags)>::type,decltype(previous())::offset,size>(flags);}
usage:
struct test { unsigned long long flags = 0; firstfield(flags,one,1); field(flags,two,one,2); }; #include <iostream> int main() { test t; t.one() = 1; //that seems less verbose t.two() = 3; std::cout << t.two(); return 0; }
http://coliru.stacked-crooked.com/a/c027d9829ce05119
the fields don't take space whatsoever except while you're working on them, , take space of single pointer. offsets , sizes , masks calculated @ compile time, should faster code too.
Comments
Post a Comment