Jestem nowy w C ++ i staram się wykonać ogólny przełącznik (tj. Urządzenie, a nie instrukcję C ++), która może być używana do migania świateł, włączanie i wyłączanie sygnału dźwiękowego itp., W moim projekcie Arduino.

Mógłbym utworzyć interfejs przełączalny i wdrożyć, że w klasach, które chcę "przełączać". Ale ponieważ robię to jako celów studiów i widziałem zdolność wskaźnika do funkcji w C ++ (to jest dla mnie nowa, ponieważ pochodzę z C # i Java), trudno byłoby to dobra okazja, aby spróbować ...

Problem polega na tym, że mogę przekazać funkcję w moim kodzie tylko wtedy, gdy jest to funkcja lokalna, ale nie zadziała, jeśli spróbuję przekazać funkcję innego obiektu jak na przykład dioda LED.

Jakiś kod ilustrujący problem. Jest to switch.cpp, otrzymuje funkcje On i Off w swoim konstruktorze i ma metodę aktualizacji, która nazywa się wewnątrz metody pętli w klasie głównej Arduino Ino:

auto_switch.cpp

using switch_function = void(*)();
auto_switch::auto_switch(const switch_function on_function, const switch_function off_function, const int max_speed_count)
{
    //sets all variables...
}

void auto_switch::update(const unsigned long millis)
{
    //turn switch on and off...
}

I to jest mój plik ino

ino file

#include <Arduino.h>
#include "led.h"
#include "auto_switch.h"

led* main_led;
auto_switch* led_switch;
int slow_speed;

//ugly code
void turn_led_on()
{
    main_led->turn_on();
}

//ugly code
void turn_led_off()
{
    main_led->turn_off();
}

void setup() {
    main_led = new led(2, 3, 4, true, color::white);

    //ugly code
    led_switch = new auto_switch(turn_led_on, turn_led_off, 3);

    slow_speed = led_switch->add_speed(100, 100, 3, 1000);
    led_switch->set_active_speed(slow_speed);
    led_switch->turn_on();
}

void loop() {
    led_switch->update(millis());
}

Działa, ale musiałem zrobić funkcję lokalną (turn_led_on i turn_led_off), aby móc przypisać wewnętrzne funkcje jako parametr do konstruktora {x2}}, części, które napisałem //ugly code

Chciałem zrobić coś takiego, bez kodu kleju pomiędzy:

//doesn't work
led_switch = new auto_switch(main_led->turn_on, main_led->turn_off, 3);

Czy to możliwe? Przeczytałem coś o statycznym wskaźniku, aby funkcjonować, a niektóre funkcje STD, które pomagają w tym, jeśli dostanę go dobrze Kodeks klejowy jest konieczny w tym przypadku, aby kompilator mógłby wiedzieć, w których pochodzą funkcje pochodzą z chyba (z jakiego obiektu .

W każdym razie ma sens, czy można to zrobić za pomocą wskaźnika do funkcji lub powinienem utworzyć interfejs lub coś innego?

0
Jheyson Bicudo de Lima 12 styczeń 2020, 20:36

1 odpowiedź

Najlepsza odpowiedź

Przed podjęciem decyzji, jak to zrobić, pytanie jest tym, co chcesz robić i dlaczego. Ponieważ może istnieją lepsze alternatywy przy użyciu prostych idiomów C ++.

Opcja 1: Specjalizacja z polimorfizmem

Czy chcesz wyspecjalizować kilka funkcji przełącznika, więc zamiast dzwonić do funkcji auto_switch Nazywasz kopułą bardziej wyspecjalizowaną?

W tym przypadku nie zrobiłbyś:

//doesn't work
led_switch = new auto_switch(main_led->turn_on, main_led->turn_off, 3);

Ale zamiast tego polegałbyś na polimorfizmie z wirtualnymi funkcjami w klasie bazowej:

class auto_switch {
...
    virtual void turn_on();  
    virtual void turn_off(); 
...
}; 

I napisz wyspecjalizowaną klasę dla diod LED:

class led_witch : public auto_switch {
...
    void turn_on() override;  
    void turn_off() override; 
...
}; 

W rzeczywistości kompilator generuje wskaźniki funkcji za sceną, ale nie musisz się troszczyć:

auto_switch s1=new auto_switch(...); 
auto_switch s2=new led_switch(...);   // no problem !! 

s1->turn_on();     // calls auto_switch::turn_on() 
s2->turn_on();     // calls led_switch::turn_on() since the real type of s2 is led_switch 

Ale wydarzenie, jeśli zachowanie każdego obiektu jest dynamiczne na podstawie rzeczywistej klasy obiektu, obiekty tej samej klasy dzielą zachowanie, które było predefiniowane w czasie kompilacji. Jeśli nie jest w porządku, przejdź do następnej opcji.

Opcja 2: Wskaźnik funkcji członka

Funkcje innych obiektów można wywołać tylko z tym obiektem pod ręką. Więc posiadanie wskaźnika funkcji do funkcji LED nie jest wystarczające: musisz również wiedzieć, na którym dioda została zastosowana.

Dlatego wskaźniki funkcji członków są różne i nieco ograniczenie: można wywołać tylko funkcje klasy wskaźnika funkcji członka. Jeśli polimorfizm jest wystarczający (tj. Jeśli klasa pochodna ma inną implementację funkcji już przewidzianej w klasie bazowej), masz szczęście. Jeśli chcesz użyć funkcji, która istnieje tylko w klasie pochodnej, a nie w klasie bazowej, nie będzie się sporządzić.

Tutaj uproszczona wersja auto_swith: Zapewniam funkcję, ale także wskaźnik do obiektu, na którym należy wywołać funkcję:

class auto_switch{
    void (led::*action)(); 
    led *ld; 
public: 
    auto_switch(void(led::*a)(), led*l) : action(a), ld(l) {}
    void go () { (ld->*action)(); }
}; 
// usage:  
auto_switch s(&led::turn_off, &l1);
s.go(); 

Demo online

Opcja 3: Działanie funkcjonalne (na to, czego szukasz?)

Innym wariancie byłoby użycie standardowej biblioteki funkcjonalnej do wiązania funkcji członka i obiekt, na którym powinien być wykonany (jak również dowolne parametry potrzebne):

class auto_switch{
    std::function<void()> action; 
public: 
    auto_switch(function<void()>a) : action(a) {}
    void go () { action(); }
}; 

Tutaj możesz wiązać wszystko: dowolna funkcja dowolnej klasy:

auto_switch s(bind(&led::turn_off, l1));
s.go(); 
auto_switch s2(bind(&blinking_led::blink, l2));
s2.go(); 

Demo online

Opcja 4: Wzór polecenia

Teraz, jeśli chcesz wykonać coś na obiekcie po włączeniu i wyłączaniu przełącznika, ale potrzebujesz całkowitej elastyczności, możesz po prostu wdrożyć

Christophe 12 styczeń 2020, 19:11