I have the following code:
MyType x = do_something_dangerous();
// ...
if (some_condition) {
// ...
bar(x);
}
else {
// ...
}
// ...
if (another_condition_which_may_depend_on_previous_if_else} {
// ...
baz(x);
}
The idea is that in some cases, which are perhaps difficult/inconvenient to determine in advance, I need to use x
. But in the cases I don't need to use it, trying to initialize it may be bad (say, could crash my process).
Now, it seems like what I need to be using is an initialize-on-demand holder (the link focuses on Java, so here's a sketch): Some kind of Wrapper<MyType>
or Wrapper<MyType, do_something_dangerous>
with a get()
method, such that the first get()
calls do_something_dangerous()
and later get()
s just pass the value the first call obtained.
Notes:
boost::optional
, but that would be a bit cumbersome and also twist the intended use: "It is recommended to use optional<T>
in situations where there is exactly one, clear (to all parties) reason for having no value of type T
, and where the lack of value is as natural as having any regular value of T
."IMHO, the solution you propose is perfectly appropriate :
Of course, if you are already using boost in your application you can look at the suggested boost::optional module, but I'm not sure it is exactly what you want as it is more designed for nullable objects when what you need is defered initialization.
My advice here is : stick to a dedicated wrapper implementation.
Unlike other answers, I think you should not use a singleton but simply something like (implementation using an initialization parameter) :
template<typename T, typename I>
class LazyInit {
T* val;
I init;
public:
LazyInit<T, I>(I init): init(init) {
val = NULL;
}
~LazyInit<T, I>() {
delete val;
val = NULL; // normally useless here
}
T& get() {
if (val == NULL) {
val = new T(init);
}
return *val;
}
};
And here is a implementation using an initialization function :
template<typename T>
class LazyInit {
T* val;
T (*init)();
public:
LazyInit<T>(T (*init)()): init(init) {
val = NULL;
}
~LazyInit<T>() {
delete val;
val = NULL; // normally useless here
}
T& get() {
if (val == NULL) {
val = new T(init());
}
return *val;
}
};
...
LazyInit<MyType> x(do_something);
...
bar(x.get()); // initialization done only here
You could easily combine both to build an implementation using a function taking a parameter.
If you have c++11 you can use a combination of std::async
and std::shared_future
:
#include <iostream>
#include <future>
using namespace std;
struct LazyObj {
LazyObj() {
cout << "LazyObj init" << endl;
}
void test() {
cout << "LazyObj test" << endl;
}
};
int main() {
auto x = std::async(launch::deferred, []() -> LazyObj& {
static LazyObj a;
return a;
}).share();
int cond=0;
if( cond == 0 ) {
x.get().test();
x.get().test();
}
return 0;
}
In this use-case launch::deferred
points std::async
not to create execution thread and call the lambda on-demand when get()
is called. To allow multiple calls of get()
method, we convert std::future
to std::shared_future
using share()
method. Now we able to get lazy-initialisaion object when or where it needed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With