In order to nicely fix https://github.com/ned14/outcome/issues/244#issuecomment-774181015, I want to know if it is possible to detect whether a token sequence is present within a C preprocessor macro argument?
#define OUTCOME_TRY_GLUE2(x, y) x##y
#define OUTCOME_TRY_GLUE(x, y) OUTCOME_TRY_GLUE2(x, y)
#define OUTCOME_TRY_UNIQUE_NAME OUTCOME_TRY_GLUE(unique, __COUNTER__)
#define OUTCOME_TRYV2_SUCCESS_LIKELY(unique, ...) \
auto &&unique = (__VA_ARGS__)
#define OUTCOME_TRY2_SUCCESS_LIKELY(unique, v, ...) \
OUTCOME_TRYV2_SUCCESS_LIKELY(unique, __VA_ARGS__); \
v = std::move(unique).value()
#define OUTCOME_TRYA(v, ...) OUTCOME_TRY2_SUCCESS_LIKELY(OUTCOME_TRY_UNIQUE_NAME, v, __VA_ARGS__)
/* I'd like this to generate:
auto unique0 = (expr); auto x = std::move(unique0).value();
*/
OUTCOME_TRYA(auto x, expr);
/* I'd like this to generate:
auto &&unique1 = (expr); auto &&x = std::move(unique1).value();
*/
OUTCOME_TRYA(auto &&x, expr);
(https://godbolt.org/z/MW74cG may be more useful)
What one needs to achieve here is detection of &&
within the expansion of the v
macro argument, but not if it is nested within ()<>"
.
Other answers on Stackoverflow can find a string within a C preprocessor macro argument, however those techniques can't work here because macro token paste of STRING_ ## &&
isn't legal. This level of C preprocessor hackery is beyond my capabilities, so I ask Stackoverflow. Thanks in advance for any help.
What one needs to achieve here is detection of && within the expansion of the v macro argument, but not if it is nested within ()<>".
No, it is not possible. I suggest separating the type from the name in a separate argument.
OUTCOME_TRYA_NEW(auto, x, expr);
OUTCOME_TRYA_NEW(auto &&, x, expr);
You could move the detection to runtime with stringify, like:
#define OUTCOME_TRYA(v, ...) \
if constexpr (your_constexpr_strstr(#v, "&&")) { \
/* do stuff with `auto &&` here */ \
} else { \
/* do stuff with `auto` here */ \
}
(or I think also with some std::is_same(decltype(v)....)
stuff), but as you want to declare variables in the macro expansion, I think that's not helpful.
Another way you could use is to delay the expansion of first argument to later phase so you could overload it on number of arguments, like so:
// Add a comma. But later.
#define OUTCOME_REF(name) &&, name
#define OUTCOME_TRYA_CHOOSE_1(name, ...) \
auto unique0 = (__VA_ARGS__); name = std::move(unique0).value();
#define OUTCOME_TRYA_CHOOSE_2_IN(name, ...) \
auto &&unique1 = (__VA_ARGS__); name = std::move(unique1).value();
#define OUTCOME_TRYA_CHOOSE_2(name, ...) \
OUTCOME_TRYA_CHOOSE_2_IN(name __VA_ARGS__)
// ^^^^^^ I have no idea why it works without a comma, but it does. It's scary.
#define OUTCOME_TRYA_CHOOSE_N(_1,_2,N,...) \
OUTCOME_TRYA_CHOOSE_##N
#define OUTCOME_TRYA_CHOOSE(...) \
OUTCOME_TRYA_CHOOSE_N(__VA_ARGS__,2,1)
#define OUTCOME_TRYA(name, ...) \
OUTCOME_TRYA_CHOOSE(name)(name, __VA_ARGS__)
// ^^^^ - overload on number of arguments in expansion of first parameter
OUTCOME_TRYA(auto x, expr1) // auto unique0 = (expr1); auto x = std::move(unique0).value();
OUTCOME_TRYA(auto OUTCOME_REF(x), expr2) // auto &&unique1 = (expr2); auto && x = std::move(unique1).value();
In similar fashion you could refactor your interface seamlessly to:
#define UNPACK(...) __VA_ARGS__
#define OUTCOME_TRYA_CHOOSE_1(name, nameunpacked, ...) \
auto unique0 = (__VA_ARGS__); name = std::move(unique0).value();
#define OUTCOME_TRYA_CHOOSE_2_IN(name1, name2, ...) \
auto &&unique1 = (__VA_ARGS__); name1 name2 = std::move(unique1).value();
#define OUTCOME_TRYA_CHOOSE_2(name, nameunpacked, ...) \
OUTCOME_TRYA_CHOOSE_2_IN(nameunpacked, __VA_ARGS__)
#define OUTCOME_TRYA_CHOOSE_N(_1,_2,_3,N,...) \
OUTCOME_TRYA_CHOOSE_##N
#define OUTCOME_TRYA_CHOOSE(n, ...) \
OUTCOME_TRYA_CHOOSE_N(n, __VA_ARGS__, 2, 1)
#define OUTCOME_TRYA(name, ...) \
OUTCOME_TRYA_CHOOSE(name, UNPACK name)(name, UNPACK name, __VA_ARGS__)
OUTCOME_TRYA(auto x, expr1, expr2)
OUTCOME_TRYA((auto &&, x), expr3, expr4)
The above looks nice and allows for the current interface to stay. The trick is in OUTCOME_TRYA_CHOOSE(name, UNPACK name)
- when name
is (something, something)
, then OUTCOME_TRYA_CHOOSE_N
is passed 3 arguments, if it's not, then 2 arguments are passed - which can be then overloaded on number of arguments. So it effectively overloads if the input contains braces with a comma inside.
I don't suppose you know of a way to achieve OUTCOME_TRYV((auto &&), expr1, expr2)
Meh ;p . Add a comma when unpacking, effectively shifting overloads to one more.
#define UNPACK_ADD_COMMA(...) ,__VA_ARGS__
#define OUTCOME_TRYA_CHOOSE_1(name, nameunpacked, ...) \
auto unique0 = (__VA_ARGS__); name = std::move(unique0).value();
#define OUTCOME_TRYA_CHOOSE_2_IN(name1, ...) \
OCH_MY_GOD()
#define OUTCOME_TRYA_CHOOSE_3_IN(name1, name2, ...) \
auto &&unique1 = (__VA_ARGS__); name1 name2 = std::move(unique1).value();
#define OUTCOME_TRYA_CHOOSE_2(name, nameunpacked, ...) \
OUTCOME_TRYA_CHOOSE_2_IN(nameunpacked, __VA_ARGS__)
#define OUTCOME_TRYA_CHOOSE_3(name, ignore, nameunpacked, ...) \
OUTCOME_TRYA_CHOOSE_3_IN(nameunpacked, __VA_ARGS__)
#define OUTCOME_TRYA_CHOOSE_N(_1,_2,_3,_4,N,...) \
OUTCOME_TRYA_CHOOSE_##N
#define OUTCOME_TRYA_CHOOSE_IN(n, ...) \
OUTCOME_TRYA_CHOOSE_N(n, __VA_ARGS__, 3, 2, 1)
#define OUTCOME_TRYA_CHOOSE(n, ...) \
OUTCOME_TRYA_CHOOSE_IN(n, __VA_ARGS__)
#define OUTCOME_TRYA(name, ...) \
OUTCOME_TRYA_CHOOSE(name, UNPACK_ADD_COMMA name)(name, UNPACK_ADD_COMMA name, __VA_ARGS__)
OUTCOME_TRYA(auto x, expr1, expr2)
OUTCOME_TRYA((auto &&, x), expr3, expr4)
OUTCOME_TRYA((auto &&), expr3, expr4)