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);
}
Community
  • 1
  • 1
Jason S
  • 184,598
  • 164
  • 608
  • 970
  • Why not init the const `regs` member in the ctor? Does it depend on some other array for initialization? – dirkgently Mar 09 '10 at 15:38
  • I have another class that has an array of EPWM objects that I can't initialize in the constructor because C++ doesn't permit such a thing. – Jason S Mar 09 '10 at 15:42
  • I get your problem. Casts always make me think twice about the design. – dirkgently Mar 09 '10 at 15:57
  • Heh. I've now thought maybe 5 or 6 times about the design. :-) Each time I think I find a better way, I always find something that comes back to the problem of trying to initialize an array of objects. – Jason S Mar 09 '10 at 16:02
  • @Jason S: See my answer. Does that help? – dirkgently Mar 09 '10 at 16:03
  • @dirkgently: not sure... The const_cast<> is the simplest approach, I just need to get it working for now, and then I have a couple of ideas to digest to see if there's a more elegant way. – Jason S Mar 09 '10 at 16:20
  • @Jason S: The problem with the cast is it may not work. Why not work on the design instead? – dirkgently Mar 09 '10 at 16:37

5 Answers5

4

You could consider const_cast and pointers, but it's something best used very rarely. Something like...

EPWM_REGS** regsPP = const_cast<EPWM_REGS**>(&regs);
*regsPP = _regs;
Adam Wright
  • 48,938
  • 12
  • 131
  • 152
  • This sounds like one of those cases. How about "*(const_cast(&regs)) = _regs;" ? – Jason S Mar 09 '10 at 15:43
  • (or "*(const_cast(&regs)) = _regs;" in my case, which seems to compile correctly.) – Jason S Mar 09 '10 at 15:44
  • 3
    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 Mar 09 '10 at 16:10
1

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
  • 108,024
  • 16
  • 131
  • 187
  • 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 Mar 09 '10 at 17:02
  • 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 Mar 09 '10 at 17:05
  • @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 Mar 09 '10 at 18:16
  • 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 Mar 09 '10 at 19:05
  • 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 Mar 09 '10 at 22:04
1

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
  • 95,107
  • 10
  • 109
  • 188
  • 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 Mar 09 '10 at 16:49
1

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.
  • 287,565
  • 48
  • 449
  • 722
0

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
  • 22,335
  • 15
  • 88
  • 124