[mpl] trouble with conditionals
I'm struggling with a problem of how to correctly use conditionals in
MPL. In a nutshell, the problem is that the compiler greedily
evaluates all parts of a conditional, even the branch that isn't
followed. This is a problem not just for performance, but because
sometimes the unused branch might not compile.
I know that eval_if is supposed to address this problem, but unless
I'm missing something, it doesn't seem to be a complete solution. It
delays evaluating the branches until one of them is selected, but the
compiler still evaluates the *arguments* to both branches. If the
arguments themselves don't compile, I'm screwed, despite the fact that
the parts of my code that are actually relevant (the branches that get
used) are perfectly correct.
How do I resolve this problem?
Here's a toy example, a metafunction which, given a map, finds the
value associated with the key "char" and, if it exists, increments the
associated value (which we assume is numeric):
template <typename m_map>
class toy_metafunction
{
typedef typename
if_
--- Geoffrey Romer wrote:
I know that eval_if is supposed to address this problem, but unless I'm missing something, it doesn't seem to be a complete solution. It delays evaluating the branches until one of them is selected, but the compiler still evaluates the *arguments* to both branches. If the arguments themselves don't compile, I'm screwed, despite the fact that the parts of my code that are actually relevant (the branches that get used) are perfectly correct.
How do I resolve this problem?
The arguments will be evaluated if they have typename metaf_<...>::type syntax inside them.
Here's a toy example, a metafunction which, given a map, finds the value associated with the key "char" and, if it exists, increments the associated value (which we assume is numeric):
template <typename m_map> class toy_metafunction { typedef typename if_
::type, typename insert , at ::type > >::type, m_map ::type type; };
Try:
template <typename m_map>
struct toy_metafunction
{
typedef typename eval_if<
has_key
OK, your correction works, but I don't understand why. I thought that
omitting the typename ... ::type syntax just creates a null-ary
metafunction. For example,
pair
Geoffrey Romer
OK, your correction works, but I don't understand why. I thought that omitting the typename ... ::type syntax just creates a null-ary metafunction. For example,
pair
, at > seems to me like it would produce not a pair consisting of a char and an int_, but a pair consisting of a char and a null-ary metafunction which, when applied, produces an int_. Am I missing something?
The 2nd argument to plus above probably isn't what you think. You're
passing it a nullary metafunction: at
On 10/5/05, David Abrahams
Geoffrey Romer
writes: OK, your correction works, but I don't understand why. I thought that omitting the typename ... ::type syntax just creates a null-ary metafunction. For example,
pair
, at > seems to me like it would produce not a pair consisting of a char and an int_, but a pair consisting of a char and a null-ary metafunction which, when applied, produces an int_. Am I missing something?
The 2nd argument to plus above probably isn't what you think. You're passing it a nullary metafunction: at
.
Right, exactly, and as I understand it, that metafunction is never actually evaluated (at least, not in the above code), so the result is, as I said, a pair consisting of char and a nullary metafunction, not a char and an int_. If so, doesn't that mean Cromwell's fix is incorrect? I'm feeling generally very confused about the circumstances in which one does and does not use the typename...::type syntax. In addition to this problem, I'm finding that placeholder expressions seem to fail to compile if they contain typename...::type in places where I would expect it to be necessary, and when I remove that syntax they seem to produce correct results, even though they shouldn't.
Geoffrey Romer
Right, exactly, and as I understand it, that metafunction is never actually evaluated (at least, not in the above code), so the result is, as I said, a pair consisting of char and a nullary metafunction, not a char and an int_.
That's correct.
If so, doesn't that mean Cromwell's fix is incorrect?
I'm afraid I've not seen his fix.
I'm feeling generally very confused about the circumstances in which one does and does not use the typename...::type syntax.
C++ Template Metaprogramming has a whole appendix dedicated to that question, which some have told me is the clearest explanation they've seen. -- Dave Abrahams Boost Consulting www.boost-consulting.com
If so, doesn't that mean Cromwell's fix is incorrect?
I'm afraid I've not seen his fix.
It was given in the grandparent of your first mail in this thread. If you like I can forward it to you privately, I'd prefer not to clutter the list by re-sending it.
I'm feeling generally very confused about the circumstances in which one does and does not use the typename...::type syntax.
C++ Template Metaprogramming has a whole appendix dedicated to that question, which some have told me is the clearest explanation they've seen.
Assuming you're referring to Appendix B, I've read it, but I don't think it addresses my confusion. That appendix addresses the issue of when the C++ language requires one to use the typename and template keywords. I think I understand that. What I don't understand is when I should be saying "typename metafunc<args>::type" and when I should be using plain "metafunc<args>". This isn't a language-correctness issue, it's an MPL usage issue.
Geoffrey Romer
If so, doesn't that mean Cromwell's fix is incorrect?
I'm afraid I've not seen his fix.
It was given in the grandparent of your first mail in this thread. If you like I can forward it to you privately, I'd prefer not to clutter the list by re-sending it.
I'm also afraid I don't have time to look at it right now.
I'm feeling generally very confused about the circumstances in which one does and does not use the typename...::type syntax.
C++ Template Metaprogramming has a whole appendix dedicated to that question, which some have told me is the clearest explanation they've seen.
Assuming you're referring to Appendix B, I've read it, but I don't think it addresses my confusion. That appendix addresses the issue of when the C++ language requires one to use the typename and template keywords. I think I understand that. What I don't understand is when I should be saying "typename metafunc<args>::type" and when I should be using plain "metafunc<args>". This isn't a language-correctness issue, it's an MPL usage issue.
Oh. Maybe mapping into the runtime world will help. If you think of
metafunc as a regular function, then "metafunc
On 10/6/05, David Abrahams
Geoffrey Romer
writes: If so, doesn't that mean Cromwell's fix is incorrect?
I'm afraid I've not seen his fix.
I'm also afraid I don't have time to look at it right now.
Fair enough. It's sort of a side issue anyway.
What I don't understand is when I should be saying "typename metafunc<args>::type" and when I should be using plain "metafunc<args>". This isn't a language-correctness issue, it's an MPL usage issue.
Oh. Maybe mapping into the runtime world will help. If you think of metafunc as a regular function, then "metafunc
::type" is equivalent to a regular function call, and "metafunc " is equivalent to boost::bind(func, args...) These are exact analogies, AFAICT.
OK, that's what I thought, but it seems not to apply to lambda-expressions. If I want to call foo<> on a placeholder, I seem to be required to write foo<_1>, and not typename foo<_1>::type, even though I want foo<> to actually be applied to the placeholder (after substitution). What am I missing?
Geoffrey Romer
On 10/6/05, David Abrahams
wrote: Geoffrey Romer
writes: If so, doesn't that mean Cromwell's fix is incorrect?
I'm afraid I've not seen his fix.
I'm also afraid I don't have time to look at it right now.
Fair enough. It's sort of a side issue anyway.
What I don't understand is when I should be saying "typename metafunc<args>::type" and when I should be using plain "metafunc<args>". This isn't a language-correctness issue, it's an MPL usage issue.
Oh. Maybe mapping into the runtime world will help. If you think of metafunc as a regular function, then "metafunc
::type" is equivalent to a regular function call, and "metafunc " is equivalent to boost::bind(func, args...) These are exact analogies, AFAICT.
OK, that's what I thought, but it seems not to apply to lambda-expressions. If I want to call foo<> on a placeholder
I don't know what you mean, but I'm pretty sure you don't mean what you said. Nobody but the MPL internals want to actually invoke metafunctions on placeholders. Very often that will cause an error, because the placeholder does not meet the expectations of the metafunction for the type that is actually supposed to be passed to it.
I seem to be required to write foo<_1>, and not typename foo<_1>::type, even though I want foo<> to actually be applied to the placeholder (after substitution). What am I missing?
Well, it's the "after substitution" bit. For the reasons described
above, you can't in general instantiate foo<_1>, and even if you did,
the result might be int, e.g.
template <class T>
struct foo
: mpl::if_
On 10/6/05, David Abrahams
Geoffrey Romer
writes: On 10/6/05, David Abrahams
wrote: Oh. Maybe mapping into the runtime world will help. If you think of metafunc as a regular function, then "metafunc
::type" is equivalent to a regular function call, and "metafunc " is equivalent to boost::bind(func, args...) These are exact analogies, AFAICT.
OK, that's what I thought, but it seems not to apply to lambda-expressions. If I want to call foo<> on a placeholder
I don't know what you mean, but I'm pretty sure you don't mean what you said. Nobody but the MPL internals want to actually invoke metafunctions on placeholders. Very often that will cause an error, because the placeholder does not meet the expectations of the metafunction for the type that is actually supposed to be passed to it.
Let me put it like this: suppose I have some metaprogramming expression like
typename foo
Geoffrey Romer
On 10/6/05, David Abrahams
wrote: Geoffrey Romer
writes: On 10/6/05, David Abrahams
wrote: Oh. Maybe mapping into the runtime world will help. If you think of metafunc as a regular function, then "metafunc
::type" is equivalent to a regular function call, and "metafunc " is equivalent to boost::bind(func, args...) These are exact analogies, AFAICT.
OK, that's what I thought, but it seems not to apply to lambda-expressions. If I want to call foo<> on a placeholder
I don't know what you mean, but I'm pretty sure you don't mean what you said. Nobody but the MPL internals want to actually invoke metafunctions on placeholders. Very often that will cause an error, because the placeholder does not meet the expectations of the metafunction for the type that is actually supposed to be passed to it.
Let me put it like this: suppose I have some metaprogramming expression like
typename foo
::type Now suppose I want to apply this operation to a sequence of types, rather than to baz, using transform<>. To do this, I need to convert it to a lambda-expression to pass to transform<>. Intuitively, I would expect this to be done by replacing baz with _1:
typename foo
::type >::type But this doesn't compile for the reasons you explain. You seem to be telling me to eliminate the typename...::type on metafunction calls that contain actual placeholders as arguments (and in practice this seems to work), which leads to:
typename foo
>::type I take it that's correct? If so, suppose now that I have another piece of code:
No, that won't work. There's still a direct invocation there. Take
this as a general rule: you never want to ask for the nested ::type of
a template that has a placeholder in any of its arguments, unless that
template is specifically designed to operate on lambda expressions.
I assume foo is not one of those. To translate the above into a
lambda expression:
foo
typename foo
::type In other words, "apply foo to the nullary metafunction bar<baz>". Now suppose again that I want to turn this into an equivalent lambda-expression. Intuitively, I would again expect to do this by substituting _1 for baz:
typename foo
>::type. However, this is identical to the lambda-expression for my first example, which has different semantics, so one of the two must be wrong. Which is wrong, and how can I fix it?
They're both wrong. The first is wrong for the reasons already
described. The second is wrong for the same reasons, and even after
you strip the ::type, you have other problems:
The MPL lambda expression mechanism will recognize bar<_1>
as something that demands substitution, perform the substitution, then
look for a nested ::type. When it finds that (it will, since bar is a
unary metafunction), that ::type will be passed on to foo.
AFAIK, the only way to get that case to work is to write a new
metafunction that does what you want:
template<class T>
struct foo_of_lazy_bar
{
// a nullary metafunction
typedef bar<T> lazy_bar;
// apply foo to that nullary metafunction
typedef typename foo
Thanks for all your help, by the way. I'm being a bit stubborn about this because I want to make sure I really know what's going on here.
You're welcome. -- Dave Abrahams Boost Consulting www.boost-consulting.com
Geoffrey Romer writes:
I'm struggling with a problem of how to correctly use conditionals in MPL. In a nutshell, the problem is that the compiler greedily evaluates all parts of a conditional, even the branch that isn't followed. This is a problem not just for performance, but because sometimes the unused branch might not compile.
I know that eval_if is supposed to address this problem, but unless I'm missing something, it doesn't seem to be a complete solution. It delays evaluating the branches until one of them is selected, but the compiler still evaluates the *arguments* to both branches. If the arguments themselves don't compile, I'm screwed, despite the fact that the parts of my code that are actually relevant (the branches that get used) are perfectly correct.
Correct.
How do I resolve this problem?
The most straightforward solution is to factor out the computation into a separate auxiliary metafunction...
Here's a toy example, a metafunction which, given a map, finds the value associated with the key "char" and, if it exists, increments the associated value (which we assume is numeric):
template <typename m_map> class toy_metafunction { typedef typename if_
::type, typename insert , at >::type > >::type, m_map ::type type; };
... like this: template< typename Map, typename Key > struct increment_key_counter : insert< Map , pair< Key , typename plus< int_<1>, typename at
participants (4)
-
Aleksey Gurtovoy
-
Cromwell Enage
-
David Abrahams
-
Geoffrey Romer