{"id":22770079,"url":"https://github.com/devolo/linktimeplugin","last_synced_at":"2025-07-11T21:39:20.206Z","repository":{"id":51055307,"uuid":"246538233","full_name":"devolo/linktimeplugin","owner":"devolo","description":"C++11 header-only library for registration and management of link-time plug-ins.","archived":false,"fork":false,"pushed_at":"2021-06-28T09:57:49.000Z","size":14,"stargazers_count":5,"open_issues_count":1,"forks_count":1,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-06-25T19:41:26.355Z","etag":null,"topics":[],"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/devolo.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}},"created_at":"2020-03-11T10:12:31.000Z","updated_at":"2021-06-28T09:57:52.000Z","dependencies_parsed_at":"2022-09-04T04:01:05.783Z","dependency_job_id":null,"html_url":"https://github.com/devolo/linktimeplugin","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/devolo/linktimeplugin","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devolo%2Flinktimeplugin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devolo%2Flinktimeplugin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devolo%2Flinktimeplugin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devolo%2Flinktimeplugin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/devolo","download_url":"https://codeload.github.com/devolo/linktimeplugin/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devolo%2Flinktimeplugin/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264904015,"owners_count":23681138,"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":[],"created_at":"2024-12-11T15:19:23.510Z","updated_at":"2025-07-11T21:39:20.186Z","avatar_url":"https://github.com/devolo.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Link-Time Plug-In Management\n\nC++11 header-only library for registration and management of link-time plug-ins.\n\nHere's a talk that introduces the motivation and implementation of link-time plug-ins: https://youtu.be/BxOiUv6bEqU\n\n## What is it?\n\nIn order to achieve a modular structure, many applications use a plug-in based architecture that cleanly separates the core logic from the implementation of individual drivers/handlers that do the actual work. For example:\n\n* A web server has a plug-in for every URI path.\n* A unit test framework has a plug-in for every test case.\n* A command line application has a plug-in for every subcommand.\n* An image viewer has a plug-in for every image file type.\n* An SNMP agent has a plug-in for every OID.\n\nThere are many possible implementations of such a plug-in architecture:\n\n* Plug-ins may be shared libraries (.so/.dll files) which the application loads\n* Plug-ins may be executables to which the application talks via stdin/stdout\n* Plug-ins may be scripts executed by an interpreter that's built into the application\n\nAll these architectures allow plug-ins to be added or removed dynamically at run-time, without having to rebuild, re-install, or (sometimes) even restart the application. We'll call these *run-time plug-ins*. Run-time plug-ins are not what this repo is about.\n\nThis repo is about *link-time plug-ins*, which are plug-ins that are compiled and linked into the application. Adding or removing plug-ins requires the application to be rebuilt, so there are no run-time dependencies on external files. Link-time plug-ins are as independent and isolated from the application core as run-time plugins:\n\n* Link-time plug-ins can be added to the application simply by adding their .cpp file name to the build configuration (e. g. cmake file).\n* Link-time plug-ins are self-contained within their .cpp files and don't export any implementation details.\n* Link-time plug-ins are registered from within their .cpp file. No external list of plug-ins needs to be maintained.\n* Link-time plug-ins don't know anything about the application that invokes them.\n* The application doesn't know anything about the plug-ins beyond their predefined API.\n* The application can iterate over existing plug-ins and invoke functions in the plug-ins.\n\n## What's the benefit?\n\nIf all plug-ins are linked into the executable, what's the point of having a plug-in architecture in the first place?\n\nThe answer is isolation. Imagine a git-like command line program that executes subcommands like this:\n\n```cpp\nint main(int argc, char** argv) {\n    ...\n\n    if (strcmp(argv[1], \"pull\")==0) {\n        doPull(argc, argv);\n    } else if (strcmp(argv[1], \"commit\")==0) {\n        doCommit(argc, argv);\n    } else if (strcmp(argv[1], \"status\")==0) {\n        doStatus(argc, argv);\n    } else  if (strcmp(argv[1], \"diff\")==0) {\n        doDiff(argc, argv);\n    }\n\n    ...\n}\n```\n\nEvery subcommand (pull, commit, etc.) needs to be implemented (doPull, doCommit, etc.) *and* added to the list in the main function, *and* probably to other lists (e. g. some showUsage function). The list of subcommands is scattered and multiplied throughout the application, making it tedious to add or remove commands.\n\nWith link-time plug-ins, it's much easier because the implementation of subcommands is perfectly isolated from the code that invokes them:\n\n```cpp\nint main(int argc, char** argv) {\n    ...\n\n    for (const auto cmd : linktimeplugin::plugins\u003cCommand\u003e()) {\n        if (strcmp(argv[1], cmd-\u003ename())==0) {\n            cmd-\u003eexecute(argc, argv);\n        }\n    }\n\n    ...\n}\n```\n\nNo list of subcommands needs to be maintained anywhere in the source code. New commands are added simply by implementing their `name()` and `execute()` functions in a class derived from the `Command` base class, hidden in an anonymous namespace somewhere in their own .cpp files. The \"show usage\" function might look like this:\n\n```cpp\nvoid showUsage(char** argv) {\n    std::cout \u003c\u003c \"Usage: \" \u003c\u003c argv[0] \u003c\u003c \" subcommand [ parameter ... ]\\n\";\n\n    for (const auto cmd : linktimeplugin::plugins\u003cCommand\u003e()) {\n        std::cout \u003c\u003c cmd-\u003eusage() \u003c\u003c \"\\n\";\n    }\n}\n```\n\nSo, when you add a new subcommand, it immediately becomes available on the command line (because `main` picks it up), *and* in the usage message (because `showUsage` picks it up), with no need to modify either of the two.\n\n## Same source, multiple executables\n\nSometimes you want to build more than one executable from the same source code, where each executable contains a certain combination of the available plug-ins only. Or maybe you're building on various platforms, and certain plug-ins are available only on some of them (and cannot even be compiled on others).\n\nNote that the \"if-else chain\" solution shown above will work in these cases only with a lot of nasty preprocessor conditionals.\n\nLink-time plug-ins introduce a clean separation between the application core and the plug-in code. The decision which plug-ins go into which executable takes place in the build configuration and need not be replicated anywhere in the source code.\n\n## How it works\n\nAll plug-ins of the same type (=that use the same API) are derived from a common base class. A \"registrar object\" is created for each such plug-in class that creates an instance of the plug-in class in its constructor. This instance is made available through a public template function, which the application uses to iterate over the plug-ins.\n\n## How to use it\n\nCopy `linktimeplugin.hpp` into an include directory of your choice.\n\nDefine a base class for your plug-ins which defines the plug-ins' API as a set of pure virtual functions.\n\nFor every plug-in, derive a class from this base class and implement the API functions. The plug-in class is usually defined in its own .cpp file in an anonymous namespace.\n\nRegister every plug-in with the `REGISTER_PLUGIN` macro (immediately after the plug-in class definition).\n\nIn the application, invoke `linktimeplugin::plugins\u003cBase\u003e()` to retrieve a list of all the plug-ins (where `Base` is the plug-in base class).\n\nYou can have more than one plug-in base class per application, each with its own API and its own set of plug-ins.\n\n## Example\n\nOverview:\n\n```cpp\n// In all files:\n#include \u003clinktimeplugin.hpp\u003e\n\n// In a header file:\nclass PluginBase {\npublic:\n    using Base = PluginBase;\n    virtual void dosomething() = 0;\n};\n\n// In the plug-in cpp file:\nnamespace {\n    class Plugin : public PluginBase {\n        void dosomething() override { ... }\n    };\n    REGISTER_PLUGIN(Plugin);\n}\n\n// In the application:\nfor (const auto plugin : linktimeplugin::plugins\u003cPluginBase\u003e()) {\n    plugin-\u003edosomething();\n}\n\n```\n\nThis repository contains a complete demonstration program. A plug-in base class is defined in `demo.hpp`, and three plug-ins are defined in `demo-cat.cpp`, `demo-dog.cpp`, and `demo-bird.cpp`. Note that these three files don't export any public symbols (everything is inside an anonymous namespace). A single main function (`demo-main.cpp`) is used with various combinations of these plug-ins to build three demo programs (`demo1`, `demo2`, and `demo3`). The selection which demo program contains which plug-in takes place in the build configuration (`CMakeLists.txt`).\n\nTo build and run the example programs:\n\n```\n$ mkdir build\n$ cd build\n$ cmake ..\n$ make\n$ ./demo1\n$ ./demo2\n$ ./demo3\n```\n\n---\n*Wolfram Rösler • wolfram@roesler-ac.de • https://gitlab.com/wolframroesler • https://twitter.com/wolframroesler • https://www.linkedin.com/in/wolframroesler/*\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevolo%2Flinktimeplugin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdevolo%2Flinktimeplugin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevolo%2Flinktimeplugin/lists"}