2010-09-30 19 views
5

Usted tiene una estructura simple, dicen:¿Hay alguna manera de aplicar una función a cada miembro de una estructura en C++?

typedef struct rect 
{ 
    int x; 
    int y; 
    int width; 
    int height; 
} 
rect; 

Y desea multiplicar cada elemento en un factor. ¿Hay una forma más concisa de realizar esta operación, que no sea multiplicar cada miembro por el valor?

+1

Si todos los miembros del mismo tipo, no pueden ponerlos en una matriz ('int members_ [4];'? Y proporcionar accesos ('int rect :: x() const;') y mutadores (' void rect :: x (int); ')? Debe escribir el código en un lugar u otro :) – Arun

+6

Al definir tipos en C++, pierda typedef. –

Respuesta

10
No

realmente. Programativamente obtener una lista de los elementos de una estructura requiere reflexión, que C++ no admite.

Sus dos opciones son simplemente darle a la estructura un método que hace esto de manera prolija y luego usar ese método en todos los demás lugares, o imitar manualmente la reflexión, por ejemplo, dando a la estructura otro elemento que es un una matriz de punteros a todos sus otros elementos (construidos dentro del constructor de la estructura), y luego volteando sobre eso para realizar la función de escalado.

+1

¿Qué hay de los sindicatos? En particular, el uso de Microsoft de ellos en D3DXMATRIX y D3DXVECTOR. –

+2

Eso es realmente lo mismo que la solución de casting que otros publicaron, pero con una sintaxis más agradable. De manera similar depende del comportamiento de un compilador particular y el estándar C++ no garantiza que funcione. –

+1

No es una lista exhaustiva de opciones. Puede usar punteros de tipo ** puntero a miembro **, como se sugiere en mi respuesta. Esto es similar a su segunda opción, pero en ese caso no tendría que crear una matriz de puntero separada para cada instancia de estructura. En su lugar, utiliza una sola matriz que se aplica a todas las instancias (puede hacer que sea * miembro * estático de la estructura si lo desea). De hecho, * this * es la forma correcta de "imitar manualmente la reflexión", no es lo que sugiere arriba. – AnT

0

si todos sus elementos son del mismo tipo:

for (int i = 0; i < sizeof(rect)/sizeof(rect.x) ; ++i) { 
    do_something(reinterpret_cast<int *>(adress_of_your_struct) + i); 
} 

con do_something esperando un puntero a int

+5

Esto no es portátil porque supone que no hay relleno entre los elementos de la estructura, lo que no está garantizado. –

+0

brevedad del código a costa del tipo de seguridad. aún así, muy conciso. +1 –

+1

@Andrew: la seguridad del tipo debe _se_ nunca debe sacrificarse en aras de la brevedad. –

10

No; no hay forma de iterar sobre los miembros de una estructura.

En este caso particular, sin embargo, usted puede lograr su objetivo mediante el uso de una matriz:

struct rect 
{ 
    // actually store the data in an array, not as distinct elements 
    int values_[4]; 

    // use accessor/mutator functions to modify the values; it is often best to 
    // provide const-qualified overloads as well. 
    int& x()  { return values_[0]; } 
    int& y()  { return values_[1]; } 
    int& width() { return values_[2]; } 
    int& height() { return values_[3]; } 

    void multiply_all_by_two() 
    { 
     for (int i = 0; i < sizeof(values_)/sizeof(values_[0]); ++i) 
      values_[i] *= 2; 
    } 
}; 

(tenga en cuenta que este ejemplo no tiene mucho sentido (¿por qué se multiplique x, y, la altura y ancho por dos?) pero demuestra una forma diferente de resolver este tipo de problema)

+0

Algunas sugerencias, aunque fuera del alcance de la pregunta original: (i) Use una constante simbólica en lugar de '4'. (ii) Acepte el multiplicador ('2' en el ejemplo) como un argumento de entrada. – Arun

0

truco horrible

int *arr = (int*)&my_rect; 
for (int i = 0; i < sizeof(rect)/sizeof(int); i++) 
{ 
    arr[i] *= factor; 
} 
+4

Esto no es portátil porque supone que no hay relleno entre los elementos de la estructura, lo que no está garantizado. –

+0

bien duh, de ahí 'horrible hack'. Funcionará en la mayoría de las máquinas de 32 bits aunque – pm100

5

Si todos los valores de encajar en un vector de 128 bits, entonces usted podría u Se SIMD instrucciones para multiplicarlos todos a la vez. Por ejemplo, las Primitivas de rendimiento integrado (IPP) de Intel tienen primitives para suma, resta, multiplicación, etc. en vectores.

Probablemente sea excesivo a menos que esté haciendo muchas operaciones de cálculo intensivo.

0

Una solución a través de una nueva clase:

  • Crear una clase con una matriz genérica template<class T> class Array, (ver how),
  • rellenar la matriz con sus propiedades, una propiedad como un elemento de la matriz.
  • asegurar que todos los tipos pueden ser multiplicados por un valor numérico (o sobrecargar el operador * para un tipo si es necesario/a crear un método específico para ese objeto)
  • crear un método multiply que realiza un bucle en la matriz y multiplica cada elemento por un valor dado.
0

Si puede volver a organizar su estructura de tomar una unión en su lugar, se puede hacer algo similar a lo que ha hecho Microsoft con DirectX.

union Rect 
{ 
    struct 
    { 
     int x; 
     int y; 
     int width; 
     int height; 
    }; 

    int members[4]; 
}; 

void multiply(Rect& rect, int factor) 
{ 
    for(int i = 0; i < 4; i++) 
    { 
     rect.members[i] *= factor; 
    } 
} 
2

Si todos son el mismo tipo, utilice una unión combinada con una estructura anónima, en el interior de su estructura:

struct MyStruct { 
    union { 
    int data[4]; 
    struct { 
     int x,y,z,w; 
    } 
    }; 
}; 

a continuación:

MyStruct my_struct; 
cout << my_struct.x << my_struct.y << my_struct.z << my_struct.w; 
for (size_t i = 0; i < 4; ++i) 
    some_function(my_struct.data[i]); // access the data via "data" member 
cout << my_struct.x << my_struct.y << my_struct.z << my_struct.w; // voila!, stryct data has changed! 

Si está utilizando diferentes tipos echar un vistazo a boost::fusion

+0

esto es feo y horrible, pero probablemente sea la mejor manera de hacer exactamente lo que el OP solicitó ... +1 – rmeador

10

Boost.Fusion ofrece la macro BOOST_FUSION_ADAPT_STRUCT, que puede convertir cualquier estructura en una secuencia Fusion, que Fusion puede luego iterar (como se demostró en el Quick Start).

Otra opción es utilizar una biblioteca de reflexión C++ de terceros como Reflex para iterar sobre los miembros de una estructura.

Sin embargo, todo esto puede ser excesivo para sus propósitos; enumerar manualmente cada uno de los miembros de la clase puede ser menos elegante, pero es mucho más simple, y la simplicidad en la codificación es importante.

+0

Batirme por 5 minutos ... –

+0

+1 por 'BOOST_FUSION_ADAPT_STRUCT': la forma más rápida de obtener una reflexión en tiempo de compilación. –

2

Como todo el mundo ha dicho, no puede hacerlo directamente, pero no hay nada que nos impida la sobrecarga del operador *:

struct rect 
{ 
    int x; 
    int y; 
    int width; 
    int height; 
} 

rect operator*(const rect& r, int f) 
{ 
    rect ret=r; 
    ret.x*=f; 
    ret.y*=f; 
    ret.width*=f; 
    ret.height*=f; 
    return ret; 
} 

entonces usted puede hacer esto

rect r; 
//assign fields here 
r=r*5; 
+0

Esta es la única solución limpia hasta el momento ... Tuve que eliminar mi respuesta, eras más rápido –

2

Suponiendo que usted es usando un compilador de C++ (no C), conviértalo en una clase y crea una función miembro para hacer esto. Estoy sorprendido de todas las soluciones incómodas presentadas a un problema simple. ¿Qué te compra una solución reflexiva? ¿Continuamente agrega nuevos miembros a esta clase, por lo que desea ahorrarse el paso de modificar la función miembro en el futuro? Si ese es un potencial ahorro de tiempo, huele a una clase que hace demasiado.

Es inevitable que haya una funcionalidad repetida que actúe sobre los miembros de una estructura, ¿por qué no convertirla en una clase desde el principio?

1

Bueno, no directamente. Pero puede lograrlo utilizando un conjunto de punteros "índice" temporales adicionales de tipo puntero a miembro.

Por ejemplo

rect a_rect[100]; 
int factor; 
... 
// We need to multiply all members of all elements of `a_rect` array by `factor` 

// Prepare index array 
int rect::*rect_members[4] = { &rect::x, &rect::y, &rect::width, &rect::height }; 

// And multiply 
for (i = 0; i < 100; ++i) 
    for (j = 0; j < 4; ++j) 
    a_rect[i].*rect_members[j] *= factor; 

Por supuesto, si tiene que hacerlo a menudo, se puede utilizar una matriz de índice permanente rect_members inicializado en el arranque del programa.

Tenga en cuenta que este método no emplea ningún ataque, como lo hacen algunos otros métodos. Los punteros de tipo puntero a miembro rara vez se utilizan, pero esta es en realidad una de las cosas para las que fueron introducidos.

Una vez más, si es necesario hacerlo a menudo, lo más apropiado para hacerlo sería hacer que la matriz de punteros un miembro static const de la estructura

struct rect 
{ 
    int x; 
    int y; 
    int width; 
    int height; 

    static int rect::* const members[4]; 
}; 

int rect::* const rect::members[4] = { &rect::x, &rect::y, &rect::width, &rect::height }; 

y cuando se necesita para iterar sobre estos miembros, se Acabo de acceder a elementos de estructura como s.*rect::members[i].

0

Existen algunos problemas asociados con la herencia de clases estándar; pero si está dispuesto a aceptar esos problemas, puede hacer algo análogo a lo que otros han sugerido, pero usando std::valarray en su lugar. Se vería algo como:

#include <valarray> 

class rect : public std::valarray<int> 
{ 
    public: 
    rect() : std::valarray(4, 0) {} 

    int& x() { return (*this)[0]; } 
    int& y() { return (*this)[1]; } 
    int& width() { return (*this)[2]; } 
    int& height() { return (*this)[3]; } 
};

Con esto, usted hereda las operaciones de vectores definidos en std::valarray<int>.

No necesariamente recomendaría este enfoque. Simplemente sugiero que se pueda hacer.

Cuestiones relacionadas