Generic iterators to specific types

R

Rennie deGraaf

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<Out> out = 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 short> vec;
std::vector<unsigned char> bytes;

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)

iD8DBQFGQVgNIvU5mZP08HERAt44AKCSYkKH27p6cz2gpfkG5IQFYeMR7ACfRVys
cxcfS/A13OPDaQAlFTNKvjU=
=1qP+
-----END PGP SIGNATURE-----
 
?

=?iso-8859-1?q?Erik_Wikstr=F6m?=

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<Out> out = 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 short> vec;
std::vector<unsigned char> bytes;

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.
 
R

Rennie deGraaf

Erik said:
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+xsk0nVkuTACeOL78
Oh1XN0cI0F2TkNfCPv+nxGA=
=BqbK
-----END PGP SIGNATURE-----
 
B

Branimir Maksimovic

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,class> class InIt,template
<class,class> class 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 char> o;
vector<unsigned short> v;
getBytes(v.begin(),v.end(),back_inserter(o));
/** error
vector<double> v1;
getBytes(v1.begin(),v1.end(),back_inserter(o));
*/
return 0;
}

Greetings, Branimir.
 
S

Sylvester Hesp

Branimir Maksimovic said:
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
 
G

Gianni Mariani

Rennie said:
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<Out> out = 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 short> vec;
std::vector<unsigned char> bytes;

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.
 
B

Branimir Maksimovic

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,class> class In, template
<class,class> class 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 char> o;
vector<unsigned short > v;
getBytes(v,v.begin(),v.end(),back_inserter(o));
/** error
vector<double> v1;
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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,968
Messages
2,570,150
Members
46,697
Latest member
AugustNabo

Latest Threads

Top