【问题标题】:Token detection within a C preprocessor macro argumentC 预处理器宏参数中的令牌检测
【发布时间】:2021-02-05 19:03:36
【问题描述】:

为了很好地修复https://github.com/ned14/outcome/issues/244#issuecomment-774181015,我想知道是否可以检测一个标记序列是否存在于一个 C 预处理器宏参数中?

#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 可能更有用)

这里需要实现的是在v宏参数的扩展中检测&&,但如果它嵌套在()<>"中则不是。

Stackoverflow 上的其他答案可以在 C 预处理器宏参数中找到一个字符串,但是这些技术在这里无法使用,因为 STRING_ ## && 的宏标记粘贴是不合法的。这种级别的 C 预处理器技巧超出了我的能力范围,所以我询问了 Stackoverflow。提前感谢您的帮助。

【问题讨论】:

    标签: c++ c-preprocessor preprocessor


    【解决方案1】:

    这里需要实现的是在 v 宏参数的扩展中检测 &&,但如果它嵌套在 ()" 中则不是。

    不,这是不可能的。我建议在单独的参数中将类型与名称分开。

    OUTCOME_TRYA_NEW(auto, x, expr);
    OUTCOME_TRYA_NEW(auto &&, x, expr);
    

    您可以使用 stringify 将检测移至运行时,例如:

    #define OUTCOME_TRYA(v, ...) \
        if constexpr (your_constexpr_strstr(#v, "&&")) {  \
             /* do stuff with `auto &&` here */  \
        } else { \
             /* do stuff with `auto` here */  \
        }
    

    (或者我认为也有一些std::is_same(decltype(v)....) 的东西),但是当您想在宏扩展中声明变量时,我认为这没有帮助。

    您可以使用的另一种方法是将第一个参数的扩展延迟到稍后阶段,以便您可以在参数数量上重载它,如下所示:

    // 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();
    

    以类似的方式,您可以将界面无缝重构为:

    #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)
    

    上面看起来不错,并且允许保留当前界面。诀窍在于OUTCOME_TRYA_CHOOSE(name, UNPACK name) - 当name(something, something) 时,OUTCOME_TRYA_CHOOSE_N 传递了 3 个参数,如果不是,则传递 2 个参数 - 然后可以在参数数量上重载。因此,如果输入包含带有逗号的大括号,它会有效地重载。


    我想你不知道实现 OUTCOME_TRYV((auto &&), expr1, expr2) 的方法

    嗯;p。解包时添加逗号,有效地将重载转移到另一个。

    #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)
    

    【讨论】:

    • 我在周末自己尝试了这个,包括通过 POD 类型的聚合初始化的魔法反射在内的多种技术,但我总是遇到某种形式的障碍。我将接受这个作为答案,感谢您尝试一下。
    • @NiallDouglas 请看一下编辑。 :p
    • 首先非常感谢(...) 会标记不同的重载!我不认为您知道实现OUTCOME_TRYV((auto &&), expr1, expr2) 的方法,即当且仅当第一个宏参数有括号时才选择不同的宏粘贴?如果第一个宏参数是两个项目,即(auto &&, x),我已经实现了它,但如果它是一个项目,我还没有想出办法。
    • 在其上应用一个添加逗号的宏。 Och uff, a sec :p 基本上就像#define ADD_COMMA(...) __VA_ARGS__, ignore #define TRYV(x, ...) OVERLOAD_ME(x, ADD_COMMA x) - 当ADD_COMMA 扩展时,它会添加一个逗号,可以在OVERLOAD_ME 中检查。如果不是,它将获得两个 args。
    • 感谢您的进一步想法。最后,我放弃了说服 MSVC 损坏的预处理器运行,我最终选择添加一个新宏,以便在您想要指定具有非推导唯一性的 OUTCOME_TRYV 时使用。您可以在github.com/ned14/outcome/blob/issue0244/test/tests/… 上查看示例。我会在 Outcome 发行说明中感谢您的帮助,您在这方面非常有帮助,谢谢!
    猜你喜欢
    • 1970-01-01
    • 2011-04-08
    • 1970-01-01
    • 2016-12-14
    • 2021-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多