質問
C++ で頻繁に使用する「foreach」マクロは、ほとんどの STL コンテナーで機能します。
#define foreach(var, container) \
for(typeof((container).begin()) var = (container).begin(); \
var != (container).end(); \
++var)
(「typeof」は gcc 拡張機能であることに注意してください。) これは次のように使用されます。
std::vector< Blorgus > blorgi = ...;
foreach(blorgus, blorgi) {
blorgus->draw();
}
マップの値を反復する同様のものを作成したいと思います。おそらく「foreach_value」と呼んでください。だから、書く代わりに
foreach(pair, mymap) {
pair->second->foo();
}
私なら書きます
foreach_value(v, mymap) {
v.foo();
}
2 つの変数を宣言する必要があるため、これを行うマクロを思いつきません。イテレータと値変数 (上記の「v」)。gcc 拡張機能を使用しても、for ループのイニシャライザでそれを行う方法がわかりません。foreach_value 呼び出しの直前に宣言することもできますが、同じスコープ内の foreach_value マクロの他のインスタンスと競合します。現在の行番号をイテレータ変数名の末尾に付けることができれば機能しますが、その方法がわかりません。
解決
これは 2 つのループを使用して実行できます。最初の例では、コンテナ変数の関数である名前を付けてイテレータを宣言します (独自のコードとの競合が心配な場合は、これをさらに醜くすることもできます)。2 番目は value 変数を宣言します。
#define ci(container) container ## iter
#define foreach_value(var, container) \
for (typeof((container).begin()) ci(container) = container.begin(); \
ci(container) != container.end(); ) \
for (typeof(ci(container)->second)* var = &ci(container)->second; \
ci(container) != container.end(); \
(++ci(container) != container.end()) ? \
(var = &ci(container)->second) : var)
同じループ終了条件を使用すると、外側のループは 1 回だけ発生します (運が良ければ、最適化されて解消されます)。また、マップが空の場合は、反復子で ->second を呼び出すことを避けます。これは、内側のループのインクリメントで三項演算子を使用する場合と同じ理由です。最後に、var は再度参照されないため、最後の値のままにします。
ci(container) をインライン化することもできますが、そのほうがマクロが読みやすくなると思います。
他のヒント
あなたは探しているでしょう BOOST_FOREACH - 彼らはあなたのためにすべての作業をすでに行っています!
独自のロールをしたい場合は、C ++のどこにでもブロックを宣言できます。
// Valid C++ code (which does nothing useful)
{
int a = 21; // Which could be storage of your value type
}
// a out of scope here
{
int a = 32; // Does not conflict with a above
}
STL 変身 関数も同様のことを行います。
引数は(順番に)次のとおりです。
- コンテナの先頭を指定する入力反復子
- コンテナの終わりを指定する入力反復子
- 出力を配置する場所を定義する出力反復子 (for-each と同様のインプレース変換の場合は、#1 で入力反復子を渡すだけです)
- 各要素に対して実行する単項関数 (関数オブジェクト)
非常に単純な例として、次のようにして文字列内の各文字を大文字にすることができます。
#include <iostream>
#include <string>
#include <algorithm>
#include <cctype>
int main(int argc, char* argv[]) {
std::string s("my lowercase string");
std::transform(s.begin(), s.end(), s.begin(), toupper);
std::cout << s << std::endl; // "MY LOWERCASE STRING"
}
あるいは、 蓄積する この関数を使用すると、関数オブジェクトの呼び出し間で一部の値を保持できます。 蓄積する の場合のように、入力コンテナ内のデータは変更されません。 変身.
を使用することを考えましたか? ブーストライブラリ?彼らは持っています foreach
マクロが実装されました これはおそらくあなたが書くものよりも堅牢です...そしてまたあります transform_iterator
これは、必要なものの 2 番目の抽出部分を実行するために使用できるようです。
残念ながら正確にお伝えすることはできません どうやって 私はC++の知識が足りないのでそれを使用します:) このGoogle検索 いくつかの有望な答えが見つかります。 comp.lang.c++.moderated, ブーストのtransform_iteratorの使用例.
Boost::For_each が最善の策です。素晴らしいのは、実際に提供されるのはマクロ BOOST_FOREACH() であり、これをラップして、コード内で本当に呼び出したいものを #define することができます。ほとんどの人は古き良き「foreach」を選択するでしょうが、他のショップでは異なるコーディング標準を採用している可能性があるため、これはその考え方に適合します。Boost には、C++ 開発者向けの特典が他にもたくさんあります。使う価値は十分にあります。
私は、ローカル変数とポインターで動作するものを含む foreach() のいくつかのバリアントを備えた小さな Foreach.h ヘルパーを作成しました。また、ループ内から要素を削除しないように保護された追加バージョンも備えています。したがって、私のマクロを使用するコードは次のように素晴らしく快適に見えます。
#include <cstdio>
#include <vector>
#include "foreach.h"
int main()
{
// make int vector and fill it
vector<int> k;
for (int i=0; i<10; ++i) k.push_back(i);
// show what the upper loop filled
foreach_ (it, k) printf("%i ",(*it));
printf("\n");
// show all of the data, but get rid of 4
// http://en.wikipedia.org/wiki/Tetraphobia :)
foreachdel_ (it, k)
{
if (*it == 4) it=k.erase(it);
printf("%i ",(*it));
}
printf("\n");
return 0;
}
出力:
0 1 2 3 4 5 6 7 8 9
0 1 2 3 5 6 7 8 9
私の Foreach.h 次のマクロを提供します。
- foreach() - ポインタの通常の foreach
- foreach_() - ローカル変数の通常の foreach
- foreachdel() - ループ内の削除をチェックする foreach バージョン、ポインター バージョン
- foreachdel_() - ループ内の削除をチェックする foreach バージョン、ローカル変数バージョン
それらは私にとって確かに効果があります。あなたの生活も少し楽になることを願っています:)
この質問には 2 つの部分があります。何らかの方法で (1) マップ上にイテレータ (またはむしろ、反復可能なシーケンス) を生成する必要があります。 価値観 (キーではありません)、(2) マクロを使用して、多くの定型文を使用せずに反復を実行します。
最もクリーンな解決策は、 ブースト範囲 アダプタ パート (1) と ブーストフォーリーチ パート(2)については。自分でマクロを書いたりイテレータを実装したりする必要はありません。
#include <map>
#include <string>
#include <boost/range/adaptor/map.hpp>
#include <boost/foreach.hpp>
int main()
{
// Sample data
std::map<int, std::string> myMap ;
myMap[0] = "Zero" ;
myMap[10] = "Ten" ;
myMap[20] = "Twenty" ;
// Loop over map values
BOOST_FOREACH( std::string text, myMap | boost::adaptors::map_values )
{
std::cout << text << " " ;
}
}
// Output:
// Zero Ten Twenty
mymap の型をテンプレート パラメーターとして受け取り、* と -> をオーバーロードすることで値の反復子のように動作するテンプレート クラスを定義できます。
#define foreach(var, container) for (typeof((container).begin()) var = (container).begin(); var != (container).end(); ++var)
C++ には typeof がありません...コンパイルはどうですか?(確かに携帯性はない)
自分で実装しました foreach_value
に基づく Boost
foreach
コード:
#include <boost/preprocessor/cat.hpp>
#define MUNZEKONZA_FOREACH_IN_MAP_ID(x) BOOST_PP_CAT(x, __LINE__)
namespace munzekonza {
namespace foreach_in_map_private {
inline bool set_false(bool& b) {
b = false;
return false;
}
}
}
#define MUNZEKONZA_FOREACH_VALUE(value, map) \
for(auto MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it) = map.begin(); \
MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it) != map.end();) \
for(bool MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue) = true; \
MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue) && \
MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it) != map.end(); \
(MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue)) ? \
((void)++MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it)) : \
(void)0) \
if( munzekonza::foreach_in_map_private::set_false( \
MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue))) {} else \
for( value = MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_it)->second; \
!MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue); \
MUNZEKONZA_FOREACH_IN_MAP_ID(_foreach_in_map_continue) = true)
たとえば、次のようにコードで使用できます。
#define MUNZEKONZA_FOREACH_VALUE foreach_value
std::map<int, std::string> mymap;
// populate the map ...
foreach_value( const std::string& value, mymap ) {
// do something with value
}
// change value
foreach_value( std::string& value, mymap ) {
value = "hey";
}
#define zforeach(var, container) for(auto var = (container).begin(); var != (container).end(); ++var)
typeof() はないので、これを使用できます。
decltype((container).begin()) var
decltype(container)::iterator var