1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
|
#include <iostream>
#include <algorithm>
#include <fstream>
#include <cstring>
struct any // discriminated union (union-like-class) to hold int, double or long long
{
enum type_t : char { INT = 1, DOUBLE = 2, LONG_LONG = 3 /* ... */ };
static constexpr std::size_t MAX_VALUE_SIZE = std::max( sizeof(double), sizeof(long long) ) ;
constexpr any( int v = 0 ) : type(INT), ival(v) {}
constexpr any( double v ) : type(DOUBLE), dval(v) {}
constexpr any( long long v ) : type(LONG_LONG), lval(v) {}
// ...
constexpr operator int() const { return operator long long() ; }
constexpr operator double() const { return type == DOUBLE ? dval : operator long long() ; }
constexpr operator long long() const { return type == INT ? ival : type == DOUBLE ? dval : lval ; }
// ...
operator int&() { if( type != INT ) throw type_error() ; return ival ; }
operator double&() { if( type != DOUBLE ) throw type_error() ; return dval ; }
operator long long&() { if( type != LONG_LONG ) throw type_error() ; return lval ; }
// ...
type_t type ;
union
{
int ival ;
double dval ;
long long lval ;
// ...
};
struct type_error : virtual std::domain_error { type_error() : std::domain_error("invalid type" ) {} } ;
};
std::ostream& write_any( std::ostream& binary_stm, any a )
{
char type = a.type ;
char value[any::MAX_VALUE_SIZE]{} ;
switch(type)
{
case any::INT: std::memcpy( value, &a.ival, sizeof(a.ival) ) ; break ;
case any::DOUBLE: std::memcpy( value, &a.dval, sizeof(a.dval) ) ; break ;
case any::LONG_LONG: std::memcpy( value, &a.lval, sizeof(a.lval) ) ; break ;
default: throw any::type_error() ;
}
binary_stm.write( &type, sizeof(type) ) ;
return binary_stm.write( value, sizeof(value) ) ;
}
// function that reads the data type (numeric, occupying 1 byte in the file)
// and the value (8 bytes, can be any numeric data type like short, int, long, long, signed and unsigned).
std::istream& read_any( std::istream& binary_stm, any& a )
{
char type ;
char value[any::MAX_VALUE_SIZE] ;
if( binary_stm.read( &type, sizeof(type) ) && binary_stm.read( value, sizeof(value) ) )
{
switch(type)
{
case any::INT: a = *reinterpret_cast< const int* >(value) ; break ;
case any::DOUBLE: a = *reinterpret_cast< const double* >(value) ; break ;
case any::LONG_LONG: a = *reinterpret_cast< const long long* >(value) ; break ;
default: binary_stm.clear( std::ios::failbit ) ;
}
}
return binary_stm ;
}
int main() // minimal test driver
{
const char* const test_file_name = "test.bin" ;
{
std::ofstream test_file( test_file_name, std::ios_base::binary ) ;
const any many[] { 123456, -67.89, 1234567890123456LL } ;
for( any a : many ) write_any( test_file, a ) ;
}
{
std::ifstream test_file( test_file_name, std::ios_base::binary ) ;
any a ;
while( read_any( test_file, a ) )
{
switch(a.type)
{
case any::INT: std::cout << int(a) << " (int)\n" ; break ;
case any::DOUBLE: std::cout << double(a) << " (double)\n" ; ; break ;
case any::LONG_LONG: std::cout << a.lval << " (long long)\n" ; break ;
default: std::cout << "this is insane\n" ;
}
}
}
}
| |