{"id":20912335,"url":"https://github.com/montellese/cpp-signal","last_synced_at":"2025-05-13T08:30:35.515Z","repository":{"id":139890656,"uuid":"78780021","full_name":"Montellese/cpp-signal","owner":"Montellese","description":"Header-only pure C++11 library providing signal and slot functionality","archived":false,"fork":false,"pushed_at":"2019-04-19T11:12:17.000Z","size":149,"stargazers_count":17,"open_issues_count":1,"forks_count":6,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-01T20:23:12.588Z","etag":null,"topics":["cpp","cxx11","signal","slot"],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Montellese.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-01-12T19:37:12.000Z","updated_at":"2024-08-25T19:06:45.000Z","dependencies_parsed_at":null,"dependency_job_id":"66311d29-1118-4607-910c-c9068d725acf","html_url":"https://github.com/Montellese/cpp-signal","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Montellese%2Fcpp-signal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Montellese%2Fcpp-signal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Montellese%2Fcpp-signal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Montellese%2Fcpp-signal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Montellese","download_url":"https://codeload.github.com/Montellese/cpp-signal/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253903568,"owners_count":21981723,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cpp","cxx11","signal","slot"],"created_at":"2024-11-18T14:26:39.417Z","updated_at":"2025-05-13T08:30:35.507Z","avatar_url":"https://github.com/Montellese.png","language":"C++","readme":"# cpp-signal #\n[![Travis CI Build Status](https://travis-ci.org/Montellese/cpp-signal.svg?branch=master)](https://travis-ci.org/Montellese/cpp-signal) [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/Montellese/cpp-signal?branch=master\u0026svg=true)](https://ci.appveyor.com/project/Montellese/cpp-signal) ![License](https://img.shields.io/github/license/Montellese/cpp-signal.svg) ![C++11](https://img.shields.io/badge/C%2B%2B-11-blue.svg)\n\ncpp-signal is a header-only pure C++11 library providing signal and slot functionality. Using it is as easy as\n```cpp\n#include \u003ccpp-signal.h\u003e\n...\nauto slot = [](int value) -\u003e int\n{\n  std::cout \u003c\u003c \"slot(\" \u003c\u003c value \u003c\u003c \u003c\u003c \")\" \u003c\u003c std::endl;\n\n  return 2 * value;\n}\n\ncpp_signal\u003c\u003e::signal\u003cint(int)\u003e signal;\nsignal.connect(slot);\n\nsignal.emit(1);  // prints \"slot(1)\"\nsignal.emit(2);  // prints \"slot(2)\"\n\nsignal.disconnect(slot);\n\nsignal.emit(3);  // this will not call the slot\n```\nwhich will result in the following output on standard out:\n```\nslot(1)\nslot(2)\n```\n\n----------\n### Table of Contents ###\n*   [Requirements](#requirements)\n    *   [Buildsystem](#buildsystem)\n    *   [Compilers](#compilers)\n*   [Features](#features)\n    *   [Type-safety](#type-safety)\n    *   [Managed connections](#managed-connections)\n    *   [Flexible slots](#flexible-slots)\n    *   [Signal chaining](#signal-chaining)\n    *   [Copying](#copying)\n    *   [Slot result collection](#slot-result-collection)\n    *   [Threading policy](#threading-policy)\n    *   [Asynchronous signal emission](#asynchronous-signal-emission)\n*   [Known Issues](#known-issues)\n    *   [Deadlocks](#deadlocks)\n*   [Performance](#performance)\n*   [The Future](#the-future)\n\n### Requirements ###\nThe only thing required to use cpp-signal is a C++11 compliant compiler.\n\n#### Buildsystem ####\nTo be able to run the tests provided with cpp-signal [CMake](https://cmake.org/) 3.1 or newer is required. Run the following commands to build the tests and execute them:\n```bash\n# mkdir build\n# cd build\n# cmake ..\n# cmake --build\n# ctest -C Debug\n```\n\n#### Compilers ####\ncpp-signal has been explictely tested with the following compilers:\n*   gcc\n    *   5.4.0\n    *   5.5.0\n*   clang\n    *   7.0.0\n*   Apple LLVM\n    *   9.1.0\n*   MSVC\n    *   Visual Studio 2015 (v140 / 1900) requires some legacy code (see ```MSVC_LEGACY``` in ```cpp-signal-async.h```)\n    *   Visual Studio 2017 (v141 / 1916)\n\n### Features ###\n#### Type-safety ####\nThanks to C++11's variadic templates cpp-signal calls are completely type-safe. It is not possible to connect a slot with mismatching return type and or parameter list to a signal. Furthermore it is not possible to emit a signal with mismatching parameters.\n\n#### Managed connections ####\nWith cpp-signal's `slot_tracker` class it is possible to automatically keep track of all methods in a class that have been connected to different signals as slots. It's as simple as deriving from `slot_tracker` which will take care of automatically disconnecting all connected slots when an object is destroyed.\n```cpp\nclass tracked_class : public cpp_signal\u003c\u003e::slot_tracker\n{\n  ...\n}\n```\n\n#### Flexible slots ####\ncpp-signal is very flexible when it comes to connecting slots to a signal.\n\n##### Static (global) methods #####\n```cpp\nstatic void global_slot(int value)\n{\n  std::cout \u003c\u003c \"global_slot(\" \u003c\u003c value \u003c\u003c \")\" \u003c\u003c std::endl;\n}\n...\ncpp_signal\u003c\u003e::signal\u003cvoid(int)\u003e signal;\nsignal.connect\u003cglobal_slot\u003e();\n\nsignal.emit(1);  // prints \"global_slot(1)\"\n```\n\n##### Static and non-static class methods #####\n```cpp\nclass test_class\n{\n  public:\n    static void static_slot(int value)\n    {\n      std::cout \u003c\u003c \"static_slot(\" \u003c\u003c value \u003c\u003c \")\" \u003c\u003c std::endl;\n    }\n\n    static void slot(int value)\n    {\n      std::cout \u003c\u003c \"slot(\" \u003c\u003c value \u003c\u003c \")\" \u003c\u003c std::endl;\n    }\n};\n\ntest_class test;\n\ncpp_signal\u003c\u003e::signal\u003cvoid(int)\u003e signal;\nsignal.connect\u003c\u0026test_class::static_slot\u003e();  // connects the static class method\nsignal.connect\u003ctest_class, \u0026test_class::slot\u003e(test);  // connects the non-static class method\n\nsignal.emit(1);  // prints \"static_slot(1)\" and \"slot(1)\"\n```\n\n##### Callables #####\n```cpp\nclass test_class\n{\n  public:\n    void operator()(int value)\n    {\n      std::cout \u003c\u003c \"test_class(\" \u003c\u003c value \u003c\u003c \")\" \u003c\u003c std::endl;\n    }\n};\n\ntest_class test;\n\nauto lambda = [](int value) { std::cout \u003c\u003c \"lambda(\" \u003c\u003c value \u003c\u003c \")\" \u003c\u003c std::endl; };\n\ncpp_signal\u003c\u003e::signal\u003cvoid(int)\u003e signal;\nsignal.connect(test);  // connects the callable test_class object\nsignal.connect(lambda);  // connects the callable lambda expression\n\nsignal.emit(1);  // prints \"test_class(1)\" and \"lambda(1)\"\n```\n\n#### Signal chaining ####\nDue to the fact that any callable object can be used as a slot it is also possible to use cpp-signal's `signal` class as a slot therefore effectively chaining multiple signals together. There's nothing more to it.\n```cpp\ncpp_signal\u003c\u003e::signal\u003cvoid(int)\u003e signal;\ncpp_signal\u003c\u003e::signal\u003cvoid(int)\u003e chained_signal;\n\nauto slot = [](int value) { std::cout \u003c\u003c \"slot(\" \u003c\u003c value \u003c\u003c \u003c\u003c \")\" \u003c\u003c std::endl; }\n\nsignal.connect(slot);\nsignal.connect(chained_signal);\nchained_signal.connect(slot);\n\nsignal.emit(1);\n```\nwill result in `slot` being called twice: once as a direct slot of `signal` and once as a slot of `chained_signal`.\n\n#### Copying ####\ncpp-signal features full support for copying classes deriving from `slot_tracker` including `signal`:\n\n*   when copying a tracked slot connected to a signal the copied slot will automatically also be connected to the same signal\n*   when copying a signal with connected slots the copied signal will have the same slots connected\n\n#### Slot result collection ####\ncpp-signal does not only support signals and slots with return type `void` but any copyable return type. A signal emitted using `signal::emit()` will simply discard the returned value(s). But using the special `signal::emit_collect()` method with a matching collector (which can be any callable object) it is possible to access and possibly collect all the values returned by connected slots. As with the rest of cpp-signal any collector needs to match the signal's return type or it will not even compile.\n```cpp\ncpp_signal\u003c\u003e::signal\u003cint(int)\u003e signal;\n\nauto slot_one = [](int value) -\u003e int { return value; };\nsignal.connect(slot_one);\nauto slot_two = [](int value) -\u003e int { return value * 2; };\nsignal.connect(slot_two);\n\nsignal.emit(1);  // the values returned by the slots is discarded\n\nint total = 0;\nauto collector = [\u0026total](int value) { total += value; }\n\nsignal.emit_collect(collector, 1);  // total == 3\n```\n\n#### Threading policy ####\nWhen it comes to threading there are many different applications out there with different needs. Some applications run in a single thread and concurrency is not an issue. Other applications use multiple threads which can all potentially interact with signals and it is necessary to protect the signal's internal state. Because locking isn't free and has a negative impact on performance cpp-signal does not enforce locking but rather provides the possibility to choose the best fitting threading policy. This is achieved by specifying the threading policy in `cppsignal\u003cTThreadingPolicy\u003e`. cpp-signal comes with the following threading policies:\n\n*   No locking (default): `cpp_signal_no_locking`\n*   Global locking: `cpp_signal_global_locking`\n*   Local locking: `cpp_signal_local_locking = std::mutex`\n*   Local recursive locking: `cpp_signal_recursive_local_locking = std::recursive_mutex`\n\nIt is also possible to implement and use custom threading policies. All that is required by any threading policy is to implement the following methods (matching `std::mutex`):\n```cpp\nvoid lock();\nvoid unlock();\n```\n\n#### Asynchronous signal emission ####\nIn addition to the `cpp-signal.h` header file cpp-signal comes with a second (optional) header file `cpp-signal-async.h` which provides `cpp_signal_async\u003c\u003e`. `cpp_signal_async\u003c\u003e` provides the same functionalities as `cpp_signal\u003c\u003e` but emitting a signal will be done asynchronously i.e. in a seperate thread.\n```cpp\n#include \u003cchrono\u003e\n#include \u003cthread\u003e\n\n#include \u003ccpp-signal-async.h\u003e\n...\nauto slot = [](int value) -\u003e int\n{\n  std::this_thread::sleep_for(std::chrono::milliseconds(1000));\n  std::cout \u003c\u003c \"slot(\" \u003c\u003c value \u003c\u003c \u003c\u003c \")\" \u003c\u003c std::endl;\n\n  return 2 * value;\n}\n\ncpp_signal\u003c\u003e::signal\u003cint(int)\u003e signal;\nsignal.connect(slot);\n\nstd::cout \u003c\u003c \"signal.emit(1)\" \u003c\u003c std::endl;\nsignal.emit(1);  // prints \"slot(1)\"\nstd::cout \u003c\u003c \"signal.emit(2)\" \u003c\u003c std::endl;\nsignal.emit(2);  // prints \"slot(2)\"\n\nstd::cout \u003c\u003c \"signal.disconnect(slot)\" \u003c\u003c std::endl;\nsignal.disconnect(slot);\n\nstd::cout \u003c\u003c \"signal.emit(3)\" \u003c\u003c std::endl;\nsignal.emit(3);  // this will not call the slot\n```\nwill result in the following output:\n```cpp\nsignal.emit(1)\nsignal.emit(2)\nslot(1)\nsignal.disconnect(slot)\nslot(2)\nslot(3)\n```\nAs can be seen the order is not the same as if it would be executed synchronously.\n\nDue to the asynchronous emission of signals there are a few things that need to be kept in mind:\n\n*   `signal` objects cannot be modified while they are being asynchronously emitted. It is possible to call any modifying methods on a `signal` object but it will block until the signal has finished the asynchronous emission.\n*   `cpp_signal_async\u003c\u003e` also supports specifying the locking policy. While for `cpp_signal\u003c\u003e` the default locking policy is no locking at all for `cpp_signal_async\u003c\u003e` the default locking policy is `std::mutex` because it needs to be thread-safe. When choosing a different locking policy it is the users responsibility to provide a thread-safe locking policy.\n*   All emission methods (`emit()`, `accumulate()`, `accumulate_op()`, `aggregate()` and `collect()`) return an `std::future\u003c\u003e` to be able to wait for emission to finish and to be able to access any results. As opposed to when using `std::future\u003c\u003e` returned from `std::async()` the returned `std::future\u003c\u003e` does not have to be stored and used but can be ignored. Either way the emission will be executed asynchronously (which is not the case with `std::async()`).\n\n### Known Issues ###\n#### Deadlocks ####\nRight now cpp-signal is not fully thread safe / reentrant and *does not* support recursive signal emissions within slots. Doing so may cause a deadlock if the emissions affect multiple threads. The reported [issue](https://github.com/Montellese/cpp-signal/issues/2) will stay open until this has been resolved.\n\n### Performance ###\nBased on the benchmarks used by [nano-signal-slot](https://github.com/NoAvailableAlias/signal-slot-benchmarks) cpp-signal ranks fourth right on [nano-signal-slot](https://github.com/NoAvailableAlias/nano-signal-slot)'s and Wink-Signals tails:\n\n| Library | Construct | Destruct | Connect | Emission | Combined | Threaded | Total |\n| ------- | ---------:| --------:| -------:| --------:| --------:| --------:|-----:|\n| jeffomatic jl_signal | 67404 | 17460 | 48014 | 37695 | 12649 | 0 | 183223 |\n| nano-signal-slot | 87259 | 12767 | 10344 | 37560 | 5907 | 0 | 153836 |\n| Wink-Signals | 82085 | 15923 | 10335 | 37565 | 6286 | 0 | 152193 |\n| cpp-signal | 85122 | 12172 | 10481 | 37321 | 5934 | 0 | 151030 |\n| Yassi | 82603 | 9621 | 6149 | 37554 | 3853 | 0 | 139779 |\n| amc522 Signal11 | 78651 | 9986 | 6529 | 37266 | 3996 | 0 | 136427 |\n| \\* fr00b0 nod | 74990 | 11400 | 7309 | 35454 | 4289 | 2667 | 136110 |\n| mwthinker Signal | 74392 | 8844 | 7109 | 37401 | 4045 | 0 | 131791 |\n| pbhogan Signals | 74262 | 10067 | 7113 | 35696 | 4460 | 0 | 131598 |\n| joanrieu signal11 | 66285 | 13515 | 8529 | 32561 | 4873 | 0 | 125764 |\n| \\* Kosta signals-cpp | 80267 | 9720 | 1859 | 20420 | 1419 | 11 | 113696 |\n| EvilTwin Observer | 71103 | 6423 | 2834 | 28855 | 1932 | 0 | 111147 |\n| \\* lsignal | 58744 | 5658 | 2789 | 34431 | 1813 | 941 | 104375 |\n| supergrover sigslot | 16461 | 2787 | 3555 | 37073 | 1481 | 0 | 61357 |\n| \\* winglot Signals | 11870 | 4367 | 4742 | 35282 | 1990 | 961 | 59212 |\n\n*\\* library is designed to always be thread-safe*\n\n### The Future ###\nI'm always open for new ideas and feedback.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmontellese%2Fcpp-signal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmontellese%2Fcpp-signal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmontellese%2Fcpp-signal/lists"}