https://github.com/hymkor/study-cpp-enumerate
std::vector / std::set 両方からデータを取れる関数はコード本体をヘッダファイルにおきたくないので、あまりテンプレート関数にしたくない
https://github.com/hymkor/study-cpp-enumerate
cpp enumerator iterator study
Last synced: 24 days ago
JSON representation
std::vector / std::set 両方からデータを取れる関数はコード本体をヘッダファイルにおきたくないので、あまりテンプレート関数にしたくない
- Host: GitHub
- URL: https://github.com/hymkor/study-cpp-enumerate
- Owner: hymkor
- Created: 2023-04-28T11:39:37.000Z (about 2 years ago)
- Default Branch: master
- Last Pushed: 2023-05-07T10:21:38.000Z (almost 2 years ago)
- Last Synced: 2025-02-10T15:50:56.319Z (3 months ago)
- Topics: cpp, enumerator, iterator, study
- Language: C++
- Homepage:
- Size: 28.3 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
std::vector / std::set 両方からデータを取れる関数の本体をあまりテンプレートにしたくない
=============現状こういうことはできない
------------------------[main0.cpp](./main0.cpp)
```main0.cpp
#include
#include
#include
#includetemplate void put(T cursor,T end);
int main(){
std::vector v;
v.push_back( "a" );
v.push_back( "b" );
v.push_back( "c" );put( v.begin() , v.end() );
std::set s;
s.insert( "1" );
s.insert( "2" );
s.insert( "3" );put( s.begin() , s.end() );
}
```[sub0.cpp](./sub0.cpp)
```sub0.cpp
#includetemplate void put(T cursor,T end)
{
for( ; cursor != end ; cursor++ ){
std::cout << *cursor << std::endl;
}
}
```これをビルドするとリンクエラーになるので、関数 put をヘッダファイルに置くか、sub0.cpp 側ですべての想定される T の型に対して実体化しておかなくていけない[^externtemplate]
[^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)
put が巨大な関数の場合、前者はビルドが遅くなるし、後者のようなものはきちんとメンテしないと使わない無駄な実体が発生してしまう。
うまいことソースを分割したまま、任意のコンテナの要素を列挙できる仕組みは作れないか
過去のトライ
-----------+ [std::vector / std::set 両方からデータを取れる関数をあまりテンプレートにしたくない - 標準愚痴出力](https://zetamatta.hatenablog.com/entry/2019/09/22/080313)
+ [(続) std::vector / std::set 両方からデータを取れる関数をあまりテンプレートにしたくない - 標準愚痴出力](https://zetamatta.hatenablog.com/entry/2019/09/22/131156)
+ [(続々) std::vector / std::set 両方からデータを取れる関数をあまりテンプレートにしたくない - 標準愚痴出力](https://zetamatta.hatenablog.com/entry/2019/09/22/163822)最新成果
--------### 利用例
#### 関数側 ([sub.cpp](./sub.cpp))
```sub.cpp
#include
#include
#include "enumerate.h"void put(enumerator &each)
{
std::string value;
while( each(value) ){
std::cout << value << std::endl;
}
}
```+ `sub.cpp` 側では、`` も `` も include していない
( ただし、`"enumerate.h"` の中で `` だけは include している。詳しくは後述 )
+ `std::vector` や `std::set` などのコレクションを引き受ける引数の型は `enumerator` という型にしておく.#### 呼び出し側 ([main.cpp](./main.cpp))
```main.cpp
#include
#include
#include
#include
#include "enumerate.h"extern void put(enumerator &each);
int main(void)
{
std::vector v;
v.push_back( "a" );
v.push_back( "b" );
v.push_back( "c" );put( make_enumerator(v) );
std::set s;
s.insert( "1" );
s.insert( "2" );
s.insert( "3" );put( make_enumerator(s) );
std::list ls;
ls.push_back( "x" );
ls.push_back( "y" );
ls.push_back( "z" );put( make_enumerator(ls) );
}
```+ 呼び出す際は、`std::vector` や `std::set` を `make_enumerator` という関数で、`enumerator` へ変換して、`put` へ引き渡す.
#### 出力
``` ./main |
a
b
c
1
2
3
x
y
z
```### 実装 ([enumerate.h](./enumerate.h))
+ `make_enumerator` という型は `bool operator()(bool(T&))` というメソッドを持っており、関数の体裁で呼び出すことができる
+ つまり、関数オブジェクトであるから、 `std::function` へ変換することができる。
+ ゆえに関数側のパラメータは `std::set` や `std::vector` などの型に固定しなくてよくなる。+ この二つの組み合わせだけでも利用可能であるが、見かけの体裁を読みやすくするため、次のようなガワをかぶせた。
+ `function` の別名として `enumerator` を定義(using)```enumerate.h
#includetemplate
class make_enumerator {
typename T::iterator m_cursor,m_end;
public:
make_enumerator(T &t)
: m_cursor(t.begin()) , m_end(t.end()) {}
bool operator() (typename T::value_type &store) {
if( m_cursor == m_end ){
return false;
}
store = *m_cursor;
m_cursor++;
return true;
};
};template
using enumerator = const std::function;
```辞書向けバージョン
-----------------### [sub2.cpp](./sub2.cpp)
```sub2.cpp
#include
#include
#include "enumerate2.h"void sub(pair_enumerator &each)
{
std::string key;
int value;
while( each(key,value) ){
std::cout << key << ":" << value << std::endl;
}
}```
### [main2.cpp](./main2.cpp)
```main2.cpp
#include
#include
#include#include "enumerate2.h"
extern void sub(pair_enumerator &each);
int main(){
std::map map;
map["ahaha"] = 1;
map["ihihi"] = 2;
map["ufufu"] = 3;
sub(make_pair_enumerator(map));std::unordered_map umap;
umap["AHAHA"] = 1;
umap["IHIHI"] = 2;
umap["UFUFU"] = 3;
sub(make_pair_enumerator(umap));
}
```### [enumerate2.h](./enumerate2.h)
```enumerate2.h
#includetemplate
class make_pair_enumerator {
typename T::iterator m_cursor,m_end;
public:
make_pair_enumerator(T &t)
: m_cursor(t.begin()) , m_end(t.end()) {}
bool operator() (typename T::key_type &first,
typename T::mapped_type &second) {
if( m_cursor == m_end ){
return false;
}
first = m_cursor->first;
second = m_cursor->second;
m_cursor++;
return true;
};
};template
using pair_enumerator = const std::function;
```