By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
448,612 Members | 1,656 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 448,612 IT Pros & Developers. It's quick & easy.

Generic iterators to specific types

P: n/a
Hello,

I would like to write a function that reads a sequence of unsigned
shorts from /any/ container, converts them to pairs of unsigned chars,
and writes them to /any/ container. In other words, something like this:
#include <iterator>
#include <vector>
#include <iostream>
#include <cassert>

template <typename InIt, typename Out>
void
getBytes(const InIt& begin, const InIt& end, const typename std::back_insert_iterator<Out>& o)
{
typename std::back_insert_iterator<Outout = o;
union
{
unsigned short u16;
unsigned char u8[2];
} elmt;

for (InIt i=begin; i!=end; ++i)
{
elmt.u16 = *i; // disregard endianness for now
out = std::copy(elmt.u8, elmt.u8+2, out);
}
}

int main()
{
std::vector<unsigned shortvec;
std::vector<unsigned charbytes;

vec.push_back(0x0123);
vec.push_back(0x4567);
vec.push_back(0x89ab);

getBytes(vec.begin(), vec.end(), std::back_inserter(bytes));

return 0;
}
That code works with the test driver provided. However, it /also/
compiles and executes if the input container holds something other than
unsigned short or if the output container takes something other than
unsigned char for which implicit conversions are defined. For instance,
getBytes() will compile with inputs from an std::set<double>, despite
the conversion only working properly for unsigned short.

The work-around that I'm currently using is to use BOOST_STATIC_ASSERT
to verify that the types are of the correct sizes in the beginning of
getBytes():
BOOST_STATIC_ASSERT(sizeof(typename Out::value_type) == sizeof(unsigned char));
BOOST_STATIC_ASSERT(sizeof(typename InIt::value_type) == sizeof(unsigned short));
However, this is non-standard, makes assumptions which may not be safe,
and only works with container classes for which value_type is defined.

What I would like would be a way to define getBytes() to take iterators
to unsigned short and unsigned char but allow the container to vary.
I'd like it to support both STL containers and arrays. Does anyone have
any ideas on how to accomplish this?

Thanks,
Rennie deGraaf
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.7 (GNU/Linux)

iD8DBQFGQVgNIvU5mZP08HERAt44AKCSYkKH27p6cz2gpfkG5I QFYeMR7ACfRVys
cxcfS/A13OPDaQAlFTNKvjU=
=1qP+
-----END PGP SIGNATURE-----

May 9 '07 #1
Share this Question
Share on Google+
6 Replies


P: n/a
On 9 Maj, 07:11, Rennie deGraaf <degr...@cpsc.no-processed-
pork.ucalgary.cawrote:
Hello,

I would like to write a function that reads a sequence of unsigned
shorts from /any/ container, converts them to pairs of unsigned chars,
and writes them to /any/ container. In other words, something like this:
#include <iterator>
#include <vector>
#include <iostream>
#include <cassert>
template <typename InIt, typename Out>
void
getBytes(const InIt& begin, const InIt& end, const typename std::back_insert_iterator<Out>& o)
{
typename std::back_insert_iterator<Outout = o;
union
{
unsigned short u16;
unsigned char u8[2];
} elmt;
for (InIt i=begin; i!=end; ++i)
{
elmt.u16 = *i; // disregard endianness for now
out = std::copy(elmt.u8, elmt.u8+2, out);
}
}
int main()
{
std::vector<unsigned shortvec;
std::vector<unsigned charbytes;
vec.push_back(0x0123);
vec.push_back(0x4567);
vec.push_back(0x89ab);
getBytes(vec.begin(), vec.end(), std::back_inserter(bytes));
return 0;
}

That code works with the test driver provided. However, it /also/
compiles and executes if the input container holds something other than
unsigned short or if the output container takes something other than
unsigned char for which implicit conversions are defined. For instance,
getBytes() will compile with inputs from an std::set<double>, despite
the conversion only working properly for unsigned short.

The work-around that I'm currently using is to use BOOST_STATIC_ASSERT
to verify that the types are of the correct sizes in the beginning of
getBytes():BOOST_STATIC_ASSERT(sizeof(typename Out::value_type) == sizeof(unsigned char));
BOOST_STATIC_ASSERT(sizeof(typename InIt::value_type) == sizeof(unsigned short));

However, this is non-standard, makes assumptions which may not be safe,
and only works with container classes for which value_type is defined.

What I would like would be a way to define getBytes() to take iterators
to unsigned short and unsigned char but allow the container to vary.
I'd like it to support both STL containers and arrays. Does anyone have
any ideas on how to accomplish this?
Why? If you have a solution that works, and in addition it's quite
generic, why would you want to reduce it to only work with unsigned
shorts? If I were you I'd try to make it even more generic instead.

--
Erik Wikström

May 9 '07 #2

P: n/a
Erik Wikström wrote:
Why? If you have a solution that works, and in addition it's quite
generic, why would you want to reduce it to only work with unsigned
shorts? If I were you I'd try to make it even more generic instead.
Because it isn't generic at the moment; it just looks like it is to the
compiler. The conversion to bytes currently truncates anything longer
than two bytes and pads anything shorter, thus either discarding
information or inserting new information.

The real version of this is designed to serialize a sequence of shorts
to network byte order. The conversion that I'd need for other primitive
types would be different. I suppose that I could write a generic
function that converts primitive types to network byte order, but that
would be more complicated and unnecessary for my program; I'm just using
htons() from <arpa/inet.h>. Also, there's no sensible way (in the
context of my system) to serialize anything into any type other than
single-byte primitives, but the code above is valid for any type of
output container for which the appropriate implicit conversions are
defined. I'm not sure if it's currently possible to use containers of
class types, and I don't know what would happen if it is.

Being generic is good, but my primary goal is to ensure that my code
does exactly what I design it to do and nothing more.

Rennie
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.7 (GNU/Linux)

iD8DBQFGQXY2IvU5mZP08HERAm3VAJ9psV42SB6GBSHQF83+xs k0nVkuTACeOL78
Oh1XN0cI0F2TkNfCPv+nxGA=
=BqbK
-----END PGP SIGNATURE-----

May 9 '07 #3

P: n/a
On May 9, 7:11 am, Rennie deGraaf <degr...@cpsc.no-processed-
pork.ucalgary.cawrote:
Hello,

I would like to write a function that reads a sequence of unsigned
shorts from /any/ container, converts them to pairs of unsigned chars,
and writes them to /any/ container. In other words, something like this:
You just need template template arguments.
I have made following to work with microsoft's compiler:

#include <vector>
#include <iterator>

using namespace std;
template <class U,class V,template <class,classclass InIt,template
<class,classclass Out>
void getBytes(const InIt<unsigned short,U>& begin,
const InIt<unsigned short,U>& end,
const typename std::back_insert_iterator<Out<unsigned char,V& o)
{
}
int main()
{
vector<unsigned charo;
vector<unsigned shortv;
getBytes(v.begin(),v.end(),back_inserter(o));
/** error
vector<doublev1;
getBytes(v1.begin(),v1.end(),back_inserter(o));
*/
return 0;
}

Greetings, Branimir.

May 9 '07 #4

P: n/a

"Branimir Maksimovic" <bm***@hotmail.comwrote in message
news:11**********************@y80g2000hsf.googlegr oups.com...
On May 9, 7:11 am, Rennie deGraaf <degr...@cpsc.no-processed-
pork.ucalgary.cawrote:
>Hello,

I would like to write a function that reads a sequence of unsigned
shorts from /any/ container, converts them to pairs of unsigned chars,
and writes them to /any/ container. In other words, something like this:

You just need template template arguments.
I have made following to work with microsoft's compiler:
That's nice, but the types of iterators are implementation defined in the
C++ standard. So while microsoft's implementation might use iterators that
are actually templates with two arguments, another implementation (or even
another version of the same implementation) can use something completely
different. So your code isn't portable C++ code at all.

However, you could use SFINAE

template<class InIt, class Out>
typename enable_if<is_same<typename
std::iterator_traits<InIt>::value_type, unsigned short>::value>::type
getBytes(const InIt& begin, const InIt& end, const typename
std::back_insert_iterator<Out>& o)
{
// ...
}

- Sylvester
May 9 '07 #5

P: n/a
Rennie deGraaf wrote:
Hello,

I would like to write a function that reads a sequence of unsigned
shorts from /any/ container, converts them to pairs of unsigned chars,
and writes them to /any/ container. In other words, something like this:
>#include <iterator>
#include <vector>
#include <iostream>
#include <cassert>

template <typename InIt, typename Out>
void
getBytes(const InIt& begin, const InIt& end, const typename std::back_insert_iterator<Out>& o)
{
typename std::back_insert_iterator<Outout = o;
union
{
unsigned short u16;
unsigned char u8[2];
} elmt;

for (InIt i=begin; i!=end; ++i)
{
elmt.u16 = *i; // disregard endianness for now
out = std::copy(elmt.u8, elmt.u8+2, out);
}
}

int main()
{
std::vector<unsigned shortvec;
std::vector<unsigned charbytes;

vec.push_back(0x0123);
vec.push_back(0x4567);
vec.push_back(0x89ab);

getBytes(vec.begin(), vec.end(), std::back_inserter(bytes));

return 0;
}

That code works with the test driver provided. However, it /also/
compiles and executes if the input container holds something other than
unsigned short or if the output container takes something other than
unsigned char for which implicit conversions are defined. For instance,
getBytes() will compile with inputs from an std::set<double>, despite
the conversion only working properly for unsigned short.

The work-around that I'm currently using is to use BOOST_STATIC_ASSERT
to verify that the types are of the correct sizes in the beginning of
getBytes():
>BOOST_STATIC_ASSERT(sizeof(typename Out::value_type) == sizeof(unsigned char));
BOOST_STATIC_ASSERT(sizeof(typename InIt::value_type) == sizeof(unsigned short));
However, this is non-standard, makes assumptions which may not be safe,
and only works with container classes for which value_type is defined.

What I would like would be a way to define getBytes() to take iterators
to unsigned short and unsigned char but allow the container to vary.
I'd like it to support both STL containers and arrays. Does anyone have
any ideas on how to accomplish this?

Thanks,
Rennie deGraaf
Place a different type of static assertion in your code.
template <typename T>
struct CheckForUnsignedShort;

template <>
struct CheckForUnsignedShort<unsigned short>
{
typedef unsigned short type;
};

// should fail to compile if passed anything other than an unsigned short
template <typename T>
typename const CheckForUnsignedShort<T>::type & GetUnsignedShort( const
T & i )
{
return i;
}

.... in this line of your code
elmt.u16 = *i

replace it with
elmt.u16 = GetUnsignedShort( *i );

If you pass anything else other than an unsigned short to
GetUnsignedShort it will be very noisy at the compile stage.

Come to think of it there are a couple of simpler solutions but this
should work.
May 9 '07 #6

P: n/a
On May 9, 11:54 am, "Sylvester Hesp" <s.h...@oisyn.nlwrote:
"Branimir Maksimovic" <b...@hotmail.comwrote in message

news:11**********************@y80g2000hsf.googlegr oups.com...
On May 9, 7:11 am, Rennie deGraaf <degr...@cpsc.no-processed-
pork.ucalgary.cawrote:
Hello,
I would like to write a function that reads a sequence of unsigned
shorts from /any/ container, converts them to pairs of unsigned chars,
and writes them to /any/ container. In other words, something like this:
You just need template template arguments.
I have made following to work with microsoft's compiler:

That's nice, but the types of iterators are implementation defined in the
C++ standard. So while microsoft's implementation might use iterators that
are actually templates with two arguments, another implementation (or even
another version of the same implementation) can use something completely
different. So your code isn't portable C++ code at all.
Of course.
Portable solution is just to use container instead of iterators.
#include <vector>
#include <iterator>

using namespace std;

template <class U,class V,template <class,classclass In, template
<class,classclass Out>
void getBytes(In<unsigned short,U>& cnt, // for param deduction
const typename In<unsigned short,U>::iterator& begin,
const typename In<unsigned short,U>::iterator& end,
const typename std::back_insert_iterator<Out<unsigned char,V&
o)
{
}

int main()
{
vector<unsigned charo;
vector<unsigned short v;
getBytes(v,v.begin(),v.end(),back_inserter(o));
/** error
vector<doublev1;
getBytes(v1,v1.begin(),v1.end(),back_inserter(o));
*/
return 0;
}
>
However, you could use SFINAE

template<class InIt, class Out>
typename enable_if<is_same<typename
std::iterator_traits<InIt>::value_type, unsigned short>::value>::type
getBytes(const InIt& begin, const InIt& end, const typename
std::back_insert_iterator<Out>& o)
{
// ...

}

- Sylvester
While this is also nice, code has two drawbacks.
You have to install boost first, second, I had bad experience
regarding implementing things like SFINAE ,
and trying to make damn thing work on different compilers.
I guess boosts classes are much better and more portable
then mine were, as boost is like addition to stdlib,
so I guess, better for me to start using it ;)

Greetings, Branimir.

May 9 '07 #7

This discussion thread is closed

Replies have been disabled for this discussion.