Merge pull request #19132 from tritao/base-remove-boost-signals
Base: Remove Boost-based signals and switch to `FastSignals`.
This commit is contained in:
Vendored
+2
@@ -1,3 +1,5 @@
|
||||
add_subdirectory(FastSignals)
|
||||
|
||||
# Build SalomeMesh for all Platforms since heavily patched
|
||||
if (BUILD_SMESH)
|
||||
add_subdirectory(salomesmesh)
|
||||
|
||||
+102
@@ -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
|
||||
...
|
||||
@@ -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
@@ -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()
|
||||
Vendored
+21
@@ -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.
|
||||
Vendored
+17
@@ -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
|
||||
|
||||
[](https://travis-ci.org/ispringteam/FastSignals)
|
||||
[](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
@@ -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
@@ -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.
|
||||
@@ -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<> 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);
|
||||
}
|
||||
```
|
||||
@@ -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
|
||||
+164
@@ -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
|
||||
+17976
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"
|
||||
+124
@@ -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);
|
||||
}
|
||||
}
|
||||
+132
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#define APP_PATH_H
|
||||
|
||||
#include <bitset>
|
||||
#include <climits>
|
||||
#include <map>
|
||||
#include <limits>
|
||||
#include <set>
|
||||
|
||||
@@ -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
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
@@ -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
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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};
|
||||
};
|
||||
|
||||
|
||||
@@ -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
@@ -20,7 +20,7 @@
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include <boost/signals2.hpp>
|
||||
#include <fastsignals/signal.h>
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
|
||||
+2
-2
@@ -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;
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -263,7 +263,7 @@ private:
|
||||
|
||||
static ManualAlignment* _instance;
|
||||
|
||||
using Connection = boost::signals2::connection;
|
||||
using Connection = fastsignals::connection;
|
||||
Connection connectApplicationDeletedDocument;
|
||||
Connection connectDocumentDeletedObject;
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
|
||||
using namespace Gui;
|
||||
|
||||
using Connection = boost::signals2::connection;
|
||||
using Connection = fastsignals::connection;
|
||||
|
||||
namespace sp = std::placeholders;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -51,7 +51,7 @@ class ComboView;
|
||||
namespace TaskView
|
||||
{
|
||||
|
||||
using Connection = boost::signals2::connection;
|
||||
using Connection = fastsignals::connection;
|
||||
class TaskEditControl;
|
||||
class TaskDialog;
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -503,7 +503,8 @@ void ToolBarManager::setupConnection()
|
||||
timer.start(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
fastsignals::advanced_tag()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
Reference in New Issue
Block a user