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.
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'm making some SDL2 wrappers in C++. Like this:
/* header file */
#include <SDL_mixer.h>
#include <memory>
class SDL2_Music {
public:
~SDL2_Music() { free(); }
bool loadMusic(const std::string& path);
bool play(int loops = -1);
// more methods
private:
void free();
Mix_Music* music_ = nullptr;
};
/* cpp file */
void SDL2_Music::free() {
if (music_ != nullptr) {
Mix_FreeMusic(music_);
music_ = nullptr;
}
}
bool SDL2_Music::loadMusic(const std::string& path) {
free();
music_ = Mix_LoadMUS(path.c_str()); // this returns a Mix_Music*
if (music_ == nullptr) {
ktp::logSDLError("Mix_LoadMUS");
return false;
}
return true;
}
// more stuff
This works fine, but I want to get rid of the raw pointer, so I can also get rid of the free() method and the dtor invoking it (yes, I'm reading about the rule of 0). So I made the following changes:
/* header file */
#include <SDL_mixer.h>
#include <memory>
class SDL2_Music {
public:
bool loadMusic(const std::string& path);
bool play(int loops = -1);
// more methods
private:
std::unique_ptr<Mix_Music> music_ = nullptr;
};
/* cpp file */
bool SDL2_Music::loadMusic(const std::string& path) {
music_ = std::make_unique<Mix_Music>(Mix_LoadMUS(path.c_str()));
if (music_ == nullptr) {
ktp::logSDLError("Mix_LoadMUS");
return false;
}
return true;
}
// more stuff
When I try to compile (GCC) I get:
"C:\Users\not_bjarne\CodeBlocks\MinGW\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\unique_ptr.h|831|error:
invalid use of incomplete type 'struct _Mix_Music'"
And codeblocks points me to unique_ptr.h, which I obviously didn't tried to "fix".
It seems that Mix_Music is an incomplete type and the correct way to free a Mix_Music object is to call the Mix_FreeMusic function. You cannot dispose of it the way you would a C++ object, namely by using delete. delete would attempt to call the destructor (which cannot be done in this context, since the type is incomplete here) and would assume that the object was allocated by new and return the memory to the same pool where new got it from. The way SDL allocates the object is an implementation detail, so you must let SDL deallocate the object itself as well, to ensure it is done properly.
std::unique_ptr can be used for this purpose, but requires a custom deleter. The default deleter will call delete, which should not be done here. The error you are seeing is because delete p; is ill-formed (where p has type Mix_Music*) because of the incompleteness. You must ensure that the custom deleter calls Mix_FreeMusic. You can see how to use custom deleters here: How do I use a custom deleter with a std::unique_ptr member?
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;
}
Here is a simple window class (members omitted for brevity):
class window {
public:
window();
window(const std::string& title, const gt::size2d& size, bool visible = true, bool fullscreen = false);
NO_COPY(window);
window(window&& o);
window& operator=(window&& o);
using close_callback = std::function<void()>;
// members omitted ...
private:
struct impl;
struct impl_deleter {
void operator()(impl* impl);
};
std::unique_ptr<impl, impl_deleter> m_pimpl;
close_callback m_close_callback = []() { DD("Close callback"); };
// ...
};
My goal is to call m_close_callback from GLFW window system, and I could implement something like this:
void close_callback_indirection(GLFWwindow* win)
{
gt::window* winptr = static_cast<gt::window*>(glfwGetWindowUserPointer(win));
if (winptr != nullptr) {
winptr->m_close_callback(); // DOES NOT COMPILE
}
}
gt::window::window(const std::string & title, const gt::size2d & size, bool visible, bool fullscreen)
: m_pimpl{ nullptr }, m_close_callback{ []() {} }, m_size_callback{ [](const gt::size2d&) { } }
{
// omitted GLFW and GL initialization here ...
GLFWwindow* win = glfwCreateWindow(size.x, size.y, title.c_str(), nullptr, nullptr);
m_pimpl.reset(new gt::window::impl);
m_pimpl->glfw_win = win;
glfwSetWindowUserPointer(win, this);
glfwSetWindowCloseCallback(win, close_callback_indirection);
// omitted rest ...
}
This, as expected, does not compile with message "'gt::window::m_close_callback': cannot access private member declared in class 'gt::window'".
However if I implement it like this:
gt::window::window(const std::string & title, const gt::size2d & size, bool visible, bool fullscreen)
: m_pimpl{ nullptr }, m_close_callback{ []() {} }, m_size_callback{ [](const gt::size2d&) { } }
{
// omitted GLFW and GL initialization here ...
GLFWwindow* win = glfwCreateWindow(size.x, size.y, title.c_str(), nullptr, nullptr);
m_pimpl.reset(new gt::window::impl);
m_pimpl->glfw_win = win;
glfwSetWindowUserPointer(win, this);
// using lambda instead of function pointer
glfwSetWindowCloseCallback(win, [](GLFWwindow* win) {
gt::window* winptr = static_cast<gt::window*>(glfwGetWindowUserPointer(win));
if (winptr != nullptr) {
// Accessing private member here
winptr->m_close_callback(); // WHY THIS WORKS?
}
});
// omitted rest ...
}
Now it compiles and it works, if I press window close button I can see the debug message.
My understanding is that lambda without capture list can and in this case will be cast to function pointer so I guess that compiler will generate function code somewhere and pass in a pointer to that, but why does it have access to private member of window object? Is the generated function private member of window (or a friend)?
Can I rely on this behavior or is this something that is considered to be undefined?
I am using MSVC++ compiler
Microsoft (R) C/C++ Optimizing Compiler Version 19.16.27026.1 for x86
All lambdas have access to whatever is accessible at the point of their declaration. If you create a lambda in a member function of a class, that lambda can access anything that the member function itself would have access to. Always.
When a captureless lambda is converted to a function pointer, the function referred to by that pointer is identical to the lambda itself. Including its accessibility.
I work on a project made with cocos2d-x framework (c++).
In my Player class, I have to manage the animations.
Iinitially I had this code that worked without any problem:
First, the animation object is a cocos2d Class cocos2d::Animation. Just remember that this object contains a cocos2d::Vector<AnimationFrame*> _frames; member.
Doc: http://www.cocos2d-x.org/reference/native-cpp/V3.5/d3/dc5/classcocos2d_1_1_animation.html#a0fdc0be158df7e09d04644be353db056
class Player : public cocos2d::Sprite {
private:
cocos2d::Map<std::string, cocos2d::Animation*> animations;
cocos2d::Vector<cocos2d::SpriteFrame*> getAnimation(const char *format, int frameStart, int count);
void update(float delta) override;
bool init() override;
public:
static Player* create();
bool init() override;
//...
};
And the implementation side:
bool Player::init() {
//...
animations.insert("idleN", Animation::createWithSpriteFrames(getAnimation("%04d", 207, 9), 0.1));
//...
}
Vector<SpriteFrame*> Player::getAnimation(const char *format, int frameStart, int count) {
auto spriteCache = SpriteFrameCache::getInstance();
Vector<SpriteFrame*> animFrames;
char str[100] = {0};
for (int i = 1; i <= count; i++)
{
sprintf(str, format, frameStart);
log("%s", str);
animFrames.pushBack(spriteCache->getSpriteFrameByName(str));
frameStart++;
}
return animFrames;
}
//later in the code execution
void Player::manageIdle() {
auto idleAnim = Animate::create(animations[0].anim);
runAction(idleAnim);
}
You can see each Animation is contained in cocos2d::Map<std::string, cocos2d::Animation*> and as I say before, this code worked perfectly, no error.
But I needed some more informations in addition to the name and the object itself so I decided to use a structure to store all infos for each animation. And I replaced the cocos2d::Map<std::string, cocos2d::Animation*> by std::vector<animData> with animData as structure. I refactored the code like so:
class Player : public cocos2d::Sprite {
public:
typedef struct animation {
std::string name;
cocos2d::Animation* anim;
//all others info needed, not relevant here, (just several enum type variables)
} animData;
private:
std::vector<animData > animations; //the new container
//rest of code stay unchanged
};
The changes in the implementation side:
bool Player::init() {
//...
animations.push_back({"idleN", Animation::createWithSpriteFrames(getAnimation("%04d", 207, 9), 0.1)});
//no problem here...
}
But now, when I try to create a new anim with a animation saved in my container (vector) I get a SegV on this line:
void Player::manageIdle() {
auto idleAnim = Animate::create(animations[0].anim); //SegV here, in Animate::create() funct
runAction(idleAnim);
}
After search, I find that each structure member anim which is type of cocos2d::Animation*, now conatains a empty cocos2d::Vector<AnimationFrame*> _frames; and there is the problem !
It’s as if they lose the cocos2d::Vector<AnimationFrame*> ref or something like that.
So my question is why cocos2d::Vector<AnimationFrame*> become empty with my refactored code and not whith the previous one ?
I found this with test like that:
auto test = animList[0].anim->getFrames();
if (test.empty()) {
log("empty"); //The test output empty
}
Debugguer screen in the end of the init() funct:
Debugguer screen in Player::manageIdle() funct:
Edit: when I add animations.back().anim->retain(); right after the line to add an element in the vector, it solves the problem !
animations.push_back({"idleN", Animation::createWithSpriteFrames(getAnimation("%04d", 207, 9), 0.1)});
animations.back().anim->retain();
Because cocos2d::Animation* inherit from cocos2d::Ref, it is an auto-release object. When used inside a cocos2d container like cocos2d::Map or cocos2d::Vector, it is auto managed by the container itself. But I use a std::vector so I lose the ref I think. Something like that.
Now I need to find a way to get rid of this additional line of code because this multiple by twice my number of line here !
So new question here: How I can get rid of the fact I have to call animations.back().anim->retain(); each time I add a new element in my vector ?
You might create a wrapper around Ref, which "retains" ownership, and store this wrapper instead, sort of a std::unique_ptr e.g.
template<typename T> class RefOwner {
public:
RefOwner(T *t) : ref(t) {
ref->retain();
}
~RefOwner() {
ref->release();
}
T *operator->() { return ref; }
private:
T *ref;
};
and then use it as
struct animData {
std::string name;
RefOwner<cocos2d::Animation> anim;
//all others info needed, not relevant here, (just several enum type variables)
};
Disclaimer: I have no experience with cocos2d-x, just looked at Animation and Ref