2011-08-22 14 views
7

Supongamos que tengo un objeto con muchos miembros:C++: ¿iterando a través de todos los miembros de un objeto?

class Example { 
    AnotherClass member1; 
    AnotherClass member2; 
    YetAnotherClass member3; 
    ... 
}; 

¿Hay una manera corta/concisa para hacer algo como:

foreach(member m: myExample) 
    m.sharedMethod(); 

lugar de acceder a cada uno en particular?

Creo que podría ponerlos en un vector y usar un shared_ptr para el mismo efecto, me preguntaba si, por ejemplo, Boost o alguna otra biblioteca popular no tiene algo para hacer esto automáticamente.

+2

Realmente no creo que esta sea una buena idea. Si necesita repetir algo, entonces declararlo como un contenedor iterable, o ¿por qué no haría esto? – leftaroundabout

+0

echa un vistazo Boost serialización –

+1

C++ no tiene reflejo. Eso significa que no es posible. –

Respuesta

11

C++ no es compatible con la introspección de clase, por lo que no puede iterar sobre todos los miembros de una clase como esa, no sin tener una función (escrita manualmente) que itere sobre todos los miembros de todos modos.

Se podría, en principio, añadir un miembro de la plantilla de este modo:

template<typename Functor> 
void doAllMembers(Functor &f) { 
    f(member1); 
    f(member2); 
    f(member3); 
} 

Dicho esto, yo consideraría esto como un diseño roto; has ido y has expuesto públicamente a todos tus miembros internos. ¿Qué pasa si agregas uno más tarde? ¿O cambiar la semántica de uno? ¿Hace uno un valor en caché que a veces está desactualizado? etc. Además, ¿qué sucede si tienes miembros que no heredan todos de los mismos tipos?

Retroceda y reconsidere su diseño.

-2

Esto no es posible con C++, si realmente realmente lo necesita, entonces use C#. Pero realmente dudo que lo haga.

Habiendo dicho eso, la única opción que realmente tiene es usar .NET y usar C++ administrado que llamadas a Microsoft administradas C++/CLI. Pero la advertencia es que su clase debe ser una "clase de referencia" administrada, es decir, una clase administrada. Eventualmente todo se compila en MSIL, y el lenguaje intermedio es independiente del idioma. Esa es la única forma en que podría utilizar la reflexión .NET en el tiempo de ejecución para descubrir sus funciones y valores. Sin embargo, incluso usando .NET no es tan fácil como describió cómo le gustaría usarlo anteriormente. Otra desventaja de la reflexión es que es lenta, por lo que si la usas mucho, tienes un mal diseño. La reflexión es agradable porque .NET la usa para ayudar a serializar los tipos de datos, lo que hace que la serialización XML sea muy fácil de usar.

3

C++ puede hacer algo como esto, si juega según sus reglas y usa la metaprogramación de plantillas.

En lugar de almacenar tus cosas en una estructura o clase, almacenarlo en una tupla:

typedef boost::tuple<AnotherClass, AnotherClass, YetAnotherClass> Example; 

continuación, puede utilizar algoritmos plantilla metaprogramación y así sucesivamente (ver Boost.Fusion) para acceder a los miembros y meter en cosas. Puede iterar, estilo de plantilla, sobre los elementos de la tupla.

0

Es posible que lo que está buscando aquí es Patrón de visitante. Si tiene un objeto como usted describe, con un número de campos de miembros no triviales, y encuentra que tiene varias funciones diferentes que atraviesan esta estructura de datos de la misma manera, el patrón de visitante puede ser muy útil para reducir la cantidad de duplicación de código. No es automático, tiene que escribir las funciones que atraviesan todos los campos de miembros, pero solo tiene que hacer eso una vez, y puede usarlo muchas veces con diferentes clases de visitantes que hacen cosas diferentes.

El patrón de visitante En qué consiste escribir un poco de código, se necesita una clase base abstracta para los visitantes:

class VisitorBase 
{ 
    virtual void enter(Example& e)=0; 
    virtual void leave(Example& e)=0; 
    virtual void enter(AnotherClass& e)=0; 
    virtual void leave(AnotherClass& e)=0; 
    etc ... 
}; 

Luego hay que aceptar las funciones en todas las clases que van a ser visitados:

void Example::accept(VisitorBase& visitor) 
{ 
    visitor.enter(*this); 
    member1.accept(visitor); 
    member2.accept(visitor); 
    member3.accept(visitor); 
    visitor.leave(*this); 
} 

Y finalmente necesita implementar clases concretas de visitantes que realicen el trabajo real que le interesa, que generalmente equivale a recopilar información de la estructura de datos, realizar cambios en la estructura de datos o combinaciones de ambos. Patrón de Google Visitor y encontrarás mucha ayuda sobre esto.

6

Existen varias soluciones a este problema, al contrario de lo que dicen los oradores, pero no de manera integrada.

C++ admiten un tipo limitado de introspección, en tiempo de compilación: puede verificar los parámetros de la plantilla.

Usando Boost.Tuple o Boost.Fusion (para su mapa), puede lograr lo que desea. En Boost.Fusion incluso tienes BOOST_FUSION_ADAPT_STRUCT para transformar una estructura básica en una Fusion Sequence (y así iterarla).

Sin embargo, requiere un poco de meta-programación de plantillas.

+0

Me gustó esta solución. Publicó el código detallado aquí: [C++ itera en el campo de estructura anidada con boost fusion adapt_struct] (http://stackoverflow.com/q/12084781/362754) – minghua

0

Esta es mi manera de hacer lo que quiere lograr en C++ 11. Utiliza tupla y plantillas. No es corto y conciso, pero si envuelve somecode en un archivo de cabecera es aceptable. Está mi ejemplo completo compilable:

#include <iostream> 
#include <string> 
using namespace std; 

#include <tuple> 

//Our iteratation code 

//first a iteration helper structure 
template<int N = 0> 
struct IterateP { 
    template<class T> 
    typename std::enable_if<(N < std::tuple_size<T>::value), void>::type 
    iterate(T& t) { 
    std::get<N>(t).sharedMethod(); //there is the method name 
    IterateP<N+1>().iterate<T>(t); 
    } 

    template<class T> 
    typename std::enable_if<!(N < std::tuple_size<T>::value), void>::type 
    iterate(T&) {} 
}; 

//wrapper of the helper structure for a more comfortable usage 
template <typename T> 
void iterate(T& t) { IterateP<>().iterate(t.members); } //look at the .members, is the name of the class tuple 

//Helper notation macro. 
#define MEMBER(name, i) std::tuple_element<i,decltype(members)>::type &name = std::get<i>(members) 

//YOUR CLASSES 

struct AnotherClass { 
    int value; 
    void sharedMethod() { cout << value << endl; } 
}; 

struct YetAnotherClass { 
    string value; 
    void sharedMethod() { cout << value << endl; } 
}; 


//The class with iterable members 
struct Example { 
    std::tuple<AnotherClass, AnotherClass, YetAnotherClass> members; //members must be in a tuple 

    //These are helper member definition to access the tuple elements as normal members (instance.member1) 
    //If you don't you this you need to access the members with tuple classes 
    MEMBER(member1, 0); //first param is the member name and the second is it's position in the tuple. 
    MEMBER(member2, 1); 
    MEMBER(member3, 2); 
}; 

//USAGE 
int main() { 

    Example example; 

    //setting members just as a normal class 
    example.member1.value = 1; 
    example.member2.value = 2; 
    example.member3.value = "hola"; 

    //magic 
    iterate(example); 
} 
Cuestiones relacionadas