|
You last visited: Today at 03:57
Advertisement
SimpleSig - minimalistic signal/slots lib for C++
Discussion on SimpleSig - minimalistic signal/slots lib for C++ within the C/C++ forum part of the Coders Den category.
10/19/2013, 18:26
|
#1
|
elite*gold: 0
Join Date: Dec 2012
Posts: 255
Received Thanks: 110
|
SimpleSig - minimalistic signal/slots lib for C++
Hey, da nun Visual Studio 2013 im RTM stand ist, habe ich mal mein signal/slots lib angepasst.
Es ist unabhängig von irgendwelchen 3rd party libs (z.B. boost) und sollte sich problemlos auf jedem C++11 fähigen Compiler, der variadische Templates unterstützt benutzen lassen.
Das Library besteht aus einer einzigen Headerdatei, die ihr nur fix in euer Projekt einbinden braucht. Es unterstützt connection-objekte, mit denen man den Status einer Verbindung abfragen bzw. den Slot vom Signal disconnecten kann. ScopedConnection wird die Verbindung trennen, wenn das Connection-Objekt zerstört wird.
Zusätzlich lässt sich über custom-compare Funktionen ganz einfach das minimale/maximale Ergebins eines Aufrufs ermitteln. Slots können auch während eines Aufrufs ohne Probleme disconnected werden.
Source:
Beispiel:
Code:
int main(int argc, const char* argv[])
{
try {
// create some signal, return value float - arguments: int, float
SimpleSig::Signal<float(int, float)> someSignal;
// connect a slot and get a default connection
auto someSlot = someSignal.connect([](int a1, float a2) {
return a1 / a2;
});
if (someSlot)
std::cout << "someSlot is connected!" << std::endl;
// connect a sig with +=
someSignal += [](int a1, float a2) {
return a1 * a2;
};
{ // connect a sig and get a scoped connection
SimpleSig::ScopedConnection<float(int, float)> g =
someSignal.connect([](int a1, float a2) {
return a1 + a2;
});
// print all results
std::cout << "printing all " << someSignal.size() << " results..." << std::endl;
for (auto result : someSignal.invoke_getall(5, 7.1f))
std::cout << result << std::endl;
} // ~g, disconnects the slot
// get the minimum of all returned results...
// invoke and operator() return a proxy class which can be used to check if anything was returned
std::cout << "getting minimum value..." << std::endl;
std::cout << *someSignal.invoke<SimpleSig::Minimum>(5, 7.1f) << std::endl;
// how many slots are left? (should be 2)
std::cout << "there are still " << someSignal.size() << " slots connected!" << std::endl;
// use default compare
std::cout << "getting default value..." << std::endl;
std::cout << *someSignal(5, 7.1f) << std::endl;
someSignal.clear();
if (!someSlot)
std::cout << "someSlot is disconnected!" << std::endl;
// should throw an exception: attempt to access empty return value...
std::cout << *someSignal(1, 2.9f) << std::endl;
}
catch (SimpleSig::Error &e) {
std::cout << e.what() << std::endl;
}
std::cin.get();
return 0;
}
|
|
|
10/20/2013, 00:03
|
#2
|
elite*gold: 0
Join Date: Aug 2012
Posts: 236
Received Thanks: 94
|
Ich habe es mir nicht genau angesehen, inwiefern ist es Boost.Signal überlegen? Jene Bibliothek soll ja auch zu TR2 gehören (  ).
m_callLock sollte von Typ std::atomic<bool> sein.
Error::operator=(Error const&) implementiert das Standardverhalten. Warum nicht den Compiler arbeiten lassen?
std::make_unique gibt es (noch) nicht.
Es gibt neben void noch const volatile void etc.
Du hast das std::move um std::next vergessen. Das wird dich der Compiler büßen lassen.
std::forward_list<std::shared_ptr<Detail::Connecti onBase<signature_t>>> m_connections - Soll das eine Platzoptimierung sein? Dann ist std::vector warscheinlich besser, da dafür bei den meisten Standardbibliothek-Implementierungen Kapazität / Größe <= 1,5 gilt. Wenn jetzt sizeof(std::shared_ptr<T>) gleich 8 ist und die Größe der von std::forward_list<shared_ptr<T>> verwalteten Elemente 12 ist, dann verbraucht die einfach verkettete Liste niemals weniger Platz, bei geringerer Geschwindigkeit.
|
|
|
10/20/2013, 00:33
|
#3
|
elite*gold: 0
Join Date: Dec 2012
Posts: 255
Received Thanks: 110
|
Quote:
Originally Posted by Tasiro
Ich habe es mir nicht genau angesehen, inwiefern ist es Boost.Signal überlegen? Jene Bibliothek soll ja auch zu TR2 gehören (  ).
Und wie kann ich Funktionsobjekte übergeben?
m_callLock sollte von Typ std::atomic<bool> sein.
Error: perator=(Error const&) implementiert das Standardverhalten. Warum nicht den Compiler arbeiten lassen?
std::make_unique gibt es (noch) nicht.
|
Der Hauptgrund ist, dass mein Library von boost unabhängig ist (boost compilt im neusten trunk immer noch nicht mit VS 2013 RTM, also hab ich einfach mal meins angepasst, stört ja nicht  ).
Außerdem ist boost signals aus irgend einem Grund recht langsam (kannst einfach mit nem Benchmark gucken und mal paar tausend Aufrufe mit beiden libs machen), sogar noch langsamer als boost::signals2, obwohl boost::signals2 noch Threadsicher ist.
Mit 100000 Signalen + 10000 Aufrufen (+verbinden) hat boost::signals2 ~95 Sekunden gebraucht, das gleiche mit meinem Library dauerte allerdings nur ~20 Sekunden. Das war allerdings vor ich den return value Proxy geaddet hab, müsste man nun nochmal testen =).
Quote:
|
Und wie kann ich Funktionsobjekte übergeben?
|
Momentan gar nicht :P
Muss momentan einfach zu std::function kompatibel sein.
Quote:
|
m_callLock sollte von Typ std::atomic<bool> sein.
|
Wird trotzdem dann nicht threadsicher sein. Muss ich noch anpassen.
Quote:
Error: perator=(Error const&) implementiert das Standardverhalten. Warum nicht den Compiler arbeiten lassen?
|
Wird erledigt.
Quote:
|
std::make_unique gibt es (noch) nicht.
|
Ups, dann mach ich nachher nen Ersatz rein.
Quote:
|
Es gibt neben void noch const volatile void etc.
|
Fixed.
Quote:
|
std::forward_list<std::shared_ptr<Detail::Connecti onBase<signature_t>>> m_connections - Soll das eine Platzoptimierung sein? Dann ist std::vector warscheinlich besser, da dafür bei den meisten Standardbibliothek-Implementierungen Kapazität / Größe <= 1,5 gilt. Wenn jetzt sizeof(std::shared_ptr<T>) gleich 8 ist und die Größe der von std::forward_list<shared_ptr<T>> verwalteten Elemente 12 ist, dann verbraucht die einfach verkettete Liste niemals weniger Platz, bei geringerer Geschwindigkeit.
|
Das wäre aber ineffizienter beim verbinden von neuen Signalen. Da müsste im worst-case der ganze Vektor relocated werden, also komplett neu Speicher reservieren und den alten aufräumen, außer ich reserviere vorher mal ein wenig.
|
|
|
10/20/2013, 01:30
|
#4
|
elite*gold: 0
Join Date: Aug 2012
Posts: 236
Received Thanks: 94
|
Quote:
Originally Posted by Master674b
Wird trotzdem dann nicht threadsicher sein. Muss ich noch anpassen.
|
Es ist mit mutable versehen, also Grund genug. mutable und const bedeuten "ist thread-sicher". Warum überhaupt mutable? Modifizieren kannst du die Verbindungen eines konstanten Signals sowieso nicht.
Besser wäre natürlich std::mutex und dann bei jeder Verwendung ein std::lock_guard<std::mutex>, dadurch kann sich der Compiler in den meisten Fällen das nothrow denken.
Quote:
Originally Posted by Master674b
Das wäre aber ineffizienter beim verbinden von neuen Signalen. Da müsste im worst-case der ganze Vektor relocated werden, also komplett neu Speicher reservieren und den alten aufräumen, außer ich reserviere vorher mal ein wenig.
|
Im durschnittlichen Fall ist die Zeit konstant, und kleiner. Insgesamt wäre das Programm schneller. Oder verwendest du std::vector aus jenem Grunde überhaupt nicht, ihm könnte ja die Kapazität ausgehen?
|
|
|
10/20/2013, 01:34
|
#5
|
elite*gold: 0
Join Date: Dec 2012
Posts: 255
Received Thanks: 110
|
Quote:
|
Du hast das std::move um std::next vergessen. Das wird dich der Compiler büßen lassen.
|
Ich brauch itr noch für m_connections.erase_after(itr)
Quote:
Originally Posted by Tasiro
Es ist mit mutable versehen, also Grund genug. mutable und const bedeuten "ist thread-sicher". Warum überhaupt mutable? Modifizieren kannst du die Verbindungen eines konstanten Signals sowieso nicht.
Besser wäre natürlich std::mutex und dann bei jeder Verwendung ein std::lock_guard<std::mutex>, dadurch kann sich der Compiler in den meisten Fällen das nothrow denken.
|
Es ist mutable, da die invoke funktion const ist und trotzdem aufräumt.
|
|
|
10/20/2013, 01:37
|
#6
|
elite*gold: 0
Join Date: Aug 2012
Posts: 236
Received Thanks: 94
|
Quote:
Originally Posted by Master674b
Ich brauch itr noch für m_connections.erase_after(itr)
|
Veränderst du da denn itr? Das ist ein Rückgabewert.
|
|
|
10/20/2013, 01:43
|
#7
|
elite*gold: 0
Join Date: Dec 2012
Posts: 255
Received Thanks: 110
|
Quote:
Originally Posted by Tasiro
Veränderst du da denn itr? Das ist ein Rückgabewert.
|
Sorry, ich versteh grade nicht was du meinst. Wo soll da denn ein std::move fehlen?
|
|
|
10/20/2013, 01:45
|
#8
|
elite*gold: 0
Join Date: Aug 2012
Posts: 236
Received Thanks: 94
|
Quote:
Originally Posted by Master674b
Es ist mutable, da die invoke funktion const ist und trotzdem aufräumt.
|
Sollte es denn aufräumen? Wie oft geschieht es denn, dass du eine Referenz auf ein konstantes Signal ( const Signal&) erhälst und dann nach und nach die Verbindungen getrennt werden? Ist es nicht warscheinlicher, dass die Funktion bald zurückkehrt (z. B. nach einem invoke) und nicht irgendwo die Referenz speichert?
Quote:
Originally Posted by Master674b
Sorry, ich versteh grade nicht was du meinst. Wo soll da denn ein std::move fehlen?
|
Der Rückgabetyp von std::next ist T, also darf itr.operator=(T&&) nicht aufgerufen werden. Oder?
|
|
|
10/20/2013, 01:51
|
#9
|
elite*gold: 0
Join Date: Dec 2012
Posts: 255
Received Thanks: 110
|
Quote:
Originally Posted by Tasiro
Sollte es denn aufräumen? Wie oft geschieht es denn, dass du eine Referenz auf ein konstantes Signal (const Signal&) erhälst und dann nach und nach die Verbindungen getrennt werden? Ist es nicht warscheinlicher, dass die Funktion bald zurückkehrt (z. B. nach einem invoke) und nicht irgendwo die Referenz speichert?
Der Rückgabetyp von std::next ist T, also darf itr.operator=(T&&) nicht aufgerufen werden. Oder?
|
Muss es zwangsweise. Wenn ein Signal disconnected wird, wird zunächst nur die Funktion in der ConnectionBase resettet. Die muss dann irgendwann auch mal aus der liste gelöscht werden, am besten einfach beim nächsten invoke, wenn ich eh iterieren muss. Der shared_ptr destructor wird dann den Speicher freigeben.
Nein, std::next gibt ++itr zurück.
|
|
|
10/20/2013, 01:54
|
#10
|
elite*gold: 0
Join Date: Aug 2012
Posts: 236
Received Thanks: 94
|
Quote:
Originally Posted by Master674b
Nein, std::next gibt ++itr zurück.
|
Aber nicht itr selbst, sondern eine Kopie.
|
|
|
10/20/2013, 01:57
|
#11
|
elite*gold: 0
Join Date: Dec 2012
Posts: 255
Received Thanks: 110
|
Quote:
Originally Posted by Tasiro
Aber nicht itr selbst, sondern eine Kopie.
|
Es entspricht einfach:
Code:
auto next = itr;
++next
Ich benötige aber sowohl itr als auch next. Also sehe ich nicht wo hier ein std::move fehlt.
|
|
|
10/20/2013, 02:06
|
#12
|
elite*gold: 0
Join Date: Aug 2012
Posts: 236
Received Thanks: 94
|
Quote:
Originally Posted by Master674b
Muss es zwangsweise. Wenn ein Signal disconnected wird, wird zunächst nur die Funktion in der ConnectionBase resettet. Die muss dann irgendwann auch mal aus der liste gelöscht werden, am besten einfach beim nächsten invoke, wenn ich eh iterieren muss. Der shared_ptr destructor wird dann den Speicher freigeben.
|
Die Elemente werden aus der Liste gelöscht, sobald das Objekt zerstört wird.
Dass es schneller sei, während des Aufrufes von invoke die Elemente zu entfernen, glaube ich dir nicht ohne Tests.
Und wenn die Slots sowieso mit den Signalen verbunden sind, kannst du auch denen auch eine Referenz auf das Signal geben, wodurch sie sich selbst löschen können.
Quote:
Originally Posted by Master674b
Es entspricht einfach:
Code:
auto next = itr;
++next
Ich benötige aber sowohl itr als auch next. Also sehe ich nicht wo hier ein std::move fehlt.
|
Fast, es entspricht:
Code:
auto copy (itr);
++copy;
auto next (copy);
|
|
|
10/20/2013, 02:11
|
#13
|
elite*gold: 0
Join Date: Dec 2012
Posts: 255
Received Thanks: 110
|
Quote:
Originally Posted by Tasiro
Die Elemente werden aus der Liste gelöscht, sobald das Objekt zerstört wird.
Dass es schneller sei, während des Aufrufes von invoke die Elemente zu entfernen, glaube ich dir nicht ohne Tests.
Und wenn die Slots sowieso mit den Signalen verbunden sind, kannst du auch denen auch eine Referenz auf das Signal geben, wodurch sie sich selbst löschen können.
Fast, es entspricht:
Code:
auto copy (itr);
++copy;
auto next (copy);
|
Die Slots haben keine Ahnung vom Signal selbst. Sie können nur auf die ConnectionBase zugreifen, haben aber keine Chance an das Signal zu kommen. Deshalb kannst du das Signal auch ohne weiteres verschieben und die Slots würden immer noch valide sein.
Sicher dass es nicht einfach return ++itr macht?
|
|
|
10/20/2013, 13:52
|
#14
|
elite*gold: 7110
Join Date: Jun 2009
Posts: 28,902
Received Thanks: 25,407
|
Sagt mal versagt gerade mein Englisch oder was redet ihr von mutable und Threadsicherheit?
Konstante Objekte (also thread-sichere) sind doch gerade unmutable und nicht mutable.
Zu den Functors:
Ist std::function nicht eh mit denen kompatibel?
|
|
|
10/20/2013, 14:19
|
#15
|
elite*gold: 0
Join Date: Aug 2012
Posts: 236
Received Thanks: 94
|
Quote:
Originally Posted by Master674b
Sicher dass es nicht einfach return ++itr macht?
|
So oder so - es ist ein Rückgabewert, und der muss kopiert werden. Also mindestens eine Kopie. Außerdem dürfte std::advance genutzt werden.
Der Rückgabetyp eines Zuweisungsoperators sollte const T& sein, damit (a=b)=c nicht möglich ist.
Intelligente Zeiger wie std::shared_ptr weisen einen Operator bool auf, so musst du nicht prüfen, ob der Zeiger ungleich nullptr sei.
Warum so kompliziert void etc. ausschalten? Es reicht, ProxyRet<void> zu spezialisieren und ProxyRet<const void> etc. davon ableiten zu lassen.
std::move ist in utility definiert. std::enable_if in type_traits.
Compare (InputIterator first, InputIterator last): Die Benennung last ist irreführend. last wird niemals dereferenziert.
Minimum und Maximum sind einfacher mit std::min_element und std::max_element zu implementieren, Signal::size mit std::count_if.
ProxyRet <R> operator () (const Args &... args) const: Was ist mit "perfect forwarding"?
Warum muss ich die "custom-compare Funktionen" in eine Klasse packen, Compare nennen und zu Templatefunktionen machen? Warum nicht das Konzept der Funktionsobjekte wie in der Standardbibliothek unterstützen?
_generic_invoke(const FuncT& function): Wie wäre es mit remove_if?
Es müsste so gehen, ich habe es nicht getestet:
Code:
template <typename FuncT>
void _generic_invoke (const FuncT & function) const {
Detail::CallLock g (m_callLock);
m_connections.remove_if ([& function] (const std::shared_ptr <Detail::ConnectionBase <signature_t>> & connection) {
if (! connection->connected ())
return true;
function (connection->connectedSlot);
return false;
});
}
Quote:
Originally Posted by MrSm!th
Sagt mal versagt gerade mein Englisch oder was redet ihr von mutable und Threadsicherheit?
Konstante Objekte (also thread-sichere) sind doch gerade unmutable und nicht mutable.
|
mutable und const heißen beide "ist thread-sicher", oder sollten es. Auch ein konstantes Objekt (oder const T&) braucht ein veränderliches Objekt der Klasse std::mutex.
|
|
|
 |
|
Similar Threads
|
[Selling] Minimalistic Design - GFX-Shop by Carfunkel
04/09/2013 - elite*gold Trading - 1 Replies
https://dl.dropbox.com/u/64993027/selling/1epvp%20 gfx%20store.pnghttps://dl.dropbox.com/u/64993027/s elling/2referenzen.pnghttps://dl.dropbox.com/u/649 93027/selling/3was%20noch%20kommt.pnghttps://dl.dr opbox.com/u/64993027/selling/4im%20einsatz.pnghttp s://dl.dropbox.com/u/64993027/selling/5im%20einsat z.png
|
Ton-Signal bei PN.
01/26/2013 - Metin2 - 9 Replies
Servus,
gibt es einen Hack, Bot der, wenn man eine PN bekommt, ein Signalton abgibt?
Weil ich oft nen Laden auf habe und was anderes vorhabe als alle 5 Minuten zu gucken ob ich PN't wurde..
|
Minimalistic UI
06/25/2011 - World of Warcraft - 2 Replies
Please close thread posted in invalid forum section.
Edit - You can find the post at http://www.elitepvpers.com/forum/wow-addons/126331 6-minamalistic-ui.html
|
Signal bei PN?
07/30/2010 - Metin2 - 22 Replies
Hayhou :D
Meine Frage ist ganz einfach, wie kann man es so einstellen, das man bei einer PN ein TonSignal bekommt??
Also mit Clientmodding....
Was bringts??
Ganz einfach, ich mekre wnen ich auf meinen Bot chars ne PN bekomme und die Bots fallen nicht auf
MFG
Bauch
|
Kein Signal am Monitor / No signal on screen
09/23/2009 - Technical Support - 2 Replies
Also mein Problem ist das wenn ich den Pc hochfahre keinen Signal an meinem TFT Monitor bekomme. Ich habe erst ein DVI Kabel verwendet, und dachte es würde vl. daran liegen und daher meinen alten Monitor per VGA angeschlossen mit dem gleichen Ergebnis. Mein PC ist einige Monate gestanden, davor hat es eigentlich funktioniert.
Was ich schon probiert habe:
-1 Ram Riegel in anderen Slot gesteckt und hochgefahren
-On Board Graka versucht zu verwenden
-Die eingebaute Graka versucht zu...
|
All times are GMT +1. The time now is 03:57.
|
|