Jammer-Thread

Hier kann über allgemeine Themen diskutiert werden, die sonst in kein Forum passen.
Insbesondere über Szene, Games, Kultur, Weltgeschehen, Persönliches, Recht, Hard- und Software.
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Jammer-Thread

Beitrag von CodingCat »

eXile hat geschrieben:Ich bin zwar kein Held der C++-Programmierung, aber hierbei muss ich dot zustimmen; wenn du im zweiten Fall die Daten aus dem struct nie als char behandelst, sondern sofort aufgrund externer Informationen immer zu einem anderen Typen castest, haben beide Alternative exakt den gleichen Informationsgehalt über den Typ: Nämlich Null.
Falsch, mit dem struct werden nur Parameter vom Typ AbstractBinderState akzeptiert, mit einem void* jeder beliebige Objektzeiger. Das ist ein ziemlich gewaltiger Unterschied, würde ich behaupten. ;)

Ok, falsch gelesen. Der void-Zeiger ist schlicht zu klein. Mein char-Array hat mehr als 4 oder 8 Bytes. ;)
Zuletzt geändert von CodingCat am 28.05.2012, 20:04, insgesamt 1-mal geändert.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
dot
Establishment
Beiträge: 1746
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Jammer-Thread

Beitrag von dot »

CodingCat hat geschrieben:Wieso sollte Apply ein void* liefern?
Damit du den void* in standardkonformer Weise dann wieder zurückcasten kannst, um einen Zeiger auf das mit placement new erzeugte Objekt zu erhalten.
CodingCat hat geschrieben:Erstens habe ich das State-Objekt doch bereits auf dem Stack, und zweitens enthält der Typ void weniger Information als der Typ AbstractBinderState?!
Aber deinen AbstractBinderState kannst du eben strenggenommen nicht standardkonform in deinen Typ casten, auch wenn es natürlich problemlos funktionieren wird. Um mehr "Typinformation" zu haben eben der Vorschlag statt void* mit einem struct das den void* enthält zu hantieren...
CodingCat hat geschrieben:
eXile hat geschrieben:Ich bin zwar kein Held der C++-Programmierung, aber hierbei muss ich dot zustimmen; wenn du im zweiten Fall die Daten aus dem struct nie als char behandelst, sondern sofort aufgrund externer Informationen immer zu einem anderen Typen castest, haben beide Alternative exakt den gleichen Informationsgehalt über den Typ: Nämlich Null.
Falsch, mit dem struct werden nur Parameter vom Typ AbstractBinderState akzeptiert, mit einem void* jeder beliebige Objektzeiger. Das ist ein ziemlich gewaltiger Unterschied, würde ich behaupten. ;)
Eben genau darum kein roher void*, sondern ein struct das den void* enthält ;)


Nichts desto trotz find ich das System immer noch sehr merkwürdig :P
Benutzeravatar
dot
Establishment
Beiträge: 1746
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Jammer-Thread

Beitrag von dot »

CodingCat hat geschrieben:Ok, falsch gelesen. Der void-Zeiger ist schlicht zu klein. Mein char-Array hat mehr als 4 oder 8 Bytes. ;)
Der void Zeiger dient auch nicht als Buffer um das Objekt darin zu konstruieren, sondern einfach als transparenter Rückgabetyp den du eben standardkonform intern wieder zurückcasten kannst. Du hast auf der einen Seite deinen char Buffer, wo auch immer der drinsteckt. Und dann hast du eben effectBinder.Apply(), welches in dem Buffer dein Objekt konstruiert und einen void* auf das konstruierte Objekt zurückliefert. Diesen void* kannst du dann per static_cast völlig standardkonform in einen Zeiger auf den konstruierten Typ zurückcasten.
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Jammer-Thread

Beitrag von CodingCat »

Naja, ein Cast von char-Array zu Objekt ist nicht weniger standardkonform als ein Cast von void-Zeiger zu Objekt, in beiden Fällen ist Bedingung, dass der referenzierte Speicherbereich auch tatsächlich das Objekt enthält. Insofern würde ein weiterer Typ die Verwirrung nur unnötig erhöhen. Wobei, jetzt, wo ich nochmal drüber nachdenke, dann durch das Typsystem immerhin noch eine Konstruktion erzwungen würde.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Jammer-Thread

Beitrag von CodingCat »

Damit bin ich jetzt bei:

Code: Alles auswählen

struct AbstractBinderStateStorage { char Data[...]; };
struct AbstractBinderState { void *Data; };

AbstractBinderStateStorage binderStateStorage;
// Intern: new (binderStateStorage.Data) ActualBinderState();
AbstractBinderState binderState = effectBinder.Apply(binderStateStorage, ...);

// Intern: reinterpret_cast<ActualBinderState*>(binderState.Data);
while (effectBinder.ApplyPass(binderState, ...))
   ...;

// effectBinder ist polymorph
:P
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
dot
Establishment
Beiträge: 1746
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Jammer-Thread

Beitrag von dot »

Genau, nur dass intern nun ein static_cast ausreicht ;)

EDIT: Was genau stellst du eigentlich damit an?
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Jammer-Thread

Beitrag von CodingCat »

Datenpolymorphie. Es gibt verschiedene Binders, die jeweils unverwandte Zustandsdaten speichern müssen. Ich kenne den Typ von effectBinder nicht, folglich weiß ich leider auch nicht, was er an Zustandsdaten speichert (z.B. aktuelles Lichtoffset etc.).
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Jammer-Thread

Beitrag von CodingCat »

Wieso hat std::array keine Konstruktoren? Was bringt mir dieses Ding, wenn ich damit die enthaltenen Objekte noch immer nicht in der Initialisierungsliste initialisieren kann ... Muss ich mir echt selbst eine Klasse schreiben, nur um Dinge wie myctor() : myarray(ctorparam1, ctorparam2, ...) {} schreiben zu können?!?

Und wieso nimmt new[] im Jahre 2012 ebenfalls noch immer keine Konstruktorargumente entgegen?!? :evil:
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Jammer-Thread

Beitrag von CodingCat »

Da ich an dieser Stelle ohnehin schon alles verflucht habe, was mir die letzten 30 Minuten in den Sinn gekommen ist, hier das Ergebnis ohne weitere Worte:

Code: Alles auswählen

template<class Element, size_t Size>
class array
{
public:
	static const size_t count = Size;

	typedef Element value_type;
	typedef value_type array_type[count];
	typedef size_t size_type;
	typedef ptrdiff_t difference_type;
	typedef value_type* pointer;
	typedef const value_type* const_pointer;
	typedef value_type& reference;
	typedef const value_type& const_reference;

	typedef pointer iterator;
	typedef const_pointer const_iterator;

	enum uninitialized_t { uninitialized };

private:
	char m_data[sizeof(array_type)];

public:
	array()
	{
		for (pointer it = data(); it < data_end(); ++it)
			new (static_cast<void*>(it)) value_type();
	}
	
	LEAN_INLINE array(uninitialized_t) { }

	array(const array &right)
	{
		const_iterator rit = right.begin();

		for (pointer it = data(); it < data_end(); ++it, ++rit)
			new (static_cast<void*>(it)) value_type(*rit);
	}
	array(array &&right)
	{
		iterator rit = right.begin();

		for (pointer it = data(); it < data_end(); ++it, ++rit)
			new (static_cast<void*>(it)) value_type(std::move(*rit));
	}

	template <class V1>
	explicit array(const V1 &v1)
	{
		for (pointer it = data(); it < data_end(); ++it)
			new (static_cast<void*>(it)) value_type(v1);
	}
	template <class V1, class V2>
	explicit array(const V1 &v1, const V2 &v2)
	{
		for (pointer it = data(); it < data_end(); ++it)
			new (static_cast<void*>(it)) value_type(v1, v2);
	}
	template <class V1, class V2, class V3>
	explicit array(const V1 &v1, const V2 &v2, const V3 &v3)
	{
		for (pointer it = data(); it < data_end(); ++it)
			new (static_cast<void*>(it)) value_type(v1, v2, v3);
	}

	~array()
	{
		for (pointer it = data(); it < data_end(); ++it)
			it->~value_type();
	}

	array& operator =(const array &right)
	{
		const_iterator rit = right.begin();

		for (pointer it = data(); it < data_end(); ++it, ++rit)
			*it = *rit;

		return *this;
	}
	array& operator =(array &&right)
	{
		iterator rit = right.begin();

		for (pointer it = data(); it < data_end(); ++it, ++rit)
			*it = std::move(*rit);

		return *this;
	}

	
	LEAN_INLINE void fill(const value_type& value)
	{
		std::fill_n(data(), count, value);
	}
	LEAN_INLINE void assign(const value_type& value) { fill(value); }

	LEAN_INLINE pointer data() { return reinterpret_cast<pointer>(&m_data[0]); }
	LEAN_INLINE const_pointer data() const { return reinterpret_cast<const_pointer>(&m_data[0]); }
	LEAN_INLINE const_pointer cdata() const { return data(); }

	LEAN_INLINE pointer data_end() { return data() + count; }
	LEAN_INLINE const_pointer data_end() const { return data() + count; }
	LEAN_INLINE const_pointer cdata_end() const { return data() + count; }

	LEAN_INLINE iterator begin() { return data(); }
	LEAN_INLINE const_iterator begin() const { return data(); }
	LEAN_INLINE const_iterator cbegin() const { return data(); }
	LEAN_INLINE iterator end() { return data_end(); }
	LEAN_INLINE const_iterator end() const { return data_end(); }
	LEAN_INLINE const_iterator cend() const { return data_end(); }

	LEAN_INLINE size_type size() const { return count; }
	LEAN_INLINE size_type max_size() const { return count; }
	LEAN_INLINE bool empty() const { return (count == 0); }

	LEAN_INLINE reference operator[](size_type pos) { return data()[pos]; }
	LEAN_INLINE const_reference operator[](size_type pos) const { return data()[pos]; }

	LEAN_INLINE reference front() { return data()[0]; }
	LEAN_INLINE const_reference front() const { return data()[0]; }
	LEAN_INLINE reference back() { return data()[count - 1]; }
	LEAN_INLINE const_reference back() const { return data()[count - 1]; }
};
Wer Fehler findet, darf sie melden. Benutzung:

Code: Alles auswählen

class myclass
{
   array<foo, 6> foos;
public:
   myclass(someresource)
      : foos(someresource)
   {
   }
};
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
Krishty
Establishment
Beiträge: 8342
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Jammer-Thread

Beitrag von Krishty »

StringCchVPrintf(), sprintf(), _sprintf_l(), swprintf(), _swprintf_l(), __swprintf_l(), _snprintf(), _snprintf_l(), _snwprintf(), _snwprintf_l(), _vsnprintf(), _vsnwprintf(), vsnprintf_s(), _vsnprintf_s(), _vsnprintf_s_l(), _vsnwprintf_s(), _vsnwprintf_s_l(), _sprintf_p(), _sprintf_p_l(), _swprintf_p(), _swprintf_p_l(), sprintf_s(), _sprintf_s_l(), swprintf_s(), _swprintf_s_l(), _vscprintf(), _vscprintf_l(), _vscwprintf(), _vscwprintf_l(), _vscprintf(), _vscprintf_l(), _vscwprintf(), _vscwprintf_l(), wvnsprintf(), wvsprintf(), wsprintf()

Bild

Ich würde ja am liebsten die CRT-_output_s_l() kopieren, den ganzen unnützen Scheiß (Locales & den Umweg über eine FILE *) rausschmeißen, daraus mein eigenes (potentiell C99-konformes) sprintf() machen, und den Mist nie wieder mit der Kneifzange anpacken. Aber das darf ich wahrscheinlich Copyright-bedingt nicht.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1746
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Jammer-Thread

Beitrag von dot »

operator << ftw ;)
Benutzeravatar
Krishty
Establishment
Beiträge: 8342
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Jammer-Thread

Beitrag von Krishty »

stream << notation(Notation::scientific) << set_padding(0) << extendToPlaces(8) << base(Base::decimal) << f << flush;

ftw
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1746
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Jammer-Thread

Beitrag von dot »

:)
Benutzeravatar
eXile
Establishment
Beiträge: 1136
Registriert: 28.02.2009, 13:27

Re: Jammer-Thread

Beitrag von eXile »

Habt ihr euch schon auf die WRL gefreut? Endlich ohne komische Extensions mit reinem C++ und allem, was C++ bietet, auf Windows programmieren?

Ha! Ihr dürft keine Exceptions werfen, die über eine WRL-Klassengrenze hinausgehen. Fehler müssen als HRESULT zurückgegeben werden. Und der Konstruktor konstruiert auch nicht ein WRL-Objekt, sondern das tut eine RuntimeClassInitialize-Funktion, die von einer Factory aufgerufen wird (und jede WRL-Klasse braucht ein eine WRL-Factory). Und ihr müsst natürlich alle Methodendeklarationen in einer komischen .idl-Datei noch zusätzlich deklarieren und synchron halten.
Benutzeravatar
dot
Establishment
Beiträge: 1746
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Jammer-Thread

Beitrag von dot »

Ja klar, WinRT ist eben COM, wo liegt das Problem? Exceptions über Interface-Grenzen werfen war sowieso noch nie eine gute Idee...

Abgesehen davon verwend doch einfach C++/CX. WinRT Code ist sowieso schon rein aus Prinzip nicht portabel und hält sich eh nur an den Modulgrenzen auf.
Benutzeravatar
eXile
Establishment
Beiträge: 1136
Registriert: 28.02.2009, 13:27

Re: Jammer-Thread

Beitrag von eXile »

dot hat geschrieben:Ja klar, WinRT ist eben COM, wo liegt das Problem?
Implizierend, dass COM kein Problem ist.
dot hat geschrieben:Exceptions über Interface-Grenzen werfen war sowieso noch nie eine gute Idee...
Wie willst du hier korrekt anzeigen, dass die Initialisierung eines WRL-Objekts fehlgeschlagen ist? Oder generell: Dass die Instanziierung der Implementierung eines Interfaces fehlgeschlagen ist?
dot hat geschrieben:Abgesehen davon verwend doch einfach C++/CX. WinRT Code ist sowieso schon rein aus Prinzip nicht portabel und hält sich eh nur an den Modulgrenzen auf.
Genau, statt anständigem Design benutzen wir einfach irgendwelche Extensions. Dass Statische-Analyse-Tools damit nicht zurechtkommen, sollte klar sein.

Außerdem habe ich immer noch keine Antwort auf die Frage, ob ich nun auch so etwas wie CreateWindow irgendwann auf WRL umstellen muss/darf/sollte.
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Jammer-Thread

Beitrag von CodingCat »

Dass es noch immer keinen Standard für Exceptions gibt, ärgert mich btw. auch tierisch. Für V-Tables haben sie es doch auch geschafft ... Und wenn sie schon dabei sind, könnten sie gleich noch Name Mangling und RTTI standardisieren. Aber ich sehe ein, eine Welt ohne separate Binaries für jeden Mistcompiler wäre zu langweilig.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
dot
Establishment
Beiträge: 1746
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Jammer-Thread

Beitrag von dot »

eXile hat geschrieben:
dot hat geschrieben:Ja klar, WinRT ist eben COM, wo liegt das Problem?
Implizierend, dass COM kein Problem ist.
dot hat geschrieben:Exceptions über Interface-Grenzen werfen war sowieso noch nie eine gute Idee...
Wie willst du hier korrekt anzeigen, dass die Initialisierung eines WRL-Objekts fehlgeschlagen ist? Oder generell: Dass die Instanziierung der Implementierung eines Interfaces fehlgeschlagen ist?
So wie man das eben macht: Indem die entsprechende Factory eben einen entsprechenden Fehlercode sowie nullptr returned? Unter der Oberfläche kannst du mit Exceptions völlig verrückt werden wenn du willst.

Ich denk du gehst hier von der falschen Annahme aus, dass es sich bei WinRT Komponenten um Klassen handelt. Ist aber nicht so, sind nur Interfaces ;)

eXile hat geschrieben:
dot hat geschrieben:Abgesehen davon verwend doch einfach C++/CX. WinRT Code ist sowieso schon rein aus Prinzip nicht portabel und hält sich eh nur an den Modulgrenzen auf.
Genau, statt anständigem Design benutzen wir einfach irgendwelche Extensions. Dass Statische-Analyse-Tools damit nicht zurechtkommen, sollte klar sein.
Wir reden hier von plattformspezifischem Interfacecode der in der Regel einen praktisch vernachlässigbaren Prozentsatz eines richtigen Projektes ausmachen wird. Abgesehen davon: Inwiefern hat das Verwenden von C++/CX deiner Meinung nach mit schlechtem Design zu tun? Außerdem kommt Visual Studio 11 mit guten Tools für statische Code-Analyse. Ich wette die kommen auch mit C++/CX zurecht.

Ich hab auch länger drüber nachgedacht was ich von C++/CX halten soll und war anfangs sehr skeptisch. Mittlerweile seh ich die Sache aber eigentlich ziemlich unproblematisch.

eXile hat geschrieben:Außerdem habe ich immer noch keine Antwort auf die Frage, ob ich nun auch so etwas wie CreateWindow irgendwann auf WRL umstellen muss/darf/sollte.
WinRT gibts nur für Metro. Zumindest im Moment. Am Desktop läuft weiterhin alles über Win32.

CodingCat hat geschrieben:Dass es noch immer keinen Standard für Exceptions gibt, ärgert mich btw. auch tierisch. Für V-Tables haben sie es doch auch geschafft ... Und wenn sie schon dabei sind, könnten sie gleich noch Name Mangling und RTTI standardisieren. Aber ich sehe ein, eine Welt ohne separate Binaries für jeden Mistcompiler wäre zu langweilig.
RTTI und dynamic_cast sind definitiv unter den Top 10 der Dinge die sie von mir aus aus dem Standard nehmen könnten. Und Name Mangling: Who cares. Binaries von verschiedenen Compilern sind aus Prinzip im Allgemeinen inkompatibel und ein standardisiertes ABI für C++ ist nicht in Sicht und es ist auch sehr unwahrscheinlich dass sowas jemals existieren wird...

Btw: Für IA-64 gibts iirc z.B. ein sehr gründliches ABI. Und unter 64 Bit Windows gäb's theoretisch auf OS Ebene ein ABI für Exceptions. Aber ABIs sind eben in ihrer Natur schon architekturspezifisch. Auch wenn es sicherlich möglich gewesen wäre, WinRT ein ABI für Exception Handling zu verpassen, was hätte man damit gewonnen? Das vernünftig und portabel umzusetzen, wäre vermutlich sehr aufwändig. Und WinRT Komponenten sind nur Interfaces und keine Klassen. Exceptions sollte man sowieso nicht über Interfacegrenzen werfen. Ich find jedenfalls die Lösung über Language Projections mindestens genauso sinnvoll.
Zuletzt geändert von dot am 30.05.2012, 23:44, insgesamt 1-mal geändert.
Benutzeravatar
eXile
Establishment
Beiträge: 1136
Registriert: 28.02.2009, 13:27

Re: Jammer-Thread

Beitrag von eXile »

dot hat geschrieben:Indem die entsprechende Factory eben einen entsprechenden Fehlercode sowie nullptr returned? Unter der Oberfläche kannst du mit Exceptions völlig verrückt werden wenn du willst.
Warum will ich für jedes Furz-WRL-Objekt eine Factory, nur weil mal die Konstruktion fehlschlägt? Und Fehlercodes? Knabe, bitte! Bei Konstruktion von mehreren Objekten muss ich ja auf korrekte Erstellung aller Objekte testen. Anstatt mit Exceptions schnell abzufangen, ob eines fehlgeschlagen ist. Das an mehreren Stellen und dann muss ich ja fast schon Factories benutzen. Und eine Factory für jedes WRL-Objekt sehe ich einfach nicht sein.
dot hat geschrieben:Inwiefern hat das Verwenden von C++/CX deiner Meinung nach mit schlechtem Design zu tun?
Nunja, anscheinend weist deren Framework so viel Text-Redudanz auf, dass anstatt diese mit Sprachmöglichkeiten wie Templates zu reduzieren, sie stattdessen sich auf einen etwas schlaueren Precompiler verlassen. Und dass das ja nur ganz wenig Text vom ganzen Projekt ist, lasse ich auch nicht gelten. WRL macht das Schreiben von UIs immer noch scheiße. Sie hatten doch genau die Aufgabe, unter C++ schöne UI-Programmierung hinzukriegen. Die haben also Scheiße gebaut, die Scheiße ist aber zum Glück nicht metastasierend? Wow, was für ein Glück.
dot hat geschrieben:Außerdem kommt Visual Studio 11 mit guten Tools für statische Code-Analyse. Ich wette die kommen auch mit C++/CX zurecht.
Ja, ich mag auch /analyze. Aber wie schon auf dem #AltDevBlogADay geschrieben, sollte man einfach alles an Analyse-Tools draufhauen, die man zur Verfügung hat.
dot hat geschrieben:Ich hab auch länger drüber nachgedacht was ich von C++/CX halten soll und war anfangs eher skeptisch. Mittlerweile seh ich die Sache aber eigentlich ziemlich unproblematisch.
Die Frage ist ja nicht, ob C++/CX nun akzeptabel ist oder nicht. Die Frage ist, was Microsoft sich vor 2-3 Jahren hätte besseres für reines C++ ausdenken könnte, damit ich jetzt nicht hier rumjammern muss.

Achja, und noch was: Sieht das nach elegantem, gut designtem C++-Code aus?
Bild
Benutzeravatar
dot
Establishment
Beiträge: 1746
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Jammer-Thread

Beitrag von dot »

eXile hat geschrieben:Warum will ich für jedes Furz-WRL-Objekt eine Factory, nur weil mal die Konstruktion fehlschlägt?
Du kannst auch RoActivateInstance() verwenden, das wrapped den Factory Kram für dich. Factories machen dann Sinn, wenn du die selbe Komponente mehrfach instanzieren willst.

eXile hat geschrieben:Und Fehlercodes? Knabe, bitte! Bei Konstruktion von mehreren Objekten muss ich ja auf korrekte Erstellung aller Objekte testen.
In der Tat, das musst du. Es gibt jedoch nichts, das dich daran hindert das über Exceptions zu regeln.

eXile hat geschrieben:Und eine Factory für jedes WRL-Objekt sehe ich einfach nicht sein.
Geht aber leider nicht wirklich anders. Wenn du WinRT Komponenten wie C++ Klassen betrachtest, versteh ich natürlich dass das für dich keinen Sinn ergibt. Dann liegt das Problem aber in deiner Betrachtungsweise. Du hantierst hier mit Interfaces, nicht mit Klassen. Insbesondere kann eine WinRT Komponente in allen möglichen Sprachen geschrieben sein. Hinter dem Interface könnte sich z.B. sogar ein .NET Objekt verstecken, das in der CLR am managed Heap liegt. Es gibt hier eigentlich praktisch keinen anderen Weg als eine Abstract Factory.

eXile hat geschrieben:Nunja, anscheinend weist deren Framework so viel Text-Redudanz auf, dass anstatt diese mit Sprachmöglichkeiten wie Templates zu reduzieren, sie stattdessen sich auf einen etwas schlaueren Precompiler verlassen.
Templates wären hier keine Lösung. Mit Templates kannst du z.B. keinen MIDL Code generieren und kompilieren...

eXile hat geschrieben:Und dass das ja nur ganz wenig Text vom ganzen Projekt ist, lasse ich auch nicht gelten.
Point taken. Wenn es rein ums Konsumieren von WinRT Objekten geht und nicht um das Erzeugen eigener WinRT Komponenten, dann kannst du das aber natürlich ohne C++/CX und ohne WRL machen und dir in stinknormalem C++ was Eigenes basteln. Ist ja alles nur COM ;)

eXile hat geschrieben:WRL macht das Schreiben von UIs immer noch scheiße.
XAML ;)

eXile hat geschrieben:Achja, und noch was: Sieht das nach elegantem, gut designtem C++-Code aus?
Ja, ich find die WRL auch nicht sonderlich elegant. Gibt imo aber dann doch wesentlich Schlimmeres ;)

Und wie gesagt, WinRT ist nur COM, du kannst sowohl auf C++/CX als auch auf die WRL verzichten und alles per Hand machen. Das ist aber bei weitem nicht so einfach wie es vielleicht scheint: http://www.interact-sw.co.uk/iangblog/2 ... nheritance
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Jammer-Thread

Beitrag von CodingCat »

dot hat geschrieben:RTTI und dynamic_cast sind definitiv unter den Top 10 der Dinge die sie von mir aus aus dem Standard nehmen könnten.
Ich brauche nur den statischen typeid-Operator, aber hier wäre es schon toll gewesen, wenn sie sich wenigstens auf einen standardisierten qualifizierten Typnamen hätten festlegen können. Von mir aus kann das Ding auch nur ein eindeutiges String-Literal zurückgeben, mehr brauche ich nicht. Aber implementation-defined ist hier einfach nur ärgerlich.
dot hat geschrieben:Binaries von verschiedenen Compilern sind aus Prinzip im Allgemeinen inkompatibel und ein standardisiertes ABI für C++ ist nicht in Sicht und es ist auch sehr unwahrscheinlich dass sowas jemals existieren wird... [...] Exceptions sollte man sowieso nicht über Interfacegrenzen werfen.
Naja, COM ist doch schonmal ein Anfang, der recht weitläufig funktioniert. Da geteilte Binaries ohnehin nicht ohne extra Indirektionen auskommen, muss ja nicht gleich die gesamte ABI standardisiert werden, für Methodenaufrufe sind V-Tables in diesem Zusammenhang schließlich quasi optimal (OK, mit Funktionszeigern könnte man eine Indirektion sparen). Aber Ausnahmen sind doch ein wichtiger Bestandteil vollständiger und strukturierter Fehlerbehandlung, und diese auf der einen Seite an der Modulgrenze einzupacken, um sie auf der anderen Seite mit einer Flut von Wrappern wieder auszupacken, ist eine absolut überflüssige Qual. Mit ThrowWinError( APICall(..., apiPtr.rebind()) ); kriegt man zwar die Wrapperzahl etwas in den Griff, aber lesbarer wird der Code dadurch auch nicht.
dot hat geschrieben:XAML
Was an XAML gut sein soll, habe ich bis heute nicht begriffen.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
dot
Establishment
Beiträge: 1746
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Jammer-Thread

Beitrag von dot »

CodingCat hat geschrieben:Ich brauche nur den statischen typeid-Operator [...]
statischer typeid Operator!?
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Jammer-Thread

Beitrag von CodingCat »

typeid(Type) liefert dir das type_info-Objekt zu Type. In Templates ist das im Zusammenhang mit Reflection enorm praktisch und, soweit ich weiß, der einzige Weg, irgendeinen eindeutigen Wert für einen bestimmten Typen zu erhalten. Funktioniert btw. auch mit RTTI disabled.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
Krishty
Establishment
Beiträge: 8342
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Jammer-Thread

Beitrag von Krishty »

Ich dachte, ich könnte thread-sicheres Logging ohne eigene Synchronisationsobjekte schreiben, indem jede Nachricht in einem einzelnen synchronen WriteFile()-Aufruf geschrieben würde – und dann habe ich gemerkt, dass synchrones WriteFile() nur atomar ist, so lange keine Sektorgrenze auf der Festplatte überschritten wird.

Warum lassen sie die Atomizität dann nicht ganz weg, so lange das Handle nicht mit FILE_FLAG_NO_BUFFERING erzeugt wurde (das ist das einzige, wo man sowieso auf die Sektorgröße achten muss)? Wie kann ich ein so unwichtiges Detail wie die Sektorgröße des Dateisystems den Zustand der Funktion bestimmen lassen? Jetzt ist das Schreiben der Datei doppelt synchronisiert; wobei die Synchronisierung der WinAPI dank diesem Sektorscheiß nichts bringt.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1746
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Jammer-Thread

Beitrag von dot »

CodingCat hat geschrieben:typeid(Type) liefert dir das type_info-Objekt zu Type. In Templates ist das im Zusammenhang mit Reflection enorm praktisch und, soweit ich weiß, der einzige Weg, irgendeinen eindeutigen Wert für einen bestimmten Typen zu erhalten. Funktioniert btw. auch mit RTTI disabled.
Ist das vom Standard garantiert, dass das zur Compiletime ausgewertet wird wenn der genaue Typ statisch bekannt ist?

Das Problem, in einem Haufen Templates zur Compiletime einen eindeutigen Wert für einen Typ zu bekommen, hatte ich auch vor kurzem, als ich einem Freund bei einem Projekt geholfen hab. Zusammen sind wir auf folgendes gekommen: Es gibt jemanden, dessen einziger Job es ist, zur Compiletime global eindeutige Werte zu produzieren: Den Linker. Lösung: Verwend einen (Member) Function Pointer ;)
Benutzeravatar
eXile
Establishment
Beiträge: 1136
Registriert: 28.02.2009, 13:27

Re: Jammer-Thread

Beitrag von eXile »

dot hat geschrieben:Es gibt jemanden, dessen einziger Job es ist, zur Compiletime global eindeutige Werte zu produzieren: Den Linker. Lösung: Verwend einen (Member) Function Pointer ;)
Gilt das auch nach COMDAT-Folding?
Benutzeravatar
dot
Establishment
Beiträge: 1746
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Jammer-Thread

Beitrag von dot »

eXile hat geschrieben:
dot hat geschrieben:Es gibt jemanden, dessen einziger Job es ist, zur Compiletime global eindeutige Werte zu produzieren: Den Linker. Lösung: Verwend einen (Member) Function Pointer ;)
Gilt das auch nach COMDAT-Folding?
In der Tat, COMDAT Folding wird ein Problem sein. Ich hatte das nur in CUDA Code gebraucht, da gibts sowas (noch) nicht und wir haben am Ende die Funktionen noch für was Andres verwendet, darum hab ich nicht dran gedacht...
Dann eben die Adresse eines statischen Members verwenden ;)
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Jammer-Thread

Beitrag von CodingCat »

dot hat geschrieben:
CodingCat hat geschrieben:typeid(Type) liefert dir das type_info-Objekt zu Type. In Templates ist das im Zusammenhang mit Reflection enorm praktisch und, soweit ich weiß, der einzige Weg, irgendeinen eindeutigen Wert für einen bestimmten Typen zu erhalten. Funktioniert btw. auch mit RTTI disabled.
Ist das vom Standard garantiert, dass das zur Compiletime ausgewertet wird wenn der genaue Typ statisch bekannt ist?
Was soll er denn anderes machen, ein Typ ist doch nicht mal ein Objekt, dessen V-Pointer zur Laufzeit dereferenziert werden könnte. Selbes gilt für nicht-polymorphe Objekte, da diese offensichtlich keinerlei Typinformation speichern können. In beiden Fällen erhälst du direkt eine Referenz auf das type_info-Objekt zu dem jeweiligen statischen Typ.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Jammer-Thread

Beitrag von CodingCat »

Kurzer Test rein interessehalber: Typnamen löst der Compiler leider nicht direkt zu Stringliteralen auf, sehr schade.

Code: Alles auswählen

	return typeid(float).name();
00D510C0  push        offset __type_info_root_node (0D540B0h)  
00D510C5  mov         ecx,offset float 'RTTI Type Descriptor' (0D5407Ch)  
00D510CA  call        dword ptr [__imp_type_info::_name_internal_method (0D530D4h)]
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
Krishty
Establishment
Beiträge: 8342
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Jammer-Thread

Beitrag von Krishty »

Weißt du auch, warum?

type_info::name() ist in Visual C++ so definiert:

char const * name(
    __type_info_node * __ptype_info_node = &__type_info_root_node
) const;


und __type_info_node ist wiederum die Datenstruktur einer verketteten Liste. Der Compiler durchsucht also alle Typinformationen in einer verketteten Liste, um den Namen zu finden. Dass das nicht wegoptimiert werden kann, wundert dann auch nicht mehr.
(Außerdem macht mich das fehlende const jedes Mal verrückt, wenn ich refactoren möchte!)


Ach nein; es ist viel lustiger:
http://stackoverflow.com/questions/200857/caching-a-const-char-as-a-return-type hat geschrieben:The MSDN page linked in the original question seems to fill in the blanks: the name is stored in its decorated form to save space, and this form is accessible via type_info::raw_name(). When you call type_info::name() for the first time on a given type, it undecorates the name, stores it in a heap-allocated buffer, caches the buffer pointer, and returns it.
<3

Man könnte nun versucht sein, raw_name() zu nutzen. Leider kommt das aber aus der Laufzeitbibliothek und wird nicht statisch gebunden. Aber ein Sprung in die Funktion verrät:

Code: Alles auswählen

mov         qword ptr [rsp+8],rcx  
mov         rax,qword ptr [this]  
add         rax,10h  
ret
Die Funktion tut nichts anderes, als den Speicher 16 Bytes hinter dem this-Zeiger zurückzugeben. Mal gucken, ob wir das auch selber hinkriegen:

auto const & foo = typeid(char);

foo auf den Debugger gezogen enthüllt die Namen der Datenstruktur:
-    foo        {_M_data=0x0000000000000000 _M_d_name=0x000000013f90c028 ".D" } const type_info &
+    __vfptr    0x000000013f8bdbd0 const type_info::`vftable' *
     _M_data    0x0000000000000000 void *
+    _M_d_name  0x000000013f90c028 ".D" char [1]


Also schnell eine eigene type_info gebaut:

extern struct __type_info_node __type_info_root_node;

class type_info {
public:
    __declspec(dllimport) bool operator == (
        type_info const &
    ) const noexcept;

    __declspec(dllimport) bool operator != (
        type_info const &
    ) const noexcept;

    __declspec(dllimport) int before(
        type_info const &
    ) const noexcept;

    __declspec(dllimport) char const * name(
        __type_info_node * __ptype_info_node = &__type_info_root_node
    ) const;

    // define here instead of importing it from the C run-time library
    // (does this cause memory leaks if 'name()' was called? I don't fucking care)
    virtual ~type_info() noexcept { }

    // (specific to Visual C++ 2010)
    size_t hash_code() const noexcept { … }

    // define here instead of importing it from the C run-time library (specific to Visual C++ 2010)
    char const * raw_name() const noexcept {
        return _M_d_name;
    }

private:
    type_info(type_info const &);
    type_info & operator = (type_info const &);

    void * _M_data;
    char _M_d_name[1];
};


und schon wird
    typeid(char).raw_name();
optimiert zu:
    lea rdx,[char `RTTI Type Descriptor'+10h (13F05E080h)]

Dekoriert bringt er dir zwar nicht viel (bei mir lautet er „.D“), aber zum Vergleichen reicht’s.

Bitteschön :)

Apopo Vergleichen (wollte nur mal Popo sagen, hihihi): Meine telepathische Verbindung zur MSVCRT.DLL suggeriert mir, dass in operator == (), operator != () und before() nur ein String-Vergleich beginnend bei Zeichen [1] durchgeführt wird; und strcmp() ist ja zum Glück ein Compiler-Intrinsic.

So. Jetzt stört nur noch der Virtual Function Table … leider kriegen wir den nicht aus der Klasse raus, weil Visual C++ die Daten für uns generiert und unsere Attribute dann verschoben wären. Jedoch: Eben WEIL Visual C++ die Daten für uns generiert, und da auch der VFT dazugehört; und der (virtuelle) D’tor niemals aufgerufen wird (die Daten sind ja statisch), können wir ein schönes __declspec(novtable) vor die Deklaration klatschen. Damit wären nochmal 16 Bytes gespart (selbst dann, wenn das Programm nirgends typeid() oder dynamic_cast<>() nutzt).


Damit hättest du die schnellste Visual C++-Typinformation bei nicht-statischer Laufzeitbibliothek der Welt ;-)

Übrigens ist das einer der Gründe, warum ich alles selber schreibe: Wenn jede Pisselfunktion aus einer DLL kommt, bringt LTCG einfach garnichts.
Zuletzt geändert von Krishty am 02.06.2012, 19:07, insgesamt 17-mal geändert.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Antworten