3

目標は、UI レベルでどのタイプのユーザーがどの操作を実行できるかを制御することです。このコードはしばらくの間有効です。少しだけ改善したいです。私が改善しようとしているファイルはおそらく自動生成されているはずですが、それでは変更が大きすぎるため、より簡単な解決策を探しています。

私たちが呼び出すファイルにはPermissionBits.h、これらの束があります。

// Here names are mangled; for example XYZ_OP_A is:
// permission to operation A in category/context XYZ
// SCU64 = static const unsigned __int64
// Some namespaces utilize all 64 bits
// The actual values (as long as they are proper bit fields) 
// do not matter - they are always used by name
namespace XYZPermissionBits
{
    SCU64 XYZ_OP_A = 1UI64 <<  0; //    1 = 0x0000000000000001
    SCU64 XYZ_OP_B = 1UI64 <<  1; //    2 = 0x0000000000000002
    SCU64 XYZ_OP_C = 1UI64 <<  2; //    4 = 0x0000000000000004
    SCU64 XYZ_OP_C = 1UI64 <<  3; //    8 = 0x0000000000000008
    SCU64 XYZ_OP_D = 1UI64 <<  4; //   16 = 0x0000000000000010
    SCU64 XYZ_OP_E = 1UI64 <<  5; //   32 = 0x0000000000000020
    SCU64 XYZ_OP_F = 1UI64 <<  6; //   64 = 0x0000000000000040
    SCU64 XYZ_OP_G = 1UI64 <<  7; //  128 = 0x0000000000000080
    SCU64 XYZ_OP_H = 1UI64 <<  8; //  256 = 0x0000000000000100
    SCU64 XYZ_OP_I = 1UI64 <<  9; //  512 = 0x0000000000000200
    SCU64 XYZ_OP_J = 1UI64 << 10; // 1024 = 0x0000000000000400
    SCU64 XYZ_OP_K = 1UI64 << 11; // 2048 = 0x0000000000000800
    SCU64 XYZ_OP_L = 1UI64 << 12; // 4096 = 0x0000000000001000 
}

ショートカットの助けを借りても1UI64 << <numBits>;、コーダーが重複した値でフラグを作成したり、タイプミスをしたりするなど、まだ問題があります.

理想的には、適切にフォーマットされ、次のように見えるマクロが必要です。

BITFIELDS_FOR_NAMESPACE(
    //*************** <<== I want to make the namespace name more vivid
    XYZPermissionBits,
    //*************** <<== somehow if that is possible. It is not a must-have.
    XYZ_OP_A, // Being able to add a comment here would be nice, but not critical
    XYZ_OP_B,
    XYZ_OP_C,
    XYZ_OP_D,
    XYZ_OP_E,
    XYZ_OP_F,
    XYZ_OP_G,
    XYZ_OP_H,
    XYZ_OP_I,
    XYZ_OP_J,
    XYZ_OP_K,
    XYZ_OP_L
)

このマクロを柔軟にして、2 つ未満または 65 個を超える引数 (名前空間名 + 64 個のフラグ) を入力できないようにしたいと考えています。私がやりたいこと、またはそれに近いことをすることは可能ですか、それとも生成されたコードに頼るべきですか? 他にどんなアドバイスがありますか?

4

5 に答える 5

6

Boost.PreProcessor を使用したテスト済みの例:

#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/comparison/greater.hpp>
#include <boost/preprocessor/comparison/less.hpp>
#include <boost/preprocessor/debug/assert.hpp>
#include <boost/preprocessor/seq/size.hpp>

#define CHECK_SIZE(size) \
  BOOST_PP_ASSERT_MSG(BOOST_PP_GREATER(size, 1), "<  2 :(") \
  BOOST_PP_ASSERT_MSG(BOOST_PP_LESS(size, 65),   "> 64 :(") \

#define DO_MAKE_BITFIELDS(a, b, i, elem) \
  SCU64 elem = 1UI64 << i;

#define BITFIELDS_FOR_NAMESPACE(name, seq) \
  CHECK_SIZE(BOOST_PP_SEQ_SIZE(seq)) \
  namespace name { \
    BOOST_PP_SEQ_FOR_EACH_I(DO_MAKE_BITFIELDS, _, seq) \
  }

使用法:

BITFIELDS_FOR_NAMESPACE(
    XYZPermissionBits,
    (XYZ_OP_A)
    (XYZ_OP_B)
    // ...
);
于 2010-06-30T16:19:17.460 に答える
2

コード生成ルートに進むことにした場合は、Cogを参照することをお勧めします。

Cog を使用すると、Python コードを C++ (またはその他の言語) ソース ファイルにコメントとして埋め込むことができます。Cog を実行すると、Python 出力がソース コードとして挿入されるため、ジェネレーター コードと生成された出力はすべて同じファイルで管理されます。これにより、メンテナンスが簡単になります。Python コードは、生成されたコードがどのように作成されたかを文書化します。

生成されたコードを含む cog を使用した例を次に示します。

namespace XYZPermissionBits
{
    /* [[[cog
    import cog
    operations = ["A", "B", "C", "D", 
                  "E", "F", "G", "H",
                  "I", "J", "K", "L"]

    assert 2 <= len(operations) <= 64

    for idx,op in enumerate(operations):
        cog.outl("SCU64 XYZ_OP_%s = 1UI64 << %s;" % (op, idx))

    ]]] */
    SCU64 XYZ_OP_A = 1UI64 << 0;
    SCU64 XYZ_OP_B = 1UI64 << 1;
    SCU64 XYZ_OP_C = 1UI64 << 2;
    SCU64 XYZ_OP_D = 1UI64 << 3;
    SCU64 XYZ_OP_E = 1UI64 << 4;
    SCU64 XYZ_OP_F = 1UI64 << 5;
    SCU64 XYZ_OP_G = 1UI64 << 6;
    SCU64 XYZ_OP_H = 1UI64 << 7;
    SCU64 XYZ_OP_I = 1UI64 << 8;
    SCU64 XYZ_OP_J = 1UI64 << 9;
    SCU64 XYZ_OP_K = 1UI64 << 10;
    SCU64 XYZ_OP_L = 1UI64 << 11;
// [[[end]]]
}
于 2010-06-30T15:53:27.847 に答える
2

IFAIK、ブースト プリプロセッサ ライブラリhttp://www.boost.org/doc/libs/1_43_0/libs/preprocessor/doc/index.htmlには、必要なすべてのプリミティブがあります。

于 2010-06-30T15:31:13.287 に答える
0

私はこのようなことをしました。次のような MAKE_THINGS というマクロを作成します。

#MAKE_THINGS の定義 \
  MAKE_THING(NAME1) \
  MAKE_THING(NAME2) \
  MAKE_THING(NAME3) \
  /* リストの後に意味的に空行を含める */

次に、MAKE_THING を定義し、宣言を開始し、MAKE_THINGS を呼び出し、宣言を終了し、MAKE_THING を未定義にすることができます。必要に応じて、それぞれに複数の属性を含めることができます。

例えば:

#MAKE_THINGS の定義 \
  MAKE_THING(HAPPY,"ハッピー") \
  MAKE_THING(SAD_AND_BLUE,"悲しい青") \
  MAKE_THING(SLEEPY,"眠い") \
  /* リストの後に意味的に空行を含める */

#define MAKE_THING(x,y) NUMBER_##x,
typedef enum {MAKE_THINGS LAST_THING} THING_NUMBER;
#undef MAKE_THING

#define MAKE_THING(x,y) FLAG_##x = (1L << NUMBER##x),
typedef enum {MAKE_THINGS ALL_FLAGS = (1 << LAST_THING)-1} THING_FLAGS;
#undef MAKE_THING

#define MAKE_THING(x,y) const char *MSG_##x = y;
ものを作ります
#undef MAKE_THING

#define MAKE_THING(x,y) MSG_##x,
const char *thing_names[] = {MAKE_THINGS 0};
#undef MAKE_THING

すべての宣言が自動的に並列に保たれることに注意してください。各オブジェクトは、数値 (0..N-1)、フラグ (その数値に対応するビット)、メッセージ文字列、およびメッセージ文字列の配列内の場所を取得します。

于 2010-06-30T18:01:06.057 に答える
0

マクロ/プリプロセッサなどの代わりに、オリジナルと同様の静的テキスト ファイルを使用します。理解し、拡張または維持するのがはるかに簡単に思えます。各値を前の値のシフトとして定義することもできますが、それは一部の読者にとってより混乱を招く可能性があります。ユーザーによるタイプミスを防ぐために、A、B、C (2 回定義)、D の代わりに、各 "op" に対してより詳細な省略形を使用することをお勧めします。

于 2010-06-30T17:26:52.473 に答える