I am trying to consolidate a number of very similar function methods from a class similar to the one shown below and I thought that the best way to efficiently implement this, would be through the use templates coupled with either template function specialization or alternatively type-traits. I am a newbie to template specialization and type-traits but I understand the basic concepts and that is why I am asking for some guidance on the details. Anyway as a starting point my class is a smart buffer class that has many similar method signatures to those listed below.
class OldSafeBuffer {
public:
intmax_t writeAt(const intmax_t& rIndex, const uint32_t val32);
intmax_t writeAt(const intmax_t& rIndex, const int32_t val32);
intmax_t readFrom(const intmax_t& rIndex, uint32_t& rVal32);
intmax_t readFrom(const intmax_t& rIndex, int32_t& rVal32);
intmax_t writeAt(const intmax_t& rIndex, const uint16_t val16);
intmax_t writeAt(const intmax_t& rIndex, const int16_t val16);
intmax_t readFrom(const intmax_t& rIndex, uint16_t& rVal16);
intmax_t readFrom(const intmax_t& rIndex, int16_t& rVal16);
intmax_t read(uint32_t& rVal32);
intmax_t read(int32_t& rVal32);
intmax_t read(uint16_t& rVal16);
intmax_t read(int16_t& rVal16);
protected:
// Actual memory storage.
std::unique_ptr<char[]> mBuffer;
// Buffer length
intmax_t mBufferLength;
// Represents the largest byte offset referenced.
// Can be used to retrieve written length of buffer.
intmax_t mHighWaterMark;
// If set, caller wanted to pack data in network-byte-order.
bool mPackNBO;
// Set on construction, determines whether value needs to be byte-swapped.
bool mSwapNeeded;
// Used for file compatibility
intmax_t mPosition;
};
I thought that this would be a perfect candidate for conversion to use template functions as these functions are very similar and I had a lot of repeated code in each method. The difference between methods was mainly the sign and the size of the 16 or 32 bit value argument.
Anyway to consolidate the readFrom methods I put the following method together. I also did similar things for the write methods. These are shown in the compiling live example.
/**
* Read value (signed or unsigned) from buffer at given byte offset.
*
* @param rIndex [in]
* @param rVal [out]
*
* @return BytesRead or -1 on error
*/
template <typename T>
inline intmax_t readFrom(const intmax_t& rIndex, T& rVal)
{
if ((rIndex + static_cast<intmax_t>(sizeof(T))) <= mBufferLength) {
T* pVal = (T *)&mBuffer[rIndex];
rVal = *pVal;
// @JC Partial Template Specialization for 16 bit entities?
if (sizeof(rVal) > sizeof(int16_t)) {
SWAP32(rVal);
} else {
SWAP16(rVal);
}
mPosition = rIndex + sizeof(T);
return sizeof(rVal);
}
return -1;
}
As can be seen from my comment, I still need to know the size of 'T& rVal' argument in order to decide whether to do a SWAP32 or SWAP16 on the argument. This was why I thought that type_traits could come in useful rather than having to put in a runtime check to compare the size of the argument.
I think that I am on the right track but I cannot figure out how to use the type_traits to check and do certain things depending on the argument type. I thought that alternatively I could use template method specialization to do special things to 16 bit arguments, but I think that would not save much effort as I would also have to specialize on both the signed adn unsigned variants of the 16 bit argument type (assuming the non specialized version was for 32 bit value arguments). Any help figuring this out would be much appreciated.