c++ 我怎样才能传递一个成员函数到需要自由函数的地方?

cygmwpex  于 2023-01-22  发布在  其他
关注(0)|答案(9)|浏览(115)

问题如下:考虑这段代码:

#include <iostream>

class aClass
{
public:
    void aTest(int a, int b)
    {
        printf("%d + %d = %d", a, b, a + b);
    }
};

void function1(void (*function)(int, int))
{
    function(1, 1);
}

void test(int a,int b)
{
    printf("%d - %d = %d", a , b , a - b);
}

int main()
{
    aClass a;

    function1(&test);
    function1(&aClass::aTest); // <-- How should I point to a's aClass::test function?
}

如何使用aaClass::test作为function1的参数?我想访问类的成员。

rbpvctlc

rbpvctlc1#

使用函数指针并没有什么错,但是指向非静态成员函数的指针与普通的函数指针不同:成员函数需要在一个对象上调用,该对象作为隐式参数传递给函数。上面的成员函数的签名是,因此

void (aClass::*)(int, int)

而不是您尝试使用的类型

void (*)(int, int)

一种方法是将成员函数设为static,在这种情况下,它不需要调用任何对象,并且可以使用void (*)(int, int)类型。
如果你需要访问类 * 和 * 的任何非静态成员,你需要坚持使用函数指针,例如,因为函数是C接口的一部分,你最好的选择是总是传递一个void*到你的函数,接受函数指针,并通过一个转发函数调用你的成员,该转发函数从void*获得一个对象,然后调用成员函数。
在一个合适的C++接口中,你可能需要让你的函数接受模板化参数来使用函数对象的任意类类型。如果不需要使用模板化接口,你应该使用类似std::function<void(int, int)>的东西:可以为这些创建适当的可调用函数对象,例如使用std::bind()
使用类类型的模板参数或合适的std::function<...>的类型安全方法优于使用void*接口,因为它们消除了由于强制转换为错误类型而导致错误的可能性。
为了阐明如何使用函数指针来调用成员函数,下面是一个示例:

// the function using the function pointers:
void somefunction(void (*fptr)(void*, int, int), void* context) {
    fptr(context, 17, 42);
}

void non_member(void*, int i0, int i1) {
    std::cout << "I don't need any context! i0=" << i0 << " i1=" << i1 << "\n";
}

struct foo {
    void member(int i0, int i1) {
        std::cout << "member function: this=" << this << " i0=" << i0 << " i1=" << i1 << "\n";
    }
};

void forwarder(void* context, int i0, int i1) {
    static_cast<foo*>(context)->member(i0, i1);
}

int main() {
    somefunction(&non_member, nullptr);
    foo object;
    somefunction(&forwarder, &object);
}
hfsqlsce

hfsqlsce2#

@Pete Becker的答案是正确的,但是在C++ 11中,您也可以不将class示例作为显式参数传递给function1

#include <functional>
using namespace std::placeholders;

void function1(std::function<void(int, int)> fun)
{
    fun(1, 1);
}

int main (int argc, const char * argv[])
{
   ...

   aClass a;
   auto fp = std::bind(&aClass::test, a, _1, _2);
   function1(fp);

   return 0;
}
cvxl0en2

cvxl0en23#

指向成员函数的指针与指向函数的指针不同,为了通过指针使用成员函数,你需要一个指向它的指针(很明显)和一个应用它的对象,所以function1的合适版本应该是

void function1(void (aClass::*function)(int, int), aClass& a) {
    (a.*function)(1, 1);
}

并称之为:

aClass a; // note: no parentheses; with parentheses it's a function declaration
function1(&aClass::test, a);
6qftjkof

6qftjkof4#

从2011年开始,如果可以更改function1,请按如下方式进行更改:

#include <functional>
#include <cstdio>

using namespace std;

class aClass
{
public:
    void aTest(int a, int b)
    {
        printf("%d + %d = %d", a, b, a + b);
    }
};

template <typename Callable>
void function1(Callable f)
{
    f(1, 1);
}

void test(int a,int b)
{
    printf("%d - %d = %d", a , b , a - b);
}

int main()
{
    aClass obj;

    // Free function
    function1(&test);

    // Bound member function
    using namespace std::placeholders;
    function1(std::bind(&aClass::aTest, obj, _1, _2));

    // Lambda
    function1([&](int a, int b) {
        obj.aTest(a, b);
    });
}

live demo

还要注意,我修复了损坏的对象定义(aClass a();声明了一个函数)。

rta7y2nd

rta7y2nd5#

我问了一个类似的问题(C++ openframeworks passing void from other classes),但我找到的答案更清楚,所以在这里解释未来的记录:
使用std::函数更容易,如下所示:

void draw(int grid, std::function<void()> element)

然后调用为:

grid.draw(12, std::bind(&BarrettaClass::draw, a, std::placeholders::_1));

或者更容易:

grid.draw(12, [&]{a.draw()});

在这里你创建一个lambda来调用通过引用捕获它的对象

zz2j4svz

zz2j4svz6#

需要注意的是,除非你可以改变接受函数的代码的签名,否则没有(简单的)方法可以做到这一点,那就是尝试在一种没有与函数相同的闭包的语言中实现闭包(C++中闭包的签名是不同的)。
有两种 * 实际 * 的方法可以实现这一点:
1.使用某种类型的单例变量/全局变量来存储闭包,然后传递一个helper函数,该函数使用闭包调用所需的函数。

#include <stdio.h>

template<class C, typename ReturnType, typename... Args>
class ClosureSingleton {
    typedef ReturnType (C::*FuncType)(Args...);

   public:
    static ClosureSingleton& getInstance() {
        static ClosureSingleton instance;
        return instance;
    }

    void setClosure(C* obj, FuncType f) {
        this->obj      = obj;
        this->function = f;
    }
    static ReturnType funcPtr(Args... args) {
        C* obj    = getInstance().obj;
        auto func = getInstance().function;

        return (obj->*func)(args...);
    }

   private:
    ClosureSingleton() {}

    C* obj;
    FuncType function;

   public:
    ClosureSingleton(ClosureSingleton const&) = delete;
    void operator=(ClosureSingleton const&)   = delete;
};

class aClass {
   public:
    void aTest1(int a, int b) { printf("%d + %d = %d\n", a, b, a + b); }
    int aTest2(int a, int b) { return a + b; }
};

void function1(void (*function)(int, int)) {
    function(1, 1);
}
int function2(int (*function)(int, int)) {
    return function(1, 1);
}

int main() {
    aClass tmp;
    ClosureSingleton<aClass, void, int, int>::getInstance().setClosure(
        &tmp, &aClass::aTest1);
    function1(&ClosureSingleton<aClass, void, int, int>::funcPtr);

    ClosureSingleton<aClass, int, int, int>::getInstance().setClosure(
        &tmp, &aClass::aTest2);
    printf(
        "function2: %d\n",
        function2(&ClosureSingleton<aClass, int, int, int>::funcPtr));

    return 0;
}

当然,这有一个明显的缺点,即闭包需要在每次调用之前设置,还有一些线程安全问题。虽然不理想,但在特定环境下可能是可行的
1.使用asmjit或动态编译来动态编译并将函数传递给C代码。这只适用于允许堆部分标记为可执行的机器。它也是非常不可移植的,因为你将编写汇编代码来完成这一点。然而,如果你让它工作,你将确实有一个真正的闭包。尽管与大多数编程语言实现闭包的方式相比,创建闭包的成本要高得多(它们不复制函数汇编,而是使用上下文对象)
1.修补包含函数处理程序的lib/dll,改变它的签名以允许上下文对象。同样,这是一个非常脆弱和非最佳的解决方案。
我最初的回答,并没有真正回答这个问题,但人们发现它很有用:
不知道为什么这个难以置信的简单解决方案已经被放弃了:

#include <stdio.h>

class aClass
{
public:
    void aTest(int a, int b)
    {
        printf("%d + %d = %d\n", a, b, a + b);
    }
};

template<class C>
void function1(void (C::*function)(int, int), C& c)
{
    (c.*function)(1, 1);
}
void function1(void (*function)(int, int)) {
  function(1, 1);
}

void test(int a,int b)
{
    printf("%d - %d = %d\n", a , b , a - b);
}

int main (int argc, const char* argv[])
{
    aClass a;

    function1(&test);
    function1<aClass>(&aClass::aTest, a);
    return 0;
}

输出:

wkyowqbh

wkyowqbh7#

我把成员函数设为静态的,所有的都可以:

#include <iostream>

class aClass
{
public:
    static void aTest(int a, int b)
    {
        printf("%d + %d = %d\n", a, b, a + b);
    }
};

void function1(int a,int b,void function(int, int))
{
    function(a, b);
}

void test(int a,int b)
{
    printf("%d - %d = %d\n", a , b , a - b);
}

int main (int argc, const char* argv[])
{
    aClass a;

    function1(10,12,test);
    function1(10,12,a.aTest); // <-- How should I point to a's aClass::test function?

    getchar();return 0;
}
polkgigr

polkgigr8#

如果你实际上不需要使用示例a(也就是说,你可以像@mathengineer的answer一样使它成为静态的),你可以简单地传入一个非捕获的lambda。(它衰减为函数指针)

#include <iostream>

class aClass
{
public:
   void aTest(int a, int b)
   {
      printf("%d + %d = %d", a, b, a + b);
   }
};

void function1(void (*function)(int, int))
{
    function(1, 1);
}

int main()
{
   //note: you don't need the `+`
   function1(+[](int a,int b){return aClass{}.aTest(a,b);}); 
}

Wandbox
注意:如果aClass的构建成本很高或者有副作用,这可能不是一个好方法。

gajydyqb

gajydyqb9#

现在你可以不用再敲你的头了。下面是成员函数的 Package 器,它支持existing函数将普通的C函数作为参数。thread_local指令是这里的关键。
http://cpp.sh/9jhk3

// Example program
#include <iostream>
#include <string>

using namespace std;

typedef int FooCooker_ (int);

// Existing function
extern "C" void cook_10_foo (FooCooker_ FooCooker) {
    cout << "Cooking 10 Foo ..." << endl;
    cout << "FooCooker:" << endl;
    FooCooker (10);
}

struct Bar_ {
    Bar_ (int Foo = 0) : Foo (Foo) {};
    int cook (int Foo) {
        cout << "This Bar got " << this->Foo << endl;
        if (this->Foo >= Foo) {
            this->Foo -= Foo;
            cout << Foo << " cooked" << endl;
            return Foo;
        } else {
            cout << "Can't cook " <<  Foo << endl;
            return 0;
        }
    }
    int Foo = 0;
};

// Each Bar_ object and a member function need to define
// their own wrapper with a global thread_local object ptr
// to be called as a plain C function.
thread_local static Bar_* Bar1Ptr = NULL;
static int cook_in_Bar1 (int Foo) {
    return Bar1Ptr->cook (Foo);
}

thread_local static Bar_* Bar2Ptr = NULL;
static int cook_in_Bar2 (int Foo) {
    return Bar2Ptr->cook (Foo);
}

int main () {
  Bar1Ptr = new Bar_ (20);
  cook_10_foo (cook_in_Bar1);
  
  Bar2Ptr = new Bar_ (40);
  cook_10_foo (cook_in_Bar2);
  
  delete Bar1Ptr;
  delete Bar2Ptr;
  return 0;
}

请就这一办法的任何问题发表评论。
其他应答无法调用现有plain C函数:http://cpp.sh/8exun

相关问题