Skip to content

Instantly share code, notes, and snippets.

@c3d
Last active February 9, 2021 17:37
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 c3d/5443d4aa86863e97c613168384ea42d3 to your computer and use it in GitHub Desktop.
Save c3d/5443d4aa86863e97c613168384ea42d3 to your computer and use it in GitHub Desktop.
#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