FASSWG msg actions: Difference between revisions

From GNU Radio
Jump to navigation Jump to search
(Removed: Extra empty spaces)
(Fix formatting - html entities)
 
Line 11: Line 11:
== Theory of operation: ==
== Theory of operation: ==


* Registering a &quot;normal&quot; class methods or functions as action to be executed on message won't work, because &quot;normal&quot; methods don't take <code>pmt_t</code>.
* 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 + &quot;normal&quot; 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 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&lt;ArbitraryType(pmt_t)&gt;</code> '''<code>converter</code>''' <code>, function&lt;void(ArbitraryType)&gt;</code> '''<code>action</code>''' <code>)</code><br />
* '''<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(&quot;interpolation&quot;), pmt::from_long(100))</code>
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(&amp;repeat_impl::set_interpolation, this, _1);@
<pre>boost::function action = boost::bind(&repeat_impl::set_interpolation, this, _1);@
register_msg_action(pmt::mp(&quot;interpolation&quot;), pmt::to_long, action);</pre>
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(&amp;repeat_impl::set_interpolation, this, _1);
<pre>boost::function action = boost::bind(&repeat_impl::set_interpolation, this, _1);
multiply_const_0-&gt;register_msg_action(pmt::mp(&quot;factor&quot;), pmt::to_float, action);
multiply_const_0->register_msg_action(pmt::mp("factor"), pmt::to_float, action);
</pre>and send pairs (key, value) to the <code>pmt::mp(&quot;actions&quot;)</code> msg port of the respective block
</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 &quot;bus&quot;, to which blocks' setters automatically are subscribed?
* 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 &quot;Variable&quot; GRC blocks are aware of this<br />
* Will require that "Variable" GRC blocks are aware of this<br />
* All blocks &quot;observing&quot; variable get their &quot;actions&quot; port subscribed to the variable's message_publisher
* 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 doing register_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 registers process_action_msg as handler

Use Cases

  • Intrinsic: gr::blocks::repeat_impl has a non-thread-safe set_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, but multiply_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.