Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optional (out) var parameters are not supported #102

Open
StefanSalewski opened this issue Nov 7, 2020 · 5 comments
Open

Optional (out) var parameters are not supported #102

StefanSalewski opened this issue Nov 7, 2020 · 5 comments

Comments

@StefanSalewski
Copy link
Owner

Simplest example is

https://developer.gnome.org/glib/stable/glib-GVariant.html#g-variant-get-string

const gchar * g_variant_get_string (GVariant *value, gsize *length);

In C we can pass NULL for gsize if we are not interested in actual length. In Nim that parameter is a var parameter, and var parameters are never optional. Well for this case we can just create an overloaded proc without that parameter. It is not easy for the automatically created bindings, but at least we can do it manually.

But then there are functions like

https://developer.gnome.org/gtk3/stable/GtkScale.html#gtk-scale-get-layout-offsets

void gtk_scale_get_layout_offsets (GtkScale *scale, gint *x, gint *y);

x, y are both optional out parameters, in C we can pass NULL when we are not interested in one. In Nim both are var parameters.

Well, GTK has not that many functions with optional out parameters, only about 700, and most of them are not used that often. But still some support of optional var parameters would be nice.

For tuple results we have the underscore for optional results, so we could try to rewrite above proc as

proc getLayoutOffsets*(scale: Scale): (x, y: int) =
gtk_scale_get_layout_offsets(scale, result.x, result.y)

and may call it as
var y: int
(_, y) = getLayoutOffset(myscale)

Again, doing that conversion for tuple results is not really easy, but maybe possible.

Or may we support underscore for optional var parameters? Call proc like
var y: int
getLayoutOffsets(myscale, _, y)

We would need in the proc body a way to check if a parameter is _, which means we have to ignore it and pass NULL to C lib for it.

@demotomohiro
Copy link
Contributor

You can use nil as default value to var type parameters.

template ignore(t: typed): untyped = cast[ptr t](nil)[]

proc foo(x: var int = ignore(int)) =
  if x.addr != nil:
    echo x
    x = 7
  else:
    echo "Ignore parameter"

foo()
var a: int
foo(a)
echo a

@StefanSalewski
Copy link
Owner Author

That is an interesting idea. Unfortunately the real problem are the C lib functions with more than one optional out parameter, https://developer.gnome.org/gtk3/stable/GtkScale.html#gtk-scale-get-layout-offsets is one example but there are many more with up to 6 optional out parameters, 700 functions total. Can you give an example code for gtk-scale-get-layout-offsets() ? (The problem is for more than one, which one the user wants, and which one he wants to ignore. Passing NULL in C makes it clear.)

@demotomohiro
Copy link
Contributor

Example code for gtk-scale-get-layout-offsets():

proc gtk_scale_get_layout_offsets (scale: ptr GtkScale, x: var gint = ignore(gint), y: var gint = ignore(gint))

var
  scale = newGtkScale()
  xval, yval: gint
gtk_scale_get_layout_offsets(scale, xval)
gtk_scale_get_layout_offsets(scale, y = yval)

More complicated runnable example code:

nilvar.c:

#include <stdio.h>

typedef struct {
  int a;
  int b;
} CObj;

void cfunc(int* x, int* y, double* z, CObj* w) {
  if(x == NULL) {
    printf("ignore x\n");
  } else {
    printf("x = %d\n", *x);
    *x = 123;
  }

  if(y == NULL) {
    printf("ignore y\n");
  } else {
    printf("y = %d\n", *y);
    *y = 987;
  }

  if(z == NULL) {
    printf("ignore z\n");
  } else {
    printf("z = %f\n", *z);
    *z = 100.0;
  }

  if(w == NULL) {
    printf("ignore w\n");
  } else {
    printf("w = %d, %d\n", w->a, w->b);
    w->a = 222;
    w->b = 333;
  }
}

nilvar.nim:

{.compile: "nilvar.c".}

template ignore(t: typed): untyped = cast[ptr t](nil)[]

type
  CObj {.pure.} = object
    a: cint
    b: cint

proc cfunc(x: var cint = ignore(cint); y: var cint = ignore(cint); z: var cdouble = ignore(cdouble); w: var CObj = ignore(CObj)) {.importc.}

proc main =
  cfunc()

  var a: cint = 1
  cfunc(a)
  echo a

  cfunc(y = a)
  echo a

  var b: cdouble = -1.0
  cfunc(z = b)
  echo b

  var c = CObj(a: -1, b: -2)
  cfunc(w = c)
  echo c

main()

Output:

$ nim c -r nilvar.nim
...

ignore x
ignore y
ignore z
ignore w
x = 1
ignore y
ignore z
ignore w
123
ignore x
y = 123
ignore z
ignore w
987
ignore x
ignore y
z = -1.000000
ignore w
100.0
ignore x
ignore y
ignore z
w = -1, -2
(a: 222, b: 333)

@StefanSalewski
Copy link
Owner Author

Thanks, looks nice!

@StefanSalewski
Copy link
Owner Author

We will have support for this in v0.8.4, try nimble install gintro@#head

https://forum.nim-lang.org/t/7117

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants