2009-09-15 18 views
5

Czy można napisać klasę lub strukturę C++, która jest w pełni zgodna z C struct. Z kompatybilności rozumiem wielkość obiektu i lokalizacji pamięci zmiennych. Wiem, że to zło używać *(point*)&pnt lub nawet (float*)&pnt (w innym przypadku, w którym zmienne są zmienne), ale uważają, że jest to naprawdę wymagane ze względu na wydajność. Nie logiczne jest używanie miliona razy na sekundę regularnego operatora odlewania.C++ Klasa lub Struct kompatybilność z C struct

Weźmy ten przykład

Class Point { 
    long x,y; 
    Point(long x, long y) { 
     this->x=x; 
     this->y=y; 
    } 

    float Distance(Point &point) { 
     return ....; 
    } 
}; 

wersja C jest struct POD

struct point { 
    long x,y; 
}; 
+0

W jakim celu potrzebujesz tej zgodności? Czy chcesz przekazać dane binarne z programu C++ do programu c, czy chcesz przekazać obiekt C++ do funkcji c? – Dima

+4

* "ale należy wziąć pod uwagę, że jest to naprawdę wymagane ze względu na wydajność.Nie logiczne jest używanie miliona razy na sekundę regularnego operatora odlewania." * Nie ma to dla mnie żadnego sensu. – GManNickG

+0

@Dima: do funkcji C @GMan: metoda niepoprawna powinna określać operator odlewania typu punkt operatora() { punkt p; p.x = this-> x; p.y = this.y; return p; } Jednak będę używać tych punktów w dwóch przypadkach int wpisany, aby przekazać funkcję okna, aby znaleźć kursor pos. Typ Float jest wysyłany do OpenGL jako wierzchołki (gdy jest konwertowany na tablicę zmiennoprzecinkową), pierwsza jest wykonywana po jednej na każdą ramkę (350 razy/s), druga do 4 x obiektów rysowanych x 350. Będą używane w grze silnik. –

Odpowiedz

8

Tak.

  • Użyj tych samych typów w tej samej kolejności w obu językach
  • Upewnij się, że klasa nie ma nic w nim wirtualny (więc nie dostać vtable wskaźnik przyklejonych na przedniej stronie)
  • W zależności od użytych kompilatorów może zajść konieczność dopasowania struktury pakowania (zwykle z pragmami) w celu zapewnienia zgodności.

(edit)

  • Ponadto, należy zadbać, aby sprawdzić sizeof() rodzaje ze swoimi kompilatorów. Na przykład napotkałem kompilator, który zapisał szorty jako wartości 32-bitowe (gdy większość użyje 16). Częstszym przypadkiem jest to, że int będzie zwykle 32-bitową architekturą 32-bitową, a 64-bitową architekturą 64-bitową.
+0

Z mojego doświadczenia wynika, że ​​pragma to zły pomysł. Jeśli zamówisz zmienne składowe w taki sposób, aby miały naturalne dopasowanie, to pragmy pakietów nie są konieczne. A jeśli nie * nakazujesz im mieć naturalne dopasowanie i próbujesz użyć pragmy, aby to naprawić, możesz napotkać dwa problemy: 1) Pragmy nadal nie będą działać. 2) Zmusisz kompilator do niewspółmierności działania Procesor nie chce być źle wyrównany, jak liczby zmiennoprzecinkowe. W PowerPC przynajmniej niewspółosiowy float wygeneruje przerwanie, które zostanie obsłużone przez oprogramowanie, które może * znacząco * wpłynąć na wydajność. – KeyserSoze

+0

Definiowanie obiektu C++ o tym samym układzie, co struktura C, działa, ale nie wywołuje intencji tak wyraźnie, jak z interetem klasy ze struktury (zobacz moją odpowiedź poniżej). –

+0

@keysersoze: Tak, naturalne dopasowanie jest lepsze. Jednak niektóre kompilatory będą nadal pakować dane inaczej, gdzie warto wspomnieć o pakowaniu. Rzeczywiście, zapomniałem wspomnieć o dbałości również o rozmiary członków. Będę edytować mój post, aby dodać ten punkt. –

-2

Dopóki klasa nie wykazują pewne zaawansowane cechy w swoim rodzaju, takie jak rosnące coś wirtualnego, to powinien być prawie taką samą strukturą.

Poza tym można zmienić Class (która jest nieważna z powodu dużej litery, w każdym razie) na struct bez żadnych szkód. Z wyjątkiem tego, że członkowie staną się publiczni (teraz są prywatni).

Ale teraz, kiedy myślę o twojej rozmowie na temat konwersji typu ... Nie ma możliwości, aby zmienić float w long reprezentujących tę samą wartość lub odwrotnie przez rzutowanie typu wskaźnika. Mam nadzieję, że chcecie tego tylko dla tych wskazówek, żeby poruszać się po okolicy.

+1

Po zmianie struktury na klasę efekt _only_ jest taki, że domyślny poziom ochrony (do pierwszego jawnego publicznego :, prywatnego: lub chronionego :) staje się publiczny, a nie prywatny. – bdonlan

+0

bdonlan, tak, wspomniałem o tym. Ale nie powinieneś lekceważyć emocjonalnej strony umowy ;-) –

+0

Wersja float to zupełnie inna sprawa (OpenGL), a int jest dla Windowsa (point struct). a przykład klasy powinien mieć publiczny: u góry. Po prostu wyrzucam to jako próbkę. –

5

POD dotyczy C++. Możesz mieć funkcje członkowskie. "A POD type in C++ is an aggregate class that contains only POD types as members, has no user-defined destructor, no user-defined copy assignment operator, and no nonstatic members of pointer-to-member type"

Powinieneś zaprojektować struktury danych POD, aby miały naturalne dopasowanie, a następnie mogą być przekazywane między programami stworzonymi przez różne kompilatory na różnych architekturach. Naturalne wyrównanie to miejsce, w którym przesunięcie pamięci dowolnego elementu jest podzielne przez rozmiar tego elementu. IE: float znajduje się pod adresem, który jest podzielny przez 4, podwójne jest na adres podzielny przez 8. Jeśli zadeklarujesz char, a następnie float, większość architektur będzie zajmować 3 bajty, ale niektóre mogą zawierać 1 bajt. Jeśli zadeklarujesz float, a następnie znak, wszystkie kompilatory (powinienem dodać źródło dla tego roszczenia, przepraszam) nie będą w ogóle padały.

+0

Nie ma gwarancji, że kompilator nie wstawi dodatkowego dopełnienia, więc nie można być pewnym, że będzie działał, chyba że użyje się pakietu pragma lub podobnego. – jalf

+0

Nigdy nie widziałem kompilatora, który wstawił dopełnienie dla struktur, które mają naturalne wyrównanie. VC++ 6.0, 2003, 2005, 2008, Metrowerks dla PPC, Metrowerks dla HC12, gcc dla ARM, gcc 2.98-3.x dla PowerPC, gcc 3.x-4.x dla x86, SDCC dla 8051, Crossworks dla MSP430. Byłbym bardzo wdzięczny za wysłuchanie kontrprzykładu lub dobre uzasadnienie, dlaczego tak się dzieje. – KeyserSoze

+0

Jeśli korzystasz z pragmy pakowania w przypadku struktur, które nie mają już naturalnego wyrównania, często występują szkodliwe efekty uboczne. Jeśli na przykład niepoprawnie wyrównywane są przepływy w PowerPC, wystąpi wyjątek sprzętowy. Program obsługi sprzętu może zresetować procesor (często w aplikacjach wbudowanych) lub rozwiązać problem w oprogramowaniu (czyli SLOOOOW). W obu przypadkach użycie pragma paczki może cię tylko skrzywdzić, definitywnie chcesz zdefiniować strukturę, aby mieć naturalne wyrównanie i nie używać paczki, aby działała tak samo na różnych platformach. – KeyserSoze

1

C i C++ są różnymi językami, ale zawsze było intencją C++, że możesz mieć implementację, która obsługuje oba języki w sposób zgodny binarnie.Ponieważ są to różne języki, zawsze jest to szczegół implementacji kompilatora, czy jest on faktycznie obsługiwany. Zazwyczaj dostawcy, którzy dostarczają zarówno kompilator C, jak i C++ (lub pojedynczy kompilator z dwoma trybami), obsługują pełną kompatybilność pod kątem przekazywania POD-structs (i wskaźników do POD-structs) między kodem C++ a kodem C.

Często wystarczy, że konstruktor zdefiniowany przez użytkownika zepsuje gwarancję, chociaż czasami można przekazać wskaźnik do takiego obiektu do funkcji C oczekującej wskaźnika na struct z identyczną strukturą danych i będzie działać.

W skrócie: sprawdź dokumentację kompilatora.

1

Użyj tej samej "struct" w C i C++. Jeśli chcesz dodać metody w implementacji C++, możesz dziedziczyć strukturę, a rozmiar powinien być taki sam, o ile nie dodajesz elementów danych lub funkcji wirtualnych.

Należy pamiętać, że jeśli masz puste elementy struct lub dane, które są pustymi strukturami, mają różne rozmiary w C i C++. W C, sizeof (empty-struct) == 0 chociaż w C99, puste-struktury nie powinny być dozwolone (ale mogą być wspierane i tak jako "rozszerzenie kompilatora"). W C++, sizeof (empty-struct)! = 0 (typowa wartość to 1).

17

Najczystsze było zrobić to dziedziczyć z C struct:

struct point 
{ 
    long x, y; 
}; 

class Point : public struct point 
{ 
    public: 
    Point(long x, long y) 
     { this->x=x; this->y=y; } 

    float Distance(Point &point) 
     {    return ....;  } 
} 

kompilator C++ gwarantuje punkt struct C styl ma taki sam układ jak z kompilatora C. Punkt klasy C++ dziedziczy ten układ dla swojej części klasy podstawowej (a ponieważ nie dodaje żadnych danych ani elementów wirtualnych, będzie miał ten sam układ). Wskaźnik do klasy Punkt zostanie przekonwertowany na wskaźnik do punktu struktury bez rzutowania, ponieważ konwersja do wskaźnika klasy bazowej jest zawsze obsługiwana. Można więc używać obiektów klasy Point i swobodnie przekazywać do nich wskaźniki do funkcji C oczekujących na wskaźnik do struct point.

Oczywiście, jeśli istnieje już plik nagłówkowy C definiujący struct point, można go po prostu dołączyć zamiast powtarzania definicji.

+0

+1. Jak powiedział George Clooney: "Co jeszcze?" ...^_^... – paercebal

+0

To właśnie mnie znaleziono. Dzięki! – Eonil

+0

Może to prowadzić do strasznych zakłóceń, ponieważ członkowie struktury są publiczni. Zasadniczo całkowicie zrywa wszelkie hermetyzacje. –

0

Oprócz innych odpowiedzi, byłbym pewien, aby nie dodawać żadnych specyfikatorów dostępu (public :, private: etc) do twojej klasy/struktury C++. IIRC kompilator może zmieniać kolejność bloków zmiennych składowych zgodnie z widocznością, dzięki czemu private: int a; pubic: int b; może otrzymać rundę aib. Zobacz np. Ten link: http://www.embedded.com/design/218600150?printable=true
Przyznaję, że jestem zaskoczony, dlaczego definicja POD nie zawiera zakazu takiego działania.