I'm trying to compile a simple example of using a little bridge template externally_locked that is enable to control access to a BankAccount only after locking its parent AccountManager object. (refer to boost synchronization)
#include <boost/concept_check.hpp>
template <typename T, typename Lockable>
// Use a little bridge template externallly_locked that controls access to a BankAccount
class externally_locked {
// This macro is used to check that a given template parameter meets certain requirements of
// has certain properties
BOOST_CONCEPT_ASSERT((boost::LockableConcept<Lockable>));
public:
externally_locked(T & obj, Lockable & lockable): obj_(obj), lockable_(lockable) {}
externally_locked(Lockable& lockable): obj_(), lockable_(lockable) {}
void set(const T& obj, Lockable& lockable) {
obj_ = obj;
lockable_ = lockable;
}
private:
T obj_;
Lockable & lockable_;
};
Then get the following error:
root@34b558e548b5:/mnt/boost_threads# g++ -ggdb -pedantic -Wall -Werror -O0 --save-temps bankmanager.cpp -o bankmanager
bankmanager.cpp:8:90: error: '*' cannot appear in a constant-expression
bankmanager.cpp:8:91: error: a call to a constructor cannot appear in a constant-expression
bankmanager.cpp:8:4: error: template argument 1 is invalid
BOOST_CONCEPT_ASSERT((boost::LockableConcept<Lockable>));
^
bankmanager.cpp:8:13: error: template argument 1 is invalid
BOOST_CONCEPT_ASSERT((boost::LockableConcept<Lockable>));
What needs to be fixed?
The name of the concept is not up-to-date with the Boost Thread source code. It probably needs to be BasicLockable
(or any more specific [standard] or extended concept, if applicable).
To avoid confusion, let's rename the macro parameter:
template <typename T, typename LockableT>
// Use a little bridge template externallly_locked that controls access to a BankAccount
class externally_locked {
// This macro is used to check that a given template parameter meets certain requirements of
// has certain properties
BOOST_CONCEPT_ASSERT((boost::BasicLockable<LockableT>));
public:
externally_locked(T& obj, LockableT& lockable) : obj_(obj), lockable_(lockable) {}
externally_locked(LockableT& lockable) : obj_(), lockable_(lockable) {}
void set(T const& obj, LockableT& lockable) {
obj_ = obj;
lockable_ = lockable;
}
private:
T obj_;
LockableT& lockable_;
};
See it Live On Coliru
#include <boost/concept_check.hpp>
#include <boost/thread.hpp>
#include <boost/thread/lockable_adapter.hpp>
#include <boost/thread/lockable_concepts.hpp>
#include <boost/thread/strict_lock.hpp>
static bool some_condition() { return rand() % 2; }
// Use a little bridge template externallly_locked that controls access to a
// BankAccount
template <typename T, typename LockableT> class externally_locked {
// This macro is used to check that a given template parameter meets
// certain requirements of has certain properties
BOOST_CONCEPT_ASSERT((boost::BasicLockable<LockableT>));
public:
externally_locked(T& obj, LockableT& lockable) : obj_(obj), lockable_(lockable) {}
externally_locked(LockableT& lockable) : obj_(), lockable_(lockable) {}
T& get(boost::strict_lock<LockableT>& lock) {
if (!lock.owns_lock(&lockable_))
throw boost::lock_error(); // run time check throw if not matching locks
return obj_;
}
void set(T const& obj, LockableT& lockable) {
obj_ = obj;
lockable_ = lockable;
}
private:
T obj_;
LockableT& lockable_;
};
class BankAccount {
int balance_;
public:
void Deposit(int amount) { balance_ += amount; }
void Withdraw(int amount) { balance_ -= amount; }
};
class AccountManager : public boost::basic_lockable_adapter<boost::mutex> {
public:
using lockable_base_type = basic_lockable_adapter<boost::mutex>;
AccountManager() : checkingAcct_(*this), savingsAcct_(*this) {}
inline void Checking2Savings(int amount);
inline void AMoreComplicatedChecking2Savings(int amount);
private:
externally_locked<BankAccount, AccountManager> checkingAcct_;
externally_locked<BankAccount, AccountManager> savingsAcct_;
};
void AccountManager::Checking2Savings(int amount) {
boost::strict_lock<AccountManager> guard(*this);
checkingAcct_.get(guard).Withdraw(amount);
savingsAcct_.get(guard).Deposit(amount);
}
int main() {
AccountManager mgr;
mgr.Checking2Savings(999);
}
To also make AccountManager::AMoreComplicatedFunction
work you need quite a bit more plumbing as explained in the tutorial