Boost logo

Boost :

Subject: [boost] A PP trick to define a synchronized block java-like macro
From: vicente.botet (vicente.botet_at_[hidden])
Date: 2009-01-20 15:46:01


In Boost.Log we can see how to define a macro that preserv the osream syntax but that logs conditionally.

LOG << "Ah" << std::end;

We use here the following trick: let the sentence generated by the macro is unterminated and form a correct sentence together with what follows the macro call.

#define LOG if (!log::enabled()); else log::os

I have recently see in [1] a trick allowing the declaration of a variable local to a block scope but OUT OF THE SCOPE!!!.

This will allows to write things like

synchronized(mtx_) {
    // block protected by a scoped lock
}

or

atomic(tr) {
    // atomic block managed by a transaction
}

The idea is to place the declaration of the needed variables on the init declaration of a for. So these variables are accessible by the next sentence or block. The condition will let executes its body only once. A variable __continue variable is used to this goal; it initialized to true and set to false on the next part of the for.

for (VARS DECLARATION, bool __continue=true;
        __continue;
        __continue=false)

The definition of the synchronized macro can be

#define synchronized(MUTEX) \
    for (boost::mutex::scoped_lock __lock(MUTEX), bool __continue=true; \
            __continue; \
            __continue=false)

In order to help when debuging we can define the __lock and _cont variables as macros generating symbols including the line number.

#define __concat(a,b) a##b
#define _concat(a,b) __concat(a,b)
#define __lock _concat(__lock_, __LINE__)
#define __continue _concat(__continue _, __LINE__)

As the variables are local to the scope, we can use the macro twice on the same line as
synchronized(mtx_1) synchronized(mtx_2){
    // block protected by a scoped lock on mtx_1 and mtx_2
    // ...
}

See how we can nest the synchronized blocks:

synchronized(mtx_1)
{ // block protected by a scoped lock on mtx_1
    // ... 1
    synchronized(mtx_2)
    { // Embeeded block protected by a scoped lock on mtx_2
        // ... 2
        synchronized(mtx_3) unique_sentence_protected_by_mtx_3();
        // ... 2
    }
    // ... 1
}

I know that most of the participants to this ML do not like macros too much, even less when we can do without them.

{ // block protected by a scoped lock on mtx_1
    boost::mutex::scoped_lock lock1(mtx_1) ;
    // ... 1
    { // Embeeded block protected by a scoped lock on mtx_2
        boost::mutex::scoped_lock lock1(mtx_2) ;
        // ... 2
        { // Artificial Embeeded block protected by a scoped lock on mtx_3
            boost::mutex::scoped_lock lock1(mtx_3) ;
            unique_sentence_protected_by_mtx_3();
        }
        // ... 2
    }
    // ... 1
}

But I think that in these cases the language-like macros make the code realy more readable, specially in the case of software transactional memory (STM), see (http://en.wikipedia.org/wiki/Software_transactional_memory).

    // language-like transaction with retry.
    try_atomic(t) {
        transaction_ptr(x) = val;
    } before_retry {}

I wanted simply to share this trick with you, maybe someone wants to use something like that in its own application. Let me know however what do you think.

Thanks to the authors of this excelent paper and the DracoSTM library.

Hoping this would inspire some of you,
Vicente
___________________________________

P.S. the exception based timed locks post http://www.nabble.com/-thread--Exception-based-timed-locks-tp21554444p21554444.html is also inspired from this article.

[1] Toward Simplified Parallel Support in C++
Justin E. Gottschlich, Jeremy G. Siek, Paul J. Rogers and Manish Vachharajani


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk