{"id":16822188,"url":"https://github.com/mborgerson/genconfig","last_synced_at":"2025-04-11T03:11:22.032Z","repository":{"id":39799557,"uuid":"483411863","full_name":"mborgerson/genconfig","owner":"mborgerson","description":"Automatic runtime configuration / settings system for your C/C++ application. Load from TOML configs into a generated C struct and back again with minimal effort.","archived":false,"fork":false,"pushed_at":"2025-01-06T22:56:02.000Z","size":259,"stargazers_count":23,"open_issues_count":1,"forks_count":6,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-11T03:11:19.242Z","etag":null,"topics":["c","configuration","cpp","settings","toml"],"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/mborgerson.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2022-04-19T21:14:45.000Z","updated_at":"2025-01-06T22:56:06.000Z","dependencies_parsed_at":"2022-07-31T23:48:36.116Z","dependency_job_id":"cdbdf4f4-e256-433f-a31e-afdbef4f8c45","html_url":"https://github.com/mborgerson/genconfig","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/mborgerson%2Fgenconfig","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mborgerson%2Fgenconfig/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mborgerson%2Fgenconfig/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mborgerson%2Fgenconfig/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mborgerson","download_url":"https://codeload.github.com/mborgerson/genconfig/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248333604,"owners_count":21086200,"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":["c","configuration","cpp","settings","toml"],"created_at":"2024-10-13T11:02:00.205Z","updated_at":"2025-04-11T03:11:22.005Z","avatar_url":"https://github.com/mborgerson.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"GenConfig\n=========\nAims to provide less-painful C/C++ app configuration support.\n\n![](infographic.png)\n\nMotivation\n----------\nCreated because all of the config systems I have used (or created) in the past had too many pain points, like:\n- Requiring things to be defined in multiple places\n- Not using standard, human-friendly configuration formats\n- Lacking validation of basic types and structure\n- Not supporting hierarchical data structures, arrays, strings very well\n- Requiring tedious structure maintenance\n- Requiring accessor calls to get at simple data fields\n- Not supporting configuration deltas\n\nThis system attempts to alleviate those pain points and make configuration suck less:\n- Automatically generates structure definition code from an input specification, which is easy to write\n- Loads application configuration from [TOML](https://toml.io/en/) format, which is standardized and human-friendly\n- Validates user configuration matches expected structure and fields are of expected type\n- Support hierarchical structures of basic types: `bool`, `int`, `float`, `enum`, strings (UTF-8), and arrays\n- Minimizes development effort: just define the setting once in a spec file and the rest is handled\n- Usable in C/C++, without having to do any lookups or extra validation: just read from a `struct`\n- Supports saving configuration deltas\n\nHow it Works\n------------\n- You define the options you want for your app in a spec file\n- You run `gen_config.py` to generate a header file with `struct config` definition\n- User runs your app, providing their settings in a `.toml` file matching your specification\n\t- Your code calls a function to load, parse, and store the config file to the `config` structure\n\t- Your code accesses the config by just reading from and writing to the `config` struct\n\t- Your code changes `config` structure in response to some user action\n\t- Your code calls a function to save the config delta to the user's `.toml` config file\n- You retain a bit more hair that you might have ripped out using/creating another configuration system\n- Your app makes lots of money and you send some my way because you support open-source development. Thanks, that's nice of you.\n\nRequirements\n------------\n- [toml++](https://marzer.github.io/tomlplusplus/), which is used to parse config file\n- Compiler that supports C++17\n\nExample\n-------\nThis is the specification file that defines your config options:\n\n```yaml\ncompany:\n  name: string\n  headquarters:\n    state: string\n    city: string\n\n  products:\n    type: array\n    items:\n      name:\n        type: string\n        default: New Product\n      price: float\n      inventory: int\n      international_shipping:\n        type: bool\n        default: true\n      suppliers:\n        type: array\n        items: string\n      category:\n        type: enum\n        values: ['fruit', 'vegetable', 'beverage', 'explosive']\n```\n\nAfter running `gen_config.py`, this C code is generated automatically.\n\n```C\nenum CONFIG_COMPANY_PRODUCTS_CATEGORY {\n\tCONFIG_COMPANY_PRODUCTS_CATEGORY_FRUIT,\n\tCONFIG_COMPANY_PRODUCTS_CATEGORY_VEGETABLE,\n\tCONFIG_COMPANY_PRODUCTS_CATEGORY_BEVERAGE,\n\tCONFIG_COMPANY_PRODUCTS_CATEGORY_EXPLOSIVE,\n\tCONFIG_COMPANY_PRODUCTS_CATEGORY__COUNT\n};\n\nstruct config {\n  struct company {\n    const char *name;\n    struct headquarters {\n      const char *state;\n      const char *city;\n    } headquarters;\n    struct products {\n      const char *name;\n      float price;\n      int inventory;\n      bool international_shipping;\n      const char **suppliers;\n      unsigned int suppliers_count;\n      enum CONFIG_COMPANY_PRODUCTS_CATEGORY category;\n    } *products;\n    unsigned int products_count;\n  } company;\n};\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e\nAdditionally, a corresponding `CNode` tree is created, that's used to support everything:\n\u003c/summary\u003e\n\n```cpp\nCNode config_tree =\n ctab(\"config\", {\n  ctab(\"company\", {\n   cstring(\n    offsetof(struct config, company.name),\n    \"name\", \"\"),\n   ctab(\"headquarters\", {\n    cstring(\n     offsetof(struct config, company.headquarters.state),\n     \"state\", \"\"),\n    cstring(\n     offsetof(struct config, company.headquarters.city),\n     \"city\", \"\")\n   }),\n   carray(\n    offsetof(struct config, company.products),\n    offsetof(struct config, company.products_count),\n    sizeof(struct config::company::products),\n    \"products\",\n    ctab(\"\", {\n     cstring(\n      offsetof(struct config::company::products, name),\n      \"name\", \"New Product\"),\n     cnumber(\n      offsetof(struct config::company::products, price),\n      \"price\", 0.0),\n     cinteger(\n      offsetof(struct config::company::products, inventory),\n      \"inventory\", 0),\n     cbool(\n      offsetof(struct config::company::products, international_shipping),\n      \"international_shipping\", true),\n     carray(\n      offsetof(struct config::company::products, suppliers),\n      offsetof(struct config::company::products, suppliers_count),\n      sizeof(((struct config::company::products *){0})-\u003esuppliers[0]),\n      \"suppliers\",\n      cstring(\n       0, \"\", \"\")\n     ),\n     cenum(\n      offsetof(struct config::company::products, category),\n      \"category\", {\"fruit\", \"vegetable\", \"beverage\", \"explosive\"}, \"fruit\")\n    })\n   )\n  })\n });\n```\n\n\u003c/details\u003e\n\nConfig file to be loaded at runtime, in [TOML](https://toml.io/en/) format:\n\n```toml\n[company]\n\tname = 'Acme Corp'\n\tproducts = [\n\t\t{ name = 'Apple', price = 1.2, inventory = 100, suppliers = ['Midwest Orchard', 'Tasty Apples Inc.'], category = 'fruit' },\n\t\t{ name = 'TNT', price = 50, inventory = 1000, category = 'explosive', international_shipping = false },\n\t\t]\n\n[company.headquarters]\n\tcity = 'Phoenix'\n\tstate = 'Arizona'\n```\n\nLoading the config basically looks like:\n\n```cpp\n#include \u003ctoml.hpp\u003e // Required for basic parsing\n#include \u003ccnode.h\u003e // Required data structure for config mgmt\n\n#define DEFINE_CONFIG_TREE\n#include \"config.h\"\n\n// Load config from user file\nstruct config s;\nauto toml_table = toml::parse_file(\"config.toml\");\nconfig_tree.update_from_table(toml_table);\nconfig_tree.store_to_struct(\u0026s);\n```\n\nThen when you are ready to save the config again:\n\n```cpp\n// Update config tree from structure if you have modified it\nconfig_tree.update_from_struct(\u0026s);\n\n// Save config\nFILE *f = fopen(\"config.toml\", \"wb\");\nfprintf(f, \"%s\", config_tree.generate_delta_toml().c_str());\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmborgerson%2Fgenconfig","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmborgerson%2Fgenconfig","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmborgerson%2Fgenconfig/lists"}