tags:

views:

155

answers:

6

So I can't use initializers in my class constructor because of using arrays, so I decided to use an init() method instead. Now I have a different problem. I have a class like this:

class EPWM {
private:
   volatile EPWM_REGS* const regs;
public:
   void init(volatile EPWM_REGS* _regs);
};

where I need to implement init() by initializing regs = _regs; but I can't because of the const. Is there a way to force the assignment in my init method? I would like to keep the const keyword so I don't accidentally reassign elsewhere.

edit: as much as I would like to use a constructor + initializer, which would solve this problem (my code used to do this), I cannot because I have another class which has an array of EPWM objects, and I can't initialize those objects because C++ does not support initializers for array members. (again, see the other question I asked a little while ago on this subject.)

Context for using EPWM is something like this:

class PwmGroup {
private:
   EPWM *epwm;

   void init(EPWM *_epwm) { epwm = _epwm; }
};

/* ... */
// main code:

EPWM epwm[3];
PwmGroup pwmGroup;

{
   // EPwm1Regs, EPwm2Regs, EPwm3Regs are structs
   // defined by TI's include files for this processor
   epwm[0].init(&EPwm1Regs);
   epwm[1].init(&EPwm2Regs);
   epwm[2].init(&EPwm3Regs);
   pwmGroup.init(epwm);
}
+4  A: 
Adam Wright
This sounds like one of those cases. How about "*(const_cast<EPWM_REGS**>(" ?
Jason S
(or "*(const_cast<volatile EPWM_REGS**>(" in my case, which seems to compile correctly.)
Jason S
This is also one of the cases where const_cast isn't guaranteed to work, since the thing you're casting const away from is actually const (rather than having const added in for example a method paramter).
Mark B
A: 

Can't you use a reference instead ?

Seb
I can't use anything that requires initialization at construction time. Read my edit closely.
Jason S
A: 

Use a constructor like this:

EPWM::EPWM(volatile EPWM_REGS* _regs)
    : regs(_regs)
{}

Then simply have no params in init:

void EPWM::init()
{
    // do something with this->regs here...
}

In other words, you can initialise everything in the class constructor - just not member arrays.

AshleysBrain
I can't. Read my edit closely.
Jason S
+1  A: 

How about the following?

struct EPWM_array {
  EPWM_array() { /* initialize array */ }
  const EPWM *begin() const;
  const EPWM *end() const;

  EPWM array[ 10 ];
};

struct EPWMWrapper {  
   volatile EPWM_REGS* const regs;
   EPWMWrapper(EPWM_array const& a) : regs(a.begin()) {}
};
dirkgently
Is EPWM_array application code? Let's talk about partitioning: I want to define EPWM and PwmGroup as library code that I can get to work and keep it unchanged. In my application code, I want to configure instances of PwmGroup / EPWM[3] with particular values of (EPWM_REGS *) that may change from one application to another, depending on how my application uses this particular part's peripherals. If I understand your example, EPWM_array() handles the details of initialization in its constructor. Unfortunately to partition the way I want, I think I'd have to use virtual subclasses :-/
Jason S
Hmm.... or maybe not. Is there a way to rewrite EPWM_array so I don't initialize its array members at construction time, but rather at a later point? this is frustrating....
Jason S
@Jason S: Sure why not? Make sure you do not instantiate the `EPWMWrapper` before you have the `EPWM_array` instantiated. I am not sure if you need virtual subclasses, but a subclassing may be required. Is there a problem with using inheritance? The way I see it, your `EPWMWrapper` design really should be geared towards an interface, if you want your users to provide the `EPWM_array` instances.
dirkgently
thanks... C++ interfaces + lower-end embedded processors do not play so well together (requires vtables for virtual functions, maybe that's not such a bad thing but I tend to stay away from them in the embedded world).
Jason S
I know about quirks of g++ on VxWorks. At the end of the day, it depends on your platform/compiler. However, I strongly suggest you try this out and see if you run into issues.
dirkgently
+1  A: 

Would something like this help? You can still intentionally violate the constness but it prevents normal people from silly mistakes (I haven't compiled this).

class EPWM {
private:
   volatile EPWM_REGS* regs_for_init_never_use;
   volatile EPWM_REGS* const& regs;
public:
   EPWM() : regs(regs_for_init_never_use)
   void init(volatile EPWM_REGS* _regs);
};
Mark B
Interesting.... I had to look at it a few times to figure out that you're initializing the const reference as an alias to the other pointer.
Jason S
+1  A: 

Playing devil's advocate: apart from the obvious documentation intent, since it's a private attribute, you could perfectly not use the const keyword and not modify it apart from the init method.

Your const_cast might actually be undefined behavior here, and I certainly prefer not to run in those dark corners, whatever the workarounds.

class EPWM {
private:
   volatile EPWM_REGS* regs; // normally const, but need to be inited :/
public:
   void init(volatile EPWM_REGS* _regs);
};

Although, revisit your question: while a raw array cannot be default constructed, you can write an array class that can be.

namespace detail
{
  template <class T, size_t N, size_t index>
  struct At
  {
    static T& Do(Array<T,N>& array)
    {
      return At<T,N-1,index-1>::Do(array.tail());
    }
  };

  template <class T, size_t N>
  struct At<T,N,0>
  {
    static T& Do(Array<T,N>& array) { return array[0]; }
  };

  template <class T, size_t index>
  struct At<T,0,index> {};

  template <class T>
  struct At<T,0,0> {};
} // namespace detail


template <class T, size_t N>
class array
{
public:
  typedef T value_type;
  static const size_t Length = N;

  array(): mHead(), mTail() {}
  array(const array& rhs): mHead(rhs.mHead), mTail(rhs.mTail) {}

  // Don't know whether it will be optimized or not
  // Not sure I can use pointer arithmetic either :p
  T& operator[](size_t index) { return index == 0 ? mHead : mTail[index-1]; }

  // Compile time access
  template <size_t index>
  T& at() { return detail::At< T, N, index >::Do(*this); }

private:
  T mHead;
  array<T, N-1> mTail;
}; // class array<T,N>

template <class T>
class array<T,1>
{
public:
  typedef T value_type;
  static const size_t Length = 1;

  array(): mHead() {}
  array(const array& rhs): mHead(rhs.mHead) {}

  T& operator[](size_t index) { return mHead; } // or error handling ;)

private:
  T mHead;
}; // class array<T,1>

template <class T> class array<T,0> {}; // int[0] does not work (stack) so...

Okay... perhaps not as efficient as a real array... you can always turn to Preprocessor generation though:

template <class T>
class Array4
{
public:
  Array4(): m0(), m1(), m2(), m3() {}
  Array4(const Array4& rhs): m0(rhs.m0), m1(rhs.m1), m2(rhs.m2), m3(rhs.m3) {}

  T& operator[](size_t index) { return *(&m0 + index); }

private:
  T m0;
  T m1;
  T m2;
  T m3;
}; // class Array4<T>
Matthieu M.