2015-12-21 16 views
9

Wahrscheinlich bin ich nicht die erste Person, die herausfindet, dass std::exception_ptr verwendet werden könnte, um einen any Typ zu implementieren (Leistungsaspekte beiseite gelegt), da es wahrscheinlich der einzige Typ in C++ ist halte etwas. Googeln brachte jedoch kein Ergebnis in diese Richtung.std :: any von std :: exception_ptr

Weiß jemand, ob der folgende Ansatz irgendwo für etwas Nützliches verwendet wurde?

#include <exception> 
#include <iostream> 

struct WrongTypeError : std::exception { }; 

class Any { 
public: 
    template <class T> 
    void set (T t) { 
     try { throw t; } 
     catch (...) { m_contained = std::current_exception(); } 
    } 

    template <class T> 
    T const & get() { 
     try { std::rethrow_exception (m_contained); } 
     catch (T const & t) { return t; } 
     catch (...) { throw WrongTypeError {}; } 
    } 

private: 
    std::exception_ptr m_contained = nullptr; 
}; 

int main() { 
    auto a = Any {}; 
    a.set (7); 
    std::cout << a.get<int>() << std::endl; 

    a.set (std::string {"Wonderful weather today"}); 
    std::cout << a.get<std::string>() << std::endl; 
    return 0; 
} 
+0

Ich habe den Code bearbeitet. Ich denke, es schneidet nicht mehr. – JohnB

+0

g ++ 4.8.4 hat keine Probleme, zehn Millionen 'Any's zu erzeugen und sie in einem' Vektor' zu speichern. Es ist jedoch schrecklich langsam. (30 Sekunden für das Setzen und Erhalten dieser 10 Millionen Werte.) – JohnB

+1

Eigentlich dachte ich immer, dass ein besserer Trick (seit es seit C++ 98 verfügbar ist) wäre, 'std :: locale' zu ​​verwenden, was immer du willst als eine Facette. –

Antwort

5

wie es wahrscheinlich der einzige Typ in C++, die alles halten kann.

Ich fürchte, das ist nicht der Fall. boost :: any kann jeden Typ halten und sogar Kopien (vorausgesetzt, der Typ ist kopierbar) auch korrekt. Es implementiert ist (grob gesagt) eine Basisklasse und eine Templat-Kind mit:

class any_base { 
    ... 
} 

template <class T> 
class any_holder : public any_base 
{ 
private: 
    T m_data; 
} 

Daraus können Sie sich vorstellen, dass Sie jede Art in einen any_holder stopfen kann (mit der richtigen Schnittstelle), und dann können Sie eine halten any_holder durch Zeiger auf any_base. Diese Technik ist eine Art von Typ löschen; Sobald wir einen any_base-Zeiger haben, halten wir ein Objekt, wissen aber nichts über den Typ. Man könnte sagen, das ist Total Type Erasure, sowas wie std :: function bietet Partial Type Erasure (und kann ähnliche Techniken unter der Haube verwenden, ich bin mir nicht ganz sicher, ob ich von Kopf weg bin).

boost :: any stellt eine zusätzliche Schnittstelle zur Verfügung, um die Verwendung eines beliebigen Typs zu unterstützen, und es bietet wahrscheinlich eine bessere Leistung, da das Werfen von Ausnahmen verrückt langsam ist. Wie ich bereits erwähnt habe, kopiert es das zugrunde liegende Objekt korrekt, was ziemlich cool ist. exception_ptr ist ein geteilter Eigentumsrecht-Zeiger, also glaube ich, dass es flache Kopien bildet.

-Boost jede Website: http://www.boost.org/doc/libs/1_59_0/doc/html/any.html

Es ist für die Standard betrachtet Sein Ich glaube: http://en.cppreference.com/w/cpp/experimental/any

Es scheint, wie die Umsetzung ist ähnlich zu steigern, sondern fügt ein kleines Objekt-Optimierung.

exception_ptr ist ein ziemlich seltsames Biest, soweit ich das beurteilen kann, ich habe es schon mal gesehen und es gegoogelt und es gibt überraschend wenig Informationen da draußen. Ich bin mir jedoch ziemlich sicher, dass es magisch ist, d. H. Es kann nicht im Benutzerbereich implementiert werden. Ich sage das, denn wenn man es wirft, scheint sich der Typ magisch zu lösen, das ist im Allgemeinen nicht möglich.

+0

Das ist, wie ich mein "Any" gemacht habe, obwohl der Mechanismus eigentlich einfach genug ist, dass es nur mit void * und einem const type_info & implementiert werden kann, aber dann müssen Sie den Destruktor speichern, wenn Sie eine ordnungsgemäße Bereinigung benötigen. – stellarpower

3

Sie sind sicherlich die erste Person, auf die ich gestoßen bin, die darüber nachgedacht hat.

Ich bin auf jeden Fall von Ihrem Querdenken Fähigkeiten beeindruckt :)

Es gibt ein Problem mit diesem Ansatz jedoch (anders als das offensichtliche Performance-Problem).

Dies rührt von der Tatsache her, dass throw eine Kopie des geworfenen Objekts erstellen darf.

Dies bedeutet erstens eine Beschränkung auf das, was Sie in Ihrer Klasse 'any' speichern können, zweitens hat es weitere Auswirkungen auf die Leistung und drittens ist der Compiler nicht jedes Mal, wenn Sie auf Ihr Objekt zugreifen, verpflichtet, Ihnen dasselbe zu geben. Es ist erlaubt, dir eine Kopie zu geben. Dies bedeutet, dass Sie zumindest unveränderliche Objekte auf diese Weise speichern sollten. (Hinweis: Wenn ich sage 'sollte' ich wirklich 'absolut nicht sollte!') :)

Sie könnten dies umgehen, indem Sie eine Klasse erstellen, die Speicher zum Speichern des Objekts zuweist, zeichnet es auf und löscht es richtig ... Aber wenn Sie das getan hätten, wären Sie besser ohne die Ausnahme Komplikation sowieso.

In jedem Fall ist das, was boost :: any unter den Deckeln tut.

+1

"Ich bin definitiv beeindruckt von deinen Querdenkenfähigkeiten" - ich kann nicht zweimal upvoten, oder? – JohnB

Verwandte Themen