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

IFAIK,boost 预处理器库http://www.boost.org/doc/libs/1_43_0/libs/preprocessor/doc/index.html具有您需要的所有原语。

于 2010-06-30T15:31:13.287 回答
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 回答
0

而不是宏/预处理器/等,我会坚持使用与原始文件类似的静态文本文件。它似乎更容易理解、扩展或维护。我可能会将每个值定义为前一个值的偏移,但这对某些读者来说似乎更令人困惑。为了帮助防止用户输入错误,我可能会为每个“操作”使用更详细的缩写,而不是 A、B、C(定义两次)、D...

于 2010-06-30T17:26:52.473 回答
0

我做过这样的事情。创建一个名为 MAKE_THINGS 的宏,如下所示:

#define MAKE_THINGS \
  MAKE_THING(NAME1)\
  MAKE_THING(NAME2)\
  MAKE_THING(NAME3) \
  /* 在列表后包含语义空白行 */

然后可以定义 MAKE_THING,开始声明,调用 MAKE_THINGS,结束声明,然后取消定义 MAKE_THING。如果需要,每个事物都可以包含多个属性:

例如:

#define MAKE_THINGS \
  MAKE_THING(快乐,“快乐”)\
  MAKE_THING(SAD_AND_BLUE,"悲伤和蓝色") \
  MAKE_THING(睡着了,“睡着了”)\
  /* 在列表后包含语义空白行 */

#define MAKE_THING(x,y) NUMBER_##x,
typedef 枚举 {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) 味精_##x,
const char *thing_names[] = {MAKE_THINGS 0};
#undef MAKE_THING

请注意,所有声明都自动保持并行。每个事物都有一个数字 (0..N-1)、一个标志(对应于它的数字的位)、一个消息字符串和一个消息字符串数组中的位置。

于 2010-06-30T18:01:06.057 回答