singleton ++?
, .
?
. , , (, , , ).
?
, , . , . , , / , .
, , ?
.
, .
:
auto j1 = jobbie();
auto j2 = jobbie();
auto j3 = jobbie();
j1.log("doh");
j2.log("ray");
j3.log("me");
{
shared_file f;
f.log("hello");
}
{
shared_file().log("goodbye");
}
shared_file().log("here another");
shared_file f2;
{
shared_file().log("no need to reopen");
shared_file().log("or here");
shared_file().log("or even here");
}
f2.log("all done");
where the object jobbieis just the facade for a single element, but the object shared_filewants to reset / close itself when not in use.
therefore, the output should look like this:
doh
ray
me
opening file
logging to file: hello
closing file
opening file
logging to file: goodbye
closing file
opening file
logging to file: here another
closing file
opening file
logging to file: no need to reopen
logging to file: or here
logging to file: or even here
logging to file: all done
closing file
We can achieve this using an idiom that I will call "value-semantics-is-a-facade-for-singleton":
struct jobbie
{
void log(const std::string& s);
private:
struct impl;
static impl& get();
};
struct jobbie::impl
{
void log(const std::string& s) {
std::cout << s << std::endl;
}
};
auto jobbie::get() -> impl& {
static impl _;
return _;
}
void jobbie::log(const std::string& s)
{
get().log(s);
}
struct shared_file
{
shared_file();
void log(const std::string& s);
private:
struct impl;
static std::shared_ptr<impl> get();
std::shared_ptr<impl> _impl;
};
struct shared_file::impl {
struct statics {
std::mutex m;
std::condition_variable cv;
bool still_open = false;
std::weak_ptr<impl> cache;
};
static statics& get_statics() {
static statics _;
return _;
}
impl() {
std::cout << "opening file\n";
}
~impl() {
std::cout << "closing file\n";
auto& stats = get_statics();
auto lock = std::unique_lock<std::mutex>(stats.m);
stats.still_open = false;
lock.unlock();
stats.cv.notify_one();
}
void log(const std::string& s) {
std::cout << "logging to file: " << s << std::endl;
}
};
auto shared_file::get() -> std::shared_ptr<impl>
{
auto& statics = impl::get_statics();
auto lock = std::unique_lock<std::mutex>(statics.m);
std::shared_ptr<impl> candidate;
statics.cv.wait(lock, [&statics, &candidate] {
return bool(candidate = statics.cache.lock())
or not statics.still_open;
});
if (candidate)
return candidate;
statics.cache = candidate = std::make_shared<impl>();
statics.still_open = true;
return candidate;
}
shared_file::shared_file() : _impl(get()) {}
void shared_file::log(const std::string& s) { _impl->log(s); }
auto main() -> int
{
using namespace std;
auto j1 = jobbie();
auto j2 = jobbie();
auto j3 = jobbie();
j1.log("doh");
j2.log("ray");
j3.log("me");
{
shared_file f;
f.log("hello");
}
{
shared_file().log("goodbye");
}
shared_file().log("here another");
shared_file f2;
{
shared_file().log("no need to reopen");
shared_file().log("or here");
shared_file().log("or even here");
}
f2.log("all done");
return 0;
}
source
share