Як зробити згруповану чи парну складку пакета параметрів?


14
template<class Msg, class... Args>
std::wstring descf(Msg, Args&&... args) {
    std::wostringstream woss;

    owss << Msg << ". " << ... << " " << args << ": '" << args << "' ";//not legal at all

    //or

    owss << Msg << ". " << args[0] << ": '" << args[1] << "'  " << args[2] << ": '" << args[3] << "' "; //... pseudo code, and so on...
}

Я знаю, що можу просто використовувати список пар або щось подібне замість цього, але мене цікавить, як це зробити, зберігаючи синтаксис функції:

const auto formatted = descf(L"message", "arg1", arg1, "arg2", arg2);

Відповіді:


9

Ви можете використовувати вираз складки! Це не найкрасивіший *, але він коротший, ніж усі представлені необоротні рішення:

template<class T, class ... Args>
std::wstring descf(T msg, Args&&... args) {
    std::wostringstream owss;
    owss << msg << ". ";

    std::array<const char*, 2> tokens{": '", "' "};
    int alternate = 0;
    ((owss << args << tokens[alternate], alternate = 1 - alternate), ...);

    return owss.str();
}

Демонстрація із зразком виводу: https://godbolt.org/z/Gs8d2x

Виконуємо складку над оператором коми, де кожен операнд є результатом одного argsта змінного маркера, плюс перемикання індексу лексеми (останні два поєднуються з іншим оператором кома).

* Для читача, знайомого з виразами складок (і оператором комами), це, мабуть, "найкращий" код, але для всіх інших - це абсолютно безглуздість, тому використовуйте власне судження про те, чи хочете ви нанести це на базі коду.


Я думаю, це також може працювати з bool (якщо потрібно лише спарювання) ала. : b ^ = вірно; а потім, можливо, оператор тенісу (b? ": '", ":"' ")
darune

1
@darune Звичайно, є й інші способи виразити чергування. Я вирішив відокремити логіку виводу / чергування від фактичних значень токена, які масив добре виконує. Мені не подобається неявне перетворення , boolщоб intпри індексації , так що я пішов з фактичним intдля перемикання стану. І до-після постфікса ++потрібні додаткові ментальні цикли для перевірки (як мінімум, для мене), в той час як окремий 1 - справді не може бути неправильно прочитаний. Коротше кажучи, я намагався зробити це максимально читабельним, але це, звичайно, під особистий смак (або відповідний посібник зі стилів). max66 конденсував це набагато більше.
Макс Ленгоф

Використання std::arrayзамість рідного масиву здається безглуздим ускладненням.
Deduplicator

@Deduplicator Я не погоджуюся, тому що я вважаю std::array<const char*, 2>нескінченно більш зрозумілим, ніж const char**. Але знову ж таки, це моя найкраща оцінка читабельності навколо деякого досить незрозумілого синтаксису, ви можете робити з ним те, що вам подобається у власному коді. Все, що я можу зробити, - це дати тобі дані про те, що я вважаю читабельним.
Макс Лангхоф

9

Це легко за допомогою декількох допоміжних функцій, які відповідають наступній схемі.

void helper() {}

template <class T1, class T2, class ... T>
void helper(T1 t1, T2 t2, T ... t)
{
     do_single_pair(t1, t2);
     helper(t...);
}

Це не вираз складки, але чистий результат той самий.


чи буде глибина рекурсії шаблону іншою, ніж із виразом складки? чи буде те саме
darune

1
@darune Немає властивої рекурсії зі виразами складки ... Складні вирази просто формально розширюються до деякого виразу (у цій конкретній інстанції варіадичного шаблону).
Макс Ленгоф

6

Я думаю, ви можете спробувати з індексом і термінальним оператором.

Щось таке

template <typename ... Args>
std::wstring descf (std::wstring const & Msg, Args && ... args)
 {
   std::wostringstream woss;

   int i = 0;

   ((woss << Msg << ". "), ... ,(woss << args << (++i & 1 ? ": '" : "' ")));

   return woss.str();
 }

@MaxLanghof Це перевага (?) У простому розширенні до більшої кількості сепараторів.
Deduplicator

@Deduplicator Я не розумію, про що ти маєш на увазі? Ви можете пояснити?
Макс Лангхоф

@Deduplicator - Незрозуміло, що ви маєте на увазі під "розширенням на більше роздільників" ... все одно ... це рішення дуже схоже на прийняте; Я не думаю, що це більш-менш розширюється. Я припускаю, що це трохи (мало! Можливо, компілятор оптимізує таким же чином) легше, тому що уникайте використання std::array(що, у всякому разі, є легким класом), але (тому я вважаю, що краще прийняти відповідь) є менш читабельним.
max66

2

Наступний код повинен зробити трюк. Пакет параметрів розгорнутий у списку ініціалізатора.

#include <string>
#include <iostream>
#include <sstream>
#include <vector>

template <typename...Args>
std::string descf(std::string msg, Args &&... args)
{
   auto argumentsVector = std::vector<std::string>{args...};

   std::stringstream ss;
   ss << msg << ". ";

   for (auto i = std::size_t{0}; i < argumentsVector.size() - 1; ++i)
      ss << argumentsVector[i] << ": '" << argumentsVector[i+1] << "' ";

   auto result = ss.str();
   if (!argumentsVector.empty())
       result.pop_back();
   return result;
}

int main()
{
   std::cout << descf("message", "arg1", "1", "arg2", "2") << std::endl;
}

Для цього потрібно argsконвертувати все в std::strings.
волоський горіх

@walnut, це правильно. Якщо це не може бути вимогою, тоді вам доведеться скласти вирази / рекурсію
Mattias De Charleroy

1

З std::index_sequence:

template <class Msg, class... Pairs>
std::wstring descf_pair(const Msg& msg, const Pairs&... pairs)
{
    std::wstringstream woss;

    woss << msg << ". ";
    auto sep = L"";
    ((woss << sep << std::get<0>(pairs) << L": '"
                  << std::get<1>(pairs) << L"'", sep = L"  "), ...);
    return woss.str();
}

template <class Msg, std::size_t... Is, class Tuple>
decltype(auto) descf_impl(const Msg& msg, std::index_sequence<Is...>, Tuple&& t)
{
    return descf_pair(msg, std::tie(std::get<2 * Is>(t), std::get<2 * Is + 1>(t))...);
}

template <class Msg, typename ... Ts>
std::wstring descf(const Msg& msg, const Ts&... ts)
{
    static_assert(sizeof...(Ts) % 2 == 0);

    return descf_impl(msg,
                      std::make_index_sequence<sizeof...(Ts) / 2>(),
                      std::tie(ts...));
}

Демо

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.