{"id":13419983,"url":"https://github.com/pfultz2/Tick","last_synced_at":"2025-03-15T06:31:07.599Z","repository":{"id":17228448,"uuid":"19997405","full_name":"pfultz2/Tick","owner":"pfultz2","description":"Trait introspection and concept creator for C++11","archived":false,"fork":false,"pushed_at":"2017-12-04T15:51:47.000Z","size":609,"stargazers_count":183,"open_issues_count":7,"forks_count":18,"subscribers_count":23,"default_branch":"master","last_synced_at":"2024-07-31T22:52:51.114Z","etag":null,"topics":["c-plus-plus","c-plus-plus-11","concepts","cpp","cpp11","cpp14","modern"],"latest_commit_sha":null,"homepage":"http://tickcpp.readthedocs.io/en/latest/doc/","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pfultz2.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-20T21:01:07.000Z","updated_at":"2024-07-03T22:37:01.000Z","dependencies_parsed_at":"2022-09-01T16:04:54.693Z","dependency_job_id":null,"html_url":"https://github.com/pfultz2/Tick","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/pfultz2%2FTick","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pfultz2%2FTick/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pfultz2%2FTick/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pfultz2%2FTick/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pfultz2","download_url":"https://codeload.github.com/pfultz2/Tick/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221548227,"owners_count":16840981,"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","c-plus-plus-11","concepts","cpp","cpp11","cpp14","modern"],"created_at":"2024-07-30T22:01:23.857Z","updated_at":"2025-03-15T06:31:07.594Z","avatar_url":"https://github.com/pfultz2.png","language":"C++","readme":"Tick\n====\n\nTrait introspection and concept creator for C++11\n\nGetting Started\n===============\n\nTick provides a mechanism for easily defining and using traits in C++11. For example, if we defined a generic `increment` function, like this:\n```cpp\ntemplate\u003cclass T\u003e\nvoid increment(T\u0026 x)\n{\n    x++;\n}\n```\nIf we pass something that does not have the `++` operator to `increment`, we will get an error inside of the `increment` function. This can make it unclear whether the error is due to a mistake by the user of the function or by the implementor of the function. Instead we want to check the type requirements of the function. \n\nUsing Tick we can create an `is_incrementable` trait, like this:\n```cpp\nTICK_TRAIT(is_incrementable)\n{\n    template\u003cclass T\u003e\n    auto require(T\u0026\u0026 x) -\u003e valid\u003c\n        decltype(x++),\n        decltype(++x)\n    \u003e;\n};\n```\nAnd then we can use a simple requires clause in our function to check the type requirements:\n```cpp\ntemplate\u003cclass T, TICK_REQUIRES(is_incrementable\u003cT\u003e())\u003e\nvoid increment(T\u0026 x)\n{\n    x++;\n}\n```\nSo, now, if we pass something that is not incrementable to `increment`:\n```cpp\nstruct foo {};\n\nfoo f;\nincrement(f);\n```\nThen we get an error like this in clang:\n\n    demo.cpp:25:2: error: no matching function for call to 'increment'\n            increment(f);\n            ^~~~~~~~~\n    demo.cpp:14:19: note: candidate template ignored: disabled by 'enable_if' [with T = foo]\n    template\u003cclass T, TICK_REQUIRES(is_incrementable\u003cT\u003e())\u003e\n                      ^\n\nThis gives an error at the call to `increment` rather than inside the function, and then pointes to the type requirements of the function. This gives enough information for most commons cases, however, sometimes we may want more information. In that case the `TICK_TRAIT_CHECK` can be used. For example, say we had the `is_incrementable` trait defined like this:\n```cpp\nTICK_TRAIT(is_incrementable, std::is_integral\u003c_\u003e)\n{\n    template\u003cclass T\u003e\n    auto require(T\u0026\u0026 x) -\u003e valid\u003c\n        decltype(x++),\n        decltype(++x)\n    \u003e;\n};\n```\nThen if we use `TICK_TRAIT_CHECK`, we can see why `int*` is not incrementable:\n```cpp\nTICK_TRAIT_CHECK(is_incrementable\u003cint*\u003e);\n```\nWhich will produce this error:\n\n    ../tick/trait_check.h:95:38: error: implicit instantiation of undefined template 'tick::TRAIT_CHECK_FAILURE\u003cstd::is_integral\u003cint *\u003e, is_incrementable\u003cint *\u003e \u003e'\n\nWhich shows the traits that failed including any refinements. So we can see that it failed because `std::is_integral\u003cint *\u003e` is not true.\n\nBuilding traits\n===============\n\nThis macro will build a boolean type trait for you. Each trait has a `require` member function of the form:\n```cpp\nTICK_TRAIT(my_trait)\n{\n    template\u003cclass T\u003e\n    auto require(T\u0026\u0026 x) -\u003e valid\u003c\n        ...\n    \u003e;\n};\n```\n\nThis will essentially build a class that inherits from `integral_constant`, so the above is equivalent to this:\n\n```cpp\ntemplate\u003cclass... Ts\u003e\nstruct my_trait\n: integral_constant\u003cbool, (...)\u003e\n{};\n```\nThe parameters to the trait are based on the parameters passed to the `require` function. \n\nThe trait will be either true or false if the expressions given are valid. Each expression in `valid` needs a `decltype` around it. If one of the expressions is not valid, the the trait will return false. For example,\n```cpp\nTICK_TRAIT(my_trait)\n{\n    template\u003cclass T\u003e\n    auto require(T\u0026\u0026 x) -\u003e valid\u003c\n        decltype(x++)\n    \u003e;\n};\n```\nThe trait above will check that `x++` is a valid expression.\n\nRefinements\n-----------\n\nRefinements can be expressed after the name. Each refinement is a [placeholder expression](http://www.boost.org/doc/libs/1_55_0/libs/mpl/doc/refmanual/placeholder-expression.html), where each placeholder(ie `_1`, `_2`, etc) are replaced by their corresponding type passed into the trait. In the case of traits that accept a single parameter the unnamed placeholder(`_`) can be used, for example:\n```cpp\nTICK_TRAIT(is_incrementable, std::is_default_constructible\u003c_\u003e)\n{\n    template\u003cclass T\u003e\n    auto require(T\u0026\u0026 x) -\u003e valid\u003c\n        decltype(x++),\n        decltype(++x)\n    \u003e;\n};\n```\nThis trait will be true when `x++` and `++x` are valid expressions and `x` is default constructible.\n\nWhen a trait has multiple parameters, its best to use named placeholders. For example:\n```cpp\nTICK_TRAIT(is_equality_comparable, \n    std::is_default_constructible\u003c_1\u003e, \n    std::is_default_constructible\u003c_2\u003e)\n{\n    template\u003cclass T, class U\u003e\n    auto require(T\u0026\u0026 x, U\u0026\u0026 y) -\u003e valid\u003c\n        decltype(x == y),\n        decltype(x != y)\n    \u003e;\n};\n```\nThis trait will be true when `x == y` and `x != y` are valid expressions and both `x` and `y` are default constructible.\n\nIn addition `quote` can be used to pass all the args from the trait to the refinement:\n\n```cpp\nTICK_TRAIT(is_comparable, \n    quote\u003cis_equality_comparable\u003e)\n{\n    template\u003cclass T, class U\u003e\n    auto require(T\u0026\u0026 x, U\u0026\u0026 y) -\u003e valid\u003c\n        decltype(x \u003c y),\n        decltype(x \u003c= y),\n        decltype(x \u003e= y),\n        decltype(x \u003e y)\n    \u003e;\n};\n```\n\nQuery operations\n================\n\nThese can be used to query more information about the types then just valid expressions.\n\nType matching\n-------------\n\nWhen a type is matched, it can either be convertible to the type given, or the evaluated placeholder expression must be true. Placeholder expressions can be given so the type can be matched against other traits.\n\n\nreturns\n-------\n\nThe `returns` query can check if the result of the expressions matches the type. For example,\n```cpp\nTICK_TRAIT(is_incrementable)\n{\n    template\u003cclass T\u003e\n    auto require(T\u0026\u0026 x) -\u003e valid\u003c\n        decltype(returns\u003cint\u003e(x++))\n    \u003e;\n};\n```\nThis trait will be true if the expressions `x++` is valid and is convertible to `int`.\n\nHere's an example using placeholder expressions as well:\n```cpp\nTICK_TRAIT(is_incrementable)\n{\n    template\u003cclass T\u003e\n    auto require(T\u0026\u0026 x) -\u003e valid\u003c\n        decltype(returns\u003cstd::is_integral\u003c_\u003e\u003e(x++))\n    \u003e;\n};\n```\nThis trait will be true if the expressions `x++` is valid and returns a type that `is_integral`.\n\nNote: The `TICK_RETURNS` macro can be used instead to improve compatability with older compilers(such as gcc 4.6):\n```cpp\nTICK_TRAIT(is_incrementable)\n{\n    template\u003cclass T\u003e\n    auto require(T\u0026\u0026 x) -\u003e valid\u003c\n        TICK_RETURNS(x++, int)\n    \u003e;\n};\n```\n\nAlso, `returns\u003cvoid\u003e` is prohibited.\n\n```cpp\nTICK_TRAIT(is_incrementable)\n{\n    template\u003cclass T\u003e\n    auto require(T\u0026\u0026 x) -\u003e valid\u003c\n        decltype(returns\u003cvoid\u003e(x++)) // Compiler error\n    \u003e;\n};\n```\n\nInstead, use either `decltype` directly without `returns`, or if there is a possibility of `void` from a computed type, use `TICK_RETURNS` or `has_type` instead.\n\nhas_type\n--------\n\nThe `has_type` query can check if a type exist and if the type matches. For example:\n```cpp\nTICK_TRAIT(has_nested_type)\n{\n    template\u003cclass T\u003e\n    auto require(const T\u0026 x) -\u003e valid\u003c\n        has_type\u003ctypename T::type\u003e\n    \u003e;\n};\n```\nThis trait will be true if `T` has a nested type called `type`.\n\nNow `has_type` used as above is not quite as useful since the above example, can also be simply written without `has_type` like this:\n```cpp\nTICK_TRAIT(has_nested_type)\n{\n    template\u003cclass T\u003e\n    auto require(const T\u0026 x) -\u003e valid\u003c\n        typename T::type\n    \u003e;\n};\n```\nSo, an optional second parameter can be provided to check if the type matches. Here's an example:\n```cpp\nTICK_TRAIT(has_nested_int_type)\n{\n    template\u003cclass T\u003e\n    auto require(const T\u0026 x) -\u003e valid\u003c\n        has_type\u003ctypename T::type, std::is_integral\u003c_\u003e\u003e\n    \u003e;\n};\n```\nThis trait will be true if `T` has a nested type called `type` which is an integral type.\n\nhas_template\n------------\n\nThe `has_template` query can check if a template exist. For example:\n```cpp\nTICK_TRAIT(has_nested_result)\n{\n    template\u003cclass T\u003e\n    auto require(const T\u0026 x) -\u003e valid\u003c\n        has_template\u003cT::template result\u003e\n    \u003e;\n};\n```\nThis trait will be true if `T` has a nested template called `result`.\n\nTrait evaluation\n----------------\n\nThe `is_true` and `is_false` queries can check if a trait is true or false. Using refinements is the preferred way of checking for additional traits, but this can be useful if the evaluation of some trait can't be used lazily with placeholder expressions. So the `is_true` and `is_false` can be used instead, for example:\n```cpp\nTICK_TRAIT(is_2d_array)\n{\n    template\u003cclass T\u003e\n    auto require(const T\u0026 x) -\u003e valid\u003c\n        is_true\u003cstd::is_same\u003cstd::rank\u003cT\u003e::type, std::integral_constant\u003cstd::size_t, 2\u003e\u003e \u003e\n    \u003e;\n};\n```\n\nHelper functions\n----------------\n\nThe library also provides `as_const` and `as_mutable` functions to ensure lvalues are either `const` or `mutable` respectively:\n\n```cpp\nTICK_TRAIT(is_copy_assignable)\n{\n    template\u003cclass T\u003e\n    auto require(T\u0026\u0026 x) -\u003e valid\u003c\n        decltype(x = as_const(x))\n    \u003e;\n};\n```\n\nBuild traits without macros\n===========================\n\nThe traits can be built without using the `TICK_TRAIT` macros. However, it may introduce problems with portability. So if only one platform is needed to be supported, then here's how to build them. First, build a class for the `require` functions and inherit from `tick::ops` to bring in all the query operations:\n```cpp\nstruct is_incrementable_r : tick::ops\n{\n    template\u003cclass T\u003e\n    auto require(T\u0026\u0026 x) -\u003e valid\u003c\n        decltype(x++),\n        decltype(++x)\n    \u003e;\n};\n```\nNext, turn it into a trait using `tick::models`:\n```cpp\ntemplate\u003cclass... Ts\u003e\nstruct is_incrementable\n: tick::models\u003cis_incrementable_r, Ts...\u003e\n{};\n```\n\nRefinements\n-----------\n\nRefinements can be used by using the `tick::refines` class:\n```cpp\nstruct is_incrementable_r \n: tick::ops, tick::refines\u003cstd::is_default_constructible\u003ctick::_\u003e\u003e\n{\n    template\u003cclass T\u003e\n    auto require(T\u0026\u0026 x) -\u003e valid\u003c\n        decltype(x++),\n        decltype(++x)\n    \u003e;\n};\n```\nNotice, the placeholders have to be fully qualified here.\n\nTemplate constraints\n====================\n\nThree macros are provided to help improve the readability of template constraints.\n\nTICK_REQUIRES\n-------------\n\nThe `TICK_REQUIRES` can be used on template parameters. For example,\n```cpp\ntemplate\u003cclass T, TICK_REQUIRES(is_incrementable\u003cT\u003e())\u003e\nvoid increment(T\u0026 x)\n{\n    x++;\n}\n```\n\nTICK_CLASS_REQUIRES\n-------------------\n\nThe `TICK_CLASS_REQUIRES` can be used when template specialization is done on classes. For example,\n```cpp\ntemplate\u003cclass T, class=void\u003e\nstruct foo\n{\n    ...\n};\n\ntemplate\u003cclass T\u003e\nstruct foo\u003cT, TICK_CLASS_REQUIRES(is_incrementable\u003cT\u003e() and not std::is_integral\u003cT\u003e())\u003e\n{\n    ...\n};\n\ntemplate\u003cclass T\u003e\nstruct foo\u003cT, TICK_CLASS_REQUIRES(std::is_integral\u003cT\u003e())\u003e\n{\n    ...\n};\n```\n\nTICK_MEMBER_REQUIRES\n--------------------\n\nThe `TICK_MEMBER_REQUIRES` can be used for member function inside of classes, that are not templated. For example,\n```cpp\ntemplate\u003cclass T\u003e\nstruct foo\n{\n    T x;\n\n    TICK_MEMBER_REQUIRES(is_incrementable\u003cT\u003e())\n    void up()\n    {\n        x++;\n    }\n};\n```\n\nTICK_PARAM_REQUIRES\n--------------------\n\nThe `TICK_PARAM_REQUIRES` can be used in the paramater of the function. This is useful for lambdas:\n```cpp\nauto increment = [](auto\u0026 x, TICK_PARAM_REQUIRES(is_incrementable\u003cdecltype(x)\u003e()))\n{\n    x++;\n};\n```\nAlso, the `trait` function is provided which can be used to deduce the type of the parameters:\n```cpp\nauto increment = [](auto\u0026 x, TICK_PARAM_REQUIRES(trait\u003cis_incrementable\u003e(x)))\n{\n    x++;\n};\n```\nNote: The `trait` function always deduces the type without references. So `trait\u003cstd::is_lvalue_reference\u003e(x)` will always be false.\n\nTICK_FUNCTION_REQUIRES\n----------------------\n\nThe `TICK_FUNCTION_REQUIRES` can be used on functions. This requires placing parenthesis around the return type:\n```cpp\ntemplate\u003cclass T\u003e\nTICK_FUNCTION_REQUIRES(is_incrementable\u003cT\u003e())\n(void) increment(T\u0026 x)\n{\n    x++;\n}\n```\nNote: The `TICK_REQUIRES` should be preferred. \n\nTrait checking\n==============\n\nThe `TICK_TRAIT_CHECK` macro will statically assert the list of traits that are true but it will show what traits failed including base traits. This can be useful to show more informative messages about why a trait is false.\n\nRequirements\n============\n\nThis requires a C++11 compiler. There a no third-party dependencies. This has been tested on clang 3.4, gcc 4.6-4.9, and Visual Studio 2015.\n\nZLang support\n=============\n\n[ZLang](https://github.com/pfultz2/ZLang) is supported for some of the macros. The macros are in the `tick` namespace. For example,\n```cpp\n$(trait is_incrementable)\n{\n    template\u003cclass T\u003e\n    auto require(T\u0026\u0026 x) -\u003e valid\u003c\n        decltype(x++),\n        decltype(++x)\n    \u003e;\n};\n```\nAcknowledgments\n===============\n\n* Eric Niebler for the idea of using a `requires` member function to check concept traits.[http://ericniebler.com/2013/11/23/concept-checking-in-c11/]\n* Jamboree for the idea of using a template class to place expressions.[https://github.com/ericniebler/range-v3/issues/29#issuecomment-51016277] \n\n","funding_links":[],"categories":["TODO scan for Android support in followings"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpfultz2%2FTick","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpfultz2%2FTick","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpfultz2%2FTick/lists"}