2017-01-23 5 views
0

Ich schreibe einige Arduino-Bibliotheken und möchte die Lesbarkeit verbessern/einige syntaktische Suggar hinzufügen.Verkettete anonyme Objekterstellung auf dem Heap

Was würde ich tun möchte, ist in einer Art und Weise Objekte auf dem Heap erstellen, die aussehen würde:

Panel panel( 
    Button(1).on(Click(clickfunc)), 
    Button(2).on(Hold(holdfunc, 1000)) 
); 

(Knopf, Click, Halten sind alle Klassen und intern verwaltet über verkettete Listen (so sie aren ‚t konstant.))

ich habe versucht, es auf diese Weise zu schreiben, aber ich stolperte über Probleme mit Verweis auf Provisorien.

Zur Zeit kann ich:

Button button1(1), button2(2); 
Click theClick(clickFunction); 
Hold theHold(holdFunction, 1000); 
Panel(button1.on(theClick), button2.on(theHold)); 

aber das ist bei weitem nicht so lesbar wie die oben und neigt dazu, fehleranfällig zu sein, weil Sie wachsam bleiben müssen und nicht setzen z.B. Klicken Sie auf eine andere Schaltfläche, die die verknüpfte Liste durchbrechen würde.

Einige stark verkürzte Ausschnitte aus den Klassen wie sind sie jetzt.

class Button { 
    Handler *_first; 
    Button(int no){...} 
    Button & on(Handler &handler){ 
     handler._next = _first; 
     _first = &handler; 
     return *this; 
    } 
    void handle(int oldValue, int newValue) { 
     Handler *handler; 
     for(handler = _first; handler; handler = handler->_next){ 
      handler->handle(oldValue, newValue); 
     } 
    } 
} 
class Handler { 
    Handler *_next; 
    virtual void handle(int oldValue, int newValue) = 0; 
    ... 
} 
class Click : public Handler { 
    ... 
} 
class Hold : public Handler { 
    ... 
} 

Beachten Sie, dass dies nicht unbedingt so bleiben muss. Das Ziel besteht darin, eine Bibliothek bereitzustellen, in der der Benutzer nicht viel über seine innere Funktionsweise wissen muss, sondern über eine einfache/saubere Schnittstelle verfügt.

+1

Hinweis: Objekte auf dem Heap sind immer anonym, da sie nie eine haben Name: Sie sind niemals Variablen. –

+0

Können Sie die genauen Fehler hinzufügen, die Sie erhalten haben, als Sie versuchten, es zu einer Zeile zu bringen? – George

+0

@drescherjm: vielleicht. Aber warum denkst du, es ist nicht auf dem Haufen? (Oder bin ich das Wort „Haufen“ zu Unrecht in cpp Zusammenhang mit?)) – Scheintod

Antwort

1

Wenn Sie Problem mit baumelnden Referenzen mit dem Code haben oben, ich vermute, Sie machen eine verknüpfte Liste, die Verweise (oder Zeiger) erstellen, die auf dem Stapel zu jenem Elemente verweist.

ich auch vermuten, dass Ihre Unterschrift wie folgt aussieht:

Button& on(const Event& event) { /* ... */ } 

Um Ihnen bei Ihrem Problem zu helfen, schlage ich vor, die Unterschrift Ihrer on Funktion, so etwas zu ändern:

template<typename EventType> 
Button& on(EventType&& event) { 

} 

auf diese Weise können Sie tatsächlich das Objekt in den Haufen, weiterleiten und eine Form der Art easure verwenden Sie ihn auf Ihrer Liste zu setzen:

struct Handler { 
    virtual void handle(int oldValue, int newValue) = 0; 

    // Defaulted virtual destructor 
    virtual ~Handler() = default; 
}; 

template<typename T> 
struct HandlerImpl : Handler { 
    // constructors 
    HandlerImpl(T h) : handler{std::forward<T>(h)} {} 

    void handle(int oldValue, int newValue) { 
     handler.handle(oldValue, newValue); 
    } 

    // We use the compiler generated destructor 

private: 
    remove_rvalue_reference_t<T> handler; 
}; 

template<typename HandlerType> 
Button& on(HandlerType&& event) { 
    // See the code example below 
} 

Was ändert sich im Rest Ihres Codes?

Nun, jetzt werden beide von Ihnen gepostete Syntax unterstützt. Die erste Syntax wird die Variable verschieben und halten. Die zweite Syntax enthält nur Verweise auf die Ereignisse und nimmt an, dass die Lebensdauer des Ereignisses gleich oder größer als die der Schaltfläche ist.

Außerdem müssen Click und Hold keine Klasse erweitern noch virtuelle Funktion oder virtuelle Destruktoren benötigen.

Wenn Sie nicht über die zweite Syntax wollen Referenzen halten und Kopie verwenden stattdessen ersetzen remove_rvalue_reference_t durch std::remove_reference_t.

Dieses Muster zeigte ich Ihnen für Button angewendet werden kann, und für jedes Widget-Typ Sie wollen.


Hier ist, wie remove_rvalue_reference_t implementiert:

template<typename T> struct remove_rvalue_reference { using type = T; }; 
template<typename T> struct remove_rvalue_reference<T&&> { using type = T; }; 
template<typename T> using remove_rvalue_reference_t = typename remove_rvalue_reference<T>::type; 

Da Sie ein Beispiel für den Code geschrieben haben, kann ich jetzt helfen, es transformieren, damit es oben mit dem Code arbeiten können.

Zuerst mochte Liste langsam sind, und handgerollte Liste mochte schlimmer werden. Ich empfehle Ihnen dringend, std::vector zu verwenden. Zweitens ist std::unique_ptr der bevorzugte Weg, Besitzzeiger zu halten. Also, nur durch das folgende und die Schritte oben erwähnt, sollte der Code wie folgt aussehen:

struct Button { 
    std::vector<std::unique_ptr<Handler>> _handlers; 

    Button(int no) { /* ... */ } 

    // This function will work for any type that 
    // happen to have an `handle` function. 
    template<typename H> // <--- H is the handler type 
    Button& on(H&& handler) { // H&& in this case means forwarding reference. 
     // We add (emplace) a new HandlerImpl, allocated on the heap using `std::make_unique` 
     _handlers.emplace_back(
      std::make_unique<HandlerImpl<H>>(std::forward<H>(handler)) 
     ); 

     return *this; 
    } 

    void handle(int oldValue, int newValue) { 
     // We use a range for loop here to iterate on the vector 
     for (auto&& handler : _handlers) { 
      handler->handle(oldValue, newValue); 
     } 
    } 
}; 

// We do not extends anything 
struct Click { 
    // Notice that the function is not virtual 
    void handle(int oldVal, int newVal) {/* ... */} 
}; 

struct Hold { 
    void handle(int oldVal, int newVal) {/* ... */} 
}; 

Hier ist ein anschauliches Beispiel bei Coliru

+0

Danke für die Antwort! Aber heilige Scheiße (!) Jetzt muss ich erst einmal tief in alle Arten von C++ Zeug graben, von denen ich keine Ahnung habe. (Aber Schande über mich, ich fragte nach Magie;) – Scheintod

+0

@Scheintod Ich fügte ein konkreteres Beispiel, das mit Ihrem Code funktioniert. –

+0

Danke nochmal. Immer noch versuchen, Sachen herauszufinden :) Ich denke, auf Arduino habe ich nicht std :: vector. Hm. std :: unique_ptr es mag es auch nicht ... – Scheintod

Verwandte Themen