Carp icon indicating copy to clipboard operation
Carp copied to clipboard

"receiver" templates

Open scolsen opened this issue 3 years ago • 2 comments

Sometimes, I'd like to write wrapper functions for struct access in carp, either because there's some additional behavior involved or because I simply want to refer to fields by a different name:

struct Foo {
 int fooFields;
 FooData fooData[1];
};
typedef struct Foo Foo

In carp, if I want to refer to fooFields as just Foo.fields there's currently three ways to do so:

  • Define a helper header file that wraps the field access and register the function:

    int Foo_fields(Foo foo) {
      return foo.fooFields;
    }
    
    (register-type Foo)
    (defmodule Foo
      (register fields (Fn [Foo] Int))
    )
    
  • Define wrappers directly in carp as templates:

    (register-type Foo)
    (defmodule Foo
      (deftemplate fields (Fn [Foo] int)
        "int Foo_fields(Foo foo)"
        "$DECL {
          return foo.fooFields;
        }"
      )
    )
    
  • Register the type with exposed fields, make the generated accessors private and define wrappers with the desired name:

    (register-type Foo [fooFields Int])
    (defmodule Foo
      (hidden fooFields)
      (defn fields [foo]
        (Foo.fooFields foo))
    )
    

Option three is preferable because:

  • Everything is contained in one file
  • No explicit C wrapping necessary

However, of all the options, it is the only one that alters the type signature which can matter if one is wrapping a struct that's typically passed by value. The first two options allow one to define accessors on value structs, while the last will only take a reference which might force the programmer to make reference conversions in other contexts.

I think it'd be great to have "receiver templates" that allow you to register a value struct and accessor functions on the struct with name overrides for any of the fields. Bonus points if we find a way to do this "in-place" instead of needing to define functions; e.g. a call like: (Foo.fields foo) should translate directly to the c foo.fooFields in-place.

scolsen avatar Feb 21 '22 15:02 scolsen

Yeah, that would be nice. Do you have any idea for what syntax to use for this?

eriksvedang avatar Feb 22 '22 07:02 eriksvedang

Maybe something like: (as <c-name> <override>) could work?

(register-type Foo [(as fooFields fields) Int])

Then this would generate the accessors etc. with the name fields instead of fooFields. This wouldn't solve the "pass by value" vs pass by reference thing though. For that we could introduce a separate form:

(register-struct Foo [fooFields])

That generates signatures that take value arguments instead of references, or we could just generate both reference style and value style getters/setters for all types, or we could just say having to ref cast every now and then isn't a huge deal so it's not something we need.

scolsen avatar Feb 22 '22 14:02 scolsen