C++ 需要元编程魔法以无错误的方式定义位字段的母矿脉吗

C++ 需要元编程魔法以无错误的方式定义位字段的母矿脉吗,c++,macros,metaprogramming,bit-fields,C++,Macros,Metaprogramming,Bit Fields,目标是控制允许哪些类型的用户在UI级别执行哪些操作。该代码已经存在一段时间了;我只是想改进一下。 我试图改进的文件可能应该是自动生成的,但这将是一个太大的变化,所以我寻求一个更简单的解决方案 我们称之为PermissionBits.h的文件包含以下内容: // Here names are mangled; for example XYZ_OP_A is: // permission to operation A in category/context XYZ // SCU64 = static

目标是控制允许哪些类型的用户在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 
}
//这里的名字被弄乱了;例如,XYZ_OP_A是:
//在类别/上下文XYZ中操作A的权限
//SCU64=无符号的静态常量\uuu int64
//有些名称空间使用所有64位
//实际值(只要它们是正确的位字段)
//没关系-它们总是按名称使用
命名空间XYZPermissionBits
{

SCU64 XYZ_OP_A=1UI64IFAIK,boost预处理器库具有您所需的所有原语。

如果您决定采用代码生成路线,我建议您看看

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]]]
}
名称空间XYZPermissionBits
{
/*齿轮
进口齿轮
操作=[“A”、“B”、“C”、“D”,
“E”、“F”、“G”、“H”,
“I”、“J”、“K”、“L”]

断言2使用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) \
  }

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

我做了如下操作。创建一个名为MAKE_THINGS的宏,如下所示:

#define MAKE_THINGS \ MAKE_THING(NAME1) \ MAKE_THING(NAME2) \ MAKE_THING(NAME3) \ /* Include semantically-blank line after list */ #定义制造事物\ 制造东西(名称1)\ 制造东西(名称2)\ 制造东西(名称3)\ /*在列表后包含语义上的空行*/ 然后可以定义MAKE\u THINGS、启动声明、调用MAKE\u THINGS、结束声明和取消定义MAKE\u THINGS。如果需要,每个事物可以包含多个属性:

例如:

#define MAKE_THINGS \ MAKE_THING(HAPPY,"Happy") \ MAKE_THING(SAD_AND_BLUE,"Sad and blue") \ MAKE_THING(SLEEPY,"Sleepy") \ /* Include semantically-blank line after list */ #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; MAKE_THINGS #undef MAKE_THING #define MAKE_THING(x,y) MSG_##x, const char *thing_names[] = {MAKE_THINGS 0}; #undef MAKE_THING #定义制造事物\ 使事物(快乐,“快乐”)\ 制造东西(悲伤和忧郁,“悲伤和忧郁”)\ 制造东西(困倦,“困倦”)\ /*在列表后包含语义上的空行*/ #定义MAKE_THING(x,y)NUMBER_##x, typedef枚举{MAKE_THINGS LAST_THING}THINGS_NUMBER; #未定义的MAKE_东西
#定义MAKE_THING(x,y)标志35;#x=(1L举个例子就好了。我可以从该库中窃取一个头文件吗?或者我必须安装一些东西吗?它仍然是标准的预处理器还是其他魔法?我在Windows上使用VS2010。如果没有Boost预处理器,将需要大量几乎相同的宏,这将比您原来的问题更容易出错m除非您使用代码生成来创建它们…Boost预处理器完全是用头实现的。您应该能够将它们直接合并到您的项目中,但是Boost还有很多其他优点,所以可能值得保留整个内容。@cog,关于您的第二条评论-那么…Boost预处理器有什么不同?您会吗boost不是必须创建数千个宏,也不是这样做的,只是这些宏已经在使用并经过了大量测试?@Hamish,boost是标准代码。虽然它有时会将编译器推向极限,但它通常包含变通功能,或者为不合规的编译器(尤其是mainlin)提供简单的降级功能我不认为这一部分很难突破极限,它只是一段乏味的代码。如果你真的能测试它,那就太棒了!谢谢,我只想澄清一下:为什么在名称空间名称后需要逗号,而不是在标志名称后?括号也被定义为宏:)?@Ham:namespace name是第一个参数,逗号将其与第二个参数分开:所有用paranteses括起来的标志名形成一个参数,并且是一个,然后可以用
BOOST\u PP\u SEQ.
@Ham:如果您感兴趣,可以在这些序列上添加一个简短的字符。有一点我必须说,以支持确定与您的第一个代码段中一样,每个权限的值都是单独的,即如果有人重新调整了权限的用途或决定取消权限,则会减少这些值更改的机会。如果您在数据库中存储标志,这可能是一个非常重要的考虑因素。顺便说一句,我注意到在您的第一个代码段中,您定义了两次XYZ_OP_C。我认为这是一个故意的错误,以表明手工操作是多么脆弱@戴夫,哈哈,不,我试着把它弄对,但在问这个问题时弄糟了。我粘贴了实际的代码,然后重新命名了东西,以防万一,使它更适合页面。我会保持原样,除非有很多对象。复制和粘贴的问题是真实的。这不仅仅是学术标准上的不洁,它实际上会咬人:)我真的要问,如果你知道的话你需要这么多,这是一个暗示,常数不是正确的方法。特别是像这样的字母顺序的。你到底为什么需要这个?“像这样的字母顺序常数”实际上没有被使用。这很容易被忽视,但问题提到了这一点。我尽量少分享实际代码,即使它很臭。我一直以完全重写的想法自娱自乐,但这不会发生;我能做的最好的事情就是尝试改进一个老家伙。等等,你发布的是源代码和结果的结合,哦r是cog“自我修改”,如果这有意义的话?@Hamish:是的,正如你所说,cog是自我修改的-cog输出包括原始文件和 #define MAKE_THINGS \ MAKE_THING(NAME1) \ MAKE_THING(NAME2) \ MAKE_THING(NAME3) \ /* Include semantically-blank line after list */ #define MAKE_THINGS \ MAKE_THING(HAPPY,"Happy") \ MAKE_THING(SAD_AND_BLUE,"Sad and blue") \ MAKE_THING(SLEEPY,"Sleepy") \ /* Include semantically-blank line after list */ #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; MAKE_THINGS #undef MAKE_THING #define MAKE_THING(x,y) MSG_##x, const char *thing_names[] = {MAKE_THINGS 0}; #undef MAKE_THING