This is a post for C++ beginners. Not an easy role in the world of programming languages. But, I know it can be a rewarding one.
So, this is the situation. You are into intensive C++ std lib sampling and learning. You have a lot of little snippets. One thing has annoyed you a lot, but you have given up on it. You will do it later.
How to print a C++ sequence (or range) from one single and nice generic function. Easy. But with no trailing comma. Not easy. Not hard either. That function finalized by you would be proof you are advancing.
In case you want such a function, to start with it, for the output like on the image above, here is the code.
The discussion comes later. Do not ignore it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
// (c) 2019/2020 by dbj@dbj.org // Licence CC BY SA 4.0 // assumption is you can place this in some header of yours // with namespace and required includes // print a sequence // arguments are two iterators // pointing to it // note: using std::cout is not recommended template<typename Iterator > inline void sequence_print( Iterator begin_ , Iterator end_ , bool show_size = true ) { using namespace std; // local size since there is no sequence // object on which to call it auto size_ = [&] { return size_t( distance( begin_, end_ ) ); }; // if size is requested, show it if (show_size) cout << " sequence [size:" << size_() << "]"; cout << " {"; auto walker = begin_ ; if (walker != end_ ) { // first sequence element // no leading comma cout << " " << *walker; walker++; // other elements, if any // with a leading comma while (walker != end_ ) { cout << " , " << *walker; // operator '++' advances the iterator // logically to the 'right' // see the diagram below walker++; } } cout << " }"; } // print a sequence with a comma in between elements // the only requirement is the 'sequence' conforms // to the concept as on the diagram bellow // sequence size is also printed by default template<typename Sequence > inline void sequence_print(Sequence const & seq_, bool show_size = true ) { using namespace std; sequence_print( begin(seq_) , end( seq_ ) , show_size ); } |
This ‘thing’ above can print anything to which begin and end can be found. For example
1 2 3 4 5 6 7 8 |
// print native array char charr[]{'A','B','C','D'}; sequence_print( charr ); // print std::string sequence_print(string{"STD::STRING"}); // print std::list sequence_print(std::list{1,2,3}); // and so on |
Explanation
I said “begin and end can be found”. This is different from “has begin() and end() methods”. Here is the (sort of a official) diagram representing a sequence
There are (generic) std::begin()
and std::end()
functions. They return those two begin and end iterators as on the diagram above. That same diagram and concept of a sequence it represents, do apply to native arrays and all the types that can be logically visualized as a sequence.
We operate using this knowledge. Key to C++ is your ability to think abstract. Not to get pulled down into the low-level details of (for example) implementations of std lib containers. That comes later. And is not mandatory.
That is why sequence_print
looks simple. It is a high level code and algorithm , hiding a lot of complexity.
- think
- think abstract
I was deliberately vague. Don’t hesitate to ask in the comments bellow. I am especially interested how you might improve this code.
Hint: here is my hint, but ultimately I would like to see your version using std::format
.