2015-05-10 19 views
6

Powiedzmy, że chcesz, aby jakiś silnik, który powinien wspierać ładowanie graficznych Image s, więc mamNowoczesne C i C++: możliwe jest użycie jednej zdefiniowanej struktury dla innej deklarowanej struktury?

struct Image; 
Image* load_image_from_file(...); 

Nie chcę zewnętrzny świat wiedział, co naprawdę jest Image, będą zajmować się tylko wskaźniki do niego.

Jednak wewnątrz engine chcę użyć określonego typu, np. SDL_Surface, który jest w pełni zdefiniowany w SDL.

Czy mogę jakoś zmienić obraz dla tego pliku, aby kompilator przyjmował SDL_Surface* za każdym razem, gdy zobaczy Image* (inny niż makro)?

tj. Chcę coś typedef struct SDL_Surface Image;

Wszystkie próby jak

using Image = SDL_Surface; 
typedef SDL_Surface Image; 
typedef struct SDL_Surface Image; 

produkować błąd kompilacji czasu (http://codepad.org/1cFn18oh).

wiem, że mogę użyć czegoś podobnego struct Image{SDL_Surface* surface}; w engine.c/engine.cpp ale tworzy niepotrzebne zadnie i będę musiał wpisać ->surface. Innym brudnym rozwiązaniem jest użycie jawnych rzutów, np. ((SDL_Surface*)image), ale interesuje mnie czystsza zmiana nazwy.

PS. Interesują mnie odpowiedzi na C i C++.

+0

O ile mi wiadomo, "struct Image {SDL_Surface surface};" (brak wskaźnika do) jest najbliższym dostępnym przybliżeniem, ale będę zainteresowany, aby dowiedzieć się, czy jest coś lepszego teraz. – zwol

+0

Och, w C++ czy wypróbowałeś 'struct Image: public SDL_Surface {};'?To ma sporo ostrych krawędzi, ale może po prostu robić to, co chcesz. – zwol

+0

@ Pozwala to na pracę, ale nadal będę musiał używać rzutów, aby przekonwertować 'SDL_Surface *', które są zwracane przez funkcje SDL. Używanie (bez wskaźnika do) nie jest możliwe, ponieważ SDL zwraca wskaźniki. – user2998754

Odpowiedz

3

Wystarczy zdefiniować alias:

using Image = SDL_Surface; 
typedef SDL_Surface Image; 

which compiles dobrze.

Jeśli chcesz ukryć SDL_Surface, po prostu zaimportuj go do anonimowej lub nazwanej przestrzeni nazwanej detail i użyj go jak this.


Jeśli z jakichś powodów chcesz zdefiniować własną Image typ, zawsze można zadeklarować (n) (implicite) konwersja funkcja/operator, jak:

struct Image { 
    /* explicit */ operator SDL_Surface() const; 
    // ... 
}; 

a także z powrotem do Image, jeśli trzeba, że:

struct Image { 
    /* explicit */ Image(SDL_Surface&&); 
    /* explicit */ Image(SDL_Surface const&); 
    // ... 
}; 
+0

Dzieje się tak, ponieważ w twoim przykładzie zdefiniowana jest definicja SDL_Surface. Chodzi o to, że nie chcę, aby świat wiedział, że istnieje SDL_Surface. Chcę, aby istnienie SDL_Surface było znane tylko w pliku, który implementuje ładowanie obrazu. To wewnętrzna szczegółowość i nie chcę dodawać zbędnych nagłówków. – user2998754

+0

@ user2998754 Wystarczy zaimportować 'SDL_Surface' wewnątrz anonimowego (lub" szczegółowego "nazwanego, jeszcze lepszego) obszaru nazw i użyć go [w ten sposób] (http://coliru.stacked-crooked.com/a/f4f0701a6d92132f). – Shoe

1

W C++ można użyć dziedziczenia:

// User view 
struct Image;     // forward declaration (inclomplete type). 
Image* LoadFromFile (...);  // You can use pointer to incomplete type 

// Implementation view 
struct Image: SDL_Surface { }; // here you go !! :-) 

Uwaga: bezpieczniejsze byłoby korzystanie z klas i prywatnego dziedziczenia, tak że tylko obraz wie, że jest to powierzchnia SDL_Surface.

W niektórych przypadkach może być niepożądane dziedziczenie z istniejącej klasy implementacji (na przykład, jeśli potrzebny jest wirtualny destruktor, a klasa podstawowa nie).Następnie PIMPL idiom może być alternatywą (kosztem dodatkowego zadnie):

//User View unchanged 
struct Image; 
int TestImage(Image*z); 

//implementation view  
struct Image { 
    struct ImageImpl { int x; }; // nested definition or typedef or whatever 
    ImageImpl *p; // works in every case, at cost of an extra indirection instead of a pointer 
}; 
int TestImage(Image* z) 
{ 
return z->p->x; 
} 

Główną zaletą PIMPL tu jest, że można wystawiać więcej niż tylko niepełną typu, a tym samym zaoferować klientom niektóre przydatne funkcje członkowskie. Ale jeśli tego nie potrzebujesz i już pracujesz z klientami po obiekcie po stronie klienta, możesz również przejść bezpośrednio do kompozycji i mieć element ImageImpl zamiast wskaźnika PIMPL.

W C nie można używać dziedziczenia. Ale skład na pewno rade:

struct Image { 
    SDL_Surface s; 
    }; 
0

Jeśli kod klient nie coś z obrazem, inne niż zrobić przekazać wskaźnik do niego, można użyć Windows API trick:

typedef void *HIMAGE; // Image Handle 
HIMAGE LoadImage(...); 
+0

To nie jest to, co robi Windows API (a nie, kiedy 'STRICT' jest tak czy inaczej, i powinieneś * zawsze * definiować' STRICT') –

+0

Nie zaprogramowałem żadnego kodu Windows API w ciągu ostatniej dekady, więc nie byłem " t świadomy STRICT. Sprawdziłem to w 'winnt.h' - fajnie! – zmbq

1

Takie operacje są zwykle wykonywane przy użyciu wzorca PIMPL (wskaźnik do implementacji). Ale jeśli chcesz teraz uniknąć pośrednich lub jeśli typ jest niekompletny (nie jest tak w przypadku SDL_Surface, ale jest z wieloma innymi klasami SDL), możesz użyć wskaźnika do void, ponieważ może wskazywać dowolne dane, a następnie rzucić go po stronie implementacji.

Tutaj używamy std::unique_ptr do korzystania z Rule of Zero. Taka Image jest teraz niekopiowalna, ale ruchoma. Jeśli chcesz, aby móc go skopiować, użyj value_ptr -jak wskaźnik (nie w standardzie, ale można łatwo napisać taki wskaźnik siebie lub użyć innej firmy jeden)

#include <memory> 

struct ImageDeleter 
{ 
    void operator()(void* ptr) const; 
}; 

class Image 
{ 
public: // but don't touch it 
    std::unique_ptr<void, ImageDeleter> internal; 
private: 
    /* private operations on surface */ 
public: 
    /* public operations */ 
    void save(const std::string& path) const; 
    Image(int width, int height); 
}; 

// EXAMPLE USAGE 

// Image img(640, 480); 
// img.save("aaa.bmp"); 

// IN THE DEFINITION FILE 

#include <SDL2/SDL.h> 

namespace detail 
{ 
    SDL_Surface* as_surface(const Image& img) 
    { 
     return static_cast<SDL_Surface*>(img.internal.get()); 
    } 
} 

void ImageDeleter::operator()(void* ptr) const 
{ 
    SDL_FreeSurface(static_cast<SDL_Surface*>(ptr)); 
} 

Image::Image(int width, int height) : 
    internal(SDL_CreateRGBSurface(0, width, height, 32, 0, 0, 0, 0)) 
{ 

} 

void Image::save(const std::string& path) const 
{ 
    SDL_SaveBMP(detail::as_surface(*this), path.c_str()); 
} 
0

W C ciebie może uciec się do niekompletnego typu.

więc zdefiniować API w pliku nagłówka:

myapi.h

struct Image; 

struct Image* load_image_from_file(...); 
... 

pamiętać, że typ struct Image, choć dostępna dla klientów, jest całkowicie ukryty przed nimi.

Teraz implementacja ma pełne oświadczenie o swojej struktury:

myapi.c:

struct Image { 
    /* whatever you want to put here */ 
    /* even SDL_Surface */ 
    /* or pointer to it */ 
}; 

/* function bodies */ 

Państwo pakiet skompilowany kod C (obiekt, statyczne lub dynamiczne Library) i nagłówek do swoich klientów .

0

W nagłówku eksportowania można przekazać SDL_Surface
a następnie zadeklarować Image jako wskaźnik do niego. Na przykład:

struct SDL_Surface; 
typedef SDL_Surface* Image; 
extern Image load_image_from_file(char*); 

W ten sposób można korzystać z biblioteki bez nagłówków SDL.
Jednak program SDL.dll jest nadal potrzebny.

Powiązane problemy