Skip to content

Understanding the generated codeΒΆ

In the previous example we defined 4 properties, 3 methods and 2 events. Those are there just to illustrate the options, but they will do for this tutorial.

The generated code for this class will result in a base class that will have:

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

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

my_class.stl.h methods
class MyClassBase: public sen::NativeObject, public 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 we need to implement is in line with what we defined in the STL file. Sen deals with wrapping these calls in asynchronous functions (that's the reason why they are protected).

If we take a look at the functions related to events:

my_class.stl.h events
public: // MyClass event handlers
  ConnectionGuard onSomethingHappened(EventCallback<>&& callback) final;
  ConnectionGuard onSomethingElseHappened(EventCallback<i32>&& callback) final;

public: // MyClass events emission functions
  inline void somethingHappened(Emit mode = Emit::onCommit);
  inline void somethingElseHappened(i32 arg, Emit mode = Emit::onCommit);
};

First, we have the functions that clients can use to receive notifications about the emission of events. Those are named like onXYX(), where XYZ is the name of the event. They receive an EventCallback<>, which is just a function where clients will get called to. 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, we have the functions we can use to fire those events. They are named after the events. The mode argument is rarely used but tells Sen when would you like to produce the event.

The functions related to our properties are the following:

my_class.stl.h properties
  // prop1
  const std::string& getProp1() const noexcept final;

  // prop2
  const basic_package::StructOfInts& getProp2() const noexcept final;
  void setNextProp2(const basic_package::StructOfInts& val) final;
  ConnectionGuard onProp2Changed(PropertyCallback&& callback) final;

  // prop3
  const basic_package::MyVariant& getProp3() const noexcept final;
  void setNextProp3(const basic_package::MyVariant& val) final;
  ConnectionGuard onProp3Changed(PropertyCallback&& callback) final;

  // prop4
  const basic_package::Vec2& getProp4() const noexcept final;
  ConnectionGuard onProp4Changed(PropertyCallback&& callback) final;

protected: // MyClass protected properties
  void setNextProp4(const basic_package::Vec2& val);

There are getters for all of them. Getters are named like getXYZ(), where XYZ is the name of the property.

There are also hooks for detecting changes. Those are the ones named like onXYZChanged(). 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.

We also have the setters. Setters are public when the property is writeable, and protected otherwise. They are name like setNextXYZ(), where XYZ is the name of the property. The reason for the "Next" in the name is that the value that we use will only be visible to the world only after we commit() our changes. This guarantees that the state of objects is consistent in every cycle, and we only make use of the inputs of the current cycle for computing the next one.

Finally, we have static properties, which are the ones that do not change during the lifetime of our objects. You need to set them via the constructor to ensure that they always have a valid value. In our case it is prop1:

my_class.stl.h constructor
MyClassBase(std::string name, std::string prop1);