Print spaces between each element using a fold expression
Asked Answered
T

5

7

I am using a fold expression to print elements in a variadic pack, but how do I get a space in between each element?

Currently the output is "1 234", the desired output is "1 2 3 4"

template<typename T, typename Comp = std::less<T> >
struct Facility
{
template<T ... list>
struct List
{
    static void print()
    {

    }
};
template<T head,T ... list>
struct List<head,list...>
{
    static void print()
    {
     std::cout<<"\""<<head<<" ";
     (std::cout<<...<<list);
    }
};
};

template<int ... intlist>
using IntList = typename Facility<int>::List<intlist...>;
int main()
{
 using List1 = IntList<1,2,3,4>;
 List1::print();
}
Trotskyism answered 11/7, 2018 at 9:23 Comment(1)
Sorry!, i edited the code, should compile nowTrotskyism
C
9

you can that

#include <iostream>

template<typename T>
struct Facility
{
template<T head,T ... list>
struct List
{
    static void print()
    {
     std::cout<<"\"" << head;
     ((std::cout << " " << list), ...);
      std::cout<<"\"";
    }
};
};

template<int ... intlist>
using IntList = typename Facility<int>::List<intlist...>;
int main()
{
 using List1 = IntList<1,2,3,4>;
 List1::print();
}

the fold expression ((std::cout << " " << list), ...) will expands to ((std::cout << " " << list1), (std::cout << " " << list2), (std::cout << " " << list3)...)

Calvary answered 11/7, 2018 at 9:42 Comment(0)
S
11

If you need space only between numbers (and not after the last or before the first too), you might do:

template <std::size_t... Is>
void print_seq(std::index_sequence<Is...>)
{
    const char* sep = "";
    (((std::cout << sep << Is), sep = " "), ...);
}

Demo

(It is similar to my "runtime version") for regular containers with for-loop.

Samuele answered 10/4, 2019 at 8:48 Comment(0)
C
9

you can that

#include <iostream>

template<typename T>
struct Facility
{
template<T head,T ... list>
struct List
{
    static void print()
    {
     std::cout<<"\"" << head;
     ((std::cout << " " << list), ...);
      std::cout<<"\"";
    }
};
};

template<int ... intlist>
using IntList = typename Facility<int>::List<intlist...>;
int main()
{
 using List1 = IntList<1,2,3,4>;
 List1::print();
}

the fold expression ((std::cout << " " << list), ...) will expands to ((std::cout << " " << list1), (std::cout << " " << list2), (std::cout << " " << list3)...)

Calvary answered 11/7, 2018 at 9:42 Comment(0)
P
7

In general, you use recursion for tasks like this.

You have to define what happens when there are 2 or more and 1 elements in the list and recursively fall back to those definitions:

template <int ...> struct List;
template <int First, int Second, int ... More> struct List {
    static void print() {
        std::cout << First << " ";
        List<Second, More ...>::print();
    }
};
template <int Last> struct List {
    static void print() {
        std::cout << Last;
    }
};
Peritonitis answered 11/7, 2018 at 9:36 Comment(3)
but if my packs have thousand of elements that recursion would fail.Trotskyism
Packs might not be the best choice for having thousands of elements, thoughSpectrogram
Recursion does O(N) instantiations, whereas folding expression allows O(1) instantiation (which shouldn't change runtime execution, but compilation time).Samuele
A
1

You can reuse print() to achieve this behaviour. Afterall you are doing a fold operation which is by definition resursive.

Live Demo

template<T head,T ... rest_of_pack>

struct List<head , rest_of_pack...>
{
    static void print_()
    {
     std::cout<<head<<" ";
     List<rest_of_pack...>::print();

    }
};

If you want to process many elements this way you might run into problems with template depth (gcc for instance has a limit of 900). Lucky for you you can use the -ftemplate-depth= option to tweak this behaviour.

You can compile with -ftemplate-depth=100000 and make it work. Note that compilation time will skyrocket (most likely) or in thhe worst case you run out of memory.

Aviate answered 11/7, 2018 at 9:45 Comment(0)
V
0

Not perfectly aligned with the question but I think may be useful putting here a solution based on fold expressions that generates a string from string-like arguments that should minimize dynamic memory allocations:

#include <iostream>
#include <concepts> // std::convertible_to
#include <string>
#include <string_view>
using namespace std::literals;

template<std::convertible_to<std::string_view>... Args>
[[nodiscard]] std::string mystrconcat(Args&&... args)
{
    std::string s;
    const std::size_t totsiz = sizeof...(args) + (std::size(args) + ...);
    s.reserve(totsiz);
    ((s+=args, s+=' '), ...);
    if(!s.empty()) s.resize(s.size()-1); // Get rid of the trailing delimiter?
    return s;
}

int main()
{
    std::cout << '\"' << mystrconcat("aa"s,"bb"sv,"ccc") << '\"' << '\n';
}
Vevay answered 28/9, 2022 at 17:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.