Du möchtest Mitglied in diesem Forum werden? Bitte setze dich dazu mit der Forenleitung in Verbindung. Ganz unten auf dieser Seite gibt es eine Kontaktmöglichkeit.

Programmieren C++

Hier kann man alles posten, was nicht ganz zum Thema passt - Hauptsache es ist interessant!
Skeltek
Ehrenmitglied
Ehrenmitglied
Beiträge: 2887
Registriert: 25. Mär 2008, 23:51

Programmieren C++

Beitrag von Skeltek » 14. Jul 2017, 06:45

Hi,

habe gerade einige Projekte laufen und es ist extrem lange her, seit ich meine 3d-Simulation-Engine in C++ programmiert habe.
Mittlerweile möchte ich meine C++ Kenntnisse (auch fürs Studium) etwas vertiefen, aber sogar einfachere Aufgaben bereiten mir mittlerweile einige Probleme (bin wohl zu viele Jahre lange auf Java gefahren).

Falls sich jemand gut mit C++ auskennt, wäre ich froh, wenn jemand vielleicht mit ein paar rudimentären Kniffen und Tipps etwas aushelfen könnte.
Gegebenenfalls würde ich später in den Semesterferien auch wieder ein größeres Projekt angehen, falls jemand Zeit und Lust hat mitzuwirken.
Skype/Teamspeak/Teamviewer oder sonstige VoIP-Software wäre für Kommunikation denke ich sehr hilfreich und angemessen.

Viele Grüße, Skel
Die plausibelste Erklaerung jedes hinreichend komplizierten Systems ist falsch

Unentscheidbarkeit für Dummies: Dieser Satz ist wahr
oder
Diese Menge hat zwei Elemente: A und B

Benutzeravatar
tomS
Administrator
Administrator
Beiträge: 8726
Registriert: 19. Nov 2007, 20:29
Wohnort: Nürnberg

Re: Programmieren C++

Beitrag von tomS » 14. Jul 2017, 23:33

Ich habe früher sehr viel in C++ programmiert, bin aber inzwischen etwas eingerostet.

Wenn du in Java fit bist, dann programmiere wie in Java - das ist auch in C++ eine vernünftige Struktur (also lass' die C-artigen Tricks, und vermeide vorerst die STL).
Gruß
Tom

Ἓν οἶδα, ὅτι οὐδὲν οἶδα.

Skeltek
Ehrenmitglied
Ehrenmitglied
Beiträge: 2887
Registriert: 25. Mär 2008, 23:51

Re: Programmieren C++

Beitrag von Skeltek » 15. Jul 2017, 00:39

Ja, nur in Java gibt es keine Pointer ^^
Habe gerade das Problem, dass ich eine Bibliothek dynamisch mit einer Hilfsklasse laden soll, sodass sie beim Löschen des Objektes automatisch wieder entladen wird.
Es gibt glaube ich wenig grausameres als etwas wirklich schweres machen zu müssen und wirklich keinen einzigen zu haben, den man fragen könnte... wie viele Tage sind da schon dabei drauf gegangen, einfach nur weil der Stoff auf Stackexchange usw zu schlecht beschrieben wird.
Die plausibelste Erklaerung jedes hinreichend komplizierten Systems ist falsch

Unentscheidbarkeit für Dummies: Dieser Satz ist wahr
oder
Diese Menge hat zwei Elemente: A und B

Benutzeravatar
tomS
Administrator
Administrator
Beiträge: 8726
Registriert: 19. Nov 2007, 20:29
Wohnort: Nürnberg

Re: Programmieren C++

Beitrag von tomS » 15. Jul 2017, 09:24

Arbeitest du unter Windows oder Linux?

Für Windows kannst du dir den Mechanismus von DCOM anschauen.

Generell würde ich wie folgt vorgehen: eine Klasse cLibMngr, die die Bibliothek verwaltet.
Erzeugen eines Objektes aus der Bibliothek erfolgt mittels überladener Konstruktoren cXXX:cXXX() je Klasse cXXX in der Bibliothek.
Bei jeder Erzeugung wird in cXXX:cXXX() eine statische Membervariable cXXX::_sInstanceCount inkrementiert, also ++_sInstanceCount.
Bei jeder Vernichtung wird in cXXX::~cXXX() dieser Zähler dekrenentiert, also --_sInstanceCount.

Wenn alle cXXX::_sInstanceCount gleich Null sind, dann wird die Bibliothek entladen.

In den cLibMngr kannst du eine Factory je Klasse reinpacken, ein cLibMngr::createXXX(), der dann mittels new cXXX das eigtl. Objekt liefert.

Dein Problem ist, dass das Laden der Bibliothek mittels LoadLibrary() aus dem Win32 API vor der Verwendung der Methoden in der Library oder in der Klassen erfolgen muss. Ich verstehe dein Problem noch nicht genau, aber evtl. kannst du deswegen die o.g. Verwaltung des _sInstanceCount gerade nicht in die Klassen packen.

DCOM packt diese Verwaltung in eine globale Variable in der DLL. Wenn der Zähler auf Null zurückgeht, dann kann die Bibliothek entladen werden. Das ist eine Eigenschaft der Bibliothek selbst, nicht eine Verwaltung im Client (der allerdings sauber mit Referenzen auf Objekte umgehen muss).

So wie ich dich verstehe, soll diese Verwaltung aber im Client ablaufen, also eine Wrapperklasse im Client, nicht in der DLL, die das LoadLibrary() kapselt und die Zähler verwaltet.

Um dir genauer helfen zu können, müsste ich mehr Details kennen.
Soll die Verwaltung in den Client oder in die DLL?
Welche Klassen und Methoden werden in der DLL zur Verfügung gestellt? Wie werden diese nach außen sichtbar gemacht?
Muss das ganze threadsafe sein?
Ganz wichtig: soll das Ganze wie bei DCOM mittels Win32 API, also LoadLibrary implementiert werden? So hatte ich dich verstanden.

Eine andere Frage ist, wie du die Pointer auf die Objekte verwaltest: sind das native C++ Pointer? Oder verendest du Auto-Pointer o.ä.?

Leider hast du da ein Problem, das erstens nicht alleine C++ betrifft, sondern auch das Betriebssystem, und für das zweitens diverse Lösungsansätze denkbar sind.
Gruß
Tom

Ἓν οἶδα, ὅτι οὐδὲν οἶδα.

Skeltek
Ehrenmitglied
Ehrenmitglied
Beiträge: 2887
Registriert: 25. Mär 2008, 23:51

Re: Programmieren C++

Beitrag von Skeltek » 15. Jul 2017, 16:57

Arbeite unter Windows10, Netbeans mit Cygwin.
Das Programm soll aber sowohl unter Windows als auch Linux laufen.

MyLib1.h:
#include <cstdlib>
#include <cstdarg>
//#pragma comment(lib, "MyLib1.dll")

typedef void (*Efp)(const char *fmt, va_list args); //int noch ändern

class __declspec(ddlexport) MyLib1{
public:
static Efp efp;
static void initializeCallback(void (*)(const char*, va_list));
//void(*callSomeFunction)(int, const char *);// **
static void printLogEx(const char*, ...);
MyLib1();
MyLib1(const MyLib1& orig);
virtual ~MyLib1();
private:
};


Problem1:
MyLib1.h:22:29: warning: 'ddlexport' attribute directive ignored [-Wattributes]
Problem2:
Darüberhinaus habe ich das Problem, dass in einer anderen Klasse die MyLib2 zwar geladen wird, er aber die Funktionsnames-Auflösung Null ergibt (die Funktionsnamen sind trotz _declspec(dllimport) nur über ihre decorated Namen abrufbar; die hab ich mit DependencyWalker ermittelt)
Problem3:
BeliebigeKlasseDefinition* myKonstruktor = (BeliebigeKlasseDefinition*)GetProcAddress(pData->myBib, name);
Im Hauptprogram bekomme ich falls ich die dekorated Namen der Funktion bekomme statt NULL den Wert 1 zurück, egal welchen Funktionsnamen ich abrufe.

Falls du Skype oder Teamviewer hast, kannst du gerne mal einen Blick darauf werfen.
Muss für zwei Prüfungen am Montag lernen und hab eigentlich keine Zeit nochmal 12 Stunden hier hinein zu investieren. Das C++ Programm hätte eigentlich vor einer Woche fertig sein sollen, musste ich aber wegen massiven Zeitmangels aufschieben.
Die plausibelste Erklaerung jedes hinreichend komplizierten Systems ist falsch

Unentscheidbarkeit für Dummies: Dieser Satz ist wahr
oder
Diese Menge hat zwei Elemente: A und B

Pippen
Ehrenmitglied
Ehrenmitglied
Beiträge: 1450
Registriert: 9. Jul 2010, 04:02

Re: Programmieren C++

Beitrag von Pippen » 16. Jul 2017, 21:35

Wenn ihr so programmiert - wie genau überlegt ihr euch vorher das Programm und wieviel wird spontan gemacht (mal unter der Annahme, es handelt sich um ein nicht-triviales Programm)? Ich frage mich immer, ob Programmierer quasi den Code schon im Kopf haben, wenn sie den Editor öffnen und dann "nur" noch alles in richtiger Form eintippen müssen oder ob sie eher nur das Gerüst im Kopf haben und daher während des Code-Schreibens noch richtig über den Code nachdenken müssen.

Benutzeravatar
tomS
Administrator
Administrator
Beiträge: 8726
Registriert: 19. Nov 2007, 20:29
Wohnort: Nürnberg

Re: Programmieren C++

Beitrag von tomS » 17. Jul 2017, 07:08

Hallo Skeltek,

zu der Frage bzgl. class __declspec(dllexport) CExampleExport : ..., der Aufrufkonventionen u.a. MS-spezifischer Themen solltest solltest du die MSDN befragen; da gibt aus Beispiele. Grundsätzlich sieht das aber vernünftig aus.

Wie möchtest du die exportierten Klassen bzw. Methoden dann in deiner Applikation verwenden? Das sehe ich noch nicht.

Dein Beispiel zeigt noch nicht das, was du unten geschrieben hast: "dass eine Bibliothek dynamisch mit einer Hilfsklasse geladen werden soll, sodass sie beim Löschen des Objektes automatisch wieder entladen wird". Was du da zeigst ist eine ganz normale DLL, die beim Programmstart bzw. -ende geladen bzw. entladen wird, unabhängig von der tatsächlichen Instantiierung der Objekte.
Gruß
Tom

Ἓν οἶδα, ὅτι οὐδὲν οἶδα.

Benutzeravatar
tomS
Administrator
Administrator
Beiträge: 8726
Registriert: 19. Nov 2007, 20:29
Wohnort: Nürnberg

Re: Programmieren C++

Beitrag von tomS » 17. Jul 2017, 07:13

Pippen hat geschrieben:
16. Jul 2017, 21:35
Ich frage mich immer, ob Programmierer quasi den Code schon im Kopf haben, wenn sie den Editor öffnen und dann "nur" noch alles in richtiger Form eintippen müssen oder ob sie eher nur das Gerüst im Kopf haben und daher während des Code-Schreibens noch richtig über den Code nachdenken müssen.
Sehr gute Programmierer, die das Gesamtkonzept verstanden haben und sich innerhalb des vertrauten Terrains bewegen und/oder im Flow sind, schreiben tatsächlich nur noch Code hin. Das gilt auch für Komponisten, teilweise auch für einige wenige Physiker (Witten soll dazugehören).
Gruß
Tom

Ἓν οἶδα, ὅτι οὐδὲν οἶδα.

Pippen
Ehrenmitglied
Ehrenmitglied
Beiträge: 1450
Registriert: 9. Jul 2010, 04:02

Re: Programmieren C++

Beitrag von Pippen » 17. Jul 2017, 20:38

@tomS: Das heißt aber: Die übrigen haben nur das grobe Gerüst im Kopf und machen den Rest "on the fly"? Das ist so ein bisschen ein Grundproblem der Arbeitsmethode, die mich interessiert: Macht man die Vorüberlegungen so genau, dass man anschließend quasi den Plan nur noch abarbeitet oder ist man bei den Vorüberlegungenn nicht ganz so genau, muss dann aber während der eigentlichen Arbeitsausführung noch über gewisse Dinge nachdenken (was die Fehlerquote beim Codeschreiben erhöht, weil man sich aufs Codeschreiben und diverse Problellösungen gleichzeitig konzentrieren muss) und hat immer das Risiko, dass sich ein neues Problem auftut - dafür kann man freilich schneller anfangen. Gibt's da Richtlinien beim Programmieren?

Benutzeravatar
tomS
Administrator
Administrator
Beiträge: 8726
Registriert: 19. Nov 2007, 20:29
Wohnort: Nürnberg

Re: Programmieren C++

Beitrag von tomS » 17. Jul 2017, 21:17

Die Grundvoraussetzung ist strukturiertes und hierarchisches Denken, d.h. die Zerlegung des Problems in sinnvolle Teilprobleme.

Die erste und wichtigste Richtlinie ist Übung bzw. Praxis. Zum einen muss man insgs. lange genug SW entwickeln, zum anderen muss man lange genug an ähnlich gelagerten Problemen in einem ähnlichen Kontext arbeiten.

Wie tief man in die Details gehen muss ist eine gute Frage; ich sehe da keine allgemeingültige Antwort. Die wesentlichen architekturiellen Randbedingungen müssen stehen und stabil sein. Im Bereich objektorientierter Programmierung bedeutet das, dass zumindest die architekturiellen Pattern stehen müssen.

Ansonsten kann und muss man sich Vorgehensweisen aneignen, also seinen persönlichen Entwicklungsprozess finden. Bsp.: modul-orientiert (z.B. DB, Server, Client), feature-orientiert (je Feature über alle drei Schichten), ...

Es gibt ein paar Empfehlungen, was man sich anschauen sollte:

https://de.wikipedia.org/wiki/Clean_Code

https://en.wikipedia.org/wiki/Separation_of_concerns
https://en.wikipedia.org/wiki/Cohesion_ ... r_science)
https://en.wikipedia.org/wiki/Don%27t_repeat_yourself

https://en.wikipedia.org/wiki/Software_craftsmanship

https://en.wikipedia.org/wiki/Software_design_pattern

https://en.wikipedia.org/wiki/Category:Anti-patterns

https://en.wikipedia.org/wiki/List_of_s ... ilosophies

Aber häppchenweise!
Gruß
Tom

Ἓν οἶδα, ὅτι οὐδὲν οἶδα.

Skeltek
Ehrenmitglied
Ehrenmitglied
Beiträge: 2887
Registriert: 25. Mär 2008, 23:51

Re: Programmieren C++

Beitrag von Skeltek » 18. Jul 2017, 01:46

@Pippen:
Habe meist das grobe Gerüst im Kopf, man kann soweit den 'Baum' im Kopf durchplanen, bis man bei den tiefsten 'Wurzeln' ankommt.
Man geht nach Prinzipien und strenger Deduktion vor.
Die Ausführung wird meist davon behindert, dass man nicht weiß, was für Werkzeuge man zur Verfügung hat, wie die Werkzeuge heißen, wie sie funktionieren und welche Kniffe die unbekannte Programmiersprache noch fordert, zu denen es praktisch kaum Dokumentation oder Hilfe gibt.
Aber prinzipiel gehorcht jedes Programm systematisch Prozeduren und kausalen Kettenreaktionen (früher linear verknotet, heute als mehrere Threads oder gar Flächen). Das alles ist ersteinmal im Kopf.

Hab vor 2 Jahren eine 3d Engine programmiert, wie bei den meisten EgoShootern... und ich hatte zu dem Zeitpunkt weder ahnung von C noch von OpenGL oder gar dem Wort Collection :D
Ich hab meine gesamte Objektverwaltung und Collections selbst geschrieben, weil ich keine Ahnung davon hatte, daß es sowas schon irgendwo implementiert gab.
Das große Problem, ist, daß zwar andere für alle gängigen Probleme welche man hat bereits irgendwo eine Bibliothek mit einer bereits guten und extrem zuverlässigen und schnellen Lösung gemacht haben,
aber leider man keine Ahnung hat wo diese sind, und wie diese sich funktionieren.
Man kann sich zwar durch die ganze entwicklung der letzten 30 Jahre selbst durchprogrammieren ohne Probleme und auch neue Sachen machen, allerdings ist das extrem zeitaufwendig und falls Fehler dring sind extrem schwer zu korrigieren.

Letztich beschäftige ich mich 95% meiner Zeit damit Befehle und Konventionen nachzuschlagen und wenn ich die Werkzeuge zusammen habe und weiß wie sie funktionieren, tippe ich den Code am Stück fehlerfrei herunter(Andere programmieren drauf los, und verbringen 90% der Zeit mit Fehlersuche oder Umstrukturierung).

@tomS:
Hab es mittlerweile gelöst.
MyLib2 hab ich oben gar nicht gepostet, da sämmtlicher relevante Code den Rahmen gesprengt hätte und für das Problem nicht relevant schien.
Obige MyLib1 wird erst in der main-Methode am Anfang geladen, hatte nur das Problem, dass Konstruktoren speziell behandelt werden müssen, da sie keinen Wert oder Objekt zurückgeben - nicht einmal void.
Wenn man sich einen Pointer auf einen Konstruktor holen würde, kann man damit kein Objekt erzeugen und an eine Variable binden/refferenzieren.
Drei Fälle:
-Pointer zu Konstruktor hat keinen Rückgabewert, daher geht folgender Code nicht: MyObject o(); //Es gibt keinen Verweis oder sonst etwas auf den Konstruktor aus der Bibliothek
-Folgender Code geht nicht: MyObject o =*fp(); //Konstruktor hat keinen Rückgabewert, auch nicht das Objekt. Mit fp als Verknüpfung zum Konstruktor
-Der folgende Code kürzt eine ganze Kette an Aufrufen ab und geht daher auch nicht: MyObject o = *fp(); //Kürzt folgenden Code ab:
1. Erstellt temporäres Objekt t (verwendet Konstruktor)
2. Tauscht Referenzen von erzeugtem Objekt t und Variable o //Was aber auch nicht geht, weil kein Zugriff auf Definition von überladenem =Operator des eigenen Structs/Klasse
3. Vernichtet, wasauchimmer vorher unter o referenziert war (braucht Destruktor)
Generell braucht man für letzteres alle 3 Definitionen, man hat aber nur den FunctionPointer auf eine Funktion oder Konstruktor... Ein Workaround in der Bibliothek selbst musste her.

ES wird ein Objekt erstellt, welches den Handle der geladenen Bibliothek in einer privaten Objektvariable abspeichert. Sobald das Objekt zerstört wird, wird die Bibliothek entladen.
Die plausibelste Erklaerung jedes hinreichend komplizierten Systems ist falsch

Unentscheidbarkeit für Dummies: Dieser Satz ist wahr
oder
Diese Menge hat zwei Elemente: A und B

Benutzeravatar
tomS
Administrator
Administrator
Beiträge: 8726
Registriert: 19. Nov 2007, 20:29
Wohnort: Nürnberg

Re: Programmieren C++

Beitrag von tomS » 18. Jul 2017, 07:17

Warum möchtest du einen Konstruktor wie eine Methode aufrufen, also myClass()? Das ist nicht sinnvoll, denn zum Zeitpunkt des Aufrufs hättest du noch gar kein Objekt, über das die Methode aufgerufen werden kann. Konstruktoren sind etwas anderes als normale Methoden.

Du benötigst keinen Func-Ptr auf einen Konstruktor; wozu soll das gut sein? Das entspricht nicht C++ und hat außerdem nichts mit deinem Problem der DLL zu tun, d.h. ist passt schon dann nicht, wenn's im selben File steht.

Wenn du die Konstruktoren richtig implementiert (und der Export stimmt), dann sollten folgende Aufrufe funktionieren:

myClass o1; // default Konstruktor
myClass *p_o2 = new myClass; // default Konstruktor mittels new-Operator für Pointer
myClass o3( o2 ); // copy-Konstruktor erstellt Kopie von o1
myClass *p_o3 = new myClass( *p_o2 ); // copy-Konstruktor erstellt Kopie des Objektes, auf das p_o2 verweist

Weitere Konstruktoren können sinnvoll sein.

Wenn bestimmte Konstruktoren explizit verboten sein sollen, dann werden sie private deklariert und nicht implementiert; dadurch stehen sie weder in der Basisklasse noch in abgeleiteten Klassen zur Verfügung. Das ist guter Stil, und folgt dem Prinzip der geringstmöglichen Überraschung: entweder tut der Konstruktor genau das, was man erwartet, oder man erhält beim Compilieren einen Fehler.
Gruß
Tom

Ἓν οἶδα, ὅτι οὐδὲν οἶδα.

Benutzeravatar
positronium
Ehrenmitglied
Ehrenmitglied
Beiträge: 2524
Registriert: 2. Feb 2011, 20:13

Re: Programmieren C++

Beitrag von positronium » 18. Jul 2017, 12:09

Pippen hat geschrieben:
17. Jul 2017, 20:38
Das heißt aber: Die übrigen haben nur das grobe Gerüst im Kopf und machen den Rest "on the fly"?
Jeder hat nur das grobe Gerüst im Kopf; wie dieses auszufüllen ist, weiss man einfach. D.h. man schreibt das alles fast wie in einem Trancezustand einfach hin.
Zäh wird das Arbeiten vor allem dann, wenn man eine neue Bibliothek benutzen muss, oder man eine neue Sprache erlernt, deren Möglichkeiten man noch nicht kennt.

Skeltek
Ehrenmitglied
Ehrenmitglied
Beiträge: 2887
Registriert: 25. Mär 2008, 23:51

Re: Programmieren C++

Beitrag von Skeltek » 18. Jul 2017, 23:14

tomS hat geschrieben:
18. Jul 2017, 07:17
Warum möchtest du einen Konstruktor wie eine Methode aufrufen, also myClass()? Das ist nicht sinnvoll, denn zum Zeitpunkt des Aufrufs hättest du noch gar kein Objekt, über das die Methode aufgerufen werden kann. Konstruktoren sind etwas anderes als normale Methoden.
Zur Compilezeit ist die Klassendefinition nicht bekannt. Typedefs ohne Parameter nimmt der Compiler nicht an als Function-Pointer-Prototyp.
tomS hat geschrieben: myClass o1; // default Konstruktor
Für mich sieht das eher wie eine Variablen-Deklaration des Typs myClass aus, wo ist das die Initialisierung?
tomS hat geschrieben: myClass *p_o2 = new myClass; // default Konstruktor mittels new-Operator für Pointer
Der Aufruf benötigt Zugriff und Kenntniss des Copy-Construktors und des Destruktors. Zur Compilezeit des Hauptprograms sind diese doch gar nicht bekannt?
Wie soll er die verketten?
tomS hat geschrieben: myClass o3( o2 ); // copy-Konstruktor erstellt Kopie von o1
myClass *p_o3 = new myClass( *p_o2 ); // copy-Konstruktor erstellt Kopie des Objektes, auf das p_o2 verweist

Weitere Konstruktoren können sinnvoll sein.

Wenn bestimmte Konstruktoren explizit verboten sein sollen, dann werden sie private deklariert und nicht implementiert; dadurch stehen sie weder in der Basisklasse noch in abgeleiteten Klassen zur Verfügung. Das ist guter Stil, und folgt dem Prinzip der geringstmöglichen Überraschung: entweder tut der Konstruktor genau das, was man erwartet, oder man erhält beim Compilieren einen Fehler.
Bevor ich zum letzten Absatz etwas frage, müsstest du mir erst erklären, wie er dein mClass o1 als Konstruktor auffasst?
Bei geladenen Bibliotheken muss man die Funktionen ja erst durch ein
GetProcAddress([myBibName], [functionName]) finden uns sich einen Pointer darauf zurück geben lassen.
Wo willst du diesen denn speichern, und wieso sollte dein obiger Konstruktor diesen functionPointer kennen?
tomS hat geschrieben: Du benötigst keinen Func-Ptr auf einen Konstruktor; wozu soll das gut sein?
Wieso sollte man einen brauchen wenn man Funktionen aufruft und keinen brauchen wenn man einen Konstruktor aufruft?
Das Objekt(z.B. ein Server) kennt die Funktionen der dnamisch geladenen Bibliothek nicht, die befinden sich ja in einem völlig anderen Binary.
Man kann höchstens mit dem Interface die Referenz der Funktion abfragen und dann eine Verknüpfung zu der Funktionalität im lokalen Program abspeichern.
Wie soll dein Konstruktor als solcher denn lokal erkannt werden, wenn du nur eine Verknüpfung via GetProcedureAdress abrufst?
Die plausibelste Erklaerung jedes hinreichend komplizierten Systems ist falsch

Unentscheidbarkeit für Dummies: Dieser Satz ist wahr
oder
Diese Menge hat zwei Elemente: A und B

Benutzeravatar
tomS
Administrator
Administrator
Beiträge: 8726
Registriert: 19. Nov 2007, 20:29
Wohnort: Nürnberg

Re: Programmieren C++

Beitrag von tomS » 22. Jul 2017, 08:44

Hallo Skeltek,

ich verstehe immer noch nicht, wo dein Problem ist.

Zu ein paar Punkten:

myClass o1; // default Konstruktor

ist in C++ die Deklaration eines Objektes o1 des Typs myClass, vergleichbar zu

int n;

Generell wird in C++ der Konstruktor nie explizit aufgerufen; das erledigt immer der Compiler implizit.

Im Falle einer Klasse wird jedoch der default-Konstruktor automatisch aufgerufen. Das kannst du ändern, wenn du einen eigenen Konstruktor

myClass:myClass( ... )
{
...
}

implementierst und die Deklaration oben mit den geeigneten Argumenten ... vornimmst

myClass o1( ... ); // dein eigener Konstruktor.

Die Konstruktion eines Objektes mittels

myClass o1( ... );
myClass * p_o2 = new MyClass( ... )

funktioniert ganz normal. Der Typ des Objektes ist zur Compilezeit natürlich bekannt, nämlich MyClass. Das reicht dem Compiler aus.

In C++ benötigst du dazu zunächst mal überhaupt keine Member-FuncPtr. Ein Pointer auf das Objekt ist völlig ausreichend:

p_o2->someMemberFunc()

Das funktioniert nicht für den Konstruktor; für den gibt es Pointer-Zugriff, weil noch kein Objekt existiert. Das brauchst du auch nicht, das erledigt der nee-Operator. Die Frage wäre, wann das bei dir nicht funktioniert und wozu du etwas anderes benötigst?


Ich glaube, du hast ein paar Probleme:
1) Du machst es dir evtl. zu kompliziert; ennst du alle Möglichkeiten von C++? ich denke, mit einer pure-virtual base class d.h. einer reinen Schnittstelle plus Vererbung bekommst du alles, was du brauchst; das Konzept ähnelt dem von Java.
2) Pointer auf Member-Functions benötigst du in C++ praktisch nie.
3) wenn du das über DLL-Grenzen hinweg machen möchtest, dann solltest du dir COM anschauen; Microsoft hat das für dich schon gelöst.
4) wenn du die zu erzeugende Klasse zur Compilezeit nicht bestimmen kannst, solltest du eine Class-Factory programmieren; das ist eine Funktion oder eine Klasse mit einer geeigneten Memberfunktion, die in Abhängigkeit von Laufzeitinfornationen einen bestimmten Typ erzeugt, also

if ( ... )
p = new MyClass1();
else
p = new MyClass2();

damit das funktioniert müssen diese von einer gemeinsamen Basisklasse abgeleitet sein:

MyClass1 : public MyBaseClass;
MyClass2 : public MyBaseClass;

Die Factory-Funktion sieht dann wie folgt aus

MyBaseClass * Create( ... )
{
p * MyBaseClass = 0;

...

if ( ... )
p = new MyClass1();
else
p = new MyClass2();

...

return p;
}

MyClass1 und MyClass2 sind dann z.B. in der DLL implementiert. Im EXE inkludierst du das Headerfile, so das beide bekannt sind. Außerdem exportierst du die Faktory-Funktion Create() - und sonst nichts! Die Erzeugung der Objekte zur Laufzeit erfolgen mittels Create(). Die Verwendung der Objekte erfolgt immer mittels des Pointer-Zugriffs, so dass der Compiler für das EXE lediglich die gemeinsame Basisklasse MyBaseClass kennen muss; aufgrund der Vererbung wird automatisch der richtige Code angelegt und die richtigen Funktionen der abgeleiteten Klassen aufgerufen.

Beachte aber, dass du die Factory nur benötigst, wenn du wirklich nicht weißt, welche MyClass1 oder MyClass2 tatsächlich vorliegt.


Schreib doch erst mal ein einfaches Programm, in dem du das Problem ohne zusätzliche DLL in sauberem C++ löst. Verwende keine Func-Pointer; in C++ ist das eigtl. nicht nötig. Dann lagerst du einige Teile in eine DLL aus und löst nur noch diese speziellen Probleme.

Und stell' evtl. den Sourcecode hier ein.
Gruß
Tom

Ἓν οἶδα, ὅτι οὐδὲν οἶδα.

Skeltek
Ehrenmitglied
Ehrenmitglied
Beiträge: 2887
Registriert: 25. Mär 2008, 23:51

Re: Programmieren C++

Beitrag von Skeltek » 22. Jul 2017, 13:35

Hi toms,
Danke für die ausführliche Antwort.
tomS hat geschrieben:
22. Jul 2017, 08:44
Ich glaube, du hast ein paar Probleme:
1) Du machst es dir evtl. zu kompliziert; ennst du alle Möglichkeiten von C++? ich denke, mit einer pure-virtual base class d.h. einer reinen Schnittstelle plus Vererbung bekommst du alles, was du brauchst; das Konzept ähnelt dem von Java.
Nein, natürlich kenne ich nicht alle Möglichkeiten, das dürfte 30 Jahre Programmiererfahrung benötigen um nur fast alles zu kennen. Und ich steh ja relativ am Anfang.
tomS hat geschrieben: 2) Pointer auf Member-Functions benötigst du in C++ praktisch nie.
3) wenn du das über DLL-Grenzen hinweg machen möchtest, dann solltest du dir COM anschauen; Microsoft hat das für dich schon gelöst.
COM sagt mir momentan nichts. Wir hatten ein Grundgerüst an marginalen Headerfile-Beschreibungen und gewollter Funktionsweise. Die genaue Implementierung und Mechanik mussten wir anhand der Funktionsnamen mehr oder weniger erraten.
tomS hat geschrieben: 4) wenn du die zu erzeugende Klasse zur Compilezeit nicht bestimmen kannst, solltest du eine Class-Factory programmieren; das ist eine Funktion oder eine Klasse mit einer geeigneten Memberfunktion, die in Abhängigkeit von Laufzeitinfornationen einen bestimmten Typ erzeugt, also
Wir sollten eine dynamische Bibliothek B zur Laufzeit laden und dafür eine Hilfsklasse verwenden.
Die Konstruktoren aus B sollten eine aus einer während dem Programstart (bevor die Main anfängt) zu ladenden Bibliothek A stammende Funktion verwenden, um eine Ausgabe in der Konsole zu tätigen.
Eine der Funktionen der Hilfsklasse war void *resolveAddress(*const char). Exportiert habe ich die gesammte Klasse B; da es für mich nahe lag, daß die Funktion von 'resolveAddress' die Ermittlung des Function Pointers einer beliebig wählbaren der expotierten Functions sein sollte.

Da die Namen der exportieten Funktionen mit Prä- und Postfixen verschnörkelt waren(überladen geht ja da nicht), war die Ermittlung des genauen Namens der Funktionen nur mit DependencyWalker möglich (Der Compiler weigerte sich, die automatisierte Entschlüsselung anhand der declspec(importLibrary)-Direktive automatisch vorzunehmen).
Letzten Endes habe ich es mit einer Hilfsfunktion gelöst, welche den Konstruktor aufruft und ein Objekt des Typs zurück gibt.
Ein direktes Zuweisen von Objekten war nicht möglich, da die Hilfsklasse die Implementierungen der Konstruktoren nicht kennt und auch keinen Zugriff darauf hat.
tomS hat geschrieben: ...
MyClass1 und MyClass2 sind dann z.B. in der DLL implementiert. Im EXE inkludierst du das Headerfile, so das beide bekannt sind. Außerdem exportierst du die Faktory-Funktion Create() - und sonst nichts! Die Erzeugung der Objekte zur Laufzeit erfolgen mittels Create(). Die Verwendung der Objekte erfolgt immer mittels des Pointer-Zugriffs, so dass der Compiler für das EXE lediglich die gemeinsame Basisklasse MyBaseClass kennen muss; aufgrund der Vererbung wird automatisch der richtige Code angelegt und die richtigen Funktionen der abgeleiteten Klassen aufgerufen.

Beachte aber, dass du die Factory nur benötigst, wenn du wirklich nicht weißt, welche MyClass1 oder MyClass2 tatsächlich vorliegt.
Leider hatten wir keine Basisklasse. Da reicht dann wohl auch der Header nehme ich an, was dann letztlich auch funktioniert hat.
Hab dann eine Factor-Funktion verwendet. Den FunctionPointer zu "Create" benötigt man ja trotzdem?
Wenn man in C++ keine FunctionPointer verwenden soll oder braucht, wie soll man dann denn die mittels GetProcAddress(*const char) ermittelte Factory-Funktion abspeichern und abrufen?

Danke für die ausführliche Antwort. Wie gesagt, mein Problem ist schon seit längerem gelöst, gegebenenfalls stell ich noch Source-Code hier rein, falls ich demnächst mal Zeit habe.
Will demnächst einen Netzwerk-Code schreiben für eine OpenGL-basierten Multiplayer-Weltraumsimulation, die ich schon vor einiger Zeit gecoded habe... noch ist es nur Single-Player. Werde wohl den halben Code umfriemeln müssen, das braucht dann auch einiges an Zeit :)

Schönen Gruß, Skel
Die plausibelste Erklaerung jedes hinreichend komplizierten Systems ist falsch

Unentscheidbarkeit für Dummies: Dieser Satz ist wahr
oder
Diese Menge hat zwei Elemente: A und B

Benutzeravatar
tomS
Administrator
Administrator
Beiträge: 8726
Registriert: 19. Nov 2007, 20:29
Wohnort: Nürnberg

Re: Programmieren C++

Beitrag von tomS » 22. Jul 2017, 18:18

Was du oben beschreibst hört sich ziemlich gruselig an. Wozu soll sowas gut sein? Meines Erachtens fehlt eine saubere Problembeschreibung und damit auch eine saubere Lösung (das ist keine Kritik an dir).

Zu der Factory-Methode: ja, diese kannst du mittels GetProcAddress() und einem Func-Pointer verwenden - aber nur diese! Für alle weiteren Zugriffe auf die Objekte ist das unnötig.

Und nochmal: warum musst die DLL überhaupt dynamisch mittels LiadLibrary() laden? Warum reicht nicht die ganz normale Bindung mittels Linker?
Gruß
Tom

Ἓν οἶδα, ὅτι οὐδὲν οἶδα.

Skeltek
Ehrenmitglied
Ehrenmitglied
Beiträge: 2887
Registriert: 25. Mär 2008, 23:51

Re: Programmieren C++

Beitrag von Skeltek » 22. Jul 2017, 23:35

Keine Ahnung.
Allerdings ist es nicht unüblich dynamische Bibliotheken erst zur Laufzeit zu laden mittels von Methoden bzw Klassen einer Hilfsbibliothek, welche meist statisch eingebunden wird.
So wird ermöglicht, Bibliotheken einfach auszutauschen, ohne das Programm vorher beenden, neu kompilieren und neu starten zu müssen.

Den Zweck die Hilfsbibliothek erst dynamisch während dem Programstart zu binden anstatt statisch ist mir nicht ganz klar, aber möglicherweise war es einfach nur zu Übungszwecken. Die kann ja theoretisch auch erst nach Programmstart gebunden werden.

Wenn du den Thread nicht-öffentlich machen würdest, könnte ich Aufgabenstellung und Quellcodes gerne hier posten.
Freue mich immer über Tipps.

Gruß, Skel

ps: Habe mir vor kurzem den Quellcode meiner ersten 3d-Engine neu angesehen und es nach Jahren geschafft alles mit allen Linker- und projekteinstellungen von Code::Blocks auf Netbeans zu portieren um dann dort weiter programmieren zu können.
Wenn ich mir ansehe, was für einen riesigen Mist ich damals in den tausenden an Zeilen Quellcode verzapft hatte (weder von Collections noch von Pointern oder gängigen Methoden irgendeine Ahnung), wird mir schlecht.
Objekte hatte ich alle bis runter zu Vektoren und Matrizen selbst definiert, Linked-Lists usw selbst geschrieben und eigene Verfahren für alles entwickelt für Kollisionsabfragen usw. Tolle Leistung, aber was für eine verschwendete Zeit... kann mir nur schwer vorstellen, wie gut ich das heute hinbekommen würde, wo ich ja jetzt etwas Halbwissen und annähernd Ahnung habe :D

Ein Auszug aus dem Gelenkmodel der Spielfigur:
PLAYER::leftArm.midJoint=vektorSumme( leftArm.topJoint,{0,-0.3*cosf(-M_PI/20*sinf(2*M_PI*animationTimeProgress)*playerAbsolutePlanarVelocity-playerAbsolutePlanarVelocity/6),-0.3*sinf(-M_PI/20*sinf(2*M_PI*animationTimeProgress)*playerAbsolutePlanarVelocity-playerAbsolutePlanarVelocity/6)});
Wie man sieht: Jede kleine Variable, Vektor- und Polygondefinition liebevoll selbst benannt :-)
Die plausibelste Erklaerung jedes hinreichend komplizierten Systems ist falsch

Unentscheidbarkeit für Dummies: Dieser Satz ist wahr
oder
Diese Menge hat zwei Elemente: A und B

Benutzeravatar
positronium
Ehrenmitglied
Ehrenmitglied
Beiträge: 2524
Registriert: 2. Feb 2011, 20:13

Re: Programmieren C++

Beitrag von positronium » 23. Jul 2017, 11:17

C++ habe ich nie viel programmiert, und das wenige ist schon 1000 Jahre her. Von daher kann ich nichts zur genauen Implementierung schreiben, und habe mich deshalb mit Kommentaren zurück gehalten.
So wie Du das schilderst, scheint das ein Ausbildungsprojekt zu sein. Offenbar soll dabei sowohl eine objektorientierte Bibliothek als auch eine klassische DLL programmiert und verwendet werden. Also, in etwa so: Das Programm enthält eine Klasse, welche bei erster Instanziierung eine Bibliothek B mit einem enthaltenen COM-Objekt lädt (und nach Freigabe aller Instanzen die Bibliothek wieder entlädt), und von diesem COM-Objekt soll in dessen Konstruktor die klassische DLL aufgerufen werden.
Eine solche Architektur müsste einen guten Grund haben - man teilt ja ein Programm nicht aus Jux und Tollerei in drei Teile auf. Ein Beispiel ist, eine Möglichkeit der modularen Erweiterbarkeit (auch für die Module von Fremdanbietern) zu schaffen.
Ich weiss gar nicht, ob man normale/alte DLLs (GetProcAddress...) heute noch verwendet. In Java würde man dafür ein Interface definieren, und dieses Entwicklern zur Verfügung stellen - ich denke, dass es dafür in C++ ein Analogon gibt.
Skeltek hat geschrieben:
22. Jul 2017, 23:35
Wie man sieht: Jede kleine Variable, Vektor- und Polygondefinition liebevoll selbst benannt :-)
So etwas ist besser, einfacher und mit weniger Arbeit verbunden, wenn man es flexibel programmiert, und jedes Teil der Figur durch ein eigenes Objekt repräsentiert. Das spart im Nu sehr viel Arbeitszeit, und ausserdem kann man dann problemlos z.B. aus Hominiden, Arachnen, oder auch 7-gliedrige, mehrgelenkige, 4- oder 9-fingrige machen, usw..

Benutzeravatar
tomS
Administrator
Administrator
Beiträge: 8726
Registriert: 19. Nov 2007, 20:29
Wohnort: Nürnberg

Re: Programmieren C++

Beitrag von tomS » 23. Jul 2017, 14:24

Wichtig wäre es, die Intention und Problemstellumg zu verstehen. Ich konstruiere mal eine:

Ein Hauptprogramm, d.h. ein EXE soll je nach dynamischer Bedingung unterschiedliche Klassen verwenden, um unterschiedliche Aufgaben zu erfüllen. Die Bedingungen sind zum Programmstart nicht bekannt; sie ergeben sich zur Laufzeit, sind evtl. von den Benutzern, Installationsumgebung, Deployment o.ä. abhängig. Genauso unterscheiden sich die Aufgaben; sie können sich ebenfalls ändern, können erweitert werden usw. Außerdem soll es möglich sein, dass die Module, die die einzelnen Unteraufgaben kapseln, unabhängig voneinander ausgetauscht werden können, z.B. für Erweiterungen oder Fehlerkorrekturen. Dennoch gibt es genügend viele Gemeinsamkeiten, so dass Teile der Aufgaben im Hauptprogramm zentral implementiert sind, und dass der Rest der Aufgaben - die Unteraufgaben - in DLLs gekapselt werden und eine identische Schnittstelle aufweisen.

Mir fallen zwei völlig verschiedene Praxisbeispiele ein: i) eine IDE als gemeinsamer Rahmen, die je nach Programmiersprache unterschiedliche Module nachlädt; ii) ein Distributed Control System (DCS) das mit unterschiedlichen Subsystemen kommuniziert (mein Fachgebiet).

Mein Ansatz wäre folgender:
1) aus einer Konfigurationsdatei oder Benutzereingabe ergeben sich die zum lösenden Unteraufgaben; je Unteraufgabe ist eine Klasse vorgesehen, von der mehrere Objekte instantiiert werden können, in denen die Unteraufgaben gekapselt sind
2) jede Klasse ist von einer gemeinsamen pure virtual base class abgeleitet, die ein einheitliches Interface definiert
3) jede Klasse liegt in einer eigenen DLL myClassX.DLL
4) wenn aus (1) eine Unteraufgaben folgt, wird dafür ein Objekt derbzugehörigen Klasse instantiiert; wenn X sowie ( ... ) die Parameter des Problemkontextes beschreibt, dann wird das Objekt mittels

= new myClassX( ... );

erzeugt.
5) zunächst muss jedoch die entsprechende DLL geladen und die Factory-Methode Create() aufgerufen werden; dies erfolgt mittels

LoadLibrary();
GetProcAddress()
Aufruf von Create();
sowie new myClassX( ... ) innerhalb von Create().

Beachte, dass Create() immer den selben Namen trägt, während die Klassen unterschiedliche Namen myClassX haben.
6) je X liegt nun eine oder mehrere Instanzen von myClassX vor; die Verwendung erfolgt jedoch mittels der gemeinsamen Schnittstelle myBaseClass, so dass das EXE die unterschiedlichen X nicht sieht, außer ...
7) ... bei der Erzeugung der Objekte; dies wird in einer zentralen Factory-Klasse gekapselt, die (1) auswertet, daraus X bestimmt und die Schritte in (5) ausführt
8) zur Verwaltung der Objekte dient außerdem eine DLL-globale oder statische Member-Variable, die je myClassX.DLL die Instanzen der Klasse myClassX zählt; sie ist mit 0 initialisiert und wird im Konstruktor (Destruktor) inkrementiert (dekrementiert); die Factory-Klasse kann die DLL entladen, wenn der Zähler Null erreicht; außerdem kann über ein externes Kommando das Entladen erzwungen werden, z.B. um die DLL austauschen (Bsp. DCS: das Kommunikationsprotokoll X zu Unterstationen wird erweitert; dazu müssen alle Unterstationen mit Protokoll X getrennt, die DLL ausgetauscht und die Unterstationen wieder neu eingebunden werden; düs erfolgt im Sinne von (1) und (5))

Passt das in etwa?
Gruß
Tom

Ἓν οἶδα, ὅτι οὐδὲν οἶδα.

Skeltek
Ehrenmitglied
Ehrenmitglied
Beiträge: 2887
Registriert: 25. Mär 2008, 23:51

Re: Programmieren C++

Beitrag von Skeltek » 23. Jul 2017, 20:18

Hat jemand das Thema/Thread schon so modifiziert,daß nur noch wir drin lesen können? Ich seh sowas normalerweise nicht.
Will die Aufgabenstellung ungerne öffentlich posten, möglicherweise hat unser Professor etwas dagegen (vielleicht auchder Grund,weshalb er sie selbst nicht online stellt sondern nur ausgedruckt verteilt).
Die plausibelste Erklaerung jedes hinreichend komplizierten Systems ist falsch

Unentscheidbarkeit für Dummies: Dieser Satz ist wahr
oder
Diese Menge hat zwei Elemente: A und B

Benutzeravatar
tomS
Administrator
Administrator
Beiträge: 8726
Registriert: 19. Nov 2007, 20:29
Wohnort: Nürnberg

Re: Programmieren C++

Beitrag von tomS » 24. Jul 2017, 17:04

Nein. Das kann nur Werner.

Du kannst mir eine PN schicken.
Gruß
Tom

Ἓν οἶδα, ὅτι οὐδὲν οἶδα.

Benutzeravatar
tomS
Administrator
Administrator
Beiträge: 8726
Registriert: 19. Nov 2007, 20:29
Wohnort: Nürnberg

Re: Programmieren C++

Beitrag von tomS » 25. Jul 2017, 22:38

OK, hab' die PN gelesen.

Da steht nichts davon, dass Methoden oder gar Konstruktoren per Func-Ptr. aufzurufen wären. Der Zugriff per Func-Ptr. wir die nur für Funktionen benötigt.

Zeichne dir mal ein sauberes UML-Diagramm.
Gruß
Tom

Ἓν οἶδα, ὅτι οὐδὲν οἶδα.

Skeltek
Ehrenmitglied
Ehrenmitglied
Beiträge: 2887
Registriert: 25. Mär 2008, 23:51

Re: Programmieren C++

Beitrag von Skeltek » 27. Jul 2017, 02:41

Hmm, dachte die Bibliothek wird als 'Modul' übergeben im Falle einer DLL, die ja auch nur ein binary(executable) mit anderer Dateiendung ist.
Die Bibliothek zu "Laden" übergibt ja nicht mehr als die Pseudonamen der symbolischen Verknüpfungen bzw Einsprungadressen in die Binary, von denen man dann mit der übergebenen Namensliste die Einsprungadresse einer ganz bestimmten Funktion ermitteln kann.
Wie würdest du denn sonst die Einsprungadresse einer Funktion in der DLL ermitteln und dann benutzen, wenn nicht per Pointer?
oder sprichst du gerade nicht von dynamisch ladbaren Bibliotheken sondern nur von statisch eingebundenen?
Das einzige was das hauptprogramm.exe von der Bibliothek weiß sind letzten Endes die Einsprungadressen, schließlich wird die DLL ja nicht mit einer Parameterliste aufgerufen im herkämmlichen Sinn.
?

Momentanarbeite ich daran, SDL und SDL_Net in mein eigenes Programm einzubinden, aber da findet er einfach bestimmte Definitionen nicht, obwohl eigentlich alles richtig verknüpft ist. Da mache ich nun auch bereits seit Tagen daran herum und es bringt anscheinend rein gar nichts, da keine anständigen Quellen zu meiner Lösung auf google.
Die plausibelste Erklaerung jedes hinreichend komplizierten Systems ist falsch

Unentscheidbarkeit für Dummies: Dieser Satz ist wahr
oder
Diese Menge hat zwei Elemente: A und B

Benutzeravatar
tomS
Administrator
Administrator
Beiträge: 8726
Registriert: 19. Nov 2007, 20:29
Wohnort: Nürnberg

Re: Programmieren C++

Beitrag von tomS » 27. Jul 2017, 21:31

Skeltek hat geschrieben:
27. Jul 2017, 02:41
Wie würdest du denn sonst die Einsprungadresse einer Funktion in der DLL ermitteln und dann benutzen, wenn nicht per Pointer?
Bei dynamisch zu ladenden DLLs funktioniert das mittels LoadLibrary() und GetProcAddress(). Aber so wie ich die Aufgabe verstehe, ist GetProcAddress() sowie die Verwendung von Func-Pointern nur für Funktionen notwendig, nicht jedoch für Klassen und deren Methoden.

Übrigens hab' ich die Zeichnung fast fertig (nicht UML, da sonst mehrere Diagramme notwendig).
Gruß
Tom

Ἓν οἶδα, ὅτι οὐδὲν οἶδα.

Antworten

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 2 Gäste