# File lib/ffi/library.rb, line 93
    def attach_function(mname, a3, a4, a5=nil)
      cname, arg_types, ret_type = a5 ? [ a3, a4, a5 ] : [ mname.to_s, a3, a4 ]

      # Convert :foo to the native type
      arg_types.map! { |e| find_type(e) }
      has_callback = arg_types.any? {|t| t.kind_of?(FFI::CallbackInfo)}
      options = Hash.new
      options[:convention] = defined?(@ffi_convention) ? @ffi_convention : :default
      options[:type_map] = @ffi_typedefs if defined?(@ffi_typedefs)
      options[:enums] = @ffi_enums if defined?(@ffi_enums)

      # Try to locate the function in any of the libraries
      invokers = []
      ffi_libraries.each do |lib|
        begin
          invokers << FFI.create_invoker(lib, cname.to_s, arg_types, find_type(ret_type), options)
        rescue LoadError => ex
        end if invokers.empty?
      end
      invoker = invokers.compact.shift
      raise FFI::NotFoundError.new(cname.to_s, ffi_libraries.map { |lib| lib.name }) unless invoker

      # Setup the parameter list for the module function as (a1, a2)
      arity = arg_types.length
      params = (1..arity).map {|i| "a#{i}" }.join(",")

      # Always use rest args for functions with callback parameters
      if has_callback || invoker.kind_of?(FFI::VariadicInvoker)
        params = "*args, &block"
      end
      call = arity <= 3 && !has_callback && !invoker.kind_of?(FFI::VariadicInvoker)? "call#{arity}" : "call"

      #
      # Attach the invoker to this module as 'mname'.
      #
      if !has_callback && !invoker.kind_of?(FFI::VariadicInvoker)
        invoker.attach(self, mname.to_s)
      else
        self.module_eval "@@\#{mname} = invoker\ndef self.\#{mname}(\#{params})\n@@\#{mname}.\#{call}(\#{params})\nend\ndef \#{mname}(\#{params})\n@@\#{mname}.\#{call}(\#{params})\nend\n"
      end
      invoker
    end