Làm thế nào để tạo nhóm gấp hoặc ghép của gói tham số?


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...
}

Tôi biết rằng tôi chỉ có thể sử dụng một danh sách các cặp hoặc một cái gì đó tương tự, nhưng tôi quan tâm đến cách làm điều này trong khi vẫn giữ cú pháp của hàm để:

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

Câu trả lời:


9

Bạn có thể sử dụng một biểu thức gấp! Nó không phải là đẹp nhất *, nhưng nó ngắn hơn tất cả các giải pháp không gấp được trình bày:

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

Bản trình diễn với đầu ra mẫu: https://godbolt.org/z/Gs8d2x

Chúng tôi thực hiện một lần so với toán tử dấu phẩy, trong đó mỗi toán hạng là một đầu ra của một argsvà mã thông báo xen kẽ, cộng với việc chuyển đổi chỉ mục mã thông báo (hai toán tử sau được kết hợp với một toán tử dấu phẩy khác).

* Đối với người đọc quen thuộc với các biểu thức gấp (và toán tử dấu phẩy) đây có thể là mã "tốt nhất", nhưng đối với mọi người khác, nó hoàn toàn vô nghĩa, vì vậy hãy sử dụng phán đoán của riêng bạn cho dù bạn muốn đưa mã này vào cơ sở mã của mình.


Tôi đoán điều này cũng có thể làm việc với một bool (nếu chỉ ghép đôi là không cần thiết) ala. : b ^ = đúng; và sau đó có lẽ là toán tử tenary (b? ": '", ":"' ")
darune

1
@darune Chắc chắn, có nhiều cách khác để diễn đạt sự xen kẽ. Tôi đã quyết định tách logic đầu ra / xen kẽ khỏi các giá trị mã thông báo thực tế, mà mảng hoàn thành tốt. Tôi không thích sự chuyển đổi ngầm từ boolđến intkhi lập chỉ mục vì vậy tôi đã đi với một thực tế intđể chuyển đổi nhà nước. Và tiền tố so với postfix ++cần thêm chu kỳ tinh thần để xác minh (ít nhất là đối với tôi), trong khi riêng biệt 1 - thực sự không thể bị đọc sai. Nói tóm lại, tôi đã cố gắng để điều này dễ đọc nhất có thể, nhưng điều này tất nhiên là tùy theo sở thích cá nhân (hoặc hướng dẫn phong cách áp dụng). max66 ngưng tụ nó nhiều hơn nữa.
Max Langhof

Sử dụng std::arraythay vì một mảng bản địa có vẻ là một sự phức tạp vô nghĩa.
Ded repeatator

@Ded repeatator Tôi không đồng ý mạnh mẽ, vì tôi thấy std::array<const char*, 2>vô cùng dễ đọc hơn const char**. Nhưng một lần nữa, đây là cách tốt nhất của tôi về khả năng đọc xung quanh một số cú pháp khá tối nghĩa, bạn có thể làm với nó những gì bạn thích trong mã của riêng bạn. Tất cả những gì tôi có thể làm là cung cấp cho bạn điểm dữ liệu về những gì tôi cho là có thể đọc được.
Max Langhof

9

Điều này thật dễ dàng với một vài hàm trợ giúp theo mẫu sau.

void helper() {}

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

Đây không phải là biểu thức gấp nhưng kết quả thực là như nhau.


chiều sâu đệ quy mẫu sẽ khác với biểu thức gấp? hoặc nó sẽ giống nhau
darune

1
@darune Không có đệ quy cố hữu với các biểu thức gấp ... Các biểu thức gấp chỉ chính thức mở rộng thành một số biểu thức (trong phần khởi tạo cụ thể của mẫu matrixdic).
Max Langhof

6

Tôi cho rằng bạn có thể thử với một chỉ mục và một toán tử ternary.

Một cái gì đó như sau

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 Điều này có lợi thế (?) Của việc mở rộng dễ dàng đến nhiều dải phân cách hơn.
Ded repeatator

@Ded repeatator Tôi không hiểu bạn đang đề cập đến cái gì? Bạn có thể giải thích?
Max Langhof

@Ded repeatator - Không rõ ràng với tôi ý của bạn là gì với "phần mở rộng cho nhiều dấu phân tách hơn" ... dù sao thì ... giải pháp này rất giống với giải pháp được chấp nhận; Tôi không nghĩ rằng nó nhiều hay ít mở rộng. Tôi cho rằng đó là một chút (ít! Có lẽ trình biên dịch tối ưu hóa theo cùng một cách) nhẹ hơn vì tránh sử dụng một std::array(đó, dù sao, là một lớp ánh sáng) nhưng (vì vậy tôi nghĩ tốt hơn là câu trả lời được chấp nhận) ít đọc hơn.
max66

2

Các mã sau đây nên làm thủ thuật. Gói tham số được mở rộng trong danh sách khởi tạo.

#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;
}

Điều này đòi hỏi tất cả argsphải được chuyển đổi thành std::strings.
quả óc chó

@walnut, đúng vậy. Nếu điều này không thể là một yêu cầu, thì bạn sẽ phải kết quả để gấp các biểu thức / đệ quy
Mattias De Charleroy

1

Với 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...));
}

Bản giới thiệu

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.