Skip to content

Instantly share code, notes, and snippets.

@rpav
Last active July 14, 2016 13:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rpav/426cc4762373057bb793c609840ab3eb to your computer and use it in GitHub Desktop.
Save rpav/426cc4762373057bb793c609840ab3eb to your computer and use it in GitHub Desktop.
COERCE in mostly C, with a bit of C++ for utility
#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