c++ - passing reference pointing to struct as template parameter -


i map structures registers memory. have working code this:

structure registers peripheral:

struct periph {     volatile uint32_t reg1;     volatile uint32_t reg2; }; 

in device peripheral 2 times located on 2 different addresses in memory, define these addresses:

static constexpr size_t periph1_base = 0x40000000; static constexpr size_t periph2_base = 0x40001000; 

then have driver can use of these registers:

template<size_t base> struct driver {     inline periph &r() {         return *reinterpret_cast<periph *>(base);     }     void setfoo(uint32_t x) {         r().reg1 = x;     }     uint32_t getbar() {         return r().reg2;     } }; 

to use driver simple, need set address of peripheral template:

driver<periph1_base> drv; uint32_t x = drv.getbar(); drv.setfoo(x); ... 

if compiler merge inline functions after optimization method works registers , without overhead.

but not safe, because can set driver address different peripheral.

my idea improve put reference structure template argument, without success.

first defined references registers:

static periph &periph1 = *reinterpret_cast<periph *>(periph1_base); static periph &periph2 = *reinterpret_cast<periph *>(periph2_base); 

this working, can directly access these registers like:

periph2.reg1 = 123; 

but have no idea how pass these references template argument, attempt following:

template<periph &r> struct driver {     void setfoo(uint32_t x) {         r.reg1 = x;     }     uint32_t getbar() {         return r.reg2;     } };  driver<periph2> drv; drv.setfoo(x); 

from following error:

`error: value of 'periph2' not usable in constant expression` 

if define periph2 constexpr error:

`error: reinterpret_cast integer pointer` 

... how put reference object template argument? or idea or suggestion make better.

also here exists lot of other solutions (like put reference driver constructor...), slow down access registers.

thanks help.

but have no idea how pass these references template argument

because different things. understood, might you: use peripherals structures singletones incapsulated base addresses.

template<std::size_t base> struct periph {      static constexpr periph volatile& instance() {         return *reinterpret_cast<periph volatile*>(base);     }      template<std::size_t n>     static constexpr std::uint32_t volatile& reg() {         return periph::instance().reg_[n];     }      // prohibit instance constructing     periph() = delete;      periph(periph const&) = delete;     periph(periph&&) = delete;  private:      uint32_t reg_[2]; }; 

you can access registers via instance() or reg<>() methods. example:

periph<0x00001000>::reg<0>() = 0; 

now, make driver's template argument take type not value:

template<typename periph> struct driver {     using periph_type = periph;      // example     void foo() {         periph_type::reg<0>() = 1234;     } }; 

look, periph::instance() reference wanted pass. can see, driver::foo() uses periph-defined static method periph::reg<>() access peripheral instance instead of explicit addresses. looks more safer.

also can discard default template implementation , implement specializations:

using periph1 = periph<0x40000000>; using periph2 = periph<0x40001000>;  template<typename periph> struct driver;  template<> struct driver<periph1> {     // specialization periph1 };  template<> struct driver<periph2> {     // specialization periph2 }; 

or

template<std::size_t base> struct driver< periph<base> > {     // use periph<base> here }; 

for another (not same periph) peripheral should implement another type (for example, i2c<>). probably, in same manner periph<> implemented (with incapsulated address template parameter). if deal multiple same-typed peripherals (for example, multiple can buses) should/might use same type different bases.

update:

also, might want on implementation:

template<std::size_t base> struct periph { private:     struct context {         std::uint32_t reg[2];     };      static constexpr context volatile& ctx() {         return *reinterpret_cast<context volatile*>(base);     }  public:      static volatile std::uint32_t & reg1;     static volatile std::uint32_t & reg2; };  template<std::size_t base> volatile std::uint32_t & periph<base>::reg1 = ctx().reg[0];  template<std::size_t base> volatile std::uint32_t & periph<base>::reg2 = ctx().reg[1]; 

in case address (base) still incapsulated struct periph registers might accessed static members:

periph<0> p; p.reg1 = 1;  periph<0>::reg1 = 0; 

Comments