Python with statement in C++
-
20-06-2021 - |
Question
I am trying to implement something similar to the python with statement in C++. As I plan to use it mainly with Qt-OpenGL the methods are called bind
and release
(in python __enter__
, __exit__
).
Code I came up with:
header:
#include <iostream>
#include <vector>
class With
{
public:
class A
{
public:
virtual ~A() { }
};
template <typename T>
class B : public A
{
public:
B(T& _t) : t(_t)
{
t.bind();
}
virtual ~B()
{
t.release();
}
T& t;
};
template <typename... Args>
With(Args&... args)
{
set(args...);
}
~With();
template <typename T, typename... Args>
void set(T& t, Args&... args)
{
set(t);
set(args...);
}
template <typename T>
void set(T& t)
{
a.push_back(dynamic_cast<A*>(new B<T>(t)));
}
std::vector<A*> a;
};
cpp:
With::~With()
{
for (auto it = a.begin(); it != a.end(); ++it)
{
delete *it;
}
}
Usage:
class X
{
public:
void bind() { std::cout << "bind x" << std::endl; }
void release() { std::cout << "release x" << std::endl; }
};
class Y
{
public:
void bind() { std::cout << "bind y" << std::endl; }
void release() { std::cout << "release y" << std::endl; }
};
int main()
{
X y;
Y y;
std::cout << "start" << std::endl;
{
With w(x, y);
std::cout << "with" << std::endl;
}
std::cout << "done" << std::endl;
return 0;
}
Questions:
- Needing
class A
andclass B
feels a bit clumsy. Is there a better alternative? - Are there any draw backs in using
&&
instead of&
? It would make the usage of tempory objects possible (e.g.With w(X(), y);
)
La solution
The with statement is a way to do in python what is already the normal thing in C++. It is called RAII: Resource acquisition is initialization.
In python, when a class object is created, the __init__
method is called (but this is not a strict guarantee). The __del__
method is called by the garbage collector at some point after the object is no longer in use, but it is not deterministic.
In C++ the destructor is called at a well defined point so there is no need for with
.
I suggest you just use something like class B (no need for class A or With).
template <typename T>
class B {
public:
B(T& t) : m_t(t){
m_t.bind();
}
~B() {
m_t.release();
}
T& m_t;
}
use it like this:
{
B<X> bound_x(x); // x.bind is called
B<Y> bound_y(y); // y.bind is called
// use x and y here
} // bound_x and bound_y is destroyed here
// so x.release and y.release is called
Autres conseils
It ships with the language, and it's called RAII.
struct X {
X() { std::cout << "bind\n"; }
~X() { std::cout << "release\n"; }
};
int main() {
X x;
}
One possible solution:
template <typename T>
void with(T *t, std::function<void ()> fn) {
t->bind();
fn();
t->unbind();
}
Usage:
with(object, []() {
// object bound
});