# HG changeset patch
# User SokoL_SD
# Date 1242320965 0
# Node ID 6faf3d3cb95ebc2520df974d20408b5c41d80949
# Parent 056e83133e29495c3d27d2b00112fbfdf7f2be96
CMake: implement install and package targets.
diff -r 056e83133e29 -r 6faf3d3cb95e CMakeLists.txt
--- a/CMakeLists.txt Thu May 14 15:58:18 2009 +0000
+++ b/CMakeLists.txt Thu May 14 17:09:25 2009 +0000
@@ -32,6 +32,7 @@
string(REGEX MATCH "Digital Mars D Compiler v[0-9]\\.[0-9]+" dmd_version "${d_output}")
if (dmd_version)
set(D_IS_MARS true)
+ set(D_IS_DMD true)
set(D_COMPILER_NAME "Digital Mars D Compiler")
string(REGEX REPLACE "Digital Mars D Compiler v([0-9])\\.[0-9]+" "\\1" D_VERSION "${dmd_version}")
string(REGEX REPLACE "Digital Mars D Compiler v[0-9]\\.([0-9]+)" "\\1" D_FRONTEND "${dmd_version}")
@@ -41,6 +42,7 @@
exec_program(${DC} ARGS "--version" OUTPUT_VARIABLE d_output)
string(REGEX MATCH "based on DMD v[0-9]\\.[0-9]+" ldc_version "${d_output}")
set(D_IS_LLVM true)
+ set(D_IS_LDC true)
if(ldc_version)
set(D_IS_LLVM true)
set(D_COMPILER_NAME "LLVM-based D Compiler")
@@ -74,12 +76,11 @@
if (D_FRONTEND LESS "041")
message(STATUS "Minimum required version of D compiler is 1.041 (or compiler based on this version)")
endif(D_FRONTEND LESS "041")
- set(D_TARGET d1-tango)
- #set(D_FLAGS ${D_FLAGS} -I${CMAKE_SOURCE_DIR}/qtd/d1)
+ set(D_TARGET d1-tango)
elseif(D_VERSION EQUAL "2")
set(D_TARGET ) ## TODO: hm... I don`t known this parameter for D2 ^(
- #set(D_FLAGS ${D_FLAGS} -I${CMAKE_SOURCE_DIR}/qtd/d2)
endif(D_VERSION EQUAL "1")
+set(D_FLAGS ${D_FLAGS} -I${CMAKE_SOURCE_DIR}/qt/d${D_VERSION})
# Debug and release flags.
if (${CMAKE_BUILD_TYPE} MATCHES [dD][eE][bB][uU][gG])
@@ -92,6 +93,11 @@
#set(CMAKE_BUILD_TYPE Release)
add_definitions(-UNO_DEBUG)
set(D_FLAGS ${D_FLAGS} -O -release -inline)
+ if(D_IS_MARS)
+ set(D_FLAGS ${D_FLAGS} -inline)
+ elseif(D_IS_LLVM)
+ set(D_FLAGS ${D_FLAGS} -enable-inlining)
+ endif(D_IS_MARS)
if(${CMAKE_SYSTEM_NAME} STREQUAL Windows)
set(D_FLAGS ${D_FLAGS} -L/subsystem:windows)
endif(${CMAKE_SYSTEM_NAME} STREQUAL Windows)
@@ -330,19 +336,33 @@
set(d_files)
set(classes)
set(d_generated_files)
+ set(d_version_files)
set(link_example)
include (${CMAKE_SOURCE_DIR}/build/${package}.txt)
## Loading package sources list.
+ foreach(d_source ${d_version_files})
+ set(d_sources ${d_sources} ${CMAKE_SOURCE_DIR}/qt/d${D_VERSION}/qt/${d_source}.d)
+ if(NOT GENERATE_DI_FILES)
+ get_filename_component(path ${d_source}.d PATH)
+ get_filename_component(name ${d_source}.d NAME_WE)
+ install(FILES ${CMAKE_SOURCE_DIR}/qt/d${D_VERSION}/qt/${d_source}.d DESTINATION include/d/qtd/${path} RENAME ${name}.di)
+ endif(NOT GENERATE_DI_FILES)
+ endforeach(d_source)
foreach(d_source ${d_files})
set(d_sources ${d_sources} ${CMAKE_SOURCE_DIR}/qt/${d_source}.d)
+ if(NOT GENERATE_DI_FILES)
+ get_filename_component(path ${d_source}.d PATH)
+ get_filename_component(name ${d_source}.d NAME_WE)
+ install(FILES ${CMAKE_SOURCE_DIR}/qt/${d_source}.d DESTINATION include/d/qtd/${path} RENAME ${name}.di)
+ endif(NOT GENERATE_DI_FILES)
endforeach(d_source)
foreach(d_source ${d_generated_files})
set(d_sources ${d_sources} ${CMAKE_BINARY_DIR}/qt/${d_source}.d)
- get_filename_component(path ${d_source}.d PATH)
- get_filename_component(path ${d_source}.d NAME_WE)
if(NOT GENERATE_DI_FILES)
- install(FILES ${d_source} DESTINATION include/d/qtd/${path} RENAME ${class}.di)
+ get_filename_component(path ${d_source}.d PATH)
+ get_filename_component(name ${d_source}.d NAME_WE)
+ install(FILES ${CMAKE_BINARY_DIR}/qt/${d_source}.d DESTINATION include/d/qtd/${path} RENAME ${name}.di)
endif(NOT GENERATE_DI_FILES)
endforeach(d_source)
foreach (cpp_source ${cpp_files})
@@ -355,7 +375,7 @@
add_sources_for_generating(${CMAKE_BINARY_DIR}/cpp/qt_${package}/${class}_shell.cpp)
add_sources_for_generating(${CMAKE_BINARY_DIR}/qt/${package}/${class}.d)
if(NOT GENERATE_DI_FILES)
- install(FILES ${d_source} DESTINATION include/d/qtd/${package} RENAME ${class}.di)
+ install(FILES ${CMAKE_BINARY_DIR}/qt/${package}/${class}.d DESTINATION include/d/qtd/${package} RENAME ${class}.di)
endif(NOT GENERATE_DI_FILES)
endforeach(class)
@@ -407,7 +427,7 @@
COMMENT "Linking ${lib_name}"
)
endif(${CMAKE_SYSTEM_NAME} STREQUAL Windows)
- install(FILES ${lib} DESTINATION lib)
+ install(FILES ${CMAKE_BINARY_DIR}/${lib} DESTINATION lib)
## Dependences.
add_dependencies(cpp_${package} dgen)
@@ -438,7 +458,7 @@
endif(${CMAKE_SYSTEM_NAME} STREQUAL Windows AND D_IS_MARS)
if(GENERATE_DI_FILES)
- set(regexp_str "(${CMAKE_BINARY_DIR}|${CMAKE_SOURCE_DIR})/([A-Za-z0-9\\-_\\\\/]+)[/]+([A-Za-z0-9\\-_\\\\]+).d")
+ set(regexp_str "(${CMAKE_SOURCE_DIR}/qt/d${D_VERSION}|${CMAKE_BINARY_DIR}|${CMAKE_SOURCE_DIR})/([A-Za-z0-9\\-_\\\\/]+)[/]+([A-Za-z0-9\\-_\\\\]+).d")
foreach(source ${d_sources})
# find_file(source ${source} PATHS ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}
# ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR})
@@ -479,4 +499,20 @@
if(BUILD_EXAMPLES)
add_subdirectory(demos)
add_subdirectory(examples)
-endif(BUILD_EXAMPLES)
\ No newline at end of file
+endif(BUILD_EXAMPLES)
+
+
+##--------------------------------------------
+## CPack.
+##--------------------------------------------
+set(CPACK_PACKAGE_VERSION_PATCH 1)
+set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "QtD is a D binding to the Qt")
+set(CPACK_PACKAGE_VENDOR "QtD team")
+set(CPACK_PACKAGE_CONTACT "e@mail.ru" )
+SET(CPACK_PACKAGE_VERSION "0.1")
+#set(CPACK_PACKAGE_VERSION_PATCH "${RFS_VERSION_BUILD}")
+set(CPACK_PACKAGE_INSTALL_DIRECTORY "qtd ${CPACK_PACKAGE_VERSION}")
+set(CPACK_PACKAGE_FILE_NAME "qtd-${CPACK_PACKAGE_VERSION}")
+set(CPACK_SOURCE_PACKAGE_FILE_NAME "qtd-${CPACK_PACKAGE_VERSION}")
+SET(CPACK_GENERATOR "TBZ2;DEB;RPM")
+include(CPack)
\ No newline at end of file
diff -r 056e83133e29 -r 6faf3d3cb95e build/core.txt
--- a/build/core.txt Thu May 14 15:58:18 2009 +0000
+++ b/build/core.txt Thu May 14 17:09:25 2009 +0000
@@ -9,13 +9,14 @@
qt_core/QString_shell qt_core/QVariant_shell
qt_core/QModelIndex_shell)
## Module specific d files.
-set (d_files QGlobal qtd/Str QtDObject Signal d1/Signal qtd/ArrayOpsPrimitive
+set (d_files QGlobal qtd/Str QtDObject qtd/ArrayOpsPrimitive
core/QPoint core/QPointF
core/QSize core/QSizeF
core/QLine core/QLineF
core/QRect core/QRectF
core/QString core/QVariant
core/QModelIndex)
+set (d_version_files Signal)
set (d_generated_files core/Qt)
## Classes.
set (classes
diff -r 056e83133e29 -r 6faf3d3cb95e qt/d1/Signal.d
--- a/qt/d1/Signal.d Thu May 14 15:58:18 2009 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1033 +0,0 @@
-/**
- *
- * Copyright: Copyright QtD Team, 2008-2009
- * Authors: Max Samukha, Eldar Insafutdinov
- * License: 1)
- memmove(a.ptr + dest, a.ptr + src, length * T.sizeof);
-}
-
-
-public class SignalException : Exception
-{
- this(char[] msg)
- {
- super(msg);
- }
-}
-
-struct Fn
-{
- void* funcptr;
-
- static typeof(*this) opCall(R, A...)(R function(A) fn)
- {
- typeof(*this) r;
- r.funcptr = fn;
- return r;
- }
-
- template call(R)
- {
- R call(A...)(A args)
- {
- alias R function(A) Fn;
- return (cast(Fn)funcptr)(args);
- }
- }
-
- S get(S)()
- {
- static assert (is(typeof(*S.init) == function));
- return cast(S)funcptr;
- }
-
- debug string toString()
- {
- return Stdout.layout.convert("funcptr: {}", funcptr);
- }
-}
-
-struct Dg
-{
- void* context;
- void* funcptr;
-
- static typeof(*this) opCall(R, A...)(R delegate(A) dg)
- {
- typeof(*this) r;
- r.context = dg.ptr;
- r.funcptr = dg.funcptr;
- return r;
- }
-
- template call(R)
- {
- R call(A...)(A args)
- {
- R delegate(A) dg; // BUG: parameter storage classes are ignored
- dg.ptr = context;
- dg.funcptr = cast(typeof(dg.funcptr))funcptr;
- return dg(args);
- }
- }
-
- S get(S)()
- {
- static assert (is(S == delegate));
- S r;
- r.ptr = context;
- r.funcptr = cast(typeof(r.funcptr))funcptr;
- return r;
- }
-
- debug string toString()
- {
- return Stdout.layout.convert("context: {}, funcptr: {}", context, funcptr);
- }
-}
-
-struct Slot(R)
-{
- alias R Receiver;
-
- Receiver receiver;
- Dg invoker;
-
- static if (is(Receiver == Dg))
- {
- static const isDelegate = true;
-
- void onReceiverDisposed(Object o)
- {
- assert (lock !is null);
- synchronized(lock)
- {
- receiver.context = null;
- receiver.funcptr = null;
- }
- }
-
- // null if receiver doesn't point to a disposable object
- Object lock;
-
- bool isDisposed()
- {
- return !receiver.funcptr;
- }
-
- Object getObject()
- {
- return lock ? _d_toObject(receiver.context) : null;
- }
- }
- else
- static const isDelegate = false;
-
- static typeof(*this) opCall(Receiver r, Dg c)
- {
- typeof(*this) ret;
- ret.receiver = r;
- ret.invoker = c;
- return ret;
- }
-
- debug string toString()
- {
- return Stdout.layout.convert("receiver: {}, invoker {}: ", receiver, invoker);
- }
-}
-
-enum SlotListId
-{
- Func, // function pointers
- Weak, // object delegates stored in C heap
- Strong // delegates stored in GC heap
-}
-
-/**
- Used to specify the type of a signal-to-slot connection.
-
- Examples:
-----
-class Sender
-{
- mixin Signal!("changed");
- void change()
- {
- changed.emit;
- }
-}
-
-
-class Receiver
-{
- void alarm() {}
-}
-
-void main()
-{
- auto s = new Sender;
- auto r = new Receiver;
- s.changed.connect(&r.alarm); // now s weakly references r
-
- r = null;
- // collect garbage (assume there is no more reachable pointers
- // to the receiver and it gets finalized)
- ...
-
- s.change;
- // weak reference to the receiving object
- // has been removed from the sender's connection lists.
-
- r = new Receiver;
- s.changed.connect(&r.alarm, ConnectionFlags.Strong);
-
- r = null;
- // collect garbage
- ...
- // the receiving object has not been finalized because s strongly references it.
-
- s.change; // the receiver is called.
- delete r;
- s.change; // the receiver is disconnected from the sender.
-
- static void foo()
- {
- }
-
- s.changed.connect(&foo);
- s.changed.emit; // foo is called.
- s.changed.disconnect(&foo); // must be explicitly disconnected.
-
- void bar()
- {
- }
-
- // ConnectionFlags.NoObject must be specified for delegates
- // to non-static local functions or struct member functions.
- s.changed.connect(&bar, ConnectionFlags.NoObject);
- s.changed.emit; // bar is called.
- s.changed.disconnect(&bar); // must be explicitly disconnected.
-}
-----
-*/
-public enum ConnectionFlags
-{
- ///
- None,
- /**
- The receiver will be stored as weak reference (implied if ConnectionFlags.NoObject is not specified).
- If the signal receiver is not a function pointer or a delegate referencing a D class instance.
- the sender will not be notified when the receiving object is deleted and emitting the signal
- connected to that receiving object will result in undefined behavior.
- */
- Weak = 0x0001,
- /**
- The receiver is stored as strong reference (implied if ConnectionFlags.NoObject is specified).
- */
- Strong = 0x0002,
- /**
- Must be specified if the receiver is not a function pointer or a delegate referencing a D class instance.
- */
- NoObject = 0x0004
-
- // Queued = 0x0004,
- // BlockingQueued = 0x0008
-}
-
-
-struct SlotList(SlotT, bool strong = false)
-{
- alias SlotT SlotType;
- SlotType[] data;
-
- void length(size_t length)
- {
- static if (strong)
- data.length = length;
- else
- realloc(data, length);
- }
-
- SlotType* add(SlotType slot)
- {
- auto oldLen = data.length;
- length = oldLen + 1;
- auto p = &data[oldLen];
- *p = slot;
- return p;
- }
-
- SlotType* get(int slotId)
- {
- return &data[slotId];
- }
-
- void remove(int slotId)
- {
- move(data, slotId, slotId + 1, data.length - slotId - 1);
- data = data[0..$ - 1];
- }
-
- size_t length()
- {
- return data.length;
- }
-
- void free()
- {
- static if (SlotType.isDelegate)
- {
- foreach (ref slot; data)
- {
- if (auto obj = slot.getObject)
- rt_detachDisposeEvent(obj, &slot.onReceiverDisposed);
- }
- }
- static if (!strong)
- cfree(data.ptr);
- }
-
- debug string toString()
- {
- string r;
- foreach(e; data)
- r ~= e.toString ~ "\n";
- return r;
- }
-}
-
-public alias void delegate(int signalId) SignalEvent;
-
-struct SignalConnections
-{
- bool isInUse;
-
- STuple!(
- SlotList!(Slot!(Fn)),
- SlotList!(Slot!(Dg)),
- SlotList!(Slot!(Dg), true)
- ) slotLists;
-
- STuple!(
- Fn[],
- Dg[]
- ) delayedDisconnects;
-
- void addDelayedDisconnect(Fn r)
- {
- delayedDisconnects.at!(0) ~= r;
- }
-
- void addDelayedDisconnect(Dg r)
- {
- delayedDisconnects.at!(1) ~= r;
- }
-
- SlotListType!(slotListId)* getSlotList(int slotListId)()
- {
- return &slotLists.tupleof[slotListId];
- }
-
- bool hasSlots()
- {
- foreach(i, e; slotLists.tupleof)
- {
- if (slotLists.tupleof[i].length)
- return true;
- }
- return false;
- }
-
- int slotCount()
- {
- int count;
- foreach(i, e; slotLists.tupleof)
- count += slotLists.at!(i).length;
- return count;
- }
-
- void slotListLengths(int[] lengths)
- {
- foreach(i, e; slotLists.tupleof)
- lengths[i] = slotLists.at!(i).length;
- }
-
- SlotType!(slotListId)* addSlot(int slotListId)(SlotType!(slotListId) slot)
- {
- return getSlotList!(slotListId).add(slot);
- }
-
- void removeSlot(int slotListId)(int slotId)
- {
- slotLists.at!(slotListId).remove(slotId);
- }
-
- void free()
- {
- foreach(i, e; slotLists.tupleof)
- {
- static if (is(typeof(slotLists.at!(i).free)))
- slotLists.at!(i).free;
- }
- }
-
- template SlotListType(int slotListId)
- {
- alias typeof(slotLists.tupleof)[slotListId] SlotListType;
- }
-
- template SlotType(int slotListId)
- {
- alias SlotListType!(slotListId).SlotType SlotType;
- }
-
- template ReceiverType(int slotListId)
- {
- alias SlotType!(slotListId).Receiver ReceiverType;
- }
-
- debug string toString()
- {
- string r;
- foreach(i, e; slotLists.tupleof)
- {
- r ~= Stdout.layout.convert("Slot list {}:", i);
- r ~= slotLists.at!(i).toString;
- }
- return r;
- }
-
- static const slotListCount = slotLists.tupleof.length;
-}
-
-
-private ThreadLocal!(Object) signalSender_;
-static this()
-{
- signalSender_ = new ThreadLocal!(Object);
-}
-
-/**
- If called from a slot, returns the object
- that is emitting the signal. Otherwise, returns null.
-*/
-public Object signalSender() {
- return signalSender_.val;
-}
-
-public class SignalHandler
-{
- SignalConnections[] connections;
- Object owner;
- int blocked;
-
- // Called after a slot has been connected to an empty signal
- SignalEvent firstSlotConnected;
- // Called after the last slot has been disconnected from a signal
- SignalEvent lastSlotDisconnected;
-
- alias SignalConnections.SlotType SlotType;
- alias SignalConnections.ReceiverType ReceiverType;
-
- public this(Object owner_) {
- owner = owner_;
- }
-
- private SignalConnections* getConnections(int signalId)
- {
- if (signalId < connections.length)
- return &connections[signalId];
- return null;
- }
-
- private SlotType!(slotListId)* addSlot(int slotListId)(int signalId, ReceiverType!(slotListId) receiver,
- Dg invoker)
- {
- if (signalId >= connections.length)
- connections.length = signalId + 1;
- auto slot = connections[signalId].addSlot!(slotListId)(SlotType!(slotListId)(receiver, invoker));
-
- if (firstSlotConnected && connections[signalId].slotCount == 1)
- firstSlotConnected(signalId);
-
- return slot;
- }
-
- private void removeSlot(int slotListId)(int signalId, int slotId)
- {
- connections[signalId].removeSlot!(slotListId)(slotId);
-
- if (lastSlotDisconnected && !connections[signalId].slotCount)
- lastSlotDisconnected(signalId);
- }
-
- private SlotType!(slotListId)* addObjectSlot(int slotListId)(size_t signalId, Object obj, Dg receiver,
- Dg invoker)
- {
- auto slot = addSlot!(slotListId)(signalId, receiver, invoker);
- slot.lock = this;
- rt_attachDisposeEvent(obj, &slot.onReceiverDisposed);
- return slot;
- }
-
- size_t slotCount(int signalId)
- {
- synchronized(this)
- {
- auto con = getConnections(signalId);
- if (con)
- return con.slotCount;
- return 0;
- }
- }
-
- void connect(Receiver)(size_t signalId, Receiver receiver,
- Dg invoker, ConnectionFlags flags)
- {
- synchronized(this)
- {
- static if (is(typeof(receiver.context)))
- {
- Object obj;
- if ((flags & ConnectionFlags.NoObject) || (obj = _d_toObject(receiver.context)) is null)
- {
- // strong by default
- if (flags & ConnectionFlags.Weak)
- addSlot!(SlotListId.Weak)(signalId, receiver, invoker);
- else
- addSlot!(SlotListId.Strong)(signalId, receiver, invoker);
- }
- else
- {
- // weak by default
- if (flags & ConnectionFlags.Strong)
- addObjectSlot!(SlotListId.Strong)(signalId, obj, receiver, invoker);
- else
- addObjectSlot!(SlotListId.Weak)(signalId, obj, receiver, invoker);
- }
- }
- else
- addSlot!(SlotListId.Func)(signalId, receiver, invoker);
- }
- }
-
- void disconnect(Receiver)(int signalId, Receiver receiver)
- {
- synchronized(this)
- {
- auto cons = getConnections(signalId);
- if (!cons)
- return;
-
- // if called from a slot being executed by this signal, delay disconnection
- // until all slots has been called.
- if (cons.isInUse)
- {
- cons.addDelayedDisconnect(receiver);
- return;
- }
-
- TOP:
- foreach (slotListId, e; cons.slotLists.tupleof)
- {
- /// COMPILER BUG: ReceiverType is evaluated to expression instead of type.
- static if (is(typeof(cons.ReceiverType!(slotListId)) == Receiver))
- {
- auto slotList = cons.getSlotList!(slotListId);
- for (int slotId; slotId < slotList.length;)
- {
- auto slot = slotList.get(slotId);
- static if (slot.isDelegate)
- {
- if (slot.isDisposed)
- {
- removeSlot!(slotListId)(signalId, slotId);
- continue;
- }
- }
-
- if (slot.receiver == receiver)
- {
- static if (slot.isDelegate)
- {
- if (auto obj = slot.getObject)
- rt_detachDisposeEvent(obj, &slot.onReceiverDisposed);
- }
- removeSlot!(slotListId)(signalId, slotId);
- break TOP;
- }
-
- slotId++;
- }
- }
- }
- }
- }
-
- void emit(A...)(size_t signalId, A args)
- {
- synchronized(this)
- {
- if (signalId >= connections.length || blocked)
- return;
- auto cons = &connections[signalId];
-
- if (cons.hasSlots)
- {
- {
- cons.isInUse = true;
- signalSender_.val = owner;
- scope(exit)
- {
- cons.isInUse = false;
- signalSender_.val = null;
- }
-
- // Store the lengths to avoid calling new slots
- // connected in the slots being called.
- // dmd bug: int[cons.slotListCount] fails
- static const c = cons.slotListCount;
- int[c] lengths = void;
- cons.slotListLengths(lengths);
-
- foreach (slotListId, e; cons.slotLists.tupleof)
- {
- auto slotList = cons.getSlotList!(slotListId);
- for (size_t slotId; slotId < lengths[slotListId];)
- {
- auto slot = slotList.get(slotId);
- static if (slot.isDelegate)
- {
- if (slot.isDisposed)
- {
- removeSlot!(slotListId)(signalId, slotId);
- lengths[slotListId]--;
- continue;
- }
- }
-
- slot.invoker.call!(void)(slot.receiver, args);
- ++slotId;
- }
- }
- }
-
-
- // process delayed disconnects if any
- foreach(i, e; cons.delayedDisconnects.tupleof)
- {
- if (cons.delayedDisconnects.at!(i).length)
- {
- foreach (d; cons.delayedDisconnects.at!(i))
- disconnect(signalId, d);
- cons.delayedDisconnects.at!(i).length = 0;
- }
- }
- }
- }
- }
-
- // Adjusts signal arguments and calls the slot. S - slot signature, A - signal arguments
- private void invokeSlot(S, Receiver, A...)(Receiver r, A args)
- {
- r.get!(S)()(args[0..ParameterTupleOf!(S).length]);
- }
-
- void blockSignals()
- {
- synchronized(this)
- blocked++;
- }
-
- void unblockSignals()
- {
- synchronized(this)
- {
- if(!blocked)
- throw new SignalException("Signals are not blocked");
- blocked--;
- }
- }
-
- ~this()
- {
- foreach(ref c; connections)
- c.free;
- }
-
- debug string toString()
- {
- string r;
- foreach (i, c; connections)
- r ~= Stdout.layout.convert("Signal {}:\n{}", i, c.toString);
- return r;
- }
-}
-
-//TODO: this could be avoided if named mixins didn't suck.
-public struct SignalOps(int sigId, A...)
-{
- private SignalHandler sh;
- enum { signalId = sigId }
-
- void connect(R, B...)(R function(B) fn, ConnectionFlags flags = ConnectionFlags.None)
- {
- alias CheckSlot!(typeof(fn), A) check;
- auto invoker = Dg(&sh.invokeSlot!(typeof(fn), Fn, A));
- sh.connect(signalId, Fn(fn), invoker, flags);
- }
-
- void connect(R, B...)(R delegate(B) dg, ConnectionFlags flags = ConnectionFlags.None)
- {
- alias CheckSlot!(typeof(dg), A) check;
- auto invoker = Dg(&sh.invokeSlot!(typeof(dg), Dg, A));
- sh.connect(signalId, Dg(dg), invoker, flags);
- }
-
- void disconnect(R, B...)(R function(B) fn)
- {
- sh.disconnect(signalId, Fn(fn));
- }
-
- void disconnect(R, B...)(R delegate(B) dg)
- {
- sh.disconnect(signalId, Dg(dg));
- }
-
- void emit(A args)
- {
- sh.emit(signalId, args);
- }
-
- debug size_t slotCount()
- {
- return sh.slotCount(signalId);
- }
-}
-
-template CheckSlot(Slot, A...)
-{
- static assert(ParameterTupleOf!(Slot).length <= A.length, "Slot " ~ ParameterTypeTuple!(Slot).stringof ~
- " has more prameters than signal " ~ A.stringof);
- alias CheckSlotImpl!(Slot, 0, A) check;
-}
-
-template CheckSlotImpl(Slot, int i, A...)
-{
- alias ParameterTupleOf!(Slot) SlotArgs;
- static if (i < SlotArgs.length)
- {
- static assert (is(SlotArgs[i] : A[i]), "Argument " ~ __toString(i) ~
- ":" ~ A[i].stringof ~ " of signal " ~ A.stringof ~ " is not implicitly convertible to parameter "
- ~ SlotArgs[i].stringof ~ " of slot " ~ SlotArgs.stringof);
- alias CheckSlotImpl!(Slot, i + 1, A) next;
- }
-}
-
-public template SignalHandlerOps()
-{
- static assert (is(typeof(this.signalHandler)),
- "SignalHandlerOps is already instantiated in " ~ typeof(this).stringof ~ " or one of its base classes");
-
-protected:
- SignalHandler signalHandler_; // manages signal-to-slot connections
-
- final SignalHandler signalHandler()
- {
- if (!signalHandler_)
- {
- signalHandler_ = new SignalHandler(this);
- onSignalHandlerCreated(signalHandler_);
- }
- return signalHandler_;
- }
-
- void onSignalHandlerCreated(ref SignalHandler sh)
- {
- }
-
-public:
- final void blockSignals()
- {
- signalHandler.blockSignals();
- }
-
- final void unblockSignals()
- {
- signalHandler.unblockSignals();
- }
-}
-
-/**
- Examples:
-----
-struct Args
-{
- bool cancel;
-}
-
-class C
-{
- private int _x;
- // reference parameters are not supported yet,
- // so we pass arguments by pointer.
- mixin Signal!("xChanging", int, Args*);
- mixin Signal!("xChanged");
-
- void x(int v)
- {
- if (v != _x)
- {
- Args args;
- xChanging.emit(v, &args);
- if (!args.cancel)
- {
- _x = v;
- xChanged.emit;
- }
- }
- }
-}
-----
-*/
-template Signal(string name, A...)
-{
- mixin SignalImpl!(0, name, A);
-}
-
-template SignalImpl(int index, string name, A...)
-{
- static if (is(typeof(mixin(typeof(this).stringof ~ ".__sig" ~ ToString!(index)))))
- mixin SignalImpl!(index + 1, name, A);
- else
- {
- // mixed-in once
- static if (!is(typeof(this.signalHandler)))
- {
- mixin SignalHandlerOps;
- }
- mixin("private static const int __sig" ~ ToString!(index) ~ " = " ~ ToString!(index) ~ ";");
- mixin("SignalOps!(" ~ ToString!(index) ~ ", A) " ~ name ~ "(){ return SignalOps!("
- ~ ToString!(index) ~ ", A)(signalHandler); }");
- }
-}
-
-extern(C) alias void function(void*) SlotConnector;
-
-debug (UnitTest)
-{
- class A
- {
- mixin Signal!("scorched", int);
-
- int signalId1 = -1;
- int signalId2 = -1;
-
- void onFirstConnect(int sId)
- {
- signalId1 = sId;
- }
-
- void onLastDisconnect(int sId)
- {
- signalId2 = sId;
- }
-
- this()
- {
- signalHandler.firstSlotConnected = &onFirstConnect;
- signalHandler.lastSlotDisconnected = &onLastDisconnect;
- }
- }
-
- class B : A
- {
- mixin Signal!("booed", int);
-
- int bazSum;
- void baz(int i)
- {
- bazSum += i;
- }
- }
-
- class C : A
- {
- mixin Signal!("cooked");
- }
-}
-
-unittest
-{
- static int fooSum;
- static int barSum;
-
- static void foo(int i)
- {
- fooSum += i;
- }
-
- void bar(long i)
- {
- barSum += i;
- }
-
- auto a = new A;
- auto b = new B;
- auto c = new C;
- assert(b.scorched.signalId == 0);
- assert(b.booed.signalId == 1);
- assert(c.cooked.signalId == 1);
-
- auto sh = b.signalHandler;
-
- b.scorched.connect(&foo);
- assert(sh.connections.length == 1);
- assert(b.signalId1 == 0);
- auto scCons = &sh.connections[0];
-
- assert(scCons.getSlotList!(SlotListId.Func).length == 1);
- b.scorched.emit(1);
- assert(fooSum == 1);
-
- b.scorched.connect(&bar, ConnectionFlags.NoObject);
- assert(sh.connections.length == 1);
- assert(scCons.getSlotList!(SlotListId.Strong).length == 1);
- b.scorched.emit(1);
- assert (fooSum == 2 && barSum == 1);
-
- b.scorched.connect(&b.baz);
- assert(scCons.getSlotList!(SlotListId.Weak).length == 1);
- b.scorched.emit(1);
- assert (fooSum == 3 && barSum == 2 && b.bazSum == 1);
-
- b.scorched.disconnect(&bar);
- assert(scCons.slotCount == 2);
- b.scorched.disconnect(&b.baz);
- assert(scCons.slotCount == 1);
- b.scorched.disconnect(&foo);
- assert(scCons.slotCount == 0);
- assert(b.signalId2 == 0);
-
- fooSum = 0;
- void connectFoo()
- {
- b.scorched.connect(&foo);
- b.scorched.disconnect(&connectFoo);
- }
-
- b.scorched.connect(&connectFoo, ConnectionFlags.NoObject);
- b.scorched.emit(1);
- assert(scCons.getSlotList!(SlotListId.Func).length == 1);
- assert(scCons.getSlotList!(SlotListId.Strong).length == 0);
- assert(!fooSum);
-
- auto r = new B();
- b.scorched.connect(&r.baz);
- assert(scCons.getSlotList!(SlotListId.Weak).length == 1);
- b.scorched.emit(1);
- assert(r.bazSum == 1);
- assert(fooSum == 1);
-
- delete(r);
- assert(scCons.getSlotList!(SlotListId.Weak).length == 1);
- b.scorched.emit(1);
- assert(scCons.getSlotList!(SlotListId.Weak).length == 0);
-}
\ No newline at end of file
diff -r 056e83133e29 -r 6faf3d3cb95e qt/d1/qt/Signal.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qt/d1/qt/Signal.d Thu May 14 17:09:25 2009 +0000
@@ -0,0 +1,1033 @@
+/**
+ *
+ * Copyright: Copyright QtD Team, 2008-2009
+ * Authors: Max Samukha, Eldar Insafutdinov
+ * License: 1)
+ memmove(a.ptr + dest, a.ptr + src, length * T.sizeof);
+}
+
+
+public class SignalException : Exception
+{
+ this(char[] msg)
+ {
+ super(msg);
+ }
+}
+
+struct Fn
+{
+ void* funcptr;
+
+ static typeof(*this) opCall(R, A...)(R function(A) fn)
+ {
+ typeof(*this) r;
+ r.funcptr = fn;
+ return r;
+ }
+
+ template call(R)
+ {
+ R call(A...)(A args)
+ {
+ alias R function(A) Fn;
+ return (cast(Fn)funcptr)(args);
+ }
+ }
+
+ S get(S)()
+ {
+ static assert (is(typeof(*S.init) == function));
+ return cast(S)funcptr;
+ }
+
+ debug string toString()
+ {
+ return Stdout.layout.convert("funcptr: {}", funcptr);
+ }
+}
+
+struct Dg
+{
+ void* context;
+ void* funcptr;
+
+ static typeof(*this) opCall(R, A...)(R delegate(A) dg)
+ {
+ typeof(*this) r;
+ r.context = dg.ptr;
+ r.funcptr = dg.funcptr;
+ return r;
+ }
+
+ template call(R)
+ {
+ R call(A...)(A args)
+ {
+ R delegate(A) dg; // BUG: parameter storage classes are ignored
+ dg.ptr = context;
+ dg.funcptr = cast(typeof(dg.funcptr))funcptr;
+ return dg(args);
+ }
+ }
+
+ S get(S)()
+ {
+ static assert (is(S == delegate));
+ S r;
+ r.ptr = context;
+ r.funcptr = cast(typeof(r.funcptr))funcptr;
+ return r;
+ }
+
+ debug string toString()
+ {
+ return Stdout.layout.convert("context: {}, funcptr: {}", context, funcptr);
+ }
+}
+
+struct Slot(R)
+{
+ alias R Receiver;
+
+ Receiver receiver;
+ Dg invoker;
+
+ static if (is(Receiver == Dg))
+ {
+ static const isDelegate = true;
+
+ void onReceiverDisposed(Object o)
+ {
+ assert (lock !is null);
+ synchronized(lock)
+ {
+ receiver.context = null;
+ receiver.funcptr = null;
+ }
+ }
+
+ // null if receiver doesn't point to a disposable object
+ Object lock;
+
+ bool isDisposed()
+ {
+ return !receiver.funcptr;
+ }
+
+ Object getObject()
+ {
+ return lock ? _d_toObject(receiver.context) : null;
+ }
+ }
+ else
+ static const isDelegate = false;
+
+ static typeof(*this) opCall(Receiver r, Dg c)
+ {
+ typeof(*this) ret;
+ ret.receiver = r;
+ ret.invoker = c;
+ return ret;
+ }
+
+ debug string toString()
+ {
+ return Stdout.layout.convert("receiver: {}, invoker {}: ", receiver, invoker);
+ }
+}
+
+enum SlotListId
+{
+ Func, // function pointers
+ Weak, // object delegates stored in C heap
+ Strong // delegates stored in GC heap
+}
+
+/**
+ Used to specify the type of a signal-to-slot connection.
+
+ Examples:
+----
+class Sender
+{
+ mixin Signal!("changed");
+ void change()
+ {
+ changed.emit;
+ }
+}
+
+
+class Receiver
+{
+ void alarm() {}
+}
+
+void main()
+{
+ auto s = new Sender;
+ auto r = new Receiver;
+ s.changed.connect(&r.alarm); // now s weakly references r
+
+ r = null;
+ // collect garbage (assume there is no more reachable pointers
+ // to the receiver and it gets finalized)
+ ...
+
+ s.change;
+ // weak reference to the receiving object
+ // has been removed from the sender's connection lists.
+
+ r = new Receiver;
+ s.changed.connect(&r.alarm, ConnectionFlags.Strong);
+
+ r = null;
+ // collect garbage
+ ...
+ // the receiving object has not been finalized because s strongly references it.
+
+ s.change; // the receiver is called.
+ delete r;
+ s.change; // the receiver is disconnected from the sender.
+
+ static void foo()
+ {
+ }
+
+ s.changed.connect(&foo);
+ s.changed.emit; // foo is called.
+ s.changed.disconnect(&foo); // must be explicitly disconnected.
+
+ void bar()
+ {
+ }
+
+ // ConnectionFlags.NoObject must be specified for delegates
+ // to non-static local functions or struct member functions.
+ s.changed.connect(&bar, ConnectionFlags.NoObject);
+ s.changed.emit; // bar is called.
+ s.changed.disconnect(&bar); // must be explicitly disconnected.
+}
+----
+*/
+public enum ConnectionFlags
+{
+ ///
+ None,
+ /**
+ The receiver will be stored as weak reference (implied if ConnectionFlags.NoObject is not specified).
+ If the signal receiver is not a function pointer or a delegate referencing a D class instance.
+ the sender will not be notified when the receiving object is deleted and emitting the signal
+ connected to that receiving object will result in undefined behavior.
+ */
+ Weak = 0x0001,
+ /**
+ The receiver is stored as strong reference (implied if ConnectionFlags.NoObject is specified).
+ */
+ Strong = 0x0002,
+ /**
+ Must be specified if the receiver is not a function pointer or a delegate referencing a D class instance.
+ */
+ NoObject = 0x0004
+
+ // Queued = 0x0004,
+ // BlockingQueued = 0x0008
+}
+
+
+struct SlotList(SlotT, bool strong = false)
+{
+ alias SlotT SlotType;
+ SlotType[] data;
+
+ void length(size_t length)
+ {
+ static if (strong)
+ data.length = length;
+ else
+ realloc(data, length);
+ }
+
+ SlotType* add(SlotType slot)
+ {
+ auto oldLen = data.length;
+ length = oldLen + 1;
+ auto p = &data[oldLen];
+ *p = slot;
+ return p;
+ }
+
+ SlotType* get(int slotId)
+ {
+ return &data[slotId];
+ }
+
+ void remove(int slotId)
+ {
+ move(data, slotId, slotId + 1, data.length - slotId - 1);
+ data = data[0..$ - 1];
+ }
+
+ size_t length()
+ {
+ return data.length;
+ }
+
+ void free()
+ {
+ static if (SlotType.isDelegate)
+ {
+ foreach (ref slot; data)
+ {
+ if (auto obj = slot.getObject)
+ rt_detachDisposeEvent(obj, &slot.onReceiverDisposed);
+ }
+ }
+ static if (!strong)
+ cfree(data.ptr);
+ }
+
+ debug string toString()
+ {
+ string r;
+ foreach(e; data)
+ r ~= e.toString ~ "\n";
+ return r;
+ }
+}
+
+public alias void delegate(int signalId) SignalEvent;
+
+struct SignalConnections
+{
+ bool isInUse;
+
+ STuple!(
+ SlotList!(Slot!(Fn)),
+ SlotList!(Slot!(Dg)),
+ SlotList!(Slot!(Dg), true)
+ ) slotLists;
+
+ STuple!(
+ Fn[],
+ Dg[]
+ ) delayedDisconnects;
+
+ void addDelayedDisconnect(Fn r)
+ {
+ delayedDisconnects.at!(0) ~= r;
+ }
+
+ void addDelayedDisconnect(Dg r)
+ {
+ delayedDisconnects.at!(1) ~= r;
+ }
+
+ SlotListType!(slotListId)* getSlotList(int slotListId)()
+ {
+ return &slotLists.tupleof[slotListId];
+ }
+
+ bool hasSlots()
+ {
+ foreach(i, e; slotLists.tupleof)
+ {
+ if (slotLists.tupleof[i].length)
+ return true;
+ }
+ return false;
+ }
+
+ int slotCount()
+ {
+ int count;
+ foreach(i, e; slotLists.tupleof)
+ count += slotLists.at!(i).length;
+ return count;
+ }
+
+ void slotListLengths(int[] lengths)
+ {
+ foreach(i, e; slotLists.tupleof)
+ lengths[i] = slotLists.at!(i).length;
+ }
+
+ SlotType!(slotListId)* addSlot(int slotListId)(SlotType!(slotListId) slot)
+ {
+ return getSlotList!(slotListId).add(slot);
+ }
+
+ void removeSlot(int slotListId)(int slotId)
+ {
+ slotLists.at!(slotListId).remove(slotId);
+ }
+
+ void free()
+ {
+ foreach(i, e; slotLists.tupleof)
+ {
+ static if (is(typeof(slotLists.at!(i).free)))
+ slotLists.at!(i).free;
+ }
+ }
+
+ template SlotListType(int slotListId)
+ {
+ alias typeof(slotLists.tupleof)[slotListId] SlotListType;
+ }
+
+ template SlotType(int slotListId)
+ {
+ alias SlotListType!(slotListId).SlotType SlotType;
+ }
+
+ template ReceiverType(int slotListId)
+ {
+ alias SlotType!(slotListId).Receiver ReceiverType;
+ }
+
+ debug string toString()
+ {
+ string r;
+ foreach(i, e; slotLists.tupleof)
+ {
+ r ~= Stdout.layout.convert("Slot list {}:", i);
+ r ~= slotLists.at!(i).toString;
+ }
+ return r;
+ }
+
+ static const slotListCount = slotLists.tupleof.length;
+}
+
+
+private ThreadLocal!(Object) signalSender_;
+static this()
+{
+ signalSender_ = new ThreadLocal!(Object);
+}
+
+/**
+ If called from a slot, returns the object
+ that is emitting the signal. Otherwise, returns null.
+*/
+public Object signalSender() {
+ return signalSender_.val;
+}
+
+public class SignalHandler
+{
+ SignalConnections[] connections;
+ Object owner;
+ int blocked;
+
+ // Called after a slot has been connected to an empty signal
+ SignalEvent firstSlotConnected;
+ // Called after the last slot has been disconnected from a signal
+ SignalEvent lastSlotDisconnected;
+
+ alias SignalConnections.SlotType SlotType;
+ alias SignalConnections.ReceiverType ReceiverType;
+
+ public this(Object owner_) {
+ owner = owner_;
+ }
+
+ private SignalConnections* getConnections(int signalId)
+ {
+ if (signalId < connections.length)
+ return &connections[signalId];
+ return null;
+ }
+
+ private SlotType!(slotListId)* addSlot(int slotListId)(int signalId, ReceiverType!(slotListId) receiver,
+ Dg invoker)
+ {
+ if (signalId >= connections.length)
+ connections.length = signalId + 1;
+ auto slot = connections[signalId].addSlot!(slotListId)(SlotType!(slotListId)(receiver, invoker));
+
+ if (firstSlotConnected && connections[signalId].slotCount == 1)
+ firstSlotConnected(signalId);
+
+ return slot;
+ }
+
+ private void removeSlot(int slotListId)(int signalId, int slotId)
+ {
+ connections[signalId].removeSlot!(slotListId)(slotId);
+
+ if (lastSlotDisconnected && !connections[signalId].slotCount)
+ lastSlotDisconnected(signalId);
+ }
+
+ private SlotType!(slotListId)* addObjectSlot(int slotListId)(size_t signalId, Object obj, Dg receiver,
+ Dg invoker)
+ {
+ auto slot = addSlot!(slotListId)(signalId, receiver, invoker);
+ slot.lock = this;
+ rt_attachDisposeEvent(obj, &slot.onReceiverDisposed);
+ return slot;
+ }
+
+ size_t slotCount(int signalId)
+ {
+ synchronized(this)
+ {
+ auto con = getConnections(signalId);
+ if (con)
+ return con.slotCount;
+ return 0;
+ }
+ }
+
+ void connect(Receiver)(size_t signalId, Receiver receiver,
+ Dg invoker, ConnectionFlags flags)
+ {
+ synchronized(this)
+ {
+ static if (is(typeof(receiver.context)))
+ {
+ Object obj;
+ if ((flags & ConnectionFlags.NoObject) || (obj = _d_toObject(receiver.context)) is null)
+ {
+ // strong by default
+ if (flags & ConnectionFlags.Weak)
+ addSlot!(SlotListId.Weak)(signalId, receiver, invoker);
+ else
+ addSlot!(SlotListId.Strong)(signalId, receiver, invoker);
+ }
+ else
+ {
+ // weak by default
+ if (flags & ConnectionFlags.Strong)
+ addObjectSlot!(SlotListId.Strong)(signalId, obj, receiver, invoker);
+ else
+ addObjectSlot!(SlotListId.Weak)(signalId, obj, receiver, invoker);
+ }
+ }
+ else
+ addSlot!(SlotListId.Func)(signalId, receiver, invoker);
+ }
+ }
+
+ void disconnect(Receiver)(int signalId, Receiver receiver)
+ {
+ synchronized(this)
+ {
+ auto cons = getConnections(signalId);
+ if (!cons)
+ return;
+
+ // if called from a slot being executed by this signal, delay disconnection
+ // until all slots has been called.
+ if (cons.isInUse)
+ {
+ cons.addDelayedDisconnect(receiver);
+ return;
+ }
+
+ TOP:
+ foreach (slotListId, e; cons.slotLists.tupleof)
+ {
+ /// COMPILER BUG: ReceiverType is evaluated to expression instead of type.
+ static if (is(typeof(cons.ReceiverType!(slotListId)) == Receiver))
+ {
+ auto slotList = cons.getSlotList!(slotListId);
+ for (int slotId; slotId < slotList.length;)
+ {
+ auto slot = slotList.get(slotId);
+ static if (slot.isDelegate)
+ {
+ if (slot.isDisposed)
+ {
+ removeSlot!(slotListId)(signalId, slotId);
+ continue;
+ }
+ }
+
+ if (slot.receiver == receiver)
+ {
+ static if (slot.isDelegate)
+ {
+ if (auto obj = slot.getObject)
+ rt_detachDisposeEvent(obj, &slot.onReceiverDisposed);
+ }
+ removeSlot!(slotListId)(signalId, slotId);
+ break TOP;
+ }
+
+ slotId++;
+ }
+ }
+ }
+ }
+ }
+
+ void emit(A...)(size_t signalId, A args)
+ {
+ synchronized(this)
+ {
+ if (signalId >= connections.length || blocked)
+ return;
+ auto cons = &connections[signalId];
+
+ if (cons.hasSlots)
+ {
+ {
+ cons.isInUse = true;
+ signalSender_.val = owner;
+ scope(exit)
+ {
+ cons.isInUse = false;
+ signalSender_.val = null;
+ }
+
+ // Store the lengths to avoid calling new slots
+ // connected in the slots being called.
+ // dmd bug: int[cons.slotListCount] fails
+ static const c = cons.slotListCount;
+ int[c] lengths = void;
+ cons.slotListLengths(lengths);
+
+ foreach (slotListId, e; cons.slotLists.tupleof)
+ {
+ auto slotList = cons.getSlotList!(slotListId);
+ for (size_t slotId; slotId < lengths[slotListId];)
+ {
+ auto slot = slotList.get(slotId);
+ static if (slot.isDelegate)
+ {
+ if (slot.isDisposed)
+ {
+ removeSlot!(slotListId)(signalId, slotId);
+ lengths[slotListId]--;
+ continue;
+ }
+ }
+
+ slot.invoker.call!(void)(slot.receiver, args);
+ ++slotId;
+ }
+ }
+ }
+
+
+ // process delayed disconnects if any
+ foreach(i, e; cons.delayedDisconnects.tupleof)
+ {
+ if (cons.delayedDisconnects.at!(i).length)
+ {
+ foreach (d; cons.delayedDisconnects.at!(i))
+ disconnect(signalId, d);
+ cons.delayedDisconnects.at!(i).length = 0;
+ }
+ }
+ }
+ }
+ }
+
+ // Adjusts signal arguments and calls the slot. S - slot signature, A - signal arguments
+ private void invokeSlot(S, Receiver, A...)(Receiver r, A args)
+ {
+ r.get!(S)()(args[0..ParameterTupleOf!(S).length]);
+ }
+
+ void blockSignals()
+ {
+ synchronized(this)
+ blocked++;
+ }
+
+ void unblockSignals()
+ {
+ synchronized(this)
+ {
+ if(!blocked)
+ throw new SignalException("Signals are not blocked");
+ blocked--;
+ }
+ }
+
+ ~this()
+ {
+ foreach(ref c; connections)
+ c.free;
+ }
+
+ debug string toString()
+ {
+ string r;
+ foreach (i, c; connections)
+ r ~= Stdout.layout.convert("Signal {}:\n{}", i, c.toString);
+ return r;
+ }
+}
+
+//TODO: this could be avoided if named mixins didn't suck.
+public struct SignalOps(int sigId, A...)
+{
+ private SignalHandler sh;
+ enum { signalId = sigId }
+
+ void connect(R, B...)(R function(B) fn, ConnectionFlags flags = ConnectionFlags.None)
+ {
+ alias CheckSlot!(typeof(fn), A) check;
+ auto invoker = Dg(&sh.invokeSlot!(typeof(fn), Fn, A));
+ sh.connect(signalId, Fn(fn), invoker, flags);
+ }
+
+ void connect(R, B...)(R delegate(B) dg, ConnectionFlags flags = ConnectionFlags.None)
+ {
+ alias CheckSlot!(typeof(dg), A) check;
+ auto invoker = Dg(&sh.invokeSlot!(typeof(dg), Dg, A));
+ sh.connect(signalId, Dg(dg), invoker, flags);
+ }
+
+ void disconnect(R, B...)(R function(B) fn)
+ {
+ sh.disconnect(signalId, Fn(fn));
+ }
+
+ void disconnect(R, B...)(R delegate(B) dg)
+ {
+ sh.disconnect(signalId, Dg(dg));
+ }
+
+ void emit(A args)
+ {
+ sh.emit(signalId, args);
+ }
+
+ debug size_t slotCount()
+ {
+ return sh.slotCount(signalId);
+ }
+}
+
+template CheckSlot(Slot, A...)
+{
+ static assert(ParameterTupleOf!(Slot).length <= A.length, "Slot " ~ ParameterTypeTuple!(Slot).stringof ~
+ " has more prameters than signal " ~ A.stringof);
+ alias CheckSlotImpl!(Slot, 0, A) check;
+}
+
+template CheckSlotImpl(Slot, int i, A...)
+{
+ alias ParameterTupleOf!(Slot) SlotArgs;
+ static if (i < SlotArgs.length)
+ {
+ static assert (is(SlotArgs[i] : A[i]), "Argument " ~ __toString(i) ~
+ ":" ~ A[i].stringof ~ " of signal " ~ A.stringof ~ " is not implicitly convertible to parameter "
+ ~ SlotArgs[i].stringof ~ " of slot " ~ SlotArgs.stringof);
+ alias CheckSlotImpl!(Slot, i + 1, A) next;
+ }
+}
+
+public template SignalHandlerOps()
+{
+ static assert (is(typeof(this.signalHandler)),
+ "SignalHandlerOps is already instantiated in " ~ typeof(this).stringof ~ " or one of its base classes");
+
+protected:
+ SignalHandler signalHandler_; // manages signal-to-slot connections
+
+ final SignalHandler signalHandler()
+ {
+ if (!signalHandler_)
+ {
+ signalHandler_ = new SignalHandler(this);
+ onSignalHandlerCreated(signalHandler_);
+ }
+ return signalHandler_;
+ }
+
+ void onSignalHandlerCreated(ref SignalHandler sh)
+ {
+ }
+
+public:
+ final void blockSignals()
+ {
+ signalHandler.blockSignals();
+ }
+
+ final void unblockSignals()
+ {
+ signalHandler.unblockSignals();
+ }
+}
+
+/**
+ Examples:
+----
+struct Args
+{
+ bool cancel;
+}
+
+class C
+{
+ private int _x;
+ // reference parameters are not supported yet,
+ // so we pass arguments by pointer.
+ mixin Signal!("xChanging", int, Args*);
+ mixin Signal!("xChanged");
+
+ void x(int v)
+ {
+ if (v != _x)
+ {
+ Args args;
+ xChanging.emit(v, &args);
+ if (!args.cancel)
+ {
+ _x = v;
+ xChanged.emit;
+ }
+ }
+ }
+}
+----
+*/
+template Signal(string name, A...)
+{
+ mixin SignalImpl!(0, name, A);
+}
+
+template SignalImpl(int index, string name, A...)
+{
+ static if (is(typeof(mixin(typeof(this).stringof ~ ".__sig" ~ ToString!(index)))))
+ mixin SignalImpl!(index + 1, name, A);
+ else
+ {
+ // mixed-in once
+ static if (!is(typeof(this.signalHandler)))
+ {
+ mixin SignalHandlerOps;
+ }
+ mixin("private static const int __sig" ~ ToString!(index) ~ " = " ~ ToString!(index) ~ ";");
+ mixin("SignalOps!(" ~ ToString!(index) ~ ", A) " ~ name ~ "(){ return SignalOps!("
+ ~ ToString!(index) ~ ", A)(signalHandler); }");
+ }
+}
+
+extern(C) alias void function(void*) SlotConnector;
+
+debug (UnitTest)
+{
+ class A
+ {
+ mixin Signal!("scorched", int);
+
+ int signalId1 = -1;
+ int signalId2 = -1;
+
+ void onFirstConnect(int sId)
+ {
+ signalId1 = sId;
+ }
+
+ void onLastDisconnect(int sId)
+ {
+ signalId2 = sId;
+ }
+
+ this()
+ {
+ signalHandler.firstSlotConnected = &onFirstConnect;
+ signalHandler.lastSlotDisconnected = &onLastDisconnect;
+ }
+ }
+
+ class B : A
+ {
+ mixin Signal!("booed", int);
+
+ int bazSum;
+ void baz(int i)
+ {
+ bazSum += i;
+ }
+ }
+
+ class C : A
+ {
+ mixin Signal!("cooked");
+ }
+}
+
+unittest
+{
+ static int fooSum;
+ static int barSum;
+
+ static void foo(int i)
+ {
+ fooSum += i;
+ }
+
+ void bar(long i)
+ {
+ barSum += i;
+ }
+
+ auto a = new A;
+ auto b = new B;
+ auto c = new C;
+ assert(b.scorched.signalId == 0);
+ assert(b.booed.signalId == 1);
+ assert(c.cooked.signalId == 1);
+
+ auto sh = b.signalHandler;
+
+ b.scorched.connect(&foo);
+ assert(sh.connections.length == 1);
+ assert(b.signalId1 == 0);
+ auto scCons = &sh.connections[0];
+
+ assert(scCons.getSlotList!(SlotListId.Func).length == 1);
+ b.scorched.emit(1);
+ assert(fooSum == 1);
+
+ b.scorched.connect(&bar, ConnectionFlags.NoObject);
+ assert(sh.connections.length == 1);
+ assert(scCons.getSlotList!(SlotListId.Strong).length == 1);
+ b.scorched.emit(1);
+ assert (fooSum == 2 && barSum == 1);
+
+ b.scorched.connect(&b.baz);
+ assert(scCons.getSlotList!(SlotListId.Weak).length == 1);
+ b.scorched.emit(1);
+ assert (fooSum == 3 && barSum == 2 && b.bazSum == 1);
+
+ b.scorched.disconnect(&bar);
+ assert(scCons.slotCount == 2);
+ b.scorched.disconnect(&b.baz);
+ assert(scCons.slotCount == 1);
+ b.scorched.disconnect(&foo);
+ assert(scCons.slotCount == 0);
+ assert(b.signalId2 == 0);
+
+ fooSum = 0;
+ void connectFoo()
+ {
+ b.scorched.connect(&foo);
+ b.scorched.disconnect(&connectFoo);
+ }
+
+ b.scorched.connect(&connectFoo, ConnectionFlags.NoObject);
+ b.scorched.emit(1);
+ assert(scCons.getSlotList!(SlotListId.Func).length == 1);
+ assert(scCons.getSlotList!(SlotListId.Strong).length == 0);
+ assert(!fooSum);
+
+ auto r = new B();
+ b.scorched.connect(&r.baz);
+ assert(scCons.getSlotList!(SlotListId.Weak).length == 1);
+ b.scorched.emit(1);
+ assert(r.bazSum == 1);
+ assert(fooSum == 1);
+
+ delete(r);
+ assert(scCons.getSlotList!(SlotListId.Weak).length == 1);
+ b.scorched.emit(1);
+ assert(scCons.getSlotList!(SlotListId.Weak).length == 0);
+}
\ No newline at end of file
diff -r 056e83133e29 -r 6faf3d3cb95e qt/d2/Signal.d
--- a/qt/d2/Signal.d Thu May 14 15:58:18 2009 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1001 +0,0 @@
-/**
- *
- * Copyright: Copyright QtD Team, 2008-2009
- * Authors: Max Samukha, Eldar Insafutdinov
- * License: 1)
- memmove(a.ptr + dest, a.ptr + src, length * T.sizeof);
-}
-
-
-public class SignalException : Exception
-{
- this(string msg)
- {
- super(msg);
- }
-}
-
-struct Fn
-{
- void* funcptr;
-
- static typeof(*this) opCall(R, A...)(R function(A) fn)
- {
- typeof(*this) r;
- r.funcptr = fn;
- return r;
- }
-
- template call(R)
- {
- R call(A...)(A args)
- {
- alias R function(A) Fn;
- return (cast(Fn)funcptr)(args);
- }
- }
-
- S get(S)()
- {
- static assert (is(typeof(*S.init) == function));
- return cast(S)funcptr;
- }
-
- debug string toString()
- {
- return Stdout.layout.convert("funcptr: {}", funcptr);
- }
-}
-
-struct Dg
-{
- void* context;
- void* funcptr;
-
- static typeof(*this) opCall(R, A...)(R delegate(A) dg)
- {
- typeof(*this) r;
- r.context = dg.ptr;
- r.funcptr = dg.funcptr;
- return r;
- }
-
- template call(R)
- {
- R call(A...)(A args)
- {
- R delegate(A) dg; // BUG: parameter storage classes are ignored
- dg.ptr = context;
- dg.funcptr = cast(typeof(dg.funcptr))funcptr;
- return dg(args);
- }
- }
-
- S get(S)()
- {
- static assert (is(S == delegate));
- S r;
- r.ptr = context;
- r.funcptr = cast(typeof(r.funcptr))funcptr;
- return r;
- }
-
- debug string toString()
- {
- return Stdout.layout.convert("context: {}, funcptr: {}", context, funcptr);
- }
-}
-
-struct Slot(R)
-{
- alias R Receiver;
-
- Receiver receiver;
- Dg invoker;
-
- static if (is(Receiver == Dg))
- {
- static const isDelegate = true;
-
- void onReceiverDisposed(Object o)
- {
- assert (lock !is null);
- synchronized(lock)
- {
- receiver.context = null;
- receiver.funcptr = null;
- }
- }
-
- // null if receiver doesn't point to a disposable object
- Object lock;
-
- bool isDisposed()
- {
- return !receiver.funcptr;
- }
-
- Object getObject()
- {
- return lock ? _d_toObject(receiver.context) : null;
- }
- }
- else
- static const isDelegate = false;
-
- debug string toString()
- {
- return Stdout.layout.convert("receiver: {}, invoker {}: ", receiver, invoker);
- }
-}
-
-enum SlotListId
-{
- Func, // function pointers
- Weak, // object delegates stored in C heap
- Strong // delegates stored in GC heap
-}
-
-/**
- Used to specify the type of a signal-to-slot connection.
-
- Examples:
-----
-class Sender
-{
- mixin Signal!("changed");
- void change()
- {
- changed.emit;
- }
-}
-
-
-class Receiver
-{
- void alarm() {}
-}
-
-void main()
-{
- auto s = new Sender;
- auto r = new Receiver;
- s.changed.connect(&r.alarm); // now s weakly references r
-
- r = null;
- // collect garbage (assume there is no more reachable pointers
- // to the receiver and it gets finalized)
- ...
-
- s.change;
- // weak reference to the receiving object
- // has been removed from the sender's connection lists.
-
- r = new Receiver;
- s.changed.connect(&r.alarm, ConnectionFlags.Strong);
-
- r = null;
- // collect garbage
- ...
- // the receiving object has not been finalized because s strongly references it.
-
- s.change; // the receiver is called.
- delete r;
- s.change; // the receiver is disconnected from the sender.
-
- static void foo()
- {
- }
-
- s.changed.connect(&foo);
- s.changed.emit; // foo is called.
- s.changed.disconnect(&foo); // must be explicitly disconnected.
-
- void bar()
- {
- }
-
- // ConnectionFlags.NoObject must be specified for delegates
- // to non-static local functions or struct member functions.
- s.changed.connect(&bar, ConnectionFlags.NoObject);
- s.changed.emit; // bar is called.
- s.changed.disconnect(&bar); // must be explicitly disconnected.
-}
-----
-*/
-public enum ConnectionFlags
-{
- ///
- None,
- /**
- The receiver will be stored as weak reference (implied if ConnectionFlags.NoObject is not specified).
- If the signal receiver is not a function pointer or a delegate referencing a D class instance.
- the sender will not be notified when the receiving object is deleted and emitting the signal
- connected to that receiving object will result in undefined behavior.
- */
- Weak = 0x0001,
- /**
- The receiver is stored as strong reference (implied if ConnectionFlags.NoObject is specified).
- */
- Strong = 0x0002,
- /**
- Must be specified if the receiver is not a function pointer or a delegate referencing a D class instance.
- */
- NoObject = 0x0004
-
- // Queued = 0x0004,
- // BlockingQueued = 0x0008
-}
-
-
-struct SlotList(SlotT, bool strong = false)
-{
- alias SlotT SlotType;
- SlotType[] data;
-
- void length(size_t length)
- {
- static if (strong)
- data.length = length;
- else
- realloc(data, length);
- }
-
- SlotType* add(SlotType slot)
- {
- auto oldLen = data.length;
- length = oldLen + 1;
- auto p = &data[oldLen];
- *p = slot;
- return p;
- }
-
- SlotType* get(int slotId)
- {
- return &data[slotId];
- }
-
- void remove(int slotId)
- {
- move(data, slotId, slotId + 1, data.length - slotId - 1);
- data = data[0..$ - 1];
- }
-
- size_t length()
- {
- return data.length;
- }
-
- void free()
- {
- static if (SlotType.isDelegate)
- {
- foreach (ref slot; data)
- {
- if (auto obj = slot.getObject)
- rt_detachDisposeEvent(obj, &slot.onReceiverDisposed);
- }
- }
- static if (!strong)
- cfree(data.ptr);
- }
-
- debug string toString()
- {
- string r;
- foreach(e; data)
- r ~= e.toString ~ "\n";
- return r;
- }
-}
-
-public alias void delegate(int signalId) SignalEvent;
-
-struct SignalConnections
-{
- bool isInUse;
-
- STuple!(
- SlotList!(Slot!(Fn)),
- SlotList!(Slot!(Dg)),
- SlotList!(Slot!(Dg), true)
- ) slotLists;
-
- STuple!(
- Fn[],
- Dg[]
- ) delayedDisconnects;
-
- void addDelayedDisconnect(Fn r)
- {
- delayedDisconnects.at!(0) ~= r;
- }
-
- void addDelayedDisconnect(Dg r)
- {
- delayedDisconnects.at!(1) ~= r;
- }
-
- SlotListType!(slotListId)* getSlotList(int slotListId)()
- {
- return &slotLists.tupleof[slotListId];
- }
-
- bool hasSlots()
- {
- foreach(i, e; slotLists.tupleof)
- {
- if (slotLists.tupleof[i].length)
- return true;
- }
- return false;
- }
-
- int slotCount()
- {
- int count;
- foreach(i, e; slotLists.tupleof)
- count += slotLists.at!(i).length;
- return count;
- }
-
- void slotListLengths(int[] lengths)
- {
- foreach(i, e; slotLists.tupleof)
- lengths[i] = slotLists.at!(i).length;
- }
-
- SlotType!(slotListId)* addSlot(int slotListId)(SlotType!(slotListId) slot)
- {
- return getSlotList!(slotListId).add(slot);
- }
-
- void removeSlot(int slotListId)(int slotId)
- {
- slotLists.at!(slotListId).remove(slotId);
- }
-
- void free()
- {
- foreach(i, e; slotLists.tupleof)
- {
- static if (is(typeof(slotLists.at!(i).free)))
- slotLists.at!(i).free;
- }
- }
-
- template SlotListType(int slotListId)
- {
- alias typeof(slotLists.tupleof)[slotListId] SlotListType;
- }
-
- template SlotType(int slotListId)
- {
- alias SlotListType!(slotListId).SlotType SlotType;
- }
-
- template ReceiverType(int slotListId)
- {
- alias SlotType!(slotListId).Receiver ReceiverType;
- }
-
- debug string toString()
- {
- string r;
- foreach(i, e; slotLists.tupleof)
- {
- r ~= Stdout.layout.convert("Slot list {}:", i);
- r ~= slotLists.at!(i).toString;
- }
- return r;
- }
-
- static const slotListCount = slotLists.tupleof.length;
-}
-
-
-private ThreadLocal!(Object) signalSender_;
-static this()
-{
- signalSender_ = new ThreadLocal!(Object);
-}
-
-/**
- If called from a slot, returns the object
- that is emitting the signal. Otherwise, returns null.
-*/
-public Object signalSender() {
- return signalSender_.val;
-}
-
-public class SignalHandler
-{
- SignalConnections[] connections;
- Object owner;
- int blocked;
-
- // Called after a slot has been connected to an empty signal
- SignalEvent firstSlotConnected;
- // Called after the last slot has been disconnected from a signal
- SignalEvent lastSlotDisconnected;
-
- alias SignalConnections.SlotType SlotType;
- alias SignalConnections.ReceiverType ReceiverType;
-
- public this(Object owner_) {
- owner = owner_;
- }
-
- private SignalConnections* getConnections(int signalId)
- {
- if (signalId < connections.length)
- return &connections[signalId];
- return null;
- }
-
- private SlotType!(slotListId)* addSlot(int slotListId)(int signalId, ReceiverType!(slotListId) receiver,
- Dg invoker)
- {
- if (signalId >= connections.length)
- connections.length = signalId + 1;
- auto slot = connections[signalId].addSlot!(slotListId)(SlotType!(slotListId)(receiver, invoker));
-
- if (firstSlotConnected && connections[signalId].slotCount == 1)
- firstSlotConnected(signalId);
-
- return slot;
- }
-
- private void removeSlot(int slotListId)(int signalId, int slotId)
- {
- connections[signalId].removeSlot!(slotListId)(slotId);
-
- if (lastSlotDisconnected && !connections[signalId].slotCount)
- lastSlotDisconnected(signalId);
- }
-
- private SlotType!(slotListId)* addObjectSlot(int slotListId)(size_t signalId, Object obj, Dg receiver,
- Dg invoker)
- {
- auto slot = addSlot!(slotListId)(signalId, receiver, invoker);
- slot.lock = this;
- rt_attachDisposeEvent(obj, &slot.onReceiverDisposed);
- return slot;
- }
-
- size_t slotCount(int signalId)
- {
- synchronized(this)
- {
- auto con = getConnections(signalId);
- if (con)
- return con.slotCount;
- return 0;
- }
- }
-
- void connect(Receiver)(size_t signalId, Receiver receiver,
- Dg invoker, ConnectionFlags flags)
- {
- synchronized(this)
- {
- static if (is(typeof(receiver.context)))
- {
- Object obj;
- if ((flags & ConnectionFlags.NoObject) || (obj = _d_toObject(receiver.context)) is null)
- {
- // strong by default
- if (flags & ConnectionFlags.Weak)
- addSlot!(SlotListId.Weak)(signalId, receiver, invoker);
- else
- addSlot!(SlotListId.Strong)(signalId, receiver, invoker);
- }
- else
- {
- // weak by default
- if (flags & ConnectionFlags.Strong)
- addObjectSlot!(SlotListId.Strong)(signalId, obj, receiver, invoker);
- else
- addObjectSlot!(SlotListId.Weak)(signalId, obj, receiver, invoker);
- }
- }
- else
- addSlot!(SlotListId.Func)(signalId, receiver, invoker);
- }
- }
-
- void disconnect(Receiver)(int signalId, Receiver receiver)
- {
- synchronized(this)
- {
- auto cons = getConnections(signalId);
- if (!cons)
- return;
-
- // if called from a slot being executed by this signal, delay disconnection
- // until all slots has been called.
- if (cons.isInUse)
- {
- cons.addDelayedDisconnect(receiver);
- return;
- }
-
- TOP:
- foreach (slotListId, e; cons.slotLists.tupleof)
- {
- /// COMPILER BUG: ReceiverType is evaluated to expression instead of type.
- static if (is(typeof(cons.ReceiverType!(slotListId)) == Receiver))
- {
- auto slotList = cons.getSlotList!(slotListId);
- for (int slotId; slotId < slotList.length;)
- {
- auto slot = slotList.get(slotId);
- static if (slot.isDelegate)
- {
- if (slot.isDisposed)
- {
- removeSlot!(slotListId)(signalId, slotId);
- continue;
- }
- }
-
- if (slot.receiver == receiver)
- {
- static if (slot.isDelegate)
- {
- if (auto obj = slot.getObject)
- rt_detachDisposeEvent(obj, &slot.onReceiverDisposed);
- }
- removeSlot!(slotListId)(signalId, slotId);
- break TOP;
- }
-
- slotId++;
- }
- }
- }
- }
- }
-
- void emit(A...)(size_t signalId, A args)
- {
- synchronized(this)
- {
- if (signalId >= connections.length || blocked)
- return;
- auto cons = &connections[signalId];
-
- if (cons.hasSlots)
- {
- {
- cons.isInUse = true;
- signalSender_.val = owner;
- scope(exit)
- {
- cons.isInUse = false;
- signalSender_.val = null;
- }
-
- // Store the lengths to avoid calling new slots
- // connected in the slots being called.
- // dmd bug: int[cons.slotListCount] fails
- static const c = cons.slotListCount;
- int[c] lengths = void;
- cons.slotListLengths(lengths);
-
- foreach (slotListId, e; cons.slotLists.tupleof)
- {
- auto slotList = cons.getSlotList!(slotListId);
- for (size_t slotId; slotId < lengths[slotListId];)
- {
- auto slot = slotList.get(slotId);
- static if (slot.isDelegate)
- {
- if (slot.isDisposed)
- {
- removeSlot!(slotListId)(signalId, slotId);
- lengths[slotListId]--;
- continue;
- }
- }
-
- slot.invoker.call!(void)(slot.receiver, args);
- ++slotId;
- }
- }
- }
-
-
- // process delayed disconnects if any
- foreach(i, e; cons.delayedDisconnects.tupleof)
- {
- if (cons.delayedDisconnects.at!(i).length)
- {
- foreach (d; cons.delayedDisconnects.at!(i))
- disconnect(signalId, d);
- cons.delayedDisconnects.at!(i).length = 0;
- }
- }
- }
- }
- }
-
- // Adjusts signal arguments and calls the slot. S - slot signature, A - signal arguments
- private void invokeSlot(S, Receiver, A...)(Receiver r, A args)
- {
- r.get!(S)()(args[0..ParameterTupleOf!(S).length]);
- }
-
- void blockSignals()
- {
- synchronized(this)
- blocked++;
- }
-
- void unblockSignals()
- {
- synchronized(this)
- {
- if(!blocked)
- throw new SignalException("Signals are not blocked");
- blocked--;
- }
- }
-
- ~this()
- {
- foreach(ref c; connections)
- c.free;
- }
-
- debug string toString()
- {
- string r;
- foreach (i, c; connections)
- r ~= Stdout.layout.convert("Signal {}:\n{}", i, c.toString);
- return r;
- }
-}
-
-//TODO: this could be avoided if named mixins didn't suck.
-public struct SignalOps(int sigId, A...)
-{
- private SignalHandler sh;
- enum { signalId = sigId }
-
- void connect(R, B...)(R function(B) fn, ConnectionFlags flags = ConnectionFlags.None)
- {
- alias CheckSlot!(typeof(fn), A) check;
- auto invoker = Dg(&sh.invokeSlot!(typeof(fn), Fn, A));
- sh.connect(signalId, Fn(fn), invoker, flags);
- }
-
- void connect(R, B...)(R delegate(B) dg, ConnectionFlags flags = ConnectionFlags.None)
- {
- alias CheckSlot!(typeof(dg), A) check;
- auto invoker = Dg(&sh.invokeSlot!(typeof(dg), Dg, A));
- sh.connect(signalId, Dg(dg), invoker, flags);
- }
-
- void disconnect(R, B...)(R function(B) fn)
- {
- sh.disconnect(signalId, Fn(fn));
- }
-
- void disconnect(R, B...)(R delegate(B) dg)
- {
- sh.disconnect(signalId, Dg(dg));
- }
-
- void emit(A args)
- {
- sh.emit(signalId, args);
- }
-
- debug size_t slotCount()
- {
- return sh.slotCount(signalId);
- }
-}
-
-template CheckSlot(Slot, A...)
-{
- static assert(ParameterTupleOf!(Slot).length <= A.length, "Slot " ~ ParameterTypeTuple!(Slot).stringof ~
- " has more prameters than signal " ~ A.stringof);
- alias CheckSlotImpl!(Slot, 0, A) check;
-}
-
-template CheckSlotImpl(Slot, int i, A...)
-{
- alias ParameterTupleOf!(Slot) SlotArgs;
- static if (i < SlotArgs.length)
- {
- static assert (is(SlotArgs[i] : A[i]), "Argument " ~ ToString!(i) ~
- ":" ~ A[i].stringof ~ " of signal " ~ A.stringof ~ " is not implicitly convertible to parameter "
-
-
-
-
- ~ SlotArgs[i].stringof ~ " of slot " ~ SlotArgs.stringof);
- alias CheckSlotImpl!(Slot, i + 1, A) next;
- }
-}
-
-public template SignalHandlerOps()
-{
- static assert (is(typeof(this.signalHandler)),
- "SignalHandlerOps is already instantiated in " ~ typeof(this).stringof ~ " or one of its base classes");
-
-protected:
- SignalHandler signalHandler_; // manages signal-to-slot connections
-
- final SignalHandler signalHandler()
- {
- if (!signalHandler_)
- {
- signalHandler_ = new SignalHandler(this);
- onSignalHandlerCreated(signalHandler_);
- }
- return signalHandler_;
- }
-
- void onSignalHandlerCreated(ref SignalHandler sh)
- {
- }
-
-public:
- final void blockSignals()
- {
- signalHandler.blockSignals();
- }
-
- final void unblockSignals()
- {
- signalHandler.unblockSignals();
- }
-}
-
-/**
- Examples:
-----
-struct Args
-{
- bool cancel;
-}
-
-class C
-{
- private int _x;
- // reference parameters are not supported yet,
- // so we pass arguments by pointer.
- mixin Signal!("xChanging", int, Args*);
- mixin Signal!("xChanged");
-
- void x(int v)
- {
- if (v != _x)
- {
- Args args;
- xChanging.emit(v, &args);
- if (!args.cancel)
- {
- _x = v;
- xChanged.emit;
- }
- }
- }
-}
-----
-*/
-template Signal(string name, A...)
-{
- mixin SignalImpl!(0, name, A);
-}
-
-template SignalImpl(int index, string name, A...)
-{
- static if (is(typeof(mixin(typeof(this).stringof ~ ".__sig" ~ ToString!(index)))))
- mixin SignalImpl!(index + 1, name, A);
- else
- {
- // mixed-in once
- static if (!is(typeof(this.signalHandler)))
- {
- mixin SignalHandlerOps;
- }
- mixin("private static const int __sig" ~ ToString!(index) ~ " = " ~ ToString!(index) ~ ";");
- mixin("SignalOps!(" ~ ToString!(index) ~ ", A) " ~ name ~ "(){ return SignalOps!("
- ~ ToString!(index) ~ ", A)(signalHandler); }");
- }
-}
-
-extern(C) alias void function(void*) SlotConnector;
-
-debug (UnitTest)
-{
- class A
- {
- mixin Signal!("scorched", int);
-
- int signalId1 = -1;
- int signalId2 = -1;
-
- void onFirstConnect(int sId)
- {
- signalId1 = sId;
- }
-
- void onLastDisconnect(int sId)
- {
- signalId2 = sId;
- }
-
- this()
- {
- signalHandler.firstSlotConnected = &onFirstConnect;
- signalHandler.lastSlotDisconnected = &onLastDisconnect;
- }
- }
-
- class B : A
- {
- mixin Signal!("booed", int);
-
- int bazSum;
- void baz(int i)
- {
- bazSum += i;
- }
- }
-
- class C : A
- {
- mixin Signal!("cooked");
- }
-}
-
-unittest
-{
- static int fooSum;
- static int barSum;
-
- static void foo(int i)
- {
- fooSum += i;
- }
-
- void bar(long i)
- {
- barSum += i;
- }
-
- auto a = new A;
- auto b = new B;
- auto c = new C;
- assert(b.scorched.signalId == 0);
- assert(b.booed.signalId == 1);
- assert(c.cooked.signalId == 1);
-
- auto sh = b.signalHandler;
-
- b.scorched.connect(&foo);
- assert(sh.connections.length == 1);
- assert(b.signalId1 == 0);
- auto scCons = &sh.connections[0];
-
- assert(scCons.getSlotList!(SlotListId.Func).length == 1);
- b.scorched.emit(1);
- assert(fooSum == 1);
-
- b.scorched.connect(&bar, ConnectionFlags.NoObject);
- assert(sh.connections.length == 1);
- assert(scCons.getSlotList!(SlotListId.Strong).length == 1);
- b.scorched.emit(1);
- assert (fooSum == 2 && barSum == 1);
-
- b.scorched.connect(&b.baz);
- assert(scCons.getSlotList!(SlotListId.Weak).length == 1);
- b.scorched.emit(1);
- assert (fooSum == 3 && barSum == 2 && b.bazSum == 1);
-
- b.scorched.disconnect(&bar);
- assert(scCons.slotCount == 2);
- b.scorched.disconnect(&b.baz);
- assert(scCons.slotCount == 1);
- b.scorched.disconnect(&foo);
- assert(scCons.slotCount == 0);
- assert(b.signalId2 == 0);
-
- fooSum = 0;
- void connectFoo()
- {
- b.scorched.connect(&foo);
- b.scorched.disconnect(&connectFoo);
- }
-
- b.scorched.connect(&connectFoo, ConnectionFlags.NoObject);
- b.scorched.emit(1);
- assert(scCons.getSlotList!(SlotListId.Func).length == 1);
- assert(scCons.getSlotList!(SlotListId.Strong).length == 0);
- assert(!fooSum);
-
- auto r = new B();
- b.scorched.connect(&r.baz);
- assert(scCons.getSlotList!(SlotListId.Weak).length == 1);
- b.scorched.emit(1);
- assert(r.bazSum == 1);
- assert(fooSum == 1);
-
- delete(r);
- assert(scCons.getSlotList!(SlotListId.Weak).length == 1);
- b.scorched.emit(1);
- assert(scCons.getSlotList!(SlotListId.Weak).length == 0);
-}
diff -r 056e83133e29 -r 6faf3d3cb95e qt/d2/qt/Signal.d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qt/d2/qt/Signal.d Thu May 14 17:09:25 2009 +0000
@@ -0,0 +1,1001 @@
+/**
+ *
+ * Copyright: Copyright QtD Team, 2008-2009
+ * Authors: Max Samukha, Eldar Insafutdinov
+ * License: 1)
+ memmove(a.ptr + dest, a.ptr + src, length * T.sizeof);
+}
+
+
+public class SignalException : Exception
+{
+ this(string msg)
+ {
+ super(msg);
+ }
+}
+
+struct Fn
+{
+ void* funcptr;
+
+ static typeof(*this) opCall(R, A...)(R function(A) fn)
+ {
+ typeof(*this) r;
+ r.funcptr = fn;
+ return r;
+ }
+
+ template call(R)
+ {
+ R call(A...)(A args)
+ {
+ alias R function(A) Fn;
+ return (cast(Fn)funcptr)(args);
+ }
+ }
+
+ S get(S)()
+ {
+ static assert (is(typeof(*S.init) == function));
+ return cast(S)funcptr;
+ }
+
+ debug string toString()
+ {
+ return Stdout.layout.convert("funcptr: {}", funcptr);
+ }
+}
+
+struct Dg
+{
+ void* context;
+ void* funcptr;
+
+ static typeof(*this) opCall(R, A...)(R delegate(A) dg)
+ {
+ typeof(*this) r;
+ r.context = dg.ptr;
+ r.funcptr = dg.funcptr;
+ return r;
+ }
+
+ template call(R)
+ {
+ R call(A...)(A args)
+ {
+ R delegate(A) dg; // BUG: parameter storage classes are ignored
+ dg.ptr = context;
+ dg.funcptr = cast(typeof(dg.funcptr))funcptr;
+ return dg(args);
+ }
+ }
+
+ S get(S)()
+ {
+ static assert (is(S == delegate));
+ S r;
+ r.ptr = context;
+ r.funcptr = cast(typeof(r.funcptr))funcptr;
+ return r;
+ }
+
+ debug string toString()
+ {
+ return Stdout.layout.convert("context: {}, funcptr: {}", context, funcptr);
+ }
+}
+
+struct Slot(R)
+{
+ alias R Receiver;
+
+ Receiver receiver;
+ Dg invoker;
+
+ static if (is(Receiver == Dg))
+ {
+ static const isDelegate = true;
+
+ void onReceiverDisposed(Object o)
+ {
+ assert (lock !is null);
+ synchronized(lock)
+ {
+ receiver.context = null;
+ receiver.funcptr = null;
+ }
+ }
+
+ // null if receiver doesn't point to a disposable object
+ Object lock;
+
+ bool isDisposed()
+ {
+ return !receiver.funcptr;
+ }
+
+ Object getObject()
+ {
+ return lock ? _d_toObject(receiver.context) : null;
+ }
+ }
+ else
+ static const isDelegate = false;
+
+ debug string toString()
+ {
+ return Stdout.layout.convert("receiver: {}, invoker {}: ", receiver, invoker);
+ }
+}
+
+enum SlotListId
+{
+ Func, // function pointers
+ Weak, // object delegates stored in C heap
+ Strong // delegates stored in GC heap
+}
+
+/**
+ Used to specify the type of a signal-to-slot connection.
+
+ Examples:
+----
+class Sender
+{
+ mixin Signal!("changed");
+ void change()
+ {
+ changed.emit;
+ }
+}
+
+
+class Receiver
+{
+ void alarm() {}
+}
+
+void main()
+{
+ auto s = new Sender;
+ auto r = new Receiver;
+ s.changed.connect(&r.alarm); // now s weakly references r
+
+ r = null;
+ // collect garbage (assume there is no more reachable pointers
+ // to the receiver and it gets finalized)
+ ...
+
+ s.change;
+ // weak reference to the receiving object
+ // has been removed from the sender's connection lists.
+
+ r = new Receiver;
+ s.changed.connect(&r.alarm, ConnectionFlags.Strong);
+
+ r = null;
+ // collect garbage
+ ...
+ // the receiving object has not been finalized because s strongly references it.
+
+ s.change; // the receiver is called.
+ delete r;
+ s.change; // the receiver is disconnected from the sender.
+
+ static void foo()
+ {
+ }
+
+ s.changed.connect(&foo);
+ s.changed.emit; // foo is called.
+ s.changed.disconnect(&foo); // must be explicitly disconnected.
+
+ void bar()
+ {
+ }
+
+ // ConnectionFlags.NoObject must be specified for delegates
+ // to non-static local functions or struct member functions.
+ s.changed.connect(&bar, ConnectionFlags.NoObject);
+ s.changed.emit; // bar is called.
+ s.changed.disconnect(&bar); // must be explicitly disconnected.
+}
+----
+*/
+public enum ConnectionFlags
+{
+ ///
+ None,
+ /**
+ The receiver will be stored as weak reference (implied if ConnectionFlags.NoObject is not specified).
+ If the signal receiver is not a function pointer or a delegate referencing a D class instance.
+ the sender will not be notified when the receiving object is deleted and emitting the signal
+ connected to that receiving object will result in undefined behavior.
+ */
+ Weak = 0x0001,
+ /**
+ The receiver is stored as strong reference (implied if ConnectionFlags.NoObject is specified).
+ */
+ Strong = 0x0002,
+ /**
+ Must be specified if the receiver is not a function pointer or a delegate referencing a D class instance.
+ */
+ NoObject = 0x0004
+
+ // Queued = 0x0004,
+ // BlockingQueued = 0x0008
+}
+
+
+struct SlotList(SlotT, bool strong = false)
+{
+ alias SlotT SlotType;
+ SlotType[] data;
+
+ void length(size_t length)
+ {
+ static if (strong)
+ data.length = length;
+ else
+ realloc(data, length);
+ }
+
+ SlotType* add(SlotType slot)
+ {
+ auto oldLen = data.length;
+ length = oldLen + 1;
+ auto p = &data[oldLen];
+ *p = slot;
+ return p;
+ }
+
+ SlotType* get(int slotId)
+ {
+ return &data[slotId];
+ }
+
+ void remove(int slotId)
+ {
+ move(data, slotId, slotId + 1, data.length - slotId - 1);
+ data = data[0..$ - 1];
+ }
+
+ size_t length()
+ {
+ return data.length;
+ }
+
+ void free()
+ {
+ static if (SlotType.isDelegate)
+ {
+ foreach (ref slot; data)
+ {
+ if (auto obj = slot.getObject)
+ rt_detachDisposeEvent(obj, &slot.onReceiverDisposed);
+ }
+ }
+ static if (!strong)
+ cfree(data.ptr);
+ }
+
+ debug string toString()
+ {
+ string r;
+ foreach(e; data)
+ r ~= e.toString ~ "\n";
+ return r;
+ }
+}
+
+public alias void delegate(int signalId) SignalEvent;
+
+struct SignalConnections
+{
+ bool isInUse;
+
+ STuple!(
+ SlotList!(Slot!(Fn)),
+ SlotList!(Slot!(Dg)),
+ SlotList!(Slot!(Dg), true)
+ ) slotLists;
+
+ STuple!(
+ Fn[],
+ Dg[]
+ ) delayedDisconnects;
+
+ void addDelayedDisconnect(Fn r)
+ {
+ delayedDisconnects.at!(0) ~= r;
+ }
+
+ void addDelayedDisconnect(Dg r)
+ {
+ delayedDisconnects.at!(1) ~= r;
+ }
+
+ SlotListType!(slotListId)* getSlotList(int slotListId)()
+ {
+ return &slotLists.tupleof[slotListId];
+ }
+
+ bool hasSlots()
+ {
+ foreach(i, e; slotLists.tupleof)
+ {
+ if (slotLists.tupleof[i].length)
+ return true;
+ }
+ return false;
+ }
+
+ int slotCount()
+ {
+ int count;
+ foreach(i, e; slotLists.tupleof)
+ count += slotLists.at!(i).length;
+ return count;
+ }
+
+ void slotListLengths(int[] lengths)
+ {
+ foreach(i, e; slotLists.tupleof)
+ lengths[i] = slotLists.at!(i).length;
+ }
+
+ SlotType!(slotListId)* addSlot(int slotListId)(SlotType!(slotListId) slot)
+ {
+ return getSlotList!(slotListId).add(slot);
+ }
+
+ void removeSlot(int slotListId)(int slotId)
+ {
+ slotLists.at!(slotListId).remove(slotId);
+ }
+
+ void free()
+ {
+ foreach(i, e; slotLists.tupleof)
+ {
+ static if (is(typeof(slotLists.at!(i).free)))
+ slotLists.at!(i).free;
+ }
+ }
+
+ template SlotListType(int slotListId)
+ {
+ alias typeof(slotLists.tupleof)[slotListId] SlotListType;
+ }
+
+ template SlotType(int slotListId)
+ {
+ alias SlotListType!(slotListId).SlotType SlotType;
+ }
+
+ template ReceiverType(int slotListId)
+ {
+ alias SlotType!(slotListId).Receiver ReceiverType;
+ }
+
+ debug string toString()
+ {
+ string r;
+ foreach(i, e; slotLists.tupleof)
+ {
+ r ~= Stdout.layout.convert("Slot list {}:", i);
+ r ~= slotLists.at!(i).toString;
+ }
+ return r;
+ }
+
+ static const slotListCount = slotLists.tupleof.length;
+}
+
+
+private ThreadLocal!(Object) signalSender_;
+static this()
+{
+ signalSender_ = new ThreadLocal!(Object);
+}
+
+/**
+ If called from a slot, returns the object
+ that is emitting the signal. Otherwise, returns null.
+*/
+public Object signalSender() {
+ return signalSender_.val;
+}
+
+public class SignalHandler
+{
+ SignalConnections[] connections;
+ Object owner;
+ int blocked;
+
+ // Called after a slot has been connected to an empty signal
+ SignalEvent firstSlotConnected;
+ // Called after the last slot has been disconnected from a signal
+ SignalEvent lastSlotDisconnected;
+
+ alias SignalConnections.SlotType SlotType;
+ alias SignalConnections.ReceiverType ReceiverType;
+
+ public this(Object owner_) {
+ owner = owner_;
+ }
+
+ private SignalConnections* getConnections(int signalId)
+ {
+ if (signalId < connections.length)
+ return &connections[signalId];
+ return null;
+ }
+
+ private SlotType!(slotListId)* addSlot(int slotListId)(int signalId, ReceiverType!(slotListId) receiver,
+ Dg invoker)
+ {
+ if (signalId >= connections.length)
+ connections.length = signalId + 1;
+ auto slot = connections[signalId].addSlot!(slotListId)(SlotType!(slotListId)(receiver, invoker));
+
+ if (firstSlotConnected && connections[signalId].slotCount == 1)
+ firstSlotConnected(signalId);
+
+ return slot;
+ }
+
+ private void removeSlot(int slotListId)(int signalId, int slotId)
+ {
+ connections[signalId].removeSlot!(slotListId)(slotId);
+
+ if (lastSlotDisconnected && !connections[signalId].slotCount)
+ lastSlotDisconnected(signalId);
+ }
+
+ private SlotType!(slotListId)* addObjectSlot(int slotListId)(size_t signalId, Object obj, Dg receiver,
+ Dg invoker)
+ {
+ auto slot = addSlot!(slotListId)(signalId, receiver, invoker);
+ slot.lock = this;
+ rt_attachDisposeEvent(obj, &slot.onReceiverDisposed);
+ return slot;
+ }
+
+ size_t slotCount(int signalId)
+ {
+ synchronized(this)
+ {
+ auto con = getConnections(signalId);
+ if (con)
+ return con.slotCount;
+ return 0;
+ }
+ }
+
+ void connect(Receiver)(size_t signalId, Receiver receiver,
+ Dg invoker, ConnectionFlags flags)
+ {
+ synchronized(this)
+ {
+ static if (is(typeof(receiver.context)))
+ {
+ Object obj;
+ if ((flags & ConnectionFlags.NoObject) || (obj = _d_toObject(receiver.context)) is null)
+ {
+ // strong by default
+ if (flags & ConnectionFlags.Weak)
+ addSlot!(SlotListId.Weak)(signalId, receiver, invoker);
+ else
+ addSlot!(SlotListId.Strong)(signalId, receiver, invoker);
+ }
+ else
+ {
+ // weak by default
+ if (flags & ConnectionFlags.Strong)
+ addObjectSlot!(SlotListId.Strong)(signalId, obj, receiver, invoker);
+ else
+ addObjectSlot!(SlotListId.Weak)(signalId, obj, receiver, invoker);
+ }
+ }
+ else
+ addSlot!(SlotListId.Func)(signalId, receiver, invoker);
+ }
+ }
+
+ void disconnect(Receiver)(int signalId, Receiver receiver)
+ {
+ synchronized(this)
+ {
+ auto cons = getConnections(signalId);
+ if (!cons)
+ return;
+
+ // if called from a slot being executed by this signal, delay disconnection
+ // until all slots has been called.
+ if (cons.isInUse)
+ {
+ cons.addDelayedDisconnect(receiver);
+ return;
+ }
+
+ TOP:
+ foreach (slotListId, e; cons.slotLists.tupleof)
+ {
+ /// COMPILER BUG: ReceiverType is evaluated to expression instead of type.
+ static if (is(typeof(cons.ReceiverType!(slotListId)) == Receiver))
+ {
+ auto slotList = cons.getSlotList!(slotListId);
+ for (int slotId; slotId < slotList.length;)
+ {
+ auto slot = slotList.get(slotId);
+ static if (slot.isDelegate)
+ {
+ if (slot.isDisposed)
+ {
+ removeSlot!(slotListId)(signalId, slotId);
+ continue;
+ }
+ }
+
+ if (slot.receiver == receiver)
+ {
+ static if (slot.isDelegate)
+ {
+ if (auto obj = slot.getObject)
+ rt_detachDisposeEvent(obj, &slot.onReceiverDisposed);
+ }
+ removeSlot!(slotListId)(signalId, slotId);
+ break TOP;
+ }
+
+ slotId++;
+ }
+ }
+ }
+ }
+ }
+
+ void emit(A...)(size_t signalId, A args)
+ {
+ synchronized(this)
+ {
+ if (signalId >= connections.length || blocked)
+ return;
+ auto cons = &connections[signalId];
+
+ if (cons.hasSlots)
+ {
+ {
+ cons.isInUse = true;
+ signalSender_.val = owner;
+ scope(exit)
+ {
+ cons.isInUse = false;
+ signalSender_.val = null;
+ }
+
+ // Store the lengths to avoid calling new slots
+ // connected in the slots being called.
+ // dmd bug: int[cons.slotListCount] fails
+ static const c = cons.slotListCount;
+ int[c] lengths = void;
+ cons.slotListLengths(lengths);
+
+ foreach (slotListId, e; cons.slotLists.tupleof)
+ {
+ auto slotList = cons.getSlotList!(slotListId);
+ for (size_t slotId; slotId < lengths[slotListId];)
+ {
+ auto slot = slotList.get(slotId);
+ static if (slot.isDelegate)
+ {
+ if (slot.isDisposed)
+ {
+ removeSlot!(slotListId)(signalId, slotId);
+ lengths[slotListId]--;
+ continue;
+ }
+ }
+
+ slot.invoker.call!(void)(slot.receiver, args);
+ ++slotId;
+ }
+ }
+ }
+
+
+ // process delayed disconnects if any
+ foreach(i, e; cons.delayedDisconnects.tupleof)
+ {
+ if (cons.delayedDisconnects.at!(i).length)
+ {
+ foreach (d; cons.delayedDisconnects.at!(i))
+ disconnect(signalId, d);
+ cons.delayedDisconnects.at!(i).length = 0;
+ }
+ }
+ }
+ }
+ }
+
+ // Adjusts signal arguments and calls the slot. S - slot signature, A - signal arguments
+ private void invokeSlot(S, Receiver, A...)(Receiver r, A args)
+ {
+ r.get!(S)()(args[0..ParameterTupleOf!(S).length]);
+ }
+
+ void blockSignals()
+ {
+ synchronized(this)
+ blocked++;
+ }
+
+ void unblockSignals()
+ {
+ synchronized(this)
+ {
+ if(!blocked)
+ throw new SignalException("Signals are not blocked");
+ blocked--;
+ }
+ }
+
+ ~this()
+ {
+ foreach(ref c; connections)
+ c.free;
+ }
+
+ debug string toString()
+ {
+ string r;
+ foreach (i, c; connections)
+ r ~= Stdout.layout.convert("Signal {}:\n{}", i, c.toString);
+ return r;
+ }
+}
+
+//TODO: this could be avoided if named mixins didn't suck.
+public struct SignalOps(int sigId, A...)
+{
+ private SignalHandler sh;
+ enum { signalId = sigId }
+
+ void connect(R, B...)(R function(B) fn, ConnectionFlags flags = ConnectionFlags.None)
+ {
+ alias CheckSlot!(typeof(fn), A) check;
+ auto invoker = Dg(&sh.invokeSlot!(typeof(fn), Fn, A));
+ sh.connect(signalId, Fn(fn), invoker, flags);
+ }
+
+ void connect(R, B...)(R delegate(B) dg, ConnectionFlags flags = ConnectionFlags.None)
+ {
+ alias CheckSlot!(typeof(dg), A) check;
+ auto invoker = Dg(&sh.invokeSlot!(typeof(dg), Dg, A));
+ sh.connect(signalId, Dg(dg), invoker, flags);
+ }
+
+ void disconnect(R, B...)(R function(B) fn)
+ {
+ sh.disconnect(signalId, Fn(fn));
+ }
+
+ void disconnect(R, B...)(R delegate(B) dg)
+ {
+ sh.disconnect(signalId, Dg(dg));
+ }
+
+ void emit(A args)
+ {
+ sh.emit(signalId, args);
+ }
+
+ debug size_t slotCount()
+ {
+ return sh.slotCount(signalId);
+ }
+}
+
+template CheckSlot(Slot, A...)
+{
+ static assert(ParameterTupleOf!(Slot).length <= A.length, "Slot " ~ ParameterTypeTuple!(Slot).stringof ~
+ " has more prameters than signal " ~ A.stringof);
+ alias CheckSlotImpl!(Slot, 0, A) check;
+}
+
+template CheckSlotImpl(Slot, int i, A...)
+{
+ alias ParameterTupleOf!(Slot) SlotArgs;
+ static if (i < SlotArgs.length)
+ {
+ static assert (is(SlotArgs[i] : A[i]), "Argument " ~ ToString!(i) ~
+ ":" ~ A[i].stringof ~ " of signal " ~ A.stringof ~ " is not implicitly convertible to parameter "
+
+
+
+
+ ~ SlotArgs[i].stringof ~ " of slot " ~ SlotArgs.stringof);
+ alias CheckSlotImpl!(Slot, i + 1, A) next;
+ }
+}
+
+public template SignalHandlerOps()
+{
+ static assert (is(typeof(this.signalHandler)),
+ "SignalHandlerOps is already instantiated in " ~ typeof(this).stringof ~ " or one of its base classes");
+
+protected:
+ SignalHandler signalHandler_; // manages signal-to-slot connections
+
+ final SignalHandler signalHandler()
+ {
+ if (!signalHandler_)
+ {
+ signalHandler_ = new SignalHandler(this);
+ onSignalHandlerCreated(signalHandler_);
+ }
+ return signalHandler_;
+ }
+
+ void onSignalHandlerCreated(ref SignalHandler sh)
+ {
+ }
+
+public:
+ final void blockSignals()
+ {
+ signalHandler.blockSignals();
+ }
+
+ final void unblockSignals()
+ {
+ signalHandler.unblockSignals();
+ }
+}
+
+/**
+ Examples:
+----
+struct Args
+{
+ bool cancel;
+}
+
+class C
+{
+ private int _x;
+ // reference parameters are not supported yet,
+ // so we pass arguments by pointer.
+ mixin Signal!("xChanging", int, Args*);
+ mixin Signal!("xChanged");
+
+ void x(int v)
+ {
+ if (v != _x)
+ {
+ Args args;
+ xChanging.emit(v, &args);
+ if (!args.cancel)
+ {
+ _x = v;
+ xChanged.emit;
+ }
+ }
+ }
+}
+----
+*/
+template Signal(string name, A...)
+{
+ mixin SignalImpl!(0, name, A);
+}
+
+template SignalImpl(int index, string name, A...)
+{
+ static if (is(typeof(mixin(typeof(this).stringof ~ ".__sig" ~ ToString!(index)))))
+ mixin SignalImpl!(index + 1, name, A);
+ else
+ {
+ // mixed-in once
+ static if (!is(typeof(this.signalHandler)))
+ {
+ mixin SignalHandlerOps;
+ }
+ mixin("private static const int __sig" ~ ToString!(index) ~ " = " ~ ToString!(index) ~ ";");
+ mixin("SignalOps!(" ~ ToString!(index) ~ ", A) " ~ name ~ "(){ return SignalOps!("
+ ~ ToString!(index) ~ ", A)(signalHandler); }");
+ }
+}
+
+extern(C) alias void function(void*) SlotConnector;
+
+debug (UnitTest)
+{
+ class A
+ {
+ mixin Signal!("scorched", int);
+
+ int signalId1 = -1;
+ int signalId2 = -1;
+
+ void onFirstConnect(int sId)
+ {
+ signalId1 = sId;
+ }
+
+ void onLastDisconnect(int sId)
+ {
+ signalId2 = sId;
+ }
+
+ this()
+ {
+ signalHandler.firstSlotConnected = &onFirstConnect;
+ signalHandler.lastSlotDisconnected = &onLastDisconnect;
+ }
+ }
+
+ class B : A
+ {
+ mixin Signal!("booed", int);
+
+ int bazSum;
+ void baz(int i)
+ {
+ bazSum += i;
+ }
+ }
+
+ class C : A
+ {
+ mixin Signal!("cooked");
+ }
+}
+
+unittest
+{
+ static int fooSum;
+ static int barSum;
+
+ static void foo(int i)
+ {
+ fooSum += i;
+ }
+
+ void bar(long i)
+ {
+ barSum += i;
+ }
+
+ auto a = new A;
+ auto b = new B;
+ auto c = new C;
+ assert(b.scorched.signalId == 0);
+ assert(b.booed.signalId == 1);
+ assert(c.cooked.signalId == 1);
+
+ auto sh = b.signalHandler;
+
+ b.scorched.connect(&foo);
+ assert(sh.connections.length == 1);
+ assert(b.signalId1 == 0);
+ auto scCons = &sh.connections[0];
+
+ assert(scCons.getSlotList!(SlotListId.Func).length == 1);
+ b.scorched.emit(1);
+ assert(fooSum == 1);
+
+ b.scorched.connect(&bar, ConnectionFlags.NoObject);
+ assert(sh.connections.length == 1);
+ assert(scCons.getSlotList!(SlotListId.Strong).length == 1);
+ b.scorched.emit(1);
+ assert (fooSum == 2 && barSum == 1);
+
+ b.scorched.connect(&b.baz);
+ assert(scCons.getSlotList!(SlotListId.Weak).length == 1);
+ b.scorched.emit(1);
+ assert (fooSum == 3 && barSum == 2 && b.bazSum == 1);
+
+ b.scorched.disconnect(&bar);
+ assert(scCons.slotCount == 2);
+ b.scorched.disconnect(&b.baz);
+ assert(scCons.slotCount == 1);
+ b.scorched.disconnect(&foo);
+ assert(scCons.slotCount == 0);
+ assert(b.signalId2 == 0);
+
+ fooSum = 0;
+ void connectFoo()
+ {
+ b.scorched.connect(&foo);
+ b.scorched.disconnect(&connectFoo);
+ }
+
+ b.scorched.connect(&connectFoo, ConnectionFlags.NoObject);
+ b.scorched.emit(1);
+ assert(scCons.getSlotList!(SlotListId.Func).length == 1);
+ assert(scCons.getSlotList!(SlotListId.Strong).length == 0);
+ assert(!fooSum);
+
+ auto r = new B();
+ b.scorched.connect(&r.baz);
+ assert(scCons.getSlotList!(SlotListId.Weak).length == 1);
+ b.scorched.emit(1);
+ assert(r.bazSum == 1);
+ assert(fooSum == 1);
+
+ delete(r);
+ assert(scCons.getSlotList!(SlotListId.Weak).length == 1);
+ b.scorched.emit(1);
+ assert(scCons.getSlotList!(SlotListId.Weak).length == 0);
+}