From: Joel VanderWerf Date: 2009-06-13T06:22:49+09:00 Subject: Re: wrapping a C struct[] constant Martin DeMello wrote: > On Fri, Jun 12, 2009 at 7:01 PM, Jason Roelofs wrote: >> Even using FFI, you'll need to define the wrapping between C constant to >> Ruby constant, and using Ruby's API, you'll be define_const-ing every one of >> those, then putting them into an array. Just define them in the Ruby >> yourself, there isn't a faster way to do it. > > I was afraid of that :( The docs are a bit on the sketchy side too. I > guess some sort of code generation would be the way to go then (swig > seems like overkill). Will give cgenerator a look. > > martin Cgenerator will work, and might be a good idea if the source (the list of values and strings) changes from time to time, and you would rather have your program regenerate the ruby extension (and data structure) automatically. Here's how it works, assuming your source files are flag.c and flag.h: $ ls flag.c flag.h flag.rb $ cat flag.h struct flag_str { unsigned int val; const char *str; }; extern struct flag_str extent_flags[]; typedef enum { FIEMAP_EXTENT_LAST = 1, FIEMAP_EXTENT_UNKNOWN, FIEMAP_EXTENT_DELALLOC, FIEMAP_EXTENT_NO_BYPASS, FIEMAP_EXTENT_SECONDARY, FIEMAP_EXTENT_NET, FIEMAP_EXTENT_DATA_COMPRESSED, FIEMAP_EXTENT_DATA_ENCRYPTED, FIEMAP_EXTENT_NOT_ALIGNED, FIEMAP_EXTENT_DATA_INLINE, FIEMAP_EXTENT_DATA_TAIL, FIEMAP_EXTENT_UNWRITTEN, FIEMAP_EXTENT_MERGED } FIEMAP_EXTENT; $ cat flag.c #include "flag.h" #ifndef NULL #define NULL 0 #endif struct flag_str extent_flags[] = { { FIEMAP_EXTENT_LAST, "last" }, { FIEMAP_EXTENT_UNKNOWN, "unkown" }, { FIEMAP_EXTENT_DELALLOC, "delalloc" }, { FIEMAP_EXTENT_NO_BYPASS, "no_bypass" }, { FIEMAP_EXTENT_SECONDARY, "secondary" }, { FIEMAP_EXTENT_NET, "net" }, { FIEMAP_EXTENT_DATA_COMPRESSED, "data_compressed" }, { FIEMAP_EXTENT_DATA_ENCRYPTED, "data_encrypted" }, { FIEMAP_EXTENT_NOT_ALIGNED, "not_aligned" }, { FIEMAP_EXTENT_DATA_INLINE, "data_inline" }, { FIEMAP_EXTENT_DATA_TAIL, "data_tail" }, { FIEMAP_EXTENT_UNWRITTEN, "unwritten" }, { FIEMAP_EXTENT_MERGED, "merged" }, { 0, NULL }, }; $ cat flag.rb #!/usr/bin/env ruby require 'cgen/cgen' require 'fileutils' module Flag; end # Generate the extension source code. lib = CGenerator::Library.new "flag_lib" lib.include "flag.h" lib.define_c_singleton_method(Flag, :extent_flags).instance_eval { # no args body %{\ VALUE a; struct flag_str *pfs; a = rb_ary_new(); for (pfs = extent_flags; pfs->val; pfs++) { rb_ary_push(a, rb_ary_new3(2, INT2NUM(pfs->val), rb_str_new2(pfs->str) )); } } returns "a" } # Normally, this isn't needed, but we need to set up the external files # as symlinks in this dir before calling commit. FileUtils.mkdir_p("flag_lib") # Put links to sources where they will be found. Dir.chdir "flag_lib" do %w{ flag.h flag.c }.each do |file| FileUtils.ln_s("../#{file}", file) rescue nil end end # Write and build lib.commit # Use the library Flag::EXTENT_FLAGS = Hash[*Flag.extent_flags.flatten] p Flag::EXTENT_FLAGS $ ruby flag.rb {5=>"secondary", 11=>"data_tail", 6=>"net", 12=>"unwritten", 1=>"last", 7=>"data_compressed", 13=>"merged", 2=>"unkown", 8=>"data_encrypted", 3=>"delalloc", 9=>"not_aligned", 4=>"no_bypass", 10=>"data_inline"} -- vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407