ESP home using Arduino library - c++

I am about to write a custom ESPHome component. I don't have much experience with C language and I am facing some troubles using external library.
For demonstration, I prepared a simple component class..
class Test: public Component {
public:
auto t = timer_create_default();
void setup() override {
ESP_LOGD("TEST", "Test setup called!");
t.every(1000, TestLog);
}
void loop() override {
ESP_LOGD("TEST", "Test loop called!");
t.tick();
}
bool TestLog(void *) {
ESP_LOGD("TEST", "TestLOG!");
return true;
}
}
With this, I receive:
In file included from src\main.cpp:32:0: src\Test.h:7:35: error:
non-static data member declared 'auto'
auto t = timer_create_default();
I took it from some example where they did not have the class, but I can't find out, how to use it.
The library is:
https://github.com/contrem/arduino-timer/
I can still rewrite it without this timer completely and handle it only in the loop function, but I would like to understand what I am doing wrong.
If I change the return type to Timer<> I got another error:
src\Test.h: In member function 'virtual void Test::setup()':
src\Test.h:11:24: error: no matching function for call to
'Timer<>::every(int, )'
t.every(1000, TestLog);

You can not use auto to declare non-static member variables so you need to replace auto with the type returned by timer_create_default().
If you are not sure what type it returns, you can simply use decltype in the declaration:
decltype(timer_create_default()) t = timer_create_default();
If I read the code in the repo correctly, the returned type is Timer<>, so this should also work:
Timer<> t = timer_create_default();
or simply:
Timer<> t;
Also: The function pointer passed to t.every() should be a bool (*)(void*) but TestLog is a non-static member function and the pointer type is bool (Test::*)(void*) - You can fix that by making TestLog static:
class Test: public Component {
public:
// ...
static bool TestLog(void *) {
ESP_LOGD("TEST", "TestLOG!");
return true;
}
};
If you want to get the Test instance in the TestLog callback, make the Timer
Timer<TIMER_MAX_TASKS, millis, Test*> t;
and change TestLog:
class Test: public Component {
public:
// ...
static bool TestLog(Test* t) {
ESP_LOGD("TEST", "TestLOG!");
return true;
}
};
and in setup():
t.every(1000, TestLog, this);
You'll now get a pointer to the Test instance in the TestLog callback and you can use this to call a non-static member function in Test.
Full example:
class Test : public Component {
public:
Timer<TIMER_MAX_TASKS, millis, Test*> t;
void setup() override {
ESP_LOGD("TEST", "Test setup called!");
// call the static member function every second:
t.every(1000, TestLogProxy, this);
}
void loop() override {
ESP_LOGD("TEST", "Test loop called!");
t.tick();
}
bool TestLog() {
ESP_LOGD("TEST", "TestLOG!");
return true;
}
static bool TestLogProxy(Test* t) {
// forward the callback call to the non-static member function:
return t->TestLog();
}
};

Related

Passing both member functions and lambdas to std::function

I'm trying to create control/window events similar to how they are done in C#'s Windows Forms/WPF. Each event class contains an argument type and passes that as well as a pointer to the sending object. This is all fine and dandy when using a lambda function, but when I try to add a member function to the event I get a compile error of C2276 '&': illegal operation on bound member function expression and C2660 'Event<MouseButtonEventArgs>::add': function does not take 1 arguments.
How would I create an event class that can take lambda functions and member functions from any class (whether or not that class is the "sender")?
Event.h
template<class Args> struct EventListener {
/* The function type to be called by the event. */
typedef function<void(ControlBaseSPtr, Args&)> ArgsFunction;
string name;
ArgsFunction function;
};
template<class Args> class Event {
/* The function type to be called by the event. */
typedef function<void(ControlBaseSPtr, Args&)> ArgsFunction;
deque<EventListener<Args>> listeners;
Event(EventDirections direction) {
this->direction = direction;
}
void add(const string& name, ArgsFunction function) {
EventListener<Args> listener = EventListener<Args>(name, function);
for (auto it = listeners.begin(); it != listeners.end(); it++) {
if (it->name == name) {
throw exception("Event::operator+= listener already exists in the event");
}
}
listeners.push_back(listener);
}
//...
};
Control.h
class ControlBase {
Event<MouseEventArgs> _eventMouseMovedGlobal;
Event<MouseButtonEventArgs> _eventMouseButtonGlobal;
ControlBase();
void onMouseMovedGlobal(ControlBaseSPtr sender, MouseEventArgs e);
};
Control.cpp
ControlBase::ControlBase() {
// Works
_eventMouseButtonGlobal.add("some name", [](ControlSPtr control, MouseButtonEventArgs e){
//...
};
// Doesn't work
_eventMouseMovedGlobal.add("some name", bind(&onMouseMovedGlobal, this, placeholders::_2))
// Doesn't work
_eventMouseMovedGlobal.add("some name", bind(&onMouseMovedGlobal, this))
}
void ControlBase::onMouseWheelGlobal(ControlBaseSPtr sender, MouseWheelEventArgs e) {
//...
}
Well, since onMouseMovedGlobal has 2 input arguments, you need placeholders or values for both of them when creating a binder. So, instead of
_eventMouseMovedGlobal.add("some name", bind(&onMouseMovedGlobal, this, placeholders::_2))
you need
_eventMouseMovedGlobal.add("some name", bind(&ControlBase::onMouseMovedGlobal, this, placeholders::_1, placeholders::_2))

C++ event handling interface with function pointers

I want to implement a simple event interface with function pointers for a simple game in C++. This will be done to improve the event interface of the allegro lib. Therefore I've written the following code but it doesn't work.
typedef void (*event_handler)(int); //type for the event handler
const int TESTKEY_A = 1; // example Key as event arg
class Game
{
private:
bool is_running ;
protected:
event_handler on_key_down[2];
public:
void run();
void do_events(int e) ;
void stop() {is_running = false;}
};
void Game::run()
{
is_running=true;
while(is_running)
do_events(1);
}
void Game::do_events(int e)
{
if(e==1)
{
for(int i = 0; i < 2 ;i++)
on_key_down[i](TESTKEY_A);
}
}
class Pong_Game : public Game
{
public:
Pong_Game();
void On_Key_Down_Player1(int key) { return;}
void On_Key_Down_Player2(int key) { return;}
};
Pong_Game::Pong_Game()
{
on_key_down[0] = &this->On_Key_Down_Player1;
on_key_down[1] = &this->On_Key_Down_Player2;
}
int main()
{
Game *my_game = new Pong_Game();
my_game->run();
return 0;
}
Compiler log:
Compiler: Default compiler
Executing g++.exe...
g++.exe "U:\Eigene Dateien\eventhandler.cpp" -o "U:\Eigene Dateien\eventhandler.exe" -I"C:\Dev-Cpp\lib\gcc\mingw32\3.4.2\include" -I"C:\Dev-Cpp\include\c++\3.4.2\backward" -I"C:\Dev-Cpp\include\c++\3.4.2\mingw32" -I"C:\Dev-Cpp\include\c++\3.4.2" -I"C:\Dev-Cpp\include" -L"C:\Dev-Cpp\lib"
U:\Eigene Dateien\eventhandler.cpp: In constructor `Pong_Game::Pong_Game()':
U:\Eigene Dateien\eventhandler.cpp:45: error: ISO C++ forbids taking the address of a bound member function to form a pointer to member function. Say `&Pong_Game::On_Key_Down_Player1'
U:\Eigene Dateien\eventhandler.cpp:45: error: cannot convert `void (Pong_Game::*)(int)' to `void (*)(int)' in assignment
U:\Eigene Dateien\eventhandler.cpp:46: error: ISO C++ forbids taking the address of a bound member function to form a pointer to member function. Say `&Pong_Game::On_Key_Down_Player2'
U:\Eigene Dateien\eventhandler.cpp:46: error: cannot convert `void (Pong_Game::*)(int)' to `void (*)(int)' in assignment
Execution terminated
EDIT:
- changed code
- added compiler log
Thank you!
You are using a member function to initialize a "plain" function pointer. Member functions are different from "plain" functions in that they have a hidden T *this passed along with the function.
This seems like something that would be MUCH better solved by having a "event handler" interface, implemented by inheritance, than using function pointers.
Something like this:
class Game
{
private:
bool is_running ;
public:
void run(){is_running=true; while(is_running) do_events(1);}
void do_events(int e) {if(e==1) On_Key_Down(TESTKEY_A);}
void stop() {is_running = false;}
virtual void On_Key_Down(int key) = 0;
... other event handlers here ...
};
class Pong_Game : public Game
{
public:
void Pong_Game() {}
void On_Key_Down(int key) { // handle key event...}
... other event handlers here ...
};
As per comments:
To cover multiple players, you may want to implement a "per player event handling":
class Player
{
public:
enum moves { move_up,
move_down,
move_left,
move_right,
move_jump,
move_shoot, ... };
...
virtual void On_Key_Down(int key) = 0;
...
};
class Player_A
{
public:
...
virtual moves On_Key_Down(int key) { if (key == 'W') return move_up; ... }
}
class Player_B
{
public:
...
virtual moves On_Key_Down(int key) { if (key == 'I') return move_up; ... }
}
class Pong_Game : public Game
{
private:
vector<Player *> players;
public:
void Pong_Game() {}
void On_Key_Down(int key)
{
for(p : players) {
Player::moves m = p->On_key_down();
...
}
}
... other event handlers here ...
};
(This is just quickly hacked up - the "moves" may perhaps be better placed somewhere else, and the exact structure is just "what I could think of right now" - you probably need a class Player_A_Pong : public Player_A or some such to deal with the specifics of "player A's racket is here, player B's racket is here..." - although there are probably other ways to deal with that too).

C++ Basic error with classes

class IEngine: public ICoreObject
{
private:
Network *_Network;
bool _Process;
public:
IEngine();
~IEngine();
void Initial(...);
void StartServer(unsigned short port);
bool Process();
void StopProcess();
void StartProcess();
friend void ShellCode(int id,struct NE_Bin var);
};
Why i cant use _Network from ShellCode ?
this is definition of ShellCode function
void ShellCode(int id,struct NE_Bin var) //функция-друг
{
std::cout<<"ShellFunc Delegated"<<std::endl;
var.data = (void *)"T";
var.length = 1;
//_Network->SendMessageW(id, var);
}
Im trying to get to the _Network class and i get errors.
IEngine has ShellCode as a friend. That means you can access private **members of an IEngine object. _Network is just a variable in your context, not bound to anything.
Something like this would work:
void ShellCode(int id,struct NE_Bin var, IEngine* pEngine)
{
std::cout<<"ShellFunc Delegated"<<std::endl;
var.data = (void *)"T";
var.length = 1;
pEngine->_Network->SendMessageW(id, var);
}
but you need to pass the IEngine as parameter:
IEngine* pEngine /*initialize it here*/;
ShellCode(0, NE_Bin(), pEngine);
or you can use a global instance inside the method (if you have one).
Well you'd need an instance of of IEngine as a parameter to access it's member variable...

Passing a member function as parameter of a member function

I'm busy with making a leveleditor class in an engine but I'm stuck at passing a member function as parameter of another member function.
First I've made a typedef
typedef void (LevelEditor::*CallFunctionPtr)();
Then I have made a member function to check if the user clicks with his mouse on a hitregion. If so, another function needs to be called. So I've my first member function with 2 parameters
LevelEditor.h
void CheckClickCollision(HitRegion* region, CallFunctionPtr callFunctionPtr);
LevelEditor.cpp
void LevelEditor::CheckClickCollision( HitRegion* region, CallFunctionPtr callFunctionPtr)
{
if(GAME_ENGINE->GetLButtonMouseState())
{
if(!m_bIsLeftPressed && region->HitTest(m_MousePosition))
(this->*callFunction)();
m_bIsLeftPressed = true;
}
else
m_bIsLeftPressed = false;
}
Then I've two stupid example member functions:
LevelEditor.h
void LevelUp();
void LevelDown();
LevelEditor.cpp
void LevelEditor::LevelUp()
{
++m_iCurrentLevel;
}
void LevelEditor::LevelDown()
{
if(m_iCurrentLevel > 0)
--m_iCurrentLevel;
else
return;
}
And now I want to call that function every tick to check if there is a hit. So in my tick function:
CheckClickCollision(m_LeftArrowRegionPtr, LevelDown);
CheckClickCollision(m_RightArrowRegionPtr, LevelUp);
And here I get the error on LevelDown and Levelup:
Error: argument of type void (LevelEditor::*)()" is incompatible with parameter of type "CallFunctionPtr *"
Dont know how to fix it. Tried different things, nothing worked
Try
CheckClickCollision(m_LeftArrowRegionPtr, &LevelEditor::LevelDown);
CheckClickCollision(m_RightArrowRegionPtr, &LevelEditor::LevelUp);
For your convenience, here's the working sample (the compiler is GCC 4.7):
#include <stdio.h>
class LevelEditor;
typedef void (LevelEditor::*CallFunctionPtr)();
class LevelEditor
{
public:
LevelEditor() {}
void CheckClickCollision(void* region, CallFunctionPtr callFunction)
{
(this->*callFunction)();
}
void LevelUp() { printf("up\n"); }
void LevelDown() { printf("down\n"); }
void Test()
{
CheckClickCollision(NULL, &LevelEditor::LevelDown);
CheckClickCollision(NULL, &LevelEditor::LevelUp);
}
};
int main()
{
LevelEditor e;
e.Test();
return 0;
}
The other way to call this:
void Test()
{
CallFunctionPtr p;
p = &LevelEditor::LevelDown;
CheckClickCollision(NULL, p);
p = &LevelEditor::LevelUp;
CheckClickCollision(NULL, p);
}
You need to use std::function and std::bind, or lambdas if you have a supporting compiler.
void LevelEditor::CheckClickCollision( HitRegion* region, std::function<void()> callFunction)
{
if(GAME_ENGINE->GetLButtonMouseState())
{
if(!m_bIsLeftPressed && region->HitTest(m_MousePosition))
callFunction();
m_bIsLeftPressed = true;
}
else
m_bIsLeftPressed = false;
}
void Test()
{
// lambda
CheckClickCollision(NULL, [this] { LevelDown(); });
// bind
CheckClickCollision(NULL, std::bind(&LevelEditor::LevelDown, this));
}

Static function needing to deal with members of a C++ class

I have to make some kind of bridge between two pieces of software, but am facing an issue I don't know how to deal with. Hopefully someone will have interesting and (preferably) working suggestions.
Here is the background : I have a C++ software suite. I have to replace some function within a given class with another function, which is ok. The problem is that the new function calls another function which has to be static, but has to deal with members of the class. This is this second function which is making me mad.
If the function is not static I get the following error :
error: argument of type ‘void (MyClass::)(…)’ does not match ‘void (*)(…)’
If I set it to static I get either the following error :
error: cannot call member function ‘void
MyClass::MyFunction(const double *)’ without object
or
error: ‘this’ is unavailable for static member functions
depending on if I use or not the "this" keyword ("Function()" or "this->Function()").
And finally, the class object requires some arguments which I cannot pass to the static function (I cannot modify the static function prototype), which prevents me to create a new instance within the static function itself.
How would you deal with such a case with minimal rewriting ?
Edit : Ok, here is a simplified sample on what I have to do, hoping it is clear and correct :
// This function is called by another class on an instance of MyClass
MyClass::BigFunction()
{
…
// Call of a function from an external piece of code,
// which prototype I cannot change
XFunction(fcn, some more args);
…
}
// This function has to be static and I cannot change its prototype,
// for it to be passed to XFunction. XFunction makes iterations on it
// changing parameters (likelihood maximization) which do not appear
// on this sample
void MyClass::fcn(some args, typeN& result)
{
// doesn't work because fcn is static
result = SomeComputation();
// doesn't work, for the same reason
result = this->SomeComputation();
// doesn't work either, because MyClass has many parameters
// which have to be set
MyClass *tmp = new MyClass();
result = tmp->SomeComputation();
}
Pointers to non-static member functions are a bit tricky to deal with. The simplest workaround would just be to add an opaque pointer argument to your function which you can then cast as a pointer to 'this', then do what you need with it.
Here's a very simple example:
void doSomething(int (*callback)(void *usrPtr), void *usrPtr)
{
// Do stuff...
int value = callback(usrPtr);
cout << value << "\n";
}
class MyClass
{
public:
void things()
{
value_ = 42;
doSomething(myCallback, this);
}
private:
int value_;
static int myCallback(void *usrPtr)
{
MyClass *parent = static_cast<MyClass *>(usrPtr);
return parent->value_;
}
};
int main()
{
MyClass object;
object.things();
return 0;
}
In this example myCallback() can access the private value_ through the opaque pointer.
If you want a more C++-like approach you could look into using Boost.Function and Boost.Bind which allow you to pass non-static member functions as callbacks:
void doSomething(boost::function<int ()> callback)
{
// Do stuff...
int value = callback();
cout << value << "\n";
}
class MyClass
{
public:
void things()
{
value_ = 42;
doSomething(boost::bind(&MyClass::myCallback, this));
}
private:
int value_;
int myCallback()
{
return value_;
}
};
int main()
{
MyClass object;
object.things();
return 0;
}
If you really can't change the function prototype you could use a global pointer, but that opens up all sorts of issues if you will ever have more than one instance of your class. It's just generally bad practice.
class MyClass;
static MyClass *myClass;
void doSomething(int (*callback)())
{
// Do stuff...
int value = callback();
cout << value << "\n";
}
class MyClass
{
public:
void things()
{
value_ = 42;
myClass = this;
doSomething(myCallback);
}
private:
int value_;
static int myCallback()
{
return myClass->value_;
}
};
int main()
{
MyClass object;
object.things();
return 0;
}
Following spencercw's suggestion below the initial question I tried the "static member variable that you set to point to this" solution (the global variable would have been tricky and dangerous within the context of the software suite).
Actually I figured out there was already something like this implemented in the code (which I didn't write) :
static void* currentObject;
So I just used it, as
((MyClass*)currentObject)->SomeComputation();
It does work, thanks !!!
non-reentrant and non-thread-safe way is to pass "this" address using global variable.
You can move the result = SomeComputation(); out of your static function and place it in BigFunction right before your call to the static function.