Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

idontknow

unregistriert

1

05.07.2015, 23:52

C - incomplete types in libraries common practice?

Moin,

auf reddit wurde mir folgendes entgegen geworfen: "What? Creator functions returning a pointer to an incomplete struct type is an absolutely standard pattern in C libraries." (Grund dafür war meine Behauptung, dass eine struct-definition (im Sinne eines complete types) nicht versteckt werden kann und der Typ an irgendeiner Stelle komplett definiert werden muss.

Ich bin mir immernoch nicht sicher ob das umsetzbar ist und kann mir noch weniger vorstellen wo es sinnvolle Anwendungsgebiete für so ein "pattern" geben könnte indem ich einen Typen tatsächlich rein als Pointer verfügbar machen möchte. (Meine einzige Vorstellung ist tatsächlich ein Library interner Typ der vom User nur innerhalb der Library "umher gereicht wird").

Ich hab kurz in die Richtung versucht zu googln, aber wenig gefunden, außer dass man grundsätzlich einen Typen incomplete definieren kann, aber alle meine gefundenen Links gingen darauf ein, dass er später vollständig definiert wird. Sprich ich habe keine wirklich sinnvolle Quelle gefunden, wobei ich nicht so Recht weiß welche keywords denn genau sinnvoll wären. Könnte natürlich sein, dass der Typ 100% scheiße verzapft hat, ganz sicher bin ich mir eben nicht ob das funktioniert..

Deswegen wollte ich dazu mal gerne ein paar Infos haben von Leuten die sich mit C auskennen, das wäre top :) Vielen Dank schonmal!

Nimelrian

Alter Hase

Beiträge: 1 216

Beruf: Softwareentwickler (aktuell Web/Node); Freiberuflicher Google Proxy

  • Private Nachricht senden

2

06.07.2015, 00:08

Ich finde Crosspostings nicht so schön, da trennt man die Diskussion auf und bekommt nicht mit, was auf der anderen Seite gesagt wird, daher mal der Link zu reddit.
Ich bin kein UserSideGoogleProxy. Und nein, dieses Forum ist kein UserSideGoogleProxyAbstractFactorySingleton.

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

3

06.07.2015, 00:08

Natürlich, das ist tatsächlich Standardvorgehen in C. Beispie:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// MyLibrary.h

struct Thingie;

Thingie* createThingie();
void destroyThingie(Thingie* t);
void useThingie(Thingie* t);


// MyLibrary implementation

struct Thingie
{
  // stuff here
};


Thingie* createThingie()
{
  Thingie* t = (Thingie*)malloc(sizeof(Thingie));
  return t;
}

void destroyThingie(Thingie* t)
{
  free(t);
}

void useThingie(Thingie* t)
{
  // ...
}

idontknow

unregistriert

4

06.07.2015, 00:43

Okey das war auch der einzig sinnvolle Fall den ich mir vorstellen konnte. Für C++ Templates bin ich dann ja gezwungen diese Typen in ein separates Struct zu wrappen, damit die Größe bekannt ist?

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »idontknow« (06.07.2015, 00:48)


Evrey

Treue Seele

Beiträge: 245

Beruf: Weltherrscher

  • Private Nachricht senden

5

06.07.2015, 06:35

Für Templates... yoah. Die Idee ist genau die hinter deiner Vermutung: Man möchte Implementierungsdetails verstecken. Entweder, weil sie Sicherheitsrisiken darstellen würden, oder weil es den Nutzer einfach nichts angeht. Beispiele wären ein Pointer auf den internen Zustand einer VM, oder ein HWND aus dem WinAPI, oder eben auch ein AL-/GL-Context. Bei Letzterem liegt es vor allem daran, dass unterschiedliche Treiber-Implementierungen unterschiedlichen Stuff in die Kontext-Structs auslagern müssen, was aber das vom Nutzer verwendete API nicht interessiert. Diese Klamotten kannste auch in Templates verwenden, da du ja nur mit Pointern auf diese Typen arbeitest. Zum Beispiel so, in Anlehnung an dot's Code:

C-/C++-Quelltext

1
std::unique_ptr<Thingie,[](Thingie* t){destroyThingie(t);}> _stuff = createThingie();


Edit: Da war ein Asterisk im Template, das da nichts zu suchen hatte.

C-/C++-Quelltext

1
2
3
4
int main(int _argc, char** _argv) noexcept {
  asm volatile("lock cmpxchg8b %eax");
  return 0;
} // ::main
(Dieses kleine Biest vermochte einst x86-Prozessoren lahm zu legen.)

=> Und er blogt unter Hackish.Codes D:

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Evrey« (06.07.2015, 13:58)


idontknow

unregistriert

6

06.07.2015, 13:22

Ahh praktisch, wusste gar nicht, dass unique_ptr auch einen Pointer Typ als Template Argument nimmt, wenn für diesen Typ keine vollständige Definition verfügbar ist.

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

7

06.07.2015, 13:24

Ahh praktisch, wusste gar nicht, dass unique_ptr auch einen Pointer Typ als Template Argument nimmt, wenn für diesen Typ keine vollständige Definition verfügbar ist.

Das tut es auch nicht... ;)

unique_ptr und shared_ptr sollten problemlos mit incomplete Types funktionieren, so lange ein entsprechender Deleter verwendet wird. Die Completeness wird nur beötigt, weil der Default Deleter einfach delete macht, was den Destruktor des jeweiligen Typs benötigt... ;)

PS: Das Beispiel von Evrey ist leider nicht ganz korrekt, hier die eine funktionierende Version:

C-/C++-Quelltext

1
2
3
auto del = [](Thingie* t){ destroyThingie(t); };

std::unique_ptr<Thingie, decltype(del)> stuff(createThingie(), del);

Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von »dot« (06.07.2015, 14:38)


8

06.07.2015, 15:16

Ist ja bei FILE im Prinzip genau das gleiche.

Evrey

Treue Seele

Beiträge: 245

Beruf: Weltherrscher

  • Private Nachricht senden

9

06.07.2015, 19:11

Dank für die Korrektur. °^° Nutze Deleter recht selten, passiert gern mal. =D"

C-/C++-Quelltext

1
2
3
4
int main(int _argc, char** _argv) noexcept {
  asm volatile("lock cmpxchg8b %eax");
  return 0;
} // ::main
(Dieses kleine Biest vermochte einst x86-Prozessoren lahm zu legen.)

=> Und er blogt unter Hackish.Codes D:

idontknow

unregistriert

10

06.07.2015, 19:48

Warum muss ich den deleter nochmal im c'tor übergeben? Ich hab bisher immer ein struct mit einem überladenen Operator (T*) im Template Argument angegeben und im c'tor nichts.

Werbeanzeige