diff options
author | Wayne Meissner <wmeissner@gmail.com> | 2011-09-22 18:29:51 -0700 |
---|---|---|
committer | Wayne Meissner <wmeissner@gmail.com> | 2011-09-22 18:29:51 -0700 |
commit | 82c315f45092811f6370667924a233dfaec93b6e (patch) | |
tree | 0b45f93a1aef3ada2a1614c867631a6df420d763 /lib | |
parent | ee39d4239cecb9e75d52cca2b6ca701b992fd81f (diff) | |
parent | 3d7d45fe5482e123fc9b301bd669cce44a214535 (diff) | |
download | ffi-82c315f45092811f6370667924a233dfaec93b6e.tar.gz |
Merge pull request #156 from sdaubert/master
Add more documentation
Diffstat (limited to 'lib')
-rw-r--r-- | lib/ffi/autopointer.rb | 89 | ||||
-rw-r--r-- | lib/ffi/errno.rb | 7 | ||||
-rw-r--r-- | lib/ffi/io.rb | 15 | ||||
-rw-r--r-- | lib/ffi/library.rb | 176 | ||||
-rw-r--r-- | lib/ffi/platform.rb | 13 | ||||
-rw-r--r-- | lib/ffi/types.rb | 2 |
6 files changed, 244 insertions, 58 deletions
diff --git a/lib/ffi/autopointer.rb b/lib/ffi/autopointer.rb index 222c4e7..7e6eed0 100644 --- a/lib/ffi/autopointer.rb +++ b/lib/ffi/autopointer.rb @@ -23,17 +23,31 @@ module FFI class AutoPointer < Pointer extend DataConverter - # call-seq: - # AutoPointer.new(pointer, method) => the passed Method will be invoked at GC time - # AutoPointer.new(pointer, proc) => the passed Proc will be invoked at GC time (SEE WARNING BELOW!) - # AutoPointer.new(pointer) { |p| ... } => the passed block will be invoked at GC time (SEE WARNING BELOW!) - # AutoPointer.new(pointer) => the pointer's release() class method will be invoked at GC time + # @overload initialize(pointer, method) + # @param [Pointer] pointer + # @param [Method] method + # @return [self] + # The passed Method will be invoked at GC time. + # @overload initialize(pointer, proc) + # @param [Pointer] pointer + # @return [self] + # The passed Proc will be invoked at GC time (SEE WARNING BELOW!) + # @note WARNING: passing a proc _may_ cause your pointer to never be GC'd, unless you're + # careful to avoid trapping a reference to the pointer in the proc. See the test + # specs for examples. + # @overload initialize(pointer) { |p| ... } + # @param [Pointer] pointer + # @yieldparam [Pointer] p +pointer+ passed to the block + # @return [self] + # The passed block will be invoked at GC time. + # @note WARNING: passing a block will cause your pointer to never be GC'd. This is bad. + # @overload initialize(pointer) + # @param [Pointer] pointer + # @return [self] + # The pointer's release() class method will be invoked at GC time. # - # WARNING: passing a proc _may_ cause your pointer to never be GC'd, unless you're careful to avoid trapping a reference to the pointer in the proc. See the test specs for examples. - # WARNING: passing a block will cause your pointer to never be GC'd. This is bad. - # - # Please note that the safest, and therefore preferred, calling - # idiom is to pass a Method as the second parameter. Example usage: + # @note The safest, and therefore preferred, calling + # idiom is to pass a Method as the second parameter. Example usage: # # class PointerHelper # def self.release(pointer) @@ -43,12 +57,12 @@ module FFI # # p = AutoPointer.new(other_pointer, PointerHelper.method(:release)) # - # The above code will cause PointerHelper#release to be invoked at GC time. - # - # The last calling idiom (only one parameter) is generally only - # going to be useful if you subclass AutoPointer, and override - # release(), which by default does nothing. + # The above code will cause PointerHelper#release to be invoked at GC time. # + # @note + # The last calling idiom (only one parameter) is generally only + # going to be useful if you subclass {AutoPointer}, and override + # #release, which by default does nothing. def initialize(ptr, proc=nil, &block) super(ptr) raise TypeError, "Invalid pointer" if ptr.nil? || !ptr.kind_of?(Pointer) \ @@ -67,21 +81,36 @@ module FFI self end + # @return [nil] + # Free the pointer. def free @releaser.free end + # @param [Boolean] autorelease + # @return [Boolean] +autorelease+ + # Set +autorelease+ property. See {Pointer Autorelease section at Pointer}. def autorelease=(autorelease) @releaser.autorelease=(autorelease) end + # @abstract Base class for {AutoPointer}'s releasers. + # + # All subclasses of Releaser should define a +#release(ptr)+ method. + # A releaser is an object in charge of release an {AutoPointer}. class Releaser + # @param [Pointer] ptr + # @param [#call] proc + # @return [nil] + # A new instance of Releaser. def initialize(ptr, proc) @ptr = ptr @proc = proc @autorelease = true end + # @return [nil] + # Free pointer. def free if @ptr release(@ptr) @@ -91,33 +120,61 @@ module FFI end end + # @param [Boolean] autorelease + # @return [Boolean] autorelease + # Set +autorelease+ attribute for pointer managed by Releaser. def autorelease=(autorelease) @autorelease = autorelease if @ptr end + # @param args + # Release pointer if +autorelease+ is set. def call(*args) release(@ptr) if @autorelease && @ptr end - + end + # DefaultReleaser is a {Releaser} used when an {AutoPointer} is defined without Proc + # or Method. In this case, the pointer to release must be of a class derived from + # AutoPointer with a +#release+ class method. class DefaultReleaser < Releaser + # @param [Pointer] ptr + # @return [nil] + # Release +ptr+ by using his #release class method. def release(ptr) @proc.release(ptr) end end + # CallableReleaser is a {Releaser} used when an {AutoPointer} is defined with a + # Proc or a Method. class CallableReleaser < Releaser + # @param [Pointer] ptr + # @return [nil] + # Release +ptr+ by using Proc or Method defined at +ptr+ {AutoPointer#initialize initialization}. def release(ptr) @proc.call(ptr) end end + # Return native type of AutoPointer. + # + # Override {DataConverter#native_type}. + # @return [Type::POINTER] + # @raise {RuntimeError} if class does not implement a +#release+ method def self.native_type raise RuntimeError.new("no release method defined for #{self.inspect}") unless self.respond_to?(:release) Type::POINTER end + # Create a new AutoPointer. + # + # Override {DataConverter#from_native}. + # @overload self.from_native(ptr, ctx) + # @param [Pointer] ptr + # @param ctx not used. Please set +nil+. + # @return [AutoPointer] def self.from_native(val, ctx) self.new(val) end diff --git a/lib/ffi/errno.rb b/lib/ffi/errno.rb index e3a6439..d419062 100644 --- a/lib/ffi/errno.rb +++ b/lib/ffi/errno.rb @@ -19,10 +19,15 @@ # module FFI + # @return (see FFI::LastError.error) + # @see FFI::LastError.error def self.errno FFI::LastError.error end + # @param error (see FFI::LastError.error=) + # @return (see FFI::LastError.error=) + # @see FFI::LastError.error= def self.errno=(error) FFI::LastError.error = error end -end
\ No newline at end of file +end diff --git a/lib/ffi/io.rb b/lib/ffi/io.rb index b936e25..3a81714 100644 --- a/lib/ffi/io.rb +++ b/lib/ffi/io.rb @@ -19,15 +19,26 @@ # module FFI + + # This module implements a couple of class methods to play with IO. module IO + # @param [Integer] fd file decriptor + # @param [String] mode mode string + # @return [::IO] + # Synonym for IO::for_fd. def self.for_fd(fd, mode = "r") ::IO.for_fd(fd, mode) end + # @param [#read] io io to read from + # @param [AbstractMemory] buf destination for data read from +io+ + # @param [nil, Numeric] len maximul number of bytes to read from +io+. If +nil+, + # read until end of file. + # @return [Numeric] length really read, in bytes # - # A version of IO#read that reads into a native buffer + # A version of IO#read that reads data from an IO and put then into a native buffer. # - # This will be optimized at some future time to eliminate the double copy + # This will be optimized at some future time to eliminate the double copy. # def self.native_read(io, buf, len) tmp = io.read(len) diff --git a/lib/ffi/library.rb b/lib/ffi/library.rb index 8ab4830..5626d9d 100644 --- a/lib/ffi/library.rb +++ b/lib/ffi/library.rb @@ -21,6 +21,16 @@ module FFI CURRENT_PROCESS = USE_THIS_PROCESS_AS_LIBRARY = Object.new + # @param [#to_s] lib library name + # @return [String] library name formatted for current platform + # Transform a generic library name to a platform library name + # @example + # # Linux + # FFI.map_library_name 'c' # -> "libc.so.6" + # FFI.map_library_name 'jpeg' # -> "libjpeg.so" + # # Windows + # FFI.map_library_name 'c' # -> "msvcrt.dll" + # FFI.map_library_name 'jpeg' # -> "jpeg.dll" def self.map_library_name(lib) # Mangle the library name to reflect the native library naming conventions lib = lib.to_s unless lib.kind_of?(String) @@ -35,20 +45,44 @@ module FFI lib end + # Exception raised when a function is not found in libraries class NotFoundError < LoadError def initialize(function, *libraries) super("Function '#{function}' not found in [#{libraries[0].nil? ? 'current process' : libraries.join(", ")}]") end end + # This module is the base to use native functions. + # + # A basic usage may be: + # require 'ffi' + # + # module Hello + # extend FFI::Library + # ffi_lib FFI::Library::LIBC + # attach_function 'puts', [ :string ], :int + # end + # + # Hello.puts("Hello, World") + # + # module Library CURRENT_PROCESS = FFI::CURRENT_PROCESS LIBC = FFI::Platform::LIBC + # @param mod extended object + # @return [nil] + # @raise {RuntimeError} if +mod+ is not a Module + # Test if extended object is a Module. If not, raise RuntimeError. def self.extended(mod) raise RuntimeError.new("must only be extended by module") unless mod.kind_of?(Module) end + + # @param [Array] names names of libraries to load + # @return [Array<DynamicLibrary>] + # @raise {LoadError} if a library cannot be opened + # Load native libraries. def ffi_lib(*names) lib_flags = defined?(@ffi_lib_flags) ? @ffi_lib_flags : FFI::DynamicLibrary::RTLD_LAZY | FFI::DynamicLibrary::RTLD_LOCAL ffi_libs = names.map do |name| @@ -109,6 +143,7 @@ module FFI # @see #ffi_lib # @return [Array<FFI::DynamicLibrary>] array of currently loaded FFI libraries # @raise [LoadError] if no libraries have been loaded (using {#ffi_lib}) + # Get FFI libraries loaded using {#ffi_lib}. def ffi_libraries raise LoadError.new("no library specified") if !defined?(@ffi_libs) || @ffi_libs.empty? @ffi_libs @@ -125,7 +160,7 @@ module FFI :now => DynamicLibrary::RTLD_NOW } - # Sets library flags for {#ffi_lib} + # Sets library flags for {#ffi_lib}. # # @example # ffi_lib_flags(:lazy, :local) # => 5 @@ -139,25 +174,24 @@ module FFI ## # @overload attach_function(func, args, returns, options = {}) + # @example attach function without an explicit name + # module Foo + # extend FFI::Library + # ffi_lib FFI::Library::LIBC + # attach_function :malloc, [:size_t], :pointer + # end + # # now callable via Foo.malloc # @overload attach_function(name, func, args, returns, options = {}) + # @example attach function with an explicit name + # module Bar + # extend FFI::Library + # ffi_lib FFI::Library::LIBC + # attach_function :c_malloc, :malloc, [:size_t], :pointer + # end + # # now callable via Bar.c_malloc # # Attach C function +func+ to this module. # - # @example attach function without an explicit name - # module Foo - # extend FFI::Library - # ffi_lib FFI::Library::LIBC - # attach_function :malloc, [:size_t], :pointer - # end - # # now callable via Foo.malloc - # - # @example attach function with an explicit name - # module Bar - # extend FFI::Library - # ffi_lib FFI::Library::LIBC - # attach_function :c_malloc, :malloc, [:size_t], :pointer - # end - # # now callable via Bar.c_malloc # # @param [#to_s] name name of ruby method to attach as # @param [#to_s] func name of C function to attach @@ -165,10 +199,10 @@ module FFI # @param [Symbol] returns type of return value # @option options [Boolean] :blocking (@blocking) set to true if the C function is a blocking call # @option options [Symbol] :convention (:default) calling convention (see {#ffi_convention}) - # @option options :enums - # @option options :type_map + # @option options [FFI::Enums] :enums + # @option options [Hash] :type_map # - # @return [FFI::VariadicInvoker, FFI::Function] + # @return [FFI::VariadicInvoker] # # @raise [FFI::NotFoundError] if +func+ cannot be found in the attached libraries (see {#ffi_lib}) def attach_function(name, func, args, returns = nil, options = nil) @@ -216,18 +250,17 @@ module FFI invoker end + # @param [#to_s] name function name + # @param [Array] arg_types function's argument types + # @return [Array<String>] + # This function returns a list of possible names to lookup. + # @note Function names on windows may be decorated if they are using stdcall. See + # * http://en.wikipedia.org/wiki/Name_mangling#C_name_decoration_in_Microsoft_Windows + # * http://msdn.microsoft.com/en-us/library/zxk0tw93%28v=VS.100%29.aspx + # * http://en.wikibooks.org/wiki/X86_Disassembly/Calling_Conventions#STDCALL + # Note that decorated names can be overridden via def files. Also note that the + # windows api, although using, doesn't have decorated names. def function_names(name, arg_types) - # Function names on windows may be decorated if they are using stdcall. See - # - # http://en.wikipedia.org/wiki/Name_mangling#C_name_decoration_in_Microsoft_Windows - # http://msdn.microsoft.com/en-us/library/zxk0tw93%28v=VS.100%29.aspx - # http://en.wikibooks.org/wiki/X86_Disassembly/Calling_Conventions#STDCALL - # - # Note that decorated names can be overridden via def files. Also note that the - # windows api, although using, doesn't have decorated names. - # - # This function returns a list of possible names to lookup. - result = [name.to_s] if @ffi_convention == :stdcall # Get the size of each parameter @@ -244,6 +277,29 @@ module FFI result end + # @overload attach_variable(mname, cname, type) + # @example + # module Bar + # extend FFI::Library + # ffi_lib 'my_lib' + # attach_variable :c_myvar, :myvar, :long + # end + # # now callable via Bar.c_myvar + # @overload attach_variable(cname, type) + # @example + # module Bar + # extend FFI::Library + # ffi_lib 'my_lib' + # attach_variable :myvar, :long + # end + # # now callable via Bar.myvar + # @param [#to_s] mname name of ruby method to attach as + # @param [#to_s] cname name of C variable to attach + # @param [DataConverter, Struct, Symbol, Type] type C varaible's type + # @return [DynamicLibrary::Symbol] + # @raise {FFI::NotFoundError} if +cname+ cannot be found in libraries + # + # Attach C variable +cname+ to this module. def attach_variable(mname, a1, a2 = nil) cname, type = a2 ? [ a1, a2 ] : [ mname.to_s, a1 ] address = nil @@ -264,7 +320,7 @@ module FFI def self.#{mname} @@ffi_gvar_#{mname} end - code +code else sc = Class.new(FFI::Struct) @@ -288,6 +344,13 @@ module FFI address end + + # @overload callback(name, params, ret) + # @overload callback(params, ret) + # @param name callback name to add to type map + # @param [Array] params array of parameters' types + # @param [DataConverter, Struct, Symbol, Type] ret callback return type + # @return [FFI::CallbackInfo] def callback(*args) raise ArgumentError, "wrong number of arguments" if args.length < 2 || args.length > 3 name, params, ret = if args.length == 3 @@ -309,6 +372,22 @@ module FFI cb end + # @param [DataConverter, Symbol, Type] old + # @param add + # @param [] info + # @return [FFI::Enum, FFI::Type] + # Register or get an already registered type definition. + # + # To register a new type definition, +old+ should be a {FFI::Type}. +add+ + # is in this case the type definition. + # + # If +old+ is a {DataConverter}, a {Type::Mapped} is returned. + # + # If +old+ is +:enum+ + # * and +add+ is an +Array+, a call to {#enum} is made with +add+ as single parameter; + # * in others cases, +info+ is used to create a named enum. + # + # If +old+ is a key for type map, #typedef get +old+ type definition. def typedef(old, add, info=nil) @ffi_typedefs = Hash.new unless defined?(@ffi_typedefs) @@ -333,13 +412,25 @@ module FFI end end + # @overload enum(name, values) + # Create a named enum. + # @example + # enum :foo, [:zero, :one, :two] # named enum + # @param [Symbol] name name for new enum + # @param [Array] values values for enum + # @overload enum(*args) + # Create an unnamed enum. + # @example + # enum :zero, :one, :two # unnamed enum + # @param args values for enum + # @overload enum(values) + # Create an unnamed enum. + # @example + # enum [:zero, :one, :two] # unnamed enum, equivalent to above example + # @param [Array] values values for enum + # @return [FFI::Enum] + # Create a new {FFI::Enum}. def enum(*args) - # - # enum can be called as: - # enum :zero, :one, :two # unnamed enum - # enum [ :zero, :one, :two ] # equivalent to above - # enum :foo, [ :zero, :one, :two ] create an enum named :foo - # name, values = if args[0].kind_of?(Symbol) && args[1].kind_of?(Array) [ args[0], args[1] ] elsif args[0].kind_of?(Array) @@ -355,14 +446,23 @@ module FFI e end + # @param name + # @return [FFI::Enum] + # Find an enum by name. def enum_type(name) @ffi_enums.find(name) if defined?(@ffi_enums) end + # @param symbol + # @return [FFI::Enum] + # Find an enum by a symbol it contains. def enum_value(symbol) @ffi_enums.__map_symbol(symbol) end + # @param [DataConverter, Type, Struct, Symbol] t type to find + # @return [Type] + # Find a type definition. def find_type(t) if t.kind_of?(Type) t @@ -380,4 +480,4 @@ module FFI end || FFI.find_type(t) end end -end
\ No newline at end of file +end diff --git a/lib/ffi/platform.rb b/lib/ffi/platform.rb index ac0aecb..ae9119a 100644 --- a/lib/ffi/platform.rb +++ b/lib/ffi/platform.rb @@ -23,6 +23,8 @@ require 'rbconfig' module FFI class PlatformError < LoadError; end + # This module defines different constants and class methods to play with + # various platforms. module Platform OS = case RbConfig::CONFIG['host_os'].downcase when /gnu/ @@ -53,6 +55,9 @@ module FFI end private + # @param [String) os + # @return [Boolean] + # Test if current OS is +os+. def self.is_os(os) OS == os end @@ -92,18 +97,26 @@ module FFI "#{LIBPREFIX}c.#{LIBSUFFIX}" end + # Test if current OS is a *BSD (include MAC) + # @return [Boolean] def self.bsd? IS_BSD end + # Test if current OS is Windows + # @return [Boolean] def self.windows? IS_WINDOWS end + # Test if current OS is Mac OS + # @return [Boolean] def self.mac? IS_MAC end + # Test if current OS is a unix OS + # @return [Boolean] def self.unix? !IS_WINDOWS end diff --git a/lib/ffi/types.rb b/lib/ffi/types.rb index e721ba6..42ca6ae 100644 --- a/lib/ffi/types.rb +++ b/lib/ffi/types.rb @@ -140,7 +140,7 @@ module FFI # @param [Pointer] val # @param [] ctx - # @return [Array<String, Pointer>] + # @return [Array(String, Pointer)] # Returns a [ String, Pointer ] tuple so the C memory for the string can be freed def self.from_native(val, ctx) [ val.null? ? nil : val.get_string(0), val ] |