BlackBoard (http://www.black-board.net/index.php)
- Design, Programmierung & Entwicklung (http://www.black-board.net/board.php?boardid=55)
-- Programmieren (http://www.black-board.net/board.php?boardid=4)
--- C OOP in ANSI C -- es lässt mich nicht los :) (http://www.black-board.net/thread.php?threadid=23876)


Geschrieben von Zirias am 13.05.2010 um 16:16:

  OOP in ANSI C -- es lässt mich nicht los :)

edit 4: GANZ WICHTIG -- die erste Version war gar nicht ANSI C...
1. Variadic Macros gibt's erst ab C99, nicht jeder C-Compiler kann das
2. Viel schlimmer: das ARG()-Makro da unten verwendet eine GNU-spezifische Syntax, um ein überflüssiges Komma bei leerer variabler Argument-Liste zu "schlucken".

Bin inzwischen dazu übergegangen, strikt C89-konform vorzugehen, das bedeutet:

1. statt ARG(...) gibt es ein #define THIS void *_this, um wenigstens den boilerplate der Methoden-Deklaration ein BISSCHEN zu verstecken. Sieht dann z.B. so aus:
code:
1:
   void FUNC(foo)(THIS, int bar);

2. um der C89/C90 Vorschrift "Deklarationen zuerst" nachzukommen, müssen Methoden alle ihre lokalen Variablen VOR dem aufruf von "METHOD(Classname);" deklarieren.
end edit 4...

Hab mal wieder etwas neues gebaut, dieses mal ist sogar so etwas wie Polymorphie möglich smile

Das ganze besteht aus einem Kern zum Memory-Management:

mm.h:
code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
#ifndef MM_H
#define MM_H

#include <stdlib.h>

#define XMALLOC(T, n) ((T *)xmalloc((n) * sizeof(T)))
#define XREALLOC(T, p, n) ((T *)xrealloc((p), (n) * sizeof(T)))
#define XFREE(p) do { if (p) {free(p); p=0;}} while(0)

extern void *xmalloc(size_t n);
extern void *xrealloc(void *p, size_t n);

#endif


mm.c:
code:
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:
#include <stdio.h>
#include "mm.h"

void *
xmalloc(size_t n)
{
    void *p = malloc(n);
    if (!p)
    {
	fprintf(stderr, "Out of memory!\n");
	exit(-1);
    }
    return p;
}

void *
xrealloc(void *p, size_t n)
{
    if (!p) return xmalloc(n);
    void *rp = realloc(p, n);
    if (!rp)
    {
	free(p);
	fprintf(stderr, "Out of memory!\n");
	exit(-1);
    }
    return rp;
}


sowie einer basis-klasse "object" mit einem Satz von helper-Makros

object.h:
code:
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:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
#ifndef OBJECT_H
#define OBJECT_H

#include "mm.h"

#define CTOR(myclass) myclass myclass##_ctor(myclass this, TypeList types)
#define DTOR(myclass) void myclass##_dtor(myclass this)

#define CLASS(name) struct name; \
    typedef struct name *name; \
    extern CTOR(name); \
    extern DTOR(name); \
    struct name

#define INHERIT(baseclass) struct baseclass _baseobject
#define CAST(pointer, type) ((type)GetObjectOf(pointer, CLASS_##type))
#define NEW(T) T##_ctor((T)XMALLOC(struct T, 1), 0)
#define DELETE(T, object) do { if (object) { \
	T##_dtor((T)object); free(object); object=0; \
    }} while (0)

#define BASECTOR(myclass, baseclass) \
    types = RegisterType(types, CLASS_##myclass); \
    baseclass##_ctor((baseclass)this, types)
#define BASEDTOR(baseclass) baseclass##_dtor((baseclass)this)

#define FUNC(name) (* name )
#define ARG(...) (void *_this, ##__VA_ARGS__)

#define METHOD(myclass) myclass this = CAST(_this, myclass)


struct TypeList;
typedef struct TypeList *TypeList;

CLASS(Object) {
    TypeList types;
};

extern Type GetType(void const *object);
extern Object GetObject(void const *object);
extern Object GetObjectOf(void const *object, const Type T);
extern TypeList RegisterType(TypeList types, const Type T);

#endif


object.c:
code:
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:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
#include "object.h"

struct TypeList {
    unsigned char n;
    Type type[16];
    TypeList next;
};

CTOR(Object)
{
    types = RegisterType(types, CLASS_Object);
    this->types = types;
    return this;
}

Type
GetType(void const *object)
{
    return GetObject(object)->types->type[0];
}

Object
GetObject(void const *object)
{
    return (Object)object;
}

Object
GetObjectOf(void const *object, const Type T)
{
    int i;
    Object p = GetObject(object);
    TypeList types = p->types;

    while (types)
    {
	for (i = 0; i < types->n; ++i)
	{
	    if (types->type[i] == T) return p;
	}
	types = types->next;
    }
    return 0;
}

TypeList
CreateTypeList(void)
{
    TypeList types = XMALLOC(struct TypeList, 1);
    types->n = 0;
    types->next = 0;
    return types;
}

TypeList
RegisterType(TypeList types, const Type T)
{
    TypeList current;

    if (!types) types = CreateTypeList();

    current = types;

    while (current->n == 16) {
	if (!current->next) current->next = CreateTypeList();
	current = current->next;
    }
    current->type[current->n++] = T;

    return types;
}

void
FreeTypeList(TypeList types)
{
    if (types->next) FreeTypeList(types->next);
    XFREE(types);
}

DTOR(Object)
{
    FreeTypeList(this->types);
}


Verwendet wird das ganze dann z.B. so:

classes.h:
code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
#ifndef CLASSES_H
#define CLASSES_H

enum Type {
    CLASS_Car,
    CLASS_Object
};
typedef enum Type Type;

#endif


car.h:
code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
#ifndef CAR_H
#define CAR_H

#include "object.h"

CLASS(Car) {
    INHERIT(Object);

    int color;

    void FUNC(drive) ARG(char *dest);
};

#endif


car.c:
code:
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:
#include <stdio.h>
#include "car.h"

static void
m_drive ARG(char *dest)
{
    METHOD(Car);

    printf("driving %d colored car to %s...\n", this->color, dest);
}

CTOR(Car)
{
    BASECTOR(Car, Object);
    this->color = 0;
    this->drive = &m_drive;
    return this;
}

DTOR(Car)
{
    BASEDTOR(Object);
}



main.c:
code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
#include "car.h"

int main(int argc, char **argv)
{
    Car c = NEW(Car);
    Object o = CAST(c, Object);
    Car c2 = CAST(o, Car);
    c2->drive(c2, "northpole");
    DELETE(Car, c2);
}


Kann jemand was damit anfangen? Augenzwinkern

Natürlich muss jede Klasse in "classes.h" eingetragen werden... man könnte das auch mit Strings lösen, aber so ist es performanter.

edit: Syntax "verschönert" (Funktionalität unverändert)
edit 2: Ganz wichtige Ergänzung: Methoden-Implementierungen UNBEDINGT static deklarieren, sonst gibt's sehr schnell Symbol-Kollisionen (kein mangling in plain C *g*)
edit 3: DELETE() tut nichts bei NULL-pointer -- ist einfach bequemer so.



Geschrieben von phlox81 am 13.05.2010 um 19:14:

 

Nimm doch direkt C++ Augenzwinkern
Mit MPL kann man viel bessere Sachen machen Zunge raus
boost::spirit::qi::_int benötigt z.b. 25-90% weniger Zeit als atoi.



Geschrieben von Zirias am 13.05.2010 um 19:18:

 

Blaa *fg*

also Hintergrund ist: ich will ENDLICH mal das gute alte AmigaBASIC (!) Game "Stoneage" auf aktuelle Plattformen portieren. Nachdem ich schonmal erfolgreich ne Portierung auf MS-DOS mit TurboPASCAL geschafft hab, sollte es jetzt etwas auf Basis von SDL sein.

Und da kommt halt die Frage auf: C++ oder C? SDL selbst ist rein imperativ, verfasst in C. Ich will natürlich objektorientiert programmieren. Zwei Möglichkeiten: SDL in C++ "wrappen" oder ANSI C objektorientiert erweitern. Letzteres find ich irgendwie "cooler" Augenzwinkern



Geschrieben von phlox81 am 14.05.2010 um 09:22:

 

Zitat:
Original von Zirias
Blaa *fg*

also Hintergrund ist: ich will ENDLICH mal das gute alte AmigaBASIC (!) Game "Stoneage" auf aktuelle Plattformen portieren. Nachdem ich schonmal erfolgreich ne Portierung auf MS-DOS mit TurboPASCAL geschafft hab, sollte es jetzt etwas auf Basis von SDL sein.

Und da kommt halt die Frage auf: C++ oder C? SDL selbst ist rein imperativ, verfasst in C. Ich will natürlich objektorientiert programmieren. Zwei Möglichkeiten: SDL in C++ "wrappen" oder ANSI C objektorientiert erweitern. Letzteres find ich irgendwie "cooler" Augenzwinkern


C ist ein Subraum von C++.
Man kann auch rein imperativ mit C++ arbeiten, hat aber trotzdem den vorteil von Templates und echten Objekten. Zu mal du in C++ auch mit der STL noch Container und Algorithmen dazu bekommst.

Und SDL C++ Wrapper gibts bestimmt auch schon. Augenzwinkern

phlox



Geschrieben von Zirias am 14.05.2010 um 11:54:

 

Zitat:
Original von phlox81
C ist ein Subraum von C++.

Stimmt nicht so ganz.

Zitat:
Man kann auch rein imperativ mit C++ arbeiten, hat aber trotzdem den vorteil von Templates und echten Objekten. Zu mal du in C++ auch mit der STL noch Container und Algorithmen dazu bekommst.

- Man WILL in C++ nicht rein imperativ arbeiten.
- So etwas wie "unechte Objekte" existiert nicht.
- Mal die STL Implementierungen angeschaut? Das ist alles Komplexität, die man sich sparen kann, wenn man sie nicht braucht.

Zitat:
Und SDL C++ Wrapper gibts bestimmt auch schon. Augenzwinkern

Wahrscheinlich sogar etliche. Will ich aber nicht smile



Geschrieben von phlox81 am 14.05.2010 um 16:51:

 

Also als C++ler kann ich dich da natürlich nicht wirklich verstehen, aber auch in C kann man ja recht angenehm programmieren.

Zitat:
Original von Zirias
Zitat:
Original von phlox81
C ist ein Subraum von C++.

Stimmt nicht so ganz.

Würde aber aussreichen.

Zitat:
Original von Zirias
Zitat:
Man kann auch rein imperativ mit C++ arbeiten, hat aber trotzdem den vorteil von Templates und echten Objekten. Zu mal du in C++ auch mit der STL noch Container und Algorithmen dazu bekommst.

- Man WILL in C++ nicht rein imperativ arbeiten.
- So etwas wie "unechte Objekte" existiert nicht.
- Mal die STL Implementierungen angeschaut? Das ist alles Komplexität, die man sich sparen kann, wenn man sie nicht braucht.

Habe da wohl eine andere Denke als du. In C++ kann man sehr gut mischen, je nach dem was man für Anforderungen hat.
Zitat:
Original von Zirias
Zitat:
Und SDL C++ Wrapper gibts bestimmt auch schon. Augenzwinkern

Wahrscheinlich sogar etliche. Will ich aber nicht smile


Wird ja niemand gezwungen, und habe in dem Bereich auch nicht die riesen Erfahrung.
Für einen Freelancer ist das ja eigentlich eine brotlose Kunst, sprich, ich beschäftige mich meistens mit wichtigerem. Zeit für Spieleprogrammierung habe ich da keine mehr.

phlox



Geschrieben von Zirias am 11.09.2010 um 10:46:

 

Soo, das Modell hat sich in den letzten Wochen noch einen gewaltigen Schritt weiterentwickelt, und zwar gibt es jetzt eine spezielle klasse "Event", bei der sich EventHandler registrieren lassen, die bei einem RaiseEvent() alle nacheinander aufgerufen werden -- im Prinzip also eine Event-Verteilung wie man sie aus vielen Frameworks (z.B. auch in .NET) kennt. Die Schnittstelle sieht grob so aus:

code:
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:
/* Signatur eines Event-Handlers: */
typedef void (* EventHandler)(THIS, Object sender, void *eventData);

/* neues Event (normalerweise als member eines Objekts) erzeugen */
Event SomeEvent = CreateEvent();

/* Event löschen (im Destruktor) */
DestroyEvent(SomeEvent);

/* Handler registrieren: */
AddHandler(SomeEvent, this, &handlerMethod);

/* Handler wieder entfernen: */
RemoveHandler(SomeEvent, this, &handlerMethod);

/* Event auslösen: */
RaiseEvent(SomeEvent, (Object) this, eventData);

/* Event zurückziehen:
 * (falls schon EventHandler aufgerufen wurden wird die Verarbeitung trotzdem
 * abgebrochen, ein EventHandler kann also dafür sorgen, dass er der letzte ist,
 * der das Event zu sehen bekommt) */
CancelEvent(SomeEvent);


Damit das funktioniert muss das Programm irgendwann InitEvents() aufrufen und die Haupt-Ereignisschleife mit DoEventLoop() anspringen. Ein DoneEvents() beendet die Ereignisschleife und gibt alle Resourcen frei.

Im Moment setzt das ganze auf SDL auf, sämtliche Ereignisse werden als SDL_UserEvent in die Event-Queue von SDL eingestellt, andere SDL-Events werden über das global definierte "Event SDLEvent" propagiert -- man definiert also seinen eigenen Handler für SDL-Ereignisse mit "AddHandler(SDLEvent, this, &Event_SDLEvent);".

Ich würde das ganze jetzt doch gerne in eine eigene Library packen, allerdings missfällt mir ein wenig die enge Kopplung an SDL. GFibt es noch andere verbreitete C-Frameworks, die ein Schema der gleichen Art wie SDL für Ereignisse bieten (WaitEvent(*event) / PollEvent(*event)) ?


Forensoftware: Burning Board 2.3.6, entwickelt von WoltLab GmbH