2015-05-19 24 views
1

Can templates be used to access struct variables by name?有一個模板函數的例子允許設置給定結構的任意成員。 簡化示例:可以使用C++中的模板名稱來訪問Bitfield成員嗎?

#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); 

編輯:(我的use_case) 我寫一種驅動程序與只讀的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傳輸時可能會出現錯誤,因此我想複製寄存器結構,更改一個或多個值,將其發送到設備,並且如果傳輸完成,使用更改後的副本覆蓋寄存器結構。

+0

你試過了嗎?編譯器說了什麼? – luk32

+3

對於這種繁瑣的價值設置(除了學術),用例是什麼? –

+1

同意Dieter。你可以在你感興趣的位域中實現一個帶有偏移量的「uberpointer」,但是我強烈建議不要這樣做(此外它在C中是**不可移植的)。這看起來像一個XY問題或純學術問題。 –

回答

3

首先,改變你的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花式體操上述允許set({construct, the, value, here})語法。我沒有爲SETTER宏做同樣的事情,但我可能可以做更多的工作。

相關問題