    # File lib/ffi/library.rb, line 210
        def attach_function(name, func, args, returns = nil, options = nil)
          mname, a2, a3, a4, a5 = name, func, args, returns, options
          cname, arg_types, ret_type, opts = (a4 && (a2.is_a?(String) || a2.is_a?(Symbol))) ? [ a2, a3, a4, a5 ] : [ mname.to_s, a2, a3, a4 ]

          # Convert :foo to the native type
          arg_types.map! { |e| find_type(e) }
          options = {
            :convention => ffi_convention,
            :type_map => defined?(@ffi_typedefs) ? @ffi_typedefs : nil,
            :blocking => defined?(@blocking) && @blocking,
            :enums => defined?(@ffi_enums) ? @ffi_enums : nil,
          }

          @blocking = false
          options.merge!(opts) if opts && opts.is_a?(Hash)

          # Try to locate the function in any of the libraries
          invokers = []
          ffi_libraries.each do |lib|
            if invokers.empty?
              begin
                function = nil
                function_names(cname, arg_types).find do |fname|
                  function = lib.find_function(fname)
                end
                raise LoadError unless function

                invokers << if arg_types.length > 0 && arg_types[arg_types.length - 1] == FFI::NativeType::VARARGS
                  VariadicInvoker.new(function, arg_types, find_type(ret_type), options)

                else
                  Function.new(find_type(ret_type), arg_types, function, options)
                end

              rescue LoadError
              end
            end
          end
          invoker = invokers.compact.shift
          raise FFI::NotFoundError.new(cname.to_s, ffi_libraries.map { |lib| lib.name }) unless invoker

          invoker.attach(self, mname.to_s)
          invoker
        end