วิธีการจัดกลุ่มหรือการพับคู่ของชุดพารามิเตอร์?


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 (ถ้ามีเพียงการจับคู่ที่จำเป็น) ala : b ^ = true; และบางทีโอเปอเรเตอร์ tenary (b? ": '", ":"' ")
darune

1
@darune แน่นอนว่ามีวิธีอื่นในการแสดงการสลับ ฉันตัดสินใจที่จะแยกลอจิกเอาท์พุท / ทางเลือกออกจากค่าโทเค็นจริงซึ่งอาเรย์นั้นทำได้ดีมาก ฉันไม่ชอบการแปลงโดยปริยายจากboolเป็นintเมื่อจัดทำดัชนีดังนั้นฉันจึงไปกับของจริงintเพื่อสลับสถานะ และ prefix กับ postfix ++ใช้วงจรจิตพิเศษในการตรวจสอบ (อย่างน้อยสำหรับฉัน) ในขณะที่การแยก1 - ไม่สามารถอ่านผิดได้ ในระยะสั้นฉันพยายามที่จะให้มันอ่านได้มากที่สุด แต่แน่นอนขึ้นอยู่กับรสนิยมส่วนตัว (หรือคู่มือสไตล์ที่ใช้งานได้) max66 ย่อมันมากขึ้น
Max Langhof

การใช้std::arrayอาร์เรย์แบบดั้งเดิมแทนที่จะเป็นภาวะแทรกซ้อนที่ไม่มีจุดหมาย
Deduplicator

@Deduplicator ผมไม่เห็นด้วยอย่างยิ่งที่ผมพบอนันต์อ่านได้มากขึ้นกว่าstd::array<const char*, 2> const char**แต่อีกครั้งนี่เป็นช็อตที่ดีที่สุดที่ฉันสามารถอ่านได้โดยใช้ไวยากรณ์ที่ไม่ชัดเจนคุณสามารถทำสิ่งที่คุณต้องการในโค้ดของคุณเองได้ สิ่งที่ฉันทำได้คือให้จุดข้อมูลที่ฉันคิดว่าสามารถอ่านได้
Max Langhof

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 ไม่มีการเรียกซ้ำโดยธรรมชาติด้วยนิพจน์พับ ... นิพจน์พับเป็นทางการขยายไปยังนิพจน์บางอย่าง (ในอินสแตนซ์เฉพาะของเทมเพลต Variadic นั้น)
Max Langhof

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

@Dupuplicator ฉันไม่เข้าใจว่าคุณหมายถึงอะไร คุณสามารถอธิบาย?
Max Langhof

@Dupuplicator - ไม่ชัดเจนสำหรับฉันคุณหมายถึงอะไรกับ "ส่วนขยายไปยังตัวคั่นเพิ่มเติม" ... อย่างไรก็ตาม ... วิธีนี้คล้ายกับที่ยอมรับ ฉันไม่คิดว่ามันจะขยายได้ไม่มากก็น้อย ฉันคิดว่านั่นเป็นเพียงเล็กน้อย (อาจเป็นเพราะคอมไพเลอร์ปรับให้เหมาะสมในลักษณะเดียวกัน) ที่เบากว่าเพราะหลีกเลี่ยงการใช้std::array(นั่นคือคลาสเล็ก) แต่ (ดังนั้นฉันคิดว่าเป็นคำตอบที่ยอมรับได้ดีกว่า) อ่านง่ายกว่า
max66

2

รหัสต่อไปนี้ควรทำเคล็ดลับ ชุดพารามิเตอร์ถูกขยายในรายการ initializer

#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 ได้
วอลนัต

@ วอลนัตที่ถูกต้อง หากสิ่งนี้ไม่เป็นข้อกำหนดคุณจะต้องส่งผลให้พับนิพจน์ / เรียกซ้ำ
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...));
}

การสาธิต

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.