Parameter

This section describes the interface of the Parameter class, along with Component one of two pillar concepts of the VSlib. This class provides a representation for a runtime-settable, via specialized web-hosted GUI, parameters of Component class. They are strongly bound, and Parameters can only be defined in Component and Component-derived classes.

General interface

Parameter is a templated class, and the template parameter is the stored type. Currently, the following types are supported:

Type class

Types

Boolean

bool

Signed integer

int8_t, int16_t, int32_t, int64_t

Unsigned integer

uint8_t, uint16_t, uint32_t, uint64_t

Floating point

float (32bit), double (64bit)

Strings

std::string

Enumerations

User-defined enum and class enum

Parameters holding numerical types (integers, floating-point) can optionally have defined lower and upper limits.

Parameter is always defined inside a Component class, for example:

#include "component.h"

using namespace vslib;

class CustomComponent : public Component
{
    CustomComponent(std::string_view name, Component& parent)
      : Component("CustomType", name, parent),
        param(*this, "scalar", 0.0, 10.0)
    {
    }

    Parameter<double> param;
};

In the example above, the 0.0 and 10.0 are lower and upper limits for param Parameter, respectively.

Parameter provides seamless interactions with scalar types, that means you can use the Parameter as if it was the scalar type it stores:

#include "customComponent.h"

void your_function(RootComponent& root)
{
    CustomComponent component("custom", root);

    component.param; // returns the current double-type value of the CustomComponent's param
    component.param.value(); // returns the same value as above but explicitly
}

In case of Parameters that are holding std::arrays, there three ways to interact with that array. Assuming the following definition:

#include "component.h"

using namespace vslib;

class CustomComponent : public Component
{
    CustomComponent(std::string_view name, Component& parent)
      : Component("CustomType", name, parent),
        param(*this, "array", 0.0, 10.0)
    {
    }

    Parameter<std::array<double, 3>> param; // the array's size is fixed at definition
};

where limits 0.0 and 10.0 apply to each array element, individually.

#include "customComponent.h"

void your_function(RootComponent& root)
{
    CustomComponent component("custom", root);

    // 1. Get the reference to the entire array:
    auto& array = component.param.value();

    // 2. Refer to an array element by operator[]:
    auto& element = component.param.value()[2]; // returns the third element

    // 3. Iterate over the array as if it was an std::array:
    for (const auto& element : component.param.value())
    {
        // use element as if it was just a double-type value
    }
}

For more details regarding the API, see the API documentation for Parameter.

Parameter setting

The Parameter value is not code-settable. The interface to set values directly is not enabled, and they can be treated as read-only in the code. This is why it is safe to place them in the public interface of you Component. The Parameter value can be only set via specialized GUI, the Vloop parameter setter, a part of the FGC Commander.

The values stored by the Parameter are double-buffered to ensure safety of read-write cycle. Whenever you access the value, the read-buffer value is returned. The value-setter GUI interacts only with the write-buffer. The buffers are swapped and synchronised by the Component owning this Parameter when all the checks performed by the setter and Component’s verifyParameters() method were successful. If any of the checks showed an issue, the new value is not accepted, and the write buffer is re-synchronised with the read-buffer.

The value-setting logic includes the following checks:

  • type correctness

  • for numerical types: whether the new value fits in the limits

  • for arrays: the length must agree

  • for enums: whether the new value exists in the enum

  • any validation logic implemented by the user in Component’s verifyParameters() method

Type correctness checks that there is no loss of information. For example, there will be no warning when you set a 32-bit integer value to a float Parameter, but the opposite combination would return a warning. Equally, C++ would not warn the user in case an implicit cast is done between boolean and integer type (e.g. value of 2 would be interpreted as true), and signed and unsigned integers, potentially leading to confusing values being set like -1 to an unsigned integer due to overflow.

Parameters have a special method to inform whether their value has been already set by the external GUI: isInitialized(). Until the Parameter is successfully set for the first time, this method will return false.