{"id":13418146,"url":"https://github.com/matt-42/iod","last_synced_at":"2025-04-04T16:16:24.741Z","repository":{"id":16772308,"uuid":"19530433","full_name":"matt-42/iod","owner":"matt-42","description":"Meta programming utilities for C++14. Merged in matt-42/lithium","archived":false,"fork":false,"pushed_at":"2019-12-07T13:32:46.000Z","size":390,"stargazers_count":726,"open_issues_count":7,"forks_count":59,"subscribers_count":48,"default_branch":"master","last_synced_at":"2025-03-28T15:10:06.134Z","etag":null,"topics":["c-plus-plus","deserialization","meta-programming","serialization"],"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/matt-42.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":"2014-05-07T10:32:43.000Z","updated_at":"2025-03-09T08:44:41.000Z","dependencies_parsed_at":"2022-08-25T11:10:49.113Z","dependency_job_id":null,"html_url":"https://github.com/matt-42/iod","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matt-42%2Fiod","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matt-42%2Fiod/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matt-42%2Fiod/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/matt-42%2Fiod/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/matt-42","download_url":"https://codeload.github.com/matt-42/iod/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247208183,"owners_count":20901570,"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-plus-plus","deserialization","meta-programming","serialization"],"created_at":"2024-07-30T22:00:58.969Z","updated_at":"2025-04-04T16:16:24.720Z","avatar_url":"https://github.com/matt-42.png","language":"C++","readme":"[![Travis build status](https://travis-ci.org/matt-42/iod.svg)](https://travis-ci.org/matt-42/iod)\n\nImportant Note\n=======================\n\nThis project has been refactored and renamed as the [Lithium Libraries](https://github.com/matt-42/lithium):\nhttps://github.com/matt-42/lithium\n\nThe IOD Library\n==========================\n\n\nThe IOD library enhances C++14 meta programming with a symbol based\nparadigm. It provides a compile-time way to introspect objects and\ngenerate code matching their data structures. It also contains few\nutilities built with symbol meta-programming.\n\n## What is a IOD symbol?\n\nSymbols are at the core of the IOD paradigm. They add to C++ a missing\npowerful feature: The way to statically store in a variable the\naccess to an object member, the call to a method, and the access to the\nstring representing the name of this variable. Here is the most simple\nbut powerful operator that is now possible with IOD symbols:\n\n```c++\n#include \u003ciostream\u003e\n#include \u003ciod/symbol.hh\u003e\n\niod_define_symbol(a); // Refer to members and methods a with symbol _a\niod_define_symbol(b); // Refer to members and methods b with symbol _b\niod_define_symbol(c); // Refer to members and methods c with symbol _c\n\nint main() {\n\n  // Symbols are located in namespace s to avoid name clash.\n  using s;\n\n  auto print_member = [] (auto\u0026 obj, auto\u0026 m)\n                      {\n                        std::cout \u003c\u003c \"obj.\" \u003c\u003c m.name()\n                                  \u003c\u003c \" == \" \u003c\u003c m.member_access(obj) \u003c\u003c std::endl;\n                      };\n\n  struct { int a; int b; int c; } obj{21, 42, 84};\n  print_member(obj, _a); // Prints \"obj.a == 21\"\n  print_member(obj, _b); // Prints \"obj.b == 42\"\n  print_member(obj, _c); // Prints \"obj.c == 84\"\n}\n```\n\nWithout symbols (or other similar constructs), it is not possible to\nwrite such a generic print_member function. Without, one would have to\nwrite the three version accessing the three different members.\n\nBy convention all the symbols start with the _[lowercase character]. And\nto avoid multiple definitions, guards should be used such as in the\nfollowing:\n\n```c++\n#ifndef IOD_SYMBOL_mysymbol\n#define IOD_SYMBOL_mysymbol\n  iod_define_symbol(mysymbol);\n#endif\n}\n```\n\nThe script ```tools/generate_symbol_definitions.sh``` automates this\nprocess by converting a file containing a list of symbols into a valid\nC++ header containing the symbol definitions.\n\n## Statically introspectable objects (SIO)\n\nStatically introspectable objects features are:\n\n  - The ability to instantiate C++ objects without having to declare any struct or class.\n  - Zero cost static introspection (i.e. without overhead on execution time or memory) on those objects.\n\n```c++\n  // Define an object\n  auto o = D(_name = \"John\", _age = 42, _city = \"NYC\");\n  // Direct access to its members.\n  assert(o.name == \"John\" \u0026\u0026 o.age == 42 \u0026\u0026 o.city == \"NYC\");\n\n  // Static Introspection. It has no execution cost since these function\n  // are computed at compile time.\n  assert(o.has(_name) == true);\n  assert(o.has(_firstName) == false);\n  assert(o.has(_firstName) == false);\n  assert(o.size() == 3);\n\n  // Output the structure of the object to std::cout:\n  // name:John\n  // age:42\n  // city:NYC\n  foreach(o) | [] (auto\u0026 m) { std::cout \u003c\u003c m.symbol().name() \u003c\u003c \":\"\n                                        \u003c\u003c m.value() \u003c\u003c std::end; }\n\n```\n## Command line parser.\n\nIod provides an easy to use, type safe command line parser :\n\n```c++\nconst char* argv[] = {\"\", \"--opt1\" , \"12\", \"--opt2\", \"abc\"};\nint argc = 5;\nauto opts = parse_command_line(argc, argv,\n                               _opt1 = int(),\n                               _opt2 = std::string());\n// With\n// ./a.out --opt1 12 --opt2 abc\n// It should give\n// opts.opt1 == 12\n// opts.opt2 == \"abc\"\n```\n\nMore example can be found in the test here:\nhttps://github.com/matt-42/iod/blob/master/tests/parse_command_line.cc\n\n## A Fast, Malloc-Free Json Parser / Encoder.\n\nAs of today, all json parsers rely upon dynamic data structures to\nparse and store the json objects. Even if some good parser reduces\ndramatically the amount of dynamic memory allocation, they still\nsuffer from the dynamic paradigm: A single function has to handle the\nwhole set of possible json objects. This comes to the absurd situation\nwhere a program handling a small set specific json object types\nhave to use a json parser handling any kind of objects.\n\nThe iod library implements the opposite approach: Using\nmeta-programming and introspection, one tiny specialized json parser\nis generated for each SIO object type so it involves no dynamic memory\nallocation and very few conditional branching. It directly fills the\nobject member according to their type without having to store the\nobject in an intermediate hash map.\n\nThis makes its performances impossible to match in other languages\nsuch as C or Java that do not provide static introspection. The\nencoder still needs optimizations, and the parser is currently from\n1.3x to 2.3x faster than the RapidJSON library without explicit use of\nSIMD vector instructions.\n\nThe only limitation of this design is when using a very large type set\nof json objects, the total code size of the generated parsers will be\nbigger than a generic dynamic parser.\n\nNote on memory allocation: While the parser does not allocate any\nintermediate structure, it allocates the destination object's\nmembers if they are of type std::vector or std::string.\n\n```c++\n\n// The type of the object contains attributes related to its json\n// representation such as alternative json keys and whether or not a\n// field should not be included in the json representation.\ntypedef decltype(D(_name(_json_key = _username) = std::string(),\n                   _age(_json_skip) = int(),\n                   _city = std::string())) User;\n  \nUser u(\"John\", 23, \"NYC\");\n\n// Encode to a json string.\nauto str = json_encode(u);\nassert(str == R\"({\"username\":\"John\",\"city\":\"NYC\"})\");\n\n// Decode a json string representing a user.\nUser u2; json_decode(u2, str);\nassert(u2.name == \"John\" and u2.city == \"NYC\");\n\n// The json throw exceptions when some value type mismatch\n// the object or when some fields are missing:\n\njson_decode(u2, R\"({\"username\":\"John\"})\");\n// Exception: json_decode error: missing field city.\n\njson_decode(u2, R\"({\"username\":\"John\",\"city\":22})\");\n//  Exception:\n//  Json parse error near name\":\"John\",\"city\":22\n                                             ^^^\n//  Expected \" got 2\n\n```\n\n\n## Named Optional Function Arguments\n\nIn classic C++, you would define a function taking optional arguments as :\n\n```c++\nvoid fun(int mandatory_arg, int optional_arg1 = 1, int optional_arg2 = 12, int optional_arg3 = 32);\n```\n\nThis has two drawbacks: First, it is not practical if the user needs to\nset the third optional argument, and oblige him to remember the place\nof each argument. SIOs are a good alternative since they solve both issues:\n\n```c++\ntemplate \u003ctypename... O\u003e\nvoid fun(int mandatory_arg, const O\u0026... opts)\n{\n  const auto options = D(opts...);\n  int optional_arg1 = options.get(_optional_arg1, 1); // return options.optional_arg1 or 1 if not set.\n  int optional_arg2 = options.get(_optional_arg2, 12);\n  int optional_arg3 = options.get(_optional_arg3, 32);\n}\n\nfun(1, _optional_arg3 = 2); // Set the thirds argument and leave the two others to their default value.\n```\n\n## Foreach for tuple and SIO\n\nWhile C++11 introduce range based for loops for a container such as\nstd::vector, it does not provide a way to iterate on\ntuples. ```foreach``` is a powerful primitive for processing tuples\nas well as SIOs.\n\n```c++\nauto my_tuple = std::make_tuple(1, \"test\", 34.f);\n// Prints 1 test 34.f.\nforeach(my_tuple) | [] (auto\u0026 e) { std::cout \u003c\u003c e \u003c\u003c \" \"; }\n\nauto my_sio = D(_name = \"John\", _age = 42);\n// Prints name John age 42\nforeach(my_sio) | [] (auto\u0026 m) { std::cout \u003c\u003c m.symbol().name() \u003c\u003c \" \" \u003c\u003c m.value() \u003c\u003c \" \"; }\n```\n\n```foreach``` also allows to iterate on several object of the same length (but different types):\n\n```c++\nauto t1 = std::make_tuple(1, \"test\", 34.f);\nauto t2 = std::make_tuple(\"x\", 34.f, \"y\");\n// Prints 1 xxx test 34 34 y\nforeach(t1, t2) | [] (auto\u0026 a, auto\u0026 b) { std::cout \u003c\u003c a \u003c\u003c \" \" \u003c\u003c b \u003c\u003c \" \"; }\n```\n\nFurthermore, it automatically builds a new tuple if the given function\nreturns a non void value:\n```c++\nauto t1 = std::make_tuple(1, \"A\", 34.f);\nauto t2 = std::make_tuple(2, \"B\", 2);\nauto t3 = foreach(t1, t2) | [] (auto\u0026 a, auto\u0026 b) { return a + b; };\n// t3 == \u003c3, \"AB\", 36\u003e\n```\n\n## Apply tuples and SIOs to functions\n\nThe ```apply``` primitive map elements of a tuple or SIO to a given\nfunction.\n\n```c++\nint fun(int x, float y) { return x + y; }\n\nauto tuple = std::make_tuple(1, 2.f);\nint res = apply(tuple, fun);\n// res == 3\n```\n\n\n## C++ Embedded Domain Specific Languages Framework\n\nThe IOD library provides a set of tools to ease the embedding of an\nembedded domain specific languages (EDSL) framework. It includes an\nabstract syntax tree (AST) with symbols and values as terminals. Here\nis an example of a simple expression:\n\n```c++\nauto exp = _a(23) + _b;\n```\n\nThis code does nothing except computing the AST type and storing the\nvalue 23.  ```exp``` has to be evaluated to actually compute\nsomething. IOD provides three handy primitives to traverse ASTs:\nexp_map_reduce, exp_transform, exp_tranform_iterate. More\ndocumentation will come later. In the meantime, you can check how the\nVideo++ library implements its image processing C++ EDSL:\nhttps://github.com/matt-42/vpp/blob/master/vpp/core/liie.hh\n\n\n## Language Integrated Queries\n\nTo demonstrate the power of the IOD framework, we embedded an\nimplementation of a subset of the SQL language in the C++ language.\nIt handles SELECT, FROM, WHERE, INNER_JOIN (with the ON criteria),\nORDER BY, GROUP BY, and aggregates such as average or sum.\n\nGiven two collection:\n\n```c++\nstd::vector\u003cdecltype(D(_name = std::string(), _age() = int(), _city_id = int()))\u003e persons;\nstd::vector\u003cdecltype(D(_id = int(), _name() = std::string(), _zipcode = int()))\u003e cities;\n```\n\nThe following requests are valid:\n\n```c++\n// SELECT * from persons;\nlinq.select().from(persons) | [] (auto\u0026 p) { std::cout \u003c\u003c p.name \u003c\u003c std::endl; }\n```\n\n```c++\n// SELECT myname = name from persons WHERE age \u003e 42;\nlinq.select(_myname = _name).from(persons).where(_age \u003e 42) |\n  [] (auto\u0026 p) { std::cout \u003c\u003c p.myname \u003c\u003c std::endl; }\n```\n\n```c++\n// SELECT name = person.name, city = city.name\n//        FROM persons as person\n//        INNER JOIN cities as city\n//        ON person.city_id == city.id\nlinq.select(_name = _person[_name], _city = _city[_name])\n    .from(persons, _as(_person))\n    .inner_join(cities, _as(_city),\n              _on(_city[_cp] == _person[_cp])) |\n  [] (auto\u0026 p) { std::cout \u003c\u003c p.name \u003c\u003c \" lives in \" \u003c\u003c p.city \u003c\u003c std::endl; }\n```\n\n```c++\n// SELECT age = avg(age), city_id = city_id\n//        FROM persons\n//        GROUP BY city_id\n\nlinq.select(_age = _avg(_age), _city_id = _city_id)\n    .from(persons)\n    .group_by(_city_id) |\n  [] (auto\u0026 p) { std::cout \u003c\u003c p.age \u003c\u003c \" is the average age in city \" \u003c\u003c p.city_id \u003c\u003c std::endl; }\n```\n\n## Compilers support\n\nIOD relies on the C++14 standard. It has been successfully compiled with :\n  - GCC 4.9\n  - Clang 3.4\n\n## Contributing\n\nContributions or suggestions are welcome. Do not hesitate to fill issues, send pull\nrequests, or discuss by email at matthieu.garrigues@gmail.com.\n","funding_links":[],"categories":["TODO scan for Android support in followings","C++","Libraries"],"sub_categories":["Misc"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmatt-42%2Fiod","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmatt-42%2Fiod","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmatt-42%2Fiod/lists"}