I am designing an utility header that pumps binary data off an sf::InputStream
. For ease of use, is comprises a single function name, readFromStream
, that has a lot of (templated and non-templated) overloads for automatically deserializing standard-layout types and type compounds like vectors, tuples and my custom-designed grid
class. The complete implementation can be found here: https://github.com/JoaoBaptMG/ReboundTheGame/blob/master/MainGame/utility/streamCommons.hpp
So, I have defined an overload readFromStream
that pumps out a vector of any type by calling readFromStream
again recursively:
template <typename T, typename std::enable_if<!is_optimization_viable<T>::value, int>::type = 0>
bool readFromStream(sf::InputStream &stream, std::vector<T> &value)
{
size_t size;
if (!readFromStream(stream, VarLength(size)))
return false;
std::vector<T> newVal(size, T());
for (auto &val : newVal)
if (!readFromStream(stream, val))
return false;
newVal.swap(value);
return true;
}
I'd like to write an optimized version for standard-layout classes for that there's not an overload for readFromStream
, so we can exploit the memory layout of them and blit them in a single read
call:
// trait is_optimization_viable is what I'm having trouble to write
template <typename T, typename std::enable_if<is_optimization_viable<T>::value, int>::type = 0>
bool readFromStream(sf::InputStream &stream, std::vector<T> &value)
{
size_t size;
if (!readFromStream(stream, VarLength(size)))
return false;
std::vector<T> newVal(size, T());
if (stream.read(newVal.data(), size*sizeof(T)) != size*sizeof(T))
return false;
newVal.swap(value);
return true;
}
Well, I could use a solution described on other answers to detect presence of a function, but there's a catch. When the type is standard-layout, I have a default readFromStream
that reads like this:
template <typename T, typename std::enable_if<std::is_standard_layout<T>::value, int>::type = 0>
bool readFromStream(sf::InputStream &stream, T& value)
{
return stream.read((void*)&value, sizeof(T)) == sizeof(T);
}
So, there's always a function that does the serialization, not just the one I wanted. The problem I want to solve here is: how can I detect the presence of a non-default readFromString
for type T
, in order to disable the optimized version of readFromString
for std::vector<T>
?
I have tried to pull a few tricks. I can't limit the optimization to POD types because I'm using sf::Vector2<T>
on some types I want to deserialize, which is not POD. I tried to compare the function addresses I get when I use a non-templatized and templatized function, like:
using FPtr = bool(*)(sf::InputStream&, T&);
return (FPtr)readFromStream == (FPtr)readFromStream<T>;
But, strangely enough, it didn't work. And I researched a lot of solutions, but none I could adapt to what I needed. Maybe it's not possible in C++, and I'll have to resort "marking" the types I don't want to be optimized. Or maybe it's some obscure template I haven't thought of. How could I do this?