Как унаследовать класс, требующий &foo() в своем конструкторе?

Общее

У меня есть библиотека с конструктором класса class Math(&foo1,&foo2);, которую я хотел бы поместить в библиотеку для class Algebra(), где foo1 и foo2 определены в Algebra.cpp

Мой текущий конструктор Algebra::Algebra():Math(&Algebra::foo1,&Algebra::foo2) возвращает ошибку в Arduino IDE в результате:

ошибка: нет соответствующей функции для вызова 'Math::Math(void (Algebra::*)(long unsigned int, byte), byte (Algebra::*)(long unsigned int))':Math(&Algebra::foo1, &Algebra::foo2)

Мета-заметка: Я считаю, что это по теме, потому что я имею дело конкретно с разработкой кода для Arduino Mega с использованием библиотек и аппаратных компонентов, разработанных для Arduino. Если я ошибаюсь, и есть лучший обмен для этого вопроса, пожалуйста, дайте мне знать


В частности,

Я пишу библиотеку для объединения I2C FRAM от Adafruit с Расширенной библиотекой базы данных (я проверил, что библиотеки работают при независимом вызове в скетче, но я хотел бы объединить их, чтобы их было легче наследовать в [Big_Machine_api.h], который я сделаю позже) Я тестирую свою библиотеку с помощью этого скетча:

/*
EDB_AT24C1024.pde
Расширенная библиотека базы данных + тестовый эскиз Adafruit_FRAM_I2C EEPROM
 
Страница проекта библиотеки расширенной базы данных находится здесь:
http://www.arduino.cc/playground/Code/ExtendedDatabaseLibrary
 
Страница проекта библиотеки Adafruit_FRAM_I2C находится здесь:
https://github.com/adafruit/Adafruit_FRAM_I2C
 
*/
#include "Arduino.h"
#include <Wire.h>
#include <Adafruit_FRAM_I2C_EDB.h>
#define TABLE_SIZE 131072

// Произвольное определение записи для этой таблицы.
// Это следует изменить в соответствии с вашими потребностями в записи.
struct LogEvent {
  int id;
  unsigned long Rotations;
} 
logEvent;

// Создать объект EDB с соответствующими обработчиками записи и чтения
Adafruit_FRAM_I2C_EDB db;

// Запустить демо
void setup() { 
  Serial.begin(115200);
  Serial.println("DONE");
}

void loop() {
}

Мой файл ADAFRUIT_FRAM_I2C_EDB.h:

#ifndef ADAFRUIT_FRAM_I2C_EDB_h
#define ADAFRUIT_FRAM_I2C_EDB_h

#include <Arduino.h>
#include <Adafruit_FRAM_I2C.h>
#include <EDB.h>

#define MB85RC_DEFAULT_ADDRESS        (0x50) /* 1010 + A2 + A1 + A0 = 0x50 default */

class Adafruit_FRAM_I2C_EDB : public EDB {
 public:
  Adafruit_FRAM_I2C_EDB(uint8_t addr = MB85RC_DEFAULT_ADDRESS);
  void getDeviceID(uint16_t *manufacturerID, uint16_t *productID);
  
 private:
  Adafruit_FRAM_I2C _fram;
  uint8_t _FRAMaddr;
  void writer(unsigned long address, byte data);
  byte reader(unsigned long address);

};
#endif

Мой файл ADAFRUIT_FRAM_I2C_EDB.cpp:

#include "Arduino.h"
#include "Adafruit_FRAM_I2C.h"
#include "EDB.h"
#include "Adafruit_FRAM_I2C_EDB.h"
Adafruit_FRAM_I2C_EDB::Adafruit_FRAM_I2C_EDB(uint8_t addr)
:_FRAMaddr(addr),EDB(&Adafruit_FRAM_I2C_EDB::writer, &Adafruit_FRAM_I2C_EDB::reader) {
  _fram = Adafruit_FRAM_I2C();
  _fram.begin(_FRAMaddr);
}

void Adafruit_FRAM_I2C_EDB::getDeviceID(uint16_t *manufacturerID, uint16_t *productID) {
  _fram.getDeviceID(manufacturerID, productID);
}


void Adafruit_FRAM_I2C_EDB::writer(unsigned long address, byte data) {
  _fram.write8(address, data);
}

byte Adafruit_FRAM_I2C_EDB::reader(unsigned long address) {
  return _fram.read8(address);
}

Полный список ошибок:

C:[...]\Arduino\libraries\Adafruit_FRAM_I2C_EDB\Adafruit_FRAM_I2C_EDB.cpp:16:84: note: candidates are:

In file included from C:[...]\Arduino\libraries\Adafruit_FRAM_I2C_EDB\Adafruit_FRAM_I2C_EDB.cpp:3:0:

C:[...]\Arduino\libraries\EDB-master/EDB.h:54:5: note: EDB::EDB(void (*)(long unsigned int, uint8_t), uint8_t (*)(long unsigned int))

     EDB(EDB_Write_Handler *, EDB_Read_Handler *);

     ^

C:[...]\Arduino\libraries\EDB-master/EDB.h:54:5: note:   no known conversion for argument 1 from 'void (Adafruit_FRAM_I2C_EDB::*)(long unsigned int, byte) {aka void (Adafruit_FRAM_I2C_EDB::*)(long unsigned int, unsigned char)}' to 'void (*)(long unsigned int, uint8_t) {aka void (*)(long unsigned int, unsigned char)}'

C:[...]\Arduino\libraries\EDB-master/EDB.h:50:7: note: constexpr EDB::EDB(const EDB&)

 class EDB {

       ^

C:[...]Arduino\libraries\EDB-master/EDB.h:50:7: note:   candidate expects 1 argument, 2 provided

C:[...]Arduino\libraries\EDB-master/EDB.h:50:7: note: constexpr EDB::EDB(EDB&&)

C:[...]\Arduino\libraries\EDB-master/EDB.h:50:7: note:   candidate expects 1 argument, 2 provided

exit status 1
Error compiling for board Arduino/Genuino Mega or Mega 2560.

, 👍0


1 ответ


3

Вы просто передаете переменные родительскому конструктору в списке инициализации дочернего элемента. Не нужно никаких квалификаций или чего-то подобного.

Вот пример, который компилируется:

class Math {
    private:
        int _foo1;
        int _foo2;

    public:    
        Math(int &foo1, int &foo2) : _foo1(foo1), _foo2(foo2) {}
};

class Algebra : public Math {
    private:
        int myFoo1 = 12;
        int myFoo2 = 34;

    public:
        Algebra() : Math(myFoo1, myFoo2) {}
};

Конструктор Math принимает два целых числа, переданных по ссылке. Список инициализации конструктора Algebra вызывает родительский конструктор Math с двумя собственными целыми числами-членами, которые затем автоматически передаются по ссылке, поскольку именно это вызывает конструктор.

Однако ваша проблема заключается не в этом. Вы спрашиваете об одном, а затем показываете ошибку, которая относится к совершенно другой проблеме.

У вас есть конструктор (на самом деле любая функция будет делать то же самое), который ожидает пару указателей на две функции:

  • void (*)(длинное беззнаковое целое, uint8_t)
  • uint8_t (*)(длинное беззнаковое целое)

То есть вам нужны две функции, такие как:

void reader(unsigned long a, uint8_t b) {
}

и

uint8_t writer(unsigned long a) {
    return 3;
}

Но вместо этого вы пытаетесь передать функции-члены класса. Проблема здесь в том, что хотя они выглядят одинаково, поскольку они являются членами класса, они не одинаковы.

Любая функция-член класса имеет дополнительный подразумеваемый параметр, который является указателем на содержащий экземпляр класса и называется this. Так что ваши функции на самом деле выглядят примерно так:

void reader(Adafruit_FRAM_I2C_EDB *this, unsigned long a, uint8_t b) {
}

и

uint8_t writer(Adafruit_FRAM_I2C_EDB*this, unsigned long a) {
    return 3;
}

(Примечание: это всего лишь приближение — на самом деле все гораздо сложнее, но это служит иллюстрацией проблемы). Как видите, это не соответствует ожидаемому результату, и поэтому вы не можете передать эти функции родительскому конструктору — они просто неверны.

Это часто встречающаяся проблема — часто наблюдается при попытке использовать функцию-член класса с attachInterrupt(). Вы просто не можете этого сделать. Или не так, во всяком случае.

Единственный способ передать функцию-член напрямую — сделать ее статической. Тогда она будет действовать как обычная функция, и параметра this не будет. Однако она может действовать только на другие статические переменные и функции. Поэтому ее использование требует немного большей осторожности — плюс, конечно, статические функции и переменные являются общими для всех экземпляров класса.

,