From bce8c0037f2254665bc3109989299f95ceff7484 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Fri, 17 May 2002 15:28:22 +0000 Subject: [PATCH] Added Boost.Signals library [SVN r13964] --- .gitattributes | 96 ++++ build/Jamfile | 24 + doc/design.html | 174 ++++++++ doc/design_rationale.html | 374 ++++++++++++++++ doc/faq.html | 30 ++ doc/history.html | 179 ++++++++ doc/index.html | 40 ++ doc/reference/connection.html | 208 +++++++++ doc/reference/last_value.html | 62 +++ doc/reference/signal.html | 41 ++ doc/reference/signalN.html | 221 ++++++++++ doc/reference/slot.html | 48 ++ doc/reference/trackable.html | 72 +++ doc/reference/visit_each.html | 62 +++ doc/tutorial.html | 22 + example/Jamfile | 45 ++ example/button_click.cpp | 65 +++ example/difference_connection.cpp | 54 +++ example/disconnect_all.cpp | 77 ++++ example/first_positive.cpp | 54 +++ example/maximum.cpp | 44 ++ example/no_function.cpp | 50 +++ example/print_sum_product.cpp | 38 ++ example/quotient_controlling.cpp | 67 +++ example/quotient_named.cpp | 72 +++ include/boost/last_value.hpp | 54 +++ include/boost/signal.hpp | 388 ++++++++++++++++ include/boost/signals/connection.hpp | 291 ++++++++++++ include/boost/signals/detail/gen_signal_N.pl | 138 ++++++ include/boost/signals/detail/signal_base.hpp | 188 ++++++++ .../boost/signals/detail/signals_common.hpp | 155 +++++++ .../signals/detail/slot_call_iterator.hpp | 119 +++++ include/boost/signals/signal0.hpp | 43 ++ include/boost/signals/signal1.hpp | 43 ++ include/boost/signals/signal10.hpp | 43 ++ include/boost/signals/signal2.hpp | 43 ++ include/boost/signals/signal3.hpp | 43 ++ include/boost/signals/signal4.hpp | 43 ++ include/boost/signals/signal5.hpp | 43 ++ include/boost/signals/signal6.hpp | 43 ++ include/boost/signals/signal7.hpp | 43 ++ include/boost/signals/signal8.hpp | 43 ++ include/boost/signals/signal9.hpp | 43 ++ include/boost/signals/signal_template.hpp | 353 +++++++++++++++ include/boost/signals/slot.hpp | 132 ++++++ include/boost/signals/trackable.hpp | 109 +++++ src/connection.cpp | 53 +++ src/signal_base.cpp | 236 ++++++++++ src/slot.cpp | 70 +++ src/trackable.cpp | 63 +++ test/Jamfile | 37 ++ test/dead_slot_test.cpp | 52 +++ test/deletion_test.cpp | 225 ++++++++++ test/ordering_test.cpp | 98 +++++ test/random_signal_system.cpp | 413 ++++++++++++++++++ test/signal_n_test.cpp | 164 +++++++ test/signal_test.cpp | 167 +++++++ test/trackable_test.cpp | 72 +++ 58 files changed, 6269 insertions(+) create mode 100644 .gitattributes create mode 100644 build/Jamfile create mode 100644 doc/design.html create mode 100644 doc/design_rationale.html create mode 100644 doc/faq.html create mode 100644 doc/history.html create mode 100644 doc/index.html create mode 100644 doc/reference/connection.html create mode 100644 doc/reference/last_value.html create mode 100644 doc/reference/signal.html create mode 100644 doc/reference/signalN.html create mode 100644 doc/reference/slot.html create mode 100644 doc/reference/trackable.html create mode 100644 doc/reference/visit_each.html create mode 100644 doc/tutorial.html create mode 100644 example/Jamfile create mode 100644 example/button_click.cpp create mode 100644 example/difference_connection.cpp create mode 100644 example/disconnect_all.cpp create mode 100644 example/first_positive.cpp create mode 100644 example/maximum.cpp create mode 100644 example/no_function.cpp create mode 100644 example/print_sum_product.cpp create mode 100644 example/quotient_controlling.cpp create mode 100644 example/quotient_named.cpp create mode 100644 include/boost/last_value.hpp create mode 100644 include/boost/signal.hpp create mode 100644 include/boost/signals/connection.hpp create mode 100644 include/boost/signals/detail/gen_signal_N.pl create mode 100644 include/boost/signals/detail/signal_base.hpp create mode 100644 include/boost/signals/detail/signals_common.hpp create mode 100644 include/boost/signals/detail/slot_call_iterator.hpp create mode 100644 include/boost/signals/signal0.hpp create mode 100644 include/boost/signals/signal1.hpp create mode 100644 include/boost/signals/signal10.hpp create mode 100644 include/boost/signals/signal2.hpp create mode 100644 include/boost/signals/signal3.hpp create mode 100644 include/boost/signals/signal4.hpp create mode 100644 include/boost/signals/signal5.hpp create mode 100644 include/boost/signals/signal6.hpp create mode 100644 include/boost/signals/signal7.hpp create mode 100644 include/boost/signals/signal8.hpp create mode 100644 include/boost/signals/signal9.hpp create mode 100644 include/boost/signals/signal_template.hpp create mode 100644 include/boost/signals/slot.hpp create mode 100644 include/boost/signals/trackable.hpp create mode 100644 src/connection.cpp create mode 100644 src/signal_base.cpp create mode 100644 src/slot.cpp create mode 100644 src/trackable.cpp create mode 100644 test/Jamfile create mode 100644 test/dead_slot_test.cpp create mode 100644 test/deletion_test.cpp create mode 100644 test/ordering_test.cpp create mode 100644 test/random_signal_system.cpp create mode 100644 test/signal_n_test.cpp create mode 100644 test/signal_test.cpp create mode 100644 test/trackable_test.cpp diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..3e84d7c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,96 @@ +* text=auto !eol svneol=native#text/plain +*.gitattributes text svneol=native#text/plain + +# Scriptish formats +*.bat text svneol=native#text/plain +*.bsh text svneol=native#text/x-beanshell +*.cgi text svneol=native#text/plain +*.cmd text svneol=native#text/plain +*.js text svneol=native#text/javascript +*.php text svneol=native#text/x-php +*.pl text svneol=native#text/x-perl +*.pm text svneol=native#text/x-perl +*.py text svneol=native#text/x-python +*.sh eol=lf svneol=LF#text/x-sh +configure eol=lf svneol=LF#text/x-sh + +# Image formats +*.bmp binary svneol=unset#image/bmp +*.gif binary svneol=unset#image/gif +*.ico binary svneol=unset#image/ico +*.jpeg binary svneol=unset#image/jpeg +*.jpg binary svneol=unset#image/jpeg +*.png binary svneol=unset#image/png +*.tif binary svneol=unset#image/tiff +*.tiff binary svneol=unset#image/tiff +*.svg text svneol=native#image/svg%2Bxml + +# Data formats +*.pdf binary svneol=unset#application/pdf +*.avi binary svneol=unset#video/avi +*.doc binary svneol=unset#application/msword +*.dsp text svneol=crlf#text/plain +*.dsw text svneol=crlf#text/plain +*.eps binary svneol=unset#application/postscript +*.gz binary svneol=unset#application/gzip +*.mov binary svneol=unset#video/quicktime +*.mp3 binary svneol=unset#audio/mpeg +*.ppt binary svneol=unset#application/vnd.ms-powerpoint +*.ps binary svneol=unset#application/postscript +*.psd binary svneol=unset#application/photoshop +*.rdf binary svneol=unset#text/rdf +*.rss text svneol=unset#text/xml +*.rtf binary svneol=unset#text/rtf +*.sln text svneol=native#text/plain +*.swf binary svneol=unset#application/x-shockwave-flash +*.tgz binary svneol=unset#application/gzip +*.vcproj text svneol=native#text/xml +*.vcxproj text svneol=native#text/xml +*.vsprops text svneol=native#text/xml +*.wav binary svneol=unset#audio/wav +*.xls binary svneol=unset#application/vnd.ms-excel +*.zip binary svneol=unset#application/zip + +# Text formats +.htaccess text svneol=native#text/plain +*.bbk text svneol=native#text/xml +*.cmake text svneol=native#text/plain +*.css text svneol=native#text/css +*.dtd text svneol=native#text/xml +*.htm text svneol=native#text/html +*.html text svneol=native#text/html +*.ini text svneol=native#text/plain +*.log text svneol=native#text/plain +*.mak text svneol=native#text/plain +*.qbk text svneol=native#text/plain +*.rst text svneol=native#text/plain +*.sql text svneol=native#text/x-sql +*.txt text svneol=native#text/plain +*.xhtml text svneol=native#text/xhtml%2Bxml +*.xml text svneol=native#text/xml +*.xsd text svneol=native#text/xml +*.xsl text svneol=native#text/xml +*.xslt text svneol=native#text/xml +*.xul text svneol=native#text/xul +*.yml text svneol=native#text/plain +boost-no-inspect text svneol=native#text/plain +CHANGES text svneol=native#text/plain +COPYING text svneol=native#text/plain +INSTALL text svneol=native#text/plain +Jamfile text svneol=native#text/plain +Jamroot text svneol=native#text/plain +Jamfile.v2 text svneol=native#text/plain +Jamrules text svneol=native#text/plain +Makefile* text svneol=native#text/plain +README text svneol=native#text/plain +TODO text svneol=native#text/plain + +# Code formats +*.c text svneol=native#text/plain +*.cpp text svneol=native#text/plain +*.h text svneol=native#text/plain +*.hpp text svneol=native#text/plain +*.ipp text svneol=native#text/plain +*.tpp text svneol=native#text/plain +*.jam text svneol=native#text/plain +*.java text svneol=native#text/plain diff --git a/build/Jamfile b/build/Jamfile new file mode 100644 index 0000000..4f77560 --- /dev/null +++ b/build/Jamfile @@ -0,0 +1,24 @@ +# (C) Copyright Douglas Gregor 2001-2. +# Permission to copy, use, modify, sell and +# distribute this software is granted provided this copyright notice appears +# in all copies. This software is provided "as is" without express or implied +# warranty, and with no claim as to its suitability for any purpose. +# +# Boost.Signals build and test Jamfile + +# declare the location of this subproject relative to the root +subproject libs/signals/build ; + +# Base names of the source files for libboost_signals +CPP_SOURCES = + trackable connection signal_base slot ; + +dll boost_signals : ../src/$(CPP_SOURCES).cpp + : $(BOOST_ROOT) + : debug release + ; + +lib boost_signals : ../src/$(CPP_SOURCES).cpp + : $(BOOST_ROOT) + : debug release + ; diff --git a/doc/design.html b/doc/design.html new file mode 100644 index 0000000..b1b871b --- /dev/null +++ b/doc/design.html @@ -0,0 +1,174 @@ + + + + Boost.Signals Design + + + + +

BoostBoost.Signals Design

+ +

This document describes the high-level design of the Boost.Signals +library. An additional document describes the design rationale. + +

Table of Contents

+ + +

Type Erasure

+

Type erasure is used extensively within the Boost.Signals library +to reduce the amount of code generated by template +instantiation. Each signal must manage a list of slots and their +associated connections, along with a std::map to map from +slot names to their associated connections. However, instantiating +this map for every token type, and perhaps within each translation +unit (for some popular template instantiation strategies) increase +compile time overhead and space overhead. + +

To combat this so-called "template bloat", we use Boost.Function and Boost.Any to store unknown types and +operations. Then, all of the code for handling the list of slots and +the mapping from named slots to connections is factored into the class +signal_base +that deals exclusively with the any and +function objects, hiding the actual implementations using +the well-known pimpl idiom. The actual signalN class +templates deal only with code that will change depending on the number +of arguments or which is inherently template-dependent (such as +connection). + +

connection class

+

The connection class is central to the behavior of +the Boost.Signals library. It is the only entity within the +Boost.Signals system that has knowledge of all objects that are +associated by a given connection. To be specific, the +connection class itself is merely a thin wrapper over a +shared_ptr +to a basic_connection object. + +

connection objects are stored by all participants in +the Signals system: each trackable object +contains a list of connection objects describing all +connections it is a part of; similarly, all signals contain a set of +pairs that define a slot. The pairs consist of a slot function object +(generally a Boost.Function +object) and a connection object (that will disconnect on +destruction). Finally, the mapping from slot groups to slots is based +on the key value in a std::multimap (the stored data in +the std::multimap is the slot pair). + +

Slot Call Iterator

+

The slot call iterator is conceptually a stack of iterator + adaptors that modify the behavior of the underlying iterator + through the list of slots. The following table describes the + type and behavior of each iterator adaptor required. Note that + this is only a conceptual model: the implementation collapses + all these layers into a single iterator adaptor because several + popular compilers failed to compile the implementation of the + conceptual model. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Iterator Adaptor
Purpose
Slot List Iterator
An iterator through the list of slots connected to a signal. The + value_type of this iterator will be + std::pair<any, connection>, where the + any contains an instance of the slot function + type.
Filter Iterator Adaptor
This filtering iterator adaptor filters out slots that have been + disconnected, so we never see a disconnected slot in later + stages.
Projection Iterator Adaptor
The projection iterator adaptor returns a reference to the first + member of the pair that constitutes a connected slot + (e.g., just the boost::any object that holds + the slot function).
Transform Iterator Adaptor
This transform iterator adaptor performs an + any_cast to extract a reference to the slot + function with the appropriate slot function type.
Transform Iterator Adaptor
This transform iterator adaptor calls the function object + returned by dereferencing the underlying iterator with + the set of arguments given to the signal itself, and + returns the result of that slot call.
Input Caching Iterator Adaptor
This iterator adaptor caches the result of dereferencing the + underlying iterator. Therefore, dereferencing this + iterator multiple times will only result in the underlying + iterator being dereferenced once; thus, a slot can only be + called once but its result can be used multiple times.
Slot Call Iterator
+
+ +

visit_each function template

+

The visit_each + function template is a mechanism for discovering objects that + are stored within another object. Function template + visit_each takes three arguments: an object to + explore, a visitor function object that is invoked with each + subobject, and the int 0.

+ +

The third parameter is merely a temporary solution to the + widespread lack of proper function template partial + ordering. The primary visit_each function template + specifies this third parameter type to be long, + whereas any user specializations must specify their third + parameter to be of type int. Thus, even though a + broken compiler cannot tell the ordering between, e.g., a + match against a parameter T and a parameter + A<T>, it can determine that the conversion + from the integer 0 to int is better than the + conversion to long. The ordering determined by this + conversion thus achieves partial ordering of the function + templates in a limited, but successful, way. The following + example illustrates the use of this technique:

+
+template<typename> class A {};
+template<typename T> void foo(T, long);
+template<typename T> void foo(A<T>, int);
+A<T> at;
+foo(at, 0);
+
+ +

In this example, we assume that our compiler can not tell that + A<T> is a better match than T, + and therefore assume that the function templates cannot be + ordered based on that parameter. Then the conversion from 0 to + int is better than the conversion from 0 to + long, and the second function template is chosen.

+
+
Douglas Gregor
+ + +Last modified: Thu Feb 28 00:23:34 EST 2002 + + + \ No newline at end of file diff --git a/doc/design_rationale.html b/doc/design_rationale.html new file mode 100644 index 0000000..cefe980 --- /dev/null +++ b/doc/design_rationale.html @@ -0,0 +1,374 @@ + + + + Boost.Signals Design Rationale + + + + +

BoostBoost.Signals Design Rationale

+ +

This document describes the rationale behind some of the major + design decisions made for the Boost.Signals library. + +

Table of Contents

+ + +

Choice of Slot Definitions

+

The definition of a slot differs amongst signals and slots + libraries. Within Boost.Signals, a slot is defined in a very loose + manner: it can be any function object that is callable given + parameters of the types specified by the signal, and whose return + value is convertible to the result type expected by the + signal. However, alternative definitions have associated pros and + cons that were considered prior to the construction of + Boost.Signals. + +

    +
  • Slots derive from a specific base class: generally a + scheme such as this will require all user-defined slots to derive + from some library-specified Slot abstract class + that defines a virtual function calling the slot. Adaptors can be + used to convert a definition such as this to a definition similar + to that used by Boost.Signals, but in this case the original + specification ties the implementation to the use of virtual + functions internally. This approach does have the benefit of + simplicity of implementation and user interface, from an + object-oriented perspective.
  • + +
  • Slots constructed from a set of primitives: in this + scheme the slot can have a limited set of types (often derived + from a common abstract base class) that are constructed from some + library-defined set of primitives that often include conversions + from free function pointers and member function pointers, and a + limited set of binding capabilities. Such an approach is + reasonably simple and cover most common cases, but it does not + allow a large degree of flexibility in slot + construction. Libraries for function object composition have + become quite advanced and it is out of the scope of a signals and + slots library to encorporate such enhancements. Thus Boost.Signals + does not include argument binding or function object composition + primitives, but instead uses a set of well-defined interfaces to + discover information about the composition of arbitrary function + objects.
  • +
+ +

Users not satisfied with the slot definition choice may opt to + replace the default slot function type with an alternative that + meets their specific needs. + +

User-level Connection Management

+

Users need to have fine control over the connection of signals + to slots and their eventual disconnection. The approach taken by + Boost.Signals is to return a connection object + that enables connected/disconnected query, manual disconnection, and + an automatic disconnection on destruction mode. Some other possible + interfaces include: +

    +
  • Pass slot to disconnect: in this interface model, the + disconnection of a slot connected with + sig.connect(slot) is performed via + sig.disconnect(slot). Internally, a linear search + using slot comparison is performed and the slot, if found, is + removed from the list. Unfortunately, querying connectedness will + generally also end up as linear-time operations. This model also + fails for implementation reasons when slots become more complex + than simple function pointers, member function pointers, and a + limited set of compositions and argument binders it is tough to + rely on comparison of function objects because arbitrary function + objects are not comparable.
  • +
  • Pass a token to disconnect: this approach identifies + slots with a token that is easily comparable (e.g., a string), + enabling slots to be arbitrary function objects. While this + approach is essentially equivalent to the approach taken by + Boost.Signals, it is possibly more error-prone for several + reasons: +
      +
    • Connections and disconnections must be paired, so the + problem becomes similar to the problems incurred when pairing + new and delete for dynamic memory + allocation. While errors of this sort would not be catastrophic + for a signals and slots implementation, their detection is + generally nontrivial.
    • +
    • Tokens must be unique, otherwise two slots will have the + same name and will be indistinguishable. In environments where + many connections will be made dynamically, name generation + becomes an additional task for the user. Uniqueness of tokens + also results in an additional failure mode when attempting to + connect a slot using a token that has already been used.
    • +
    • More parameterization would be required, because the token + type must be user-defined. Additional parameterization steepens + the learning curver and overcomplicates a simple interface.
    • +
    +

    This type of interface is supported in Boost.Signals via the + named connections mechanism. It augments the connection + object-based connection management scheme. +

  • +
+ +

Combiner Interface

+

The Combiner interface was chosen to mimic a call to an + algorithm in the C++ standard library. It is felt that by viewing + slot call results as merely a sequence of values accessed by input + iterators, the combiner interface would be most natural to a + proficient C++ programmer. Competing interface design generally + required the combiners to be constructed to conform to an interface + that would be customized for (and limited to) the Signals + library. While these interfaces are generally enable more + straighforward implementation of the signals & slots libraries, + the combiners are unfortunately not reusable (either in other + signals & slots libraries or within other generic algorithms), + and the learning curve is steepened slightly to learn the specific + combiner interface. + +

The Signals formulation of combiners is based on the combiner + using the "pull" mode of communication, instead of the more complex + "push" mechanism. With a "pull" mechanism, the combiner's state can + be kept on the stack and in the program counter, because whenever + new data is required (i.e., calling the next slot to retrieve its + return value), there is a simple interface to retrieve that data + immediately and without returning from the combiner's code. Contrast + this with the "push" mechanism, where the combiner must keep all + state in class members because the combiner's routines will be + invoked for each signal called. Compare, for example, a combiner + that returns the maximum element from calling the slots. If the + maximum element ever exceeds 100, no more slots are to be + called. + + + + + + + + + + +
PullPush
+
+struct pull_max {
+  typedef int result_type;
+
+  template<typename InputIterator>
+  result_type operator()(InputIterator first,
+                         InputIterator last)
+  {
+    if (first == last)
+      throw std::runtime_error("Empty!");
+
+    int max_value = *first++;
+    while(first != last && *first <= 100) {
+      if (*first > max_value)
+        max_value = *first;
+      ++first;
+    }
+
+    return max_value;
+  }
+};
+  
+
+
+struct push_max {
+  typedef int result_type;
+
+  push_max() : max_value(), got_first(false) {}
+
+  // returns false when we want to stop
+  bool operator()(int result) {
+    if (result > 100)
+      return false;
+
+    if (!got_first) {
+      got_first = true;
+      max_value = result;
+      return true;
+    }
+
+    if (result > max_value)
+      max_value = result;
+
+    return true;
+  }
+
+  int get_value() const 
+  { 
+    if (!got_first)
+      throw std::runtime_error("Empty!");
+    return max_value; 
+  }
+
+private:
+  int  max_value; 
+  bool got_first;
+};
+  
+
+

There are several points to note in these examples. The "pull" + version is a reusable function object that is based on an input + iterator sequence with an integer value_type, and is + very straightforward in design. The "push" model, on the other hand, + relies on an interface specific to the caller and is generally + reusable. It also requires extra state values to determine, for + instance, if any elements have been received. Though code quality + and ease-of-use is generally subjective, the "pull" model is clearly + shorter and more reusable and will often be construed as easier to + write and understand, even outside the context of a signals & + slots library. + +

The cost of the "pull" combiner interface is paid in the + implementation of the Signals library itself. To correctly handle + slot disconnections during calls (e.g., when the dereference + operator is invoked), one must construct the iterator to skip over + disconnected slots. Additionally, the iterator must carry with it + the set of arguments to pass to each slot (although a + reference to a structure containing those arguments suffices), and + must cache the result of calling the slot so that multiple + dereferences don't result in multiple calls. This apparently + requires a large degree of overhead, though if one considers the + entire process of invoking slots one sees that the overhead is + nearly equivalent to that in the "push" model, but we have inverted + the control structures to make iteration and dereference complex + (instead of making combiner state-finding complex). + +

Connection Interfaces: += operator

+

Boost.Signals supports a connection syntax with the form + sig.connect(slot), but a more terse syntax sig += + slot has been suggested (and has been used by other signals + & slots implementations). There are several reasons as to why + this syntax has been rejected: +

    +
  • It's unnecessary: the connection syntax supplied by + Boost.Signals is no less powerful that than supplied by the + signal. The savings in typing (connect() + vs. +=) is essentially negligible. Furthemore, one + could argue that calling connect() is more readable + than an overload of +=. + +
  • Ambiguous return type: + there is an ambiguity concerning the return value of the + += operation: should it be a reference to the signal + itself, to enable sig += slot1 += slot2, or should it + return a connection for the + newly-created signal/slot connection?
  • + +
  • Gateway to operators -=, +: when one has added a + connection operator +=, it seems natural to have a + disconnection operator -=. However, this presents + problems when the library allows arbitrary function objects to + implicitly become slots, because slots are no longer comparable + (see the discussion on this topic in User-level Connection Management). + +

    The second obvious addition when one has + operator+= would be to add a + operator + that supports addition of multiple slots, followed by assignment + to a signal. However, this would require implementing + + such that it can accept any two function objects, + which is technically infeasible. +

+ +

trackable rationale

+

The trackable + class is the primary user interface to automatic connection + lifetime management, and its design affects users directly. Two + issues stick out most: the odd copying behavior of + trackable, and the limitation requiring users to + derive from trackable to create types that can + participate in automatic connection management. + +

trackable copying behavior

+

The copying behavior of trackable is essentially + that trackable subobjects are never copied; + instead, the copy operation is merely a no-op. To understand + this, we look at the nature of a signal-slot connection and note + that the connection is based on the entities that are being + connected; when one of the entities is destroyed, the connection + is destroyed. Therefore, when a trackable subobject + is copied, we cannot copy the connections because the + connections don't refer to the target entity - they refer to the + source entity. This reason is dual to the reason signals are + noncopyable: the slots connected to them are connected to that + particular signal, not the data contained in the signal. + +

Why derivation from trackable?

+

For trackable to work properly, there are two + constraints: +

    +
  • trackable must have storage space to keep + track of all connections made to this object.
  • +
  • trackable must be notified when the object is + being destructed so that it can disconnect its + connections.
  • +
+ + Clearly, deriving from trackable meets these two + guidelines. We have not yet found a superior solution. + +

libsigc++

+

libsigc++ is a C++ signals + & slots library that originally started as part of an initiative + to wrap the C interfaces to GTK + libraries in C++, and has grown to be a separate library maintained + by Karl Nelson. There are many similarities between libsigc++ and + Boost.Signals, and indeed Boost.Signals was strongly influenced by + Karl Nelson and libsigc++. A cursory inspection of each library will + find a similar syntax for the construction of signals and in the use + of connections and automatic connection lifetime management. There + are some major differences in design that separate these libraries: +

    +
  • Slot definitions: slots in libsigc++ are created using + a set of primitives defined by the library. These primitives allow + binding of objects (as part of the library), explicit + adaptation from the argument and return types of the signal to the + argument and return types of the slot (libsigc++ is, by default, + more strict about types than Boost.Signals). A discussion of this + approach with a comparison against the approach taken by + Boost.Signals is given in + Choice of Slot Definitions.
  • + +
  • Combiner/Marshaller interface: the equivalent to + Boost.Signals combiners in libsigc++ are the + marshallers. Marshallers are similar to the "push" interface + described in Combiner Interface, and a + proper treatment of the topic is given there.
  • +
+ +

.NET delegates

+

Microsoft has introduced + the .NET Framework and an associated set of languages and language + extensions, one of which is the delgate. Delegates are similar + to signals and slots, but they are more limited than most C++ + signals and slots implemetations in that they: +

    +
  • Require exact type matches between a delegate and what it is + calling
  • +
  • Do not allow return types
  • +
  • Must call a method with this already bound
  • +
+ +
+
Douglas Gregor
+ + +Last modified: Thu Feb 14 21:07:10 EST 2002 + + + \ No newline at end of file diff --git a/doc/faq.html b/doc/faq.html new file mode 100644 index 0000000..b4abc15 --- /dev/null +++ b/doc/faq.html @@ -0,0 +1,30 @@ + + + + Boost.Signals Frequently Asked Questions + + + +

BoostBoost.Signals Frequently Asked Questions

+ +

Q: Don't noncopyable signal semantics mean that a class with a signal member will be noncopyable as well?

+

No. The compiler will not be able to generate a copy + constructor or copy assignment operator for your class if it has + a signal as a member, but you are free to write your own copy + constructor and/or copy assignment operator. Just don't try to + copy the signal.

+ +

Q: Is Boost.Signals thread-safe?

+

No. Using Boost.Signals in a multithreaded concept is very + dangerous, and it is very likely that the results will be less + than satisfying. Boost.Signals will support thread safety in the + future.

+ +
+
+ + +Last modified: Thu Feb 28 00:05:16 EST 2002 + + + diff --git a/doc/history.html b/doc/history.html new file mode 100644 index 0000000..6fda9f9 --- /dev/null +++ b/doc/history.html @@ -0,0 +1,179 @@ + + + + Boost.Signals Revision History + + + +

BoostBoost.Signals Revision History

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DateChange Summary
2002-04-10 +
    +
  • Added Frequently Asked Questions document.
  • +
  • Added visit_each discussion to the + design document.
  • +
  • Made deletion_test actually perform tests.
  • +
  • Fixed slot_call_iterator behavior when a slot + deletes the next slot after incrementing the + slot call iterator to that slot (i.e., when using the + *first++ syntax) again.
  • +
  • Moved last_value into the top-level Boost + directory and removed dependencies on Signals + library.
  • +
  • Moved implementation detail headers + signal_base.hpp, + signals_common.hpp and + slot_call_iterator.hpp into + boost/signals/detail.
  • +
  • Moved trackable and connection + classes into namespace + boost::signals.
  • +
  • Moved implementation details from namespace + detail::signals to namespace + signals::detail.
  • +
  • Fixed signalN.hpp headers to use types + T1, T2, ..., TN instead of + T0, T1, ..., TN-1. Added + appropriate typedefs argument_type, + first_argument_type and + second_argument_type.
  • +
  • Minor exception safety updates in constructors.
  • +
  • Removed "controlling" connections from the public + interface. Added the scoped_connection + class to pick up the slack, but with more obvious + semantics.
  • +
  • Slot names have become connection groups. Slot call ordering + is now dependent on the connection group ordering, with + ungrouped slots being called last.
  • +
  • Added testcase for slot call ordering.
  • +
+
2002-02-14 +
    +
  • Make sure all shared_ptr copies are performed + only when complete types are available.
  • +
  • Added test for dead slots.
  • +
  • Removed use of scoped_ptr (it made Borland C++ + unhappy).
  • +
  • Jamfile updates for the latest Jam changes in CVS.
  • +
  • Validated HTML.
  • +
+
2002-01-19 +
    +
  • Refactored slot connection into signal_base.
  • +
  • Revisited exception safety in slot connection routines.
  • +
  • Updated slot_call_iterator to properly cache + values even when there are multiple input iterators.
  • +
  • Changed signal_base to use the handle/body idiom + via a shared_ptr. This enables safe recursive + deletion.
  • +
  • Added a comprehensive connection-tracking test system based on + the Boost graph library. See random_signal_system.cpp.
  • +
  • Refactored slot class into a slot_base class; + connection management code moved into slot.cpp.
  • +
+
2001-12-28 +
    +
  • Added slot class template so that slots may be + passed as arguments to non-template functions. (Karl + Nelson)
  • +
  • Updated signalN class templates to use the + new slot class template.
  • +
  • Tutorial updated to contain information about + slot.
  • +
  • visit_each documentation added. (Karl + Nelson)
  • +
  • More Borland C++ fixes.
  • +
+
2001-12-24 +
    +
  • Update to match visit_each framework supported + by Boost.Bind
  • +
  • Signals can be connected to other signals directly.
  • +
  • Signals can be connected to references to slot function + objects (function objects are then not copied).
  • +
  • Added argI_type types to signal + classes, and cleaned up the corresponding + documentation (thanks to Bill Kempf).
  • +
  • Document noncopyable requirement (thanks to Bill + Kempf).
  • +
  • "Bindable" changed to "Trackable" (thanks to Peter Dimov).
  • +
  • Added named slot connections (thanks for Peter Dimov for the suggestion, and Brad King for the push toward a small implementation).
  • +
  • Trackable rationale documentation.
  • +
  • Explicitly instantiate what Signals needs within the non-template source.
  • +
  • Editorial fixes in the tutorial.
  • +
  • Added examples directory.
  • +
  • Added SlotFunction template parameter to specify the type of object to hold the slot function objects (suggested by Peter Dimov).
  • +
  • Documentation updates for signalN classes.
  • +
  • Link to all header files within the documentation for those header files.
  • +
  • Moved all headers except signal.hpp into signals subdirectory.
  • +
  • return_last_value renamed to last_value.
  • +
  • Proper documentation for last_value class.
  • +
+
2001-11-25 +
    +
  • Fixes for MSVC and Borland C++.
  • +
  • Added Documentation: design rationale & comparisons with other signals & slots implementations.
  • +
  • Signal connection now meets the strong exception guarantee.
  • +
  • Jamfile fixes for building the "bindable.cpp" test
  • +
  • Combined transform_iterator, skip_if_iterator, and + input_caching_iterator into a single + slot_call_iterator to reduce template depth and + compiler confusion.
  • +
+
2001-11-18 +
    +
  • Refactoring of signal connection management code.
  • +
  • Exception safety greatly improved (it is now safe for slots to throw +exceptions)
  • +
  • Uses Boost.Build
  • +
  • Documentation (reference and tutorial)
  • +
  • Updated to work with newer versions of Iterator + Adaptors
  • +
+
2001-07-02Initial prototype
+ + +
+
+ + +Last modified: Wed Apr 10 19:47:13 EDT 2002 + + + diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 0000000..6926b59 --- /dev/null +++ b/doc/index.html @@ -0,0 +1,40 @@ + + + + Boost.Signals + + + + +

BoostBoost.Signals

+

The Boost.Signals library is an implementation of a managed signals and slots system. Signals represent callbacks with multiple targets, and are also called publishers or events in similar systems. Signals are connected to some set of slots, which are callback receivers (also called event targets or subscribers), which are called when the signal is "emitted." +

Signals and slots are managed, in that signals and slots (or, more properly, objects that occur as part of the slots) track all connections and are capable of automatically disconnecting signal/slot connections when either is destroyed. This enables the user to make signal/slot connections without expending a great effort to manage the lifetimes of those connections with regard to the lifetimes of all objects involved. +

When signals are connected to multiple slots, there is a question regarding the relationship between the return values of the slots and the return value of the signals. Boost.Signals allows the user to specify the manner in which multiple return values are combined. + +

Table of Contents

+ +
+
Douglas Gregor
+ + +Last modified: Thu Feb 28 00:05:38 EST 2002 + + + \ No newline at end of file diff --git a/doc/reference/connection.html b/doc/reference/connection.html new file mode 100644 index 0000000..e495b07 --- /dev/null +++ b/doc/reference/connection.html @@ -0,0 +1,208 @@ + + + + Boost.Signals: Header <boost/signals/connection.hpp> + + + + +

BoostBoost.Signals: Header <boost/signals/connection.hpp>

+ +

Header <boost/signals/connection.hpp> synopsis

+ +
+namespace boost {
+  namespace signals {
+    class connection;
+    class scoped_connection;
+
+    void swap(connection&, connection&);
+    void swap(scoped_connection&, scoped_connection&);
+  }
+}
+
+ +

Class connection synopsis

+

The connection class represents a connection between a Signal and a Slot. It is a lightweight object that has the ability to query whether the signal and slot are currently connected, and to disconnect the signal and slot. It is always safe to query or disconnect a connection. + +

+namespace boost {
+  namespace signals {
+    class connection : // Class connection is LessThanComparable and EqualityComparable
+      private less_than_comparable1<connection>, // Exposition only.
+      private equality_comparable1<connection>// Exposition only.
+    {
+    public:
+      connection();
+      connection(const connection&);
+      ~connection();
+  
+      void disconnect() const;
+      bool connected() const;
+
+      connection& operator=(const connection&);
+      void swap(connection&);
+
+      bool operator==(const connection& other) const;
+      bool operator<(const connection& other) const;
+    };
+  }
+}
+
+ +

Class connection members

+ +

Constructors

+

connection(); +

    +
  • Effects: Sets the currently represented connection to the NULL connection.
  • +
  • Postconditions: !this->connected() +
  • Throws: will not throw.
  • +
+ +

connection(const connection& other); +

    +
  • Effects: this references the connection referenced by other.
  • +
  • Throws: will not throw.
  • +
+ +

Destructor

+ +

~connection(); +

    +
  • Effects: none.
  • +
+ +

Connection Management

+

void disconnect() const; +

    +
  • Effects: if this->is_connected(), disconnects the signal and slot referenced by this; otherwise, this operation is a no-op.
  • +
  • Postconditions: !this->is_connected()
  • +
+ +

bool connected() const; +

    +
  • Returns: true if this references a non-NULL connection that is still active (connected), and false otherwise.
  • +
  • Throws: will not throw.
  • +
+ +

Assignment and Swap

+

connection& operator=(const connection& other); +

    +
  • Effects: connection(other).swap(*this); +
  • Returns: *this
  • +
+ +

void swap(connection& other); +

    +
  • Effects: swaps the connections referenced in this and other.
  • +
  • Throws: will not throw.
  • +
+ +

Comparison

+

bool operator==(const connection& other) const; +

    +
  • Returns: true if this and other reference the same connection or both reference the NULL connection, and false otherwise.
  • +
  • Throws: will not throw.
  • +
+ +

bool operator<(const connection& other) const; +

    +
  • Returns: true if the connection referenced by this precedes the connection referenced by other based on some implementation-defined ordering, and false otherwise.
  • +
  • Throws: will not throw.
  • +
+ +

Class scoped_connection synopsis

+ +

The scoped_connection class is a connection that will +be automatically disconnected when the scoped_connection +instance is destructed. + +

+namespace boost {
+  namespace signals {
+    class scoped_connection : public connection
+    {
+    public:
+      scoped_connection();
+      scoped_connection(const scoped_connection&);
+      scoped_connection(const connection&);
+      ~scoped_connection();
+
+      connection& operator=(const scoped_connection&);
+      connection& operator=(const connection&);
+      void swap(connection&);
+    };
+  }
+}
+
+ +

Class scoped_connection members

+ +

Constructors

+

scoped_connection(); +

    +
  • Effects: Sets the currently represented connection to the NULL connection.
  • +
  • Postconditions: !this->connected() +
  • Throws: will not throw.
  • +
+ +

scoped_connection(const scoped_connection& other); +

    +
  • Effects: this references the connection referenced by other.
  • +
  • Throws: will not throw.
  • +
+ +

scoped_connection(const connection& other); +

    +
  • Effects: this references the connection referenced by other.
  • +
  • Throws: will not throw.
  • +
+ +

Destructor

+ +

~connection(); +

    +
  • Effects: this->disconnect()
  • +
+ +

Assignment and Swap

+

scoped_connection& operator=(const scoped_connection& other); +

    +
  • Effects: scoped_connection(other).swap(*this); +
  • Returns: *this
  • +
+ +

scoped_connection& operator=(const connection& other); +

    +
  • Effects: scoped_connection(other).swap(*this); +
  • Returns: *this
  • +
+ +

void swap(scoped_connection& other); +

    +
  • Effects: swaps the connections referenced in this and other.
  • +
  • Throws: will not throw.
  • +
+ +

Free Functions

+

void swap(connection& c1, connection& c2); +

    +
  • Effects: c1.swap(c2).
  • +
  • Throws: will not throw.
  • +
+ +

void swap(scoped_connection& c1, scoped_connection& c2); +

    +
  • Effects: c1.swap(c2).
  • +
  • Throws: will not throw.
  • +
+ +
+
Douglas Gregor
+ + +Last modified: Wed Mar 6 19:04:00 EST 2002 + + + \ No newline at end of file diff --git a/doc/reference/last_value.html b/doc/reference/last_value.html new file mode 100644 index 0000000..c13bd80 --- /dev/null +++ b/doc/reference/last_value.html @@ -0,0 +1,62 @@ + + + + Boost.Signals: class templates signalN + + + + +

BoostBoost.Signals: class template last_value

+ +

Header

+
+#include <boost/signals/last_value.hpp>
+
+ +

Synopsis

+
+namespace boost {
+  template<typename T>
+  class last_value {
+  public:
+    typedef T result_type;
+
+    template<typename InputIterator>
+    result_type operator()(InputIterator, InputIterator) const;
+  };
+
+  template<>
+  class last_value<void> {
+  public:
+    typedef implementation-defined result_type; // cannot be void
+
+    template<typename InputIterator>
+    result_type operator()(InputIterator, InputIterator) const;
+  };
+}
+
+ +

Members

+

template<typename InputIterator> + result_type operator()(InputIterator first, InputIterator last) const; +

    +
  • Precondition: first != last.
  • +
  • Effects: dereferences every iterator in the sequence [first, last).
  • +
  • Returns: the result of dereferencing the last iterator dereferenced.
  • +
+ +

template<typename InputIterator> + result_type operator()(InputIterator first, InputIterator last) const; +

    +
  • Effects: dereferences every iterator in the sequence [first, last).
  • +
  • Returns: a value that cannot be meaningfully used by the user, but not void.
  • +
  • Rationale: a function that returns void often requires many workarounds within systems that compose function objects, and therefore we avoid returning void and instead opt to return an implementation-defined, useless value that still leaves the function object easily adaptable.
  • +
+ +
Douglas Gregor
+ + +Last modified: Thu Feb 14 21:14:07 EST 2002 + + + \ No newline at end of file diff --git a/doc/reference/signal.html b/doc/reference/signal.html new file mode 100644 index 0000000..ba32236 --- /dev/null +++ b/doc/reference/signal.html @@ -0,0 +1,41 @@ + + + + Boost.Signals: header <boost/signal.hpp> + + + + +

BoostBoost.Signals: header <boost/signal.hpp>

+ +

Header

+
+#include <boost/signal.hpp>
+
+

Synopsis

+

The specification form for the signal class template uses two positive integers, BOOST_SIGNALS_MAX_ARGS and N. The former describes the maximum number of function parameters that may be passed onto the underlying slots, whereas the latter describes the number of function parameters for a given instantiation of signal. BOOST_SIGNALS_MAX_ARGS is present in the header as a preprocessor macro defining the maximum number of arguments supported by the implementation. +

+namespace boost {
+  template<typename R,
+           typename T1 = implementation-defined,
+           typename T2 = implementation-defined,
+           ...
+           typename TBOOST_SIGNALS_MAX_ARGS = implementation-defined >
+  class signal : public signalN<R, T1, T2, ..., TN>
+  {
+    template<typename Combiner>
+    struct combiner {
+      typedef signalN<R, T1, T2, ..., TN, Combiner> type;
+    };
+  };
+}
+
+ +
+
Douglas Gregor
+ + +Last modified: Thu Feb 14 21:14:32 EST 2002 + + + diff --git a/doc/reference/signalN.html b/doc/reference/signalN.html new file mode 100644 index 0000000..2fcfa8a --- /dev/null +++ b/doc/reference/signalN.html @@ -0,0 +1,221 @@ + + + + Boost.Signals: class templates signalN + + + + +

BoostBoost.Signals: class templates signalN

+ +

Headers

+

N is the number of signal parameters supported. So the header <boost/signals/signal0.hpp> contains signal0, header <boost/signals/signal1.hpp> contains signal1, etc. The maximum number of signal parameters supported is implementation-defined, but must be at least 10. + +

+#include <boost/signals/signalN.hpp>
+
+ +

Synopsis

+

This document covers several related classes signal0, + signal1, signal2, + etc., where the number suffix describes the number of function + parameters the signal and its connected slots will take. Instead + of enumerating all classes, a single pattern + signalN will be described, where + N represents the number of function parameters. + +

+namespace boost {
+  template<typename R,
+           typename T1,
+           typename T2,
+           ...
+           typename TN,
+           typename Combiner = last_value<R>,
+           typename Group = int,
+           typename GroupCompare = std::less<Group>,
+           typename SlotFunction = boost::functionN<R, T1, T2, ..., TN> >
+  class signalN :
+    boost::noncopyable // exposition only: class meets Noncopyable requirement,
+    boost::trackable
+  {
+  public:
+    typedef typename Combiner::result_type result_type;
+    typedef Combiner combiner_type;
+    typedef Group group_type;
+    typedef GroupCompare group_compare_type;
+    typedef SlotFunction slot_function_type;
+    typedef slot<slot_function_type> slot_type;
+    typedef implementation-defined slot_result_type; // if SlotFunction has a void return type, may not be void; otherwise it is the SlotFunction return type
+    typedef implementation-defined slot_call_iterator; // InputIterator with value_type R.
+    typedef T1 argument_type; // If N == 1 then signal models AdaptableUnaryFunction
+    typedef T1 first_argument_type; // If N==2 then signal models AdaptableBinaryFunction
+    typedef T2 second_argument_type; // If N==2 then signal models AdaptableBinaryFunction
+    
+    typedef T1 arg1_type;
+    typedef T2 arg2_type;
+             .
+             .
+             .
+    typedef TN argN_type;
+
+    explicit signalN(const combiner_type& = combiner_type(), const group_compare_type& = group_compare_type());
+    ~signal();
+    signals::connection connect(const slot_type&);
+    signals::connection connect(const group_type&, const slot_type& slot);
+    void disconnect(const group_type&);
+    void disconnect_all_slots();
+    bool empty() const;
+    result_type operator()(T1 a1, T2 a2, ..., TN aN);
+    result_type operator()(T1 a1, T2 a2, ..., TN aN) const;
+
+  private:
+    combiner_type combiner; // exposition only
+  };
+}
+
+ +

Associated Types

+

Combiner

+

A Combiner is a function object that accepts an iterator sequence [first, last) and dereferences some number of the iterators within the sequence, then returns a value. The type of the iterators passed to the combiner will be the slot call iterator type. + +

Group

+

The slot group defines the type to be used to group connections. It must be DefaultConstructible and CopyConstructible. + +

Group comparison

+

The group comparison is a BinaryPredicate where the argument types coincide with the group type. It defines an ordering relation on the connection groups. + +

Slot Function

+

The slot function type must be a function object adaptor capable of being constructed with another compatible function object (where "compatible" is defined by the slot function type itself). The slot function type must accept parameters of types T1, T2, ..., TN and must return a result that is convertible to the template type parameter R of the signal; note that when R is void, any slot function return type will be ignored. + +

For connections to other signals and to function object +references, the slot function type must be able to accept reference_wrapper +objects. + +


Members

+

Slot Result Type

+

type slot_result_type: when the SlotFunction returns void, the slot result type may be an implementation-defined type; otherwise, the slot result type must be the type retuned by SlotFunction function objects. + +

Slot Call Iterator

+

type slot_call_iterator: an InputIterator whose value_type is R. The dereference operator of the slot_call_iterator is responsible for invoking the underlying slot given a specific set of arguments, and returning its result. The result must be cached to ensure that multiple dereferences of an iterator do not invoke the slot multiple times. + +


Constructor

+

explicit signalN(const combiner_type& = combiner_type(), const group_compare_type& = group_compare_type()); +

    +
  • Effects: initializes the signal to contain no slots, copies the given combiner into internal storage, and stores the given group comparison function object to compare groups.
  • +
  • Postconditions: this->empty();
  • +
+ +

Destructor

+

~signal(); +

    +
  • Effects: disconnects all slot connections.
  • +
+ +

Connection Management

+

signals::connection connect(const slot_type& slot); +

    +
  • Effects: connects the signal this to the + incoming slot. If the slot is inactive, + i.e., any of the + trackable objects + bound by the slot call have been destroyed, then the call to + connect is a no-op.
  • +
  • Returns: returns a + signals::connection + object that + references the newly-created connection between the signal and + the slot; if the slot is inactive, returns a + disconnected connection.
  • +
  • Throws: strong exception guarantee, where any exception thrown will cause the slot to not be connected to the signal.
  • +
  • Complexity: O(lg n) where n is the + number of slots known to the signal.
  • +
  • Notes: it is unspecified whether connecting a slot while + the signal is calling will result in the slot being called + immediately.
  • +
+ +

signals::connection connect(const group_type& group, const slot_type& slot); +

    +
  • Effects: connects the given slot to the signal (i.e., connect(slot)), and associates this slot connection with the given group group. +
  • Returns: returns a signals::connection object + that references the newly-created connection between the signal and + the slot.
  • +
  • Throws: strong exception guarantee, where any exception + thrown will cause the slot to not be connected to the signal.
  • +
  • Complexity: O(lg n) where n is the + number of slots known to the signal.
  • +
  • Notes: it is unspecified whether connecting a slot while + the signal is calling will result in the slot being called + immediately.
  • +
+ +

void disconnect(const group_type& group); +

    +
  • Effects: any slots in the given group are disconnected.
  • +
  • Throws: will not throw unless a user destructor + throws. If a user destructor throws, not all slots in this group may + be disconnected.
  • +
  • Complexity: O(lg n) + k where n is the + number of slots known to the signal and k is the number of + .
  • +
+ +

void disconnect_all_slots(); +

    +
  • Effects: disconnects all slots connected to the signal.
  • +
  • Postconditions: this->empty().
  • +
  • Throws: if disconnecting a slot causes an exception to be + thrown, not all slots may be disconnected.
  • +
  • Complexity: linear in the number of slots known to the signal.
  • +
  • Notes: may be called at any time within the lifetime of + the signal, including during calls to the signal's slots.
  • +
+ +

bool empty() const; +

    +
  • Returns: returns true if no slots are connected to the signal, and false otherwise.
  • +
  • Throws: will not throw.
  • +
  • Complexity: linear in the number of slots known to the signal.
  • +
  • Rationale: slots can disconnect at any point in time, including while those same slots are being invoked. It is therefore possible that the implementation must search through a list of disconnected slots to determine if any slots are still connected.
  • +
+ +

Signal Invocation

+

+

+result_type operator()(T1 a1, T2 a2, ..., TN aN);
+result_type operator()(T1 a1, T2 a2, ..., TN aN) const;
+
+
    +
  • Effects: invokes the combiner with a + slot_call_iterator range [first, last) + (i.e., combiner(first, last)) that iterates over the + results of calling each slot with the given set of parameters + a1, a2, ..., aN. Slots are called according to + the partial ordering given by the group comparison function object, + with ungrouped slots called last.
  • +
  • Returns: the result returned by the combiner.
  • +
  • Throws: if an exception is thrown by a slot call, or if the combiner does not dereference any slot past some given slot, all slots after that slot in the internal list of connected slots will not be invoked.
  • +
  • Notes: the const version of the + function call operator will invoke the combiner as + const, whereas the non-const + version will invoke the combiner as non-const. +

    Ordering between members of a given group or between ungrouped + slots is unspecified. +

    Calling the function call operator may invoke undefined behavior if no + slots are connected to the signal, depending on the combiner used. The + default combiner is well-defined for zero slots when the return type is + void but is undefined when the return type is any + other type (because there is no way to synthesize a return value).

  • + +
+
+
Douglas Gregor
+ + +Last modified: Fri May 17 10:18:50 EDT 2002 + + + diff --git a/doc/reference/slot.html b/doc/reference/slot.html new file mode 100644 index 0000000..bea2584 --- /dev/null +++ b/doc/reference/slot.html @@ -0,0 +1,48 @@ + + + + Boost.Signals: class template slot + + + +

BoostBoost.Signals: class template slot

+ +

Header

+
+#include <boost/signals/slot.hpp>
+
+ +

Synopsis

+

The slot class template allows slots to be created + and passed as arguments to non-template functions. It is CopyConstructible, but not DefaultConstructible or Assignable. +

+namespace boost {
+  template<typename SlotFunction>
+  class slot {
+  public:
+    template<typename Slot> slot(const Slot&);
+
+  private:
+    SlotFunction stored_slot_function; // exposition only
+  };
+}
+
+ +

Constructor

+

template<typename Slot> slot(const Slot& slot); +

    +
  • Effects: initializes this to contain the + incoming slot, which may be any function object + that SlotFunction can be constructed with. +
+ +
+
+ + +Last modified: Thu Feb 14 21:18:12 EST 2002 + + + diff --git a/doc/reference/trackable.html b/doc/reference/trackable.html new file mode 100644 index 0000000..bd8ad2c --- /dev/null +++ b/doc/reference/trackable.html @@ -0,0 +1,72 @@ + + + + Boost.Signals: Class trackable + + + + +

BoostBoost.Signals: Class trackable

+ +

Header

+
+#include <boost/signals/trackable.hpp>
+
+ +

Synopsis

+

The trackable class is responsible for managing the automatic disconnection of signals and slots when an object that is bound as part of a slot is destroyed. The trackable class may only be used as a base class for some other class; when used as such, that class may be bound to function objects used as part of slots. The manner in which a the trackable object tracks the set of signal-slot connections it is a part of is implementation-defined. + +

+namespace boost {
+  namespace signals {
+    class trackable {
+    protected:
+      trackable();
+      trackable(const trackable&);
+      ~trackable();
+
+      trackable& operator=(const trackable&);
+    };
+  }
+}
+
+ +

Members

+ +

Constructors

+

trackable(); +

    +
  • Effects: sets the list of connected slots to empty.
  • +
  • Throws: will not throw.
  • +
+ +

trackable(const trackable&); +

    +
  • Effects: sets the list of connected slots to empty.
  • +
  • Throws: will not throw.
  • +
  • Rational: signal-slot connections can only be created via calls to an explicit connect method, and therefore cannot be created here when trackable objects are copied.
  • +
+ +

Destructor

+ +

~trackable(); +

    +
  • Effects: disconnects all connected slots.
  • +
+ +

Assignment

+

trackable& operator=(const trackable& other); +

    +
  • Effects: disconnects all connected slots.
  • +
  • Returns: *this
  • +
  • Rational: signal-slot connections can only be created via calls to an explicit connect method, and therefore cannot be created here when trackable objects are copied.
  • +
+ +
+
Douglas Gregor
+ + +Last modified: Wed Mar 6 19:03:23 EST 2002 + + + \ No newline at end of file diff --git a/doc/reference/visit_each.html b/doc/reference/visit_each.html new file mode 100644 index 0000000..be2b59f --- /dev/null +++ b/doc/reference/visit_each.html @@ -0,0 +1,62 @@ + + + + function template visit_each + + + +

Boostfunction template visit_each

+ +

Header

+
+#include <boost/visit_each.hpp>
+
+ +

Synopsis

+

The visit_each mechanism allows a visitor to be + applied to every subobject in a given object. It is used by the + Signals library to discover trackable objects within a function + object, but other uses may surface if it used universally (e.g., + conservative garbage collection). To fit within the + visit_each framework, a visit_each + overload must be supplied for each object type. + +

+namespace boost {
+  template<typename Visitor, typename T>
+  void visit_each(Visitor&, const T&, int);
+}
+
+ +

Functions

+

template<typename Visitor, typename T> + void visit_each(Visitor& v, const T& t, int); +

    +
  • Effects: v(t), and for every subobject + x of t, +
      +
    • If x is a reference, executes + v(boost::ref(x)).
    • +
    • If x is not a reference, executes + v(x).
    • +
    + +
  • Returns: nothing.
  • +
  • Notes: the third parameter is long for the + fallback version of visit_each defined in + <boost/visit_each.hpp>, and the argument supplied to this third paramter must always be 0. The third parameter is an artifact of the widespread lack of proper function template ordering, and will be removed in the future. + +

    Library authors will be expected to add additional overloads + that specialize the T argument for their classes, + so that subobjects can be visited.

  • +
+ +
+
+ + +Last modified: Thu Feb 14 21:19:03 EST 2002 + + + diff --git a/doc/tutorial.html b/doc/tutorial.html new file mode 100644 index 0000000..6f95364 --- /dev/null +++ b/doc/tutorial.html @@ -0,0 +1,22 @@ + + + + Boost.Signals Tutorial + + + + +

BoostBoost.Signals Tutorial

+ +

Outline

+
    +
  • +
+
+
+ + +Last modified: Sat Apr 13 14:13:52 EDT 2002 + + + diff --git a/example/Jamfile b/example/Jamfile new file mode 100644 index 0000000..d6b9642 --- /dev/null +++ b/example/Jamfile @@ -0,0 +1,45 @@ +subproject libs/signals/example ; + +exe button_click : ../build/boost_signals + button_click.cpp + : $(BOOST_ROOT) + ; + +exe difference_connection : ../build/boost_signals + difference_connection.cpp + : $(BOOST_ROOT) + ; + +exe disconnect_all : ../build/boost_signals + disconnect_all.cpp + : $(BOOST_ROOT) + ; + +exe first_positive : ../build/boost_signals + first_positive.cpp + : $(BOOST_ROOT) + ; + +exe maximum : ../build/boost_signals + maximum.cpp + : $(BOOST_ROOT) + ; +exe print_sum_product : ../build/boost_signals + print_sum_product.cpp + : $(BOOST_ROOT) + ; + +exe quotient_controlling : ../build/boost_signals + quotient_controlling.cpp + : $(BOOST_ROOT) + ; + +exe quotient_named : ../build/boost_signals + quotient_named.cpp + : $(BOOST_ROOT) + ; + +exe no_function : ../build/boost_signals + no_function.cpp + : $(BOOST_ROOT) + ; diff --git a/example/button_click.cpp b/example/button_click.cpp new file mode 100644 index 0000000..c7485dd --- /dev/null +++ b/example/button_click.cpp @@ -0,0 +1,65 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#include +#include +#include + +struct print_string : public boost::signals::trackable { + typedef void result_type; + + void print(const std::string& s) const { std::cout << s << std::endl; } +}; + +struct my_button { + typedef boost::signal1 click_signal_type; + typedef click_signal_type::slot_type click_slot_type; + + boost::signals::connection on_click_connect(const click_slot_type& s) + { return on_click.connect(s); } + + my_button(const std::string& l) : label(l) {} + + virtual ~my_button() {} + + void click(); + +protected: + virtual void clicked() { on_click(label); } + +private: + std::string label; + click_signal_type on_click; +}; + +void my_button::click() +{ + clicked(); +} + +int main() +{ + my_button* b = new my_button("OK!"); + print_string* ps = new print_string(); + b->on_click_connect(boost::bind(&print_string::print, ps, _1)); + + b->click(); // prints OK! + + delete ps; + + b->click(); // prints nothing + + return 0; +} diff --git a/example/difference_connection.cpp b/example/difference_connection.cpp new file mode 100644 index 0000000..c02ba2d --- /dev/null +++ b/example/difference_connection.cpp @@ -0,0 +1,54 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#include +#include +#include + +struct print_sum { + void operator()(int x, int y) const { std::cout << x+y << std::endl; } +}; + +struct print_product { + void operator()(int x, int y) const { std::cout << x*y << std::endl; } +}; + +struct print_difference { + void operator()(int x, int y) const { std::cout << x-y << std::endl; } +}; + +int main() +{ + boost::signal2 sig; + + sig.connect(print_sum()); + sig.connect(print_product()); + + sig(3, 5); + + boost::signals::connection print_diff_con = sig.connect(print_difference()); + + // sig is still connected to print_diff_con + assert(print_diff_con.connected()); + + sig(5, 3); // prints 8, 15, and 2 + + print_diff_con.disconnect(); // disconnect the print_difference slot + + sig(5, 3); // now prints 8 and 15, but not the difference + + assert(!print_diff_con.connected()); // not connected any more + return 0; +} diff --git a/example/disconnect_all.cpp b/example/disconnect_all.cpp new file mode 100644 index 0000000..3f64057 --- /dev/null +++ b/example/disconnect_all.cpp @@ -0,0 +1,77 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#include +#include +#include + +struct print_sum { + void operator()(int x, int y) const { std::cout << x+y << std::endl; } +}; + +struct print_product { + void operator()(int x, int y) const { std::cout << x*y << std::endl; } +}; + +struct print_difference { + void operator()(int x, int y) const { std::cout << x-y << std::endl; } +}; + +struct print_quotient { + void operator()(int x, int y) const { std::cout << x/-y << std::endl; } +}; + + +int main() +{ + boost::signal2, std::string> sig; + + sig.connect(print_sum()); + sig.connect(print_product()); + + sig(3, 5); + + boost::signals::connection print_diff_con = sig.connect(print_difference()); + + // sig is still connected to print_diff_con + assert(print_diff_con.connected()); + + sig(5, 3); // prints 8, 15, and 2 + + print_diff_con.disconnect(); // disconnect the print_difference slot + + sig(5, 3); // now prints 8 and 15, but not the difference + + assert(!print_diff_con.connected()); // not connected any more + + { + boost::signals::scoped_connection c = sig.connect(print_quotient()); + sig(5, 3); // prints 8, 15, and 1 + } // c falls out of scope, so sig and print_quotient are disconnected + + sig(5, 3); // prints 8 and 15 + + sig.connect("quotient", print_quotient()); + sig(5, 3); // prints 8, 15, and 1 + sig.disconnect("quotient"); + sig(5, 3); // prints 8 and 15 + + std::cout << "Disconnected all slots" << std::endl; + sig.disconnect_all_slots(); + assert(sig.empty()); + sig(5, 3); // prints nothing - no slots are connected + + return 0; +} diff --git a/example/first_positive.cpp b/example/first_positive.cpp new file mode 100644 index 0000000..bb033a2 --- /dev/null +++ b/example/first_positive.cpp @@ -0,0 +1,54 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#include +#include + +template +struct first_positive { + typedef T result_type; + + template + T operator()(InputIterator first, InputIterator last) const + { + while (first != last && !(*first > 0)) { ++first; } + return (first == last) ? 0 + : *first; + } +}; + +template +struct noisy_divide { + typedef T result_type; + + T operator()(const T& x, const T& y) const + { + std::cout << "Dividing " << x << " and " << y << std::endl; + return x/y; + } +}; + +int main() +{ + boost::signal2 > sig_positive; + sig_positive.connect(std::plus()); + sig_positive.connect(std::multiplies()); + sig_positive.connect(std::minus()); + sig_positive.connect(noisy_divide()); + + assert(sig_positive(3, -5) == 8); // returns 8, but prints nothing + + return 0; +} diff --git a/example/maximum.cpp b/example/maximum.cpp new file mode 100644 index 0000000..2317ae1 --- /dev/null +++ b/example/maximum.cpp @@ -0,0 +1,44 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#include +#include +#include + +template +struct maximum { + typedef T result_type; + + template + T operator()(InputIterator first, InputIterator last) const + { + if (first == last) + throw std::runtime_error("Cannot compute maximum of zero elements!"); + return *std::max_element(first, last); + } +}; + +int main() +{ + boost::signal2 > sig_max; + sig_max.connect(std::plus()); + sig_max.connect(std::multiplies()); + sig_max.connect(std::minus()); + sig_max.connect(std::divides()); + + std::cout << sig_max(5, 3) << std::endl; // prints 15 + + return 0; +} diff --git a/example/no_function.cpp b/example/no_function.cpp new file mode 100644 index 0000000..6721388 --- /dev/null +++ b/example/no_function.cpp @@ -0,0 +1,50 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#include +#include +#include +#include + +void print_sum(int x, int y) +{ + std::cout << "Sum = " << x+y << std::endl; +} + +void print_product(int x, int y) +{ + std::cout << "Product = " << x*y << std::endl; +} + +void print_quotient(float x, float y) +{ + std::cout << "Quotient = " << x/y << std::endl; +} + +int main() +{ + typedef boost::signal2, + std::string, std::less, + void (*)(int, int)> sig_type; + + sig_type sig; + sig.connect(&print_sum); + sig.connect(&print_product); + + sig(3, 5); // print sum and product of 3 and 5 + + // should fail + // sig.connect(&print_quotient); +} diff --git a/example/print_sum_product.cpp b/example/print_sum_product.cpp new file mode 100644 index 0000000..13298c6 --- /dev/null +++ b/example/print_sum_product.cpp @@ -0,0 +1,38 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#include +#include + +struct print_sum { + void operator()(int x, int y) const { std::cout << x+y << std::endl; } +}; + +struct print_product { + void operator()(int x, int y) const { std::cout << x*y << std::endl; } +}; + + +int main() +{ + boost::signal2 sig; + + sig.connect(print_sum()); + sig.connect(print_product()); + + sig(3, 5); // prints 8 and 15 + + return 0; +} diff --git a/example/quotient_controlling.cpp b/example/quotient_controlling.cpp new file mode 100644 index 0000000..8de3834 --- /dev/null +++ b/example/quotient_controlling.cpp @@ -0,0 +1,67 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#include +#include +#include + +struct print_sum { + void operator()(int x, int y) const { std::cout << x+y << std::endl; } +}; + +struct print_product { + void operator()(int x, int y) const { std::cout << x*y << std::endl; } +}; + +struct print_difference { + void operator()(int x, int y) const { std::cout << x-y << std::endl; } +}; + +struct print_quotient { + void operator()(int x, int y) const { std::cout << x/-y << std::endl; } +}; + + +int main() +{ + boost::signal2 sig; + + sig.connect(print_sum()); + sig.connect(print_product()); + + sig(3, 5); + + boost::signals::connection print_diff_con = sig.connect(print_difference()); + + // sig is still connected to print_diff_con + assert(print_diff_con.connected()); + + sig(5, 3); // prints 8, 15, and 2 + + print_diff_con.disconnect(); // disconnect the print_difference slot + + sig(5, 3); // now prints 8 and 15, but not the difference + + assert(!print_diff_con.connected()); // not connected any more + + { + boost::signals::scoped_connection c = sig.connect(print_quotient()); + sig(5, 3); // prints 8, 15, and 1 + } // c falls out of scope, so sig and print_quotient are disconnected + + sig(5, 3); // prints 8 and 15 + + return 0; +} diff --git a/example/quotient_named.cpp b/example/quotient_named.cpp new file mode 100644 index 0000000..efda572 --- /dev/null +++ b/example/quotient_named.cpp @@ -0,0 +1,72 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#include +#include +#include + +struct print_sum { + void operator()(int x, int y) const { std::cout << x+y << std::endl; } +}; + +struct print_product { + void operator()(int x, int y) const { std::cout << x*y << std::endl; } +}; + +struct print_difference { + void operator()(int x, int y) const { std::cout << x-y << std::endl; } +}; + +struct print_quotient { + void operator()(int x, int y) const { std::cout << x/-y << std::endl; } +}; + + +int main() +{ + boost::signal2, std::string> sig; + + sig.connect(print_sum()); + sig.connect(print_product()); + + sig(3, 5); + + boost::signals::connection print_diff_con = sig.connect(print_difference()); + + // sig is still connected to print_diff_con + assert(print_diff_con.connected()); + + sig(5, 3); // prints 8, 15, and 2 + + print_diff_con.disconnect(); // disconnect the print_difference slot + + sig(5, 3); // now prints 8 and 15, but not the difference + + assert(!print_diff_con.connected()); // not connected any more + + { + boost::signals::scoped_connection c = sig.connect(print_quotient()); + sig(5, 3); // prints 8, 15, and 1 + } // c falls out of scope, so sig and print_quotient are disconnected + + sig(5, 3); // prints 8 and 15 + + sig.connect("quotient", print_quotient()); + sig(5, 3); // prints 8, 15, and 1 + sig.disconnect("quotient"); + sig(5, 3); // prints 8 and 15 + + return 0; +} diff --git a/include/boost/last_value.hpp b/include/boost/last_value.hpp new file mode 100644 index 0000000..a992065 --- /dev/null +++ b/include/boost/last_value.hpp @@ -0,0 +1,54 @@ +// last_value function object (documented as part of Boost.Signals) +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#ifndef BOOST_LAST_VALUE_HPP +#define BOOST_LAST_VALUE_HPP + +#include + +namespace boost { + template + struct last_value { + typedef T result_type; + + template + T operator()(InputIterator first, InputIterator last) const + { + assert(first != last); + T value = *first++; + while (first != last) + value = *first++; + return value; + } + }; + + template<> + class last_value { + struct unusable {}; + + public: + typedef unusable result_type; + + template + result_type + operator()(InputIterator first, InputIterator last) const + { + while (first != last) + *first++; + return result_type(); + } + }; +} +#endif // BOOST_SIGNALS_LAST_VALUE_HPP diff --git a/include/boost/signal.hpp b/include/boost/signal.hpp new file mode 100644 index 0000000..a740009 --- /dev/null +++ b/include/boost/signal.hpp @@ -0,0 +1,388 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#ifndef BOOST_SIGNAL_HPP +#define BOOST_SIGNAL_HPP + +#define BOOST_SIGNALS_MAX_ARGS 10 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { + namespace signals { + namespace detail { + // The unusable class is a placeholder for unused function arguments. + struct unused {}; + + // value=1 if the given type is not "unused" + template + struct count_if_used + { + BOOST_STATIC_CONSTANT(int, value = 1); + }; + + // value=0 for unused types + template<> + struct count_if_used + { + BOOST_STATIC_CONSTANT(int, value = 0); + }; + + // Count the number of arguments (from the given set) which are not + // "unused" (therefore, count those arguments that are used). + template + struct count_used_args + { + BOOST_STATIC_CONSTANT(int, value = + (count_if_used::value + + count_if_used::value + + count_if_used::value + + count_if_used::value + + count_if_used::value + + count_if_used::value + + count_if_used::value + + count_if_used::value + + count_if_used::value + + count_if_used::value)); + }; + + // Choose the appropriate underlying implementation + template struct real_get_signal_impl {}; + + template<> + struct real_get_signal_impl<0> + { + template< + typename R, + typename T1, + typename T2, + typename T3, + typename T4, + typename T5, + typename T6, + typename T7, + typename T8, + typename T9, + typename T10, + typename Combiner = last_value + > + struct params + { + typedef signal0 type; + }; + }; + + template<> + struct real_get_signal_impl<1> + { + template< + typename R, + typename T1, + typename T2, + typename T3, + typename T4, + typename T5, + typename T6, + typename T7, + typename T8, + typename T9, + typename T10, + typename Combiner = last_value + > + struct params + { + typedef signal1 type; + }; + }; + + template<> + struct real_get_signal_impl<2> + { + template< + typename R, + typename T1, + typename T2, + typename T3, + typename T4, + typename T5, + typename T6, + typename T7, + typename T8, + typename T9, + typename T10, + typename Combiner = last_value + > + struct params + { + typedef signal2 type; + }; + }; + + template<> + struct real_get_signal_impl<3> + { + template< + typename R, + typename T1, + typename T2, + typename T3, + typename T4, + typename T5, + typename T6, + typename T7, + typename T8, + typename T9, + typename T10, + typename Combiner = last_value + > + struct params + { + typedef signal3 type; + }; + }; + + template<> + struct real_get_signal_impl<4> + { + template< + typename R, + typename T1, + typename T2, + typename T3, + typename T4, + typename T5, + typename T6, + typename T7, + typename T8, + typename T9, + typename T10, + typename Combiner = last_value + > + struct params + { + typedef signal4 type; + }; + }; + + template<> + struct real_get_signal_impl<5> + { + template< + typename R, + typename T1, + typename T2, + typename T3, + typename T4, + typename T5, + typename T6, + typename T7, + typename T8, + typename T9, + typename T10, + typename Combiner = last_value + > + struct params + { + typedef signal5 + type; + }; + }; + + template<> + struct real_get_signal_impl<6> + { + template< + typename R, + typename T1, + typename T2, + typename T3, + typename T4, + typename T5, + typename T6, + typename T7, + typename T8, + typename T9, + typename T10, + typename Combiner = last_value + > + struct params + { + typedef signal6 type; + }; + }; + + template<> + struct real_get_signal_impl<7> + { + template< + typename R, + typename T1, + typename T2, + typename T3, + typename T4, + typename T5, + typename T6, + typename T7, + typename T8, + typename T9, + typename T10, + typename Combiner = last_value + > + struct params + { + typedef signal7 type; + }; + }; + + template<> + struct real_get_signal_impl<8> + { + template< + typename R, + typename T1, + typename T2, + typename T3, + typename T4, + typename T5, + typename T6, + typename T7, + typename T8, + typename T9, + typename T10, + typename Combiner = last_value + > + struct params + { + typedef signal8 type; + }; + }; + + template<> + struct real_get_signal_impl<9> + { + template< + typename R, + typename T1, + typename T2, + typename T3, + typename T4, + typename T5, + typename T6, + typename T7, + typename T8, + typename T9, + typename T10, + typename Combiner = last_value + > + struct params + { + typedef signal9 type; + }; + }; + + template<> + struct real_get_signal_impl<10> + { + template< + typename R, + typename T1, + typename T2, + typename T3, + typename T4, + typename T5, + typename T6, + typename T7, + typename T8, + typename T9, + typename T10, + typename Combiner = last_value + > + struct params + { + typedef signal10 type; + }; + }; + + template< + typename R, + typename T1, + typename T2, + typename T3, + typename T4, + typename T5, + typename T6, + typename T7, + typename T8, + typename T9, + typename T10, + typename Combiner = last_value + > + struct get_signal_impl + { + typedef typename real_get_signal_impl< + (count_used_args::value) + >::template params::type + type; + }; + } // end namespace detail + } // end namespace signals + + // Very lightweight wrapper around the signalN classes that allows signals to + // be created where the number of arguments does not need to be part of the + // class name. + template< + typename R, + typename T1 = signals::detail::unused, + typename T2 = signals::detail::unused, + typename T3 = signals::detail::unused, + typename T4 = signals::detail::unused, + typename T5 = signals::detail::unused, + typename T6 = signals::detail::unused, + typename T7 = signals::detail::unused, + typename T8 = signals::detail::unused, + typename T9 = signals::detail::unused, + typename T10 = signals::detail::unused + > + class signal : + public signals::detail::get_signal_impl::type + { + public: + template + struct combiner { + typedef typename signals::detail::get_signal_impl::type + type; + }; + }; +} // end namespace boost + +#endif // BOOST_SIGNAL_HPP diff --git a/include/boost/signals/connection.hpp b/include/boost/signals/connection.hpp new file mode 100644 index 0000000..25cec74 --- /dev/null +++ b/include/boost/signals/connection.hpp @@ -0,0 +1,291 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#ifndef BOOST_SIGNALS_CONNECTION_HPP +#define BOOST_SIGNALS_CONNECTION_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace boost { + namespace signals { + class trackable; + + namespace detail { + // Represents an object that has been bound as part of a slot, and how + // to notify that object of a disconnect + struct bound_object { + void* obj; + void* data; + void (*disconnect)(void*, void*); + + bool operator==(const bound_object& other) const + { return obj == other.obj && data == other.data; } + bool operator<(const bound_object& other) const + { return obj < other.obj; } + }; + + // Describes the connection between a signal and the objects that are + // bound for a specific slot. Enables notification of the signal and the + // slots when a disconnect is requested. + struct basic_connection { + void* signal; + void* signal_data; + void (*signal_disconnect)(void*, void*); + + std::list bound_objects; + }; + } // end namespace detail + + // The user may freely pass around the "connection" object and terminate + // the connection at any time using disconnect(). + class connection : + private less_than_comparable1, + private equality_comparable1 + { + public: + connection(); + connection(const connection&); + ~connection(); + + // Disconnect the signal and slot, if they are connected + void disconnect() const; + + // Returns true if the signal and slot are connected + bool connected() const { return con.get() && con->signal_disconnect; } + + // Comparison of connections + inline bool operator==(const connection& other) const; + inline bool operator<(const connection& other) const; + + // Connection assignment + connection& operator=(const connection& other) ; + + // Swap connections + void swap(connection& other); + + public: // TBD: CHANGE THIS + // Set whether this connection object is controlling or not + void set_controlling() { controlling_connection = true; } + + private: + friend class detail::signal_base_impl; + friend class detail::slot_base; + friend class trackable; + + // Reset this connection to refer to a different actual connection + void reset(signals::detail::basic_connection*); + + // Add a bound object to this connection (not for users) + void add_bound_object(const signals::detail::bound_object& b); + + friend class signals::detail::bound_objects_visitor; + + // Pointer to the actual contents of the connection + shared_ptr con; + + // True if the destruction of this connection object should disconnect + bool controlling_connection; + }; + + // Similar to connection, but will disconnect the connection when it is + // destroyed unless release() has been called. + class scoped_connection : public connection { + public: + scoped_connection() : connection(), released(false) {} + scoped_connection(const connection&); + scoped_connection(const scoped_connection&); + ~scoped_connection(); + + connection release(); + + void swap(scoped_connection&); + + scoped_connection& operator=(const connection&); + scoped_connection& operator=(const scoped_connection&); + + private: + bool released; + }; + + inline connection::connection() : + con(), controlling_connection(false) + { + } + + inline connection::connection(const connection& other) : + con(other.con), controlling_connection(other.controlling_connection) + { + } + + inline connection::~connection() + { + if (controlling_connection) { + disconnect(); + } + } + + inline void + connection::reset(signals::detail::basic_connection* new_con) + { + con.reset(new_con); + } + + inline void + connection::add_bound_object(const signals::detail::bound_object& b) + { + assert(con.get()); + con->bound_objects.push_back(b); + } + + inline bool connection::operator==(const connection& other) const + { + return con.get() == other.con.get(); + } + + inline bool connection::operator<(const connection& other) const + { + return con.get() < other.con.get(); + } + + inline connection& connection::operator=(const connection& other) + { + connection(other).swap(*this); + return *this; + } + + inline void connection::swap(connection& other) + { + this->con.swap(other.con); + std::swap(this->controlling_connection, other.controlling_connection); + } + + inline void swap(connection& c1, connection& c2) + { + c1.swap(c2); + } + + inline scoped_connection::scoped_connection(const connection& other) : + connection(other), + released(false) + { + } + + inline + scoped_connection::scoped_connection(const scoped_connection& other) : + connection(other), + released(other.released) + { + } + + inline scoped_connection::~scoped_connection() + { + if (!released) { + this->disconnect(); + } + } + + inline connection scoped_connection::release() + { + released = true; + return *this; + } + + inline void scoped_connection::swap(scoped_connection& other) + { + this->connection::swap(other); + bool other_released = other.released; + other.released = this->released; + this->released = other_released; + } + + inline void swap(scoped_connection& c1, scoped_connection& c2) + { + c1.swap(c2); + } + + inline scoped_connection& + scoped_connection::operator=(const connection& other) + { + scoped_connection(other).swap(*this); + return *this; + } + + inline scoped_connection& + scoped_connection::operator=(const scoped_connection& other) + { + scoped_connection(other).swap(*this); + return *this; + } + + namespace detail { + struct connection_slot_pair { + connection first; + any second; + + connection_slot_pair() {} + + connection_slot_pair(const connection& c, const any& a) + : first(c), second(a) + { + } + + // Dummys to allow explicit instantiation to work + bool operator==(const connection_slot_pair&) const { return false; } + bool operator<(const connection_slot_pair&) const { return false;} + }; + + // Determines if the underlying connection is disconnected + struct is_disconnected { + typedef std::pair argument_type; + typedef bool result_type; + + inline bool operator()(const argument_type& c) const + { + return !c.second.first.connected(); + } + }; + + // Autodisconnects the bound object when it is destroyed unless the + // release method is invoked. + class auto_disconnect_bound_object { + public: + auto_disconnect_bound_object(const bound_object& b) : + binding(b), auto_disconnect(true) + { + } + + ~auto_disconnect_bound_object() + { + if (auto_disconnect) + binding.disconnect(binding.obj, binding.data); + } + + void release() { auto_disconnect = false; } + + private: + bound_object binding; + bool auto_disconnect; + }; + } // end namespace detail + } // end namespace signals +} // end namespace boost + +#endif // BOOST_SIGNALS_CONNECTION_HPP diff --git a/include/boost/signals/detail/gen_signal_N.pl b/include/boost/signals/detail/gen_signal_N.pl new file mode 100644 index 0000000..1dfd577 --- /dev/null +++ b/include/boost/signals/detail/gen_signal_N.pl @@ -0,0 +1,138 @@ +#!/usr/bin/perl -w +# +# Boost.Signals library +# +# Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +# +# Permission to copy, use, sell and distribute this software is granted +# provided this copyright notice appears in all copies. +# Permission to modify the code and to distribute modified code is granted +# provided this copyright notice appears in all copies, and a notice +# that the code was modified is included with the copyright notice. +# +# This software is provided "as is" without express or implied warranty, +# and with no claim as to its suitability for any purpose. +# +# For more information, see http://www.boost.org +use English; + +if ($#ARGV < 0) { + print "Usage: perl gen_signal_N \n"; + exit; +} + + +$totalNumArgs = $ARGV[0]; +for ($numArgs = 0; $numArgs <= $totalNumArgs; ++$numArgs) { + open OUT, ">signal$numArgs.hpp"; + print OUT "// Boost.Signals library\n"; + print OUT "//\n"; + print OUT "// Copyright (C) 2001 Doug Gregor (gregod\@cs.rpi.edu)\n"; + print OUT "//\n"; + print OUT "// Permission to copy, use, sell and distribute this software is granted\n"; + print OUT "// provided this copyright notice appears in all copies.\n"; + print OUT "// Permission to modify the code and to distribute modified code is granted\n"; + print OUT "// provided this copyright notice appears in all copies, and a notice\n"; + print OUT "// that the code was modified is included with the copyright notice.\n"; + print OUT "//\n"; + print OUT "// This software is provided \"as is\" without express or implied warranty,\n"; + print OUT "// and with no claim as to its suitability for any purpose.\n"; + print OUT " \n"; + print OUT "// For more information, see http://www.boost.org\n"; + print OUT "\n"; + print OUT "#ifndef BOOST_SIGNALS_SIGNAL" . $numArgs . "_HEADER\n"; + print OUT "#define BOOST_SIGNALS_SIGNAL" , $numArgs . "_HEADER\n"; + print OUT "\n"; + print OUT "#define BOOST_SIGNALS_NUM_ARGS $numArgs\n"; + + $templateParms = ""; + for ($i = 1; $i <= $numArgs; ++$i) { + if ($i > 1) { + $templateParms .= ", "; + } + $templateParms .= "typename T$i"; + } + print OUT "#define BOOST_SIGNALS_TEMPLATE_PARMS $templateParms\n"; + + $_ = $templateParms; + s/typename //g; + $templateArgs = $_; + print OUT "#define BOOST_SIGNALS_TEMPLATE_ARGS $templateArgs\n"; + + $parms = ""; + for ($i = 1; $i <= $numArgs; ++$i) { + if ($i > 1) { + $parms .= ", "; + } + $parms .= "T$i a$i"; + } + print OUT "#define BOOST_SIGNALS_PARMS $parms\n"; + + $args = ""; + for ($i = 1; $i <= $numArgs; ++$i) { + if ($i > 1) { + $args .= ", "; + } + $args .= "a$i"; + } + print OUT "#define BOOST_SIGNALS_ARGS $args\n"; + + $boundArgs = ""; + for ($i = 1; $i <= $numArgs; ++$i) { + if ($i > 1) { + $boundArgs .= ", "; + } + $boundArgs .= "args->a$i"; + } + print OUT "#define BOOST_SIGNALS_BOUND_ARGS $boundArgs\n"; + + $argsAsMembers = ""; + for ($i = 1; $i <= $numArgs; ++$i) { + $argsAsMembers .= "T$i a$i;"; + } + print OUT "#define BOOST_SIGNALS_ARGS_AS_MEMBERS $argsAsMembers\n"; + + $copyParms = ""; + for ($i = 1; $i <= $numArgs; ++$i) { + if ($i > 1) { + $copyParms .= ", "; + } + $copyParms .= "T$i ia$i"; + } + print OUT "#define BOOST_SIGNALS_COPY_PARMS $copyParms\n"; + + $initArgs = ""; + if ($numArgs > 0) { + $initArgs = ":"; + } + for ($i = 1; $i <= $numArgs; ++$i) { + if ($i > 1) { + $initArgs .= ", "; + } + $initArgs .= "a$i(ia$i)"; + } + print OUT "#define BOOST_SIGNALS_INIT_ARGS $initArgs\n"; + + $argTypes = ""; + for ($i = 1; $i <= $numArgs; ++$i) { + $argTypes .= "typedef T$i arg". ($i+1) . "_type; "; + } + + print OUT "#define BOOST_SIGNALS_ARG_TYPES $argTypes\n"; + print OUT "\n"; + print OUT "#include \n"; + print OUT "\n"; + print OUT "#undef BOOST_SIGNALS_ARG_TYPES\n"; + print OUT "#undef BOOST_SIGNALS_INIT_ARGS\n"; + print OUT "#undef BOOST_SIGNALS_COPY_PARMS\n"; + print OUT "#undef BOOST_SIGNALS_ARGS_AS_MEMBERS\n"; + print OUT "#undef BOOST_SIGNALS_BOUND_ARGS\n"; + print OUT "#undef BOOST_SIGNALS_ARGS\n"; + print OUT "#undef BOOST_SIGNALS_PARMS\n"; + print OUT "#undef BOOST_SIGNALS_TEMPLATE_ARGS\n"; + print OUT "#undef BOOST_SIGNALS_TEMPLATE_PARMS\n"; + print OUT "#undef BOOST_SIGNALS_NUM_ARGS\n"; + print OUT "\n"; + print OUT "#endif // BOOST_SIGNALS_SIGNAL" . $numArgs . "_HEADER\n"; + close OUT; +} diff --git a/include/boost/signals/detail/signal_base.hpp b/include/boost/signals/detail/signal_base.hpp new file mode 100644 index 0000000..c6e50f4 --- /dev/null +++ b/include/boost/signals/detail/signal_base.hpp @@ -0,0 +1,188 @@ +// Boost.Signals library +// +// Copyright (C) 2001-2 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#ifndef BOOST_SIGNALS_SIGNAL_BASE_HEADER +#define BOOST_SIGNALS_SIGNAL_BASE_HEADER + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { + namespace signals { + namespace detail { + // Forward declaration for the mapping from slot names to connections + class named_slot_map; + + // This function object bridges from a pair of any objects that hold + // values of type Key to the underlying function object that compares + // values of type Key. + template + class any_bridge_compare { + public: + typedef bool result_type; + typedef const any& first_argument_type; + typedef const any& second_argument_type; + + any_bridge_compare(const Compare& c) : comp(c) {} + + bool operator()(const any& k1, const any& k2) const + { + // if k1 is empty, then it precedes nothing + if (k1.empty()) + return false; + + // if k2 is empty, then k1 must precede it + if (k2.empty()) + return true; + + // Neither is empty, so compare their values to order them + // The strange */& is so that we will get a reference to the + // value stored in the any object instead of a copy + return comp(*any_cast(&k1), *any_cast(&k2)); + } + + private: + Compare comp; + }; + + // Must be constructed before calling the slots, because it safely + // manages call depth + class call_notification { + public: + call_notification(const shared_ptr&); + ~call_notification(); + + shared_ptr impl; + }; + + // Implementation of base class for all signals. It handles the + // management of the underlying slot lists. + class signal_base_impl { + public: + friend class call_notification; + + typedef function2 compare_type; + + // Make sure that an exception does not cause the "clearing" flag to + // remain set + class temporarily_set_clearing { + public: + temporarily_set_clearing(signal_base_impl* b) : base(b) + { + base->flags.clearing = true; + } + + ~temporarily_set_clearing() + { + base->flags.clearing = false; + } + + private: + signal_base_impl* base; + }; + + friend class temporarily_set_clearing; + + signal_base_impl(const compare_type&); + ~signal_base_impl(); + + // Disconnect all slots connected to this signal + void disconnect_all_slots(); + + // Are there any connected slots? + bool empty() const; + + // Disconnect all slots in the given group + void disconnect(const any&); + + // We're being notified that a slot has disconnected + static void slot_disconnected(void* obj, void* data); + + connection connect_slot(const any& slot, + const any& name, + const std::vector&); + + private: + // Remove all of the slots that have been marked "disconnected" + void remove_disconnected_slots() const; + + public: + // Our call depth when invoking slots (> 1 when we have a loop) + mutable int call_depth; + + struct { + // True if some slots have disconnected, but we were not able to + // remove them from the list of slots because there are valid + // iterators into the slot list + mutable bool delayed_disconnect:1; + + // True if we are disconnecting all disconnected slots + bool clearing:1; + } flags; + + // Slots + typedef std::multimap + slot_container_type; + typedef slot_container_type::iterator slot_iterator; + typedef slot_container_type::value_type stored_slot_type; + mutable slot_container_type slots; + }; + + class signal_base : public noncopyable { + public: + typedef signal_base_impl::compare_type compare_type; + + friend class call_notification; + + signal_base(const compare_type& comp) : impl() + { + impl.reset(new signal_base_impl(comp)); + } + + ~signal_base(); + + public: + // Disconnect all slots connected to this signal + void disconnect_all_slots() { impl->disconnect_all_slots(); } + + // Are there any connected slots? + bool empty() const { return impl->empty(); } + + protected: + connection connect_slot(const any& slot, + const any& name, + const std::vector& bound) + { + return impl->connect_slot(slot, name, bound); + } + + typedef signal_base_impl::slot_iterator slot_iterator; + typedef signal_base_impl::stored_slot_type stored_slot_type; + + shared_ptr impl; + }; + } // end namespace detail + } // end namespace signals +} // end namespace boost + +#endif // BOOST_SIGNALS_SIGNAL_BASE_HEADER diff --git a/include/boost/signals/detail/signals_common.hpp b/include/boost/signals/detail/signals_common.hpp new file mode 100644 index 0000000..606e8d4 --- /dev/null +++ b/include/boost/signals/detail/signals_common.hpp @@ -0,0 +1,155 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#ifndef BOOST_SIGNALS_COMMON_HEADER +#define BOOST_SIGNALS_COMMON_HEADER + +#include +#include + +namespace boost { + namespace signals { + namespace detail { + // The unusable class is a placeholder for unused function arguments + // It is also completely unusable except that it constructable from + // anything. This helps compilers without partial specialization + // handle slots returning void. + struct unusable { + unusable() {} + }; + + // Determine the result type of a slot call + template + struct slot_result_type { + typedef R type; + }; + + template<> + struct slot_result_type { + typedef unusable type; + }; + + // Determine if the given type T is a signal + class signal_base; + + template + struct is_signal { + BOOST_STATIC_CONSTANT(bool, + value = (is_convertible::value)); + }; + + /* + * The IF implementation is temporary code. When a Boost metaprogramming + * library is introduced, Boost.Signals will use it instead. + */ + namespace intimate { + struct SelectThen + { + template + struct Result + { + typedef Then type; + }; + }; + + struct SelectElse + { + template + struct Result + { + typedef Else type; + }; + }; + + template + struct Selector + { + typedef SelectThen type; + }; + + template<> + struct Selector + { + typedef SelectElse type; + }; + } // end namespace intimate + + template + struct IF + { + typedef typename intimate::Selector::type select; + typedef typename select::template Result::type type; + }; + + // Determine if the incoming argument is a reference_wrapper +#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION + template + struct is_ref + { + BOOST_STATIC_CONSTANT(bool, value = false); + }; + + template + struct is_ref > + { + BOOST_STATIC_CONSTANT(bool, value = true); + }; +#else // no partial specialization + typedef char yes_type; + typedef double no_type; + + no_type is_ref_tester(...); + + template + yes_type is_ref_tester(reference_wrapper*); + + template + struct is_ref + { + static T* t; + BOOST_STATIC_CONSTANT(bool, + value = (sizeof(is_ref_tester(t)) == sizeof(yes_type))); + }; +#endif // BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION + + // A slot can be a signal, a reference to a function object, or a + // function object. + struct signal_tag {}; + struct reference_tag {}; + struct value_tag {}; + + // Classify the given slot as a signal, a reference-to-slot, or a + // standard slot + template + class get_slot_tag { + typedef typename IF<(is_signal::value), + signal_tag, + value_tag>::type signal_or_value; + + public: + typedef typename IF<(is_ref::value), + reference_tag, + signal_or_value>::type type; + }; + + // Forward declaration needed in lots of places + class signal_base_impl; + class bound_objects_visitor; + class slot_base; + } // end namespace detail + } // end namespace signals +} // end namespace boost + +#endif // BOOST_SIGNALS_COMMON_HEADER diff --git a/include/boost/signals/detail/slot_call_iterator.hpp b/include/boost/signals/detail/slot_call_iterator.hpp new file mode 100644 index 0000000..ed611d5 --- /dev/null +++ b/include/boost/signals/detail/slot_call_iterator.hpp @@ -0,0 +1,119 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#ifndef BOOST_SIGNALS_SLOT_CALL_ITERATOR +#define BOOST_SIGNALS_SLOT_CALL_ITERATOR + +#include +#include +#include +#include + +namespace boost { + namespace signals { + namespace detail { + // A cached return value from a slot + template + struct cached_return_value { + cached_return_value(const T& t) : value(t) {} + + T value; + }; + + // Generates a slot call iterator. Essentially, this is an iterator that: + // - skips over disconnected slots in the underlying list + // - calls the connected slots when dereferenced + // - caches the result of calling the slots + template + class slot_call_policies : public default_iterator_policies { + public: + typedef typename Function::result_type result_type; + + slot_call_policies() {} + + slot_call_policies(const Iterator& x, Function fi) : + end(x), f(fi), cache() + { + } + + void initialize(Iterator& x) + { + x = std::find_if(x, end, std::not1(is_disconnected())); + cache.reset(); + } + + template + typename IteratorAdaptor::reference + dereference(const IteratorAdaptor& x) const + { + if (!cache.get()) { + cache.reset(new cached_return_value(f(*x.base()))); + } + + return cache->value; + } + + template + void increment(IteratorAdaptor& x) + { + ++x.base(); + x.base() = std::find_if(x.base(), x.policies().end, + std::not1(is_disconnected())); + cache.reset(); + } + + template + bool equal(const IteratorAdaptor1& x, const IteratorAdaptor2& y) const + { + Iterator xb = std::find_if(x.base(), x.policies().end, + std::not1(is_disconnected())); + Iterator yb = std::find_if(y.base(), y.policies().end, + std::not1(is_disconnected())); + const_cast(x).base() = xb; + const_cast(y).base() = yb; + return xb == yb; + } + + private: + Iterator end; + Function f; + mutable shared_ptr< cached_return_value > cache; + }; + + template + class slot_call_iterator_generator { + private: + typedef typename Function::result_type value_type; + public: + typedef slot_call_policies policy_type; + typedef iterator_adaptor type; + }; + + template + inline typename slot_call_iterator_generator::type + make_slot_call_iterator(Iterator first, Iterator last, Function f) + { + typedef slot_call_iterator_generator gen; + typedef typename gen::type sc_iterator; + typedef typename gen::policy_type sc_policy; + + return sc_iterator(first, sc_policy(last, f)); + } + } // end namespace detail + } // end namespace signals +} // end namespace boost +#endif // BOOST_SIGNALS_SLOT_CALL_ITERATOR diff --git a/include/boost/signals/signal0.hpp b/include/boost/signals/signal0.hpp new file mode 100644 index 0000000..6b9ac29 --- /dev/null +++ b/include/boost/signals/signal0.hpp @@ -0,0 +1,43 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#ifndef BOOST_SIGNALS_SIGNAL0_HEADER +#define BOOST_SIGNALS_SIGNAL0_HEADER + +#define BOOST_SIGNALS_NUM_ARGS 0 +#define BOOST_SIGNALS_TEMPLATE_PARMS +#define BOOST_SIGNALS_TEMPLATE_ARGS +#define BOOST_SIGNALS_PARMS +#define BOOST_SIGNALS_ARGS +#define BOOST_SIGNALS_BOUND_ARGS +#define BOOST_SIGNALS_ARGS_AS_MEMBERS +#define BOOST_SIGNALS_COPY_PARMS +#define BOOST_SIGNALS_INIT_ARGS +#define BOOST_SIGNALS_ARG_TYPES + +#include + +#undef BOOST_SIGNALS_ARG_TYPES +#undef BOOST_SIGNALS_INIT_ARGS +#undef BOOST_SIGNALS_COPY_PARMS +#undef BOOST_SIGNALS_ARGS_AS_MEMBERS +#undef BOOST_SIGNALS_BOUND_ARGS +#undef BOOST_SIGNALS_ARGS +#undef BOOST_SIGNALS_PARMS +#undef BOOST_SIGNALS_TEMPLATE_ARGS +#undef BOOST_SIGNALS_TEMPLATE_PARMS +#undef BOOST_SIGNALS_NUM_ARGS + +#endif // BOOST_SIGNALS_SIGNAL0_HEADER diff --git a/include/boost/signals/signal1.hpp b/include/boost/signals/signal1.hpp new file mode 100644 index 0000000..b2daffc --- /dev/null +++ b/include/boost/signals/signal1.hpp @@ -0,0 +1,43 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#ifndef BOOST_SIGNALS_SIGNAL1_HEADER +#define BOOST_SIGNALS_SIGNAL1_HEADER + +#define BOOST_SIGNALS_NUM_ARGS 1 +#define BOOST_SIGNALS_TEMPLATE_PARMS typename T1 +#define BOOST_SIGNALS_TEMPLATE_ARGS T1 +#define BOOST_SIGNALS_PARMS T1 a1 +#define BOOST_SIGNALS_ARGS a1 +#define BOOST_SIGNALS_BOUND_ARGS args->a1 +#define BOOST_SIGNALS_ARGS_AS_MEMBERS T1 a1; +#define BOOST_SIGNALS_COPY_PARMS T1 ia1 +#define BOOST_SIGNALS_INIT_ARGS :a1(ia1) +#define BOOST_SIGNALS_ARG_TYPES typedef T1 arg2_type; + +#include + +#undef BOOST_SIGNALS_ARG_TYPES +#undef BOOST_SIGNALS_INIT_ARGS +#undef BOOST_SIGNALS_COPY_PARMS +#undef BOOST_SIGNALS_ARGS_AS_MEMBERS +#undef BOOST_SIGNALS_BOUND_ARGS +#undef BOOST_SIGNALS_ARGS +#undef BOOST_SIGNALS_PARMS +#undef BOOST_SIGNALS_TEMPLATE_ARGS +#undef BOOST_SIGNALS_TEMPLATE_PARMS +#undef BOOST_SIGNALS_NUM_ARGS + +#endif // BOOST_SIGNALS_SIGNAL1_HEADER diff --git a/include/boost/signals/signal10.hpp b/include/boost/signals/signal10.hpp new file mode 100644 index 0000000..2665f03 --- /dev/null +++ b/include/boost/signals/signal10.hpp @@ -0,0 +1,43 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#ifndef BOOST_SIGNALS_SIGNAL10_HEADER +#define BOOST_SIGNALS_SIGNAL10_HEADER + +#define BOOST_SIGNALS_NUM_ARGS 10 +#define BOOST_SIGNALS_TEMPLATE_PARMS typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10 +#define BOOST_SIGNALS_TEMPLATE_ARGS T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 +#define BOOST_SIGNALS_PARMS T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7, T8 a8, T9 a9, T10 a10 +#define BOOST_SIGNALS_ARGS a1, a2, a3, a4, a5, a6, a7, a8, a9, a10 +#define BOOST_SIGNALS_BOUND_ARGS args->a1, args->a2, args->a3, args->a4, args->a5, args->a6, args->a7, args->a8, args->a9, args->a10 +#define BOOST_SIGNALS_ARGS_AS_MEMBERS T1 a1;T2 a2;T3 a3;T4 a4;T5 a5;T6 a6;T7 a7;T8 a8;T9 a9;T10 a10; +#define BOOST_SIGNALS_COPY_PARMS T1 ia1, T2 ia2, T3 ia3, T4 ia4, T5 ia5, T6 ia6, T7 ia7, T8 ia8, T9 ia9, T10 ia10 +#define BOOST_SIGNALS_INIT_ARGS :a1(ia1), a2(ia2), a3(ia3), a4(ia4), a5(ia5), a6(ia6), a7(ia7), a8(ia8), a9(ia9), a10(ia10) +#define BOOST_SIGNALS_ARG_TYPES typedef T1 arg2_type; typedef T2 arg3_type; typedef T3 arg4_type; typedef T4 arg5_type; typedef T5 arg6_type; typedef T6 arg7_type; typedef T7 arg8_type; typedef T8 arg9_type; typedef T9 arg10_type; typedef T10 arg11_type; + +#include + +#undef BOOST_SIGNALS_ARG_TYPES +#undef BOOST_SIGNALS_INIT_ARGS +#undef BOOST_SIGNALS_COPY_PARMS +#undef BOOST_SIGNALS_ARGS_AS_MEMBERS +#undef BOOST_SIGNALS_BOUND_ARGS +#undef BOOST_SIGNALS_ARGS +#undef BOOST_SIGNALS_PARMS +#undef BOOST_SIGNALS_TEMPLATE_ARGS +#undef BOOST_SIGNALS_TEMPLATE_PARMS +#undef BOOST_SIGNALS_NUM_ARGS + +#endif // BOOST_SIGNALS_SIGNAL10_HEADER diff --git a/include/boost/signals/signal2.hpp b/include/boost/signals/signal2.hpp new file mode 100644 index 0000000..371ec4f --- /dev/null +++ b/include/boost/signals/signal2.hpp @@ -0,0 +1,43 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#ifndef BOOST_SIGNALS_SIGNAL2_HEADER +#define BOOST_SIGNALS_SIGNAL2_HEADER + +#define BOOST_SIGNALS_NUM_ARGS 2 +#define BOOST_SIGNALS_TEMPLATE_PARMS typename T1, typename T2 +#define BOOST_SIGNALS_TEMPLATE_ARGS T1, T2 +#define BOOST_SIGNALS_PARMS T1 a1, T2 a2 +#define BOOST_SIGNALS_ARGS a1, a2 +#define BOOST_SIGNALS_BOUND_ARGS args->a1, args->a2 +#define BOOST_SIGNALS_ARGS_AS_MEMBERS T1 a1;T2 a2; +#define BOOST_SIGNALS_COPY_PARMS T1 ia1, T2 ia2 +#define BOOST_SIGNALS_INIT_ARGS :a1(ia1), a2(ia2) +#define BOOST_SIGNALS_ARG_TYPES typedef T1 arg2_type; typedef T2 arg3_type; + +#include + +#undef BOOST_SIGNALS_ARG_TYPES +#undef BOOST_SIGNALS_INIT_ARGS +#undef BOOST_SIGNALS_COPY_PARMS +#undef BOOST_SIGNALS_ARGS_AS_MEMBERS +#undef BOOST_SIGNALS_BOUND_ARGS +#undef BOOST_SIGNALS_ARGS +#undef BOOST_SIGNALS_PARMS +#undef BOOST_SIGNALS_TEMPLATE_ARGS +#undef BOOST_SIGNALS_TEMPLATE_PARMS +#undef BOOST_SIGNALS_NUM_ARGS + +#endif // BOOST_SIGNALS_SIGNAL2_HEADER diff --git a/include/boost/signals/signal3.hpp b/include/boost/signals/signal3.hpp new file mode 100644 index 0000000..5fbc9e6 --- /dev/null +++ b/include/boost/signals/signal3.hpp @@ -0,0 +1,43 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#ifndef BOOST_SIGNALS_SIGNAL3_HEADER +#define BOOST_SIGNALS_SIGNAL3_HEADER + +#define BOOST_SIGNALS_NUM_ARGS 3 +#define BOOST_SIGNALS_TEMPLATE_PARMS typename T1, typename T2, typename T3 +#define BOOST_SIGNALS_TEMPLATE_ARGS T1, T2, T3 +#define BOOST_SIGNALS_PARMS T1 a1, T2 a2, T3 a3 +#define BOOST_SIGNALS_ARGS a1, a2, a3 +#define BOOST_SIGNALS_BOUND_ARGS args->a1, args->a2, args->a3 +#define BOOST_SIGNALS_ARGS_AS_MEMBERS T1 a1;T2 a2;T3 a3; +#define BOOST_SIGNALS_COPY_PARMS T1 ia1, T2 ia2, T3 ia3 +#define BOOST_SIGNALS_INIT_ARGS :a1(ia1), a2(ia2), a3(ia3) +#define BOOST_SIGNALS_ARG_TYPES typedef T1 arg2_type; typedef T2 arg3_type; typedef T3 arg4_type; + +#include + +#undef BOOST_SIGNALS_ARG_TYPES +#undef BOOST_SIGNALS_INIT_ARGS +#undef BOOST_SIGNALS_COPY_PARMS +#undef BOOST_SIGNALS_ARGS_AS_MEMBERS +#undef BOOST_SIGNALS_BOUND_ARGS +#undef BOOST_SIGNALS_ARGS +#undef BOOST_SIGNALS_PARMS +#undef BOOST_SIGNALS_TEMPLATE_ARGS +#undef BOOST_SIGNALS_TEMPLATE_PARMS +#undef BOOST_SIGNALS_NUM_ARGS + +#endif // BOOST_SIGNALS_SIGNAL3_HEADER diff --git a/include/boost/signals/signal4.hpp b/include/boost/signals/signal4.hpp new file mode 100644 index 0000000..1f473bc --- /dev/null +++ b/include/boost/signals/signal4.hpp @@ -0,0 +1,43 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#ifndef BOOST_SIGNALS_SIGNAL4_HEADER +#define BOOST_SIGNALS_SIGNAL4_HEADER + +#define BOOST_SIGNALS_NUM_ARGS 4 +#define BOOST_SIGNALS_TEMPLATE_PARMS typename T1, typename T2, typename T3, typename T4 +#define BOOST_SIGNALS_TEMPLATE_ARGS T1, T2, T3, T4 +#define BOOST_SIGNALS_PARMS T1 a1, T2 a2, T3 a3, T4 a4 +#define BOOST_SIGNALS_ARGS a1, a2, a3, a4 +#define BOOST_SIGNALS_BOUND_ARGS args->a1, args->a2, args->a3, args->a4 +#define BOOST_SIGNALS_ARGS_AS_MEMBERS T1 a1;T2 a2;T3 a3;T4 a4; +#define BOOST_SIGNALS_COPY_PARMS T1 ia1, T2 ia2, T3 ia3, T4 ia4 +#define BOOST_SIGNALS_INIT_ARGS :a1(ia1), a2(ia2), a3(ia3), a4(ia4) +#define BOOST_SIGNALS_ARG_TYPES typedef T1 arg2_type; typedef T2 arg3_type; typedef T3 arg4_type; typedef T4 arg5_type; + +#include + +#undef BOOST_SIGNALS_ARG_TYPES +#undef BOOST_SIGNALS_INIT_ARGS +#undef BOOST_SIGNALS_COPY_PARMS +#undef BOOST_SIGNALS_ARGS_AS_MEMBERS +#undef BOOST_SIGNALS_BOUND_ARGS +#undef BOOST_SIGNALS_ARGS +#undef BOOST_SIGNALS_PARMS +#undef BOOST_SIGNALS_TEMPLATE_ARGS +#undef BOOST_SIGNALS_TEMPLATE_PARMS +#undef BOOST_SIGNALS_NUM_ARGS + +#endif // BOOST_SIGNALS_SIGNAL4_HEADER diff --git a/include/boost/signals/signal5.hpp b/include/boost/signals/signal5.hpp new file mode 100644 index 0000000..b2ad9bb --- /dev/null +++ b/include/boost/signals/signal5.hpp @@ -0,0 +1,43 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#ifndef BOOST_SIGNALS_SIGNAL5_HEADER +#define BOOST_SIGNALS_SIGNAL5_HEADER + +#define BOOST_SIGNALS_NUM_ARGS 5 +#define BOOST_SIGNALS_TEMPLATE_PARMS typename T1, typename T2, typename T3, typename T4, typename T5 +#define BOOST_SIGNALS_TEMPLATE_ARGS T1, T2, T3, T4, T5 +#define BOOST_SIGNALS_PARMS T1 a1, T2 a2, T3 a3, T4 a4, T5 a5 +#define BOOST_SIGNALS_ARGS a1, a2, a3, a4, a5 +#define BOOST_SIGNALS_BOUND_ARGS args->a1, args->a2, args->a3, args->a4, args->a5 +#define BOOST_SIGNALS_ARGS_AS_MEMBERS T1 a1;T2 a2;T3 a3;T4 a4;T5 a5; +#define BOOST_SIGNALS_COPY_PARMS T1 ia1, T2 ia2, T3 ia3, T4 ia4, T5 ia5 +#define BOOST_SIGNALS_INIT_ARGS :a1(ia1), a2(ia2), a3(ia3), a4(ia4), a5(ia5) +#define BOOST_SIGNALS_ARG_TYPES typedef T1 arg2_type; typedef T2 arg3_type; typedef T3 arg4_type; typedef T4 arg5_type; typedef T5 arg6_type; + +#include + +#undef BOOST_SIGNALS_ARG_TYPES +#undef BOOST_SIGNALS_INIT_ARGS +#undef BOOST_SIGNALS_COPY_PARMS +#undef BOOST_SIGNALS_ARGS_AS_MEMBERS +#undef BOOST_SIGNALS_BOUND_ARGS +#undef BOOST_SIGNALS_ARGS +#undef BOOST_SIGNALS_PARMS +#undef BOOST_SIGNALS_TEMPLATE_ARGS +#undef BOOST_SIGNALS_TEMPLATE_PARMS +#undef BOOST_SIGNALS_NUM_ARGS + +#endif // BOOST_SIGNALS_SIGNAL5_HEADER diff --git a/include/boost/signals/signal6.hpp b/include/boost/signals/signal6.hpp new file mode 100644 index 0000000..b538f5e --- /dev/null +++ b/include/boost/signals/signal6.hpp @@ -0,0 +1,43 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#ifndef BOOST_SIGNALS_SIGNAL6_HEADER +#define BOOST_SIGNALS_SIGNAL6_HEADER + +#define BOOST_SIGNALS_NUM_ARGS 6 +#define BOOST_SIGNALS_TEMPLATE_PARMS typename T1, typename T2, typename T3, typename T4, typename T5, typename T6 +#define BOOST_SIGNALS_TEMPLATE_ARGS T1, T2, T3, T4, T5, T6 +#define BOOST_SIGNALS_PARMS T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6 +#define BOOST_SIGNALS_ARGS a1, a2, a3, a4, a5, a6 +#define BOOST_SIGNALS_BOUND_ARGS args->a1, args->a2, args->a3, args->a4, args->a5, args->a6 +#define BOOST_SIGNALS_ARGS_AS_MEMBERS T1 a1;T2 a2;T3 a3;T4 a4;T5 a5;T6 a6; +#define BOOST_SIGNALS_COPY_PARMS T1 ia1, T2 ia2, T3 ia3, T4 ia4, T5 ia5, T6 ia6 +#define BOOST_SIGNALS_INIT_ARGS :a1(ia1), a2(ia2), a3(ia3), a4(ia4), a5(ia5), a6(ia6) +#define BOOST_SIGNALS_ARG_TYPES typedef T1 arg2_type; typedef T2 arg3_type; typedef T3 arg4_type; typedef T4 arg5_type; typedef T5 arg6_type; typedef T6 arg7_type; + +#include + +#undef BOOST_SIGNALS_ARG_TYPES +#undef BOOST_SIGNALS_INIT_ARGS +#undef BOOST_SIGNALS_COPY_PARMS +#undef BOOST_SIGNALS_ARGS_AS_MEMBERS +#undef BOOST_SIGNALS_BOUND_ARGS +#undef BOOST_SIGNALS_ARGS +#undef BOOST_SIGNALS_PARMS +#undef BOOST_SIGNALS_TEMPLATE_ARGS +#undef BOOST_SIGNALS_TEMPLATE_PARMS +#undef BOOST_SIGNALS_NUM_ARGS + +#endif // BOOST_SIGNALS_SIGNAL6_HEADER diff --git a/include/boost/signals/signal7.hpp b/include/boost/signals/signal7.hpp new file mode 100644 index 0000000..2185558 --- /dev/null +++ b/include/boost/signals/signal7.hpp @@ -0,0 +1,43 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#ifndef BOOST_SIGNALS_SIGNAL7_HEADER +#define BOOST_SIGNALS_SIGNAL7_HEADER + +#define BOOST_SIGNALS_NUM_ARGS 7 +#define BOOST_SIGNALS_TEMPLATE_PARMS typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7 +#define BOOST_SIGNALS_TEMPLATE_ARGS T1, T2, T3, T4, T5, T6, T7 +#define BOOST_SIGNALS_PARMS T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7 +#define BOOST_SIGNALS_ARGS a1, a2, a3, a4, a5, a6, a7 +#define BOOST_SIGNALS_BOUND_ARGS args->a1, args->a2, args->a3, args->a4, args->a5, args->a6, args->a7 +#define BOOST_SIGNALS_ARGS_AS_MEMBERS T1 a1;T2 a2;T3 a3;T4 a4;T5 a5;T6 a6;T7 a7; +#define BOOST_SIGNALS_COPY_PARMS T1 ia1, T2 ia2, T3 ia3, T4 ia4, T5 ia5, T6 ia6, T7 ia7 +#define BOOST_SIGNALS_INIT_ARGS :a1(ia1), a2(ia2), a3(ia3), a4(ia4), a5(ia5), a6(ia6), a7(ia7) +#define BOOST_SIGNALS_ARG_TYPES typedef T1 arg2_type; typedef T2 arg3_type; typedef T3 arg4_type; typedef T4 arg5_type; typedef T5 arg6_type; typedef T6 arg7_type; typedef T7 arg8_type; + +#include + +#undef BOOST_SIGNALS_ARG_TYPES +#undef BOOST_SIGNALS_INIT_ARGS +#undef BOOST_SIGNALS_COPY_PARMS +#undef BOOST_SIGNALS_ARGS_AS_MEMBERS +#undef BOOST_SIGNALS_BOUND_ARGS +#undef BOOST_SIGNALS_ARGS +#undef BOOST_SIGNALS_PARMS +#undef BOOST_SIGNALS_TEMPLATE_ARGS +#undef BOOST_SIGNALS_TEMPLATE_PARMS +#undef BOOST_SIGNALS_NUM_ARGS + +#endif // BOOST_SIGNALS_SIGNAL7_HEADER diff --git a/include/boost/signals/signal8.hpp b/include/boost/signals/signal8.hpp new file mode 100644 index 0000000..f13599a --- /dev/null +++ b/include/boost/signals/signal8.hpp @@ -0,0 +1,43 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#ifndef BOOST_SIGNALS_SIGNAL8_HEADER +#define BOOST_SIGNALS_SIGNAL8_HEADER + +#define BOOST_SIGNALS_NUM_ARGS 8 +#define BOOST_SIGNALS_TEMPLATE_PARMS typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8 +#define BOOST_SIGNALS_TEMPLATE_ARGS T1, T2, T3, T4, T5, T6, T7, T8 +#define BOOST_SIGNALS_PARMS T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7, T8 a8 +#define BOOST_SIGNALS_ARGS a1, a2, a3, a4, a5, a6, a7, a8 +#define BOOST_SIGNALS_BOUND_ARGS args->a1, args->a2, args->a3, args->a4, args->a5, args->a6, args->a7, args->a8 +#define BOOST_SIGNALS_ARGS_AS_MEMBERS T1 a1;T2 a2;T3 a3;T4 a4;T5 a5;T6 a6;T7 a7;T8 a8; +#define BOOST_SIGNALS_COPY_PARMS T1 ia1, T2 ia2, T3 ia3, T4 ia4, T5 ia5, T6 ia6, T7 ia7, T8 ia8 +#define BOOST_SIGNALS_INIT_ARGS :a1(ia1), a2(ia2), a3(ia3), a4(ia4), a5(ia5), a6(ia6), a7(ia7), a8(ia8) +#define BOOST_SIGNALS_ARG_TYPES typedef T1 arg2_type; typedef T2 arg3_type; typedef T3 arg4_type; typedef T4 arg5_type; typedef T5 arg6_type; typedef T6 arg7_type; typedef T7 arg8_type; typedef T8 arg9_type; + +#include + +#undef BOOST_SIGNALS_ARG_TYPES +#undef BOOST_SIGNALS_INIT_ARGS +#undef BOOST_SIGNALS_COPY_PARMS +#undef BOOST_SIGNALS_ARGS_AS_MEMBERS +#undef BOOST_SIGNALS_BOUND_ARGS +#undef BOOST_SIGNALS_ARGS +#undef BOOST_SIGNALS_PARMS +#undef BOOST_SIGNALS_TEMPLATE_ARGS +#undef BOOST_SIGNALS_TEMPLATE_PARMS +#undef BOOST_SIGNALS_NUM_ARGS + +#endif // BOOST_SIGNALS_SIGNAL8_HEADER diff --git a/include/boost/signals/signal9.hpp b/include/boost/signals/signal9.hpp new file mode 100644 index 0000000..1288b21 --- /dev/null +++ b/include/boost/signals/signal9.hpp @@ -0,0 +1,43 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#ifndef BOOST_SIGNALS_SIGNAL9_HEADER +#define BOOST_SIGNALS_SIGNAL9_HEADER + +#define BOOST_SIGNALS_NUM_ARGS 9 +#define BOOST_SIGNALS_TEMPLATE_PARMS typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9 +#define BOOST_SIGNALS_TEMPLATE_ARGS T1, T2, T3, T4, T5, T6, T7, T8, T9 +#define BOOST_SIGNALS_PARMS T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7, T8 a8, T9 a9 +#define BOOST_SIGNALS_ARGS a1, a2, a3, a4, a5, a6, a7, a8, a9 +#define BOOST_SIGNALS_BOUND_ARGS args->a1, args->a2, args->a3, args->a4, args->a5, args->a6, args->a7, args->a8, args->a9 +#define BOOST_SIGNALS_ARGS_AS_MEMBERS T1 a1;T2 a2;T3 a3;T4 a4;T5 a5;T6 a6;T7 a7;T8 a8;T9 a9; +#define BOOST_SIGNALS_COPY_PARMS T1 ia1, T2 ia2, T3 ia3, T4 ia4, T5 ia5, T6 ia6, T7 ia7, T8 ia8, T9 ia9 +#define BOOST_SIGNALS_INIT_ARGS :a1(ia1), a2(ia2), a3(ia3), a4(ia4), a5(ia5), a6(ia6), a7(ia7), a8(ia8), a9(ia9) +#define BOOST_SIGNALS_ARG_TYPES typedef T1 arg2_type; typedef T2 arg3_type; typedef T3 arg4_type; typedef T4 arg5_type; typedef T5 arg6_type; typedef T6 arg7_type; typedef T7 arg8_type; typedef T8 arg9_type; typedef T9 arg10_type; + +#include + +#undef BOOST_SIGNALS_ARG_TYPES +#undef BOOST_SIGNALS_INIT_ARGS +#undef BOOST_SIGNALS_COPY_PARMS +#undef BOOST_SIGNALS_ARGS_AS_MEMBERS +#undef BOOST_SIGNALS_BOUND_ARGS +#undef BOOST_SIGNALS_ARGS +#undef BOOST_SIGNALS_PARMS +#undef BOOST_SIGNALS_TEMPLATE_ARGS +#undef BOOST_SIGNALS_TEMPLATE_PARMS +#undef BOOST_SIGNALS_NUM_ARGS + +#endif // BOOST_SIGNALS_SIGNAL9_HEADER diff --git a/include/boost/signals/signal_template.hpp b/include/boost/signals/signal_template.hpp new file mode 100644 index 0000000..11eaae1 --- /dev/null +++ b/include/boost/signals/signal_template.hpp @@ -0,0 +1,353 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +// This file intentionally does not have include guards, because it is meant +// to be included multiple times (one for each signalN class). The +// BOOST_SIGNALS_SIGNAL_TEMPLATE_HEADER_INCLUDED macro merely serves to +// suppress reinclusion of the files that this header depends on. +#ifndef BOOST_SIGNALS_SIGNAL_TEMPLATE_HEADER_INCLUDED +#define BOOST_SIGNALS_SIGNAL_TEMPLATE_HEADER_INCLUDED +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif // !BOOST_SIGNALS_SIGNAL_TEMPLATE_HEADER_INCLUDED + +// Include the appropriate functionN header +#define BOOST_SIGNAL_FUNCTION_N_HEADER BOOST_JOIN() +#include BOOST_SIGNAL_FUNCTION_N_HEADER + +// Determine if a comma should follow a listing of the arguments/parameters +#if BOOST_SIGNALS_NUM_ARGS == 0 +# define BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS +#else +# define BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS , +#endif // BOOST_SIGNALS_NUM_ARGS > 0 + +// Define class names used +#define BOOST_SIGNALS_SIGNAL BOOST_JOIN(signal,BOOST_SIGNALS_NUM_ARGS) +#define BOOST_SIGNALS_FUNCTION BOOST_JOIN(function,BOOST_SIGNALS_NUM_ARGS) +#define BOOST_SIGNALS_ARGS_STRUCT BOOST_JOIN(args,BOOST_SIGNALS_NUM_ARGS) +#define BOOST_SIGNALS_CALL_BOUND BOOST_JOIN(call_bound,BOOST_SIGNALS_NUM_ARGS) + +// Define commonly-used instantiations +#define BOOST_SIGNALS_ARGS_STRUCT_INST \ + signals::detail::BOOST_SIGNALS_ARGS_STRUCT + +namespace boost { + namespace signals { + namespace detail { + // Holds the arguments for a bound slot call in a single place + template + struct BOOST_SIGNALS_ARGS_STRUCT { + BOOST_SIGNALS_ARGS_STRUCT(BOOST_SIGNALS_COPY_PARMS) + BOOST_SIGNALS_INIT_ARGS + { + } + + BOOST_SIGNALS_ARGS_AS_MEMBERS + }; + + // Function object that calls the function object given to it, passing + // the bound arguments along to that underlying function object + template + struct BOOST_SIGNALS_CALL_BOUND { + template + struct caller { + typedef BOOST_SIGNALS_ARGS_STRUCT* + args_type; + + args_type args; + + typedef R result_type; + + caller() {} + caller(args_type a) : args(a) {} + + template + R operator()(const Pair& slot) const + { + F* target = const_cast(any_cast(&slot.second.second)); + return (*target)(BOOST_SIGNALS_BOUND_ARGS); + } + }; + }; + + template<> + struct BOOST_SIGNALS_CALL_BOUND { + template + struct caller { + typedef BOOST_SIGNALS_ARGS_STRUCT* + args_type; + + args_type args; + + typedef unusable result_type; + + caller(args_type a) : args(a) {} + + template + unusable operator()(const Pair& slot) const + { + F* target = const_cast(any_cast(&slot.second.second)); + (*target)(BOOST_SIGNALS_BOUND_ARGS); + return unusable(); + } + }; + }; + } // namespace detail + } // namespace signals + + // The actual signalN class + template< + typename R, + BOOST_SIGNALS_TEMPLATE_PARMS + BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS + typename Combiner = last_value, + typename Group = int, + typename GroupCompare = std::less, + typename SlotFunction = BOOST_SIGNALS_FUNCTION< + R BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS + BOOST_SIGNALS_TEMPLATE_ARGS> + > + class BOOST_SIGNALS_SIGNAL : + public signals::detail::signal_base, // management of slot list + public signals::trackable // signals are trackable + { + public: + // The slot function type + typedef SlotFunction slot_function_type; + + // Result type of a slot + typedef typename signals::detail::slot_result_type::type + slot_result_type; + + // Argument types + BOOST_SIGNALS_ARG_TYPES + +#if BOOST_SIGNALS_NUM_ARGS == 1 + typedef T1 argument_type; +#elif BOOST_SIGNALS_NUM_ARGS == 2 + typedef T1 first_argument_type; + typedef T2 second_argument_type; +#endif + + private: + // The real slot name comparison object type + typedef signals::detail::any_bridge_compare + real_group_compare_type; + + // The function object passed to the slot call iterator that will call + // the underlying slot function with its arguments bound + typedef signals::detail::BOOST_SIGNALS_CALL_BOUND + outer_bound_slot_caller; + typedef typename outer_bound_slot_caller::template + caller + call_bound_slot; + + public: + // Combiner's result type + typedef typename Combiner::result_type result_type; + + // Combiner type + typedef Combiner combiner_type; + + // Slot type + typedef slot slot_type; + + // Slot name type and comparison + typedef Group group_type; + typedef GroupCompare group_compare_type; + + typedef typename signals::detail::slot_call_iterator_generator< + call_bound_slot, + slot_iterator>::type slot_call_iterator; + + explicit + BOOST_SIGNALS_SIGNAL(const Combiner& c = Combiner(), + const GroupCompare& comp = GroupCompare()) : + signals::detail::signal_base(real_group_compare_type(comp)), + combiner(c) + { + } + + // Connect a slot to this signal + signals::connection connect(const slot_type&); + signals::connection connect(const group_type&, const slot_type&); + + // Disconnect a named slot + void disconnect(const group_type& group) + { + impl->disconnect(group); + } + + // Emit the signal + result_type operator()(BOOST_SIGNALS_PARMS); + result_type operator()(BOOST_SIGNALS_PARMS) const; + + private: + Combiner combiner; + }; + + template< + typename R, + BOOST_SIGNALS_TEMPLATE_PARMS + BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS + typename Combiner, + typename Group, + typename GroupCompare, + typename SlotFunction + > + signals::connection + BOOST_SIGNALS_SIGNAL< + R, BOOST_SIGNALS_TEMPLATE_ARGS + BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS + Combiner, Group, GroupCompare, SlotFunction + >::connect(const slot_type& in_slot) + { + // If the slot has been disconnected, just return a disconnected + // connection + if (!in_slot.is_active()) { + return signals::connection(); + } + + return impl->connect_slot(in_slot.get_slot_function(), + any(), + in_slot.get_bound_objects()); + } + + template< + typename R, + BOOST_SIGNALS_TEMPLATE_PARMS + BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS + typename Combiner, + typename Group, + typename GroupCompare, + typename SlotFunction + > + signals::connection + BOOST_SIGNALS_SIGNAL< + R, BOOST_SIGNALS_TEMPLATE_ARGS + BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS + Combiner, Group, GroupCompare, SlotFunction + >::connect(const group_type& group, + const slot_type& in_slot) + { + return impl->connect_slot(in_slot.get_slot_function(), + group, + in_slot.get_bound_objects()); + } + + template< + typename R, + BOOST_SIGNALS_TEMPLATE_PARMS + BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS + typename Combiner, + typename Group, + typename GroupCompare, + typename SlotFunction + > + typename BOOST_SIGNALS_SIGNAL< + R, BOOST_SIGNALS_TEMPLATE_ARGS + BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS + Combiner, Group, GroupCompare, SlotFunction>::result_type + BOOST_SIGNALS_SIGNAL< + R, BOOST_SIGNALS_TEMPLATE_ARGS + BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS + Combiner, Group, GroupCompare, SlotFunction + >::operator()(BOOST_SIGNALS_PARMS) + { + // Notify the slot handling code that we are making a call + signals::detail::call_notification notification(this->impl); + + // Construct a function object that will call the underlying slots + // with the given arguments. +#if BOOST_SIGNALS_NUM_ARGS == 0 + BOOST_SIGNALS_ARGS_STRUCT_INST args; +#else + BOOST_SIGNALS_ARGS_STRUCT_INST args(BOOST_SIGNALS_ARGS); +#endif // BOOST_SIGNALS_NUM_ARGS > 0 + call_bound_slot f(&args); + + // Let the combiner call the slots via a pair of input iterators + return combiner(signals::detail::make_slot_call_iterator( + notification.impl->slots.begin(), impl->slots.end(), f), + signals::detail::make_slot_call_iterator( + notification.impl->slots.end(), impl->slots.end(), f)); + } + + template< + typename R, + BOOST_SIGNALS_TEMPLATE_PARMS + BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS + typename Combiner, + typename Group, + typename GroupCompare, + typename SlotFunction + > + typename BOOST_SIGNALS_SIGNAL< + R, BOOST_SIGNALS_TEMPLATE_ARGS + BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS + Combiner, Group, GroupCompare, SlotFunction>::result_type + BOOST_SIGNALS_SIGNAL< + R, BOOST_SIGNALS_TEMPLATE_ARGS + BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS + Combiner, Group, GroupCompare, SlotFunction + >::operator()(BOOST_SIGNALS_PARMS) const + { + // Notify the slot handling code that we are making a call + signals::detail::call_notification notification(this->impl); + + // Construct a function object that will call the underlying slots + // with the given arguments. +#if BOOST_SIGNALS_NUM_ARGS == 0 + BOOST_SIGNALS_ARGS_STRUCT_INST args; +#else + BOOST_SIGNALS_ARGS_STRUCT_INST args(BOOST_SIGNALS_ARGS); +#endif // BOOST_SIGNALS_NUM_ARGS > 0 + + call_bound_slot f(&args); + + // Let the combiner call the slots via a pair of input iterators + return combiner(signals::detail::make_slot_call_iterator( + notification.impl->slots.begin(), impl->slots.end(), f), + signals::detail::make_slot_call_iterator( + notification.impl->slots.end(), impl->slots.end(), f)); + } +} // namespace boost + +#undef BOOST_SIGNAL_FUNCTION_N_HEADER +#undef BOOST_SIGNALS_ARGS_STRUCT_INST +#undef BOOST_SIGNALS_CALL_BOUND +#undef BOOST_SIGNALS_ARGS_STRUCT +#undef BOOST_SIGNALS_FUNCTION +#undef BOOST_SIGNALS_SIGNAL +#undef BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS diff --git a/include/boost/signals/slot.hpp b/include/boost/signals/slot.hpp new file mode 100644 index 0000000..427e1a7 --- /dev/null +++ b/include/boost/signals/slot.hpp @@ -0,0 +1,132 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#ifndef BOOST_SIGNALS_SLOT_HEADER +#define BOOST_SIGNALS_SLOT_HEADER + +#include +#include +#include +#include +#include + +namespace boost { + namespace signals { + namespace detail { + class slot_base { + // We would have to enumerate all of the signalN classes here as + // friends to make this private (as it otherwise should be). We can't + // name all of them because we don't know how many there are. + public: + // Get the set of bound objects + std::vector& get_bound_objects() const + { return bound_objects; } + + // Determine if this slot is still "active", i.e., all of the bound + // objects still exist + bool is_active() const { return watch_bound_objects.connected(); } + + protected: + // Create a connection for this slot + void create_connection(); + + // Get the slot so that it can be copied + template + reference_wrapper + get_invocable_slot(const F& f, signals::detail::signal_tag) + { return reference_wrapper(f); } + + template + const F& get_invocable_slot(const F& f, signals::detail::reference_tag) + { return f; } + + template + const F& get_invocable_slot(const F& f, signals::detail::value_tag) + { return f; } + + // Get the slot so that it can be inspected for trackable objects + template + const F& get_inspectable_slot(const F& f, + signals::detail::signal_tag) + { return f; } + + template + const F& get_inspectable_slot(const F& f, + signals::detail::reference_tag) + { return f.get(); } + + template + const F& get_inspectable_slot(const F& f, signals::detail::value_tag) + { return f; } + + // Determines the type of the slot - is it a signal, a reference to a + // slot or just a normal slot. + template + typename signals::detail::get_slot_tag::type + tag_type(const F&) + { + typename signals::detail::get_slot_tag::type tag; + return tag; + } + + mutable std::vector bound_objects; + connection watch_bound_objects; + + private: + static void bound_object_destructed(void*, void*) {} + }; + } // end namespace detail + } // end namespace signals + + template + class slot : public signals::detail::slot_base { + public: + template + slot(const F& f) : slot_function(get_invocable_slot(f, tag_type(f))) + { + // Visit each of the bound objects and store them for later use + // An exception thrown here will allow the basic_connection to be + // destroyed when this goes out of scope, and no other connections + // have been made. + signals::detail::bound_objects_visitor do_bind(bound_objects); + visit_each(do_bind, get_inspectable_slot(f, tag_type(f))); + + create_connection(); + } + +#ifdef __BORLANDC__ + template + slot(F* f) : slot_function(f) + { + create_connection(); + } +#endif // __BORLANDC__ + + // We would have to enumerate all of the signalN classes here as friends + // to make this private (as it otherwise should be). We can't name all of + // them because we don't know how many there are. + public: + // Get the slot function to call the actual slot + const SlotFunction& get_slot_function() const { return slot_function; } + + private: + slot(); // no default constructor + slot& operator=(const slot&); // no assignment operator + + SlotFunction slot_function; + }; +} // end namespace boost + +#endif // BOOST_SIGNALS_SLOT_HEADER diff --git a/include/boost/signals/trackable.hpp b/include/boost/signals/trackable.hpp new file mode 100644 index 0000000..a6ed2df --- /dev/null +++ b/include/boost/signals/trackable.hpp @@ -0,0 +1,109 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#ifndef BOOST_SIGNALS_TRACKABLE_HPP +#define BOOST_SIGNALS_TRACKABLE_HPP + +#include +#include +#include +#include +#include + +namespace boost { + namespace signals { + // Base class for "trackable" objects that can be tracked when they are + // bound in slot target functions. When a trackable object is destroyed, + // the signal/slot connections are disconnected automatically. + class trackable { + private: + static void signal_disconnected(void* obj, void* data); + + friend class detail::signal_base_impl; + friend class detail::slot_base; + void signal_connected(connection, signals::detail::bound_object&) const; + + protected: + trackable() : connected_signals(), dying(false) {} + trackable(const trackable&) : connected_signals(), dying(false) {} + ~trackable(); + + trackable& operator=(const trackable&) + { + connected_signals.clear(); + return *this; + } + + private: + typedef std::list connection_list; + typedef connection_list::iterator connection_iterator; + + // List of connections that this object is part of + mutable connection_list connected_signals; + + // True when the object is being destroyed + mutable bool dying; + }; + + namespace detail { + template struct truth {}; + + // A visitor that adds each trackable object to a vector + class bound_objects_visitor { + public: + bound_objects_visitor(std::vector& v) : + bound_objects(v) + { + } + + template + inline void operator()(const T& t) const + { + visit(t, truth::value>()); + visit(&t, truth::value>()); + } + + template + inline void operator()(const boost::reference_wrapper& r) const + { + (*this)(r.get()); + } + + inline void operator()(const trackable* b) const + { + if (b) { + bound_objects.push_back(b); + } + } + + private: + template + inline void visit(const T&, truth) const + { + } + + template + inline void visit(const T& t, truth) const + { + (*this)(static_cast(t)); + } + + mutable std::vector& bound_objects; + }; + } // end namespace detail + } // end namespace signals +} + +#endif // BOOST_SIGNALS_TRACKABLE_HPP diff --git a/src/connection.cpp b/src/connection.cpp new file mode 100644 index 0000000..c1233e8 --- /dev/null +++ b/src/connection.cpp @@ -0,0 +1,53 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#include +#include + +namespace boost { + namespace signals { + void connection::disconnect() const + { + if (this->connected()) { + // Make sure we have a reference to the basic_connection object, + // because 'this' may disappear + shared_ptr local_con = con; + + void (*signal_disconnect)(void*, void*) = local_con->signal_disconnect; + + // Note that this connection no longer exists + // Order is important here: we could get into an infinite loop if this + // isn't cleared before we try the disconnect. + local_con->signal_disconnect = 0; + + // Disconnect signal + signal_disconnect(local_con->signal, local_con->signal_data); + + // Disconnect all bound objects + typedef std::list::iterator iterator; + for (iterator i = local_con->bound_objects.begin(); + i != local_con->bound_objects.end(); ++i) { + assert(i->disconnect); + i->disconnect(i->obj, i->data); + } + } + } + } // end namespace boost +} // end namespace boost + +#ifndef BOOST_MSVC +// Explicit instantiations to keep everything in the library +template class std::list; +#endif diff --git a/src/signal_base.cpp b/src/signal_base.cpp new file mode 100644 index 0000000..fe83912 --- /dev/null +++ b/src/signal_base.cpp @@ -0,0 +1,236 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#include +#include + +namespace boost { + namespace signals { + namespace detail { + signal_base_impl::signal_base_impl(const compare_type& comp) : + call_depth(0), + slots(comp) + { + flags.delayed_disconnect = false; + flags.clearing = false; + } + + signal_base_impl::~signal_base_impl() + { + // Set the "clearing" flag to ignore extraneous disconnect requests, + // because all slots will be disconnected on destruction anyway. + flags.clearing = true; + } + + void signal_base_impl::disconnect_all_slots() + { + // Do nothing if we're already clearing the slot list + if (flags.clearing) + return; + + if (call_depth == 0) { + // Clearing the slot list will disconnect all slots automatically + temporarily_set_clearing set_clearing(this); + slots.clear(); + } + else { + // We can't actually remove elements from the slot list because there + // are still iterators into the slot list that must not be + // invalidated by this operation. So just disconnect each slot + // without removing it from the slot list. When the call depth does + // reach zero, the call list will be cleared. + flags.delayed_disconnect = true; + temporarily_set_clearing set_clearing(this); + for (slot_iterator i = slots.begin(); i != slots.end(); ++i) { + i->second.first.disconnect(); + } + } + } + + connection + signal_base_impl:: + connect_slot(const any& slot, + const any& name, + const std::vector& bound_objects) + { + // Allocate storage for a new basic_connection object to represent the + // connection + basic_connection* con = new basic_connection(); + + // Create a new connection handle object and place the basic_connection + // object we just created under its control. Note that the "reset" + // routine will delete con if allocation throws. + connection slot_connection; + slot_connection.reset(con); + + // Allocate storage for an iterator that will hold the point of + // insertion of the slot into the list. This is used to later remove + // the slot when it is disconnected. + std::auto_ptr saved_iter(new slot_iterator()); + + // Add the slot to the list. + + slot_iterator pos = + slots.insert(stored_slot_type(name, + connection_slot_pair(slot_connection, + slot))); + + // Make the copy of the connection in the list disconnect when it is + // destroyed + pos->second.first.set_controlling(); + + // The assignment operation here absolutely must not throw, which + // intuitively makes sense (because any container's insert method + // becomes impossible to use in an exception-safe manner without this + // assumption), but doesn't appear to be mentioned in the standard. + *saved_iter = pos; + + // Fill out the connection object appropriately. None of these + // operations can throw + con->signal = this; + con->signal_data = saved_iter.release(); + con->signal_disconnect = &signal_base_impl::slot_disconnected; + + // If an exception is thrown the connection will automatically be + // disconnected. + scoped_connection safe_connection = slot_connection; + + // Connect each of the bound objects + for(std::vector::const_iterator i = + bound_objects.begin(); + i != bound_objects.end(); + ++i) { + // Notify the object that the signal is connecting to it by passing + // it a copy of the connection. If the connection + // should throw, the scoped connection safe_connection will + // disconnect the connection completely. + bound_object binding; + (*i)->signal_connected(slot_connection, binding); + + // This will notify the bound object that the connection just made + // should be disconnected if an exception is thrown before the + // end of this iteration + auto_disconnect_bound_object disconnector(binding); + + // Add the binding to the list of bindings for the connection. + con->bound_objects.push_back(binding); + + // The connection object now knows about the bound object, so if an + // exception is thrown later the connection object will notify the + // bound object of the disconnection automatically + disconnector.release(); + } + + // No exceptions will be thrown past this point, and we must not + // disconnect the connection now + safe_connection.release(); + + return slot_connection; + } + + bool signal_base_impl::empty() const + { + // Disconnected slots may still be in the list of slots if + // a) this is called while slots are being invoked (call_depth > 0) + // b) an exception was thrown in remove_disconnected_slots + for (slot_iterator i = slots.begin(); i != slots.end(); ++i) { + if (i->second.first.connected()) + return false; + } + + return true; + } + + void signal_base_impl::disconnect(const any& group) + { + std::pair group_slots = + slots.equal_range(group); + while (group_slots.first != group_slots.second) { + slot_iterator next = group_slots.first; + ++next; + + group_slots.first->second.first.disconnect(); + group_slots.first = next; + } + } + + void signal_base_impl::slot_disconnected(void* obj, void* data) + { + signal_base_impl* self = reinterpret_cast(obj); + + // We won't need the slot iterator after this + std::auto_ptr slot( + reinterpret_cast(data)); + + // If we're flags.clearing, we don't bother updating the list of slots + if (!self->flags.clearing) { + // If we're in a call, note the fact that a slot has been deleted so + // we can come back later to remove the iterator + if (self->call_depth > 0) { + self->flags.delayed_disconnect = true; + } + else { + // Just remove the slot now, it's safe + self->slots.erase(*slot); + } + } + } + + void signal_base_impl::remove_disconnected_slots() const + { + // Remove any disconnected slots + for (slot_iterator i = slots.begin(); i != slots.end(); /* none */) { + if (!i->second.first.connected()) + slots.erase(i++); + else + ++i; + } + } + + call_notification:: + call_notification(const shared_ptr& b) : + impl(b) + { + // A call will be made, so increment the call depth as a notification + impl->call_depth++; + } + + call_notification::~call_notification() + { + impl->call_depth--; + + // If the call depth is zero and we have some slots that have been + // disconnected during the calls, remove those slots from the list + if (impl->call_depth == 0 && + impl->flags.delayed_disconnect) { + impl->remove_disconnected_slots(); + impl->flags.delayed_disconnect = false; + } + } + + signal_base::~signal_base() + { + } + } // namespace detail + } // namespace signals +} // namespace boost + +#ifndef BOOST_MSVC +// Explicit instantiations to keep in the library +template class boost::function2; +template class std::multimap >; +#endif diff --git a/src/slot.cpp b/src/slot.cpp new file mode 100644 index 0000000..b76297d --- /dev/null +++ b/src/slot.cpp @@ -0,0 +1,70 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#include + +namespace boost { + namespace signals { + namespace detail { + void slot_base::create_connection() + { + // Create a new connection object + basic_connection* con = new basic_connection(); + + /* nothrow */ { + // The signal portion isn't really necessary, except that we need a + // signal for the connection to be connected. + con->signal = static_cast(this); + con->signal_data = 0; + con->signal_disconnect = &bound_object_destructed; + } + + // This connection watches for destruction of bound objects. Note + // that the reset routine will delete con if an allocation throws + watch_bound_objects.reset(con); + + // We create a scoped connection, so that exceptions thrown while + // adding bound objects will cause a cleanup of the bound objects + // already connected. + scoped_connection safe_connection(watch_bound_objects); + + // Now notify each of the bound objects that they are connected to this + // slot. + for(std::vector::iterator i = bound_objects.begin(); + i != bound_objects.end(); ++i) { + // Notify the object that the slot is connecting to it + signals::detail::bound_object binding; + (*i)->signal_connected(watch_bound_objects, binding); + + // This will notify the bound object that the connection just made + // should be disconnected if an exception is thrown before the + // end of this iteration + signals::detail::auto_disconnect_bound_object disconnector(binding); + + // Add the binding to the list of bindings for the connection + con->bound_objects.push_back(binding); + + // The connection object now knows about the bound object, so if an + // exception is thrown later the connection object will notify the + // bound object of the disconnection automatically + disconnector.release(); + } + + // No exceptions will be thrown past this point. + safe_connection.release(); + } + } // end namespace detail + } // end namespace signals +} // end namespace boost diff --git a/src/trackable.cpp b/src/trackable.cpp new file mode 100644 index 0000000..f33bdeb --- /dev/null +++ b/src/trackable.cpp @@ -0,0 +1,63 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#include +#include + +namespace boost { + namespace signals { + void trackable::signal_disconnected(void* obj, void* data) + { + trackable* self = reinterpret_cast(obj); + connection_iterator* signal = + reinterpret_cast(data); + + // If we're dying, don't bother erasing the connection from the list; + // it'll be gone anyway + if (!self->dying) { + self->connected_signals.erase(*signal); + } + + // This iterator pointer won't ever be used again + delete signal; + } + + void + trackable::signal_connected(connection c, + signals::detail::bound_object& binding) const + { + // Insert the connection + connection_iterator pos = + connected_signals.insert(connected_signals.end(), c); + + // Make this copy of the object disconnect when destroyed + pos->set_controlling(); + + binding.obj = const_cast(reinterpret_cast(this)); + binding.data = reinterpret_cast(new connection_iterator(pos)); + binding.disconnect = &signal_disconnected; + } + + trackable::~trackable() + { + dying = true; + } + } // end namespace signals +} + +#ifndef BOOST_MSVC +// Explicit instantiations to keep in the library +template class std::list; +#endif diff --git a/test/Jamfile b/test/Jamfile new file mode 100644 index 0000000..64034d6 --- /dev/null +++ b/test/Jamfile @@ -0,0 +1,37 @@ +subproject libs/signals/test ; + +exe signal_n_test : ../build/boost_signals + signal_n_test.cpp + : $(BOOST_ROOT) + ; + +exe signal_test : ../build/boost_signals + signal_test.cpp + : $(BOOST_ROOT) + ; + + +exe deletion_test : ../build/boost_signals + deletion_test.cpp + : $(BOOST_ROOT) + ; + +exe trackable_test : ../build/boost_signals + trackable_test.cpp + : $(BOOST_ROOT) + ; + +exe random_signal_system : ../build/boost_signals + random_signal_system.cpp + : $(BOOST_ROOT) + ; + +exe dead_slot_test : ../build/boost_signals + dead_slot_test.cpp + : $(BOOST_ROOT) + ; + +exe ordering_test : ../build/boost_signals + ordering_test.cpp + : $(BOOST_ROOT) + ; diff --git a/test/dead_slot_test.cpp b/test/dead_slot_test.cpp new file mode 100644 index 0000000..a609976 --- /dev/null +++ b/test/dead_slot_test.cpp @@ -0,0 +1,52 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#define BOOST_INCLUDE_MAIN +#include +#include +#include + +typedef boost::signal1 sig_type; + +class with_constant : public boost::signals::trackable { +public: + with_constant(int c) : constant(c) {} + + int add(int i) { return i + constant; } + +private: + int constant; +}; + +void do_delayed_connect(with_constant* wc, + sig_type& sig, + sig_type::slot_type slot) +{ + // Should invalidate the slot, so that we cannot connect to it + delete wc; + + boost::signals::connection c = sig.connect(slot); + BOOST_TEST(!c.connected()); +} + +int test_main(int, char*[]) +{ + sig_type s1; + with_constant* wc1 = new with_constant(7); + + do_delayed_connect(wc1, s1, boost::bind(&with_constant::add, wc1, _1)); + + return 0; +} diff --git a/test/deletion_test.cpp b/test/deletion_test.cpp new file mode 100644 index 0000000..3b71ccd --- /dev/null +++ b/test/deletion_test.cpp @@ -0,0 +1,225 @@ +// Boost.Signals library +// +// Copyright (C) 2001-2 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#define BOOST_INCLUDE_MAIN +#include +#include +#include +#include + +static boost::signals::connection connections[5]; + +static std::string test_output; + +struct remove_connection { + explicit remove_connection(int v = 0, int i = -1) : value(v), idx(i) {} + + void operator()() const { + if (idx >= 0) + connections[idx].disconnect(); + + //return value; + std::cout << value << " "; + + // test_output.push_back(static_cast(value + '0')); + test_output.insert(test_output.end(), value + '0'); + } + + int value; + int idx; +}; + +static void +test_remove_self() +{ + boost::signal0 s0; + + connections[0] = s0.connect(remove_connection(0)); + connections[1] = s0.connect(remove_connection(1)); + connections[2] = s0.connect(remove_connection(2, 2)); + connections[3] = s0.connect(remove_connection(3)); + + std::cout << "Deleting 2" << std::endl; + + test_output = ""; + s0(); std::cout << std::endl; + BOOST_TEST(test_output == "0123"); + + test_output = ""; + s0(); std::cout << std::endl; + BOOST_TEST(test_output == "013"); + + s0.disconnect_all_slots(); + BOOST_TEST(s0.empty()); + + connections[0] = s0.connect(remove_connection(0)); + connections[1] = s0.connect(remove_connection(1)); + connections[2] = s0.connect(remove_connection(2)); + connections[3] = s0.connect(remove_connection(3, 3)); + + std::cout << "Deleting 3" << std::endl; + + test_output = ""; + s0(); std::cout << std::endl; + BOOST_TEST(test_output == "0123"); + + test_output = ""; + s0(); std::cout << std::endl; + BOOST_TEST(test_output == "012"); + + s0.disconnect_all_slots(); + BOOST_TEST(s0.empty()); + + connections[0] = s0.connect(remove_connection(0, 0)); + connections[1] = s0.connect(remove_connection(1)); + connections[2] = s0.connect(remove_connection(2)); + connections[3] = s0.connect(remove_connection(3)); + + std::cout << "Deleting 0" << std::endl; + + test_output = ""; + s0(); std::cout << std::endl; + BOOST_TEST(test_output == "0123"); + + test_output = ""; + s0(); std::cout << std::endl; + BOOST_TEST(test_output == "123"); + + s0.disconnect_all_slots(); + BOOST_TEST(s0.empty()); + + connections[0] = s0.connect(remove_connection(0, 0)); + connections[1] = s0.connect(remove_connection(1, 1)); + connections[2] = s0.connect(remove_connection(2, 2)); + connections[3] = s0.connect(remove_connection(3, 3)); + + std::cout << "Mass suicide" << std::endl; + + test_output = ""; + s0(); std::cout << std::endl; + BOOST_TEST(test_output == "0123"); + + test_output = ""; + s0(); std::cout << std::endl; + BOOST_TEST(test_output == ""); +} + +static void +test_remove_prior() +{ + boost::signal0 s0; + + connections[0] = s0.connect(remove_connection(0)); + connections[1] = s0.connect(remove_connection(1, 0)); + connections[2] = s0.connect(remove_connection(2)); + connections[3] = s0.connect(remove_connection(3)); + + std::cout << "1 removes 0" << std::endl; + + test_output = ""; + s0(); std::cout << std::endl; + BOOST_TEST(test_output == "0123"); + + test_output = ""; + s0(); std::cout << std::endl; + BOOST_TEST(test_output == "123"); + + s0.disconnect_all_slots(); + BOOST_TEST(s0.empty()); + + connections[0] = s0.connect(remove_connection(0)); + connections[1] = s0.connect(remove_connection(1)); + connections[2] = s0.connect(remove_connection(2)); + connections[3] = s0.connect(remove_connection(3, 2)); + + std::cout << "3 removes 2" << std::endl; + + test_output = ""; + s0(); std::cout << std::endl; + BOOST_TEST(test_output == "0123"); + + test_output = ""; + s0(); std::cout << std::endl; + BOOST_TEST(test_output == "013"); +} + +static void +test_remove_after() +{ + boost::signal0 s0; + + connections[0] = s0.connect(remove_connection(0, 1)); + connections[1] = s0.connect(remove_connection(1)); + connections[2] = s0.connect(remove_connection(2)); + connections[3] = s0.connect(remove_connection(3)); + + std::cout << "0 removes 1" << std::endl; + + test_output = ""; + s0(); std::cout << std::endl; + BOOST_TEST(test_output == "023"); + + test_output = ""; + s0(); std::cout << std::endl; + BOOST_TEST(test_output == "023"); + + s0.disconnect_all_slots(); + BOOST_TEST(s0.empty()); + + connections[0] = s0.connect(remove_connection(0)); + connections[1] = s0.connect(remove_connection(1, 3)); + connections[2] = s0.connect(remove_connection(2)); + connections[3] = s0.connect(remove_connection(3)); + + std::cout << "1 removes 3" << std::endl; + + test_output = ""; + s0(); std::cout << std::endl; + BOOST_TEST(test_output == "012"); + + test_output = ""; + s0(); std::cout << std::endl; + BOOST_TEST(test_output == "012"); +} + +static void +test_bloodbath() +{ + boost::signal0 s0; + + connections[0] = s0.connect(remove_connection(0, 1)); + connections[1] = s0.connect(remove_connection(1, 1)); + connections[2] = s0.connect(remove_connection(2, 0)); + connections[3] = s0.connect(remove_connection(3, 2)); + + std::cout << "0 removes 1, 2 removes 0, 3 removes 2" << std::endl; + + test_output = ""; + s0(); std::cout << std::endl; + BOOST_TEST(test_output == "023"); + + test_output = ""; + s0(); std::cout << std::endl; + BOOST_TEST(test_output == "3"); +} + +int test_main(int, char* []) +{ + test_remove_self(); + test_remove_prior(); + test_remove_after(); + test_bloodbath(); + return 0; +} diff --git a/test/ordering_test.cpp b/test/ordering_test.cpp new file mode 100644 index 0000000..2f59eab --- /dev/null +++ b/test/ordering_test.cpp @@ -0,0 +1,98 @@ +// Boost.Signals library +// +// Copyright (C) 2002 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#define BOOST_INCLUDE_MAIN +#include +#include +#include +#include +#include +#include + +std::vector valuesOutput; +bool ungrouped1 = false; +bool ungrouped2 = false; +bool ungrouped3 = false; + +struct emit_int { + emit_int(int v) : value(v) {} + + void operator()() const + { + BOOST_TEST(!ungrouped1 && !ungrouped2 && !ungrouped3); + valuesOutput.push_back(value); + std::cout << value << ' '; + } + +private: + int value; +}; + +struct write_ungrouped1 { + void operator()() const + { + BOOST_TEST(!ungrouped1); + ungrouped1 = true; + std::cout << "(Ungrouped #1)" << ' '; + } +}; + +struct write_ungrouped2 { + void operator()() const + { + BOOST_TEST(!ungrouped2); + ungrouped2 = true; + std::cout << "(Ungrouped #2)" << ' '; + } +}; + +struct write_ungrouped3 { + void operator()() const + { + BOOST_TEST(!ungrouped3); + ungrouped3 = true; + std::cout << "(Ungrouped #3)" << ' '; + } +}; + +int test_main(int, char* []) +{ + std::vector sortedValues; + + boost::signal0 sig; + sig.connect(write_ungrouped1()); + for (int i = 0; i < 100; ++i) { + using namespace std; + int v = random() % 100; + sortedValues.push_back(v); + sig.connect(v, emit_int(v)); + + if (i == 50) { + sig.connect(write_ungrouped2()); + } + } + sig.connect(write_ungrouped3()); + + std::sort(sortedValues.begin(), sortedValues.end()); + + sig(); + std::cout << std::endl; + + BOOST_TEST(valuesOutput == sortedValues); + BOOST_TEST(ungrouped1); + BOOST_TEST(ungrouped2); + BOOST_TEST(ungrouped3); + return 0; +} diff --git a/test/random_signal_system.cpp b/test/random_signal_system.cpp new file mode 100644 index 0000000..f31ffef --- /dev/null +++ b/test/random_signal_system.cpp @@ -0,0 +1,413 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace boost; +using namespace boost::signals; + +struct signal_tag { + typedef vertex_property_tag kind; +}; + +struct connection_tag { + typedef edge_property_tag kind; +}; + +typedef signal signal_type; +typedef adjacency_list >, + // Edge properties + property > > + signal_graph_type; +typedef signal_graph_type::vertex_descriptor vertex_descriptor; +typedef signal_graph_type::edge_descriptor edge_descriptor; + +// The signal graph +static signal_graph_type signal_graph; + +// Mapping from a signal to its associated vertex descriptor +static std::map signal_to_descriptor; + +// Mapping from a connection to its associated edge descriptor +static std::map connection_to_descriptor; + +std::map min_signal_propagate_distance; + +void remove_disconnected_connections() +{ + // remove disconnected connections + std::map::iterator i = + connection_to_descriptor.begin(); + while (i != connection_to_descriptor.end()) { + if (!i->first.connected()) { + connection_to_descriptor.erase(i++); + } + else { + ++i; + } + } +} + +void remove_signal(signal_type* sig) +{ + clear_vertex(signal_to_descriptor[sig], signal_graph); + remove_vertex(signal_to_descriptor[sig], signal_graph); + delete sig; + signal_to_descriptor.erase(sig); + remove_disconnected_connections(); +} + +void random_remove_signal(minstd_rand& rand_gen); + +struct tracking_bridge { + tracking_bridge(signal_type* s, minstd_rand& rg) : sig(s), rand_gen(rg) {} + + void operator()(int cur_dist, int max_dist, double deletion_prob, + int& deletions_left) const + { + if (signal_to_descriptor.find(sig) == signal_to_descriptor.end()) + return; + + ++cur_dist; + + // Update the directed Bacon distance + if (min_signal_propagate_distance.find(sig) == + min_signal_propagate_distance.end()) { + min_signal_propagate_distance[sig] = cur_dist; + } + else if (cur_dist < min_signal_propagate_distance[sig]) { + min_signal_propagate_distance[sig] = cur_dist; + } + else if (deletion_prob == 0.0) { + // don't bother calling because we've already found a better route here + return; + } + + // Maybe delete the signal + if (uniform_01(rand_gen)() < deletion_prob && + deletions_left-- && signal_to_descriptor.size() > 1) { + random_remove_signal(rand_gen); + } + // propagate the signal + else if (cur_dist < max_dist) { + (*sig)(cur_dist, max_dist, deletion_prob, deletions_left); + } + } + + signal_type* sig; + minstd_rand& rand_gen; +}; + +namespace boost { + template + void visit_each(V& v, const tracking_bridge& t, int) + { + v(t); + v(t.sig); + } +} + +signal_type* add_signal() +{ + signal_type* sig = new signal_type(); + vertex_descriptor v = add_vertex(signal_graph); + signal_to_descriptor[sig] = v; + put(signal_tag(), signal_graph, v, sig); + + return sig; +} + +connection add_connection(signal_type* sig1, signal_type* sig2, + minstd_rand& rand_gen) +{ + std::cout << " Adding connection: " << sig1 << " -> " << sig2 << std::endl; + + connection c = sig1->connect(tracking_bridge(sig2, rand_gen)); + edge_descriptor e = + add_edge(signal_to_descriptor[sig1], signal_to_descriptor[sig2], + signal_graph).first; + connection_to_descriptor[c] = e; + put(connection_tag(), signal_graph, e, c); + put(edge_weight, signal_graph, e, 1); + return c; +} + +void remove_connection(connection c) +{ + signal_type* sig1 = get(signal_tag(), signal_graph, + source(connection_to_descriptor[c], signal_graph)); + signal_type* sig2 = get(signal_tag(), signal_graph, + target(connection_to_descriptor[c], signal_graph)); + std::cout << " Removing connection: " << sig1 << " -> " << sig2 + << std::endl; + c.disconnect(); + remove_edge(connection_to_descriptor[c], signal_graph); + connection_to_descriptor.erase(c); +} + +bool signal_connection_exists(signal_type* sig1, signal_type* sig2, + edge_descriptor& edge_desc) +{ + vertex_descriptor source_sig = signal_to_descriptor[sig1]; + vertex_descriptor target_sig = signal_to_descriptor[sig2]; + signal_graph_type::out_edge_iterator e; + for (e = out_edges(source_sig, signal_graph).first; + e != out_edges(source_sig, signal_graph).second; ++e) { + if (target(*e, signal_graph) == target_sig) { + edge_desc = *e; + return true; + } + } + return false; +} + +bool signal_connection_exists(signal_type* sig1, signal_type* sig2) +{ + edge_descriptor e; + return signal_connection_exists(sig1, sig2, e); +} + +std::map::iterator +choose_random_signal(minstd_rand& rand_gen) +{ + int signal_idx = uniform_int(rand_gen, + 0, signal_to_descriptor.size() - 1)(); + std::map::iterator result = + signal_to_descriptor.begin(); + for(; signal_idx; --signal_idx) + ++result; + + return result; +} + +void random_remove_signal(minstd_rand& rand_gen) +{ + std::map::iterator victim = + choose_random_signal(rand_gen); + std::cout << " Removing signal " << victim->first << std::endl; + remove_signal(victim->first); +} + +void random_add_connection(minstd_rand& rand_gen) +{ + std::map::iterator source; + std::map::iterator target; + do { + source = choose_random_signal(rand_gen); + target = choose_random_signal(rand_gen); + } while (signal_connection_exists(source->first, target->first)); + + add_connection(source->first, target->first, rand_gen); +} + +void random_remove_connection(minstd_rand& rand_gen) +{ + int victim_idx = + uniform_int(rand_gen, 0, num_edges(signal_graph)-1)(); + signal_graph_type::edge_iterator e = edges(signal_graph).first; + while (victim_idx--) { + ++e; + } + + remove_connection(get(connection_tag(), signal_graph, *e)); +} + +void random_bacon_test(minstd_rand& rand_gen) +{ + signal_type* kevin = choose_random_signal(rand_gen)->first; + min_signal_propagate_distance.clear(); + min_signal_propagate_distance[kevin] = 0; + + const int horizon = 10; // only go to depth 10 at most + + std::cout << " Bacon test: kevin is " << kevin + << "\n Propagating signal..."; + + // Propagate the signal out to the horizon + int deletions_left = 0; + (*kevin)(0, horizon, 0.0, deletions_left); + + std::cout << "OK\n Finding shortest paths..."; + + // Initialize all colors to white + { + unsigned int num = 0; + for (signal_graph_type::vertex_iterator v = vertices(signal_graph).first; + v != vertices(signal_graph).second; + ++v) { + // put(vertex_color, signal_graph, *v, white_color); + put(vertex_index, signal_graph, *v, num++); + } + + assert(num == num_vertices(signal_graph)); + } + + // Perform a breadth-first search starting at kevin, and record the + // distances from kevin to each reachable node. + std::map bacon_distance_map; + +#if 0 + bacon_distance_map[signal_to_descriptor[kevin]] = 0; + breadth_first_visit(signal_graph, signal_to_descriptor[kevin], + visitor( + make_bfs_visitor( + record_distances( + make_assoc_property_map(bacon_distance_map), + on_examine_edge()))). + color_map(get(vertex_color, signal_graph))); +#endif + + dijkstra_shortest_paths(signal_graph, signal_to_descriptor[kevin], + distance_map(make_assoc_property_map(bacon_distance_map))); + std::cout << "OK\n"; + // Make sure the bacon distances agree (prior to the horizon) + { + std::map::iterator i; + for (i = min_signal_propagate_distance.begin(); + i != min_signal_propagate_distance.end(); + ++i) { + if (i->second != bacon_distance_map[signal_to_descriptor[i->first]]) { + std::cout << "Signal distance to " << i->first << " was " + << i->second << std::endl; + std::cout << "Graph distance was " + << bacon_distance_map[signal_to_descriptor[i->first]] + << std::endl; + } + assert(i->second == bacon_distance_map[signal_to_descriptor[i->first]]); + } + } +} + +void randomly_create_connections(minstd_rand& rand_gen, double edge_probability) +{ + // Randomly create connections + uniform_01 random(rand_gen); + for (signal_graph_type::vertex_iterator v1 = vertices(signal_graph).first; + v1 != vertices(signal_graph).second; ++v1) { + for (signal_graph_type::vertex_iterator v2 = vertices(signal_graph).first; + v2 != vertices(signal_graph).second; ++v2) { + if (random() < edge_probability) { + add_connection(get(signal_tag(), signal_graph, *v1), + get(signal_tag(), signal_graph, *v2), + rand_gen); + } + } + } +} + +void random_recursive_deletion(minstd_rand& rand_gen) +{ + signal_type* kevin = choose_random_signal(rand_gen)->first; + min_signal_propagate_distance.clear(); + min_signal_propagate_distance[kevin] = 0; + + const int horizon = 4; // only go to depth "horizon" at most + + std::cout << " Recursive deletion test: start is " << kevin << std::endl; + + // Propagate the signal out to the horizon + int deletions_left = (int)(0.05*num_vertices(signal_graph)); + (*kevin)(0, horizon, 0.05, deletions_left); +} + +int main(int argc, char* argv[]) +{ + if (argc < 4) { + std::cerr << "Usage: random_signal_system <# of initial signals> " + << " " << std::endl; + return 1; + } + + int number_of_initial_signals = atoi(argv[1]); + double edge_probability = atof(argv[2]); + int iterations = atoi(argv[3]); + + int seed; + if (argc == 5) + seed = atoi(argv[4]); + else + seed = time(0); + + std::cout << "Number of initial signals: " << number_of_initial_signals + << std::endl; + std::cout << "Edge probability: " << edge_probability << std::endl; + std::cout << "Iterations: " << iterations << std::endl; + std::cout << "Seed: " << seed << std::endl; + + // Initialize random number generator + minstd_rand rand_gen; + rand_gen.seed(seed); + + for (int iter = 0; iter < iterations; ++iter) { + if (num_vertices(signal_graph) < 2) { + for (int i = 0; i < number_of_initial_signals; ++i) + add_signal(); + } + + while (num_edges(signal_graph) < 2) { + randomly_create_connections(rand_gen, edge_probability); + } + + std::cerr << "Iteration #" << (iter+1) << std::endl; + + uniform_int random_action(rand_gen, 0, 7); + switch (random_action()) { + case 0: + std::cout << " Adding new signal: " << add_signal() << std::endl; + break; + + case 1: + random_remove_signal(rand_gen); + break; + + case 2: + if (num_edges(signal_graph) < + num_vertices(signal_graph)*num_vertices(signal_graph)) { + random_add_connection(rand_gen); + } + break; + + case 3: + random_remove_connection(rand_gen); + break; + + case 4: + case 5: + case 6: + random_bacon_test(rand_gen); + break; + + case 7: + random_recursive_deletion(rand_gen); + break; + } + } + + return 0; +} diff --git a/test/signal_n_test.cpp b/test/signal_n_test.cpp new file mode 100644 index 0000000..59f1838 --- /dev/null +++ b/test/signal_n_test.cpp @@ -0,0 +1,164 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#define BOOST_INCLUDE_MAIN +#include +#include +#include + +template +struct max_or_default { + typedef T result_type; + template + typename InputIterator::value_type + operator()(InputIterator first, InputIterator last) const + { + if (first == last) + return T(); + + T max = *first++; + for (; first != last; ++first) + max = (*first > max)? *first : max; + + return max; + } +}; + +struct make_int { + make_int(int n, int cn) : N(n), CN(n) {} + int operator()() { return N; } + int operator()() const { return CN; } + + int N; + int CN; +}; + +template +struct make_increasing_int { + make_increasing_int() : n(N) {} + + int operator()() const { return n++; } + + mutable int n; +}; + +static void +test_zero_args() +{ + make_int i42(42, 41); + make_int i2(2, 1); + make_int i72(72, 71); + make_int i63(63, 63); + make_int i62(62, 61); + + { + boost::signal0, std::string> s0; + boost::signals::connection c2 = s0.connect(i2); + boost::signals::connection c72 = s0.connect("72", i72); + boost::signals::connection c62 = s0.connect("6x", i62); + boost::signals::connection c42 = s0.connect(i42); + + BOOST_TEST(s0() == 72); + + s0.disconnect("72"); + BOOST_TEST(s0() == 62); + + c72.disconnect(); // Double-disconnect should be safe + BOOST_TEST(s0() == 62); + + s0.disconnect("72"); // Triple-disconect should be safe + BOOST_TEST(s0() == 62); + + // Also connect 63 in the same group as 62 + s0.connect("6x", i63); + BOOST_TEST(s0() == 63); + + // Disconnect all of the 60's + s0.disconnect("6x"); + BOOST_TEST(s0() == 42); + + c42.disconnect(); + BOOST_TEST(s0() == 2); + + c2.disconnect(); + BOOST_TEST(s0() == 0); + } + + { + boost::signal0 > s0; + boost::signals::connection c2 = s0.connect(i2); + boost::signals::connection c72 = s0.connect(i72); + boost::signals::connection c62 = s0.connect(i62); + boost::signals::connection c42 = s0.connect(i42); + + const boost::signal0 >& cs0 = s0; + BOOST_TEST(cs0() == 72); + } + + { + make_increasing_int<7> i7; + make_increasing_int<10> i10; + + boost::signal0 > s0; + boost::signals::connection c7 = s0.connect(i7); + boost::signals::connection c10 = s0.connect(i10); + + BOOST_TEST(s0() == 10); + BOOST_TEST(s0() == 11); + } +} + +static void +test_one_arg() +{ + boost::signal1 > s1; + + s1.connect(std::negate()); + s1.connect(std::bind1st(std::multiplies(), 2)); + + BOOST_TEST(s1(1) == 2); + BOOST_TEST(s1(-1) == 1); +} + +static void +test_signal_signal_connect() +{ + boost::signal1 > s1; + + s1.connect(std::negate()); + + BOOST_TEST(s1(3) == -3); + + { + boost::signal1 > s2; + s1.connect(s2); + s2.connect(std::bind1st(std::multiplies(), 2)); + s2.connect(std::bind1st(std::multiplies(), -3)); + + BOOST_TEST(s2(-3) == 9); + BOOST_TEST(s1(3) == 6); + } // s2 goes out of scope and disconnects + + BOOST_TEST(s1(3) == -3); +} + +int +test_main(int, char* []) +{ + test_zero_args(); + test_one_arg(); + test_signal_signal_connect(); + return 0; +} diff --git a/test/signal_test.cpp b/test/signal_test.cpp new file mode 100644 index 0000000..03202ab --- /dev/null +++ b/test/signal_test.cpp @@ -0,0 +1,167 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#define BOOST_INCLUDE_MAIN +#include +#include +#include + +template +struct max_or_default { + typedef T result_type; + template + typename InputIterator::value_type + operator()(InputIterator first, InputIterator last) const + { + if (first == last) + return T(); + + T max = *first++; + for (; first != last; ++first) + max = (*first > max)? *first : max; + + return max; + } +}; + +struct make_int { + make_int(int n, int cn) : N(n), CN(cn) {} + + int operator()() { return N; } + int operator()() const { return CN; } + + int N; + int CN; +}; + +template +struct make_increasing_int { + make_increasing_int() : n(N) {} + + int operator()() const { return n++; } + + mutable int n; +}; + +static void +test_zero_args() +{ + make_int i42(42, 41); + make_int i2(2, 1); + make_int i72(72, 71); + make_int i63(63, 63); + make_int i62(62, 61); + + { + boost::signal::combiner >::type s0; + + std::cout << "sizeof(signal) = " << sizeof(s0) << std::endl; + boost::signals::connection c2 = s0.connect(i2); + boost::signals::connection c72 = s0.connect(72, i72); + boost::signals::connection c62 = s0.connect(60, i62); + boost::signals::connection c42 = s0.connect(i42); + + BOOST_TEST(s0() == 72); + + s0.disconnect(72); + BOOST_TEST(s0() == 62); + + c72.disconnect(); // Double-disconnect should be safe + BOOST_TEST(s0() == 62); + + s0.disconnect(72); // Triple-disconect should be safe + BOOST_TEST(s0() == 62); + + // Also connect 63 in the same group as 62 + s0.connect(60, i63); + BOOST_TEST(s0() == 63); + + // Disconnect all of the 60's + s0.disconnect(60); + BOOST_TEST(s0() == 42); + + c42.disconnect(); + BOOST_TEST(s0() == 2); + + c2.disconnect(); + BOOST_TEST(s0() == 0); + } + + { + boost::signal::combiner >::type s0; + boost::signals::connection c2 = s0.connect(i2); + boost::signals::connection c72 = s0.connect(i72); + boost::signals::connection c62 = s0.connect(i62); + boost::signals::connection c42 = s0.connect(i42); + + const boost::signal::combiner >::type& cs0 = s0; + BOOST_TEST(cs0() == 72); + } + + { + make_increasing_int<7> i7; + make_increasing_int<10> i10; + + boost::signal::combiner >::type s0; + boost::signals::connection c7 = s0.connect(i7); + boost::signals::connection c10 = s0.connect(i10); + + BOOST_TEST(s0() == 10); + BOOST_TEST(s0() == 11); + } +} + +static void +test_one_arg() +{ + boost::signal::combiner >::type s1; + + s1.connect(std::negate()); + s1.connect(std::bind1st(std::multiplies(), 2)); + + BOOST_TEST(s1(1) == 2); + BOOST_TEST(s1(-1) == 1); +} + +static void +test_signal_signal_connect() +{ + boost::signal::combiner >::type s1; + + s1.connect(std::negate()); + + BOOST_TEST(s1(3) == -3); + + { + boost::signal::combiner >::type s2; + s1.connect(s2); + s2.connect(std::bind1st(std::multiplies(), 2)); + s2.connect(std::bind1st(std::multiplies(), -3)); + + BOOST_TEST(s2(-3) == 9); + BOOST_TEST(s1(3) == 6); + } // s2 goes out of scope and disconnects + + BOOST_TEST(s1(3) == -3); +} + +int +test_main(int, char* []) +{ + test_zero_args(); + test_one_arg(); + test_signal_signal_connect(); + return 0; +} diff --git a/test/trackable_test.cpp b/test/trackable_test.cpp new file mode 100644 index 0000000..8a522ad --- /dev/null +++ b/test/trackable_test.cpp @@ -0,0 +1,72 @@ +// Boost.Signals library +// +// Copyright (C) 2001 Doug Gregor (gregod@cs.rpi.edu) +// +// Permission to copy, use, sell and distribute this software is granted +// provided this copyright notice appears in all copies. +// Permission to modify the code and to distribute modified code is granted +// provided this copyright notice appears in all copies, and a notice +// that the code was modified is included with the copyright notice. +// +// This software is provided "as is" without express or implied warranty, +// and with no claim as to its suitability for any purpose. + +// For more information, see http://www.boost.org + +#define BOOST_INCLUDE_MAIN +#include +#include +#include + +struct short_lived : public boost::signals::trackable { + ~short_lived() {} +}; + +struct swallow { + template int operator()(const T*, int i) { return i; } +}; + +template +struct max_or_default { + typedef T result_type; + + template + T operator()(InputIterator first, InputIterator last) const + { + if (first == last) + return T(); + + T max = *first++; + for (; first != last; ++first) + max = (*first > max)? *first : max; + + return max; + } +}; + +int test_main(int, char**) +{ + typedef boost::signal1 > sig_type; + sig_type s1; + + // Test auto-disconnection + BOOST_TEST(s1(5) == 0); + { + short_lived shorty; + s1.connect(boost::bind(swallow(), &shorty, _1)); + BOOST_TEST(s1(5) == 5); + } + BOOST_TEST(s1(5) == 0); + + // Test auto-disconnection of slot before signal connection + { + short_lived* shorty = new short_lived(); + + sig_type::slot_type slot(boost::bind(swallow(), shorty, _1)); + delete shorty; + + BOOST_TEST(s1(5) == 0); + } + + return 0; +}