boost::signals2

I have two class variables that store a list of external slot functions depending on the state. These variables are grouped by map<[key type], boost::signals2::signal>. However I'm having trouble passing one slot function from one map to another map.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
struct MyUpdate {
	long key;
	std::string update_id;
	std::string value;
};

class MyClass {
public:
	typedef boost::signals2::signal<void(const MyUpdate&)> signal_update;
	void init(const long key, const signal_update::slot_type& update_func) {
		key_update[key].connect(update_func);
	}
	void swap(const long from_key, const std::string to_key) {
		if (key_update.count(from_key) > 0) {
			update_id_update[to_key].connect(key_update[from_key]);
			key_update[from_key].disconnect_all_slots();
			key_update.erase(from_key);
		}
	}
	void call_func(const MyUpdate& this_update) {
		if (key_update.count(this_update.key) > 0) {
			key_update[this_update.key](this_update);
		}
		if (update_id_update.count(this_update.update_id) > 0) {
			update_id_update[this_update.update_id](this_update);
		}
	}
private:
	std::map<long, signal_update> key_update;
	std::map<std::string, signal_update> update_id_update;
};


void print_update1(const MyUpdate& update) {
	cout << "print_update1" << "\t";
	cout << "key = " << update.key << "\t";
	cout << "update_id = " << update.update_id << "\t";
	cout << "value= " << update.value << endl;
}

void print_update2(const MyUpdate& update) {
	cout << "print_update2" << "\t";
	cout << "key = " << update.key << "\t";
	cout << "update_id = " << update.update_id << "\t";
	cout << "value= " << update.value << endl;
}

void print_update3(const MyUpdate& update) {
	cout << "print_update3" << "\t";
	cout << "key = " << update.key << "\t";
	cout << "update_id = " << update.update_id << "\t";
	cout << "value= " << update.value << endl;
}

void main_test() {
	MyClass myCls;
	myCls.init(1, &print_update1);
	myCls.init(2, &print_update2);
	myCls.init(3, &print_update3);

	MyUpdate dummy_update;
	dummy_update.key = 1;
	dummy_update.update_id = "a";
	dummy_update.value = "abc123";

	myCls.call_func(dummy_update);
	dummy_update.key = 2;
	myCls.call_func(dummy_update);

	myCls.swap(2, "a");
	myCls.call_func(dummy_update);

}


However the problem come from line 16-17. When I disconnect and/or erase the key from the original map, it seems to disconnect the newly connected signal as well. If I commented out both lines it works fine but I have extra keys in the key_update map. If I delete either of the lines it still doesn't work (ie. won't call the external function).

Current output:
1
2
print_update1   key = 1 update_id = a   value= abc123
print_update2   key = 2 update_id = a   value= abc123


If I comment out line 16-17:
1
2
3
4
print_update1   key = 1 update_id = a   value= abc123
print_update2   key = 2 update_id = a   value= abc123
print_update2   key = 2 update_id = a   value= abc123
print_update2   key = 2 update_id = a   value= abc123


If I comment out line 16 or line 17:
1
2
print_update1   key = 1 update_id = a   value= abc123
print_update2   key = 2 update_id = a   value= abc123


However I want to print 3 lines only, with key_update having only keys (1, 3) and update_id_update having only keys ("a")

Can anyone help on this?
I'd suggest to move line 16 before line 15.
Why would that work?

Anyway, tried that and same result (2 lines of output)
Ok, it's not easy to understand what you're doing.
You connect a signal to another signal which will be erased. That cannot possibly work.


boost provides a swap function for signals:

http://www.boost.org/doc/libs/1_57_0/doc/html/signals2/reference.html#header.boost.signals2.signal_hpp
Thanks coder777.

Tried swap and it works if there is only one function per signal

Anyway, I tried storing the slot function upon initial connection (function init) in some map, and connect that to the other signal while erasing the original connected signal. That works finally.
Anyway, I think it make good sense that boost provide some functions for signal to pass its connected slots to another signal. This allows more flexible way to manage signals allocation.
In your code above, I don't see where you put anything in your map "update_id_update".

1
2
3
if (update_id_update.count(this_update.update_id) > 0) {
			update_id_update[this_update.update_id](this_update);
		}
How could this be true?
It's done under the function MyClass::swap
boost provide some functions for signal to pass its connected slots to another signal.
But that doesn't happen. The signatur of key_update[from_key] fulfills the requirement to act as a slot hence connect can be applied. Not sure what happens when signal is actually erased.

Tried swap and it works if there is only one function per signalAre you sure?
You may test this in a parallel test project
I mean it will be great is boost allows something like
1
2
3
4
5
6
7
8
9
first_signal_map[id].connect([some external slot1]);
first_signal_map[id].connect([some external slot2]);
first_signal_map[id].connect([some external slot3]);
second_signal.connect([some external slot4]);

second_signal.take_over_slots(first_signal_map[id]);   // wish list
first_signal_map[id].disconnect_all_slots();
first_signal_map.erase(id);
second_signal();   // still run the four external functions 


Also in this example the "swap" function simply swap slots between the two signals, instead of passing them from one to another. Hence if I replace "take_over_slots" by "swap", slot4 will be lost.
The focus of the signal/slot mechanism is the connection.
When a signal dies everything is ok since the slot is not called anymore.
When a slot dies we have a problem since if it is called it's likely that the program crashes, therefore a slot (using the result of connect(...)) can disconnect itself. With scoped_connection it can be done automatically.

You cannot have two signals calling one slot since this would break the connnection mechanism. Hence you can swap signals but not assign one to another.

So signal is not designed to be used the way you do. What you rather want is a container (like vector or list) of functions. I'd suggest that you use boost function with a container instead.
Actually I wasn't trying to have two signals calling the same slot. Instead I'm trying to pass a slot from one signal to another, while the later signal may already have connected to other slots. Hence my "wish thinking" [take_over_slots] function, doing exactly that.
Instead I'm trying to pass a slot from one signal to another
You cannot do this directly because it breaks the connection concept. Move a slot is done like so:
1
2
3
4
5
6
7
signal sig1
signal sig2

connection conn = sig1.connect(func) //Now you have a connection from sig1 to func via conn
...
conn.disconnect()
conn = sig2.connect(func)


Since you don't want the connection scheme (or better: you don't use it) you better use boost functions:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
struct MyUpdate {
	long key;
	std::string update_id;
	std::string value;
};

class MyClass {
public:
	typedef boost::function<void(const MyUpdate&)> func_update_type;
	void init(const long key, const func_update_type& update_func) {
		key_update[key].push_back(update_func);
	}
	void swap(const long from_key, const std::string to_key) {
		auto &to = update_id_update[to_key]; // Note: & is important
		auto it = key_update.find(from_key);
		if(it != key_update.end())
		{
			for(const func_update_type &func_update : it->second)
			{
				to.push_back(func_update);
			}
			key_update.erase(it);
		}
	}
	void call_func(const MyUpdate& this_update) {
		auto it = key_update.find(from_key);
		if(it != key_update.end())
		{
			for(const func_update_type &func_update : it->second)
			{
				func_update(this_update);
 			}
		}
...
	}
private:
	std::map<long, std::vector<func_update_type> > key_update;
	std::map<std::string, std::vector<func_update_type> > update_id_update;
};


void print_update1(const MyUpdate& update) {
	cout << "print_update1" << "\t";
	cout << "key = " << update.key << "\t";
	cout << "update_id = " << update.update_id << "\t";
	cout << "value= " << update.value << endl;
}

void print_update2(const MyUpdate& update) {
	cout << "print_update2" << "\t";
	cout << "key = " << update.key << "\t";
	cout << "update_id = " << update.update_id << "\t";
	cout << "value= " << update.value << endl;
}

void print_update3(const MyUpdate& update) {
	cout << "print_update3" << "\t";
	cout << "key = " << update.key << "\t";
	cout << "update_id = " << update.update_id << "\t";
	cout << "value= " << update.value << endl;
}

void main_test() {
	MyClass myCls;
	myCls.init(1, &print_update1);
	myCls.init(2, &print_update2);
	myCls.init(3, &print_update3);

	MyUpdate dummy_update;
	dummy_update.key = 1;
	dummy_update.update_id = "a";
	dummy_update.value = "abc123";

	myCls.call_func(dummy_update);
	dummy_update.key = 2;
	myCls.call_func(dummy_update);

	myCls.swap(2, "a");
	myCls.call_func(dummy_update);

}
Note that it is not tested!

[EDIT] With the connection concept you can have two signal to one slot
Last edited on
Ahhh I see. So this is sort of like a custom made method using vector of functions to "pretend"to be signal2::signal right?

Thanks.
Topic archived. No new replies allowed.