boolscher Operator
-
HEZ schrieb:
operator void* ... ich stehe ja eigentlich nicht so auf void-Zeiger... da lässt sich doch noch viel mehr Wahnsinn zuweisen?
Nunja, eigentlich kannst du nur andere Zeiger damit vergleichen. Du kannst es aber auch vollkommen idiotensicher machen. Dazu nimmst du eine simple Klasse, zB:
class safe_bool { public: typedef void (safe_bool::*type)(); static type value(bool source) { return source ? &safe_bool::non_null : NULL; } private: void non_null() { } };Die Verwendung sieht dann wie folgt aus:
operator safe_bool::type() const { return safe_bool::value(!this->VarName.IsEmpty() || !this->Value.IsEmpty()); }
-
[cpp]
class safe_bool
{
public:
typedef void (safe_bool::*type)();
[cpp]
sag mir ma, wie groß sizeof type ist. nicht schätzen. miss mal.void non_null()
{
}diese funktion wird leider generiert, weil da jemand von die adresse gezogen hat. ich würde lieber reinterpret_cast nehmen als sowas.
warum nicht this? das liegt doch eh im register cx und mit mov ax,cx breicht man nur einen takt, um this als returnwert zu haben.außerdem würde ich nen zeiger auf nen typen zurückgeben. auf struct NoType{}; vielleicht. der ist dann eh nur mit 0 vergleichbar, aber das macht nix.
-
richtig: messen
folgendes kleine program zum demonstrieren:#include <iostream> struct foo { void bar() const {} typedef void (foo::*safe_bool)() const; struct bar2 {}; static const bar2 b; operator bool() const { return m < RAND_MAX / 2; } // operator const void*() const { return m < RAND_MAX / 2 ? this : 0; } // operator const bar2*() const { return m < RAND_MAX / 2 ? &b : 0; } // operator safe_bool() const { return m < RAND_MAX / 2 ? &foo::bar : 0; } int m; }; const foo::bar2 foo::b; unsigned rdtsc() { __asm rdtsc } int main() { using namespace std; foo a; srand( rdtsc() ); a.m = rand(); if ( a ) cout << "true"; else cout << "false"; }das ganze compiliert mit vc++ 7.1 mit allen optimierungenergibt:
21: a.m = rand(); 0040101B E8 70 29 00 00 call rand (403990h) 22: if ( a ) 00401020 3D FF 3F 00 00 cmp eax,3FFFh 00401025 7D 13 jge main+2Ah (40103Ah) 23: cout << "true"; 00401027 68 28 C1 40 00 push offset string "true" (40C128h)mit operator const void*:
21: a.m = rand(); 0040101C E8 7F 29 00 00 call rand (4039A0h) 22: if ( a ) 00401021 33 C9 xor ecx,ecx 00401023 3D FF 3F 00 00 cmp eax,3FFFh 00401028 0F 9D C1 setge cl 0040102B 8D 14 24 lea edx,[esp] 0040102E 83 E9 01 sub ecx,1 00401031 85 CA test edx,ecx 00401033 74 14 je main+39h (401049h) 23: cout << "true"; 00401035 68 28 C1 40 00 push offset string "true" (40C128h)mit operator const bar2*
21: a.m = rand(); 0040101B E8 80 29 00 00 call rand (4039A0h) 22: if ( a ) 00401020 33 C9 xor ecx,ecx 00401022 3D FF 3F 00 00 cmp eax,3FFFh 00401027 0F 9D C1 setge cl 0040102A 83 E9 01 sub ecx,1 0040102D F7 C1 1C C1 40 00 test ecx,offset foo::b (40C11Ch) 00401033 74 13 je main+38h (401048h) 23: cout << "true"; 00401035 68 28 C1 40 00 push offset string "true" (40C128h)und mit operator safe_bool:
21: a.m = rand(); 0040102B E8 80 29 00 00 call rand (4039B0h) 22: if ( a ) 00401030 33 C9 xor ecx,ecx 00401032 3D FF 3F 00 00 cmp eax,3FFFh 00401037 0F 9D C1 setge cl 0040103A 83 E9 01 sub ecx,1 0040103D F7 C1 00 10 40 00 test ecx,offset foo::bar (401000h) 00401043 74 13 je main+38h (401058h) 23: cout << "true"; 00401045 68 28 C1 40 00 push offset string "true" (40C128h)nun sieht das mit einem anderen compiler sicher anders aus.
bleibt festzuhalten: nur operator bool() wird optimal übersetzt - das problem ist hier aber, dass der compiler sich nicht bewusst ist, dass benannte objekte niemals die adresse 0 haben können. das lässt sich heilen - allerdings nicht gänzlich standardkonform (da pointer auch trapvalues haben können):
mitoperator const void*() const { return m < RAND_MAX / 2 ? (const void*)1 : 0; }erhalte ich:
31: a.m = rand(); 0040101B E8 70 29 00 00 call rand (403990h) 32: if ( a ) 00401020 3D FF 3F 00 00 cmp eax,3FFFh 00401025 7D 13 jge main+2Ah (40103Ah) 33: cout << "true"; 00401027 68 28 C1 40 00 push offset string "true" (40C128h)wunderbar. das ganze geht auch für memberfuntionpointer:
operator safe_bool() const { union { unsigned x; safe_bool result; } result = { m < RAND_MAX / 2 }; return result.result; }31: a.m = rand(); 0040101B E8 70 29 00 00 call rand (403990h) 32: if ( a ) 00401020 3D FF 3F 00 00 cmp eax,3FFFh 00401025 7D 13 jge main+2Ah (40103Ah) 33: cout << "true"; 00401027 68 28 C1 40 00 push offset string "true" (40C128h)sehr schön. da jetzt auch bezug auf bar::foo() mehr genommen wird spart man diese 16 byte (1 byte function, 15 byte für ausrichtung des einsprungpunktes, so sinnlos das hier auch ist) auch noch ein.
fazit: mit ein paar tricks kann jede form optimalen code liefern. memberfunctionspointer sind dann aber am sichersten, denn auch mit bar* kann man noch viele unsinnige sachen wie pointerarithmetik betreiben (es sei denn, bar ist ein unvollständiger typ, aber dann muss man auch wieder einen hack wie oben benutzen, um ein bar* zurückliefern zu können). ausserdem ist sowohl bar* als auch void* mit jedem anderen void* pointer vergleichbar.
-
groovemaster schrieb:
Nunja, eigentlich kannst du nur andere Zeiger damit vergleichen. Du kannst es aber auch vollkommen idiotensicher machen. Dazu nimmst du eine simple Klasse, zB:
Also nehmen wir mal eine Klasse MeinStream, deren Zustand mit operator safe_bool überprüfbar ist, und eine Klasse Farbe, deren safe_bool "true" ergibt, wenn die Farbe nicht unsichtbar ist, und wir bekommen
void doBockmist(MeinStream& a, Farbe& b) { if (a == b) ...; }Boost bietet doch afaik eine safe_bool-Bibliothek an - ich würde mich einfach mal darauf verlassen, dass das da einigermaßen vernünftig gelöst ist.
-
safe_bool überprüfbar ist, und eine Klasse Farbe, deren safe_bool "true" ergibt, wenn die Farbe nicht unsichtbar ist, und wir bekommen
void doBockmist(MeinStream& a, Farbe& b) { if (a == b) ...; }das ist auch sinnvoller code.
der sagt "wenn a den gleichen gültigkeitswert hat wie b". null probleme und sinnvoll.
es gibt darum, daß man nicht bei
cout<<a; vom rechner "true" erzählt kriegt, sondern das richtige "ätsch, ich kann gar keinen MeinStream ausgeben".
-
So kann mans natürlich auch interpretieren. Ich finde das eine sehr verwirrende Benutzung des operator==, andererseits würde ich auch nie auf die Idee kommen, irgendeine spezielle Eigenschaft meines Objekts per if (obj) abzufragen. Ich bin dann eher der "bool isXyz() const"-Typ, glaube ich.
-
operator void schrieb:
So kann mans natürlich auch interpretieren. Ich finde das eine sehr verwirrende Benutzung des operator==, andererseits würde ich auch nie auf die Idee kommen, irgendeine spezielle Eigenschaft meines Objekts per if (obj) abzufragen. Ich bin dann eher der "bool isXyz() const"-Typ, glaube ich.
naja, jeder wird wohl irgendwann sowas gemacht haben:
Object* p=foo(); if(p){ .... }wieso dann nicht auch so?
SmartPointer<Object> p=foo(); if(p){ .... }
-
Mache ich, weil es geht und idiomatisch ist. Im Prinzip halte ichs da aber eher mit Java/C#

-
OMG, ich hatte wohl übersehen, dass ich im Assembler-Forum bin.

Vielleicht hätte ich dazu schreiben sollen, dass es für Leute, die im Mikrobereich optimieren, andere Möglichkeiten gibt. Das Beispiel war einzig und allein für Codesicherheit gedacht.volkard schrieb:
sag mir ma, wie groß sizeof type ist
Wozu? Stört dich der Member-Funktionszeiger? Den kann man natürlich noch eliminieren.
class safe_bool { private: struct closed { }; public: typedef void (*type)(closed); static type value(bool source) { return source ? &safe_bool::non_null : NULL; } private: static void non_null(closed) { } };volkard schrieb:
warum nicht this?
Weil ich in einer Non-Memberfunktion nunmal kein this habe. Ich bin aber schon gespannt auf ein Beispiel von dir.
-
groovemaster schrieb:
volkard schrieb:
sag mir ma, wie groß sizeof type ist
Wozu? Stört dich der Member-Funktionszeiger?
8 oder 12 bytes. nicht 4, und damit schmerzhaft.
Den kann man natürlich noch eliminieren.
class safe_bool { private: struct closed { }; public: typedef void (*type)(closed); static type value(bool source) { return source ? &safe_bool::non_null : NULL; } private: static void non_null(closed) { } };der vorteil deines member-zeugs war wohl, daß man die sehr schwer aus versehen casten kann.
einen möglichen fehler sehe ich sogar. man hantiert mit zeigern und placement-new. und das will nen void*. und man steckt nen safe_bool rein.
wird der aktuelle normale funktionszeiger nicht von allein in nen void* konvertiert?volkard schrieb:
warum nicht this?
Weil ich in einer Non-Memberfunktion nunmal kein this habe. Ich bin aber schon gespannt auf ein Beispiel von dir.[/quote]
satic zu nehmen ist neu.
mir scheint, reinterpret_cast muß her.
-
volkard schrieb:
der vorteil deines member-zeugs war wohl, daß man die sehr schwer aus versehen casten kann.
Yep.
volkard schrieb:
einen möglichen fehler sehe ich sogar. man hantiert mit zeigern und placement-new. und das will nen void*. und man steckt nen safe_bool rein.
wird der aktuelle normale funktionszeiger nicht von allein in nen void* konvertiert?An das implizite Casten nach void* hab ich jetzt beim zweiten Beispiel gar nicht mal gedacht. Aber ein weiterer Grund, weshalb ich wohl beim Member-Funktionszeiger bleiben werden.
volkard schrieb:
volkard schrieb:
warum nicht this?
Weil ich in einer Non-Memberfunktion nunmal kein this habe. Ich bin aber schon gespannt auf ein Beispiel von dir.
satic zu nehmen ist neu.
mir scheint, reinterpret_cast muß her.Ich bin immer noch auf Code von dir gespannt. Ich habe es nicht geschafft, gültigen Code mit this zu schreiben, da dies auch mein erster Ansatz war. Allerdings habe ich nicht allzu viel Zeit darin investiert, um sagen zu können, alle Fälle abgecheckt zu haben.