I'm new in C++. I'm trying to do clickable button with OpenGL. I work for add callback function for each button for 2 days, I tried many methods I found but I can't do this. My below codes are giving memory error. Where is my mistake?
main.h
#include <vector>
class Button {
public:
// Storage Vector
static std::vector<Button> Buttons;
// typedef Function
typedef void (*pointerFunction)();
// Constructor
Button(/*Parameters*/);
// Setting Callback
void setCallBack(void(*function)());
// Callback pointer
pointerFunction callback;
int value{ 4 };
};
main.cpp
#include <iostream>
#include "main.h"
std::vector<Button> Button::Buttons;
Button::Button(/*Parameters*/) {
// ...
Button::Buttons.push_back(*this);
}
void Button::setCallBack(void(*function)()) {
this->callback = function;
this->callback(); // Here is work!
}
void testFunction() {
std::cout << "Test\n";
}
void createMember() {
Button classMember;
classMember.setCallBack(&testFunction);
}
int main() {
createMember();
for (Button& button : Button::Buttons) {
std::cout << button.value; // I can access this value.
button.callback(); // But here is give memory error!
}
return 0;
}
Within this function
void createMember() {
Button classMember;
classMember.setCallBack(&testFunction);
}
there are two things that are being doing. The first one is creating the local object classMember. The called construcfor pushes a copy of the object inside the vector Buttons. The data member callback of the copy was not initialized.
It is the data member callback of the .local object classMember that was initialized after its copy was pushed on the vector.
Rewrite the function at least like
void createMember() {
Button classMember;
Button::Buttons.back().setCallBack(&testFunction);
}
You should initialize all data members for example using in particular the literal nullptr if a corresponding initializer was not explicitly supplied. In this case you will be able to check whether a data member of a pointer type is equal to nullptr or stores an actual value.
Your createMember function don't work as you expect.
void createMember() {
Button classMember;
classMember.setCallBack(&testFunction);
}
Creates a local object that will be destroyed at function exit.
You can do it like this (though I don't think it is a good solution.)
Button & createMember() {
static Button classMember;
classMember.setCallBack(&testFunction);
return classMemeber;
}
A better solution:
std::vector<Button> Button::Buttons;
int main() {
Button b;
for (Button& button : Button::Buttons) {
button.setCallBack(testFunction);
std::cout << button.value; // I can access this value.
button.callback(); // But here is give memory error!
}
return 0;
}
Note that you have to defile Button::Buttons somewhere as it is a static member This was correct in your code, I overlooked it.
And, to add at least a Button, you have to create one to be added to the vector.
Ouput:
Test
4Test
You are calling testFunction twice, at setCallBack and in the loop.
(I've added a newline.)
If as the createMember function name suggest, you want to call that to create each new element, you could pass the function pointer in constructor. If it is trivially copyable like is in your example (no pointers or resource allocation in the class) you can just create the instance and the vector copy will be fine.
Button::Button(pointerFunction f) : callback (f) {
// ...
Button::Buttons.push_back(*this);
}
void createMember() {
Button classMember (testFunction);
}
int main() {
createMember ();
for (Button& button : Button::Buttons) {
std::cout << button.value; // I can access this value.
button.callback(); // But here is give memory error!
}
}
I don't think this is a good design for anything real, though.
Your mistake is that you create a local object, push the copy of it into the vector, put the callback address to the original object, and then destroy the original object. Well, you can put the callback address as the constructor argument, then the copy would have it.
// Constructor
Button(void(*function)(), /*Parameters*/) : callback{function} {
Button::Buttons.push_back(*this);
}
But I would recommend to add a static function to the Button class which is responsible for creation a Button object and returning reference to it. This is also eliminate unnecessary creation/deletion of temporary objects.
#include <iostream>
#include <vector>
class Button {
public:
// Storage Vector
static std::vector<Button> Buttons;
// typedef Function
typedef void (*pointerFunction)();
// Constructor
Button(/*Parameters*/);
// Setting Callback
void setCallBack(void(*function)());
// Callback pointer
pointerFunction callback;
template<class... U>
static Button& createButton(U&&... u) {
return Buttons.emplace_back(std::forward<U>(u)...);
}
int value{ 4 };
};
std::vector<Button> Button::Buttons;
Button::Button(/*Parameters*/) {
// ...
Button::Buttons.push_back(*this);
}
void Button::setCallBack(void(*function)()) {
this->callback = function;
this->callback(); // Here is work!
}
void testFunction() {
std::cout << "Test\n";
}
void createMember() {
auto &classMember = Button::createButton(/**/);
//Button classMember;
classMember.setCallBack(&testFunction);
}
int main() {
createMember();
for (Button& button : Button::Buttons) {
std::cout << button.value;
button.callback();
}
return 0;
}
Related
I am going from C development to C++ on the STM32 platform and simply cant find a suitable solution for my problem.
Please have a look at the simplified example code attached to this post.
#include <iostream>
#include <functional>
#include <list>
using namespace std;
class Pipeline {
public:
std::list<std::function<void(Pipeline*)>> handlers;
//add handler to list --> works fine
void addHandler(std::function<void(Pipeline*)> handler) {
this->handlers.push_front(handler);
}
void ethernetCallback(void) {
//handle received data and notify all callback subscriptions --> still works fine
// this callback function is normally sitting in a child class of Pipeline
int len = handlers.size();
for (auto const &handler : this->handlers) {
handler(this);
}
}
void removeHandler(std::function<void(Pipeline*)> handler) {
// Here starts the problem. I can not use handlers.remove(handler) here to
// unregister the callback function. I understood why I can't do that,
// but I don't know another way of coding the given situation.
}
};
class Engine {
public:
void callback(Pipeline *p) {
// Gets called when new data arrives
cout<<"I've been called.";
}
void assignPipelineToEngine(Pipeline *p) {
p->addHandler(std::bind(&Engine::callback, this, std::placeholders::_1));
}
};
int main()
{
Engine *e = new Engine();
Pipeline *p = new Pipeline();
e->assignPipelineToEngine(p);
// the ethernet callback function would be called by LWIP if new udp data is available
// calling from here for demo purposes only
p->ethernetCallback();
return 0;
}
The idea is that when the class "Pipeline" receives new data over ethernet, it informs all registered callback functions by calling a method. The callback functions are stored in a std::list. Everything works fine till here, but the problem with this approach is that I can't remove the callback functions from the list, which is required for the project.
I know why I can't simply remove the callback function pointers from the list, but I don't know another approach at the moment.
Probably anybody could give me a hint where I could have a look for solving this problem. All resources I've researched don't really show my specific case.
Thank you all in advance for your support! :)
One option would be to have addHandler return some sort of identifier that can later be passed to removeHandler. For example:
class Pipeline {
public:
std::map<int, std::function<void(Pipeline*)>> handlers;
int nextId = 0;
//add handler to list --> works fine
void addHandler(std::function<void(Pipeline*)> handler) {
handlers[nextId++] = handler;
}
void ethernetCallback(void) {
for (auto const& entry : handlers) {
entry.second(this);
}
}
void removeHandler(int handlerToken) {
handlers.erase(handlerToken);
}
};
class Engine {
public:
void callback(Pipeline *p) {
// Gets called when new data arrives
cout<<"I've been called.";
}
void assignPipelineToEngine(Pipeline *p) {
handlerToken = p->addHandler(
std::bind(
&Engine::callback,
this,
std::placeholders::_1
)
);
}
void unregisterPipelineFromEngine(Pipeline *p) {
p->removeHandler(handlerToken);
}
private:
int handlerToken;
};
Perhaps you could attach an ID to each handler. Very crude variant would just use this address as an ID if you have at most one callback per instance.
#include <functional>
#include <iostream>
#include <list>
using namespace std;
class Pipeline {
public:
using ID_t = void *; // Or use integer-based one...
struct Handler {
std::function<void(Pipeline *)> callback;
ID_t id;
// Not necessary for emplace_front since C++20 due to agreggate ctor
// being considered.
Handler(std::function<void(Pipeline *)> callback, ID_t id)
: callback(std::move(callback)), id(id) {}
};
std::list<Handler> handlers;
// add handler to list --> works fine
void addHandler(std::function<void(Pipeline *)> handler, ID_t id) {
this->handlers.emplace_front(std::move(handler), id);
}
void ethernetCallback(void) {
// handle received data and notify all callback subscriptions --> still
// works fine
// this callback function is normally sitting in a child class of
// Pipeline
int len = handlers.size();
for (auto const &handler : this->handlers) {
handler.callback(this);
}
}
void removeHandler(ID_t id) {
handlers.remove_if([id = id](const Handler &h) { return h.id == id; });
}
};
class Engine {
public:
void callback(Pipeline *p) {
// Gets called when new data arrives
cout << "I've been called.";
}
void assignPipelineToEngine(Pipeline *p) {
//p->addHandler(std::bind(&Engine::callback, this, std::placeholders::_1), this);
//Or with a lambda
p->addHandler([this](Pipeline*p){this->callback(p);},this);
}
void removePipelineFromEngine(Pipeline *p) { p->removeHandler(this); }
};
int main() {
Engine *e = new Engine();
Pipeline *p = new Pipeline();
e->assignPipelineToEngine(p);
// the ethernet callback function would be called by LWIP if new udp data is
// available calling from here for demo purposes only
p->ethernetCallback();
return 0;
}
You might also consider std::map<ID_t,std::function<...>> instead of list, not sure how memory/performance constrained you are.
Obligatory: do not use new, use std::unique_ptr, or better use automatic storage whenever you can. Although in this case a pointer is appropriate for e as you need stable address due to this capture/bind/ID.
std::functions are not comparable as there isn't a good generic way how to define this comparison.
I have a class named Window, and a big bunch of functions which need an instance of Window to work. So I have passed Window* win as an argument to each of these functions, but this approach creates a copy of Window* every time it is called and also, it seems kinda unnecessary passing the argument to each function. I have tried passing const Window* but that won't work either because the functions inside Window don't have a const version. I thought that using std:::shared_ptr and std::weak_ptr would help, but I still haven't figured it out correctly. Here's my current code:
// Window.h
class Window
{
public:
// some getters/setters and other functions
private:
// members are here
};
// on another file
int func1(Window* win /*I want to remove this */, ...) {
// use win and other parameters
}
And here's the smart pointers approach.
// class Window as declared above
// file.h
namespace space
{
struct context;
context* make_context(std::shared_ptr<Window> const& win); // const& to avoid copy
void destroy_context(context* ctx);
int func1(...); // note that ... is used to show some params whose type isn't important for the question.
}
// some other functions
// file.cpp
namespace space
{
struct context
{
std::weak_ptr<Window> target;
};
context* make_context(std::shared_ptr<Window> const& win) // const& to avoid copy
{
context* ctx = new context{};
ctx->target = win;
return ctx;
}
void destroy_context(context* ctx)
{
if(ctx != nullptr)
{
delete ctx;
ctx = nullptr;
}
}
int func1(...)
{
// use ... params
} // int func1(...)
} // namespace space
// main.cpp
int main(int, char**)
{
std::shared_ptr<Window> window{new Window{/*some arguments*/}};
auto ctx{space::make_context(window)};
int func1();
// some other code
delete ctx;
}
As explained already in the various comments, a parameter which is a pointer like Window* will never create a copy of the Window object. This is why a pointer is made for.
Before trying to figure out more complex tools like smart pointers, I suggest you first spend time understanding C++ fundamental. You should start by what is a value, what is a pointer, what is a reference.
I need to call a function when a button is pressed. The following function should take in the function to be called:
void ButtonLayer::LoadButton(void(*func)()) {
// do button loading stuff
// if button is clicked...
func();
}
This would work except for the fact that passing a function within a seperate namespace gives the following error:
argument of type "void(OtherLayer::*)()" is incompatiable with parameter of type "void(*)()"
I don't want to make every function I pass static to avoid this problem, so I need some way of converting a function within a namespace to be of type void(*). I have tried static casting but I'm unsure of the exact syntax as I'm new to C++
It seems that you want to pass a member function.
This example may help you.
class A {
public:
int i;
int fun(int j) {
return i + j;
};
};
void fun(int j, A ob, int (A::* p)(int)) {
std::cout << (ob.*p)(j);
}
void main() {
int (A:: * fp)(int); //declare fp as a function pointer in class A
fp = &A::fun; //init fp
A obj;
obj.i = 1;
fun(123, obj, fp);
}
Based on #Yksisarvinen and #MSalters comments, the solution was:
void ButtonLayer::LoadButton(std::function<void()>) {
// do button loading stuff
// if button is clicked...
func();
}
and then to call it:
LoadButton([this] { functionToCall; });
It is a callback function but I can't figure out how this part works
if (cb_onPress) { cb_onPress(*this); } //fire the onPress event
class Button;
typedef void (*buttonEventHandler)(Button&);
class Button {
public:
//code
private:
//code
buttonEventHandler cb_onPress;
};
void Button::process(void)
{
//code
if (cb_onPress) { cb_onPress(*this); } //fire the onPress event
}
void Button::pressHandler(buttonEventHandler handler)
{
cb_onPress = handler;
}
cb_onPress is a pointer to a function returning void and taking a Button& parameter. It could point to something like this:
void foo(Button&){ std::cout << "Foo Button!\n"; }
This line, inside a Button member function,
if (cb_onPress) { cb_onPress(*this); }
checks that the pointer to function is not null, and if so, calls it, passing the same instance of Button as parameter (that is what passing *this achieves).
Example of use:
Button b;
b.pressHandler(foo); // sets cb_onPress to point to foo
....
b.process(); // Prints "Foo Button"
although presumably the call to process happens internally, in response to a n event.
if (cb_onPress) { cb_onPress(*this); }
cb_onPress is a pointer to a function. If the pointer's a nullptr you can't call it, so the code checks it's not beforehand.
The overall supported client usage is like this:
void myButtonEventHandler(Button& b) { ...do something when pressed... };
Button button; // make a button
button.pressHandler(myButtonEventHandler);
if (cb_onPress)
checks if cb_onPress is null pointer. In other words checks if that function was defined before. If it isn't then it calls function
cb_onPress
on that object
struct object
{
void function()
{
std::cout << "function" << std::endl;
}
};
int main()
{
// create vectors for objects and functions)
std::vector<object*> objectvec;
std::vector<void*> functionlist;
objectvec.push_back(new object);
// create a pointer to an object's function
void (object::* ptfptr_function) (void) = &object::function;
functionlist.push_back(&ptfptr_tryfunc);
// how do I call "functionvec[0]->tryfunc()" using the functionlist?
// (following line does nothing:)
functionlist[0];
}
You want this:
std::vector<void(object::*)()> functionlist; // container
functionlist.push_back(&object::function); // add pointer-to-member-fn
(objectvec[0]->*functionlist[0])(); // invoke ptmf on an instance