我正在尝试建立一个宏
#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内部,所以我无法真正控制它。
有没有简单的方法可以在没有重载宏或没有重载宏但已存在的lib中执行此操作?
编辑: 我正在使用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;
}
输出:
A 1
B 3
C 3
D 7
感谢@rici,这个想法!
答案 0 :(得分: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))`