Last active
July 14, 2016 13:48
-
-
Save rpav/426cc4762373057bb793c609840ab3eb to your computer and use it in GitHub Desktop.
COERCE in mostly C, with a bit of C++ for utility
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdlib.h> | |
#include <stdint.h> | |
#include <cpk++/log.hpp> | |
typedef int8_t i8; | |
typedef uint8_t u8; | |
typedef int16_t i16; | |
typedef uint16_t u16; | |
typedef int32_t i32; | |
typedef uint32_t u32; | |
typedef int64_t i64; | |
typedef uint64_t u64; | |
enum Size : u8 { | |
Size8 = 1, | |
Size16 = 2, | |
Size32 = 4, | |
Size64 = 8 | |
}; | |
struct NumType { | |
Size size; | |
u8 is_signed : 1; | |
u8 is_float : 1; | |
NumType(Size size_, bool signed_, bool float_) | |
: size(size_), is_signed(signed_), is_float(float_) | |
{ } | |
}; | |
const NumType i8_type(Size8, true, false); | |
const NumType u8_type(Size8, false, false); | |
const NumType i16_type(Size16, true, false); | |
const NumType u16_type(Size16, false, false); | |
const NumType i32_type(Size32, true, false); | |
const NumType u32_type(Size32, false, false); | |
const NumType i64_type(Size64, true, false); | |
const NumType u64_type(Size64, false, false); | |
const NumType float_type(Size32, true, true); | |
const NumType double_type(Size64, true, true); | |
u64 coerce_to64(void *from, NumType type) { | |
if(type.is_float) { | |
switch(type.size) { | |
case Size32: return (i32)*(float*)from; | |
case Size64: return (i64)*(double*)from; | |
// ^ Without first casting double to i64, clang++-3.7 | |
// with -stdlib=libc++ seems to have issues. | |
default: return 0; | |
} | |
} else if(type.is_signed) { | |
switch(type.size) { | |
case Size8: return *(i8*) from; | |
case Size16: return *(i16*)from; | |
case Size32: return *(i32*)from; | |
case Size64: return *(i64*)from; | |
} | |
} else { | |
switch(type.size) { | |
case Size8: return *(u8*) from; | |
case Size16: return *(u16*)from; | |
case Size32: return *(u32*)from; | |
case Size64: return *(u64*)from; | |
} | |
} | |
} | |
void coerce_from64(void *to, NumType type, u64 from) { | |
if(type.is_float) { | |
switch(type.size) { | |
case Size32: *(float*) to = (i64)from; break; | |
case Size64: *(double*)to = (i64)from; break; | |
default:; | |
} | |
} else if(type.is_signed) { | |
switch(type.size) { | |
case Size8: *(i8*) to = from; break; | |
case Size16: *(i16*)to = from; break; | |
case Size32: *(i32*)to = from; break; | |
case Size64: *(i64*)to = from; break; | |
} | |
} else { | |
switch(type.size) { | |
case Size8: *(u8*) to = from; break; | |
case Size16: *(u16*)to = from; break; | |
case Size32: *(u32*)to = from; break; | |
case Size64: *(u64*)to = from; break; | |
} | |
} | |
} | |
void coerce(void *to, NumType to_type, | |
void *from, NumType from_type) { | |
if(from_type.is_float && to_type.is_float) { | |
// If/when there are more float types, a float-float (using | |
// long doubles or whatnot) coerce would be in order. | |
switch(from_type.size) { | |
case 4: | |
switch(to_type.size) { | |
case 4: *(float*)to = *(float*)from; break; | |
case 8: *(double*)to = *(float*)from; break; | |
default:; | |
} break; | |
case 8: | |
switch(to_type.size) { | |
case 4: *(float*)to = *(double*)from; break; | |
case 8: *(double*)to = *(double*)from; break; | |
default:; | |
} break; | |
default:; | |
} | |
} else { | |
coerce_from64(to, to_type, | |
coerce_to64(from, from_type)); | |
} | |
} | |
// A bit of C++ for testing | |
template<typename T> struct TypeOf { static const NumType &type; }; | |
template<> const NumType& TypeOf<i8>::type = i8_type; | |
template<> const NumType& TypeOf<u8>::type = u8_type; | |
template<> const NumType& TypeOf<i16>::type = i16_type; | |
template<> const NumType& TypeOf<u16>::type = u16_type; | |
template<> const NumType& TypeOf<i32>::type = i32_type; | |
template<> const NumType& TypeOf<u32>::type = u32_type; | |
template<> const NumType& TypeOf<i64>::type = i64_type; | |
template<> const NumType& TypeOf<u64>::type = u64_type; | |
template<> const NumType& TypeOf<float>::type = float_type; | |
template<> const NumType& TypeOf<double>::type = double_type; | |
// This sortof defeats the point of the exercise, but it helps testing | |
template<typename A, typename B> | |
void coerceTo(B from) { | |
A to; | |
auto &to_type = TypeOf<A>::type; | |
auto &from_type = TypeOf<B>::type; | |
coerce(&to, to_type, &from, from_type); | |
if(from_type.size == 1) LOG("from = ", (int)from); | |
else LOG("from = ", from); | |
if(to_type.size == 1) LOG("to = ", (int)to); | |
else LOG("to = ", to); | |
LOG(""); | |
} | |
int main() { | |
double to = 0; | |
i8 from = -1; | |
// What I need in practice | |
coerce(&to, double_type, | |
&from, i8_type); | |
LOG("from = ", (int)from, "\nto = ", to, "\n"); | |
// Test some variations. | |
coerceTo <double> ((i8) -1); | |
coerceTo <i8> ((double) -2.4); | |
coerceTo <u8> ((float) -2.4); | |
coerceTo <u8> ((float) -1); | |
coerceTo <u32> ((i8) -2); | |
coerceTo <i8> ((u16) 32767); | |
coerceTo <u32> ((float) 1.5); | |
coerceTo <u64> ((double) -100.10383149183); | |
coerceTo <u64> ((double) 100.10383149183); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment