{"id":18053893,"url":"https://github.com/hymkor/study-cpp-enumerate","last_synced_at":"2025-07-01T22:32:09.167Z","repository":{"id":158503817,"uuid":"633833153","full_name":"hymkor/study-cpp-enumerate","owner":"hymkor","description":"std::vector / std::set 両方からデータを取れる関数はコード本体をヘッダファイルにおきたくないので、あまりテンプレート関数にしたくない","archived":false,"fork":false,"pushed_at":"2023-05-07T10:21:38.000Z","size":29,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-05T08:25:58.193Z","etag":null,"topics":["cpp","enumerator","iterator","study"],"latest_commit_sha":null,"homepage":"","language":"C++","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/hymkor.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":"2023-04-28T11:39:37.000Z","updated_at":"2023-05-04T06:48:29.000Z","dependencies_parsed_at":"2023-05-22T12:45:22.761Z","dependency_job_id":null,"html_url":"https://github.com/hymkor/study-cpp-enumerate","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/hymkor/study-cpp-enumerate","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hymkor%2Fstudy-cpp-enumerate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hymkor%2Fstudy-cpp-enumerate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hymkor%2Fstudy-cpp-enumerate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hymkor%2Fstudy-cpp-enumerate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hymkor","download_url":"https://codeload.github.com/hymkor/study-cpp-enumerate/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hymkor%2Fstudy-cpp-enumerate/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263046490,"owners_count":23405194,"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":["cpp","enumerator","iterator","study"],"created_at":"2024-10-31T00:08:28.519Z","updated_at":"2025-07-01T22:32:09.071Z","avatar_url":"https://github.com/hymkor.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"std::vector / std::set 両方からデータを取れる関数の本体をあまりテンプレートにしたくない\r\n=============\r\n\r\n現状こういうことはできない\r\n------------------------\r\n\r\n[main0.cpp](./main0.cpp)\r\n\r\n```main0.cpp\r\n#include \u003cstring\u003e\r\n#include \u003cvector\u003e\r\n#include \u003cset\u003e\r\n#include \u003clist\u003e\r\n\r\ntemplate\u003cclass T\u003e void put(T cursor,T end);\r\n\r\nint main(){\r\n    std::vector\u003cstd::string\u003e v;\r\n    v.push_back( \"a\" );\r\n    v.push_back( \"b\" );\r\n    v.push_back( \"c\" );\r\n\r\n    put( v.begin() , v.end() );\r\n\r\n    std::set\u003cstd::string\u003e s;\r\n    s.insert( \"1\" );\r\n    s.insert( \"2\" );\r\n    s.insert( \"3\" );\r\n\r\n    put( s.begin() , s.end() );\r\n}\r\n```\r\n\r\n[sub0.cpp](./sub0.cpp)\r\n\r\n```sub0.cpp\r\n#include \u003ciostream\u003e\r\n\r\ntemplate\u003cclass T\u003e void put(T cursor,T end)\r\n{\r\n    for( ; cursor != end ; cursor++ ){\r\n        std::cout \u003c\u003c *cursor \u003c\u003c std::endl;\r\n    }\r\n}\r\n```\r\n\r\nこれをビルドするとリンクエラーになるので、関数 put をヘッダファイルに置くか、sub0.cpp 側ですべての想定される T の型に対して実体化しておかなくていけない[^externtemplate]\r\n\r\n[^externtemplate]: externtemplate というものを使う。例：[main0_.cpp](https://github.com/hymkor/study-cpp-enumerate/blob/master/main0_.cpp),  [sub0_.cpp](https://github.com/hymkor/study-cpp-enumerate/blob/master/sub0_.cpp) ; 参考文献：[[C++11] extern templateの機能とその使い道 - Qiita](https://qiita.com/Kogia_sima/items/b7a1e23a78f81d913089)\r\n\r\nput が巨大な関数の場合、前者はビルドが遅くなるし、後者のようなものはきちんとメンテしないと使わない無駄な実体が発生してしまう。\r\n\r\nうまいことソースを分割したまま、任意のコンテナの要素を列挙できる仕組みは作れないか\r\n\r\n過去のトライ\r\n-----------\r\n\r\n+ [std::vector / std::set 両方からデータを取れる関数をあまりテンプレートにしたくない - 標準愚痴出力](https://zetamatta.hatenablog.com/entry/2019/09/22/080313)\r\n+ [(続) std::vector / std::set 両方からデータを取れる関数をあまりテンプレートにしたくない - 標準愚痴出力](https://zetamatta.hatenablog.com/entry/2019/09/22/131156)\r\n+ [(続々) std::vector / std::set 両方からデータを取れる関数をあまりテンプレートにしたくない - 標準愚痴出力](https://zetamatta.hatenablog.com/entry/2019/09/22/163822)\r\n\r\n最新成果\r\n--------\r\n\r\n### 利用例\r\n\r\n#### 関数側 ([sub.cpp](./sub.cpp))\r\n\r\n```sub.cpp\r\n#include \u003cstring\u003e\r\n#include \u003ciostream\u003e\r\n#include \"enumerate.h\"\r\n\r\nvoid put(enumerator\u003cstd::string\u003e \u0026each)\r\n{\r\n    std::string value;\r\n    while( each(value) ){\r\n        std::cout \u003c\u003c value \u003c\u003c std::endl;\r\n    }\r\n}\r\n```\r\n\r\n+ `sub.cpp` 側では、`\u003cvector\u003e` も `\u003cset\u003e` も include していない\r\n  ( ただし、`\"enumerate.h\"` の中で `\u003cfunctional.h\u003e` だけは include している。詳しくは後述 )\r\n+ `std::vector` や `std::set` などのコレクションを引き受ける引数の型は `enumerator\u003cT\u003e` という型にしておく.\r\n\r\n#### 呼び出し側 ([main.cpp](./main.cpp))\r\n\r\n```main.cpp\r\n#include \u003cstring\u003e\r\n#include \u003cvector\u003e\r\n#include \u003cset\u003e\r\n#include \u003clist\u003e\r\n#include \"enumerate.h\"\r\n\r\nextern void put(enumerator\u003cstd::string\u003e \u0026each);\r\n\r\nint main(void)\r\n{\r\n    std::vector\u003cstd::string\u003e v;\r\n    v.push_back( \"a\" );\r\n    v.push_back( \"b\" );\r\n    v.push_back( \"c\" );\r\n\r\n    put( make_enumerator(v) );\r\n\r\n    std::set\u003cstd::string\u003e s;\r\n    s.insert( \"1\" );\r\n    s.insert( \"2\" );\r\n    s.insert( \"3\" );\r\n\r\n    put( make_enumerator(s) );\r\n\r\n    std::list\u003cstd::string\u003e ls;\r\n    ls.push_back( \"x\" );\r\n    ls.push_back( \"y\" );\r\n    ls.push_back( \"z\" );\r\n\r\n    put( make_enumerator(ls) );\r\n}\r\n```\r\n\r\n+ 呼び出す際は、`std::vector\u003cT\u003e` や `std::set\u003cT\u003e` を `make_enumerator` という関数で、`enumerator\u003cT\u003e` へ変換して、`put` へ引き渡す.\r\n\r\n#### 出力\r\n\r\n``` ./main |\r\na\r\nb\r\nc\r\n1\r\n2\r\n3\r\nx\r\ny\r\nz\r\n```\r\n\r\n### 実装 ([enumerate.h](./enumerate.h))\r\n\r\n+ `make_enumerator\u003cT\u003e` という型は `bool operator()(bool(T\u0026))` というメソッドを持っており、関数の体裁で呼び出すことができる\r\n+ つまり、関数オブジェクトであるから、 `std::function\u003cbool(T\u0026)\u003e` へ変換することができる。\r\n+ ゆえに関数側のパラメータは `std::set` や `std::vector` などの型に固定しなくてよくなる。\r\n\r\n+ この二つの組み合わせだけでも利用可能であるが、見かけの体裁を読みやすくするため、次のようなガワをかぶせた。\r\n    + `function\u003cbool(T\u0026)\u003e` の別名として `enumerator\u003cT\u003e` を定義(using)\r\n\r\n```enumerate.h\r\n#include \u003cfunctional\u003e\r\n\r\ntemplate \u003ctypename T\u003e\r\nclass make_enumerator {\r\n    typename T::iterator m_cursor,m_end;\r\npublic:\r\n    make_enumerator(T \u0026t)\r\n        : m_cursor(t.begin()) , m_end(t.end()) {}\r\n    bool operator() (typename T::value_type \u0026store) {\r\n        if( m_cursor == m_end ){\r\n            return false;\r\n        }\r\n        store = *m_cursor;\r\n        m_cursor++;\r\n        return true;\r\n    };\r\n};\r\n\r\ntemplate \u003ctypename T\u003e\r\nusing enumerator = const std::function\u003cbool(T\u0026)\u003e;\r\n```\r\n\r\n辞書向けバージョン\r\n-----------------\r\n\r\n### [sub2.cpp](./sub2.cpp)\r\n\r\n```sub2.cpp\r\n#include \u003cstring\u003e\r\n#include \u003ciostream\u003e\r\n#include \"enumerate2.h\"\r\n\r\nvoid sub(pair_enumerator\u003cstd::string,int\u003e \u0026each)\r\n{\r\n    std::string key;\r\n    int value;\r\n    while( each(key,value) ){\r\n        std::cout \u003c\u003c key \u003c\u003c \":\" \u003c\u003c value \u003c\u003c std::endl;\r\n    }\r\n}\r\n\r\n```\r\n\r\n### [main2.cpp](./main2.cpp)\r\n\r\n```main2.cpp\r\n#include \u003cstring\u003e\r\n#include \u003cmap\u003e\r\n#include \u003cunordered_map\u003e\r\n\r\n#include \"enumerate2.h\"\r\n\r\nextern void sub(pair_enumerator\u003cstd::string,int\u003e \u0026each);\r\n\r\nint main(){\r\n    std::map\u003cstd::string,int\u003e map;\r\n    map[\"ahaha\"] = 1;\r\n    map[\"ihihi\"] = 2;\r\n    map[\"ufufu\"] = 3;\r\n    sub(make_pair_enumerator(map));\r\n\r\n    std::unordered_map\u003cstd::string,int\u003e umap;\r\n    umap[\"AHAHA\"] = 1;\r\n    umap[\"IHIHI\"] = 2;\r\n    umap[\"UFUFU\"] = 3;\r\n    sub(make_pair_enumerator(umap));\r\n}\r\n```\r\n\r\n### [enumerate2.h](./enumerate2.h)\r\n\r\n```enumerate2.h\r\n#include \u003cfunctional\u003e\r\n\r\ntemplate \u003ctypename T\u003e\r\nclass make_pair_enumerator {\r\n    typename T::iterator m_cursor,m_end;\r\npublic:\r\n    make_pair_enumerator(T \u0026t)\r\n        : m_cursor(t.begin()) , m_end(t.end()) {}\r\n    bool operator() (typename T::key_type    \u0026first,\r\n                     typename T::mapped_type \u0026second) {\r\n        if( m_cursor == m_end ){\r\n            return false;\r\n        }\r\n        first = m_cursor-\u003efirst;\r\n        second = m_cursor-\u003esecond;\r\n        m_cursor++;\r\n        return true;\r\n    };\r\n};\r\n\r\ntemplate \u003ctypename Key,typename Mapped\u003e\r\nusing pair_enumerator = const std::function\u003cbool(Key\u0026,Mapped\u0026)\u003e;\r\n```\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhymkor%2Fstudy-cpp-enumerate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhymkor%2Fstudy-cpp-enumerate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhymkor%2Fstudy-cpp-enumerate/lists"}