Last active
February 9, 2021 17:37
-
-
Save c3d/5443d4aa86863e97c613168384ea42d3 to your computer and use it in GitHub Desktop.
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 <iostream> | |
#include <vector> | |
#include <deque> | |
#include <type_traits> | |
#include <cstdio> | |
#include <cmath> | |
// ============================================================================ | |
// | |
// The basic type system for our "translator" (everything as a string) | |
// | |
// ============================================================================ | |
typedef std::string xl_typeid; | |
const xl_typeid TYPE_UNSAFE = "unsafe"; | |
const xl_typeid TYPE_VOID = "void"; | |
const xl_typeid TYPE_INT = "int"; | |
const xl_typeid TYPE_I8 = "int8"; | |
const xl_typeid TYPE_U8 = "unsigned int8"; | |
const xl_typeid TYPE_I16 = "int16"; | |
const xl_typeid TYPE_I32 = "int32"; | |
const xl_typeid TYPE_I64 = "int64"; | |
const xl_typeid TYPE_DOUBLE = "double"; | |
typedef std::string xl_boxed_t; | |
typedef xl_boxed_t xl_boxed_void_t; | |
typedef xl_boxed_t xl_boxed_integral_t; | |
typedef xl_boxed_t xl_boxed_fp_t; | |
typedef xl_boxed_t xl_boxed_pointer_t; | |
typedef xl_boxed_t xl_unsafe_t; | |
typedef std::deque<xl_boxed_t> arglist_t; | |
xl_boxed_t arglist_get(arglist_t &args, size_t n) | |
{ | |
xl_boxed_t arg = args[n]; | |
std::cout << "Getting argument #" << n << "=" << arg << "\n"; | |
return arg; | |
} | |
template<typename T> | |
xl_boxed_t xl_box_unsafe(T t) | |
{ | |
std::cout << "WARNING: Boxing some unknown type\n"; | |
return "unboxable"; | |
} | |
xl_unsafe_t xl_unbox_unsafe(xl_boxed_t t) | |
{ | |
std::cout << "WARNING: Unboxing some unknown type\n"; | |
return "unboxable"; | |
} | |
xl_boxed_void_t xl_nil = "nil"; | |
template<typename Num> | |
xl_boxed_t xl_box_integral(Num n) | |
{ | |
std::cout << "Integral boxing of " << n << "\n"; | |
return std::to_string(n); | |
} | |
long long xl_unbox_integral(xl_boxed_t b) | |
{ | |
std::cout << "Integral unboxing of " << b << "\n"; | |
return std::stoll(b); | |
} | |
template<typename Num> | |
xl_boxed_t xl_box_fp(Num n) | |
{ | |
std::cout << "Floating-point boxing of " << n << "\n"; | |
return std::to_string(n); | |
} | |
double xl_unbox_fp(xl_boxed_t b) | |
{ | |
std::cout << "Floating-point unboxing of " << b << "\n"; | |
return std::stoi(b); | |
} | |
template<typename T> | |
xl_boxed_t xl_box_pointer(T *n) | |
{ | |
std::cout << "Integral boxing of " << (void *) n << "\n"; | |
return std::to_string((intptr_t) n); | |
} | |
intptr_t xl_unbox_pointer(xl_boxed_t b) | |
{ | |
std::cout << "Integral unboxing of " << b << "\n"; | |
return std::stoll(b); | |
} | |
xl_typeid xl_pointer_typeid(xl_typeid base) | |
{ | |
return "pointer[" + base + "]"; | |
} | |
typedef std::string xl_signature_t; | |
xl_signature_t xl_parameters() | |
{ | |
return ""; | |
} | |
xl_signature_t xl_parameters(xl_typeid type) | |
{ | |
return type; | |
} | |
template <typename ...As> | |
xl_signature_t xl_parameters(xl_typeid first, As... rest) | |
{ | |
return xl_parameters(first) + "," + xl_parameters(rest...); | |
} | |
template <typename ...As> | |
xl_signature_t xl_signature(xl_boxed_t rettype, size_t arity, As... args) | |
{ | |
if (sizeof...(args) != arity) | |
std::cerr << "Error: wrong number of arguments, " | |
<< "got " << sizeof...(args) << ", expected" << arity << "\n"; | |
return rettype | |
+ "(" + xl_parameters(args...) + ")" | |
+ " arity: " + std::to_string(arity); | |
} | |
// **************************************************************************** | |
// | |
// The actual native interface begins here | |
// | |
// **************************************************************************** | |
// ============================================================================ | |
// | |
// Auto-boxing and unboxing of C types | |
// | |
// ============================================================================ | |
template <typename T, typename Enable = void> | |
struct xl_type | |
{ | |
using native_t = T; | |
using boxed_t = xl_boxed_t; | |
static boxed_t Box(native_t n) { return xl_box_unsafe(n); } | |
static native_t Unbox(boxed_t b) { return xl_unbox_unsafe(b); } | |
static xl_typeid TranslatorType() { return TYPE_UNSAFE; } | |
}; | |
// Special-case void | |
template <> | |
struct xl_type<void> | |
{ | |
using native_t = void; | |
using boxed_t = xl_boxed_void_t; | |
static boxed_t Box() { return xl_nil; } | |
static void Unbox(boxed_t b) { return; } | |
static xl_typeid TranslatorType() { return TYPE_VOID; } | |
}; | |
// Integral types | |
template <typename Num> | |
struct xl_type<Num, | |
typename std::enable_if<std::is_integral<Num>::value>::type> | |
{ | |
using native_t = Num; | |
using boxed_t = xl_boxed_integral_t; | |
static boxed_t Box(native_t n) { return xl_box_integral(n); } | |
static native_t Unbox(boxed_t b) { return xl_unbox_integral(b); } | |
static xl_typeid TranslatorType() { return TYPE_INT; } | |
}; | |
// Floating-point types | |
template <typename Num> | |
struct xl_type<Num, | |
typename std::enable_if<std::is_floating_point<Num>::value>::type> | |
{ | |
using native_t = Num; | |
using boxed_t = xl_boxed_fp_t; | |
static boxed_t Box(native_t n) { return xl_box_fp(n); } | |
static native_t Unbox(boxed_t b) { return xl_unbox_fp(b); } | |
static xl_typeid TranslatorType() { return TYPE_DOUBLE; } | |
}; | |
// Pointer types | |
template <typename T> | |
struct xl_type<T *> | |
{ | |
using native_t = T *; | |
using boxed_t = xl_boxed_pointer_t; | |
static boxed_t Box(native_t n) { return xl_box_pointer(n); } | |
static native_t Unbox(boxed_t b) { return xl_unbox_pointer(b); } | |
static xl_typeid TranslatorType() | |
{ | |
xl_typeid base = xl_type<T>::TranslatorType(); | |
return xl_pointer_typeid(base); | |
} | |
}; | |
// ============================================================================ | |
// | |
// Representation of functions | |
// | |
// ============================================================================ | |
template <typename F> struct function_type; | |
template <typename R, typename ...As> | |
struct function_type<R(*)(As...)> | |
{ | |
using return_t = R; | |
using pointer_t = R (*)(As...); | |
using boxed_return_t = typename xl_type<R>::boxed_t; | |
static const size_t arity = sizeof...(As); | |
static xl_signature_t Signature() | |
{ | |
return xl_signature(xl_type<R>::TranslatorType(), | |
sizeof...(As), | |
xl_type<As>::TranslatorType()...); | |
} | |
static size_t incr(size_t &ref) { return ref++; } | |
static boxed_return_t Call(pointer_t callee, arglist_t &args) | |
{ | |
size_t index = 0; | |
auto result = callee(xl_type<As>::Unbox(arglist_get(args, incr(index)))...); | |
return xl_type<R>::Box(result); | |
} | |
}; | |
// Need to specialize void functions because we call them differently | |
template <typename ...As> | |
struct function_type<void(*)(As...)> | |
{ | |
using pointer_t = void (*)(As...); | |
using boxed_return_t = xl_type<void>::boxed_t; | |
static const size_t arity = sizeof...(As); | |
static xl_signature_t Signature() | |
{ | |
return xl_signature(xl_type<void>::TranslatorType(), | |
sizeof...(As), | |
xl_type<As>::TranslatorType()...); | |
} | |
static boxed_return_t Call(pointer_t callee, arglist_t &args) | |
{ | |
size_t index = 0; | |
callee(xl_type<As>::Unbox(arglist_get(args, index++))...); | |
return xl_type<void>::Box(); | |
} | |
}; | |
// ============================================================================ | |
// | |
// Native interface | |
// | |
// ============================================================================ | |
struct NativeInterface | |
{ | |
virtual xl_signature_t Signature() = 0; | |
virtual xl_boxed_t Call(arglist_t) = 0; | |
}; | |
template <typename F> | |
struct NativeImplementation : NativeInterface | |
{ | |
using ftype = function_type<F>; | |
using ptype = typename ftype::pointer_t; | |
NativeImplementation(ptype function): function(function) {} | |
xl_signature_t Signature() { return ftype::Signature(); } | |
xl_boxed_t Call(arglist_t args) | |
{ | |
if (args.size() != ftype::arity) | |
std::cerr << "Error, got " << args.size() << " args, " | |
<< "expected " << ftype::arity << " instead\n"; | |
return ftype::Call(function, args); | |
} | |
ptype function; | |
}; | |
struct Native | |
{ | |
template<typename F> | |
Native(F function, std::string name) | |
: name(name), | |
implementation(new NativeImplementation<F>(function)) | |
{ | |
all.push_back(this); | |
} | |
xl_signature_t Signature() { return implementation->Signature(); } | |
xl_boxed_t Call(arglist_t args) { return implementation->Call(args); } | |
xl_boxed_t operator()(arglist_t args) { return implementation->Call(args); } | |
std::string name; | |
NativeInterface *implementation; | |
static std::vector<Native *> all; | |
}; | |
std::vector<Native *> Native::all; | |
#define NATIVE(N) static Native native_##N(N, #N) | |
// ============================================================================ | |
// | |
// Declaring some type ids | |
// | |
// ============================================================================ | |
template<> xl_typeid xl_type<int8_t>::TranslatorType() { return TYPE_I8; } | |
template<> xl_typeid xl_type<int16_t>::TranslatorType() { return TYPE_I16; } | |
template<> xl_typeid xl_type<uint8_t>::TranslatorType() { return TYPE_U8; } | |
#define TYPE(T) \ | |
template<> xl_typeid xl_type<T>::TranslatorType() { return #T; } | |
TYPE(int32_t) | |
TYPE(int64_t) | |
TYPE(uint16_t) | |
TYPE(uint32_t) | |
TYPE(uint64_t) | |
TYPE(float) | |
TYPE(double) | |
TYPE(FILE) | |
// ============================================================================ | |
// | |
// Let's test it | |
// | |
// ============================================================================ | |
// For simple functions, it just works... | |
int foo(int n) | |
{ | |
std::cout << "Foo called with n=" << n << "\n"; | |
return n + 1; | |
} | |
NATIVE(foo); | |
double bar(int n, int64_t a, double x, float y, uint16_t b, int8_t c) | |
{ | |
return (n+3) * a + exp(x*c) * sin(y*b); | |
} | |
NATIVE(bar); | |
// Exit as __attribute__((noreturn)), wrap it in regular function | |
namespace { | |
void exit(int rc) | |
{ | |
std::exit(rc); | |
} | |
NATIVE(exit); | |
} | |
// Most math functions are heavily overloaded, so deduction fails | |
// We need small wrappers to make it unambiguous | |
namespace { | |
double exp(double x) { return std::exp(x); } | |
double pow(double x, double y) { return std::pow(x, y); } | |
NATIVE(exp); | |
NATIVE(pow); | |
// It is possible to give separate names in your translator | |
Native my_native_function(exp, "exponential"); | |
} | |
// BEWARE: Some C functions may have surprising signatures | |
NATIVE(putchar); | |
// ============================================================================ | |
// | |
// Let's verify that it works | |
// | |
// ============================================================================ | |
int main() | |
{ | |
for (auto n : Native::all) | |
std::cout << "Native " << n->name | |
<< " has signature " | |
<< n->Signature() | |
<< "\n"; | |
arglist_t args { "1" }; | |
xl_boxed_t result = native_foo.Call(args); | |
std::cout << "Boxed value after call to foo is " << result << "\n"; | |
args = arglist_t { "1", "3", "0.01", "7.3", "42", "-1" }; | |
result = native_bar.Call(args); | |
std::cout << "Boxed value after call to bar is " << result << "\n"; | |
args = arglist_t { "0.0" }; | |
result = my_native_function(args); | |
std::cout << "Exponential of 0 is " << result << " (should be 1)\n"; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment