]> git.lyx.org Git - lyx.git/commitdiff
Use new signal library `nod` instead of `boost::signals2`
authorYuriy Skalko <yuriy.skalko@gmail.com>
Sun, 13 Dec 2020 21:06:05 +0000 (23:06 +0200)
committerYuriy Skalko <yuriy.skalko@gmail.com>
Sun, 13 Dec 2020 21:11:19 +0000 (23:11 +0200)
Thanks Enrico for updating autotools files.

21 files changed:
3rdparty/Makefile.am
3rdparty/nod/Makefile.am [new file with mode: 0644]
3rdparty/nod/README.md [new file with mode: 0644]
3rdparty/nod/nod.hpp [new file with mode: 0644]
CMakeLists.txt
config/lyxinclude.m4
configure.ac
src/Makefile.am
src/Server.cpp
src/client/Makefile.am
src/frontends/Makefile.am
src/frontends/qt/Makefile.am
src/graphics/GraphicsConverter.cpp
src/graphics/GraphicsConverter.h
src/graphics/GraphicsLoader.cpp
src/graphics/PreviewLoader.cpp
src/graphics/PreviewLoader.h
src/support/ForkedCalls.cpp
src/support/Makefile.am
src/support/signals.h
src/tex2lyx/Makefile.am

index ec12adb8831245140c00011b9256aa66621437f5..7b5c569f3230d6f87bc16e9941909f64809a593d 100644 (file)
@@ -1,6 +1,10 @@
 include $(top_srcdir)/config/common.am
 
-DIST_SUBDIRS = boost dtl hunspell mythes libiconv zlib
+DIST_SUBDIRS = boost dtl hunspell mythes libiconv zlib nod
+
+if USE_INCLUDED_NOD
+NOD = nod
+endif
 
 if USE_INCLUDED_BOOST
 BOOST = boost
@@ -26,7 +30,7 @@ if BUILD_INCLUDED_DTL
 DTL=dtl
 endif
 
-SUBDIRS = $(BOOST) $(DTL) $(HUNSPELL) $(MYTHES) $(ICONV) $(ZLIB)
+SUBDIRS = $(BOOST) $(DTL) $(HUNSPELL) $(MYTHES) $(ICONV) $(ZLIB) $(NOD)
 
 EXTRA_DIST = \
        scripts/evince_sync/evince_backward_search \
diff --git a/3rdparty/nod/Makefile.am b/3rdparty/nod/Makefile.am
new file mode 100644 (file)
index 0000000..07caaa4
--- /dev/null
@@ -0,0 +1,4 @@
+include $(top_srcdir)/config/common.am
+
+EXTRA_DIST = nod \
+       README.md
diff --git a/3rdparty/nod/README.md b/3rdparty/nod/README.md
new file mode 100644 (file)
index 0000000..0cf74f1
--- /dev/null
@@ -0,0 +1,257 @@
+# Nod
+[![Build Status](https://travis-ci.org/fr00b0/nod.svg?branch=master)](https://travis-ci.org/fr00b0/nod)
+[![GitHub tag](https://img.shields.io/github/tag/fr00b0/nod.svg?label=version)](https://github.com/fr00b0/nod/releases)
+
+Dependency free, header only signals and slot library implemented with C++11.
+
+## Usage
+
+### Simple usage
+The following example creates a signal and then connects a lambda as a slot.
+
+```cpp
+// Create a signal which accepts slots with no arguments and void return value.
+nod::signal<void()> signal;
+// Connect a lambda slot that writes "Hello, World!" to stdout
+signal.connect([](){
+               std::cout << "Hello, World!" << std::endl;
+       });
+// Call the slots
+signal();
+```
+
+### Connecting multiple slots
+If multiple slots are connected to the same signal, all of the slots will be
+called when the signal is invoked. The slots will be called in the same order
+as they where connected.
+
+```cpp
+void endline() {
+       std::cout << std::endl;
+}
+
+// Create a signal
+nod::signal<void()> signal;
+// Connect a lambda that prints a message
+signal.connect([](){
+               std::cout << "Message without endline!";
+       });
+// Connect a function that prints a endline
+signal.connect(endline);
+
+// Call the slots
+signal();
+```
+
+#### Slot type
+The signal types in the library support connection of the same types that is
+supported by `std::function<T>`.
+
+### Slot arguments
+When a signal calls it's connected slots, any arguments passed to the signal
+are propagated to the slots. To make this work, we do need to specify the 
+signature of the signal to accept the arguments.
+
+```cpp
+void print_sum( int x, int y ) {
+       std::cout << x << "+" << y << "=" << (x+y) << std::endl;
+}
+void print_product( int x, int y ) {
+       std::cout << x << "*" << y << "=" << (x*y) << std::endl;
+}
+
+
+// We create a signal with two integer arguments.
+nod::signal<void(int,int)> signal;
+// Let's connect our slot
+signal.connect( print_sum );
+signal.connect( print_product );
+
+// Call the slots
+signal(10, 15);
+signal(-5, 7); 
+
+```
+
+### Disconnecting slots
+There are many circumstances where the programmer needs to diconnect a slot that
+no longer want to recieve events from the signal. This can be really important
+if the lifetime of the slots are shorter than the lifetime of the signal. That
+could cause the signal to call slots that have been destroyed but not
+disconnected, leading to undefined behaviour and probably segmentation faults.
+
+When a slot is connected, the return value from the  `connect` method returns
+an instance of the class `nod::connection`, that can be used to disconnect
+that slot.
+
+```cpp
+// Let's create a signal
+nod::signal<void()> signal;
+// Connect a slot, and save the connection
+nod::connection connection = signal.connect([](){
+                                                                std::cout << "I'm connected!" << std::endl;
+                                                        });
+// Triggering the signal will call the slot
+signal();
+// Now we disconnect the slot
+connection.disconnect();
+// Triggering the signal will no longer call the slot
+signal();
+```    
+
+### Scoped connections
+To assist in disconnecting slots, one can use the class `nod::scoped_connection`
+to capture a slot connection. A scoped connection will automatically disconnect
+the slot when the connection object goes out of scope.
+
+```cpp
+// We create a signal
+nod::signal<void()> signal;
+// Let's use a scope to control lifetime
+{ 
+       // Let's save the connection in a scoped_connection
+       nod::scoped_connection connection =
+               signal.connect([](){
+                       std::cout << "This message should only be emitted once!" << std::endl; 
+               });
+       // If we trigger the signal, the slot will be called
+       signal();
+} // Our scoped connection is destructed, and disconnects the slot
+// Triggering the signal now will not call the slot
+signal();      
+```
+
+### Slot return values
+
+#### Accumulation of return values
+It is possible for slots to have a return value. The return values can be
+returned from the signal using a *accumulator*, which is a function object that
+acts as a proxy object that processes the slot return values. When triggering a
+signal through a accumulator, the accumulator gets called for each slot return
+value, does the desired accumulation and then return the result to the code
+triggering the signal. The accumulator is designed to work in a similar way as 
+the STL numerical algorithm `std::accumulate`.
+
+```cpp
+// We create a singal with slots that return a value
+nod::signal<int(int, int)> signal;
+// Then we connect some signals
+signal.connect( std::plus<int>{} );
+signal.connect( std::multiplies<int>{} );
+signal.connect( std::minus<int>{} );           
+// Let's say we want to calculate the sum of all the slot return values
+// when triggering the singal with the parameters 10 and 100.
+// We do this by accumulating the return values with the initial value 0
+// and a plus function object, like so:
+std::cout << "Sum: " << signal.accumulate(0, std::plus<int>{})(10,100) << std::endl;
+// Or accumulate by multiplying (this needs 1 as initial value):
+std::cout << "Product: " << signal.accumulate(1, std::multiplies<int>{})(10,100) << std::endl;
+// If we instead want to build a vector with all the return values
+// we can accumulate them this way (start with a empty vector and add each value):                     
+auto vec = signal.accumulate( std::vector<int>{}, []( std::vector<int> result, int value ) {
+               result.push_back( value );
+               return result;
+       })(10,100);
+
+std::cout << "Vector: ";
+for( auto const& element : vec ) {
+       std::cout << element << " "; 
+}
+std::cout << std::endl;
+```
+#### Aggregation
+As we can see from the previous example, we can use the `accumulate` method if
+we want to aggregate all the return values of the slots. Doing the aggregation
+that way is not very optimal. It is both a inefficient algorithm for doing
+aggreagtion to a container, and it obscures the call site as the caller needs to
+express the aggregation using the verb *accumulate*. To remedy these
+shortcomings we can turn to the method `aggregate` instead. This is a template
+method, taking the type of container to aggregate to as a template parameter.
+
+```cpp
+// We create a singal
+nod::signal<int(int, int)> signal;
+// Let's connect some slots
+signal.connect( std::plus<int>{} );
+signal.connect( std::multiplies<int>{} );
+signal.connect( std::minus<int>{} );
+// We can now trigger the signal and aggregate the slot return values
+auto vec = signal.aggregate<std::vector<int>>(10,100);
+
+std::cout << "Result: ";
+for( auto const& element : vec ) {
+       std::cout << element << " "; 
+}
+std::cout << std::endl;
+```
+
+## Thread safety
+There are two types of signals in the library. The first is `nod::signal<T>`
+which is safe to use in a multi threaded environment. Multiple threads can read,
+write, connect slots and disconnect slots simultaneously, and the signal will 
+provide the nessesary synchronization. When triggering a slignal, all the
+registered slots will be called and executed by the thread that triggered the
+signal.
+
+The second type of signal is `nod::unsafe_signal<T>` which is **not** safe to
+use in a multi threaded environment. No syncronization will be performed on the
+internal state of the signal. Instances of the signal should theoretically be
+safe to read from multiple thread simultaneously, as long as no thread is
+writing to the same object at the same time. There can be a performance gain
+involved in using the unsafe version of a signal, since no syncronization
+primitives will be used.
+
+`nod::connection` and `nod::scoped_connection` are thread safe for reading from
+multiple threads, as long as no thread is writing to the same object. Writing in
+this context means calling any non const member function, including destructing
+the object. If an object is being written by one thread, then all reads and
+writes to that object from the same or other threads needs to be prevented.
+This basically means that a connection is only allowed to be disconnected from
+one thread, and you should not check connection status or reassign the
+connection while it is being disconnected.
+
+## Building the tests
+The test project uses [premake5](https://premake.github.io/download.html) to 
+generate make files or similiar.
+
+### Linux
+To build and run the tests using gcc and gmake on linux, execute the following
+from the test directory:
+```bash
+premake5 gmake
+make -C build/gmake
+bin/gmake/debug/nod_tests
+```
+
+### Visual Studio 2013
+To build and run the tests, execute the following from the test directory:
+
+```batchfile
+REM Adjust paths to suite your environment
+c:\path\to\premake\premake5.exe vs2013
+"c:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\vsvars32.bat"
+msbuild /m build\vs2013\nod_tests.sln
+bin\vs2013\debug\nod_tests.exe
+```
+
+## The MIT License (MIT)
+
+Copyright (c) 2015 Fredrik Berggren
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/3rdparty/nod/nod.hpp b/3rdparty/nod/nod.hpp
new file mode 100644 (file)
index 0000000..5c4a93c
--- /dev/null
@@ -0,0 +1,680 @@
+#ifndef IG_NOD_INCLUDE_NOD_HPP
+#define IG_NOD_INCLUDE_NOD_HPP
+
+#include <vector>       // std::vector
+#include <functional>   // std::function
+#include <mutex>        // std::mutex, std::lock_guard
+#include <memory>       // std::shared_ptr, std::weak_ptr
+#include <algorithm>    // std::find_if()
+#include <cassert>      // assert()
+#include <thread>       // std::this_thread::yield()
+#include <type_traits>  // std::is_same
+#include <iterator>     // std::back_inserter
+
+namespace nod {
+       // implementational details
+       namespace detail {
+               /// Interface for type erasure when disconnecting slots
+               struct disconnector {
+                       virtual void operator()( std::size_t index ) const = 0;
+               };
+               /// Deleter that doesn't delete
+               inline void no_delete(disconnector*){
+               };
+       } // namespace detail
+
+       /// Base template for the signal class
+       template <class P, class T>
+       class signal_type;
+
+
+       /// Connection class.
+       ///
+       /// This is used to be able to disconnect slots after they have been connected.
+       /// Used as return type for the connect method of the signals.
+       ///
+       /// Connections are default constructible.
+       /// Connections are not copy constructible or copy assignable.
+       /// Connections are move constructible and move assignable.
+       ///
+       class connection {
+               public:
+                       /// Default constructor
+                       connection() :
+                               _index()
+                       {}
+
+                       // Connection are not copy constructible or copy assignable
+                       connection( connection const& ) = delete;
+                       connection& operator=( connection const& ) = delete;
+
+                       /// Move constructor
+                       /// @param other   The instance to move from.
+                       connection( connection&& other ) :
+                               _weak_disconnector( std::move(other._weak_disconnector) ),
+                               _index( other._index )
+                       {}
+
+                       /// Move assign operator.
+                       /// @param other   The instance to move from.
+                       connection& operator=( connection&& other ) {
+                               _weak_disconnector = std::move( other._weak_disconnector );
+                               _index = other._index;
+                               return *this;
+                       }
+
+                       /// @returns `true` if the connection is connected to a signal object,
+                       ///          and `false` otherwise.
+                       bool connected() const {
+                               return !_weak_disconnector.expired();
+                       }
+
+                       /// Disconnect the slot from the connection.
+                       ///
+                       /// If the connection represents a slot that is connected to a signal object, calling
+                       /// this method will disconnect the slot from that object. The result of this operation
+                       /// is that the slot will stop receiving calls when the signal is invoked.
+                       void disconnect();
+
+               private:
+                       /// The signal template is a friend of the connection, since it is the
+                       /// only one allowed to create instances using the meaningful constructor.
+                       template<class P,class T> friend class signal_type;
+
+                       /// Create a connection.
+                       /// @param shared_disconnector   Disconnector instance that will be used to disconnect
+                       ///                              the connection when the time comes. A weak pointer
+                       ///                              to the disconnector will be held within the connection
+                       ///                              object.
+                       /// @param index                 The slot index of the connection.
+                       connection( std::shared_ptr<detail::disconnector> const& shared_disconnector, std::size_t index ) :
+                               _weak_disconnector( shared_disconnector ),
+                               _index( index )
+                       {}
+
+                       /// Weak pointer to the current disconnector functor.
+                       std::weak_ptr<detail::disconnector> _weak_disconnector;
+                       /// Slot index of the connected slot.
+                       std::size_t _index;
+       };
+
+       /// Scoped connection class.
+       ///
+       /// This type of connection is automatically disconnected when
+       /// the connection object is destructed.
+       ///
+       class scoped_connection
+       {
+               public:
+                       /// Scoped are default constructible
+                       scoped_connection() = default;
+                       /// Scoped connections are not copy constructible
+                       scoped_connection( scoped_connection const& ) = delete;
+                       /// Scoped connections are not copy assingable
+                       scoped_connection& operator=( scoped_connection const& ) = delete;
+
+                       /// Move constructor
+                       scoped_connection( scoped_connection&& other ) :
+                               _connection( std::move(other._connection) )
+                       {}
+
+                       /// Move assign operator.
+                       /// @param other   The instance to move from.
+                       scoped_connection& operator=( scoped_connection&& other ) {
+                               reset( std::move( other._connection ) );
+                               return *this;
+                       }
+
+                       /// Construct a scoped connection from a connection object
+                       /// @param connection   The connection object to manage
+                       scoped_connection( connection&& c ) :
+                               _connection( std::forward<connection>(c) )
+                       {}
+
+                       /// destructor
+                       ~scoped_connection() {
+                               disconnect();
+                       }
+
+                       /// Assignment operator moving a new connection into the instance.
+                       /// @note If the scoped_connection instance already contains a
+                       ///       connection, that connection will be disconnected as if
+                       ///       the scoped_connection was destroyed.
+                       /// @param c   New connection to manage
+                       scoped_connection& operator=( connection&& c ) {
+                               reset( std::forward<connection>(c) );
+                               return *this;
+                       }
+
+                       /// Reset the underlying connection to another connection.
+                       /// @note The connection currently managed by the scoped_connection
+                       ///       instance will be disconnected when resetting.
+                       /// @param c   New connection to manage
+                       void reset( connection&& c = {} ) {
+                               disconnect();
+                               _connection = std::move(c);
+                       }
+
+                       /// Release the underlying connection, without disconnecting it.
+                       /// @returns The newly released connection instance is returned.
+                       connection release() {
+                               connection c = std::move(_connection);
+                               _connection = connection{};
+                               return c;
+                       }
+
+                       ///
+                       /// @returns `true` if the connection is connected to a signal object,
+                       ///          and `false` otherwise.
+                       bool connected() const {
+                               return _connection.connected();
+                       }
+
+                       /// Disconnect the slot from the connection.
+                       ///
+                       /// If the connection represents a slot that is connected to a signal object, calling
+                       /// this method will disconnect the slot from that object. The result of this operation
+                       /// is that the slot will stop receiving calls when the signal is invoked.
+                       void disconnect() {
+                               _connection.disconnect();
+                       }
+
+               private:
+                       /// Underlying connection object
+                       connection _connection;
+       };
+
+       /// Policy for multi threaded use of signals.
+       ///
+       /// This policy provides mutex and lock types for use in
+       /// a multithreaded environment, where signals and slots
+       /// may exists in different threads.
+       ///
+       /// This policy is used in the `nod::signal` type provided
+       /// by the library.
+       struct multithread_policy
+       {
+               using mutex_type = std::mutex;
+               using mutex_lock_type = std::unique_lock<mutex_type>;
+               /// Function that yields the current thread, allowing
+               /// the OS to reschedule.
+               static void yield_thread() {
+                       std::this_thread::yield();
+               }
+               /// Function that defers a lock to a lock function that prevents deadlock
+               static mutex_lock_type defer_lock(mutex_type & m){
+                       return mutex_lock_type{m, std::defer_lock};
+               }
+               /// Function that locks two mutexes and prevents deadlock
+               static void lock(mutex_lock_type & a,mutex_lock_type & b) {
+                       std::lock(a,b);
+               }
+       };
+
+       /// Policy for single threaded use of signals.
+       ///
+       /// This policy provides dummy implementations for mutex
+       /// and lock types, resulting in that no synchronization
+       /// will take place.
+       ///
+       /// This policy is used in the `nod::unsafe_signal` type
+       /// provided by the library.
+       struct singlethread_policy
+       {
+               /// Dummy mutex type that doesn't do anything
+               struct mutex_type{};
+               /// Dummy lock type, that doesn't do any locking.
+               struct mutex_lock_type
+               {
+                       /// A lock type must be constructible from a
+                       /// mutex type from the same thread policy.
+                       explicit mutex_lock_type( mutex_type const& ) {
+                       }
+               };
+               /// Dummy implementation of thread yielding, that
+               /// doesn't do any actual yielding.
+               static void yield_thread() {
+               }
+               /// Dummy implemention of defer_lock that doesn't
+               /// do anything
+               static mutex_lock_type defer_lock(mutex_type &m){
+                       return mutex_lock_type{m};
+               }
+               /// Dummy implemention of lock that doesn't
+               /// do anything
+               static void lock(mutex_lock_type &,mutex_lock_type &) {
+               }
+       };
+
+       /// Signal accumulator class template.
+       ///
+       /// This acts sort of as a proxy for triggering a signal and
+       /// accumulating the slot return values.
+       ///
+       /// This class is not really intended to instantiate by client code.
+       /// Instances are aquired as return values of the method `accumulate()`
+       /// called on signals.
+       ///
+       /// @tparam S      Type of signal. The signal_accumulator acts
+       ///                as a type of proxy for a signal instance of
+       ///                this type.
+       /// @tparam T      Type of initial value of the accumulate algorithm.
+       ///                This type must meet the requirements of `CopyAssignable`
+       ///                and `CopyConstructible`
+       /// @tparam F      Type of accumulation function.
+       /// @tparam A...   Argument types of the underlying signal type.
+       ///
+       template <class S, class T, class F, class...A>
+       class signal_accumulator
+       {
+               public:
+                       /// Result type when calling the accumulating function operator.
+                       using result_type = typename std::result_of<F(T, typename S::slot_type::result_type)>::type;
+
+                       /// Construct a signal_accumulator as a proxy to a given signal
+                       //
+                       /// @param signal   Signal instance.
+                       /// @param init     Initial value of the accumulate algorithm.
+                       /// @param func     Binary operation function object that will be
+                       ///                 applied to all slot return values.
+                       ///                 The signature of the function should be
+                       ///                 equivalent of the following:
+                       ///                   `R func( T1 const& a, T2 const& b )`
+                       ///                  - The signature does not need to have `const&`.
+                       ///                  - The initial value, type `T`, must be implicitly
+                       ///                    convertible to `R`
+                       ///                  - The return type `R` must be implicitly convertible
+                       ///                    to type `T1`.
+                       ///                  - The type `R` must be `CopyAssignable`.
+                       ///                  - The type `S::slot_type::result_type` (return type of
+                       ///                    the signals slots) must be implicitly convertible to
+                       ///                    type `T2`.
+                       signal_accumulator( S const& signal, T init, F func ) :
+                               _signal( signal ),
+                               _init( init ),
+                               _func( func )
+                       {}
+
+                       /// Function call operator.
+                       ///
+                       /// Calling this will trigger the underlying signal and accumulate
+                       /// all of the connected slots return values with the current
+                       /// initial value and accumulator function.
+                       ///
+                       /// When called, this will invoke the accumulator function will
+                       /// be called for each return value of the slots. The semantics
+                       /// are similar to the `std::accumulate` algorithm.
+                       ///
+                       /// @param args   Arguments to propagate to the slots of the
+                       ///               underlying when triggering the signal.
+                       result_type operator()( A const& ... args ) const {
+                               return _signal.trigger_with_accumulator( _init, _func, args... );
+                       }
+
+               private:
+
+                       /// Reference to the underlying signal to proxy.
+                       S const& _signal;
+                       /// Initial value of the accumulate algorithm.
+                       T _init;
+                       /// Accumulator function.
+                       F _func;
+
+       };
+
+       /// Signal template specialization.
+       ///
+       /// This is the main signal implementation, and it is used to
+       /// implement the observer pattern whithout the overhead
+       /// boilerplate code that typically comes with it.
+       ///
+       /// Any function or function object is considered a slot, and
+       /// can be connected to a signal instance, as long as the signature
+       /// of the slot matches the signature of the signal.
+       ///
+       /// @tparam P      Threading policy for the signal.
+       ///                A threading policy must provide two type definitions:
+       ///                 - P::mutex_type, this type will be used as a mutex
+       ///                   in the signal_type class template.
+       ///                 - P::mutex_lock_type, this type must implement a
+       ///                   constructor that takes a P::mutex_type as a parameter,
+       ///                   and it must have the semantics of a scoped mutex lock
+       ///                   like std::lock_guard, i.e. locking in the constructor
+       ///                   and unlocking in the destructor.
+       ///
+       /// @tparam R      Return value type of the slots connected to the signal.
+       /// @tparam A...   Argument types of the slots connected to the signal.
+       template <class P, class R, class... A >
+       class signal_type<P,R(A...)>
+       {
+               public:
+                       /// signals are not copy constructible
+                       signal_type( signal_type const& ) = delete;
+                       /// signals are not copy assignable
+                       signal_type& operator=( signal_type const& ) = delete;
+                       /// signals are move constructible
+                       signal_type(signal_type&& other)
+                       {
+                               mutex_lock_type lock{other._mutex};
+                               _slot_count = std::move(other._slot_count);
+                               _slots = std::move(other._slots);
+                               if(other._shared_disconnector != nullptr)
+                               {
+                                       _disconnector = disconnector{ this };
+                                       _shared_disconnector = std::move(other._shared_disconnector);
+                                       // replace the disconnector with our own disconnector
+                                       *static_cast<disconnector*>(_shared_disconnector.get()) = _disconnector;
+                               }
+                       }
+                       /// signals are move assignable
+                       signal_type& operator=(signal_type&& other)
+                       {
+                               auto lock = thread_policy::defer_lock(_mutex);
+                               auto other_lock = thread_policy::defer_lock(other._mutex);
+                               thread_policy::lock(lock,other_lock);
+
+                               _slot_count = std::move(other._slot_count);
+                               _slots = std::move(other._slots);
+                               if(other._shared_disconnector != nullptr)
+                               {
+                                       _disconnector = disconnector{ this };
+                                       _shared_disconnector = std::move(other._shared_disconnector);
+                                       // replace the disconnector with our own disconnector
+                                       *static_cast<disconnector*>(_shared_disconnector.get()) = _disconnector;
+                               }
+                               return *this;
+                       }
+
+                       /// signals are default constructible
+                       signal_type() :
+                               _slot_count(0)
+                       {}
+
+                       // Destruct the signal object.
+                       ~signal_type() {
+                               invalidate_disconnector();
+                       }
+
+                       /// Type that will be used to store the slots for this signal type.
+                       using slot_type = std::function<R(A...)>;
+                       /// Type that is used for counting the slots connected to this signal.
+                       using size_type = typename std::vector<slot_type>::size_type;
+
+
+                       /// Connect a new slot to the signal.
+                       ///
+                       /// The connected slot will be called every time the signal
+                       /// is triggered.
+                       /// @param slot   The slot to connect. This must be a callable with
+                       ///               the same signature as the signal itself.
+                       /// @return       A connection object is returned, and can be used to
+                       ///               disconnect the slot.
+                       template <class T>
+                       connection connect( T&& slot ) {
+                               mutex_lock_type lock{ _mutex };
+                               _slots.push_back( std::forward<T>(slot) );
+                               std::size_t index = _slots.size()-1;
+                               if( _shared_disconnector == nullptr ) {
+                                       _disconnector = disconnector{ this };
+                                       _shared_disconnector = std::shared_ptr<detail::disconnector>{&_disconnector, detail::no_delete};
+                               }
+                               ++_slot_count;
+                               return connection{ _shared_disconnector, index };
+                       }
+
+                       /// Function call operator.
+                       ///
+                       /// Calling this is how the signal is triggered and the
+                       /// connected slots are called.
+                       ///
+                       /// @note The slots will be called in the order they were
+                       ///       connected to the signal.
+                       ///
+                       /// @param args   Arguments that will be propagated to the
+                       ///               connected slots when they are called.
+                       void operator()( A const&... args ) const {
+                               for( auto const& slot : copy_slots() ) {
+                                       if( slot ) {
+                                               slot( args... );
+                                       }
+                               }
+                       }
+
+                       /// Construct a accumulator proxy object for the signal.
+                       ///
+                       /// The intended purpose of this function is to create a function
+                       /// object that can be used to trigger the signal and accumulate
+                       /// all the slot return values.
+                       ///
+                       /// The algorithm used to accumulate slot return values is similar
+                       /// to `std::accumulate`. A given binary function is called for
+                       /// each return value with the parameters consisting of the
+                       /// return value of the accumulator function applied to the
+                       /// previous slots return value, and the current slots return value.
+                       /// A initial value must be provided for the first slot return type.
+                       ///
+                       /// @note This can only be used on signals that have slots with
+                       ///       non-void return types, since we can't accumulate void
+                       ///       values.
+                       ///
+                       /// @tparam T      The type of the initial value given to the accumulator.
+                       /// @tparam F      The accumulator function type.
+                       /// @param init    Initial value given to the accumulator.
+                       /// @param op      Binary operator function object to apply by the accumulator.
+                       ///                The signature of the function should be
+                       ///                equivalent of the following:
+                       ///                  `R func( T1 const& a, T2 const& b )`
+                       ///                 - The signature does not need to have `const&`.
+                       ///                 - The initial value, type `T`, must be implicitly
+                       ///                   convertible to `R`
+                       ///                 - The return type `R` must be implicitly convertible
+                       ///                   to type `T1`.
+                       ///                 - The type `R` must be `CopyAssignable`.
+                       ///                 - The type `S::slot_type::result_type` (return type of
+                       ///                   the signals slots) must be implicitly convertible to
+                       ///                   type `T2`.
+                       template <class T, class F>
+                       signal_accumulator<signal_type, T, F, A...> accumulate( T init, F op ) const {
+                               static_assert( std::is_same<R,void>::value == false, "Unable to accumulate slot return values with 'void' as return type." );
+                               return { *this, init, op };
+                       }
+
+
+                       /// Trigger the signal, calling the slots and aggregate all
+                       /// the slot return values into a container.
+                       ///
+                       /// @tparam C     The type of container. This type must be
+                       ///               `DefaultConstructible`, and usable with
+                       ///               `std::back_insert_iterator`. Additionally it
+                       ///               must be either copyable or moveable.
+                       /// @param args   The arguments to propagate to the slots.
+                       template <class C>
+                       C aggregate( A const&... args ) const {
+                               static_assert( std::is_same<R,void>::value == false, "Unable to aggregate slot return values with 'void' as return type." );
+                               C container;
+                               auto iterator = std::back_inserter( container );
+                               for( auto const& slot : copy_slots() ) {
+                                       if( slot ) {
+                                               (*iterator) = slot( args... );
+                                       }
+                               }
+                               return container;
+                       }
+
+                       /// Count the number of slots connected to this signal
+                       /// @returns   The number of connected slots
+                       size_type slot_count() const {
+                               return _slot_count;
+                       }
+
+                       /// Determine if the signal is empty, i.e. no slots are connected
+                       /// to it.
+                       /// @returns   `true` is returned if the signal has no connected
+                       ///            slots, and `false` otherwise.
+                       bool empty() const {
+                               return slot_count() == 0;
+                       }
+
+                       /// Disconnects all slots
+                       /// @note This operation invalidates all scoped_connection objects
+                       void disconnect_all_slots() {
+                               mutex_lock_type lock{ _mutex };
+                               _slots.clear();
+                               _slot_count = 0;
+                               invalidate_disconnector();
+                       }
+
+               private:
+                       template<class, class, class, class...> friend class signal_accumulator;
+                       /// Thread policy currently in use
+                       using thread_policy = P;
+                       /// Type of mutex, provided by threading policy
+                       using mutex_type = typename thread_policy::mutex_type;
+                       /// Type of mutex lock, provided by threading policy
+                       using mutex_lock_type = typename thread_policy::mutex_lock_type;
+
+                       /// Invalidate the internal disconnector object in a way
+                       /// that is safe according to the current thread policy.
+                       ///
+                       /// This will effectively make all current connection objects to
+                       /// to this signal incapable of disconnecting, since they keep a
+                       /// weak pointer to the shared disconnector object.
+                       void invalidate_disconnector() {
+                               // If we are unlucky, some of the connected slots
+                               // might be in the process of disconnecting from other threads.
+                               // If this happens, we are risking to destruct the disconnector
+                               // object managed by our shared pointer before they are done
+                               // disconnecting. This would be bad. To solve this problem, we
+                               // discard the shared pointer (that is pointing to the disconnector
+                               // object within our own instance), but keep a weak pointer to that
+                               // instance. We then stall the destruction until all other weak
+                               // pointers have released their "lock" (indicated by the fact that
+                               // we will get a nullptr when locking our weak pointer).
+                               std::weak_ptr<detail::disconnector> weak{_shared_disconnector};
+                               _shared_disconnector.reset();
+                               while( weak.lock() != nullptr ) {
+                                       // we just yield here, allowing the OS to reschedule. We do
+                                       // this until all threads has released the disconnector object.
+                                       thread_policy::yield_thread();
+                               }
+                       }
+
+                       /// Retrieve a copy of the current slots
+                       ///
+                       /// It's useful and necessary to copy the slots so we don't need
+                       /// to hold the lock while calling the slots. If we hold the lock
+                       /// we prevent the called slots from modifying the slots vector.
+                       /// This simple "double buffering" will allow slots to disconnect
+                       /// themself or other slots and connect new slots.
+                       std::vector<slot_type> copy_slots() const
+                       {
+                               mutex_lock_type lock{ _mutex };
+                               return _slots;
+                       }
+
+                       /// Implementation of the signal accumulator function call
+                       template <class T, class F>
+                       typename signal_accumulator<signal_type, T, F, A...>::result_type trigger_with_accumulator( T value, F& func, A const&... args ) const {
+                               for( auto const& slot : copy_slots() ) {
+                                       if( slot ) {
+                                               value = func( value, slot( args... ) );
+                                       }
+                               }
+                               return value;
+                       }
+
+                       /// Implementation of the disconnection operation.
+                       ///
+                       /// This is private, and only called by the connection
+                       /// objects created when connecting slots to this signal.
+                       /// @param index   The slot index of the slot that should
+                       ///                be disconnected.
+                       void disconnect( std::size_t index ) {
+                               mutex_lock_type lock( _mutex );
+                               assert( _slots.size() > index );
+                               if( _slots[ index ] != nullptr ) {
+                                       --_slot_count;
+                               }
+                               _slots[ index ] = slot_type{};
+                               while( _slots.size()>0 && !_slots.back() ) {
+                                       _slots.pop_back();
+                               }
+                       }
+
+                       /// Implementation of the shared disconnection state
+                       /// used by all connection created by signal instances.
+                       ///
+                       /// This inherits the @ref detail::disconnector interface
+                       /// for type erasure.
+                       struct disconnector :
+                               detail::disconnector
+                       {
+                               /// Default constructor, resulting in a no-op disconnector.
+                               disconnector() :
+                                       _ptr(nullptr)
+                               {}
+
+                               /// Create a disconnector that works with a given signal instance.
+                               /// @param ptr   Pointer to the signal instance that the disconnector
+                               ///              should work with.
+                               disconnector( signal_type<P,R(A...)>* ptr ) :
+                                       _ptr( ptr )
+                               {}
+
+                               /// Disconnect a given slot on the current signal instance.
+                               /// @note If the instance is default constructed, or created
+                               ///       with `nullptr` as signal pointer this operation will
+                               ///       effectively be a no-op.
+                               /// @param index   The index of the slot to disconnect.
+                               void operator()( std::size_t index ) const override {
+                                       if( _ptr ) {
+                                               _ptr->disconnect( index );
+                                       }
+                               }
+
+                               /// Pointer to the current signal.
+                               signal_type<P,R(A...)>* _ptr;
+                       };
+
+                       /// Mutex to synchronize access to the slot vector
+                       mutable mutex_type _mutex;
+                       /// Vector of all connected slots
+                       std::vector<slot_type> _slots;
+                       /// Number of connected slots
+                       size_type _slot_count;
+                       /// Disconnector operation, used for executing disconnection in a
+                       /// type erased manner.
+                       disconnector _disconnector;
+                       /// Shared pointer to the disconnector. All connection objects has a
+                       /// weak pointer to this pointer for performing disconnections.
+                       std::shared_ptr<detail::disconnector> _shared_disconnector;
+       };
+
+       // Implementation of the disconnect operation of the connection class
+       inline void connection::disconnect() {
+               auto ptr = _weak_disconnector.lock();
+               if( ptr ) {
+                       (*ptr)( _index );
+               }
+               _weak_disconnector.reset();
+       }
+
+       /// Signal type that is safe to use in multithreaded environments,
+       /// where the signal and slots exists in different threads.
+       /// The multithreaded policy provides mutexes and locks to synchronize
+       /// access to the signals internals.
+       ///
+       /// This is the recommended signal type, even for single threaded
+       /// environments.
+       template <class T> using signal = signal_type<multithread_policy, T>;
+
+       /// Signal type that is unsafe in multithreaded environments.
+       /// No synchronizations are provided to the signal_type for accessing
+       /// the internals.
+       ///
+       /// Only use this signal type if you are sure that your environment is
+       /// single threaded and performance is of importance.
+       template <class T> using unsafe_signal = signal_type<singlethread_policy, T>;
+} // namespace nod
+
+#endif // IG_NOD_INCLUDE_NOD_HPP
index 02a1b3000db773d74ea4fdf8114472b846107f4f..91dd05a5475472b8c9d62a46112d940d75f4f37f 100644 (file)
@@ -940,6 +940,8 @@ set(Lyx_Boost_Libraries)
 add_definitions(-DBOOST_USER_CONFIG=<config.h>)
 include_directories(${TOP_SRC_DIR}/3rdparty/boost)
 
+include_directories(${TOP_SRC_DIR}/3rdparty/nod)
+
 if(WIN32)
        if(LYX_CONSOLE)
                set(LYX_QTMAIN_LIBRARY)
index d07e1bcfccde15bd2ba46e74207e3ec98efa9230..0e9ff19b5bb8d5854aa8ef7cba2306ed2c57782f 100644 (file)
@@ -416,6 +416,34 @@ if test x$GXX = xyes; then
 fi
 ])
 
+dnl Usage: LYX_USE_INCLUDED_NOD : select if the included nod should be used.
+AC_DEFUN([LYX_USE_INCLUDED_NOD],[
+       AC_MSG_CHECKING([whether to use included nod library])
+       AC_ARG_WITH(included-nod,
+           [AS_HELP_STRING([--without-included-nod], [do not use the nod lib supplied with LyX, try to find one in the system directories - compilation will abort if nothing suitable is found])],
+           [lyx_cv_with_included_nod=$withval],
+           [lyx_cv_with_included_nod=yes])
+       AM_CONDITIONAL(USE_INCLUDED_NOD, test x$lyx_cv_with_included_nod = xyes)
+       AC_MSG_RESULT([$lyx_cv_with_included_nod])
+       if test x$lyx_cv_with_included_nod = xyes ; then
+           lyx_included_libs="$lyx_included_libs nod"
+           NOD_INCLUDES='-I$(top_srcdir)/3rdparty/nod'
+       else
+           NOD_INCLUDES=
+           AC_LANG_PUSH(C++)
+           AC_MSG_CHECKING([for nod library])
+           AC_LINK_IFELSE(
+               [AC_LANG_PROGRAM([#include <nod.hpp>],
+                   [nod::scoped_connection conn;])],
+               [AC_MSG_RESULT([yes])],
+               [AC_MSG_RESULT([no])
+               AC_MSG_ERROR([cannot find suitable nod library (do not use --without-included-nod)])
+           ])
+           AC_LANG_POP(C++)
+       fi
+       AC_SUBST(NOD_INCLUDES)
+])
+
 dnl Usage: LYX_USE_INCLUDED_BOOST : select if the included boost should
 dnl        be used.
 AC_DEFUN([LYX_USE_INCLUDED_BOOST],[
index 318986b4e247611aaa953a09b663d0099e142dfb..9853fa53dad355de45209e0d7c244793978dce01 100644 (file)
@@ -94,6 +94,7 @@ AC_SUBST(LIBPSAPI)
 AC_CHECK_LIB(gdi32, main)
 AC_CHECK_LIB(ole32, main)
 
+LYX_USE_INCLUDED_NOD
 LYX_USE_INCLUDED_BOOST
 
 ### we need to know the byte order for unicode conversions
@@ -337,6 +338,7 @@ AC_CONFIG_FILES([Makefile \
       3rdparty/dtl/Makefile \
       3rdparty/hunspell/Makefile \
       3rdparty/mythes/Makefile \
+      3rdparty/nod/Makefile \
       3rdparty/libiconv/Makefile \
       $ICONV_ICONV_H_IN \
       3rdparty/zlib/Makefile \
index 032b174cf3ceccea7775fda91cb8235ec6851941..31701f2fb154735a9f1b5fb3f19c269fc37b8468 100644 (file)
@@ -5,7 +5,7 @@ include $(top_srcdir)/config/common.am
 AM_CPPFLAGS += -I$(top_srcdir)/src
 AM_CPPFLAGS += $(BOOST_INCLUDES) $(ICONV_INCLUDES) $(ZLIB_INCLUDES)
 AM_CPPFLAGS += $(ENCHANT_CFLAGS) $(HUNSPELL_CFLAGS) $(MYTHES_INCLUDES)
-AM_CPPFLAGS += $(QT_CPPFLAGS) $(QT_CORE_INCLUDES)
+AM_CPPFLAGS += $(NOD_INCLUDES) $(QT_CPPFLAGS) $(QT_CORE_INCLUDES)
 
 if BUILD_CLIENT_SUBDIR
 CLIENT = client
index 79340492c6d62254beb78fcc8d03471b3ff5d3d2..8d9e0c61db1b10f3bda5467cdc97e8ae06ec9d34 100644 (file)
 #include <iostream>
 
 #ifdef _WIN32
-#include <io.h>
-#include <QCoreApplication>
+# include <io.h>
+# include <QCoreApplication>
+#else
+# ifdef HAVE_UNISTD_H
+#  include <unistd.h>
+# endif
 #endif
 #include <QThread>
 
index 5a6db831afeb8350f46ba97cd61485fb294d2f09..580556d6b14fe8d58b5e57fe011b02772b319ca8 100644 (file)
@@ -9,7 +9,7 @@ bin_PROGRAMS = lyxclient
 EXTRA_DIST = lyxclient.1in CMakeLists.txt
 
 AM_CPPFLAGS += -I$(srcdir)/.. \
-       $(BOOST_INCLUDES) $(ICONV_INCLUDES) $(ZLIB_INCLUDES)
+       $(BOOST_INCLUDES) $(ICONV_INCLUDES) $(ZLIB_INCLUDES) $(NOD_INCLUDES)
 
 lyxclient_LDADD = \
        $(top_builddir)/src/support/liblyxsupport.a \
index 2acdeebb26292d086074f338a80e0a9d5b109c0f..a8f233e25a74f6367f7765556653d95a2cdf8632 100644 (file)
@@ -7,7 +7,7 @@ DIST_SUBDIRS = qt .
 noinst_LIBRARIES = liblyxfrontends.a
 
 AM_CPPFLAGS += -I$(srcdir)/.. \
-       $(BOOST_INCLUDES) $(ICONV_INCLUDES) $(ZLIB_INCLUDES)
+       $(BOOST_INCLUDES) $(ICONV_INCLUDES) $(ZLIB_INCLUDES) $(NOD_INCLUDES)
 
 liblyxfrontends_a_SOURCES = \
        alert.h \
index 2a497af887e0ba8a36c88e80ab57698d66c52691..d559623310664087a9def46233a4b07364ecab06 100644 (file)
@@ -37,7 +37,7 @@ AM_CPPFLAGS += \
        -I$(top_srcdir)/src/frontends \
        -I$(top_srcdir)/images \
        $(QT_INCLUDES) \
-       $(BOOST_INCLUDES) $(ICONV_INCLUDES) $(ZLIB_INCLUDES)
+       $(BOOST_INCLUDES) $(ICONV_INCLUDES) $(ZLIB_INCLUDES) $(NOD_INCLUDES)
 
 SOURCEFILES = \
        ButtonPolicy.cpp \
index 1461fdb27c6f2683bd972affd07f91055b974828..66e198ace4bf8d6b35118db5944b93724c2d8718 100644 (file)
@@ -38,7 +38,7 @@ namespace graphics {
 class Converter::Impl {
 public:
        ///
-       Impl(FileName const & doc_fname,
+       Impl(Converter const & parent, FileName const & doc_fname,
             FileName const & from_file, string const & to_file_base,
             string const & from_format, string const & to_format);
 
@@ -59,6 +59,8 @@ public:
        ///
        sig finishedConversion;
 
+       ///
+       Converter const & parent_;
        ///
        FileName const doc_fname_;
        ///
@@ -71,8 +73,6 @@ public:
        bool valid_process_;
        ///
        bool finished_;
-       ///
-       Trackable tracker_;
 };
 
 
@@ -86,16 +86,10 @@ bool Converter::isReachable(string const & from_format_name,
 Converter::Converter(FileName const & doc_fname,
                      FileName const & from_file, string const & to_file_base,
                      string const & from_format, string const & to_format)
-       : pimpl_(new Impl(doc_fname, from_file, to_file_base, from_format, to_format))
+       : pimpl_(make_shared<Impl>(*this, doc_fname, from_file, to_file_base, from_format, to_format))
 {}
 
 
-Converter::~Converter()
-{
-       delete pimpl_;
-}
-
-
 void Converter::startConversion() const
 {
        pimpl_->startConversion();
@@ -123,10 +117,10 @@ static void build_script(string const & doc_fname,
                  ostream & script);
 
 
-Converter::Impl::Impl(FileName const & doc_fname,
+Converter::Impl::Impl(Converter const & parent, FileName const & doc_fname,
                      FileName const & from_file, string const & to_file_base,
                      string const & from_format, string const & to_format)
-       : doc_fname_(doc_fname), valid_process_(false), finished_(false)
+       : parent_(parent), doc_fname_(doc_fname), valid_process_(false), finished_(false)
 {
        LYXERR(Debug::GRAPHICS, "Converter c-tor:\n"
                << "doc_fname:        " << doc_fname
@@ -188,9 +182,12 @@ void Converter::Impl::startConversion()
        }
 
        ForkedCall::sigPtr ptr = ForkedCallQueue::add(script_command_);
-       ptr->connect(ForkedCall::slot([this](pid_t pid, int retval){
-                               converted(pid, retval);
-                       }).track_foreign(tracker_.p()));
+       weak_ptr<Converter::Impl> this_ = parent_.pimpl_;
+       ptr->connect([this_](pid_t pid, int retval){
+                       if (auto p = this_.lock()) {
+                               p->converted(pid, retval);
+                       }
+               });
 }
 
 
index c038029bd8f99b61559fbfc7b0447e4aab4d9cb0..7eb3689ad15c3c223ae4dac1eda15fb083b3b598 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "support/signals.h"
 
+#include <memory>
 
 namespace lyx {
 
@@ -39,9 +40,6 @@ public:
                  support::FileName const & from_file, std::string const & to_file_base,
                  std::string const & from_format, std::string const & to_format);
 
-       /// Needed for the pimpl
-       ~Converter();
-
        /// We are explicit about when we begin the conversion process.
        void startConversion() const;
 
@@ -70,7 +68,7 @@ private:
        /// Use the Pimpl idiom to hide the internals.
        class Impl;
        /// The pointer never changes although *pimpl_'s contents may.
-       Impl * const pimpl_;
+       std::shared_ptr<Impl> const pimpl_;
 };
 
 } // namespace graphics
index f0a09340916f649b2df3e5fe75968d87aa59ab1a..2316a4ac693449c35f7e668c3e8a6766ce5ae7ca 100644 (file)
@@ -21,6 +21,7 @@
 #include "support/lassert.h"
 #include "support/Timeout.h"
 
+#include <list>
 #include <queue>
 #include <memory>
 #include <set>
index d0590f6b2882387524d153f8fabe50ba8d49ad72..e1177a8d87290a5e966ae1dec9d84b62e269c047 100644 (file)
@@ -226,8 +226,6 @@ private:
        ///
        QTimer * delay_refresh_;
        ///
-       Trackable trackable_;
-       ///
        bool finished_generating_;
 
        /// We don't own this
@@ -244,16 +242,10 @@ lyx::Converter const * PreviewLoader::Impl::pconverter_;
 //
 
 PreviewLoader::PreviewLoader(Buffer const & b)
-       : pimpl_(new Impl(*this, b))
+       : pimpl_(make_shared<Impl>(*this, b))
 {}
 
 
-PreviewLoader::~PreviewLoader()
-{
-       delete pimpl_;
-}
-
-
 PreviewImage const * PreviewLoader::preview(string const & latex_snippet) const
 {
        return pimpl_->preview(latex_snippet);
@@ -721,9 +713,12 @@ void PreviewLoader::Impl::startLoading(bool wait)
 
        // Initiate the conversion from LaTeX to bitmap images files.
        ForkedCall::sigPtr convert_ptr = make_shared<ForkedCall::sig>();
-       convert_ptr->connect(ForkedProcess::slot([this](pid_t pid, int retval){
-                               finishedGenerating(pid, retval);
-                       }).track_foreign(trackable_.p()));
+       weak_ptr<PreviewLoader::Impl> this_ = parent_.pimpl_;
+       convert_ptr->connect([this_](pid_t pid, int retval){
+                       if (auto p = this_.lock()) {
+                               p->finishedGenerating(pid, retval);
+                       }
+               });
 
        ForkedCall call(buffer_.filePath());
        int ret = call.startScript(command, convert_ptr);
index ca22a9fa5c56706ef327ded5a5868e1ec837ba4d..5d0bf8e2340dc014065a319f3c75a7453bb831c2 100644 (file)
 #ifndef PREVIEWLOADER_H
 #define PREVIEWLOADER_H
 
+#include "ColorCode.h"
 #include "support/signals.h"
 
 #include <QObject>
 
-#include "ColorCode.h"
+#include <memory>
 
 namespace lyx {
 
@@ -39,8 +40,6 @@ public:
         *  LaTeX file.
         */
        PreviewLoader(Buffer const & buffer);
-       ///
-       ~PreviewLoader();
 
        /** Is there an image already associated with this snippet of LaTeX?
         *  If so, returns a pointer to it, else returns 0.
@@ -108,7 +107,7 @@ private:
        /// Use the Pimpl idiom to hide the internals.
        class Impl;
        /// The pointer never changes although *pimpl_'s contents may.
-       Impl * const pimpl_;
+       std::shared_ptr<Impl> const pimpl_;
 };
 
 } // namespace graphics
index 7718a745aec7a29717b76f824f721d83e841eb9b..387f8561bfb55e459a337057cb9694076c50beb8 100644 (file)
@@ -24,6 +24,8 @@
 #include "support/bind.h"
 
 #include <cerrno>
+#include <cstring>
+#include <list>
 #include <queue>
 #include <sstream>
 #include <utility>
index 83e67eabdf132a6e36a1029899a5391c43bed40d..3dedf57fea685ddec94beefa460e6e1c438fdb94 100644 (file)
@@ -29,7 +29,7 @@ liblyxsupport_a_DEPENDENCIES = $(MOCEDFILES)
 
 AM_CPPFLAGS += -I$(srcdir)/.. \
        $(BOOST_INCLUDES) $(ICONV_INCLUDES) $(ZLIB_INCLUDES) \
-       $(QT_CPPFLAGS) $(QT_INCLUDES)
+       $(NOD_INCLUDES) $(QT_CPPFLAGS) $(QT_INCLUDES)
 
 liblyxsupport_a_SOURCES = \
        FileMonitor.h \
index f768f2f873259182cde3307dfb49ac5ab650cda7..ceca3709c67c16f058e9245a5e73a86321cb47a0 100644 (file)
 #ifndef LYX_SIGNALS_H
 #define LYX_SIGNALS_H
 
-#include <boost/signals2/signal.hpp>
+#include <nod.hpp>
 
 #include <memory>
 
 namespace lyx {
 
-namespace signals2 = ::boost::signals2;
+namespace signals2 = ::nod;
 
 namespace support {
 
index 923d6950ec1f9c04dd6b9f4337d2d5637a96c752..7ad91a08289f28584a64e40cb51af8ca64560062 100644 (file)
@@ -17,7 +17,7 @@ bin_PROGRAMS = tex2lyx
 
 AM_CPPFLAGS += -I$(top_srcdir)/src/tex2lyx \
        -I$(top_srcdir)/src -I$(top_builddir) -I$(top_builddir)/src \
-       $(BOOST_INCLUDES) $(ICONV_INCLUDES) $(ZLIB_INCLUDES)
+       $(BOOST_INCLUDES) $(ICONV_INCLUDES) $(ZLIB_INCLUDES) $(NOD_INCLUDES)
 
 TEST_FILES = \
        test/runtests.cmake \