Fold expression for a parameter pack with comma operator: How to add additional parameters when expanding the pack?

huangapple go评论64阅读模式
英文:

Fold expression for a parameter pack with comma operator: How to add additional parameters when expanding the pack?

问题

template<unsigned... Ns>
constexpr auto concatWithSeparator(const char(&...s)[Ns])
{
    return CTString<((0U + Ns) * 2 - 1U)>{(s, '|', ...)};
}
英文:

I want to design a compile-time-string class CTString that can e.g. be constructed from a parameter pack of string literals. This works using a comma-fold-expression (for this toy example, I tried to avoid the use of any system headers to make it self-contained):

template&lt;unsigned N&gt;
struct CTString
{
    char m_chars[N + 1U];

    template&lt;unsigned... Ns&gt;
    constexpr CTString(const char (&amp;...s)[Ns])
    {
        auto* p{ m_chars };
        ((p = CopyN_(s, Ns - 1U, p)), ...);
        *p = &#39;
template&lt;unsigned N&gt;
struct CTString
{
char m_chars[N + 1U];
template&lt;unsigned... Ns&gt;
constexpr CTString(const char (&amp;...s)[Ns])
{
auto* p{ m_chars };
((p = CopyN_(s, Ns - 1U, p)), ...);
*p = &#39;\0&#39;;
}
// copy size characters and return one past last copy:
constexpr char* CopyN_(const char* pFrom, unsigned size, char* pTo)
{
for (auto i{ 0U }; i &lt; size; ++i)
*(pTo++) = *(pFrom++);
return pTo;
}
};
template&lt;unsigned... Ns&gt;
constexpr auto concat(const char(&amp;...s)[Ns])
{
return CTString&lt;(0U + ... + (Ns - 1U))&gt;{s...};
}
constexpr auto cHelloWorld{ concat(&quot;Hello&quot;, &quot;World&quot;) };
static_assert(cHelloWorld.m_chars[9] == &#39;d&#39;);
static_assert(cHelloWorld.m_chars[10] == &#39;\0&#39;);
&#39;; } // copy size characters and return one past last copy: constexpr char* CopyN_(const char* pFrom, unsigned size, char* pTo) { for (auto i{ 0U }; i &lt; size; ++i) *(pTo++) = *(pFrom++); return pTo; } }; template&lt;unsigned... Ns&gt; constexpr auto concat(const char(&amp;...s)[Ns]) { return CTString&lt;(0U + ... + (Ns - 1U))&gt;{s...}; } constexpr auto cHelloWorld{ concat(&quot;Hello&quot;, &quot;World&quot;) }; static_assert(cHelloWorld.m_chars[9] == &#39;d&#39;); static_assert(cHelloWorld.m_chars[10] == &#39;
template&lt;unsigned N&gt;
struct CTString
{
char m_chars[N + 1U];
template&lt;unsigned... Ns&gt;
constexpr CTString(const char (&amp;...s)[Ns])
{
auto* p{ m_chars };
((p = CopyN_(s, Ns - 1U, p)), ...);
*p = &#39;\0&#39;;
}
// copy size characters and return one past last copy:
constexpr char* CopyN_(const char* pFrom, unsigned size, char* pTo)
{
for (auto i{ 0U }; i &lt; size; ++i)
*(pTo++) = *(pFrom++);
return pTo;
}
};
template&lt;unsigned... Ns&gt;
constexpr auto concat(const char(&amp;...s)[Ns])
{
return CTString&lt;(0U + ... + (Ns - 1U))&gt;{s...};
}
constexpr auto cHelloWorld{ concat(&quot;Hello&quot;, &quot;World&quot;) };
static_assert(cHelloWorld.m_chars[9] == &#39;d&#39;);
static_assert(cHelloWorld.m_chars[10] == &#39;\0&#39;);
&#39;);

Now I have an additional use case to insert a separator after each literal. How can I expand/fold the parameter pack to insert e.g. the literal &quot;|&quot; after each element of the pack?
This is my feeble attempt that fails because the expression (s, &quot;|&quot;)... does not work: The comma here just leads to the left operand being discarded:

template&lt;unsigned... Ns&gt;
constexpr auto concatWithSeparator(const char(&amp;...s)[Ns])
{
    return CTString&lt;(0U + ... + Ns)&gt;{(s, &quot;|&quot;)...};
}
// Compilation error:
constexpr auto cHelloCommaSeparated{ concatWithSeparator(&quot;Hello&quot;, &quot;World&quot;) };

I can work around this problem by introducing a helper class and having the compile-time-string also accept packs of the helper class in its constructors. But I was wondering whether there is neat idiom that I am missing.(I did read and re-read this great article, but to no avail:
C++20 idioms for parameter packs

The code that compiles is here:
Godbolt
Un-comment the last line to see how it fails.

答案1

得分: 4

template<unsigned... Ns>
constexpr auto concatWithSeparator(const char(&...s)[Ns])
{
    return CTString<(0U + ... + Ns)>{concat(s, "|").m_chars...};
}
英文:

A simple and pragmatic solution would be to re-use your concat function:

template&lt;unsigned... Ns&gt;
constexpr auto concatWithSeparator(const char(&amp;...s)[Ns])
{
    return CTString&lt;(0U + ... + Ns)&gt;{concat(s, &quot;|&quot;).m_chars...};
}

https://godbolt.org/z/hzv5qv6no.


If you want to know how to interleave the parameter pack with the seperator |, I don't think there is a simple solution. One option is to first create a tuple, whose elements are the parameter pack interleaved with the seperator:

auto tup = std::tuple_cat(
    std::tuple&lt;const char(&amp;)[Ns], const char(&amp;)[2] &gt;(s, &quot;|&quot;)...
);

As a next step, you would need to translate the tuple back to a parameter pack, which in turn can be passed as argument(s) for the CTString constructor.

You could do this by forwarding the tuple to a templated helper function using std::index_sequence. With C++20, you have templated lambdas so that the helper function could be a lambda defined in the body of concatWithSeparator that you immediately evaluate:

template&lt;unsigned... Ns&gt;
constexpr auto concatWithSeparator(const char(&amp;...s)[Ns])
{
    auto tup = std::tuple_cat(
        std::tuple&lt;const char(&amp;)[Ns], const char(&amp;)[2] &gt;(s, &quot;|&quot;)...
    );
    return [&amp;]&lt;std::size_t ... Is&gt;(std::index_sequence&lt;Is...&gt;)
    {
        return CTString&lt;(0U + ... + Ns)&gt;{std::get&lt;Is&gt;(tup)...};
    }
    (
        std::make_index_sequence&lt;2*sizeof...(Ns)&gt;{}
    );
}

https://godbolt.org/z/W1jhcrGc5


A slightly more readible version of the second solution would be to use std::apply:

template&lt;unsigned... Ns&gt;
constexpr auto concatWithSeparator(const char(&amp;...s)[Ns])
{
    return std::apply(
        [&amp;](auto const&amp;... args){ return CTString&lt;(0U + ... + Ns)&gt;(args...); },
        std::tuple_cat(std::tuple&lt;const char(&amp;)[Ns], const char(&amp;)[2] &gt;{s, &quot;|&quot;}...)
    );
}

https://godbolt.org/z/q3GshYx5a

答案2

得分: 1

你不能将一个包 arg0, .., argN 转换为 arg0, sep, .., argN, sep

作为替代,你可以添加额外的构造函数,以使包的每个元素执行额外的操作:

struct SeparatorTag{};

template<unsigned N>
struct CTString
{
    char m_chars[N + 1U];

    template<unsigned int NSep, unsigned... Ns>
    constexpr CTString(SeparatorTag, const char(&sep)[NSep], const char (&...s)[Ns])
    {
        auto* p{ m_chars };
        ((p = CopyN_(s, Ns - 1U, p), p = CopyN_(sep, NSep - 1, p)), ...);
        *p = '
struct SeparatorTag{};

template<unsigned N>
struct CTString
{
    char m_chars[N + 1U];

    template<unsigned int NSep, unsigned... Ns>
    constexpr CTString(SeparatorTag, const char(&sep)[NSep], const char (&...s)[Ns])
    {
        auto* p{ m_chars };
        ((p = CopyN_(s, Ns - 1U, p), p = CopyN_(sep, NSep - 1, p)), ...);
        *p = '\0';
    }
    // ...
};
'
;
} // ... };

演示

英文:

You cannot transform a pack arg0, .., argN into arg0, sep, .., argN, sep.

As alternative, you might add extra constructor to make extra works by element of pack:

struct SeparatorTag{};

template&lt;unsigned N&gt;
struct CTString
{
    char m_chars[N + 1U];

    template&lt;unsigned int NSep, unsigned... Ns&gt;
    constexpr CTString(SeparatorTag, const char(&amp;sep)[NSep], const char (&amp;...s)[Ns])
    {
        auto* p{ m_chars };
        ((p = CopyN_(s, Ns - 1U, p), p = CopyN_(sep, NSep - 1, p)), ...);
        *p = &#39;
struct SeparatorTag{};
template&lt;unsigned N&gt;
struct CTString
{
char m_chars[N + 1U];
template&lt;unsigned int NSep, unsigned... Ns&gt;
constexpr CTString(SeparatorTag, const char(&amp;sep)[NSep], const char (&amp;...s)[Ns])
{
auto* p{ m_chars };
((p = CopyN_(s, Ns - 1U, p), p = CopyN_(sep, NSep - 1, p)), ...);
*p = &#39;\0&#39;;
}
// ...
};
&#39;; } // ... };

Demo

huangapple
  • 本文由 发表于 2023年4月17日 18:40:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/76034271.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定