从内存位置调用函数指针

时间:2015-11-12 19:22:48

标签: c++ function-pointers

我试图将函数指针调用为uint64_t值,并传递其地址存储在uint64_t结构中的参数。 这是代码。

double sum(double *a, long len){
    double tmp = 0;
    for(size_t i=0;i<len;i++){
        tmp += a[i];
    }
    return tmp;
}

int main(){
    long size = 1000;
    double *a = new double[size];

    for(int i=0;i<size;i++){
        a[i] = i*1.0;
    }

    struct __attribute__ ((aligned(16))) args_t {
        double *a_func;
        long len_func;
    } args;

    args.a_func = a;
    args.len_func = size;

    double ret;

    uint64_t arg_ptr = reinterpret_cast<uint64_t>(&args);
    uint64_t arg_size = sizeof(args);
    uint64_t func_ptr = reinterpret_cast<uint64_t>(&sum);
    uint64_t func_ret = reinterpret_cast<uint64_t>(&ret);
// How should I call the function with arguments passed to it and get a return value?

}

Moto:我正在尝试构建一个库,它接受任何函数指针,它的参数和返回地址,它执行函数并通过返回地址返回值。 谢谢! :)

3 个答案:

答案 0 :(得分:1)

试试这个。您在结构中缺少函数指针声明。

此外,您的func_ptr和func_ret调用未正确输入。我不会在uint64_t中保存指针类型,我建议使用void *,因为它将是您的架构的正确长度。你绝对不应该在uint64_t中保存一个函数指针,因为标准不能保证sizeof(funcptr) == sizeof(void *)

double sum( double * a, long len )
{
    //...
    return 0.0;
}

int main(int argc, char const *argv[])
{
    long size = 1000;
    double * a = new double[size];

    struct args_t
    {
        /** you need a ptr declaration */
        double (*func_hsa)(double *, long );
        double * a_hsa;
        long len_hsa;
    } args;

    args.func_hsa = sum;
    args.a_hsa = a;
    args.len_hsa = size;

    double ret;

    ret = args.func_hsa(args.a_hsa, args.len_hsa);

    return 0;
}

答案 1 :(得分:1)

  

Moto:我正在尝试构建一个库,它接受任何函数指针,它的参数和返回地址,它执行函数并通过返回地址返回值。

您需要至少在运行时动态地知道被调用函数的signature。 由于各种application binary interface(ABI)约定规定了不同且不兼容的calling conventions(例如,double参数在浮点寄存器中传递,但int值在某个整数寄存器中传递,这是处理器和ABI特定的,然后是编译器和链接器等...),你应该使用一个特定的库(它包含一些处理器和ABI特定的汇编代码)。 libffi就是这样一个库,您应该使用它。如果您无法使用它,请研究您的实施的ABI,例如: {/ 3}}适用于Linux / x86-64 ABI。

  

我试图将函数指针调用为uint64_t值,并传递其地址存储在uint64_t结构中的参数。

你不能以可移植的方式(不使用像this ...这样的东西)(即你需要一些处理器和ABI特定的代码,可能是汇编程序)。您隐含地假设参数是在某个堆栈上传递的,或者至少是通过可寻址的内存传递的,这通常是错误的,并且对于大多数libffi系统来说是错误的:大多数参数通常在x86-64中传递(和详细信息是处理器和ABI特定的,因此在Windows和Linux上都有所不同。)

问题不是函数指针,它是调用约定和传递的参数(以及结果传递)。

BTW,在C和C ++中,可转换为指针的整数类型为intptr_t(来自C中的<stdint.h>,来自C ++中的<cstdint>;在ARM和&amp ;;等32位体系结构上使用(和来自/来自的函数指针)int64_t。 x86不合适。

正如我评论的那样,如果您至少可以使用processor registersC++11(即closures)和lambda functions非常有帮助。读 也是关于std::function

另见callbacks的GCC内置。

答案 2 :(得分:0)

所以, 最后我开始工作了。

double sum(double *a, long len){
    double tmp = 0;
    for(size_t i=0;i<len;i++){
        tmp += a[i];
    }
    return tmp;
}

int main(){
    long size = 1000;
    double *a = new double[size];

    for(int i=0;i<size;i++){
        a[i] = i*1.0;
    }

    struct __attribute__ ((aligned(16))) args_t {
        double *a_func;
        long len_func;
    } args;

    args.a_func = a;
    args.len_func = size;

    double ret;

    uint64_t arg_ptr = reinterpret_cast<uint64_t>(&args);
    uint64_t arg_size = sizeof(args);
    uint64_t func_ptr = reinterpret_cast<uint64_t>(&sum);
    uint64_t func_ret = reinterpret_cast<uint64_t>(&ret);

    // We are trying to make a generic function caller
    size_t num_args = arg_size/8; // as elements of struct are 64bit long
    uint64_t *varg = reinterpret_cast<uint64_t*>(arg_ptr);
    switch(num_args){
        case 2:
            {
                void* (*fun)(void*, void*) = reinterpret_cast<void* (*)(void*, void*)>(func_ptr);
                uint64_t *ret_add = reinterpret_cast<uint64_t*>(func_ret);
                *ret_add = reinterpret_cast<uint64_t>(fun((void*)varg[0], (void*)varg[1]));
                break;
            }
        default:
            break;
    }
}

使用switch case不是一种正确的方法,但没有更好的方法来构建展开而不使用任何包或使用最新标准。虽然C ++ 14具有将元组作为参数列表传递的功能,但它并未完全实现。切换案例可以扩展为多个参数。