C++ 需要元编程魔法以无错误的方式定义位字段的母矿脉吗
目标是控制允许哪些类型的用户在UI级别执行哪些操作。该代码已经存在一段时间了;我只是想改进一下。 我试图改进的文件可能应该是自动生成的,但这将是一个太大的变化,所以我寻求一个更简单的解决方案 我们称之为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
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