module Fiddle::CStructBuilder - RDoc Documentation

# module Fiddle::CStructBuilder

Used to construct C classes (`CUnion`, `CStruct`, etc)

`Fiddle::Importer#struct` and `Fiddle::Importer#union` wrap this functionality in an easy-to-use manner.

### Public Class Methods

create(klass, types, members) click to toggle source

Construct a new class given a C:

- class `klass` (`CUnion`, `CStruct`, or other that provide an entity_class)

- `types` (Fiddle::TYPE_INT, Fiddle::TYPE_SIZE_T, etc., see the C types constants)

- corresponding `members`

`Fiddle::Importer#struct` and `Fiddle::Importer#union` wrap this functionality in an easy-to-use manner.

Examples:

    require 'fiddle/struct'
    require 'fiddle/cparser'

    include Fiddle::CParser

    types, members = parse_struct_signature(['int i','char c'])

    MyStruct = Fiddle::CStructBuilder.create(Fiddle::CUnion, types, members)

    MyStruct.malloc(Fiddle::RUBY_FREE) do |obj|
      ...
    end

    obj = MyStruct.malloc(Fiddle::RUBY_FREE)
    begin
      ...
    ensure
      obj.call_free
    end

    obj = MyStruct.malloc
    begin
      ...
    ensure
      Fiddle.free obj.to_ptr
    end

    # File ext/fiddle/lib/fiddle/struct.rb, line 215
    def create(klass, types, members)
      new_class = Class.new(klass){
        define_method(:initialize){|addr, func = nil|
          if addr.is_a?(self.class.entity_class)
            @entity = addr
          else
            @entity = self.class.entity_class.new(addr, types, func)
          end
          @entity.assign_names(members)
        }
        define_method(:[]) { |*args| @entity.send(:[], *args) }
        define_method(:[]=) { |*args| @entity.send(:[]=, *args) }
        define_method(:to_ptr){ @entity }
        define_method(:to_i){ @entity.to_i }
        define_singleton_method(:types) { types }
        define_singleton_method(:members) { members }

        # Return the offset of a struct member given its name.
        # For example:
        #
        #     MyStruct = struct [
        #       "int64_t i",
        #       "char c",
        #     ]
        #
        #     MyStruct.offsetof("i") # => 0
        #     MyStruct.offsetof("c") # => 8
        #
        define_singleton_method(:offsetof) { |name|
          klass.offsetof(name, members, types)
        }
        members.each{|name|
          name = name[0] if name.is_a?(Array) # name is a nested struct
          next if method_defined?(name)
          define_method(name){ @entity[name] }
          define_method(name + "="){|val| @entity[name] = val }
        }
        entity_class = klass.entity_class
        alignment = entity_class.alignment(types)
        size = entity_class.size(types)
        define_singleton_method(:alignment) { alignment }
        define_singleton_method(:size) { size }
        define_singleton_method(:malloc) do |func=nil, &block|
          if block
            entity_class.malloc(types, func, size) do |entity|
              block.call(new(entity))
            end
          else
            new(entity_class.malloc(types, func, size))
          end
        end
      }
      return new_class
    end

### Private Instance Methods

create(klass, types, members) click to toggle source

Construct a new class given a C:

- class `klass` (`CUnion`, `CStruct`, or other that provide an entity_class)

- `types` (Fiddle::TYPE_INT, Fiddle::TYPE_SIZE_T, etc., see the C types constants)

- corresponding `members`

`Fiddle::Importer#struct` and `Fiddle::Importer#union` wrap this functionality in an easy-to-use manner.

Examples:

    require 'fiddle/struct'
    require 'fiddle/cparser'

    include Fiddle::CParser

    types, members = parse_struct_signature(['int i','char c'])

    MyStruct = Fiddle::CStructBuilder.create(Fiddle::CUnion, types, members)

    MyStruct.malloc(Fiddle::RUBY_FREE) do |obj|
      ...
    end

    obj = MyStruct.malloc(Fiddle::RUBY_FREE)
    begin
      ...
    ensure
      obj.call_free
    end

    obj = MyStruct.malloc
    begin
      ...
    ensure
      Fiddle.free obj.to_ptr
    end

    # File ext/fiddle/lib/fiddle/struct.rb, line 215
    def create(klass, types, members)
      new_class = Class.new(klass){
        define_method(:initialize){|addr, func = nil|
          if addr.is_a?(self.class.entity_class)
            @entity = addr
          else
            @entity = self.class.entity_class.new(addr, types, func)
          end
          @entity.assign_names(members)
        }
        define_method(:[]) { |*args| @entity.send(:[], *args) }
        define_method(:[]=) { |*args| @entity.send(:[]=, *args) }
        define_method(:to_ptr){ @entity }
        define_method(:to_i){ @entity.to_i }
        define_singleton_method(:types) { types }
        define_singleton_method(:members) { members }

        # Return the offset of a struct member given its name.
        # For example:
        #
        #     MyStruct = struct [
        #       "int64_t i",
        #       "char c",
        #     ]
        #
        #     MyStruct.offsetof("i") # => 0
        #     MyStruct.offsetof("c") # => 8
        #
        define_singleton_method(:offsetof) { |name|
          klass.offsetof(name, members, types)
        }
        members.each{|name|
          name = name[0] if name.is_a?(Array) # name is a nested struct
          next if method_defined?(name)
          define_method(name){ @entity[name] }
          define_method(name + "="){|val| @entity[name] = val }
        }
        entity_class = klass.entity_class
        alignment = entity_class.alignment(types)
        size = entity_class.size(types)
        define_singleton_method(:alignment) { alignment }
        define_singleton_method(:size) { size }
        define_singleton_method(:malloc) do |func=nil, &block|
          if block
            entity_class.malloc(types, func, size) do |entity|
              block.call(new(entity))
            end
          else
            new(entity_class.malloc(types, func, size))
          end
        end
      }
      return new_class
    end