我的线程似乎与previous one类似,我设法使用lambda函数对其进行了处理。 但是我又遇到了一个问题,我猜想lambda函数不是解决问题的方法,我实际上还没有完全理解。
我需要什么: 在我的arduino项目中,我经常需要在运行时手动更改变量的值(例如,占空比,led的状态或其他)。 因此,我实现了一个以易于使用的方式提供该功能的类,这意味着我可以通过解析以我自己的协议格式设置的输入字符串(通过串行,tcp,telnet等方式)来访问全局变量。 另一个要求是,还应该可以将事件链接到这样的变量,该变量在通过我的界面访问该变量时执行。
我做了什么: 在简化版本中,我的“类参数”具有变量地址的属性,选择访问它的名称以及指向要绑定的函数的指针。 相应的读取和写入方法句柄用于访问给定地址的实际值。 原始版本还具有包含有关访问级别和数据类型的信息的属性。 另外,单独的事件也必定会读写事件,但是我将忽略这一部分,以及所有相关函数都将重载以访问不同数据类型的变量这一事实。 因此,我会坚持使用简化版本,因为它足够简短以证明我的问题。
要使我的类正常工作,我需要一个函数'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
这符合我过去的要求。到目前为止,一切都很好!
在我当前的项目中,我有数量可变的从设备通过I2C连接到我的单片机(ESP32),每个从设备都有一组相同的参数(例如,用于温度控制的设定温度),现在我想通过以前的参数访问展示了“类参数”。 由于从设备是同一种设备,因此创建“类设备”是显而易见的解决方案。 然后,根据连接的i2c从站数量,创建任意数量的对象。 使用“类设备”的意思是,我的参数现在将指向一个属性,而相应的事件函数现在是一种事件方法。 问题在于,此事件方法必须将数据传输到特定的从属设备,因此不能是静态的,因为必须使用不同的i2c地址来调用它(对吗?)。 我已经尽我所能尝试了一切,但是还没有使它起作用。
这是我对“设备类”的简化版本:
//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()'传递给'class parameter'的构造函数的所有其他方式也不起作用,即使我经常理解为什么,我也不知道它如何工作。
创建本地对象,将该对象的副本存储到全局数组并仅在该副本上工作是不重要的吗?我猜,那就是我上面的代码所做的。我还尝试将'Device devices [MAX_DEVICES];'
声明为静态,但是没有用。我尝试使用Lambda函数,但也没有运气...
很难说出我还尝试了什么,但是我仍然认为我的总体结构还是有问题。
我愿意接受新的建议,但是由于“类参数”是库的一部分,所以我希望不要更改该类!
答案 0 :(得分:1)
void (*event)()
是一个设计不良的回调。 C风格代码中的回调既具有函数指针,又具有void*
,如下所示:
void (*event)(void*);
void* state;
答案 1 :(得分:0)
您可以在回叫中添加参数,如其他答案所述。另一种选择是完全消除回调并使用OOP设计。创建一个围绕int的包装器类,并使用用于获取和设置的虚拟函数。
class Parameter {
public:
Parameter(String n, int default_value = 0) :
name(n), value(default_value) {}
// Virtual functions that can be overridden but have default functionality
virtual int read() {
return value;
}
virtual int write(int new_value) {
return value = new_value;
}
String name;
protected:
int value;
// Below are static functions and variables
// ----
// std lib is not supported on Arduino, so use an array
// Arduino programs are small enough that you should be
// able to make an educated guess at MAX_PARAMETERS
// Also note it is an array of pointers for virtual functions to work
static Parameter *all_values[MAX_PARAMETERS];
static int parameter_count;
public:
static bool add_param(Parameter *p) {
if (parameter_count < MAX_PARAMETERS) {
all_values[parameter_count++] = p;
return true;
}
return false;
}
static Parameter * find_param(String name) {
for (int i = 0; i < parameter_count; i++) {
if (all_values[i]->name == name) return all_values[i];
}
return nullptr;
}
};
Parameter * Parameter::all_values[MAX_PARAMETERS];
int Parameter::parameter_count = 0;
对于i2c参数,您可以扩展此类
class Device : public Parameter
{
protected:
uint8_t address;
public:
Device(std::string n, uint8_t i2c_address) :
address(i2c_address), Parameter(n) {}
// Override these
int read() {
// Add your code here to read the value from the bus....
return value;
}
int write(int new_value) {
Parameter::write(new_value);
// Add your code here to write the value to the bus....
return value;
}
};
要使用它,您可以执行以下操作:
// Create and add regular param
Parameter::add_param(new Parameter("test1"));
// Create and add i2c device param
Parameter::add_param(new Device("test2", 99));
Parameter::find_param("test1")->write(100);
int x = Parameter::find_param("test1")->read();
Parameter::find_param("test2")->write(123);
int y = Parameter::find_param("test2")->read();
// You can also use temp vars to simplify
Parameter *some_device = Parameter::find_param("test2");
some_device->write(100);
int z = some_device->read();
我知道Arduinos的资源非常有限,所以我很想看看这段代码如何在设备上执行。