Add simple signal/slot implementation loosly based on boost.sigal
Originally committed to SVN as r4898.
This commit is contained in:
parent
31e2828517
commit
4fc1ff6ad6
5 changed files with 609 additions and 42 deletions
|
@ -76,6 +76,65 @@
|
||||||
Name="VCPostBuildEventTool"
|
Name="VCPostBuildEventTool"
|
||||||
/>
|
/>
|
||||||
</Configuration>
|
</Configuration>
|
||||||
|
<Configuration
|
||||||
|
Name="Debug|x64"
|
||||||
|
ConfigurationType="4"
|
||||||
|
InheritedPropertySheets=".\wxlib_include.vsprops;.\libraries_outdirs.vsprops;.\compiler_options_debug.vsprops;..\aegisub_vs2008\wxlib_lib64.vsprops;..\aegisub_vs2008\suffix_debug64.vsprops;.\precomp_header.vsprops"
|
||||||
|
CharacterSet="1"
|
||||||
|
>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreBuildEventTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCustomBuildTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXMLDataGeneratorTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCWebServiceProxyGeneratorTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCMIDLTool"
|
||||||
|
TargetEnvironment="3"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
Optimization="0"
|
||||||
|
MinimalRebuild="true"
|
||||||
|
BasicRuntimeChecks="3"
|
||||||
|
RuntimeLibrary="3"
|
||||||
|
WarningLevel="3"
|
||||||
|
DebugInformationFormat="3"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCManagedResourceCompilerTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCResourceCompilerTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreLinkEventTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCLibrarianTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCALinkTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXDCMakeTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCBscMakeTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCFxCopTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPostBuildEventTool"
|
||||||
|
/>
|
||||||
|
</Configuration>
|
||||||
<Configuration
|
<Configuration
|
||||||
Name="Release|Win32"
|
Name="Release|Win32"
|
||||||
ConfigurationType="4"
|
ConfigurationType="4"
|
||||||
|
@ -135,26 +194,6 @@
|
||||||
Name="VCPostBuildEventTool"
|
Name="VCPostBuildEventTool"
|
||||||
/>
|
/>
|
||||||
</Configuration>
|
</Configuration>
|
||||||
<Configuration
|
|
||||||
Name="Debug|x64"
|
|
||||||
ConfigurationType="4"
|
|
||||||
InheritedPropertySheets=".\wxlib_include.vsprops;.\libraries_outdirs.vsprops;.\compiler_options_debug.vsprops;..\aegisub_vs2008\wxlib_lib64.vsprops;..\aegisub_vs2008\suffix_debug64.vsprops;.\precomp_header.vsprops"
|
|
||||||
CharacterSet="1"
|
|
||||||
>
|
|
||||||
<Tool
|
|
||||||
Name="VCMIDLTool"
|
|
||||||
TargetEnvironment="3"
|
|
||||||
/>
|
|
||||||
<Tool
|
|
||||||
Name="VCCLCompilerTool"
|
|
||||||
Optimization="0"
|
|
||||||
MinimalRebuild="true"
|
|
||||||
BasicRuntimeChecks="3"
|
|
||||||
RuntimeLibrary="3"
|
|
||||||
WarningLevel="3"
|
|
||||||
DebugInformationFormat="3"
|
|
||||||
/>
|
|
||||||
</Configuration>
|
|
||||||
<Configuration
|
<Configuration
|
||||||
Name="Release|x64"
|
Name="Release|x64"
|
||||||
ConfigurationType="4"
|
ConfigurationType="4"
|
||||||
|
@ -162,6 +201,18 @@
|
||||||
CharacterSet="1"
|
CharacterSet="1"
|
||||||
WholeProgramOptimization="1"
|
WholeProgramOptimization="1"
|
||||||
>
|
>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreBuildEventTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCustomBuildTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXMLDataGeneratorTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCWebServiceProxyGeneratorTool"
|
||||||
|
/>
|
||||||
<Tool
|
<Tool
|
||||||
Name="VCMIDLTool"
|
Name="VCMIDLTool"
|
||||||
TargetEnvironment="3"
|
TargetEnvironment="3"
|
||||||
|
@ -175,6 +226,33 @@
|
||||||
WarningLevel="3"
|
WarningLevel="3"
|
||||||
DebugInformationFormat="3"
|
DebugInformationFormat="3"
|
||||||
/>
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCManagedResourceCompilerTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCResourceCompilerTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreLinkEventTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCLibrarianTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCALinkTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXDCMakeTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCBscMakeTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCFxCopTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPostBuildEventTool"
|
||||||
|
/>
|
||||||
</Configuration>
|
</Configuration>
|
||||||
</Configurations>
|
</Configurations>
|
||||||
<References>
|
<References>
|
||||||
|
@ -259,7 +337,7 @@
|
||||||
/>
|
/>
|
||||||
</FileConfiguration>
|
</FileConfiguration>
|
||||||
<FileConfiguration
|
<FileConfiguration
|
||||||
Name="Release|Win32"
|
Name="Debug|x64"
|
||||||
>
|
>
|
||||||
<Tool
|
<Tool
|
||||||
Name="VCCLCompilerTool"
|
Name="VCCLCompilerTool"
|
||||||
|
@ -267,7 +345,7 @@
|
||||||
/>
|
/>
|
||||||
</FileConfiguration>
|
</FileConfiguration>
|
||||||
<FileConfiguration
|
<FileConfiguration
|
||||||
Name="Debug|x64"
|
Name="Release|Win32"
|
||||||
>
|
>
|
||||||
<Tool
|
<Tool
|
||||||
Name="VCCLCompilerTool"
|
Name="VCCLCompilerTool"
|
||||||
|
@ -375,6 +453,10 @@
|
||||||
RelativePath="..\..\libaegisub\include\libaegisub\path.h"
|
RelativePath="..\..\libaegisub\include\libaegisub\path.h"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\libaegisub\include\libaegisub\signals.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\..\libaegisub\include\libaegisub\types.h"
|
RelativePath="..\..\libaegisub\include\libaegisub\types.h"
|
||||||
>
|
>
|
||||||
|
|
|
@ -89,6 +89,65 @@
|
||||||
CommandLine="cd "$(ExecutableOutDir)"
"$(ProjectDir)\..\..\tests\setup.bat" "$(ProjectDir)\..\..\tests"
"
|
CommandLine="cd "$(ExecutableOutDir)"
"$(ProjectDir)\..\..\tests\setup.bat" "$(ProjectDir)\..\..\tests"
"
|
||||||
/>
|
/>
|
||||||
</Configuration>
|
</Configuration>
|
||||||
|
<Configuration
|
||||||
|
Name="Debug|x64"
|
||||||
|
ConfigurationType="4"
|
||||||
|
InheritedPropertySheets="..\aegisub_vs2008\compiler_options_debug.vsprops;..\aegisub_vs2008\compiler_options.vsprops;.\googletest_include.vsprops;..\aegisub_vs2008\libaegisub_include_dir.vsprops;.\test_exe_filename.vsprops"
|
||||||
|
CharacterSet="1"
|
||||||
|
>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreBuildEventTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCustomBuildTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXMLDataGeneratorTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCWebServiceProxyGeneratorTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCMIDLTool"
|
||||||
|
TargetEnvironment="3"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCLCompilerTool"
|
||||||
|
Optimization="0"
|
||||||
|
MinimalRebuild="true"
|
||||||
|
BasicRuntimeChecks="3"
|
||||||
|
RuntimeLibrary="3"
|
||||||
|
WarningLevel="3"
|
||||||
|
DebugInformationFormat="3"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCManagedResourceCompilerTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCResourceCompilerTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreLinkEventTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCLibrarianTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCALinkTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXDCMakeTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCBscMakeTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCFxCopTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPostBuildEventTool"
|
||||||
|
/>
|
||||||
|
</Configuration>
|
||||||
<Configuration
|
<Configuration
|
||||||
Name="Release|Win32"
|
Name="Release|Win32"
|
||||||
ConfigurationType="4"
|
ConfigurationType="4"
|
||||||
|
@ -148,26 +207,6 @@
|
||||||
Name="VCPostBuildEventTool"
|
Name="VCPostBuildEventTool"
|
||||||
/>
|
/>
|
||||||
</Configuration>
|
</Configuration>
|
||||||
<Configuration
|
|
||||||
Name="Debug|x64"
|
|
||||||
ConfigurationType="4"
|
|
||||||
InheritedPropertySheets="..\aegisub_vs2008\compiler_options_debug.vsprops;..\aegisub_vs2008\compiler_options.vsprops;.\googletest_include.vsprops;..\aegisub_vs2008\libaegisub_include_dir.vsprops;.\test_exe_filename.vsprops"
|
|
||||||
CharacterSet="1"
|
|
||||||
>
|
|
||||||
<Tool
|
|
||||||
Name="VCMIDLTool"
|
|
||||||
TargetEnvironment="3"
|
|
||||||
/>
|
|
||||||
<Tool
|
|
||||||
Name="VCCLCompilerTool"
|
|
||||||
Optimization="0"
|
|
||||||
MinimalRebuild="true"
|
|
||||||
BasicRuntimeChecks="3"
|
|
||||||
RuntimeLibrary="3"
|
|
||||||
WarningLevel="3"
|
|
||||||
DebugInformationFormat="3"
|
|
||||||
/>
|
|
||||||
</Configuration>
|
|
||||||
<Configuration
|
<Configuration
|
||||||
Name="Release|x64"
|
Name="Release|x64"
|
||||||
ConfigurationType="4"
|
ConfigurationType="4"
|
||||||
|
@ -175,6 +214,18 @@
|
||||||
CharacterSet="1"
|
CharacterSet="1"
|
||||||
WholeProgramOptimization="1"
|
WholeProgramOptimization="1"
|
||||||
>
|
>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreBuildEventTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCCustomBuildTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXMLDataGeneratorTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCWebServiceProxyGeneratorTool"
|
||||||
|
/>
|
||||||
<Tool
|
<Tool
|
||||||
Name="VCMIDLTool"
|
Name="VCMIDLTool"
|
||||||
TargetEnvironment="3"
|
TargetEnvironment="3"
|
||||||
|
@ -188,6 +239,33 @@
|
||||||
WarningLevel="3"
|
WarningLevel="3"
|
||||||
DebugInformationFormat="3"
|
DebugInformationFormat="3"
|
||||||
/>
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCManagedResourceCompilerTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCResourceCompilerTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPreLinkEventTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCLibrarianTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCALinkTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCXDCMakeTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCBscMakeTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCFxCopTool"
|
||||||
|
/>
|
||||||
|
<Tool
|
||||||
|
Name="VCPostBuildEventTool"
|
||||||
|
/>
|
||||||
</Configuration>
|
</Configuration>
|
||||||
</Configurations>
|
</Configurations>
|
||||||
<References>
|
<References>
|
||||||
|
@ -245,6 +323,10 @@
|
||||||
RelativePath="..\..\tests\libaegisub_option.cpp"
|
RelativePath="..\..\tests\libaegisub_option.cpp"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\tests\libaegisub_signals.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\..\tests\libaegisub_util.cpp"
|
RelativePath="..\..\tests\libaegisub_util.cpp"
|
||||||
>
|
>
|
||||||
|
|
297
aegisub/libaegisub/include/libaegisub/signals.h
Normal file
297
aegisub/libaegisub/include/libaegisub/signals.h
Normal file
|
@ -0,0 +1,297 @@
|
||||||
|
// Copyright (c) 2010, Thomas Goyne <plorkyeran@aegisub.org>
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
//
|
||||||
|
// $Id$
|
||||||
|
|
||||||
|
/// @file signals.h
|
||||||
|
/// @brief
|
||||||
|
/// @ingroup libaegisub
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if !defined(AGI_PRE) && !defined(LAGI_PRE)
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#else
|
||||||
|
#include <tr1/functional>
|
||||||
|
#include <map>
|
||||||
|
#include <tr1/memory>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace agi {
|
||||||
|
namespace signal {
|
||||||
|
|
||||||
|
using namespace std::tr1::placeholders;
|
||||||
|
|
||||||
|
class Connection;
|
||||||
|
|
||||||
|
/// Implementation details; nothing outside this file should directly touch
|
||||||
|
/// anything in the detail namespace
|
||||||
|
namespace detail {
|
||||||
|
class SignalBase;
|
||||||
|
class ConnectionToken {
|
||||||
|
friend class agi::signal::Connection;
|
||||||
|
friend class SignalBase;
|
||||||
|
|
||||||
|
SignalBase *signal;
|
||||||
|
bool blocked;
|
||||||
|
bool claimed;
|
||||||
|
|
||||||
|
ConnectionToken(SignalBase *signal) : signal(signal), blocked(false), claimed(false) { }
|
||||||
|
inline void Disconnect();
|
||||||
|
public:
|
||||||
|
~ConnectionToken() { Disconnect(); }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @class Connection
|
||||||
|
/// @brief Object representing a connection to a signal
|
||||||
|
class Connection {
|
||||||
|
std::tr1::shared_ptr<detail::ConnectionToken> token;
|
||||||
|
public:
|
||||||
|
Connection() { }
|
||||||
|
Connection(detail::ConnectionToken *token) : token(token) { token->claimed = true; }
|
||||||
|
|
||||||
|
/// @brief End this connection
|
||||||
|
///
|
||||||
|
/// This normally does not need to be manually called, as a connection is
|
||||||
|
/// automatically closed when all Connection objects referring to it are
|
||||||
|
/// gone. To temporarily enable or disable a connection, use Block/Unblock
|
||||||
|
/// instead
|
||||||
|
void Disconnect() { if (token.get()) token->Disconnect(); }
|
||||||
|
|
||||||
|
/// @brief Disable this connection until Unblock is called
|
||||||
|
void Block() { if (token.get()) token->blocked = true; }
|
||||||
|
|
||||||
|
/// @brief Reenable this connection after it was disabled by Block
|
||||||
|
void Unblock() { if (token.get()) token->blocked = false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @class UnscopedConnection
|
||||||
|
/// @brief A connection which is not automatically closed
|
||||||
|
///
|
||||||
|
/// Connections initially start out owned by the signal. If a slot knows that it
|
||||||
|
/// will outlive a signal and does not need to be able to block a connection, it
|
||||||
|
/// can simply ignore the return value of Connect.
|
||||||
|
///
|
||||||
|
/// If a slot needs to be able to disconnect from a signal, it should store the
|
||||||
|
/// returned connection in a Connection, which transfers ownership of the
|
||||||
|
/// connection to the slot. If there is any chance that the signal will outlive
|
||||||
|
/// the slot, this must be done.
|
||||||
|
class UnscopedConnection {
|
||||||
|
detail::ConnectionToken *token;
|
||||||
|
public:
|
||||||
|
UnscopedConnection(detail::ConnectionToken *token) : token(token) { }
|
||||||
|
operator Connection() { return Connection(token); }
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
/// @brief Polymorphic base class for slots
|
||||||
|
///
|
||||||
|
/// This class has two purposes: to avoid having to make Connection
|
||||||
|
/// templated on what type of connection it is controlling, and to avoid
|
||||||
|
/// some messiness with templated friend classes
|
||||||
|
class SignalBase {
|
||||||
|
friend class ConnectionToken;
|
||||||
|
/// @brief Disconnect the passed slot from the signal
|
||||||
|
/// @param tok Token to disconnect
|
||||||
|
virtual void Disconnect(ConnectionToken *tok)=0;
|
||||||
|
protected:
|
||||||
|
/// @brief Notify a slot that it has been disconnected
|
||||||
|
/// @param tok Token to disconnect
|
||||||
|
///
|
||||||
|
/// Used by the signal when the signal wishes to end a connection (such
|
||||||
|
/// as if the signal is being destroyed while slots are still connected
|
||||||
|
/// to it)
|
||||||
|
void DisconnectToken(ConnectionToken *tok) { tok->signal = NULL; }
|
||||||
|
|
||||||
|
/// @brief Has a token been claimed by a scoped connection object?
|
||||||
|
bool TokenClaimed(ConnectionToken *tok) { return tok->claimed; }
|
||||||
|
|
||||||
|
/// @brief Create a new connection to this slot
|
||||||
|
ConnectionToken *MakeToken() { return new ConnectionToken(this); }
|
||||||
|
|
||||||
|
/// @brief Check if a connection currently wants to receive signals
|
||||||
|
bool Blocked(ConnectionToken *tok) { return tok->blocked; }
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void ConnectionToken::Disconnect() {
|
||||||
|
if (signal) signal->Disconnect(this);
|
||||||
|
signal = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Templated common code for signals
|
||||||
|
template<class Slot>
|
||||||
|
class SignalBaseImpl : public SignalBase {
|
||||||
|
protected:
|
||||||
|
typedef std::map<ConnectionToken*, Slot> SlotMap;
|
||||||
|
|
||||||
|
SlotMap slots; /// Signals currently connected to this slot
|
||||||
|
|
||||||
|
void Disconnect(ConnectionToken *tok) {
|
||||||
|
slots.erase(tok);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Protected destructor so that we don't need a virtual destructor
|
||||||
|
~SignalBaseImpl() {
|
||||||
|
for (typename SlotMap::iterator cur = slots.begin(); cur != slots.end(); ++cur) {
|
||||||
|
DisconnectToken(cur->first);
|
||||||
|
if (!TokenClaimed(cur->first)) delete cur->first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
/// @brief Connect a signal to this slot
|
||||||
|
/// @param sig Signal to connect
|
||||||
|
/// @return The connection object
|
||||||
|
UnscopedConnection Connect(Slot sig) {
|
||||||
|
ConnectionToken *token = MakeToken();
|
||||||
|
slots.insert(std::make_pair(token, sig));
|
||||||
|
return UnscopedConnection(token);
|
||||||
|
}
|
||||||
|
template<class F, class Arg1>
|
||||||
|
UnscopedConnection Connect(F func, Arg1 a1) {
|
||||||
|
return Connect(std::tr1::bind(func, a1));
|
||||||
|
}
|
||||||
|
template<class F, class Arg1, class Arg2>
|
||||||
|
UnscopedConnection Connect(F func, Arg1 a1, Arg2 a2) {
|
||||||
|
return Connect(std::tr1::bind(func, a1, a2));
|
||||||
|
}
|
||||||
|
template<class F, class Arg1, class Arg2, class Arg3>
|
||||||
|
UnscopedConnection Connect(F func, Arg1 a1, Arg2 a2, Arg3 a3) {
|
||||||
|
return Connect(std::tr1::bind(func, a1, a2, a3));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SIGNALS_H_FOR_EACH_SIGNAL(...) \
|
||||||
|
for (typename super::SlotMap::iterator cur = slots.begin(); cur != slots.end(); ++cur) { \
|
||||||
|
if (!Blocked(cur->first)) cur->second(__VA_ARGS__); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @class Signal
|
||||||
|
/// @brief Two-argument signal
|
||||||
|
/// @param Arg1 Type of first argument to pass to slots
|
||||||
|
/// @param Arg2 Type of second argument to pass to slots
|
||||||
|
template<class Arg1 = void, class Arg2 = void>
|
||||||
|
class Signal : public detail::SignalBaseImpl<std::tr1::function<void (Arg1, Arg2)> > {
|
||||||
|
typedef detail::SignalBaseImpl<std::tr1::function<void (Arg1, Arg2)> > super;
|
||||||
|
using super::Blocked;
|
||||||
|
using super::slots;
|
||||||
|
public:
|
||||||
|
Signal() { }
|
||||||
|
|
||||||
|
/// @brief Trigger this signal
|
||||||
|
/// @param a1 First argument to the signal
|
||||||
|
/// @param a2 Second argument to the signal
|
||||||
|
///
|
||||||
|
/// The order in which connected slots are called is undefined and should
|
||||||
|
/// not be relied on
|
||||||
|
void operator()(Arg1 a1, Arg2 a2) { SIGNALS_H_FOR_EACH_SIGNAL(a1, a2) }
|
||||||
|
|
||||||
|
// Don't hide the base overloads
|
||||||
|
using super::Connect;
|
||||||
|
|
||||||
|
/// @brief Connect a member function with the correct signature to this signal
|
||||||
|
/// @param func Function to connect
|
||||||
|
/// @param a1 Object
|
||||||
|
///
|
||||||
|
/// This overload is purely for convenience so that classes can do
|
||||||
|
/// sig.Connect(&Class::Foo, this) rather than
|
||||||
|
/// sig.Connect(&Class::Foo, this, _1, _2)
|
||||||
|
template<class T>
|
||||||
|
UnscopedConnection Connect(void (T::*func)(Arg1, Arg2), T* a1) {
|
||||||
|
return Connect(std::tr1::bind(func, a1, _1, _2));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @class Signal
|
||||||
|
/// @brief One-argument signal
|
||||||
|
/// @param Arg1 Type of the argument to pass to slots
|
||||||
|
template<class Arg1>
|
||||||
|
class Signal<Arg1, void> : public detail::SignalBaseImpl<std::tr1::function<void (Arg1)> > {
|
||||||
|
typedef detail::SignalBaseImpl<std::tr1::function<void (Arg1)> > super;
|
||||||
|
using super::Blocked;
|
||||||
|
using super::slots;
|
||||||
|
public:
|
||||||
|
Signal() { }
|
||||||
|
|
||||||
|
/// @brief Trigger this signal
|
||||||
|
/// @param a1 The argument to the signal
|
||||||
|
///
|
||||||
|
/// The order in which connected slots are called is undefined and should
|
||||||
|
/// not be relied on
|
||||||
|
void operator()(Arg1 a1) { SIGNALS_H_FOR_EACH_SIGNAL(a1) }
|
||||||
|
|
||||||
|
// Don't hide the base overloads
|
||||||
|
using super::Connect;
|
||||||
|
|
||||||
|
/// @brief Connect a member function with the correct signature to this signal
|
||||||
|
/// @param func Function to connect
|
||||||
|
/// @param a1 Object
|
||||||
|
///
|
||||||
|
/// This overload is purely for convenience so that classes can do
|
||||||
|
/// sig.Connect(&Class::Foo, this) rather than sig.Connect(&Class::Foo, this, _1)
|
||||||
|
template<class T>
|
||||||
|
UnscopedConnection Connect(void (T::*func)(Arg1), T* a1) {
|
||||||
|
return Connect(std::tr1::bind(func, a1, _1));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @class Signal
|
||||||
|
/// @brief Zero-argument signal
|
||||||
|
template<>
|
||||||
|
class Signal<void> : public detail::SignalBaseImpl<std::tr1::function<void ()> > {
|
||||||
|
typedef detail::SignalBaseImpl<std::tr1::function<void ()> > super;
|
||||||
|
using super::Blocked;
|
||||||
|
using super::slots;
|
||||||
|
public:
|
||||||
|
Signal() { }
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
// MSVC incorrectly considers this not a template context due to it being fully
|
||||||
|
// specified, making typename invalid here
|
||||||
|
#define typename
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// @brief Trigger this signal
|
||||||
|
///
|
||||||
|
/// The order in which connected slots are called is undefined and should
|
||||||
|
/// not be relied on
|
||||||
|
void operator()() { SIGNALS_H_FOR_EACH_SIGNAL() }
|
||||||
|
#undef typename
|
||||||
|
};
|
||||||
|
|
||||||
|
#undef SIGNALS_H_FOR_EACH_SIGNAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Define functions which forward their arguments to the connect method
|
||||||
|
/// of the named signal
|
||||||
|
/// @param sig Name of the signal
|
||||||
|
/// @param method Name of the connect method
|
||||||
|
///
|
||||||
|
/// When a signal is a member of a class, we typically want other objects to be
|
||||||
|
/// able to connect to the signal, but not to be able to trigger it. To do this,
|
||||||
|
/// make this signal private then use this macro to create public subscription
|
||||||
|
/// methods
|
||||||
|
///
|
||||||
|
/// Defines AddSignalNameListener
|
||||||
|
#define DEFINE_SIGNAL_ADDERS(sig, method) \
|
||||||
|
template<class A> agi::signal::UnscopedConnection method(A a) { return sig.Connect(a); } \
|
||||||
|
template<class A,class B> agi::signal::UnscopedConnection method(A a,B b) { return sig.Connect(a,b); } \
|
||||||
|
template<class A,class B,class C> agi::signal::UnscopedConnection method(A a,B b,C c) { return sig.Connect(a,b,c); } \
|
||||||
|
template<class A,class B,class C,class D> agi::signal::UnscopedConnection method(A a,B b,C c,D d) { return sig.Connect(a,b,c,d); }
|
|
@ -22,6 +22,7 @@ SRC = \
|
||||||
libaegisub_iconv.cpp \
|
libaegisub_iconv.cpp \
|
||||||
libaegisub_line_iterator.cpp \
|
libaegisub_line_iterator.cpp \
|
||||||
libaegisub_mru.cpp \
|
libaegisub_mru.cpp \
|
||||||
|
libaegisub_signals.cpp \
|
||||||
libaegisub_util.cpp \
|
libaegisub_util.cpp \
|
||||||
libaegisub_vfr.cpp
|
libaegisub_vfr.cpp
|
||||||
|
|
||||||
|
|
105
aegisub/tests/libaegisub_signals.cpp
Normal file
105
aegisub/tests/libaegisub_signals.cpp
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
// Copyright (c) 2010, Thomas Goyne <plorkyeran@aegisub.org>
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
//
|
||||||
|
// $Id$
|
||||||
|
|
||||||
|
/// @file libaegisub_signals.cpp
|
||||||
|
/// @brief agi::signals tests
|
||||||
|
/// @ingroup
|
||||||
|
|
||||||
|
#include <libaegisub/signals.h>
|
||||||
|
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
using namespace agi::signal;
|
||||||
|
|
||||||
|
struct increment {
|
||||||
|
int *num;
|
||||||
|
increment(int &num) : num(&num) { }
|
||||||
|
void operator()() const { ++*num; }
|
||||||
|
void operator()(int n) const { *num += n; }
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(lagi_signal, basic) {
|
||||||
|
Signal<> s;
|
||||||
|
int x = 0;
|
||||||
|
Connection c = s.Connect(increment(x));
|
||||||
|
|
||||||
|
EXPECT_EQ(0, x);
|
||||||
|
s();
|
||||||
|
EXPECT_EQ(1, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(lagi_signal, multiple) {
|
||||||
|
Signal<> s;
|
||||||
|
int x = 0;
|
||||||
|
Connection c1 = s.Connect(increment(x));
|
||||||
|
Connection c2 = s.Connect(increment(x));
|
||||||
|
|
||||||
|
EXPECT_EQ(0, x);
|
||||||
|
s();
|
||||||
|
EXPECT_EQ(2, x);
|
||||||
|
}
|
||||||
|
TEST(lagi_signal, manual_disconnect) {
|
||||||
|
Signal<> s;
|
||||||
|
int x = 0;
|
||||||
|
Connection c1 = s.Connect(increment(x));
|
||||||
|
EXPECT_EQ(0, x);
|
||||||
|
s();
|
||||||
|
EXPECT_EQ(1, x);
|
||||||
|
c1.Disconnect();
|
||||||
|
s();
|
||||||
|
EXPECT_EQ(1, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(lagi_signal, auto_disconnect) {
|
||||||
|
Signal<> s;
|
||||||
|
int x = 0;
|
||||||
|
|
||||||
|
EXPECT_EQ(0, x);
|
||||||
|
{
|
||||||
|
Connection c = s.Connect(increment(x));
|
||||||
|
s();
|
||||||
|
EXPECT_EQ(1, x);
|
||||||
|
}
|
||||||
|
s();
|
||||||
|
EXPECT_EQ(1, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(lagi_signal, connection_outlives_slot) {
|
||||||
|
int x = 0;
|
||||||
|
Connection c;
|
||||||
|
|
||||||
|
EXPECT_EQ(0, x);
|
||||||
|
{
|
||||||
|
Signal<> s;
|
||||||
|
c = s.Connect(increment(x));
|
||||||
|
s();
|
||||||
|
EXPECT_EQ(1, x);
|
||||||
|
}
|
||||||
|
c.Disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(lagi_signal, one_arg) {
|
||||||
|
Signal<int> s;
|
||||||
|
int x = 0;
|
||||||
|
|
||||||
|
Connection c = s.Connect(increment(x));
|
||||||
|
s(0);
|
||||||
|
EXPECT_EQ(0, x);
|
||||||
|
s(10);
|
||||||
|
EXPECT_EQ(10, x);
|
||||||
|
s(20);
|
||||||
|
EXPECT_EQ(30, x);
|
||||||
|
}
|
Loading…
Reference in a new issue