Мой поток похож на мой предыдущий , который мне удалось обработать с помощью лямбда-функции.Но опять же у меня есть проблема, и я думаю, что лямбда-функции не являются решением проблемы, которую я на самом деле еще не полностью понял.
Что мне требовалось: в моих проектах arduino мне часто приходилось нуждатьсяизменить значение переменной вручную во время выполнения (например, рабочий цикл, состояние светодиода или что-либо еще).Поэтому я реализовал класс, который обеспечивает эту функциональность простым в использовании способом, то есть я могу получить доступ к глобальным переменным путем анализа входной строки (через serial, tcp, telnet, we), отформатированной в моем собственном протоколе.Еще одно требование состояло в том, чтобы было возможно связать событие с такой переменной, которая выполняется, когда к переменной обращаются через мой интерфейс.
Что я сделал: в упрощенной версии мой «параметр класса» имеетатрибуты для адреса моей переменной, имя, которое я выбираю для доступа к нему, и указатель на функцию, которую я хочу связать.Соответствующие методы read и write обрабатывают для доступа к фактическому значению по заданному адресу.Оригинальная версия также имеет атрибуты с информацией об уровне доступа и типе данных.Кроме того, отдельные события связаны с событиями чтения и записи, но я опущу эту часть, а также тот факт, что все соответствующие функции перегружены для доступа к переменным различных типов данных.Поэтому я буду придерживаться упрощенной версии, поскольку она достаточно короткая, чтобы продемонстрировать мою проблему.
Чтобы мой класс работал, мне нужна функция 'new_parameter (,)' для хранения моих параметров в векторе и функциях 'read_param () '/' write_param () 'для фактического доступа к определенному параметру через строку.Эти последние функции представляют мой синтаксический анализатор, но я также опущу это.
Ниже приведен код, который, кажется, работает хорошо, хотя я уверен, что есть более эффективные способы сделать это.Для MCVE он скомпилирован с g ++ 7.3.0, но, наконец, он должен скомпилироваться с avr-g ++.
//parameter.h
using namespace std;
class Parameter {
public:
Parameter(int *p, std::string n, void (*e)());
long int address;
std::string name;
void (*event)();
int read();
void write(int v);
};
Parameter::Parameter (int *p, std::string n, void (*e)()) {
address = (long int) p;
name=n;
event=e;
}
int Parameter::read () {
if(event!=NULL){
event();
}
int value = *reinterpret_cast<int*>(address);
return value;
}
void Parameter::write (int v) {
if(event!=NULL){
event();
}
*(int*)(address)=v;
}
std::vector<Parameter> parameters;
void new_parameter (int *p, std::string n="defaultname", void (*e)()=NULL) {
parameters.push_back(Parameter(p,n,e));
}
int read_param(std::string n) {
for (int i=0;i<parameters.size();i++) {
if (parameters[i].name == n) {
int v = parameters[i].read();
return v;
}
}
return -1;
}
void write_param(std::string n, int v) {
for (int i=0;i<parameters.size();i++) {
if (parameters[i].name == n) {
parameters[i].write(v);
break;
}
}
}
//simple_main.cpp
#include <vector>
#include <string>
#include <locale>
#include <functional>
#include "device.h"
//a global variable
int variable1=-1;
//an event executed when global variable is accessed
void variable1_event () {printf("variable1 event\n");}
int main () {
//create parameter object for variable
new_parameter(&variable1,"variable1",variable1_event);
//read parameter
printf("1: %i\n",variable1);
printf("2: %i\n",read_param("variable1"));
//change value
write_param("variable1",10);
//read parameter
printf("3: %i\n",variable1);
printf("4: %i\n",read_param("variable1"));
}
При выполнении main () имеет следующий вывод:
1: -1
variable1 event
2: -1
variable1 event
3: 10
variable1 event
4: 10
Это отвечало моим требованиям в прошлом.Пока все хорошо!
В моем текущем проекте у меня есть переменное количество подчиненных устройств, подключенных к моему mcu (ESP32) с I2C, каждое из которых имеет идентичный набор параметров (например, заданная температура для контроля температуры)что я теперь хочу получить доступ через мой ранее продемонстрированный «параметр класса».Поскольку ведомые устройства одного типа, создание «класса Device» является очевидным решением.Затем я создаю любое количество объектов, в зависимости от того, сколько i2c-ведомых подключено.Использование «класса Device» означает, что мой параметр теперь будет указывать на атрибут, а соответствующая функция события теперь является методом события.Дело в том, что этот event-метод должен передавать данные определенному ведомому устройству и поэтому не может быть статичным, так как он должен вызываться с разными i2c-адресами (верно?).Я попробовал все, что было в моих силах, но пока не заставил его работать.
Это моя упрощенная версия «класса Device»:
//parameter.h
#define MAX_DEVICES 4
int device_count=0;
class Device {
public:
Device();
Device(uint8_t i2c_address);
bool is_default;
uint8_t i2c_address;
int data;
void i2c_write();
};
Device::Device () {
is_default=true;
}
Device::Device (uint8_t i2c) {
is_default=false;
i2c_address=i2c;
}
Device devices [MAX_DEVICES];
void Device::i2c_write () {
printf("call to i2c_write (address %i, data %i)\n",i2c_address,data);
}
int get_free_index () {
for (int i=0; i<MAX_DEVICES; i++) {
if (devices[i].is_default) return i;
}
return -1;
}
void new_device (uint8_t i2c) {
int new_index=get_free_index();
if (new_index>=0) {
devices[new_index]=Device(i2c);
// new_parameter(&devices[new_index].data, "device"+std::to_string(new_index)+"data", devices[new_index].i2c_transmit)
}
else printf("Error: exceeded maximum number of engines\n");
}
См. Мою расширенную основную функциюниже, чтобы увидеть, как я хотел бы обработать свои устройства.
//advanced_main.cpp
#include <vector>
#include <string>
#include <locale>
#include <functional>
#include "parameter2.h"
#include "device2.h"
int variable1=-1;
void variable1_event () {printf("variable1 event\n");}
int main () {
//create parameter object for variable
new_parameter(&variable1,"variable1",variable1_event);
new_device(10);
new_device(10);
//read/write parameter
printf("1: %i\n",read_param("variable1"));
printf("2: %i\n",read_param("device0data"));
printf("3: %i\n",read_param("device1data"));
write_param("variable1",10);
write_param("device0data",20);
write_param("device1data",30);
printf("4: %i\n",read_param("variable1"));
printf("5: %i\n",read_param("device0data"));
printf("6: %i\n",read_param("device1data"));
}
Результат, который я ожидал бы, если бы это работало:
variable1 event
1: -1
call to i2c_transmit (address 19, data 123)
2: 123
call to i2c_transmit (address 23, data 123)
3: 123
variable1 event
call to i2c_transmit (address 19, data 123)
call to i2c_transmit (address 23, data 123)
variable1 event
4: 10
call to i2c_transmit (address 19, data 20)
5: 20
call to i2c_transmit (address 23, data 30)
6: 30
, но на самом деле он даже не компилируетсяв этой версии:
device.h:40:120: error: invalid use of non-static member function ‘void Device::i2c_transmit()’
devices[new_index].data, "device"+std::to_string(new_index)+"data", devices[new_index].i2c_transmit)
Все остальные способы, которыми я пытался передать функцию-член 'i2c_transmit ()' в конструктор 'параметра класса', тоже не работали, и хотя я часто понимаю, почему,я понятия не имею, КАК это работает ...
Тривиально ли создать локальный объект, сохранить копию этого объекта в глобальном массиве и работать только с этой копией?Я думаю, это то, что делает мой код выше.Я также пытался объявить 'Device devices [MAX_DEVICES];'
как статический, но это не сработало.Я пытался использовать лямбда-функцию, но тоже не повезло ... Трудно сказать, что еще я пробовал, но я все равно думаю, что у меня есть проблемы с общей структурой.Я открыт для новых предложений, но так как «Параметр класса» является частью библиотеки, я бы хотел, чтобы этот класс не изменялся!