Boost logo

Boost Users :

Subject: [Boost-users] [signals (thread safe)] Scope of slot_call_iterator (or how to implement deferred signals)
From: Fabio Fracassi (f.fracassi_at_[hidden])
Date: 2009-02-03 18:15:56


Hi,

I am currently trying to implement deferred signals using boost signals
(well thread safe signals). I had the (crazy?) idea that it might be
possible to hijack the combiner interface to do this.

The design rational said that during the signal call the state
(including the arguments that were passed) are cached for the combiner.
So, what I tried to do was to copy and store the first and last iterater
out of the combiner, to iterate over them at a later time, to call the
slots. What I don't know is whether the slot_call_iterator (and the data
it uses ) stays in scope after the signal call (i.e.
signal...::operator() ) returns.

Here is what I tried:

#include <boost/signal.hpp>
#include <boost/tr1/memory.hpp>
#include <queue>

struct signal_queue {

   signal_queue() : m_signals() {}

   template <typename InputIterator>
   void add_signal(InputIterator first, InputIterator last) {
     m_signals.push(
       std::tr1::shared_ptr<sig_iterator_range_placeholder>(
         new sig_iterator_range_holder<InputIterator>(first, last)));
   }
        
   bool process_one_signal() {
     if (!m_signals.empty()) {
       std::tr1::shared_ptr<sig_iterator_range_placeholder> sig =
         m_signals.front();
       m_signals.pop();
                
       sig->send();
       return true;
     }
     return false;
   }

   private:
     struct sig_iterator_range_placeholder {
       virtual void send() = 0;
     };

     template <typename InputIterator>
     struct sig_iterator_range_holder : sig_iterator_range_placeholder {
       sig_iterator_range_holder(
         InputIterator first,
         InputIterator last) :
           m_first( first ),
           m_last( last )
       {}

       virtual void send () {
         while (m_first != m_last) {
           *m_first++;
         }
         return;
       }
       private:
         InputIterator m_first;
         InputIterator m_last;
     };
        
     std::queue< std::tr1::shared_ptr<sig_iterator_range_placeholder> >
       m_signals;
};

struct defered_send {
   typedef void result_type;

   template<typename InputIterator>
   void operator()(InputIterator first, InputIterator last) const {
     s_signalQueue.add_signal( first, last );
   }
   static signal_queue s_signalQueue;
};
signal_queue defered_send::s_signalQueue = signal_queue();

// Tests
struct test_slots {
   void operator()() const {
     std::cout << "exec Slot()" << std::endl;
   }
   void operator()(int t1, int t2) const {
     std::cout << "exec Slot(" << t1 << ", " << t2 << ")" << std::endl;
   }
};

int main () {
   boost::signal<void ( int, int ), defered_send> def_ii;

   test_slots test;
   def_ii.connect(test);

   std::cout << "Send Defered Signals ... " << std::endl;
   def_ii(42, 24); // <- Point 1
        
   std::cout << "Entering 'event loop' ... " << std::endl;
   while ( defered_send::s_signalQueue.process_one_signal() ) {}

   std::cout << "Done." << std::endl << std::flush;

   return EXIT_SUCCESS;
}

The good news is that it doesn't crash, though I suspect that this is
pure luck. After the slot call (Point 1) some things go out of scope
(most notably the cache variable in slot_call_iterator).

Now to make a long story short. Is what I am trying to do possible?
If not should it? Or do I have to find another way to achive this? If so
does someone have a hint?

Regards

Fabio


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net