Merge pull request #19132 from tritao/base-remove-boost-signals

Base: Remove Boost-based signals and switch to `FastSignals`.
This commit is contained in:
Kacper Donat
2026-01-08 12:41:29 +01:00
committed by GitHub
155 changed files with 22008 additions and 380 deletions
+2
View File
@@ -1,3 +1,5 @@
add_subdirectory(FastSignals)
# Build SalomeMesh for all Platforms since heavily patched
if (BUILD_SMESH)
add_subdirectory(salomesmesh)
+102
View File
@@ -0,0 +1,102 @@
---
Language: Cpp
# BasedOnStyle: WebKit
AccessModifierOffset: -4
AlignAfterOpenBracket: DontAlign
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlinesLeft: false
AlignOperands: false
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: false
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: true
AfterControlStatement: true
AfterEnum: true
AfterFunction: true
AfterNamespace: true
AfterObjCDeclaration: false
AfterStruct: true
AfterUnion: true
BeforeCatch: true
BeforeElse: true
IndentBraces: false
BreakBeforeBinaryOperators: All
BreakBeforeBraces: Custom
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: true
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 0
CommentPragmas: '^ IWYU pragma:'
BreakBeforeInheritanceComma: true
FixNamespaceComments: true
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: false
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
# FixNamespaceComments: false
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
IncludeCategories:
- Regex: '^"(stdafx|PrecompiledHeader)'
Priority: -2
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 4
- Regex: '^(<|"(gtest|isl|json)/)'
Priority: 3
- Regex: '.*'
Priority: 2
IncludeIsMainRegex: '$'
IndentCaseLabels: false
IndentWidth: 4
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: 'BEGIN_MESSAGE_MAP_CUSTOM$|BEGIN_MSG_MAP$|BEGIN_SINK_MAP$|BEGIN_MESSAGE_MAP$|BOOST_FIXTURE_TEST_SUITE$|BOOST_AUTO_TEST_SUITE$|BEGIN_DLGRESIZE_MAP$|BEGIN_MSG_MAP_EX$|BEGIN_DDX_MAP$|BEGIN_COM_MAP$|BEGIN_CONNECTION_POINT_MAP$|WTL_BEGIN_LAYOUT_MAP$|WTL_BEGIN_LAYOUT_CONTAINER$'
MacroBlockEnd: 'END_MESSAGE_MAP_CUSTOM$|END_MSG_MAP$|END_SINK_MAP$|END_MESSAGE_MAP$|BOOST_AUTO_TEST_SUITE_END$|END_DLGRESIZE_MAP$|END_DDX_MAP$|END_COM_MAP$|END_CONNECTION_POINT_MAP$|WTL_END_LAYOUT_MAP$|WTL_END_LAYOUT_CONTAINER$'
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBlockIndentWidth: 4
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 100
PenaltyReturnTypeOnItsOwnLine: 10000000
PointerAlignment: Left
ReflowComments: true
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 4
UseTab: Always
IndentPPDirectives: AfterHash
...
+367
View File
@@ -0,0 +1,367 @@
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# ------------
# Custom Rules
build/
+12
View File
@@ -0,0 +1,12 @@
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
project(FastSignals)
include(cmake/functions.cmake)
add_subdirectory(libfastsignals)
if(BUILD_FASTSIGNALS_TESTING)
add_subdirectory(tests/libfastsignals_stress_tests)
add_subdirectory(tests/libfastsignals_unit_tests)
endif()
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 iSpring Solutions Inc.
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.
+17
View File
@@ -0,0 +1,17 @@
# FastSignals
Yet another C++ signals and slots library
* Works as drop-in replacement for Boost.Signals2 with the same API
* Has better performance and more compact binary code
* Thread-safe in most operations, including concurrent connects/disconnects/emits
* Implemented with compact, pure C++17 code
[![Build Status](https://travis-ci.org/ispringteam/FastSignals.svg?branch=master)](https://travis-ci.org/ispringteam/FastSignals)
[![License: MIT](https://img.shields.io/badge/License-MIT-brightgreen.svg)](https://opensource.org/licenses/MIT)
## Documentation
* [Why FastSignals?](docs/why-fastsignals.md)
* [Simple Examples](docs/simple-examples.md)
* [Migration from Boost.Signals2](docs/migration-from-boost-signals2.md)
+24
View File
@@ -0,0 +1,24 @@
# Function to add a library target.
function(custom_add_library_from_dir TARGET)
# Gather files from the current directory
file(GLOB TARGET_SRC "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/*.h")
add_library(${TARGET} ${TARGET_SRC})
endfunction()
# Function to add an executable target.
function(custom_add_executable_from_dir TARGET)
# Gather files from the current directory
file(GLOB TARGET_SRC "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/*.h")
add_executable(${TARGET} ${TARGET_SRC})
endfunction()
# Function to add an executable target containing tests for a library.
function(custom_add_test_from_dir TARGET LIBRARY)
custom_add_executable_from_dir(${TARGET})
# Add path to Catch framework header
target_include_directories(${TARGET} PRIVATE "${CMAKE_SOURCE_DIR}/libs/catch")
# Link with the library being tested
target_link_libraries(${TARGET} ${LIBRARY})
# Register the executable with CMake as a test set
add_test(${TARGET} ${TARGET})
endfunction()
+94
View File
@@ -0,0 +1,94 @@
# Function bind_weak
## Usage
* Use `fastsignals::bind_weak` instead of `std::bind` to ensure that nothing happens if method called when binded object already destroyed
* Pass pointer to T class method as first argument, `shared_ptr<T>` or `weak_ptr<T>` as second argument
* Example: `bind_weak(&Document::save(), document, std::placeholders::_1)`, where `document` is a `weak_ptr<Document>` or `shared_ptr<Document>`
## Weak this idiom
The `fastsignals::bind_weak(...)` function implements "weak this" idiom. This idiom helps to avoid dangling pointers and memory access wiolations in asynchronous and/or multithreaded programs.
In the following example, we use weak this idiom to avoid using dangling pointer wehn calling `print()` method of the `Enityt`:
```cpp
struct Entity : std::enable_shared_from_this<Entity>
{
int value = 42;
void print()
{
std::cout << "print called, num = " << value << std::endl;
}
std::function<void()> print_later()
{
// ! weak this idiom here !
auto weak_this = weak_from_this();
return [weak_this] {
if (auto shared_this = weak_this.lock())
{
shared_this->print();
}
};
}
};
int main()
{
auto entity = std::make_shared<Entity>();
auto print = entity->print_later();
// Prints OK.
print();
// Prints nothing - last shared_ptr to the Entity destroyed, so `weak_this.lock()` will return nullptr.
entity = nullptr;
print();
}
```
## Using bind_weak to avoid signal receiver lifetime issues
In the following example, `Entity::print()` method connected to the signal. Signal emmited once before and once after the `Entity` instance destroyed. However, no memory access violation happens: once `Entity` destoryed, no slot will be called because `bind_weak` doesn't call binded method if it cannot lock `std::weak_ptr` to binded object. The second `event()` expression just does nothing.
```cpp
#include <fastsignals/signal.h>
#include <fastsignals/bind_weak.h>
#include <iostream>
using VoidSignal = fastsignals::signal<void()>;
using VoidSlot = VoidSignal::slot_type;
struct Entity : std::enable_shared_from_this<Entity>
{
int value = 42;
VoidSlot get_print_slot()
{
// Here fastsignals::bind_weak() used instead of std::bind.
return fastsignals::bind_weak(&Entity::print, weak_from_this());
}
void print()
{
std::cout << "print called, num = " << value << std::endl;
}
};
int main()
{
VoidSignal event;
auto entity = std::make_shared<Entity>();
event.connect(entity->get_print_slot());
// Here slot called - it prints `slot called, num = 42`
event();
entity = nullptr;
// Here nothing happens - no exception, no slot call.
event();
}
```
@@ -0,0 +1,163 @@
# Migration from Boost.Signals2
This guide helps to migrate large codebase from Boost.Signals2 to `FastSignals` signals/slots library. It helps to solve known migration issues in the right way.
During migrations, you will probably face with following things:
* You code uses `boost::signals2::` namespace and `<boost/signals2.hpp>` header directly
* You code uses third-party headers included implicitly by the `<boost/signals2.hpp>` header
## Reasons migrate from Boost.Signals2 to FastSignals
FastSignals API mostly compatible with Boost.Signals2 - there are differences, and all differences has their reasons explained below.
Comparing to Boost.Signals2, FastSignals has following pros:
* FastSignals is not header-only - so binary code will be more compact
* FastSignals implemented using C++17 with variadic templates, `constexpr if` and other modern metaprogramming techniques - so it compiles faster and, again, binary code will be more compact
* FastSignals probably will faster than Boost.Signals2 for your codebase because with FastSignals you don't pay for things that you don't use, with one exception: you always pay for the multithreading support
## Step 1: Create header with aliases
## Step 2: Rebuild and fix compile errors
### 2.1 Add missing includes
Boost.Signals2 is header-only library. It includes a lot of STL/Boost stuff while FastSignals does not:
```cpp
#include <boost/signals2.hpp>
// Also includes std::map, boost::variant, boost::optional, etc.
// Compiled OK even without `#include <map>`!
std::map CreateMyMap();
```
With FastSignals, you must include headers like `<map>` manually. The following table shows which files should be included explicitly if you see compile erros after migration.
| Class | Header |
|--------------------|:--------------------------------------:|
| std::map | `#include <map>` |
| boost::variant | `#include <boost/variant/variant.hpp>` |
| boost::optional | `#include <boost/optional/optional.hpp>` |
| boost::scoped_ptr | `#include <boost/scoped_ptr.hpp>` |
| boost::noncopyable | `#include <boost/noncopyable.hpp>` |
| boost::bind | `#include <boost/bind.hpp>` |
| boost::function | `#include <boost/function.hpp>` |
If you just want to compile you code, you can add following includes in you `signals.h` header:
```cpp
// WARNING: [libfastsignals] we do not recommend to include following extra headers.
#include <map>
#include <boost/variant/variant.hpp>
#include <boost/optional/optional.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/noncopyable.hpp>
#include <boost/bind.hpp>
#include <boost/function.hpp>
```
### 2.2 Remove redundant returns for void signals
With Boost.Signals2, following code compiled without any warning:
```cpp
boost::signals2::signal<void()> event;
event.connect([] {
return true;
});
```
With FastSignals, slot cannot return non-void value when for `signal<void(...)>`. You must fix your code: just remove returns from your slots or add lambdas to wrap slot and ignore it result.
### 2.3 Replace track() and track_foreign() with bind_weak_ptr()
Boost.Signals2 [can track connected objects lifetype](https://www.boost.org/doc/libs/1_55_0/doc/html/signals2/tutorial.html#idp204830936) using `track(...)` and `track_foreign(...)` methods. In the following example `Entity` created with `make_shared`, and `Entity::get_print_slot()` creates slot function which tracks weak pointer to Entity:
```cpp
#include <boost/signals2.hpp>
#include <iostream>
#include <memory>
using VoidSignal = boost::signals2::signal<void()>;
using VoidSlot = VoidSignal::slot_type;
struct Entity : std::enable_shared_from_this<Entity>
{
int value = 42;
VoidSlot get_print_slot()
{
// Here track() tracks object itself.
return VoidSlot(std::bind(&Entity::print, this)).track_foreign(shared_from_this());
}
void print()
{
std::cout << "print called, num = " << value << std::endl;
}
};
int main()
{
VoidSignal event;
auto entity = std::make_shared<Entity>();
event.connect(entity->get_print_slot());
// Here slot called - it prints `print called, num = 42`
event();
entity = nullptr;
// This call does nothing.
event();
}
```
FastSignals uses another approach: `bind_weak` function:
```cpp
#include <fastsignals/bind_weak.h>
#include <iostream>
using VoidSignal = fastsignals::signal<void()>;
using VoidSlot = VoidSignal::slot_type;
struct Entity : std::enable_shared_from_this<Entity>
{
int value = 42;
VoidSlot get_print_slot()
{
// Here fastsignals::bind_weak() used instead of std::bind.
return fastsignals::bind_weak(&Entity::print, weak_from_this());
}
void print()
{
std::cout << "print called, num = " << value << std::endl;
}
};
int main()
{
VoidSignal event;
auto entity = std::make_shared<Entity>();
event.connect(entity->get_print_slot());
// Here slot called - it prints `slot called, num = 42`
event();
entity = nullptr;
// Here nothing happens - no exception, no slot call.
event();
}
```
### FastSignals Differences in Result Combiners
## Step 3: Run Tests
Run all automated tests that you have (unit tests, integration tests, system tests, stress tests, benchmarks, UI tests).
Probably you will see no errors. If you see any, please report an issue.
+55
View File
@@ -0,0 +1,55 @@
# Simple Examples
>If you are not familar with Boost.Signals2, please read [Boost.Signals2: Connections](https://theboostcpplibraries.com/boost.signals2-connections)
## Example with signal&lt;&gt; and connection
```cpp
// Creates signal and connects 1 slot, calls 2 times, disconnects, calls again.
// Outputs:
// 13
// 17
#include "libfastsignals/signal.h"
using namespace fastsignals;
int main()
{
signal<void(int)> valueChanged;
connection conn;
conn = valueChanged.connect([](int value) {
cout << value << endl;
});
valueChanged(13);
valueChanged(17);
conn.disconnect();
valueChanged(42);
}
```
## Example with scoped_connection
```cpp
// Creates signal and connects 1 slot, calls 2 times, calls again after scoped_connection destroyed.
// - note: scoped_connection closes connection in destructor
// Outputs:
// 13
// 17
#include "libfastsignals/signal.h"
using namespace fastsignals;
int main()
{
signal<void(int)> valueChanged;
{
scoped_connection conn;
conn = valueChanged.connect([](int value) {
cout << value << endl;
});
valueChanged(13);
valueChanged(17);
}
valueChanged(42);
}
```
+40
View File
@@ -0,0 +1,40 @@
# Why FastSignals?
FastSignals is a C++17 signals/slots implementation which API is compatible with Boost.Signals2.
FastSignals pros:
* Faster than Boost.Signals2
* Has more compact binary code
* Has the same API as Boost.Signals2
FastSignals cons:
* Supports only C++17 compatible compilers: Visual Studio 2017, modern Clang, modern GCC
* Lacks a few rarely used features presented in Boost.Signals2
* No access to connection from slot with `signal::connect_extended` method
* No connected object tracking with `slot::track` method
* Use [bind_weak](bind_weak.md) instead
* No temporary signal blocking with `shared_connection_block` class
* Cannot disconnect equivalent slots since no `disconnect(slot)` function overload
* Any other API difference is a bug - please report it!
See also [Migration from Boost.Signals2](migration-from-boost-signals2.md).
## Benchmark results
Directory `tests/libfastsignals_bench` contains simple benchmark with compares two signal/slot implementations:
* Boost.Signals2
* libfastsignals
Benchmark compairs performance when signal emitted frequently with 0, 1 and 8 active connections. In these cases libfastsignals is 3-6 times faster.
```
*** Results:
measure emit_boost emit_fastsignals
emit_boost/0 1.00 3.00
emit_boost/1 1.00 5.76
emit_boost/8 1.00 3.70
***
```
@@ -0,0 +1,5 @@
file(GLOB LIBFASTSIGNALS_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/include/*.h")
add_library(libfastsignals STATIC ${LIBFASTSIGNALS_SRC})
set_target_properties(libfastsignals PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_include_directories(libfastsignals INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include")
@@ -0,0 +1,79 @@
#pragma once
#include <functional>
#include <memory>
#include <type_traits>
namespace fastsignals
{
namespace detail
{
template <class ReturnType, class ClassType, bool AddConst, class... Args>
struct weak_binder
{
using ConstMethodType = ReturnType (ClassType::*)(Args... args) const;
using NonConstMethodType = ReturnType (ClassType::*)(Args... args);
using MethodType = std::conditional_t<AddConst, ConstMethodType, NonConstMethodType>;
using WeakPtrType = std::weak_ptr<ClassType>;
weak_binder(MethodType pMethod, WeakPtrType&& pObject)
: m_pMethod(pMethod)
, m_pObject(std::move(pObject))
{
}
ReturnType operator()(Args... args) const
{
if (auto pThis = m_pObject.lock())
{
return (pThis.get()->*m_pMethod)(std::forward<Args>(args)...);
}
return ReturnType();
}
MethodType m_pMethod;
WeakPtrType m_pObject;
};
} // namespace detail
/// Weak this binding of non-const methods.
template <typename ReturnType, typename ClassType, typename... Params, typename... Args>
decltype(auto) bind_weak(ReturnType (ClassType::*memberFn)(Params... args), std::shared_ptr<ClassType> const& pThis, Args... args)
{
using weak_binder_alias = detail::weak_binder<ReturnType, ClassType, false, Params...>;
weak_binder_alias invoker(memberFn, std::weak_ptr<ClassType>(pThis));
return std::bind(invoker, args...);
}
/// Weak this binding of const methods.
template <typename ReturnType, typename ClassType, typename... Params, typename... Args>
decltype(auto) bind_weak(ReturnType (ClassType::*memberFn)(Params... args) const, std::shared_ptr<ClassType> const& pThis, Args... args)
{
using weak_binder_alias = detail::weak_binder<ReturnType, ClassType, true, Params...>;
weak_binder_alias invoker(memberFn, std::weak_ptr<ClassType>(pThis));
return std::bind(invoker, args...);
}
/// Weak this binding of non-const methods.
template <typename ReturnType, typename ClassType, typename... Params, typename... Args>
decltype(auto) bind_weak(ReturnType (ClassType::*memberFn)(Params... args), std::weak_ptr<ClassType> pThis, Args... args)
{
using weak_binder_alias = detail::weak_binder<ReturnType, ClassType, false, Params...>;
weak_binder_alias invoker(memberFn, std::move(pThis));
return std::bind(invoker, args...);
}
/// Weak this binding of const methods.
template <typename ReturnType, typename ClassType, typename... Params, typename... Args>
decltype(auto) bind_weak(ReturnType (ClassType::*memberFn)(Params... args) const, std::weak_ptr<ClassType> pThis, Args... args)
{
using weak_binder_alias = detail::weak_binder<ReturnType, ClassType, true, Params...>;
weak_binder_alias invoker(memberFn, std::move(pThis));
return std::bind(invoker, args...);
}
} // namespace fastsignals
@@ -0,0 +1,40 @@
#pragma once
#include <optional>
namespace fastsignals
{
/**
* This results combiner reduces results collection into last value of this collection.
* In other words, it keeps only result of the last slot call.
*/
template <class T>
class optional_last_value
{
public:
using result_type = std::optional<T>;
template <class TRef>
void operator()(TRef&& value)
{
m_result = std::forward<TRef>(value);
}
result_type get_value() const
{
return m_result;
}
private:
result_type m_result = {};
};
template <>
class optional_last_value<void>
{
public:
using result_type = void;
};
} // namespace fastsignals
@@ -0,0 +1,116 @@
#pragma once
#include "signal_impl.h"
namespace fastsignals
{
// Connection keeps link between signal and slot and can disconnect them.
// Disconnect operation is thread-safe: any thread can disconnect while
// slots called on other thread.
// This class itself is not thread-safe: you can't use the same connection
// object from different threads at the same time.
class connection
{
public:
connection() noexcept;
explicit connection(detail::signal_impl_weak_ptr storage, uint64_t id) noexcept;
connection(const connection& other) noexcept;
connection& operator=(const connection& other) noexcept;
connection(connection&& other) noexcept;
connection& operator=(connection&& other) noexcept;
~connection() = default;
bool connected() const noexcept;
void disconnect() noexcept;
protected:
detail::signal_impl_weak_ptr m_storage;
uint64_t m_id = 0;
};
// Connection class that supports blocking callback execution
class advanced_connection : public connection
{
public:
struct advanced_connection_impl
{
void block() noexcept;
void unblock() noexcept;
bool is_blocked() const noexcept;
private:
std::atomic<int> m_blockCounter {0};
};
using impl_ptr = std::shared_ptr<advanced_connection_impl>;
advanced_connection() noexcept;
explicit advanced_connection(connection&& conn, impl_ptr&& impl) noexcept;
advanced_connection(const advanced_connection&) noexcept;
advanced_connection& operator=(const advanced_connection&) noexcept;
advanced_connection(advanced_connection&& other) noexcept;
advanced_connection& operator=(advanced_connection&& other) noexcept;
~advanced_connection() = default;
protected:
impl_ptr m_impl;
};
// Blocks advanced connection, so its callback will not be executed
class shared_connection_block
{
public:
shared_connection_block(const advanced_connection& connection = advanced_connection(), bool initially_blocked = true) noexcept;
shared_connection_block(const shared_connection_block& other) noexcept;
shared_connection_block(shared_connection_block&& other) noexcept;
shared_connection_block& operator=(const shared_connection_block& other) noexcept;
shared_connection_block& operator=(shared_connection_block&& other) noexcept;
~shared_connection_block();
void block() noexcept;
void unblock() noexcept;
bool blocking() const noexcept;
private:
void increment_if_blocked() const noexcept;
std::weak_ptr<advanced_connection::advanced_connection_impl> m_connection;
std::atomic<bool> m_blocked {false};
};
// Scoped connection keeps link between signal and slot and disconnects them in destructor.
// Scoped connection is movable, but not copyable.
class scoped_connection : public connection
{
public:
scoped_connection() noexcept;
scoped_connection(const connection& conn) noexcept;
scoped_connection(connection&& conn) noexcept;
scoped_connection(const advanced_connection& conn) = delete;
scoped_connection(advanced_connection&& conn) noexcept = delete;
scoped_connection(const scoped_connection&) = delete;
scoped_connection& operator=(const scoped_connection&) = delete;
scoped_connection(scoped_connection&& other) noexcept;
scoped_connection& operator=(scoped_connection&& other) noexcept;
~scoped_connection();
connection release() noexcept;
};
// scoped connection for advanced connections
class advanced_scoped_connection : public advanced_connection
{
public:
advanced_scoped_connection() noexcept;
advanced_scoped_connection(const advanced_connection& conn) noexcept;
advanced_scoped_connection(advanced_connection&& conn) noexcept;
advanced_scoped_connection(const advanced_scoped_connection&) = delete;
advanced_scoped_connection& operator=(const advanced_scoped_connection&) = delete;
advanced_scoped_connection(advanced_scoped_connection&& other) noexcept;
advanced_scoped_connection& operator=(advanced_scoped_connection&& other) noexcept;
~advanced_scoped_connection();
advanced_connection release() noexcept;
};
} // namespace fastsignals
@@ -0,0 +1,54 @@
#pragma once
#include "function_detail.h"
namespace fastsignals
{
// Derive your class from not_directly_callable to prevent function from wrapping it using its template constructor
// Useful if your class provides custom operator for casting to function
struct not_directly_callable
{
};
template <class Fn, class Function, class Return, class... Arguments>
using enable_if_callable_t = typename std::enable_if_t<
!std::is_same_v<std::decay_t<Fn>, Function> && !std::is_base_of_v<not_directly_callable, std::decay_t<Fn>> && std::is_same_v<std::invoke_result_t<Fn, Arguments...>, Return>>;
template <class Signature>
class function;
// Compact function class - causes minimal code bloat when compiled.
// Replaces std::function in this library.
template <class Return, class... Arguments>
class function<Return(Arguments...)>
{
public:
function() = default;
function(const function& other) = default;
function(function&& other) noexcept = default;
function& operator=(const function& other) = default;
function& operator=(function&& other) noexcept = default;
template <class Fn, typename = enable_if_callable_t<Fn, function<Return(Arguments...)>, Return, Arguments...>>
function(Fn&& function) noexcept(detail::is_noexcept_packed_function_init<Fn, Return, Arguments...>)
{
m_packed.init<Fn, Return, Arguments...>(std::forward<Fn>(function));
}
Return operator()(Arguments&&... args) const
{
auto& proxy = m_packed.get<Return(Arguments...)>();
return proxy(std::forward<Arguments>(args)...);
}
detail::packed_function release() noexcept
{
return std::move(m_packed);
}
private:
detail::packed_function m_packed;
};
} // namespace fastsignals
@@ -0,0 +1,164 @@
#pragma once
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <type_traits>
#include <utility>
namespace fastsignals::detail
{
/// Buffer for callable object in-place construction,
/// helps to implement Small Buffer Optimization.
static constexpr size_t inplace_buffer_size = (sizeof(int) == sizeof(void*) ? 8 : 6) * sizeof(void*);
/// Structure that has size enough to keep type "T" including vtable.
template <class T>
struct type_container
{
T data;
};
/// Type that has enough space to keep type "T" (including vtable) with suitable alignment.
using function_buffer_t = std::aligned_storage_t<inplace_buffer_size>;
/// Constantly is true if callable fits function buffer, false otherwise.
template <class T>
inline constexpr bool fits_inplace_buffer = (sizeof(type_container<T>) <= inplace_buffer_size);
// clang-format off
/// Constantly is true if callable fits function buffer and can be safely moved, false otherwise
template <class T>
inline constexpr bool can_use_inplace_buffer =
fits_inplace_buffer<T> &&
std::is_nothrow_move_constructible_v<T>;
// clang format on
/// Type that is suitable to keep copy of callable object.
/// - equal to pointer-to-function if Callable is pointer-to-function
/// - otherwise removes const/volatile and references to allow copying callable.
template <class Callable>
using callable_copy_t = std::conditional_t<std::is_function_v<std::remove_reference_t<Callable>>,
Callable,
std::remove_cv_t<std::remove_reference_t<Callable>>>;
class base_function_proxy
{
public:
virtual ~base_function_proxy() = default;
virtual base_function_proxy* clone(void* buffer) const = 0;
virtual base_function_proxy* move(void* buffer) noexcept = 0;
};
template <class Signature>
class function_proxy;
template <class Return, class... Arguments>
class function_proxy<Return(Arguments...)> : public base_function_proxy
{
public:
virtual Return operator()(Arguments&&...) = 0;
};
template <class Callable, class Return, class... Arguments>
class function_proxy_impl final : public function_proxy<Return(Arguments...)>
{
public:
// If you see this error, probably your function returns value and you're trying to
// connect it to `signal<void(...)>`. Just remove return value from callback.
static_assert(std::is_same_v<std::invoke_result_t<Callable, Arguments...>, Return>,
"cannot construct function<> class from callable object with different return type");
template <class FunctionObject>
explicit function_proxy_impl(FunctionObject&& function)
: m_callable(std::forward<FunctionObject>(function))
{
}
Return operator()(Arguments&&... args) final
{
return m_callable(std::forward<Arguments>(args)...);
}
base_function_proxy* clone(void* buffer) const final
{
if constexpr (can_use_inplace_buffer<function_proxy_impl>)
{
return new (buffer) function_proxy_impl(*this);
}
else
{
(void)buffer;
return new function_proxy_impl(*this);
}
}
base_function_proxy* move(void* buffer) noexcept final
{
if constexpr (can_use_inplace_buffer<function_proxy_impl>)
{
base_function_proxy* moved = new (buffer) function_proxy_impl(std::move(*this));
this->~function_proxy_impl();
return moved;
}
else
{
(void)buffer;
return this;
}
}
private:
callable_copy_t<Callable> m_callable;
};
template <class Fn, class Return, class... Arguments>
inline constexpr bool is_noexcept_packed_function_init = can_use_inplace_buffer<function_proxy_impl<Fn, Return, Arguments...>>;
class packed_function final
{
public:
packed_function() = default;
packed_function(packed_function&& other) noexcept;
packed_function(const packed_function& other);
packed_function& operator=(packed_function&& other) noexcept;
packed_function& operator=(const packed_function& other);
~packed_function() noexcept;
// Initializes packed function.
// Cannot be called without reset().
template <class Callable, class Return, class... Arguments>
void init(Callable&& function) noexcept(is_noexcept_packed_function_init<Callable, Return, Arguments...>)
{
using proxy_t = function_proxy_impl<Callable, Return, Arguments...>;
assert(m_proxy == nullptr);
if constexpr (can_use_inplace_buffer<proxy_t>)
{
m_proxy = new (&m_buffer) proxy_t{ std::forward<Callable>(function) };
}
else
{
m_proxy = new proxy_t{ std::forward<Callable>(function) };
}
}
template <class Signature>
function_proxy<Signature>& get() const
{
return static_cast<function_proxy<Signature>&>(unwrap());
}
void reset() noexcept;
private:
base_function_proxy* move_proxy_from(packed_function&& other) noexcept;
base_function_proxy* clone_proxy_from(const packed_function &other);
base_function_proxy& unwrap() const;
bool is_buffer_allocated() const noexcept;
function_buffer_t m_buffer[1] = {};
base_function_proxy* m_proxy = nullptr;
};
} // namespace fastsignals::detail
@@ -0,0 +1,151 @@
#pragma once
#include "combiners.h"
#include "connection.h"
#include "function.h"
#include "signal_impl.h"
#include "type_traits.h"
#include <type_traits>
namespace fastsignals
{
template <class Signature, template <class T> class Combiner = optional_last_value>
class signal;
struct advanced_tag
{
};
/// Signal allows to fire events to many subscribers (slots).
/// In other words, it implements one-to-many relation between event and listeners.
/// Signal implements observable object from Observable pattern.
template <class Return, class... Arguments, template <class T> class Combiner>
class signal<Return(Arguments...), Combiner> : private not_directly_callable
{
public:
using signature_type = Return(signal_arg_t<Arguments>...);
using slot_type = function<signature_type>;
using combiner_type = Combiner<Return>;
using result_type = typename combiner_type::result_type;
signal()
: m_slots(std::make_shared<detail::signal_impl>())
{
}
/// No copy construction
signal(const signal&) = delete;
/// Moves signal from other. Any operations on other except destruction, move, and swap are invalid
signal(signal&& other) = default;
/// No copy assignment
signal& operator=(const signal&) = delete;
/// Moves signal from other. Any operations on other except destruction, move, and swap are invalid
signal& operator=(signal&& other) = default;
/**
* connect(slot) method subscribes slot to signal emission event.
* Each time you call signal as functor, all slots are also called with given arguments.
* @returns connection - object which manages signal-slot connection lifetime
*/
connection connect(slot_type slot)
{
const uint64_t id = m_slots->add(slot.release());
return connection(m_slots, id);
}
/**
* connect(slot, advanced_tag) method subscribes slot to signal emission event with the ability to temporarily block slot execution
* Each time you call signal as functor, all non-blocked slots are also called with given arguments.
* You can temporarily block slot execution using shared_connection_block
* @returns advanced_connection - object which manages signal-slot connection lifetime
*/
advanced_connection connect(slot_type slot, advanced_tag)
{
static_assert(std::is_void_v<Return>, "Advanced connect can only be used with slots returning void (implementation limitation)");
auto conn_impl = std::make_shared<advanced_connection::advanced_connection_impl>();
slot_type slot_impl = [this, slot, weak_conn_impl = std::weak_ptr(conn_impl)](signal_arg_t<Arguments>... args) {
(void)this;
auto conn_impl = weak_conn_impl.lock();
if (!conn_impl || !conn_impl->is_blocked())
{
slot(args...);
}
};
auto conn = connect(std::move(slot_impl));
return advanced_connection(std::move(conn), std::move(conn_impl));
}
/**
* disconnect_all_slots() method disconnects all slots from signal emission event.
*/
void disconnect_all_slots() noexcept
{
m_slots->remove_all();
}
/**
* num_slots() method returns number of slots attached to this singal
*/
[[nodiscard]] std::size_t num_slots() const noexcept
{
return m_slots->count();
}
/**
* empty() method returns true if signal has any slots attached
*/
[[nodiscard]] bool empty() const noexcept
{
return m_slots->count() == 0;
}
/**
* operator(args...) calls all slots connected to this signal.
* Logically, it fires signal emission event.
*/
result_type operator()(signal_arg_t<Arguments>... args) const
{
return detail::signal_impl_ptr(m_slots)->invoke<combiner_type, result_type, signature_type, signal_arg_t<Arguments>...>(args...);
}
void swap(signal& other) noexcept
{
m_slots.swap(other.m_slots);
}
/**
* Allows using signals as slots for another signal
*/
operator slot_type() const noexcept
{
return [weakSlots = detail::signal_impl_weak_ptr(m_slots)](signal_arg_t<Arguments>... args) {
if (auto slots = weakSlots.lock())
{
return slots->invoke<combiner_type, result_type, signature_type, signal_arg_t<Arguments>...>(args...);
}
};
}
private:
detail::signal_impl_ptr m_slots;
};
} // namespace fastsignals
namespace std
{
// free swap function, findable by ADL
template <class Signature, template <class T> class Combiner>
void swap(
::fastsignals::signal<Signature, Combiner>& sig1,
::fastsignals::signal<Signature, Combiner>& sig2)
{
sig1.swap(sig2);
}
} // namespace std
@@ -0,0 +1,59 @@
#pragma once
#include "function_detail.h"
#include "spin_mutex.h"
#include <memory>
#include <vector>
namespace fastsignals::detail
{
class signal_impl
{
public:
uint64_t add(packed_function fn);
void remove(uint64_t id) noexcept;
void remove_all() noexcept;
size_t count() const noexcept;
template <class Combiner, class Result, class Signature, class... Args>
Result invoke(Args... args) const
{
packed_function slot;
size_t slotIndex = 0;
uint64_t slotId = 1;
if constexpr (std::is_same_v<Result, void>)
{
while (get_next_slot(slot, slotIndex, slotId))
{
slot.get<Signature>()(std::forward<Args>(args)...);
}
}
else
{
Combiner combiner;
while (get_next_slot(slot, slotIndex, slotId))
{
combiner(slot.get<Signature>()(std::forward<Args>(args)...));
}
return combiner.get_value();
}
}
private:
bool get_next_slot(packed_function& slot, size_t& expectedIndex, uint64_t& nextId) const;
mutable spin_mutex m_mutex;
std::vector<packed_function> m_functions;
std::vector<uint64_t> m_ids;
uint64_t m_nextId = 1;
};
using signal_impl_ptr = std::shared_ptr<signal_impl>;
using signal_impl_weak_ptr = std::weak_ptr<signal_impl>;
} // namespace fastsignals::detail
@@ -0,0 +1,38 @@
#pragma once
#include <atomic>
namespace fastsignals::detail
{
class spin_mutex
{
public:
spin_mutex() = default;
spin_mutex(const spin_mutex&) = delete;
spin_mutex& operator=(const spin_mutex&) = delete;
spin_mutex(spin_mutex&&) = delete;
spin_mutex& operator=(spin_mutex&&) = delete;
inline bool try_lock() noexcept
{
return !m_busy.test_and_set(std::memory_order_acquire);
}
inline void lock() noexcept
{
while (!try_lock())
{
/* do nothing */;
}
}
inline void unlock() noexcept
{
m_busy.clear(std::memory_order_release);
}
private:
std::atomic_flag m_busy = ATOMIC_FLAG_INIT;
};
} // namespace fastsignals::detail
@@ -0,0 +1,24 @@
#pragma once
namespace fastsignals
{
namespace detail
{
template <typename T>
struct signal_arg
{
using type = const T&;
};
template <typename U>
struct signal_arg<U&>
{
using type = U&;
};
} // namespace detail
template <typename T>
using signal_arg_t = typename detail::signal_arg<T>::type;
} // namespace fastsignals
@@ -0,0 +1,251 @@
#include "../include/fastsignals/connection.h"
namespace fastsignals
{
namespace
{
auto get_advanced_connection_impl(const advanced_connection& connection) noexcept
{
struct advanced_connection_impl_getter : private advanced_connection
{
advanced_connection_impl_getter(const advanced_connection& connection) noexcept
: advanced_connection(connection)
{
}
using advanced_connection::m_impl;
};
return advanced_connection_impl_getter(connection).m_impl;
}
} // namespace
connection::connection(connection&& other) noexcept
: m_storage(other.m_storage)
, m_id(other.m_id)
{
other.m_storage.reset();
other.m_id = 0;
}
connection::connection(detail::signal_impl_weak_ptr storage, uint64_t id) noexcept
: m_storage(std::move(storage))
, m_id(id)
{
}
connection::connection() noexcept = default;
connection::connection(const connection& other) noexcept = default;
connection& connection::operator=(connection&& other) noexcept
{
m_storage = other.m_storage;
m_id = other.m_id;
other.m_storage.reset();
other.m_id = 0;
return *this;
}
connection& connection::operator=(const connection& other) noexcept = default;
bool connection::connected() const noexcept
{
return (m_id != 0);
}
void connection::disconnect() noexcept
{
if (auto storage = m_storage.lock())
{
storage->remove(m_id);
m_storage.reset();
}
m_id = 0;
}
scoped_connection::scoped_connection(connection&& conn) noexcept
: connection(std::move(conn))
{
}
scoped_connection::scoped_connection(const connection& conn) noexcept
: connection(conn)
{
}
scoped_connection::scoped_connection() noexcept = default;
scoped_connection::scoped_connection(scoped_connection&& other) noexcept = default;
scoped_connection& scoped_connection::operator=(scoped_connection&& other) noexcept
{
disconnect();
static_cast<connection&>(*this) = std::move(other);
return *this;
}
scoped_connection::~scoped_connection()
{
disconnect();
}
connection scoped_connection::release() noexcept
{
connection conn = std::move(static_cast<connection&>(*this));
return conn;
}
bool advanced_connection::advanced_connection_impl::is_blocked() const noexcept
{
return m_blockCounter.load(std::memory_order_acquire) != 0;
}
void advanced_connection::advanced_connection_impl::block() noexcept
{
++m_blockCounter;
}
void advanced_connection::advanced_connection_impl::unblock() noexcept
{
--m_blockCounter;
}
advanced_connection::advanced_connection() noexcept = default;
advanced_connection::advanced_connection(connection&& conn, impl_ptr&& impl) noexcept
: connection(std::move(conn))
, m_impl(std::move(impl))
{
}
advanced_connection::advanced_connection(const advanced_connection&) noexcept = default;
advanced_connection::advanced_connection(advanced_connection&& other) noexcept = default;
advanced_connection& advanced_connection::operator=(const advanced_connection&) noexcept = default;
advanced_connection& advanced_connection::operator=(advanced_connection&& other) noexcept = default;
shared_connection_block::shared_connection_block(const advanced_connection& connection, bool initially_blocked) noexcept
: m_connection(get_advanced_connection_impl(connection))
{
if (initially_blocked)
{
block();
}
}
shared_connection_block::shared_connection_block(const shared_connection_block& other) noexcept
: m_connection(other.m_connection)
, m_blocked(other.m_blocked.load(std::memory_order_acquire))
{
increment_if_blocked();
}
shared_connection_block::shared_connection_block(shared_connection_block&& other) noexcept
: m_connection(other.m_connection)
, m_blocked(other.m_blocked.load(std::memory_order_acquire))
{
other.m_connection.reset();
other.m_blocked.store(false, std::memory_order_release);
}
shared_connection_block& shared_connection_block::operator=(const shared_connection_block& other) noexcept
{
if (&other != this)
{
unblock();
m_connection = other.m_connection;
m_blocked = other.m_blocked.load(std::memory_order_acquire);
increment_if_blocked();
}
return *this;
}
shared_connection_block& shared_connection_block::operator=(shared_connection_block&& other) noexcept
{
if (&other != this)
{
unblock();
m_connection = other.m_connection;
m_blocked = other.m_blocked.load(std::memory_order_acquire);
other.m_connection.reset();
other.m_blocked.store(false, std::memory_order_release);
}
return *this;
}
shared_connection_block::~shared_connection_block()
{
unblock();
}
void shared_connection_block::block() noexcept
{
bool blocked = false;
if (m_blocked.compare_exchange_strong(blocked, true, std::memory_order_acq_rel, std::memory_order_relaxed))
{
if (auto connection = m_connection.lock())
{
connection->block();
}
}
}
void shared_connection_block::unblock() noexcept
{
bool blocked = true;
if (m_blocked.compare_exchange_strong(blocked, false, std::memory_order_acq_rel, std::memory_order_relaxed))
{
if (auto connection = m_connection.lock())
{
connection->unblock();
}
}
}
bool shared_connection_block::blocking() const noexcept
{
return m_blocked;
}
void shared_connection_block::increment_if_blocked() const noexcept
{
if (m_blocked)
{
if (auto connection = m_connection.lock())
{
connection->block();
}
}
}
advanced_scoped_connection::advanced_scoped_connection() noexcept = default;
advanced_scoped_connection::advanced_scoped_connection(const advanced_connection& conn) noexcept
: advanced_connection(conn)
{
}
advanced_scoped_connection::advanced_scoped_connection(advanced_connection&& conn) noexcept
: advanced_connection(std::move(conn))
{
}
advanced_scoped_connection::advanced_scoped_connection(advanced_scoped_connection&& other) noexcept = default;
advanced_scoped_connection& advanced_scoped_connection::operator=(advanced_scoped_connection&& other) noexcept = default;
advanced_scoped_connection::~advanced_scoped_connection()
{
disconnect();
}
advanced_connection advanced_scoped_connection::release() noexcept
{
advanced_connection conn = std::move(static_cast<advanced_connection&>(*this));
return conn;
}
} // namespace fastsignals
@@ -0,0 +1,96 @@
#include "../include/fastsignals/function_detail.h"
#include <cstddef>
#include <functional>
namespace fastsignals::detail
{
packed_function::packed_function(packed_function&& other) noexcept
: m_proxy(move_proxy_from(std::move(other)))
{
}
packed_function::packed_function(const packed_function& other)
: m_proxy(clone_proxy_from(other))
{
}
packed_function& packed_function::operator=(packed_function&& other) noexcept
{
assert(this != &other);
reset();
m_proxy = move_proxy_from(std::move(other));
return *this;
}
base_function_proxy* packed_function::move_proxy_from(packed_function&& other) noexcept
{
auto proxy = other.m_proxy ? other.m_proxy->move(&m_buffer) : nullptr;
other.m_proxy = nullptr;
return proxy;
}
base_function_proxy* packed_function::clone_proxy_from(const packed_function& other)
{
return other.m_proxy ? other.m_proxy->clone(&m_buffer) : nullptr;
}
packed_function& packed_function::operator=(const packed_function& other)
{
if (this != &other)
{
if (other.is_buffer_allocated() && is_buffer_allocated())
{
// "This" and "other" are using SBO. Safe assignment must use copy+move
*this = packed_function(other);
}
else
{
// Buffer is used either by "this" or by "other" or not used at all.
// If this uses buffer then other's proxy is null or allocated on heap, so clone won't overwrite buffer
// If this uses heap or null then other's proxy can safely use buffer because reset() won't access buffer
auto newProxy = clone_proxy_from(other);
reset();
m_proxy = newProxy;
}
}
return *this;
}
packed_function::~packed_function() noexcept
{
reset();
}
void packed_function::reset() noexcept
{
if (m_proxy != nullptr)
{
if (is_buffer_allocated())
{
m_proxy->~base_function_proxy();
}
else
{
delete m_proxy;
}
m_proxy = nullptr;
}
}
base_function_proxy& packed_function::unwrap() const
{
if (m_proxy == nullptr)
{
throw std::bad_function_call();
}
return *m_proxy;
}
bool packed_function::is_buffer_allocated() const noexcept
{
return std::less_equal<const void*>()(&m_buffer[0], m_proxy)
&& std::less<const void*>()(m_proxy, &m_buffer[1]);
}
} // namespace fastsignals::detail
@@ -0,0 +1,84 @@
#include "../include/fastsignals/signal_impl.h"
#include <algorithm>
#include <mutex>
namespace fastsignals::detail
{
uint64_t signal_impl::add(packed_function fn)
{
std::lock_guard lock(m_mutex);
m_functions.emplace_back(std::move(fn));
try
{
m_ids.emplace_back(m_nextId);
}
catch (const std::bad_alloc& /*e*/)
{
// Remove function since we failed to add its id
m_functions.pop_back();
throw;
}
return m_nextId++;
}
void signal_impl::remove(uint64_t id) noexcept
{
std::lock_guard lock(m_mutex);
// We use binary search because ids array is always sorted.
auto it = std::lower_bound(m_ids.begin(), m_ids.end(), id);
if (it != m_ids.end() && *it == id)
{
size_t i = std::distance(m_ids.begin(), it);
m_ids.erase(m_ids.begin() + i);
m_functions.erase(m_functions.begin() + i);
}
}
void signal_impl::remove_all() noexcept
{
std::lock_guard lock(m_mutex);
m_functions.clear();
m_ids.clear();
}
bool signal_impl::get_next_slot(packed_function& slot, size_t& expectedIndex, uint64_t& nextId) const
{
// Slots always arranged by ID, so we can use a simple algorithm which avoids races:
// - on each step find first slot with ID >= slotId
// - after each call increment slotId
std::lock_guard lock(m_mutex);
// Avoid binary search if next slot wasn't moved between mutex locks.
if (expectedIndex >= m_ids.size() || m_ids[expectedIndex] != nextId)
{
auto it = (nextId < m_nextId)
? std::lower_bound(m_ids.cbegin(), m_ids.cend(), nextId)
: m_ids.end();
if (it == m_ids.end())
{
return false;
}
expectedIndex = std::distance(m_ids.cbegin(), it);
}
slot.reset();
slot = m_functions[expectedIndex];
nextId = (expectedIndex + 1 < m_ids.size()) ? m_ids[expectedIndex + 1] : m_ids[expectedIndex] + 1;
++expectedIndex;
return true;
}
size_t signal_impl::count() const noexcept
{
std::lock_guard lock(m_mutex);
return m_functions.size();
}
} // namespace fastsignals::detail
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,3 @@
custom_add_test_from_dir(libfastsignals_stress_tests libfastsignals)
target_include_directories(libfastsignals_stress_tests PRIVATE "${CMAKE_SOURCE_DIR}/tests")
@@ -0,0 +1,2 @@
#define CATCH_CONFIG_MAIN
#include "catch2/catch.hpp"
@@ -0,0 +1,124 @@
#include "catch2/catch.hpp"
#include <fastsignals/signal.h>
#include <array>
#include <mutex>
#include <random>
#include <vector>
#include <thread>
using namespace fastsignals;
namespace
{
using string_signal = signal<void(std::string)>;
using string_slot = string_signal::slot_type;
using void_signal = signal<void()>;
using void_slot = void_signal::slot_type;
class named_entity
{
public:
std::string name() const
{
std::lock_guard lock(m_nameMutex);
return m_name;
}
void fire_changed(std::string value)
{
bool fire = false;
{
std::lock_guard lock(m_nameMutex);
if (m_name != value)
{
m_name = std::move(value);
fire = true;
}
}
if (fire)
{
m_nameChanged(value);
}
}
connection on_name_changed(string_slot slot)
{
return m_nameChanged.connect(std::move(slot));
}
private:
mutable std::mutex m_nameMutex;
std::string m_name;
signal<void(std::string)> m_nameChanged;
};
unsigned get_next_seed()
{
static std::minstd_rand seedEngine(777);
return seedEngine();
}
size_t get_random_index(size_t size)
{
thread_local std::minstd_rand disconnectRandomEngine{ get_next_seed() };
std::uniform_int_distribution<size_t> disconnectIndexDistribution{ 0, size - 1 };
return disconnectIndexDistribution(disconnectRandomEngine);
}
} // namespace
TEST_CASE("Can work in a few threads", "[signal]")
{
constexpr unsigned fireThreadCount = 8;
constexpr unsigned signalsCount = 7;
constexpr unsigned fireCountPerThread = 100'000;
constexpr unsigned connectCallsCount = 80'000;
constexpr unsigned totalRunCount = 10;
for (unsigned i = 0; i < totalRunCount; ++i)
{
std::array<void_signal, signalsCount> signals;
std::mutex connectionsMutex;
std::vector<connection> connections;
connections.reserve(connectCallsCount);
std::vector<std::thread> threads;
auto slot = [&] {
std::lock_guard lock(connectionsMutex);
if (!connections.empty())
{
const size_t index = get_random_index(connections.size());
connections.at(index).disconnect();
}
};
threads.emplace_back([&] {
for (unsigned cci = 0; cci < connectCallsCount; ++cci)
{
const size_t index = get_random_index(signalsCount);
connection conn = signals.at(index).connect(slot);
{
std::lock_guard lock(connectionsMutex);
connections.emplace_back(conn);
}
}
});
for (unsigned fti = 0; fti < fireThreadCount; ++fti)
{
threads.emplace_back([&] {
for (unsigned fi = 0; fi < fireCountPerThread; ++fi)
{
const size_t index = get_random_index(signalsCount);
signals.at(index)();
}
});
}
for (auto& thread : threads)
{
thread.join();
}
}
}
@@ -0,0 +1,3 @@
custom_add_test_from_dir(libfastsignals_unit_tests libfastsignals)
target_include_directories(libfastsignals_unit_tests PRIVATE "${CMAKE_SOURCE_DIR}/tests")
@@ -0,0 +1,618 @@
#include "catch2/catch.hpp"
#include <fastsignals/function.h>
#include <array>
using namespace fastsignals;
namespace
{
int Abs(int x)
{
return x >= 0 ? x : -x;
}
int Sum(int a, int b)
{
return a + b;
}
void InplaceAbs(int& x)
{
x = Abs(x);
}
std::string GetStringHello()
{
return "hello";
}
class AbsFunctor
{
public:
int operator()(int x) const
{
return Abs(x);
}
};
class SumFunctor
{
public:
int operator()(int a, int b) const
{
return Sum(a, b);
}
};
class InplaceAbsFunctor
{
public:
void operator()(int& x) /* non-const */
{
if (m_calledOnce)
{
abort();
}
m_calledOnce = true;
InplaceAbs(x);
}
private:
bool m_calledOnce = false;
};
class GetStringFunctor
{
public:
explicit GetStringFunctor(const std::string& value)
: m_value(value)
{
}
std::string operator()() /* non-const */
{
if (m_calledOnce)
{
abort();
}
m_calledOnce = true;
return m_value;
}
private:
bool m_calledOnce = false;
std::string m_value;
};
} // namespace
TEST_CASE("Can use free function with 1 argument", "[function]")
{
function<int(int)> fn = Abs;
REQUIRE(fn(10) == 10);
REQUIRE(fn(-10) == 10);
REQUIRE(fn(0) == 0);
}
TEST_CASE("Can use free function with 2 arguments", "[function]")
{
function<int(int, int)> fn = Sum;
REQUIRE(fn(10, 5) == 15);
REQUIRE(fn(-10, 0) == -10);
}
TEST_CASE("Can use free function without arguments", "[function]")
{
function<std::string()> fn = GetStringHello;
REQUIRE(fn() == "hello");
}
TEST_CASE("Can use free function without return value", "[function]")
{
function<void(int&)> fn = InplaceAbs;
int a = -10;
fn(a);
REQUIRE(a == 10);
}
TEST_CASE("Can use lambda with 1 argument", "[function]")
{
function<int(int)> fn = [](int value) {
return Abs(value);
};
REQUIRE(fn(10) == 10);
REQUIRE(fn(-10) == 10);
REQUIRE(fn(0) == 0);
}
TEST_CASE("Can use lambda with 2 arguments", "[function]")
{
function<int(int, int)> fn = [](auto&& a, auto&& b) {
return Sum(a, b);
};
REQUIRE(fn(10, 5) == 15);
REQUIRE(fn(-10, 0) == -10);
}
TEST_CASE("Can use lambda without arguments", "[function]")
{
function<std::string()> fn = [] {
return GetStringHello();
};
REQUIRE(fn() == "hello");
}
TEST_CASE("Can use lambda without return value", "[function]")
{
bool calledOnce = false;
function<void(int&)> fn = [calledOnce](auto& value) mutable {
if (calledOnce)
{
abort();
}
calledOnce = true;
InplaceAbs(value);
};
int a = -10;
fn(a);
REQUIRE(a == 10);
}
TEST_CASE("Can use functor with 1 argument", "[function]")
{
function<int(int)> fn = AbsFunctor();
REQUIRE(fn(10) == 10);
REQUIRE(fn(-10) == 10);
REQUIRE(fn(0) == 0);
}
TEST_CASE("Can use functor with 2 arguments", "[function]")
{
function<int(int, int)> fn = SumFunctor();
REQUIRE(fn(10, 5) == 15);
REQUIRE(fn(-10, 0) == -10);
}
TEST_CASE("Can use functor without arguments", "[function]")
{
function<std::string()> fn = GetStringFunctor("hello");
REQUIRE(fn() == "hello");
}
TEST_CASE("Can use functor without return value", "[function]")
{
function<void(int&)> fn = InplaceAbsFunctor();
int a = -10;
fn(a);
REQUIRE(a == 10);
}
TEST_CASE("Can construct function with cons std::function<>&", "[function]")
{
using BoolCallback = std::function<void(bool succeed)>;
bool value = false;
const BoolCallback& cb = [&value](bool succeed) {
value = succeed;
};
function<void(bool)> fn = cb;
fn(true);
REQUIRE(value == true);
fn(false);
REQUIRE(value == false);
fn(true);
REQUIRE(value == true);
}
TEST_CASE("Can copy function", "[function]")
{
unsigned calledCount = 0;
bool value = false;
function<void(bool)> callback = [&](bool gotValue) {
++calledCount;
value = gotValue;
};
auto callback2 = callback;
REQUIRE(calledCount == 0);
CHECK(!value);
callback(true);
REQUIRE(calledCount == 1);
CHECK(value);
callback2(false);
REQUIRE(calledCount == 2);
CHECK(!value);
}
TEST_CASE("Can move function", "[function]")
{
bool called = false;
function<void()> callback = [&] {
called = true;
};
auto callback2(std::move(callback));
REQUIRE_THROWS(callback());
REQUIRE(!called);
callback2();
REQUIRE(called);
}
TEST_CASE("Works when copying self", "[function]")
{
bool called = false;
function<void()> callback = [&] {
called = true;
};
callback = callback;
callback();
REQUIRE(called);
}
TEST_CASE("Can release packed function", "[function]")
{
function<int()> iota = [v = 0]() mutable {
return v++;
};
REQUIRE(iota() == 0);
auto packedFn = std::move(iota).release();
REQUIRE_THROWS_AS(iota(), std::bad_function_call);
auto&& proxy = packedFn.get<int()>();
REQUIRE(proxy() == 1);
REQUIRE(proxy() == 2);
}
TEST_CASE("Function copy has its own packed function", "[function]")
{
function<int()> iota = [v = 0]() mutable {
return v++;
};
REQUIRE(iota() == 0);
auto iotaCopy(iota);
REQUIRE(iota() == 1);
REQUIRE(iota() == 2);
REQUIRE(iotaCopy() == 1);
REQUIRE(iotaCopy() == 2);
}
TEST_CASE("can work with callables that have vtable", "[function]")
{
class Base
{
};
class Interface : public Base
{
public:
virtual ~Interface() = default;
virtual void operator()() const = 0;
};
class Class : public Interface
{
public:
Class(bool* destructorCalled)
: m_destructorCalled(destructorCalled)
{
}
~Class()
{
*m_destructorCalled = true;
}
void operator()() const override
{
}
bool* m_destructorCalled = nullptr;
};
bool destructorCalled = false;
{
function<void()> f = Class(&destructorCalled);
f();
auto packed = f.release();
destructorCalled = false;
}
CHECK(destructorCalled);
}
TEST_CASE("can work with callables with virtual inheritance", "[function]")
{
struct A
{
void operator()() const
{
m_called = true;
}
~A()
{
*m_destructorCalled = true;
}
mutable bool m_called = false;
bool* m_destructorCalled = nullptr;
};
struct B : public virtual A
{
};
struct C : public virtual A
{
};
struct D : virtual public B
, virtual public C
{
D(bool* destructorCalled)
{
m_destructorCalled = destructorCalled;
}
using A::operator();
};
bool destructorCalled = false;
{
function<void()> f = D(&destructorCalled);
f();
auto packed = f.release();
destructorCalled = false;
}
CHECK(destructorCalled);
}
TEST_CASE("uses copy constructor if callable's move constructor throws", "[function]")
{
struct Callable
{
Callable() = default;
Callable(Callable&&)
{
throw std::runtime_error("throw");
}
Callable(const Callable& other) = default;
void operator()() const
{
}
};
Callable c;
function<void()> f(c);
auto f2 = std::move(f);
f2();
CHECK_THROWS(f());
}
TEST_CASE("uses move constructor if it is noexcept", "[function]")
{
struct Callable
{
Callable() = default;
Callable(Callable&& other) noexcept = default;
Callable(const Callable&)
{
throw std::runtime_error("throw");
}
void operator()() const
{
}
};
Callable c;
function<void()> f(std::move(c));
auto f2 = std::move(f);
f2();
CHECK_THROWS(f());
}
TEST_CASE("can copy and move empty function", "[function]")
{
function<void()> f;
auto f2 = f;
auto f3 = std::move(f);
}
TEST_CASE("properly copies callable on assignment", "[function]")
{
struct Callable
{
Callable(int& aliveCounter)
: m_aliveCounter(&aliveCounter)
{
++*m_aliveCounter;
}
Callable(const Callable& other)
: m_aliveCounter(other.m_aliveCounter)
{
if (m_aliveCounter)
{
++*m_aliveCounter;
}
}
Callable(Callable&& other) noexcept
: m_aliveCounter(other.m_aliveCounter)
{
other.m_aliveCounter = nullptr;
}
~Callable()
{
if (m_aliveCounter)
{
--*m_aliveCounter;
}
}
void operator()() const
{
}
int* m_aliveCounter = nullptr;
};
int aliveCounter1 = 0;
int aliveCounter2 = 0;
function<void()> f = Callable(aliveCounter1);
function<void()> f2 = Callable(aliveCounter2);
CHECK(aliveCounter1 == 1);
CHECK(aliveCounter2 == 1);
f = f2;
CHECK(aliveCounter1 == 0);
CHECK(aliveCounter2 == 2);
f = function<void()>();
f2 = function<void()>();
CHECK(aliveCounter1 == 0);
CHECK(aliveCounter2 == 0);
}
TEST_CASE("copy assignment operator provides strong exception safety", "[function]")
{
struct State
{
int callCount = 0;
bool throwOnCopy = false;
};
struct Callable
{
Callable(State& state)
: state(&state)
{
}
void operator()()
{
++state->callCount;
}
Callable(const Callable& other)
: state(other.state)
{
if (state->throwOnCopy)
{
throw std::runtime_error("throw on request");
}
}
State* state = nullptr;
};
static_assert(!detail::can_use_inplace_buffer<Callable>);
State srcState;
State dstState;
function<void()> srcFn(Callable{ srcState });
function<void()> dstFn(Callable{ dstState });
srcFn();
dstFn();
REQUIRE(srcState.callCount == 1);
REQUIRE(dstState.callCount == 1);
srcState.throwOnCopy = true;
REQUIRE_THROWS_AS(dstFn = srcFn, std::runtime_error);
// srcFn and dstFn must not be emptied even if assignment throws
REQUIRE_NOTHROW(srcFn());
REQUIRE_NOTHROW(dstFn());
// srcFn and dstFn must keep their state
REQUIRE(srcState.callCount == 2);
REQUIRE(dstState.callCount == 2);
// The next copy will succeed
srcState.throwOnCopy = false;
REQUIRE_NOTHROW(dstFn = srcFn);
// Both functions are usable
REQUIRE_NOTHROW(srcFn());
REQUIRE_NOTHROW(dstFn());
// After assignment, dstFn and srcFn refer the same state - srcState
REQUIRE(srcState.callCount == 4);
REQUIRE(dstState.callCount == 2);
}
TEST_CASE("assignment of variously allocated functions", "[function]")
{
int heapCalls = 0;
auto onHeap = [&heapCalls, largeVar = std::array<std::string, 1000>()]() mutable {
std::fill(largeVar.begin(), largeVar.end(), "large string to be allocated on heap instead of stack");
++heapCalls;
};
int stackCalls = 0;
auto onStack = [&stackCalls] {
++stackCalls;
};
static_assert(detail::can_use_inplace_buffer<detail::function_proxy_impl<decltype(onStack), void>>);
static_assert(!detail::can_use_inplace_buffer<detail::function_proxy_impl<decltype(onHeap), void>>);
using Fn = function<void()>;
{
Fn heap(onHeap);
Fn stack(onStack);
heap = stack;
heap();
REQUIRE(stackCalls == 1);
REQUIRE(heapCalls == 0);
}
{
Fn heap(onHeap);
Fn stack(onStack);
stack = heap;
stack();
REQUIRE(stackCalls == 1);
REQUIRE(heapCalls == 1);
}
{
Fn heap(onHeap);
Fn heap1(onHeap);
heap = heap1;
heap();
REQUIRE(stackCalls == 1);
REQUIRE(heapCalls == 2);
}
{
Fn stack(onStack);
Fn stack1(onStack);
stack = stack1;
stack();
REQUIRE(stackCalls == 2);
REQUIRE(heapCalls == 2);
}
{
Fn heap(onHeap);
Fn empty;
heap = empty;
REQUIRE_THROWS(heap());
REQUIRE(stackCalls == 2);
REQUIRE(heapCalls == 2);
}
{
Fn stack(onStack);
Fn empty;
stack = empty;
REQUIRE_THROWS(stack());
REQUIRE(stackCalls == 2);
REQUIRE(heapCalls == 2);
}
{
Fn empty;
Fn heap(onHeap);
empty = heap;
empty();
REQUIRE(stackCalls == 2);
REQUIRE(heapCalls == 3);
}
{
Fn empty;
Fn stack(onStack);
empty = stack;
empty();
REQUIRE(stackCalls == 3);
REQUIRE(heapCalls == 3);
}
{
Fn empty;
Fn empty1;
empty = empty1;
REQUIRE_THROWS(empty());
REQUIRE(stackCalls == 3);
REQUIRE(heapCalls == 3);
}
}
@@ -0,0 +1,132 @@
#include "catch2/catch.hpp"
#include <fastsignals/bind_weak.h>
using namespace fastsignals;
namespace
{
class Testbed
{
public:
Testbed(unsigned& counter)
: m_pCounter(&counter)
{
}
void IncrementNonConst()
{
++(*m_pCounter);
}
void IncrementsConst() const
{
++(*m_pCounter);
}
int ReflectInt(int value) const
{
return value;
}
private:
unsigned* m_pCounter = nullptr;
};
} // namespace
TEST_CASE("can bind const methods", "[bind_weak]")
{
unsigned counter = 0;
auto pSharedBed = std::make_shared<Testbed>(counter);
auto boundFn = bind_weak(&Testbed::IncrementNonConst, pSharedBed);
REQUIRE(counter == 0u);
boundFn();
REQUIRE(counter == 1u);
boundFn();
REQUIRE(counter == 2u);
pSharedBed = nullptr;
boundFn();
REQUIRE(counter == 2u);
boundFn();
REQUIRE(counter == 2u);
}
TEST_CASE("can bind non const methods", "[bind_weak]")
{
unsigned counter = 0;
auto pSharedBed = std::make_shared<Testbed>(counter);
auto boundFn = bind_weak(&Testbed::IncrementsConst, pSharedBed);
REQUIRE(counter == 0u);
boundFn();
REQUIRE(counter == 1u);
boundFn();
REQUIRE(counter == 2u);
pSharedBed = nullptr;
boundFn();
REQUIRE(counter == 2u);
boundFn();
REQUIRE(counter == 2u);
}
TEST_CASE("can bind method with argument value", "[bind_weak]")
{
unsigned counter = 0;
auto pSharedBed = std::make_shared<Testbed>(counter);
auto boundFn = bind_weak(&Testbed::ReflectInt, pSharedBed, 42);
REQUIRE(boundFn() == 42);
REQUIRE(boundFn() == 42);
pSharedBed = nullptr;
REQUIRE(boundFn() == 0);
REQUIRE(boundFn() == 0);
}
TEST_CASE("copies value when bind method with argument const reference value", "[bind_weak]")
{
unsigned counter = 0;
auto pSharedBed = std::make_shared<Testbed>(counter);
auto makeBoundFn = [&]() {
int value = 15;
const int& valueRef = value;
auto result = bind_weak(&Testbed::ReflectInt, pSharedBed, valueRef);
value = 25;
return result;
};
auto boundFn = makeBoundFn();
REQUIRE(boundFn(42) == 15);
REQUIRE(boundFn(42) == 15);
pSharedBed = nullptr;
REQUIRE(boundFn(42) == 0);
REQUIRE(boundFn(42) == 0);
}
TEST_CASE("copies value when bind method with argument reference value", "[bind_weak]")
{
unsigned counter = 0;
auto pSharedBed = std::make_shared<Testbed>(counter);
auto makeBoundFn = [&]() {
int value = 15;
int& valueRef = value;
auto result = bind_weak(&Testbed::ReflectInt, pSharedBed, valueRef);
valueRef = 25;
return result;
};
auto boundFn = makeBoundFn();
REQUIRE(boundFn(42) == 15);
REQUIRE(boundFn(42) == 15);
pSharedBed = nullptr;
REQUIRE(boundFn(42) == 0);
REQUIRE(boundFn(42) == 0);
}
TEST_CASE("can bind method with placeholder", "[bind_weak]")
{
unsigned counter = 0;
auto pSharedBed = std::make_shared<Testbed>(counter);
auto boundFn = bind_weak(&Testbed::ReflectInt, pSharedBed, std::placeholders::_1);
REQUIRE(boundFn(42) == 42);
REQUIRE(boundFn(42) == 42);
pSharedBed = nullptr;
REQUIRE(boundFn(42) == 0);
REQUIRE(boundFn(42) == 0);
}
@@ -0,0 +1,2 @@
#define CATCH_CONFIG_MAIN
#include "catch2/catch.hpp"
@@ -0,0 +1,707 @@
#include "catch2/catch.hpp"
#include <fastsignals/signal.h>
#include <string>
using namespace fastsignals;
using namespace std::literals;
namespace
{
template <class T>
class any_of_combiner
{
public:
static_assert(std::is_same_v<T, bool>);
using result_type = bool;
template <class TRef>
void operator()(TRef&& value)
{
m_result = m_result || bool(value);
}
result_type get_value() const
{
return m_result;
}
private:
result_type m_result = {};
};
} // namespace
TEST_CASE("Can connect a few slots and emit", "[signal]")
{
signal<void(int)> valueChanged;
int value1 = 0;
int value2 = 0;
valueChanged.connect([&value1](int value) {
value1 = value;
});
valueChanged.connect([&value2](int value) {
value2 = value;
});
REQUIRE(value1 == 0);
REQUIRE(value2 == 0);
valueChanged(10);
REQUIRE(value1 == 10);
REQUIRE(value2 == 10);
}
TEST_CASE("Can safely pass rvalues", "[signal]")
{
const std::string expected = "If the type T is a reference type, provides the member typedef type which is the type referred to by T. Otherwise type is T.";
std::string passedValue = expected;
signal<void(std::string)> valueChanged;
std::string value1;
std::string value2;
valueChanged.connect([&value1](std::string value) {
value1 = value;
});
valueChanged.connect([&value2](std::string value) {
value2 = value;
});
valueChanged(std::move(passedValue));
REQUIRE(value1 == expected);
REQUIRE(value2 == expected);
}
TEST_CASE("Can pass mutable ref", "[signal]")
{
const std::string expected = "If the type T is a reference type, provides the member typedef type which is the type referred to by T. Otherwise type is T.";
signal<void(std::string&)> valueChanged;
std::string passedValue;
valueChanged.connect([expected](std::string& value) {
value = expected;
});
valueChanged(passedValue);
REQUIRE(passedValue == expected);
}
TEST_CASE("Can disconnect slot with explicit call", "[signal]")
{
signal<void(int)> valueChanged;
int value1 = 0;
int value2 = 0;
int value3 = 0;
auto conn1 = valueChanged.connect([&value1](int value) {
value1 = value;
});
auto conn2 = valueChanged.connect([&value2](int value) {
value2 = value;
});
valueChanged.connect([&value3](int value) {
value3 = value;
});
REQUIRE(value1 == 0);
REQUIRE(value2 == 0);
REQUIRE(value3 == 0);
valueChanged(10);
REQUIRE(value1 == 10);
REQUIRE(value2 == 10);
REQUIRE(value3 == 10);
conn2.disconnect();
valueChanged(-99);
REQUIRE(value1 == -99);
REQUIRE(value2 == 10);
REQUIRE(value3 == -99);
conn1.disconnect();
valueChanged(17);
REQUIRE(value1 == -99);
REQUIRE(value2 == 10);
REQUIRE(value3 == 17);
}
TEST_CASE("Can disconnect slot with scoped_connection", "[signal]")
{
signal<void(int)> valueChanged;
int value1 = 0;
int value2 = 0;
int value3 = 0;
{
scoped_connection conn1 = valueChanged.connect([&value1](int value) {
value1 = value;
});
{
scoped_connection conn2 = valueChanged.connect([&value2](int value) {
value2 = value;
});
valueChanged.connect([&value3](int value) {
value3 = value;
});
REQUIRE(value1 == 0);
REQUIRE(value2 == 0);
REQUIRE(value3 == 0);
valueChanged(10);
REQUIRE(value1 == 10);
REQUIRE(value2 == 10);
REQUIRE(value3 == 10);
}
// conn2 disconnected.
valueChanged(-99);
REQUIRE(value1 == -99);
REQUIRE(value2 == 10);
REQUIRE(value3 == -99);
}
// conn1 disconnected.
valueChanged(17);
REQUIRE(value1 == -99);
REQUIRE(value2 == 10);
REQUIRE(value3 == 17);
}
TEST_CASE("Can disconnect all", "[signal]")
{
signal<void(int)> valueChanged;
int value1 = 0;
int value2 = 0;
int value3 = 0;
valueChanged.connect([&value1](int value) {
value1 = value;
});
valueChanged.connect([&value2](int value) {
value2 = value;
});
valueChanged.connect([&value3](int value) {
value3 = value;
});
REQUIRE(value1 == 0);
REQUIRE(value2 == 0);
REQUIRE(value3 == 0);
valueChanged(63);
REQUIRE(value1 == 63);
REQUIRE(value2 == 63);
REQUIRE(value3 == 63);
valueChanged.disconnect_all_slots();
valueChanged(101);
REQUIRE(value1 == 63);
REQUIRE(value2 == 63);
REQUIRE(value3 == 63);
}
TEST_CASE("Can disconnect inside slot", "[signal]")
{
signal<void(int)> valueChanged;
int value1 = 0;
int value2 = 0;
int value3 = 0;
connection conn2;
valueChanged.connect([&value1](int value) {
value1 = value;
});
conn2 = valueChanged.connect([&](int value) {
value2 = value;
conn2.disconnect();
});
valueChanged.connect([&value3](int value) {
value3 = value;
});
REQUIRE(value1 == 0);
REQUIRE(value2 == 0);
REQUIRE(value3 == 0);
valueChanged(63);
REQUIRE(value1 == 63);
REQUIRE(value2 == 63);
REQUIRE(value3 == 63);
valueChanged(101);
REQUIRE(value1 == 101);
REQUIRE(value2 == 63); // disconnected in slot.
REQUIRE(value3 == 101);
}
TEST_CASE("Disconnects OK if signal dead first", "[signal]")
{
connection conn2;
{
scoped_connection conn1;
{
signal<void(int)> valueChanged;
conn2 = valueChanged.connect([](int) {
});
// Just unused.
valueChanged.connect([](int) {
});
conn1 = valueChanged.connect([](int) {
});
}
REQUIRE(conn2.connected());
REQUIRE(conn1.connected());
conn2.disconnect();
REQUIRE(!conn2.connected());
REQUIRE(conn1.connected());
}
conn2.disconnect();
}
TEST_CASE("Returns last called slot result with default combiner", "[signal]")
{
connection conn2;
{
scoped_connection conn1;
{
signal<int(int)> absSignal;
conn2 = absSignal.connect([](int value) {
return value * value;
});
conn1 = absSignal.connect([](int value) {
return abs(value);
});
absSignal(-1);
REQUIRE(absSignal(45) == 45);
REQUIRE(absSignal(-1) == 1);
REQUIRE(absSignal(-177) == 177);
REQUIRE(absSignal(0) == 0);
}
REQUIRE(conn2.connected());
conn2.disconnect();
REQUIRE(!conn2.connected());
}
conn2.disconnect();
REQUIRE(!conn2.connected());
}
TEST_CASE("Works with custom any_of combiner", "[signal]")
{
using cancellable_signal = signal<bool(std::string), any_of_combiner>;
cancellable_signal startRequested;
auto conn1 = startRequested.connect([](std::string op) {
return op == "1";
});
auto conn2 = startRequested.connect([](std::string op) {
return op == "1" || op == "2";
});
REQUIRE(startRequested("0") == false);
REQUIRE(startRequested("1") == true);
REQUIRE(startRequested("2") == true);
REQUIRE(startRequested("3") == false);
conn1.disconnect();
conn2.disconnect();
REQUIRE(startRequested("0") == false);
REQUIRE(startRequested("1") == false);
REQUIRE(startRequested("2") == false);
REQUIRE(startRequested("3") == false);
}
TEST_CASE("Can release scoped connection", "[signal]")
{
int value2 = 0;
int value3 = 0;
signal<void(int)> valueChanged;
connection conn1;
{
scoped_connection conn2;
scoped_connection conn3;
conn2 = valueChanged.connect([&value2](int x) {
value2 = x;
});
conn3 = valueChanged.connect([&value3](int x) {
value3 = x;
});
valueChanged(42);
REQUIRE(value2 == 42);
REQUIRE(value3 == 42);
REQUIRE(conn2.connected());
REQUIRE(conn3.connected());
REQUIRE(!conn1.connected());
conn1 = conn3.release();
REQUIRE(conn2.connected());
REQUIRE(!conn3.connected());
REQUIRE(conn1.connected());
valueChanged(144);
REQUIRE(value2 == 144);
REQUIRE(value3 == 144);
}
// conn2 disconnected, conn1 connected.
valueChanged(17);
REQUIRE(value2 == 144);
REQUIRE(value3 == 17);
REQUIRE(conn1.connected());
conn1.disconnect();
valueChanged(90);
REQUIRE(value2 == 144);
REQUIRE(value3 == 17);
}
TEST_CASE("Can use signal with more than one argument", "[signal]")
{
signal<void(int, std::string, std::vector<std::string>)> event;
int value1 = 0;
std::string value2;
std::vector<std::string> value3;
event.connect([&](int v1, const std::string& v2, const std::vector<std::string>& v3) {
value1 = v1;
value2 = v2;
value3 = v3;
});
event(9815, "using namespace std::literals!"s, std::vector{ "std::vector"s, "using namespace std::literals"s });
REQUIRE(value1 == 9815);
REQUIRE(value2 == "using namespace std::literals!"s);
REQUIRE(value3 == std::vector{ "std::vector"s, "using namespace std::literals"s });
}
TEST_CASE("Can blocks slots using shared_connection_block", "[signal]")
{
bool callbackShouldBeCalled = true;
bool callbackCalled = false;
const int value = 123;
signal<void(int)> event;
auto conn = event.connect([&](int gotValue) {
CHECK(gotValue == value);
callbackCalled = true;
if (!callbackShouldBeCalled)
{
FAIL("callback is blocked and should not be called");
}
},
advanced_tag{});
event(value);
REQUIRE(callbackCalled);
shared_connection_block block(conn);
callbackShouldBeCalled = false;
callbackCalled = false;
event(value);
REQUIRE(!callbackCalled);
block.unblock();
callbackShouldBeCalled = true;
event(value);
REQUIRE(callbackCalled);
}
TEST_CASE("Other slots are unaffected by the block", "[signal]")
{
bool callback1Called = false;
bool callback2Called = false;
const int value = 123;
signal<void(int)> event;
auto conn1 = event.connect([&](int gotValue) {
CHECK(gotValue == value);
callback1Called = true;
},
advanced_tag{});
auto conn2 = event.connect([&](int) {
callback2Called = true;
FAIL("callback is blocked and should not be called");
},
advanced_tag{});
shared_connection_block block(conn2);
event(value);
REQUIRE(callback1Called);
REQUIRE(!callback2Called);
}
TEST_CASE("Multiple blocks block until last one is unblocked", "[signal]")
{
bool callbackShouldBeCalled = false;
bool callbackCalled = false;
const int value = 123;
signal<void(int)> event;
auto conn = event.connect([&](int gotValue) {
CHECK(gotValue == value);
callbackCalled = true;
if (!callbackShouldBeCalled)
{
FAIL("callback is blocked and should not be called");
}
},
advanced_tag{});
shared_connection_block block1(conn);
shared_connection_block block2(conn);
event(value);
REQUIRE(!callbackCalled);
block1.unblock();
event(value);
REQUIRE(!callbackCalled);
block1.block();
block2.unblock();
event(value);
REQUIRE(!callbackCalled);
block1.unblock();
callbackShouldBeCalled = true;
event(value);
REQUIRE(callbackCalled);
}
TEST_CASE("Can copy and move shared_connection_block objects", "[signal]")
{
bool callbackShouldBeCalled = false;
bool callbackCalled = false;
const int value = 123;
signal<void(int)> event;
auto conn = event.connect([&](int gotValue) {
CHECK(gotValue == value);
callbackCalled = true;
if (!callbackShouldBeCalled)
{
FAIL("callback is blocked and should not be called");
}
},
advanced_tag{});
shared_connection_block block1(conn);
shared_connection_block block2(block1);
event(value);
REQUIRE(block1.blocking());
REQUIRE(block2.blocking());
REQUIRE(!callbackCalled);
shared_connection_block block3(std::move(block2));
event(value);
REQUIRE(block1.blocking());
REQUIRE(!block2.blocking());
REQUIRE(block3.blocking());
REQUIRE(!callbackCalled);
block2 = block3;
event(value);
REQUIRE(block1.blocking());
REQUIRE(block2.blocking());
REQUIRE(block3.blocking());
REQUIRE(!callbackCalled);
block3 = std::move(block2);
event(value);
REQUIRE(block1.blocking());
REQUIRE(!block2.blocking());
REQUIRE(block3.blocking());
REQUIRE(!callbackCalled);
block3 = shared_connection_block(conn, false);
event(value);
REQUIRE(block1.blocking());
REQUIRE(!block2.blocking());
REQUIRE(!block3.blocking());
REQUIRE(!callbackCalled);
block1.unblock();
callbackShouldBeCalled = true;
event(value);
REQUIRE(!block1.blocking());
REQUIRE(!block2.blocking());
REQUIRE(!block3.blocking());
REQUIRE(callbackCalled);
}
TEST_CASE("Unblocks when shared_connection_block goes out of scope")
{
bool callbackCalled = false;
const int value = 123;
signal<void(int)> event;
auto conn = event.connect([&](int gotValue) {
CHECK(gotValue == value);
callbackCalled = true;
},
advanced_tag{});
callbackCalled = false;
event(value);
CHECK(callbackCalled);
{
callbackCalled = false;
shared_connection_block block(conn);
event(value);
CHECK(!callbackCalled);
{
callbackCalled = false;
shared_connection_block block2(conn);
event(value);
CHECK(!callbackCalled);
}
}
callbackCalled = false;
event(value);
CHECK(callbackCalled);
}
TEST_CASE("Can disconnect advanced slot using advanced_scoped_connection", "[signal]")
{
signal<void(int)> valueChanged;
int value1 = 0;
int value2 = 0;
int value3 = 0;
{
advanced_scoped_connection conn1 = valueChanged.connect([&value1](int value) {
value1 = value;
},
advanced_tag{});
{
advanced_scoped_connection conn2 = valueChanged.connect([&value2](int value) {
value2 = value;
},
advanced_tag{});
valueChanged.connect([&value3](int value) {
value3 = value;
});
REQUIRE(value1 == 0);
REQUIRE(value2 == 0);
REQUIRE(value3 == 0);
valueChanged(10);
REQUIRE(value1 == 10);
REQUIRE(value2 == 10);
REQUIRE(value3 == 10);
}
// conn2 disconnected.
valueChanged(-99);
REQUIRE(value1 == -99);
REQUIRE(value2 == 10);
REQUIRE(value3 == -99);
}
// conn1 disconnected.
valueChanged(17);
REQUIRE(value1 == -99);
REQUIRE(value2 == 10);
REQUIRE(value3 == 17);
}
TEST_CASE("Can move signal", "[signal]")
{
signal<void()> src;
int srcFireCount = 0;
auto srcConn = src.connect([&srcFireCount] {
++srcFireCount;
});
src();
REQUIRE(srcFireCount == 1);
auto dst = std::move(src);
int dstFireCount = 0;
auto dstConn = dst.connect([&dstFireCount] {
++dstFireCount;
});
dst();
REQUIRE(srcFireCount == 2);
REQUIRE(dstFireCount == 1);
srcConn.disconnect();
dstConn.disconnect();
dst();
REQUIRE(srcFireCount == 2);
REQUIRE(dstFireCount == 1);
}
TEST_CASE("Can swap signals", "[signal]")
{
signal<void()> s1;
signal<void()> s2;
int s1FireCount = 0;
int s2FireCount = 0;
s1.connect([&s1FireCount] {
++s1FireCount;
});
s2.connect([&s2FireCount] {
++s2FireCount;
});
std::swap(s1, s2);
s1();
REQUIRE(s1FireCount == 0);
REQUIRE(s2FireCount == 1);
s2();
REQUIRE(s1FireCount == 1);
REQUIRE(s2FireCount == 1);
}
TEST_CASE("Signal can be destroyed inside its slot and will call the rest of its slots", "[signal]")
{
std::optional<signal<void()>> s;
s.emplace();
s->connect([&] {
s.reset();
});
bool called = false;
s->connect([&] {
called = true;
});
(*s)();
CHECK(called);
}
TEST_CASE("Signal can be used as a slot for another signal", "[signal]")
{
signal<void()> s1;
bool called = false;
s1.connect([&] {
called = true;
});
signal<void()> s2;
s2.connect(s1);
s2();
CHECK(called);
}
// memory leak fix
TEST_CASE("Releases lambda and its captured const data", "[signal]")
{
struct Captured
{
Captured(bool& released)
: m_released(released)
{
}
~Captured()
{
m_released = true;
}
private:
bool& m_released;
};
bool released = false;
{
const auto captured = std::make_shared<Captured>(released);
signal<void()> changeSignal;
changeSignal.connect([captured]{});
}
CHECK(released);
}
+2 -2
View File
@@ -651,8 +651,8 @@ struct DocTiming {
class DocOpenGuard {
public:
bool &flag;
boost::signals2::signal<void ()> &signal;
DocOpenGuard(bool &f, boost::signals2::signal<void ()> &s)
fastsignals::signal<void ()> &signal;
DocOpenGuard(bool &f, fastsignals::signal<void ()> &s)
:flag(f),signal(s)
{
flag = true;
+45 -44
View File
@@ -26,10 +26,11 @@
#ifndef SRC_APP_APPLICATION_H_
#define SRC_APP_APPLICATION_H_
#include <boost/signals2.hpp>
#include <fastsignals/signal.h>
#include <QtCore/qtextstream.h>
#include <deque>
#include <list>
#include <vector>
#include <list>
#include <set>
@@ -222,49 +223,49 @@ public:
/** @name Signals of the Application */
//@{
/// signal on new Document
boost::signals2::signal<void (const Document&, bool)> signalNewDocument;
fastsignals::signal<void (const Document&, bool)> signalNewDocument;
/// signal on document getting deleted
boost::signals2::signal<void (const Document&)> signalDeleteDocument;
fastsignals::signal<void (const Document&)> signalDeleteDocument;
/// signal on already deleted Document
boost::signals2::signal<void ()> signalDeletedDocument;
fastsignals::signal<void ()> signalDeletedDocument;
/// signal on relabeling Document (user name)
boost::signals2::signal<void (const Document&)> signalRelabelDocument;
fastsignals::signal<void (const Document&)> signalRelabelDocument;
/// signal on renaming Document (internal name)
boost::signals2::signal<void (const Document&)> signalRenameDocument;
fastsignals::signal<void (const Document&)> signalRenameDocument;
/// signal on activating Document
boost::signals2::signal<void (const Document&)> signalActiveDocument;
fastsignals::signal<void (const Document&)> signalActiveDocument;
/// signal on saving Document
boost::signals2::signal<void (const Document&)> signalSaveDocument;
fastsignals::signal<void (const Document&)> signalSaveDocument;
/// signal on starting to restore Document
boost::signals2::signal<void (const Document&)> signalStartRestoreDocument;
fastsignals::signal<void (const Document&)> signalStartRestoreDocument;
/// signal on restoring Document
boost::signals2::signal<void (const Document&)> signalFinishRestoreDocument;
fastsignals::signal<void (const Document&)> signalFinishRestoreDocument;
/// signal on pending reloading of a partial Document
boost::signals2::signal<void (const Document&)> signalPendingReloadDocument;
fastsignals::signal<void (const Document&)> signalPendingReloadDocument;
/// signal on starting to save Document
boost::signals2::signal<void (const Document&, const std::string&)> signalStartSaveDocument;
fastsignals::signal<void (const Document&, const std::string&)> signalStartSaveDocument;
/// signal on saved Document
boost::signals2::signal<void (const Document&, const std::string&)> signalFinishSaveDocument;
fastsignals::signal<void (const Document&, const std::string&)> signalFinishSaveDocument;
/// signal on undo in document
boost::signals2::signal<void (const Document&)> signalUndoDocument;
fastsignals::signal<void (const Document&)> signalUndoDocument;
/// signal on application wide undo
boost::signals2::signal<void ()> signalUndo;
fastsignals::signal<void ()> signalUndo;
/// signal on redo in document
boost::signals2::signal<void (const Document&)> signalRedoDocument;
fastsignals::signal<void (const Document&)> signalRedoDocument;
/// signal on application wide redo
boost::signals2::signal<void ()> signalRedo;
fastsignals::signal<void ()> signalRedo;
/// signal before open active transaction
boost::signals2::signal<void (const std::string&)> signalBeforeOpenTransaction;
fastsignals::signal<void (const std::string&)> signalBeforeOpenTransaction;
/// signal before close/abort active transaction
boost::signals2::signal<void (bool)> signalBeforeCloseTransaction;
fastsignals::signal<void (bool)> signalBeforeCloseTransaction;
/// signal after close/abort active transaction
boost::signals2::signal<void (bool)> signalCloseTransaction;
fastsignals::signal<void (bool)> signalCloseTransaction;
/// signal on show hidden items
boost::signals2::signal<void (const Document&)> signalShowHidden;
fastsignals::signal<void (const Document&)> signalShowHidden;
/// signal on start opening document(s)
boost::signals2::signal<void ()> signalStartOpenDocument;
fastsignals::signal<void ()> signalStartOpenDocument;
/// signal on finished opening document(s)
boost::signals2::signal<void ()> signalFinishOpenDocument;
fastsignals::signal<void ()> signalFinishOpenDocument;
//@}
@@ -274,34 +275,34 @@ public:
*/
//@{
/// signal before change of doc property
boost::signals2::signal<void (const App::Document&, const App::Property&)> signalBeforeChangeDocument;
fastsignals::signal<void (const App::Document&, const App::Property&)> signalBeforeChangeDocument;
/// signal on changed doc property
boost::signals2::signal<void (const App::Document&, const App::Property&)> signalChangedDocument;
fastsignals::signal<void (const App::Document&, const App::Property&)> signalChangedDocument;
/// signal on new Object
boost::signals2::signal<void (const App::DocumentObject&)> signalNewObject;
//boost::signals2::signal<void (const App::DocumentObject&)> m_sig;
fastsignals::signal<void (const App::DocumentObject&)> signalNewObject;
//fastsignals::signal<void (const App::DocumentObject&)> m_sig;
/// signal on deleted Object
boost::signals2::signal<void (const App::DocumentObject&)> signalDeletedObject;
fastsignals::signal<void (const App::DocumentObject&)> signalDeletedObject;
/// signal on changed Object
boost::signals2::signal<void (const App::DocumentObject&, const App::Property&)> signalBeforeChangeObject;
fastsignals::signal<void (const App::DocumentObject&, const App::Property&)> signalBeforeChangeObject;
/// signal on changed Object
boost::signals2::signal<void (const App::DocumentObject&, const App::Property&)> signalChangedObject;
fastsignals::signal<void (const App::DocumentObject&, const App::Property&)> signalChangedObject;
/// signal on relabeled Object
boost::signals2::signal<void (const App::DocumentObject&)> signalRelabelObject;
fastsignals::signal<void (const App::DocumentObject&)> signalRelabelObject;
/// signal on activated Object
boost::signals2::signal<void (const App::DocumentObject&)> signalActivatedObject;
fastsignals::signal<void (const App::DocumentObject&)> signalActivatedObject;
/// signal before recomputed document
boost::signals2::signal<void (const App::Document&)> signalBeforeRecomputeDocument;
fastsignals::signal<void (const App::Document&)> signalBeforeRecomputeDocument;
/// signal on recomputed document
boost::signals2::signal<void (const App::Document&)> signalRecomputed;
fastsignals::signal<void (const App::Document&)> signalRecomputed;
/// signal on recomputed document object
boost::signals2::signal<void (const App::DocumentObject&)> signalObjectRecomputed;
fastsignals::signal<void (const App::DocumentObject&)> signalObjectRecomputed;
// signal on opened transaction
boost::signals2::signal<void (const App::Document&, std::string)> signalOpenTransaction;
fastsignals::signal<void (const App::Document&, std::string)> signalOpenTransaction;
// signal a committed transaction
boost::signals2::signal<void (const App::Document&)> signalCommitTransaction;
fastsignals::signal<void (const App::Document&)> signalCommitTransaction;
// signal an aborted transaction
boost::signals2::signal<void (const App::Document&)> signalAbortTransaction;
fastsignals::signal<void (const App::Document&)> signalAbortTransaction;
//@}
/** @name Signals of property changes
@@ -310,13 +311,13 @@ public:
*/
//@{
/// signal on adding a dynamic property
boost::signals2::signal<void (const App::Property&)> signalAppendDynamicProperty;
fastsignals::signal<void (const App::Property&)> signalAppendDynamicProperty;
/// signal on renaming a dynamic property
boost::signals2::signal<void (const App::Property&, const char*)> signalRenameDynamicProperty;
fastsignals::signal<void (const App::Property&, const char*)> signalRenameDynamicProperty;
/// signal on about removing a dynamic property
boost::signals2::signal<void (const App::Property&)> signalRemoveDynamicProperty;
fastsignals::signal<void (const App::Property&)> signalRemoveDynamicProperty;
/// signal on about changing the editor mode of a property
boost::signals2::signal<void (const App::Document&, const App::Property&)> signalChangePropertyEditor;
fastsignals::signal<void (const App::Document&, const App::Property&)> signalChangePropertyEditor;
//@}
/** @name Signals of extension changes
@@ -326,9 +327,9 @@ public:
*/
//@{
/// signal before adding the extension
boost::signals2::signal<void (const App::ExtensionContainer&, std::string extension)> signalBeforeAddingDynamicExtension;
fastsignals::signal<void (const App::ExtensionContainer&, std::string extension)> signalBeforeAddingDynamicExtension;
/// signal after the extension was added
boost::signals2::signal<void (const App::ExtensionContainer&, std::string extension)> signalAddedDynamicExtension;
fastsignals::signal<void (const App::ExtensionContainer&, std::string extension)> signalAddedDynamicExtension;
//@}
// clang-format off
// NOLINTEND
+32 -32
View File
@@ -165,61 +165,61 @@ public:
//@{
// clang-format off
/// signal before changing an doc property
boost::signals2::signal<void(const Document&, const Property&)> signalBeforeChange;
fastsignals::signal<void(const Document&, const Property&)> signalBeforeChange;
/// signal on changed doc property
boost::signals2::signal<void(const Document&, const Property&)> signalChanged;
fastsignals::signal<void(const Document&, const Property&)> signalChanged;
/// signal on new Object
boost::signals2::signal<void(const DocumentObject&)> signalNewObject;
fastsignals::signal<void(const DocumentObject&)> signalNewObject;
/// signal on deleted Object
boost::signals2::signal<void(const DocumentObject&)> signalDeletedObject;
fastsignals::signal<void(const DocumentObject&)> signalDeletedObject;
/// signal before changing an Object
boost::signals2::signal<void(const DocumentObject&, const Property&)> signalBeforeChangeObject;
fastsignals::signal<void(const DocumentObject&, const Property&)> signalBeforeChangeObject;
/// signal on changed Object
boost::signals2::signal<void(const DocumentObject&, const Property&)> signalChangedObject;
fastsignals::signal<void(const DocumentObject&, const Property&)> signalChangedObject;
/// signal on manually called DocumentObject::touch()
boost::signals2::signal<void(const DocumentObject&)> signalTouchedObject;
fastsignals::signal<void(const DocumentObject&)> signalTouchedObject;
/// signal on relabeled Object
boost::signals2::signal<void(const DocumentObject&)> signalRelabelObject;
fastsignals::signal<void(const DocumentObject&)> signalRelabelObject;
/// signal on activated Object
boost::signals2::signal<void(const DocumentObject&)> signalActivatedObject;
fastsignals::signal<void(const DocumentObject&)> signalActivatedObject;
/// signal on created object
boost::signals2::signal<void(const DocumentObject&, Transaction*)> signalTransactionAppend;
fastsignals::signal<void(const DocumentObject&, Transaction*)> signalTransactionAppend;
/// signal on removed object
boost::signals2::signal<void(const DocumentObject&, Transaction*)> signalTransactionRemove;
fastsignals::signal<void(const DocumentObject&, Transaction*)> signalTransactionRemove;
/// signal on undo
boost::signals2::signal<void(const Document&)> signalUndo;
fastsignals::signal<void(const Document&)> signalUndo;
/// signal on redo
boost::signals2::signal<void(const Document&)> signalRedo;
fastsignals::signal<void(const Document&)> signalRedo;
/** signal on load/save document
* this signal is given when the document gets streamed.
* you can use this hook to write additional information in
* the file (like the Gui::Document does).
*/
boost::signals2::signal<void(Base::Writer&)> signalSaveDocument;
boost::signals2::signal<void(Base::XMLReader&)> signalRestoreDocument;
boost::signals2::signal<void(const std::vector<DocumentObject*>&, Base::Writer&)> signalExportObjects;
boost::signals2::signal<void(const std::vector<DocumentObject*>&, Base::Writer&)> signalExportViewObjects;
boost::signals2::signal<void(const std::vector<DocumentObject*>&, Base::XMLReader&)> signalImportObjects;
boost::signals2::signal<void(const std::vector<DocumentObject*>&, Base::Reader&,
fastsignals::signal<void(Base::Writer&)> signalSaveDocument;
fastsignals::signal<void(Base::XMLReader&)> signalRestoreDocument;
fastsignals::signal<void(const std::vector<DocumentObject*>&, Base::Writer&)> signalExportObjects;
fastsignals::signal<void(const std::vector<DocumentObject*>&, Base::Writer&)> signalExportViewObjects;
fastsignals::signal<void(const std::vector<DocumentObject*>&, Base::XMLReader&)> signalImportObjects;
fastsignals::signal<void(const std::vector<DocumentObject*>&, Base::Reader&,
const std::map<std::string, std::string>&)> signalImportViewObjects;
boost::signals2::signal<void(const std::vector<DocumentObject*>&)> signalFinishImportObjects;
fastsignals::signal<void(const std::vector<DocumentObject*>&)> signalFinishImportObjects;
// signal starting a save action to a file
boost::signals2::signal<void(const Document&, const std::string&)> signalStartSave;
fastsignals::signal<void(const Document&, const std::string&)> signalStartSave;
// signal finishing a save action to a file
boost::signals2::signal<void(const Document&, const std::string&)> signalFinishSave;
boost::signals2::signal<void(const Document&)> signalBeforeRecompute;
boost::signals2::signal<void(const Document&, const std::vector<DocumentObject*>&)> signalRecomputed;
boost::signals2::signal<void(const DocumentObject&)> signalRecomputedObject;
fastsignals::signal<void(const Document&, const std::string&)> signalFinishSave;
fastsignals::signal<void(const Document&)> signalBeforeRecompute;
fastsignals::signal<void(const Document&, const std::vector<DocumentObject*>&)> signalRecomputed;
fastsignals::signal<void(const DocumentObject&)> signalRecomputedObject;
// signal a new opened transaction
boost::signals2::signal<void(const Document&, std::string)> signalOpenTransaction;
fastsignals::signal<void(const Document&, std::string)> signalOpenTransaction;
// signal a committed transaction
boost::signals2::signal<void(const Document&)> signalCommitTransaction;
fastsignals::signal<void(const Document&)> signalCommitTransaction;
// signal an aborted transaction
boost::signals2::signal<void(const Document&)> signalAbortTransaction;
boost::signals2::signal<void(const Document&, const std::vector<DocumentObject*>&)> signalSkipRecompute;
boost::signals2::signal<void(const DocumentObject&)> signalFinishRestoreObject;
boost::signals2::signal<void(const Document&, const Property&)> signalChangePropertyEditor;
boost::signals2::signal<void(std::string)> signalLinkXsetValue;
fastsignals::signal<void(const Document&)> signalAbortTransaction;
fastsignals::signal<void(const Document&, const std::vector<DocumentObject*>&)> signalSkipRecompute;
fastsignals::signal<void(const DocumentObject&)> signalFinishRestoreObject;
fastsignals::signal<void(const Document&, const Property&)> signalChangePropertyEditor;
fastsignals::signal<void(std::string)> signalLinkXsetValue;
// clang-format on
//@}
// NOLINTEND
+3 -3
View File
@@ -129,11 +129,11 @@ public:
// clang-format off
/// signal before changing a property of this object
boost::signals2::signal<void(const App::DocumentObject&, const App::Property&)> signalBeforeChange;
fastsignals::signal<void(const App::DocumentObject&, const App::Property&)> signalBeforeChange;
/// signal on changed property of this object
boost::signals2::signal<void(const App::DocumentObject&, const App::Property&)> signalChanged;
fastsignals::signal<void(const App::DocumentObject&, const App::Property&)> signalChanged;
/// signal on changed property of this object before document scoped signalChangedObject
boost::signals2::signal<void(const App::DocumentObject&, const App::Property&)> signalEarlyChanged;
fastsignals::signal<void(const App::DocumentObject&, const App::Property&)> signalEarlyChanged;
// clang-format on
/// returns the type name of the ViewProvider
+2 -2
View File
@@ -709,7 +709,7 @@ public:
}
App::Document* _document;
using Connection = boost::signals2::scoped_connection;
using Connection = fastsignals::scoped_connection;
Connection connectApplicationDeletedDocument;
};
@@ -800,7 +800,7 @@ public:
App::DocumentObject* object;
bool indocument {false};
using Connection = boost::signals2::scoped_connection;
using Connection = fastsignals::scoped_connection;
Connection connectApplicationDeletedDocument;
Connection connectDocumentCreatedObject;
Connection connectDocumentDeletedObject;
+2 -2
View File
@@ -28,7 +28,7 @@
#include <Base/BaseClass.h>
#include <Base/Bitmask.h>
#include <boost/signals2.hpp>
#include <fastsignals/signal.h>
#include <memory>
#include <set>
#include <FCGlobal.h>
@@ -559,7 +559,7 @@ protected:
private:
App::Document* _document;
using Connection = boost::signals2::connection;
using Connection = fastsignals::connection;
Connection connectApplicationCreatedDocument;
Connection connectApplicationDeletedDocument;
Connection connectApplicationActivateDocument;
+2 -2
View File
@@ -27,7 +27,7 @@
#define APP_DOCUMENTOBSERVERPYTHON_H
#include <FCGlobal.h>
#include <boost/signals2.hpp>
#include <fastsignals/signal.h>
#include <CXX/Objects.hxx>
#include <string>
#include <vector>
@@ -125,7 +125,7 @@ private:
using Connection = struct PythonObject
{
boost::signals2::scoped_connection slot;
fastsignals::scoped_connection slot;
Py::Object py;
PyObject* ptr()
{
+1 -1
View File
@@ -154,7 +154,7 @@ private:
// for tracking children visibility
void slotChildChanged(const App::DocumentObject&, const App::Property&);
std::unordered_map<const App::DocumentObject*, boost::signals2::scoped_connection> _Conns;
std::unordered_map<const App::DocumentObject*, fastsignals::scoped_connection> _Conns;
};
+1 -1
View File
@@ -797,7 +797,7 @@ void LinkBaseExtension::setupCopyOnChange(DocumentObject* parent, bool checkSour
bool LinkBaseExtension::setupCopyOnChange(
DocumentObject* parent,
DocumentObject* linked,
std::vector<boost::signals2::scoped_connection>* copyOnChangeConns,
std::vector<fastsignals::scoped_connection>* copyOnChangeConns,
bool checkExisting)
{
if (!parent || !linked) {
+5 -5
View File
@@ -420,7 +420,7 @@ public:
static bool
setupCopyOnChange(App::DocumentObject* obj,
App::DocumentObject* linked,
std::vector<boost::signals2::scoped_connection>* copyOnChangeConns,
std::vector<fastsignals::scoped_connection>* copyOnChangeConns,
bool checkExisting);
static bool isCopyOnChangeProperty(App::DocumentObject* obj, const Property& prop);
@@ -483,7 +483,7 @@ protected:
mutable std::vector<std::string> mySubElements;
mutable std::string mySubName;
std::unordered_map<const App::DocumentObject*, boost::signals2::scoped_connection>
std::unordered_map<const App::DocumentObject*, fastsignals::scoped_connection>
plainGroupConns;
long prevLinkedObjectID = 0;
@@ -492,14 +492,14 @@ protected:
mutable bool enableLabelCache {false};
bool hasOldSubElement {false};
std::vector<boost::signals2::scoped_connection> copyOnChangeConns;
std::vector<boost::signals2::scoped_connection> copyOnChangeSrcConns;
std::vector<fastsignals::scoped_connection> copyOnChangeConns;
std::vector<fastsignals::scoped_connection> copyOnChangeSrcConns;
bool hasCopyOnChange {true};
mutable bool checkingProperty = false;
bool pauseCopyOnChange = false;
boost::signals2::scoped_connection connCopyOnChangeSource;
fastsignals::scoped_connection connCopyOnChangeSource;
};
///////////////////////////////////////////////////////////////////////////
+2 -2
View File
@@ -27,7 +27,7 @@
#define APP_MERGEDOCUMENTS_H
#include <Base/Persistence.h>
#include <boost/signals2.hpp>
#include <fastsignals/signal.h>
namespace zipios
{
@@ -72,7 +72,7 @@ private:
App::Document* appdoc {nullptr};
std::vector<App::DocumentObject*> objects;
std::map<std::string, std::string> nameMap;
using Connection = boost::signals2::connection;
using Connection = fastsignals::connection;
Connection connectExport;
Connection connectImport;
};
+1
View File
@@ -27,6 +27,7 @@
#define APP_PATH_H
#include <bitset>
#include <climits>
#include <map>
#include <limits>
#include <set>
+1 -1
View File
@@ -74,7 +74,7 @@
// Boost
#include <boost_graph_adjacency_list.hpp>
#include <boost/signals2.hpp>
#include <fastsignals/signal.h>
#include <boost/program_options.hpp>
#include <boost/regex.hpp>
+2 -2
View File
@@ -29,7 +29,7 @@
#include <Base/Exception.h>
#include <Base/Persistence.h>
#include <boost/any.hpp>
#include <boost/signals2.hpp>
#include <fastsignals/signal.h>
#include <bitset>
#include <string>
#include <FCGlobal.h>
@@ -608,7 +608,7 @@ private:
public:
/// Signal emitted when the property value has changed.
boost::signals2::signal<void(const App::Property&)> signalChanged;
fastsignals::signal<void(const App::Property&)> signalChanged;
};
+1 -1
View File
@@ -91,7 +91,7 @@ struct PropertyExpressionEngine::Private
{
// For some reason, MSVC has trouble with vector of scoped_connection if
// defined in header, hence the private structure here.
std::vector<boost::signals2::scoped_connection> conns;
std::vector<fastsignals::scoped_connection> conns;
std::unordered_map<std::string, std::vector<ObjectIdentifier>> propMap;
};
+2 -2
View File
@@ -29,7 +29,7 @@
#include <set>
#include <boost/unordered/unordered_map.hpp>
#include <boost/signals2.hpp>
#include <fastsignals/signal.h>
#include <boost_graph_adjacency_list.hpp>
#include <boost/graph/topological_sort.hpp>
@@ -184,7 +184,7 @@ public:
size_t numExpressions() const;
/// signal called when an expression was changed
boost::signals2::signal<void(const App::ObjectIdentifier&)> expressionChanged;
fastsignals::signal<void(const App::ObjectIdentifier&)> expressionChanged;
void afterRestore() override;
void onContainerRestored() override;
+1 -1
View File
@@ -3304,7 +3304,7 @@ DocInfoMap _DocInfoMap;
class App::DocInfo: public std::enable_shared_from_this<App::DocInfo>
{
public:
using Connection = boost::signals2::scoped_connection;
using Connection = fastsignals::scoped_connection;
Connection connFinishRestoreDocument;
Connection connPendingReloadDocument;
Connection connDeleteDocument;
+1 -1
View File
@@ -618,7 +618,7 @@ public:
void setSilentRestore(bool enable);
boost::signals2::signal<void(const std::string&, const std::string&)>
fastsignals::signal<void(const std::string&, const std::string&)>
signalUpdateElementReference;
protected:
+3 -3
View File
@@ -53,12 +53,12 @@ const char* TextDocument::getViewProviderName() const
return "Gui::ViewProviderTextDocument";
}
boost::signals2::connection TextDocument::connectText(const TextSlot& sub)
fastsignals::advanced_connection TextDocument::connectText(const TextSlot& sub)
{
return textChanged.connect(sub);
return textChanged.connect(sub, fastsignals::advanced_tag());
}
boost::signals2::connection TextDocument::connectLabel(const TextSlot& sub)
fastsignals::connection TextDocument::connectLabel(const TextSlot& sub)
{
return labelChanged.connect(sub);
}
+3 -3
View File
@@ -38,7 +38,7 @@ class AppExport TextDocument: public App::DocumentObject
PROPERTY_HEADER_WITH_OVERRIDE(App::TextDocument);
public:
using TextSignal = boost::signals2::signal<void()>;
using TextSignal = fastsignals::signal<void()>;
using TextSlot = TextSignal::slot_type;
PropertyString Text;
@@ -49,8 +49,8 @@ public:
void onChanged(const Property* prop) override;
const char* getViewProviderName() const override;
boost::signals2::connection connectText(const TextSlot& sub);
boost::signals2::connection connectLabel(const TextSlot& sub);
fastsignals::advanced_connection connectText(const TextSlot& sub);
fastsignals::connection connectLabel(const TextSlot& sub);
private:
TextSignal textChanged;
+1 -1
View File
@@ -84,7 +84,7 @@ endif()
list(APPEND FreeCADBase_LIBS ${QtCore_LIBRARIES})
list(APPEND FreeCADBase_LIBS fmt::fmt)
list(APPEND FreeCADBase_LIBS libfastsignals fmt::fmt)
if (BUILD_DYNAMIC_LINK_PYTHON)
list(APPEND FreeCADBase_LIBS ${Python3_LIBRARIES})
+2 -2
View File
@@ -51,7 +51,7 @@ using PyObject = struct _object;
#include <map>
#include <vector>
#include <boost/signals2.hpp>
#include <fastsignals/signal.h>
#include <xercesc/util/XercesDefs.hpp>
#include "Handle.h"
@@ -565,7 +565,7 @@ public:
* - Group removal: both 'name' and 'value' are empty
* - Group rename: 'name' is the new name, and 'value' is the old name
*/
boost::signals2::signal<
fastsignals::signal<
void(ParameterGrp* /*param*/, ParamType /*type*/, const char* /*name*/, const char* /*value*/)>
signalParamChanged;
+1 -1
View File
@@ -92,7 +92,7 @@ public:
public:
// NOLINTBEGIN
Py::Object callable;
boost::signals2::scoped_connection conn;
fastsignals::scoped_connection conn;
ParameterGrp* _target = nullptr; // no reference counted, do not access
// NOLINTEND
+4 -15
View File
@@ -29,22 +29,13 @@
#ifndef FC_GLOBAL_H
# include <FCGlobal.h>
#endif
#include <algorithm>
#include <cmath>
#include <numbers>
#include <ostream>
#include <string>
#include <vector>
#include <boost/signals2/shared_connection_block.hpp>
namespace boost
{
namespace signals2
{
class connection;
}
} // namespace boost
#include <fastsignals/signal.h>
class QString;
@@ -332,12 +323,10 @@ private:
class ConnectionBlocker
{
using Connection = boost::signals2::connection;
using ConnectionBlock = boost::signals2::shared_connection_block;
ConnectionBlock blocker;
fastsignals::shared_connection_block blocker;
public:
ConnectionBlocker(Connection& c)
ConnectionBlocker(fastsignals::advanced_connection& c)
: blocker(c)
{}
~ConnectionBlocker() = default;
+19 -19
View File
@@ -118,43 +118,43 @@ public:
/** @name Signals of the Application */
//@{
/// signal on new Document
boost::signals2::signal<void(const Gui::Document&, bool)> signalNewDocument;
fastsignals::signal<void(const Gui::Document&, bool)> signalNewDocument;
/// signal on deleted Document
boost::signals2::signal<void(const Gui::Document&)> signalDeleteDocument;
fastsignals::signal<void(const Gui::Document&)> signalDeleteDocument;
/// signal on relabeling Document
boost::signals2::signal<void(const Gui::Document&)> signalRelabelDocument;
fastsignals::signal<void(const Gui::Document&)> signalRelabelDocument;
/// signal on renaming Document
boost::signals2::signal<void(const Gui::Document&)> signalRenameDocument;
fastsignals::signal<void(const Gui::Document&)> signalRenameDocument;
/// signal on activating Document
boost::signals2::signal<void(const Gui::Document&)> signalActiveDocument;
fastsignals::signal<void(const Gui::Document&)> signalActiveDocument;
/// signal on new Object
boost::signals2::signal<void(const Gui::ViewProvider&)> signalNewObject;
fastsignals::signal<void(const Gui::ViewProvider&)> signalNewObject;
/// signal on deleted Object
boost::signals2::signal<void(const Gui::ViewProvider&)> signalDeletedObject;
fastsignals::signal<void(const Gui::ViewProvider&)> signalDeletedObject;
/// signal on changed Object
boost::signals2::signal<void(const Gui::ViewProvider&, const App::Property&)> signalBeforeChangeObject;
fastsignals::signal<void(const Gui::ViewProvider&, const App::Property&)> signalBeforeChangeObject;
/// signal on changed object property
boost::signals2::signal<void(const Gui::ViewProvider&, const App::Property&)> signalChangedObject;
fastsignals::signal<void(const Gui::ViewProvider&, const App::Property&)> signalChangedObject;
/// signal on renamed Object
boost::signals2::signal<void(const Gui::ViewProvider&)> signalRelabelObject;
fastsignals::signal<void(const Gui::ViewProvider&)> signalRelabelObject;
/// signal on activated Object
boost::signals2::signal<void(const Gui::ViewProvider&)> signalActivatedObject;
fastsignals::signal<void(const Gui::ViewProvider&)> signalActivatedObject;
/// signal on activated workbench
boost::signals2::signal<void(const char*)> signalActivateWorkbench;
fastsignals::signal<void(const char*)> signalActivateWorkbench;
/// signal on added/removed workbench
boost::signals2::signal<void()> signalRefreshWorkbenches;
fastsignals::signal<void()> signalRefreshWorkbenches;
/// signal on show hidden items
boost::signals2::signal<void(const Gui::Document&)> signalShowHidden;
fastsignals::signal<void(const Gui::Document&)> signalShowHidden;
/// signal on activating view
boost::signals2::signal<void(const Gui::MDIView*)> signalActivateView;
fastsignals::signal<void(const Gui::MDIView*)> signalActivateView;
/// signal on closing view
boost::signals2::signal<void(const Gui::MDIView*)> signalCloseView;
fastsignals::signal<void(const Gui::MDIView*)> signalCloseView;
/// signal on entering in edit mode
boost::signals2::signal<void(const Gui::ViewProviderDocumentObject&)> signalInEdit;
fastsignals::signal<void(const Gui::ViewProviderDocumentObject&)> signalInEdit;
/// signal on leaving edit mode
boost::signals2::signal<void(const Gui::ViewProviderDocumentObject&)> signalResetEdit;
fastsignals::signal<void(const Gui::ViewProviderDocumentObject&)> signalResetEdit;
/// signal on changing user edit mode
boost::signals2::signal<void(int)> signalUserEditModeChanged;
fastsignals::signal<void(int)> signalUserEditModeChanged;
//@}
/** @name methods for Document handling */
+2 -2
View File
@@ -29,7 +29,7 @@
#include <map>
#include <set>
#include <string>
#include <boost/signals2.hpp>
#include <fastsignals/signal.h>
#include <Base/Writer.h>
namespace App
@@ -56,7 +56,7 @@ public:
private:
void slotNewObject(const App::DocumentObject&);
void slotChangePropertyData(const App::Property&);
using Connection = boost::signals2::connection;
using Connection = fastsignals::connection;
Connection documentNew;
Connection documentMod;
};
+4
View File
@@ -83,6 +83,8 @@ target_include_directories(
FreeCADGui
SYSTEM
PRIVATE
${FastSignals_INCLUDE_DIRS}
${Boost_INCLUDE_DIRS}
${EIGEN3_INCLUDE_DIR}
${3DCONNEXION_INCLUDE_DIR}
)
@@ -99,6 +101,7 @@ endif(MSVC)
if(MSVC)
set(FreeCADGui_LIBS
FreeCADApp
libfastsignals
${OPENGL_gl_LIBRARY}
)
@@ -111,6 +114,7 @@ if(MSVC)
else(MSVC)
set(FreeCADGui_LIBS
FreeCADApp
libfastsignals
${Boost_LIBRARIES}
${OPENGL_gl_LIBRARY}
${3DCONNEXION_LINKFLAGS}
+2 -2
View File
@@ -1595,7 +1595,7 @@ bool PythonCommand::isChecked() const
}
}
void PythonCommand::onActionInit() const
void PythonCommand::onActionInit()
{
try {
Base::PyGILStateLocker lock;
@@ -1979,7 +1979,7 @@ bool PythonGroupCommand::hasDropDownMenu() const
}
}
void PythonGroupCommand::onActionInit() const
void PythonGroupCommand::onActionInit()
{
try {
Base::PyGILStateLocker lock;
+7 -7
View File
@@ -28,7 +28,7 @@
#include <map>
#include <string>
#include <vector>
#include <boost/signals2.hpp>
#include <fastsignals/signal.h>
#include <Base/Type.h>
#include <Gui/Application.h>
@@ -829,9 +829,9 @@ protected:
/// the activation sequence
std::string Activation;
//// set the parameters on action creation
void onActionInit() const;
void onActionInit();
boost::signals2::connection connPyCmdInitialized;
fastsignals::connection connPyCmdInitialized;
};
/** The Python group command class
@@ -880,13 +880,13 @@ protected:
/// Returns the resource values
const char* getResource(const char* sName) const;
//// set the parameters on action creation
void onActionInit() const;
void onActionInit();
/// a pointer to the Python command object
PyObject* _pcPyCommand;
/// the command object resources
PyObject* _pcPyResource;
boost::signals2::connection connPyCmdInitialized;
fastsignals::connection connPyCmdInitialized;
};
@@ -1019,10 +1019,10 @@ public:
}
/// Signal on any addition or removal of command
boost::signals2::signal<void()> signalChanged;
fastsignals::signal<void()> signalChanged;
/// Signal to Python command on first workbench activation
boost::signals2::signal<void()> signalPyCmdInitialized;
fastsignals::signal<void()> signalPyCmdInitialized;
/**
* Returns a pointer to a conflicting command, or nullptr if there is no conflict.
+2 -2
View File
@@ -27,7 +27,7 @@
#include <memory>
#include <vector>
#include <boost/signals2.hpp>
#include <fastsignals/signal.h>
#include <QBrush>
#include <QGraphicsScene>
@@ -87,7 +87,7 @@ private Q_SLOTS:
private:
Model() = default;
// documentObject slots.
using Connection = boost::signals2::connection;
using Connection = fastsignals::connection;
Connection connectNewObject;
Connection connectDelObject;
Connection connectChgObject;
+2 -2
View File
@@ -36,7 +36,7 @@
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/signals2.hpp>
#include <fastsignals/signal.h>
#include "DAGRectItem.h"
@@ -85,7 +85,7 @@ struct VertexProperty
std::shared_ptr<QGraphicsPixmapItem> stateIcon; //!< visible Icon
std::shared_ptr<QGraphicsPixmapItem> icon; //!< icon
std::shared_ptr<QGraphicsTextItem> text; //!< text
boost::signals2::connection connChangeIcon;
fastsignals::connection connChangeIcon;
int row = 0; //!< row for this entry.
ColumnMask column = 0; //!< column number containing the point.
int topoSortIndex = 0;
+3 -3
View File
@@ -26,7 +26,7 @@
#include <memory>
#include <QGraphicsView>
#include <boost/signals2.hpp>
#include <fastsignals/signal.h>
#include <Gui/DockWindow.h>
#include <Gui/Document.h>
@@ -58,8 +58,8 @@ private:
using ModelMap = std::map<const Gui::Document*, std::shared_ptr<Model>>;
ModelMap modelMap;
boost::signals2::scoped_connection conActive;
boost::signals2::scoped_connection conDelete;
fastsignals::scoped_connection conActive;
fastsignals::scoped_connection conDelete;
};
//! @brief dock window for DAG viewer
+3 -3
View File
@@ -22,7 +22,7 @@
* *
***************************************************************************/
#include <boost/signals2/connection.hpp>
#include <fastsignals/connection.h>
#include <QAction>
#include <QHeaderView>
@@ -230,7 +230,7 @@ void DlgCustomKeyboardImp::populateCommandList(
commandTreeWidget->resizeColumnToContents(3);
}
boost::signals2::connection DlgCustomKeyboardImp::initCommandList(
fastsignals::connection DlgCustomKeyboardImp::initCommandList(
QTreeWidget* commandTreeWidget,
QTreeWidgetItem* separatorItem,
QComboBox* combo
@@ -317,7 +317,7 @@ void DlgCustomKeyboardImp::initPriorityList(
});
}
boost::signals2::connection DlgCustomKeyboardImp::initCommandWidgets(
fastsignals::connection DlgCustomKeyboardImp::initCommandWidgets(
QTreeWidget* commandTreeWidget,
QTreeWidgetItem* separatorItem,
QComboBox* comboGroups,
+4 -4
View File
@@ -26,7 +26,7 @@
#ifndef GUI_DIALOG_DLGKEYBOARD_IMP_H
#define GUI_DIALOG_DLGKEYBOARD_IMP_H
#include <boost/signals2.hpp>
#include <fastsignals/connection.h>
#include <memory>
#include <QPointer>
#include <QAction>
@@ -77,7 +77,7 @@ public:
* @return Return a boost signal connection for monitoring command changes.
* Most disconnect the signal before widgets gets destroyed.
*/
static boost::signals2::connection initCommandWidgets(
static fastsignals::connection initCommandWidgets(
QTreeWidget* commandTreeWidget,
QTreeWidgetItem* separatorItem,
QComboBox* comboGroups,
@@ -99,7 +99,7 @@ protected:
QTreeWidget* treeWidget,
QTreeWidgetItem* separatorItem
);
static boost::signals2::connection initCommandList(QTreeWidget*, QTreeWidgetItem*, QComboBox* combo);
static fastsignals::connection initCommandList(QTreeWidget*, QTreeWidgetItem*, QComboBox* combo);
static void initPriorityList(QTreeWidget*, QAbstractButton* buttonUp, QAbstractButton* buttonDown);
static void populateCommandGroups(QComboBox*);
static void populateCommandList(QTreeWidget*, QTreeWidgetItem*, QComboBox*);
@@ -132,7 +132,7 @@ protected:
private:
std::unique_ptr<Ui_DlgCustomKeyboard> ui;
bool firstShow;
boost::signals2::scoped_connection conn;
fastsignals::scoped_connection conn;
};
} // namespace Dialog
+2 -2
View File
@@ -26,7 +26,7 @@
#ifndef GUI_DIALOG_DLGTOOLBARS_IMP_H
#define GUI_DIALOG_DLGTOOLBARS_IMP_H
#include <boost/signals2/connection.hpp>
#include <fastsignals/connection.h>
#include "PropertyPage.h"
#include <memory>
@@ -100,7 +100,7 @@ protected:
private:
Type type;
boost::signals2::scoped_connection conn;
fastsignals::scoped_connection conn;
};
/** This class implements the creation of user defined toolbars.
+3 -2
View File
@@ -158,7 +158,7 @@ struct DockWindowManagerP
QMap<QString, QPointer<QWidget>> _dockWindows;
DockWindowItems _dockWindowItems;
ParameterGrp::handle _hPref;
boost::signals2::scoped_connection _connParam;
fastsignals::advanced_scoped_connection _connParam;
QTimer _timer;
DockWidgetEventFilter _dockWidgetEventFilter;
QPointer<OverlayManager> overlayManager;
@@ -228,7 +228,8 @@ void DockWindowManager::setupOverlayManagement()
break;
}
}
}
},
fastsignals::advanced_tag()
);
d->_timer.setSingleShot(true);
+11 -8
View File
@@ -117,12 +117,13 @@ struct DocumentP
std::map<std::string, ViewProvider*> _ViewProviderMapAnnotation;
std::list<ViewProviderDocumentObject*> _redoViewProviders;
using Connection = boost::signals2::connection;
using Connection = fastsignals::connection;
using AdvancedConnection = fastsignals::advanced_connection;
Connection connectNewObject;
Connection connectDelObject;
Connection connectCngObject;
Connection connectRenObject;
Connection connectActObject;
AdvancedConnection connectActObject;
Connection connectSaveDocument;
Connection connectRestDocument;
Connection connectStartLoadDocument;
@@ -141,9 +142,9 @@ struct DocumentP
Connection connectTransactionRemove;
Connection connectTouchedObject;
Connection connectChangePropertyEditor;
Connection connectChangeDocument;
AdvancedConnection connectChangeDocument;
using ConnectionBlock = boost::signals2::shared_connection_block;
using ConnectionBlock = fastsignals::shared_connection_block;
ConnectionBlock connectActObjectBlocker;
ConnectionBlock connectChangeDocumentBlocker;
@@ -458,9 +459,10 @@ Document::Document(App::Document* pcDocument, Application* app)
std::bind(&Gui::Document::slotRelabelObject, this, sp::_1)
);
d->connectActObject = pcDocument->signalActivatedObject.connect(
std::bind(&Gui::Document::slotActivatedObject, this, sp::_1)
std::bind(&Gui::Document::slotActivatedObject, this, sp::_1),
fastsignals::advanced_tag()
);
d->connectActObjectBlocker = boost::signals2::shared_connection_block(d->connectActObject, false);
d->connectActObjectBlocker = fastsignals::shared_connection_block(d->connectActObject, false);
d->connectSaveDocument = pcDocument->signalSaveDocument.connect(
std::bind(&Gui::Document::Save, this, sp::_1)
);
@@ -482,9 +484,10 @@ Document::Document(App::Document* pcDocument, Application* app)
);
d->connectChangeDocument
= d->_pcDocument->signalChanged.connect // use the same slot function
(std::bind(&Gui::Document::slotChangePropertyEditor, this, sp::_1, sp::_2));
(std::bind(&Gui::Document::slotChangePropertyEditor, this, sp::_1, sp::_2),
fastsignals::advanced_tag());
d->connectChangeDocumentBlocker
= boost::signals2::shared_connection_block(d->connectChangeDocument, true);
= fastsignals::shared_connection_block(d->connectChangeDocument, true);
d->connectFinishRestoreObject = pcDocument->signalFinishRestoreObject.connect(
std::bind(&Gui::Document::slotFinishRestoreObject, this, sp::_1)
);
+15 -15
View File
@@ -26,7 +26,7 @@
#include <list>
#include <map>
#include <string>
#include <boost/signals2.hpp>
#include <fastsignals/signal.h>
#include <QString>
#include <Base/Persistence.h>
@@ -112,23 +112,23 @@ public:
/** @name Signals of the document */
//@{
/// signal on new Object
mutable boost::signals2::signal<void(const Gui::ViewProviderDocumentObject&)> signalNewObject;
mutable fastsignals::signal<void(const Gui::ViewProviderDocumentObject&)> signalNewObject;
/// signal on deleted Object
mutable boost::signals2::signal<void(const Gui::ViewProviderDocumentObject&)> signalDeletedObject;
mutable fastsignals::signal<void(const Gui::ViewProviderDocumentObject&)> signalDeletedObject;
/** signal on changed Object, the 2nd argument is the changed property
of the referenced document object, not of the view provider */
mutable boost::signals2::signal<void(const Gui::ViewProviderDocumentObject&, const App::Property&)>
mutable fastsignals::signal<void(const Gui::ViewProviderDocumentObject&, const App::Property&)>
signalChangedObject;
/// signal on renamed Object
mutable boost::signals2::signal<void(const Gui::ViewProviderDocumentObject&)> signalRelabelObject;
mutable fastsignals::signal<void(const Gui::ViewProviderDocumentObject&)> signalRelabelObject;
/// signal on activated Object
mutable boost::signals2::signal<void(const Gui::ViewProviderDocumentObject&)> signalActivatedObject;
mutable fastsignals::signal<void(const Gui::ViewProviderDocumentObject&)> signalActivatedObject;
/// signal on entering in edit mode
mutable boost::signals2::signal<void(const Gui::ViewProviderDocumentObject&)> signalInEdit;
mutable fastsignals::signal<void(const Gui::ViewProviderDocumentObject&)> signalInEdit;
/// signal on leaving edit mode
mutable boost::signals2::signal<void(const Gui::ViewProviderDocumentObject&)> signalResetEdit;
mutable fastsignals::signal<void(const Gui::ViewProviderDocumentObject&)> signalResetEdit;
/// signal on changed Object, the 2nd argument is the highlite mode to use
mutable boost::signals2::signal<void(
mutable fastsignals::signal<void(
const Gui::ViewProviderDocumentObject&,
const Gui::HighlightMode&,
bool,
@@ -137,7 +137,7 @@ public:
)>
signalHighlightObject;
/// signal on changed Object, the 2nd argument is the highlite mode to use
mutable boost::signals2::signal<void(
mutable fastsignals::signal<void(
const Gui::ViewProviderDocumentObject&,
const Gui::TreeItemMode&,
App::DocumentObject* parent,
@@ -145,15 +145,15 @@ public:
)>
signalExpandObject;
/// signal on changed ShowInTree property in view provider
mutable boost::signals2::signal<void(const Gui::ViewProviderDocumentObject&)> signalShowItem;
mutable fastsignals::signal<void(const Gui::ViewProviderDocumentObject&)> signalShowItem;
/// signal on scrolling to an object
mutable boost::signals2::signal<void(const Gui::ViewProviderDocumentObject&)> signalScrollToObject;
mutable fastsignals::signal<void(const Gui::ViewProviderDocumentObject&)> signalScrollToObject;
/// signal on undo Document
mutable boost::signals2::signal<void(const Gui::Document& doc)> signalUndoDocument;
mutable fastsignals::signal<void(const Gui::Document& doc)> signalUndoDocument;
/// signal on redo Document
mutable boost::signals2::signal<void(const Gui::Document& doc)> signalRedoDocument;
mutable fastsignals::signal<void(const Gui::Document& doc)> signalRedoDocument;
/// signal on deleting Document
mutable boost::signals2::signal<void(const Gui::Document& doc)> signalDeleteDocument;
mutable fastsignals::signal<void(const Gui::Document& doc)> signalDeleteDocument;
//@}
/** @name I/O of the document */
+2 -2
View File
@@ -248,7 +248,7 @@ public:
}
Gui::Document* _document;
using Connection = boost::signals2::scoped_connection;
using Connection = fastsignals::scoped_connection;
Connection connectApplicationDeletedDocument;
};
@@ -349,7 +349,7 @@ public:
Gui::ViewProviderDocumentObject* object;
bool indocument {false};
using Connection = boost::signals2::scoped_connection;
using Connection = fastsignals::scoped_connection;
Connection connectApplicationDeletedDocument;
Connection connectDocumentCreatedObject;
Connection connectDocumentDeletedObject;
+2 -2
View File
@@ -24,7 +24,7 @@
#define GUI_DOCUMENTOBSERVER_H
#include <Base/BaseClass.h>
#include <boost/signals2.hpp>
#include <fastsignals/signal.h>
namespace App
@@ -370,7 +370,7 @@ private:
virtual void slotDeleteDocument(const Document& Doc);
private:
using Connection = boost::signals2::scoped_connection;
using Connection = fastsignals::scoped_connection;
Connection connectDocumentCreatedObject;
Connection connectDocumentDeletedObject;
Connection connectDocumentChangedObject;
+2 -2
View File
@@ -25,7 +25,7 @@
#define GUI_DOCUMENTOBSERVERPYTHON_H
#include <FCGlobal.h>
#include <boost/signals2.hpp>
#include <fastsignals/signal.h>
#include <CXX/Objects.hxx>
#include <string>
#include <vector>
@@ -89,7 +89,7 @@ private:
using Connection = struct PythonObject
{
boost::signals2::scoped_connection slot;
fastsignals::scoped_connection slot;
Py::Object py;
PyObject* ptr()
{
+4 -4
View File
@@ -27,7 +27,7 @@
#include <string>
#include <App/ObjectIdentifier.h>
#include <QPalette>
#include <boost/signals2.hpp>
#include <fastsignals/signal.h>
namespace App
@@ -89,9 +89,9 @@ protected:
void expressionChange(const App::ObjectIdentifier& id);
void objectDeleted(const App::DocumentObject&);
void onDocumentDeleted(const App::Document&);
boost::signals2::scoped_connection expressionchanged;
boost::signals2::scoped_connection objectdeleted;
boost::signals2::scoped_connection documentdeleted;
fastsignals::scoped_connection expressionchanged;
fastsignals::scoped_connection objectdeleted;
fastsignals::scoped_connection documentdeleted;
bool m_autoApply {false};
};
+3 -2
View File
@@ -23,8 +23,9 @@
#ifndef GUI_GRAPHVIZVIEW_H
#define GUI_GRAPHVIZVIEW_H
#include "MDIView.h"
#include <fastsignals/signal.h>
#include "MDIView.h"
class QGraphicsScene;
class QGraphicsView;
@@ -81,7 +82,7 @@ private:
GraphvizWorker* thread;
int nPending;
using Connection = boost::signals2::scoped_connection;
using Connection = fastsignals::scoped_connection;
Connection recomputeConnection;
Connection undoConnection;
Connection redoConnection;
+1 -1
View File
@@ -20,7 +20,7 @@
* *
***************************************************************************/
#include <boost/signals2.hpp>
#include <fastsignals/signal.h>
#include <boost/core/ignore_unused.hpp>
#include <QAction>
#include <QApplication>
+2 -2
View File
@@ -23,7 +23,7 @@
#ifndef GUI_MDIVIEW_H
#define GUI_MDIVIEW_H
#include <boost/signals2.hpp>
#include <fastsignals/signal.h>
#include <QMainWindow>
#include <Gui/ActiveObjectList.h>
#include <Gui/View.h>
@@ -207,7 +207,7 @@ private:
Qt::WindowStates wstate;
// list of active objects of this view
ActiveObjectList ActiveObjects;
using Connection = boost::signals2::connection;
using Connection = fastsignals::connection;
Connection connectDelObject; // remove active object upon delete.
friend class MainWindow;
+3 -2
View File
@@ -321,7 +321,7 @@ struct MainWindowP
int actionUpdateDelay = 0;
QMap<QString, QPointer<UrlHandler>> urlHandler;
std::string hiddenDockWindows;
boost::signals2::scoped_connection connParam;
fastsignals::advanced_scoped_connection connParam;
ParameterGrp::handle hGrp;
bool _restoring = false;
QTime _showNormal;
@@ -367,7 +367,8 @@ MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags f)
OverlayManager::instance()->reload(OverlayManager::ReloadMode::ReloadPause);
d->restoreStateTimer.start(100);
}
}
},
fastsignals::advanced_tag()
);
d->hGrp = App::GetApplication().GetParameterGroupByPath(
+1 -1
View File
@@ -263,7 +263,7 @@ private:
static ManualAlignment* _instance;
using Connection = boost::signals2::connection;
using Connection = fastsignals::connection;
Connection connectApplicationDeletedDocument;
Connection connectDocumentDeletedObject;
+2 -2
View File
@@ -27,7 +27,7 @@
#include <Base/Persistence.h>
#include <map>
#include <vector>
#include <boost/signals2.hpp>
#include <fastsignals/signal.h>
namespace zipios
{
@@ -62,7 +62,7 @@ private:
Gui::Document* document;
std::vector<App::DocumentObject*> objects;
std::map<std::string, std::string> nameMap;
using Connection = boost::signals2::connection;
using Connection = fastsignals::connection;
Connection connectExport;
Connection connectImport;
};
+1 -1
View File
@@ -52,7 +52,7 @@
using namespace Gui;
using Connection = boost::signals2::connection;
using Connection = fastsignals::connection;
namespace sp = std::placeholders;
+1 -1
View File
@@ -199,7 +199,7 @@ struct OverlayInfo
Qt::DockWidgetArea dockArea;
std::unordered_map<QDockWidget*, OverlayInfo*>& overlayMap;
ParameterGrp::handle hGrp;
boost::signals2::scoped_connection conn;
fastsignals::scoped_connection conn;
OverlayInfo(
QWidget* parent,
+2 -2
View File
@@ -24,7 +24,7 @@
#include <map>
#include <memory>
#include <boost/signals2.hpp>
#include <fastsignals/signal.h>
#include <QTimer>
#include <FCGlobal.h>
@@ -261,7 +261,7 @@ public:
protected:
std::map<ParamKey, std::shared_ptr<ParamHandler>> handlers;
std::set<std::shared_ptr<ParamHandler>> pendings;
boost::signals2::scoped_connection conn;
fastsignals::scoped_connection conn;
QTimer timer;
};
+1 -1
View File
@@ -95,7 +95,7 @@ private Q_SLOTS:
void openTransaction();
private:
using Connection = boost::signals2::scoped_connection;
using Connection = fastsignals::scoped_connection;
std::string propertyName; // the name of the placement property
std::set<std::string> documents;
/** If false apply the placement directly to the transform nodes,
+1 -1
View File
@@ -67,7 +67,7 @@
// Boost
#include <boost_graph_adjacency_list.hpp>
#include <boost/signals2.hpp>
#include <fastsignals/signal.h>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/bind/bind.hpp>
#include <boost/core/ignore_unused.hpp>
+1 -1
View File
@@ -99,7 +99,7 @@ private:
private:
struct PropInfo;
struct PropFind;
using Connection = boost::signals2::connection;
using Connection = fastsignals::connection;
Connection connectPropData;
Connection connectPropView;
Connection connectPropAppend;
+4 -4
View File
@@ -260,7 +260,7 @@ private:
void _onSelectionChanged(const SelectionChanges& msg);
private:
using Connection = boost::signals2::connection;
using Connection = fastsignals::connection;
Connection connectSelection;
std::string filterDocName;
std::string filterObjName;
@@ -503,12 +503,12 @@ public:
void setClarifySelectionActive(bool active);
/// signal on new object
boost::signals2::signal<void(const SelectionChanges& msg)> signalSelectionChanged;
fastsignals::signal<void(const SelectionChanges& msg)> signalSelectionChanged;
/// signal on selection change with resolved object
boost::signals2::signal<void(const SelectionChanges& msg)> signalSelectionChanged2;
fastsignals::signal<void(const SelectionChanges& msg)> signalSelectionChanged2;
/// signal on selection change with resolved object and sub element map
boost::signals2::signal<void(const SelectionChanges& msg)> signalSelectionChanged3;
fastsignals::signal<void(const SelectionChanges& msg)> signalSelectionChanged3;
/** Returns a vector of selection objects
*
+1 -1
View File
@@ -50,7 +50,7 @@ namespace sp = std::placeholders;
class ElementColors::Private: public Gui::SelectionGate
{
public:
using Connection = boost::signals2::connection;
using Connection = fastsignals::connection;
std::unique_ptr<Ui_TaskElementColors> ui;
ViewProviderDocumentObject* vp;
ViewProviderDocumentObject* vpParent;
+1 -1
View File
@@ -41,7 +41,7 @@ class ViewProvider;
namespace TaskView
{
using TaskAppearance_Connection = boost::signals2::connection;
using TaskAppearance_Connection = fastsignals::connection;
class Ui_TaskAppearance;
class TaskAppearance: public TaskBox, public Gui::SelectionSingleton::ObserverType
+2 -2
View File
@@ -24,12 +24,12 @@
#ifndef GUI_TASKVIEW_TaskSolverMessages_H
#define GUI_TASKVIEW_TaskSolverMessages_H
#include <boost/signals2.hpp>
#include <fastsignals/signal.h>
#include <Gui/TaskView/TaskView.h>
class Ui_TaskSolverMessages;
using Connection = boost::signals2::connection;
using Connection = fastsignals::connection;
namespace App
{
+1 -1
View File
@@ -51,7 +51,7 @@ class ComboView;
namespace TaskView
{
using Connection = boost::signals2::connection;
using Connection = fastsignals::connection;
class TaskEditControl;
class TaskDialog;
+1 -1
View File
@@ -125,7 +125,7 @@ void TextDocumentEditorView::refresh()
void TextDocumentEditorView::saveToObject()
{
boost::signals2::shared_connection_block textBlock {textConnection};
fastsignals::shared_connection_block textBlock {textConnection};
textDocument->Text.setValue(getEditor()->document()->toPlainText().toUtf8());
textDocument->purgeTouched();
}
+2 -2
View File
@@ -75,8 +75,8 @@ private:
private:
QPlainTextEdit* const editor;
App::TextDocument* const textDocument;
boost::signals2::connection textConnection;
boost::signals2::connection labelConnection;
fastsignals::advanced_connection textConnection;
fastsignals::connection labelConnection;
bool aboutToClose = false;
};
+1 -1
View File
@@ -35,7 +35,7 @@ ToolBarAreaWidget::ToolBarAreaWidget(
QWidget* parent,
ToolBarArea area,
const ParameterGrp::handle& hParam,
boost::signals2::scoped_connection& conn,
fastsignals::advanced_scoped_connection& conn,
QTimer* timer
)
: QWidget(parent)
+3 -3
View File
@@ -26,7 +26,7 @@
#include <QToolBar>
#include <QPointer>
#include <QWidget>
#include <boost/signals2.hpp>
#include <fastsignals/signal.h>
#include <Base/Parameter.h>
namespace Gui
@@ -56,7 +56,7 @@ public:
QWidget* parent,
ToolBarArea area,
const ParameterGrp::handle& hParam,
boost::signals2::scoped_connection& conn,
fastsignals::advanced_scoped_connection& conn,
QTimer* timer = nullptr
);
@@ -110,7 +110,7 @@ private:
QHBoxLayout* _layout;
QPointer<QTimer> _sizingTimer;
ParameterGrp::handle _hParam;
boost::signals2::scoped_connection& _conn;
fastsignals::advanced_scoped_connection& _conn;
ToolBarArea _area;
};
+2 -1
View File
@@ -503,7 +503,8 @@ void ToolBarManager::setupConnection()
timer.start(100);
}
}
}
},
fastsignals::advanced_tag()
);
}
+2 -2
View File
@@ -25,7 +25,7 @@
#define GUI_TOOLBARMANAGER_H
#include <string>
#include <boost/signals2.hpp>
#include <fastsignals/signal.h>
#include <QStringList>
#include <QPointer>
@@ -226,7 +226,7 @@ private:
QTimer menuBarTimer;
QTimer sizeTimer;
QTimer resizeTimer;
boost::signals2::scoped_connection connParam;
fastsignals::advanced_scoped_connection connParam;
ToolBarAreaWidget* statusBarAreaWidget = nullptr;
ToolBarAreaWidget* menuBarLeftAreaWidget = nullptr;
ToolBarAreaWidget* menuBarRightAreaWidget = nullptr;
+1 -1
View File
@@ -251,7 +251,7 @@ public:
std::string label2;
std::string internalName;
using Connection = boost::signals2::scoped_connection;
using Connection = fastsignals::scoped_connection;
Connection connectIcon;
Connection connectTool;
+3 -3
View File
@@ -300,7 +300,7 @@ private:
friend class TreeParams;
friend class TreeWidgetItemDelegate;
using Connection = boost::signals2::connection;
using Connection = fastsignals::connection;
Connection connectNewDocument;
Connection connectDelDocument;
Connection connectRenDocument;
@@ -430,7 +430,7 @@ private:
ExpandInfoPtr _ExpandInfo;
void restoreItemExpansion(const ExpandInfoPtr&, DocumentObjectItem*);
using Connection = boost::signals2::connection;
using Connection = fastsignals::connection;
Connection connectNewObject;
Connection connectDelObject;
Connection connectChgObject;
@@ -531,7 +531,7 @@ private:
DocumentItem* myOwner;
DocumentObjectDataPtr myData;
std::vector<std::string> mySubs;
using Connection = boost::signals2::connection;
using Connection = fastsignals::connection;
int previousStatus;
int selected;
bool populated;

Some files were not shown because too many files have changed in this diff Show More