# class Fiddle::Function

## Description¶ ↑

A representation of a C function

## Examples¶ ↑

### 'strcpy'¶ ↑

    @libc = Fiddle.dlopen "/lib/libc.so.6"
       #=> #<Fiddle::Handle:0x00000001d7a8d8>
    f = Fiddle::Function.new(
      @libc['strcpy'],
      [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP],
      Fiddle::TYPE_VOIDP)
       #=> #<Fiddle::Function:0x00000001d8ee00>
    buff = "000"
       #=> "000"
    str = f.call(buff, "123")
       #=> #<Fiddle::Pointer:0x00000001d0c380 ptr=0x000000018a21b8 size=0 free=0x00000000000000>
    str.to_s
    => "123"

### ABI check¶ ↑

    @libc = Fiddle.dlopen "/lib/libc.so.6"
       #=> #<Fiddle::Handle:0x00000001d7a8d8>
    f = Fiddle::Function.new(@libc['strcpy'], [TYPE_VOIDP, TYPE_VOIDP], TYPE_VOIDP)
       #=> #<Fiddle::Function:0x00000001d8ee00>
    f.abi == Fiddle::Function::DEFAULT
       #=> true

### Constants

DEFAULT

  `DEFAULT`

  Default ABI

STDCALL

  `STDCALL`

  FFI implementation of WIN32 stdcall convention

### Attributes

abi[R]

The ABI of the `Function`.

name[R]

The name of this function

ptr[R]

The address of this function

### Public Class Methods

new(ptr, args, ret_type, abi = DEFAULT) click to toggle source

Constructs a `Function` object.

- `ptr` is a referenced function, of a `Fiddle::Handle`

- `args` is an `Array` of arguments, passed to the `ptr` function

- `ret_type` is the return type of the function

- `abi` is the ABI of the function

    static VALUE
    initialize(int argc, VALUE argv[], VALUE self)
    {
        ffi_cif * cif;
        ffi_type **arg_types, *rtype;
        ffi_status result;
        VALUE ptr, args, ret_type, abi, kwds, ary;
        int i, len;
        int nabi;
        void *cfunc;

        rb_scan_args(argc, argv, "31:", &ptr, &args, &ret_type, &abi, &kwds);
        ptr = rb_Integer(ptr);
        cfunc = NUM2PTR(ptr);
        PTR2NUM(cfunc);
        nabi = NIL_P(abi) ? FFI_DEFAULT_ABI : NUM2INT(abi);
        abi = INT2FIX(nabi);
        i = NUM2INT(ret_type);
        rtype = INT2FFI_TYPE(i);
        ret_type = INT2FIX(i);

        Check_Type(args, T_ARRAY);
        len = RARRAY_LENINT(args);
        Check_Max_Args("args", len);
        ary = rb_ary_subseq(args, 0, len);
        for (i = 0; i < RARRAY_LEN(args); i++) {
            VALUE a = RARRAY_AREF(args, i);
            int type = NUM2INT(a);
            (void)INT2FFI_TYPE(type); /* raise */
            if (INT2FIX(type) != a) rb_ary_store(ary, i, INT2FIX(type));
        }
        OBJ_FREEZE(ary);

        rb_iv_set(self, "@ptr", ptr);
        rb_iv_set(self, "@args", args);
        rb_iv_set(self, "@return_type", ret_type);
        rb_iv_set(self, "@abi", abi);

        if (!NIL_P(kwds)) rb_hash_foreach(kwds, parse_keyword_arg_i, self);

        TypedData_Get_Struct(self, ffi_cif, &function_data_type, cif);

        arg_types = xcalloc(len + 1, sizeof(ffi_type *));

        for (i = 0; i < RARRAY_LEN(args); i++) {
            int type = NUM2INT(RARRAY_AREF(args, i));
            arg_types[i] = INT2FFI_TYPE(type);
        }
        arg_types[len] = NULL;

        result = ffi_prep_cif(cif, nabi, len, rtype, arg_types);

        if (result)
            rb_raise(rb_eRuntimeError, "error creating CIF %d", result);

        return self;
    }

### Public Instance Methods

call(*args) click to toggle source

Calls the constructed `Function`, with `args`. Caller must ensure the underlying function is called in a thread-safe manner if running in a multi-threaded process.

For an example see `Fiddle::Function`

    static VALUE
    function_call(int argc, VALUE argv[], VALUE self)
    {
        struct nogvl_ffi_call_args args = { 0 };
        fiddle_generic *generic_args;
        VALUE cfunc, types, cPointer;
        int i;
        VALUE alloc_buffer = 0;

        cfunc    = rb_iv_get(self, "@ptr");
        types    = rb_iv_get(self, "@args");
        cPointer = rb_const_get(mFiddle, rb_intern("Pointer"));

        Check_Max_Args("number of arguments", argc);
        if (argc != (i = RARRAY_LENINT(types))) {
            rb_error_arity(argc, i, i);
        }

        TypedData_Get_Struct(self, ffi_cif, &function_data_type, args.cif);

        if (rb_safe_level() >= 1) {
            for (i = 0; i < argc; i++) {
                VALUE src = argv[i];
                if (OBJ_TAINTED(src)) {
                    rb_raise(rb_eSecurityError, "tainted parameter not allowed");
                }
            }
        }

        generic_args = ALLOCV(alloc_buffer,
            (size_t)(argc + 1) * sizeof(void *) + (size_t)argc * sizeof(fiddle_generic));
        args.values = (void **)((char *)generic_args +
                                (size_t)argc * sizeof(fiddle_generic));

        for (i = 0; i < argc; i++) {
            VALUE type = RARRAY_AREF(types, i);
            VALUE src = argv[i];
            int argtype = FIX2INT(type);

            if (argtype == TYPE_VOIDP) {
                if(NIL_P(src)) {
                    src = INT2FIX(0);
                } else if(cPointer != CLASS_OF(src)) {
                    src = rb_funcall(cPointer, rb_intern("[]"), 1, src);
                }
                src = rb_Integer(src);
            }

            VALUE2GENERIC(argtype, src, &generic_args[i]);
            args.values[i] = (void *)&generic_args[i];
        }
        args.values[argc] = NULL;
        args.fn = (void(*)(void))NUM2PTR(cfunc);

        (void)rb_thread_call_without_gvl(nogvl_ffi_call, &args, 0, 0);

        rb_funcall(mFiddle, rb_intern("last_error="), 1, INT2NUM(errno));
    #if defined(_WIN32)
        rb_funcall(mFiddle, rb_intern("win32_last_error="), 1, INT2NUM(errno));
    #endif

        ALLOCV_END(alloc_buffer);

        return GENERIC2VALUE(rb_iv_get(self, "@return_type"), args.retval);
    }

to_i() click to toggle source

The integer memory location of this function

    # File ext/fiddle/lib/fiddle/function.rb, line 14
    def to_i
      ptr.to_i
    end