Search code examples
c++boostsynchronizationboost-thread

Compile error while using boost::concept_check to check a template argument


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?


Solution

  • 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