На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
  
> получить указатель на функцию внутри класса , нужна помощь чайнику
    Прувет, народ! Вопрос больше для ардуинщиков, но и относится к Си.
    Всю голову сломал, помогите, в поиске ответы уж очень заумные.
    Требуется вызвать функцию внутри класса по указателю. при попытке получить указатель компилятор ругается.

    ExpandedWrap disabled
      uint8_t ADDR;
      void (*ptrFnc)();
       
      class OWSlvDevice {
        public:
          OWSlvDevice(byte Interrupt_pin){// конструктор
            Itrrpt.Pin(Interrupt_pin);
          }
          
          void begin(uint8_addr) {
            ADDR = addr;
            ptrFnc = CreateObject;
            
          }
          
        private:
          void CreateObject(){
            //код
          }
      };


    компилятор выдает ошибку:

    In file included from C:\...\...\T...\TX.ino:1:0:
    sketch\OWSlvDevice.h: In member function 'void OWSlvDevice::begin(uint8_t)':
    OWSlvDevice.h:63:14: error: cannot convert 'OWSlvDevice::CreateObject' from type 'void (OWSlvDevice::)()' to type 'void (*)()'
    ptrFnc=CreateObject;
    ^~~~~~~~~~~~
    exit status 1
    cannot convert 'OWSlvDevice::CreateObject' from type 'void (OWSlvDevice::)()' to type 'void (*)()'
    Что не так, подскажите!
      Типы не совпадают. И не могут совпадать. И не смогут никогда. :) Тип указателя на метод кардинально отличается от типа указателя на функцию.
      ExpandedWrap disabled
        void (OWSlvDevice::*ptrFnc)();

      Вот так работать будет.

      Добавлено
      Но и вызывать придётся иначе. Нужно указывать экземпляр класса, чей метод дёргаешь. Логично ж?
      Что-то типа:
      ExpandedWrap disabled
        OWSlvDevice shelezjaka(6);
         
        /* ... */
        shelezjaka.*ptrFnc();

      Возможно скобки забыл.

      Добавлено
      P.S. Ну да, забыл. Вот так надо:
      ExpandedWrap disabled
        (shelezjaka.*ptrFnc)();
      Сообщение отредактировано: Qraizer -
        Цитата Qraizer @
        Вот так работать будет.
        ExpandedWrap disabled
          void (OWSlvDevice::*ptrFnc)();


        Блин, как я об этом не подумал...

        Цитата Qraizer @
        Но и вызывать придётся иначе. Нужно указывать экземпляр класса, чей метод дёргаешь. Логично ж?
        Что-то типа:
        ExpandedWrap disabled
          OWSlvDevice shelezjaka(6);
           
          /* ... */
          shelezjaka.*ptrFnc();
          OWSlvDevice::ptrFnc = OWSlvDevice::CreateObject;


        Да, вот только вызов должен быть внутри класса, и к тому же при присвоении значения этой переменной
        ExpandedWrap disabled
          ptrFnc = &CreateObject;OWSlvDevice::ptrFnc = OWSlvDevice::CreateObject;
        компилятор опять злится: n file included from C:\...\...\TX\TX.ino:1:0:
        sketch\OWSlvDevice.h: In member function 'void OWSlvDevice::begin(uint8_t)':
        OWSlvDevice.h:64:41: error: cannot convert 'OWSlvDevice::CreateObject' from type 'void (OWSlvDevice::)()' to type 'void (OWSlvDevice::*)()'
        OWSlvDevice::ptrFnc = OWSlvDevice::CreateObject;
        ^~~~~~~~~~~~
        exit status 1
        cannot convert 'OWSlvDevice::CreateObject' from type 'void (OWSlvDevice::)()' to type 'void (OWSlvDevice::*)()'
        за то вот так:
        ExpandedWrap disabled
           ptrFnc = &CreateObject;
        работает,
        но, как теперь вызвать ее внутри модуля, если метод вызывающий ее находится вне класса?
        ExpandedWrap disabled
          PIN_INT{
           (OWSlvDevice.*ptrFnc)();
          }
        Сообщение отредактировано: SOY -
          Цитата SOY @
          но, как теперь вызвать ее внутри модуля, если метод вызывающий ее находится вне класса?

          Цитата Qraizer @
          (shelezjaka.*ptrFnc)();
            Цитата SOY @
            Да, вот только вызов должен быть внутри класса
            Сдается, вы пытаетесь в плюсах применить сишные навыки и вручную организовать виртуальные функции. Может вы опишете задачу более подробно и мы посоветуем, как обойтись вообще без укателя на функцию-член?
            Сообщение отредактировано: Dushevny -
              Цитата Dushevny
              Может вы опишете задачу более подробно и мы посоветуем, как обойтись вообще без указателя на функцию-член?

              задача организовать аппаратное прерывание внутри класса.
              макросы:
              ExpandedWrap disabled
                #define EN_INT0 {EICRA &= ~(1 << ISC00);EICRA|= (1 << ISC01);EIMSK|=(1 << INT0);}
                #define EN_INT1 {EICRA &= ~(1 << ISC10);EICRA|= (1 << ISC11);EIMSK|=(1<<INT1);}

              В зависимости от используемого пина выбираем нужную установку регистров и вектор прерывания:
              ExpandedWrap disabled
                byte Pin(const byte  Interrupt_pin) {
                    #if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
                      if(Interrupt_pin==2){
                         EN_INT0
                         #define PIN_INT ISR(INT0_vect)  // the interrupt service routine
                         return;
                      }
                    #elif defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
                      if(Interrupt_pin==2){
                         EN_INT0
                         #define PIN_INT ISR(INT0_vect)  // the interrupt service routine
                         return;
                      }
                    #elif defined (__AVR_ATmega328P__)
                      switch (Interrupt_pin) {
                        case 2:
                         EN_INT0
                         #define PIN_INT ISR(INT0_vect)  // the interrupt service routine
                         return;
                        case 3:
                         EN_INT1
                         #define PIN_INT ISR(INT1_vect)  // the interrupt service routine
                         return;
                      }
                    }
                }

              далее обработчик прерывания
              ExpandedWrap disabled
                PIN_INT{
                 
                }

              надо использовать внутри класса. Однако это не удается без танцев с бубном. Либо объявлять все методы static в области public, либо вызывать методы по адресу функции, что я пытаюсь осуществить.
              Как вариант конечно можно использовать конструкцию attachInterrupt(pin, handler, mode);, но в моем случае это не айс, хотя работает,но, опять таки с танцем с бубном :wall: .
              Сообщение отредактировано: SOY -
                SOY, для обработки прерывания могу предложить только синглтон, причем создание инстанса должно быть до первого срабатывания.
                  Цитата shm @
                  SOY, для обработки прерывания могу предложить только синглтон, причем создание инстанса должно быть до первого срабатывания.

                  ExpandedWrap disabled
                    Class OWSlvDevice {
                        static OWSlvDevice * instances;
                        static void ShllFnc() {
                          if (OWSlvDevice::instances != NULL)
                            OWSlvDevice::instances ->INT ();
                        }
                      public:
                        OWSlvDevice(byte Interrupt_pin) { // конструктор
                          byte Vctr = Itnrrpt.Pin(Interrupt_pin);
                          if (Vctr != 0xFF) {
                            pinMode (Interrupt_pin, INPUT_PULLUP);
                            attachInterrupt (Vctr, ShllFnc, FALLING);
                            instances  = this;
                          }
                         }
                      private:
                     
                        void INT() {
                          if (comm == SEARCH_DEVICE)CreateObject();
                        }
                        void CreateObject() {
                          .
                          .
                          .
                        }
                      };


                  Цитата
                  Как вариант конечно можно использовать конструкцию attachInterrupt(pin, handler, mode);, но в моем случае это не айс, хотя работает


                  shm,если есть другие варианты, то буду при много благодарен.
                    SOY, я не понимаю проблему, которую ты пытаешься решить. У тебя один источник прерывания и один класс, который его обрабатывает. Что тут еще изобретать? Если у тебя несколько источников прерываний и ты каждый из них хочешь обрабатывать в своем объекте, то создай глобальный контейнер указателей на объекты и по какому-нибудь критерию (номер прерывания, id устройства, и т.п.) доставай указатель и дергай через него метод. Только не забывай корректно синхронизировать доступ к контейнеру, иначе будут проблемы. Вангую, что ты хочешь аналог std::bind для прерываний, так его нет по причине слишком низкого уровня обработчика, но ты можешь сделать что-то подобное самостоятельно.

                    Добавлено
                    И приведенный тобою фрагмент кода не является синглтоном. Более того, он потенциально опасен, а в обработчике прерывания опасен^2.
                    Сообщение отредактировано: shm -
                      Я конечно соррь в ваших терках ... но, ЕМНИП, связывание идет на уровне линковки - а линкеру пофик ваши классы!
                      И адреса метода в классе быть попросту не может. Есть адрес метода объявленного объекта.
                      Но есть одно "но", на этапе компиляции - можно узнать позицию(индекс) виртуального метода в VMT.
                      На счет невиртуального метода - я ХЗ. Может кто-то разъяснит процедуру компиляции и линковки классов - я увы, пас.
                        Цитата
                        ExpandedWrap disabled
                                switch (Interrupt_pin) {
                                  case 2:
                                   EN_INT0
                                  #define PIN_INT ISR(INT0_vect)  // the interrupt service routine
                                   return;
                                  case 3:
                                   EN_INT1
                                  #define PIN_INT ISR(INT1_vect)  // the interrupt service routine
                                   return;
                        Это работать не будет. Препроцессор работает до компилятора, он не видит switch и выдаст здесь что-то вроде "macro PIN_INT redefined"

                        "Подвесить" функцию-член прямо на вектор прерывания невозможно. Просто потому что семантика вызова функции-члена другая - в момент вызова обработчика у ядра нет this. Для всяких Cortex-Mx можно "подвесить" на вектор статическую функцию - член, но ей все равно нужно откуда-то брать this для вызова уже собственно функции-члена для конкретного экземпляра класса. Для AVR даже это не работает из-за особого подхода к привязыванию функций-обработчиков к векторам через магические имена функций - все то, что скрыто внутри ISR(INT0_vect). На электрониксе обсуждали не так давно, в общем - не решается эта задача так, как вам хочется. Я бы использовал шаблон с номером вывода в качестве параметра. Чтобы не плодить кучу одинакового исполняемого кода - можно общую функциональность вынести в базовый класс, а в шаблоне реализовать только то, что зависит он номера вывода. Примерно так:
                        ExpandedWrap disabled
                          class int_pin_base
                          {
                          public:
                             inline void hander();
                          };
                           
                           
                          tempalte<uint8_t int_num>
                          class int_pin : public int_pin_base
                          {
                          public:
                              enum class trigger
                              {
                                  LOW_LEVEL,
                                  ANY_EDGE,
                                  FALLING_EDGE
                                  RISING_EDGE
                              }
                              int_pin(trigger trigger)        { EICRA = (EICRA & ~(0x03 << 2 * int_num)) | (uint8_t(trigger) << 2 * int_num);
                              
                              void enable()                   { EIMSK |=   1 << int_num;  }
                              void disable()                  { EIMSK &= ~(1 << int_num); }
                          };
                           
                          /*   или так, если реализации сильно отличаются в зависимости от параметра шаблона
                          template<>
                          int_pin<0>::int_pin(trigger trigger)
                          {
                              EICRA = (EICRA & ~(1 << (ISC01 | ISC00))) | (uint8_t(trigger) << ISC00);
                          }
                           
                          template<>
                          int_pin<1>::int_pin(trigger trigger)
                          {
                              EICRA = (EICRA & ~(1 << (ISC11 | ISC10))) | (uint8_t(trigger) << ISC10);
                          }
                          */
                           
                          int_pin<0> Int0(ANY_EDGE);
                          int_pin<1> Int1(RISING_EDGE);
                           
                           
                          ISR(INT0_vect)
                          {
                              Int0.handler();
                          }
                           
                          ISR(INT1_vect)
                          {
                              Int1.handler();
                          }
                          AVR-ка да, недавно с нею разбирался. 2048 прерываний – настоящий монстр. Я-то думал, ARM жестокий, PPC по сравнению с ним просто чуточку неудобный.
                          SOY, простое решение вряд ли возможно будет найти. Насколько я понимаю, у тебя архитектура предусматривает по экземпляру класса на каждый обработчик. Тут надо как-то связать pin с этим экземпляром, + обеспечить корректную передачу this. Вариантов масса, включая бинд этих this в лямбды, которые уже несложно вызывать без параметров. Но я очень сомневаюсь, что компилятор сможет в исполнительном окружении обеспечить корректный вызов лямбд, учитывая, что это происходит асинхронно.
                          Я бы всё-таки в подобных условиях сделал собственный диспетчер, который определял нужный экземпляр this по тому pin, который сработал. В той же AVR-ки иначе и никак не сделать-то. Та и в PPC архитектура внешних прерываний способствует этому же. Диспетчер же вполне можно сделать static, и пусть себе читает регистры и определяет pin.
                            кому интересно сделал так:

                            файл _ISR.h

                            ExpandedWrap disabled
                              #ifndef _ISR_H
                              #define _ISR_H
                              #if ARDUINO >= 100
                               #include <Arduino.h>
                              #else
                              #include <WProgram.h>
                              #endif
                              // ============================ КОНСТАНТЫ ==============================
                              #define ISR_PIN          2 //пин(менять перед объявлением библиотеки)
                               
                              #if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
                                  #if (ISR_PIN == PB2) ||  (ISR_PIN == B2) ||  (ISR_PIN == 2) || defined (ISR_PIN == 7)
                                      #define EICRA   _SFR_IO8(0x35)
                                      #define ISC00 0
                                      #define ISC01 1
                                      #define EIMSK   _SFR_IO8(0x3B)
                                      #define INT0    6
                                      #define EIFR _SFR_IO8(0x3A)
                                      #define INTF0   6
                                      //OW Pin
                                      #define PINS    PINB
                                      #define PIN     PINB2
                                      #define PORT    PORTB//_SFR_IO8(0x18)
                                      #define DDR     DDRB
                                      
                                      #define PIN_INT ISR(INT0_vect)  // the interrupt service routine
                                      
                                      #define ISR_INIT 1
                                  #endif
                              #elif defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
                                  #if (ISR_PIN == PB2) ||  (ISR_PIN == B2) ||  (ISR_PIN == 2) || defined (ISR_PIN == 5)
                                      #define EICRA   _SFR_IO8(0x35)
                                      #define ISC00 0
                                      #define ISC01 1
                                      #define EIMSK   _SFR_IO8(0x3B)
                                      #define INT0    6
                                      #define EIFR _SFR_IO8(0x3A)
                                      #define INTF0   6
                                      //OW Pin
                                      #define PINS    PINB
                                      #define PIN     PINB2
                                      #define PORT    PORTB//_SFR_IO8(0x18)
                                      #define DDR     DDRB
                                      
                                      #define PIN_INT ISR(INT0_vect)  // the interrupt service routine
                                      
                                      #define ISR_INIT 1
                                  #endif
                              #elif defined (__AVR_ATmega328P__)
                                  #if (ISR_PIN == 2) || (ISR_PIN == D2)
                                      //OW Pin
                                      #define PINS _SFR_IO8(0x09)
                                      #define PIN   PIND2
                                      #define PORT _SFR_IO8(0x0B)
                                      #define DDR  _SFR_IO8(0x0A)
                                      
                                      #define PIN_INT ISR(INT0_vect)  // the interrupt service routine
                                      
                                      #define ISR_INIT 1
                                  #endif
                               
                                  #if  (ISR_PIN == 3)  || (ISR_PIN == D3)
                                      //OW Pin
                                      #define PINS _SFR_IO8(0x09)
                                      #define PIN   PIND3
                                      #define PORT _SFR_IO8(0x0B)
                                      #define DDR  _SFR_IO8(0x0A)
                                      
                                      #define PIN_INT ISR(INT1_vect)  // the interrupt service routine
                                      
                                      #define ISR_INIT 1
                                  #endif
                               
                              #endif
                               
                              #if defined ISR_INIT
                               
                              //OW Pin
                              #define OW_PORT PORT //1 Wire Port
                              #define OW_PIN PINS //1 Wire Pin as number
                              #define OW_PORTN (1<<PIN)  //Pin as bit in registers
                              #define OW_PINN (1<<PIN)
                              #define OW_DDR DDR  //регистр направления
                              #define SET_LOW {OW_DDR|=OW_PINN;OW_PORT&=~OW_PORTN;}  //установить линию 1-Wire на низкий уровень
                              #define RESET_LOW {OW_DDR&=~OW_PINN;}  //установить вывод 1-Wire в качестве входа
                               
                              //Pin interrupt
                              #define EN_INT {EIMSK|=(1<<INT0);EIFR|=(1<<INTF0);}  //enable interrupt
                              #define DIS_INT  EIMSK&=~(1<<INT0);  //disable interrupt
                              #define SET_RISING EICRA=(1<<ISC01)|(1<<ISC00);  //set interrupt at rising edge
                              #define SET_FALLING {EICRA &= ~(1 << ISC00);EICRA|=(1<<ISC01);} //set interrupt at falling edge
                              #define SET_CHANGE {EICRA &= ~(1 << ISC01) ;EICRA|=(1<<ISC00);}//set interrupt at change edge
                              #define CHK_INT_EN (EIMSK&(1<<INT0))==(1<<INT0) //test if interrupt enabled
                               
                              class _ISR {
                                  
                                  private:
                                     void proc();
                                     void(*CallbackCommand_)(uint8_t retv);
                                     friend void INTP();
                                      ;
                                  
                                  public:
                                      _ISR();
                                      void begin(void (*callback)(uint8_t retv));
                                      
                              };
                              #endif
                              #endif


                            файл _ISR.cpp

                            ExpandedWrap disabled
                              #include "Arduino.h"
                               
                              #include "_ISR.h"
                               
                              /* Access variable for ISRs */
                              static _ISR *instance = NULL;
                              void (*p_function)();   // указатель на p_function
                              _ISR::_ISR(){}// конструктор
                               
                              PIN_INT {//прерывание
                                (*p_function)();
                              }
                              // спасибо Гайверу за универсальную функцию
                              void attachFunction(void (*function)()) { // передача указателя на функцию
                                p_function = *function;
                              }
                               
                              void INTP(){
                                  instance->proc();
                              }
                               
                              void _ISR::proc(){
                                  CallbackCommand_(0x01);
                              }
                               
                              void _ISR::begin(void (*callback)(uint8_t retv)) {
                                  cli();//откл. глоб.прер.
                                  RESET_LOW;//пин на вход
                                  SET_FALLING;
                                  EN_INT;
                                  sei(); // enable interrupts
                                  attachFunction(INTP);
                                  instance = this;
                                  CallbackCommand_ = callback;
                              }


                            файл tst_ISR_on_CLASS.ino

                            ExpandedWrap disabled
                              #include <_ISR.h>
                               
                              _ISR tst;
                              void INT(uint8_t retv);
                              void setup() {
                                Serial.begin(9600);
                                tst.begin(&INT);
                               
                              }
                               
                              void loop() {
                                // put your main code here, to run repeatedly:
                               
                              }
                               
                              void INT(uint8_t retv){
                                Serial.println("OK");
                              }


                            код для второй ардуины для теста (просто шлем импульсы на вход прерывания второй ардуины

                            ExpandedWrap disabled
                              #define INT 4
                              void setup() {
                               pinMode(INT, OUTPUT);
                               pinMode(LED_BUILTIN, OUTPUT);
                               
                              }
                               
                              void loop() {
                                delay(1000);
                                digitalWrite(INT,1);
                                digitalWrite(LED_BUILTIN,1);
                                delay(200);
                                digitalWrite(INT,0);
                                digitalWrite(LED_BUILTIN,0);
                              }


                            тема закрыта, всем спасибо! :)
                              Цитата SOY @
                              кому интересно сделал так:

                              Можно вот так попробовать. Но я так не делал:
                              Скрытый текст

                              ExpandedWrap disabled
                                // --------------------------------------------------------------------------
                                //Определим интерфейс:
                                class I_interrupt
                                {
                                 public:
                                  virtual void work (int intNum)=0;
                                };
                                // --------------------------------------------------------------------------
                                // определим указатель на интерфейс
                                I_interrupt* pInt=NULL;
                                // --------------------------------------------------------------------------
                                // установим адрес интерфейса:
                                void Set_I_interrupt(I_interrupt* p)
                                {
                                 pInt = p;
                                }
                                // --------------------------------------------------------------------------
                                //Далее короткие функции прерываний в стиле С:
                                // не знаю, как для ARDUINO. Пусть для определённости будет так:
                                interrupt [TIMER1_COMPB_vect] void t1_compb (void)
                                {
                                 if(pInt) pInt->work(TIMER1_COMPB_vect);
                                }
                                // --------------------------------------------------------------------------
                                interrupt [TIMER1_COMPA_vect] void t1_compa (void)
                                {
                                 if(pInt) pInt->work(TIMER1_COMPA_vect);
                                }
                                // --------------------------------------------------------------------------
                                //Пишем реализацию интерфейса для конкретного проекта:
                                class IntProject : virtual public I_interrupt
                                {
                                 public:
                                 
                                  virtual void work (int intNum);
                                 
                                  virtual void routine_TIMER1_COMPB_vect();
                                  virtual void routine_TIMER1_COMPA_vect();
                                };
                                // --------------------------------------------------------------------------
                                void IntProject::work (int intNum)
                                {
                                // тут возможно что-то общее для всех, если нужно
                                 switch(intNum)
                                 {
                                  case TIMER1_COMPB_vect: routine_TIMER1_COMPB_vect(); break;
                                  case TIMER1_COMPA_vect: routine_TIMER1_COMPA_vect(); break;
                                 }
                                // тут возможно что-то общее для всех, если нужно
                                }
                                // --------------------------------------------------------------------------
                                void IntProject::routine_TIMER1_COMPB_vect()
                                {
                                // ...
                                }
                                // --------------------------------------------------------------------------
                                void IntProject::routine_TIMER1_COMPA_vect()
                                {
                                // ...
                                }
                                // --------------------------------------------------------------------------
                                // В результате работаем с прерываниями используя классы.
                                // Возможны варианты.
                                 
                                // используем:
                                void main (void)
                                {
                                 // ...
                                 
                                 IntProject intObject; Set_I_interrupt(&intObject);
                                 
                                 // ...
                                }


                              После таких мероприятий реализации можно менять "как перчатки".
                              Сообщение отредактировано: ЫукпШ -
                              0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                              0 пользователей:


                              Рейтинг@Mail.ru
                              [ Script execution time: 0,0516 ]   [ 16 queries used ]   [ Generated: 29.03.24, 08:13 GMT ]