designfrage: futures
-
Artchi schrieb:
empty() reicht meiner Meinung nach völlig. Weil empty auf deutsch "leer" und nicht "leeren" (to empty) heißt. Also zum leeren wäre vielleicht to_empty() sinnvoll? to_ als prefix habe ich aber selten gesehen.

Interessante Argumentation. Auf der anderen Seite fragt man einen Container mit "clear" doch auch nicht ob er "eindeutig" oder "durchsichtig" ist, sondern will ihn leeren (to clear).
Ich habe mir angewöhnt get_ set_ und is_ Präfixe für die Eigenschaften zu nehmen - aber wie immer, alles eine Frage des persönlichen Stils. Wie man oben sieht macht's die Standardbibliothek auch nicht immer konsequent

Artchi schrieb:
Wäre das Problem mit dem is_ und nicht is_ gelöst, wenn man ein Fragezeichen im Bezeichner benutzen könnte?
Hier könnte tatsächlich eine Eigenschaftensyntax mehr Übersichtlichkeit bringen - Klammern: Funktion, keine Klammern: Datum. Aber selbst das müsste man konsequent durchziehen, sonst bringt das alles nichts.
-
LordJaxom schrieb:
Interessante Argumentation. Auf der anderen Seite fragt man einen Container mit "clear" doch auch nicht ob er "eindeutig" oder "durchsichtig" ist, sondern will ihn leeren (to clear).
Stimmt! Denke das kommt vielleicht daher, das die Klammern sagen "mach was!" (halt Funktion). Die Klammern habe ich bei meiner to_-Theorie nicht bedacht.
clear(); ^ ^ | | | machen sauberYo, eigentlich ganz einfach.

LordJaxom schrieb:
Ich habe mir angewöhnt get_ set_ und is_ Präfixe für die Eigenschaften zu nehmen - aber wie immer, alles eine Frage des persönlichen Stils. Wie man oben sieht macht's die Standardbibliothek auch nicht immer konsequent

In der Std-Lib gibts aber gar kein is, get oder set. Mir ist das schon immer ein Rätzel gewesen, wie ich eigentlich in der Std-Lib Eigenschaften setze: Alles wird durch Aktionen bestimmt, die die Eigenschaften implizit setzen. Eigenschaften kann ich eigentlich nur abfragen (begin, end, size usw.). Aber ich kann die nie wirklich direkt setzen.
Oder fällt jemand eine Methode ein, die wirklich als Präfix set hat?
-
Dort weden keine Präfixe verwendet. StringStreams haben zB die beiden Member
const std::string& str(); // get_String void str(const std::string&) // set_Stringoder so ähnlich...
-
find prefixes für methoden sind ne feine sache, um leicht unterscheiden zu können, was die machen. außerdem sorgen sie dafür, dass die ganzen code completion tools anständig filtern können. wenn ich z.b. wissen will, was mir eine klasse so alles an informationen liefern kann, muss ich mir nur methoden mit nem bestimmten prefix angucken. aber bleibt halt geschmackssache.
vom überladen des () operators rate ich übrigens ab. hab das ne zeitlang auch mal gemacht und es führte schlußendlich dazu, dass der code unglaublich unleserlich wurde. man mag sich erst denken, dass so ein operator ne tolle sache ist, weil er zeichen spart, aber im endeffekt sorgt er mehr für verwirrung.
foo f(23, 32); double d = f(12, 32) + foo(50, 300)(12, 32);... zum davonlaufen ^^
-
Also, der Reihe nach (das wird dauern, hab' nicht mit so vielen Antworten gerechnet) ...
Was ist denn der Zweck der Klasse? Kann sein, dass eine future-Klasse per se eine bestimmte Aufgabe hat, aber ich denke ein Großteil der Benutzer hier weiß erstmal nix damit anzufangen
Eine future ist ein quasi eine Art Variable die erst "in der Zukunft" einen Wert bekommt.
Verwenden tut man das dann meist in Zusammenhang mit Threads (muss aber nicht, man kann genauso ne future basteln die den Wert sofort bestimmt oder die ihn einfach in dem Thread ausrechnet der den Wert abfragt).
Vielleicht helfen 2 kleine Beispiele dass du dir was vorstellen kannst:// beispiel 1: typedef boost::shared_ptr<image> image_ptr; class my_super_game { public: static int const background_id = 1; static int const menu_graphics_id = 2; my_super_game() : m_thread_pool(1) // "pool" mit einen thread anlegen (nur einer, sonst würde sich load_image beim disk IO selbst ausbremsen) { // laden der bilder starten m_images[background_id] = xyz::make_future(m_thread_pool.start(&my_super_game::load_image, boost::bind(L"bg.bmp")); m_images[menu_graphics_id] = xyz::make_future(m_thread_pool.start(&my_super_game::load_image, boost::bind(L"menu.bmp")); } void draw_background() { image& bg = get_image(background_id); // bild holen // ... } image& get_image(int id) const { return *(m_images[id].get_result()); // wartet bis das bild geladen wurde und gibt dann das ergebnis zurück } static image_ptr load_image(std::wstring const& name) { // ... } private: xyz::thread_pool m_thread_pool; std::map<int, xyz::future<image_ptr> mutable m_images; }; // beispiel 2: void foo(int a, int b, int c) { // hoffentlich selbsterklärend: xzy::future<int> x(xyz::start_thread(boost::bind(&lengthy_computation_1, a, b))); xzy::future<int> y(xyz::start_thread(boost::bind(&lengthy_computation_2, b, c))); return x() + y(); }
-
Helium schrieb:
'Ne Abfrage, ob ne Exception oder ein Ergebnis vorliegt wäre vielleicht manchmal ganz nett, aber IMO nicht unbedingt wichtig. Zumal man dann ja erst waren müsste, dann gucken ob ein wert vorliegt und dann den Wert abholen.
Naja, wenn dann würde die "wurde mit exception beendet?" Funktion blockieren, anders macht das IMO keinen Sinn.
Eine andere Möglichkeit wäre der "is_finished" bzw. "is_ready" Funktion nen int als Returnwert zu verpassen wo dann 0 = läuft noch, 1 = erfolgreich, -1 = exception. Ist aber nicht sehr selbsterklärend, von daher werde ich das wohl eher nicht machen.Ein "get" statt "get_result" würde mir in dem Fall reichen. Vielleicht aber auch der C++-typische operator*.
Nö, also () UND * überlade ich sicher nicht. Und () wird fix überladen (einfach weil das Ding als Funktor verwendbar sein soll).
-
Das ist jetzt Absicht, dass man hier den asynchronen Aufruf noch nicht sieht, oder? EDIT: Achso, das ist get_task_handle oder? Das würde ich aber echt anders nennen. EDIT2: Ok. Der ()-Operator macht den Aufruf nehme ich an. Was macht get_task_handle? Und gibt es eine Möglichkeit, den Future auch direkt blockierend aufzurufen (will man manchmal auch)?
Würdet ihr eine "is_ready" Funktion einbauen -- bzw. eine solche Funktion unter einem anderen Namen?
Ja, habe ich schon ein paar mal brauchen können. Würde ich auf jeden Fall einbauen.
-
Artchi schrieb:
empty() reicht meiner Meinung nach völlig. Weil empty auf deutsch "leer" und nicht "leeren" (to empty) heißt. Also zum leeren wäre vielleicht to_empty() sinnvoll? to_ als prefix habe ich aber selten gesehen.

Das liegt wohl einfach daran dass C++ eine imperative Programmiersprache ist, und der Imperativ von "to empty" ist nunmal "empty!" (und der von "to clear" eben "clear!")

-
Optimizer schrieb:
Das ist jetzt Absicht, dass man hier den asynchronen Aufruf noch nicht sieht, oder? EDIT: Achso, das ist get_task_handle oder? Das würde ich aber echt anders nennen.
Der future selbst ist das im Prinzip egal wo der Wert herkommt, die soll nur ein common Interface sein für
* threads
* thread pools
* lazy evaluation
* stink normale "fertige" werte (wenn man mit was interfacen muss was ne future annimmt, aber keine braucht)
* theoretisch sogar asynchrone IOs ohne eigenen thread
Der asynchrone Aufruf ist das mit was man die future "anfüllt" bevor man sie verwenden kann.
Und was würdest du anders nennen? Die "get_task" Funktion müsste man garnicht unbedingt haben, die ist nur dazu da dass man nen "task" den man reingesteckt hat auch wieder "auslesen" kann, falls man das mal braucht.
Und zum Erzeugen dieser "task handles" sind dann Klassen wie thread_starter, thread_pool etc. zuständig, bzw. wer auch immer das Interface "abstract_task_handle" implementieren will.Würdet ihr eine "is_ready" Funktion einbauen -- bzw. eine solche Funktion unter einem anderen Namen?
Ja, habe ich schon ein paar mal brauchen können. Würde ich auf jeden Fall einbauen.
Jop, werde ich dann wohl machen.
-
thordk schrieb:
vom überladen des () operators rate ich übrigens ab. hab das ne zeitlang auch mal gemacht und es führte schlußendlich dazu, dass der code unglaublich unleserlich wurde. man mag sich erst denken, dass so ein operator ne tolle sache ist, weil er zeichen spart, aber im endeffekt sorgt er mehr für verwirrung.
Den () will ich auf jeden Fall überladen, damit das Ding ein Funktor ist. Einige Libraries (wie z.B. die STL selbst oder auch boost) können mit Funktoren schön umgehen, dadurch werden einige Dinge recht elegant möglich die sonst irgendwelche blöden Helferklassen oder zusätzliche Aufrufe von boost::bind benötigen würden.
Auf jeden Fall wird's aber zusätzlich eine "namentliche" Methode ala "get_result" geben.
-
Ich hab deinen Future zu sehr als Funktor mit Future-Funktion gesehen. Jetzt ist es klarer, es soll nur das Ergebnis repräsentieren, ok.
-
Danke für die ausführliche Erklärung hustbaer! Diese futures sind ja eine abgefahrene Sache an die mein bescheidener Verstand gar nicht zu denken gewagt hätte

Das Interface kommt jedenfalls schonmal in meine Schnipsel-Sammlung, wenn du nichts dagegen hast. Würde es dir etwas ausmachen, den kompletten Code zu posten, wenn die Klasse fertig ist?
-
@Badestrand:
Es geht um etwas mehr als bloss eine Klasse, das wird eine ganze Threading Library
Und es wird leider noch dauern (paar Monate schätze ich) bis die Sache fertig ist (Windows Version, andere OSe kommen später dran).Kommt aber dann auf jeden Fall auf Source-Forge, und ich werde sicher hier kurz posten.
-
p.S.: was das "is_" oder nicht "is_" angeht: nachdem die Meinungen hier ziemlich 50:50 verteilt sind (wenn ich richtig gezählt habe) werde ich einfach nach meiner persönlichen Vorliebe "is_" und "get_" etc. verwenden.
(Was eigentlich irgendwie sowieso klar war, da die ganzen anderen Klassen in der Library auch eher "sprechend & lang zum schnell verstehen" als "schön kurz für kleine bildschirme" gehalten sind.)
-
hustbaer schrieb:
Eine andere Möglichkeit wäre der "is_finished" bzw. "is_ready" Funktion nen int als Returnwert zu verpassen wo dann 0 = läuft noch, 1 = erfolgreich, -1 = exception. Ist aber nicht sehr selbsterklärend, von daher werde ich das wohl eher nicht machen.
Wieso denn irgendwelche Zauberkonstanten? Wieso nicht eine Enumeration, die teil des futures ist, so dass man dann
if (foo.is_ready() == future<...>::exception)schreibt.
Wobei das auch doof ist.if (foo.is_ready())Soll ja auch Funktionieren.
Dann müsste man sich da einen Tick mehr Mühe geben und irgendwas zurückliefern, das sich in if() verwenden lässt, aber auch mit bestimmten vorgefertigten Konstanten (successfull, ...) vergleichen lässt.
-
Ich finde die Future-Sachen auch toll, sie werden höchstwahrscheinlich in C++0x oder TR2 enthalten. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2276.html
Bin sogar der Meinung, das Herb Sutter es mal als Sprachfeature (und nicht als Lib) vorgestellt hatte.
-
Als teil des Concur-Projekts
-
Helium schrieb:
Wieso denn irgendwelche Zauberkonstanten? Wieso nicht eine Enumeration, die teil des futures ist, so dass man dann
if (foo.is_ready() == future<...>::exception)schreibt.
Wobei das auch doof ist.if (foo.is_ready())Soll ja auch Funktionieren.
Vielleicht
switch (foo.get_state()) { case future<...>::busy: case future<...>::ready: case future<...>::exception_occured: }und dazu
inline bool is_ready() const { return get_state() == ready; } // etc.
-
-