Перебирать препроцессор определяет и обрабатывает их - PullRequest
0 голосов
/ 28 февраля 2020

Я пытаюсь построить макрос

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

I Я пытался использовать 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, поэтому я не могу по-настоящему контролировать его.

Есть ли простой способ сделать это без перегруженных макросов или с перегруженными макросами, но в уже существующей библиотеке?

EDIT: I ' м с использованием 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, за идея!

1 Ответ

1 голос
/ 28 февраля 2020

Аргументы для F немедленно расширяются, прежде чем препроцессор начнет расширять список замен. Таким образом, к моменту вызова BOOST_PP_VARIADIC_TO_SEQ имена параметров уже потеряны. Это не проблема, которая может быть решена этой библиотекой.

Немедленное расширение аргументов макроса может быть подавлено с помощью # или ## в теле замены. Это также относится и к аргументам variadi c, поэтому вы можете определить, например, что

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

INTERPOLATE будет затем вызываться с аргументами "A, B, C, D", 1, 2, 3, 4, и ему придется разделить свой первый аргумент и распределить это. Это было бы достаточно просто во время выполнения, например, strstr, поэтому, если решение во время выполнения приемлемо, оно доступно. (Вы можете использовать библиотеку BOOST_PP для подсчета аргументов и передачи этого числа в функцию времени выполнения variadi c, возможно, упрощая реализацию.)

Разделение строкового литерала может быть выполнено во время компиляции, если вы используете C ++ 17, который имеет constexpr std::stringview функций-членов.
Но в C нет средства, способного это сделать. (Вполне возможно, что вы можете убедить компилятор оптимизировать код времени выполнения. Тем не менее, потребуются некоторые эксперименты.)

Если ничего из вышеперечисленного не работает для вас, вы можете легко выполнить преобразование исходного кода. текст с чем-то простым, например Python скрипт, который заменяет вызов F(A, B, C, D) чем-то вроде:

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