views:

230

answers:

4

This program:

test_header.hpp

#include <boost/signal.hpp>
#include <utility>

class Sensor;

class Recorder : public ::boost::signals::trackable {
 public:
   explicit Recorder(int id) : id_(id) {}

   // Cannot be copied
   Recorder(const Recorder &) = delete;
   Recorder &operator =(const Recorder &) = delete;

   // But can be moved so it can be stored in a vector.
   // There's a proposal for having a compiler generated default for this that would be
   // very convenient.
   Recorder(Recorder &&b) : id_(b.id_) {
      b.id_ = -1;
   }
   Recorder &operator =(Recorder &&b) {
      id_ = b.id_;
      b.id_ = -1;
      return *this;
   }

   void recordSensor(const Sensor &s);
   void addSensor(Sensor &s);

 private:
   int id_;
   char space_[1312];
};

class Sensor {
 public:
   typedef ::boost::signal<void (const Sensor &)> sigtype_t;

   explicit Sensor(int id) : val_(0), id_(id) { }

   void notify(const sigtype_t::slot_type &slot) { signal_.connect(slot); }
   void updateSensor(double newval) { val_ = newval; signal_(*this); }
   double getValue() const { return val_; }
   int getId() const { return id_; }

 private:
   sigtype_t signal_;
   double val_;
   const int id_;
};

test_body.cpp

#include "test_header.hpp"
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <iostream>
#include <vector>
#include <string>
#include <sstream>

void Recorder::addSensor(Sensor &s)
{
   ::std::cout << "Recorder #" << id_
               << " now recording Sensor #" << s.getId() << '\n';
   ::std::cout.flush();
   s.notify(::boost::bind(&Recorder::recordSensor, this, _1));
}

void Recorder::recordSensor(const Sensor &s)
{
   ::std::cout << "Recorder #" << id_ << " - new value for sensor named Sensor #"
               << s.getId() << ": " << s.getValue() << '\n';
   ::std::cout.flush();
}


int main(int argc, const char *argv[])
{
   using ::boost::shared_ptr;
   using ::std::vector;
   vector<Recorder> recorders;
   vector<shared_ptr<Sensor> > sensors;
   double val = 0.1;
   static const unsigned int recorder_every = 4;
   static const unsigned int sensor_every = 2;

   for (unsigned int i = 0; i < 9; ++i) {
      if (i % recorder_every == 0) {
         recorders.push_back(Recorder(i / recorder_every));
      }
      if (i % sensor_every == 0) {
         shared_ptr<Sensor> sp(new Sensor(i / sensor_every));
         sensors.push_back(sp);
         for (auto r = recorders.begin(); r != recorders.end(); ++r) {
            r->addSensor(*sp);
         }
      }
      for (auto s = sensors.begin(); s != sensors.end(); ++s, val *= 1.001) {
         (*s)->updateSensor(val);
      }
   }
}

And I get this output:

Recorder #0 now recording Sensor #0
Recorder #0 - new value for sensor named Sensor #0: 0.1
Recorder #0 - new value for sensor named Sensor #0: 0.1001
Recorder #0 now recording Sensor #1
Recorder #0 - new value for sensor named Sensor #0: 0.1002
Recorder #0 - new value for sensor named Sensor #1: 0.1003
Recorder #0 - new value for sensor named Sensor #0: 0.100401
Recorder #0 - new value for sensor named Sensor #1: 0.100501
Recorder #0 now recording Sensor #2
Recorder #1 now recording Sensor #2
Recorder #0 - new value for sensor named Sensor #2: 0.100803
Recorder #1 - new value for sensor named Sensor #2: 0.100803
Recorder #0 - new value for sensor named Sensor #2: 0.101106
Recorder #1 - new value for sensor named Sensor #2: 0.101106
Recorder #0 now recording Sensor #3
Recorder #1 now recording Sensor #3
Recorder #0 - new value for sensor named Sensor #2: 0.101409
Recorder #1 - new value for sensor named Sensor #2: 0.101409
Recorder #0 - new value for sensor named Sensor #3: 0.101511
Recorder #1 - new value for sensor named Sensor #3: 0.101511
Recorder #0 - new value for sensor named Sensor #2: 0.101815
Recorder #1 - new value for sensor named Sensor #2: 0.101815
Recorder #0 - new value for sensor named Sensor #3: 0.101917
Recorder #1 - new value for sensor named Sensor #3: 0.101917
Recorder #0 now recording Sensor #4
Recorder #1 now recording Sensor #4
Recorder #2 now recording Sensor #4
Recorder #0 - new value for sensor named Sensor #4: 0.102428
Recorder #1 - new value for sensor named Sensor #4: 0.102428
Recorder #2 - new value for sensor named Sensor #4: 0.102428

I'm kind of confused. When I add a Recorder it seems like all the old sensors are forgotten about.

+2  A: 

Recorders can move in memory because you are adding to a vector as you create them, but you are binding to them for signaling as you are doing this.

ergosys
Any ideas on how to fix the issue? I really need to store the Records by value.
Omnifarious
I'd use a deque instead.
ergosys
A: 

Give the recorder an index to the sensor vector instead of a pointer to the sensor. Works fine as long as you don't delete sensors.

Hans
I just tried that and it didn't change anything. I think it has more to do with the pointer to Recorder that's hidden inside the signal than the reference to the signal that's being given to the recorder.
Omnifarious
A: 

Have you tried using straight arrays instead of ::std::vector ? I know that ::std::vector is a fashionable thing but it's not really a vector (more like a half-list half-array).

ZXX
Say what? He needs a dynamic array, a "straight" array is not dynamic. And it's a bit weird to say a vector is not a vector.
GMan
Nope - he just needs to keep two normal indexes and place new objects into stable slots, that's all. It can even be made thread-safe with one more refinement.Read the source of what STL vector actually does before protesting and then read definition of that is vector. Perpetuating misleading names just keeps confusing issues. Vector and list have very different behavior, complexity and thread safety. Unfortunately STL created a naming mess and the best we can do is to be aware of it.
ZXX
@zb_z: Use @name to reply. What definition of `vector`? There is an interface and requirements set by the standard, there is no "the" definition. There is no STL, there is a standard library. Vector is a perfectly reasonable name; it contains an array of indexable values, just like a math vector. Just because you can change the size doesn't mean anything. And no, a slot system won't fix anything when he needs a *dynamic array*. You can't guess how many slots you need up-front, and it would be stupid to re-invent `vector` with some hard-coded size. Just use a `deque` as the proper alternative.
GMan
And `vector` use isn't "fashionable", it's correct. You need a dynamic array, you use a `vector`. Your analogy fails as well; if anything, a `deque` is a half-list half-array, not the `vector`.
GMan
Notice how you keep calling it dynamic array just about all the time - a thing that presents the interface of an array but but has the functionality of a list. Honest name for that is ArrayList or equivalent. Definition of vector in math is an ordered tuple of the fixed, immutable size - no pushing and popping involved. The world existed before STL injected a misleading name and it will keep confusing people just about as long as there's math and physics.
ZXX
Just rewrite the example from this question with straight array and you'll see that it works. If you really, really don;t see the max length of it use 9 and you'll do fine :-) I don't know why do you keep imagining that the max size of an array for this problem is not known before the for loop starts running - maybe just for the sake of arguing ??
ZXX
@zb: Like I said before, please use @name so your replies will show up for me and I don't have to check. A `vector` does **not** have the functionality of a list. A list is a data structure that stores nodes with pointers to adjacent nodes, if any. An array is a *contiguous* collection of elements. These are definitions within computer science. A `vector` is *contiguous*, therefore it is an *array*. It is also resizeable, therefore it's a *dynamic array*. That's it, simple as that. It's not anything like a list except that it's a sequence container. In fact, `vector` is often the best first...
GMan
...choice for a container while a list is the *last resort*. This is because of the very fact `vector` is nothing like a list! Like I said above, if anything a `deque` would be called an "array list". This is because a common implementation is to link chunks of memory together, i.e. a linked-list of arrays. I'm well-aware what a vector is in math and that it's been around longer. But you do know not all vectors are the same size, we can have *k*-dimensional vectors with any integer k. (Of course) Just because `vector` lets us choose k at runtime doesn't suddenly mean it's not a vector,...
GMan
...it's *still a k-dimensional collection of elements*. I thought it was obvious 9 was just his testing number and the real number could be anything determined in the real application at runtime. I don't disagree using an array with fixed size would work, but it's also not really solving his problem since he made it clear he needs dynamic storage by using it. No, I don't do things for the sake of arguing.
GMan
You can have vectors of different dimensions k but that dimension is still fixed, immutable once the vector is defined. You don't get to push and pop in and out of vector do you? When people see name vector they expect that guarantee of stability - not to have to wonder if and when something is going to change the location of contained objects.
ZXX
We both know the name of data structure into which you can push and pop and the location of elements can change wiht every such operations don't we? And we know that it can be implemented in a continuous block of memory, but it still belongs to the family of lists. We also know that queue can also be implenented in a contimuous memory block - doesn't make it a vector does it ?
ZXX
On and it's entirely your private imagination that just because someone used STL vector by mistake, believeing that it's stable, means that he actually needed infinitely growing container. For the problem at hand (massive event broadcasting) one can easily argure the need for well defined max size by the very definition of the problem.
ZXX
Also, feel free to worship your STL "vector" and whatever else you want, but stop bullying everyone who doesn't and claiming that a container which changes size, moves it's elements around and allows pushing and popping in and out can be honestly called vector - unless you have armed force to forbid all mathematics, physics and a few other fields. I really don't have any more time for such pointless exchanges.
ZXX
A: 

Ergosys already described the reason for the problem. What's missing is the solution: Make sure you finish adding all the recorders and sensors to the vectors before you bind the signals. That way their addresses will be done changing.

Here's a revision of the main loop:

for (unsigned int i = 0; i < 9; ++i) {
   if (i % recorder_every == 0) {
      recorders.push_back(Recorder(i / recorder_every));
   }
   if (i % sensor_every == 0) {
      shared_ptr<Sensor> sp(new Sensor(i / sensor_every));
      sensors.push_back(sp);
   }
}
for (unsigned int i = 0; i < 9; ++i) {
   if (i % sensor_every == 0) {
      for (auto r = recorders.begin(); r != recorders.end(); ++r) {
         shared_ptr<Sensor> sp = sensors[i / sensor_every];
         r->addSensor(*sp);
      }
   }
   for (auto s = sensors.begin(); s != sensors.end(); ++s, val *= 1.001) {
      (*s)->updateSensor(val);
   }
}
Nathan Kitchen