Reputation:
I have made a few functions to write and read class to binary file.
The 1st writeElement
function i want to just be called if the argument is a class.
Then i want to have the other functions for int, double, size_t
in one function.
There has to be a better way to do this, i shouldn't have to create a new function for all different types.
template <class T>
inline void writeElement(ostream& out, T target) { target.write(out); }
inline void writeElement(ostream& out, int target) { out.write((char*)&target, sizeof(int)); }
inline void writeElement(ostream& out, double target) { out.write((char*)&target, sizeof(double)); }
inline void writeElement(ostream& out, size_t target) { out.write((char*)&target, sizeof(size_t)); }
inline void writeElement(ostream& out, const string str)
{
size_t size = str.size();
writeElement(out, size);
out.write(&str[0], size);
}
template <class T>
inline void writeElement(ostream& out, vector<T> vector)
{
size_t size = vector.size();
writeElement(out, size);
for (auto &element : vector)
{
writeElement(out, element);
}
}
class Header
{
public:
string sig;
double version;
public:
void read(istream& in)
{
readElement(in, sig);
readElement(in, version);
}
void write(ostream& out)
{
writeElement(out, sig);
writeElement(out, version);
}
};
Upvotes: 0
Views: 161
Reputation: 3911
Also using SFINAE and splitting in
Classes that have void write(ostream&)
method:
template <typename T>
typename std::enable_if<std::is_same<decltype(&T::write), void(T::*)(ostream&)>::value, void>::type
writeElement(ostream& out, T& target)
{
target.write(out);
}
Note Better pass the class by reference to prevent copying.
Arithmetic types (int
, double
, size_t
etc):
template <typename T>
typename std::enable_if<std::is_arithmetic<T>::value, void>::type
writeElement(ostream& out, T target)
{
out.write((char*)&target, sizeof(T));
}
std::is_scalar
if you want to include enums.
And std::string
and std::vector<T>
inline void writeElement(ostream& out, const string& str)
{
const size_t size = str.size();
writeElement(out, size);
out.write(str.data(), size); // NOTE: Accessing &str[0] if the `.size() == 0` is *Undefined behavior*, use `std::string::data()` insetad
}
template <class T>
inline void writeElement(ostream& out, const vector<T>& vector)
{
const size_t size = vector.size();
writeElement(out, size);
for (const auto &element : vector)
{
writeElement(out, element);
}
}
Note Accessing string
s &str[0]
if its size is zero leads to Undefined behavior
Upvotes: 0
Reputation: 25623
Simply use SFINAE for class,native and then other special defined types like:
template <typename T, typename std::enable_if < std::is_class<T>::value, void >::type* = nullptr >
inline void writeElement( T )
{
std::cout << "Class write" << std::endl;
}
template <typename T, typename std::enable_if<!std::is_class<T>::value, void>::type* = nullptr>
inline void writeElement( T )
{
std::cout << "Native type" << std::endl;
}
template <typename T>
inline void writeElement( const std::vector<T>& )
{
std::cout << "vector" << std::endl;
}
inline void writeElement( std::string )
{
std::cout << "string" << std::endl;
}
class A{};
int main()
{
writeElement( A{} );
writeElement( 1 );
writeElement( std::vector<int>{1,2,3,4} );
writeElement( std::string{"Hallo"});
}
So every "native type" can be handled with one template function. You should also take a look for pointers, because you could not handle a pointer in that way, because you can't pass a length with a pointer itself.
Upvotes: 0
Reputation: 21576
Then i want to have the other functions for
int, double, size_t
in one function. There has to be a better way to do this, i shouldn't have to create a new function for all different types.
You may want to prefer using "trait-like" class-template for this, and you have one function do the job for you.
//Primary templates for every other object
template<typename T, typename = void>
struct WriteHelper{
static void write(ostream& out, T target){
target.write(out);
}
};
//Specialization for integral types
template<typename T>
struct WriteHelper<T, std::enable_if_t<std::is_arithmetic<T>::value >>{
static void write(ostream& out, T target){
out.write((char*)&target, sizeof(T));
}
};
//Specialization for std::string
template<typename T>
struct WriteHelper<T, std::enable_if_t<std::is_same<std::string, T>::value >>{
static void write(ostream& out, T target){
size_t size = str.size();
writeElement(out, size);
out.write(&str[0], size);
}
};
And use as:
template<typename T, typename X = std::decay_t<T>>
inline void writeElement(ostream& out, T&& target){
WriteHelper<X>::write(out, std::forward<T>(target));
}
Upvotes: 1
Reputation: 66230
I suppose there are many ways to do this.
I propose the use of SFINAE with the help of a custom type traits that set a constexpr
value
template <typename>
struct foo { static constexpr std::size_t value { 0U }; };
template <>
struct foo<int> { static constexpr std::size_t value { 1U }; };
template <>
struct foo<double> { static constexpr std::size_t value { 1U }; };
template <>
struct foo<std::size_t> { static constexpr std::size_t value { 1U }; };
template <>
struct foo<std::string> { static constexpr std::size_t value { 2U }; };
So you can enable different write function using the value of foo<T>::value
template <typename T>
inline typename std::enable_if<0U == foo<T>::value>::type
writeElement (std::ostream & out, T const & t)
{ std::cout << "generic case" << std::endl; }
template <typename T>
inline typename std::enable_if<1U == foo<T>::value>::type
writeElement (std::ostream & out, T const & t)
{ std::cout << "int, double, size_t case" << std::endl; }
template <typename T>
inline typename std::enable_if<2U == foo<T>::value>::type
writeElement (std::ostream & out, T const & t)
{ std::cout << "string case" << std::endl; }
template <typename T>
inline void writeElement (std::ostream & out, std::vector<T> const & v)
{ std::cout << "vector case" << std::endl; }
The following is a full working example
#include <set>
#include <vector>
#include <iostream>
#include <type_traits>
template <typename>
struct foo { static constexpr std::size_t value { 0U }; };
template <>
struct foo<int> { static constexpr std::size_t value { 1U }; };
template <>
struct foo<double> { static constexpr std::size_t value { 1U }; };
template <>
struct foo<std::size_t> { static constexpr std::size_t value { 1U }; };
template <>
struct foo<std::string> { static constexpr std::size_t value { 2U }; };
template <typename T>
inline typename std::enable_if<0U == foo<T>::value>::type
writeElement (std::ostream & out, T const & t)
{ std::cout << "generic case" << std::endl; }
template <typename T>
inline typename std::enable_if<1U == foo<T>::value>::type
writeElement (std::ostream & out, T const & t)
{ std::cout << "int, double, size_t case" << std::endl; }
template <typename T>
inline typename std::enable_if<2U == foo<T>::value>::type
writeElement (std::ostream & out, T const & t)
{ std::cout << "string case" << std::endl; }
template <typename T>
inline void writeElement (std::ostream & out, std::vector<T> const & v)
{ std::cout << "vector case" << std::endl; }
int main ()
{
writeElement(std::cout, std::set<int>{});
writeElement(std::cout, 0);
writeElement(std::cout, 0.0);
writeElement(std::cout, std::size_t{});
writeElement(std::cout, std::string{"0"});
writeElement(std::cout, std::vector<int>{});
}
Upvotes: 1