Two Templates

K

KevinSimonson

Can someone tell me what the following code does? The first
"template<typename T, Endianess endianPolicy> struct TransformTo"
makes sense to me, but I don't understand what the "template<typename
T > struct TransformTo< T, Machine::endianess>" does. Also, in the
first one "endianPolicy" is mentioned but never used. So does that
mean including it has no effect? I'm kind of confused on that.

//! \brief Generic function object to give its char serialization a
given
//! specified byte ordering.
//!
//! The byte ordering of the argument is swapped unless it matches the
byte
//! ordering of the target machine.
//! We use partial specialization to achieve this.
template<typename T, Endianess endianPolicy> struct TransformTo
{
T operator()(T value) const { return swapEndianess< T >( value ); }
};
template<typename T > struct TransformTo< T, Machine::endianess >
{
T operator()(T value) const { return value; }
};

Kevin S
 
I

Ian Collins

Can someone tell me what the following code does? The first
"template<typename T, Endianess endianPolicy> struct TransformTo"
makes sense to me, but I don't understand what the "template<typename
T> struct TransformTo< T, Machine::endianess>" does. Also, in the
first one "endianPolicy" is mentioned but never used. So does that
mean including it has no effect? I'm kind of confused on that.

As the comment says...
//! \brief Generic function object to give its char serialization a
given
//! specified byte ordering.
//!
//! The byte ordering of the argument is swapped unless it matches the
byte
//! ordering of the target machine.
//! We use partial specialization to achieve this.

the second is a partial specialisation of the first.
template<typename T, Endianess endianPolicy> struct TransformTo
{
T operator()(T value) const { return swapEndianess< T>( value ); }
};
template<typename T> struct TransformTo< T, Machine::endianess>
{
T operator()(T value) const { return value; }
};

The second template argument is there to differentiate between the
general case and the specialised one.
 
J

Jim Langston

KevinSimonson said:
Can someone tell me what the following code does? The first
"template<typename T, Endianess endianPolicy> struct TransformTo"
makes sense to me, but I don't understand what the "template<typename
T > struct TransformTo< T, Machine::endianess>" does. Also, in the
first one "endianPolicy" is mentioned but never used. So does that
mean including it has no effect? I'm kind of confused on that.

//! \brief Generic function object to give its char serialization a
given
//! specified byte ordering.
//!
//! The byte ordering of the argument is swapped unless it matches the
byte
//! ordering of the target machine.
//! We use partial specialization to achieve this.
template<typename T, Endianess endianPolicy> struct TransformTo
{
T operator()(T value) const { return swapEndianess< T >( value ); }
};
template<typename T > struct TransformTo< T, Machine::endianess >
{
T operator()(T value) const { return value; }
};

Kevin S

Somewhere machine::endianess is defined or typedefed or such to represent a
specific endian policy for that particular machine. The second one is
called a "Template specialiation". If the Endianess does not match
Machine::endianes then the first version of the template will be
called/built that returns the swapped values. However, if the endianPolicy
matches Machien::endianess, then the second template will be called/built
that simply returns the value. If the endianess already matches the
endianess of the machine do nothing.

I am curious, however, why this TransformTo even exists, why that logic
isn't being handled in the tempalte swapEndianess<T> which could do the
exact same thing if the endianess is the same, simply return the value. I
do not know of any other reasoning for specialiazing then leaving it alone,
and that is what swapEndianess<T> should do in the firstplace.

I'm curious what happens if you call swapEndianess< T > with a value with
the same endian policy. swapEndianess is probably interesting to look at
also.
 
K

KevinSimonson

As the comment says...


the second is a partial specialisation of the first.


The second template argument is there to differentiate between the
general case and the specialised one.

So are you saying that if the "endianess" of the host machine is the
same as the "endianess" of the target machine, "TransformTo( value)"
will just return the value of "value", but that if the "endianess" of
the host machine is _different_ from the "endianess" of the target
machine, the bytes will get swapped? Presuming that "swapEndianess()"
swaps the bytes?

Kevin S
 
K

KevinSimonson

I am curious, however, why this TransformTo even exists, why that logic
isn't being handled in the tempalte swapEndianess<T> which could do the
exact same thing if the endianess is the same, simply return the value.  I
do not know of any other reasoning for specialiazing then leaving it alone,
and that is what swapEndianess<T> should do in the firstplace.

I'm curious what happens if you call swapEndianess< T > with a value with
the same endian policy.  swapEndianess is probably interesting to look at
also.

Well, the "TransformTo" code with the "swapEndianess" that precedes it
is:

//! \brief generic function to swap the byte ordering of a given type
//!
//! The byte ordering can be swapped meaningfully only for unsigned
integer types
//! therefore specializations are provided only for those types. We
use
//! template specialization in order to avoid automatic argument
conversion.
template<typename T>
struct SwapEndianess {};

template<> struct SwapEndianess< uint8 >
{
uint8 operator()(uint8 value) const { return value; }
};
template<> struct SwapEndianess< uint16 >
{
uint16 operator()(uint16 value) const
{
return _byteswap_ushort( value );
}
};

template<> struct SwapEndianess< uint32 >
{
uint32 operator()(uint32 value) const
{
return _byteswap_ulong( value );
}
};

template<> struct SwapEndianess< uint64 >
{
uint64 operator()(uint64 value) const
{
return _byteswap_uint64( value );
}
};

template<typename T>
inline T swapEndianess(T value)
{
return SwapEndianess< T >()( value ); <--- Point of interest
}

//! \brief Generic function object to give its char serialization a
given
//! specified byte ordering.
//!
//! The byte ordering of the argument is swapped unless it matches the
byte
//! ordering of the target machine.
//! We use partial specialization to achieve this.
template<typename T, Endianess endianPolicy> struct TransformTo
{
T operator()(T value) const { return swapEndianess< T >( value ); }
};
template<typename T > struct TransformTo< T, Machine::endianess >
{
T operator()(T value) const { return value; }
};

It looks to me like the author of the code put a lot more effort into
swapping the "Endianess" than he needed to. And the problem I'm up
against is that the call to "SwapEndianess< T >()( value )", next to
which I added the comment "Point of interest" doesn't compile on my
version of Visual Studio. So I'm trying to find some other way of
doing this that achieves the same functionality. If I go on the
assumption that all "swapEndianess" does is swap the order of the
bytes, couldn't I just write:

uint8 swapEndianess ( uint8 value)
{
return value;
}

uint16 swapEndianess ( uint16 value)
{
return _byteswap_ushort( value);
}

uint32 swapEndianess ( uint32 value)
{
return _byteswap_ulong( value);
}

uint64 swapEndianess ( uint64 value)
{
return _byteswap_uint64( value);
}

Granted that would require me to write a lot more code for the
"TransformTo()"s then the current author has done, but at least it
would compile. I think I would have to write four "TransformTo()"s
instead of just the one that's there, but I'd be willing to do that.
The first one would just be:

uint8 TransformTo ( uint8 value)
{
return value;
}

wouldn't it? And for the next three I'd have:

uint16 TransformTo ( uint16 value)
{
return [some condition] ? value : swapEndianess( value);
}

uint32 TransformTo ( uint32 value)
{
return [some condition] ? value : swapEndianess( value);
}

uint64 TransformTo ( uint64 value)
{
return [some condition] ? value : swapEndianess( value);
}

where [some condition] would be identical for the three, but what
exactly would [some condition] be? I'd be comparing something against
"Machine::endianess", but what?

Kevin S
 
K

KevinSimonson

Wait a minute! _Could_ I just write:

uint8 swapEndianess ( uint8 value)
{
return value;

}

uint16 swapEndianess ( uint16 value)
{
return _byteswap_ushort( value);

}

uint32 swapEndianess ( uint32 value)
{
return _byteswap_ulong( value);

}

uint64 swapEndianess ( uint64 value)
{
return _byteswap_uint64( value);

}

//! Existing comments abbreviated.

template< typename T, Endianess endianPolicy> struct TransformTo
{
T operator() ( T value) const
{
return swapEndianess< T>( value);
}
};

template< typename T> struct TransformTo< T, Machine::endianess>
{
T operator() ( T value) const
{
return value;
}
};

In other words, could I leave the "struct TransformTo" code intact,
and just rewrite
the four "swapEndianess()"s, and get the same functionality?

Kevin S
 
Ö

Öö Tiib

Wait a minute!  _Could_ I just write:

[ ... blah-blah "Endianess" blah-blah ]


No, does not compile. You are now mixing up template specializations
and overloads. These are different things despite they work similarly.
It is spelled "endianness" btw. I know, it is not so bad typo as for
example "filed" versus "field" that my coworkers sometimes make.
In other words, could I leave the "struct TransformTo" code intact,
and just rewrite the four "swapEndianess()"s, and get the same functionality?

Best is to write special I/O code per platform, keeping interface same
and never provide chance to have buggy forms around. Reading from
stream, transforming to platform-specific form and checking it should
be done in one operation (same with writing). That is called
"serializing" and "deserializing". Otherwise you will have 3
operations that should be always done in same order, so result is
unneeded interface complexity and chance for bugs. If you use template
specializations or overloads for it is really matter of taste.
 
I

Ian Collins

So are you saying that if the "endianess" of the host machine is the
same as the "endianess" of the target machine, "TransformTo( value)"
will just return the value of "value", but that if the "endianess" of
the host machine is _different_ from the "endianess" of the target
machine, the bytes will get swapped? Presuming that "swapEndianess()"
swaps the bytes?

Pretty much, yes.

Although they appear to have gone to an awful lot of trouble and added
heaps of complexity to solve a relatively trivial problem.
 
I

Ian Collins

Wait a minute! _Could_ I just write:
In other words, could I leave the "struct TransformTo" code intact,
and just rewrite
the four "swapEndianess()"s, and get the same functionality?

Hang on, what problem are you trying to solve?

If it's the same one as yesterday, the problem isn't so much the code as
the types of the values being used to instantiate the templates.
 
K

KevinSimonson

Pretty much, yes.

Although they appear to have gone to an awful lot of trouble and added
heaps of complexity to solve a relatively trivial problem.

As far as you can tell, is there a way to tell in the code whether the
endianness of the host machine is different from the endianness of the
target machine? Then I could just use the ternary operator to either
return "value" or "swapEndianess( value)".
 
K

KevinSimonson

Hang on, what problem are you trying to solve?

If it's the same one as yesterday, the problem isn't so much the code as
the types of the values being used to instantiate the templates.

I'm just trying to get the code to compile, with the same
functionality as the original coder intended. It looks to me like all
"swapEndianess()" is supposed to do is switch the order of the bytes,
and all "TransformTo()" and "TransformFrom" are supposed to do is call
"swapEndianess( value)" if the endianness is different between the
host machine and the target machine, and just return "value"
otherwise. But I've tried every possible permutation of ways of
defining the functions and calling them that I can think of. So if
you could give me some advice I'm ready to hear it.
 
Ö

Öö Tiib

I'm just trying to get the code to compile, with the same
functionality as the original coder intended.  It looks to me like all
"swapEndianess()" is supposed to do is switch the order of the bytes,
and all "TransformTo()" and "TransformFrom" are supposed to do is call
"swapEndianess( value)" if the endianness is different between the
host machine and the target machine, and just return "value"
otherwise.  But I've tried every possible permutation of ways of
defining the functions and calling them that I can think of.  So if
you could give me some advice I'm ready to hear it.

Communicating machines should not care what is the hardware and
platform of the other machine.

Both should only know what are the properties of data that travels
between them (such properties are called communication protocol) and
what are the properties of the data in their own memory. The
properties like size in bits, order of bits (endianness) and meaning
of values of bits.

Based on that knowledge they should convert the bits they got from
communication channel into variables in memory and variables in memory
into bits that they write into communication channel. That is usually
trivial task.

Original coder has made it likely so that the rules in protocol should
be known compile time and rules for memory (Machine::endianess) are
known as well. So if these are same then no conversion is needed. You
are attempting to apply these dynamically, run-time (depending on the
other machine properties) and so you will fail and fail. Templates
work only with data that is known compile time.
 
J

Jorgen Grahn

Best is to write special I/O code per platform, keeping interface same
and never provide chance to have buggy forms around.

Why would you need separate code for different platforms?
Serialization is not rocket science; all you have to do is to write a
sequence of octets representing what you'd like to write, one by one.
Reading from
stream, transforming to platform-specific form and checking it should
be done in one operation (same with writing).

I fully agree here.

And that's why endian-swapping functions are pretty useless for
serialization. What you want is a function "serialize this integer
onto a stream/into a buffer", not "turn this integer into something
which looks like an integer, but isn't".

/Jorgen
 
Ö

Öö Tiib

Why would you need separate code for different platforms?
Serialization is not rocket science; all you have to do is to write a
sequence of octets representing what you'd like to write, one by one.

Yes, it is not a rocket science. Very elementary operations.
Endianness is known compile time so it is simple to have different
code included or linked depending on endianness. That is why i don't
like to see there some template and bit-shifting nonsense that tries
to make it to look universal and complex (and sometimes even contains
crap about PDP-style endianness that exists next to nowhere in
practice).

Supporting floating point values and bit-packing the values is
slightly more fun but again not a rocket science, especially if to not
try to solve the issue universally for all imaginable platforms at
once with same code.
 
B

Brian Wood

Why would you need separate code for different platforms?
Serialization is not rocket science; all you have to do is to write a
sequence of octets representing what you'd like to write, one by one.

Marshalling/serialization isn't trivial either. Useful marshalling
support involves automated creation of marshalling functions for
types. To my knowledge there's only one solution that has this
basic functionality.


Brian Wood
Ebenezer Enterprises
http://webEbenezer.net
 
J

Jorgen Grahn

Yes, it is not a rocket science. Very elementary operations.
Endianness is known compile time so it is simple to have different
code included or linked depending on endianness.

My point was that even *that* is overkill. Code like

const uint8_t* p = inbuf;
unsigned n = p[0] << 8 + p[1];

reads a big-endian 16-bit number and is portable[1].
No conditional compilation needed.

/Jorgen

[1] On machines with uint8_t, that is.
 

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,777
Messages
2,569,604
Members
45,224
Latest member
BettieToom

Latest Threads

Top