# 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); +}