cpp-common/bt2: add `plugin-dev.hpp`
authorPhilippe Proulx <eeppeliteloop@gmail.com>
Wed, 8 Nov 2023 16:28:08 +0000 (11:28 -0500)
committerSimon Marchi <simon.marchi@efficios.com>
Thu, 14 Dec 2023 15:57:04 +0000 (10:57 -0500)
commitaf38ae4f5e13adddb0d69bec31d3ba10fc5d5db9
tree722ca330b028d547aacdcfb4eaa7671362347436
parent7a72f18a55652b7166fb4adec37431c892e37fc9
cpp-common/bt2: add `plugin-dev.hpp`

This new file makes it possible to write pure C++ component and message
iterator classes without having to deal with C methods.

`plugin-dev.hpp` offers this to the C++ component class author:

* Inherit `bt2::UserSourceComponent`, `bt2::UserFilterComponent`, or
  `bt2::UserSinkComponent` to implement a C++ component class.

  This class template uses the CRTP to implement static polymorphism.
  Its only template parameter is your actual user component class.

  The protected constructor of those base classes accept a specific self
  component wrapper and keeps it privately.

  The constructor also accepts a logging tag prefix with which it builds
  a protected `bt2c::Logger` named `_mLogger` so that you may use the
  BT_CPPLOG*() macros in your own component class. The constructor
  appends `/[`, the name of the component, and `]` to the logging tag
  (for example, if your tag prefix is `SRC.CTF.FS`, working with a
  component named `mein-comp`, then the complete tag is
  `SRC.CTF.FS/[mein-comp]`).

  Those base classes implement default, overloadable methods
  (_inputPortConnected() and _getSupportedMipVersions(), for example)
  when possible.

  They also offer protected methods which are proxies of the contained
  specific self component wrapper, for example _loggingLevel() and
  _addOutputPort().

* Inherit `bt2::UserMessageIterator` to implement a C++ message
  iterator.

  This class template uses the CRTP to implement static polymorphism.
  Its first template parameter is your actual user message iterator
  class.

  The other parameter is your C++ component class, for example:

      class MeinSourceComponent :
          public bt2::UserSourceComponent<MeinSourceComponent>
      {
          friend bt2::UserSourceComponent<MeinSourceComponent>;

          // ...
      };

      class MeinMessageIterator :
          public bt2::UserMessageIterator<MeinMessageIterator,
                                          MeinSourceComponent>
      {
          friend bt2::UserMessageIterator<MeinMessageIterator,
                                          MeinSourceComponent>;
          // ...
      };

  This makes MeinMessageIterator::_component() return
  `MeinSourceComponent&` (parent source component).

  `bt2::UserMessageIterator` also offers the a protected `bt2c::Logger`
  named `_mLogger` so that you may use the BT_CPPLOG*() macros in your
  own message iterator class. When you build a
  `bt2::UserMessageIterator`, you pass a logging tag suffix which will
  be appended to the logging tag of the main logger of the component to
  form the complete tag, for example `SRC.CTF.FS/[mein-comp]/MSG-ITER`.

  The public next() method (called by the bridge) implements the very
  common pattern of appending messages into the output array, and,
  meanwhile:

  If it catches a `bt2::TryAgain` exception:
      If the message array isn't empty, transform this into a success
      (don't throw).

      Otherwise rethrow.

  If it catches an error:
      If the message array isn't empty, transform this into a success
      (don't throw), but save the error of the current thread and the
      type of error to throw the next time the user calls next().

      Otherwise rethrow.

  The derived class must implement:

      void _next(bt2::ConstMessageArray& messages);

  This method fills `messages` with at most `messages.capacity()`
  messages and may throw `bt2::TryAgain` or a valid error whenever.
  Leaving an empty `messages` means the end of iteration.

* When you inherit `bt2::User*Component` and `bt2::UserMessageIterator`,
  your own methods may throw allowed exceptions (in `exc.hpp`): they'll
  get translated to corresponding library status codes.

  In general, prefer using BT_CPPLOGE_APPEND_CAUSE_AND_THROW() or
  BT_CPPLOGE_APPEND_CAUSE_AND_RETHROW().

* Use BT_CPP_PLUGIN_SOURCE_COMPONENT_CLASS(),
  BT_CPP_PLUGIN_FILTER_COMPONENT_CLASS(), or
  BT_CPP_PLUGIN_SINK_COMPONENT_CLASS() to translate your C++ component
  and message iterator classes into library classes, for example:

      BT_CPP_PLUGIN_SOURCE_COMPONENT_CLASS(mein, MeinSourceComponent,
                                           MeinMessageIterator);

  Those macros take care of all the C methods.

  You may still add a description and help with the usual
  BT_PLUGIN_*_COMPONENT_CLASS_DESCRIPTION() and
  BT_PLUGIN_*_COMPONENT_CLASS_HELP() macros.

  You may also used the `_WITH_ID` versions if your component class name
  isn't a valid C identifier.

Internally, the C++ plugin macros use bridging classes (thank you Simon
for the initial version of those). A bridging class offers static
methods which have the signature which libbabeltrace2 expects: such a
method calls the corresponding instance method of the user C++ class.

For example,
bt2::internal::MsgIterClsBridge<MeinMessageIterator>::canSeekBeginning()
calls MeinMessageIterator::canSeekBeginning() with the correct instance
which in turns calls MeinMessageIterator::_canSeekBeginning().

The init() method of a bridging class instantiates the user C++ class
and keeps it as the private data of the C class. The finalize() method
of a bridging class destroys the user C++ class instance.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Change-Id: If8ff312b3557202f1a4dfb1584c9abff1cd35e03
Reviewed-on: https://review.lttng.org/c/babeltrace/+/11299
Tested-by: jenkins <jenkins@lttng.org>
Reviewed-by: Simon Marchi <simon.marchi@efficios.com>
CI-Build: Simon Marchi <simon.marchi@efficios.com>
src/Makefile.am
src/cpp-common/bt2/exc.hpp
src/cpp-common/bt2/plugin-dev.hpp [new file with mode: 0644]
This page took 0.025652 seconds and 4 git commands to generate.