Application-relevant changes 2025:
Changes of C++ standard, libraries and classes

Note: Up to the 2025.0 release, interfaces and other details may change without prior notice.

Update to C++ 20

With firmware release 2025.0, the C++ language standard used in the SDK is updated to C++ 20 to enable the usage of new features provided by the language. Since features of C++ 20 are used in the SDK’s header files, all compilation units including headers from the PLCnext Technology SDK must be recompiled using at least C++ 20.

The new features of C++ 20 which are now available with PLCnext Technology can be found in the cppreference.com for C++ 20.

The main improvements regarding Arp code are described in the following subsections.

Simplified namespace syntax

The syntax of namespace declaration was simplified as shown in the following example.

Before C++ 20:

namespace Arp { namespace System { namespace Commons
{
 
// code here
 
}}} // end of namespace Arp::System::Commons

From C++ 20:

namespace Arp::System::Commons 
{  
 
// code here  
 
} // end of namespace Arp::System::Commons

The new namespace syntax should be preferred in future development.

Library Arp.Base.Core 

(formerly Arp.System.Core)

The entire code of the Arp.System.Core library is re-implemented in the Arp.Base.Core library. The purpose of this change is to provide a source-compatible migration path to ensure binary compatibility in future releases. Arp.Base.Core implements some techniques to further separate the interface from the implementation to enable new features and easier fixes. Some inconsistencies in naming and behavior have also been fixed.

Even though the location of the library in the SDK has changed, it does not cause any adjustments of the include directives nor any type names:

  • All include directives of Arp.System.Core files are delegated to the corresponding header file in Arp.Base.Core
  • All types of the Arp.Base.Core library are defined in namespace Arp::Base::Core, but are imported into the Arp namespace, as was in the Arp.System.Core library.

All (non-template) classes in the Arp.Base.Core library implement the PImpl pattern now. This is to avoid any binary incompatibilities in future when extensions or modifications become necessary.

The Arp.System.Core library was built as archive and linked statically, which causes some trouble with the AppDomain singleton pattern, so that the AppDomain instance had to be injected into any shared library. This issue is now fixed, since the Arp.Base.Core library is built as a shared library and linked dynamically. Therefore, dependencies do not propagate to the client's link process anymore.

There are only a few minor changes regarding the usage of Arp.Base.Core, which are mostly indicated through deprecated warnings during compilation, while the warning message gives a hint how to fix it. All deprecated operations will be removed in future (but not yet in firmware release 2025.0).

Mapping Arp::byte to std::byte

C++ 17 introduced a new datatype called std::byte. This type might be seen as a real binary type of size 'one byte', which was lacking in C++ for the time being.

The characteristics of this datatype are:

  • It's not an integral datatype.
  • It's not an arithmetic datatype.
  • It only supports logical bit operations.
  • Initialization with integral datatypes is not possible; therefore, in namespace Arptwo string literal operators were defined:  _b and _B (see examples in the code block below).

PLCnext Technology firmware release 2021.6 switched to the C++ 17 standard already. At that time, the C++ compiler may have regarded the datatype byte as ambiguous, so that our Change Notes stated:

C++ 17 introduces the datatype std::byte which is unfortunately not compatible with Arp::byte. Thus, if the namespaces std and Arp are both active, the compilation results in an error.  In this case existing C++ sources have to be adjusted, so that they explicitly use Arp::byte (e.g. by adding using byte = Arp::byte;).

With the 2025.0 firmware, the Arp::byte datatype is switched from std::uint8_t to std::byte. Up to now it was just an alias of Arp::uint8 which maps to unsigned char. Thus, if user code generates compile errors related to the new Arp::byte datatype, it is necessary to just switch to Arp::uint8 to avoid them.

Another approach would be to adapt the code to the new datatype if it should represent a plain byte buffer. To get familiar with the new byte datatype, examine the following code examples, which demonstrate the usage of it:

String literal operator for byte datatype:

byte b = 0;                      // distinct type => compile error! 
byte b1 = 0_b; 
byte b2 = 123_B; 
byte b3 = 0xff_B; 
byte b4 = 0xFE_b; 
byte b5 = 333_B; 

Zero initialization:

byte b = static_cast<byte>(0);   // static_cast operator 
byte b0 = (byte)0;               // explicit C cast 
byte b1 = byte(0);               // explicit C++ cast 
byte b2 = 0_b;                   // use of string literal operator _b 
byte b3{ 0 };                    // {}-Initialization with implicit cast 
byte b4{ 0_b };                  // {}-Initialization (initialize first field by 0, 
                                 // all others are default initialized) 
byte b5{ };                      // Default {}-Initialization (value is zero'ed) 
byte b6[6]{ };                   // Default array {}-Initialization (array is zero'ed) 
byte b7[7]{ 0_b };               // Explicit array {}-Initialization with zero 
byte b8[]{ 0_b, 0_b, 0_b };      // Initialization through initializer list (size==3) 
byte b9[9] { 0 };                // distinct type => compile error! 

Number initialization:

byte b = 123;                    // distinct type => compile error! 
byte b = static_cast<byte>(1);   // static_cast operator 
byte b0 = (byte)123;             // explicit C cast 
byte b1 = byte(0x9A);            // explicit C++ cast 
byte b2 = 111_b;                 // use of string literal operator _b 
byte b3{ 255 };                  // {}-Initialization with implicit cast 
byte b4{ 0xA4_b };               // {}-Initialization 
byte b5[5]{ 5_b };               // Partial array Initialization through 
                                 // initializer list => {5, 0, 0, 0, 0} 
byte b6[6]{ 1_b, 2_b, 3_b };     // Partial array Initialization through 
                                 // initializer list => {1, 2, 3, 0, 0} 
byte b7[]{ 1_b, 2_b, 3_b };      // Initialization through
                                 // initializer list (size==3) 
byte b8[8] { 8 };                // distinct type => compile error! 

Byte format operations defined by Arp:

byte b0{}; 
byte b1 = 1_b; 
byte b2 = 43_B;                  // ==0x2B 
byte b3 = 0xff_B; 
 
Assert::AreEqual("00", String::Format("{}", b0)); 
Assert::AreEqual("01", String::Format("{}", b1)); 
Assert::AreEqual("2b", String::Format("{}", b2)); 
Assert::AreEqual("ff", String::Format("{}", b3)); 

Bit operations:

byte b11111110 = 254_b; 
byte b00001111 = 1_b | 2_b | 4_b | 8_b;         // bitwise | operator 
byte b00001110 = b00001111 & b11111110;         // bitwise & operator 
byte b01111111 = static_cast<byte>(~0x80_b);    // bitwise ~ operator
 
Assert::AreEqual(0x0F_b, b00001111, "Bitwise | operator."); 
Assert::AreEqual(0x0E_b, b00001110, "Bitwise & operator."); 
Assert::AreEqual(0x7F_b, b01111111, "Bitwise ~ operator."); 

Shift operation:

constexpr byte b1 = 0x01_b; 
uint16 u16 = uint16(b1 << 7); 
static_assert(u16 == 128, "Shift operation of byte in range.");

If shift operation fails:

byte b1 = 0x01_b; 
uint16 u16 = uint16(b1 << 8);    // fails, correct code would be: 
uint16 u16 = uint16(b1) << 8     // ok!
Assert::AreEqual(u16, 256, "Shift operation of byte as uint16.");
// The failing shift operation would be okay  
// for any 1 byte integral data type like uint8, 
// but not for std::byte, 
// because std::byte is not cast implicitly to int 

Converting string from and to bytes:

String actual("abc"); 
std::vector<byte> bytes = actual.ToBytes(); 
String expected(bytes); 
 
Assert::AreEqual(expected, actual);

class AppDomain

The following operations are deprecated:

  • AppDomain::GetCurrent()→ use AppDomain::GetInstance() instead.
  • AppDomain::Assign(AppDomain&) → it's not required any more.

class AppDomainSingleton

The following operation is deprecated:

  • AppDomainSingleton::GetInstancePtr()→ use AppDomainSingleton::IsCreated() to check if it was created,
    and use &AppDomainSingleton::GetInstance() to get access to the instance pointer.

The following operation changes its behavior:

  • AppDomainSingleton::CreateInstance(...)→ throws an exception now if the singleton was already created.

The AppDomainSingleton class is deprecated.

It should not be used any more. The main goal of this class was to fix the issue, that the Arp::Singleton<T> base class has to be implemented as template, so that it only works within shared-library scope, but not within process-wide scope.

The GlobalSingleton pattern should be used instead of the AppDomainSingleton implementation. The following demo code might be seen as a template to be copied into custom projects, with replacing the class name by the desired custom class name.

Header file of the GlobalSingleton pattern:

#pragma once 
#include "Arp/Base/Core/Arp.hpp"
#include <memory>
 
namespace Apps::Demo::Extension::Internal
{
 
class GlobalSingleton
{
public: // usings
    using Instance = GlobalSingleton;
    using InstancePtr = std::unique_ptr<Instance>;
    
public: // construction
    // If the constructor shall get parameters
    // then the CreateInstance(..) operation has to be adjusted accordingly
    GlobalSingleton(void) = default;
    
public: // copy/move/assign/destruction
    GlobalSingleton(const GlobalSingleton& arg) = delete;
    GlobalSingleton(GlobalSingleton&& arg) = delete;
    GlobalSingleton& operator=(const GlobalSingleton& arg) = delete;
    GlobalSingleton& operator=(GlobalSingleton&& arg) = delete;
    ~GlobalSingleton(void) = default;
    
public: // static singleton operations 
    static Instance&     CreateInstance(void);
    static bool          IsCreated(void);
    static void          DisposeInstance(void);
    static Instance&     GetInstance(void);
    
private: // static fields 
    static InstancePtr instancePtr;
};
 
/////////////////////////////////////////////////////////////////////////////// 
// inline methods of class GlobalSingleton
 
} // end of namespace Apps::Demo::Extension::Internal

Source file of GlobalSingleton pattern:

#include "Apps/Demo/Extension/Internal/GlobalSingleton.hpp"
#include "Arp/Base/Core/TypeName.hxx"
#include "Arp/Base/Core/Exception.hpp" 
 
namespace Apps::Demo::Extension::Internal 
{  
 
using namespace Arp;
   
// initializing of static fields 
GlobalSingleton::InstancePtr GlobalSingleton::instancePtr; 
 
GlobalSingleton::Instance& GlobalSingleton::CreateInstance() 
{ 
  if (IsCreated()) 
  { 
     throw Exception("Singleton instance of type '{}' was created yet!", TypeName<Instance>()); 
  } 
  instancePtr = std::make_unique<Instance>(); 
  return *instancePtr; 
} 
 
bool GlobalSingleton::IsCreated() 
{ 
  return instancePtr != nullptr; 
} 
 
GlobalSingleton::Instance& GlobalSingleton::GetInstance() 
{ 
  if (!instancePtr) 
  { 
    throw Exception("Singleton instance of type '{}' was not created yet!", TypeName<Instance>()); 
  } 
  return *instancePtr; 
} 
 
void GlobalSingleton::DisposeInstance() 
{
  instancePtr.reset(); 
} 
 
} // end of namespace Apps::Demo::Extension::Internal

If the constructor of the GlobalSingleton class requires some arguments then the CreateInstance(..) operation must be adjusted accordingly.

Note: The GlobalSingleton pattern does not work from within static libraries.

class Singleton<T>

The Singleton<T> class implemented some operations only intended to be used by the AppDomain and AppDomainSingleton classes. Since they got refactored, these functions are not needed any more.

The following operation is deprecated:

  • Singleton<T>::GetInstancePtr(void) → use Singleton<T>::IsCreated() to check if it was created; use &Singleton<T>::GetInstance() to get access to the instance pointer.

The following protected operation is deprecated:

  • Singleton<T>::SetInstance(T* pInstance)→ not required anymore.

The following operation changes its behavior:

  • Singleton<T>::CreateInstance(...) → throws exception now if singleton was already created.

class ArpVersion

The ArpVersion combined multiple functionalities in a single class: a version number and additional information like state and name. To simplify the usage of ArpVersion, this class was divided into two classes in the Arp.Base.Core library:

  • Version → a simple version class consisting of four integer properties: Major, Minor, Patch, and Build
  • ArpVersion → providing the build version, the build name and the build state

The following operations and properties of class ArpVersion are therefore deprecated:

  • ArpVersion::Current
    → use ArpVersion::GetCurrent() instead
  • ArpVersion::GetMajor()
    → use ArpVersion::GetBuildVersion().GetMajor() instead
  • ArpVersion::GetMinor()
    → use ArpVersion::GetBuildVersion().GetMinor() instead
  • ArpVersion::GetPatch()
    → use ArpVersion::GetBuildVersion().GetPatch() instead
  • ArpVersion::GetBuild()
    → use ArpVersion::GetBuildVersion().GetBuildNumber() instead
  • ArpVersion::operator<
    → use ArpVersion::GetBuildVersion() for comparison instead
  • ArpVersion::operator>
    → use ArpVersion::GetBuildVersion() for comparison instead
  • ArpVersion::operator<=
    → use ArpVersion::GetBuildVersion() for comparison instead
  • ArpVersion::operator>=
    → use ArpVersion::GetBuildVersion() for comparison instead

The following constructors and operations of the Version class are deprecated and were only added for code compliance:

  • Version(Value major, Value minor, Value patch, Value build, const String& state, const String& name) 
    → use ArpVersion class instead
  • Version::GetName()
    → use ArpVersion class instead
  • Version::GetState()
    → use ArpVersion class instead

class Exception

The following protected operations are deprecated:

  • Exception::Exception(String&&, const Exception::Ptr&)
    → use Exception(ExceptionTypeId, String&&, const Exception&)instead
  • void Exception::Format(int indentLevel, bool withInnerException)const
    → use void Exception::Format(bool withInnerException)const instead
  • uint32 Exception::GetTypeCodeInternal(void)const
    → not required anymore
  • The return value of Exception::GetInnerException() changed  from Exception::Ptr to const Exception&
    → not a deprecation warning but a compile error occurs when using this operation.

class TypeName<T> and CommonTypeName<T>

The classes TypeName<T> and CommonTypeName<T> provided a public member variable Value. This can cause issues regarding compatibility in the future.  TypeName<T> contained a function to generate the CommonTypeName<T>. This violates the Single Responsibility principle.

class TypeName<T>

The following operations and properties are deprecated:

  • TypeName<T>::Value
    → use operation TypeName<T>::GetFullName(void) instead
  • TypeName<T>::GetCommonName(void)
    → use class CommonTypeName<T> instead

class CommonTypeName<T>

The following properties are deprecated:

  • CommonTypeName<T>::Value
    → use operation CommonTypeName<T>::GetFullName() instead

class DateTime

The class DateTime provided public member variables. This can cause compatibility issues in the future.

Currently, DateTime only implements UTC-based timestamps. When support for local time is added in the future, DateTime objects with an unspecified time zone will cause problems. Therefore, these are not tolerated anymore.

The following properties are deprecated:

  • DateTime::MinTicks
    → use DateTime::GetMinTicks() instead
  • DateTime::MaxTicks
    → use DateTime::GetMaxTicks() instead

The following operation changes its behaviour:

  • DateTime constructor throws an exception now if the DateTimeKind parameter is not set to DateTimeKind::Utc

class String

Some member functions of String were renamed for more clarity and for conformance to the C++ standard library.

The following properties are deprecated:

  • String::GetBaseString()
    → use String::GetStdString() instead
  • String::StartWith(..)
    → use String::StartsWith(..) instead (C++ 20 introduced std::basic_string<…>::starts_with()).
  • String::EndWith(..)
    → use String::EndsWith(..) instead (C++ 20 introduced std::basic_string<…>::ends_with()).

Since C++ 20, the C++ standard defines the std::span type to determine the size of a static array automatically by the compiler. Thus, the explicit specified parameter delimitersCount is not required any more, and shall be omitted.

Therefore, the following operation change its signature and causes compile errors if not altered:

  • String::Split(const CharType delimiters[], size_t delimitersCount, bool trimTokens, bool removeEmptyTokens) 
    → the new signature is:
    String::Split(std::span<const CharType> delimiters, bool trimTokens, bool removeEmptyTokens)const;

The following operations change their behavior and would cause compile errors, which is caused by the upgrade of libfmt library:

  • When using String::Format(..) the formatting of some primitive types changes:
    • Arp::int8 is formatted as a number now (formerly as a character)
    • Arp::uint8 is formatted as a number now (formerly as a character)
    • Arp::byte is formatted as a 2-digit hex number number now (formerly as a character)
  • String::Format(..) does not accept following types any more:
    • raw enums 
      → cast the values to int instead
    • enum classes which do not implement the Arp enum pattern 
      → cast the values to int instead
    • Variables of type std::atomic<T>
      → format the raw value by calling std::atomic<T>::load() instead
    • std::thread::id
      → no suggestions so far
    • smart pointers like std::shared_ptr<T>
      → obtain the raw pointer by calling std::shared_ptr<T>::get() and cast it to void*
  • The requirements on custom types to be formatted using String::Format have changed.  
    • Instead of providing an operator<<(std::ostream&, T) function, a fmt::formatter<T> specialization has to be provided.
    • If the ostream operator operator<< is defined already, this is just a matter of adding an appropriate using declaration on global namespace level: template<> struct fmt::formatter<T> : public fmt::ostream_formatter {}; where T is the fully qualified name of the custom type (see example below).

Template specialization of String formatter:

template<> struct fmt::formatter<Arp::Base::Core::String> : public fmt::ostream_formatter {};

Library Arp.Base.Acf.Commons

As mentioned above, the public code of the Arp.System.Acf library has moved to the new library Arp.Base.Acf.Commons. This was caused by the fact, that the Acf did not separate the interfaces from the implementation code, which violates the Inversion of Dependencies principle.

Even though the location of the library in the SDK has changed, it does not cause any adjustments of the include directives nor any type names:

  • All include directives of Arp.System.Acf files are delegated to the corresponding header file in Arp.Base.Acf.Commons
  • All types of the Arp.Base.Acf.Commons library are defined in namespace Arp::Base::Acf::Commons, but are imported also into the former namespace Arp::System::Acf to avoid code adaptation

But in contrast to the Arp.Base.Core library, the revised Acf code causes some major code changes:

  • All user code based on the Acf classes ComponentBase and LibraryBase must be adjusted to fit to the new interfaces.

The library implementation of a custom project

The library declaration

The project's library class is derived by LibraryBase from Acf and implements a default constructor as well as an operation to obtain the singleton.

Header file:

#pragma once 
#include "Arp/Base/Core/Arp.hpp"
#include "Arp/Base/Acf/Commons/LibraryBase.hpp" 
 
namespace Apps::Demo::Sdk 
{ 
 
using Arp::Base::Acf::Commons::ILibrary; 
using Arp::Base::Acf::Commons::LibraryBase; 
 
class SdkLibrary : public LibraryBase 
{ 
public: // construction/destruction 
    SdkLibrary(void); 
 
public: // static singleton operations 
    static ILibrary& GetInstance(void); 
}; 
 
} // end of namespace Apps::Demo::Sdk

Changes:

  • The former code also derives the library class by Singleton<SdkLibrary> → remove this
  • The signature of the constructor was formerly SdkLibrary(AppDomain& appDomain)→ remove the parameter

The library definition

The implementation of the project's library shall look like this:

#include "Apps/Demo/Sdk/SdkLibrary.hpp"
#include "Apps/Demo/Sdk/SdkComponent.hpp"
#include "Arp/Base/Core/TypeName.hxx" 
 
namespace Apps::Demo::Sdk 
{ 
 
SdkLibrary::SdkLibrary()
    : LibraryBase(/* User defined version: */ ArpVersion(1,2,3)) 
{ 
    this->AddComponentType<SdkComponent>(); 
    // Add more component types here if required 
} 
 
ILibrary& SdkLibrary::GetInstance() 
{ 
    static SdkLibrary instance; 
    return instance; 
} 
 
extern "C" ARP_EXPORT ILibrary& Apps_Demo_Sdk_MainEntry() 
{ 
    return SdkLibrary::GetInstance(); 
} 
 
} // end of namespace Apps::Demo::Sdk

Changes:

  • The former code passed the ARP_VERSION_CURRENT macro to the constructor of LibraryBase. This is no longer necessary. The version of the SDK is passed to the library using another mechanism.
  • The version parameter to the constructor of LibraryBase is a version number intended for the user. It can be used to track a version of a library. The new Arp.System.Acf.Services.ISystemInfoService provides methods to query information about the loaded components. For each component the Arp SDK version and the user version is provided by the service.
  • The former implementation of the library constructor registers the provided components through accessing the protected componentFactory member variable directly. This is now replaced by the call of LibraryBase::AddComponentType<T> operation, where T is the component type to register.
  • The singleton is implemented by the project's library itself through the GetInstance() operation, using a local static instance (see Scott Meyers (1996), More Effective C++, Addison-Wesley, pp. 146 ff.).
  • The name of the library's entry function was static so far and always called ArpDynamicLibraryMain. From now on it depends on the project's name (or library's name, respectively). It consists of the safe name of the shared library omitting the extension .so as well as the default Linux lib prefix and appends a static suffix called _MainEntry. The safe name of the library replaces all special characters by underscores, e.g. if the project/library is called libArp.Plc.Gds.so, the main entry function shall be called: Arp_Plc_Gds_MainEntry.
  • In the ACF configuration files (*.acf.config) the new optional attribute mainEntry of the Library element may be used to specify an alternative name for the mainEntry function.

The component implementation of a custom project

The component declaration

The project's component class is derived by ComponentBase from Acf

Header file:

#pragma once 
#include "Arp/Base/Core/Arp.hpp" 
#include "Arp/Base/Acf/Commons/ComponentBase.hpp" 
 
namespace Apps::Demo::Sdk 
{ 
 
using namespace Arp; 
using namespace Arp::Base::Acf::Commons; 
 
class SdkComponent : public ComponentBase 
{ 
public: // construction/destruction 
    SdkComponent(ILibrary& library, const String& name); 
 
public: // IComponent operations 
    void Initialize(void)override; 
    void SubscribeServices(void)override; 
    void LoadSettings(const String& settingsPath)override; 
    void SetupSettings(void)override; 
    void PublishServices(void)override; 
    void LoadConfig(void)override; 
    void SetupConfig(void)override; 
    void ResetConfig(void)override; 
    void Dispose(void)override; 
    void PowerDown(void)override; 
 
public: // properties 
 
public: // operations 
 
private: // methods 
 
private: // fields 
 
private: // static fields 
}; 
 
} // end of namespace Apps::Demo::Sdk

Changes:

  • The signature of the constructor changed from SdkComponent(IApplication& application, const String& name) to  SdkComponent(ILibrary& library, const String& name)
  • The static SdkComponent::Create(...) operation having the same signature as the former constructor might be removed.
  • All special member functions (copy and move operations, destructor) can be removed, if not required by the implementation of the derived class.
  • All canonical constructors and assignment operators can be removed.
  • The function IComponent::GetVersion() has been removed, since it is now ambiguous. It can be replaced using GetLibrary().GetBuildVersion(). Note the new function ILibrary::GetLibraryVersion().

The component definition

The implementation of the project's component shall look like this:

#include "Apps/Demo/Sdk/SdkComponent.hpp"
#include "Apps/Demo/Sdk/SdkLibrary.hpp" 
 
namespace Apps::Demo::Sdk 
{ 
 
SdkComponent::SdkComponent(ILibrary& library, const String& name) 
    : ComponentBase(library, name, ComponentCategory::Custom, GetDefaultStartOrder()) 
{ 
} 
 
void SdkComponent::Initialize() 
{ 
    // register components, initialize singletons 
    // subscribe notifications or events here 
} 
 
void SdkComponent::SubscribeServices() 
{ 
    // subscribe the services used by this component here 
} 
 
void SdkComponent::LoadSettings(const String& settingsPath) 
{ 
    // load firmware settings here 
} 
 
void SdkComponent::SetupSettings() 
{ 
    // setup firmware settings here 
} 
 
void SdkComponent::PublishServices() 
{ 
    // publish the services of this component here 
} 
 
void SdkComponent::LoadConfig() 
{ 
    // load project config here 
} 
 
void SdkComponent::SetupConfig() 
{ 
    // setup project config here 
} 
 
void SdkComponent::ResetConfig() 
{ 
    // implement this inverse to SetupConfig() and LoadConfig() 
} 
 
void SdkComponent::Dispose() 
{ 
    // implement this inverse to SetupSettings(), LoadSettings() and Initialize() 
} 
 
void SdkComponent::PowerDown() 
{ 
    // implement this only if data must be retained even on power down event 
} 
 
} // end of namespace Apps::Demo::Sdk

Changes:

  • The signature of the constructor of class ComponentBase changed
    from ComponentBase(IApplication&, ILibrary&, const String&, ComponentCategory, size_t, bool) 
    to ComponentBase(ILibrary&, const String&, ComponentCategory, size_t) 
    Thus, the first parameter of type IApplication was removed. Pass the component's constructor arguments accordingly to ComponentBase class as shown in the example above. The start order of components must now be passed explicitly.
  • Use GetDefaultStartOrder() for a sensible default value for the start order. The value shall only be changed to resolve dependencies of multiple custom components.

TraceController from Arp.System.Commons

The class Arp::System::Commons::Diagnostics::TraceController is removed from the SDK. The functionality is offered by the Arp.Services.TraceController component and its ITraceControllerServiceRSC service.

Library Arp.Base.Rsc.Commons

(formerly Arp.System.Rsc.Services)

Changes:

  • When calling RSC services in C++ with a parameter of type RscVariant<N>, then compile errors might occur because the interface of RscVariant has changed. In this case, please ask for direct developer support.
  • Related to template class RscVariant<N> and template class RscString<N>:
    The template parameter N specifies the maximal length of the string. Formerly the effective length was N-1, due to the NUL terminator; now it is N. This issue was claimed frequently, so that it was fixed with this Application-relevant Changes release.
  • If any RSC services were implemented (non-public feature), the service implementation code has to be re-generated by the RscGenerator. In some rare cases, the code of the service implementation has to be adjusted.
  • The class RscStreamAdapter in C++ was renamed to RscStream. Thus, if the compiler claims a non-overridden Service-Impl operation, which uses RscStreamAdapter, just rename it to RscStream.
  • RscStructReader.hxx and RscStructWriter.hxx have been replaced with RscStructReader.hpp and RscStructWriter.hpp (respectively). The template classes RscStructReader<MaxStringSize> and RscStructWriter<MaxStringSize> have been replaced with the non-template classes RscStructReader and RscStructWriter (respectively). The latter classes implement template operations which deduce the template arguments automatically.


• Published/reviewed: 2024-09-24   ☀  Revision 073 •