简体   繁体   English

可以使用模板在C ++中按名称访问Bitfield成员吗?

[英]Can templates be used to access Bitfield members by name in C++?

In Can templates be used to access struct variables by name? 可以使用模板按名称访问结构变量吗? there is an example of a template function allowing to set arbitrary members of a given struct. 有一个模板函数的示例,它允许设置给定结构的任意成员。 Simplified example: 简化示例:

#include <iostream>
#include <ostream>
#include <string>


template < typename T, typename M, typename V >
void set(T *obj, M mem, V val)
{
    obj->*mem = val;
}


struct Obj1
{
    int a;
    double b;
};

struct Obj2
{
    std::string foo;
    float bar;
};


int main()
{
    Obj1 o1;
    o1.a=10;
    o1.b=10;
    Obj2 o2;
    o2.foo="foobarbaz";
    o2.bar=3.2f;

    std::cout<<"o1.a="<<o1.a<<", o1.b="<<o1.b<<std::endl;
    set(&o1, &Obj1::a, 30);
    std::cout<<"o1.a="<<o1.a<<", o1.b="<<o1.b<<std::endl;


    std::cout<<"o2.foo="<<o2.foo<<", o2.bar="<<o2.bar<<std::endl;
    set(&o2, &Obj2::foo, "example text");
    std::cout<<"o2.foo="<<o2.foo<<", o2.bar="<<o2.bar<<std::endl;

    return 0;
}

This works as expected. 这按预期工作。 However, it will not work for bitfields because no pointer to bitfield members can be taken. 但是,它不适用于位域,因为无法获取指向位域成员的指针。 So, my question is, is there a way to write something like this for a bitfield? 所以,我的问题是,有没有办法为位域编写这样的内容? Say, I want to access the members of: 说,我想访问以下成员:

struct Obj3
{
    unsigned char first:3;
    unsigned char second:5;
};

Regards, 问候,

mox MOX

edit: If I expand the code to 编辑:如果我将代码扩展到

#include <iostream>
#include <ostream>
#include <string>


template < typename T, typename M, typename V >
void set(T *obj, M mem, V val)
{
    obj->*mem = val;
}


struct Obj1
{
    int a;
    double b;
};

struct Obj2
{
    std::string foo;
    float bar;
};

struct Obj3
{
    unsigned char first:3;
    unsigned char second:5;
};



int main()
{
    Obj1 o1;
    o1.a=10;
    o1.b=10;
    Obj2 o2;
    o2.foo="foobarbaz";
    o2.bar=3.2f;

    Obj3 o3;
    o3.first=1;
    o3.second=1;

    std::cout<<"o1.a="<<o1.a<<", o1.b="<<o1.b<<std::endl;
    set(&o1, &Obj1::a, 30);
    std::cout<<"o1.a="<<o1.a<<", o1.b="<<o1.b<<std::endl;


    std::cout<<"o2.foo="<<o2.foo<<", o2.bar="<<o2.bar<<std::endl;
    set(&o2, &Obj2::foo, "example text");
    std::cout<<"o2.foo="<<o2.foo<<", o2.bar="<<o2.bar<<std::endl;

    std::cout<<"o3.first="<<o3.first<<", o1.second="<<o3.second<<std::endl;
    set(&o3, &Obj3::second, 2);
    std::cout<<"o3.first="<<o3.first<<", o1.second="<<o3.second<<std::endl;
    return 0;
}

I get the following error message: 我收到以下错误消息:

$ g++ main.cpp && ./a.out
main.cpp: In function ‘int main()’:
main.cpp:56:18: error: invalid pointer to bit-field ‘Obj3::second’
  set(&o3, &Obj3::second, 2);

Edit: (My use_case) I write sort of a driver for communication with a read-only i2c-device. 编辑:(我的用例)我编写了一种用于与只读i2c设备通信的驱动程序。 The usual, old C way of doing this would require #define-ing the register addresses, the offsets of each bit within the registers and then saving a bunch of uint16_t my_register_whatever values. 通常的老式C方法需要#定义寄存器地址,寄存器中每个位的偏移量,然后保存一堆uint16_t my_register_whatever值。 Access would be done by bitwise operations. 访问将通过按位操作完成。 I wanted to try a more C++-like approach: 我想尝试一种更像C ++的方法:

struct __attribute__((__packed__))
{
    unsigned char address : 7;
    unsigned char DAC : 4;
    unsigned char ADC : 1;
    unsigned char MIC : 3;
    unsigned char LINE : 1;
} power_down_control;

This would add type-safety and a much cleaner, more readable syntax. 这将增加类型安全性,并使语法更简洁,可读性更好。 Now, since my I²C-device will not allow reading and there can be errors when doing an I²C-transfer, I want to copy the register struct, change one or more values, send it to the device and if the transfer went all right, overwrite the register struct with the changed copy. 现在,由于我的I²C设备将不允许读取,并且在进行I²C传输时可能会出现错误,因此我想复制寄存器结构,更改一个或多个值,将其发送到设备,如果传输正常,用更改后的副本覆盖寄存器结构。

First, change your set to be: 首先,将您的set更改为:

template<class M>
struct member_type;
template<class T, class X>
struct member_type<T::*X> {
  using type=X;
};
template<class M>
using member_t = typename member_type<M>::type;

template<class T, class M>
struct set_t {
  T* t;
  M* m;
  template<class V>
  auto operator()(V&& v)const
  -> decltype(
    (std::declval<T* const&>(t)->*std::declval<M* const&>())
    =std::declval<V>()
  ) {
    return (t->*m) = std::forward<V>(v);
  }
  member_t<M*>& operator()(member_t<M*>&& v)const{
    return (t->*m) = std::move(v);
  }
};
template<class T, class M>
set_t<T,M> set(T* t, M* m) { return {t, m}; }

and use becomes: 使用成为:

auto setter = set(&o2, &Obj2::foo);
setter("example text");

or 要么

set(&o2, &Obj2::foo)("example text");

on one line. 在一行上。

The idea is that we split the creating of the setting object from the setting. 这个想法是我们将设置对象的创建与设置分开。

Then we can create specific setting objects for a given field. 然后,我们可以为给定字段创建特定的设置对象。

#define SETTER(M) \
  [&](auto* t){
    return [t,&](auto&& v) {
      return (t->M) = decltype(v)(v);
    };
  }

used like: 像这样使用:

auto setter = SETTER(second)(&o3);
setter(2);

or 要么

SETTER(second)(&o3)(2);

in short. 简而言之。


As an aside, the fancy gymnastics involving member_t above allow set({construct, the, value, here}) syntax. member_t上面的member_t涉及的花式体操允许使用set({construct, the, value, here})语法。 I didn't do the same for the SETTER macro, but I probably could with more work. 我没有对SETTER宏做同样的事情,但是我可能可以做更多的工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM