3

この質問の前置きとして、私はさまざまなC ++ユーティリティ関数と(必要な場合は)マクロを、自分で使用するためのより大きなツールキットに実装してきました。最近、BOOST_FOREACHと反復可能な意識関数に基づいてさまざまなループマクロを作成していました。

簡単に言うと、BOOST_FOREACHを使用する列挙ループを作成するのに苦労しましたが、ループの反復ごとにインクリメントされる追加のパラメーターが渡されました。これは、多くのボイラープレートコードを追加せずに任意のイテレーターをループするときに役立つPythonのenumerateコマンドのように機能します。マクロはBOOST_FOREACHのような形式を取ります。

ENUMERATE_FOREACH(COUNT, VAR, COL)

そしてこれを効果的に行いますが、不均衡な'}'を配置して閉じる(または他のループ終了マクロ)ことに依存することはありません:

COUNT = 0; BOOST_FOREACH(VAR, COL) { COUNT++; ...Rest of loop...

C++で非C++演算子を作成しようとしているという非難を受ける前に、これが外国の概念をC ++に詰め込んでいることを十分に認識しており、PythonizeC++コードを試していません。極端な依存関係なしに、既知のツールセット/ブーストを使用してこのようなループをクリーンに実装できるかどうか、私は単に興味があります。このようなマクロ定義があると、反復するときにカウントする必要がある特定のスタイルのループのバグの原因となる可能性がなくなり、COUNT変数の目的が明確になります。

BOOST_FOREACHに入る前にCOL変数でテンプレートラッパーを実行することを考えましたが、COLの可能性の数は、異なるバージョンのENUMERATE_FOREACHを作成し、多くのBOOST_FOREACHを再実装せずに、反復可能な変数のいくつかの組み合わせでそれを困難/不可能にします- -膨大なテスト/時間のない、困難でばかげた価値のあるタスク。

別のインライン関数を実行することは機能する可能性がありますが、ループの構文が壊れて、(すでに実装した)各スタイルの演算子関数の受け渡しの状況になります。

これにより、boostのライブラリからforeach.hppの最後の行を取得し、追加の引数に独自のインクリメント演算子を挿入することになりました。次に、ブーストバージョンに依存するようになり、ハッキーなカスタムマクロの破壊をブーストするための新しい更新(構文の変更)について心配します。

私が考えた最後のオプションは、ENUMERATE_BEGINとENUMERATE_ENDを実行して、イテレーターのインクリメント操作を非表示にすることです。このアプローチは、ユーザーが1つではなく2つのマクロを配置する必要があるため、単一のマクロよりもエラーが発生しやすくなります。ただし、これが唯一の単純な解決策である可能性があります。

私はSOや他の情報源を調べて、誰かが以前にこれを試したことがあるかどうかを確認しました。うまくいけば、誰かがそのような実装の概念をうまく試したか、私のアプローチを変えることについてのアイデアを持っています。これを行うためのクリーンな方法がない場合は、カウントしたいときにcount++でループを開始し続けることができます。繰り返しになりますが、これは好奇心であり、誰かが私が口論したアイデアの1つは、完全に合理的なアプローチであるか、それが得られる限り優れていると提案するかもしれません。

4

2 に答える 2

4

ENUMERATE_FOREACH最後の引数が呼び出す述語である の 4 つの引数バージョンを使用することで、これを解決できます。

何かのようなもの:

ENUMERATE_FOREACH(COUNT, VAR, COL, PRED)

そして、それは次のように展開されます

{
    COUNT = 0;
    BOOST_FOREACH(VAR, COL)
    {
        PRED(COUNT);
        COUNT++;
    }
}

上記の良い点は、述語を C++11 ラムダ関数にすることができることです。

ENUMERATE_FOREACH(COUNT, VAR, COL,
                  [](int count){ cout << "count = " << count << '\n'; });

編集:別の方法は、4番目のパラメーターを引き続き使用することですが、そのパラメーターを実際のコードにします。例えば:

ENUMERATE_FOREACH(COUNT, VAR, COL, do { std::cout << COUNT << '\n'; } while (0))

に拡大します

{
    COUNT = 0;
    BOOST_FOREACH(VAR, COL)
    {
        do { std::cout << COUNT << '\n'; } while (0);
        COUNT++;
    }
}

たとえば C++11 ラムダを使用するよりも少し面倒かもしれません。

編集 2: C++11 があり、ラムダを使用できる場合は、おそらく新しい範囲ベースの for ループも使用できます。つまり、代わりに次のような適切な関数を作成できます。

template<typename SeqT, typename PredT>
void for_each_count(SeqT seq, PredT pred)
{
    int count = 0;
    for (auto val : seq)
    {
        pred(val, count);
        count++;
    }
}

使用法:

std::vector<int> v = {1, 2, 3, 4};
for_each_count(v,
    [](int v, int c){
        std::cout << "value = " << v << ", count = " << c << '\n';
    });

上記が印刷されます

値 = 1、カウント = 0
値 = 2、カウント = 1
値 = 3、カウント = 2
値 = 4、カウント = 3
于 2012-07-31T05:16:20.723 に答える
1

Joachim の応答を読んだ後、私はほとんど満足しましたが、少なくとも非常にハックな方法でできるようになるまで BOOST_FOREACH を操作しようとしましたが、マクロ全体を書き直したり、# を実行したりすることなく、実際に良性の方法でカウンターを実装できることがわかりました。 BOOST_FOREACH_NEXT の undef #define ステートメント。

BOOST_FOREACH に (VAR = derefence(...); ...) ステートメントがあるという事実を悪用し、VAR と = の間に構造体を配置して、 (VAR = IncrementCountAndPassRHS = derefence(...); .. .)。

マクロ展開の問題については (まだ) あまりテストしていませんが、forloop 内では安全だと思います。

EDIT同じ行に複数のループがある場合の変数スコープ名の重複の問題を修正するための更新を追加しました。

namespace loopnamespace {
template<typename T>
void incrementT(T *t) {
    (*t)++;
}

struct IncrementCounterPassthrough {
    bool checker;
    boost::function<void(void)> incrementer;

    template<typename Count>
    IncrementCounterPassthrough(Count& t) {
        t = -1;
        checker = true;
        incrementer = boost::bind(&incrementT<Count>, &t);
    }

    template<typename T>
    T& operator=(T& rhs) {
        incrementer();
        return rhs;
    }
};
}

#define ENUMERATE_FOREACH(COUNT, VAR, COL)                                                                   \
    for(::loopnamespace::IncrementCounterPassthrough BOOST_FOREACH_ID(_foreach_count_pass)(COUNT);           \
        BOOST_FOREACH_ID(_foreach_count_pass).checker; BOOST_FOREACH_ID(_foreach_count_pass).checker = false)\
    BOOST_FOREACH(VAR = BOOST_FOREACH_ID(_foreach_count_pass), COL)

私ができること:

std::string hello( "Hello, boost world!" );
unsigned int value;
ENUMERATE_FOREACH( value, char ch, hello ) {
   std::cout << ch << " => " << value << "\n";
}

出力する:

H => 0
e => 1
l => 2
l => 3
o => 4
, => 5
  => 6
b => 7
o => 8
o => 9
s => 10
t => 11
  => 12
w => 13
o => 14
r => 15
l => 16
d => 17
! => 18
于 2012-07-31T21:13:31.703 に答える