 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Irek Szczesniak Guest
|
Posted: Thu May 11, 2006 10:21 pm Post subject: something like Perl's join function |
|
|
I like the Perl's join function and want to get something similar in
C++, preferably with std or boost. In Perl you can say:
print join(", ", (10, 2, 15));
which prints "10, 2, 15".
In C++ you can get a similar thing this way:
#include <iostream>
#include <iterator>
#include <algorithm>
using namespace std;
int main()
{
int table[] = {10, 2, 15};
copy(table, table + 3, ostream_iterator<int>(cout, ", "));
}
However, you get "10, 2, 15, ", i.e. you get a trailing unwanted ", ".
Do you know some nifty way of getting the functionality of the Perl's
join function?
Thanks & best,
Irek
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
Carlos Moreno Guest
|
Posted: Sat May 13, 2006 12:21 am Post subject: Re: something like Perl's join function |
|
|
Irek Szczesniak wrote:
| Quote: | I like the Perl's join function and want to get something similar in
C++, preferably with std or boost. In Perl you can say:
print join(", ", (10, 2, 15));
which prints "10, 2, 15".
In C++ you can get a similar thing this way:
#include <iostream
#include <iterator
#include <algorithm
using namespace std;
int main()
{
int table[] = {10, 2, 15};
copy(table, table + 3, ostream_iterator<int>(cout, ", "));
}
However, you get "10, 2, 15, ", i.e. you get a trailing unwanted ", ".
Do you know some nifty way of getting the functionality of the Perl's
join function?
|
I don't think any "out of the box" solution is available.
I believe I've written at some point a join function (creating
SQL statements where I needed an IN operation in the WHERE
clause).
It's not too bad:
// untested, off the top of my head code
template <typename Iterator>
string join (Iterator begin, Iterator end, const string & sep)
{
typedef typename iterator_traits<Iterator>::value_type Value;
if (begin == end) return "";
--end;
ostringstream joined;
copy (begin, end, ostream_iterator<Value>(joined, sep));
joined << *end;
return joined.str();
}
If you want to avoid the trouble with the iterator_traits, you
might use your hand-coded loop using << instead of copy for the
entire sequence. You could also overload it to receive a
container (and then inside the function you take cont.begin()
and cont.end())
HTH,
Carlos
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
Seungbeom Kim Guest
|
Posted: Mon May 15, 2006 12:22 am Post subject: Re: something like Perl's join function |
|
|
Irek Szczesniak wrote:
| Quote: | I like the Perl's join function and want to get something similar in
C++, preferably with std or boost. In Perl you can say:
print join(", ", (10, 2, 15));
which prints "10, 2, 15".
In C++ you can get a similar thing this way:
#include <iostream
#include <iterator
#include <algorithm
using namespace std;
int main()
{
int table[] = {10, 2, 15};
copy(table, table + 3, ostream_iterator<int>(cout, ", "));
}
However, you get "10, 2, 15, ", i.e. you get a trailing unwanted ", ".
Do you know some nifty way of getting the functionality of the Perl's
join function?
|
A quick implementation of mine:
#include <ostream>
#include <iterator>
template <typename T, typename CharT = char,
typename Traits = std::char_traits<CharT> >
class ostream_join_iterator
: public std::iterator<std::output_iterator_tag, void, void, void, void>
{
public:
typedef CharT char_type;
typedef Traits traits_type;
typedef std::basic_ostream<CharT, Traits> ostream_type;
ostream_join_iterator(ostream_type& s, const char_type* c = 0)
: stream(&s), string(c), print_string(0) { }
ostream_join_iterator& operator=(const T& value)
{
if (print_string) *stream << print_string;
print_string = string;
*stream << value;
return *this;
}
ostream_join_iterator& operator*() { return *this; }
ostream_join_iterator& operator++() { return *this; }
ostream_join_iterator& operator++(int) { return *this; }
private:
ostream_type* stream;
const char_type* string;
const char_type* print_string;
};
Use this as a replacement of std::ostream_iterator:
std::copy(begin, end, ostream_join_iterator<int>(std::cout, ", "));
--
Seungbeom Kim
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
Joshua Lehrer Guest
|
Posted: Mon May 15, 2006 12:21 pm Post subject: Re: something like Perl's join function |
|
|
Irek Szczesniak wrote:
| Quote: | I like the Perl's join function and want to get something similar in
C++, preferably with std or boost. In Perl you can say:
print join(", ", (10, 2, 15));
|
I have a solution that lets you do:
std::cout << join(begin,end,sep) << std::endl;
and
join(begin,end,sep).toString();
Unfortunately, I can not paste the code here. However, here is the
approach:
First, you need a templated creator function, called join, that returns
an object, JoinObj. JoinObj stores the iterators and the separator.
This function is a lot like make_pair.
JoinObj then has the ability to insert itself into an ostream and also
has the method toString() on it. You can add an operator string() if
you like, as well.
The entire thing is just a few lines of code.
Enjoy,
joshua lehrer
http://www.lehrerfamily.com/
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
abdallaalothman@myway.com Guest
|
Posted: Mon May 15, 2006 12:21 pm Post subject: Re: something like Perl's join function |
|
|
Irek Szczesniak wrote:
[...]
| Quote: | Do you know some nifty way of getting the functionality of the Perl's
join function?
|
I wanted something like the QStringList::join() to be in a custom
string class. Here's what I have (works with deques and vectors):
template<template<typename A> class X, typename T>
T join(const X<T> &c, const T &s)
{
T temp;
for(typename X<T>::const_iterator i = c.begin(); i != c.end(); ++i)
{
temp += *i;
if( i != c.end() -1 )
{
temp += s;
}
}
return temp;
}
int main()
{
vector<string> sv;
sv.push_back("he");
sv.push_back("him");
sv.push_back("she");
sv.push_back("shim");
string s = join(sv, string(" | "));
cout << s << endl;
return 0;
}
You can use stringstreams to convert to and from different types.
Abdalla Alothman
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
Carl Barron Guest
|
Posted: Mon May 15, 2006 7:21 pm Post subject: Re: something like Perl's join function |
|
|
In article <1147678546.453775.221810 (AT) u72g2000cwu (DOT) googlegroups.com>,
<"abdallaalothman (AT) myway (DOT) com"> wrote:
| Quote: | Irek Szczesniak wrote:
[...]
Do you know some nifty way of getting the functionality of the Perl's
join function?
I wanted something like the QStringList::join() to be in a custom
string class. Here's what I have (works with deques and vectors):
template<template<typename A> class X, typename T
T join(const X<T> &c, const T &s)
{
T temp;
for(typename X<T>::const_iterator i = c.begin(); i != c.end(); ++i)
{
temp += *i;
if( i != c.end() -1 )
{
temp += s;
}
}
return temp;
}
int main()
{
vector<string> sv;
sv.push_back("he");
sv.push_back("him");
sv.push_back("she");
sv.push_back("shim");
string s = join(sv, string(" | "));
cout << s << endl;
return 0;
}
You can use stringstreams to convert to and from different types.
That won't compile:) std::vector has two template paramenters not |
one:) default template parameters do not reduce the # of template
paramenters of the type only provide a short hand to use a specific one
without specifying it. This is one reason why the option of
implementors to add extra default args was removed from the standard.
if you use something like
template <class C,class T>
T join(const C &c,const T &i)
{
for(typename C::const_iterator i= // etc.
}
it will compile, If you want to force the value type of the container
to be T as well [the above only requires it be convertable to T.]
use something like
template <class C,class T>
T join (const C &c,const T &d, boost::enable_if
<
boost::is_same
<
typename C::value_type,
T
// etc.
}
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
Abdalla Alothman Guest
|
Posted: Tue May 16, 2006 10:21 am Post subject: Re: something like Perl's join function |
|
|
Carl Barron wrote:
| Quote: |
That won't compile:)
|
It compiles fine under gcc 3.3.5 if the container is a vector or a
deque.
If there's a need to use vectors, deques, lists, and sets, I would
change
that line:
if( i != c.end() -1 )
to:
if( distance(i, c.end()) != 1 )
I haven't tested it on any other compiler than g++ (3.3.5), to be
honest.
So perhaps it might not compile on other implementations.
Thanks for the tip, though. :)
Abdalla Alothman
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
Carl Barron Guest
|
Posted: Wed May 17, 2006 2:21 am Post subject: Re: something like Perl's join function |
|
|
In article <1147724011.621368.311160 (AT) y43g2000cwc (DOT) googlegroups.com>,
Abdalla Alothman <abdalla_ (AT) myway (DOT) com> wrote:
| Quote: | Carl Barron wrote:
That won't compile:)
Metrowerks 9.6 does not compile your code! If the allocator argument |
is added then it will work for sequence containers but no as written.
| Quote: | It compiles fine under gcc 3.3.5 if the container is a vector or a
deque.
either your code and post are differenet or there is a bug in gcc |
3.3.5. It is non standard if it passes through...
| Quote: | If there's a need to use vectors, deques, lists, and sets, I would
change
that line:
Sorry there is no type std::vector<T> only std::vector<T,A> where |
ussually defaults to std::allocator<T>.
template <class T,class A=std::allocator<T> >
class vector { /**/};
see 14.1 for details...
just like void f(int i= 0) { /* */}
f() is not function call with no args but a function call with one arg
that is 0.
void (*p)() = f; // syntax error.
void (*p)(int) = f;// ok.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
Guest
|
Posted: Fri May 19, 2006 10:21 am Post subject: Re: something like Perl's join function |
|
|
Carlos, Seungbeom, Abdalla, Carl, Joshua and others,
Thanks for your replies. Seungbeom, your ostream_join_iterator is
really cool. I wish it got to the standard some day, but I can only
wish. Finally I have this minimalistic << operator for a vector
(shown below) and now it's sufficient for me.
Best,
Irek
*******************************************
#include <iostream>
#include <vector>
using namespace std;
template <typename T>
ostream &operator << (ostream &os, const vector<T> &v)
{
typename vector<T>::const_iterator i = v.begin();
while(i != v.end())
{
os << *i;
if (++i != v.end())
os << ", ";
}
return os;
}
int main()
{
vector<int> a;
cout << a << endl;
a.push_back(1);
cout << a << endl;
a.push_back(2);
cout << a << endl;
a.push_back(3);
cout << a << endl;
return 0;
}
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
Abdalla Alothman Guest
|
Posted: Tue May 23, 2006 11:21 am Post subject: Re: something like Perl's join function |
|
|
Greetings, Carl.
Carl Barron wrote:
| Quote: | Metrowerks 9.6 does not compile your code! If the allocator argument
is added then it will work for sequence containers but no as written.
|
I see. I'm afraid I can't help (I don't have time).
If you apply the change in the second message and use class X, 'join'
will work with sets, which are not sequential.
template<class X, typename T>
T join(const X &c, const T &s)
{
T temp;
for(typename X::const_iterator i = c.begin(); i != c.end(); ++i)
{
temp += *i;
if( distance(i, c.end()) != 1 )
temp += s;
}
return temp;
}
Try it out with lists, vectors, deques, and sets and try compiling it
here (if your compiler still complains):
http://www.comeaucomputing.com/tryitout/
| Quote: | either your code and post are differenet or there is a bug in gcc
3.3.5. It is non standard if it passes through...
|
It's not a "bug," probably just a difference between STL
implementations and how the compiler orders and processes templates. I
know of two (I think there are around four) different implementations
and they are not one and the same.
| Quote: | template <class T,class A=std::allocator<T
class vector { /**/};
|
I'm aware of that.
vector, in gcc, is implemented as such (SGI), but it inherits (by
protected means) from a base class. The code is available online, I
would rather check it before making conclusions. I don't think the
problem is in vector's implementation, but probably the order in
processing the template.
Your analogy of function pointers does not quite apply because
templates and function pointers are treated differently by compilers.
Sincerely,
Abdalla Alothman
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
Carl Barron Guest
|
Posted: Tue May 23, 2006 10:21 pm Post subject: Re: something like Perl's join function |
|
|
In article <1148182179.893052.40720 (AT) 38g2000cwa (DOT) googlegroups.com>,
Abdalla Alothman <abdalla_ (AT) myway (DOT) com> wrote:
| Quote: | Greetings, Carl.
Carl Barron wrote:
Metrowerks 9.6 does not compile your code! If the allocator argument
is added then it will work for sequence containers but no as written.
I see. I'm afraid I can't help (I don't have time).
If you apply the change in the second message and use class X, 'join'
will work with sets, which are not sequential.
template<class X, typename T
T join(const X &c, const T &s)
{
T temp;
for(typename X::const_iterator i = c.begin(); i != c.end(); ++i)
{
temp += *i;
if( distance(i, c.end()) != 1 )
temp += s;
}
return temp;
}
This is not the original code:) as above is fine as long as T is |
convertable to X::value_type, which it probably is any reasonable use
of this function template.
note this could be improved on by using another typename ,
X::const_iterator jt(i);
if(++jt !=c.end()) temp += s;
is more effiicient than if(distance(i,c.end()) !=1 ) temp+=s;
as c.size() gets larger.
Default arguments do not provid new signatures, only a value to use
if it is not explicitly provided. That makes the original code
invalid.
This is one reason mpl uses place holders and does not use template
template parameters....
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|