[英]c++ metaprogramming madness
consider the following templated datastructures 考虑以下模板化数据结构
enum eContent{
EINT = 1,
EFLOAT = 2,
EBOOL = 4
};
template<int>
struct Container{
Container(){assert(false);} //woops, don't do that!
};
template<>
struct Container<EINT>{
Container():i(123){}
int i;
};
template<>
struct Container<EFLOAT>{
Container():f(123.456f){}
float f;
};
template<>
struct Container<EBOOL>{
Container():b(true){}
bool b;
};
<fancy macro goes here that creates me all kind of combinations including for example>
template<>
struct Container<EFLOAT | EBOOL>: public Container<EFLOAT>, public Container<EBOOL>{
Container():Container<EFLOAT>(),Container<EBOOL>(){}
};
</fancy macro>
such that I then can for example define a variable like this: 这样我就可以例如定义一个这样的变量:
Container<EINT|EFLOAT|EBOOL> myVar;
how would I define this fancy macro? 我将如何定义这个奇特的宏?
Why I want this? 为什么我要这个? Let it be for the sake of fun and learning metaprogramming 为了乐趣和学习元编程而去
Well, first off, ||
好吧,首先, ||
is the boolean or operator; 是布尔值或运算符; when used as you have used it, it'll always result in 1
(or true
, rather, but true
is always promoted to 1
when cast to int
as it is in this case) which in the case of your code is equal to EINT
, so your template would always instantiate as Container<EINT>
. 当您使用它时,它将始终为1
(或true
,而不是,但是当EINT
为int
时, true
总是提升为1
,在这种情况下),在您的代码中等于EINT
因此,您的模板将始终实例化为Container<EINT>
。
Presumably, you're looking for the bitwise or operator, |
想必您正在寻找按位或运算符, |
. 。 Even then, the compiler is going to actually bitwise-or the values, so you'll get a value of 7
which will result in the unspecialized template being used, which will fail. 即使这样,编译器实际上仍将按位或值,所以您将获得7
的值,这将导致使用未专业化的模板,这将失败。
Exactly what are you trying to accomplish? 您到底想完成什么? There are ways to make a type that's flexible enough to hold multiple data of multiple types, but the or operator doesn't do anything remotely like what you want in the context of template arguments. 有多种方法可以使类型足够灵活,以容纳多种类型的多个数据,但是or运算符不会像在模板参数的上下文中那样远程执行任何操作。
enum eContent{
eInt = 1,
eFloat = 2,
eBool = 4
};
template<unsigned, unsigned>
struct Member {};
template<>
struct Member<eInt, eInt>{
Member():i(123){}
unsigned i;
};
template<>
struct Member<eFloat, eFloat>{
Member():f(123.456f){}
float f;
};
template<>
struct Member<eBool, eBool>{
Member():b(true){}
bool b;
};
template< unsigned members >
struct Container
: Member< members & eInt, eInt >
, Member< members & eFloat, eFloat >
, Member< members & eBool, eBool >
{};
int main()
{
Container< eFloat | eBool > c;
c.f; // OK
c.b; // OK
c.i; // !Nah
}
But I don't think it's good for anything, really, it's just a solution to the literal problem you stated. 但是,我认为这对任何事情都没有好处,实际上,这只是您所说的字面问题的解决方案。
If you have some real problem in mind (for which you think this could be a solution), try to ask about that. 如果您有一些实际的问题(您认为这可能是一个解决方案),请尝试提出有关问题。
Unless it's just play, or homework, of course. 当然,除非只是玩耍或做作业。 :-) :-)
Cheers & hth., 干杯,……
PS: As a matter of good C++ programming practice, reserve ALL UPPERCASE names for macros, and only for macros. PS:作为良好的C ++编程惯例,请将所有大写名称保留给宏,并且仅保留给宏。 That way you avoid many potential name collisions. 这样,您可以避免许多潜在的名称冲突。 Using ALL UPPERCASE for constants is a Java/Python/etc. 将ALL UPPERCASE用作常量是Java / Python / etc。 convention, to some degree suitable for those languages, but decidedly not for C++. 约定,某种程度上适合那些语言,但绝对不适合C ++。 It stems from early C, where constants had to be expressed as macros. 它起源于早期的C,其中常量必须表示为宏。 ALL UPPERCASE was (and is) used for macros, not for constants -- well, except Brian Kernighan, but let's not delve into history... ;-) 所有大写都是(并且是)用于宏的,而不是常量的-恩,除了布莱恩·科尼根(Brian Kernighan),但让我们不探究历史... ;-)
如果您在做我想做的事,请看一下boost::variant
,它确实在做您想做的事情。
ok, I use your Container structs, combine them with XCont, and define the XContainer you want: 好的,我使用您的Container结构,将它们与XCont结合在一起,然后定义所需的XContainer:
// a (bit-)LIST is an int that contains the value (TAIL<<1|HEAD),
// where TAIL is a LIST, and HEAD is either 1 or 0.
// while iterating through the LIST from right,
// SHL counts how far each consumed HEAD has to be shifted left,
// back to its original position.
template<int TAIL,int HEAD,int SHL>
struct XCont;
//the empty LIST
template<int SHL>
struct XCont<0,0,SHL>{};
//HEAD equals 0, so we just recurse through the TAIL.
template<int TAIL,int SHL>
struct XCont<TAIL,0,SHL>:public XCont< (TAIL>>1) , (TAIL&1) , (SHL+1) >{};
//HEAD equals 1, so we do like above, but we have to append Container< (1<<SHL) >.
template<int TAIL,int SHL>
struct XCont<TAIL,1,SHL>:public XCont< (TAIL>>1) , (TAIL&1) , (SHL+1) >,public Container< (1<<SHL) >{};
template<int E>
struct XContainer:public XCont< (E>>1) , (E&1) , (0) >{};
It works like this: 它是这样的:
These do equal: 这些确实相等:
C++ templates behave like pattern-matching in Haskell. C ++模板的行为类似于Haskell中的模式匹配。 So, to me, it is easier to think about it in a simple style of Haskell functions without any fancy Haskell things. 因此,对我来说,用一种简单的Haskell函数样式进行思考就比较容易,而无需花哨的Haskell事物。 If anyone is curious: 如果有人好奇:
xcontainer :: Int -> String
xcontainer(e) = "struct XContainer:" ++ (
xcont( (e .>>. 1) , (e .&. 1) , (0) )
) ++ "{}"
xcont :: (Int,Int,Int) -> String
xcont( 0,0,shl) = "public XCont<0,0," ++ show(shl) ++ ">"
xcont(tail,0,shl) = ( xcont( (tail .>>. 1) , (tail .&. 1) , (shl+1) )
)
xcont(tail,1,shl) = ( xcont( (tail .>>. 1) , (tail .&. 1) , (shl+1) )
) ++ "," ++ container(1 .<<. shl)
container :: Int -> String
container(e) = "public Container<" ++ show(e) ++ ">"
(This is valid Haskell but in a non-haskell writing style.) (这是有效的Haskell,但采用非haskell写作风格。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.