带有/不带类的回调函数指针C ++

时间:2012-01-11 12:50:55

标签: c++ class pointers callback arduino

我卡住了。我试图形成一个函数,它将吃掉无类函数指针和来自对象的函数。这是我目前的代码,希望能够解释更多。

(它应该在Arduino上运行,所以我不能使用大型库。)

首先,我将这个库用于Arduino:

/* SimpleTimer - A timer library for Arduino.
 * Author: mromani@ottotecnica.com
 * Copyright (c) 2010 OTTOTECNICA Italy
 */

它采用它在此类型的设定计时器间隔上调用的函数:

typedef void (*timer_callback)(void);

据我所知,它是一个类别功能,网页 Pointers to member functions 让我真的很远,但还不够远。可能是我这方面的术语缺陷。

现在,我已经创建了自己的类,我想依次使用这个SimpleTimer库。但是,如果我为SimpleTimer提供我的类函数,它就不喜欢它们(我理解)。但是如何在不改变SimpleTimer库的情况下实现这一目标。

所以有类机器人,它有Robot::halt()。我希望机器人能够向前移动一段时间。像这样:

void Robot::forward(int speed, long time) {
    reset();
    timer.setTimer(time, c_func, 1);

    analogWrite(l_a, speed);
    analogWrite(r_a, speed);
    isMoving(true);
}

void Robot::halt() {
    __isMoving = false;
    digitalWrite(r_a, LOW);
    digitalWrite(r_b, LOW);
    digitalWrite(l_b, LOW);
    digitalWrite(l_a, LOW);
}

此时c_func变量是无类函数,但我想使用Robot::halt函数。我看了,读过,学过但还没有成功。我似乎无法绕过这个,因为我错过了一些角度。

我试过了:

timer.setTimer(time, (this->*halt), 1);
timer.setTimer(time, Robot::*halt, 1);
timer.setTimer(time, &Robot::halt), 1);

但这一切都等于同样的问题/我只是在黑暗中刺伤......

修改

之前,我说不想更改SimpleTimer库代码。我想回归这个,我想改变它会有更好的选择。

感谢所有目前的答案,我只被允许标记一个作为可行的答案,实际上我在这里读到的每一个都非常有帮助。

要继续此操作,请更改SimpleTimer代码。这个类需要引用一个持有“halt”函数的对象,对吧?所以,将settimer函数重载为将我的对象和我的函数作为两个单独的指针的东西可以工作......?我想我已经掌握了这一点,但是,我还没有我的头脑。

修改

我不知道是谁再次来到这个,但是,任何人都找到了这个帖子。如果找到 Member Function Pointers and the Fastest Possible C++ Delegates ,可以在函数指针和成员函数指针中进行非常好的介绍。

修改

搞定了,更改了SimpleTimer库以使用此Delegate系统: http://www.codeproject.com/KB/cpp/FastDelegate.aspx

它集成得非常好,在Arduino库中有一个像这样的标准Delegate系统可能会很好。

代码与测试(工作)

的typedef

typedef FastDelegate0<> FuncDelegate;

机器人类中的代码:

void Robot::test(){
    FuncDelegate f_delegate;
    f_delegate = MakeDelegate(this, &Robot::halt);

    timer.setTimerDelg(1, f_delegate, 1);
}

void Robot::halt() {
    Serial.println("TEST");
}

SimpleTimer类中的代码:

int SimpleTimer::setTimerDelg(long d, FuncDelegate f, int n){
    f();
}

Arduino在控制台中打印TEST。

下一步将它放入数组中,看不到很多问题。谢谢大家,我无法相信我在两天内学到的东西。

那味道是什么?这是......的味道吗?成功了!

对于感兴趣的人,使用的Delegate系统不等于内存容量问题: 使用FastDelegate

AVR Memory Usage
----------------
Device: atmega2560

Program:   17178 bytes (6.6% Full)
(.text + .data + .bootloader)

Data:       1292 bytes (15.8% Full)
(.data + .bss + .noinit)


Finished building: sizedummy

没有FastDelegate:

AVR Memory Usage
----------------
Device: atmega2560

Program:   17030 bytes (6.5% Full)
(.text + .data + .bootloader)

Data:       1292 bytes (15.8% Full)
(.data + .bss + .noinit)


Finished building: sizedummy

4 个答案:

答案 0 :(得分:8)

您可以通过创建一个functor对象来完成此操作,该对象充当计时器代码和代码之间的代理。

class MyHaltStruct
{
public:
    MyHaltStruct(Robot &robot)
        : m_robot(robot)
        { }

    void operator()()
        { robot.halt(); }

private:
    Robot &m_robot;
}

// ...

timer.setTimer(time, MyHaltStruct(*this), 1);

修改

如果无法通过仿函数对象完成,则可以使用全局变量和函数,可能在命名空间中:

namespace my_robot_halter
{
    Robot *robot = 0;

    void halt()
    {
        if (robot)
            robot->halt();
    }
}

// ...

my_robot_halter::robot = this;
timer.setTimer(time, my_robot_halter::halt, 1);

这只适用于你有一个机器人实例的情况。

答案 1 :(得分:3)

由于计时器回调签名不带任何参数,遗憾的是你需要使用一些全局(或静态)状态:

Robot *global_robot_for_timer;
void robot_halt_callback()
{
    global_robot_for_timer->halt();
}

你至少可以将这一批包装到它自己的文件中,但它并不漂亮。正如Matthew Murdoch所说,编辑SimpleTimer本身可能更好。一个更传统的界面是:

typedef void (*timer_callback)(void *);

SimpleTimer::setTimer(long time, timer_callback f, void *data);

void robot_halt_callback(void *data)
{
    Robot *r = (Robot *)data;
    r->halt();
}

即,当你调用setTimer时,你提供一个传递给回调的参数。

SimpleTimer的最小变化是:

SimpleTimer.h

typedef void (*timer_function)(void *);
struct timer_callback {
    timer_function func;
    void *arg;
};
// ... every method taking a callback should look like this:
int SimpleTimer::setTimeout(long, timer_function, void *);

SimpleTimer.cpp

// ... callbacks is now an array of structures
callbacks[i] = {0};

// ... findFirstFreeSlot
if (callbacks[i].func == 0) {

// ... SimpleTimer::setTimer can take the timer_callback structure, but
// that means it's callers have to construct it ...
int SimpleTimer::setTimeout(long d, timer_function func, void *arg) {
    timer_callback cb = {func, arg};
    return setTimer(d, cb, RUN_ONCE);
}

答案 2 :(得分:2)

您不能在那里传递非静态成员函数 - 只能是静态成员函数。签名应该是这样的:

static void halt()
{
    //implementation
}

原因是每个非静态成员函数都有一个称为Robot*指针的隐式this参数,它有助于访问当前对象。由于回调签名没有这样的Robot*参数,因此除非它是静态的,否则您不能传递class Robot的成员函数。

这样在你的实现中

void halt();

生效

static void halt( Robot* thisPointer );

当你做

void Robot::halt() {
    __isMoving = false;
}

你实际上有这个:

void Robot::halt( Robot* thisPointer ) {
    thisPointer->__isMoving = false;
}

当然不能传递halt( Robot*)函数指针来代替void (*)(void) C回调函数。

是的,如果你需要从回调内部访问class Robot的非静态成员变量,你必须以某种方式检索指向其他地方class Robot实例的指针 - 例如,将其存储为一个静态成员变量,以便您不依赖this指针。

答案 3 :(得分:1)

重要的是要理解函数指针和指向类成员的指针是不同的,不是出于任意原因,而是实例方法具有隐式this参数的事实(同样,它们必须使用继承和虚函数,这增加了更多的复杂性;因此它们的大小可以是16个或更多字节)。换句话说,指向类成员的函数指针只与类的实例一起有意义。

正如目前最热门的回答所说,最好的办法是选择仿函数。虽然setTimer函数可能只接受函数指针,但可以编写模板函数来包装调用并接受它们。对于更精细的处理,您可以编写模板元程序(Boost.TypeTraits具有is_pointeris_function甚至is_member_function_pointer)来处理不同的情况。

如何制作仿函数是另一回事。您可以选择手动编写它们(这意味着为每个类创建一个operator()的类),但这取决于您的需求可能是乏味的。有两种选择:

  • std::bind:您可以使用它来创建一个仿函数,其第一个参数将绑定到您指定的值 - 在成员函数的情况下,它将是实例。
  • 根据您的编译器,您可能无法访问std::bind - 在这种情况下,我建议boost::bind。它是一个仅限标头的库,并提供相同的功能。
  • 您可以使用another delegate implementation。我没有这方面的经验,但声称比其他实现(包括std :: function)更快。

上述库只是标题库,因此它们可能不算作“大型库”。