2010-03-09 11 views
6

Así que yo can't use initializers in my class constructor debido al uso de matrices, así que decidí usar un método init() en su lugar. Ahora tengo un problema diferente. Tengo una clase como esta:consiguiendo const en un método init

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

donde tengo que poner en práctica init() inicializando regs = _regs; pero no puede debido a la const. ¿Hay alguna manera de forzar la asignación en mi método init? Me gustaría conservar la palabra clave const para no reasignarla accidentalmente a otra parte.

edición: tanto como me gustaría utilizar un constructor + inicializador, lo que resolvería este problema (mi código utilizado para hacer esto), no puedo porque tengo otra clase que tiene una matriz de objetos EPWM, y no puedo inicializar esos objetos porque C++ no admite los inicializadores para los miembros de la matriz. (De nuevo, ver the other question I asked a little while ago on this subject.)

Contexto para el uso de EPWM es algo como esto:

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

Por qué no inicializar el 'const miembro de regs' en el ctor? ¿Depende de alguna otra matriz para la inicialización? – dirkgently

+0

Tengo otra clase que tiene una matriz de objetos EPWM que no puedo inicializar en el constructor porque C++ no permite tal cosa. –

+0

Me sale tu problema. Los yesos siempre me hacen pensar dos veces sobre el diseño. – dirkgently

Respuesta

4

Usted podría considerar const_cast y punteros, pero es algo que utiliza mejor muy raramente. Algo así como ...

EPWM_REGS** regsPP = const_cast<EPWM_REGS**>(&regs); 
*regsPP = _regs; 
+0

Esto suena como uno de esos casos. ¿Qué tal "* (const_cast (& regs)) = _regs;" ? –

+0

(o "* (const_cast (& regs)) = _regs;" en mi caso, que parece compilarse correctamente.) –

+3

Este es también uno de los casos donde no se garantiza que funcione const_cast, ya que lo que estás lanzando const lejos es en realidad const (en lugar de tener const añadido en, por ejemplo, un parámetro de método). –

0

Utilice un constructor de esta manera:

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

Después, simplemente no tienen params en init:

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

En otras palabras, se puede inicializar todo en el constructor de clase - simplemente no matrices miembros.

+2

No puedo. Lee mi edición de cerca. –

1

¿Qué tal lo siguiente?

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

¿Es el código de la aplicación EPWM_array? Hablemos de particionamiento: quiero definir EPWM y PwmGroup como código de biblioteca que puedo trabajar y mantener sin cambios. En mi código de aplicación, deseo configurar instancias de PwmGroup/EPWM [3] con valores particulares de (EPWM_REGS *) que pueden cambiar de una aplicación a otra, dependiendo de cómo mi aplicación use los periféricos de esta parte en particular. Si entiendo tu ejemplo, EPWM_array() maneja los detalles de la inicialización en su constructor. Desafortunadamente para dividir de la forma que quiero, creo que tendré que usar subclases virtuales: -/ –

+0

Hmm .... o tal vez no. ¿Hay alguna manera de reescribir EPWM_array para que no inicialice sus miembros de matriz en el momento de la construcción, sino en un momento posterior? esto es frustrante ... –

+0

@Jason S: ¿Por qué no? Asegúrese de no crear una instancia del 'EPWMWrapper' antes de tener el' EPWM_array' instanciado. No estoy seguro si necesita subclases virtuales, pero puede ser necesaria una subclase. ¿Hay algún problema con el uso de la herencia? Tal como lo veo, su diseño 'EPWMWrapper' realmente debería estar orientado a una interfaz, si desea que sus usuarios proporcionen las instancias' EPWM_array'. – dirkgently

1

¿Le sirve algo así? Todavía puede violar intencionalmente la constness pero evita que la gente normal cometa errores tontos (no lo he compilado).

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

Interesante .... Tuve que mirarlo unas cuantas veces para descubrir que está inicializando la referencia constante como un alias del otro puntero. –

1

haciendo de abogado del diablo: además de la documentación intención obvia, ya que es un atributo privado, podría perfectamente no utilizar la palabra clave const y no modificarlo aparte del método init.

Su const_cast podría ser un comportamiento indefinido aquí, y ciertamente prefiero no correr en esas esquinas oscuras, cualesquiera que sean las soluciones.

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

Aunque, volver a su pregunta: mientras que una serie raw no puede ser construido default, puede escribir una clase de matriz que puede ser.

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... 

bien ... tal vez no sea tan eficiente como una matriz de verdad ... siempre se puede recurrir a la generación de preprocesador sin embargo:

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> 
Cuestiones relacionadas