2010-09-30 5 views
5

masz prosty struct, powiedzieć:Czy istnieje sposób zastosowania funkcji do każdego członka struktury w C++?

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

I chcesz pomnożyć każdy element przez współczynnik. Czy istnieje bardziej zwięzły sposób wykonania tej operacji, inny niż pomnożenie każdego elementu przez wartość?

+1

Jeśli wszystkich członków tego samego typu, nie możesz umieścić ich w tablicy ('int members_ [4];'? I zapewnić accessors ('int rect :: x() const;') i mutators (' void rect :: x (int); ')? Musisz napisać kod jedno miejsce lub drugie :) – Arun

+6

Podczas definiowania typów w C++, tracisz typedef. –

Odpowiedz

10

Niezupełnie. Programowe uzyskiwanie listy elementów struktury wymaga refleksji, której C++ nie obsługuje.

Dwiema opcjami są po prostu nadanie structowi metody, która robi to w sposób rozwlekły, a następnie użycie tej metody we wszystkich innych miejscach lub ręczne naśladowanie odbicia, na przykład przez nadanie structowi innego elementu, który jest tablica wskaźników do wszystkich innych elementów (wbudowanych w konstruktor struktury), a następnie pętla nad tym, aby wykonać funkcję skalowania.

+1

Co ze związkami zawodowymi? W szczególności wykorzystanie przez Microsoft ich w D3DXMATRIX i D3DXVECTOR. –

+2

To naprawdę to samo, co rozwiązanie castingowe, które inni napisali, ale z ładniejszą składnią. Podobnie jest w przypadku zachowania konkretnego kompilatora, a standardowe C++ nie gwarantuje, że będzie działać. –

+1

Brak wyczerpującej listy opcji. Możesz użyć wskaźników typu ** wskaźnik-do-członka **, jak zasugerowano w mojej odpowiedzi. Jest to podobne do drugiej opcji, ale w takim przypadku nie trzeba tworzyć oddzielnej tablicy wskaźnika dla każdej instancji klasy struct. Zamiast tego używasz pojedynczej tablicy, która ma zastosowanie do wszystkich instancji (możesz uczynić ją * statyczną * członkiem struktury, jeśli chcesz). W rzeczywistości * to * jest prawidłowym sposobem "ręcznego naśladowania odbicia", a nie tego, co sugerujesz powyżej. – AnT

0

czy wszystkie elementy są tego samego typu:

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

z do_something spodziewa się wskaźnik do int

+5

To nie jest przenośne, ponieważ zakłada, że ​​nie ma dopełnienia między elementami konstrukcji, co nie jest gwarantowane. –

+0

zwięzłość kodu kosztem bezpieczeństwa typu. wciąż bardzo zwięzły. +1 –

+1

@Andrew: W celu zwięzłości należy poświęcić typ bezpieczeństwa _never_, poświęcając go. –

10

Nie; nie ma sposobu na iterację członków struktury.

W tym konkretnym przypadku, choć można osiągnąć swój cel za pomocą tablicy:

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

(Zauważ, że ten przykład nie ma sensu (po co mnożyć x, y, wysokość i szerokość przez dwa?), ale to pokazuje inny sposób na rozwiązanie tego typu problemu)

+0

Niektóre sugestie, mimo że wykraczają poza zakres pierwotnego pytania: (i) Użyj symbolicznej stałej zamiast "4". (ii) Zaakceptuj mnożnik ("2" w przykładzie) jako argument wejściowy. – Arun

0

przerażającą siekać

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

To nie jest przenośne, ponieważ zakłada, że ​​nie ma dopełnienia między elementami konstrukcji, co nie jest gwarantowane. –

+0

oraz duh, stąd "okropny hack". Będzie działać na większości 32-bitowych maszyn tho – pm100

5

Jeśli wszystkie wartości mieszczą się w 128-bitowym wektorem, a następnie można u se instrukcje SIMD, aby pomnożyć je wszystkie naraz. Na przykład Intel Primitive Performance Primitive (IPP) ma primitives do dodawania, odejmowania, mnożenia itp. Na wektorach.

To prawdopodobnie przesada, chyba że wykonujesz wiele operacji wymagających dużej mocy obliczeniowej.

0

Rozwiązanie za pomocą nowej klasy:

  • Utwórz klasę z template<class T> class Array rodzajowego tablicy (patrz how)
  • Wypełnij tablicę ze swoimi właściwościami, jedną właściwość jako elementu tablicy.
  • Sprawdź, czy wszystkie typy mogą być mnożone przez wartości numerycznych (lub przeciążenia operatora * dla typu, jeśli konieczne/utworzyć określony sposób na ten obiekt)
  • stworzenie sposobu multiply który wykonuje pętlę na tablicy i mnoży każdy element o podaną wartość.
0

Jeśli możesz ponownie zorganizować swoją strukturę, aby stała się unią, możesz zrobić coś podobnego do tego, co Microsoft zrobił z 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

Jeśli wszystkie są sam typ, użyj unii połączeniu z anonimowej struktury, wewnątrz swojej struktury:

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

następnie:

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! 

Jeżeli użytkownik za pomocą różne rodzaje spojrzeć boost::fusion

+0

to jest brzydkie i okropne, ale to chyba najlepszy sposób na zrobienie dokładnie tego, o co prosił OP ... +1 – rmeador

10

Boost.Fusion oferuje BOOST_FUSION_ADAPT_STRUCT makro, które można włączyć do jakiegokolwiek struct sekwencji fuzyjnych, z których topnienia może następnie iteracyjnego (jak demoed w Quick Start).

Inną opcją jest użycie biblioteki refleksyjnej C++ innej firmy, takiej jak Reflex, do iterowania po elementach struktury.

Jednak wszystko to może być przesada dla swoich celów; ręczne wpisywanie każdego z członków klasy może być mniej eleganckie, ale jest o wiele prostsze, a prostota w kodowaniu jest ważna.

+0

Pokonaj mnie przez 5 minut ... –

+0

+1 dla 'BOOST_FUSION_ADAPT_STRUCT': najszybszy sposób na uzyskanie refleksji podczas kompilacji. –

2

Jak każdy powiedział, że nie może to zrobić bezpośrednio, ale nie ma nic Cię powstrzymuje przed przeciążeniem operatora *:

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

następnie można to

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

To jest tylko czyste rozwiązanie do tej pory ... Musiałem usunąć moją odpowiedź, byłeś szybszy –

2

zrobić zakładając, że jesteś przy użyciu kompilatora C++ (nie C), uczyń go klasą i utwórz funkcję składową, aby to zrobić. Jestem zaskoczony wszystkimi niezręcznymi rozwiązaniami przedstawionymi prostemu problemowi. Co kupi Ci rozwiązanie odblaskowe? Czy ciągle dodajecie nowych członków do tej klasy, więc chcecie zaoszczędzić sobie kroku na modyfikacji funkcji członka w przyszłości? Jeśli jest to poważnie potencjalne oszczędności czasu, pachnie klasą robiącą zbyt wiele.

Jest nieuniknione, że będą powtarzane funkcje działające na członków struktury, więc dlaczego nie zrobić to klasa od samego początku?

1

Cóż, nie bezpośrednio. Ale możesz to osiągnąć, używając dodatkowej tymczasowej tablicy "index" wskaźników typu wskaźnik-do-członka.

Na przykład

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; 

Oczywiście, jeśli trzeba to robić często, można wykorzystać stałą tablicę indeksu rect_members zainicjowany przy starcie programu.

Należy zauważyć, że metoda ta nie zatrudnia żadnych hacków, jak niektóre inne metody zrobić. Wskaźniki typu wskaźnik-do-członka są rzadko używane, ale jest to jedna z rzeczy, dla których zostały wprowadzone.

Ponownie, jeśli trzeba to robić częściej, właściwa rzecz do zrobienia byłoby uczynić tablicę ruchy wskaźnika do static const członkiem struct

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

i kiedy trzeba iteracyjne nad tych członków, ty 'd tylko dostęp do elementów struktury jako s.*rect::members[i].

0

Istnieje kilka problemów związanych z dziedziczeniem z klas standardowych; ale jeśli chcesz zaakceptować te problemy, możesz zrobić coś analagicznego do tego, co sugerują inni, ale zamiast tego użyj std::valarray. Byłoby to wyglądać mniej więcej tak:

#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]; } 
};

Dzięki temu można dziedziczyć operacje wektorowe zdefiniowane na std::valarray<int>.

Nie zaleciłbym takiego podejścia. Po prostu sugeruję, że można to zrobić.

Powiązane problemy