{"id":26474181,"url":"https://github.com/mrizaln/sync-cpp","last_synced_at":"2025-03-19T22:45:20.982Z","repository":{"id":219943232,"uuid":"750325276","full_name":"mrizaln/sync-cpp","owner":"mrizaln","description":"Synchronized object wrapper for C++20","archived":false,"fork":false,"pushed_at":"2025-01-11T19:52:24.000Z","size":106,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-11T20:32:02.931Z","etag":null,"topics":["cpp","data-structures","synchronization"],"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/mrizaln.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":"2024-01-30T12:35:25.000Z","updated_at":"2025-01-11T19:52:27.000Z","dependencies_parsed_at":"2024-04-17T08:37:07.305Z","dependency_job_id":"18f11cd6-fed7-4cc3-b8ca-6cfb8591078c","html_url":"https://github.com/mrizaln/sync-cpp","commit_stats":null,"previous_names":["mrizaln/sync-cpp"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrizaln%2Fsync-cpp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrizaln%2Fsync-cpp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrizaln%2Fsync-cpp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrizaln%2Fsync-cpp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mrizaln","download_url":"https://codeload.github.com/mrizaln/sync-cpp/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244520022,"owners_count":20465624,"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","data-structures","synchronization"],"created_at":"2025-03-19T22:45:20.456Z","updated_at":"2025-03-19T22:45:20.970Z","avatar_url":"https://github.com/mrizaln.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# sync-cpp\n\nSynchronized object wrapper for C++20\n\n## TODO\n\n- [x] Documentation\n- [ ] Allow stateful lambda for `SyncContainer` `Getter` (but should I though?)\n- [ ] Rework `Sync::read` and `Sync::write` that has member function arguments with some kind of function traits ([see](https://breese.github.io/2022/03/06/deducing-function-signatures.html)) to reduce repetition\n\n## Dependencies\n\n- C++20\n  \u003e The compiler must have support for constraints and concepts both in language and library, coroutines is not necessary\n\n## Usage\n\n### Setting up\n\nClone this repository (or as submodule) into your project somewhere, even easier, use FetchContent. Then link your target against `sync-cpp`.\n\n```cmake\n# If you are using FetchContent\ninclude(FetchContent)\nFetchContent_Declare(\n  sync-cpp\n  GIT_REPOSITORY https://github.com/mrizaln/sync-cpp\n  GIT_TAG main)\n\n# # If you clone/submodule the repository\n# add_subdirectory(path/to/the/cloned/repository)\n\nadd_executable(main main.cpp)\ntarget_link_libraries(main PRIVATE sync-cpp)\n```\n\n### Example\n\nThis is an example usage of this library.\n\n```cpp\n#include \u003csync_cpp/sync.hpp\u003e                // for Sync\u003cT, M\u003e;\n#include \u003csync_cpp/sync_smart_ptr.hpp\u003e      // SyncUnique, SyncUniqueCustom, SyncShared: wrapper for Sync\u003cstd::unique_ptr, M\u003e (also shared_ptr)\n#include \u003csync_cpp/sync_opt.hpp\u003e            // same as above, but for std::optional\n#include \u003csync_cpp/group.hpp\u003e               // allow grouped lock through spp::Group wrapper and spp::group factory function\n\n// #include \u003csync_cpp/sync_container.hpp\u003e   // Sync container adapter (for your own container, single valued like std::unique_ptr)\n\n#include \u003ciostream\u003e\n\nstruct Foo\n{\n    int bar(double d) const { return static_cast\u003cint\u003e(d / 2); };\n};\n\nint main()\n{\n    using namespace std::string_literals;\n\n    auto string = spp::Sync{ \"sdafjh\"s };                         // deduction guide -\u003e spp::Sync\u003cstd::string, std::mutex\u003e\n    auto substr = string.write([](auto\u0026 str) {                    // mutate the value inside Sync\n        str = \"hello world!\";\n        return str.substr(6, 5);\n    });\n\n    std::cout \u003c\u003c substr \u003c\u003c '\\n';\n\n    auto sync_a = spp::Sync\u003cFoo, std::shared_mutex\u003e{};            // using std::shared_mutex\n    auto value  = sync_a.read(\u0026Foo::bar, 403.9);                  // calling (const) member function\n\n    auto sync_unique = spp::SyncUnique\u003cFoo\u003e{ new Foo{} };         // Sync\u003cstd::unique_ptr\u003cT\u003e, M\u003e but with more convenient API\n    auto not_null    = sync_unique.read([](const auto\u0026 sp) {      // access (read) the unique_ptr\n        return sp != nullptr;\n    });\n\n    if (sync_unique) {                                            // bool conversion just like std::unique_ptr\n        auto n = sync_unique.read_value([](const Foo\u0026 a) {        // read the value contained within unique_ptr\n            int v = a.bar(42.0);\n            return v * 12;\n        });\n    }\n\n    // grouped lock                         ---- or use read()/write() for const/non-const access to all\n    //                                      ---- lock() access for each element is dependent on the constness of the underlying Sync\n    spp::group(string, sync_a, sync_unique).lock([](auto\u0026\u0026 s, auto\u0026\u0026 a, auto\u0026\u0026 b) {\n        std::cout \u003c\u003c \"string: \" \u003c\u003c s \u003c\u003c '\\n'\n                  \u003c\u003c \"Foo 1 : \" \u003c\u003c a.bar(3.14) \u003c\u003c '\\n'\n                  \u003c\u003c \"Foo 2 : \" \u003c\u003c b-\u003ebar(1000.0) \u003c\u003c '\\n';\n    });\n}\n```\n\n\u003e See also an example project [here](./example)\n\n## Limitations\n\n- Member functions of `Sync` that receives member function pointer can't disambiguate an overloaded function. A work around is to use a lambda.\n\n```cpp\nclass Foo {\n    int something() \u0026;\n    int something() \u0026\u0026;\n};\n\nspp::Sync\u003cFoo\u003e foo;\n// int value = foo.write(\u0026Foo::something);                      // won't compile\nint value = foo.write([](auto\u0026 f) { return f.something(); });   // compiles\n```\n\n- We might want to return a reference to a value that may have longer lifetime or have static storage duration from an instance of a class. We can't directly do that because the constraint on the `Sync::read` and `Sync::write` functions prohibits returning a reference. However, we can return a pointer.\n\n  \u003e This pattern is heavily discouraged, you are basically trying to access a resource that might not be synchronized by doing this.\n\n```cpp\nstruct Something;\n\nstruct Foo\n{\n    Something\u0026 get_global_something() const;\n};\n\nauto foo = spp::Sync\u003cFoo\u003e{};\n\n// ...\n\nauto* something = foo.read([](const Foo\u0026 f) { return \u0026f.get_global_something(); });\n\n// even this\nauto* innerFoo = foo.write([](Foo\u0026 f) { return \u0026f; });    // don't do this\n```\n\n- Unfortunately, currently we can pass a `nullptr` as member function pointer to the read and write function. For now, I'll ignore this case, and assume that every member function pointer passed to the functions are not `nullptr`. I don't know what to do when it's `nullptr` (`throw`? `assert`? do nothing?)\n\n```cpp\nstruct Foo\n{\n    int bar(int v);\n};\n\nspp::Sync\u003cFoo\u003e foo;\n\n// ...\n\nusing F = decltype(\u0026Foo::bar);\nF f     = nullptr;\nint v   = foo.write(f, 1);    // passing a nullptr, very bad\n```\n\n## Customization\n\nThe class [`SyncContainer`](./include/sync_cpp/sync_container.hpp) is an adapter class that flattens the accessor (read and write) to the value inside Sync (read_value and writeValue). You can extend from this class to work with other container (or your custom type) so it will be easier to work with\n\n\u003e For examples, see how [SyncSmartPtr](./include/sync_cpp/sync_smart_ptr.hpp) and [SyncOpt](./include/sync_cpp/sync_opt.hpp) implemented.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmrizaln%2Fsync-cpp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmrizaln%2Fsync-cpp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmrizaln%2Fsync-cpp/lists"}