amiga-news ENGLISH VERSION
.
Links| Forum| Kommentare| News melden
.
Chat| Umfragen| Newsticker| Archiv
.

amiga-news.de Forum > Programmierung > C++ Problem [ - Suche - Neue Beiträge - Registrieren - Login - ]

-1- 2 [ - Beitrag schreiben - ]

05.10.2006, 04:50 Uhr

DariusBrewka
Posts: 899
[Benutzer gesperrt]
Ich habe lange nichts mehr mit C++ gemacht, darum ein Beispiel wo Ich nicht weiß wie ich das lösen soll

code:
class ListC:private MinList
{
public:
ListC(void);
~ListC(void);
...
}

ListC::ListC()
{
NewList((List *)this);
}


MinList stammt aus "exec/lists.h" und NewList() stammt aus der exec.lib,

das Problem ist NewList() kann nur Funktionieren, wenn die MiniList am anfang der Struktur liegt,

d.h. wenn ein ListC c = new ListC() und &c->mln_Head identisch sind, was aber bei mir nicht der Fall ist, d.h. gibt es einen Weg den Compiler anzuweisen bei einer abgeleiteten Klasse die Basis an den Anfang zu legen ohne irgendwas davor zu schreiben.

Es ist ja nicht nur NewList() was hier in diesem Fall nicht funktionieren kann, auch die anderen Struktureinträge stimmen nicht überein. Man kann nicht mittels xyz_Succ Members in den nächsten Listeneintrag springen, da dieser in diesem Fall nicht auf den Anfang der Struktur zeigt.

[ - Antworten - Zitieren - Direktlink - ]

05.10.2006, 08:28 Uhr

Solar
Posts: 3680
Nutzer
Zitat:
Original von DariusBrewka:

es einen Weg den Compiler anzuweisen bei einer abgeleiteten Klasse die Basis an den Anfang zu legen ohne irgendwas davor zu schreiben.


Eventuell durch Compiler-spezifische Erweiterungen, aber nicht durch C++-Sprachmittel.

Ich kenne die von Dir benutzten Datenstrukturen nicht, aber Du solltest vielleicht über "ordentliche" Wrapper-Klassen nachdenken, die die C-Strukturen in C++-Objekte verpacken, ohne Annahmen über die interne Struktur von C++-Objekten zu machen.

[ - Antworten - Zitieren - Direktlink - ]

05.10.2006, 10:53 Uhr

DariusBrewka
Posts: 899
[Benutzer gesperrt]
Zitat:
Original von Solar:
Ich kenne die von Dir benutzten Datenstrukturen nicht, aber Du solltest vielleicht über "ordentliche" Wrapper-Klassen nachdenken, die die C-Strukturen in C++-Objekte verpacken, ohne Annahmen über die interne Struktur von C++-Objekten zu machen.


Es geht um DigitalAlmanac, welches Ich vor einiger Zeit auf AROS angefangen habe zu portieren aber wegen Zeitmangels ein wenig auf Eis gelegt habe. An einigen Stellen ist es abgeschmiert und Ich habe nun nachgeschaut woran das liegt und das liegt an dem Oben beschriebenen, daß ein Object erzeugt wird und dieses dann eine Adresse hat, und in eine Liste eingefügt wird. Wenn durch die Liste gegangen wird, dann ist die Objectadresse um 4 Bytes Verschoben.

Dort werden für die Listen SystemFunktionen benutzt und darum muß man Sich halt Gedanken um den internen Aufbau machen. Es stammt halt nicht von mir. Ich persönlich schreibe meine Programme IMMER so, daß der Compiler die Einträge nach belieben verschieben kann, also so, dass z.B. ein Eintrag der in der Struktur vor einem anderen steht, bei anderen Compilern dahinter stehen kann.

DA3 ist Codemäßig schon 1A, aber bei einem Konstrukt

code:
class ListC:struct MinList
{
}

bin Ich skeptisch.


[ - Antworten - Zitieren - Direktlink - ]

05.10.2006, 11:09 Uhr

Holger
Posts: 8116
Nutzer
Zitat:
Original von DariusBrewka:
DA3 ist Codemäßig schon 1A, aber bei einem Konstrukt

code:
class ListC:struct MinList
{
}

bin Ich skeptisch.

Na ja, class und struct sind für C++ trotzdem identisch, nur der default-Wert für die Sichtbarkeit (public/private) unterscheidet sich. Zumindest habe ich das mal irgendwann so gelernt.

Und bei class ListC:struct MinList müsste doch innerhalb der Klasse folgendes legal sein:
C++ code:
ListC::irgendeine_methode()
{
  MinList *list=this;

  funktion_die_MinList_erwartet(list);
}

Man beachte, dass es keinen type-cast benutzt. Wenn das nicht funktioniert, wozu ist dann C++ überhaupt gut?

mfg
--
Good coders do not comment. What was hard to write should be hard to read too.

[ - Antworten - Zitieren - Direktlink - ]

05.10.2006, 12:07 Uhr

DariusBrewka
Posts: 899
[Benutzer gesperrt]
@Holger

Vererbung heißt für mich, das die Eigenschaften einer Basisklasse übernommen werden, aber nicht daß in diesem Fall die MinList an Anfang der neuen Klasse stehen muß.

Und in diesem Fall unter dem AROS Compiler, welchen ich nutze, ist das definitiv nicht der Fall:

code:
class ClassC:struct MinList {
int a,
}


der Compiler hat u.A. folgende zwei Möglichkeiten die Klasse im Speicher zu hinterlegen.

(Typ1)
code:
struct MinList ml;
int a


oder (Typ2)

code:
int a
struct MinList ml;


oder 1000 andere, indem er irgendwelche Padding-Bytes einfügt. Wenn man auf die MinList Einträge mit normalen C(++) Mitteln zugreift gibt es da keine Problem, die Systemfunktionen kennen aber die internen Eigenschaften der neuen Klasse nicht, d.h. wenn der Zweite Typ von der Klasse intern erstellt wird dann macht Killt ein NewList(this) natürlich den Member a.

Bei meinem Beispiel wird zwischen der Adresse eines neu erzeugten Objects und der darin enthaltenen MinList noch ein ULONG (bzw. 4 Bytes eingefügt).

[ - Antworten - Zitieren - Direktlink - ]

05.10.2006, 13:35 Uhr

Solar
Posts: 3680
Nutzer
Zitat:
Original von DariusBrewka:

Wenn man auf die MinList Einträge mit normalen C(++) Mitteln zugreift gibt es da keine Problem, die Systemfunktionen kennen aber die internen Eigenschaften der neuen Klasse nicht...


Das ist der Grund, warum man für C-Strukturen geschriebene API-Funktionen nur mit äußerster Vorsicht auf C++-Objekte loslassen sollte, und besser Wrapper-Funktionen schreibt, die zwischen den Welten C und C++ vermitteln, ohne sich auf den internen Aufbau von Structs und Classes zu verlassen. Spätestens bei der Speicherverwaltung (malloc() vs. new) wird's nämlich kriminell.

Und, um Holgers Frage zu beantworten: C++ ist dafür gut, das man eben C++ schreibt, und nicht irgendwie C und C++ aneinanderzunageln versucht. Wenn der von Darius verwendete Code komplett in C++ wäre, hätte er das Problem nicht...

[ - Antworten - Zitieren - Direktlink - ]

05.10.2006, 21:48 Uhr

woop
Posts: 67
Nutzer
Zitat:
Original von DariusBrewka:

code:
class ListC:private MinList
{
public:
ListC(void);
~ListC(void);
...
}

ListC::ListC()
{
NewList((List *)this);
}


das Problem ist NewList() kann nur Funktionieren, wenn die MiniList am anfang der Struktur liegt,


Nein, du brauchst nur einen Zeiger auf die MinList-struct in deiner Klasse, aber der Kompiler weiß natürlich nicht, dass du diese haben willst, da du auf List* castest.

Unschön, schnell und schmutzig sollte der Aufruf
code:
NewList(reinterpret_cast<List*>(dynamic_cast<MinList*>(this)));

funktionieren. (Wenn du ohne Typinfo kompilierst kannst du statt dynamic_cast auch static_cast benutzen)

Besser wäre aber der Klasse ListC einen entsprechenden Casting-Operator auf List hinzuzufügen.

Außerdem kannst du das (void) bei C++ durch () ersetzen. Früher bzw. in C hat man das gebraucht um sicherzustellen, dass man einer Funktion nicht doch Parameter übergibt, aber selbst ältere C++-Kompiler lassen das nicht mehr zu.



[ Dieser Beitrag wurde von woop am 05.10.2006 um 21:52 Uhr geändert. ]

[ - Antworten - Zitieren - Direktlink - ]

06.10.2006, 01:47 Uhr

DariusBrewka
Posts: 899
[Benutzer gesperrt]
Zitat:
Original von Solar:
Wenn der von Darius verwendete Code komplett in C++ wäre, hätte er das Problem nicht...


Da bin Ich mir nicht sicher, ich habe die C Teile, darunter auch die OS Calls gegen C(++) Code getauscht, d.h. NewList als auch AddTail(), gegen den Code getauscht und dennoch geht das ganze Nicht.

code:
NodeC *ListC::head() const
{
return (NodeC *)mlh_Head;
}


ein

NodeC *n = list.head();

sollte die Erste Node liefern, es liefert aber die letzte Node. Woran das liegt weiß Ich nicht, habe mich mit Exec Listen noch nicht viel beschäftigt, aber der C++ Code an sich dürfte OK sein, schliesslich funktioniert DA3 auf 68k völlig Korrekt. Das einzige was mir einfallen würde, ist halt das zwischen den Listenelementen der Nodes und anfang der Klasse 4 Bytes liegen.

Das schöne an Exec Listen ist, daß man keine Sonderfälle betrachten muß wenn man Nodes hinzufügt / entfernt.

[ - Antworten - Zitieren - Direktlink - ]

06.10.2006, 03:30 Uhr

DariusBrewka
Posts: 899
[Benutzer gesperrt]
Ich habe jetzt wohl herausgefunden, warum das nicht geht und zwar wegen der Definition der Node und der Liste, die Member mln_Tail und mln_Tailpred definieren in der Liste eine Node, beim Initialisieren der Liste mittels NEWLIST() wird mlh_Head auf die Adresse von mln_Tail gesetzt

code:
this->mlh_Head     = (struct NodeC *) &this->mlh_Tail;


d.h. mlh_Head zeigt direkt auf den Member mlh_Tail bzw. dieses entspricht in der Node mln_Succ, dabei ist aber Vorausgesetzt dass die Node zwischen Objektanfang und der Adresse von mln_Succ keine Padding Bytes hat, ansonsten zeigt mlh_Head nicht auf den anfang der Node und damit das Objekt, sondern auf einen in diesem um 4 Bytes verschobenen Speicherbereich.

zum Testen habe ich obiges durch

code:
this->mlh_Head     = (struct NodeC *) &this->mlh_Tail-4;


ersetzt, dann ging das Ganze.

Exec Listen können somit nur Funktionieren, wenn die MinList, MinNode am anfang der Struktur steht, weil die Adresse von mln_Succ gleichzeitig die Adresse des Objektes ist, was natürlich nicht mehr der Fall sein kann, wenn vorher noch ein paar Bytes liegen.

[ - Antworten - Zitieren - Direktlink - ]

06.10.2006, 08:35 Uhr

Solar
Posts: 3680
Nutzer
Zitat:
Original von DariusBrewka:
Ich habe jetzt wohl herausgefunden, warum das nicht geht und zwar wegen der Definition der Node und der Liste...


Nein, wegen impliziter Annahmen über die Struktur eines C++-Objektes (anstatt die entsprechenden Casts und Wrapper zu benutzen). Aber hauptsache es geht, überzeugen wird man Dich wohl kaum können.

[ - Antworten - Zitieren - Direktlink - ]

06.10.2006, 10:52 Uhr

DariusBrewka
Posts: 899
[Benutzer gesperrt]
Zitat:
Original von Solar:
Zitat:
Original von DariusBrewka:
Ich habe jetzt wohl herausgefunden, warum das nicht geht und zwar wegen der Definition der Node und der Liste...


Nein, wegen impliziter Annahmen über die Struktur eines C++-Objektes (anstatt die entsprechenden Casts und Wrapper zu benutzen). Aber hauptsache es geht, überzeugen wird man Dich wohl kaum können.


was wird das jetzt?

In den RKMs steht das die Node's am Anfang der Strukturen stehen müssen, d.h. Offset=0, da wird auch kein Cast helfen, aber du kannst es ja mal Probieren und mich Überzeugen.

class_lists.h
code:
#ifndef CLASS_LIST_H
#define CLASS_LIST_H
#ifndef EXEC_LISTS_H
#include <exec/lists.h>
#endif

class ListC;

class NodeC //:private MinNode
{

// Eingefügt um vor der MinNode ein paar Bytes einzufügen
    ULONG dummy1
    struct MinNode *mln_Succ;
    struct MinNode *mln_Pred;
//

public:
NodeC(ListC &);
virtual ~NodeC(void);
void remove(void);
NodeC *succ(void) const;
NodeC *pred(void) const;
NodeC *succr(void) const;
NodeC *predr(void) const;
ListC *list(void) const;
unsigned getnum(void) const;
NodeC *operator++(int) const;
NodeC *operator--(int) const;
friend class ListC;
};

class ListC  //:private MinList
{
// einfgefügt um vor der MinList ein Padding LONG einzufügen
   ULONG dummy2;
   struct  MinNode *mlh_Head;
   struct  MinNode *mlh_Tail;
   struct  MinNode *mlh_TailPred;
//
public:
ListC(void);
~ListC(void);
void init(void);
void free(void);
void insert(NodeC *,NodeC *);
void addhead(NodeC *);
void addtail(NodeC *);
NodeC *remhead(void);
NodeC *remtail(void);
NodeC *getnode(unsigned) const;
unsigned count(void) const;
NodeC *head(void) const;
NodeC *tail(void) const;
operator void*(void) const;
friend class NodeC;
};

#endif


class_lists.c
code:
#include <clib/alib_protos.h>
#include <clib/exec_protos.h>
#include "class_list.h"

ListC::ListC()
{
// original NewList((List *)this);
NewList((List *)&this->mlh_Head;);
}

void ListC::free()
{
for(;;)
	{
	NodeC *n=(NodeC *)mlh_Head;
	if(!n->mln_Succ) break;
	delete n;
	}
}

ListC::~ListC()
{
free();
}

void ListC::addtail(NodeC *n)
{
// original AddTail((List *)this,(Node *)n);
AddTail((List *)&this->mlh_Head;,(Node *)n);
}

NodeC *ListC::head() const
{
return (NodeC *)mlh_Head;
}

NodeC::NodeC(ListC &list)
{
list.addtail(this);
}

NodeC::~NodeC()
{
Remove((Node *)this);
}

NodeC *NodeC::operator++(int) const
{
return (NodeC *)mln_Succ;
}


Ich würde mich freuen wenn du das hinbekomen würdest. Ich kann das nicht, aber wie gesagt mit den C++ Hardcore Internas kenne Ich mich nicht so aus.

[ - Antworten - Zitieren - Direktlink - ]

06.10.2006, 11:40 Uhr

Holger
Posts: 8116
Nutzer
Zitat:
Original von DariusBrewka:
@Holger
Vererbung heißt für mich, das die Eigenschaften einer Basisklasse übernommen werden, aber nicht daß in diesem Fall die MinList an Anfang der neuen Klasse stehen muß.

Das ist auch vollkommen egal. Wenn B eine Unterklasse von A ist, muss eine implizite Umwandlung eines Zeigers von B* nach A* auch um einen Offset verschieben, wenn der Datenteil von A in einem Objekt von Typ B nicht am Anfang steht. Würde der Compiler das nicht machen, würde C++ überhaupt nicht funktionieren.

Und die Exec-Listnodes müssen nicht am Anfang stehen. Die Exec-Funktionen interessieren sich ja gar nicht für die Daten, die davor oder dahinter stehen.

Dein Programm muss nur wissen, wie es zwischen einem Pointer auf ListNode und der Unterklasse konvertiert. Aber praktischerweise weiß das der C++-Compiler und macht das sogar vollautomatisch. Wenn man ihn nicht mit irgendwelchen Konstruktionen zu unterwandern versucht.

Zitat:
Original von Solar:
Und, um Holgers Frage zu beantworten: C++ ist dafür gut, das man eben C++ schreibt, und nicht irgendwie C und C++ aneinanderzunageln versucht. Wenn der von Darius verwendete Code komplett in C++ wäre, hätte er das Problem nicht...

Meine rethorische Frage bezog sich darauf, was wäre, wenn C++ nicht zwischen Pointern der Basisklasse und Unterklassen konvertieren könnte. Wenn ich also class X:class Y habe, und der Datenteil von Y nicht am Anfang von X liegt, was passiert, wenn ich einen Pointer X* einer Variablen vom Type Y* zuweise? Wird dann der Pointer um den Offset korrigiert? Ich würde das erwarten, schließlich wissen auch unter C++ Funktionen, die einen Pointer auf Y erwarten, nichts von X.

Und, nebenbei bemerkt, ist C++ die letzte Sprache, die dafür entwickelt wurde, eigenständig ohne C-Altlasten verwendet zu werden.
Zitat:
Original von DariusBrewka:
Ich habe jetzt wohl herausgefunden, warum das nicht geht und zwar wegen der Definition der Node und der Liste, die Member mln_Tail und mln_Tailpred definieren in der Liste eine Node, beim Initialisieren der Liste mittels NEWLIST() wird mlh_Head auf die Adresse von mln_Tail gesetzt

code:
this->mlh_Head     = (struct NodeC *) &this->mlh_Tail;


und wieso wird dort nicht auf ListNode, sondern auf die Unterklasse gecastet? An der Stelle muss, wenn überhaupt, ein reinterpret_cast erfolgen, weil ansonsten genau die Adresskonvertierung stattfindet, an die Du nicht glaubst. Allerdings geht es auch einfacher, wenn man nur auf ListNode castet, denn die besitzt ja keine eingebetteten oder geerbten Strukturen.

Zitat:
Original von DariusBrewka:
was wird das jetzt?

In den RKMs steht das die Node's am Anfang der Strukturen stehen müssen, d.h. Offset=0, da wird auch kein Cast helfen, aber du kannst es ja mal Probieren und mich Überzeugen.

class_lists.h
code:
#ifndef CLASS_LIST_H
#define CLASS_LIST_H
#ifndef EXEC_LISTS_H
#include <exec/lists.h>
#endif

class ListC;

class NodeC //:private MinNode
{

// Eingefügt um vor der MinNode ein paar Bytes einzufügen
    ULONG dummy1
    struct MinNode *mln_Succ;
    struct MinNode *mln_Pred;
//



Muss man das noch kommentieren? Was hat das mit Vererbung zu tun? Dass C++ korrekt zwischen Pointern der Basisklasse und den Unterklassen konvertiert, kannst Du mit dem Schwachsinn da oben nicht widerlegen.

mfg
--
Good coders do not comment. What was hard to write should be hard to read too.

[ - Antworten - Zitieren - Direktlink - ]

06.10.2006, 12:34 Uhr

DariusBrewka
Posts: 899
[Benutzer gesperrt]
@Holger

Naja, dann bin ich halt Doof weil Ich das nicht hinbekomme, egal was Ich mache. Die Definition der Liste ist ja oben gegeben und die Änderungen ebenfalls, lassen sich auch Rückgängig machen.

Es wäre schön wenn jamand mir sagt, was Ich ändern muß, damit das läuft, ansonsten lasse Ich das erstmal bis Ich wieder mehr Zeit habe.

[ - Antworten - Zitieren - Direktlink - ]

06.10.2006, 12:46 Uhr

Holger
Posts: 8116
Nutzer
Zitat:
Original von DariusBrewka:
@Holger

Naja, dann bin ich halt Doof weil Ich das nicht hinbekomme, egal was Ich mache. Die Definition der Liste ist ja oben gegeben und die Änderungen ebenfalls, lassen sich auch Rückgängig machen.


Die Definition der Liste ist nicht das Problem, sofern Du die meinst, die tatsächlich eine Unterklasse der Liste ist.

Hier mal ein kleines Beispiel für eine C++-Klasse, die auf einer primitiven C-Struktur einer einfach-verketteten Liste aufbaut. Wie man am Output sehen kann, unterscheiden sich die Adressen für die C-Struktur und die C++-Unterklasse. Aber der C++-Compiler konvertiert automatisch.

Selbst der Vergleich zwischen einem SubNode* und einem Node* Pointer in der remove Methode funktioniert, obwohl deren Zahlenwerte unterschiedlich sein müssten.
C++ code:
#include <stdio.h>

extern "C" {

  struct Node { struct Node *next; char *name; };

  /* old style functions */
  void add(struct Node *first, struct Node *toAdd)
  {
    printf("old: adding 0x%p %s\n", toAdd, toAdd->name);
    for(; first->next; first=first->next);
    first->next=toAdd;
    toAdd->next=NULL;
  }
  void printAll_old(struct Node *first)
  {
    printf("old: list( ");
    for(; first; first=first->next)
      printf("node( 0x%p %s) ", first, first->name);
    printf(")\n");
  }
}

// new style C++ function
template <class N> void printAll_new(N *first)
{
    printf("new: list( ");
    for(; first; first=first->next())
      printf("node( 0x%p %s) ", first, first->name);
    printf(")\n");
}

class SubNode:public Node
{
  public:
   SubNode(char* name) { this->name=name; Node::next=NULL; }
   virtual ~SubNode(){}
   SubNode *next() { return (SubNode*)Node::next; }
   bool remove(SubNode* node);
};

bool SubNode::remove(SubNode* node)
{
  printf("new: removing 0x%p %s\n", node, node->name);
  Node *n;
  for(n=this; n&&n->next!=node; n=n->next);
  if(!n) return false;
  n->Node::next=node->Node::next;
  node->Node::next=NULL;
  return true;
}
int main()
{
  SubNode one("one"), two("two"), three("three");

  add(&one, &two);
  add(&one, &three);

  printAll_old(&one);
  printAll_new(&one);

  one.remove(&two);
  one.remove(&three);

  printAll_old(&one);
  printAll_new(&one);
}


Wie schon gesagt, Du solltest type-casts vermeiden, wo sie unnötig sind. Und da, wo effektiv ein reinterpret_cast stattfindet, bei der Verlinkung des ListHeaders mit sich selbst, darfst Du auf keinen Fall auf die Unterklasse casten.

Und Du musst vermutlich sehr aufmerksam sein, wo die List-Funktionen womöglich als Preprozessor-Makros implementiert sind.

mfg
--
Good coders do not comment. What was hard to write should be hard to read too.

[ - Antworten - Zitieren - Direktlink - ]

06.10.2006, 15:26 Uhr

Solar
Posts: 3680
Nutzer
Zitat:
Original von DariusBrewka:

In den RKMs steht das die Node's am Anfang der Strukturen stehen müssen, d.h. Offset=0, da wird auch kein Cast helfen, aber du kannst es ja mal Probieren und mich Überzeugen.


Nochmal: Funktionen, die für C-Strukturen gedacht sind, läßt man besser nicht auf C++-Objekte los - denn es sind nun einmal zwei verschiedene Dinge. Eine C-Struktur hat bestenfalls Padding-Bytes; ein C++-Objekt hat eine von der ABI bestimmte interne Struktur, von der man besser die Finger läßt.

Zitat:
Ich kann das nicht, aber wie gesagt mit den C++ Hardcore Internas kenne Ich mich nicht so aus.

Ich habe keine Ahnung, was Du da eigentlich verhackstücken willst, und eigentlich weder Zeit noch Lust, mich einzulesen. Versuche nicht, von Exec verwaltete Listen-Nodes in C++-Objekte zu kapseln, das ist mein Rat.

[ - Antworten - Zitieren - Direktlink - ]

06.10.2006, 20:01 Uhr

malte2
Posts: 148
Nutzer
Schau mal bei sourceforge.net vorbei, dort findest Du die aktuellen DA3 sources, die sich auch mit gcc >= 3.x.x kompilieren lassen.

[ - Antworten - Zitieren - Direktlink - ]

06.10.2006, 21:21 Uhr

woop
Posts: 67
Nutzer
[quote]
Original von Solar:
Zitat:
Ich habe keine Ahnung, was Du da eigentlich verhackstücken willst, und eigentlich weder Zeit noch Lust, mich einzulesen. Versuche nicht, von Exec verwaltete Listen-Nodes in C++-Objekte zu kapseln, das ist mein Rat.

Weshalb denn das? C++ ist so entworfen, dass es zu C kompatibel sein kann, es wäre schlimm wenn das nicht so wäre. Ich habe mal extra ein kleines, zugegeben nicht sehr gehaltvolles Beispiel für Exec-Listen in C++-Objekten geschrieben, mit virtuellen Funktionen und Mehrfachvererbung; kein Problem:
http://eazy.amigager.de/~kai/ListTest.lha

Das entstand unter dem uralten Maxon C++4 PRO, da ich keinen anderen C++-Kompiler für Amiga schnell zur Verfügung habe. Da der keine C++-Casts kennt verwende ich da C-Style-Casts, was natürlich unschön ist und in einem echten Kompiler verbessert werden sollte.

[ - Antworten - Zitieren - Direktlink - ]

07.10.2006, 00:59 Uhr

DariusBrewka
Posts: 899
[Benutzer gesperrt]
@Holger

es ging mir nicht um Listen und Nodes in C++, sondern um das Konkrete Beispiel welches ich aus dem DA3 Source habe, diese Liste würde auch in C nicht funktionieren, wenn die Node im Objekt nicht bei Offset 0 beginnt. Das ganze wie Ich es hier habe funktioniert halt Problemlos in C++, wie schon gesagt, aber der Compiler der das Compiliert hat hat die beiden Node Einträge ans Offset 0 und 4 im Objekt gepackt. Ich versuche mal zu zeigen, warum kein Cast etc. egal ob C oder C++ bei Execlisten helfen kann.

nehme ein Object welches eine MiniNode inne hat

code:
struct MinNode
{
    struct MinNode * mln_Succ,
		   * mln_Pred;
};

struct TestNode {
 struct MinNode node;
}


und nun die zugehörige Listendefinition

code:
struct MinList
{
    struct MinNode * mlh_Head,
                   * mlh_Tail,
		   * mlh_TailPred;
};


wenn du dir das NEWLIST Makro anschaust

code:
#define NEWLIST(x) \
	{								\
		(x).lh_Head	= (struct Node *) &(x).lh_Tail;		\
		(x).lh_Tail	= NULL;					\
		(x).lh_TailPred	= (struct Node *) &(x).lh_Head;		\
	}


lh_Head soll somit auf eine Node zeigen, welche sich an der Adresse der Liste + Offset lh_Tail befindet, also eine in der Liste selber eingebettete Node, die also die Folgenden zwei Member hat

code:
* mlh_Tail,
		   * mlh_TailPred;


als Node, entspricht mlh_Tail nun mln_Succ und mlh_TeilPred mln_Pred.
An Offset 0 auf welches lh_Head zeigt liegt in der Liste also der Member mlh_Tail, wie auch mln_Succ an Offset 0 im Object liegt.

definiere die TestNode jetzt mal so,

code:
struct TestNode {
 ULONG dummy;
 struct MinNode node;
}


wenn du jetzt nach NewList() ml_Head ausliest zeigt dieses auf &mlh_Tail, da das ein Object vom Type Node ist, mln_Succ liegt aber in diesem Fall im Objekt wegen dem ULONG am Anfang nicht mehr an Offset 0, sondern am Offset 4. Dieses ULONG ist in der Liste aber nicht drinne, ein list->ml_Head->mln_Succ würde nicht am Offset 0 lesen (wo der Member in der List ist auf dem lh_Head zeigt), sondern am Offset 4, also anstatt mln_Tail mln_TeilPred.


@woop

Es geht nicht darum das man Exec Listen nicht in C++ verwenden kann, oder das man hin&her Casten kann, exec Listen gehen nicht wenn die member succ und pred der node nicht am Anfang des Objektes stehen, darum ist das auch kein C++ Problem.

[ - Antworten - Zitieren - Direktlink - ]

07.10.2006, 01:22 Uhr

malte2
Posts: 148
Nutzer
Wie gesgat, das Problem ist in den Sources von sourceforge.net nicht vorhanden.

[ - Antworten - Zitieren - Direktlink - ]

07.10.2006, 11:31 Uhr

Holger
Posts: 8116
Nutzer
Zitat:
Original von DariusBrewka:
@Holger

es ging mir nicht um Listen und Nodes in C++, sondern um das Konkrete Beispiel welches ich aus dem DA3 Source habe, diese Liste würde auch in C nicht funktionieren, wenn die Node im Objekt nicht bei Offset 0 beginnt.


Wenn Du nicht in der Lage bist, zu lesen und zu verstehen, wozu ein Beispiel dient, kann man Dir nicht helfen.

Ich habe Dir ein Beispiel gepostet, das eine C-Funktion mit einer Node enthält, die ganz genauso wie die exec-Funktionen von festen Bedingungen ausgeht. Außerdem enthält es eine C++-Klasse, die davon erbt und die Daten der Node nicht an Offset 0 enthält.

Das Beispiel zeigt, dass es mit Vererbung!!! funktioniert, weil C++ die Zeiger automatisch korrekt konvertiert.

Solange Du aber weiter Dünnschiss mit Offsets und irgendwelchen dummy-Variablen am Anfang irgendwelcher Strukturen, die nicht das Geringste mit der originalen ListNode-Struktur zu tun haben, von Dir gibst, wird man Dir nicht helfen können.

Selbst im AmigaOS gibt es Strukturen, bei denen die ListNode an einer anderen Position als am Anfang eingebettet ist.

mfg
--
Good coders do not comment. What was hard to write should be hard to read too.

[ - Antworten - Zitieren - Direktlink - ]

07.10.2006, 13:46 Uhr

Georg
Posts: 107
Nutzer
Zitat:
Original von malte2:
Wie gesgat, das Problem ist in den Sources von sourceforge.net nicht vorhanden.


In class_list.c gabs scheinbar ein paar Änderungen im Vergleich zu orig. DA3 Sourcen.

Neu:

code:
#include <clib/alib_protos.h>
#include <proto/exec.h>
#include "class_list.h"

ListC::ListC()
{
NewList((List *)this);
}

void ListC::free()
{
for(;;)
{
NodeC *n=(NodeC *)mlh_Head;
if(!n->mln_Succ) break;
delete n;
}
}

ListC::~ListC()
{
free();
}

void ListC::addtail(NodeC *n)
{
//AddTail((List *)this,(Node *)&n->minnode);
AddTail((List *)this,(Node *)&n->mln_Succ);
}

NodeC *ListC::head() const
{
return (NodeC *)mlh_Head;
}

NodeC::NodeC(ListC &list)
{
list.addtail(this);
}

NodeC::~NodeC()
{
Remove((Node *)&this->mln_Succ);
}

NodeC *NodeC::operator++(int) const
{
return (NodeC *)mln_Succ;
}


Alt:

code:
#include <clib/alib_protos.h>
#include <clib/exec_protos.h>
#include "class_list.h"

ListC::ListC()
{
NewList((List *)this);
}

void ListC::free()
{
for(;;)
	{
	NodeC *n=(NodeC *)mlh_Head;
	if(!n->mln_Succ) break;
	delete n;
	}
}

ListC::~ListC()
{
free();
}

void ListC::addtail(NodeC *n)
{
AddTail((List *)this,(Node *)n);
}

NodeC *ListC::head() const
{
return (NodeC *)mlh_Head;
}

NodeC::NodeC(ListC &list)
{
list.addtail(this);
}

NodeC::~NodeC()
{
Remove((Node *)this);
}

NodeC *NodeC::operator++(int) const
{
return (NodeC *)mln_Succ;
}






[ - Antworten - Zitieren - Direktlink - ]

07.10.2006, 13:51 Uhr

DariusBrewka
Posts: 899
[Benutzer gesperrt]
@Holger

Habe Ich gesagt das Ich mit Nodes Probleme habe? Das Beispiel was du geschrieben hast habe Ich auch selber gemacht und es hat Funktioniert, solange du nur Nodes nutzt dann hast DU kein Problem, sobald du aber eine List Struktur mit einbeziehst dann bekommst du das Problem.

Du kommst her und machst ein abgekürztes Beispiel was Funktioniert, so wie du es gemacht hast habe Ich es auch schon Tausendfach gemacht, es geht um die List Struktur und Nodes, nicht um NUR um Nodes. Es IST völlig Logisch, dass ich wenn ich bei einer Node den Succ Member lese Ich auch das Nächste Objekt bekomme, da ist es völlig EGAL wo die Node sich in dem Objekt befindet.

Würde Dein Beispiel nicht Funktionieren wäre es sehr sehr Dämlich.

Anbei wo werden im System Nodes verwendet die Nicht am Anfang der Struktur stehen, die auch in eine List-Struktur eingebettet sind?


[ - Antworten - Zitieren - Direktlink - ]

07.10.2006, 14:21 Uhr

DariusBrewka
Posts: 899
[Benutzer gesperrt]
@Georg, ja zwei in addtail, dort wird direkt auf den Succ Member gezeigt anstatt auf den Objektanfang und im Node-Destruktor.

Ich habe noch einige Tests gemacht und Herausgefunden das Holger in manchem doch Recht hat, wenn man von z.B. NodeC auf Node castet, dann bekommt man schon die Adresse der Node Struktur, darum geht das hier.

Das habe ich beim Cast nicht berücksichtigt, dass der auch die Adressen anpasst. Sorry.

[ - Antworten - Zitieren - Direktlink - ]

07.10.2006, 19:00 Uhr

woop
Posts: 67
Nutzer
Zitat:
Original von DariusBrewka:
Ich habe noch einige Tests gemacht und Herausgefunden das Holger in manchem doch Recht hat, wenn man von z.B. NodeC auf Node castet, dann bekommt man schon die Adresse der Node Struktur, darum geht das hier.

Das habe ich beim Cast nicht berücksichtigt, dass der auch die Adressen anpasst. Sorry.


Na endlich, genau das wollte ich dir ja schon mit meinem ersten Posting hier sagen und mit meinem Beispiel sagen, dort verwende ich ja auch die amiga.lib- und Exec-Funktionen um die Liste und die Nodes zu verwalten. ;)


[ - Antworten - Zitieren - Direktlink - ]

08.10.2006, 00:03 Uhr

DariusBrewka
Posts: 899
[Benutzer gesperrt]
Dann hätte Ich eigentlich aber gerade erwartet dass das auch ohne Cast gehen müsste.

[ - Antworten - Zitieren - Direktlink - ]

08.10.2006, 11:15 Uhr

Holger
Posts: 8116
Nutzer
Zitat:
Original von DariusBrewka:
Dann hätte Ich eigentlich aber gerade erwartet dass das auch ohne Cast gehen müsste.

Würde es ja auch. Wenn es sich nicht zum einen um ein Preprozessor-Makro handeln würde, das von sich aus schon brachial herumcastet, ohne sich für den echten Datentyp auch nur im geringsten zu interessieren und zum anderen um typische AmigaOS-Funktionen, die für "List*" und "Node*" deklariert sind, aber eben nicht für "MinList*" und "MinNode*".

Die würden ja selbst in C nicht ohne cast funktionieren. Aber das kennt man doch schon von der Amiga-C Programmierung, dass man im Zweifelsfall lieber noch einen weiteren cast hinzufügt.
Zitat:
Original von DariusBrewka:
Anbei wo werden im System Nodes verwendet die Nicht am Anfang der Struktur stehen, die auch in eine List-Struktur eingebettet sind?


BOOPSI-Gadgets zum Beispiel.

mfg

PS: Was ist an den Exec-Funktionen/Makros so toll, dass man sich lieber mit diesen Inkonsistenzen herumschlägt, statt die Zwei-/Dreizeiler noch mal als saubere C++-Methoden hinschreibt?

[ - Antworten - Zitieren - Direktlink - ]

08.10.2006, 12:32 Uhr

DariusBrewka
Posts: 899
[Benutzer gesperrt]
Zitat:
Original von Holger:
PS: Was ist an den Exec-Funktionen/Makros so toll, dass man sich lieber mit diesen Inkonsistenzen herumschlägt, statt die Zwei-/Dreizeiler noch mal als saubere C++-Methoden hinschreibt?


Ich habe die nochmal als Replacements hingeschrieben, aber Leider Falsch. Außerdem war der Sourcecode so

Was die Nodes in Listen angeht, habe ich in den Includes nichts gefunden, wo die Nodes nicht am Anfang stehen, in den Listen ist es völlig egal, wenn man zum Initialisieren ein &list->mlh_Head nutzt.

[ - Antworten - Zitieren - Direktlink - ]

09.10.2006, 10:18 Uhr

Solar
Posts: 3680
Nutzer
Zitat:
Original von woop:

C++ ist so entworfen, dass es zu C kompatibel sein kann, es wäre schlimm wenn das nicht so wäre.


Falsch: C++ wurde so entworfen, das gutes C gleichzeitig auch gültiges C++ ist. Es wurde nicht entworfen, um beliebig C- und C++-Semantik durcheinanderwerfen zu können. Es ist möglich, aber dazu muß man beide Sprachen wirklich verstanden haben - und meiner Erfahrung nach ist das gerade bei denen, die das versuchen wollen, nicht der Fall. (Denn wer C++ wirklich durchblickt hat, läßt von den typischen C-Semantiken die Finger.)

Oder, wie jemand anders es so schön ausdrückte: "Real Programmers can write Fortran in any language."

I-)

Schön, das Darius' Problem sich jetzt "verflüchtigt" hat.

[ - Antworten - Zitieren - Direktlink - ]

09.10.2006, 15:55 Uhr

Holger
Posts: 8116
Nutzer
Zitat:
Original von DariusBrewka:
Was die Nodes in Listen angeht, habe ich in den Includes nichts gefunden, wo die Nodes nicht am Anfang stehen, in den Listen ist es völlig egal, wenn man zum Initialisieren ein &list->mlh_Head nutzt.

Wirst Du in den includes auch nicht finden. Den Automatismus von C++, der die Offsets korrigiert, gibt es in C ja nicht. Deshalb sind alle Strukturen, wo so etwas passiert, privat und man kann nur mit den zugehörigen OS-Funktionen iterieren. Das betrifft hauptsächlich Strukturen, die entweder unter AOS1.x noch nicht existierten oder zumindest erst in einer späteren OS-Version die List-Node Fähigkeit bekommen haben.

Das BOOPSI-Gadget Beispiel habe ich Dir ja schon genannt. Für die CLI-Struktur würde ich es auch vermuten, ist aber schwer zu überprüfen, da, wie gesagt, diese neuen Eigenschaften nicht mehr öffentlich sind.
Da haben die AmigaOS-Entwickler mal dazugerlernt.

mfg
--
Good coders do not comment. What was hard to write should be hard to read too.

[ - Antworten - Zitieren - Direktlink - ]

09.10.2006, 16:32 Uhr

Georg
Posts: 107
Nutzer
Nodes die nicht am Anfang der Struktur liegen (In OS Includes ist das z. B. in midi/camd.h zu finden) machen schon ab und zu Sinn. Z. B. wenn man Dinge doppelt verlinkt haben will, also in 2 oder mehr LIsten gleichzeitig. Z. B. könnte man nen MyExtendedMsgPort mit hinten dran nem weiteren Node machen und dann den Port mit exec/AddPort() öffentlich machen (MsgPort Node wird benutzt)) und zusätzlich den Port über den 2. Node in eine private MeinePublicPorts Liste eintragen.

Wenn man diese 2. Liste durchwandert bekommt man natürlich nicht Zeiger auf den Struktur-Anfang (MyExtendedMsgPort), sondern auf den
entsprechenden Node der beim Einfügen in die Liste angegeben wurde.
Deshalb muß man die Adresse "berechnen":

code:
p = (struct MyExtendedMsgPort *)(((UBYTE *)node) - offsetof(struct MyExtendedMsgPort, meinNode));



[ - Antworten - Zitieren - Direktlink - ]


-1- 2 [ - Beitrag schreiben - ]


amiga-news.de Forum > Programmierung > C++ Problem [ - Suche - Neue Beiträge - Registrieren - Login - ]


.
Impressum | Datenschutzerklärung | Netiquette | Werbung | Kontakt
Copyright © 1998-2024 by amiga-news.de - alle Rechte vorbehalten.
.