Jason S
Jason S

Reputation: 189796

getting around const in an init method

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

Upvotes: 6

Views: 656

Answers (5)

Matthieu M.
Matthieu M.

Reputation: 300019

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>

Upvotes: 1

Mark B
Mark B

Reputation: 96281

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

Upvotes: 1

dirkgently
dirkgently

Reputation: 111200

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()) {}
};

Upvotes: 1

AshleysBrain
AshleysBrain

Reputation: 22591

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.

Upvotes: 0

Adam Wright
Adam Wright

Reputation: 49386

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;

Upvotes: 4

Related Questions