Skip to content

Understanding the generated C++

This page continues the package built in Create your first package, where we gave my_package.MyClass a handful of properties, methods and events. They are there just to illustrate the options, but they will do here.

This page is about the C++ output. What each of the other generators produces is described with it under sen generate.

Generation produces a base class holding:

  • Getters and setters for the properties.
  • Pure virtual functions for the methods (that you will need to implement).
  • Protected functions for firing the events.

Let's have a look at the generated file (remember that files generated by Sen end in .stl.h).

Methods

my_class.stl.h methods
class MyClassBase: public ::sen::NativeObject, public virtual MyClassInterface
{
protected:  // MyClass methods
  virtual i32 addNumbersImpl(i32 a, i32 b) = 0;
  virtual std::string echoImpl(const std::string& message) = 0;
  virtual void changePropsImpl() = 0;
};

You see that the signature of the methods that you need to implement is in line with what you defined in the STL file. Sen deals with wrapping these calls in asynchronous functions (that's the reason why they are protected).

The generator writes the short type names i32, f32 and u64, which sen/core/base/numbers.h defines as aliases for the fixed-width C++ types. Your implementation may use either spelling, since i32 and int32_t are the same type.

Events

The functions related to events are these:

my_class.stl.h events
public: // MyClass events
  inline ::sen::ConnectionGuard onSomethingHappened(::sen::EventCallback<>&& callback) final;
  inline void somethingHappened(::sen::Emit mode = ::sen::Emit::onCommit);
  inline ::sen::ConnectionGuard onSomethingElseHappened(::sen::EventCallback<i32>&& callback) final;
  inline void somethingElseHappened(i32 arg, ::sen::Emit mode = ::sen::Emit::onCommit);

Each event produces a pair, listed together.

First, there are the functions that clients can use to receive notifications about the emission of events. Those are named like on<Event>(), taking the name of the event. They receive an EventCallback<>, which is the function Sen calls back on. The template parameters are the types of the arguments of the event (that's where we get the signature of the callback we expect to receive).

Second, there are the functions that fire those events. They are named after the events. The mode argument is rarely used but tells Sen when you want the event produced.

Properties

The functions related to the properties are the following:

my_class.stl.h properties
public: // MyClass properties
  // prop1
  [[nodiscard]] inline const std::string& getProp1() const noexcept final;

  // prop2
  [[nodiscard]] inline const ::my_package::StructOfInts& getProp2() const noexcept final;
  inline void setNextProp2(const ::my_package::StructOfInts& val) final;
  [[nodiscard]] inline const ::my_package::StructOfInts& getNextProp2() const noexcept;
  inline ::sen::ConnectionGuard onProp2Changed(::sen::PropertyCallback&& callback) final;

  // prop4: not writable, so its setter is not final, see below
  [[nodiscard]] inline const ::my_package::Vec2& getProp4() const noexcept final;
  inline void setNextProp4(const ::my_package::Vec2& val);
  [[nodiscard]] inline const ::my_package::Vec2& getNextProp4() const noexcept;
  inline ::sen::ConnectionGuard onProp4Changed(::sen::PropertyCallback&& callback) final;

  // prop5
  [[nodiscard]] inline i32 getProp5() const noexcept final;
  inline void setNextProp5(i32 val) final;
  [[nodiscard]] inline i32 getNextProp5() const noexcept;
  inline ::sen::ConnectionGuard onProp5Changed(::sen::PropertyCallback&& callback) final;

prop3 follows the same shape as prop2 and is left out here for brevity.

Every property has a getter, named like get<Prop>(), taking the name of the property.

Change notification comes from the hooks named like on<Prop>Changed(). They all receive a callback to notify clients. You can see that those functions return a ConnectionGuard. ConnectionGuards keep your callback installed in the object.

The setters are named setNext<Prop>(). Every dynamic property has one on the base class, because your implementation has to be able to change its own state. A [static] property has none at all: it is set once through the constructor and never changes, so prop1 above has a getter and nothing else. What [writable] decides is whether the setter also appears on MyClassInterface, which is what other objects hold: prop2, prop3 and prop5 are writable, so their setters are declared on the interface and the base marks them final. prop4 is not, so its setter exists only on the base, so no other object can reach it.

The "Next" in the name is because the value only becomes visible to the world after commit(). That guarantees the state of objects is consistent within a cycle: the next cycle is computed from the inputs of the current one. To read back a value you have staged but not yet committed, use the matching getNext<Prop>().

Static properties and the constructors

Static properties are the ones that do not change during the lifetime of your objects. You need to set them via the constructor to ensure that they always have a valid value. In this case it is prop1:

my_class.stl.h constructors
MyClassBase(std::string name, std::string prop1);
MyClassBase(std::string name, const ::sen::VarMap& args);

The first takes the static properties directly. The second takes them as a map, and is the one the kernel uses when it builds an object from a YAML configuration, which is why a missing static property is reported as a missing constructor argument. You will not find it written out in the generated file: it comes from the SEN_IMPL_GEN_BASE_CLASS macro at the top of the class.