Fiddle: enums?

Hi,

I used to wrap C libraries in Ruby using the ffi gem[1], but I recently
learned that Ruby has already gained bindings to libffi in its stdlib,
named fiddle[2]. fiddle's API looks a little more raw than ffi's, but it
makes me wonder whether ffi will be around for a long time still once
fiddle's API gets a little love.

Anyway, I decided to give fiddle a try to eliminate the dependency on
ffi, but was stumped when I wanted to wrap a function using enums. I
know I can just tell fiddle the enum argument is an int and everything
ought to work fine, but when calling the function I'd rather not use the
raw numbers, but symbolic values. What is the proper way to do this with
fiddle?

Here's an example. A C header may contain this:

    enum something {
      FOO = 1,
      BAR,
      BAZ
    };
    void func(enum something flag);

Now I can probably do the following with fiddle:

    require "fiddle/import"

    module MyLib
      extend Fiddle::Importer
      dlload "mylib.so"
      extern "void func(int)"
    end

    MyLib.func(2)

That does probably work, but it will allow any integer values for
func(), and not just 1-3. Also, it forces me to use the numeric values,
which is not ideal. So the next step would be to wrap it in a cleaner
function, like this:

    module MyLib

      SOMETHING = {:foo => 1, :bar => 2, :baz => 3}

      def wrapped_func(arg)
        arg = SOMETHING[arg] || raise(ArgumentError, "Unknown flag: #{arg}")
        func(arg)
      end

    end

    MyLib.wrapped_func(:bar)

But now the function in Ruby is named #wrapped_func instead of #func,
and I have to write useless boilerplate, perhaps for quite a number of
functions (for each one that uses an enum). To resolve the first
problem, I could revert to the old alias trick, but I still regard that
one as hacky, and it does not address the boilerplate problem:

    module MyLib
      alias old_func func
      def func(arg)
        arg = SOMETHING[arg] || raise(ArgumentError, "Unknown flag: #{arg}")
        old_func(arg)
      end
    end

    MyLib.func(:bar)

Is there a way to declare the "something" enum from the above example
with fiddle and have the #func method check that the provided argument
is valid in its context, without having me to write that all by hand
using a hacky alias trick?

Greetings
Marvin

[1]: https://rubygems.org/gems/ffi
[2]: http://ruby-doc.org/stdlib-2.4.0/libdoc/fiddle/rdoc/Fiddle.html

···

--
Blog: http://www.guelkerdev.de
PGP/GPG ID: F1D8799FBCC8BC4F

That does probably work, but it will allow any integer values for
func(), and not just 1-3.

This is true in C as well. Totally valid code:

enum something {
  FOO = 1,
  BAR,
  BAZ
};

void func(enum something flag) {
  /* ... */
}

int main() {
  func(4);
}

Not exactly your point, but...

···

On Jan 10, 2017, at 13:08, Marvin Gülker <m-guelker@phoenixmail.de> wrote:

What's wrong with something like:

module MyLib
  extend Fiddle::Importer
  dlload "mylib.so"
  FOO = 1
  BAR = 2
  BAZ = 3
  extern "void func(int)"
end

MyLib.func(MyLib::BAR)

···

On Jan 10, 2017, at 13:08, Marvin Gülker <m-guelker@phoenixmail.de> wrote:

   enum something {
     FOO = 1,
     BAR,
     BAZ
   };
   void func(enum something flag);

Now I can probably do the following with fiddle:

   require "fiddle/import"

   module MyLib
     extend Fiddle::Importer
     dlload "mylib.so"
     extern "void func(int)"
   end

   MyLib.func(2)

---

You could wrap that list of const assignments up into some sort of class method `enum` that creates the consts from a list of names (and maybe a starting offset).

---

what you're asking for wrt fiddle treating enums like types rather than named numbers is outside the scope of what fiddle is for. If you want to do extra arg checking, that's on you. That's not the job of ruby, fiddle, _or_ C.

Hi,

What's wrong with something like:

module MyLib
  extend Fiddle::Importer
  dlload "mylib.so"
  FOO = 1
  BAR = 2
  BAZ = 3
  extern "void func(int)"
end

MyLib.func(MyLib::BAR)

Somehow I haven't thought about using constants for the enum's values,
but yes, it is an option indeed. It doesn't look as rubyish as a

    MyLib.func(:bar)

would do, though, but I understand that this is something I have to
implement. Thank you for your suggestion anyway.

You could wrap that list of const assignments up into some sort of
class method `enum` that creates the consts from a list of names (and
maybe a starting offset).

Yes, that's the obvious consequence to draw from this approach.

what you're asking for wrt fiddle treating enums like types rather
than named numbers is outside the scope of what fiddle is for. If you
want to do extra arg checking, that's on you. That's not the job of
ruby, fiddle, _or_ C.

The expectation came from using ruby-ffi, which does have special
treatment for enums[1], and as ruby-ffi and fiddle serve the same
purpose after all, it did not occur to me that handling of enums is out
of scope for fiddle.

Greetings
Marvin

[1]: Enums · ffi/ffi Wiki · GitHub

···

On Wed, Jan 11, 2017 at 03:53:06AM -0800, Ryan Davis wrote:

--
Blog: http://www.guelkerdev.de
PGP/GPG ID: F1D8799FBCC8BC4F