Reputation: 5265
I have the following code (as is):
template<class T, class FieldT>
using addRefU = typename std::conditional<
std::is_rvalue_reference<T>::value,
typename std::add_rvalue_reference< FieldT >::type,
typename std::conditional<
std::is_rvalue_reference<FieldT>::value,
typename std::add_rvalue_reference< FieldT >::type,
typename std::add_lvalue_reference< FieldT >::type
>::type
>::type;
#define VA_NARGS_IMPL(_1, _2, _3, _4, _5, N, ...) N
#define VA_NARGS(...) VA_NARGS_IMPL(X,##__VA_ARGS__, 4, 3, 2, 1, 0)
#define VARARG_IMPL2(base, count, ...) base##count(__VA_ARGS__)
#define VARARG_IMPL(base, count, ...) VARARG_IMPL2(base, count, __VA_ARGS__)
#define VARARG(base, ...) VARARG_IMPL(base, VA_NARGS(__VA_ARGS__), __VA_ARGS__)
#define REF2(val, p1) addRefU<decltype(val), decltype(val.p1)>
#define REF3(val, p1, p2) addRefU<decltype(val), decltype(val.p1.p2)>
#define REF4(val, p1, p2, p3) addRefU<decltype(val), decltype(val.p1.p2.p3)>
#define REF5(val, p1, p2, p3, p4) addRefU<decltype(val), decltype(val.p1.p2.p3.p4)>
#define REF(...) VARARG(REF, __VA_ARGS__) // It says REF is not defined here
#define CAST_REF2(val, p1) static_cast<REF(val, p1)>(val.p1)
#define CAST_REF3(val, p1, p2) static_cast<REF(val, p1, p2)>(val.p1.p2)
#define CAST_REF4(val, p1, p2, p3) static_cast<REF(val, p1, p2, p3)>(val.p1.p2.p3)
#define CAST_REF5(val, p1, p2, p3, p4) static_cast<REF(val, p1, p2, p3, p4)>(val.p1.p2.p3.p4)
#define CAST_REF(...) VARARG(CAST_REF, __VA_ARGS__)
struct A{};
struct B{A a;};
int main()
{
B b;
using t = REF(b, a); // Ok
auto &&k = CAST_REF2(b, a); // work
auto &&k1 = CAST_REF(b, a); // NOT work
return 0;
}
http://coliru.stacked-crooked.com/a/8dff49f68b7e15e1
If I change
#define CAST_REF2(val, p1) static_cast<REF(val, p1)>(val.p1)
to
#define CAST_REF2(val, p1) static_cast<REF2(val, p1)>(val.p1)
(change REF
to REF2
)
It works.
I don't understand why I can call REF
directly, and can't do this from macro.
Upvotes: 1
Views: 533
Reputation: 385274
Your code (sans includes) preprocesses to the following:
struct A{};
struct B{A a;};
int main()
{
B b;
using t = addRefU<decltype(b), decltype(b.a)>
auto &&k = static_cast<VARARG(REF, b, a)>(b.a);
auto &&k1 = static_cast<addRefU<decltype(b), decltype(b.a)> >(b.a);
return 0;
}
Clearly, VARARG
hasn't been expanded, and this is because it was already expanded previously in the expansion of CAST_REF
(which isn't the case for CAST_REF2
):
[C++11: 16.3.4/2]:
If the name of the macro being replaced is found during this scan of the replacement list (not including the rest of the source file’s preprocessing tokens), it is not replaced. Furthermore, if any nested replacements encounter the name of the macro being replaced, it is not replaced. These nonreplaced macro name preprocessing tokens are no longer available for further replacement even if they are later (re)examined in contexts in which that macro name preprocessing token would otherwise have been replaced.
The intent of the rule is to prohibit infinite recursions, even though you're not actually at risk of one in this particular case.
This has been discussed on Stack Overflow before, but I'm not entirely sure how you can get around it, at this point (other than by copying VARARG
).
Upvotes: 3