如何使这个"template/constexpr"构造更优雅/更简洁?

5 c++ static templates constexpr auto

我有这个伪位域实现:

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;
};
Run Code Online (Sandbox Code Playgroud)

但是,以目前的形式使用它是非常冗长的:

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);
Run Code Online (Sandbox Code Playgroud)

我想让它更优雅,所以不是上面的语法,我可以简单地这样做:

t.one.read();
t.one.write(1);
Run Code Online (Sandbox Code Playgroud)

我尝试这样做的方法是Flags &为每个Field实现read()write()方法在Flags内部使用它的目标.

然而,这需要将Field模板制作成模板,这进一步增加了详细程度,现在T也必须为字段指定.

我尝试T使用a隐式指定,Flags<T>::makeField()但它很快变得混乱constexprt,static常规成员和方法之间的不兼容,auto以及诸如此类,所以进入圈子后最终决定寻求更多经验的人的建议.

当然,要求Fields不占用运行时存储,并且在编译期间解析尽可能多的表达式.

qea*_*adz 1

看起来没有其他人在咬人,所以我只提一下我想到的两种方法。

我认为这里的关键是一个能够修改特定值而不占用任何存储空间的字段。因此,实现这一目标的突出语言功能是:

匿名联合,提供test.one.read()类型的语法。

空基优化将提供test.one().read()类型的语法。

前者的一个快速示例,没有实际的位逻辑 - 此示例中的所有字段仅修改整个值。按位逻辑很简单:

template< typename T >
struct Bitmask
{
    T m_val;
};

template< typename BITMASK, int INDEX >
struct Field : private BITMASK
{
    int read() const { return BITMASK::m_val; }
    void write( int i ) { BITMASK::m_val = i; }
};

struct Test
{
    typedef Bitmask<int> Flags;
    union
    {
        Flags m_flags;
        Field<Flags,0> one;
        Field<Flags,1> two;
        Field<Flags,2> three;
    };
};
Run Code Online (Sandbox Code Playgroud)

这满足您的特定用法,但需要注意的是该字段也是模板化的。顺便说一句,我真的认为无论做什么,它实际上应该是test.m_flags.one.read()或类似的,因为如果这些位是唯一但通用命名的,那么这允许任何具有 Flags 实例的类有多个其中没有问题。

我尚未模拟的函数的空基优化,但该函数将返回一个访问器对象 - 与示例中的 Field 非常相似,但所需的“Flags&”参数已被绑定。

空基也可能需要单一继承和一些转换。从好的方面来说,我认为它可以精确支持位掩码中的位数。因此,如果需要 3 位,它可能会存储为无符号字符,但只会出现函数 one()、two() 和 Three()。

如果你愿意,并且如果我有时间,我也可以模拟这个例子。

据我所知,这两种技术都应该有效,所以我有兴趣知道它们是否不可移植,如果是,原因是什么。

编辑:快速阅读 cppreference 关于联合的部分表明标准不支持从联合的非活动成员读取。然而主要的编译器都支持它。因此,这种方法存在一个问题。