Reputation: 87
I've already encountered many time the case when there's a class, e.g. LogFile
, that has no default constructor, while I want to initialize an instance of it, not on the heap.
Here is an example:
LogFile logfile;
void init(const std::string& path) {
logfile = LogFile(path);
}
The above code will not compile, since LogFile
class has no default constructor.
There are a few workarounds I currently use:
1. Persisting not to make any heap calls manually, storing the object in a list (the list will for sure use the heap, but I won't care about it)
std::list<LogFile> logfile_holder;
void init(const std::string& path) {
logfile_holder.push_back(LogFile(path));
}
2. Using the heap, with a shared pointer/unique pointer:
std::shared_ptr<LogFile> logfile_ptr;
void init(const std::string& path) {
logfile_ptr.reset(new LogFile(path));
}
I there a more standard solution for doing such things?
Even something that looks like the list workaround, but with a template class that is designated specifically for this purpose, without the overhead of the list, would be nice.
Upvotes: 2
Views: 1633
Reputation: 8018
The usual way is to separate a Logger
class and have a Logfile
class which are coupled internally and separate responsibilities:
Logger
class is instantiated when needed and is used to write text formatted logging messagesLogfile
class is responsible for the configuration of the application wide logfile. There will be a static
class member representing the std::ostream
shared with all the Logger
instances.Here's a sketch:
class Logfile {
friend class Logger;
static std::unique_ptr<std::ostream> plogstream_;
public:
void set_logfile(const std::string& logfile_path) {
void set_logstream(std::unique_ptr<std::ostream> logstream);
};
Logfile::plogstream_ = std::unique_ptr<std::ostream>(std::cerr,[](void*){});
class Logger {
public:
operator ostream&()() {
return *Logfile::plogstream_;
}
};
Upvotes: 1
Reputation: 238351
You could use indirection. Use a global pointer, and initialize it with a function local static:
LogFile* logfile_ptr;
void init(const std::string& path) {
static LogFile logfile(path);
logfile_ptr = &logfile;
}
For your suggestions, there is little point in using a list (or other data structure) when you could use a smart pointer if you were to use heap.
Do note that in this design based on yours, you need to pay attention to not use the global logfile until it has been initialized. If unsure, it is easy to check if the pointer is null.
A simpler approach would be to simply add the default constructor. That would allow changing the behaviour of Logfile
if it is used before the path is set. For example, you could terminate the program and describe the error, or you could silently ignore such bugs if you don't care about premature logging that goes missing. Or you could even store logging into a buffer and flush it as soon as the path is finally initialized.
Upvotes: 4
Reputation: 217275
optional
can be an alternative (std
or boost
one depending of your version of compiler):
std::optional<LogFile> logfile;
void init(const std::string& path) {
logfile = LogFile(path);
}
Upvotes: 5