파라미터 팩을 그룹화 또는 쌍으로 접는 방법?


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 ^ = 참; 그리고 아마도 십진 연산자 (b? ": '", ":"' ")
darune

1
@darune 물론, 다른 표현 방법이 있습니다. 출력 / 대체 로직을 실제 토큰 값과 분리하기로 결정했습니다.이 토큰은 배열이 훌륭하게 수행합니다. 인덱싱 boolint때 의 암시 적 변환을 싫어 하므로 실제로 int상태를 전환하기 위해 갔습니다 . 그리고 pre-post postfix ++는 (적어도 나를 위해) 검증하기 위해 추가적인 정신주기를 필요로하지만, 분리 된 부분 1 - 은 실제로 잘못 읽을 수 없습니다. 요컨대, 나는 이것을 가능한 한 읽을 수있게 유지하려고 노력했지만 이것은 물론 개인적인 취향 (또는 적용 가능한 스타일 가이드)에 달려 있습니다. max66이 훨씬 더 압축했습니다.
맥스 랭 호프

std::array네이티브 배열 대신을 사용하는 것은 의미가없는 합병증으로 보입니다.
중복 제거기

@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 December

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- "더 많은 구분 기호로 확장"이란 무엇을 의미합니까? 어쨌든 ...이 솔루션은 허용되는 솔루션과 매우 유사합니다. 나는 그것이 다소 확장 가능한 것이라고 생각하지 않습니다. 나는 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;
}

이것은 모두 s args로 변환 할 수 있어야합니다 std::string.
호두

@ 호두, 맞습니다. 이 요구 사항을 할 수없는 경우에, 당신은 표현 / 재귀 접어 결과를해야합니다
마티아스 드 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.