【问题标题】:Iterate over preprocessor defines and process them遍历预处理器定义并处理它们
【发布时间】:2020-02-28 15:44:22
【问题描述】:

我正在尝试构建一个宏

#define F(...) ???

这将扩展以下结构

#define A 1
#define B 2
#define C 3
#define D 4
F(A, B, C, D)

进入以下代码:

1, "A", 2, "B", 3, "C", 4, "D"

我尝试使用 Boost.Preprocessor,但似乎缺少所需的功能:

#define Q(r, data, elem) elem, BOOST_PP_STRINGIZE(elem),
#define F(...) BOOST_PP_SEQ_FOR_EACH(Q,,BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))
F(A, B, C, D)

扩展到 1, "1", 2, "2", 3, "3", 4, "4" 这不是我想要实现的。这发生在 BOOST_PP_VARIADIC_TO_SEQ 内部,所以我无法真正控制它。

是否有任何简单的方法可以在没有重载宏或重载宏但在现有库中执行此操作?

编辑: 我正在使用 C++,所以 C 和 C++ 解决方案都很好

EDIT2:

这是一个纯 C++17 解决方案

#include <iostream>
#include <tuple>
#include <array>
#include <string_view>

namespace detail {
    template <std::size_t N>
    constexpr auto split_args(std::string_view s) {
        std::array<std::string_view, N> arr{};

        std::size_t begin{ 0 }, end{ 0 };
        for (std::size_t i = 0; i < N && end != std::string_view::npos; ++i)
        {
            end = s.find_first_of(',', begin);
            arr[i] = s.substr(begin, end - begin);
            arr[i].remove_prefix(std::min(arr[i].find_first_not_of(' '), arr[i].size()));
            begin = end + 1;
        }

        return arr;
    }

    template <std::size_t N, int ...Values, std::size_t ...I>
    constexpr auto get_array(std::array<std::string_view, N> strings, std::index_sequence<I...>) {
        return std::array<std::pair<std::string_view, int>, N> { std::make_pair(strings[I], Values)... };
    }
}

#define EXPAND(x) x
#define VA_ARGS_SIZE(...) std::tuple_size<decltype(std::make_tuple(__VA_ARGS__))>::value
#define F(...) detail::get_array<VA_ARGS_SIZE(__VA_ARGS__), __VA_ARGS__>( detail::split_args<VA_ARGS_SIZE(__VA_ARGS__)>( EXPAND( #__VA_ARGS__ ) ), std::make_index_sequence<VA_ARGS_SIZE(__VA_ARGS__)>{} )

#define A 1
#define B 3
#define C 3
#define D 7

int main()
{
    constexpr auto x = F(A, B, C, D);
    for (const auto &c : x) {
        std::cout << c.first << " " << c.second << "\n";
    }
    
    return 0;
}

输出:

一个 1

B 3

C 3

D 7

感谢@rici 的创意!

【问题讨论】:

  • 因为它是 c++,你考虑过 constexpr 吗?
  • 您已经标记了 [c] 和 [c++]。这些是不同的语言。您实际上需要解决哪一个问题?您已标记 [boost-preprocessor]。你真的需要使用 Boost(一方面,它暗示着 C++)吗?
  • 需要Fvariadic宏吗?
  • @john:使用 Boost 预处理器库并不意味着使用 C++。 “Boost Preprocessing 库是一个宏库,支持预处理器元编程。该库支持 C++ 和 C 编译。它不依赖于任何其他 Boost 库,因此可以作为独立的库使用图书馆。” (来自the docs,添加了重点。)
  • @rici,感谢您的指点

标签: c++ c-preprocessor


【解决方案1】:

F 的参数在预处理器开始扩展替换列表之前立即被扩展。所以在调用BOOST_PP_VARIADIC_TO_SEQ 时,参数的名称已经丢失。这不是该库可以解决的真正问题。

可以通过在替换主体中使用### 来抑制宏参数的立即扩展。这也适用于可变参数,因此您可以定义,例如

#define F(...) INTERPOLATE(#__VA_ARGS__, __VA_ARGS__)
#define INTERPOLATE(names, ...) /* See below */

INTERPOLATE 然后将使用参数"A, B, C, D", 1, 2, 3, 4 调用,它必须拆分第一个参数并分发它。在运行时使用 strstr 会很容易,所以如果运行时解决方案是可以接受的,那么它是可用的。 (您可以使用 BOOST_PP 库来计算参数并将该数字传递给可变参数运行时函数,这可能会简化实现。)

如果您使用的是 C++17,则可以在编译时拆分字符串文字,它具有 constexpr std::stringview 成员函数。
但是C中没有可以做到这一点的工具。 (不过,您可以说服编译器优化运行时代码。一些实验是必要的。)

如果上述方法都不适合您,您可以使用简单的 Python 脚本对源文本进行转换,将 F(A, B, C, D) 的调用替换为以下内容:

F(("A", A), ("B", B), ("C", C), ("D", D))`

【讨论】:

  • 我真的不想使用脚本,这对我来说也是一种挑战:) 我实际上使用的是 C++,但由于宏是 C 功能,所以我也将它标记为 C。我已经实现了扩展到"A, B, C, D", 1, 2, 3, 4 的构造,但是我没有想到在编译时(运行时确实不希望)进一步处理它!这也可能通过模板推导实现,但我会检查是否可以将我的项目升级到 C++17,因为使用 constexpr 确实应该更容易。谢谢!
  • @espkk:如果你使用 C++,你应该只标记为 C++。这使人们回答的生活更轻松。如果相关,您还可以使用 c-preprocessor 标记(尽管它的名称,它适用于 C 和 C++ 预处理器。)
  • (例如,如果我知道你只对 C++ 感兴趣,我可以只关注那个解决方案,从而节省我们双方的时间)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-02-10
  • 2021-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-06
  • 1970-01-01
相关资源
最近更新 更多