I've been thinking about how to implement the various exception safety guarantee, especially the the strong guarantee, i.e. data is rolled back to it's original state when an exception occurs.
Consider the following, wonderfully contrived examples (C++11 code). Suppose there is a simple data structure storing some value
struct Data
{
int value = 321;
};
and some function modify() operating on that value
void modify(Data& data, int newValue, bool throwExc = false)
{
data.value = newValue;
if(throwExc)
{
// some exception occurs, sentry will roll-back stuff
throw std::exception();
}
}
(one can see how contrived this is). Suppose we wanted to offer the strong exception-safety guarantee for modify(). In case of an exception, the value of Data::value is obviously not rolled back to its original value. One could naively go ahead and try the whole function, setting back stuff manually in appropriate catch block, which is enormously tedious and doesn't scale at all.
Another approach is to use some scoped, RAII helper - sort of like a sentry which knows what to temporarily save and restore in case of an error:
struct FakeSentry
{
FakeSentry(Data& data) : data_(data), value_(data_.value)
{
}
~FakeSentry()
{
if(!accepted_)
{
// roll-back if accept() wasn't called
data_.value = value_;
}
}
void accept()
{
accepted_ = true;
}
Data& data_ ;
int value_;
bool accepted_ = false;
};
The application is simple and require to only call accept() in case of modify() succeeding:
void modify(Data& data, int newValue, bool throwExc = false)
{
FakeSentry sentry(data);
data.value = newValue;
if(throwExc)
{
// some exception occurs, sentry will roll-back stuff
throw std::exception();
}
// prevent rollback
sentry.accept();
}
This gets the job done but doesn't scale well either. There would need to be a sentry for each distinct user-defined type, knowing all the internals of said type.
My question now is: What other patterns, idioms or preferred courses of action come to mind when trying to implement strongly exception safe code?
In general it is called ScopeGuard idiom. It is not always possible to use temporary variable and swap to commit (though it is easy when acceptable) - sometime you need to modify existing structures.
Andrei Alexandrescu and Petru Marginean discuss it in details in following paper: "Generic: Change the Way You Write Exception-Safe Code — Forever".
There is Boost.ScopeExit library which allows to make guard code without coding auxiliary classes. Example from documentation:
void world::add_person(person const& a_person) {
bool commit = false;
persons_.push_back(a_person); // (1) direct action
// Following block is executed when the enclosing scope exits.
BOOST_SCOPE_EXIT(&commit, &persons_) {
if(!commit) persons_.pop_back(); // (2) rollback action
} BOOST_SCOPE_EXIT_END
// ... // (3) other operations
commit = true; // (4) disable rollback actions
}
D programming language has special construct in language for that purpose - scope(failure)
Transaction abc()
{
Foo f;
Bar b;
f = dofoo();
scope(failure) dofoo_undo(f);
b = dobar();
return Transaction(f, b);
}:
Andrei Alexandrescu shows advantages of that language construct in his talk: "Three Unlikely Successful Features of D"
I have made platform dependent implementation of scope(failure) feature which works on MSVC, GCC, Clag and Intel compilers. It is in library: stack_unwinding. In C++11 it allows to achieve syntax which is very close to D language. Here is Online DEMO:
int main()
{
using namespace std;
{
cout << "success case:" << endl;
scope(exit)
{
cout << "exit" << endl;
};
scope(success)
{
cout << "success" << endl;
};
scope(failure)
{
cout << "failure" << endl;
};
}
cout << string(16,'_') << endl;
try
{
cout << "failure case:" << endl;
scope(exit)
{
cout << "exit" << endl;
};
scope(success)
{
cout << "success" << endl;
};
scope(failure)
{
cout << "failure" << endl;
};
throw 1;
}
catch(int){}
}
Output is:
success case:
success
exit
________________
failure case:
failure
exit
The usual approach is not to roll back in case of an exception, but to commit in case of no exception. That means, do the critical stuff first in a way that does not necessarily alter program state, and then commit with a series of non-throwing actions.
Your example would be done like follows then:
void modify(Data& data, int newValue, bool throwExc = false)
{
//first try the critical part
if(throwExc)
{
// some exception occurs, sentry will roll-back stuff
throw std::exception();
}
//then non-throwing commit
data.value = newValue;
}
Of course RAII plays a major role in exception safety, but it's not the only solution.
Another example for "try-and-commit" is the copy-swap-idiom:
X& operator=(X const& other) {
X tmp(other); //copy-construct, might throw
tmp.swap(*this); //swap is a no-throw operation
}
As you can see, this sometimes comes at the cost of additional actions (e.g. if C's copy ctor allocates memory), but that's the price you have to pay some times for exceptionsafety.
I found this question when faced with the case at the end.
If you want to ensure the commit-or-rollback semantics without using copy-and-swap I would recommend providing proxies for all objects and using the proxies consistently.
The idea would be to hide the implementation details and limit the operations on the data to a sub-set that can be rolled back efficiently.
So the code using the data-structure would be something like this:
void modify(Data&data) {
CoRProxy proxy(data);
// Only modify data through proxy - DO NOT USE data
... foo(proxy);
...
proxy.commit(); // If we don't reach this point data will be rolled back
}
struct Data {
int value;
MyBigDataStructure value2; // Expensive to copy
};
struct CoRProxy {
int& value;
const MyBigDataStructure& value2; // Read-only access
void commit() {m_commit=true;}
CoRProxy(data&d):value(d.value),value2(d.value2),
m_commit(false),m_origValue(d.value){;}
~CoRProxy() {if (!m_commit) std::swap(m_origValue,value);}
private:
bool m_commit;
int m_origValue;
};
The main point is that the proxy restricts the interface to data to operations that the proxy can roll back, and (optionally) provides read-only access to the rest of the data. If we really want to ensure that there is no direct access to data we can send the proxy to a new function (or use a lambda).
A similar use-case is using a vector and rolling back push_back in case of failure.
template <class T> struct CoRVectorPushBack {
void push_back(const T&t) {m_value.push_back(t);}
void commit() {m_commit=true;}
CoRVectorPushBack(std::vector<T>&data):
m_value(data),m_origSize(data.size()),m_commit(false){;}
~CoRVectorPushBack() {if (!m_commit) value.resize(m_origSize);}
private:
std::vector<T>&m_value;
size_t m_origSize;
bool m_commit;
};
The downside of this is the need for making a separate class for each operation.
The upside is that the code using the proxies is straightforward and safe (we could even add if (m_commit) throw std::logic_error(); in push_back).
Related
I have a class complicated which features various setters that modify some internal state. That internal state modification is potentially expensive, so I want to do it not too often. In particular, if several setters are invoked in immediate succession, I want to perform the expensive update of the internal state only once after the last of these setter invocations.
I have solved (or "solved"?) that requirement with a proxy. The following would be a minimal working code example:
#include <iostream>
class complicated
{
public:
class proxy
{
public:
proxy(complicated& tbu) : to_be_updated(&tbu) {
}
~proxy() {
if (nullptr != to_be_updated) {
to_be_updated->update_internal_state();
}
}
// If the user uses this operator, disable update-call in the destructor!
complicated* operator->() {
auto* ret = to_be_updated;
to_be_updated = nullptr;
return ret;
}
private:
complicated* to_be_updated;
};
public:
proxy set_a(int value) {
std::cout << "set_a" << std::endl;
a = value;
return proxy(*this);
}
proxy set_b(int value) {
std::cout << "set_b" << std::endl;
b = value;
return proxy(*this);
}
proxy set_c(int value) {
std::cout << "set_c" << std::endl;
c = value;
return proxy(*this);
}
void update_internal_state() {
std::cout << "update" << std::endl;
expensive_to_compute_internal_state = a + b + c;
}
private:
int a;
int b;
int c;
int expensive_to_compute_internal_state;
};
int main()
{
complicated x;
x.set_a(1);
std::cout << std::endl;
x.set_a(1)->set_b(2);
std::cout << std::endl;
x.set_a(1)->set_b(2)->set_c(3);
}
It produces the following output which is looking like exactly what I wanted:
set_a
update
set_a
set_b
update
set_a
set_b
set_c
update
My questions are: Is my approach legit/best practice?
Is it okay to rely on temporary objects (i.e. the proxy objects which are returned) which will be destroyed at the semicolon?
I'm asking because I have a bad feeling about this for some reason. Maybe my bad feeling comes just from Visual Studio's warning which says:
Warning C26444 Avoid unnamed objects with custom construction and
destruction (es.84).
But maybe/hopefully my bad feelings are unjustified and that warning can just be ignored?
What bothers me the most: Is there any case in which the update_internal_state method will NOT be called (maybe by misusing my class or by some compiler optimization or whatever)?
Lastly: Is there any better approach to implement what I try to achieve with modern C++?
I think your solution is legit, but it has a drawback that it hides from the user of the code, that the update is expensive, so one will more likely write:
x.set_a(1);
x.set_b(2);
than
x.set_a(1)->set_b(2);
I would suggest make setters private and add a friend transaction class, so that modifying object would look like:
complicated x;
{
transaction t(x);
t.set_a(1);
t.set_b(2);
// implicit commit may be also done in destructor
t.commit();
}
If transaction will be the only way to modify complicated - users will more tend to call several setters in a one transaction.
The danger I see here is if your class has any methods that do not return a proxy (or any public members). You disable the update call if operator-> of the proxy is used (which yields the complicated), but this is only safe if that usage of operator-> always yields another proxy object which will take over the updating task. This seems like a huge pitfall for anybody who modifies the class later on.
I think it would be safer if complicated were to keep track of the number of alive proxy objects created on it so that the last proxy to be destroyed performs the update call.
Following Dmitry Gordon's argument of people selecting 'wrong' approach, you might have the matter a bit simpler, though (especially from user's view):
class Demo
{
int m_x
int m_y; // cached value, result of complex calculation
bool m_isDirtyY;
public:
int x() { return m_x; }
void x(int value) { m_x = value; m_isDirtyY = true; }
int y()
{
if(m_isDirtyY)
{
// complex calculations...
m_y = result;
m_isDirtyY = false;
}
return m_y;
}
};
This way, you'll only ever execute the calculations on need, no additional extensions like the proxy objects or explicit transactions.
Depending on your needs, you might perhaps encapsulate this pattern in a separate (template?) class, perhaps receiving an updater object (lambda?) or with a pure virtual update function to repeat less code.
Side note: Setting a value might invalidate more than one cached value – no problem, set more than one dirty flag to true then...
I have a custom, generalized serialization system written in C++ where I've handled intrinsics, std::string and structures containing those. However, for a memory stream class containing a std::vector<byte>, I'd like to make it possible to store and retrieve a std::shared_ptr<T> inside of it (where T is any class that derives from Abstract). Of course, I'd like a solution without using Boost as it would defeat my intent.
As stated on http://en.cppreference.com/w/cpp/memory/shared_ptr :
Constructing a new shared_ptr using the raw underlying pointer owned by another shared_ptr leads to undefined behavior.
The only (hacky) solution I have come up so far is for the binary memory stream class having a small lookup table of std::shared_ptr<Abstract> referenced by the raw pointer itself, making it fairly trivial to read and write them out, and ownership/reference count would be reliable. Then it becomes possible/useful to serialize the raw pointer.
However, ownership/reference count is not of concern as it's guaranteed for the use case. If there is a solution that would only use the std::vector<byte>, I would consider it a more elegant approach as it could provide other use cases.
Since your serialization/deserialization process happens in the same process (i.e. the same memory space) then you can store the raw memory pointers as the binary data in your stream. Consider the idea below, written as a trivial demo.
Unfortunately, std::enable_shared_from_this does not allow to increment/decrement manually the reference counter because it is just storing a weak reference, that is not able to destroy the object on ref == 0 internally. That is why we have to make a manual reference management, specifically for the instances in the byte stream.
class Abstract : public std::enable_shared_from_this<Abstract> {
public:
Abstract() : _count(0) {}
~Abstract() { cout << "I am destoryed" << endl; }
void incrementStreamRef() {
std::lock_guard<std::mutex> lock(_mutex);
if (!_count) {
_guard = this->shared_from_this();
}
++_count;
};
void decrementStreamRef() {
std::lock_guard<std::mutex> lock(_mutex);
if (_count == 0)
return;
if (_count == 1) {
if (_guard.use_count() == 1) {
// After this call `this` will be destroyed
_guard.reset();
return;
}
_guard.reset();
}
--_count;
};
private:
std::mutex _mutex;
std::shared_ptr<Abstract> _guard;
std::size_t _count;
};
void addAbstractToStream(std::vector<uint8_t>& byteStream, Abstract* abstract) {
abstract->incrementStreamRef();
auto offset = byteStream.size();
try {
// 1 byte for type identification
byteStream.resize(offset + sizeof(abstract) + 1);
byteStream[offset]
= 0xEE; // Means the next bytes are the raw pointer to an Abstract instance
++offset;
// Add the raw pointer to the stream
// prealocate memory here
// byteStream.push_back(....;
// ....
} catch (...) {
abstract->decrementStreamRef();
return;
}
std::memcpy(byteStream.data() + static_cast<std::ptrdiff_t>(offset),
(void*)&abstract,
sizeof(abstract));
}
void removeAbstractFromStream(std::vector<uint8_t>& byteStream, std::size_t offset) {
Abstract* abstract;
std::memcpy((void*)&abstract,
byteStream.data() + static_cast<std::ptrdiff_t>(offset),
sizeof(abstract));
abstract->decrementStreamRef();
}
void tryMe(std::vector<uint8_t>& byteStream) {
// Must not be destoryed when we leave the scope
auto abstract = std::make_shared<Abstract>();
addAbstractToStream(byteStream, abstract.get());
cout << "Scope is about to be left" << endl;
}
int main() {
// Always walk over the stream and use `removeAbstractFromStream`
std::vector<uint8_t> byteStream;
// `try` to always clean the byte stream
// Of course RAII is much better
try {
// Do some work with the stream
} catch (...) {
removeAbstractFromStream(byteStream, 1);
throw;
}
tryMe(byteStream);
cout << "Main is about to be left" << endl;
removeAbstractFromStream(byteStream, 1);
cout << "Main is even closer to be left" << endl;
return 0;
}
Of course, more elaborate locking could be fine, or discarded at all if the thread-safety is not a concern. Please, revise the code for corner cases before using in production.
I used to logging in C with variable amount of arguments and formatting, and I wanna how Can I meet this in C++.
Through Q&A like this (How to make a variadic macro for std::cout?), I know how to handle variable amount. But what I still do not know is, how to format, cause I can not use methods like 'setbase' between arguments now.
For example:
// in C
#define err(fmt, ...) (printf("[%s] "fmt"\n", __FUNCTION__, ##__VA_ARGS__))
#define FATAL(fmt, ...) do{\
err(fmt, ##__VA_ARGS__);\
CLEAN_UP;\
exit(1);\
}while(0)
int main(){
if(1) FATAL("Just a test: 0x%lX, %d", 1, 2);
return 0;
}
"FATAL" here, accept variable amount of arguments with formatting, print them, and do some extra. I have no idea how to declare such a "FATAL" in C++.
You can achieve that by using the operator<< and a custom destructor on an ad-hoc logging object.
class log_error
{
public:
log_error() = default;
log_error(log_error&& other) = default;
~log_error()
{
// Do whatever you want with the input
// Add a timestamp, process/thread id
// Write it to a file, send it to a server ...
std::cerr << "[ERROR] " << ss.str() << std::endl;
throw std::runtime_error(ss.str());
}
std::stringstream ss;
};
template<typename T>
log_error operator<<(log_error&& le, const T& t)
{
le.ss << t;
return std::move(le);
}
I only included the essentials for basic usage. For more complex usage you want to consider a copy variant of the ctor / operator<<.
The usage is very idiomatic C++. But you have to remember the ():
log_error() << "Ooops " << 23 << ", 0x" << std::setbase(16) << 23;
This line will print out the message and throw an exception.
You can customize this however you want. Write to logfiles, add timestamps or other helpful information, verbosity levels and thresholds. It is even possible to have most cases completely optimized out in production builds.
Live example
C++ is not C! While you can use C-style (and often C) code this is not advisable. Firstly you should not normally rely on macros as they violate the type system, use (possibly inlined or constexpr) functions instead. Then you should not use C-style error handling technique, use exceptions instead. I'd also recommend against variadic arguments in general and finally you don't need C-style string formatting techniques -> this is C++, use stringstreams to format your code.
In your particular case I'd do something like this:
#include <exception>
#include <iostream>
#include <sstream>
#include <string>
inline void fatal(std::string msg) {
// clean_up
throw std::runtime_error(msg);
}
int main(){
std::ostringstream msg;
msg << "Just a test: " << 1 << 2;
if(1) fatal(msg.str());
return 0;
}
I also have to point out that C++ and C are two different languages with different patterns and idioms. C++ has better alternatives for many C constructs which are more type-safe and thus preferable. IN your case, I would throw an exception in this case. If you ban catch(...) in your code, it will terminate your program. When the exception is propagated, the compiler will also call destructors of objects and thus do clean-up. If you haven't, I recommend you read up on resource-acquisition-is-initialization (RAII). Since it looks like you are transitioning from C to C++, I recommend to read the tour of C++ which shows fundamental C++ principles. For RAII, the gist is to manage resources in special handler objects which allocate in the constructor and deallocate in the destructor, and implement move semantics. This way, you cannot leak resources. Example implementations are std::vector, std::unique_ptr or std::iostream. As another example, consider mutex locking/unlocking:
class Mutex {
public:
void lock() { ... }
void unlock() { ... }
};
When you use it, it easy to forget unlocking in your code, especially when making modifications to existing code. Also, in case of exceptions, you need try/catch blocks to unlock all the time. Instead, define a MutexLocker class:
class MutexLocker
{
public:
MutexLocker(std::mullptr_t) = delete;
MutexLocker(Mutex* m): mutex_(m) {mutex_->lock();}
MutexLocker(MutexLocker const&) = delete;
MutexLocker& operator=(MutexLocker const&) = delete;
MutexLocker(MutexLocker&& l): mutex_(l.mutex_) {l.mutex_ = nullptr;}
MutexLocker& operator=(MutexLocker&& l)
{
mutex_ = l.mutex_,
l.mutex_ = nullptr;
return *this;
}
~MutexLocker() {if (mutex_) {mutex_->unlock()} };
private:
Mutex* mutex_;
};
Now, you can never forget to unlock a Mutex. The MutexLocker object cannot be copied, but you can transfer ownership. This is superior to anything you can do in C.
For formatting output, you can google "variadic template printf" which should give you some examples, e.g. on Wikipedia:
void printf(const char *s)
{
while (*s) {
if (*s == '%') {
if (*(s + 1) == '%') {
++s;
}
else {
throw std::runtime_error("invalid format string: missing arguments");
}
}
std::cout << *s++;
}
}
template<typename T, typename... Args>
void printf(const char *s, T value, Args... args)
{
while (*s) {
if (*s == '%') {
if (*(s + 1) == '%') {
++s;
}
else {
std::cout << value;
s += 2; // this only works on 2 characters format strings ( %d, %f, etc ). Fails miserably with %5.4f
printf(s, args...); // call even when *s == 0 to detect extra arguments
return;
}
}
std::cout << *s++;
}
}
Or you can use a library, e.g. boost::format or probably thousands of other implementations. If it is only for logging, you could take a look at a logging framework, e.g. boost.log.
First, even it often leads to harder to maintain code, you call always use C techniques in C++. stdio.h functions work natively in C++ and almost all macro are translated the same.
If you want to make use of c++ goodies (better type control at compile time)... you will have to forget old C variadic functions, notably all xprintf. There may be one interesting part anyway with templates.
Anyway the example given in the referenced Q&A is all you need here. Formatting instructions are simply injected in streams the same values are.
But here is a C++11 example showing that you can do what you want without using any macro. It is much longer than the C macro version, but it looks form me much more clear and extensible without the ugly do { ... } while 0 idom:
#include <iostream>
#include <string>
// disp is a variadic templated function injecting any arguments to a stream
// version for one single arg
template <typename T>
void disp(std::ostream& out, T arg) {
out << arg;
}
// recursively displays every arg
template <typename T, typename ... U>
void disp(std::ostream& out, T arg, U ... args) {
disp(out, arg) ;
disp(out, args...);
}
/* fatal displays its args to std::cout, preceded with "FATAL " and followed
* by a newline.
* It then does some cleanup and exits
*/
template<typename ... T>
void fatal(T ... args) {
std::cout << "FATAL ";
disp(std::cout, args...);
std::cout << std::endl;
// cleanup
exit(1);
}
int main() {
int i = 21;
int j = 32;
std::string s = "foo";
if(1) fatal(1, " " , s, " ab ", i, " 0x", std::hex, j);
return 0;
}
output is
FATAL 1 foo ab 21 0x20
Last but not least, you'd better use a throw FatalException() where FatalException is a subclass of std::exception instead of directly use exit(1). You could even write to a stringstream and pass the resulting string to the exception instead of writing to a real stream, but then you should be prepared to deal with bad_alloc exceptions.
I've been trying to fully digest the undo pattern demoed in Sean Parent's talk "Inheritance Is The Base Class of Evil". The talk covers a number of bases, including C++ move semantics, and the use of concepts to implement polymorphism instead of inheritance, but the delta-undo storage pattern is the one I've been trying to get my head around. Here is a working adaptation of the example Parent gave in his talk:
#include <iostream>
#include <memory>
#include <vector>
#include <assert.h>
using namespace std;
template <typename T>
void draw(const T& x, ostream& out, size_t position)
{
out << string(position, ' ') << x << endl;
}
class object_t {
public:
template <typename T>
object_t(T x) : self_(make_shared<model<T>>(move(x))) {}
friend void draw(const object_t& x, ostream& out, size_t position)
{ x.self_->draw_(out, position); }
private:
struct concept_t {
virtual ~concept_t() = default;
virtual void draw_(ostream&, size_t) const = 0;
};
template <typename T>
struct model : concept_t {
model(T x) : data_(move(x)) { }
void draw_(ostream& out, size_t position) const
{ draw(data_, out, position); }
T data_; };
shared_ptr<const concept_t> self_;
};
// The document itself is drawable
using document_t = vector<object_t>;
void draw(const document_t& x, ostream& out, size_t position)
{
out << string(position, ' ') << "<document>" << endl;
for (const auto& e : x) draw(e, out, position + 2);
out << string(position, ' ') << "</document>" << endl;
}
// An arbitrary class
class my_class_t {
/* ... */
};
void draw(const my_class_t&, ostream& out, size_t position)
{ out << string(position, ' ') << "my_class_t" << endl; }
// Undo management...
using history_t = vector<document_t>;
void commit(history_t& x) { assert(x.size()); x.push_back(x.back()); }
void undo(history_t& x) { assert(x.size()); x.pop_back(); }
document_t& current(history_t& x) { assert(x.size()); return x.back(); }
// Usage example.
int main(int argc, const char * argv[])
{
history_t h(1);
current(h).emplace_back(0);
current(h).emplace_back(string("Hello!"));
draw(current(h), cout, 0);
cout << "--------------------------" << endl;
commit(h);
current(h).emplace_back(current(h));
current(h).emplace_back(my_class_t());
current(h)[1] = string("World");
draw(current(h), cout, 0);
cout << "--------------------------" << endl;
undo(h);
draw(current(h), cout, 0);
return EXIT_SUCCESS;
}
Instead of tracking undo as a stack of commands which capture their before and after states, this pattern tracks undo states as a stack of "whole documents" where every entry is, in effect, a complete copy of the document. The trick of the pattern is that, storage/allocations are only incurred for the portions of the document which are different between each state, using some indirection and shared_ptr. Each "copy" only incurs the storage penalty for what's different between it and the prior state.
The pattern in Parent's example shows that the "current" document is completely mutable, but gets committed to the history when you call commit on the history. This pushes a "copy" of the current state onto the history.
In the abstract, I find this pattern compelling. The example Parent presents in this talk was clearly contrived primarily to demonstrate his points about concept-based polymorphism and move semantics. Relative to those, the undo pattern feels ancillary, although I think its role is to point out the value of value semantics.
In the example, the document "model" is just "a vector of objects conforming to the concept". That served it's purpose for the demo, but I'm finding it hard to extrapolate from "vector of concepts" to "real world, typed model." (Let's just say that for the purposes of this question the concept-based polymorphism is not relevant.) So, for instance, consider the following trivial model where the "document" is a company with some number of employees, each with a name, a salary, and a picture:
struct image {
uint8_t bitmapData[640 * 480 * 4];
};
struct employee {
string name;
double salary;
image picture;
};
struct company {
string name;
string bio;
vector<employee> employees;
};
The question I have is: How can I introduce the indirection necessary to get the storage sharing without losing the ability to interact with the model directly and simply? By simplicity of interaction, I mean that you should be able to continue to interact with the model in a straightforward manner without lots of RTTI or casting, etc. For instance, if you were trying to give everyone named "Susan" a 10% raise, capturing an undo state after every change, a simple interaction might look something like this:
using document_t = company;
using history_t = vector<document_t>;
void commit(history_t& x) { assert(x.size()); x.push_back(x.back()); }
void undo(history_t& x) { assert(x.size()); x.pop_back(); }
document_t& current(history_t& x) { assert(x.size()); return x.back(); }
void doStuff()
{
history_t h(1);
for (auto& e : current(h).employees)
{
if (e.name.find("Susan") == 0)
{
e.salary *= 1.1;
commit(h);
}
}
}
The trick seems to be injecting the indirection provided by object_t, but it's not clear how I can both introduce the necessary indirection and subsequently traverse that indirection transparently. I can generally get around in C++ code, but it's not my everyday language, so this could very well be something dead simple. Regardless, it's unsurprising that Parent's example doesn't cover this since a large part of his point was the ability to hide the type through the use of concepts.
Anyone have any thoughts on this?
While the document is mutable, the objects are not.
In order to edit an object, you need to create a new one.
In a practical solution, each object might be a cooy on write smart pointer holder that you can access either by reading or writing. A write access duplicates the object iff it has a reference count above one.
If you are willing to restrict all mutation to accessor methods, you can do the copy on write in them. If not, the get_writable method does the copy on write. Note that a modification usually implies a modification all the way back to the root as well, so your write method may need to take a path to the root where the copy on write is propagated up to there. Alternatively, you can use a document context and guid equivalent identifiers and a hash map, so editing foo contained in bar leaves bar unchanged, as it identifies foo by its name not a pointer.
I'll open by saying I've been looking into this for a few days, trying to grok what is the "right way" of doing it. After plenty of googling on RAII/pool design/smart pointers, and having come to no definite conclusion (except that maybe there is no absolute "right way"), I thought maybe it's time for someone more knowledgeable to point me in the right direction.
I'm building an object pool, and I'm trying to make sure the client code can use RAII, if desired.
There are 3 entities involved:
Resource. Expensive to build, semi-cheap to reuse (checking its state on release is not trivial). Difficult to copy, as it wraps some C constructs, with their own allocated memory/resources.
Wrapper/Handle. Wrapper for the Resource. Is given a Resource on ctor, releases it on dtor.
Controller/Pool. Maintains a pool of Resources and manages their use by clients, via Wrapper or, at the client's discretion, directly.
I present below a simplified example of what I've come up with. In the function DoSomethingElse(), you can see what I'm after - I get a reference to a Wrapper and, at the end of the scope, its dtor is invoked, and the Resource is released back to the pool.
My question has to do with the definition of Factory::GetResource(). The simplified version presented here just allocates a new one every time; my actual implementation checks the pool for an available Resource (creates one if there's none available), marks it as in use, and returns a reference to it.
I'd rather avoid having to define a proper copy ctor for the Resource, hence the return by reference, instead of by value. The Resource is guaranteed to outlive the caller, and the Controller maintains ownership throughout the app's life - they're not handed to the client code for life-cycle management. Of course, if the client asks for a direct reference, i.e., without the Wrapper, all bets are off.
Is this design sound? Would I be better off using shared_ptr? Or some other mechanism/design?
Thanks for your time.
#include <iostream>
#include <vector>
using namespace std;
static int seq = 0; // POOR MAN'S SEQUENCE FOR INSTANCE IDs
class Resource
{
public:
Resource() : id(seq++) { cout << "Resource ctor: " << id << endl; }
~Resource() { cout << "Resource dtor: " << id << endl; }
private:
int id;
};
class Wrapper
{
public:
// ON ACTUAL IMPLEMENTATION, NOTIFY THE CONTROLLER OF THE RELEASE
~Wrapper()
{ cout << "Wrapper dtor: " << id << "Welease Bwian! Ee, I mean, the wesouwce" << endl; }
explicit Wrapper(Resource& r) : id(seq++), res(r)
{ cout << "Wrapper ctor: " << id << endl; }
int getID() const { return id; }
private:
int id;
Resource& res;
};
class Controller
{
public:
~Controller() { for (auto r : allres) delete r; }
Resource& GetResource();
private:
// SIMPLIFIED. I'M USING Boost PTR CONTAINER
vector<Resource *> allres;
};
// SIMPLIFIED. IT WOULD ACTUALLY GET A RESOURCE FROM THE POOL
Resource& Controller::GetResource()
{
Resource* newres = new Resource();
allres.push_back(newres);
return *(newres);
}
// SIMULATE GLOBAL CONTEXT
Controller& GetController()
{
static Controller f;
return f;
}
void DoSomething(Wrapper& wr)
{
cout << "DoSth INI" << endl;
cout << wr.getID() << endl;
cout << "DoSth END" << endl;
}
void DoSomethingElse()
{
cout << "DoSthElse INI" << endl;
Wrapper w(GetController().GetResource());
DoSomething(w);
cout << "DoSthElse END" << endl;
}
int main(int argc, char *argv[])
{
cout << "main INI" << endl;
cout << "Calling DoSthElse" << endl;
DoSomethingElse();
cout << "Called DoSthElse" << endl;
cout << "main END" << endl;
}
RAII is really about ownership. Who owns the object, and what do they need to do once they relinquish ownership of it?
The situation you're describing is that the resources are really owned by the Controller. The lifetimes of the resource objects are managed by the Controller.
Users of the resources effectively just "lock" the resource, marking it as "in use", but they don't take ownership of it. They don't affect its lifetime. (You could say that they own a lock, and then that is the resource that they need to manage)
So I'd suggest exposing something like a std::unique_ptr<Resource>, which is created with a custom deleter. (and which can be returned by value from the controller.getResource() call
Users can do with this unique_ptr what they like: it's not copyable, but it can be moved, and once it goes out of scope, it calls its custom deleter, which marks it as "unused" in the Controller, effectively returning it to the pool
That way you get to return an object by value, which is nice and simple to work with for the client, and you avoid exposing the "unwrapped" Resource objects at all: clients always get them wrapped in a unique_ptr, which eliminates a lot of potential errors.
Note that in your current code there isn't a way for the Controller to tell when a caller is done using a Wrapper/Resource. This means that when you get around to implementing Controller::GetResource there won't be any way for the controller to know whether it can return a previously created Resource or not.
Usually designs like this include a Controller::ReleaseResource that is called in the Wrapper destructor. This means that when the Wrapper is constructed it gets a resource, then when it is destructed it releases the resource. This is exactly RAII.