{"id":24876023,"url":"https://github.com/jlaumon/handle","last_synced_at":"2025-04-13T11:41:29.026Z","repository":{"id":150471652,"uuid":"138472007","full_name":"jlaumon/handle","owner":"jlaumon","description":"An (obviously) super cool handle implementation.","archived":false,"fork":false,"pushed_at":"2019-02-23T16:42:06.000Z","size":611,"stargazers_count":10,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-03-27T02:45:33.954Z","etag":null,"topics":["cpp","gamedev","generational-index","handle"],"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/jlaumon.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2018-06-24T10:14:13.000Z","updated_at":"2025-03-24T12:46:40.000Z","dependencies_parsed_at":"2023-07-28T14:16:09.633Z","dependency_job_id":null,"html_url":"https://github.com/jlaumon/handle","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/jlaumon%2Fhandle","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jlaumon%2Fhandle/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jlaumon%2Fhandle/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jlaumon%2Fhandle/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jlaumon","download_url":"https://codeload.github.com/jlaumon/handle/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248709428,"owners_count":21149178,"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","gamedev","generational-index","handle"],"created_at":"2025-02-01T08:18:59.840Z","updated_at":"2025-04-13T11:41:29.018Z","avatar_url":"https://github.com/jlaumon.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build status](https://ci.appveyor.com/api/projects/status/nntont83nb1uk9q2?svg=true)](https://ci.appveyor.com/project/jlaumon/handle)\n\n# Handle\n\nHandles (in general) are a way of referencing an object with an integer. It's a little bit like pointers, except the additional indirection\nthey introduce can be leveraged to implement a way of determining if the object is still valid before accessing it.\n\nThis can be useful for a variety of reasons, but basically, any time controlling who owns pointers to the object is not possible\nor too complicated, a handle system could be useful.\n\n```c++\nusing EntityID = Handle\u003cEntity\u003e;\n\nvoid SpawnEntity(const SpawnData\u0026 data)\n{\n    // Create an entity and return a handle to it\n    EntityID id = EntityID::Create(data);\n\n    QueueEntityForProcessing(id);\n}\n\n...\n\n// Executed potentially much later\nvoid ProcessEntity(EntityID id)\n{\n    if (Entity* entity = EntityID::Get(id))\n    {\n        entity-\u003eprocess();\n    }\n    else\n    {\n        // Oh no, the entity was destroyed in the meantime\n        goHaveADrink();\n    }\n}\n```\nA more complete example is available [in the example folder](https://github.com/jlaumon/handle/blob/master/example/main.cpp).\n\n## Under the hood\n\nEach handle is made of a number of bits used as an index in an array (that's where the referenced object is), \nand some bits acting as a version number. If the version number in the handle matches the one in the array at the same index, the object is valid.\nOtherwise it means the object has been destroyed. This technique is also sometimes called generational indices (where the \"generation\" is the version). \n\nNow, that's also pretty much what all the other handle implementations do. Here are the advantages of this one:\n\n### It's not so opaque\n\nThe provided **natvis** files mean that - if you use Visual Studio - you can see the value behind a handle at anytime in the debugger.\n\n![natvis0](https://user-images.githubusercontent.com/2878094/41821247-fba2833a-77dd-11e8-993c-e883f7e146bf.PNG)\n\n### It's resizable AND thread-safe\n\nOne very common limitation of this kind of handle system, is that it needs to allocate the array where the objects\nare stored during initialization. And to be able to resize this array means you have to be sure no one is reading it, \nso it's usually either not thread-safe, or not resizable (or you'd need to lock every time you access any object, but that's not very appealing).\n\nThis implementation uses virtual memory to reserve enough address space to store all the objects you could fit the index bits of the handle,\nbut only commits the memory that you need to store the current number of objects, and can commit more as needed. It never shrinks though.\n\nAccessing an object from a handle is lock-free, it doesn't need any synchronization since growing the array does not move existing objects.\nIt's also very fast since it's just indexing an array.\nCreating/destroying handles does use locks however, but they are short enough.\n\n### It's stongly typed\n\nHandles are not typedefs to integers, they are a class, which is great for type-safety.\n\nThe `Tag` template parameter also means you can have two handles to the same object type behave as different types.\n\n```c++\nusing TextureID = Handle\u003cTexture\u003e;\nusing DebugTextureID = Handle\u003cTexture, Debug\u003e; // provided that Debug is a type\n// Can't pass a TextureID to a function taking a DebugTextureID.\n```\n\n### It's customizable\n\nThe template parameters can be used to choose the type of integer to use, and the number of bits for the index/version \n(although kind of indirectly: `MaxHandles` will influence the number of index bits, and the version will use all the \nremaining bits in the choosen integer type).\n\n```c++\nusing ObjectHandle = Handle\u003cObject, void, uint16_t, 512\u003e;\n// ObjectHandles take 2 bytes (they're uint16_t) and there can be only 512 hanles in flight (which means 9 bits of index and 7 bits of version)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjlaumon%2Fhandle","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjlaumon%2Fhandle","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjlaumon%2Fhandle/lists"}