How can I call (not define) a function with a variable number of arguments in C?

前端 未结 6 1752
感情败类
感情败类 2021-01-20 01:05

Is there any way to make this code shorter?

long call_f(int argc, long *argv) {
  switch (argc) {
    case 0:
      return f();
      break;
    case 1:
             


        
相关标签:
6条回答
  • 2021-01-20 01:13

    There's actually a method to call a function at run-time if you know its calling convention and which parameters it receives. This however lies out of the standard C/C++ language scope.

    For x86 assembler:

    Assuming the following:

    1. You know to prepare all the parameters for your function in a solid buffer, exactly in the manner they'd be packed on the stack.
    2. Your function doesn't take/return C++ objects by value.

    You may use then the following function:

    int CallAnyFunc(PVOID pfn, PVOID pParams, size_t nSizeParams)
    {
        // Reserve the space on the stack
        // This is equivalent (in some sense) to 'push' all the parameters into the stack.
        // NOTE: Don't just subtract the stack pointer, better to call _alloca, because it also takes
        // care of ensuring all the consumed memory pages are accessible
        _alloca(nSizeParams);
    
        // Obtain the stack top pointer
        char* pStack;
        _asm {
            mov pStack, esp
        };
    
        // Copy all the parameters into the stack
        // NOTE: Don't use the memcpy function. Because the call to it
        // will overwrite the stack (which we're currently building)
        for (size_t i = 0; i < nSizeParams; i++)
            pStack[i] = ((char*) pParams)[i];
    
        // Call your function
        int retVal;
        _asm {
            call pfn
            // Most of the calling conventions return the value of the function (if anything is returned)
            // in EAX register
            mov retVal, eax
        };
    
        return retVal;
    }
    

    You may need to adjust this function, depending on the calling convention used

    0 讨论(0)
  • 2021-01-20 01:18

    No, there isn't any good way to do this. See here: http://c-faq.com/varargs/handoff.html

    You can write a macro with token pasting to hide this behavior but that macro will be no simpler than this code, thus it's only worth writing if you have multiple functions like f() where you would otherwise have to duplicate this case statement.

    0 讨论(0)
  • 2021-01-20 01:18

    I don't know how you can make your code shorter but I saw this line in your code:

     return f();
    

    From the next calls to f function, it seems that f is a function that takes variable number of arguments.

    You can read in wikipedia that:

    Variadic functions must have at least one named parameter, so, for instance,

    char *wrong(...);

    is not allowed in C.

    Based on that, maybe the return f(); statement is causing you trouble?

    0 讨论(0)
  • 2021-01-20 01:21

    I'll post here the same answer as I posted at the duplicated question, but you should take a look at the discussion there:

    What is libffi?

    Some programs may not know at the time of compilation what arguments are to be passed to a function. For instance, an interpreter may be told at run-time about the number and types of arguments used to call a given function. ‘libffi’ can be used in such programs to provide a bridge from the interpreter program to compiled code.

    The ‘libffi’ library provides a portable, high level programming interface to various calling conventions. This allows a programmer to call any function specified by a call interface description at run time.

    FFI stands for Foreign Function Interface. A foreign function interface is the popular name for the interface that allows code written in one language to call code written in another language. The ‘libffi’ library really only provides the lowest, machine dependent layer of a fully featured foreign function interface. A layer must exist above ‘libffi’ that handles type conversions for values passed between the two languages.

    ‘libffi’ assumes that you have a pointer to the function you wish to call and that you know the number and types of arguments to pass it, as well as the return type of the function.


    Historic background

    libffi, originally developed by Anthony Green (SO user: anthony-green), was inspired by the Gencall library from Silicon Graphics. Gencall was developed by Gianni Mariani, then employed by SGI, for the purpose of allowing calls to functions by address and creating a call frame for the particular calling convention. Anthony Green refined the idea and extended it to other architectures and calling conventions and open sourcing libffi.


    Calling pow with libffi

    #include <stdio.h>
    #include <math.h>
    #include <ffi.h>
    
    int main()
    {
      ffi_cif     call_interface;
      ffi_type    *ret_type;
      ffi_type    *arg_types[2];
    
      /* pow signature */
      ret_type = &ffi_type_double;
      arg_types[0] = &ffi_type_double;
      arg_types[1] = &ffi_type_double;
    
      /* prepare pow function call interface */
      if (ffi_prep_cif(&call_interface, FFI_DEFAULT_ABI, 2, ret_type, arg_types) == FFI_OK)
      {
        void *arg_values[2];
        double x, y, z;
    
        /* z stores the return */
        z = 0;
    
        /* arg_values elements point to actual arguments */
        arg_values[0] = &x;
        arg_values[1] = &y;
    
        x = 2;
        y = 3;
    
        /* call pow */
        ffi_call(&call_interface, FFI_FN(pow), &z, arg_values);
    
        /* 2^3=8 */
        printf("%.0f^%.0f=%.0f\n", x, y, z);
      }
    
      return 0;
    }
    

    I think I can assert libffi is a portable way to do what I asked, contrary to Antti Haapala's assertion that there isn't such a way. If we can't call libffi a portable technology, given how far it's ported/implemented across compilers and architectures, and which interface complies with C standard, we too can't call C, or anything, portable.

    Information and history extracted from:

    https://github.com/atgreen/libffi/blob/master/doc/libffi.info

    http://en.wikipedia.org/wiki/Libffi

    0 讨论(0)
  • 2021-01-20 01:28

    Does f have to accept a variable number of pointers to long? Can you rewrite it to accept an array and a count?

    0 讨论(0)
  • 2021-01-20 01:33

    You can check out my answer to:

    Best Way to Store a va_list for Later Use in C/C++

    Which seems to work, yet scare people. It's not guaranteed cross-platform or portable, but it seems to be workable on a couple of platforms, at least. ;)

    0 讨论(0)
提交回复
热议问题