FASSWG msg actions: Difference between revisions
(Removed: Extra empty spaces) |
Devnulling (talk | contribs) (Fix formatting - html entities) |
||
Line 11: | Line 11: | ||
== Theory of operation: == | == Theory of operation: == | ||
* Registering a | * Registering a "normal" class methods or functions as action to be executed on message won't work, because "normal" methods don't take <code>pmt_t</code>. | ||
* Define a combined converter + | * Define a combined converter + "normal" method: Input is <code>pmt_t</code>, output is void, but in between, <code>pmt_t</code> gets converted to the native argument type of the method | ||
* Define a converter function; in most case, the PMT standard converters do, e.g. <code>pmt::to_long</code><br /> | * Define a converter function; in most case, the PMT standard converters do, e.g. <code>pmt::to_long</code><br /> | ||
Line 24: | Line 24: | ||
<code>basic_block::register_msg_action(key,conv,action)</code> method (or rather four methods, including three convenience wrappers): | <code>basic_block::register_msg_action(key,conv,action)</code> method (or rather four methods, including three convenience wrappers): | ||
* '''<code>register_msg_action</code>''' <code>(pmt_t </code> '''<code>key</code>''' <code>, function | * '''<code>register_msg_action</code>''' <code>(pmt_t </code> '''<code>key</code>''' <code>, function<ArbitraryType(pmt_t)></code> '''<code>converter</code>''' <code>, function<void(ArbitraryType)></code> '''<code>action</code>''' <code>)</code><br /> | ||
registers an action by generating a function chain functor, and putting that in a std::vector in a std::map. | registers an action by generating a function chain functor, and putting that in a std::vector in a std::map. | ||
* <code>basic_block::trigger_actions(key, value)</code><br /> | * <code>basic_block::trigger_actions(key, value)</code><br /> | ||
triggers all actions registered for a single key | triggers all actions registered for a single key | ||
* <code>basic_block::process_action_msg(pmt::pair)</code><br /> | * <code>basic_block::process_action_msg(pmt::pair)</code><br /> | ||
takes in pairs like <code>pmt::cons(pmt::mp( | takes in pairs like <code>pmt::cons(pmt::mp("interpolation"), pmt::from_long(100))</code> | ||
* the <code>boost::bind</code> magic that GR already uses when doing <code>register_msg_handler</code> gets used, so (hopefully) no new dependencies or boost strangenesses. | * the <code>boost::bind</code> magic that GR already uses when doing <code>register_msg_handler</code> gets used, so (hopefully) no new dependencies or boost strangenesses. | ||
* the <code>basic_block</code> constructor now sets up a message port to which one can send such pairs, and registers <code>process_action_msg</code> as handler | * the <code>basic_block</code> constructor now sets up a message port to which one can send such pairs, and registers <code>process_action_msg</code> as handler | ||
Line 37: | Line 37: | ||
* '''Intrinsic:''' <code>gr::blocks::repeat_impl</code> has a non-thread-safe <code>set_interpolation(int)</code> method. It wants to keep that private and expose a message action. It does | * '''Intrinsic:''' <code>gr::blocks::repeat_impl</code> has a non-thread-safe <code>set_interpolation(int)</code> method. It wants to keep that private and expose a message action. It does | ||
<pre>boost::function action = boost::bind(& | <pre>boost::function action = boost::bind(&repeat_impl::set_interpolation, this, _1);@ | ||
register_msg_action(pmt::mp( | register_msg_action(pmt::mp("interpolation"), pmt::to_long, action);</pre> | ||
* '''Extrinsic:''' User/GRC knows there's a <code>multiply_const_x::set_k(float)</code> method, but <code>multiply_const</code> doesn't offer a message passing interface. Instead of despairing, we now can do | * '''Extrinsic:''' User/GRC knows there's a <code>multiply_const_x::set_k(float)</code> method, but <code>multiply_const</code> doesn't offer a message passing interface. Instead of despairing, we now can do | ||
<pre>boost::function action = boost::bind(& | <pre>boost::function action = boost::bind(&repeat_impl::set_interpolation, this, _1); | ||
multiply_const_0- | multiply_const_0->register_msg_action(pmt::mp("factor"), pmt::to_float, action); | ||
</pre>and send pairs (key, value) to the <code>pmt::mp( | </pre>and send pairs (key, value) to the <code>pmt::mp("actions")</code> msg port of the respective block | ||
== Discussion == | == Discussion == | ||
* How to do getters? | * How to do getters? | ||
* Should GR have one central settings | * Should GR have one central settings "bus", to which blocks' setters automatically are subscribed? | ||
== Yet to do == | == Yet to do == | ||
Line 56: | Line 56: | ||
* replacing GRC's callback mechanism by something that registers a python function as action handler | * replacing GRC's callback mechanism by something that registers a python function as action handler | ||
* Will require that | * Will require that "Variable" GRC blocks are aware of this<br /> | ||
* All blocks | * All blocks "observing" variable get their "actions" port subscribed to the variable's message_publisher | ||
As you can see, not nearly complete, but already useful. I'm not quite sure the naming and architecture take things in the right direction, so I RFC before I PR. | As you can see, not nearly complete, but already useful. I'm not quite sure the naming and architecture take things in the right direction, so I RFC before I PR. |
Latest revision as of 01:23, 21 March 2017
msg_actions PROPOSAL
Problem
Millions of setters in GNU Radio. Some safe to call from another thread whilst general_work is running, others not. No unified scheme of communicating settings.
Approach
Harnessing the power of message passing, and using a bit of old school boost magic to put functions into STL containers, so that they can be automagically called.
Theory of operation:
- Registering a "normal" class methods or functions as action to be executed on message won't work, because "normal" methods don't take
pmt_t
. - Define a combined converter + "normal" method: Input is
pmt_t
, output is void, but in between,pmt_t
gets converted to the native argument type of the method
- Define a converter function; in most case, the PMT standard converters do, e.g.
pmt::to_long
- Find a way to do something like a lambda without breaking with old Boost / C++-pre-0x
- Registry can then be of function(al)s that are
void action(pmt_t)
- Registry has
std::vector
of actions to be taken upon a specific key
Implementation so far
basic_block::register_msg_action(key,conv,action)
method (or rather four methods, including three convenience wrappers):
register_msg_action
(pmt_t
key
, function<ArbitraryType(pmt_t)>
converter
, function<void(ArbitraryType)>
action
)
registers an action by generating a function chain functor, and putting that in a std::vector in a std::map.
basic_block::trigger_actions(key, value)
triggers all actions registered for a single key
basic_block::process_action_msg(pmt::pair)
takes in pairs like pmt::cons(pmt::mp("interpolation"), pmt::from_long(100))
- the
boost::bind
magic that GR already uses when doingregister_msg_handler
gets used, so (hopefully) no new dependencies or boost strangenesses. - the
basic_block
constructor now sets up a message port to which one can send such pairs, and registersprocess_action_msg
as handler
Use Cases
- Intrinsic:
gr::blocks::repeat_impl
has a non-thread-safeset_interpolation(int)
method. It wants to keep that private and expose a message action. It does
boost::function action = boost::bind(&repeat_impl::set_interpolation, this, _1);@ register_msg_action(pmt::mp("interpolation"), pmt::to_long, action);
- Extrinsic: User/GRC knows there's a
multiply_const_x::set_k(float)
method, butmultiply_const
doesn't offer a message passing interface. Instead of despairing, we now can do
boost::function action = boost::bind(&repeat_impl::set_interpolation, this, _1); multiply_const_0->register_msg_action(pmt::mp("factor"), pmt::to_float, action);
and send pairs (key, value) to the pmt::mp("actions")
msg port of the respective block
Discussion
- How to do getters?
- Should GR have one central settings "bus", to which blocks' setters automatically are subscribed?
Yet to do
- drawing pretty pictures and expanding docs beyond the doxygen method docs
- block_gateway to enable python methods to be registered. Should be relatively simple, if it weren't for swig.
- replacing GRC's callback mechanism by something that registers a python function as action handler
- Will require that "Variable" GRC blocks are aware of this
- All blocks "observing" variable get their "actions" port subscribed to the variable's message_publisher
As you can see, not nearly complete, but already useful. I'm not quite sure the naming and architecture take things in the right direction, so I RFC before I PR.