mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-11-25 02:49:46 +00:00
Use new signal library nod
instead of boost::signals2
Thanks Enrico for updating autotools files.
This commit is contained in:
parent
9602d24039
commit
bda4570400
8
3rdparty/Makefile.am
vendored
8
3rdparty/Makefile.am
vendored
@ -1,6 +1,10 @@
|
|||||||
include $(top_srcdir)/config/common.am
|
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
|
if USE_INCLUDED_BOOST
|
||||||
BOOST = boost
|
BOOST = boost
|
||||||
@ -26,7 +30,7 @@ if BUILD_INCLUDED_DTL
|
|||||||
DTL=dtl
|
DTL=dtl
|
||||||
endif
|
endif
|
||||||
|
|
||||||
SUBDIRS = $(BOOST) $(DTL) $(HUNSPELL) $(MYTHES) $(ICONV) $(ZLIB)
|
SUBDIRS = $(BOOST) $(DTL) $(HUNSPELL) $(MYTHES) $(ICONV) $(ZLIB) $(NOD)
|
||||||
|
|
||||||
EXTRA_DIST = \
|
EXTRA_DIST = \
|
||||||
scripts/evince_sync/evince_backward_search \
|
scripts/evince_sync/evince_backward_search \
|
||||||
|
4
3rdparty/nod/Makefile.am
vendored
Normal file
4
3rdparty/nod/Makefile.am
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
include $(top_srcdir)/config/common.am
|
||||||
|
|
||||||
|
EXTRA_DIST = nod \
|
||||||
|
README.md
|
257
3rdparty/nod/README.md
vendored
Normal file
257
3rdparty/nod/README.md
vendored
Normal file
@ -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.
|
680
3rdparty/nod/nod.hpp
vendored
Normal file
680
3rdparty/nod/nod.hpp
vendored
Normal file
@ -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
|
@ -940,6 +940,8 @@ set(Lyx_Boost_Libraries)
|
|||||||
add_definitions(-DBOOST_USER_CONFIG=<config.h>)
|
add_definitions(-DBOOST_USER_CONFIG=<config.h>)
|
||||||
include_directories(${TOP_SRC_DIR}/3rdparty/boost)
|
include_directories(${TOP_SRC_DIR}/3rdparty/boost)
|
||||||
|
|
||||||
|
include_directories(${TOP_SRC_DIR}/3rdparty/nod)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
if(LYX_CONSOLE)
|
if(LYX_CONSOLE)
|
||||||
set(LYX_QTMAIN_LIBRARY)
|
set(LYX_QTMAIN_LIBRARY)
|
||||||
|
@ -416,6 +416,34 @@ if test x$GXX = xyes; then
|
|||||||
fi
|
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 Usage: LYX_USE_INCLUDED_BOOST : select if the included boost should
|
||||||
dnl be used.
|
dnl be used.
|
||||||
AC_DEFUN([LYX_USE_INCLUDED_BOOST],[
|
AC_DEFUN([LYX_USE_INCLUDED_BOOST],[
|
||||||
|
@ -94,6 +94,7 @@ AC_SUBST(LIBPSAPI)
|
|||||||
AC_CHECK_LIB(gdi32, main)
|
AC_CHECK_LIB(gdi32, main)
|
||||||
AC_CHECK_LIB(ole32, main)
|
AC_CHECK_LIB(ole32, main)
|
||||||
|
|
||||||
|
LYX_USE_INCLUDED_NOD
|
||||||
LYX_USE_INCLUDED_BOOST
|
LYX_USE_INCLUDED_BOOST
|
||||||
|
|
||||||
### we need to know the byte order for unicode conversions
|
### we need to know the byte order for unicode conversions
|
||||||
@ -337,6 +338,7 @@ AC_CONFIG_FILES([Makefile \
|
|||||||
3rdparty/dtl/Makefile \
|
3rdparty/dtl/Makefile \
|
||||||
3rdparty/hunspell/Makefile \
|
3rdparty/hunspell/Makefile \
|
||||||
3rdparty/mythes/Makefile \
|
3rdparty/mythes/Makefile \
|
||||||
|
3rdparty/nod/Makefile \
|
||||||
3rdparty/libiconv/Makefile \
|
3rdparty/libiconv/Makefile \
|
||||||
$ICONV_ICONV_H_IN \
|
$ICONV_ICONV_H_IN \
|
||||||
3rdparty/zlib/Makefile \
|
3rdparty/zlib/Makefile \
|
||||||
|
@ -5,7 +5,7 @@ include $(top_srcdir)/config/common.am
|
|||||||
AM_CPPFLAGS += -I$(top_srcdir)/src
|
AM_CPPFLAGS += -I$(top_srcdir)/src
|
||||||
AM_CPPFLAGS += $(BOOST_INCLUDES) $(ICONV_INCLUDES) $(ZLIB_INCLUDES)
|
AM_CPPFLAGS += $(BOOST_INCLUDES) $(ICONV_INCLUDES) $(ZLIB_INCLUDES)
|
||||||
AM_CPPFLAGS += $(ENCHANT_CFLAGS) $(HUNSPELL_CFLAGS) $(MYTHES_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
|
if BUILD_CLIENT_SUBDIR
|
||||||
CLIENT = client
|
CLIENT = client
|
||||||
|
@ -60,8 +60,12 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <io.h>
|
# include <io.h>
|
||||||
#include <QCoreApplication>
|
# include <QCoreApplication>
|
||||||
|
#else
|
||||||
|
# ifdef HAVE_UNISTD_H
|
||||||
|
# include <unistd.h>
|
||||||
|
# endif
|
||||||
#endif
|
#endif
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ bin_PROGRAMS = lyxclient
|
|||||||
EXTRA_DIST = lyxclient.1in CMakeLists.txt
|
EXTRA_DIST = lyxclient.1in CMakeLists.txt
|
||||||
|
|
||||||
AM_CPPFLAGS += -I$(srcdir)/.. \
|
AM_CPPFLAGS += -I$(srcdir)/.. \
|
||||||
$(BOOST_INCLUDES) $(ICONV_INCLUDES) $(ZLIB_INCLUDES)
|
$(BOOST_INCLUDES) $(ICONV_INCLUDES) $(ZLIB_INCLUDES) $(NOD_INCLUDES)
|
||||||
|
|
||||||
lyxclient_LDADD = \
|
lyxclient_LDADD = \
|
||||||
$(top_builddir)/src/support/liblyxsupport.a \
|
$(top_builddir)/src/support/liblyxsupport.a \
|
||||||
|
@ -7,7 +7,7 @@ DIST_SUBDIRS = qt .
|
|||||||
noinst_LIBRARIES = liblyxfrontends.a
|
noinst_LIBRARIES = liblyxfrontends.a
|
||||||
|
|
||||||
AM_CPPFLAGS += -I$(srcdir)/.. \
|
AM_CPPFLAGS += -I$(srcdir)/.. \
|
||||||
$(BOOST_INCLUDES) $(ICONV_INCLUDES) $(ZLIB_INCLUDES)
|
$(BOOST_INCLUDES) $(ICONV_INCLUDES) $(ZLIB_INCLUDES) $(NOD_INCLUDES)
|
||||||
|
|
||||||
liblyxfrontends_a_SOURCES = \
|
liblyxfrontends_a_SOURCES = \
|
||||||
alert.h \
|
alert.h \
|
||||||
|
@ -37,7 +37,7 @@ AM_CPPFLAGS += \
|
|||||||
-I$(top_srcdir)/src/frontends \
|
-I$(top_srcdir)/src/frontends \
|
||||||
-I$(top_srcdir)/images \
|
-I$(top_srcdir)/images \
|
||||||
$(QT_INCLUDES) \
|
$(QT_INCLUDES) \
|
||||||
$(BOOST_INCLUDES) $(ICONV_INCLUDES) $(ZLIB_INCLUDES)
|
$(BOOST_INCLUDES) $(ICONV_INCLUDES) $(ZLIB_INCLUDES) $(NOD_INCLUDES)
|
||||||
|
|
||||||
SOURCEFILES = \
|
SOURCEFILES = \
|
||||||
ButtonPolicy.cpp \
|
ButtonPolicy.cpp \
|
||||||
|
@ -38,7 +38,7 @@ namespace graphics {
|
|||||||
class Converter::Impl {
|
class Converter::Impl {
|
||||||
public:
|
public:
|
||||||
///
|
///
|
||||||
Impl(FileName const & doc_fname,
|
Impl(Converter const & parent, FileName const & doc_fname,
|
||||||
FileName const & from_file, string const & to_file_base,
|
FileName const & from_file, string const & to_file_base,
|
||||||
string const & from_format, string const & to_format);
|
string const & from_format, string const & to_format);
|
||||||
|
|
||||||
@ -59,6 +59,8 @@ public:
|
|||||||
///
|
///
|
||||||
sig finishedConversion;
|
sig finishedConversion;
|
||||||
|
|
||||||
|
///
|
||||||
|
Converter const & parent_;
|
||||||
///
|
///
|
||||||
FileName const doc_fname_;
|
FileName const doc_fname_;
|
||||||
///
|
///
|
||||||
@ -71,8 +73,6 @@ public:
|
|||||||
bool valid_process_;
|
bool valid_process_;
|
||||||
///
|
///
|
||||||
bool finished_;
|
bool finished_;
|
||||||
///
|
|
||||||
Trackable tracker_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -86,16 +86,10 @@ bool Converter::isReachable(string const & from_format_name,
|
|||||||
Converter::Converter(FileName const & doc_fname,
|
Converter::Converter(FileName const & doc_fname,
|
||||||
FileName const & from_file, string const & to_file_base,
|
FileName const & from_file, string const & to_file_base,
|
||||||
string const & from_format, string const & to_format)
|
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
|
void Converter::startConversion() const
|
||||||
{
|
{
|
||||||
pimpl_->startConversion();
|
pimpl_->startConversion();
|
||||||
@ -123,10 +117,10 @@ static void build_script(string const & doc_fname,
|
|||||||
ostream & script);
|
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,
|
FileName const & from_file, string const & to_file_base,
|
||||||
string const & from_format, string const & to_format)
|
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"
|
LYXERR(Debug::GRAPHICS, "Converter c-tor:\n"
|
||||||
<< "doc_fname: " << doc_fname
|
<< "doc_fname: " << doc_fname
|
||||||
@ -188,9 +182,12 @@ void Converter::Impl::startConversion()
|
|||||||
}
|
}
|
||||||
|
|
||||||
ForkedCall::sigPtr ptr = ForkedCallQueue::add(script_command_);
|
ForkedCall::sigPtr ptr = ForkedCallQueue::add(script_command_);
|
||||||
ptr->connect(ForkedCall::slot([this](pid_t pid, int retval){
|
weak_ptr<Converter::Impl> this_ = parent_.pimpl_;
|
||||||
converted(pid, retval);
|
ptr->connect([this_](pid_t pid, int retval){
|
||||||
}).track_foreign(tracker_.p()));
|
if (auto p = this_.lock()) {
|
||||||
|
p->converted(pid, retval);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "support/signals.h"
|
#include "support/signals.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace lyx {
|
namespace lyx {
|
||||||
|
|
||||||
@ -39,9 +40,6 @@ public:
|
|||||||
support::FileName const & from_file, std::string const & to_file_base,
|
support::FileName const & from_file, std::string const & to_file_base,
|
||||||
std::string const & from_format, std::string const & to_format);
|
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.
|
/// We are explicit about when we begin the conversion process.
|
||||||
void startConversion() const;
|
void startConversion() const;
|
||||||
|
|
||||||
@ -70,7 +68,7 @@ private:
|
|||||||
/// Use the Pimpl idiom to hide the internals.
|
/// Use the Pimpl idiom to hide the internals.
|
||||||
class Impl;
|
class Impl;
|
||||||
/// The pointer never changes although *pimpl_'s contents may.
|
/// The pointer never changes although *pimpl_'s contents may.
|
||||||
Impl * const pimpl_;
|
std::shared_ptr<Impl> const pimpl_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace graphics
|
} // namespace graphics
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "support/lassert.h"
|
#include "support/lassert.h"
|
||||||
#include "support/Timeout.h"
|
#include "support/Timeout.h"
|
||||||
|
|
||||||
|
#include <list>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
@ -226,8 +226,6 @@ private:
|
|||||||
///
|
///
|
||||||
QTimer * delay_refresh_;
|
QTimer * delay_refresh_;
|
||||||
///
|
///
|
||||||
Trackable trackable_;
|
|
||||||
///
|
|
||||||
bool finished_generating_;
|
bool finished_generating_;
|
||||||
|
|
||||||
/// We don't own this
|
/// We don't own this
|
||||||
@ -244,16 +242,10 @@ lyx::Converter const * PreviewLoader::Impl::pconverter_;
|
|||||||
//
|
//
|
||||||
|
|
||||||
PreviewLoader::PreviewLoader(Buffer const & b)
|
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
|
PreviewImage const * PreviewLoader::preview(string const & latex_snippet) const
|
||||||
{
|
{
|
||||||
return pimpl_->preview(latex_snippet);
|
return pimpl_->preview(latex_snippet);
|
||||||
@ -721,9 +713,12 @@ void PreviewLoader::Impl::startLoading(bool wait)
|
|||||||
|
|
||||||
// Initiate the conversion from LaTeX to bitmap images files.
|
// Initiate the conversion from LaTeX to bitmap images files.
|
||||||
ForkedCall::sigPtr convert_ptr = make_shared<ForkedCall::sig>();
|
ForkedCall::sigPtr convert_ptr = make_shared<ForkedCall::sig>();
|
||||||
convert_ptr->connect(ForkedProcess::slot([this](pid_t pid, int retval){
|
weak_ptr<PreviewLoader::Impl> this_ = parent_.pimpl_;
|
||||||
finishedGenerating(pid, retval);
|
convert_ptr->connect([this_](pid_t pid, int retval){
|
||||||
}).track_foreign(trackable_.p()));
|
if (auto p = this_.lock()) {
|
||||||
|
p->finishedGenerating(pid, retval);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
ForkedCall call(buffer_.filePath());
|
ForkedCall call(buffer_.filePath());
|
||||||
int ret = call.startScript(command, convert_ptr);
|
int ret = call.startScript(command, convert_ptr);
|
||||||
|
@ -18,11 +18,12 @@
|
|||||||
#ifndef PREVIEWLOADER_H
|
#ifndef PREVIEWLOADER_H
|
||||||
#define PREVIEWLOADER_H
|
#define PREVIEWLOADER_H
|
||||||
|
|
||||||
|
#include "ColorCode.h"
|
||||||
#include "support/signals.h"
|
#include "support/signals.h"
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
#include "ColorCode.h"
|
#include <memory>
|
||||||
|
|
||||||
namespace lyx {
|
namespace lyx {
|
||||||
|
|
||||||
@ -39,8 +40,6 @@ public:
|
|||||||
* LaTeX file.
|
* LaTeX file.
|
||||||
*/
|
*/
|
||||||
PreviewLoader(Buffer const & buffer);
|
PreviewLoader(Buffer const & buffer);
|
||||||
///
|
|
||||||
~PreviewLoader();
|
|
||||||
|
|
||||||
/** Is there an image already associated with this snippet of LaTeX?
|
/** Is there an image already associated with this snippet of LaTeX?
|
||||||
* If so, returns a pointer to it, else returns 0.
|
* If so, returns a pointer to it, else returns 0.
|
||||||
@ -108,7 +107,7 @@ private:
|
|||||||
/// Use the Pimpl idiom to hide the internals.
|
/// Use the Pimpl idiom to hide the internals.
|
||||||
class Impl;
|
class Impl;
|
||||||
/// The pointer never changes although *pimpl_'s contents may.
|
/// The pointer never changes although *pimpl_'s contents may.
|
||||||
Impl * const pimpl_;
|
std::shared_ptr<Impl> const pimpl_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace graphics
|
} // namespace graphics
|
||||||
|
@ -24,6 +24,8 @@
|
|||||||
#include "support/bind.h"
|
#include "support/bind.h"
|
||||||
|
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
|
#include <cstring>
|
||||||
|
#include <list>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
@ -29,7 +29,7 @@ liblyxsupport_a_DEPENDENCIES = $(MOCEDFILES)
|
|||||||
|
|
||||||
AM_CPPFLAGS += -I$(srcdir)/.. \
|
AM_CPPFLAGS += -I$(srcdir)/.. \
|
||||||
$(BOOST_INCLUDES) $(ICONV_INCLUDES) $(ZLIB_INCLUDES) \
|
$(BOOST_INCLUDES) $(ICONV_INCLUDES) $(ZLIB_INCLUDES) \
|
||||||
$(QT_CPPFLAGS) $(QT_INCLUDES)
|
$(NOD_INCLUDES) $(QT_CPPFLAGS) $(QT_INCLUDES)
|
||||||
|
|
||||||
liblyxsupport_a_SOURCES = \
|
liblyxsupport_a_SOURCES = \
|
||||||
FileMonitor.h \
|
FileMonitor.h \
|
||||||
|
@ -12,13 +12,13 @@
|
|||||||
#ifndef LYX_SIGNALS_H
|
#ifndef LYX_SIGNALS_H
|
||||||
#define LYX_SIGNALS_H
|
#define LYX_SIGNALS_H
|
||||||
|
|
||||||
#include <boost/signals2/signal.hpp>
|
#include <nod.hpp>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace lyx {
|
namespace lyx {
|
||||||
|
|
||||||
namespace signals2 = ::boost::signals2;
|
namespace signals2 = ::nod;
|
||||||
|
|
||||||
namespace support {
|
namespace support {
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ bin_PROGRAMS = tex2lyx
|
|||||||
|
|
||||||
AM_CPPFLAGS += -I$(top_srcdir)/src/tex2lyx \
|
AM_CPPFLAGS += -I$(top_srcdir)/src/tex2lyx \
|
||||||
-I$(top_srcdir)/src -I$(top_builddir) -I$(top_builddir)/src \
|
-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_FILES = \
|
||||||
test/runtests.cmake \
|
test/runtests.cmake \
|
||||||
|
Loading…
Reference in New Issue
Block a user