5

I am currently looking into implementing a cleaner way to call native C functions from the Gravity scripting language.

So far, the most simplistic example would be this one:

int add(int lhs, int rhs) {
  return lhs + rhs;
}

static void gravity_wrap_add(
  gravity_vm* vm,
  gravity_value_t* args, uint32_t nargs, 
  uint32_t retIndex, void* data
) {
  int lhs, rhs, rt;

  // Unwrap
  lhs = VALUE_AS_INT(args[1]);
  rhs = VALUE_AS_INT(args[2]);

  // Perform call, capture return
  rt = add(lhs, rhs);

  // Forward the return
  gravity_vm_setslot(vm, VALUE_FROM_INT(rt), retIndex);
}

By using C++ (98) templating or C preprocessor magic, would there be a way to generate wrapper functions?

A very, very crunched example of the above wrapper function, would be this one:

static void gravity_wrap_add(
  gravity_vm* vm,
  gravity_value_t* args, uint32_t nargs, 
  uint32_t retIndex, void* data
) {
  gravity_vm_setslot(vm, 
   VALUE_FROM_INT(
     add(VALUE_AS_INT(args[1]), VALUE_AS_INT(args[2]))
   ), 
  retIndex);
}

This version is technically what I want to achieve - but through methods like the preprocessor or C++ templating. For cross-platform compatibility reasons, I'd like to stick with C++98 (since MSVC isn't exactly good on modern features).

1
  • Hard to tell exactly what you are attempting, but if I understand, you could include limits.h and within gravity_wrap_add validate INT_MIN <= args[1] && args[1] <= INT_MAX (the same for args[2]) and then simply cast (int)args[1] and for args[2] -- presuming gravity_value_t is the same size as int on your system. If that's not what you are asking, update your question. (admittedly, I may be completely missing the point as I'm not familiar with gravity) That would be C89/C++98 compiant. Commented Jan 29, 2019 at 1:47

1 Answer 1

0

It’s pretty tedious without variadic templates, but you should be able to do a small number of arguments:

namespace gravity {
  template<class T> struct value {};
  // for brevity and deduction:
  template<class T> T get(const gravity_value_t &v)
  {return value<T>::get(v);}
  template<class T> gravity_value_t put(const T &t)
  {return value<T>::put(t);}
  template<> struct value<int> {
    int get(const gravity_value_t &v)
    {return VALUE_AS_INT(v);}
    gravity_value_t put(int i)
    {return VALUE_FROM_INT(i);}
  };
  // more specializations…

  template<class R,R f()>
  void wrap(gravity_vm* vm,
            gravity_value_t* args, uint32_t nargs, 
            uint32_t retIndex, void* data) {
    assert(nargs==0);
    gravity_vm_setslot(vm,put(f()),retIndex);
  }
  template<class R,class A,R f(A)>
  void wrap(gravity_vm* vm,
            gravity_value_t* args, uint32_t nargs, 
            uint32_t retIndex, void* data) {
    assert(nargs==1);
    gravity_vm_setslot(vm,put(f(get<A>(args[0]))),retIndex);
  }
  // more overloads with more arguments…

  /*gravity_register*/(wrap<int,int,int,add>);
}

As usual, void return types will be a pain; you have to repeat each argument-count overload for R of void.

It would be possible to use deduction to avoid repeating the signature when using wrap, but then you get fewer functions and extra closure objects to distinguish them (which you can presumably use via the data argument).

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.