[英]Can templates be used to access Bitfield members by name in C++?
可以使用模板按名称访问结构变量吗? 有一个模板函数的示例,它允许设置给定结构的任意成员。 简化示例:
#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;
}
这按预期工作。 但是,它不适用于位域,因为无法获取指向位域成员的指针。 所以,我的问题是,有没有办法为位域编写这样的内容? 说,我想访问以下成员:
struct Obj3
{
unsigned char first:3;
unsigned char second:5;
};
问候,
MOX
编辑:如果我将代码扩展到
#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;
}
我收到以下错误消息:
$ 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);
编辑:(我的用例)我编写了一种用于与只读i2c设备通信的驱动程序。 通常的老式C方法需要#定义寄存器地址,寄存器中每个位的偏移量,然后保存一堆uint16_t my_register_whatever值。 访问将通过按位操作完成。 我想尝试一种更像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;
这将增加类型安全性,并使语法更简洁,可读性更好。 现在,由于我的I²C设备将不允许读取,并且在进行I²C传输时可能会出现错误,因此我想复制寄存器结构,更改一个或多个值,将其发送到设备,如果传输正常,用更改后的副本覆盖寄存器结构。
首先,将您的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}; }
使用成为:
auto setter = set(&o2, &Obj2::foo);
setter("example text");
要么
set(&o2, &Obj2::foo)("example text");
在一行上。
这个想法是我们将设置对象的创建与设置分开。
然后,我们可以为给定字段创建特定的设置对象。
#define SETTER(M) \
[&](auto* t){
return [t,&](auto&& v) {
return (t->M) = decltype(v)(v);
};
}
像这样使用:
auto setter = SETTER(second)(&o3);
setter(2);
要么
SETTER(second)(&o3)(2);
简而言之。
member_t
上面的member_t
涉及的花式体操允许使用set({construct, the, value, here})
语法。 我没有对SETTER
宏做同样的事情,但是我可能可以做更多的工作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.