2015-05-10 11 views
6

Lassen Sie uns sagen, dass ich eine Art von Motor machen wollen, die grafische Image s Laden unterstützen sollte, so habe ichModern C und C++: Es ist möglich, eine definierte Struktur für andere deklarierte Struktur zu verwenden?

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

Ich will nicht Außenwelt wissen, was Image wirklich ist, sie mit nur beschäftigen werden Hinweise darauf.

Allerdings innerhalb engine möchte ich spezifischen Typ, z. SDL_Surface, die vollständig in SDL definiert ist.

Kann ich irgendwie Image für diese Datei neu so Compiler nimmt SDL_Surface* jedes Mal, wenn es Image* (andere als Makro) sieht?

I.e. Ich möchte so etwas wie typedef struct SDL_Surface Image;

Alle Versuche, wie

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

produzieren Zeitfehler kompilieren (http://codepad.org/1cFn18oh).

Ich weiß, dass ich so etwas wie struct Image{SDL_Surface* surface}; in engine.c/engine.cpp verwenden kann, aber es schafft unnötige indirection und ich werde ->surface geben muß. Eine weitere schmutzige Lösung besteht darin, explizite Umwandlungen zu verwenden, z. ((SDL_Surface*)image) aber ich interessiere mich für sauberere Umbenennung.

PS. Ich interessiere mich für Antworten für C und C++.

+0

Soweit ich weiß, ist 'struct Image {SDL_Surface surface};' (kein Zeiger-to) die nächste verfügbare Annäherung, aber ich bin daran interessiert herauszufinden, ob es jetzt etwas Besseres gibt. – zwol

+0

Oh, in C++ hast du versucht, 'struct Image: public SDL_Surface {};'?Das hat eine Reihe von Ecken und Kanten, aber es könnte tun, was Sie wollen. – zwol

+0

@zwol diese Arbeit, aber ich muss immer noch Umwandlungen verwenden, um 'SDL_Surface *' zu konvertieren, die von SDL-Funktionen zurückgegeben werden. Die Verwendung von (no pointer-to) ist nicht möglich, da SDL Zeiger zurückgibt. – user2998754

Antwort

3

definieren Sie einfach einen Alias:

using Image = SDL_Surface; 
typedef SDL_Surface Image; 

which compiles just fine.

Wenn Sie SDL_Surface verstecken müssen, importieren Sie es einfach in einige anonyme oder detail-benannte Namespace und verwenden Sie es wie this.


Wenn aus irgendwelchen Gründen, möchten Sie Ihren eigenen Image Typen definieren, können Sie immer ein (n) erklären (impliziter) Konvertierungsfunktion/Betreiber, wie:

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

und auch zurück wenn Sie Image müssen, dass:

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

Das liegt daran, dass in Ihrem Beispiel SDL_Surface definiert ist. Der Punkt ist, ich möchte nicht, dass die Welt weiß, dass SDL_Surface existiert. Ich möchte, dass die Existenz von SDL_Surface nur in einer Datei bekannt ist, die das Laden von Bildern implementiert. Es ist ein internes Detail und ich möchte nicht eine Menge unnötiger Header enthalten. – user2998754

+0

@ user2998754 Importieren Sie 'SDL_Surface' einfach in einen anonymen (oder' detail' genannten, noch besseren) Namespace und benutzen Sie ihn [so] (http://coliru.stacked-crooked.com/a/f4f0701a6d92132f). – Shoe

1

In C++ Sie die Vererbung verwenden können:

Anmerkung: Es wäre sicherer, Klassen und private Vererbung zu verwenden, so dass nur Image weiß, dass es eine SDL_Surface ist.

In einigen Fällen kann es unerwünscht sein, von einer vorhandenen Implementierungsklasse zu übernehmen (wenn Sie beispielsweise einen virtuellen Destruktor benötigen und die Basisklasse nicht).Dann könnte die PIMPL idiom eine Alternative sein (allerdings auf Kosten eines zusätzlichen Indirektion):

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

Der Hauptvorteil von PIMPL hier ist, dass Sie mehr als nur einen unvollständigen Typen aussetzen könnte, und somit für die Kunden bieten einige nützliche Mitgliedsfunktionen. Aber wenn Sie das nicht benötigen und Sie bereits mit poitners zum Objekt auf der Client-Seite arbeiten, können Sie auch direkt zur Komposition gehen und ein ImageImpl Mitglied anstelle eines PIMPL-Zeigers haben.

In C können Sie keine Vererbung verwenden. Aber Zusammensetzung tun würde sicherlich den Trick:

struct Image { 
    SDL_Surface s; 
    }; 
0

Wenn Ihr Client-Code alles tun, nicht mit dem Bild, andere als einen Zeiger auf es passieren, können Sie den Windows-API-Trick:

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

Das ist nicht, was die Windows API tut (nicht, wenn 'STRICT' definiert ist, und Sie sollten * immer *' STRICT' definieren) –

+0

Habe in den letzten zehn Jahren keinen Windows API Code programmiert, also war ich nicht ' t bewusst von STRICT. Ich habe es in 'winnt.h' nachgeschlagen - cool! – zmbq

1

Solche Operationen werden normalerweise mit PIMPL (Zeiger auf Implementierung) -Muster durchgeführt. Aber wenn Sie Umwege für jetzt vermeiden wollen, oder wenn der Typ unvollständig ist (dies ist nicht der Fall mit SDL_Surface, aber es ist mit vielen anderen SDL-Klassen) können Sie Zeiger auf void verwenden, da es auf beliebige Daten zeigen kann, und dann auf der Implementationsseite umsetzen.

Hier verwenden wir std::unique_ptr, um Rule of Zero zu verwenden. Solcher Image ist jetzt nicht kopierbar, aber beweglich. Wenn Sie es kopieren zu können, verwenden Sie einen value_ptr -ähnlichen Zeiger (nicht im Standard, aber Sie können leicht solche Schreibzeiger selbst oder verwenden Sie ein Dritter ein)

#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

In C Sie kann auf einen unvollständigen Typ zurückgreifen. So

Sie definieren Ihre API in einer Header-Datei:

myapi.h

struct Image; 

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

Beachten Sie, dass Ihre struct Image Typ, wenn auch für Ihre Kunden, vollständig von ihnen verborgen.

Jetzt ist Ihre Implementierung funktioniert die vollständige Deklaration der Struktur:

myapi.c:

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

/* function bodies */ 

Sie bündeln die kompilierte C-Code (Objekt, statische oder dynamische Bibliothek) und den Header an Ihre Kunden .

0

In der Kopfzeile exportieren Sie Sie SDL_Surface
weiterleiten kann und dann Image deklarieren einen Zeiger auf sie zu sein. Wie:

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

Auf diese Weise kann Ihre Bibliothek ohne die SDL-Header verwendet werden.
SDL.dll wird jedoch weiterhin benötigt.

Verwandte Themen