{"id":18594070,"url":"https://github.com/lucretia/test_binding","last_synced_at":"2026-04-29T01:02:47.316Z","repository":{"id":146215941,"uuid":"128235597","full_name":"Lucretia/test_binding","owner":"Lucretia","description":"Testing binding to C++ from Ada 2012","archived":false,"fork":false,"pushed_at":"2018-04-05T17:55:35.000Z","size":15,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-05-29T09:02:14.609Z","etag":null,"topics":["ada2012","bindings","cplusplus","example"],"latest_commit_sha":null,"homepage":null,"language":"Ada","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Lucretia.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-04-05T16:45:54.000Z","updated_at":"2022-07-26T19:24:03.000Z","dependencies_parsed_at":null,"dependency_job_id":"a9c8028e-0655-4dd4-ae82-f4c3a7286b84","html_url":"https://github.com/Lucretia/test_binding","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Lucretia/test_binding","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lucretia%2Ftest_binding","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lucretia%2Ftest_binding/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lucretia%2Ftest_binding/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lucretia%2Ftest_binding/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Lucretia","download_url":"https://codeload.github.com/Lucretia/test_binding/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lucretia%2Ftest_binding/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32405904,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-28T19:38:08.556Z","status":"ssl_error","status_checked_at":"2026-04-28T19:37:55.688Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["ada2012","bindings","cplusplus","example"],"created_at":"2024-11-07T01:14:27.505Z","updated_at":"2026-04-29T01:02:47.301Z","avatar_url":"https://github.com/Lucretia.png","language":"Ada","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Binding\n\nThis small project is a demonstration on how you can bind to C++ classes using C wrappers from Ada.\n\nThe source is organised as follows:\n\n* An untouchable library that is stored in ```src/cpp/untouchable/```, this represents a library you cannot modify, but\n  you want to use from Ada.\n* A C and C++ binding layer which exports a set of C functions which is stored in ```src/cpp/binding/```, this is the\n  interface to the Ada language.\n* An Ada binding which maps Ada tagged types and interfaces onto the C++ class hierarchy, stored in\n  ```src/ada/binding/```.\n* A test program stored in ```src/ada/```.\n\n## Virtuals\n\nThere are two cases we need to map onto when binding to C++:\n\n1. The library you are binding has been passed an Ada object and the library calls a virtual method on this objects\n   bound to C++ instantiation, i.e. Button-\u003eDraw() =\u003e ends up calling Button.Draw in Ada.\n2. The Ada user code overrides a C++ virtual and then wants to call the base class' version of that method.\n\nWe cannot instantiate anything from a virtual base. In Ada we would have an abstract or interface type to map\nonto the C++ virtual Base class. We need a proxy class to sit between C++ and Ada which will call the Ada equivalent\nof DoOne() and DoTwo(). If We have an Ada derived type and the C++ code side calls DoOne(), it needs to call the\ncorrect Ada primitive. The \"class\" hierarchy looks like the following:\n\n```\nSome_Derived (Application - Ada)\n      |\n    Base     (Binding - Ada)\n      |\n   B_Base    (Binding - C/C++)\n      |\n    Base     (Untouchable - C++ class)\n```\n\nWe can use the idea of a \"thunk\" which is a pointer to a function. This can be implemented as a static constant\npointer to a function in the C/C++ binding, which is initialised at link-time to an Ada subprogram which takes a\nclass-wide parameter, i.e. \"Self : access Base'Class\" which will then dispatch to the correct Ada primitive in the\ntype hierarchy.\n\n[1] Here is a function call sequence diagram to show this thunk in action, to make things clearer.\n\n```\n (C++ class -\n  dispatches via vtable)\n  1. base_ptr-\u003eDoOne()    (C/C++ binding)\n| -------------------\u003e | 2. B_Base::DoOne()         (Ada binding)\n|                      | -----------------\u003e | 3. B_Base::ThunkPtr_DoOne   (Ada binding - dispatches)\n|                      |                    | ------------------------\u003e | 4. Base'Class(Self.Do_One)  (Ada derived type)\n|                      |                    |                           | -------------------------\u003e | 5. Derived.Do_One\n|                      |                    |                           |                            |                   |\n|                      |                    |                           |                            | \u003c---------------- |\n|                      |                    |                           | \u003c------------------------- |\n|                      |                    | \u003c------------------------ |\n|                      | \u003c----------------- |\n| \u003c------------------- |\n```\n\nThis is quite long-winded and unfortunately, the nature of wrapping C++ in another language. Ideally, direct\nbinding to C++ would be the best way forward, but this is not an option right now due to bugs in the compiler.\n\nDue to this thunk calling into the Ada code, the C++ class needs to be able to say which Ada object it is calling\ninto. Therefore, the Ada binding needs to supply an address of the Ada object to the C/C++ layer when it is created.\n\nFrom the Ada side, there is a case where the primitive, Base.Do_Two, should call the actual Base::DoTwo() when it is\ncalled without any vtable dispatching from the C/C++ side of the binding. If there was vtable dispatching, this would\nthen call into the Ada side again and cause and infinite call loop and stack overflow, which is not what we want.\n\nFor C++ virtuals which are not pure, we need two C function calls to bind to, a dispatching function and a\nnon-dispatching function. The dispatching case is the same case as that above in [1], the non-dispatching case is\nas follows in [2] and is used for calling back down to the C++ base class.\n\n[2]\n\n```\n  (Ada derived type)\n  1. Derived.Do_Two     (C/C++ binding)\n| ----------------\u003e | 2. Base_DoTwo(self)        (C/C++ binding)\n|                   | ------------------\u003e | 3. ((Base *)self)-\u003eDoTwo()\n|                   |                     |                            |\n|                   |                     | \u003c------------------------- |\n|                   | \u003c------------------ |\n| \u003c---------------- |\n```\n\nIn the case where the very base object has a virtual which does something, in this case Base::DoTwo(), if from the\nAda side the user were to call Base (Object).Do_Two, the actual Base::DoTwo() needs to be called. This is usually\ndone from within Derived.Do_Two as an extra step to make sure the base has been called.\n\n```\n  (Ada derived type)\n  1. Derived.Do_Two    (Ada binding)\n| ----------------\u003e | 2. Base.Do_Two   (C/C++ binding)\n|                   | -------------\u003e | 3. Base_DoTwo()     (C++ binding)\n|                   |                | --------------\u003e | 4. B_Base::DoTwo()       (C++ class)\n|                   |                |                 | -----------------\u003e | 5. Base::DoTwo()\n|                   |                |                 |                    |\n|                   |                |                 | \u003c----------------- |\n|                   |                | \u003c-------------- |\n|                   | \u003c------------- |\n| \u003c---------------- |\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flucretia%2Ftest_binding","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flucretia%2Ftest_binding","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flucretia%2Ftest_binding/lists"}