{"id":13629773,"url":"https://github.com/palacaze/sigslot","last_synced_at":"2025-10-05T18:53:07.697Z","repository":{"id":20598689,"uuid":"90383729","full_name":"palacaze/sigslot","owner":"palacaze","description":"A simple C++14 signal-slots implementation","archived":false,"fork":false,"pushed_at":"2025-07-26T12:04:36.000Z","size":144,"stargazers_count":813,"open_issues_count":10,"forks_count":106,"subscribers_count":22,"default_branch":"master","last_synced_at":"2025-07-26T17:39:04.075Z","etag":null,"topics":["cpp","cpp14","signal-slots"],"latest_commit_sha":null,"homepage":"","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/palacaze.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-05-05T14:21:32.000Z","updated_at":"2025-07-26T12:15:14.000Z","dependencies_parsed_at":"2024-05-17T20:21:21.616Z","dependency_job_id":"d4f4755b-e398-460e-9fc9-de3a3f23d2d8","html_url":"https://github.com/palacaze/sigslot","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/palacaze/sigslot","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/palacaze%2Fsigslot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/palacaze%2Fsigslot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/palacaze%2Fsigslot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/palacaze%2Fsigslot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/palacaze","download_url":"https://codeload.github.com/palacaze/sigslot/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/palacaze%2Fsigslot/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278503206,"owners_count":25997717,"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","status":"online","status_checked_at":"2025-10-05T02:00:06.059Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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","cpp14","signal-slots"],"created_at":"2024-08-01T22:01:18.870Z","updated_at":"2025-10-05T18:53:07.673Z","avatar_url":"https://github.com/palacaze.png","language":"C++","funding_links":[],"categories":["Miscellaneous","C++"],"sub_categories":[],"readme":"# Sigslot, a signal-slot library\n\nSigslot is a header-only, thread safe implementation of signal-slots for C++.\n\n## Features\n\nThe main goal was to replace Boost.Signals2.\n\nApart from the usual features, it offers\n\n- Thread safety,\n- Object lifetime tracking for automatic slot disconnection (extensible through ADL),\n- RAII connection management,\n- Slot groups to enforce slots execution order,\n- Reasonable performance. and a simple and straightforward implementation.\n\nSigslot is unit-tested and should be reliable and stable enough to replace Boost Signals2.\n\nThe tests run cleanly under the address, thread and undefined behaviour sanitizers.\n\nMany implementations allow signal return types, Sigslot does not because I have\nno use for them. If I can be convinced of otherwise I may change my mind later on.\n\n## Installation\n\nNo compilation or installation is required, just include `sigslot/signal.hpp`\nand use it. Sigslot currently depends on a C++14 compliant compiler, but if need\narises it may be retrofitted to C++11. It is known to work with Clang 4.0 and GCC\n5.0+ compilers on GNU Linux, MSVC 2017 and up, Clang-cl and MinGW on Windows.\n\nHowever, be aware of a potential gotcha on Windows with MSVC and Clang-Cl compilers,\nwhich may need the `/OPT:NOICF` linker flags in exceptional situations. Read The\nImplementation Details chapter for an explanation.\n\nA CMake list file is supplied for installation purpose and generating a CMake import\nmodule. This is the preferred installation method. The `Pal::Sigslot` imported target\nis available and already applies the needed linker flags. It is also required for\nexamples and tests, which optionally depend on Qt5 and Boost for adapters unit tests.\n\n```cmake\n# Using Sigslot from cmake\nfind_package(PalSigslot)\n\nadd_executable(MyExe main.cpp)\ntarget_link_libraries(MyExe PRIVATE Pal::Sigslot)\n```\n\nA configuration option `SIGSLOT_REDUCE_COMPILE_TIME` is available at configuration\ntime. When activated, it attempts to reduce code bloat by avoiding heavy template\ninstantiations resulting from calls to `std::make_shared`.\nThis option is off by default, but can be activated for those who wish to favor\ncode size and compilation time at the expanse of slightly less efficient code.\n\nInstallation may be done using the following instructions from the root directory:\n\n```sh\nmkdir build \u0026\u0026 cd build\ncmake .. -DSIGSLOT_REDUCE_COMPILE_TIME=ON -DCMAKE_INSTALL_PREFIX=~/local\ncmake --build . --target install\n\n# If you want to compile examples:\ncmake --build . --target sigslot-examples\n\n# And compile/execute unit tests:\ncmake --build . --target sigslot-tests\n```\n\n### CMake FetchContent\n\n`Pal::Sigslot` can also be integrated using the [FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html) method.\n\n```cmake\ninclude(FetchContent)\n\nFetchContent_Declare(\n  sigslot\n  GIT_REPOSITORY https://github.com/palacaze/sigslot\n  GIT_TAG        19a6f0f5ea11fc121fe67f81fd5e491f2d7a4637 # v1.2.0\n)\nFetchContent_MakeAvailable(sigslot)\n\nadd_executable(MyExe main.cpp)\ntarget_link_libraries(MyExe PRIVATE Pal::Sigslot)\n```\n\n## Documentation\n\nSigslot implements the signal-slot construct popular in UI frameworks, making it\neasy to use the observer pattern or event-based programming. The main entry point\nof the library is the `sigslot::signal\u003cT...\u003e` class template.\n\nA signal is an object that can emit typed notifications, really values parametrized\nafter the signal class template parameters, and register any number of notification\nhandlers (callables) of compatible argument types to be executed with the values\nsupplied whenever a signal emission happens. In signal-slot parlance this is called\nconnecting a slot to a signal, where a \"slot\" represents a callable instance and\na \"connection\" can be thought of as a conceptual link from signal to slot.\n\nAll the snippets presented below are available in compilable source code form in\nthe example subdirectory.\n\n### Basic usage\n\nHere is a first example that showcases the most basic features of the library.\n\nWe first declare a parameter-free signal `sig`, then we proceed to connect several\nslots and at last emit a signal which triggers the invocation of every slot callable\nconnected beforehand. Notice how The library handles diverse forms of callables.\n\n```cpp\n#include \u003csigslot/signal.hpp\u003e\n#include \u003ciostream\u003e\n\nvoid f() { std::cout \u003c\u003c \"free function\\n\"; }\n\nstruct s {\n    void m() { std::cout \u003c\u003c \"member function\\n\"; }\n    static void sm() { std::cout \u003c\u003c \"static member function\\n\";  }\n};\n\nstruct o {\n    void operator()() { std::cout \u003c\u003c \"function object\\n\"; }\n};\n\nint main() {\n    s d;\n    auto lambda = []() { std::cout \u003c\u003c \"lambda\\n\"; };\n    auto gen_lambda = [](auto \u0026\u0026 ...a) { std::cout \u003c\u003c \"generic lambda\\n\"; };\n\n    // declare a signal instance with no arguments\n    sigslot::signal\u003c\u003e sig;\n\n    // connect slots\n    sig.connect(f);\n    sig.connect(\u0026s::m, \u0026d);\n    sig.connect(\u0026s::sm);\n    sig.connect(o());\n    sig.connect(lambda);\n    sig.connect(gen_lambda);\n\n    // a free connect() function is also available\n    sigslot::connect(sig, f);\n\n    // emit a signal\n    sig();\n}\n```\n\nBy default, the slot invocation order when emitting a signal is unspecified, please\ndo not rely on it being always the same. You may constrain a particular invocation\norder by using slot groups, which are presented later on.\n\n### Signal with arguments\n\nThat first example was simple but not so useful, let us move on to a signal that\nemits values instead. A signal can emit any number of arguments, below.\n\n```cpp\n#include \u003csigslot/signal.hpp\u003e\n#include \u003ciostream\u003e\n#include \u003cstring\u003e\n\nstruct foo {\n    // Notice how we accept a double as first argument here.\n    // This is fine because float is convertible to double.\n    // 's' is a reference and can thus be modified.\n    void bar(double d, int i, bool b, std::string \u0026s) {\n        s = b ? std::to_string(i) : std::to_string(d);\n    }\n};\n\n// Function objects can cope with default arguments and overloading.\n// It does not work with static and member functions.\nstruct obj {\n    void operator()(float, int, bool, std::string \u0026, int = 0) {\n        std::cout \u003c\u003c \"I was here\\n\";\n    }\n\n    void operator()() {}\n};\n\nint main() {\n    // declare a signal with float, int, bool and string\u0026 arguments\n    sigslot::signal\u003cfloat, int, bool, std::string\u0026\u003e sig;\n\n    // a generic lambda that prints its arguments to stdout\n    auto printer = [] (auto a, auto \u0026\u0026 ...args) {\n        std::cout \u003c\u003c a;\n        (void)std::initializer_list\u003cint\u003e{\n            ((void)(std::cout \u003c\u003c \" \" \u003c\u003c args), 1)...\n        };\n        std::cout \u003c\u003c \"\\n\";\n    };\n\n    // connect the slots\n    foo ff;\n    sig.connect(printer);\n    sig.connect(\u0026foo::bar, \u0026ff);\n    sig.connect(obj());\n\n    float f = 1.f;\n    short i = 2;  // convertible to int\n    std::string s = \"0\";\n\n    // emit a signal\n    sig(f, i, false, s);\n    sig(f, i, true, s);\n}\n```\n\nAs shown, slots arguments types don't need to be strictly identical to the signal\ntemplate parameters, being convertible-from is fine. Generic arguments are fine too,\nas shown with the `printer` generic lambda (which could have been written as a\nfunction template too).\n\nRight now there are two limitations that I can think of with respect to callable\nhandling: default arguments and function overloading. Both are working correctly\nin the case of function objects but will fail to compile with static and member\nfunctions, for different but related reasons.\n\n#### Coping with overloaded functions\n\nConsider the following piece of code:\n\n```cpp\nstruct foo {\n    void bar(double d);\n    void bar();\n};\n```\n\nWhat should `\u0026foo::bar` refer to? As per overloading, this pointer over member\nfunction does not map to a unique symbol, so the compiler won't be able to pick\nthe right symbol. One way of resolving the right symbol is to explicitly cast the\nfunction pointer to the right function type. Here is an example that does just that\nusing a little helper tool for a lighter syntax (In fact I will probably add this\nto the library soon).\n\n```cpp\n#include \u003csigslot/signal.hpp\u003e\n\ntemplate \u003ctypename... Args, typename C\u003e\nconstexpr auto overload(void (C::*ptr)(Args...)) {\n    return ptr;\n}\n\ntemplate \u003ctypename... Args\u003e\nconstexpr auto overload(void (*ptr)(Args...)) {\n    return ptr;\n}\n\nstruct obj {\n    void operator()(int) const {}\n    void operator()() {}\n};\n\nstruct foo {\n    void bar(int) {}\n    void bar() {}\n\n    static void baz(int) {}\n    static void baz() {}\n};\n\nvoid moo(int) {}\nvoid moo() {}\n\nint main() {\n    sigslot::signal\u003cint\u003e sig;\n\n    // connect the slots, casting to the right overload if necessary\n    foo ff;\n    sig.connect(overload\u003cint\u003e(\u0026foo::bar), \u0026ff);\n    sig.connect(overload\u003cint\u003e(\u0026foo::baz));\n    sig.connect(overload\u003cint\u003e(\u0026moo));\n    sig.connect(obj());\n\n    sig(0);\n\n    return 0;\n}\n```\n\n#### Coping with function with default arguments\n\nDefault arguments are not part of the function type signature, and can be redefined,\nso they are really difficult to deal with. When connecting a slot to a signal, the\nlibrary determines if the supplied callable can be invoked with the signal argument\ntypes, but at this point the existence of default function arguments is unknown\nso there might be a mismatch in the number of arguments.\n\nA simple work around for this use case would is to create a bind adapter, in fact\nwe can even make it quite generic like so:\n\n```cpp\n#include \u003csigslot/signal.hpp\u003e\n\n#define ADAPT(func) \\\n    [=](auto \u0026\u0026 ...a) { (func)(std::forward\u003cdecltype(a)\u003e(a)...); }\n\nvoid foo(int \u0026i, int b = 1) {\n    i += b;\n}\n\nint main() {\n    int i = 0;\n\n    // fine, all the arguments are handled\n    sigslot::signal\u003cint\u0026, int\u003e sig1;\n    sig1.connect(foo);\n    sig1(i, 2);\n\n    // must wrap in an adapter\n    i = 0;\n    sigslot::signal\u003cint\u0026\u003e sig2;\n    sig2.connect(ADAPT(foo));\n    sig2(i);\n\n    return 0;\n}\n```\n\n### Connection management\n\n#### Connection object\n\nWhat was not made apparent until now is that `signal::connect()` actually returns\na `sigslot::connection` object that may be used to manage the behaviour and lifetime\nof a signal-slot connection. `sigslot::connection` is a lightweight object (basically\na `std::weak_ptr`) that allows interaction with an ongoing signal-slot connection\nand exposes the following features:\n\n- Status querying, that is testing whether a connection is valid, ongoing or facing destruction,\n- Connection (un)blocking, which allows to temporarily disable the invocation of a slot when a signal is emitted,\n- Disconnection of a slot, the destruction of a connection previously created via `signal::connect()`.\n\nA `sigslot::connection` does not tie a connection to a scope: this is not a RAII\nobject, which explains why it can be copied. It can be however implicitly converted\ninto a `sigslot::scoped_connection` which destroys the connection when going out\nof scope.\n\nHere is an example illustrating some of those features:\n\n```cpp\n#include \u003csigslot/signal.hpp\u003e\n#include \u003cstring\u003e\n\nint i = 0;\n\nvoid f() { i += 1; }\n\nint main() {\n    sigslot::signal\u003c\u003e sig;\n\n    // keep a sigslot::connection object\n    auto c1 = sig.connect(f);\n\n    // disconnection\n    sig();  // i == 1\n    c1.disconnect();\n    sig();  // i == 1\n\n    // scope based disconnection\n    {\n        sigslot::scoped_connection sc = sig.connect(f);\n        sig();  // i == 2\n    }\n\n    sig();  // i == 2;\n\n\n    // connection blocking\n    auto c2 = sig.connect(f);\n    sig();  // i == 3\n    c2.block();\n    sig();  // i == 3\n    c2.unblock();\n    sig();  // i == 4\n}\n```\n\n#### Extended connection signature\n\nSigslot supports an extended slot signature with an additional `sigslot::connection`\nreference as first argument, which permits connection management from inside the\nslot. This extended signature is accessible using the `connect_extended()` method.\n\n```cpp\n#include \u003csigslot/signal.hpp\u003e\n\nint main() {\n    int i = 0;\n    sigslot::signal\u003c\u003e sig;\n\n    // extended connection\n    auto f = [](auto \u0026con) {\n        i += 1;             // do work\n        con.disconnect();   // then disconnects\n    };\n\n    sig.connect_extended(f);\n    sig();  // i == 1\n    sig();  // i == 1 because f was disconnected\n}\n```\n\n#### Automatic slot lifetime tracking\n\nThe user must make sure that the lifetime of a slot exceeds the one of a signal,\nwhich may get tedious in complex software. To simplify this task, Sigslot can\nautomatically disconnect slot object whose lifetime it is able to track. In order\nto do that, the slot must be convertible to a weak pointer of some form.\n\n`std::shared_ptr` and `std::weak_ptr` are supported out of the box, and adapters\nare provided to support `boost::shared_ptr`, `boost::weak_ptr` and Qt `QSharedPointer`,\n`QWeakPointer` and any class deriving from `QObject`.\n\nOther trackable objects can be added by declaring a `to_weak()` adapter function.\n\n```cpp\n#include \u003csigslot/signal.hpp\u003e\n#include \u003csigslot/adapter/qt.hpp\u003e\n\nint sum = 0;\n\nstruct s {\n    void f(int i) { sum += i; }\n};\n\nclass MyObject : public QObject {\n    Q_OBJECT\npublic:\n    void add(int i) const { sum += i; }\n};\n\nint main() {\n    sum = 0;\n    signal\u003cint\u003e sig;\n\n    // track lifetime of object and also connect to a member function\n    auto p = std::make_shared\u003cs\u003e();\n    sig.connect(\u0026s::f, p);\n\n    sig(1);     // sum == 1\n    p.reset();\n    sig(1);     // sum == 1\n\n    // track an unrelated object lifetime\n    struct dummy;\n    auto l = [\u0026](int i) { sum += i; };\n\n    auto d = std::make_shared\u003cdummy\u003e();\n    sig.connect(l, d);\n    sig(1);     // sum == 2\n    d.reset();\n    sig(1);     // sum == 2\n\n    // track a QObject\n    {\n        MyObject o;\n        sig.connect(\u0026MyObject::add, \u0026o);\n\n        sig(1); // sum == 3\n    }\n\n    sig(1);     // sum == 3\n}\n```\n\n#### Intrusive slot lifetime tracking\n\nAnother way of ensuring automatic disconnection of pointer over member functions\nslots is by explicitly inheriting from `sigslot::observer` or `sigslot::observer_st`.\nThe former is thread-safe, contrary to the later.\n\nHere is an example usage.\n\n```cpp\n#include \u003csigslot/signal.hpp\u003e\n\nint sum = 0;\n\nstruct s : sigslot::observer_st {\n    void f(int i) { sum += i; }\n};\n\nstruct s_mt : sigslot::observer {\n    ~s_mt() {\n        // Needed to ensure proper disconnection prior to object destruction\n        // in multithreaded contexts.\n        this-\u003edisconnect_all();\n    }\n\n    void f(int i) { sum += i; }\n};\n\nint main() {\n    sum = 0;\n    signal\u003cint\u003e sig;\n\n    {\n        // Lifetime of object instance p is tracked\n        s p;\n        s_mt pm;\n        sig.connect(\u0026s::f, \u0026p);\n        sig.connect(\u0026s_mt::f, \u0026pm);\n        sig(1);     // sum == 2\n    }\n\n    // The slots got disconnected at instance destruction\n    sig(1);         // sum == 2\n}\n```\n\nThe objects that use this intrusive approach may be connected to any number of\nunrelated signals.\n\n### Disconnection without a connection object\n\nSupport for slot disconnection by supplying an appropriate function signature,\nobject pointer or tracker has been introduced in version 1.2.0.\n\nOne can disconnect any number of slots using the `signal::disconnect()` method,\nwhich proposes 4 overloads to specify the disconnection criterion:\n\n- The first takes a reference to a callable. Any kind of callable can be passed,\n  even pointers to member functions, function objects and lambdas,\n- The second takes a pointer to an object, for slots bound to a pointer to member\n  function, or a tracking object,\n- The third overload takes both kinds of arguments at the same time and can be\n  used to pinpoint a specific pair of object + callable.\n- The last overload takes a group id and disconnects all the slots in this group.\n\nDisconnection of lambdas is only possible for lambdas bound to a variable, due\nto their uniqueness.\n\nThe second overload currently needs RTTI to disconnect from pointers to member\nfunctions, function objects and lambdas. This limitation does not apply to free\nand static member functions. The reasons stems from the fact that in C++, pointers\nto member functions of unrelated types are not comparable, contrary to pointers to\nfree and static member functions. For instance, the pointer to member functions of\nvirtual methods of different classes can have the same address (they kind of store\nthe offset of the method into the vtable).\n\nHowever, Sigslot can be compiled with RTTI disabled and the overload will be\ndeactivated for problematic cases.\n\nAs a side node, this feature admittedly added more code than anticipated at first\nbecause it is a tricky and easy to get wrong. It has been designed carefully, with\ncorrectness in mind, and does not have any hidden costs unless you actually use it.\n\nHere is an example demonstrating the feature.\n\n```cpp\n#include \u003csigslot/signal.hpp\u003e\n#include \u003cstring\u003e\n\nstatic int i = 0;\n\nvoid f1() { i += 1; }\nvoid f2() { i += 1; }\n\nstruct s {\n    void m1() { i += 1; }\n    void m2() { i += 1; }\n    void m3() { i += 1; }\n};\n\nstruct o {\n    void operator()() { i += 1; }\n};\n\nint main() {\n    sigslot::signal\u003c\u003e sig;\n    s s1;\n    auto s2 = std::make_shared\u003cs\u003e();\n\n    auto lbd = [\u0026] { i += 1; };\n\n    sig.connect(f1);           // #1\n    sig.connect(f2);           // #2\n    sig.connect(\u0026s::m1, \u0026s1);  // #3\n    sig.connect(\u0026s::m2, \u0026s1);  // #4\n    sig.connect(\u0026s::m3, \u0026s1);  // #5\n    sig.connect(\u0026s::m1, s2);   // #6\n    sig.connect(\u0026s::m2, s2);   // #7\n    sig.connect(o{});          // #8\n    sig.connect(lbd);          // #9\n\n    sig();  // i == 9\n\n    sig.disconnect(f2);              // #2 is removed\n    sig.disconnect(\u0026s::m1);          // #3 and #6 are removed\n    sig.disconnect(o{});             // #8 and is removed\n // sig.disconnect(\u0026o::operator());  // same as the above, more efficient\n    sig.disconnect(lbd);             // #9 and is removed\n    sig.disconnect(s2);              // #7 is removed\n    sig.disconnect(\u0026s::m3, \u0026s1);     // #5 is removed, not #4\n\n    sig();  // i == 11\n\n    sig.disconnect_all();         // remove all remaining slots\n    return 0;\n}\n```\n\n### Enforcing slot invocation order with slot groups\n\nFrom version 1.2.0, slots can be assigned a group id in order to control the\nrelative order of invocation of slots.\n\nThe order of invocation of slots in a same group is unspecified and should not be\nrelied upon, however slot groups are invoked in ascending group id order.\nWhen the group id of a slot is not set, it is assigned to the group 0.\nGroup ids can have any value in the range of signed 32 bit integers.\n\n```cpp\n#include \u003csigslot/signal.hpp\u003e\n#include \u003ccstdio\u003e\n#include \u003climits\u003e\n\nint main() {\n    sigslot::signal\u003c\u003e sig;\n\n    // simply assigning a group id as last argument to connect\n    sig.connect([] { std::puts(\"Second\"); }, 1);\n    sig.connect([] { std::puts(\"Last\"); }, std::numeric_limits\u003csigslot::group_id\u003e::max());\n    sig.connect([] { std::puts(\"First\"); }, -10);\n    sig();\n\n    return 0;\n}\n```\n\n### Signal chaining\n\nThe freestanding `sigslot::connect()` function can be used to connect a signal\nto another with compatible arguments.\n\n```cpp\n#include \u003csigslot/signal.hpp\u003e\n#include \u003ciostream\u003e\n\nint main() {\n    sigslot::signal\u003cint\u003e sig1;\n    sigslot::signal\u003cdouble\u003e sig2;\n\n    sigslot::connect(sig1, sig2);\n    sigslot::connect(sig2, [] (double d) { std::cout \u003c\u003c \"got \" \u003c\u003c d \u003c\u003c std::endl; });\n    sig(1);\n\n    return 0;\n}\n```\n\n### Thread safety\n\nThread safety is unit-tested. In particular, cross-signal emission and recursive\nemission run fine in a multiple threads scenario.\n\n`sigslot::signal` is a typedef to the more general `sigslot::signal_base` template\nclass, whose first template argument must be a Lockable type. This type will dictate\nthe locking policy of the class.\n\nSigslot offers 2 typedefs,\n\n- `sigslot::signal` usable from multiple threads and uses std::mutex as a lockable.\n  In particular, connection, disconnection, emission and slot execution are thread\n  safe. It is also safe with recursive signal emission.\n- `sigslot::signal_st` is a non thread-safe alternative, it trades safety for slightly\n  faster operation.\n\n\n## Implementation details\n\n### Using function pointers to disconnect slots\n\nComparing function pointers is a nightmare in C++. Here is a table demonstrating\nthe size and address of a variety of cases as a showcase:\n\n```cpp\nvoid fun() {}\n\nstruct b1 {\n    virtual ~b1() = default;\n    static void sm() {}\n    void m() {}\n    virtual void vm() {}\n};\n\nstruct b2 {\n    virtual ~b2() = default;\n    static void sm() {}\n    void m() {}\n    virtual void vm() {}\n};\n\nstruct c {\n    virtual ~c() = default;\n    virtual void w() {}\n};\n\nstruct d : b1 {\n    static void sm() {}\n    void m() {}\n    void vm() override {}\n};\n\nstruct e : b1, c {\n    static void sm() {}\n    void m() {}\n    void vm() override{}\n};\n```\n\n| Symbol  | GCC 9 Linux 64\u003cbr\u003eSizeof | GCC 9 Linux 64\u003cbr\u003eAddress | MSVC 16.6 32\u003cbr\u003eSizeof | MSVC 16.6 32\u003cbr\u003eAddress | GCC 8 Mingw 32\u003cbr\u003eSizeof | GCC 8 Mingw 32\u003cbr\u003eAddress | Clang-cl 9 32\u003cbr\u003eSizeof | Clang-cl 9 32\u003cbr\u003eAddress |\n|---------|--------------------------|---------------------------|------------------------|-------------------------|--------------------------|---------------------------|-------------------------|--------------------------|\n| fun     | 8                        | 0x802340                  | 4                      | 0x1311A6                | 4                        | 0xF41540                  | 4                       | 0x0010AE                 |\n| \u0026b1::sm | 8                        | 0xE03140                  | 4                      | 0x7612A5                | 4                        | 0x308D40                  | 4                       | 0x0010AE                 |\n| \u0026b1::m  | 16                       | 0xF03240                  | 4                      | 0x1514A5                | 8                        | 0x248D40                  | 4                       | 0x0010AE                 |\n| \u0026b1::vm | 16                       | 0x11                      | 4                      | 0x9F11A5                | 8                        | 0x09                      | 4                       | 0x8023AE                 |\n| \u0026b2::sm | 8                        | 0x003340                  | 4                      | 0xA515A5                | 4                        | 0x408D40                  | 4                       | 0x0010AE                 |\n| \u0026b2::m  | 16                       | 0x103440                  | 4                      | 0xEB10A5                | 8                        | 0x348D40                  | 4                       | 0x0010AE                 |\n| \u0026b2::vm | 16                       | 0x11                      | 4                      | 0x6A14A5                | 8                        | 0x09                      | 4                       | 0x8023AE                 |\n| \u0026d::sm  | 8                        | 0x203440                  | 4                      | 0x2612A5                | 4                        | 0x108D40                  | 4                       | 0x0010AE                 |\n| \u0026d::m   | 16                       | 0x303540                  | 4                      | 0x9D13A5                | 8                        | 0x048D40                  | 4                       | 0x0010AE                 |\n| \u0026d::vm  | 16                       | 0x11                      | 4                      | 0x4412A5                | 8                        | 0x09                      | 4                       | 0x8023AE                 |\n| \u0026e::sm  | 8                        | 0x403540                  | 4                      | 0xF911A5                | 4                        | 0x208D40                  | 4                       | 0x0010AE                 |\n| \u0026e::m   | 16                       | 0x503640                  | 8                      | 0x8111A5                | 8                        | 0x148D40                  | 8                       | 0x0010AE                 |\n| \u0026e::vm  | 16                       | 0x11                      | 8                      | 0xA911A5                | 8                        | 0x09                      | 8                       | 0x8023AE                 |\n\nMSVC and Clang-cl in Release mode optimize functions with the same definition by\nmerging them. This is a behaviour that can be deactivated with the `/OPT:NOICF`\nlinker option.\nSigslot tests and examples rely on a lot a identical callables which trigger this\nbehaviour, which is why it deactivates this particular optimization on the affected\ncompilers.\n\n### Known bugs\n\nUsing generic lambdas with GCC less than version 7.4 can trigger [Bug #68071](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68071).\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpalacaze%2Fsigslot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpalacaze%2Fsigslot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpalacaze%2Fsigslot/lists"}